Skip to content

Commit 574123f

Browse files
committed
fix: check_user regression
1 parent 7e2a2e6 commit 574123f

2 files changed

Lines changed: 42 additions & 5 deletions

File tree

‎tests/test_pydantic.py‎

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
from types import SimpleNamespace
44
from unittest.mock import AsyncMock
55

6+
import httpx
67
import pytest
7-
from fastapi import Request
8-
from lnbits.core.models import User
8+
from fastapi import FastAPI, Request
9+
from fastapi.responses import JSONResponse
10+
from lnbits.core.models import Account, User
11+
from lnbits.decorators import check_account_exists, check_user_exists
12+
from lnbits.settings import settings
913

1014
from .. import views
1115
from ..models import ExtensionSettings, PaymentsFilters, ProxiesFilters
@@ -38,3 +42,36 @@ async def test_frontend_serializes_user_for_template(monkeypatch):
3842
context = await views.index(request, user)
3943
assert json.loads(context["user"])["id"] == user.id
4044
assert User.model_validate_json(context["user"]).created_at == now
45+
46+
47+
@pytest.mark.asyncio
48+
@pytest.mark.parametrize("is_super_user", [True, False])
49+
async def test_page_dependencies_supply_full_user_and_enforce_superuser(monkeypatch, is_super_user):
50+
monkeypatch.setattr(settings, "super_user", "owner")
51+
monkeypatch.setattr(settings, "lnbits_admin_users", [])
52+
account = Account(id="owner" if is_super_user else "other", password_hash="test-only")
53+
user = User(id=account.id, super_user=is_super_user, created_at=account.created_at, updated_at=account.updated_at)
54+
app = FastAPI()
55+
app.include_router(views.reverse_proxy_generic_router)
56+
# Preserve the real superuser guard, including its Account return type.
57+
app.dependency_overrides[check_account_exists] = lambda: account
58+
app.dependency_overrides[check_user_exists] = lambda: user
59+
renderer = SimpleNamespace(TemplateResponse=lambda name, context: JSONResponse(json.loads(context["user"])))
60+
monkeypatch.setattr(views, "reverse_proxy_renderer", lambda: renderer)
61+
monkeypatch.setattr(views, "get_settings", AsyncMock(return_value=ExtensionSettings()))
62+
cleanup = AsyncMock()
63+
monkeypatch.setattr(views, "cleanup_expired_proxies_and_sync_caddy", cleanup)
64+
65+
async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app), base_url="http://test") as client:
66+
response = await client.get("/")
67+
68+
if not is_super_user:
69+
assert response.status_code == 403
70+
cleanup.assert_not_awaited()
71+
return
72+
assert response.status_code == 200
73+
payload = response.json()
74+
assert payload["wallets"] == []
75+
assert payload["extensions"] == []
76+
assert payload["super_user"] is True
77+
assert "password_hash" not in payload

‎views.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from fastapi import APIRouter, Depends, Request
22
from fastapi.responses import HTMLResponse
33
from lnbits.core.models import User
4-
from lnbits.decorators import check_super_user
4+
from lnbits.decorators import check_super_user, check_user_exists
55
from lnbits.helpers import template_renderer
66

77
from .services import cleanup_expired_proxies_and_sync_caddy, get_settings
@@ -13,8 +13,8 @@ def reverse_proxy_renderer():
1313
return template_renderer(["reverse_proxy/templates"])
1414

1515

16-
@reverse_proxy_generic_router.get("/", response_class=HTMLResponse)
17-
async def index(req: Request, user: User = Depends(check_super_user)):
16+
@reverse_proxy_generic_router.get("/", response_class=HTMLResponse, dependencies=[Depends(check_super_user)])
17+
async def index(req: Request, user: User = Depends(check_user_exists)):
1818
# Opportunistically clean up expired tunnels and sync Caddy
1919
try:
2020
settings = await get_settings(user.id)

0 commit comments

Comments
 (0)