Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7d68b72

Browse files
committed
feat: add multiple API key scopes support with granular permissions
Change-Id: I5857fd833f8114d53f575b9fa48a8e5e7dbfdb2cSigned-off-by: Thomas Kosiewski <tk@coder.com>
1 parent99e7a7b commit7d68b72

File tree

65 files changed

+1612
-459
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+1612
-459
lines changed

‎.claude/notes/token-scopes.md‎

Lines changed: 541 additions & 0 deletions
Large diffs are not rendered by default.

‎cli/exp_scaletest.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1232,7 +1232,7 @@ func (r *RootCmd) scaletestDashboard() *serpent.Command {
12321232
name:=fmt.Sprintf("dashboard-%s",usr.Username)
12331233
userTokResp,err:=client.CreateToken(ctx,usr.ID.String(), codersdk.CreateTokenRequest{
12341234
Lifetime:30*24*time.Hour,
1235-
Scope:"",
1235+
Scopes:[]codersdk.APIKeyScope{codersdk.APIKeyScopeAll},
12361236
TokenName:fmt.Sprintf("scaletest-%d",time.Now().Unix()),
12371237
})
12381238
iferr!=nil {

‎coderd/apidoc/docs.go‎

Lines changed: 41 additions & 23 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json‎

Lines changed: 47 additions & 17 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apikey.go‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ import (
2424
"github.com/coder/coder/v2/codersdk"
2525
)
2626

27+
// convertAPIKeyScopesToDatabase converts SDK API key scopes to database API key scopes
28+
funcconvertAPIKeyScopesToDatabase(scopes []codersdk.APIKeyScope) []database.APIKeyScope {
29+
dbScopes:=make([]database.APIKeyScope,0,len(scopes))
30+
for_,scope:=rangescopes {
31+
dbScopes=append(dbScopes,database.APIKeyScope(scope))
32+
}
33+
returndbScopes
34+
}
35+
2736
// Creates a new token API key with the given scope and lifetime.
2837
//
2938
// @Summary Create token API key
@@ -56,9 +65,10 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) {
5665
return
5766
}
5867

59-
scope:=database.APIKeyScopeAll
60-
ifscope!="" {
61-
scope=database.APIKeyScope(createToken.Scope)
68+
// Use the scopes from the request, or default to 'all' if empty
69+
scopes:=createToken.Scopes
70+
iflen(scopes)==0 {
71+
scopes= []codersdk.APIKeyScope{codersdk.APIKeyScopeAll}
6272
}
6373

6474
tokenName:=namesgenerator.GetRandomName(1)
@@ -71,7 +81,7 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) {
7181
UserID:user.ID,
7282
LoginType:database.LoginTypeToken,
7383
DefaultLifetime:api.DeploymentValues.Sessions.DefaultTokenDuration.Value(),
74-
Scope:scope,
84+
Scopes:convertAPIKeyScopesToDatabase(scopes),// New scopes array
7585
TokenName:tokenName,
7686
}
7787

@@ -380,7 +390,7 @@ func (api *API) validateAPIKeyLifetime(ctx context.Context, userID uuid.UUID, li
380390
// getMaxTokenLifetime returns the maximum allowed token lifetime for a user.
381391
// It distinguishes between regular users and owners.
382392
func (api*API)getMaxTokenLifetime(ctx context.Context,userID uuid.UUID) (time.Duration,error) {
383-
subject,_,err:=httpmw.UserRBACSubject(ctx,api.Database,userID,rbac.ScopeAll)
393+
subject,_,err:=httpmw.UserRBACSubject(ctx,api.Database,userID,[]rbac.ExpandableScope{rbac.ScopeAll})
384394
iferr!=nil {
385395
return0,xerrors.Errorf("failed to get user rbac subject: %w",err)
386396
}

‎coderd/apikey/apikey.go‎

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ type CreateParams struct {
2525
// Optional.
2626
ExpiresAt time.Time
2727
LifetimeSecondsint64
28-
Scope database.APIKeyScope
28+
Scope database.APIKeyScope// Legacy single scope (for backward compatibility)
29+
Scopes []database.APIKeyScope// New scopes array
2930
TokenNamestring
3031
RemoteAddrstring
3132
}
@@ -62,14 +63,24 @@ func Generate(params CreateParams) (database.InsertAPIKeyParams, string, error)
6263

6364
bitlen:=len(ip)*8
6465

65-
scope:=database.APIKeyScopeAll
66-
ifparams.Scope!="" {
67-
scope=params.Scope
66+
// Determine scopes - prioritize Scopes array, fallback to legacy Scope
67+
varscopes []database.APIKeyScope
68+
iflen(params.Scopes)>0 {
69+
scopes=params.Scopes
70+
}else {
71+
// Fallback to legacy single scope
72+
scope:=database.APIKeyScopeAll
73+
ifparams.Scope!="" {
74+
scope=params.Scope
75+
}
76+
scopes= []database.APIKeyScope{scope}
6877
}
69-
switchscope {
70-
casedatabase.APIKeyScopeAll,database.APIKeyScopeApplicationConnect:
71-
default:
72-
return database.InsertAPIKeyParams{},"",xerrors.Errorf("invalid API key scope: %q",scope)
78+
79+
// Validate all scopes
80+
for_,scope:=rangescopes {
81+
if!scope.Valid() {
82+
return database.InsertAPIKeyParams{},"",xerrors.Errorf("invalid API key scope: %q",scope)
83+
}
7384
}
7485

7586
token:=fmt.Sprintf("%s-%s",keyID,keySecret)
@@ -92,7 +103,7 @@ func Generate(params CreateParams) (database.InsertAPIKeyParams, string, error)
92103
UpdatedAt:dbtime.Now(),
93104
HashedSecret:hashed[:],
94105
LoginType:params.LoginType,
95-
Scope:scope,
106+
Scopes:scopes,// New scopes array
96107
TokenName:params.TokenName,
97108
},token,nil
98109
}

‎coderd/apikey/apikey_test.go‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func TestGenerate(t *testing.T) {
3535
LifetimeSeconds:int64(time.Hour.Seconds()),
3636
TokenName:"hello",
3737
RemoteAddr:"1.2.3.4",
38-
Scope:database.APIKeyScopeApplicationConnect,
38+
Scopes:[]database.APIKeyScope{database.APIKeyScopeApplicationConnect},
3939
},
4040
},
4141
{
@@ -48,7 +48,7 @@ func TestGenerate(t *testing.T) {
4848
LifetimeSeconds:int64(time.Hour.Seconds()),
4949
TokenName:"hello",
5050
RemoteAddr:"1.2.3.4",
51-
Scope:database.APIKeyScope("test"),
51+
Scopes:[]database.APIKeyScope{database.APIKeyScope("test")},
5252
},
5353
fail:true,
5454
},
@@ -62,7 +62,7 @@ func TestGenerate(t *testing.T) {
6262
ExpiresAt: time.Time{},
6363
TokenName:"hello",
6464
RemoteAddr:"1.2.3.4",
65-
Scope:database.APIKeyScopeApplicationConnect,
65+
Scopes:[]database.APIKeyScope{database.APIKeyScopeApplicationConnect},
6666
},
6767
},
6868
{
@@ -75,7 +75,7 @@ func TestGenerate(t *testing.T) {
7575
ExpiresAt: time.Time{},
7676
TokenName:"hello",
7777
RemoteAddr:"1.2.3.4",
78-
Scope:database.APIKeyScopeApplicationConnect,
78+
Scopes:[]database.APIKeyScope{database.APIKeyScopeApplicationConnect},
7979
},
8080
},
8181
{
@@ -88,7 +88,7 @@ func TestGenerate(t *testing.T) {
8888
LifetimeSeconds:int64(time.Hour.Seconds()),
8989
TokenName:"hello",
9090
RemoteAddr:"",
91-
Scope:database.APIKeyScopeApplicationConnect,
91+
Scopes:[]database.APIKeyScope{database.APIKeyScopeApplicationConnect},
9292
},
9393
},
9494
{
@@ -101,7 +101,7 @@ func TestGenerate(t *testing.T) {
101101
LifetimeSeconds:int64(time.Hour.Seconds()),
102102
TokenName:"hello",
103103
RemoteAddr:"1.2.3.4",
104-
Scope:"",
104+
Scopes:[]database.APIKeyScope{},
105105
},
106106
},
107107
}
@@ -158,10 +158,10 @@ func TestGenerate(t *testing.T) {
158158
assert.Equal(t,"0.0.0.0",key.IPAddress.IPNet.IP.String())
159159
}
160160

