UNPKG

@mamoorali295/rbac

Version:

Complete RBAC (Role-Based Access Control) system for Node.js with Express middleware, NestJS integration, GraphQL support, MongoDB & PostgreSQL support, modern admin dashboard, TypeScript support, and dynamic permission management

313 lines (299 loc) 13.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PostgresAdapter = void 0; const DatabaseAdapter_1 = require("./DatabaseAdapter"); const User_1 = require("../postgres/models/User"); const UserRole_1 = require("../postgres/models/UserRole"); const Feature_1 = require("../postgres/models/Feature"); const Permission_1 = require("../postgres/models/Permission"); class PostgresAdapter extends DatabaseAdapter_1.DatabaseAdapter { constructor(pool) { super(); this.pool = pool; this.userModel = new User_1.PostgresUser(pool); this.roleModel = new UserRole_1.PostgresUserRole(pool); this.featureModel = new Feature_1.PostgresFeature(pool); this.permissionModel = new Permission_1.PostgresPermission(pool); } init() { return __awaiter(this, void 0, void 0, function* () { // Run schema initialization yield this.initializeSchema(); yield this.createStandardPermissions(); }); } initializeSchema() { return __awaiter(this, void 0, void 0, function* () { // Inline schema to avoid file path issues const schema = ` -- RBAC PostgreSQL Schema CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- RbacPermissions table CREATE TABLE IF NOT EXISTS rbac_permissions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(100) NOT NULL UNIQUE, description TEXT NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- RbacFeatures table CREATE TABLE IF NOT EXISTS rbac_features ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(100) NOT NULL UNIQUE, description TEXT NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- RbacRoles table CREATE TABLE IF NOT EXISTS rbac_roles ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(100) NOT NULL UNIQUE, description TEXT NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- RbacUsers table CREATE TABLE IF NOT EXISTS rbac_users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id VARCHAR(255) NOT NULL UNIQUE, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE, role_id UUID REFERENCES rbac_roles(id) ON DELETE SET NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- Junction table for role-feature-permissions CREATE TABLE IF NOT EXISTS rbac_role_feature_permissions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), role_id UUID NOT NULL REFERENCES rbac_roles(id) ON DELETE CASCADE, feature_id UUID NOT NULL REFERENCES rbac_features(id) ON DELETE CASCADE, permission_id UUID NOT NULL REFERENCES rbac_permissions(id) ON DELETE CASCADE, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, UNIQUE(role_id, feature_id, permission_id) ); -- Indexes for performance CREATE INDEX IF NOT EXISTS idx_rbac_users_user_id ON rbac_users(user_id); CREATE INDEX IF NOT EXISTS idx_rbac_users_email ON rbac_users(email); CREATE INDEX IF NOT EXISTS idx_rbac_users_role_id ON rbac_users(role_id); CREATE INDEX IF NOT EXISTS idx_rbac_role_feature_permissions_role_id ON rbac_role_feature_permissions(role_id); CREATE INDEX IF NOT EXISTS idx_rbac_role_feature_permissions_feature_id ON rbac_role_feature_permissions(feature_id); CREATE INDEX IF NOT EXISTS idx_rbac_permissions_name ON rbac_permissions(name); CREATE INDEX IF NOT EXISTS idx_rbac_features_name ON rbac_features(name); CREATE INDEX IF NOT EXISTS idx_rbac_roles_name ON rbac_roles(name); -- Update trigger function CREATE OR REPLACE FUNCTION update_updated_at_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = CURRENT_TIMESTAMP; RETURN NEW; END; $$ language 'plpgsql'; -- Apply update triggers DROP TRIGGER IF EXISTS update_rbac_permissions_updated_at ON rbac_permissions; CREATE TRIGGER update_rbac_permissions_updated_at BEFORE UPDATE ON rbac_permissions FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); DROP TRIGGER IF EXISTS update_rbac_features_updated_at ON rbac_features; CREATE TRIGGER update_rbac_features_updated_at BEFORE UPDATE ON rbac_features FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); DROP TRIGGER IF EXISTS update_rbac_roles_updated_at ON rbac_roles; CREATE TRIGGER update_rbac_roles_updated_at BEFORE UPDATE ON rbac_roles FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); DROP TRIGGER IF EXISTS update_rbac_users_updated_at ON rbac_users; CREATE TRIGGER update_rbac_users_updated_at BEFORE UPDATE ON rbac_users FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); DROP TRIGGER IF EXISTS update_rbac_role_feature_permissions_updated_at ON rbac_role_feature_permissions; CREATE TRIGGER update_rbac_role_feature_permissions_updated_at BEFORE UPDATE ON rbac_role_feature_permissions FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- Insert standard permissions INSERT INTO rbac_permissions (name, description) VALUES ('read', 'View and access resources'), ('create', 'Add new resources'), ('update', 'Modify existing resources'), ('delete', 'Remove resources'), ('sudo', 'Full administrative access') ON CONFLICT (name) DO NOTHING; `; yield this.pool.query(schema); }); } createStandardPermissions() { return __awaiter(this, void 0, void 0, function* () { yield this.permissionModel.createStandard(); }); } // User operations createUser(userData) { return __awaiter(this, void 0, void 0, function* () { return yield this.userModel.create(userData); }); } findUserByUserId(user_id) { return __awaiter(this, void 0, void 0, function* () { return yield this.userModel.findByUserId(user_id); }); } findUserByUserIdWithRole(user_id) { return __awaiter(this, void 0, void 0, function* () { return yield this.userModel.findByUserIdWithRole(user_id); }); } updateUser(user_id, updates) { return __awaiter(this, void 0, void 0, function* () { yield this.userModel.update(user_id, updates); }); } deleteUser(user_id) { return __awaiter(this, void 0, void 0, function* () { yield this.userModel.delete(user_id); }); } getAllUsers(limit, offset, search) { return __awaiter(this, void 0, void 0, function* () { const result = yield this.userModel.getAll(limit, offset, search); return { items: result.users, total: result.total }; }); } // Role operations createRole(roleData) { return __awaiter(this, void 0, void 0, function* () { return yield this.roleModel.create(roleData); }); } findRoleByName(name) { return __awaiter(this, void 0, void 0, function* () { return yield this.roleModel.findByName(name); }); } findRoleById(id) { return __awaiter(this, void 0, void 0, function* () { return yield this.roleModel.findById(id); }); } findRoleByIdWithFeatures(id) { return __awaiter(this, void 0, void 0, function* () { return yield this.roleModel.findByIdWithFeatures(id); }); } updateRole(id, updates) { return __awaiter(this, void 0, void 0, function* () { yield this.roleModel.update(id, updates); }); } deleteRole(id) { return __awaiter(this, void 0, void 0, function* () { yield this.roleModel.delete(id); }); } assignRoleFeaturePermissions(roleId, featurePermissions) { return __awaiter(this, void 0, void 0, function* () { yield this.roleModel.assignFeaturePermissions(roleId, featurePermissions); }); } getAllRoles(limit, offset) { return __awaiter(this, void 0, void 0, function* () { const result = yield this.roleModel.getAll(limit, offset); return { items: result.roles, total: result.total }; }); } // Feature operations createFeature(featureData) { return __awaiter(this, void 0, void 0, function* () { return yield this.featureModel.create(featureData); }); } findFeatureByName(name) { return __awaiter(this, void 0, void 0, function* () { return yield this.featureModel.findByName(name); }); } findFeatureById(id) { return __awaiter(this, void 0, void 0, function* () { return yield this.featureModel.findById(id); }); } updateFeature(id, updates) { return __awaiter(this, void 0, void 0, function* () { yield this.featureModel.update(id, updates); }); } deleteFeature(id) { return __awaiter(this, void 0, void 0, function* () { yield this.featureModel.delete(id); }); } getAllFeatures(limit, offset) { return __awaiter(this, void 0, void 0, function* () { const result = yield this.featureModel.getAll(limit, offset); return { items: result.features, total: result.total }; }); } // Permission operations createPermission(permissionData) { return __awaiter(this, void 0, void 0, function* () { return yield this.permissionModel.create(permissionData); }); } findPermissionByName(name) { return __awaiter(this, void 0, void 0, function* () { return yield this.permissionModel.findByName(name); }); } findPermissionById(id) { return __awaiter(this, void 0, void 0, function* () { return yield this.permissionModel.findById(id); }); } updatePermission(id, updates) { return __awaiter(this, void 0, void 0, function* () { yield this.permissionModel.update(id, updates); }); } deletePermission(id) { return __awaiter(this, void 0, void 0, function* () { yield this.permissionModel.delete(id); }); } getAllPermissions(limit, offset) { return __awaiter(this, void 0, void 0, function* () { const result = yield this.permissionModel.getAll(limit, offset); return { items: result.permissions, total: result.total }; }); } getUserFeaturePermissions(user_id, featureName) { return __awaiter(this, void 0, void 0, function* () { var _a; const user = yield this.userModel.findByUserIdWithRole(user_id); if (!user || !user.role) { return []; } const role = user.role; const feature = (_a = role.features) === null || _a === void 0 ? void 0 : _a.find((f) => f.feature.name === featureName); if (!feature) { return []; } return feature.permissions.map((p) => p.name); }); } getDashboardStats() { return __awaiter(this, void 0, void 0, function* () { const queries = [ 'SELECT COUNT(*) FROM rbac_users', 'SELECT COUNT(*) FROM rbac_roles', 'SELECT COUNT(*) FROM rbac_features', 'SELECT COUNT(*) FROM rbac_permissions' ]; const results = yield Promise.all(queries.map(query => this.pool.query(query))); return { users: parseInt(results[0].rows[0].count), roles: parseInt(results[1].rows[0].count), features: parseInt(results[2].rows[0].count), permissions: parseInt(results[3].rows[0].count) }; }); } } exports.PostgresAdapter = PostgresAdapter;