From 67d8b67ee5e2e74be6f2bafba1f2c368de88a56c Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Tue, 9 May 2023 11:22:19 +0300 Subject: [PATCH] Create super user account if it does not exist (#1688) * fix: temp create account for `super_user_id` if missing * chore: remove dumb import * refactor: move logic outside `crud` * feat: add uuid4 conversion * fix: require valid string in .env file * fix: update the `settings.super_user` value in case or normalisation for UUID4 * fix: allow long super_user * chore: code format * fix: add UI redirect with the normalized user * fix: normalize `super_user` up one level * fix: should normalize user in non-ui mode also --- lnbits/core/crud.py | 13 ++++++++++--- lnbits/core/helpers.py | 12 ++++++++++++ lnbits/core/services.py | 7 +++++-- lnbits/core/views/generic.py | 10 ++++++++++ 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index ae00a3f51..5059a9deb 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -2,7 +2,7 @@ import datetime import json from typing import Any, Dict, List, Optional from urllib.parse import urlparse -from uuid import uuid4 +from uuid import UUID, uuid4 import shortuuid @@ -18,8 +18,15 @@ from .models import BalanceCheck, Payment, PaymentFilters, TinyURL, User, Wallet # -------- -async def create_account(conn: Optional[Connection] = None) -> User: - user_id = uuid4().hex +async def create_account( + conn: Optional[Connection] = None, user_id: Optional[str] = None +) -> User: + if user_id: + user_uuid4 = UUID(hex=user_id, version=4) + assert user_uuid4.hex == user_id, "User ID is not valid UUID4 hex string" + else: + user_id = uuid4().hex + await (conn or db).execute("INSERT INTO accounts (id) VALUES (?)", (user_id,)) new_account = await get_account(user_id=user_id, conn=conn) diff --git a/lnbits/core/helpers.py b/lnbits/core/helpers.py index 6769d5851..7e86840df 100644 --- a/lnbits/core/helpers.py +++ b/lnbits/core/helpers.py @@ -1,6 +1,7 @@ import importlib import re from typing import Any +from uuid import UUID import httpx from loguru import logger @@ -63,3 +64,14 @@ async def stop_extension_background_work(ext_id: str, user: str): url = f"https://{settings.host}:{settings.port}/{ext_id}/api/v1?usr={user}" except Exception as ex: logger.warning(ex) + + +def to_valid_user_id(user_id: str) -> UUID: + if len(user_id) < 32: + raise ValueError("User ID must have at least 128 bits") + try: + int(user_id, 16) + except: + raise ValueError("Invalid hex string for User ID.") + + return UUID(hex=user_id[:32], version=4) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 7f7542517..c8ea00a3c 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -43,6 +43,7 @@ from .crud import ( update_payment_status, update_super_user, ) +from .helpers import to_valid_user_id from .models import Payment @@ -437,6 +438,9 @@ async def update_wallet_balance(wallet_id: str, amount: int): async def check_admin_settings(): + if settings.super_user: + settings.super_user = to_valid_user_id(settings.super_user).hex + if settings.lnbits_admin_ui: settings_db = await get_super_settings() if not settings_db: @@ -488,8 +492,7 @@ async def init_admin_settings(super_user: Optional[str] = None) -> SuperSettings if super_user: account = await get_account(super_user) if not account: - account = await create_account() - super_user = account.id + account = await create_account(user_id=super_user) if not account.wallets or len(account.wallets) == 0: await create_wallet(user_id=account.id) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index bf8bb3945..26597b6ee 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -11,6 +11,7 @@ from pydantic.types import UUID4 from starlette.responses import HTMLResponse, JSONResponse from lnbits.core import db +from lnbits.core.helpers import to_valid_user_id from lnbits.core.models import User from lnbits.decorators import check_admin, check_user_exists from lnbits.helpers import template_renderer, url_for @@ -414,6 +415,15 @@ async def index(request: Request, user: User = Depends(check_admin)): ) +@core_html_routes.get("/uuidv4/{hex_value}") +async def hex_to_uuid4(hex_value: str): + try: + user_id = to_valid_user_id(hex_value).hex + return RedirectResponse(url=f"/wallet?usr={user_id}") + except Exception as e: + raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e)) + + async def toggle_extension(extension_to_enable, extension_to_disable, user_id): if extension_to_enable and extension_to_disable: raise HTTPException(