logging
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import { Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Ber, BerWriter, Client } from 'ldapts';
|
||||
import { ApplicationErrorCategory, ApplicationErrorCode } from '../application-error-log/application-error-codes';
|
||||
import { ApplicationErrorLoggerService } from '../application-error-log/application-error-logger.service';
|
||||
import { markErrorAsLogged } from '../application-error-log/logged-error-marker';
|
||||
import { RequestContextService } from '../common/request-context.service';
|
||||
|
||||
interface LldapUserInput {
|
||||
username: string;
|
||||
@@ -75,7 +79,11 @@ export class LldapService {
|
||||
|
||||
private cachedHeaders?: { expiresAt: number; headers: Record<string, string> };
|
||||
|
||||
constructor(private readonly config: ConfigService) {}
|
||||
constructor(
|
||||
private readonly config: ConfigService,
|
||||
private readonly applicationErrorLogger: ApplicationErrorLoggerService,
|
||||
private readonly requestContext: RequestContextService,
|
||||
) {}
|
||||
|
||||
async createUser(input: LldapUserInput): Promise<void> {
|
||||
await this.graphql(
|
||||
@@ -113,7 +121,26 @@ export class LldapService {
|
||||
this.passwordModifyRequestValue(this.userDn(username), password),
|
||||
);
|
||||
} catch (error) {
|
||||
throw new InternalServerErrorException(`LLDAP password change failed: ${this.errorMessage(error)}`);
|
||||
await this.applicationErrorLogger.log({
|
||||
error,
|
||||
category: ApplicationErrorCategory.EXTERNAL_API,
|
||||
code: ApplicationErrorCode.EXTERNAL_API_REQUEST_FAILED,
|
||||
module: 'LldapModule',
|
||||
service: LldapService.name,
|
||||
operation: 'setPassword',
|
||||
requestContext: {
|
||||
...this.requestContext.get(),
|
||||
userId: username,
|
||||
},
|
||||
context: {
|
||||
provider: 'LLDAP',
|
||||
protocol: 'LDAP',
|
||||
},
|
||||
handled: true,
|
||||
});
|
||||
const exception = new InternalServerErrorException(`LLDAP password change failed: ${this.errorMessage(error)}`);
|
||||
markErrorAsLogged(exception);
|
||||
throw exception;
|
||||
} finally {
|
||||
await client.unbind().catch(() => undefined);
|
||||
}
|
||||
@@ -394,15 +421,22 @@ export class LldapService {
|
||||
|
||||
private async graphql<T = unknown>(query: string, variables: Record<string, unknown>): Promise<T> {
|
||||
const endpoint = `${this.config.getOrThrow<string>('LLDAP_URL').replace(/\/$/, '')}/api/graphql`;
|
||||
const headers = await this.adminHeaders();
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
...headers,
|
||||
},
|
||||
body: JSON.stringify({ query, variables }),
|
||||
});
|
||||
let response: Response;
|
||||
try {
|
||||
const headers = await this.adminHeaders();
|
||||
response = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
...headers,
|
||||
},
|
||||
body: JSON.stringify({ query, variables }),
|
||||
});
|
||||
} catch (error) {
|
||||
await this.logGraphqlFailure(error, endpoint, 'request_failed');
|
||||
markErrorAsLogged(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
const payload = (await response.json().catch(() => ({}))) as {
|
||||
data?: T;
|
||||
@@ -414,11 +448,21 @@ export class LldapService {
|
||||
if (/not found/i.test(message)) {
|
||||
throw new NotFoundException('LLDAP user not found');
|
||||
}
|
||||
throw new InternalServerErrorException(`LLDAP GraphQL request failed: ${message}`);
|
||||
const exception = new InternalServerErrorException(`LLDAP GraphQL request failed: ${message}`);
|
||||
await this.logGraphqlFailure(exception, endpoint, 'bad_response', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
errorMessages: payload.errors?.map((error) => error.message),
|
||||
});
|
||||
markErrorAsLogged(exception);
|
||||
throw exception;
|
||||
}
|
||||
|
||||
if (!payload.data) {
|
||||
throw new InternalServerErrorException('LLDAP GraphQL response did not contain data');
|
||||
const exception = new InternalServerErrorException('LLDAP GraphQL response did not contain data');
|
||||
await this.logGraphqlFailure(exception, endpoint, 'missing_data', { status: response.status });
|
||||
markErrorAsLogged(exception);
|
||||
throw exception;
|
||||
}
|
||||
|
||||
return payload.data;
|
||||
@@ -484,4 +528,28 @@ export class LldapService {
|
||||
private errorMessage(error: unknown): string {
|
||||
return error instanceof Error ? error.message : 'unknown error';
|
||||
}
|
||||
|
||||
private async logGraphqlFailure(
|
||||
error: unknown,
|
||||
endpoint: string,
|
||||
reason: string,
|
||||
context: Record<string, unknown> = {},
|
||||
): Promise<void> {
|
||||
await this.applicationErrorLogger.log({
|
||||
error,
|
||||
category: ApplicationErrorCategory.EXTERNAL_API,
|
||||
code: ApplicationErrorCode.EXTERNAL_API_REQUEST_FAILED,
|
||||
module: 'LldapModule',
|
||||
service: LldapService.name,
|
||||
operation: 'graphql',
|
||||
requestContext: this.requestContext.get(),
|
||||
context: {
|
||||
provider: 'LLDAP',
|
||||
endpoint,
|
||||
reason,
|
||||
...context,
|
||||
},
|
||||
handled: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user