authz

package
v2.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: BSD-3-Clause Imports: 3 Imported by: 0

Documentation

Overview

Package authz defines the permission taxonomy and authorization primitives used across Arcane handlers. Permissions are strings of the form "<resource>:<action>" and are classified as either org-level (require a globally-scoped role) or env-scoped (resolved against the environment ID from the request path).

Index

Constants

View Source
const (
	AccessSurfaceKindRoute             = "route"
	AccessSurfaceKindSettingsCategory  = "settings-category"
	AccessSurfaceKindCustomizeCategory = "customize-category"
	AccessSurfaceKindLanding           = "landing"

	AccessModePermissions = "permissions"
	AccessModeAnyChild    = "any-child"

	AccessMatchModeAnyOf = "any-of"
	AccessMatchModeAllOf = "all-of"

	AccessScopeModeGlobalOnly            = "global-only"
	AccessScopeModeSelectedEnvPlusGlobal = "selected-env-plus-global"
	AccessScopeModeAnyEffectiveScope     = "any-effective-scope"
)
View Source
const (
	PermissionScopeGlobal = "global"
	PermissionScopeEnv    = "env"
)
View Source
const (
	BuiltInRoleAdmin         = "role_admin"
	BuiltInRoleEditor        = "role_editor"
	BuiltInRoleNoShellEditor = "role_no_shell_editor"
	BuiltInRoleDeployer      = "role_deployer"
	BuiltInRoleMonitor       = "role_monitor"
	BuiltInRoleViewer        = "role_viewer"
)

Built-in role IDs. Stable across migrations so other code may reference them safely. All six built-in roles are seeded by migration 054_add_rbac.

View Source
const (
	PermUsersList   = "users:list"
	PermUsersRead   = "users:read"
	PermUsersCreate = "users:create"
	PermUsersUpdate = "users:update"
	PermUsersDelete = "users:delete"

	// PermRolesList and the role permissions below cover role management
	// (Create / Update / Delete) and role assignment to users. They are reserved
	// for global admins and intentionally not exposed as delegated permissions —
	// see backend/api/middleware/role.go::RequireGlobalAdmin. Likewise, managing
	// OIDC group → role mappings is admin-only because it is effectively another
	// path for granting role assignments.
	PermRolesList = "roles:list"
	PermRolesRead = "roles:read"

	PermApiKeysList   = "apikeys:list"
	PermApiKeysRead   = "apikeys:read"
	PermApiKeysCreate = "apikeys:create"
	PermApiKeysUpdate = "apikeys:update"
	PermApiKeysDelete = "apikeys:delete"

	PermFederatedList   = "federated:list"
	PermFederatedRead   = "federated:read"
	PermFederatedCreate = "federated:create"
	PermFederatedUpdate = "federated:update"
	PermFederatedDelete = "federated:delete"

	PermSettingsRead  = "settings:read"
	PermSettingsWrite = "settings:write"

	PermEnvironmentsList   = "environments:list"
	PermEnvironmentsRead   = "environments:read"
	PermEnvironmentsCreate = "environments:create"
	PermEnvironmentsUpdate = "environments:update"
	PermEnvironmentsDelete = "environments:delete"
	PermEnvironmentsPair   = "environments:pair"
	PermEnvironmentsSync   = "environments:sync"

	PermRegistriesList   = "registries:list"
	PermRegistriesRead   = "registries:read"
	PermRegistriesCreate = "registries:create"
	PermRegistriesUpdate = "registries:update"
	PermRegistriesDelete = "registries:delete"
	PermRegistriesTest   = "registries:test"

	PermTemplatesList   = "templates:list"
	PermTemplatesRead   = "templates:read"
	PermTemplatesCreate = "templates:create"
	PermTemplatesUpdate = "templates:update"
	PermTemplatesDelete = "templates:delete"

	PermGitReposList   = "git-repositories:list"
	PermGitReposRead   = "git-repositories:read"
	PermGitReposCreate = "git-repositories:create"
	PermGitReposUpdate = "git-repositories:update"
	PermGitReposDelete = "git-repositories:delete"
	PermGitReposTest   = "git-repositories:test"
	PermGitReposSync   = "git-repositories:sync"

	PermEventsRead      = "events:read"
	PermEventsDelete    = "events:delete"
	PermCustomizeManage = "customize:manage"

	// PermDiagnosticsRead gates the admin-only runtime diagnostics surface
	// (runtime/memory/GC stats, WebSocket metrics, pprof profiles, and the live
	// backend log tail). Global-scoped; seeded only into the Admin role.
	PermDiagnosticsRead = "diagnostics:read"
)

Org-level permissions (require a global-scope role assignment).

