Skip to content

Commit

Permalink
add skip-permissions-validation flag (#671)
Browse files Browse the repository at this point in the history
* add skip-permissions-validation flag and auto skip for fine-grained token

* fix bbs validate check

* if using new fine-grained token throw and error to use skip permission flag

* if using new fine-grained token throw and error to use skip permission flag

* bump

* wip

* auto skip permissions validation for upgrade and uninstall

* auto skip permissions validation for upgrade and uninstall

* mark flag hidden

* bump

* resolve conflicts
  • Loading branch information
kim-codefresh authored Feb 8, 2023
1 parent f1ac2ab commit 05bc518
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 16 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION=v0.1.30
VERSION=v0.1.31

OUT_DIR=dist
YEAR?=$(shell date +"%Y")
Expand Down
9 changes: 7 additions & 2 deletions cmd/commands/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ func getValueFromUserInput(label, defaultValue string, validate promptui.Validat
}

// ensureGitRuntimeToken gets the runtime token from the user (if !silent), and verifys it with he provider (if available)
func ensureGitRuntimeToken(cmd *cobra.Command, gitProvider cfgit.Provider, cloneOpts *apgit.CloneOptions) error {
func ensureGitRuntimeToken(cmd *cobra.Command, gitProvider cfgit.Provider, cloneOpts *apgit.CloneOptions, skipPermissionsValidation bool) error {
ctx := cmd.Context()
errMessage := "Value stored in environment variable GIT_TOKEN is invalid; enter a valid runtime token: %w"
if cloneOpts.Auth.Password == "" && !store.Get().Silent {
Expand All @@ -292,7 +292,12 @@ func ensureGitRuntimeToken(cmd *cobra.Command, gitProvider cfgit.Provider, clone
}

if gitProvider != nil {
err := gitProvider.VerifyRuntimeToken(ctx, cloneOpts.Auth)
var err error
if skipPermissionsValidation {
err = gitProvider.ValidateToken(ctx, cloneOpts.Auth)
} else {
err = gitProvider.VerifyRuntimeToken(ctx, cloneOpts.Auth)
}
if err != nil {
// in case when we get invalid value from env variable TOKEN we clean
cloneOpts.Auth.Password = ""
Expand Down
4 changes: 2 additions & 2 deletions cmd/commands/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func runtimeUninstallCommandPreRunHandler(cmd *cobra.Command, args []string, opt
}

if !opts.Managed {
err = ensureGitRuntimeToken(cmd, nil, opts.CloneOpts)
err = ensureGitRuntimeToken(cmd, nil, opts.CloneOpts, true)
}

handleCliStep(reporter.UninstallStepPreCheckEnsureGitToken, "Getting git token", err, true, false)
Expand Down Expand Up @@ -230,7 +230,7 @@ func runtimeUpgradeCommandPreRunHandler(cmd *cobra.Command, args []string, opts
return err
}

err = ensureGitRuntimeToken(cmd, nil, opts.CloneOpts)
err = ensureGitRuntimeToken(cmd, nil, opts.CloneOpts, true)
handleCliStep(reporter.UpgradeStepPreCheckEnsureGitToken, "Getting git token", err, true, false)
if err != nil {
return err
Expand Down
22 changes: 13 additions & 9 deletions cmd/commands/runtime_install.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,14 @@ type (
SkipIngress bool
BypassIngressClassCheck bool

versionStr string
kubeContext string
kubeconfig string
gitProvider cfgit.Provider
useGatewayAPI bool
featuresToInstall []runtime.InstallFeature
runtimeDef string
versionStr string
kubeContext string
kubeconfig string
gitProvider cfgit.Provider
useGatewayAPI bool
featuresToInstall []runtime.InstallFeature
runtimeDef string
SkipPermissionsValidation bool
}

CreateIngressOptions struct {
Expand Down Expand Up @@ -278,6 +279,7 @@ func NewRuntimeInstallCommand() *cobra.Command {
cmd.Flags().StringVar(&installationOpts.TunnelRegisterHost, "tunnel-register-host", "register-tunnels.cf-cd.com", "The host name for registering a new tunnel")
cmd.Flags().StringVar(&installationOpts.TunnelDomain, "tunnel-domain", "tunnels.cf-cd.com", "The base domain for the tunnels")
cmd.Flags().StringVar(&installationOpts.IpsAllowList, "ips-allow-list", "", "lists the rules to configure which IP addresses (IPv4/IPv6) and subnet masks can access your client (e.g \"192.168.0.0/16, FE80:CD00:0000:0CDE:1257::/64\")")
cmd.Flags().BoolVar(&installationOpts.SkipPermissionsValidation, "skip-permissions-validation", false, "Skip personal access token permissions validation (default: false)")

installationOpts.InsCloneOpts = apu.AddCloneFlags(cmd, &apu.CloneFlagsOptions{
CreateIfNotExist: true,
Expand All @@ -299,6 +301,7 @@ func NewRuntimeInstallCommand() *cobra.Command {
util.Die(cmd.Flags().MarkHidden("ips-allow-list"))
util.Die(cmd.Flags().MarkHidden("runtime-def"))
util.Die(cmd.Flags().MarkHidden("set-default-resources"))
util.Die(cmd.Flags().MarkHidden("skip-permissions-validation"))
cmd.MarkFlagsMutuallyExclusive("runtime-def", "version")
cmd.MarkFlagsMutuallyExclusive("runtime-def", "set-default-resources")

Expand Down Expand Up @@ -467,10 +470,10 @@ func getGitToken(cmd *cobra.Command, opts *RuntimeInstallOptions) error {
var err error

if store.Get().Silent {
err = ensureGitRuntimeToken(cmd, opts.gitProvider, opts.InsCloneOpts)
err = ensureGitRuntimeToken(cmd, opts.gitProvider, opts.InsCloneOpts, opts.SkipPermissionsValidation)
} else {
handleValidationFailsWithRepeat(func() error {
err = ensureGitRuntimeToken(cmd, opts.gitProvider, opts.InsCloneOpts)
err = ensureGitRuntimeToken(cmd, opts.gitProvider, opts.InsCloneOpts, opts.SkipPermissionsValidation)
if isValidationError(err) {
fmt.Println(err)
return err
Expand Down Expand Up @@ -1548,6 +1551,7 @@ func configureAppProxy(ctx context.Context, opts *RuntimeInstallOptions, rt *run
fmt.Sprintf("cfHost=%s", cfConfig.GetCurrentContext().URL),
fmt.Sprintf("cors=%s", cfConfig.GetCurrentContext().URL),
"env=production",
fmt.Sprintf("skipPermissionsValidation=%v", opts.SkipPermissionsValidation),
}

// configure codefresh host
Expand Down
1 change: 1 addition & 0 deletions pkg/git/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type (
Type() ProviderType
VerifyRuntimeToken(ctx context.Context, auth apgit.Auth) error
VerifyUserToken(ctx context.Context, auth apgit.Auth) error
ValidateToken(ctx context.Context, auth apgit.Auth) error
}
)

Expand Down
9 changes: 9 additions & 0 deletions pkg/git/provider_bitbucket-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ func (bbs *bitbucketServer) Type() ProviderType {
return bbs.providerType
}

func (bbs *bitbucketServer) ValidateToken(ctx context.Context, auth apgit.Auth) error {
_, err := bbs.getCurrentUsername(ctx, auth.Password)
if err != nil {
return fmt.Errorf("failed validate token: %w", err)
}

return nil
}

func (bbs *bitbucketServer) VerifyRuntimeToken(ctx context.Context, auth apgit.Auth) error {
return bbs.checkProjectAdminPermission(ctx, auth.Password)
}
Expand Down
18 changes: 18 additions & 0 deletions pkg/git/provider_bitbucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,24 @@ func (bb *bitbucket) VerifyUserToken(ctx context.Context, auth apgit.Auth) error
return bb.verifyToken(ctx, auth.Password, auth.Username, patScopes)
}

func (bb *bitbucket) ValidateToken(ctx context.Context, auth apgit.Auth) error {
if auth.Username == "" {
return fmt.Errorf("user name is require for bitbucket cloud request")
}

res, err := bb.request(ctx, auth.Username, auth.Password, http.MethodHead, "user", nil)
if err != nil {
return fmt.Errorf("failed getting current user: %w", err)
}

defer res.Body.Close()

if res.StatusCode == 401 {
return fmt.Errorf("invalid token")
}
return nil
}

func (bb *bitbucket) verifyToken(ctx context.Context, token string, username string, requiredScopes [][]string) error {
scopes, err := bb.getCurrentUserScopes(ctx, token, username)
if err != nil {
Expand Down
58 changes: 56 additions & 2 deletions pkg/git/provider_github.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,16 @@ func (g *github) Type() ProviderType {
}

func (g *github) VerifyRuntimeToken(ctx context.Context, auth apgit.Auth) error {
err := g.verifyToken(ctx, auth.Password, runtime_token_scopes)
tokenType, err := g.getTokenType(auth.Password)
if err != nil {
return fmt.Errorf("failed getting token type: %w", err)
}

if tokenType == "fine-grained" {
return fmt.Errorf("validation for github fine-grained PAT is not supported yet, please retry with --skip-permissions-validation or use a classic token")
}

err = g.verifyToken(ctx, auth.Password, runtime_token_scopes)
if err != nil {
return fmt.Errorf("git-token invalid: %w", err)
}
Expand All @@ -94,14 +103,59 @@ func (g *github) VerifyRuntimeToken(ctx context.Context, auth apgit.Auth) error
}

func (g *github) VerifyUserToken(ctx context.Context, auth apgit.Auth) error {
err := g.verifyToken(ctx, auth.Password, user_token_scopes)
tokenType, err := g.getTokenType(auth.Password)
if err != nil {
return fmt.Errorf("failed getting token type: %w", err)
}

if tokenType == "fine-grained" {
return fmt.Errorf("validation for github fine-grained PAT is not supported yet, please retry with --skip-permissions-validation or use a classic token")
}

err = g.verifyToken(ctx, auth.Password, user_token_scopes)
if err != nil {
return fmt.Errorf("personal-git-token invalid: %w", err)
}

return nil
}

func (g *github) ValidateToken(ctx context.Context, auth apgit.Auth) error {
if auth.Password == "" {
return fmt.Errorf("user name is require for bitbucket cloud request")
}

reqHeaders := map[string]string{
"Authorization": "token " + auth.Password,
}
req, err := httputil.NewRequest(ctx, http.MethodHead, g.apiURL.String(), reqHeaders, nil)
if err != nil {
return err
}

res, err := g.c.Do(req)
if err != nil {
return err
}
defer res.Body.Close()

if res.StatusCode == 401 {
return fmt.Errorf("invalid token")
}
return nil

}

func (g *github) getTokenType(token string) (string, error) {
if token == "" {
return "", errors.New("missing token")
}
if strings.HasPrefix(token, "github_pat") {
return "fine-grained", nil
}
return "classic", nil
}

func (g *github) verifyToken(ctx context.Context, token string, requiredScopes []string) error {
reqHeaders := map[string]string{
"Authorization": "token " + token,
Expand Down
19 changes: 19 additions & 0 deletions pkg/git/provider_gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,25 @@ func (g *gitlab) VerifyUserToken(ctx context.Context, auth apgit.Auth) error {
return g.checkReadRepositoryScope(ctx, auth.Password)
}

func (g *gitlab) ValidateToken(ctx context.Context, auth apgit.Auth) error {
if auth.Password == "" {
return fmt.Errorf("user name is require for bitbucket cloud request")
}

res, err := g.request(ctx, auth.Password, http.MethodHead, "user")

if err != nil {
return fmt.Errorf("failed getting user: %w", err)
}

defer res.Body.Close()

if res.StatusCode == 401 {
return fmt.Errorf("invalid token")
}
return nil
}

// POST to projects without a body.
// if it returns 400 - the token has "api" scope
// otherwise - the token does not have the scope
Expand Down

0 comments on commit 05bc518

Please sign in to comment.