476 lines
23 KiB
Python
476 lines
23 KiB
Python
"""
|
|
Rechtensysteem: wie mag wat, in welke school.
|
|
|
|
Gebruikers staan in de CENTRALE database (één account voor alle scholen).
|
|
Per gebruiker wordt per school bijgehouden op welke onderdelen hij rechten heeft:
|
|
|
|
Gebruiker_rechten (Gebruiker_ID, School_ID, Onderdeel_ID)
|
|
|
|
Zo kan iemand in school A '2FA tijdelijk uitzetten' mogen en in school B niet.
|
|
|
|
Veiligheidsgroepen:
|
|
1 = Administrator : alle rechten in alle scholen, beheert gebruikers, rechten en scholen
|
|
2 = Gebruiker : enkel de rechten die per school zijn aangevinkt
|
|
5 = Leerkracht : (geen account) personeelslid dat met Google inlogt → enkel de personeelsmodule
|
|
|
|
Gebruik in de code:
|
|
@recht_vereist('herstellingen') # route
|
|
if heeft_recht('tweestaps_uitzetten'): ... # in Python
|
|
{% if mag('co2') %} ... {% endif %} # in templates
|
|
"""
|
|
import functools
|
|
import sys
|
|
from collections import OrderedDict
|
|
|
|
from flask import session, g, flash, redirect, url_for, request, has_request_context
|
|
|
|
ADMIN = 1
|
|
GEBRUIKER = 2
|
|
|
|
VEILIGHEIDSGROEPEN = OrderedDict([
|
|
(ADMIN, 'Administrator'),
|
|
(GEBRUIKER, 'Gebruiker'),
|
|
])
|
|
|
|
# (code, naam, categorie). De volgorde hier is de volgorde op het scherm.
|
|
# Nieuwe onderdelen worden bij het opstarten automatisch in de tabel Onderdelen gezet.
|
|
ONDERDELEN = [
|
|
# --- Chromebooks ---
|
|
('eigendom_bekijken', 'Eigendom bekijken', 'Chromebooks'),
|
|
('toestellen_uitdelen', 'Toestellen uitdelen en inleveren', 'Chromebooks'),
|
|
('toestellen_beheren', 'Toestelleninventaris beheren', 'Chromebooks'),
|
|
('toestel_blokkeren', 'Chromebook blokkeren / deblokkeren', 'Chromebooks'),
|
|
('herstellingen', 'Herstellingen', 'Chromebooks'),
|
|
('signpost', 'Signpost-toestellen', 'Chromebooks'),
|
|
# --- Leerlingen ---
|
|
('leerlingen_bekijken', 'Leerlingen bekijken en bewerken', 'Leerlingen'),
|
|
('leerlingen_synchroniseren', 'Leerlingen toevoegen en synchroniseren', 'Leerlingen'),
|
|
('leerlingenaantal', 'Leerlingenaantallen (alle scholen)', 'Leerlingen'),
|
|
# --- Basisonderwijs (blok enkel zichtbaar bij een basisschool) ---
|
|
('bingel', 'Bingel: leerlingenlijst (Excel)', 'Basisonderwijs'),
|
|
# --- Internaat ---
|
|
('internen', 'Internaat: internen bekijken', 'Internaat'),
|
|
('internaat_aanwezigheid', 'Internaat: aanwezigheden registreren', 'Internaat'),
|
|
# --- Leerlingfacturatie ---
|
|
('leerlingfacturatie', 'Leerlingfacturatie: facturen bekijken', 'Leerlingfacturatie'),
|
|
# --- Google ---
|
|
('google_wachtwoord', 'Google-wachtwoord resetten', 'Google'),
|
|
('google_accounts', 'Google-accounts aanmaken', 'Google'),
|
|
('tweestaps_codes', '2FA: back-upcodes tonen / aanmaken', 'Google'),
|
|
('tweestaps_uitzetten', '2FA tijdelijk uitzetten (uitzondering)', 'Google'),
|
|
('google_licenties', 'Google-licenties', 'Google'),
|
|
('gmail_delegatie', 'Mailbox-delegatie', 'Google'),
|
|
('verzendlijsten', 'Verzendlijsten (Google Groepen) beheren', 'Google'),
|
|
('gmail_labels', 'E-mails met een label naar een andere mailbox', 'Google'),
|
|
('gmail_handtekeningen', 'Gmail-handtekeningen instellen (functie/telefoon)', 'Google'),
|
|
('gedeelde_drives', 'Gedeelde drives: leden en rechten beheren', 'Google'),
|
|
('gmail_contacten', 'Contacten: contactlijsten in mailboxen, adresboek', 'Google'),
|
|
# --- Smartschool ---
|
|
('smartschool', 'Smartschool-wachtwoorden & info', 'Smartschool'),
|
|
('smartschool_activiteit', 'Activiteit co-accounts & berichten', 'Smartschool'),
|
|
('afwezigheden', 'Afwezigheden leerlingen (B-codes)', 'Smartschool'),
|
|
('laatkomers', 'Laatkomers', 'Smartschool'),
|
|
('skore', 'Skore-synchronisatie (starten en plannen)', 'Smartschool'),
|
|
('sgr_groepen', 'SGR groepen: subgroepen en leden bekijken', 'Smartschool'),
|
|
# --- Personeel ---
|
|
('personeel_directie', 'Personeel: aanvragen goedkeuren (directie)', 'Personeel'),
|
|
('personeel_secretariaat', 'Personeel: dagoverzicht (secretariaat)', 'Personeel'),
|
|
('gopro', 'GO! Pro: personeel en rollen', 'Personeel'),
|
|
('personeelsaantal', 'Personeelsaantallen (alle scholen)', 'Personeel'),
|
|
# --- Tools ---
|
|
('qr_codes', 'QR-code generator', 'Tools'),
|
|
# --- IoT ---
|
|
('co2', 'CO2-sensoren', 'IoT'),
|
|
# --- Scholengroep ---
|
|
# --- Beheer ---
|
|
('bugs_bekijken', 'Bugs bekijken', 'Beheer'),
|
|
('bugs_beheren', 'Status van bugs wijzigen', 'Beheer'),
|
|
('nieuw_schooljaar', 'Nieuw schooljaar (accounts & toestellen)', 'Beheer'),
|
|
('ip_whitelist', 'IP-adressen toestaan voor deze school', 'Beheer'),
|
|
('database_toestellen', 'Database: toestellen importeren uit Excel', 'Beheer'),
|
|
# --- PIVOT ---
|
|
('gebruikersbeheer', 'Gebruikers & rechten van deze school', 'PIVOT'),
|
|
('koppelingen', 'Koppelingen van deze school', 'PIVOT'),
|
|
]
|
|
CODES = [o[0] for o in ONDERDELEN]
|
|
|
|
|
|
# =====================================================================
|
|
# Tabellen (centrale database)
|
|
# =====================================================================
|
|
SCHEMA = [
|
|
"""CREATE TABLE IF NOT EXISTS Veiligheidsgroepen (
|
|
Veiligheidsgroep TINYINT NOT NULL PRIMARY KEY,
|
|
Naam VARCHAR(50) NOT NULL,
|
|
Omschrijving VARCHAR(255) NULL
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS Gebruikers (
|
|
Gebruiker_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
Gebruikersnaam VARCHAR(100) NOT NULL,
|
|
Veiligheidsgroep TINYINT NOT NULL DEFAULT 2,
|
|
Passwoordhash VARCHAR(255) NULL,
|
|
Emailadres VARCHAR(255) NULL,
|
|
Last_login DATETIME NULL,
|
|
IP_laatste_login VARCHAR(45) NULL,
|
|
Laatste_school VARCHAR(32) NULL,
|
|
Actief TINYINT NOT NULL DEFAULT 1,
|
|
UNIQUE KEY uq_gebruikersnaam (Gebruikersnaam),
|
|
UNIQUE KEY uq_emailadres (Emailadres),
|
|
CONSTRAINT fk_gebruikers_groep FOREIGN KEY (Veiligheidsgroep)
|
|
REFERENCES Veiligheidsgroepen (Veiligheidsgroep)
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS Onderdelen (
|
|
Onderdeel_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
Code VARCHAR(50) NOT NULL,
|
|
Naam VARCHAR(255) NOT NULL,
|
|
Categorie VARCHAR(50) NOT NULL,
|
|
Volgorde INT NOT NULL DEFAULT 0,
|
|
UNIQUE KEY uq_onderdeel_code (Code)
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS Gebruiker_rechten (
|
|
Gebruiker_ID INT NOT NULL,
|
|
School_ID INT NOT NULL,
|
|
Onderdeel_ID INT NOT NULL,
|
|
Toegekend_op DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
Toegekend_door VARCHAR(100) NULL,
|
|
PRIMARY KEY (Gebruiker_ID, School_ID, Onderdeel_ID),
|
|
KEY idx_rechten_school (School_ID),
|
|
KEY idx_rechten_onderdeel (Onderdeel_ID),
|
|
CONSTRAINT fk_rechten_gebruiker FOREIGN KEY (Gebruiker_ID)
|
|
REFERENCES Gebruikers (Gebruiker_ID) ON DELETE CASCADE,
|
|
CONSTRAINT fk_rechten_school FOREIGN KEY (School_ID)
|
|
REFERENCES Scholen (School_ID) ON DELETE CASCADE,
|
|
CONSTRAINT fk_rechten_onderdeel FOREIGN KEY (Onderdeel_ID)
|
|
REFERENCES Onderdelen (Onderdeel_ID) ON DELETE CASCADE
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS Handleidingen (
|
|
Handleiding_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
Slug VARCHAR(100) NOT NULL,
|
|
Titel VARCHAR(200) NOT NULL,
|
|
Categorie VARCHAR(100) NULL,
|
|
Samenvatting VARCHAR(300) NULL,
|
|
Inhoud MEDIUMTEXT NULL,
|
|
Zichtbaar VARCHAR(10) NOT NULL DEFAULT 'iedereen',
|
|
Volgorde INT NOT NULL DEFAULT 0,
|
|
Gewijzigd_op DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
Gewijzigd_door VARCHAR(100) NULL,
|
|
UNIQUE KEY uq_handleiding_slug (Slug)
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS Handleidingen_versies (
|
|
Versie_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
Handleiding_ID INT NOT NULL,
|
|
Titel VARCHAR(200) NOT NULL,
|
|
Inhoud MEDIUMTEXT NULL,
|
|
Opgeslagen_op DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
Door VARCHAR(100) NULL,
|
|
KEY idx_versie_handleiding (Handleiding_ID, Opgeslagen_op),
|
|
CONSTRAINT fk_versie_handleiding FOREIGN KEY (Handleiding_ID)
|
|
REFERENCES Handleidingen (Handleiding_ID) ON DELETE CASCADE
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS Leerlingenaantal_tellingen (
|
|
Telling_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
School_ID INT NOT NULL,
|
|
Peildatum DATE NOT NULL,
|
|
Schooljaar VARCHAR(10) NOT NULL,
|
|
Aantal INT NOT NULL,
|
|
Detail TEXT NULL,
|
|
Opgevraagd_op DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
Door VARCHAR(100) NULL,
|
|
UNIQUE KEY uq_telling (School_ID, Peildatum),
|
|
KEY idx_telling_datum (Peildatum),
|
|
CONSTRAINT fk_telling_school FOREIGN KEY (School_ID)
|
|
REFERENCES Scholen (School_ID) ON DELETE CASCADE
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS Personeelsaantal_tellingen (
|
|
Telling_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
School_ID INT NOT NULL,
|
|
Peildatum DATE NOT NULL,
|
|
Schooljaar VARCHAR(10) NOT NULL,
|
|
Aantal INT NOT NULL,
|
|
Detail TEXT NULL,
|
|
Opgevraagd_op DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
Door VARCHAR(100) NULL,
|
|
UNIQUE KEY uq_ptelling (School_ID, Peildatum),
|
|
KEY idx_ptelling_datum (Peildatum),
|
|
CONSTRAINT fk_ptelling_school FOREIGN KEY (School_ID)
|
|
REFERENCES Scholen (School_ID) ON DELETE CASCADE
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS Personeelsaantal_huidig (
|
|
School_ID INT NOT NULL PRIMARY KEY,
|
|
Aantal INT NOT NULL,
|
|
Detail TEXT NULL,
|
|
Gesynchroniseerd DATETIME NOT NULL,
|
|
CONSTRAINT fk_phuidig_school FOREIGN KEY (School_ID)
|
|
REFERENCES Scholen (School_ID) ON DELETE CASCADE
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS PIVOT_instellingen (
|
|
Sleutel VARCHAR(50) NOT NULL PRIMARY KEY,
|
|
Waarde VARCHAR(255) NULL,
|
|
Bijgewerkt DATETIME NULL,
|
|
Door VARCHAR(100) NULL
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS IP_blokkades (
|
|
ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
Reeks VARCHAR(64) NOT NULL,
|
|
Soort VARCHAR(10) NOT NULL DEFAULT 'blokkeren',
|
|
Opmerking VARCHAR(255) NULL,
|
|
Scholen VARCHAR(1000) NULL,
|
|
Aangemaakt DATETIME NOT NULL,
|
|
Door VARCHAR(100) NULL,
|
|
Gebruiker_ID INT NULL,
|
|
Aantal_geweigerd INT NOT NULL DEFAULT 0,
|
|
Laatst_geweigerd DATETIME NULL,
|
|
UNIQUE KEY uq_ip_reeks (Reeks, Soort)
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS Koppeling_tests (
|
|
School_ID INT NOT NULL,
|
|
Onderdeel VARCHAR(50) NOT NULL,
|
|
Ok TINYINT NULL,
|
|
Detail VARCHAR(500) NULL,
|
|
Getest_op DATETIME NOT NULL,
|
|
PRIMARY KEY (School_ID, Onderdeel),
|
|
CONSTRAINT fk_ktest_school FOREIGN KEY (School_ID) REFERENCES Scholen (School_ID) ON DELETE CASCADE
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS Logs (
|
|
Log_ID BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
Timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
Bezochte_url VARCHAR(2048) NOT NULL,
|
|
Gebruiker_ID INT NULL,
|
|
School_ID INT NULL,
|
|
Actie VARCHAR(255) NULL,
|
|
IP VARCHAR(45) NULL,
|
|
KEY idx_logs_tijd (Timestamp),
|
|
KEY idx_logs_gebruiker (Gebruiker_ID),
|
|
CONSTRAINT fk_logs_gebruiker FOREIGN KEY (Gebruiker_ID)
|
|
REFERENCES Gebruikers (Gebruiker_ID) ON DELETE SET NULL,
|
|
CONSTRAINT fk_logs_school FOREIGN KEY (School_ID)
|
|
REFERENCES Scholen (School_ID) ON DELETE SET NULL
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS Status (
|
|
Bugstatus_ID TINYINT NOT NULL PRIMARY KEY,
|
|
Omschrijving VARCHAR(50) NOT NULL,
|
|
UNIQUE KEY uq_status_omschrijving (Omschrijving)
|
|
) ENGINE=InnoDB""",
|
|
"""CREATE TABLE IF NOT EXISTS Bugs (
|
|
Bug_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
Timestamp DATETIME NOT NULL,
|
|
Omschrijving TEXT NOT NULL,
|
|
Foutcode VARCHAR(100) NULL,
|
|
Bugstatus_ID TINYINT NOT NULL DEFAULT 1,
|
|
Gebruiker_ID INT NULL,
|
|
Gebruikersnaam VARCHAR(255) NULL,
|
|
School_ID INT NULL,
|
|
Gemeld_op DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
KEY idx_bugs_status (Bugstatus_ID),
|
|
CONSTRAINT fk_bugs_status FOREIGN KEY (Bugstatus_ID) REFERENCES Status (Bugstatus_ID),
|
|
CONSTRAINT fk_bugs_gebruiker FOREIGN KEY (Gebruiker_ID)
|
|
REFERENCES Gebruikers (Gebruiker_ID) ON DELETE SET NULL,
|
|
CONSTRAINT fk_bugs_school FOREIGN KEY (School_ID)
|
|
REFERENCES Scholen (School_ID) ON DELETE SET NULL
|
|
) ENGINE=InnoDB""",
|
|
]
|
|
|
|
BUGSTATUS = [(1, 'Nieuw'), (2, 'In behandeling'), (3, 'Fixed')]
|
|
|
|
|
|
def ensure_schema():
|
|
"""Maakt de tabellen aan en zet de vaste lijsten (groepen, onderdelen, bugstatussen) goed."""
|
|
from database import central_query
|
|
for sql in SCHEMA:
|
|
central_query(sql, commit=True)
|
|
# Bestaande tabel Gebruikers van vóór "laatst actieve school" (het aanmeldscherm vraagt geen school meer)
|
|
if 'laatste_school' not in {r['Field'].lower() for r in central_query("SHOW COLUMNS FROM Gebruikers") or []}:
|
|
central_query("ALTER TABLE Gebruikers ADD COLUMN Laatste_school VARCHAR(32) NULL AFTER IP_laatste_login", commit=True)
|
|
for nr, naam in VEILIGHEIDSGROEPEN.items():
|
|
central_query("INSERT INTO Veiligheidsgroepen (Veiligheidsgroep, Naam) VALUES (%s, %s) "
|
|
"ON DUPLICATE KEY UPDATE Naam = VALUES(Naam)", (nr, naam), commit=True)
|
|
for i, (code, naam, cat) in enumerate(ONDERDELEN, start=1):
|
|
central_query("INSERT INTO Onderdelen (Code, Naam, Categorie, Volgorde) VALUES (%s, %s, %s, %s) "
|
|
"ON DUPLICATE KEY UPDATE Naam = VALUES(Naam), Categorie = VALUES(Categorie), "
|
|
"Volgorde = VALUES(Volgorde)", (code, naam, cat, i * 10), commit=True)
|
|
for nr, oms in BUGSTATUS:
|
|
central_query("INSERT INTO Status (Bugstatus_ID, Omschrijving) VALUES (%s, %s) "
|
|
"ON DUPLICATE KEY UPDATE Omschrijving = VALUES(Omschrijving)", (nr, oms), commit=True)
|
|
# Standaardhandleidingen die nog niet bestaan (daarna beheer je ze in PIVOT; verwijderen = verbergen)
|
|
from services.handleidingen_standaard import STANDAARD
|
|
bestaand = {r['Slug'] for r in central_query("SELECT Slug FROM Handleidingen")}
|
|
for i, h in enumerate(STANDAARD, start=1):
|
|
if h['slug'] not in bestaand:
|
|
central_query("INSERT IGNORE INTO Handleidingen (Slug, Titel, Categorie, Samenvatting, Inhoud, Volgorde, "
|
|
"Gewijzigd_door) VALUES (%s, %s, %s, %s, %s, %s, 'PIVOT')",
|
|
(h['slug'], h['titel'], h['categorie'], h['samenvatting'], h['inhoud'].strip(), i * 10),
|
|
commit=True)
|
|
|
|
|
|
# =====================================================================
|
|
# Gebruikers
|
|
# =====================================================================
|
|
def zoek_gebruiker(naam_of_email):
|
|
"""Actieve of inactieve gebruiker op gebruikersnaam of e-mailadres (hoofdletterongevoelig)."""
|
|
from database import central_query
|
|
w = (naam_of_email or '').strip()
|
|
if not w:
|
|
return None
|
|
rows = central_query("SELECT * FROM Gebruikers WHERE Gebruikersnaam = %s OR Emailadres = %s "
|
|
"ORDER BY Gebruikersnaam = %s DESC LIMIT 1", (w, w.lower(), w))
|
|
return rows[0] if rows else None
|
|
|
|
|
|
def gebruiker_op_id(gid):
|
|
from database import central_query
|
|
rows = central_query("SELECT * FROM Gebruikers WHERE Gebruiker_ID = %s", (gid,))
|
|
return rows[0] if rows else None
|
|
|
|
|
|
def registreer_login(gid, ip):
|
|
from database import central_query
|
|
try:
|
|
central_query("UPDATE Gebruikers SET Last_login = NOW(), IP_laatste_login = %s WHERE Gebruiker_ID = %s",
|
|
(ip, gid), commit=True)
|
|
except Exception as e:
|
|
sys.stderr.write(f"PIVOT: laatste login niet bewaard: {e}\n")
|
|
|
|
|
|
def bewaar_laatste_school(gid, code):
|
|
"""Onthoudt de school waarin de gebruiker het laatst werkte (die wordt bij de volgende aanmelding geladen)."""
|
|
from database import central_query
|
|
if not gid or not code:
|
|
return
|
|
try:
|
|
central_query("UPDATE Gebruikers SET Laatste_school = %s WHERE Gebruiker_ID = %s", (code, gid), commit=True)
|
|
except Exception as e:
|
|
sys.stderr.write(f"PIVOT: laatst actieve school niet bewaard: {e}\n")
|
|
|
|
|
|
def rechten_van(gid, school_id):
|
|
"""Set met codes van onderdelen waarop gebruiker `gid` rechten heeft in school `school_id`."""
|
|
from database import central_query
|
|
rows = central_query(
|
|
"SELECT o.Code FROM Gebruiker_rechten r JOIN Onderdelen o ON o.Onderdeel_ID = r.Onderdeel_ID "
|
|
"WHERE r.Gebruiker_ID = %s AND r.School_ID = %s", (gid, school_id))
|
|
return {r['Code'] for r in rows}
|
|
|
|
|
|
def scholen_met_rechten(gid):
|
|
"""School_ID's waar de gebruiker minstens één recht heeft."""
|
|
from database import central_query
|
|
rows = central_query("SELECT DISTINCT School_ID FROM Gebruiker_rechten WHERE Gebruiker_ID = %s", (gid,))
|
|
return {r['School_ID'] for r in rows}
|
|
|
|
|
|
def mag_in_school(gebruiker, school):
|
|
"""Mag deze gebruiker (rij uit Gebruikers) in deze school inloggen?"""
|
|
if not gebruiker or not school:
|
|
return False
|
|
if int(gebruiker['Veiligheidsgroep']) == ADMIN:
|
|
return True
|
|
return school.id in scholen_met_rechten(gebruiker['Gebruiker_ID'])
|
|
|
|
|
|
def zet_rechten(gid, school_id, codes, door=None):
|
|
"""Vervangt alle rechten van gebruiker `gid` in school `school_id` door `codes`."""
|
|
from database import get_central_connection
|
|
codes = [c for c in codes if c in CODES]
|
|
conn = get_central_connection()
|
|
cur = conn.cursor()
|
|
try:
|
|
conn.start_transaction()
|
|
cur.execute("DELETE FROM Gebruiker_rechten WHERE Gebruiker_ID = %s AND School_ID = %s", (gid, school_id))
|
|
if codes:
|
|
plek = ','.join(['%s'] * len(codes))
|
|
cur.execute(
|
|
f"INSERT INTO Gebruiker_rechten (Gebruiker_ID, School_ID, Onderdeel_ID, Toegekend_door) "
|
|
f"SELECT %s, %s, Onderdeel_ID, %s FROM Onderdelen WHERE Code IN ({plek})",
|
|
(gid, school_id, door, *codes))
|
|
conn.commit()
|
|
except Exception:
|
|
conn.rollback()
|
|
raise
|
|
finally:
|
|
cur.close()
|
|
conn.close()
|
|
|
|
|
|
# =====================================================================
|
|
# Huidige gebruiker
|
|
# =====================================================================
|
|
def is_admin():
|
|
return bool(session.get('admin')) or bool(session.get('superbeheerder'))
|
|
|
|
|
|
def huidige_rechten():
|
|
"""Rechten van de ingelogde gebruiker in de huidige school (één keer per request opgehaald)."""
|
|
if not has_request_context() or not session.get('auth'):
|
|
return set()
|
|
if is_admin():
|
|
return set(CODES)
|
|
if '_rechten' in g:
|
|
return g._rechten
|
|
gid = session.get('gebruiker_id')
|
|
school = g.get('school')
|
|
rechten = set()
|
|
if gid and school:
|
|
try:
|
|
rechten = rechten_van(gid, school.id)
|
|
except Exception as e:
|
|
sys.stderr.write(f"PIVOT: rechten ophalen mislukt: {e}\n")
|
|
g._rechten = rechten
|
|
return rechten
|
|
|
|
|
|
def heeft_recht(*codes):
|
|
"""True als de ingelogde gebruiker in de huidige school minstens één van deze rechten heeft."""
|
|
rechten = huidige_rechten()
|
|
return any(c in rechten for c in codes)
|
|
|
|
|
|
def recht_vereist(*codes):
|
|
"""Decorator voor routes: de gebruiker moet in de huidige school één van deze rechten hebben."""
|
|
for c in codes:
|
|
if c not in CODES:
|
|
raise ValueError(f"Onbekend onderdeel '{c}'")
|
|
|
|
def decorator(view):
|
|
@functools.wraps(view)
|
|
def wrapped(*a, **kw):
|
|
if not session.get('auth') or not session.get('school'):
|
|
return redirect(url_for('auth.login'))
|
|
if not heeft_recht(*codes):
|
|
flash("Je hebt geen rechten voor dit onderdeel in deze school.", "error")
|
|
return redirect(url_for('main.dashboard'))
|
|
return view(*a, **kw)
|
|
return wrapped
|
|
return decorator
|
|
|
|
|
|
def admin_vereist(view):
|
|
@functools.wraps(view)
|
|
def wrapped(*a, **kw):
|
|
if not session.get('auth'):
|
|
return redirect(url_for('auth.login'))
|
|
if not is_admin():
|
|
flash("Enkel voor administrators.", "error")
|
|
return redirect(url_for('main.dashboard'))
|
|
return view(*a, **kw)
|
|
return wrapped
|
|
|
|
|
|
# =====================================================================
|
|
# Logboek (tabel Logs)
|
|
# =====================================================================
|
|
def log(actie=None, url=None):
|
|
"""Schrijft een regel in Logs. Fouten worden genegeerd (loggen mag PIVOT nooit blokkeren)."""
|
|
from database import central_query
|
|
try:
|
|
school = g.get('school') if has_request_context() else None
|
|
central_query(
|
|
"INSERT INTO Logs (Bezochte_url, Gebruiker_ID, School_ID, Actie, IP) VALUES (%s, %s, %s, %s, %s)",
|
|
((url or (request.full_path.rstrip('?') if has_request_context() else ''))[:2048],
|
|
session.get('gebruiker_id') if has_request_context() else None,
|
|
school.id if school else None,
|
|
(actie or '')[:255] or None,
|
|
request.remote_addr if has_request_context() else None),
|
|
commit=True)
|
|
except Exception as e:
|
|
sys.stderr.write(f"PIVOT: loggen mislukt: {e}\n")
|