s3db.js 13.4.0 → 13.6.0
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 +25 -10
- package/dist/{s3db.cjs.js → s3db.cjs} +38801 -32446
- package/dist/s3db.cjs.map +1 -0
- package/dist/s3db.es.js +38653 -32291
- package/dist/s3db.es.js.map +1 -1
- package/package.json +218 -22
- package/src/concerns/id.js +90 -6
- package/src/concerns/index.js +2 -1
- package/src/concerns/password-hashing.js +150 -0
- package/src/database.class.js +6 -2
- package/src/plugins/api/auth/basic-auth.js +40 -10
- package/src/plugins/api/auth/index.js +49 -3
- package/src/plugins/api/auth/oauth2-auth.js +171 -0
- package/src/plugins/api/auth/oidc-auth.js +789 -0
- package/src/plugins/api/auth/oidc-client.js +462 -0
- package/src/plugins/api/auth/path-auth-matcher.js +284 -0
- package/src/plugins/api/concerns/event-emitter.js +134 -0
- package/src/plugins/api/concerns/failban-manager.js +651 -0
- package/src/plugins/api/concerns/guards-helpers.js +402 -0
- package/src/plugins/api/concerns/metrics-collector.js +346 -0
- package/src/plugins/api/index.js +510 -57
- package/src/plugins/api/middlewares/failban.js +305 -0
- package/src/plugins/api/middlewares/rate-limit.js +301 -0
- package/src/plugins/api/middlewares/request-id.js +74 -0
- package/src/plugins/api/middlewares/security-headers.js +120 -0
- package/src/plugins/api/middlewares/session-tracking.js +194 -0
- package/src/plugins/api/routes/auth-routes.js +119 -78
- package/src/plugins/api/routes/resource-routes.js +73 -30
- package/src/plugins/api/server.js +1139 -45
- package/src/plugins/api/utils/custom-routes.js +102 -0
- package/src/plugins/api/utils/guards.js +213 -0
- package/src/plugins/api/utils/mime-types.js +154 -0
- package/src/plugins/api/utils/openapi-generator.js +91 -12
- package/src/plugins/api/utils/path-matcher.js +173 -0
- package/src/plugins/api/utils/static-filesystem.js +262 -0
- package/src/plugins/api/utils/static-s3.js +231 -0
- package/src/plugins/api/utils/template-engine.js +188 -0
- package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +853 -0
- package/src/plugins/cloud-inventory/drivers/aws-driver.js +2554 -0
- package/src/plugins/cloud-inventory/drivers/azure-driver.js +637 -0
- package/src/plugins/cloud-inventory/drivers/base-driver.js +99 -0
- package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +620 -0
- package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +698 -0
- package/src/plugins/cloud-inventory/drivers/gcp-driver.js +645 -0
- package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +559 -0
- package/src/plugins/cloud-inventory/drivers/linode-driver.js +614 -0
- package/src/plugins/cloud-inventory/drivers/mock-drivers.js +449 -0
- package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +771 -0
- package/src/plugins/cloud-inventory/drivers/oracle-driver.js +768 -0
- package/src/plugins/cloud-inventory/drivers/vultr-driver.js +636 -0
- package/src/plugins/cloud-inventory/index.js +20 -0
- package/src/plugins/cloud-inventory/registry.js +146 -0
- package/src/plugins/cloud-inventory/terraform-exporter.js +362 -0
- package/src/plugins/cloud-inventory.plugin.js +1333 -0
- package/src/plugins/concerns/plugin-dependencies.js +62 -2
- package/src/plugins/eventual-consistency/analytics.js +1 -0
- package/src/plugins/eventual-consistency/consolidation.js +2 -2
- package/src/plugins/eventual-consistency/garbage-collection.js +2 -2
- package/src/plugins/eventual-consistency/install.js +2 -2
- package/src/plugins/identity/README.md +335 -0
- package/src/plugins/identity/concerns/mfa-manager.js +204 -0
- package/src/plugins/identity/concerns/password.js +138 -0
- package/src/plugins/identity/concerns/resource-schemas.js +273 -0
- package/src/plugins/identity/concerns/token-generator.js +172 -0
- package/src/plugins/identity/email-service.js +422 -0
- package/src/plugins/identity/index.js +1052 -0
- package/src/plugins/identity/oauth2-server.js +1033 -0
- package/src/plugins/identity/oidc-discovery.js +285 -0
- package/src/plugins/identity/rsa-keys.js +323 -0
- package/src/plugins/identity/server.js +500 -0
- package/src/plugins/identity/session-manager.js +453 -0
- package/src/plugins/identity/ui/layouts/base.js +251 -0
- package/src/plugins/identity/ui/middleware.js +135 -0
- package/src/plugins/identity/ui/pages/admin/client-form.js +247 -0
- package/src/plugins/identity/ui/pages/admin/clients.js +179 -0
- package/src/plugins/identity/ui/pages/admin/dashboard.js +181 -0
- package/src/plugins/identity/ui/pages/admin/user-form.js +283 -0
- package/src/plugins/identity/ui/pages/admin/users.js +263 -0
- package/src/plugins/identity/ui/pages/consent.js +262 -0
- package/src/plugins/identity/ui/pages/forgot-password.js +104 -0
- package/src/plugins/identity/ui/pages/login.js +144 -0
- package/src/plugins/identity/ui/pages/mfa-backup-codes.js +180 -0
- package/src/plugins/identity/ui/pages/mfa-enrollment.js +187 -0
- package/src/plugins/identity/ui/pages/mfa-verification.js +178 -0
- package/src/plugins/identity/ui/pages/oauth-error.js +225 -0
- package/src/plugins/identity/ui/pages/profile.js +361 -0
- package/src/plugins/identity/ui/pages/register.js +226 -0
- package/src/plugins/identity/ui/pages/reset-password.js +128 -0
- package/src/plugins/identity/ui/pages/verify-email.js +172 -0
- package/src/plugins/identity/ui/routes.js +2541 -0
- package/src/plugins/identity/ui/styles/main.css +465 -0
- package/src/plugins/index.js +4 -1
- package/src/plugins/ml/base-model.class.js +65 -16
- package/src/plugins/ml/classification-model.class.js +1 -1
- package/src/plugins/ml/timeseries-model.class.js +3 -1
- package/src/plugins/ml.plugin.js +584 -31
- package/src/plugins/shared/error-handler.js +147 -0
- package/src/plugins/shared/index.js +9 -0
- package/src/plugins/shared/middlewares/compression.js +117 -0
- package/src/plugins/shared/middlewares/cors.js +49 -0
- package/src/plugins/shared/middlewares/index.js +11 -0
- package/src/plugins/shared/middlewares/logging.js +54 -0
- package/src/plugins/shared/middlewares/rate-limit.js +73 -0
- package/src/plugins/shared/middlewares/security.js +158 -0
- package/src/plugins/shared/response-formatter.js +264 -0
- package/src/plugins/state-machine.plugin.js +57 -2
- package/src/resource.class.js +140 -12
- package/src/schema.class.js +30 -1
- package/src/validator.class.js +57 -6
- package/dist/s3db.cjs.js.map +0 -1
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
import { BaseCloudDriver } from './base-driver.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Production-ready Oracle Cloud Infrastructure (OCI) inventory driver using official oci-sdk.
|
|
5
|
+
*
|
|
6
|
+
* Covers 15+ services with 25+ resource types:
|
|
7
|
+
* - Compute (instances, boot volumes, images)
|
|
8
|
+
* - Kubernetes (OKE clusters, node pools)
|
|
9
|
+
* - Databases (Autonomous DB, DB Systems)
|
|
10
|
+
* - Storage (block volumes, buckets, file systems)
|
|
11
|
+
* - Networking (VCNs, subnets, load balancers, network load balancers, gateways)
|
|
12
|
+
* - Identity (users, groups, compartments, policies)
|
|
13
|
+
* - DNS (zones, records)
|
|
14
|
+
*
|
|
15
|
+
* @see https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/typescriptsdk.htm
|
|
16
|
+
* @see https://github.com/oracle/oci-typescript-sdk
|
|
17
|
+
*/
|
|
18
|
+
export class OracleInventoryDriver extends BaseCloudDriver {
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
super({ ...options, driver: options.driver || 'oracle' });
|
|
21
|
+
|
|
22
|
+
this._provider = null;
|
|
23
|
+
this._tenancyId = null;
|
|
24
|
+
this._compartmentId = null;
|
|
25
|
+
this._accountId = this.config?.accountId || 'oracle';
|
|
26
|
+
|
|
27
|
+
// Services to collect (can be filtered via config.services)
|
|
28
|
+
this._services = this.config?.services || [
|
|
29
|
+
'compute',
|
|
30
|
+
'kubernetes',
|
|
31
|
+
'database',
|
|
32
|
+
'blockstorage',
|
|
33
|
+
'objectstorage',
|
|
34
|
+
'filestorage',
|
|
35
|
+
'vcn',
|
|
36
|
+
'loadbalancer',
|
|
37
|
+
'identity',
|
|
38
|
+
'dns'
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
// Regions to scan (can be filtered via config.regions)
|
|
42
|
+
this._regions = this.config?.regions || null; // null = all subscribed regions
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Initialize the OCI provider and clients.
|
|
47
|
+
*/
|
|
48
|
+
async _initializeProvider() {
|
|
49
|
+
if (this._provider) return;
|
|
50
|
+
|
|
51
|
+
const credentials = this.credentials || {};
|
|
52
|
+
|
|
53
|
+
// Import OCI common module
|
|
54
|
+
const common = await import('oci-common');
|
|
55
|
+
|
|
56
|
+
// Setup authentication provider
|
|
57
|
+
// Support multiple auth methods: config file, instance principal, resource principal
|
|
58
|
+
if (credentials.configFilePath) {
|
|
59
|
+
// Config file authentication
|
|
60
|
+
this._provider = new common.ConfigFileAuthenticationDetailsProvider(
|
|
61
|
+
credentials.configFilePath,
|
|
62
|
+
credentials.profile || 'DEFAULT'
|
|
63
|
+
);
|
|
64
|
+
} else if (credentials.instancePrincipal) {
|
|
65
|
+
// Instance principal authentication (for OCI compute instances)
|
|
66
|
+
this._provider = await common.ResourcePrincipalAuthenticationDetailsProvider.builder();
|
|
67
|
+
} else if (credentials.user && credentials.fingerprint && credentials.privateKey) {
|
|
68
|
+
// Direct credentials
|
|
69
|
+
this._provider = new common.SimpleAuthenticationDetailsProvider(
|
|
70
|
+
credentials.tenancy || this.config?.tenancyId,
|
|
71
|
+
credentials.user,
|
|
72
|
+
credentials.fingerprint,
|
|
73
|
+
credentials.privateKey,
|
|
74
|
+
credentials.passphrase || null,
|
|
75
|
+
credentials.region || this.config?.region || common.Region.US_ASHBURN_1
|
|
76
|
+
);
|
|
77
|
+
} else {
|
|
78
|
+
// Default to config file
|
|
79
|
+
this._provider = new common.ConfigFileAuthenticationDetailsProvider();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this._tenancyId = credentials.tenancy || this.config?.tenancyId;
|
|
83
|
+
this._compartmentId = this.config?.compartmentId || this._tenancyId;
|
|
84
|
+
|
|
85
|
+
this.logger('info', 'OCI provider initialized', {
|
|
86
|
+
accountId: this._accountId,
|
|
87
|
+
services: this._services.length
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Main entry point - lists all resources from configured services.
|
|
93
|
+
*/
|
|
94
|
+
async *listResources(options = {}) {
|
|
95
|
+
await this._initializeProvider();
|
|
96
|
+
|
|
97
|
+
const serviceCollectors = {
|
|
98
|
+
compute: () => this._collectCompute(),
|
|
99
|
+
kubernetes: () => this._collectKubernetes(),
|
|
100
|
+
database: () => this._collectDatabases(),
|
|
101
|
+
blockstorage: () => this._collectBlockStorage(),
|
|
102
|
+
objectstorage: () => this._collectObjectStorage(),
|
|
103
|
+
filestorage: () => this._collectFileStorage(),
|
|
104
|
+
vcn: () => this._collectVCN(),
|
|
105
|
+
loadbalancer: () => this._collectLoadBalancers(),
|
|
106
|
+
identity: () => this._collectIdentity(),
|
|
107
|
+
dns: () => this._collectDNS()
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
for (const service of this._services) {
|
|
111
|
+
const collector = serviceCollectors[service];
|
|
112
|
+
if (!collector) {
|
|
113
|
+
this.logger('warn', `Unknown OCI service: ${service}`, { service });
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
this.logger('info', `Collecting OCI ${service} resources`, { service });
|
|
119
|
+
yield* collector();
|
|
120
|
+
} catch (err) {
|
|
121
|
+
// Continue with next service instead of failing entire sync
|
|
122
|
+
this.logger('error', `OCI service collection failed, skipping to next service`, {
|
|
123
|
+
service,
|
|
124
|
+
error: err.message,
|
|
125
|
+
errorName: err.name,
|
|
126
|
+
stack: err.stack
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Collect Compute instances.
|
|
134
|
+
*/
|
|
135
|
+
async *_collectCompute() {
|
|
136
|
+
try {
|
|
137
|
+
const { core } = await import('oci-core');
|
|
138
|
+
const computeClient = new core.ComputeClient({ authenticationDetailsProvider: this._provider });
|
|
139
|
+
|
|
140
|
+
const regions = await this._getRegions();
|
|
141
|
+
|
|
142
|
+
for (const region of regions) {
|
|
143
|
+
computeClient.region = region;
|
|
144
|
+
|
|
145
|
+
// List instances
|
|
146
|
+
const instancesResponse = await computeClient.listInstances({
|
|
147
|
+
compartmentId: this._compartmentId
|
|
148
|
+
});
|
|
149
|
+
const instances = instancesResponse.items || [];
|
|
150
|
+
|
|
151
|
+
for (const instance of instances) {
|
|
152
|
+
yield {
|
|
153
|
+
provider: 'oracle',
|
|
154
|
+
accountId: this._accountId,
|
|
155
|
+
region: region.regionName,
|
|
156
|
+
service: 'compute',
|
|
157
|
+
resourceType: 'oracle.compute.instance',
|
|
158
|
+
resourceId: instance.id,
|
|
159
|
+
name: instance.displayName || instance.id,
|
|
160
|
+
tags: this._extractTags(instance.freeformTags, instance.definedTags),
|
|
161
|
+
configuration: this._sanitize(instance)
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this.logger('info', `Collected ${instances.length} OCI compute instances in ${region.regionName}`);
|
|
166
|
+
}
|
|
167
|
+
} catch (err) {
|
|
168
|
+
this.logger('error', 'Failed to collect OCI compute', {
|
|
169
|
+
error: err.message,
|
|
170
|
+
stack: err.stack
|
|
171
|
+
});
|
|
172
|
+
throw err;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Collect Kubernetes (OKE) clusters.
|
|
178
|
+
*/
|
|
179
|
+
async *_collectKubernetes() {
|
|
180
|
+
try {
|
|
181
|
+
const { containerengine } = await import('oci-containerengine');
|
|
182
|
+
const containerClient = new containerengine.ContainerEngineClient({
|
|
183
|
+
authenticationDetailsProvider: this._provider
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const regions = await this._getRegions();
|
|
187
|
+
|
|
188
|
+
for (const region of regions) {
|
|
189
|
+
containerClient.region = region;
|
|
190
|
+
|
|
191
|
+
const clustersResponse = await containerClient.listClusters({
|
|
192
|
+
compartmentId: this._compartmentId
|
|
193
|
+
});
|
|
194
|
+
const clusters = clustersResponse.items || [];
|
|
195
|
+
|
|
196
|
+
for (const cluster of clusters) {
|
|
197
|
+
yield {
|
|
198
|
+
provider: 'oracle',
|
|
199
|
+
accountId: this._accountId,
|
|
200
|
+
region: region.regionName,
|
|
201
|
+
service: 'kubernetes',
|
|
202
|
+
resourceType: 'oracle.kubernetes.cluster',
|
|
203
|
+
resourceId: cluster.id,
|
|
204
|
+
name: cluster.name,
|
|
205
|
+
tags: this._extractTags(cluster.freeformTags, cluster.definedTags),
|
|
206
|
+
configuration: this._sanitize(cluster)
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// Collect node pools
|
|
210
|
+
try {
|
|
211
|
+
const nodePoolsResponse = await containerClient.listNodePools({
|
|
212
|
+
compartmentId: this._compartmentId,
|
|
213
|
+
clusterId: cluster.id
|
|
214
|
+
});
|
|
215
|
+
const nodePools = nodePoolsResponse.items || [];
|
|
216
|
+
|
|
217
|
+
for (const nodePool of nodePools) {
|
|
218
|
+
yield {
|
|
219
|
+
provider: 'oracle',
|
|
220
|
+
accountId: this._accountId,
|
|
221
|
+
region: region.regionName,
|
|
222
|
+
service: 'kubernetes',
|
|
223
|
+
resourceType: 'oracle.kubernetes.nodepool',
|
|
224
|
+
resourceId: nodePool.id,
|
|
225
|
+
name: nodePool.name,
|
|
226
|
+
tags: this._extractTags(nodePool.freeformTags, nodePool.definedTags),
|
|
227
|
+
metadata: { clusterId: cluster.id, clusterName: cluster.name },
|
|
228
|
+
configuration: this._sanitize(nodePool)
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
} catch (npErr) {
|
|
232
|
+
this.logger('warn', `Failed to collect node pools for cluster ${cluster.id}`, {
|
|
233
|
+
clusterId: cluster.id,
|
|
234
|
+
error: npErr.message
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
this.logger('info', `Collected ${clusters.length} OCI OKE clusters in ${region.regionName}`);
|
|
240
|
+
}
|
|
241
|
+
} catch (err) {
|
|
242
|
+
this.logger('error', 'Failed to collect OCI Kubernetes', {
|
|
243
|
+
error: err.message,
|
|
244
|
+
stack: err.stack
|
|
245
|
+
});
|
|
246
|
+
throw err;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Collect Databases (Autonomous Database, DB Systems).
|
|
252
|
+
*/
|
|
253
|
+
async *_collectDatabases() {
|
|
254
|
+
try {
|
|
255
|
+
const { database } = await import('oci-database');
|
|
256
|
+
const databaseClient = new database.DatabaseClient({
|
|
257
|
+
authenticationDetailsProvider: this._provider
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const regions = await this._getRegions();
|
|
261
|
+
|
|
262
|
+
for (const region of regions) {
|
|
263
|
+
databaseClient.region = region;
|
|
264
|
+
|
|
265
|
+
// Autonomous Databases
|
|
266
|
+
const autonomousDbsResponse = await databaseClient.listAutonomousDatabases({
|
|
267
|
+
compartmentId: this._compartmentId
|
|
268
|
+
});
|
|
269
|
+
const autonomousDbs = autonomousDbsResponse.items || [];
|
|
270
|
+
|
|
271
|
+
for (const db of autonomousDbs) {
|
|
272
|
+
yield {
|
|
273
|
+
provider: 'oracle',
|
|
274
|
+
accountId: this._accountId,
|
|
275
|
+
region: region.regionName,
|
|
276
|
+
service: 'database',
|
|
277
|
+
resourceType: 'oracle.database.autonomous',
|
|
278
|
+
resourceId: db.id,
|
|
279
|
+
name: db.displayName || db.dbName,
|
|
280
|
+
tags: this._extractTags(db.freeformTags, db.definedTags),
|
|
281
|
+
configuration: this._sanitize(db)
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// DB Systems
|
|
286
|
+
const dbSystemsResponse = await databaseClient.listDbSystems({
|
|
287
|
+
compartmentId: this._compartmentId
|
|
288
|
+
});
|
|
289
|
+
const dbSystems = dbSystemsResponse.items || [];
|
|
290
|
+
|
|
291
|
+
for (const dbSystem of dbSystems) {
|
|
292
|
+
yield {
|
|
293
|
+
provider: 'oracle',
|
|
294
|
+
accountId: this._accountId,
|
|
295
|
+
region: region.regionName,
|
|
296
|
+
service: 'database',
|
|
297
|
+
resourceType: 'oracle.database.system',
|
|
298
|
+
resourceId: dbSystem.id,
|
|
299
|
+
name: dbSystem.displayName,
|
|
300
|
+
tags: this._extractTags(dbSystem.freeformTags, dbSystem.definedTags),
|
|
301
|
+
configuration: this._sanitize(dbSystem)
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
this.logger('info', `Collected ${autonomousDbs.length} Autonomous DBs and ${dbSystems.length} DB Systems in ${region.regionName}`);
|
|
306
|
+
}
|
|
307
|
+
} catch (err) {
|
|
308
|
+
this.logger('error', 'Failed to collect OCI databases', {
|
|
309
|
+
error: err.message,
|
|
310
|
+
stack: err.stack
|
|
311
|
+
});
|
|
312
|
+
throw err;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Collect Block Storage volumes.
|
|
318
|
+
*/
|
|
319
|
+
async *_collectBlockStorage() {
|
|
320
|
+
try {
|
|
321
|
+
const { core } = await import('oci-core');
|
|
322
|
+
const blockstorageClient = new core.BlockstorageClient({
|
|
323
|
+
authenticationDetailsProvider: this._provider
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
const regions = await this._getRegions();
|
|
327
|
+
|
|
328
|
+
for (const region of regions) {
|
|
329
|
+
blockstorageClient.region = region;
|
|
330
|
+
|
|
331
|
+
const volumesResponse = await blockstorageClient.listVolumes({
|
|
332
|
+
compartmentId: this._compartmentId
|
|
333
|
+
});
|
|
334
|
+
const volumes = volumesResponse.items || [];
|
|
335
|
+
|
|
336
|
+
for (const volume of volumes) {
|
|
337
|
+
yield {
|
|
338
|
+
provider: 'oracle',
|
|
339
|
+
accountId: this._accountId,
|
|
340
|
+
region: region.regionName,
|
|
341
|
+
service: 'blockstorage',
|
|
342
|
+
resourceType: 'oracle.blockstorage.volume',
|
|
343
|
+
resourceId: volume.id,
|
|
344
|
+
name: volume.displayName,
|
|
345
|
+
tags: this._extractTags(volume.freeformTags, volume.definedTags),
|
|
346
|
+
configuration: this._sanitize(volume)
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
this.logger('info', `Collected ${volumes.length} OCI block volumes in ${region.regionName}`);
|
|
351
|
+
}
|
|
352
|
+
} catch (err) {
|
|
353
|
+
this.logger('error', 'Failed to collect OCI block storage', {
|
|
354
|
+
error: err.message,
|
|
355
|
+
stack: err.stack
|
|
356
|
+
});
|
|
357
|
+
throw err;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Collect Object Storage buckets.
|
|
363
|
+
*/
|
|
364
|
+
async *_collectObjectStorage() {
|
|
365
|
+
try {
|
|
366
|
+
const { objectstorage } = await import('oci-objectstorage');
|
|
367
|
+
const objectStorageClient = new objectstorage.ObjectStorageClient({
|
|
368
|
+
authenticationDetailsProvider: this._provider
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
const regions = await this._getRegions();
|
|
372
|
+
|
|
373
|
+
// Get namespace
|
|
374
|
+
const namespaceResponse = await objectStorageClient.getNamespace({});
|
|
375
|
+
const namespace = namespaceResponse.value;
|
|
376
|
+
|
|
377
|
+
for (const region of regions) {
|
|
378
|
+
objectStorageClient.region = region;
|
|
379
|
+
|
|
380
|
+
const bucketsResponse = await objectStorageClient.listBuckets({
|
|
381
|
+
namespaceName: namespace,
|
|
382
|
+
compartmentId: this._compartmentId
|
|
383
|
+
});
|
|
384
|
+
const buckets = bucketsResponse.items || [];
|
|
385
|
+
|
|
386
|
+
for (const bucket of buckets) {
|
|
387
|
+
yield {
|
|
388
|
+
provider: 'oracle',
|
|
389
|
+
accountId: this._accountId,
|
|
390
|
+
region: region.regionName,
|
|
391
|
+
service: 'objectstorage',
|
|
392
|
+
resourceType: 'oracle.objectstorage.bucket',
|
|
393
|
+
resourceId: bucket.name,
|
|
394
|
+
name: bucket.name,
|
|
395
|
+
tags: this._extractTags(bucket.freeformTags, bucket.definedTags),
|
|
396
|
+
metadata: { namespace },
|
|
397
|
+
configuration: this._sanitize(bucket)
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
this.logger('info', `Collected ${buckets.length} OCI object storage buckets in ${region.regionName}`);
|
|
402
|
+
}
|
|
403
|
+
} catch (err) {
|
|
404
|
+
this.logger('error', 'Failed to collect OCI object storage', {
|
|
405
|
+
error: err.message,
|
|
406
|
+
stack: err.stack
|
|
407
|
+
});
|
|
408
|
+
throw err;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Collect File Storage file systems.
|
|
414
|
+
*/
|
|
415
|
+
async *_collectFileStorage() {
|
|
416
|
+
try {
|
|
417
|
+
const { filestorage } = await import('oci-filestorage');
|
|
418
|
+
const fileStorageClient = new filestorage.FileStorageClient({
|
|
419
|
+
authenticationDetailsProvider: this._provider
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
const regions = await this._getRegions();
|
|
423
|
+
|
|
424
|
+
for (const region of regions) {
|
|
425
|
+
fileStorageClient.region = region;
|
|
426
|
+
|
|
427
|
+
const fileSystemsResponse = await fileStorageClient.listFileSystems({
|
|
428
|
+
compartmentId: this._compartmentId,
|
|
429
|
+
availabilityDomain: region.regionName // Note: may need proper AD
|
|
430
|
+
});
|
|
431
|
+
const fileSystems = fileSystemsResponse.items || [];
|
|
432
|
+
|
|
433
|
+
for (const fs of fileSystems) {
|
|
434
|
+
yield {
|
|
435
|
+
provider: 'oracle',
|
|
436
|
+
accountId: this._accountId,
|
|
437
|
+
region: region.regionName,
|
|
438
|
+
service: 'filestorage',
|
|
439
|
+
resourceType: 'oracle.filestorage.filesystem',
|
|
440
|
+
resourceId: fs.id,
|
|
441
|
+
name: fs.displayName,
|
|
442
|
+
tags: this._extractTags(fs.freeformTags, fs.definedTags),
|
|
443
|
+
configuration: this._sanitize(fs)
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
this.logger('info', `Collected ${fileSystems.length} OCI file systems in ${region.regionName}`);
|
|
448
|
+
}
|
|
449
|
+
} catch (err) {
|
|
450
|
+
this.logger('error', 'Failed to collect OCI file storage', {
|
|
451
|
+
error: err.message,
|
|
452
|
+
stack: err.stack
|
|
453
|
+
});
|
|
454
|
+
throw err;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Collect VCN (Virtual Cloud Network) resources.
|
|
460
|
+
*/
|
|
461
|
+
async *_collectVCN() {
|
|
462
|
+
try {
|
|
463
|
+
const { core } = await import('oci-core');
|
|
464
|
+
const vcnClient = new core.VirtualNetworkClient({
|
|
465
|
+
authenticationDetailsProvider: this._provider
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
const regions = await this._getRegions();
|
|
469
|
+
|
|
470
|
+
for (const region of regions) {
|
|
471
|
+
vcnClient.region = region;
|
|
472
|
+
|
|
473
|
+
// VCNs
|
|
474
|
+
const vcnsResponse = await vcnClient.listVcns({
|
|
475
|
+
compartmentId: this._compartmentId
|
|
476
|
+
});
|
|
477
|
+
const vcns = vcnsResponse.items || [];
|
|
478
|
+
|
|
479
|
+
for (const vcn of vcns) {
|
|
480
|
+
yield {
|
|
481
|
+
provider: 'oracle',
|
|
482
|
+
accountId: this._accountId,
|
|
483
|
+
region: region.regionName,
|
|
484
|
+
service: 'vcn',
|
|
485
|
+
resourceType: 'oracle.vcn.network',
|
|
486
|
+
resourceId: vcn.id,
|
|
487
|
+
name: vcn.displayName,
|
|
488
|
+
tags: this._extractTags(vcn.freeformTags, vcn.definedTags),
|
|
489
|
+
configuration: this._sanitize(vcn)
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// Collect subnets for this VCN
|
|
493
|
+
try {
|
|
494
|
+
const subnetsResponse = await vcnClient.listSubnets({
|
|
495
|
+
compartmentId: this._compartmentId,
|
|
496
|
+
vcnId: vcn.id
|
|
497
|
+
});
|
|
498
|
+
const subnets = subnetsResponse.items || [];
|
|
499
|
+
|
|
500
|
+
for (const subnet of subnets) {
|
|
501
|
+
yield {
|
|
502
|
+
provider: 'oracle',
|
|
503
|
+
accountId: this._accountId,
|
|
504
|
+
region: region.regionName,
|
|
505
|
+
service: 'vcn',
|
|
506
|
+
resourceType: 'oracle.vcn.subnet',
|
|
507
|
+
resourceId: subnet.id,
|
|
508
|
+
name: subnet.displayName,
|
|
509
|
+
tags: this._extractTags(subnet.freeformTags, subnet.definedTags),
|
|
510
|
+
metadata: { vcnId: vcn.id, vcnName: vcn.displayName },
|
|
511
|
+
configuration: this._sanitize(subnet)
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
} catch (subnetErr) {
|
|
515
|
+
this.logger('warn', `Failed to collect subnets for VCN ${vcn.id}`, {
|
|
516
|
+
vcnId: vcn.id,
|
|
517
|
+
error: subnetErr.message
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
this.logger('info', `Collected ${vcns.length} OCI VCNs in ${region.regionName}`);
|
|
523
|
+
}
|
|
524
|
+
} catch (err) {
|
|
525
|
+
this.logger('error', 'Failed to collect OCI VCN', {
|
|
526
|
+
error: err.message,
|
|
527
|
+
stack: err.stack
|
|
528
|
+
});
|
|
529
|
+
throw err;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Collect Load Balancers.
|
|
535
|
+
*/
|
|
536
|
+
async *_collectLoadBalancers() {
|
|
537
|
+
try {
|
|
538
|
+
const { loadbalancer } = await import('oci-loadbalancer');
|
|
539
|
+
const lbClient = new loadbalancer.LoadBalancerClient({
|
|
540
|
+
authenticationDetailsProvider: this._provider
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
const regions = await this._getRegions();
|
|
544
|
+
|
|
545
|
+
for (const region of regions) {
|
|
546
|
+
lbClient.region = region;
|
|
547
|
+
|
|
548
|
+
const lbsResponse = await lbClient.listLoadBalancers({
|
|
549
|
+
compartmentId: this._compartmentId
|
|
550
|
+
});
|
|
551
|
+
const lbs = lbsResponse.items || [];
|
|
552
|
+
|
|
553
|
+
for (const lb of lbs) {
|
|
554
|
+
yield {
|
|
555
|
+
provider: 'oracle',
|
|
556
|
+
accountId: this._accountId,
|
|
557
|
+
region: region.regionName,
|
|
558
|
+
service: 'loadbalancer',
|
|
559
|
+
resourceType: 'oracle.loadbalancer',
|
|
560
|
+
resourceId: lb.id,
|
|
561
|
+
name: lb.displayName,
|
|
562
|
+
tags: this._extractTags(lb.freeformTags, lb.definedTags),
|
|
563
|
+
configuration: this._sanitize(lb)
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
this.logger('info', `Collected ${lbs.length} OCI load balancers in ${region.regionName}`);
|
|
568
|
+
}
|
|
569
|
+
} catch (err) {
|
|
570
|
+
this.logger('error', 'Failed to collect OCI load balancers', {
|
|
571
|
+
error: err.message,
|
|
572
|
+
stack: err.stack
|
|
573
|
+
});
|
|
574
|
+
throw err;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Collect Identity resources (users, groups, compartments).
|
|
580
|
+
*/
|
|
581
|
+
async *_collectIdentity() {
|
|
582
|
+
try {
|
|
583
|
+
const { identity } = await import('oci-identity');
|
|
584
|
+
const identityClient = new identity.IdentityClient({
|
|
585
|
+
authenticationDetailsProvider: this._provider
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
// Users
|
|
589
|
+
const usersResponse = await identityClient.listUsers({
|
|
590
|
+
compartmentId: this._tenancyId
|
|
591
|
+
});
|
|
592
|
+
const users = usersResponse.items || [];
|
|
593
|
+
|
|
594
|
+
for (const user of users) {
|
|
595
|
+
yield {
|
|
596
|
+
provider: 'oracle',
|
|
597
|
+
accountId: this._accountId,
|
|
598
|
+
region: null, // Identity is global
|
|
599
|
+
service: 'identity',
|
|
600
|
+
resourceType: 'oracle.identity.user',
|
|
601
|
+
resourceId: user.id,
|
|
602
|
+
name: user.name,
|
|
603
|
+
tags: this._extractTags(user.freeformTags, user.definedTags),
|
|
604
|
+
configuration: this._sanitize(user)
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Groups
|
|
609
|
+
const groupsResponse = await identityClient.listGroups({
|
|
610
|
+
compartmentId: this._tenancyId
|
|
611
|
+
});
|
|
612
|
+
const groups = groupsResponse.items || [];
|
|
613
|
+
|
|
614
|
+
for (const group of groups) {
|
|
615
|
+
yield {
|
|
616
|
+
provider: 'oracle',
|
|
617
|
+
accountId: this._accountId,
|
|
618
|
+
region: null,
|
|
619
|
+
service: 'identity',
|
|
620
|
+
resourceType: 'oracle.identity.group',
|
|
621
|
+
resourceId: group.id,
|
|
622
|
+
name: group.name,
|
|
623
|
+
tags: this._extractTags(group.freeformTags, group.definedTags),
|
|
624
|
+
configuration: this._sanitize(group)
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Compartments
|
|
629
|
+
const compartmentsResponse = await identityClient.listCompartments({
|
|
630
|
+
compartmentId: this._tenancyId
|
|
631
|
+
});
|
|
632
|
+
const compartments = compartmentsResponse.items || [];
|
|
633
|
+
|
|
634
|
+
for (const compartment of compartments) {
|
|
635
|
+
yield {
|
|
636
|
+
provider: 'oracle',
|
|
637
|
+
accountId: this._accountId,
|
|
638
|
+
region: null,
|
|
639
|
+
service: 'identity',
|
|
640
|
+
resourceType: 'oracle.identity.compartment',
|
|
641
|
+
resourceId: compartment.id,
|
|
642
|
+
name: compartment.name,
|
|
643
|
+
tags: this._extractTags(compartment.freeformTags, compartment.definedTags),
|
|
644
|
+
configuration: this._sanitize(compartment)
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
this.logger('info', `Collected ${users.length} users, ${groups.length} groups, ${compartments.length} compartments`);
|
|
649
|
+
} catch (err) {
|
|
650
|
+
this.logger('error', 'Failed to collect OCI identity', {
|
|
651
|
+
error: err.message,
|
|
652
|
+
stack: err.stack
|
|
653
|
+
});
|
|
654
|
+
throw err;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Collect DNS zones.
|
|
660
|
+
*/
|
|
661
|
+
async *_collectDNS() {
|
|
662
|
+
try {
|
|
663
|
+
const { dns } = await import('oci-dns');
|
|
664
|
+
const dnsClient = new dns.DnsClient({
|
|
665
|
+
authenticationDetailsProvider: this._provider
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
const zonesResponse = await dnsClient.listZones({
|
|
669
|
+
compartmentId: this._compartmentId
|
|
670
|
+
});
|
|
671
|
+
const zones = zonesResponse.items || [];
|
|
672
|
+
|
|
673
|
+
for (const zone of zones) {
|
|
674
|
+
yield {
|
|
675
|
+
provider: 'oracle',
|
|
676
|
+
accountId: this._accountId,
|
|
677
|
+
region: null, // DNS is global
|
|
678
|
+
service: 'dns',
|
|
679
|
+
resourceType: 'oracle.dns.zone',
|
|
680
|
+
resourceId: zone.id,
|
|
681
|
+
name: zone.name,
|
|
682
|
+
tags: this._extractTags(zone.freeformTags, zone.definedTags),
|
|
683
|
+
configuration: this._sanitize(zone)
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
this.logger('info', `Collected ${zones.length} OCI DNS zones`);
|
|
688
|
+
} catch (err) {
|
|
689
|
+
this.logger('error', 'Failed to collect OCI DNS', {
|
|
690
|
+
error: err.message,
|
|
691
|
+
stack: err.stack
|
|
692
|
+
});
|
|
693
|
+
throw err;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Get list of subscribed regions.
|
|
699
|
+
*/
|
|
700
|
+
async _getRegions() {
|
|
701
|
+
if (this._regions && Array.isArray(this._regions)) {
|
|
702
|
+
// Use configured regions
|
|
703
|
+
const common = await import('oci-common');
|
|
704
|
+
return this._regions.map(r => ({ regionName: r, region: common.Region[r] }));
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// Get all subscribed regions
|
|
708
|
+
const { identity } = await import('oci-identity');
|
|
709
|
+
const identityClient = new identity.IdentityClient({
|
|
710
|
+
authenticationDetailsProvider: this._provider
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
const regionsResponse = await identityClient.listRegionSubscriptions({
|
|
714
|
+
tenancyId: this._tenancyId
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
return regionsResponse.items || [];
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Extract tags from OCI freeform and defined tags.
|
|
722
|
+
*/
|
|
723
|
+
_extractTags(freeformTags, definedTags) {
|
|
724
|
+
const tags = {};
|
|
725
|
+
|
|
726
|
+
if (freeformTags && typeof freeformTags === 'object') {
|
|
727
|
+
Object.assign(tags, freeformTags);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
if (definedTags && typeof definedTags === 'object') {
|
|
731
|
+
// Flatten defined tags
|
|
732
|
+
for (const [namespace, namespaceTags] of Object.entries(definedTags)) {
|
|
733
|
+
for (const [key, value] of Object.entries(namespaceTags)) {
|
|
734
|
+
tags[`${namespace}.${key}`] = value;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return tags;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Sanitize configuration by removing sensitive data.
|
|
744
|
+
*/
|
|
745
|
+
_sanitize(config) {
|
|
746
|
+
if (!config || typeof config !== 'object') return config;
|
|
747
|
+
|
|
748
|
+
const sanitized = { ...config };
|
|
749
|
+
const sensitiveFields = [
|
|
750
|
+
'password',
|
|
751
|
+
'adminPassword',
|
|
752
|
+
'privateKey',
|
|
753
|
+
'publicKey',
|
|
754
|
+
'secret',
|
|
755
|
+
'token',
|
|
756
|
+
'connectionString',
|
|
757
|
+
'connectionStrings'
|
|
758
|
+
];
|
|
759
|
+
|
|
760
|
+
for (const field of sensitiveFields) {
|
|
761
|
+
if (field in sanitized) {
|
|
762
|
+
sanitized[field] = '***REDACTED***';
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return sanitized;
|
|
767
|
+
}
|
|
768
|
+
}
|