diff --git a/pkg/auth/common/login_handler.go b/pkg/auth/common/login_handler.go index 432cf1e3b..8903963bb 100644 --- a/pkg/auth/common/login_handler.go +++ b/pkg/auth/common/login_handler.go @@ -432,24 +432,28 @@ func (h *TanzuLoginHandler) promptAndLoginWithAuthCode(ctx context.Context, auth // of if persisted cert information associated with the issuer endpoint is found, // with the provided information taking precedence over persisted information. func (h *TanzuLoginHandler) getTLSConfig() *tls.Config { + return GetTLSConfig(h.issuer, h.caCertData, h.tlsSkipVerify) +} + +func GetTLSConfig(endpoint, certData string, skipVerify bool) *tls.Config { var savedCertData string var savedSkipVerify bool - c, _ := config.GetCert(h.issuer) + c, _ := config.GetCert(endpoint) if c != nil { savedCertData = c.CACertData savedSkipVerify, _ = strconv.ParseBool(c.SkipCertVerify) } - if savedSkipVerify || h.tlsSkipVerify { + if savedSkipVerify || skipVerify { //nolint:gosec // skipTLSVerify: true is only possible if the user has explicitly enabled it return &tls.Config{InsecureSkipVerify: true, MinVersion: tls.VersionTLS12} } caCertData := savedCertData - if h.caCertData != "" { - caCertData = h.caCertData + if certData != "" { + caCertData = certData } if caCertData != "" { @@ -458,7 +462,7 @@ func (h *TanzuLoginHandler) getTLSConfig() *tls.Config { decodedCACertData, err := base64.StdEncoding.DecodeString(caCertData) if err != nil { - log.Infof("unable to use custom cert for '%s' endpoint. Error: %s", h.issuer, err.Error()) + log.Infof("unable to use custom cert for '%s' endpoint. Error: %s", endpoint, err.Error()) return nil } @@ -468,7 +472,7 @@ func (h *TanzuLoginHandler) getTLSConfig() *tls.Config { } if ok := pool.AppendCertsFromPEM(decodedCACertData); !ok { - log.Infof("unable to use custom cert for %s endpoint", h.issuer) + log.Infof("unable to use custom cert for %s endpoint", endpoint) return nil } return &tls.Config{RootCAs: pool, MinVersion: tls.VersionTLS12} diff --git a/pkg/command/context.go b/pkg/command/context.go index f02c4829e..7d45b6305 100644 --- a/pkg/command/context.go +++ b/pkg/command/context.go @@ -649,31 +649,101 @@ func globalTanzuLogin(c *configtypes.Context, generateContextNameFunc func(orgNa return errors.New(invalidIdpType) } -func globalTanzuLoginUAA(c *configtypes.Context, generateContextNameFunc func(orgName, endpoint string, isStaging bool) string) error { - uaaEndpoint := c.AdditionalMetadata[config.TanzuAuthEndpointKey].(string) - log.V(7).Infof("Login to UAA endpoint: %s", uaaEndpoint) +type DeploymentMetadata struct { + APIEndpoint string `json:"apiEndpoint"` + Organization DeploymentMetadataOrganization `json:"organization"` +} - claims, err := doInteractiveLoginAndUpdateContext(c, uaaEndpoint) +type DeploymentMetadataOrganization struct { + DisplayName string `json:"displayName"` + ID string `json:"id"` + Name string `json:"name"` +} + +func getOrgFromSelfManagedEndpoint(c *configtypes.Context) (string, string, error) { + val, ok := c.AdditionalMetadata[config.TanzuHubEndpointKey] + if !ok { + return "", "", errors.New("Hub endpoint not set") + } + hubEndpoint := strings.TrimRight(val.(string), " /") + metadataURL := hubEndpoint + "/assets/env-config/env-config.json" + req, _ := http.NewRequest("GET", metadataURL, http.NoBody) //nolint:noctx + + tlsConfig := commonauth.GetTLSConfig(hubEndpoint, "", false) + client := &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + TLSClientConfig: tlsConfig, + }, + Timeout: time.Second * 10, + } + + response, err := client.Do(req) if err != nil { - return err + return "", "", errors.Wrap(err, "failed to get tanzu platform deployment metadata") + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + return "", "", errors.New("failed to get tanzu platform deployment metadata") + } + + responseBody, err := io.ReadAll(response.Body) + if err != nil { + return "", "", errors.Wrap(err, "failed to read the response body") + } + + var dm DeploymentMetadata + err = json.Unmarshal(responseBody, &dm) + if err != nil { + return "", "", errors.Wrap(err, "error parsing http response body ") } - // UAA-based authentication does not provide org id or name yet. - // Note: org id/name may be discoverable in UAA-based auth in the future. + return dm.Organization.ID, dm.Organization.Name, nil +} + +func getSelfManagedOrg(c *configtypes.Context) (string, string) { + // UAA-based authentication itself not provide org id or name. + // Instead they are retrievable via a predefined location var orgID string orgName := "self-managed" + + retrievedOrgID, retrievedOrgName, err := getOrgFromSelfManagedEndpoint(c) + if err == nil && retrievedOrgID != "" { + orgID = retrievedOrgID + if retrievedOrgName != "" { + orgName = retrievedOrgName + } + } + uaaOrgIDValue, ok := os.LookupEnv(constants.UAALoginOrgID) if ok { + if retrievedOrgID != "" && retrievedOrgID != uaaOrgIDValue { + log.Infof("Organization ID %s retrieved from endpoint is being overridden by environment variable value %s\n", + retrievedOrgID, uaaOrgIDValue) + } orgID = uaaOrgIDValue } - claims.OrgID = orgID uaaOrgNameValue, ok := os.LookupEnv(constants.UAALoginOrgName) if ok { orgName = uaaOrgNameValue - } else if orgID != "" { - orgName = orgID } + return orgID, orgName +} + +func globalTanzuLoginUAA(c *configtypes.Context, generateContextNameFunc func(orgName, endpoint string, isStaging bool) string) error { + uaaEndpoint := c.AdditionalMetadata[config.TanzuAuthEndpointKey].(string) + log.V(7).Infof("Login to UAA endpoint: %s", uaaEndpoint) + + claims, err := doInteractiveLoginAndUpdateContext(c, uaaEndpoint) + if err != nil { + return err + } + + orgID, orgName := getSelfManagedOrg(c) + claims.OrgID = orgID + if err := updateContextOnTanzuLogin(c, generateContextNameFunc, claims, orgName); err != nil { return err } diff --git a/pkg/command/context_test.go b/pkg/command/context_test.go index 2ce128603..9bb30a419 100644 --- a/pkg/command/context_test.go +++ b/pkg/command/context_test.go @@ -6,6 +6,8 @@ import ( "bytes" "encoding/json" "fmt" + "net/http" + "net/http/httptest" "os" "path/filepath" "strings" @@ -1280,6 +1282,86 @@ var _ = Describe("testing context use", func() { }) }) +var _ = Describe("Test get org information", func() { + var ( + err error + serverGoodData *httptest.Server + serverBadData *httptest.Server + tanzuContext *configtypes.Context + ) + const ( + fakeContextName = "fake-context" + fakeAccessToken = "fake-access-token" + fakeEndpoint = "fake.tanzu.cloud.vmware.com" + fakeIssuer = "https://fake.issuer.come/auth" + ) + BeforeEach(func() { + tanzuContext = &configtypes.Context{ + Name: fakeContextName, + ContextType: configtypes.ContextTypeTanzu, + AdditionalMetadata: map[string]interface{}{}, + GlobalOpts: &configtypes.GlobalServer{ + Endpoint: fakeEndpoint, + Auth: configtypes.GlobalServerAuth{ + AccessToken: fakeAccessToken, + Issuer: fakeIssuer, + Type: common.APITokenType, + }, + }, + } + + serverGoodData = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"apiEndpoint":"/hub/graphql","apiHost":"","organization":{"displayName":"TPSM","id":"c4558dfb-18ae-480a-af06-8222600b198f","name":"Test Org"}}`)) + })) + serverBadData = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"badkey":"badvalue"}`)) + })) + }) + + It("should return org information from metadata file if found but env vars if set", func() { + tanzuContext.AdditionalMetadata[config.TanzuHubEndpointKey] = serverGoodData.URL + err = config.SetContext(tanzuContext, false) + Expect(err).To(BeNil()) + + orgID, orgName := getSelfManagedOrg(tanzuContext) + Expect(orgID).To(Equal("c4558dfb-18ae-480a-af06-8222600b198f")) + Expect(orgName).To(Equal("Test Org")) + + os.Setenv(constants.UAALoginOrgID, "11111111-1111-1111-1111-111111111111") + os.Setenv(constants.UAALoginOrgName, "OverwrittenOrgName") + orgID, orgName = getSelfManagedOrg(tanzuContext) + Expect(orgID).To(Equal("11111111-1111-1111-1111-111111111111")) + Expect(orgName).To(Equal("OverwrittenOrgName")) + defer os.Unsetenv(constants.UAALoginOrgID) + defer os.Unsetenv(constants.UAALoginOrgName) + }) + + It("should return default org information or on invalid metadata or env vars if set", func() { + tanzuContext.AdditionalMetadata[config.TanzuHubEndpointKey] = serverBadData.URL + err = config.SetContext(tanzuContext, false) + Expect(err).To(BeNil()) + + orgID, orgName := getSelfManagedOrg(tanzuContext) + Expect(orgID).To(Equal("")) + Expect(orgName).To(Equal("self-managed")) + + os.Setenv(constants.UAALoginOrgID, "11111111-2222-2222-2222-222222222222") + os.Setenv(constants.UAALoginOrgName, "OverwrittenOrgName") + orgID, orgName = getSelfManagedOrg(tanzuContext) + Expect(orgID).To(Equal("11111111-2222-2222-2222-222222222222")) + Expect(orgName).To(Equal("OverwrittenOrgName")) + defer os.Unsetenv(constants.UAALoginOrgID) + defer os.Unsetenv(constants.UAALoginOrgName) + }) + + AfterEach(func() { + serverGoodData.Close() + serverBadData.Close() + }) +}) + func TestCompletionContext(t *testing.T) { ctxK8s1 := &configtypes.Context{ Name: "tkg1",