fix: permissions

This commit is contained in:
Vlad Stan
2026-07-09 16:33:09 +03:00
parent 21a6bbe73f
commit df04f5cc65
+18 -6
View File
@@ -55,6 +55,7 @@ class ExtensionAPIMethodExport:
sdk_name: str sdk_name: str
description: str description: str
required_permission: str | None = None required_permission: str | None = None
require_auth: bool = True
@dataclass(frozen=True) @dataclass(frozen=True)
@@ -69,6 +70,7 @@ class ExtensionAPIMethod:
request_model: type[BaseModel] request_model: type[BaseModel]
response_model: type[BaseModel] response_model: type[BaseModel]
required_permission: str | None = None required_permission: str | None = None
require_auth: bool = True
@property @property
def sdk_qualified_name(self) -> str: def sdk_qualified_name(self) -> str:
@@ -84,6 +86,7 @@ def extension_api_method(
sdk_name: str, sdk_name: str,
description: str, description: str,
required_permission: str | None = None, required_permission: str | None = None,
require_auth: bool = True,
) -> Callable[ ) -> Callable[
[Callable[[ExtensionAPI, _RequestModel], Awaitable[_ResponseModel]]], [Callable[[ExtensionAPI, _RequestModel], Awaitable[_ResponseModel]]],
Callable[[ExtensionAPI, _RequestModel], Awaitable[_ResponseModel]], Callable[[ExtensionAPI, _RequestModel], Awaitable[_ResponseModel]],
@@ -96,6 +99,7 @@ def extension_api_method(
sdk_name=sdk_name, sdk_name=sdk_name,
description=description, description=description,
required_permission=required_permission, required_permission=required_permission,
require_auth=require_auth,
) )
def decorator( def decorator(
@@ -103,6 +107,10 @@ def extension_api_method(
) -> Callable[[ExtensionAPI, _RequestModel], Awaitable[_ResponseModel]]: ) -> Callable[[ExtensionAPI, _RequestModel], Awaitable[_ResponseModel]]:
@wraps(function) @wraps(function)
async def wrapper(self: ExtensionAPI, request: _RequestModel) -> _ResponseModel: async def wrapper(self: ExtensionAPI, request: _RequestModel) -> _ResponseModel:
if require_auth and not self.user_id:
raise PermissionError(
f"Extension API method '{method_id}' requires authentication."
)
self.require_permission(required_permission) self.require_permission(required_permission)
return await function(self, request) return await function(self, request)
@@ -140,9 +148,9 @@ class ExtensionAPI:
sdk_name="get", sdk_name="get",
description="Read one row from an extension storage table.", description="Read one row from an extension storage table.",
required_permission="ext.storage.read_write", required_permission="ext.storage.read_write",
require_auth=True,
) )
async def storage_get(self, request: StorageGetRequest) -> StorageGetResponse: async def storage_get(self, request: StorageGetRequest) -> StorageGetResponse:
self.require_permission("ext.storage.read_write")
row = await storage_get_row(self.extension_id, request.table, request.id) row = await storage_get_row(self.extension_id, request.table, request.id)
return StorageGetResponse(data_json=json.dumps(row) if row else None) return StorageGetResponse(data_json=json.dumps(row) if row else None)
@@ -154,9 +162,9 @@ class ExtensionAPI:
sdk_name="set", sdk_name="set",
description="Create or update one row in an extension storage table.", description="Create or update one row in an extension storage table.",
required_permission="ext.storage.read_write", required_permission="ext.storage.read_write",
require_auth=True,
) )
async def storage_set(self, request: StorageSetRequest) -> StorageSetResponse: async def storage_set(self, request: StorageSetRequest) -> StorageSetResponse:
self.require_permission("ext.storage.read_write")
await storage_set_row(self.extension_id, request.table, request.data) await storage_set_row(self.extension_id, request.table, request.data)
return StorageSetResponse() return StorageSetResponse()
@@ -168,11 +176,11 @@ class ExtensionAPI:
sdk_name="getPaginated", sdk_name="getPaginated",
description="Get filtered, searched, sorted, paginated storage rows.", description="Get filtered, searched, sorted, paginated storage rows.",
required_permission="ext.storage.read_write", required_permission="ext.storage.read_write",
require_auth=True,
) )
async def storage_get_paginated( async def storage_get_paginated(
self, request: StoragePaginatedRequest self, request: StoragePaginatedRequest
) -> StoragePaginatedResponse: ) -> StoragePaginatedResponse:
self.require_permission("ext.storage.read_write")
page = await storage_get_paginated_rows( page = await storage_get_paginated_rows(
self.extension_id, self.extension_id,
request.table, request.table,
@@ -197,11 +205,11 @@ class ExtensionAPI:
sdk_name="delete", sdk_name="delete",
description="Delete one row from an extension storage table.", description="Delete one row from an extension storage table.",
required_permission="ext.storage.read_write", required_permission="ext.storage.read_write",
require_auth=True,
) )
async def storage_delete( async def storage_delete(
self, request: StorageDeleteRequest self, request: StorageDeleteRequest
) -> StorageDeleteResponse: ) -> StorageDeleteResponse:
self.require_permission("ext.storage.read_write")
await storage_delete_row(self.extension_id, request.table, request.id) await storage_delete_row(self.extension_id, request.table, request.id)
return StorageDeleteResponse() return StorageDeleteResponse()
@@ -213,11 +221,11 @@ class ExtensionAPI:
sdk_name="createInvoice", sdk_name="createInvoice",
description="Create an incoming Lightning invoice for an allowed wallet.", description="Create an incoming Lightning invoice for an allowed wallet.",
required_permission="wallet.create_invoice", required_permission="wallet.create_invoice",
require_auth=False, # allow public pages to create invoices
) )
async def wallet_create_invoice( async def wallet_create_invoice(
self, request: CreateInvoiceRequest self, request: CreateInvoiceRequest
) -> CreateInvoiceResponse: ) -> CreateInvoiceResponse:
self.require_permission("wallet.create_invoice")
from lnbits.core.crud.wallets import get_wallet from lnbits.core.crud.wallets import get_wallet
from lnbits.core.models.payments import CreateInvoice from lnbits.core.models.payments import CreateInvoice
from lnbits.core.services.payments import create_payment_request from lnbits.core.services.payments import create_payment_request
@@ -265,7 +273,6 @@ class ExtensionAPI:
raise PermissionError( raise PermissionError(
"Listing user wallets requires an authenticated user context." "Listing user wallets requires an authenticated user context."
) )
self.require_permission("wallet.list")
from lnbits.core.crud.wallets import get_wallets from lnbits.core.crud.wallets import get_wallets
@@ -288,6 +295,7 @@ class ExtensionAPI:
host_name="random_id", host_name="random_id",
sdk_name="id", sdk_name="id",
description="Create a random extension-local identifier.", description="Create a random extension-local identifier.",
require_auth=True,
) )
async def system_random_id(self, request: RandomIdRequest) -> RandomIdResponse: async def system_random_id(self, request: RandomIdRequest) -> RandomIdResponse:
return RandomIdResponse( return RandomIdResponse(
@@ -301,6 +309,7 @@ class ExtensionAPI:
host_name="now", host_name="now",
sdk_name="now", sdk_name="now",
description="Return the current Unix timestamp.", description="Return the current Unix timestamp.",
require_auth=True,
) )
async def system_now(self, request: EmptyRequest) -> NowResponse: async def system_now(self, request: EmptyRequest) -> NowResponse:
return NowResponse(timestamp=int(time.time())) return NowResponse(timestamp=int(time.time()))
@@ -312,6 +321,7 @@ class ExtensionAPI:
host_name="log", host_name="log",
sdk_name="log", sdk_name="log",
description="Write a bounded message to the extension log.", description="Write a bounded message to the extension log.",
require_auth=True,
) )
async def system_log(self, request: LogRequest) -> LogResponse: async def system_log(self, request: LogRequest) -> LogResponse:
log = getattr(logger, request.level) log = getattr(logger, request.level)
@@ -342,6 +352,7 @@ def list_extension_api_methods(
request_model=request_model, request_model=request_model,
response_model=response_model, response_model=response_model,
required_permission=export.required_permission, required_permission=export.required_permission,
require_auth=export.require_auth,
) )
) )
@@ -384,6 +395,7 @@ def extension_api_contract(
"sdk_qualified_name": method.sdk_qualified_name, "sdk_qualified_name": method.sdk_qualified_name,
"description": method.description, "description": method.description,
"required_permission": method.required_permission, "required_permission": method.required_permission,
"require_auth": method.require_auth,
"request_schema": method.request_model.schema( "request_schema": method.request_model.schema(
ref_template="#/definitions/{model}" ref_template="#/definitions/{model}"
), ),