Documentation
¶
Overview ¶
Package scopes defines API permission scopes for hypeman API keys.
Scopes follow the pattern "resource:action" where resource is one of the API resource types and action is read, write, or delete. Tokens without a "permissions" claim are treated as having full access for backward compatibility with existing tokens.
Index ¶
- Variables
- func ContextWithPermissions(ctx context.Context, perms []Scope) context.Context
- func HasFullAccess(ctx context.Context) bool
- func HasScope(ctx context.Context, required Scope) bool
- func Middleware() func(http.Handler) http.Handler
- func RequireScope(required Scope) func(http.Handler) http.Handler
- func ScopeStrings(scopes []Scope) []string
- type Scope
Constants ¶
This section is empty.
Variables ¶
var DirectScopeRoutes = map[string]Scope{ "GET /instances/{id}/exec": InstanceWrite, "GET /instances/{id}/cp": InstanceWrite, }
DirectScopeRoutes lists route keys that enforce scopes via RequireScope middleware directly (not via the Middleware() scope checker). These are outside the OpenAPI router group (e.g. WebSocket endpoints).
var PublicRoutes = map[string]bool{ "GET /spec.yaml": true, "GET /spec.json": true, "GET /swagger": true, }
PublicRoutes lists route keys ("METHOD /path") that are intentionally unscoped — they do not require authentication or scope checks. The test uses this to distinguish "intentionally public" from "forgot to add a scope mapping".
var RouteScopes = map[string]Scope{ "GET /builds": BuildRead, "POST /builds": BuildWrite, "DELETE /builds/{id}": BuildDelete, "GET /builds/{id}": BuildRead, "GET /builds/{id}/events": BuildRead, "GET /devices": DeviceRead, "POST /devices": DeviceWrite, "GET /devices/available": DeviceRead, "DELETE /devices/{id}": DeviceDelete, "GET /devices/{id}": DeviceRead, "GET /health": ResourceRead, "GET /resources": ResourceRead, "POST /resources/memory/reclaim": ResourceWrite, "GET /images": ImageRead, "POST /images": ImageWrite, "DELETE /images/{name}": ImageDelete, "GET /images/{name}": ImageRead, "GET /ingresses": IngressRead, "POST /ingresses": IngressWrite, "DELETE /ingresses/{id}": IngressDelete, "GET /ingresses/{id}": IngressRead, "GET /instances": InstanceRead, "POST /instances": InstanceWrite, "DELETE /instances/{id}": InstanceDelete, "GET /instances/{id}": InstanceRead, "POST /instances/{id}/fork": InstanceWrite, "GET /instances/{id}/logs": InstanceRead, "POST /instances/{id}/restore": InstanceWrite, "DELETE /instances/{id}/snapshot-schedule": SnapshotDelete, "GET /instances/{id}/snapshot-schedule": SnapshotRead, "PUT /instances/{id}/snapshot-schedule": SnapshotWrite, "POST /instances/{id}/snapshots": SnapshotWrite, "POST /instances/{id}/snapshots/{snapshotId}/restore": SnapshotWrite, "POST /instances/{id}/standby": InstanceWrite, "POST /instances/{id}/start": InstanceWrite, "GET /instances/{id}/stat": InstanceRead, "GET /instances/{id}/stats": InstanceRead, "GET /instances/{id}/auto-standby/status": InstanceRead, "GET /instances/{id}/wait": InstanceRead, "POST /instances/{id}/stop": InstanceWrite, "PATCH /instances/{id}": InstanceWrite, "DELETE /instances/{id}/volumes/{volumeId}": VolumeWrite, "POST /instances/{id}/volumes/{volumeId}": VolumeWrite, "GET /snapshots": SnapshotRead, "DELETE /snapshots/{snapshotId}": SnapshotDelete, "GET /snapshots/{snapshotId}": SnapshotRead, "POST /snapshots/{snapshotId}/fork": SnapshotWrite, "GET /volumes": VolumeRead, "POST /volumes": VolumeWrite, "POST /volumes/from-archive": VolumeWrite, "DELETE /volumes/{id}": VolumeDelete, "GET /volumes/{id}": VolumeRead, }
RouteScopes maps "METHOD /path-pattern" to the required scope. Path patterns use chi-style {param} placeholders.
Functions ¶
func ContextWithPermissions ¶
ContextWithPermissions stores the granted scopes in the context. A nil slice means full access (legacy behavior).
func HasFullAccess ¶
HasFullAccess returns true if the request context indicates full access (no scopes restriction). This is the case for legacy tokens without a permissions claim.
func HasScope ¶
HasScope checks whether the context has the required scope. Returns true if:
- No permissions claim was set (legacy full-access token)
- The wildcard scope "*" is present
- The specific scope is present
func Middleware ¶
Middleware returns a chi middleware that enforces scoped permissions. It looks up the required scope for the matched route pattern and rejects requests that lack the required scope with 403 Forbidden.
Routes not in the scope map are allowed through (e.g. health check or unauthenticated routes that shouldn't reach this middleware).
func RequireScope ¶
RequireScope returns an HTTP middleware that rejects requests lacking the specified scope with 403 Forbidden.
func ScopeStrings ¶
ScopeStrings converts a slice of Scopes to strings.
Types ¶
type Scope ¶
type Scope string
Scope represents a permission scope for API access.
const ( // Instance scopes InstanceRead Scope = "instance:read" InstanceWrite Scope = "instance:write" // create, start, stop, standby, restore, fork, exec, cp InstanceDelete Scope = "instance:delete" // Image scopes ImageRead Scope = "image:read" ImageWrite Scope = "image:write" // pull/create ImageDelete Scope = "image:delete" // Volume scopes VolumeRead Scope = "volume:read" VolumeWrite Scope = "volume:write" // create, attach, detach VolumeDelete Scope = "volume:delete" // Snapshot scopes SnapshotRead Scope = "snapshot:read" SnapshotWrite Scope = "snapshot:write" // create, restore, fork SnapshotDelete Scope = "snapshot:delete" // Build scopes BuildRead Scope = "build:read" BuildWrite Scope = "build:write" // create BuildDelete Scope = "build:delete" // cancel // Device scopes DeviceRead Scope = "device:read" DeviceWrite Scope = "device:write" // register DeviceDelete Scope = "device:delete" // unregister // Ingress scopes IngressRead Scope = "ingress:read" IngressWrite Scope = "ingress:write" IngressDelete Scope = "ingress:delete" // Resource/health scopes ResourceRead Scope = "resource:read" ResourceWrite Scope = "resource:write" // Wildcard scope — grants all permissions All Scope = "*" )
func AllScopes ¶
func AllScopes() []Scope
AllScopes returns the complete list of valid scopes (excluding wildcard).
func GetPermissions ¶
GetPermissions extracts the granted scopes from context. Returns nil if no permissions are set (legacy full-access token).
func ParseScopes ¶
ParseScopes parses a comma-separated scope string into a slice of Scopes. Returns an error if any scope is unrecognized.
func ScopeForRoute ¶
ScopeForRoute looks up the required scope for a given HTTP method and chi route pattern (e.g. "GET", "/instances/{id}"). Returns the scope and true if found, or ("", false) if the route is not mapped.