github.com/asifdxtreme/cli@v6.1.3-0.20150123051144-9ead8700b4ae+incompatible/cf/api/authentication/authentication.go (about)

     1  package authentication
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"net/url"
     7  	"strings"
     8  
     9  	. "github.com/cloudfoundry/cli/cf/i18n"
    10  
    11  	"github.com/cloudfoundry/cli/cf/configuration/core_config"
    12  	"github.com/cloudfoundry/cli/cf/errors"
    13  	"github.com/cloudfoundry/cli/cf/net"
    14  )
    15  
    16  type TokenRefresher interface {
    17  	RefreshAuthToken() (updatedToken string, apiErr error)
    18  }
    19  
    20  type AuthenticationRepository interface {
    21  	RefreshAuthToken() (updatedToken string, apiErr error)
    22  	Authenticate(credentials map[string]string) (apiErr error)
    23  	GetLoginPromptsAndSaveUAAServerURL() (map[string]core_config.AuthPrompt, error)
    24  }
    25  
    26  type UAAAuthenticationRepository struct {
    27  	config  core_config.ReadWriter
    28  	gateway net.Gateway
    29  }
    30  
    31  func NewUAAAuthenticationRepository(gateway net.Gateway, config core_config.ReadWriter) (uaa UAAAuthenticationRepository) {
    32  	uaa.gateway = gateway
    33  	uaa.config = config
    34  	return
    35  }
    36  
    37  func (uaa UAAAuthenticationRepository) Authenticate(credentials map[string]string) (apiErr error) {
    38  	data := url.Values{
    39  		"grant_type": {"password"},
    40  		"scope":      {""},
    41  	}
    42  	for key, val := range credentials {
    43  		data[key] = []string{val}
    44  	}
    45  
    46  	apiErr = uaa.getAuthToken(data)
    47  	switch response := apiErr.(type) {
    48  	case errors.HttpError:
    49  		if response.StatusCode() == 401 {
    50  			apiErr = errors.New(T("Credentials were rejected, please try again."))
    51  		}
    52  	}
    53  
    54  	return
    55  }
    56  
    57  type LoginResource struct {
    58  	Prompts map[string][]string
    59  	Links   map[string]string
    60  }
    61  
    62  var knownAuthPromptTypes = map[string]core_config.AuthPromptType{
    63  	"text":     core_config.AuthPromptTypeText,
    64  	"password": core_config.AuthPromptTypePassword,
    65  }
    66  
    67  func (r *LoginResource) parsePrompts() (prompts map[string]core_config.AuthPrompt) {
    68  	prompts = make(map[string]core_config.AuthPrompt)
    69  	for key, val := range r.Prompts {
    70  		prompts[key] = core_config.AuthPrompt{
    71  			Type:        knownAuthPromptTypes[val[0]],
    72  			DisplayName: val[1],
    73  		}
    74  	}
    75  	return
    76  }
    77  
    78  func (uaa UAAAuthenticationRepository) GetLoginPromptsAndSaveUAAServerURL() (prompts map[string]core_config.AuthPrompt, apiErr error) {
    79  	url := fmt.Sprintf("%s/login", uaa.config.AuthenticationEndpoint())
    80  	resource := &LoginResource{}
    81  	apiErr = uaa.gateway.GetResource(url, resource)
    82  
    83  	prompts = resource.parsePrompts()
    84  	if resource.Links["uaa"] == "" {
    85  		uaa.config.SetUaaEndpoint(uaa.config.AuthenticationEndpoint())
    86  	} else {
    87  		uaa.config.SetUaaEndpoint(resource.Links["uaa"])
    88  	}
    89  	return
    90  }
    91  
    92  func (uaa UAAAuthenticationRepository) RefreshAuthToken() (string, error) {
    93  	data := url.Values{
    94  		"refresh_token": {uaa.config.RefreshToken()},
    95  		"grant_type":    {"refresh_token"},
    96  		"scope":         {""},
    97  	}
    98  
    99  	apiErr := uaa.getAuthToken(data)
   100  	updatedToken := uaa.config.AccessToken()
   101  
   102  	return updatedToken, apiErr
   103  }
   104  
   105  func (uaa UAAAuthenticationRepository) getAuthToken(data url.Values) error {
   106  	type uaaErrorResponse struct {
   107  		Code        string `json:"error"`
   108  		Description string `json:"error_description"`
   109  	}
   110  
   111  	type AuthenticationResponse struct {
   112  		AccessToken  string           `json:"access_token"`
   113  		TokenType    string           `json:"token_type"`
   114  		RefreshToken string           `json:"refresh_token"`
   115  		Error        uaaErrorResponse `json:"error"`
   116  	}
   117  
   118  	path := fmt.Sprintf("%s/oauth/token", uaa.config.AuthenticationEndpoint())
   119  	request, err := uaa.gateway.NewRequest("POST", path, "Basic "+base64.StdEncoding.EncodeToString([]byte("cf:")), strings.NewReader(data.Encode()))
   120  	if err != nil {
   121  		return errors.NewWithError(T("Failed to start oauth request"), err)
   122  	}
   123  	request.HttpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   124  
   125  	response := new(AuthenticationResponse)
   126  	_, err = uaa.gateway.PerformRequestForJSONResponse(request, &response)
   127  
   128  	switch err.(type) {
   129  	case nil:
   130  	case errors.HttpError:
   131  		return err
   132  	case *errors.InvalidTokenError:
   133  		return errors.New(T("Authentication has expired.  Please log back in to re-authenticate.\n\nTIP: Use `cf login -a <endpoint> -u <user> -o <org> -s <space>` to log back in and re-authenticate."))
   134  	default:
   135  		return errors.NewWithError(T("auth request failed"), err)
   136  	}
   137  
   138  	// TODO: get the actual status code
   139  	if response.Error.Code != "" {
   140  		return errors.NewHttpError(0, response.Error.Code, response.Error.Description)
   141  	}
   142  
   143  	uaa.config.SetAccessToken(fmt.Sprintf("%s %s", response.TokenType, response.AccessToken))
   144  	uaa.config.SetRefreshToken(response.RefreshToken)
   145  
   146  	return nil
   147  }