first commit
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import pytest
|
||||
|
||||
from app.core.config import Settings
|
||||
from app.services.auth_service import (
|
||||
derive_refresh_token,
|
||||
hash_password,
|
||||
issue_refresh_token,
|
||||
issue_token,
|
||||
verify_password,
|
||||
verify_token,
|
||||
)
|
||||
|
||||
|
||||
def settings(**overrides):
|
||||
return Settings(
|
||||
target_base_url="https://target.test",
|
||||
judge_base_url="https://judge.test",
|
||||
**overrides,
|
||||
)
|
||||
|
||||
|
||||
def test_password_hash_is_salted_and_verifiable():
|
||||
encoded = hash_password("correct-horse-battery-staple")
|
||||
assert encoded.startswith("pbkdf2_sha256$600000$")
|
||||
assert "correct-horse-battery-staple" not in encoded
|
||||
assert verify_password("correct-horse-battery-staple", encoded)
|
||||
assert not verify_password("wrong", encoded)
|
||||
assert not verify_password("wrong", "not-a-password-hash")
|
||||
|
||||
|
||||
def test_signed_token_rejects_tampering_and_requires_secret_outside_tests():
|
||||
token, expires_at = issue_token(7, settings(auth_token_secret="x" * 32))
|
||||
assert expires_at > 0
|
||||
assert verify_token(token, settings(auth_token_secret="x" * 32)) == 7
|
||||
assert verify_token(token + "x", settings(auth_token_secret="x" * 32)) is None
|
||||
with pytest.raises(ValueError):
|
||||
issue_token(7, settings(app_env="production", auth_token_secret=""))
|
||||
with pytest.raises(ValueError):
|
||||
issue_token(7, settings(app_env="production", auth_token_secret="too-short"))
|
||||
|
||||
|
||||
def test_refresh_tokens_are_random_but_idempotent_rotation_is_stable():
|
||||
config = settings(auth_token_secret="x" * 32)
|
||||
first, second = issue_refresh_token(), issue_refresh_token()
|
||||
|
||||
assert first != second
|
||||
assert derive_refresh_token(first, "attempt-1", config) == derive_refresh_token(
|
||||
first, "attempt-1", config
|
||||
)
|
||||
assert derive_refresh_token(first, "attempt-1", config) != derive_refresh_token(
|
||||
first, "attempt-2", config
|
||||
)
|
||||
|
||||
|
||||
def test_session_timing_configuration_requires_refresh_window_before_expiry():
|
||||
assert settings().auth_token_ttl_seconds == 3600
|
||||
assert settings().auth_token_refresh_window_seconds == 1800
|
||||
assert settings().auth_refresh_token_ttl_seconds == 28800
|
||||
with pytest.raises(ValueError):
|
||||
settings(auth_token_ttl_seconds=1800, auth_token_refresh_window_seconds=1800)
|
||||
|
||||
|
||||
def test_create_user_cli_prompts_and_never_accepts_password_argument(monkeypatch):
|
||||
from app import create_user
|
||||
|
||||
created = {}
|
||||
|
||||
class FakeDatabase:
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *_):
|
||||
return None
|
||||
|
||||
class FakeRepository:
|
||||
def __init__(self, _):
|
||||
pass
|
||||
|
||||
def create(self, *, username, password, is_admin):
|
||||
created.update(username=username, password=password, is_admin=is_admin)
|
||||
|
||||
monkeypatch.setattr(create_user, "SessionLocal", FakeDatabase)
|
||||
monkeypatch.setattr(create_user, "UserRepository", FakeRepository)
|
||||
monkeypatch.setattr(create_user.getpass, "getpass", lambda _: "correct-horse-battery-staple")
|
||||
monkeypatch.setattr("sys.argv", ["create_user", "auditor"])
|
||||
create_user.main()
|
||||
assert created == {
|
||||
"username": "auditor",
|
||||
"password": "correct-horse-battery-staple",
|
||||
"is_admin": True,
|
||||
}
|
||||
Reference in New Issue
Block a user