mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-06-11 00:34:46 +02:00
131 lines
4.3 KiB
Python
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()
|