Added IAM support

This commit is contained in:
Matthew Bessette 2023-04-03 16:00:14 -04:00
parent 2c78be1b3f
commit 29d36a57d6
22 changed files with 1167 additions and 52 deletions

View File

@ -1,56 +1,216 @@
export enum Action {
// IAM
IamAddClientIDToOpenIDConnectProvider = 'AddClientIDToOpenIDConnectProvider',
IamAddRoleToInstanceProfile = 'AddRoleToInstanceProfile',
IamAddUserToGroup = 'AddUserToGroup',
IamAttachGroupPolicy = 'AttachGroupPolicy',
IamAttachRolePolicy = 'AttachRolePolicy',
IamAttachUserPolicy = 'AttachUserPolicy',
IamChangePassword = 'ChangePassword',
IamCreateAccessKey = 'CreateAccessKey',
IamCreateAccountAlias = 'CreateAccountAlias',
IamCreateGroup = 'CreateGroup',
IamCreateInstanceProfile = 'CreateInstanceProfile',
IamCreateLoginProfile = 'CreateLoginProfile',
IamCreateOpenIDConnectProvider = 'CreateOpenIDConnectProvider',
IamCreatePolicy = 'CreatePolicy',
IamCreatePolicyVersion = 'CreatePolicyVersion',
IamCreateRole = 'CreateRole',
IamCreateSAMLProvider = 'CreateSAMLProvider',
IamCreateServiceLinkedRole = 'CreateServiceLinkedRole',
IamCreateServiceSpecificCredential = 'CreateServiceSpecificCredential',
IamCreateUser = 'CreateUser',
IamCreateVirtualMFADevice = 'CreateVirtualMFADevice',
IamDeactivateMFADevice = 'DeactivateMFADevice',
IamDeleteAccessKey = 'DeleteAccessKey',
IamDeleteAccountAlias = 'DeleteAccountAlias',
IamDeleteAccountPasswordPolicy = 'DeleteAccountPasswordPolicy',
IamDeleteGroup = 'DeleteGroup',
IamDeleteGroupPolicy = 'DeleteGroupPolicy',
IamDeleteInstanceProfile = 'DeleteInstanceProfile',
IamDeleteLoginProfile = 'DeleteLoginProfile',
IamDeleteOpenIDConnectProvider = 'DeleteOpenIDConnectProvider',
IamDeletePolicy = 'DeletePolicy',
IamDeletePolicyVersion = 'DeletePolicyVersion',
IamDeleteRole = 'DeleteRole',
IamDeleteRolePermissionsBoundary = 'DeleteRolePermissionsBoundary',
IamDeleteRolePolicy = 'DeleteRolePolicy',
IamDeleteSAMLProvider = 'DeleteSAMLProvider',
IamDeleteServerCertificate = 'DeleteServerCertificate',
IamDeleteServiceLinkedRole = 'DeleteServiceLinkedRole',
IamDeleteServiceSpecificCredential = 'DeleteServiceSpecificCredential',
IamDeleteSigningCertificate = 'DeleteSigningCertificate',
IamDeleteSSHPublicKey = 'DeleteSSHPublicKey',
IamDeleteUser = 'DeleteUser',
IamDeleteUserPermissionsBoundary = 'DeleteUserPermissionsBoundary',
IamDeleteUserPolicy = 'DeleteUserPolicy',
IamDeleteVirtualMFADevice = 'DeleteVirtualMFADevice',
IamDetachGroupPolicy = 'DetachGroupPolicy',
IamDetachRolePolicy = 'DetachRolePolicy',
IamDetachUserPolicy = 'DetachUserPolicy',
IamEnableMFADevice = 'EnableMFADevice',
IamGenerateCredentialReport = 'GenerateCredentialReport',
IamGenerateOrganizationsAccessReport = 'GenerateOrganizationsAccessReport',
IamGenerateServiceLastAccessedDetails = 'GenerateServiceLastAccessedDetails',
IamGetAccessKeyLastUsed = 'GetAccessKeyLastUsed',
IamGetAccountAuthorizationDetails = 'GetAccountAuthorizationDetails',
IamGetAccountPasswordPolicy = 'GetAccountPasswordPolicy',
IamGetAccountSummary = 'GetAccountSummary',
IamGetContextKeysForCustomPolicy = 'GetContextKeysForCustomPolicy',
IamGetContextKeysForPrincipalPolicy = 'GetContextKeysForPrincipalPolicy',
IamGetCredentialReport = 'GetCredentialReport',
IamGetGroup = 'GetGroup',
IamGetGroupPolicy = 'GetGroupPolicy',
IamGetInstanceProfile = 'GetInstanceProfile',
IamGetLoginProfile = 'GetLoginProfile',
IamGetOpenIDConnectProvider = 'GetOpenIDConnectProvider',
IamGetOrganizationsAccessReport = 'GetOrganizationsAccessReport',
IamGetPolicy = 'GetPolicy',
IamGetPolicyVersion = 'GetPolicyVersion',
IamGetRole = 'GetRole',
IamGetRolePolicy = 'GetRolePolicy',
IamGetSAMLProvider = 'GetSAMLProvider',
IamGetServerCertificate = 'GetServerCertificate',
IamGetServiceLastAccessedDetails = 'GetServiceLastAccessedDetails',
IamGetServiceLastAccessedDetailsWithEntities = 'GetServiceLastAccessedDetailsWithEntities',
IamGetServiceLinkedRoleDeletionStatus = 'GetServiceLinkedRoleDeletionStatus',
IamGetSSHPublicKey = 'GetSSHPublicKey',
IamGetUser = 'GetUser',
IamGetUserPolicy = 'GetUserPolicy',
IamListAccessKeys = 'ListAccessKeys',
IamListAccountAliases = 'ListAccountAliases',
IamListAttachedGroupPolicies = 'ListAttachedGroupPolicies',
IamListAttachedRolePolicies = 'ListAttachedRolePolicies',
IamListAttachedUserPolicies = 'ListAttachedUserPolicies',
IamListEntitiesForPolicy = 'ListEntitiesForPolicy',
IamListGroupPolicies = 'ListGroupPolicies',
IamListGroups = 'ListGroups',
IamListGroupsForUser = 'ListGroupsForUser',
IamListInstanceProfiles = 'ListInstanceProfiles',
IamListInstanceProfilesForRole = 'ListInstanceProfilesForRole',
IamListInstanceProfileTags = 'ListInstanceProfileTags',
IamListMFADevices = 'ListMFADevices',
IamListMFADeviceTags = 'ListMFADeviceTags',
IamListOpenIDConnectProviders = 'ListOpenIDConnectProviders',
IamListOpenIDConnectProviderTags = 'ListOpenIDConnectProviderTags',
IamListPolicies = 'ListPolicies',
IamListPoliciesGrantingServiceAccess = 'ListPoliciesGrantingServiceAccess',
IamListPolicyTags = 'ListPolicyTags',
IamListPolicyVersions = 'ListPolicyVersions',
IamListRolePolicies = 'ListRolePolicies',
IamListRoles = 'ListRoles',
IamListRoleTags = 'ListRoleTags',
IamListSAMLProviders = 'ListSAMLProviders',
IamListSAMLProviderTags = 'ListSAMLProviderTags',
IamListServerCertificates = 'ListServerCertificates',
IamListServerCertificateTags = 'ListServerCertificateTags',
IamListServiceSpecificCredentials = 'ListServiceSpecificCredentials',
IamListSigningCertificates = 'ListSigningCertificates',
IamListSSHPublicKeys = 'ListSSHPublicKeys',
IamListUserPolicies = 'ListUserPolicies',
IamListUsers = 'ListUsers',
IamListUserTags = 'ListUserTags',
IamListVirtualMFADevices = 'ListVirtualMFADevices',
IamPutGroupPolicy = 'PutGroupPolicy',
IamPutRolePermissionsBoundary = 'PutRolePermissionsBoundary',
IamPutRolePolicy = 'PutRolePolicy',
IamPutUserPermissionsBoundary = 'PutUserPermissionsBoundary',
IamPutUserPolicy = 'PutUserPolicy',
IamRemoveClientIDFromOpenIDConnectProvider = 'RemoveClientIDFromOpenIDConnectProvider',
IamRemoveRoleFromInstanceProfile = 'RemoveRoleFromInstanceProfile',
IamRemoveUserFromGroup = 'RemoveUserFromGroup',
IamResetServiceSpecificCredential = 'ResetServiceSpecificCredential',
IamResyncMFADevice = 'ResyncMFADevice',
IamSetDefaultPolicyVersion = 'SetDefaultPolicyVersion',
IamSetSecurityTokenServicePreferences = 'SetSecurityTokenServicePreferences',
IamSimulateCustomPolicy = 'SimulateCustomPolicy',
IamSimulatePrincipalPolicy = 'SimulatePrincipalPolicy',
IamTagInstanceProfile = 'TagInstanceProfile',
IamTagMFADevice = 'TagMFADevice',
IamTagOpenIDConnectProvider = 'TagOpenIDConnectProvider',
IamTagPolicy = 'TagPolicy',
IamTagRole = 'TagRole',
IamTagSAMLProvider = 'TagSAMLProvider',
IamTagServerCertificate = 'TagServerCertificate',
IamTagUser = 'TagUser',
IamUntagInstanceProfile = 'UntagInstanceProfile',
IamUntagMFADevice = 'UntagMFADevice',
IamUntagOpenIDConnectProvider = 'UntagOpenIDConnectProvider',
IamUntagPolicy = 'UntagPolicy',
IamUntagRole = 'UntagRole',
IamUntagSAMLProvider = 'UntagSAMLProvider',
IamUntagServerCertificate = 'UntagServerCertificate',
IamUntagUser = 'UntagUser',
IamUpdateAccessKey = 'UpdateAccessKey',
IamUpdateAccountPasswordPolicy = 'UpdateAccountPasswordPolicy',
IamUpdateAssumeRolePolicy = 'UpdateAssumeRolePolicy',
IamUpdateGroup = 'UpdateGroup',
IamUpdateLoginProfile = 'UpdateLoginProfile',
IamUpdateOpenIDConnectProviderThumbprint = 'UpdateOpenIDConnectProviderThumbprint',
IamUpdateRole = 'UpdateRole',
IamUpdateRoleDescription = 'UpdateRoleDescription',
IamUpdateSAMLProvider = 'UpdateSAMLProvider',
IamUpdateServerCertificate = 'UpdateServerCertificate',
IamUpdateServiceSpecificCredential = 'UpdateServiceSpecificCredential',
IamUpdateSigningCertificate = 'UpdateSigningCertificate',
IamUpdateSSHPublicKey = 'UpdateSSHPublicKey',
IamUpdateUser = 'UpdateUser',
IamUploadServerCertificate = 'UploadServerCertificate',
IamUploadSigningCertificate = 'UploadSigningCertificate',
IamUploadSSHPublicKey = 'UploadSSHPublicKey',
// KMS
KmsCancelKeyDeletion = 'CancelKeyDeletion',
KmsConnectCustomKeyStore = 'ConnectCustomKeyStore',
KmsCreateAlias = 'CreateAlias',
KmsCreateCustomKeyStore = 'CreateCustomKeyStore',
KmsCreateGrant = 'CreateGrant',
KmsCreateKey = 'CreateKey',
KmsDecrypt = 'Decrypt',
KmsDeleteAlias = 'DeleteAlias',
KmsDeleteCustomKeyStore = 'DeleteCustomKeyStore',
KmsDeleteImportedKeyMaterial = 'DeleteImportedKeyMaterial',
KmsDescribeCustomKeyStores = 'DescribeCustomKeyStores',
KmsDescribeKey = 'DescribeKey',
KmsDisableKey = 'DisableKey',
KmsDisableKeyRotation = 'DisableKeyRotation',
KmsDisconnectCustomKeyStore = 'DisconnectCustomKeyStore',
KmsEnableKey = 'EnableKey',
KmsEnableKeyRotation = 'EnableKeyRotation',
KmsEncrypt = 'Encrypt',
KmsGenerateDataKey = 'GenerateDataKey',
KmsGenerateDataKeyPair = 'GenerateDataKeyPair',
KmsGenerateDataKeyPairWithoutPlaintext = 'GenerateDataKeyPairWithoutPlaintext',
KmsGenerateDataKeyWithoutPlaintext = 'GenerateDataKeyWithoutPlaintext',
KmsGenerateMac = 'GenerateMac',
KmsGenerateRandom = 'GenerateRandom',
KmsGetKeyPolicy = 'GetKeyPolicy',
KmsGetKeyRotationStatus = 'GetKeyRotationStatus',
KmsGetParametersForImport = 'GetParametersForImport',
KmsGetPublicKey = 'GetPublicKey',
KmsImportKeyMaterial = 'ImportKeyMaterial',
KmsListAliases = 'ListAliases',
KmsListGrants = 'ListGrants',
KmsListKeyPolicies = 'ListKeyPolicies',
KmsListKeys = 'ListKeys',
KmsListResourceTags = 'ListResourceTags',
KmsListRetirableGrants = 'ListRetirableGrants',
KmsPutKeyPolicy = 'PutKeyPolicy',
KmsReEncrypt = 'ReEncrypt',
KmsReplicateKey = 'ReplicateKey',
KmsRetireGrant = 'RetireGrant',
KmsRevokeGrant = 'RevokeGrant',
KmsScheduleKeyDeletion = 'ScheduleKeyDeletion',
KmsSign = 'Sign',
KmsTagResource = 'TagResource',
KmsUntagResource = 'UntagResource',
KmsUpdateAlias = 'UpdateAlias',
KmsUpdateCustomKeyStore = 'UpdateCustomKeyStore',
KmsUpdateKeyDescription = 'UpdateKeyDescription',
KmsUpdatePrimaryRegion = 'UpdatePrimaryRegion',
KmsVerify = 'Verify',
KmsVerifyMac = 'VerifyMac',
KmsCancelKeyDeletion = 'TrentService.CancelKeyDeletion',
KmsConnectCustomKeyStore = 'TrentService.ConnectCustomKeyStore',
KmsCreateAlias = 'TrentService.CreateAlias',
KmsCreateCustomKeyStore = 'TrentService.CreateCustomKeyStore',
KmsCreateGrant = 'TrentService.CreateGrant',
KmsCreateKey = 'TrentService.CreateKey',
KmsDecrypt = 'TrentService.Decrypt',
KmsDeleteAlias = 'TrentService.DeleteAlias',
KmsDeleteCustomKeyStore = 'TrentService.DeleteCustomKeyStore',
KmsDeleteImportedKeyMaterial = 'TrentService.DeleteImportedKeyMaterial',
KmsDescribeCustomKeyStores = 'TrentService.DescribeCustomKeyStores',
KmsDescribeKey = 'TrentService.DescribeKey',
KmsDisableKey = 'TrentService.DisableKey',
KmsDisableKeyRotation = 'TrentService.DisableKeyRotation',
KmsDisconnectCustomKeyStore = 'TrentService.DisconnectCustomKeyStore',
KmsEnableKey = 'TrentService.EnableKey',
KmsEnableKeyRotation = 'TrentService.EnableKeyRotation',
KmsEncrypt = 'TrentService.Encrypt',
KmsGenerateDataKey = 'TrentService.GenerateDataKey',
KmsGenerateDataKeyPair = 'TrentService.GenerateDataKeyPair',
KmsGenerateDataKeyPairWithoutPlaintext = 'TrentService.GenerateDataKeyPairWithoutPlaintext',
KmsGenerateDataKeyWithoutPlaintext = 'TrentService.GenerateDataKeyWithoutPlaintext',
KmsGenerateMac = 'TrentService.GenerateMac',
KmsGenerateRandom = 'TrentService.GenerateRandom',
KmsGetKeyPolicy = 'TrentService.GetKeyPolicy',
KmsGetKeyRotationStatus = 'TrentService.GetKeyRotationStatus',
KmsGetParametersForImport = 'TrentService.GetParametersForImport',
KmsGetPublicKey = 'TrentService.GetPublicKey',
KmsImportKeyMaterial = 'TrentService.ImportKeyMaterial',
KmsListAliases = 'TrentService.ListAliases',
KmsListGrants = 'TrentService.ListGrants',
KmsListKeyPolicies = 'TrentService.ListKeyPolicies',
KmsListKeys = 'TrentService.ListKeys',
KmsListResourceTags = 'TrentService.ListResourceTags',
KmsListRetirableGrants = 'TrentService.ListRetirableGrants',
KmsPutKeyPolicy = 'TrentService.PutKeyPolicy',
KmsReEncrypt = 'TrentService.ReEncrypt',
KmsReplicateKey = 'TrentService.ReplicateKey',
KmsRetireGrant = 'TrentService.RetireGrant',
KmsRevokeGrant = 'TrentService.RevokeGrant',
KmsScheduleKeyDeletion = 'TrentService.ScheduleKeyDeletion',
KmsSign = 'TrentService.Sign',
KmsTagResource = 'TrentService.TagResource',
KmsUntagResource = 'TrentService.UntagResource',
KmsUpdateAlias = 'TrentService.UpdateAlias',
KmsUpdateCustomKeyStore = 'TrentService.UpdateCustomKeyStore',
KmsUpdateKeyDescription = 'TrentService.UpdateKeyDescription',
KmsUpdatePrimaryRegion = 'TrentService.UpdatePrimaryRegion',
KmsVerify = 'TrentService.Verify',
KmsVerifyMac = 'TrentService.VerifyMac',
// SecretsManager
SecretsManagerCancelRotateSecret = 'secretsmanager.CancelRotateSecret',

View File

@ -33,6 +33,7 @@ export class AppController {
}, {})
const queryParams = { __path: request.path, ...body, ...lowerCasedHeaders };
console.log({ queryParams })
const actionKey = queryParams['x-amz-target'] ? 'x-amz-target' : 'Action';
const { error: actionError } = Joi.object({
[actionKey]: Joi.string().valid(...Object.values(Action)).required(),

View File

@ -17,6 +17,8 @@ import { AuditInterceptor } from './audit/audit.interceptor';
import { KmsModule } from './kms/kms.module';
import { KMSHandlers } from './kms/kms.constants';
import { configValidator } from './config/config.validator';
import { IamModule } from './iam/iam.module';
import { IAMHandlers } from './iam/iam.constants';
@Module({
imports: [
@ -35,6 +37,7 @@ import { configValidator } from './config/config.validator';
}),
}),
TypeOrmModule.forFeature([Audit]),
IamModule,
KmsModule,
SecretsManagerModule,
SnsModule,
@ -54,6 +57,7 @@ import { configValidator } from './config/config.validator';
SqsHandlers,
SecretsManagerHandlers,
KMSHandlers,
IAMHandlers,
],
},
],

View File

@ -0,0 +1,47 @@
import { Injectable } from '@nestjs/common';
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
import { Action } from '../action.enum';
import * as Joi from 'joi';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as uuid from 'uuid';
import { IamPolicy } from './iam-policy.entity';
import { IamRolePolicyAttachment } from './iam-role-policy-attachment.entity';
import { IamRole } from './iam-role.entity';
type QueryParams = {
PolicyArn: string;
RoleName: string;
}
@Injectable()
export class AttachRolePolicyHandler extends AbstractActionHandler<QueryParams> {
constructor(
@InjectRepository(IamRole)
private readonly roleRepo: Repository<IamRole>,
@InjectRepository(IamRolePolicyAttachment)
private readonly attachRepo: Repository<IamRolePolicyAttachment>,
) {
super();
}
format = Format.Xml;
action = Action.IamAttachRolePolicy;
validator = Joi.object<QueryParams, true>({
PolicyArn: Joi.string().required(),
RoleName: Joi.string().required(),
});
protected async handle({ PolicyArn, RoleName }: QueryParams, awsProperties: AwsProperties) {
const role = await this.roleRepo.findOne({ where: { roleName: RoleName, accountId: awsProperties.accountId} });
await this.attachRepo.create({
id: uuid.v4(),
policyArn: PolicyArn,
roleId: role.id,
accountId: awsProperties.accountId,
}).save();
}
}

View File

@ -0,0 +1,62 @@
import { Injectable } from '@nestjs/common';
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
import { Action } from '../action.enum';
import * as Joi from 'joi';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as uuid from 'uuid';
import { IamPolicy } from './iam-policy.entity';
import { breakdownArn } from '../util/breakdown-arn';
type QueryParams = {
PolicyArn: string;
PolicyDocument: string;
SetAsDefault: boolean;
}
@Injectable()
export class CreatePolicyVersionHandler extends AbstractActionHandler<QueryParams> {
constructor(
@InjectRepository(IamPolicy)
private readonly policyRepo: Repository<IamPolicy>,
) {
super();
}
format = Format.Xml;
action = Action.IamCreatePolicyVersion;
validator = Joi.object<QueryParams, true>({
PolicyArn: Joi.string().required(),
PolicyDocument: Joi.string().required(),
SetAsDefault: Joi.boolean().required(),
});
protected async handle({ PolicyArn, PolicyDocument, SetAsDefault }: QueryParams, awsProperties: AwsProperties) {
const { identifier, accountId } = breakdownArn(PolicyArn);
const [_policy, name] = identifier.split('/');
const currentPolicy = await this.policyRepo.findOne({ where: { accountId, name, isDefault: true } });
if (SetAsDefault) {
await this.policyRepo.update({ accountId, name }, { isDefault: false })
}
const policy = await this.policyRepo.create({
id: uuid.v4(),
name: name,
isDefault: SetAsDefault,
version: currentPolicy.version + 1,
document: PolicyDocument,
accountId: awsProperties.accountId,
}).save();
return {
PolicyVersion: {
IsDefaultVersion: policy.isDefault,
VersionId: `v${policy.version}`,
CreateDate: new Date(policy.createdAt).toISOString(),
}
}
}
}

View File

@ -0,0 +1,54 @@
import { Injectable } from '@nestjs/common';
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
import { Action } from '../action.enum';
import * as Joi from 'joi';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as uuid from 'uuid';
import { IamPolicy } from './iam-policy.entity';
type QueryParams = {
PolicyName: string;
PolicyDocument: string;
}
@Injectable()
export class CreatePolicyHandler extends AbstractActionHandler<QueryParams> {
constructor(
@InjectRepository(IamPolicy)
private readonly policyRepo: Repository<IamPolicy>,
) {
super();
}
format = Format.Xml;
action = Action.IamCreatePolicy;
validator = Joi.object<QueryParams, true>({
PolicyName: Joi.string().required(),
PolicyDocument: Joi.string().required(),
});
protected async handle({ PolicyName, PolicyDocument }: QueryParams, awsProperties: AwsProperties) {
const policy = await this.policyRepo.create({
id: uuid.v4(),
name: PolicyName,
document: PolicyDocument,
accountId: awsProperties.accountId,
}).save();
return {
Policy: {
PolicyName: policy.name,
DefaultVersionId: 'v1',
PolicyId: policy.id,
Path: '/',
Arn: policy.arn,
AttachmentCount: 0,
CreateDate: new Date(policy.createdAt).toISOString(),
UpdateDate: new Date(policy.updatedAt).toISOString(),
}
}
}
}

View File

@ -0,0 +1,62 @@
import { Injectable } from '@nestjs/common';
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
import { Action } from '../action.enum';
import * as Joi from 'joi';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { IamRole } from './iam-role.entity';
import * as uuid from 'uuid';
import { IamPolicy } from './iam-policy.entity';
type QueryParams = {
RoleName: string;
Path: string;
AssumeRolePolicyDocument: string;
}
@Injectable()
export class CreateRoleHandler extends AbstractActionHandler<QueryParams> {
constructor(
@InjectRepository(IamRole)
private readonly roleRepo: Repository<IamRole>,
@InjectRepository(IamPolicy)
private readonly policyRepo: Repository<IamPolicy>,
) {
super();
}
format = Format.Xml;
action = Action.IamCreateRole;
validator = Joi.object<QueryParams, true>({
RoleName: Joi.string().required(),
Path: Joi.string().required(),
AssumeRolePolicyDocument: Joi.string().required(),
});
protected async handle({ RoleName, Path, AssumeRolePolicyDocument }: QueryParams, awsProperties: AwsProperties) {
const policy = await this.policyRepo.create({
id: uuid.v4(),
name: `${RoleName}-AssumeRolePolicyDocument`,
document: AssumeRolePolicyDocument,
accountId: awsProperties.accountId,
}).save();
const id = uuid.v4();
await this.roleRepo.create({
id,
roleName: RoleName,
path: Path,
accountId: awsProperties.accountId,
assumeRolePolicyDocumentId: policy.id,
}).save();
const role = await this.roleRepo.findOne({ where: { id }});
return {
Role: role.metadata,
}
}
}

View File

@ -0,0 +1,58 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
import { Action } from '../action.enum';
import * as Joi from 'joi';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { IamPolicy } from './iam-policy.entity';
import { breakdownArn } from '../util/breakdown-arn';
import { IamRolePolicyAttachment } from './iam-role-policy-attachment.entity';
type QueryParams = {
PolicyArn: string;
}
@Injectable()
export class GetPolicyHandler extends AbstractActionHandler<QueryParams> {
constructor(
@InjectRepository(IamPolicy)
private readonly policyRepo: Repository<IamPolicy>,
@InjectRepository(IamRolePolicyAttachment)
private readonly attachmentRepo: Repository<IamRolePolicyAttachment>,
) {
super();
}
format = Format.Xml;
action = Action.IamGetPolicy;
validator = Joi.object<QueryParams, true>({
PolicyArn: Joi.string().required(),
});
protected async handle({ PolicyArn }: QueryParams, awsProperties: AwsProperties) {
const { identifier, accountId } = breakdownArn(PolicyArn);
const [_policy, name] = identifier.split('/');
const policy = await this.policyRepo.findOne({ where: { name, accountId, isDefault: true }});
if (!policy) {
throw new NotFoundException('NoSuchEntity', 'The request was rejected because it referenced a resource entity that does not exist. The error message describes the resource.');
}
const attachmentCount = await this.attachmentRepo.count({ where: { policyArn: policy.arn } });
return {
Policy: {
PolicyName: policy.name,
DefaultVersionId: `v${policy.version}`,
PolicyId: policy.id,
Path: '/',
Arn: policy.arn,
AttachmentCount: attachmentCount,
CreateDate: new Date(policy.createdAt).toISOString(),
UpdateDate: new Date(policy.updatedAt).toISOString(),
}
}
}
}

View File

@ -0,0 +1,41 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
import { Action } from '../action.enum';
import * as Joi from 'joi';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { IamRole } from './iam-role.entity';
type QueryParams = {
RoleName: string;
}
@Injectable()
export class GetRoleHandler extends AbstractActionHandler<QueryParams> {
constructor(
@InjectRepository(IamRole)
private readonly roleRepo: Repository<IamRole>,
) {
super();
}
format = Format.Xml;
action = Action.IamGetRole;
validator = Joi.object<QueryParams, true>({
RoleName: Joi.string().required(),
});
protected async handle({ RoleName }: QueryParams, awsProperties: AwsProperties) {
const role = await this.roleRepo.findOne({ where: { roleName: RoleName, accountId: awsProperties.accountId } });
if (!role) {
throw new NotFoundException('NoSuchEntity', 'The request was rejected because it referenced a resource entity that does not exist. The error message describes the resource.');
}
return {
Role: role.metadata,
}
}
}

View File

@ -0,0 +1,38 @@
import { BaseEntity, Column, CreateDateColumn, Entity, JoinColumn, OneToMany, OneToOne, PrimaryColumn, UpdateDateColumn } from 'typeorm';
import { IamRolePolicyAttachment } from './iam-role-policy-attachment.entity';
import { IamRole } from './iam-role.entity';
@Entity({ name: 'iam_policy' })
export class IamPolicy extends BaseEntity {
@PrimaryColumn()
id: string;
@Column({ default: 1 })
version: number;
@Column({ name: 'is_default', default: true })
isDefault: boolean;
@Column()
name: string;
@Column()
document: string;
@Column({ name: 'account_id', nullable: false })
accountId: string;
@CreateDateColumn()
createdAt: string;
@UpdateDateColumn()
updatedAt: string;
@OneToOne(() => IamRole, role => role.assumeRolePolicyDocument)
iamRole: IamRole;
get arn() {
return `arn:aws:iam::${this.accountId}:policy/${this.name}`;
}
}

View File

