Initial commit
This commit is contained in:
commit
536ff43ce4
158 files changed
+30355
No files matched your search
@@ -0,0 +1,291 @@
|
||||
"""
|
||||
Gebruikers & rechten.
|
||||
|
||||
- Administrators: alle gebruikers, alle scholen, rol (Administrator/Gebruiker) wijzigen.
|
||||
- Gebruikers met het recht 'gebruikersbeheer' in een school: gebruikers aanmaken en hun
|
||||
rechten in DIE school(en) instellen. Ze kunnen geen administrators wijzigen.
|
||||
|
||||
Gebruikers staan in de centrale database (één account voor alle scholen); de rechten per
|
||||
school in Gebruiker_rechten.
|
||||
"""
|
||||
from collections import OrderedDict
|
||||
|
||||
from flask import Blueprint, render_template, request, session, redirect, url_for, flash
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
import rechten
|
||||
from rechten import ADMIN, GEBRUIKER, VEILIGHEIDSGROEPEN, ONDERDELEN, is_admin
|
||||
from database import central_query, central_insert
|
||||
from tenancy import alle_scholen, school_op_code
|
||||
|
||||
admin_bp = Blueprint('admin', __name__)
|
||||
|
||||
MIN_WACHTWOORD = 8
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Hulpfuncties
|
||||
# ------------------------------------------------------------------
|
||||
def beheerbare_scholen():
|
||||
"""Scholen waarvan de ingelogde gebruiker de rechten mag instellen."""
|
||||
scholen = alle_scholen(ook_inactief=True)
|
||||
if is_admin():
|
||||
return scholen
|
||||
gid = session.get('gebruiker_id')
|
||||
if not gid:
|
||||
return []
|
||||
rows = central_query(
|
||||
"SELECT r.School_ID FROM Gebruiker_rechten r JOIN Onderdelen o ON o.Onderdeel_ID = r.Onderdeel_ID "
|
||||
"WHERE r.Gebruiker_ID = %s AND o.Code = 'gebruikersbeheer'", (gid,))
|
||||
ids = {r['School_ID'] for r in rows}
|
||||
return [s for s in scholen if s.id in ids]
|
||||
|
||||
|
||||
def _toegang():
|
||||
"""None als de gebruiker hier mag komen, anders een redirect."""
|
||||
if not session.get('auth') or not session.get('school'):
|
||||
return redirect(url_for('auth.login'))
|
||||
if not is_admin() and not rechten.heeft_recht('gebruikersbeheer'):
|
||||
flash("Je hebt geen rechten om gebruikers te beheren.", "error")
|
||||
return redirect(url_for('main.dashboard'))
|
||||
return None
|
||||
|
||||
|
||||
def _aantal_admins(behalve=None):
|
||||
rows = central_query("SELECT COUNT(*) AS n FROM Gebruikers WHERE Veiligheidsgroep = %s AND Actief = 1 "
|
||||
"AND Gebruiker_ID <> %s", (ADMIN, behalve or 0))
|
||||
return rows[0]['n']
|
||||
|
||||
|
||||
def _onderdelen_per_categorie():
|
||||
groepen = OrderedDict()
|
||||
for code, naam, cat in ONDERDELEN:
|
||||
groepen.setdefault(cat, []).append((code, naam))
|
||||
return groepen
|
||||
|
||||
|
||||
def _wachtwoord_ok(pw, pw2):
|
||||
if pw != pw2:
|
||||
return "De wachtwoorden komen niet overeen."
|
||||
if len(pw) < MIN_WACHTWOORD:
|
||||
return f"Het wachtwoord moet minstens {MIN_WACHTWOORD} tekens hebben."
|
||||
return None
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Overzicht + nieuwe gebruiker
|
||||
# ------------------------------------------------------------------
|
||||
@admin_bp.route("/settings", methods=["GET"])
|
||||
def settings():
|
||||
weg = _toegang()
|
||||
if weg:
|
||||
return weg
|
||||
users = central_query(
|
||||
"SELECT g.Gebruiker_ID, g.Gebruikersnaam, g.Emailadres, g.Veiligheidsgroep, g.Actief, g.Last_login, "
|
||||
" v.Naam AS Rol "
|
||||
"FROM Gebruikers g JOIN Veiligheidsgroepen v ON v.Veiligheidsgroep = g.Veiligheidsgroep "
|
||||
"ORDER BY g.Veiligheidsgroep, g.Gebruikersnaam")
|
||||
if not is_admin():
|
||||
users = [u for u in users if int(u['Veiligheidsgroep']) != ADMIN]
|
||||
|
||||
# Aantal rechten per gebruiker per school (voor het overzicht)
|
||||
per = {}
|
||||
for r in central_query("SELECT Gebruiker_ID, School_ID, COUNT(*) AS n FROM Gebruiker_rechten "
|
||||
"GROUP BY Gebruiker_ID, School_ID"):
|
||||
per.setdefault(r['Gebruiker_ID'], {})[r['School_ID']] = r['n']
|
||||
scholen = {s.id: s for s in alle_scholen(ook_inactief=True)}
|
||||
for u in users:
|
||||
u['scholen'] = sorted(((scholen[sid].naam, n) for sid, n in per.get(u['Gebruiker_ID'], {}).items()
|
||||
if sid in scholen), key=lambda x: (-x[1], x[0].lower()))
|
||||
|
||||
return render_template("admin_accounts.html", users=users, groepen=VEILIGHEIDSGROEPEN,
|
||||
ADMIN=ADMIN, totaal_onderdelen=len(ONDERDELEN))
|
||||
|
||||
|
||||
@admin_bp.route("/gebruiker/nieuw", methods=["POST"])
|
||||
def nieuw():
|
||||
weg = _toegang()
|
||||
if weg:
|
||||
return weg
|
||||
naam = (request.form.get('username') or '').strip()
|
||||
email = (request.form.get('email') or '').strip().lower() or None
|
||||
pw = request.form.get('password') or ''
|
||||
pw2 = request.form.get('password_confirm') or ''
|
||||
groep = int(request.form.get('veiligheidsgroep') or GEBRUIKER)
|
||||
if groep not in VEILIGHEIDSGROEPEN or (groep == ADMIN and not is_admin()):
|
||||
groep = GEBRUIKER
|
||||
|
||||
fout = None
|
||||
if not naam:
|
||||
fout = "Vul een gebruikersnaam in."
|
||||
elif not pw and not email:
|
||||
fout = "Geef een wachtwoord, of een e-mailadres om met Google in te loggen."
|
||||
elif pw:
|
||||
fout = _wachtwoord_ok(pw, pw2)
|
||||
if not fout and rechten.zoek_gebruiker(naam):
|
||||
fout = f"Er bestaat al een gebruiker '{naam}'."
|
||||
if not fout and email and rechten.zoek_gebruiker(email):
|
||||
fout = f"Het e-mailadres {email} is al in gebruik."
|
||||
if fout:
|
||||
flash(fout, "error")
|
||||
return redirect(url_for('admin.settings', nieuw=1))
|
||||
|
||||
nieuw_id = central_insert(
|
||||
"INSERT INTO Gebruikers (Gebruikersnaam, Veiligheidsgroep, Passwoordhash, Emailadres, Actief) "
|
||||
"VALUES (%s, %s, %s, %s, 1)",
|
||||
(naam, groep, generate_password_hash(pw) if pw else None, email))
|
||||
rechten.log(actie=f"Gebruiker {naam} aangemaakt")
|
||||
if groep == ADMIN:
|
||||
flash(f"Administrator '{naam}' aangemaakt.", "success")
|
||||
return redirect(url_for('admin.settings'))
|
||||
flash(f"Gebruiker '{naam}' aangemaakt. Vink nu aan wat hij per school mag.", "success")
|
||||
return redirect(url_for('admin.gebruiker_rechten', gid=nieuw_id))
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Eén gebruiker: account (gegevens, rol, wachtwoord)
|
||||
# ------------------------------------------------------------------
|
||||
def _gebruiker_of_terug(gid):
|
||||
"""(gebruiker, None) of (None, redirect)."""
|
||||
u = rechten.gebruiker_op_id(gid)
|
||||
if not u:
|
||||
flash("Onbekende gebruiker.", "error")
|
||||
return None, redirect(url_for('admin.settings'))
|
||||
if int(u['Veiligheidsgroep']) == ADMIN and not is_admin():
|
||||
flash("Enkel een administrator kan een administrator wijzigen.", "error")
|
||||
return None, redirect(url_for('admin.settings'))
|
||||
return u, None
|
||||
|
||||
|
||||
@admin_bp.route("/gebruiker/<int:gid>", methods=["GET", "POST"])
|
||||
def gebruiker(gid):
|
||||
weg = _toegang()
|
||||
if weg:
|
||||
return weg
|
||||
u, weg = _gebruiker_of_terug(gid)
|
||||
if weg:
|
||||
return weg
|
||||
zelf = gid == session.get('gebruiker_id')
|
||||
if request.method == 'POST':
|
||||
return _bewaar_account(u, zelf)
|
||||
return render_template("admin_gebruiker.html", u=u, groepen=VEILIGHEIDSGROEPEN, ADMIN=ADMIN, zelf=zelf,
|
||||
min_wachtwoord=MIN_WACHTWOORD)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Eén gebruiker: rechten, één school tegelijk
|
||||
# ------------------------------------------------------------------
|
||||
@admin_bp.route("/gebruiker/<int:gid>/rechten", methods=["GET", "POST"])
|
||||
def gebruiker_rechten(gid):
|
||||
weg = _toegang()
|
||||
if weg:
|
||||
return weg
|
||||
u, weg = _gebruiker_of_terug(gid)
|
||||
if weg:
|
||||
return weg
|
||||
scholen = beheerbare_scholen()
|
||||
per_id = {s.id: s for s in scholen}
|
||||
aantallen = {}
|
||||
for r in central_query("SELECT School_ID, COUNT(*) AS n FROM Gebruiker_rechten WHERE Gebruiker_ID = %s "
|
||||
"GROUP BY School_ID", (gid,)):
|
||||
aantallen[r['School_ID']] = r['n']
|
||||
|
||||
if request.method == 'POST':
|
||||
sid = request.form.get('school_id', type=int)
|
||||
if sid not in per_id:
|
||||
flash("Je mag de rechten in deze school niet instellen.", "error")
|
||||
return redirect(url_for('admin.gebruiker_rechten', gid=gid))
|
||||
codes = [c for c in rechten.CODES if request.form.get(f"r|{c}")]
|
||||
doel = [sid]
|
||||
if request.form.get('ook_alle'):
|
||||
doel = list(per_id)
|
||||
else:
|
||||
doel += [int(x) for x in request.form.getlist('ook') if x.isdigit() and int(x) in per_id]
|
||||
doel = list(dict.fromkeys(doel))
|
||||
door = session.get('user')
|
||||
for school_id in doel:
|
||||
rechten.zet_rechten(gid, school_id, codes, door=door)
|
||||
if len(doel) == 1:
|
||||
rechten.log(actie=f"Rechten van {u['Gebruikersnaam']} in {per_id[sid].naam} gewijzigd")
|
||||
flash(f"Rechten van {u['Gebruikersnaam']} in {per_id[sid].naam} opgeslagen.", "success")
|
||||
else:
|
||||
rechten.log(actie=f"Rechten van {u['Gebruikersnaam']} in {len(doel)} scholen gewijzigd")
|
||||
flash(f"Rechten van {u['Gebruikersnaam']} opgeslagen in {len(doel)} scholen.", "success")
|
||||
return redirect(url_for('admin.gebruiker_rechten', gid=gid, school=sid))
|
||||
|
||||
# Welke school tonen: gevraagd > huidige school > eerste school met rechten > eerste school
|
||||
gekozen = request.args.get('school', type=int)
|
||||
if gekozen not in per_id:
|
||||
huidig = school_op_code(session.get('school') or '')
|
||||
huidig_id = huidig.id if huidig else None
|
||||
if huidig_id in per_id and (aantallen.get(huidig_id) or not aantallen):
|
||||
gekozen = huidig_id
|
||||
else:
|
||||
met = [s.id for s in scholen if aantallen.get(s.id)]
|
||||
gekozen = met[0] if met else (scholen[0].id if scholen else None)
|
||||
|
||||
lijst = sorted(scholen, key=lambda s: (not aantallen.get(s.id), s.naam.lower()))
|
||||
return render_template("admin_rechten.html", u=u, scholen=lijst, gekozen=per_id.get(gekozen),
|
||||
aantallen=aantallen, huidig=rechten.rechten_van(gid, gekozen) if gekozen else set(),
|
||||
categorieen=_onderdelen_per_categorie(), ADMIN=ADMIN, totaal=len(ONDERDELEN))
|
||||
|
||||
|
||||
def _bewaar_account(u, zelf):
|
||||
gid = u['Gebruiker_ID']
|
||||
email = (request.form.get('email') or '').strip().lower() or None
|
||||
pw = request.form.get('new_password') or ''
|
||||
pw2 = request.form.get('new_password_confirm') or ''
|
||||
actief = 1 if request.form.get('actief') else 0
|
||||
groep = int(u['Veiligheidsgroep'])
|
||||
if is_admin() and request.form.get('veiligheidsgroep'):
|
||||
groep = int(request.form['veiligheidsgroep'])
|
||||
if groep not in VEILIGHEIDSGROEPEN:
|
||||
groep = int(u['Veiligheidsgroep'])
|
||||
|
||||
terug = redirect(url_for('admin.gebruiker', gid=gid))
|
||||
if zelf and (not actief or groep != int(u['Veiligheidsgroep'])):
|
||||
flash("Je kan je eigen rol niet wijzigen en jezelf niet uitschakelen.", "error")
|
||||
return terug
|
||||
if int(u['Veiligheidsgroep']) == ADMIN and (groep != ADMIN or not actief) and _aantal_admins(behalve=gid) == 0:
|
||||
flash("Er moet minstens één actieve administrator overblijven.", "error")
|
||||
return terug
|
||||
if email:
|
||||
ander = rechten.zoek_gebruiker(email)
|
||||
if ander and ander['Gebruiker_ID'] != gid:
|
||||
flash(f"Het e-mailadres {email} is al in gebruik.", "error")
|
||||
return terug
|
||||
if pw:
|
||||
fout = _wachtwoord_ok(pw, pw2)
|
||||
if fout:
|
||||
flash(fout, "error")
|
||||
return terug
|
||||
central_query("UPDATE Gebruikers SET Passwoordhash = %s WHERE Gebruiker_ID = %s",
|
||||
(generate_password_hash(pw), gid), commit=True)
|
||||
|
||||
central_query("UPDATE Gebruikers SET Emailadres = %s, Veiligheidsgroep = %s, Actief = %s WHERE Gebruiker_ID = %s",
|
||||
(email, groep, actief, gid), commit=True)
|
||||
rechten.log(actie=f"Account {u['Gebruikersnaam']} gewijzigd")
|
||||
flash(f"Account van {u['Gebruikersnaam']} opgeslagen.", "success")
|
||||
return terug
|
||||
|
||||
|
||||
@admin_bp.route("/gebruiker/<int:gid>/verwijderen", methods=["POST"])
|
||||
def verwijderen(gid):
|
||||
weg = _toegang()
|
||||
if weg:
|
||||
return weg
|
||||
u = rechten.gebruiker_op_id(gid)
|
||||
if not u:
|
||||
return redirect(url_for('admin.settings'))
|
||||
if not is_admin():
|
||||
flash("Enkel een administrator kan gebruikers verwijderen.", "error")
|
||||
elif gid == session.get('gebruiker_id'):
|
||||
flash("Je kan jezelf niet verwijderen.", "error")
|
||||
elif int(u['Veiligheidsgroep']) == ADMIN and _aantal_admins(behalve=gid) == 0:
|
||||
flash("Er moet minstens één actieve administrator overblijven.", "error")
|
||||
else:
|
||||
central_query("DELETE FROM Gebruikers WHERE Gebruiker_ID = %s", (gid,), commit=True)
|
||||
rechten.log(actie=f"Gebruiker {u['Gebruikersnaam']} verwijderd")
|
||||
flash(f"Gebruiker '{u['Gebruikersnaam']}' is verwijderd.", "success")
|
||||
return redirect(url_for('admin.settings'))
|
||||
return redirect(url_for('admin.gebruiker', gid=gid))
|
||||
Reference in new issue
Block a user