Skip to content

Commit

Permalink
Allow refreshing org and team tokens (#191)
Browse files Browse the repository at this point in the history
Fixes #159
  • Loading branch information
komalali committed Nov 20, 2023
1 parent cce168d commit 6d5109b
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 167 deletions.
34 changes: 26 additions & 8 deletions provider/pkg/internal/pulumiapi/orgtokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ import (
"path"
)

type OrgAccessToken struct {
ID string `json:"id"`
TokenValue string `json:"tokenValue"`
Description string `json:"description"`
}

type createOrgTokenResponse struct {
ID string `json:"id"`
TokenValue string `json:"tokenValue"`
Expand All @@ -38,7 +32,7 @@ type createOrgTokenRequest struct {
Admin bool `json:"admin"`
}

func (c *Client) CreateOrgAccessToken(ctx context.Context, name string, orgName string, description string, admin bool) (*AccessToken, error) {
func (c *Client) CreateOrgAccessToken(ctx context.Context, name, orgName, description string, admin bool) (*AccessToken, error) {

if len(orgName) == 0 {
return nil, errors.New("empty orgName")
Expand Down Expand Up @@ -72,7 +66,7 @@ func (c *Client) CreateOrgAccessToken(ctx context.Context, name string, orgName

}

func (c *Client) DeleteOrgAccessToken(ctx context.Context, tokenId string, orgName string) error {
func (c *Client) DeleteOrgAccessToken(ctx context.Context, tokenId, orgName string) error {
if len(tokenId) == 0 {
return errors.New("tokenid length must be greater than zero")
}
Expand All @@ -92,3 +86,27 @@ func (c *Client) DeleteOrgAccessToken(ctx context.Context, tokenId string, orgNa

return nil
}

func (c *Client) GetOrgAccessToken(ctx context.Context, tokenId, orgName string) (*AccessToken, error) {
apiPath := path.Join("orgs", orgName, "tokens")

var listRes listTokenResponse

_, err := c.do(ctx, http.MethodGet, apiPath, nil, &listRes)

if err != nil {
return nil, fmt.Errorf("failed to list org access tokens: %w", err)
}

for i := 0; i < len(listRes.Tokens); i++ {
token := listRes.Tokens[i]
if token.ID == tokenId {
return &AccessToken{
ID: token.ID,
Description: token.Description,
}, nil
}
}

return nil, nil
}
61 changes: 58 additions & 3 deletions provider/pkg/internal/pulumiapi/orgtokens_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package pulumiapi

import (
"context"
"fmt"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
)

var orgCtx = context.Background()

func TestDeleteOrgAccessToken(t *testing.T) {
orgName := "anOrg"
tokenId := "abcdegh"
Expand Down Expand Up @@ -121,3 +119,60 @@ func TestCreateOrgAccessToken(t *testing.T) {
)
})
}

func TestGetOrgAccessToken(t *testing.T) {
id := "uuid"
desc := "token description"
org := "anOrg"
lastUsed := 123
t.Run("Happy Path", func(t *testing.T) {
resp := listTokenResponse{
Tokens: []accessTokenResponse{
{
ID: id,
Description: desc,
LastUsed: lastUsed,
},
{
ID: "other",
Description: desc,
LastUsed: lastUsed,
},
},
}
c, cleanup := startTestServer(t, testServerConfig{
ExpectedReqMethod: http.MethodGet,
ExpectedReqBody: nil,
ExpectedReqPath: fmt.Sprintf("/api/orgs/%s/tokens", org),
ResponseCode: 200,
ResponseBody: resp,
})
defer cleanup()
token, err := c.GetOrgAccessToken(ctx, id, org)
assert.NoError(t, err)
assert.Equal(t, &AccessToken{
ID: id,
Description: desc,
}, token)
})

t.Run("Error", func(t *testing.T) {
c, cleanup := startTestServer(t, testServerConfig{
ExpectedReqMethod: http.MethodGet,
ExpectedReqPath: fmt.Sprintf("/api/orgs/%s/tokens", org),
ExpectedReqBody: nil,
ResponseCode: 401,
ResponseBody: errorResponse{
StatusCode: 401,
Message: "unauthorized",
},
})
defer cleanup()
token, err := c.GetOrgAccessToken(ctx, id, org)
assert.Nil(t, token, "token should be nil")
assert.EqualError(t,
err,
`failed to list org access tokens: 401 API error: unauthorized`,
)
})
}
34 changes: 26 additions & 8 deletions provider/pkg/internal/pulumiapi/teamtokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ import (
"path"
)

type TeamAccessToken struct {
ID string `json:"id"`
TokenValue string `json:"tokenValue"`
Description string `json:"description"`
}

type createTeamTokenResponse struct {
ID string `json:"id"`
TokenValue string `json:"tokenValue"`
Expand All @@ -37,7 +31,7 @@ type createTeamTokenRequest struct {
Description string `json:"description"`
}

func (c *Client) CreateTeamAccessToken(ctx context.Context, name string, orgName string, teamName string, description string) (*AccessToken, error) {
func (c *Client) CreateTeamAccessToken(ctx context.Context, name, orgName, teamName, description string) (*AccessToken, error) {

if len(orgName) == 0 {
return nil, errors.New("empty orgName")
Expand Down Expand Up @@ -70,7 +64,7 @@ func (c *Client) CreateTeamAccessToken(ctx context.Context, name string, orgName

}

func (c *Client) DeleteTeamAccessToken(ctx context.Context, tokenId string, orgName string, teamName string) error {
func (c *Client) DeleteTeamAccessToken(ctx context.Context, tokenId, orgName, teamName string) error {
if len(tokenId) == 0 {
return errors.New("tokenid length must be greater than zero")
}
Expand All @@ -92,3 +86,27 @@ func (c *Client) DeleteTeamAccessToken(ctx context.Context, tokenId string, orgN

return nil
}

func (c *Client) GetTeamAccessToken(ctx context.Context, tokenId, orgName, teamName string) (*AccessToken, error) {
apiPath := path.Join("orgs", orgName, "teams", teamName, "tokens")

var listRes listTokenResponse

_, err := c.do(ctx, http.MethodGet, apiPath, nil, &listRes)

if err != nil {
return nil, fmt.Errorf("failed to list team access tokens: %w", err)
}

for i := 0; i < len(listRes.Tokens); i++ {
token := listRes.Tokens[i]
if token.ID == tokenId {
return &AccessToken{
ID: token.ID,
Description: token.Description,
}, nil
}
}

return nil, nil
}
59 changes: 59 additions & 0 deletions provider/pkg/internal/pulumiapi/teamtokens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pulumiapi

import (
"context"
"fmt"
"net/http"
"testing"

Expand Down Expand Up @@ -96,3 +97,61 @@ func TestCreateTeamAccessToken(t *testing.T) {
)
})
}

func TestGetTeamAccessToken(t *testing.T) {
id := "uuid"
desc := "token description"
org := "anOrg"
team := "aTeam"
lastUsed := 123
t.Run("Happy Path", func(t *testing.T) {
resp := listTokenResponse{
Tokens: []accessTokenResponse{
{
ID: id,
Description: desc,
LastUsed: lastUsed,
},
{
ID: "other",
Description: desc,
LastUsed: lastUsed,
},
},
}
c, cleanup := startTestServer(t, testServerConfig{
ExpectedReqMethod: http.MethodGet,
ExpectedReqBody: nil,
ExpectedReqPath: fmt.Sprintf("/api/orgs/%s/teams/%s/tokens", org, team),
ResponseCode: 200,
ResponseBody: resp,
})
defer cleanup()
token, err := c.GetTeamAccessToken(ctx, id, org, team)
assert.NoError(t, err)
assert.Equal(t, &AccessToken{
ID: id,
Description: desc,
}, token)
})

t.Run("Error", func(t *testing.T) {
c, cleanup := startTestServer(t, testServerConfig{
ExpectedReqMethod: http.MethodGet,
ExpectedReqPath: fmt.Sprintf("/api/orgs/%s/teams/%s/tokens", org, team),
ExpectedReqBody: nil,
ResponseCode: 401,
ResponseBody: errorResponse{
StatusCode: 401,
Message: "unauthorized",
},
})
defer cleanup()
token, err := c.GetTeamAccessToken(ctx, id, org, team)
assert.Nil(t, token, "token should be nil")
assert.EqualError(t,
err,
`failed to list team access tokens: 401 API error: unauthorized`,
)
})
}
53 changes: 10 additions & 43 deletions provider/pkg/provider/access_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,32 +41,7 @@ func (at *PulumiServiceAccessTokenResource) Name() string {
}

func (at *PulumiServiceAccessTokenResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) {
olds, err := plugin.UnmarshalProperties(req.GetOlds(), plugin.MarshalOptions{KeepUnknowns: false, SkipNulls: true})
if err != nil {
return nil, err
}

news, err := plugin.UnmarshalProperties(req.GetNews(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: false})
if err != nil {
return nil, err
}

diffs := olds["__inputs"].ObjectValue().Diff(news)
if diffs == nil {
return &pulumirpc.DiffResponse{
Changes: pulumirpc.DiffResponse_DIFF_NONE,
}, nil
}

changes := pulumirpc.DiffResponse_DIFF_NONE
if diffs.Changed("description") {
changes = pulumirpc.DiffResponse_DIFF_SOME
}

return &pulumirpc.DiffResponse{
Changes: changes,
Replaces: []string{"description"},
}, nil
return considerAllChangesReplaces(req)
}

func (at *PulumiServiceAccessTokenResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) {
Expand Down Expand Up @@ -115,19 +90,20 @@ func (at *PulumiServiceAccessTokenResource) Check(req *pulumirpc.CheckRequest) (
return &pulumirpc.CheckResponse{Inputs: req.News, Failures: nil}, nil
}

func (at *PulumiServiceAccessTokenResource) Update(req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) {
return &pulumirpc.UpdateResponse{}, nil
func (at *PulumiServiceAccessTokenResource) Update(_ *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) {
// all updates are destructive, so we just call Create.
return nil, fmt.Errorf("unexpected call to update, expected create to be called instead")
}

func (at *PulumiServiceAccessTokenResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) {
ctx := context.Background()

// the access token is immutable; if we get nil it got deleted, otherwise all data is the same
accesstoken, err := at.getAccessToken(ctx, req.GetId())
accessToken, err := at.client.GetAccessToken(ctx, req.GetId())
if err != nil {
return nil, err
}
if accesstoken == nil {
if accessToken == nil {
return &pulumirpc.ReadResponse{}, nil
}

Expand All @@ -137,32 +113,23 @@ func (at *PulumiServiceAccessTokenResource) Read(req *pulumirpc.ReadRequest) (*p
}, nil
}

func (at *PulumiServiceAccessTokenResource) Invoke(s *pulumiserviceProvider, req *pulumirpc.InvokeRequest) (*pulumirpc.InvokeResponse, error) {
func (at *PulumiServiceAccessTokenResource) Invoke(_ *pulumiserviceProvider, req *pulumirpc.InvokeRequest) (*pulumirpc.InvokeResponse, error) {
return &pulumirpc.InvokeResponse{Return: nil}, fmt.Errorf("unknown function '%s'", req.Tok)
}

func (at *PulumiServiceAccessTokenResource) Configure(config PulumiServiceConfig) {
func (at *PulumiServiceAccessTokenResource) Configure(_ PulumiServiceConfig) {
}

func (at *PulumiServiceAccessTokenResource) createAccessToken(ctx context.Context, input PulumiServiceAccessTokenInput) (*pulumiapi.AccessToken, error) {

accesstoken, err := at.client.CreateAccessToken(ctx, input.Description)
accessToken, err := at.client.CreateAccessToken(ctx, input.Description)
if err != nil {
return nil, err
}

return accesstoken, nil
return accessToken, nil
}

func (at *PulumiServiceAccessTokenResource) deleteAccessToken(ctx context.Context, tokenId string) error {
return at.client.DeleteAccessToken(ctx, tokenId)
}

func (at *PulumiServiceAccessTokenResource) getAccessToken(ctx context.Context, id string) (*pulumiapi.AccessToken, error) {
accesstoken, err := at.client.GetAccessToken(ctx, id)
if err != nil {
return nil, err
}

return accesstoken, nil
}
Loading

0 comments on commit 6d5109b

Please sign in to comment.