From 6c0a8ba04974f21d8be9d431dc2a5f59cd0e21c1 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 15 Sep 2024 23:48:52 +0200 Subject: [PATCH] changed name to mspk client module changed the name Signed-off-by: Alex #744 --- web/app.py | 12 +- web/blueprints/mpskclient/__init__.py | 158 ++++++++++++++++++ .../{wlanhost => mpskclient}/forms.py | 12 +- .../{wlanhost => mpskclient}/tables.py | 33 ++-- web/blueprints/user/__init__.py | 17 +- web/templates/user/user_show.html | 4 +- 6 files changed, 198 insertions(+), 38 deletions(-) create mode 100644 web/blueprints/mpskclient/__init__.py rename web/blueprints/{wlanhost => mpskclient}/forms.py (64%) rename web/blueprints/{wlanhost => mpskclient}/tables.py (60%) diff --git a/web/app.py b/web/app.py index 6e10f46d5..40c5fec98 100644 --- a/web/app.py +++ b/web/app.py @@ -31,7 +31,15 @@ from web.blueprints import task from . import template_filters, template_tests from .blueprints import ( - facilities, finance, infrastructure, login, properties, user, host, health, wlanhost + facilities, + finance, + infrastructure, + login, + properties, + user, + host, + health, + mpskclient, ) from .blueprints.login import login_manager @@ -99,7 +107,7 @@ def make_app(hades_logs: bool = True) -> PycroftFlask: app.register_blueprint(login.bp) app.register_blueprint(api.bp, url_prefix="/api/v0") app.register_blueprint(health.bp, url_prefix="/health") - app.register_blueprint(wlanhost.bp, url_prefix="/wlan-host") + app.register_blueprint(mpskclient.bp, url_prefix="/wlan-host") template_filters.register_filters(app) template_tests.register_checks(app) diff --git a/web/blueprints/mpskclient/__init__.py b/web/blueprints/mpskclient/__init__.py new file mode 100644 index 000000000..573d4a7e6 --- /dev/null +++ b/web/blueprints/mpskclient/__init__.py @@ -0,0 +1,158 @@ +import re + +from flask import Blueprint, flash, abort, redirect, url_for, render_template, request +from flask.typing import ResponseReturnValue +from flask_login import current_user +from flask_wtf import FlaskForm +from netaddr import IPAddress + +from pycroft.exc import PycroftException +from pycroft.helpers.net import mac_regex, get_interface_manufacturer +from pycroft.lib import mpsk_client as lib_mspk +from pycroft.lib.net import get_subnets_for_room +from pycroft.lib.facilities import get_room +from pycroft.model import session +from pycroft.model.host import Host, Interface +from pycroft.model.mspk_client import MSPKClient +from pycroft.model.user import User +from web.blueprints.access import BlueprintAccess +from web.blueprints.helpers.exception import abort_on_error +from web.blueprints.helpers.form import refill_room_data +from web.blueprints.helpers.user import get_user_or_404 +from web.blueprints.host.forms import InterfaceForm, HostForm +from web.blueprints.host.tables import InterfaceTable, HostTable, HostRow, InterfaceRow +from web.blueprints.mpskclient.forms import WLANInterfaceForm +from web.blueprints.mpskclient.tables import MSPKRow +from web.table.table import TableResponse, BtnColResponse, LinkColResponse + +bp = Blueprint("wlan-host", __name__) +access = BlueprintAccess(bp, required_properties=["user_show"]) + + +def get_mpsk_client_or_404(mspk_id: int) -> MSPKClient: + if (mspk := session.session.get(MSPKClient, mspk_id)) is None: + flash("Host existiert nicht.", "error") + abort(404) + return mspk + + +@bp.route("/create", methods=["GET", "POST"]) +@access.require("hosts_change") +def host_create() -> ResponseReturnValue: + user = get_user_or_404(request.args.get("user_id", default=None, type=int)) + form = WLANInterfaceForm(owner_id=user.id) + + def default_response() -> ResponseReturnValue: + form_args = {"form": form, "cancel_to": url_for("user.user_show", user_id=user.id)} + + return render_template( + "generic_form.html", page_title="MSPK Client erstellen", form_args=form_args, form=form + ) + + if not form.is_submitted(): + return default_response() + refill_room_data(form, user.room) + + if not form.validate_on_submit(): + return default_response() + + # existence verified by validator + # TODO can't we provide an attribute for that on the form? + owner = session.session.get(User, form.owner_id.data) + with abort_on_error(default_response), session.session.begin_nested(): + + host = lib_mspk.mpsk_client_create( + owner, form.name.data, form.mac.data, processor=current_user + ) + session.session.commit() + + flash("MSPK Client erfolgreich erstellt.", "success") + return redirect(url_for("user.user_show", user_id=host.owner.id, _anchor="mspks")) + + +@bp.route("//delete", methods=["GET", "POST"]) +@access.require("hosts_change") +def mpsk_delete(mpsk_id: int) -> ResponseReturnValue: + mpsk = get_mpsk_client_or_404(mpsk_id) + owner = mpsk.owner + form = FlaskForm() + + def default_response() -> ResponseReturnValue: + form_args = { + "form": form, + "cancel_to": url_for("user.user_show", user_id=owner.id), + "submit_text": "Löschen", + "actions_offset": 0, + } + + return render_template( + "generic_form.html", page_title="MPSK Client löschen", form_args=form_args, form=form + ) + + if not form.is_submitted(): + return default_response() + + with abort_on_error(default_response), session.session.begin_nested(): + lib_mspk.mpsk_delete(mpsk, current_user) + session.session.commit() + + flash("MPSK Client erfolgreich gelöscht.", "success") + return redirect(url_for("user.user_show", user_id=owner.id, _anchor="mspks")) + + +@bp.route("//edit", methods=["GET", "POST"]) +@access.require("hosts_change") +def mpsk_edit(mpsk_id: int) -> ResponseReturnValue: + mpsk = get_mpsk_client_or_404(mpsk_id) + + form = WLANInterfaceForm(obj=mpsk) + + def default_response() -> ResponseReturnValue: + form_args = {"form": form, "cancel_to": url_for("user.user_show", user_id=mpsk.owner_id)} + + return render_template( + "generic_form.html", page_title="MPSK Client editieren", form_args=form_args + ) + + if not form.is_submitted() or not form.validate(): + return default_response() + + with abort_on_error(default_response), session.session.begin_nested(): + lib_mspk.mpsk_edit(mpsk, mpsk.owner, form.name.data, form.mac.data, current_user) + session.session.commit() + flash("MPSK Client erfolgreich bearbeitet.", "success") + return redirect(url_for("user.user_show", user_id=mpsk.owner_id, _anchor="mspks")) + + +@bp.route("/") +def user_hosts_json(user_id: int) -> ResponseReturnValue: + user = get_user_or_404(user_id) + # TODO: Importend when the for the returning of actual mpsk mac addresses for MPSK devices + # return "" + return TableResponse[MSPKRow]( + items=[_mspk_row(mpsk, user_id) for mpsk in user.mspks] + ).model_dump() + + +def _mspk_row(client, user_id: int) -> MSPKRow: + # println(f"{client}") + # client = get_wlan_host_or_404(client) + return MSPKRow( + id=client.id, + name=client.name, + mac=client.mac, + actions=[ + BtnColResponse( + href=url_for(".mpsk_edit", mpsk_id=client.id), + title="Bearbeiten", + icon="fa-edit", + btn_class="btn-link", + ), + BtnColResponse( + href=url_for(".mpsk_delete", mpsk_id=client.id), + title="Löschen", + icon="fa-trash", + btn_class="btn-link", + ), + ], + ) diff --git a/web/blueprints/wlanhost/forms.py b/web/blueprints/mpskclient/forms.py similarity index 64% rename from web/blueprints/wlanhost/forms.py rename to web/blueprints/mpskclient/forms.py index 705bf5c31..5471fd660 100644 --- a/web/blueprints/wlanhost/forms.py +++ b/web/blueprints/mpskclient/forms.py @@ -3,18 +3,18 @@ # the Apache License, Version 2.0. See the LICENSE file for details from flask_wtf import FlaskForm as Form -from wtforms.validators import Optional -from wtforms_widgets.fields.core import TextField, SelectMultipleField +from wtforms.validators import DataRequired +from wtforms_widgets.fields.core import TextField from wtforms_widgets.fields.custom import MacField from wtforms_widgets.fields.validators import MacAddress -from web.blueprints.facilities.forms import SelectRoomForm from web.blueprints.helpers.host import UniqueMac from web.form.widgets import UserIDField - class WLANInterfaceForm(Form): - name = TextField("Name", validators=[Optional()]) + name = TextField("Name", validators=[DataRequired(message="Es muss ein Name gesetzt werden!")]) + owner_id = UserIDField("Besitzer-ID") - mac = MacField("MAC", [MacAddress(message="MAC ist ungültig!")], UniqueMac(annex_field=None)) + mac = MacField("MAC", [MacAddress(message="MAC ist ungültig!"), UniqueMac(annex_field=None)]) + _order = ("name", "owner_id") diff --git a/web/blueprints/wlanhost/tables.py b/web/blueprints/mpskclient/tables.py similarity index 60% rename from web/blueprints/wlanhost/tables.py rename to web/blueprints/mpskclient/tables.py index 3b0be738d..c1f63698a 100644 --- a/web/blueprints/wlanhost/tables.py +++ b/web/blueprints/mpskclient/tables.py @@ -14,36 +14,20 @@ button_toolbar, MultiBtnColumn, BtnColResponse, - MultiLinkColumn, - LinkColResponse, ) -class HostRow(BaseModel): - name: str | None = None - actions: list[BtnColResponse] - interfaces_table_link: str - interface_create_link: str - id: int +class MPSKTable(BootstrapTable): + """A table for displaying hosts""" -class WlanHostTable(BootstrapTable): - """A table for displaying hosts - """ name = Column("Name") - interface_create_link = Column("Mac") + mac = Column("Mac") actions = MultiBtnColumn("Aktionen", hide_if=no_hosts_change, width=3) - #interfaces_table_link = Column("", hide_if=lambda: True) + # interfaces_table_link = Column("", hide_if=lambda: True) id = Column("", hide_if=lambda: True) def __init__(self, *, user_id: int | None = None, **kw: t.Any) -> None: - table_args = kw.pop('table_args', {}) - table_args.setdefault('data-load-subtables', "true") - table_args.setdefault('data-detail-view', "true") - table_args.setdefault('data-detail-formatter', "table.hostDetailFormatter") - table_args.setdefault('data-response-handler', "table.userHostResponseHandler") - kw['table_args'] = table_args - super().__init__(**kw) self.user_id = user_id @@ -55,4 +39,11 @@ def toolbar(self) -> HasDunderStr | None: return None href = url_for("wlan-host.host_create", user_id=self.user_id) - return button_toolbar("Host", href) + return button_toolbar("Client", href) + + +class MSPKRow(BaseModel): + name: str | None = None + mac: str + actions: list[BtnColResponse] + id: int diff --git a/web/blueprints/user/__init__.py b/web/blueprints/user/__init__.py index 1bd117dd3..368b9e48f 100644 --- a/web/blueprints/user/__init__.py +++ b/web/blueprints/user/__init__.py @@ -68,7 +68,7 @@ from web.blueprints.helpers.form import refill_room_data from web.blueprints.helpers.user import get_user_or_404, get_pre_member_or_404 from web.blueprints.host.tables import HostTable -from web.blueprints.wlanhost.tables import WlanHostTable +from web.blueprints.mpskclient.tables import MPSKTable from web.blueprints.navigation import BlueprintNavigation from web.blueprints.task.tables import TaskTable from web.blueprints.user.forms import ( @@ -348,12 +348,15 @@ def user_show(user_id: int) -> ResponseReturnValue: user_id=user.id, data_url=_membership_endpoint(group_filter="active"), ), - host_table=HostTable(data_url=url_for("host.user_hosts_json", user_id=user.id), - user_id=user.id), - wlan_host_table=WlanHostTable(data_url=url_for("wlan-host.user_hosts_json", user_id=user.id), - user_id=user.id), - task_table=TaskTable(data_url=url_for("task.json_tasks_for_user", user_id=user.id), - hidden_columns=['user']), + host_table=HostTable( + data_url=url_for("host.user_hosts_json", user_id=user.id), user_id=user.id + ), + wlan_host_table=MPSKTable( + data_url=url_for("wlan-host.user_hosts_json", user_id=user.id), user_id=user.id + ), + task_table=TaskTable( + data_url=url_for("task.json_tasks_for_user", user_id=user.id), hidden_columns=["user"] + ), finance_table_regular=FinanceTable( data_url=tbl_data_url, user_id=user.id, diff --git a/web/templates/user/user_show.html b/web/templates/user/user_show.html index 19ae23448..b248280cb 100644 --- a/web/templates/user/user_show.html +++ b/web/templates/user/user_show.html @@ -18,8 +18,8 @@ }, { 'id': 'wlans', - 'icon': 'fa-clipboard-check', - 'name': 'MPSK Clients', + 'icon': 'fa-tablet', + 'name': 'MSPK Clients', 'badge': user.tasks | length, }, {