mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2024-11-19 09:54:21 +01:00
Login improve UI config (#2171)
* feat: show auth configs on the admin UI * fix: do not access settings on load * fix: redirect for click on item (not only on text) * fix: remove `Display Name` * fix: do not show `Verify email with` if no auth option is available * feat: show warning before logout * feat: i18n of account page * fix: show account icon for user ID login * fix: always check `isUserAuthorized` * fix: update the `disclaimer_dialog` message * feat: hide user ID by default * fix: redirect from login page when user authorized * feat: update logout message * fix: do not translate company names
This commit is contained in:
parent
24b02cc656
commit
bb918a8523
@ -1,62 +1,145 @@
|
||||
<q-tab-panel name="users">
|
||||
<q-card-section class="q-pa-none">
|
||||
<h6 class="q-my-none">User Management</h6>
|
||||
<br />
|
||||
<div>
|
||||
<p>Admin Users</p>
|
||||
<q-input
|
||||
filled
|
||||
v-model="formAddAdmin"
|
||||
@keydown.enter="addAdminUser"
|
||||
type="text"
|
||||
label="User ID"
|
||||
hint="Users with admin privileges"
|
||||
>
|
||||
<q-btn @click="addAdminUser" dense flat icon="add"></q-btn>
|
||||
</q-input>
|
||||
<div>
|
||||
{%raw%}
|
||||
<q-chip
|
||||
v-for="user in formData.lnbits_admin_users"
|
||||
:key="user"
|
||||
removable
|
||||
@remove="removeAdminUser(user)"
|
||||
color="primary"
|
||||
text-color="white"
|
||||
<h6 class="q-my-none q-mb-sm">User Management</h6>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12 q-pr-sm">
|
||||
<p>Admin Users</p>
|
||||
<q-input
|
||||
filled
|
||||
v-model="formAddAdmin"
|
||||
@keydown.enter="addAdminUser"
|
||||
type="text"
|
||||
label="User ID"
|
||||
hint="Users with admin privileges"
|
||||
>
|
||||
{{ user }}
|
||||
</q-chip>
|
||||
{%endraw%}
|
||||
<q-btn @click="addAdminUser" dense flat icon="add"></q-btn>
|
||||
</q-input>
|
||||
<div>
|
||||
{%raw%}
|
||||
<q-chip
|
||||
v-for="user in formData.lnbits_admin_users"
|
||||
:key="user"
|
||||
removable
|
||||
@remove="removeAdminUser(user)"
|
||||
color="primary"
|
||||
text-color="white"
|
||||
>
|
||||
{{ user }}
|
||||
</q-chip>
|
||||
{%endraw%}
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<p>Allowed Users</p>
|
||||
<q-input
|
||||
filled
|
||||
v-model="formAddUser"
|
||||
@keydown.enter="addAllowedUser"
|
||||
type="text"
|
||||
label="User ID"
|
||||
hint="Only these users can use LNbits"
|
||||
>
|
||||
<q-btn @click="addAllowedUser" dense flat icon="add"></q-btn>
|
||||
</q-input>
|
||||
<div>
|
||||
{% raw %}
|
||||
<q-chip
|
||||
v-for="user in formData.lnbits_allowed_users"
|
||||
:key="user"
|
||||
removable
|
||||
@remove="removeAllowedUser(user)"
|
||||
color="primary"
|
||||
text-color="white"
|
||||
>
|
||||
{{ user }}
|
||||
</q-chip>
|
||||
{% endraw %}
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
<div>
|
||||
<p>Allowed Users</p>
|
||||
<q-input
|
||||
filled
|
||||
v-model="formAddUser"
|
||||
@keydown.enter="addAllowedUser"
|
||||
type="text"
|
||||
label="User ID"
|
||||
hint="Only these users can use LNbits"
|
||||
>
|
||||
<q-btn @click="addAllowedUser" dense flat icon="add"></q-btn>
|
||||
</q-input>
|
||||
<div>
|
||||
{% raw %}
|
||||
<q-chip
|
||||
v-for="user in formData.lnbits_allowed_users"
|
||||
:key="user"
|
||||
removable
|
||||
@remove="removeAllowedUser(user)"
|
||||
color="primary"
|
||||
text-color="white"
|
||||
</q-card-section>
|
||||
<q-separator></q-separator>
|
||||
<q-card-section class="q-pa-none">
|
||||
<h6 class="q-my-none q-mb-sm">Authentication</h6>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12 q-pr-sm">
|
||||
<q-input
|
||||
filled
|
||||
v-model="formData.auth_token_expire_minutes"
|
||||
type="number"
|
||||
label="Token expire minutes"
|
||||
hint="Time in minutes until the token expires"
|
||||
>
|
||||
{{ user }}
|
||||
</q-chip>
|
||||
{% endraw %}
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 q-pr-sm">
|
||||
<q-select
|
||||
filled
|
||||
v-model="formData.auth_allowed_methods"
|
||||
multiple
|
||||
hint="Allowed authorization methods"
|
||||
label="Select authorization methods"
|
||||
:options="formData.auth_all_methods"
|
||||
></q-select>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section
|
||||
v-if="formData.auth_allowed_methods?.includes('google-auth')"
|
||||
class="q-pl-xl"
|
||||
>
|
||||
<strong class="q-my-none q-mb-sm">Google Auth</strong>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12 q-pr-sm">
|
||||
<q-input
|
||||
filled
|
||||
v-model="formData.google_client_id"
|
||||
label="Google Client ID"
|
||||
hint="Make sure thant the authorized redirect URIs contain https://{domain}/api/v1/auth/google/token"
|
||||
>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<q-input
|
||||
filled
|
||||
v-model="formData.google_client_secret"
|
||||
type="password"
|
||||
label="Google Client Secret"
|
||||
>
|
||||
</q-input>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section
|
||||
v-if="formData.auth_allowed_methods?.includes('github-auth')"
|
||||
class="q-pl-xl"
|
||||
>
|
||||
<strong class="q-my-none q-mb-sm">GitHub Auth</strong>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12 q-pr-sm">
|
||||
<q-input
|
||||
filled
|
||||
v-model="formData.github_client_id"
|
||||
label="GitHub Client ID"
|
||||
hint="Make sure thant the authorization callback URL is set to https://{domain}/api/v1/auth/github/token"
|
||||
>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<q-input
|
||||
filled
|
||||
v-model="formData.github_client_secret"
|
||||
type="password"
|
||||
label="GitHub Client Secret"
|
||||
>
|
||||
</q-input>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-tab-panel>
|
||||
|
@ -11,7 +11,9 @@
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h4 class="q-my-none">Password Settings</h4>
|
||||
<h4 class="q-my-none">
|
||||
<span v-text="$t('password_config')"></span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-img
|
||||
@ -33,26 +35,26 @@
|
||||
label="Old Password"
|
||||
filled
|
||||
dense
|
||||
:rules="[(val) => !val || val.length >= 8 || 'Password must have at least 8 characters']"
|
||||
:rules="[(val) => !val || val.length >= 8 || $t('invalid_password')]"
|
||||
></q-input>
|
||||
<q-input
|
||||
v-model="passwordData.newPassword"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
label="New Password"
|
||||
:label="$t('password')"
|
||||
filled
|
||||
dense
|
||||
:rules="[(val) => !val || val.length >= 8 || 'Password must have at least 8 characters']"
|
||||
:rules="[(val) => !val || val.length >= 8 || $t('invalid_password')]"
|
||||
></q-input>
|
||||
<q-input
|
||||
v-model="passwordData.newPasswordRepeat"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
label="New Password Repeat"
|
||||
:label="$t('password_repeat')"
|
||||
filled
|
||||
dense
|
||||
class="q-mb-md"
|
||||
:rules="[(val) => !val || val.length >= 8 || 'Password must have at least 8 characters']"
|
||||
:rules="[(val) => !val || val.length >= 8 || $t('invalid_password')]"
|
||||
></q-input>
|
||||
</q-card-section>
|
||||
<q-separator></q-separator>
|
||||
@ -62,11 +64,12 @@
|
||||
:disable="(!passwordData.newPassword || !passwordData.newPasswordRepeat) || passwordData.newPassword !== passwordData.newPasswordRepeat"
|
||||
unelevated
|
||||
color="primary"
|
||||
>Update Password</q-btn
|
||||
:label="$t('change_password')"
|
||||
>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
@click="passwordData.show = false"
|
||||
label="Back"
|
||||
:label="$t('back')"
|
||||
outline
|
||||
unelevated
|
||||
color="grey"
|
||||
@ -78,7 +81,9 @@
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h4 class="q-my-none">Account Settings</h4>
|
||||
<h4 class="q-my-none">
|
||||
<span v-text="$t('account_settings')"></span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-img
|
||||
@ -95,16 +100,23 @@
|
||||
<q-card-section>
|
||||
<q-input
|
||||
v-model="user.id"
|
||||
label="User ID"
|
||||
:label="$t('user_id')"
|
||||
filled
|
||||
dense
|
||||
readonly
|
||||
:type="showUserId ? 'text': 'password'"
|
||||
class="q-mb-md"
|
||||
>
|
||||
><q-btn
|
||||
@click="showUserId = !showUserId"
|
||||
dense
|
||||
flat
|
||||
:icon="showUserId ? 'visibility_off' : 'visibility'"
|
||||
color="grey"
|
||||
></q-btn>
|
||||
</q-input>
|
||||
<q-input
|
||||
v-model="user.username"
|
||||
label="Username"
|
||||
:label="$t('username')"
|
||||
filled
|
||||
dense
|
||||
:readonly="hasUsername"
|
||||
@ -113,7 +125,7 @@
|
||||
</q-input>
|
||||
<q-input
|
||||
v-model="user.email"
|
||||
label="Email"
|
||||
:label="$t('email')"
|
||||
filled
|
||||
dense
|
||||
readonly
|
||||
@ -122,8 +134,12 @@
|
||||
</q-input>
|
||||
<div v-if="!user.email" class="row"></div>
|
||||
<div v-if="!user.email" class="row">
|
||||
<div class="col q-pa-sm text-h6">Check email using:</div>
|
||||
{% if "google-auth" in LNBITS_AUTH_METHODS %}
|
||||
{% if "google-auth" in LNBITS_AUTH_METHODS or "github-auth" in
|
||||
LNBITS_AUTH_METHODS %}
|
||||
<div class="col q-pa-sm text-h6">
|
||||
<span v-text="$t('verify_email')"></span>:
|
||||
</div>
|
||||
{%endif%} {% if "google-auth" in LNBITS_AUTH_METHODS %}
|
||||
<div class="col q-pa-sm">
|
||||
<q-btn
|
||||
:href="`/api/v1/auth/google?user_id=${user.id}`"
|
||||
@ -166,17 +182,9 @@
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section v-if="user.config">
|
||||
<q-input
|
||||
v-model="user.config.display_name"
|
||||
label="Display Name"
|
||||
filled
|
||||
dense
|
||||
class="q-mb-md"
|
||||
>
|
||||
</q-input>
|
||||
<q-input
|
||||
v-model="user.config.first_name"
|
||||
label="First Name"
|
||||
:label="$t('first_name')"
|
||||
filled
|
||||
dense
|
||||
class="q-mb-md"
|
||||
@ -184,7 +192,7 @@
|
||||
</q-input>
|
||||
<q-input
|
||||
v-model="user.config.last_name"
|
||||
label="Last Name"
|
||||
:label="$t('last_name')"
|
||||
filled
|
||||
dense
|
||||
class="q-mb-md"
|
||||
@ -192,7 +200,7 @@
|
||||
</q-input>
|
||||
<q-input
|
||||
v-model="user.config.provider"
|
||||
label="Auth Provider"
|
||||
:label="$t('auth_provider')"
|
||||
filled
|
||||
dense
|
||||
readonly
|
||||
@ -201,7 +209,7 @@
|
||||
</q-input>
|
||||
<q-input
|
||||
v-model="user.config.picture"
|
||||
label="Picture"
|
||||
:label="$t('picture')"
|
||||
filled
|
||||
dense
|
||||
class="q-mb-md"
|
||||
@ -210,12 +218,12 @@
|
||||
</q-card-section>
|
||||
<q-separator></q-separator>
|
||||
<q-card-section>
|
||||
<q-btn @click="updateAccount" unelevated color="primary"
|
||||
>Update Account</q-btn
|
||||
>
|
||||
<q-btn @click="updateAccount" unelevated color="primary">
|
||||
<span v-text="$t('update_account')"></span>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
@click="showChangePassword()"
|
||||
:label="user.has_password ? 'Change Password': 'Set Password'"
|
||||
:label="user.has_password ? $t('change_password'): $t('set_password')"
|
||||
outline
|
||||
unelevated
|
||||
color="grey"
|
||||
@ -227,7 +235,7 @@
|
||||
<div v-else class="col-12 col-md-6 q-gutter-y-md">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<h4 class="q-my-none">Account</h4>
|
||||
<h4 class="q-my-none"><span v-text="$t('account')"></span></h4>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
|
@ -7,7 +7,7 @@
|
||||
class="col-12 col-md-7 col-lg-6 q-gutter-y-md"
|
||||
></div>
|
||||
<div v-else class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
|
||||
<div>
|
||||
<div class="gt-sm">
|
||||
<h3 class="q-my-none">{{SITE_TITLE}}</h3>
|
||||
<h5 class="q-my-md">{{SITE_TAGLINE}}</h5>
|
||||
</div>
|
||||
@ -32,7 +32,7 @@
|
||||
</div>
|
||||
{%else%} {% endif %}
|
||||
|
||||
<div class="row q-mt-xl">
|
||||
<div class="row q-mt-md">
|
||||
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
||||
<q-badge v-if="isAccessTokenExpired" color="primary" rounded>
|
||||
<div class="text-h5">
|
||||
@ -218,7 +218,7 @@
|
||||
>
|
||||
<div class="row">
|
||||
{% if "google-auth" in LNBITS_AUTH_METHODS %}
|
||||
<div class="col q-pa-sm">
|
||||
<div class="col-12 full-width q-pa-sm">
|
||||
<q-btn
|
||||
href="/api/v1/auth/google"
|
||||
type="a"
|
||||
@ -239,7 +239,7 @@
|
||||
</q-btn>
|
||||
</div>
|
||||
{%endif%} {% if "github-auth" in LNBITS_AUTH_METHODS %}
|
||||
<div class="col q-pa-sm">
|
||||
<div class="col-12 full-width q-pa-sm">
|
||||
<q-btn
|
||||
href="/api/v1/auth/github"
|
||||
type="a"
|
||||
@ -281,7 +281,10 @@
|
||||
</div>
|
||||
|
||||
<!-- Ads -->
|
||||
<div class="col-12 col-md-3 col-lg-3" v-if="'{{SITE_TITLE}}' == 'LNbits'">
|
||||
<div
|
||||
class="col-12 col-md-3 col-lg-3 gt-sm"
|
||||
v-if="'{{SITE_TITLE}}' == 'LNbits'"
|
||||
>
|
||||
<div class="row q-col-gutter-lg justify-center">
|
||||
<div class="col-6 col-sm-4 col-md-8 q-gutter-y-sm">
|
||||
<q-btn
|
||||
@ -480,4 +483,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row gt-sm q-mt-xl">
|
||||
<div class="col-1"></div>
|
||||
<div class="col-10 q-pl-xl">
|
||||
<span v-text="$t('lnbits_description')"></span>
|
||||
</div>
|
||||
<div class="col-1"></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -878,8 +878,9 @@
|
||||
<q-btn
|
||||
outline
|
||||
color="grey"
|
||||
@click="copyText(disclaimerDialog.location.href)"
|
||||
:label="$t('copy_wallet_url')"
|
||||
type="a"
|
||||
href="/account"
|
||||
:label="$t('my_account')"
|
||||
></q-btn>
|
||||
<q-btn
|
||||
v-close-popup
|
||||
|
@ -48,38 +48,6 @@ from ..models import (
|
||||
auth_router = APIRouter()
|
||||
|
||||
|
||||
def _init_google_sso() -> Optional[GoogleSSO]:
|
||||
if not settings.is_auth_method_allowed(AuthMethods.google_auth):
|
||||
return None
|
||||
if not settings.is_google_auth_configured:
|
||||
logger.warning("Google Auth allowed but not configured.")
|
||||
return None
|
||||
return GoogleSSO(
|
||||
settings.google_client_id,
|
||||
settings.google_client_secret,
|
||||
None,
|
||||
allow_insecure_http=True,
|
||||
)
|
||||
|
||||
|
||||
def _init_github_sso() -> Optional[GithubSSO]:
|
||||
if not settings.is_auth_method_allowed(AuthMethods.github_auth):
|
||||
return None
|
||||
if not settings.is_github_auth_configured:
|
||||
logger.warning("Github Auth allowed but not configured.")
|
||||
return None
|
||||
return GithubSSO(
|
||||
settings.github_client_id,
|
||||
settings.github_client_secret,
|
||||
None,
|
||||
allow_insecure_http=True,
|
||||
)
|
||||
|
||||
|
||||
google_sso = _init_google_sso()
|
||||
github_sso = _init_github_sso()
|
||||
|
||||
|
||||
@auth_router.get("/api/v1/auth", description="Get the authenticated user")
|
||||
async def get_auth_user(user: User = Depends(check_user_exists)) -> User:
|
||||
return user
|
||||
@ -128,6 +96,7 @@ async def login_usr(data: LoginUsr) -> JSONResponse:
|
||||
|
||||
@auth_router.get("/api/v1/auth/google", description="Google SSO")
|
||||
async def login_with_google(request: Request, user_id: Optional[str] = None):
|
||||
google_sso = _new_google_sso()
|
||||
if not google_sso:
|
||||
raise HTTPException(HTTP_401_UNAUTHORIZED, "Login by 'Google' not allowed.")
|
||||
|
||||
@ -139,6 +108,7 @@ async def login_with_google(request: Request, user_id: Optional[str] = None):
|
||||
|
||||
@auth_router.get("/api/v1/auth/github", description="Github SSO")
|
||||
async def login_with_github(request: Request, user_id: Optional[str] = None):
|
||||
github_sso = _new_github_sso()
|
||||
if not github_sso:
|
||||
raise HTTPException(HTTP_401_UNAUTHORIZED, "Login by 'GitHub' not allowed.")
|
||||
|
||||
@ -152,6 +122,7 @@ async def login_with_github(request: Request, user_id: Optional[str] = None):
|
||||
"/api/v1/auth/google/token", description="Handle Google OAuth callback"
|
||||
)
|
||||
async def handle_google_token(request: Request) -> RedirectResponse:
|
||||
google_sso = _new_google_sso()
|
||||
if not google_sso:
|
||||
raise HTTPException(HTTP_401_UNAUTHORIZED, "Login by 'Google' not allowed.")
|
||||
|
||||
@ -177,6 +148,7 @@ async def handle_google_token(request: Request) -> RedirectResponse:
|
||||
"/api/v1/auth/github/token", description="Handle Github OAuth callback"
|
||||
)
|
||||
async def handle_github_token(request: Request) -> RedirectResponse:
|
||||
github_sso = _new_github_sso()
|
||||
if not github_sso:
|
||||
raise HTTPException(HTTP_401_UNAUTHORIZED, "Login by 'GitHub' not allowed.")
|
||||
|
||||
@ -336,6 +308,34 @@ def _auth_redirect_response(path: str, email: str) -> RedirectResponse:
|
||||
return response
|
||||
|
||||
|
||||
def _new_google_sso() -> Optional[GoogleSSO]:
|
||||
if not settings.is_auth_method_allowed(AuthMethods.google_auth):
|
||||
return None
|
||||
if not settings.is_google_auth_configured:
|
||||
logger.warning("Google Auth allowed but not configured.")
|
||||
return None
|
||||
return GoogleSSO(
|
||||
settings.google_client_id,
|
||||
settings.google_client_secret,
|
||||
None,
|
||||
allow_insecure_http=True,
|
||||
)
|
||||
|
||||
|
||||
def _new_github_sso() -> Optional[GithubSSO]:
|
||||
if not settings.is_auth_method_allowed(AuthMethods.github_auth):
|
||||
return None
|
||||
if not settings.is_github_auth_configured:
|
||||
logger.warning("Github Auth allowed but not configured.")
|
||||
return None
|
||||
return GithubSSO(
|
||||
settings.github_client_id,
|
||||
settings.github_client_secret,
|
||||
None,
|
||||
allow_insecure_http=True,
|
||||
)
|
||||
|
||||
|
||||
def _encrypt_message(m: Optional[str] = None) -> Optional[str]:
|
||||
if not m:
|
||||
return None
|
||||
|
@ -247,6 +247,45 @@ class NodeUISettings(LNbitsSettings):
|
||||
lnbits_node_ui_transactions: bool = Field(default=False)
|
||||
|
||||
|
||||
class AuthMethods(Enum):
|
||||
user_id_only = "user-id-only"
|
||||
username_and_password = "username-password"
|
||||
google_auth = "google-auth"
|
||||
github_auth = "github-auth"
|
||||
|
||||
|
||||
class AuthSettings(LNbitsSettings):
|
||||
auth_token_expire_minutes: int = Field(default=525600)
|
||||
auth_all_methods = [a.value for a in AuthMethods]
|
||||
auth_allowed_methods: List[str] = Field(
|
||||
default=[
|
||||
AuthMethods.user_id_only.value,
|
||||
AuthMethods.username_and_password.value,
|
||||
]
|
||||
)
|
||||
|
||||
def is_auth_method_allowed(self, method: AuthMethods):
|
||||
return method.value in self.auth_allowed_methods
|
||||
|
||||
|
||||
class GoogleAuthSettings(LNbitsSettings):
|
||||
google_client_id: str = Field(default="")
|
||||
google_client_secret: str = Field(default="")
|
||||
|
||||
@property
|
||||
def is_google_auth_configured(self):
|
||||
return self.google_client_id != "" and self.google_client_secret != ""
|
||||
|
||||
|
||||
class GitHubAuthSettings(LNbitsSettings):
|
||||
github_client_id: str = Field(default="")
|
||||
github_client_secret: str = Field(default="")
|
||||
|
||||
@property
|
||||
def is_github_auth_configured(self):
|
||||
return self.github_client_id != "" and self.github_client_secret != ""
|
||||
|
||||
|
||||
class EditableSettings(
|
||||
UsersSettings,
|
||||
ExtensionsSettings,
|
||||
@ -257,6 +296,9 @@ class EditableSettings(
|
||||
LightningSettings,
|
||||
WebPushSettings,
|
||||
NodeUISettings,
|
||||
AuthSettings,
|
||||
GoogleAuthSettings,
|
||||
GitHubAuthSettings,
|
||||
):
|
||||
@validator(
|
||||
"lnbits_admin_users",
|
||||
@ -298,6 +340,7 @@ class EnvSettings(LNbitsSettings):
|
||||
lnbits_path: str = Field(default=".")
|
||||
lnbits_extensions_path: str = Field(default="lnbits")
|
||||
super_user: str = Field(default="")
|
||||
auth_secret_key: str = Field(default="")
|
||||
version: str = Field(default="0.0.0")
|
||||
user_agent: str = Field(default="")
|
||||
enable_log_to_file: bool = Field(default=True)
|
||||
@ -310,45 +353,6 @@ class EnvSettings(LNbitsSettings):
|
||||
return self.lnbits_extensions_path == "lnbits"
|
||||
|
||||
|
||||
class AuthMethods(Enum):
|
||||
user_id_only = "user-id-only"
|
||||
username_and_password = "username-password"
|
||||
google_auth = "google-auth"
|
||||
github_auth = "github-auth"
|
||||
|
||||
|
||||
class AuthSettings(LNbitsSettings):
|
||||
auth_secret_key: str = Field(default="")
|
||||
auth_token_expire_minutes: int = Field(default=30)
|
||||
auth_allowed_methods: List[str] = Field(
|
||||
default=[
|
||||
AuthMethods.user_id_only.value,
|
||||
AuthMethods.username_and_password.value,
|
||||
]
|
||||
)
|
||||
|
||||
def is_auth_method_allowed(self, method: AuthMethods):
|
||||
return method.value in self.auth_allowed_methods
|
||||
|
||||
|
||||
class GoogleAuthSettings(LNbitsSettings):
|
||||
google_client_id: str = Field(default="")
|
||||
google_client_secret: str = Field(default="")
|
||||
|
||||
@property
|
||||
def is_google_auth_configured(self):
|
||||
return self.google_client_id != "" and self.google_client_secret != ""
|
||||
|
||||
|
||||
class GitHubAuthSettings(LNbitsSettings):
|
||||
github_client_id: str = Field(default="")
|
||||
github_client_secret: str = Field(default="")
|
||||
|
||||
@property
|
||||
def is_github_auth_configured(self):
|
||||
return self.github_client_id != "" and self.github_client_secret != ""
|
||||
|
||||
|
||||
class SaaSSettings(LNbitsSettings):
|
||||
lnbits_saas_callback: Optional[str] = Field(default=None)
|
||||
lnbits_saas_secret: Optional[str] = Field(default=None)
|
||||
@ -397,9 +401,6 @@ class ReadOnlySettings(
|
||||
SaaSSettings,
|
||||
PersistenceSettings,
|
||||
SuperUserSettings,
|
||||
AuthSettings,
|
||||
GoogleAuthSettings,
|
||||
GitHubAuthSettings,
|
||||
):
|
||||
lnbits_admin_ui: bool = Field(default=False)
|
||||
|
||||
|
18
lnbits/static/bundle.min.js
vendored
18
lnbits/static/bundle.min.js
vendored
File diff suppressed because one or more lines are too long
@ -96,7 +96,7 @@ window.localisation.en = {
|
||||
i_understand: 'I understand',
|
||||
copy_wallet_url: 'Copy wallet URL',
|
||||
disclaimer_dialog:
|
||||
'Login functionality to be released in a future update, for now, make sure you bookmark this page for future access to your wallet! This service is in BETA, and we hold no responsibility for people losing access to funds.',
|
||||
'To ensure continuous access to your wallets, please remember to securely store your login credentials! Please visit the "My Account" page. This service is in BETA, and we hold no responsibility for people losing access to funds.',
|
||||
no_transactions: 'No transactions made yet',
|
||||
manage: 'Manage',
|
||||
extensions: 'Extensions',
|
||||
@ -197,15 +197,30 @@ window.localisation.en = {
|
||||
create_new_wallet: 'Create New Wallet',
|
||||
login_to_account: 'Login to your account',
|
||||
create_account: 'Create account',
|
||||
account_settings: 'Account Settings',
|
||||
signin_with_google: 'Sign in with Google',
|
||||
signin_with_github: 'Sign in with GitHub',
|
||||
username_or_email: 'Username or Email',
|
||||
password: 'Password',
|
||||
password_config: 'Password Config',
|
||||
password_repeat: 'Password repeat',
|
||||
change_password: 'Change Password',
|
||||
set_password: 'Set Password',
|
||||
invalid_password: 'Password must have at least 8 characters',
|
||||
login: 'Login',
|
||||
register: 'Register',
|
||||
username: 'Username',
|
||||
user_id: 'User ID',
|
||||
email: 'Email',
|
||||
first_name: 'First Name',
|
||||
last_name: 'Last Name',
|
||||
picture: 'Picture',
|
||||
verify_email: 'Verify email with',
|
||||
account: 'Account',
|
||||
update_account: 'Update Account',
|
||||
invalid_username: 'Invalid Username',
|
||||
back: 'Back'
|
||||
auth_provider: 'Auth Provider',
|
||||
my_account: 'My Account',
|
||||
back: 'Back',
|
||||
logout: 'Logout'
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ new Vue({
|
||||
return {
|
||||
user: null,
|
||||
hasUsername: false,
|
||||
showUserId: false,
|
||||
passwordData: {
|
||||
show: false,
|
||||
oldPassword: null,
|
||||
|
@ -407,6 +407,7 @@ window.windowMixin = {
|
||||
data: function () {
|
||||
return {
|
||||
toggleSubs: true,
|
||||
isUserAuthorized: false,
|
||||
g: {
|
||||
offline: !navigator.onLine,
|
||||
visibleDrawer: false,
|
||||
@ -420,12 +421,6 @@ window.windowMixin = {
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
isUserAuthorized() {
|
||||
return this.$q.cookies.get('is_lnbits_user_authorized')
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
activeLanguage: function (lang) {
|
||||
return window.i18n.locale === lang
|
||||
@ -452,31 +447,45 @@ window.windowMixin = {
|
||||
})
|
||||
},
|
||||
checkUsrInUrl: async function () {
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const usr = params.get('usr')
|
||||
if (!usr) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const usr = params.get('usr')
|
||||
if (!usr) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.isUserAuthorized) {
|
||||
await LNbits.api.loginUsr(usr)
|
||||
}
|
||||
params.delete('usr')
|
||||
const cleanQueryPrams = params.size ? `?${params.toString()}` : ''
|
||||
if (!this.isUserAuthorized) {
|
||||
await LNbits.api.loginUsr(usr)
|
||||
}
|
||||
|
||||
window.history.replaceState(
|
||||
{},
|
||||
document.title,
|
||||
window.location.pathname + cleanQueryPrams
|
||||
)
|
||||
params.delete('usr')
|
||||
const cleanQueryPrams = params.size ? `?${params.toString()}` : ''
|
||||
|
||||
window.history.replaceState(
|
||||
{},
|
||||
document.title,
|
||||
window.location.pathname + cleanQueryPrams
|
||||
)
|
||||
} finally {
|
||||
this.isUserAuthorized = !!this.$q.cookies.get(
|
||||
'is_lnbits_user_authorized'
|
||||
)
|
||||
}
|
||||
},
|
||||
logout: async function () {
|
||||
try {
|
||||
await LNbits.api.logout()
|
||||
window.location = '/'
|
||||
} catch (e) {
|
||||
LNbits.utils.notifyApiError(e)
|
||||
}
|
||||
LNbits.utils
|
||||
.confirmDialog(
|
||||
'Do you really want to logout?' +
|
||||
' Please visit "My Account" page to check your credentials!'
|
||||
)
|
||||
.onOk(async () => {
|
||||
try {
|
||||
await LNbits.api.logout()
|
||||
window.location = '/'
|
||||
} catch (e) {
|
||||
LNbits.utils.notifyApiError(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created: async function () {
|
||||
|
@ -8,6 +8,7 @@ new Vue({
|
||||
data: {},
|
||||
description: ''
|
||||
},
|
||||
isUserAuthorized: false,
|
||||
authAction: 'login',
|
||||
authMethod: 'username-password',
|
||||
usr: '',
|
||||
@ -23,9 +24,6 @@ new Vue({
|
||||
formatDescription() {
|
||||
return LNbits.utils.convertMarkdown(this.description)
|
||||
},
|
||||
isUserAuthorized() {
|
||||
return this.$q.cookies.get('is_lnbits_user_authorized')
|
||||
},
|
||||
isAccessTokenExpired() {
|
||||
return this.$q.cookies.get('is_access_token_expired')
|
||||
}
|
||||
@ -96,6 +94,7 @@ new Vue({
|
||||
created() {
|
||||
this.description = SITE_DESCRIPTION
|
||||
|
||||
this.isUserAuthorized = !!this.$q.cookies.get('is_lnbits_user_authorized')
|
||||
if (this.isUserAuthorized) {
|
||||
window.location.href = '/wallet'
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// update cache version every time there is a new deployment
|
||||
// so the service worker reinitializes the cache
|
||||
const CACHE_VERSION = 88
|
||||
const CACHE_VERSION = 95
|
||||
const CURRENT_CACHE = `lnbits-${CACHE_VERSION}-`
|
||||
|
||||
const getApiKey = request => {
|
||||
|
@ -266,18 +266,13 @@
|
||||
</div>
|
||||
</template>
|
||||
<q-list>
|
||||
<q-item clickable v-close-popup
|
||||
<q-item tag="a" href="/account" clickable v-close-popup
|
||||
><q-item-section>
|
||||
<q-icon name="person" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<!-- todo: no word break -->
|
||||
<q-item-label>
|
||||
<a
|
||||
style="text-decoration: none; color: inherit"
|
||||
href="/account"
|
||||
><span> My Account</span></a
|
||||
>
|
||||
<span v-text="$t('my_account')"></span>
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
@ -290,7 +285,9 @@
|
||||
<q-icon name="logout" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label> Logout </q-item-label>
|
||||
<q-item-label>
|
||||
<span v-text="$t('logout')"></span>
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label> </q-item-label>
|
||||
|
Loading…
Reference in New Issue
Block a user