Bastian Wagner e89ac1481e fix
2026-07-17 14:52:20 +02:00
fix
2026-07-17 14:52:20 +02:00
url
2026-07-15 16:03:49 +02:00
2026-07-15 14:31:56 +02:00
2026-07-17 14:34:44 +02:00
2026-07-15 14:09:28 +02:00
2026-07-15 15:19:15 +02:00
2026-07-15 15:19:15 +02:00
2026-07-15 14:09:28 +02:00
2026-07-15 14:09:28 +02:00
2026-07-15 14:09:28 +02:00
2026-07-17 14:34:44 +02:00
2026-07-15 14:09:28 +02:00

LDAP Portal

Self-Service-Portal fuer LLDAP mit NestJS API, Angular Frontend und eigenem OpenID-Connect-Provider fuer Web-SSO.

Die Anwendung ist als Identity-Portal vor einem bestehenden LLDAP gedacht: Nutzer koennen sich registrieren, E-Mail und Passwort verwalten, Administratoren koennen Registrierungen, Nutzer, Gruppen und OIDC-Clients pflegen.

Funktionen

  • Registrierung mit E-Mail-Verifikation und anschliessender Admin-Freigabe
  • Login gegen LDAP/LLDAP
  • Passwortaenderung nach erfolgreichem LDAP-Login
  • Passwort-Reset ueber eigene Tokens und SMTP
  • Profilbearbeitung, E-Mail-Aenderung mit Verifikation und Account-Loeschanfrage
  • Admin-Bereiche fuer Registrierungen, Nutzer, Gruppen und Audit
  • Audit-Events fuer sicherheitsrelevante Aktionen in MySQL
  • OpenID Connect Provider fuer Web-SSO
  • Single-Container-Deployment mit Nginx, Angular und NestJS API

Architektur

Das Portal besteht aus drei Teilen:

  • Angular Web-UI: Login, Registrierung, Konto- und Admin-Oberflaechen.
  • NestJS API: REST-Endpunkte, OIDC Provider, Mailversand, Audit, LLDAP-Integration.
  • Nginx im Container: liefert die Web-UI aus und routet API/OIDC-Pfade intern zur NestJS API.

Externe Dienste werden nicht mitgeliefert und muessen vorhanden sein:

  • MySQL-Datenbank fuer Portal-Daten, Registrierungen, Tokens, Audit und OIDC-Speicher.
  • LLDAP HTTP/GraphQL-Endpunkt fuer administrative Operationen.
  • LLDAP LDAP-Endpunkt fuer Passwortpruefung und Passwortaenderung.
  • SMTP-Server fuer Verifikations- und Passwort-Reset-Mails.

Lokale Entwicklung

cp .env.example .env
npm install
npm run start:api
npm run start:web

In der lokalen Entwicklung laeuft die API standardmaessig auf http://localhost:3000, das Frontend auf http://localhost:4200.

Wichtige lokale URLs:

  • Web UI: http://localhost:4200
  • API: http://localhost:3000
  • OIDC Discovery bei direkter API-Nutzung: http://localhost:3000/.well-known/openid-configuration

Deployment

Voraussetzungen

Vor dem Start muessen erreichbar sein:

  • MySQL, z. B. mysql://ldap_portal:secret@mysql.example.com:3306/ldap_portal
  • LLDAP Web/GraphQL, z. B. https://lldap.example.com
  • LLDAP LDAP, z. B. ldap://lldap.example.com:3890
  • SMTP, z. B. smtp.example.com:587

Erzeuge produktive Secrets fuer mindestens:

  • JWT_SECRET
  • TOKEN_SECRET
  • OIDC_COOKIE_SECRET
  • LLDAP_ADMIN_PASSWORD
  • SMTP_PASS

JWT_SECRET, TOKEN_SECRET und OIDC_COOKIE_SECRET muessen lang, zufaellig und stabil sein. Wenn sie nach dem Deployment geaendert werden, werden bestehende Sessions, Tokens oder verschluesselte Portal-Daten unbrauchbar.

Single Container

Das Root-Dockerfile baut API und Angular in ein einzelnes Image. Der Container startet:

  • Nginx Web-UI und Reverse Proxy auf Port 8080
  • NestJS API / IdP intern auf Port 3000

Build und Push:

docker build -t registry.example.com/ldap-portal/idp:latest .
docker push registry.example.com/ldap-portal/idp:latest

Start:

docker run -d --name ldap-portal-idp \
  --env-file .env \
  -p 8080:8080 \
  registry.example.com/ldap-portal/idp:latest

Docker Compose:

cp .env.example .env
docker compose up --build

Reverse Proxy und externe URLs

Der Container erwartet eingehenden Traffic auf Port 8080. Ein externer Reverse Proxy kann davor TLS terminieren.

Bei produktivem Betrieb hinter https://idp.example.com sollten diese Werte zusammenpassen:

NODE_ENV=production
PUBLIC_WEB_URL=https://idp.example.com
OIDC_ISSUER=https://idp.example.com
OIDC_TRUST_PROXY=true
API_BASE_URL=/api

PUBLIC_WEB_URL wird fuer CORS, E-Mail-Links und den Registrierungslink auf der SSO-Loginseite verwendet.

OIDC_ISSUER muss exakt die externe Issuer-URL sein, die SSO-Clients verwenden. Wenn der Dienst hinter TLS laeuft, muss hier die https://-URL stehen.

API_BASE_URL sollte im Single-Container-Deployment bei /api bleiben. Das Frontend ruft dann /api/... auf; Nginx entfernt den Prefix intern und routet zur NestJS API. Die OIDC-Pfade /.well-known/, /oidc/ und /interaction/ werden ebenfalls zur API geroutet.

Konfiguration

Die wichtigsten Variablen aus .env.example:

Variable Bedeutung
NODE_ENV In Produktion production; deaktiviert TypeORM synchronize und aktiviert sichere OIDC-Cookies.
API_PORT Interner Port der NestJS API, im Container standardmaessig 3000.
PUBLIC_WEB_URL Externe Web-URL fuer CORS, Mail-Links und Registrierung aus dem SSO-Login.
API_BASE_URL API-Basis aus Sicht des Browsers, im Single Container /api.
APP_PRODUCT_NAME, APP_COMPANY_NAME Zentrale Branding-Namen fuer OIDC-Seiten und Mail-Templates.
APP_PRIMARY_COLOR Zentrale Primaerfarbe als sechsstelliger Hex-Wert.
APP_SUPPORT_EMAIL, APP_LOGO_URL, APP_IMPRINT_URL, APP_PRIVACY_URL Zentrale Branding-Werte; Bild- und Link-URLs muessen HTTPS verwenden.
DATABASE_URL MySQL-Verbindungs-URL. Alternativ DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD, DB_DATABASE.
DATABASE_SSL true, wenn MySQL TLS verlangt.
JWT_SECRET Signatur-Secret fuer Portal-JWTs.
TOKEN_SECRET Verschluesselung/Hashing fuer Portal-Tokens und gespeicherte Secrets.
LLDAP_URL LLDAP Web/GraphQL-Basis-URL ohne /api/graphql.
LLDAP_LDAP_URL LDAP-Endpunkt fuer Bind und Password Modify.
LLDAP_BASE_DN LDAP Base DN, z. B. dc=example,dc=com.
LLDAP_ADMIN_USERNAME LLDAP Admin-User fuer GraphQL und LDAP-Admin-Bind.
LLDAP_ADMIN_PASSWORD Passwort des LLDAP Admin-Users.
LLDAP_GRAPHQL_TOKEN Optionaler statischer Bearer Token fuer GraphQL statt Login per Admin-User.
LLDAP_DEFAULT_GROUP Optionale numerische LLDAP-Gruppen-ID, in die freigegebene neue Nutzer aufgenommen werden.
SMTP_HOST, SMTP_PORT, SMTP_SECURE SMTP-Verbindung.
SMTP_USER, SMTP_PASS Optionale SMTP-Authentifizierung.
SMTP_FROM Absenderadresse fuer Portal-Mails.
MAIL_PRODUCT_NAME, MAIL_COMPANY_NAME Zentrale Branding-Namen fuer Mail-Templates.
MAIL_PRIMARY_COLOR Primaerfarbe fuer Mail-Buttons und Links.
MAIL_SUPPORT_EMAIL, MAIL_LOGO_URL, MAIL_IMPRINT_URL, MAIL_PRIVACY_URL Optionale Branding- und Footer-Werte fuer Mails.
OIDC_ISSUER Externe Issuer-URL des OIDC Providers.
OIDC_COOKIE_SECRET Cookie-Secret fuer OIDC Sessions; Fallback ist TOKEN_SECRET.
OIDC_ADMIN_GROUP Gruppe fuer OIDC-Clientverwaltung, Standard client_manager.
OIDC_ADMIN_GROUP_UUID Zusaetzlich akzeptierte Gruppen-UUID fuer OIDC-Clientverwaltung.
OIDC_TRUST_PROXY true, wenn ein Reverse Proxy TLS/Host-Header setzt.
USER_MANAGER_GROUP LLDAP-Gruppe, deren Mitglieder per E-Mail benachrichtigt werden, wenn eine Registrierung nach E-Mail-Bestaetigung auf Freigabe wartet. Standard user_manager.

