add 2FA pages

This commit is contained in:
Jordan Blasenhauer 2023-12-27 15:05:56 +01:00
parent bafd3a5b6a
commit 783d83094c
3 changed files with 354 additions and 65 deletions

View file

@ -102,7 +102,7 @@
</h5>
<label class="sr-only" for="username">username</label>
<input
type="username"
type="text"
id="username"
name="username"
class="col-span-12 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-4 py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
@ -131,6 +131,7 @@
/>
</div>
<!-- end password inpt-->
<!-- totp -->
<div class="flex justify-center">
<button
type="submit"

View file

@ -7,7 +7,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
<h5 class="my-2 font-bold dark:text-white/90 mx-2">PROFILE</h5>
<div class="grid grid-cols-12 justify-items-center w-full">
<div
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 md:col-span-6 lg:px-6 lg:my-3 max-w-[400px] w-full"
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
>
<h5
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
@ -49,14 +49,14 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
/>
<!-- username inpt-->
<div
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 md:col-span-6 lg:px-6 lg:my-3 max-w-[400px] w-full"
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
>
<h5
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
>
New Username
username
</h5>
<label class="sr-only" for="admin_username">New Username</label>
<label class="sr-only" for="admin_username">New username</label>
<input
type="text"
id="admin_username"
@ -65,71 +65,72 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
placeholder="enter username"
pattern="(.*?)"
maxlength="256"
value="{{ username }}"
/>
</div>
<!-- end username inpt-->
<!-- password inpt-->
<div
data-input-group
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 md:col-span-6 lg:px-6 lg:my-3 max-w-[400px] w-full"
>
<h5
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
>
Current password
</h5>
<label class="sr-only" for="curr_password">Current password</label>
<input
type="password"
id="curr_password"
name="curr_password"
class="col-span-12 regular-input"
placeholder="enter password"
value=""
pattern="^(?=.*?\d)(?=.*?[ !\u0022#$%&'\(\)*+,.\/:;<=>?@\[\\\]^_`\u007B\u007C\u007D\u007E\u002D]).{8,}$"
minlength="8"
required
/>
<div
data-setting-password-container
class="absolute flex right-8 h-5 w-5 top-[60%] lg:top-11"
>
<button
data-setting-password="visible"
class="h-5 w-5 flex items-center align-middle dark:fill-blue-500 hover:brightness-75 transition-all"
type="button"
>
<svg
class="fill-primary pointer-events-none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 576 512"
>
<path
d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"
/>
</svg>
</button>
<button
data-setting-password="invisible"
class="hidden -translate-y-0.2 scale-110 h-5 w-5 items-center align-middle"
>
<svg
class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"
>
<path
d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"
/>
</svg>
</button>
</div>
</div>
<!-- end password inpt-->
<!-- password inpt-->
<div
data-input-group
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 md:col-span-6 lg:px-6 lg:my-3 max-w-[400px] w-full"
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
>
<h5
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
>
Current password
</h5>
<label class="sr-only" for="curr_password">Current password</label>
<input
type="password"
id="curr_password"
name="curr_password"
class="col-span-12 regular-input"
placeholder="enter password"
value=""
pattern="^(?=.*?\d)(?=.*?[ !\u0022#$%&'\(\)*+,.\/:;<=>?@\[\\\]^_`\u007B\u007C\u007D\u007E\u002D]).{8,}$"
minlength="8"
required
/>
<div
data-setting-password-container
class="absolute flex right-8 h-5 w-5 top-[60%] lg:top-11"
>
<button
data-setting-password="visible"
class="h-5 w-5 flex items-center align-middle dark:fill-blue-500 hover:brightness-75 transition-all"
type="button"
>
<svg
class="fill-primary pointer-events-none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 576 512"
>
<path
d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"
/>
</svg>
</button>
<button
data-setting-password="invisible"
class="hidden -translate-y-0.2 scale-110 h-5 w-5 items-center align-middle"
>
<svg
class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"
>
<path
d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"
/>
</svg>
</button>
</div>
</div>
<!-- end password inpt-->
<!-- password inpt-->
<div
data-input-group
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
>
<h5
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
@ -186,7 +187,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
<!-- password inpt-->
<div
data-input-group
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 md:col-span-6 lg:px-6 lg:my-3 max-w-[400px] w-full"
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
>
<h5
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
@ -258,4 +259,159 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
</div>
</form>
</div>
<div
class="w-full overflow-hidden overflow-y-auto overflow-x-auto max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
<h5 class="my-2 font-bold dark:text-white/90 mx-2">
2FA SETTINGS {% if not is_totp %} (NOT SETUP) {% else %} (SETUP) {% endif %}
</h5>
<form
class="grid grid-cols-12 w-full justify-items-center"
id="profile-form"
action="profile"
method="POST"
autocomplete="off"
>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input
type="hidden"
name="next"
value="{{ request.values.get('next', '') }}"
/>
{% if not is_totp %}
<!-- qr secret -->
<div
data-input-group
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
>
<h5
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
>
2FA QR CODE
</h5>
<img
src="data:image/png;base64, {{ totp_qr_image }}"
alt="Secret Token"
style="width: 200px; height: 200px"
/>
</div>
<!-- end qr secret -->
<!-- secret -->
<div
data-input-group
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
>
<h5
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
>
Secret key
</h5>
<label class="sr-only" for="secret-key">secret key</label>
<input
type="text"
id="secret-key"
name="secret-key"
class="col-span-12 regular-input"
placeholder="secret token"
value="{{ secret_token }}"
readonly
/>
</div>
<!-- end secret -->
{% endif %} {% if is_totp or not is_totp %}
<!-- username inpt-->
<div
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
>
<h5
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
>
2FA code
</h5>
<label class="sr-only" for="totp_token">totp code</label>
<input
type="text"
id="totp_token"
name="totp_token"
class="col-span-12 regular-input"
placeholder="enter code"
pattern="(.*?)"
maxlength="256"
required
/>
</div>
<!-- password inpt-->
{% endif %}
<div
data-input-group
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
>
<h5
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
>
Current password
</h5>
<label class="sr-only" for="totp_password">Current password</label>
<input
type="password"
id="totp_password"
name="totp_password"
class="col-span-12 regular-input"
placeholder="enter password"
value=""
pattern="^(?=.*?\d)(?=.*?[ !\u0022#$%&'\(\)*+,.\/:;<=>?@\[\\\]^_`\u007B\u007C\u007D\u007E\u002D]).{8,}$"
minlength="8"
required
/>
<div
data-setting-password-container
class="absolute flex right-8 h-5 w-5 top-[60%] lg:top-11"
>
<button
data-setting-password="visible"
class="h-5 w-5 flex items-center align-middle dark:fill-blue-500 hover:brightness-75 transition-all"
type="button"
>
<svg
class="fill-primary pointer-events-none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 576 512"
>
<path
d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"
/>
</svg>
</button>
<button
data-setting-password="invisible"
class="hidden -translate-y-0.2 scale-110 h-5 w-5 items-center align-middle"
>
<svg
class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"
>
<path
d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"
/>
</svg>
</button>
</div>
</div>
<!-- end password inpt-->
<div class="col-span-12 flex justify-center">
<button
type="submit"
id="profile-button"
name="profile-button"
value="profile"
class="valid-btn"
>
{% if not is_totp %} enabled totp {% else %} disabled totp {% endif %}
</button>
</div>
</form>
</div>
{% endblock %}