@ -0,0 +1,18 @@
import { BaseEntity, Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm';
import { IamPolicy } from './iam-policy.entity';
@Entity({ name: 'iam_role_policy_attachment' })
export class IamRolePolicyAttachment extends BaseEntity {
@PrimaryColumn()
id: string;
@Column({ name: 'policy_arn' })
policyArn: string;
@Column({ name: 'role_name' })
roleId: string;
@Column({ name: 'account_id'})
accountId: string;
}

View File

@ -0,0 +1,48 @@
import { BaseEntity, Column, CreateDateColumn, Entity, JoinColumn, OneToOne, PrimaryColumn, UpdateDateColumn } from 'typeorm';
import { IamPolicy } from './iam-policy.entity';
@Entity({ name: 'iam_role' })
export class IamRole extends BaseEntity {
@PrimaryColumn()
id: string
@Column({ name: 'role_name' })
roleName: string;
@Column()
path: string;
@Column({ name: 'assume_role_policy_document_id', nullable: false })
assumeRolePolicyDocumentId: string;
@Column({ name: 'account_id', nullable: false })
accountId: string;
@CreateDateColumn()
createdAt: string;
@UpdateDateColumn()
updatedAt: string;
@OneToOne(() => IamPolicy, (policy) => policy.id, { eager: true })
@JoinColumn({ name: 'assume_role_policy_document_id' })
assumeRolePolicyDocument: IamPolicy;
get arn() {
const identifier = this.path.split('/');
identifier.push(this.roleName);
return `arn:aws:iam::${this.accountId}:role/${identifier.join('/')}`;
}
get metadata() {
return {
Path: this.path,
Arn: this.arn,
RoleName: this.roleName,
AssumeRolePolicyDocument: this.assumeRolePolicyDocument.document,
CreateDate: new Date(this.createdAt).toISOString(),
RoleId: this.id,
}
}
}

5
src/iam/iam.constants.ts Normal file
View File

@ -0,0 +1,5 @@
import { AbstractActionHandler } from '../abstract-action.handler';
import { Action } from '../action.enum';
export type IAMHandlers = Record<Action, AbstractActionHandler>;
export const IAMHandlers = Symbol.for('IAMHandlers');

205
src/iam/iam.module.ts Normal file
View File

@ -0,0 +1,205 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Format } from '../abstract-action.handler';
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 { AttachRolePolicyHandler } from './attach-role-policy.handler';
import { CreatePolicyVersionHandler } from './create-policy-version.handler';
import { CreatePolicyHandler } from './create-policy.handler';
import { CreateRoleHandler } from './create-role.handler';
import { GetPolicyHandler } from './get-policy.handler';
import { GetRoleHandler } from './get-role.handler';
import { IamPolicy } from './iam-policy.entity';
import { IamRolePolicyAttachment } from './iam-role-policy-attachment.entity';
import { IamRole } from './iam-role.entity';
import { IAMHandlers } from './iam.constants';
import { ListAttachedRolePoliciesHandler } from './list-attached-role-policies';
import { ListRolePoliciesHandler } from './list-role-policies.handler';
const handlers = [
AttachRolePolicyHandler,
CreatePolicyHandler,
CreatePolicyVersionHandler,
CreateRoleHandler,
GetPolicyHandler,
GetRoleHandler,
ListAttachedRolePoliciesHandler,
ListRolePoliciesHandler,
]
const actions = [
Action.IamAddClientIDToOpenIDConnectProvider,
Action.IamAddRoleToInstanceProfile,
Action.IamAddUserToGroup,
Action.IamAttachGroupPolicy,
Action.IamAttachRolePolicy,
Action.IamAttachUserPolicy,
Action.IamChangePassword,
Action.IamCreateAccessKey,
Action.IamCreateAccountAlias,
Action.IamCreateGroup,
Action.IamCreateInstanceProfile,
Action.IamCreateLoginProfile,
Action.IamCreateOpenIDConnectProvider,
Action.IamCreatePolicy,
Action.IamCreatePolicyVersion,
Action.IamCreateRole,
Action.IamCreateSAMLProvider,
Action.IamCreateServiceLinkedRole,
Action.IamCreateServiceSpecificCredential,
Action.IamCreateUser,
Action.IamCreateVirtualMFADevice,
Action.IamDeactivateMFADevice,
Action.IamDeleteAccessKey,
Action.IamDeleteAccountAlias,
Action.IamDeleteAccountPasswordPolicy,
Action.IamDeleteGroup,
Action.IamDeleteGroupPolicy,
Action.IamDeleteInstanceProfile,
Action.IamDeleteLoginProfile,
Action.IamDeleteOpenIDConnectProvider,
Action.IamDeletePolicy,
Action.IamDeletePolicyVersion,
Action.IamDeleteRole,
Action.IamDeleteRolePermissionsBoundary,
Action.IamDeleteRolePolicy,
Action.IamDeleteSAMLProvider,
Action.IamDeleteServerCertificate,
Action.IamDeleteServiceLinkedRole,
Action.IamDeleteServiceSpecificCredential,
Action.IamDeleteSigningCertificate,
Action.IamDeleteSSHPublicKey,
Action.IamDeleteUser,
Action.IamDeleteUserPermissionsBoundary,
Action.IamDeleteUserPolicy,
Action.IamDeleteVirtualMFADevice,
Action.IamDetachGroupPolicy,
Action.IamDetachRolePolicy,
Action.IamDetachUserPolicy,
Action.IamEnableMFADevice,
Action.IamGenerateCredentialReport,
Action.IamGenerateOrganizationsAccessReport,
Action.IamGenerateServiceLastAccessedDetails,
Action.IamGetAccessKeyLastUsed,
Action.IamGetAccountAuthorizationDetails,
Action.IamGetAccountPasswordPolicy,
Action.IamGetAccountSummary,
Action.IamGetContextKeysForCustomPolicy,
Action.IamGetContextKeysForPrincipalPolicy,
Action.IamGetCredentialReport,
Action.IamGetGroup,
Action.IamGetGroupPolicy,
Action.IamGetInstanceProfile,
Action.IamGetLoginProfile,
Action.IamGetOpenIDConnectProvider,
Action.IamGetOrganizationsAccessReport,
Action.IamGetPolicy,
Action.IamGetPolicyVersion,
Action.IamGetRole,
Action.IamGetRolePolicy,
Action.IamGetSAMLProvider,
Action.IamGetServerCertificate,
Action.IamGetServiceLastAccessedDetails,
Action.IamGetServiceLastAccessedDetailsWithEntities,
Action.IamGetServiceLinkedRoleDeletionStatus,
Action.IamGetSSHPublicKey,
Action.IamGetUser,
Action.IamGetUserPolicy,
Action.IamListAccessKeys,
Action.IamListAccountAliases,
Action.IamListAttachedGroupPolicies,
Action.IamListAttachedRolePolicies,
Action.IamListAttachedUserPolicies,
Action.IamListEntitiesForPolicy,
Action.IamListGroupPolicies,
Action.IamListGroups,
Action.IamListGroupsForUser,
Action.IamListInstanceProfiles,
Action.IamListInstanceProfilesForRole,
Action.IamListInstanceProfileTags,
Action.IamListMFADevices,
Action.IamListMFADeviceTags,
Action.IamListOpenIDConnectProviders,
Action.IamListOpenIDConnectProviderTags,
Action.IamListPolicies,
Action.IamListPoliciesGrantingServiceAccess,
Action.IamListPolicyTags,
Action.IamListPolicyVersions,
Action.IamListRolePolicies,
Action.IamListRoles,
Action.IamListRoleTags,
Action.IamListSAMLProviders,
Action.IamListSAMLProviderTags,
Action.IamListServerCertificates,
Action.IamListServerCertificateTags,
Action.IamListServiceSpecificCredentials,
Action.IamListSigningCertificates,
Action.IamListSSHPublicKeys,
Action.IamListUserPolicies,
Action.IamListUsers,
Action.IamListUserTags,
Action.IamListVirtualMFADevices,
Action.IamPutGroupPolicy,
Action.IamPutRolePermissionsBoundary,
Action.IamPutRolePolicy,
Action.IamPutUserPermissionsBoundary,
Action.IamPutUserPolicy,
Action.IamRemoveClientIDFromOpenIDConnectProvider,
Action.IamRemoveRoleFromInstanceProfile,
Action.IamRemoveUserFromGroup,
Action.IamResetServiceSpecificCredential,
Action.IamResyncMFADevice,
Action.IamSetDefaultPolicyVersion,
Action.IamSetSecurityTokenServicePreferences,
Action.IamSimulateCustomPolicy,
Action.IamSimulatePrincipalPolicy,
Action.IamTagInstanceProfile,
Action.IamTagMFADevice,
Action.IamTagOpenIDConnectProvider,
Action.IamTagPolicy,
Action.IamTagRole,
Action.IamTagSAMLProvider,
Action.IamTagServerCertificate,
Action.IamTagUser,
Action.IamUntagInstanceProfile,
Action.IamUntagMFADevice,
Action.IamUntagOpenIDConnectProvider,
Action.IamUntagPolicy,
Action.IamUntagRole,
Action.IamUntagSAMLProvider,
Action.IamUntagServerCertificate,
Action.IamUntagUser,
Action.IamUpdateAccessKey,
Action.IamUpdateAccountPasswordPolicy,
Action.IamUpdateAssumeRolePolicy,
Action.IamUpdateGroup,
Action.IamUpdateLoginProfile,
Action.IamUpdateOpenIDConnectProviderThumbprint,
Action.IamUpdateRole,
Action.IamUpdateRoleDescription,
Action.IamUpdateSAMLProvider,
Action.IamUpdateServerCertificate,
Action.IamUpdateServiceSpecificCredential,
Action.IamUpdateSigningCertificate,
Action.IamUpdateSSHPublicKey,
Action.IamUpdateUser,
Action.IamUploadServerCertificate,
Action.IamUploadSigningCertificate,
Action.IamUploadSSHPublicKey,
]
@Module({
imports: [
TypeOrmModule.forFeature([IamPolicy, IamRole, IamRolePolicyAttachment]),
AwsSharedEntitiesModule,
],
providers: [
...handlers,
ExistingActionHandlersProvider(handlers),
DefaultActionHandlerProvider(IAMHandlers, Format.Xml, actions),
],
exports: [IAMHandlers],
})
export class IamModule {}

