rbac and licenses
This commit is contained in:
65
dbcreate.sql
65
dbcreate.sql
@@ -44,26 +44,27 @@ GO
|
|||||||
-- DROP TABLE IF EXISTS dbo.ObjectSource;
|
-- DROP TABLE IF EXISTS dbo.ObjectSource;
|
||||||
-- DROP TABLE IF EXISTS dbo.AuthenticationUAC;
|
-- DROP TABLE IF EXISTS dbo.AuthenticationUAC;
|
||||||
-- DROP TABLE IF EXISTS dbo.Vault;
|
-- DROP TABLE IF EXISTS dbo.Vault;
|
||||||
GO
|
-- GO
|
||||||
|
|
||||||
|
|
||||||
/* =========================================================
|
/* =========================================================
|
||||||
CORE TABLES
|
CORE TABLES
|
||||||
========================================================= */
|
========================================================= */
|
||||||
CREATE TABLE dbo.Vault (
|
CREATE TABLE dbo.Vault (
|
||||||
ID int IDENTITY(1,1) NOT NULL,
|
ID INT IDENTITY(1,1) NOT NULL,
|
||||||
CustomerGUID uniqueidentifier NOT NULL,
|
License_ID INT NOT NULL,
|
||||||
Feature nvarchar(128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
Customer_ID INT NOT NULL,
|
||||||
Payload nvarchar(MAX) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
Signature NVARCHAR(512) NOT NULL,
|
||||||
Signature nvarchar(MAX) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
EncryptedPayload VARBINARY(MAX) NOT NULL,
|
||||||
Active bit DEFAULT 1 NOT NULL,
|
ExpiresAt DATETIME2 NULL,
|
||||||
ExpiresAt datetime NULL,
|
Status_ID TINYINT NOT NULL,
|
||||||
CreatedAt datetime DEFAULT getdate() NOT NULL,
|
LastVerifiedAt DATETIME2 NULL,
|
||||||
UpdatedAt datetime DEFAULT getdate() NULL,
|
CreateDate DATETIME2 NOT NULL
|
||||||
CONSTRAINT PK__Vault__3214EC275180843D PRIMARY KEY (ID)
|
CONSTRAINT DF_Vault_CreateDate DEFAULT SYSDATETIME(),
|
||||||
|
CONSTRAINT PK_Vault PRIMARY KEY (ID),
|
||||||
|
CONSTRAINT CK_Vault_Status CHECK (Status_ID IN (0,1,2,3,4))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE dbo.ObjectSource (
|
CREATE TABLE dbo.ObjectSource (
|
||||||
ID INT IDENTITY(1,1) PRIMARY KEY,
|
ID INT IDENTITY(1,1) PRIMARY KEY,
|
||||||
Name VARCHAR(100) NOT NULL UNIQUE
|
Name VARCHAR(100) NOT NULL UNIQUE
|
||||||
@@ -371,9 +372,10 @@ INSERT INTO dbo.EventLevels VALUES
|
|||||||
INSERT INTO dbo.[Role] (Name,Description,RoleType)
|
INSERT INTO dbo.[Role] (Name,Description,RoleType)
|
||||||
VALUES ('ADMIN','System Administrators','SYSTEM');
|
VALUES ('ADMIN','System Administrators','SYSTEM');
|
||||||
|
|
||||||
INSERT INTO dbo.Permission (Scope,Resource,Action)
|
|
||||||
VALUES ('SYSTEM','ALL','ALL');
|
|
||||||
|
|
||||||
|
INSERT INTO dbo.Permission (Scope,Resource,Action) VALUES
|
||||||
|
('SYSTEM','ALL','ALL')
|
||||||
|
('SYSTEM','ALL','Default_Access')
|
||||||
|
|
||||||
INSERT INTO dbo.RolePermissions
|
INSERT INTO dbo.RolePermissions
|
||||||
SELECT r.ID, p.ID
|
SELECT r.ID, p.ID
|
||||||
@@ -382,6 +384,10 @@ JOIN dbo.Permission p ON p.Scope='SYSTEM'
|
|||||||
WHERE r.Name='ADMIN';
|
WHERE r.Name='ADMIN';
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO dbo.Group (ObjectGUID,Name,ObjectSource_ID) VALUES
|
||||||
|
('00000000-0000-0000-0000-000000000001','ADMINISTRATORS',1)
|
||||||
|
('00000000-0000-0000-0000-000000000002','USERS',1);
|
||||||
|
|
||||||
/* =========================================================
|
/* =========================================================
|
||||||
ADMIN USER
|
ADMIN USER
|
||||||
========================================================= */
|
========================================================= */
|
||||||
@@ -499,19 +505,28 @@ SELECT
|
|||||||
r.Description,
|
r.Description,
|
||||||
r.RoleType,
|
r.RoleType,
|
||||||
|
|
||||||
COUNT(DISTINCT gr.Group_ObjectGUID) AS GroupCount,
|
ISNULL(g.GroupCount, 0) AS GroupCount,
|
||||||
COUNT(DISTINCT vau.ObjectGUID) AS UserCount
|
ISNULL(u.UserCount, 0) AS UserCount
|
||||||
|
|
||||||
FROM dbo.Role AS r
|
FROM dbo.Role r
|
||||||
|
|
||||||
LEFT JOIN dbo.vAuthenticationRoles AS vau
|
-- 👥 Gruppen zählen
|
||||||
ON vau.Role_ID = r.ID
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
Role_ID,
|
||||||
|
COUNT(DISTINCT Group_ObjectGUID) AS GroupCount
|
||||||
|
FROM dbo.GroupRoles
|
||||||
|
GROUP BY Role_ID
|
||||||
|
) g ON g.Role_ID = r.ID
|
||||||
|
|
||||||
LEFT JOIN dbo.GroupRoles AS gr
|
-- 👤 NUR direkte User zählen (WICHTIG)
|
||||||
ON gr.Role_ID = r.ID
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
GROUP BY
|
Role_ID,
|
||||||
r.ID, r.Name, r.Description, r.RoleType;
|
COUNT(DISTINCT Authentication_ObjectGUID) AS UserCount
|
||||||
|
FROM dbo.AuthenticationRoles
|
||||||
|
GROUP BY Role_ID
|
||||||
|
) u ON u.Role_ID = r.ID;
|
||||||
GO
|
GO
|
||||||
|
|
||||||
|
|
||||||
@@ -606,7 +621,7 @@ FROM dbo.ObjectSource RIGHT OUTER JOIN
|
|||||||
dbo.AuthenticationGroups AS ag ON dbo.[Group].ObjectGUID = ag.Group_ObjectGUID LEFT OUTER JOIN
|
dbo.AuthenticationGroups AS ag ON dbo.[Group].ObjectGUID = ag.Group_ObjectGUID LEFT OUTER JOIN
|
||||||
dbo.GroupRoles AS gr ON dbo.[Group].ObjectGUID = gr.Group_ObjectGUID
|
dbo.GroupRoles AS gr ON dbo.[Group].ObjectGUID = gr.Group_ObjectGUID
|
||||||
GROUP BY dbo.[Group].ObjectGUID, dbo.[Group].Name, dbo.ObjectSource.Name, dbo.[Group].distinguishedName
|
GROUP BY dbo.[Group].ObjectGUID, dbo.[Group].Name, dbo.ObjectSource.Name, dbo.[Group].distinguishedName
|
||||||
|
GO
|
||||||
|
|
||||||
-- ========================================================
|
-- ========================================================
|
||||||
-- 9. BONUS: PERMISSION TRACE (WHY DOES USER HAVE THIS?)
|
-- 9. BONUS: PERMISSION TRACE (WHY DOES USER HAVE THIS?)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const ctx = new ContextMenu();
|
|||||||
const windowCleanup = new Map();
|
const windowCleanup = new Map();
|
||||||
const username = getCookie('sAMAccountName');
|
const username = getCookie('sAMAccountName');
|
||||||
const LS_KEY = (key) => `${username}:${key}`;
|
const LS_KEY = (key) => `${username}:${key}`;
|
||||||
const MAX_PADDING = { left: 4, top: 4, right: 4, bottom: (56 - 4) };
|
let MAX_PADDING = { left: 4, top: 4, right: 4, bottom: 40 };
|
||||||
|
|
||||||
startBtn.addEventListener('click', (evt) => {
|
startBtn.addEventListener('click', (evt) => {
|
||||||
evt.stopPropagation(); // verhindert sofortiges Schließen
|
evt.stopPropagation(); // verhindert sofortiges Schließen
|
||||||
@@ -28,13 +28,19 @@ startBtn.addEventListener('click', (evt) => {
|
|||||||
|
|
||||||
function updateStartMenuPosition() {
|
function updateStartMenuPosition() {
|
||||||
const height = taskbar.offsetHeight;
|
const height = taskbar.offsetHeight;
|
||||||
startMenu.style.bottom = (height + 5) + 'px';
|
startMenu.style.bottom = (height + 12) + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
const observer = new ResizeObserver(entries => {
|
const observer = new ResizeObserver(entries => {
|
||||||
for (let entry of entries) {
|
for (let entry of entries) {
|
||||||
const height = entry.contentRect.height;
|
const height = entry.contentRect.height;
|
||||||
document.documentElement.style.setProperty('--auto-taskbar-height', height + 'px');
|
MAX_PADDING = { left: 4, top: 4, right: 4, bottom: height + 8 };
|
||||||
|
|
||||||
|
windowsContainer.querySelectorAll('[data-winid]').forEach(win => {
|
||||||
|
if(win.dataset.state === 'maximized') {
|
||||||
|
applyMaximized(win) ;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ const RBAC = {
|
|||||||
createRole: (name) => api('/api/rbac/role/create', 'POST', { name }),
|
createRole: (name) => api('/api/rbac/role/create', 'POST', { name }),
|
||||||
deleteRole: (id) => api(`/api/rbac/role/${id}`, 'DELETE'),
|
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
|
// 🔗 ASSIGNMENTS
|
||||||
addUserToGroup: (authGuid, groupGuid) => api('/api/rbac/group/add-user', 'POST', { authGuid, groupGuid }),
|
addUserToGroup: (authGuid, groupGuid) => api('/api/rbac/group/add-user', 'POST', { authGuid, groupGuid }),
|
||||||
addUserToRole: (authGuid, roleId) => api('/api/rbac/role/add-user', 'POST', { authGuid, roleId }),
|
addUserToRole: (authGuid, roleId) => api('/api/rbac/role/add-user', 'POST', { authGuid, roleId }),
|
||||||
@@ -115,7 +120,7 @@ function createDropZone(el, type, target) {
|
|||||||
// 📋 TABLE (USERS)
|
// 📋 TABLE (USERS)
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
|
|
||||||
const rbacVT = virtualTable({
|
const rbacUsersVT = virtualTable({
|
||||||
tableEl: document.querySelector('#rbacUsersTable'),
|
tableEl: document.querySelector('#rbacUsersTable'),
|
||||||
data: [],
|
data: [],
|
||||||
rowHeight: 20,
|
rowHeight: 20,
|
||||||
@@ -177,13 +182,81 @@ const rbacVT = virtualTable({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
// 📋 TABLE (PERMISSIONS)
|
||||||
|
//////////////////////////////
|
||||||
|
|
||||||
|
const rbacPermissionsVT = virtualTable({
|
||||||
|
tableEl: document.querySelector('#rbacPermissionsTable'),
|
||||||
|
data: [],
|
||||||
|
rowHeight: 20,
|
||||||
|
buffer: 5,
|
||||||
|
groupKey: 'Scope',
|
||||||
|
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['ID'], { classes: [ 'text-align:left' ], styles: { 'max-width': '100px' } } );
|
||||||
|
createTd(tr, row['Scope'], { classes: [ 'text-align:left' ] });
|
||||||
|
createTd(tr, row['Resource'], { classes: [ 'text-align:center' ] });
|
||||||
|
createTd(tr, row['Action'], { classes: [ 'text-align:center' ] });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
// 📥 LOADERS
|
// 📥 LOADERS
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
|
|
||||||
async function loadUsers() {
|
async function loadUsers() {
|
||||||
try {
|
try {
|
||||||
rbacVT.source(await RBAC.loadUsers());
|
rbacUsersVT.source(await RBAC.loadUsers());
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadPermissions() {
|
||||||
|
try {
|
||||||
|
rbacPermissionsVT.source(await RBAC.loadPermissions());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
@@ -273,6 +346,7 @@ async function loadRoles() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
// 👤 USER ACTIONS
|
// 👤 USER ACTIONS
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
@@ -307,6 +381,9 @@ async function deleteUser(guid, name) {
|
|||||||
const user = await RBAC.deleteUser(guid);
|
const user = await RBAC.deleteUser(guid);
|
||||||
sendUserEvent('RBAC', `Benutzer ${user.sAMAccountName || ''} [${user.ObjectGUID}] gelöscht`, null, 0);
|
sendUserEvent('RBAC', `Benutzer ${user.sAMAccountName || ''} [${user.ObjectGUID}] gelöscht`, null, 0);
|
||||||
loadUsers();
|
loadUsers();
|
||||||
|
loadGroups();
|
||||||
|
loadRoles();
|
||||||
|
loadPermissions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,6 +408,8 @@ async function deleteGroup(guid, name) {
|
|||||||
sendUserEvent('RBAC', `Du hast die Gruppe ${name || ''} [${group.ObjectGUID}] gelöscht`, null, 0);
|
sendUserEvent('RBAC', `Du hast die Gruppe ${name || ''} [${group.ObjectGUID}] gelöscht`, null, 0);
|
||||||
loadUsers();
|
loadUsers();
|
||||||
loadGroups();
|
loadGroups();
|
||||||
|
loadRoles();
|
||||||
|
loadPermissions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -369,19 +448,65 @@ async function deleteRole(id, name) {
|
|||||||
|
|
||||||
sendUserEvent('RBAC', `Rolle ${name} gelöscht`, null, 0);
|
sendUserEvent('RBAC', `Rolle ${name} gelöscht`, null, 0);
|
||||||
|
|
||||||
|
loadUsers();
|
||||||
|
loadGroups();
|
||||||
loadRoles();
|
loadRoles();
|
||||||
loadUsers(); // optional, falls RoleCount betroffen
|
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
|
// 🚀 INIT
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
|
|
||||||
loadUsers();
|
loadUsers();
|
||||||
loadGroups();
|
loadGroups();
|
||||||
loadRoles();
|
loadRoles();
|
||||||
|
loadPermissions();
|
||||||
@@ -31,8 +31,8 @@ body, html { margin:0; padding:0; height:100%; overflow: hidden; font-family: va
|
|||||||
.window-resize-se { bottom: -6px;right: -6px;cursor: var(--theme-cursor-resize-270); }
|
.window-resize-se { bottom: -6px;right: -6px;cursor: var(--theme-cursor-resize-270); }
|
||||||
.window-resize-sw { bottom: -6px;left: -6px;cursor: var(--theme-cursor-resize-45); }
|
.window-resize-sw { bottom: -6px;left: -6px;cursor: var(--theme-cursor-resize-45); }
|
||||||
|
|
||||||
#taskbar { z-index: 2; position: absolute; width:100%; bottom:0; left:0; height:var(--auto-taskbar-height); overflow:visible; display:flex; flex: 0 0 auto; min-width:0; align-items:center; padding:0 8px; box-sizing:border-box; }
|
#taskbar { z-index: 2; position: absolute; width:100%; bottom:0; left:0; height:auto; overflow:visible; display:flex; flex: 0 0 auto; min-width:0; align-items:center; padding:0 8px; box-sizing:border-box; }
|
||||||
#start-btn { transition: background-color var(--times-transition-colors) ease; padding: 8px 12px; border-radius: 5px; border: none; margin-right:8px; }
|
#start-btn { transition: background-color var(--times-transition-colors) ease; padding: 5px 12px; border-radius: 5px; border: none; margin-right:8px; }
|
||||||
#taskbar-windows { display:flex; gap:6px; align-items:center; flex:1; overflow-y:hidden;overflow-x: auto; min-width: 0;scrollbar-width: thin; }
|
#taskbar-windows { display:flex; gap:6px; align-items:center; flex:1; overflow-y:hidden;overflow-x: auto; min-width: 0;scrollbar-width: thin; }
|
||||||
.taskbar-item { display: flex; position: relative; padding:4px 10px; border-radius:4px; }
|
.taskbar-item { display: flex; position: relative; padding:4px 10px; border-radius:4px; }
|
||||||
.taskbar-item::before { content: ''; position: absolute; bottom:1px; left:50%; width:40%; height: 4px; border-radius:4px; transform:translateX(-50%) scaleX(0); transform-origin:center; transition:transform var(--times-transition-colors) ease; }
|
.taskbar-item::before { content: ''; position: absolute; bottom:1px; left:50%; width:40%; height: 4px; border-radius:4px; transform:translateX(-50%) scaleX(0); transform-origin:center; transition:transform var(--times-transition-colors) ease; }
|
||||||
@@ -59,7 +59,7 @@ body, html { margin:0; padding:0; height:100%; overflow: hidden; font-family: va
|
|||||||
.hidden { opacity: 0; pointer-events: none; }
|
.hidden { opacity: 0; pointer-events: none; }
|
||||||
|
|
||||||
/* Open submenu vertical */
|
/* Open submenu vertical */
|
||||||
#start-menu { z-index: 2; transition: opacity var(--times-transition-opacity); display:flex; flex-direction:column; position: absolute; bottom: 50px; left: 8px; width: auto; min-width: 300px; border-radius: 8px; overflow: hidden; }
|
#start-menu { z-index: 2; transition: opacity var(--times-transition-opacity); display:flex; flex-direction:column; position: absolute; bottom: auto; left: 8px; width: auto; min-width: 300px; border-radius: 8px; overflow: hidden; }
|
||||||
.start-header { display: flex; align-items: center; padding: 8px; font-weight: 600; }
|
.start-header { display: flex; align-items: center; padding: 8px; font-weight: 600; }
|
||||||
.start-header > img { height: 24px; width: 24px; margin-right: 5px; }
|
.start-header > img { height: 24px; width: 24px; margin-right: 5px; }
|
||||||
.start-list { list-style: none; margin: 0; padding: 8px 0; height: 60vh; overflow-y: auto; }
|
.start-list { list-style: none; margin: 0; padding: 8px 0; height: 60vh; overflow-y: auto; }
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ input {
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- USERS -->
|
<!-- USERS -->
|
||||||
<div class="container static" style="height:100vh;max-width:100vw;flex-direction:row;flex-wrap:wrap">
|
<div class="container static" style="height:100vh;max-width:100vw;flex-direction:row;flex-wrap:wrap;">
|
||||||
<div class="card" style="flex:1 1 auto;min-width:300px">
|
<div class="card" style="flex:1 1 auto;min-width:300px">
|
||||||
Users <input id="newUserName" placeholder="sAMAccountName" /> <button class="bluebutton" onclick="createUser()">Create User</button>
|
Users <input id="newUserName" placeholder="sAMAccountName" /> <button class="bluebutton" onclick="createUser()">Create User</button>
|
||||||
<div class="table-wrapper fit-table">
|
<div class="table-wrapper fit-table">
|
||||||
@@ -109,13 +109,26 @@ input {
|
|||||||
|
|
||||||
<!-- PERMISSIONS -->
|
<!-- PERMISSIONS -->
|
||||||
<div class="card" style="min-width:300px;flex:1 1 auto">
|
<div class="card" style="min-width:300px;flex:1 1 auto">
|
||||||
<h3>Permissions</h3>
|
<input id="permScope" placeholder="Scope" />.<input id="permResource" placeholder="Resource" />.<input id="permAction" placeholder="Action" />
|
||||||
|
|
||||||
<input id="permScope" placeholder="Scope" />
|
|
||||||
<input id="permResource" placeholder="Resource" />
|
|
||||||
<input id="permAction" placeholder="Action" />
|
|
||||||
|
|
||||||
<button class="bluebutton" onclick="createPermission()">Create Permission</button>
|
<button class="bluebutton" onclick="createPermission()">Create Permission</button>
|
||||||
|
|
||||||
|
<div class="table-wrapper fit-table">
|
||||||
|
<table id="rbacPermissionsTable" style="height:100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-align:left"></th>
|
||||||
|
<th class="text-align:left">ID</th>
|
||||||
|
<th class="text-align:left">Scope</th>
|
||||||
|
<th class="text-align:center">Resource</th>
|
||||||
|
<th class="text-align:center">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td colspan="100%">BERECHTIGUNGEN WERDEN GELADEN . . .</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -117,6 +117,9 @@ const server = https.createServer(httpsOptions, app);
|
|||||||
databaseModel.set('authenticationOverviewView', require(`@models/authenticationOverviewView`)(service.get('sqlManager').getInstance('main')));
|
databaseModel.set('authenticationOverviewView', require(`@models/authenticationOverviewView`)(service.get('sqlManager').getInstance('main')));
|
||||||
databaseModel.set('groupOverviewView', require(`@models/groupOverviewView`)(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')));
|
databaseModel.set('roleOverviewView', require(`@models/roleOverviewView`)(service.get('sqlManager').getInstance('main')));
|
||||||
|
databaseModel.set('PermissionTraceView', require(`@models/PermissionTraceView`)(service.get('sqlManager').getInstance('main')));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
service.set('rbacManager', new RBACManager(databaseModel, runtimeFile.configuration.live.integration.token.secret));
|
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));
|
service.set('authenticationManager', new AuthenticationManager(databaseModel.get('authentication'), runtimeFile.configuration.live.integration.token.secret));
|
||||||
|
|||||||
44
src/models/PermissionTraceView.js
Normal file
44
src/models/PermissionTraceView.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
const { DataTypes } = require('sequelize');
|
||||||
|
|
||||||
|
module.exports = (sequelize) => {
|
||||||
|
const PermissionTrace = sequelize.define('vPermissionTrace', {
|
||||||
|
|
||||||
|
Authentication_ObjectGUID: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
|
||||||
|
RoleName: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
|
||||||
|
Scope: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
|
||||||
|
Resource: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
|
||||||
|
Action: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
|
||||||
|
PermissionKey: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
}
|
||||||
|
|
||||||
|
}, {
|
||||||
|
tableName: 'vPermissionTrace',
|
||||||
|
timestamps: false,
|
||||||
|
freezeTableName: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return PermissionTrace;
|
||||||
|
};
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Administration"
|
"action": "Administration"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Administration"
|
"action": "Administration"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -44,6 +46,7 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Administration"
|
"action": "Administration"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -69,6 +72,7 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Administration"
|
"action": "Administration"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -94,6 +98,7 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Administration"
|
"action": "Administration"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -119,6 +124,7 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Administration"
|
"action": "Administration"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -144,6 +150,7 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Default_Access"
|
"action": "Default_Access"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -165,6 +172,7 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Default_Access"
|
"action": "Default_Access"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -10,29 +10,24 @@ module.exports = (sequelize) => {
|
|||||||
autoIncrement: true
|
autoIncrement: true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
License_ID: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
|
||||||
Customer_ID: {
|
Customer_ID: {
|
||||||
type: DataTypes.UUIDV4,
|
type: DataTypes.INTEGER,
|
||||||
allowNull: false
|
|
||||||
},
|
|
||||||
|
|
||||||
Feature: {
|
|
||||||
type: DataTypes.STRING(128),
|
|
||||||
allowNull: false
|
|
||||||
},
|
|
||||||
|
|
||||||
Payload: {
|
|
||||||
type: DataTypes.TEXT, // NVARCHAR(MAX)
|
|
||||||
allowNull: false
|
allowNull: false
|
||||||
},
|
},
|
||||||
|
|
||||||
Signature: {
|
Signature: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.STRING(512),
|
||||||
allowNull: false
|
allowNull: false
|
||||||
},
|
},
|
||||||
|
|
||||||
Active: {
|
EncryptedPayload: {
|
||||||
type: DataTypes.BOOLEAN,
|
type: DataTypes.BLOB('long'),
|
||||||
defaultValue: true
|
allowNull: false
|
||||||
},
|
},
|
||||||
|
|
||||||
ExpiresAt: {
|
ExpiresAt: {
|
||||||
@@ -40,12 +35,20 @@ module.exports = (sequelize) => {
|
|||||||
allowNull: true
|
allowNull: true
|
||||||
},
|
},
|
||||||
|
|
||||||
CreatedAt: {
|
Status_ID: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.TINYINT,
|
||||||
defaultValue: DataTypes.NOW
|
allowNull: false,
|
||||||
|
validate: {
|
||||||
|
isIn: [[0, 1, 2, 3, 4]]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
UpdatedAt: {
|
LastVerifiedAt: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
|
||||||
|
CreateDate: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
defaultValue: DataTypes.NOW
|
defaultValue: DataTypes.NOW
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -348,8 +348,17 @@ module.exports = {
|
|||||||
// =========================================================
|
// =========================================================
|
||||||
// 🔐 PERMISSIONS
|
// 🔐 PERMISSIONS
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
app.post('/api/rbac/permission/get', async (req, res) => {
|
||||||
|
try {
|
||||||
|
rbacPermissions = await service.get('rbacManager').getPermission();
|
||||||
|
res.json(rbacPermissions);
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/permission', async (req, res) => {
|
|
||||||
|
app.post('/api/rbac/permission/create', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const perm = await service.get('rbacManager').createPermission(req.body);
|
const perm = await service.get('rbacManager').createPermission(req.body);
|
||||||
res.json(perm);
|
res.json(perm);
|
||||||
@@ -358,6 +367,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app.put('/permission/:id', async (req, res) => {
|
app.put('/permission/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await service.get('rbacManager').updatePermission(req.params.id, req.body);
|
await service.get('rbacManager').updatePermission(req.params.id, req.body);
|
||||||
|
|||||||
@@ -313,8 +313,8 @@ class PluginManager {
|
|||||||
permissions: [
|
permissions: [
|
||||||
{
|
{
|
||||||
scope: name, // Plugin Scope (default = plugin name)
|
scope: name, // Plugin Scope (default = plugin name)
|
||||||
action: "Default_Access",
|
resource: "Plugin",
|
||||||
resource: "MenuItem"
|
action: "Default_Access"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -424,6 +424,12 @@ async removeRoleFromUser(authId, roleId) {
|
|||||||
// =========================================================
|
// =========================================================
|
||||||
// 🔐 PERMISSION CRUD
|
// 🔐 PERMISSION CRUD
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
async getPermission() {
|
||||||
|
const permission = this.db.get('permissionModel');
|
||||||
|
console.log(permission)
|
||||||
|
return await permission.findAll({ raw: true }) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async createPermission(data) {
|
async createPermission(data) {
|
||||||
const Permission = this.db.get('permissionModel');
|
const Permission = this.db.get('permissionModel');
|
||||||
|
|||||||
@@ -75,26 +75,40 @@ class VaultifyManager {
|
|||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
verify(record) {
|
verify(record) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const payload = this.parsePayload(record.Payload);
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
Customer_ID: record.Customer_ID,
|
Customer_ID: record.Customer_ID,
|
||||||
Feature: record.Feature,
|
Feature: record.Feature,
|
||||||
Payload: this.parsePayload(record.Payload),
|
Payload: payload,
|
||||||
ExpiresAt: record.ExpiresAt
|
ExpiresAt: record.ExpiresAt ?? null
|
||||||
};
|
};
|
||||||
|
|
||||||
const verifier = crypto.createVerify('RSA-SHA256');
|
const verifier = crypto.createVerify('RSA-SHA256');
|
||||||
|
|
||||||
verifier.update(JSON.stringify(data));
|
verifier.update(JSON.stringify(data));
|
||||||
verifier.end();
|
verifier.end();
|
||||||
|
|
||||||
return verifier.verify(
|
const isValid = verifier.verify(
|
||||||
this.publicKey,
|
this.publicKey,
|
||||||
record.Signature,
|
record.Signature,
|
||||||
'base64'
|
'base64'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!isValid) return false;
|
||||||
|
|
||||||
|
// 🔥 WICHTIG: Ablaufdatum HIER erzwingen
|
||||||
|
if (payload.expiresAt && record.ExpiresAt) {
|
||||||
|
if (payload.expiresAt !== record.ExpiresAt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (record.ExpiresAt && new Date(record.ExpiresAt) < new Date()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
17
utils.js
17
utils.js
@@ -63,7 +63,6 @@ module.exports = startMenuItems = async function (app, objectGuid) {
|
|||||||
|
|
||||||
plugin.menu.items = (plugin.menu.items || []).map(item => {
|
plugin.menu.items = (plugin.menu.items || []).map(item => {
|
||||||
|
|
||||||
const resource = item.label;
|
|
||||||
const requiredPermissions = item.permissions || [];
|
const requiredPermissions = item.permissions || [];
|
||||||
|
|
||||||
const debugTrace = [];
|
const debugTrace = [];
|
||||||
@@ -77,25 +76,25 @@ module.exports = startMenuItems = async function (app, objectGuid) {
|
|||||||
const scopeMatch =
|
const scopeMatch =
|
||||||
userPerm.scope === required.scope;
|
userPerm.scope === required.scope;
|
||||||
|
|
||||||
|
const resourceMatch =
|
||||||
|
!userPerm.resource ||
|
||||||
|
userPerm.resource === 'ALL' ||
|
||||||
|
userPerm.resource === required.resource;
|
||||||
|
|
||||||
const actionMatch =
|
const actionMatch =
|
||||||
userPerm.action === 'ALL' ||
|
userPerm.action === 'ALL' ||
|
||||||
userPerm.action === required.action ||
|
userPerm.action === required.action ||
|
||||||
required.action === 'ALL';
|
required.action === 'ALL';
|
||||||
|
|
||||||
const resourceMatch =
|
const result = scopeMatch && resourceMatch && actionMatch;
|
||||||
!userPerm.resource ||
|
|
||||||
userPerm.resource === 'ALL' ||
|
|
||||||
userPerm.resource === resource;
|
|
||||||
|
|
||||||
const result = scopeMatch && actionMatch && resourceMatch;
|
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
debugTrace.push({
|
debugTrace.push({
|
||||||
userPerm,
|
userPerm,
|
||||||
required,
|
required,
|
||||||
scopeMatch,
|
scopeMatch,
|
||||||
actionMatch,
|
|
||||||
resourceMatch,
|
resourceMatch,
|
||||||
|
actionMatch,
|
||||||
result
|
result
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -105,7 +104,7 @@ module.exports = startMenuItems = async function (app, objectGuid) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
log(`\nMENU ITEM: ${item.label} - AUTHORIZED:', ${authorized} - TRACE:', ${debugTrace}`);
|
log(`\nMENU ITEM: ${item.label} - AUTHORIZED:', ${authorized} - TRACE:', ${JSON.stringify(debugTrace)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user