mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2024-11-19 01:43:42 +01:00
fixup!
This commit is contained in:
parent
3d5730492f
commit
5f32975a56
@ -38,9 +38,9 @@
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-img
|
||||
v-if="user.config.picture"
|
||||
v-if="user.extra.picture"
|
||||
style="max-width: 100px"
|
||||
:src="user.config.picture"
|
||||
:src="user.extra.picture"
|
||||
class="float-right"
|
||||
></q-img>
|
||||
</div>
|
||||
@ -133,9 +133,9 @@
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-img
|
||||
v-if="user.config.picture"
|
||||
v-if="user.extra.picture"
|
||||
style="max-width: 100px"
|
||||
:src="user.config.picture"
|
||||
:src="user.extra.picture"
|
||||
class="float-right"
|
||||
></q-img>
|
||||
</div>
|
||||
@ -236,9 +236,9 @@
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section v-if="user.config">
|
||||
<q-card-section v-if="user.extra">
|
||||
<q-input
|
||||
v-model="user.config.first_name"
|
||||
v-model="user.extra.first_name"
|
||||
:label="$t('first_name')"
|
||||
filled
|
||||
dense
|
||||
@ -246,7 +246,7 @@
|
||||
>
|
||||
</q-input>
|
||||
<q-input
|
||||
v-model="user.config.last_name"
|
||||
v-model="user.extra.last_name"
|
||||
:label="$t('last_name')"
|
||||
filled
|
||||
dense
|
||||
@ -254,7 +254,7 @@
|
||||
>
|
||||
</q-input>
|
||||
<q-input
|
||||
v-model="user.config.provider"
|
||||
v-model="user.extra.provider"
|
||||
:label="$t('auth_provider')"
|
||||
filled
|
||||
dense
|
||||
@ -263,7 +263,7 @@
|
||||
>
|
||||
</q-input>
|
||||
<q-input
|
||||
v-model="user.config.picture"
|
||||
v-model="user.extra.picture"
|
||||
:label="$t('picture')"
|
||||
filled
|
||||
class="q-mb-md"
|
||||
|
@ -76,26 +76,16 @@ async def nostr_login(request: Request) -> JSONResponse:
|
||||
raise HTTPException(
|
||||
HTTPStatus.UNAUTHORIZED, "Login with Nostr Auth not allowed."
|
||||
)
|
||||
|
||||
try:
|
||||
event = _nostr_nip98_event(request)
|
||||
account = await get_account_by_pubkey(event["pubkey"])
|
||||
if not account:
|
||||
account = Account(
|
||||
id=uuid4().hex,
|
||||
pubkey=event["pubkey"],
|
||||
extra=UserExtra(provider="nostr"),
|
||||
)
|
||||
await create_account(account)
|
||||
|
||||
return _auth_success_response(account.username or "", account.id, account.email)
|
||||
except HTTPException as exc:
|
||||
raise exc
|
||||
except AssertionError as exc:
|
||||
raise HTTPException(HTTPStatus.UNAUTHORIZED, str(exc)) from exc
|
||||
except Exception as exc:
|
||||
logger.warning(exc)
|
||||
raise HTTPException(HTTPStatus.INTERNAL_SERVER_ERROR, "Cannot login.") from exc
|
||||
event = _nostr_nip98_event(request)
|
||||
account = await get_account_by_pubkey(event["pubkey"])
|
||||
if not account:
|
||||
account = Account(
|
||||
id=uuid4().hex,
|
||||
pubkey=event["pubkey"],
|
||||
extra=UserExtra(provider="nostr"),
|
||||
)
|
||||
await create_account(account)
|
||||
return _auth_success_response(account.username or "", account.id, account.email)
|
||||
|
||||
|
||||
@auth_router.post("/usr", description="Login via the User ID")
|
||||
@ -139,23 +129,15 @@ async def handle_oauth_token(request: Request, provider: str) -> RedirectRespons
|
||||
detail=f"Login by '{provider}' not allowed.",
|
||||
)
|
||||
|
||||
try:
|
||||
with provider_sso:
|
||||
userinfo = await provider_sso.verify_and_process(request)
|
||||
assert userinfo is not None
|
||||
user_id = decrypt_internal_message(provider_sso.state)
|
||||
request.session.pop("user", None)
|
||||
return await _handle_sso_login(userinfo, user_id)
|
||||
except HTTPException as exc:
|
||||
raise exc
|
||||
except ValueError as exc:
|
||||
raise HTTPException(HTTPStatus.FORBIDDEN, str(exc)) from exc
|
||||
except Exception as exc:
|
||||
logger.debug(exc)
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
detail=f"Cannot authenticate user with {provider} Auth.",
|
||||
) from exc
|
||||
with provider_sso:
|
||||
userinfo = await provider_sso.verify_and_process(request)
|
||||
if not userinfo:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.UNAUTHORIZED, detail="Invalid user info."
|
||||
)
|
||||
user_id = decrypt_internal_message(provider_sso.state)
|
||||
request.session.pop("user", None)
|
||||
return await _handle_sso_login(userinfo, user_id)
|
||||
|
||||
|
||||
@auth_router.post("/logout")
|
||||
@ -191,6 +173,11 @@ async def register(data: CreateUser) -> JSONResponse:
|
||||
status_code=HTTPStatus.BAD_REQUEST, detail="Invalid username."
|
||||
)
|
||||
|
||||
if await get_account_by_username(data.username):
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST, detail="Username already exists."
|
||||
)
|
||||
|
||||
if data.email and not is_valid_email_address(data.email):
|
||||
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="Invalid email.")
|
||||
|
||||
@ -212,17 +199,19 @@ async def update_pubkey(
|
||||
) -> Optional[User]:
|
||||
if data.user_id != user.id:
|
||||
raise HTTPException(HTTPStatus.BAD_REQUEST, "Invalid user ID.")
|
||||
|
||||
if (
|
||||
data.pubkey
|
||||
and data.pubkey != user.pubkey
|
||||
and await get_account_by_pubkey(data.pubkey)
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST, detail="Public key already in use."
|
||||
)
|
||||
account = await get_account(user.id)
|
||||
if not account:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND, detail="Account not found."
|
||||
)
|
||||
account_existing = await get_account_by_pubkey(data.pubkey)
|
||||
if account_existing and account_existing.id != account.id:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST, detail="Public key already in use."
|
||||
)
|
||||
_validate_auth_timeout(payload.auth_time)
|
||||
account.pubkey = normalize_public_key(data.pubkey)
|
||||
await update_account(account)
|
||||
@ -301,16 +290,19 @@ async def update(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
detail="Email mismatch.",
|
||||
)
|
||||
account = await get_account(user.id)
|
||||
if not account:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND, detail="Account not found."
|
||||
)
|
||||
if data.username and await get_account_by_username(data.username):
|
||||
if (
|
||||
data.username
|
||||
and user.username != data.username
|
||||
and await get_account_by_username(data.username)
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST, detail="Username already exists."
|
||||
)
|
||||
if data.email and await get_account_by_email(data.email):
|
||||
if (
|
||||
data.email
|
||||
and data.email != user.email
|
||||
and await get_account_by_email(data.email)
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST, detail="Email already exists."
|
||||
)
|
||||
@ -462,37 +454,43 @@ def _find_auth_provider_class(provider: str) -> Callable:
|
||||
|
||||
def _nostr_nip98_event(request: Request) -> dict:
|
||||
auth_header = request.headers.get("Authorization")
|
||||
assert auth_header, "Nostr Auth header missing."
|
||||
|
||||
if not auth_header:
|
||||
raise HTTPException(HTTPStatus.UNAUTHORIZED, "Nostr Auth header missing.")
|
||||
scheme, token = auth_header.split()
|
||||
assert scheme.lower() == "nostr", "Authorization header is not nostr."
|
||||
|
||||
if scheme.lower() != "nostr":
|
||||
raise HTTPException(HTTPStatus.UNAUTHORIZED, "Invalid Authorization scheme.")
|
||||
event = None
|
||||
try:
|
||||
event_json = base64.b64decode(token.encode("ascii"))
|
||||
event = json.loads(event_json)
|
||||
except Exception as exc:
|
||||
logger.warning(exc)
|
||||
|
||||
assert event, "Nostr login event cannot be parsed."
|
||||
|
||||
assert verify_event(event), "Nostr login event is not valid."
|
||||
|
||||
assert event["kind"] == 27_235, "Invalid event kind."
|
||||
if not event:
|
||||
raise HTTPException(
|
||||
HTTPStatus.BAD_REQUEST, "Nostr login event cannot be parsed."
|
||||
)
|
||||
if not verify_event(event):
|
||||
raise HTTPException(HTTPStatus.BAD_REQUEST, "Nostr login event is not valid.")
|
||||
if event["kind"] != 27_235:
|
||||
raise HTTPException(HTTPStatus.BAD_REQUEST, "Invalid event kind.")
|
||||
auth_threshold = settings.auth_credetials_update_threshold
|
||||
assert (
|
||||
abs(time() - event["created_at"]) < auth_threshold
|
||||
), f"More than {auth_threshold} seconds have passed since the event was signed."
|
||||
|
||||
if abs(time() - event["created_at"]) > auth_threshold:
|
||||
raise HTTPException(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
f"{auth_threshold} seconds have passed since the event was signed.",
|
||||
)
|
||||
method: Optional[str] = next((v for k, v in event["tags"] if k == "method"), None)
|
||||
assert method, "Tag 'method' is missing."
|
||||
assert method.upper() == "POST", "Incorrect value for tag 'method'."
|
||||
if not method:
|
||||
raise HTTPException(HTTPStatus.BAD_REQUEST, "Tag 'method' is missing.")
|
||||
if method.upper() != "POST":
|
||||
raise HTTPException(HTTPStatus.BAD_REQUEST, "Invalid value for tag 'method'.")
|
||||
|
||||
url = next((v for k, v in event["tags"] if k == "u"), None)
|
||||
assert url, "Tag 'u' for URL is missing."
|
||||
if not url:
|
||||
raise HTTPException(HTTPStatus.BAD_REQUEST, "Tag 'u' for URL is missing.")
|
||||
accepted_urls = [f"{u}/nostr" for u in settings.nostr_absolute_request_urls]
|
||||
assert url in accepted_urls, f"Incorrect value for tag 'u': '{url}'."
|
||||
|
||||
if url not in accepted_urls:
|
||||
raise HTTPException(HTTPStatus.BAD_REQUEST, "Invalid value for tag 'u'.")
|
||||
return event
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional, Type
|
||||
|
||||
@ -184,7 +184,7 @@ def is_valid_username(username: str) -> bool:
|
||||
|
||||
|
||||
def create_access_token(data: dict):
|
||||
expire = datetime.utcnow() + timedelta(minutes=settings.auth_token_expire_minutes)
|
||||
expire = datetime.now(UTC) + timedelta(minutes=settings.auth_token_expire_minutes)
|
||||
to_encode = data.copy()
|
||||
to_encode.update({"exp": expire})
|
||||
return jwt.encode(to_encode, settings.auth_secret_key, "HS256")
|
||||
|
@ -92,7 +92,7 @@ window.app = Vue.createApp({
|
||||
user_id: this.user.id,
|
||||
username: this.user.username,
|
||||
email: this.user.email,
|
||||
config: this.user.config
|
||||
extra: this.user.extra
|
||||
}
|
||||
)
|
||||
this.user = data
|
||||
@ -183,7 +183,7 @@ window.app = Vue.createApp({
|
||||
const {data} = await LNbits.api.getAuthenticatedUser()
|
||||
this.user = data
|
||||
this.hasUsername = !!data.username
|
||||
if (!this.user.config) this.user.config = {}
|
||||
if (!this.user.extra) this.user.extra = {}
|
||||
} catch (e) {
|
||||
LNbits.utils.notifyApiError(e)
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
# ruff: noqa: E402
|
||||
import asyncio
|
||||
from time import time
|
||||
|
||||
import uvloop
|
||||
from asgi_lifespan import LifespanManager
|
||||
|
||||
uvloop.install()
|
||||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
||||
|
||||
from time import time
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from asgi_lifespan import LifespanManager
|
||||
from fastapi.testclient import TestClient
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
|
||||
@ -92,7 +92,6 @@ async def user_alan():
|
||||
username="alan",
|
||||
)
|
||||
account.hash_password("secret1234")
|
||||
await create_account(account)
|
||||
yield account
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user