""" Tests unitaires pour le système de contrôle d'accès. Ces tests vérifient l'authentification, l'autorisation RBAC, et la gestion des sessions. """ import time from datetime import datetime, timedelta import pytest from pipeline_mco_pmsi.security import AccessControl, Role, Permission @pytest.fixture def access_control(): """Crée une instance d'AccessControl pour les tests.""" return AccessControl(session_duration_hours=1) @pytest.fixture def sample_users(access_control): """Crée des utilisateurs de test.""" tim = access_control.create_user( username="tim_user", password="password123", role=Role.TIM, email="tim@example.com", full_name="TIM User" ) responsable = access_control.create_user( username="responsable_user", password="password456", role=Role.RESPONSABLE_DIM, email="responsable@example.com", full_name="Responsable DIM" ) admin = access_control.create_user( username="admin_user", password="password789", role=Role.ADMINISTRATEUR, email="admin@example.com", full_name="Admin User" ) return {"tim": tim, "responsable": responsable, "admin": admin} class TestAccessControlInit: """Tests d'initialisation du système de contrôle d'accès.""" def test_init_with_defaults(self): """Test l'initialisation avec valeurs par défaut.""" ac = AccessControl() assert ac.session_duration_hours == 8 def test_init_with_custom_duration(self): """Test l'initialisation avec durée personnalisée.""" ac = AccessControl(session_duration_hours=12) assert ac.session_duration_hours == 12 class TestCreateUser: """Tests de création d'utilisateurs.""" def test_create_user_success(self, access_control): """Test la création réussie d'un utilisateur.""" user = access_control.create_user( username="test_user", password="test_password", role=Role.TIM, email="test@example.com", full_name="Test User" ) assert user.username == "test_user" assert user.role == Role.TIM assert user.email == "test@example.com" assert user.full_name == "Test User" assert user.is_active is True assert user.password_hash != "test_password" # Le mot de passe doit être hashé assert ":" in user.password_hash # Format salt:hash def test_create_user_duplicate_username(self, access_control): """Test que la création échoue si l'utilisateur existe déjà.""" access_control.create_user( username="duplicate_user", password="password", role=Role.TIM ) with pytest.raises(ValueError, match="existe déjà"): access_control.create_user( username="duplicate_user", password="password2", role=Role.RESPONSABLE_DIM ) def test_create_user_different_roles(self, access_control): """Test la création d'utilisateurs avec différents rôles.""" tim = access_control.create_user("tim", "pass", Role.TIM) responsable = access_control.create_user("resp", "pass", Role.RESPONSABLE_DIM) admin = access_control.create_user("admin", "pass", Role.ADMINISTRATEUR) assert tim.role == Role.TIM assert responsable.role == Role.RESPONSABLE_DIM assert admin.role == Role.ADMINISTRATEUR class TestAuthenticate: """Tests d'authentification.""" def test_authenticate_success(self, access_control, sample_users): """Test l'authentification réussie.""" session = access_control.authenticate("tim_user", "password123") assert session is not None assert session.user_id == sample_users["tim"].user_id assert session.session_id is not None assert len(session.session_id) > 0 assert session.expires_at > datetime.now() def test_authenticate_wrong_password(self, access_control, sample_users): """Test l'authentification avec mauvais mot de passe.""" session = access_control.authenticate("tim_user", "wrong_password") assert session is None def test_authenticate_unknown_user(self, access_control): """Test l'authentification avec utilisateur inconnu.""" session = access_control.authenticate("unknown_user", "password") assert session is None def test_authenticate_inactive_user(self, access_control, sample_users): """Test l'authentification avec compte inactif.""" # Désactiver l'utilisateur access_control.deactivate_user(sample_users["tim"].user_id) # Tenter de se connecter session = access_control.authenticate("tim_user", "password123") assert session is None def test_authenticate_with_ip_address(self, access_control, sample_users): """Test l'authentification avec adresse IP.""" session = access_control.authenticate( "tim_user", "password123", ip_address="192.168.1.1" ) assert session is not None assert session.ip_address == "192.168.1.1" class TestGetUserFromSession: """Tests de récupération d'utilisateur depuis une session.""" def test_get_user_from_valid_session(self, access_control, sample_users): """Test la récupération avec session valide.""" session = access_control.authenticate("tim_user", "password123") user = access_control.get_user_from_session(session.session_id) assert user is not None assert user.username == "tim_user" assert user.role == Role.TIM def test_get_user_from_invalid_session(self, access_control): """Test la récupération avec session invalide.""" user = access_control.get_user_from_session("invalid_session_id") assert user is None def test_get_user_from_expired_session(self, access_control, sample_users): """Test la récupération avec session expirée.""" # Créer un AccessControl avec durée très courte ac = AccessControl(session_duration_hours=0) ac.create_user("test", "pass", Role.TIM) # Créer une session qui expire immédiatement session = ac.authenticate("test", "pass") # Attendre que la session expire time.sleep(0.1) # Modifier manuellement l'expiration pour simuler une session expirée ac._sessions[session.session_id] = type(session)( session_id=session.session_id, user_id=session.user_id, created_at=session.created_at, expires_at=datetime.now() - timedelta(seconds=1), ip_address=session.ip_address ) user = ac.get_user_from_session(session.session_id) assert user is None # La session expirée devrait être supprimée assert session.session_id not in ac._sessions class TestHasPermission: """Tests de vérification des permissions.""" def test_tim_has_view_codes_permission(self, access_control, sample_users): """Test que TIM a la permission VIEW_CODES.""" session = access_control.authenticate("tim_user", "password123") assert access_control.has_permission(session.session_id, Permission.VIEW_CODES) is True def test_tim_has_correct_code_permission(self, access_control, sample_users): """Test que TIM a la permission CORRECT_CODE.""" session = access_control.authenticate("tim_user", "password123") assert access_control.has_permission(session.session_id, Permission.CORRECT_CODE) is True def test_tim_no_manage_users_permission(self, access_control, sample_users): """Test que TIM n'a pas la permission MANAGE_USERS.""" session = access_control.authenticate("tim_user", "password123") assert access_control.has_permission(session.session_id, Permission.MANAGE_USERS) is False def test_responsable_has_export_audit_permission(self, access_control, sample_users): """Test que Responsable DIM a la permission EXPORT_AUDIT.""" session = access_control.authenticate("responsable_user", "password456") assert access_control.has_permission(session.session_id, Permission.EXPORT_AUDIT) is True def test_responsable_no_manage_users_permission(self, access_control, sample_users): """Test que Responsable DIM n'a pas la permission MANAGE_USERS.""" session = access_control.authenticate("responsable_user", "password456") assert access_control.has_permission(session.session_id, Permission.MANAGE_USERS) is False def test_admin_has_all_permissions(self, access_control, sample_users): """Test que Admin a toutes les permissions.""" session = access_control.authenticate("admin_user", "password789") # Tester quelques permissions assert access_control.has_permission(session.session_id, Permission.VIEW_CODES) is True assert access_control.has_permission(session.session_id, Permission.MANAGE_USERS) is True assert access_control.has_permission(session.session_id, Permission.EXPORT_AUDIT) is True assert access_control.has_permission(session.session_id, Permission.MANAGE_REFERENTIELS) is True def test_has_permission_invalid_session(self, access_control): """Test la vérification avec session invalide.""" assert access_control.has_permission("invalid_session", Permission.VIEW_CODES) is False class TestRequirePermission: """Tests de vérification stricte des permissions.""" def test_require_permission_success(self, access_control, sample_users): """Test la vérification réussie d'une permission.""" session = access_control.authenticate("tim_user", "password123") user = access_control.require_permission(session.session_id, Permission.VIEW_CODES) assert user is not None assert user.username == "tim_user" def test_require_permission_denied(self, access_control, sample_users): """Test la vérification échouée d'une permission.""" session = access_control.authenticate("tim_user", "password123") with pytest.raises(PermissionError, match="Permission refusée"): access_control.require_permission(session.session_id, Permission.MANAGE_USERS) def test_require_permission_invalid_session(self, access_control): """Test la vérification avec session invalide.""" with pytest.raises(PermissionError, match="Session invalide"): access_control.require_permission("invalid_session", Permission.VIEW_CODES) class TestLogout: """Tests de déconnexion.""" def test_logout_success(self, access_control, sample_users): """Test la déconnexion réussie.""" session = access_control.authenticate("tim_user", "password123") result = access_control.logout(session.session_id) assert result is True # La session ne devrait plus exister user = access_control.get_user_from_session(session.session_id) assert user is None def test_logout_invalid_session(self, access_control): """Test la déconnexion avec session invalide.""" result = access_control.logout("invalid_session") assert result is False class TestDeactivateUser: """Tests de désactivation d'utilisateurs.""" def test_deactivate_user_success(self, access_control, sample_users): """Test la désactivation réussie d'un utilisateur.""" # Créer une session session = access_control.authenticate("tim_user", "password123") # Désactiver l'utilisateur result = access_control.deactivate_user(sample_users["tim"].user_id) assert result is True # La session devrait être supprimée user = access_control.get_user_from_session(session.session_id) assert user is None # L'utilisateur ne devrait plus pouvoir se connecter new_session = access_control.authenticate("tim_user", "password123") assert new_session is None def test_deactivate_user_invalid_id(self, access_control): """Test la désactivation avec ID invalide.""" result = access_control.deactivate_user("invalid_user_id") assert result is False class TestGetUserPermissions: """Tests de récupération des permissions.""" def test_get_tim_permissions(self, access_control, sample_users): """Test la récupération des permissions TIM.""" session = access_control.authenticate("tim_user", "password123") permissions = access_control.get_user_permissions(session.session_id) assert Permission.VIEW_CODES in permissions assert Permission.CORRECT_CODE in permissions assert Permission.VALIDATE_STAY in permissions assert Permission.MANAGE_USERS not in permissions def test_get_responsable_permissions(self, access_control, sample_users): """Test la récupération des permissions Responsable DIM.""" session = access_control.authenticate("responsable_user", "password456") permissions = access_control.get_user_permissions(session.session_id) assert Permission.VIEW_CODES in permissions assert Permission.EXPORT_AUDIT in permissions assert Permission.MANAGE_RULES in permissions assert Permission.MANAGE_USERS not in permissions def test_get_admin_permissions(self, access_control, sample_users): """Test la récupération des permissions Admin.""" session = access_control.authenticate("admin_user", "password789") permissions = access_control.get_user_permissions(session.session_id) # Admin devrait avoir toutes les permissions assert len(permissions) == len(Permission) assert Permission.MANAGE_USERS in permissions def test_get_permissions_invalid_session(self, access_control): """Test la récupération avec session invalide.""" permissions = access_control.get_user_permissions("invalid_session") assert len(permissions) == 0 class TestPasswordHashing: """Tests de hachage des mots de passe.""" def test_password_is_hashed(self, access_control): """Test que le mot de passe est hashé.""" user = access_control.create_user("test", "password123", Role.TIM) # Le hash ne devrait pas être le mot de passe en clair assert user.password_hash != "password123" # Le hash devrait contenir un salt assert ":" in user.password_hash def test_same_password_different_hashes(self, access_control): """Test que le même mot de passe produit des hashes différents (salt aléatoire).""" user1 = access_control.create_user("user1", "password", Role.TIM) user2 = access_control.create_user("user2", "password", Role.TIM) # Les hashes devraient être différents (salt différent) assert user1.password_hash != user2.password_hash def test_password_verification(self, access_control): """Test la vérification du mot de passe.""" user = access_control.create_user("test", "password123", Role.TIM) # Le bon mot de passe devrait fonctionner assert access_control._verify_password("password123", user.password_hash) is True # Un mauvais mot de passe ne devrait pas fonctionner assert access_control._verify_password("wrong_password", user.password_hash) is False