Skip to content

Commit 43fd5f4

Browse files
author
Marco Franceschi
authored
Merge pull request #151 from cloudgraphdev/feat/EP-3189
feat: Added missing data to SES
2 parents f930336 + 9eee1b3 commit 43fd5f4

28 files changed

Lines changed: 703 additions & 108 deletions

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi
170170
| secretsManager | kms, lambda |
171171
| securityGroup | alb, asg, clientVpnEndpoint, codebuild, dmsReplicationInstance, ecsService, lambda, ec2, elasticSearchDomain, elb, rdsCluster, rdsDbInstance, eksCluster, elastiCacheCluster, managedAirflow, sageMakerNotebookInstance, networkInterface, vpcEndpoint, mskCluster |
172172
| securityHub | |
173-
| ses | |
173+
| ses | |
174+
| sesReceiptRuleSet | |
175+
| sesDomain | |
176+
| sesEmail | cognitoUserPool |
174177
| sns | kms, cloudtrail, cloudwatch, s3 |
175178
| sqs | elasticBeanstalkEnv, s3 |
176179
| subnet | alb, asg, codebuild, dmsReplicationInstance, ec2, ecsService, efsMountTarget, elastiCacheCluster, elasticSearchDomain, elb, lambda, managedAirflow, natGateway, networkInterface, rdsCluster, sageMakerNotebookInstance, routeTable, vpc, vpcEndpoint, eksCluster, emrCluster, flowLog, mskCluster |

src/enums/schemasMap.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ export default {
104104
[services.s3]: 'awsS3',
105105
[services.secretsManager]: 'awsSecretsManager',
106106
[services.ses]: 'awsSes',
107+
[services.sesEmail]: 'awsSesEmail',
108+
[services.sesDomain]: 'awsSesDomain',
107109
[services.sns]: 'awsSns',
108110
[services.systemsManagerInstance]: 'awsSystemsManagerInstance',
109111
[services.systemsManagerDocument]: 'awsSystemsManagerDocument',

src/enums/serviceMap.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ import SecretsManager from '../services/secretsManager'
103103
import AwsSecurityGroup from '../services/securityGroup'
104104
import SecurityHub from '../services/securityHub'
105105
import SES from '../services/ses'
106+
import SESReceiptRuleSet from '../services/sesReceiptRuleSet'
107+
import SESEmail from '../services/sesEmail'
108+
import SESDomain from '../services/sesDomain'
106109
import SNS from '../services/sns'
107110
import SQS from '../services/sqs'
108111
import AwsSubnet from '../services/subnet'
@@ -218,6 +221,9 @@ export default {
218221
[services.secretsManager]: SecretsManager,
219222
[services.securityHub]: SecurityHub,
220223
[services.ses]: SES,
224+
[services.sesReceiptRuleSet]: SESReceiptRuleSet,
225+
[services.sesEmail]: SESEmail,
226+
[services.sesDomain]: SESDomain,
221227
[services.iamAccessAnalyzer]: IamAccessAnalyzer,
222228
[services.iamUser]: IamUser,
223229
[services.iamGroup]: IamGroup,

src/enums/services.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ export default {
102102
secretsManager: 'secretsManager',
103103
securityHub: 'securityHub',
104104
ses: 'ses',
105+
sesReceiptRuleSet: 'sesReceiptRuleSet',
106+
sesEmail: 'sesEmail',
107+
sesDomain: 'sesDomain',
105108
sg: 'sg',
106109
sns: 'sns',
107110
sqs: 'sqs',

src/services/account/schema.graphql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ type awsAccount implements awsOptionalService @key(fields: "id") {
103103
systemsManagerInstances: [awsSystemsManagerInstance]
104104
systemsManagerParameters: [awsSystemsManagerParameter]
105105
ses: [awsSes]
106+
sesReceiptRuleSet: [awsSesReceiptRuleSet]
107+
sesEmail: [awsSesEmail]
108+
sesDomain: [awsSesDomain]
106109
sns: [awsSns]
107110
sqs: [awsSqs]
108111
subnets: [awsSubnet]

src/services/cognitoUserPool/connections.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { isEmpty } from 'lodash'
55
import services from '../../enums/services'
66
import { sesArn } from '../../utils/generateArns'
77
import { RawAwsLambdaFunction } from '../lambda/data'
8-
import { RawAwsSes } from '../ses/data'
8+
import { RawAwsSesEmail } from '../sesEmail/data'
99
import { RawAwsIamRole } from '../iamRole/data'
1010
import { AwsKms } from '../kms/data'
1111

@@ -121,20 +121,20 @@ export default ({
121121
* related to this cognito user pool
122122
*/
123123
const emailConfigSourceArn = emailConfiguration?.SourceArn
124-
const emails = data.find(({ name }) => name === services.ses)
124+
const emails = data.find(({ name }) => name === services.sesEmail)
125125

126126
if (emailConfigSourceArn && emails?.data?.[region]) {
127-
const emailInRegion: RawAwsSes = emails.data[region].find(
128-
({ Identity }: RawAwsSes) =>
129-
emailConfigSourceArn === sesArn({ region, account, email: Identity })
127+
const emailInRegion: RawAwsSesEmail = emails.data[region].find(
128+
({ Identity }: RawAwsSesEmail) =>
129+
emailConfigSourceArn === sesArn({ region, account, identity: Identity })
130130
)
131131

132132
if (emailInRegion) {
133133
connections.push({
134-
id: sesArn({ region, account, email: emailInRegion.Identity }),
135-
resourceType: services.ses,
134+
id: sesArn({ region, account, identity: emailInRegion.Identity }),
135+
resourceType: services.sesEmail,
136136
relation: 'child',
137-
field: 'ses',
137+
field: 'sesEmail',
138138
})
139139
}
140140
}

src/services/cognitoUserPool/schema.graphql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ type awsCognitoUserPool implements awsBaseService @key(fields: "id") {
122122
lambdas: [awsLambda] @hasInverse(field: cognitoUserPools)
123123
appSync: [awsAppSync] @hasInverse(field: cognitoUserPool)
124124
kms: [awsKms] @hasInverse(field: cognitoUserPools)
125-
ses: [awsSes] @hasInverse(field: cognitoUserPools)
125+
sesEmail: [awsSesEmail] @hasInverse(field: cognitoUserPools)
126126
iamRole: [awsIamRole] @hasInverse(field: cognitoUserPools)
127127
elasticSearchDomains: [awsElasticSearchDomain] @hasInverse(field: cognitoUserPool)
128128
}

src/services/ses/data.ts

Lines changed: 65 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,77 @@
11
import SES, {
2-
ListIdentitiesResponse,
3-
IdentityVerificationAttributes,
4-
GetIdentityVerificationAttributesResponse,
2+
ListConfigurationSetsResponse,
3+
ConfigurationSet,
4+
ListTemplatesResponse,
5+
TemplateMetadata,
56
} from 'aws-sdk/clients/ses'
67
import { AWSError } from 'aws-sdk/lib/error'
78
import { Config } from 'aws-sdk/lib/config'
89

910
import CloudGraph from '@cloudgraph/sdk'
1011
import groupBy from 'lodash/groupBy'
11-
import isEmpty from 'lodash/isEmpty'
1212

1313
import awsLoggerText from '../../properties/logger'
1414
import { initTestEndpoint } from '../../utils'
1515
import AwsErrorLog from '../../utils/errorLog'
1616

1717
const lt = { ...awsLoggerText }
1818
const { logger } = CloudGraph
19-
const serviceName = 'SES'
19+
const serviceName = 'SES '
2020
const errorLog = new AwsErrorLog(serviceName)
2121
const endpoint = initTestEndpoint(serviceName)
2222

2323
/**
24-
* SES
24+
* SES
2525
*/
26-
export interface RawAwsSes extends IdentityVerificationAttributes {
27-
Identity: string
26+
export interface RawAwsSes {
27+
ConfigurationSets: ConfigurationSet[]
28+
EmailTemplates: TemplateMetadata[]
2829
region: string
2930
}
3031

32+
33+
const getEmailTemplates = async (ses: SES): Promise<TemplateMetadata[]> =>
34+
new Promise(resolve => {
35+
try {
36+
ses.listTemplates(
37+
(err: AWSError, data: ListTemplatesResponse) => {
38+
if (err) {
39+
errorLog.generateAwsErrorLog({
40+
functionName: 'ses:listTemplates',
41+
err,
42+
})
43+
return resolve([])
44+
}
45+
const { TemplatesMetadata = [] } = data || {}
46+
resolve(TemplatesMetadata)
47+
}
48+
)
49+
} catch (error) {
50+
resolve([])
51+
}
52+
})
53+
54+
const getConfigurationSets = async (ses: SES): Promise<ConfigurationSet[]> =>
55+
new Promise(resolve => {
56+
try {
57+
ses.listConfigurationSets(
58+
(err: AWSError, data: ListConfigurationSetsResponse) => {
59+
if (err) {
60+
errorLog.generateAwsErrorLog({
61+
functionName: 'ses:listConfigurationSets',
62+
err,
63+
})
64+
return resolve([])
65+
}
66+
const { ConfigurationSets = [] } = data || {}
67+
resolve(ConfigurationSets)
68+
}
69+
)
70+
} catch (error) {
71+
resolve([])
72+
}
73+
})
74+
3175
export default async ({
3276
regions,
3377
config,
@@ -38,84 +82,28 @@ export default async ({
3882
new Promise(async resolve => {
3983
const sesData: RawAwsSes[] = []
4084
const regionPromises = []
41-
const identityVerificationPromises = []
4285

4386
regions.split(',').map(region => {
44-
const regionPromise = new Promise<void>(resolveRegion => {
87+
const regionPromise = new Promise<void>(async resolveRegion => {
4588
const ses = new SES({ ...config, region, endpoint })
4689

47-
ses.listIdentities(
48-
{},
49-
(err: AWSError, data: ListIdentitiesResponse) => {
50-
/**
51-
* No Data for the region
52-
*/
53-
if (isEmpty(data)) {
54-
return resolveRegion()
55-
}
56-
57-
if (err) {
58-
errorLog.generateAwsErrorLog({
59-
functionName: 'ses:listIdentities',
60-
err,
61-
})
62-
}
63-
64-
const { Identities }: { Identities: string[] } = data
65-
66-
/**
67-
* No Identities Found
68-
*/
69-
70-
if (isEmpty(Identities)) {
71-
return resolveRegion()
72-
}
73-
74-
logger.debug(lt.fetchedSesIdentities(Identities.length))
75-
76-
const identityVerificationPromise = new Promise<void>(
77-
resolveIdVer => {
78-
ses.getIdentityVerificationAttributes(
79-
{ Identities },
80-
(
81-
err: AWSError,
82-
{
83-
VerificationAttributes: identities,
84-
}: GetIdentityVerificationAttributesResponse
85-
) => {
86-
if (err) {
87-
errorLog.generateAwsErrorLog({
88-
functionName: 'ses:getIdentityVerificationAttributes',
89-
err,
90-
})
91-
}
92-
93-
if (!isEmpty(identities)) {
94-
sesData.push(
95-
...Identities.map(Identity => ({
96-
Identity,
97-
...identities[Identity],
98-
region,
99-
}))
100-
)
101-
}
102-
103-
resolveIdVer()
104-
resolveRegion()
105-
}
106-
)
107-
}
108-
)
109-
identityVerificationPromises.push(identityVerificationPromise)
110-
}
111-
)
90+
const configurationSets = await getConfigurationSets(ses)
91+
const emailTemplates = await getEmailTemplates(ses)
92+
93+
sesData.push({
94+
ConfigurationSets: configurationSets,
95+
EmailTemplates: emailTemplates,
96+
region
97+
})
98+
resolveRegion()
99+
100+
112101
})
113102
regionPromises.push(regionPromise)
114103
})
115104

116-
await Promise.all(regionPromises)
117105

118-
await Promise.all(identityVerificationPromises)
106+
await Promise.all(regionPromises)
119107
errorLog.reset()
120108

121109
resolve(groupBy(sesData, 'region'))

src/services/ses/format.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,37 @@
11
import { RawAwsSes } from './data'
22
import { AwsSes } from '../../types/generated'
3-
import { sesArn } from '../../utils/generateArns'
3+
import { generateUniqueId } from '@cloudgraph/sdk'
44

55
/**
6-
* SES
6+
* SES
77
*/
88

9-
export default ({
9+
export default ({
1010
service,
1111
account,
1212
region,
13-
}:{
13+
}: {
1414
service: RawAwsSes
1515
account: string
1616
region: string
1717
}): AwsSes => {
1818
const {
19-
Identity: email,
20-
VerificationStatus: verificationStatus,
19+
ConfigurationSets = [],
20+
EmailTemplates = [],
2121
} = service
22-
const arn = sesArn({region, account, email})
22+
23+
const configurationSets = ConfigurationSets.map(cs => cs.Name)
24+
const emailTemplates = EmailTemplates.map(e => e.Name)
2325

2426
return {
25-
id: arn,
27+
id: generateUniqueId({
28+
...service,
29+
account,
30+
region
31+
}),
2632
accountId: account,
27-
arn,
2833
region,
29-
email,
30-
verificationStatus,
34+
configurationSets,
35+
emailTemplates,
3136
}
3237
}

src/services/ses/schema.graphql

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
type awsSes implements awsBaseService @key(fields: "arn") {
2-
email: String @search(by: [hash, regexp])
3-
verificationStatus: String @search(by: [hash, regexp])
4-
cognitoUserPools: [awsCognitoUserPool] @hasInverse(field: ses)
1+
type awsSes implements awsOptionalService @key(fields: "id") {
2+
configurationSets: [String] @search(by: [hash])
3+
emailTemplates: [String] @search(by: [hash])
54
}

0 commit comments

Comments
 (0)