Fileshare: Hostname-Aufloesung fuer UNC-Pfade + Zuordnungen bearbeitbar

- Freigabe-Hostnamen (statt nur IP) werden jetzt unterstuetzt: mount.cifs
  bekommt die tatsaechliche Ziel-IP gezielt per "ip="-Option mitgegeben,
  aufgeloest ueber _resolve_unc_host_ip(). Deckt zwei reale Faelle ab, die
  beim Live-Test mit einem echten Server (Kurzname "s2025") auftraten:
  1. Ein bloer Kurzname ohne DNS-Suffixsuche loest auf diesem Server gar
     nicht auf -- Fallback probiert zusaetzlich den aus der LDAP-Server-
     Einstellung abgeleiteten AD-Domaenensuffix (a la ".ad.eertmoed.net").
  2. IPv4 wird bevorzugt aufgeloest, IPv6 nur als Fallback genutzt, falls
     fuer den Hostnamen keine IPv4-Adresse existiert.
  Der Hostname bleibt dabei unveraendert in der UNC sichtbar/gespeichert.
- AD-Gruppenzuordnungen (App-Rechte) und Fileshare-Gruppen sind jetzt per
  Bearbeiten-Button (Stift-Icon) direkt aenderbar statt nur loeschen+neu
  anlegen zu koennen -- neue edit_ldap_group_mapping/edit_fileshare_mapping
  POST-Routen (UPDATE per id), Bearbeiten-Modals vorbefuellt inkl.
  AD-Gruppen-Dropdown (gleiches Lade-Muster wie beim Anlegen).
- UX: "Erforderliche AD-Gruppe fuer Login" ist jetzt ein Dropdown mit
  "Gruppen laden" (identisches Muster wie die anderen AD-Gruppenfelder)
  statt eines Freitextfelds, in das der volle DN von Hand einzutragen war.
- _normalize_share_unc()-Hilfsfunktion aus dem Add-Handler herausgezogen,
  jetzt auch vom Edit-Handler genutzt (kein duplizierter Code).

