Skip to content

Commit 4019e88

Browse files
jacobbednarzpatryk
authored andcommitted
Add support for Access Service Tokens (cloudflare#339)
Updates the library to support creating, updating and deleting a Service Token for Cloudflare Access. API documentation: https://api.cloudflare.com/#access-service-tokens
1 parent 2862fce commit 4019e88

File tree

2 files changed

+346
-0
lines changed

2 files changed

+346
-0
lines changed

access_service_tokens.go

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package cloudflare
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"time"
7+
8+
"github.com/pkg/errors"
9+
)
10+
11+
// AccessServiceToken represents an Access Service Token.
12+
type AccessServiceToken struct {
13+
ClientID string `json:"client_id"`
14+
CreatedAt *time.Time `json:"created_at"`
15+
ExpiresAt *time.Time `json:"expires_at"`
16+
ID string `json:"id"`
17+
Name string `json:"name"`
18+
UpdatedAt *time.Time `json:"updated_at"`
19+
}
20+
21+
// AccessServiceTokenUpdateResponse represents the response from the API
22+
// when a new Service Token is updated. This base struct is also used in the
23+
// Create as they are very similar responses.
24+
type AccessServiceTokenUpdateResponse struct {
25+
CreatedAt *time.Time `json:"created_at"`
26+
UpdatedAt *time.Time `json:"updated_at"`
27+
ID string `json:"id"`
28+
Name string `json:"name"`
29+
ClientID string `json:"client_id"`
30+
}
31+
32+
// AccessServiceTokenCreateResponse is the same API response as the Update
33+
// operation with the exception that the `ClientSecret` is present in a
34+
// Create operation.
35+
type AccessServiceTokenCreateResponse struct {
36+
CreatedAt *time.Time `json:"created_at"`
37+
UpdatedAt *time.Time `json:"updated_at"`
38+
ID string `json:"id"`
39+
Name string `json:"name"`
40+
ClientID string `json:"client_id"`
41+
ClientSecret string `json:"client_secret"`
42+
}
43+
44+
// AccessServiceTokensListResponse represents the response from the list
45+
// Access Service Tokens endpoint.
46+
type AccessServiceTokensListResponse struct {
47+
Result []AccessServiceToken `json:"result"`
48+
Response
49+
ResultInfo `json:"result_info"`
50+
}
51+
52+
// AccessServiceTokensDetailResponse is the API response, containing a single
53+
// Access Service Token.
54+
type AccessServiceTokensDetailResponse struct {
55+
Success bool `json:"success"`
56+
Errors []string `json:"errors"`
57+
Messages []string `json:"messages"`
58+
Result AccessServiceToken `json:"result"`
59+
}
60+
61+
// AccessServiceTokensCreationDetailResponse is the API response, containing a
62+
// single Access Service Token.
63+
type AccessServiceTokensCreationDetailResponse struct {
64+
Success bool `json:"success"`
65+
Errors []string `json:"errors"`
66+
Messages []string `json:"messages"`
67+
Result AccessServiceTokenCreateResponse `json:"result"`
68+
}
69+
70+
// AccessServiceTokensUpdateDetailResponse is the API response, containing a
71+
// single Access Service Token.
72+
type AccessServiceTokensUpdateDetailResponse struct {
73+
Success bool `json:"success"`
74+
Errors []string `json:"errors"`
75+
Messages []string `json:"messages"`
76+
Result AccessServiceTokenUpdateResponse `json:"result"`
77+
}
78+
79+
// AccessServiceTokens returns all Access Service Tokens for an account.
80+
//
81+
// API reference: https://api.cloudflare.com/#access-service-tokens-list-access-service-tokens
82+
func (api *API) AccessServiceTokens(accountID string) ([]AccessServiceToken, ResultInfo, error) {
83+
uri := "/accounts/" + accountID + "/access/service_tokens"
84+
85+
res, err := api.makeRequest("GET", uri, nil)
86+
if err != nil {
87+
return []AccessServiceToken{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError)
88+
}
89+
90+
var accessServiceTokensListResponse AccessServiceTokensListResponse
91+
err = json.Unmarshal(res, &accessServiceTokensListResponse)
92+
if err != nil {
93+
return []AccessServiceToken{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError)
94+
}
95+
96+
return accessServiceTokensListResponse.Result, accessServiceTokensListResponse.ResultInfo, nil
97+
}
98+
99+
// CreateAccessServiceToken creates a new Access Service Token for an account.
100+
//
101+
// API reference: https://api.cloudflare.com/#access-service-tokens-create-access-service-token
102+
func (api *API) CreateAccessServiceToken(accountID, name string) (AccessServiceTokenCreateResponse, error) {
103+
uri := "/accounts/" + accountID + "/access/service_tokens"
104+
marshalledName, _ := json.Marshal(struct {
105+
Name string `json:"name"`
106+
}{name})
107+
108+
res, err := api.makeRequest("POST", uri, marshalledName)
109+
110+
if err != nil {
111+
return AccessServiceTokenCreateResponse{}, errors.Wrap(err, errMakeRequestError)
112+
}
113+
114+
var accessServiceTokenCreation AccessServiceTokensCreationDetailResponse
115+
err = json.Unmarshal(res, &accessServiceTokenCreation)
116+
if err != nil {
117+
return AccessServiceTokenCreateResponse{}, errors.Wrap(err, errUnmarshalError)
118+
}
119+
120+
return accessServiceTokenCreation.Result, nil
121+
}
122+
123+
// UpdateAccessServiceToken updates an existing Access Service Token for an
124+
// account.
125+
//
126+
// API reference: https://api.cloudflare.com/#access-service-tokens-update-access-service-token
127+
func (api *API) UpdateAccessServiceToken(accountID, uuid, name string) (AccessServiceTokenUpdateResponse, error) {
128+
uri := fmt.Sprintf("/accounts/%s/access/service_tokens/%s", accountID, uuid)
129+
130+
marshalledName, _ := json.Marshal(struct {
131+
Name string `json:"name"`
132+
}{name})
133+
134+
res, err := api.makeRequest("PUT", uri, marshalledName)
135+
if err != nil {
136+
return AccessServiceTokenUpdateResponse{}, errors.Wrap(err, errMakeRequestError)
137+
}
138+
139+
var accessServiceTokenUpdate AccessServiceTokensUpdateDetailResponse
140+
err = json.Unmarshal(res, &accessServiceTokenUpdate)
141+
if err != nil {
142+
return AccessServiceTokenUpdateResponse{}, errors.Wrap(err, errUnmarshalError)
143+
}
144+
145+
return accessServiceTokenUpdate.Result, nil
146+
}
147+
148+
// DeleteAccessServiceToken removes an existing Access Service Token for an
149+
// account.
150+
//
151+
// API reference: https://api.cloudflare.com/#access-service-tokens-delete-access-service-token
152+
func (api *API) DeleteAccessServiceToken(accountID, uuid string) (AccessServiceTokenUpdateResponse, error) {
153+
uri := fmt.Sprintf("/accounts/%s/access/service_tokens/%s", accountID, uuid)
154+
155+
res, err := api.makeRequest("DELETE", uri, nil)
156+
if err != nil {
157+
return AccessServiceTokenUpdateResponse{}, errors.Wrap(err, errMakeRequestError)
158+
}
159+
160+
var accessServiceTokenUpdate AccessServiceTokensUpdateDetailResponse
161+
err = json.Unmarshal(res, &accessServiceTokenUpdate)
162+
if err != nil {
163+
return AccessServiceTokenUpdateResponse{}, errors.Wrap(err, errUnmarshalError)
164+
}
165+
166+
return accessServiceTokenUpdate.Result, nil
167+
}

