@gugananuvem/aws-local-simulator 1.0.35 → 1.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +662 -658
- package/package.json +10 -3
- package/src/config/default-config.js +71 -0
- package/src/config/env-loader.js +4 -2
- package/src/middleware/authorization.js +271 -0
- package/src/middleware/cloudtrail-logger.js +333 -0
- package/src/middleware/error-handler.js +138 -0
- package/src/middleware/index.js +79 -0
- package/src/middleware/policy-enforcement.js +407 -0
- package/src/server.js +74 -4
- package/src/services/acm/index.js +66 -0
- package/src/services/acm/models/certificate.js +202 -0
- package/src/services/acm/server.js +224 -0
- package/src/services/acm/simulator.js +293 -0
- package/src/services/alb/index.js +104 -0
- package/src/services/alb/models/loadBalancer.js +88 -0
- package/src/services/alb/models/targetGroup.js +128 -0
- package/src/services/alb/server.js +160 -0
- package/src/services/alb/simulator.js +262 -0
- package/src/services/apigateway/index.js +22 -0
- package/src/services/apigateway/server.js +247 -35
- package/src/services/apigateway/simulator.js +780 -26
- package/src/services/asg/index.js +100 -0
- package/src/services/asg/models/autoScalingGroup.js +110 -0
- package/src/services/asg/models/launchTemplate.js +41 -0
- package/src/services/asg/models/scalingPolicy.js +51 -0
- package/src/services/asg/server.js +236 -0
- package/src/services/asg/simulator.js +410 -0
- package/src/services/aurora/cluster-manager.js +731 -0
- package/src/services/aurora/data-api.js +514 -0
- package/src/services/aurora/db-engine.js +270 -0
- package/src/services/aurora/index.js +159 -0
- package/src/services/aurora/rds-api.js +189 -0
- package/src/services/aurora/server.js +213 -0
- package/src/services/aurora/sql-translator.js +334 -0
- package/src/services/cloudformation/index.js +2 -2
- package/src/services/cloudformation/server.js +1 -0
- package/src/services/cloudfront/cache-engine.js +225 -0
- package/src/services/cloudfront/geo-restrictions.js +377 -0
- package/src/services/cloudfront/index.js +220 -0
- package/src/services/cloudfront/models/behavior.js +158 -0
- package/src/services/cloudfront/models/distribution.js +284 -0
- package/src/services/cloudfront/models/index.js +99 -0
- package/src/services/cloudfront/models/invalidation.js +109 -0
- package/src/services/cloudfront/models/origin.js +155 -0
- package/src/services/cloudfront/origin-router.js +552 -0
- package/src/services/cloudfront/origin-validator.js +398 -0
- package/src/services/cloudfront/server.js +512 -0
- package/src/services/cloudfront/signed-url-generator.js +377 -0
- package/src/services/cloudfront/simulator.js +425 -0
- package/src/services/cloudfront/store.js +326 -0
- package/src/services/cloudshell/env-builder.js +27 -0
- package/src/services/cloudshell/index.js +48 -0
- package/src/services/cloudshell/routes.js +60 -0
- package/src/services/cloudshell/session-manager.js +226 -0
- package/src/services/cloudshell/static/.gitkeep +0 -0
- package/src/services/cloudshell/static/index.html +406 -0
- package/src/services/cloudshell/websocket-handler.js +107 -0
- package/src/services/cloudtrail/index.js +2 -3
- package/src/services/cloudtrail/server.js +68 -0
- package/src/services/cognito/server.js +4 -1
- package/src/services/dynamodb/server.js +4 -1
- package/src/services/dynamodb/simulator.js +2 -1
- package/src/services/ec2/index.js +101 -0
- package/src/services/ec2/instanceServer.js +311 -0
- package/src/services/ec2/models/ami.js +39 -0
- package/src/services/ec2/models/instance.js +98 -0
- package/src/services/ec2/models/networkInterface.js +74 -0
- package/src/services/ec2/models/securityGroup.js +71 -0
- package/src/services/ec2/server.js +350 -0
- package/src/services/ec2/simulator.js +436 -0
- package/src/services/eventbridge/server.js +36 -0
- package/src/services/eventbridge/simulator.js +230 -45
- package/src/services/iam/index.js +65 -0
- package/src/services/iam/middleware.js +99 -0
- package/src/services/iam/server.js +331 -0
- package/src/services/iam/simulator.js +1432 -0
- package/src/services/index.js +28 -0
- package/src/services/kinesis-analytics/index.js +105 -0
- package/src/services/kinesis-analytics/models/application.js +325 -0
- package/src/services/kinesis-analytics/models/index.js +12 -0
- package/src/services/kinesis-analytics/server.js +250 -0
- package/src/services/kinesis-analytics/simulator.js +407 -0
- package/src/services/kinesis-analytics/store.js +176 -0
- package/src/services/kinesis-data-streams/index.js +106 -0
- package/src/services/kinesis-data-streams/models/index.js +12 -0
- package/src/services/kinesis-data-streams/models/record.js +70 -0
- package/src/services/kinesis-data-streams/models/stream.js +280 -0
- package/src/services/kinesis-data-streams/server.js +255 -0
- package/src/services/kinesis-data-streams/simulator.js +514 -0
- package/src/services/kinesis-data-streams/store.js +163 -0
- package/src/services/kinesis-firehose/index.js +106 -0
- package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
- package/src/services/kinesis-firehose/models/index.js +10 -0
- package/src/services/kinesis-firehose/server.js +215 -0
- package/src/services/kinesis-firehose/simulator.js +388 -0
- package/src/services/kinesis-firehose/store.js +163 -0
- package/src/services/kms/server.js +4 -1
- package/src/services/lambda/index.js +26 -0
- package/src/services/lambda/server.js +12 -1
- package/src/services/lambda/simulator.js +4 -0
- package/src/services/opensearch/index.js +105 -0
- package/src/services/opensearch/models/document.js +115 -0
- package/src/services/opensearch/models/index.js +73 -0
- package/src/services/opensearch/server.js +310 -0
- package/src/services/opensearch/simulator.js +605 -0
- package/src/services/opensearch/store.js +162 -0
- package/src/services/organizations/control-tower-simulator.js +321 -0
- package/src/services/organizations/control-tower-store.js +182 -0
- package/src/services/organizations/iam-integration.js +413 -0
- package/src/services/organizations/index.js +172 -0
- package/src/services/organizations/models/index.js +13 -0
- package/src/services/organizations/models/invitation.js +48 -0
- package/src/services/organizations/models/organization.js +566 -0
- package/src/services/organizations/models/role.js +244 -0
- package/src/services/organizations/models/scp.js +131 -0
- package/src/services/organizations/server.js +736 -0
- package/src/services/organizations/simulator.js +593 -0
- package/src/services/organizations/store.js +235 -0
- package/src/services/organizations/vpc-integration.js +453 -0
- package/src/services/route53/index.js +67 -0
- package/src/services/route53/models/healthCheck.js +101 -0
- package/src/services/route53/models/hostedZone.js +37 -0
- package/src/services/route53/models/resourceRecord.js +106 -0
- package/src/services/route53/server.js +841 -0
- package/src/services/route53/simulator.js +350 -0
- package/src/services/s3/index.js +62 -0
- package/src/services/s3/presigned-url-generator.js +337 -0
- package/src/services/s3/server.js +231 -0
- package/src/services/s3/simulator.js +551 -4
- package/src/services/sns/index.js +31 -0
- package/src/services/sns/server.js +9 -0
- package/src/services/sqs/index.js +38 -0
- package/src/services/sqs/server.js +57 -4
- package/src/services/sqs/simulator.js +423 -33
- package/src/services/sts/simulator.js +209 -17
- package/src/services/vpc/README.md +459 -0
- package/src/services/vpc/ec2ResponseFormatter.js +431 -0
- package/src/services/vpc/index.js +90 -0
- package/src/services/vpc/models/internetGateway.js +71 -0
- package/src/services/vpc/models/routeTable.js +160 -0
- package/src/services/vpc/models/subnet.js +48 -0
- package/src/services/vpc/models/vpc.js +37 -0
- package/src/services/vpc/models/vpcEndpoint.js +100 -0
- package/src/services/vpc/models/vpcGateway.js +134 -0
- package/src/services/vpc/server.js +836 -0
- package/src/services/vpc/simulator.js +613 -0
- package/src/utils/policy-cache.js +215 -0
- package/aws-config +0 -154
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS Organizations - Organization Model
|
|
3
|
+
* Representa a raiz da organização e gerencia estrutura hierárquica
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { v4: uuidv4 } = require('uuid');
|
|
7
|
+
const ServiceControlPolicy = require('./scp');
|
|
8
|
+
|
|
9
|
+
class Organization {
|
|
10
|
+
constructor(config = {}) {
|
|
11
|
+
this.id = config.id || uuidv4().substring(0, 12);
|
|
12
|
+
this.arn = config.arn || `arn:aws:organizations::123456789012:organization/o-${this.id}`;
|
|
13
|
+
this.rootId = config.rootId || `r-${uuidv4().substring(0, 4)}`;
|
|
14
|
+
this.masterAccountId = config.masterAccountId || '123456789012';
|
|
15
|
+
this.masterAccountArn = config.masterAccountArn ||
|
|
16
|
+
`arn:aws:organizations::123456789012:account/o-${this.id}/${this.masterAccountId}`;
|
|
17
|
+
this.masterAccountEmail = config.masterAccountEmail || 'master@example.com';
|
|
18
|
+
this.masterAccountName = config.masterAccountName || 'Master Account';
|
|
19
|
+
|
|
20
|
+
this.createdAt = config.createdAt || new Date().toISOString();
|
|
21
|
+
this.status = config.status || 'ACTIVE';
|
|
22
|
+
this.featureSet = config.featureSet || 'ALL'; // ALL ou CONSOLIDATED_BILLING
|
|
23
|
+
|
|
24
|
+
// Contas da organização
|
|
25
|
+
this.accounts = new Map();
|
|
26
|
+
this.accountCount = 1; // Conta master
|
|
27
|
+
|
|
28
|
+
// OUs (Organizational Units)
|
|
29
|
+
this.ous = new Map();
|
|
30
|
+
this.ous.set(this.rootId, {
|
|
31
|
+
id: this.rootId,
|
|
32
|
+
name: 'Root',
|
|
33
|
+
arn: `arn:aws:organizations::123456789012:ou/o-${this.id}/${this.rootId}`,
|
|
34
|
+
parentId: null,
|
|
35
|
+
children: []
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Policies (SCPs, Tag Policies, etc)
|
|
39
|
+
this.policies = new Map();
|
|
40
|
+
this.policyTargets = new Map(); // Mapear policy -> targets
|
|
41
|
+
|
|
42
|
+
// Policy types habilitados na organização (mapa Type -> Status).
|
|
43
|
+
// SERVICE_CONTROL_POLICY (SCP) já vem habilitado ao criar a organização.
|
|
44
|
+
this.policyTypes = new Map();
|
|
45
|
+
const enabledTypes = Array.isArray(config.policyTypes) && config.policyTypes.length > 0
|
|
46
|
+
? config.policyTypes
|
|
47
|
+
: ['SERVICE_CONTROL_POLICY'];
|
|
48
|
+
for (const entry of enabledTypes) {
|
|
49
|
+
const type = typeof entry === 'string' ? entry : entry?.Type;
|
|
50
|
+
const status = typeof entry === 'string' ? 'ENABLED' : (entry?.Status || 'ENABLED');
|
|
51
|
+
if (type) this.policyTypes.set(type, status);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Trusted access: serviços integrados habilitados (mapa Principal -> DateEnabled ISO).
|
|
55
|
+
this.serviceAccess = new Map();
|
|
56
|
+
const enabledServices = Array.isArray(config.serviceAccess) ? config.serviceAccess : [];
|
|
57
|
+
for (const entry of enabledServices) {
|
|
58
|
+
const principal = typeof entry === 'string' ? entry : entry?.ServicePrincipal;
|
|
59
|
+
const dateEnabled = typeof entry === 'string'
|
|
60
|
+
? new Date().toISOString()
|
|
61
|
+
: (entry?.DateEnabled || new Date().toISOString());
|
|
62
|
+
if (principal) this.serviceAccess.set(principal, dateEnabled);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Cria conta master
|
|
66
|
+
this.createMasterAccount();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Cria a conta master
|
|
71
|
+
*/
|
|
72
|
+
createMasterAccount() {
|
|
73
|
+
const masterAccount = {
|
|
74
|
+
id: this.masterAccountId,
|
|
75
|
+
arn: this.masterAccountArn,
|
|
76
|
+
email: this.masterAccountEmail,
|
|
77
|
+
name: this.masterAccountName,
|
|
78
|
+
status: 'ACTIVE',
|
|
79
|
+
type: 'MASTER',
|
|
80
|
+
parentId: this.rootId,
|
|
81
|
+
createdAt: this.createdAt,
|
|
82
|
+
joinedMethod: 'CREATED'
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
this.accounts.set(this.masterAccountId, masterAccount);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Cria uma nova OU
|
|
90
|
+
*/
|
|
91
|
+
createOrganizationalUnit(parentId, name, config = {}) {
|
|
92
|
+
if (!this.ous.has(parentId)) {
|
|
93
|
+
throw new Error(`Parent OU '${parentId}' not found`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const ouId = `ou-${uuidv4().substring(0, 8)}`;
|
|
97
|
+
const ou = {
|
|
98
|
+
id: ouId,
|
|
99
|
+
name: name,
|
|
100
|
+
arn: `arn:aws:organizations::123456789012:ou/o-${this.id}/${ouId}`,
|
|
101
|
+
parentId: parentId,
|
|
102
|
+
children: [],
|
|
103
|
+
createdAt: new Date().toISOString()
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
this.ous.set(ouId, ou);
|
|
107
|
+
|
|
108
|
+
const parent = this.ous.get(parentId);
|
|
109
|
+
if (parent) parent.children.push(ouId);
|
|
110
|
+
|
|
111
|
+
return ou;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Obtém OU por ID
|
|
116
|
+
*/
|
|
117
|
+
getOrganizationalUnit(ouId) {
|
|
118
|
+
return this.ous.get(ouId);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Lista OUs filhas de um parent
|
|
123
|
+
*/
|
|
124
|
+
listOrganizationalUnitsForParent(parentId) {
|
|
125
|
+
return Array.from(this.ous.values()).filter(ou => ou.parentId === parentId);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Cria uma nova conta
|
|
130
|
+
*/
|
|
131
|
+
createAccount(email, accountName, config = {}) {
|
|
132
|
+
for (const acc of this.accounts.values()) {
|
|
133
|
+
if (acc.email === email) {
|
|
134
|
+
throw new Error(`Account with email '${email}' already exists`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const accountId = config.accountId || Math.floor(100000000000 + Math.random() * 899999999999).toString();
|
|
139
|
+
const parentOUId = config.parentOUId || this.rootId;
|
|
140
|
+
|
|
141
|
+
if (!this.ous.has(parentOUId)) {
|
|
142
|
+
throw new Error(`Parent OU '${parentOUId}' not found`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const account = {
|
|
146
|
+
id: accountId,
|
|
147
|
+
arn: `arn:aws:organizations::123456789012:account/o-${this.id}/${accountId}`,
|
|
148
|
+
email: email,
|
|
149
|
+
name: accountName,
|
|
150
|
+
status: config.status || 'ACTIVE',
|
|
151
|
+
type: 'MEMBER',
|
|
152
|
+
parentId: parentOUId,
|
|
153
|
+
createdAt: new Date().toISOString(),
|
|
154
|
+
joinedMethod: config.joinedMethod || 'CREATED',
|
|
155
|
+
roleName: config.roleName || 'OrganizationAccountAccessRole',
|
|
156
|
+
tags: config.tags || []
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
this.accounts.set(accountId, account);
|
|
160
|
+
this.accountCount++;
|
|
161
|
+
|
|
162
|
+
const parent = this.ous.get(parentOUId);
|
|
163
|
+
if (parent && !parent.children.includes(accountId)) parent.children.push(accountId);
|
|
164
|
+
|
|
165
|
+
return account;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Move uma conta para outra OU
|
|
170
|
+
*/
|
|
171
|
+
moveAccount(accountId, newParentId) {
|
|
172
|
+
const account = this.accounts.get(accountId);
|
|
173
|
+
if (!account) {
|
|
174
|
+
throw new Error(`Account '${accountId}' not found`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const newParent = this.ous.get(newParentId);
|
|
178
|
+
if (!newParent) {
|
|
179
|
+
throw new Error(`Target OU '${newParentId}' not found`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const oldParent = this.ous.get(account.parentId);
|
|
183
|
+
if (oldParent) {
|
|
184
|
+
oldParent.children = oldParent.children.filter(id => id !== accountId);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
account.parentId = newParentId;
|
|
188
|
+
if (!newParent.children.includes(accountId)) newParent.children.push(accountId);
|
|
189
|
+
|
|
190
|
+
return account;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Remove uma conta
|
|
195
|
+
*/
|
|
196
|
+
removeAccount(accountId) {
|
|
197
|
+
const account = this.accounts.get(accountId);
|
|
198
|
+
if (!account) {
|
|
199
|
+
throw new Error(`Account '${accountId}' not found`);
|
|
200
|
+
}
|
|
201
|
+
if (account.type === 'MASTER') {
|
|
202
|
+
throw new Error('The management account cannot be removed from the organization');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const parent = this.ous.get(account.parentId);
|
|
206
|
+
if (parent) parent.children = parent.children.filter(id => id !== accountId);
|
|
207
|
+
|
|
208
|
+
this.accounts.delete(accountId);
|
|
209
|
+
this.accountCount--;
|
|
210
|
+
|
|
211
|
+
return account;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Obtém conta por ID
|
|
216
|
+
*/
|
|
217
|
+
getAccount(accountId) {
|
|
218
|
+
return this.accounts.get(accountId);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Lista todas as contas
|
|
223
|
+
*/
|
|
224
|
+
listAccounts() {
|
|
225
|
+
return Array.from(this.accounts.values());
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Lista contas filhas de um parent
|
|
230
|
+
*/
|
|
231
|
+
listAccountsForParent(parentId) {
|
|
232
|
+
return Array.from(this.accounts.values()).filter(acc => acc.parentId === parentId);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Remove uma OU
|
|
237
|
+
*/
|
|
238
|
+
deleteOrganizationalUnit(ouId) {
|
|
239
|
+
const ou = this.ous.get(ouId);
|
|
240
|
+
if (!ou) {
|
|
241
|
+
throw new Error(`OU '${ouId}' not found`);
|
|
242
|
+
}
|
|
243
|
+
if (ou.parentId === null) {
|
|
244
|
+
throw new Error('The root cannot be deleted');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const childOus = this.listOrganizationalUnitsForParent(ouId);
|
|
248
|
+
if (childOus.length > 0) {
|
|
249
|
+
throw new Error(`OU '${ouId}' has child OUs. Remove them first.`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const accountsInOu = this.listAccountsForParent(ouId);
|
|
253
|
+
if (accountsInOu.length > 0) {
|
|
254
|
+
throw new Error(`OU '${ouId}' has accounts. Move them first.`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
this.ous.delete(ouId);
|
|
258
|
+
|
|
259
|
+
if (ou.parentId) {
|
|
260
|
+
const parent = this.ous.get(ou.parentId);
|
|
261
|
+
if (parent) parent.children = parent.children.filter(id => id !== ouId);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Cria uma SCP (Service Control Policy)
|
|
269
|
+
*/
|
|
270
|
+
createPolicy(name, document, type = 'SERVICE_CONTROL_POLICY', config = {}) {
|
|
271
|
+
const policyId = `p-${uuidv4().substring(0, 8)}`;
|
|
272
|
+
|
|
273
|
+
// Usa o modelo SCP para ter evaluateAction/matchesPattern disponíveis.
|
|
274
|
+
const policy = new ServiceControlPolicy(name, document, config);
|
|
275
|
+
policy.id = policyId;
|
|
276
|
+
policy.arn = `arn:aws:organizations::123456789012:policy/o-${this.id}/service_control_policy/${policyId}`;
|
|
277
|
+
policy.type = type;
|
|
278
|
+
policy.targets = [];
|
|
279
|
+
|
|
280
|
+
this.policies.set(policyId, policy);
|
|
281
|
+
this.policyTargets.set(policyId, []);
|
|
282
|
+
|
|
283
|
+
return policy;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Obtém policy por ID
|
|
288
|
+
*/
|
|
289
|
+
getPolicy(policyId) {
|
|
290
|
+
return this.policies.get(policyId);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Lista policies
|
|
295
|
+
*/
|
|
296
|
+
listPolicies(type) {
|
|
297
|
+
return Array.from(this.policies.values())
|
|
298
|
+
.filter(p => !type || p.type === type);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Lista os policy types habilitados na raiz (formato da API ListRoots).
|
|
303
|
+
*/
|
|
304
|
+
listPolicyTypes() {
|
|
305
|
+
// A API ListRoots devolve apenas os tipos habilitados.
|
|
306
|
+
return Array.from(this.policyTypes.entries())
|
|
307
|
+
.filter(([, Status]) => Status === 'ENABLED')
|
|
308
|
+
.map(([Type, Status]) => ({ Type, Status }));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Verifica se um policy type está habilitado.
|
|
313
|
+
*/
|
|
314
|
+
isPolicyTypeEnabled(type) {
|
|
315
|
+
return this.policyTypes.get(type) === 'ENABLED';
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Habilita um policy type (EnablePolicyType).
|
|
320
|
+
*/
|
|
321
|
+
enablePolicyType(type) {
|
|
322
|
+
if (!type) throw new Error('PolicyType is required');
|
|
323
|
+
this.policyTypes.set(type, 'ENABLED');
|
|
324
|
+
return { Type: type, Status: 'ENABLED' };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Desabilita um policy type (DisablePolicyType).
|
|
329
|
+
*/
|
|
330
|
+
disablePolicyType(type) {
|
|
331
|
+
if (!type) throw new Error('PolicyType is required');
|
|
332
|
+
this.policyTypes.delete(type);
|
|
333
|
+
return { Type: type, Status: 'DISABLED' };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Lista os serviços com trusted access habilitado (EnableAWSServiceAccess).
|
|
338
|
+
*/
|
|
339
|
+
listServiceAccess() {
|
|
340
|
+
return Array.from(this.serviceAccess.entries()).map(([ServicePrincipal, DateEnabled]) => ({
|
|
341
|
+
ServicePrincipal,
|
|
342
|
+
DateEnabled
|
|
343
|
+
}));
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Verifica se um serviço está com trusted access habilitado.
|
|
348
|
+
*/
|
|
349
|
+
isServiceAccessEnabled(principal) {
|
|
350
|
+
return this.serviceAccess.has(principal);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Habilita trusted access de um serviço (EnableAWSServiceAccess).
|
|
355
|
+
*/
|
|
356
|
+
enableServiceAccess(principal) {
|
|
357
|
+
if (!principal) throw new Error('ServicePrincipal is required');
|
|
358
|
+
const dateEnabled = this.serviceAccess.get(principal) || new Date().toISOString();
|
|
359
|
+
this.serviceAccess.set(principal, dateEnabled);
|
|
360
|
+
return { ServicePrincipal: principal, DateEnabled: dateEnabled };
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Desabilita trusted access de um serviço (DisableAWSServiceAccess).
|
|
365
|
+
*/
|
|
366
|
+
disableServiceAccess(principal) {
|
|
367
|
+
if (!principal) throw new Error('ServicePrincipal is required');
|
|
368
|
+
this.serviceAccess.delete(principal);
|
|
369
|
+
return { ServicePrincipal: principal };
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Anexa policy a um target (OU ou Account)
|
|
374
|
+
*/
|
|
375
|
+
attachPolicy(policyId, targetId) {
|
|
376
|
+
const policy = this.policies.get(policyId);
|
|
377
|
+
if (!policy) {
|
|
378
|
+
throw new Error(`Policy '${policyId}' not found`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const ou = this.ous.get(targetId);
|
|
382
|
+
const account = this.accounts.get(targetId);
|
|
383
|
+
|
|
384
|
+
if (!ou && !account) {
|
|
385
|
+
throw new Error(`Target '${targetId}' not found`);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const targets = this.policyTargets.get(policyId) || [];
|
|
389
|
+
if (!targets.includes(targetId)) {
|
|
390
|
+
targets.push(targetId);
|
|
391
|
+
this.policyTargets.set(policyId, targets);
|
|
392
|
+
policy.targets = targets;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return policy;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Desanexa policy de um target
|
|
400
|
+
*/
|
|
401
|
+
detachPolicy(policyId, targetId) {
|
|
402
|
+
const policy = this.policies.get(policyId);
|
|
403
|
+
if (!policy) {
|
|
404
|
+
throw new Error(`Policy '${policyId}' not found`);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const targets = this.policyTargets.get(policyId) || [];
|
|
408
|
+
const index = targets.indexOf(targetId);
|
|
409
|
+
|
|
410
|
+
if (index > -1) {
|
|
411
|
+
targets.splice(index, 1);
|
|
412
|
+
policy.targets = targets;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return policy;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Lista policies anexadas a um target
|
|
420
|
+
*/
|
|
421
|
+
listPoliciesForTarget(targetId, type = 'SERVICE_CONTROL_POLICY') {
|
|
422
|
+
const policies = [];
|
|
423
|
+
|
|
424
|
+
for (const [policyId, policy] of this.policies) {
|
|
425
|
+
if (policy.type === type) {
|
|
426
|
+
const targets = this.policyTargets.get(policyId) || policy.targets || [];
|
|
427
|
+
if (targets.includes(targetId)) {
|
|
428
|
+
policies.push(policy);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return policies;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Descreve a organização
|
|
438
|
+
*/
|
|
439
|
+
describe() {
|
|
440
|
+
return {
|
|
441
|
+
Organization: {
|
|
442
|
+
Id: this.id,
|
|
443
|
+
Arn: this.arn,
|
|
444
|
+
RootId: this.rootId,
|
|
445
|
+
MasterAccountId: this.masterAccountId,
|
|
446
|
+
MasterAccountArn: this.masterAccountArn,
|
|
447
|
+
MasterAccountEmail: this.masterAccountEmail,
|
|
448
|
+
CreatedAt: this.createdAt,
|
|
449
|
+
Status: this.status,
|
|
450
|
+
FeatureSet: this.featureSet
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Snapshot serializável para persistência (mesmo padrão JSON do IAM).
|
|
457
|
+
*/
|
|
458
|
+
toSnapshot() {
|
|
459
|
+
return {
|
|
460
|
+
id: this.id,
|
|
461
|
+
arn: this.arn,
|
|
462
|
+
rootId: this.rootId,
|
|
463
|
+
masterAccountId: this.masterAccountId,
|
|
464
|
+
masterAccountArn: this.masterAccountArn,
|
|
465
|
+
masterAccountEmail: this.masterAccountEmail,
|
|
466
|
+
masterAccountName: this.masterAccountName,
|
|
467
|
+
createdAt: this.createdAt,
|
|
468
|
+
status: this.status,
|
|
469
|
+
featureSet: this.featureSet,
|
|
470
|
+
accounts: Array.from(this.accounts.values()),
|
|
471
|
+
ous: Array.from(this.ous.values()),
|
|
472
|
+
policies: Array.from(this.policies.values()),
|
|
473
|
+
policyTypes: this.listPolicyTypes(),
|
|
474
|
+
serviceAccess: this.listServiceAccess()
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Reconstrói a organização a partir de um snapshot persistido.
|
|
480
|
+
*/
|
|
481
|
+
static fromSnapshot(snapshot) {
|
|
482
|
+
if (!snapshot || !snapshot.id) return new Organization();
|
|
483
|
+
|
|
484
|
+
const organization = new Organization({
|
|
485
|
+
id: snapshot.id,
|
|
486
|
+
arn: snapshot.arn,
|
|
487
|
+
rootId: snapshot.rootId,
|
|
488
|
+
masterAccountId: snapshot.masterAccountId,
|
|
489
|
+
masterAccountArn: snapshot.masterAccountArn,
|
|
490
|
+
masterAccountEmail: snapshot.masterAccountEmail,
|
|
491
|
+
masterAccountName: snapshot.masterAccountName,
|
|
492
|
+
createdAt: snapshot.createdAt,
|
|
493
|
+
status: snapshot.status,
|
|
494
|
+
featureSet: snapshot.featureSet,
|
|
495
|
+
policyTypes: snapshot.policyTypes,
|
|
496
|
+
serviceAccess: snapshot.serviceAccess
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
if (Array.isArray(snapshot.ous) && snapshot.ous.length > 0) {
|
|
500
|
+
organization.ous = new Map(snapshot.ous.map(ou => [ou.id, ou]));
|
|
501
|
+
}
|
|
502
|
+
if (Array.isArray(snapshot.accounts) && snapshot.accounts.length > 0) {
|
|
503
|
+
organization.accounts = new Map(snapshot.accounts.map(a => [a.id, a]));
|
|
504
|
+
}
|
|
505
|
+
if (Array.isArray(snapshot.policies)) {
|
|
506
|
+
organization.policies = new Map();
|
|
507
|
+
organization.policyTargets = new Map();
|
|
508
|
+
for (const stored of snapshot.policies) {
|
|
509
|
+
// Reidrata como instância de SCP para preservar evaluateAction().
|
|
510
|
+
const policy = new ServiceControlPolicy(stored.name, stored.document, {
|
|
511
|
+
awsManaged: stored.awsManaged
|
|
512
|
+
});
|
|
513
|
+
policy.id = stored.id;
|
|
514
|
+
policy.arn = stored.arn;
|
|
515
|
+
policy.type = stored.type || 'SERVICE_CONTROL_POLICY';
|
|
516
|
+
policy.status = stored.status || 'ACTIVE';
|
|
517
|
+
policy.createdAt = stored.createdAt || policy.createdAt;
|
|
518
|
+
policy.lastModifiedAt = stored.lastModifiedAt || policy.createdAt;
|
|
519
|
+
policy.targets = stored.targets || [];
|
|
520
|
+
|
|
521
|
+
organization.policies.set(policy.id, policy);
|
|
522
|
+
organization.policyTargets.set(policy.id, policy.targets);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
organization.accountCount = organization.accounts.size;
|
|
526
|
+
|
|
527
|
+
// Reparações defensivas: garante root existente e sem duplicidade nos children.
|
|
528
|
+
if (!organization.ous.has(organization.rootId)) {
|
|
529
|
+
organization.ous.set(organization.rootId, {
|
|
530
|
+
id: organization.rootId,
|
|
531
|
+
name: 'Root',
|
|
532
|
+
arn: `arn:aws:organizations::123456789012:ou/o-${organization.id}/${organization.rootId}`,
|
|
533
|
+
parentId: null,
|
|
534
|
+
children: []
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
for (const ou of organization.ous.values()) {
|
|
538
|
+
const childOus = organization.listOrganizationalUnitsForParent(ou.id).map(o => o.id);
|
|
539
|
+
const childAccounts = organization.listAccountsForParent(ou.id).map(a => a.id);
|
|
540
|
+
ou.children = [...childOus, ...childAccounts];
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return organization;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
toJSON() {
|
|
547
|
+
return {
|
|
548
|
+
id: this.id,
|
|
549
|
+
arn: this.arn,
|
|
550
|
+
rootId: this.rootId,
|
|
551
|
+
masterAccountId: this.masterAccountId,
|
|
552
|
+
masterAccountEmail: this.masterAccountEmail,
|
|
553
|
+
masterAccountName: this.masterAccountName,
|
|
554
|
+
createdAt: this.createdAt,
|
|
555
|
+
status: this.status,
|
|
556
|
+
featureSet: this.featureSet,
|
|
557
|
+
accountCount: this.accountCount,
|
|
558
|
+
ouCount: this.ous.size,
|
|
559
|
+
policyCount: this.policies.size,
|
|
560
|
+
policyTypes: this.listPolicyTypes(),
|
|
561
|
+
serviceAccess: this.listServiceAccess()
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
module.exports = Organization;
|