Initial commit

This commit is contained in:
dahoud
2025-10-01 01:39:07 +00:00
commit b430bf3b96
826 changed files with 255287 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
'use client';
import React, { createContext, useContext, useEffect, useState } from 'react';
interface DevAuthContextType {
isAuthenticated: boolean;
user: any;
login: () => void;
logout: () => void;
hasRole: (role: string) => boolean;
}
const DevAuthContext = createContext<DevAuthContextType | null>(null);
export const useDevAuth = () => {
const context = useContext(DevAuthContext);
if (!context) {
throw new Error('useDevAuth must be used within DevAuthProvider');
}
return context;
};
interface DevAuthProviderProps {
children: React.ReactNode;
}
export const DevAuthProvider: React.FC<DevAuthProviderProps> = ({ children }) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [user, setUser] = useState(null);
useEffect(() => {
// En mode développement, simuler un utilisateur connecté
if (process.env.NODE_ENV === 'development') {
setIsAuthenticated(true);
setUser({
id: 'dev-user-1',
username: 'admin',
email: 'admin@btpxpress.dev',
firstName: 'Admin',
lastName: 'BTPXpress',
roles: ['admin', 'manager', 'user'],
permissions: ['*']
});
}
}, []);
const login = () => {
setIsAuthenticated(true);
setUser({
id: 'dev-user-1',
username: 'admin',
email: 'admin@btpxpress.dev',
firstName: 'Admin',
lastName: 'BTPXpress',
roles: ['admin', 'manager', 'user'],
permissions: ['*']
});
};
const logout = () => {
setIsAuthenticated(false);
setUser(null);
};
const hasRole = (role: string) => {
return user?.roles?.includes(role) || user?.roles?.includes('admin') || false;
};
return (
<DevAuthContext.Provider value={{
isAuthenticated,
user,
login,
logout,
hasRole
}}>
{children}
</DevAuthContext.Provider>
);
};

View File

@@ -0,0 +1,237 @@
'use client';
import React, { ReactNode, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '@/contexts/AuthContext';
import { KEYCLOAK_REDIRECTS } from '@/config/keycloak';
import LoadingSpinner from '@/components/ui/LoadingSpinner';
// Props pour le composant de route protégée
interface ProtectedRouteProps {
children: ReactNode;
requiredRoles?: string[];
requiredPermissions?: string[];
requireAnyRole?: boolean; // Si true, l'utilisateur doit avoir au moins un des rôles requis
requireAllRoles?: boolean; // Si true, l'utilisateur doit avoir tous les rôles requis
requireAnyPermission?: boolean; // Si true, l'utilisateur doit avoir au moins une des permissions requises
requireAllPermissions?: boolean; // Si true, l'utilisateur doit avoir toutes les permissions requises
fallbackComponent?: ReactNode;
redirectTo?: string;
showUnauthorized?: boolean;
}
// Composant de route protégée
const ProtectedRoute: React.FC<ProtectedRouteProps> = ({
children,
requiredRoles = [],
requiredPermissions = [],
requireAnyRole = true,
requireAllRoles = false,
requireAnyPermission = true,
requireAllPermissions = false,
fallbackComponent,
redirectTo,
showUnauthorized = true,
}) => {
const router = useRouter();
const {
isAuthenticated,
isLoading,
user,
hasRole,
hasAnyRole,
hasPermission,
login
} = useAuth();
// Vérification de l'authentification et des autorisations
useEffect(() => {
if (isLoading) return;
// Si l'utilisateur n'est pas authentifié
if (!isAuthenticated) {
if (redirectTo) {
router.push(redirectTo);
} else {
// Rediriger vers la page de connexion
login();
}
return;
}
// Si l'utilisateur est authentifié mais n'a pas les rôles requis
if (requiredRoles.length > 0 && user) {
let hasRequiredRoles = false;
if (requireAllRoles) {
// L'utilisateur doit avoir tous les rôles requis
hasRequiredRoles = requiredRoles.every(role => hasRole(role));
} else if (requireAnyRole) {
// L'utilisateur doit avoir au moins un des rôles requis
hasRequiredRoles = hasAnyRole(requiredRoles);
}
if (!hasRequiredRoles) {
if (redirectTo) {
router.push(redirectTo);
} else {
router.push(KEYCLOAK_REDIRECTS.FORBIDDEN);
}
return;
}
}
// Si l'utilisateur est authentifié mais n'a pas les permissions requises
if (requiredPermissions.length > 0 && user) {
let hasRequiredPermissions = false;
if (requireAllPermissions) {
// L'utilisateur doit avoir toutes les permissions requises
hasRequiredPermissions = requiredPermissions.every(permission => hasPermission(permission));
} else if (requireAnyPermission) {
// L'utilisateur doit avoir au moins une des permissions requises
hasRequiredPermissions = requiredPermissions.some(permission => hasPermission(permission));
}
if (!hasRequiredPermissions) {
if (redirectTo) {
router.push(redirectTo);
} else {
router.push(KEYCLOAK_REDIRECTS.FORBIDDEN);
}
return;
}
}
}, [
isAuthenticated,
isLoading,
user,
requiredRoles,
requiredPermissions,
requireAnyRole,
requireAllRoles,
requireAnyPermission,
requireAllPermissions,
redirectTo,
router,
hasRole,
hasAnyRole,
hasPermission,
login,
]);
// Affichage pendant le chargement
if (isLoading) {
return (
<div className="flex items-center justify-center min-h-screen">
<LoadingSpinner size="large" />
</div>
);
}
// Si l'utilisateur n'est pas authentifié
if (!isAuthenticated) {
if (fallbackComponent) {
return <>{fallbackComponent}</>;
}
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<h2 className="text-2xl font-bold text-gray-900 mb-4">
Authentification requise
</h2>
<p className="text-gray-600 mb-6">
Vous devez être connecté pour accéder à cette page.
</p>
<button
onClick={login}
className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
Se connecter
</button>
</div>
</div>
);
}
// Vérification des rôles
if (requiredRoles.length > 0 && user) {
let hasRequiredRoles = false;
if (requireAllRoles) {
hasRequiredRoles = requiredRoles.every(role => hasRole(role));
} else if (requireAnyRole) {
hasRequiredRoles = hasAnyRole(requiredRoles);
}
if (!hasRequiredRoles) {
if (fallbackComponent) {
return <>{fallbackComponent}</>;
}
if (showUnauthorized) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<h2 className="text-2xl font-bold text-red-600 mb-4">
Accès non autorisé
</h2>
<p className="text-gray-600 mb-6">
Vous n'avez pas les permissions nécessaires pour accéder à cette page.
</p>
<p className="text-sm text-gray-500">
Rôles requis: {requiredRoles.join(', ')}
</p>
<p className="text-sm text-gray-500">
Vos rôles: {user.roles.join(', ')}
</p>
</div>
</div>
);
}
return null;
}
}
// Vérification des permissions
if (requiredPermissions.length > 0 && user) {
let hasRequiredPermissions = false;
if (requireAllPermissions) {
hasRequiredPermissions = requiredPermissions.every(permission => hasPermission(permission));
} else if (requireAnyPermission) {
hasRequiredPermissions = requiredPermissions.some(permission => hasPermission(permission));
}
if (!hasRequiredPermissions) {
if (fallbackComponent) {
return <>{fallbackComponent}</>;
}
if (showUnauthorized) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<h2 className="text-2xl font-bold text-red-600 mb-4">
Permissions insuffisantes
</h2>
<p className="text-gray-600 mb-6">
Vous n'avez pas les permissions nécessaires pour accéder à cette page.
</p>
<p className="text-sm text-gray-500">
Permissions requises: {requiredPermissions.join(', ')}
</p>
<p className="text-sm text-gray-500">
Vos permissions: {user.permissions.join(', ')}
</p>
</div>
</div>
);
}
return null;
}
}
// Si toutes les vérifications passent, afficher le contenu
return <>{children}</>;
};
export default ProtectedRoute;