github.com/cobalt77/jfrog-client-go@v0.14.5/artifactory/services/security.go (about)

     1  package services
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"net/url"
     9  	"strconv"
    10  
    11  	rthttpclient "github.com/cobalt77/jfrog-client-go/artifactory/httpclient"
    12  	"github.com/cobalt77/jfrog-client-go/artifactory/services/utils"
    13  	"github.com/cobalt77/jfrog-client-go/auth"
    14  	clientutils "github.com/cobalt77/jfrog-client-go/utils"
    15  	"github.com/cobalt77/jfrog-client-go/utils/errorutils"
    16  )
    17  
    18  const tokenPath = "api/security/token"
    19  const APIKeyPath = "api/security/apiKey"
    20  
    21  type SecurityService struct {
    22  	client     *rthttpclient.ArtifactoryHttpClient
    23  	ArtDetails auth.ServiceDetails
    24  }
    25  
    26  func NewSecurityService(client *rthttpclient.ArtifactoryHttpClient) *SecurityService {
    27  	return &SecurityService{client: client}
    28  }
    29  
    30  func (ss *SecurityService) getArtifactoryDetails() auth.ServiceDetails {
    31  	return ss.ArtDetails
    32  }
    33  
    34  // RegenerateAPIKey regenerates the API Key in Artifactory
    35  func (ss *SecurityService) RegenerateAPIKey() (string, error) {
    36  	httpClientDetails := ss.ArtDetails.CreateHttpClientDetails()
    37  
    38  	reqURL, err := utils.BuildArtifactoryUrl(ss.ArtDetails.GetUrl(), APIKeyPath, nil)
    39  	if err != nil {
    40  		return "", err
    41  	}
    42  
    43  	resp, body, err := ss.client.SendPut(reqURL, nil, &httpClientDetails)
    44  	if err != nil {
    45  		return "", err
    46  	}
    47  
    48  	if resp.StatusCode != http.StatusOK {
    49  		return "", errors.New("API key reneration failed with status: " + resp.Status + "\n" + clientutils.IndentJson(body))
    50  	}
    51  
    52  	var data map[string]interface{} = make(map[string]interface{})
    53  	if err := json.Unmarshal(body, &data); err != nil {
    54  		return "", fmt.Errorf("Unable to decode json. Error: %w Upstream response: %s", err, string(body))
    55  	}
    56  
    57  	apiKey := data["apiKey"].(string)
    58  	return apiKey, nil
    59  }
    60  
    61  func (ss *SecurityService) CreateToken(params CreateTokenParams) (CreateTokenResponseData, error) {
    62  	artifactoryUrl := ss.ArtDetails.GetUrl()
    63  	data := buildCreateTokenUrlValues(params)
    64  	httpClientsDetails := ss.getArtifactoryDetails().CreateHttpClientDetails()
    65  	resp, body, err := ss.client.SendPostForm(artifactoryUrl+tokenPath, data, &httpClientsDetails)
    66  	tokenInfo := CreateTokenResponseData{}
    67  	if err != nil {
    68  		return tokenInfo, err
    69  	}
    70  	if resp.StatusCode != http.StatusOK {
    71  		return tokenInfo, errorutils.CheckError(
    72  			errors.New("Artifactory response: " + resp.Status + "\n" + clientutils.IndentJson(body)))
    73  	}
    74  	if err = json.Unmarshal(body, &tokenInfo); err != nil {
    75  		return tokenInfo, errorutils.CheckError(err)
    76  	}
    77  	return tokenInfo, err
    78  }
    79  
    80  func (ss *SecurityService) GetTokens() (GetTokensResponseData, error) {
    81  	artifactoryUrl := ss.ArtDetails.GetUrl()
    82  	httpClientsDetails := ss.getArtifactoryDetails().CreateHttpClientDetails()
    83  	resp, body, _, err := ss.client.SendGet(artifactoryUrl+tokenPath, true, &httpClientsDetails)
    84  	tokens := GetTokensResponseData{}
    85  	if err != nil {
    86  		return tokens, err
    87  	}
    88  	if resp.StatusCode != http.StatusOK {
    89  		return tokens, errorutils.CheckError(
    90  			errors.New("Artifactory response: " + resp.Status + "\n" + clientutils.IndentJson(body)))
    91  	}
    92  	if err = json.Unmarshal(body, &tokens); err != nil {
    93  		return tokens, errorutils.CheckError(err)
    94  	}
    95  	return tokens, err
    96  }
    97  
    98  func (ss *SecurityService) RefreshToken(params RefreshTokenParams) (CreateTokenResponseData, error) {
    99  	artifactoryUrl := ss.ArtDetails.GetUrl()
   100  	data := buildRefreshTokenUrlValues(params)
   101  	httpClientsDetails := ss.getArtifactoryDetails().CreateHttpClientDetails()
   102  	resp, body, err := ss.client.SendPostForm(artifactoryUrl+tokenPath, data, &httpClientsDetails)
   103  	tokenInfo := CreateTokenResponseData{}
   104  	if err != nil {
   105  		return tokenInfo, err
   106  	}
   107  	if resp.StatusCode != http.StatusOK {
   108  		return tokenInfo, errorutils.CheckError(
   109  			errors.New("Artifactory response: " + resp.Status + "\n" + clientutils.IndentJson(body)))
   110  	}
   111  	if err = json.Unmarshal(body, &tokenInfo); err != nil {
   112  		return tokenInfo, errorutils.CheckError(err)
   113  	}
   114  	return tokenInfo, err
   115  }
   116  
   117  func (ss *SecurityService) RevokeToken(params RevokeTokenParams) (string, error) {
   118  	artifactoryUrl := ss.ArtDetails.GetUrl()
   119  	requestFullUrl := artifactoryUrl + tokenPath + "/revoke"
   120  	httpClientsDetails := ss.getArtifactoryDetails().CreateHttpClientDetails()
   121  	data := buildRevokeTokenUrlValues(params)
   122  	resp, body, err := ss.client.SendPostForm(requestFullUrl, data, &httpClientsDetails)
   123  	if err != nil {
   124  		return "", err
   125  	}
   126  	if resp.StatusCode != http.StatusOK {
   127  		return "", errorutils.CheckError(
   128  			errors.New("Artifactory response: " + resp.Status + "\n" + clientutils.IndentJson(body)))
   129  	}
   130  	return string(body), err
   131  }
   132  
   133  func buildCreateTokenUrlValues(params CreateTokenParams) url.Values {
   134  	// Gathers required data while avoiding default/ignored values
   135  	data := url.Values{}
   136  	if params.Refreshable {
   137  		data.Set("refreshable", "true")
   138  	}
   139  	if params.Scope != "" {
   140  		data.Set("scope", params.Scope)
   141  	}
   142  	if params.Username != "" {
   143  		data.Set("username", params.Username)
   144  	}
   145  	if params.Audience != "" {
   146  		data.Set("audience", params.Audience)
   147  	}
   148  	if params.ExpiresIn >= 0 {
   149  		data.Set("expires_in", strconv.Itoa(params.ExpiresIn))
   150  	}
   151  	return data
   152  }
   153  
   154  func buildRefreshTokenUrlValues(params RefreshTokenParams) url.Values {
   155  	data := buildCreateTokenUrlValues(params.Token)
   156  
   157  	// <grant_type> is used to tell the rest api whether to create or refresh a token.
   158  	// Both operations are performed by the same endpoint.
   159  	data.Set("grant_type", "refresh_token")
   160  
   161  	if params.RefreshToken != "" {
   162  		data.Set("refresh_token", params.RefreshToken)
   163  	}
   164  	if params.AccessToken != "" {
   165  		data.Set("access_token", params.AccessToken)
   166  	}
   167  	return data
   168  }
   169  
   170  func buildRevokeTokenUrlValues(params RevokeTokenParams) url.Values {
   171  	data := url.Values{}
   172  	if params.Token != "" {
   173  		data.Set("token", params.Token)
   174  	}
   175  	if params.TokenId != "" {
   176  		data.Set("token_id", params.TokenId)
   177  	}
   178  	return data
   179  }
   180  
   181  type CreateTokenResponseData struct {
   182  	Scope        string `json:"scope,omitempty"`
   183  	AccessToken  string `json:"access_token,omitempty"`
   184  	ExpiresIn    int    `json:"expires_in,omitempty"`
   185  	TokenType    string `json:"token_type,omitempty"`
   186  	RefreshToken string `json:"refresh_token,omitempty"`
   187  }
   188  
   189  type GetTokensResponseData struct {
   190  	Tokens []struct {
   191  		Issuer      string `json:"issuer,omitempty"`
   192  		Subject     string `json:"subject,omitempty"`
   193  		Refreshable bool   `json:"refreshable,omitempty"`
   194  		Expiry      int    `json:"expiry,omitempty"`
   195  		TokenId     string `json:"token_id,omitempty"`
   196  		IssuedAt    int    `json:"issued_at,omitempty"`
   197  	}
   198  }
   199  
   200  type CreateTokenParams struct {
   201  	Scope       string
   202  	Username    string
   203  	ExpiresIn   int
   204  	Refreshable bool
   205  	Audience    string
   206  }
   207  
   208  type RefreshTokenParams struct {
   209  	Token        CreateTokenParams
   210  	RefreshToken string
   211  	AccessToken  string
   212  }
   213  
   214  type RevokeTokenParams struct {
   215  	Token   string
   216  	TokenId string
   217  }
   218  
   219  func NewCreateTokenParams() CreateTokenParams {
   220  	return CreateTokenParams{ExpiresIn: -1}
   221  }
   222  
   223  func NewRefreshTokenParams() RefreshTokenParams {
   224  	return RefreshTokenParams{Token: NewCreateTokenParams()}
   225  }
   226  
   227  func NewRevokeTokenParams() RevokeTokenParams {
   228  	return RevokeTokenParams{}
   229  }