auth

package
v0.8.4 Latest Latest
Warning

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

Go to latest
Published: Oct 9, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package auth provides RBAC: a codegen-typed permission registry, built-in roles, and the RequirePermission middleware that combines RBAC and license-gate checks in one pass.

Identity binding is performed by the internal/identity package's production binder (session cookie + Bearer JWT). This package owns only the Identity shape and the permission-enforcement logic.

Index

Constants

View Source
const APITokenPrefix = "owk_"

APITokenPrefix identifies an OpenWatch API service-account token in an Authorization: Bearer header. The identity binder routes bearer values carrying this prefix to the API-token authenticator (the rest are JWTs). "owk" = OpenWatch Key.

Variables

View Source
var BuiltInRoles = map[RoleID]RoleDefinition{
	RoleViewer: {
		ID:          RoleViewer,
		Description: `Read-only access across the platform`,
		IsBuiltIn:   true,
		Permissions: []Permission{
			AlertRead,
			AuditRead,
			AuthRead,
			BaselineRead,
			ComplianceRead,
			ExceptionRead,
			HostRead,
			IntegrationRead,
			LicenseRead,
			NotificationRead,
			PolicyRead,
			RemediationRead,
			RoleRead,
			ScanRead,
			ScanTemplateRead,
			SystemRead,
		},
	},
	RoleAuditor: {
		ID:          RoleAuditor,
		Description: `Read-only plus exception authority and audit export`,
		IsBuiltIn:   true,
		Permissions: []Permission{
			AlertRead,
			AuditExport,
			AuditRead,
			AuthRead,
			AuthWrite,
			BaselineRead,
			ComplianceRead,
			ExceptionApprove,
			ExceptionComment,
			ExceptionRead,
			ExceptionRequest,
			HostRead,
			IntegrationRead,
			LicenseRead,
			NotificationRead,
			PolicyRead,
			RemediationRead,
			ScanRead,
			ScanTemplateRead,
			SystemRead,
		},
	},
	RoleOpsLead: {
		ID:          RoleOpsLead,
		Description: `Day-to-day operations - hosts, scans, alerts`,
		IsBuiltIn:   true,
		Permissions: []Permission{
			AlertAcknowledge,
			AlertRead,
			AlertResolve,
			AuditRead,
			AuthRead,
			AuthWrite,
			BaselineRead,
			BaselineWrite,
			ComplianceRead,
			CredentialRead,
			ExceptionComment,
			ExceptionRead,
			ExceptionRequest,
			HostConnectivityCheck,
			HostIntelligenceRefresh,
			HostRead,
			HostWrite,
			IntegrationRead,
			LicenseRead,
			NotificationRead,
			NotificationTest,
			PolicyRead,
			RemediationExecute,
			RemediationRead,
			RemediationRequest,
			RemediationRollback,
			ScanCancel,
			ScanExecute,
			ScanRead,
			ScanTemplateRead,
			ScanTemplateWrite,
			SystemRead,
		},
	},
	RoleSecurityAdmin: {
		ID:          RoleSecurityAdmin,
		Description: `Full security operations including dangerous and license-gated actions`,
		IsBuiltIn:   true,
		Permissions: []Permission{
			AlertAcknowledge,
			AlertRead,
			AlertResolve,
			AlertWrite,
			AuditExport,
			AuditRead,
			AuthRead,
			AuthWrite,
			BaselineDelete,
			BaselineRead,
			BaselineWrite,
			ComplianceRead,
			CredentialDelete,
			CredentialRead,
			CredentialWrite,
			ExceptionApprove,
			ExceptionComment,
			ExceptionRead,
			ExceptionRequest,
			ExceptionRevoke,
			HostConnectivityCheck,
			HostDelete,
			HostIntelligenceRefresh,
			HostRead,
			HostWrite,
			IntegrationExecute,
			IntegrationRead,
			IntegrationWrite,
			LicenseInstall,
			LicenseRead,
			NotificationDelete,
			NotificationRead,
			NotificationTest,
			NotificationWrite,
			PolicyInstall,
			PolicyRead,
			PolicyReload,
			RemediationApprove,
			RemediationExecute,
			RemediationRead,
			RemediationRequest,
			RemediationRollback,
			ScanCancel,
			ScanExecute,
			ScanRead,
			ScanTemplateDelete,
			ScanTemplateRead,
			ScanTemplateWrite,
			SystemAuthPolicyRead,
			SystemAuthPolicyWrite,
			SystemRead,
			TokenDelete,
			TokenRead,
			TokenWrite,
			UserRead,
			UserWrite,
		},
	},
	RoleAdmin: {
		ID:          RoleAdmin,
		Description: `Full system administration`,
		IsBuiltIn:   true,
		Permissions: []Permission{
			AdminRetentionPolicy,
			AdminRoleManage,
			AdminSsoProvider,
			AdminSystemSetting,
			AdminUserManage,
			AlertAcknowledge,
			AlertRead,
			AlertResolve,
			AlertWrite,
			AuditExport,
			AuditRead,
			AuthRead,
			AuthWrite,
			BaselineDelete,
			BaselineRead,
			BaselineWrite,
			ComplianceRead,
			CredentialDelete,
			CredentialRead,
			CredentialWrite,
			ExceptionApprove,
			ExceptionComment,
			ExceptionRead,
			ExceptionRequest,
			ExceptionRevoke,
			HostConnectivityCheck,
			HostDelete,
			HostIntelligenceRefresh,
			HostRead,
			HostWrite,
			IntegrationExecute,
			IntegrationRead,
			IntegrationWrite,
			LicenseInstall,
			LicenseRead,
			LicenseRevoke,
			NotificationDelete,
			NotificationRead,
			NotificationTest,
			NotificationWrite,
			PolicyInstall,
			PolicyRead,
			PolicyReload,
			RemediationApprove,
			RemediationExecute,
			RemediationRead,
			RemediationRequest,
			RemediationRollback,
			RoleAssign,
			RoleRead,
			RoleWrite,
			ScanCancel,
			ScanExecute,
			ScanRead,
			ScanTemplateDelete,
			ScanTemplateRead,
			ScanTemplateWrite,
			SystemAuthPolicyRead,
			SystemAuthPolicyWrite,
			SystemConfigWrite,
			SystemRead,
			TokenDelete,
			TokenRead,
			TokenWrite,
			UserDelete,
			UserRead,
			UserWrite,
		},
	},
}

