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

Commitf516f26

Browse files
committed
feat: add structured JSON format for APIAllowListTarget
APIAllowListTarget now marshals to/from structured JSON objects`{"type":"workspace","id":"<uuid>"}` instead of colon-delimitedstrings. This improves type safety and frontend ergonomics.Changes:- Modified UnmarshalJSON to parse structured object representation- Extracted setValues helper for shared validation logic- Preserved UnmarshalText for backward compatibility with CLI flags and database helpers- Added MarshalJSON/UnmarshalJSON to x/wildcard/Value for proper JSON handling of wildcard values- Updated frontend mock data to use structured format- Added test coverage for both text and object unmarshaling- Added resource ID matchers to regosql converters for template and workspace ID filtering
1 parent2d313ef commitf516f26

File tree

13 files changed

+315
-49
lines changed

13 files changed

+315
-49
lines changed

‎coderd/apidoc/docs.go‎

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

‎coderd/apidoc/swagger.json‎

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

‎coderd/apikey.go‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,10 @@ func (api *API) apiKeyByID(rw http.ResponseWriter, r *http.Request) {
217217
return
218218
}
219219

220-
httpapi.Write(ctx,rw,http.StatusOK,convertAPIKey(key))
220+
sdkKey:=api.convertAPIKey(ctx,key)
221+
api.populateAllowListDisplayNames(ctx,sdkKey.AllowList)
222+
223+
httpapi.Write(ctx,rw,http.StatusOK,sdkKey)
221224
}
222225

223226
// @Summary Get API key by token name
@@ -252,7 +255,10 @@ func (api *API) apiKeyByName(rw http.ResponseWriter, r *http.Request) {
252255
return
253256
}
254257

255-
httpapi.Write(ctx,rw,http.StatusOK,convertAPIKey(token))
258+
sdkKey:=api.convertAPIKey(ctx,token)
259+
api.populateAllowListDisplayNames(ctx,sdkKey.AllowList)
260+
261+
httpapi.Write(ctx,rw,http.StatusOK,sdkKey)
256262
}
257263

258264
// @Summary Update token API key
@@ -389,7 +395,7 @@ func (api *API) patchToken(rw http.ResponseWriter, r *http.Request) {
389395

390396
aReq.New=updatedToken
391397
aReq.SetAdditionalFields(audit.WrapAPIKeyFields(audit.APIKeyFields(ctx,api.Logger,updatedToken)))
392-
httpapi.Write(ctx,rw,http.StatusOK,convertAPIKey(updatedToken))
398+
httpapi.Write(ctx,rw,http.StatusOK,api.convertAPIKey(ctx,updatedToken))
393399
}
394400