161-
iftc.params.Scope!="" {
162-
assert.Equal(t,tc.params.Scope,key.Scope)
161+
iflen(tc.params.Scopes)>0 {
162+
assert.Equal(t,tc.params.Scopes,key.Scopes)
163163
}else {
164-
assert.Equal(t,database.APIKeyScopeAll,key.Scope)
164+
assert.Equal(t,[]database.APIKeyScope{database.APIKeyScopeAll},key.Scopes)
165165
}
166166

167167
iftc.params.TokenName!="" {

‎coderd/apikey_test.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func TestTokenCRUD(t *testing.T) {
4747
// expires_at should default to 30 days
4848
require.Greater(t,keys[0].ExpiresAt,time.Now().Add(time.Hour*24*6))
4949
require.Less(t,keys[0].ExpiresAt,time.Now().Add(time.Hour*24*8))
50-
require.Equal(t,codersdk.APIKeyScopeAll,keys[0].Scope)
50+
require.Equal(t,[]codersdk.APIKeyScope{codersdk.APIKeyScopeAll},keys[0].Scopes)
5151

5252
// no update
5353

@@ -73,7 +73,7 @@ func TestTokenScoped(t *testing.T) {
7373
_=coderdtest.CreateFirstUser(t,client)
7474

7575
res,err:=client.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
76-
Scope:codersdk.APIKeyScopeApplicationConnect,
76+
Scopes: []codersdk.APIKeyScope{codersdk.APIKeyScopeApplicationConnect},
7777
})
7878
require.NoError(t,err)
7979
require.Greater(t,len(res.Key),2)
@@ -82,7 +82,7 @@ func TestTokenScoped(t *testing.T) {
8282
require.NoError(t,err)
8383
require.EqualValues(t,len(keys),1)
8484
require.Contains(t,res.Key,keys[0].ID)
85-
require.Equal(t,keys[0].Scope,codersdk.APIKeyScopeApplicationConnect)
85+
require.Equal(t,[]codersdk.APIKeyScope{codersdk.APIKeyScopeApplicationConnect},keys[0].Scopes)
8686
}
8787

8888
funcTestUserSetTokenDuration(t*testing.T) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp