/** * 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(); 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;