View Source
const (
	PermContainersList       = "containers:list"
	PermContainersRead       = "containers:read"
	PermContainersLogs       = "containers:logs"
	PermContainersCreate     = "containers:create"
	PermContainersStart      = "containers:start"
	PermContainersStop       = "containers:stop"
	PermContainersRestart    = "containers:restart"
	PermContainersRedeploy   = "containers:redeploy"
	PermContainersKill       = "containers:kill"
	PermContainersPause      = "containers:pause"
	PermContainersDelete     = "containers:delete"
	PermContainersExec       = "containers:exec"
	PermContainersAutoUpdate = "containers:autoupdate"

	PermProjectsList    = "projects:list"
	PermProjectsRead    = "projects:read"
	PermProjectsLogs    = "projects:logs"
	PermProjectsCreate  = "projects:create"
	PermProjectsUpdate  = "projects:update"
	PermProjectsDeploy  = "projects:deploy"
	PermProjectsDown    = "projects:down"
	PermProjectsRestart = "projects:restart"
	PermProjectsDelete  = "projects:delete"
	PermProjectsArchive = "projects:archive"

	PermImagesList   = "images:list"
	PermImagesRead   = "images:read"
	PermImagesPull   = "images:pull"
	PermImagesPush   = "images:push"
	PermImagesBuild  = "images:build"
	PermImagesTag    = "images:tag"
	PermImagesCommit = "images:commit"
	PermImagesPrune  = "images:prune"
	PermImagesDelete = "images:delete"
	PermImagesUpload = "images:upload"

	PermVolumesList   = "volumes:list"
	PermVolumesRead   = "volumes:read"
	PermVolumesCreate = "volumes:create"
	PermVolumesDelete = "volumes:delete"
	PermVolumesPrune  = "volumes:prune"
	PermVolumesBrowse = "volumes:browse"
	PermVolumesUpload = "volumes:upload"
	PermVolumesBackup = "volumes:backup"

	PermNetworksList   = "networks:list"
	PermNetworksRead   = "networks:read"
	PermNetworksCreate = "networks:create"
	PermNetworksDelete = "networks:delete"
	PermNetworksPrune  = "networks:prune"

	PermSwarmRead         = "swarm:read"
	PermSwarmInit         = "swarm:init"
	PermSwarmJoin         = "swarm:join"
	PermSwarmLeave        = "swarm:leave"
	PermSwarmSpec         = "swarm:spec"
	PermSwarmNodes        = "swarm:nodes"
	PermSwarmServices     = "swarm:services"
	PermSwarmServicesLogs = "swarm:services:logs"
	PermSwarmStacks       = "swarm:stacks"
	PermSwarmConfigs      = "swarm:configs"
	PermSwarmSecrets      = "swarm:secrets"
	PermSwarmUnlock       = "swarm:unlock"

	PermGitOpsList   = "gitops:list"
	PermGitOpsRead   = "gitops:read"
	PermGitOpsCreate = "gitops:create"
	PermGitOpsUpdate = "gitops:update"
	PermGitOpsDelete = "gitops:delete"
	PermGitOpsSync   = "gitops:sync"
	// PermGitOpsLifecycle gates configuring a sync's pre-deploy lifecycle hook
	// (script + runner image + env + host bind mounts + network mode), which is
	// effectively arbitrary code execution on the host. It is intentionally
	// separate from gitops:create / gitops:update so that holding those broader
	// permissions (e.g. the built-in Editor role) does not by itself grant the
	// ability to author hooks; seeded only into the Admin built-in role.
	PermGitOpsLifecycle = "gitops:lifecycle"

	PermWebhooksList   = "webhooks:list"
	PermWebhooksCreate = "webhooks:create"
	PermWebhooksUpdate = "webhooks:update"
	PermWebhooksDelete = "webhooks:delete"

	PermJobsManage          = "jobs:manage"
	PermNotificationsManage = "notifications:manage"
	PermDashboardRead       = "dashboard:read"

	PermSystemRead    = "system:read"
	PermSystemPrune   = "system:prune"
	PermSystemUpgrade = "system:upgrade"

	PermImageUpdatesRead  = "image-updates:read"
	PermImageUpdatesCheck = "image-updates:check"

	PermVulnsRead   = "vulnerabilities:read"
	PermVulnsScan   = "vulnerabilities:scan"
	PermVulnsManage = "vulnerabilities:manage"

	PermBuildWorkspacesManage = "build-workspaces:manage"

	PermActivitiesRead   = "activities:read"
	PermActivitiesCancel = "activities:cancel"
	PermActivitiesDelete = "activities:delete"
)

Env-scoped permissions (resolved against the {id} env ID in the path).

View Source
const MetaRequiredPermission = "arcane:requiredPermission"