Hinweis: REGISTRATION_MANAGER_GROUP, GROUP_MANAGER_GROUP und AUDIT_VIEWER_GROUP stehen aktuell in .env.example, werden im Code aber nicht ausgewertet. Die Admin-Gruppennamen sind derzeit fest verdrahtet, siehe "Admin-Rollen".

Die APP_*-Werte haben Vorrang. Solange sie nicht gesetzt sind, verwendet die Anwendung fuer die Rueckwaertskompatibilitaet die entsprechenden MAIL_*-Werte. Die OIDC-Seiten liegen unter apps/api/src/oidc/templates und werden beim API-Build nach dist/oidc/templates kopiert. Sie werden ohne clientseitiges JavaScript direkt durch NestJS gerendert.

Datenbank

Die App nutzt TypeORM mit MySQL. In NODE_ENV=production ist synchronize deaktiviert. Fuer produktive Deployments muss das Schema vorab vorhanden sein oder es muessen Migrationen ergaenzt und ausgefuehrt werden.

In Entwicklung kann TypeORM das Schema automatisch synchronisieren, solange NODE_ENV nicht production ist.

Registrierung und Nutzerfluss

Die Registrierung laeuft in zwei Schritten:

  1. Nutzer registrieren sich mit E-Mail, Anzeigename und Passwort.
  2. Der Nutzer bestaetigt seine E-Mail ueber den Mail-Link.
  3. Ein Admin mit Gruppe registration_manager gibt die Registrierung frei.
  4. Erst bei Freigabe wird der LLDAP-User erstellt und optional in LLDAP_DEFAULT_GROUP aufgenommen.

Nach Schritt 2 sendet das Portal eine Benachrichtigung an die E-Mail-Adressen der Mitglieder aus USER_MANAGER_GROUP, damit offene Freigaben sichtbar werden. Wenn die Gruppe nicht gefunden wird, keine Mitglieder mit E-Mail-Adresse hat oder der Mailversand fehlschlaegt, bleibt die Registrierung trotzdem im Status pending_approval; der Fehler wird im Audit protokolliert.

Passwortregeln:

  • mindestens 12 Zeichen
  • mindestens ein Kleinbuchstabe
  • mindestens ein Grossbuchstabe
  • mindestens eine Ziffer

Der Benutzername in LLDAP ist die E-Mail-Adresse der Registrierung.

Admin-Rollen

Admin-Berechtigungen werden ueber LLDAP-Gruppen gesteuert:

  • client_manager: OIDC-Clients verwalten.
  • registration_manager: Registrierungen freigeben oder ablehnen.
  • user_manager: Nutzer anzeigen, bearbeiten, loeschen und Gruppenmitgliedschaften aendern.
  • group_manager: Gruppen anzeigen, erstellen, bearbeiten und loeschen.
  • audit_viewer: Audit-Events anzeigen.

Die allgemeinen Admin-Rollen werden aktuell per Gruppen-Displayname geprueft. Die OIDC-Clientverwaltung akzeptiert zusaetzlich die konfigurierte OIDC_ADMIN_GROUP_UUID.

Wichtig fuer die Erstinbetriebnahme: Der erste Admin muss in LLDAP bereits existieren und in die benoetigten Gruppen aufgenommen werden, bevor er die Admin-Bereiche im Portal nutzen kann.

OpenID Connect

