Files
2026-10-14 13:34:37 +02:00

178 lines
7.7 KiB
Python

from datetime import datetime
from flask import Blueprint, render_template, request, redirect, url_for, flash, session
from tenancy import huidige_school, per_school
from blueprints.auth import login_required
from rechten import recht_vereist
from config import Config
from services.tweestaps_service import TweestapsService, TweestapsError
tweestaps_bp = Blueprint('tweestaps', __name__)
LOG_PER_PAGINA = 25 # standaard; te kiezen: 10 / 25 / 50 / 100 / alles (0)
LOG_KEUZES = (10, 25, 50, 100, 0)
service = per_school(TweestapsService)
_tables_ready = set() # scholen waarvoor de tabellen al gecontroleerd zijn
def _ensure_tables():
if huidige_school().code not in _tables_ready:
try:
service.ensure_tables()
_tables_ready.add(huidige_school().code)
except Exception as e:
flash(f"Kon de 2FA-tabellen niet aanmaken: {e}", "error")
def _render(email=None, codes=None, code_actie=None, fout=None):
"""Bouwt de pagina op; codes worden enkel in deze response getoond, nooit bewaard of gelogd."""
tab = request.args.get('tab', 'hulp')
zoek = (request.args.get('zoek') or '').strip()
email = email or (request.args.get('email') or '').strip()
gebruiker, uitzondering, zoekresultaten, reset_wacht = None, None, [], None
if zoek and '@' not in zoek:
zoekresultaten = service.search_users(zoek)
if not zoekresultaten:
fout = fout or f"Geen gebruikers gevonden voor '{zoek}'."
elif zoek:
email = zoek
if email:
try:
gebruiker = service.get_user(email)
uitzondering = service.active_for(gebruiker['email'])
reset_wacht = service.reset_bezig(gebruiker['email'])
except TweestapsError as e:
fout = fout or str(e)
try:
actief = service.active_exceptions()
# Aantal per pagina: uit ?logper=..., anders de vorige keuze (sessie), anders 25
per = request.args.get('logper', type=int)
if per in LOG_KEUZES:
session['tweestaps_logper'] = per
per = session.get('tweestaps_logper', LOG_PER_PAGINA)
grootte = per or 100000 # 0 = alles
log_pagina = max(1, request.args.get('logpagina', 1, type=int) or 1)
logboek, log_totaal = service.recent_log(log_pagina, grootte)
log_paginas = max(1, -(-log_totaal // grootte))
if log_pagina > log_paginas:
log_pagina = log_paginas
logboek, log_totaal = service.recent_log(log_pagina, grootte)
except Exception:
actief, logboek, log_totaal, log_pagina, log_paginas, per = [], [], 0, 1, 1, LOG_PER_PAGINA
lijst, lijst_fout = None, None
if tab == 'lijst':
try:
lijst = service.niet_ingesteld(force=request.args.get('vernieuw') == '1')
lijst_fout = service.ou_melding() # sommige OU's bestaan niet: lijst tonen + waarschuwing
except TweestapsError as e:
lijst_fout = str(e)
except Exception as e:
lijst_fout = TweestapsService._friendly(e)
return render_template('tweestaps.html', tab=tab, zoek=zoek, zoekresultaten=zoekresultaten,
gebruiker=gebruiker, uitzondering=uitzondering, reset_wacht=reset_wacht, codes=codes, code_actie=code_actie,
fout=fout, actief=actief, logboek=logboek, log_totaal=log_totaal, log_pagina=log_pagina, log_per=per, log_keuzes=LOG_KEUZES,
log_paginas=log_paginas, lijst=lijst, lijst_fout=lijst_fout,
einde=service.einde_vandaag(), groep=service.exception_group,
ous=Config.TWEESTAPS_OUS, nu=datetime.now())
@tweestaps_bp.route('/', methods=['GET'])
@login_required
@recht_vereist('tweestaps_codes', 'tweestaps_uitzetten')
def overzicht():
_ensure_tables()
return _render()
#---------------------------------------------
# Back-upcodes
#---------------------------------------------
@tweestaps_bp.route('/codes', methods=['POST'])
@login_required
@recht_vereist('tweestaps_codes')
def codes():
_ensure_tables()
email = request.form.get('email', '')
actie = request.form.get('actie', 'tonen')
door = session.get('user')
try:
user = service.get_user(email)
if actie == 'ongeldig':
service.invalidate_codes(user['email'])
service.log(door, 'codes ongeldig gemaakt', user['email'])
flash(f"Alle back-upcodes van {user['email']} zijn ongeldig gemaakt.", "success")
return redirect(url_for('tweestaps.overzicht', email=user['email']))
bestaand = [] if actie == 'nieuw' else service.list_codes(user['email'])
if bestaand:
lijst = bestaand
service.log(door, 'code getoond', user['email'])
else:
lijst = service.generate_codes(user['email'])
service.log(door, 'nieuwe codes', user['email'], f"{len(lijst)} codes")
return _render(email=user['email'], codes=lijst, code_actie='nieuw' if not bestaand else 'bestaand')
except TweestapsError as e:
return _render(email=email, fout=str(e))
#---------------------------------------------
# Tijdelijke uitzondering
#---------------------------------------------
@tweestaps_bp.route('/uitzondering', methods=['POST'])
@login_required
@recht_vereist('tweestaps_uitzetten')
def uitzondering():
_ensure_tables()
email = request.form.get('email', '')
reden = (request.form.get('reden') or '').strip()
eindtijd = service.einde_vandaag()
try:
user = service.grant_exception(email, eindtijd, session.get('user'), reden)
flash(f"{user['email']} kan tot {eindtijd:%H:%M} zonder 2FA inloggen. Daarna zet PIVOT 2FA automatisch "
"terug aan. Let op: het kan enkele minuten duren voor Google de uitzondering toepast.", "success")
except TweestapsError as e:
flash(str(e), "error")
return redirect(url_for('tweestaps.overzicht', email=email.strip()))
@tweestaps_bp.route('/reset', methods=['POST'])
@login_required
@recht_vereist('tweestaps_uitzetten')
def reset():
"""2FA resetten voor iemand met een nieuwe of verloren telefoon."""
_ensure_tables()
email = request.form.get('email', '')
eindtijd = service.einde_vandaag()
try:
user = service.reset_2fa(email, eindtijd, session.get('user'), (request.form.get('reden') or '').strip())
if user.get('wacht'):
flash(f"{user['email']} staat nu in de uitzonderingsgroep, maar Google past dat pas na enkele minuten toe. "
"PIVOT probeert de 2FA-reset elke minuut opnieuw (tot de uitzondering afloopt om "
f"{eindtijd:%H:%M}). Je hoeft niets meer te doen; vernieuw deze pagina om de status te zien.", "info")
return redirect(url_for('tweestaps.overzicht', email=email.strip()))
tekst = f"2FA is uitgezet voor {user['email']}. Hij kan nu inloggen en 2FA opnieuw instellen met zijn nieuwe telefoon"
if user.get('uitzondering'):
tekst += f" (vóór {eindtijd:%H:%M}; daarna is 2FA weer verplicht)"
flash(tekst + ". Het kan enkele minuten duren voor Google dit toepast.", "success")
except TweestapsError as e:
flash(str(e), "error")
return redirect(url_for('tweestaps.overzicht', email=email.strip()))
@tweestaps_bp.route('/uitzondering/<int:uitz_id>/stop', methods=['POST'])
@login_required
@recht_vereist('tweestaps_uitzetten')
def stop_uitzondering(uitz_id):
_ensure_tables()
try:
email = service.end_exception(uitz_id, session.get('user'))
flash(f"2FA is opnieuw verplicht voor {email}.", "success")
except TweestapsError as e:
flash(str(e), "error")
terug = request.form.get('terug')
return redirect(url_for('tweestaps.overzicht', email=terug) if terug else url_for('tweestaps.overzicht'))