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),
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 })
};
//////////////////////////////
// 🖱️ DRAG & DROP
//////////////////////////////
function createDragZone(el, data) {
el.draggable = true;
el.addEventListener('dragstart', (evt) => {
evt.dataTransfer.setData('application/json', JSON.stringify(data));
});
}
function createDropZone(el, type, target) {
const targetValue = target.ObjectGUID || 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) {
sendUserEvent('RBAC', 'Deinem Drop-Ziel wurde kein ID-Attribut zugewiesen. Frag einfach Manuel ', null, 2);
return;
}
switch (type) {
case 'group':
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`;
process.response = await RBAC.addUserToRole(data.ObjectGUID, targetValue);
break;
case 'group-to-role':
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 den Berechtigung der Rolle hinzugefügt`;
process.response = await RBAC.addPermissionToRole(targetValue, data.ID);
break;
}
if(process.failure) return;
sendUserEvent('RBAC', process.action, null, 0);
});
}
//////////////////////////////
// 📋 TABLE (USERS)
//////////////////////////////
const rbacVT = 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);
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,
``, {
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' ] });
}
});
//////////////////////////////
// 📥 LOADERS
//////////////////////////////
async function loadUsers() {
try {
rbacVT.source(await RBAC.loadUsers());
} 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);
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.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
//////////////////////////////
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 deleteUser(guid, name) {
feedbox({
title: `Benutzer löschen`,
message: `Soll der Benutzer ${name} und
alle seine Zugehörigkeiten entfernt werden`,
buttons: {
no: { text: 'Nein' },
yes: {
text: 'Ja',
onClick: async () => {
const user = await RBAC.deleteUser(guid);
sendUserEvent('RBAC', `Benutzer ${user.sAMAccountName || ''} [${user.ObjectGUID}] gelöscht`, null, 0);
loadUsers();
}
}
}
});
}
//////////////////////////////
// 👥 GROUP ACTIONS
//////////////////////////////
async function deleteGroup(guid, name) {
feedbox({
title: `Gruppe löschen`,
message: `Möchtest du die Gruppe ${name} [${guid}] wirklich löschen`,
buttons: {
no: { text: 'Nein' },
yes: {
text: 'Ja',
onClick: async () => {
const group = await RBAC.deleteGroup(guid);
sendUserEvent('RBAC', `Du hast die Gruppe ${name || ''} [${group.ObjectGUID}] gelöscht`, null, 0);
loadUsers();
loadGroups();
}
}
}
});
}
//////////////////////////////
// 🎭 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: `Rolle löschen`,
message: `Möchtest du die Rolle ${name} wirklich löschen`,
buttons: {
no: { text: 'Nein' },
yes: {
text: 'Ja',
onClick: async () => {
await RBAC.deleteRole(id);
sendUserEvent('RBAC', `Rolle ${name} gelöscht`, null, 0);
loadRoles();
loadUsers(); // optional, falls RoleCount betroffen
}
}
}
});
}
//////////////////////////////
// 🚀 INIT
//////////////////////////////
loadUsers();
loadGroups();
loadRoles();
// const ctx = new ContextMenu();
// const vt = virtualTable({
// tableEl: document.querySelector('#rbacUsersTable'),
// data: [],
// rowHeight: 20,
// buffer: 5,
// groupKey: 'ObjectSourceName', // optional zum Gruppieren
// rowKey: 'ObjectGUID',
// filterConfig: {
// hideCounter: true,
// exceptedColumns: ['', 'Rollen', 'Gruppen'],
// columnModes: {
// Aktiv: 'dropdown',
// Online: 'dropdown'
// }
// },
// customRender: (row, tr) => {
// tr.draggable = true;
// tr.dataset.guid = row['ObjectGUID'];
// tr.dataset.sAMAccountName = row['sAMAccountName'];
// tr.ondragstart = (evt) => {
// const data = {
// guid: tr.dataset.guid,
// sAMAccountName: tr.dataset.sAMAccountName
// };
// evt.dataTransfer.setData('application/json', JSON.stringify(data));
// };
// tr.oncontextmenu = async evt => {
// if (evt.target.tagName === 'BUTTON') return;
// ctx.setItems([ { label: "Details", onClick: () => showAuthDetails(row['ObjectGUID']) } ]);
// evt.preventDefault();
// ctx.show(evt.pageX + 5, {y: evt.pageY + 5 });
// };
// createTd(tr,
// ``, {
// styles: {
// 'position': 'sticky',
// 'left': '0px',
// '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['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['active'], { classes: [ 'text-align:center' ] });
// createTd(tr, row['online'], { classes: [ 'text-align:center' ] });
// createTd(tr, row['RoleCount'], { classes: [ 'text-align:center' ] });
// createTd(tr, row['GroupCount'], { classes: [ 'text-align:center' ] });
// }
// });
// async function api(url, method = 'GET', body) {
// const res = await fetch(url, {
// method,
// headers: { 'Content-Type': 'application/json' },
// body: body ? JSON.stringify(body) : undefined
// });
// return res.json();
// }
// async function showAuthDetails(guid) {
// const details = await api(`/api/rbac/auth/details/${guid}`)
// if(!details) return;
// feedbox({
// title: `Details`,
// message: Object.entries(details).map(([key, value]) => /*html*/`
${key}:${value}
`).join(''), // buttons: { // yes: { // text: 'Schließen' // } // }, // lock: true, // primary: 'yes' // }) // } // async function createUser() { // const name = document.getElementById('newUserName').value; // if(!name || !name.includes('.')) { // sendUserEvent('RBAC', 'Der sAMAccountName muss einen Punkt enthaltenMöchtest du den Benutzer ${sAMAccountName} [${guid}] wirklich löschen
// `, // buttons: { // no: { // text: 'Nein' // }, // yes: { // text: 'Ja', // onClick: async () => { // const user = await api(`/api/rbac/auth/${guid}`, 'DELETE', { // guid // }); // if(user) { // loadUsers(); // sendUserEvent('RBAC', `Benutzer ${user.sAMAccountName || ''} [${user.ObjectGUID}] gelöscht`, null, 0); // } // } // } // } // }); // } // async function loadUsers() { // try { // const users = await api('/api/rbac/auth/get', 'POST'); // if(users) { // vt.source(users); // return; // } // sendUserEvent('RBAC', 'Benutzer konnten nicht geladen', null, 4); // } catch(err) { // writeEventLog(4, 'RBAC', err); // } // } // 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 deleteGroup(guid, name) { // feedbox({ // title: `Gruppe löschen`, // message: ` //Möchtest du die Gruppe ${name} [${guid}] wirklich löschen
// `, // buttons: { // no: { // text: 'Nein' // }, // yes: { // text: 'Ja', // onClick: async () => { // const group = await api(`/api/rbac/group/${guid}`, 'DELETE', { // guid // }); // if(group) { // sendUserEvent('RBAC', `Gruppe ${group.Name || ''} [${group.ObjectGUID}] gelöscht`, null, 0); // loadGroups(); // } // } // } // } // }); // // 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(); // // } // } // // HIER WEITER - GRUPPEN KARTEN MÜSSEN HÜBSCHER WERDEN. // // BENUTZER UND GRUPPEN KÖNNEN NOCH NICHT GELÖSCHT WERDEN. // // GRUPPEN AUCH OBJECTSOURCE_ID 1? // async function loadGroups() { // try { // const rbacGroupContainer = document.getElementById('rbacGroupContainer'); // rbacGroupContainer.innerHTML = ''; // const groups = await api('/api/rbac/group/get', 'POST'); // if(groups) { // let fragment = document.createDocumentFragment(); // groups.forEach(group => { // const section = document.createElement('section'); // createDropZone(section, 'group', group.ObjectGUID); // section.innerHTML = `${group.Name}