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

Commitacadd79

Browse files
committed
feat: rename special API key scopes to coder:* namespace
This change unifies scope handling by migrating special scopes to thecoder:* namespace while maintaining backward compatibility:- Database: 'all' -> 'coder:all', 'application_connect' -> 'coder:application_connect'- API accepts both legacy and canonical forms in requests- Responses maintain legacy format for existing client compatibility- Scope catalog returns all public scopes including canonical specials- Validation enforces public scope requirements using unified logicThe migration preserves existing API key functionality while establishingconsistent scope naming conventions for future extensibility.
1 parent6b9783a commitacadd79

25 files changed

+285
-85
lines changed

‎Makefile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,7 @@ codersdk/rbacresources_gen.go: scripts/typegen/codersdk.gotmpl scripts/typegen/m
872872
touch "$@"
873873

874874
codersdk/apikey_scopes_gen.go: scripts/apikeyscopesgen/main.go coderd/rbac/scopes_catalog.go coderd/rbac/scopes.go
875-
# Generate SDK constants forpublic low-level API key scopes.
875+
# Generate SDK constants forexternal API key scopes.
876876
go run ./scripts/apikeyscopesgen> /tmp/apikey_scopes_gen.go
877877
mv /tmp/apikey_scopes_gen.go codersdk/apikey_scopes_gen.go
878878
touch"$@"

‎coderd/apidoc/swagger.json‎

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

‎coderd/apikey/apikey_test.go‎

