Development
This commit is contained in:
parent
d8d1d46fd2
commit
eed1d295dc
69 changed files with 3355 additions and 3230 deletions
112
docker/routlin-dash/app/pages/accountverifyemail/action.py
Normal file
112
docker/routlin-dash/app/pages/accountverifyemail/action.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
from flask import Blueprint, request, session, redirect, flash
|
||||
import json, os, secrets
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from auth import require_level
|
||||
from config_utils import ACCOUNTS_FILE
|
||||
|
||||
bp = Blueprint('accountverifyemail', __name__)
|
||||
|
||||
|
||||
|
||||
def _load_accounts():
|
||||
try:
|
||||
with open(ACCOUNTS_FILE) as f:
|
||||
return json.load(f)
|
||||
except Exception:
|
||||
return {'accounts': []}
|
||||
|
||||
def _save_accounts(data):
|
||||
with open(ACCOUNTS_FILE, 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
|
||||
@bp.route('/action/verify_email', methods=['POST'])
|
||||
@require_level('nothing')
|
||||
def verify_email():
|
||||
# Abort if already logged in
|
||||
if session.get('access_level', 'nothing') != 'nothing':
|
||||
return redirect('/view/view_overview')
|
||||
|
||||
pending = session.get('pending_create_account')
|
||||
|
||||
if not pending:
|
||||
flash('No pending account creation found. Please start over.', 'error')
|
||||
return redirect('/view/view_createaccount')
|
||||
|
||||
expires = datetime.fromisoformat(pending['expires'])
|
||||
if datetime.now(tz=timezone.utc) > expires:
|
||||
session.pop('pending_create_account', None)
|
||||
flash('Verification code has expired. Please start over.', 'error')
|
||||
return redirect('/view/view_createaccount')
|
||||
|
||||
submitted = request.form.get('code', '').strip()
|
||||
if submitted != pending['code']:
|
||||
flash('Incorrect verification code.', 'error')
|
||||
return redirect('/view/view_verifyemail')
|
||||
|
||||
data = _load_accounts()
|
||||
accounts = data.get('accounts', [])
|
||||
account = next(
|
||||
(a for a in accounts if a.get('email_address', '').lower() == pending['email'].lower()),
|
||||
None
|
||||
)
|
||||
|
||||
if account is None:
|
||||
session.pop('pending_create_account', None)
|
||||
flash('Account no longer exists. Contact your manager.', 'error')
|
||||
return redirect('/view/view_createaccount')
|
||||
|
||||
if account.get('hashed_password'):
|
||||
session.pop('pending_create_account', None)
|
||||
flash('This account is already set up. Please log in.', 'error')
|
||||
return redirect('/view/view_login')
|
||||
|
||||
now = datetime.now(tz=timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
account['hashed_password'] = pending['hashed_password']
|
||||
account['timezone'] = pending['timezone']
|
||||
if not account.get('account_created_utc'):
|
||||
account['account_created_utc'] = now
|
||||
if not account.get('account_created_by'):
|
||||
account['account_created_by'] = 'self'
|
||||
|
||||
_save_accounts(data)
|
||||
session.pop('pending_create_account', None)
|
||||
|
||||
session['email_address'] = account['email_address']
|
||||
session['access_level'] = account.get('access_level', 'viewer')
|
||||
session['timezone'] = pending['timezone']
|
||||
session.permanent = True
|
||||
|
||||
return redirect('/view/view_overview')
|
||||
|
||||
|
||||
@bp.route('/action/resend_verification')
|
||||
@require_level('nothing')
|
||||
def resend_verification():
|
||||
# Abort if already logged in
|
||||
if session.get('access_level', 'nothing') != 'nothing':
|
||||
return redirect('/view/view_overview')
|
||||
|
||||
from pages.accountcreate.action import _send_verification_email, CODE_TTL_MIN
|
||||
|
||||
pending = session.get('pending_create_account')
|
||||
|
||||
if not pending:
|
||||
flash('No pending account creation found. Please start over.', 'error')
|
||||
return redirect('/view/view_createaccount')
|
||||
|
||||
code = f'{secrets.randbelow(1000000):06d}'
|
||||
expires = (datetime.now(tz=timezone.utc) + timedelta(minutes=CODE_TTL_MIN)).isoformat()
|
||||
|
||||
try:
|
||||
_send_verification_email(pending['email'], code)
|
||||
except Exception as exc:
|
||||
flash(f'Could not resend verification email: {exc}', 'error')
|
||||
return redirect('/view/view_verifyemail')
|
||||
|
||||
pending['code'] = code
|
||||
pending['expires'] = expires
|
||||
session['pending_create_account'] = pending
|
||||
|
||||
flash('A new verification code has been sent.', 'success')
|
||||
return redirect('/view/view_verifyemail')
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
{
|
||||
"id": "view_verifyemail",
|
||||
"client_requirement": "client_is_nothing=",
|
||||
"items": [
|
||||
{
|
||||
"type": "auth_wrapper",
|
||||
"client_requirement": "client_is_nothing=",
|
||||
"items": [
|
||||
{
|
||||
"type": "auth_card",
|
||||
"items": [
|
||||
{
|
||||
"type": "h1",
|
||||
"text": "Verify Your Email"
|
||||
},
|
||||
{
|
||||
"type": "p",
|
||||
"text": "A 6-digit code was sent to your email address. Enter it below to complete your account."
|
||||
},
|
||||
{
|
||||
"type": "form",
|
||||
"action": "/action/verify_email",
|
||||
"method": "post",
|
||||
"items": [
|
||||
{
|
||||
"type": "field",
|
||||
"label": "Verification Code",
|
||||
"name": "code",
|
||||
"input_type": "text",
|
||||
"placeholder": "000000",
|
||||
"hint": "The code expires in 15 minutes."
|
||||
},
|
||||
{
|
||||
"type": "button_primary",
|
||||
"action": "/action/verify_email",
|
||||
"method": "post",
|
||||
"text": "Verify",
|
||||
"class": "btn-full"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "p",
|
||||
"text": "Didn't receive it?",
|
||||
"link": {
|
||||
"action": "/action/resend_verification",
|
||||
"text": "Resend code"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "p",
|
||||
"text": "Wrong email?",
|
||||
"link": {
|
||||
"action": "/view/view_createaccount",
|
||||
"text": "Start over"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"client_requirement": "client_is_viewer+",
|
||||
"items": [
|
||||
{
|
||||
"type": "h1",
|
||||
"text": "Already logged in."
|
||||
},
|
||||
{
|
||||
"type": "p",
|
||||
"text": "Your account is already active."
|
||||
},
|
||||
{
|
||||
"type": "spacer"
|
||||
},
|
||||
{
|
||||
"type": "button_primary",
|
||||
"action": "/view/view_overview",
|
||||
"text": "Go to Overview"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue