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
- Variables
- func Categories() []string
- func CategoryDescription(id string) string
- func EnforcePermission(w http.ResponseWriter, r *http.Request, p Permission) (denied bool)
- func IsDangerous(p Permission) bool
- func IsKnown(p Permission) bool
- func RequestActor(ctx context.Context, fallback audit.Actor) audit.Actor
- func RequirePermission(p Permission) func(http.Handler) http.Handler
- func RoleGrantsWithin(caller Identity, requested RoleID) bool
- func RoleGrantsWithinResolved(caller Identity, requested RoleID, resolve RoleResolver) bool
- func SetIdentity(ctx context.Context, id Identity) context.Context
- type Identity
- type Permission
- type PermissionMeta
- type RoleDefinition
- type RoleID
- type RoleResolver
Constants ¶
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 ¶
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.
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 ¶
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
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:
- Identity has p? If not → 401 auth.required for an anonymous caller, 403 authz.permission_denied for an authenticated one.
- 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 ¶
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.
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 ¶
FromContext returns the Identity bound on the context, or an anonymous Identity if none is set.
func (Identity) AccountableUser ¶ added in v0.8.2
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
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.