132
src/ui/templates/totp.html vendored Normal file
View file

@ -0,0 +1,132 @@
<!DOCTYPE html>
{% block content %}
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>BunkerWeb UI | Log in</title>
<link href="images/favicon.ico" rel="icon" type="image/x-icon" />
<link rel="stylesheet" href="css/dashboard.css" />
<link rel="stylesheet" href="css/login.css" />
</head>
<body>
<div
data-loader
class="fixed z-[10000] transition duration-300 h-screen w-screen bg-primary flex justify-center align-middle items-center"
>
<img
data-loader-img
src="images/logo-menu-2.png"
class="duration-300 w-40 h-12 sm:w-50 sm:h-14 md:w-60 md:h-16 lg:w-80 lg:h-24 inline transition-all"
alt="main logo"
/>
</div>
{% with messages = get_flashed_messages(with_categories=true) %} {% if
messages %}
<!-- flash message-->
{% for category, message in messages %}
<div
role="alert"
aria-description="login message alert"
data-flash-message
class="p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 bg-white rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button
data-close-flash-message
role="close alert message"
class="absolute right-7 top-1.5"
>
<svg
class="cursor-pointer fill-gray-600 dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
{% if category == 'error' or (message|safe).startswith("Please log in") %}
<h5 class="text-lg mb-0 text-red-500">Error</h5>
<p class="text-gray-700 dark:text-gray-300 mb-0 text-sm">
{{ message|safe }}
</p>
{% else %}
<h5 class="text-lg mb-0 text-green-500">Success</h5>
<p class="text-gray-700 dark:text-gray-300 mb-0 text-sm">
{{ message|safe }}
</p>
{% endif %}
</div>
{% endfor %}
<!-- end flash message-->
{% endif %} {% endwith %}
<!-- end flash message-->
<!--content -->
<main class="grid grid-cols-2 align-middle items-center min-h-screen">
<!--form -->
<div
class="mx-4 col-span-2 bg-none h-full flex flex-col items-center justify-center"
>
<div
class="bg-gray-50 rounded px-4 sm:px-12 py-16 w-full max-w-[400px]"
>
<div class="flex justify-center">
<img
class="max-w-60 max-h-30 mb-6"
src="images/BUNKERWEB-print-hd.png"
alt="logo"
class="logo"
/>
</div>
<h1 class="hidden text-center font-bold dark:text-white mb-8">
Log in
</h1>
<form action="totp" method="POST" autocomplete="off">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input
type="hidden"
name="next"
value="{{ request.values.get('next', '') }}"
/>
<!-- totp -->
<div class="flex flex-col relative col-span-12 my-3">
<h5
class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-md font-bold m-0 dark:text-gray-300"
>
2FA
</h5>
<label class="sr-only" for="totp_token">code totp</label>
<input
type="text"
id="totp_token"
name="totp_token"
class="col-span-12 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-4 py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="enter totp"
pattern="(.*?)"
required
/>
</div>
<!-- end totp-->
<div class="flex justify-center">
<button
type="submit"
id="login"
name="login"
value="login"
class="my-4 dark:brightness-90 inline-block px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-primary hover:bg-primary/80 focus:bg-primary/80 leading-normal text-sm ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md"
>
Log in
</button>
</div>
</form>
</div>
</div>
<!-- end form -->
</main>
</body>
</html>
{% endblock %}