BuiltInRoles maps every built-in RoleID to its definition with category wildcards expanded at codegen time. Runtime callers do not need to interpret wildcards.

View Source
var Permissions = map[Permission]PermissionMeta{
	AuthRead: {
		ID:          AuthRead,
		Category:    "auth",
		Description: `View own profile, sessions, MFA status`,
		Dangerous:   false,
	},
	AuthWrite: {
		ID:          AuthWrite,
		Category:    "auth",
		Description: `Change own password, manage own MFA, revoke own sessions`,
		Dangerous:   false,
	},
	UserRead: {
		ID:          UserRead,
		Category:    "user",
		Description: `List users, view user details`,
		Dangerous:   false,
	},
	UserWrite: {
		ID:          UserWrite,
		Category:    "user",
		Description: `Create or update user records (excluding role assignment)`,
		Dangerous:   false,
	},
	UserDelete: {
		ID:          UserDelete,
		Category:    "user",
		Description: `Delete user accounts`,
		Dangerous:   true,
	},
	HostRead: {
		ID:          HostRead,
		Category:    "host",
		Description: `View host details, list hosts, view host audit history`,
		Dangerous:   false,
	},
	HostWrite: {
		ID:          HostWrite,
		Category:    "host",
		Description: `Create, update, bulk-import hosts`,
		Dangerous:   false,
	},
	HostDelete: {
		ID:          HostDelete,
		Category:    "host",
		Description: `Delete a host record and its scan history pointer`,
		Dangerous:   true,
	},
	HostConnectivityCheck: {
		ID:          HostConnectivityCheck,
		Category:    "host",
		Description: `Trigger an on-demand SSH/ping connectivity check`,
		Dangerous:   false,
	},
	HostIntelligenceRefresh: {
		ID:          HostIntelligenceRefresh,
		Category:    "host",
		Description: `Trigger an on-demand server-intelligence collection`,
		Dangerous:   false,
	},
	CredentialRead: {
		ID:          CredentialRead,
		Category:    "credential",
		Description: `List credentials (metadata only — secrets never returned), resolve credential for a host`,
		Dangerous:   false,
	},
	CredentialWrite: {
		ID:          CredentialWrite,
		Category:    "credential",
		Description: `Create credentials (system or host scope), set is_default`,
		Dangerous:   false,
	},
	CredentialDelete: {
		ID:          CredentialDelete,
		Category:    "credential",
		Description: `Soft-delete credentials (set is_active=false)`,
		Dangerous:   true,
	},
	ScanRead: {
		ID:          ScanRead,
		Category:    "scan",
		Description: `View scans, scan sessions, and scan results`,
		Dangerous:   false,
	},
	ScanExecute: {
		ID:          ScanExecute,
		Category:    "scan",
		Description: `Queue or start a scan against a host or fleet`,
		Dangerous:   false,
	},
	ScanCancel: {
		ID:          ScanCancel,
		Category:    "scan",
		Description: `Cancel a running or queued scan`,
		Dangerous:   false,
	},
	ScanTemplateRead: {
		ID:          ScanTemplateRead,
		Category:    "scan_template",
		Description: `List and view scan templates`,
		Dangerous:   false,
	},
	ScanTemplateWrite: {
		ID:          ScanTemplateWrite,
		Category:    "scan_template",
		Description: `Create or update scan templates`,
		Dangerous:   false,
	},
	ScanTemplateDelete: {
		ID:          ScanTemplateDelete,
		Category:    "scan_template",
		Description: `Delete a scan template (does not affect historical scans)`,
		Dangerous:   true,
	},
	ComplianceRead: {
		ID:          ComplianceRead,
		Category:    "compliance",
		Description: `View compliance state, drift detection results, posture queries`,
		Dangerous:   false,
	},
	BaselineRead: {
		ID:          BaselineRead,
		Category:    "baseline",
		Description: `View compliance baselines`,
		Dangerous:   false,
	},
	BaselineWrite: {
		ID:          BaselineWrite,
		Category:    "baseline",
		Description: `Establish or update a compliance baseline`,
		Dangerous:   false,
	},
	BaselineDelete: {
		ID:          BaselineDelete,
		Category:    "baseline",
		Description: `Clear an existing baseline (resets drift detection)`,
		Dangerous:   true,
	},
	ExceptionRead: {
		ID:          ExceptionRead,
		Category:    "exception",
		Description: `View exception requests and active exceptions`,
		Dangerous:   false,
	},
	ExceptionRequest: {
		ID:          ExceptionRequest,
		Category:    "exception",
		Description: `Submit a new exception request`,
		Dangerous:   false,
	},
	ExceptionComment: {
		ID:          ExceptionComment,
		Category:    "exception",
		Description: `Add comments to exception requests`,
		Dangerous:   false,
	},
	ExceptionApprove: {
		ID:          ExceptionApprove,
		Category:    "exception",
		Description: `Approve or reject exception requests`,
		Dangerous:   false,
	},
	ExceptionRevoke: {
		ID:          ExceptionRevoke,
		Category:    "exception",
		Description: `Revoke an active exception before its expiry`,
		Dangerous:   true,
	},
	AlertRead: {
		ID:          AlertRead,
		Category:    "alert",
		Description: `View alerts and alert history`,
		Dangerous:   false,
	},
	AlertAcknowledge: {
		ID:          AlertAcknowledge,
		Category:    "alert",
		Description: `Acknowledge an alert (suppresses repeat notifications)`,
		Dangerous:   false,
	},
	AlertResolve: {
		ID:          AlertResolve,
		Category:    "alert",
		Description: `Mark an alert as resolved`,
		Dangerous:   false,
	},
	AlertWrite: {
		ID:          AlertWrite,
		Category:    "alert",
		Description: `Create or modify alert thresholds`,
		Dangerous:   false,
	},
	NotificationRead: {
		ID:          NotificationRead,
		Category:    "notification",
		Description: `View notification channels and delivery history`,
		Dangerous:   false,
	},
	NotificationWrite: {
		ID:          NotificationWrite,
		Category:    "notification",
		Description: `Create or update notification channels`,
		Dangerous:   false,
	},
	NotificationDelete: {
		ID:          NotificationDelete,
		Category:    "notification",
		Description: `Delete notification channels`,
		Dangerous:   true,
	},
	NotificationTest: {
		ID:          NotificationTest,
		Category:    "notification",
		Description: `Send a test notification through a channel`,
		Dangerous:   false,
	},
	TokenRead: {
		ID:          TokenRead,
		Category:    "token",
		Description: `View API tokens (metadata only; secrets are never shown)`,
		Dangerous:   false,
	},
	TokenWrite: {
		ID:          TokenWrite,
		Category:    "token",
		Description: `Create API tokens for automation`,
		Dangerous:   false,
	},
	TokenDelete: {
		ID:          TokenDelete,
		Category:    "token",
		Description: `Revoke API tokens`,
		Dangerous:   true,
	},
	LicenseRead: {
		ID:          LicenseRead,
		Category:    "license",
		Description: `View license status, features, and history`,
		Dangerous:   false,
	},
	LicenseInstall: {
		ID:          LicenseInstall,
		Category:    "license",
		Description: `Install or replace the active license file`,
		Dangerous:   true,
	},
	LicenseRevoke: {
		ID:          LicenseRevoke,
		Category:    "license",
		Description: `Mark the current license as revoked (forces Free tier)`,
		Dangerous:   true,
	},
	PolicyRead: {
		ID:          PolicyRead,
		Category:    "policy",
		Description: `View active policies, policy_history, and reload outcomes`,
		Dangerous:   false,
	},
	PolicyReload: {
		ID:          PolicyReload,
		Category:    "policy",
		Description: `Trigger a policy reload (SIGHUP equivalent via API)`,
		Dangerous:   false,
	},
	PolicyInstall: {
		ID:          PolicyInstall,
		Category:    "policy",
		Description: `Install a new policy version (writes a signed file)`,
		Dangerous:   true,
	},
	RemediationRead: {
		ID:          RemediationRead,
		Category:    "remediation",
		Description: `View remediation requests and history`,
		Dangerous:   false,
	},
	RemediationRequest: {
		ID:          RemediationRequest,
		Category:    "remediation",
		Description: `Submit a remediation request`,
		Dangerous:   false,
	},
	RemediationApprove: {
		ID:          RemediationApprove,
		Category:    "remediation",
		Description: `Approve or reject remediation requests`,
		Dangerous:   false,
	},
	RemediationExecute: {
		ID:          RemediationExecute,
		Category:    "remediation",
		Description: `Execute an approved single-rule remediation against a host (free core)`,
		Dangerous:   true,
	},
	RemediationRollback: {
		ID:          RemediationRollback,
		Category:    "remediation",
		Description: `Roll back a previously executed remediation (free core)`,
		Dangerous:   true,
	},
	IntegrationRead: {
		ID:          IntegrationRead,
		Category:    "integration",
		Description: `View configured plugins and webhook endpoints`,
		Dangerous:   false,
	},
	IntegrationWrite: {
		ID:          IntegrationWrite,
		Category:    "integration",
		Description: `Configure plugins and webhook endpoints`,
		Dangerous:   false,
	},
	IntegrationExecute: {
		ID:          IntegrationExecute,
		Category:    "integration",
		Description: `Manually invoke a plugin (out-of-band of the normal trigger)`,
		Dangerous:   false,
	},
	AuditRead: {
		ID:          AuditRead,
		Category:    "audit",
		Description: `Query the audit log via filters or DSL`,
		Dangerous:   false,
	},
	AuditExport: {
		ID:          AuditExport,
		Category:    "audit",
		Description: `Export the audit log to file (large extracts)`,
		Dangerous:   false,
	},
	SystemRead: {
		ID:          SystemRead,
		Category:    "system",
		Description: `View system health, version, and non-secret configuration`,
		Dangerous:   false,
	},
	SystemConfigWrite: {
		ID:          SystemConfigWrite,
		Category:    "system",
		Description: `Modify runtime system configuration`,
		Dangerous:   true,
	},
	SystemAuthPolicyRead: {
		ID:          SystemAuthPolicyRead,
		Category:    "system",
		Description: `View the workspace authentication policy (require-MFA, session timeouts)`,
		Dangerous:   false,
	},
	SystemAuthPolicyWrite: {
		ID:          SystemAuthPolicyWrite,
		Category:    "system",
		Description: `Modify the workspace authentication policy (require-MFA, session timeouts)`,
		Dangerous:   true,
	},
	RoleRead: {
		ID:          RoleRead,
		Category:    "role",
		Description: `View roles and built-in role definitions`,
		Dangerous:   false,
	},
	RoleWrite: {
		ID:          RoleWrite,
		Category:    "role",
		Description: `Create or update custom roles (Stage 2)`,
		Dangerous:   true,
	},
	RoleAssign: {
		ID:          RoleAssign,
		Category:    "role",
		Description: `Assign roles to users`,
		Dangerous:   true,
	},
	AdminUserManage: {
		ID:          AdminUserManage,
		Category:    "admin",
		Description: `Manage users including role assignments`,
		Dangerous:   true,
	},
	AdminRoleManage: {
		ID:          AdminRoleManage,
		Category:    "admin",
		Description: `Create, update, delete custom roles; assign roles to users`,
		Dangerous:   true,
	},
	AdminRetentionPolicy: {
		ID:          AdminRetentionPolicy,
		Category:    "admin",
		Description: `Modify audit/scan/log retention policies`,
		Dangerous:   true,
	},
	AdminSsoProvider: {
		ID:          AdminSsoProvider,
		Category:    "admin",
		Description: `Configure SSO providers (OIDC, SAML)`,
		Dangerous:   true,
	},
	AdminSystemSetting: {
		ID:          AdminSystemSetting,
		Category:    "admin",
		Description: `Modify high-impact system settings (TLS, listener, FIPS mode hint)`,
		Dangerous:   true,
	},
}

Permissions maps every active permission id to its registry entry.

Functions

func Categories

func Categories() []string

Categories returns the registered category ids in declaration order.

func CategoryDescription

func CategoryDescription(id string) string

CategoryDescription returns the description for a category id, or "" if unknown.

func EnforcePermission

func EnforcePermission(w http.ResponseWriter, r *http.Request, p Permission) (denied bool)

EnforcePermission performs the same RBAC check as RequirePermission but as a function callable inside an oapi-codegen-generated handler. Returns true if the response is already written (the handler should return immediately).

This checks RBAC only. Entitlement is a separate, later check: a handler that also needs a paid feature calls license.EnforceFeature after this returns false. Keeping the two apart is what lets one permission cover both a free per-host route and a paid fleet-scale route.

func IsDangerous

func IsDangerous(p Permission) bool

IsDangerous reports whether p is registered with dangerous=true.

func IsKnown

func IsKnown(p Permission) bool

IsKnown reports whether p is a registered (non-deprecated) permission.

func RequestActor added in v0.8.2

func RequestActor(ctx context.Context, fallback audit.Actor) audit.Actor

RequestActor returns the audit actor for the request on ctx: the bound identity's AuditActor (a token as itself, a user as the user). When no identity is bound, which happens only on a direct call outside a request (a worker, a test), it returns fallback. Services that record an accountable user in their own columns use this for the audit actor, so a token's action is never attributed to its owner. Spec system-audit-emission C-12; bugs/OW-100.

