More support for secrets manager
This commit is contained in:
parent
05208e1277
commit
07d3841cd7
|
|
@ -1,4 +1,4 @@
|
|||
import { BadRequestException, Body, Controller, Get, Inject, Post, Headers } from '@nestjs/common';
|
||||
import { BadRequestException, Body, Controller, Get, Inject, Post, Headers, Header } from '@nestjs/common';
|
||||
import { ActionHandlers } from './app.constants';
|
||||
import * as Joi from 'joi';
|
||||
import { Action } from './action.enum';
|
||||
|
|
@ -6,6 +6,7 @@ import { AbstractActionHandler, Format } from './abstract-action.handler';
|
|||
import * as js2xmlparser from 'js2xmlparser';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { CommonConfig } from './config/common-config.interface';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
|
|
@ -17,6 +18,7 @@ export class AppController {
|
|||
) {}
|
||||
|
||||
@Post()
|
||||
@Header('x-amzn-RequestId', uuid.v4())
|
||||
async post(
|
||||
@Body() body: Record<string, any>,
|
||||
@Headers() headers: Record<string, any>,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,11 @@ export class TagsService {
|
|||
return pairs;
|
||||
}
|
||||
|
||||
static getXmlSafeAttributesMap(tags: Tag[]) {
|
||||
static getXmlSafeTagsMap(tags: Tag[]) {
|
||||
return { Tags: { member: tags.map(({ name, value }) => ({ Key: name, Value: value })) } };
|
||||
}
|
||||
|
||||
static getJsonSafeTagsMap(tags: Tag[]) {
|
||||
return tags.map(({ name, value }) => ({ Key: name, Value: value }));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { ClassSerializerInterceptor } from '@nestjs/common';
|
|||
import { NestFactory, Reflector } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import * as morgan from 'morgan';
|
||||
const bodyParser = require('body-parser');
|
||||
|
||||
(async () => {
|
||||
|
||||
|
|
@ -9,6 +10,7 @@ import * as morgan from 'morgan';
|
|||
const app = await NestFactory.create(AppModule);
|
||||
app.use(morgan('dev'));
|
||||
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
|
||||
app.use(bodyParser.json({ type: 'application/x-amz-json-1.1'}));
|
||||
|
||||
await app.listen(port, () => console.log(`Listening on port ${port}`));
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import * as Joi from 'joi';
|
||||
import { Secret } from './secret.entity';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
type QueryParams = {
|
||||
Name: string;
|
||||
Description: string;
|
||||
SecretString: string;
|
||||
ClientRequestToken: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateSecretHandler extends AbstractActionHandler<QueryParams> {
|
||||
|
||||
constructor(
|
||||
@InjectRepository(Secret)
|
||||
private readonly secretRepo: Repository<Secret>,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
format = Format.Json;
|
||||
action = Action.SecretsManagerCreateSecret;
|
||||
validator = Joi.object<QueryParams, true>({
|
||||
Name: Joi.string().required(),
|
||||
Description: Joi.string().allow('', null),
|
||||
SecretString: Joi.string().allow('', null),
|
||||
ClientRequestToken: Joi.string().required(),
|
||||
});
|
||||
|
||||
protected async handle(params: QueryParams, awsProperties: AwsProperties) {
|
||||
|
||||
const { Name: name, Description: description, SecretString: secretString, ClientRequestToken } = params;
|
||||
|
||||
const secret = await this.secretRepo.create({
|
||||
versionId: ClientRequestToken,
|
||||
description,
|
||||
name,
|
||||
secretString,
|
||||
accountId: awsProperties.accountId,
|
||||
region: awsProperties.region,
|
||||
}).save();
|
||||
|
||||
return { ARN: secret.arn, VersionId: secret.versionId, Name: secret.name };
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
|
||||
import { Action } from '../action.enum';
|
||||
import * as Joi from 'joi';
|
||||
import { Secret } from './secret.entity';
|
||||
import { TagsService } from '../aws-shared-entities/tags.service';
|
||||
|
||||
type QueryParams = {
|
||||
SecretId: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DescribeSecretHandler extends AbstractActionHandler {
|
||||
|
||||
constructor(
|
||||
@InjectRepository(Secret)
|
||||
private readonly secretRepo: Repository<Secret>,
|
||||
private readonly tagsService: TagsService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
format = Format.Json;
|
||||
action = Action.SecretsManagerDescribeSecret;
|
||||
validator = Joi.object<QueryParams, true>({ SecretId: Joi.string().required() });
|
||||
|
||||
protected async handle({ SecretId }: QueryParams, awsProperties: AwsProperties) {
|
||||
|
||||
const parts = SecretId.split(':');
|
||||
const name = parts.length > 1 ? parts[-1] : SecretId;
|
||||
|
||||
const secret = await this.secretRepo.findOne({ where: { name } });
|
||||
|
||||
if (!secret) {
|
||||
throw new BadRequestException('ResourceNotFoundException', "Secrets Manager can't find the resource that you asked for.");
|
||||
}
|
||||
|
||||
const tags = await this.tagsService.getByArn(secret.arn);
|
||||
const listOfTagPairs = TagsService.getJsonSafeTagsMap(tags);
|
||||
|
||||
return {
|
||||
"ARN": secret.arn,
|
||||
"CreatedDate": new Date(secret.createdAt).getMilliseconds(),
|
||||
"DeletedDate": 0,
|
||||
"Description": secret.description,
|
||||
"KmsKeyId": "",
|
||||
"LastAccessedDate": new Date().getMilliseconds(),
|
||||
"LastChangedDate": new Date(secret.createdAt).getMilliseconds(),
|
||||
"LastRotatedDate": 0,
|
||||
"Name": secret.name,
|
||||
"OwningService": secret.accountId,
|
||||
"PrimaryRegion": secret.region,
|
||||
"ReplicationStatus": [],
|
||||
"RotationEnabled": false,
|
||||
"Tags": listOfTagPairs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,31 @@
|
|||
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { BaseEntity, Column, CreateDateColumn, Entity, Index, PrimaryColumn, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
@Entity('secret')
|
||||
export class Secret extends BaseEntity {
|
||||
|
||||
@PrimaryGeneratedColumn({ name: 'id' })
|
||||
id: string;
|
||||
@PrimaryColumn({ name: 'versionId' })
|
||||
versionId: string;
|
||||
|
||||
@Column({ name: 'name', nullable: false })
|
||||
@Index()
|
||||
name: string;
|
||||
|
||||
@Column({ name: 'description', nullable: true })
|
||||
description: string;
|
||||
|
||||
@Column({ name: 'secret_string', nullable: true })
|
||||
secretString: string;
|
||||
|
||||
@Column({ name: 'account_id', nullable: false })
|
||||
accountId: string;
|
||||
|
||||
@Column({ name: 'region', nullable: false })
|
||||
region: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: string;
|
||||
|
||||
get arn(): string {
|
||||
return `arn:aws:secretsmanager:${this.region}:${this.accountId}:${this.name}`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,14 @@ import { Action } from '../action.enum';
|
|||
import { AwsSharedEntitiesModule } from '../aws-shared-entities/aws-shared-entities.module';
|
||||
import { DefaultActionHandlerProvider } from '../default-action-handler/default-action-handler.provider';
|
||||
import { ExistingActionHandlersProvider } from '../default-action-handler/existing-action-handlers.provider';
|
||||
import { CreateSecretHandler } from './create-secret.handler';
|
||||
import { DescribeSecretHandler } from './describe-secret.handler';
|
||||
import { Secret } from './secret.entity';
|
||||
import { SecretsManagerHandlers } from './secrets-manager.constants';
|
||||
|
||||
const handlers = [
|
||||
|
||||
CreateSecretHandler,
|
||||
DescribeSecretHandler,
|
||||
]
|
||||
|
||||
const actions = [
|
||||
|
|
@ -38,7 +42,7 @@ const actions = [
|
|||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([]),
|
||||
TypeOrmModule.forFeature([Secret]),
|
||||
AwsSharedEntitiesModule,
|
||||
],
|
||||
providers: [
|
||||
|
|
|
|||
|
|
@ -26,6 +26,6 @@ export class ListTagsForResourceHandler extends AbstractActionHandler {
|
|||
|
||||
protected async handle({ ResourceArn }: QueryParams, awsProperties: AwsProperties) {
|
||||
const tags = await this.tagsService.getByArn(ResourceArn);
|
||||
return TagsService.getXmlSafeAttributesMap(tags);
|
||||
return TagsService.getXmlSafeTagsMap(tags);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue