398 lines
16 KiB
Python
398 lines
16 KiB
Python
"""
|
|
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
|