auth0-deploy-cli 9.0.0-beta.2 → 9.1.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/AGENTS.md +2 -357
- package/CHANGELOG.md +185 -10
- package/CLAUDE.md +138 -2
- package/README.md +1 -1
- package/lib/context/defaults.d.ts +7 -0
- package/lib/context/defaults.js +74 -30
- package/lib/context/directory/handlers/actionModules.js +1 -4
- package/lib/context/directory/handlers/actions.js +1 -4
- package/lib/context/directory/handlers/attackProtection.js +11 -1
- package/lib/context/directory/handlers/branding.js +4 -1
- package/lib/context/directory/handlers/clientAuthCredentials.d.ts +5 -0
- package/lib/context/directory/handlers/clientAuthCredentials.js +13 -0
- package/lib/context/directory/handlers/clientGrants.js +78 -24
- package/lib/context/directory/handlers/clients.js +20 -3
- package/lib/context/directory/handlers/connections.js +19 -5
- package/lib/context/directory/handlers/databases.js +1 -4
- package/lib/context/directory/handlers/emailTemplates.js +5 -0
- package/lib/context/directory/handlers/guardianEmailFactorSettings.d.ts +5 -0
- package/lib/context/directory/handlers/guardianEmailFactorSettings.js +38 -0
- package/lib/context/directory/handlers/guardianPhoneFactorSettings.d.ts +5 -0
- package/lib/context/directory/handlers/guardianPhoneFactorSettings.js +38 -0
- package/lib/context/directory/handlers/guardianSettings.d.ts +5 -0
- package/lib/context/directory/handlers/guardianSettings.js +38 -0
- package/lib/context/directory/handlers/hooks.js +1 -4
- package/lib/context/directory/handlers/index.js +12 -0
- package/lib/context/directory/handlers/networkACLKeys.d.ts +6 -0
- package/lib/context/directory/handlers/networkACLKeys.js +54 -0
- package/lib/context/directory/handlers/prompts.js +7 -3
- package/lib/context/directory/handlers/rateLimitPolicies.d.ts +6 -0
- package/lib/context/directory/handlers/rateLimitPolicies.js +53 -0
- package/lib/context/directory/handlers/rules.js +1 -4
- package/lib/context/directory/index.js +1 -4
- package/lib/context/index.js +32 -31
- package/lib/context/yaml/handlers/attackProtection.js +5 -2
- package/lib/context/yaml/handlers/branding.js +4 -1
- package/lib/context/yaml/handlers/clientAuthCredentials.d.ts +5 -0
- package/lib/context/yaml/handlers/clientAuthCredentials.js +13 -0
- package/lib/context/yaml/handlers/clients.js +19 -1
- package/lib/context/yaml/handlers/connections.js +8 -1
- package/lib/context/yaml/handlers/flows.js +3 -0
- package/lib/context/yaml/handlers/forms.js +3 -0
- package/lib/context/yaml/handlers/guardianEmailFactorSettings.d.ts +5 -0
- package/lib/context/yaml/handlers/guardianEmailFactorSettings.js +15 -0
- package/lib/context/yaml/handlers/guardianPhoneFactorSettings.d.ts +5 -0
- package/lib/context/yaml/handlers/guardianPhoneFactorSettings.js +15 -0
- package/lib/context/yaml/handlers/guardianSettings.d.ts +5 -0
- package/lib/context/yaml/handlers/guardianSettings.js +15 -0
- package/lib/context/yaml/handlers/index.js +12 -0
- package/lib/context/yaml/handlers/networkACLKeys.d.ts +6 -0
- package/lib/context/yaml/handlers/networkACLKeys.js +42 -0
- package/lib/context/yaml/handlers/prompts.js +3 -1
- package/lib/context/yaml/handlers/rateLimitPolicies.d.ts +6 -0
- package/lib/context/yaml/handlers/rateLimitPolicies.js +27 -0
- package/lib/context/yaml/index.js +85 -7
- package/lib/keywordPreservation.d.ts +1 -1
- package/lib/keywordPreservation.js +2 -1
- package/lib/readonly.js +5 -0
- package/lib/tools/auth0/handlers/attackProtection.d.ts +12 -0
- package/lib/tools/auth0/handlers/attackProtection.js +66 -6
- package/lib/tools/auth0/handlers/clientAuthCredentials.d.ts +27 -0
- package/lib/tools/auth0/handlers/clientAuthCredentials.js +234 -0
- package/lib/tools/auth0/handlers/clientAuthCredentialsPre.d.ts +38 -0
- package/lib/tools/auth0/handlers/clientAuthCredentialsPre.js +123 -0
- package/lib/tools/auth0/handlers/clientGrants.d.ts +1 -1
- package/lib/tools/auth0/handlers/clients.d.ts +106 -0
- package/lib/tools/auth0/handlers/clients.js +207 -3
- package/lib/tools/auth0/handlers/connectionProfiles.d.ts +27 -0
- package/lib/tools/auth0/handlers/connectionProfiles.js +30 -0
- package/lib/tools/auth0/handlers/connections.d.ts +34 -6
- package/lib/tools/auth0/handlers/connections.js +75 -11
- package/lib/tools/auth0/handlers/databases.d.ts +28 -0
- package/lib/tools/auth0/handlers/databases.js +58 -24
- package/lib/tools/auth0/handlers/default.d.ts +23 -0
- package/lib/tools/auth0/handlers/default.js +47 -29
- package/lib/tools/auth0/handlers/eventStreams.js +10 -2
- package/lib/tools/auth0/handlers/guardianEmailFactorSettings.d.ts +22 -0
- package/lib/tools/auth0/handlers/guardianEmailFactorSettings.js +77 -0
- package/lib/tools/auth0/handlers/guardianFactorTemplates.js +13 -4
- package/lib/tools/auth0/handlers/guardianPhoneFactorMessageTypes.js +11 -9
- package/lib/tools/auth0/handlers/guardianPhoneFactorSelectedProvider.js +11 -9
- package/lib/tools/auth0/handlers/guardianPhoneFactorSettings.d.ts +22 -0
- package/lib/tools/auth0/handlers/guardianPhoneFactorSettings.js +77 -0
- package/lib/tools/auth0/handlers/guardianPolicies.js +11 -1
- package/lib/tools/auth0/handlers/guardianSettings.d.ts +30 -0
- package/lib/tools/auth0/handlers/guardianSettings.js +85 -0
- package/lib/tools/auth0/handlers/hooks.js +1 -1
- package/lib/tools/auth0/handlers/index.js +16 -0
- package/lib/tools/auth0/handlers/networkACLKeys.d.ts +45 -0
- package/lib/tools/auth0/handlers/networkACLKeys.js +177 -0
- package/lib/tools/auth0/handlers/networkACLs.d.ts +132 -2
- package/lib/tools/auth0/handlers/networkACLs.js +100 -16
- package/lib/tools/auth0/handlers/organizations.d.ts +33 -1
- package/lib/tools/auth0/handlers/organizations.js +177 -19
- package/lib/tools/auth0/handlers/phoneProvider.js +10 -2
- package/lib/tools/auth0/handlers/phoneTemplates.d.ts +4 -1
- package/lib/tools/auth0/handlers/phoneTemplates.js +67 -10
- package/lib/tools/auth0/handlers/prompts.d.ts +17 -0
- package/lib/tools/auth0/handlers/prompts.js +47 -14
- package/lib/tools/auth0/handlers/rateLimitPolicies.d.ts +81 -0
- package/lib/tools/auth0/handlers/rateLimitPolicies.js +203 -0
- package/lib/tools/auth0/handlers/resourceServers.d.ts +48 -0
- package/lib/tools/auth0/handlers/resourceServers.js +56 -2
- package/lib/tools/auth0/handlers/riskAssessment.d.ts +1 -1
- package/lib/tools/auth0/handlers/riskAssessment.js +19 -1
- package/lib/tools/auth0/handlers/roles.d.ts +4 -0
- package/lib/tools/auth0/handlers/roles.js +27 -9
- package/lib/tools/auth0/handlers/rules.js +19 -7
- package/lib/tools/auth0/handlers/scimHandler.js +8 -3
- package/lib/tools/auth0/handlers/tenant.d.ts +45 -0
- package/lib/tools/auth0/handlers/tenant.js +48 -0
- package/lib/tools/auth0/handlers/themes.d.ts +37 -1
- package/lib/tools/auth0/handlers/themes.js +64 -0
- package/lib/tools/auth0/handlers/tokenExchangeProfiles.js +3 -1
- package/lib/tools/auth0/index.js +12 -6
- package/lib/tools/calculateDryRunChanges.js +12 -8
- package/lib/tools/constants.d.ts +3 -0
- package/lib/tools/constants.js +8 -0
- package/lib/tools/utils.d.ts +3 -0
- package/lib/tools/utils.js +41 -2
- package/lib/types.d.ts +10 -1
- package/lib/utils.d.ts +6 -0
- package/lib/utils.js +17 -2
- package/package.json +2 -2
- package/references/code-style.md +133 -0
- package/references/commands.md +98 -0
- package/references/docs-update.md +34 -0
- package/references/git-workflow.md +135 -0
- package/references/pitfalls.md +62 -0
- package/references/testing.md +118 -0
|
@@ -181,6 +181,25 @@ exports.schema = {
|
|
|
181
181
|
action_id: { type: 'string' },
|
|
182
182
|
},
|
|
183
183
|
},
|
|
184
|
+
otp_settings: {
|
|
185
|
+
type: 'object',
|
|
186
|
+
properties: {
|
|
187
|
+
email: {
|
|
188
|
+
type: 'object',
|
|
189
|
+
properties: {
|
|
190
|
+
otp_length: { type: 'integer' },
|
|
191
|
+
otp_expiry: { type: 'integer' },
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
phone: {
|
|
195
|
+
type: 'object',
|
|
196
|
+
properties: {
|
|
197
|
+
otp_length: { type: 'integer' },
|
|
198
|
+
otp_expiry: { type: 'integer' },
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
184
203
|
password_options: {
|
|
185
204
|
type: 'object',
|
|
186
205
|
properties: {
|
|
@@ -419,6 +438,22 @@ class DatabaseHandler extends default_1.default {
|
|
|
419
438
|
}
|
|
420
439
|
return this.client.connections[fn].bind(this.client.connections);
|
|
421
440
|
}
|
|
441
|
+
// `enabled_clients` is no longer returned by connections.list; it must be fetched from the
|
|
442
|
+
// dedicated enabled-clients endpoint. Enriching the remote connections keeps the diff and the
|
|
443
|
+
// excluded-client preservation in `getEnabledClients` correct. Without it the dry-run diff
|
|
444
|
+
// reports a false "found in localObj but not in remoteObj" and a real import can silently
|
|
445
|
+
// disable an excluded-but-enabled client.
|
|
446
|
+
async enrichConnectionsWithEnabledClients(connections) {
|
|
447
|
+
return Promise.all(connections.map(async (con) => {
|
|
448
|
+
if (!con?.id)
|
|
449
|
+
return con;
|
|
450
|
+
const enabledClients = await (0, connections_1.getConnectionEnabledClients)(this.client, con.id);
|
|
451
|
+
if (enabledClients?.length) {
|
|
452
|
+
return { ...con, enabled_clients: enabledClients };
|
|
453
|
+
}
|
|
454
|
+
return con;
|
|
455
|
+
}));
|
|
456
|
+
}
|
|
422
457
|
async getType() {
|
|
423
458
|
if (this.existing)
|
|
424
459
|
return this.existing;
|
|
@@ -433,16 +468,7 @@ class DatabaseHandler extends default_1.default {
|
|
|
433
468
|
include_totals: true,
|
|
434
469
|
}),
|
|
435
470
|
]);
|
|
436
|
-
const dbConnectionsWithEnabledClients = await
|
|
437
|
-
if (!con?.id)
|
|
438
|
-
return con;
|
|
439
|
-
const enabledClients = await (0, connections_1.getConnectionEnabledClients)(this.client, con.id);
|
|
440
|
-
const connection = { ...con };
|
|
441
|
-
if (enabledClients && enabledClients?.length) {
|
|
442
|
-
connection.enabled_clients = enabledClients;
|
|
443
|
-
}
|
|
444
|
-
return connection;
|
|
445
|
-
}));
|
|
471
|
+
const dbConnectionsWithEnabledClients = await this.enrichConnectionsWithEnabledClients(connections);
|
|
446
472
|
// Convert action ID back to action name for export
|
|
447
473
|
const dbConnectionsWithActionNames = dbConnectionsWithEnabledClients.map((connection) => {
|
|
448
474
|
if (connection.options && 'custom_password_hash' in connection.options) {
|
|
@@ -462,12 +488,21 @@ class DatabaseHandler extends default_1.default {
|
|
|
462
488
|
}
|
|
463
489
|
return connection;
|
|
464
490
|
});
|
|
491
|
+
// A connection can return both legacy password fields and `password_options` on read, but
|
|
492
|
+
// they're mutually exclusive on write. Strip the legacy fields to keep the export deployable.
|
|
493
|
+
const dbConnectionsNormalized = dbConnectionsWithActionNames.map((connection) => {
|
|
494
|
+
if (connection.options && 'password_options' in connection.options) {
|
|
495
|
+
const { passwordPolicy, password_complexity_options, password_history, password_no_personal_info, password_dictionary, ...options } = connection.options;
|
|
496
|
+
return { ...connection, options };
|
|
497
|
+
}
|
|
498
|
+
return connection;
|
|
499
|
+
});
|
|
465
500
|
// If options option is empty for all connection, log the missing options scope.
|
|
466
|
-
const isOptionExists =
|
|
501
|
+
const isOptionExists = dbConnectionsNormalized.every((c) => c.options && Object.keys(c.options).length > 0);
|
|
467
502
|
if (!isOptionExists) {
|
|
468
503
|
logger_1.default.warn(`Insufficient scope the read:connections_options scope is required to get ${this.type} options.`);
|
|
469
504
|
}
|
|
470
|
-
this.existing =
|
|
505
|
+
this.existing = dbConnectionsNormalized;
|
|
471
506
|
return this.existing;
|
|
472
507
|
}
|
|
473
508
|
async calcChanges(assets) {
|
|
@@ -480,28 +515,26 @@ class DatabaseHandler extends default_1.default {
|
|
|
480
515
|
update: [],
|
|
481
516
|
conflicts: [],
|
|
482
517
|
};
|
|
483
|
-
// Convert enabled_clients by name to the id
|
|
484
|
-
//
|
|
485
|
-
|
|
518
|
+
// Convert enabled_clients by name to the id. `getType()` fetches and enriches the existing
|
|
519
|
+
// connections with `enabled_clients` and caches the result in `this.existing`, so the
|
|
520
|
+
// `super.calcChanges()` call below reuses it instead of fetching again.
|
|
521
|
+
const [clients, actions, existingWithEnabledClients] = await Promise.all([
|
|
486
522
|
(0, client_1.paginate)(this.client.clients.list, {
|
|
487
523
|
paginate: true,
|
|
488
524
|
}),
|
|
489
|
-
(0, client_1.paginate)(this.client.connections.list, {
|
|
490
|
-
strategy: [auth0_1.Management.ConnectionStrategyEnum.Auth0],
|
|
491
|
-
checkpoint: true,
|
|
492
|
-
include_totals: true,
|
|
493
|
-
}),
|
|
494
525
|
(0, client_1.paginate)(this.client.actions.list, {
|
|
495
526
|
paginate: true,
|
|
496
527
|
include_totals: true,
|
|
497
528
|
}),
|
|
529
|
+
this.getType(),
|
|
498
530
|
]);
|
|
531
|
+
const existingConnections = existingWithEnabledClients || [];
|
|
499
532
|
const formatted = databases.map((db) => {
|
|
500
533
|
const { options, ...rest } = db;
|
|
501
534
|
const formattedOptions = this.getFormattedOptions(options, actions);
|
|
502
535
|
const formattedDb = { ...rest, options: formattedOptions };
|
|
503
536
|
if (db.enabled_clients) {
|
|
504
|
-
formattedDb.enabled_clients = (0, utils_1.getEnabledClients)(assets, db,
|
|
537
|
+
formattedDb.enabled_clients = (0, utils_1.getEnabledClients)(assets, db, existingConnections, clients);
|
|
505
538
|
}
|
|
506
539
|
return formattedDb;
|
|
507
540
|
});
|
|
@@ -531,21 +564,22 @@ class DatabaseHandler extends default_1.default {
|
|
|
531
564
|
include_totals: true,
|
|
532
565
|
}),
|
|
533
566
|
]);
|
|
567
|
+
const existingWithEnabledClients = await this.enrichConnectionsWithEnabledClients(existingDatabasesConnections);
|
|
534
568
|
const formatted = databases.map((db) => {
|
|
535
569
|
const { options, ...rest } = db;
|
|
536
570
|
const formattedOptions = this.getFormattedOptions(options, actions);
|
|
537
571
|
const formattedDb = { ...rest, options: formattedOptions };
|
|
538
572
|
if (db.enabled_clients) {
|
|
539
|
-
formattedDb.enabled_clients = (0, utils_1.getEnabledClients)(assets, db,
|
|
573
|
+
formattedDb.enabled_clients = (0, utils_1.getEnabledClients)(assets, db, existingWithEnabledClients, clients);
|
|
540
574
|
}
|
|
541
575
|
return formattedDb;
|
|
542
576
|
});
|
|
543
577
|
return (0, calculateDryRunChanges_1.calculateDryRunChanges)({
|
|
544
578
|
type: this.type,
|
|
545
579
|
assets: formatted,
|
|
546
|
-
existing:
|
|
580
|
+
existing: existingWithEnabledClients,
|
|
547
581
|
identifiers: this.identifiers,
|
|
548
|
-
ignoreDryRunFields: this.
|
|
582
|
+
ignoreDryRunFields: this.getEffectiveIgnoreDryRunFields(),
|
|
549
583
|
});
|
|
550
584
|
}
|
|
551
585
|
// Run after clients are updated so we can convert all the enabled_clients names to id's
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { Asset, Assets, Auth0APIClient, CalculatedChanges } from '../../../types';
|
|
2
2
|
import { ConfigFunction } from '../../../configFactory';
|
|
3
3
|
export declare function order(value: any): (t: any, n: any, descriptor: any) => any;
|
|
4
|
+
export interface RetryOptions {
|
|
5
|
+
maxRetries?: number;
|
|
6
|
+
initialDelay?: number;
|
|
7
|
+
maxDelay?: number;
|
|
8
|
+
onRetry?: (error: any, attempt: number, delay: number) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Executes a function with exponential backoff retry logic for rate limit errors (429).
|
|
12
|
+
*
|
|
13
|
+
* @param fn - The function to execute with retry logic
|
|
14
|
+
* @param options - Configuration options for retry behavior
|
|
15
|
+
* @returns Promise that resolves with the function result or rejects after max retries
|
|
16
|
+
*/
|
|
17
|
+
export declare function retryWithExponentialBackoff<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
4
18
|
type ApiMethodOverride = string | Function;
|
|
5
19
|
export default class APIHandler {
|
|
6
20
|
config: ConfigFunction;
|
|
@@ -50,6 +64,15 @@ export default class APIHandler {
|
|
|
50
64
|
*/
|
|
51
65
|
getEffectiveIgnoreDryRunFields(): string[];
|
|
52
66
|
getClientFN(fn: ApiMethodOverride): Function;
|
|
67
|
+
/**
|
|
68
|
+
* Builds the exponential-backoff retry configuration for this handler from the
|
|
69
|
+
* shared `AUTH0_MAX_RETRIES` / `AUTH0_RETRY_INITIAL_DELAY_MS` /
|
|
70
|
+
* `AUTH0_RETRY_MAX_DELAY_MS` config keys. Exposed so that handlers which issue
|
|
71
|
+
* writes outside the default `processChanges` flow (e.g. organizations, which
|
|
72
|
+
* writes nested connections/grants directly) can wrap those calls in the same
|
|
73
|
+
* 429 backoff behaviour as the base handler.
|
|
74
|
+
*/
|
|
75
|
+
getRetryConfig(): RetryOptions;
|
|
53
76
|
didDelete(item: Asset): void;
|
|
54
77
|
didCreate(item: Asset): void;
|
|
55
78
|
didUpdate(item: Asset): void;
|
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.order = order;
|
|
7
|
-
|
|
7
|
+
exports.retryWithExponentialBackoff = retryWithExponentialBackoff;
|
|
8
8
|
const validationError_1 = __importDefault(require("../../validationError"));
|
|
9
9
|
const utils_1 = require("../../utils");
|
|
10
10
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
@@ -20,6 +20,19 @@ function order(value) {
|
|
|
20
20
|
return descriptor;
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* node-auth0 v7 removed the exported `JSONApiResponse` class, so we can no
|
|
25
|
+
* longer use `instanceof` to tell an SDK response envelope apart from a plain
|
|
26
|
+
* asset. Duck-type the `ApiResponse<T>` shape ({ data, status, headers })
|
|
27
|
+
* instead.
|
|
28
|
+
*/
|
|
29
|
+
function isApiResponseEnvelope(item) {
|
|
30
|
+
return (typeof item === 'object' &&
|
|
31
|
+
item !== null &&
|
|
32
|
+
'data' in item &&
|
|
33
|
+
'status' in item &&
|
|
34
|
+
'headers' in item);
|
|
35
|
+
}
|
|
23
36
|
// Retry configuration constants
|
|
24
37
|
const DEFAULT_MAX_RETRIES = 3;
|
|
25
38
|
const DEFAULT_INITIAL_DELAY_MS = 1000; // 1 second
|
|
@@ -114,24 +127,35 @@ class APIHandler {
|
|
|
114
127
|
}
|
|
115
128
|
return fn;
|
|
116
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Builds the exponential-backoff retry configuration for this handler from the
|
|
132
|
+
* shared `AUTH0_MAX_RETRIES` / `AUTH0_RETRY_INITIAL_DELAY_MS` /
|
|
133
|
+
* `AUTH0_RETRY_MAX_DELAY_MS` config keys. Exposed so that handlers which issue
|
|
134
|
+
* writes outside the default `processChanges` flow (e.g. organizations, which
|
|
135
|
+
* writes nested connections/grants directly) can wrap those calls in the same
|
|
136
|
+
* 429 backoff behaviour as the base handler.
|
|
137
|
+
*/
|
|
138
|
+
getRetryConfig() {
|
|
139
|
+
const retryConfig = {
|
|
140
|
+
maxRetries: this.config('AUTH0_MAX_RETRIES') || DEFAULT_MAX_RETRIES,
|
|
141
|
+
initialDelay: this.config('AUTH0_RETRY_INITIAL_DELAY_MS') || DEFAULT_INITIAL_DELAY_MS,
|
|
142
|
+
maxDelay: this.config('AUTH0_RETRY_MAX_DELAY_MS') || DEFAULT_MAX_DELAY_MS,
|
|
143
|
+
onRetry: (error, attempt, delay) => {
|
|
144
|
+
logger_1.default.warn(`Rate limit hit for [${this.type}]. Retrying attempt ${attempt}/${retryConfig.maxRetries} after ${Math.round(delay / 1000)}s...`);
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
return retryConfig;
|
|
148
|
+
}
|
|
117
149
|
didDelete(item) {
|
|
118
150
|
logger_1.default.info(`Deleted [${this.type}]: ${this.objString(item)}`);
|
|
119
151
|
}
|
|
120
152
|
didCreate(item) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
logger_1.default.info(`Created [${this.type}]: ${this.objString(item)}`);
|
|
126
|
-
}
|
|
153
|
+
const payload = isApiResponseEnvelope(item) ? item.data : item;
|
|
154
|
+
logger_1.default.info(`Created [${this.type}]: ${this.objString(payload)}`);
|
|
127
155
|
}
|
|
128
156
|
didUpdate(item) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
logger_1.default.info(`Updated [${this.type}]: ${this.objString(item)}`);
|
|
134
|
-
}
|
|
157
|
+
const payload = isApiResponseEnvelope(item) ? item.data : item;
|
|
158
|
+
logger_1.default.info(`Updated [${this.type}]: ${this.objString(payload)}`);
|
|
135
159
|
}
|
|
136
160
|
objString(item) {
|
|
137
161
|
return (0, utils_1.convertJsonToString)(item);
|
|
@@ -168,6 +192,9 @@ class APIHandler {
|
|
|
168
192
|
this.type === 'branding' ||
|
|
169
193
|
this.type === 'emailProvider' ||
|
|
170
194
|
this.type === 'guardianPhoneFactorSelectedProvider' ||
|
|
195
|
+
this.type === 'guardianPhoneFactorSettings' ||
|
|
196
|
+
this.type === 'guardianEmailFactorSettings' ||
|
|
197
|
+
this.type === 'guardianSettings' ||
|
|
171
198
|
this.type === 'guardianPolicies' ||
|
|
172
199
|
this.type === 'riskAssessment') {
|
|
173
200
|
return `${this.type} settings`;
|
|
@@ -210,7 +237,6 @@ class APIHandler {
|
|
|
210
237
|
return (0, calculateDryRunChanges_1.calculateDryRunChanges)({
|
|
211
238
|
type: this.type,
|
|
212
239
|
assets: typeAssets,
|
|
213
|
-
// @ts-ignore TODO: investigate what happens when `existing` is null
|
|
214
240
|
existing,
|
|
215
241
|
identifiers: this.identifiers,
|
|
216
242
|
ignoreDryRunFields: this.getEffectiveIgnoreDryRunFields(),
|
|
@@ -243,7 +269,6 @@ class APIHandler {
|
|
|
243
269
|
return (0, calculateDryRunChanges_1.calculateDryRunChanges)({
|
|
244
270
|
type: this.type,
|
|
245
271
|
assets: typeAssets,
|
|
246
|
-
// @ts-ignore TODO: investigate what happens when `existing` is null
|
|
247
272
|
existing,
|
|
248
273
|
identifiers: this.identifiers,
|
|
249
274
|
ignoreDryRunFields: this.getEffectiveIgnoreDryRunFields(),
|
|
@@ -282,14 +307,7 @@ class APIHandler {
|
|
|
282
307
|
const conflicts = changes.conflicts || [];
|
|
283
308
|
logger_1.default.debug(`Start processChanges for ${this.type} [delete:${del.length}] [update:${update.length}], [create:${create.length}], [conflicts:${conflicts.length}]`);
|
|
284
309
|
// Set retry configuration from config
|
|
285
|
-
const retryConfig =
|
|
286
|
-
maxRetries: this.config('AUTH0_MAX_RETRIES') || DEFAULT_MAX_RETRIES,
|
|
287
|
-
initialDelay: this.config('AUTH0_RETRY_INITIAL_DELAY_MS') || DEFAULT_INITIAL_DELAY_MS,
|
|
288
|
-
maxDelay: this.config('AUTH0_RETRY_MAX_DELAY_MS') || DEFAULT_MAX_DELAY_MS,
|
|
289
|
-
onRetry: (error, attempt, delay) => {
|
|
290
|
-
logger_1.default.warn(`Rate limit hit for [${this.type}]. Retrying attempt ${attempt}/${retryConfig.maxRetries} after ${Math.round(delay / 1000)}s...`);
|
|
291
|
-
},
|
|
292
|
-
};
|
|
310
|
+
const retryConfig = this.getRetryConfig();
|
|
293
311
|
// Process Deleted
|
|
294
312
|
if (del.length > 0) {
|
|
295
313
|
const allowDelete = this.config('AUTH0_ALLOW_DELETE') === 'true' || this.config('AUTH0_ALLOW_DELETE') === true;
|
|
@@ -328,8 +346,8 @@ class APIHandler {
|
|
|
328
346
|
const updateFN = this.getClientFN(this.functions.update);
|
|
329
347
|
const updatePayload = (() => {
|
|
330
348
|
const data = (0, utils_1.stripFields)({ ...updateItem }, this.stripUpdateFields);
|
|
331
|
-
const
|
|
332
|
-
return (0, utils_1.
|
|
349
|
+
const obfuscatedStripped = (0, utils_1.stripObfuscatedFieldsFromPayload)(data, this.sensitiveFieldsToObfuscate);
|
|
350
|
+
return (0, utils_1.stripUnresolvedPlaceholders)(obfuscatedStripped, this.type, this.objString(updateItem));
|
|
333
351
|
})();
|
|
334
352
|
return updateFN(updateItem[this.id], updatePayload);
|
|
335
353
|
}, retryConfig)
|
|
@@ -347,8 +365,8 @@ class APIHandler {
|
|
|
347
365
|
const createFunction = this.getClientFN(this.functions.create);
|
|
348
366
|
const createPayload = (() => {
|
|
349
367
|
const strippedPayload = (0, utils_1.stripFields)(createItem, this.stripCreateFields);
|
|
350
|
-
const
|
|
351
|
-
return (0, utils_1.
|
|
368
|
+
const obfuscatedStripped = (0, utils_1.stripObfuscatedFieldsFromPayload)(strippedPayload, this.sensitiveFieldsToObfuscate);
|
|
369
|
+
return (0, utils_1.stripUnresolvedPlaceholders)(obfuscatedStripped, this.type, this.objString(createItem));
|
|
352
370
|
})();
|
|
353
371
|
return createFunction(createPayload);
|
|
354
372
|
}, retryConfig)
|
|
@@ -369,8 +387,8 @@ class APIHandler {
|
|
|
369
387
|
const updateFN = this.getClientFN(this.functions.update);
|
|
370
388
|
const updatePayload = (() => {
|
|
371
389
|
const data = (0, utils_1.stripFields)({ ...updateItem }, this.stripUpdateFields);
|
|
372
|
-
const
|
|
373
|
-
return (0, utils_1.
|
|
390
|
+
const obfuscatedStripped = (0, utils_1.stripObfuscatedFieldsFromPayload)(data, this.sensitiveFieldsToObfuscate);
|
|
391
|
+
return (0, utils_1.stripUnresolvedPlaceholders)(obfuscatedStripped, this.type, this.objString(updateItem));
|
|
374
392
|
})();
|
|
375
393
|
return updateFN(updateItem[this.id], updatePayload);
|
|
376
394
|
}, retryConfig)
|
|
@@ -38,10 +38,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
38
38
|
return result;
|
|
39
39
|
};
|
|
40
40
|
})();
|
|
41
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
42
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
43
|
+
};
|
|
41
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
45
|
exports.schema = void 0;
|
|
43
46
|
const default_1 = __importStar(require("./default"));
|
|
44
47
|
const client_1 = require("../client");
|
|
48
|
+
const logger_1 = __importDefault(require("../../../logger"));
|
|
45
49
|
exports.schema = {
|
|
46
50
|
type: 'array',
|
|
47
51
|
items: {
|
|
@@ -161,8 +165,12 @@ class EventStreamsHandler extends default_1.default {
|
|
|
161
165
|
create: changesResponse.create.map((stream) => this.resolveActionId(stream)),
|
|
162
166
|
update: changesResponse.update.map((stream) => {
|
|
163
167
|
const resolved = this.resolveActionId(stream);
|
|
164
|
-
//
|
|
165
|
-
|
|
168
|
+
// A destination's type cannot be changed once an event stream is created. The API
|
|
169
|
+
// rejects the destination on PATCH for eventbridge and action streams, so strip it
|
|
170
|
+
// from the update payload and only apply the other mutable fields.
|
|
171
|
+
if (resolved.destination?.type === 'eventbridge' ||
|
|
172
|
+
resolved.destination?.type === 'action') {
|
|
173
|
+
logger_1.default.info(`Skipping destination update for event stream "${resolved.name}": ${resolved.destination.type} destinations cannot be changed after creation.`);
|
|
166
174
|
const { destination: _dest, ...rest } = resolved;
|
|
167
175
|
return rest;
|
|
168
176
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import DefaultHandler from './default';
|
|
2
|
+
import { Asset, Assets } from '../../../types';
|
|
3
|
+
export declare const schema: {
|
|
4
|
+
type: string;
|
|
5
|
+
properties: {
|
|
6
|
+
otp_length: {
|
|
7
|
+
type: string;
|
|
8
|
+
description: string;
|
|
9
|
+
};
|
|
10
|
+
otp_expiration_time: {
|
|
11
|
+
type: string;
|
|
12
|
+
description: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
additionalProperties: boolean;
|
|
16
|
+
};
|
|
17
|
+
export default class GuardianEmailFactorSettingsHandler extends DefaultHandler {
|
|
18
|
+
existing: Asset;
|
|
19
|
+
constructor(options: any);
|
|
20
|
+
getType(): Promise<Asset | null>;
|
|
21
|
+
processChanges(assets: Assets): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.schema = void 0;
|
|
7
|
+
const default_1 = __importDefault(require("./default"));
|
|
8
|
+
const utils_1 = require("../../utils");
|
|
9
|
+
exports.schema = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
otp_length: {
|
|
13
|
+
type: 'number',
|
|
14
|
+
description: 'The length of the OTP code.',
|
|
15
|
+
},
|
|
16
|
+
otp_expiration_time: {
|
|
17
|
+
type: 'number',
|
|
18
|
+
description: 'The OTP expiration time in seconds.',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
additionalProperties: false,
|
|
22
|
+
};
|
|
23
|
+
class GuardianEmailFactorSettingsHandler extends default_1.default {
|
|
24
|
+
constructor(options) {
|
|
25
|
+
super({
|
|
26
|
+
...options,
|
|
27
|
+
type: 'guardianEmailFactorSettings',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async getType() {
|
|
31
|
+
if (this.existing)
|
|
32
|
+
return this.existing;
|
|
33
|
+
try {
|
|
34
|
+
const data = await this.client.guardian.factors.email.get();
|
|
35
|
+
this.existing = data;
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if ((0, utils_1.isFeatureUnavailableError)(err)) {
|
|
39
|
+
// Gracefully skip processing this configuration value.
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if ((0, utils_1.isForbiddenFeatureError)(err, this.type)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
return this.existing;
|
|
48
|
+
}
|
|
49
|
+
async processChanges(assets) {
|
|
50
|
+
// No API to delete or create guardianEmailFactorSettings, we can only update.
|
|
51
|
+
const { guardianEmailFactorSettings } = assets;
|
|
52
|
+
// Do nothing if not set
|
|
53
|
+
if (!guardianEmailFactorSettings) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if ((0, utils_1.isDryRun)(this.config)) {
|
|
57
|
+
const { del, update, create } = await this.calcChanges(assets);
|
|
58
|
+
if (create.length === 0 && update.length === 0 && del.length === 0) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
await this.client.guardian.factors.email.set(guardianEmailFactorSettings);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
if ((0, utils_1.isFeatureUnavailableError)(err) || (0, utils_1.isForbiddenFeatureError)(err, this.type)) {
|
|
67
|
+
// Feature is unavailable (404) or disabled (403) on this tenant; skip instead of failing
|
|
68
|
+
// the import. The 403 path logs a warning via isForbiddenFeatureError; the 404 path is silent.
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
this.updated += 1;
|
|
74
|
+
this.didUpdate(guardianEmailFactorSettings);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.default = GuardianEmailFactorSettingsHandler;
|
|
@@ -60,11 +60,20 @@ class GuardianFactorTemplatesHandler extends default_1.default {
|
|
|
60
60
|
await Promise.all(guardianFactorTemplates.map(async (fatorTemplates) => {
|
|
61
61
|
const { name, ...data } = fatorTemplates;
|
|
62
62
|
const params = { name: fatorTemplates.name };
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
try {
|
|
64
|
+
if (name === 'sms') {
|
|
65
|
+
await this.client.guardian.factors.sms.setTemplates(data);
|
|
66
|
+
}
|
|
67
|
+
else if (name === 'phone') {
|
|
68
|
+
await this.client.guardian.factors.phone.setTemplates(data);
|
|
69
|
+
}
|
|
65
70
|
}
|
|
66
|
-
|
|
67
|
-
|
|
71
|
+
catch (err) {
|
|
72
|
+
if ((0, utils_1.isForbiddenFeatureError)(err, this.type)) {
|
|
73
|
+
// Feature is deprecated/disabled on this tenant; warn and skip instead of failing the import.
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
throw err;
|
|
68
77
|
}
|
|
69
78
|
this.didUpdate(params);
|
|
70
79
|
this.updated += 1;
|
|
@@ -25,14 +25,7 @@ const isFeatureUnavailableError = (err) => {
|
|
|
25
25
|
// Older Management API version where the endpoint is not available.
|
|
26
26
|
return true;
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
err.originalError &&
|
|
30
|
-
err.originalError.response &&
|
|
31
|
-
err.originalError.response.body &&
|
|
32
|
-
err.originalError.response.body.errorCode === 'voice_mfa_not_allowed') {
|
|
33
|
-
// Recent Management API version, but with feature explicitly disabled.
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
28
|
+
// 403s (feature explicitly disabled) are handled by isForbiddenFeatureError.
|
|
36
29
|
return false;
|
|
37
30
|
};
|
|
38
31
|
class GuardianPhoneMessageTypesHandler extends default_1.default {
|
|
@@ -75,7 +68,16 @@ class GuardianPhoneMessageTypesHandler extends default_1.default {
|
|
|
75
68
|
}
|
|
76
69
|
}
|
|
77
70
|
const data = guardianPhoneFactorMessageTypes;
|
|
78
|
-
|
|
71
|
+
try {
|
|
72
|
+
await this.client.guardian.factors.phone.setMessageTypes(data);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
if (isFeatureUnavailableError(err) || (0, utils_1.isForbiddenFeatureError)(err, this.type)) {
|
|
76
|
+
// Feature is deprecated/disabled on this tenant; warn and skip instead of failing the import.
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
79
81
|
this.updated += 1;
|
|
80
82
|
this.didUpdate(guardianPhoneFactorMessageTypes);
|
|
81
83
|
}
|
|
@@ -22,14 +22,7 @@ const isFeatureUnavailableError = (err) => {
|
|
|
22
22
|
// Older Management API version where the endpoint is not available.
|
|
23
23
|
return true;
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
err.originalError &&
|
|
27
|
-
err.originalError.response &&
|
|
28
|
-
err.originalError.response.body &&
|
|
29
|
-
err.originalError.response.body.errorCode === 'hooks_not_allowed') {
|
|
30
|
-
// Recent Management API version, but with feature explicitly disabled.
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
25
|
+
// 403s (feature explicitly disabled) are handled by isForbiddenFeatureError.
|
|
33
26
|
return false;
|
|
34
27
|
};
|
|
35
28
|
class GuardianPhoneSelectedProviderHandler extends default_1.default {
|
|
@@ -72,7 +65,16 @@ class GuardianPhoneSelectedProviderHandler extends default_1.default {
|
|
|
72
65
|
}
|
|
73
66
|
}
|
|
74
67
|
const data = guardianPhoneFactorSelectedProvider;
|
|
75
|
-
|
|
68
|
+
try {
|
|
69
|
+
await this.client.guardian.factors.phone.setProvider(data);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
if (isFeatureUnavailableError(err) || (0, utils_1.isForbiddenFeatureError)(err, this.type)) {
|
|
73
|
+
// Feature is deprecated/disabled on this tenant; warn and skip instead of failing the import.
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
76
78
|
this.updated += 1;
|
|
77
79
|
this.didUpdate(guardianPhoneFactorSelectedProvider);
|
|
78
80
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import DefaultHandler from './default';
|
|
2
|
+
import { Asset, Assets } from '../../../types';
|
|
3
|
+
export declare const schema: {
|
|
4
|
+
type: string;
|
|
5
|
+
properties: {
|
|
6
|
+
otp_length: {
|
|
7
|
+
type: string;
|
|
8
|
+
description: string;
|
|
9
|
+
};
|
|
10
|
+
otp_expiration_time: {
|
|
11
|
+
type: string;
|
|
12
|
+
description: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
additionalProperties: boolean;
|
|
16
|
+
};
|
|
17
|
+
export default class GuardianPhoneFactorSettingsHandler extends DefaultHandler {
|
|
18
|
+
existing: Asset;
|
|
19
|
+
constructor(options: any);
|
|
20
|
+
getType(): Promise<Asset | null>;
|
|
21
|
+
processChanges(assets: Assets): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.schema = void 0;
|
|
7
|
+
const default_1 = __importDefault(require("./default"));
|
|
8
|
+
const utils_1 = require("../../utils");
|
|
9
|
+
exports.schema = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
otp_length: {
|
|
13
|
+
type: 'number',
|
|
14
|
+
description: 'The length of the OTP code.',
|
|
15
|
+
},
|
|
16
|
+
otp_expiration_time: {
|
|
17
|
+
type: 'number',
|
|
18
|
+
description: 'The OTP expiration time in seconds.',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
additionalProperties: false,
|
|
22
|
+
};
|
|
23
|
+
class GuardianPhoneFactorSettingsHandler extends default_1.default {
|
|
24
|
+
constructor(options) {
|
|
25
|
+
super({
|
|
26
|
+
...options,
|
|
27
|
+
type: 'guardianPhoneFactorSettings',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async getType() {
|
|
31
|
+
if (this.existing)
|
|
32
|
+
return this.existing;
|
|
33
|
+
try {
|
|
34
|
+
const data = await this.client.guardian.factors.phone.get();
|
|
35
|
+
this.existing = data;
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if ((0, utils_1.isFeatureUnavailableError)(err)) {
|
|
39
|
+
// Gracefully skip processing this configuration value.
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if ((0, utils_1.isForbiddenFeatureError)(err, this.type)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
return this.existing;
|
|
48
|
+
}
|
|
49
|
+
async processChanges(assets) {
|
|
50
|
+
// No API to delete or create guardianPhoneFactorSettings, we can only update.
|
|
51
|
+
const { guardianPhoneFactorSettings } = assets;
|
|
52
|
+
// Do nothing if not set
|
|
53
|
+
if (!guardianPhoneFactorSettings) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if ((0, utils_1.isDryRun)(this.config)) {
|
|
57
|
+
const { del, update, create } = await this.calcChanges(assets);
|
|
58
|
+
if (create.length === 0 && update.length === 0 && del.length === 0) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
await this.client.guardian.factors.phone.set(guardianPhoneFactorSettings);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
if ((0, utils_1.isFeatureUnavailableError)(err) || (0, utils_1.isForbiddenFeatureError)(err, this.type)) {
|
|
67
|
+
// Feature is unavailable (404) or disabled (403) on this tenant; skip instead of failing
|
|
68
|
+
// the import. The 403 path logs a warning via isForbiddenFeatureError; the 404 path is silent.
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
this.updated += 1;
|
|
74
|
+
this.didUpdate(guardianPhoneFactorSettings);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.default = GuardianPhoneFactorSettingsHandler;
|