rdev/internal/auth/scopes.go
jordan 72d16929ca feat: Implement hexagonal architecture with services, webhooks, queue, and telemetry
Major refactoring to hexagonal (ports & adapters) architecture:

- Add service layer (apikey_service, project_service) for business logic
- Add webhook system with dispatcher and delivery tracking
- Add command queue with priority-based processing
- Add rate limiting with sliding window algorithm
- Add audit logging for command execution
- Add OpenTelemetry integration (traces, metrics, spans)
- Add circuit breaker for fault tolerance
- Add cached repository wrapper for performance
- Add comprehensive validation package
- Add Kubernetes client integration for pod management
- Add database migrations (allowed_ips, audit_log, rate_limiting, queue, webhooks)
- Add network policy and PodDisruptionBudget for k8s
- Remove legacy executor and projects/registry packages
- Untrack secrets.yaml (now managed via envault)
- Add coverage.out to .gitignore
- Add e2e test infrastructure with docker-compose
- Add comprehensive documentation (API, architecture, operations, plans)
- Add golangci-lint config and pre-commit hook

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 19:57:46 -07:00

117 lines
3.1 KiB
Go

package auth
import "slices"
// Scope represents an API permission scope.
type Scope string
// Available scopes.
const (
ScopeProjectsRead Scope = "projects:read"
ScopeProjectsExecute Scope = "projects:execute"
ScopeKeysRead Scope = "keys:read"
ScopeKeysWrite Scope = "keys:write"
ScopeAuditRead Scope = "audit:read"
ScopeQueueRead Scope = "queue:read"
ScopeQueueWrite Scope = "queue:write"
ScopeWebhookRead Scope = "webhook:read"
ScopeWebhookWrite Scope = "webhook:write"
ScopeAdmin Scope = "admin"
)
// AllScopes is the list of all valid scopes.
var AllScopes = []Scope{
ScopeProjectsRead,
ScopeProjectsExecute,
ScopeKeysRead,
ScopeKeysWrite,
ScopeAuditRead,
ScopeQueueRead,
ScopeQueueWrite,
ScopeWebhookRead,
ScopeWebhookWrite,
ScopeAdmin,
}
// ScopeDescriptions provides human-readable descriptions.
var ScopeDescriptions = map[Scope]string{
ScopeProjectsRead: "List and view project details",
ScopeProjectsExecute: "Execute commands (claude, shell, git) on projects",
ScopeKeysRead: "List API keys (metadata only, not secrets)",
ScopeKeysWrite: "Create and revoke API keys",
ScopeAuditRead: "View audit logs for command executions",
ScopeQueueRead: "View queued commands and queue status",
ScopeQueueWrite: "Enqueue and cancel queued commands",
ScopeWebhookRead: "View webhooks and delivery history",
ScopeWebhookWrite: "Create, update, and delete webhooks",
ScopeAdmin: "Full administrative access (includes all scopes)",
}
// IsValid checks if a scope is valid.
func (s Scope) IsValid() bool {
return slices.Contains(AllScopes, s)
}
// String returns the scope as a string.
func (s Scope) String() string {
return string(s)
}
// ScopesFromStrings converts string slice to Scope slice.
func ScopesFromStrings(ss []string) []Scope {
scopes := make([]Scope, len(ss))
for i, s := range ss {
scopes[i] = Scope(s)
}
return scopes
}
// ScopesToStrings converts Scope slice to string slice.
func ScopesToStrings(scopes []Scope) []string {
ss := make([]string, len(scopes))
for i, s := range scopes {
ss[i] = string(s)
}
return ss
}
// ValidateScopes checks if all scopes are valid.
func ValidateScopes(scopes []Scope) bool {
for _, s := range scopes {
if !s.IsValid() {
return false
}
}
return true
}
// HasScope checks if a scope list contains a required scope.
// Admin scope grants access to everything.
func HasScope(scopes []Scope, required Scope) bool {
for _, s := range scopes {
if s == ScopeAdmin || s == required {
return true
}
}
return false
}
// HasAnyScope checks if a scope list contains any of the required scopes.
func HasAnyScope(scopes []Scope, required ...Scope) bool {
for _, r := range required {
if HasScope(scopes, r) {
return true
}
}
return false
}
// HasProjectAccess checks if the key has access to a specific project.
// projectIDs nil means access to all projects.
func HasProjectAccess(allowedProjects []string, projectID string) bool {
if allowedProjects == nil {
return true // nil = all projects
}
return slices.Contains(allowedProjects, projectID)
}