More refactors, adding wider support
This commit is contained in:
parent
da84b6b085
commit
d8930a6a30
|
|
@ -33,5 +33,5 @@ abstract-action.handler.ts
|
|||
* format: the format for output (XML or JSON)
|
||||
* action: the action the handler is implementing (will be use to key by)
|
||||
* validator: the Joi validator to be executed to check for required params
|
||||
* handle(queryParams: T, awsProperties: AwsProperties): Record<string, any> | void
|
||||
* handle(queryParams: T, { awsProperties} : RequestContext): Record<string, any> | void
|
||||
* the method that implements the AWS action
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
-- CreateTable
|
||||
CREATE TABLE "Attribute" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"arn" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"value" TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Audit" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"action" TEXT,
|
||||
"request" TEXT,
|
||||
"response" TEXT
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Secret" (
|
||||
"versionId" TEXT NOT NULL PRIMARY KEY,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"secretString" TEXT NOT NULL,
|
||||
"accountId" TEXT NOT NULL,
|
||||
"region" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletionDate" DATETIME
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "SnsTopic" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"accountId" TEXT NOT NULL,
|
||||
"region" TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "SnsTopicSubscription" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"topicArn" TEXT NOT NULL,
|
||||
"endpoint" TEXT,
|
||||
"protocol" TEXT NOT NULL,
|
||||
"accountId" TEXT NOT NULL,
|
||||
"region" TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "SqsQueue" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"accountId" TEXT NOT NULL,
|
||||
"region" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "SqsQueueMessage" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"queueId" INTEGER NOT NULL,
|
||||
"senderId" TEXT NOT NULL,
|
||||
"message" TEXT NOT NULL,
|
||||
"inFlightRelease" DATETIME NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "SqsQueueMessage_queueId_fkey" FOREIGN KEY ("queueId") REFERENCES "SqsQueue" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Tag" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"arn" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"value" TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Attribute_arn_name_key" ON "Attribute"("arn", "name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Secret_name_idx" ON "Secret"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "SnsTopic_accountId_region_name_key" ON "SnsTopic"("accountId", "region", "name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "SqsQueue_accountId_region_name_key" ON "SqsQueue"("accountId", "region", "name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "SqsQueueMessage_queueId_idx" ON "SqsQueueMessage"("queueId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Tag_arn_name_key" ON "Tag"("arn", "name");
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
-- CreateTable
|
||||
CREATE TABLE "KmsAlias" (
|
||||
"name" TEXT NOT NULL,
|
||||
"accountId" TEXT NOT NULL,
|
||||
"region" TEXT NOT NULL,
|
||||
"kmsKeyId" TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY ("accountId", "region", "name")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "KmsKey" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"usage" TEXT NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"keySpec" TEXT NOT NULL,
|
||||
"key" TEXT NOT NULL,
|
||||
"accountId" TEXT NOT NULL,
|
||||
"region" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `updatedAt` to the `KmsAlias` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- RedefineTables
|
||||
PRAGMA defer_foreign_keys=ON;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_KmsAlias" (
|
||||
"name" TEXT NOT NULL,
|
||||
"accountId" TEXT NOT NULL,
|
||||
"region" TEXT NOT NULL,
|
||||
"kmsKeyId" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
|
||||
PRIMARY KEY ("accountId", "region", "name"),
|
||||
CONSTRAINT "KmsAlias_kmsKeyId_fkey" FOREIGN KEY ("kmsKeyId") REFERENCES "KmsKey" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_KmsAlias" ("accountId", "kmsKeyId", "name", "region") SELECT "accountId", "kmsKeyId", "name", "region" FROM "KmsAlias";
|
||||
DROP TABLE "KmsAlias";
|
||||
ALTER TABLE "new_KmsAlias" RENAME TO "KmsAlias";
|
||||
PRAGMA foreign_keys=ON;
|
||||
PRAGMA defer_foreign_keys=OFF;
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to alter the column `key` on the `KmsKey` table. The data in that column could be lost. The data in that column will be cast from `String` to `Binary`.
|
||||
- Added the required column `enabled` to the `KmsKey` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `keyState` to the `KmsKey` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `multiRegion` to the `KmsKey` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `origin` to the `KmsKey` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `policy` to the `KmsKey` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `updatedAt` to the `KmsKey` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- RedefineTables
|
||||
PRAGMA defer_foreign_keys=ON;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_KmsKey" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"enabled" BOOLEAN NOT NULL,
|
||||
"usage" TEXT NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"keySpec" TEXT NOT NULL,
|
||||
"keyState" TEXT NOT NULL,
|
||||
"origin" TEXT NOT NULL,
|
||||
"multiRegion" BOOLEAN NOT NULL,
|
||||
"policy" TEXT NOT NULL,
|
||||
"key" BLOB NOT NULL,
|
||||
"accountId" TEXT NOT NULL,
|
||||
"region" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_KmsKey" ("accountId", "createdAt", "description", "id", "key", "keySpec", "region", "usage") SELECT "accountId", "createdAt", "description", "id", "key", "keySpec", "region", "usage" FROM "KmsKey";
|
||||
DROP TABLE "KmsKey";
|
||||
ALTER TABLE "new_KmsKey" RENAME TO "KmsKey";
|
||||
PRAGMA foreign_keys=ON;
|
||||
PRAGMA defer_foreign_keys=OFF;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "KmsKey" ADD COLUMN "nextRotation" DATETIME;
|
||||
ALTER TABLE "KmsKey" ADD COLUMN "rotationPeriod" INTEGER;
|
||||
|
|
@ -37,12 +37,14 @@ model IamRole {
|
|||
accountId String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
policies IamRoleIamPolicyAttachment[]
|
||||
|
||||
@@unique([accountId, name])
|
||||
}
|
||||
|
||||
model IamPolicy {
|
||||
id String
|
||||
version Int @default(1)
|
||||
version Int @default(1)
|
||||
isDefault Boolean
|
||||
path String?
|
||||
name String
|
||||
|
|
@ -57,6 +59,15 @@ model IamPolicy {
|
|||
@@unique([accountId, path, name])
|
||||
}
|
||||
|
||||
model IamRoleIamPolicyAttachment {
|
||||
iamRoleId String
|
||||
iamPolicyId String
|
||||
|
||||
role IamRole @relation(fields: [iamRoleId], references: [id])
|
||||
|
||||
@@id([iamRoleId, iamPolicyId])
|
||||
}
|
||||
|
||||
model KmsAlias {
|
||||
name String
|
||||
accountId String
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { Request } from "express";
|
||||
|
||||
import { Action } from "../action.enum";
|
||||
import { Format } from "../abstract-action.handler";
|
||||
import { AwsProperties, Format } from "../abstract-action.handler";
|
||||
|
||||
export interface RequestContext {
|
||||
action?: Action;
|
||||
format?: Format;
|
||||
awsProperties: AwsProperties;
|
||||
readonly requestId: string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { randomUUID } from 'crypto';
|
||||
import { Action } from './action.enum';
|
||||
import * as Joi from 'joi';
|
||||
import { RequestContext } from './_context/request.context';
|
||||
|
||||
export type AwsProperties = {
|
||||
accountId: string;
|
||||
|
|
@ -17,18 +18,18 @@ export abstract class AbstractActionHandler<T = Record<string, string | number |
|
|||
|
||||
audit = true;
|
||||
abstract format: Format;
|
||||
abstract action: Action;
|
||||
abstract action: Action | Action[];
|
||||
abstract validator: Joi.ObjectSchema<T>;
|
||||
protected abstract handle(queryParams: T, awsProperties: AwsProperties): Record<string, any> | void;
|
||||
protected abstract handle(queryParams: T, context: RequestContext): Record<string, any> | void;
|
||||
|
||||
async getResponse(queryParams: T, awsProperties: AwsProperties) {
|
||||
async getResponse(queryParams: T, context: RequestContext) {
|
||||
if (this.format === Format.Xml) {
|
||||
return await this.getXmlResponse(queryParams, awsProperties);
|
||||
return await this.getXmlResponse(queryParams, context);
|
||||
}
|
||||
return await this.getJsonResponse(queryParams, awsProperties);
|
||||
return await this.getJsonResponse(queryParams, context);
|
||||
}
|
||||
|
||||
private async getXmlResponse(queryParams: T, awsProperties: AwsProperties) {
|
||||
private async getXmlResponse(queryParams: T, context: RequestContext) {
|
||||
const response = {
|
||||
'@': {
|
||||
xmlns: "https://sns.amazonaws.com/doc/2010-03-31/"
|
||||
|
|
@ -38,21 +39,22 @@ export abstract class AbstractActionHandler<T = Record<string, string | number |
|
|||
}
|
||||
}
|
||||
|
||||
const result = await this.handle(queryParams, awsProperties);
|
||||
const result = await this.handle(queryParams, context);
|
||||
|
||||
if (!result) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const action = Array.isArray(this.action) ? this.action[0] : this.action;
|
||||
return {
|
||||
[`${this.action}Result`]: {
|
||||
[`${action}Result`]: {
|
||||
...result,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async getJsonResponse(queryParams: T, awsProperties: AwsProperties) {
|
||||
const result = await this.handle(queryParams, awsProperties);
|
||||
private async getJsonResponse(queryParams: T, context: RequestContext) {
|
||||
const result = await this.handle(queryParams, context);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -302,6 +302,28 @@ export enum Action {
|
|||
SqsTagQueue = 'TagQueue',
|
||||
SqsUntagQueue = 'UntagQueue',
|
||||
|
||||
// V2 SQS
|
||||
V2_SqsAddPermisson = 'AmazonSQS.AddPermission',
|
||||
V2_SqsChangeMessageVisibility = 'AmazonSQS.ChangeMessageVisibility',
|
||||
V2_SqsChangeMessageVisibilityBatch = 'AmazonSQS.ChangeMessageVisibilityBatch',
|
||||
V2_SqsCreateQueue = 'AmazonSQS.CreateQueue',
|
||||
V2_SqsDeleteMessage = 'AmazonSQS.DeleteMessage',
|
||||
V2_SqsDeleteMessageBatch = 'AmazonSQS.DeleteMessageBatch',
|
||||
V2_SqsDeleteQueue = 'AmazonSQS.DeleteQueue',
|
||||
V2_SqsGetQueueAttributes = 'AmazonSQS.GetQueueAttributes',
|
||||
V2_SqsGetQueueUrl = 'AmazonSQS.GetQueueUrl',
|
||||
V2_SqsListDeadLetterSourceQueues = 'AmazonSQS.ListDeadLetterSourceQueues',
|
||||
V2_SqsListQueues = 'AmazonSQS.ListQueues',
|
||||
V2_SqsListQueueTags = 'AmazonSQS.ListQueueTags',
|
||||
V2_SqsPurgeQueue = 'AmazonSQS.PurgeQueue',
|
||||
V2_SqsReceiveMessage = 'AmazonSQS.ReceiveMessage',
|
||||
V2_SqsRemovePermission = 'AmazonSQS.RemovePermission',
|
||||
V2_SqsSendMessage = 'AmazonSQS.SendMessage',
|
||||
V2_SqsSendMessageBatch = 'AmazonSQS.SendMessageBatch',
|
||||
V2_SqsSetQueueAttributes = 'AmazonSQS.SetQueueAttributes',
|
||||
V2_SqsTagQueue = 'AmazonSQS.TagQueue',
|
||||
V2_SqsUntagQueue = 'AmazonSQS.UntagQueue',
|
||||
|
||||
// STS
|
||||
StsAssumeRole = 'AssumeRole',
|
||||
StsAssumeRoleWithSaml = 'AssumeRoleWithSaml',
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ import * as js2xmlparser from 'js2xmlparser';
|
|||
import { AbstractActionHandler, Format } from './abstract-action.handler';
|
||||
import { Action } from './action.enum';
|
||||
import { ActionHandlers } from './app.constants';
|
||||
import { AuditInterceptor } from './_context/audit.interceptor';
|
||||
import { AuditInterceptor } from './audit/audit.interceptor';
|
||||
import { CommonConfig } from './config/common-config.interface';
|
||||
import { InvalidAction, ValidationError } from './aws-shared-entities/aws-exceptions';
|
||||
import { IRequest } from './_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
__path: string;
|
||||
|
|
@ -28,7 +29,7 @@ export class AppController {
|
|||
@HttpCode(200)
|
||||
@UseInterceptors(AuditInterceptor)
|
||||
async post(
|
||||
@Req() request: Request,
|
||||
@Req() request: IRequest,
|
||||
@Body() body: Record<string, any>,
|
||||
@Headers() headers: Record<string, any>,
|
||||
) {
|
||||
|
|
@ -56,15 +57,11 @@ export class AppController {
|
|||
throw new ValidationError(validatorError.message);
|
||||
}
|
||||
|
||||
const awsProperties = {
|
||||
accountId: this.configService.get('AWS_ACCOUNT_ID'),
|
||||
region: this.configService.get('AWS_REGION'),
|
||||
host: `${this.configService.get('PROTO')}://${this.configService.get('HOST')}:${this.configService.get('PORT')}`,
|
||||
};
|
||||
const jsonResponse = await handler.getResponse(validQueryParams, request.context);
|
||||
|
||||
const jsonResponse = await handler.getResponse(validQueryParams, awsProperties);
|
||||
if (handler.format === Format.Xml) {
|
||||
return js2xmlparser.parse(`${handler.action}Response`, jsonResponse);
|
||||
const action = Array.isArray(handler.action) ? handler.action[0] : handler.action;
|
||||
return js2xmlparser.parse(`${action}Response`, jsonResponse);
|
||||
}
|
||||
return jsonResponse;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { ConfigModule } from '@nestjs/config';
|
|||
|
||||
import { ActionHandlers } from './app.constants';
|
||||
import { AppController } from './app.controller';
|
||||
import { AuditInterceptor } from './_context/audit.interceptor';
|
||||
import { AuditInterceptor } from './audit/audit.interceptor';
|
||||
import { AwsSharedEntitiesModule } from './aws-shared-entities/aws-shared-entities.module';
|
||||
import localConfig from './config/local.config';
|
||||
import { KMSHandlers } from './kms/kms.constants';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import { Controller } from "@nestjs/common";
|
||||
import { AuditService } from "./audit.service";
|
||||
|
||||
@Controller('_audit')
|
||||
export class AuditController {
|
||||
|
||||
constructor(
|
||||
private readonly auditService: AuditService,
|
||||
) {}
|
||||
|
||||
}
|
||||
|
|
@ -9,7 +9,8 @@ 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';
|
||||
import { IRequest, RequestContext } from '../_context/request.context';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
|
||||
@Injectable()
|
||||
|
|
@ -21,12 +22,20 @@ export class AuditInterceptor<T> implements NestInterceptor<T, Response> {
|
|||
@Inject(ActionHandlers)
|
||||
private readonly handlers: ActionHandlers,
|
||||
private readonly prismaService: PrismaService,
|
||||
private readonly configService: ConfigService,
|
||||
) {}
|
||||
|
||||
intercept(context: ExecutionContext, next: CallHandler<T>): Observable<any> {
|
||||
|
||||
const awsProperties = {
|
||||
accountId: this.configService.get('AWS_ACCOUNT_ID'),
|
||||
region: this.configService.get('AWS_REGION'),
|
||||
host: `${this.configService.get('PROTO')}://${this.configService.get('HOST')}:${this.configService.get('PORT')}`,
|
||||
};
|
||||
|
||||
const requestContext: RequestContext = {
|
||||
requestId: randomUUID(),
|
||||
awsProperties,
|
||||
}
|
||||
|
||||
const httpContext = context.switchToHttp();
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { Module } from "@nestjs/common";
|
||||
|
||||
import { PrismaModule } from "../_prisma/prisma.module";
|
||||
import { AuditController } from "./audit.controller";
|
||||
import { AuditInterceptor } from "./audit.interceptor";
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [AuditController],
|
||||
providers: [AuditInterceptor],
|
||||
})
|
||||
export class AuditModule {}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
import { PrismaService } from "../_prisma/prisma.service";
|
||||
|
||||
@Injectable()
|
||||
export class AuditService {
|
||||
|
||||
|
||||
|
||||
constructor(
|
||||
private readonly prismaService: PrismaService,
|
||||
) {}
|
||||
|
||||
}
|
||||
|
|
@ -172,3 +172,23 @@ export class EntityAlreadyExists extends AwsException {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class NoSuchEntity extends AwsException {
|
||||
constructor() {
|
||||
super(
|
||||
'The request was rejected because it referenced a resource entity that does not exist. The error message describes the resource.',
|
||||
NoSuchEntity.name,
|
||||
HttpStatus.NOT_FOUND,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class QueueNameExists extends AwsException {
|
||||
constructor() {
|
||||
super(
|
||||
'A queue with this name already exists. Amazon SQS returns this error only if the request includes attributes whose values differ from those of the existing queue.',
|
||||
QueueNameExists.name,
|
||||
HttpStatus.BAD_REQUEST,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ export class TagsService {
|
|||
await this.prismaService.tag.deleteMany({ where: { arn, name } });
|
||||
}
|
||||
|
||||
static tagPairs(queryParams: Record<string, string>): { key: string, value: string }[] {
|
||||
static tagPairs(queryParams: Record<string, any>): { key: string, value: string }[] {
|
||||
const pairs: { key: string, value: string }[] = [];
|
||||
for (const param of Object.keys(queryParams)) {
|
||||
const components = breakdownAwsQueryParam(param);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,14 @@ import { ExistingActionHandlers } from './default-action-handler.constants';
|
|||
export const ExistingActionHandlersProvider = (inject: Array<InjectionToken | OptionalFactoryDependency>): Provider => ({
|
||||
provide: ExistingActionHandlers,
|
||||
useFactory: (...args: AbstractActionHandler[]) => args.reduce((m, h) => {
|
||||
|
||||
if (Array.isArray(h.action)) {
|
||||
for (const action of h.action) {
|
||||
m[action] = h;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
m[h.action] = h;
|
||||
return m;
|
||||
}, {} as Record<Action, AbstractActionHandler>),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { Injectable } from '@nestjs/common';
|
|||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import * as Joi from 'joi';
|
||||
import { IamService } from './iam.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
PolicyArn: string;
|
||||
|
|
@ -12,7 +14,7 @@ type QueryParams = {
|
|||
export class AttachRolePolicyHandler extends AbstractActionHandler<QueryParams> {
|
||||
|
||||
constructor(
|
||||
|
||||
private readonly iamService: IamService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
|
@ -24,8 +26,12 @@ export class AttachRolePolicyHandler extends AbstractActionHandler<QueryParams>
|
|||
RoleName: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ PolicyArn, RoleName }: QueryParams, awsProperties: AwsProperties) {
|
||||
|
||||
protected async handle({ PolicyArn, RoleName }: QueryParams, context: RequestContext) {
|
||||
|
||||
await this.iamService.attachPolicyToRoleName(
|
||||
context.awsProperties.accountId,
|
||||
PolicyArn,
|
||||
RoleName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Action } from '../action.enum';
|
|||
import * as Joi from 'joi';
|
||||
import { IamPolicy } from './iam-policy.entity';
|
||||
import { breakdownArn } from '../util/breakdown-arn';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
PolicyArn: string;
|
||||
|
|
@ -27,7 +28,7 @@ export class CreatePolicyVersionHandler extends AbstractActionHandler<QueryParam
|
|||
SetAsDefault: Joi.boolean().required(),
|
||||
});
|
||||
|
||||
protected async handle({ PolicyArn, PolicyDocument, SetAsDefault }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ PolicyArn, PolicyDocument, SetAsDefault }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { AbstractActionHandler, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import * as Joi from 'joi';
|
||||
import { IamPolicy } from './iam-policy.entity';
|
||||
import { IamService } from './iam.service';
|
||||
import { TagsService } from '../aws-shared-entities/tags.service';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
Description: string;
|
||||
|
|
@ -33,7 +33,7 @@ export class CreatePolicyHandler extends AbstractActionHandler<QueryParams> {
|
|||
PolicyName: Joi.string().min(1).max(128).required(),
|
||||
});
|
||||
|
||||
protected async handle(params: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle(params: QueryParams, context: RequestContext) {
|
||||
|
||||
const { Description, Path, PolicyName, PolicyDocument } = params;
|
||||
|
||||
|
|
@ -45,9 +45,11 @@ export class CreatePolicyHandler extends AbstractActionHandler<QueryParams> {
|
|||
path: Path,
|
||||
description: Description,
|
||||
policy: PolicyDocument,
|
||||
accountId: awsProperties.accountId,
|
||||
accountId: context.awsProperties.accountId,
|
||||
});
|
||||
|
||||
return policy.metadata;
|
||||
return {
|
||||
Policy: policy.metadata
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Action } from '../action.enum';
|
|||
import * as Joi from 'joi';
|
||||
import { IamService } from './iam.service';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
RoleName: string;
|
||||
|
|
@ -32,7 +33,7 @@ export class CreateRoleHandler extends AbstractActionHandler<QueryParams> {
|
|||
RoleName: Joi.string().min(1).max(64).required(),
|
||||
});
|
||||
|
||||
protected async handle({ RoleName, Path, AssumeRolePolicyDocument, MaxSessionDuration, Description }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ RoleName, Path, AssumeRolePolicyDocument, MaxSessionDuration, Description }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const role = await this.iamService.createRole({
|
||||
id: randomUUID(),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Action } from '../action.enum';
|
|||
import * as Joi from 'joi';
|
||||
import { IamService } from './iam.service';
|
||||
import { NotFoundException } from '../aws-shared-entities/aws-exceptions';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
RoleName: string;
|
||||
|
|
@ -24,7 +25,7 @@ export class DeleteRoleHandler extends AbstractActionHandler<QueryParams> {
|
|||
RoleName: Joi.string().min(1).max(64).required(),
|
||||
});
|
||||
|
||||
protected async handle({ RoleName }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ RoleName }: QueryParams, { awsProperties} : RequestContext) {
|
||||
await this.iamService.deleteRoleByName(awsProperties.accountId, RoleName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { Injectable } from '@nestjs/common';
|
|||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import * as Joi from 'joi';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
import { IamService } from './iam.service';
|
||||
|
||||
type QueryParams = {
|
||||
PolicyArn: string;
|
||||
|
|
@ -12,7 +14,7 @@ type QueryParams = {
|
|||
export class GetPolicyVersionHandler extends AbstractActionHandler<QueryParams> {
|
||||
|
||||
constructor(
|
||||
|
||||
private readonly iamService: IamService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
|
@ -24,7 +26,17 @@ export class GetPolicyVersionHandler extends AbstractActionHandler<QueryParams>
|
|||
VersionId: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ PolicyArn, VersionId }: QueryParams, awsProperties: AwsProperties) {
|
||||
|
||||
protected async handle({ PolicyArn, VersionId }: QueryParams, { awsProperties} : RequestContext) {
|
||||
const maybeVersion = Number(VersionId);
|
||||
const version = Number.isNaN(maybeVersion) ? Number(VersionId.toLowerCase().split('v')[1]) : Number(maybeVersion);
|
||||
const policy = await this.iamService.getPolicyByArnAndVersion(PolicyArn, version);
|
||||
return {
|
||||
PolicyVersion: {
|
||||
Document: policy.policy,
|
||||
IsDefaultVersion: policy.isDefault,
|
||||
VersionId: policy.version,
|
||||
CreateDate: policy.createdAt.toISOString(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { Injectable } from '@nestjs/common';
|
|||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import * as Joi from 'joi';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
import { IamService } from './iam.service';
|
||||
|
||||
type QueryParams = {
|
||||
PolicyArn: string;
|
||||
|
|
@ -11,7 +13,7 @@ type QueryParams = {
|
|||
export class GetPolicyHandler extends AbstractActionHandler<QueryParams> {
|
||||
|
||||
constructor(
|
||||
|
||||
private readonly iamService: IamService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
|
@ -22,7 +24,10 @@ export class GetPolicyHandler extends AbstractActionHandler<QueryParams> {
|
|||
PolicyArn: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ PolicyArn }: QueryParams, awsProperties: AwsProperties) {
|
||||
|
||||
protected async handle({ PolicyArn }: QueryParams, { awsProperties} : RequestContext) {
|
||||
const policy = await this.iamService.getPolicyByArn(PolicyArn);
|
||||
return {
|
||||
Policy: policy.metadata,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Action } from '../action.enum';
|
|||
import * as Joi from 'joi';
|
||||
import { IamService } from './iam.service';
|
||||
import { NotFoundException } from '../aws-shared-entities/aws-exceptions';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
RoleName: string;
|
||||
|
|
@ -24,14 +25,8 @@ export class GetRoleHandler extends AbstractActionHandler<QueryParams> {
|
|||
RoleName: Joi.string().min(1).max(64).required(),
|
||||
});
|
||||
|
||||
protected async handle({ RoleName }: QueryParams, awsProperties: AwsProperties) {
|
||||
|
||||
protected async handle({ RoleName }: QueryParams, { awsProperties} : RequestContext) {
|
||||
const role = await this.iamService.findOneRoleByName(awsProperties.accountId, RoleName);
|
||||
|
||||
if (!role) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return {
|
||||
Role: role.metadata,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,19 @@ import { IAMHandlers } from './iam.constants';
|
|||
import { PrismaModule } from '../_prisma/prisma.module';
|
||||
import { IamService } from './iam.service';
|
||||
import { GetRoleHandler } from './get-role.handler';
|
||||
import { GetPolicyHandler } from './get-policy.handler';
|
||||
import { GetPolicyVersionHandler } from './get-policy-version.handler';
|
||||
import { AttachRolePolicyHandler } from './attach-role-policy.handler';
|
||||
import { ListAttachedRolePoliciesHandler } from './list-attached-role-policies';
|
||||
|
||||
const handlers = [
|
||||
AttachRolePolicyHandler,
|
||||
CreatePolicyHandler,
|
||||
CreateRoleHandler,
|
||||
GetPolicyVersionHandler,
|
||||
GetPolicyHandler,
|
||||
GetRoleHandler,
|
||||
ListAttachedRolePoliciesHandler,
|
||||
]
|
||||
|
||||
const actions = [
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import { PrismaService } from "../_prisma/prisma.service";
|
|||
import { Prisma } from "@prisma/client";
|
||||
import { IamPolicy } from "./iam-policy.entity";
|
||||
import { IamRole } from "./iam-role.entity";
|
||||
import { EntityAlreadyExists } from "../aws-shared-entities/aws-exceptions";
|
||||
import { EntityAlreadyExists, NoSuchEntity, NotFoundException } from "../aws-shared-entities/aws-exceptions";
|
||||
import { ArnUtil } from "../util/arn-util.static";
|
||||
|
||||
@Injectable()
|
||||
export class IamService {
|
||||
|
|
@ -22,15 +23,18 @@ export class IamService {
|
|||
}
|
||||
}
|
||||
|
||||
async findOneRoleByName(accountId: string, name: string): Promise<IamRole | null> {
|
||||
const record = await this.prismaService.iamRole.findFirst({
|
||||
where: {
|
||||
name,
|
||||
accountId,
|
||||
}
|
||||
});
|
||||
|
||||
return record ? new IamRole(record) : null;
|
||||
async findOneRoleByName(accountId: string, name: string): Promise<IamRole> {
|
||||
try {
|
||||
const record = await this.prismaService.iamRole.findFirstOrThrow({
|
||||
where: {
|
||||
name,
|
||||
accountId,
|
||||
}
|
||||
});
|
||||
return new IamRole(record);
|
||||
} catch (error) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
async deleteRoleByName(accountId: string, name: string) {
|
||||
|
|
@ -42,8 +46,84 @@ export class IamService {
|
|||
});
|
||||
}
|
||||
|
||||
async createPolicy(data: Prisma.IamPolicyCreateInput): Promise<IamPolicy> {
|
||||
const record = await this.prismaService.iamPolicy.create({ data });
|
||||
return new IamPolicy(record);
|
||||
async listRolePolicies(): Promise<IamPolicy[]> {
|
||||
// return await this.prismaService;
|
||||
return [];
|
||||
}
|
||||
|
||||
async getPolicyByArn(arn: string): Promise<IamPolicy> {
|
||||
try {
|
||||
const name = arn.split('/')[1];
|
||||
const record = await this.prismaService.iamPolicy.findFirstOrThrow({
|
||||
where: {
|
||||
name,
|
||||
},
|
||||
orderBy: {
|
||||
version: 'desc',
|
||||
},
|
||||
});
|
||||
return new IamPolicy(record);
|
||||
} catch (err) {
|
||||
throw new NoSuchEntity();
|
||||
}
|
||||
}
|
||||
|
||||
async getPolicyByArnAndVersion(arn: string, version: number): Promise<IamPolicy> {
|
||||
try {
|
||||
const name = arn.split('/')[1];
|
||||
const record = await this.prismaService.iamPolicy.findFirstOrThrow({
|
||||
where: {
|
||||
name,
|
||||
version,
|
||||
}
|
||||
});
|
||||
return new IamPolicy(record);
|
||||
} catch (err) {
|
||||
throw new NoSuchEntity();
|
||||
}
|
||||
}
|
||||
|
||||
async createPolicy(data: Prisma.IamPolicyCreateInput): Promise<IamPolicy> {
|
||||
try {
|
||||
const record = await this.prismaService.iamPolicy.create({ data });
|
||||
return new IamPolicy(record);
|
||||
} catch (err) {
|
||||
throw new EntityAlreadyExists(`PolicyName ${data.name} already exists`);
|
||||
}
|
||||
}
|
||||
|
||||
async attachPolicyToRoleName(accountId: string, arn: string, roleName: string) {
|
||||
const policy = await this.getPolicyByArn(arn);
|
||||
const role = await this.findOneRoleByName(accountId, roleName);
|
||||
await this.prismaService.iamRoleIamPolicyAttachment.create({
|
||||
data: {
|
||||
iamPolicyId: policy.id,
|
||||
iamRoleId: role.id,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async findAttachedRolePoliciesByRoleName(accountId: string, roleName: string): Promise<IamPolicy[]> {
|
||||
try {
|
||||
const record = await this.prismaService.iamRole.findFirstOrThrow({
|
||||
where: {
|
||||
name: roleName,
|
||||
accountId,
|
||||
},
|
||||
include: {
|
||||
policies: true,
|
||||
}
|
||||
});
|
||||
const policyIds = record.policies.map(p => p.iamPolicyId);
|
||||
const policies = await this.prismaService.iamPolicy.findMany({ where: {
|
||||
id: {
|
||||
in: policyIds,
|
||||
},
|
||||
isDefault: true,
|
||||
}});
|
||||
return policies.map(p => new IamPolicy(p));
|
||||
} catch (error) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { Injectable } from '@nestjs/common';
|
|||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import * as Joi from 'joi';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
import { IamService } from './iam.service';
|
||||
|
||||
type QueryParams = {
|
||||
RoleName: string;
|
||||
|
|
@ -11,6 +13,7 @@ type QueryParams = {
|
|||
export class ListAttachedRolePoliciesHandler extends AbstractActionHandler<QueryParams> {
|
||||
|
||||
constructor(
|
||||
private readonly iamService: IamService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
|
@ -21,8 +24,15 @@ export class ListAttachedRolePoliciesHandler extends AbstractActionHandler<Query
|
|||
RoleName: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ RoleName }: QueryParams, awsProperties: AwsProperties) {
|
||||
|
||||
|
||||
protected async handle({ RoleName }: QueryParams, { awsProperties} : RequestContext) {
|
||||
const policies = await this.iamService.findAttachedRolePoliciesByRoleName(awsProperties.accountId, RoleName);
|
||||
return {
|
||||
AttachedPolicies: policies.map(p => ({
|
||||
member: {
|
||||
PolicyName: p.name,
|
||||
PolicyArn: p.arn,
|
||||
}
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
|||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import * as Joi from 'joi';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
Marker: string;
|
||||
|
|
@ -25,7 +26,8 @@ export class ListRolePoliciesHandler extends AbstractActionHandler<QueryParams>
|
|||
RoleName: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ RoleName }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ RoleName }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Action } from '../action.enum';
|
|||
import * as Joi from 'joi';
|
||||
import { KmsService } from './kms.service';
|
||||
import { NotFoundException } from '../aws-shared-entities/aws-exceptions';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
AliasName: string;
|
||||
|
|
@ -26,7 +27,7 @@ export class CreateAliasHandler extends AbstractActionHandler<QueryParams> {
|
|||
AliasName: Joi.string().min(1).max(256).regex(new RegExp(`^alias/[a-zA-Z0-9/_-]+$`)).required(),
|
||||
});
|
||||
|
||||
protected async handle({ TargetKeyId, AliasName }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ TargetKeyId, AliasName }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const keyRecord = await this.kmsService.findOneByRef(TargetKeyId, awsProperties);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import * as crypto from 'crypto';
|
|||
import { keySpecToUsageType } from './kms-key.entity';
|
||||
import { UnsupportedOperationException } from '../aws-shared-entities/aws-exceptions';
|
||||
import { TagsService } from '../aws-shared-entities/tags.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type NoUndefinedField<T> = { [P in keyof T]-?: NoUndefinedField<NonNullable<T[P]>> };
|
||||
|
||||
|
|
@ -71,7 +72,7 @@ export class CreateKeyHandler extends AbstractActionHandler<QueryParams> {
|
|||
}) as unknown as Joi.StringSchema,
|
||||
});
|
||||
|
||||
protected async handle({ KeyUsage, Description, KeySpec, Origin, MultiRegion, Policy, Tags, CustomerMasterKeySpec }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ KeyUsage, Description, KeySpec, Origin, MultiRegion, Policy, Tags, CustomerMasterKeySpec }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const keySpec = CustomerMasterKeySpec ?? KeySpec;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Action } from '../action.enum';
|
|||
import * as Joi from 'joi';
|
||||
import { KmsService } from './kms.service';
|
||||
import { NotFoundException } from '../aws-shared-entities/aws-exceptions';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
GrantTokens?: string[];
|
||||
|
|
@ -26,7 +27,7 @@ export class DescribeKeyHandler extends AbstractActionHandler<QueryParams> {
|
|||
GrantTokens: Joi.array().items(Joi.string()),
|
||||
});
|
||||
|
||||
protected async handle({ KeyId }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ KeyId }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const keyRecord = await this.kmsService.findOneByRef(KeyId, awsProperties);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Action } from '../action.enum';
|
|||
import * as Joi from 'joi';
|
||||
import { KmsService } from './kms.service';
|
||||
import { NotFoundException } from '../aws-shared-entities/aws-exceptions';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
KeyId: string;
|
||||
|
|
@ -26,9 +27,9 @@ export class EnableKeyRotationHandler extends AbstractActionHandler<QueryParams>
|
|||
RotationPeriodInDays: Joi.number().min(90).max(2560).default(365),
|
||||
});
|
||||
|
||||
protected async handle({ KeyId, RotationPeriodInDays }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ KeyId, RotationPeriodInDays }: QueryParams, context: RequestContext) {
|
||||
|
||||
const keyRecord = await this.kmsService.findOneByRef(KeyId, awsProperties);
|
||||
const keyRecord = await this.kmsService.findOneByRef(KeyId, context.awsProperties);
|
||||
|
||||
if (!keyRecord) {
|
||||
throw new NotFoundException();
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Action } from '../action.enum';
|
|||
import * as Joi from 'joi';
|
||||
import { KmsService } from './kms.service';
|
||||
import { NotFoundException } from '../aws-shared-entities/aws-exceptions';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
PolicyName: string;
|
||||
|
|
@ -26,9 +27,9 @@ export class GetKeyPolicyHandler extends AbstractActionHandler<QueryParams> {
|
|||
PolicyName: Joi.string().min(1).max(128).default('default'),
|
||||
});
|
||||
|
||||
protected async handle({ KeyId, PolicyName }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ KeyId, PolicyName }: QueryParams, context: RequestContext) {
|
||||
|
||||
const keyRecord = await this.kmsService.findOneByRef(KeyId, awsProperties);
|
||||
const keyRecord = await this.kmsService.findOneByRef(KeyId, context.awsProperties);
|
||||
|
||||
if (!keyRecord) {
|
||||
throw new NotFoundException();
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Action } from '../action.enum';
|
|||
import * as Joi from 'joi';
|
||||
import { KmsService } from './kms.service';
|
||||
import { NotFoundException } from '../aws-shared-entities/aws-exceptions';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
KeyId: string;
|
||||
|
|
@ -24,7 +25,7 @@ export class GetKeyRotationStatusHandler extends AbstractActionHandler<QueryPara
|
|||
KeyId: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ KeyId }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ KeyId }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const keyRecord = await this.kmsService.findOneByRef(KeyId, awsProperties);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import * as Joi from 'joi';
|
||||
import { KmsService } from './kms.service';
|
||||
import { NotFoundException } from '../aws-shared-entities/aws-exceptions';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
KeyId: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class GetPublicKeyHandler extends AbstractActionHandler<QueryParams> {
|
||||
|
||||
constructor(
|
||||
private readonly kmsService: KmsService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
format = Format.Json;
|
||||
action = Action.KmsGetPublicKey;
|
||||
validator = Joi.object<QueryParams, true>({
|
||||
KeyId: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ KeyId }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const keyRecord = await this.kmsService.findOneByRef(KeyId, awsProperties);
|
||||
|
||||
if (!keyRecord) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return {
|
||||
...keyRecord.metadata,
|
||||
PublicKey: Buffer.from(keyRecord.keyPair.publicKey).toString('base64'),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -58,6 +58,10 @@ export class KmsKey implements PrismaKmsKey {
|
|||
get arn() {
|
||||
return `arn:aws:kms:${this.region}:${this.accountId}:key/${this.id}`;
|
||||
}
|
||||
|
||||
get keyPair(): { publicKey: string; privateKey: string } {
|
||||
return JSON.parse(Buffer.from(this.key).toString('utf-8'));
|
||||
}
|
||||
|
||||
get metadata() {
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import { GetKeyRotationStatusHandler } from './get-key-rotation-status.handler';
|
|||
import { GetKeyPolicyHandler } from './get-key-policy.handler';
|
||||
import { ListResourceTagsHandler } from './list-resource-tags.handler';
|
||||
import { CreateAliasHandler } from './create-alias.handler';
|
||||
import { GetPublicKeyHandler } from './get-public-key.handler';
|
||||
import { SignHandler } from './sign.handler';
|
||||
|
||||
const handlers = [
|
||||
CreateAliasHandler,
|
||||
|
|
@ -24,8 +26,10 @@ const handlers = [
|
|||
EnableKeyRotationHandler,
|
||||
GetKeyPolicyHandler,
|
||||
GetKeyRotationStatusHandler,
|
||||
GetPublicKeyHandler,
|
||||
ListAliasesHandler,
|
||||
ListResourceTagsHandler,
|
||||
SignHandler,
|
||||
]
|
||||
|
||||
const actions = [
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { KmsKey } from './kms-key.entity';
|
|||
import { KmsAlias } from './kms-alias.entity';
|
||||
import { AwsProperties } from '../abstract-action.handler';
|
||||
import { NotFoundException } from '../aws-shared-entities/aws-exceptions';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
@Injectable()
|
||||
export class KmsService {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import * as Joi from 'joi';
|
|||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import { KmsService } from './kms.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
KeyId?: string;
|
||||
|
|
@ -28,7 +29,7 @@ export class ListAliasesHandler extends AbstractActionHandler<QueryParams> {
|
|||
Marker: Joi.string(),
|
||||
});
|
||||
|
||||
protected async handle({ KeyId, Limit, Marker }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ KeyId, Limit, Marker }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const records = await (KeyId
|
||||
? this.kmsService.findAndCountAliasesByKeyId(awsProperties.accountId, awsProperties.region, Limit, KeyId, Marker)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import * as Joi from 'joi';
|
|||
import { KmsService } from './kms.service';
|
||||
import { NotFoundException } from '../aws-shared-entities/aws-exceptions';
|
||||
import { TagsService } from '../aws-shared-entities/tags.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
KeyId: string;
|
||||
|
|
@ -30,9 +31,9 @@ export class ListResourceTagsHandler extends AbstractActionHandler<QueryParams>
|
|||
Marker: Joi.string(),
|
||||
});
|
||||
|
||||
protected async handle({ KeyId }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ KeyId }: QueryParams, context: RequestContext) {
|
||||
|
||||
const keyRecord = await this.kmsService.findOneByRef(KeyId, awsProperties);
|
||||
const keyRecord = await this.kmsService.findOneByRef(KeyId, context.awsProperties);
|
||||
|
||||
if (!keyRecord) {
|
||||
throw new NotFoundException();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import * as Joi from 'joi';
|
||||
import { KmsService } from './kms.service';
|
||||
import { NotFoundException, UnsupportedOperationException } from '../aws-shared-entities/aws-exceptions';
|
||||
import * as crypto from 'crypto';
|
||||
import { KeySpec, SigningAlgorithmSpec } from '@aws-sdk/client-kms';
|
||||
import { KmsKey } from './kms-key.entity';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
KeyId: string;
|
||||
Message: string;
|
||||
MessageType: string;
|
||||
SigningAlgorithm: string;
|
||||
}
|
||||
|
||||
const signingAlgorithmToSigningFn: Record<SigningAlgorithmSpec, (base64: string, key: KmsKey) => string> = {
|
||||
ECDSA_SHA_256: function (base64: string): string {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
ECDSA_SHA_384: function (base64: string): string {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
ECDSA_SHA_512: function (base64: string): string {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
RSASSA_PKCS1_V1_5_SHA_256: function (base64: string, key: KmsKey): string {
|
||||
const buffer = Buffer.from(base64);
|
||||
return crypto.sign('sha256WithRSAEncryption', buffer, key.keyPair.privateKey).toString('base64');
|
||||
},
|
||||
RSASSA_PKCS1_V1_5_SHA_384: function (base64: string): string {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
RSASSA_PKCS1_V1_5_SHA_512: function (base64: string): string {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
RSASSA_PSS_SHA_256: function (base64: string): string {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
RSASSA_PSS_SHA_384: function (base64: string): string {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
RSASSA_PSS_SHA_512: function (base64: string): string {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
SM2DSA: function (base64: string): string {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class SignHandler extends AbstractActionHandler<QueryParams> {
|
||||
|
||||
constructor(
|
||||
private readonly kmsService: KmsService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
format = Format.Json;
|
||||
action = Action.KmsSign;
|
||||
validator = Joi.object<QueryParams, true>({
|
||||
KeyId: Joi.string().required(),
|
||||
Message: Joi.string().required(),
|
||||
MessageType: Joi.string().required(),
|
||||
SigningAlgorithm: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ KeyId, Message, SigningAlgorithm }: QueryParams, { awsProperties } : RequestContext) {
|
||||
|
||||
const keyRecord = await this.kmsService.findOneByRef(KeyId, awsProperties);
|
||||
|
||||
if (!keyRecord) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (!(keyRecord.metadata as any).SigningAlgorithms.includes(SigningAlgorithm)) {
|
||||
throw new UnsupportedOperationException('Invalid signing algorithm');
|
||||
}
|
||||
|
||||
const signature = signingAlgorithmToSigningFn[SigningAlgorithm as SigningAlgorithmSpec](Message, keyRecord);
|
||||
|
||||
return {
|
||||
KeyId: keyRecord.arn,
|
||||
Signature: signature,
|
||||
SigningAlgorithm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ Date.prototype.getAwsTime = function (this: Date) {
|
|||
const app = await NestFactory.create(AppModule);
|
||||
// app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
|
||||
app.useGlobalFilters(new AwsExceptionFilter());
|
||||
app.use(bodyParser.json({ type: 'application/x-amz-json-1.0'}));
|
||||
app.use(bodyParser.json({ type: 'application/x-amz-json-1.1'}));
|
||||
|
||||
const configService: ConfigService<CommonConfig, true> = app.get(ConfigService);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action
|
|||
import { Action } from '../action.enum';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { SecretService } from './secret.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
Name: string;
|
||||
|
|
@ -28,11 +29,11 @@ export class CreateSecretHandler extends AbstractActionHandler<QueryParams> {
|
|||
validator = Joi.object<QueryParams, true>({
|
||||
Name: Joi.string().required(),
|
||||
Description: Joi.string().allow('', null),
|
||||
SecretString: Joi.string().allow('', null),
|
||||
SecretString: Joi.string().allow('', null).default(''),
|
||||
ClientRequestToken: Joi.string(),
|
||||
});
|
||||
|
||||
protected async handle(params: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle(params: QueryParams, context: RequestContext) {
|
||||
|
||||
const { Name: name, Description: description, SecretString: secretString, ClientRequestToken } = params;
|
||||
|
||||
|
|
@ -41,8 +42,8 @@ export class CreateSecretHandler extends AbstractActionHandler<QueryParams> {
|
|||
description,
|
||||
name,
|
||||
secretString,
|
||||
accountId: awsProperties.accountId,
|
||||
region: awsProperties.region,
|
||||
accountId: context.awsProperties.accountId,
|
||||
region: context.awsProperties.region,
|
||||
});
|
||||
|
||||
const arn = ArnUtil.fromSecret(secret);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action
|
|||
import { Action } from '../action.enum';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { SecretService } from './secret.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
SecretId: string;
|
||||
|
|
@ -29,7 +30,7 @@ export class DeleteSecretHandler extends AbstractActionHandler {
|
|||
VersionId: Joi.string().allow(null, ''),
|
||||
});
|
||||
|
||||
protected async handle({ SecretId, VersionId }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ SecretId, VersionId }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const name = ArnUtil.getSecretNameFromSecretId(SecretId);
|
||||
const secret = VersionId ?
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import * as Joi from 'joi';
|
||||
|
||||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
|
|
@ -6,6 +6,8 @@ import { Action } from '../action.enum';
|
|||
import { TagsService } from '../aws-shared-entities/tags.service';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { SecretService } from './secret.service';
|
||||
import { NotFoundException } from '../aws-shared-entities/aws-exceptions';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
SecretId: string;
|
||||
|
|
@ -25,13 +27,13 @@ export class DescribeSecretHandler extends AbstractActionHandler {
|
|||
action = Action.SecretsManagerDescribeSecret;
|
||||
validator = Joi.object<QueryParams, true>({ SecretId: Joi.string().required() });
|
||||
|
||||
protected async handle({ SecretId }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ SecretId }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const name = ArnUtil.getSecretNameFromSecretId(SecretId);
|
||||
const secret = await this.secretService.findLatestByNameAndRegion(name, awsProperties.region);
|
||||
|
||||
if (!secret) {
|
||||
throw new BadRequestException('ResourceNotFoundException', "Secrets Manager can't find the resource that you asked for.");
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
const arn = ArnUtil.fromSecret(secret);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { Action } from '../action.enum';
|
|||
import { AttributesService } from '../aws-shared-entities/attributes.service';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { SecretService } from './secret.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
SecretId: string;
|
||||
|
|
@ -25,7 +26,7 @@ export class GetResourcePolicyHandler extends AbstractActionHandler {
|
|||
action = Action.SecretsManagerGetResourcePolicy;
|
||||
validator = Joi.object<QueryParams, true>({ SecretId: Joi.string().required() });
|
||||
|
||||
protected async handle({ SecretId }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ SecretId }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const name = ArnUtil.getSecretNameFromSecretId(SecretId);
|
||||
const secret = await this.secretService.findLatestByNameAndRegion(name, awsProperties.region);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action
|
|||
import { Action } from '../action.enum';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { SecretService } from './secret.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
SecretId: string;
|
||||
|
|
@ -27,7 +28,7 @@ export class GetSecretValueHandler extends AbstractActionHandler {
|
|||
VersionId: Joi.string().allow(null, ''),
|
||||
});
|
||||
|
||||
protected async handle({ SecretId, VersionId}: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ SecretId, VersionId}: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const name = ArnUtil.getSecretNameFromSecretId(SecretId);
|
||||
const secret = VersionId ?
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { Action } from '../action.enum';
|
|||
import { AttributesService } from '../aws-shared-entities/attributes.service';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { SecretService } from './secret.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
SecretId: string;
|
||||
|
|
@ -29,10 +30,10 @@ export class PutResourcePolicyHandler extends AbstractActionHandler {
|
|||
ResourcePolicy: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ SecretId, ResourcePolicy }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ SecretId, ResourcePolicy }: QueryParams, context: RequestContext) {
|
||||
|
||||
const name = ArnUtil.getSecretNameFromSecretId(SecretId);
|
||||
const secret = await this.secretService.findLatestByNameAndRegion(name, awsProperties.region);
|
||||
const secret = await this.secretService.findLatestByNameAndRegion(name, context.awsProperties.region);
|
||||
|
||||
if (!secret) {
|
||||
throw new BadRequestException('ResourceNotFoundException', "Secrets Manager can't find the resource that you asked for.");
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action
|
|||
import { Action } from '../action.enum';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { SecretService } from './secret.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
ClientRequestToken?: string;
|
||||
|
|
@ -30,11 +31,11 @@ export class PutSecretValueHandler extends AbstractActionHandler<QueryParams> {
|
|||
SecretString: Joi.string(),
|
||||
});
|
||||
|
||||
protected async handle(params: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle(params: QueryParams, context: RequestContext) {
|
||||
|
||||
const { SecretId, SecretString: secretString, ClientRequestToken } = params;
|
||||
const name = ArnUtil.getSecretNameFromSecretId(SecretId);
|
||||
const oldSecret = await this.secretService.findLatestByNameAndRegion(name, awsProperties.region);
|
||||
const oldSecret = await this.secretService.findLatestByNameAndRegion(name, context.awsProperties.region);
|
||||
|
||||
if (!oldSecret) {
|
||||
throw new BadRequestException('ResourceNotFoundException', "Secrets Manager can't find the resource that you asked for.");
|
||||
|
|
@ -44,8 +45,8 @@ export class PutSecretValueHandler extends AbstractActionHandler<QueryParams> {
|
|||
versionId: ClientRequestToken ?? randomUUID(),
|
||||
name: oldSecret.name,
|
||||
secretString,
|
||||
accountId: awsProperties.accountId,
|
||||
region: awsProperties.region,
|
||||
accountId: context.awsProperties.accountId,
|
||||
region: context.awsProperties.region,
|
||||
});
|
||||
|
||||
const arn = ArnUtil.fromSecret(secret);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action
|
|||
import { Action } from '../action.enum';
|
||||
import { TagsService } from '../aws-shared-entities/tags.service';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
Name: string;
|
||||
|
|
@ -25,15 +26,15 @@ export class CreateTopicHandler extends AbstractActionHandler<QueryParams> {
|
|||
action = Action.SnsCreateTopic;
|
||||
validator = Joi.object<QueryParams, true>({ Name: Joi.string().required() });
|
||||
|
||||
protected async handle(params: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle(params: QueryParams, context: RequestContext) {
|
||||
|
||||
const { Name: name } = params;
|
||||
|
||||
const topic = await this.prismaService.snsTopic.create({
|
||||
data: {
|
||||
name,
|
||||
accountId: awsProperties.accountId,
|
||||
region: awsProperties.region,
|
||||
accountId: context.awsProperties.accountId,
|
||||
region: context.awsProperties.region,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action
|
|||
import { Action } from '../action.enum';
|
||||
import { AttributesService } from '../aws-shared-entities/attributes.service';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
SubscriptionArn: string;
|
||||
|
|
@ -25,7 +26,7 @@ export class GetSubscriptionAttributesHandler extends AbstractActionHandler {
|
|||
action = Action.SnsGetSubscriptionAttributes;
|
||||
validator = Joi.object<QueryParams, true>({ SubscriptionArn: Joi.string().required() });
|
||||
|
||||
protected async handle({ SubscriptionArn }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ SubscriptionArn }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const id = SubscriptionArn.split(':').pop();
|
||||
const subscription = await this.prismaService.snsTopicSubscription.findFirst({ where: { id }});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action
|
|||
import { Action } from '../action.enum';
|
||||
import { AttributesService } from '../aws-shared-entities/attributes.service';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
TopicArn: string;
|
||||
|
|
@ -25,7 +26,7 @@ export class GetTopicAttributesHandler extends AbstractActionHandler {
|
|||
action = Action.SnsGetTopicAttributes;
|
||||
validator = Joi.object<QueryParams, true>({ TopicArn: Joi.string().required() });
|
||||
|
||||
protected async handle({ TopicArn }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ TopicArn }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const name = TopicArn.split(':').pop();
|
||||
const topic = await this.prismaService.snsTopic.findFirst({ where: { name }});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import * as Joi from 'joi';
|
|||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import { TagsService } from '../aws-shared-entities/tags.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
ResourceArn: string;
|
||||
|
|
@ -22,7 +23,7 @@ export class ListTagsForResourceHandler extends AbstractActionHandler {
|
|||
action = Action.SnsListTagsForResource;
|
||||
validator = Joi.object<QueryParams, true>({ ResourceArn: Joi.string().required() });
|
||||
|
||||
protected async handle({ ResourceArn }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ ResourceArn }: QueryParams, { awsProperties} : RequestContext) {
|
||||
const tags = await this.tagsService.getByArn(ResourceArn);
|
||||
return TagsService.getXmlSafeTagsMap(tags);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { PrismaService } from '../_prisma/prisma.service';
|
|||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
NextToken: number;
|
||||
|
|
@ -23,7 +24,7 @@ export class ListTopicsHandler extends AbstractActionHandler {
|
|||
action = Action.SnsListTopics;
|
||||
validator = Joi.object<QueryParams, true>({ NextToken: Joi.number().default(0) });
|
||||
|
||||
protected async handle({ NextToken: skip }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ NextToken: skip }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const [ topics, total ] = await Promise.all([
|
||||
this.prismaService.snsTopic.findMany({ orderBy: { name: 'desc' }, take: 100, skip }),
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { AttributesService } from '../aws-shared-entities/attributes.service';
|
|||
import { SqsQueueEntryService } from '../sqs/sqs-queue-entry.service';
|
||||
import { SqsQueue } from '../sqs/sqs-queue.entity';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
TopicArn: string;
|
||||
|
|
@ -37,7 +38,7 @@ export class PublishHandler extends AbstractActionHandler<QueryParams> {
|
|||
Message: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ TopicArn, TargetArn, Message, Subject }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ TopicArn, TargetArn, Message, Subject }: QueryParams, context: RequestContext) {
|
||||
const arn = TopicArn ?? TargetArn;
|
||||
|
||||
if (!arn) {
|
||||
|
|
@ -65,7 +66,7 @@ export class PublishHandler extends AbstractActionHandler<QueryParams> {
|
|||
SignatureVersion: topicAttributes.find(a => a.name === 'SignatureVersion')?.value ?? '1',
|
||||
Signature: '',
|
||||
SigningCertURL: '',
|
||||
UnsubscribeURL: `${awsProperties.host}/?Action=Unsubscribe&SubscriptionArn=${subArn}`,
|
||||
UnsubscribeURL: `${context.awsProperties.host}/?Action=Unsubscribe&SubscriptionArn=${subArn}`,
|
||||
});
|
||||
|
||||
await this.sqsQueueEntryService.publish(queueAccountId, queueName, message);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import * as Joi from 'joi';
|
|||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import { AttributesService } from '../aws-shared-entities/attributes.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
AttributeName: string;
|
||||
|
|
@ -28,7 +29,7 @@ export class SetSubscriptionAttributesHandler extends AbstractActionHandler<Quer
|
|||
TopicArn: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ AttributeName, AttributeValue, TopicArn }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ AttributeName, AttributeValue, TopicArn }: QueryParams, { awsProperties} : RequestContext) {
|
||||
await this.attributeService.create({ name: AttributeName, value: AttributeValue, arn: TopicArn });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import * as Joi from 'joi';
|
|||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import { AttributesService } from '../aws-shared-entities/attributes.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
AttributeName: string;
|
||||
|
|
@ -28,7 +29,7 @@ export class SetTopicAttributesHandler extends AbstractActionHandler<QueryParams
|
|||
TopicArn: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ AttributeName, AttributeValue, TopicArn }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ AttributeName, AttributeValue, TopicArn }: QueryParams, { awsProperties} : RequestContext) {
|
||||
await this.attributeService.create({ name: AttributeName, value: AttributeValue, arn: TopicArn });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@ import { randomUUID } from 'crypto';
|
|||
import * as Joi from 'joi';
|
||||
|
||||
import { PrismaService } from '../_prisma/prisma.service';
|
||||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { AbstractActionHandler, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import { AttributesService } from '../aws-shared-entities/attributes.service';
|
||||
import { TagsService } from '../aws-shared-entities/tags.service';
|
||||
import { ArnUtil } from '../util/arn-util.static';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
TopicArn: string;
|
||||
|
|
@ -34,7 +35,7 @@ export class SubscribeHandler extends AbstractActionHandler<QueryParams> {
|
|||
Protocol: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle(params: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle(params: QueryParams, context: RequestContext) {
|
||||
|
||||
const subscription = await this.prismaService.snsTopicSubscription.create({
|
||||
data: {
|
||||
|
|
@ -42,8 +43,8 @@ export class SubscribeHandler extends AbstractActionHandler<QueryParams> {
|
|||
topicArn: params.TopicArn,
|
||||
protocol: params.Protocol,
|
||||
endpoint: params.Endpoint,
|
||||
accountId: awsProperties.accountId,
|
||||
region: awsProperties.region,
|
||||
accountId: context.awsProperties.accountId,
|
||||
region: context.awsProperties.region,
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export class UnsubscribeHandler extends AbstractActionHandler<QueryParams> {
|
|||
SubscriptionArn: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle(params: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle(params: QueryParams) {
|
||||
|
||||
const id = params.SubscriptionArn.split(':').pop();
|
||||
const subscription = await this.prismaService.snsTopicSubscription.findFirst({ where: { id } });
|
||||
|
|
|
|||
|
|
@ -1,48 +1,18 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import * as Joi from 'joi';
|
||||
|
||||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import { AttributesService } from '../aws-shared-entities/attributes.service';
|
||||
import { TagsService } from '../aws-shared-entities/tags.service';
|
||||
import { SqsQueueEntryService } from './sqs-queue-entry.service';
|
||||
import { SqsQueue } from './sqs-queue.entity';
|
||||
import { V2CreateQueueHandler } from './v2-create-queue.handler';
|
||||
|
||||
type QueryParams = {
|
||||
QueueName: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateQueueHandler extends AbstractActionHandler<QueryParams> {
|
||||
|
||||
constructor(
|
||||
private readonly sqsQueueEntryService: SqsQueueEntryService,
|
||||
private readonly tagsService: TagsService,
|
||||
private readonly attributeService: AttributesService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
export class CreateQueueHandler extends V2CreateQueueHandler {
|
||||
|
||||
format = Format.Xml;
|
||||
action = Action.SqsCreateQueue;
|
||||
validator = Joi.object<QueryParams, true>({ QueueName: Joi.string().required() });
|
||||
|
||||
protected async handle(params: QueryParams, awsProperties: AwsProperties) {
|
||||
|
||||
const { QueueName: name } = params;
|
||||
|
||||
const queue = await this.sqsQueueEntryService.createQueue({
|
||||
name,
|
||||
accountId: awsProperties.accountId,
|
||||
region: awsProperties.region,
|
||||
});
|
||||
|
||||
const tags = TagsService.tagPairs(params);
|
||||
await this.tagsService.createMany(queue.arn, tags);
|
||||
|
||||
const attributes = SqsQueue.attributePairs(params);
|
||||
await this.attributeService.createMany(queue.arn, attributes);
|
||||
|
||||
return { QueueUrl: queue.getUrl(awsProperties.host) };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action
|
|||
import { Action } from '../action.enum';
|
||||
import { SqsQueueEntryService } from './sqs-queue-entry.service';
|
||||
import { SqsQueue } from './sqs-queue.entity';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
QueueUrl: string;
|
||||
|
|
@ -26,7 +27,7 @@ export class DeleteMessageBatchHandler extends AbstractActionHandler<QueryParams
|
|||
QueueUrl: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle( params : QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle( params : QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const { QueueUrl } = params;
|
||||
const [accountId, name] = SqsQueue.tryGetAccountIdAndNameFromPathOrArn(QueueUrl);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action
|
|||
import { Action } from '../action.enum';
|
||||
import { SqsQueueEntryService } from './sqs-queue-entry.service';
|
||||
import { SqsQueue } from './sqs-queue.entity';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
QueueUrl: string;
|
||||
|
|
@ -28,7 +29,7 @@ export class DeleteMessageHandler extends AbstractActionHandler<QueryParams> {
|
|||
ReceiptHandle: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle({ QueueUrl, ReceiptHandle }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ QueueUrl, ReceiptHandle }: QueryParams, { awsProperties} : RequestContext) {
|
||||
await this.sqsQueueEntryService.deleteMessage(ReceiptHandle);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export class DeleteQueueHandler extends AbstractActionHandler<QueryParams> {
|
|||
__path: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle(params: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle(params: QueryParams) {
|
||||
|
||||
const [accountId, name] = SqsQueue.tryGetAccountIdAndNameFromPathOrArn(params.QueueUrl ?? params.__path);
|
||||
const queue = await this.sqsQueueEntryService.findQueueByAccountIdAndName(accountId, name);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export class GetQueueAttributesHandler extends AbstractActionHandler<QueryParams
|
|||
__path: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle(params: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle(params: QueryParams) {
|
||||
|
||||
const attributeNames = Object.keys(params).reduce((l, k) => {
|
||||
const [name, _] = k.split('.');
|
||||
|
|
|
|||
|
|
@ -1,39 +1,18 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import * as Joi from 'joi';
|
||||
|
||||
import { PrismaService } from '../_prisma/prisma.service';
|
||||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import { SqsQueue } from './sqs-queue.entity';
|
||||
import { V2ListQueuesHandler } from './v2-list-queues.handler';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {}
|
||||
|
||||
@Injectable()
|
||||
export class ListQueuesHandler extends AbstractActionHandler<QueryParams> {
|
||||
|
||||
constructor(
|
||||
private readonly prismaService: PrismaService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
export class ListQueuesHandler extends V2ListQueuesHandler {
|
||||
format = Format.Xml;
|
||||
action = Action.SqsListQueues;
|
||||
validator = Joi.object<QueryParams, true>();
|
||||
|
||||
protected async handle(params: QueryParams, awsProperties: AwsProperties) {
|
||||
|
||||
const rawQueues = await this.prismaService.sqsQueue.findMany({
|
||||
where: {
|
||||
accountId: awsProperties.accountId,
|
||||
region: awsProperties.region,
|
||||
}
|
||||
});
|
||||
|
||||
const queues = rawQueues.map(q => new SqsQueue(q));
|
||||
|
||||
return {
|
||||
QueueUrl: queues.map((q) => q.getUrl(awsProperties.host))
|
||||
}
|
||||
override async handle(params: {}, context: RequestContext) {
|
||||
const response: any = await super.handle(params, context);
|
||||
return { QueueUrl: response.QueueUrls }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Action } from '../action.enum';
|
|||
import * as Joi from 'joi';
|
||||
import { SqsQueue } from './sqs-queue.entity';
|
||||
import { SqsQueueEntryService } from './sqs-queue-entry.service';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
QueueUrl: string;
|
||||
|
|
@ -22,7 +23,7 @@ export class PurgeQueueHandler extends AbstractActionHandler<QueryParams> {
|
|||
action = Action.SqsPurgeQueue;
|
||||
validator = Joi.object<QueryParams, true>({ QueueUrl: Joi.string().required() });
|
||||
|
||||
protected async handle({ QueueUrl }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ QueueUrl }: QueryParams, { awsProperties} : RequestContext) {
|
||||
|
||||
const [accountId, name] = SqsQueue.tryGetAccountIdAndNameFromPathOrArn(QueueUrl);
|
||||
await this.sqsQueueEntryService.purge(accountId, name);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export class ReceiveMessageHandler extends AbstractActionHandler<QueryParams> {
|
|||
VisibilityTimeout: Joi.number(),
|
||||
});
|
||||
|
||||
protected async handle({ QueueUrl, MaxNumberOfMessages, VisibilityTimeout }: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle({ QueueUrl, MaxNumberOfMessages, VisibilityTimeout }: QueryParams) {
|
||||
|
||||
const [accountId, name] = SqsQueue.tryGetAccountIdAndNameFromPathOrArn(QueueUrl);
|
||||
const records = await this.sqsQueueEntryService.receiveMessages(accountId, name, MaxNumberOfMessages, VisibilityTimeout);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export class SetQueueAttributesHandler extends AbstractActionHandler<QueryParams
|
|||
__path: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle(params: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle(params: QueryParams) {
|
||||
const [accountId, name] = SqsQueue.getAccountIdAndNameFromPath(params.__path);
|
||||
const queue = await this.sqsQueueEntryService.findQueueByAccountIdAndName(accountId, name);
|
||||
const attributes = SqsQueue.attributePairs(params);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { randomUUID } from 'crypto';
|
|||
|
||||
import { PrismaService } from '../_prisma/prisma.service';
|
||||
import { SqsQueue } from './sqs-queue.entity';
|
||||
import { QueueNameExists } from '../aws-shared-entities/aws-exceptions';
|
||||
|
||||
type QueueEntry = {
|
||||
id: string;
|
||||
|
|
@ -33,8 +34,12 @@ export class SqsQueueEntryService {
|
|||
}
|
||||
|
||||
async createQueue(data: Prisma.SqsQueueCreateInput): Promise<SqsQueue> {
|
||||
const prisma = await this.prismaService.sqsQueue.create({ data });
|
||||
return new SqsQueue(prisma);
|
||||
try {
|
||||
const prisma = await this.prismaService.sqsQueue.create({ data });
|
||||
return new SqsQueue(prisma);
|
||||
} catch (error) {
|
||||
throw new QueueNameExists();
|
||||
}
|
||||
}
|
||||
|
||||
async deleteQueue(id: number): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -54,7 +54,12 @@ const attributeSlotMap = {
|
|||
return SqsQueue.getAccountIdAndNameFromPath(workingString);
|
||||
}
|
||||
|
||||
static attributePairs(queryParams: Record<string, string>): { key: string, value: string }[] {
|
||||
static attributePairs(queryParams: Record<string, any>): { key: string, value: string }[] {
|
||||
|
||||
if (queryParams.Attributes) {
|
||||
return Object.entries(queryParams.Attributes as Record<string, string>).map(([key, value]) => ({ key, value }));
|
||||
}
|
||||
|
||||
const pairs: { key: string, value: string }[] = [];
|
||||
for (const param of Object.keys(queryParams)) {
|
||||
const components = this.breakdownAwsQueryParam(param);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import { SqsQueueEntryService } from './sqs-queue-entry.service';
|
|||
import { SqsHandlers } from './sqs.constants';
|
||||
import { DeleteMessageBatchHandler } from './delete-message-batch.handler';
|
||||
import { PrismaModule } from '../_prisma/prisma.module';
|
||||
import { V2ListQueuesHandler } from './v2-list-queues.handler';
|
||||
import { V2CreateQueueHandler } from './v2-create-queue.handler';
|
||||
|
||||
const handlers = [
|
||||
CreateQueueHandler,
|
||||
|
|
@ -28,6 +30,8 @@ const handlers = [
|
|||
PurgeQueueHandler,
|
||||
ReceiveMessageHandler,
|
||||
SetQueueAttributesHandler,
|
||||
V2CreateQueueHandler,
|
||||
V2ListQueuesHandler,
|
||||
]
|
||||
|
||||
const actions = [
|
||||
|
|
@ -51,6 +55,26 @@ const actions = [
|
|||
Action.SqsSetQueueAttributes,
|
||||
Action.SqsTagQueue,
|
||||
Action.SqsUntagQueue,
|
||||
Action.V2_SqsAddPermisson,
|
||||
Action.V2_SqsChangeMessageVisibility,
|
||||
Action.V2_SqsChangeMessageVisibilityBatch,
|
||||
Action.V2_SqsCreateQueue,
|
||||
Action.V2_SqsDeleteMessage,
|
||||
Action.V2_SqsDeleteMessageBatch,
|
||||
Action.V2_SqsDeleteQueue,
|
||||
Action.V2_SqsGetQueueAttributes,
|
||||
Action.V2_SqsGetQueueUrl,
|
||||
Action.V2_SqsListDeadLetterSourceQueues,
|
||||
Action.V2_SqsListQueues,
|
||||
Action.V2_SqsListQueueTags,
|
||||
Action.V2_SqsPurgeQueue,
|
||||
Action.V2_SqsReceiveMessage,
|
||||
Action.V2_SqsRemovePermission,
|
||||
Action.V2_SqsSendMessage,
|
||||
Action.V2_SqsSendMessageBatch,
|
||||
Action.V2_SqsSetQueueAttributes,
|
||||
Action.V2_SqsTagQueue,
|
||||
Action.V2_SqsUntagQueue,
|
||||
]
|
||||
|
||||
@Module({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import * as Joi from 'joi';
|
||||
|
||||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import { AttributesService } from '../aws-shared-entities/attributes.service';
|
||||
import { TagsService } from '../aws-shared-entities/tags.service';
|
||||
import { SqsQueueEntryService } from './sqs-queue-entry.service';
|
||||
import { SqsQueue } from './sqs-queue.entity';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {
|
||||
QueueName: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class V2CreateQueueHandler extends AbstractActionHandler<QueryParams> {
|
||||
|
||||
constructor(
|
||||
private readonly sqsQueueEntryService: SqsQueueEntryService,
|
||||
private readonly tagsService: TagsService,
|
||||
private readonly attributeService: AttributesService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
format = Format.Json;
|
||||
action = Action.V2_SqsCreateQueue;
|
||||
validator = Joi.object<QueryParams, true>({
|
||||
QueueName: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle(params: QueryParams, context: RequestContext) {
|
||||
|
||||
const { QueueName: name } = params;
|
||||
|
||||
const queue = await this.sqsQueueEntryService.createQueue({
|
||||
name,
|
||||
accountId: context.awsProperties.accountId,
|
||||
region: context.awsProperties.region,
|
||||
});
|
||||
|
||||
const tags = TagsService.tagPairs(params);
|
||||
await this.tagsService.createMany(queue.arn, tags);
|
||||
|
||||
const attributes = SqsQueue.attributePairs(params);
|
||||
await this.attributeService.createMany(queue.arn, attributes);
|
||||
|
||||
return { QueueUrl: queue.getUrl(context.awsProperties.host) };
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import * as Joi from 'joi';
|
||||
|
||||
import { PrismaService } from '../_prisma/prisma.service';
|
||||
import { AbstractActionHandler, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import { SqsQueue } from './sqs-queue.entity';
|
||||
import { RequestContext } from '../_context/request.context';
|
||||
|
||||
type QueryParams = {}
|
||||
|
||||
@Injectable()
|
||||
export class V2ListQueuesHandler extends AbstractActionHandler<QueryParams> {
|
||||
|
||||
constructor(
|
||||
private readonly prismaService: PrismaService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
format = Format.Json;
|
||||
action = Action.V2_SqsListQueues;
|
||||
validator = Joi.object<QueryParams, true>();
|
||||
|
||||
protected async handle(params: QueryParams, context: RequestContext): Promise<{ QueueUrl: string[] } | { QueueUrls: string[] } > {
|
||||
|
||||
const rawQueues = await this.prismaService.sqsQueue.findMany({
|
||||
where: {
|
||||
accountId: context.awsProperties.accountId,
|
||||
region: context.awsProperties.region,
|
||||
}
|
||||
});
|
||||
|
||||
const queues = rawQueues.map(q => new SqsQueue(q));
|
||||
|
||||
return {
|
||||
QueueUrls: queues.map((q) => q.getUrl(context.awsProperties.host))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import * as Joi from "joi";
|
|||
|
||||
import { AbstractActionHandler, AwsProperties, Format } from "../abstract-action.handler";
|
||||
import { Action } from "../action.enum";
|
||||
import { RequestContext } from "../_context/request.context";
|
||||
|
||||
type QueryParams = {}
|
||||
|
||||
|
|
@ -13,11 +14,11 @@ export class GetCallerIdentityHandler extends AbstractActionHandler<QueryParams>
|
|||
action = Action.StsGetCallerIdentity;
|
||||
validator = Joi.object<QueryParams, true>();
|
||||
|
||||
protected async handle(queryParams: QueryParams, awsProperties: AwsProperties) {
|
||||
protected async handle(queryParams: QueryParams, context: RequestContext) {
|
||||
return {
|
||||
"UserId": "AIDASAMPLEUSERID",
|
||||
"Account": awsProperties.accountId,
|
||||
"Arn": `arn:aws:iam::${awsProperties.accountId}:user/DevAdmin`
|
||||
"Account": context.awsProperties.accountId,
|
||||
"Arn": `arn:aws:iam::${context.awsProperties.accountId}:user/DevAdmin`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
class LinkedListNode<T> {
|
||||
previous: LinkedListNode<T> | null;
|
||||
next: LinkedListNode<T> | null;
|
||||
|
||||
constructor(
|
||||
readonly record: T,
|
||||
) {
|
||||
this.previous = null;
|
||||
this.next = null;
|
||||
}
|
||||
}
|
||||
|
||||
export class Stack<T> {
|
||||
|
||||
private head: LinkedListNode<T> | null = null;
|
||||
private tail: LinkedListNode<T> | null = null;
|
||||
private size = 0;
|
||||
|
||||
constructor(
|
||||
private readonly maxSize: number,
|
||||
) {}
|
||||
|
||||
/*
|
||||
Add E
|
||||
D ... B <-> A
|
||||
Cull A
|
||||
*/
|
||||
push(record: T): Stack<T> {
|
||||
|
||||
const D = this.head;
|
||||
const E = new LinkedListNode(record);
|
||||
this.head = E;
|
||||
|
||||
if (D === null) {
|
||||
this.tail = E;
|
||||
this.size++;
|
||||
return this;
|
||||
}
|
||||
|
||||
if (this.size < this.maxSize) {
|
||||
E.next = D;
|
||||
D.previous = E;
|
||||
this.size++;
|
||||
return this;
|
||||
}
|
||||
|
||||
const A = this.tail;
|
||||
const B = A!.previous!;
|
||||
B.next = null;
|
||||
this.tail = B;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue