Fixed API calls on governance page
Enforced multitenancy
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
import { AccountActivationModal } from '../components/auth/AccountActivationModal';
|
||||
import { SessionExpiredModal } from '../components/auth/SessionExpiredModal';
|
||||
import { encodeHashIdSync } from '../services/toolsService';
|
||||
import { verifyTenantLockout } from '../services/tenantLockoutService';
|
||||
import {
|
||||
onSessionExpired,
|
||||
triggerSessionExpired,
|
||||
@@ -120,6 +121,79 @@ export function extractTenantId(claims?: UserClaims | null, token?: string | nul
|
||||
return null;
|
||||
}
|
||||
|
||||
export function extractTenantClaimValues(
|
||||
claims?: UserClaims | null,
|
||||
token?: string | null
|
||||
): {
|
||||
tenantId: number | string | null;
|
||||
tenant: string | null;
|
||||
} {
|
||||
let tenantId: number | string | null = null;
|
||||
let tenant: string | null = null;
|
||||
|
||||
if (claims) {
|
||||
const rawId =
|
||||
claims.tenant_id ??
|
||||
(claims as any).tenantId ??
|
||||
claims['tenant_id'] ??
|
||||
claims['http://schemas.microsoft.com/ws/2008/06/identity/claims/tenantid'];
|
||||
if (rawId !== undefined && rawId !== null && rawId !== '') {
|
||||
const parsed = Number(rawId);
|
||||
tenantId = !isNaN(parsed) && isFinite(parsed) ? parsed : String(rawId).trim();
|
||||
}
|
||||
|
||||
const rawTenant =
|
||||
claims.tenant ??
|
||||
claims['tenant'] ??
|
||||
claims['http://schemas.microsoft.com/ws/2008/06/identity/claims/tenant'] ??
|
||||
claims.tenant_name ??
|
||||
(claims as any).tenantName ??
|
||||
claims.org;
|
||||
if (rawTenant !== undefined && rawTenant !== null && rawTenant !== '') {
|
||||
if (typeof rawTenant === 'string') {
|
||||
tenant = rawTenant.trim();
|
||||
} else if (typeof rawTenant === 'object' && rawTenant.name) {
|
||||
tenant = String(rawTenant.name).trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (token && (!tenantId || !tenant)) {
|
||||
try {
|
||||
const tokenClaims = parseJwtClaims(token);
|
||||
if (!tenantId) {
|
||||
const rawId =
|
||||
tokenClaims.tenant_id ??
|
||||
tokenClaims.tenantId ??
|
||||
tokenClaims['tenant_id'] ??
|
||||
tokenClaims['http://schemas.microsoft.com/ws/2008/06/identity/claims/tenantid'];
|
||||
if (rawId !== undefined && rawId !== null && rawId !== '') {
|
||||
const parsed = Number(rawId);
|
||||
tenantId = !isNaN(parsed) && isFinite(parsed) ? parsed : String(rawId).trim();
|
||||
}
|
||||
}
|
||||
if (!tenant) {
|
||||
const rawTenant =
|
||||
tokenClaims.tenant ??
|
||||
tokenClaims['tenant'] ??
|
||||
tokenClaims['http://schemas.microsoft.com/ws/2008/06/identity/claims/tenant'] ??
|
||||
tokenClaims.tenant_name ??
|
||||
(tokenClaims as any).tenantName ??
|
||||
tokenClaims.org;
|
||||
if (rawTenant !== undefined && rawTenant !== null && rawTenant !== '') {
|
||||
if (typeof rawTenant === 'string') {
|
||||
tenant = rawTenant.trim();
|
||||
} else if (typeof rawTenant === 'object' && rawTenant.name) {
|
||||
tenant = String(rawTenant.name).trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return { tenantId, tenant };
|
||||
}
|
||||
|
||||
export function getCurrentUserTenantId(): number | null {
|
||||
return extractTenantId(
|
||||
getCurrentUserClaims(),
|
||||
@@ -283,7 +357,8 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
localStorage.setItem(LOCAL_STORAGE_SUB_KEY, userSub);
|
||||
localStorage.setItem(LOCAL_STORAGE_CLAIMS_KEY, JSON.stringify(mergedClaims));
|
||||
|
||||
const tenantClaim = (mergedClaims.tenant as string) || (oidcUser.profile?.tenant as string) || undefined;
|
||||
const tenantClaimValues = extractTenantClaimValues(mergedClaims, token);
|
||||
const tenantClaim = (mergedClaims.tenant as string) || (oidcUser.profile?.tenant as string) || tenantClaimValues.tenant || undefined;
|
||||
const parsedTenantId = extractTenantId(mergedClaims, token);
|
||||
setTenantId(parsedTenantId);
|
||||
|
||||
@@ -304,6 +379,20 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
// Clean up code and state from URL
|
||||
window.history.replaceState({}, document.title, window.location.pathname);
|
||||
|
||||
// Verify tenant lockout at the instance tenant and tenant_id claims are retrieved
|
||||
const tenantLockoutResult = await verifyTenantLockout(
|
||||
parsedTenantId ?? tenantClaimValues.tenantId,
|
||||
tenantClaim
|
||||
);
|
||||
|
||||
if (!tenantLockoutResult.isActive) {
|
||||
setIsPendingActivation(true);
|
||||
setIsSynced(false);
|
||||
setActivationError(tenantLockoutResult.errorMessage || 'Tenant is inactive or not found');
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Synchronize authenticated user profile with backend directory
|
||||
try {
|
||||
const syncResult = await syncUserAccount(userSub, token, profile, true);
|
||||
@@ -367,6 +456,8 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
|
||||
const storedTenantId = extractTenantId(parsedClaims, storedToken);
|
||||
setTenantId(storedTenantId);
|
||||
const tenantClaimValues = extractTenantClaimValues(parsedClaims, storedToken);
|
||||
const storedTenantClaim = (parsedClaims.tenant as string) || tenantClaimValues.tenant || undefined;
|
||||
|
||||
let resolvedUser: AuthUserProfile | null = null;
|
||||
if (storedUser) {
|
||||
@@ -383,6 +474,20 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Verify tenant lockout at the instance tenant and tenant_id claims are retrieved
|
||||
const tenantLockoutResult = await verifyTenantLockout(
|
||||
storedTenantId ?? tenantClaimValues.tenantId,
|
||||
storedTenantClaim
|
||||
);
|
||||
|
||||
if (!tenantLockoutResult.isActive) {
|
||||
setIsPendingActivation(true);
|
||||
setIsSynced(false);
|
||||
setActivationError(tenantLockoutResult.errorMessage || 'Tenant is inactive or not found');
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if session is already marked pending activation or needs sync check
|
||||
const wasPending = isActivationPending();
|
||||
setIsPendingActivation(wasPending);
|
||||
@@ -435,6 +540,25 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
|
||||
const syncUserSession = async (force = true): Promise<UserSyncResult> => {
|
||||
try {
|
||||
// Re-verify tenant status via GetTenant as part of the lockout resolution
|
||||
const tenantClaimValues = extractTenantClaimValues(claims, accessToken);
|
||||
const currentTenantId = tenantId ?? tenantClaimValues.tenantId;
|
||||
const currentTenantClaim = (claims.tenant as string) || user?.tenant || tenantClaimValues.tenant || undefined;
|
||||
|
||||
const tenantLockoutResult = await verifyTenantLockout(currentTenantId, currentTenantClaim);
|
||||
if (!tenantLockoutResult.isActive) {
|
||||
const msg = tenantLockoutResult.errorMessage || 'Tenant is not Active. Lockout active.';
|
||||
setIsPendingActivation(true);
|
||||
setIsSynced(false);
|
||||
setActivationError(msg);
|
||||
return {
|
||||
success: false,
|
||||
isActivationPending: true,
|
||||
errorMessage: msg,
|
||||
};
|
||||
}
|
||||
|
||||
// Tenant is verified Active: proceed with user sync
|
||||
const result = await syncUserAccount(sub, accessToken, user || undefined, force);
|
||||
if (result.isUnauthorized) {
|
||||
await logout();
|
||||
|
||||
Reference in New Issue
Block a user