access_service_tokens_test.go

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package cloudflare
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"testing"
7+
"time"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestAccessServiceTokens(t *testing.T) {
13+
setup()
14+
defer teardown()
15+
16+
handler := func(w http.ResponseWriter, r *http.Request) {
17+
assert.Equal(t, r.Method, "GET", "Expected method 'GET', got %s", r.Method)
18+
w.Header().Set("content-type", "application/json")
19+
fmt.Fprintf(w, `{
20+
"success": true,
21+
"errors": [],
22+
"messages": [],
23+
"result": [
24+
{
25+
"created_at": "2014-01-01T05:20:00.12345Z",
26+
"updated_at": "2014-01-01T05:20:00.12345Z",
27+
"id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
28+
"name": "CI/CD token",
29+
"client_id": "88bf3b6d86161464f6509f7219099e57.access.example.com"
30+
}
31+
]
32+
}
33+
`)
34+
}
35+
36+
mux.HandleFunc("/accounts/01a7362d577a6c3019a474fd6f485823/access/service_tokens", handler)
37+
createdAt, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00.12345Z")
38+
updatedAt, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00.12345Z")
39+
40+
want := []AccessServiceToken{AccessServiceToken{
41+
CreatedAt: &createdAt,
42+
UpdatedAt: &updatedAt,
43+
ID: "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
44+
Name: "CI/CD token",
45+
ClientID: "88bf3b6d86161464f6509f7219099e57.access.example.com",
46+
}}
47+
48+
actual, _, err := client.AccessServiceTokens("01a7362d577a6c3019a474fd6f485823")
49+
50+
if assert.NoError(t, err) {
51+
assert.Equal(t, want, actual)
52+
}
53+
}
54+
55+
func TestCreateAccessServiceToken(t *testing.T) {
56+
setup()
57+
defer teardown()
58+
59+
handler := func(w http.ResponseWriter, r *http.Request) {
60+
assert.Equal(t, r.Method, "POST", "Expected method 'POST', got %s", r.Method)
61+
w.Header().Set("content-type", "application/json")
62+
fmt.Fprintf(w, `{
63+
"success": true,
64+
"errors": [],
65+
"messages": [],
66+
"result": {
67+
"created_at": "2014-01-01T05:20:00.12345Z",
68+
"updated_at": "2014-01-01T05:20:00.12345Z",
69+
"id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
70+
"name": "CI/CD token",
71+
"client_id": "88bf3b6d86161464f6509f7219099e57.access.example.com",
72+
"client_secret": "bdd31cbc4dec990953e39163fbbb194c93313ca9f0a6e420346af9d326b1d2a5"
73+
}
74+
}
75+
`)
76+
}
77+
78+
mux.HandleFunc("/accounts/01a7362d577a6c3019a474fd6f485823/access/service_tokens", handler)
79+
80+
expected := AccessServiceTokenCreateResponse{
81+
CreatedAt: &createdAt,
82+
UpdatedAt: &updatedAt,
83+
ID: "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
84+
Name: "CI/CD token",
85+
ClientID: "88bf3b6d86161464f6509f7219099e57.access.example.com",
86+
ClientSecret: "bdd31cbc4dec990953e39163fbbb194c93313ca9f0a6e420346af9d326b1d2a5",
87+
}
88+
89+
actual, err := client.CreateAccessServiceToken("01a7362d577a6c3019a474fd6f485823", "CI/CD token")
90+
91+
if assert.NoError(t, err) {
92+
assert.Equal(t, expected, actual)
93+
}
94+
}
95+
96+
func TestUpdateAccessServiceToken(t *testing.T) {
97+
setup()
98+
defer teardown()
99+
100+
handler := func(w http.ResponseWriter, r *http.Request) {
101+
assert.Equal(t, r.Method, "PUT", "Expected method 'PUT', got %s", r.Method)
102+
w.Header().Set("content-type", "application/json")
103+
fmt.Fprintf(w, `{
104+
"success": true,
105+
"errors": [],
106+
"messages": [],
107+
"result": {
108+
"created_at": "2014-01-01T05:20:00.12345Z",
109+
"updated_at": "2014-01-01T05:20:00.12345Z",
110+
"id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
111+
"name": "CI/CD token",
112+
"client_id": "88bf3b6d86161464f6509f7219099e57.access.example.com"
113+
}
114+
}
115+
`)
116+
}
117+
118+
mux.HandleFunc("/accounts/01a7362d577a6c3019a474fd6f485823/access/service_tokens/f174e90a-fafe-4643-bbbc-4a0ed4fc8415", handler)
119+
120+
expected := AccessServiceTokenUpdateResponse{
121+
CreatedAt: &createdAt,
122+
UpdatedAt: &updatedAt,
123+
ID: "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
124+
Name: "CI/CD token",
125+
ClientID: "88bf3b6d86161464f6509f7219099e57.access.example.com",
126+
}
127+
128+
actual, err := client.UpdateAccessServiceToken(
129+
"01a7362d577a6c3019a474fd6f485823",
130+
"f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
131+
"CI/CD token",
132+
)
133+
134+
if assert.NoError(t, err) {
135+
assert.Equal(t, expected, actual)
136+
}
137+
}
138+
139+
func TestDeleteAccessServiceToken(t *testing.T) {
140+
setup()
141+
defer teardown()
142+
143+
handler := func(w http.ResponseWriter, r *http.Request) {
144+
assert.Equal(t, r.Method, "DELETE", "Expected method 'DELETE', got %s", r.Method)
145+
w.Header().Set("content-type", "application/json")
146+
fmt.Fprintf(w, `{
147+
"success": true,
148+
"errors": [],
149+
"messages": [],
150+
"result": {
151+
"created_at": "2014-01-01T05:20:00.12345Z",
152+
"updated_at": "2014-01-01T05:20:00.12345Z",
153+
"id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
154+
"name": "CI/CD token",
155+
"client_id": "88bf3b6d86161464f6509f7219099e57.access.example.com"
156+
}
157+
}
158+
`)
159+
}
160+
161+
mux.HandleFunc("/accounts/01a7362d577a6c3019a474fd6f485823/access/service_tokens/f174e90a-fafe-4643-bbbc-4a0ed4fc8415", handler)
162+
163+
expected := AccessServiceTokenUpdateResponse{
164+
CreatedAt: &createdAt,
165+
UpdatedAt: &updatedAt,
166+
ID: "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
167+
Name: "CI/CD token",
168+
ClientID: "88bf3b6d86161464f6509f7219099e57.access.example.com",
169+
}
170+
171+
actual, err := client.DeleteAccessServiceToken(
172+
"01a7362d577a6c3019a474fd6f485823",
173+
"f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
174+
)
175+
176+
if assert.NoError(t, err) {
177+
assert.Equal(t, expected, actual)
178+
}
179+
}

0 commit comments

Comments
 (0)