Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Platform Access Token #822

Merged
merged 7 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 40 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@
- [Cleaning Unreferenced Git LFS Files from Artifactory](#cleaning-unreferenced-git-lfs-files-from-artifactory)
- [Executing AQLs](#executing-aqls)
- [Reading Files in Artifactory](#reading-files-in-artifactory)
- [Creating an Access Token](#creating-an-access-token)
- [Fetching Access Tokens](#fetching-access-tokens)
- [Fetching Access Tokens of a User](#fetching-access-tokens-of-a-user)
- [Refreshing an Access Token](#refreshing-an-access-token)
- [Revoking an Access Token](#revoking-an-access-token)
- [Creating an Artifactory Access Token](#creating-an-artifactory-access-token)
- [Fetching Artifactory Access Tokens](#fetching-artifactory-access-tokens)
- [Fetching Artifactory Access Tokens of a User](#fetching-artifactory-access-tokens-of-a-user)
- [Refreshing an Artifactory Access Token](#refreshing-an-artifactory-access-token)
- [Revoking an Artifactory Access Token](#revoking-an-artifactory-access-token)
- [Create API Key](#create-api-key)
- [Regenerate API Key](#regenerate-api-key)
- [Get API Key](#get-api-key)
Expand Down Expand Up @@ -111,13 +111,15 @@
- [Getting a Project](#getting-a-project)
- [Getting all Projects](#getting-all-projects)
- [Assigning Repository to Project](#assigning-repository-to-project)
- [Unassigning Repository from Project](#unassigning-repository-from-project)
- [Un-assigning Repository from Project](#un-assigning-repository-from-project)
- [Get all groups assigned to a project](#get-all-groups-assigned-to-a-project)
- [Get a specific group assigned to a project](#get-a-specific-group-assigned-to-a-project)
- [Add or update a group assigned to a project](#add-or-update-a-group-assigned-to-a-project)
- [Remove a group from a project](#remove-a-group-from-a-project)
- [Send Web Login Authentication Request](#send-web-login-authentication-request)
- [Get Web Login Authentication Token](#get-web-login-authentication-token)
- [Creating an Access Token](#creating-an-access-token)
- [Refreshing an Access Token](#refreshing-an-access-token)
- [Distribution APIs](#distribution-apis)
- [Creating Distribution Service Manager](#creating-distribution-service-manager)
- [Creating Distribution Details](#creating-distribution-details)
Expand Down Expand Up @@ -725,7 +727,7 @@ rtManager.Aql(aql string)
rtManager.ReadRemoteFile(FilePath string)
```

#### Creating an Access Token
#### Creating an Artifactory Access Token

```go
params := services.NewCreateTokenParams()
Expand All @@ -739,19 +741,19 @@ params.Audience = "jfrt@<serviceID1> jfrt@<serviceID2>"
results, err := rtManager.CreateToken(params)
```

#### Fetching Access Tokens
#### Fetching Artifactory Access Tokens

```go
results, err := rtManager.GetTokens()
```

#### Fetching Access Tokens of a User
#### Fetching Artifactory Access Tokens of a User

```g
results, err := rtManager.GetUserTokens(username)
```

#### Refreshing an Access Token
#### Refreshing an Artifactory Access Token

```go
params := services.NewRefreshTokenParams()
Expand All @@ -762,7 +764,7 @@ params.Token.ExpiresIn = 3600
results, err := rtManager.RefreshToken(params)
```

#### Revoking an Access Token
#### Revoking an Artifactory Access Token

```go
params := services.NewRevokeTokenParams()
Expand Down Expand Up @@ -1467,7 +1469,7 @@ err = accessManager.GetAllProjects()
err = accessManager.AssignRepoToProject("repoName", "tstprj", true)
```

#### Unassigning Repository from Project
#### Un-assigning Repository from Project

```go
err = accessManager.AssignRepoToProject("repoName")
Expand Down Expand Up @@ -1515,6 +1517,32 @@ uuid := "09b34617-b48a-455d-8b05-25a6989fb76a"
err = accessManager.GetLoginAuthenticationToken(uuid)
```

#### Creating an Access Token

```go
params := CreateTokenParams{}
params.Scope = "applied-permissions/user"
params.Username = "my-user"
params.ExpiresIn = 12345 // nil = system default, 0 = no expiry.
params.Refreshable = true
params.Audience = "jfrt@<serviceID1>"
reference := true
params.IncludeReferenceToken = &reference
params.ProjectKey = "my-project"
params.Description = "my-token"

results, err := accessManager.CreateToken(params)
```

#### Refreshing an Access Token

```go
params := accessServices.CreateTokenParams{}
params.RefreshToken = "<refresh token>"

results, err := accessManager.RefreshToken(params)
```

## Distribution APIs

### Creating Distribution Service Manager
Expand Down
41 changes: 22 additions & 19 deletions access/services/accesstoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ type TokenService struct {

type CreateTokenParams struct {
auth.CommonTokenParams
IncludeReferenceToken *bool `json:"include_reference_token,omitempty"`
IncludeReferenceToken *bool `json:"include_reference_token,omitempty"`
Username string `json:"username,omitempty"`
ProjectKey string `json:"project_key,omitempty"`
Description string `json:"description,omitempty"`
}

func NewCreateTokenParams(params CreateTokenParams) CreateTokenParams {
Expand All @@ -37,22 +40,19 @@ func (ps *TokenService) CreateAccessToken(params CreateTokenParams) (auth.Create
return ps.createAccessToken(params)
}

func (ps *TokenService) RefreshAccessToken(token CreateTokenParams) (auth.CreateTokenResponseData, error) {
param, err := createRefreshTokenRequestParams(token)
func (ps *TokenService) RefreshAccessToken(params CreateTokenParams) (auth.CreateTokenResponseData, error) {
refreshParams, err := prepareForRefresh(params)
if err != nil {
return auth.CreateTokenResponseData{}, err
}
return ps.createAccessToken(*param)
return ps.createAccessToken(*refreshParams)
}

// createAccessToken is used to create & refresh access tokens.
func (ps *TokenService) createAccessToken(params CreateTokenParams) (auth.CreateTokenResponseData, error) {
// Set the request headers
tokenInfo := auth.CreateTokenResponseData{}
func (ps *TokenService) createAccessToken(params CreateTokenParams) (tokenInfo auth.CreateTokenResponseData, err error) {
httpDetails := ps.ServiceDetails.CreateHttpClientDetails()
utils.SetContentType("application/json", &httpDetails.Headers)
err := ps.addAccessTokenAuthorizationHeader(params, &httpDetails)
if err != nil {
if err = ps.handleUnauthenticated(params, &httpDetails); err != nil {
return tokenInfo, err
}
requestContent, err := json.Marshal(params)
Expand All @@ -71,23 +71,26 @@ func (ps *TokenService) createAccessToken(params CreateTokenParams) (auth.Create
return tokenInfo, errorutils.CheckError(err)
}

func (ps *TokenService) addAccessTokenAuthorizationHeader(params CreateTokenParams, httpDetails *httputils.HttpClientDetails) error {
access := ps.ServiceDetails.GetAccessToken()
if access == "" {
access = params.AccessToken
func (ps *TokenService) handleUnauthenticated(params CreateTokenParams, httpDetails *httputils.HttpClientDetails) error {
// Creating access tokens using username and password is available since Artifactory 7.63.2,
// by enabling "Enable Token Generation via API" in the UI.
if httpDetails.AccessToken != "" || (httpDetails.User != "" && httpDetails.Password != "") {
RobiNino marked this conversation as resolved.
Show resolved Hide resolved
return nil
}
if access == "" {
return errorutils.CheckErrorf("failed: adding accessToken authorization, but No accessToken was provided. ")
// Use token from params if provided.
if params.AccessToken != "" {
httpDetails.AccessToken = params.AccessToken
return nil
}
utils.AddHeader("Authorization", fmt.Sprintf("Bearer %s", access), &httpDetails.Headers)
return nil
return errorutils.CheckErrorf("cannot create access token without credentials")
}

func createRefreshTokenRequestParams(p CreateTokenParams) (*CreateTokenParams, error) {
func prepareForRefresh(p CreateTokenParams) (*CreateTokenParams, error) {
// Validate provided parameters
if p.RefreshToken == "" {
return nil, errorutils.CheckErrorf("error: trying to refresh token, but 'refresh_token' field wasn't provided. ")
return nil, errorutils.CheckErrorf("trying to refresh token, but 'refresh_token' field wasn't provided")
}

params := NewCreateTokenParams(p)
// Set refresh required parameters
params.GrantType = "refresh_token"
Expand Down
3 changes: 2 additions & 1 deletion auth/authutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import (
type CreateTokenResponseData struct {
CommonTokenParams
ReferenceToken string `json:"reference_token,omitempty"`
TokenId string `json:"token_id,omitempty"`
}

type CommonTokenParams struct {
Scope string `json:"scope,omitempty"`
AccessToken string `json:"access_token,omitempty"`
ExpiresIn int `json:"expires_in,omitempty"`
ExpiresIn *uint `json:"expires_in,omitempty"`
TokenType string `json:"token_type,omitempty"`
Refreshable *bool `json:"refreshable,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
Expand Down
10 changes: 5 additions & 5 deletions tests/accesstokens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func testCreateRefreshableToken(t *testing.T) {
assert.NotEqual(t, "", token.AccessToken, "Access token is empty")
assert.NotEqual(t, tokenParams.AccessToken, token.AccessToken, "New access token is identical to original one")
assert.NotEqual(t, "", token.RefreshToken, "Refresh token is empty")
assert.Equal(t, testExpiredInSeconds, token.ExpiresIn)
assert.EqualValues(t, testExpiredInSeconds, *token.ExpiresIn)
assert.Empty(t, token.ReferenceToken)
}

Expand All @@ -36,7 +36,7 @@ func testAccessTokenWithReference(t *testing.T) {
assert.NotEqual(t, "", token.AccessToken, "Access token is empty")
assert.NotEqual(t, tokenParams.AccessToken, token.AccessToken, "New access token is identical to original one")
assert.NotEqual(t, "", token.RefreshToken, "Refresh token is empty")
assert.Equal(t, testExpiredInSeconds, token.ExpiresIn)
assert.EqualValues(t, testExpiredInSeconds, *token.ExpiresIn)
assert.NotEmpty(t, token.ReferenceToken)
}

Expand All @@ -52,13 +52,13 @@ func testRefreshTokenTest(t *testing.T) {
// Validate
assert.NotEqual(t, token.AccessToken, newToken.AccessToken, "New access token is identical to original one")
assert.NotEqual(t, token.RefreshToken, newToken.RefreshToken, "New refresh token is identical to original one")
assert.Equal(t, token.ExpiresIn, newToken.ExpiresIn, "New access token's expiration is different from original one")
assert.EqualValues(t, token.ExpiresIn, newToken.ExpiresIn, "New access token's expiration is different from original one")
assert.Empty(t, token.ReferenceToken)
}

func createRefreshableAccessTokenParams(expiredIn int) services.CreateTokenParams {
func createRefreshableAccessTokenParams(expiredIn uint) services.CreateTokenParams {
tokenParams := services.CreateTokenParams{}
tokenParams.ExpiresIn = expiredIn
tokenParams.ExpiresIn = &expiredIn
tokenParams.Refreshable = utils.Pointer(true)
tokenParams.Audience = "*@*"
return tokenParams
Expand Down
Loading