MetaRequiredPermission is the huma.Operation.Metadata key under which the environment-scoped registration helper records the permission an operation requires. The remote environment proxy reads this metadata (via the OpenAPI document) to enforce the same permission locally before forwarding a request to an agent.

Variables

This section is empty.

Functions

func AllPermissions

func AllPermissions() []string

AllPermissions returns every recognized permission constant. Used to seed the Admin built-in role and to validate role definitions.

func BuiltInDeployerPermissions

func BuiltInDeployerPermissions() []string

BuiltInDeployerPermissions returns the permission set for the Deployer built-in role: container/project lifecycle and read-only on everything else. Cannot create or delete resources; cannot manage settings/users/roles/keys.

func BuiltInEditorPermissions

func BuiltInEditorPermissions() []string

BuiltInEditorPermissions returns the permission set for the Editor built-in role: read+write on Docker resources and read on most org-level resources. Excludes user/role/key management and settings writes.

func BuiltInMonitorPermissions

func BuiltInMonitorPermissions() []string

BuiltInMonitorPermissions returns the permission set for the Monitor built-in role: observability-only access — read Docker resources, view logs, dashboards, and events. No mutations, no exec, no user/role/settings access.

func BuiltInNoShellEditorPermissions

func BuiltInNoShellEditorPermissions() []string

BuiltInNoShellEditorPermissions returns the Editor permission set minus PermContainersExec. For teams that want full Docker management but no interactive shell access into running containers.

func BuiltInViewerPermissions

func BuiltInViewerPermissions() []string

BuiltInViewerPermissions returns the permission set for the Viewer built-in role: read-only access across every resource.

func CanAccessCustomizeCategory

func CanAccessCustomizeCategory(ps *PermissionSet, categoryID, selectedEnvID string) bool

CanAccessCustomizeCategory reports whether a customize category is reachable.

func CanAccessSettingsCategory

func CanAccessSettingsCategory(ps *PermissionSet, categoryID, selectedEnvID string) bool

CanAccessSettingsCategory reports whether a settings category is reachable for the selected environment.

func CanAccessSurface

func CanAccessSurface(ps *PermissionSet, surfaceID, selectedEnvID string) bool

CanAccessSurface evaluates backend-owned UI reachability metadata for the caller. It is for advisory UX only; middleware and handlers still enforce permissions on actual API requests.

func EnvIDFromPath

func EnvIDFromPath(path string) string

EnvIDFromPath extracts the environment ID from a Huma operation path of the form /environments/{id}/... Returns "" for paths without an env segment. Tolerates a leading /api prefix for safety, though the Huma group already strips it.

func IsEnvScoped

func IsEnvScoped(perm string) bool

IsEnvScoped reports whether the given permission is resolved against an environment ID. Returns false for permissions not in AllPermissions().

func IsKnownPermission

func IsKnownPermission(perm string) bool

IsKnownPermission reports whether perm matches any defined permission constant. Used to reject role definitions referencing unknown permissions.

func IsOrgLevel

func IsOrgLevel(perm string) bool

IsOrgLevel reports whether the given permission requires a globally-scoped role assignment (and applies only to org-level endpoints).

func TotalPermissionsCount

func TotalPermissionsCount() int

TotalPermissionsCount returns the number of distinct permission constants the package defines. Computed once at init; cheap to call repeatedly.

Types

type AccessSurface

type AccessSurface struct {
	ID            string
	Kind          string
	URL           string
	Label         string
	AccessMode    string
	MatchMode     string
	ScopeMode     string
	Permissions   []string
	Children      []string
	FallbackOrder int
}

AccessSurface describes one frontend-visible surface whose reachability is derived from backend-owned permission metadata. This is decision metadata for UX gating; backend middleware and service checks remain authoritative.

func AccessSurfaces

func AccessSurfaces() []AccessSurface

AccessSurfaces returns a defensive copy of every backend-owned access surface in stable evaluation order.

type PermissionCatalogAction

type PermissionCatalogAction struct {
	Key         string
	Permission  string
	Label       string
	Description string
}

PermissionCatalogAction describes one recognized permission.

type PermissionCatalogResource

type PermissionCatalogResource struct {
	Key     string
	Label   string
	Scope   string
	Actions []PermissionCatalogAction
}

PermissionCatalogResource describes a resource group in the permission catalog. It is the authz-owned source for permission ordering, scope, and display metadata used by API manifests and validation helpers.

func PermissionCatalog

func PermissionCatalog() []PermissionCatalogResource

PermissionCatalog returns a defensive copy of the full permission catalog.

type PermissionMatcher added in v2.1.0

type PermissionMatcher struct {
	// contains filtered or unexported fields
}

PermissionMatcher maps an HTTP method and environment-relative resource path to the permission required to perform it. It mirrors the per-operation RequirePermission checks enforced for the local environment so the remote environment proxy can enforce the same permission before forwarding a request to an agent (which itself runs with a sudo permission set and performs no authorization).