func RequirePermission

func RequirePermission(p Permission) func(http.Handler) http.Handler

RequirePermission returns a chi middleware that enforces RBAC for the given permission. The check order is:

  1. Identity has p? If not → 401 auth.required for an anonymous caller, 403 authz.permission_denied for an authenticated one.
  2. Otherwise the inner handler runs.

There is no license stage here. A route that needs an entitlement declares x-required-feature in api/openapi.yaml and its handler calls license.EnforceFeature, which produces the 402. RBAC still fails first, because the handler cannot run until this middleware passes. Never leak the entitlement state to a caller who lacks the permission anyway.

Spec system-rbac AC-08, AC-09, AC-10, AC-11.

func RoleGrantsWithin

func RoleGrantsWithin(caller Identity, requested RoleID) bool

RoleGrantsWithin reports whether every permission conferred by the requested role is also held by the caller. It is the anti-privilege- escalation primitive: a caller may never grant, via an API token, a role assignment, or a custom role, a permission they do not themselves hold.

It resolves BUILT-IN roles only, and DENIES anything else.

This function previously returned true (allow) for any role missing from BuiltInRoles, on the reasoning that an unknown role confers nothing and a downstream existence check would reject it. That reasoning does not hold for custom roles: they exist, they are not in BuiltInRoles, and the downstream check accepts them. The guard therefore passed for every custom role, which is fail-open on the one code path whose entire job is to fail closed.

