7b04ca7477
Co-authored-by: Cursor <cursoragent@cursor.com>
147 lines
5.3 KiB
Python
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 '')
|