auth0-deploy-cli 8.31.0 → 8.33.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +46 -1
  2. package/lib/args.d.ts +4 -0
  3. package/lib/args.js +18 -0
  4. package/lib/commands/import.js +40 -3
  5. package/lib/context/directory/handlers/clients.js +14 -0
  6. package/lib/context/directory/handlers/connections.js +1 -0
  7. package/lib/context/directory/handlers/guardianFactors.js +8 -6
  8. package/lib/context/directory/handlers/tenant.d.ts +1 -6
  9. package/lib/context/directory/handlers/tenant.js +0 -1
  10. package/lib/context/index.js +5 -0
  11. package/lib/context/yaml/handlers/clients.js +12 -0
  12. package/lib/context/yaml/handlers/connections.js +1 -17
  13. package/lib/context/yaml/handlers/guardianFactors.js +3 -1
  14. package/lib/context/yaml/handlers/tenant.js +1 -1
  15. package/lib/sessionDurationsToMinutes.d.ts +9 -7
  16. package/lib/sessionDurationsToMinutes.js +11 -10
  17. package/lib/tools/auth0/handlers/actions.d.ts +1 -0
  18. package/lib/tools/auth0/handlers/actions.js +68 -29
  19. package/lib/tools/auth0/handlers/attackProtection.js +15 -0
  20. package/lib/tools/auth0/handlers/branding.js +7 -0
  21. package/lib/tools/auth0/handlers/clientGrants.d.ts +5 -0
  22. package/lib/tools/auth0/handlers/clientGrants.js +31 -9
  23. package/lib/tools/auth0/handlers/clients.d.ts +62 -2
  24. package/lib/tools/auth0/handlers/clients.js +187 -22
  25. package/lib/tools/auth0/handlers/connections.d.ts +18 -0
  26. package/lib/tools/auth0/handlers/connections.js +86 -5
  27. package/lib/tools/auth0/handlers/databases.d.ts +91 -0
  28. package/lib/tools/auth0/handlers/databases.js +162 -2
  29. package/lib/tools/auth0/handlers/default.d.ts +4 -0
  30. package/lib/tools/auth0/handlers/default.js +82 -4
  31. package/lib/tools/auth0/handlers/emailProvider.js +8 -0
  32. package/lib/tools/auth0/handlers/emailTemplates.js +7 -0
  33. package/lib/tools/auth0/handlers/flowVaultConnections.js +6 -1
  34. package/lib/tools/auth0/handlers/flows.js +13 -4
  35. package/lib/tools/auth0/handlers/forms.js +10 -1
  36. package/lib/tools/auth0/handlers/guardianFactors.js +7 -1
  37. package/lib/tools/auth0/handlers/guardianPhoneFactorMessageTypes.js +8 -1
  38. package/lib/tools/auth0/handlers/guardianPhoneFactorSelectedProvider.js +8 -1
  39. package/lib/tools/auth0/handlers/guardianPolicies.js +7 -0
  40. package/lib/tools/auth0/handlers/hooks.d.ts +1 -0
  41. package/lib/tools/auth0/handlers/hooks.js +10 -0
  42. package/lib/tools/auth0/handlers/organizations.d.ts +11 -1
  43. package/lib/tools/auth0/handlers/organizations.js +48 -52
  44. package/lib/tools/auth0/handlers/pages.js +7 -0
  45. package/lib/tools/auth0/handlers/phoneProvider.js +7 -0
  46. package/lib/tools/auth0/handlers/prompts.js +7 -0
  47. package/lib/tools/auth0/handlers/resourceServers.d.ts +1 -2
  48. package/lib/tools/auth0/handlers/resourceServers.js +9 -25
  49. package/lib/tools/auth0/handlers/roles.js +7 -0
  50. package/lib/tools/auth0/handlers/rules.d.ts +1 -0
  51. package/lib/tools/auth0/handlers/rules.js +9 -0
  52. package/lib/tools/auth0/handlers/rulesConfigs.d.ts +1 -0
  53. package/lib/tools/auth0/handlers/rulesConfigs.js +3 -0
  54. package/lib/tools/auth0/handlers/scimHandler.js +8 -1
  55. package/lib/tools/auth0/handlers/selfServiceProfiles.d.ts +1 -1
  56. package/lib/tools/auth0/handlers/selfServiceProfiles.js +10 -17
  57. package/lib/tools/auth0/handlers/tenant.d.ts +18 -1
  58. package/lib/tools/auth0/handlers/tenant.js +48 -4
  59. package/lib/tools/auth0/handlers/themes.js +11 -2
  60. package/lib/tools/auth0/handlers/triggers.js +6 -0
  61. package/lib/tools/auth0/handlers/userAttributeProfiles.js +16 -10
  62. package/lib/tools/auth0/index.d.ts +3 -0
  63. package/lib/tools/auth0/index.js +204 -0
  64. package/lib/tools/calculateChanges.d.ts +2 -2
  65. package/lib/tools/calculateChanges.js +21 -6
  66. package/lib/tools/calculateDryRunChanges.d.ts +71 -0
  67. package/lib/tools/calculateDryRunChanges.js +492 -0
  68. package/lib/tools/deploy.js +25 -1
  69. package/lib/tools/utils.d.ts +5 -0
  70. package/lib/tools/utils.js +64 -3
  71. package/lib/types.d.ts +16 -0
  72. package/lib/utils.d.ts +7 -0
  73. package/lib/utils.js +33 -1
  74. package/package.json +7 -4
