feat(auth): audit login success and failure attempts [FASE-3.2]
This commit is contained in:
58
backend/tests/test_auth_login_audit.py
Normal file
58
backend/tests/test_auth_login_audit.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""Tests for login attempt auditing (SEC-009)."""
|
||||
|
||||
from app.models.audit import AuditLog
|
||||
|
||||
|
||||
def test_login_failed_creates_audit_entry(client, admin_user, db):
|
||||
response = client.post(
|
||||
"/api/v1/auth/login",
|
||||
data={"username": "admin", "password": "wrong"},
|
||||
headers={"X-Forwarded-For": "198.51.100.10", "User-Agent": "LoginAuditTest/1.0"},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
|
||||
log = (
|
||||
db.query(AuditLog)
|
||||
.filter(AuditLog.action == "LOGIN_FAILED")
|
||||
.order_by(AuditLog.timestamp.desc())
|
||||
.first()
|
||||
)
|
||||
assert log is not None
|
||||
assert log.entity_type == "auth"
|
||||
assert log.details["username"] == "admin"
|
||||
assert log.details["reason"] == "invalid_credentials"
|
||||
assert log.ip_address == "198.51.100.10"
|
||||
assert log.user_agent == "LoginAuditTest/1.0"
|
||||
assert log.integrity_hash
|
||||
|
||||
|
||||
def test_login_success_creates_audit_entry(client, admin_user, db):
|
||||
client.cookies.clear()
|
||||
response = client.post(
|
||||
"/api/v1/auth/login",
|
||||
data={"username": "admin", "password": "admin123"},
|
||||
headers={"X-Forwarded-For": "198.51.100.20"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
log = (
|
||||
db.query(AuditLog)
|
||||
.filter(AuditLog.action == "LOGIN_SUCCESS")
|
||||
.order_by(AuditLog.timestamp.desc())
|
||||
.first()
|
||||
)
|
||||
assert log is not None
|
||||
assert log.user_id == admin_user.id
|
||||
assert log.ip_address == "198.51.100.20"
|
||||
assert log.integrity_hash
|
||||
|
||||
|
||||
def test_login_unknown_user_still_audited(client, db):
|
||||
response = client.post(
|
||||
"/api/v1/auth/login",
|
||||
data={"username": "nobody", "password": "password"},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
log = db.query(AuditLog).filter(AuditLog.action == "LOGIN_FAILED").first()
|
||||
assert log is not None
|
||||
assert log.user_id is None
|
||||
Reference in New Issue
Block a user