diff --git a/general/envsetup/envsetup.go b/general/envsetup/envsetup.go index cbe844133..a7cc75943 100644 --- a/general/envsetup/envsetup.go +++ b/general/envsetup/envsetup.go @@ -27,10 +27,9 @@ import ( type OutputFormat string const ( - myJfrogEndPoint = "https://myjfrog-api.jfrog.com/api/v1/activation/cloud/cli/getStatus/" - syncSleepInterval = 5 * time.Second // 5 seconds - maxWaitMinutes = 30 * time.Minute // 30 minutes - nonExpiredTokenValue = 0 // Access Tokens with 0 expiration value are actually generated by Access with 1 year expiration. + myJfrogEndPoint = "https://myjfrog-api.jfrog.com/api/v1/activation/cloud/cli/getStatus/" + syncSleepInterval = 5 * time.Second // 5 seconds + maxWaitMinutes = 30 * time.Minute // 30 minutes // OutputFormat values Human OutputFormat = "human" @@ -265,7 +264,8 @@ func GenerateNewLongTermRefreshableAccessToken(server *config.ServerDetails) (er func createLongExpirationRefreshableTokenParams() *services.CreateTokenParams { params := services.CreateTokenParams{} - params.ExpiresIn = nonExpiredTokenValue + // Using the platform's default expiration (1 year by default). + params.ExpiresIn = nil params.Refreshable = clientUtils.Pointer(true) params.Audience = "*@*" return ¶ms diff --git a/general/token/accesstokencreate.go b/general/token/accesstokencreate.go new file mode 100644 index 000000000..e1a7c4226 --- /dev/null +++ b/general/token/accesstokencreate.go @@ -0,0 +1,150 @@ +package token + +import ( + "encoding/json" + rtUtils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" + "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-client-go/access/services" + "github.com/jfrog/jfrog-client-go/auth" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "strings" +) + +const ( + AdminScope = "applied-permissions/admin" + GroupsScopePrefix = "applied-permissions/groups:" +) + +type AccessTokenCreateCommand struct { + serverDetails *config.ServerDetails + username string + projectKey string + + scope string + groups string + grantAdmin bool + + expiry *uint + refreshable bool + description string + + audience string + includeReferenceToken bool + + response *auth.CreateTokenResponseData +} + +func NewAccessTokenCreateCommand() *AccessTokenCreateCommand { + return &AccessTokenCreateCommand{response: new(auth.CreateTokenResponseData)} +} + +func (atc *AccessTokenCreateCommand) SetServerDetails(serverDetails *config.ServerDetails) *AccessTokenCreateCommand { + atc.serverDetails = serverDetails + return atc +} + +func (atc *AccessTokenCreateCommand) SetUsername(username string) *AccessTokenCreateCommand { + atc.username = username + return atc +} + +func (atc *AccessTokenCreateCommand) SetProjectKey(projectKey string) *AccessTokenCreateCommand { + atc.projectKey = projectKey + return atc +} + +func (atc *AccessTokenCreateCommand) SetGroups(groups string) *AccessTokenCreateCommand { + atc.groups = groups + return atc +} + +func (atc *AccessTokenCreateCommand) SetScope(scope string) *AccessTokenCreateCommand { + atc.scope = scope + return atc +} + +func (atc *AccessTokenCreateCommand) SetGrantAdmin(grantAdmin bool) *AccessTokenCreateCommand { + atc.grantAdmin = grantAdmin + return atc +} + +func (atc *AccessTokenCreateCommand) SetExpiry(expiry *uint) *AccessTokenCreateCommand { + atc.expiry = expiry + return atc +} + +func (atc *AccessTokenCreateCommand) SetRefreshable(refreshable bool) *AccessTokenCreateCommand { + atc.refreshable = refreshable + return atc +} + +func (atc *AccessTokenCreateCommand) SetDescription(description string) *AccessTokenCreateCommand { + atc.description = description + return atc +} + +func (atc *AccessTokenCreateCommand) SetAudience(audience string) *AccessTokenCreateCommand { + atc.audience = audience + return atc +} + +func (atc *AccessTokenCreateCommand) SetIncludeReferenceToken(includeReferenceToken bool) *AccessTokenCreateCommand { + atc.includeReferenceToken = includeReferenceToken + return atc +} + +func (atc *AccessTokenCreateCommand) Response() ([]byte, error) { + content, err := json.Marshal(*atc.response) + return content, errorutils.CheckError(err) +} + +func (atc *AccessTokenCreateCommand) ServerDetails() (*config.ServerDetails, error) { + return atc.serverDetails, nil +} + +func (atc *AccessTokenCreateCommand) CommandName() string { + return "jf_access_token_create" +} + +func (atc *AccessTokenCreateCommand) Run() error { + servicesManager, err := rtUtils.CreateAccessServiceManager(atc.serverDetails, false) + if err != nil { + return err + } + + *atc.response, err = servicesManager.CreateAccessToken(atc.getTokenParams()) + return err +} + +func (atc *AccessTokenCreateCommand) getTokenParams() services.CreateTokenParams { + tokenParams := services.CreateTokenParams{} + + tokenParams.Username = strings.ToLower(atc.username) + tokenParams.ProjectKey = atc.projectKey + tokenParams.Scope = atc.getScope() + tokenParams.ExpiresIn = atc.expiry + tokenParams.Refreshable = &atc.refreshable + tokenParams.Description = atc.description + tokenParams.Audience = atc.audience + tokenParams.IncludeReferenceToken = &atc.includeReferenceToken + return tokenParams +} + +// If an explicit scope was provided, apply it. +// Otherwise, if admin or groups scopes were requested, construct scope from them (space separated). +// If no scopes were requested, leave scope empty to provide the default user scope. +func (atc *AccessTokenCreateCommand) getScope() string { + if atc.scope != "" { + return atc.scope + } + + var scopes []string + if atc.groups != "" { + scopes = append(scopes, GroupsScopePrefix+atc.groups) + } + + if atc.grantAdmin { + scopes = append(scopes, AdminScope) + } + return strings.Join(scopes, " ") +} diff --git a/go.mod b/go.mod index da2539daf..7046713fa 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/google/uuid v1.3.1 github.com/gookit/color v1.5.4 github.com/jedib0t/go-pretty/v6 v6.4.7 - 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-apps-config v1.0.1 github.com/jfrog/jfrog-client-go v1.32.3 @@ -95,6 +95,6 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect ) -replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20230928142526-622034e3f57b +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.20230928084830-478bd49f5d3e diff --git a/go.sum b/go.sum index 31b7169b1..d3e2ce58f 100644 --- a/go.sum +++ b/go.sum @@ -201,8 +201,8 @@ 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-client-go v1.28.1-0.20230928142526-622034e3f57b h1:WXxRXkWaxFRdMechZJ0pkzZWCI6HNqlxLzAfr3qXjP8= -github.com/jfrog/jfrog-client-go v1.28.1-0.20230928142526-622034e3f57b/go.mod h1:AePTNv5H1YSGycxiL+1jXHCzqu3rCGruVP7S0N+BEEo= +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/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=