snapshot: preserve session hardening and account surface
This commit is contained in:
26
backend/workflows/tests/test_account_ui.py
Normal file
26
backend/workflows/tests/test_account_ui.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import Client, TestCase
|
||||
|
||||
|
||||
class AccountUISmokeTests(TestCase):
|
||||
def setUp(self):
|
||||
self.user = get_user_model().objects.create_user(
|
||||
username='profile-user',
|
||||
email='profile@example.com',
|
||||
password='secret-12345',
|
||||
first_name='Profile',
|
||||
last_name='User',
|
||||
)
|
||||
self.client = Client()
|
||||
self.client.force_login(self.user)
|
||||
|
||||
def test_account_profile_page_renders(self):
|
||||
response = self.client.get('/account/', HTTP_HOST='localhost')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, 'profile@example.com')
|
||||
self.assertContains(response, 'Passwort ändern')
|
||||
|
||||
def test_password_change_page_renders(self):
|
||||
response = self.client.get('/accounts/password_change/', HTTP_HOST='localhost')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, 'Aktuelles Passwort')
|
||||
@@ -12,7 +12,7 @@ class OnboardingFlowTests(TestCase):
|
||||
self.user = user_model.objects.create_user(
|
||||
username='onboard_user',
|
||||
password='secret123',
|
||||
email='requester@tub.co',
|
||||
email='requester@workdock.de',
|
||||
first_name='Mia',
|
||||
last_name='Beispiel',
|
||||
)
|
||||
@@ -26,7 +26,7 @@ class OnboardingFlowTests(TestCase):
|
||||
'gender': 'herr',
|
||||
'job_title': 'Consultant',
|
||||
'department': 'IT-Service',
|
||||
'work_email': 'max.mustermann@tub.co',
|
||||
'work_email': 'max.mustermann@workdock.de',
|
||||
'contract_start': '2026-11-01',
|
||||
'employment_type': 'unbefristet',
|
||||
'group_mailboxes_required_choice': 'nein',
|
||||
@@ -43,8 +43,8 @@ class OnboardingFlowTests(TestCase):
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertIn('/onboarding/new/?saved=1&id=', response['Location'])
|
||||
|
||||
obj = OnboardingRequest.objects.get(work_email='max.mustermann@tub.co')
|
||||
obj = OnboardingRequest.objects.get(work_email='max.mustermann@workdock.de')
|
||||
self.assertEqual(obj.full_name, 'Max Mustermann')
|
||||
self.assertEqual(obj.onboarded_by_email, 'requester@tub.co')
|
||||
self.assertEqual(obj.onboarded_by_email, 'requester@workdock.de')
|
||||
self.assertEqual(obj.onboarded_by_name, 'Mia Beispiel')
|
||||
mock_delay.assert_called_once_with(obj.id)
|
||||
|
||||
96
backend/workflows/tests/test_security_hardening.py
Normal file
96
backend/workflows/tests/test_security_hardening.py
Normal file
@@ -0,0 +1,96 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import Client, TestCase, override_settings
|
||||
|
||||
from workflows.checks import security_settings_check
|
||||
|
||||
|
||||
@override_settings(DEBUG=True)
|
||||
class RateLimitMiddlewareTests(TestCase):
|
||||
@override_settings(RATE_LIMIT_LOGIN_LIMIT=2, RATE_LIMIT_LOGIN_WINDOW=60)
|
||||
def test_login_is_rate_limited(self):
|
||||
client = Client(REMOTE_ADDR='10.10.10.10')
|
||||
for _ in range(2):
|
||||
response = client.post('/accounts/login/', {'username': 'x', 'password': 'y'}, HTTP_HOST='localhost')
|
||||
self.assertNotEqual(response.status_code, 429)
|
||||
|
||||
response = client.post('/accounts/login/', {'username': 'x', 'password': 'y'}, HTTP_HOST='localhost')
|
||||
self.assertEqual(response.status_code, 429)
|
||||
self.assertIn('Retry-After', response)
|
||||
|
||||
@override_settings(RATE_LIMIT_PASSWORD_RESET_LIMIT=1, RATE_LIMIT_PASSWORD_RESET_WINDOW=60)
|
||||
def test_password_reset_is_rate_limited(self):
|
||||
client = Client(REMOTE_ADDR='10.10.10.20')
|
||||
response = client.post('/accounts/password_reset/', {'email': 'nobody@example.com'}, HTTP_HOST='localhost')
|
||||
self.assertNotEqual(response.status_code, 429)
|
||||
|
||||
response = client.post('/accounts/password_reset/', {'email': 'nobody@example.com'}, HTTP_HOST='localhost')
|
||||
self.assertEqual(response.status_code, 429)
|
||||
|
||||
@override_settings(RATE_LIMIT_ADMIN_ACTION_LIMIT=1, RATE_LIMIT_ADMIN_ACTION_WINDOW=60)
|
||||
def test_sensitive_admin_posts_are_rate_limited(self):
|
||||
client = Client(REMOTE_ADDR='10.10.10.30')
|
||||
response = client.post('/admin-tools/branding/save/', {'portal_title': 'A'}, HTTP_HOST='localhost')
|
||||
self.assertNotEqual(response.status_code, 429)
|
||||
|
||||
response = client.post('/admin-tools/branding/save/', {'portal_title': 'B'}, HTTP_HOST='localhost')
|
||||
self.assertEqual(response.status_code, 429)
|
||||
|
||||
@override_settings(RATE_LIMIT_LOGIN_LIMIT=1, RATE_LIMIT_LOGIN_WINDOW=60)
|
||||
def test_get_requests_are_not_rate_limited(self):
|
||||
client = Client(REMOTE_ADDR='10.10.10.40')
|
||||
first = client.get('/accounts/login/', HTTP_HOST='localhost')
|
||||
second = client.get('/accounts/login/', HTTP_HOST='localhost')
|
||||
self.assertEqual(first.status_code, 200)
|
||||
self.assertEqual(second.status_code, 200)
|
||||
|
||||
|
||||
class SecurityChecksTests(TestCase):
|
||||
@override_settings(
|
||||
DEBUG=False,
|
||||
SECRET_KEY='unsafe-dev-key',
|
||||
ALLOWED_HOSTS=[],
|
||||
SESSION_COOKIE_SECURE=False,
|
||||
CSRF_COOKIE_SECURE=False,
|
||||
SECURE_SSL_REDIRECT=False,
|
||||
RUN_SECURITY_CHECKS_DURING_TESTS=True,
|
||||
)
|
||||
def test_security_checks_report_production_issues(self):
|
||||
issues = security_settings_check(None)
|
||||
ids = {issue.id for issue in issues}
|
||||
self.assertIn('workdock.E001', ids)
|
||||
self.assertIn('workdock.E002', ids)
|
||||
self.assertIn('workdock.E003', ids)
|
||||
self.assertIn('workdock.E004', ids)
|
||||
self.assertIn('workdock.W001', ids)
|
||||
|
||||
|
||||
@override_settings(DEBUG=True)
|
||||
class AuthSessionHardeningTests(TestCase):
|
||||
def setUp(self):
|
||||
self.user = get_user_model().objects.create_user(username='security-user', password='secret-12345')
|
||||
|
||||
@override_settings(SESSION_IDLE_TIMEOUT_SECONDS=60)
|
||||
def test_idle_session_forces_relogin(self):
|
||||
client = Client(REMOTE_ADDR='10.10.10.50')
|
||||
client.force_login(self.user)
|
||||
session = client.session
|
||||
session['last_activity_ts'] = 1
|
||||
session['auth_fresh_ts'] = 1
|
||||
session.save()
|
||||
|
||||
response = client.get('/', HTTP_HOST='localhost')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertIn('/accounts/login/', response['Location'])
|
||||
|
||||
@override_settings(SENSITIVE_ACTION_REAUTH_SECONDS=60)
|
||||
def test_stale_sensitive_post_forces_relogin(self):
|
||||
client = Client(REMOTE_ADDR='10.10.10.60')
|
||||
client.force_login(self.user)
|
||||
session = client.session
|
||||
session['last_activity_ts'] = 9999999999
|
||||
session['auth_fresh_ts'] = 1
|
||||
session.save()
|
||||
|
||||
response = client.post('/admin-tools/branding/save/', {'portal_title': 'Blocked'}, HTTP_HOST='localhost')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertIn('/accounts/login/', response['Location'])
|
||||
Reference in New Issue
Block a user