509 lines
12 KiB
JavaScript
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; |