Lines changed: 5 additions & 5 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+
Scope:database.ApiKeyScopeCoderApplicationConnect,
3939
},
4040
},
4141
{
@@ -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+
Scope:database.ApiKeyScopeCoderApplicationConnect,
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+
Scope:database.ApiKeyScopeCoderApplicationConnect,
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+
Scope:database.ApiKeyScopeCoderApplicationConnect,
9292
},
9393
},
9494
{
@@ -161,7 +161,7 @@ func TestGenerate(t *testing.T) {
161161
iftc.params.Scope!="" {
162162
assert.True(t,key.Scopes.Has(tc.params.Scope))
163163
}else {
164-
assert.True(t,key.Scopes.Has(database.APIKeyScopeAll))
164+
assert.True(t,key.Scopes.Has(database.ApiKeyScopeCoderAll))
165165
}
166166

167167
iftc.params.TokenName!="" {

‎coderd/apikey_test.go‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,54 @@ func TestTokenScoped(t *testing.T) {
8888
require.Equal(t,keys[0].Scope,codersdk.APIKeyScopeApplicationConnect)
8989
}
9090

91+
// Ensure backward-compat: when a token is created using the legacy singular
92+
// scope names ("all" or "application_connect"), the API returns the same
93+
// legacy value in the deprecated singular Scope field while also supporting
94+
// the new multi-scope field.
95+
funcTestTokenLegacySingularScopeCompat(t*testing.T) {
96+
t.Parallel()
97+
98+
cases:= []struct {
99+
namestring
100+
scope codersdk.APIKeyScope
101+
scopes []codersdk.APIKeyScope
102+
}{
103+
{
104+
name:"all",
105+
scope:codersdk.APIKeyScopeAll,
106+
scopes: []codersdk.APIKeyScope{codersdk.APIKeyScopeCoderAll},
107+
},
108+
{
109+
name:"application_connect",
110+
scope:codersdk.APIKeyScopeApplicationConnect,
111+
scopes: []codersdk.APIKeyScope{codersdk.APIKeyScopeCoderApplicationConnect},
112+
},
113+
}
114+
115+
for_,tc:=rangecases {
116+
t.Run(tc.name,func(t*testing.T) {
117+
t.Parallel()
118+
ctx,cancel:=context.WithTimeout(t.Context(),testutil.WaitLong)
119+
defercancel()
120+
client:=coderdtest.New(t,nil)
121+
_=coderdtest.CreateFirstUser(t,client)
122+
123+
// Create with legacy singular scope.
124+
_,err:=client.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
125+
Scope:tc.scope,
126+
})
127+
require.NoError(t,err)
128+
129+
// Read back and ensure the deprecated singular field matches exactly.
130+
keys,err:=client.Tokens(ctx,codersdk.Me, codersdk.TokensFilter{})
131+
require.NoError(t,err)
132+
require.Len(t,keys,1)
133+
require.Equal(t,tc.scope,keys[0].Scope)
134+
require.ElementsMatch(t,keys[0].Scopes,tc.scopes)
135+
})
136+
}
137+
}
138+
91139
funcTestUserSetTokenDuration(t*testing.T) {
92140
t.Parallel()
93141

‎coderd/database/dbauthz/dbauthz_test.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ func (s *MethodTestSuite) TestAPIKey() {
251251
}))
252252
s.Run("InsertAPIKey",s.Mocked(func(dbm*dbmock.MockStore,faker*gofakeit.Faker,check*expects) {
253253
u:=testutil.Fake(s.T(),faker, database.User{})
254-
arg:= database.InsertAPIKeyParams{UserID:u.ID,LoginType:database.LoginTypePassword,Scopes: database.APIKeyScopes{database.APIKeyScopeAll},IPAddress:defaultIPAddress()}
254+
arg:= database.InsertAPIKeyParams{UserID:u.ID,LoginType:database.LoginTypePassword,Scopes: database.APIKeyScopes{database.ApiKeyScopeCoderAll},IPAddress:defaultIPAddress()}
255255
ret:=testutil.Fake(s.T(),faker, database.APIKey{UserID:u.ID,LoginType:database.LoginTypePassword})
256256
dbm.EXPECT().InsertAPIKey(gomock.Any(),arg).Return(ret,nil).AnyTimes()
257257
check.Args(arg).Asserts(rbac.ResourceApiKey.WithOwner(u.ID.String()),policy.ActionCreate)
@@ -265,7 +265,7 @@ func (s *MethodTestSuite) TestAPIKey() {
265265
check.Args(arg).Asserts(a,policy.ActionUpdate).Returns()
266266
}))
267267
s.Run("DeleteApplicationConnectAPIKeysByUserID",s.Mocked(func(dbm*dbmock.MockStore,faker*gofakeit.Faker,check*expects) {
268-
a:=testutil.Fake(s.T(),faker, database.APIKey{Scopes: database.APIKeyScopes{database.APIKeyScopeApplicationConnect}})
268+
a:=testutil.Fake(s.T(),faker, database.APIKey{Scopes: database.APIKeyScopes{database.ApiKeyScopeCoderApplicationConnect}})
269269
dbm.EXPECT().DeleteApplicationConnectAPIKeysByUserID(gomock.Any(),a.UserID).Return(nil).AnyTimes()
270270
check.Args(a.UserID).Asserts(rbac.ResourceApiKey.WithOwner(a.UserID.String()),policy.ActionDelete).Returns()
271271
}))

‎coderd/database/dbgen/dbgen.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ func APIKey(t testing.TB, db database.Store, seed database.APIKey, munge ...func
185185
CreatedAt:takeFirst(seed.CreatedAt,dbtime.Now()),
186186
UpdatedAt:takeFirst(seed.UpdatedAt,dbtime.Now()),
187187
LoginType:takeFirst(seed.LoginType,database.LoginTypePassword),
188-
Scopes:takeFirstSlice([]database.APIKeyScope(seed.Scopes), []database.APIKeyScope{database.APIKeyScopeAll}),
189-
AllowList:takeFirstSlice(seed.AllowList, database.AllowList{database.AllowListWildcard()}),
188+
Scopes:takeFirstSlice([]database.APIKeyScope(seed.Scopes), []database.APIKeyScope{database.ApiKeyScopeCoderAll}),
189+
AllowList:takeFirstSlice(seed.AllowList, database.AllowList{}),
190190
TokenName:takeFirst(seed.TokenName),
191191
}
192192
for_,fn:=rangemunge {

‎coderd/database/dump.sql‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- Revert canonicalization of special API key scopes
2+
-- Rename enum values back: 'coder:all' -> 'all', 'coder:application_connect' -> 'application_connect'
3+
4+
ALTERTYPE api_key_scope RENAME VALUE'coder:all' TO'all';
5+
ALTERTYPE api_key_scope RENAME VALUE'coder:application_connect' TO'application_connect';
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- Canonicalize special API key scopes to coder:* namespace
2+
-- Rename enum values: 'all' -> 'coder:all', 'application_connect' -> 'coder:application_connect'
3+
4+
ALTERTYPE api_key_scope RENAME VALUE'all' TO'coder:all';
5+
ALTERTYPE api_key_scope RENAME VALUE'application_connect' TO'coder:application_connect';

‎coderd/database/modelmethods.go‎

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,9 @@ func (w ConnectionLog) RBACObject() rbac.Object {
134134

135135
func (sAPIKeyScope)ToRBAC() rbac.ScopeName {
136136
switchs {
137-
caseAPIKeyScopeAll:
137+
caseApiKeyScopeCoderAll:
138138
returnrbac.ScopeAll
139-
caseAPIKeyScopeApplicationConnect:
139+
caseApiKeyScopeCoderApplicationConnect:
140140
returnrbac.ScopeApplicationConnect
141141
default:
142142
// Allow low-level resource:action scopes to flow through to RBAC for
@@ -203,6 +203,29 @@ func (s APIKeyScopes) Expand() (rbac.Scope, error) {
203203
}
204204
}
205205

206+
// De-duplicate permissions across Site/Org/User
207+
dedup:=func(in []rbac.Permission) []rbac.Permission {
208+
iflen(in)==0 {
209+
returnin
210+
}
211+
seen:=make(map[string]struct{},len(in))
212+
out:=make([]rbac.Permission,0,len(in))
213+
for_,p:=rangein {
214+
key:=p.ResourceType+"\x00"+string(p.Action)+"\x00"+strconv.FormatBool(p.Negate)
215+
if_,ok:=seen[key];ok {
216+
continue
217+
}
218+
seen[key]=struct{}{}
219+
out=append(out,p)
220+
}
221+
returnout
222+
}
223+
merged.Site=dedup(merged.Site)
224+
fororgID,perms:=rangemerged.Org {
225+
merged.Org[orgID]=dedup(perms)
226+
}
227+
merged.User=dedup(merged.User)
228+
206229
ifallowAll||len(allowSet)==0 {
207230
merged.AllowIDList= []rbac.AllowListElement{rbac.AllowListAll()}
208231
}else {
@@ -218,7 +241,8 @@ func (s APIKeyScopes) Expand() (rbac.Scope, error) {
218241
// Name returns a human-friendly identifier for tracing/logging.
219242
func (sAPIKeyScopes)Name() rbac.RoleIdentifier {
220243
iflen(s)==0 {
221-
return rbac.RoleIdentifier{Name:string(APIKeyScopeAll)}
244+
// Return all for backward compatibility.
245+
return rbac.RoleIdentifier{Name:string(ApiKeyScopeCoderAll)}
222246
}
223247
names:=make([]string,0,len(s))
224248
for_,s:=ranges {
@@ -227,6 +251,41 @@ func (s APIKeyScopes) Name() rbac.RoleIdentifier {
227251
return rbac.RoleIdentifier{Name:"scopes["+strings.Join(names,"+")+"]"}
228252
}
229253

254+
// APIKeyEffectiveScope merges expanded scopes with the API key's DB allow_list.
255+
// If the DB allow_list is a wildcard or empty, the merged scope's allow list is unchanged.
256+
// Otherwise, the DB allow_list overrides the merged AllowIDList to enforce the token's
257+
// resource scoping consistently across all permissions.
258+
typeAPIKeyEffectiveScopestruct {
259+
ScopesAPIKeyScopes
260+
AllowListAllowList
261+
}
262+
263+
func (eAPIKeyEffectiveScope)Name() rbac.RoleIdentifier {returne.Scopes.Name() }
264+
265+
func (eAPIKeyEffectiveScope)Expand() (rbac.Scope,error) {
266+
merged,err:=e.Scopes.Expand()
267+
iferr!=nil {
268+
return rbac.Scope{},err
269+
}
270+
iflen(e.AllowList)==0 {
271+
returnmerged,nil
272+
}
273+
274+
// If allow list contains a single wildcard (*:*), keep merged allow list as-is
275+
for_,entry:=rangee.AllowList {
276+
ifentry.Type.IsAny()&&entry.ID.IsAny() {
277+
returnmerged,nil
278+
}
279+
}
280+
281+
out:=make([]rbac.AllowListElement,0,len(e.AllowList))
282+
for_,t:=rangee.AllowList {
283+
out=append(out, rbac.AllowListElement{Type:t.Type.String(),ID:t.ID.String()})
284+
}
285+
merged.AllowIDList=out
286+
returnmerged,nil
287+
}
288+
230289
func (kAPIKey)RBACObject() rbac.Object {
231290
returnrbac.ResourceApiKey.WithIDString(k.ID).
232291
WithOwner(k.UserID.String())

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp