From 98bc8e37e5c8319a0f2301731fe763ec8a5d8898 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 7 Sep 2023 17:48:36 +0300 Subject: [PATCH 1/7] Support Platform Access Token --- access_test.go | 153 ++++++++- artifactory/cli.go | 23 +- artifactory_test.go | 2 +- docs/artifactory/accesstokencreate/help.go | 8 +- docs/general/token/help.go | 15 + general/token/cli.go | 114 +++++++ go.mod | 4 +- go.sum | 8 +- main.go | 26 +- main_test.go | 2 +- utils/accesstoken/utils.go | 15 + utils/cliutils/cli_consts.go | 2 +- utils/cliutils/commandsflags.go | 347 +++++++++++++-------- 13 files changed, 543 insertions(+), 176 deletions(-) create mode 100644 docs/general/token/help.go create mode 100644 general/token/cli.go create mode 100644 utils/accesstoken/utils.go diff --git a/access_test.go b/access_test.go index 919eba11c..d985fa5d7 100644 --- a/access_test.go +++ b/access_test.go @@ -3,18 +3,25 @@ package main import ( "encoding/base64" "encoding/json" + "fmt" "github.com/jfrog/jfrog-cli-core/v2/common/commands" - coreenvsetup "github.com/jfrog/jfrog-cli-core/v2/general/envsetup" + coreEnvSetup "github.com/jfrog/jfrog-cli-core/v2/general/envsetup" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" - coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" + coreTests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" "github.com/jfrog/jfrog-cli/utils/tests" "github.com/jfrog/jfrog-client-go/auth" + clientUtils "github.com/jfrog/jfrog-client-go/utils" clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/stretchr/testify/assert" "testing" ) +var ( + accessDetails *config.ServerDetails + accessCli *tests.JfrogCli +) + func initAccessTest(t *testing.T) { if !*tests.TestAccess { t.Skip("Skipping Access test. To run Access test add the '-test.access=true' option.") @@ -23,13 +30,13 @@ func initAccessTest(t *testing.T) { func TestSetupInvitedUser(t *testing.T) { initAccessTest(t) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) + tempDirPath, createTempDirCallback := coreTests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() setEnvCallBack := clientTestUtils.SetEnvWithCallbackAndAssert(t, coreutils.HomeDir, tempDirPath) defer setEnvCallBack() - serverDetails := &config.ServerDetails{Url: *tests.JfrogUrl, AccessToken: *tests.JfrogAccessToken} - encodedCred := encodeConnectionDetails(serverDetails, t) - setupCmd := coreenvsetup.NewEnvSetupCommand().SetEncodedConnectionDetails(encodedCred) + setupServerDetails := &config.ServerDetails{Url: *tests.JfrogUrl, AccessToken: *tests.JfrogAccessToken} + encodedCred := encodeConnectionDetails(setupServerDetails, t) + setupCmd := coreEnvSetup.NewEnvSetupCommand().SetEncodedConnectionDetails(encodedCred) suffix := setupCmd.SetupAndConfigServer() assert.Empty(t, suffix) configs, err := config.GetAllServersConfigs() @@ -54,7 +61,7 @@ func TestRefreshableAccessTokens(t *testing.T) { initAccessTest(t) server := &config.ServerDetails{Url: *tests.JfrogUrl, AccessToken: *tests.JfrogAccessToken} - err := coreenvsetup.GenerateNewLongTermRefreshableAccessToken(server) + err := coreEnvSetup.GenerateNewLongTermRefreshableAccessToken(server) assert.NoError(t, err) assert.NotEmpty(t, server.RefreshToken) configCmd := commands.NewConfigCommand(commands.AddOrEdit, tests.ServerId).SetDetails(server).SetInteractive(false) @@ -115,3 +122,135 @@ func getAccessTokensFromConfig(t *testing.T, serverId string) (accessToken, refr } return details.AccessToken, details.RefreshToken, nil } + +const ( + userScope = "applied-permissions/user" + defaultExpiry = 31536000 +) + +func TestAccessTokenCreate(t *testing.T) { + initAccessTest(t) + if *tests.JfrogAccessToken == "" { + t.Skip("access token create command only supports authorization with access token, but a token is not provided. Skipping...") + } + testCases := []struct { + name string + args []string + shouldExpire bool + expectedExpiry uint + expectedScope string + expectedRefreshable bool + expectedReference bool + }{ + { + name: "default", + args: []string{"atc"}, + shouldExpire: true, + expectedExpiry: defaultExpiry, + expectedScope: userScope, + expectedRefreshable: false, + expectedReference: false, + }, + { + name: "explicit user, no expiry", + args: []string{"atc", auth.ExtractUsernameFromAccessToken(*tests.JfrogAccessToken), "--expiry=0"}, + shouldExpire: false, + expectedExpiry: 0, + expectedScope: userScope, + expectedRefreshable: false, + expectedReference: false, + }, + { + name: "refreshable, admin", + args: []string{"atc", "--refreshable", "--grant-admin"}, + shouldExpire: true, + expectedExpiry: defaultExpiry, + expectedScope: "applied-permissions/admin", + expectedRefreshable: true, + expectedReference: false, + }, + { + name: "reference, custom scope, custom expiry", + args: []string{"atc", "--reference", "--scope=system:metrics:r", "--expiry=1234"}, + shouldExpire: true, + expectedExpiry: 1234, + expectedScope: "system:metrics:r", + expectedRefreshable: false, + expectedReference: true, + }, + { + name: "groups, description", + args: []string{"atc", "--groups=group1,group2", "--description=description"}, + shouldExpire: true, + expectedExpiry: defaultExpiry, + expectedScope: "applied-permissions/groups:group1,group2", + expectedRefreshable: false, + expectedReference: false, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + var token auth.CreateTokenResponseData + output := accessCli.RunCliCmdWithOutput(t, test.args...) + assert.NoError(t, json.Unmarshal([]byte(output), &token)) + + if test.shouldExpire { + assert.EqualValues(t, test.expectedExpiry, *token.ExpiresIn) + } else { + assert.Nil(t, token.ExpiresIn) + } + assert.NotEmpty(t, token.AccessToken) + assert.Equal(t, test.expectedScope, token.Scope) + assertNotEmptyIfExpected(t, test.expectedRefreshable, token.RefreshToken) + assertNotEmptyIfExpected(t, test.expectedReference, token.ReferenceToken) + + // Try pinging Artifactory with the new token. + assert.NoError(t, tests.NewJfrogCli(execMain, "jfrog rt", + "--url="+*tests.JfrogUrl+tests.ArtifactoryEndpoint+" --access-token="+token.AccessToken).Exec("ping")) + }) + } +} + +func assertNotEmptyIfExpected(t *testing.T, expected bool, output string) { + if expected { + assert.NotEmpty(t, output) + } else { + assert.Empty(t, output) + } +} + +func initAccessCli() { + if accessCli != nil { + return + } + accessCli = tests.NewJfrogCli(execMain, "jfrog", authenticateAccess()) +} + +func InitAccessTests() { + initArtifactoryCli() + initAccessCli() + cleanUpOldBuilds() + cleanUpOldRepositories() + cleanUpOldUsers() + tests.AddTimestampToGlobalVars() + createRequiredRepos() + cleanArtifactoryTest() +} + +func authenticateAccess() string { + *tests.JfrogUrl = clientUtils.AddTrailingSlashIfNeeded(*tests.JfrogUrl) + accessDetails = &config.ServerDetails{ + AccessUrl: *tests.JfrogUrl + tests.AccessEndpoint} + + cred := fmt.Sprintf("--url=%s", *tests.JfrogUrl) + if *tests.JfrogAccessToken != "" { + accessDetails.AccessToken = *tests.JfrogAccessToken + cred += fmt.Sprintf(" --access-token=%s", accessDetails.AccessToken) + } else { + accessDetails.User = *tests.JfrogUser + accessDetails.Password = *tests.JfrogPassword + cred += fmt.Sprintf(" --user=%s --password=%s", accessDetails.User, accessDetails.Password) + } + return cred +} diff --git a/artifactory/cli.go b/artifactory/cli.go index 1e02ab521..12a639464 100644 --- a/artifactory/cli.go +++ b/artifactory/cli.go @@ -3,6 +3,7 @@ package artifactory import ( "errors" "fmt" + "github.com/jfrog/jfrog-cli/utils/accesstoken" "os" "strconv" "strings" @@ -860,13 +861,13 @@ func GetCommands() []cli.Command { { Name: "access-token-create", Aliases: []string{"atc"}, - Flags: cliutils.GetCommandFlags(cliutils.AccessTokenCreate), + Flags: cliutils.GetCommandFlags(cliutils.ArtifactoryAccessTokenCreate), Usage: accesstokencreate.GetDescription(), HelpName: corecommon.CreateUsage("rt atc", accesstokencreate.GetDescription(), accesstokencreate.Usage), UsageText: accesstokencreate.GetArguments(), ArgsUsage: common.CreateEnvVars(), BashComplete: corecommon.CreateBashCompletionFunc(), - Action: accessTokenCreateCmd, + Action: artifactoryAccessTokenCreateCmd, }, { Name: "transfer-settings", @@ -2199,7 +2200,7 @@ func groupDeleteCmd(c *cli.Context) error { return commands.Exec(groupDeleteCmd) } -func accessTokenCreateCmd(c *cli.Context) error { +func artifactoryAccessTokenCreateCmd(c *cli.Context) error { if c.NArg() > 1 { return cliutils.WrongNumberOfArgumentsHandler(c) } @@ -2208,20 +2209,16 @@ func accessTokenCreateCmd(c *cli.Context) error { if err != nil { return err } - // If the username is provided as an argument, then it is used when creating the token. - // If not, then the configured username (or the value of the --user option) is used. - var userName string - if c.NArg() > 0 { - userName = c.Args().Get(0) - } else { - userName = serverDetails.GetUser() - } - expiry, err := cliutils.GetIntFlagValue(c, "expiry", cliutils.TokenExpiry) + + username := accesstoken.GetSubjectUsername(c, serverDetails) + expiry, err := cliutils.GetIntFlagValue(c, cliutils.Expiry, cliutils.ArtifactoryTokenExpiry) if err != nil { return err } accessTokenCreateCmd := generic.NewAccessTokenCreateCommand() - accessTokenCreateCmd.SetUserName(userName).SetServerDetails(serverDetails).SetRefreshable(c.Bool("refreshable")).SetExpiry(expiry).SetGroups(c.String("groups")).SetAudience(c.String("audience")).SetGrantAdmin(c.Bool("grant-admin")) + accessTokenCreateCmd.SetUserName(username).SetServerDetails(serverDetails). + SetRefreshable(c.Bool(cliutils.Refreshable)).SetExpiry(expiry).SetGroups(c.String(cliutils.Groups)). + SetAudience(c.String(cliutils.Audience)).SetGrantAdmin(c.Bool(cliutils.GrantAdmin)) err = commands.Exec(accessTokenCreateCmd) if err != nil { return err diff --git a/artifactory_test.go b/artifactory_test.go index 390961e66..573049659 100644 --- a/artifactory_test.go +++ b/artifactory_test.go @@ -5190,7 +5190,7 @@ func TestArtifactoryReplicationCreate(t *testing.T) { cleanArtifactoryTest() } -func TestAccessTokenCreate(t *testing.T) { +func TestArtifactoryAccessTokenCreate(t *testing.T) { initArtifactoryTest(t, "") buffer, _, previousLog := coretests.RedirectLogOutputToBuffer() diff --git a/docs/artifactory/accesstokencreate/help.go b/docs/artifactory/accesstokencreate/help.go index 22f57c693..f51c8e6f0 100644 --- a/docs/artifactory/accesstokencreate/help.go +++ b/docs/artifactory/accesstokencreate/help.go @@ -1,12 +1,12 @@ package accesstokencreate -var Usage = []string{"rt atc", "rt atc "} +var Usage = []string{"rt atc", "rt atc "} func GetDescription() string { - return "Creates an access token. By default an user-scoped token will be created, unless the --groups and/or --grant-admin options are specified." + return "Creates an Artifactory access token. By default an user-scoped token will be created, unless the --groups and/or --grant-admin options are specified." } func GetArguments() string { - return ` user name - The user name for which this token is created. If not specified, the token will be created for the current user.` + return ` username + The username for which this token is created. If not specified, the token will be created for the current user.` } diff --git a/docs/general/token/help.go b/docs/general/token/help.go new file mode 100644 index 000000000..a52830f1c --- /dev/null +++ b/docs/general/token/help.go @@ -0,0 +1,15 @@ +package token + +var Usage = []string{"atc", "atc "} + +func GetDescription() string { + return `Creates an access token. + By default, an user-scoped token will be created. + Administrator may provide a single of the following options to modify the scope of the token: + '--groups', '--grant-admin' or '--scope.'` +} + +func GetArguments() string { + return ` username + The username for which this token is created. If not specified, the token will be created for the current user.` +} diff --git a/general/token/cli.go b/general/token/cli.go new file mode 100644 index 000000000..5b099f1ba --- /dev/null +++ b/general/token/cli.go @@ -0,0 +1,114 @@ +package token + +import ( + "errors" + "fmt" + "github.com/jfrog/jfrog-cli-core/v2/common/commands" + generic "github.com/jfrog/jfrog-cli-core/v2/general/token" + coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" + "github.com/jfrog/jfrog-cli/utils/accesstoken" + "github.com/jfrog/jfrog-cli/utils/cliutils" + clientUtils "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/jfrog/jfrog-client-go/utils/log" + "github.com/urfave/cli" + "strconv" +) + +func AccessTokenCreateCmd(c *cli.Context) error { + if c.NArg() > 1 { + return cliutils.WrongNumberOfArgumentsHandler(c) + } + + serverDetails, err := createPlatformDetailsByFlags(c) + if err != nil { + return err + } + + if err = assertAccessTokenAvailable(serverDetails); err != nil { + return err + } + + if err = assertSingleScopeOptionProvided(c); err != nil { + return err + } + + username := accesstoken.GetSubjectUsername(c, serverDetails) + + expiry, err := getExpiry(c) + if err != nil { + return err + } + + accessTokenCreateCmd := generic.NewAccessTokenCreateCommand() + accessTokenCreateCmd. + SetServerDetails(serverDetails). + SetUsername(username). + SetProjectKey(c.String(cliutils.Project)). + SetGroups(c.String(cliutils.Groups)). + SetScope(c.String(cliutils.Scope)). + SetGrantAdmin(c.Bool(cliutils.GrantAdmin)). + SetExpiry(expiry). + SetRefreshable(c.Bool(cliutils.Refreshable)). + SetDescription(c.String(cliutils.Description)). + SetAudience(c.String(cliutils.Audience)). + SetIncludeReferenceToken(c.Bool(cliutils.Reference)) + err = commands.Exec(accessTokenCreateCmd) + if err != nil { + return err + } + resString, err := accessTokenCreateCmd.Response() + if err != nil { + return err + } + log.Output(clientUtils.IndentJson(resString)) + + return nil +} + +func createPlatformDetailsByFlags(c *cli.Context) (*coreConfig.ServerDetails, error) { + platformDetails, err := cliutils.CreateServerDetailsWithConfigOffer(c, true, cliutils.Platform) + if err != nil { + return nil, err + } + if platformDetails.Url == "" { + return nil, errors.New("platform URL is mandatory for access token creation") + } + return platformDetails, nil +} + +func getExpiry(c *cli.Context) (*uint, error) { + if !c.IsSet(cliutils.Expiry) { + return nil, nil + } + + expiryInt, err := strconv.Atoi(c.String(cliutils.Expiry)) + if err != nil { + return nil, cliutils.PrintHelpAndReturnError( + fmt.Sprintf("The '--%s' option must have a numeric value. ", cliutils.Expiry), c) + } + if expiryInt < 0 { + return nil, cliutils.PrintHelpAndReturnError( + fmt.Sprintf("The '--%s' option must be non-negative. ", cliutils.Expiry), c) + } + expiry := uint(expiryInt) + return &expiry, nil +} + +func assertSingleScopeOptionProvided(c *cli.Context) error { + authMethods := []bool{c.IsSet(cliutils.GrantAdmin), c.IsSet(cliutils.Groups), c.IsSet(cliutils.Scope)} + if coreutils.SumTrueValues(authMethods) > 1 { + return cliutils.PrintHelpAndReturnError( + fmt.Sprintf("Only one of the following options can be provided: '--%s', '--%s' or '--%s'. ", + cliutils.GrantAdmin, cliutils.Groups, cliutils.Scope), c) + } + return nil +} + +func assertAccessTokenAvailable(serverDetails *coreConfig.ServerDetails) error { + if serverDetails.AccessToken == "" { + return errorutils.CheckErrorf("authenticating with access token is currently mandatory for creating access tokens") + } + return nil +} diff --git a/go.mod b/go.mod index 7f67c2b4b..5d1edb89b 100644 --- a/go.mod +++ b/go.mod @@ -125,10 +125,10 @@ require ( // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230828134416-f0db33dd9344 -replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230907095444-fd00f19be95d +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230907144319-90160c43dda4 // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 -replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20230906115540-2c3c91d271d6 +replace github.com/jfrog/jfrog-client-go => github.com/RobiNino/jfrog-client-go v0.0.0-20230907143309-f15076b47356 replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230905120411-62d1bdd4eb38 diff --git a/go.sum b/go.sum index f5dd5a740..bccf3cd4f 100644 --- a/go.sum +++ b/go.sum @@ -53,6 +53,10 @@ github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5 github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230907144319-90160c43dda4 h1:DtbwRCppgvlBWtKTR+rqd2Sh8edb4hDAyX4HcEySN2M= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230907144319-90160c43dda4/go.mod h1:3eNPt5ugM5wKakWzJ7I42kTNCE4jjDOvrpbgemtjx0k= +github.com/RobiNino/jfrog-client-go v0.0.0-20230907143309-f15076b47356 h1:giGQihLn3m4KR0hhWYYdQY+GgclUkJn1Ikfs4x18f6Y= +github.com/RobiNino/jfrog-client-go v0.0.0-20230907143309-f15076b47356/go.mod h1:soD5VL3X+G+0KKUNSlb0CSdF9nwHsQZCr0xqOGedAHM= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -239,10 +243,6 @@ github.com/jfrog/build-info-go v1.8.9-0.20230905120411-62d1bdd4eb38 h1:XyAcwWP2a github.com/jfrog/build-info-go v1.8.9-0.20230905120411-62d1bdd4eb38/go.mod h1:QEskae5fQpjeY2PBzsjWtUQVskYSNDF2sSmw/Gx44dQ= github.com/jfrog/gofrog v1.3.0 h1:o4zgsBZE4QyDbz2M7D4K6fXPTBJht+8lE87mS9bw7Gk= github.com/jfrog/gofrog v1.3.0/go.mod h1:IFMc+V/yf7rA5WZ74CSbXe+Lgf0iApEQLxRZVzKRUR0= -github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230907095444-fd00f19be95d h1:7Qlsj5PkqSfayuNaM07L8W+H0bBqEGd+iPusoJOU6w8= -github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230907095444-fd00f19be95d/go.mod h1:PCRqGSz6tKQNtiITSk9WZGflJjno/Vg4DvXPLkH6xO8= -github.com/jfrog/jfrog-client-go v1.28.1-0.20230906115540-2c3c91d271d6 h1:9mNCAUu/uHx80s4rMc9PeI1lllrZ1MOPUesIMglFoTY= -github.com/jfrog/jfrog-client-go v1.28.1-0.20230906115540-2c3c91d271d6/go.mod h1:soD5VL3X+G+0KKUNSlb0CSdF9nwHsQZCr0xqOGedAHM= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jszwec/csvutil v1.8.0 h1:G7vS2LGdpZZDH1HmHeNbxOaJ/ZnJlpwGFvOkTkJzzNk= diff --git a/main.go b/main.go index 7304e0906..8cf9155c0 100644 --- a/main.go +++ b/main.go @@ -2,13 +2,6 @@ package main import ( "fmt" - "github.com/jfrog/jfrog-cli/lifecycle" - "golang.org/x/exp/slices" - "os" - "runtime" - "sort" - "strings" - "github.com/agnivade/levenshtein" corecommon "github.com/jfrog/jfrog-cli-core/v2/docs/common" setupcore "github.com/jfrog/jfrog-cli-core/v2/general/envsetup" @@ -23,10 +16,13 @@ import ( "github.com/jfrog/jfrog-cli/docs/common" "github.com/jfrog/jfrog-cli/docs/general/cisetup" loginDocs "github.com/jfrog/jfrog-cli/docs/general/login" + tokenDocs "github.com/jfrog/jfrog-cli/docs/general/token" cisetupcommand "github.com/jfrog/jfrog-cli/general/cisetup" "github.com/jfrog/jfrog-cli/general/envsetup" "github.com/jfrog/jfrog-cli/general/login" "github.com/jfrog/jfrog-cli/general/project" + "github.com/jfrog/jfrog-cli/general/token" + "github.com/jfrog/jfrog-cli/lifecycle" "github.com/jfrog/jfrog-cli/missioncontrol" "github.com/jfrog/jfrog-cli/pipelines" "github.com/jfrog/jfrog-cli/plugins" @@ -38,6 +34,11 @@ import ( "github.com/jfrog/jfrog-client-go/utils/io/fileutils" clientlog "github.com/jfrog/jfrog-client-go/utils/log" "github.com/urfave/cli" + "golang.org/x/exp/slices" + "os" + "runtime" + "sort" + "strings" ) const commandHelpTemplate string = `{{.HelpName}}{{if .UsageText}} @@ -276,6 +277,17 @@ func getCommands() []cli.Command { Category: otherCategory, Action: login.LoginCmd, }, + { + Name: "access-token-create", + Aliases: []string{"atc"}, + Flags: cliutils.GetCommandFlags(cliutils.AccessTokenCreate), + Usage: tokenDocs.GetDescription(), + HelpName: corecommon.CreateUsage("atc", tokenDocs.GetDescription(), tokenDocs.Usage), + UsageText: tokenDocs.GetArguments(), + ArgsUsage: common.CreateEnvVars(), + BashComplete: corecommon.CreateBashCompletionFunc(), + Action: token.AccessTokenCreateCmd, + }, } allCommands := append(slices.Clone(cliNameSpaces), utils.GetPlugins()...) allCommands = append(allCommands, scan.GetCommands()...) diff --git a/main_test.go b/main_test.go index 96efc1218..41805010a 100644 --- a/main_test.go +++ b/main_test.go @@ -71,7 +71,7 @@ func setupIntegrationTests() { InitXrayTests() } if *tests.TestAccess { - InitArtifactoryTests() + InitAccessTests() } if *tests.TestTransfer { InitTransferTests() diff --git a/utils/accesstoken/utils.go b/utils/accesstoken/utils.go new file mode 100644 index 000000000..f5ab0fc42 --- /dev/null +++ b/utils/accesstoken/utils.go @@ -0,0 +1,15 @@ +package accesstoken + +import ( + coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/urfave/cli" +) + +// If the username is provided as an argument, then it is used when creating the token. +// If not, then the configured username (or the value of the --user option) is used. +func GetSubjectUsername(c *cli.Context, serverDetails *coreConfig.ServerDetails) string { + if c.NArg() > 0 { + return c.Args().Get(0) + } + return serverDetails.GetUser() +} diff --git a/utils/cliutils/cli_consts.go b/utils/cliutils/cli_consts.go index bc0d2f16a..e4395af22 100644 --- a/utils/cliutils/cli_consts.go +++ b/utils/cliutils/cli_consts.go @@ -28,7 +28,7 @@ const ( Retries = 3 RetryWaitMilliSecs = 0 Threads = 3 - TokenExpiry = 3600 + ArtifactoryTokenExpiry = 3600 DefaultLicenseCount = 1 LatestCliVersionCheckInterval = time.Hour * 6 diff --git a/utils/cliutils/commandsflags.go b/utils/cliutils/commandsflags.go index 24bc36d46..3368e96c6 100644 --- a/utils/cliutils/commandsflags.go +++ b/utils/cliutils/commandsflags.go @@ -18,74 +18,74 @@ const ( Intro = "intro" // Artifactory's Commands Keys - DeleteConfig = "delete-config" - Upload = "upload" - Download = "download" - Move = "move" - Copy = "copy" - Delete = "delete" - Properties = "properties" - Search = "search" - BuildPublish = "build-publish" - BuildAppend = "build-append" - BuildScanLegacy = "build-scan-legacy" - BuildPromote = "build-promote" - BuildDiscard = "build-discard" - BuildAddDependencies = "build-add-dependencies" - BuildAddGit = "build-add-git" - BuildCollectEnv = "build-collect-env" - GitLfsClean = "git-lfs-clean" - Mvn = "mvn" - MvnConfig = "mvn-config" - Gradle = "gradle" - GradleConfig = "gradle-config" - DockerPromote = "docker-promote" - Docker = "docker" - DockerPush = "docker-push" - DockerPull = "docker-pull" - ContainerPull = "container-pull" - ContainerPush = "container-push" - BuildDockerCreate = "build-docker-create" - OcStartBuild = "oc-start-build" - NpmConfig = "npm-config" - Npm = "npm" - NpmInstallCi = "npm-install-ci" - NpmPublish = "npm-publish" - YarnConfig = "yarn-config" - Yarn = "yarn" - NugetConfig = "nuget-config" - Nuget = "nuget" - Dotnet = "dotnet" - DotnetConfig = "dotnet-config" - Go = "go" - GoConfig = "go-config" - GoPublish = "go-publish" - Pip = "pip" - PipInstall = "pip-install" - PipConfig = "pip-config" - TerraformConfig = "terraform-config" - Terraform = "terraform" - Pipenv = "pipenv" - PipenvConfig = "pipenv-config" - PipenvInstall = "pipenv-install" - PoetryConfig = "poetry-config" - Poetry = "poetry" - Ping = "ping" - RtCurl = "rt-curl" - TemplateConsumer = "template-consumer" - RepoDelete = "repo-delete" - ReplicationDelete = "replication-delete" - PermissionTargetDelete = "permission-target-delete" - AccessTokenCreate = "access-token-create" - UserCreate = "user-create" - UsersCreate = "users-create" - UsersDelete = "users-delete" - GroupCreate = "group-create" - GroupAddUsers = "group-add-users" - GroupDelete = "group-delete" - TransferConfig = "transfer-config" - TransferConfigMerge = "transfer-config-merge" - passphrase = "passphrase" + DeleteConfig = "delete-config" + Upload = "upload" + Download = "download" + Move = "move" + Copy = "copy" + Delete = "delete" + Properties = "properties" + Search = "search" + BuildPublish = "build-publish" + BuildAppend = "build-append" + BuildScanLegacy = "build-scan-legacy" + BuildPromote = "build-promote" + BuildDiscard = "build-discard" + BuildAddDependencies = "build-add-dependencies" + BuildAddGit = "build-add-git" + BuildCollectEnv = "build-collect-env" + GitLfsClean = "git-lfs-clean" + Mvn = "mvn" + MvnConfig = "mvn-config" + Gradle = "gradle" + GradleConfig = "gradle-config" + DockerPromote = "docker-promote" + Docker = "docker" + DockerPush = "docker-push" + DockerPull = "docker-pull" + ContainerPull = "container-pull" + ContainerPush = "container-push" + BuildDockerCreate = "build-docker-create" + OcStartBuild = "oc-start-build" + NpmConfig = "npm-config" + Npm = "npm" + NpmInstallCi = "npm-install-ci" + NpmPublish = "npm-publish" + YarnConfig = "yarn-config" + Yarn = "yarn" + NugetConfig = "nuget-config" + Nuget = "nuget" + Dotnet = "dotnet" + DotnetConfig = "dotnet-config" + Go = "go" + GoConfig = "go-config" + GoPublish = "go-publish" + Pip = "pip" + PipInstall = "pip-install" + PipConfig = "pip-config" + TerraformConfig = "terraform-config" + Terraform = "terraform" + Pipenv = "pipenv" + PipenvConfig = "pipenv-config" + PipenvInstall = "pipenv-install" + PoetryConfig = "poetry-config" + Poetry = "poetry" + Ping = "ping" + RtCurl = "rt-curl" + TemplateConsumer = "template-consumer" + RepoDelete = "repo-delete" + ReplicationDelete = "replication-delete" + PermissionTargetDelete = "permission-target-delete" + ArtifactoryAccessTokenCreate = "artifactory-access-token-create" + UserCreate = "user-create" + UsersCreate = "users-create" + UsersDelete = "users-delete" + GroupCreate = "group-create" + GroupAddUsers = "group-add-users" + GroupDelete = "group-delete" + TransferConfig = "transfer-config" + TransferConfigMerge = "transfer-config-merge" + passphrase = "passphrase" // Distribution's Command Keys ReleaseBundleV1Create = "release-bundle-v1-create" @@ -135,9 +135,13 @@ const ( ReleaseBundlePromote = "release-bundle-promote" ReleaseBundleDistribute = "release-bundle-distribute" + // Access Token Create commands keys + AccessTokenCreate = "access-token-create" + // *** Artifactory Commands' flags *** // Base flags url = "url" + platformUrl = "platform-url" user = "user" password = "password" accessToken = "access-token" @@ -281,7 +285,7 @@ const ( envInclude = "env-include" envExclude = "env-exclude" buildUrl = "build-url" - project = "project" + Project = "project" // Unique build-add-dependencies flags badPrefix = "bad-" @@ -394,12 +398,35 @@ const ( Replace = "replace" Admin = "admin" + // Mutual *-access-token-create flags + Groups = "groups" + GrantAdmin = "grant-admin" + Expiry = "expiry" + Refreshable = "refreshable" + Audience = "audience" + + // Unique artifactory-access-token-create flags + artifactoryAccessTokenCreatePrefix = "rt-atc-" + rtAtcGroups = artifactoryAccessTokenCreatePrefix + Groups + rtAtcGrantAdmin = artifactoryAccessTokenCreatePrefix + GrantAdmin + rtAtcExpiry = artifactoryAccessTokenCreatePrefix + Expiry + rtAtcRefreshable = artifactoryAccessTokenCreatePrefix + Refreshable + rtAtcAudience = artifactoryAccessTokenCreatePrefix + Audience + // Unique access-token-create flags - groups = "groups" - grantAdmin = "grant-admin" - expiry = "expiry" - refreshable = "refreshable" - audience = "audience" + accessTokenCreatePrefix = "atc-" + atcProject = accessTokenCreatePrefix + Project + Scope = "scope" + atcScope = accessTokenCreatePrefix + Scope + Description = "description" + atcDescription = accessTokenCreatePrefix + Description + Reference = "reference" + atcReference = accessTokenCreatePrefix + Reference + atcGroups = accessTokenCreatePrefix + Groups + atcGrantAdmin = accessTokenCreatePrefix + GrantAdmin + atcExpiry = accessTokenCreatePrefix + Expiry + atcRefreshable = accessTokenCreatePrefix + Refreshable + atcAudience = accessTokenCreatePrefix + Audience // Unique Xray Flags for upload/publish commands xrayScan = "scan" @@ -548,7 +575,7 @@ const ( lifecyclePrefix = "lc-" lcUrl = lifecyclePrefix + url lcSync = lifecyclePrefix + Sync - lcProject = lifecyclePrefix + project + lcProject = lifecyclePrefix + Project Builds = "builds" lcBuilds = lifecyclePrefix + Builds ReleaseBundles = "release-bundles" @@ -565,6 +592,10 @@ const ( var flagsMap = map[string]cli.Flag{ // Common commands flags + platformUrl: cli.StringFlag{ + Name: url, + Usage: "[Optional] JFrog platform URL.` `", + }, user: cli.StringFlag{ Name: user, Usage: "[Optional] JFrog username.` `", @@ -907,8 +938,8 @@ var flagsMap = map[string]cli.Flag{ Name: buildUrl, Usage: "[Optional] Can be used for setting the CI server build URL in the build-info.` `", }, - project: cli.StringFlag{ - Name: project, + Project: cli.StringFlag{ + Name: Project, Usage: "[Optional] JFrog Artifactory project key.` `", }, bpDryRun: cli.BoolFlag{ @@ -1140,26 +1171,26 @@ var flagsMap = map[string]cli.Flag{ Name: vars, Usage: "[Optional] List of variables in the form of \"key1=value1;key2=value2;...\" to be replaced in the template. In the template, the variables should be used as follows: ${key1}.` `", }, - groups: cli.StringFlag{ - Name: groups, + rtAtcGroups: cli.StringFlag{ + Name: Groups, Usage: "[Default: *] A list of comma-separated groups for the access token to be associated with. " + "Specify * to indicate that this is a 'user-scoped token', i.e., the token provides the same access privileges that the current subject has, and is therefore evaluated dynamically. " + "A non-admin user can only provide a scope that is a subset of the groups to which he belongs` `", }, - grantAdmin: cli.BoolFlag{ - Name: grantAdmin, + rtAtcGrantAdmin: cli.BoolFlag{ + Name: GrantAdmin, Usage: "[Default: false] Set to true to provide admin privileges to the access token. This is only available for administrators.` `", }, - expiry: cli.StringFlag{ - Name: expiry, - Usage: "[Default: " + strconv.Itoa(TokenExpiry) + "] The time in seconds for which the token will be valid. To specify a token that never expires, set to zero. Non-admin can only set a value that is equal to or less than the default 3600.` `", + rtAtcExpiry: cli.StringFlag{ + Name: Expiry, + Usage: "[Default: " + strconv.Itoa(ArtifactoryTokenExpiry) + "] The time in seconds for which the token will be valid. To specify a token that never expires, set to zero. Non-admin may only set a value that is equal to or less than the default 3600.` `", }, - refreshable: cli.BoolFlag{ - Name: refreshable, + rtAtcRefreshable: cli.BoolFlag{ + Name: Refreshable, Usage: "[Default: false] Set to true if you'd like the token to be refreshable. A refresh token will also be returned in order to be used to generate a new token once it expires.` `", }, - audience: cli.StringFlag{ - Name: audience, + rtAtcAudience: cli.StringFlag{ + Name: Audience, Usage: "[Optional] A space-separate list of the other Artifactory instances or services that should accept this token identified by their Artifactory Service IDs, as obtained by the 'jfrog rt curl api/system/service_id' command.` `", }, usersCreateCsv: cli.StringFlag{ @@ -1602,7 +1633,7 @@ var flagsMap = map[string]cli.Flag{ Usage: "[Default: false] Set to true to run synchronously.` `", }, lcProject: cli.StringFlag{ - Name: project, + Name: Project, Usage: "[Optional] Project key associated with the Release Bundle version.` `", }, lcBuilds: cli.StringFlag{ @@ -1634,6 +1665,45 @@ var flagsMap = map[string]cli.Flag{ Name: dryRun, Usage: "[Default: false] Set to true to only simulate the distribution of the release bundle.` `", }, + atcProject: cli.StringFlag{ + Name: Project, + Usage: "[Optional] The project for which this token is created. Enter the project name on which you want to apply this token.` `", + }, + atcGrantAdmin: cli.BoolFlag{ + Name: GrantAdmin, + Usage: "[Default: false] Set to true to provide admin privileges to the access token. This is only available for administrators.` `", + }, + atcGroups: cli.StringFlag{ + Name: Groups, + Usage: "[Optional] A list of comma-separated groups for the access token to be associated with. " + + "This is only available for administrators.` `", + }, + atcScope: cli.StringFlag{ + Name: Scope, + Usage: "[Optional] The scope of access that the token provides. This is only available for administrators.` `", + }, + atcExpiry: cli.StringFlag{ + Name: Expiry, + Usage: "[Optional] The amount of time, in seconds, it would take for the token to expire. Must be non-negative." + + "If not provided, the platform default will be used. To specify a token that never expires, set to zero. " + + "Non-admin may only set a value that is equal or lower than the platform default that was set by an administrator (1 year by default).` `", + }, + atcRefreshable: cli.BoolFlag{ + Name: Refreshable, + Usage: "[Default: false] Set to true if you'd like the token to be refreshable. A refresh token will also be returned in order to be used to generate a new token once it expires.` `", + }, + atcDescription: cli.StringFlag{ + Name: Description, + Usage: "[Optional] Free text token description. Useful for filtering and managing tokens. Limited to 1024 characters.` `", + }, + atcAudience: cli.StringFlag{ + Name: Audience, + Usage: "[Optional] A space-separated list of the other instances or services that should accept this token identified by their Service-IDs.` `", + }, + atcReference: cli.BoolFlag{ + Name: Reference, + Usage: "[Default: false] Generate a Reference Token (alias to Access Token) in addition to the full token (available from Artifactory 7.38.10)` `", + }, } var commandFlags = map[string][]string{ @@ -1652,7 +1722,7 @@ var commandFlags = map[string][]string{ url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, uploadTargetProps, ClientCertKeyPath, specFlag, specVars, buildName, buildNumber, module, uploadExclusions, deb, uploadRecursive, uploadFlat, uploadRegexp, retries, retryWaitTime, dryRun, uploadExplode, symlinks, includeDirs, - failNoOp, threads, uploadSyncDeletes, syncDeletesQuiet, InsecureTls, detailedSummary, project, + failNoOp, threads, uploadSyncDeletes, syncDeletesQuiet, InsecureTls, detailedSummary, Project, uploadAnt, uploadArchive, }, Download: { @@ -1660,74 +1730,74 @@ var commandFlags = map[string][]string{ ClientCertKeyPath, specFlag, specVars, buildName, buildNumber, module, exclusions, sortBy, sortOrder, limit, offset, downloadRecursive, downloadFlat, build, includeDeps, excludeArtifacts, minSplit, splitCount, retries, retryWaitTime, dryRun, downloadExplode, bypassArchiveInspection, validateSymlinks, bundle, publicGpgKey, includeDirs, - downloadProps, downloadExcludeProps, failNoOp, threads, archiveEntries, downloadSyncDeletes, syncDeletesQuiet, InsecureTls, detailedSummary, project, + downloadProps, downloadExcludeProps, failNoOp, threads, archiveEntries, downloadSyncDeletes, syncDeletesQuiet, InsecureTls, detailedSummary, Project, skipChecksum, }, Move: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, specFlag, specVars, exclusions, sortBy, sortOrder, limit, offset, moveRecursive, moveFlat, dryRun, build, includeDeps, excludeArtifacts, moveProps, moveExcludeProps, failNoOp, threads, archiveEntries, - InsecureTls, retries, retryWaitTime, project, + InsecureTls, retries, retryWaitTime, Project, }, Copy: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, specFlag, specVars, exclusions, sortBy, sortOrder, limit, offset, copyRecursive, copyFlat, dryRun, build, includeDeps, excludeArtifacts, bundle, copyProps, copyExcludeProps, failNoOp, threads, - archiveEntries, InsecureTls, retries, retryWaitTime, project, + archiveEntries, InsecureTls, retries, retryWaitTime, Project, }, Delete: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, specFlag, specVars, exclusions, sortBy, sortOrder, limit, offset, deleteRecursive, dryRun, build, includeDeps, excludeArtifacts, deleteQuiet, deleteProps, deleteExcludeProps, failNoOp, threads, archiveEntries, - InsecureTls, retries, retryWaitTime, project, + InsecureTls, retries, retryWaitTime, Project, }, Search: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, specFlag, specVars, exclusions, sortBy, sortOrder, limit, offset, searchRecursive, build, includeDeps, excludeArtifacts, count, bundle, includeDirs, searchProps, searchExcludeProps, failNoOp, archiveEntries, - InsecureTls, searchTransitive, retries, retryWaitTime, project, searchInclude, + InsecureTls, searchTransitive, retries, retryWaitTime, Project, searchInclude, }, Properties: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, specFlag, specVars, exclusions, sortBy, sortOrder, limit, offset, propsRecursive, build, includeDeps, excludeArtifacts, bundle, includeDirs, failNoOp, threads, archiveEntries, propsProps, propsExcludeProps, - InsecureTls, retries, retryWaitTime, project, + InsecureTls, retries, retryWaitTime, Project, }, BuildPublish: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, buildUrl, bpDryRun, - envInclude, envExclude, InsecureTls, project, bpDetailedSummary, + envInclude, envExclude, InsecureTls, Project, bpDetailedSummary, }, BuildAppend: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, buildUrl, bpDryRun, - envInclude, envExclude, InsecureTls, project, + envInclude, envExclude, InsecureTls, Project, }, BuildAddDependencies: { - specFlag, specVars, uploadExclusions, badRecursive, badRegexp, badDryRun, project, badFromRt, serverId, badModule, + specFlag, specVars, uploadExclusions, badRecursive, badRegexp, badDryRun, Project, badFromRt, serverId, badModule, }, BuildAddGit: { - configFlag, serverId, project, + configFlag, serverId, Project, }, BuildCollectEnv: { - project, + Project, }, BuildDockerCreate: { buildName, buildNumber, module, url, user, password, accessToken, sshPassphrase, sshKeyPath, - serverId, imageFile, project, + serverId, imageFile, Project, }, OcStartBuild: { - buildName, buildNumber, module, project, serverId, ocStartBuildRepo, + buildName, buildNumber, module, Project, serverId, ocStartBuildRepo, }, BuildScanLegacy: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, fail, InsecureTls, - project, + Project, }, BuildPromote: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, Status, comment, - sourceRepo, includeDependencies, copyFlag, failFast, bprDryRun, bprProps, InsecureTls, project, + sourceRepo, includeDependencies, copyFlag, failFast, bprDryRun, bprProps, InsecureTls, Project, }, BuildDiscard: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, maxDays, maxBuilds, - excludeBuilds, deleteArtifacts, bdiAsync, InsecureTls, project, + excludeBuilds, deleteArtifacts, bdiAsync, InsecureTls, Project, }, GitLfsClean: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, refs, glcRepo, glcDryRun, @@ -1741,21 +1811,21 @@ var commandFlags = map[string][]string{ deployIvyDesc, ivyDescPattern, ivyArtifactsPattern, }, Mvn: { - buildName, buildNumber, deploymentThreads, InsecureTls, project, detailedSummary, xrayScan, xrOutput, + buildName, buildNumber, deploymentThreads, InsecureTls, Project, detailedSummary, xrayScan, xrOutput, }, Gradle: { - buildName, buildNumber, deploymentThreads, project, detailedSummary, xrayScan, xrOutput, + buildName, buildNumber, deploymentThreads, Project, detailedSummary, xrayScan, xrOutput, }, Docker: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, serverId, skipLogin, threads, detailedSummary, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, BypassArchiveLimits, MinSeverity, FixableOnly, }, DockerPush: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, serverId, skipLogin, threads, detailedSummary, }, DockerPull: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, serverId, skipLogin, }, DockerPromote: { @@ -1764,54 +1834,54 @@ var commandFlags = map[string][]string{ }, ContainerPush: { buildName, buildNumber, module, url, user, password, accessToken, sshPassphrase, sshKeyPath, - serverId, skipLogin, threads, project, detailedSummary, + serverId, skipLogin, threads, Project, detailedSummary, }, ContainerPull: { buildName, buildNumber, module, url, user, password, accessToken, sshPassphrase, sshKeyPath, - serverId, skipLogin, project, + serverId, skipLogin, Project, }, NpmConfig: { global, serverIdResolve, serverIdDeploy, repoResolve, repoDeploy, }, NpmInstallCi: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, NpmPublish: { - buildName, buildNumber, module, project, npmDetailedSummary, xrayScan, xrOutput, + buildName, buildNumber, module, Project, npmDetailedSummary, xrayScan, xrOutput, }, YarnConfig: { global, serverIdResolve, repoResolve, }, Yarn: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, NugetConfig: { global, serverIdResolve, repoResolve, nugetV2, }, Nuget: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, DotnetConfig: { global, serverIdResolve, repoResolve, nugetV2, }, Dotnet: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, GoConfig: { global, serverIdResolve, serverIdDeploy, repoResolve, repoDeploy, }, GoPublish: { - url, user, password, accessToken, buildName, buildNumber, module, project, detailedSummary, goPublishExclusions, + url, user, password, accessToken, buildName, buildNumber, module, Project, detailedSummary, goPublishExclusions, }, Go: { - buildName, buildNumber, module, project, noFallback, + buildName, buildNumber, module, Project, noFallback, }, TerraformConfig: { global, serverIdDeploy, repoDeploy, }, Terraform: { namespace, provider, tag, exclusions, - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, TransferConfig: { Force, Verbose, IncludeRepos, ExcludeRepos, SourceWorkingDir, TargetWorkingDir, PreChecks, @@ -1830,19 +1900,19 @@ var commandFlags = map[string][]string{ global, serverIdResolve, repoResolve, }, PipInstall: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, PipenvConfig: { global, serverIdResolve, repoResolve, }, PipenvInstall: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, PoetryConfig: { global, serverIdResolve, repoResolve, }, Poetry: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, ReleaseBundleV1Create: { distUrl, user, password, accessToken, serverId, specFlag, specVars, targetProps, @@ -1880,9 +1950,14 @@ var commandFlags = map[string][]string{ url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, deleteQuiet, }, - AccessTokenCreate: { + ArtifactoryAccessTokenCreate: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, - ClientCertKeyPath, groups, grantAdmin, expiry, refreshable, audience, + ClientCertKeyPath, rtAtcGroups, rtAtcGrantAdmin, rtAtcExpiry, rtAtcRefreshable, rtAtcAudience, + }, + AccessTokenCreate: { + platformUrl, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, + atcProject, atcGrantAdmin, atcGroups, atcScope, atcExpiry, + atcRefreshable, atcDescription, atcAudience, atcReference, }, UserCreate: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, @@ -1933,37 +2008,37 @@ var commandFlags = map[string][]string{ curationOutput, workingDirs, curationThreads, }, Audit: { - xrUrl, user, password, accessToken, serverId, InsecureTls, project, watches, repoPath, licenses, xrOutput, ExcludeTestDeps, + xrUrl, user, password, accessToken, serverId, InsecureTls, Project, watches, repoPath, licenses, xrOutput, ExcludeTestDeps, useWrapperAudit, DepType, RequirementsFile, fail, ExtendedTable, workingDirs, Mvn, Gradle, Npm, Yarn, Go, Nuget, Pip, Pipenv, Poetry, MinSeverity, FixableOnly, }, AuditMvn: { - xrUrl, user, password, accessToken, serverId, InsecureTls, project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, useWrapperAudit, + xrUrl, user, password, accessToken, serverId, InsecureTls, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, useWrapperAudit, }, AuditGradle: { - xrUrl, user, password, accessToken, serverId, ExcludeTestDeps, useWrapperAudit, project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, + xrUrl, user, password, accessToken, serverId, ExcludeTestDeps, useWrapperAudit, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, }, AuditNpm: { - xrUrl, user, password, accessToken, serverId, DepType, project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, + xrUrl, user, password, accessToken, serverId, DepType, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, }, AuditGo: { - xrUrl, user, password, accessToken, serverId, project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, + xrUrl, user, password, accessToken, serverId, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, }, AuditPip: { - xrUrl, user, password, accessToken, serverId, RequirementsFile, project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, + xrUrl, user, password, accessToken, serverId, RequirementsFile, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, }, AuditPipenv: { - xrUrl, user, password, accessToken, serverId, project, watches, repoPath, licenses, xrOutput, ExtendedTable, + xrUrl, user, password, accessToken, serverId, Project, watches, repoPath, licenses, xrOutput, ExtendedTable, }, XrScan: { xrUrl, user, password, accessToken, serverId, specFlag, threads, scanRecursive, scanRegexp, scanAnt, - project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, BypassArchiveLimits, MinSeverity, FixableOnly, + Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, BypassArchiveLimits, MinSeverity, FixableOnly, }, DockerScan: { // Flags added here should be also added to Docker command - serverId, project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, BypassArchiveLimits, MinSeverity, FixableOnly, + serverId, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, BypassArchiveLimits, MinSeverity, FixableOnly, }, BuildScan: { - xrUrl, user, password, accessToken, serverId, project, vuln, xrOutput, fail, ExtendedTable, rescan, + xrUrl, user, password, accessToken, serverId, Project, vuln, xrOutput, fail, ExtendedTable, rescan, }, // Mission Control's commands McConfig: { From 431efaab233105e5866aaeedc25072d62f939ec2 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 14 Sep 2023 14:38:13 +0300 Subject: [PATCH 2/7] Allow more than one scope --- docs/general/token/help.go | 3 +-- general/token/cli.go | 12 +++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/general/token/help.go b/docs/general/token/help.go index a52830f1c..b8ebb9fa1 100644 --- a/docs/general/token/help.go +++ b/docs/general/token/help.go @@ -5,8 +5,7 @@ var Usage = []string{"atc", "atc "} func GetDescription() string { return `Creates an access token. By default, an user-scoped token will be created. - Administrator may provide a single of the following options to modify the scope of the token: - '--groups', '--grant-admin' or '--scope.'` + Administrator may provide the scope explicitly with '--scope', or implicitly with '--groups', '--grant-admin'` } func GetArguments() string { diff --git a/general/token/cli.go b/general/token/cli.go index 5b099f1ba..748a0f0a1 100644 --- a/general/token/cli.go +++ b/general/token/cli.go @@ -6,7 +6,6 @@ import ( "github.com/jfrog/jfrog-cli-core/v2/common/commands" generic "github.com/jfrog/jfrog-cli-core/v2/general/token" coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" - "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli/utils/accesstoken" "github.com/jfrog/jfrog-cli/utils/cliutils" clientUtils "github.com/jfrog/jfrog-client-go/utils" @@ -30,7 +29,7 @@ func AccessTokenCreateCmd(c *cli.Context) error { return err } - if err = assertSingleScopeOptionProvided(c); err != nil { + if err = assertScopeOptions(c); err != nil { return err } @@ -96,12 +95,11 @@ func getExpiry(c *cli.Context) (*uint, error) { return &expiry, nil } -func assertSingleScopeOptionProvided(c *cli.Context) error { - authMethods := []bool{c.IsSet(cliutils.GrantAdmin), c.IsSet(cliutils.Groups), c.IsSet(cliutils.Scope)} - if coreutils.SumTrueValues(authMethods) > 1 { +func assertScopeOptions(c *cli.Context) error { + if c.IsSet(cliutils.Scope) && (c.IsSet(cliutils.GrantAdmin) || c.IsSet(cliutils.Groups)) { return cliutils.PrintHelpAndReturnError( - fmt.Sprintf("Only one of the following options can be provided: '--%s', '--%s' or '--%s'. ", - cliutils.GrantAdmin, cliutils.Groups, cliutils.Scope), c) + fmt.Sprintf("Scope can either be provided explicitly with '--%s', or implicitly with '--%s' and '--%s'. ", + cliutils.Scope, cliutils.GrantAdmin, cliutils.Groups), c) } return nil } From 10359e45a5098893890462506bedf34a56a53679 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 14 Sep 2023 14:58:34 +0300 Subject: [PATCH 3/7] nosec --- utils/cliutils/commandsflags.go | 117 ++++++++++++++++---------------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/utils/cliutils/commandsflags.go b/utils/cliutils/commandsflags.go index b34d997a7..ef823ad63 100644 --- a/utils/cliutils/commandsflags.go +++ b/utils/cliutils/commandsflags.go @@ -18,64 +18,65 @@ const ( Intro = "intro" // Artifactory's Commands Keys - DeleteConfig = "delete-config" - Upload = "upload" - Download = "download" - Move = "move" - Copy = "copy" - Delete = "delete" - Properties = "properties" - Search = "search" - BuildPublish = "build-publish" - BuildAppend = "build-append" - BuildScanLegacy = "build-scan-legacy" - BuildPromote = "build-promote" - BuildDiscard = "build-discard" - BuildAddDependencies = "build-add-dependencies" - BuildAddGit = "build-add-git" - BuildCollectEnv = "build-collect-env" - GitLfsClean = "git-lfs-clean" - Mvn = "mvn" - MvnConfig = "mvn-config" - Gradle = "gradle" - GradleConfig = "gradle-config" - DockerPromote = "docker-promote" - Docker = "docker" - DockerPush = "docker-push" - DockerPull = "docker-pull" - ContainerPull = "container-pull" - ContainerPush = "container-push" - BuildDockerCreate = "build-docker-create" - OcStartBuild = "oc-start-build" - NpmConfig = "npm-config" - Npm = "npm" - NpmInstallCi = "npm-install-ci" - NpmPublish = "npm-publish" - YarnConfig = "yarn-config" - Yarn = "yarn" - NugetConfig = "nuget-config" - Nuget = "nuget" - Dotnet = "dotnet" - DotnetConfig = "dotnet-config" - Go = "go" - GoConfig = "go-config" - GoPublish = "go-publish" - Pip = "pip" - PipInstall = "pip-install" - PipConfig = "pip-config" - TerraformConfig = "terraform-config" - Terraform = "terraform" - Pipenv = "pipenv" - PipenvConfig = "pipenv-config" - PipenvInstall = "pipenv-install" - PoetryConfig = "poetry-config" - Poetry = "poetry" - Ping = "ping" - RtCurl = "rt-curl" - TemplateConsumer = "template-consumer" - RepoDelete = "repo-delete" - ReplicationDelete = "replication-delete" - PermissionTargetDelete = "permission-target-delete" + DeleteConfig = "delete-config" + Upload = "upload" + Download = "download" + Move = "move" + Copy = "copy" + Delete = "delete" + Properties = "properties" + Search = "search" + BuildPublish = "build-publish" + BuildAppend = "build-append" + BuildScanLegacy = "build-scan-legacy" + BuildPromote = "build-promote" + BuildDiscard = "build-discard" + BuildAddDependencies = "build-add-dependencies" + BuildAddGit = "build-add-git" + BuildCollectEnv = "build-collect-env" + GitLfsClean = "git-lfs-clean" + Mvn = "mvn" + MvnConfig = "mvn-config" + Gradle = "gradle" + GradleConfig = "gradle-config" + DockerPromote = "docker-promote" + Docker = "docker" + DockerPush = "docker-push" + DockerPull = "docker-pull" + ContainerPull = "container-pull" + ContainerPush = "container-push" + BuildDockerCreate = "build-docker-create" + OcStartBuild = "oc-start-build" + NpmConfig = "npm-config" + Npm = "npm" + NpmInstallCi = "npm-install-ci" + NpmPublish = "npm-publish" + YarnConfig = "yarn-config" + Yarn = "yarn" + NugetConfig = "nuget-config" + Nuget = "nuget" + Dotnet = "dotnet" + DotnetConfig = "dotnet-config" + Go = "go" + GoConfig = "go-config" + GoPublish = "go-publish" + Pip = "pip" + PipInstall = "pip-install" + PipConfig = "pip-config" + TerraformConfig = "terraform-config" + Terraform = "terraform" + Pipenv = "pipenv" + PipenvConfig = "pipenv-config" + PipenvInstall = "pipenv-install" + PoetryConfig = "poetry-config" + Poetry = "poetry" + Ping = "ping" + RtCurl = "rt-curl" + TemplateConsumer = "template-consumer" + RepoDelete = "repo-delete" + ReplicationDelete = "replication-delete" + PermissionTargetDelete = "permission-target-delete" + // #nosec G101 -- False positive - no hardcoded credentials. ArtifactoryAccessTokenCreate = "artifactory-access-token-create" UserCreate = "user-create" UsersCreate = "users-create" From 5b80035ed78ea74a5768f8c4f409cd64d738201b Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 14 Sep 2023 15:21:00 +0300 Subject: [PATCH 4/7] gomod --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a90148ca5..79394e410 100644 --- a/go.mod +++ b/go.mod @@ -126,7 +126,7 @@ require ( // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230828134416-f0db33dd9344 -replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230914112942-386428f16ab4 +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230914121902-5d87822fae6d // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 diff --git a/go.sum b/go.sum index 0c74e164f..9ec1d873c 100644 --- a/go.sum +++ b/go.sum @@ -53,8 +53,8 @@ github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5 github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230914112942-386428f16ab4 h1:7VHeUa4GDAXDQluZC6Aa87nadEfc7cDrh7uxIH3jfSc= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230914112942-386428f16ab4/go.mod h1:8+7FsK3yGO5TpxlEyJ6d3tHRwk8XvFsHde6Ih5Aty6c= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230914121902-5d87822fae6d h1:cefYQmqRldmCK6wKlnnGWjvPmbIZdBik908u5PsdlXI= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230914121902-5d87822fae6d/go.mod h1:8+7FsK3yGO5TpxlEyJ6d3tHRwk8XvFsHde6Ih5Aty6c= github.com/RobiNino/jfrog-client-go v0.0.0-20230914111739-e5625524b232 h1:WnSlTYiMi3TLbZgHK82wcbl9QMNGO5PHMDmP1n5jINk= github.com/RobiNino/jfrog-client-go v0.0.0-20230914111739-e5625524b232/go.mod h1:UewnwkIf/77HzBgwCPzOHZCK6V/Nw5/JwdzN/tRb4aU= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= From da83114f340ff4678c52d0b38e8050c5ff32751e Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Mon, 2 Oct 2023 18:40:31 +0300 Subject: [PATCH 5/7] CR comments --- access_test.go | 200 +++++++++++++++++++++---------------- docs/general/token/help.go | 2 +- go.mod | 4 +- go.sum | 8 +- 4 files changed, 120 insertions(+), 94 deletions(-) diff --git a/access_test.go b/access_test.go index 837e1241a..041c8446a 100644 --- a/access_test.go +++ b/access_test.go @@ -11,15 +11,20 @@ import ( coreTests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" "github.com/jfrog/jfrog-cli/utils/tests" "github.com/jfrog/jfrog-client-go/auth" + "github.com/jfrog/jfrog-client-go/http/httpclient" clientUtils "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/jfrog/jfrog-client-go/utils/io/httputils" clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/stretchr/testify/assert" + "net/http" "testing" ) var ( - accessDetails *config.ServerDetails - accessCli *tests.JfrogCli + accessDetails *config.ServerDetails + accessCli *tests.JfrogCli + accessHttpDetails httputils.HttpClientDetails ) func initAccessTest(t *testing.T) { @@ -28,6 +33,47 @@ func initAccessTest(t *testing.T) { } } +func initAccessCli() { + if accessCli != nil { + return + } + accessCli = tests.NewJfrogCli(execMain, "jfrog", authenticateAccess()) +} + +func InitAccessTests() { + initArtifactoryCli() + initAccessCli() + cleanUpOldBuilds() + cleanUpOldRepositories() + cleanUpOldUsers() + tests.AddTimestampToGlobalVars() + createRequiredRepos() + cleanArtifactoryTest() +} + +func authenticateAccess() string { + *tests.JfrogUrl = clientUtils.AddTrailingSlashIfNeeded(*tests.JfrogUrl) + accessDetails = &config.ServerDetails{ + AccessUrl: *tests.JfrogUrl + tests.AccessEndpoint} + + cred := fmt.Sprintf("--url=%s", *tests.JfrogUrl) + if *tests.JfrogAccessToken != "" { + accessDetails.AccessToken = *tests.JfrogAccessToken + cred += fmt.Sprintf(" --access-token=%s", accessDetails.AccessToken) + } else { + accessDetails.User = *tests.JfrogUser + accessDetails.Password = *tests.JfrogPassword + cred += fmt.Sprintf(" --user=%s --password=%s", accessDetails.User, accessDetails.Password) + } + + accessAuth, err := accessDetails.CreateAccessAuthConfig() + if err != nil { + coreutils.ExitOnErr(err) + } + accessHttpDetails = accessAuth.CreateHttpClientDetails() + return cred +} + func TestSetupInvitedUser(t *testing.T) { initAccessTest(t) tempDirPath, createTempDirCallback := coreTests.CreateTempDirWithCallbackAndAssert(t) @@ -128,72 +174,74 @@ const ( defaultExpiry = 31536000 ) +var atcTestCases = []struct { + name string + args []string + shouldExpire bool + expectedExpiry uint + expectedScope string + expectedRefreshable bool + expectedReference bool +}{ + { + name: "default", + args: []string{"atc"}, + shouldExpire: true, + expectedExpiry: defaultExpiry, + expectedScope: userScope, + expectedRefreshable: false, + expectedReference: false, + }, + { + name: "explicit user, no expiry", + args: []string{"atc", auth.ExtractUsernameFromAccessToken(*tests.JfrogAccessToken), "--expiry=0"}, + shouldExpire: false, + expectedExpiry: 0, + expectedScope: userScope, + expectedRefreshable: false, + expectedReference: false, + }, + { + name: "refreshable, admin", + args: []string{"atc", "--refreshable", "--grant-admin"}, + shouldExpire: true, + expectedExpiry: defaultExpiry, + expectedScope: "applied-permissions/admin", + expectedRefreshable: true, + expectedReference: false, + }, + { + name: "reference, custom scope, custom expiry", + args: []string{"atc", "--reference", "--scope=system:metrics:r", "--expiry=1234"}, + shouldExpire: true, + expectedExpiry: 1234, + expectedScope: "system:metrics:r", + expectedRefreshable: false, + expectedReference: true, + }, + { + name: "groups, description", + args: []string{"atc", "--groups=group1,group2", "--description=description"}, + shouldExpire: true, + expectedExpiry: defaultExpiry, + expectedScope: "applied-permissions/groups:group1,group2", + expectedRefreshable: false, + expectedReference: false, + }, +} + func TestAccessTokenCreate(t *testing.T) { initAccessTest(t) if *tests.JfrogAccessToken == "" { t.Skip("access token create command only supports authorization with access token, but a token is not provided. Skipping...") } - testCases := []struct { - name string - args []string - shouldExpire bool - expectedExpiry uint - expectedScope string - expectedRefreshable bool - expectedReference bool - }{ - { - name: "default", - args: []string{"atc"}, - shouldExpire: true, - expectedExpiry: defaultExpiry, - expectedScope: userScope, - expectedRefreshable: false, - expectedReference: false, - }, - { - name: "explicit user, no expiry", - args: []string{"atc", auth.ExtractUsernameFromAccessToken(*tests.JfrogAccessToken), "--expiry=0"}, - shouldExpire: false, - expectedExpiry: 0, - expectedScope: userScope, - expectedRefreshable: false, - expectedReference: false, - }, - { - name: "refreshable, admin", - args: []string{"atc", "--refreshable", "--grant-admin"}, - shouldExpire: true, - expectedExpiry: defaultExpiry, - expectedScope: "applied-permissions/admin", - expectedRefreshable: true, - expectedReference: false, - }, - { - name: "reference, custom scope, custom expiry", - args: []string{"atc", "--reference", "--scope=system:metrics:r", "--expiry=1234"}, - shouldExpire: true, - expectedExpiry: 1234, - expectedScope: "system:metrics:r", - expectedRefreshable: false, - expectedReference: true, - }, - { - name: "groups, description", - args: []string{"atc", "--groups=group1,group2", "--description=description"}, - shouldExpire: true, - expectedExpiry: defaultExpiry, - expectedScope: "applied-permissions/groups:group1,group2", - expectedRefreshable: false, - expectedReference: false, - }, - } - for _, test := range testCases { + for _, test := range atcTestCases { t.Run(test.name, func(t *testing.T) { var token auth.CreateTokenResponseData output := accessCli.RunCliCmdWithOutput(t, test.args...) assert.NoError(t, json.Unmarshal([]byte(output), &token)) + defer revokeToken(t, token.TokenId) if test.shouldExpire { assert.EqualValues(t, test.expectedExpiry, *token.ExpiresIn) @@ -220,37 +268,15 @@ func assertNotEmptyIfExpected(t *testing.T, expected bool, output string) { } } -func initAccessCli() { - if accessCli != nil { +func revokeToken(t *testing.T, tokenId string) { + if tokenId == "" { return } - accessCli = tests.NewJfrogCli(execMain, "jfrog", authenticateAccess()) -} - -func InitAccessTests() { - initArtifactoryCli() - initAccessCli() - cleanUpOldBuilds() - cleanUpOldRepositories() - cleanUpOldUsers() - tests.AddTimestampToGlobalVars() - createRequiredRepos() - cleanArtifactoryTest() -} -func authenticateAccess() string { - *tests.JfrogUrl = clientUtils.AddTrailingSlashIfNeeded(*tests.JfrogUrl) - accessDetails = &config.ServerDetails{ - AccessUrl: *tests.JfrogUrl + tests.AccessEndpoint} + client, err := httpclient.ClientBuilder().Build() + assert.NoError(t, err) - cred := fmt.Sprintf("--url=%s", *tests.JfrogUrl) - if *tests.JfrogAccessToken != "" { - accessDetails.AccessToken = *tests.JfrogAccessToken - cred += fmt.Sprintf(" --access-token=%s", accessDetails.AccessToken) - } else { - accessDetails.User = *tests.JfrogUser - accessDetails.Password = *tests.JfrogPassword - cred += fmt.Sprintf(" --user=%s --password=%s", accessDetails.User, accessDetails.Password) - } - return cred + resp, _, err := client.SendDelete(*tests.JfrogUrl+"access/api/v1/tokens/"+tokenId, nil, accessHttpDetails, "") + assert.NoError(t, err) + assert.NoError(t, errorutils.CheckResponseStatus(resp, http.StatusOK)) } diff --git a/docs/general/token/help.go b/docs/general/token/help.go index b8ebb9fa1..601a1665d 100644 --- a/docs/general/token/help.go +++ b/docs/general/token/help.go @@ -5,7 +5,7 @@ var Usage = []string{"atc", "atc "} func GetDescription() string { return `Creates an access token. By default, an user-scoped token will be created. - Administrator may provide the scope explicitly with '--scope', or implicitly with '--groups', '--grant-admin'` + Administrator may provide the scope explicitly with '--scope', or implicitly with '--groups', '--grant-admin'.` } func GetArguments() string { diff --git a/go.mod b/go.mod index a829c0983..fcc67cc20 100644 --- a/go.mod +++ b/go.mod @@ -128,10 +128,10 @@ require ( replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230928084830-478bd49f5d3e -replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20231002131847-e7ac8274f4e3 +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20231002153919-5ea5f96bc2a1 // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 -replace github.com/jfrog/jfrog-client-go => github.com/RobiNino/jfrog-client-go v0.0.0-20231002130445-b16f43c60c85 +replace github.com/jfrog/jfrog-client-go => github.com/RobiNino/jfrog-client-go v0.0.0-20231002153224-502dc4746fbc // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230831151231-e5e7bd035ddc diff --git a/go.sum b/go.sum index 63d5b84d9..d29671350 100644 --- a/go.sum +++ b/go.sum @@ -54,10 +54,10 @@ github.com/Microsoft/hcsshim v0.11.0 h1:7EFNIY4igHEXUdj1zXgAyU3fLc7QfOKHbkldRVTB github.com/Microsoft/hcsshim v0.11.0/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20231002131847-e7ac8274f4e3 h1:p2Q/CBUdWPEmFQ7lPYElNvezgXKBowWFzeTvjjZzluE= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20231002131847-e7ac8274f4e3/go.mod h1:Ij3mfD91tmAMnQ19cFuv2eCs3UAsCfZ54ICxdQtxUg4= -github.com/RobiNino/jfrog-client-go v0.0.0-20231002130445-b16f43c60c85 h1:lfFzAw4zLbV/zlvQVy0441vvkJbqIBFOgPJtptn1G64= -github.com/RobiNino/jfrog-client-go v0.0.0-20231002130445-b16f43c60c85/go.mod h1:AePTNv5H1YSGycxiL+1jXHCzqu3rCGruVP7S0N+BEEo= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20231002153919-5ea5f96bc2a1 h1:hSHUwPYQ/zU186qm9fBqXldYbH5RSbbkXPDBh+yOzUU= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20231002153919-5ea5f96bc2a1/go.mod h1:fOwXp2oGZYSLSAU9z36X9cscZlsIW2XLbfAxIDpTobU= +github.com/RobiNino/jfrog-client-go v0.0.0-20231002153224-502dc4746fbc h1:dYkzhDo2Qqx4HVcuwbpq4pTL+D29PkyVHccR9ElD4A8= +github.com/RobiNino/jfrog-client-go v0.0.0-20231002153224-502dc4746fbc/go.mod h1:AePTNv5H1YSGycxiL+1jXHCzqu3rCGruVP7S0N+BEEo= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= From a5a1f15790343f4d3d0f16a4361473fc8d063825 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Tue, 3 Oct 2023 10:53:10 +0300 Subject: [PATCH 6/7] fix failing test --- access_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/access_test.go b/access_test.go index 041c8446a..299167a1b 100644 --- a/access_test.go +++ b/access_test.go @@ -212,9 +212,9 @@ var atcTestCases = []struct { }, { name: "reference, custom scope, custom expiry", - args: []string{"atc", "--reference", "--scope=system:metrics:r", "--expiry=1234"}, + args: []string{"atc", "--reference", "--scope=system:metrics:r", "--expiry=123456"}, shouldExpire: true, - expectedExpiry: 1234, + expectedExpiry: 123456, expectedScope: "system:metrics:r", expectedRefreshable: false, expectedReference: true, From 1f211e89b39306def675ee591d5e174991397085 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Tue, 3 Oct 2023 11:41:42 +0300 Subject: [PATCH 7/7] update gomod --- go.mod | 6 +++--- go.sum | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index fcc67cc20..3e4da02e4 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/buger/jsonparser v1.1.1 github.com/go-git/go-git/v5 v5.9.0 github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d - github.com/jfrog/build-info-go v1.9.10 + github.com/jfrog/build-info-go v1.9.11 github.com/jfrog/gofrog v1.3.0 github.com/jfrog/jfrog-cli-core/v2 v2.43.2 github.com/jfrog/jfrog-client-go v1.32.3 @@ -128,10 +128,10 @@ require ( replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230928084830-478bd49f5d3e -replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20231002153919-5ea5f96bc2a1 +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20231003083915-9469fc8de766 // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 -replace github.com/jfrog/jfrog-client-go => github.com/RobiNino/jfrog-client-go v0.0.0-20231002153224-502dc4746fbc +replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20231003083451-568b46797866 // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230831151231-e5e7bd035ddc diff --git a/go.sum b/go.sum index d29671350..121ccb37d 100644 --- a/go.sum +++ b/go.sum @@ -54,10 +54,6 @@ github.com/Microsoft/hcsshim v0.11.0 h1:7EFNIY4igHEXUdj1zXgAyU3fLc7QfOKHbkldRVTB github.com/Microsoft/hcsshim v0.11.0/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20231002153919-5ea5f96bc2a1 h1:hSHUwPYQ/zU186qm9fBqXldYbH5RSbbkXPDBh+yOzUU= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20231002153919-5ea5f96bc2a1/go.mod h1:fOwXp2oGZYSLSAU9z36X9cscZlsIW2XLbfAxIDpTobU= -github.com/RobiNino/jfrog-client-go v0.0.0-20231002153224-502dc4746fbc h1:dYkzhDo2Qqx4HVcuwbpq4pTL+D29PkyVHccR9ElD4A8= -github.com/RobiNino/jfrog-client-go v0.0.0-20231002153224-502dc4746fbc/go.mod h1:AePTNv5H1YSGycxiL+1jXHCzqu3rCGruVP7S0N+BEEo= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -246,6 +242,10 @@ github.com/jfrog/gofrog v1.3.0 h1:o4zgsBZE4QyDbz2M7D4K6fXPTBJht+8lE87mS9bw7Gk= github.com/jfrog/gofrog v1.3.0/go.mod h1:IFMc+V/yf7rA5WZ74CSbXe+Lgf0iApEQLxRZVzKRUR0= github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYLipdsOFMY= github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w= +github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20231003083915-9469fc8de766 h1:HqgXCK9umbVutqd7ZFlip6qPAasBBXMtF3UAYktXA/s= +github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20231003083915-9469fc8de766/go.mod h1:fnOBbz6OfIRX1UNlX7kaf7Q+3AJkHb1ZiAG2aKeY2Jk= +github.com/jfrog/jfrog-client-go v1.28.1-0.20231003083451-568b46797866 h1:0SWHyECx5QfCjQXf8hDzbyM94B78Dvzei7TvD9CpsCY= +github.com/jfrog/jfrog-client-go v1.28.1-0.20231003083451-568b46797866/go.mod h1:wtk8jhtdrlzYvo3LLIwOn0OrqoSm8J5TiMfZzHIwLe8= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jszwec/csvutil v1.8.0 h1:G7vS2LGdpZZDH1HmHeNbxOaJ/ZnJlpwGFvOkTkJzzNk=