linuxrouter/docker/routlin-dash/app/action_verify_email.py
2026-05-24 00:47:43 -04:00

112 lines
3.8 KiB
Python

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('action_verify_email', __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_create_account')
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_create_account')
submitted = request.form.get('code', '').strip()
if submitted != pending['code']:
flash('Incorrect verification code.', 'error')
return redirect('/view/view_verify_email')
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_create_account')
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_log_in')
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 action_create_account 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_create_account')
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_verify_email')
pending['code'] = code
pending['expires'] = expires
session['pending_create_account'] = pending
flash('A new verification code has been sent.', 'success')
return redirect('/view/view_verify_email')