auth0-deploy-cli 8.34.0 → 8.36.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/CHANGELOG.md +36 -1
- package/lib/args.d.ts +1 -0
- package/lib/args.js +5 -0
- package/lib/commands/export.js +5 -1
- package/lib/context/defaults.d.ts +5 -4
- package/lib/context/defaults.js +15 -5
- package/lib/context/directory/handlers/actionModules.js +3 -0
- package/lib/context/directory/handlers/actions.js +3 -0
- package/lib/context/directory/handlers/attackProtection.js +1 -1
- package/lib/context/directory/handlers/connections.js +21 -1
- package/lib/context/directory/handlers/databases.js +10 -3
- package/lib/context/directory/handlers/emailProvider.js +1 -1
- package/lib/context/directory/handlers/logStreams.js +1 -1
- package/lib/context/directory/index.js +15 -0
- package/lib/context/yaml/handlers/attackProtection.js +1 -1
- package/lib/context/yaml/handlers/connections.js +1 -1
- package/lib/context/yaml/handlers/emailProvider.js +1 -1
- package/lib/context/yaml/handlers/logStreams.js +1 -1
- package/lib/context/yaml/index.js +3 -0
- package/lib/keywordPreservation.js +8 -3
- package/lib/tools/auth0/handlers/connections.d.ts +1 -0
- package/lib/tools/auth0/handlers/connections.js +1 -0
- package/lib/tools/auth0/handlers/default.d.ts +7 -0
- package/lib/tools/auth0/handlers/default.js +21 -2
- package/lib/tools/auth0/handlers/networkACLs.d.ts +42 -0
- package/lib/tools/auth0/handlers/networkACLs.js +21 -0
- package/lib/types.d.ts +4 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [8.36.0] - 2026-06-05
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Add optional flag to skip secret masking during export. [#1396]
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Remove stale connection files from directory export when connections are deleted. [#1389]
|
|
19
|
+
|
|
20
|
+
## [8.35.0] - 2026-05-22
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- Add `AUTH0_IGNORE_DRY_RUN_FIELDS` config option to exclude specified fields from `--dry-run` diff output per handler type. [#1385]
|
|
25
|
+
- Add hostnames and CIDR fields to network ACL match schema. [#1386]
|
|
26
|
+
- Add `ES384` and `ES512` as valid `dpop_signing_alg` values for OIDC and Okta enterprise connections (GA). [#1391]
|
|
27
|
+
|
|
28
|
+
### Deprecated
|
|
29
|
+
|
|
30
|
+
- Deprecate absolute and external file path references in config handlers; a warning is emitted when detected. Support will be removed in a future major version. [#1392]
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Fix keyword markers in themes not being preserved during default export configuration. [#1387]
|
|
35
|
+
|
|
10
36
|
## [8.34.0] - 2026-05-11
|
|
11
37
|
|
|
12
38
|
### Added
|
|
@@ -1778,7 +1804,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1778
1804
|
[#1379]: https://github.com/auth0/auth0-deploy-cli/issues/1379
|
|
1779
1805
|
[#1380]: https://github.com/auth0/auth0-deploy-cli/issues/1380
|
|
1780
1806
|
[#1381]: https://github.com/auth0/auth0-deploy-cli/issues/1381
|
|
1781
|
-
[
|
|
1807
|
+
[#1385]: https://github.com/auth0/auth0-deploy-cli/issues/1385
|
|
1808
|
+
[#1386]: https://github.com/auth0/auth0-deploy-cli/issues/1386
|
|
1809
|
+
[#1387]: https://github.com/auth0/auth0-deploy-cli/issues/1387
|
|
1810
|
+
[#1389]: https://github.com/auth0/auth0-deploy-cli/issues/1389
|
|
1811
|
+
[#1391]: https://github.com/auth0/auth0-deploy-cli/issues/1391
|
|
1812
|
+
[#1392]: https://github.com/auth0/auth0-deploy-cli/issues/1392
|
|
1813
|
+
[#1396]: https://github.com/auth0/auth0-deploy-cli/issues/1396
|
|
1814
|
+
[Unreleased]: https://github.com/auth0/auth0-deploy-cli/compare/v8.36.0...HEAD
|
|
1815
|
+
[8.36.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.35.0...v8.36.0
|
|
1816
|
+
[8.35.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.34.0...v8.35.0
|
|
1782
1817
|
[8.34.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.33.0...v8.34.0
|
|
1783
1818
|
[8.33.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.32.0...v8.33.0
|
|
1784
1819
|
[8.32.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.31.0...v8.32.0
|
package/lib/args.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ type ExportSpecificParams = {
|
|
|
21
21
|
output_folder: string;
|
|
22
22
|
export_ids?: boolean;
|
|
23
23
|
export_ordered?: boolean;
|
|
24
|
+
export_secrets?: boolean;
|
|
24
25
|
};
|
|
25
26
|
export type ExportParams = ExportSpecificParams & SharedParams;
|
|
26
27
|
export type ImportParams = ImportSpecificParams & SharedParams;
|
package/lib/args.js
CHANGED
|
@@ -103,6 +103,11 @@ function getParams() {
|
|
|
103
103
|
describe: 'Order keys in exported JSON files for consistent diffs.',
|
|
104
104
|
type: 'boolean',
|
|
105
105
|
},
|
|
106
|
+
export_secrets: {
|
|
107
|
+
describe: 'Export actual secret values instead of placeholder markers.',
|
|
108
|
+
type: 'boolean',
|
|
109
|
+
default: false,
|
|
110
|
+
},
|
|
106
111
|
})
|
|
107
112
|
.example('$0 export -c config.json -f yaml -o path/to/export', 'Dump Auth0 config to folder in YAML format')
|
|
108
113
|
.example('$0 export -c config.json -f directory -o path/to/export', 'Dump Auth0 config to folder in directory format')
|
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, export_ordered: exportOrdered, 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, export_secrets: exportSecrets, secret: clientSecret, env: shouldInheritEnv = false, experimental_ea: experimentalEA, } = params;
|
|
15
15
|
if (shouldInheritEnv) {
|
|
16
16
|
nconf_1.default.env().use('memory');
|
|
17
17
|
}
|
|
@@ -36,6 +36,10 @@ async function exportCMD(params) {
|
|
|
36
36
|
if (exportOrdered) {
|
|
37
37
|
overrides.AUTH0_EXPORT_ORDERED = exportOrdered;
|
|
38
38
|
}
|
|
39
|
+
// Allow passed in export_secrets to override the configured one
|
|
40
|
+
if (exportSecrets) {
|
|
41
|
+
overrides.AUTH0_EXPORT_SECRETS = exportSecrets;
|
|
42
|
+
}
|
|
39
43
|
// Overrides AUTH0_INCLUDE_EXPERIMENTAL_EA is experimental_ea passed in command line
|
|
40
44
|
if (experimentalEA) {
|
|
41
45
|
overrides.AUTH0_EXPERIMENTAL_EA = experimentalEA;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { AttackProtection } from '../tools/auth0/handlers/attackProtection';
|
|
2
|
-
|
|
2
|
+
import { Config } from '../types';
|
|
3
|
+
export declare function emailProviderDefaults(emailProvider: any, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): any;
|
|
3
4
|
export declare function phoneProviderDefaults(phoneProvider: any): any;
|
|
4
5
|
export declare function phoneTemplatesDefaults(phoneTemplate: any): any;
|
|
5
|
-
export declare function connectionDefaults(connection: any): any;
|
|
6
|
-
export declare function logStreamDefaults(logStreams: any): any;
|
|
7
|
-
export declare function attackProtectionDefaults(attackProtection: AttackProtection): AttackProtection;
|
|
6
|
+
export declare function connectionDefaults(connection: any, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): any;
|
|
7
|
+
export declare function logStreamDefaults(logStreams: any, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): any;
|
|
8
|
+
export declare function attackProtectionDefaults(attackProtection: AttackProtection, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): AttackProtection;
|
package/lib/context/defaults.js
CHANGED
|
@@ -7,9 +7,15 @@ exports.connectionDefaults = connectionDefaults;
|
|
|
7
7
|
exports.logStreamDefaults = logStreamDefaults;
|
|
8
8
|
exports.attackProtectionDefaults = attackProtectionDefaults;
|
|
9
9
|
const utils_1 = require("../tools/utils");
|
|
10
|
+
// env vars arrive as strings, so check both forms
|
|
11
|
+
function isExportSecrets(config) {
|
|
12
|
+
return (config?.AUTH0_EXPORT_SECRETS === true || config?.AUTH0_EXPORT_SECRETS === 'true');
|
|
13
|
+
}
|
|
10
14
|
// eslint-disable-next-line import/prefer-default-export
|
|
11
|
-
function emailProviderDefaults(emailProvider) {
|
|
15
|
+
function emailProviderDefaults(emailProvider, config) {
|
|
12
16
|
// eslint-disable-line
|
|
17
|
+
if (isExportSecrets(config))
|
|
18
|
+
return emailProvider;
|
|
13
19
|
const updated = { ...emailProvider };
|
|
14
20
|
const apiKeyProviders = ['mailgun', 'mandrill', 'sendgrid', 'sparkpost'];
|
|
15
21
|
// Add placeholder for credentials as they cannot be exported
|
|
@@ -100,8 +106,8 @@ function phoneTemplatesDefaults(phoneTemplate) {
|
|
|
100
106
|
});
|
|
101
107
|
return updated;
|
|
102
108
|
}
|
|
103
|
-
function connectionDefaults(connection) {
|
|
104
|
-
if (connection.options) {
|
|
109
|
+
function connectionDefaults(connection, config) {
|
|
110
|
+
if (!isExportSecrets(config) && connection.options) {
|
|
105
111
|
// Mask secret for key: connection.options.client_secret
|
|
106
112
|
(0, utils_1.maskSecretAtPath)({
|
|
107
113
|
resourceTypeName: 'connections',
|
|
@@ -112,7 +118,9 @@ function connectionDefaults(connection) {
|
|
|
112
118
|
}
|
|
113
119
|
return connection;
|
|
114
120
|
}
|
|
115
|
-
function logStreamDefaults(logStreams) {
|
|
121
|
+
function logStreamDefaults(logStreams, config) {
|
|
122
|
+
if (isExportSecrets(config))
|
|
123
|
+
return logStreams;
|
|
116
124
|
// masked sensitive fields
|
|
117
125
|
const sensitiveKeys = [
|
|
118
126
|
'httpAuthorization',
|
|
@@ -138,7 +146,9 @@ function logStreamDefaults(logStreams) {
|
|
|
138
146
|
});
|
|
139
147
|
return maskedLogStreams;
|
|
140
148
|
}
|
|
141
|
-
function attackProtectionDefaults(attackProtection) {
|
|
149
|
+
function attackProtectionDefaults(attackProtection, config) {
|
|
150
|
+
if (isExportSecrets(config))
|
|
151
|
+
return attackProtection;
|
|
142
152
|
const { captcha } = attackProtection;
|
|
143
153
|
if (captcha) {
|
|
144
154
|
const providersWithSecrets = ['arkose', 'hcaptcha', 'friendly_captcha', 'recaptcha_v2'];
|
|
@@ -26,6 +26,9 @@ function parse(context) {
|
|
|
26
26
|
// It can be a relative path, so we need to handle both cases.
|
|
27
27
|
const unixPath = module.code.replace(/[\\/]+/g, '/').replace(/^([a-zA-Z]+:|\.\/)/, '');
|
|
28
28
|
if (fs_extra_1.default.existsSync(unixPath)) {
|
|
29
|
+
logger_1.default.warn(`Support for absolute paths and paths outside the config root will be deprecated in a future version to improve the security of the tool. ` +
|
|
30
|
+
`Please update your configuration to use paths relative to the config directory. ` +
|
|
31
|
+
`Current absolute path used: ["${module.code}"]`);
|
|
29
32
|
module.code = context.loadFile(unixPath, moduleFolder);
|
|
30
33
|
}
|
|
31
34
|
else {
|
|
@@ -28,6 +28,9 @@ function parse(context) {
|
|
|
28
28
|
const unixPath = action.code.replace(/[\\/]+/g, '/').replace(/^([a-zA-Z]+:|\.\/)/, '');
|
|
29
29
|
if (fs_extra_1.default.existsSync(unixPath)) {
|
|
30
30
|
// If the Unix-style path exists, load the file from that path
|
|
31
|
+
logger_1.default.warn(`Support for absolute paths and paths outside the config root will be deprecated in a future version to improve the security of the tool. ` +
|
|
32
|
+
`Please update your configuration to use paths relative to the config directory. ` +
|
|
33
|
+
`Current absolute path used: ["${action.code}"]`);
|
|
31
34
|
action.code = context.loadFile(unixPath, actionFolder);
|
|
32
35
|
}
|
|
33
36
|
else {
|
|
@@ -65,7 +65,7 @@ async function dump(context) {
|
|
|
65
65
|
return;
|
|
66
66
|
const files = attackProtectionFiles(context.filePath);
|
|
67
67
|
fs_extra_1.default.ensureDirSync(files.directory);
|
|
68
|
-
const maskedAttackProtection = (0, defaults_1.attackProtectionDefaults)(attackProtection);
|
|
68
|
+
const maskedAttackProtection = (0, defaults_1.attackProtectionDefaults)(attackProtection, context.config);
|
|
69
69
|
if (maskedAttackProtection.botDetection) {
|
|
70
70
|
(0, utils_1.dumpJSON)(files.botDetection, maskedAttackProtection.botDetection);
|
|
71
71
|
}
|
|
@@ -51,6 +51,14 @@ async function dump(context) {
|
|
|
51
51
|
}
|
|
52
52
|
const connectionsFolder = path_1.default.join(context.filePath, tools_1.constants.CONNECTIONS_DIRECTORY);
|
|
53
53
|
fs_extra_1.default.ensureDirSync(connectionsFolder);
|
|
54
|
+
// Track files that should remain after dump (written + excluded).
|
|
55
|
+
// Seed from exclude names directly so excluded connections' files are preserved
|
|
56
|
+
// regardless of whether the connection still exists on the tenant.
|
|
57
|
+
const expectedFiles = new Set();
|
|
58
|
+
for (const name of excludedConnections) {
|
|
59
|
+
expectedFiles.add(`${(0, utils_1.sanitize)(name)}.json`);
|
|
60
|
+
expectedFiles.add(`${(0, utils_1.sanitize)(name)}.html`);
|
|
61
|
+
}
|
|
54
62
|
// Convert enabled_clients from id to name
|
|
55
63
|
connections.forEach((connection) => {
|
|
56
64
|
let dumpedConnection = {
|
|
@@ -62,7 +70,7 @@ async function dump(context) {
|
|
|
62
70
|
};
|
|
63
71
|
const connectionName = (0, utils_1.sanitize)(dumpedConnection.name);
|
|
64
72
|
// Mask secrets
|
|
65
|
-
dumpedConnection = (0, defaults_1.connectionDefaults)(dumpedConnection);
|
|
73
|
+
dumpedConnection = (0, defaults_1.connectionDefaults)(dumpedConnection, context.config);
|
|
66
74
|
if (dumpedConnection.strategy === 'email') {
|
|
67
75
|
(0, utils_1.ensureProp)(dumpedConnection, 'options.email.body');
|
|
68
76
|
const html = dumpedConnection.options.email.body;
|
|
@@ -81,7 +89,19 @@ async function dump(context) {
|
|
|
81
89
|
}
|
|
82
90
|
const connectionFile = path_1.default.join(connectionsFolder, `${connectionName}.json`);
|
|
83
91
|
(0, utils_1.dumpJSON)(connectionFile, dumpedConnection);
|
|
92
|
+
expectedFiles.add(`${connectionName}.json`);
|
|
93
|
+
if (dumpedConnection.strategy === 'email')
|
|
94
|
+
expectedFiles.add(`${connectionName}.html`);
|
|
84
95
|
});
|
|
96
|
+
// Remove files that belong to connections no longer present (and not excluded)
|
|
97
|
+
if (fs_extra_1.default.existsSync(connectionsFolder)) {
|
|
98
|
+
for (const existing of fs_extra_1.default.readdirSync(connectionsFolder)) {
|
|
99
|
+
const fullPath = path_1.default.join(connectionsFolder, existing);
|
|
100
|
+
if (fs_extra_1.default.statSync(fullPath).isFile() && !expectedFiles.has(existing)) {
|
|
101
|
+
fs_extra_1.default.removeSync(fullPath);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
85
105
|
}
|
|
86
106
|
const connectionsHandler = {
|
|
87
107
|
parse,
|
|
@@ -8,7 +8,7 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
|
8
8
|
const tools_1 = require("../../../tools");
|
|
9
9
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
10
10
|
const utils_1 = require("../../../utils");
|
|
11
|
-
function getDatabase(folder, mappingOpts) {
|
|
11
|
+
function getDatabase(folder, configRoot, mappingOpts) {
|
|
12
12
|
const metaFile = path_1.default.join(folder, 'database.json');
|
|
13
13
|
const metaData = (() => {
|
|
14
14
|
try {
|
|
@@ -40,7 +40,14 @@ function getDatabase(folder, mappingOpts) {
|
|
|
40
40
|
logger_1.default.warn('Skipping invalid database configuration: ' + name);
|
|
41
41
|
}
|
|
42
42
|
else {
|
|
43
|
-
|
|
43
|
+
const resolvedBase = path_1.default.resolve(configRoot);
|
|
44
|
+
const toLoad = path_1.default.resolve(folder, script);
|
|
45
|
+
if (!toLoad.startsWith(resolvedBase + path_1.default.sep)) {
|
|
46
|
+
logger_1.default.warn(`Support for absolute paths and paths outside the config root will be deprecated in a future version to improve the security of the tool. ` +
|
|
47
|
+
`Please update your configuration to use paths relative to the config directory. ` +
|
|
48
|
+
`Current absolute path used: ["${script}"]`);
|
|
49
|
+
}
|
|
50
|
+
database.options.customScripts[name] = (0, tools_1.loadFileAndReplaceKeywords)(toLoad, mappingOpts);
|
|
44
51
|
}
|
|
45
52
|
});
|
|
46
53
|
}
|
|
@@ -55,7 +62,7 @@ function parse(context) {
|
|
|
55
62
|
.map((f) => path_1.default.join(databaseFolder, f))
|
|
56
63
|
.filter((f) => (0, utils_1.isDirectory)(f));
|
|
57
64
|
const databases = folders
|
|
58
|
-
.map((f) => getDatabase(f, {
|
|
65
|
+
.map((f) => getDatabase(f, context.filePath, {
|
|
59
66
|
mappings: context.mappings,
|
|
60
67
|
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
61
68
|
}))
|
|
@@ -31,7 +31,7 @@ async function dump(context) {
|
|
|
31
31
|
const excludedDefaults = context.assets.exclude?.defaults || [];
|
|
32
32
|
if (!excludedDefaults.includes('emailProvider')) {
|
|
33
33
|
// Add placeholder for credentials as they cannot be exported
|
|
34
|
-
return (0, defaults_1.emailProviderDefaults)(context.assets.emailProvider);
|
|
34
|
+
return (0, defaults_1.emailProviderDefaults)(context.assets.emailProvider, context.config);
|
|
35
35
|
}
|
|
36
36
|
return context.assets.emailProvider;
|
|
37
37
|
})();
|
|
@@ -31,7 +31,7 @@ async function dump(context) {
|
|
|
31
31
|
const logStreamsDirectory = path_1.default.join(context.filePath, tools_1.constants.LOG_STREAMS_DIRECTORY);
|
|
32
32
|
fs_extra_1.default.ensureDirSync(logStreamsDirectory);
|
|
33
33
|
// masked sensitive fields
|
|
34
|
-
const maskedLogStreams = (0, defaults_1.logStreamDefaults)(logStreams);
|
|
34
|
+
const maskedLogStreams = (0, defaults_1.logStreamDefaults)(logStreams, context.config);
|
|
35
35
|
maskedLogStreams.forEach((logStream) => {
|
|
36
36
|
const ruleFile = path_1.default.join(logStreamsDirectory, `${(0, utils_1.sanitize)(logStream.name)}.json`);
|
|
37
37
|
(0, utils_1.dumpJSON)(ruleFile, logStream);
|
|
@@ -73,6 +73,9 @@ class DirectoryContext {
|
|
|
73
73
|
if (!(0, utils_1.isFile)(toLoad)) {
|
|
74
74
|
// try load not relative to yaml file
|
|
75
75
|
toLoad = f;
|
|
76
|
+
logger_1.default.warn(`Support for absolute paths and paths outside the config root will be deprecated in a future version to improve the security of the tool. ` +
|
|
77
|
+
`Please update your configuration to use paths relative to the config directory. ` +
|
|
78
|
+
`Current absolute path used: ["${f}"]`);
|
|
76
79
|
}
|
|
77
80
|
return (0, tools_1.loadFileAndReplaceKeywords)(toLoad, {
|
|
78
81
|
mappings: this.mappings,
|
|
@@ -123,6 +126,18 @@ class DirectoryContext {
|
|
|
123
126
|
else {
|
|
124
127
|
this.assets = auth0.assets;
|
|
125
128
|
}
|
|
129
|
+
// Re-attach exclude/include config lost when assets was overwritten above
|
|
130
|
+
this.assets.exclude = {
|
|
131
|
+
rules: this.config.AUTH0_EXCLUDED_RULES || [],
|
|
132
|
+
clients: this.config.AUTH0_EXCLUDED_CLIENTS || [],
|
|
133
|
+
databases: this.config.AUTH0_EXCLUDED_DATABASES || [],
|
|
134
|
+
connections: this.config.AUTH0_EXCLUDED_CONNECTIONS || [],
|
|
135
|
+
resourceServers: this.config.AUTH0_EXCLUDED_RESOURCE_SERVERS || [],
|
|
136
|
+
defaults: this.config.AUTH0_EXCLUDED_DEFAULTS || [],
|
|
137
|
+
};
|
|
138
|
+
this.assets.include = {
|
|
139
|
+
connections: this.config.AUTH0_INCLUDED_CONNECTIONS || [],
|
|
140
|
+
};
|
|
126
141
|
// Clean known read only fields
|
|
127
142
|
this.assets = (0, readonly_1.default)(this.assets, this.config);
|
|
128
143
|
// Copy clients to be used by handlers which require converting client_id to the name
|
|
@@ -25,7 +25,7 @@ async function dump(context) {
|
|
|
25
25
|
if (captcha) {
|
|
26
26
|
attackProtectionConfig.captcha = captcha;
|
|
27
27
|
}
|
|
28
|
-
const maskedAttackProtection = (0, defaults_1.attackProtectionDefaults)(attackProtectionConfig);
|
|
28
|
+
const maskedAttackProtection = (0, defaults_1.attackProtectionDefaults)(attackProtectionConfig, context.config);
|
|
29
29
|
return {
|
|
30
30
|
attackProtection: maskedAttackProtection,
|
|
31
31
|
};
|
|
@@ -53,7 +53,7 @@ async function dump(context) {
|
|
|
53
53
|
}),
|
|
54
54
|
};
|
|
55
55
|
// Mask secrets
|
|
56
|
-
dumpedConnection = (0, defaults_1.connectionDefaults)(dumpedConnection);
|
|
56
|
+
dumpedConnection = (0, defaults_1.connectionDefaults)(dumpedConnection, context.config);
|
|
57
57
|
if (dumpedConnection.strategy === 'email') {
|
|
58
58
|
(0, utils_1.ensureProp)(connection, 'options.email.body');
|
|
59
59
|
const connectionsFolder = path_1.default.join(context.basePath, tools_1.constants.CONNECTIONS_DIRECTORY);
|
|
@@ -17,7 +17,7 @@ async function dump(context) {
|
|
|
17
17
|
const excludedDefaults = context.assets.exclude?.defaults || [];
|
|
18
18
|
if (emailProvider && !excludedDefaults.includes('emailProvider')) {
|
|
19
19
|
// Add placeholder for credentials as they cannot be exported
|
|
20
|
-
return (0, defaults_1.emailProviderDefaults)(emailProvider);
|
|
20
|
+
return (0, defaults_1.emailProviderDefaults)(emailProvider, context.config);
|
|
21
21
|
}
|
|
22
22
|
return emailProvider;
|
|
23
23
|
})();
|
|
@@ -14,7 +14,7 @@ async function dump(context) {
|
|
|
14
14
|
if (!logStreams)
|
|
15
15
|
return { logStreams: null };
|
|
16
16
|
// masked sensitive fields
|
|
17
|
-
const maskedLogStreams = (0, defaults_1.logStreamDefaults)(logStreams);
|
|
17
|
+
const maskedLogStreams = (0, defaults_1.logStreamDefaults)(logStreams, context.config);
|
|
18
18
|
return {
|
|
19
19
|
logStreams: maskedLogStreams,
|
|
20
20
|
};
|
|
@@ -47,6 +47,9 @@ class YAMLContext {
|
|
|
47
47
|
if (!(0, utils_1.isFile)(toLoad)) {
|
|
48
48
|
// try load not relative to yaml file
|
|
49
49
|
toLoad = f;
|
|
50
|
+
logger_1.default.warn(`Support for absolute paths and paths outside the config root will be deprecated in a future version to improve the security of the tool. ` +
|
|
51
|
+
`Please update your configuration to use paths relative to the config directory. ` +
|
|
52
|
+
`Current absolute path used: ["${f}"]`);
|
|
50
53
|
}
|
|
51
54
|
return (0, tools_1.loadFileAndReplaceKeywords)(path_1.default.resolve(toLoad), {
|
|
52
55
|
mappings: this.mappings,
|
|
@@ -48,17 +48,22 @@ const getPreservableFieldsFromAssets = (asset, keywordMappings, resourceSpecific
|
|
|
48
48
|
const specificAddress = resourceIdentifiers.reduce((aggregateAddress, resourceIdentifier) => {
|
|
49
49
|
resourceSpecificIdentifiers[address];
|
|
50
50
|
if (resourceIdentifier === undefined)
|
|
51
|
-
return
|
|
51
|
+
return aggregateAddress; // See if this specific resource type has an identifier
|
|
52
52
|
const identifierFieldValue = arrayItem[resourceIdentifier];
|
|
53
53
|
if (identifierFieldValue === undefined)
|
|
54
|
-
return
|
|
54
|
+
return aggregateAddress; // See if this specific array item possess the resource-specific identifier
|
|
55
55
|
if (aggregateAddress === '') {
|
|
56
56
|
return `${resourceIdentifier}=${identifierFieldValue}`;
|
|
57
57
|
}
|
|
58
58
|
return `${aggregateAddress}||${resourceIdentifier}=${identifierFieldValue}`;
|
|
59
59
|
}, '');
|
|
60
60
|
if (specificAddress.length === 0) {
|
|
61
|
-
|
|
61
|
+
// No identifiers registered: skip. Identifiers registered but absent from item: fall back to positional index.
|
|
62
|
+
if (resourceIdentifiers.length === 0) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const arrayIndex = asset.indexOf(arrayItem);
|
|
66
|
+
return (0, exports.getPreservableFieldsFromAssets)(arrayItem, keywordMappings, resourceSpecificIdentifiers, `${address}${shouldRenderDot ? '.' : ''}${arrayIndex}`);
|
|
62
67
|
}
|
|
63
68
|
return (0, exports.getPreservableFieldsFromAssets)(arrayItem, keywordMappings, resourceSpecificIdentifiers, `${address}${shouldRenderDot ? '.' : ''}[${specificAddress}]`);
|
|
64
69
|
})
|
|
@@ -42,6 +42,13 @@ export default class APIHandler {
|
|
|
42
42
|
};
|
|
43
43
|
ignoreDryRunFields: APIHandler['ignoreDryRunFields'];
|
|
44
44
|
});
|
|
45
|
+
/**
|
|
46
|
+
* Returns the effective `ignoreDryRunFields` for this handler, merging the
|
|
47
|
+
* handler's hardcoded defaults with any user-supplied entries from the
|
|
48
|
+
* `AUTH0_IGNORE_DRY_RUN_FIELDS[<type>]` config key. Read lazily so that the
|
|
49
|
+
* constructor remains free of config-provider invocations.
|
|
50
|
+
*/
|
|
51
|
+
getEffectiveIgnoreDryRunFields(): string[];
|
|
45
52
|
getClientFN(fn: ApiMethodOverride): Function;
|
|
46
53
|
didDelete(item: Asset): void;
|
|
47
54
|
didCreate(item: Asset): void;
|
|
@@ -88,6 +88,25 @@ class APIHandler {
|
|
|
88
88
|
this.created = 0;
|
|
89
89
|
this.deleted = 0;
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Returns the effective `ignoreDryRunFields` for this handler, merging the
|
|
93
|
+
* handler's hardcoded defaults with any user-supplied entries from the
|
|
94
|
+
* `AUTH0_IGNORE_DRY_RUN_FIELDS[<type>]` config key. Read lazily so that the
|
|
95
|
+
* constructor remains free of config-provider invocations.
|
|
96
|
+
*/
|
|
97
|
+
getEffectiveIgnoreDryRunFields() {
|
|
98
|
+
let configured = [];
|
|
99
|
+
try {
|
|
100
|
+
const map = this.config('AUTH0_IGNORE_DRY_RUN_FIELDS');
|
|
101
|
+
if (map && Array.isArray(map[this.type])) {
|
|
102
|
+
configured = map[this.type];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
configured = [];
|
|
107
|
+
}
|
|
108
|
+
return [...this.ignoreDryRunFields, ...configured].filter((field, index, allFields) => allFields.indexOf(field) === index);
|
|
109
|
+
}
|
|
91
110
|
getClientFN(fn) {
|
|
92
111
|
if (typeof fn === 'string') {
|
|
93
112
|
const client = this.client[this.type];
|
|
@@ -194,7 +213,7 @@ class APIHandler {
|
|
|
194
213
|
// @ts-ignore TODO: investigate what happens when `existing` is null
|
|
195
214
|
existing,
|
|
196
215
|
identifiers: this.identifiers,
|
|
197
|
-
ignoreDryRunFields: this.
|
|
216
|
+
ignoreDryRunFields: this.getEffectiveIgnoreDryRunFields(),
|
|
198
217
|
});
|
|
199
218
|
}
|
|
200
219
|
// if we are not in dry run mode, calculate the changes
|
|
@@ -227,7 +246,7 @@ class APIHandler {
|
|
|
227
246
|
// @ts-ignore TODO: investigate what happens when `existing` is null
|
|
228
247
|
existing,
|
|
229
248
|
identifiers: this.identifiers,
|
|
230
|
-
ignoreDryRunFields: this.
|
|
249
|
+
ignoreDryRunFields: this.getEffectiveIgnoreDryRunFields(),
|
|
231
250
|
});
|
|
232
251
|
}
|
|
233
252
|
async validate(assets) {
|
|
@@ -150,6 +150,27 @@ export declare const schema: {
|
|
|
150
150
|
minItems: number;
|
|
151
151
|
maxItems: number;
|
|
152
152
|
};
|
|
153
|
+
hostnames: {
|
|
154
|
+
type: string;
|
|
155
|
+
items: {
|
|
156
|
+
type: string;
|
|
157
|
+
};
|
|
158
|
+
uniqueItems: boolean;
|
|
159
|
+
};
|
|
160
|
+
connecting_ipv4_cidrs: {
|
|
161
|
+
type: string;
|
|
162
|
+
items: {
|
|
163
|
+
type: string;
|
|
164
|
+
};
|
|
165
|
+
uniqueItems: boolean;
|
|
166
|
+
};
|
|
167
|
+
connecting_ipv6_cidrs: {
|
|
168
|
+
type: string;
|
|
169
|
+
items: {
|
|
170
|
+
type: string;
|
|
171
|
+
};
|
|
172
|
+
uniqueItems: boolean;
|
|
173
|
+
};
|
|
153
174
|
};
|
|
154
175
|
additionalProperties: boolean;
|
|
155
176
|
};
|
|
@@ -228,6 +249,27 @@ export declare const schema: {
|
|
|
228
249
|
minItems: number;
|
|
229
250
|
maxItems: number;
|
|
230
251
|
};
|
|
252
|
+
hostnames: {
|
|
253
|
+
type: string;
|
|
254
|
+
items: {
|
|
255
|
+
type: string;
|
|
256
|
+
};
|
|
257
|
+
uniqueItems: boolean;
|
|
258
|
+
};
|
|
259
|
+
connecting_ipv4_cidrs: {
|
|
260
|
+
type: string;
|
|
261
|
+
items: {
|
|
262
|
+
type: string;
|
|
263
|
+
};
|
|
264
|
+
uniqueItems: boolean;
|
|
265
|
+
};
|
|
266
|
+
connecting_ipv6_cidrs: {
|
|
267
|
+
type: string;
|
|
268
|
+
items: {
|
|
269
|
+
type: string;
|
|
270
|
+
};
|
|
271
|
+
uniqueItems: boolean;
|
|
272
|
+
};
|
|
231
273
|
};
|
|
232
274
|
additionalProperties: boolean;
|
|
233
275
|
};
|
|
@@ -133,6 +133,27 @@ const MatchSchema = {
|
|
|
133
133
|
minItems: 1,
|
|
134
134
|
maxItems: 10,
|
|
135
135
|
},
|
|
136
|
+
hostnames: {
|
|
137
|
+
type: 'array',
|
|
138
|
+
items: {
|
|
139
|
+
type: 'string',
|
|
140
|
+
},
|
|
141
|
+
uniqueItems: true,
|
|
142
|
+
},
|
|
143
|
+
connecting_ipv4_cidrs: {
|
|
144
|
+
type: 'array',
|
|
145
|
+
items: {
|
|
146
|
+
type: 'string',
|
|
147
|
+
},
|
|
148
|
+
uniqueItems: true,
|
|
149
|
+
},
|
|
150
|
+
connecting_ipv6_cidrs: {
|
|
151
|
+
type: 'array',
|
|
152
|
+
items: {
|
|
153
|
+
type: 'string',
|
|
154
|
+
},
|
|
155
|
+
uniqueItems: true,
|
|
156
|
+
},
|
|
136
157
|
},
|
|
137
158
|
additionalProperties: false,
|
|
138
159
|
};
|
package/lib/types.d.ts
CHANGED
|
@@ -72,10 +72,14 @@ export type Config = {
|
|
|
72
72
|
AUTH0_KEYWORD_REPLACE_MAPPINGS?: KeywordMappings;
|
|
73
73
|
AUTH0_EXPORT_IDENTIFIERS?: boolean;
|
|
74
74
|
AUTH0_EXPORT_ORDERED?: boolean;
|
|
75
|
+
AUTH0_EXPORT_SECRETS?: boolean;
|
|
75
76
|
AUTH0_CONNECTIONS_DIRECTORY?: string;
|
|
76
77
|
AUTH0_DRY_RUN?: boolean | 'preview';
|
|
77
78
|
AUTH0_DRY_RUN_INTERACTIVE?: boolean;
|
|
78
79
|
AUTH0_DRY_RUN_APPLY?: boolean;
|
|
80
|
+
AUTH0_IGNORE_DRY_RUN_FIELDS?: {
|
|
81
|
+
[handlerType: string]: string[];
|
|
82
|
+
};
|
|
79
83
|
EXCLUDED_PROPS?: {
|
|
80
84
|
[key: string]: string[];
|
|
81
85
|
};
|