Prefer RoleGrantsWithinResolved wherever custom roles are legitimate, so a real custom role is checked on its actual permissions rather than refused.

func RoleGrantsWithinResolved added in v0.7.0

func RoleGrantsWithinResolved(caller Identity, requested RoleID, resolve RoleResolver) bool

RoleGrantsWithinResolved is RoleGrantsWithin with custom-role support. resolve supplies the permission set for a role that is not built in; a nil resolver, or a resolver reporting the role unknown, DENIES.

Fail-closed is deliberate and load-bearing here. The alternative, treating an unresolvable role as harmless because nothing currently enforces its permissions, is what made this a latent escalation: the day custom-role enforcement is wired, every previously-waved-through grant becomes real.

func SetIdentity

func SetIdentity(ctx context.Context, id Identity) context.Context

SetIdentity returns a derived context with the identity attached.

Types

type Identity

type Identity struct {
	// ID is a stable string identifier for the calling principal. Stage 0
	// uses the role name itself; Stage 2 uses the user/account UUID.
	ID string

	// UserID is the user account answerable for the request: the signed-in
	// user on a session cookie or Bearer JWT, and the token's owner
	// (api_tokens.created_by) on an API token. For a token, ID is the
	// token's own id, which is not a users row, so a column that references
	// users(id) takes UserID and never ID. uuid.Nil means none is bound.
	// Read it through AccountableUser. Spec system-api-tokens C-05;
	// bugs/OW-097.
	UserID uuid.UUID

	// IsAPIToken is true when the request authenticated with an owk_ API
	// token. ID is then the token's id and UserID its owner. Permissions
	// still come from the token's own role (RoleID), never the owner's.
	// Endpoints scoped to the calling user's own account refuse a token.
	// Spec system-api-tokens C-06, C-07; bugs/OW-098.
	IsAPIToken bool

	// RoleID is the built-in role granting the effective permissions. Stage 2
	// replaces this with a union of roles, but the spec only requires a
	// single role concept for Day 8.
	RoleID RoleID

	// IsAnonymous is true when no role was bound. The middleware uses this
	// to short-circuit the permission lookup; an anonymous identity has
	// no permissions.
	IsAnonymous bool

	// Grants is the stored permission set of a CUSTOM role (one that
	// BuiltInRoles does not resolve), attached by the identity binder at
	// bind time from the roles table. It is consulted only when RoleID is
	// not built in; a built-in role's permissions come from the registry
	// and Grants is ignored. nil means "nothing resolved": a custom role
	// that is absent or whose lookup failed binds with no permissions.
	//
	// Spec system-rbac C-11.
	Grants []Permission
}

