auth0-deploy-cli 8.30.0 → 8.32.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/.circleci/config.yml +17 -5
- package/AGENTS.md +21 -1
- package/CHANGELOG.md +48 -415
- package/CLAUDE.md +3 -0
- package/lib/args.d.ts +5 -0
- package/lib/args.js +23 -0
- package/lib/commands/export.js +5 -1
- package/lib/commands/import.js +40 -3
- package/lib/context/directory/handlers/actions.js +1 -2
- package/lib/context/directory/handlers/clients.js +14 -0
- package/lib/context/directory/handlers/connections.js +1 -0
- package/lib/context/directory/handlers/guardianFactors.js +8 -6
- package/lib/context/directory/handlers/tenant.d.ts +1 -6
- package/lib/context/directory/handlers/tenant.js +0 -1
- package/lib/context/directory/handlers/triggers.js +1 -3
- package/lib/context/index.js +5 -0
- package/lib/context/yaml/handlers/clients.js +12 -0
- package/lib/context/yaml/handlers/connections.js +1 -17
- package/lib/context/yaml/handlers/guardianFactors.js +3 -1
- package/lib/context/yaml/handlers/tenant.js +1 -1
- package/lib/context/yaml/index.js +2 -1
- package/lib/sessionDurationsToMinutes.d.ts +9 -7
- package/lib/sessionDurationsToMinutes.js +11 -10
- package/lib/tools/auth0/handlers/actions.d.ts +1 -0
- package/lib/tools/auth0/handlers/actions.js +36 -0
- package/lib/tools/auth0/handlers/attackProtection.js +15 -0
- package/lib/tools/auth0/handlers/branding.js +7 -0
- package/lib/tools/auth0/handlers/clientGrants.js +32 -4
- package/lib/tools/auth0/handlers/clients.d.ts +52 -2
- package/lib/tools/auth0/handlers/clients.js +175 -22
- package/lib/tools/auth0/handlers/connections.d.ts +1 -1
- package/lib/tools/auth0/handlers/connections.js +42 -6
- package/lib/tools/auth0/handlers/customDomains.d.ts +5 -0
- package/lib/tools/auth0/handlers/customDomains.js +26 -0
- package/lib/tools/auth0/handlers/databases.d.ts +91 -0
- package/lib/tools/auth0/handlers/databases.js +162 -2
- package/lib/tools/auth0/handlers/default.d.ts +4 -0
- package/lib/tools/auth0/handlers/default.js +82 -4
- package/lib/tools/auth0/handlers/emailProvider.js +8 -0
- package/lib/tools/auth0/handlers/emailTemplates.js +7 -0
- package/lib/tools/auth0/handlers/flowVaultConnections.js +6 -1
- package/lib/tools/auth0/handlers/flows.js +13 -4
- package/lib/tools/auth0/handlers/forms.js +10 -1
- package/lib/tools/auth0/handlers/guardianFactors.js +7 -1
- package/lib/tools/auth0/handlers/guardianPhoneFactorMessageTypes.js +8 -1
- package/lib/tools/auth0/handlers/guardianPhoneFactorSelectedProvider.js +8 -1
- package/lib/tools/auth0/handlers/guardianPolicies.js +7 -0
- package/lib/tools/auth0/handlers/hooks.d.ts +1 -0
- package/lib/tools/auth0/handlers/hooks.js +10 -0
- package/lib/tools/auth0/handlers/organizations.d.ts +11 -1
- package/lib/tools/auth0/handlers/organizations.js +48 -52
- package/lib/tools/auth0/handlers/pages.js +7 -0
- package/lib/tools/auth0/handlers/phoneProvider.js +7 -0
- package/lib/tools/auth0/handlers/prompts.js +7 -0
- package/lib/tools/auth0/handlers/resourceServers.d.ts +1 -2
- package/lib/tools/auth0/handlers/resourceServers.js +9 -25
- package/lib/tools/auth0/handlers/roles.js +7 -0
- package/lib/tools/auth0/handlers/rules.d.ts +1 -0
- package/lib/tools/auth0/handlers/rules.js +9 -0
- package/lib/tools/auth0/handlers/rulesConfigs.d.ts +1 -0
- package/lib/tools/auth0/handlers/rulesConfigs.js +3 -0
- package/lib/tools/auth0/handlers/scimHandler.js +8 -1
- package/lib/tools/auth0/handlers/selfServiceProfiles.d.ts +1 -1
- package/lib/tools/auth0/handlers/selfServiceProfiles.js +10 -17
- package/lib/tools/auth0/handlers/tenant.d.ts +13 -1
- package/lib/tools/auth0/handlers/tenant.js +48 -4
- package/lib/tools/auth0/handlers/themes.js +11 -2
- package/lib/tools/auth0/handlers/triggers.js +6 -0
- package/lib/tools/auth0/handlers/userAttributeProfiles.js +16 -10
- package/lib/tools/auth0/index.d.ts +3 -0
- package/lib/tools/auth0/index.js +204 -0
- package/lib/tools/calculateChanges.d.ts +2 -2
- package/lib/tools/calculateChanges.js +21 -6
- package/lib/tools/calculateDryRunChanges.d.ts +71 -0
- package/lib/tools/calculateDryRunChanges.js +492 -0
- package/lib/tools/deploy.js +25 -1
- package/lib/tools/utils.d.ts +5 -0
- package/lib/tools/utils.js +64 -3
- package/lib/types.d.ts +17 -0
- package/lib/utils.d.ts +7 -0
- package/lib/utils.js +46 -2
- package/package.json +10 -6
package/CLAUDE.md
ADDED
package/lib/args.d.ts
CHANGED
|
@@ -9,13 +9,18 @@ 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';
|
|
17
21
|
output_folder: string;
|
|
18
22
|
export_ids?: boolean;
|
|
23
|
+
export_ordered?: boolean;
|
|
19
24
|
};
|
|
20
25
|
export type ExportParams = ExportSpecificParams & SharedParams;
|
|
21
26
|
export type ImportParams = ImportSpecificParams & SharedParams;
|
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: {
|
|
@@ -83,11 +98,19 @@ function getParams() {
|
|
|
83
98
|
type: 'boolean',
|
|
84
99
|
default: false,
|
|
85
100
|
},
|
|
101
|
+
export_ordered: {
|
|
102
|
+
alias: 's',
|
|
103
|
+
describe: 'Order keys in exported JSON files for consistent diffs.',
|
|
104
|
+
type: 'boolean',
|
|
105
|
+
},
|
|
86
106
|
})
|
|
87
107
|
.example('$0 export -c config.json -f yaml -o path/to/export', 'Dump Auth0 config to folder in YAML format')
|
|
88
108
|
.example('$0 export -c config.json -f directory -o path/to/export', 'Dump Auth0 config to folder in directory format')
|
|
89
109
|
.example('$0 import -c config.json -i tenant.yaml', 'Deploy Auth0 via YAML')
|
|
90
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)')
|
|
91
114
|
.example('$0 dump -c config.json -f yaml -o path/to/export', 'Dump Auth0 config to folder in YAML format')
|
|
92
115
|
.example('$0 dump -c config.json -f directory -o path/to/export', 'Dump Auth0 config to folder in directory format')
|
|
93
116
|
.example('$0 deploy -c config.json -i tenant.yaml', 'Deploy Auth0 via YAML')
|
package/lib/commands/export.js
CHANGED
|
@@ -11,7 +11,7 @@ const logger_1 = __importDefault(require("../logger"));
|
|
|
11
11
|
const utils_1 = require("../utils");
|
|
12
12
|
const index_1 = require("../context/index");
|
|
13
13
|
async function exportCMD(params) {
|
|
14
|
-
const { output_folder: outputFolder, base_path: basePath, config_file: configFile, config: configObj, export_ids: exportIds, secret: clientSecret, env: shouldInheritEnv = false, experimental_ea: experimentalEA, } = params;
|
|
14
|
+
const { output_folder: outputFolder, base_path: basePath, config_file: configFile, config: configObj, export_ids: exportIds, export_ordered: exportOrdered, secret: clientSecret, env: shouldInheritEnv = false, experimental_ea: experimentalEA, } = params;
|
|
15
15
|
if (shouldInheritEnv) {
|
|
16
16
|
nconf_1.default.env().use('memory');
|
|
17
17
|
}
|
|
@@ -32,6 +32,10 @@ async function exportCMD(params) {
|
|
|
32
32
|
if (exportIds) {
|
|
33
33
|
overrides.AUTH0_EXPORT_IDENTIFIERS = exportIds;
|
|
34
34
|
}
|
|
35
|
+
// Allow passed in export_ordered to override the configured one
|
|
36
|
+
if (exportOrdered) {
|
|
37
|
+
overrides.AUTH0_EXPORT_ORDERED = exportOrdered;
|
|
38
|
+
}
|
|
35
39
|
// Overrides AUTH0_INCLUDE_EXPERIMENTAL_EA is experimental_ea passed in command line
|
|
36
40
|
if (experimentalEA) {
|
|
37
41
|
overrides.AUTH0_EXPERIMENTAL_EA = experimentalEA;
|
package/lib/commands/import.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
82
|
+
if (!effectiveDryRun) {
|
|
83
|
+
logger_1.default.info('Import Successful');
|
|
84
|
+
}
|
|
48
85
|
}
|
|
@@ -99,8 +99,7 @@ async function dump(context) {
|
|
|
99
99
|
// Dump template metadata
|
|
100
100
|
const name = (0, utils_1.sanitize)(action.name);
|
|
101
101
|
const actionFile = path_1.default.join(actionsFolder, `${name}.json`);
|
|
102
|
-
|
|
103
|
-
fs_extra_1.default.writeFileSync(actionFile, JSON.stringify(mapToAction(context.filePath, action, includeIdentifiers), null, 2));
|
|
102
|
+
(0, utils_1.dumpJSON)(actionFile, mapToAction(context.filePath, action, includeIdentifiers));
|
|
104
103
|
});
|
|
105
104
|
}
|
|
106
105
|
const actionsHandler = {
|
|
@@ -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,
|
|
13
|
+
if (!(0, utils_2.existsMustBeDir)(factorsFolder))
|
|
13
14
|
return { guardianFactors: null }; // Skip
|
|
14
|
-
const foundFiles = (0,
|
|
15
|
-
|
|
16
|
-
.map((f) => (0,
|
|
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,
|
|
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;
|
|
@@ -7,7 +7,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
9
|
const utils_1 = require("../../../utils");
|
|
10
|
-
const logger_1 = __importDefault(require("../../../logger"));
|
|
11
10
|
function parse(context) {
|
|
12
11
|
const triggersFolder = path_1.default.join(context.filePath, tools_1.constants.TRIGGERS_DIRECTORY);
|
|
13
12
|
if (!(0, utils_1.existsMustBeDir)(triggersFolder))
|
|
@@ -29,8 +28,7 @@ async function dump(context) {
|
|
|
29
28
|
const triggersFolder = path_1.default.join(context.filePath, tools_1.constants.TRIGGERS_DIRECTORY);
|
|
30
29
|
fs_extra_1.default.ensureDirSync(triggersFolder);
|
|
31
30
|
const triggerFile = path_1.default.join(triggersFolder, 'triggers.json');
|
|
32
|
-
|
|
33
|
-
fs_extra_1.default.writeFileSync(triggerFile, JSON.stringify(triggers, null, 2));
|
|
31
|
+
(0, utils_1.dumpJSON)(triggerFile, triggers);
|
|
34
32
|
}
|
|
35
33
|
const triggersHandler = {
|
|
36
34
|
parse,
|
package/lib/context/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
};
|
|
@@ -183,7 +183,8 @@ class YAMLContext {
|
|
|
183
183
|
cleaned = (0, utils_1.stripIdentifiers)(auth0, cleaned);
|
|
184
184
|
}
|
|
185
185
|
// Write YAML File
|
|
186
|
-
const
|
|
186
|
+
const exportOrdered = Boolean(this.config.AUTH0_EXPORT_ORDERED);
|
|
187
|
+
const raw = js_yaml_1.default.dump(cleaned, { sortKeys: exportOrdered });
|
|
187
188
|
logger_1.default.info(`Writing ${this.configFile}`);
|
|
188
189
|
fs_extra_1.default.writeFileSync(this.configFile, raw);
|
|
189
190
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
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 =
|
|
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
|
-
|
|
8
|
+
function sessionDurationsToMinutes({ session_lifetime, idle_session_lifetime, ephemeral_session_lifetime, idle_ephemeral_session_lifetime, } = {}) {
|
|
8
9
|
const sessionDurations = {};
|
|
9
|
-
if (
|
|
10
|
+
if (session_lifetime !== undefined)
|
|
10
11
|
sessionDurations.session_lifetime_in_minutes = hoursToMinutes(session_lifetime);
|
|
11
|
-
if (
|
|
12
|
+
if (idle_session_lifetime !== undefined)
|
|
12
13
|
sessionDurations.idle_session_lifetime_in_minutes = hoursToMinutes(idle_session_lifetime);
|
|
13
|
-
if (
|
|
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.
|
|
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,6 +280,42 @@ 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;
|
|
@@ -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
|
|
@@ -84,8 +84,11 @@ class ClientGrantsHandler extends default_1.default {
|
|
|
84
84
|
type: 'clientGrants',
|
|
85
85
|
id: 'id',
|
|
86
86
|
// @ts-ignore because not sure why two-dimensional array passed in
|
|
87
|
-
|
|
87
|
+
// Try ['client_id', 'audience', 'subject_type'] first; falls through to
|
|
88
|
+
// ['client_id', 'audience'] when subject_type is null (falsy).
|
|
89
|
+
identifiers: ['id', ['client_id', 'audience', 'subject_type'], ['client_id', 'audience']],
|
|
88
90
|
stripUpdateFields: ['audience', 'client_id', 'subject_type', 'is_system'],
|
|
91
|
+
ignoreDryRunFields: ['_clientName'],
|
|
89
92
|
});
|
|
90
93
|
}
|
|
91
94
|
objString(item) {
|
|
@@ -155,6 +158,31 @@ class ClientGrantsHandler extends default_1.default {
|
|
|
155
158
|
...assets,
|
|
156
159
|
clientGrants: formatted,
|
|
157
160
|
});
|
|
161
|
+
// subject_type is immutable (in stripUpdateFields). Grants matched via the
|
|
162
|
+
// ['client_id', 'audience'] fallback with a mismatched subject_type must become
|
|
163
|
+
// DELETE + CREATE, not UPDATE, so the tenant converges to the desired state.
|
|
164
|
+
const subjectTypeMismatches = update.filter((localGrant) => {
|
|
165
|
+
// Only flag when local explicitly specifies subject_type (backward compat).
|
|
166
|
+
if (localGrant.subject_type === undefined)
|
|
167
|
+
return false;
|
|
168
|
+
const remoteGrant = (this.existing || []).find((e) => e.id === localGrant.id);
|
|
169
|
+
return (remoteGrant && (remoteGrant.subject_type ?? null) !== (localGrant.subject_type ?? null));
|
|
170
|
+
});
|
|
171
|
+
const adjustedUpdate = update.filter((u) => !subjectTypeMismatches.includes(u));
|
|
172
|
+
const adjustedDel = [
|
|
173
|
+
...del,
|
|
174
|
+
...subjectTypeMismatches
|
|
175
|
+
.map((u) => (this.existing || []).find((e) => e.id === u.id))
|
|
176
|
+
.filter((e) => e !== undefined),
|
|
177
|
+
];
|
|
178
|
+
const adjustedCreate = [
|
|
179
|
+
...create,
|
|
180
|
+
...subjectTypeMismatches
|
|
181
|
+
.map((u) => formatted.find((f) => f.client_id === u.client_id &&
|
|
182
|
+
f.audience === u.audience &&
|
|
183
|
+
(f.subject_type ?? null) === (u.subject_type ?? null)))
|
|
184
|
+
.filter((g) => g !== undefined),
|
|
185
|
+
];
|
|
158
186
|
const filterGrants = (list) => {
|
|
159
187
|
let filtered = list;
|
|
160
188
|
// Filter out the current client (Auth0 Management API client)
|
|
@@ -174,11 +202,11 @@ class ClientGrantsHandler extends default_1.default {
|
|
|
174
202
|
};
|
|
175
203
|
const changes = {
|
|
176
204
|
// @ts-ignore because this expects `client_id` and that's not yet typed on Asset
|
|
177
|
-
del: filterGrants(
|
|
205
|
+
del: filterGrants(adjustedDel),
|
|
178
206
|
// @ts-ignore because this expects `client_id` and that's not yet typed on Asset
|
|
179
|
-
update: filterGrants(
|
|
207
|
+
update: filterGrants(adjustedUpdate),
|
|
180
208
|
// @ts-ignore because this expects `client_id` and that's not yet typed on Asset
|
|
181
|
-
create: filterGrants(
|
|
209
|
+
create: filterGrants(adjustedCreate),
|
|
182
210
|
// @ts-ignore because this expects `client_id` and that's not yet typed on Asset
|
|
183
211
|
conflicts: filterGrants(conflicts),
|
|
184
212
|
};
|