Add secure administrator operations console
Provide TOTP-authenticated, role-controlled user and credit workflows with paginated audit data and SQL-backed statistics so operations can manage growth safely.
This commit is contained in:
@@ -30,6 +30,15 @@ app:
|
||||
antiAbuse:
|
||||
identityHmacKeyBase64: "$IDENTITY_HMAC_KEY"
|
||||
tombstoneRetentionDays: "$IDENTITY_TOMBSTONE_RETENTION_DAYS:365"
|
||||
admin:
|
||||
enabled: "$ADMIN_ENABLED:false"
|
||||
bootstrapEnabled: "$ADMIN_BOOTSTRAP_ENABLED:false"
|
||||
bootstrapOperatorId: "$ADMIN_BOOTSTRAP_OPERATOR_ID:"
|
||||
bootstrapUsername: "$ADMIN_BOOTSTRAP_USERNAME:"
|
||||
bootstrapPasswordHash: "$ADMIN_BOOTSTRAP_PASSWORD_HASH:"
|
||||
bootstrapTotpSecretBase32: "$ADMIN_BOOTSTRAP_TOTP_SECRET_BASE32:"
|
||||
sessionHours: "$ADMIN_SESSION_HOURS:8"
|
||||
maximumManualGrant: "$ADMIN_MAXIMUM_MANUAL_GRANT:100000"
|
||||
apple:
|
||||
teamId: "$APPLE_TEAM_ID:"
|
||||
keyId: "$APPLE_KEY_ID:"
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
CREATE TABLE admin_operators (
|
||||
id CHAR(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
username VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
|
||||
password_hash VARCHAR(255) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
encrypted_totp_secret TEXT NOT NULL,
|
||||
role VARCHAR(32) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
failed_login_count INT NOT NULL DEFAULT 0,
|
||||
locked_until DATETIME(6) NULL,
|
||||
last_totp_counter BIGINT NULL,
|
||||
last_login_at DATETIME(6) NULL,
|
||||
disabled_at DATETIME(6) NULL,
|
||||
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_admin_operators_username (username),
|
||||
INDEX idx_admin_operators_status_created (disabled_at, created_at),
|
||||
CONSTRAINT chk_admin_operators_role
|
||||
CHECK (role IN ('SUPER_ADMIN', 'SUPPORT', 'ANALYST')),
|
||||
CONSTRAINT chk_admin_operators_failed_logins
|
||||
CHECK (failed_login_count >= 0),
|
||||
CONSTRAINT chk_admin_operators_totp_counter
|
||||
CHECK (last_totp_counter IS NULL OR last_totp_counter >= 0)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE admin_sessions (
|
||||
id CHAR(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
operator_id CHAR(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
token_hash CHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
csrf_token_hash CHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
expires_at DATETIME(6) NOT NULL,
|
||||
revoked_at DATETIME(6) NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_admin_sessions_token_hash (token_hash),
|
||||
INDEX idx_admin_sessions_operator_active (operator_id, revoked_at, expires_at),
|
||||
INDEX idx_admin_sessions_expiry (expires_at),
|
||||
CONSTRAINT fk_admin_sessions_operator
|
||||
FOREIGN KEY (operator_id) REFERENCES admin_operators (id) ON DELETE RESTRICT,
|
||||
CONSTRAINT chk_admin_sessions_expiry
|
||||
CHECK (expires_at > created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE admin_audit_log (
|
||||
id CHAR(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
actor_operator_id CHAR(36) CHARACTER SET ascii COLLATE ascii_bin NULL,
|
||||
action VARCHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
outcome VARCHAR(32) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
target_type VARCHAR(64) CHARACTER SET ascii COLLATE ascii_bin NULL,
|
||||
target_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL,
|
||||
request_id VARCHAR(128) CHARACTER SET ascii COLLATE ascii_bin NULL,
|
||||
occurred_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (id),
|
||||
INDEX idx_admin_audit_occurred (occurred_at, id),
|
||||
INDEX idx_admin_audit_action_occurred (action, occurred_at),
|
||||
INDEX idx_admin_audit_actor_occurred (actor_operator_id, occurred_at),
|
||||
INDEX idx_admin_audit_target (target_type, target_id, occurred_at),
|
||||
CONSTRAINT fk_admin_audit_actor
|
||||
FOREIGN KEY (actor_operator_id) REFERENCES admin_operators (id) ON DELETE RESTRICT,
|
||||
CONSTRAINT chk_admin_audit_target_pair CHECK (
|
||||
(target_type IS NULL AND target_id IS NULL)
|
||||
OR (target_type IS NOT NULL AND target_id IS NOT NULL)
|
||||
)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE admin_credit_grants (
|
||||
id CHAR(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
operator_id CHAR(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
account_id VARCHAR(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
amount BIGINT NOT NULL,
|
||||
reason VARCHAR(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
idempotency_key VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
ledger_entry_id CHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
|
||||
audit_log_id CHAR(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_admin_credit_grants_idempotency (idempotency_key),
|
||||
UNIQUE KEY uk_admin_credit_grants_ledger (ledger_entry_id),
|
||||
UNIQUE KEY uk_admin_credit_grants_audit (audit_log_id),
|
||||
INDEX idx_admin_credit_grants_account_created (account_id, created_at),
|
||||
INDEX idx_admin_credit_grants_operator_created (operator_id, created_at),
|
||||
INDEX idx_admin_credit_grants_created (created_at),
|
||||
CONSTRAINT fk_admin_credit_grants_operator
|
||||
FOREIGN KEY (operator_id) REFERENCES admin_operators (id) ON DELETE RESTRICT,
|
||||
CONSTRAINT fk_admin_credit_grants_ledger
|
||||
FOREIGN KEY (ledger_entry_id) REFERENCES credit_ledger (id) ON DELETE RESTRICT,
|
||||
CONSTRAINT fk_admin_credit_grants_audit
|
||||
FOREIGN KEY (audit_log_id) REFERENCES admin_audit_log (id) ON DELETE RESTRICT,
|
||||
CONSTRAINT chk_admin_credit_grants_amount CHECK (amount > 0),
|
||||
CONSTRAINT chk_admin_credit_grants_reason
|
||||
CHECK (CHAR_LENGTH(TRIM(reason)) BETWEEN 1 AND 500),
|
||||
CONSTRAINT chk_admin_credit_grants_idempotency
|
||||
CHECK (CHAR_LENGTH(idempotency_key) BETWEEN 8 AND 128)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- account_id intentionally has no foreign key: account deletion must preserve
|
||||
-- the immutable, pseudonymized grant and ledger records.
|
||||
|
||||
-- Dashboard queries aggregate by creation time and state. These indexes avoid
|
||||
-- full scans without changing existing business data or ledger semantics.
|
||||
CREATE INDEX idx_accounts_created ON accounts (created_at);
|
||||
CREATE INDEX idx_credit_ledger_type_created ON credit_ledger (entry_type, created_at);
|
||||
CREATE INDEX idx_credit_usage_created ON credit_usage_records (created_at);
|
||||
CREATE INDEX idx_referral_bindings_status_bound
|
||||
ON referral_bindings (reward_status, bound_at);
|
||||
|
||||
-- admin_audit_log and admin_credit_grants are append-only. The production
|
||||
-- runtime database role must receive SELECT/INSERT only on these tables.
|
||||
Reference in New Issue
Block a user