Files
ACORE/core/tests_security.py

147 lines
5.3 KiB
Python

"""Тесты hardening безопасности ACORE."""
from datetime import date
from unittest import mock
from django.contrib.auth.models import Group
from django.test import Client, TestCase
from apps.employees.models import Employee
from core.security import user_requires_mandatory_2fa
def make_employee(**overrides):
n = Employee.objects.count() + 1
defaults = {
'username': f'sec.user{n}',
'email': f'sec.user{n}@company.local',
'first_name': 'Тест',
'last_name': 'Безопасность',
'hire_date': date(2026, 1, 1),
'user_location_type': 'pharmacy',
'mattermost_sync_enabled': False,
'ad_sync_enabled': False,
'is_staff': False,
}
defaults.update(overrides)
return Employee.objects.create(**defaults)
from core.test_utils import login_2fa
class Mandatory2FATests(TestCase):
def test_staff_requires_2fa(self):
user = make_employee(is_staff=True)
self.assertTrue(user_requires_mandatory_2fa(user))
def test_hr_group_does_not_require_mandatory_2fa(self):
user = make_employee()
user.groups.add(Group.objects.get(name='HR'))
if hasattr(user, '_group_perm_cache'):
del user._group_perm_cache
self.assertFalse(user_requires_mandatory_2fa(user))
def test_it_group_requires_2fa_without_staff(self):
user = make_employee()
user.groups.add(Group.objects.get(name='IT'))
if hasattr(user, '_group_perm_cache'):
del user._group_perm_cache
self.assertTrue(user_requires_mandatory_2fa(user))
def test_regular_user_without_totp_not_required(self):
user = make_employee()
self.assertFalse(user_requires_mandatory_2fa(user))
def test_hr_portal_accessible_without_2fa(self):
user = make_employee()
user.groups.add(Group.objects.get(name='HR'))
client = Client()
client.force_login(user)
response = client.get('/hr-portal/', HTTP_HX_REQUEST='true')
self.assertEqual(response.status_code, 200)
class HRPortalITSeparationTests(TestCase):
def test_hr_sees_ticket_number_without_it_manager_link(self):
from apps.hiring.services import create_hiring_request
from apps.locations.models import Pharmacy
from core.models import Directory
hr_user = make_employee(user_location_type='office')
hr_user.groups.add(Group.objects.get(name='HR'))
dept = Directory.objects.filter(directory_type__code='department').first()
if dept:
hr_user.department = dept
hr_user.save(update_fields=['department'])
pharmacy = Pharmacy.objects.filter(is_active=True).first()
if not pharmacy:
self.skipTest('no pharmacy fixture')
create_hiring_request(
requester=hr_user,
last_name='Тест',
first_name='Кандидат',
middle_name='',
inn_code='0000000023',
user_location_type='pharmacy',
user_pharmacy_id=pharmacy.pk,
hire_date=date(2026, 6, 1),
trial_days=3,
)
client = Client()
client.force_login(hr_user)
response = client.get('/hr-portal/', HTTP_HX_REQUEST='true')
self.assertEqual(response.status_code, 200)
self.assertNotIn('href="/it-manager/"', response.content.decode())
class ModalAddEmployeeSecurityTests(TestCase):
def test_regular_user_forbidden(self):
user = make_employee()
login_2fa(self.client, user)
response = self.client.get('/modal-add-employee/')
self.assertEqual(response.status_code, 403)
def test_hr_user_allowed(self):
user = make_employee()
user.groups.add(Group.objects.get(name='HR'))
self.client.force_login(user)
response = self.client.get('/modal-add-employee/')
self.assertEqual(response.status_code, 200)
class ADSyncLogPasswordTests(TestCase):
def test_create_log_does_not_contain_password(self):
from apps.ad_connector.services.sync_service import sync_employee_to_ad
from apps.ad_connector.models import ADSyncLog
employee = make_employee(
user_location_type='office',
email='office@test.local',
department_id=None,
)
from core.models import Directory
dept = Directory.objects.filter(directory_type__code='department').first()
mock_service = mock.MagicMock()
mock_service.use_ssl = True
mock_service.users_ou = 'OU=Users,DC=test,DC=local'
mock_service.domain = 'test.local'
mock_service.get_user_by_dn.return_value = None
mock_service.get_user_by_sam_account_name.return_value = None
mock_service.get_user_by_email.return_value = None
with mock.patch('apps.ad_connector.services.sync_service.ADService', return_value=mock_service):
with mock.patch(
'apps.ad_connector.services.sync_service.generate_random_password',
return_value='SecretPass123',
):
sync_employee_to_ad(employee)
log = ADSyncLog.objects.filter(employee=employee, action='create').latest('created_at')
self.assertNotIn('SecretPass123', log.message)
employee.refresh_from_db()
self.assertNotIn('SecretPass123', employee.ad_sync_error or '')