Skip to content

Commit

Permalink
Support login to UAA using API token
Browse files Browse the repository at this point in the history
Signed-off-by: Vui Lam <vui.lam@broadcom.com>
  • Loading branch information
vuil committed Oct 3, 2024
1 parent 9896322 commit 45623eb
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 6 deletions.
12 changes: 8 additions & 4 deletions pkg/auth/uaa/tanzu.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ import (
const (
// Tanzu CLI client ID for UAA that has http://127.0.0.1/callback as the
// only allowed Redirect URI and does not have an associated client secret.
tanzuCLIClientID = "tp_cli_app"
tanzuCLIClientSecret = ""
defaultListenAddress = "127.0.0.1:0"
defaultCallbackPath = "/callback"
tanzuCLIClientID = "tp_cli_app"
// Alternate client ID for UAA associated with a longer refresh token
// validity. Use this for CLI use cases where it is impractical to
// interactively reauthenticated once the refresh token expires.
tanzuCLIClientIDExtended = "tp_cli_app" // TODO(vui) update once available.
tanzuCLIClientSecret = ""
defaultListenAddress = "127.0.0.1:0"
defaultCallbackPath = "/callback"
)

func getIssuerEndpoints(issuerURL string) common.IssuerEndPoints {
Expand Down
55 changes: 55 additions & 0 deletions pkg/auth/uaa/uaa.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,67 @@
package uaa

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"

"github.com/pkg/errors"

"github.com/vmware-tanzu/tanzu-cli/pkg/auth/common"
"github.com/vmware-tanzu/tanzu-cli/pkg/constants"
"github.com/vmware-tanzu/tanzu-cli/pkg/interfaces"
)

var (
httpRestClient interfaces.HTTPClient
)

// GetAccessTokenFromAPIToken fetches access token using the API-token.
func GetAccessTokenFromAPIToken(apiToken, uaaEndpoint, endpointCACertPath string, skipTLSVerify bool) (*common.Token, error) {
tokenURL := getIssuerEndpoints(uaaEndpoint).TokenURL
data := url.Values{}
data.Set("refresh_token", apiToken)
data.Set("client_id", tanzuCLIClientIDExtended)
data.Set("grant_type", "refresh_token")

req, _ := http.NewRequestWithContext(context.Background(), "POST", tokenURL, bytes.NewBufferString(data.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

if httpRestClient == nil {
tlsConfig := common.GetTLSConfig(uaaEndpoint, endpointCACertPath, skipTLSVerify)
if tlsConfig == nil {
return nil, errors.New("unable to set up tls config")
}

tr := http.DefaultTransport.(*http.Transport).Clone()
tr.TLSClientConfig = tlsConfig
httpRestClient = &http.Client{Transport: tr}
}

resp, err := httpRestClient.Do(req)
if err != nil {
return nil, errors.WithMessage(err, "Failed to obtain access token. Please provide valid API token")
}
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, errors.Errorf("Failed to obtain access token. Please provide valid API token -- %s", string(body))
}

defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
token := common.Token{}

if err = json.Unmarshal(body, &token); err != nil {
return nil, errors.Wrap(err, "could not unmarshal auth token")
}

return &token, nil
}

// GetTokens fetches the UAA access token
func GetTokens(refreshOrAPIToken, _, issuer, tokenType string) (*common.Token, error) {
var token *common.Token
Expand Down
46 changes: 44 additions & 2 deletions pkg/command/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -732,11 +732,53 @@ func getSelfManagedOrg(c *configtypes.Context) (string, string) {
return orgID, orgName
}

func globalTanzuLoginUAA(c *configtypes.Context, generateContextNameFunc func(orgName, endpoint string, isStaging bool) string) error {
func doUAAAPITokenAuthAndUpdateContext(c *configtypes.Context, uaaEndpoint, apiTokenValue string) (claims *commonauth.Claims, err error) {
token, err := uaa.GetAccessTokenFromAPIToken(apiTokenValue, uaaEndpoint, endpointCACertPath, skipTLSVerify)
if err != nil {
return nil, errors.Wrap(err, "failed to get token from UAA")
}
claims, err = commonauth.ParseToken(&oauth2.Token{AccessToken: token.AccessToken}, config.UAAIdpType)
if err != nil {
return nil, err
}

a := configtypes.GlobalServerAuth{}
a.Issuer = uaaEndpoint
a.UserName = claims.Username
a.Permissions = claims.Permissions
a.AccessToken = token.AccessToken
a.IDToken = token.IDToken
a.RefreshToken = apiTokenValue
a.Type = commonauth.APITokenType
expiresAt := time.Now().Local().Add(time.Second * time.Duration(token.ExpiresIn))
a.Expiration = expiresAt
c.GlobalOpts.Auth = a
if c.AdditionalMetadata == nil {
c.AdditionalMetadata = make(map[string]interface{})
}

return claims, nil
}

func doUAAAuthentication(c *configtypes.Context) (*commonauth.Claims, error) {
if c.AdditionalMetadata[config.TanzuAuthEndpointKey] == nil {
return nil, errors.New("auth endpoint not set")
}
uaaEndpoint := c.AdditionalMetadata[config.TanzuAuthEndpointKey].(string)
log.V(7).Infof("Login to UAA endpoint: %s", uaaEndpoint)

claims, err := doInteractiveLoginAndUpdateContext(c, uaaEndpoint)
apiTokenValue, ok := os.LookupEnv(config.EnvAPITokenKey)
// Use API Token login flow if TANZU_API_TOKEN environment variable is set, else fall back to interactive login flow
if ok {
log.Info("API token env var is set")
return doUAAAPITokenAuthAndUpdateContext(c, uaaEndpoint, apiTokenValue)
}

return doInteractiveLoginAndUpdateContext(c, uaaEndpoint)
}

func globalTanzuLoginUAA(c *configtypes.Context, generateContextNameFunc func(orgName, endpoint string, isStaging bool) string) error {
claims, err := doUAAAuthentication(c)
if err != nil {
return err
}
Expand Down

0 comments on commit 45623eb

Please sign in to comment.