auth0-deploy-cli 8.43.0 → 8.45.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 +53 -1
- package/README.md +1 -1
- package/lib/context/defaults.d.ts +7 -0
- package/lib/context/defaults.js +74 -30
- package/lib/context/directory/handlers/attackProtection.js +1 -1
- package/lib/context/directory/handlers/branding.js +9 -1
- package/lib/context/directory/handlers/clients.js +9 -3
- package/lib/context/directory/handlers/connections.js +10 -4
- package/lib/context/directory/handlers/emailTemplates.js +9 -0
- package/lib/context/directory/handlers/index.js +2 -0
- package/lib/context/directory/handlers/networkACLKeys.d.ts +6 -0
- package/lib/context/directory/handlers/networkACLKeys.js +54 -0
- package/lib/context/directory/handlers/prompts.js +11 -3
- package/lib/context/yaml/handlers/attackProtection.js +1 -1
- package/lib/context/yaml/handlers/branding.js +8 -1
- package/lib/context/yaml/handlers/clients.js +4 -1
- package/lib/context/yaml/handlers/connections.js +3 -1
- package/lib/context/yaml/handlers/flows.js +7 -0
- package/lib/context/yaml/handlers/forms.js +7 -0
- package/lib/context/yaml/handlers/index.js +2 -0
- package/lib/context/yaml/handlers/networkACLKeys.d.ts +6 -0
- package/lib/context/yaml/handlers/networkACLKeys.js +42 -0
- package/lib/context/yaml/handlers/prompts.js +7 -1
- package/lib/keywordPreservation.d.ts +1 -1
- package/lib/keywordPreservation.js +2 -1
- package/lib/readonly.js +5 -0
- package/lib/tools/auth0/handlers/attackProtection.js +3 -1
- package/lib/tools/auth0/handlers/clients.d.ts +40 -0
- package/lib/tools/auth0/handlers/clients.js +58 -1
- package/lib/tools/auth0/handlers/connectionProfiles.d.ts +27 -0
- package/lib/tools/auth0/handlers/connectionProfiles.js +30 -0
- package/lib/tools/auth0/handlers/connections.js +10 -3
- package/lib/tools/auth0/handlers/databases.d.ts +28 -0
- package/lib/tools/auth0/handlers/databases.js +47 -22
- package/lib/tools/auth0/handlers/default.d.ts +23 -0
- package/lib/tools/auth0/handlers/default.js +21 -10
- package/lib/tools/auth0/handlers/index.js +2 -0
- package/lib/tools/auth0/handlers/networkACLKeys.d.ts +45 -0
- package/lib/tools/auth0/handlers/networkACLKeys.js +177 -0
- package/lib/tools/auth0/handlers/networkACLs.d.ts +114 -2
- package/lib/tools/auth0/handlers/networkACLs.js +91 -16
- package/lib/tools/auth0/handlers/organizations.d.ts +1 -1
- package/lib/tools/auth0/handlers/organizations.js +49 -20
- package/lib/tools/auth0/handlers/phoneProvider.js +10 -2
- package/lib/tools/calculateDryRunChanges.js +12 -8
- package/lib/tools/constants.d.ts +1 -0
- package/lib/tools/constants.js +1 -0
- package/lib/types.d.ts +3 -1
- package/lib/utils.js +5 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [8.45.0] - 2026-09-09
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Support `b2b_integration_configuration` for Enterprise Connect (EA). [#1486]
|
|
15
|
+
- Add `networkACLKeys` handler for HMAC signature key management. [#1480]
|
|
16
|
+
- Support `match_all` in network ACL rules for unconditional block/allow. [#1483]
|
|
17
|
+
- Add `otp_settings` support for database connections. [#1481]
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Prevent export crash when a handler has no identifiers during keyword preservation. [#1487]
|
|
22
|
+
- Catch `feature_not_enabled` via `err.body.errorCode` and 400 status in organizations handler. [#1482]
|
|
23
|
+
- Prevent false `enabled_clients` diff in database dry-run. [#1479]
|
|
24
|
+
|
|
25
|
+
## [8.44.0] - 2026-08-26
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- Support `third_party_client_access` and `cross_app_access_resource_app` on clients and connections. [#1471]
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Warn on path traversal in file references and fix double-path resolution in YAML connections handler. [#1477]
|
|
34
|
+
- Strip `form_submission_mode` and `form_submission_behavior` from `bruteForceProtection` on export and import. [#1476]
|
|
35
|
+
- Include empty credentials for custom phone provider on import and export to prevent 400 errors. [#1474]
|
|
36
|
+
- Preserve keyword placeholder strings for all email provider credentials. [#1470]
|
|
37
|
+
- Preserve `idpinitiated.enabled:false` on SAML connection export instead of emitting `undefined_clientId`. [#1465]
|
|
38
|
+
- Apply 429 backoff to organization connection writes to prevent rate-limit failures. [#1464]
|
|
39
|
+
- Resolve dry-run reporting bugs across multiple asset types. [#1462]
|
|
40
|
+
- Skip organization PATCH when no top-level properties have changed. [#1461]
|
|
41
|
+
- Sort `attackProtection` arrays on export to prevent spurious diffs. [#1460]
|
|
42
|
+
|
|
10
43
|
## [8.43.0] - 2026-08-12
|
|
11
44
|
|
|
12
45
|
### Added
|
|
@@ -1927,7 +1960,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1927
1960
|
[#1454]: https://github.com/auth0/auth0-deploy-cli/issues/1454
|
|
1928
1961
|
[#1456]: https://github.com/auth0/auth0-deploy-cli/issues/1456
|
|
1929
1962
|
[#1457]: https://github.com/auth0/auth0-deploy-cli/issues/1457
|
|
1930
|
-
[
|
|
1963
|
+
[#1460]: https://github.com/auth0/auth0-deploy-cli/issues/1460
|
|
1964
|
+
[#1461]: https://github.com/auth0/auth0-deploy-cli/issues/1461
|
|
1965
|
+
[#1462]: https://github.com/auth0/auth0-deploy-cli/issues/1462
|
|
1966
|
+
[#1464]: https://github.com/auth0/auth0-deploy-cli/issues/1464
|
|
1967
|
+
[#1465]: https://github.com/auth0/auth0-deploy-cli/issues/1465
|
|
1968
|
+
[#1470]: https://github.com/auth0/auth0-deploy-cli/issues/1470
|
|
1969
|
+
[#1471]: https://github.com/auth0/auth0-deploy-cli/issues/1471
|
|
1970
|
+
[#1474]: https://github.com/auth0/auth0-deploy-cli/issues/1474
|
|
1971
|
+
[#1476]: https://github.com/auth0/auth0-deploy-cli/issues/1476
|
|
1972
|
+
[#1477]: https://github.com/auth0/auth0-deploy-cli/issues/1477
|
|
1973
|
+
[#1479]: https://github.com/auth0/auth0-deploy-cli/issues/1479
|
|
1974
|
+
[#1480]: https://github.com/auth0/auth0-deploy-cli/issues/1480
|
|
1975
|
+
[#1481]: https://github.com/auth0/auth0-deploy-cli/issues/1481
|
|
1976
|
+
[#1482]: https://github.com/auth0/auth0-deploy-cli/issues/1482
|
|
1977
|
+
[#1483]: https://github.com/auth0/auth0-deploy-cli/issues/1483
|
|
1978
|
+
[#1486]: https://github.com/auth0/auth0-deploy-cli/issues/1486
|
|
1979
|
+
[#1487]: https://github.com/auth0/auth0-deploy-cli/issues/1487
|
|
1980
|
+
[Unreleased]: https://github.com/auth0/auth0-deploy-cli/compare/v8.45.0...HEAD
|
|
1981
|
+
[8.45.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.44.0...v8.45.0
|
|
1982
|
+
[8.44.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.43.0...v8.44.0
|
|
1931
1983
|
[8.43.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.42.0...v8.43.0
|
|
1932
1984
|
[8.42.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.41.0...v8.42.0
|
|
1933
1985
|
[8.41.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.40.0...v8.41.0
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
The Auth0 Deploy CLI is a tool that helps you manage your Auth0 tenant configuration. It integrates into your development workflows as a standalone CLI or as a node module.
|
|
10
10
|
|
|
11
|
-
**Supported resource types:** actions, branding, client grants, clients (applications), connections, databases, custom domains, email templates, emails, grants, guardian, hook secrets, log streams, organizations, pages, prompts, resource servers (APIs), roles, tenant settings, themes, forms, flows, self-service profiles, network ACLs.
|
|
11
|
+
**Supported resource types:** actions, branding, client grants, clients (applications), connections, databases, custom domains, email templates, emails, grants, guardian, hook secrets, log streams, organizations, pages, prompts, resource servers (APIs), roles, tenant settings, themes, forms, flows, self-service profiles, network ACLs, network ACL keys.
|
|
12
12
|
|
|
13
13
|
🎢 [Highlights](#highlights) • 📚 [Documentation](#documentation) • 🚀 [Getting Started](#getting-started) • 📝 [Feedback](#feedback) • 💬 [Discussions](https://github.com/auth0/auth0-deploy-cli/discussions)
|
|
14
14
|
|
|
@@ -6,4 +6,11 @@ export declare function phoneTemplatesDefaults(phoneTemplate: any): any;
|
|
|
6
6
|
export declare function connectionDefaults(connection: any, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): any;
|
|
7
7
|
export declare function logStreamDefaults(logStreams: any, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): any;
|
|
8
8
|
export declare function eventStreamDefaults(eventStreams: any[], config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): any[];
|
|
9
|
+
/**
|
|
10
|
+
* Sorts primitive arrays (e.g. `shields`, `admin_notification_frequency`) within the
|
|
11
|
+
* attackProtection subtree in place, so repeated exports produce deterministic output.
|
|
12
|
+
* These are unordered, string-typed enum sets — ordering is cosmetic. Object arrays are
|
|
13
|
+
* left as-is.
|
|
14
|
+
*/
|
|
15
|
+
export declare function sortAttackProtectionArrays(attackProtection: AttackProtection): AttackProtection;
|
|
9
16
|
export declare function attackProtectionDefaults(attackProtection: AttackProtection, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): AttackProtection;
|
package/lib/context/defaults.js
CHANGED
|
@@ -6,6 +6,7 @@ exports.phoneTemplatesDefaults = phoneTemplatesDefaults;
|
|
|
6
6
|
exports.connectionDefaults = connectionDefaults;
|
|
7
7
|
exports.logStreamDefaults = logStreamDefaults;
|
|
8
8
|
exports.eventStreamDefaults = eventStreamDefaults;
|
|
9
|
+
exports.sortAttackProtectionArrays = sortAttackProtectionArrays;
|
|
9
10
|
exports.attackProtectionDefaults = attackProtectionDefaults;
|
|
10
11
|
const utils_1 = require("../tools/utils");
|
|
11
12
|
// env vars arrive as strings, so check both forms
|
|
@@ -22,45 +23,57 @@ function emailProviderDefaults(emailProvider, config) {
|
|
|
22
23
|
// Add placeholder for credentials as they cannot be exported
|
|
23
24
|
const { name } = updated;
|
|
24
25
|
if (apiKeyProviders.includes(name)) {
|
|
25
|
-
updated.credentials
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
if (typeof updated.credentials !== 'string') {
|
|
27
|
+
updated.credentials = {
|
|
28
|
+
api_key: `##${name.toUpperCase()}_API_KEY##`,
|
|
29
|
+
...(updated.credentials || {}),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
29
32
|
}
|
|
30
33
|
if (name === 'smtp') {
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
// If credentials is a keyword placeholder string (e.g. @@SMTP_CREDENTIALS@@), preserve it as-is.
|
|
35
|
+
// The `in` operator requires an object and would throw a TypeError on a string.
|
|
36
|
+
if (typeof updated.credentials !== 'string') {
|
|
37
|
+
// This is to mask smtp_user to '##SMTP_USER##'
|
|
38
|
+
if (updated.credentials && 'smtp_user' in updated.credentials) {
|
|
39
|
+
delete updated.credentials.smtp_user;
|
|
40
|
+
}
|
|
41
|
+
updated.credentials = {
|
|
42
|
+
smtp_host: '##SMTP_HOSTNAME##',
|
|
43
|
+
smtp_port: '##SMTP_PORT##',
|
|
44
|
+
smtp_user: '##SMTP_USER##',
|
|
45
|
+
smtp_pass: '##SMTP_PASS##',
|
|
46
|
+
...(updated.credentials || {}),
|
|
47
|
+
};
|
|
34
48
|
}
|
|
35
|
-
updated.credentials = {
|
|
36
|
-
smtp_host: '##SMTP_HOSTNAME##',
|
|
37
|
-
smtp_port: '##SMTP_PORT##',
|
|
38
|
-
smtp_user: '##SMTP_USER##',
|
|
39
|
-
smtp_pass: '##SMTP_PASS##',
|
|
40
|
-
...(updated.credentials || {}),
|
|
41
|
-
};
|
|
42
49
|
}
|
|
43
50
|
if (name === 'ses') {
|
|
44
|
-
updated.credentials
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
if (typeof updated.credentials !== 'string') {
|
|
52
|
+
updated.credentials = {
|
|
53
|
+
accessKeyId: '##SES_ACCESS_KEY_ID##',
|
|
54
|
+
secretAccessKey: '##SES_ACCESS_SECRET_KEY##',
|
|
55
|
+
region: '##SES_AWS_REGION##',
|
|
56
|
+
...(updated.credentials || {}),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
50
59
|
}
|
|
51
60
|
if (name === 'azure_cs') {
|
|
52
|
-
updated.credentials
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
if (typeof updated.credentials !== 'string') {
|
|
62
|
+
updated.credentials = {
|
|
63
|
+
connectionString: '##AZURE_CS_CONNECTION_KEY##',
|
|
64
|
+
...(updated.credentials || {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
56
67
|
}
|
|
57
68
|
if (name === 'ms365') {
|
|
58
|
-
updated.credentials
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
if (typeof updated.credentials !== 'string') {
|
|
70
|
+
updated.credentials = {
|
|
71
|
+
tenantId: '##MS365_TENANT_ID##',
|
|
72
|
+
clientId: '##MS365_CLIENT_ID##',
|
|
73
|
+
clientSecret: '##MS365_CLIENT_SECRET##',
|
|
74
|
+
...(updated.credentials || {}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
64
77
|
}
|
|
65
78
|
return updated;
|
|
66
79
|
}
|
|
@@ -87,6 +100,12 @@ function phoneProviderDefaults(phoneProvider) {
|
|
|
87
100
|
auth_token: `##${name.toUpperCase()}_AUTH_TOKEN##`,
|
|
88
101
|
};
|
|
89
102
|
}
|
|
103
|
+
else if (name === 'custom') {
|
|
104
|
+
// The `custom` provider has no exportable secrets, but the API requires a
|
|
105
|
+
// `credentials` object on import even when empty. Emit an empty object so
|
|
106
|
+
// exported configs round-trip cleanly.
|
|
107
|
+
updated.credentials = {};
|
|
108
|
+
}
|
|
90
109
|
return updated;
|
|
91
110
|
}
|
|
92
111
|
function phoneTemplatesDefaults(phoneTemplate) {
|
|
@@ -193,6 +212,31 @@ function eventStreamDefaults(eventStreams, config) {
|
|
|
193
212
|
};
|
|
194
213
|
});
|
|
195
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Sorts primitive arrays (e.g. `shields`, `admin_notification_frequency`) within the
|
|
217
|
+
* attackProtection subtree in place, so repeated exports produce deterministic output.
|
|
218
|
+
* These are unordered, string-typed enum sets — ordering is cosmetic. Object arrays are
|
|
219
|
+
* left as-is.
|
|
220
|
+
*/
|
|
221
|
+
function sortAttackProtectionArrays(attackProtection) {
|
|
222
|
+
const sortArraysDeep = (value) => {
|
|
223
|
+
if (Array.isArray(value)) {
|
|
224
|
+
const isPrimitiveArray = value.every((item) => item === null || typeof item !== 'object');
|
|
225
|
+
if (isPrimitiveArray) {
|
|
226
|
+
value.sort();
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
value.forEach(sortArraysDeep);
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (value && typeof value === 'object') {
|
|
234
|
+
Object.values(value).forEach(sortArraysDeep);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
sortArraysDeep(attackProtection);
|
|
238
|
+
return attackProtection;
|
|
239
|
+
}
|
|
196
240
|
function attackProtectionDefaults(attackProtection, config) {
|
|
197
241
|
if (isExportSecrets(config))
|
|
198
242
|
return attackProtection;
|
|
@@ -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, context.config);
|
|
68
|
+
const maskedAttackProtection = (0, defaults_1.sortAttackProtectionArrays)((0, defaults_1.attackProtectionDefaults)(attackProtection, context.config));
|
|
69
69
|
if (maskedAttackProtection.botDetection) {
|
|
70
70
|
(0, utils_1.dumpJSON)(files.botDetection, maskedAttackProtection.botDetection);
|
|
71
71
|
}
|
|
@@ -6,6 +6,7 @@ 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 logger_1 = __importDefault(require("../../../logger"));
|
|
9
10
|
const utils_1 = require("../../../utils");
|
|
10
11
|
function parse(context) {
|
|
11
12
|
const brandingDirectory = path_1.default.join(context.filePath, tools_1.constants.BRANDING_DIRECTORY);
|
|
@@ -31,7 +32,14 @@ function parse(context) {
|
|
|
31
32
|
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
32
33
|
});
|
|
33
34
|
const normalizedPathArray = (0, utils_1.nomalizedYAMLPath)(definition.body);
|
|
34
|
-
|
|
35
|
+
const resolvedBodyFile = path_1.default.resolve(brandingTemplatesFolder, ...normalizedPathArray);
|
|
36
|
+
const configRoot = path_1.default.resolve(context.filePath);
|
|
37
|
+
if (!resolvedBodyFile.startsWith(configRoot + path_1.default.sep)) {
|
|
38
|
+
logger_1.default.warn(`Branding template body path "${definition.body}" resolves to "${resolvedBodyFile}" which is outside the config directory "${configRoot}". ` +
|
|
39
|
+
`This will be blocked as an error in the next major release. ` +
|
|
40
|
+
`Move the file inside your config directory.`);
|
|
41
|
+
}
|
|
42
|
+
definition.body = (0, tools_1.loadFileAndReplaceKeywords)(resolvedBodyFile, {
|
|
35
43
|
mappings: context.mappings,
|
|
36
44
|
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
37
45
|
});
|
|
@@ -20,9 +20,15 @@ function parse(context) {
|
|
|
20
20
|
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
21
21
|
});
|
|
22
22
|
if (client.custom_login_page) {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const configRoot = path_1.default.resolve(context.filePath);
|
|
24
|
+
const resolvedLoginPage = path_1.default.resolve(clientsFolder, client.custom_login_page);
|
|
25
|
+
if ((0, utils_1.isFile)(resolvedLoginPage)) {
|
|
26
|
+
if (!resolvedLoginPage.startsWith(configRoot + path_1.default.sep)) {
|
|
27
|
+
logger_1.default.warn(`Path "${client.custom_login_page}" resolves to "${resolvedLoginPage}" which is outside the config directory "${configRoot}". ` +
|
|
28
|
+
`This will be blocked as an error in the next major release. ` +
|
|
29
|
+
`Move the file inside your config directory.`);
|
|
30
|
+
}
|
|
31
|
+
client.custom_login_page = (0, tools_1.loadFileAndReplaceKeywords)(resolvedLoginPage, {
|
|
26
32
|
mappings: context.mappings,
|
|
27
33
|
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
28
34
|
});
|
|
@@ -23,11 +23,17 @@ function parse(context) {
|
|
|
23
23
|
});
|
|
24
24
|
if (connection.strategy === 'email') {
|
|
25
25
|
(0, utils_1.ensureProp)(connection, 'options.email.body');
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const configRoot = path_1.default.resolve(context.filePath);
|
|
27
|
+
const resolvedHtmlFile = path_1.default.resolve(connectionsFolder, connection.options.email.body);
|
|
28
|
+
if (!(0, utils_1.isFile)(resolvedHtmlFile)) {
|
|
29
|
+
throw new Error(`Passwordless email template purportedly located at ${resolvedHtmlFile} does not exist for connection. Ensure the existence of this file to proceed with deployment.`);
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
+
if (!resolvedHtmlFile.startsWith(configRoot + path_1.default.sep)) {
|
|
32
|
+
logger_1.default.warn(`Path "${connection.options.email.body}" resolves to "${resolvedHtmlFile}" which is outside the config directory "${configRoot}". ` +
|
|
33
|
+
`This will be blocked as an error in the next major release. ` +
|
|
34
|
+
`Move the file inside your config directory.`);
|
|
35
|
+
}
|
|
36
|
+
connection.options.email.body = (0, tools_1.loadFileAndReplaceKeywords)(resolvedHtmlFile, {
|
|
31
37
|
mappings: context.mappings,
|
|
32
38
|
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
33
39
|
});
|
|
@@ -34,6 +34,15 @@ function parse(context) {
|
|
|
34
34
|
logger_1.default.warn(`Skipping email template file ${meta.body} as missing the corresponding '.json' file`);
|
|
35
35
|
return [];
|
|
36
36
|
}
|
|
37
|
+
if (meta.body !== undefined) {
|
|
38
|
+
const configRoot = path_1.default.resolve(context.filePath);
|
|
39
|
+
const resolvedTemplatePath = path_1.default.resolve(templateFilePath);
|
|
40
|
+
if (!resolvedTemplatePath.startsWith(configRoot + path_1.default.sep)) {
|
|
41
|
+
logger_1.default.warn(`Email template body path "${meta.body}" resolves to "${resolvedTemplatePath}" which is outside the config directory "${configRoot}". ` +
|
|
42
|
+
`This will be blocked as an error in the next major release. ` +
|
|
43
|
+
`Move the file inside your config directory.`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
37
46
|
return {
|
|
38
47
|
...meta,
|
|
39
48
|
body: (0, tools_1.loadFileAndReplaceKeywords)(templateFilePath, {
|
|
@@ -38,6 +38,7 @@ const rulesConfigs_1 = __importDefault(require("./rulesConfigs"));
|
|
|
38
38
|
const forms_1 = __importDefault(require("./forms"));
|
|
39
39
|
const flows_1 = __importDefault(require("./flows"));
|
|
40
40
|
const flowVaultConnections_1 = __importDefault(require("./flowVaultConnections"));
|
|
41
|
+
const networkACLKeys_1 = __importDefault(require("./networkACLKeys"));
|
|
41
42
|
const networkACLs_1 = __importDefault(require("./networkACLs"));
|
|
42
43
|
const userAttributeProfiles_1 = __importDefault(require("./userAttributeProfiles"));
|
|
43
44
|
const connectionProfiles_1 = __importDefault(require("./connectionProfiles"));
|
|
@@ -84,6 +85,7 @@ const directoryHandlers = {
|
|
|
84
85
|
flows: flows_1.default,
|
|
85
86
|
flowVaultConnections: flowVaultConnections_1.default,
|
|
86
87
|
selfServiceProfiles: selfServiceProfiles_1.default,
|
|
88
|
+
networkACLKeys: networkACLKeys_1.default,
|
|
87
89
|
networkACLs: networkACLs_1.default,
|
|
88
90
|
userAttributeProfiles: userAttributeProfiles_1.default,
|
|
89
91
|
connectionProfiles: connectionProfiles_1.default,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DirectoryHandler } from '.';
|
|
2
|
+
import { ParsedAsset } from '../../../types';
|
|
3
|
+
import { NetworkAclKey } from '../../../tools/auth0/handlers/networkACLKeys';
|
|
4
|
+
type ParsedNetworkACLKeys = ParsedAsset<'networkACLKeys', NetworkAclKey[]>;
|
|
5
|
+
declare const networkACLKeysHandler: DirectoryHandler<ParsedNetworkACLKeys>;
|
|
6
|
+
export default networkACLKeysHandler;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const path_1 = __importDefault(require("path"));
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const tools_1 = require("../../../tools");
|
|
9
|
+
const utils_1 = require("../../../utils");
|
|
10
|
+
const logger_1 = __importDefault(require("../../../logger"));
|
|
11
|
+
function parse(context) {
|
|
12
|
+
const networkACLKeysDirectory = path_1.default.join(context.filePath, tools_1.constants.NETWORK_ACL_KEYS_DIRECTORY);
|
|
13
|
+
if (!(0, utils_1.existsMustBeDir)(networkACLKeysDirectory))
|
|
14
|
+
return { networkACLKeys: null }; // Skip
|
|
15
|
+
const foundFiles = (0, utils_1.getFiles)(networkACLKeysDirectory, ['.json']);
|
|
16
|
+
const networkACLKeys = foundFiles
|
|
17
|
+
.map((f) => (0, utils_1.loadJSON)(f, {
|
|
18
|
+
mappings: context.mappings,
|
|
19
|
+
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
20
|
+
}))
|
|
21
|
+
.filter((p) => Object.keys(p).length > 0); // Filter out empty configs
|
|
22
|
+
return {
|
|
23
|
+
networkACLKeys,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async function dump(context) {
|
|
27
|
+
const { networkACLKeys } = context.assets;
|
|
28
|
+
if (!networkACLKeys)
|
|
29
|
+
return; // Skip, nothing to dump
|
|
30
|
+
if (Array.isArray(networkACLKeys) && networkACLKeys.length === 0) {
|
|
31
|
+
logger_1.default.info('No network ACL keys available, skipping dump');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const networkACLKeysDirectory = path_1.default.join(context.filePath, tools_1.constants.NETWORK_ACL_KEYS_DIRECTORY);
|
|
35
|
+
fs_extra_1.default.ensureDirSync(networkACLKeysDirectory);
|
|
36
|
+
// value is write-only — never returned by the API and must not be exported.
|
|
37
|
+
// created_at and updated_at are API-generated metadata; not needed in config.
|
|
38
|
+
const removeKeysFromOutput = ['value', 'created_at', 'updated_at'];
|
|
39
|
+
networkACLKeys.forEach((networkACLKey) => {
|
|
40
|
+
const out = { ...networkACLKey };
|
|
41
|
+
removeKeysFromOutput.forEach((key) => {
|
|
42
|
+
if (key in out)
|
|
43
|
+
delete out[key];
|
|
44
|
+
});
|
|
45
|
+
const fileName = (0, utils_1.sanitize)(networkACLKey.name);
|
|
46
|
+
const filePath = path_1.default.join(networkACLKeysDirectory, `${fileName}.json`);
|
|
47
|
+
(0, utils_1.dumpJSON)(filePath, out);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const networkACLKeysHandler = {
|
|
51
|
+
parse,
|
|
52
|
+
dump,
|
|
53
|
+
};
|
|
54
|
+
exports.default = networkACLKeysHandler;
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const path_1 = __importDefault(require("path"));
|
|
7
7
|
const fs_extra_1 = require("fs-extra");
|
|
8
8
|
const tools_1 = require("../../../tools");
|
|
9
|
+
const logger_1 = __importDefault(require("../../../logger"));
|
|
9
10
|
const utils_1 = require("../../../utils");
|
|
10
11
|
const getPromptsDirectory = (filePath) => path_1.default.join(filePath, tools_1.constants.PROMPTS_DIRECTORY);
|
|
11
12
|
const getPromptsSettingsFile = (promptsDirectory) => path_1.default.join(promptsDirectory, 'prompts.json');
|
|
@@ -46,9 +47,16 @@ function parse(context) {
|
|
|
46
47
|
const screensObject = screensArray[0];
|
|
47
48
|
acc[promptName] = Object.entries(screensObject).reduce((screenAcc, [screenName, items]) => {
|
|
48
49
|
screenAcc[screenName] = items.reduce((insertionAcc, { name, template }) => {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
const configRoot = path_1.default.resolve(context.filePath);
|
|
51
|
+
const resolvedTemplatePath = path_1.default.resolve(promptsDirectory, template);
|
|
52
|
+
if ((0, utils_1.isFile)(resolvedTemplatePath) &&
|
|
53
|
+
!resolvedTemplatePath.startsWith(configRoot + path_1.default.sep)) {
|
|
54
|
+
logger_1.default.warn(`Prompt partial template "${template}" resolves to "${resolvedTemplatePath}" which is outside the config directory "${configRoot}". ` +
|
|
55
|
+
`This will be blocked as an error in the next major release. ` +
|
|
56
|
+
`Move the file inside your config directory.`);
|
|
57
|
+
}
|
|
58
|
+
insertionAcc[name] = (0, utils_1.isFile)(resolvedTemplatePath)
|
|
59
|
+
? (0, tools_1.loadFileAndReplaceKeywords)(resolvedTemplatePath, {
|
|
52
60
|
mappings: context.mappings,
|
|
53
61
|
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
54
62
|
}).trim()
|
|
@@ -27,7 +27,7 @@ async function dump(context) {
|
|
|
27
27
|
}
|
|
28
28
|
const maskedAttackProtection = (0, defaults_1.attackProtectionDefaults)(attackProtectionConfig, context.config);
|
|
29
29
|
return {
|
|
30
|
-
attackProtection: maskedAttackProtection,
|
|
30
|
+
attackProtection: (0, defaults_1.sortAttackProtectionArrays)(maskedAttackProtection),
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
const attackProtectionHandler = {
|
|
@@ -18,9 +18,16 @@ async function parse(context) {
|
|
|
18
18
|
const parsedTemplates = templates.map((templateDefinition) => {
|
|
19
19
|
const normalizedPathArray = (0, utils_1.nomalizedYAMLPath)(templateDefinition.body);
|
|
20
20
|
const markupFile = path_1.default.join(context.basePath, ...normalizedPathArray);
|
|
21
|
+
const configRoot = path_1.default.resolve(context.basePath);
|
|
22
|
+
const resolvedMarkupFile = path_1.default.resolve(markupFile);
|
|
23
|
+
if (!resolvedMarkupFile.startsWith(configRoot + path_1.default.sep)) {
|
|
24
|
+
logger_1.default.warn(`Branding template body path "${templateDefinition.body}" resolves to "${resolvedMarkupFile}" which is outside the config directory "${configRoot}". ` +
|
|
25
|
+
`This will be blocked as an error in the next major release. ` +
|
|
26
|
+
`Move the file inside your config directory.`);
|
|
27
|
+
}
|
|
21
28
|
return {
|
|
22
29
|
template: templateDefinition.template,
|
|
23
|
-
body: (0, tools_1.loadFileAndReplaceKeywords)(
|
|
30
|
+
body: (0, tools_1.loadFileAndReplaceKeywords)(resolvedMarkupFile, {
|
|
24
31
|
mappings: context.mappings,
|
|
25
32
|
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
26
33
|
}),
|
|
@@ -21,7 +21,10 @@ async function parse(context) {
|
|
|
21
21
|
if (client.custom_login_page) {
|
|
22
22
|
const htmlFileName = path_1.default.join(clientsFolder, client.custom_login_page);
|
|
23
23
|
if ((0, utils_1.isFile)(htmlFileName)) {
|
|
24
|
-
|
|
24
|
+
// Pass a path relative to basePath so context.loadFile() resolves it correctly.
|
|
25
|
+
// Passing htmlFileName (which already includes basePath) would cause double resolution
|
|
26
|
+
// when basePath is a relative path (e.g. when AUTH0_INPUT_FILE is a relative path).
|
|
27
|
+
client.custom_login_page = context.loadFile(path_1.default.join(tools_1.constants.CLIENTS_DIRECTORY, client.custom_login_page));
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
30
|
return client;
|
|
@@ -26,7 +26,9 @@ async function parse(context) {
|
|
|
26
26
|
logger_1.default.error(missingTemplateErrorMessage);
|
|
27
27
|
throw new Error(missingTemplateErrorMessage);
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
// Pass a path relative to basePath so context.loadFile() resolves it correctly.
|
|
30
|
+
// Passing htmlFileName (which already includes basePath) would cause double resolution.
|
|
31
|
+
connection.options.email.body = context.loadFile(path_1.default.join(tools_1.constants.CONNECTIONS_DIRECTORY, connection.options.email.body));
|
|
30
32
|
}
|
|
31
33
|
return connection;
|
|
32
34
|
}),
|
|
@@ -14,6 +14,13 @@ async function parse(context) {
|
|
|
14
14
|
return { flows: null };
|
|
15
15
|
const parsedFlows = flows.map((flow) => {
|
|
16
16
|
const flowFile = path_1.default.join(context.basePath, flow.body);
|
|
17
|
+
const configRoot = path_1.default.resolve(context.basePath);
|
|
18
|
+
const resolvedFlowFile = path_1.default.resolve(flowFile);
|
|
19
|
+
if (!resolvedFlowFile.startsWith(configRoot + path_1.default.sep)) {
|
|
20
|
+
logger_1.default.warn(`Flow body file "${flow.body}" resolves to "${resolvedFlowFile}" which is outside the config directory "${configRoot}". ` +
|
|
21
|
+
`This will be blocked as an error in the next major release. ` +
|
|
22
|
+
`Move the file inside your config directory.`);
|
|
23
|
+
}
|
|
17
24
|
const parsedFlowBody = (0, utils_1.loadJSON)(flowFile, {
|
|
18
25
|
mappings: context.mappings,
|
|
19
26
|
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
@@ -14,6 +14,13 @@ async function parse(context) {
|
|
|
14
14
|
return { forms: null };
|
|
15
15
|
const parsedForms = forms.map((form) => {
|
|
16
16
|
const formFile = path_1.default.join(context.basePath, form.body);
|
|
17
|
+
const configRoot = path_1.default.resolve(context.basePath);
|
|
18
|
+
const resolvedFormFile = path_1.default.resolve(formFile);
|
|
19
|
+
if (!resolvedFormFile.startsWith(configRoot + path_1.default.sep)) {
|
|
20
|
+
logger_1.default.warn(`Form body file "${form.body}" resolves to "${resolvedFormFile}" which is outside the config directory "${configRoot}". ` +
|
|
21
|
+
`This will be blocked as an error in the next major release. ` +
|
|
22
|
+
`Move the file inside your config directory.`);
|
|
23
|
+
}
|
|
17
24
|
const parsedFormBody = (0, utils_1.loadJSON)(formFile, {
|
|
18
25
|
mappings: context.mappings,
|
|
19
26
|
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
@@ -38,6 +38,7 @@ const hooks_1 = __importDefault(require("./hooks"));
|
|
|
38
38
|
const forms_1 = __importDefault(require("./forms"));
|
|
39
39
|
const flows_1 = __importDefault(require("./flows"));
|
|
40
40
|
const flowVaultConnections_1 = __importDefault(require("./flowVaultConnections"));
|
|
41
|
+
const networkACLKeys_1 = __importDefault(require("./networkACLKeys"));
|
|
41
42
|
const networkACLs_1 = __importDefault(require("./networkACLs"));
|
|
42
43
|
const userAttributeProfiles_1 = __importDefault(require("./userAttributeProfiles"));
|
|
43
44
|
const connectionProfiles_1 = __importDefault(require("./connectionProfiles"));
|
|
@@ -84,6 +85,7 @@ const yamlHandlers = {
|
|
|
84
85
|
flows: flows_1.default,
|
|
85
86
|
flowVaultConnections: flowVaultConnections_1.default,
|
|
86
87
|
selfServiceProfiles: selfServiceProfiles_1.default,
|
|
88
|
+
networkACLKeys: networkACLKeys_1.default,
|
|
87
89
|
networkACLs: networkACLs_1.default,
|
|
88
90
|
userAttributeProfiles: userAttributeProfiles_1.default,
|
|
89
91
|
connectionProfiles: connectionProfiles_1.default,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { YAMLHandler } from '.';
|
|
2
|
+
import { ParsedAsset } from '../../../types';
|
|
3
|
+
import { NetworkAclKey } from '../../../tools/auth0/handlers/networkACLKeys';
|
|
4
|
+
type ParsedNetworkACLKeys = ParsedAsset<'networkACLKeys', NetworkAclKey[]>;
|
|
5
|
+
declare const networkACLKeysHandler: YAMLHandler<ParsedNetworkACLKeys>;
|
|
6
|
+
export default networkACLKeysHandler;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const logger_1 = __importDefault(require("../../../logger"));
|
|
7
|
+
async function parse(context) {
|
|
8
|
+
const { networkACLKeys } = context.assets;
|
|
9
|
+
if (!networkACLKeys)
|
|
10
|
+
return { networkACLKeys: null };
|
|
11
|
+
return {
|
|
12
|
+
networkACLKeys,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
async function dump(context) {
|
|
16
|
+
let { networkACLKeys } = context.assets;
|
|
17
|
+
if (!networkACLKeys)
|
|
18
|
+
return { networkACLKeys: null };
|
|
19
|
+
if (Array.isArray(networkACLKeys) && networkACLKeys.length === 0) {
|
|
20
|
+
logger_1.default.info('No network ACL keys available, skipping dump');
|
|
21
|
+
return { networkACLKeys: null };
|
|
22
|
+
}
|
|
23
|
+
// value is write-only — never returned by the API and must not be exported.
|
|
24
|
+
// created_at and updated_at are API-generated metadata; not needed in config.
|
|
25
|
+
const removeKeysFromOutput = ['value', 'created_at', 'updated_at'];
|
|
26
|
+
networkACLKeys = networkACLKeys.map((key) => {
|
|
27
|
+
const out = { ...key };
|
|
28
|
+
removeKeysFromOutput.forEach((k) => {
|
|
29
|
+
if (k in out)
|
|
30
|
+
delete out[k];
|
|
31
|
+
});
|
|
32
|
+
return out;
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
networkACLKeys,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const networkACLKeysHandler = {
|
|
39
|
+
parse,
|
|
40
|
+
dump,
|
|
41
|
+
};
|
|
42
|
+
exports.default = networkACLKeysHandler;
|
|
@@ -19,7 +19,13 @@ const loadScreenRenderers = (context, screenRenderArray) => {
|
|
|
19
19
|
Object.entries(screens).forEach(([, fileName]) => {
|
|
20
20
|
const filePath = fileName;
|
|
21
21
|
try {
|
|
22
|
-
const
|
|
22
|
+
const configRoot = path_1.default.resolve(context.basePath);
|
|
23
|
+
const rendererFile = path_1.default.resolve(context.basePath, filePath);
|
|
24
|
+
if (!rendererFile.startsWith(configRoot + path_1.default.sep)) {
|
|
25
|
+
logger_1.default.warn(`Screen renderer file "${filePath}" resolves to "${rendererFile}" which is outside the config directory "${configRoot}". ` +
|
|
26
|
+
`This will be blocked as an error in the next major release. ` +
|
|
27
|
+
`Move the file inside your config directory.`);
|
|
28
|
+
}
|
|
23
29
|
const rendererData = (0, utils_1.loadJSON)(rendererFile, {
|
|
24
30
|
mappings: context.mappings,
|
|
25
31
|
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
@@ -10,6 +10,6 @@ export declare const preserveKeywords: ({ localAssets, remoteAssets, keywordMapp
|
|
|
10
10
|
remoteAssets: object;
|
|
11
11
|
keywordMappings: KeywordMappings;
|
|
12
12
|
auth0Handlers: (Pick<APIHandler, "id" | "type"> & {
|
|
13
|
-
identifiers
|
|
13
|
+
identifiers?: (string | string[])[];
|
|
14
14
|
})[];
|
|
15
15
|
}) => object;
|
|
@@ -164,7 +164,8 @@ const preserveKeywords = ({ localAssets, remoteAssets, keywordMappings, auth0Han
|
|
|
164
164
|
if (Object.keys(keywordMappings).length === 0)
|
|
165
165
|
return remoteAssets;
|
|
166
166
|
const resourceSpecificIdentifiers = auth0Handlers.reduce((acc, handler) => {
|
|
167
|
-
|
|
167
|
+
// Standalone handlers (e.g. clientAuthCredentials) don't extend APIHandler and have no identifiers.
|
|
168
|
+
acc[handler.type] = (handler.identifiers ?? []).flat();
|
|
168
169
|
return acc;
|
|
169
170
|
}, {});
|
|
170
171
|
const addresses = (0, exports.getPreservableFieldsFromAssets)(localAssets, keywordMappings, resourceSpecificIdentifiers, '');
|