Initial commit
This commit is contained in:
80
components/auth/DevAuthProvider.tsx
Normal file
80
components/auth/DevAuthProvider.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
237
components/auth/ProtectedRoute.tsx
Normal file
237
components/auth/ProtectedRoute.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user