395401
// @Summary Get user tokens
@@ -456,12 +462,12 @@ func (api *API) tokens(rw http.ResponseWriter, r *http.Request) {
456462
for_,key:=rangekeys {
457463
ifuser,exists:=usersByID[key.UserID];exists {
458464
apiKeys=append(apiKeys, codersdk.APIKeyWithOwner{
459-
APIKey:convertAPIKey(key),
465+
APIKey:api.convertAPIKey(ctx,key),
460466
Username:user.Username,
461467
})
462468
}else {
463469
apiKeys=append(apiKeys, codersdk.APIKeyWithOwner{
464-
APIKey:convertAPIKey(key),
470+
APIKey:api.convertAPIKey(ctx,key),
465471
Username:"",
466472
})
467473
}

‎coderd/apikey_resolve_test.go‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package coderd
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/google/uuid"
9+
"github.com/stretchr/testify/require"
10+
"go.uber.org/mock/gomock"
11+
12+
"cdr.dev/slog"
13+
14+
"github.com/coder/coder/v2/coderd/database"
15+
"github.com/coder/coder/v2/coderd/database/dbmock"
16+
"github.com/coder/coder/v2/coderd/rbac/policy"
17+
"github.com/coder/coder/v2/codersdk"
18+
)
19+
20+
funcTestConvertAPIKeyAllowListDisplayName(t*testing.T) {
21+
t.Parallel()
22+
23+
ctrl:=gomock.NewController(t)
24+
t.Cleanup(ctrl.Finish)
25+
26+
ctx:=context.Background()
27+
db:=dbmock.NewMockStore(ctrl)
28+
templateID:=uuid.New()
29+
30+
db.EXPECT().
31+
GetTemplateByID(gomock.Any(),templateID).
32+
Return(database.Template{
33+
ID:templateID,
34+
Name:"infra-template",
35+
DisplayName:"Infra Template",
36+
},nil).
37+
Times(1)
38+
39+
api:=&API{
40+
Options:&Options{
41+
Database:db,
42+
Logger:slog.Make(),
43+
},
44+
}
45+
46+
key:= database.APIKey{
47+
ID:"key-1",
48+
UserID:uuid.New(),
49+
LastUsed:time.Now(),
50+
ExpiresAt:time.Now().Add(time.Hour),
51+
CreatedAt:time.Now(),
52+
UpdatedAt:time.Now(),
53+
LoginType:database.LoginTypeToken,
54+
LifetimeSeconds:int64(time.Hour.Seconds()),
55+
TokenName:"cli",
56+
Scopes: database.APIKeyScopes{database.ApiKeyScopeCoderAll},
57+
AllowList: database.AllowList{
58+
{Type:string(codersdk.ResourceTemplate),ID:templateID.String()},
59+
},
60+
}
61+
62+
result:=api.convertAPIKey(ctx,key)
63+
64+
require.Len(t,result.AllowList,1)
65+
require.Equal(t,codersdk.ResourceTemplate,result.AllowList[0].Type)
66+
require.Equal(t,templateID.String(),result.AllowList[0].ID)
67+
require.Equal(t,"Infra Template",result.AllowList[0].DisplayName)
68+
}
69+
70+
funcTestConvertAPIKeyAllowListDisplayNameWildcard(t*testing.T) {
71+
t.Parallel()
72+
73+
ctrl:=gomock.NewController(t)
74+
t.Cleanup(ctrl.Finish)
75+
76+
ctx:=context.Background()
77+
db:=dbmock.NewMockStore(ctrl)
78+
79+
api:=&API{
80+
Options:&Options{
81+
Database:db,
82+
Logger:slog.Make(),
83+
},
84+
}
85+
86+
key:= database.APIKey{
87+
ID:"key-2",
88+
AllowList: database.AllowList{
89+
{Type:string(codersdk.ResourceWildcard),ID:policy.WildcardSymbol},
90+
},
91+
}
92+
93+
result:=api.convertAPIKey(ctx,key)
94+
95+
require.Len(t,result.AllowList,1)
96+
require.Equal(t,codersdk.ResourceWildcard,result.AllowList[0].Type)
97+
require.Equal(t,"*",result.AllowList[0].ID)
98+
require.Empty(t,result.AllowList[0].DisplayName)
99+
}

‎coderd/apikey_scopes_validation_test.go‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,10 @@ func TestTokenCreation_AllowListValidation(t *testing.T) {
9393
// Invalid resource type should be rejected.
9494
_,err:=client.CreateToken(ctx,codersdk.Me, codersdk.CreateTokenRequest{
9595
Scopes: []codersdk.APIKeyScope{codersdk.APIKeyScopeWorkspaceRead},
96-
AllowList: []codersdk.APIAllowListTarget{
97-
{Type:codersdk.RBACResource("unknown"),ID:uuid.New().String()},
98-
},
96+
AllowList: []codersdk.APIAllowListTarget{{
97+
Type:codersdk.RBACResource("unknown"),
98+
ID:uuid.New().String(),
99+
}},
99100
})
100101
require.Error(t,err)
101102

‎coderd/rbac/regosql/compile_test.go‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,14 @@ func TestRegoQueries(t *testing.T) {
217217
" OR (workspaces.group_acl#>array['96c55a0e-73b4-44fc-abac-70d53c35c04c', 'permissions'] ? '*'))",
218218
VariableConverter:regosql.WorkspaceConverter(),
219219
},
220+
{
221+
Name:"WorkspaceIDMatcher",
222+
Queries: []string{
223+
`input.object.id = "a8d0f8ce-6a01-4d0d-ab1d-1d546958feae"`,
224+
},
225+
ExpectedSQL:p("workspaces.id :: text = 'a8d0f8ce-6a01-4d0d-ab1d-1d546958feae'"),
226+
VariableConverter:regosql.WorkspaceConverter(),
227+
},
220228
{
221229
Name:"NoACLConfig",
222230
Queries: []string{
@@ -262,6 +270,14 @@ neq(input.object.owner, "");
262270
p("false")),
263271
VariableConverter:regosql.TemplateConverter(),
264272
},
273+
{
274+
Name:"TemplateIDMatcher",
275+
Queries: []string{
276+
`input.object.id = "a829cb9d-7c5b-4c3b-bf78-053827a56e58"`,
277+
},
278+
ExpectedSQL:p("t.id :: text = 'a829cb9d-7c5b-4c3b-bf78-053827a56e58'"),
279+
VariableConverter:regosql.TemplateConverter(),
280+
},
265281
{
266282
Name:"UserNoOrgOwner",
267283
Queries: []string{

‎coderd/rbac/regosql/configs.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func userACLMatcher(m sqltypes.VariableMatcher) ACLMappingVar {
2424

2525
funcTemplateConverter()*sqltypes.VariableConverter {
2626
matcher:=sqltypes.NewVariableConverter().RegisterMatcher(
27-
resourceIDMatcher(),
27+
sqltypes.StringVarMatcher("t.id :: text", []string{"input","object","id"}),
2828
sqltypes.StringVarMatcher("t.organization_id :: text", []string{"input","object","org_owner"}),
2929
// Templates have no user owner, only owner by an organization.
3030
sqltypes.AlwaysFalse(userOwnerMatcher()),
@@ -38,7 +38,7 @@ func TemplateConverter() *sqltypes.VariableConverter {
3838

3939
funcWorkspaceConverter()*sqltypes.VariableConverter {
4040
matcher:=sqltypes.NewVariableConverter().RegisterMatcher(
41-
resourceIDMatcher(),
41+
sqltypes.StringVarMatcher("workspaces.id :: text", []string{"input","object","id"}),
4242
sqltypes.StringVarMatcher("workspaces.organization_id :: text", []string{"input","object","org_owner"}),
4343
userOwnerMatcher(),
4444
)

‎coderd/users.go‎

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1578,7 +1578,7 @@ func userOrganizationIDs(ctx context.Context, api *API, user database.User) ([]u
15781578
returnmember.OrganizationIDs,nil
15791579
}
15801580

1581-
funcconvertAPIKey(k database.APIKey) codersdk.APIKey {
1581+
func(api*API)convertAPIKey(ctx context.Context,k database.APIKey) codersdk.APIKey {
15821582
// Derive a single legacy scope name for response compatibility.
15831583
// Historically, the API exposed only two scope strings: "all" and
15841584
// "application_connect". Continue to return those for clients even
@@ -1622,3 +1622,65 @@ func convertAPIKey(k database.APIKey) codersdk.APIKey {
16221622
AllowList:allowList,
16231623
}
16241624
}
1625+
1626+
func (api*API)populateAllowListDisplayNames(ctx context.Context,allowList []codersdk.APIAllowListTarget) {
1627+
iflen(allowList)==0 {
1628+
return
1629+
}
1630+
1631+
cache:=make(map[string]string,len(allowList))
1632+
fori:=rangeallowList {
1633+
target:=allowList[i]
1634+
iftarget.Type==codersdk.ResourceWildcard||target.ID==policy.WildcardSymbol {
1635+
continue
1636+
}
1637+
1638+
key:=target.String()
1639+
name,ok:=cache[key]
1640+
if!ok {
1641+
name,ok=api.allowListDisplayName(ctx,target.Type,target.ID)
1642+
if!ok {
1643+
cache[key]=""
1644+
continue
1645+
}
1646+
cache[key]=name
1647+
}
1648+
ifname!="" {
1649+
allowList[i].DisplayName=name
1650+
}
1651+
}
1652+
}
1653+
1654+
func (api*API)allowListDisplayName(ctx context.Context,resource codersdk.RBACResource,rawIDstring) (string,bool) {
1655+
ifapi==nil||api.Options==nil||api.Database==nil {
1656+
return"",false
1657+
}
1658+
ifrawID==""||rawID==policy.WildcardSymbol {
1659+
return"",false
1660+
}
1661+
1662+
id,err:=uuid.Parse(rawID)
1663+
iferr!=nil {
1664+
return"",false
1665+
}
1666+
1667+
switchresource {
1668+
casecodersdk.ResourceWorkspace:
1669+
workspace,err:=api.Database.GetWorkspaceByID(ctx,id)
1670+
iferr!=nil {
1671+
return"",false
1672+
}
1673+
returnworkspace.Name,true
1674+
casecodersdk.ResourceTemplate:
1675+
template,err:=api.Database.GetTemplateByID(ctx,id)
1676+
iferr!=nil {
1677+
return"",false
1678+
}
1679+
iftemplate.DisplayName!="" {
1680+
returntemplate.DisplayName,true
1681+
}
1682+
returntemplate.Name,true
1683+
default:
1684+
return"",false
1685+
}
1686+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp