mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-10-01 12:31:59 +00:00
pkg/security no longer contains SQL. Every provider calls a store interface
from lookup, implemented by a procedure backend (Postgres stored procedures,
the default there) and a direct backend (dialect-driven SQL for postgres,
sqlite, mysql and mssql with configurable table and column names).
- add sectypes, lookup, lookup/{dialect,procedure,direct,backends,ddl,conformance}
- split totp and providers sub packages out of the core package
- replace SQLNames/TableNames/QueryMode with lookup.Config (see breaking_changes.md)
- direct backend now covers column/row security and API-key login
- move txsettings SQL to lookup.ApplyTxSettings; remove password.go
- move schema scripts under lookup/, add reference DDL per dialect
- add a shared conformance suite; run it on sqlite, and on Postgres in a
podman/docker container (RESOLVESPEC_TEST_CONTAINERS=1)
- fix procedure schema bugs found on real Postgres: duplicate p_data
parameter, JSON null arrays, expires_at timezone casts, passkey list
GROUP BY, missing resolvespec_passkey_login; accept zone-less timestamps
224 lines
7.0 KiB
SQL
224 lines
7.0 KiB
SQL
-- Reference schema for the lookup direct backend: MySQL 8.0.16+ / MariaDB 10.2+. Direct backend only. Run each statement separately (see ddl.Statements) unless multiStatements=true.
|
|
-- Table and column names are the lookup.DefaultSchema defaults, override them with lookup.Config.Schema.
|
|
-- Generated by the project, edit freely for your deployment (types, collations, extra columns).
|
|
|
|
-- password: bcrypt hash (nullable for OAuth2 users), legacy cleartext is accepted at login
|
|
-- roles: comma-separated roles
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
username VARCHAR(255) NOT NULL UNIQUE,
|
|
email VARCHAR(255) NOT NULL UNIQUE,
|
|
password VARCHAR(255),
|
|
user_level INT DEFAULT 0,
|
|
roles VARCHAR(500),
|
|
is_active TINYINT(1) DEFAULT 1,
|
|
created_at DATETIME NULL,
|
|
updated_at DATETIME NULL,
|
|
last_login_at DATETIME,
|
|
program_user_id INT DEFAULT 0,
|
|
program_user_table VARCHAR(255) DEFAULT '',
|
|
remote_id VARCHAR(255),
|
|
auth_provider VARCHAR(50),
|
|
totp_secret VARCHAR(255),
|
|
totp_enabled TINYINT(1) DEFAULT 0,
|
|
totp_enabled_at DATETIME
|
|
);
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS user_sessions (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
session_token VARCHAR(500) NOT NULL UNIQUE,
|
|
user_id INT NOT NULL,
|
|
expires_at DATETIME NOT NULL,
|
|
created_at DATETIME NULL,
|
|
last_activity_at DATETIME NULL,
|
|
ip_address VARCHAR(45),
|
|
user_agent TEXT,
|
|
access_token TEXT,
|
|
refresh_token TEXT,
|
|
token_type VARCHAR(50) DEFAULT 'Bearer',
|
|
auth_provider VARCHAR(50),
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
INDEX idx_user_sessions_user_id (user_id),
|
|
INDEX idx_user_sessions_expires_at (expires_at)
|
|
);
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS token_blacklist (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
token VARCHAR(500) NOT NULL,
|
|
user_id INT,
|
|
expires_at DATETIME NOT NULL,
|
|
created_at DATETIME NULL,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
);
|
|
|
|
|
|
-- code_hash: SHA-256 hex of the backup code
|
|
|
|
CREATE TABLE IF NOT EXISTS user_totp_backup_codes (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id INT NOT NULL,
|
|
code_hash VARCHAR(64) NOT NULL,
|
|
used TINYINT(1) DEFAULT 0,
|
|
used_at DATETIME,
|
|
created_at DATETIME NULL,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
INDEX idx_totp_user_id (user_id),
|
|
INDEX idx_totp_code_hash (code_hash)
|
|
);
|
|
|
|
|
|
-- credential_id: base64 text
|
|
-- public_key: base64 text
|
|
-- aaguid: base64 text
|
|
-- transports: JSON-encoded array
|
|
|
|
CREATE TABLE IF NOT EXISTS user_passkey_credentials (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id INT NOT NULL,
|
|
credential_id VARCHAR(1400) CHARACTER SET ascii NOT NULL UNIQUE,
|
|
public_key TEXT NOT NULL,
|
|
attestation_type VARCHAR(50) DEFAULT 'none',
|
|
aaguid TEXT,
|
|
sign_count INT DEFAULT 0,
|
|
clone_warning TINYINT(1) DEFAULT 0,
|
|
transports TEXT,
|
|
backup_eligible TINYINT(1) DEFAULT 0,
|
|
backup_state TINYINT(1) DEFAULT 0,
|
|
name VARCHAR(255),
|
|
created_at DATETIME NULL,
|
|
last_used_at DATETIME NULL,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
INDEX idx_passkey_user_id (user_id)
|
|
);
|
|
|
|
|
|
-- token_hash: SHA-256 hex of the raw token
|
|
|
|
CREATE TABLE IF NOT EXISTS user_password_resets (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id INT NOT NULL,
|
|
token_hash VARCHAR(64) NOT NULL UNIQUE,
|
|
expires_at DATETIME NOT NULL,
|
|
created_at DATETIME NULL,
|
|
used TINYINT(1) DEFAULT 0,
|
|
used_at DATETIME,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
INDEX idx_pw_reset_user_id (user_id),
|
|
INDEX idx_pw_reset_expires_at (expires_at)
|
|
);
|
|
|
|
|
|
-- redirect_uris: JSON-encoded array
|
|
-- grant_types: JSON-encoded array
|
|
-- allowed_scopes: JSON-encoded array
|
|
-- client_secret_hash: SHA-256 hex of the confidential-client secret, NULL for public clients
|
|
|
|
CREATE TABLE IF NOT EXISTS oauth_clients (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
client_id VARCHAR(255) NOT NULL UNIQUE,
|
|
redirect_uris TEXT NOT NULL,
|
|
client_name VARCHAR(255),
|
|
grant_types TEXT,
|
|
allowed_scopes TEXT,
|
|
client_secret_hash TEXT,
|
|
token_endpoint_auth_method VARCHAR(30) DEFAULT 'none',
|
|
is_active TINYINT(1) DEFAULT 1,
|
|
created_at DATETIME NULL
|
|
);
|
|
|
|
|
|
-- scopes: JSON-encoded array
|
|
|
|
CREATE TABLE IF NOT EXISTS oauth_codes (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
code VARCHAR(255) NOT NULL UNIQUE,
|
|
client_id VARCHAR(255) NOT NULL,
|
|
redirect_uri TEXT NOT NULL,
|
|
client_state TEXT,
|
|
code_challenge VARCHAR(255) NOT NULL,
|
|
code_challenge_method VARCHAR(10) DEFAULT 'S256',
|
|
session_token TEXT NOT NULL,
|
|
refresh_token TEXT,
|
|
scopes TEXT,
|
|
expires_at DATETIME NOT NULL,
|
|
created_at DATETIME NULL,
|
|
INDEX idx_oauth_codes_expires (expires_at)
|
|
);
|
|
|
|
|
|
-- key_hash: SHA-256 hex
|
|
-- scopes: JSON-encoded array
|
|
-- meta: JSON-encoded object
|
|
|
|
CREATE TABLE IF NOT EXISTS user_keys (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id INT NOT NULL,
|
|
key_type VARCHAR(50) NOT NULL,
|
|
key_hash VARCHAR(64) NOT NULL UNIQUE,
|
|
name VARCHAR(255) NOT NULL DEFAULT '',
|
|
scopes TEXT,
|
|
meta TEXT,
|
|
expires_at DATETIME,
|
|
created_at DATETIME NULL,
|
|
last_used_at DATETIME,
|
|
is_active TINYINT(1) DEFAULT 1,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
INDEX idx_user_keys_user_id (user_id),
|
|
INDEX idx_user_keys_key_type (key_type)
|
|
);
|
|
|
|
|
|
-- Optional: omit to use per-user rules only.
|
|
|
|
CREATE TABLE IF NOT EXISTS sec_group_members (
|
|
group_id INT NOT NULL,
|
|
user_id INT NOT NULL,
|
|
PRIMARY KEY (group_id, user_id),
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
);
|
|
|
|
|
|
-- column_path: dot path under the table: col or col.sub.field
|
|
-- access_type: mask, hide, read, ...
|
|
-- extra_filters: JSON object
|
|
|
|
CREATE TABLE IF NOT EXISTS sec_column_rules (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id INT,
|
|
group_id INT,
|
|
schema_name VARCHAR(255) NOT NULL,
|
|
table_name VARCHAR(255) NOT NULL,
|
|
column_path VARCHAR(255) NOT NULL,
|
|
access_type VARCHAR(50) NOT NULL,
|
|
mask_start INT DEFAULT 0,
|
|
mask_end INT DEFAULT 0,
|
|
mask_invert TINYINT(1) DEFAULT 0,
|
|
mask_char VARCHAR(10) DEFAULT '*',
|
|
extra_filters TEXT,
|
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
CHECK ((user_id IS NULL AND group_id IS NOT NULL) OR (user_id IS NOT NULL AND group_id IS NULL)),
|
|
INDEX idx_sec_column_rules_table (schema_name, table_name)
|
|
);
|
|
|
|
|
|
-- template: SQL fragment, e.g. user_id = {UserID}
|
|
|
|
CREATE TABLE IF NOT EXISTS sec_row_rules (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id INT,
|
|
group_id INT,
|
|
schema_name VARCHAR(255) NOT NULL,
|
|
table_name VARCHAR(255) NOT NULL,
|
|
template TEXT,
|
|
has_block TINYINT(1) NOT NULL DEFAULT 0,
|
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
CHECK ((user_id IS NULL AND group_id IS NOT NULL) OR (user_id IS NOT NULL AND group_id IS NULL)),
|
|
INDEX idx_sec_row_rules_table (schema_name, table_name)
|
|
);
|
|
|