refactor: use decorators for disabled endpoints (#3481)

This commit is contained in:
dni ⚡ 2025-11-10 09:33:16 +01:00 committed by GitHub
parent b54eedee84
commit 6bac34fc6b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 51 additions and 57 deletions

View file

@ -1,9 +1,8 @@
import os import os
import shutil import shutil
from hashlib import sha256 from hashlib import sha256
from http import HTTPStatus
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, Depends
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from lnbits.core.models import ( from lnbits.core.models import (
@ -28,9 +27,9 @@ from lnbits.core.services.extensions_builder import (
) )
from lnbits.decorators import ( from lnbits.decorators import (
check_admin, check_admin,
check_extension_builder,
check_user_exists, check_user_exists,
) )
from lnbits.settings import settings
from ..crud import ( from ..crud import (
create_user_extension, create_user_extension,
@ -47,19 +46,12 @@ extension_builder_router = APIRouter(
@extension_builder_router.post( @extension_builder_router.post(
"/zip", "/zip",
summary="Build and download extension zip.", summary="Build and download extension zip.",
dependencies=[Depends(check_extension_builder)],
description=""" description="""
This endpoint generates a zip file for the extension based on the provided data. This endpoint generates a zip file for the extension based on the provided data.
""", """,
) )
async def api_build_extension( async def api_build_extension(data: ExtensionData) -> FileResponse:
data: ExtensionData,
user: User = Depends(check_user_exists),
) -> FileResponse:
if not settings.lnbits_extensions_builder_activate_non_admins and not user.admin:
raise HTTPException(
HTTPStatus.FORBIDDEN,
"Extension Builder is disabled for non admin users.",
)
stub_ext_id = "extension_builder_stub" # todo: do not hardcode, fetch from manifest stub_ext_id = "extension_builder_stub" # todo: do not hardcode, fetch from manifest
release, build_dir = await build_extension_from_data(data, stub_ext_id) release, build_dir = await build_extension_from_data(data, stub_ext_id)
@ -132,16 +124,12 @@ async def api_deploy_extension(
@extension_builder_router.post( @extension_builder_router.post(
"/preview", "/preview",
summary="Build and preview the extension ui.", summary="Build and preview the extension ui.",
dependencies=[Depends(check_extension_builder)],
) )
async def api_preview_extension( async def api_preview_extension(
data: ExtensionData, data: ExtensionData,
user: User = Depends(check_user_exists), user: User = Depends(check_user_exists),
) -> SimpleStatus: ) -> SimpleStatus:
if not settings.lnbits_extensions_builder_activate_non_admins and not user.admin:
raise HTTPException(
HTTPStatus.FORBIDDEN,
"Extension Builder is disabled for non admin users.",
)
stub_ext_id = "extension_builder_stub" stub_ext_id = "extension_builder_stub"
working_dir_name = "preview_" + sha256(user.id.encode("utf-8")).hexdigest() working_dir_name = "preview_" + sha256(user.id.encode("utf-8")).hexdigest()
await build_extension_from_data(data, stub_ext_id, working_dir_name) await build_extension_from_data(data, stub_ext_id, working_dir_name)

View file

@ -17,7 +17,12 @@ from lnbits.core.models import User
from lnbits.core.models.extensions import ExtensionMeta, InstallableExtension from lnbits.core.models.extensions import ExtensionMeta, InstallableExtension
from lnbits.core.services import create_invoice, create_user_account from lnbits.core.services import create_invoice, create_user_account
from lnbits.core.services.extensions import get_valid_extensions from lnbits.core.services.extensions import get_valid_extensions
from lnbits.decorators import check_admin, check_user_exists from lnbits.decorators import (
check_admin,
check_admin_ui,
check_extension_builder,
check_user_exists,
)
from lnbits.helpers import check_callback_url, template_renderer from lnbits.helpers import check_callback_url, template_renderer
from lnbits.settings import settings from lnbits.settings import settings
@ -209,14 +214,13 @@ async def extensions(request: Request, user: User = Depends(check_user_exists)):
@generic_router.get( @generic_router.get(
"/extensions/builder", name="extensions builder", response_class=HTMLResponse "/extensions/builder",
name="extensions builder",
dependencies=[Depends(check_extension_builder)],
) )
async def extensions_builder(request: Request, user: User = Depends(check_user_exists)): async def extensions_builder(
if not settings.lnbits_extensions_builder_activate_non_admins and not user.admin: request: Request, user: User = Depends(check_user_exists)
raise HTTPException( ) -> HTMLResponse:
HTTPStatus.FORBIDDEN,
"Extension Builder is disabled for non admin users.",
)
return template_renderer().TemplateResponse( return template_renderer().TemplateResponse(
request, request,
"core/extensions_builder.html", "core/extensions_builder.html",
@ -230,19 +234,14 @@ async def extensions_builder(request: Request, user: User = Depends(check_user_e
@generic_router.get( @generic_router.get(
"/extensions/builder/preview/{ext_id}", "/extensions/builder/preview/{ext_id}",
name="extensions builder", name="extensions builder",
response_class=HTMLResponse, dependencies=[Depends(check_extension_builder)],
) )
async def extensions_builder_preview( async def extensions_builder_preview(
request: Request, request: Request,
ext_id: str, ext_id: str,
page_name: str | None = None, page_name: str | None = None,
user: User = Depends(check_user_exists), user: User = Depends(check_user_exists),
): ) -> HTMLResponse:
if not settings.lnbits_extensions_builder_activate_non_admins and not user.admin:
raise HTTPException(
HTTPStatus.FORBIDDEN,
"Extension Builder is disabled for non admin users.",
)
working_dir_name = "preview_" + sha256(user.id.encode("utf-8")).hexdigest() working_dir_name = "preview_" + sha256(user.id.encode("utf-8")).hexdigest()
html_file_name = "index.html" html_file_name = "index.html"
if page_name == "public_page": if page_name == "public_page":
@ -383,10 +382,19 @@ async def manifest(request: Request, usr: str):
} }
@generic_router.get("/payments", response_class=HTMLResponse) admin_ui_checks = [Depends(check_admin), Depends(check_admin_ui)]
@generic_router.get("/wallets", response_class=HTMLResponse)
@generic_router.get("/account", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)): @generic_router.get("/payments")
@generic_router.get("/wallets")
@generic_router.get("/account")
@generic_router.get("/users", dependencies=admin_ui_checks)
@generic_router.get("/audit", dependencies=admin_ui_checks)
@generic_router.get("/node", dependencies=admin_ui_checks)
@generic_router.get("/admin", dependencies=admin_ui_checks)
async def index(
request: Request, user: User = Depends(check_user_exists)
) -> HTMLResponse:
return template_renderer().TemplateResponse( return template_renderer().TemplateResponse(
request, request,
"index.html", "index.html",
@ -396,27 +404,8 @@ async def index(request: Request, user: User = Depends(check_user_exists)):
) )
@generic_router.get("/users", response_class=HTMLResponse) @generic_router.get("/node/public")
@generic_router.get("/audit", response_class=HTMLResponse) async def index_public(request: Request) -> HTMLResponse:
@generic_router.get("/node", response_class=HTMLResponse)
@generic_router.get("/admin", response_class=HTMLResponse)
async def index_admin(request: Request, admin: User = Depends(check_admin)):
if not settings.lnbits_admin_ui:
raise HTTPException(
status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail="Admin UI is disabled."
)
return template_renderer().TemplateResponse(
request,
"index.html",
{
"user": admin.json(),
},
)
@generic_router.get("/node/public", response_class=HTMLResponse)
async def index_public(request: Request):
return template_renderer().TemplateResponse(request, "index_public.html") return template_renderer().TemplateResponse(request, "index_public.html")

View file

@ -359,3 +359,20 @@ def url_for_interceptor(original_method):
# Upgraded extensions modify the path. # Upgraded extensions modify the path.
# This interceptor ensures that the path is normalized. # This interceptor ensures that the path is normalized.
Request.url_for = url_for_interceptor(Request.url_for) # type: ignore[method-assign] Request.url_for = url_for_interceptor(Request.url_for) # type: ignore[method-assign]
async def check_admin_ui() -> None:
if not settings.lnbits_admin_ui:
raise HTTPException(
status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail="Admin UI is disabled."
)
async def check_extension_builder(
user: Annotated[User, Depends(check_user_exists)],
) -> None:
if not settings.lnbits_extensions_builder_activate_non_admins and not user.admin:
raise HTTPException(
HTTPStatus.FORBIDDEN,
"Extension Builder is disabled for non admin users.",
)