Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
556ccc3a73 | ||
|
|
4db4ac755c | ||
|
|
63931bc4d5 | ||
|
|
0a28a3e493 | ||
|
|
b34f912857 | ||
| 2bd7cdd302 | |||
| c848633a1f | |||
| e5ee067db4 | |||
|
|
f09f148aea | ||
| e208ef1759 | |||
| 76a30fc94f | |||
| 6679ed20fe | |||
| bbd9441b31 | |||
|
|
90497deebf | ||
| 15f3a5f80d | |||
| c98089e359 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,4 +6,5 @@ stylesheet.json
|
|||||||
.npmrc
|
.npmrc
|
||||||
radix_os_*.png
|
radix_os_*.png
|
||||||
radix_os_icon.ico
|
radix_os_icon.ico
|
||||||
login.hbs
|
login.hbs
|
||||||
|
todo.txt
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
DEMO IS RUNNING:
|
DEMO IS RUNNING:
|
||||||
https://demo.sowadanas.dynv6.net/
|
https://demo.sowada.dev/
|
||||||
|
|
||||||
|
|
||||||
globalize:
|
globalize:
|
||||||
|
|||||||
272
dbcreate.sql
272
dbcreate.sql
@@ -24,6 +24,7 @@ GO
|
|||||||
-- DROP VIEW IF EXISTS dbo.vAuthentications;
|
-- DROP VIEW IF EXISTS dbo.vAuthentications;
|
||||||
-- DROP VIEW IF EXISTS dbo.vEventLog;
|
-- DROP VIEW IF EXISTS dbo.vEventLog;
|
||||||
-- DROP VIEW IF EXISTS dbo.vNotifyTray;
|
-- DROP VIEW IF EXISTS dbo.vNotifyTray;
|
||||||
|
-- DROP VIEW IF EXISTS dbo.vPermissionOverview;s
|
||||||
|
|
||||||
-- DROP TABLE IF EXISTS dbo.AuthenticationRoles;
|
-- DROP TABLE IF EXISTS dbo.AuthenticationRoles;
|
||||||
-- DROP TABLE IF EXISTS dbo.AuthenticationGroups;
|
-- DROP TABLE IF EXISTS dbo.AuthenticationGroups;
|
||||||
@@ -44,26 +45,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
|
||||||
@@ -260,6 +262,9 @@ JOIN dbo.RolePermissions rp ON rp.Role_ID = r.Role_ID
|
|||||||
JOIN dbo.Permission p ON p.ID = rp.Permission_ID;
|
JOIN dbo.Permission p ON p.ID = rp.Permission_ID;
|
||||||
GO
|
GO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* =========================================================
|
/* =========================================================
|
||||||
FIXED vEventLog (SEQUELIZE MATCH + SYSTEM FIX)
|
FIXED vEventLog (SEQUELIZE MATCH + SYSTEM FIX)
|
||||||
========================================================= */
|
========================================================= */
|
||||||
@@ -364,14 +369,14 @@ INSERT INTO dbo.EventLevels VALUES
|
|||||||
(4,'error','Error',1),
|
(4,'error','Error',1),
|
||||||
(8,'throw_exception','Exception',0);
|
(8,'throw_exception','Exception',0);
|
||||||
|
|
||||||
INSERT INTO dbo.Plugins VALUES ('SYSTEM',1,'1.0.0');
|
|
||||||
|
|
||||||
INSERT INTO dbo.[Role] (Name,Description,RoleType)
|
INSERT INTO dbo.[Role] (Name,Description,RoleType)
|
||||||
VALUES ('ADMIN','System Administrator','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
|
||||||
@@ -380,6 +385,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
|
||||||
========================================================= */
|
========================================================= */
|
||||||
@@ -412,4 +421,233 @@ SELECT
|
|||||||
'00000000-0000-0000-0000-000000000001',
|
'00000000-0000-0000-0000-000000000001',
|
||||||
ID
|
ID
|
||||||
FROM dbo.[Role]
|
FROM dbo.[Role]
|
||||||
WHERE Name='ADMIN';
|
WHERE Name='ADMIN';
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* =========================================================
|
||||||
|
EXTENDED RBAC VIEWS
|
||||||
|
========================================================= */
|
||||||
|
|
||||||
|
-- ========================================================
|
||||||
|
-- 1. USER GROUPS (DIRECT + INHERITED)
|
||||||
|
-- ========================================================
|
||||||
|
CREATE OR ALTER VIEW dbo.vAuthenticationGroupsExpanded AS
|
||||||
|
SELECT
|
||||||
|
ag.Authentication_ObjectGUID,
|
||||||
|
g.ObjectGUID AS GroupGUID,
|
||||||
|
g.Name AS GroupName,
|
||||||
|
'DIRECT' AS Source
|
||||||
|
FROM dbo.AuthenticationGroups ag
|
||||||
|
JOIN dbo.[Group] g
|
||||||
|
ON g.ObjectGUID = ag.Group_ObjectGUID
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
ag.Authentication_ObjectGUID,
|
||||||
|
gc.ParentGroup_ObjectGUID,
|
||||||
|
g.Name,
|
||||||
|
'INHERITED'
|
||||||
|
FROM dbo.AuthenticationGroups ag
|
||||||
|
JOIN dbo.GroupClosure gc
|
||||||
|
ON gc.ChildGroup_ObjectGUID = ag.Group_ObjectGUID
|
||||||
|
JOIN dbo.[Group] g
|
||||||
|
ON g.ObjectGUID = gc.ParentGroup_ObjectGUID;
|
||||||
|
GO
|
||||||
|
|
||||||
|
|
||||||
|
-- ========================================================
|
||||||
|
-- 2. ROLES (DIRECT + GROUP + HIERARCHY)
|
||||||
|
-- ========================================================
|
||||||
|
CREATE OR ALTER VIEW dbo.vAuthenticationRolesExpanded AS
|
||||||
|
SELECT
|
||||||
|
ar.Authentication_ObjectGUID,
|
||||||
|
ar.Role_ID,
|
||||||
|
r.Name AS RoleName,
|
||||||
|
'DIRECT' AS Source
|
||||||
|
FROM dbo.AuthenticationRoles ar
|
||||||
|
JOIN dbo.[Role] r
|
||||||
|
ON r.ID = ar.Role_ID
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
ag.Authentication_ObjectGUID,
|
||||||
|
gr.Role_ID,
|
||||||
|
r.Name,
|
||||||
|
'GROUP'
|
||||||
|
FROM dbo.AuthenticationGroups ag
|
||||||
|
JOIN dbo.GroupRoles gr
|
||||||
|
ON gr.Group_ObjectGUID = ag.Group_ObjectGUID
|
||||||
|
JOIN dbo.[Role] r
|
||||||
|
ON r.ID = gr.Role_ID
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
ag.Authentication_ObjectGUID,
|
||||||
|
gr.Role_ID,
|
||||||
|
r.Name,
|
||||||
|
'GROUP_INHERITED'
|
||||||
|
FROM dbo.AuthenticationGroups ag
|
||||||
|
JOIN dbo.GroupClosure gc
|
||||||
|
ON gc.ChildGroup_ObjectGUID = ag.Group_ObjectGUID
|
||||||
|
JOIN dbo.GroupRoles gr
|
||||||
|
ON gr.Group_ObjectGUID = gc.ParentGroup_ObjectGUID
|
||||||
|
JOIN dbo.[Role] r
|
||||||
|
ON r.ID = gr.Role_ID;
|
||||||
|
GO
|
||||||
|
|
||||||
|
|
||||||
|
CREATE OR ALTER VIEW dbo.vRoleOverview AS
|
||||||
|
SELECT
|
||||||
|
r.ID,
|
||||||
|
r.Name,
|
||||||
|
r.Description,
|
||||||
|
r.RoleType,
|
||||||
|
|
||||||
|
ISNULL(g.GroupCount, 0) AS GroupCount,
|
||||||
|
ISNULL(u.UserCount, 0) AS UserCount
|
||||||
|
|
||||||
|
FROM dbo.Role r
|
||||||
|
|
||||||
|
-- 👥 Gruppen zählen
|
||||||
|
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
|
||||||
|
|
||||||
|
-- 👤 NUR direkte User zählen (WICHTIG)
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
Role_ID,
|
||||||
|
COUNT(DISTINCT Authentication_ObjectGUID) AS UserCount
|
||||||
|
FROM dbo.AuthenticationRoles
|
||||||
|
GROUP BY Role_ID
|
||||||
|
) u ON u.Role_ID = r.ID;
|
||||||
|
GO
|
||||||
|
|
||||||
|
|
||||||
|
-- ========================================================
|
||||||
|
-- 4. PERMISSIONS (DETAILED WITH ROLE SOURCE)
|
||||||
|
-- ========================================================
|
||||||
|
CREATE OR ALTER VIEW dbo.vAuthenticationPermissionsDetailed AS
|
||||||
|
SELECT
|
||||||
|
r.Authentication_ObjectGUID,
|
||||||
|
r.Role_ID,
|
||||||
|
r.RoleName,
|
||||||
|
p.Scope,
|
||||||
|
p.Resource,
|
||||||
|
p.Action,
|
||||||
|
CONCAT(p.Scope,'.',p.Resource,'.',p.Action) AS PermissionKey
|
||||||
|
FROM dbo.vAuthenticationRolesExpanded r
|
||||||
|
JOIN dbo.RolePermissions rp
|
||||||
|
ON rp.Role_ID = r.Role_ID
|
||||||
|
JOIN dbo.Permission p
|
||||||
|
ON p.ID = rp.Permission_ID;
|
||||||
|
GO
|
||||||
|
|
||||||
|
|
||||||
|
CREATE OR ALTER VIEW dbo.vPermissionOverview AS
|
||||||
|
SELECT p.ID AS Permission_ID, p.Scope, p.Resource, p.Action, p.Scope + '.' + p.Resource + '.' + p.Action AS PermissionKey, r.ID AS Role_ID, /* 🔥 NEU: Anzahl Rollen pro Permission*/ COUNT(r.ID) OVER (PARTITION BY p.ID)
|
||||||
|
AS RoleCount, COUNT(gr.Group_ObjectGUID) AS GroupCount, COUNT(ar.Authentication_ObjectGUID) AS DirectUserCount, COUNT(ag.Authentication_ObjectGUID) AS GroupUserCount,
|
||||||
|
COUNT(COALESCE (ar.Authentication_ObjectGUID, ag.Authentication_ObjectGUID)) AS TotalUserCount
|
||||||
|
FROM dbo.Permission AS p FULL JOIN
|
||||||
|
dbo.RolePermissions AS rp ON rp.Permission_ID = p.ID LEFT JOIN
|
||||||
|
dbo.Role AS r ON r.ID = rp.Role_ID LEFT JOIN
|
||||||
|
dbo.GroupRoles AS gr ON gr.Role_ID = r.ID LEFT JOIN
|
||||||
|
dbo.AuthenticationRoles AS ar ON ar.Role_ID = r.ID LEFT JOIN
|
||||||
|
dbo.AuthenticationGroups AS ag ON ag.Group_ObjectGUID = gr.Group_ObjectGUID
|
||||||
|
GROUP BY p.ID, p.Scope, p.Resource, p.Action, r.ID, r.Name;
|
||||||
|
GO
|
||||||
|
|
||||||
|
|
||||||
|
-- ========================================================
|
||||||
|
-- 5. PERMISSION MATRIX (FAST LOOKUP)
|
||||||
|
-- ========================================================
|
||||||
|
CREATE OR ALTER VIEW dbo.vPermissionMatrix AS
|
||||||
|
SELECT DISTINCT
|
||||||
|
Authentication_ObjectGUID,
|
||||||
|
CONCAT(Scope,'.',Resource,'.',Action) AS PermissionKey
|
||||||
|
FROM dbo.vAuthenticationPermissionsDetailed;
|
||||||
|
GO
|
||||||
|
|
||||||
|
|
||||||
|
-- ========================================================
|
||||||
|
-- 6. GROUP ROLES OVERVIEW
|
||||||
|
-- ========================================================
|
||||||
|
CREATE OR ALTER VIEW dbo.vGroupRolesDetailed AS
|
||||||
|
SELECT
|
||||||
|
g.ObjectGUID,
|
||||||
|
g.Name AS GroupName,
|
||||||
|
r.ID AS Role_ID,
|
||||||
|
r.Name AS RoleName
|
||||||
|
FROM dbo.GroupRoles gr
|
||||||
|
JOIN dbo.[Group] g
|
||||||
|
ON g.ObjectGUID = gr.Group_ObjectGUID
|
||||||
|
JOIN dbo.[Role] r
|
||||||
|
ON r.ID = gr.Role_ID;
|
||||||
|
GO
|
||||||
|
|
||||||
|
|
||||||
|
-- ========================================================
|
||||||
|
-- 7. GROUP HIERARCHY (READABLE)
|
||||||
|
-- ========================================================
|
||||||
|
CREATE OR ALTER VIEW dbo.vGroupHierarchyReadable AS
|
||||||
|
SELECT
|
||||||
|
parent.ObjectGUID AS ParentGroupGUID,
|
||||||
|
parent.Name AS ParentGroupName,
|
||||||
|
child.ObjectGUID AS ChildGroupGUID,
|
||||||
|
child.Name AS ChildGroupName,
|
||||||
|
gc.Depth
|
||||||
|
FROM dbo.GroupClosure gc
|
||||||
|
JOIN dbo.[Group] parent
|
||||||
|
ON parent.ObjectGUID = gc.ParentGroup_ObjectGUID
|
||||||
|
JOIN dbo.[Group] child
|
||||||
|
ON child.ObjectGUID = gc.ChildGroup_ObjectGUID;
|
||||||
|
GO
|
||||||
|
|
||||||
|
|
||||||
|
-- ========================================================
|
||||||
|
-- 8. USER OVERVIEW (ADMIN DASHBOARD)
|
||||||
|
-- ========================================================
|
||||||
|
CREATE OR ALTER VIEW dbo.vAuthenticationOverview AS
|
||||||
|
SELECT a.ObjectGUID, a.sAMAccountName, a.mail, a.givenName, a.sn, a.active, a.online, COUNT(DISTINCT r.Role_ID) AS RoleCount, COUNT(DISTINCT g.GroupGUID) AS GroupCount, a.title, a.department, a.streetAddress,
|
||||||
|
a.telephoneNumber, a.physicalDeliveryOfficeName, a.distinguishedName, dbo.ObjectSource.Name AS ObjectSourceName
|
||||||
|
FROM dbo.Authentication AS a LEFT OUTER JOIN
|
||||||
|
dbo.ObjectSource ON a.ObjectSource_ID = dbo.ObjectSource.ID LEFT OUTER JOIN
|
||||||
|
dbo.vAuthenticationRolesExpanded AS r ON r.Authentication_ObjectGUID = a.ObjectGUID LEFT OUTER JOIN
|
||||||
|
dbo.vAuthenticationGroupsExpanded AS g ON g.Authentication_ObjectGUID = a.ObjectGUID
|
||||||
|
GROUP BY a.ObjectGUID, a.sAMAccountName, a.mail, a.givenName, a.sn, a.active, a.online, a.title, a.department, a.streetAddress, a.telephoneNumber, a.physicalDeliveryOfficeName, a.distinguishedName, dbo.ObjectSource.Name
|
||||||
|
GO
|
||||||
|
|
||||||
|
|
||||||
|
-- ========================================================
|
||||||
|
-- 8.1. GROUP OVERVIEW (ADMIN DASHBOARD)
|
||||||
|
-- ========================================================
|
||||||
|
CREATE OR ALTER VIEW dbo.vGroupOverview AS
|
||||||
|
SELECT dbo.[Group].ObjectGUID, dbo.[Group].Name, COUNT(DISTINCT ag.Authentication_ObjectGUID) AS UserCount, COUNT(DISTINCT gr.Role_ID) AS RoleCount, dbo.ObjectSource.Name AS ObjectSourceName,
|
||||||
|
dbo.[Group].distinguishedName
|
||||||
|
FROM dbo.ObjectSource RIGHT OUTER JOIN
|
||||||
|
dbo.[Group] ON dbo.ObjectSource.ID = dbo.[Group].ObjectSource_ID 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
|
||||||
|
GROUP BY dbo.[Group].ObjectGUID, dbo.[Group].Name, dbo.ObjectSource.Name, dbo.[Group].distinguishedName
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- ========================================================
|
||||||
|
-- 9. BONUS: PERMISSION TRACE (WHY DOES USER HAVE THIS?)
|
||||||
|
-- ========================================================
|
||||||
|
CREATE OR ALTER VIEW dbo.vPermissionTrace AS
|
||||||
|
SELECT
|
||||||
|
apd.Authentication_ObjectGUID,
|
||||||
|
apd.RoleName,
|
||||||
|
apd.Scope,
|
||||||
|
apd.Resource,
|
||||||
|
apd.Action,
|
||||||
|
apd.PermissionKey
|
||||||
|
FROM dbo.vAuthenticationPermissionsDetailed apd;
|
||||||
|
GO
|
||||||
@@ -1,621 +0,0 @@
|
|||||||
|
|
||||||
/* =========================================================
|
|
||||||
DATABASE
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
IF DB_ID('Radix_OS') IS NULL
|
|
||||||
BEGIN
|
|
||||||
CREATE DATABASE Radix_OS;
|
|
||||||
END
|
|
||||||
GO
|
|
||||||
|
|
||||||
USE Radix_OS;
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
CLEAN RESET
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
-- DROP VIEW IF EXISTS dbo.vAuthenticationEffectivePermissions;
|
|
||||||
-- DROP VIEW IF EXISTS dbo.vAuthenticationRoles;
|
|
||||||
-- DROP VIEW IF EXISTS dbo.vAuthenticationGroups;
|
|
||||||
-- DROP VIEW IF EXISTS dbo.vGroupHierarchy;
|
|
||||||
-- DROP VIEW IF EXISTS dbo.vAuthentications;
|
|
||||||
-- DROP VIEW IF EXISTS dbo.vEventLog;
|
|
||||||
-- DROP VIEW IF EXISTS dbo.vNotifyTray;
|
|
||||||
|
|
||||||
-- DROP TABLE IF EXISTS dbo.AuthenticationRoles;
|
|
||||||
-- DROP TABLE IF EXISTS dbo.AuthenticationGroups;
|
|
||||||
-- DROP TABLE IF EXISTS dbo.GroupRoles;
|
|
||||||
-- DROP TABLE IF EXISTS dbo.RolePermissions;
|
|
||||||
-- DROP TABLE IF EXISTS dbo.GroupClosure;
|
|
||||||
|
|
||||||
-- DROP TABLE IF EXISTS dbo.NotifyTray;
|
|
||||||
-- DROP TABLE IF EXISTS dbo.NotifyTrayObjects;
|
|
||||||
-- DROP TABLE IF EXISTS dbo.EventLog;
|
|
||||||
-- DROP TABLE IF EXISTS dbo.EventLevels;
|
|
||||||
|
|
||||||
-- DROP TABLE IF EXISTS dbo.Authentication;
|
|
||||||
-- DROP TABLE IF EXISTS dbo.[Group];
|
|
||||||
-- DROP TABLE IF EXISTS dbo.[Role];
|
|
||||||
-- DROP TABLE IF EXISTS dbo.Permission;
|
|
||||||
-- DROP TABLE IF EXISTS dbo.Plugins;
|
|
||||||
-- DROP TABLE IF EXISTS dbo.ObjectSource;
|
|
||||||
-- DROP TABLE IF EXISTS dbo.AuthenticationUAC;
|
|
||||||
-- DROP TABLE IF EXISTS dbo.Vault;
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
CORE TABLES
|
|
||||||
========================================================= */
|
|
||||||
CREATE TABLE dbo.Vault (
|
|
||||||
ID int IDENTITY(1,1) NOT NULL,
|
|
||||||
CustomerGUID uniqueidentifier NOT NULL,
|
|
||||||
Feature nvarchar(128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
|
||||||
Payload nvarchar(MAX) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
|
||||||
Signature nvarchar(MAX) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
|
||||||
Active bit DEFAULT 1 NOT NULL,
|
|
||||||
ExpiresAt datetime NULL,
|
|
||||||
CreatedAt datetime DEFAULT getdate() NOT NULL,
|
|
||||||
UpdatedAt datetime DEFAULT getdate() NULL,
|
|
||||||
CONSTRAINT PK__Vault__3214EC275180843D PRIMARY KEY (ID)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE dbo.ObjectSource (
|
|
||||||
ID INT IDENTITY(1,1) PRIMARY KEY,
|
|
||||||
Name VARCHAR(100) NOT NULL UNIQUE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE dbo.AuthenticationUAC (
|
|
||||||
ID INT PRIMARY KEY,
|
|
||||||
AttributeName NVARCHAR(100),
|
|
||||||
AttributeOriginal VARCHAR(255)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE dbo.[Role] (
|
|
||||||
ID INT IDENTITY(1,1) PRIMARY KEY,
|
|
||||||
Name NVARCHAR(255) UNIQUE,
|
|
||||||
Description NVARCHAR(MAX),
|
|
||||||
RoleType VARCHAR(50)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE dbo.Permission (
|
|
||||||
ID INT IDENTITY(1,1) PRIMARY KEY,
|
|
||||||
Scope VARCHAR(100),
|
|
||||||
Resource VARCHAR(100),
|
|
||||||
Action VARCHAR(100),
|
|
||||||
CONSTRAINT UQ_Permission UNIQUE (Scope, Resource, Action)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE dbo.Plugins (
|
|
||||||
Name VARCHAR(50) PRIMARY KEY,
|
|
||||||
Active BIT,
|
|
||||||
Version VARCHAR(25)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
AUTHENTICATION
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
CREATE TABLE dbo.Authentication (
|
|
||||||
ObjectGUID UNIQUEIDENTIFIER PRIMARY KEY,
|
|
||||||
|
|
||||||
sAMAccountName VARCHAR(255),
|
|
||||||
mail VARCHAR(255),
|
|
||||||
givenName VARCHAR(255),
|
|
||||||
sn VARCHAR(255),
|
|
||||||
|
|
||||||
employeeID VARCHAR(255),
|
|
||||||
title VARCHAR(255),
|
|
||||||
department VARCHAR(255),
|
|
||||||
streetAddress VARCHAR(255),
|
|
||||||
|
|
||||||
userAccountControl_ID INT,
|
|
||||||
|
|
||||||
telephoneNumber VARCHAR(255),
|
|
||||||
physicalDeliveryOfficeName VARCHAR(255),
|
|
||||||
distinguishedName VARCHAR(MAX),
|
|
||||||
|
|
||||||
password VARCHAR(MAX),
|
|
||||||
refreshtoken VARCHAR(MAX),
|
|
||||||
|
|
||||||
active BIT,
|
|
||||||
online BIT,
|
|
||||||
|
|
||||||
ObjectSource_ID INT,
|
|
||||||
FOREIGN KEY (ObjectSource_ID) REFERENCES dbo.ObjectSource(ID)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE dbo.[Group] (
|
|
||||||
ObjectGUID UNIQUEIDENTIFIER PRIMARY KEY,
|
|
||||||
Name VARCHAR(255),
|
|
||||||
ObjectSource_ID INT,
|
|
||||||
distinguishedName VARCHAR(MAX),
|
|
||||||
FOREIGN KEY (ObjectSource_ID) REFERENCES dbo.ObjectSource(ID)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
GROUP CLOSURE
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
CREATE TABLE dbo.GroupClosure (
|
|
||||||
ParentGroup_ObjectGUID UNIQUEIDENTIFIER,
|
|
||||||
ChildGroup_ObjectGUID UNIQUEIDENTIFIER,
|
|
||||||
Depth INT,
|
|
||||||
PRIMARY KEY (ParentGroup_ObjectGUID, ChildGroup_ObjectGUID)
|
|
||||||
);
|
|
||||||
GO
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
RBAC
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
CREATE TABLE dbo.AuthenticationRoles (
|
|
||||||
Authentication_ObjectGUID UNIQUEIDENTIFIER,
|
|
||||||
Role_ID INT,
|
|
||||||
PRIMARY KEY (Authentication_ObjectGUID, Role_ID)
|
|
||||||
);
|
|
||||||
GO
|
|
||||||
|
|
||||||
CREATE TABLE dbo.AuthenticationGroups (
|
|
||||||
Authentication_ObjectGUID UNIQUEIDENTIFIER,
|
|
||||||
Group_ObjectGUID UNIQUEIDENTIFIER,
|
|
||||||
PRIMARY KEY (Authentication_ObjectGUID, Group_ObjectGUID)
|
|
||||||
);
|
|
||||||
GO
|
|
||||||
|
|
||||||
CREATE TABLE dbo.GroupRoles (
|
|
||||||
Group_ObjectGUID UNIQUEIDENTIFIER,
|
|
||||||
Role_ID INT,
|
|
||||||
PRIMARY KEY (Group_ObjectGUID, Role_ID)
|
|
||||||
);
|
|
||||||
GO
|
|
||||||
|
|
||||||
CREATE TABLE dbo.RolePermissions (
|
|
||||||
Role_ID INT,
|
|
||||||
Permission_ID INT,
|
|
||||||
PRIMARY KEY (Role_ID, Permission_ID)
|
|
||||||
);
|
|
||||||
GO
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
EVENT SYSTEM
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
CREATE TABLE dbo.EventLevels (
|
|
||||||
ID INT PRIMARY KEY,
|
|
||||||
LevelName VARCHAR(50),
|
|
||||||
DisplayName VARCHAR(150),
|
|
||||||
Priority INT
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE dbo.EventLog (
|
|
||||||
ID INT IDENTITY(1,1) PRIMARY KEY,
|
|
||||||
Message VARCHAR(MAX),
|
|
||||||
Trace VARCHAR(MAX),
|
|
||||||
Level_ID INT,
|
|
||||||
PluginName VARCHAR(50),
|
|
||||||
Date DATETIME2,
|
|
||||||
ObjectGUID UNIQUEIDENTIFIER
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
NOTIFY SYSTEM
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
CREATE TABLE dbo.NotifyTrayObjects (
|
|
||||||
ID INT IDENTITY(1,1) PRIMARY KEY,
|
|
||||||
PluginName VARCHAR(50),
|
|
||||||
Message VARCHAR(MAX),
|
|
||||||
JSON VARCHAR(MAX),
|
|
||||||
ActionRequired BIT DEFAULT 0,
|
|
||||||
CreatedAt DATETIME2,
|
|
||||||
ExpiresAt DATETIME2
|
|
||||||
);
|
|
||||||
GO
|
|
||||||
|
|
||||||
CREATE TABLE dbo.NotifyTray (
|
|
||||||
ID INT IDENTITY(1,1) PRIMARY KEY,
|
|
||||||
ObjectGUID UNIQUEIDENTIFIER,
|
|
||||||
NotifyTrayObject_ID INT,
|
|
||||||
SeenAt DATETIME2
|
|
||||||
);
|
|
||||||
GO
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
SECURITY VIEWS
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
CREATE VIEW dbo.vAuthenticationRoles AS
|
|
||||||
SELECT a.ObjectGUID, r.ID Role_ID, r.Name, 'DIRECT' Source
|
|
||||||
FROM dbo.Authentication a
|
|
||||||
JOIN dbo.AuthenticationRoles ar ON ar.Authentication_ObjectGUID = a.ObjectGUID
|
|
||||||
JOIN dbo.[Role] r ON r.ID = ar.Role_ID
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT a.ObjectGUID, r.ID, r.Name, 'GROUP'
|
|
||||||
FROM dbo.Authentication a
|
|
||||||
JOIN dbo.AuthenticationGroups ag ON ag.Authentication_ObjectGUID = a.ObjectGUID
|
|
||||||
JOIN dbo.GroupRoles gr ON gr.Group_ObjectGUID = ag.Group_ObjectGUID
|
|
||||||
JOIN dbo.[Role] r ON r.ID = gr.Role_ID;
|
|
||||||
GO
|
|
||||||
|
|
||||||
CREATE VIEW dbo.vAuthenticationEffectivePermissions AS
|
|
||||||
SELECT DISTINCT
|
|
||||||
a.ObjectGUID,
|
|
||||||
p.Scope,
|
|
||||||
p.Resource,
|
|
||||||
p.Action,
|
|
||||||
CONCAT(p.Scope,'.',p.Resource,'.',p.Action) PermissionKey
|
|
||||||
FROM dbo.Authentication a
|
|
||||||
JOIN dbo.vAuthenticationRoles r ON r.ObjectGUID = a.ObjectGUID
|
|
||||||
JOIN dbo.RolePermissions rp ON rp.Role_ID = r.Role_ID
|
|
||||||
JOIN dbo.Permission p ON p.ID = rp.Permission_ID;
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
FIXED vEventLog (SEQUELIZE MATCH + SYSTEM FIX)
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
CREATE OR ALTER VIEW dbo.vEventLog
|
|
||||||
AS
|
|
||||||
SELECT
|
|
||||||
e.ID,
|
|
||||||
e.Message,
|
|
||||||
e.Trace,
|
|
||||||
e.Date,
|
|
||||||
|
|
||||||
e.Level_ID,
|
|
||||||
el.LevelName,
|
|
||||||
el.DisplayName AS LevelDisplayName,
|
|
||||||
el.Priority AS LevelPriority,
|
|
||||||
|
|
||||||
e.PluginName,
|
|
||||||
|
|
||||||
COALESCE(a.sn + ' ' + a.givenName, 'SYSTEM') AS ClearTextUser,
|
|
||||||
|
|
||||||
a.sn AS Surname,
|
|
||||||
a.givenName,
|
|
||||||
|
|
||||||
e.ObjectGUID,
|
|
||||||
|
|
||||||
a.sAMAccountName,
|
|
||||||
a.mail,
|
|
||||||
a.department,
|
|
||||||
|
|
||||||
a.telephoneNumber AS Phone,
|
|
||||||
a.physicalDeliveryOfficeName AS Office,
|
|
||||||
a.streetAddress AS Adress,
|
|
||||||
|
|
||||||
COALESCE(a.ObjectSource_ID, 1) AS ObjectSource_ID,
|
|
||||||
os.Name AS ObjectSourceName
|
|
||||||
|
|
||||||
FROM dbo.EventLog e
|
|
||||||
LEFT JOIN dbo.Authentication a ON a.ObjectGUID = e.ObjectGUID
|
|
||||||
LEFT JOIN dbo.EventLevels el ON el.ID = e.Level_ID
|
|
||||||
LEFT JOIN dbo.ObjectSource os ON os.ID = COALESCE(a.ObjectSource_ID, 1);
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
AUTH VIEW
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
CREATE VIEW dbo.vAuthentications AS
|
|
||||||
SELECT a.*, os.Name AS ObjectSource
|
|
||||||
FROM dbo.Authentication a
|
|
||||||
LEFT JOIN dbo.ObjectSource os ON os.ID = a.ObjectSource_ID;
|
|
||||||
GO
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
GROUP VIEW
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
CREATE VIEW dbo.vGroupHierarchy AS
|
|
||||||
SELECT * FROM dbo.GroupClosure;
|
|
||||||
GO
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
NOTIFY VIEWS
|
|
||||||
========================================================= */
|
|
||||||
CREATE VIEW vNotifyTray AS
|
|
||||||
SELECT
|
|
||||||
n.ID,
|
|
||||||
n.ObjectGUID,
|
|
||||||
n.SeenAt,
|
|
||||||
|
|
||||||
a.sAMAccountName,
|
|
||||||
a.givenName,
|
|
||||||
a.sn,
|
|
||||||
a.mail,
|
|
||||||
a.active,
|
|
||||||
a.online,
|
|
||||||
|
|
||||||
nto.PluginName,
|
|
||||||
nto.JSON,
|
|
||||||
nto.ActionRequired,
|
|
||||||
nto.CreatedAt,
|
|
||||||
nto.Message
|
|
||||||
|
|
||||||
FROM NotifyTray n
|
|
||||||
LEFT JOIN Authentication a ON a.ObjectGUID = n.ObjectGUID
|
|
||||||
LEFT JOIN NotifyTrayObjects nto ON n.ID = n.NotifyTrayObject_ID
|
|
||||||
|
|
||||||
GO
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
SEED DATA
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
INSERT INTO dbo.ObjectSource VALUES ('LOCAL'),('AD');
|
|
||||||
|
|
||||||
INSERT INTO dbo.EventLevels VALUES
|
|
||||||
(-1,'test','Test',5),
|
|
||||||
(0,'success','Success',4),
|
|
||||||
(1,'log','Log',3),
|
|
||||||
(2,'warn','Warn',2),
|
|
||||||
(4,'error','Error',1),
|
|
||||||
(8,'throw_exception','Exception',0);
|
|
||||||
|
|
||||||
INSERT INTO dbo.Plugins VALUES ('SYSTEM',1,'1.0.0');
|
|
||||||
|
|
||||||
INSERT INTO dbo.[Role] (Name,Description,RoleType)
|
|
||||||
VALUES ('ADMIN','System Administrator','SYSTEM');
|
|
||||||
|
|
||||||
INSERT INTO dbo.Permission (Scope,Resource,Action)
|
|
||||||
VALUES ('SYSTEM','ALL','ALL');
|
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO dbo.RolePermissions
|
|
||||||
SELECT r.ID, p.ID
|
|
||||||
FROM dbo.[Role] r
|
|
||||||
JOIN dbo.Permission p ON p.Scope='SYSTEM'
|
|
||||||
WHERE r.Name='ADMIN';
|
|
||||||
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
ADMIN USER
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
INSERT INTO dbo.Authentication (
|
|
||||||
ObjectGUID,
|
|
||||||
sAMAccountName,
|
|
||||||
mail,
|
|
||||||
givenName,
|
|
||||||
sn,
|
|
||||||
active,
|
|
||||||
online,
|
|
||||||
ObjectSource_ID
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
'00000000-0000-0000-0000-000000000001',
|
|
||||||
'admin',
|
|
||||||
'admin@local',
|
|
||||||
'System',
|
|
||||||
'Admin',
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
ID
|
|
||||||
FROM dbo.ObjectSource
|
|
||||||
WHERE Name='LOCAL';
|
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO dbo.AuthenticationRoles
|
|
||||||
SELECT
|
|
||||||
'00000000-0000-0000-0000-000000000001',
|
|
||||||
ID
|
|
||||||
FROM dbo.[Role]
|
|
||||||
WHERE Name='ADMIN';
|
|
||||||
GO
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
EXTENDED RBAC VIEWS
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
-- ========================================================
|
|
||||||
-- 1. USER GROUPS (DIRECT + INHERITED)
|
|
||||||
-- ========================================================
|
|
||||||
CREATE OR ALTER VIEW dbo.vAuthenticationGroupsExpanded AS
|
|
||||||
SELECT
|
|
||||||
ag.Authentication_ObjectGUID,
|
|
||||||
g.ObjectGUID AS GroupGUID,
|
|
||||||
g.Name AS GroupName,
|
|
||||||
'DIRECT' AS Source
|
|
||||||
FROM dbo.AuthenticationGroups ag
|
|
||||||
JOIN dbo.[Group] g
|
|
||||||
ON g.ObjectGUID = ag.Group_ObjectGUID
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
ag.Authentication_ObjectGUID,
|
|
||||||
gc.ParentGroup_ObjectGUID,
|
|
||||||
g.Name,
|
|
||||||
'INHERITED'
|
|
||||||
FROM dbo.AuthenticationGroups ag
|
|
||||||
JOIN dbo.GroupClosure gc
|
|
||||||
ON gc.ChildGroup_ObjectGUID = ag.Group_ObjectGUID
|
|
||||||
JOIN dbo.[Group] g
|
|
||||||
ON g.ObjectGUID = gc.ParentGroup_ObjectGUID;
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
-- ========================================================
|
|
||||||
-- 2. ROLES (DIRECT + GROUP + HIERARCHY)
|
|
||||||
-- ========================================================
|
|
||||||
CREATE OR ALTER VIEW dbo.vAuthenticationRolesExpanded AS
|
|
||||||
SELECT
|
|
||||||
ar.Authentication_ObjectGUID,
|
|
||||||
ar.Role_ID,
|
|
||||||
r.Name AS RoleName,
|
|
||||||
'DIRECT' AS Source
|
|
||||||
FROM dbo.AuthenticationRoles ar
|
|
||||||
JOIN dbo.[Role] r
|
|
||||||
ON r.ID = ar.Role_ID
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
ag.Authentication_ObjectGUID,
|
|
||||||
gr.Role_ID,
|
|
||||||
r.Name,
|
|
||||||
'GROUP'
|
|
||||||
FROM dbo.AuthenticationGroups ag
|
|
||||||
JOIN dbo.GroupRoles gr
|
|
||||||
ON gr.Group_ObjectGUID = ag.Group_ObjectGUID
|
|
||||||
JOIN dbo.[Role] r
|
|
||||||
ON r.ID = gr.Role_ID
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
ag.Authentication_ObjectGUID,
|
|
||||||
gr.Role_ID,
|
|
||||||
r.Name,
|
|
||||||
'GROUP_INHERITED'
|
|
||||||
FROM dbo.AuthenticationGroups ag
|
|
||||||
JOIN dbo.GroupClosure gc
|
|
||||||
ON gc.ChildGroup_ObjectGUID = ag.Group_ObjectGUID
|
|
||||||
JOIN dbo.GroupRoles gr
|
|
||||||
ON gr.Group_ObjectGUID = gc.ParentGroup_ObjectGUID
|
|
||||||
JOIN dbo.[Role] r
|
|
||||||
ON r.ID = gr.Role_ID;
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
-- ========================================================
|
|
||||||
-- 3. EFFECTIVE ROLES (DEDUPLICATED)
|
|
||||||
-- ========================================================
|
|
||||||
CREATE OR ALTER VIEW dbo.vAuthenticationEffectiveRoles AS
|
|
||||||
SELECT DISTINCT
|
|
||||||
Authentication_ObjectGUID,
|
|
||||||
Role_ID,
|
|
||||||
RoleName
|
|
||||||
FROM dbo.vAuthenticationRolesExpanded;
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
-- ========================================================
|
|
||||||
-- 4. PERMISSIONS (DETAILED WITH ROLE SOURCE)
|
|
||||||
-- ========================================================
|
|
||||||
CREATE OR ALTER VIEW dbo.vAuthenticationPermissionsDetailed AS
|
|
||||||
SELECT
|
|
||||||
r.Authentication_ObjectGUID,
|
|
||||||
r.Role_ID,
|
|
||||||
r.RoleName,
|
|
||||||
p.Scope,
|
|
||||||
p.Resource,
|
|
||||||
p.Action,
|
|
||||||
CONCAT(p.Scope,'.',p.Resource,'.',p.Action) AS PermissionKey
|
|
||||||
FROM dbo.vAuthenticationRolesExpanded r
|
|
||||||
JOIN dbo.RolePermissions rp
|
|
||||||
ON rp.Role_ID = r.Role_ID
|
|
||||||
JOIN dbo.Permission p
|
|
||||||
ON p.ID = rp.Permission_ID;
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
-- ========================================================
|
|
||||||
-- 5. PERMISSION MATRIX (FAST LOOKUP)
|
|
||||||
-- ========================================================
|
|
||||||
CREATE OR ALTER VIEW dbo.vPermissionMatrix AS
|
|
||||||
SELECT DISTINCT
|
|
||||||
Authentication_ObjectGUID,
|
|
||||||
CONCAT(Scope,'.',Resource,'.',Action) AS PermissionKey
|
|
||||||
FROM dbo.vAuthenticationPermissionsDetailed;
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
-- ========================================================
|
|
||||||
-- 6. GROUP ROLES OVERVIEW
|
|
||||||
-- ========================================================
|
|
||||||
CREATE OR ALTER VIEW dbo.vGroupRolesDetailed AS
|
|
||||||
SELECT
|
|
||||||
g.ObjectGUID,
|
|
||||||
g.Name AS GroupName,
|
|
||||||
r.ID AS Role_ID,
|
|
||||||
r.Name AS RoleName
|
|
||||||
FROM dbo.GroupRoles gr
|
|
||||||
JOIN dbo.[Group] g
|
|
||||||
ON g.ObjectGUID = gr.Group_ObjectGUID
|
|
||||||
JOIN dbo.[Role] r
|
|
||||||
ON r.ID = gr.Role_ID;
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
-- ========================================================
|
|
||||||
-- 7. GROUP HIERARCHY (READABLE)
|
|
||||||
-- ========================================================
|
|
||||||
CREATE OR ALTER VIEW dbo.vGroupHierarchyReadable AS
|
|
||||||
SELECT
|
|
||||||
parent.ObjectGUID AS ParentGroupGUID,
|
|
||||||
parent.Name AS ParentGroupName,
|
|
||||||
child.ObjectGUID AS ChildGroupGUID,
|
|
||||||
child.Name AS ChildGroupName,
|
|
||||||
gc.Depth
|
|
||||||
FROM dbo.GroupClosure gc
|
|
||||||
JOIN dbo.[Group] parent
|
|
||||||
ON parent.ObjectGUID = gc.ParentGroup_ObjectGUID
|
|
||||||
JOIN dbo.[Group] child
|
|
||||||
ON child.ObjectGUID = gc.ChildGroup_ObjectGUID;
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
-- ========================================================
|
|
||||||
-- 8. USER OVERVIEW (ADMIN DASHBOARD)
|
|
||||||
-- ========================================================
|
|
||||||
CREATE OR ALTER VIEW dbo.vAuthenticationOverview AS
|
|
||||||
SELECT
|
|
||||||
a.ObjectGUID,
|
|
||||||
a.sAMAccountName,
|
|
||||||
a.mail,
|
|
||||||
a.givenName,
|
|
||||||
a.sn,
|
|
||||||
a.active,
|
|
||||||
a.online,
|
|
||||||
|
|
||||||
COUNT(DISTINCT r.Role_ID) AS RoleCount,
|
|
||||||
COUNT(DISTINCT g.GroupGUID) AS GroupCount
|
|
||||||
|
|
||||||
FROM dbo.Authentication a
|
|
||||||
|
|
||||||
LEFT JOIN dbo.vAuthenticationRolesExpanded r
|
|
||||||
ON r.Authentication_ObjectGUID = a.ObjectGUID
|
|
||||||
|
|
||||||
LEFT JOIN dbo.vAuthenticationGroupsExpanded g
|
|
||||||
ON g.Authentication_ObjectGUID = a.ObjectGUID
|
|
||||||
|
|
||||||
GROUP BY
|
|
||||||
a.ObjectGUID,
|
|
||||||
a.sAMAccountName,
|
|
||||||
a.mail,
|
|
||||||
a.givenName,
|
|
||||||
a.sn,
|
|
||||||
a.active,
|
|
||||||
a.online;
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
||||||
-- ========================================================
|
|
||||||
-- 9. BONUS: PERMISSION TRACE (WHY DOES USER HAVE THIS?)
|
|
||||||
-- ========================================================
|
|
||||||
CREATE OR ALTER VIEW dbo.vPermissionTrace AS
|
|
||||||
SELECT
|
|
||||||
apd.Authentication_ObjectGUID,
|
|
||||||
apd.RoleName,
|
|
||||||
apd.Scope,
|
|
||||||
apd.Resource,
|
|
||||||
apd.Action,
|
|
||||||
apd.PermissionKey
|
|
||||||
FROM dbo.vAuthenticationPermissionsDetailed apd;
|
|
||||||
GO
|
|
||||||
@@ -14,6 +14,7 @@ module.exports = {
|
|||||||
localPath,
|
localPath,
|
||||||
cache: {
|
cache: {
|
||||||
startMenuItems: [],
|
startMenuItems: [],
|
||||||
|
clientsOnline: []
|
||||||
},
|
},
|
||||||
runtimeFile: {
|
runtimeFile: {
|
||||||
package: new HotReload(path.join(localPath.root, 'package.json')),
|
package: new HotReload(path.join(localPath.root, 'package.json')),
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
© 2025 Grünflächenamt | Manuel Sowada
|
|
||||||
|
|
||||||
Diese Software ist ausschließlich für den internen dienstlichen Gebrauch durch Mitarbeiter des Grünflächenamtes vorgesehen.
|
|
||||||
Weitergabe, Veröffentlichung oder private Nutzung ist ohne ausdrückliche Genehmigung untersagt.
|
|
||||||
387
package-lock.json
generated
387
package-lock.json
generated
@@ -9,7 +9,6 @@
|
|||||||
"version": "0.9",
|
"version": "0.9",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"activedirectory2": "^2.2.0",
|
|
||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
"child_process": "^1.0.2",
|
"child_process": "^1.0.2",
|
||||||
@@ -21,7 +20,7 @@
|
|||||||
"fs-extra": "^11.3.2",
|
"fs-extra": "^11.3.2",
|
||||||
"https": "^1.0.0",
|
"https": "^1.0.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"ldapjs": "^3.0.7",
|
"ldapts": "^8.1.7",
|
||||||
"module-alias": "^2.2.3",
|
"module-alias": "^2.2.3",
|
||||||
"multer": "^2.0.2",
|
"multer": "^2.0.2",
|
||||||
"net": "^1.0.2",
|
"net": "^1.0.2",
|
||||||
@@ -298,92 +297,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.6.5.tgz",
|
"resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.6.5.tgz",
|
||||||
"integrity": "sha512-3zwefSMwHpu8iVUW8YYz227sIv6UFqO31p1Bf1ZH/Vom7CmNyUsXjDBlnNzcuhmOL1XfxZ3nvND42kR23XlbcQ=="
|
"integrity": "sha512-3zwefSMwHpu8iVUW8YYz227sIv6UFqO31p1Bf1ZH/Vom7CmNyUsXjDBlnNzcuhmOL1XfxZ3nvND42kR23XlbcQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@ldapjs/asn1": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-G9+DkEOirNgdPmD0I8nu57ygQJKOOgFEMKknEuQvIHbGLwP3ny1mY+OTUYLCbCaGJP4sox5eYgBJRuSUpnAddA==",
|
|
||||||
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md"
|
|
||||||
},
|
|
||||||
"node_modules/@ldapjs/attribute": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ldapjs/attribute/-/attribute-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-ptMl2d/5xJ0q+RgmnqOi3Zgwk/TMJYG7dYMC0Keko+yZU6n+oFM59MjQOUht5pxJeS4FWrImhu/LebX24vJNRQ==",
|
|
||||||
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
|
|
||||||
"dependencies": {
|
|
||||||
"@ldapjs/asn1": "2.0.0",
|
|
||||||
"@ldapjs/protocol": "^1.2.1",
|
|
||||||
"process-warning": "^2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@ldapjs/change": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ldapjs/change/-/change-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-EOQNFH1RIku3M1s0OAJOzGfAohuFYXFY4s73wOhRm4KFGhmQQ7MChOh2YtYu9Kwgvuq1B0xKciXVzHCGkB5V+Q==",
|
|
||||||
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
|
|
||||||
"dependencies": {
|
|
||||||
"@ldapjs/asn1": "2.0.0",
|
|
||||||
"@ldapjs/attribute": "1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@ldapjs/controls": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ldapjs/controls/-/controls-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-2pFdD1yRC9V9hXfAWvCCO2RRWK9OdIEcJIos/9cCVP9O4k72BY1bLDQQ4KpUoJnl4y/JoD4iFgM+YWT3IfITWw==",
|
|
||||||
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
|
|
||||||
"dependencies": {
|
|
||||||
"@ldapjs/asn1": "^1.2.0",
|
|
||||||
"@ldapjs/protocol": "^1.2.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@ldapjs/controls/node_modules/@ldapjs/asn1": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-KX/qQJ2xxzvO2/WOvr1UdQ+8P5dVvuOLk/C9b1bIkXxZss8BaR28njXdPgFCpj5aHaf1t8PmuVnea+N9YG9YMw==",
|
|
||||||
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md"
|
|
||||||
},
|
|
||||||
"node_modules/@ldapjs/dn": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ldapjs/dn/-/dn-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-R72zH5ZeBj/Fujf/yBu78YzpJjJXG46YHFo5E4W1EqfNpo1UsVPqdLrRMXeKIsJT3x9dJVIfR6OpzgINlKpi0A==",
|
|
||||||
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
|
|
||||||
"dependencies": {
|
|
||||||
"@ldapjs/asn1": "2.0.0",
|
|
||||||
"process-warning": "^2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@ldapjs/filter": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ldapjs/filter/-/filter-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-TwPK5eEgNdUO1ABPBUQabcZ+h9heDORE4V9WNZqCtYLKc06+6+UAJ3IAbr0L0bYTnkkWC/JEQD2F+zAFsuikNw==",
|
|
||||||
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
|
|
||||||
"dependencies": {
|
|
||||||
"@ldapjs/asn1": "2.0.0",
|
|
||||||
"@ldapjs/protocol": "^1.2.1",
|
|
||||||
"process-warning": "^2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@ldapjs/messages": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ldapjs/messages/-/messages-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-K7xZpXJ21bj92jS35wtRbdcNrwmxAtPwy4myeh9duy/eR3xQKvikVycbdWVzkYEAVE5Ce520VXNOwCHjomjCZw==",
|
|
||||||
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
|
|
||||||
"dependencies": {
|
|
||||||
"@ldapjs/asn1": "^2.0.0",
|
|
||||||
"@ldapjs/attribute": "^1.0.0",
|
|
||||||
"@ldapjs/change": "^1.0.0",
|
|
||||||
"@ldapjs/controls": "^2.1.0",
|
|
||||||
"@ldapjs/dn": "^1.1.0",
|
|
||||||
"@ldapjs/filter": "^2.1.1",
|
|
||||||
"@ldapjs/protocol": "^1.2.1",
|
|
||||||
"process-warning": "^2.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@ldapjs/protocol": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ldapjs/protocol/-/protocol-1.2.1.tgz",
|
|
||||||
"integrity": "sha512-O89xFDLW2gBoZWNXuXpBSM32/KealKCTb3JGtJdtUQc7RjAk8XzrRgyz02cPAwGKwKPxy0ivuC7UP9bmN87egQ==",
|
|
||||||
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md"
|
|
||||||
},
|
|
||||||
"node_modules/@socket.io/component-emitter": {
|
"node_modules/@socket.io/component-emitter": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||||
@@ -455,11 +368,6 @@
|
|||||||
"node": ">=6.5"
|
"node": ">=6.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/abstract-logging": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="
|
|
||||||
},
|
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
||||||
@@ -472,40 +380,6 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/activedirectory2": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/activedirectory2/-/activedirectory2-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-uGbw74xttFG6hgocU8T1a0oDofLsyTp44BPTn42JN5C2QlyO5kRl2E7ZoUdfpFzV+yxhaQTKI+8QqRB5HONYvA==",
|
|
||||||
"deprecated": "Decomissioned.",
|
|
||||||
"dependencies": {
|
|
||||||
"abstract-logging": "^2.0.0",
|
|
||||||
"async": "^3.1.0",
|
|
||||||
"ldapjs": "^2.3.3",
|
|
||||||
"merge-options": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/activedirectory2/node_modules/ldapjs": {
|
|
||||||
"version": "2.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-2.3.3.tgz",
|
|
||||||
"integrity": "sha512-75QiiLJV/PQqtpH+HGls44dXweviFwQ6SiIK27EqzKQ5jU/7UFrl2E5nLdQ3IYRBzJ/AVFJI66u0MZ0uofKYwg==",
|
|
||||||
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
|
|
||||||
"dependencies": {
|
|
||||||
"abstract-logging": "^2.0.0",
|
|
||||||
"asn1": "^0.2.4",
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"backoff": "^2.5.0",
|
|
||||||
"ldap-filter": "^0.3.3",
|
|
||||||
"once": "^1.4.0",
|
|
||||||
"vasync": "^2.2.0",
|
|
||||||
"verror": "^1.8.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/agent-base": {
|
"node_modules/agent-base": {
|
||||||
"version": "7.1.4",
|
"version": "7.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||||
@@ -541,38 +415,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||||
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
|
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
|
||||||
},
|
},
|
||||||
"node_modules/asn1": {
|
|
||||||
"version": "0.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
|
||||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"safer-buffer": "~2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/assert-plus": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/async": {
|
|
||||||
"version": "3.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
|
||||||
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="
|
|
||||||
},
|
|
||||||
"node_modules/backoff": {
|
|
||||||
"version": "2.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
|
|
||||||
"integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==",
|
|
||||||
"dependencies": {
|
|
||||||
"precond": "0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||||
@@ -885,11 +727,6 @@
|
|||||||
"node": ">=6.6.0"
|
"node": ">=6.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/core-util-is": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
|
|
||||||
},
|
|
||||||
"node_modules/cors": {
|
"node_modules/cors": {
|
||||||
"version": "2.8.5",
|
"version": "2.8.5",
|
||||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||||
@@ -1219,14 +1056,6 @@
|
|||||||
"node": ">=22.15.0"
|
"node": ">=22.15.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/extsprintf": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==",
|
|
||||||
"engines": [
|
|
||||||
"node >=0.6.0"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/finalhandler": {
|
"node_modules/finalhandler": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
||||||
@@ -1558,14 +1387,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-plain-obj": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-promise": {
|
"node_modules/is-promise": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
||||||
@@ -1660,37 +1481,15 @@
|
|||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ldap-filter": {
|
"node_modules/ldapts": {
|
||||||
"version": "0.3.3",
|
"version": "8.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/ldapts/-/ldapts-8.1.7.tgz",
|
||||||
"integrity": "sha512-/tFkx5WIn4HuO+6w9lsfxq4FN3O+fDZeO9Mek8dCD8rTUpqzRa766BOBO7BcGkn3X86m5+cBm1/2S/Shzz7gMg==",
|
"integrity": "sha512-TJl6T92eIwMf/OJ0hDfKVa6ISwzo+lqCWCI5Mf//ARlKa3LKQZaSrme/H2rCRBhW0DZCQlrsV+fgoW5YHRNLUw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"assert-plus": "^1.0.0"
|
"strict-event-emitter-types": "2.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8"
|
"node": ">=20"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ldapjs": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-1ky+WrN+4CFMuoekUOv7Y1037XWdjKpu0xAPwSP+9KdvmV9PG+qOKlssDV6a+U32apwxdD3is/BZcWOYzN30cg==",
|
|
||||||
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
|
|
||||||
"dependencies": {
|
|
||||||
"@ldapjs/asn1": "^2.0.0",
|
|
||||||
"@ldapjs/attribute": "^1.0.0",
|
|
||||||
"@ldapjs/change": "^1.0.0",
|
|
||||||
"@ldapjs/controls": "^2.1.0",
|
|
||||||
"@ldapjs/dn": "^1.1.0",
|
|
||||||
"@ldapjs/filter": "^2.1.1",
|
|
||||||
"@ldapjs/messages": "^1.3.0",
|
|
||||||
"@ldapjs/protocol": "^1.2.1",
|
|
||||||
"abstract-logging": "^2.0.1",
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"backoff": "^2.5.0",
|
|
||||||
"once": "^1.4.0",
|
|
||||||
"vasync": "^2.2.1",
|
|
||||||
"verror": "^1.10.1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
@@ -1769,17 +1568,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/merge-options": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/merge-options/-/merge-options-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-S7xYIeWHl2ZUKF7SDeBhGg6rfv5bKxVBdk95s/I7wVF8d+hjLSztJ/B271cnUiF6CAFduEQ5Zn3HYwAjT16DlQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"is-plain-obj": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mime-db": {
|
"node_modules/mime-db": {
|
||||||
"version": "1.54.0",
|
"version": "1.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||||
@@ -2102,23 +1890,6 @@
|
|||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/@isaacs/balanced-match": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": "20 || >=22"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/@isaacs/brace-expansion": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@isaacs/balanced-match": "^4.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "20 || >=22"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/@isaacs/fs-minipass": {
|
"node_modules/npm/node_modules/@isaacs/fs-minipass": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -2637,14 +2408,6 @@
|
|||||||
"node": ">=0.3.1"
|
"node": ">=0.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/encoding": {
|
|
||||||
"version": "0.1.13",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"iconv-lite": "^0.6.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/env-paths": {
|
"node_modules/npm/node_modules/env-paths": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -2653,10 +2416,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/err-code": {
|
|
||||||
"version": "2.0.3",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/exponential-backoff": {
|
"node_modules/npm/node_modules/exponential-backoff": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -2769,13 +2528,6 @@
|
|||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/imurmurhash": {
|
|
||||||
"version": "0.1.4",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8.19"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/ini": {
|
"node_modules/npm/node_modules/ini": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -3448,17 +3200,6 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/promise-retry": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"err-code": "^2.0.2",
|
|
||||||
"retry": "^0.12.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/promzard": {
|
"node_modules/npm/node_modules/promzard": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -3496,13 +3237,6 @@
|
|||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/retry": {
|
|
||||||
"version": "0.12.0",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/safer-buffer": {
|
"node_modules/npm/node_modules/safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -3582,22 +3316,6 @@
|
|||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/spdx-correct": {
|
|
||||||
"version": "3.2.0",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"spdx-expression-parse": "^3.0.0",
|
|
||||||
"spdx-license-ids": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"spdx-exceptions": "^2.1.0",
|
|
||||||
"spdx-license-ids": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/spdx-exceptions": {
|
"node_modules/npm/node_modules/spdx-exceptions": {
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -3727,47 +3445,11 @@
|
|||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/unique-filename": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"unique-slug": "^6.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/unique-slug": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"imurmurhash": "^0.1.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/util-deprecate": {
|
"node_modules/npm/node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/validate-npm-package-license": {
|
|
||||||
"version": "3.0.4",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"spdx-correct": "^3.0.0",
|
|
||||||
"spdx-expression-parse": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"spdx-exceptions": "^2.1.0",
|
|
||||||
"spdx-license-ids": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/validate-npm-package-name": {
|
"node_modules/npm/node_modules/validate-npm-package-name": {
|
||||||
"version": "7.0.2",
|
"version": "7.0.2",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -3960,14 +3642,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
|
||||||
"integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="
|
"integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="
|
||||||
},
|
},
|
||||||
"node_modules/precond": {
|
|
||||||
"version": "0.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
|
|
||||||
"integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/process": {
|
"node_modules/process": {
|
||||||
"version": "0.11.10",
|
"version": "0.11.10",
|
||||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
@@ -3976,11 +3650,6 @@
|
|||||||
"node": ">= 0.6.0"
|
"node": ">= 0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/process-warning": {
|
|
||||||
"version": "2.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz",
|
|
||||||
"integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA=="
|
|
||||||
},
|
|
||||||
"node_modules/proxy-addr": {
|
"node_modules/proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
@@ -4508,6 +4177,11 @@
|
|||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/strict-event-emitter-types": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA=="
|
||||||
|
},
|
||||||
"node_modules/string_decoder": {
|
"node_modules/string_decoder": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
@@ -4735,43 +4409,6 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vasync": {
|
|
||||||
"version": "2.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz",
|
|
||||||
"integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==",
|
|
||||||
"engines": [
|
|
||||||
"node >=0.6.0"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"verror": "1.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vasync/node_modules/verror": {
|
|
||||||
"version": "1.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
|
||||||
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
|
|
||||||
"engines": [
|
|
||||||
"node >=0.6.0"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"core-util-is": "1.0.2",
|
|
||||||
"extsprintf": "^1.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/verror": {
|
|
||||||
"version": "1.10.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
|
|
||||||
"integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
|
|
||||||
"dependencies": {
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"core-util-is": "1.0.2",
|
|
||||||
"extsprintf": "^1.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"licensefile": "license_internal.txt",
|
"licensefile": "license_internal.txt",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"activedirectory2": "^2.2.0",
|
|
||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
"child_process": "^1.0.2",
|
"child_process": "^1.0.2",
|
||||||
@@ -33,7 +32,7 @@
|
|||||||
"fs-extra": "^11.3.2",
|
"fs-extra": "^11.3.2",
|
||||||
"https": "^1.0.0",
|
"https": "^1.0.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"ldapjs": "^3.0.7",
|
"ldapts": "^8.1.7",
|
||||||
"module-alias": "^2.2.3",
|
"module-alias": "^2.2.3",
|
||||||
"multer": "^2.0.2",
|
"multer": "^2.0.2",
|
||||||
"net": "^1.0.2",
|
"net": "^1.0.2",
|
||||||
|
|||||||
BIN
public/images/settings.png
Normal file
BIN
public/images/settings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
BIN
public/images/shield.png
Normal file
BIN
public/images/shield.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
@@ -140,6 +140,7 @@ class ContextMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show(target, options = {}) {
|
show(target, options = {}) {
|
||||||
|
// this.hide();
|
||||||
this.closeAllSubmenus();
|
this.closeAllSubmenus();
|
||||||
|
|
||||||
let x, y;
|
let x, y;
|
||||||
|
|||||||
@@ -147,25 +147,28 @@ function slideOutMessage(message) {
|
|||||||
//#region Feedbox
|
//#region Feedbox
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* feedbox({
|
feedbox({
|
||||||
* title: `<span style="color:#f44336">⚠ Upload abbrechen?</span>`,
|
title: `<span style="color:#f44336">⚠ Upload abbrechen?</span>`,
|
||||||
* message: `
|
lock: true,
|
||||||
* <p>Es laufen noch <b>aktive Uploads</b>.</p>
|
primary: 'yes',
|
||||||
* <p>Möchtest du wirklich <u>alle abbrechen</u>?</p>
|
replace: false,
|
||||||
* `,
|
message: `
|
||||||
* buttons: {
|
<p>Es laufen noch <b>aktive Uploads</b>.</p>
|
||||||
* yes: {
|
<p>Möchtest du wirklich <u>alle abbrechen</u>?</p>
|
||||||
* text: '<b>Ja</b>, abbrechen',
|
`,
|
||||||
* onClick: () => stopUploadQueue()
|
buttons: {
|
||||||
* },
|
yes: {
|
||||||
* no: {
|
text: '<b>Ja</b>, abbrechen',
|
||||||
* text: 'Weiter hochladen'
|
onClick: () => stopUploadQueue()
|
||||||
* },
|
},
|
||||||
* cancel: {
|
no: {
|
||||||
* text: 'Zurück'
|
text: 'Weiter hochladen'
|
||||||
* }
|
},
|
||||||
* }
|
cancel: {
|
||||||
*});
|
text: 'Zurück'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
*/
|
*/
|
||||||
function feedbox({
|
function feedbox({
|
||||||
title = '',
|
title = '',
|
||||||
|
|||||||
@@ -1033,6 +1033,7 @@ function virtualTable({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Live-Counter
|
// Live-Counter
|
||||||
|
if(!filterConfig || filterConfig.hideCounter) return
|
||||||
if(filterState.counterEl) filterState.counterEl.textContent = `${visibleCount} Treffer`;
|
if(filterState.counterEl) filterState.counterEl.textContent = `${visibleCount} Treffer`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1224,20 +1225,21 @@ window.addEventListener('resize', () => {
|
|||||||
tableEl.before(container);
|
tableEl.before(container);
|
||||||
|
|
||||||
|
|
||||||
// Höhe messen → thead offset setzen
|
if(!filterConfig.hideCounter) {
|
||||||
requestAnimationFrame(()=>{
|
filterState.counterEl = document.createElement('div');
|
||||||
const h = container.getBoundingClientRect().height;
|
filterState.counterEl.className = 'live-counter';
|
||||||
tableEl.style.setProperty('--filter-height', h + 'px');
|
container.appendChild(filterState.counterEl);
|
||||||
});
|
}
|
||||||
|
|
||||||
filterState.counterEl = document.createElement('div');
|
|
||||||
filterState.counterEl.className = 'live-counter';
|
|
||||||
container.appendChild(filterState.counterEl);
|
|
||||||
syncFilterWidth(container);
|
syncFilterWidth(container);
|
||||||
|
|
||||||
const wrapperResizeObserver = new ResizeObserver(() => {
|
const wrapperResizeObserver = new ResizeObserver(() => {
|
||||||
// Filter-Header anpassen
|
// Filter-Header anpassen
|
||||||
syncFilterWidth(container);
|
syncFilterWidth(container);
|
||||||
|
|
||||||
|
// Höhe messen → thead offset setzen
|
||||||
|
const h = container.getBoundingClientRect().height;
|
||||||
|
tableEl.style.setProperty('--filter-height', h + 'px');
|
||||||
|
|
||||||
// Tabelle neu rendern
|
// Tabelle neu rendern
|
||||||
rebuildPrefix();
|
rebuildPrefix();
|
||||||
@@ -1483,7 +1485,7 @@ window.addEventListener('resize', () => {
|
|||||||
},
|
},
|
||||||
refresh(){ applyFilters(); render(); },
|
refresh(){ applyFilters(); render(); },
|
||||||
clearData() { data = [] },
|
clearData() { data = [] },
|
||||||
source(newData) { data = []; this.addData(newData); },
|
source(newData) { data = []; this.addData(newData); this.refresh(); },
|
||||||
prepareData() { prepareData(); }
|
prepareData() { prepareData(); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,19 +11,44 @@ const restoreIcon = '🗗';
|
|||||||
|
|
||||||
const startBtn = document.getElementById('start-btn');
|
const startBtn = document.getElementById('start-btn');
|
||||||
const startMenu = document.getElementById('start-menu');
|
const startMenu = document.getElementById('start-menu');
|
||||||
|
const taskbar = document.getElementById('taskbar');
|
||||||
const windowsContainer = document.getElementById('windows');
|
const windowsContainer = document.getElementById('windows');
|
||||||
const taskbarWindows = document.getElementById('taskbar-windows');
|
const taskbarWindows = document.getElementById('taskbar-windows');
|
||||||
const ctx = new ContextMenu();
|
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
|
||||||
|
startBtn.classList.toggle('active');
|
||||||
startMenu.classList.toggle('hidden');
|
startMenu.classList.toggle('hidden');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function updateStartMenuPosition() {
|
||||||
|
const height = taskbar.offsetHeight;
|
||||||
|
startMenu.style.bottom = (height + 12) + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new ResizeObserver(entries => {
|
||||||
|
for (let entry of entries) {
|
||||||
|
const height = entry.contentRect.height;
|
||||||
|
MAX_PADDING = { left: 4, top: 4, right: 4, bottom: height + 8 };
|
||||||
|
|
||||||
|
windowsContainer.querySelectorAll('[data-winid]').forEach(win => {
|
||||||
|
if(win.dataset.state === 'maximized') {
|
||||||
|
applyMaximized(win) ;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(taskbar);
|
||||||
|
|
||||||
|
window.addEventListener('resize', updateStartMenuPosition);
|
||||||
|
window.addEventListener('load', updateStartMenuPosition);
|
||||||
|
|
||||||
// Launch app when clicking start menu item
|
// Launch app when clicking start menu item
|
||||||
document.addEventListener('click', async (e) => {
|
document.addEventListener('click', async (e) => {
|
||||||
const target = e.target.closest('.start-item');
|
const target = e.target.closest('.start-item');
|
||||||
@@ -31,6 +56,7 @@ document.addEventListener('click', async (e) => {
|
|||||||
const clickedButton = startBtn.contains(e.target);
|
const clickedButton = startBtn.contains(e.target);
|
||||||
|
|
||||||
if(!clickedInsideMenu && !clickedButton) {
|
if(!clickedInsideMenu && !clickedButton) {
|
||||||
|
startBtn.classList.remove('active');
|
||||||
startMenu.classList.add('hidden');
|
startMenu.classList.add('hidden');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
563
public/javascript/rbacAPI.js
Normal file
563
public/javascript/rbacAPI.js
Normal file
@@ -0,0 +1,563 @@
|
|||||||
|
const ctxRBAC = 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: 35,
|
||||||
|
// buffer: 10,
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// ctxRBAC.setItems([
|
||||||
|
// {
|
||||||
|
// label: "Details",
|
||||||
|
// onClick: () => showAuthDetails(row.ObjectGUID)
|
||||||
|
// }
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
// ctxRBAC.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();
|
||||||
|
|
||||||
|
// ctxRBAC.setItems([
|
||||||
|
// {
|
||||||
|
// label: "Details",
|
||||||
|
// onClick: () => showAuthDetails(row.ObjectGUID)
|
||||||
|
// }
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
// ctxRBAC.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' } });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
class Tile {
|
||||||
|
constructor(title, parent, options = {}) {
|
||||||
|
this.title = title;
|
||||||
|
this.parent = parent;
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const tile = document.createElement('div');
|
||||||
|
tile.classList.add('tile');
|
||||||
|
tile.innerHTML = this.title;
|
||||||
|
|
||||||
|
if(this.options.onclick) {
|
||||||
|
tile.addEventListener('click', this.options.onclick);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent.appendChild(tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
// 📥 LOADERS
|
||||||
|
//////////////////////////////
|
||||||
|
|
||||||
|
async function loadUsers() {
|
||||||
|
try {
|
||||||
|
// rbacUsersVT.source(await RBAC.loadUsers());
|
||||||
|
const users = await RBAC.loadUsers();
|
||||||
|
|
||||||
|
users.forEach(user => {
|
||||||
|
const tile = new Tile(`[${user.UserCount}] ${user.sAMAccountName}`, document.querySelector('#rbacTabContents') , { onclick: () => showAuthDetails(user.ObjectGUID) });
|
||||||
|
tile.render();
|
||||||
|
})
|
||||||
|
|
||||||
|
} 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();
|
||||||
@@ -41,11 +41,11 @@ function writeEventLog(levelId, pluginName, message) {
|
|||||||
// sendToParams: where clause to find objectGUIDs to send
|
// sendToParams: where clause to find objectGUIDs to send
|
||||||
function sendUserEvent(pluginName, message, sendToParams, levelId = -1) {
|
function sendUserEvent(pluginName, message, sendToParams, levelId = -1) {
|
||||||
mainSocket.emit('event', {
|
mainSocket.emit('event', {
|
||||||
objectGuid: getCookie('ObjectGUID'),
|
objectGuid: getCookie('ObjectGUID'),
|
||||||
levelId: levelId,
|
levelId: levelId,
|
||||||
pluginName: pluginName,
|
pluginName: pluginName,
|
||||||
message: message.stack === undefined ? message : { message: message.message },
|
message: message.stack === undefined ? message : { message: message.message },
|
||||||
sendToParams: sendToParams
|
sendToParams: sendToParams
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5,11 +5,11 @@
|
|||||||
#tutorial-tooltip img {filter: invert(1);}
|
#tutorial-tooltip img {filter: invert(1);}
|
||||||
|
|
||||||
#start-btn { background:var(--theme-accent-default-backcolor); color:var(--theme-accent-default-color); }
|
#start-btn { background:var(--theme-accent-default-backcolor); color:var(--theme-accent-default-color); }
|
||||||
|
#start-btn.active { background:var(--theme-accent-active-backcolor); color:var(--theme-accent-active-color); }
|
||||||
#start-btn:hover { background:var(--theme-accent-hover-backcolor); color:var(--theme-accent-hover-color); }
|
#start-btn:hover { background:var(--theme-accent-hover-backcolor); color:var(--theme-accent-hover-color); }
|
||||||
#start-btn:active { background:var(--theme-accent-active-backcolor); color:var(--theme-accent-active-color); }
|
|
||||||
|
|
||||||
/* #start-menu, .submenu { background:var(--theme-taskbar-backcolor); color:var(--theme-taskbar-color); } */
|
/* #start-menu, .submenu { background:var(--theme-taskbar-backcolor); color:var(--theme-taskbar-color); } */
|
||||||
#start-menu { padding-bottom:10px; background:var(--theme-startmenu-backcolor); color:var(--theme-startmenu-color); border:3px solid rgb(128,128,128); }
|
#start-menu { padding-bottom:10px; background:var(--theme-startmenu-backcolor); color:var(--theme-startmenu-color); border:1px solid rgb(10,10,10); }
|
||||||
.start-submenu-head { background:var(--theme-startmenu-submenu-header-backcolor); color:var(--theme-startmenu-submenu-header-backcolor) }
|
.start-submenu-head { background:var(--theme-startmenu-submenu-header-backcolor); color:var(--theme-startmenu-submenu-header-backcolor) }
|
||||||
.start-icon { background:var(--theme-accent-default-backcolor); }
|
.start-icon { background:var(--theme-accent-default-backcolor); }
|
||||||
|
|
||||||
@@ -21,10 +21,11 @@
|
|||||||
.start-item .unload { background:var(--theme-startmenu-item-disabled-backcolor); color:var(--theme-startmenu-item-disabled-color); }
|
.start-item .unload { background:var(--theme-startmenu-item-disabled-backcolor); color:var(--theme-startmenu-item-disabled-color); }
|
||||||
/* #taskbar .taskbar-item { background:var(--theme-taskbar-item-backcolor); } */
|
/* #taskbar .taskbar-item { background:var(--theme-taskbar-item-backcolor); } */
|
||||||
|
|
||||||
.taskbar-item { position: relative;}
|
.taskbar-item { position: relative; }
|
||||||
.taskbar-item::before { background: var(--theme-accent-active-color); }
|
.taskbar-item::before { background: var(--theme-accent-active-backcolor); }
|
||||||
.taskbar-item.focus::before { background: var(--theme-accent-active-backcolor); }
|
.taskbar-item.focus::before { background: var(--theme-accent-active-backcolor); }
|
||||||
/* .taskbar-item.minimized { background:var(--theme-taskbar-item-minimized-backcolor); color:var(--theme-taskbar-item-minimized-color); border-color:var(--theme-taskbar-item-minimized-border-color);} */
|
.taskbar-item.minimized::before { background: var(--theme-taskbar-item-minimized-backcolor); }
|
||||||
|
/* .taskbar-item.minimized { background:var(--theme-taskbar-item-minimized-backcolor); color:var(--theme-taskbar-item-minimized-color); border-color:var(--theme-taskbar-item-minimized-border-color);} */
|
||||||
.taskbar-item.default { background:var(--theme-taskbar-item-default-backcolor); color:var(--theme-taskbar-item-default-color); border-color:var(--theme-taskbar-item-default-border-color);}
|
.taskbar-item.default { background:var(--theme-taskbar-item-default-backcolor); color:var(--theme-taskbar-item-default-color); border-color:var(--theme-taskbar-item-default-border-color);}
|
||||||
.taskbar-item:hover { background-color:var(--theme-startmenu-item-hover-backcolor); color:var(--theme-startmenu-item-hover-color); }
|
.taskbar-item:hover { background-color:var(--theme-startmenu-item-hover-backcolor); color:var(--theme-startmenu-item-hover-color); }
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.selectable { user-select: text !important; cursor: var(--theme-cursor-pointer) 0 16, pointer; }
|
.selectable { user-select: text !important; cursor: var(--theme-cursor-default) 1 1, auto; }
|
||||||
|
|
||||||
input, input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="date"], textarea, select { border-width:2px; border-style:solid; font-size: var(--fontSize); font-family: var(--fontFamily); border-radius:10px; padding:10px 12px; outline:none; transition:all var(--times-transition-colors) ease; width:auto; }
|
/* input, input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="date"], textarea, select { border-width:2px; border-style:solid; font-size: var(--fontSize); font-family: var(--fontFamily); border-radius:10px; padding:10px 12px; outline:none; transition:all var(--times-transition-colors) ease; width:auto; } */
|
||||||
|
input, input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="date"], textarea, select { border-width:2px; border-style:solid; font-size: var(--fontSize); font-family: var(--fontFamily); border-radius:10px; padding:4px 6px; outline:none; transition:all var(--times-transition-colors) ease; width:auto; }
|
||||||
input.width\:100px { width:100px; }
|
input.width\:100px { width:100px; }
|
||||||
input.width\:90px { width:90px; }
|
input.width\:90px { width:90px; }
|
||||||
input.width\:75px { width:75px; }
|
input.width\:75px { width:75px; }
|
||||||
@@ -18,7 +19,9 @@ input.width\:25px { width:25px; }
|
|||||||
*::placeholder, input[id="sAMAccountName"] { font-style:italic; font-weight:100; letter-spacing:3px; }
|
*::placeholder, input[id="sAMAccountName"] { font-style:italic; font-weight:100; letter-spacing:3px; }
|
||||||
|
|
||||||
html, button { font-size: var(--fontSize); font-family: var(--fontFamily); }
|
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.monolyth { background-color:transparent; }
|
||||||
button:not(:disabled).monolyth:hover { opacity:0.9; }
|
button:not(:disabled).monolyth:hover { opacity:0.9; }
|
||||||
button.bluebutton { color:var(--theme-button-blue-default-color); background:var(--theme-button-blue-default-backcolor); }
|
button.bluebutton { color:var(--theme-button-blue-default-color); background:var(--theme-button-blue-default-backcolor); }
|
||||||
@@ -35,14 +38,14 @@ button:not(:disabled).yellowbutton:hover { background:var(--theme-button-yellow-
|
|||||||
|
|
||||||
|
|
||||||
/* #region Container */
|
/* #region Container */
|
||||||
.container.static { width:calc(100% - 20px); margin:10px auto; display:flex; gap:12px; min-height:0; overflow:auto; max-height:100%; flex-direction: column;}
|
.container.static { width:calc(100% - 10px); margin:5px auto; display:flex; gap:6px; min-height:0; overflow:auto; max-height:100vh; flex-direction: column;}
|
||||||
/* .card.static { display:flex; flex-direction:column;flex: 0 0 auto; } */
|
/* .card.static { display:flex; flex-direction:column;flex: 0 0 auto; } */
|
||||||
.card.static.row { overflow:hidden; display:flex; flex-direction:row; flex-wrap: wrap;}
|
.card.static.row { overflow:hidden; display:flex; flex-direction:row; flex-wrap: wrap;}
|
||||||
.card.static { overflow:hidden; display:flex; flex-direction:column; }
|
.card.static { overflow:hidden; display:flex; flex-direction:column; }
|
||||||
|
|
||||||
.container { width:calc(100% - 20px); margin:10px auto; display:grid; grid-template-columns:100%; gap:12px; min-height:0; overflow:auto; max-height:100%; }
|
.container:not(.static) { width:calc(100% - 10px); margin:5px auto; display:grid; grid-template-columns:100%; gap:12px; min-height:0; overflow:auto; max-height:100%; }
|
||||||
.container:not(.static) * { box-sizing:border-box; }
|
.container:not(.static) * { box-sizing:border-box; }
|
||||||
.card { border-width:1px; border-style:solid; border-radius:8px; padding:20px; }
|
.card { border-width:1px; border-style:solid; border-radius:8px; padding:10px; }
|
||||||
|
|
||||||
.grid { display:grid; gap:16px; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); }
|
.grid { display:grid; gap:16px; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); }
|
||||||
/* #endregion */
|
/* #endregion */
|
||||||
@@ -93,114 +96,16 @@ button:not(:disabled).yellowbutton:hover { background:var(--theme-button-yellow-
|
|||||||
<span class="cb-label">Ein / Aus</span>
|
<span class="cb-label">Ein / Aus</span>
|
||||||
</label>
|
</label>
|
||||||
*/
|
*/
|
||||||
.cb-switch {
|
.cb-switch input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
|
||||||
--w: var(--responsive-switch-width);
|
.switch-track { width: var(--responsive-switch-width); height: var(--responsive-switch-height); border-radius: 999px; padding: 3px; border: 1px solid; box-sizing: border-box; display: flex; align-items: center; position: relative; flex-shrink: 0; transition: background var(--times-transition-colors) ease, border-color var(--times-transition-colors) ease; }
|
||||||
--h: var(--responsive-switch-height);
|
.switch-thumb { position: absolute; top: 50%; left: 3px; width: calc(var(--responsive-switch-height) - 6px); height: calc(var(--responsive-switch-height) - 6px); background: var(--theme-switch-thumb); border-radius: 50%; box-shadow: 0 2px 6px rgba(0,0,0,.15); transform: translateY(-50%);transition: left var(--times-transition-transform) cubic-bezier(.2,.9,.2,1), background var(--times-transition-colors) ease; }
|
||||||
|
.cb-switch input:checked + .switch-track { background: var(--theme-switch-active); }
|
||||||
display: inline-flex;
|
.cb-switch input:checked + .switch-track .switch-thumb { left: calc(var(--responsive-switch-width) - var(--responsive-switch-height) + 3px); }
|
||||||
align-items: center;
|
.cb-switch input:not(:disabled):hover + .switch-track { border-color: var(--theme-switch-hover); }
|
||||||
flex-shrink: 0;
|
.cb-switch input:focus-visible + .switch-track { box-shadow: 0 0 0 6px rgba(6, 193, 103, 0.12); }
|
||||||
gap: 8px;
|
.cb-switch input:disabled + .switch-track { background-color: dimgray; opacity: 0.6; }
|
||||||
}
|
|
||||||
|
|
||||||
/* Input verstecken */
|
|
||||||
.cb-switch input {
|
|
||||||
position: absolute;
|
|
||||||
opacity: 0;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TRACK */
|
|
||||||
.switch-track {
|
|
||||||
width: var(--w);
|
|
||||||
height: var(--h);
|
|
||||||
|
|
||||||
border-radius: 999px;
|
|
||||||
padding: 3px;
|
|
||||||
border: 1px solid;
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
transition: background var(--times-transition-colors) ease, border-color var(--times-transition-colors) ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* THUMB */
|
|
||||||
.switch-thumb {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 3px;
|
|
||||||
|
|
||||||
width: calc(var(--h) - 6px);
|
|
||||||
height: calc(var(--h) - 6px);
|
|
||||||
|
|
||||||
background: var(--theme-switch-thumb);
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: 0 2px 6px rgba(0,0,0,.15);
|
|
||||||
|
|
||||||
transform: translateY(-50%);
|
|
||||||
|
|
||||||
transition: left var(--times-transition-transform) cubic-bezier(.2,.9,.2,1), background var(--times-transition-colors) ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CHECKED STATE */
|
|
||||||
.cb-switch input:checked + .switch-track {
|
|
||||||
background: var(--theme-switch-active);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cb-switch input:checked + .switch-track .switch-thumb {
|
|
||||||
left: calc(var(--w) - var(--h) + 3px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HOVER */
|
|
||||||
.cb-switch input:not(:disabled):hover + .switch-track {
|
|
||||||
border-color: var(--theme-switch-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FOCUS */
|
|
||||||
.cb-switch input:focus-visible + .switch-track {
|
|
||||||
box-shadow: 0 0 0 6px rgba(6, 193, 103, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* DISABLED */
|
|
||||||
.cb-switch input:disabled + .switch-track {
|
|
||||||
background-color: dimgray;
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cb-switch input:disabled + .switch-track .switch-thumb {
|
|
||||||
background-color: rgb(54, 50, 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* optional label spacing */
|
|
||||||
.cb-switch label {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
/* .cb-switch { --w:45px; --h:27px; display:inline-flex; align-items:center; }
|
|
||||||
.cb-switch input { position:absolute; opacity:0; width:0; height:0; pointer-events:none; }
|
|
||||||
.switch-track { width:var(--w); height:var(--h); border-radius:999px; padding:3px; border-width:1px; border-style:solid; box-sizing:border-box; display:inline-flex; align-items:center; transition:background .18s ease, transform .12s ease, border-color .25s ease; }
|
|
||||||
.switch-thumb { min-width:calc(var(--h) - 2 * 3px); width:calc(var(--h) - 2 * 3px); height:calc(var(--h) - 2 * 3px); background:var(--theme-switch-thumb); border-radius:50%; box-shadow:0 2px 6px rgba(0,0,0,.15); transform:translateX(-1px); transition:transform .25s cubic-bezier(.2,.9,.2,1), background .18s ease }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.cb-switch input:disabled + .switch-track { background-color: dimgray;}
|
|
||||||
.cb-switch input:disabled + .switch-track .switch-thumb { background-color: rgb(54, 50, 50); }
|
.cb-switch input:disabled + .switch-track .switch-thumb { background-color: rgb(54, 50, 50); }
|
||||||
|
.cb-switch label { cursor: pointer; }
|
||||||
.cb-switch input:not(:disabled):checked + .switch-track { background:var(--theme-switch-active); }
|
|
||||||
|
|
||||||
.cb-switch input:not(:disabled):hover + .switch-track { border-color:var(--theme-switch-hover); }
|
|
||||||
|
|
||||||
.cb-switch input:focus-visible + .switch-track { box-shadow:0 0 0 6px rgba(6,193,103,0.12); }
|
|
||||||
.cb-switch input:checked + .switch-track .switch-thumb { transform:translateX(calc(var(--w) - var(--h))); }
|
|
||||||
.cb-switch label { width: calc(100% - var(--w)); } */
|
|
||||||
/* #endregion */
|
|
||||||
|
|
||||||
|
|
||||||
/* #region CheckBox */
|
/* #region CheckBox */
|
||||||
/*
|
/*
|
||||||
@@ -209,13 +114,12 @@ button:not(:disabled).yellowbutton:hover { background:var(--theme-button-yellow-
|
|||||||
<span class="cb-box" aria-hidden="true"></span>
|
<span class="cb-box" aria-hidden="true"></span>
|
||||||
</label>
|
</label>
|
||||||
*/
|
*/
|
||||||
.cb { display:inline-flex; align-items:center; gap:10px; user-select:none; transform:translateY(2px); }
|
.cb { display:inline-grid;grid-auto-flow:column;align-items:center;gap:10px;user-select:none;vertical-align:middle; }
|
||||||
.cb input { position:absolute; opacity:0; width:0; height:0; pointer-events:none; }
|
.cb input { position:absolute;opacity:0;width:0;height:0;pointer-events:none; }
|
||||||
.cb-box { width:20px; height:20px; border-radius:6px; background:var(--theme-checkbox-backcolor); border-width:2px; border-style:solid; display:inline-grid; place-items:center; transition:transform var(--times-transition-transform) ease, border-color var(--times-transition-colors) ease, background var(--times-transition-colors) ease; }
|
.cb-box { width:20px;height:20px;border-radius:6px;background:var(--theme-checkbox-backcolor);border:2px solid;display:grid;place-items:center;transition:transform var(--times-transition-transform) ease,border-color var(--times-transition-colors) ease,background var(--times-transition-colors) ease;flex-shrink:0; }
|
||||||
.cb-box::after { content:""; width:12px; height:8px; transform:scale(0) translateY(-2px); background-repeat:no-repeat; background-position:center; background-size:contain; background-image:url("data:image/svg+xml,%3Csvg width='12' height='8' viewBox='0 0 12 8' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 4L4 7l7-7' stroke='%23fff' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); transition:transform var(--times-transition-transform) cubic-bezier(.2,.9,.2,1); }
|
.cb-box::after { content:"";width:10px;height:6px;border-left:2px solid #fff;border-bottom:2px solid #fff;transform:rotate(-45deg)scale(0);transform-origin:center;transition:transform var(--times-transition-transform)cubic-bezier(.2,.9,.2,1); }
|
||||||
|
.cb input:checked + .cb-box::after { transform: rotate(-45deg) scale(1) translate(1px, -1px); }
|
||||||
.cb input:checked + .cb-box { transform:translateY(-1px); }
|
.cb input:checked + .cb-box { transform:translateY(-1px); }
|
||||||
.cb input:checked + .cb-box::after { transform:scale(1) translate(-1px, 1px); }
|
|
||||||
table .cb input:checked + .cb-box::after { transform:scale(1) translate(1px, 1px); }
|
|
||||||
.cb input:focus-visible + .cb-box { outline:none; }
|
.cb input:focus-visible + .cb-box { outline:none; }
|
||||||
/* #endregion */
|
/* #endregion */
|
||||||
|
|
||||||
@@ -226,8 +130,8 @@ table .cb input:checked + .cb-box::after { transform:scale(1) translate(1px, 1px
|
|||||||
|
|
||||||
|
|
||||||
/* #region Tabs */
|
/* #region Tabs */
|
||||||
.tabs { display: flex; margin-bottom: 10px; border-bottom-width: 2px; border-bottom-style: solid; }
|
.tabs { display: flex; flex:1 0 auto; margin-bottom: 5px; border-bottom-width: 2px; border-bottom-style: solid; overflow-x: auto; scrollbar-width: thin;}
|
||||||
.tab { padding: 10px 20px; border: 1px solid transparent; border-top-left-radius: 5px; border-top-right-radius: 5px; margin-right: 5px; transition: background .25s, color .25s, border-color .25s; }
|
.tab { padding: 5px 10px; text-wrap: nowrap; border: 1px solid transparent; border-top-left-radius: 5px; border-top-right-radius: 5px; margin-right: 5px; transition: background .25s, color .25s, border-color .25s; }
|
||||||
.tab-content { border-width: 1px; border-style: solid; border-radius: 5px; padding: 15px; }
|
.tab-content { border-width: 1px; border-style: solid; border-radius: 5px; padding: 15px; }
|
||||||
.item { margin-bottom: 5px; }
|
.item { margin-bottom: 5px; }
|
||||||
/* #endregion */
|
/* #endregion */
|
||||||
@@ -235,7 +139,7 @@ table .cb input:checked + .cb-box::after { transform:scale(1) translate(1px, 1px
|
|||||||
|
|
||||||
/* #region Feebox */
|
/* #region Feebox */
|
||||||
.feedbox-overlay { position:fixed; top:0; left:0; width:100vw; height:100vh; display:flex; align-items:center; justify-content:center; z-index:999; }
|
.feedbox-overlay { position:fixed; top:0; left:0; width:100vw; height:100vh; display:flex; align-items:center; justify-content:center; z-index:999; }
|
||||||
.feedbox { border-radius:8px; max-width:50vw; width:100%; padding:20px; animation:feedboxFadeIn 0.2s ease-out; max-height:80vh; display:flex; flex-direction:column; overflow:hidden; }
|
.feedbox { border-radius:8px; max-width:50vw; max-width:90vw; padding:20px; animation:feedboxFadeIn 0.2s ease-out; max-height:80vh; display:flex; flex-direction:column; overflow:hidden; }
|
||||||
.feedbox h3 { margin:0 0 10px 0; }
|
.feedbox h3 { margin:0 0 10px 0; }
|
||||||
.feedbox-message { margin-bottom:20px; line-height:1.4; flex:1; overflow-y: auto; /* font-size:1rem; */ }
|
.feedbox-message { margin-bottom:20px; line-height:1.4; flex:1; overflow-y: auto; /* font-size:1rem; */ }
|
||||||
.feedbox-actions { display:flex; justify-content:flex-end; gap:10px; flex-wrap:wrap; flex-shrink:0; }
|
.feedbox-actions { display:flex; justify-content:flex-end; gap:10px; flex-wrap:wrap; flex-shrink:0; }
|
||||||
@@ -406,14 +310,23 @@ label { color:var(--muted); display:block; margin-bottom:6px; }
|
|||||||
border-bottom:1px solid #eee;
|
border-bottom:1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.removeButton {
|
.removeButton {
|
||||||
border:none;
|
border:none;
|
||||||
background:none;
|
background:#d11a2a;
|
||||||
color:#d11a2a;
|
color:white;
|
||||||
cursor:var(--theme-cursor-pointer) -16 16, pointer;
|
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"] {
|
input[type="file"] {
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ body, html { margin:0; padding:0; height:100%; overflow: hidden; font-family: va
|
|||||||
.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:auto; 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; }
|
||||||
.taskbar-item.focus::before { transform: translateX(-50%) scaleX(1); }
|
.taskbar-item.minimized::before, .taskbar-item.focus::before { transform: translateX(-50%) scaleX(1); }
|
||||||
|
|
||||||
.notify-button { margin-left:auto; flex: 0 0 auto; display: flex; align-items: center; justify-content: center; }
|
.notify-button { margin-left:auto; flex: 0 0 auto; display: flex; align-items: center; justify-content: center; }
|
||||||
.notify-button.resume, .notify-button.pulse { animation: pulse 1.5s infinite; animation-play-state: running; }
|
.notify-button.resume, .notify-button.pulse { animation: pulse 1.5s infinite; animation-play-state: running; }
|
||||||
@@ -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; }
|
||||||
@@ -70,7 +70,7 @@ body, html { margin:0; padding:0; height:100%; overflow: hidden; font-family: va
|
|||||||
.start-item.has-submenu { position: relative; display: flex; flex-direction: column; padding-bottom: 8px; }
|
.start-item.has-submenu { position: relative; display: flex; flex-direction: column; padding-bottom: 8px; }
|
||||||
.start-item.has-submenu > .submenu { width: 100%; list-style: none; padding-left: 2px; max-height: 0; overflow: hidden; transition: max-height var(--times-transition-transform) ease; margin: 2px 0 0 8px; }
|
.start-item.has-submenu > .submenu { width: 100%; list-style: none; padding-left: 2px; max-height: 0; overflow: hidden; transition: max-height var(--times-transition-transform) ease; margin: 2px 0 0 8px; }
|
||||||
|
|
||||||
.start-item-sys-container { position: relative; left:0; bottom: -8px; padding: 5px 0px; width:100%; display:flex; height:30px; flex-direction:row; justify-content:flex-end; }
|
.start-item-sys-container { position: relative; left:0; bottom: -10px; padding: 5px 0px; width:100%; display:flex; height:30px; flex-direction:row; justify-content:flex-end; }
|
||||||
.start-sys-item { margin: 0 10px 0 8px !important; }
|
.start-sys-item { margin: 0 10px 0 8px !important; }
|
||||||
|
|
||||||
.start-submenu-head { position: relative; margin-left: 16px; height:32px; display:flex; flex-direction: row; align-items: center; gap:8px; }
|
.start-submenu-head { position: relative; margin-left: 16px; height:32px; display:flex; flex-direction: row; align-items: center; gap:8px; }
|
||||||
@@ -80,7 +80,6 @@ body, html { margin:0; padding:0; height:100%; overflow: hidden; font-family: va
|
|||||||
.start-item.has-submenu > .submenu li { opacity: 0; transform: translateY(-5px); transition: opacity var(--times-transition-opacity), transform 0.2s; }
|
.start-item.has-submenu > .submenu li { opacity: 0; transform: translateY(-5px); transition: opacity var(--times-transition-opacity), transform 0.2s; }
|
||||||
.start-item.has-submenu.open > .submenu li { opacity: 1; transform: translateY(0); }
|
.start-item.has-submenu.open > .submenu li { opacity: 1; transform: translateY(0); }
|
||||||
|
|
||||||
|
|
||||||
img.icon { width: auto; height:20px; object-fit: contain; filter: var(--theme-notifybubble-filter); transform: translate(2px, 2px) }
|
img.icon { width: auto; height:20px; object-fit: contain; filter: var(--theme-notifybubble-filter); transform: translate(2px, 2px) }
|
||||||
|
|
||||||
|
|
||||||
@@ -123,6 +122,7 @@ img.icon { width: auto; height:20px; object-fit: contain; filter: var(--theme-no
|
|||||||
#taskbar {
|
#taskbar {
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
#start-btn {
|
#start-btn {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ table thead { position:sticky; top:0; z-index:20; }
|
|||||||
|
|
||||||
/* #endregion */
|
/* #endregion */
|
||||||
/* echte Tabelle */
|
/* 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 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);}
|
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 thead th { padding:5px; }
|
||||||
|
/* table tbody td { padding:0 5px; } */
|
||||||
/* table tbody td { padding:5px 0px 5px 20px; } */
|
/* 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 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; }
|
table tbody tr.grouprow { font-weight:700; }
|
||||||
@@ -46,23 +47,25 @@ table .text-align\:left { text-align:left; }
|
|||||||
td { overflow:hidden; text-overflow:ellipsis; /* verhindert, dass Inhalt die Zelle sprengt */ }
|
td { overflow:hidden; text-overflow:ellipsis; /* verhindert, dass Inhalt die Zelle sprengt */ }
|
||||||
|
|
||||||
.table-filter-container {
|
.table-filter-container {
|
||||||
border-bottom-width:8px;
|
border-bottom-width:1px;
|
||||||
border-bottom-style:solid;
|
border-bottom-style:solid;
|
||||||
display:flex;
|
display:flex;
|
||||||
justify-content:flex-start;
|
|
||||||
flex-direction:column;
|
flex-direction:column;
|
||||||
flex-wrap:wrap;
|
gap:0;
|
||||||
gap:10px;
|
|
||||||
|
|
||||||
position:sticky;
|
position:sticky;
|
||||||
left:0px;
|
left:0px;
|
||||||
top:0px;
|
top:0px;
|
||||||
|
width: 100% !important;
|
||||||
/* z-index:20; */
|
/* z-index:20; */
|
||||||
padding:5px 10px;
|
padding:0px;
|
||||||
border-radius:var(--border-raduis) var(--border-raduis) 0 0;
|
border-radius:var(--border-raduis) var(--border-raduis) 0 0;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex: 1;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
overflow-x:auto;
|
||||||
}
|
}
|
||||||
.table-filter-container .live-counter { position:absolute; right:18px; margin-left:auto; font-weight:bold; }
|
.table-filter-container .live-counter { position:static; text-align: left ; padding-left:10px; font-style: italic; }
|
||||||
.table-filter-container input, .table-filter-container select { padding:5px !important; }
|
.table-filter-container input { padding:5px !important; ; margin:5px 0;}
|
||||||
|
|
||||||
|
|
||||||
th.sort-asc::after {
|
th.sort-asc::after {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
max-height: 50vh;
|
max-height: 50vh;
|
||||||
width: max-content; /* 🔑 wächst mit Inhalt */
|
width: max-content; /* 🔑 wächst mit Inhalt */
|
||||||
max-width: 600px; /* aber capped */
|
max-width: 500px; /* aber capped */
|
||||||
background: var(--theme-taskbar-tray-backcolor);
|
background: var(--theme-taskbar-tray-backcolor);
|
||||||
color: var(--theme-taskbar-tray-color);
|
color: var(--theme-taskbar-tray-color);
|
||||||
border-color: var(--theme-taskbar-tray-border-color);
|
border-color: var(--theme-taskbar-tray-border-color);
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
overflow-y: auto; /* vertikal scrollen */
|
overflow-y: auto; /* vertikal scrollen */
|
||||||
overflow-x: hidden; /* horizontal verhindern */
|
overflow-x: hidden; /* horizontal verhindern */
|
||||||
box-shadow: 0 8px 20px rgba(0,0,0,0.4);
|
box-shadow: 0 8px 20px rgba(0,0,0,0.4);
|
||||||
padding: 10px 10px 6px 10px;
|
padding: 6px 6px 0px 6px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transform: translateY(10px);
|
transform: translateY(10px);
|
||||||
@@ -28,8 +28,8 @@
|
|||||||
.bubble-item {
|
.bubble-item {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, auto) 250px;
|
grid-template-columns: minmax(0, auto) 250px;
|
||||||
gap: 10px;
|
gap: 5px;
|
||||||
padding: 6px 8px;
|
padding: 3px 6px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
/* cursor: pointer; */
|
/* cursor: pointer; */
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -55,7 +55,9 @@
|
|||||||
|
|
||||||
/* hover Effekt */
|
/* hover Effekt */
|
||||||
.bubble-item:hover {
|
.bubble-item:hover {
|
||||||
background: rgba(64,64,64,0.4);
|
background: var(--theme-accent-hover-backcolor);
|
||||||
|
color: var(--theme-accent-hover-color);
|
||||||
|
border-color: var(--theme-accent-hover-border-color);
|
||||||
transform: scale(1.01);
|
transform: scale(1.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
{{#each items}}
|
{{#each items}}
|
||||||
{{#ifSingle this.menu.items}}
|
{{#ifSingle this.menu.items}}
|
||||||
{{#if this.authorized}}
|
{{#if this.authorized}}
|
||||||
<li class="start-item {{#unless ../this.active}}unload{{/unless}}" data-active="{{#equaler ../this.active "&&" this.authorized}}true{{else}}false{{/equaler}}" data-appname="{{../this.name}}" data-appview="{{this.view}}" data-viewlabel="{{this.label}}">
|
<li class="start-item {{#unless ../this.active}}unload{{/unless}}" {{#if ../this.description}}data-tooltip="{{../this.description}}"{{/if}} data-active="{{#equaler ../this.active "&&" this.authorized}}true{{else}}false{{/equaler}}" data-appname="{{../this.name}}" data-appview="{{this.view}}" data-viewlabel="{{this.label}}">
|
||||||
{{#if this.icon}}
|
{{#if this.icon}}
|
||||||
<img src="{{#if ../this.pluginPath}}/{{../this.name}}{{/if}}/images/{{this.icon}}" class="start-icon" />
|
<img src="{{#if ../this.pluginPath}}/{{../this.name}}{{/if}}/images/{{this.icon}}" class="start-icon" />
|
||||||
{{else}}
|
{{else}}
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
<li class="start-item has-submenu">
|
<li class="start-item has-submenu">
|
||||||
<img src="{{#if ../this.pluginPath}}/{{../this.name}}{{/if}}/images/folder.png" class="start-icon" style="position:absolute;left:12px;"/>
|
<img src="{{#if ../this.pluginPath}}/{{../this.name}}{{/if}}/images/folder.png" class="start-icon" style="position:absolute;left:12px;"/>
|
||||||
<span class="menu-label">{{this.menu.label}}</span>
|
<span {{#if this.description}}data-tooltip="{{this.description}}"{{/if}} class="menu-label">{{this.menu.label}}</span>
|
||||||
{{!-- {{#if this.version}}<small>v{{this.version}}</small>{{/if}} --}}
|
{{!-- {{#if this.version}}<small>v{{this.version}}</small>{{/if}} --}}
|
||||||
|
|
||||||
<ul class="submenu">
|
<ul class="submenu">
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
|
|
||||||
<!-- Taskbar -->
|
<!-- Taskbar -->
|
||||||
<div id="taskbar">
|
<div id="taskbar">
|
||||||
<button id="start-btn">☰</button>
|
<button class="" id="start-btn">☰</button>
|
||||||
<div id="taskbar-windows"></div>
|
<div id="taskbar-windows"></div>
|
||||||
<button style="margin-right:0;" class="monolyth notify-button pulse">
|
<button style="margin-right:0;" class="monolyth notify-button pulse">
|
||||||
<img class="icon" src="/images/notifybubble.png">
|
<img class="icon" src="/images/notifybubble.png">
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
<script src="javascript/contextMenu.js"></script>
|
<script src="javascript/contextMenu.js"></script>
|
||||||
<script src="javascript/tableFilter.js"></script>
|
<script src="javascript/tableFilter.js"></script>
|
||||||
<script src="javascript/requiredFields.js"></script>
|
<script src="javascript/requiredFields.js"></script>
|
||||||
<script src="javascript/loadOnce.js"></script>
|
<script src="javascript/uiEvents.js"></script>
|
||||||
<script src="javascript/JSON.js"></script>
|
<script src="javascript/JSON.js"></script>
|
||||||
<script src="javascript/os.js"></script>
|
<script src="javascript/os.js"></script>
|
||||||
<script>
|
<script>
|
||||||
@@ -116,7 +116,10 @@
|
|||||||
const trayNotifyButton = document.querySelector('#taskbar > .notify-button')
|
const trayNotifyButton = document.querySelector('#taskbar > .notify-button')
|
||||||
const notify = new NotifyBubble(trayNotifyButton, "#notify-bubble");
|
const notify = new NotifyBubble(trayNotifyButton, "#notify-bubble");
|
||||||
|
|
||||||
document.addEventListener("contextmenu", evt => evt.preventDefault());
|
document.addEventListener("contextmenu", evt => {
|
||||||
|
evt.preventDefault()
|
||||||
|
evt.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
document.querySelectorAll('#start-menu .start-item.has-submenu').forEach(item => {
|
document.querySelectorAll('#start-menu .start-item.has-submenu').forEach(item => {
|
||||||
item.addEventListener('click', evt => {
|
item.addEventListener('click', evt => {
|
||||||
@@ -149,4 +152,24 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/* Checks tab visibility
|
||||||
|
document.addEventListener(
|
||||||
|
'visibilitychange',
|
||||||
|
() => {
|
||||||
|
|
||||||
|
if (document.hidden) {
|
||||||
|
|
||||||
|
alert('AWAY AT: ' + new Date().toISOString());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
updateActivity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
fetch('/api/getConfig', { method: 'POST' })
|
fetch('/api/config/get', { method: 'POST' })
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(json => {
|
.then(json => {
|
||||||
const tree = createJsonTree({
|
const tree = createJsonTree({
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
expandInitially: true,
|
expandInitially: true,
|
||||||
onSave: json => {
|
onSave: json => {
|
||||||
console.log(JSON.stringify(tree.getChanges()));
|
console.log(JSON.stringify(tree.getChanges()));
|
||||||
fetch('/config', {
|
fetch('/api/config/save', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(tree.getChanges(), null, 2)
|
body: JSON.stringify(tree.getChanges(), null, 2)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
fetch('/api/getServerInfo', { method: 'POST' })
|
fetch('/api/serverInfo/get', { method: 'POST' })
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(json => {
|
.then(json => {
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
fetch('/api/getStyles', { method: 'POST' })
|
fetch('/api/styles/get', { method: 'POST' })
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(json => {
|
.then(json => {
|
||||||
const tree = createJsonTree({
|
const tree = createJsonTree({
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
data: json,
|
data: json,
|
||||||
expandInitially: true,
|
expandInitially: true,
|
||||||
onSave: json => {
|
onSave: json => {
|
||||||
fetch('/style', {
|
fetch('/api/styles/save', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(tree.getChanges(), null, 2)
|
body: JSON.stringify(tree.getChanges(), null, 2)
|
||||||
|
|||||||
@@ -1,14 +1,185 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Role Based Access Control</title>
|
<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: 200px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 0 2px 2px 0;
|
||||||
|
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);
|
||||||
|
border-color:var(--theme-accent-hover-boder-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
section:active {
|
||||||
|
background: var(--theme-accent-active-backcolor);
|
||||||
|
color: var(--theme-accent-active-color);
|
||||||
|
border-color:var(--theme-accent-active-boder-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
section span {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.tile {
|
||||||
|
display:inline-flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: stretch;
|
||||||
|
height:60px;
|
||||||
|
padding: 5px;
|
||||||
|
width: 200px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 0 2px 2px 0;
|
||||||
|
transition: background var(--times-transition-colors) ease, border-color var(--times-transition-colors) ease, color var(--times-transition-colors) ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container static" style="height: 100vh;">
|
<div class="container static" style="height:100vh;">
|
||||||
<div class="card static" style="overflow-y:auto;flex: 1 1 auto;" >
|
<div class="card static">
|
||||||
|
<div class="tabs" id="rbacTabs" style="flex: 0 0 auto;">
|
||||||
|
<div class="tab">Verwaltung</div>
|
||||||
|
<div class="tab">Benutzer & Gruppen</div>
|
||||||
|
<div class="tab">Rollen & Berechtigungen</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="rbacTabContents" class="tab-contents" style="flex: 1 1 100%;overflow-y:auto;padding:0;">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{{!-- <div class="container static" style="max-width:100vw;flex-direction:row;flex-wrap:wrap;flex: 1 1 100vh;">
|
||||||
|
<!-- USERS -->
|
||||||
|
<div class="card static row" style="align-items:center;height:fit-content;width:100%">
|
||||||
|
AD Synchronisation:
|
||||||
|
<button class="yellowbutton" data-tooltip="Synchronisiert die Benutzer aus dem AD" onclick="syncUsersFromAD()">Users</button>
|
||||||
|
<button class="yellowbutton" data-tooltip="Synchronisiert die Gruppen aus dem AD" onclick="syncGroupsFromAD()">Groups</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="card" style="aflex-wrap:wrap;flex: 1 1 100%;justify-content: flex-start;">
|
||||||
|
Users <input id="newUserName" placeholder="sAMAccountName" />
|
||||||
|
<button class="bluebutton" onclick="createUser()">Create User</button>
|
||||||
|
<div class="table-wrapper " style="max-height:300px;overflow:auto">
|
||||||
|
<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:center">Rollen</th>
|
||||||
|
<th class="text-align:center">Gruppen</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>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td colspan="100%">BENUTZER WERDEN GELADEN . . .</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- GROUPS -->
|
||||||
|
<div class="card static" style="min-width:300px;flex:1 1 calc(300px); max-height: 400px;overflow:auto">
|
||||||
|
<input id="newGroupName" placeholder="Gruppenname" /> <button class="bluebutton" onclick="createGroup()">Create Group</button>
|
||||||
|
|
||||||
|
<div id="rbacGroupContainer">
|
||||||
|
<span>GRUPPEN WERDEN GELADEN . . .</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ROLES -->
|
||||||
|
<div class="card" style="min-width:300px;flex:1 1 calc(300px)">
|
||||||
|
<input id="newRoleName" placeholder="Rollenname" />
|
||||||
|
<button class="bluebutton" onclick="createRole()">Create Role</button>
|
||||||
|
|
||||||
|
<div id="rbacRoleContainer">
|
||||||
|
<span>ROLLEN WERDEN GELADEN . . .</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- PERMISSIONS -->
|
||||||
|
<div class="card" style="min-width:300px;flex:1 1 auto;align-items:center;">
|
||||||
|
<input id="permScope" placeholder="Scope" />.<input id="permResource" placeholder="Resource" />.<input id="permAction" placeholder="Action" />
|
||||||
|
|
||||||
|
<button class="bluebutton" onclick="createPermission()">Create Permission</button>
|
||||||
|
|
||||||
|
<div class="table-wrapper fit-table">
|
||||||
|
<table id="rbacPermissionsTable" style="">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-align:left"></th>
|
||||||
|
<th class="text-align:left">ID</th>
|
||||||
|
<th class="text-align:center">Gruppen</th>
|
||||||
|
<th class="text-align:center">Benutzer</th>
|
||||||
|
<th class="text-align:center">Rollen</th>
|
||||||
|
<th class="text-align:right">Scope</th>
|
||||||
|
<th class="text-align:center">Resource</th>
|
||||||
|
<th class="text-align:left">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td colspan="100%">BERECHTIGUNGEN WERDEN GELADEN . . .</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div> --}}
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
reloadPluginScript('/javascript/rbacAPI.js');
|
||||||
|
|
||||||
|
</script>
|
||||||
</html>
|
</html>
|
||||||
25
server.js
25
server.js
@@ -57,7 +57,8 @@ const server = https.createServer(httpsOptions, app);
|
|||||||
|
|
||||||
|
|
||||||
const io = new Server(server, {
|
const io = new Server(server, {
|
||||||
pingTimeout: 60000,
|
// pingInterval: 1000, // alle 25s Ping senden
|
||||||
|
// pingTimeout: 200, // 20s ohne Pong => disconnect
|
||||||
maxHttpBufferSize: 1e8, // 100 MB
|
maxHttpBufferSize: 1e8, // 100 MB
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -107,17 +108,24 @@ const server = https.createServer(httpsOptions, app);
|
|||||||
databaseModel.set('authenticationGroupsModel', require(`@models/authenticationGroupsModel`)(service.get('sqlManager').getInstance('main')));
|
databaseModel.set('authenticationGroupsModel', require(`@models/authenticationGroupsModel`)(service.get('sqlManager').getInstance('main')));
|
||||||
databaseModel.set('authenticationRolesModel', require(`@models/authenticationRolesModel`)(service.get('sqlManager').getInstance('main')));
|
databaseModel.set('authenticationRolesModel', require(`@models/authenticationRolesModel`)(service.get('sqlManager').getInstance('main')));
|
||||||
databaseModel.set('groupClosureModel', require(`@models/groupClosureModel`)(service.get('sqlManager').getInstance('main')));
|
databaseModel.set('groupClosureModel', require(`@models/groupClosureModel`)(service.get('sqlManager').getInstance('main')));
|
||||||
databaseModel.set('groupModel', require(`@models/groupModel`)(service.get('sqlManager').getInstance('main')));
|
databaseModel.set('group', require(`@models/groupModel`)(service.get('sqlManager').getInstance('main')));
|
||||||
databaseModel.set('groupRolesModel', require(`@models/groupRolesModel`)(service.get('sqlManager').getInstance('main')));
|
databaseModel.set('groupRolesModel', require(`@models/groupRolesModel`)(service.get('sqlManager').getInstance('main')));
|
||||||
databaseModel.set('objectSourceModel', require(`@models/objectSourceModel`)(service.get('sqlManager').getInstance('main')));
|
databaseModel.set('objectSourceModel', require(`@models/objectSourceModel`)(service.get('sqlManager').getInstance('main')));
|
||||||
databaseModel.set('permissionModel', require(`@models/permissionModel`)(service.get('sqlManager').getInstance('main')));
|
databaseModel.set('permissionModel', require(`@models/permissionModel`)(service.get('sqlManager').getInstance('main')));
|
||||||
databaseModel.set('roleModel', require(`@models/roleModel`)(service.get('sqlManager').getInstance('main')));
|
databaseModel.set('roleModel', require(`@models/roleModel`)(service.get('sqlManager').getInstance('main')));
|
||||||
databaseModel.set('rolePermissionsModel', require(`@models/rolePermissionsModel`)(service.get('sqlManager').getInstance('main')));
|
databaseModel.set('rolePermissionsModel', require(`@models/rolePermissionsModel`)(service.get('sqlManager').getInstance('main')));
|
||||||
|
|
||||||
service.set('rbacManager', new RBACManager(databaseModel));
|
databaseModel.set('authenticationOverviewView', require(`@models/authenticationOverviewView`)(service.get('sqlManager').getInstance('main')));
|
||||||
service.set('authenticationManager', new AuthenticationManager(databaseModel.get('authentication'), runtimeFile.configuration.live.integration.token.secret));
|
databaseModel.set('groupOverviewView', require(`@models/groupOverviewView`)(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')));
|
||||||
|
databaseModel.set('permissionOverviewView', require(`@models/permissionOverviewView`)(service.get('sqlManager').getInstance('main')));
|
||||||
|
|
||||||
|
|
||||||
service.set('activeDirectoryManager', new ActiveDirectory(runtimeFile.configuration.live.integration.activedirectory))
|
service.set('activeDirectoryManager', new ActiveDirectory(runtimeFile.configuration.live.integration.activedirectory))
|
||||||
|
service.set('rbacManager', new RBACManager(databaseModel, runtimeFile.configuration.live.integration.token.secret, service));
|
||||||
|
service.set('authenticationManager', new AuthenticationManager(databaseModel.get('authentication'), runtimeFile.configuration.live.integration.token.secret));
|
||||||
|
|
||||||
|
|
||||||
// everytime last created service!
|
// everytime last created service!
|
||||||
service.set('pluginManager', new PluginManager(app, databaseModel.get('plugin'), localPath.plugins, runtimeFile.configuration.live.plugin.chown, service));
|
service.set('pluginManager', new PluginManager(app, databaseModel.get('plugin'), localPath.plugins, runtimeFile.configuration.live.plugin.chown, service));
|
||||||
@@ -128,13 +136,11 @@ const server = https.createServer(httpsOptions, app);
|
|||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
|
||||||
//#region Service-Registration/Middleware/Utils/Helpers
|
|
||||||
require(`${localPath.root}/utils.js`);
|
require(`${localPath.root}/utils.js`);
|
||||||
let helpers = service.get('fileSystemManager').loadAllFiles(`${localPath.public}/helpers`, '.js');
|
let helpers = service.get('fileSystemManager').loadAllFiles(`${localPath.public}/helpers`, '.js');
|
||||||
exports.helpers = helpers;
|
exports.helpers = helpers;
|
||||||
|
|
||||||
// app.use(service.get('vaultifyManager').createMiddleware());
|
// app.use(service.get('vaultifyManager').createMiddleware());
|
||||||
app.use(service.get('rbacManager').requirePermissionMiddleware());
|
|
||||||
|
|
||||||
app.use(express.urlencoded({ extended: true }));
|
app.use(express.urlencoded({ extended: true }));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
@@ -145,7 +151,9 @@ const server = https.createServer(httpsOptions, app);
|
|||||||
app.use(express.static(localPath.public));
|
app.use(express.static(localPath.public));
|
||||||
app.use(express.static(localPath.source));
|
app.use(express.static(localPath.source));
|
||||||
|
|
||||||
|
//#region Service-Registration/Middleware/Utils/Helpers
|
||||||
|
app.use(service.get('rbacManager').authenticate());
|
||||||
|
app.use(service.get('rbacManager').requirePermissionMiddleware());
|
||||||
|
|
||||||
app.use(function(request, response, next) {
|
app.use(function(request, response, next) {
|
||||||
if (!request.secure) {
|
if (!request.secure) {
|
||||||
@@ -200,14 +208,13 @@ const server = https.createServer(httpsOptions, app);
|
|||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
|
||||||
//#region Implement routes
|
//#region Implement routes
|
||||||
require(`${localPath.source}/routes/loginRoutes.js`).route(app, service); // #1 - no token security! important: first!!!
|
require(`${localPath.source}/routes/loginRoutes.js`).route(app, service); // #1 - no token security! important: first!!!
|
||||||
require(`${localPath.source}/routes/indexRoutes.js`).route(app, service); // #2 - token security enabled at this point
|
require(`${localPath.source}/routes/indexRoutes.js`).route(app, service); // #2 - token security enabled at this point
|
||||||
require(`${localPath.source}/routes/adminRoutes.js`).route(app, service); // #3 - token security always enabled
|
require(`${localPath.source}/routes/adminRoutes.js`).route(app, service); // #3 - token security always enabled
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
app.use(service.get('rbacManager').authenticate());
|
|
||||||
|
|
||||||
//#region Implements sockets
|
//#region Implements sockets
|
||||||
require(`${localPath.source}/sockets/mainSocket.js`)(
|
require(`${localPath.source}/sockets/mainSocket.js`)(
|
||||||
|
|||||||
@@ -12,6 +12,15 @@ module.exports = (sequelize) => {
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
tableName: 'AuthenticationGroups',
|
tableName: 'AuthenticationGroups',
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
unique: true,
|
||||||
|
fields: [
|
||||||
|
'Authentication_ObjectGUID',
|
||||||
|
'Group_ObjectGUID'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
timestamps: false
|
timestamps: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ module.exports = (sequelize) => {
|
|||||||
const Authentication = sequelize.define('Authentication', {
|
const Authentication = sequelize.define('Authentication', {
|
||||||
ObjectGUID: {
|
ObjectGUID: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUID,
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
},
|
},
|
||||||
@@ -41,7 +42,7 @@ module.exports = (sequelize) => {
|
|||||||
allowNull: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
userAccountControl_ID: {
|
userAccountControl_ID: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.INTEGER,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
telephoneNumber: {
|
telephoneNumber: {
|
||||||
@@ -72,6 +73,10 @@ module.exports = (sequelize) => {
|
|||||||
type: DataTypes.BOOLEAN,
|
type: DataTypes.BOOLEAN,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
|
ObjectSource_ID: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
tableName: 'Authentication', // Tabellenname in der Datenbank
|
tableName: 'Authentication', // Tabellenname in der Datenbank
|
||||||
timestamps: false, // Falls du keine createdAt/updatedAt Spalten hast
|
timestamps: false, // Falls du keine createdAt/updatedAt Spalten hast
|
||||||
|
|||||||
46
src/models/authenticationOverviewView.js
Normal file
46
src/models/authenticationOverviewView.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
const { DataTypes } = require('sequelize');
|
||||||
|
|
||||||
|
module.exports = (sequelize) => {
|
||||||
|
const AuthenticationOverview = sequelize.define('AuthenticationOverviewView', {
|
||||||
|
ObjectGUID: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
sAMAccountName: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
mail: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
givenName: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
sn: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
type: DataTypes.BOOLEAN
|
||||||
|
},
|
||||||
|
online: {
|
||||||
|
type: DataTypes.BOOLEAN
|
||||||
|
},
|
||||||
|
RoleCount: {
|
||||||
|
type: DataTypes.INTEGER
|
||||||
|
},
|
||||||
|
GroupCount: {
|
||||||
|
type: DataTypes.INTEGER
|
||||||
|
},
|
||||||
|
ObjectSourceName: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
tableName: 'vAuthenticationOverview',
|
||||||
|
schema: 'dbo',
|
||||||
|
timestamps: false,
|
||||||
|
|
||||||
|
// 🔥 WICHTIG für Views
|
||||||
|
freezeTableName: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return AuthenticationOverview;
|
||||||
|
};
|
||||||
@@ -4,7 +4,9 @@ module.exports = (sequelize) => {
|
|||||||
const Group = sequelize.define('Group', {
|
const Group = sequelize.define('Group', {
|
||||||
ObjectGUID: {
|
ObjectGUID: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
primaryKey: true
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
allowNull: false
|
||||||
},
|
},
|
||||||
Name: DataTypes.STRING(255),
|
Name: DataTypes.STRING(255),
|
||||||
ObjectSource_ID: DataTypes.INTEGER,
|
ObjectSource_ID: DataTypes.INTEGER,
|
||||||
|
|||||||
39
src/models/groupOverviewView.js
Normal file
39
src/models/groupOverviewView.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
const { DataTypes } = require('sequelize');
|
||||||
|
|
||||||
|
module.exports = (sequelize) => {
|
||||||
|
const GroupOverview = sequelize.define('GroupOverviewView', {
|
||||||
|
|
||||||
|
ObjectGUID: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
|
||||||
|
Name: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
|
||||||
|
UserCount: {
|
||||||
|
type: DataTypes.INTEGER
|
||||||
|
},
|
||||||
|
|
||||||
|
RoleCount: {
|
||||||
|
type: DataTypes.INTEGER
|
||||||
|
},
|
||||||
|
|
||||||
|
ObjectSourceName: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
|
||||||
|
distinguishedName: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
}
|
||||||
|
|
||||||
|
}, {
|
||||||
|
tableName: 'vGroupOverview',
|
||||||
|
schema: 'dbo',
|
||||||
|
timestamps: false,
|
||||||
|
freezeTableName: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return GroupOverview;
|
||||||
|
};
|
||||||
@@ -13,10 +13,10 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Administration"
|
"action": "Administration"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"authorized": true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Configs",
|
"label": "Configs",
|
||||||
@@ -29,34 +29,29 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Administration"
|
"action": "Administration"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"authorized": true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "RBAC",
|
"label": "RBAC",
|
||||||
"description": "Role-Based Access Control ist eine rollenbasierte Zugriffskontrolle, die auf Basis von Rollen und Gruppen, systemweite Berechtigungen vergibt",
|
"description": "Role-Based Access Control ist eine rollenbasierte Zugriffskontrolle, die auf Basis von Rollen und Gruppen, systemweite Berechtigungen vergibt",
|
||||||
"view": "rbac.hbs",
|
"view": "rbac",
|
||||||
"defaultSize": {
|
"defaultSize": {
|
||||||
"width": "800px",
|
"width": "800px",
|
||||||
"height": "600px"
|
"height": "600px"
|
||||||
},
|
},
|
||||||
"icon": "app.png",
|
"icon": "shield.png",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Administration"
|
"action": "Administration"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"authorized": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"onlyAdministration": false,
|
|
||||||
"defaultSize": {
|
|
||||||
"width": "800px",
|
|
||||||
"height": "600px"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -70,24 +65,19 @@
|
|||||||
"label": "EventLog",
|
"label": "EventLog",
|
||||||
"view": "eventlog",
|
"view": "eventlog",
|
||||||
"defaultSize": {
|
"defaultSize": {
|
||||||
"width": "1200px",
|
"width": "800px",
|
||||||
"height": "1200px"
|
"height": "600px"
|
||||||
},
|
},
|
||||||
"icon": "eventlog.ico",
|
"icon": "eventlog.ico",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Administration"
|
"action": "Administration"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"authorized": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"onlyAdministration": false,
|
|
||||||
"defaultSize": {
|
|
||||||
"width": "1200px",
|
|
||||||
"height": "1200px"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -108,17 +98,12 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Administration"
|
"action": "Administration"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"authorized": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"onlyAdministration": false,
|
|
||||||
"defaultSize": {
|
|
||||||
"width": "900px",
|
|
||||||
"height": "800px"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -139,17 +124,12 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Administration"
|
"action": "Administration"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"authorized": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"onlyAdministration": false,
|
|
||||||
"defaultSize": {
|
|
||||||
"width": "900px",
|
|
||||||
"height": "500px"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -166,21 +146,16 @@
|
|||||||
"width": "460px",
|
"width": "460px",
|
||||||
"height": "515px"
|
"height": "515px"
|
||||||
},
|
},
|
||||||
"icon": "app.png",
|
"icon": "settings.png",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Default_Access"
|
"action": "Default_Access"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"authorized": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"onlyAdministration": false,
|
|
||||||
"defaultSize": {
|
|
||||||
"width": "460px",
|
|
||||||
"height": "515px"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -197,17 +172,12 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"scope": "SYSTEM",
|
"scope": "SYSTEM",
|
||||||
|
"resource": "ALL",
|
||||||
"action": "Default_Access"
|
"action": "Default_Access"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"authorized": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"onlyAdministration": false,
|
|
||||||
"defaultSize": {
|
|
||||||
"width": 800,
|
|
||||||
"height": 600
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
65
src/models/permissionOverviewView.js
Normal file
65
src/models/permissionOverviewView.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
const { DataTypes } = require('sequelize');
|
||||||
|
|
||||||
|
module.exports = (sequelize) => {
|
||||||
|
const VPermissionOverviewView = sequelize.define(
|
||||||
|
'vPermissionOverviewView',
|
||||||
|
{
|
||||||
|
Permission_ID: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
|
||||||
|
Scope: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
Resource: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
Action: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
PermissionKey: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
|
||||||
|
Role_ID: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
|
||||||
|
RoleCount: {
|
||||||
|
type: DataTypes.INTEGER
|
||||||
|
},
|
||||||
|
|
||||||
|
GroupCount: {
|
||||||
|
type: DataTypes.INTEGER
|
||||||
|
},
|
||||||
|
|
||||||
|
DirectUserCount: {
|
||||||
|
type: DataTypes.INTEGER
|
||||||
|
},
|
||||||
|
|
||||||
|
GroupUserCount: {
|
||||||
|
type: DataTypes.INTEGER
|
||||||
|
},
|
||||||
|
|
||||||
|
TotalUserCount: {
|
||||||
|
type: DataTypes.INTEGER
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tableName: 'vPermissionOverview', // ⚠️ exakt dein SQL View Name
|
||||||
|
timestamps: false,
|
||||||
|
freezeTableName: true,
|
||||||
|
|
||||||
|
// 🔒 Wichtig für Views
|
||||||
|
createdAt: false,
|
||||||
|
updatedAt: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 🚫 keine Mutationen erlauben (View!)
|
||||||
|
VPermissionOverviewView.removeAttribute('id');
|
||||||
|
|
||||||
|
return VPermissionOverviewView;
|
||||||
|
};
|
||||||
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;
|
||||||
|
};
|
||||||
36
src/models/roleOverviewView.js
Normal file
36
src/models/roleOverviewView.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const { DataTypes } = require('sequelize');
|
||||||
|
|
||||||
|
module.exports = (sequelize ) => {
|
||||||
|
return sequelize.define('vRoleOverview', {
|
||||||
|
|
||||||
|
ID: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
|
||||||
|
Name: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
|
||||||
|
Description: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
|
||||||
|
RoleType: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
},
|
||||||
|
|
||||||
|
GroupCount: {
|
||||||
|
type: DataTypes.INTEGER
|
||||||
|
},
|
||||||
|
|
||||||
|
UserCount: {
|
||||||
|
type: DataTypes.INTEGER
|
||||||
|
}
|
||||||
|
|
||||||
|
}, {
|
||||||
|
tableName: 'vRoleOverview', // 🔥 wichtig: Name deiner VIEW
|
||||||
|
timestamps: false,
|
||||||
|
freezeTableName: true
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,25 @@ const { exec } = require('child_process');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { localPath, cache, runtimeFile } = require('@root/globalize.js');
|
const { localPath, cache, runtimeFile } = require('@root/globalize.js');
|
||||||
|
const { databaseModel } = require('@root/server.js');
|
||||||
|
const { raw } = require('body-parser');
|
||||||
const configurationFile = path.join(require('@root/server.js').path.source, 'models', 'configuration.json');
|
let rbacUsers, rbacGroups, rbacRoles, rbacPermissions = [];
|
||||||
const stylesheetFile = path.join(require('@root/server.js').path.source, 'models', 'stylesheet.json');
|
|
||||||
const serverInfoFile = path.join(require('@root/server.js').path.root, 'package.json');
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
route(app, service) {
|
route(app, service) {
|
||||||
// JSON configuration abrufen
|
// JSON configuration abrufen
|
||||||
app.post('/api/getConfig', (req, res) => {
|
app.post('/api/config/get', (req, res) => {
|
||||||
res.status(200).send(runtimeFile.configuration.live);
|
res.status(200).send(runtimeFile.configuration.live);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// JSON stylesheet speichern
|
||||||
|
app.post('/api/styles/get', (req, res) => {
|
||||||
|
res.status(200).send(runtimeFile.stylesheet.live);
|
||||||
|
});
|
||||||
|
|
||||||
// JSON configuration speichern
|
// JSON configuration speichern
|
||||||
app.post('/config', (req, res) => {
|
app.post('/api/config/save', (req, res) => {
|
||||||
try {
|
try {
|
||||||
runtimeFile.configuration.save(req.body);
|
runtimeFile.configuration.save(req.body);
|
||||||
res.status(200).send({ status: 'ok' });
|
res.status(200).send({ status: 'ok' });
|
||||||
@@ -27,7 +30,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// JSON stylesheet abrufen
|
// JSON stylesheet abrufen
|
||||||
app.post('/api/getStyles', (req, res) => {
|
app.post('/api/styles/save', (req, res) => {
|
||||||
try {
|
try {
|
||||||
runtimeFile.stylesheet.save(req.body);
|
runtimeFile.stylesheet.save(req.body);
|
||||||
res.status(200).send({ status: 'ok' });
|
res.status(200).send({ status: 'ok' });
|
||||||
@@ -35,30 +38,12 @@ module.exports = {
|
|||||||
return res.status(500).send(err)
|
return res.status(500).send(err)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// JSON stylesheet speichern
|
|
||||||
app.post('/style', (req, res) => {
|
|
||||||
fs.writeFile(runtimeFile.stylesheet.live, JSON.stringify(req.body, null, 2), (err) => {
|
|
||||||
if (err) return res.status(500).send(err);
|
|
||||||
res.status(200).send({ status: 'ok' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// JSON package.json abrufen
|
// JSON package.json abrufen
|
||||||
app.post('/api/getServerInfo', (req, res) => {
|
app.post('/api/serverInfo/get', (req, res) => {
|
||||||
res.status(200).send({ package: runtimeFile.package.live, pid: process.pid, releaseNotes: runtimeFile.releaseNotes.live });
|
res.status(200).send({ package: runtimeFile.package.live, pid: process.pid, releaseNotes: runtimeFile.releaseNotes.live });
|
||||||
});
|
});
|
||||||
|
|
||||||
// JSON package.json speichern
|
|
||||||
app.post('/serverinfo', (req, res) => {
|
|
||||||
fs.writeFile(runtimeFile.package.live, JSON.stringify(req.body, null, 2), (err) => {
|
|
||||||
if (err) return res.status(500).send(err);
|
|
||||||
res.status(200).send({ status: 'ok' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.post('/api/eventlog/clearlog', (req, res) => {
|
app.post('/api/eventlog/clearlog', (req, res) => {
|
||||||
service.get('eventManager').clear();
|
service.get('eventManager').clear();
|
||||||
res.status(200).send({ status: 'ok' })
|
res.status(200).send({ status: 'ok' })
|
||||||
@@ -103,11 +88,334 @@ module.exports = {
|
|||||||
result = await service.get('pluginManager').unload(name);
|
result = await service.get('pluginManager').unload(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
service.get('eventManager').write(null, result.levelId, name, result.message);
|
service.get('eventManager').write(req.cookies.ObjectGUID, result.levelId, name, result.message);
|
||||||
service.get('socketManager').broadcast('/', 'plugin_status', result);
|
service.get('socketManager').broadcast('/', 'plugin_status', result);
|
||||||
res.status(200).json(result);
|
res.status(200).json(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// #region RBAC
|
||||||
|
// =========================================================
|
||||||
|
// 👤 AUTH
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
app.post('/api/rbac/auth/get', async (req, res) => {
|
||||||
|
try {
|
||||||
|
rbacUsers = await service.get('rbacManager').getAuth();
|
||||||
|
res.json(rbacUsers);
|
||||||
|
} catch (err) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', err);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/api/rbac/auth/details/:guid', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const singleAuth = await databaseModel.get('authentication').findOne({ where: { ObjectGUID: req.params.guid }, raw: true });
|
||||||
|
const { refreshtoken, password, ...rest} = singleAuth
|
||||||
|
res.json(rest);
|
||||||
|
} catch (err) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', err);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
app.post('/api/rbac/auth/create', async (req, res) => {
|
||||||
|
try {
|
||||||
|
if(rbacUsers.map(user => user.sAMAccountName.toLowerCase() ).includes(req.body.sAMAccountName.toLowerCase())) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 2, 'RBAC', `${req.body.sAMAccountName} nicht angelegt.\r\nBenutzer existiert bereits`);
|
||||||
|
return res.status(400).json({ error: `${req.body.sAMAccountName} existiert bereits` });
|
||||||
|
}
|
||||||
|
const user = await service.get('rbacManager').createAuth(req.body);
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 0, 'RBAC', `Benutzer ${user.sAMAccountName} [${user.ObjectGUID}] angelegt`);
|
||||||
|
res.json(user);
|
||||||
|
} catch (err) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', err.message);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/rbac/auth/syncFromAD', async (req, res) => {
|
||||||
|
try {
|
||||||
|
rbacUsers = await service.get('rbacManager').syncAuthByActiveDirectory();
|
||||||
|
console.log(await service.get('rbacManager').syncAuthenticationGroupsFromAD());
|
||||||
|
res.json(rbacUsers);
|
||||||
|
} catch (err) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', err.message);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put('/api/rbac/auth/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
await service.get('rbacManager').updateAuth(req.params.id, req.body);
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete('/api/rbac/auth/:guid', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const user = await databaseModel.get('authentication').findOne({ where: { ObjectGUID: req.params.guid }, raw: true });
|
||||||
|
if(!user) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 2, 'RBAC', `Authentifizierungs-GUID ${req.params.guid} nicht gefunden`);
|
||||||
|
res.status(400);
|
||||||
|
}
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 0, 'RBAC', `Benutzer ${user.sAMAccountName} [${user.ObjectGUID}] gelöscht`);
|
||||||
|
await service.get('rbacManager').deleteAuth(user.ObjectGUID);
|
||||||
|
res.status(200).json(user);
|
||||||
|
} catch (err) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', err);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 👥 GROUPS
|
||||||
|
// =========================================================
|
||||||
|
app.post('/api/rbac/group/get', async (req, res) => {
|
||||||
|
try {
|
||||||
|
rbacGroups = await service.get('rbacManager').getGroup();
|
||||||
|
res.json(rbacGroups);
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/rbac/group/create', async (req, res) => {
|
||||||
|
try {
|
||||||
|
if(rbacGroups.map(group => group.Name.toLowerCase()).includes(req.body.name.toLowerCase())) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 2, 'RBAC', `${req.body.name} nicht angelegt.\r\nGruppe existiert bereits`);
|
||||||
|
return res.status(400).json({ error: `${req.body.name} existiert bereits` });
|
||||||
|
}
|
||||||
|
const group = await service.get('rbacManager').createGroup(req.body);
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 0, 'RBAC', `Gruppe ${group.Name} [${group.ObjectGUID}] angelegt`);
|
||||||
|
res.json(group);
|
||||||
|
} catch (err) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', err.message);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/rbac/group/syncFromAD', async (req, res) => {
|
||||||
|
try {
|
||||||
|
console.log(await service.get('rbacManager').syncGroupClosureFromAD());
|
||||||
|
rbacGroups = await service.get('rbacManager').syncGroupByActiveDirectory();
|
||||||
|
res.json(rbacGroups);
|
||||||
|
} catch (err) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', err);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put('/api/rbac/group/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
await service.get('rbacManager').updateGroup(req.params.id, req.body);
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete('/api/rbac/group/:guid', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const group = await databaseModel.get('group').findOne({ where: { ObjectGUID: req.params.guid }, raw: true });
|
||||||
|
if(!group) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 2, 'RBAC', `Gruppen-GUID ${req.params.guid} nicht gefunden`);
|
||||||
|
res.status(400);
|
||||||
|
}
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 0, 'RBAC', `Gruppe ${group.Name} [${group.ObjectGUID}] gelöscht`);
|
||||||
|
await service.get('rbacManager').deleteGroup(group.ObjectGUID);
|
||||||
|
res.status(200).json(group);
|
||||||
|
} catch (err) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', err);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 🔗 USER ↔ GROUP
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
app.post('/api/rbac/group/add-user', async (req, res) => {
|
||||||
|
const { authGuid, groupGuid } = req.body;
|
||||||
|
const auth = await databaseModel.get('authentication').findOne({ where: { ObjectGUID: authGuid }, raw: true });
|
||||||
|
const group = await databaseModel.get('group').findOne({ where: { ObjectGUID: groupGuid }, raw: true });
|
||||||
|
try {
|
||||||
|
const result = await service.get('rbacManager').addUserToGroup(authGuid, groupGuid);
|
||||||
|
if(!result) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 2, 'RBAC', `Benuzer ${auth.sAMAccountName} [${auth.ObjectGUID}] bereits in Gruppe ${group.Name} [${group.ObjectGUID}]`);
|
||||||
|
res.status(400).send('Benutzer bereits in Gruppe enthalten');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', [ `Fehler beim Hinzufügen von ${auth.sAMAccountName} [${auth.ObjectGUID}] in Gruppe ${group.Name} [${group.ObjectGUID}]` ,err ]);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/group/remove-user', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { authGuid, groupId } = req.body;
|
||||||
|
await service.get('rbacManager').removeUserFromGroup(authId, groupId);
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 🎭 ROLES
|
||||||
|
// =========================================================
|
||||||
|
app.post('/api/rbac/role/get', async (req, res) => {
|
||||||
|
try {
|
||||||
|
rbacRoles = await service.get('rbacManager').getRole();
|
||||||
|
res.json(rbacRoles);
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/rbac/role/create', async (req, res) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
if(rbacRoles.map(role => role.Name.toLowerCase()).includes(req.body.name.toLowerCase())) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 2, 'RBAC', `${req.body.name} nicht angelegt.\r\nGruppe existiert bereits`);
|
||||||
|
return res.status(400).json({ error: `${req.body.name} existiert bereits` });
|
||||||
|
}
|
||||||
|
const role = await service.get('rbacManager').createRole(req.body);
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 0, 'RBAC', `Gruppe ${role.Name} [${role.ID}] angelegt`);
|
||||||
|
console.log(role);
|
||||||
|
res.json(role);
|
||||||
|
} catch (err) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', err.message);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put('/api/rbac/role/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
await service.get('rbacManager').updateRole(req.params.id, req.body);
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete('/api/rbac/role/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const role = await databaseModel.get('roleModel').findOne({ where: { ID: req.params.id }, raw: true });
|
||||||
|
if(!role) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 2, 'RBAC', `Rolen-ID ${req.params.id} nicht gefunden`);
|
||||||
|
res.status(400);
|
||||||
|
}
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 0, 'RBAC', `Role ${role.Name} [${role.ID}] gelöscht`);
|
||||||
|
await service.get('rbacManager').deleteRole(role.ID);
|
||||||
|
res.status(200).json(role);
|
||||||
|
} catch (err) {
|
||||||
|
service.get('eventManager').writeLog(req.cookies.ObjectGUID, 4, 'RBAC', err);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 🔗 ROLE ASSIGNMENTS
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
app.post('/api/rbac/role/add-user', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { authGuid, roleId } = req.body;
|
||||||
|
await service.get('rbacManager').assignRoleToUser(authGuid, roleId);
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/rbac/role/add-group', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { groupGuid, roleId } = req.body;
|
||||||
|
await service.get('rbacManager').assignRoleToGroup(groupGuid, roleId);
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/rbac/role/add-permission', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { roleId, permissionId } = req.body;
|
||||||
|
await service.get('rbacManager').addPermissionToRole(roleId, permissionId);
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/role/remove-permission', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { roleId, permissionId } = req.body;
|
||||||
|
await service.get('rbacManager').removePermissionFromRole(roleId, permissionId);
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 🔐 PERMISSIONS
|
||||||
|
// =========================================================
|
||||||
|
app.post('/api/rbac/permission/get', async (req, res) => {
|
||||||
|
try {
|
||||||
|
rbacPermissions = await service.get('rbacManager').getPermission();
|
||||||
|
console.log(rbacPermissions)
|
||||||
|
res.json(rbacPermissions);
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.post('/api/rbac/permission/create', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const perm = await service.get('rbacManager').createPermission(req.body);
|
||||||
|
res.json(perm);
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.put('/permission/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
await service.get('rbacManager').updatePermission(req.params.id, req.body);
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete('/permission/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
await service.get('rbacManager').deletePermission(req.params.id);
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//#endregion RBAC
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.post('/api/plugins/getAll', async (req, res) => {
|
app.post('/api/plugins/getAll', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const plugins = await service.get('pluginManager').getStatus();
|
const plugins = await service.get('pluginManager').getStatus();
|
||||||
@@ -119,7 +427,7 @@ module.exports = {
|
|||||||
|
|
||||||
app.post('/api/plugins/integrated', async (req, res) => {
|
app.post('/api/plugins/integrated', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
res.status(200).json(cached.startMenuItems.live);
|
res.status(200).json(runtimeFile.startMenuItems.live);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ error: error.message });
|
res.status(500).json({ error: error.message });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,11 @@ module.exports = {
|
|||||||
|
|
||||||
let context = fs.existsSync(pluginPath)
|
let context = fs.existsSync(pluginPath)
|
||||||
? service.get('fileSystemManager').loadJSON(pluginPath)
|
? service.get('fileSystemManager').loadJSON(pluginPath)
|
||||||
: runtimeFile.startMenuItems.live.find(item => item.name == name);
|
: cache.startMenuItems.find(item => item.name == name);
|
||||||
|
|
||||||
context.defaultSize =
|
context.defaultSize =
|
||||||
context.menu.items.find(item => item.label == viewLabel)?.defaultSize ||
|
context.menu.items.find(item => item.label == viewLabel)?.defaultSize ||
|
||||||
{ width: 800, height: 600 };
|
{ width: 800, height: 600 };
|
||||||
|
|
||||||
delete context.config;
|
delete context.config;
|
||||||
res.json({ name, view, viewLabel, context, location, size, state, zIndex });
|
res.json({ name, view, viewLabel, context, location, size, state, zIndex });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const { verify } = require("jsonwebtoken");
|
const { localPath, cache, runtimeFile } = require('@root/globalize.js');
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -71,7 +71,7 @@ module.exports = {
|
|||||||
|
|
||||||
res.clearCookie('sAMAccountName');
|
res.clearCookie('sAMAccountName');
|
||||||
res.clearCookie('ObjectGUID');
|
res.clearCookie('ObjectGUID');
|
||||||
res.render('login', { layout: false, title: app.locals.configuration.server.name })
|
res.render('login', { layout: false, title: runtimeFile.configuration.live.server.name })
|
||||||
// setTimeout(() => res.render('login', { layout: false, title: app.locals.configuration.server.name }), 3000);
|
// setTimeout(() => res.render('login', { layout: false, title: app.locals.configuration.server.name }), 3000);
|
||||||
// res.json({ message: 'Logout erfolgreich' });
|
// res.json({ message: 'Logout erfolgreich' });
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,10 @@ class AuthenticationManager {
|
|||||||
return { token: null, levelId: 2, message: 'Falsches Passwort' };
|
return { token: null, levelId: 2, message: 'Falsches Passwort' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!user.active) {
|
||||||
|
return { token: null, levelId: 2, message: 'Benutzer nicht aktiv' };
|
||||||
|
}
|
||||||
|
|
||||||
const token = jwt.sign(
|
const token = jwt.sign(
|
||||||
{
|
{
|
||||||
ObjectGUID: user.ObjectGUID,
|
ObjectGUID: user.ObjectGUID,
|
||||||
@@ -67,12 +71,26 @@ class AuthenticationManager {
|
|||||||
);
|
);
|
||||||
|
|
||||||
user.refreshtoken = token;
|
user.refreshtoken = token;
|
||||||
user.online = true;
|
await this.setOnline(sAMAccountName);
|
||||||
await user.save();
|
await user.save();
|
||||||
|
|
||||||
return { token, levelId: 0, message: 'Erfolgreich angemeldet' };
|
return { token, levelId: 0, message: 'Erfolgreich angemeldet' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async setOnline(sAMAccountName) {
|
||||||
|
const user = await this.findUser(sAMAccountName);
|
||||||
|
user.online = true;
|
||||||
|
await user.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
async setOffline(sAMAccountName) {
|
||||||
|
const user = await this.findUser(sAMAccountName);
|
||||||
|
user.online = false;
|
||||||
|
await user.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// LOGOUT
|
// LOGOUT
|
||||||
// =========================================================
|
// =========================================================
|
||||||
@@ -85,8 +103,8 @@ class AuthenticationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
user.refreshtoken = null;
|
user.refreshtoken = null;
|
||||||
user.online = false;
|
|
||||||
await user.save();
|
await user.save();
|
||||||
|
await this.setOffline();
|
||||||
|
|
||||||
return { token: null, levelId: 0, message: 'Erfolgreich abgemeldet' };
|
return { token: null, levelId: 0, message: 'Erfolgreich abgemeldet' };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class EventManager {
|
|||||||
const stackLine = err.stack.split('\n')[2]; // calls trace-line
|
const stackLine = err.stack.split('\n')[2]; // calls trace-line
|
||||||
const trace = stackLine.match(/\/.*\d+/)[0].replace(localPath.root, ''); // path:line:column
|
const trace = stackLine.match(/\/.*\d+/)[0].replace(localPath.root, ''); // path:line:column
|
||||||
|
|
||||||
const message = `${this.EventLog.tableName} cleared successfully`;
|
const message = `${this.EventLog.tableName} geleert`;
|
||||||
await this.EventLog.destroy({
|
await this.EventLog.destroy({
|
||||||
where: {},
|
where: {},
|
||||||
truncate: true,
|
truncate: true,
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
// rbac/RbacService.js
|
const jwt = require('jsonwebtoken');
|
||||||
|
const sequelize = require('sequelize');
|
||||||
|
|
||||||
class RBACManager {
|
class RBACManager {
|
||||||
constructor(databaseModel) {
|
constructor(databaseModel, SECRET_KEY, service) {
|
||||||
this.db = databaseModel;
|
this.db = databaseModel;
|
||||||
|
this.SECRET_KEY = SECRET_KEY;
|
||||||
|
this.service = service;
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolvePermissions(objectGuid) {
|
async resolvePermissions(objectGuid) {
|
||||||
@@ -128,8 +131,6 @@ class RBACManager {
|
|||||||
return next(); // oder 401 wenn du streng sein willst
|
return next(); // oder 401 wenn du streng sein willst
|
||||||
}
|
}
|
||||||
|
|
||||||
const rbac = this.rbac;
|
|
||||||
|
|
||||||
const permissions = req.user.permissions || [];
|
const permissions = req.user.permissions || [];
|
||||||
const isSuperAdmin = req.user.isSuperAdmin || false;
|
const isSuperAdmin = req.user.isSuperAdmin || false;
|
||||||
|
|
||||||
@@ -137,16 +138,12 @@ class RBACManager {
|
|||||||
permissions,
|
permissions,
|
||||||
isSuperAdmin,
|
isSuperAdmin,
|
||||||
hasPermission: (required) =>
|
hasPermission: (required) =>
|
||||||
rbac.hasPermission(permissions, required, isSuperAdmin)
|
this.hasPermission(permissions, required, isSuperAdmin)
|
||||||
};
|
};
|
||||||
|
|
||||||
return next();
|
|
||||||
|
|
||||||
next();
|
next();
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[RBAC MIDDLEWARE ERROR]', err);
|
return res.status(500).json('[RBAC MIDDLEWARE ERROR]', err);
|
||||||
return res.status(500).json({ message: 'RBAC Fehler' });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -168,7 +165,11 @@ class RBACManager {
|
|||||||
|
|
||||||
const publicRoutes = [
|
const publicRoutes = [
|
||||||
'/login',
|
'/login',
|
||||||
'/public'
|
'/public',
|
||||||
|
'/css',
|
||||||
|
'/js',
|
||||||
|
'/images',
|
||||||
|
'/favicon.ico'
|
||||||
];
|
];
|
||||||
|
|
||||||
const isPublicRoute = publicRoutes.some(route =>
|
const isPublicRoute = publicRoutes.some(route =>
|
||||||
@@ -189,8 +190,9 @@ class RBACManager {
|
|||||||
return res.redirect('/login');
|
return res.redirect('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await this.db.get('authentication').findOne( { where: { sAMAccountName } } );
|
req.user = {};
|
||||||
|
|
||||||
|
const user = await this.db.get('authentication').findOne( { where: { sAMAccountName } } );
|
||||||
if (!user || !user.active) {
|
if (!user || !user.active) {
|
||||||
return res.redirect('/login');
|
return res.redirect('/login');
|
||||||
}
|
}
|
||||||
@@ -216,7 +218,7 @@ class RBACManager {
|
|||||||
permissions: normalized,
|
permissions: normalized,
|
||||||
isSuperAdmin
|
isSuperAdmin
|
||||||
};
|
};
|
||||||
|
|
||||||
next();
|
next();
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -234,6 +236,513 @@ class RBACManager {
|
|||||||
resource: p.resource || null
|
resource: p.resource || null
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//#region CRUD
|
||||||
|
// =========================================================
|
||||||
|
// 👤 AUTH CRUD
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
async getAuth() {
|
||||||
|
const Auth = this.db.get('authenticationOverviewView');
|
||||||
|
return await Auth.findAll({ raw: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async syncAuthByActiveDirectory() {
|
||||||
|
const auth = await this.db.get('authentication');
|
||||||
|
const all = await this.service.get('activeDirectoryManager').getAllUsers();
|
||||||
|
|
||||||
|
all.forEach(async user => {
|
||||||
|
try {
|
||||||
|
if(user.objectGUID !== null) {
|
||||||
|
await auth.upsert({...user, ObjectGUID: user.objectGUID});
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async createAuth(data) {
|
||||||
|
const Auth = this.db.get('authentication');
|
||||||
|
|
||||||
|
return await Auth.create({
|
||||||
|
sAMAccountName: data.sAMAccountName,
|
||||||
|
mail: data.mail,
|
||||||
|
sn: data.sn,
|
||||||
|
givenName: data.givenName,
|
||||||
|
ObjectSource_ID: 1,
|
||||||
|
active: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateAuth(id, data) {
|
||||||
|
const Auth = this.db.get('authentication');
|
||||||
|
|
||||||
|
return await Auth.update(data, {
|
||||||
|
where: { ObjectGUID: id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteAuth(id) {
|
||||||
|
const Auth = this.db.get('authentication');
|
||||||
|
const AuthGroups = this.db.get('authenticationGroupsModel');
|
||||||
|
|
||||||
|
await AuthGroups.destroy({
|
||||||
|
where: { Authentication_ObjectGUID: id }
|
||||||
|
});
|
||||||
|
return await Auth.destroy({
|
||||||
|
where: { ObjectGUID: id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 👥 GROUP CRUD
|
||||||
|
// =========================================================
|
||||||
|
async getGroup() {
|
||||||
|
const group = this.db.get('groupOverviewView');
|
||||||
|
return await group.findAll({ raw: true }) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async createGroup(data) {
|
||||||
|
const group = this.db.get('group');
|
||||||
|
return await group.create({
|
||||||
|
Name: data.name,
|
||||||
|
ObjectSource_ID: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async syncGroupByActiveDirectory() {
|
||||||
|
const group = await this.db.get('group');
|
||||||
|
const all = await this.service.get('activeDirectoryManager').getAllGroups({
|
||||||
|
attributes: ['objectGUID', 'sAMAccountName', 'distinguishedName'],
|
||||||
|
includeMembers: false
|
||||||
|
});
|
||||||
|
|
||||||
|
all.forEach(async adGroup => {
|
||||||
|
try {
|
||||||
|
if(adGroup.objectGUID !== null) {
|
||||||
|
await group.upsert({...adGroup, ObjectGUID: adGroup.objectGUID, Name: adGroup.sAMAccountName});
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateGroup(id, data) {
|
||||||
|
const Group = this.db.get('group');
|
||||||
|
|
||||||
|
return await Group.update(data, {
|
||||||
|
where: { ObjectGUID: id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteGroup(id) {
|
||||||
|
const Group = this.db.get('group');
|
||||||
|
const AuthGroups = this.db.get('authenticationGroupsModel');
|
||||||
|
|
||||||
|
await AuthGroups.destroy({
|
||||||
|
where: { Group_ObjectGUID: id }
|
||||||
|
});
|
||||||
|
|
||||||
|
return await Group.destroy({
|
||||||
|
where: { ObjectGUID: id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 🔗 AUTH ↔ GROUP RELATION
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
async addUserToGroup(authGuid, groupGuid) {
|
||||||
|
const AuthGroups = this.db.get('authenticationGroupsModel');
|
||||||
|
if(await AuthGroups.findOne({ where: { Authentication_ObjectGUID: authGuid } })) { // AuthGroups.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const group = await AuthGroups.create({
|
||||||
|
Authentication_ObjectGUID: authGuid,
|
||||||
|
Group_ObjectGUID: groupGuid
|
||||||
|
});
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeUserFromGroup(authId, groupId) {
|
||||||
|
const AuthGroups = this.db.get('authenticationGroupsModel');
|
||||||
|
|
||||||
|
return await AuthGroups.destroy({
|
||||||
|
where: {
|
||||||
|
Authentication_ObjectGUID: authId,
|
||||||
|
Group_ObjectGUID: groupId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async syncAuthenticationGroupsFromAD() {
|
||||||
|
|
||||||
|
const AuthGroups = this.db.get('authenticationGroupsModel');
|
||||||
|
|
||||||
|
const users = await this.service
|
||||||
|
.get('activeDirectoryManager')
|
||||||
|
.getAllUsers([
|
||||||
|
'objectGUID',
|
||||||
|
'memberOf'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const groups = await this.service
|
||||||
|
.get('activeDirectoryManager')
|
||||||
|
.getAllGroups({
|
||||||
|
attributes: [
|
||||||
|
'objectGUID',
|
||||||
|
'distinguishedName'
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -----------------------------------------------------
|
||||||
|
* GROUP MAP (DN -> GUID)
|
||||||
|
* -----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
const groupMap = new Map();
|
||||||
|
|
||||||
|
for (const g of groups) {
|
||||||
|
|
||||||
|
if (g.distinguishedName && g.objectGUID) {
|
||||||
|
groupMap.set(
|
||||||
|
g.distinguishedName.toLowerCase(),
|
||||||
|
g.objectGUID
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -----------------------------------------------------
|
||||||
|
* BUILD RELATIONS
|
||||||
|
* -----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
const relations = [];
|
||||||
|
|
||||||
|
for (const user of users) {
|
||||||
|
|
||||||
|
if (!user.objectGUID) continue;
|
||||||
|
|
||||||
|
const memberOf = Array.isArray(user.memberOf)
|
||||||
|
? user.memberOf
|
||||||
|
: user.memberOf
|
||||||
|
? [user.memberOf]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
for (const dn of memberOf) {
|
||||||
|
|
||||||
|
const groupGuid = groupMap.get(dn.toLowerCase());
|
||||||
|
|
||||||
|
if (!groupGuid) continue;
|
||||||
|
|
||||||
|
relations.push({
|
||||||
|
Authentication_ObjectGUID: user.objectGUID,
|
||||||
|
Group_ObjectGUID: groupGuid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -----------------------------------------------------
|
||||||
|
* DB SYNC (clean rebuild)
|
||||||
|
* -----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
// await AuthGroups.destroy({
|
||||||
|
// where: {}
|
||||||
|
// });
|
||||||
|
|
||||||
|
if (relations.length) {
|
||||||
|
await AuthGroups.upsert(relations, {
|
||||||
|
// ignoreDuplicates: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return relations;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async syncGroupClosureFromAD() {
|
||||||
|
|
||||||
|
const GroupClosure = this.db.get('groupClosureModel');
|
||||||
|
|
||||||
|
const groups = await this.service
|
||||||
|
.get('activeDirectoryManager')
|
||||||
|
.getAllGroups({
|
||||||
|
attributes: [
|
||||||
|
'objectGUID',
|
||||||
|
'distinguishedName',
|
||||||
|
'member'
|
||||||
|
],
|
||||||
|
includeMembers: false
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -----------------------------------------------------
|
||||||
|
* MAP: DN -> GUID
|
||||||
|
* -----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
const groupMap = new Map();
|
||||||
|
|
||||||
|
for (const g of groups) {
|
||||||
|
if (g.distinguishedName && g.objectGUID) {
|
||||||
|
groupMap.set(
|
||||||
|
g.distinguishedName.toLowerCase(),
|
||||||
|
g.objectGUID
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -----------------------------------------------------
|
||||||
|
* BUILD DIRECT RELATIONS
|
||||||
|
* -----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
const edges = [];
|
||||||
|
|
||||||
|
for (const group of groups) {
|
||||||
|
|
||||||
|
if (!group.objectGUID) continue;
|
||||||
|
|
||||||
|
const members = Array.isArray(group.member)
|
||||||
|
? group.member
|
||||||
|
: group.member
|
||||||
|
? [group.member]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
for (const memberDN of members) {
|
||||||
|
|
||||||
|
const childGUID = groupMap.get(
|
||||||
|
memberDN.toLowerCase()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!childGUID) continue;
|
||||||
|
|
||||||
|
edges.push({
|
||||||
|
ParentGroup_ObjectGUID: group.objectGUID,
|
||||||
|
ChildGroup_ObjectGUID: childGUID,
|
||||||
|
Depth: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -----------------------------------------------------
|
||||||
|
* TRANSITIVE CLOSURE (DFS)
|
||||||
|
* -----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
const closure = [...edges];
|
||||||
|
|
||||||
|
const adjacency = new Map();
|
||||||
|
|
||||||
|
for (const e of edges) {
|
||||||
|
|
||||||
|
if (!adjacency.has(e.ParentGroup_ObjectGUID)) {
|
||||||
|
adjacency.set(e.ParentGroup_ObjectGUID, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
adjacency.get(e.ParentGroup_ObjectGUID)
|
||||||
|
.push(e.ChildGroup_ObjectGUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
const visitedCache = new Map();
|
||||||
|
|
||||||
|
const dfs = (root, current, depth) => {
|
||||||
|
|
||||||
|
const children = adjacency.get(current) || [];
|
||||||
|
|
||||||
|
for (const child of children) {
|
||||||
|
|
||||||
|
closure.push({
|
||||||
|
ParentGroup_ObjectGUID: root,
|
||||||
|
ChildGroup_ObjectGUID: child,
|
||||||
|
Depth: depth + 1
|
||||||
|
});
|
||||||
|
|
||||||
|
dfs(root, child, depth + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const group of groups) {
|
||||||
|
|
||||||
|
const root = group.objectGUID;
|
||||||
|
if (!root) continue;
|
||||||
|
|
||||||
|
dfs(root, root, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -----------------------------------------------------
|
||||||
|
* CLEAN & SAVE
|
||||||
|
* -----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
// await GroupClosure.destroy({
|
||||||
|
// where: {}
|
||||||
|
// });
|
||||||
|
|
||||||
|
await GroupClosure.bulkCreate(closure, {
|
||||||
|
ignoreDuplicates: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return closure.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 🎭 ROLE CRUD
|
||||||
|
// =========================================================
|
||||||
|
async getRole() {
|
||||||
|
const role = this.db.get('roleOverviewView');
|
||||||
|
return await role.findAll({ raw: true }) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async createRole(data) {
|
||||||
|
const Role = this.db.get('roleModel');
|
||||||
|
|
||||||
|
return await Role.create({
|
||||||
|
Name: data.name,
|
||||||
|
Description: data.description || null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateRole(id, data) {
|
||||||
|
const Role = this.db.get('roleModel');
|
||||||
|
|
||||||
|
return await Role.update(data, {
|
||||||
|
where: { ID: id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteRole(id) {
|
||||||
|
const Role = this.db.get('roleModel');
|
||||||
|
const RolePermissions = this.db.get('rolePermissionsModel');
|
||||||
|
const GroupRoles = this.db.get('groupRolesModel');
|
||||||
|
const AuthRoles = this.db.get('authenticationRolesModel');
|
||||||
|
|
||||||
|
await AuthRoles.destroy({
|
||||||
|
where: { Role_ID: id }
|
||||||
|
});
|
||||||
|
await GroupRoles.destroy({
|
||||||
|
where: { Role_ID: id }
|
||||||
|
});
|
||||||
|
await RolePermissions.destroy({
|
||||||
|
where: { Role_ID: id }
|
||||||
|
});
|
||||||
|
return await Role.destroy({
|
||||||
|
where: { ID: id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 🔗 ROLE ASSIGNMENTS
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
async assignRoleToUser(authId, roleId) {
|
||||||
|
const AuthRoles = this.db.get('authenticationRolesModel');
|
||||||
|
|
||||||
|
return await AuthRoles.create({
|
||||||
|
Authentication_ObjectGUID: authId,
|
||||||
|
Role_ID: roleId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async assignRoleToGroup(groupId, roleId) {
|
||||||
|
const GroupRoles = this.db.get('groupRolesModel');
|
||||||
|
|
||||||
|
return await GroupRoles.create({
|
||||||
|
Group_ObjectGUID: groupId,
|
||||||
|
Role_ID: roleId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeRoleFromUser(authId, roleId) {
|
||||||
|
const AuthRoles = this.db.get('authenticationRolesModel');
|
||||||
|
|
||||||
|
return await AuthRoles.destroy({
|
||||||
|
where: {
|
||||||
|
Authentication_ObjectGUID: authId,
|
||||||
|
Role_ID: roleId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 🔐 PERMISSION CRUD
|
||||||
|
// =========================================================
|
||||||
|
async getPermission() {
|
||||||
|
const permission = this.db.get('permissionOverviewView');
|
||||||
|
return await permission.findAll({ raw: true }) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPermission(data) {
|
||||||
|
const Permission = this.db.get('permissionModel');
|
||||||
|
|
||||||
|
return await Permission.create({
|
||||||
|
Scope: data.scope,
|
||||||
|
Resource: data.resource,
|
||||||
|
Action: data.action
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePermission(id, data) {
|
||||||
|
const Permission = this.db.get('permissionModel');
|
||||||
|
|
||||||
|
return await Permission.update(data, {
|
||||||
|
where: { ID: id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePermission(id) {
|
||||||
|
const Permission = this.db.get('permissionModel');
|
||||||
|
|
||||||
|
return await Permission.destroy({
|
||||||
|
where: { ID: id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 🔗 ROLE ↔ PERMISSION
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
async addPermissionToRole(roleId, permissionId) {
|
||||||
|
const RolePerms = this.db.get('rolePermissionsModel');
|
||||||
|
|
||||||
|
return await RolePerms.create({
|
||||||
|
Role_ID: roleId,
|
||||||
|
Permission_ID: permissionId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async removePermissionFromRole(roleId, permissionId) {
|
||||||
|
const RolePerms = this.db.get('rolePermissionsModel');
|
||||||
|
|
||||||
|
return await RolePerms.destroy({
|
||||||
|
where: {
|
||||||
|
Role_ID: roleId,
|
||||||
|
Permission_ID: permissionId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = RBACManager;
|
module.exports = RBACManager;
|
||||||
@@ -56,7 +56,7 @@ module.exports = {
|
|||||||
...{ name: payload.name, view: payload.view, viewLabel: payload.viewLabel},
|
...{ name: payload.name, view: payload.view, viewLabel: payload.viewLabel},
|
||||||
...data,
|
...data,
|
||||||
};
|
};
|
||||||
console.log(templateData)
|
// console.log(templateData)
|
||||||
// Zuerst Plugin-View rendern
|
// Zuerst Plugin-View rendern
|
||||||
app.render(view, templateData, (err, contentHtml) => {
|
app.render(view, templateData, (err, contentHtml) => {
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ class SocketManager {
|
|||||||
userName: sAMAccountName
|
userName: sAMAccountName
|
||||||
});
|
});
|
||||||
|
|
||||||
// console.log(`${sAMAccountName} [${objectGuid}] connected to ${namespace}`);
|
console.log(`${sAMAccountName} [${objectGuid}] connected to ${namespace}`);
|
||||||
|
|
||||||
socket.on('disconnect', () => {
|
socket.on('disconnect', () => {
|
||||||
clients.delete(objectGuid);
|
clients.delete(objectGuid);
|
||||||
// console.log(`${sAMAccountName} [${objectGuid}] disconnected from ${namespace}`);
|
console.log(`${sAMAccountName} [${objectGuid}] disconnected from ${namespace}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const { cache } = require('@root/globalize.js');
|
||||||
|
const { service } = require('@root/server.js');
|
||||||
|
|
||||||
|
|
||||||
module.exports = (app, socketManager, namespace, pluginManager, authenticationModel, fileSystemManager, eventManager, activeDirectory) => {
|
module.exports = (app, socketManager, namespace, pluginManager, authenticationModel, fileSystemManager, eventManager, activeDirectory) => {
|
||||||
@@ -12,9 +14,26 @@ module.exports = (app, socketManager, namespace, pluginManager, authenticationMo
|
|||||||
// eventManager.write(data.objectGuid, data.levelId, data.pluginName, data.message)
|
// eventManager.write(data.objectGuid, data.levelId, data.pluginName, data.message)
|
||||||
});
|
});
|
||||||
|
|
||||||
// global.json.configuration.onChange(info => {
|
|
||||||
// console.log(info.delta)
|
|
||||||
// });
|
socketManager.io.on('connection', async (socket) => {
|
||||||
|
if(!socket.handshake.auth.objectGuid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await service.get('authenticationManager').setOnline(socket.handshake.auth.sAMAccountName);
|
||||||
|
});
|
||||||
|
|
||||||
|
socketManager.on(namespace, 'disconnect', (socket, data) => {
|
||||||
|
if(!socket.handshake.auth.objectGuid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
if(!socketManager.clients.get(namespace).has(socket.handshake.auth.objectGuid)) {
|
||||||
|
await service.get('authenticationManager').setOffline(socket.handshake.auth.sAMAccountName);
|
||||||
|
}
|
||||||
|
}, 8000);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
mainSocket.on('connection', socket => {
|
mainSocket.on('connection', socket => {
|
||||||
|
|||||||
20
utils.js
20
utils.js
@@ -21,8 +21,7 @@ module.exports = startMenuItems = async function (app, objectGuid) {
|
|||||||
// =========================
|
// =========================
|
||||||
// Load menu sources
|
// Load menu sources
|
||||||
// =========================
|
// =========================
|
||||||
const integratedStartmenuItems = runtimeFile.startMenuItems.live;
|
const integratedStartmenuItems = safeClone(runtimeFile.startMenuItems.live);
|
||||||
|
|
||||||
const plugins = service
|
const plugins = service
|
||||||
.get('pluginManager')
|
.get('pluginManager')
|
||||||
.getStatus()
|
.getStatus()
|
||||||
@@ -64,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 = [];
|
||||||
@@ -78,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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -106,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