feat: LDAP-User-Sync in Mandanten-Benutzerliste
Neuer Endpoint POST /api/admin/tenants/{id}/ldap/sync importiert alle
LDAP-User (source=ldap) per UpsertLDAPUser in die Tenant-Benutzerliste.
Im Nutzer-Dialog erscheint ein "LDAP-Benutzer synchronisieren"-Button
wenn LDAP für den Mandanten aktiv ist. Unterstützt Univention UCS
(mailPrimaryAddress, inetOrgPerson). Benutzertabelle zeigt jetzt auch
die Quelle (local/ldap).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+50
-1
@@ -39,6 +39,8 @@ import {
|
||||
saveAdminTenantLDAPConfig,
|
||||
deleteAdminTenantLDAPConfig,
|
||||
testAdminTenantLDAPConfig,
|
||||
syncAdminTenantLDAP,
|
||||
type LDAPSyncResult,
|
||||
getAdminLabels,
|
||||
createAdminLabel,
|
||||
deleteAdminLabel,
|
||||
@@ -266,6 +268,8 @@ export default function AdminPage() {
|
||||
const [tenantUsers, setTenantUsers] = useState<User[]>([]);
|
||||
const [tenantUsersLoading, setTenantUsersLoading] = useState(false);
|
||||
const [tenantUsersError, setTenantUsersError] = useState("");
|
||||
const [tenantUsersSyncing, setTenantUsersSyncing] = useState(false);
|
||||
const [tenantUsersSyncResult, setTenantUsersSyncResult] = useState<LDAPSyncResult | null>(null);
|
||||
|
||||
// Labels state
|
||||
const [adminLabels, setAdminLabels] = useState<MailLabel[]>([]);
|
||||
@@ -786,6 +790,7 @@ export default function AdminPage() {
|
||||
setTenantUsersLoading(true);
|
||||
setTenantUsers([]);
|
||||
setTenantUsersError("");
|
||||
setTenantUsersSyncResult(null);
|
||||
try {
|
||||
const users = await getTenantUsers(t.id);
|
||||
setTenantUsers(users || []);
|
||||
@@ -796,6 +801,23 @@ export default function AdminPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSyncLDAPUsers() {
|
||||
if (!tenantUsersDialogId) return;
|
||||
setTenantUsersSyncing(true);
|
||||
setTenantUsersSyncResult(null);
|
||||
try {
|
||||
const result = await syncAdminTenantLDAP(tenantUsersDialogId);
|
||||
setTenantUsersSyncResult(result);
|
||||
// Reload user list after sync
|
||||
const users = await getTenantUsers(tenantUsersDialogId);
|
||||
setTenantUsers(users || []);
|
||||
} catch (err: unknown) {
|
||||
setTenantUsersSyncResult({ synced: 0, errors: [err instanceof Error ? err.message : "Sync fehlgeschlagen"] });
|
||||
} finally {
|
||||
setTenantUsersSyncing(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAddDomain() {
|
||||
if (!domainDialogTenant || !newDomain) return;
|
||||
setAddDomainLoading(true);
|
||||
@@ -2889,7 +2911,7 @@ export default function AdminPage() {
|
||||
<div className="py-4 text-center space-y-2">
|
||||
<p className="text-sm text-muted-foreground">Keine lokalen Benutzer diesem Mandanten zugewiesen.</p>
|
||||
{tenantUsersDialogLdap && (
|
||||
<p className="text-xs text-muted-foreground">LDAP ist aktiv — Benutzer erscheinen hier nach ihrem ersten Login.</p>
|
||||
<p className="text-xs text-muted-foreground">LDAP ist aktiv — Benutzer erscheinen hier nach ihrem ersten Login oder nach der Synchronisation.</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
@@ -2899,6 +2921,7 @@ export default function AdminPage() {
|
||||
<TableHead>Benutzername</TableHead>
|
||||
<TableHead>E-Mail</TableHead>
|
||||
<TableHead>Rolle</TableHead>
|
||||
<TableHead>Quelle</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
@@ -2908,6 +2931,7 @@ export default function AdminPage() {
|
||||
<TableCell className="font-medium">{u.username}</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">{u.email}</TableCell>
|
||||
<TableCell><Badge variant="outline">{u.role}</Badge></TableCell>
|
||||
<TableCell><Badge variant="secondary">{u.source || "local"}</Badge></TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={u.active ? "default" : "secondary"}>
|
||||
{u.active ? "Aktiv" : "Inaktiv"}
|
||||
@@ -2918,6 +2942,31 @@ export default function AdminPage() {
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
{tenantUsersDialogLdap && (
|
||||
<div className="border-t pt-3 space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={handleSyncLDAPUsers}
|
||||
disabled={tenantUsersSyncing}
|
||||
>
|
||||
{tenantUsersSyncing ? "Synchronisiere..." : "LDAP-Benutzer synchronisieren"}
|
||||
</Button>
|
||||
{tenantUsersSyncResult && (
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{tenantUsersSyncResult.synced} Benutzer synchronisiert
|
||||
{tenantUsersSyncResult.errors.length > 0 && (
|
||||
<span className="text-destructive ml-1">({tenantUsersSyncResult.errors.length} Fehler)</span>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{tenantUsersSyncResult?.errors?.length > 0 && (
|
||||
<p className="text-xs text-destructive font-mono">{tenantUsersSyncResult.errors.join(", ")}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user