proxy
This commit is contained in:
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user