Node-RED kann nun nur von einer Liste von Benutzern benutzt werden.
Build and Publish Site / docker (push) Successful in 16s

Diese Liste in docker_compose.yml unter ALLOWED_USERS abgelegt.

Arbeit an der Dokumentation.
This commit is contained in:
2026-07-02 15:08:08 +02:00
parent 926c06b0b0
commit 9f7030ab35
8 changed files with 221 additions and 18 deletions
+1
View File
@@ -13,3 +13,4 @@ site/
*.backup
logic/node-red-data/size_by_ownergroup_and_number_of_copies_*
*.no_use
logic/node-red-data/.sessions.json
+1
View File
@@ -15,6 +15,7 @@ services:
- JSON_CACHE_WITH_PATH=/data/results/json_cache.json
- NON_SPECIFIED_GROUPS=/data/non_specified_groups.csv
- ERROR_LOGFILE=/data/logs/get_new_groupinfo_error.log
- ALLOWED_USERS=huesser,kallmeier_m,bumbak_b,rebmann_m,bugmann_r,scheffler,fiehn_m
- AD_SERVER=d.psi.ch
- AD_SEARCH_BASE=DC=d,DC=psi,DC=ch
- AD_USER=${AD_USER}
+16 -7
View File
@@ -4,11 +4,20 @@ Die Applikation teilt sich in eine Datenerfassungs- und eine Visualisierungsschi
```mermaid
graph TD
User([PSI User]) -->|HTTPS:443| Nginx[Zentraler NGINX Proxy]
Nginx -->|Port 8501| Streamlit[Analytics App: Streamlit]
Nginx -->|Port 1880 /logic| NodeRed[Logic App: Node-RED]
NodeRed -->|LDAP:389| AD[PSI Active Directory]
NodeRed -->|Cron Job| Script[Python-Skripte /opt/python-packages]
Script -->|Daten| Shared[(shared_data / json_cache.json)]
Streamlit -->|Liest| Shared
subgraph "Datenerfassung (Backend)"
AD[PSI Active Directory] -->|Liest a-/p-Gruppen Infos| NodeRed[Logic App: Node-RED]
Metabase[metabase.psi.ch] -->|Saugt a-/p-Gruppen Infos ab| NodeRed
NodeRed -->|Schreibt| SharedData[shared_data/json_cache.json]
end
subgraph "Datenvisualisierung (Frontend)"
SharedData -->|Liest| Streamlit[Analytics App: Streamlit]
Streamlit -->|Zeigt Webseite| User([Benutzer])
end
subgraph "Netzwerk & Zugriff"
User -->|HTTPS:443| Nginx[Zentraler NGINX Proxy]
Nginx -->|Port 8501| Streamlit
Nginx -->|Port 1880 /logic| NodeRed
end
```
+11 -9
View File
@@ -29,19 +29,21 @@ COPY --from=builder /opt/python-packages /opt/python-packages
# Den neuen Paket-Ordner zum PYTHONPATH hinzufügen
ENV PYTHONPATH="/opt/python-packages"
# Zurück zum sicheren Standard-User von Node-RED
USER node-red
# Für globale npm-Installationen im Container bleiben wir kurz root,
# übergeben aber das Node-RED-Verzeichnis als Zielpfad
WORKDIR /usr/src/node-red
RUN npm install --no-update-notifier \
node-red-contrib-moment \
node-red-contrib-cron-plus \
passport-ldapauth
# Node-RED Module installieren
RUN npm install node-red-contrib-moment node-red-contrib-cron-plus
# Wechsel zu root, um das Entrypoint-Skript zu kopieren und die Rechte zu setzen
USER root
# Kopiere unser neues Entrypoint-Skript
# Kopiere unser neues Entrypoint-Skript und setze Rechte
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
# Zurück zum sicheren Standard-User von Node-RED für den laufenden Betrieb
USER node-red
# Setze unser Skript als den neuen Entrypoint
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
+147
View File
@@ -69,5 +69,152 @@
}
},
"menu-menu-item-left-sidebar": true
},
"peter.huesser@psi.ch": {
"editor": {
"view": {
"view-store-zoom": false,
"view-store-position": false,
"view-show-grid": true,
"view-snap-grid": true,
"view-grid-size": 20,
"view-node-status": true,
"view-node-info-icon": true,
"view-node-show-label": true,
"view-show-tips": true,
"view-show-welcome-tours": true
},
"sidebar": {
"state": {
"v": 4,
"tabs": [],
"primary": {
"width": 300,
"top": {
"tabs": [
"info",
"help",
"config",
"context"
],
"height": 302,
"hidden": false,
"active": "info"
},
"bottom": {
"tabs": [
"debug"
],
"hidden": false,
"active": "debug"
}
},
"secondary": {
"width": 210,
"top": {
"tabs": [
"explorer"
],
"height": 302,
"hidden": false,
"active": "explorer"
},
"bottom": {
"tabs": [
"palette"
],
"hidden": false,
"active": "palette"
}
}
}
},
"tours": {
"welcome": "5.0.0"
}
}
},
"huesser@d.psi.ch": {
"editor": {
"view": {
"view-store-zoom": false,
"view-store-position": false,
"view-show-grid": true,
"view-snap-grid": true,
"view-grid-size": 20,
"view-node-status": true,
"view-node-info-icon": true,
"view-node-show-label": true,
"view-show-tips": true,
"view-show-welcome-tours": true
},
"tours": {
"welcome": "5.0.0"
}
}
},
"huesser": {
"editor": {
"view": {
"view-store-zoom": false,
"view-store-position": false,
"view-show-grid": true,
"view-snap-grid": true,
"view-grid-size": 20,
"view-node-status": true,
"view-node-info-icon": true,
"view-node-show-label": true,
"view-show-tips": true,
"view-show-welcome-tours": true
},
"sidebar": {
"state": {
"v": 4,
"tabs": [],
"primary": {
"width": 300,
"top": {
"tabs": [
"info",
"help",
"config",
"context"
],
"height": 302,
"hidden": false,
"active": "info"
},
"bottom": {
"tabs": [
"debug"
],
"hidden": false,
"active": "debug"
}
},
"secondary": {
"width": 210,
"top": {
"tabs": [
"explorer"
],
"height": 302,
"hidden": false,
"active": "explorer"
},
"bottom": {
"tabs": [
"palette"
],
"hidden": false,
"active": "palette"
}
}
}
},
"tours": {
"welcome": "5.0.0"
}
}
}
}
@@ -1 +1 @@
size_by_ownergroup_and_number_of_copies_2026-07-01T14_24_54.json
size_by_ownergroup_and_number_of_copies_2026-07-02T12_59_59.json
+43
View File
@@ -81,7 +81,50 @@ module.exports = {
// permissions: "*"
// }]
//},
adminAuth: {
type: "credentials",
users: function(username) {
return Promise.resolve({ username: username, permissions: "*" });
},
authenticate: function(username, password) {
return new Promise(function(resolve, reject) {
// 1. Die Liste aus der Docker-Umgebungsvariable auslesen und aufsplitten
const envUsers = process.env.ALLOWED_USERS || "";
const allowedUsers = envUsers.split(',').map(u => u.trim().toLowerCase());
// Sicherstellen, dass Groß-/Kleinschreibung beim Tippen keine Rolle spielt
const normalizedUsername = username.toLowerCase().trim();
// 2. Prüfen, ob der User auf der Liste steht (und die Variable nicht leer ist)
if (allowedUsers.length === 0 || !allowedUsers.includes(normalizedUsername) || envUsers === "") {
console.log(`Login-Abgewiesen: Benutzer '${username}' steht nicht auf der Docker-Whitelist.`);
return resolve(null); // Direkt abweisen, ohne das AD zu belasten
}
// 3. Wenn er auf der Liste steht -> Normal gegen das AD prüfen
const ldap = require('ldapjs');
const client = ldap.createClient({
url: 'ldaps://d.psi.ch', // Umgestellt auf verschlüsseltes LDAPS
tlsOptions: { rejectUnauthorized: false }
});
// Deine funktionierende Domain-Struktur
const userPrincipalName = username + '@d.psi.ch';
client.bind(userPrincipalName, password, function(err) {
client.destroy();
if (err) {
// Protokolliert den exakten AD-Fehler im Docker-Log für dich
console.log("PSI-AD-Fehlerdetails:", err.message);
return resolve(null);
}
return resolve({ username: username, permissions: "*" });
});
});
}
},
/** The following property can be used to enable HTTPS
* This property can be either an object, containing both a (private) key
* and a (public) certificate, or a function that returns such an object.
File diff suppressed because one or more lines are too long