package/CHANGELOG.md CHANGED
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [8.33.0] - 2026-05-05
11
+
12
+ ### Added
13
+
14
+ - Add support for Third Party Apps CORE EA Release 1 properties, including updated `clientGrants` matching and `clients` schema handling. [#1372]
15
+ - Add support for additional signing algorithms (`token_endpoint_auth_signing_alg`, `id_token_signed_response_algs`) in OIDC and Okta enterprise connections. [#1375]
16
+
17
+ ### Fixed
18
+
19
+ - Fix Deploy CLI hanging indefinitely when importing 3 or more actions that reference action modules, caused by a deadlock in the shared rate-limiting pool. [#1374]
20
+
21
+ ## [8.32.0] - 2026-04-23
22
+
23
+ ### Added
24
+
25
+ - Add support for CIMD client registration via `external_client_id`, allowing clients to be registered from an OAuth 2.0 Client ID Metadata Document. [#1369]
26
+ - Add `on_behalf_of_token_exchange` to supported `token_exchange.allow_any_profile_of_type` values for clients. [#1366]
27
+ - Add Flexible Password Policy support for database connections. [#1362]
28
+ - Add `My Org` client references support and expand organization connection sync. [#1367]
29
+ - Add dry-run mode (GA) with `--dry-run`, `--dry-run --interactive`, and `--dry-run --apply` flags. [#1336]
30
+
31
+ ### Fixed
32
+
33
+ - Fix silent process exit when deploying 3+ organizations with discovery domains. [#1354]
34
+ - Fix organization creation response handling and add ID validation. [#1365]
35
+ - Fix undefined `enabled_clients` handling in `getEnabledClients`. [#1364]
36
+ - Fix unresolved keyword placeholders being sent in API payloads before deploy. [#1360]
37
+
10
38
  ## [8.31.0] - 2026-04-10
11
39
 
12
40
  ### Added
@@ -1716,12 +1744,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1716
1744
  [#1321]: https://github.com/auth0/auth0-deploy-cli/issues/1321
1717
1745
  [#1322]: https://github.com/auth0/auth0-deploy-cli/issues/1322
1718
1746
  [#1328]: https://github.com/auth0/auth0-deploy-cli/issues/1328
1747
+ [#1330]: https://github.com/auth0/auth0-deploy-cli/issues/1330
1719
1748
  [#1333]: https://github.com/auth0/auth0-deploy-cli/issues/1333
1720
1749
  [#1334]: https://github.com/auth0/auth0-deploy-cli/issues/1334
1750
+ [#1335]: https://github.com/auth0/auth0-deploy-cli/issues/1335
1751
+ [#1336]: https://github.com/auth0/auth0-deploy-cli/issues/1336
1721
1752
  [#1340]: https://github.com/auth0/auth0-deploy-cli/issues/1340
1753
+ [#1341]: https://github.com/auth0/auth0-deploy-cli/issues/1341
1722
1754
  [#1342]: https://github.com/auth0/auth0-deploy-cli/issues/1342
1723
1755
  [#1343]: https://github.com/auth0/auth0-deploy-cli/issues/1343
1724
- [Unreleased]: https://github.com/auth0/auth0-deploy-cli/compare/v8.31.0...HEAD
1756
+ [#1354]: https://github.com/auth0/auth0-deploy-cli/issues/1354
1757
+ [#1360]: https://github.com/auth0/auth0-deploy-cli/issues/1360
1758
+ [#1362]: https://github.com/auth0/auth0-deploy-cli/issues/1362
1759
+ [#1364]: https://github.com/auth0/auth0-deploy-cli/issues/1364
1760
+ [#1365]: https://github.com/auth0/auth0-deploy-cli/issues/1365
1761
+ [#1366]: https://github.com/auth0/auth0-deploy-cli/issues/1366
1762
+ [#1367]: https://github.com/auth0/auth0-deploy-cli/issues/1367
1763
+ [#1369]: https://github.com/auth0/auth0-deploy-cli/issues/1369
1764
+ [#1372]: https://github.com/auth0/auth0-deploy-cli/issues/1372
1765
+ [#1374]: https://github.com/auth0/auth0-deploy-cli/issues/1374
1766
+ [#1375]: https://github.com/auth0/auth0-deploy-cli/issues/1375
1767
+ [Unreleased]: https://github.com/auth0/auth0-deploy-cli/compare/v8.33.0...HEAD
1768
+ [8.33.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.32.0...v8.33.0
1769
+ [8.32.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.31.0...v8.32.0
1725
1770
  [8.31.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.30.0...v8.31.0
1726
1771
  [8.30.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.29.3...v8.30.0
1727
1772
  [8.29.3]: https://github.com/auth0/auth0-deploy-cli/compare/v8.29.2...v8.29.3
package/lib/args.d.ts CHANGED
@@ -9,8 +9,12 @@ type SharedParams = {
9
9
  config?: Partial<Config>;
10
10
  experimental_ea?: boolean;
11
11
  };
12
+ export type DryRunMode = 'preview';
12
13
  type ImportSpecificParams = {
13
14
  input_file: string;
15
+ dry_run?: boolean | '' | DryRunMode;
16
+ interactive?: boolean;
17
+ apply?: boolean;
14
18
  };
15
19
  type ExportSpecificParams = {
16
20
  format: 'yaml' | 'directory';
package/lib/args.js CHANGED
@@ -47,6 +47,21 @@ function getParams() {
47
47
  describe: 'The client secret, this allows you to encrypt the secret in your build configuration instead of storing it in a config file',
48
48
  type: 'string',
49
49
  },
50
+ dry_run: {
51
+ alias: 'dry-run',
52
+ describe: 'Dry-run mode. Show plan without applying changes. Use --interactive for the menu or --apply to show the plan and apply without prompting.',
53
+ type: 'string',
54
+ },
55
+ interactive: {
56
+ describe: 'Use with --dry-run to enable the interactive menu (apply / export to file / exit). Not available in non-TTY environments.',
57
+ type: 'boolean',
58
+ default: false,
59
+ },
60
+ apply: {
61
+ describe: 'Use with --dry-run to show the plan and then apply without prompting.',
62
+ type: 'boolean',
63
+ default: false,
64
+ },
50
65
  })
51
66
  .command(['export', 'dump'], 'Export Auth0 Tenant Configuration', {
52
67
  output_folder: {
@@ -93,6 +108,9 @@ function getParams() {
93
108
  .example('$0 export -c config.json -f directory -o path/to/export', 'Dump Auth0 config to folder in directory format')
94
109
  .example('$0 import -c config.json -i tenant.yaml', 'Deploy Auth0 via YAML')
95
110
  .example('$0 import -c config.json -i path/to/files', 'Deploy Auth0 via Path')
111
+ .example('$0 import -c config.json -i tenant.yaml --dry-run', 'Preview changes and exit without applying changes')
112
+ .example('$0 import -c config.json -i tenant.yaml --dry-run --apply', 'Show plan and apply without prompting (CI deployment)')
113
+ .example('$0 import -c config.json -i tenant.yaml --dry-run --interactive', 'Show plan with interactive menu (apply / export / exit)')
96
114
  .example('$0 dump -c config.json -f yaml -o path/to/export', 'Dump Auth0 config to folder in YAML format')
97
115
  .example('$0 dump -c config.json -f directory -o path/to/export', 'Dump Auth0 config to folder in directory format')
98
116
  .example('$0 deploy -c config.json -i tenant.yaml', 'Deploy Auth0 via YAML')
@@ -9,8 +9,29 @@ const configFactory_1 = require("../configFactory");
9
9
  const tools_1 = require("../tools");
10
10
  const logger_1 = __importDefault(require("../logger"));
11
11
  const context_1 = require("../context");
12
+ const utils_1 = require("../utils");
12
13
  async function importCMD(params) {
13
- const { input_file: inputFile, base_path: basePath, config_file: configFile, config: configObj, env: shouldInheritEnv = false, secret: clientSecret, experimental_ea: experimentalEA, } = params;
14
+ const { input_file: inputFile, base_path: basePath, config_file: configFile, config: configObj, env: shouldInheritEnv = false, secret: clientSecret, experimental_ea: experimentalEA, dry_run: dryRun, interactive = false, apply = false, } = params;
15
+ const normalizedDryRun = dryRun === true || dryRun === '' ? 'preview' : dryRun;
16
+ let effectiveDryRun;
17
+ if (!normalizedDryRun) {
18
+ effectiveDryRun = undefined;
19
+ }
20
+ else if (normalizedDryRun !== 'preview') {
21
+ throw new Error(`Invalid value for --dry-run: ${normalizedDryRun}. Use --dry-run or --dry-run=preview.`);
22
+ }
23
+ else {
24
+ effectiveDryRun = normalizedDryRun;
25
+ }
26
+ if (apply && !effectiveDryRun) {
27
+ throw new Error('--apply must be used with --dry-run.');
28
+ }
29
+ if (interactive && !effectiveDryRun) {
30
+ throw new Error('--interactive must be used with --dry-run.');
31
+ }
32
+ if (interactive && apply) {
33
+ throw new Error('--interactive and --apply cannot be used together.');
34
+ }
14
35
  if (shouldInheritEnv) {
15
36
  nconf_1.default.env().use('memory');
16
37
  const mappings = nconf_1.default.get('AUTH0_KEYWORD_REPLACE_MAPPINGS') || {};
@@ -36,13 +57,29 @@ async function importCMD(params) {
36
57
  // nconf.overrides() sometimes doesn't work, so we need to set it manually to ensure it's set
37
58
  nconf_1.default.set('AUTH0_EXPERIMENTAL_EA', experimentalEA);
38
59
  }
60
+ // Override AUTH0_DRY_RUN if dry_run passed in command line
61
+ if (effectiveDryRun) {
62
+ overrides.AUTH0_DRY_RUN = effectiveDryRun;
63
+ nconf_1.default.set('AUTH0_DRY_RUN', effectiveDryRun);
64
+ }
65
+ if (interactive) {
66
+ overrides.AUTH0_DRY_RUN_INTERACTIVE = interactive;
67
+ nconf_1.default.set('AUTH0_DRY_RUN_INTERACTIVE', interactive);
68
+ }
69
+ const existingDryRunApply = nconf_1.default.get('AUTH0_DRY_RUN_APPLY');
70
+ if (apply || (0, utils_1.isTruthy)(existingDryRunApply)) {
71
+ overrides.AUTH0_DRY_RUN_APPLY = true;
72
+ nconf_1.default.set('AUTH0_DRY_RUN_APPLY', true);
73
+ }
39
74
  nconf_1.default.overrides(overrides);
40
75
  // Setup context and load
41
76
  const context = await (0, context_1.setupContext)(nconf_1.default.get(), 'import');
42
77
  await context.loadAssetsFromLocal();
43
78
  const config = (0, configFactory_1.configFactory)();
44
79
  config.setProvider((key) => nconf_1.default.get(key));
45
- //@ts-ignore because context and assets still need to be typed TODO: type assets and type context
80
+ // @ts-ignore because context and assets still need to be typed TODO: type assets and type context
46
81
  await (0, tools_1.deploy)(context.assets, context.mgmtClient, config);
47
- logger_1.default.info('Import Successful');
82
+ if (!effectiveDryRun) {
83
+ logger_1.default.info('Import Successful');
84
+ }
48
85
  }
@@ -77,6 +77,20 @@ async function dump(context) {
77
77
  client.express_configuration.okta_oin_client_id = o?.name || oktaOinClientId;
78
78
  }
79
79
  }
80
+ if (client.my_organization_configuration) {
81
+ const myOrganizationUserAttributeProfileId = client.my_organization_configuration.user_attribute_profile_id;
82
+ if (myOrganizationUserAttributeProfileId) {
83
+ const p = userAttributeProfiles?.find((uap) => uap.id === myOrganizationUserAttributeProfileId);
84
+ client.my_organization_configuration.user_attribute_profile_id =
85
+ p?.name || myOrganizationUserAttributeProfileId;
86
+ }
87
+ const myOrganizationConnectionProfileId = client.my_organization_configuration.connection_profile_id;
88
+ if (myOrganizationConnectionProfileId) {
89
+ const c = connectionProfiles?.find((cp) => cp.id === myOrganizationConnectionProfileId);
90
+ client.my_organization_configuration.connection_profile_id =
91
+ c?.name || myOrganizationConnectionProfileId;
92
+ }
93
+ }
80
94
  if (client.app_type === 'express_configuration') {
81
95
  // only keep relevant fields for express configuration
82
96
  client = {
@@ -55,6 +55,7 @@ async function dump(context) {
55
55
  connections.forEach((connection) => {
56
56
  let dumpedConnection = {
57
57
  ...connection,
58
+ ...(0, utils_1.getFormattedOptions)(connection, clientsOrig),
58
59
  ...(connection.enabled_clients && {
59
60
  enabled_clients: (0, utils_1.mapClientID2NameSorted)(connection.enabled_clients, clientsOrig || []),
60
61
  }),
@@ -6,18 +6,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const path_1 = __importDefault(require("path"));
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
8
  const tools_1 = require("../../../tools");
9
- const utils_1 = require("../../../utils");
9
+ const utils_1 = require("../../../tools/utils");
10
+ const utils_2 = require("../../../utils");
10
11
  function parse(context) {
11
12
  const factorsFolder = path_1.default.join(context.filePath, tools_1.constants.GUARDIAN_DIRECTORY, tools_1.constants.GUARDIAN_FACTORS_DIRECTORY);
12
- if (!(0, utils_1.existsMustBeDir)(factorsFolder))
13
+ if (!(0, utils_2.existsMustBeDir)(factorsFolder))
13
14
  return { guardianFactors: null }; // Skip
14
- const foundFiles = (0, utils_1.getFiles)(factorsFolder, ['.json']);
15
- const guardianFactors = foundFiles
16
- .map((f) => (0, utils_1.loadJSON)(f, {
15
+ const foundFiles = (0, utils_2.getFiles)(factorsFolder, ['.json']);
16
+ let guardianFactors = foundFiles
17
+ .map((f) => (0, utils_2.loadJSON)(f, {
17
18
  mappings: context.mappings,
18
19
  disableKeywordReplacement: context.disableKeywordReplacement,
19
20
  }))
20
21
  .filter((p) => Object.keys(p).length > 0); // Filter out empty guardianFactors
22
+ guardianFactors = (0, utils_1.sortGuardianFactors)(guardianFactors);
21
23
  return {
22
24
  guardianFactors,
23
25
  };
@@ -30,7 +32,7 @@ async function dump(context) {
30
32
  fs_extra_1.default.ensureDirSync(factorsFolder);
31
33
  guardianFactors.forEach((factor) => {
32
34
  const factorFile = path_1.default.join(factorsFolder, `${factor.name}.json`);
33
- (0, utils_1.dumpJSON)(factorFile, factor);
35
+ (0, utils_2.dumpJSON)(factorFile, factor);
34
36
  });
35
37
  }
36
38
  const guardianFactorsHandler = {
@@ -1,10 +1,5 @@
1
1
  import { DirectoryHandler } from '.';
2
2
  import { Asset, ParsedAsset } from '../../../types';
3
- type ParsedTenant = ParsedAsset<'tenant', {
4
- session_lifetime: number;
5
- idle_session_lifetime: number;
6
- } & {
7
- [key: string]: Asset;
8
- }>;
3
+ type ParsedTenant = ParsedAsset<'tenant', Asset>;
9
4
  declare const tenantHandler: DirectoryHandler<ParsedTenant>;
10
5
  export default tenantHandler;
@@ -41,7 +41,6 @@ async function dump(context) {
41
41
  (0, utils_1.clearTenantFlags)(tenant);
42
42
  const tenantFile = path_1.default.join(context.filePath, 'tenant.json');
43
43
  (0, utils_1.dumpJSON)(tenantFile, tenant);
44
- return;
45
44
  }
46
45
  const tenantHandler = {
47
46
  parse,
@@ -158,6 +158,11 @@ const setupContext = async (config, command) => {
158
158
  token: accessToken,
159
159
  headers: {
160
160
  'User-agent': `deploy-cli/${packageVersion} (node.js/${process.version.replace('v', '')})`,
161
+ ...((0, utils_1.isTruthy)(config.AUTH0_DRY_RUN)
162
+ ? {
163
+ 'X-Auth0-Deploy-Mode': 'dry-run',
164
+ }
165
+ : {}),
161
166
  },
162
167
  maxRetries: config.AUTH0_API_MAX_RETRIES || 10,
163
168
  });
@@ -58,6 +58,18 @@ async function dump(context) {
58
58
  const o = clients?.find((uap) => uap.client_id === oktaOinClientId);
59
59
  client.express_configuration.okta_oin_client_id = o?.name || oktaOinClientId;
60
60
  }
61
+ const myOrganizationUserAttributeProfileId = client?.my_organization_configuration?.user_attribute_profile_id;
62
+ if (client.my_organization_configuration && myOrganizationUserAttributeProfileId) {
63
+ const p = userAttributeProfiles?.find((uap) => uap.id === myOrganizationUserAttributeProfileId);
64
+ client.my_organization_configuration.user_attribute_profile_id =
65
+ p?.name || myOrganizationUserAttributeProfileId;
66
+ }
67
+ const myOrganizationConnectionProfileId = client?.my_organization_configuration?.connection_profile_id;
68
+ if (client.my_organization_configuration && myOrganizationConnectionProfileId) {
69
+ const c = connectionProfiles?.find((cp) => cp.id === myOrganizationConnectionProfileId);
70
+ client.my_organization_configuration.connection_profile_id =
71
+ c?.name || myOrganizationConnectionProfileId;
72
+ }
61
73
  return client;
62
74
  });
63
75
  return {
@@ -33,22 +33,6 @@ async function parse(context) {
33
33
  ],
34
34
  };
35
35
  }
36
- const getFormattedOptions = (connection, clients) => {
37
- try {
38
- return {
39
- options: {
40
- ...connection.options,
41
- idpinitiated: {
42
- ...connection.options.idpinitiated,
43
- client_id: (0, utils_1.convertClientIdToName)(connection.options.idpinitiated.client_id, clients),
44
- },
45
- },
46
- };
47
- }
48
- catch (e) {
49
- return {};
50
- }
51
- };
52
36
  async function dump(context) {
53
37
  let { connections } = context.assets;
54
38
  const { clients } = context.assets;
@@ -63,7 +47,7 @@ async function dump(context) {
63
47
  connections: connections.map((connection) => {
64
48
  let dumpedConnection = {
65
49
  ...connection,
66
- ...getFormattedOptions(connection, clients),
50
+ ...(0, utils_1.getFormattedOptions)(connection, clients),
67
51
  ...(connection.enabled_clients && {
68
52
  enabled_clients: (0, utils_1.mapClientID2NameSorted)(connection.enabled_clients, clients || []),
69
53
  }),
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("../../../tools/utils");
3
4
  async function parseAndDump(context) {
4
- const { guardianFactors } = context.assets;
5
+ let { guardianFactors } = context.assets;
5
6
  if (!guardianFactors)
6
7
  return { guardianFactors: null };
8
+ guardianFactors = (0, utils_1.sortGuardianFactors)(guardianFactors);
7
9
  return {
8
10
  guardianFactors,
9
11
  };
@@ -22,7 +22,7 @@ async function parse(context) {
22
22
  };
23
23
  }
24
24
  async function dump(context) {
25
- const tenant = context.assets.tenant;
25
+ const { tenant } = context.assets;
26
26
  if (!tenant)
27
27
  return { tenant: null };
28
28
  (0, utils_1.clearTenantFlags)(tenant);
@@ -1,11 +1,13 @@
1
- export declare const sessionDurationsToMinutes: ({ session_lifetime, idle_session_lifetime, idle_ephemeral_session_lifetime, ephemeral_session_lifetime, }: {
2
- session_lifetime?: number;
3
- idle_session_lifetime?: number;
4
- idle_ephemeral_session_lifetime?: number;
5
- ephemeral_session_lifetime?: number;
6
- }) => {
1
+ type SessionDurations = {
7
2
  session_lifetime_in_minutes?: number;
8
3
  idle_session_lifetime_in_minutes?: number;
9
- idle_ephemeral_session_lifetime_in_minutes?: number;
10
4
  ephemeral_session_lifetime_in_minutes?: number;
5
+ idle_ephemeral_session_lifetime_in_minutes?: number;
11
6
  };
7
+ export declare function sessionDurationsToMinutes({ session_lifetime, idle_session_lifetime, ephemeral_session_lifetime, idle_ephemeral_session_lifetime, }?: {
8
+ session_lifetime?: number;
9
+ idle_session_lifetime?: number;
10
+ ephemeral_session_lifetime?: number;
11
+ idle_ephemeral_session_lifetime?: number;
12
+ }): SessionDurations;
13
+ export default sessionDurationsToMinutes;
@@ -1,19 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sessionDurationsToMinutes = void 0;
3
+ exports.sessionDurationsToMinutes = sessionDurationsToMinutes;
4
+ /* eslint-disable camelcase */
4
5
  function hoursToMinutes(hours) {
5
- return Math.round(hours * 60);
6
+ return parseFloat(Math.round(hours * 60).toFixed(4));
6
7
  }
7
- const sessionDurationsToMinutes = ({ session_lifetime, idle_session_lifetime, idle_ephemeral_session_lifetime, ephemeral_session_lifetime, }) => {
8
+ function sessionDurationsToMinutes({ session_lifetime, idle_session_lifetime, ephemeral_session_lifetime, idle_ephemeral_session_lifetime, } = {}) {
8
9
  const sessionDurations = {};
9
- if (!!session_lifetime)
10
+ if (session_lifetime !== undefined)
10
11
  sessionDurations.session_lifetime_in_minutes = hoursToMinutes(session_lifetime);
11
- if (!!idle_session_lifetime)
12
+ if (idle_session_lifetime !== undefined)
12
13
  sessionDurations.idle_session_lifetime_in_minutes = hoursToMinutes(idle_session_lifetime);
13
- if (!!idle_ephemeral_session_lifetime)
14
- sessionDurations.idle_ephemeral_session_lifetime_in_minutes = hoursToMinutes(idle_ephemeral_session_lifetime);
15
- if (!!ephemeral_session_lifetime)
14
+ if (ephemeral_session_lifetime !== undefined)
16
15
  sessionDurations.ephemeral_session_lifetime_in_minutes = hoursToMinutes(ephemeral_session_lifetime);
16
+ if (idle_ephemeral_session_lifetime !== undefined)
17
+ sessionDurations.idle_ephemeral_session_lifetime_in_minutes = hoursToMinutes(idle_ephemeral_session_lifetime);
17
18
  return sessionDurations;
18
- };
19
- exports.sessionDurationsToMinutes = sessionDurationsToMinutes;
19
+ }
20
+ exports.default = sessionDurationsToMinutes;
@@ -118,6 +118,7 @@ export default class ActionHandler extends DefaultAPIHandler {
118
118
  actionChanges(action: any, found: any): Promise<Asset>;
119
119
  getType(): Promise<Asset[] | null>;
120
120
  calcChanges(assets: Assets): Promise<CalculatedChanges>;
121
+ dryRunChanges(assets: Assets): Promise<CalculatedChanges>;
121
122
  enrichActionWithModuleIds(action: Action, modules: ActionModule[]): Promise<Action>;
122
123
  processChanges(assets: Assets): Promise<void>;
123
124
  }
@@ -280,41 +280,80 @@ class ActionHandler extends default_1.default {
280
280
  }
281
281
  return super.calcChanges({ ...assets, actions });
282
282
  }
283
+ async dryRunChanges(assets) {
284
+ let { actions, actionModules } = assets;
285
+ if (!actions) {
286
+ return {
287
+ del: [],
288
+ create: [],
289
+ update: [],
290
+ conflicts: [],
291
+ };
292
+ }
293
+ let modules = null;
294
+ if (actionModules && actionModules.length > 0) {
295
+ modules = actionModules;
296
+ }
297
+ else {
298
+ try {
299
+ modules = await (0, client_1.paginate)(this.client.actions.modules.list, {
300
+ paginate: true,
301
+ });
302
+ }
303
+ catch {
304
+ logger_1.default.debug('Skipping actions modules enrichment because action modules could not be retrieved.');
305
+ modules = null;
306
+ }
307
+ }
308
+ if (modules != null) {
309
+ const processedActions = await this.client.pool
310
+ .addEachTask({
311
+ data: actions || [],
312
+ generator: (action) => this.enrichActionWithModuleIds(action, modules),
313
+ })
314
+ .promise();
315
+ actions = processedActions;
316
+ }
317
+ return super.dryRunChanges({ ...assets, actions });
318
+ }
283
319
  async enrichActionWithModuleIds(action, modules) {
284
320
  if (!action.modules || action.modules.length === 0) {
285
321
  return action;
286
322
  }
287
- const updatedModules = await this.client.pool
288
- .addEachTask({
289
- data: action.modules,
290
- generator: async (module) => {
291
- const foundModule = modules.find((m) => m.name === module.module_name);
292
- if (foundModule && foundModule.id) {
293
- // paginate to get all versions of the module
294
- const allModuleVersions = [];
295
- let moduleVersions = await this.client.actions.modules.versions.list(foundModule.id);
296
- // Process first page
323
+ // Process modules sequentially to avoid a pool deadlock.
324
+ // This function is called as a task inside the shared pool (via addEachTask in calcChanges).
325
+ // If we submitted further addEachTask calls on the same pool here, all concurrency slots
326
+ // could be held by outer action tasks waiting for inner module tasks that can never start
327
+ // (because no slots are free), causing a hang with 3+ actions that have modules.
328
+ const updatedModules = [];
329
+ for (const module of action.modules) {
330
+ const foundModule = modules.find((m) => m.name === module.module_name);
331
+ if (foundModule && foundModule.id) {
332
+ // paginate to get all versions of the module
333
+ const allModuleVersions = [];
334
+ let moduleVersions = await this.client.actions.modules.versions.list(foundModule.id);
335
+ // Process first page
336
+ allModuleVersions.push(...moduleVersions.data);
337
+ // Fetch remaining pages
338
+ while (moduleVersions.hasNextPage()) {
339
+ moduleVersions = await moduleVersions.getNextPage();
297
340
  allModuleVersions.push(...moduleVersions.data);
298
- // Fetch remaining pages
299
- while (moduleVersions.hasNextPage()) {
300
- moduleVersions = await moduleVersions.getNextPage();
301
- allModuleVersions.push(...moduleVersions.data);
302
- }
303
- const moduleVersionId = allModuleVersions?.find((v) => v.version_number === module.module_version_number)?.id;
304
- if (!moduleVersionId) {
305
- throw new Error(`Could not find action module version id for module '${module.module_name}' version '${module.module_version_number}'`);
306
- }
307
- return {
308
- module_name: module.module_name,
309
- module_id: foundModule.id,
310
- module_version_number: module.module_version_number,
311
- module_version_id: moduleVersionId,
312
- };
313
341
  }
314
- return module;
315
- },
316
- })
317
- .promise();
342
+ const moduleVersionId = allModuleVersions?.find((v) => v.version_number === module.module_version_number)?.id;
343
+ if (!moduleVersionId) {
344
+ throw new Error(`Could not find action module version id for module '${module.module_name}' version '${module.module_version_number}'`);
345
+ }
346
+ updatedModules.push({
347
+ module_name: module.module_name,
348
+ module_id: foundModule.id,
349
+ module_version_number: module.module_version_number,
350
+ module_version_id: moduleVersionId,
351
+ });
352
+ }
353
+ else {
354
+ updatedModules.push(module);
355
+ }
356
+ }
318
357
  return {
319
358
  ...action,
320
359
  modules: updatedModules,
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.schema = exports.CAPTCHA_PROVIDERS = void 0;
7
7
  const default_1 = __importDefault(require("./default"));
8
+ const utils_1 = require("../../utils");
8
9
  const logger_1 = __importDefault(require("../../../logger"));
9
10
  exports.CAPTCHA_PROVIDERS = [
10
11
  'arkose',
@@ -187,6 +188,14 @@ class AttackProtectionHandler extends default_1.default {
187
188
  super({
188
189
  ...config,
189
190
  type: 'attackProtection',
191
+ ignoreDryRunFields: [
192
+ 'captcha.arkose.secret',
193
+ 'captcha.friendly_captcha.secret',
194
+ 'captcha.hcaptcha.secret',
195
+ 'captcha.recaptcha_enterprise.api_key',
196
+ 'captcha.recaptcha_enterprise.project_id',
197
+ 'captcha.recaptcha_v2.secret',
198
+ ],
190
199
  });
191
200
  }
192
201
  objString(item) {
@@ -263,6 +272,12 @@ class AttackProtectionHandler extends default_1.default {
263
272
  if (!attackProtection || !Object.keys(attackProtection).length) {
264
273
  return;
265
274
  }
275
+ if ((0, utils_1.isDryRun)(this.config)) {
276
+ const { del, update, create } = await this.calcChanges(assets);
277
+ if (create.length === 0 && update.length === 0 && del.length === 0) {
278
+ return;
279
+ }
280
+ }
266
281
  const updates = [];
267
282
  if (attackProtection.botDetection && Object.keys(attackProtection.botDetection).length) {
268
283
  updates.push(this.client.attackProtection.botDetection.update(attackProtection.botDetection));
@@ -46,6 +46,7 @@ exports.schema = void 0;
46
46
  const default_1 = __importStar(require("./default"));
47
47
  const constants_1 = __importDefault(require("../../constants"));
48
48
  const logger_1 = __importDefault(require("../../../logger"));
49
+ const utils_1 = require("../../utils");
49
50
  exports.schema = {
50
51
  type: 'object',
51
52
  properties: {
@@ -117,6 +118,12 @@ class BrandingHandler extends default_1.default {
117
118
  async processChanges(assets) {
118
119
  if (!assets.branding)
119
120
  return;
121
+ if ((0, utils_1.isDryRun)(this.config)) {
122
+ const { del, update, create } = await this.calcChanges(assets);
123
+ if (del.length === 0 && update.length === 0 && create.length === 0) {
124
+ return;
125
+ }
126
+ }
120
127
  const { templates, ...brandingSettings } = assets.branding;
121
128
  if (brandingSettings.logo_url === '') {
122
129
  // Sometimes blank logo_url returned by API but is invalid on import. See: DXCDT-240
@@ -37,6 +37,11 @@ export declare const schema: {
37
37
  type: string;
38
38
  description: string;
39
39
  };
40
+ default_for: {
41
+ type: string;
42
+ enum: string[];
43
+ description: string;
44
+ };
40
45
  };
41
46
  required: string[];
42
47
  };