Neue Rolle ist_organisationsadmin (auth.py), bewusst getrennt von is_superuser: is_superuser bleibt exklusiv internes, organisationsuebergreifendes Tuxflotte-Personal mit Zugriff auf /admin/* - ist_organisationsadmin ist auf die eigene Organisation beschränkt und kann nie über Self-Service gesetzt werden (organization_id/is_superuser kommen bei POST /organisation/konten nie aus dem Formular, sondern immer aus der Session). /organisation zeigt jetzt Kontakt-/Stammdaten (bearbeitbar) und die Kolleg:innen-Liste der eigenen Organisation (für alle sichtbar, Verwaltung nur für Org-Admins/internes Personal via require_org_admin). Selbstlöschung des eigenen Kontos wird verweigert. fetch_benutzer_fuer_organisation() von admin_kunden.py nach auth.py verschoben (jetzt von beiden Routern importiert, kein Duplikat) - liefert zusätzlich id + ist_organisationsadmin. admin_kunden.py bekommt eine neue Organisationsdaten-Sektion (nutzt denselben PATCH-Endpoint) und die neue Checkbox beim Konto-Anlegen, um den ersten Org-Admin einer neuen Organisation zu bootstrappen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
186 lines
7.1 KiB
HTML
186 lines
7.1 KiB
HTML
{% extends "admin/layout.html" %}
|
|
{% block title %}{{ organization.name if organization else organization_id }} — Tuxflotte Admin{% endblock %}
|
|
{% block admin_content %}
|
|
<p><a href="/admin/kunden">← Kunden</a></p>
|
|
<h2>{{ organization.name if organization else organization_id }}</h2>
|
|
|
|
<section>
|
|
<h3>Organisationsdaten</h3>
|
|
<form method="post" action="/admin/kunden/{{ organization_id }}/organisationsdaten">
|
|
<label for="org_name">Name</label>
|
|
<input type="text" id="org_name" name="name" value="{{ organization.name if organization else '' }}" required>
|
|
|
|
<label for="org_kontakt_name">Ansprechpartner:in</label>
|
|
<input type="text" id="org_kontakt_name" name="kontakt_name" value="{{ organization.kontakt_name or '' if organization else '' }}">
|
|
|
|
<label for="org_adresse">Adresse</label>
|
|
<input type="text" id="org_adresse" name="adresse" value="{{ organization.adresse or '' if organization else '' }}">
|
|
|
|
<label for="org_telefon">Telefon</label>
|
|
<input type="text" id="org_telefon" name="telefon" value="{{ organization.telefon or '' if organization else '' }}">
|
|
|
|
<label for="org_kontakt_email">Kontakt-E-Mail</label>
|
|
<input type="email" id="org_kontakt_email" name="kontakt_email" value="{{ organization.kontakt_email or '' if organization else '' }}">
|
|
|
|
<label for="org_notizen">Notizen</label>
|
|
<textarea id="org_notizen" name="notizen" rows="3">{{ organization.notizen or '' if organization else '' }}</textarea>
|
|
|
|
<button type="submit">Speichern</button>
|
|
</form>
|
|
</section>
|
|
|
|
<section>
|
|
<h3>Aktivierungscodes</h3>
|
|
<figure>
|
|
<table>
|
|
<thead><tr><th>Code</th><th>Status</th><th>Angelegt</th></tr></thead>
|
|
<tbody>
|
|
{% for ac in activation_codes %}
|
|
<tr>
|
|
<td><code>{{ ac.code }}</code></td>
|
|
<td>{{ "aktiv" if ac.active else "inaktiv" }}</td>
|
|
<td>{{ ac.created_at }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</figure>
|
|
<form method="post" action="/admin/kunden/{{ organization_id }}/activation-codes">
|
|
<button type="submit">Neuen Aktivierungscode erzeugen</button>
|
|
</form>
|
|
</section>
|
|
|
|
<section>
|
|
<h3>Bereitstellungsvorlagen</h3>
|
|
<figure>
|
|
<table>
|
|
<thead><tr><th>Bezeichnung</th><th>Workspace</th><th>Backend</th><th>Standard</th></tr></thead>
|
|
<tbody>
|
|
{% for v in bereitstellungsvorlagen %}
|
|
<tr>
|
|
<td>{{ v.label }}</td>
|
|
<td>{{ v.workspace.name }}</td>
|
|
<td>{{ v.backend.name }} {{ v.backend.version }}</td>
|
|
<td>{{ "ja" if v.is_default else "nein" }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</figure>
|
|
<details>
|
|
<summary role="button" class="secondary outline">Neue Bereitstellungsvorlage anlegen</summary>
|
|
<form method="post" action="/admin/kunden/{{ organization_id }}/bereitstellungsvorlagen">
|
|
<label for="label">Bezeichnung</label>
|
|
<input type="text" id="label" name="label" required>
|
|
|
|
<label for="workspace_id">Workspace</label>
|
|
<select id="workspace_id" name="workspace_id" required>
|
|
{% for w in workspaces %}
|
|
<option value="{{ w.id }}">{{ w.name }}{{ " (" + w.organization_name + ")" if w.organization_name else " (global)" }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<label for="backend_id">Backend</label>
|
|
<select id="backend_id" name="backend_id" required>
|
|
{% for b in backends %}
|
|
<option value="{{ b.id }}">{{ b.name }} {{ b.version }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<label for="root_filesystem">Root-Dateisystem</label>
|
|
<select id="root_filesystem" name="root_filesystem">
|
|
<option value="ext4">ext4</option>
|
|
<option value="btrfs">btrfs</option>
|
|
</select>
|
|
|
|
<label>
|
|
<input type="checkbox" name="is_default">
|
|
Als Standard für diese Organisation festlegen
|
|
</label>
|
|
<label>
|
|
<input type="checkbox" name="disk_encryption">
|
|
Festplattenverschlüsselung
|
|
</label>
|
|
<label>
|
|
<input type="checkbox" name="secure_boot_required">
|
|
Secure Boot erforderlich
|
|
</label>
|
|
|
|
<button type="submit">Anlegen</button>
|
|
</form>
|
|
</details>
|
|
</section>
|
|
|
|
<section>
|
|
<h3>Kundenkonten</h3>
|
|
<figure>
|
|
<table>
|
|
<thead><tr><th>E-Mail</th><th>Tuxflotte-Admin</th><th>Org-Admin</th><th>Angelegt</th></tr></thead>
|
|
<tbody>
|
|
{% for b in benutzer %}
|
|
<tr>
|
|
<td>{{ b.email }}</td>
|
|
<td>{{ "ja" if b.is_superuser else "nein" }}</td>
|
|
<td>{{ "ja" if b.ist_organisationsadmin else "nein" }}</td>
|
|
<td>{{ b.created_at }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</figure>
|
|
<details>
|
|
<summary role="button" class="secondary outline">Neues Konto anlegen</summary>
|
|
<form method="post" action="/admin/kunden/{{ organization_id }}/konten">
|
|
<label for="email">E-Mail</label>
|
|
<input type="email" id="email" name="email" required>
|
|
<label for="password">Passwort</label>
|
|
<input type="password" id="password" name="password" required>
|
|
<label>
|
|
<input type="checkbox" name="is_superuser">
|
|
Admin-Zugriff (organisationsübergreifend, internes Tuxflotte-Personal)
|
|
</label>
|
|
<label>
|
|
<input type="checkbox" name="ist_organisationsadmin">
|
|
Admin dieser Organisation (darf eigene Kolleg:innen-Konten verwalten)
|
|
</label>
|
|
<button type="submit">Anlegen</button>
|
|
</form>
|
|
</details>
|
|
</section>
|
|
|
|
<section>
|
|
<h3>Gruppen</h3>
|
|
<p><small>Querschnitts-Gruppen (z.B. Lehrer/Schulleitung) — ein Gerät kann in mehreren gleichzeitig sein. Anders als OEs vorerst nur hier intern verwaltbar, siehe Konfigurationsgruppen-ADR.</small></p>
|
|
{% if not gruppen %}
|
|
<p>Keine Gruppen angelegt.</p>
|
|
{% else %}
|
|
<figure>
|
|
<table>
|
|
<thead><tr><th>Name</th><th></th></tr></thead>
|
|
<tbody>
|
|
{% for gruppe in gruppen %}
|
|
<tr>
|
|
<td>{{ gruppe.name }}</td>
|
|
<td>
|
|
<a href="/admin/kunden/{{ organization_id }}/gruppen/{{ gruppe.id }}" role="button" class="outline">Verwalten</a>
|
|
<form method="post" action="/admin/kunden/{{ organization_id }}/gruppen/{{ gruppe.id }}/loeschen" style="display:inline">
|
|
<button type="submit" class="outline secondary">Löschen</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</figure>
|
|
{% endif %}
|
|
<details>
|
|
<summary role="button" class="secondary outline">Neue Gruppe anlegen</summary>
|
|
<form method="post" action="/admin/kunden/{{ organization_id }}/gruppen">
|
|
<label for="gruppe_name">Name</label>
|
|
<input type="text" id="gruppe_name" name="name" required>
|
|
<button type="submit">Anlegen</button>
|
|
</form>
|
|
</details>
|
|
</section>
|
|
{% endblock %}
|