Skip to content
Open
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
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@
- [Creating an Access Token](#creating-an-access-token)
- [Refreshing an Access Token](#refreshing-an-access-token)
- [Exchanging an OIDC Access Token](#exchanging-an-oidc-access-token)
- [Getting Access Tokens](#getting-access-tokens)
- [Getting an Access Token by ID](#getting-an-access-token-by-id)
- [Revoking an Access Token by ID](#revoking-an-access-token-by-id)
- [Distribution APIs](#distribution-apis)
- [Creating Distribution Service Manager](#creating-distribution-service-manager)
- [Creating Distribution Details](#creating-distribution-details)
Expand Down Expand Up @@ -1879,6 +1882,37 @@ params := services.CreateOidcTokenParams{
response, err = servicesManager.ExchangeOidcToken(params)
```

#### Getting Access Tokens

```go
params := services.GetTokensParams{
// Optional filters
Description: "my-token-description", // Filter by token description
Username: "admin", // Filter by username
Refreshable: utils.Pointer(true), // Filter by refreshable status
TokenId: "token-id", // Filter by specific token ID
OrderBy: "token_id", // Order by field (created|token_id|owner|subject|expiry)
DescendingOrder: utils.Pointer(false), // Sort order (true for descending)
}

tokens, err := accessManager.GetTokens(params)
```

#### Getting an Access Token by ID

```go
token, err := accessManager.GetTokenByID("my-token-id")

# currently used token
token, err := accessManager.GetTokenByID("me")
```

#### Revoking an Access Token by ID

```go
err := accessManager.RevokeTokenByID("my-token-id")
```

## Distribution APIs

### Creating Distribution Service Manager
Expand Down
18 changes: 18 additions & 0 deletions access/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,21 @@ func (sm *AccessServicesManager) ExchangeOidcToken(params services.CreateOidcTok
tokenService.ServiceDetails = sm.config.GetServiceDetails()
return tokenService.ExchangeOidcToken(params)
}

func (sm *AccessServicesManager) GetTokens(params services.GetTokensParams) ([]services.TokenInfo, error) {
tokenService := services.NewTokenService(sm.client)
tokenService.ServiceDetails = sm.config.GetServiceDetails()
return tokenService.GetTokens(params)
}

func (sm *AccessServicesManager) GetTokenByID(tokenId string) (*services.TokenInfo, error) {
tokenService := services.NewTokenService(sm.client)
tokenService.ServiceDetails = sm.config.GetServiceDetails()
return tokenService.GetTokenByID(tokenId)
}

func (sm *AccessServicesManager) RevokeTokenByID(tokenId string) error {
tokenService := services.NewTokenService(sm.client)
tokenService.ServiceDetails = sm.config.GetServiceDetails()
return tokenService.RevokeTokenByID(tokenId)
}
108 changes: 107 additions & 1 deletion access/services/accesstoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package services
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"

"github.com/jfrog/jfrog-client-go/auth"
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
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"
"net/http"
)

// #nosec G101 -- False positive - no hardcoded credentials.
Expand Down Expand Up @@ -50,6 +53,31 @@ type CreateOidcTokenParams struct {
ApplicationKey string `json:"application_key,omitempty"`
}

type GetTokensParams struct {
Description string `url:"description,omitempty"`
Username string `url:"username,omitempty"`
Refreshable *bool `url:"refreshable,omitempty"`
TokenId string `url:"token_id,omitempty"`
OrderBy string `url:"order_by,omitempty"`
DescendingOrder *bool `url:"descending_order,omitempty"`
}

type TokenInfos struct {
Tokens []TokenInfo `json:"tokens"`
}

type TokenInfo struct {
TokenId string `json:"token_id"`
Subject string `json:"subject"`
Expiry int64 `json:"expiry,omitempty"`
IssuedAt int64 `json:"issued_at"`
Issuer string `json:"issuer"`
Description string `json:"description,omitempty"`
Refreshable bool `json:"refreshable,omitempty"`
Scope string `json:"scope,omitempty"`
LastUsed int64 `json:"last_used,omitempty"`
}

func NewCreateTokenParams(params CreateTokenParams) CreateTokenParams {
return CreateTokenParams{CommonTokenParams: params.CommonTokenParams, IncludeReferenceToken: params.IncludeReferenceToken}
}
Expand Down Expand Up @@ -127,6 +155,84 @@ func (ps *TokenService) ExchangeOidcToken(params CreateOidcTokenParams) (auth.Oi
return tokenInfo, errorutils.CheckError(err)
}

func (ps *TokenService) GetTokens(params GetTokensParams) ([]TokenInfo, error) {
httpDetails := ps.ServiceDetails.CreateHttpClientDetails()
requestUrl := fmt.Sprintf("%s%s", ps.ServiceDetails.GetUrl(), tokensApi)

// Build query parameters manually
queryParams := url.Values{}
if params.Description != "" {
queryParams.Add("description", params.Description)
}
if params.Username != "" {
queryParams.Add("username", params.Username)
}
if params.Refreshable != nil {
queryParams.Add("refreshable", strconv.FormatBool(*params.Refreshable))
}
if params.TokenId != "" {
queryParams.Add("token_id", params.TokenId)
}
if params.OrderBy != "" {
queryParams.Add("order_by", params.OrderBy)
}
if params.DescendingOrder != nil {
queryParams.Add("descending_order", strconv.FormatBool(*params.DescendingOrder))
}

if queryString := queryParams.Encode(); queryString != "" {
requestUrl += "?" + queryString
}

resp, body, _, err := ps.client.SendGet(requestUrl, true, &httpDetails)
if err != nil {
return nil, err
}
if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK); err != nil {
return nil, err
}

var tokenInfos TokenInfos
err = json.Unmarshal(body, &tokenInfos)
return tokenInfos.Tokens, errorutils.CheckError(err)
}

func (ps *TokenService) GetTokenByID(tokenId string) (*TokenInfo, error) {
if tokenId == "" {
return nil, errorutils.CheckErrorf("token ID cannot be empty")
}

httpDetails := ps.ServiceDetails.CreateHttpClientDetails()
url := fmt.Sprintf("%s%s/%s", ps.ServiceDetails.GetUrl(), tokensApi, tokenId)

resp, body, _, err := ps.client.SendGet(url, true, &httpDetails)
if err != nil {
return nil, err
}
if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK); err != nil {
return nil, err
}

var tokenInfo TokenInfo
err = json.Unmarshal(body, &tokenInfo)
return &tokenInfo, errorutils.CheckError(err)
}

func (ps *TokenService) RevokeTokenByID(tokenId string) error {
if tokenId == "" {
return errorutils.CheckErrorf("token ID cannot be empty")
}

httpDetails := ps.ServiceDetails.CreateHttpClientDetails()
url := fmt.Sprintf("%s%s/%s", ps.ServiceDetails.GetUrl(), tokensApi, tokenId)

resp, body, err := ps.client.SendDelete(url, nil, &httpDetails)
if err != nil {
return err
}
return errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK, http.StatusNoContent)
}

func prepareForRefresh(p CreateTokenParams) (*CreateTokenParams, error) {
// Validate provided parameters
if p.RefreshToken == "" {
Expand Down
Loading