feat(PROJ-22): LDAP Web-GUI + feat(PROJ-21): Multi-Tenancy Phase 1
PROJ-22 – LDAP Web-GUI Konfiguration & Test: - internal/ldapconfig/store.go: AES-256-GCM Passwortspeicherung, CRUD Upsert (id=1) - internal/ldapauth/client.go: TestConnection (RootDSE, UserCount) + Authenticate (2-step bind) - internal/auth/auth.go: LDAP-Fallback in Login(), Gruppen-Rollenzuordnung, issueToken helper - internal/api/ldap_tenants.go: GET/PUT/DELETE/POST-test /api/admin/ldap mit Audit-Log - go.mod: github.com/go-ldap/ldap/v3 v3.4.8 hinzugefügt - Frontend: LDAPConfig/LDAPTestResult Typen, LDAP-Tab mit Gruppen-Mappings + Testergebnis PROJ-21 Phase 1+6+7 – Multi-Tenancy Grundstruktur: - internal/tenantstore/store.go: tenants, tenant_domains, tenant_ldap Schema; Migration users/audit_log - API: 8 Tenant-Routen (CRUD + Domain-Management) via SetTenants() - cmd/archivmail/main.go: ldapSt + tenantSt initialisiert - Frontend: Mandanten-Tab mit Tabelle, Domain-Dialog, Deaktivieren/Löschen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+129
@@ -599,3 +599,132 @@ export async function fixSecurityIssue(action: string): Promise<{ message: strin
|
||||
body: JSON.stringify({ action }),
|
||||
});
|
||||
}
|
||||
|
||||
// ── LDAP ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface LDAPGroupMapping {
|
||||
group_dn: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface LDAPConfig {
|
||||
id?: number;
|
||||
enabled: boolean;
|
||||
url: string;
|
||||
bind_dn: string;
|
||||
bind_password: string;
|
||||
base_dn: string;
|
||||
user_filter: string;
|
||||
tls: boolean;
|
||||
tls_skip_verify: boolean;
|
||||
default_role: string;
|
||||
group_mappings: LDAPGroupMapping[];
|
||||
updated_at?: string;
|
||||
updated_by?: string;
|
||||
}
|
||||
|
||||
export interface LDAPTestResult {
|
||||
ok: boolean;
|
||||
message: string;
|
||||
latency_ms: number;
|
||||
server_info: string;
|
||||
users_found: number;
|
||||
error_detail: string;
|
||||
}
|
||||
|
||||
export async function getLDAPConfig(): Promise<LDAPConfig | null> {
|
||||
try {
|
||||
return await request<LDAPConfig>("/api/admin/ldap");
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error && e.message.includes("404")) return null;
|
||||
if (e instanceof Error && e.message.includes("no ldap config")) return null;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveLDAPConfig(cfg: Partial<LDAPConfig>): Promise<void> {
|
||||
await request<void>("/api/admin/ldap", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(cfg),
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteLDAPConfig(): Promise<void> {
|
||||
await request<void>("/api/admin/ldap", { method: "DELETE" });
|
||||
}
|
||||
|
||||
export async function testLDAPConfig(
|
||||
payload: { use_saved: boolean } & Partial<LDAPConfig>
|
||||
): Promise<LDAPTestResult> {
|
||||
return request<LDAPTestResult>("/api/admin/ldap/test", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
// ── Tenants ────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface Tenant {
|
||||
id: number;
|
||||
name: string;
|
||||
slug: string;
|
||||
active: boolean;
|
||||
created_at: string;
|
||||
domain_count?: number;
|
||||
user_count?: number;
|
||||
}
|
||||
|
||||
export interface TenantDomain {
|
||||
id: number;
|
||||
tenant_id: number;
|
||||
domain: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export async function getTenants(): Promise<Tenant[]> {
|
||||
return request<Tenant[]>("/api/tenants");
|
||||
}
|
||||
|
||||
export async function createTenant(name: string, slug: string): Promise<Tenant> {
|
||||
return request<Tenant>("/api/tenants", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ name, slug }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateTenant(
|
||||
id: number,
|
||||
data: { name?: string; active?: boolean }
|
||||
): Promise<Tenant> {
|
||||
return request<Tenant>(`/api/tenants/${id}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteTenant(id: number): Promise<void> {
|
||||
await request<void>(`/api/tenants/${id}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export async function getTenantDomains(id: number): Promise<TenantDomain[]> {
|
||||
return request<TenantDomain[]>(`/api/tenants/${id}/domains`);
|
||||
}
|
||||
|
||||
export async function addTenantDomain(
|
||||
tenantId: number,
|
||||
domain: string
|
||||
): Promise<TenantDomain> {
|
||||
return request<TenantDomain>(`/api/tenants/${tenantId}/domains`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ domain }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function removeTenantDomain(
|
||||
tenantId: number,
|
||||
domainId: number
|
||||
): Promise<void> {
|
||||
await request<void>(`/api/tenants/${tenantId}/domains/${domainId}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user