Identity is the calling user's identity carried on the request context. Bound by the production identity binder (session cookie or Bearer JWT) — see internal/identity/binder.go.

func FromContext

func FromContext(ctx context.Context) Identity

FromContext returns the Identity bound on the context, or an anonymous Identity if none is set.

func (Identity) AccountableUser added in v0.8.2

func (i Identity) AccountableUser() (uuid.UUID, bool)

AccountableUser returns the user account answerable for the request, and false when none is bound: an anonymous identity, or one built without a UserID. A caller that records a requester refuses on false rather than falling back to ID, because ID names a token on the token arm. Spec system-api-tokens C-05; bugs/OW-097.

func (Identity) AuditActor added in v0.8.2

func (i Identity) AuditActor() audit.Actor

AuditActor returns who the audit trail names for this identity: the token itself (api_key and the token's id) for an API token, the user for a session, and anonymous when nothing is bound. It is never the token's owner; that is AccountableUser, a different fact recorded in a service's own columns. Spec system-audit-emission C-12; bugs/OW-100.

func (Identity) HasPermission

func (i Identity) HasPermission(p Permission) bool

HasPermission returns true iff this identity's role grants p. Anonymous identities have no permissions.

Spec system-rbac AC-05.

func (Identity) Permissions

func (i Identity) Permissions() []Permission

Permissions returns the effective permission list for this identity in registry order. Empty slice for anonymous.

Spec system-rbac AC-13.

type Permission

type Permission string

Permission is a stable, resource:action identifier.

const (
	// View own profile, sessions, MFA status
	AuthRead Permission = "auth:read"
	// Change own password, manage own MFA, revoke own sessions
	AuthWrite Permission = "auth:write"
	// List users, view user details
	UserRead Permission = "user:read"
	// Create or update user records (excluding role assignment)
	UserWrite Permission = "user:write"
	// Delete user accounts
	UserDelete Permission = "user:delete"
	// View host details, list hosts, view host audit history
	HostRead Permission = "host:read"
	// Create, update, bulk-import hosts
	HostWrite Permission = "host:write"
	// Delete a host record and its scan history pointer
	HostDelete Permission = "host:delete"
	// Trigger an on-demand SSH/ping connectivity check
	HostConnectivityCheck Permission = "host:connectivity_check"
	// Trigger an on-demand server-intelligence collection
	HostIntelligenceRefresh Permission = "host:intelligence_refresh"
	// List credentials (metadata only — secrets never returned), resolve credential for a host
	CredentialRead Permission = "credential:read"
	// Create credentials (system or host scope), set is_default
	CredentialWrite Permission = "credential:write"
	// Soft-delete credentials (set is_active=false)
	CredentialDelete Permission = "credential:delete"
	// View scans, scan sessions, and scan results
	ScanRead Permission = "scan:read"
	// Queue or start a scan against a host or fleet
	ScanExecute Permission = "scan:execute"
	// Cancel a running or queued scan
	ScanCancel Permission = "scan:cancel"
	// List and view scan templates
	ScanTemplateRead Permission = "scan_template:read"
	// Create or update scan templates
	ScanTemplateWrite Permission = "scan_template:write"
	// Delete a scan template (does not affect historical scans)
	ScanTemplateDelete Permission = "scan_template:delete"
	// View compliance state, drift detection results, posture queries
	ComplianceRead Permission = "compliance:read"
	// View compliance baselines
	BaselineRead Permission = "baseline:read"
	// Establish or update a compliance baseline
	BaselineWrite Permission = "baseline:write"
	// Clear an existing baseline (resets drift detection)
	BaselineDelete Permission = "baseline:delete"
	// View exception requests and active exceptions
	ExceptionRead Permission = "exception:read"
	// Submit a new exception request
	ExceptionRequest Permission = "exception:request"
	// Add comments to exception requests
	ExceptionComment Permission = "exception:comment"
	// Approve or reject exception requests
	ExceptionApprove Permission = "exception:approve"
	// Revoke an active exception before its expiry
	ExceptionRevoke Permission = "exception:revoke"
	// View alerts and alert history
	AlertRead Permission = "alert:read"
	// Acknowledge an alert (suppresses repeat notifications)
	AlertAcknowledge Permission = "alert:acknowledge"
	// Mark an alert as resolved
	AlertResolve Permission = "alert:resolve"
	// Create or modify alert thresholds
	AlertWrite Permission = "alert:write"
	// View notification channels and delivery history
	NotificationRead Permission = "notification:read"
	// Create or update notification channels
	NotificationWrite Permission = "notification:write"
	// Delete notification channels
	NotificationDelete Permission = "notification:delete"
	// Send a test notification through a channel
	NotificationTest Permission = "notification:test"
	// View API tokens (metadata only; secrets are never shown)
	TokenRead Permission = "token:read"
	// Create API tokens for automation
	TokenWrite Permission = "token:write"
	// Revoke API tokens
	TokenDelete Permission = "token:delete"
	// View license status, features, and history
	LicenseRead Permission = "license:read"
	// Install or replace the active license file
	LicenseInstall Permission = "license:install"
	// Mark the current license as revoked (forces Free tier)
	LicenseRevoke Permission = "license:revoke"
	// View active policies, policy_history, and reload outcomes
	PolicyRead Permission = "policy:read"
	// Trigger a policy reload (SIGHUP equivalent via API)
	PolicyReload Permission = "policy:reload"
	// Install a new policy version (writes a signed file)
	PolicyInstall Permission = "policy:install"
	// View remediation requests and history
	RemediationRead Permission = "remediation:read"
	// Submit a remediation request
	RemediationRequest Permission = "remediation:request"
	// Approve or reject remediation requests
	RemediationApprove Permission = "remediation:approve"
	// Execute an approved single-rule remediation against a host (free core)
	RemediationExecute Permission = "remediation:execute"
	// Roll back a previously executed remediation (free core)
	RemediationRollback Permission = "remediation:rollback"
	// View configured plugins and webhook endpoints
	IntegrationRead Permission = "integration:read"
	// Configure plugins and webhook endpoints
	IntegrationWrite Permission = "integration:write"
	// Manually invoke a plugin (out-of-band of the normal trigger)
	IntegrationExecute Permission = "integration:execute"
	// Query the audit log via filters or DSL
	AuditRead Permission = "audit:read"
	// Export the audit log to file (large extracts)
	AuditExport Permission = "audit:export"
	// View system health, version, and non-secret configuration
	SystemRead Permission = "system:read"
	// Modify runtime system configuration
	SystemConfigWrite Permission = "system:config_write"
	// View the workspace authentication policy (require-MFA, session timeouts)
	SystemAuthPolicyRead Permission = "system:auth_policy_read"
	// Modify the workspace authentication policy (require-MFA, session timeouts)
	SystemAuthPolicyWrite Permission = "system:auth_policy_write"
	// View roles and built-in role definitions
	RoleRead Permission = "role:read"
	// Create or update custom roles (Stage 2)
	RoleWrite Permission = "role:write"
	// Assign roles to users
	RoleAssign Permission = "role:assign"
	// Manage users including role assignments
	AdminUserManage Permission = "admin:user_manage"
	// Create, update, delete custom roles; assign roles to users
	AdminRoleManage Permission = "admin:role_manage"
	// Modify audit/scan/log retention policies
	AdminRetentionPolicy Permission = "admin:retention_policy"
	// Configure SSO providers (OIDC, SAML)
	AdminSsoProvider Permission = "admin:sso_provider"
	// Modify high-impact system settings (TLS, listener, FIPS mode hint)
	AdminSystemSetting Permission = "admin:system_setting"
)

Permission constants. Hand-typed permission strings are forbidden by lint; use these constants in handler middleware and tests.

func AllPermissions

func AllPermissions() []Permission

AllPermissions returns every active permission id in registry order.

func PermissionsWithin added in v0.7.0

func PermissionsWithin(caller Identity, requested []Permission) []Permission

PermissionsWithin reports whether every permission in requested is held by the caller. It is the same subset rule as RoleGrantsWithin applied to a raw permission list, for the custom-role CREATION path: a caller must not be able to author a role granting more than they themselves hold, since authoring it and then assigning it is a two-step escalation that neither step alone would catch.

Returns the permissions the caller lacks, so the API can name them.

type PermissionMeta

type PermissionMeta struct {
	ID          Permission
	Category    string
	Description string
	Dangerous   bool
}

PermissionMeta is the per-permission metadata from the registry.

There is no license-gate field. An operation that needs an entitlement carries x-required-feature on its route in api/openapi.yaml, and the handler enforces it with license.EnforceFeature. Tiering is by scope: one host is free, the same vocabulary at fleet scale is paid, and both use the same permission. Only the route can tell them apart.

type RoleDefinition

type RoleDefinition struct {
	ID          RoleID
	Description string
	Permissions []Permission
	IsBuiltIn   bool
}

RoleDefinition is a role and its effective permission set.

type RoleID

type RoleID string

RoleID is the stable identifier of a built-in or custom role.

const (
	// Read-only access across the platform
	RoleViewer RoleID = "viewer"
	// Read-only plus exception authority and audit export
	RoleAuditor RoleID = "auditor"
	// Day-to-day operations - hosts, scans, alerts
	RoleOpsLead RoleID = "ops_lead"
	// Full security operations including dangerous and license-gated actions
	RoleSecurityAdmin RoleID = "security_admin"
	// Full system administration
	RoleAdmin RoleID = "admin"
)

Built-in role constants. Custom roles created at runtime use ad-hoc RoleID values; the registry only ships built-in role definitions.

func BuiltInRoleIDs

func BuiltInRoleIDs() []RoleID

BuiltInRoleIDs returns the IDs of the built-in roles in declaration order.

func RolesWithPermission

func RolesWithPermission(p Permission) []RoleID

RolesWithPermission returns the built-in role IDs whose definition grants p, in declaration order. It is the recipient-resolution primitive for permission-scoped fan-out (e.g. "every role that can approve an exception"): callers translate a permission into the set of roles, then query user_roles for the holders. An unknown/empty permission yields no roles.

type RoleResolver added in v0.7.0

type RoleResolver func(RoleID) (perms []Permission, found bool, err error)

RoleResolver answers what a non-built-in role confers. It exists so the anti-escalation guard can evaluate a CUSTOM role (whose permission set lives in the roles table) without internal/auth importing a database layer.

The three states are distinct and the distinction is load-bearing:

found=true,  err=nil  the role exists; perms is its permission set
found=false, err=nil  the role PROVABLY does not exist
err != nil            cannot determine (query failed, service missing)

"Provably absent" and "cannot determine" must never be collapsed. An absent role confers nothing and is rejected downstream as bad input, so the guard lets it through to produce the correct 400. An indeterminate answer is a denial, because the guard cannot show the grant is within the caller's authority.

Jump to

Keyboard shortcuts

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