diff --git a/.changelog/1074.txt b/.changelog/1074.txt new file mode 100644 index 00000000000..1ecbf322893 --- /dev/null +++ b/.changelog/1074.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +access_service_token: add support for refreshing an existing token in place +``` diff --git a/access_service_tokens.go b/access_service_tokens.go index fec466cb996..da3debf2278 100644 --- a/access_service_tokens.go +++ b/access_service_tokens.go @@ -30,6 +30,17 @@ type AccessServiceTokenUpdateResponse struct { ClientID string `json:"client_id"` } +// AccessServiceTokenRefreshResponse represents the response from the API +// when an existing service token is refreshed to last longer. +type AccessServiceTokenRefreshResponse struct { + CreatedAt *time.Time `json:"created_at"` + UpdatedAt *time.Time `json:"updated_at"` + ExpiresAt *time.Time `json:"expires_at"` + ID string `json:"id"` + Name string `json:"name"` + ClientID string `json:"client_id"` +} + // AccessServiceTokenCreateResponse is the same API response as the Update // operation with the exception that the `ClientSecret` is present in a // Create operation. @@ -78,6 +89,15 @@ type AccessServiceTokensUpdateDetailResponse struct { Result AccessServiceTokenUpdateResponse `json:"result"` } +// AccessServiceTokensRefreshDetailResponse is the API response, containing a +// single Access Service Token. +type AccessServiceTokensRefreshDetailResponse struct { + Success bool `json:"success"` + Errors []string `json:"errors"` + Messages []string `json:"messages"` + Result AccessServiceTokenRefreshResponse `json:"result"` +} + // AccessServiceTokens returns all Access Service Tokens for an account. // // API reference: https://api.cloudflare.com/#access-service-tokens-list-access-service-tokens @@ -213,3 +233,24 @@ func (api *API) deleteAccessServiceToken(ctx context.Context, id, uuid string, r return accessServiceTokenUpdate.Result, nil } + +// RefreshAccessServiceToken updates the expiry of an Access Service Token +// in place. +// +// API reference: https://api.cloudflare.com/#access-service-tokens-refresh-a-service-token +func (api *API) RefreshAccessServiceToken(ctx context.Context, rc *ResourceContainer, id string) (AccessServiceTokenRefreshResponse, error) { + uri := fmt.Sprintf("/%s/%s/access/service_tokens/%s/refresh", rc.Level, rc.Identifier, id) + + res, err := api.makeRequestContext(ctx, http.MethodPost, uri, nil) + if err != nil { + return AccessServiceTokenRefreshResponse{}, err + } + + var accessServiceTokenRefresh AccessServiceTokensRefreshDetailResponse + err = json.Unmarshal(res, &accessServiceTokenRefresh) + if err != nil { + return AccessServiceTokenRefreshResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return accessServiceTokenRefresh.Result, nil +} diff --git a/access_service_tokens_test.go b/access_service_tokens_test.go index fd6df1c2b8b..391a88b92c3 100644 --- a/access_service_tokens_test.go +++ b/access_service_tokens_test.go @@ -167,6 +167,47 @@ func TestUpdateAccessServiceToken(t *testing.T) { } } +func TestRefreshAccessServiceToken(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result": { + "created_at": "2014-01-01T05:20:00.12345Z", + "updated_at": "2014-01-01T05:20:00.12345Z", + "expires_at": "2015-01-01T05:20:00.12345Z", + "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", + "name": "CI/CD token", + "client_id": "88bf3b6d86161464f6509f7219099e57.access.example.com" + } + } + `) + } + + expected := AccessServiceTokenRefreshResponse{ + CreatedAt: &createdAt, + UpdatedAt: &updatedAt, + ExpiresAt: &expiresAt, + ID: "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", + Name: "CI/CD token", + ClientID: "88bf3b6d86161464f6509f7219099e57.access.example.com", + } + + mux.HandleFunc("/accounts/"+testAccountID+"/access/service_tokens/f174e90a-fafe-4643-bbbc-4a0ed4fc8415/refresh", handler) + + actual, err := client.RefreshAccessServiceToken(context.Background(), AccountIdentifier(testAccountID), "f174e90a-fafe-4643-bbbc-4a0ed4fc8415") + + if assert.NoError(t, err) { + assert.Equal(t, expected, actual) + } +} + func TestDeleteAccessServiceToken(t *testing.T) { setup() defer teardown()