Sts addition, kms updates, context object, improved exception handling

This commit is contained in:
2024-12-20 01:07:33 -05:00
parent 095ecbd643
commit c34ea76e4e
50 changed files with 3129 additions and 1149 deletions

View File

@@ -0,0 +1,102 @@
import { CallHandler, ExecutionContext, HttpException, Inject, Injectable, Logger, NestInterceptor, RequestTimeoutException } from '@nestjs/common';
import { randomUUID } from 'crypto';
import { catchError, Observable, tap, throwError } from 'rxjs';
import { Request as ExpressRequest, Response } from 'express';
import * as Joi from 'joi';
import { PrismaService } from '../_prisma/prisma.service';
import { ActionHandlers } from '../app.constants';
import { Action } from '../action.enum';
import { Format } from '../abstract-action.handler';
import { AwsException, InternalFailure } from '../aws-shared-entities/aws-exceptions';
import { IRequest, RequestContext } from './request.context';
@Injectable()
export class AuditInterceptor<T> implements NestInterceptor<T, Response> {
private readonly logger = new Logger(AuditInterceptor.name);
constructor(
@Inject(ActionHandlers)
private readonly handlers: ActionHandlers,
private readonly prismaService: PrismaService,
) {}
intercept(context: ExecutionContext, next: CallHandler<T>): Observable<any> {
const requestContext: RequestContext = {
requestId: randomUUID(),
}
const httpContext = context.switchToHttp();
const request = httpContext.getRequest<IRequest>();
request.context = requestContext;
const hasTargetHeader = Object.keys(request.headers).some( k => k.toLocaleLowerCase() === 'x-amz-target');
const action = hasTargetHeader ? request.headers['x-amz-target'] : request.body.Action;
const { value: resolvedAction } = Joi.string().required().valid(...Object.values(Action)).validate(action) as { value: Action | undefined };
requestContext.action = resolvedAction;
const response = context.switchToHttp().getResponse<Response>();
response.header('x-amzn-RequestId', requestContext.requestId);
if (!resolvedAction || !this.handlers[resolvedAction]?.audit) {
return next.handle().pipe(
catchError(async (error: Error) => {
await this.prismaService.audit.create({
data: {
id: requestContext.requestId,
action,
request: JSON.stringify({ __path: request.path, ...request.headers, ...request.body }),
response: JSON.stringify(error),
}
});
this.logger.error(error.message);
return error;
})
);
}
const handler = this.handlers[resolvedAction];
requestContext.format = handler.format;
return next.handle().pipe(
catchError((error: Error) => {
return throwError(() => {
if (error instanceof AwsException) {
return error;
}
const defaultError = new InternalFailure('Unexpected local AWS exception...');
this.logger.error(error.message);
defaultError.requestId = requestContext.requestId;
return defaultError;
});
}),
tap({
next: async (data) => await this.prismaService.audit.create({
data: {
id: requestContext.requestId,
action,
request: JSON.stringify({ __path: request.path, ...request.headers, ...request.body }),
response: JSON.stringify(data),
}
}),
error: async (error) => await this.prismaService.audit.create({
data: {
id: requestContext.requestId,
action,
request: JSON.stringify({ __path: request.path, ...request.headers, ...request.body }),
response: JSON.stringify(error),
}
}),
})
);
}
}

View File

@@ -0,0 +1,25 @@
import { ArgumentsHost, Catch, ExceptionFilter } from "@nestjs/common";
import { Response } from 'express';
import { AwsException } from "../aws-shared-entities/aws-exceptions";
import { IRequest } from "./request.context";
import { Format } from "../abstract-action.handler";
@Catch(AwsException)
export class AwsExceptionFilter implements ExceptionFilter {
catch(exception: AwsException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const request = ctx.getRequest<IRequest>();
const response = ctx.getResponse<Response>();
exception.requestId = request.context.requestId;
if (request.context.format === Format.Xml) {
const xml = exception.toXml();
return response.status(exception.statusCode).send(xml);
}
const [newError, newHeaders] = exception.toJson();
response.setHeaders(new Map(Object.entries(newHeaders)));
return response.status(exception.statusCode).json(newError.getResponse());
}
}

View File

@@ -0,0 +1,20 @@
import { Request } from "express";
import { Action } from "../action.enum";
import { Format } from "../abstract-action.handler";
export interface RequestContext {
action?: Action;
format?: Format;
requestId: string;
}
export interface IRequest extends Request {
context: RequestContext;
headers: {
'x-amz-target'?: string;
},
body: {
'Action'?: string;
}
}