mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-26 23:50:17 +00:00
Adds a `role` field (admin|member) to user management: Schema: - `role TEXT NOT NULL DEFAULT 'member'` added to users table in both PostgreSQL V14 migration and libSQL schema/incremental migration - UserRecord gains `role: String` field - UserIdentity gains `role: String` field, populated from DB in DbAuthenticator and defaulting to "admin" for single-user mode Access control: - AdminUser extractor: returns 403 Forbidden if role != "admin" - /api/admin/users/* handlers: require AdminUser (create, list, detail, update, suspend, activate) - POST /api/invitations: requires AdminUser (only admins can invite) - User creation accepts optional "role" param (defaults to "member") - Invitation acceptance creates users with "member" role Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
44 lines
2.1 KiB
SQL
44 lines
2.1 KiB
SQL
-- User management tables for multi-tenant deployments.
|
|
--
|
|
-- Replaces the static GATEWAY_USER_TOKENS env var with DB-backed
|
|
-- user registration, API token management, and invitation flow.
|
|
|
|
CREATE TABLE users (
|
|
id TEXT PRIMARY KEY, -- matches existing user_id pattern (string, not UUID)
|
|
email TEXT UNIQUE, -- nullable for token-only users
|
|
display_name TEXT NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'active', -- active | suspended | deactivated
|
|
role TEXT NOT NULL DEFAULT 'member', -- admin | member
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
last_login_at TIMESTAMPTZ,
|
|
created_by TEXT REFERENCES users(id), -- who invited this user (nullable for bootstrap)
|
|
metadata JSONB NOT NULL DEFAULT '{}' -- extensible profile data
|
|
);
|
|
|
|
CREATE TABLE api_tokens (
|
|
id UUID PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
token_hash BYTEA NOT NULL, -- SHA-256 hash (never store plaintext)
|
|
token_prefix TEXT NOT NULL, -- first 8 hex chars for display
|
|
name TEXT NOT NULL, -- human label ("my-laptop", "ci-bot")
|
|
expires_at TIMESTAMPTZ, -- nullable = never expires
|
|
last_used_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
revoked_at TIMESTAMPTZ -- soft-revoke: set this instead of deleting
|
|
);
|
|
CREATE INDEX idx_api_tokens_user ON api_tokens(user_id);
|
|
CREATE INDEX idx_api_tokens_hash ON api_tokens(token_hash);
|
|
|
|
CREATE TABLE invitations (
|
|
id UUID PRIMARY KEY,
|
|
email TEXT, -- nullable for link-based invites
|
|
invite_token_hash BYTEA NOT NULL, -- SHA-256 hash of the invite link token
|
|
invited_by TEXT NOT NULL REFERENCES users(id),
|
|
status TEXT NOT NULL DEFAULT 'pending', -- pending | accepted | expired | revoked
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
accepted_at TIMESTAMPTZ,
|
|
accepted_by TEXT REFERENCES users(id),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|