s3db.js 13.6.0 → 14.0.2
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 +139 -43
- package/dist/s3db.cjs +72425 -38970
- package/dist/s3db.cjs.map +1 -1
- package/dist/s3db.es.js +72177 -38764
- package/dist/s3db.es.js.map +1 -1
- package/mcp/lib/base-handler.js +157 -0
- package/mcp/lib/handlers/connection-handler.js +280 -0
- package/mcp/lib/handlers/query-handler.js +533 -0
- package/mcp/lib/handlers/resource-handler.js +428 -0
- package/mcp/lib/tool-registry.js +336 -0
- package/mcp/lib/tools/connection-tools.js +161 -0
- package/mcp/lib/tools/query-tools.js +267 -0
- package/mcp/lib/tools/resource-tools.js +404 -0
- package/package.json +94 -49
- package/src/clients/memory-client.class.js +346 -191
- package/src/clients/memory-storage.class.js +300 -84
- package/src/clients/s3-client.class.js +7 -6
- package/src/concerns/geo-encoding.js +19 -2
- package/src/concerns/ip.js +59 -9
- package/src/concerns/money.js +8 -1
- package/src/concerns/password-hashing.js +49 -8
- package/src/concerns/plugin-storage.js +186 -18
- package/src/concerns/storage-drivers/filesystem-driver.js +284 -0
- package/src/database.class.js +139 -29
- package/src/errors.js +332 -42
- package/src/plugins/api/auth/oidc-auth.js +66 -17
- package/src/plugins/api/auth/strategies/base-strategy.class.js +74 -0
- package/src/plugins/api/auth/strategies/factory.class.js +63 -0
- package/src/plugins/api/auth/strategies/global-strategy.class.js +44 -0
- package/src/plugins/api/auth/strategies/path-based-strategy.class.js +83 -0
- package/src/plugins/api/auth/strategies/path-rules-strategy.class.js +118 -0
- package/src/plugins/api/concerns/failban-manager.js +106 -57
- package/src/plugins/api/concerns/opengraph-helper.js +116 -0
- package/src/plugins/api/concerns/route-context.js +601 -0
- package/src/plugins/api/concerns/state-machine.js +288 -0
- package/src/plugins/api/index.js +180 -41
- package/src/plugins/api/routes/auth-routes.js +198 -30
- package/src/plugins/api/routes/resource-routes.js +19 -4
- package/src/plugins/api/server/health-manager.class.js +163 -0
- package/src/plugins/api/server/middleware-chain.class.js +310 -0
- package/src/plugins/api/server/router.class.js +472 -0
- package/src/plugins/api/server.js +280 -1303
- package/src/plugins/api/utils/custom-routes.js +17 -5
- package/src/plugins/api/utils/guards.js +76 -17
- package/src/plugins/api/utils/openapi-generator-cached.class.js +133 -0
- package/src/plugins/api/utils/openapi-generator.js +7 -6
- package/src/plugins/api/utils/template-engine.js +77 -3
- package/src/plugins/audit.plugin.js +30 -8
- package/src/plugins/backup.plugin.js +110 -14
- package/src/plugins/cache/cache.class.js +22 -5
- package/src/plugins/cache/filesystem-cache.class.js +116 -19
- package/src/plugins/cache/memory-cache.class.js +211 -57
- package/src/plugins/cache/multi-tier-cache.class.js +371 -0
- package/src/plugins/cache/partition-aware-filesystem-cache.class.js +168 -47
- package/src/plugins/cache/redis-cache.class.js +552 -0
- package/src/plugins/cache/s3-cache.class.js +17 -8
- package/src/plugins/cache.plugin.js +176 -61
- package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/aws-driver.js +60 -29
- package/src/plugins/cloud-inventory/drivers/azure-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/base-driver.js +16 -2
- package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/linode-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/vultr-driver.js +8 -1
- package/src/plugins/cloud-inventory/index.js +29 -8
- package/src/plugins/cloud-inventory/registry.js +64 -42
- package/src/plugins/cloud-inventory.plugin.js +240 -138
- package/src/plugins/concerns/plugin-dependencies.js +54 -0
- package/src/plugins/concerns/resource-names.js +100 -0
- package/src/plugins/consumers/index.js +10 -2
- package/src/plugins/consumers/sqs-consumer.js +12 -2
- package/src/plugins/cookie-farm-suite.plugin.js +278 -0
- package/src/plugins/cookie-farm.errors.js +73 -0
- package/src/plugins/cookie-farm.plugin.js +869 -0
- package/src/plugins/costs.plugin.js +7 -1
- package/src/plugins/eventual-consistency/analytics.js +94 -19
- package/src/plugins/eventual-consistency/config.js +15 -7
- package/src/plugins/eventual-consistency/consolidation.js +29 -11
- package/src/plugins/eventual-consistency/garbage-collection.js +3 -1
- package/src/plugins/eventual-consistency/helpers.js +39 -14
- package/src/plugins/eventual-consistency/install.js +21 -2
- package/src/plugins/eventual-consistency/utils.js +32 -10
- package/src/plugins/fulltext.plugin.js +38 -11
- package/src/plugins/geo.plugin.js +61 -9
- package/src/plugins/identity/concerns/config.js +61 -0
- package/src/plugins/identity/concerns/mfa-manager.js +15 -2
- package/src/plugins/identity/concerns/rate-limit.js +124 -0
- package/src/plugins/identity/concerns/resource-schemas.js +9 -1
- package/src/plugins/identity/concerns/token-generator.js +29 -4
- package/src/plugins/identity/drivers/auth-driver.interface.js +76 -0
- package/src/plugins/identity/drivers/client-credentials-driver.js +127 -0
- package/src/plugins/identity/drivers/index.js +18 -0
- package/src/plugins/identity/drivers/password-driver.js +122 -0
- package/src/plugins/identity/email-service.js +17 -2
- package/src/plugins/identity/index.js +413 -69
- package/src/plugins/identity/oauth2-server.js +413 -30
- package/src/plugins/identity/oidc-discovery.js +16 -8
- package/src/plugins/identity/rsa-keys.js +115 -35
- package/src/plugins/identity/server.js +166 -45
- package/src/plugins/identity/session-manager.js +53 -7
- package/src/plugins/identity/ui/pages/mfa-verification.js +17 -15
- package/src/plugins/identity/ui/routes.js +363 -255
- package/src/plugins/importer/index.js +153 -20
- package/src/plugins/index.js +9 -2
- package/src/plugins/kubernetes-inventory/index.js +6 -0
- package/src/plugins/kubernetes-inventory/k8s-driver.js +867 -0
- package/src/plugins/kubernetes-inventory/resource-types.js +274 -0
- package/src/plugins/kubernetes-inventory.plugin.js +980 -0
- package/src/plugins/metrics.plugin.js +64 -16
- package/src/plugins/ml/base-model.class.js +25 -15
- package/src/plugins/ml/regression-model.class.js +1 -1
- package/src/plugins/ml.errors.js +57 -25
- package/src/plugins/ml.plugin.js +28 -4
- package/src/plugins/namespace.js +210 -0
- package/src/plugins/plugin.class.js +180 -8
- package/src/plugins/puppeteer/console-monitor.js +729 -0
- package/src/plugins/puppeteer/cookie-manager.js +492 -0
- package/src/plugins/puppeteer/network-monitor.js +816 -0
- package/src/plugins/puppeteer/performance-manager.js +746 -0
- package/src/plugins/puppeteer/proxy-manager.js +478 -0
- package/src/plugins/puppeteer/stealth-manager.js +556 -0
- package/src/plugins/puppeteer.errors.js +81 -0
- package/src/plugins/puppeteer.plugin.js +1327 -0
- package/src/plugins/queue-consumer.plugin.js +69 -14
- package/src/plugins/recon/behaviors/uptime-behavior.js +691 -0
- package/src/plugins/recon/concerns/command-runner.js +148 -0
- package/src/plugins/recon/concerns/diff-detector.js +372 -0
- package/src/plugins/recon/concerns/fingerprint-builder.js +307 -0
- package/src/plugins/recon/concerns/process-manager.js +338 -0
- package/src/plugins/recon/concerns/report-generator.js +478 -0
- package/src/plugins/recon/concerns/security-analyzer.js +571 -0
- package/src/plugins/recon/concerns/target-normalizer.js +68 -0
- package/src/plugins/recon/config/defaults.js +321 -0
- package/src/plugins/recon/config/resources.js +370 -0
- package/src/plugins/recon/index.js +778 -0
- package/src/plugins/recon/managers/dependency-manager.js +174 -0
- package/src/plugins/recon/managers/scheduler-manager.js +179 -0
- package/src/plugins/recon/managers/storage-manager.js +745 -0
- package/src/plugins/recon/managers/target-manager.js +274 -0
- package/src/plugins/recon/stages/asn-stage.js +314 -0
- package/src/plugins/recon/stages/certificate-stage.js +84 -0
- package/src/plugins/recon/stages/dns-stage.js +107 -0
- package/src/plugins/recon/stages/dnsdumpster-stage.js +362 -0
- package/src/plugins/recon/stages/fingerprint-stage.js +71 -0
- package/src/plugins/recon/stages/google-dorks-stage.js +440 -0
- package/src/plugins/recon/stages/http-stage.js +89 -0
- package/src/plugins/recon/stages/latency-stage.js +148 -0
- package/src/plugins/recon/stages/massdns-stage.js +302 -0
- package/src/plugins/recon/stages/osint-stage.js +1373 -0
- package/src/plugins/recon/stages/ports-stage.js +169 -0
- package/src/plugins/recon/stages/screenshot-stage.js +94 -0
- package/src/plugins/recon/stages/secrets-stage.js +514 -0
- package/src/plugins/recon/stages/subdomains-stage.js +295 -0
- package/src/plugins/recon/stages/tls-audit-stage.js +78 -0
- package/src/plugins/recon/stages/vulnerability-stage.js +78 -0
- package/src/plugins/recon/stages/web-discovery-stage.js +113 -0
- package/src/plugins/recon/stages/whois-stage.js +349 -0
- package/src/plugins/recon.plugin.js +75 -0
- package/src/plugins/recon.plugin.js.backup +2635 -0
- package/src/plugins/relation.errors.js +87 -14
- package/src/plugins/replicator.plugin.js +514 -137
- package/src/plugins/replicators/base-replicator.class.js +89 -1
- package/src/plugins/replicators/bigquery-replicator.class.js +66 -22
- package/src/plugins/replicators/dynamodb-replicator.class.js +22 -15
- package/src/plugins/replicators/mongodb-replicator.class.js +22 -15
- package/src/plugins/replicators/mysql-replicator.class.js +52 -17
- package/src/plugins/replicators/planetscale-replicator.class.js +30 -4
- package/src/plugins/replicators/postgres-replicator.class.js +62 -27
- package/src/plugins/replicators/s3db-replicator.class.js +25 -18
- package/src/plugins/replicators/schema-sync.helper.js +3 -3
- package/src/plugins/replicators/sqs-replicator.class.js +8 -2
- package/src/plugins/replicators/turso-replicator.class.js +23 -3
- package/src/plugins/replicators/webhook-replicator.class.js +42 -4
- package/src/plugins/s3-queue.plugin.js +464 -65
- package/src/plugins/scheduler.plugin.js +20 -6
- package/src/plugins/state-machine.plugin.js +40 -9
- package/src/plugins/tfstate/README.md +126 -126
- package/src/plugins/tfstate/base-driver.js +28 -4
- package/src/plugins/tfstate/errors.js +65 -10
- package/src/plugins/tfstate/filesystem-driver.js +52 -8
- package/src/plugins/tfstate/index.js +163 -90
- package/src/plugins/tfstate/s3-driver.js +64 -6
- package/src/plugins/ttl.plugin.js +72 -17
- package/src/plugins/vector/distances.js +18 -12
- package/src/plugins/vector/kmeans.js +26 -4
- package/src/resource.class.js +115 -19
- package/src/testing/factory.class.js +20 -3
- package/src/testing/seeder.class.js +7 -1
- package/src/clients/memory-client.md +0 -917
- package/src/plugins/cloud-inventory/drivers/mock-drivers.js +0 -449
|
@@ -30,13 +30,19 @@ import { Plugin } from '../plugin.class.js';
|
|
|
30
30
|
import { requirePluginDependency } from '../concerns/plugin-dependencies.js';
|
|
31
31
|
import tryFn from '../../concerns/try-fn.js';
|
|
32
32
|
import { OAuth2Server } from './oauth2-server.js';
|
|
33
|
+
import { RateLimiter } from './concerns/rate-limit.js';
|
|
34
|
+
import { resolveResourceNames } from '../concerns/resource-names.js';
|
|
35
|
+
import { prepareResourceConfigs } from './concerns/config.js';
|
|
36
|
+
import { PluginError } from '../../errors.js';
|
|
33
37
|
import {
|
|
34
38
|
BASE_USER_ATTRIBUTES,
|
|
35
39
|
BASE_TENANT_ATTRIBUTES,
|
|
36
40
|
BASE_CLIENT_ATTRIBUTES,
|
|
37
|
-
validateResourcesConfig,
|
|
38
41
|
mergeResourceConfig
|
|
39
42
|
} from './concerns/resource-schemas.js';
|
|
43
|
+
import { verifyPassword } from './concerns/password.js';
|
|
44
|
+
import { createBuiltInAuthDrivers } from './drivers/index.js';
|
|
45
|
+
import { AuthDriver } from './drivers/auth-driver.interface.js';
|
|
40
46
|
|
|
41
47
|
/**
|
|
42
48
|
* Identity Provider Plugin class
|
|
@@ -51,31 +57,32 @@ export class IdentityPlugin extends Plugin {
|
|
|
51
57
|
constructor(options = {}) {
|
|
52
58
|
super(options);
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
this._internalResourceOverrides = options.resourceNames || options.internalResources || {};
|
|
61
|
+
this._internalResourceDescriptors = {
|
|
62
|
+
oauthKeys: {
|
|
63
|
+
defaultName: 'plg_identity_oauth_keys',
|
|
64
|
+
override: this._internalResourceOverrides.oauthKeys
|
|
65
|
+
},
|
|
66
|
+
authCodes: {
|
|
67
|
+
defaultName: 'plg_identity_auth_codes',
|
|
68
|
+
override: this._internalResourceOverrides.authCodes
|
|
69
|
+
},
|
|
70
|
+
sessions: {
|
|
71
|
+
defaultName: 'plg_identity_sessions',
|
|
72
|
+
override: this._internalResourceOverrides.sessions
|
|
73
|
+
},
|
|
74
|
+
passwordResetTokens: {
|
|
75
|
+
defaultName: 'plg_identity_password_reset_tokens',
|
|
76
|
+
override: this._internalResourceOverrides.passwordResetTokens
|
|
77
|
+
},
|
|
78
|
+
mfaDevices: {
|
|
79
|
+
defaultName: 'plg_identity_mfa_devices',
|
|
80
|
+
override: this._internalResourceOverrides.mfaDevices
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
this.internalResourceNames = this._resolveInternalResourceNames();
|
|
62
84
|
|
|
63
|
-
|
|
64
|
-
mergeResourceConfig(
|
|
65
|
-
{ attributes: BASE_USER_ATTRIBUTES },
|
|
66
|
-
options.resources.users,
|
|
67
|
-
'users'
|
|
68
|
-
);
|
|
69
|
-
mergeResourceConfig(
|
|
70
|
-
{ attributes: BASE_TENANT_ATTRIBUTES },
|
|
71
|
-
options.resources.tenants,
|
|
72
|
-
'tenants'
|
|
73
|
-
);
|
|
74
|
-
mergeResourceConfig(
|
|
75
|
-
{ attributes: BASE_CLIENT_ATTRIBUTES },
|
|
76
|
-
options.resources.clients,
|
|
77
|
-
'clients'
|
|
78
|
-
);
|
|
85
|
+
const normalizedResources = prepareResourceConfigs(options.resources);
|
|
79
86
|
|
|
80
87
|
this.config = {
|
|
81
88
|
// Server configuration
|
|
@@ -86,7 +93,7 @@ export class IdentityPlugin extends Plugin {
|
|
|
86
93
|
// OAuth2/OIDC configuration
|
|
87
94
|
issuer: options.issuer || `http://localhost:${options.port || 4000}`,
|
|
88
95
|
supportedScopes: options.supportedScopes || ['openid', 'profile', 'email', 'offline_access'],
|
|
89
|
-
supportedGrantTypes: options.supportedGrantTypes || ['authorization_code', 'client_credentials', '
|
|
96
|
+
supportedGrantTypes: options.supportedGrantTypes || ['authorization_code', 'refresh_token', 'client_credentials', 'password'],
|
|
90
97
|
supportedResponseTypes: options.supportedResponseTypes || ['code', 'token', 'id_token'],
|
|
91
98
|
|
|
92
99
|
// Token expiration
|
|
@@ -96,21 +103,9 @@ export class IdentityPlugin extends Plugin {
|
|
|
96
103
|
authCodeExpiry: options.authCodeExpiry || '10m',
|
|
97
104
|
|
|
98
105
|
// Resource configuration (REQUIRED)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
userConfig: options.resources.users, // Store user's full config
|
|
103
|
-
mergedConfig: null // Will be populated in _createResources()
|
|
104
|
-
},
|
|
105
|
-
tenants: {
|
|
106
|
-
userConfig: options.resources.tenants,
|
|
107
|
-
mergedConfig: null
|
|
108
|
-
},
|
|
109
|
-
clients: {
|
|
110
|
-
userConfig: options.resources.clients,
|
|
111
|
-
mergedConfig: null
|
|
112
|
-
}
|
|
113
|
-
},
|
|
106
|
+
resources: normalizedResources,
|
|
107
|
+
|
|
108
|
+
resourceNames: this.internalResourceNames,
|
|
114
109
|
|
|
115
110
|
// CORS configuration
|
|
116
111
|
cors: {
|
|
@@ -318,6 +313,23 @@ export class IdentityPlugin extends Plugin {
|
|
|
318
313
|
}
|
|
319
314
|
},
|
|
320
315
|
|
|
316
|
+
// Rate Limiting Configuration
|
|
317
|
+
rateLimit: {
|
|
318
|
+
enabled: options.rateLimit?.enabled !== false,
|
|
319
|
+
login: {
|
|
320
|
+
windowMs: options.rateLimit?.login?.windowMs || 60000,
|
|
321
|
+
max: options.rateLimit?.login?.max ?? 10
|
|
322
|
+
},
|
|
323
|
+
token: {
|
|
324
|
+
windowMs: options.rateLimit?.token?.windowMs || 60000,
|
|
325
|
+
max: options.rateLimit?.token?.max ?? 60
|
|
326
|
+
},
|
|
327
|
+
authorize: {
|
|
328
|
+
windowMs: options.rateLimit?.authorize?.windowMs || 60000,
|
|
329
|
+
max: options.rateLimit?.authorize?.max ?? 30
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
|
|
321
333
|
// Features (MVP - Phase 1)
|
|
322
334
|
features: {
|
|
323
335
|
// Endpoints (can be disabled individually)
|
|
@@ -353,7 +365,9 @@ export class IdentityPlugin extends Plugin {
|
|
|
353
365
|
// emailVerification: { enabled: false, required: false },
|
|
354
366
|
// passwordPolicy: { enabled: false, minLength: 8, ... },
|
|
355
367
|
// webhooks: { enabled: false, endpoints: [], events: [] }
|
|
356
|
-
}
|
|
368
|
+
},
|
|
369
|
+
|
|
370
|
+
authDrivers: options.authDrivers || {}
|
|
357
371
|
};
|
|
358
372
|
|
|
359
373
|
this.server = null;
|
|
@@ -375,6 +389,24 @@ export class IdentityPlugin extends Plugin {
|
|
|
375
389
|
this.usersResource = null;
|
|
376
390
|
this.tenantsResource = null;
|
|
377
391
|
this.clientsResource = null;
|
|
392
|
+
|
|
393
|
+
// Rate limiters
|
|
394
|
+
this.rateLimiters = this._createRateLimiters();
|
|
395
|
+
this.authDrivers = new Map();
|
|
396
|
+
this.authDriverInstances = [];
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
_resolveInternalResourceNames() {
|
|
400
|
+
return resolveResourceNames('identity', this._internalResourceDescriptors, {
|
|
401
|
+
namespace: this.namespace
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
onNamespaceChanged() {
|
|
406
|
+
this.internalResourceNames = this._resolveInternalResourceNames();
|
|
407
|
+
if (this.config) {
|
|
408
|
+
this.config.resourceNames = this.internalResourceNames;
|
|
409
|
+
}
|
|
378
410
|
}
|
|
379
411
|
|
|
380
412
|
/**
|
|
@@ -388,6 +420,34 @@ export class IdentityPlugin extends Plugin {
|
|
|
388
420
|
});
|
|
389
421
|
}
|
|
390
422
|
|
|
423
|
+
/**
|
|
424
|
+
* Initialize rate limiters for sensitive endpoints
|
|
425
|
+
* @private
|
|
426
|
+
* @returns {Object<string, RateLimiter>}
|
|
427
|
+
*/
|
|
428
|
+
_createRateLimiters() {
|
|
429
|
+
if (!this.config.rateLimit.enabled) {
|
|
430
|
+
return {};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const limiters = {};
|
|
434
|
+
const { login, token, authorize } = this.config.rateLimit;
|
|
435
|
+
|
|
436
|
+
if (login?.max > 0 && login?.windowMs > 0) {
|
|
437
|
+
limiters.login = new RateLimiter(login);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (token?.max > 0 && token?.windowMs > 0) {
|
|
441
|
+
limiters.token = new RateLimiter(token);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (authorize?.max > 0 && authorize?.windowMs > 0) {
|
|
445
|
+
limiters.authorize = new RateLimiter(authorize);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return limiters;
|
|
449
|
+
}
|
|
450
|
+
|
|
391
451
|
/**
|
|
392
452
|
* Install plugin
|
|
393
453
|
*/
|
|
@@ -428,6 +488,9 @@ export class IdentityPlugin extends Plugin {
|
|
|
428
488
|
// Initialize MFA Manager
|
|
429
489
|
await this._initializeMFAManager();
|
|
430
490
|
|
|
491
|
+
// Initialize authentication drivers
|
|
492
|
+
await this._initializeAuthDrivers();
|
|
493
|
+
|
|
431
494
|
if (this.config.verbose) {
|
|
432
495
|
console.log('[Identity Plugin] Installed successfully');
|
|
433
496
|
}
|
|
@@ -438,10 +501,12 @@ export class IdentityPlugin extends Plugin {
|
|
|
438
501
|
* @private
|
|
439
502
|
*/
|
|
440
503
|
async _createOAuth2Resources() {
|
|
504
|
+
const names = this.internalResourceNames;
|
|
505
|
+
|
|
441
506
|
// 1. OAuth Keys Resource (RSA keys for token signing)
|
|
442
507
|
const [okKeys, errKeys, keysResource] = await tryFn(() =>
|
|
443
508
|
this.database.createResource({
|
|
444
|
-
name:
|
|
509
|
+
name: names.oauthKeys,
|
|
445
510
|
attributes: {
|
|
446
511
|
kid: 'string|required',
|
|
447
512
|
publicKey: 'string|required',
|
|
@@ -460,12 +525,12 @@ export class IdentityPlugin extends Plugin {
|
|
|
460
525
|
if (okKeys) {
|
|
461
526
|
this.oauth2KeysResource = keysResource;
|
|
462
527
|
if (this.config.verbose) {
|
|
463
|
-
console.log(
|
|
528
|
+
console.log(`[Identity Plugin] Created ${names.oauthKeys} resource`);
|
|
464
529
|
}
|
|
465
|
-
} else if (this.database.resources.
|
|
466
|
-
this.oauth2KeysResource = this.database.resources.
|
|
530
|
+
} else if (this.database.resources[names.oauthKeys]) {
|
|
531
|
+
this.oauth2KeysResource = this.database.resources[names.oauthKeys];
|
|
467
532
|
if (this.config.verbose) {
|
|
468
|
-
console.log(
|
|
533
|
+
console.log(`[Identity Plugin] Using existing ${names.oauthKeys} resource`);
|
|
469
534
|
}
|
|
470
535
|
} else {
|
|
471
536
|
throw errKeys;
|
|
@@ -474,7 +539,7 @@ export class IdentityPlugin extends Plugin {
|
|
|
474
539
|
// 2. OAuth Authorization Codes Resource (authorization_code flow)
|
|
475
540
|
const [okCodes, errCodes, codesResource] = await tryFn(() =>
|
|
476
541
|
this.database.createResource({
|
|
477
|
-
name:
|
|
542
|
+
name: names.authCodes,
|
|
478
543
|
attributes: {
|
|
479
544
|
code: 'string|required',
|
|
480
545
|
clientId: 'string|required',
|
|
@@ -496,12 +561,12 @@ export class IdentityPlugin extends Plugin {
|
|
|
496
561
|
if (okCodes) {
|
|
497
562
|
this.oauth2AuthCodesResource = codesResource;
|
|
498
563
|
if (this.config.verbose) {
|
|
499
|
-
console.log(
|
|
564
|
+
console.log(`[Identity Plugin] Created ${names.authCodes} resource`);
|
|
500
565
|
}
|
|
501
|
-
} else if (this.database.resources.
|
|
502
|
-
this.oauth2AuthCodesResource = this.database.resources.
|
|
566
|
+
} else if (this.database.resources[names.authCodes]) {
|
|
567
|
+
this.oauth2AuthCodesResource = this.database.resources[names.authCodes];
|
|
503
568
|
if (this.config.verbose) {
|
|
504
|
-
console.log(
|
|
569
|
+
console.log(`[Identity Plugin] Using existing ${names.authCodes} resource`);
|
|
505
570
|
}
|
|
506
571
|
} else {
|
|
507
572
|
throw errCodes;
|
|
@@ -510,7 +575,7 @@ export class IdentityPlugin extends Plugin {
|
|
|
510
575
|
// 3. Sessions Resource (user sessions for UI/admin)
|
|
511
576
|
const [okSessions, errSessions, sessionsResource] = await tryFn(() =>
|
|
512
577
|
this.database.createResource({
|
|
513
|
-
name:
|
|
578
|
+
name: names.sessions,
|
|
514
579
|
attributes: {
|
|
515
580
|
userId: 'string|required',
|
|
516
581
|
expiresAt: 'string|required',
|
|
@@ -528,12 +593,12 @@ export class IdentityPlugin extends Plugin {
|
|
|
528
593
|
if (okSessions) {
|
|
529
594
|
this.sessionsResource = sessionsResource;
|
|
530
595
|
if (this.config.verbose) {
|
|
531
|
-
console.log(
|
|
596
|
+
console.log(`[Identity Plugin] Created ${names.sessions} resource`);
|
|
532
597
|
}
|
|
533
|
-
} else if (this.database.resources.
|
|
534
|
-
this.sessionsResource = this.database.resources.
|
|
598
|
+
} else if (this.database.resources[names.sessions]) {
|
|
599
|
+
this.sessionsResource = this.database.resources[names.sessions];
|
|
535
600
|
if (this.config.verbose) {
|
|
536
|
-
console.log(
|
|
601
|
+
console.log(`[Identity Plugin] Using existing ${names.sessions} resource`);
|
|
537
602
|
}
|
|
538
603
|
} else {
|
|
539
604
|
throw errSessions;
|
|
@@ -542,7 +607,7 @@ export class IdentityPlugin extends Plugin {
|
|
|
542
607
|
// 4. Password Reset Tokens Resource (for password reset flow)
|
|
543
608
|
const [okResetTokens, errResetTokens, resetTokensResource] = await tryFn(() =>
|
|
544
609
|
this.database.createResource({
|
|
545
|
-
name:
|
|
610
|
+
name: names.passwordResetTokens,
|
|
546
611
|
attributes: {
|
|
547
612
|
userId: 'string|required',
|
|
548
613
|
token: 'string|required',
|
|
@@ -559,12 +624,12 @@ export class IdentityPlugin extends Plugin {
|
|
|
559
624
|
if (okResetTokens) {
|
|
560
625
|
this.passwordResetTokensResource = resetTokensResource;
|
|
561
626
|
if (this.config.verbose) {
|
|
562
|
-
console.log(
|
|
627
|
+
console.log(`[Identity Plugin] Created ${names.passwordResetTokens} resource`);
|
|
563
628
|
}
|
|
564
|
-
} else if (this.database.resources.
|
|
565
|
-
this.passwordResetTokensResource = this.database.resources.
|
|
629
|
+
} else if (this.database.resources[names.passwordResetTokens]) {
|
|
630
|
+
this.passwordResetTokensResource = this.database.resources[names.passwordResetTokens];
|
|
566
631
|
if (this.config.verbose) {
|
|
567
|
-
console.log(
|
|
632
|
+
console.log(`[Identity Plugin] Using existing ${names.passwordResetTokens} resource`);
|
|
568
633
|
}
|
|
569
634
|
} else {
|
|
570
635
|
throw errResetTokens;
|
|
@@ -574,7 +639,7 @@ export class IdentityPlugin extends Plugin {
|
|
|
574
639
|
if (this.config.mfa.enabled) {
|
|
575
640
|
const [okMFA, errMFA, mfaResource] = await tryFn(() =>
|
|
576
641
|
this.database.createResource({
|
|
577
|
-
name:
|
|
642
|
+
name: names.mfaDevices,
|
|
578
643
|
attributes: {
|
|
579
644
|
userId: 'string|required',
|
|
580
645
|
type: 'string|required', // 'totp', 'sms', 'email'
|
|
@@ -600,15 +665,15 @@ export class IdentityPlugin extends Plugin {
|
|
|
600
665
|
if (okMFA) {
|
|
601
666
|
this.mfaDevicesResource = mfaResource;
|
|
602
667
|
if (this.config.verbose) {
|
|
603
|
-
console.log(
|
|
668
|
+
console.log(`[Identity Plugin] Created ${names.mfaDevices} resource`);
|
|
604
669
|
}
|
|
605
|
-
} else if (this.database.resources.
|
|
606
|
-
this.mfaDevicesResource = this.database.resources.
|
|
670
|
+
} else if (this.database.resources[names.mfaDevices]) {
|
|
671
|
+
this.mfaDevicesResource = this.database.resources[names.mfaDevices];
|
|
607
672
|
if (this.config.verbose) {
|
|
608
|
-
console.log(
|
|
673
|
+
console.log(`[Identity Plugin] Using existing ${names.mfaDevices} resource`);
|
|
609
674
|
}
|
|
610
675
|
} else {
|
|
611
|
-
console.warn(
|
|
676
|
+
console.warn(`[Identity Plugin] MFA enabled but failed to create ${names.mfaDevices} resource:`, errMFA?.message);
|
|
612
677
|
}
|
|
613
678
|
}
|
|
614
679
|
}
|
|
@@ -748,6 +813,7 @@ export class IdentityPlugin extends Plugin {
|
|
|
748
813
|
});
|
|
749
814
|
|
|
750
815
|
await this.oauth2Server.initialize();
|
|
816
|
+
this.oauth2Server.setIdentityPlugin(this);
|
|
751
817
|
|
|
752
818
|
if (this.config.verbose) {
|
|
753
819
|
console.log('[Identity Plugin] OAuth2 Server initialized');
|
|
@@ -938,6 +1004,277 @@ export class IdentityPlugin extends Plugin {
|
|
|
938
1004
|
}
|
|
939
1005
|
}
|
|
940
1006
|
|
|
1007
|
+
async _initializeAuthDrivers() {
|
|
1008
|
+
const driverConfig = this.config.authDrivers;
|
|
1009
|
+
|
|
1010
|
+
if (driverConfig === false) {
|
|
1011
|
+
this.authDrivers = new Map();
|
|
1012
|
+
this.authDriverInstances = [];
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
const disableBuiltIns = this._isPlainObject(driverConfig) && driverConfig.disableBuiltIns === true;
|
|
1016
|
+
|
|
1017
|
+
const context = {
|
|
1018
|
+
database: this.database,
|
|
1019
|
+
config: this.config,
|
|
1020
|
+
resources: {
|
|
1021
|
+
users: this.usersResource,
|
|
1022
|
+
tenants: this.tenantsResource,
|
|
1023
|
+
clients: this.clientsResource
|
|
1024
|
+
},
|
|
1025
|
+
helpers: {
|
|
1026
|
+
password: {
|
|
1027
|
+
verify: verifyPassword
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
|
|
1032
|
+
const drivers = [];
|
|
1033
|
+
|
|
1034
|
+
if (!disableBuiltIns) {
|
|
1035
|
+
const builtInOptions = this._extractBuiltInDriverOptions(driverConfig);
|
|
1036
|
+
drivers.push(...createBuiltInAuthDrivers(builtInOptions));
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
drivers.push(...this._collectCustomAuthDrivers(driverConfig));
|
|
1040
|
+
|
|
1041
|
+
if (!drivers.length) {
|
|
1042
|
+
this.authDrivers = new Map();
|
|
1043
|
+
this.authDriverInstances = [];
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
this.authDrivers = new Map();
|
|
1048
|
+
this.authDriverInstances = [];
|
|
1049
|
+
|
|
1050
|
+
for (const driver of drivers) {
|
|
1051
|
+
if (!driver || typeof driver.initialize !== 'function') {
|
|
1052
|
+
throw new PluginError('Auth drivers must implement initialize(context)', {
|
|
1053
|
+
pluginName: 'IdentityPlugin',
|
|
1054
|
+
operation: 'initializeAuthDrivers',
|
|
1055
|
+
statusCode: 500,
|
|
1056
|
+
retriable: false,
|
|
1057
|
+
suggestion: 'Ensure custom auth drivers extend AuthDriver and implement initialize(context).'
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
try {
|
|
1062
|
+
await driver.initialize(context);
|
|
1063
|
+
} catch (error) {
|
|
1064
|
+
const driverName = driver.name || driver.constructor?.name || 'UnknownDriver';
|
|
1065
|
+
throw new PluginError(`Failed to initialize auth driver "${driverName}": ${error.message}`, {
|
|
1066
|
+
pluginName: 'IdentityPlugin',
|
|
1067
|
+
operation: 'initializeAuthDrivers',
|
|
1068
|
+
statusCode: 500,
|
|
1069
|
+
retriable: false,
|
|
1070
|
+
suggestion: 'Review driver configuration and ensure required dependencies are available.',
|
|
1071
|
+
original: error,
|
|
1072
|
+
metadata: { driverName }
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
const supportedTypes = Array.isArray(driver.supportedTypes) && driver.supportedTypes.length > 0
|
|
1077
|
+
? driver.supportedTypes
|
|
1078
|
+
: driver.name
|
|
1079
|
+
? [driver.name]
|
|
1080
|
+
: [];
|
|
1081
|
+
|
|
1082
|
+
if (!supportedTypes.length) {
|
|
1083
|
+
const driverName = driver.constructor?.name || 'AuthDriver';
|
|
1084
|
+
throw new PluginError(`Auth driver "${driverName}" must declare supportedTypes or name`, {
|
|
1085
|
+
pluginName: 'IdentityPlugin',
|
|
1086
|
+
operation: 'registerAuthDriver',
|
|
1087
|
+
statusCode: 500,
|
|
1088
|
+
retriable: false,
|
|
1089
|
+
suggestion: 'Set driver.supportedTypes = ["password"] or provide a name property to map grants.',
|
|
1090
|
+
metadata: { driverName }
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
for (const type of supportedTypes) {
|
|
1095
|
+
if (!type) continue;
|
|
1096
|
+
if (this.authDrivers.has(type)) {
|
|
1097
|
+
const existingDriver = this.authDrivers.get(type);
|
|
1098
|
+
const existingName = existingDriver?.name || existingDriver?.constructor?.name || 'AuthDriver';
|
|
1099
|
+
const newName = driver.name || driver.constructor?.name || 'AuthDriver';
|
|
1100
|
+
throw new PluginError(`Duplicate auth driver registration for type "${type}"`, {
|
|
1101
|
+
pluginName: 'IdentityPlugin',
|
|
1102
|
+
operation: 'registerAuthDriver',
|
|
1103
|
+
statusCode: 409,
|
|
1104
|
+
retriable: false,
|
|
1105
|
+
suggestion: 'Remove duplicate registrations or use distinct driver types for each grant.',
|
|
1106
|
+
metadata: { type, existingDriver: existingName, newDriver: newName }
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
this.authDrivers.set(type, driver);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
this.authDriverInstances.push(driver);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
getAuthDriver(type) {
|
|
1117
|
+
return this.authDrivers.get(type);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
_sanitizeAuthSubject(subject) {
|
|
1121
|
+
if (!subject || typeof subject !== 'object') {
|
|
1122
|
+
return subject;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
const sensitiveFields = [
|
|
1126
|
+
'password',
|
|
1127
|
+
'passwordHash',
|
|
1128
|
+
'password_hash',
|
|
1129
|
+
'salt',
|
|
1130
|
+
'secret',
|
|
1131
|
+
'secrets',
|
|
1132
|
+
'clientSecret',
|
|
1133
|
+
'mfaSecret',
|
|
1134
|
+
'totpSecret',
|
|
1135
|
+
'backupCodes'
|
|
1136
|
+
];
|
|
1137
|
+
|
|
1138
|
+
const sanitized = { ...subject };
|
|
1139
|
+
for (const field of sensitiveFields) {
|
|
1140
|
+
if (sanitized[field] !== undefined) {
|
|
1141
|
+
delete sanitized[field];
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
return sanitized;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
async authenticateWithPassword({ email, password, user }) {
|
|
1149
|
+
const driver = this.getAuthDriver('password');
|
|
1150
|
+
if (!driver) {
|
|
1151
|
+
return {
|
|
1152
|
+
success: false,
|
|
1153
|
+
error: 'password_driver_not_configured',
|
|
1154
|
+
statusCode: 500
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
const result = await driver.authenticate({
|
|
1159
|
+
type: 'password',
|
|
1160
|
+
email,
|
|
1161
|
+
password,
|
|
1162
|
+
user
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
if (result?.success && result.user) {
|
|
1166
|
+
return {
|
|
1167
|
+
...result,
|
|
1168
|
+
user: this._sanitizeAuthSubject(result.user)
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
return result;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
_collectCustomAuthDrivers(config) {
|
|
1176
|
+
const candidates = [];
|
|
1177
|
+
|
|
1178
|
+
const addCandidate = (candidate) => {
|
|
1179
|
+
if (!candidate) return;
|
|
1180
|
+
|
|
1181
|
+
if (Array.isArray(candidate)) {
|
|
1182
|
+
if (candidate.length > 0 && typeof candidate[0] === 'function') {
|
|
1183
|
+
const [Ctor, options] = candidate;
|
|
1184
|
+
const instance = new Ctor(options);
|
|
1185
|
+
if (!(instance instanceof AuthDriver)) {
|
|
1186
|
+
throw new PluginError('Custom auth driver constructors must extend AuthDriver', {
|
|
1187
|
+
pluginName: 'IdentityPlugin',
|
|
1188
|
+
operation: 'collectCustomAuthDrivers',
|
|
1189
|
+
statusCode: 500,
|
|
1190
|
+
retriable: false,
|
|
1191
|
+
suggestion: 'Extend AuthDriver to ensure consistent interface for initialize/authenticate.'
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
candidates.push(instance);
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
for (const item of candidate) {
|
|
1198
|
+
addCandidate(item);
|
|
1199
|
+
}
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
if (candidate instanceof AuthDriver) {
|
|
1204
|
+
candidates.push(candidate);
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
if (typeof candidate === 'function') {
|
|
1209
|
+
const instance = new candidate();
|
|
1210
|
+
if (!(instance instanceof AuthDriver)) {
|
|
1211
|
+
throw new PluginError('Custom auth driver constructors must extend AuthDriver', {
|
|
1212
|
+
pluginName: 'IdentityPlugin',
|
|
1213
|
+
operation: 'collectCustomAuthDrivers',
|
|
1214
|
+
statusCode: 500,
|
|
1215
|
+
retriable: false,
|
|
1216
|
+
suggestion: 'Update the constructor to extend AuthDriver before registering it.'
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
candidates.push(instance);
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
if (candidate && typeof candidate === 'object' &&
|
|
1224
|
+
typeof candidate.initialize === 'function' &&
|
|
1225
|
+
typeof candidate.authenticate === 'function'
|
|
1226
|
+
) {
|
|
1227
|
+
candidates.push(candidate);
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
throw new PluginError('Invalid auth driver provided. Drivers must extend AuthDriver.', {
|
|
1232
|
+
pluginName: 'IdentityPlugin',
|
|
1233
|
+
operation: 'collectCustomAuthDrivers',
|
|
1234
|
+
statusCode: 400,
|
|
1235
|
+
retriable: false,
|
|
1236
|
+
suggestion: 'Provide an AuthDriver instance, subclass, or plain object with initialize/authenticate methods.'
|
|
1237
|
+
});
|
|
1238
|
+
};
|
|
1239
|
+
|
|
1240
|
+
if (Array.isArray(config)) {
|
|
1241
|
+
addCandidate(config);
|
|
1242
|
+
return candidates;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
if (this._isPlainObject(config)) {
|
|
1246
|
+
addCandidate(config.drivers);
|
|
1247
|
+
addCandidate(config.custom);
|
|
1248
|
+
addCandidate(config.customDrivers);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
return candidates;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
_extractBuiltInDriverOptions(config) {
|
|
1255
|
+
if (!this._isPlainObject(config)) {
|
|
1256
|
+
return {};
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
if (this._isPlainObject(config.builtIns)) {
|
|
1260
|
+
return config.builtIns;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
const {
|
|
1264
|
+
drivers,
|
|
1265
|
+
custom,
|
|
1266
|
+
customDrivers,
|
|
1267
|
+
disableBuiltIns,
|
|
1268
|
+
...builtInOptions
|
|
1269
|
+
} = config;
|
|
1270
|
+
|
|
1271
|
+
return builtInOptions;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
_isPlainObject(value) {
|
|
1275
|
+
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
941
1278
|
/**
|
|
942
1279
|
* Start plugin
|
|
943
1280
|
*/
|
|
@@ -1019,7 +1356,14 @@ export class IdentityPlugin extends Plugin {
|
|
|
1019
1356
|
|
|
1020
1357
|
// Optionally delete OAuth2 resources
|
|
1021
1358
|
if (purgeData) {
|
|
1022
|
-
const resourcesToDelete = [
|
|
1359
|
+
const resourcesToDelete = new Set([
|
|
1360
|
+
this.internalResourceNames.oauthKeys,
|
|
1361
|
+
this.internalResourceNames.authCodes,
|
|
1362
|
+
this.internalResourceNames.sessions,
|
|
1363
|
+
this.internalResourceNames.passwordResetTokens,
|
|
1364
|
+
this.internalResourceNames.mfaDevices,
|
|
1365
|
+
'plg_oauth_clients'
|
|
1366
|
+
]);
|
|
1023
1367
|
|
|
1024
1368
|
for (const resourceName of resourcesToDelete) {
|
|
1025
1369
|
const [ok] = await tryFn(() => this.database.deleteResource(resourceName));
|