diff --git a/dbcreate.sql b/dbcreate.sql index ca4be61..f2e4d9c 100644 --- a/dbcreate.sql +++ b/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) -- ======================================================== diff --git a/public/javascript/rbacAPI.js b/public/javascript/rbacAPI.js index e343a87..c4b66fc 100644 --- a/public/javascript/rbacAPI.js +++ b/public/javascript/rbacAPI.js @@ -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: `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 @@ -308,7 +382,7 @@ async function deleteGroup(guid, name) { loadUsers(); loadGroups(); - +loadRoles(); // const ctx = new ContextMenu(); diff --git a/public/views/rbac.hbs b/public/views/rbac.hbs index 53a8fb9..59e17ce 100644 --- a/public/views/rbac.hbs +++ b/public/views/rbac.hbs @@ -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 { -