534 lines
16 KiB
JavaScript
534 lines
16 KiB
JavaScript
const ctx = new ContextMenu();
|
|
|
|
//////////////////////////////
|
|
// 🌐 API LAYER
|
|
//////////////////////////////
|
|
|
|
const api = async (url, method = 'GET', body) => {
|
|
const res = await fetch(url, {
|
|
method,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: body ? JSON.stringify(body) : undefined
|
|
});
|
|
if(res.status >= 400) {
|
|
const text = await res.text();
|
|
sendUserEvent('RBAC', `Hoppla, da ist etwas schief gelaufen:\r\n${text}`, null, 4);
|
|
}
|
|
return res.json();
|
|
};
|
|
|
|
//////////////////////////////
|
|
// 🧠 RBAC SERVICE LAYER
|
|
//////////////////////////////
|
|
|
|
const RBAC = {
|
|
// 👤 USERS
|
|
loadUsers: async () => (await api('/api/rbac/auth/get', 'POST'))
|
|
.map(({ active, online, ...rest }) => ({
|
|
...rest,
|
|
Aktiv: active
|
|
}))
|
|
.sort((a, b) => a.sAMAccountName.localeCompare(b.sAMAccountName)),
|
|
createUser: (data) => api('/api/rbac/auth/create', 'POST', data),
|
|
syncUsersFromAD: () => api('/api/rbac/auth/syncFromAD', 'POST', { }),
|
|
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 }),
|
|
syncGroupsFromAD: () => api('/api/rbac/group/syncFromAD', 'POST', { }),
|
|
deleteGroup: (guid) => api(`/api/rbac/group/${guid}`, 'DELETE'),
|
|
|
|
// 🎭 ROLES
|
|
loadRoles: () => api('/api/rbac/role/get', 'POST'),
|
|
createRole: (name) => api('/api/rbac/role/create', 'POST', { name }),
|
|
deleteRole: (id) => api(`/api/rbac/role/${id}`, 'DELETE'),
|
|
|
|
// 🎭 PERMISSIONS
|
|
loadPermissions: () => api('/api/rbac/permission/get', 'POST'),
|
|
createPermission: (data) => api('/api/rbac/permission/create', 'POST', data),
|
|
deletePermission: (id) => api(`/api/rbac/permission/${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 })
|
|
};
|
|
|
|
//////////////////////////////
|
|
// 🖱️ DRAG & DROP
|
|
//////////////////////////////
|
|
|
|
function createDragZone(el, data, type) {
|
|
el.draggable = true;
|
|
el.addEventListener('dragstart', (evt) => {
|
|
evt.dataTransfer.setData('application/json', JSON.stringify( { ...data, type }));
|
|
});
|
|
}
|
|
|
|
function createDropZone(el, type, target) {
|
|
const targetValue = target.ObjectGUID || target.ID || target.Role_ID || target.Permission_ID;
|
|
let process = { action: null, response: null, failure: false };
|
|
el.addEventListener('dragover', e => e.preventDefault());
|
|
|
|
el.addEventListener('drop', async (e) => {
|
|
e.preventDefault();
|
|
|
|
const data = JSON.parse(
|
|
e.dataTransfer.getData('application/json')
|
|
);
|
|
|
|
if(!targetValue || !data.type) {
|
|
sendUserEvent('RBAC', 'Deinem Drop-Ziel wurde kein ID-Attribut zugewiesen. Frag einfach Manuel ', null, 2);
|
|
return;
|
|
}
|
|
|
|
// NOCH NICHT UNTER ADMINROUTES EDITIERT: WENN BENUTZER/GRUPPE BEREITS IN ROLLE ENTHALTEN IST
|
|
switch (type) {
|
|
case 'group':
|
|
if(data.type === 'user') {
|
|
process.action = `Du hast den Benutzer der Gruppe hinzugefügt`;
|
|
process.response = await RBAC.addUserToGroup(data.ObjectGUID, targetValue);
|
|
}
|
|
break;
|
|
case 'role':
|
|
if(data.type === 'user') {
|
|
process.action = `Du hast den Benutzer der Rolle hinzugefügt`;
|
|
process.response = await RBAC.addUserToRole(data.ObjectGUID, targetValue);
|
|
break;
|
|
}
|
|
if(data.type === 'group') {
|
|
process.action = `Du hast die Gruppe der Rolle hinzugefügt`;
|
|
process.response = await RBAC.addGroupToRole(data.ObjectGUID, targetValue);
|
|
break;
|
|
}
|
|
case 'permission':
|
|
process.action = `Du hast die Berechtigung der Rolle hinzugefügt`;
|
|
process.response = await RBAC.addPermissionToRole(targetValue, data.ID);
|
|
break;
|
|
}
|
|
|
|
if(process.failure || !process.action) return;
|
|
loadUsers();
|
|
loadGroups();
|
|
loadRoles();
|
|
sendUserEvent('RBAC', process.action, null, 0);
|
|
});
|
|
}
|
|
|
|
|
|
//////////////////////////////
|
|
// 📋 TABLE (USERS)
|
|
//////////////////////////////
|
|
|
|
const rbacUsersVT = virtualTable({
|
|
tableEl: document.querySelector('#rbacUsersTable'),
|
|
data: [],
|
|
rowHeight: 20,
|
|
buffer: 5,
|
|
groupKey: 'ObjectSourceName',
|
|
rowKey: 'ObjectGUID',
|
|
filterConfig: {
|
|
hideCounter: true,
|
|
exceptedColumns: ['', 'Rollen', 'Gruppen'],
|
|
columnModes: {
|
|
Aktiv: 'dropdown',
|
|
Online: 'dropdown'
|
|
}
|
|
},
|
|
|
|
customRender: (row, tr) => {
|
|
|
|
createDragZone(tr, row, 'user');
|
|
|
|
tr.addEventListener('contextmenu', (evt) => {
|
|
evt.preventDefault();
|
|
|
|
ctx.setItems([
|
|
{
|
|
label: "Details",
|
|
onClick: () => showAuthDetails(row.ObjectGUID)
|
|
}
|
|
]);
|
|
|
|
ctx.show(evt.pageX + 5, { y: evt.pageY + 5 });
|
|
});
|
|
|
|
createTd(tr,
|
|
`<button class="redbutton"
|
|
${row['ObjectGUID'] === '00000000-0000-0000-0000-000000000001' ?
|
|
'disabled data-tooltip="Der Administrator kann nicht gelöscht werden"' :
|
|
''
|
|
}>X</button>`, {
|
|
styles: {
|
|
'position': 'sticky',
|
|
'left': '0px',
|
|
'max-width': '20px',
|
|
'z-index': '2'
|
|
}, classes: [
|
|
'text-align:left'
|
|
], onclick: () => {
|
|
if(row['ObjectGUID'] === '00000000-0000-0000-0000-000000000001') return;
|
|
deleteUser(row['ObjectGUID'], row['sAMAccountName']);
|
|
}
|
|
});
|
|
createTd(tr, row['ObjectGUID'], { classes: [ 'text-align:left' ], styles: { 'max-width': '100px' }, attributes: { 'data-tooltip': row['ObjectGUID'] } });
|
|
createTd(tr, row['sAMAccountName'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['sAMAccountName'] } });
|
|
createTd(tr, row['RoleCount'], { classes: [ 'text-align:center' ] });
|
|
createTd(tr, row['GroupCount'], { classes: [ 'text-align:center' ] });
|
|
createTd(tr, row['sn'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['sn'] } });
|
|
createTd(tr, row['givenName'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['givenName'] } });
|
|
createTd(tr, row['mail'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['mail'] } });
|
|
createTd(tr, row['Aktiv'], { classes: [ 'text-align:center' ] });
|
|
}
|
|
});
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
// 📋 TABLE (PERMISSIONS)
|
|
//////////////////////////////
|
|
|
|
const rbacPermissionsVT = virtualTable({
|
|
tableEl: document.querySelector('#rbacPermissionsTable'),
|
|
data: [],
|
|
rowHeight: 20,
|
|
buffer: 5,
|
|
groupKey: null,
|
|
rowKey: 'ID',
|
|
filterConfig: {
|
|
hideCounter: true,
|
|
exceptedColumns: ['', 'ID']
|
|
},
|
|
|
|
customRender: (row, tr) => {
|
|
|
|
createDragZone(tr, row, 'user');
|
|
|
|
tr.addEventListener('contextmenu', (evt) => {
|
|
evt.preventDefault();
|
|
|
|
ctx.setItems([
|
|
{
|
|
label: "Details",
|
|
onClick: () => showAuthDetails(row.ObjectGUID)
|
|
}
|
|
]);
|
|
|
|
ctx.show(evt.pageX + 5, { y: evt.pageY + 5 });
|
|
});
|
|
|
|
createTd(tr,
|
|
`<button class="redbutton"
|
|
${row['ID'] === 1 ?
|
|
'disabled data-tooltip="Die SYSTEM-Berechtigung kann nicht gelöscht werden"' :
|
|
''
|
|
}>X</button>`, {
|
|
styles: {
|
|
'position': 'sticky',
|
|
'left': '0px',
|
|
'max-width': '20px',
|
|
'z-index': '2'
|
|
}, classes: [
|
|
'text-align:left'
|
|
], onclick: () => {
|
|
if(row['ID'] === 1) return;
|
|
deletePermission(row['ID'], `${row['Scope']}.${row['Resource']}.${row['Action']}`);
|
|
}
|
|
});
|
|
createTd(tr, row['Permission_ID'], { classes: [ 'text-align:left' ], styles: { 'width': '100px' } } );
|
|
createTd(tr, row['GroupUserCount'], { classes: [ 'text-align:center' ] });
|
|
createTd(tr, row['TotalUserCount'], { classes: [ 'text-align:center' ] });
|
|
createTd(tr, row['RoleCount'], { classes: [ 'text-align:center' ], styles: { 'width': '100px' } });
|
|
createTd(tr, row['Scope'], { classes: [ 'text-align:right' ], styles: { 'width': '100px' } });
|
|
createTd(tr, row['Resource'], { classes: [ 'text-align:center' ], styles: { 'width': '100px' } });
|
|
createTd(tr, row['Action'], { classes: [ 'text-align:left' ], styles: { 'width': '100px' } });
|
|
}
|
|
});
|
|
|
|
//////////////////////////////
|
|
// 📥 LOADERS
|
|
//////////////////////////////
|
|
|
|
async function loadUsers() {
|
|
try {
|
|
rbacUsersVT.source(await RBAC.loadUsers());
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
}
|
|
|
|
async function loadPermissions() {
|
|
try {
|
|
rbacPermissionsVT.source(await RBAC.loadPermissions());
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
}
|
|
|
|
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.UserCount}] ${group.Name}`;
|
|
groupName.dataset.tooltip = group.Name;
|
|
groupName.dataset.tooltipMode = 'ellipsis';
|
|
section.append(groupName);
|
|
|
|
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);
|
|
|
|
createDragZone(section, group, 'group');
|
|
createDropZone(section, 'group', group);
|
|
|
|
fragment.appendChild(section);
|
|
});
|
|
|
|
container.appendChild(fragment);
|
|
}
|
|
|
|
async function createGroup() {
|
|
const name = document.getElementById('newGroupName').value;
|
|
const group = await api('/api/rbac/group/create', 'POST', {
|
|
name
|
|
});
|
|
if(group) {
|
|
sendUserEvent('RBAC', `Gruppe ${name} angelegt`, null, 0);
|
|
loadGroups();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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.UserCount}][${role.GroupCount}] ${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);
|
|
|
|
createDropZone(section, 'role', role);
|
|
|
|
fragment.appendChild(section);
|
|
});
|
|
|
|
container.appendChild(fragment);
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
// 👤 USER ACTIONS
|
|
//////////////////////////////
|
|
|
|
async function createUser() {
|
|
const name = document.getElementById('newUserName').value;
|
|
|
|
if (!name || !name.includes('.')) return;
|
|
|
|
const [givenName, sn] = name.split('.');
|
|
|
|
const user = await RBAC.createUser({
|
|
sAMAccountName: name,
|
|
mail: `${name}@test.com`,
|
|
givenName: givenName[0].toUpperCase() + givenName.slice(1),
|
|
sn: sn[0].toUpperCase() + sn.slice(1)
|
|
});
|
|
|
|
sendUserEvent('RBAC', `Benutzer ${user.sAMAccountName} [${user.ObjectGUID}] angelegt`, null, 0);
|
|
loadUsers();
|
|
}
|
|
|
|
|
|
async function syncUsersFromAD() {
|
|
const users = await RBAC.syncUsersFromAD();
|
|
sendUserEvent('RBAC', `${users.length} Benutzer aus dem AD synchronisiert`, null, 0);
|
|
loadUsers();
|
|
}
|
|
|
|
async function deleteUser(guid, name) {
|
|
feedbox({
|
|
title: `<span>Benutzer löschen</span>`,
|
|
message: `Soll der Benutzer <b>${name}</b> und<br><b>alle seine Zugehörigkeiten</b> entfernt werden`,
|
|
buttons: {
|
|
no: { text: 'Nein' },
|
|
yes: {
|
|
text: '<b style=color:red>Ja</b>',
|
|
onClick: async () => {
|
|
const user = await RBAC.deleteUser(guid);
|
|
sendUserEvent('RBAC', `Benutzer ${user.sAMAccountName || ''} [${user.ObjectGUID}] gelöscht`, null, 0);
|
|
loadUsers();
|
|
loadGroups();
|
|
loadRoles();
|
|
loadPermissions();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
//////////////////////////////
|
|
// 👥 GROUP ACTIONS
|
|
//////////////////////////////
|
|
|
|
async function deleteGroup(guid, name) {
|
|
feedbox({
|
|
title: `<span>Gruppe löschen</span>`,
|
|
message: `Möchtest du die Gruppe <b style="color:red;">${name} [${guid}]</b> wirklich löschen`,
|
|
buttons: {
|
|
no: { text: 'Nein' },
|
|
yes: {
|
|
text: '<b style=color:red>Ja</b>',
|
|
onClick: async () => {
|
|
const group = await RBAC.deleteGroup(guid);
|
|
sendUserEvent('RBAC', `Du hast die Gruppe ${name || ''} [${group.ObjectGUID}] gelöscht`, null, 0);
|
|
loadUsers();
|
|
loadGroups();
|
|
loadRoles();
|
|
loadPermissions();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
async function syncGroupsFromAD() {
|
|
const group = await RBAC.syncGroupsFromAD();
|
|
|
|
sendUserEvent('RBAC', `${group.length} Gruppen aus dem AD synchronisiert`, null, 0);
|
|
|
|
loadGroups();
|
|
loadUsers();
|
|
}
|
|
|
|
|
|
//////////////////////////////
|
|
// 🎭 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);
|
|
|
|
loadUsers();
|
|
loadGroups();
|
|
loadRoles();
|
|
loadPermissions(); // optional, falls RoleCount betroffen
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
//////////////////////////////
|
|
// 🎭 PERMISSION ACTIONS
|
|
//////////////////////////////
|
|
|
|
async function createPermission() {
|
|
const scope = document.getElementById('permScope').value;
|
|
const resource = document.getElementById('permResource').value;
|
|
const action = document.getElementById('permAction').value;
|
|
|
|
if (!scope || !resource || !action) return;
|
|
|
|
|
|
const permission = await RBAC.createPermission( { scope, resource, action } );
|
|
|
|
sendUserEvent('RBAC', `Berechtigung ${scope}.${resource}.${action} angelegt`, null, 0);
|
|
loadPermissions();
|
|
}
|
|
|
|
|
|
async function deletePermission(id, name) {
|
|
feedbox({
|
|
title: `<span>Berechtigung löschen</span>`,
|
|
message: `Möchtest du die Berechtigung <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.deletePermission(id);
|
|
|
|
sendUserEvent('RBAC', `Berechtigung ${name} gelöscht`, null, 0);
|
|
|
|
loadUsers();
|
|
loadGroups();
|
|
loadRoles();
|
|
loadPermissions();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
//////////////////////////////
|
|
// 🚀 INIT
|
|
//////////////////////////////
|
|
|
|
loadUsers();
|
|
loadGroups();
|
|
loadRoles();
|
|
loadPermissions(); |