rbac roles implementation
This commit is contained in:
10
dbcreate.sql
10
dbcreate.sql
@@ -492,6 +492,16 @@ JOIN dbo.[Role] r
|
||||
GO
|
||||
|
||||
|
||||
CREATE OR ALTER VIEW dbo.vRoleOverview AS
|
||||
SELECT r.ID, r.Name, r.Description, r.RoleType, COUNT(gr.Group_ObjectGUID) AS GroupCount, COUNT(vau.ObjectGUID) AS UserCount
|
||||
FROM dbo.Role AS r LEFT OUTER JOIN
|
||||
dbo.vAuthenticationRoles AS vau ON vau.Role_ID = r.ID LEFT OUTER JOIN
|
||||
dbo.GroupRoles AS gr ON gr.Role_ID = r.ID LEFT OUTER JOIN
|
||||
dbo.AuthenticationGroups AS ag ON ag.Group_ObjectGUID = gr.Group_ObjectGUID
|
||||
GROUP BY r.ID, r.Name, r.Description, r.RoleType
|
||||
GO
|
||||
|
||||
|
||||
-- ========================================================
|
||||
-- 4. PERMISSIONS (DETAILED WITH ROLE SOURCE)
|
||||
-- ========================================================
|
||||
|
||||
@@ -22,7 +22,6 @@ const api = async (url, method = 'GET', body) => {
|
||||
//////////////////////////////
|
||||
|
||||
const RBAC = {
|
||||
|
||||
// 👤 USERS
|
||||
loadUsers: async () => (await api('/api/rbac/auth/get', 'POST'))
|
||||
.map(({ active, online, ...rest }) => ({
|
||||
@@ -30,31 +29,24 @@ const RBAC = {
|
||||
Aktiv: active
|
||||
}))
|
||||
.sort((a, b) => a.sAMAccountName.localeCompare(b.sAMAccountName)),
|
||||
|
||||
createUser: (data) => api('/api/rbac/auth/create', 'POST', data),
|
||||
|
||||
deleteUser: (guid) => api(`/api/rbac/auth/${guid}`, 'DELETE'),
|
||||
|
||||
// 👥 GROUPS
|
||||
loadGroups: () => api('/api/rbac/group/get', 'POST'),
|
||||
|
||||
createGroup: (name) => api('/api/rbac/group/create', 'POST', { name }),
|
||||
|
||||
deleteGroup: (guid) => api(`/api/rbac/group/${guid}`, 'DELETE'),
|
||||
|
||||
// 🎭 ROLES
|
||||
loadRoles: () => api('/api/rbac/role/get', 'POST'),
|
||||
createRole: (name) => api('/api/rbac/role', 'POST', { name }),
|
||||
deleteRole: (id) => api(`/api/rbac/role/${id}`, 'DELETE'),
|
||||
|
||||
// 🔗 ASSIGNMENTS
|
||||
addUserToGroup: (authGuid, groupGuid) =>
|
||||
api('/api/rbac/group/add-user', 'POST', { authGuid, groupGuid }),
|
||||
|
||||
addUserToRole: (authGuid, roleId) =>
|
||||
api('/api/rbac/role/add-user', 'POST', { authGuid, roleId }),
|
||||
|
||||
addGroupToRole: (groupGuid, roleId) =>
|
||||
api('/api/rbac/role/add-group', 'POST', { groupGuid, roleId }),
|
||||
|
||||
addPermissionToRole: (roleId, permissionId) =>
|
||||
api('/api/rbac/role/add-permission', 'POST', { roleId, permissionId })
|
||||
|
||||
addUserToGroup: (authGuid, groupGuid) => api('/api/rbac/group/add-user', 'POST', { authGuid, groupGuid }),
|
||||
addUserToRole: (authGuid, roleId) => api('/api/rbac/role/add-user', 'POST', { authGuid, roleId }),
|
||||
addGroupToRole: (groupGuid, roleId) => api('/api/rbac/role/add-group', 'POST', { groupGuid, roleId }),
|
||||
addPermissionToRole: (roleId, permissionId) => api('/api/rbac/role/add-permission', 'POST', { roleId, permissionId })
|
||||
};
|
||||
|
||||
//////////////////////////////
|
||||
@@ -90,6 +82,7 @@ function createDropZone(el, type, target) {
|
||||
process.action = `Du hast den Benutzer der Gruppe hinzugefügt`;
|
||||
process.response = await RBAC.addUserToGroup(data.ObjectGUID, targetValue);
|
||||
loadUsers();
|
||||
loadGroups();
|
||||
break;
|
||||
case 'role':
|
||||
process.action = `Du hast den Benutzer der Rolle hinzugefügt`;
|
||||
@@ -116,7 +109,7 @@ function createDropZone(el, type, target) {
|
||||
// 📋 TABLE (USERS)
|
||||
//////////////////////////////
|
||||
|
||||
const vt = virtualTable({
|
||||
const rbacVT = virtualTable({
|
||||
tableEl: document.querySelector('#rbacUsersTable'),
|
||||
data: [],
|
||||
rowHeight: 20,
|
||||
@@ -184,7 +177,7 @@ const vt = virtualTable({
|
||||
|
||||
async function loadUsers() {
|
||||
try {
|
||||
vt.source(await RBAC.loadUsers());
|
||||
rbacVT.source(await RBAC.loadUsers());
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
@@ -193,29 +186,29 @@ async function loadUsers() {
|
||||
async function loadGroups() {
|
||||
const container = document.getElementById('rbacGroupContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
const groups = await RBAC.loadGroups();
|
||||
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
groups.forEach(group => {
|
||||
|
||||
const section = document.createElement('section');
|
||||
const groupName = document.createElement('span');
|
||||
groupName.innerHTML = group.Name;
|
||||
groupName.innerHTML = `[${group.UserCount}] ${group.Name}`;
|
||||
groupName.dataset.tooltip = group.Name;
|
||||
groupName.dataset.tooltipMode = 'ellipsis';
|
||||
section.append(groupName);
|
||||
|
||||
if(group.ObjectGUID !== '00000000-0000-0000-0000-000000000001') {
|
||||
const removeButton = document.createElement('div');
|
||||
removeButton.className = 'removeButton';
|
||||
removeButton.innerHTML = 'X';
|
||||
removeButton.onclick = async () => {
|
||||
await deleteGroup(group.ObjectGUID, group.Name);
|
||||
};
|
||||
section.append(removeButton);
|
||||
const removeButton = document.createElement('button');
|
||||
|
||||
if(group.ObjectGUID === '00000000-0000-0000-0000-000000000001') {
|
||||
removeButton.disabled = true;
|
||||
removeButton.dataset.tooltip = `Die Gruppe ${group.Name} kann nicht gelöscht werden`;
|
||||
}
|
||||
removeButton.className = 'removeButton';
|
||||
removeButton.innerHTML = 'X';
|
||||
removeButton.onclick = async () => {
|
||||
await deleteGroup(group.ObjectGUID, group.Name);
|
||||
};
|
||||
section.append(removeButton);
|
||||
|
||||
|
||||
createDropZone(section, 'group', group);
|
||||
|
||||
@@ -236,6 +229,49 @@ async function createGroup() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function loadRoles() {
|
||||
const container = document.getElementById('rbacRoleContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
const roles = await RBAC.loadRoles();
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
roles.forEach(role => {
|
||||
|
||||
const section = document.createElement('section');
|
||||
|
||||
const roleName = document.createElement('span');
|
||||
roleName.innerHTML = role.Name;
|
||||
roleName.dataset.tooltip = role.Name;
|
||||
|
||||
section.append(roleName);
|
||||
|
||||
const removeButton = document.createElement('button');
|
||||
removeButton.className = 'removeButton';
|
||||
removeButton.innerHTML = 'X';
|
||||
|
||||
removeButton.onclick = async () => {
|
||||
await deleteRole(role.ID, role.Name);
|
||||
};
|
||||
|
||||
section.append(removeButton);
|
||||
|
||||
// 🔥 DROP ZONES
|
||||
createDropZone(section, 'role', role); // User → Role
|
||||
createDropZone(section, 'group-to-role', role); // Group → Role
|
||||
|
||||
// optional: permissions
|
||||
createDropZone(section, 'permission', role);
|
||||
|
||||
fragment.appendChild(section);
|
||||
});
|
||||
|
||||
container.appendChild(fragment);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
// 👤 USER ACTIONS
|
||||
//////////////////////////////
|
||||
@@ -301,6 +337,44 @@ async function deleteGroup(guid, name) {
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
// 🎭 ROLE ACTIONS
|
||||
//////////////////////////////
|
||||
|
||||
|
||||
async function createRole() {
|
||||
const name = document.getElementById('newRoleName').value;
|
||||
|
||||
if (!name) return;
|
||||
|
||||
const role = await RBAC.createRole(name);
|
||||
|
||||
sendUserEvent('RBAC', `Rolle ${name} angelegt`, null, 0);
|
||||
|
||||
loadRoles();
|
||||
}
|
||||
|
||||
|
||||
async function deleteRole(id, name) {
|
||||
feedbox({
|
||||
title: `<span>Rolle löschen</span>`,
|
||||
message: `Möchtest du die Rolle <b style="color:red;">${name}</b> wirklich löschen`,
|
||||
buttons: {
|
||||
no: { text: 'Nein' },
|
||||
yes: {
|
||||
text: '<b style=color:red>Ja</b>',
|
||||
onClick: async () => {
|
||||
await RBAC.deleteRole(id);
|
||||
|
||||
sendUserEvent('RBAC', `Rolle ${name} gelöscht`, null, 0);
|
||||
|
||||
loadRoles();
|
||||
loadUsers(); // optional, falls RoleCount betroffen
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// 🚀 INIT
|
||||
@@ -308,7 +382,7 @@ async function deleteGroup(guid, name) {
|
||||
|
||||
loadUsers();
|
||||
loadGroups();
|
||||
|
||||
loadRoles();
|
||||
|
||||
|
||||
// const ctx = new ContextMenu();
|
||||
|
||||
@@ -36,6 +36,7 @@ section {
|
||||
transition: background var(--times-transition-colors) ease, border-color var(--times-transition-colors) ease, color var(--times-transition-colors) ease;
|
||||
}
|
||||
|
||||
|
||||
section:hover {
|
||||
background: var(--theme-accent-hover-backcolor);
|
||||
color: var(--theme-accent-hover-color);
|
||||
@@ -101,7 +102,7 @@ input {
|
||||
<input id="newRoleName" placeholder="Role Name" />
|
||||
<button class="bluebutton" onclick="createRole()">Create Role</button>
|
||||
|
||||
<div id="rbacGroupContainer">
|
||||
<div id="rbacRoleContainer">
|
||||
<span>ROLLEN WERDEN GELADEN . . .</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -116,6 +116,7 @@ const server = https.createServer(httpsOptions, app);
|
||||
|
||||
databaseModel.set('authenticationOverviewView', require(`@models/authenticationOverviewView`)(service.get('sqlManager').getInstance('main')));
|
||||
databaseModel.set('groupOverviewView', require(`@models/groupOverviewView`)(service.get('sqlManager').getInstance('main')));
|
||||
databaseModel.set('roleOverviewView', require(`@models/roleOverviewView`)(service.get('sqlManager').getInstance('main')));
|
||||
|
||||
service.set('rbacManager', new RBACManager(databaseModel, runtimeFile.configuration.live.integration.token.secret));
|
||||
service.set('authenticationManager', new AuthenticationManager(databaseModel.get('authentication'), runtimeFile.configuration.live.integration.token.secret));
|
||||
|
||||
36
src/models/roleOverviewView.js
Normal file
36
src/models/roleOverviewView.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize ) => {
|
||||
return sequelize.define('vRoleOverview', {
|
||||
|
||||
ID: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true
|
||||
},
|
||||
|
||||
Name: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
|
||||
Description: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
|
||||
RoleType: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
|
||||
GroupCount: {
|
||||
type: DataTypes.INTEGER
|
||||
},
|
||||
|
||||
UserCount: {
|
||||
type: DataTypes.INTEGER
|
||||
}
|
||||
|
||||
}, {
|
||||
tableName: 'vRoleOverview', // 🔥 wichtig: Name deiner VIEW
|
||||
timestamps: false,
|
||||
freezeTableName: true
|
||||
});
|
||||
};
|
||||
@@ -249,17 +249,31 @@ module.exports = {
|
||||
// =========================================================
|
||||
// 🎭 ROLES
|
||||
// =========================================================
|
||||
|
||||
app.post('/api/role', async (req, res) => {
|
||||
app.post('/api/rbac/role/get', async (req, res) => {
|
||||
try {
|
||||
const role = await service.get('rbacManager').createRole(req.body);
|
||||
res.json(role);
|
||||
rbacRoles = await service.get('rbacManager').getRole();
|
||||
res.json(rbacRoles);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/role/:id', async (req, res) => {
|
||||
app.post('/api/rbac/role/create', async (req, res) => {
|
||||
try {
|
||||
if(rbacRoles.map(role => role.Name.toLowerCase()).includes(req.body.name.toLowerCase())) {
|
||||
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 2, 'RBAC', `${req.body.name} nicht angelegt.\r\nGruppe existiert bereits`);
|
||||
return res.status(400).json({ error: `${req.body.name} existiert bereits` });
|
||||
}
|
||||
const role = await service.get('rbacManager').createRole(req.body);
|
||||
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 0, 'RBAC', `Gruppe ${role.Name} [${role.ID}] angelegt`);
|
||||
res.json(role);
|
||||
} catch (err) {
|
||||
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', err.message);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/api/rbac/role/:id', async (req, res) => {
|
||||
try {
|
||||
await service.get('rbacManager').updateRole(req.params.id, req.body);
|
||||
res.json({ ok: true });
|
||||
@@ -268,11 +282,18 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/role/:id', async (req, res) => {
|
||||
try {
|
||||
await service.get('rbacManager').deleteRole(req.params.id);
|
||||
res.json({ ok: true });
|
||||
app.delete('/api/rbac/role/:id', async (req, res) => {
|
||||
try {
|
||||
const role = await databaseModel.get('role').findOne({ where: { ID: req.params.id }, raw: true });
|
||||
if(!role) {
|
||||
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 2, 'RBAC', `Gruppen-GUID ${req.params.guid} nicht gefunden`);
|
||||
res.status(400);
|
||||
}
|
||||
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 0, 'RBAC', `Gruppe ${role.Name} [${role.ID}] gelöscht`);
|
||||
await service.get('rbacManager').deleteRole(role.ID);
|
||||
res.status(200).json(role);
|
||||
} catch (err) {
|
||||
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -346,6 +346,10 @@ async removeUserFromGroup(authId, 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('rolesModel');
|
||||
|
||||
Reference in New Issue
Block a user