Paths registered and looked up are the resource suffix AFTER the /environments/{id} prefix, e.g. "/containers/{containerId}/start" for the REST API or "/ws/containers/{containerId}/terminal" for WebSocket streams.

The matcher is populated once during startup and is read-only afterwards, so concurrent Lookup calls during request serving need no synchronization.

func NewPermissionMatcher added in v2.1.0

func NewPermissionMatcher() *PermissionMatcher

NewPermissionMatcher returns an empty matcher.

func (*PermissionMatcher) Add added in v2.1.0

func (m *PermissionMatcher) Add(method, pathTemplate, perm string)

Add registers the permission required for method + pathTemplate. pathTemplate is the resource suffix after /environments/{id}. Path parameters may use either Huma's "{name}" or Echo's ":name" syntax; both are treated as single-segment wildcards.

func (*PermissionMatcher) AddPublic added in v2.1.0

func (m *PermissionMatcher) AddPublic(method, pathTemplate string)

AddPublic registers method + pathTemplate as a proxied route that requires no permission (an intentionally public endpoint). Lookup reports it as found with an empty permission string, which the proxy treats as "allow".

func (*PermissionMatcher) CollectFromHumaAPI added in v2.1.0

func (m *PermissionMatcher) CollectFromHumaAPI(humaAPI huma.API)

CollectFromHumaAPI walks every operation registered on humaAPI and records, for each environment-scoped operation, the permission it requires. The permission is read from the operation metadata set by the registration helper (see MetaRequiredPermission).

Operation paths are stored as the resource suffix after /environments/{id} (for example "/containers/{containerId}/start") so the remote environment proxy can match them against forwarded request paths.

func (*PermissionMatcher) Lookup added in v2.1.0

func (m *PermissionMatcher) Lookup(method, suffixPath string) (string, bool)

Lookup returns the permission required for method + suffixPath, where suffixPath is the resource path after /environments/{id} (for example "/containers/abc123/start"). When multiple templates match, the most specific one wins — a route with more literal (non-wildcard) segments is preferred, so static routes such as "/containers/counts" take precedence over parameterized routes such as "/containers/{containerId}".

The boolean result reports whether any route matched. Callers should treat a false result as "deny" for proxied resource paths.

type PermissionSet

type PermissionSet struct {
	Global map[string]struct{}
	PerEnv map[string]map[string]struct{}
	Sudo   bool
}

PermissionSet is the effective permission set for one caller in one request. Built once by the auth bridge and stashed in the request context.

Global permissions apply to every environment and to org-level endpoints. PerEnv permissions apply only when the caller is acting on that specific environment. Sudo bypasses all checks (used for the agent token path).

func EnvironmentPermissionSet added in v2.2.0

func EnvironmentPermissionSet(envID string) *PermissionSet

EnvironmentPermissionSet returns the effective permission set for a per-environment access token. It grants every environment-scoped permission only for envID and no global or sudo permissions.

func NewPermissionSet

func NewPermissionSet() *PermissionSet

NewPermissionSet builds an empty PermissionSet ready for population.

func SudoPermissionSet

func SudoPermissionSet() *PermissionSet

SudoPermissionSet returns a PermissionSet that allows every action. Used for the agent token path, which bypasses per-user permission resolution entirely.

func (*PermissionSet) AddEnv

func (ps *PermissionSet) AddEnv(envID string, perms ...string)

AddEnv grants `perms` scoped to envID.

func (*PermissionSet) AddGlobal

func (ps *PermissionSet) AddGlobal(perms ...string)

AddGlobal grants `perms` at global scope.

func (*PermissionSet) Allows

func (ps *PermissionSet) Allows(perm, envID string) bool

Allows reports whether the caller may perform `perm`. For env-scoped permissions, envID is the target environment's ID. For org-level permissions, pass envID = "" — only Global permissions count.

func (*PermissionSet) AllowsAny added in v2.4.0

func (ps *PermissionSet) AllowsAny(perm string) bool

AllowsAny reports whether the caller may perform perm in at least one effective scope. Global and sudo permissions satisfy the check immediately; otherwise every explicitly granted environment is considered.

func (*PermissionSet) IsGlobalAdmin

func (ps *PermissionSet) IsGlobalAdmin() bool

IsGlobalAdmin reports whether the caller holds enough global permissions to be considered an administrator. True for sudo callers and for callers whose Global set contains every defined permission. Used by the backward-compat IsAdminFromContext helper and by last-admin guards.

Implementation: first checks cardinality against TotalPermissionsCount() for a fast early exit, then walks AllPermissions() to confirm every known permission is present. This guards against a ps.Global that contains the right count but includes injected unknown permissions instead of all real ones.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL