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
|
||||
radix_os_*.png
|
||||
radix_os_icon.ico
|
||||
login.hbs
|
||||
login.hbs
|
||||
todo.txt
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
DEMO IS RUNNING:
|
||||
https://demo.sowadanas.dynv6.net/
|
||||
https://demo.sowada.dev/
|
||||
|
||||
|
||||
globalize:
|
||||
|
||||
272
dbcreate.sql
272
dbcreate.sql
@@ -24,6 +24,7 @@ GO
|
||||
-- DROP VIEW IF EXISTS dbo.vAuthentications;
|
||||
-- DROP VIEW IF EXISTS dbo.vEventLog;
|
||||
-- DROP VIEW IF EXISTS dbo.vNotifyTray;
|
||||
-- DROP VIEW IF EXISTS dbo.vPermissionOverview;s
|
||||
|
||||
-- DROP TABLE IF EXISTS dbo.AuthenticationRoles;
|
||||
-- DROP TABLE IF EXISTS dbo.AuthenticationGroups;
|
||||
@@ -44,26 +45,27 @@ GO
|
||||
-- DROP TABLE IF EXISTS dbo.ObjectSource;
|
||||
-- DROP TABLE IF EXISTS dbo.AuthenticationUAC;
|
||||
-- DROP TABLE IF EXISTS dbo.Vault;
|
||||
GO
|
||||
-- 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)
|
||||
ID INT IDENTITY(1,1) NOT NULL,
|
||||
License_ID INT NOT NULL,
|
||||
Customer_ID INT NOT NULL,
|
||||
Signature NVARCHAR(512) NOT NULL,
|
||||
EncryptedPayload VARBINARY(MAX) NOT NULL,
|
||||
ExpiresAt DATETIME2 NULL,
|
||||
Status_ID TINYINT NOT NULL,
|
||||
LastVerifiedAt DATETIME2 NULL,
|
||||
CreateDate DATETIME2 NOT NULL
|
||||
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 (
|
||||
ID INT IDENTITY(1,1) PRIMARY KEY,
|
||||
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;
|
||||
GO
|
||||
|
||||
|
||||
|
||||
|
||||
/* =========================================================
|
||||
FIXED vEventLog (SEQUELIZE MATCH + SYSTEM FIX)
|
||||
========================================================= */
|
||||
@@ -364,14 +369,14 @@ INSERT INTO dbo.EventLevels VALUES
|
||||
(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');
|
||||
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
|
||||
SELECT r.ID, p.ID
|
||||
@@ -380,6 +385,10 @@ JOIN dbo.Permission p ON p.Scope='SYSTEM'
|
||||
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
|
||||
========================================================= */
|
||||
@@ -412,4 +421,233 @@ SELECT
|
||||
'00000000-0000-0000-0000-000000000001',
|
||||
ID
|
||||
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,
|
||||
cache: {
|
||||
startMenuItems: [],
|
||||
clientsOnline: []
|
||||
},
|
||||
runtimeFile: {
|
||||
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",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"activedirectory2": "^2.2.0",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"body-parser": "^2.2.0",
|
||||
"child_process": "^1.0.2",
|
||||
@@ -21,7 +20,7 @@
|
||||
"fs-extra": "^11.3.2",
|
||||
"https": "^1.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"ldapjs": "^3.0.7",
|
||||
"ldapts": "^8.1.7",
|
||||
"module-alias": "^2.2.3",
|
||||
"multer": "^2.0.2",
|
||||
"net": "^1.0.2",
|
||||
@@ -298,92 +297,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.6.5.tgz",
|
||||
"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": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
@@ -455,11 +368,6 @@
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
||||
@@ -472,40 +380,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": {
|
||||
"version": "7.1.4",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
@@ -885,11 +727,6 @@
|
||||
"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": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||
@@ -1219,14 +1056,6 @@
|
||||
"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": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
||||
@@ -1558,14 +1387,6 @@
|
||||
"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": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
||||
@@ -1660,37 +1481,15 @@
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ldap-filter": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.3.3.tgz",
|
||||
"integrity": "sha512-/tFkx5WIn4HuO+6w9lsfxq4FN3O+fDZeO9Mek8dCD8rTUpqzRa766BOBO7BcGkn3X86m5+cBm1/2S/Shzz7gMg==",
|
||||
"node_modules/ldapts": {
|
||||
"version": "8.1.7",
|
||||
"resolved": "https://registry.npmjs.org/ldapts/-/ldapts-8.1.7.tgz",
|
||||
"integrity": "sha512-TJl6T92eIwMf/OJ0hDfKVa6ISwzo+lqCWCI5Mf//ARlKa3LKQZaSrme/H2rCRBhW0DZCQlrsV+fgoW5YHRNLUw==",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0"
|
||||
"strict-event-emitter-types": "2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"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": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
@@ -1769,17 +1568,6 @@
|
||||
"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": {
|
||||
"version": "1.54.0",
|
||||
"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_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": {
|
||||
"version": "4.0.1",
|
||||
"inBundle": true,
|
||||
@@ -2637,14 +2408,6 @@
|
||||
"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": {
|
||||
"version": "2.2.1",
|
||||
"inBundle": true,
|
||||
@@ -2653,10 +2416,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/npm/node_modules/err-code": {
|
||||
"version": "2.0.3",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/npm/node_modules/exponential-backoff": {
|
||||
"version": "3.1.3",
|
||||
"inBundle": true,
|
||||
@@ -2769,13 +2528,6 @@
|
||||
"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": {
|
||||
"version": "6.0.0",
|
||||
"inBundle": true,
|
||||
@@ -3448,17 +3200,6 @@
|
||||
"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": {
|
||||
"version": "3.0.1",
|
||||
"inBundle": true,
|
||||
@@ -3496,13 +3237,6 @@
|
||||
"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": {
|
||||
"version": "2.1.2",
|
||||
"inBundle": true,
|
||||
@@ -3582,22 +3316,6 @@
|
||||
"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": {
|
||||
"version": "2.5.0",
|
||||
"inBundle": true,
|
||||
@@ -3727,47 +3445,11 @@
|
||||
"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": {
|
||||
"version": "1.0.2",
|
||||
"inBundle": true,
|
||||
"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": {
|
||||
"version": "7.0.2",
|
||||
"inBundle": true,
|
||||
@@ -3960,14 +3642,6 @@
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
|
||||
"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": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
@@ -3976,11 +3650,6 @@
|
||||
"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": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -4508,6 +4177,11 @@
|
||||
"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": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
@@ -4735,43 +4409,6 @@
|
||||
"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": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
"license": "UNLICENSED",
|
||||
"licensefile": "license_internal.txt",
|
||||
"dependencies": {
|
||||
"activedirectory2": "^2.2.0",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"body-parser": "^2.2.0",
|
||||
"child_process": "^1.0.2",
|
||||
@@ -33,7 +32,7 @@
|
||||
"fs-extra": "^11.3.2",
|
||||
"https": "^1.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"ldapjs": "^3.0.7",
|
||||
"ldapts": "^8.1.7",
|
||||
"module-alias": "^2.2.3",
|
||||
"multer": "^2.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 = {}) {
|
||||
// this.hide();
|
||||
this.closeAllSubmenus();
|
||||
|
||||
let x, y;
|
||||
|
||||
@@ -147,25 +147,28 @@ function slideOutMessage(message) {
|
||||
//#region Feedbox
|
||||
|
||||
/**
|
||||
* feedbox({
|
||||
* title: `<span style="color:#f44336">⚠ Upload abbrechen?</span>`,
|
||||
* message: `
|
||||
* <p>Es laufen noch <b>aktive Uploads</b>.</p>
|
||||
* <p>Möchtest du wirklich <u>alle abbrechen</u>?</p>
|
||||
* `,
|
||||
* buttons: {
|
||||
* yes: {
|
||||
* text: '<b>Ja</b>, abbrechen',
|
||||
* onClick: () => stopUploadQueue()
|
||||
* },
|
||||
* no: {
|
||||
* text: 'Weiter hochladen'
|
||||
* },
|
||||
* cancel: {
|
||||
* text: 'Zurück'
|
||||
* }
|
||||
* }
|
||||
*});
|
||||
feedbox({
|
||||
title: `<span style="color:#f44336">⚠ Upload abbrechen?</span>`,
|
||||
lock: true,
|
||||
primary: 'yes',
|
||||
replace: false,
|
||||
message: `
|
||||
<p>Es laufen noch <b>aktive Uploads</b>.</p>
|
||||
<p>Möchtest du wirklich <u>alle abbrechen</u>?</p>
|
||||
`,
|
||||
buttons: {
|
||||
yes: {
|
||||
text: '<b>Ja</b>, abbrechen',
|
||||
onClick: () => stopUploadQueue()
|
||||
},
|
||||
no: {
|
||||
text: 'Weiter hochladen'
|
||||
},
|
||||
cancel: {
|
||||
text: 'Zurück'
|
||||
}
|
||||
}
|
||||
});
|
||||
*/
|
||||
function feedbox({
|
||||
title = '',
|
||||
|
||||
@@ -1033,6 +1033,7 @@ function virtualTable({
|
||||
});
|
||||
|
||||
// Live-Counter
|
||||
if(!filterConfig || filterConfig.hideCounter) return
|
||||
if(filterState.counterEl) filterState.counterEl.textContent = `${visibleCount} Treffer`;
|
||||
}
|
||||
|
||||
@@ -1224,20 +1225,21 @@ window.addEventListener('resize', () => {
|
||||
tableEl.before(container);
|
||||
|
||||
|
||||
// Höhe messen → thead offset setzen
|
||||
requestAnimationFrame(()=>{
|
||||
const h = container.getBoundingClientRect().height;
|
||||
tableEl.style.setProperty('--filter-height', h + 'px');
|
||||
});
|
||||
|
||||
filterState.counterEl = document.createElement('div');
|
||||
filterState.counterEl.className = 'live-counter';
|
||||
container.appendChild(filterState.counterEl);
|
||||
if(!filterConfig.hideCounter) {
|
||||
filterState.counterEl = document.createElement('div');
|
||||
filterState.counterEl.className = 'live-counter';
|
||||
container.appendChild(filterState.counterEl);
|
||||
}
|
||||
|
||||
syncFilterWidth(container);
|
||||
|
||||
const wrapperResizeObserver = new ResizeObserver(() => {
|
||||
// Filter-Header anpassen
|
||||
syncFilterWidth(container);
|
||||
|
||||
// Höhe messen → thead offset setzen
|
||||
const h = container.getBoundingClientRect().height;
|
||||
tableEl.style.setProperty('--filter-height', h + 'px');
|
||||
|
||||
// Tabelle neu rendern
|
||||
rebuildPrefix();
|
||||
@@ -1483,7 +1485,7 @@ window.addEventListener('resize', () => {
|
||||
},
|
||||
refresh(){ applyFilters(); render(); },
|
||||
clearData() { data = [] },
|
||||
source(newData) { data = []; this.addData(newData); },
|
||||
source(newData) { data = []; this.addData(newData); this.refresh(); },
|
||||
prepareData() { prepareData(); }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,19 +11,44 @@ const restoreIcon = '🗗';
|
||||
|
||||
const startBtn = document.getElementById('start-btn');
|
||||
const startMenu = document.getElementById('start-menu');
|
||||
const taskbar = document.getElementById('taskbar');
|
||||
const windowsContainer = document.getElementById('windows');
|
||||
const taskbarWindows = document.getElementById('taskbar-windows');
|
||||
const ctx = new ContextMenu();
|
||||
const windowCleanup = new Map();
|
||||
const username = getCookie('sAMAccountName');
|
||||
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) => {
|
||||
evt.stopPropagation(); // verhindert sofortiges Schließen
|
||||
startBtn.classList.toggle('active');
|
||||
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
|
||||
document.addEventListener('click', async (e) => {
|
||||
const target = e.target.closest('.start-item');
|
||||
@@ -31,6 +56,7 @@ document.addEventListener('click', async (e) => {
|
||||
const clickedButton = startBtn.contains(e.target);
|
||||
|
||||
if(!clickedInsideMenu && !clickedButton) {
|
||||
startBtn.classList.remove('active');
|
||||
startMenu.classList.add('hidden');
|
||||
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
|
||||
function sendUserEvent(pluginName, message, sendToParams, levelId = -1) {
|
||||
mainSocket.emit('event', {
|
||||
objectGuid: getCookie('ObjectGUID'),
|
||||
levelId: levelId,
|
||||
pluginName: pluginName,
|
||||
message: message.stack === undefined ? message : { message: message.message },
|
||||
sendToParams: sendToParams
|
||||
objectGuid: getCookie('ObjectGUID'),
|
||||
levelId: levelId,
|
||||
pluginName: pluginName,
|
||||
message: message.stack === undefined ? message : { message: message.message },
|
||||
sendToParams: sendToParams
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#tutorial-tooltip img {filter: invert(1);}
|
||||
|
||||
#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: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 { 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-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); }
|
||||
/* #taskbar .taskbar-item { background:var(--theme-taskbar-item-backcolor); } */
|
||||
|
||||
.taskbar-item { position: relative;}
|
||||
.taskbar-item::before { background: var(--theme-accent-active-color); }
|
||||
.taskbar-item { position: relative; }
|
||||
.taskbar-item::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: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\:90px { width:90px; }
|
||||
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; }
|
||||
|
||||
html, button { font-size: var(--fontSize); font-family: var(--fontFamily); }
|
||||
button.monolyth, button.bluebutton, button.greenbutton, button.yellowbutton, button.redbutton { display:inline-block; padding:8px 10px; margin:0.2rem 1.6rem; font-weight:600; text-align:center; text-decoration:none; color:rgb(255, 255, 255); border:none; border-radius:8px; box-shadow:0 4px 6px rgba(0,0,0,0.1); transition:all var(--times-transition-colors) ease; }
|
||||
/* button.monolyth, button.bluebutton, button.greenbutton, button.yellowbutton, button.redbutton { display:inline-block; padding:8px 10px; margin:0.2rem 1.6rem; font-weight:600; text-align:center; text-decoration:none; color:rgb(255, 255, 255); border:none; border-radius:8px; box-shadow:0 4px 6px rgba(0,0,0,0.1); transition:all var(--times-transition-colors) ease; } */
|
||||
button.monolyth, button.bluebutton, button.greenbutton, button.yellowbutton, button.redbutton { display:inline-block; padding:4px 8px; margin:0px 5px; font-weight:600; text-align:center; text-decoration:none; color:rgb(255, 255, 255); border:none; border-radius:8px; box-shadow:0 4px 6px rgba(0,0,0,0.1); transition:all var(--times-transition-colors) ease; }
|
||||
|
||||
button.monolyth { background-color:transparent; }
|
||||
button:not(:disabled).monolyth:hover { opacity:0.9; }
|
||||
button.bluebutton { color:var(--theme-button-blue-default-color); background:var(--theme-button-blue-default-backcolor); }
|
||||
@@ -35,14 +38,14 @@ button:not(:disabled).yellowbutton:hover { background:var(--theme-button-yellow-
|
||||
|
||||
|
||||
/* #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.row { overflow:hidden; display:flex; flex-direction:row; flex-wrap: wrap;}
|
||||
.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; }
|
||||
.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)); }
|
||||
/* #endregion */
|
||||
@@ -93,114 +96,16 @@ button:not(:disabled).yellowbutton:hover { background:var(--theme-button-yellow-
|
||||
<span class="cb-label">Ein / Aus</span>
|
||||
</label>
|
||||
*/
|
||||
.cb-switch {
|
||||
--w: var(--responsive-switch-width);
|
||||
--h: var(--responsive-switch-height);
|
||||
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 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 { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
|
||||
.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; }
|
||||
.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); }
|
||||
.cb-switch input:checked + .switch-track .switch-thumb { left: calc(var(--responsive-switch-width) - var(--responsive-switch-height) + 3px); }
|
||||
.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:disabled + .switch-track { background-color: dimgray; opacity: 0.6; }
|
||||
.cb-switch input:disabled + .switch-track .switch-thumb { background-color: rgb(54, 50, 50); }
|
||||
|
||||
.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 */
|
||||
|
||||
.cb-switch label { cursor: pointer; }
|
||||
|
||||
/* #region CheckBox */
|
||||
/*
|
||||
@@ -209,13 +114,12 @@ button:not(:disabled).yellowbutton:hover { background:var(--theme-button-yellow-
|
||||
<span class="cb-box" aria-hidden="true"></span>
|
||||
</label>
|
||||
*/
|
||||
.cb { display:inline-flex; align-items:center; gap:10px; user-select:none; transform:translateY(2px); }
|
||||
.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::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 { 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-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: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::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; }
|
||||
/* #endregion */
|
||||
|
||||
@@ -226,8 +130,8 @@ table .cb input:checked + .cb-box::after { transform:scale(1) translate(1px, 1px
|
||||
|
||||
|
||||
/* #region Tabs */
|
||||
.tabs { display: flex; margin-bottom: 10px; border-bottom-width: 2px; border-bottom-style: solid; }
|
||||
.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; }
|
||||
.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: 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; }
|
||||
.item { margin-bottom: 5px; }
|
||||
/* #endregion */
|
||||
@@ -235,7 +139,7 @@ table .cb input:checked + .cb-box::after { transform:scale(1) translate(1px, 1px
|
||||
|
||||
/* #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 { 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-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; }
|
||||
@@ -406,14 +310,23 @@ label { color:var(--muted); display:block; margin-bottom:6px; }
|
||||
border-bottom:1px solid #eee;
|
||||
}
|
||||
|
||||
button.removeButton {
|
||||
.removeButton {
|
||||
border:none;
|
||||
background:none;
|
||||
color:#d11a2a;
|
||||
background:#d11a2a;
|
||||
color:white;
|
||||
cursor:var(--theme-cursor-pointer) -16 16, pointer;
|
||||
/* font-size:14px; */
|
||||
padding: 0 4px;
|
||||
border-radius: 47%;
|
||||
transition: background var(--times-transition-colors) ease, color var(--times-transition-colors) ease;
|
||||
}
|
||||
|
||||
.removeButton:hover {
|
||||
background:#ec5d4d;
|
||||
color:white;
|
||||
}
|
||||
|
||||
|
||||
|
||||
input[type="file"] {
|
||||
display:none;
|
||||
}
|
||||
|
||||
@@ -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); }
|
||||
|
||||
#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-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.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.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; }
|
||||
|
||||
/* 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 > img { height: 24px; width: 24px; margin-right: 5px; }
|
||||
.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 > .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-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.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) }
|
||||
|
||||
|
||||
@@ -123,6 +122,7 @@ img.icon { width: auto; height:20px; object-fit: contain; filter: var(--theme-no
|
||||
#taskbar {
|
||||
padding: 0 6px;
|
||||
overflow: hidden;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#start-btn {
|
||||
|
||||
@@ -22,7 +22,7 @@ table thead { position:sticky; top:0; z-index:20; }
|
||||
|
||||
/* #endregion */
|
||||
/* echte Tabelle */
|
||||
table { width:calc(100%); border-spacing:0 5px; }
|
||||
table { width:calc(100%); border-spacing:0 2px; }
|
||||
table th, table td { min-width:100px; max-width:250px; overflow:hidden; white-space:nowrap; }
|
||||
|
||||
table tr.grouprow:hover { background: rgba(0,0,0,0.05);}
|
||||
@@ -35,6 +35,7 @@ thead, tbody { display:table-row-group; }
|
||||
|
||||
|
||||
table thead th { padding:5px; }
|
||||
/* table tbody td { padding:0 5px; } */
|
||||
/* table tbody td { padding:5px 0px 5px 20px; } */
|
||||
table tbody td:not(:first-child):not(:last-child), table thead th:not(:first-child):not(:last-child) { border-width:0; border-style:solid; }
|
||||
table tbody tr.grouprow { font-weight:700; }
|
||||
@@ -46,23 +47,25 @@ table .text-align\:left { text-align:left; }
|
||||
td { overflow:hidden; text-overflow:ellipsis; /* verhindert, dass Inhalt die Zelle sprengt */ }
|
||||
|
||||
.table-filter-container {
|
||||
border-bottom-width:8px;
|
||||
border-bottom-width:1px;
|
||||
border-bottom-style:solid;
|
||||
display:flex;
|
||||
justify-content:flex-start;
|
||||
flex-direction:column;
|
||||
flex-wrap:wrap;
|
||||
gap:10px;
|
||||
|
||||
gap:0;
|
||||
position:sticky;
|
||||
left:0px;
|
||||
top:0px;
|
||||
width: 100% !important;
|
||||
/* z-index:20; */
|
||||
padding:5px 10px;
|
||||
padding:0px;
|
||||
border-radius:var(--border-raduis) var(--border-raduis) 0 0;
|
||||
justify-content: flex-start;
|
||||
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 input, .table-filter-container select { padding:5px !important; }
|
||||
.table-filter-container .live-counter { position:static; text-align: left ; padding-left:10px; font-style: italic; }
|
||||
.table-filter-container input { padding:5px !important; ; margin:5px 0;}
|
||||
|
||||
|
||||
th.sort-asc::after {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
min-width: 200px;
|
||||
max-height: 50vh;
|
||||
width: max-content; /* 🔑 wächst mit Inhalt */
|
||||
max-width: 600px; /* aber capped */
|
||||
max-width: 500px; /* aber capped */
|
||||
background: var(--theme-taskbar-tray-backcolor);
|
||||
color: var(--theme-taskbar-tray-color);
|
||||
border-color: var(--theme-taskbar-tray-border-color);
|
||||
@@ -14,7 +14,7 @@
|
||||
overflow-y: auto; /* vertikal scrollen */
|
||||
overflow-x: hidden; /* horizontal verhindern */
|
||||
box-shadow: 0 8px 20px rgba(0,0,0,0.4);
|
||||
padding: 10px 10px 6px 10px;
|
||||
padding: 6px 6px 0px 6px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateY(10px);
|
||||
@@ -28,8 +28,8 @@
|
||||
.bubble-item {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, auto) 250px;
|
||||
gap: 10px;
|
||||
padding: 6px 8px;
|
||||
gap: 5px;
|
||||
padding: 3px 6px;
|
||||
border-radius: 6px;
|
||||
/* cursor: pointer; */
|
||||
align-items: center;
|
||||
@@ -55,7 +55,9 @@
|
||||
|
||||
/* hover Effekt */
|
||||
.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);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
{{#each items}}
|
||||
{{#ifSingle this.menu.items}}
|
||||
{{#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}}
|
||||
<img src="{{#if ../this.pluginPath}}/{{../this.name}}{{/if}}/images/{{this.icon}}" class="start-icon" />
|
||||
{{else}}
|
||||
@@ -47,7 +47,7 @@
|
||||
{{else}}
|
||||
<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;"/>
|
||||
<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}} --}}
|
||||
|
||||
<ul class="submenu">
|
||||
@@ -82,7 +82,7 @@
|
||||
|
||||
<!-- Taskbar -->
|
||||
<div id="taskbar">
|
||||
<button id="start-btn">☰</button>
|
||||
<button class="" id="start-btn">☰</button>
|
||||
<div id="taskbar-windows"></div>
|
||||
<button style="margin-right:0;" class="monolyth notify-button pulse">
|
||||
<img class="icon" src="/images/notifybubble.png">
|
||||
@@ -96,7 +96,7 @@
|
||||
<script src="javascript/contextMenu.js"></script>
|
||||
<script src="javascript/tableFilter.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/os.js"></script>
|
||||
<script>
|
||||
@@ -116,7 +116,10 @@
|
||||
const trayNotifyButton = document.querySelector('#taskbar > .notify-button')
|
||||
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 => {
|
||||
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>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
|
||||
<script>
|
||||
fetch('/api/getConfig', { method: 'POST' })
|
||||
fetch('/api/config/get', { method: 'POST' })
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
const tree = createJsonTree({
|
||||
@@ -24,7 +24,7 @@
|
||||
expandInitially: true,
|
||||
onSave: json => {
|
||||
console.log(JSON.stringify(tree.getChanges()));
|
||||
fetch('/config', {
|
||||
fetch('/api/config/save', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
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(json => {
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
fetch('/api/getStyles', { method: 'POST' })
|
||||
fetch('/api/styles/get', { method: 'POST' })
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
const tree = createJsonTree({
|
||||
@@ -18,7 +18,7 @@
|
||||
data: json,
|
||||
expandInitially: true,
|
||||
onSave: json => {
|
||||
fetch('/style', {
|
||||
fetch('/api/styles/save', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(tree.getChanges(), null, 2)
|
||||
|
||||
@@ -1,14 +1,185 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<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>
|
||||
<body>
|
||||
<div class="container static" style="height: 100vh;">
|
||||
<div class="card static" style="overflow-y:auto;flex: 1 1 auto;" >
|
||||
<div class="container static" style="height:100vh;">
|
||||
<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 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>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
reloadPluginScript('/javascript/rbacAPI.js');
|
||||
|
||||
</script>
|
||||
</html>
|
||||
25
server.js
25
server.js
@@ -57,7 +57,8 @@ const server = https.createServer(httpsOptions, app);
|
||||
|
||||
|
||||
const io = new Server(server, {
|
||||
pingTimeout: 60000,
|
||||
// pingInterval: 1000, // alle 25s Ping senden
|
||||
// pingTimeout: 200, // 20s ohne Pong => disconnect
|
||||
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('authenticationRolesModel', require(`@models/authenticationRolesModel`)(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('objectSourceModel', require(`@models/objectSourceModel`)(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('rolePermissionsModel', require(`@models/rolePermissionsModel`)(service.get('sqlManager').getInstance('main')));
|
||||
|
||||
service.set('rbacManager', new RBACManager(databaseModel));
|
||||
service.set('authenticationManager', new AuthenticationManager(databaseModel.get('authentication'), runtimeFile.configuration.live.integration.token.secret));
|
||||
databaseModel.set('authenticationOverviewView', require(`@models/authenticationOverviewView`)(service.get('sqlManager').getInstance('main')));
|
||||
databaseModel.set('groupOverviewView', require(`@models/groupOverviewView`)(service.get('sqlManager').getInstance('main')));
|
||||
databaseModel.set('roleOverviewView', require(`@models/roleOverviewView`)(service.get('sqlManager').getInstance('main')));
|
||||
databaseModel.set('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('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!
|
||||
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
|
||||
|
||||
|
||||
//#region Service-Registration/Middleware/Utils/Helpers
|
||||
require(`${localPath.root}/utils.js`);
|
||||
let helpers = service.get('fileSystemManager').loadAllFiles(`${localPath.public}/helpers`, '.js');
|
||||
exports.helpers = helpers;
|
||||
|
||||
// app.use(service.get('vaultifyManager').createMiddleware());
|
||||
app.use(service.get('rbacManager').requirePermissionMiddleware());
|
||||
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
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.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) {
|
||||
if (!request.secure) {
|
||||
@@ -200,14 +208,13 @@ const server = https.createServer(httpsOptions, app);
|
||||
});
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Implement routes
|
||||
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/adminRoutes.js`).route(app, service); // #3 - token security always enabled
|
||||
//#endregion
|
||||
|
||||
app.use(service.get('rbacManager').authenticate());
|
||||
|
||||
|
||||
//#region Implements sockets
|
||||
require(`${localPath.source}/sockets/mainSocket.js`)(
|
||||
|
||||
@@ -12,6 +12,15 @@ module.exports = (sequelize) => {
|
||||
}
|
||||
}, {
|
||||
tableName: 'AuthenticationGroups',
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: [
|
||||
'Authentication_ObjectGUID',
|
||||
'Group_ObjectGUID'
|
||||
]
|
||||
}
|
||||
],
|
||||
timestamps: false
|
||||
});
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ module.exports = (sequelize) => {
|
||||
const Authentication = sequelize.define('Authentication', {
|
||||
ObjectGUID: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
allowNull: false,
|
||||
},
|
||||
@@ -41,7 +42,7 @@ module.exports = (sequelize) => {
|
||||
allowNull: true,
|
||||
},
|
||||
userAccountControl_ID: {
|
||||
type: DataTypes.STRING,
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
},
|
||||
telephoneNumber: {
|
||||
@@ -72,6 +73,10 @@ module.exports = (sequelize) => {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: true,
|
||||
},
|
||||
ObjectSource_ID: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
}
|
||||
}, {
|
||||
tableName: 'Authentication', // Tabellenname in der Datenbank
|
||||
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', {
|
||||
ObjectGUID: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
Name: DataTypes.STRING(255),
|
||||
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": [
|
||||
{
|
||||
"scope": "SYSTEM",
|
||||
"resource": "ALL",
|
||||
"action": "Administration"
|
||||
}
|
||||
],
|
||||
"authorized": true
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Configs",
|
||||
@@ -29,34 +29,29 @@
|
||||
"permissions": [
|
||||
{
|
||||
"scope": "SYSTEM",
|
||||
"resource": "ALL",
|
||||
"action": "Administration"
|
||||
}
|
||||
],
|
||||
"authorized": true
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "RBAC",
|
||||
"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": {
|
||||
"width": "800px",
|
||||
"height": "600px"
|
||||
},
|
||||
"icon": "app.png",
|
||||
"icon": "shield.png",
|
||||
"permissions": [
|
||||
{
|
||||
"scope": "SYSTEM",
|
||||
"resource": "ALL",
|
||||
"action": "Administration"
|
||||
}
|
||||
],
|
||||
"authorized": true
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"onlyAdministration": false,
|
||||
"defaultSize": {
|
||||
"width": "800px",
|
||||
"height": "600px"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -70,24 +65,19 @@
|
||||
"label": "EventLog",
|
||||
"view": "eventlog",
|
||||
"defaultSize": {
|
||||
"width": "1200px",
|
||||
"height": "1200px"
|
||||
"width": "800px",
|
||||
"height": "600px"
|
||||
},
|
||||
"icon": "eventlog.ico",
|
||||
"permissions": [
|
||||
{
|
||||
"scope": "SYSTEM",
|
||||
"resource": "ALL",
|
||||
"action": "Administration"
|
||||
}
|
||||
],
|
||||
"authorized": true
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"onlyAdministration": false,
|
||||
"defaultSize": {
|
||||
"width": "1200px",
|
||||
"height": "1200px"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -108,17 +98,12 @@
|
||||
"permissions": [
|
||||
{
|
||||
"scope": "SYSTEM",
|
||||
"resource": "ALL",
|
||||
"action": "Administration"
|
||||
}
|
||||
],
|
||||
"authorized": true
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"onlyAdministration": false,
|
||||
"defaultSize": {
|
||||
"width": "900px",
|
||||
"height": "800px"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -139,17 +124,12 @@
|
||||
"permissions": [
|
||||
{
|
||||
"scope": "SYSTEM",
|
||||
"resource": "ALL",
|
||||
"action": "Administration"
|
||||
}
|
||||
],
|
||||
"authorized": true
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"onlyAdministration": false,
|
||||
"defaultSize": {
|
||||
"width": "900px",
|
||||
"height": "500px"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -166,21 +146,16 @@
|
||||
"width": "460px",
|
||||
"height": "515px"
|
||||
},
|
||||
"icon": "app.png",
|
||||
"icon": "settings.png",
|
||||
"permissions": [
|
||||
{
|
||||
"scope": "SYSTEM",
|
||||
"resource": "ALL",
|
||||
"action": "Default_Access"
|
||||
}
|
||||
],
|
||||
"authorized": true
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"onlyAdministration": false,
|
||||
"defaultSize": {
|
||||
"width": "460px",
|
||||
"height": "515px"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -197,17 +172,12 @@
|
||||
"permissions": [
|
||||
{
|
||||
"scope": "SYSTEM",
|
||||
"resource": "ALL",
|
||||
"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
|
||||
},
|
||||
|
||||
License_ID: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
Customer_ID: {
|
||||
type: DataTypes.UUIDV4,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
Feature: {
|
||||
type: DataTypes.STRING(128),
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
Payload: {
|
||||
type: DataTypes.TEXT, // NVARCHAR(MAX)
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
Signature: {
|
||||
type: DataTypes.TEXT,
|
||||
type: DataTypes.STRING(512),
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
Active: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true
|
||||
EncryptedPayload: {
|
||||
type: DataTypes.BLOB('long'),
|
||||
allowNull: false
|
||||
},
|
||||
|
||||
ExpiresAt: {
|
||||
@@ -40,12 +35,20 @@ module.exports = (sequelize) => {
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
CreatedAt: {
|
||||
type: DataTypes.DATE,
|
||||
defaultValue: DataTypes.NOW
|
||||
Status_ID: {
|
||||
type: DataTypes.TINYINT,
|
||||
allowNull: false,
|
||||
validate: {
|
||||
isIn: [[0, 1, 2, 3, 4]]
|
||||
}
|
||||
},
|
||||
|
||||
UpdatedAt: {
|
||||
LastVerifiedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true
|
||||
},
|
||||
|
||||
CreateDate: {
|
||||
type: DataTypes.DATE,
|
||||
defaultValue: DataTypes.NOW
|
||||
}
|
||||
|
||||
@@ -2,22 +2,25 @@ const { exec } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { localPath, cache, runtimeFile } = require('@root/globalize.js');
|
||||
|
||||
|
||||
const configurationFile = path.join(require('@root/server.js').path.source, 'models', 'configuration.json');
|
||||
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');
|
||||
const { databaseModel } = require('@root/server.js');
|
||||
const { raw } = require('body-parser');
|
||||
let rbacUsers, rbacGroups, rbacRoles, rbacPermissions = [];
|
||||
|
||||
|
||||
module.exports = {
|
||||
route(app, service) {
|
||||
// JSON configuration abrufen
|
||||
app.post('/api/getConfig', (req, res) => {
|
||||
app.post('/api/config/get', (req, res) => {
|
||||
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
|
||||
app.post('/config', (req, res) => {
|
||||
app.post('/api/config/save', (req, res) => {
|
||||
try {
|
||||
runtimeFile.configuration.save(req.body);
|
||||
res.status(200).send({ status: 'ok' });
|
||||
@@ -27,7 +30,7 @@ module.exports = {
|
||||
});
|
||||
|
||||
// JSON stylesheet abrufen
|
||||
app.post('/api/getStyles', (req, res) => {
|
||||
app.post('/api/styles/save', (req, res) => {
|
||||
try {
|
||||
runtimeFile.stylesheet.save(req.body);
|
||||
res.status(200).send({ status: 'ok' });
|
||||
@@ -35,30 +38,12 @@ module.exports = {
|
||||
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
|
||||
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 });
|
||||
});
|
||||
|
||||
// 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) => {
|
||||
service.get('eventManager').clear();
|
||||
res.status(200).send({ status: 'ok' })
|
||||
@@ -103,11 +88,334 @@ module.exports = {
|
||||
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);
|
||||
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) => {
|
||||
try {
|
||||
const plugins = await service.get('pluginManager').getStatus();
|
||||
@@ -119,7 +427,7 @@ module.exports = {
|
||||
|
||||
app.post('/api/plugins/integrated', async (req, res) => {
|
||||
try {
|
||||
res.status(200).json(cached.startMenuItems.live);
|
||||
res.status(200).json(runtimeFile.startMenuItems.live);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
|
||||
@@ -21,12 +21,11 @@ module.exports = {
|
||||
|
||||
let context = fs.existsSync(pluginPath)
|
||||
? service.get('fileSystemManager').loadJSON(pluginPath)
|
||||
: runtimeFile.startMenuItems.live.find(item => item.name == name);
|
||||
: cache.startMenuItems.find(item => item.name == name);
|
||||
|
||||
context.defaultSize =
|
||||
context.menu.items.find(item => item.label == viewLabel)?.defaultSize ||
|
||||
{ width: 800, height: 600 };
|
||||
|
||||
delete context.config;
|
||||
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 = {
|
||||
@@ -71,7 +71,7 @@ module.exports = {
|
||||
|
||||
res.clearCookie('sAMAccountName');
|
||||
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);
|
||||
// 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' };
|
||||
}
|
||||
|
||||
if(!user.active) {
|
||||
return { token: null, levelId: 2, message: 'Benutzer nicht aktiv' };
|
||||
}
|
||||
|
||||
const token = jwt.sign(
|
||||
{
|
||||
ObjectGUID: user.ObjectGUID,
|
||||
@@ -67,12 +71,26 @@ class AuthenticationManager {
|
||||
);
|
||||
|
||||
user.refreshtoken = token;
|
||||
user.online = true;
|
||||
await this.setOnline(sAMAccountName);
|
||||
await user.save();
|
||||
|
||||
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
|
||||
// =========================================================
|
||||
@@ -85,8 +103,8 @@ class AuthenticationManager {
|
||||
}
|
||||
|
||||
user.refreshtoken = null;
|
||||
user.online = false;
|
||||
await user.save();
|
||||
await this.setOffline();
|
||||
|
||||
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 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({
|
||||
where: {},
|
||||
truncate: true,
|
||||
|
||||
@@ -313,8 +313,8 @@ class PluginManager {
|
||||
permissions: [
|
||||
{
|
||||
scope: name, // Plugin Scope (default = plugin name)
|
||||
action: "Default_Access",
|
||||
resource: "MenuItem"
|
||||
resource: "Plugin",
|
||||
action: "Default_Access"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
// rbac/RbacService.js
|
||||
const jwt = require('jsonwebtoken');
|
||||
const sequelize = require('sequelize');
|
||||
|
||||
class RBACManager {
|
||||
constructor(databaseModel) {
|
||||
constructor(databaseModel, SECRET_KEY, service) {
|
||||
this.db = databaseModel;
|
||||
this.SECRET_KEY = SECRET_KEY;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
async resolvePermissions(objectGuid) {
|
||||
@@ -128,8 +131,6 @@ class RBACManager {
|
||||
return next(); // oder 401 wenn du streng sein willst
|
||||
}
|
||||
|
||||
const rbac = this.rbac;
|
||||
|
||||
const permissions = req.user.permissions || [];
|
||||
const isSuperAdmin = req.user.isSuperAdmin || false;
|
||||
|
||||
@@ -137,16 +138,12 @@ class RBACManager {
|
||||
permissions,
|
||||
isSuperAdmin,
|
||||
hasPermission: (required) =>
|
||||
rbac.hasPermission(permissions, required, isSuperAdmin)
|
||||
this.hasPermission(permissions, required, isSuperAdmin)
|
||||
};
|
||||
|
||||
return next();
|
||||
|
||||
next();
|
||||
|
||||
} catch (err) {
|
||||
console.error('[RBAC MIDDLEWARE ERROR]', err);
|
||||
return res.status(500).json({ message: 'RBAC Fehler' });
|
||||
return res.status(500).json('[RBAC MIDDLEWARE ERROR]', err);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -168,7 +165,11 @@ class RBACManager {
|
||||
|
||||
const publicRoutes = [
|
||||
'/login',
|
||||
'/public'
|
||||
'/public',
|
||||
'/css',
|
||||
'/js',
|
||||
'/images',
|
||||
'/favicon.ico'
|
||||
];
|
||||
|
||||
const isPublicRoute = publicRoutes.some(route =>
|
||||
@@ -189,8 +190,9 @@ class RBACManager {
|
||||
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) {
|
||||
return res.redirect('/login');
|
||||
}
|
||||
@@ -216,7 +218,7 @@ class RBACManager {
|
||||
permissions: normalized,
|
||||
isSuperAdmin
|
||||
};
|
||||
|
||||
|
||||
next();
|
||||
|
||||
} catch (err) {
|
||||
@@ -234,6 +236,513 @@ class RBACManager {
|
||||
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;
|
||||
@@ -56,7 +56,7 @@ module.exports = {
|
||||
...{ name: payload.name, view: payload.view, viewLabel: payload.viewLabel},
|
||||
...data,
|
||||
};
|
||||
console.log(templateData)
|
||||
// console.log(templateData)
|
||||
// Zuerst Plugin-View rendern
|
||||
app.render(view, templateData, (err, contentHtml) => {
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@ class SocketManager {
|
||||
userName: sAMAccountName
|
||||
});
|
||||
|
||||
// console.log(`${sAMAccountName} [${objectGuid}] connected to ${namespace}`);
|
||||
console.log(`${sAMAccountName} [${objectGuid}] connected to ${namespace}`);
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
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) {
|
||||
|
||||
try {
|
||||
const payload = this.parsePayload(record.Payload);
|
||||
|
||||
const data = {
|
||||
Customer_ID: record.Customer_ID,
|
||||
Feature: record.Feature,
|
||||
Payload: this.parsePayload(record.Payload),
|
||||
ExpiresAt: record.ExpiresAt
|
||||
Payload: payload,
|
||||
ExpiresAt: record.ExpiresAt ?? null
|
||||
};
|
||||
|
||||
const verifier = crypto.createVerify('RSA-SHA256');
|
||||
|
||||
verifier.update(JSON.stringify(data));
|
||||
verifier.end();
|
||||
|
||||
return verifier.verify(
|
||||
const isValid = verifier.verify(
|
||||
this.publicKey,
|
||||
record.Signature,
|
||||
'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 {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const path = require('path');
|
||||
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) => {
|
||||
@@ -12,9 +14,26 @@ module.exports = (app, socketManager, namespace, pluginManager, authenticationMo
|
||||
// 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 => {
|
||||
|
||||
20
utils.js
20
utils.js
@@ -21,8 +21,7 @@ module.exports = startMenuItems = async function (app, objectGuid) {
|
||||
// =========================
|
||||
// Load menu sources
|
||||
// =========================
|
||||
const integratedStartmenuItems = runtimeFile.startMenuItems.live;
|
||||
|
||||
const integratedStartmenuItems = safeClone(runtimeFile.startMenuItems.live);
|
||||
const plugins = service
|
||||
.get('pluginManager')
|
||||
.getStatus()
|
||||
@@ -64,7 +63,6 @@ module.exports = startMenuItems = async function (app, objectGuid) {
|
||||
|
||||
plugin.menu.items = (plugin.menu.items || []).map(item => {
|
||||
|
||||
const resource = item.label;
|
||||
const requiredPermissions = item.permissions || [];
|
||||
|
||||
const debugTrace = [];
|
||||
@@ -78,25 +76,25 @@ module.exports = startMenuItems = async function (app, objectGuid) {
|
||||
const scopeMatch =
|
||||
userPerm.scope === required.scope;
|
||||
|
||||
const resourceMatch =
|
||||
!userPerm.resource ||
|
||||
userPerm.resource === 'ALL' ||
|
||||
userPerm.resource === required.resource;
|
||||
|
||||
const actionMatch =
|
||||
userPerm.action === 'ALL' ||
|
||||
userPerm.action === required.action ||
|
||||
required.action === 'ALL';
|
||||
|
||||
const resourceMatch =
|
||||
!userPerm.resource ||
|
||||
userPerm.resource === 'ALL' ||
|
||||
userPerm.resource === resource;
|
||||
|
||||
const result = scopeMatch && actionMatch && resourceMatch;
|
||||
const result = scopeMatch && resourceMatch && actionMatch;
|
||||
|
||||
if (debug) {
|
||||
debugTrace.push({
|
||||
userPerm,
|
||||
required,
|
||||
scopeMatch,
|
||||
actionMatch,
|
||||
resourceMatch,
|
||||
actionMatch,
|
||||
result
|
||||
});
|
||||
}
|
||||
@@ -106,7 +104,7 @@ module.exports = startMenuItems = async function (app, objectGuid) {
|
||||
});
|
||||
|
||||
if (debug) {
|
||||
log(`\nMENU ITEM: ${item.label} - AUTHORIZED:', ${authorized} - TRACE:', ${debugTrace}`);
|
||||
log(`\nMENU ITEM: ${item.label} - AUTHORIZED:', ${authorized} - TRACE:', ${JSON.stringify(debugTrace)}`);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user