From 954249ef9c5752029951fbbf2d9be5e4b8e5f11b Mon Sep 17 00:00:00 2001 From: Oren Date: Mon, 9 Dec 2024 20:06:27 +0200 Subject: [PATCH 1/5] allow insecure http --- crud.py | 2 +- manifest.json | 2 +- migrations.py | 10 ++++++++++ models.py | 6 ++++-- static/js/index.js | 5 +++++ views.py | 8 +++++--- views_api.py | 9 ++++++--- 7 files changed, 32 insertions(+), 10 deletions(-) diff --git a/crud.py b/crud.py index 7b377df..deb2028 100644 --- a/crud.py +++ b/crud.py @@ -14,7 +14,7 @@ async def get_or_create_lnurlp_settings() -> LnurlpSettings: if row: return LnurlpSettings(**row) else: - settings = LnurlpSettings(nostr_private_key=PrivateKey().hex()) + settings = LnurlpSettings(nostr_private_key=PrivateKey().hex(),allow_insecure_http=False) await db.execute( insert_query("lnurlp.settings", settings), (*settings.dict().values(),) ) diff --git a/manifest.json b/manifest.json index cc0c3d2..aeb3d81 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "repos": [ { "id": "lnurlp", - "organisation": "lnbits", + "organisation": "oren-z0", "repository": "lnurlp" } ] diff --git a/migrations.py b/migrations.py index d39bc7c..12da2d8 100644 --- a/migrations.py +++ b/migrations.py @@ -174,3 +174,13 @@ async def m009_add_settings(db): ); """ ) + +async def m010_add_allow_insecure_http_to_settings(db): + """ + Add extension settings table + """ + await db.execute( + """ + ALTER TABLE lnurlp.settings ADD COLUMN allow_insecure_http BOOLEAN; + """ + ) diff --git a/models.py b/models.py index 22a8ace..7706936 100644 --- a/models.py +++ b/models.py @@ -5,6 +5,7 @@ from fastapi import Request from fastapi.param_functions import Query from lnurl import encode as lnurl_encode +from lnurl.helpers import url_encode from lnurl.types import LnurlPayMetadata from pydantic import BaseModel @@ -14,6 +15,7 @@ class LnurlpSettings(BaseModel): nostr_private_key: str + allow_insecure_http: bool | None @property def private_key(self) -> PrivateKey: @@ -69,14 +71,14 @@ def from_row(cls, row: Row) -> "PayLink": data["max"] /= data["fiat_base_multiplier"] return cls(**data) - def lnurl(self, req: Request) -> str: + def lnurl(self, req: Request, allow_insecure_http = False) -> str: url = req.url_for("lnurlp.api_lnurl_response", link_id=self.id) url_str = str(url) if url.netloc.endswith(".onion"): # change url string scheme to http url_str = url_str.replace("https://", "http://") - return lnurl_encode(url_str) + return url_encode(url_str) if allow_insecure_http else lnurl_encode(url_str) @property def lnurlpay_metadata(self) -> LnurlPayMetadata: diff --git a/static/js/index.js b/static/js/index.js index 6c3711b..b1e1c2c 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -36,6 +36,11 @@ new Vue({ type: 'str', description: 'Nostr private key used to zap', name: 'nostr_private_key' + }, + { + type: 'bool', + description: 'Allow insecure http: advance usage only, local domain names encoded in lightning-addresses and lnurls may be broken, and communication may be insecure.', + name: 'allow_insecure_http' } ], domain: window.location.host, diff --git a/views.py b/views.py index d56c13e..e5d7b79 100644 --- a/views.py +++ b/views.py @@ -8,7 +8,7 @@ from starlette.exceptions import HTTPException from starlette.responses import HTMLResponse -from .crud import get_pay_link +from .crud import get_pay_link, get_or_create_lnurlp_settings lnurlp_generic_router = APIRouter() @@ -34,7 +34,8 @@ async def display(request: Request, link_id): raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist." ) - ctx = {"request": request, "lnurl": link.lnurl(req=request)} + settings = await get_or_create_lnurlp_settings() + ctx = {"request": request, "lnurl": link.lnurl(req=request, allow_insecure_http=settings.allow_insecure_http)} return lnurlp_renderer().TemplateResponse("lnurlp/display.html", ctx) @@ -45,5 +46,6 @@ async def print_qr(request: Request, link_id): raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist." ) - ctx = {"request": request, "lnurl": link.lnurl(req=request)} + settings = await get_or_create_lnurlp_settings() + ctx = {"request": request, "lnurl": link.lnurl(req=request, allow_insecure_http=settings.allow_insecure_http)} return lnurlp_renderer().TemplateResponse("lnurlp/print_qr.html", ctx) diff --git a/views_api.py b/views_api.py index 8d04dab..b02e5de 100644 --- a/views_api.py +++ b/views_api.py @@ -51,8 +51,9 @@ async def api_links( wallet_ids = user.wallet_ids if user else [] try: + settings = await get_or_create_lnurlp_settings() return [ - {**link.dict(), "lnurl": link.lnurl(req)} + {**link.dict(), "lnurl": link.lnurl(req, settings.allow_insecure_http)} for link in await get_pay_links(wallet_ids) ] @@ -87,7 +88,8 @@ async def api_link_retrieve( detail="Not your pay link.", status_code=HTTPStatus.FORBIDDEN ) - return {**link.dict(), **{"lnurl": link.lnurl(r)}} + settings = await get_or_create_lnurlp_settings() + return {**link.dict(), **{"lnurl": link.lnurl(r, settings.allow_insecure_http)}} async def check_username_exists(username: str): @@ -197,7 +199,8 @@ async def api_link_create_or_update( link = await create_pay_link(data) assert link - return {**link.dict(), "lnurl": link.lnurl(request)} + settings = await get_or_create_lnurlp_settings() + return {**link.dict(), "lnurl": link.lnurl(request, settings.allow_insecure_http)} @lnurlp_api_router.delete("/api/v1/links/{link_id}", status_code=HTTPStatus.OK) From ca6c8faaddf0c2854060b690379cbb0f2d712132 Mon Sep 17 00:00:00 2001 From: Oren Date: Mon, 9 Dec 2024 21:10:57 +0200 Subject: [PATCH 2/5] views_lnurl apis to support allow-insecure-http --- views_lnurl.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/views_lnurl.py b/views_lnurl.py index 02dbfe3..483d923 100644 --- a/views_lnurl.py +++ b/views_lnurl.py @@ -7,6 +7,7 @@ from lnurl import LnurlErrorResponse, LnurlPayActionResponse, LnurlPayResponse from lnurl.models import MessageAction, UrlAction from lnurl.types import ( + Url, ClearnetUrl, DebugUrl, LightningInvoice, @@ -22,6 +23,9 @@ increment_pay_link, ) +class InsecureClearnetUrl(Url): + allowed_schemes = {"http"} + lnurlp_lnurl_router = APIRouter() @@ -110,8 +114,9 @@ async def api_lnurl_callback( action: Optional[Union[MessageAction, UrlAction]] = None if link.success_url: + settings = await get_or_create_lnurlp_settings() url = parse_obj_as( - Union[DebugUrl, OnionUrl, ClearnetUrl], # type: ignore + Union[DebugUrl, OnionUrl, ClearnetUrl, InsecureClearnetUrl] if settings.allow_insecure_http else Union[DebugUrl, OnionUrl, ClearnetUrl], # type: ignore str(link.success_url), ) desc = parse_obj_as(Max144Str, link.success_text) @@ -150,8 +155,9 @@ async def api_lnurl_response( url = url.include_query_params(webhook_data=webhook_data) link.domain = request.url.netloc + settings = await get_or_create_lnurlp_settings() callback_url = parse_obj_as( - Union[DebugUrl, OnionUrl, ClearnetUrl], # type: ignore + Union[DebugUrl, OnionUrl, ClearnetUrl, InsecureClearnetUrl] if settings.allow_insecure_http else Union[DebugUrl, OnionUrl, ClearnetUrl], # type: ignore str(url), ) @@ -167,7 +173,6 @@ async def api_lnurl_response( params["commentAllowed"] = link.comment_chars if link.zaps: - settings = await get_or_create_lnurlp_settings() params["allowsNostr"] = True params["nostrPubkey"] = settings.public_key return params From 55f6c7defbc29fb1e801303f1377215de880d6eb Mon Sep 17 00:00:00 2001 From: Oren Date: Mon, 9 Dec 2024 21:40:38 +0200 Subject: [PATCH 3/5] allow missing tld when insecure --- views_lnurl.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/views_lnurl.py b/views_lnurl.py index 483d923..518b44a 100644 --- a/views_lnurl.py +++ b/views_lnurl.py @@ -24,7 +24,8 @@ ) class InsecureClearnetUrl(Url): - allowed_schemes = {"http"} + tld_required = False + allowed_schemes = {"http", "https"} lnurlp_lnurl_router = APIRouter() From f65cc54eee641c429eb769cf72d60e561c59a0ac Mon Sep 17 00:00:00 2001 From: Oren Date: Mon, 9 Dec 2024 22:23:15 +0200 Subject: [PATCH 4/5] accept insecure urls in LnurlPayResponse --- views_lnurl.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/views_lnurl.py b/views_lnurl.py index 518b44a..e528ce5 100644 --- a/views_lnurl.py +++ b/views_lnurl.py @@ -27,6 +27,9 @@ class InsecureClearnetUrl(Url): tld_required = False allowed_schemes = {"http", "https"} +class InsecureLnurlPayResponse(LnurlPayResponse): + callback: Union[ClearnetUrl, OnionUrl, DebugUrl, InsecureClearnetUrl] + lnurlp_lnurl_router = APIRouter() @@ -162,12 +165,20 @@ async def api_lnurl_response( str(url), ) - resp = LnurlPayResponse( - callback=callback_url, - minSendable=MilliSatoshi(round(link.min * rate) * 1000), - maxSendable=MilliSatoshi(round(link.max * rate) * 1000), - metadata=link.lnurlpay_metadata, - ) + if settings.allow_insecure_http: + resp = InsecureLnurlPayResponse( + callback=callback_url, + minSendable=MilliSatoshi(round(link.min * rate) * 1000), + maxSendable=MilliSatoshi(round(link.max * rate) * 1000), + metadata=link.lnurlpay_metadata, + ) + else: + resp = LnurlPayResponse( + callback=callback_url, + minSendable=MilliSatoshi(round(link.min * rate) * 1000), + maxSendable=MilliSatoshi(round(link.max * rate) * 1000), + metadata=link.lnurlpay_metadata, + ) params = resp.dict() if link.comment_chars > 0: From 7555a3c3cee56ffde7ed71894b7d4f57e69879f6 Mon Sep 17 00:00:00 2001 From: Oren Date: Wed, 11 Dec 2024 22:13:30 +0200 Subject: [PATCH 5/5] just spacing --- views_lnurl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/views_lnurl.py b/views_lnurl.py index e528ce5..c3b48e6 100644 --- a/views_lnurl.py +++ b/views_lnurl.py @@ -30,6 +30,7 @@ class InsecureClearnetUrl(Url): class InsecureLnurlPayResponse(LnurlPayResponse): callback: Union[ClearnetUrl, OnionUrl, DebugUrl, InsecureClearnetUrl] + lnurlp_lnurl_router = APIRouter()