Skip to content

Commit

Permalink
feat: add default team id (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
thevilledev committed Jul 11, 2023
1 parent c676aaa commit 73c961d
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 15 deletions.
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ currently being supported with security updates.

| Version | Supported |
| ------- | ------------------ |
| 0.2.x | :white_check_mark: |
| < 0.2 | :x: |
| 0.3.x | :white_check_mark: |
| < 0.3 | :x: |

## Reporting a Vulnerability

Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ token_id bababababa
Optional parameters are:

- `ttl=<seconds>`: Custom lease duration. Has to be lower than or equal to `max_ttl` configured to the plugin backend.
- `team_id=<vercel-team-id>`: Set token scope for a specific Vercel team. Requires a Vercel Pro plan.
- `team_id=<vercel-team-id>`: Set token scope for a specific Vercel team. If backend configuration has a default team ID set, this value has to be equal to that. Requires a Vercel Pro plan.

## Revoke tokens

Expand Down
34 changes: 26 additions & 8 deletions internal/plugin/path_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import (
)

const (
pathPatternConfig = "config"
pathConfigAPIKey = "api_key"
pathConfigBaseURL = "base_url"
pathConfigMaxTTL = "max_ttl"
defaultMaxTTL = 600
pathPatternConfig = "config"
pathConfigAPIKey = "api_key"
pathConfigBaseURL = "base_url"
pathConfigMaxTTL = "max_ttl"
pathConfigDefaultTeamID = "default_team_id"
defaultMaxTTL = 600

pathConfigHelpDescription = `
Configuration path used to set the API key that the plugin uses to communicate with the Vercel API.
Expand All @@ -30,6 +31,9 @@ Configure the Vercel plugin backend.`
(Optional) Base URL for the Vercel API. Used by mock tests mostly.`
pathConfigMaxTTLDescription = `
(Optional) Maximum TTL for all the API tokens generated by this plugin. Defaults to 600 seconds.`
pathConfigDefaultTeamIDDescription = `
(Optional) Default Team ID used for all token creation actions.
If set, individual tokens cannot override this value per token.`
)

var (
Expand All @@ -39,9 +43,10 @@ var (
)

type backendConfig struct {
APIKey string `json:"api_key"`
BaseURL string `json:"base_url"`
MaxTTL time.Duration `json:"max_ttl"`
APIKey string `json:"api_key"`
BaseURL string `json:"base_url"`
MaxTTL time.Duration `json:"max_ttl"`
DefaultTeamID string `json:"default_team_id"`
}

func (b *backend) pathConfig() []*framework.Path {
Expand All @@ -67,6 +72,10 @@ func (b *backend) pathConfig() []*framework.Path {
Description: pathConfigMaxTTLDescription,
Default: defaultMaxTTL,
},
pathConfigDefaultTeamID: {
Type: framework.TypeString,
Description: pathConfigDefaultTeamIDDescription,
},
},

Operations: map[logical.Operation]framework.OperationHandler{
Expand Down Expand Up @@ -116,6 +125,15 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request,
}
}

if v, ok := data.GetOk(pathConfigDefaultTeamID); ok {
config.DefaultTeamID, ok = v.(string)
if !ok {
b.Logger().Trace("type assertion failed: %+v", v)

return nil, errTypeAssertionFailed
}
}

if v, ok := data.GetOk(pathConfigBaseURL); ok {
config.BaseURL, ok = v.(string)
if !ok {
Expand Down
24 changes: 24 additions & 0 deletions internal/plugin/path_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,30 @@ func TestBackend_PathConfigWrite(t *testing.T) {
require.NotNil(t, cfg)
require.Equal(t, cfg.APIKey, "foo")
})

t.Run("WriteConfigurationWithValidTeamData", func(t *testing.T) {
t.Parallel()

ctx := context.Background()
b, storage := newTestBackend(t)

_, err := b.HandleRequest(ctx, &logical.Request{
Storage: storage,
Operation: logical.CreateOperation,
Path: pathPatternConfig,
Data: map[string]any{
"api_key": "foo",
"default_team_id": "bar",
},
})
require.NoError(t, err)

cfg, err := b.getConfig(ctx, storage)
require.NoError(t, err)
require.NotNil(t, cfg)
require.Equal(t, cfg.APIKey, "foo")
require.Equal(t, cfg.DefaultTeamID, "bar")
})
}

func TestBackend_PathConfigDelete(t *testing.T) {
Expand Down
10 changes: 8 additions & 2 deletions internal/plugin/path_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ Generate a Vercel API token with the given TTL.`
)

var (
errTokenMaxTTLExceeded = errors.New("given TTL exceeds the maximum allowed value")
errTokenMaxTTLExceeded = errors.New("given TTL exceeds the maximum allowed value")
errCannotOverrideDefaultTeamID = errors.New("cannot override team_id different than set on backend config")
)

func (b *backend) pathToken() []*framework.Path {
Expand Down Expand Up @@ -103,7 +104,7 @@ func (b *backend) pathTokenWrite(ctx context.Context, req *logical.Request,
return nil, errTokenMaxTTLExceeded
}

teamID := ""
teamID := cfg.DefaultTeamID

if vr, ok := data.GetOk(pathTokenTeamID); ok {
v, ta := vr.(string)
Expand All @@ -113,6 +114,10 @@ func (b *backend) pathTokenWrite(ctx context.Context, req *logical.Request,
return nil, errTypeAssertionFailed
}

if teamID != "" && v != "" && teamID != v {
return nil, errCannotOverrideDefaultTeamID
}

teamID = v
}

Expand All @@ -131,6 +136,7 @@ func (b *backend) pathTokenWrite(ctx context.Context, req *logical.Request,
Data: map[string]any{
pathTokenID: tokenID,
pathTokenBearerToken: bearerToken,
pathTokenTeamID: teamID,
},
Secret: &logical.Secret{
InternalData: map[string]any{
Expand Down
93 changes: 91 additions & 2 deletions internal/plugin/path_token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func TestToken_Create(t *testing.T) {
Storage: storage,
Operation: logical.CreateOperation,
Path: pathPatternConfig,
Data: map[string]interface{}{
Data: map[string]any{
"api_key": "foo",
"base_url": ts.URL,
"max_ttl": 10,
Expand All @@ -157,6 +157,95 @@ func TestToken_Create(t *testing.T) {
"ttl": 11,
},
})
require.Error(t, err)
require.ErrorIs(t, err, errTokenMaxTTLExceeded)
})

t.Run("CreateTokenWithDefaultTeamID", func(t *testing.T) {
t.Parallel()

b, storage := newTestBackend(t)

ts := httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, _ *http.Request) {
t.Helper()

body, _ := json.Marshal(&client.CreateAuthTokenResponse{
Token: client.Token{
ID: "foo",
Name: "bar",
},
BearerToken: "zyzz",
})
w.WriteHeader(http.StatusOK)
_, _ = w.Write(body)
}),
)
defer ts.Close()

_, err := b.HandleRequest(context.Background(), &logical.Request{
Storage: storage,
Operation: logical.CreateOperation,
Path: pathPatternConfig,
Data: map[string]any{
"api_key": "foo",
"base_url": ts.URL,
"default_team_id": "foo",
},
})
require.NoError(t, err)

r, err := b.HandleRequest(context.Background(), &logical.Request{
Storage: storage,
Operation: logical.CreateOperation,
Path: pathPatternToken,
Data: map[string]any{},
})
require.NoError(t, err)
require.Equal(t, r.Data["team_id"], "foo")
})

t.Run("CreateTokenWithConflictingTeamIDs", func(t *testing.T) {
t.Parallel()

b, storage := newTestBackend(t)

ts := httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, _ *http.Request) {
t.Helper()

body, _ := json.Marshal(&client.CreateAuthTokenResponse{
Token: client.Token{
ID: "foo",
Name: "bar",
},
BearerToken: "zyzz",
})
w.WriteHeader(http.StatusOK)
_, _ = w.Write(body)
}),
)
defer ts.Close()

_, err := b.HandleRequest(context.Background(), &logical.Request{
Storage: storage,
Operation: logical.CreateOperation,
Path: pathPatternConfig,
Data: map[string]any{
"api_key": "foo",
"base_url": ts.URL,
"default_team_id": "foo",
},
})
require.NoError(t, err)

_, err = b.HandleRequest(context.Background(), &logical.Request{
Storage: storage,
Operation: logical.CreateOperation,
Path: pathPatternToken,
Data: map[string]any{
"team_id": "bar",
},
})
require.ErrorIs(t, err, errCannotOverrideDefaultTeamID)
})
}

0 comments on commit 73c961d

Please sign in to comment.