Files
pyMC_Repeater/tests/test_auth_components.py
T

131 lines
4.3 KiB
Python

from types import SimpleNamespace
from unittest.mock import MagicMock
import cherrypy
import jwt
import pytest
from repeater.web.auth.api_tokens import APITokenManager
from repeater.web.auth.cherrypy_tool import check_auth
from repeater.web.auth.jwt_handler import JWTHandler
def test_jwt_handler_create_and_verify_and_invalid_cases():
secret = "test-secret-key-minimum-32-bytes!!"
h = JWTHandler(secret, expiry_minutes=15)
token = h.create_jwt("admin", "client-1")
payload = h.verify_jwt(token)
assert payload is not None
assert payload["sub"] == "admin"
assert payload["client_id"] == "client-1"
expired = jwt.encode(
{"sub": "admin", "client_id": "c", "iat": 1, "exp": 1}, secret, algorithm="HS256"
)
assert h.verify_jwt(expired) is None
assert h.verify_jwt("not-a-token") is None
def test_api_token_manager_happy_paths_and_revoke_false():
db = SimpleNamespace(
create_api_token=MagicMock(return_value=10),
verify_api_token=MagicMock(return_value={"id": 10, "name": "n1"}),
revoke_api_token=MagicMock(side_effect=[True, False]),
list_api_tokens=MagicMock(return_value=[{"id": 10, "name": "n1"}]),
)
mgr = APITokenManager(sqlite_handler=db, secret_key="k")
token_id, plaintext = mgr.create_token("n1")
assert token_id == 10
assert isinstance(plaintext, str)
assert len(plaintext) == 64
verified = mgr.verify_token(plaintext)
assert verified["id"] == 10
assert mgr.revoke_token(10) is True
assert mgr.revoke_token(11) is False
assert mgr.list_tokens()[0]["name"] == "n1"
def _set_cp(monkeypatch, method="GET", path="/api/private", headers=None, params=None, cfg=None):
req = SimpleNamespace(
method=method,
path_info=path,
headers=headers or {},
params=params or {},
user=None,
)
resp = SimpleNamespace(status=200, headers={})
monkeypatch.setattr(cherrypy, "request", req, raising=False)
monkeypatch.setattr(cherrypy, "response", resp, raising=False)
monkeypatch.setattr(cherrypy, "config", cfg or {}, raising=False)
return req, resp
def test_check_auth_skips_options_and_login(monkeypatch):
_set_cp(monkeypatch, method="OPTIONS")
assert check_auth() is None
_set_cp(monkeypatch, method="GET", path="/auth/login")
assert check_auth() is None
def test_check_auth_missing_handlers_raises_http_500(monkeypatch):
_set_cp(monkeypatch, cfg={})
with pytest.raises(cherrypy.HTTPError) as exc_info:
check_auth()
assert exc_info.value.status == 500
def test_check_auth_accepts_bearer_token(monkeypatch):
jwt_handler = SimpleNamespace(verify_jwt=lambda _t: {"sub": "admin", "client_id": "c1"})
token_manager = SimpleNamespace(verify_token=lambda _k: None)
req, _resp = _set_cp(
monkeypatch,
headers={"Authorization": "Bearer abc"},
cfg={"jwt_handler": jwt_handler, "token_manager": token_manager},
)
assert check_auth() is None
assert req.user["auth_type"] == "jwt"
def test_check_auth_accepts_query_token_and_removes_it(monkeypatch):
jwt_handler = SimpleNamespace(verify_jwt=lambda _t: {"sub": "admin", "client_id": "c2"})
token_manager = SimpleNamespace(verify_token=lambda _k: None)
req, _resp = _set_cp(
monkeypatch,
params={"token": "xyz", "x": "1"},
cfg={"jwt_handler": jwt_handler, "token_manager": token_manager},
)
assert check_auth() is None
assert req.user["auth_type"] == "jwt_query"
assert "token" not in req.params
def test_check_auth_accepts_api_key(monkeypatch):
jwt_handler = SimpleNamespace(verify_jwt=lambda _t: None)
token_manager = SimpleNamespace(verify_token=lambda _k: {"id": 3, "name": "svc"})
req, _resp = _set_cp(
monkeypatch,
headers={"X-API-Key": "k"},
cfg={"jwt_handler": jwt_handler, "token_manager": token_manager},
)
assert check_auth() is None
assert req.user["auth_type"] == "api_token"
def test_check_auth_unauthorized_raises_http_error(monkeypatch):
jwt_handler = SimpleNamespace(verify_jwt=lambda _t: None)
token_manager = SimpleNamespace(verify_token=lambda _k: None)
_set_cp(monkeypatch, cfg={"jwt_handler": jwt_handler, "token_manager": token_manager})
with pytest.raises(cherrypy.HTTPError):
check_auth()