auth0-deploy-cli 9.0.0-beta.2 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -357
- package/CHANGELOG.md +185 -10
- package/CLAUDE.md +138 -2
- package/README.md +1 -1
- package/lib/context/defaults.d.ts +7 -0
- package/lib/context/defaults.js +74 -30
- package/lib/context/directory/handlers/actionModules.js +1 -4
- package/lib/context/directory/handlers/actions.js +1 -4
- package/lib/context/directory/handlers/attackProtection.js +11 -1
- package/lib/context/directory/handlers/branding.js +4 -1
- package/lib/context/directory/handlers/clientAuthCredentials.d.ts +5 -0
- package/lib/context/directory/handlers/clientAuthCredentials.js +13 -0
- package/lib/context/directory/handlers/clientGrants.js +78 -24
- package/lib/context/directory/handlers/clients.js +20 -3
- package/lib/context/directory/handlers/connections.js +19 -5
- package/lib/context/directory/handlers/databases.js +1 -4
- package/lib/context/directory/handlers/emailTemplates.js +5 -0
- package/lib/context/directory/handlers/guardianEmailFactorSettings.d.ts +5 -0
- package/lib/context/directory/handlers/guardianEmailFactorSettings.js +38 -0
- package/lib/context/directory/handlers/guardianPhoneFactorSettings.d.ts +5 -0
- package/lib/context/directory/handlers/guardianPhoneFactorSettings.js +38 -0
- package/lib/context/directory/handlers/guardianSettings.d.ts +5 -0
- package/lib/context/directory/handlers/guardianSettings.js +38 -0
- package/lib/context/directory/handlers/hooks.js +1 -4
- package/lib/context/directory/handlers/index.js +12 -0
- package/lib/context/directory/handlers/networkACLKeys.d.ts +6 -0
- package/lib/context/directory/handlers/networkACLKeys.js +54 -0
- package/lib/context/directory/handlers/prompts.js +7 -3
- package/lib/context/directory/handlers/rateLimitPolicies.d.ts +6 -0
- package/lib/context/directory/handlers/rateLimitPolicies.js +53 -0
- package/lib/context/directory/handlers/rules.js +1 -4
- package/lib/context/directory/index.js +1 -4
- package/lib/context/index.js +32 -31
- package/lib/context/yaml/handlers/attackProtection.js +5 -2
- package/lib/context/yaml/handlers/branding.js +4 -1
- package/lib/context/yaml/handlers/clientAuthCredentials.d.ts +5 -0
- package/lib/context/yaml/handlers/clientAuthCredentials.js +13 -0
- package/lib/context/yaml/handlers/clients.js +19 -1
- package/lib/context/yaml/handlers/connections.js +8 -1
- package/lib/context/yaml/handlers/flows.js +3 -0
- package/lib/context/yaml/handlers/forms.js +3 -0
- package/lib/context/yaml/handlers/guardianEmailFactorSettings.d.ts +5 -0
- package/lib/context/yaml/handlers/guardianEmailFactorSettings.js +15 -0
- package/lib/context/yaml/handlers/guardianPhoneFactorSettings.d.ts +5 -0
- package/lib/context/yaml/handlers/guardianPhoneFactorSettings.js +15 -0
- package/lib/context/yaml/handlers/guardianSettings.d.ts +5 -0
- package/lib/context/yaml/handlers/guardianSettings.js +15 -0
- package/lib/context/yaml/handlers/index.js +12 -0
- package/lib/context/yaml/handlers/networkACLKeys.d.ts +6 -0
- package/lib/context/yaml/handlers/networkACLKeys.js +42 -0
- package/lib/context/yaml/handlers/prompts.js +3 -1
- package/lib/context/yaml/handlers/rateLimitPolicies.d.ts +6 -0
- package/lib/context/yaml/handlers/rateLimitPolicies.js +27 -0
- package/lib/context/yaml/index.js +85 -7
- package/lib/keywordPreservation.d.ts +1 -1
- package/lib/keywordPreservation.js +2 -1
- package/lib/readonly.js +5 -0
- package/lib/tools/auth0/handlers/attackProtection.d.ts +12 -0
- package/lib/tools/auth0/handlers/attackProtection.js +66 -6
- package/lib/tools/auth0/handlers/clientAuthCredentials.d.ts +27 -0
- package/lib/tools/auth0/handlers/clientAuthCredentials.js +234 -0
- package/lib/tools/auth0/handlers/clientAuthCredentialsPre.d.ts +38 -0
- package/lib/tools/auth0/handlers/clientAuthCredentialsPre.js +123 -0
- package/lib/tools/auth0/handlers/clientGrants.d.ts +1 -1
- package/lib/tools/auth0/handlers/clients.d.ts +106 -0
- package/lib/tools/auth0/handlers/clients.js +207 -3
- package/lib/tools/auth0/handlers/connectionProfiles.d.ts +27 -0
- package/lib/tools/auth0/handlers/connectionProfiles.js +30 -0
- package/lib/tools/auth0/handlers/connections.d.ts +34 -6
- package/lib/tools/auth0/handlers/connections.js +75 -11
- package/lib/tools/auth0/handlers/databases.d.ts +28 -0
- package/lib/tools/auth0/handlers/databases.js +58 -24
- package/lib/tools/auth0/handlers/default.d.ts +23 -0
- package/lib/tools/auth0/handlers/default.js +47 -29
- package/lib/tools/auth0/handlers/eventStreams.js +10 -2
- package/lib/tools/auth0/handlers/guardianEmailFactorSettings.d.ts +22 -0
- package/lib/tools/auth0/handlers/guardianEmailFactorSettings.js +77 -0
- package/lib/tools/auth0/handlers/guardianFactorTemplates.js +13 -4
- package/lib/tools/auth0/handlers/guardianPhoneFactorMessageTypes.js +11 -9
- package/lib/tools/auth0/handlers/guardianPhoneFactorSelectedProvider.js +11 -9
- package/lib/tools/auth0/handlers/guardianPhoneFactorSettings.d.ts +22 -0
- package/lib/tools/auth0/handlers/guardianPhoneFactorSettings.js +77 -0
- package/lib/tools/auth0/handlers/guardianPolicies.js +11 -1
- package/lib/tools/auth0/handlers/guardianSettings.d.ts +30 -0
- package/lib/tools/auth0/handlers/guardianSettings.js +85 -0
- package/lib/tools/auth0/handlers/hooks.js +1 -1
- package/lib/tools/auth0/handlers/index.js +16 -0
- package/lib/tools/auth0/handlers/networkACLKeys.d.ts +45 -0
- package/lib/tools/auth0/handlers/networkACLKeys.js +177 -0
- package/lib/tools/auth0/handlers/networkACLs.d.ts +132 -2
- package/lib/tools/auth0/handlers/networkACLs.js +100 -16
- package/lib/tools/auth0/handlers/organizations.d.ts +33 -1
- package/lib/tools/auth0/handlers/organizations.js +177 -19
- package/lib/tools/auth0/handlers/phoneProvider.js +10 -2
- package/lib/tools/auth0/handlers/phoneTemplates.d.ts +4 -1
- package/lib/tools/auth0/handlers/phoneTemplates.js +67 -10
- package/lib/tools/auth0/handlers/prompts.d.ts +17 -0
- package/lib/tools/auth0/handlers/prompts.js +47 -14
- package/lib/tools/auth0/handlers/rateLimitPolicies.d.ts +81 -0
- package/lib/tools/auth0/handlers/rateLimitPolicies.js +203 -0
- package/lib/tools/auth0/handlers/resourceServers.d.ts +48 -0
- package/lib/tools/auth0/handlers/resourceServers.js +56 -2
- package/lib/tools/auth0/handlers/riskAssessment.d.ts +1 -1
- package/lib/tools/auth0/handlers/riskAssessment.js +19 -1
- package/lib/tools/auth0/handlers/roles.d.ts +4 -0
- package/lib/tools/auth0/handlers/roles.js +27 -9
- package/lib/tools/auth0/handlers/rules.js +19 -7
- package/lib/tools/auth0/handlers/scimHandler.js +8 -3
- package/lib/tools/auth0/handlers/tenant.d.ts +45 -0
- package/lib/tools/auth0/handlers/tenant.js +48 -0
- package/lib/tools/auth0/handlers/themes.d.ts +37 -1
- package/lib/tools/auth0/handlers/themes.js +64 -0
- package/lib/tools/auth0/handlers/tokenExchangeProfiles.js +3 -1
- package/lib/tools/auth0/index.js +12 -6
- package/lib/tools/calculateDryRunChanges.js +12 -8
- package/lib/tools/constants.d.ts +3 -0
- package/lib/tools/constants.js +8 -0
- package/lib/tools/utils.d.ts +3 -0
- package/lib/tools/utils.js +41 -2
- package/lib/types.d.ts +10 -1
- package/lib/utils.d.ts +6 -0
- package/lib/utils.js +17 -2
- package/package.json +2 -2
- package/references/code-style.md +133 -0
- package/references/commands.md +98 -0
- package/references/docs-update.md +34 -0
- package/references/git-workflow.md +135 -0
- package/references/pitfalls.md +62 -0
- package/references/testing.md +118 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { YAMLHandler } from '.';
|
|
2
|
+
import { Asset, ParsedAsset } from '../../../types';
|
|
3
|
+
type ParsedGuardianPhoneFactorSettings = ParsedAsset<'guardianPhoneFactorSettings', Asset>;
|
|
4
|
+
declare const guardianPhoneFactorSettingsHandler: YAMLHandler<ParsedGuardianPhoneFactorSettings>;
|
|
5
|
+
export default guardianPhoneFactorSettingsHandler;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
async function parseAndDump(context) {
|
|
4
|
+
const { guardianPhoneFactorSettings } = context.assets;
|
|
5
|
+
if (!guardianPhoneFactorSettings)
|
|
6
|
+
return { guardianPhoneFactorSettings: null };
|
|
7
|
+
return {
|
|
8
|
+
guardianPhoneFactorSettings,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const guardianPhoneFactorSettingsHandler = {
|
|
12
|
+
parse: parseAndDump,
|
|
13
|
+
dump: parseAndDump,
|
|
14
|
+
};
|
|
15
|
+
exports.default = guardianPhoneFactorSettingsHandler;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { YAMLHandler } from '.';
|
|
2
|
+
import { Asset, ParsedAsset } from '../../../types';
|
|
3
|
+
type ParsedGuardianSettings = ParsedAsset<'guardianSettings', Asset>;
|
|
4
|
+
declare const guardianSettingsHandler: YAMLHandler<ParsedGuardianSettings>;
|
|
5
|
+
export default guardianSettingsHandler;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
async function parseAndDump(context) {
|
|
4
|
+
const { guardianSettings } = context.assets;
|
|
5
|
+
if (!guardianSettings)
|
|
6
|
+
return { guardianSettings: null };
|
|
7
|
+
return {
|
|
8
|
+
guardianSettings,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const guardianSettingsHandler = {
|
|
12
|
+
parse: parseAndDump,
|
|
13
|
+
dump: parseAndDump,
|
|
14
|
+
};
|
|
15
|
+
exports.default = guardianSettingsHandler;
|
|
@@ -17,6 +17,9 @@ const guardianFactorProviders_1 = __importDefault(require("./guardianFactorProvi
|
|
|
17
17
|
const guardianFactorTemplates_1 = __importDefault(require("./guardianFactorTemplates"));
|
|
18
18
|
const guardianPhoneFactorMessageTypes_1 = __importDefault(require("./guardianPhoneFactorMessageTypes"));
|
|
19
19
|
const guardianPhoneFactorSelectedProvider_1 = __importDefault(require("./guardianPhoneFactorSelectedProvider"));
|
|
20
|
+
const guardianPhoneFactorSettings_1 = __importDefault(require("./guardianPhoneFactorSettings"));
|
|
21
|
+
const guardianEmailFactorSettings_1 = __importDefault(require("./guardianEmailFactorSettings"));
|
|
22
|
+
const guardianSettings_1 = __importDefault(require("./guardianSettings"));
|
|
20
23
|
const guardianPolicies_1 = __importDefault(require("./guardianPolicies"));
|
|
21
24
|
const roles_1 = __importDefault(require("./roles"));
|
|
22
25
|
const organizations_1 = __importDefault(require("./organizations"));
|
|
@@ -38,12 +41,15 @@ const hooks_1 = __importDefault(require("./hooks"));
|
|
|
38
41
|
const forms_1 = __importDefault(require("./forms"));
|
|
39
42
|
const flows_1 = __importDefault(require("./flows"));
|
|
40
43
|
const flowVaultConnections_1 = __importDefault(require("./flowVaultConnections"));
|
|
44
|
+
const networkACLKeys_1 = __importDefault(require("./networkACLKeys"));
|
|
41
45
|
const networkACLs_1 = __importDefault(require("./networkACLs"));
|
|
42
46
|
const userAttributeProfiles_1 = __importDefault(require("./userAttributeProfiles"));
|
|
43
47
|
const connectionProfiles_1 = __importDefault(require("./connectionProfiles"));
|
|
44
48
|
const tokenExchangeProfiles_1 = __importDefault(require("./tokenExchangeProfiles"));
|
|
45
49
|
const supplementalSignals_1 = __importDefault(require("./supplementalSignals"));
|
|
50
|
+
const rateLimitPolicies_1 = __importDefault(require("./rateLimitPolicies"));
|
|
46
51
|
const eventStreams_1 = __importDefault(require("./eventStreams"));
|
|
52
|
+
const clientAuthCredentials_1 = __importDefault(require("./clientAuthCredentials"));
|
|
47
53
|
const selfServiceProfiles_1 = __importDefault(require("./selfServiceProfiles"));
|
|
48
54
|
const yamlHandlers = {
|
|
49
55
|
rules: rules_1.default,
|
|
@@ -64,6 +70,9 @@ const yamlHandlers = {
|
|
|
64
70
|
roles: roles_1.default,
|
|
65
71
|
guardianPhoneFactorMessageTypes: guardianPhoneFactorMessageTypes_1.default,
|
|
66
72
|
guardianPhoneFactorSelectedProvider: guardianPhoneFactorSelectedProvider_1.default,
|
|
73
|
+
guardianPhoneFactorSettings: guardianPhoneFactorSettings_1.default,
|
|
74
|
+
guardianEmailFactorSettings: guardianEmailFactorSettings_1.default,
|
|
75
|
+
guardianSettings: guardianSettings_1.default,
|
|
67
76
|
guardianPolicies: guardianPolicies_1.default,
|
|
68
77
|
actions: actions_1.default,
|
|
69
78
|
actionModules: actionModules_1.default,
|
|
@@ -82,11 +91,14 @@ const yamlHandlers = {
|
|
|
82
91
|
flows: flows_1.default,
|
|
83
92
|
flowVaultConnections: flowVaultConnections_1.default,
|
|
84
93
|
selfServiceProfiles: selfServiceProfiles_1.default,
|
|
94
|
+
networkACLKeys: networkACLKeys_1.default,
|
|
85
95
|
networkACLs: networkACLs_1.default,
|
|
86
96
|
userAttributeProfiles: userAttributeProfiles_1.default,
|
|
87
97
|
connectionProfiles: connectionProfiles_1.default,
|
|
88
98
|
tokenExchangeProfiles: tokenExchangeProfiles_1.default,
|
|
89
99
|
supplementalSignals: supplementalSignals_1.default,
|
|
100
|
+
rateLimitPolicies: rateLimitPolicies_1.default,
|
|
90
101
|
eventStreams: eventStreams_1.default,
|
|
102
|
+
clientAuthCredentials: clientAuthCredentials_1.default,
|
|
91
103
|
};
|
|
92
104
|
exports.default = yamlHandlers;
|
|
@@ -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;
|
|
@@ -18,8 +18,10 @@ const loadScreenRenderers = (context, screenRenderArray) => {
|
|
|
18
18
|
const screens = promptEntry[prompt];
|
|
19
19
|
Object.entries(screens).forEach(([, fileName]) => {
|
|
20
20
|
const filePath = fileName;
|
|
21
|
+
const configRoot = path_1.default.resolve(context.basePath);
|
|
22
|
+
const rendererFile = path_1.default.resolve(context.basePath, filePath);
|
|
23
|
+
(0, utils_1.assertInsideConfigRoot)(filePath, rendererFile, configRoot);
|
|
21
24
|
try {
|
|
22
|
-
const rendererFile = path_1.default.join(context.basePath, filePath);
|
|
23
25
|
const rendererData = (0, utils_1.loadJSON)(rendererFile, {
|
|
24
26
|
mappings: context.mappings,
|
|
25
27
|
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { YAMLHandler } from '.';
|
|
2
|
+
import { ParsedAsset } from '../../../types';
|
|
3
|
+
import { RateLimitPolicy } from '../../../tools/auth0/handlers/rateLimitPolicies';
|
|
4
|
+
type ParsedRateLimitPolicies = ParsedAsset<'rateLimitPolicies', RateLimitPolicy[]>;
|
|
5
|
+
declare const rateLimitPoliciesHandler: YAMLHandler<ParsedRateLimitPolicies>;
|
|
6
|
+
export default rateLimitPoliciesHandler;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
async function parse(context) {
|
|
4
|
+
const { rateLimitPolicies } = context.assets;
|
|
5
|
+
if (!rateLimitPolicies)
|
|
6
|
+
return { rateLimitPolicies: null };
|
|
7
|
+
return { rateLimitPolicies };
|
|
8
|
+
}
|
|
9
|
+
async function dump(context) {
|
|
10
|
+
const { rateLimitPolicies } = context.assets;
|
|
11
|
+
if (!rateLimitPolicies)
|
|
12
|
+
return { rateLimitPolicies: null };
|
|
13
|
+
const removeKeysFromOutput = ['id', 'created_at', 'updated_at'];
|
|
14
|
+
const cleaned = rateLimitPolicies.map((policy) => {
|
|
15
|
+
const policyToWrite = { ...policy };
|
|
16
|
+
removeKeysFromOutput.forEach((key) => {
|
|
17
|
+
delete policyToWrite[key];
|
|
18
|
+
});
|
|
19
|
+
return policyToWrite;
|
|
20
|
+
});
|
|
21
|
+
return { rateLimitPolicies: cleaned };
|
|
22
|
+
}
|
|
23
|
+
const rateLimitPoliciesHandler = {
|
|
24
|
+
parse,
|
|
25
|
+
dump,
|
|
26
|
+
};
|
|
27
|
+
exports.default = rateLimitPoliciesHandler;
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
7
7
|
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const lodash_1 = require("lodash");
|
|
9
10
|
const tools_1 = require("../../tools");
|
|
10
11
|
const client_1 = __importDefault(require("../../tools/auth0/client"));
|
|
11
12
|
const logger_1 = __importDefault(require("../../logger"));
|
|
@@ -14,6 +15,83 @@ const handlers_1 = __importDefault(require("./handlers"));
|
|
|
14
15
|
const readonly_1 = __importDefault(require("../../readonly"));
|
|
15
16
|
const __1 = require("..");
|
|
16
17
|
const keywordPreservation_1 = require("../../keywordPreservation");
|
|
18
|
+
// Symbol marker so a literal `__include` key in user YAML isn't read as a directive
|
|
19
|
+
const INCLUDE = Symbol('include');
|
|
20
|
+
// Custom YAML type for file includes
|
|
21
|
+
const includeType = new js_yaml_1.default.Type('!include', {
|
|
22
|
+
kind: 'scalar',
|
|
23
|
+
resolve: (data) => typeof data === 'string',
|
|
24
|
+
construct: (data) => {
|
|
25
|
+
// This will be handled during the actual loading process
|
|
26
|
+
return { [INCLUDE]: data };
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
const schema = js_yaml_1.default.DEFAULT_SCHEMA.extend([includeType]);
|
|
30
|
+
function prepareYamlContent(content, mappings, disableKeywordReplacement) {
|
|
31
|
+
return disableKeywordReplacement
|
|
32
|
+
? (0, tools_1.wrapArrayReplaceMarkersInQuotes)(content, mappings)
|
|
33
|
+
: (0, tools_1.keywordReplace)(content, mappings);
|
|
34
|
+
}
|
|
35
|
+
function parseYaml(content) {
|
|
36
|
+
return js_yaml_1.default.load(content, { schema });
|
|
37
|
+
}
|
|
38
|
+
// Resolves !include directives. Keyword handling is applied by the caller before parse.
|
|
39
|
+
// configRoot is threaded unchanged so chained `../` includes can't escape it.
|
|
40
|
+
function resolveIncludes(obj, basePath, configRoot, loadIncludedYaml, visitedFiles = new Set()) {
|
|
41
|
+
if (Array.isArray(obj)) {
|
|
42
|
+
return obj.map((item) => resolveIncludes(item, basePath, configRoot, loadIncludedYaml, visitedFiles));
|
|
43
|
+
}
|
|
44
|
+
if (obj && typeof obj === 'object') {
|
|
45
|
+
const includePath = obj[INCLUDE];
|
|
46
|
+
if (includePath !== undefined) {
|
|
47
|
+
const filePath = path_1.default.resolve(basePath, includePath);
|
|
48
|
+
(0, utils_1.assertInsideConfigRoot)(includePath, filePath, configRoot);
|
|
49
|
+
if (visitedFiles.has(filePath)) {
|
|
50
|
+
throw new Error(`Circular include detected: ${filePath}`);
|
|
51
|
+
}
|
|
52
|
+
let stat;
|
|
53
|
+
try {
|
|
54
|
+
stat = fs_extra_1.default.statSync(filePath);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
if (err.code === 'ENOENT') {
|
|
58
|
+
throw new Error(`Include file not found: ${filePath}`);
|
|
59
|
+
}
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
if (stat.isDirectory()) {
|
|
63
|
+
throw new Error(`Include path is a directory, expected a file: ${filePath}`);
|
|
64
|
+
}
|
|
65
|
+
// visitedFiles backtracks (add before, delete after), so one shared set is enough
|
|
66
|
+
visitedFiles.add(filePath);
|
|
67
|
+
const result = resolveIncludes(loadIncludedYaml(filePath), path_1.default.dirname(filePath), configRoot, loadIncludedYaml, visitedFiles);
|
|
68
|
+
visitedFiles.delete(filePath);
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
// Only rebuild plain objects; pass others (e.g. a Date from a YAML timestamp) through untouched
|
|
72
|
+
if (!(0, lodash_1.isPlainObject)(obj)) {
|
|
73
|
+
return obj;
|
|
74
|
+
}
|
|
75
|
+
const result = {};
|
|
76
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
77
|
+
const resolved = resolveIncludes(value, basePath, configRoot, loadIncludedYaml, visitedFiles);
|
|
78
|
+
// Define `__proto__` as an own property so an included file can't pollute the prototype
|
|
79
|
+
if (key === '__proto__') {
|
|
80
|
+
Object.defineProperty(result, key, {
|
|
81
|
+
value: resolved,
|
|
82
|
+
enumerable: true,
|
|
83
|
+
writable: true,
|
|
84
|
+
configurable: true,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
result[key] = resolved;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
return obj;
|
|
94
|
+
}
|
|
17
95
|
class YAMLContext {
|
|
18
96
|
constructor(config, mgmtClient) {
|
|
19
97
|
this.configFile = config.AUTH0_INPUT_FILE;
|
|
@@ -45,10 +123,7 @@ class YAMLContext {
|
|
|
45
123
|
loadFile(f) {
|
|
46
124
|
const configRoot = path_1.default.resolve(this.basePath);
|
|
47
125
|
const toLoad = path_1.default.resolve(this.basePath, f.replace(/\\/g, '/'));
|
|
48
|
-
|
|
49
|
-
throw new Error(`Path "${f}" resolves to "${toLoad}" which is outside the config directory "${configRoot}". ` +
|
|
50
|
-
`Move the file inside your config directory.`);
|
|
51
|
-
}
|
|
126
|
+
(0, utils_1.assertInsideConfigRoot)(f, toLoad, configRoot);
|
|
52
127
|
return (0, tools_1.loadFileAndReplaceKeywords)(toLoad, {
|
|
53
128
|
mappings: this.mappings,
|
|
54
129
|
disableKeywordReplacement: this.disableKeywordReplacement,
|
|
@@ -64,9 +139,12 @@ class YAMLContext {
|
|
|
64
139
|
try {
|
|
65
140
|
const fPath = path_1.default.resolve(this.configFile);
|
|
66
141
|
logger_1.default.debug(`Loading YAML from ${fPath}`);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
142
|
+
const prepareContent = (content) => prepareYamlContent(content, this.mappings, opts.disableKeywordReplacement);
|
|
143
|
+
const loadIncludedYaml = (filePath) => parseYaml(prepareContent(fs_extra_1.default.readFileSync(filePath, 'utf8')));
|
|
144
|
+
const parsed = parseYaml(prepareContent(fs_extra_1.default.readFileSync(fPath, 'utf8')));
|
|
145
|
+
// Bound includes by the same config root loadFile() enforces
|
|
146
|
+
const configRoot = path_1.default.resolve(this.basePath);
|
|
147
|
+
Object.assign(this.assets, resolveIncludes(parsed, path_1.default.dirname(fPath), configRoot, loadIncludedYaml) || {});
|
|
70
148
|
}
|
|
71
149
|
catch (err) {
|
|
72
150
|
logger_1.default.debug(err.stack);
|
|
@@ -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, '');
|
package/lib/readonly.js
CHANGED
|
@@ -28,6 +28,11 @@ const readOnlyFields = {
|
|
|
28
28
|
'config_route',
|
|
29
29
|
'owners',
|
|
30
30
|
],
|
|
31
|
+
attackProtection: [
|
|
32
|
+
// Returned by GET but rejected by PATCH with "Additional properties not allowed"
|
|
33
|
+
'bruteForceProtection.form_submission_mode',
|
|
34
|
+
'bruteForceProtection.form_submission_behavior',
|
|
35
|
+
],
|
|
31
36
|
};
|
|
32
37
|
function getExcludedFields(config) {
|
|
33
38
|
const strippedFields = { ...readOnlyFields };
|
|
@@ -165,6 +165,17 @@ export declare const schema: {
|
|
|
165
165
|
suspiciousIpThrottling: {
|
|
166
166
|
type: string;
|
|
167
167
|
};
|
|
168
|
+
phoneProviderProtection: {
|
|
169
|
+
type: string;
|
|
170
|
+
properties: {
|
|
171
|
+
type: {
|
|
172
|
+
type: string;
|
|
173
|
+
enum: string[];
|
|
174
|
+
description: string;
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
required: string[];
|
|
178
|
+
};
|
|
168
179
|
};
|
|
169
180
|
additionalProperties: boolean;
|
|
170
181
|
};
|
|
@@ -174,6 +185,7 @@ export type AttackProtection = {
|
|
|
174
185
|
bruteForceProtection: Asset;
|
|
175
186
|
captcha?: Asset | null;
|
|
176
187
|
suspiciousIpThrottling: Asset;
|
|
188
|
+
phoneProviderProtection?: Asset | null;
|
|
177
189
|
};
|
|
178
190
|
export default class AttackProtectionHandler extends DefaultAPIHandler {
|
|
179
191
|
existing: AttackProtection | null;
|
|
@@ -180,6 +180,17 @@ exports.schema = {
|
|
|
180
180
|
suspiciousIpThrottling: {
|
|
181
181
|
type: 'object',
|
|
182
182
|
},
|
|
183
|
+
phoneProviderProtection: {
|
|
184
|
+
type: 'object',
|
|
185
|
+
properties: {
|
|
186
|
+
type: {
|
|
187
|
+
type: 'string',
|
|
188
|
+
enum: ['exponential', 'default'],
|
|
189
|
+
description: 'The backoff strategy applied to Send SMS during MFA enrollment. Gated by the sms_exponential_backoff feature flag; when the flag is disabled the API returns 403 and this setting is skipped on export.',
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
required: ['type'],
|
|
193
|
+
},
|
|
183
194
|
},
|
|
184
195
|
additionalProperties: false,
|
|
185
196
|
};
|
|
@@ -227,6 +238,11 @@ class AttackProtectionHandler extends default_1.default {
|
|
|
227
238
|
enabled: item.suspiciousIpThrottling.enabled,
|
|
228
239
|
};
|
|
229
240
|
}
|
|
241
|
+
if (item.phoneProviderProtection?.type) {
|
|
242
|
+
obj['phone-provider-protection'] = {
|
|
243
|
+
type: item.phoneProviderProtection.type,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
230
246
|
return obj;
|
|
231
247
|
})();
|
|
232
248
|
return super.objString(objectString);
|
|
@@ -242,15 +258,38 @@ class AttackProtectionHandler extends default_1.default {
|
|
|
242
258
|
]);
|
|
243
259
|
let botDetection = null;
|
|
244
260
|
let captcha = null;
|
|
261
|
+
let phoneProviderProtection = null;
|
|
262
|
+
try {
|
|
263
|
+
botDetection = await this.client.attackProtection.botDetection.get();
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
if (err.statusCode === 403) {
|
|
267
|
+
logger_1.default.warn("Bot detection is not available on this tenant's current plan. Skipping bot detection settings.");
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
throw err;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
245
273
|
try {
|
|
246
|
-
|
|
247
|
-
this.client.attackProtection.botDetection.get(),
|
|
248
|
-
this.client.attackProtection.captcha.get(),
|
|
249
|
-
]);
|
|
274
|
+
captcha = await this.client.attackProtection.captcha.get();
|
|
250
275
|
}
|
|
251
276
|
catch (err) {
|
|
252
277
|
if (err.statusCode === 403) {
|
|
253
|
-
logger_1.default.warn(
|
|
278
|
+
logger_1.default.warn("Captcha is not available on this tenant's current plan. Skipping captcha settings.");
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
throw err;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
phoneProviderProtection = await this.client.attackProtection.phoneProviderProtection.get();
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
if (err.statusCode === 403) {
|
|
289
|
+
logger_1.default.warn('Phone provider protection is not enabled for this tenant (sms_exponential_backoff feature flag). Skipping phone provider protection settings.');
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
throw err;
|
|
254
293
|
}
|
|
255
294
|
}
|
|
256
295
|
const attackProtection = {
|
|
@@ -264,6 +303,9 @@ class AttackProtectionHandler extends default_1.default {
|
|
|
264
303
|
if (captcha) {
|
|
265
304
|
attackProtection.captcha = captcha;
|
|
266
305
|
}
|
|
306
|
+
if (phoneProviderProtection) {
|
|
307
|
+
attackProtection.phoneProviderProtection = phoneProviderProtection;
|
|
308
|
+
}
|
|
267
309
|
this.existing = attackProtection;
|
|
268
310
|
return this.existing;
|
|
269
311
|
}
|
|
@@ -303,11 +345,29 @@ class AttackProtectionHandler extends default_1.default {
|
|
|
303
345
|
updates.push(this.client.attackProtection.captcha.update(attackProtection.captcha));
|
|
304
346
|
}
|
|
305
347
|
if (attackProtection.bruteForceProtection) {
|
|
306
|
-
|
|
348
|
+
// Strip fields returned by GET but rejected by PATCH
|
|
349
|
+
const { form_submission_mode, form_submission_behavior, ...bruteForcePayload } = attackProtection.bruteForceProtection;
|
|
350
|
+
updates.push(this.client.attackProtection.bruteForceProtection.update(bruteForcePayload));
|
|
307
351
|
}
|
|
308
352
|
if (attackProtection.suspiciousIpThrottling) {
|
|
309
353
|
updates.push(this.client.attackProtection.suspiciousIpThrottling.update(attackProtection.suspiciousIpThrottling));
|
|
310
354
|
}
|
|
355
|
+
// Phone provider protection uses PATCH (not update) and requires `type` on every request.
|
|
356
|
+
// Guard the 403 (sms_exponential_backoff flag off) per-promise so a flag-off tenant does
|
|
357
|
+
// not fail the whole attackProtection stage — mirrors the 403-skip in getType().
|
|
358
|
+
if (attackProtection.phoneProviderProtection?.type) {
|
|
359
|
+
updates.push(this.client.attackProtection.phoneProviderProtection
|
|
360
|
+
.patch({
|
|
361
|
+
type: attackProtection.phoneProviderProtection.type,
|
|
362
|
+
})
|
|
363
|
+
.catch((err) => {
|
|
364
|
+
if (err.statusCode === 403) {
|
|
365
|
+
logger_1.default.warn('Phone provider protection is not enabled for this tenant (sms_exponential_backoff feature flag). Skipping phone provider protection update.');
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
throw err;
|
|
369
|
+
}));
|
|
370
|
+
}
|
|
311
371
|
if (!updates.length) {
|
|
312
372
|
return;
|
|
313
373
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Assets, Auth0APIClient, CalculatedChanges } from '../../../types';
|
|
2
|
+
import { ConfigFunction } from '../../../configFactory';
|
|
3
|
+
export declare const schema: {
|
|
4
|
+
type: string;
|
|
5
|
+
items: {
|
|
6
|
+
type: string;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export default class ClientAuthCredentialsHandler {
|
|
10
|
+
client: Auth0APIClient;
|
|
11
|
+
config: ConfigFunction;
|
|
12
|
+
type: string;
|
|
13
|
+
existing: null;
|
|
14
|
+
constructor({ client, config }: {
|
|
15
|
+
client: Auth0APIClient;
|
|
16
|
+
config: ConfigFunction;
|
|
17
|
+
});
|
|
18
|
+
getType(): Promise<null>;
|
|
19
|
+
load(): Promise<{
|
|
20
|
+
[x: string]: null;
|
|
21
|
+
}>;
|
|
22
|
+
validate(): Promise<null>;
|
|
23
|
+
dryRunChanges(_assets: Assets): Promise<CalculatedChanges>;
|
|
24
|
+
processChanges(assets: Assets): Promise<void>;
|
|
25
|
+
private inferCredentialType;
|
|
26
|
+
private inferMethodFromType;
|
|
27
|
+
}
|