Skip to content

Commit

Permalink
feat(tokens): Adding test and token filter in fixture
Browse files Browse the repository at this point in the history
  • Loading branch information
paketeserrano committed Feb 6, 2025
1 parent 192b724 commit c7a5460
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 54 deletions.
58 changes: 41 additions & 17 deletions upcloud/service/fixtures/token.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,25 @@ interactions:
headers:
Accept:
- application/json
Authorization:
- Basic [REDACTED]
Content-Type:
- application/json
User-Agent:
- upcloud-go-api/8.14.0
url: https://api.upcloud.com/1.3/account/tokens
method: POST
response:
body: '{"token":"ucat_01JFJ0HDPBXE0DJBP9DXNBEGGP","id":"0c0cb933-d5d5-4027-a4f5-20019f30a913","name":"my_1st_token","type":"workspace","created_at":"2024-12-20T12:26:36.619315Z","expires_at":"2025-12-01T00:00:00.000013Z","can_create_tokens":true,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false}'
body: '{"allowed_ip_ranges":["0.0.0.0/0","::/0"],"can_create_tokens":true,"created_at":"2025-02-06T15:33:07.534408Z","expires_at":"2025-12-01T00:00:00.00001Z","gui":false,"id":"0c511c65-5375-4dd3-8a76-df12ba772110","name":"my_1st_token","token":"ucat_[REDACTED]","type":"workspace"}'
headers:
Content-Length:
- "292"
- "291"
Content-Type:
- application/json
Date:
- Fri, 20 Dec 2024 12:26:36 GMT
- Thu, 06 Feb 2025 15:33:07 GMT
Strict-Transport-Security:
- max-age=63072000
status: 201 Created
code: 201
duration: ""
Expand All @@ -31,21 +35,25 @@ interactions:
headers:
Accept:
- application/json
Authorization:
- Basic [REDACTED]
Content-Type:
- application/json
User-Agent:
- upcloud-go-api/8.14.0
url: https://api.upcloud.com/1.3/account/tokens
method: POST
response:
body: '{"token":"ucat_01JFJ0HDR7EHQVZER2KZXBR5NC","id":"0c54f4bf-0b31-47da-b9f5-5cebeda621a4","name":"my_2nd_token","type":"workspace","created_at":"2024-12-20T12:26:36.679304Z","expires_at":"2025-12-01T00:00:00.000013Z","can_create_tokens":false,"allowed_ip_ranges":["0.0.0.0/1","::/0"],"gui":false}'
body: '{"allowed_ip_ranges":["0.0.0.0/1","::/0"],"can_create_tokens":false,"created_at":"2025-02-06T15:33:07.895253Z","expires_at":"2025-12-01T00:00:00.000012Z","gui":false,"id":"0c26b13f-8079-4415-889d-6e4e6fba6de1","name":"my_2nd_token","token":"ucat_[REDACTED]","type":"workspace"}'
headers:
Content-Length:
- "293"
Content-Type:
- application/json
Date:
- Fri, 20 Dec 2024 12:26:36 GMT
- Thu, 06 Feb 2025 15:33:07 GMT
Strict-Transport-Security:
- max-age=63072000
status: 201 Created
code: 201
duration: ""
Expand All @@ -55,21 +63,25 @@ interactions:
headers:
Accept:
- application/json
Authorization:
- Basic [REDACTED]
Content-Type:
- application/json
User-Agent:
- upcloud-go-api/8.14.0
url: https://api.upcloud.com/1.3/account/tokens/0c0cb933-d5d5-4027-a4f5-20019f30a913
url: https://api.upcloud.com/1.3/account/tokens/0c511c65-5375-4dd3-8a76-df12ba772110
method: GET
response:
body: '{"id":"0c0cb933-d5d5-4027-a4f5-20019f30a913","name":"my_1st_token","type":"workspace","created_at":"2024-12-20T12:26:36.619315Z","expires_at":"2025-12-01T00:00:00.000013Z","can_create_tokens":true,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false}'
body: '{"allowed_ip_ranges":["0.0.0.0/0","::/0"],"can_create_tokens":true,"created_at":"2025-02-06T15:33:07.534408Z","expires_at":"2025-12-01T00:00:00.00001Z","gui":false,"id":"0c511c65-5375-4dd3-8a76-df12ba772110","name":"my_1st_token","type":"workspace"}'
headers:
Content-Length:
- "250"
- "249"
Content-Type:
- application/json
Date:
- Fri, 20 Dec 2024 12:26:36 GMT
- Thu, 06 Feb 2025 15:33:08 GMT
Strict-Transport-Security:
- max-age=63072000
status: 200 OK
code: 200
duration: ""
Expand All @@ -79,21 +91,25 @@ interactions:
headers:
Accept:
- application/json
Authorization:
- Basic [REDACTED]
Content-Type:
- application/json
User-Agent:
- upcloud-go-api/8.14.0
url: https://api.upcloud.com/1.3/account/tokens
method: GET
response:
body: '[{"id":"0c2adaf6-0805-4f18-bb45-03fce1bc1c2d","name":"token","type":"workspace","created_at":"2024-12-19T11:46:09.888763Z","expires_at":"2024-12-19T12:16:09.888531Z","can_create_tokens":true,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false},{"id":"0c1a21dc-11b0-47aa-9979-b45e03b38788","name":"token","type":"workspace","created_at":"2024-12-19T11:57:33.40507Z","expires_at":"2024-12-19T12:27:33.404897Z","last_used_at":"2024-12-19T12:01:13.538016Z","can_create_tokens":true,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false},{"id":"0c09ff1a-aec0-4a22-bbca-01970a710ecc","name":"token","type":"workspace","created_at":"2024-12-19T12:17:17.14145Z","expires_at":"2024-12-19T12:47:17.141249Z","can_create_tokens":true,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false},{"id":"0cc7fdd4-af6f-4da4-9a59-a7b923d27317","name":"my_1st_token","type":"workspace","created_at":"2024-12-19T13:14:40.617399Z","expires_at":"2024-12-19T14:14:40.532908Z","can_create_tokens":false,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false},{"id":"0c0d735c-4414-46f1-a1e8-64bb27667196","name":"my_1st_token","type":"workspace","created_at":"2024-12-19T13:17:35.961859Z","expires_at":"2024-12-19T14:17:35.845322Z","can_create_tokens":false,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false},{"id":"0cd3aa86-b1ed-416b-82a9-427778762d43","name":"my_1st_token","type":"workspace","created_at":"2024-12-20T07:44:49.162896Z","expires_at":"2024-12-20T08:44:49.066726Z","can_create_tokens":true,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false},{"id":"0c3869ea-bb43-4588-a16d-02d07a394d94","name":"my_1st_token","type":"workspace","created_at":"2024-12-20T07:49:59.192905Z","expires_at":"2025-01-01T00:00:00.000012Z","can_create_tokens":true,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false},{"id":"0c0cb933-d5d5-4027-a4f5-20019f30a913","name":"my_1st_token","type":"workspace","created_at":"2024-12-20T12:26:36.619315Z","expires_at":"2025-12-01T00:00:00.000013Z","can_create_tokens":true,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false},{"id":"0c54f4bf-0b31-47da-b9f5-5cebeda621a4","name":"my_2nd_token","type":"workspace","created_at":"2024-12-20T12:26:36.679304Z","expires_at":"2025-12-01T00:00:00.000013Z","can_create_tokens":false,"allowed_ip_ranges":["0.0.0.0/1","::/0"],"gui":false}]'
body: '[{"id":"0cd85ae7-0a16-423c-817b-85b8f4bcbb03","name":"my_1st_token","type":"workspace","created_at":"2025-01-30T17:40:59.267513Z","expires_at":"2025-12-01T00:00:00.000009Z","last_used_at":"2025-01-30T17:40:59.50002Z","can_create_tokens":true,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false},{"id":"0ca1d997-a25f-427a-af81-3911b0d92912","name":"my_1st_token","type":"workspace","created_at":"2025-01-31T08:59:43.218102Z","expires_at":"2025-12-01T00:00:00.00001Z","last_used_at":"2025-01-31T08:59:43.499057Z","can_create_tokens":true,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false},{"id":"0c511c65-5375-4dd3-8a76-df12ba772110","name":"my_1st_token","type":"workspace","created_at":"2025-02-06T15:33:07.534408Z","expires_at":"2025-12-01T00:00:00.00001Z","can_create_tokens":true,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false},{"id":"0c26b13f-8079-4415-889d-6e4e6fba6de1","name":"my_2nd_token","type":"workspace","created_at":"2025-02-06T15:33:07.895253Z","expires_at":"2025-12-01T00:00:00.000012Z","can_create_tokens":false,"allowed_ip_ranges":["0.0.0.0/1","::/0"],"gui":false}]'
headers:
Content-Length:
- "2285"
- "1093"
Content-Type:
- application/json
Date:
- Fri, 20 Dec 2024 12:26:36 GMT
- Thu, 06 Feb 2025 15:33:08 GMT
Strict-Transport-Security:
- max-age=63072000
status: 200 OK
code: 200
duration: ""
Expand All @@ -103,21 +119,25 @@ interactions:
headers:
Accept:
- application/json
Authorization:
- Basic [REDACTED]
Content-Type:
- application/json
User-Agent:
- upcloud-go-api/8.14.0
url: https://api.upcloud.com/1.3/account/tokens
method: POST
response:
body: '{"token":"ucat_01JFJ0HDVC5NQFFD0BYE5N0VBG","id":"0c62c862-2f4a-41d3-8f07-1cfbded87295","name":"my_1st_token","type":"workspace","created_at":"2024-12-20T12:26:36.78057Z","expires_at":"2025-12-01T00:00:00.000018Z","can_create_tokens":true,"allowed_ip_ranges":["0.0.0.0/0","::/0"],"gui":false}'
body: '{"allowed_ip_ranges":["0.0.0.0/0","::/0"],"can_create_tokens":true,"created_at":"2025-02-06T15:33:09.092616Z","expires_at":"2025-12-01T00:00:00.000008Z","gui":false,"id":"0ce0d95e-a0ac-4b99-af2c-392da20ef103","name":"my_1st_token","token":"ucat_[REDACTED]","type":"workspace"}'
headers:
Content-Length:
- "291"
- "292"
Content-Type:
- application/json
Date:
- Fri, 20 Dec 2024 12:26:36 GMT
- Thu, 06 Feb 2025 15:33:09 GMT
Strict-Transport-Security:
- max-age=63072000
status: 201 Created
code: 201
duration: ""
Expand All @@ -127,17 +147,21 @@ interactions:
headers:
Accept:
- application/json
Authorization:
- Basic [REDACTED]
Content-Type:
- application/json
User-Agent:
- upcloud-go-api/8.14.0
url: https://api.upcloud.com/1.3/account/tokens/0c62c862-2f4a-41d3-8f07-1cfbded87295
url: https://api.upcloud.com/1.3/account/tokens/0ce0d95e-a0ac-4b99-af2c-392da20ef103
method: DELETE
response:
body: ""
headers:
Date:
- Fri, 20 Dec 2024 12:26:36 GMT
- Thu, 06 Feb 2025 15:33:09 GMT
Strict-Transport-Security:
- max-age=63072000
status: 204 No Content
code: 204
duration: ""
67 changes: 33 additions & 34 deletions upcloud/service/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package service

import (
"context"
"os"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -32,8 +33,6 @@ func TestToken(t *testing.T) {
}

record(t, "token", func(ctx context.Context, t *testing.T, rec *recorder.Recorder, svc *Service) {
// TODO: obfuscate real tokes from fixtures. Currently committed tokens in token.yaml are from local env
// with the url changed to prod host. rec.AddFilter() for the win.
// Create some tokens
ids := make([]string, len(tokenRequests))

Expand Down Expand Up @@ -71,53 +70,53 @@ func TestToken(t *testing.T) {
})
}

// TestClientWithToken tests that a client can be created with a token and used to make authenticated API requests
func TestClientWithToken(t *testing.T) {
if os.Getenv("UPCLOUD_GO_SDK_TEST_NO_CREDENTIALS") == "yes" || testing.Short() {
t.Skip("Skipping TestGetAccount...")
}
expires := time.Date(2025, 12, 1, 0, 0, 0, 0, time.UTC)
tokenRequest := request.CreateTokenRequest{
Name: "my_1st_token",
ExpiresAt: expires,
AllowedIPRanges: []string{"0.0.0.0/0", "::/0"},
CanCreateSubTokens: true,
}

// Create client that retries the initial token
user, password := getCredentials()
clt := client.New(user, password)
svc := New(clt)
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
defer cancel()

// Get the initial token
token, err := svc.CreateToken(context.Background(), &tokenRequest)
require.NoError(t, err)

// Create a new client with the initial token
authCfg := client.WithBearerAuth(token.APIToken)
cltWithToken := client.New("", "", authCfg)

// Create a new service with the client with the initial token
svcWithToken := New(cltWithToken)

account, err := svcWithToken.GetAccount(context.Background())
require.NoError(t, err)

// Print account details
t.Logf("Account: %+v", account)

if account.UserName != user {
t.Errorf("TestGetAccount expected %s, got %s", user, account.UserName)
}

assert.NotZero(t, account.ResourceLimits.Cores)
assert.NotZero(t, account.ResourceLimits.Memory)
assert.NotZero(t, account.ResourceLimits.Networks)
assert.NotZero(t, account.ResourceLimits.PublicIPv6)
assert.NotZero(t, account.ResourceLimits.StorageHDD)
assert.NotZero(t, account.ResourceLimits.StorageSSD)
user, password := getCredentials()
svc := New(client.New(user, password))
token, err := svc.CreateToken(ctx, &tokenRequest)
require.NoError(t, err, "Failed to create token")
require.NotNil(t, token, "Token must not be nil")

// Make sure that we cleanup the token
t.Cleanup(cleanupTokenFunc(t, svc, token.ID))

// Create a new client with the token
svcWithToken := New(client.New("", "", client.WithBearerAuth(token.APIToken)))

// Make an authenticated API request
server, err := svcWithToken.GetServers(ctx)
require.NotEmpty(t, server, "Failed to get servers. This points to a problem with token auth")
require.NoError(t, err, "Error getting the servers. This points to a problem with token auth")

//Delete the token

Check failure on line 106 in upcloud/service/token_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

File is not properly formatted (gofumpt)
err = svcWithToken.DeleteToken(ctx, &request.DeleteTokenRequest{ID: token.ID})
require.NoError(t, err, "Token deletion should not fail")

// Make sure the token is deleted
server, err = svcWithToken.GetServers(ctx)
require.Error(t, err, "Getting servers with deleted token should fail")
require.Empty(t, server, "Getting servers with deleted token should return empty list")
}

func cleanupTokenFunc(t *testing.T, svc *Service, id string) func() {
return func() {
if err := svc.DeleteToken(context.Background(), &request.DeleteTokenRequest{ID: id}); err != nil {
t.Log(err)
t.Log(err, "This might not be a problem if the test deleted the token already")
}
}
}
60 changes: 57 additions & 3 deletions upcloud/service/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package service

import (
"context"
"encoding/json"
"log"
"net/http"
"net/http/httptest"
Expand All @@ -28,8 +29,18 @@ func (c *customRoundTripper) RoundTrip(r *http.Request) (*http.Response, error)
return c.fn(r)
}

// getTokenCredentials reads the token credential from the environment
func getTokenCredentials() string {
return os.Getenv("UPCLOUD_GO_SDK_TOKEN")
}

// Reads the API username and password from the environment, panics if they are not available.
func getCredentials() (string, string) {
// Read UPCLOUD_GO_SDK_TOKEN environment variable
if token := os.Getenv("UPCLOUD_GO_SDK_TOKEN"); token != "" {
return token, ""
}

if os.Getenv("UPCLOUD_GO_SDK_TEST_NO_CREDENTIALS") == "yes" {
return "username", "password"
}
Expand Down Expand Up @@ -60,8 +71,42 @@ func record(t *testing.T, fixture string, f func(context.Context, *testing.T, *r
r, err := recorder.New("fixtures/" + fixture)
require.NoError(t, err)

// Redact sensitive information from Authorization field
r.AddFilter(func(i *cassette.Interaction) error {
delete(i.Request.Headers, "Authorization")
if authHeader, ok := i.Request.Headers["Authorization"]; ok {
var redactedAuthHeader []string
for _, value := range authHeader {
if strings.HasPrefix(value, "Bearer ") {
redactedAuthHeader = append(redactedAuthHeader, "Bearer [REDACTED]")
} else if strings.HasPrefix(value, "Basic ") {
redactedAuthHeader = append(redactedAuthHeader, "Basic [REDACTED]")
}
}

if len(redactedAuthHeader) > 0 {
i.Request.Headers["Authorization"] = redactedAuthHeader
} else {
delete(i.Request.Headers, "Authorization")
}
}

// Redact sensitive information from response body
if i.Response.Body != "" {
var responseData map[string]interface{}

err := json.Unmarshal([]byte(i.Response.Body), &responseData)
if err == nil {
// Redact sensitive fields
if _, exists := responseData["token"]; exists {
responseData["token"] = "ucat_[REDACTED]"
}

// Convert back to string and update response body
updatedBody, _ := json.Marshal(responseData)
i.Response.Body = string(updatedBody)
}
}

if i.Request.Method == http.MethodPut && strings.Contains(i.Request.URL, "uploader") {
// We will remove the body from the upload to reduce fixture size
i.Request.Body = ""
Expand All @@ -74,7 +119,12 @@ func record(t *testing.T, fixture string, f func(context.Context, *testing.T, *r
require.NoError(t, err)
}()

user, password := getCredentials()
//Read token credentials from the environment, if it does not exists try to read user and password

Check failure on line 122 in upcloud/service/utils_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

File is not properly formatted (gofumpt)
var user, password string
token := getTokenCredentials()
if token == "" {
user, password = getCredentials()
}

httpClient := client.NewDefaultHTTPClient()
origTransport := httpClient.Transport
Expand All @@ -95,7 +145,11 @@ func record(t *testing.T, fixture string, f func(context.Context, *testing.T, *r
// just some random timeout value. High enough that it won't be reached during normal test.
ctx, cancel := context.WithTimeout(context.Background(), waitTimeout*4)
defer cancel()
f(ctx, t, r, New(client.New(user, password, client.WithHTTPClient(httpClient))))
if token == "" {
f(ctx, t, r, New(client.New(user, password, client.WithHTTPClient(httpClient))))
} else {
f(ctx, t, r, New(client.New("", "", client.WithBearerAuth(token), client.WithHTTPClient(httpClient))))
}
}

// Tears down the test environment by removing all resources.
Expand Down

0 comments on commit c7a5460

Please sign in to comment.