a75fa45585
- Add 13 tests for security.py (logs, stats, filtering, error handling) - Add 5 tests for system.py (stats, audit log, notifications, token) - Test security log parsing (JSON and logfmt formats) - Test audit log classification and noise filtering - Test system stats format and data integrity - Improve test coverage for security-sensitive endpoints
146 lines
6.7 KiB
Python
146 lines
6.7 KiB
Python
"""Integration tests for security router"""
|
|
|
|
import pytest
|
|
|
|
|
|
def test_security_logs_endpoint(test_app, admin_headers, mock_docker_client):
|
|
"""Test security logs endpoint returns expected format"""
|
|
# Mock Authelia container logs
|
|
mock_container = mock_docker_client.containers.get.return_value
|
|
mock_container.logs.return_value = b'{"level":"error","msg":"Unsuccessful 1FA authentication attempt","time":"2024-01-01T10:00:00Z","username":"testuser","remote_ip":"1.2.3.4"}\n'
|
|
|
|
response = test_app.get("/api/security/logs", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "logs" in data
|
|
assert isinstance(data["logs"], list)
|
|
|
|
|
|
def test_security_logs_with_filters(test_app, admin_headers, mock_docker_client):
|
|
"""Test security logs with type and IP filters"""
|
|
mock_container = mock_docker_client.containers.get.return_value
|
|
mock_container.logs.return_value = b'{"level":"error","msg":"Unsuccessful 1FA authentication attempt","time":"2024-01-01T10:00:00Z","username":"testuser","remote_ip":"1.2.3.4"}\n'
|
|
|
|
response = test_app.get("/api/security/logs?type=auth_failure&ip=1.2.3.4", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "logs" in data
|
|
|
|
|
|
def test_security_logs_with_date_range(test_app, admin_headers, mock_docker_client):
|
|
"""Test security logs with date range filter"""
|
|
mock_container = mock_docker_client.containers.get.return_value
|
|
mock_container.logs.return_value = b'{"level":"error","msg":"Unsuccessful 1FA authentication attempt","time":"2024-01-01T10:00:00Z","username":"testuser","remote_ip":"1.2.3.4"}\n'
|
|
|
|
response = test_app.get(
|
|
"/api/security/logs?start_date=2024-01-01&end_date=2024-01-31", headers=admin_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "logs" in data
|
|
|
|
|
|
def test_security_logs_tail_limit(test_app, admin_headers, mock_docker_client):
|
|
"""Test security logs respects tail and limit parameters"""
|
|
mock_container = mock_docker_client.containers.get.return_value
|
|
mock_container.logs.return_value = b'{"level":"error","msg":"Unsuccessful 1FA authentication attempt","time":"2024-01-01T10:00:00Z","username":"testuser","remote_ip":"1.2.3.4"}\n'
|
|
|
|
response = test_app.get("/api/security/logs?tail=100&limit=50", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "logs" in data
|
|
assert len(data["logs"]) <= 50
|
|
|
|
|
|
def test_security_logs_handles_missing_container(test_app, admin_headers, mock_docker_client):
|
|
"""Test security logs handles missing Authelia container gracefully"""
|
|
import docker.errors
|
|
|
|
mock_docker_client.containers.get.side_effect = docker.errors.NotFound("Container not found")
|
|
|
|
response = test_app.get("/api/security/logs", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "logs" in data
|
|
assert isinstance(data["logs"], list)
|
|
|
|
|
|
def test_security_stats_endpoint(test_app, admin_headers, mock_docker_client):
|
|
"""Test security stats endpoint returns expected format"""
|
|
mock_container = mock_docker_client.containers.get.return_value
|
|
mock_container.logs.return_value = b'{"level":"error","msg":"Unsuccessful 1FA authentication attempt","time":"2024-01-01T10:00:00Z","username":"testuser","remote_ip":"1.2.3.4"}\n'
|
|
|
|
response = test_app.get("/api/security/stats", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "failed_logins_24h" in data
|
|
assert "suspicious_requests" in data
|
|
assert "unique_ips" in data
|
|
assert "top_ips" in data
|
|
assert "timeline" in data
|
|
assert isinstance(data["top_ips"], list)
|
|
assert isinstance(data["timeline"], list)
|
|
|
|
|
|
def test_security_stats_timeline_format(test_app, admin_headers, mock_docker_client):
|
|
"""Test security stats timeline has correct format"""
|
|
mock_container = mock_docker_client.containers.get.return_value
|
|
mock_container.logs.return_value = b'{"level":"error","msg":"Unsuccessful 1FA authentication attempt","time":"2024-01-01T10:00:00Z","username":"testuser","remote_ip":"1.2.3.4"}\n'
|
|
|
|
response = test_app.get("/api/security/stats", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert len(data["timeline"]) == 24 # 24 hours
|
|
for entry in data["timeline"]:
|
|
assert "hours_ago" in entry
|
|
assert "count" in entry
|
|
|
|
|
|
def test_security_stats_top_ips_format(test_app, admin_headers, mock_docker_client):
|
|
"""Test security stats top IPs have correct format"""
|
|
mock_container = mock_docker_client.containers.get.return_value
|
|
mock_container.logs.return_value = b'{"level":"error","msg":"Unsuccessful 1FA authentication attempt","time":"2024-01-01T10:00:00Z","username":"testuser","remote_ip":"1.2.3.4"}\n'
|
|
|
|
response = test_app.get("/api/security/stats", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
for entry in data["top_ips"]:
|
|
assert "ip" in entry
|
|
assert "count" in entry
|
|
|
|
|
|
def test_security_logs_parses_logfmt(test_app, admin_headers, mock_docker_client):
|
|
"""Test security logs can parse logfmt format"""
|
|
mock_container = mock_docker_client.containers.get.return_value
|
|
mock_container.logs.return_value = (
|
|
b'level=error msg="Unsuccessful 1FA authentication attempt" time=2024-01-01T10:00:00Z username=testuser remote_ip=1.2.3.4\n'
|
|
)
|
|
|
|
response = test_app.get("/api/security/logs", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "logs" in data
|
|
|
|
|
|
def test_security_logs_filters_suspicious_paths(test_app, admin_headers, mock_docker_client):
|
|
"""Test security logs identifies suspicious paths"""
|
|
mock_container = mock_docker_client.containers.get.return_value
|
|
# Simulate Caddy log with suspicious path (though Caddy logs are disabled in current implementation)
|
|
mock_container.logs.return_value = b'{"level":"error","msg":"Unsuccessful 1FA authentication attempt","time":"2024-01-01T10:00:00Z","username":"testuser","remote_ip":"1.2.3.4"}\n'
|
|
|
|
response = test_app.get("/api/security/logs", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "logs" in data
|
|
|
|
|
|
def test_security_stats_handles_errors(test_app, admin_headers, mock_docker_client):
|
|
"""Test security stats handles Docker errors gracefully"""
|
|
mock_docker_client.containers.get.side_effect = Exception("Docker error")
|
|
|
|
response = test_app.get("/api/security/stats", headers=admin_headers)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "failed_logins_24h" in data
|
|
assert data["failed_logins_24h"] == 0
|