PIVOT 1.1.0
This commit is contained in:
commit
8a53234eb6
161 files changed
+31109
No files matched your search
@@ -0,0 +1,285 @@
|
||||
"""
|
||||
Beheer › IP-blokkades. Geldt voor alle scholen.
|
||||
|
||||
- Administrators zien en beheren alles (blokkeren, altijd toestaan, schakelaar, testen).
|
||||
- Een gewone gebruiker met het recht 'ip_whitelist' ziet enkel de toegestane adressen van de actieve school,
|
||||
kan een adres toevoegen (school = actieve school, opmerking = zijn naam) en enkel zijn eigen regels verwijderen.
|
||||
"""
|
||||
import functools
|
||||
|
||||
from flask import Blueprint, render_template, request, flash, redirect, url_for, session
|
||||
|
||||
from blueprints.auth import login_required
|
||||
from database import central_query
|
||||
from rechten import admin_vereist, recht_vereist, is_admin, log
|
||||
from services import ipblokkade as ipb
|
||||
from tenancy import alle_scholen, school_op_code, huidige_school
|
||||
|
||||
ipblokkade_bp = Blueprint('ipblokkade', __name__)
|
||||
|
||||
|
||||
def _schoolnaam(code):
|
||||
s = school_op_code(code, ook_inactief=True)
|
||||
return s.naam if s else code
|
||||
|
||||
|
||||
def admin_of_whitelist(view):
|
||||
"""Administrators altijd (ook zonder gekozen school); anderen met het recht 'ip_whitelist' in de actieve school."""
|
||||
beschermd = recht_vereist('ip_whitelist')(view)
|
||||
|
||||
@functools.wraps(view)
|
||||
def wrapped(*a, **kw):
|
||||
return view(*a, **kw) if session.get('auth') and is_admin() else beschermd(*a, **kw)
|
||||
return wrapped
|
||||
|
||||
|
||||
# Gewone gebruikers mogen hoogstens zo'n reeks toestaan (een ruimere reeks laat te veel adressen altijd binnen)
|
||||
MAX_PREFIX_GEBRUIKER = {4: 24, 6: 64}
|
||||
|
||||
|
||||
def _eigen(r):
|
||||
"""Mag de ingelogde (gewone) gebruiker deze regel verwijderen? Enkel als hij hem zelf toevoegde."""
|
||||
gid = session.get('gebruiker_id')
|
||||
return bool(gid) and r.get('Gebruiker_ID') == gid and r.get('Soort') == 'toestaan'
|
||||
|
||||
|
||||
def _gekozen_scholen():
|
||||
"""De aangevinkte scholen uit het formulier (enkel bestaande schoolcodes)."""
|
||||
return ipb.scholen_waarde(c for c in request.form.getlist('scholen') if school_op_code(c, ook_inactief=True))
|
||||
|
||||
|
||||
@ipblokkade_bp.route("/", methods=["GET"])
|
||||
@login_required
|
||||
@admin_of_whitelist
|
||||
def overzicht():
|
||||
ipb.ensure_table()
|
||||
rijen = central_query("SELECT * FROM IP_blokkades ORDER BY Soort, Reeks") or []
|
||||
if not is_admin():
|
||||
return _overzicht_gebruiker(rijen)
|
||||
import ipaddress
|
||||
for r in rijen: # sorteersleutel: numeriek op adres (zodat 9.x vóór 85.x komt), dan op grootte van de reeks
|
||||
try:
|
||||
net = ipaddress.ip_network(r['Reeks'], strict=False)
|
||||
r['Sorteer'] = f"{net.version}-{int(net.network_address):040d}-{net.prefixlen:03d}"
|
||||
except ValueError:
|
||||
r['Sorteer'] = r['Reeks']
|
||||
r['Scholen_codes'] = ipb.scholen_codes(r.get('Scholen'))
|
||||
r['Scholen_namen'] = sorted((_schoolnaam(c) for c in r['Scholen_codes']), key=str.lower)
|
||||
test_ip = (request.args.get('test') or '').strip()
|
||||
test = None
|
||||
if test_ip:
|
||||
try:
|
||||
lijst = ipb.regels(vers=True)
|
||||
geweigerd, rid = ipb.beoordeel(test_ip, lijst)
|
||||
regel = next((r for r in rijen if r['ID'] == rid), None)
|
||||
test = {'ip': test_ip, 'geweigerd': geweigerd, 'regel': regel}
|
||||
except Exception as e:
|
||||
flash(f"Test mislukt: {e}", "error")
|
||||
modus = ipb.instelling(ipb.MODUS_SLEUTEL)
|
||||
teller = ipb.instelling(ipb.TELLER_SLEUTEL)
|
||||
scholen = sorted(alle_scholen(ook_inactief=True), key=lambda s: s.naam.lower())
|
||||
return render_template("ipblokkade.html", rijen=rijen, mijn_ip=request.remote_addr, test=test, scholen=scholen,
|
||||
blokkeren=[r for r in rijen if r['Soort'] == 'blokkeren'],
|
||||
toestaan=[r for r in rijen if r['Soort'] == 'toestaan'],
|
||||
enkel_toegestaan=bool(modus and modus['Waarde'] == '1'), modus=modus,
|
||||
lokaal=ipb.is_lokaal(request.remote_addr),
|
||||
modus_geweigerd=int(teller['Waarde']) if teller and str(teller['Waarde']).isdigit() else 0)
|
||||
|
||||
|
||||
def _overzicht_gebruiker(rijen):
|
||||
"""Gewone gebruiker: enkel de toegestane adressen van de actieve school."""
|
||||
school = huidige_school()
|
||||
eigen_school = [r for r in rijen if r['Soort'] == 'toestaan' and school.code in ipb.scholen_codes(r.get('Scholen'))]
|
||||
import ipaddress
|
||||
for r in eigen_school:
|
||||
try:
|
||||
net = ipaddress.ip_network(r['Reeks'], strict=False)
|
||||
r['Sorteer'] = f"{net.version}-{int(net.network_address):040d}-{net.prefixlen:03d}"
|
||||
except ValueError:
|
||||
r['Sorteer'] = r['Reeks']
|
||||
r['Eigen'] = _eigen(r)
|
||||
return render_template("ipblokkade_school.html", rijen=eigen_school, school_naam=school.naam,
|
||||
mijn_ip=request.remote_addr, lokaal=ipb.is_lokaal(request.remote_addr),
|
||||
naam=session.get('naam') or session.get('user'))
|
||||
|
||||
|
||||
@ipblokkade_bp.route("/modus", methods=["POST"])
|
||||
@login_required
|
||||
@admin_vereist
|
||||
def modus():
|
||||
"""Schakelaar: enkel nog adressen uit "Altijd toestaan" binnenlaten."""
|
||||
ipb.ensure_table()
|
||||
aan = request.form.get('aan') == '1'
|
||||
if aan:
|
||||
mijn = request.remote_addr
|
||||
if ipb.is_lokaal(mijn):
|
||||
flash(f"PIVOT ziet jouw adres als {mijn}. Dan draait PIVOT achter een reverse proxy (of test je lokaal) en "
|
||||
"lijkt elke bezoeker van dat adres te komen: de witte lijst zou dan niets tegenhouden of iedereen "
|
||||
"buitensluiten. Zet eerst in config.ini › [algemeen] proxy_aantal = 1 en herstart PIVOT; bij "
|
||||
"'Jouw IP-adres' moet je echte adres verschijnen.", "error")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
lijst = ipb.regels(vers=True)
|
||||
if not any(sz == 'toestaan' for _, sz, _ in lijst):
|
||||
flash("Voeg eerst minstens één adres of reeks toe bij 'Altijd toestaan'.", "error")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
if ipb.beoordeel(mijn, lijst, enkel_toe=True)[0]:
|
||||
flash(f"Jouw eigen adres ({mijn}) staat niet bij 'Altijd toestaan': dan sluit je jezelf buiten. "
|
||||
"Voeg het eerst toe.", "error")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
ipb.zet_enkel_toegestaan(aan, session.get('user'))
|
||||
flash("Enkel nog adressen uit 'Altijd toestaan' worden binnengelaten." if aan
|
||||
else "Iedereen wordt weer binnengelaten, behalve de geblokkeerde adressen.", "success" if not aan else "warning")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
|
||||
|
||||
@ipblokkade_bp.route("/toevoegen", methods=["POST"])
|
||||
@login_required
|
||||
@admin_of_whitelist
|
||||
def toevoegen():
|
||||
if not is_admin():
|
||||
return _toevoegen_gebruiker()
|
||||
soort = 'toestaan' if request.form.get('soort') == 'toestaan' else 'blokkeren'
|
||||
try:
|
||||
net = ipb.normaliseer(request.form.get('reeks'))
|
||||
except ipb.OngeldigeReeks as e:
|
||||
flash(str(e), "error")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
ipb.ensure_table()
|
||||
if soort == 'blokkeren':
|
||||
# Niet jezelf buitensluiten: weigeren als je eigen adres erin valt (tenzij het uitdrukkelijk toegestaan is)
|
||||
lijst = ipb.regels(vers=True) + [(0, 'blokkeren', net)]
|
||||
if ipb.beoordeel(request.remote_addr, lijst)[0]:
|
||||
flash(f"Deze reeks bevat je eigen IP-adres ({request.remote_addr}): dan sluit je jezelf buiten. "
|
||||
"Voeg eerst je eigen adres toe als uitzondering (toestaan), of kies een kleinere reeks.", "error")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
bestaat = central_query("SELECT ID FROM IP_blokkades WHERE Reeks = %s AND Soort = %s", (str(net), soort))
|
||||
if bestaat:
|
||||
flash(f"{net} staat al in de lijst.", "warning")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
# Bij "Altijd toestaan" kan je aanduiden bij welke school/scholen het adres hoort (enkel ter info in de tabel)
|
||||
scholen = _gekozen_scholen() if soort == 'toestaan' else None
|
||||
central_query("INSERT INTO IP_blokkades (Reeks, Soort, Opmerking, Scholen, Aangemaakt, Door, Gebruiker_ID) "
|
||||
"VALUES (%s, %s, %s, %s, NOW(), %s, %s)",
|
||||
(str(net), soort, (request.form.get('opmerking') or '').strip()[:255] or None,
|
||||
scholen, session.get('user'), session.get('gebruiker_id')),
|
||||
commit=True)
|
||||
ipb.vergeet_cache()
|
||||
aantal = net.num_addresses
|
||||
getal = f"{aantal:,}".replace(',', '.')
|
||||
flash(f"{net} toegevoegd ({'geblokkeerd' if soort == 'blokkeren' else 'altijd toegestaan'}"
|
||||
f"{', ' + ', '.join(_schoolnaam(c) for c in ipb.scholen_codes(scholen)) if scholen else ''}, {getal} adres{'sen' if aantal != 1 else ''}).", "success")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
|
||||
|
||||
def _toevoegen_gebruiker():
|
||||
"""Gewone gebruiker: enkel 'Altijd toestaan', school = actieve school, opmerking = zijn naam."""
|
||||
terug = redirect(url_for('ipblokkade.overzicht'))
|
||||
try:
|
||||
net = ipb.normaliseer(request.form.get('reeks'))
|
||||
except ipb.OngeldigeReeks as e:
|
||||
flash(str(e), "error")
|
||||
return terug
|
||||
grens = MAX_PREFIX_GEBRUIKER[net.version]
|
||||
if net.prefixlen < grens:
|
||||
flash(f"{net} is een te grote reeks. Je kan één adres toevoegen of hoogstens een /{grens}-reeks "
|
||||
f"(bv. {'85.12.3.' if net.version == 4 else 'een /64'}). Vraag een administrator voor een grotere reeks.", "error")
|
||||
return terug
|
||||
ipb.ensure_table()
|
||||
school = huidige_school()
|
||||
naam = (session.get('naam') or session.get('user') or '').strip()[:255] or None
|
||||
bestaat = central_query("SELECT ID, Scholen FROM IP_blokkades WHERE Reeks = %s AND Soort = 'toestaan'", (str(net),))
|
||||
if bestaat:
|
||||
codes = ipb.scholen_codes(bestaat[0]['Scholen'])
|
||||
if school.code in codes:
|
||||
flash(f"{net} staat al in de lijst van {school.naam}.", "warning")
|
||||
else:
|
||||
# Al toegestaan voor een andere school: deze school erbij zetten (de regel blijft van wie hem maakte)
|
||||
central_query("UPDATE IP_blokkades SET Scholen = %s WHERE ID = %s",
|
||||
(ipb.scholen_waarde(codes + [school.code]), bestaat[0]['ID']), commit=True)
|
||||
flash(f"{net} was al toegestaan voor een andere school en staat nu ook bij {school.naam}.", "success")
|
||||
return terug
|
||||
central_query("INSERT INTO IP_blokkades (Reeks, Soort, Opmerking, Scholen, Aangemaakt, Door, Gebruiker_ID) "
|
||||
"VALUES (%s, 'toestaan', %s, %s, NOW(), %s, %s)",
|
||||
(str(net), naam, school.code, session.get('user'), session.get('gebruiker_id')), commit=True)
|
||||
ipb.vergeet_cache()
|
||||
log(f"IP toegestaan voor {school.code}: {net}")
|
||||
flash(f"{net} toegevoegd aan de toegestane adressen van {school.naam}.", "success")
|
||||
return terug
|
||||
|
||||
|
||||
@ipblokkade_bp.route("/leegmaken", methods=["POST"])
|
||||
@login_required
|
||||
@admin_vereist
|
||||
def leegmaken():
|
||||
"""Verwijdert alle 'blokkeren'-regels. 'Altijd toestaan' en de schakelaar blijven ongemoeid."""
|
||||
ipb.ensure_table()
|
||||
rij = central_query("SELECT COUNT(*) AS n FROM IP_blokkades WHERE Soort = 'blokkeren'")
|
||||
aantal = int(rij[0]['n']) if rij else 0
|
||||
if not aantal:
|
||||
flash("De bloklijst is al leeg.", "warning")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
central_query("DELETE FROM IP_blokkades WHERE Soort = 'blokkeren'", commit=True)
|
||||
ipb.vergeet_cache()
|
||||
try:
|
||||
from rechten import log
|
||||
log(f"Bloklijst IP-blokkades leeggemaakt ({aantal} regels)")
|
||||
except Exception:
|
||||
pass
|
||||
flash(f"Bloklijst leeggemaakt: {aantal} regel{'s' if aantal != 1 else ''} verwijderd.", "success")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
|
||||
|
||||
@ipblokkade_bp.route("/<int:regel_id>/bewerken", methods=["POST"])
|
||||
@login_required
|
||||
@admin_vereist
|
||||
def bewerken(regel_id):
|
||||
"""Reeks, scholen en opmerking van een bestaande regel aanpassen (de soort blijft dezelfde)."""
|
||||
terug = redirect(url_for('ipblokkade.overzicht'))
|
||||
ipb.ensure_table()
|
||||
rij = central_query("SELECT ID, Soort FROM IP_blokkades WHERE ID = %s", (regel_id,))
|
||||
if not rij:
|
||||
flash("Deze regel bestaat niet meer.", "error")
|
||||
return terug
|
||||
soort = rij[0]['Soort']
|
||||
try:
|
||||
net = ipb.normaliseer(request.form.get('reeks'))
|
||||
except ipb.OngeldigeReeks as e:
|
||||
flash(str(e), "error")
|
||||
return terug
|
||||
if central_query("SELECT ID FROM IP_blokkades WHERE Reeks = %s AND Soort = %s AND ID <> %s", (str(net), soort, regel_id)):
|
||||
flash(f"{net} staat al in de lijst.", "warning")
|
||||
return terug
|
||||
# Niet jezelf buitensluiten: met de aangepaste regel moet je eigen adres nog binnen mogen
|
||||
mijn = request.remote_addr
|
||||
nieuw = [r for r in ipb.regels(vers=True) if r[0] != regel_id] + [(regel_id, soort, net)]
|
||||
if ipb.beoordeel(mijn, nieuw, enkel_toe=ipb.enkel_toegestaan())[0] and not ipb.beoordeel(mijn, ipb.regels())[0]:
|
||||
flash(f"Met deze aanpassing zou je eigen IP-adres ({mijn}) geweigerd worden: dan sluit je jezelf buiten.", "error")
|
||||
return terug
|
||||
central_query("UPDATE IP_blokkades SET Reeks = %s, Opmerking = %s, Scholen = %s WHERE ID = %s",
|
||||
(str(net), (request.form.get('opmerking') or '').strip()[:255] or None,
|
||||
_gekozen_scholen() if soort == 'toestaan' else None, regel_id), commit=True)
|
||||
ipb.vergeet_cache()
|
||||
flash(f"{net} bijgewerkt.", "success")
|
||||
return terug
|
||||
|
||||
|
||||
@ipblokkade_bp.route("/<int:regel_id>/verwijderen", methods=["POST"])
|
||||
@login_required
|
||||
@admin_of_whitelist
|
||||
def verwijderen(regel_id):
|
||||
if not is_admin():
|
||||
rij = central_query("SELECT ID, Soort, Gebruiker_ID FROM IP_blokkades WHERE ID = %s", (regel_id,))
|
||||
if not rij or not _eigen(rij[0]):
|
||||
flash("Je kan enkel adressen verwijderen die je zelf toegevoegd hebt.", "error")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
# Staat de witte-lijstmodus aan, dan mag je de regel die jou zelf binnenlaat niet verwijderen
|
||||
if ipb.enkel_toegestaan(vers=True):
|
||||
rest = [r for r in ipb.regels(vers=True) if r[0] != regel_id]
|
||||
if ipb.beoordeel(request.remote_addr, rest, enkel_toe=True)[0]:
|
||||
flash("Deze regel laat jou zelf binnen terwijl enkel 'Altijd toestaan' geldt: verwijderen zou je "
|
||||
"buitensluiten. Zet eerst de schakelaar uit of voeg je adres apart toe.", "error")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
central_query("DELETE FROM IP_blokkades WHERE ID = %s", (regel_id,), commit=True)
|
||||
ipb.vergeet_cache()
|
||||
flash("Regel verwijderd.", "success")
|
||||
return redirect(url_for('ipblokkade.overzicht'))
|
||||
Reference in new issue
Block a user