feat(02-04): LdapModule with sync service, config service, scheduler, and controller

- LdapService uses ldapts for DIRECTORY SYNC ONLY (anti-pattern avoidance)
- LdapConfigService creates default field mappings per D-16 (displayName, mail, sAMAccountName)
- Custom field mappings can be added/removed per D-17
- Per-tenant LDAP config per D-18
- syncUsersForTenant deactivates users removed from LDAP per D-15
- LdapSyncScheduler sets tenant context explicitly per Pitfall 2
- Manual sync endpoint POST /ldap/sync per D-14
- Auto-sync cron checks syncIntervalMin per D-14
- Test connection endpoint for LDAP config validation
- OpenLDAP + phpLDAPadmin added to docker-compose.dev.yml
- LDAP search filter sanitization per T-02-16
- bindPassword never returned in API responses per T-02-17
This commit is contained in:
2026-06-19 08:38:07 +02:00
parent ac617f4fe5
commit f928cd7713
10 changed files with 874 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
import { PartialType } from '@nestjs/mapped-types';
import {
IsBoolean,
IsInt,
IsNotEmpty,
IsOptional,
IsString,
IsUrl,
Min,
} from 'class-validator';
/**
* DTO for creating a new LDAP configuration per tenant (D-18).
*/
export class CreateLdapConfigDto {
@IsUrl({ protocols: ['ldap', 'ldaps'], require_tld: false })
serverUrl!: string;
@IsString()
@IsNotEmpty()
baseDn!: string;
@IsString()
@IsNotEmpty()
bindDn!: string;
@IsString()
@IsNotEmpty()
bindPassword!: string;
@IsString()
@IsOptional()
searchFilter?: string = '(objectClass=person)';
@IsInt()
@IsOptional()
@Min(0)
syncIntervalMin?: number = 60;
@IsBoolean()
@IsOptional()
isActive?: boolean = true;
}
/**
* DTO for updating an existing LDAP configuration.
*/
export class UpdateLdapConfigDto extends PartialType(CreateLdapConfigDto) {}
/**
* DTO for creating a field mapping entry (D-17).
*/
export class CreateFieldMappingDto {
@IsString()
@IsNotEmpty()
ldapField!: string;
@IsString()
@IsNotEmpty()
tesseraField!: string;
@IsBoolean()
@IsOptional()
isDefault?: boolean;
}