Live auf POETEST verifiziert: Hostname-Mount ("s2025") erfolgreich nach
Fix, Bearbeiten-Modals korrekt vorbefuellt und persistiert, Dropdown laedt
55 AD-Gruppen und behaelt die aktuelle Auswahl beim Neuladen bei.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 16:56:40 +02:00
co-authored by Claude Sonnet 5
parent 09b222615a
commit 2c9ec8a100
2 changed files with 241 additions and 13 deletions
+130 -10
View File
@@ -135,12 +135,18 @@
<td>{{ m.app_group_name or '—' }}</td>
<td>
{% if can_edit %}
<form method="post" data-confirm="Zuordnung „{{ m.ad_group_name }} → {{ m.app_group_name }}“ löschen?">
<input type="hidden" name="delete_ldap_group_mapping" value="{{ m.id }}">
<button type="submit" class="icon-btn" style="color:var(--danger);" title="Löschen">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0l-1 14a2 2 0 01-2 2H7a2 2 0 01-2-2L4 6"/></svg>
<div class="row-actions">
<button type="button" class="icon-btn" title="Bearbeiten"
onclick="openEditLdapMappingModal('{{ m.id }}','{{ m.ad_group_dn|e }}','{{ m.ad_group_name|e }}','{{ m.app_group_id }}')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 013 3L7 19l-4 1 1-4z"/></svg>
</button>
</form>
<form method="post" data-confirm="Zuordnung „{{ m.ad_group_name }} → {{ m.app_group_name }}“ löschen?">
<input type="hidden" name="delete_ldap_group_mapping" value="{{ m.id }}">
<button type="submit" class="icon-btn" style="color:var(--danger);" title="Löschen">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0l-1 14a2 2 0 01-2 2H7a2 2 0 01-2-2L4 6"/></svg>
</button>
</form>
</div>
{% endif %}
</td>
</tr>
@@ -186,12 +192,18 @@
<td class="mono" style="font-size:12px;">{{ m.share_unc }}</td>
<td>
{% if can_edit %}
<form method="post" data-confirm="Fileshare-Zuordnung „{{ m.ad_group_name }} → {{ m.share_label }}“ löschen?">
<input type="hidden" name="delete_fileshare_mapping" value="{{ m.id }}">
<button type="submit" class="icon-btn" style="color:var(--danger);" title="Löschen">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0l-1 14a2 2 0 01-2 2H7a2 2 0 01-2-2L4 6"/></svg>
<div class="row-actions">
<button type="button" class="icon-btn" title="Bearbeiten"
onclick="openEditFileshareMappingModal('{{ m.id }}','{{ m.ad_group_dn|e }}','{{ m.ad_group_name|e }}','{{ m.share_label|e }}','{{ m.share_unc|e }}')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 013 3L7 19l-4 1 1-4z"/></svg>
</button>
</form>
<form method="post" data-confirm="Fileshare-Zuordnung „{{ m.ad_group_name }} → {{ m.share_label }}“ löschen?">
<input type="hidden" name="delete_fileshare_mapping" value="{{ m.id }}">
<button type="submit" class="icon-btn" style="color:var(--danger);" title="Löschen">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0l-1 14a2 2 0 01-2 2H7a2 2 0 01-2-2L4 6"/></svg>
</button>
</form>
</div>
{% endif %}
</td>
</tr>
@@ -283,6 +295,77 @@
</div>
</div>
<div class="modal-overlay" id="editLdapMappingModal">
<div class="modal">
<form method="post">
<input type="hidden" name="edit_ldap_group_mapping" id="editLdapMappingId" value="">
<div class="modal-header">
<h3>AD-Gruppenzuordnung bearbeiten</h3>
<button type="button" class="modal-close" data-close-modal>&times;</button>
</div>
<div class="modal-body">
<div class="field">
<label>AD-Gruppe</label>
<div class="flex gap-2">
<select name="ad_group_dn" id="editLdapMappingAdGroup" required style="flex:1;"></select>
<button type="button" class="btn btn-secondary btn-sm" id="editLdapMappingLoadGroupsBtn">Gruppen laden</button>
</div>
<input type="hidden" name="ad_group_name" id="editLdapMappingAdGroupName">
<div class="field-hint" id="editLdapMappingLoadStatus">Fragt live per Bind-Konto alle Gruppen aus dem Verzeichnis ab.</div>
</div>
<div class="field"><label>App-Rechtegruppe</label>
<select name="app_group_id" id="editLdapMappingAppGroup" required>
<option value="">— auswählen —</option>
<option value="admin">Admin (alle Rechte)</option>
{% for g in ldap_groups %}
<option value="{{ g['id'] }}">{{ g['name'] }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
<button type="submit" class="btn btn-primary">Speichern</button>
</div>
</form>
</div>
</div>
<div class="modal-overlay" id="editFileshareMappingModal">
<div class="modal">
<form method="post">
<input type="hidden" name="edit_fileshare_mapping" id="editFsMappingId" value="">
<div class="modal-header">
<h3>Fileshare-Zuordnung bearbeiten</h3>
<button type="button" class="modal-close" data-close-modal>&times;</button>
</div>
<div class="modal-body">
<div class="field">
<label>AD-Gruppe</label>
<div class="flex gap-2">
<select name="fs_ad_group_dn" id="editFsMappingAdGroup" required style="flex:1;"></select>
<button type="button" class="btn btn-secondary btn-sm" id="editFsMappingLoadGroupsBtn">Gruppen laden</button>
</div>
<input type="hidden" name="fs_ad_group_name" id="editFsMappingAdGroupName">
<div class="field-hint" id="editFsMappingLoadStatus">Fragt live per Bind-Konto alle Gruppen aus dem Verzeichnis ab.</div>
</div>
<div class="field"><label>Bezeichnung</label>
<input type="text" name="fs_share_label" id="editFsMappingLabel" placeholder="z.B. Vertrieb" required>
<div class="field-hint">Anzeigename in der Freigaben-Auswahl — auch Ordnername unter dem Mount-Punkt.</div>
</div>
<div class="field"><label>Freigabe-Pfad (UNC)</label>
<input type="text" name="fs_share_unc" id="editFsMappingUnc" placeholder="//fileserver/freigabe" required>
<div class="field-hint">Beide Schreibweisen funktionieren — <code>\\server\freigabe</code> wird automatisch in das von Linux benötigte <code>//server/freigabe</code> umgewandelt.</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
<button type="submit" class="btn btn-primary">Speichern</button>
</div>
</form>
</div>
</div>
<script>
(function () {
function wireAdGroupLoader(btnId, selectId, nameFieldId, statusId, includeEmptyOption) {
@@ -342,7 +425,44 @@
wireAdGroupLoader('ldapMappingLoadGroupsBtn', 'ldapMappingAdGroup', 'ldapMappingAdGroupName', 'ldapMappingLoadStatus', false);
wireAdGroupLoader('fsMappingLoadGroupsBtn', 'fsMappingAdGroup', 'fsMappingAdGroupName', 'fsMappingLoadStatus', false);
wireAdGroupLoader('requiredGroupLoadBtn', 'requiredGroupSelect', null, 'requiredGroupLoadStatus', true);
wireAdGroupLoader('editLdapMappingLoadGroupsBtn', 'editLdapMappingAdGroup', 'editLdapMappingAdGroupName', 'editLdapMappingLoadStatus', false);
wireAdGroupLoader('editFsMappingLoadGroupsBtn', 'editFsMappingAdGroup', 'editFsMappingAdGroupName', 'editFsMappingLoadStatus', false);
})();
// Bearbeiten-Modals vorbefuellen -- die AD-Gruppe steht dabei zunaechst nur
// als DN+Name aus der Datenbank zur Verfuegung (ohne erneute LDAP-Abfrage);
// "Gruppen laden" ersetzt die Auswahlliste bei Bedarf durch die vollstaendige,
// aktuelle AD-Gruppenliste und behaelt den bisherigen Wert dabei bei (siehe
// wireAdGroupLoader oben).
function seedMappingSelect(selectId, dn, name) {
var select = document.getElementById(selectId);
select.innerHTML = '';
var opt = document.createElement('option');
opt.value = dn;
opt.textContent = name || dn;
opt.dataset.name = name || dn;
opt.selected = true;
select.appendChild(opt);
}
function openEditLdapMappingModal(id, dn, name, appGroupId) {
document.getElementById('editLdapMappingId').value = id;
seedMappingSelect('editLdapMappingAdGroup', dn, name);
document.getElementById('editLdapMappingAdGroupName').value = name;
document.getElementById('editLdapMappingAppGroup').value = appGroupId;
document.getElementById('editLdapMappingLoadStatus').textContent = 'Fragt live per Bind-Konto alle Gruppen aus dem Verzeichnis ab.';
PoeUI.openModal('editLdapMappingModal');
}
function openEditFileshareMappingModal(id, dn, name, label, unc) {
document.getElementById('editFsMappingId').value = id;
seedMappingSelect('editFsMappingAdGroup', dn, name);
document.getElementById('editFsMappingAdGroupName').value = name;
document.getElementById('editFsMappingLabel').value = label;
document.getElementById('editFsMappingUnc').value = unc;
document.getElementById('editFsMappingLoadStatus').textContent = 'Fragt live per Bind-Konto alle Gruppen aus dem Verzeichnis ab.';
PoeUI.openModal('editFileshareMappingModal');
}
</script>
{% endif %}
{% endblock %}