This commit is contained in:
Bastian Wagner
2026-07-15 15:53:27 +02:00
parent a79988b35c
commit d51d915c74
8 changed files with 119 additions and 36 deletions

View File

@@ -1,5 +1,6 @@
import { Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Ber, BerWriter, Client } from 'ldapts';
interface LldapUserInput {
username: string;
@@ -70,6 +71,8 @@ export interface LldapAccountUser {
@Injectable()
export class LldapService {
private static readonly passwordModifyOid = '1.3.6.1.4.1.4203.1.11.1';
private cachedHeaders?: { expiresAt: number; headers: Record<string, string> };
constructor(private readonly config: ConfigService) {}
@@ -84,11 +87,17 @@ export class LldapService {
id: input.username,
email: input.email,
displayName: input.displayName,
password: input.password,
},
},
);
try {
await this.setPassword(input.username, input.password);
} catch (error) {
await this.deleteUser(input.username).catch(() => undefined);
throw error;
}
const defaultGroup = this.config.get<string>('LLDAP_DEFAULT_GROUP');
if (defaultGroup) {
await this.addUserToGroup(input.username, defaultGroup);
@@ -96,12 +105,18 @@ export class LldapService {
}
async setPassword(username: string, password: string): Promise<void> {
await this.graphql(
`mutation SetPassword($userId: String!, $password: String!) {
setPassword(userId: $userId, password: $password)
}`,
{ userId: username, password },
);
const client = new Client({ url: this.config.getOrThrow<string>('LLDAP_LDAP_URL') });
try {
await client.bind(this.adminDn(), this.config.getOrThrow<string>('LLDAP_ADMIN_PASSWORD'));
await client.exop(
LldapService.passwordModifyOid,
this.passwordModifyRequestValue(this.userDn(username), password),
);
} catch (error) {
throw new InternalServerErrorException(`LLDAP password change failed: ${this.errorMessage(error)}`);
} finally {
await client.unbind().catch(() => undefined);
}
}
async updateUser(username: string, input: LldapUserUpdateInput): Promise<void> {
@@ -438,4 +453,30 @@ export class LldapService {
this.cachedHeaders = { headers, expiresAt: Date.now() + 5 * 60_000 };
return headers;
}
private passwordModifyRequestValue(userDn: string, newPassword: string): Buffer {
const writer = new BerWriter();
writer.startSequence(Ber.Sequence | Ber.Constructor);
writer.writeString(userDn, 0x80);
writer.writeString(newPassword, 0x82);
writer.endSequence();
return writer.buffer;
}
private adminDn(): string {
return this.userDn(this.config.getOrThrow<string>('LLDAP_ADMIN_USERNAME'));
}
private userDn(username: string): string {
const baseDn = this.config.getOrThrow<string>('LLDAP_BASE_DN');
return `uid=${this.escapeDn(username)},ou=people,${baseDn}`;
}
private escapeDn(value: string): string {
return value.replace(/[\\,+"<>;=]/g, (char) => `\\${char}`);
}
private errorMessage(error: unknown): string {
return error instanceof Error ? error.message : 'unknown error';
}
}