@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
JavaScript
"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;