Inhaltsverzeichnis
4.1 DB-Funktionen — Auth Controller
Stand: 2026-03-05
Übergeordnet: 4. Datenschicht — DB-Schemas Verwandt: 3. Gateway.Service
Dieses Dokument beschreibt alle gespeicherten Prozeduren, Funktionen und Views in
ENIVERSSIAM, die WvdS.Data.Gateway.Controller.Auth auf Datenbankebene unterstützen.
Endpoint → DB-Zuordnung
| Endpoint | DB-Objekte |
|---|---|
POST /auth/kerberos | fn_user_lookup, usp_login_success, usp_device_session_create, fn_user_roles |
POST /auth/external | fn_user_lookup, usp_password_failure, usp_mfa_session_create |
POST /auth/totp/verify | usp_mfa_session_consume, usp_totp_failure, usp_login_success, usp_device_session_create, fn_user_roles |
POST /auth/token/refresh | usp_refresh_token_rotate |
GET /auth/totp/enroll | usp_totp_enrollment_begin |
DELETE /auth/totp/enroll | usp_totp_reset |
GET /features | fn_get_features |
| Middleware: Replay-Schutz | usp_nonce_check_insert |
| Middleware: Rate-Limiting | usp_rate_limit_record, fn_is_rate_limited |
| Middleware: Autorisierung | fn_get_user_id, fn_is_user_member_of |
| Hintergrundaufgabe (periodisch) | usp_expired_cleanup |
Naming-Konvention
| Präfix | Typ | Beispiel |
|---|---|---|
usp_ | Stored Procedure (mit DML) | auth.usp_login_success |
fn_ | Scalar / Table-Valued Function | auth.fn_user_lookup, auth.fn_is_rate_limited |
v_ | View | auth.v_user_auth |
Faustregel: Reine Lesezugriffe mit tabellarischem Ergebnis → TVF (fn_), verwendbar inFROM/JOIN/CROSS APPLY.
Operationen mit DML (INSERT,UPDATE) oderOUTPUT-Parametern → Stored Procedure (usp_).
Identifier: lowercase snake_case (konsistent mit den Tabellennamen in ENIVERSSIAM).
Stored Procedures
auth.usp_login_success
Zweck: Erfolgreichen Login protokollieren — last_login_at aktualisieren, Passwort-Fehlversuche zurücksetzen.
CREATE OR ALTER PROCEDURE auth.usp_login_success @user_id INT, @ip_address VARCHAR(45) AS BEGIN SET NOCOUNT ON; UPDATE auth.[USER] SET last_login_at = SYSUTCDATETIME(), password_failed_attempts = 0, password_locked_until = NULL, updated_at = SYSUTCDATETIME() WHERE id = @user_id; END;
| Tabellen | auth.[user] |
| Aufgerufen von | POST /auth/kerberos, POST /auth/totp/verify |
auth.usp_password_failure
Zweck: Fehlgeschlagenen Passwort-Versuch registrieren. Setzt Lockout, wenn der Schwellwert erreicht ist. Gibt die verbleibenden Versuche und ggf. den Lockout-Zeitpunkt zurück.
CREATE OR ALTER PROCEDURE auth.usp_password_failure @user_id INT, @max_attempts INT = 5, @lockout_minutes INT = 15, @attempts_remaining INT OUTPUT, @locked_until DATETIME2 OUTPUT AS BEGIN SET NOCOUNT ON; UPDATE auth.[USER] SET password_failed_attempts = password_failed_attempts + 1, password_locked_until = CASE WHEN password_failed_attempts + 1 >= @max_attempts THEN DATEADD(MINUTE, @lockout_minutes, SYSUTCDATETIME()) ELSE NULL END, updated_at = SYSUTCDATETIME() WHERE id = @user_id; SELECT @attempts_remaining = CASE WHEN password_failed_attempts >= @max_attempts THEN 0 ELSE @max_attempts - password_failed_attempts END, @locked_until = password_locked_until FROM auth.[USER] WHERE id = @user_id; END;
| Tabellen | auth.[user] |
| Aufgerufen von | POST /auth/external |
auth.usp_mfa_session_create
Zweck: TOTP-Challenge-Session anlegen. Gibt eine neue challenge_id zurück, die der Gateway im JSON-Response an den Client sendet.
CREATE OR ALTER PROCEDURE auth.usp_mfa_session_create @user_id INT, @reason VARCHAR(50), @lifetime_seconds INT = 300, @challenge_id VARCHAR(64) OUTPUT, @expires_at DATETIME2 OUTPUT AS BEGIN SET NOCOUNT ON; -- challenge_id: Gateway übergibt eine bereits generierte UUID -- (NEWID() wäre akzeptabel, aber der Gateway-Layer kontrolliert die Entropie) SET @expires_at = DATEADD(SECOND, @lifetime_seconds, SYSUTCDATETIME()); INSERT INTO auth.mfa_session (mfa_session_id, user_id, reason, expires_at) VALUES (@challenge_id, @user_id, @reason, @expires_at); END;
| Tabellen | auth.mfa_session |
| Aufgerufen von | POST /auth/external |
| Hinweis | @challenge_id wird vom Gateway generiert (kryptografisch zufällig) und als INPUT übergeben. |
auth.usp_mfa_session_consume
Zweck: TOTP-Challenge atomar prüfen und verbrauchen. Inkrementiert den Versuchs-Counter; sperrt die Challenge bei Überschreitung von @max_attempts. Gibt Validierungsstatus und verbleibende Versuche zurück.
CREATE OR ALTER PROCEDURE auth.usp_mfa_session_consume @challenge_id VARCHAR(64), @max_attempts INT = 5, @user_id INT OUTPUT, @is_valid BIT OUTPUT, @is_expired BIT OUTPUT, @attempts INT OUTPUT, @attempts_remaining INT OUTPUT AS BEGIN SET NOCOUNT ON; BEGIN TRANSACTION; SELECT @user_id = user_id, @is_expired = CASE WHEN expires_at < SYSUTCDATETIME() THEN 1 ELSE 0 END, @attempts = attempts FROM auth.mfa_session WITH (UPDLOCK, ROWLOCK) WHERE mfa_session_id = @challenge_id AND is_verified = 0; IF @user_id IS NULL BEGIN -- Challenge unbekannt SET @is_valid = 0; ROLLBACK; RETURN; END; -- Versuchs-Counter erhöhen -- NOTE: mfa_session hat keine updated_at-Spalte UPDATE auth.mfa_session SET attempts = attempts + 1 WHERE mfa_session_id = @challenge_id; SET @attempts = @attempts + 1; SET @attempts_remaining = CASE WHEN @attempts >= @max_attempts THEN 0 ELSE @max_attempts - @attempts END; -- Gültig: Kein Ablauf, kein Überschreiten der Max-Versuche SET @is_valid = CASE WHEN @is_expired = 0 AND @attempts <= @max_attempts THEN 1 ELSE 0 END; -- Bei Erfolg: Session als verifiziert markieren IF @is_valid = 1 UPDATE auth.mfa_session SET is_verified = 1, verified_at = SYSUTCDATETIME() WHERE mfa_session_id = @challenge_id; COMMIT; END;
| Tabellen | auth.mfa_session |
| Aufgerufen von | POST /auth/totp/verify |
| Transaktional | Ja — UPDLOCK verhindert Race Conditions bei gleichzeitigen Verify-Requests |
auth.usp_totp_failure
Zweck: Fehlerhaften TOTP-Code registrieren. Analog zu usp_password_failure, aber für das TOTP-Lockout.
CREATE OR ALTER PROCEDURE auth.usp_totp_failure @user_id INT, @max_attempts INT = 5, @lockout_minutes INT = 15, @attempts_remaining INT OUTPUT, @locked_until DATETIME2 OUTPUT AS BEGIN SET NOCOUNT ON; UPDATE auth.[USER] SET totp_failed_attempts = totp_failed_attempts + 1, totp_locked_until = CASE WHEN totp_failed_attempts + 1 >= @max_attempts THEN DATEADD(MINUTE, @lockout_minutes, SYSUTCDATETIME()) ELSE NULL END, updated_at = SYSUTCDATETIME() WHERE id = @user_id; SELECT @attempts_remaining = CASE WHEN totp_failed_attempts >= @max_attempts THEN 0 ELSE @max_attempts - totp_failed_attempts END, @locked_until = totp_locked_until FROM auth.[USER] WHERE id = @user_id; END;
| Tabellen | auth.[user] |
| Aufgerufen von | POST /auth/totp/verify |
auth.usp_device_session_create
Zweck: Neues Gerät registrieren und initialen Refresh-Token-Hash speichern. Der eigentliche Refresh-Token-Wert verlässt den Gateway nie in Klartext — nur der SHA-256-Hash landet in der DB.
CREATE OR ALTER PROCEDURE auth.usp_device_session_create @user_id INT, @device_id VARCHAR(64), @name NVARCHAR(100), @refresh_token_hash VARBINARY(64), @family_id VARCHAR(64), @ip_address VARCHAR(45), @user_agent NVARCHAR(512), @device_pk INT OUTPUT AS BEGIN SET NOCOUNT ON; INSERT INTO auth.device (device_id, user_id, name, refresh_token_hash, refresh_family_id, refresh_generation, last_ip, last_user_agent, session_state, is_active, created_at) VALUES (@device_id, @user_id, @name, @refresh_token_hash, @family_id, 1, @ip_address, @user_agent, 'ACTIVE', 1, SYSUTCDATETIME()); SET @device_pk = SCOPE_IDENTITY(); -- Refresh-Token-Family initialisieren INSERT INTO auth.refresh_token_family (family_id, user_id, device_id, current_generation, is_revoked, created_at) VALUES (@family_id, @user_id, @device_pk, 1, 0, SYSUTCDATETIME()); END;
| Tabellen | auth.device, auth.refresh_token_family |
| Aufgerufen von | POST /auth/kerberos, POST /auth/totp/verify |
auth.usp_refresh_token_rotate
Zweck: Refresh-Token rotieren — Generation prüfen, neuen Hash speichern, Generation inkrementieren. Bei Generation-Mismatch: gesamte Token-Familie widerrufen (gestohlenes Token erkannt, CWE-613).
CREATE OR ALTER PROCEDURE auth.usp_refresh_token_rotate @family_id VARCHAR(64), @expected_generation INT, @new_token_hash VARBINARY(64), @new_expires_at DATETIME2, @success BIT OUTPUT, @user_id INT OUTPUT, @device_pk INT OUTPUT, @revoke_reason VARCHAR(50) OUTPUT AS BEGIN SET NOCOUNT ON; BEGIN TRANSACTION; SELECT @user_id = user_id, @device_pk = device_id FROM auth.refresh_token_family WITH (UPDLOCK, ROWLOCK) WHERE family_id = @family_id AND current_generation = @expected_generation AND is_revoked = 0; IF @user_id IS NULL BEGIN -- Generation-Mismatch oder bereits widerrufen → -- Gesamte Familie widerrufen (Replay / Diebstahl) SET @success = 0; SET @revoke_reason = 'generation_mismatch'; UPDATE auth.refresh_token_family SET is_revoked = 1, revoked_reason = 'generation_mismatch', revoked_at = SYSUTCDATETIME() WHERE family_id = @family_id; COMMIT; RETURN; END; -- Generation inkrementieren, neuen Hash speichern UPDATE auth.refresh_token_family SET current_generation = current_generation + 1, last_used_at = SYSUTCDATETIME() WHERE family_id = @family_id; UPDATE auth.device SET refresh_token_hash = @new_token_hash, refresh_token_expires_at = @new_expires_at, refresh_generation = refresh_generation + 1, last_used_at = SYSUTCDATETIME() WHERE id = @device_pk; SET @success = 1; SET @revoke_reason = NULL; COMMIT; END;
| Tabellen | auth.refresh_token_family, auth.device |
| Aufgerufen von | POST /auth/token/refresh |
| Transaktional | Ja — UPDLOCK verhindert parallele Rotation derselben Family |
auth.usp_session_revoke
Zweck: Geräte-Session widerrufen (Logout, Admin-Eingriff).
CREATE OR ALTER PROCEDURE auth.usp_session_revoke @device_id VARCHAR(64), @reason VARCHAR(50) AS BEGIN SET NOCOUNT ON; -- NOTE: auth.device hat keine updated_at-Spalte UPDATE auth.device SET session_state = 'REVOKED', session_revoked_at = SYSUTCDATETIME(), session_revoked_reason = @reason, is_active = 0 WHERE device_id = @device_id; -- Zugehörige Refresh-Token-Familie ebenfalls widerrufen UPDATE rtf SET rtf.is_revoked = 1, rtf.revoked_reason = @reason, rtf.revoked_at = SYSUTCDATETIME() FROM auth.refresh_token_family rtf JOIN auth.device d ON d.id = rtf.device_id WHERE d.device_id = @device_id; END;
| Tabellen | auth.device, auth.refresh_token_family |
| Aufgerufen von | Logout-Endpoint (zukünftig), Admin-Revoke |
auth.usp_totp_enrollment_begin
Zweck: TOTP-Enrollment starten — totp_pending_secret setzen.
Das Geheimnis verbleibt bis zur Bestätigung im Pending-Zustand (CWE-256: verschlüsselt übergeben).
CREATE OR ALTER PROCEDURE auth.usp_totp_enrollment_begin @user_id INT, @pending_secret VARBINARY(256), -- AES-256-GCM-verschlüsselt vom Gateway @expires_minutes INT = 10 AS BEGIN SET NOCOUNT ON; UPDATE auth.[USER] SET totp_pending_secret = @pending_secret, totp_pending_expires_at = DATEADD(MINUTE, @expires_minutes, SYSUTCDATETIME()), updated_at = SYSUTCDATETIME() WHERE id = @user_id; END;
| Tabellen | auth.[user] |
| Aufgerufen von | GET /auth/totp/enroll |
auth.usp_totp_enrollment_confirm
Zweck: TOTP-Enrollment abschließen — totp_pending_secret → totp_secret übernehmen, totp_enabled = 1 setzen.
Schlägt fehl (@success = 0) wenn kein gültiges Pending vorhanden oder abgelaufen.
CREATE OR ALTER PROCEDURE auth.usp_totp_enrollment_confirm @user_id INT, @success BIT OUTPUT AS BEGIN SET NOCOUNT ON; UPDATE auth.[USER] SET totp_secret = totp_pending_secret, totp_enabled = 1, totp_verified_at = SYSUTCDATETIME(), totp_failed_attempts = 0, totp_locked_until = NULL, totp_pending_secret = NULL, totp_pending_expires_at = NULL, updated_at = SYSUTCDATETIME() WHERE id = @user_id AND totp_pending_secret IS NOT NULL AND totp_pending_expires_at > SYSUTCDATETIME(); SET @success = CASE WHEN @@ROWCOUNT > 0 THEN 1 ELSE 0 END; END;
| Tabellen | auth.[user] |
| Aufgerufen von | GET /auth/totp/enroll (nach Bestätigungs-Code vom User) |
auth.usp_totp_reset
Zweck: TOTP-Enrollment vollständig zurücksetzen — totp_secret, totp_enabled, Pending-Felder löschen.
CREATE OR ALTER PROCEDURE auth.usp_totp_reset @user_id INT AS BEGIN SET NOCOUNT ON; UPDATE auth.[USER] SET totp_secret = NULL, totp_enabled = 0, totp_verified_at = NULL, totp_failed_attempts = 0, totp_locked_until = NULL, totp_pending_secret = NULL, totp_pending_expires_at = NULL, updated_at = SYSUTCDATETIME() WHERE id = @user_id; END;
| Tabellen | auth.[user] |
| Aufgerufen von | DELETE /auth/totp/enroll |
auth.usp_nonce_check_insert
Zweck: Replay-Schutz — Nonce atomar prüfen und einfügen. Ein PK-Konflikt bedeutet Replay (die Nonce wurde bereits verarbeitet).
CREATE OR ALTER PROCEDURE auth.usp_nonce_check_insert @nonce VARCHAR(64), @device_id INT, @expires_at DATETIME2, @is_replay BIT OUTPUT AS BEGIN SET NOCOUNT ON; BEGIN TRY INSERT INTO auth.nonce_cache (nonce, device_id, received_at, expires_at) VALUES (@nonce, @device_id, SYSUTCDATETIME(), @expires_at); SET @is_replay = 0; END TRY BEGIN CATCH -- PK-Violation = Nonce wurde bereits gesehen → Replay IF ERROR_NUMBER() = 2627 SET @is_replay = 1; ELSE THROW; END CATCH; END;
| Tabellen | auth.nonce_cache |
| Aufgerufen von | Replay-Schutz-Middleware (vor jeder authentifizierten Anfrage) |
auth.usp_rate_limit_record
Zweck: Rate-Limit-Ereignis protokollieren. Der Gateway ruft diese SP nach jedem relevanten Auth-Ereignis auf (Fehlversuch, unbekannter User, Brute-Force-Indikator).
CREATE OR ALTER PROCEDURE auth.usp_rate_limit_record @user_id INT = NULL, @device_id INT = NULL, @ip_address VARCHAR(45), @event_type VARCHAR(30) AS BEGIN SET NOCOUNT ON; INSERT INTO auth.rate_limit_event (user_id, device_id, ip_address, event_type, created_at) VALUES (@user_id, @device_id, @ip_address, @event_type, SYSUTCDATETIME()); END;
| Tabellen | auth.rate_limit_event |
| Aufgerufen von | Rate-Limit-Middleware, Auth-Controller bei Fehlversuchen |
auth.usp_expired_cleanup
Zweck: Wartungsaufgabe — abgelaufene Nonces und MFA-Sessions bereinigen. Wird vom Gateway periodisch aufgerufen (z. B. alle 5 Minuten via Timer-Thread).
CREATE OR ALTER PROCEDURE auth.usp_expired_cleanup AS BEGIN SET NOCOUNT ON; DECLARE @cutoff DATETIME2 = SYSUTCDATETIME(); DELETE FROM auth.nonce_cache WHERE expires_at < @cutoff; DELETE FROM auth.mfa_session WHERE expires_at < @cutoff AND is_verified = 0; DELETE FROM auth.rate_limit_event WHERE created_at < DATEADD(DAY, -7, @cutoff); END;
| Tabellen | auth.nonce_cache, auth.mfa_session, auth.rate_limit_event |
| Aufgerufen von | Gateway-interner Maintenance-Timer |
Scalar Functions
auth.fn_is_user_locked
Zweck: Prüft ob der Benutzer wegen Passwort-Fehlversuchen gesperrt ist.
CREATE OR ALTER FUNCTION auth.fn_is_user_locked (@user_id INT) RETURNS BIT AS BEGIN RETURN ( SELECT CASE WHEN password_locked_until > SYSUTCDATETIME() THEN 1 ELSE 0 END FROM auth.[USER] WHERE id = @user_id ); END;
auth.fn_is_totp_locked
Zweck: Prüft ob der TOTP-Kanal des Benutzers gesperrt ist.
CREATE OR ALTER FUNCTION auth.fn_is_totp_locked (@user_id INT) RETURNS BIT AS BEGIN RETURN ( SELECT CASE WHEN totp_locked_until > SYSUTCDATETIME() THEN 1 ELSE 0 END FROM auth.[USER] WHERE id = @user_id ); END;
auth.fn_is_rate_limited
Zweck: Zählt Rate-Limit-Ereignisse in einem Zeitfenster und gibt 1 zurück wenn das Limit überschritten ist.
CREATE OR ALTER FUNCTION auth.fn_is_rate_limited ( @user_id INT, @ip_address VARCHAR(45), @event_type VARCHAR(30), @window_seconds INT, @max_count INT ) RETURNS BIT AS BEGIN DECLARE @COUNT INT; DECLARE @window_start DATETIME2 = DATEADD(SECOND, -@window_seconds, SYSUTCDATETIME()); SELECT @COUNT = COUNT(*) FROM auth.rate_limit_event WHERE event_type = @event_type AND created_at >= @window_start AND ( (@user_id IS NOT NULL AND user_id = @user_id) OR (@ip_address IS NOT NULL AND ip_address = @ip_address) ); RETURN CASE WHEN @COUNT >= @max_count THEN 1 ELSE 0 END; END;
auth.fn_get_user_id
Zweck: Komfort-Wrapper um fn_user_lookup — gibt direkt die user_id zurück.
Vermeidet CROSS APPLY, wenn nur die ID benötigt wird (z. B. in Middleware-Checks vor dem eigentlichen Auth-Flow).
Gibt NULL zurück, wenn der Username nicht existiert.
CREATE OR ALTER FUNCTION auth.fn_get_user_id (@username VARCHAR(50)) RETURNS INT AS BEGIN RETURN ( SELECT user_id FROM auth.fn_user_lookup(@username) ); END;
| Aufgerufen von | Middleware: Autorisierung, überall wo user_id aus username benötigt wird |
auth.fn_is_user_member_of
Zweck: Prüft ob ein Benutzer mindestens eine Rolle aus einer kommaseparierten Liste besitzt.
Wraps fn_user_roles und vergleicht role_code gegen alle Einträge in @csv.
Gibt 1 zurück sobald ein Treffer gefunden wird, sonst 0.
Typische Verwendung in Middleware-Autorisierung:
-- Zugriff erlauben wenn der User 'admin' oder 'supervisor' ist IF auth.fn_is_user_member_of(@user_id, 'admin,supervisor') = 1 ...
CREATE OR ALTER FUNCTION auth.fn_is_user_member_of ( @user_id INT, @csv VARCHAR(250) -- kommaseparierte role_codes, z. B. 'admin,supervisor' ) RETURNS BIT AS BEGIN -- CHARINDEX mit umgebenden Kommas verhindert Teilstring-Treffer -- (z. B. 'admin' matcht nicht 'administrator' in 'administrator,viewer') RETURN ( SELECT CASE WHEN EXISTS ( SELECT 1 FROM auth.fn_user_roles(@user_id, NULL) r WHERE CHARINDEX( ',' + LTRIM(RTRIM(r.role_code)) + ',', ',' + @csv + ',' ) > 0 ) THEN 1 ELSE 0 END ); END;
| Aufgerufen von | Middleware: Autorisierung |
| Kombinierbar mit | auth.fn_get_user_id für den typischen Pattern: fn_is_user_member_of(fn_get_user_id(@username), @csv) |
Table-Valued Functions
auth.fn_user_lookup
Zweck: User-Datensatz für Authentifizierung laden — reine Leseoperation, daher TVF. Passwort-Hash und TOTP-Status werden zurückgegeben; der Gateway-Layer vergleicht den Hash selbst (CWE-256 — kein Krypto-Code in der DB). Gibt 0 Zeilen zurück wenn der Username nicht existiert.
Verwendbar in FROM, JOIN und CROSS APPLY — kombinierbar mit fn_user_roles:
-- Beispiel: User-Daten + Rollen in einem Query SELECT u.*, r.role_code FROM auth.fn_user_lookup('max.muster') u CROSS APPLY auth.fn_user_roles(u.user_id, NULL) r;
CREATE OR ALTER FUNCTION auth.fn_user_lookup (@username VARCHAR(50)) RETURNS TABLE AS RETURN ( -- Passwort-Hash + Salt werden im Gateway per PBKDF2 verglichen (CWE-256). -- Niemals Klartext-Passwort zurückgeben oder in der DB vergleichen. SELECT u.id AS user_id, u.person_id, p.first_name, p.last_name, u.is_active, u.password_hash, u.password_salt, u.password_changed_at, u.password_failed_attempts, u.password_locked_until, u.totp_enabled, u.totp_failed_attempts, u.totp_locked_until FROM auth.[USER] u JOIN core.person p ON p.id = u.person_id WHERE u.username = @username COLLATE Latin1_General_CI_AS );
| Tabellen | auth.[user], core.person |
| Aufgerufen von | POST /auth/kerberos, POST /auth/external |
auth.fn_user_roles
Zweck: Gibt alle aktiven Rollen eines Benutzers zurück — optional gefiltert nach Site-Scope. Wird im Gateway verwendet, um JWT-Role-Claims zu befüllen.
CREATE OR ALTER FUNCTION auth.fn_user_roles ( @user_id INT, @site_id INT = NULL -- NULL = globale + site-spezifische Rollen zurückgeben ) RETURNS TABLE AS RETURN ( SELECT r.id AS role_id, r.code AS role_code, r.name AS role_name, ur.site_id, ur.granted_at FROM auth.user_role ur JOIN auth.role r ON r.id = ur.role_id WHERE ur.user_id = @user_id AND r.is_active = 1 AND ( @site_id IS NULL -- alle Rollen OR ur.site_id IS NULL -- globale Rollen immer einschließen OR ur.site_id = @site_id -- site-spezifische Rollen ) );
| Aufgerufen von | POST /auth/kerberos, POST /auth/totp/verify (JWT-Claims befüllen) |
auth.fn_get_features
Zweck: Gibt die Feature-Keys zurück, die für eine gegebene install_id aktiv sind.
Liest aktuell aus ENIVERSCAFM (auth.*-Namespace), später aus ENIVERSASYS.
-- Platzhalter-Implementierung: aktuell Feature-Flags aus einer -- einfachen Konfigurations-Tabelle. Schema wird in Phase 5 erweitert. CREATE OR ALTER FUNCTION auth.fn_get_features (@install_id VARCHAR(64)) RETURNS TABLE AS RETURN ( SELECT feature_key FROM auth.feature_flag WHERE is_active = 1 AND (install_id = @install_id OR install_id IS NULL) );
Hinweis:auth.feature_flagist eine zukünftige Tabelle — wird in Phase 5
(ENIVERSASYS) definiert. Bis dahin kannfn_get_featureseine statische Liste zurückgeben.
| Aufgerufen von | GET /features |
Views
auth.v_user_auth
Zweck: Kombinierte Sicht auf User + Person + berechnete Lockout-Flags. Vereinfacht den häufigsten Auth-Lesezugriff im Gateway.
CREATE OR ALTER VIEW auth.v_user_auth AS SELECT u.id AS user_id, u.person_id, u.username, p.first_name, p.last_name, u.is_active, u.totp_enabled, u.totp_failed_attempts, -- Berechnete Lockout-Flags (live, kein Cache-Risiko) CASE WHEN u.password_locked_until > SYSUTCDATETIME() THEN 1 ELSE 0 END AS is_password_locked, u.password_locked_until, u.password_failed_attempts, CASE WHEN u.totp_locked_until > SYSUTCDATETIME() THEN 1 ELSE 0 END AS is_totp_locked, u.totp_locked_until, u.last_login_at, u.created_at FROM auth.[USER] u JOIN core.person p ON p.id = u.person_id;
| Tabellen | auth.[user], core.person |
| Aufgerufen von | Direkter SELECT statt usp_user_lookup für einfache Lesefälle |