View File

@ -0,0 +1,57 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
import { Action } from '../action.enum';
import * as Joi from 'joi';
import { InjectRepository } from '@nestjs/typeorm';
import { In, Repository } from 'typeorm';
import { IamRole } from './iam-role.entity';
import { IamRolePolicyAttachment } from './iam-role-policy-attachment.entity';
import { IamPolicy } from './iam-policy.entity';
import { breakdownArn } from '../util/breakdown-arn';
type QueryParams = {
RoleName: string;
}
@Injectable()
export class ListAttachedRolePoliciesHandler extends AbstractActionHandler<QueryParams> {
constructor(
@InjectRepository(IamRole)
private readonly roleRepo: Repository<IamRole>,
@InjectRepository(IamPolicy)
private readonly policyRepo: Repository<IamPolicy>,
@InjectRepository(IamRolePolicyAttachment)
private readonly attachmentRepo: Repository<IamRolePolicyAttachment>,
) {
super();
}
format = Format.Xml;
action = Action.IamListAttachedRolePolicies;
validator = Joi.object<QueryParams, true>({
RoleName: Joi.string().required(),
});
protected async handle({ RoleName }: QueryParams, awsProperties: AwsProperties) {
const role = await this.roleRepo.findOne({ where: { roleName: RoleName, accountId: awsProperties.accountId } });
if (!role) {
throw new NotFoundException('NoSuchEntity', 'The request was rejected because it referenced a resource entity that does not exist. The error message describes the resource.');
}
const attachments = await this.attachmentRepo.find({ where: { roleId: role.id } })
const policyIds = attachments.map(({ policyArn }) => breakdownArn(policyArn)).map(({ identifier }) => identifier.split('/')[1]);
const policies = await this.policyRepo.find({ where: { name: In(policyIds), isDefault: true } });
return {
AttachedPolicies: {
member: [role.assumeRolePolicyDocument, ...policies].map(p => ({
PolicyName: p.name,
PolicyArn: p.arn,
})),
}
}
}
}

View File

@ -0,0 +1,44 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
import { Action } from '../action.enum';
import * as Joi from 'joi';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { IamRole } from './iam-role.entity';
import { IamRolePolicyAttachment } from './iam-role-policy-attachment.entity';
type QueryParams = {
RoleName: string;
}
@Injectable()
export class ListRolePoliciesHandler extends AbstractActionHandler<QueryParams> {
constructor(
@InjectRepository(IamRole)
private readonly roleRepo: Repository<IamRole>,
@InjectRepository(IamRolePolicyAttachment)
private readonly attachmentRepo: Repository<IamRolePolicyAttachment>,
) {
super();
}
format = Format.Xml;
action = Action.IamListRolePolicies;
validator = Joi.object<QueryParams, true>({
RoleName: Joi.string().required(),
});
protected async handle({ RoleName }: QueryParams, awsProperties: AwsProperties) {
const role = await this.roleRepo.findOne({ where: { roleName: RoleName, accountId: awsProperties.accountId } });
if (!role) {
throw new NotFoundException('NoSuchEntity', 'The request was rejected because it referenced a resource entity that does not exist. The error message describes the resource.');
}
return {
PolicyNames: [],
}
}
}

View File

@ -0,0 +1,40 @@
import { Injectable } from '@nestjs/common';
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
import { Action } from '../action.enum';
import * as Joi from 'joi';
import { KmsKeyAlias } from './kms-key-alias.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
type QueryParams = {
AliasName: string;
TargetKeyId: string;
}
@Injectable()
export class CreateAliasHandler extends AbstractActionHandler<QueryParams> {
constructor(
@InjectRepository(KmsKeyAlias)
private readonly aliasRepo: Repository<KmsKeyAlias>,
) {
super();
}
format = Format.Json;
action = Action.KmsCreateAlias;
validator = Joi.object<QueryParams, true>({
AliasName: Joi.string().required(),
TargetKeyId: Joi.string().required(),
});
protected async handle({ AliasName, TargetKeyId }: QueryParams, awsProperties: AwsProperties) {
await this.aliasRepo.save({
name: AliasName.split('/')[1],
targetKeyId: TargetKeyId,
accountId: awsProperties.accountId,
region: awsProperties.region,
});
}
}

View File

@ -0,0 +1,66 @@
import { Injectable } from '@nestjs/common';
import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler';
import { Action } from '../action.enum';
import * as Joi from 'joi';
import { KmsKeyAlias } from './kms-key-alias.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { KmsKey } from './kms-key.entity';
import { ArnParts, breakdownArn } from '../util/breakdown-arn';
type QueryParams = {
KeyId: string;
}
@Injectable()
export class DescribeKeyHandler extends AbstractActionHandler<QueryParams> {
constructor(
@InjectRepository(KmsKeyAlias)
private readonly aliasRepo: Repository<KmsKeyAlias>,
@InjectRepository(KmsKey)
private readonly keyRepo: Repository<KmsKey>,
) {
super();
}
format = Format.Json;
action = Action.KmsDescribeKey;
validator = Joi.object<QueryParams, true>({
KeyId: Joi.string().required(),
});
protected async handle({ KeyId }: QueryParams, awsProperties: AwsProperties) {
const searchable = KeyId.startsWith('arn') ? breakdownArn(KeyId) : {
service: 'kms',
region: awsProperties.region,
accountId: awsProperties.accountId,
identifier: KeyId,
};
const [ type, pk ] = searchable.identifier.split('/');
const keyId: Promise<string> = type === 'key' ?
Promise.resolve(pk) :
this.findKeyIdFromAlias(pk, searchable);
const keyRecord = await this.keyRepo.findOne({ where: {
id: await keyId,
region: searchable.region,
accountId: searchable.accountId,
}});
return {
KeyMetadata: keyRecord.metadata,
}
}
private async findKeyIdFromAlias(alias: string ,arn: ArnParts): Promise<string> {
const record = await this.aliasRepo.findOne({ where: {
name: alias,
accountId: arn.accountId,
region: arn.region,
}});
return record.targetKeyId;
}
}

View File

@ -0,0 +1,21 @@
import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm';
@Entity({ name: 'kms_key_alias' })
export class KmsKeyAlias extends BaseEntity {
@PrimaryColumn()
name: string;
@Column({ name: 'target_key_id' })
targetKeyId: string;
@Column({ name: 'account_id', nullable: false })
accountId: string;
@Column({ name: 'region', nullable: false })
region: string;
get arn() {
return `arn:aws:kms:${this.region}:${this.accountId}:alias/${this.name}`;
}
}

58
src/kms/kms-key.entity.ts Normal file
View File

@ -0,0 +1,58 @@
import { BaseEntity, Column, CreateDateColumn, Entity, PrimaryColumn } from 'typeorm';
@Entity({ name: 'kms_key'})
export class KmsKey extends BaseEntity {
@PrimaryColumn()
id: string;
@Column({ name: 'usage' })
usage: string;
@Column({ name: 'description' })
description: string;
@Column({ name: 'key_spec' })
keySpec: string;
@Column({ name: 'key' })
key: string;
@Column({ name: 'account_id', nullable: false })
accountId: string;
@Column({ name: 'region', nullable: false })
region: string;
@CreateDateColumn()
createdAt: string;
get arn() {
return `arn:aws:kms:${this.region}:${this.accountId}:key/${this.id}`;
}
get metadata() {
return {
AWSAccountId: this.accountId,
KeyId: this.id,
Arn: this.arn,
CreationDate: new Date(this.createdAt).toISOString(),
Enabled: true,
Description: this.description,
KeyUsage: this.usage,
KeyState: 'Enabled',
KeyManager: "CUSTOMER",
CustomerMasterKeySpec: this.keySpec,
KeySpec: this.keySpec,
DeletionDate: null,
SigningAlgorithms: [
"RSASSA_PSS_SHA_256",
"RSASSA_PSS_SHA_384",
"RSASSA_PSS_SHA_512",
"RSASSA_PKCS1_V1_5_SHA_256",
"RSASSA_PKCS1_V1_5_SHA_384",
"RSASSA_PKCS1_V1_5_SHA_512"
]
}
}
}

View File

@ -5,10 +5,15 @@ 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 { CreateAliasHandler } from './create-alias.handler';
import { DescribeKeyHandler } from './describe-key.handler';
import { KmsKeyAlias } from './kms-key-alias.entity';
import { KmsKey } from './kms-key.entity';
import { KMSHandlers } from './kms.constants';
const handlers = [
CreateAliasHandler,
DescribeKeyHandler,
]
const actions = [
@ -66,7 +71,7 @@ const actions = [
@Module({
imports: [
TypeOrmModule.forFeature([]),
TypeOrmModule.forFeature([KmsKey, KmsKeyAlias]),
AwsSharedEntitiesModule,
],
providers: [

21
src/util/breakdown-arn.ts Normal file
View File

@ -0,0 +1,21 @@
export type ArnParts = {
service: string;
region: string;
accountId: string;
identifier: string;
}
export const breakdownArn = (arn: string): ArnParts => {
if (!arn.startsWith('arn')) {
throw new Error('Invalid arn');
}
const [_arn, _aws, service, region, accountId, ...identifierData] = arn.split(':');
return {
service,
region,
accountId,
identifier: identifierData.join(':'),
}
}