mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
refactor: add wildcard scope entries for API key scopes (#20032)
# Add API Key Scope Wildcards This PR adds wildcard API key scopes (`resource:*`) for all RBAC resources to ensure every resource has a matching wildcard value. It also adds all individual `resource:action` scopes to the API documentation and TypeScript definitions. The changes include: - Adding a new database migration (000377) that adds wildcard API key scopes - Updating the API documentation to include all available scopes - Enhancing the scope generation scripts to include all resource wildcards - Updating the TypeScript definitions to match the expanded scope list These changes make creating API keys with comprehensive permissions for specific resource types easier.
This commit is contained in:
@@ -25,8 +25,8 @@ func main() {
|
||||
}
|
||||
|
||||
func generate() ([]byte, error) {
|
||||
names := rbac.ExternalScopeNames()
|
||||
slices.Sort(names)
|
||||
allNames := collectAllScopeNames()
|
||||
publicNames := rbac.ExternalScopeNames()
|
||||
|
||||
var b bytes.Buffer
|
||||
if _, err := b.WriteString("// Code generated by scripts/apikeyscopesgen. DO NOT EDIT.\n"); err != nil {
|
||||
@@ -61,13 +61,9 @@ func generate() ([]byte, error) {
|
||||
if _, err := b.WriteString("\tAPIKeyScopeApplicationConnect APIKeyScope = \"application_connect\"\n"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, n := range names {
|
||||
res, act := splitRA(n)
|
||||
if act == policy.WildcardSymbol {
|
||||
act = "All"
|
||||
}
|
||||
constName := fmt.Sprintf("APIKeyScope%s%s", pascal(res), pascal(act))
|
||||
if _, err := fmt.Fprintf(&b, "\t%s APIKeyScope = \"%s\"\n", constName, n); err != nil {
|
||||
for _, name := range allNames {
|
||||
constName := constNameForScope(name)
|
||||
if _, err := fmt.Fprintf(&b, "\t%s APIKeyScope = \"%s\"\n", constName, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -82,12 +78,8 @@ func generate() ([]byte, error) {
|
||||
if _, err := b.WriteString("var PublicAPIKeyScopes = []APIKeyScope{\n"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, n := range names {
|
||||
res, act := splitRA(n)
|
||||
if act == policy.WildcardSymbol {
|
||||
act = "All"
|
||||
}
|
||||
constName := fmt.Sprintf("APIKeyScope%s%s", pascal(res), pascal(act))
|
||||
for _, name := range publicNames {
|
||||
constName := constNameForScope(name)
|
||||
if _, err := fmt.Fprintf(&b, "\t%s,\n", constName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -99,6 +91,54 @@ func generate() ([]byte, error) {
|
||||
return format.Source(b.Bytes())
|
||||
}
|
||||
|
||||
func collectAllScopeNames() []string {
|
||||
seen := make(map[string]struct{})
|
||||
var names []string
|
||||
add := func(name string) {
|
||||
if name == "" {
|
||||
return
|
||||
}
|
||||
if _, ok := seen[name]; ok {
|
||||
return
|
||||
}
|
||||
seen[name] = struct{}{}
|
||||
names = append(names, name)
|
||||
}
|
||||
|
||||
for resource, def := range policy.RBACPermissions {
|
||||
if resource == policy.WildcardSymbol {
|
||||
continue
|
||||
}
|
||||
add(resource + ":" + policy.WildcardSymbol)
|
||||
for action := range def.Actions {
|
||||
add(resource + ":" + string(action))
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range rbac.CompositeScopeNames() {
|
||||
add(name)
|
||||
}
|
||||
|
||||
for _, name := range rbac.BuiltinScopeNames() {
|
||||
s := string(name)
|
||||
if !strings.Contains(s, ":") {
|
||||
continue
|
||||
}
|
||||
add(s)
|
||||
}
|
||||
|
||||
slices.Sort(names)
|
||||
return names
|
||||
}
|
||||
|
||||
func constNameForScope(name string) string {
|
||||
resource, action := splitRA(name)
|
||||
if action == policy.WildcardSymbol {
|
||||
action = "All"
|
||||
}
|
||||
return fmt.Sprintf("APIKeyScope%s%s", pascal(resource), pascal(action))
|
||||
}
|
||||
|
||||
func splitRA(name string) (resource string, action string) {
|
||||
parts := strings.SplitN(name, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
|
||||
@@ -58,23 +58,37 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// expectedFromRBAC returns the set of <resource>:<action> pairs derived from RBACPermissions.
|
||||
// expectedFromRBAC returns the set of scope names the DB enum must support.
|
||||
func expectedFromRBAC() map[string]struct{} {
|
||||
want := make(map[string]struct{})
|
||||
// Low-level <resource>:<action>
|
||||
add := func(name string) {
|
||||
if name == "" {
|
||||
return
|
||||
}
|
||||
want[name] = struct{}{}
|
||||
}
|
||||
// Low-level <resource>:<action> and synthesized <resource>:* wildcards
|
||||
for resource, def := range policy.RBACPermissions {
|
||||
if resource == policy.WildcardSymbol {
|
||||
// Ignore wildcard entry; it has no concrete <resource>:<action> pairs.
|
||||
continue
|
||||
}
|
||||
add(resource + ":" + policy.WildcardSymbol)
|
||||
for action := range def.Actions {
|
||||
key := resource + ":" + string(action)
|
||||
want[key] = struct{}{}
|
||||
add(resource + ":" + string(action))
|
||||
}
|
||||
}
|
||||
// Composite coder:* names
|
||||
for _, n := range rbac.CompositeScopeNames() {
|
||||
want[n] = struct{}{}
|
||||
add(n)
|
||||
}
|
||||
// Built-in coder-prefixed scopes such as coder:all
|
||||
for _, n := range rbac.BuiltinScopeNames() {
|
||||
s := string(n)
|
||||
if !strings.Contains(s, ":") {
|
||||
continue
|
||||
}
|
||||
add(s)
|
||||
}
|
||||
return want
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/coderd/rbac/policy"
|
||||
)
|
||||
|
||||
func main() {
|
||||
seen := map[string]struct{}{}
|
||||
var vals []string
|
||||
for resource, def := range policy.RBACPermissions {
|
||||
if resource == policy.WildcardSymbol {
|
||||
continue
|
||||
}
|
||||
for action := range def.Actions {
|
||||
vals = append(vals, fmt.Sprintf("%s:%s", resource, action))
|
||||
}
|
||||
}
|
||||
// Include composite coder:* scopes as first-class enum values
|
||||
vals = append(vals, rbac.CompositeScopeNames()...)
|
||||
sort.Strings(vals)
|
||||
for _, v := range vals {
|
||||
if _, ok := seen[v]; ok {
|
||||
continue
|
||||
}
|
||||
seen[v] = struct{}{}
|
||||
_, _ = fmt.Printf("ALTER TYPE api_key_scope ADD VALUE IF NOT EXISTS '%s';\n", v)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user