from flask import Blueprint, request, session, redirect, flash import json, os, secrets from datetime import datetime, timezone, timedelta from auth import require_level bp = Blueprint('action_verify_email', __name__) DATA_DIR = '/data' ACCOUNTS_FILE = f'{DATA_DIR}/authorized_accounts.json' 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')