@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,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenSearch Store - Persistência de índices e documentos em JSON
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const logger = require('../../utils/logger');
|
|
8
|
+
|
|
9
|
+
class OpenSearchStore {
|
|
10
|
+
constructor(dataDir) {
|
|
11
|
+
this.dataDir = dataDir;
|
|
12
|
+
this.indicesDir = path.join(dataDir, 'opensearch');
|
|
13
|
+
this.metadataFile = path.join(this.indicesDir, '__indices__.json');
|
|
14
|
+
|
|
15
|
+
if (!fs.existsSync(this.indicesDir)) {
|
|
16
|
+
fs.mkdirSync(this.indicesDir, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Carrega metadados de todos os índices
|
|
22
|
+
*/
|
|
23
|
+
loadMetadata() {
|
|
24
|
+
if (fs.existsSync(this.metadataFile)) {
|
|
25
|
+
try {
|
|
26
|
+
const data = fs.readFileSync(this.metadataFile, 'utf8');
|
|
27
|
+
return JSON.parse(data);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logger.warn(`Erro ao ler ${this.metadataFile}: ${error.message}`);
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Salva metadados de todos os índices
|
|
38
|
+
*/
|
|
39
|
+
saveMetadata(metadata) {
|
|
40
|
+
try {
|
|
41
|
+
fs.writeFileSync(this.metadataFile, JSON.stringify(metadata, null, 2), 'utf8');
|
|
42
|
+
logger.debug(`Metadados salvos em ${this.metadataFile}`);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
logger.error(`Erro ao salvar ${this.metadataFile}: ${error.message}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Obtém caminho do arquivo de índice
|
|
50
|
+
*/
|
|
51
|
+
getIndexPath(indexName) {
|
|
52
|
+
return path.join(this.indicesDir, `${indexName}.json`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Carrega documentos de um índice
|
|
57
|
+
*/
|
|
58
|
+
loadDocuments(indexName) {
|
|
59
|
+
const indexPath = this.getIndexPath(indexName);
|
|
60
|
+
|
|
61
|
+
if (!fs.existsSync(indexPath)) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const data = fs.readFileSync(indexPath, 'utf8');
|
|
67
|
+
return JSON.parse(data);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
logger.warn(`Erro ao ler índice ${indexName}: ${error.message}`);
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Salva documentos de um índice
|
|
76
|
+
*/
|
|
77
|
+
saveDocuments(indexName, documents) {
|
|
78
|
+
try {
|
|
79
|
+
const indexPath = this.getIndexPath(indexName);
|
|
80
|
+
fs.writeFileSync(indexPath, JSON.stringify(documents, null, 2), 'utf8');
|
|
81
|
+
logger.debug(`${documents.length} documentos salvos no índice ${indexName}`);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
logger.error(`Erro ao salvar índice ${indexName}: ${error.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Adiciona documento ao índice
|
|
89
|
+
*/
|
|
90
|
+
addDocument(indexName, document) {
|
|
91
|
+
const documents = this.loadDocuments(indexName);
|
|
92
|
+
|
|
93
|
+
// Remove se já existe (atualização)
|
|
94
|
+
const index = documents.findIndex(doc => doc._id === document._id);
|
|
95
|
+
if (index !== -1) {
|
|
96
|
+
documents[index] = document;
|
|
97
|
+
} else {
|
|
98
|
+
documents.push(document);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.saveDocuments(indexName, documents);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Remove documento do índice
|
|
106
|
+
*/
|
|
107
|
+
removeDocument(indexName, documentId) {
|
|
108
|
+
const documents = this.loadDocuments(indexName);
|
|
109
|
+
const filtered = documents.filter(doc => doc._id !== documentId);
|
|
110
|
+
|
|
111
|
+
if (filtered.length < documents.length) {
|
|
112
|
+
this.saveDocuments(indexName, filtered);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Deleta índice completamente
|
|
121
|
+
*/
|
|
122
|
+
deleteIndex(indexName) {
|
|
123
|
+
try {
|
|
124
|
+
const indexPath = this.getIndexPath(indexName);
|
|
125
|
+
if (fs.existsSync(indexPath)) {
|
|
126
|
+
fs.unlinkSync(indexPath);
|
|
127
|
+
logger.debug(`Índice ${indexName} deletado`);
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
logger.error(`Erro ao deletar índice ${indexName}: ${error.message}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Calcula tamanho total do índice em bytes
|
|
136
|
+
*/
|
|
137
|
+
getIndexSize(indexName) {
|
|
138
|
+
const documents = this.loadDocuments(indexName);
|
|
139
|
+
return documents.reduce((sum, doc) => sum + JSON.stringify(doc._source).length, 0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Limpa todos os índices
|
|
144
|
+
*/
|
|
145
|
+
clear() {
|
|
146
|
+
try {
|
|
147
|
+
if (fs.existsSync(this.indicesDir)) {
|
|
148
|
+
const files = fs.readdirSync(this.indicesDir);
|
|
149
|
+
files.forEach(file => {
|
|
150
|
+
if (file.endsWith('.json')) {
|
|
151
|
+
fs.unlinkSync(path.join(this.indicesDir, file));
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
logger.debug('Todos os índices foram limpos');
|
|
155
|
+
}
|
|
156
|
+
} catch (error) {
|
|
157
|
+
logger.error(`Erro ao limpar índices: ${error.message}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = OpenSearchStore;
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS Control Tower Simulator
|
|
3
|
+
* Gerencia landing zones, guardrails e conformidade
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const LocalStore = require('../../utils/local-store');
|
|
7
|
+
const logger = require('../../utils/logger');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { v4: uuidv4 } = require('uuid');
|
|
10
|
+
|
|
11
|
+
class ControlTowerSimulator {
|
|
12
|
+
constructor(config, organizationsSimulator) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.organizationsSimulator = organizationsSimulator;
|
|
15
|
+
|
|
16
|
+
const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR || config.dataDir || './.aws-local-simulator-data';
|
|
17
|
+
this.dataDir = path.join(dataDir, 'control-tower');
|
|
18
|
+
|
|
19
|
+
this.store = new LocalStore(this.dataDir);
|
|
20
|
+
|
|
21
|
+
// Landing zones
|
|
22
|
+
this.landingZones = new Map();
|
|
23
|
+
|
|
24
|
+
// Guardrails (Preventive e Detective)
|
|
25
|
+
this.guardrails = new Map();
|
|
26
|
+
this.guardrailStatus = new Map(); // Mapear OU -> guardrails ativos
|
|
27
|
+
|
|
28
|
+
// Compliance checks
|
|
29
|
+
this.complianceChecks = new Map();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async initialize() {
|
|
33
|
+
logger.debug('Inicializando Control Tower Simulator...');
|
|
34
|
+
|
|
35
|
+
this.createDefaultGuardrails();
|
|
36
|
+
logger.debug(`✅ Control Tower Simulator inicializado`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Cria guardrails padrão
|
|
41
|
+
*/
|
|
42
|
+
createDefaultGuardrails() {
|
|
43
|
+
// Preventive Guardrails
|
|
44
|
+
this.createGuardrail('DisablePublicS3Buckets', 'PREVENTIVE', {
|
|
45
|
+
description: 'Bloqueia criação de buckets S3 públicos',
|
|
46
|
+
service: 's3',
|
|
47
|
+
action: 'PutBucketPublicAccessBlock'
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
this.createGuardrail('RequireMFA', 'PREVENTIVE', {
|
|
51
|
+
description: 'Requer MFA para operações sensíveis',
|
|
52
|
+
service: 'iam',
|
|
53
|
+
action: 'iam:*'
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
this.createGuardrail('RestrictEC2InstanceTypes', 'PREVENTIVE', {
|
|
57
|
+
description: 'Restringe tipos de instância EC2 a tipos aprovados',
|
|
58
|
+
service: 'ec2',
|
|
59
|
+
action: 'ec2:RunInstances'
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Detective Guardrails
|
|
63
|
+
this.createGuardrail('LogAllAPIActivity', 'DETECTIVE', {
|
|
64
|
+
description: 'Detecta falta de CloudTrail logging',
|
|
65
|
+
service: 'cloudtrail',
|
|
66
|
+
action: 'cloudtrail:StartLogging'
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
this.createGuardrail('DetectRootAccountUsage', 'DETECTIVE', {
|
|
70
|
+
description: 'Detecta uso de conta root',
|
|
71
|
+
service: 'iam',
|
|
72
|
+
action: 'root-account-usage'
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
this.createGuardrail('DetectUnencryptedData', 'DETECTIVE', {
|
|
76
|
+
description: 'Detecta dados não criptografados',
|
|
77
|
+
service: 'kms',
|
|
78
|
+
action: 'kms:Encrypt'
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Cria um guardrail
|
|
84
|
+
*/
|
|
85
|
+
createGuardrail(name, type, config = {}) {
|
|
86
|
+
const guardrailId = uuidv4().substring(0, 12);
|
|
87
|
+
|
|
88
|
+
const guardrail = {
|
|
89
|
+
id: guardrailId,
|
|
90
|
+
name: name,
|
|
91
|
+
type: type, // PREVENTIVE ou DETECTIVE
|
|
92
|
+
arn: `arn:aws:controltower::123456789012:guardrail/${guardrailId}`,
|
|
93
|
+
description: config.description || '',
|
|
94
|
+
service: config.service,
|
|
95
|
+
action: config.action,
|
|
96
|
+
createdAt: new Date().toISOString(),
|
|
97
|
+
status: 'ACTIVE'
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
this.guardrails.set(guardrailId, guardrail);
|
|
101
|
+
return guardrail;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Estabelece landing zone
|
|
106
|
+
*/
|
|
107
|
+
setupLandingZone(organizationRootId, config = {}) {
|
|
108
|
+
const landingZoneId = uuidv4().substring(0, 12);
|
|
109
|
+
|
|
110
|
+
const landingZone = {
|
|
111
|
+
id: landingZoneId,
|
|
112
|
+
arn: `arn:aws:controltower::123456789012:landingzone/${landingZoneId}`,
|
|
113
|
+
organizationRootId: organizationRootId,
|
|
114
|
+
status: 'ACTIVE',
|
|
115
|
+
createdAt: new Date().toISOString(),
|
|
116
|
+
lastUpdatedAt: new Date().toISOString(),
|
|
117
|
+
version: '2.0',
|
|
118
|
+
// OUs criadas pela Landing Zone
|
|
119
|
+
ous: {
|
|
120
|
+
managementOu: null,
|
|
121
|
+
securityOu: null,
|
|
122
|
+
sandboxOu: null
|
|
123
|
+
},
|
|
124
|
+
enabledGuardrails: [],
|
|
125
|
+
complianceStatus: 'COMPLIANT'
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
this.landingZones.set(landingZoneId, landingZone);
|
|
129
|
+
|
|
130
|
+
logger.info(`✅ Landing Zone estabelecida: ${landingZoneId}`);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
LandingZone: landingZone
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Habilita guardrail em uma OU
|
|
139
|
+
*/
|
|
140
|
+
enableGuardrail(guardrailId, targetOUId) {
|
|
141
|
+
const guardrail = this.guardrails.get(guardrailId);
|
|
142
|
+
if (!guardrail) {
|
|
143
|
+
throw new Error(`Guardrail '${guardrailId}' not found`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Valida OU
|
|
147
|
+
const organization = this.organizationsSimulator.organization;
|
|
148
|
+
const ou = organization.getOrganizationalUnit(targetOUId);
|
|
149
|
+
if (!ou) {
|
|
150
|
+
throw new Error(`OU '${targetOUId}' not found`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!this.guardrailStatus.has(targetOUId)) {
|
|
154
|
+
this.guardrailStatus.set(targetOUId, []);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const enabledGuardrails = this.guardrailStatus.get(targetOUId);
|
|
158
|
+
if (!enabledGuardrails.includes(guardrailId)) {
|
|
159
|
+
enabledGuardrails.push(guardrailId);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
logger.info(`✅ Guardrail habilitado: ${guardrail.name} em OU ${targetOUId}`);
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
GuardrailStatus: {
|
|
166
|
+
GuardrailId: guardrailId,
|
|
167
|
+
TargetOUId: targetOUId,
|
|
168
|
+
Status: 'ENABLED'
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Desabilita guardrail em uma OU
|
|
175
|
+
*/
|
|
176
|
+
disableGuardrail(guardrailId, targetOUId) {
|
|
177
|
+
const guardrails = this.guardrailStatus.get(targetOUId);
|
|
178
|
+
if (!guardrails) {
|
|
179
|
+
throw new Error(`No guardrails enabled for OU '${targetOUId}'`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const index = guardrails.indexOf(guardrailId);
|
|
183
|
+
if (index > -1) {
|
|
184
|
+
guardrails.splice(index, 1);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
logger.info(`✅ Guardrail desabilitado: ${guardrailId} em OU ${targetOUId}`);
|
|
188
|
+
|
|
189
|
+
return {};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Lista guardrails disponíveis
|
|
194
|
+
*/
|
|
195
|
+
listGuardrails() {
|
|
196
|
+
return {
|
|
197
|
+
Guardrails: Array.from(this.guardrails.values())
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Lista guardrails habilitados em uma OU
|
|
203
|
+
*/
|
|
204
|
+
getGuardrailsForOU(ouId) {
|
|
205
|
+
const enabledIds = this.guardrailStatus.get(ouId) || [];
|
|
206
|
+
const enabledGuardrails = enabledIds.map(id => this.guardrails.get(id));
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
Guardrails: enabledGuardrails.filter(g => g)
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Verifica conformidade de um account
|
|
215
|
+
*/
|
|
216
|
+
checkCompliance(accountId) {
|
|
217
|
+
const organization = this.organizationsSimulator.organization;
|
|
218
|
+
const account = organization.getAccount(accountId);
|
|
219
|
+
|
|
220
|
+
if (!account) {
|
|
221
|
+
throw new Error(`Account '${accountId}' not found`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Coleta guardrails aplicáveis
|
|
225
|
+
const applicableGuardrails = [];
|
|
226
|
+
|
|
227
|
+
let currentOUId = account.parentId;
|
|
228
|
+
while (currentOUId) {
|
|
229
|
+
const guardrails = this.guardrailStatus.get(currentOUId) || [];
|
|
230
|
+
applicableGuardrails.push(...guardrails);
|
|
231
|
+
|
|
232
|
+
const ou = organization.getOrganizationalUnit(currentOUId);
|
|
233
|
+
currentOUId = ou?.parentId;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Simula verificação de conformidade
|
|
237
|
+
const results = {
|
|
238
|
+
AccountId: accountId,
|
|
239
|
+
ComplianceStatus: 'COMPLIANT',
|
|
240
|
+
Guardrails: applicableGuardrails.map(guardrailId => {
|
|
241
|
+
const guardrail = this.guardrails.get(guardrailId);
|
|
242
|
+
return {
|
|
243
|
+
GuardrailId: guardrailId,
|
|
244
|
+
GuardrailName: guardrail?.name,
|
|
245
|
+
Status: 'COMPLIANT', // Em produção seria baseado em verificações reais
|
|
246
|
+
LastCheckedAt: new Date().toISOString()
|
|
247
|
+
};
|
|
248
|
+
})
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
return results;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Gera relatório de conformidade
|
|
256
|
+
*/
|
|
257
|
+
generateComplianceReport() {
|
|
258
|
+
const organization = this.organizationsSimulator.organization;
|
|
259
|
+
const accounts = organization.listAccounts();
|
|
260
|
+
|
|
261
|
+
const report = {
|
|
262
|
+
ReportId: uuidv4().substring(0, 12),
|
|
263
|
+
GeneratedAt: new Date().toISOString(),
|
|
264
|
+
AccountsChecked: accounts.length,
|
|
265
|
+
CompliantAccounts: 0,
|
|
266
|
+
NonCompliantAccounts: 0,
|
|
267
|
+
AccountResults: accounts.map(account => {
|
|
268
|
+
const compliance = this.checkCompliance(account.id);
|
|
269
|
+
if (compliance.ComplianceStatus === 'COMPLIANT') {
|
|
270
|
+
report.CompliantAccounts++;
|
|
271
|
+
} else {
|
|
272
|
+
report.NonCompliantAccounts++;
|
|
273
|
+
}
|
|
274
|
+
return compliance;
|
|
275
|
+
})
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
return report;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Obtém status da landing zone
|
|
283
|
+
*/
|
|
284
|
+
getLandingZoneStatus(landingZoneId) {
|
|
285
|
+
const landingZone = this.landingZones.get(landingZoneId);
|
|
286
|
+
if (!landingZone) {
|
|
287
|
+
throw new Error(`Landing Zone '${landingZoneId}' not found`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
LandingZone: landingZone
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Obtém estatísticas
|
|
297
|
+
*/
|
|
298
|
+
getStats() {
|
|
299
|
+
return {
|
|
300
|
+
landingZonesCount: this.landingZones.size,
|
|
301
|
+
guardrailsCount: this.guardrails.size,
|
|
302
|
+
preventiveGuardrailsCount: Array.from(this.guardrails.values()).filter(g => g.type === 'PREVENTIVE').length,
|
|
303
|
+
detectiveGuardrailsCount: Array.from(this.guardrails.values()).filter(g => g.type === 'DETECTIVE').length,
|
|
304
|
+
enabledGuardrailsCount: Array.from(this.guardrailStatus.values()).reduce((sum, arr) => sum + arr.length, 0)
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Reset
|
|
310
|
+
*/
|
|
311
|
+
async reset() {
|
|
312
|
+
this.landingZones.clear();
|
|
313
|
+
this.guardrailStatus.clear();
|
|
314
|
+
this.guardrails.clear();
|
|
315
|
+
|
|
316
|
+
this.createDefaultGuardrails();
|
|
317
|
+
logger.info('✅ Control Tower Simulator resetado');
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
module.exports = ControlTowerSimulator;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Control Tower Store - Persistência de Landing Zones e Guardrails
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const logger = require('../../utils/logger');
|
|
8
|
+
|
|
9
|
+
class ControlTowerStore {
|
|
10
|
+
constructor(dataDir) {
|
|
11
|
+
this.dataDir = dataDir;
|
|
12
|
+
this.controlTowerDir = path.join(dataDir, 'control-tower');
|
|
13
|
+
this.metadataFile = path.join(this.controlTowerDir, '__metadata__.json');
|
|
14
|
+
|
|
15
|
+
if (!fs.existsSync(this.controlTowerDir)) {
|
|
16
|
+
fs.mkdirSync(this.controlTowerDir, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Carrega landing zones
|
|
22
|
+
*/
|
|
23
|
+
loadLandingZones() {
|
|
24
|
+
const landingZonesFile = path.join(this.controlTowerDir, '__landing_zones__.json');
|
|
25
|
+
|
|
26
|
+
if (fs.existsSync(landingZonesFile)) {
|
|
27
|
+
try {
|
|
28
|
+
const data = fs.readFileSync(landingZonesFile, 'utf8');
|
|
29
|
+
return JSON.parse(data);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
logger.warn(`Erro ao ler landing zones: ${error.message}`);
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Salva landing zones
|
|
40
|
+
*/
|
|
41
|
+
saveLandingZones(landingZones) {
|
|
42
|
+
try {
|
|
43
|
+
const landingZonesFile = path.join(this.controlTowerDir, '__landing_zones__.json');
|
|
44
|
+
const data = {};
|
|
45
|
+
|
|
46
|
+
for (const [lzId, lz] of landingZones) {
|
|
47
|
+
data[lzId] = lz;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fs.writeFileSync(landingZonesFile, JSON.stringify(data, null, 2), 'utf8');
|
|
51
|
+
logger.debug(`${Object.keys(data).length} landing zones salvas`);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logger.error(`Erro ao salvar landing zones: ${error.message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Carrega guardrails
|
|
59
|
+
*/
|
|
60
|
+
loadGuardrails() {
|
|
61
|
+
const guardrailsFile = path.join(this.controlTowerDir, '__guardrails__.json');
|
|
62
|
+
|
|
63
|
+
if (fs.existsSync(guardrailsFile)) {
|
|
64
|
+
try {
|
|
65
|
+
const data = fs.readFileSync(guardrailsFile, 'utf8');
|
|
66
|
+
return JSON.parse(data);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
logger.warn(`Erro ao ler guardrails: ${error.message}`);
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Salva guardrails
|
|
77
|
+
*/
|
|
78
|
+
saveGuardrails(guardrails) {
|
|
79
|
+
try {
|
|
80
|
+
const guardrailsFile = path.join(this.controlTowerDir, '__guardrails__.json');
|
|
81
|
+
const data = {};
|
|
82
|
+
|
|
83
|
+
for (const [guardrailId, guardrail] of guardrails) {
|
|
84
|
+
data[guardrailId] = guardrail;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fs.writeFileSync(guardrailsFile, JSON.stringify(data, null, 2), 'utf8');
|
|
88
|
+
logger.debug(`${Object.keys(data).length} guardrails salvos`);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
logger.error(`Erro ao salvar guardrails: ${error.message}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Carrega status de guardrails por OU
|
|
96
|
+
*/
|
|
97
|
+
loadGuardrailStatus() {
|
|
98
|
+
const statusFile = path.join(this.controlTowerDir, '__guardrail_status__.json');
|
|
99
|
+
|
|
100
|
+
if (fs.existsSync(statusFile)) {
|
|
101
|
+
try {
|
|
102
|
+
const data = fs.readFileSync(statusFile, 'utf8');
|
|
103
|
+
return JSON.parse(data);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
logger.warn(`Erro ao ler status de guardrails: ${error.message}`);
|
|
106
|
+
return {};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Salva status de guardrails
|
|
114
|
+
*/
|
|
115
|
+
saveGuardrailStatus(guardrailStatus) {
|
|
116
|
+
try {
|
|
117
|
+
const statusFile = path.join(this.controlTowerDir, '__guardrail_status__.json');
|
|
118
|
+
const data = {};
|
|
119
|
+
|
|
120
|
+
for (const [ouId, guardrails] of guardrailStatus) {
|
|
121
|
+
data[ouId] = guardrails;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
fs.writeFileSync(statusFile, JSON.stringify(data, null, 2), 'utf8');
|
|
125
|
+
logger.debug(`Status de guardrails salvo`);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
logger.error(`Erro ao salvar status de guardrails: ${error.message}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Carrega compliance checks
|
|
133
|
+
*/
|
|
134
|
+
loadComplianceChecks() {
|
|
135
|
+
const checksFile = path.join(this.controlTowerDir, '__compliance_checks__.json');
|
|
136
|
+
|
|
137
|
+
if (fs.existsSync(checksFile)) {
|
|
138
|
+
try {
|
|
139
|
+
const data = fs.readFileSync(checksFile, 'utf8');
|
|
140
|
+
return JSON.parse(data);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
logger.warn(`Erro ao ler compliance checks: ${error.message}`);
|
|
143
|
+
return {};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Salva compliance checks
|
|
151
|
+
*/
|
|
152
|
+
saveComplianceChecks(checks) {
|
|
153
|
+
try {
|
|
154
|
+
const checksFile = path.join(this.controlTowerDir, '__compliance_checks__.json');
|
|
155
|
+
fs.writeFileSync(checksFile, JSON.stringify(checks, null, 2), 'utf8');
|
|
156
|
+
logger.debug(`Compliance checks salvos`);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
logger.error(`Erro ao salvar compliance checks: ${error.message}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Limpa tudo
|
|
164
|
+
*/
|
|
165
|
+
clear() {
|
|
166
|
+
try {
|
|
167
|
+
if (fs.existsSync(this.controlTowerDir)) {
|
|
168
|
+
const files = fs.readdirSync(this.controlTowerDir);
|
|
169
|
+
files.forEach(file => {
|
|
170
|
+
if (file.endsWith('.json')) {
|
|
171
|
+
fs.unlinkSync(path.join(this.controlTowerDir, file));
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
logger.debug('Todos os dados de Control Tower foram limpos');
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
logger.error(`Erro ao limpar Control Tower: ${error.message}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
module.exports = ControlTowerStore;
|