perf: use check_account_exists decorator (#3600)

This commit is contained in:
Vlad Stan
2025-12-04 10:17:47 +02:00
committed by GitHub
parent 5213508dc1
commit b3efb4d378
12 changed files with 182 additions and 114 deletions
+61 -7
View File
@@ -27,9 +27,11 @@ from lnbits.core.models import (
User,
WalletTypeInfo,
)
from lnbits.core.models.users import AccountId
from lnbits.db import Connection, Filter, Filters, TFilterModel
from lnbits.helpers import normalize_path, path_segments
from lnbits.helpers import normalize_path, path_segments, sha256s
from lnbits.settings import AuthMethods, settings
from lnbits.utils.cache import cache
oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="api/v1/auth",
@@ -106,7 +108,7 @@ class KeyChecker(SecurityBase):
detail="Invalid adminkey.",
)
await _check_user_extension_access(wallet.user, request["path"])
await _check_user_access(request, wallet.user)
key_type = KeyType.admin if wallet.adminkey == key_value else KeyType.invoice
return WalletTypeInfo(key_type, wallet)
@@ -144,11 +146,49 @@ async def check_access_token(
return header_access_token or cookie_access_token or bearer_access_token
async def check_user_exists(
async def check_account_id_exists(
r: Request,
access_token: Annotated[str | None, Depends(check_access_token)],
usr: UUID4 | None = None,
) -> User:
) -> AccountId:
cache_key: str | None = None
if access_token:
cache_key = f"auth:access_token:{sha256s(access_token)}"
elif usr:
cache_key = f"auth:user_id:{sha256s(usr.hex)}"
if cache_key and settings.auth_authentication_cache_minutes > 0:
account_id = cache.get(cache_key)
if account_id:
r.scope["user_id"] = account_id.id
await _check_user_access(r, account_id)
return account_id
account = await check_account_exists(r, access_token, usr)
account_id = AccountId(id=account.id)
if cache_key and settings.auth_authentication_cache_minutes > 0:
cache.set(
cache_key,
account_id,
expiry=settings.auth_authentication_cache_minutes * 60,
)
return account_id
async def check_account_exists(
r: Request,
access_token: Annotated[str | None, Depends(check_access_token)],
usr: UUID4 | None = None,
) -> Account:
"""
Check that the account exists based on access token or user id.
More performant version of `check_user_exists`.
Unlike `check_user_exists`, this function:
- does not fetch the user wallets
- caches the account info based on settings cache time
"""
if access_token:
account = await _get_account_from_token(access_token, r["path"], r["method"])
elif usr and settings.is_auth_method_allowed(AuthMethods.user_id_only):
@@ -164,13 +204,21 @@ async def check_user_exists(
raise HTTPException(HTTPStatus.UNAUTHORIZED, "User not found.")
r.scope["user_id"] = account.id
if not settings.is_user_allowed(account.id):
raise HTTPException(HTTPStatus.FORBIDDEN, "User not allowed.")
await _check_user_access(r, account.id)
return account
async def check_user_exists(
r: Request,
access_token: Annotated[str | None, Depends(check_access_token)],
usr: UUID4 | None = None,
) -> User:
account = await check_account_exists(r, access_token, usr)
user = await get_user_from_account(account)
if not user:
raise HTTPException(HTTPStatus.UNAUTHORIZED, "User not found.")
await _check_user_extension_access(user.id, r["path"])
return user
@@ -280,6 +328,12 @@ async def check_user_extension_access(
return SimpleStatus(success=True, message="OK")
async def _check_user_access(r: Request, user_id: str):
if not settings.is_user_allowed(user_id):
raise HTTPException(HTTPStatus.FORBIDDEN, "User not allowed.")
await _check_user_extension_access(user_id, r["path"])
async def _check_user_extension_access(user_id: str, path: str):
ext_id = path_segments(path)[0]
status = await check_user_extension_access(user_id, ext_id)