-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauth_test.py
More file actions
117 lines (89 loc) · 3.98 KB
/
auth_test.py
File metadata and controls
117 lines (89 loc) · 3.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
"""Tests for auth.py."""
from __future__ import annotations
from pathlib import Path
from unittest import mock
import json
import pytest
@pytest.fixture
def auth_module(tmp_path: Path):
"""Import auth module with temp settings file."""
import auth
# Patch settings files to temp location
original_server = auth.SERVER_SETTINGS_FILE
original_users = auth.USERS_DIR
auth.SERVER_SETTINGS_FILE = tmp_path / "server_settings.json"
auth.USERS_DIR = tmp_path / "users"
auth.USERS_DIR.mkdir(exist_ok=True)
yield auth
auth.SERVER_SETTINGS_FILE = original_server
auth.USERS_DIR = original_users
class TestPasswordHashing:
def test_hash_password_creates_salt(self, auth_module):
hashed = auth_module._hash_password("mypassword")
assert ":" in hashed
salt, key = hashed.split(":")
assert len(salt) == 32 # 16 bytes hex
assert len(key) == 64 # 32 bytes hex
def test_hash_password_with_salt_deterministic(self, auth_module):
salt = "a" * 32
h1 = auth_module._hash_password("test", salt)
h2 = auth_module._hash_password("test", salt)
assert h1 == h2
def test_verify_hashed_password_correct(self, auth_module):
hashed = auth_module._hash_password("secret")
assert auth_module._verify_hashed_password("secret", hashed)
def test_verify_hashed_password_wrong(self, auth_module):
hashed = auth_module._hash_password("secret")
assert not auth_module._verify_hashed_password("wrong", hashed)
class TestUserManagement:
def test_is_setup_required_no_users(self, auth_module):
assert auth_module.is_setup_required()
def test_create_user_and_verify(self, auth_module):
auth_module.create_user("admin", "password123")
assert not auth_module.is_setup_required()
assert auth_module.verify_password("admin", "password123")
assert not auth_module.verify_password("admin", "wrongpass")
assert not auth_module.verify_password("nobody", "password123")
class TestTokens:
def test_create_and_verify_token(self, auth_module):
payload = {"user": "admin", "role": "admin"}
token = auth_module.create_token(payload)
result = auth_module.verify_token(token)
assert result is not None
assert result["user"] == "admin"
assert result["role"] == "admin"
assert "exp" in result
def test_token_format(self, auth_module):
token = auth_module.create_token({"test": 1})
assert "." in token
_, sig = token.split(".")
assert len(sig) == 64 # sha256 hex
def test_invalid_token_rejected(self, auth_module):
assert auth_module.verify_token("invalid") is None
assert auth_module.verify_token("abc.def") is None
assert auth_module.verify_token("") is None
def test_tampered_token_rejected(self, auth_module):
token = auth_module.create_token({"user": "admin"})
# Tamper with signature
data, _ = token.split(".")
tampered = f"{data}.{'0' * 64}"
assert auth_module.verify_token(tampered) is None
def test_expired_token_rejected(self, auth_module):
# Create token with expired time
with mock.patch.object(auth_module, "TOKEN_EXPIRY", -1):
token = auth_module.create_token({"user": "admin"})
assert auth_module.verify_token(token) is None
class TestSecretKey:
def test_get_secret_key_generates_and_persists(self, auth_module):
key1 = auth_module._get_secret_key()
assert len(key1) == 64 # 32 bytes hex
# Should return same key
key2 = auth_module._get_secret_key()
assert key1 == key2
# Should be persisted (uses _get_settings_file which returns legacy if server doesn't exist)
settings_file = auth_module._get_settings_file()
settings = json.loads(settings_file.read_text())
assert settings["secret_key"] == key1
if __name__ == "__main__":
from testing import run_tests
run_tests(__file__)