access-manager/lib/db.js
TARS 0b0871ffea feat: Access Manager v3 — RBAC engine, SQLite, permission system
- SQLite database with full schema: users, roles, permissions,
  role_permissions, user_roles, services, audit_log
- RBAC engine with wildcard permission resolution (*.*.*)
- Automatic v2→v3 migration from JSON files
- 5 default roles: super_admin, admin, editor, user, viewer
- Feature registration for APP, GGL, FDX (119 permissions)
- 8 services seeded
- Full API: roles CRUD, permission check, user-role assignment,
  feature registration, audit log, stats
- Backward compatible with existing auth flows
2026-04-16 00:57:27 +00:00

98 lines
3.2 KiB
JavaScript

const Database = require('better-sqlite3');
const path = require('path');
const fs = require('fs');
const DB_PATH = path.join(__dirname, '..', 'data', 'access-manager.db');
fs.mkdirSync(path.dirname(DB_PATH), { recursive: true });
const db = new Database(DB_PATH);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');
// ─── Schema ───
db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL COLLATE NOCASE,
name TEXT NOT NULL,
password_hash TEXT,
auth_methods TEXT NOT NULL DEFAULT '["google"]',
metadata TEXT DEFAULT '{}',
tags TEXT DEFAULT '[]',
created_at TEXT NOT NULL DEFAULT (datetime('now')),
created_by TEXT DEFAULT 'system',
last_login TEXT,
status TEXT NOT NULL DEFAULT 'active'
);
CREATE TABLE IF NOT EXISTS roles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
display_name TEXT NOT NULL,
description TEXT,
is_system INTEGER NOT NULL DEFAULT 0,
priority INTEGER NOT NULL DEFAULT 100,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS permissions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
app TEXT NOT NULL,
feature TEXT NOT NULL,
action TEXT NOT NULL,
display_name TEXT,
description TEXT,
category TEXT DEFAULT 'general',
created_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(app, feature, action)
);
CREATE TABLE IF NOT EXISTS role_permissions (
role_id INTEGER NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
permission_id INTEGER NOT NULL REFERENCES permissions(id) ON DELETE CASCADE,
PRIMARY KEY (role_id, permission_id)
);
CREATE TABLE IF NOT EXISTS user_roles (
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role_id INTEGER NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
scope TEXT NOT NULL DEFAULT '*',
granted_at TEXT NOT NULL DEFAULT (datetime('now')),
granted_by TEXT,
PRIMARY KEY (user_id, role_id, scope)
);
CREATE TABLE IF NOT EXISTS services (
id TEXT PRIMARY KEY,
display_name TEXT NOT NULL,
hostname TEXT,
hostnames TEXT DEFAULT '[]',
port INTEGER,
status TEXT NOT NULL DEFAULT 'active',
features_registered INTEGER DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL DEFAULT (datetime('now')),
actor_email TEXT NOT NULL,
action TEXT NOT NULL,
target_type TEXT,
target_id TEXT,
detail TEXT,
ip TEXT,
service TEXT
);
CREATE INDEX IF NOT EXISTS idx_user_roles_user ON user_roles(user_id);
CREATE INDEX IF NOT EXISTS idx_user_roles_role ON user_roles(role_id);
CREATE INDEX IF NOT EXISTS idx_role_perms_role ON role_permissions(role_id);
CREATE INDEX IF NOT EXISTS idx_permissions_app ON permissions(app);
CREATE INDEX IF NOT EXISTS idx_audit_actor ON audit_log(actor_email);
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log(timestamp);
`);
module.exports = db;