rbac build

This commit is contained in:
2026-04-29 15:44:20 +02:00
parent 90497deebf
commit bbd9441b31
14 changed files with 1006 additions and 635 deletions

View File

@@ -1483,7 +1483,7 @@ window.addEventListener('resize', () => {
},
refresh(){ applyFilters(); render(); },
clearData() { data = [] },
source(newData) { data = []; this.addData(newData); },
source(newData) { data = []; this.addData(newData); this.refresh(); },
prepareData() { prepareData(); }
};
}

View File

@@ -1,46 +1,176 @@
function test() {
function createInput({ id, placeholder }) {
const input = document.createElement('input');
input.type = 'text';
input.id = id;
input.style.width = 'calc(100% - 30px)';
input.placeholder = placeholder;
input.required = true;
return input;
}
const container = document.createElement('div');
container.id = 'rbacAuthCreation';
container.append(
createInput({ id: 'rbacAuthsAMAccountName', placeholder: 'sAMAccountName <Vorname.Nachname>' }),
createInput({ id: 'rbacAuthsMail', placeholder: 'E-Mail' }),
createInput({ id: 'rbacAuthsSn', placeholder: 'Vorname' }),
createInput({ id: 'rbacAuthsGivenName', placeholder: 'Nachname' })
);
feedbox({
title: `<span>Erstelle eine neue Authentifizierung</span>`,
message: container.outerHTML,
buttons: {
cancel: {
text: 'Abbrechen'
},
yes: {
text: '<b>Erstellen</b>',
onClick: () => {
fetch('/api/rbac/auths/create', {
method: 'POST',
body: JSON.stringify({
sAMAccountName: document.getElementById('rbacAuthsAMAccountName').value,
mail: document.getElementById('rbacAuthsMail').value,
sn: document.getElementById('rbacAuthsSn').value,
givenName: document.getElementById('rbacAuthsGivenName').value
})
})
const vt = virtualTable({
tableEl: document.querySelector('#rbacUsersTable'),
data: [],
rowHeight: 20,
buffer: 5,
groupKey: 'ObjectSourceName', // optional zum Gruppieren
rowKey: 'ObjectGUID',
filterConfig: {
exceptedColumns: ['Status_ID', 'Anhänge'],
columnModes: {
ID: 'text', Status: 'dropdown', Objekt: 'text', Priorität: 'dropdown',
Erstelldatum: 'text', Gewerk: 'dropdown', Typ: 'dropdown',
Bedarfsmelder: 'text', Bearbeiter: 'text', Genehmiger: 'text',
Status: 'dropdown'
}
},
customRender: (row, tr) => {
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',
'width': '20px',
'z-index': '2'
}, classes: [
'text-align:left'
], onclick: () => {
sendUserEvent('RBAC', `Benutzer ${row['sn'][0].toUpperCase() + row['sn'].slice(1)}, ${row['givenName'][0].toUpperCase() + row['givenName'].slice(1)} gelöscht`, null, 3);
}
});
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' ] });
createTd(tr, row['ObjectSourceName'], { classes: [ 'text-align:right' ] });
}
},
lock: true
});
});
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 createUser() {
const name = document.getElementById('newUserName').value;
const sn = name.split('.')[1];
const givenName = name.split('.')[0];
const mail = `${name}@test.com`;
const user = await api('/api/rbac/auth/create', 'POST', {
sAMAccountName: name,
mail: mail,
sn: sn[0].toUpperCase() + sn.slice(1),
givenName: givenName[0].toUpperCase() + givenName.slice(1)
});
if(user) {
sendUserEvent('RBAC', `Benutzer ${sn[0].toUpperCase() + sn.slice(1)}, ${givenName[0].toUpperCase() + givenName.slice(1)} angelegt`, null, 0);
loadUsers();
}
}
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();
}
}
// 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');
section.innerHTML = `<span>${group.Name}</span><div class="removeButton" onclick="this.parentNode.remove()">X</div>`;
section.dataset.tooltip = group.Name;
fragment.appendChild(section);
});
rbacGroupContainer.innerHTML = '';
rbacGroupContainer.appendChild(fragment);
return;
}
sendUserEvent('RBAC', 'Gruppen konnten nicht geladen', null, 4);
} catch(err) {
writeEventLog(4, 'RBAC', err);
}
}
loadUsers();
loadGroups();
async function createRole() {
const name = document.getElementById('newRoleName').value;
await api('/api/role', 'POST', {
name
});
loadRoles();
}
async function loadRoles() {
document.getElementById('roleList').innerHTML = 'Reload roles...';
}
async function createPermission() {
const scope = document.getElementById('permScope').value;
const resource = document.getElementById('permResource').value;
const action = document.getElementById('permAction').value;
await api('/permission', 'POST', {
scope,
resource,
action
});
alert('Permission created');
}
async function addUserToGroup(authId, groupId) {
await api('/api/rbac/group/add-user', 'POST', {
authId,
groupId
});
}
async function addPermissionToRole(roleId, permissionId) {
await api('/role/add-permission', 'POST', {
roleId,
permissionId
});
}

View File

@@ -18,7 +18,9 @@ input.width\:25px { width:25px; }
*::placeholder, input[id="sAMAccountName"] { font-style:italic; font-weight:100; letter-spacing:3px; }
html, button { font-size: var(--fontSize); font-family: var(--fontFamily); }
button.monolyth, button.bluebutton, button.greenbutton, button.yellowbutton, button.redbutton { display:inline-block; padding:8px 10px; margin:0.2rem 1.6rem; font-weight:600; text-align:center; text-decoration:none; color:rgb(255, 255, 255); border:none; border-radius:8px; box-shadow:0 4px 6px rgba(0,0,0,0.1); transition:all var(--times-transition-colors) ease; }
/* button.monolyth, button.bluebutton, button.greenbutton, button.yellowbutton, button.redbutton { display:inline-block; padding:8px 10px; margin:0.2rem 1.6rem; font-weight:600; text-align:center; text-decoration:none; color:rgb(255, 255, 255); border:none; border-radius:8px; box-shadow:0 4px 6px rgba(0,0,0,0.1); transition:all var(--times-transition-colors) ease; } */
button.monolyth, button.bluebutton, button.greenbutton, button.yellowbutton, button.redbutton { display:inline-block; padding:4px 8px; margin:0px 5px; font-weight:600; text-align:center; text-decoration:none; color:rgb(255, 255, 255); border:none; border-radius:8px; box-shadow:0 4px 6px rgba(0,0,0,0.1); transition:all var(--times-transition-colors) ease; }
button.monolyth { background-color:transparent; }
button:not(:disabled).monolyth:hover { opacity:0.9; }
button.bluebutton { color:var(--theme-button-blue-default-color); background:var(--theme-button-blue-default-backcolor); }
@@ -406,14 +408,23 @@ label { color:var(--muted); display:block; margin-bottom:6px; }
border-bottom:1px solid #eee;
}
button.removeButton {
.removeButton {
border:none;
background:none;
color:#d11a2a;
background:#d11a2a;
color:white;
cursor:var(--theme-cursor-pointer) -16 16, pointer;
/* font-size:14px; */
padding: 0 4px;
border-radius: 47%;
transition: background var(--times-transition-colors) ease, color var(--times-transition-colors) ease;
}
.removeButton:hover {
background:#ec5d4d;
color:white;
}
input[type="file"] {
display:none;
}

View File

@@ -22,7 +22,7 @@ table thead { position:sticky; top:0; z-index:20; }
/* #endregion */
/* echte Tabelle */
table { width:calc(100%); border-spacing:0 5px; }
table { width:calc(100%); border-spacing:0 2px; }
table th, table td { min-width:100px; max-width:250px; overflow:hidden; white-space:nowrap; }
table tr.grouprow:hover { background: rgba(0,0,0,0.05);}
@@ -35,6 +35,7 @@ thead, tbody { display:table-row-group; }
table thead th { padding:5px; }
/* table tbody td { padding:0 5px; } */
/* table tbody td { padding:5px 0px 5px 20px; } */
table tbody td:not(:first-child):not(:last-child), table thead th:not(:first-child):not(:last-child) { border-width:0; border-style:solid; }
table tbody tr.grouprow { font-weight:700; }
@@ -49,20 +50,21 @@ td { overflow:hidden; text-overflow:ellipsis; /* verhindert, dass Inhalt die Zel
border-bottom-width:8px;
border-bottom-style:solid;
display:flex;
justify-content:flex-start;
flex-direction:column;
flex-wrap:wrap;
gap:10px;
flex-direction:row;
gap:0;
position:sticky;
left:0px;
top:0px;
width: 100% !important;
/* z-index:20; */
padding:5px 10px;
padding:0px;
border-radius:var(--border-raduis) var(--border-raduis) 0 0;
justify-content: flex-start;
align-items: center;
flex: 1;
}
.table-filter-container .live-counter { position:absolute; right:18px; margin-left:auto; font-weight:bold; }
.table-filter-container input, .table-filter-container select { padding:5px !important; }
.table-filter-container input { padding:5px !important; }
th.sort-asc::after {

View File

@@ -4,20 +4,107 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#rbacAdmin {
font-family: Arial;
padding: 20px;
}
#rbacGroupContainer {
display: flex;
width: 100%;
height: 100%;
flex: 1;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
align-content: flex-start;
justify-content: flex-start;
}
section {
display:inline-flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 5px;
width: 120px;
border: 1px solid #ccc;
border-radius: 8px;
margin: 0 2px 2px 0;
}
section span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
input {
margin: 5px;
}
</style>
</head>
<body>
<div class="container grid" style="height:100vh; grid-template-columns: 1fr 1fr;">
<div class="card static" id="rbacEntities">
<!-- USERS -->
<div class="container grid" style="grid-template-columns: calc(50% - 8px) calc(50% - 8px);height:100vh;">
<div class="card">
Users <input id="newUserName" placeholder="sAMAccountName" /> <button class="bluebutton" onclick="createUser()">Create User</button>
<div class="table-wrapper fit-table">
<table id="rbacUsersTable">
<thead>
<tr>
<th class="text-align:left"></th>
<th class="text-align:left">ObjectGUID</th>
<th class="text-align:left">sAMAccountName</th>
<th class="text-align:left">Name</th>
<th class="text-align:left">Vorname</th>
<th class="text-align:left">Mail</th>
<th class="text-align:center">Aktiv</th>
<th class="text-align:center">Online</th>
<th class="text-align:center">Rollen</th>
<th class="text-align:center">Gruppen</th>
<th class="text-align:right">Herkunft</th>
</tr>
</thead>
<tbody>
<tr><td colspan="100%">BENUTZER WERDEN GELADEN . . .</td></tr>
</tbody>
</table>
</div>
</div>
<div class="card static" id="rbacEntityContent">
<!-- GROUPS -->
<div class="card static" style="flex:1 0 100vw;">
<input id="newGroupName" placeholder="Gruppenname" /> <button class="bluebutton" onclick="createGroup()">Create Group</button>
<div id="rbacGroupContainer">
<span>GRUPPEN WERDEN GELADEN . . .</span>
</div>
<input id="newGroupName" placeholder="Group Name" />
<button class="bluebutton" onclick="createGroup()">Create Group</button>
</div>
</div>
<div class="static" style="pointer-events:none;position:absolute; bottom:20px;right:0px;">
<button id="rbacCreateAuthentication" class="yellowbutton" onclick="test()" style="pointer-events:auto">Neuer Benutzer</button>
<!-- ROLES -->
<div class="card">
<h3>Roles</h3>
<div id="roleList"></div>
<input id="newRoleName" placeholder="Role Name" />
<button class="bluebutton" onclick="createRole()">Create Role</button>
</div>
<!-- PERMISSIONS -->
<div class="card">
<h3>Permissions</h3>
<input id="permScope" placeholder="Scope" />
<input id="permResource" placeholder="Resource" />
<input id="permAction" placeholder="Action" />
<button class="bluebutton" onclick="createPermission()">Create Permission</button>
</div>
</div>
</body>
@@ -25,11 +112,5 @@
reloadPluginScript('/javascript/rbacAPI.js');
fetch('/api/rbac/getEntities', { method: 'POST' })
.then(res => res.json())
.then(json => {
});
</script>
</html>