RBAC Improvements

Caching improvements
Maker-Checker Implementation
This commit is contained in:
2026-10-21 07:27:47 +02:00
parent dee9098379
commit 5d7e2f74ce
25 changed files with 1269 additions and 309 deletions
@@ -36,12 +36,22 @@ export interface UserSyncResult {
let inFlightSyncPromise: Promise<UserSyncResult> | null = null;
// In-memory cache to prevent synchronous localStorage parsing on every HTTP request
let inMemorySyncRecord: UserSyncRecord | null = null;
let inMemoryActivationPending: boolean | null = null;
let inMemoryActivationReason: string | null = null;
/**
* Checks if the user is currently flagged in the browser as pending administrator activation / lockout.
*/
export function isActivationPending(): boolean {
if (inMemoryActivationPending !== null) {
return inMemoryActivationPending;
}
try {
return localStorage.getItem(LOCAL_STORAGE_ACTIVATION_PENDING_KEY) === 'true';
const val = localStorage.getItem(LOCAL_STORAGE_ACTIVATION_PENDING_KEY) === 'true';
inMemoryActivationPending = val;
return val;
} catch {
return false;
}
@@ -51,8 +61,13 @@ export function isActivationPending(): boolean {
* Gets the reason/error message recorded when activation check was last pending.
*/
export function getActivationPendingReason(): string | null {
if (inMemoryActivationReason !== null) {
return inMemoryActivationReason;
}
try {
return localStorage.getItem(LOCAL_STORAGE_ACTIVATION_REASON_KEY);
const reason = localStorage.getItem(LOCAL_STORAGE_ACTIVATION_REASON_KEY);
inMemoryActivationReason = reason;
return reason;
} catch {
return null;
}
@@ -62,6 +77,8 @@ export function getActivationPendingReason(): string | null {
* Sets or clears the pending activation flag in browser persistent storage.
*/
export function setActivationPending(pending: boolean, reason?: string): void {
inMemoryActivationPending = pending;
inMemoryActivationReason = reason || null;
try {
if (pending) {
localStorage.setItem(LOCAL_STORAGE_ACTIVATION_PENDING_KEY, 'true');
@@ -91,10 +108,15 @@ export function isUserSyncedInBrowser(currentSub?: string, currentToken?: string
return false;
}
const raw = localStorage.getItem(LOCAL_STORAGE_USER_SYNC_KEY);
if (!raw) return false;
if (!inMemorySyncRecord) {
const raw = localStorage.getItem(LOCAL_STORAGE_USER_SYNC_KEY);
if (!raw) return false;
const parsed: UserSyncRecord = JSON.parse(raw);
if (!parsed || parsed.status !== 'synced') return false;
inMemorySyncRecord = parsed;
}
const record: UserSyncRecord = JSON.parse(raw);
const record = inMemorySyncRecord;
if (!record || record.status !== 'synced') return false;
// Check if the stored sync matches the current user sub (if specified)
@@ -134,6 +156,7 @@ export function saveUserSyncInBrowser(
status: 'synced',
userData: userData || undefined,
};
inMemorySyncRecord = record;
localStorage.setItem(LOCAL_STORAGE_USER_SYNC_KEY, JSON.stringify(record));
} catch (err) {
console.warn('[UserSync] Failed to write sync record to localStorage:', err);
@@ -144,6 +167,9 @@ export function saveUserSyncInBrowser(
* Clears the user sync record from browser storage (used on logout or when activation fails).
*/
export function clearUserSyncInBrowser(): void {
inMemorySyncRecord = null;
inMemoryActivationPending = null;
inMemoryActivationReason = null;
try {
localStorage.removeItem(LOCAL_STORAGE_USER_SYNC_KEY);
localStorage.removeItem(LOCAL_STORAGE_ACTIVATION_PENDING_KEY);
@@ -155,6 +181,7 @@ export function clearUserSyncInBrowser(): void {
* Clears only the successful sync record, leaving activation pending intact.
*/
export function clearSuccessfulSyncRecord(): void {
inMemorySyncRecord = null;
try {
localStorage.removeItem(LOCAL_STORAGE_USER_SYNC_KEY);
} catch {}