mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
4bda39585d
# Add support for low-level API key scopes This PR adds support for fine-grained API key scopes based on RBAC resource:action pairs. It includes: 1. A new endpoint `/api/v2/auth/scopes` to list all public low-level API key scopes 2. Generated constants in the SDK for all public scopes 3. Tests to verify scope validation during token creation 4. Updated API documentation to reflect the expanded scope options The implementation allows users to create API keys with specific permissions like `workspace:read` or `template:use` instead of only the legacy `all` or `application_connect` scopes. Fixes #19847
100 lines
2.3 KiB
Go
100 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"go/format"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/coder/coder/v2/coderd/rbac"
|
|
"github.com/coder/coder/v2/coderd/rbac/policy"
|
|
)
|
|
|
|
func main() {
|
|
out, err := generate()
|
|
if err != nil {
|
|
_, _ = fmt.Fprintf(os.Stderr, "generate apikey scopes: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
if _, err := fmt.Print(string(out)); err != nil {
|
|
_, _ = fmt.Fprintf(os.Stderr, "write output: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func generate() ([]byte, error) {
|
|
names := rbac.ExternalScopeNames()
|
|
sort.Strings(names)
|
|
|
|
var b bytes.Buffer
|
|
if _, err := b.WriteString("// Code generated by scripts/apikeyscopesgen. DO NOT EDIT.\n"); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := b.WriteString("package codersdk\n\n"); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Constants
|
|
if _, err := b.WriteString("const (\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 {
|
|
return nil, err
|
|
}
|
|
}
|
|
if _, err := b.WriteString(")\n\n"); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Slices
|
|
if _, err := b.WriteString("// PublicAPIKeyScopes lists all public low-level API key scopes.\n"); err != nil {
|
|
return nil, err
|
|
}
|
|
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))
|
|
if _, err := fmt.Fprintf(&b, "\t%s,\n", constName); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if _, err := b.WriteString("}\n\n"); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return format.Source(b.Bytes())
|
|
}
|
|
|
|
func splitRA(name string) (resource string, action string) {
|
|
parts := strings.SplitN(name, ":", 2)
|
|
if len(parts) != 2 {
|
|
return name, ""
|
|
}
|
|
return parts[0], parts[1]
|
|
}
|
|
|
|
func pascal(s string) string {
|
|
// Replace non-identifier separators with spaces, then Title-case and strip.
|
|
s = strings.ReplaceAll(s, "_", " ")
|
|
s = strings.ReplaceAll(s, "-", " ")
|
|
s = strings.ReplaceAll(s, ":", " ")
|
|
words := strings.Fields(s)
|
|
for i := range words {
|
|
words[i] = strings.ToUpper(words[i][:1]) + words[i][1:]
|
|
}
|
|
return strings.Join(words, "")
|
|
}
|