0395ad8ddc
Co-authored-by: Claude Code <kjh2064@gmail.com> Co-committed-by: Claude Code <kjh2064@gmail.com>
11 KiB
11 KiB
VS-01: Identity Access Control (IAC) & Role-Based Access
Vertical Slice: VS-01 (Identity & Authorization)
Version: 1.0 DRAFT
Date: 2026-08-07
Owner: Security & Identity Architecture
Status: 📋 DRAFT (Specification Ready for Contract Review)
📋 User Story
As a platform security architect
I want to establish identity, MFA, RBAC role hierarchy, and maker-checker approval boundaries
So that all downstream slices (VS-02 through VS-08) can enforce consistent access control and segregation of duties
Acceptance Criteria:
- 📋 Identity contract defined (user/role/permission schema)
- 📋 MFA policy specified (2FA/TOTP/WebAuthn tiers)
- 📋 RBAC role hierarchy formalized (Guest/User/Operator/Admin/SuperAdmin + domain-specific roles)
- 📋 Maker-checker approval boundaries documented (for critical operations like model promotion, dataset freeze)
- 📋 Permission matrix mapped (read/write/delete/audit per role)
🎯 Non-Goals
- ❌ Implement UI/API endpoints (belongs to BE/FE slices)
- ❌ Integrate with external identity provider (OIDC/Kerberos setup deferred)
- ❌ Build MFA enforcement engine (belongs to separate AUTH_ENFORCEMENT slice)
- ❌ Execute permission checks (belongs to handler/middleware slices)
- ❌ Seed production user data (deferred to operations)
🔄 State Transitions
Identity Lifecycle
[UNDEFINED]
↓ (user registered)
[ACTIVE]
↓ (MFA required but not set)
[REQUIRES_MFA_SETUP]
↓ (MFA device registered)
[MFA_CONFIGURED]
↓ (temporary disable during password reset)
[MFA_SUSPENDED]
↓ (re-enable)
[MFA_CONFIGURED]
↓ (admin deactivation)
[INACTIVE]
↓ (security breach)
[REVOKED]
Role Assignment Workflow (Maker-Checker)
User requests elevated role (e.g., OPERATOR → ADMIN)
↓
[PENDING_APPROVAL] ← Role request created (requester_id, requested_role, reason)
↓
Admin receives notification (role.required_approver_count = 2)
↓
Approver-1 reviews & approves/rejects
↓
[APPROVED_BY_1] or [REJECTED]
↓ (if approved by 1, awaits Approver-2)
[APPROVED_BY_2]
↓
[ACTIVE] (role_assignment.effective_at set, correlation_id = approval_request.id)
↓
[EXPIRED] (optional: time-bound roles like "Quarterly Reviewer")
🔐 RBAC Constraints
Core Role Hierarchy
| Role | Description | Can Access | Can Modify | Can Approve | Maker-Checker Approval Required |
|---|---|---|---|---|---|
| GUEST | Anonymous/public | Public resources (GDP compliant) | ❌ | ❌ | N/A |
| USER | Authenticated individual | Own data + shared workspace | Own data | ❌ | N/A |
| OPERATOR | Operations team (data ops, risk team) | All non-sensitive data | Configurations | MODEL_ACTIVATION (1 more) | MODEL_ACTIVATION, DATASET_FREEZE |
| ADMIN | Platform administrator | All data (except audit logs) | All (soft delete) | All (except critical) | CRITICAL_CONFIG, USER_REVOCATION |
| SUPER_ADMIN | Super administrator | All (including audit logs) | All (hard delete) | All | N/A (can self-approve in emergency) |
Domain-Specific Roles (Optional, for Future Slices)
- QUANT_ENGINEER — Can read market data, backtest code; cannot modify live models
- RISK_MANAGER — Can read risk dashboards, flag models; cannot freeze or promote
- COMPLIANCE_OFFICER — Can audit all; cannot modify data
- MODEL_REVIEWER — Can read model cards, evidence; approves promotion via maker-checker
MFA Tiers
| Tier | Requirement | Impact | Users |
|---|---|---|---|
| NO_MFA | None (legacy) | Guest/public read | Public API consumers |
| TOTP_OPTIONAL | Google Authenticator / Authy (optional) | USER tier | General staff |
| TOTP_REQUIRED | TOTP mandatory | OPERATOR+ tier | Operations, Risk, Compliance |
| HARDWARE_KEY | YubiKey / FIDO2 (required) | SUPER_ADMIN tier | Executives, DBAs |
📊 Data Contract (v1.0)
Point-in-Time (PIT) Envelope (Inherited from VS-00)
All identity tables MUST include:
-- Core identity tables
CREATE TABLE identity.users (
id UUID PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
display_name VARCHAR(255),
mfa_status VARCHAR(50) NOT NULL DEFAULT 'REQUIRES_MFA_SETUP', -- ACTIVE, REQUIRES_MFA_SETUP, MFA_CONFIGURED, INACTIVE, REVOKED
mfa_method VARCHAR(50), -- TOTP, HARDWARE_KEY, none
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
revision INT NOT NULL DEFAULT 1,
correlation_id UUID NOT NULL
);
CREATE TABLE identity.roles (
id UUID PRIMARY KEY,
name VARCHAR(100) NOT NULL UNIQUE, -- GUEST, USER, OPERATOR, ADMIN, SUPER_ADMIN
description TEXT,
required_approver_count INT DEFAULT 1, -- How many approvers needed for elevation to this role
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
revision INT NOT NULL DEFAULT 1,
correlation_id UUID NOT NULL
);
CREATE TABLE identity.user_roles (
id UUID PRIMARY KEY,
user_id UUID NOT NULL REFERENCES identity.users(id),
role_id UUID NOT NULL REFERENCES identity.roles(id),
assigned_by_user_id UUID, -- Who assigned this role
effective_at TIMESTAMPTZ NOT NULL,
expires_at TIMESTAMPTZ, -- Optional: time-bound roles
is_active BOOLEAN DEFAULT TRUE,
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
revision INT NOT NULL DEFAULT 1,
correlation_id UUID NOT NULL
);
CREATE TABLE identity.role_approval_requests (
id UUID PRIMARY KEY,
user_id UUID NOT NULL REFERENCES identity.users(id),
requested_role_id UUID NOT NULL REFERENCES identity.roles(id),
reason TEXT,
status VARCHAR(50) NOT NULL DEFAULT 'PENDING_APPROVAL', -- PENDING_APPROVAL, APPROVED_BY_1, APPROVED_BY_2, REJECTED, WITHDRAWN
approver_count_required INT NOT NULL,
approvers JSONB NOT NULL DEFAULT '[]'::JSONB, -- [{ "approver_id": UUID, "approved_at": TIMESTAMPTZ, "reason": "" }]
created_at TIMESTAMPTZ NOT NULL,
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
revision INT NOT NULL DEFAULT 1,
correlation_id UUID NOT NULL
);
CREATE TABLE identity.mfa_devices (
id UUID PRIMARY KEY,
user_id UUID NOT NULL REFERENCES identity.users(id),
device_type VARCHAR(50) NOT NULL, -- TOTP, HARDWARE_KEY
secret_hash VARCHAR(255), -- Hashed TOTP secret (never store plaintext)
device_name VARCHAR(255), -- User-friendly name ("My YubiKey", "Work Phone")
registered_at TIMESTAMPTZ NOT NULL,
last_used_at TIMESTAMPTZ,
is_backup_device BOOLEAN DEFAULT FALSE,
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
revision INT NOT NULL DEFAULT 1,
correlation_id UUID NOT NULL
);
CREATE TABLE identity.permissions (
id UUID PRIMARY KEY,
role_id UUID NOT NULL REFERENCES identity.roles(id),
resource VARCHAR(255) NOT NULL, -- "model_activation", "dataset_freeze", "user_management"
action VARCHAR(50) NOT NULL, -- READ, WRITE, DELETE, AUDIT
constraints JSONB, -- Optional: { "requires_approval_count": 2, "requires_evidence": ["model_card"] }
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
revision INT NOT NULL DEFAULT 1,
correlation_id UUID NOT NULL,
UNIQUE(role_id, resource, action)
);
Data Quality Rules
- ✅ No direct password storage (use bcrypt + salt)
- ✅ MFA secrets never logged or exposed in HTTP responses
- ✅ All role changes tracked in
user_rolesappend-only (no soft deletes) - ✅ Approval requests immutable once APPROVED_BY_1 or REJECTED
- ✅ PIT envelope strictly enforced:
published_at <= cutofffor all reads - ✅
correlation_idlinks all related tables for audit trail
🛡️ Governance Gates
Pre-Merge Gates
- RBAC Matrix Approved: Security team signs off on role hierarchy and permission matrix
- MFA Tier Mapping: Confirm mapping between role tiers and MFA requirements
- Maker-Checker Thresholds: Define approval_count per critical operation (e.g., model promotion = 2 approvers)
- Audit Log Design: Confirm all authorization decisions (grant/deny/revoke) are logged with
correlation_id - Identity Provider Integration Plan: Document OIDC/Kerberos provider (if applicable)
Post-Merge Validation
- Schema Tests: User/role/MFA creation tests pass (40+ scenarios)
- RBAC Policy Tests: Permission matrix matches code (cross-checked vs ADR-SEC-001)
- PIT Query Tests: All reads include
WHERE published_at <= @cutoff
📋 Source / Assumptions / Unknown
Source
- ADR-SEC-001: OIDC/JWT/DevelopmentHeader authentication tiers (approved 2026-08-04)
- Existing RBAC: VS-00-SLICE_SPEC (base governance, roles table exists)
- Maker-Checker Pattern: Standard 2-approver workflow from compliance requirements
Assumptions
- ✅ OIDC identity provider will be integrated later (separate slice); VS-01 is schema + policy only
- ✅ MFA enforcement (checking device before operation) happens in middleware/handler layer (not here)
- ✅ Audit logging of permission checks is already handled by OutboxPollerJob + SerilogCorrelation
- ✅ All users are human; no service-account roles yet (may expand in future)
Unknown
- ❓ OIDC Provider Identity: Which OIDC provider (Keycloak, Auth0, Azure AD)? Deferred to separate architecture decision.
- ❓ Hardware Key Vendor: YubiKey vs other FIDO2 vendors? Deferred to procurement.
- ❓ Approval SLA: How long can role requests stay in PENDING_APPROVAL before escalation alert? (Assumed 5 business days; confirm with ops)
- ❓ Audit Retention: How long to retain
role_approval_requestshistory? (Assumed 7 years for compliance; confirm with legal) - ❓ Domain-Specific Roles: Should QUANT_ENGINEER/RISK_MANAGER/COMPLIANCE roles be predefined, or dynamically created per organization? (Deferred to VS-03+)
✅ Compliance & Traceability
Governance: AGENTS.md v16.0 Maturity gate (contract-first, no placeholder code)
Related ADRs:
- ADR-SEC-001: Authentication strategy (OIDC tiers)
- ADR-GOV-001: Role-based access control (assumed; link when available)
WBS Dependencies:
- ✅ AEG-X-001 (Version Coverage Matrix): Prerequisite for schema versioning
- ✅ AEG-VS-00-02 (Data Contract): PIT envelope inherited
Next Slices (Depend on VS-01):
- VS-02: Financial Security Master (source approval RBAC)
- VS-03: Model Operations (model promotion maker-checker)
- VS-04+: All domain slices (inherit identity & approval boundaries)
Status
📋 DRAFT: Specification complete, ready for:
- Security team approval (RBAC matrix + MFA tiers)
- Compliance team approval (maker-checker SLA + audit retention)
- Architecture review (schema + PIT readiness)
- Next: Implementation (separate PR for schema migration + tests)