Die API stellt einen OIDC Provider bereit. Die wichtigsten Endpunkte:

  • Discovery: /.well-known/openid-configuration
  • Authorization: /oidc/auth
  • Token: /oidc/token
  • UserInfo: /oidc/me
  • JWKS: /oidc/jwks
  • Logout: /oidc/session/end
  • Revocation: /oidc/token/revocation
  • Introspection: /oidc/token/introspection
  • Interactions/Login/Consent: /interaction/...

OIDC-Clients werden im Frontend unter /admin/oidc-clients verwaltet. Zugriff erhaelt ein eingeloggter Nutzer mit OIDC-Admin-Berechtigung. Client Secrets werden nur direkt nach Erstellung oder Secret-Rotation angezeigt.

V1 unterstuetzt Authorization Code Flow mit verpflichtendem PKCE. Dynamic Client Registration und SAML sind nicht aktiviert.

Client-Optionen

  • Redirect URIs: erlaubte Callback-URLs des SSO-Clients.
  • Logout Redirect URIs: erlaubte Redirects nach RP-initiated Logout.
  • Scopes: Scopes, die der Client verwenden darf, z. B. openid profile email groups.
  • Public Client ohne Secret: erstellt einen Client ohne Secret und mit token_endpoint_auth_method=none; gedacht fuer Browser/native Apps, die kein Secret sicher halten koennen.
  • Confidential Client: Standard, erzeugt ein Client Secret und verwendet client_secret_basic.
  • Gruppen-Claim ausgeben: steuert, ob der groups Claim in OIDC-Claims ausgegeben wird.
  • First-Party Client: fuer eigene/interne Clients. Nach erfolgreichem Login wird die Consent-Seite uebersprungen und die angeforderten Berechtigungen werden automatisch bestaetigt.

Nach der Erstellung koennen Name, Redirect URIs, Logout Redirect URIs, Scopes, First-Party-Flag, Gruppen-Claim und Aktivstatus im Admin-UI bearbeitet werden. Die client_id bleibt stabil.

Confidential Clients koennen ihr Client Secret im Admin-UI rotieren. Das neue Secret wird nur einmal angezeigt und muss danach in der angebundenen Anwendung hinterlegt werden. Bestehende Grants, Sessions und Refresh Tokens werden durch die Rotation nicht aktiv widerrufen; neue Token-Requests muessen aber das neue Secret verwenden. Public Clients haben kein Secret und koennen daher nicht rotiert werden.

LLDAP-Hinweise

Die API nutzt:

  • LDAP-Bind fuer Login und Passwortpruefung.
  • LDAP Password Modify Extended Operation fuer Passwortaenderungen.
  • GraphQL fuer User-, Gruppen- und Admin-Operationen.

Der LLDAP Admin-User muss ausreichende Rechte fuer diese Operationen haben.

Falls sich GraphQL-Mutationsnamen zwischen LLDAP-Versionen unterscheiden, muessen die Queries in apps/api/src/lldap/lldap.service.ts an die Zielversion angepasst werden.

Produktions-Checkliste

  • Externe MySQL-Datenbank angelegt und erreichbar.
  • Datenbankschema/Migrationen fuer Produktion geklaert.
  • LLDAP Web/GraphQL und LDAP vom Container aus erreichbar.
  • SMTP erreichbar und Absender korrekt gesetzt.
  • PUBLIC_WEB_URL und OIDC_ISSUER auf die externe HTTPS-URL gesetzt.
  • OIDC_TRUST_PROXY=true, wenn TLS am Reverse Proxy terminiert.
  • API_BASE_URL=/api fuer Single-Container-Deployment gesetzt.
  • Lange zufaellige Secrets gesetzt und gesichert.
  • LLDAP-Gruppen fuer Admin-Rollen angelegt.
  • Mindestens ein bestehender Admin-User ist Mitglied der benoetigten Gruppen.
  • OIDC-Clients mit korrekten Redirect URIs angelegt.
  • Reverse Proxy leitet /.well-known/, /oidc/, /interaction/, /api/ und die Web-UI an den Container weiter.
Description
No description provided
Readme 1.1 MiB
Languages
TypeScript 90.6%
Handlebars 5.7%
CSS 2.3%
Dockerfile 0.7%
JavaScript 0.5%
Other 0.2%