Files
btpxpress-frontend/config/keycloak.ts
2025-10-13 05:29:32 +02:00

335 lines
8.3 KiB
TypeScript

/**
* Configuration Keycloak pour BTP Xpress
* Intégration complète avec security.lions.dev
*/
import Keycloak from 'keycloak-js';
import { KEYCLOAK_CONFIG, KEYCLOAK_ROLES, KEYCLOAK_SCOPES } from './api';
// Configuration de l'instance Keycloak
export const keycloakConfig = {
url: KEYCLOAK_CONFIG.url,
realm: KEYCLOAK_CONFIG.realm,
clientId: KEYCLOAK_CONFIG.clientId,
};
// Instance Keycloak globale avec gestion SSR et développement
let keycloak: any = null;
// Fonction pour initialiser Keycloak côté client uniquement
export const initKeycloak = () => {
if (typeof window === 'undefined') {
// Côté serveur - retourner un mock
return {
init: () => Promise.resolve(false),
login: () => console.log('Mock Keycloak login'),
logout: () => console.log('Mock Keycloak logout'),
updateToken: () => Promise.resolve(false),
authenticated: false,
token: null,
refreshToken: null,
tokenParsed: null,
onTokenExpired: null,
onAuthRefreshSuccess: null,
onAuthRefreshError: null,
onAuthLogout: null,
};
}
// Côté client - initialiser Keycloak réel
if (!keycloak) {
try {
keycloak = new Keycloak(keycloakConfig);
} catch (error) {
console.warn('Keycloak non disponible en mode développement:', error);
// Mock Keycloak pour le développement local
keycloak = {
init: () => Promise.resolve(false),
login: () => console.log('Mock Keycloak login'),
logout: () => console.log('Mock Keycloak logout'),
updateToken: () => Promise.resolve(false),
authenticated: false,
token: null,
refreshToken: null,
tokenParsed: null,
onTokenExpired: null,
onAuthRefreshSuccess: null,
onAuthRefreshError: null,
onAuthLogout: null,
};
}
}
return keycloak;
};
// Export pour compatibilité
export { keycloak };
// Options d'initialisation Keycloak
export const keycloakInitOptions = {
onLoad: 'login-required' as const,
checkLoginIframe: false,
pkceMethod: 'S256' as const,
scope: KEYCLOAK_SCOPES.join(' '),
responseMode: 'fragment' as const,
flow: 'standard' as const,
enableLogging: process.env.NODE_ENV === 'development',
};
// Configuration des rôles BTP Xpress
export const BTP_ROLES = {
// Rôles administratifs
SUPER_ADMIN: 'super_admin',
ADMIN: 'admin',
// Rôles de gestion
DIRECTEUR: 'directeur',
MANAGER: 'manager',
CHEF_CHANTIER: 'chef_chantier',
// Rôles opérationnels
CONDUCTEUR_TRAVAUX: 'conducteur_travaux',
CHEF_EQUIPE: 'chef_equipe',
EMPLOYE: 'employe',
OUVRIER: 'ouvrier',
// Rôles clients
CLIENT_ENTREPRISE: 'client_entreprise',
CLIENT_PARTICULIER: 'client_particulier',
// Rôles techniques
COMPTABLE: 'comptable',
COMMERCIAL: 'commercial',
LOGISTICIEN: 'logisticien',
// Rôles de consultation
VIEWER: 'viewer',
GUEST: 'guest',
} as const;
// Hiérarchie des rôles (du plus élevé au plus bas)
export const ROLE_HIERARCHY = [
BTP_ROLES.SUPER_ADMIN,
BTP_ROLES.ADMIN,
BTP_ROLES.DIRECTEUR,
BTP_ROLES.MANAGER,
BTP_ROLES.CHEF_CHANTIER,
BTP_ROLES.CONDUCTEUR_TRAVAUX,
BTP_ROLES.CHEF_EQUIPE,
BTP_ROLES.COMMERCIAL,
BTP_ROLES.COMPTABLE,
BTP_ROLES.LOGISTICIEN,
BTP_ROLES.EMPLOYE,
BTP_ROLES.OUVRIER,
BTP_ROLES.CLIENT_ENTREPRISE,
BTP_ROLES.CLIENT_PARTICULIER,
BTP_ROLES.VIEWER,
BTP_ROLES.GUEST,
] as const;
// Permissions par rôle
export const ROLE_PERMISSIONS = {
[BTP_ROLES.SUPER_ADMIN]: ['*'], // Toutes les permissions
[BTP_ROLES.ADMIN]: [
'users.manage',
'chantiers.manage',
'materiels.manage',
'employes.manage',
'clients.manage',
'devis.manage',
'factures.manage',
'reports.view',
'dashboard.admin',
],
[BTP_ROLES.DIRECTEUR]: [
'chantiers.manage',
'employes.manage',
'clients.manage',
'devis.manage',
'factures.manage',
'reports.view',
'dashboard.management',
],
[BTP_ROLES.MANAGER]: [
'chantiers.view',
'chantiers.edit',
'employes.view',
'employes.edit',
'materiels.view',
'materiels.edit',
'devis.view',
'devis.edit',
'factures.view',
'reports.view',
'dashboard.management',
],
[BTP_ROLES.CHEF_CHANTIER]: [
'chantiers.view',
'chantiers.edit',
'employes.view',
'materiels.view',
'materiels.reserve',
'planning.view',
'planning.edit',
'dashboard.chantier',
],
[BTP_ROLES.CONDUCTEUR_TRAVAUX]: [
'chantiers.view',
'employes.view',
'materiels.view',
'planning.view',
'dashboard.chantier',
],
[BTP_ROLES.CHEF_EQUIPE]: [
'chantiers.view',
'employes.view',
'materiels.view',
'planning.view',
'dashboard.equipe',
],
[BTP_ROLES.COMMERCIAL]: [
'clients.manage',
'devis.manage',
'factures.view',
'chantiers.view',
'dashboard.commercial',
],
[BTP_ROLES.COMPTABLE]: [
'factures.manage',
'devis.view',
'clients.view',
'reports.financial',
'dashboard.financial',
],
[BTP_ROLES.LOGISTICIEN]: [
'materiels.manage',
'chantiers.view',
'planning.view',
'dashboard.logistique',
],
[BTP_ROLES.EMPLOYE]: [
'chantiers.view',
'planning.view',
'profile.edit',
'dashboard.employe',
],
[BTP_ROLES.OUVRIER]: [
'chantiers.view',
'planning.view',
'profile.edit',
'dashboard.employe',
],
[BTP_ROLES.CLIENT_ENTREPRISE]: [
'chantiers.view.own',
'devis.view.own',
'factures.view.own',
'profile.edit',
'dashboard.client',
],
[BTP_ROLES.CLIENT_PARTICULIER]: [
'chantiers.view.own',
'devis.view.own',
'factures.view.own',
'profile.edit',
'dashboard.client',
],
[BTP_ROLES.VIEWER]: [
'chantiers.view',
'dashboard.readonly',
],
[BTP_ROLES.GUEST]: [
'dashboard.public',
],
} as const;
// Utilitaires pour la gestion des rôles
export const RoleUtils = {
/**
* Vérifie si l'utilisateur a un rôle spécifique
*/
hasRole: (userRoles: string[], requiredRole: string): boolean => {
return userRoles.includes(requiredRole);
},
/**
* Vérifie si l'utilisateur a au moins un des rôles requis
*/
hasAnyRole: (userRoles: string[], requiredRoles: string[]): boolean => {
return requiredRoles.some(role => userRoles.includes(role));
},
/**
* Vérifie si l'utilisateur a tous les rôles requis
*/
hasAllRoles: (userRoles: string[], requiredRoles: string[]): boolean => {
return requiredRoles.every(role => userRoles.includes(role));
},
/**
* Obtient le rôle le plus élevé de l'utilisateur
*/
getHighestRole: (userRoles: string[]): string | null => {
for (const role of ROLE_HIERARCHY) {
if (userRoles.includes(role)) {
return role;
}
}
return null;
},
/**
* Vérifie si l'utilisateur a une permission spécifique
*/
hasPermission: (userRoles: string[], permission: string): boolean => {
for (const role of userRoles) {
const rolePermissions = ROLE_PERMISSIONS[role as keyof typeof ROLE_PERMISSIONS];
if (rolePermissions?.includes('*') || rolePermissions?.includes(permission)) {
return true;
}
}
return false;
},
/**
* Obtient toutes les permissions de l'utilisateur
*/
getUserPermissions: (userRoles: string[]): string[] => {
const permissions = new Set<string>();
for (const role of userRoles) {
const rolePermissions = ROLE_PERMISSIONS[role as keyof typeof ROLE_PERMISSIONS];
if (rolePermissions) {
rolePermissions.forEach(permission => permissions.add(permission));
}
}
return Array.from(permissions);
},
/**
* Vérifie si un rôle est supérieur à un autre
*/
isRoleHigher: (role1: string, role2: string): boolean => {
const index1 = ROLE_HIERARCHY.indexOf(role1 as any);
const index2 = ROLE_HIERARCHY.indexOf(role2 as any);
return index1 !== -1 && index2 !== -1 && index1 < index2;
},
};
// Configuration des redirections après authentification
export const KEYCLOAK_REDIRECTS = {
LOGIN_SUCCESS: '/dashboard',
LOGIN_ERROR: '/auth/error',
LOGOUT_SUCCESS: '/auth/logout',
UNAUTHORIZED: '/auth/unauthorized',
FORBIDDEN: '/auth/forbidden',
} as const;
// Configuration des timeouts
export const KEYCLOAK_TIMEOUTS = {
TOKEN_REFRESH_BEFORE_EXPIRY: 30, // secondes
SESSION_CHECK_INTERVAL: 60, // secondes
SILENT_CHECK_SSO_TIMEOUT: 5000, // millisecondes
} as const;
export default keycloak;