-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support to check if the selected org is Tanzu Application platfor…
…m enabled - Added support to show warning to users if the CSP token received through login doesn't contain the necessary TAP scopes. The TAP scopes are fetched from the CLI central configuration file and can be modified without releasing new CLI version. - User can skip the TAP scopes validation on "tanzu" context using the environment variable "TANZU_CLI_SKIP_TAP_SCOPES_VALIDATION_ON_TANZU_CONTEXT" - Updated the local test central repo with updated central config file Signed-off-by: Prem Kumar Kalle <[email protected]>
- Loading branch information
Showing
9 changed files
with
196 additions
and
3 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// Copyright 2024 VMware, Inc. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package command | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
|
||
"github.com/vmware-tanzu/tanzu-plugin-runtime/config" | ||
"github.com/vmware-tanzu/tanzu-plugin-runtime/log" | ||
|
||
"github.com/vmware-tanzu/tanzu-cli/pkg/auth/csp" | ||
"github.com/vmware-tanzu/tanzu-cli/pkg/centralconfig" | ||
cliconfig "github.com/vmware-tanzu/tanzu-cli/pkg/config" | ||
"github.com/vmware-tanzu/tanzu-cli/pkg/constants" | ||
) | ||
|
||
const centralConfigTanzuApplicationPlatformScopesKey = "cli.core.tanzu_application_platform_scopes" | ||
|
||
type tapScope struct { | ||
Scope string `yaml:"scope" json:"scope"` | ||
} | ||
|
||
type tapScopesGetter func() ([]string, error) | ||
|
||
// validateTokenForTAPScopes validates if the token claims contains at least one of the TAP scopes listed in | ||
// the central configuration. If the central configuration doesn't have any TAP scopes listed, it will return success. | ||
// It will skip the validation and return success if TANZU_CLI_SKIP_TANZU_CONTEXT_TAP_SCOPES_VALIDATION environment | ||
// variable is set to true | ||
func validateTokenForTAPScopes(claims *csp.Claims, scopesGetter tapScopesGetter) (bool, error) { | ||
if skipTAPScopeValidation, _ := strconv.ParseBool(os.Getenv(constants.SkipTAPScopesValidationOnTanzuContext)); skipTAPScopeValidation { | ||
return true, nil | ||
} | ||
if scopesGetter == nil { | ||
scopesGetter = getTAPScopesFromCentralConfig | ||
} | ||
tapScopes, err := scopesGetter() | ||
if err != nil { | ||
log.V(7).Error(err, "error retrieving TAP scopes from the central config") | ||
return false, errors.Wrap(err, "error retrieving TAP scopes from the central config") | ||
} | ||
|
||
// validate only if we get the permissions/scopes from central configuration | ||
if len(tapScopes) == 0 { | ||
return true, nil | ||
} | ||
|
||
for _, tapScope := range tapScopes { | ||
for _, perm := range claims.Permissions { | ||
tapScopeSuffix := fmt.Sprintf("/%s", tapScope) | ||
if strings.HasSuffix(perm, tapScopeSuffix) { | ||
return true, nil | ||
} | ||
} | ||
} | ||
|
||
return false, nil | ||
} | ||
|
||
func getTAPScopesFromCentralConfig() ([]string, error) { | ||
// We will get the central configuration from the default discovery source | ||
discoverySource, err := config.GetCLIDiscoverySource(cliconfig.DefaultStandaloneDiscoveryName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Get the Tanzu Application Platform scopes from the central configuration | ||
reader := centralconfig.NewCentralConfigReader(discoverySource) | ||
var tapScopes []tapScope | ||
err = reader.GetCentralConfigEntry(centralConfigTanzuApplicationPlatformScopesKey, &tapScopes) | ||
if err != nil { | ||
// If the key is not found in the central config, it does not return an error because some central repositories | ||
// may choose not to have a central config file. | ||
var keyNotFoundError *centralconfig.KeyNotFoundError | ||
if errors.As(err, &keyNotFoundError) { | ||
return nil, nil | ||
} | ||
return nil, err | ||
} | ||
// extract the scope names | ||
var scopeNames []string | ||
for _, ts := range tapScopes { | ||
scopeNames = append(scopeNames, ts.Scope) | ||
} | ||
return scopeNames, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Copyright 2024 VMware, Inc. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package command | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
|
||
"github.com/vmware-tanzu/tanzu-cli/pkg/auth/csp" | ||
) | ||
|
||
func TestValidateTokenForTanzuScopes(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
claims *csp.Claims | ||
scopesGetter tapScopesGetter | ||
expectedBool bool | ||
expectedErr error | ||
}{ | ||
{ | ||
name: "When the central config does not have any TAP scopes listed, it should return success.", | ||
claims: &csp.Claims{Permissions: []string{}}, | ||
scopesGetter: func() ([]string, error) { return []string{}, nil }, | ||
expectedBool: true, | ||
expectedErr: nil, | ||
}, | ||
{ | ||
name: "When the token has at least one of the specified TAP scopes listed in central config, it should return success", | ||
claims: &csp.Claims{Permissions: []string{"external/UID-123-567/matching-scope", "csp:org_admin", "csp:developer"}}, | ||
scopesGetter: func() ([]string, error) { | ||
return []string{"matching-scope", "matching-another-scope"}, nil | ||
}, | ||
expectedBool: true, | ||
expectedErr: nil, | ||
}, | ||
{ | ||
name: "When the token lacks at least one of the specified TAP scopes listed in the central config, it should return an error", | ||
claims: &csp.Claims{Permissions: []string{"external/UID-123-567/non-matching-scope", "csp:org_member", "csp:software_installer"}}, | ||
scopesGetter: func() ([]string, error) { | ||
return []string{"matching-scope", "matching-another-scope"}, nil | ||
}, | ||
expectedBool: false, | ||
expectedErr: nil, | ||
}, | ||
{ | ||
name: "It should return an error if fetching the TAP scopes from the central config fails", | ||
claims: &csp.Claims{}, | ||
scopesGetter: func() ([]string, error) { | ||
return nil, errors.New("error retrieving TAP scopes") | ||
}, | ||
expectedBool: false, | ||
expectedErr: errors.New("error retrieving TAP scopes form the central config: error retrieving TAP scopes"), | ||
}, | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.name, func(t *testing.T) { | ||
actualBool, actualErr := validateTokenForTAPScopes(tc.claims, tc.scopesGetter) | ||
if actualBool != tc.expectedBool { | ||
t.Errorf("Test case %s failed: Expected bool value %t, but got %t", tc.name, tc.expectedBool, actualBool) | ||
} | ||
if actualErr != nil && tc.expectedErr != nil && actualErr.Error() != tc.expectedErr.Error() { | ||
t.Errorf("Test case %s failed: Expected error %v, but got %v", tc.name, tc.expectedErr, actualErr) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters