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

Commit47c92ad

Browse files
authored
feat: add public RBAC scope catalog for user-requestable permissions (#19913)
# Add a curated catalog of public RBAC scopesThis PR introduces a curated catalog of public RBAC scopes that are exposed to users. It adds:- A `publicLowLevel` map in `scopes_catalog.go` that defines which resource:action pairs are user-requestable- `IsPublicLowLevel()` function to check if a scope is in the public catalog- `PublicLowLevelScopeNames()` function that returns a sorted list of public scopes- Tests to verify the catalog entries are valid and properly sorted- Updated documentation in the check-scopes README to clarify that public scopes should be added to this catalogThis change helps distinguish between internal-only scopes and those that should be exposed to users in the API.
1 parenteb55f0a commit47c92ad

File tree

5 files changed

+146
-2
lines changed

5 files changed

+146
-2
lines changed

‎coderd/rbac/scopes.go‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,11 @@ func parseLowLevelScope(name ScopeName) (resource string, action policy.Action,
205205
if!exists {
206206
return"","",false
207207
}
208+
209+
ifact==policy.WildcardSymbol {
210+
returnres,policy.WildcardSymbol,true
211+
}
212+
208213
if_,exists:=def.Actions[policy.Action(act)];!exists {
209214
return"","",false
210215
}

‎coderd/rbac/scopes_catalog.go‎

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package rbac
2+
3+
import (
4+
"sort"
5+
"strings"
6+
)
7+
8+
// externalLowLevel is the curated set of low-level scope names exposed to users.
9+
// Any valid resource:action pair not in this set is considered internal-only
10+
// and must not be user-requestable.
11+
varexternalLowLevel=map[ScopeName]struct{}{
12+
// Workspaces
13+
"workspace:read": {},
14+
"workspace:create": {},
15+
"workspace:update": {},
16+
"workspace:delete": {},
17+
"workspace:ssh": {},
18+
"workspace:start": {},
19+
"workspace:stop": {},
20+
"workspace:application_connect": {},
21+
"workspace:*": {},
22+
23+
// Templates
24+
"template:read": {},
25+
"template:create": {},
26+
"template:update": {},
27+
"template:delete": {},
28+
"template:use": {},
29+
"template:*": {},
30+
31+
// API keys (self-management)
32+
"api_key:read": {},
33+
"api_key:create": {},
34+
"api_key:update": {},
35+
"api_key:delete": {},
36+
"api_key:*": {},
37+
38+
// Files
39+
"file:read": {},
40+
"file:create": {},
41+
"file:*": {},
42+
43+
// Users (personal profile only)
44+
"user:read_personal": {},
45+
"user:update_personal": {},
46+
47+
// User secrets
48+
"user_secret:read": {},
49+
"user_secret:create": {},
50+
"user_secret:update": {},
51+
"user_secret:delete": {},
52+
"user_secret:*": {},
53+
}
54+
55+
// IsExternalScope returns true if the scope is public, including the
56+
// `all` and `application_connect` special scopes and the curated
57+
// low-level resource:action scopes.
58+
funcIsExternalScope(nameScopeName)bool {
59+
switchname {
60+
caseScopeAll,ScopeApplicationConnect:
61+
returntrue
62+
}
63+
if_,ok:=externalLowLevel[name];ok {
64+
returntrue
65+
}
66+
67+
returnfalse
68+
}
69+
70+
// ExternalScopeNames returns a sorted list of all public scopes, which includes
71+
// the `all` and `application_connect` special scopes and the curated public
72+
// low-level names.
73+
funcExternalScopeNames() []string {
74+
names:=make([]string,0,len(externalLowLevel)+2)
75+
names=append(names,string(ScopeAll))
76+
names=append(names,string(ScopeApplicationConnect))
77+
78+
// curated low-level names, filtered for validity
79+
forname:=rangeexternalLowLevel {
80+
if_,_,ok:=parseLowLevelScope(name);ok {
81+
names=append(names,string(name))
82+
}
83+
}
84+
85+
sort.Slice(names,func(i,jint)bool {returnstrings.Compare(names[i],names[j])<0 })
86+
returnnames
87+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package rbac
2+
3+
import (
4+
"sort"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
funcTestExternalScopeNames(t*testing.T) {
11+
t.Parallel()
12+
13+
names:=ExternalScopeNames()
14+
require.NotEmpty(t,names)
15+
16+
// Ensure sorted ascending
17+
sorted:=append([]string(nil),names...)
18+
sort.Strings(sorted)
19+
require.Equal(t,sorted,names)
20+
21+
// Ensure each entry parses and expands to site-only
22+
for_,name:=rangenames {
23+
// Skip `all` and `application_connect` since they do not
24+
// expand into a low level scope.
25+
// They are handled differently.
26+
ifname==string(ScopeAll)||name==string(ScopeApplicationConnect) {
27+
continue
28+
}
29+
30+
res,act,ok:=parseLowLevelScope(ScopeName(name))
31+
require.Truef(t,ok,"catalog entry should parse: %s",name)
32+
33+
s,err:=ScopeName(name).Expand()
34+
require.NoErrorf(t,err,"catalog entry should expand: %s",name)
35+
require.Len(t,s.Site,1)
36+
require.Equal(t,res,s.Site[0].ResourceType)
37+
require.Equal(t,act,s.Site[0].Action)
38+
require.Empty(t,s.Org)
39+
require.Empty(t,s.User)
40+
}
41+
}
42+
43+
funcTestIsExternalScope(t*testing.T) {
44+
t.Parallel()
45+
46+
require.True(t,IsExternalScope("workspace:read"))
47+
require.True(t,IsExternalScope("template:use"))
48+
require.True(t,IsExternalScope("workspace:*"))
49+
require.False(t,IsExternalScope("debug_info:read"))// internal-only
50+
require.False(t,IsExternalScope("unknown:read"))
51+
}

‎scripts/check-scopes/README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,5 @@ When the tool reports missing values:
4040
make -B gen/db&& make lint/check-scopes
4141
```
4242

43-
3. Decide whether each new scope is public (exposed in the catalog) or internal-only (handled by the catalog task).
43+
3. Decide whether each new scope is public (exposed in the catalog) or internal-only.
44+
- If public, add it to the curated map in`coderd/rbac/scopes_catalog.go` (`externalLowLevel`) so it appears in the public catalog and can be requested by users.

‎scripts/check-scopes/main.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func main() {
5353
_,_=fmt.Fprintf(os.Stderr," ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS '%s';\n",m)
5454
}
5555
_,_=fmt.Fprintln(os.Stderr)
56-
_,_=fmt.Fprintln(os.Stderr,"Also decide if each new scope ispublic (exposed in thecatalog) or internal-only (catalog task).")
56+
_,_=fmt.Fprintln(os.Stderr,"Also decide if each new scope isexternal (exposed in the`externalLowLevel` in coderd/rbac/scopes_catalog.go) or internal-only.")
5757
os.Exit(1)
5858
}
5959

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp