From c848633a1f96ceb67924e3aea4b8128035453094 Mon Sep 17 00:00:00 2001 From: "manuel.sowada" Date: Thu, 7 May 2026 15:28:26 +0200 Subject: [PATCH] styles bugfix --- package-lock.json | 106 -------- public/styles/default.css | 11 +- public/styles/os.css | 1 - public/styles/userNotification.css | 12 +- server.js | 4 +- src/routes/adminRoutes.js | 1 + src/services/activeDirectoryManager.js | 330 +++++++++++++++++++++++++ src/services/rbacManager.js | 28 ++- 8 files changed, 371 insertions(+), 122 deletions(-) diff --git a/package-lock.json b/package-lock.json index a924858..5e4f950 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2102,23 +2102,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 +2620,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 +2628,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 +2740,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 +3412,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 +3449,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 +3528,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 +3657,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, diff --git a/public/styles/default.css b/public/styles/default.css index ceb4a96..f3a42ce 100644 --- a/public/styles/default.css +++ b/public/styles/default.css @@ -212,13 +212,12 @@ button:not(:disabled).yellowbutton:hover { background:var(--theme-button-yellow- */ -.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 */ diff --git a/public/styles/os.css b/public/styles/os.css index f4bba4d..d799b65 100644 --- a/public/styles/os.css +++ b/public/styles/os.css @@ -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) } diff --git a/public/styles/userNotification.css b/public/styles/userNotification.css index 9a6ed0f..13d04e3 100644 --- a/public/styles/userNotification.css +++ b/public/styles/userNotification.css @@ -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); } diff --git a/server.js b/server.js index 8bba14f..71e7ed0 100644 --- a/server.js +++ b/server.js @@ -121,10 +121,10 @@ const server = https.createServer(httpsOptions, app); databaseModel.set('permissionOverviewView', require(`@models/permissionOverviewView`)(service.get('sqlManager').getInstance('main'))); - service.set('rbacManager', new RBACManager(databaseModel, runtimeFile.configuration.live.integration.token.secret)); + 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)); - service.set('activeDirectoryManager', new ActiveDirectory(runtimeFile.configuration.live.integration.activedirectory)) // everytime last created service! service.set('pluginManager', new PluginManager(app, databaseModel.get('plugin'), localPath.plugins, runtimeFile.configuration.live.plugin.chown, service)); diff --git a/src/routes/adminRoutes.js b/src/routes/adminRoutes.js index cf06be3..d312e1c 100644 --- a/src/routes/adminRoutes.js +++ b/src/routes/adminRoutes.js @@ -102,6 +102,7 @@ module.exports = { app.post('/api/rbac/auth/get', async (req, res) => { try { + console.log(await service.get('rbacManager').syncAuthByActiveDirectory()); rbacUsers = await service.get('rbacManager').getAuth(); res.json(rbacUsers); } catch (err) { diff --git a/src/services/activeDirectoryManager.js b/src/services/activeDirectoryManager.js index 3229adf..958dc9c 100644 --- a/src/services/activeDirectoryManager.js +++ b/src/services/activeDirectoryManager.js @@ -56,6 +56,30 @@ class ActiveDirectoryManager { }); } + + async getAllUsers(attributes = this.userAttributes) { + const options = { + baseDN: this.ad.baseDN, + filter: '(&(objectClass=user)(objectCategory=person))', + attributes: ['ObjectGUID' + ,'sAMAccountName' + ,'mail' + ,'givenName' + ,'sn' + ,'employeeID' + ,'title' + ,'department' + ,'streetAddress' + ,'telephoneNumber' + ,'physicalDeliveryOfficeName' + ,'distinguishedName'] + }; + const result = await this.ldapSearch(options); + console.log(result) + return result.users || []; + } + + async getUserDN(username) { const user = await this.getUser(username); return user?.dn || null; @@ -256,3 +280,309 @@ class ActiveDirectoryManager { } module.exports = ActiveDirectoryManager; + + + + + +// const ldap = require('ldapjs'); + +// class ActiveDirectoryManager { +// constructor({ +// url, +// baseDN, +// username, +// password, +// userAttributes = [], +// groupAttributes = [], +// computerAttributes = [] +// }) { +// this.url = url; +// this.baseDN = baseDN; +// this.username = username; +// this.password = password; + +// this.userAttributes = userAttributes; +// this.groupAttributes = groupAttributes; +// this.computerAttributes = computerAttributes; + +// this.client = ldap.createClient({ +// url: this.url, +// reconnect: true, +// timeout: 10000, +// connectTimeout: 10000 +// }); +// } + +// /** +// * ----------------------------------------------------- +// * CONNECTION HANDLING +// * ----------------------------------------------------- +// */ +// async bind() { +// return new Promise((resolve, reject) => { +// this.client.bind(this.username, this.password, (err) => { +// if (err) return reject(err); +// resolve(); +// }); +// }); +// } + +// async unbind() { +// return new Promise((resolve, reject) => { +// this.client.unbind(err => { +// if (err) return reject(err); +// resolve(); +// }); +// }); +// } + +// async withConnection(fn) { +// try { +// await this.bind(); +// return await fn(); +// } finally { +// await this.unbind(); +// } +// } + +// /** +// * ----------------------------------------------------- +// * INTERNAL HELPERS +// * ----------------------------------------------------- +// */ + +// escape(value) { +// return String(value).replace(/[*()\\]/g, '\\$&'); +// } + +// async ldapSearch({ baseDN = this.baseDN, filter, attributes = [] }) { +// const opts = { +// filter, +// scope: 'sub', +// attributes, +// paged: true +// }; + +// return new Promise((resolve, reject) => { +// const results = []; + +// this.client.search(baseDN, opts, (err, res) => { +// if (err) return reject(err); + +// res.on('searchEntry', (entry) => { +// results.push(entry.object); +// }); + +// res.on('error', (err) => reject(err)); +// res.on('end', () => resolve(results)); +// }); +// }); +// } + +// /** +// * ----------------------------------------------------- +// * USER FUNCTIONS +// * ----------------------------------------------------- +// */ + +// async getUser(username, attributes = this.userAttributes) { +// const safe = this.escape(username); + +// const filter = `(&(objectCategory=person)(objectClass=user)(|(sAMAccountName=${safe})(mail=${safe})(cn=${safe}))))`; + +// const res = await this.ldapSearch({ filter, attributes }); +// return res[0] || null; +// } + +// async getUserDN(username) { +// const user = await this.getUser(username); +// return user?.distinguishedName || null; +// } + +// async findUsers(query, attributes = this.userAttributes) { +// const safe = this.escape(query); + +// const filter = `(&(objectCategory=person)(objectClass=user)(|(cn=${safe})(sAMAccountName=${safe})(mail=${safe})(displayName=${safe})))`; + +// return await this.ldapSearch({ filter, attributes }); +// } + +// async getAllUsers(attributes = this.userAttributes) { +// const filter = '(&(objectCategory=person)(objectClass=user))'; + +// return await this.ldapSearch({ filter, attributes }); +// } + +// /** +// * ----------------------------------------------------- +// * GROUP FUNCTIONS +// * ----------------------------------------------------- +// */ + +// async getGroup(groupName, attributes = this.groupAttributes) { +// const safe = this.escape(groupName); + +// const filter = `(&(objectClass=group)(cn=${safe}))`; + +// const res = await this.ldapSearch({ filter, attributes }); +// return res[0] || null; +// } + +// async findGroups(query, attributes = this.groupAttributes) { +// const safe = this.escape(query); + +// const filter = `(&(objectClass=group)(cn=${safe}))`; + +// return await this.ldapSearch({ filter, attributes }); +// } + +// async getAllGroups(attributes = this.groupAttributes) { +// const filter = '(objectClass=group)'; + +// return await this.ldapSearch({ filter, attributes }); +// } + +// /** +// * ----------------------------------------------------- +// * COMPUTER / OU FUNCTIONS +// * ----------------------------------------------------- +// */ + +// async getComputer(name, attributes = this.computerAttributes) { +// const safe = this.escape(name); + +// const filter = `(&(objectClass=computer)(|(cn=${safe})(dNSHostName=${safe})))`; + +// const res = await this.ldapSearch({ filter, attributes }); +// return res[0] || null; +// } + +// async getComputers(attributes = this.computerAttributes) { +// const filter = '(objectClass=computer)'; + +// return await this.ldapSearch({ filter, attributes }); +// } + +// async getComputersFromOU(ouDn, attributes = this.computerAttributes) { +// const filter = '(objectClass=computer)'; + +// return await this.ldapSearch({ +// baseDN: ouDn, +// filter, +// attributes +// }); +// } + +// async findComputers(query, attributes = this.computerAttributes) { +// const safe = this.escape(query); + +// const filter = `(&(objectClass=computer)(|(cn=${safe})(dNSHostName=${safe})))`; + +// return await this.ldapSearch({ filter, attributes }); +// } + +// /** +// * ----------------------------------------------------- +// * GROUP MEMBERSHIP +// * ----------------------------------------------------- +// */ + +// async isUserMemberOfDirect(username, groupName) { +// const user = await this.getUser(username, ['distinguishedName']); +// if (!user) return false; + +// const userDN = user.distinguishedName; +// const safeGroup = this.escape(groupName); + +// const filter = `(&(objectClass=group)(cn=${safeGroup})(member=${userDN}))`; + +// const res = await this.ldapSearch({ filter, attributes: ['cn'] }); +// return res.length > 0; +// } + +// async isUserMemberOfRecursive(username, groupName, visited = new Set()) { +// const key = groupName.toLowerCase(); +// if (visited.has(key)) return false; +// visited.add(key); + +// const direct = await this.isUserMemberOfDirect(username, groupName); +// if (direct) return true; + +// const group = await this.getGroup(groupName, ['member']); +// if (!group || !group.member) return false; + +// const members = Array.isArray(group.member) ? group.member : [group.member]; + +// for (const dn of members) { +// const match = dn.match(/CN=([^,]+)/i); +// if (!match) continue; + +// const subGroup = match[1]; +// const found = await this.isUserMemberOfRecursive(username, subGroup, visited); +// if (found) return true; +// } + +// return false; +// } + +// async getGroupSubgroups(groupName, visited = new Set()) { +// const key = groupName.toLowerCase(); +// if (visited.has(key)) return []; + +// visited.add(key); + +// const group = await this.getGroup(groupName, ['member']); +// if (!group || !group.member) return []; + +// const members = Array.isArray(group.member) ? group.member : [group.member]; + +// const results = []; + +// for (const dn of members) { +// const match = dn.match(/CN=([^,]+)/i); +// if (!match) continue; + +// const subGroupName = match[1]; +// const sub = await this.getGroup(subGroupName).catch(() => null); +// if (!sub) continue; + +// results.push(sub); +// results.push(...await this.getGroupSubgroups(subGroupName, visited)); +// } + +// return results; +// } + +// async getGroupRecursive(groupName, visited = new Set()) { +// const key = groupName.toLowerCase(); +// if (visited.has(key)) return null; + +// visited.add(key); + +// const group = await this.getGroup(groupName); +// if (!group) return null; + +// const result = { +// ...group, +// subgroups: [] +// }; + +// if (!group.member) return result; + +// const members = Array.isArray(group.member) ? group.member : [group.member]; + +// for (const dn of members) { +// const match = dn.match(/CN=([^,]+)/i); +// if (!match) continue; + +// const subGroupName = match[1]; +// const subTree = await this.getGroupRecursive(subGroupName, visited); +// if (subTree) result.subgroups.push(subTree); +// } + +// return result; +// } +// } + +// module.exports = ActiveDirectoryManager; diff --git a/src/services/rbacManager.js b/src/services/rbacManager.js index 833810e..db3b696 100644 --- a/src/services/rbacManager.js +++ b/src/services/rbacManager.js @@ -1,9 +1,11 @@ const jwt = require('jsonwebtoken'); +const sequelize = require('sequelize'); class RBACManager { - constructor(databaseModel, SECRET_KEY) { + constructor(databaseModel, SECRET_KEY, service) { this.db = databaseModel; this.SECRET_KEY = SECRET_KEY; + this.service = service; } async resolvePermissions(objectGuid) { @@ -246,6 +248,29 @@ async getAuth() { return await Auth.findAll({ raw: true }); } + +async syncAuthByActiveDirectory() { + const auth = await this.db.get('authentication'); + const all = await this.service.get('activeDirectoryManager').getAllUsers(); + + await Promise.all( + all.map(async (user) => { + user = {...user, userAccountControl_ID: user.userAccountControl}; + const [record, created] = await auth.findOrCreate({ + where: { dn: user.dn }, + defaults: user + }); + + if (!created) { + await record.update(user); + } + }) + ); + + // return all; +} + + async createAuth(data) { const Auth = this.db.get('authentication'); @@ -429,7 +454,6 @@ async getPermission() { return await permission.findAll({ raw: true }) || []; } - async createPermission(data) { const Permission = this.db.get('permissionModel');