Phase 14: Full WebAuthn support for passwordless passkey login and hardware security key 2FA. - go-webauthn/webauthn v0.16.1 dependency - WebAuthnConfig with RPID/RPOrigin/DisplayName validation - Migration 000009: webauthn_credentials table - DB CRUD with ownership checks and admin operations - internal/webauthn adapter: encrypt/decrypt at rest with AES-256-GCM - REST: register begin/finish, login begin/finish, list, delete - Web UI: profile enrollment, login passkey button, admin management - gRPC: ListWebAuthnCredentials, RemoveWebAuthnCredential RPCs - mciasdb: webauthn list/delete/reset subcommands - OpenAPI: 6 new endpoints, WebAuthnCredentialInfo schema - Policy: self-service enrollment rule, admin remove via wildcard - Tests: DB CRUD, adapter round-trip, interface compliance - Docs: ARCHITECTURE.md §22, PROJECT_PLAN.md Phase 14 Security: Credential IDs and public keys encrypted at rest with AES-256-GCM via vault master key. Challenge ceremonies use 128-bit nonces with 120s TTL in sync.Map. Sign counter validated on each assertion to detect cloned authenticators. Password re-auth required for registration (SEC-01 pattern). No credential material in API responses or logs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
106 lines
4.3 KiB
Go
106 lines
4.3 KiB
Go
package policy
|
|
|
|
// defaultRules are the compiled-in authorization rules. They cannot be
|
|
// modified or deleted via the API. They reproduce the previous binary
|
|
// admin/non-admin behavior exactly when no operator rules exist, so wiring
|
|
// the policy engine alongside RequireRole("admin") produces identical results.
|
|
//
|
|
// All defaults use Priority 0 so they are evaluated before any operator rule
|
|
// (which defaults to Priority 100). Within priority 0, deny-wins still applies,
|
|
// but the defaults contain no Deny rules — they only grant the minimum required
|
|
// for self-service and admin operations.
|
|
//
|
|
// Security rationale for each rule is documented inline.
|
|
var defaultRules = []Rule{
|
|
{
|
|
// Admin wildcard: an account bearing the "admin" role is permitted to
|
|
// perform any action on any resource. This mirrors the previous
|
|
// RequireRole("admin") check and is the root of all administrative trust.
|
|
ID: -1,
|
|
Description: "Admin wildcard: admin role allows all actions",
|
|
Priority: 0,
|
|
Roles: []string{"admin"},
|
|
Effect: Allow,
|
|
},
|
|
{
|
|
// Self-service logout and token renewal: any authenticated principal may
|
|
// revoke or renew their own token. No resource scoping is needed because
|
|
// the handler independently verifies that the JTI belongs to the caller.
|
|
ID: -2,
|
|
Description: "Self-service: any principal may logout or renew their own token",
|
|
Priority: 0,
|
|
Actions: []Action{ActionLogout, ActionRenewToken},
|
|
Effect: Allow,
|
|
},
|
|
{
|
|
// Self-service TOTP enrollment: any authenticated human account may
|
|
// initiate and confirm their own TOTP enrollment. The handler verifies
|
|
// the subject matches before writing.
|
|
ID: -3,
|
|
Description: "Self-service: any principal may enroll their own TOTP",
|
|
Priority: 0,
|
|
Actions: []Action{ActionEnrollTOTP},
|
|
Effect: Allow,
|
|
},
|
|
{
|
|
// Self-service password change: any authenticated human account may
|
|
// change their own password. The handler derives the target exclusively
|
|
// from the JWT subject (claims.Subject) and requires the current
|
|
// password, so a non-admin caller can only affect their own account.
|
|
ID: -7,
|
|
Description: "Self-service: any human account may change their own password",
|
|
Priority: 0,
|
|
AccountTypes: []string{"human"},
|
|
Actions: []Action{ActionChangePassword},
|
|
Effect: Allow,
|
|
},
|
|
{
|
|
// System accounts reading their own pgcreds: a service that has already
|
|
// authenticated (e.g. via its bearer service token) may retrieve its own
|
|
// Postgres credentials without admin privilege. OwnerMatchesSubject
|
|
// ensures the service can only reach its own row — not another service's.
|
|
ID: -4,
|
|
Description: "System accounts may read their own pg_credentials",
|
|
Priority: 0,
|
|
AccountTypes: []string{"system"},
|
|
Actions: []Action{ActionReadPGCreds},
|
|
ResourceType: ResourcePGCreds,
|
|
OwnerMatchesSubject: true,
|
|
Effect: Allow,
|
|
},
|
|
{
|
|
// System accounts issuing or renewing their own service token: a system
|
|
// account may rotate its own bearer token. OwnerMatchesSubject ensures
|
|
// it cannot issue tokens for other accounts.
|
|
ID: -5,
|
|
Description: "System accounts may issue or renew their own service token",
|
|
Priority: 0,
|
|
AccountTypes: []string{"system"},
|
|
Actions: []Action{ActionIssueToken, ActionRenewToken},
|
|
ResourceType: ResourceToken,
|
|
OwnerMatchesSubject: true,
|
|
Effect: Allow,
|
|
},
|
|
{
|
|
// Self-service WebAuthn enrollment: any authenticated human account may
|
|
// register and manage their own passkeys/security keys. The handler
|
|
// verifies the subject matches before writing. Mirrors TOTP rule -3.
|
|
ID: -8,
|
|
Description: "Self-service: any principal may enroll their own WebAuthn credentials",
|
|
Priority: 0,
|
|
Actions: []Action{ActionEnrollWebAuthn},
|
|
Effect: Allow,
|
|
},
|
|
{
|
|
// Public endpoints: token validation and login do not require
|
|
// authentication. The middleware exempts them from RequireAuth entirely;
|
|
// this rule exists so that if a policy check is accidentally applied to
|
|
// these paths, it does not block them.
|
|
ID: -6,
|
|
Description: "Public: token validation and login are always permitted",
|
|
Priority: 0,
|
|
Actions: []Action{ActionValidateToken, ActionLogin},
|
|
Effect: Allow,
|
|
},
|
|
}
|