Files
radixOS/src/services/rbacManager.js
2026-05-07 15:28:26 +02:00

509 lines
12 KiB
JavaScript

const jwt = require('jsonwebtoken');
const sequelize = require('sequelize');
class RBACManager {
constructor(databaseModel, SECRET_KEY, service) {
this.db = databaseModel;
this.SECRET_KEY = SECRET_KEY;
this.service = service;
}
async resolvePermissions(objectGuid) {
const AuthenticationGroups = this.db.get('authenticationGroupsModel');
const GroupClosure = this.db.get('groupClosureModel');
const AuthenticationRoles = this.db.get('authenticationRolesModel');
const GroupRoles = this.db.get('groupRolesModel');
const RolePermissions = this.db.get('rolePermissionsModel');
const Permission = this.db.get('permissionModel');
// 1. USER GROUPS
const userGroups = await AuthenticationGroups.findAll({
where: { Authentication_ObjectGUID: objectGuid }
});
const directGroupIds = userGroups.map(g => g.Group_ObjectGUID);
// 2. NESTED GROUPS
let allGroupIds = [...directGroupIds];
if (directGroupIds.length) {
const closure = await GroupClosure.findAll({
where: { ParentGroup_ObjectGUID: directGroupIds }
});
allGroupIds.push(...closure.map(c => c.ChildGroup_ObjectGUID));
}
allGroupIds = [...new Set(allGroupIds)];
// 3. ROLES
const userRoles = await AuthenticationRoles.findAll({
where: { Authentication_ObjectGUID: objectGuid }
});
const groupRoles = await GroupRoles.findAll({
where: { Group_ObjectGUID: allGroupIds }
});
const roleIds = [
...new Set([
...userRoles.map(r => r.Role_ID),
...groupRoles.map(r => r.Role_ID)
])
];
// 4. PERMISSIONS
const rolePerms = await RolePermissions.findAll({
where: { Role_ID: roleIds }
});
const permissionIds = rolePerms.map(r => r.Permission_ID);
const permissions = await Permission.findAll({
where: { ID: permissionIds }
});
return {
groups: allGroupIds,
roles: roleIds,
permissions: permissions.map(p => ({
id: p.ID,
scope: p.Scope,
resource: p.Resource,
action: p.Action
}))
};
}
// 🔥 SUPER CLEAN CHECK (wiederverwendbar überall)
isSuperAdmin(permissions) {
return permissions.some(p =>
p.scope === 'SYSTEM' &&
p.resource === 'ALL' &&
p.action === 'ALL'
);
}
// 🔥 GENERIC CHECK FUNCTION (WICHTIG)
hasPermission(userPerms, requiredPerms, isSuperAdmin = false) {
if (isSuperAdmin) return true;
return userPerms.some(userPerm =>
requiredPerms.some(required => {
const scopeMatch = userPerm.scope === required.scope;
const actionMatch =
userPerm.action === 'ALL' ||
userPerm.action === required.action ||
required.action === 'ALL';
const resourceMatch =
!userPerm.resource ||
userPerm.resource === 'ALL' ||
userPerm.resource === required.resource;
return scopeMatch && actionMatch && resourceMatch;
})
);
}
// =========================================================
// 🔐 GLOBAL RBAC MIDDLEWARE (app.use)
// =========================================================
//
// USAGE:
// app.get('/admin/users', (req, res) => {
// if (!req.auth.hasPermission([
// { scope: 'USER', action: 'READ', resource: 'USERS' }
// ])) {
// return res.status(403).send('Forbidden');
// }
// res.json({ ok: true });
// });
requirePermissionMiddleware() {
return async (req, res, next) => {
try {
// 🔥 wenn noch kein User da ist → Auth Middleware fehlt
if (!req.user) {
return next(); // oder 401 wenn du streng sein willst
}
const permissions = req.user.permissions || [];
const isSuperAdmin = req.user.isSuperAdmin || false;
req.auth = {
permissions,
isSuperAdmin,
hasPermission: (required) =>
this.hasPermission(permissions, required, isSuperAdmin)
};
next();
} catch (err) {
return res.status(500).json('[RBAC MIDDLEWARE ERROR]', err);
}
};
}
// =========================================================
// 🔥 MIDDLEWARE BLEIBT HIER
// =========================================================
authenticate() {
return async (req, res, next) => {
try {
// =====================================================
// 🔥 GLOBAL PUBLIC ROUTE BYPASS (ROBUST)
// =====================================================
const url = req.originalUrl.split('?')[0];
const publicRoutes = [
'/login',
'/public',
'/css',
'/js',
'/images',
'/favicon.ico'
];
const isPublicRoute = publicRoutes.some(route =>
url === route || url.startsWith(route + '/')
);
if (isPublicRoute) {
return next();
}
// =====================================================
// 🔐 AUTH FLOW
// =====================================================
const sAMAccountName = req.cookies?.sAMAccountName;
if (!sAMAccountName) {
return res.redirect('/login');
}
const user = await this.db.get('authentication').findOne( { where: { sAMAccountName } } );
if (!user || !user.active) {
return res.redirect('/login');
}
let payload;
try {
payload = jwt.verify(user.refreshtoken, this.SECRET_KEY);
} catch {
return res.redirect('/login');
}
const rbac = await this.resolvePermissions(user.ObjectGUID);
const normalized = this.normalize(rbac.permissions);
const isSuperAdmin = this.isSuperAdmin(normalized);
req.user = {
...user.toJSON(),
jwt: payload,
groups: rbac.groups,
roles: rbac.roles,
permissions: normalized,
isSuperAdmin
};
next();
} catch (err) {
console.error(err);
return res.redirect('/login');
}
};
}
normalize(permissions) {
return permissions.map(p => ({
scope: p.scope,
action: p.action,
resource: p.resource || null
}));
}
//#region CRUD
// =========================================================
// 👤 AUTH CRUD
// =========================================================
async getAuth() {
const Auth = this.db.get('authenticationOverviewView');
return await Auth.findAll({ raw: true });
}
async syncAuthByActiveDirectory() {
const auth = await this.db.get('authentication');
const all = await this.service.get('activeDirectoryManager').getAllUsers();
await Promise.all(
all.map(async (user) => {
user = {...user, userAccountControl_ID: user.userAccountControl};
const [record, created] = await auth.findOrCreate({
where: { dn: user.dn },
defaults: user
});
if (!created) {
await record.update(user);
}
})
);
// return all;
}
async createAuth(data) {
const Auth = this.db.get('authentication');
return await Auth.create({
sAMAccountName: data.sAMAccountName,
mail: data.mail,
sn: data.sn,
givenName: data.givenName,
ObjectSource_ID: 1,
active: true
});
}
async updateAuth(id, data) {
const Auth = this.db.get('authentication');
return await Auth.update(data, {
where: { ObjectGUID: id }
});
}
async deleteAuth(id) {
const Auth = this.db.get('authentication');
const AuthGroups = this.db.get('authenticationGroupsModel');
await AuthGroups.destroy({
where: { Authentication_ObjectGUID: id }
});
return await Auth.destroy({
where: { ObjectGUID: id }
});
}
// =========================================================
// 👥 GROUP CRUD
// =========================================================
async getGroup() {
const group = this.db.get('groupOverviewView');
return await group.findAll({ raw: true }) || [];
}
async createGroup(data) {
const group = this.db.get('group');
return await group.create({
Name: data.name,
ObjectSource_ID: 1
});
}
async updateGroup(id, data) {
const Group = this.db.get('group');
return await Group.update(data, {
where: { ObjectGUID: id }
});
}
async deleteGroup(id) {
const Group = this.db.get('group');
const AuthGroups = this.db.get('authenticationGroupsModel');
await AuthGroups.destroy({
where: { Group_ObjectGUID: id }
});
return await Group.destroy({
where: { ObjectGUID: id }
});
}
// =========================================================
// 🔗 AUTH ↔ GROUP RELATION
// =========================================================
async addUserToGroup(authGuid, groupGuid) {
const AuthGroups = this.db.get('authenticationGroupsModel');
if(await AuthGroups.findOne({ where: { Authentication_ObjectGUID: authGuid } })) { // AuthGroups.
return false
}
const group = await AuthGroups.create({
Authentication_ObjectGUID: authGuid,
Group_ObjectGUID: groupGuid
});
return group;
}
async removeUserFromGroup(authId, groupId) {
const AuthGroups = this.db.get('authenticationGroupsModel');
return await AuthGroups.destroy({
where: {
Authentication_ObjectGUID: authId,
Group_ObjectGUID: groupId
}
});
}
// =========================================================
// 🎭 ROLE CRUD
// =========================================================
async getRole() {
const role = this.db.get('roleOverviewView');
return await role.findAll({ raw: true }) || [];
}
async createRole(data) {
const Role = this.db.get('roleModel');
return await Role.create({
Name: data.name,
Description: data.description || null
});
}
async updateRole(id, data) {
const Role = this.db.get('roleModel');
return await Role.update(data, {
where: { ID: id }
});
}
async deleteRole(id) {
const Role = this.db.get('roleModel');
const RolePermissions = this.db.get('rolePermissionsModel');
const GroupRoles = this.db.get('groupRolesModel');
const AuthRoles = this.db.get('authenticationRolesModel');
await AuthRoles.destroy({
where: { Role_ID: id }
});
await GroupRoles.destroy({
where: { Role_ID: id }
});
await RolePermissions.destroy({
where: { Role_ID: id }
});
return await Role.destroy({
where: { ID: id }
});
}
// =========================================================
// 🔗 ROLE ASSIGNMENTS
// =========================================================
async assignRoleToUser(authId, roleId) {
const AuthRoles = this.db.get('authenticationRolesModel');
return await AuthRoles.create({
Authentication_ObjectGUID: authId,
Role_ID: roleId
});
}
async assignRoleToGroup(groupId, roleId) {
const GroupRoles = this.db.get('groupRolesModel');
return await GroupRoles.create({
Group_ObjectGUID: groupId,
Role_ID: roleId
});
}
async removeRoleFromUser(authId, roleId) {
const AuthRoles = this.db.get('authenticationRolesModel');
return await AuthRoles.destroy({
where: {
Authentication_ObjectGUID: authId,
Role_ID: roleId
}
});
}
// =========================================================
// 🔐 PERMISSION CRUD
// =========================================================
async getPermission() {
const permission = this.db.get('permissionOverviewView');
return await permission.findAll({ raw: true }) || [];
}
async createPermission(data) {
const Permission = this.db.get('permissionModel');
return await Permission.create({
Scope: data.scope,
Resource: data.resource,
Action: data.action
});
}
async updatePermission(id, data) {
const Permission = this.db.get('permissionModel');
return await Permission.update(data, {
where: { ID: id }
});
}
async deletePermission(id) {
const Permission = this.db.get('permissionModel');
return await Permission.destroy({
where: { ID: id }
});
}
// =========================================================
// 🔗 ROLE ↔ PERMISSION
// =========================================================
async addPermissionToRole(roleId, permissionId) {
const RolePerms = this.db.get('rolePermissionsModel');
return await RolePerms.create({
Role_ID: roleId,
Permission_ID: permissionId
});
}
async removePermissionFromRole(roleId, permissionId) {
const RolePerms = this.db.get('rolePermissionsModel');
return await RolePerms.destroy({
where: {
Role_ID: roleId,
Permission_ID: permissionId
}
});
}
//#endregion
}
module.exports = RBACManager;