From 2da61689ea8011259d8a47c307b3709823a36a64 Mon Sep 17 00:00:00 2001 From: sysops Date: Fri, 20 Mar 2026 11:23:12 +0100 Subject: [PATCH] feat: LDAP-Test zeigt User-Tabelle mit Univention-UCS-Support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nach erfolgreichem Verbindungstest werden bis zu 50 Benutzer (UID, Name, E-Mail) in einer scrollbaren Tabelle angezeigt. Unterstützt mailPrimaryAddress (Univention UCS) als Fallback für mail sowie inetOrgPerson objectClass. Co-Authored-By: Claude Sonnet 4.6 --- internal/ldapauth/client.go | 62 ++++++++++++++++++++++++-------- src/app/admin/page.tsx | 70 ++++++++++++++++++++++++++++++++----- src/lib/api.ts | 8 +++++ 3 files changed, 118 insertions(+), 22 deletions(-) diff --git a/internal/ldapauth/client.go b/internal/ldapauth/client.go index 6f3a031..cf6c5a5 100644 --- a/internal/ldapauth/client.go +++ b/internal/ldapauth/client.go @@ -23,14 +23,23 @@ type Config struct { TLSSkipVerify bool } +// LDAPUser is a preview entry returned by TestConnection. +type LDAPUser struct { + DN string `json:"dn"` + UID string `json:"uid"` + DisplayName string `json:"display_name"` + Mail string `json:"mail"` +} + // TestResult is the structured output of TestConnection. type TestResult struct { - OK bool `json:"ok"` - Message string `json:"message"` - LatencyMS int64 `json:"latency_ms"` - ServerInfo string `json:"server_info"` - UsersFound int `json:"users_found"` - ErrorDetail string `json:"error_detail"` + OK bool `json:"ok"` + Message string `json:"message"` + LatencyMS int64 `json:"latency_ms"` + ServerInfo string `json:"server_info"` + UsersFound int `json:"users_found"` + Users []LDAPUser `json:"users"` + ErrorDetail string `json:"error_detail"` } // TestConnection opens a connection to the LDAP server, binds with the service @@ -62,8 +71,8 @@ func TestConnection(cfg Config) TestResult { // Query RootDSE for server info serverInfo := queryRootDSE(conn) - // Count user objects (capped at 500 to avoid large result sets) - usersFound := countUsers(conn, cfg.BaseDN) + // Fetch user objects (capped at 500 for count, 50 for preview list) + usersFound, users := listUsers(conn, cfg.BaseDN) return TestResult{ OK: true, @@ -71,6 +80,7 @@ func TestConnection(cfg Config) TestResult { LatencyMS: time.Since(start).Milliseconds(), ServerInfo: serverInfo, UsersFound: usersFound, + Users: users, } } @@ -176,20 +186,44 @@ func queryRootDSE(conn *ldapv3.Conn) string { return strings.Join(parts, " ") } -// countUsers searches for person/user objects and returns the count (max 500). -func countUsers(conn *ldapv3.Conn, baseDN string) int { +// listUsers searches for person/user objects and returns the total count (max 500) +// plus a preview slice of up to 50 entries. Supports both AD (mail) and +// Univention UCS (mailPrimaryAddress) mail attributes. +func listUsers(conn *ldapv3.Conn, baseDN string) (int, []LDAPUser) { req := ldapv3.NewSearchRequest( baseDN, ldapv3.ScopeWholeSubtree, ldapv3.NeverDerefAliases, 500, 30, false, - "(|(objectClass=person)(objectClass=user))", - []string{"dn"}, + "(|(objectClass=person)(objectClass=user)(objectClass=inetOrgPerson))", + []string{"dn", "uid", "cn", "displayName", "mail", "mailPrimaryAddress"}, nil, ) res, err := conn.Search(req) if err != nil { - return 0 + return 0, nil } - return len(res.Entries) + + preview := make([]LDAPUser, 0, min(50, len(res.Entries))) + for i, e := range res.Entries { + if i >= 50 { + break + } + mail := e.GetAttributeValue("mail") + if mail == "" { + mail = e.GetAttributeValue("mailPrimaryAddress") + } + displayName := e.GetAttributeValue("displayName") + if displayName == "" { + displayName = e.GetAttributeValue("cn") + } + preview = append(preview, LDAPUser{ + DN: e.DN, + UID: e.GetAttributeValue("uid"), + DisplayName: displayName, + Mail: mail, + }) + } + return len(res.Entries), preview } + diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index cda93ce..a47bce6 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -2195,7 +2195,7 @@ export default function AdminPage() { {/* Test result */} {ldapTestResult && ( - +
{ldapTestResult.ok ? "Verbunden" : "Fehler"} @@ -2208,12 +2208,39 @@ export default function AdminPage() { {ldapTestResult.server_info && (

{ldapTestResult.server_info}

)} - {ldapTestResult.users_found > 0 && ( -

{ldapTestResult.users_found} Benutzer gefunden

- )} {ldapTestResult.error_detail && (

{ldapTestResult.error_detail}

)} + {ldapTestResult.ok && ldapTestResult.users_found > 0 && ( +
+

+ {ldapTestResult.users_found} Benutzer gefunden + {ldapTestResult.users?.length < ldapTestResult.users_found && ( + (Vorschau: {ldapTestResult.users?.length}) + )} +

+
+ + + + + + + + + + {ldapTestResult.users?.map((u, i) => ( + + + + + + ))} + +
UIDNameE-Mail
{u.uid || "–"}{u.display_name || "–"}{u.mail || "–"}
+
+
+ )} )} @@ -2458,7 +2485,7 @@ export default function AdminPage() { {/* Test result */} {tenantLdapTestResult && ( - +
{tenantLdapTestResult.ok ? "Verbunden" : "Fehler"} @@ -2471,12 +2498,39 @@ export default function AdminPage() { {tenantLdapTestResult.server_info && (

{tenantLdapTestResult.server_info}

)} - {tenantLdapTestResult.users_found > 0 && ( -

{tenantLdapTestResult.users_found} Benutzer gefunden

- )} {tenantLdapTestResult.error_detail && (

{tenantLdapTestResult.error_detail}

)} + {tenantLdapTestResult.ok && tenantLdapTestResult.users_found > 0 && ( +
+

+ {tenantLdapTestResult.users_found} Benutzer gefunden + {tenantLdapTestResult.users?.length < tenantLdapTestResult.users_found && ( + (Vorschau: {tenantLdapTestResult.users?.length}) + )} +

+
+ + + + + + + + + + {tenantLdapTestResult.users?.map((u, i) => ( + + + + + + ))} + +
UIDNameE-Mail
{u.uid || "–"}{u.display_name || "–"}{u.mail || "–"}
+
+
+ )} )} diff --git a/src/lib/api.ts b/src/lib/api.ts index 782514e..008d253 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -642,12 +642,20 @@ export interface LDAPConfig { updated_by?: string; } +export interface LDAPTestUser { + dn: string; + uid: string; + display_name: string; + mail: string; +} + export interface LDAPTestResult { ok: boolean; message: string; latency_ms: number; server_info: string; users_found: number; + users: LDAPTestUser[]; error_detail: string; }