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
|
@@ -34,6 +34,6 @@ export type RiskAssessment = {
|
|
|
34
34
|
export default class RiskAssessmentHandler extends DefaultAPIHandler {
|
|
35
35
|
existing: RiskAssessment;
|
|
36
36
|
constructor(config: DefaultAPIHandler);
|
|
37
|
-
getType(): Promise<RiskAssessment>;
|
|
37
|
+
getType(): Promise<RiskAssessment | null>;
|
|
38
38
|
processChanges(assets: Assets): Promise<void>;
|
|
39
39
|
}
|
|
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.schema = void 0;
|
|
7
7
|
const default_1 = __importDefault(require("./default"));
|
|
8
8
|
const auth0_1 = require("auth0");
|
|
9
|
+
const logger_1 = __importDefault(require("../../../logger"));
|
|
10
|
+
const utils_1 = require("../../utils");
|
|
9
11
|
exports.schema = {
|
|
10
12
|
type: 'object',
|
|
11
13
|
properties: {
|
|
@@ -50,6 +52,9 @@ class RiskAssessmentHandler extends default_1.default {
|
|
|
50
52
|
if (err instanceof auth0_1.ManagementError && err?.statusCode === 404) {
|
|
51
53
|
return { remember_for: 0 };
|
|
52
54
|
}
|
|
55
|
+
if ((0, utils_1.isInsufficientEntitlementError)(err)) {
|
|
56
|
+
return { remember_for: 0 };
|
|
57
|
+
}
|
|
53
58
|
throw err;
|
|
54
59
|
}),
|
|
55
60
|
]);
|
|
@@ -71,6 +76,10 @@ class RiskAssessmentHandler extends default_1.default {
|
|
|
71
76
|
this.existing = riskAssessment;
|
|
72
77
|
return this.existing;
|
|
73
78
|
}
|
|
79
|
+
if ((0, utils_1.isInsufficientEntitlementError)(err)) {
|
|
80
|
+
logger_1.default.warn('Skipping riskAssessment export: tenant does not have Adaptive MFA entitlement');
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
74
83
|
throw err;
|
|
75
84
|
}
|
|
76
85
|
}
|
|
@@ -87,7 +96,16 @@ class RiskAssessmentHandler extends default_1.default {
|
|
|
87
96
|
if (riskAssessment.new_device) {
|
|
88
97
|
updates.push(this.client.riskAssessments.settings.newDevice.update(riskAssessment.new_device));
|
|
89
98
|
}
|
|
90
|
-
|
|
99
|
+
try {
|
|
100
|
+
await Promise.all(updates);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
if ((0, utils_1.isInsufficientEntitlementError)(err)) {
|
|
104
|
+
logger_1.default.warn('Skipping riskAssessment deploy: tenant does not have Adaptive MFA entitlement');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
throw err;
|
|
108
|
+
}
|
|
91
109
|
this.updated += 1;
|
|
92
110
|
this.didUpdate(riskAssessment);
|
|
93
111
|
}
|
|
@@ -56,6 +56,7 @@ exports.schema = {
|
|
|
56
56
|
name: { type: 'string' },
|
|
57
57
|
id: { type: 'string' },
|
|
58
58
|
description: { type: 'string' },
|
|
59
|
+
type: { type: 'string', enum: ['tenant', 'organization'] },
|
|
59
60
|
permissions: {
|
|
60
61
|
type: 'array',
|
|
61
62
|
items: {
|
|
@@ -81,6 +82,9 @@ class RolesHandler extends default_1.default {
|
|
|
81
82
|
async createRole(data) {
|
|
82
83
|
const role = { ...data };
|
|
83
84
|
delete role.permissions;
|
|
85
|
+
// deploy-cli only manages tenant-level roles, so `type` is stripped on write; it
|
|
86
|
+
// defaults to `tenant` on create. This keeps exports (which carry `type`) round-trippable.
|
|
87
|
+
delete role.type;
|
|
84
88
|
const created = await this.client.roles.create(role);
|
|
85
89
|
if (created.id && typeof data.permissions !== 'undefined' && data.permissions.length > 0) {
|
|
86
90
|
await this.client.roles.permissions.add(created.id, { permissions: data.permissions });
|
|
@@ -128,19 +132,30 @@ class RolesHandler extends default_1.default {
|
|
|
128
132
|
}
|
|
129
133
|
}
|
|
130
134
|
async updateRole(data, roles) {
|
|
131
|
-
|
|
135
|
+
// Match on id first so a renamed role (matched by id, new name) still resolves to its
|
|
136
|
+
// existing permissions; fall back to name for configs imported without ids.
|
|
137
|
+
const existingRole = roles.find((r) => r.id === data.id) || roles.find((r) => r.name === data.name);
|
|
132
138
|
const params = { id: data.id };
|
|
133
|
-
const newPermissions = data.permissions;
|
|
139
|
+
const newPermissions = data.permissions || [];
|
|
134
140
|
delete data.permissions;
|
|
135
141
|
delete data.id;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
// deploy-cli only manages tenant-level roles, so `type` is stripped on write; this
|
|
143
|
+
// lets files that already carry it (from a prior export) be imported cleanly.
|
|
144
|
+
delete data.type;
|
|
145
|
+
const retryConfig = this.getRetryConfig();
|
|
146
|
+
await (0, default_1.retryWithExponentialBackoff)(() => this.client.roles.update(params.id, data), retryConfig);
|
|
147
|
+
// Only touch permissions that actually changed, and add before removing so an
|
|
148
|
+
// interrupted run can never leave a role with fewer permissions than it started with.
|
|
149
|
+
const existingPermissions = existingRole.permissions || [];
|
|
150
|
+
const samePermission = (a, b) => a.permission_name === b.permission_name &&
|
|
151
|
+
a.resource_server_identifier === b.resource_server_identifier;
|
|
152
|
+
const permissionsToAdd = newPermissions.filter((p) => !existingPermissions.some((e) => samePermission(e, p)));
|
|
153
|
+
const permissionsToRemove = existingPermissions.filter((e) => !newPermissions.some((p) => samePermission(e, p)));
|
|
154
|
+
if (permissionsToAdd.length > 0) {
|
|
155
|
+
await (0, default_1.retryWithExponentialBackoff)(() => this.client.roles.permissions.add(params.id, { permissions: permissionsToAdd }), retryConfig);
|
|
141
156
|
}
|
|
142
|
-
if (
|
|
143
|
-
await this.client.roles.permissions.
|
|
157
|
+
if (permissionsToRemove.length > 0) {
|
|
158
|
+
await (0, default_1.retryWithExponentialBackoff)(() => this.client.roles.permissions.delete(params.id, { permissions: permissionsToRemove }), retryConfig);
|
|
144
159
|
}
|
|
145
160
|
return params;
|
|
146
161
|
}
|
|
@@ -164,9 +179,12 @@ class RolesHandler extends default_1.default {
|
|
|
164
179
|
return this.existing;
|
|
165
180
|
}
|
|
166
181
|
try {
|
|
182
|
+
// Only manage tenant-level roles; exclude org-scoped roles surfaced when
|
|
183
|
+
// the api2_org_level_roles_ea flag is enabled.
|
|
167
184
|
const roles = await (0, client_1.paginate)(this.client.roles.list, {
|
|
168
185
|
paginate: true,
|
|
169
186
|
include_totals: true,
|
|
187
|
+
type: 'tenant',
|
|
170
188
|
});
|
|
171
189
|
for (let index = 0; index < roles.length; index++) {
|
|
172
190
|
const allPermission = [];
|
|
@@ -9,6 +9,7 @@ const utils_1 = require("../../utils");
|
|
|
9
9
|
const default_1 = __importDefault(require("./default"));
|
|
10
10
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
11
11
|
const calculateChanges_1 = require("../../calculateChanges");
|
|
12
|
+
const calculateDryRunChanges_1 = require("../../calculateDryRunChanges");
|
|
12
13
|
const client_1 = require("../client");
|
|
13
14
|
exports.excludeSchema = {
|
|
14
15
|
type: 'array',
|
|
@@ -135,13 +136,24 @@ class RulesHandler extends default_1.default {
|
|
|
135
136
|
};
|
|
136
137
|
}
|
|
137
138
|
async dryRunChanges(assets) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
del,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
139
|
+
let { rules } = assets;
|
|
140
|
+
if (!rules) {
|
|
141
|
+
return { del: [], create: [], conflicts: [], update: [] };
|
|
142
|
+
}
|
|
143
|
+
const excludedRules = (assets.exclude && assets.exclude.rules) || [];
|
|
144
|
+
rules = rules.filter((r) => !excludedRules.includes(r.name));
|
|
145
|
+
let existing = await this.getType();
|
|
146
|
+
if (existing === null) {
|
|
147
|
+
return { del: [], create: [], conflicts: [], update: [] };
|
|
148
|
+
}
|
|
149
|
+
existing = existing.filter((r) => !excludedRules.includes(r.name));
|
|
150
|
+
return (0, calculateDryRunChanges_1.calculateDryRunChanges)({
|
|
151
|
+
type: this.type,
|
|
152
|
+
assets: rules,
|
|
153
|
+
existing,
|
|
154
|
+
identifiers: this.identifiers,
|
|
155
|
+
ignoreDryRunFields: this.getEffectiveIgnoreDryRunFields(),
|
|
156
|
+
});
|
|
145
157
|
}
|
|
146
158
|
async validate(assets) {
|
|
147
159
|
const { rules } = assets;
|
|
@@ -39,6 +39,10 @@ class ScimHandler {
|
|
|
39
39
|
* @param connections
|
|
40
40
|
*/
|
|
41
41
|
async createIdMap(connections) {
|
|
42
|
+
// No `read:scim_config` scope means nothing to fetch. Return before clearing
|
|
43
|
+
// to keep any existing map and skip a no-op pool task per connection.
|
|
44
|
+
if (!this.scimScopes.read)
|
|
45
|
+
return;
|
|
42
46
|
this.idMap.clear();
|
|
43
47
|
const logMsg = 'Reviewing connections for SCIM support. This may take a while...';
|
|
44
48
|
if (nconf_1.default.get('AUTH0_DRY_RUN')) {
|
|
@@ -47,14 +51,15 @@ class ScimHandler {
|
|
|
47
51
|
else {
|
|
48
52
|
logger_1.default.info(logMsg);
|
|
49
53
|
}
|
|
54
|
+
// Only SCIM-capable strategies can carry a SCIM configuration; filtering here
|
|
55
|
+
// keeps non-SCIM connections out of the throttled pool.
|
|
56
|
+
const scimConnections = (connections || []).filter((connection) => this.isScimStrategy(connection.strategy));
|
|
50
57
|
await this.poolClient
|
|
51
58
|
.addEachTask({
|
|
52
|
-
data:
|
|
59
|
+
data: scimConnections,
|
|
53
60
|
generator: (connection) => {
|
|
54
61
|
if (!this.scimScopes.read)
|
|
55
62
|
return Promise.resolve(null);
|
|
56
|
-
if (!this.isScimStrategy(connection.strategy))
|
|
57
|
-
return Promise.resolve(null);
|
|
58
63
|
this.idMap.set(connection.id, { strategy: connection.strategy });
|
|
59
64
|
return this.getScimConfiguration(connection.id)
|
|
60
65
|
.then((response) => {
|
|
@@ -79,6 +79,51 @@ export declare const schema: {
|
|
|
79
79
|
enum: string[];
|
|
80
80
|
description: string;
|
|
81
81
|
};
|
|
82
|
+
country_codes: {
|
|
83
|
+
type: string[];
|
|
84
|
+
description: string;
|
|
85
|
+
properties: {
|
|
86
|
+
list: {
|
|
87
|
+
type: string;
|
|
88
|
+
description: string;
|
|
89
|
+
items: {
|
|
90
|
+
type: string;
|
|
91
|
+
pattern: string;
|
|
92
|
+
};
|
|
93
|
+
minItems: number;
|
|
94
|
+
uniqueItems: boolean;
|
|
95
|
+
};
|
|
96
|
+
mode: {
|
|
97
|
+
type: string;
|
|
98
|
+
enum: string[];
|
|
99
|
+
description: string;
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
required: string[];
|
|
103
|
+
additionalProperties: boolean;
|
|
104
|
+
};
|
|
105
|
+
sessions: {
|
|
106
|
+
type: string;
|
|
107
|
+
description: string;
|
|
108
|
+
properties: {
|
|
109
|
+
anonymous: {
|
|
110
|
+
type: string;
|
|
111
|
+
description: string;
|
|
112
|
+
properties: {
|
|
113
|
+
lifetime_in_minutes: {
|
|
114
|
+
type: string;
|
|
115
|
+
description: string;
|
|
116
|
+
};
|
|
117
|
+
activate_cookie: {
|
|
118
|
+
type: string;
|
|
119
|
+
description: string;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
additionalProperties: boolean;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
additionalProperties: boolean;
|
|
126
|
+
};
|
|
82
127
|
};
|
|
83
128
|
};
|
|
84
129
|
export interface Tenant extends Management.GetTenantSettingsResponseContent {
|
|
@@ -106,6 +106,53 @@ exports.schema = {
|
|
|
106
106
|
enum: ['strict', 'permissive'],
|
|
107
107
|
description: 'Indicates the security mode for new clients created through the Dynamic Client Registration endpoint.',
|
|
108
108
|
},
|
|
109
|
+
country_codes: {
|
|
110
|
+
type: ['object', 'null'],
|
|
111
|
+
description: 'Phone country code configuration for identifier input. Set to `null` to remove filtering (allow all countries). Requires the `tenant_country_codes_filtering` feature flag to be enabled on the tenant.',
|
|
112
|
+
properties: {
|
|
113
|
+
list: {
|
|
114
|
+
type: 'array',
|
|
115
|
+
description: 'ISO 3166-1 alpha-2 country codes (e.g. `US`, `GB`).',
|
|
116
|
+
items: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
pattern: '^[A-Z]{2}$',
|
|
119
|
+
},
|
|
120
|
+
minItems: 1,
|
|
121
|
+
uniqueItems: true,
|
|
122
|
+
},
|
|
123
|
+
mode: {
|
|
124
|
+
type: 'string',
|
|
125
|
+
enum: ['allow', 'deny'],
|
|
126
|
+
description: 'Whether the list is an allowlist or denylist.',
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
required: ['list', 'mode'],
|
|
130
|
+
additionalProperties: false,
|
|
131
|
+
},
|
|
132
|
+
sessions: {
|
|
133
|
+
type: 'object',
|
|
134
|
+
description: 'Sessions related settings for the tenant.',
|
|
135
|
+
properties: {
|
|
136
|
+
anonymous: {
|
|
137
|
+
type: 'object',
|
|
138
|
+
description: 'Anonymous session settings for the tenant.',
|
|
139
|
+
properties: {
|
|
140
|
+
lifetime_in_minutes: {
|
|
141
|
+
type: 'integer',
|
|
142
|
+
description: 'The lifetime of an anonymous session, in minutes.',
|
|
143
|
+
},
|
|
144
|
+
activate_cookie: {
|
|
145
|
+
type: 'boolean',
|
|
146
|
+
description: 'Whether to activate the anonymous session cookie.',
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
// Permissive to match the parent `sessions` object so new Management API
|
|
150
|
+
// session fields round-trip through export/import without validation failure.
|
|
151
|
+
additionalProperties: true,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
additionalProperties: true,
|
|
155
|
+
},
|
|
109
156
|
},
|
|
110
157
|
};
|
|
111
158
|
const blockPageKeys = [
|
|
@@ -147,6 +194,7 @@ exports.allowedTenantFlags = [
|
|
|
147
194
|
'require_pushed_authorization_requests',
|
|
148
195
|
'mfa_show_factor_list_on_enrollment',
|
|
149
196
|
'improved_signup_bot_detection_in_classic',
|
|
197
|
+
'use_scope_descriptions_for_consent',
|
|
150
198
|
];
|
|
151
199
|
const removeUnallowedTenantFlags = (proposedFlags) => {
|
|
152
200
|
if (proposedFlags === undefined)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Management } from 'auth0';
|
|
2
|
-
import { Assets } from '../../../types';
|
|
2
|
+
import { Assets, CalculatedChanges } from '../../../types';
|
|
3
3
|
import DefaultHandler from './default';
|
|
4
4
|
/**
|
|
5
5
|
* Schema
|
|
@@ -179,6 +179,41 @@ export declare const schema: {
|
|
|
179
179
|
pattern: string;
|
|
180
180
|
type: string;
|
|
181
181
|
};
|
|
182
|
+
identifiers: {
|
|
183
|
+
additionalProperties: boolean;
|
|
184
|
+
description: string;
|
|
185
|
+
properties: {
|
|
186
|
+
login_display: {
|
|
187
|
+
description: string;
|
|
188
|
+
enum: string[];
|
|
189
|
+
type: string;
|
|
190
|
+
};
|
|
191
|
+
otp_autocomplete: {
|
|
192
|
+
description: string;
|
|
193
|
+
type: string;
|
|
194
|
+
};
|
|
195
|
+
phone_display: {
|
|
196
|
+
additionalProperties: boolean;
|
|
197
|
+
description: string;
|
|
198
|
+
properties: {
|
|
199
|
+
formatting: {
|
|
200
|
+
description: string;
|
|
201
|
+
enum: string[];
|
|
202
|
+
type: string;
|
|
203
|
+
};
|
|
204
|
+
masking: {
|
|
205
|
+
description: string;
|
|
206
|
+
enum: string[];
|
|
207
|
+
type: string;
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
required: string[];
|
|
211
|
+
type: string;
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
required: string[];
|
|
215
|
+
type: string;
|
|
216
|
+
};
|
|
182
217
|
fonts: {
|
|
183
218
|
additionalProperties: boolean;
|
|
184
219
|
properties: {
|
|
@@ -375,6 +410,7 @@ export default class ThemesHandler extends DefaultHandler {
|
|
|
375
410
|
existing: Theme[] | null;
|
|
376
411
|
constructor(options: DefaultHandler);
|
|
377
412
|
objString(theme: Theme): string;
|
|
413
|
+
dryRunChanges(assets: Assets): Promise<CalculatedChanges>;
|
|
378
414
|
getType(): Promise<Theme[] | null>;
|
|
379
415
|
processChanges(assets: Assets): Promise<void>;
|
|
380
416
|
deleteThemes(): Promise<void>;
|
|
@@ -46,6 +46,7 @@ exports.schema = void 0;
|
|
|
46
46
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
47
47
|
const default_1 = __importStar(require("./default"));
|
|
48
48
|
const utils_1 = require("../../utils");
|
|
49
|
+
const calculateDryRunChanges_1 = require("../../calculateDryRunChanges");
|
|
49
50
|
/**
|
|
50
51
|
* Schema
|
|
51
52
|
*/
|
|
@@ -251,6 +252,41 @@ exports.schema = {
|
|
|
251
252
|
pattern: '^[^<>]*$',
|
|
252
253
|
type: 'string',
|
|
253
254
|
},
|
|
255
|
+
identifiers: {
|
|
256
|
+
additionalProperties: false,
|
|
257
|
+
description: 'Identifier display settings on the theme. Requires the `universal_login_theme_identifiers` feature flag to be enabled on the tenant.',
|
|
258
|
+
properties: {
|
|
259
|
+
login_display: {
|
|
260
|
+
description: 'Login display mode',
|
|
261
|
+
enum: ['separate', 'unified'],
|
|
262
|
+
type: 'string',
|
|
263
|
+
},
|
|
264
|
+
otp_autocomplete: {
|
|
265
|
+
description: 'Whether OTP autocomplete is enabled',
|
|
266
|
+
type: 'boolean',
|
|
267
|
+
},
|
|
268
|
+
phone_display: {
|
|
269
|
+
additionalProperties: false,
|
|
270
|
+
description: 'Phone number display settings',
|
|
271
|
+
properties: {
|
|
272
|
+
formatting: {
|
|
273
|
+
description: 'Phone number formatting style',
|
|
274
|
+
enum: ['international', 'regional'],
|
|
275
|
+
type: 'string',
|
|
276
|
+
},
|
|
277
|
+
masking: {
|
|
278
|
+
description: 'Phone number masking strategy',
|
|
279
|
+
enum: ['hide_country_code', 'mask_digits', 'show_all'],
|
|
280
|
+
type: 'string',
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
required: ['formatting', 'masking'],
|
|
284
|
+
type: 'object',
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
required: ['login_display', 'otp_autocomplete', 'phone_display'],
|
|
288
|
+
type: 'object',
|
|
289
|
+
},
|
|
254
290
|
fonts: {
|
|
255
291
|
additionalProperties: false,
|
|
256
292
|
properties: {
|
|
@@ -470,6 +506,34 @@ class ThemesHandler extends default_1.default {
|
|
|
470
506
|
objString(theme) {
|
|
471
507
|
return theme.displayName || JSON.stringify(theme);
|
|
472
508
|
}
|
|
509
|
+
async dryRunChanges(assets) {
|
|
510
|
+
const { themes } = assets;
|
|
511
|
+
if (!themes) {
|
|
512
|
+
return { del: [], create: [], conflicts: [], update: [] };
|
|
513
|
+
}
|
|
514
|
+
const existing = await this.getType();
|
|
515
|
+
// getType() returns null when Universal Login customizations are not enabled
|
|
516
|
+
// on the tenant (the 400 "no-code not enabled" path in getThemes).
|
|
517
|
+
if (existing === null) {
|
|
518
|
+
return { del: [], create: [], conflicts: [], update: [] };
|
|
519
|
+
}
|
|
520
|
+
// Themes are singletons — at most one per tenant. If the local theme has no
|
|
521
|
+
// themeId (stripped during export when --export_ids is not set), backfill it
|
|
522
|
+
// from the remote so the matcher can find a match without requiring --export_ids.
|
|
523
|
+
const normalizedThemes = themes.map((theme) => {
|
|
524
|
+
if (!theme.themeId && existing.length > 0 && existing[0].themeId) {
|
|
525
|
+
return { ...theme, themeId: existing[0].themeId };
|
|
526
|
+
}
|
|
527
|
+
return theme;
|
|
528
|
+
});
|
|
529
|
+
return (0, calculateDryRunChanges_1.calculateDryRunChanges)({
|
|
530
|
+
type: this.type,
|
|
531
|
+
assets: normalizedThemes,
|
|
532
|
+
existing,
|
|
533
|
+
identifiers: this.identifiers,
|
|
534
|
+
ignoreDryRunFields: this.getEffectiveIgnoreDryRunFields(),
|
|
535
|
+
});
|
|
536
|
+
}
|
|
473
537
|
async getType() {
|
|
474
538
|
if (!this.existing) {
|
|
475
539
|
this.existing = await this.getThemes();
|
|
@@ -138,7 +138,9 @@ class TokenExchangeProfilesHandler extends default_1.default {
|
|
|
138
138
|
}
|
|
139
139
|
catch (err) {
|
|
140
140
|
if (err.statusCode === 403) {
|
|
141
|
-
logger_1.default.warn(
|
|
141
|
+
logger_1.default.warn(`Cannot retrieve tokenExchangeProfiles: ${err.message}. ` +
|
|
142
|
+
`Verify the M2M application has the 'read:token_exchange_profiles' scope and ` +
|
|
143
|
+
`your subscription plan includes Token Exchange Profiles.`);
|
|
142
144
|
return [];
|
|
143
145
|
}
|
|
144
146
|
throw err;
|
package/lib/tools/auth0/index.js
CHANGED
|
@@ -11,13 +11,10 @@ const promise_pool_executor_1 = require("promise-pool-executor");
|
|
|
11
11
|
const client_1 = __importDefault(require("./client"));
|
|
12
12
|
const schema_1 = __importDefault(require("./schema"));
|
|
13
13
|
const handlers_1 = __importDefault(require("./handlers"));
|
|
14
|
-
// Cache the compiled AJV validator to avoid recompiling the schema on every validate() call.
|
|
15
|
-
// Schema compilation is expensive (~5s) due to the complexity of the combined resource schema.
|
|
16
|
-
const ajv = new ajv_1.default({ useDefaults: true, nullable: true });
|
|
17
|
-
const validateSchema = ajv.compile(schema_1.default);
|
|
18
14
|
const calculateDryRunChanges_1 = require("../calculateDryRunChanges");
|
|
19
15
|
const utils_1 = require("../../utils");
|
|
20
16
|
const utils_2 = require("../utils");
|
|
17
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
21
18
|
/**
|
|
22
19
|
* Sorts handlers by their @order decorator metadata for a given stage.
|
|
23
20
|
* Handlers are sorted in ascending order (lower values execute first).
|
|
@@ -109,12 +106,13 @@ class Auth0 {
|
|
|
109
106
|
}
|
|
110
107
|
}
|
|
111
108
|
async validate() {
|
|
109
|
+
const ajv = new ajv_1.default({ useDefaults: true, nullable: true });
|
|
112
110
|
const nonNullAssets = Object.keys(this.assets)
|
|
113
111
|
.filter((k) => this.assets[k] != null)
|
|
114
112
|
.reduce((a, k) => ({ ...a, [k]: this.assets[k] }), {});
|
|
115
|
-
const valid =
|
|
113
|
+
const valid = ajv.validate(schema_1.default, nonNullAssets);
|
|
116
114
|
if (!valid) {
|
|
117
|
-
throw new Error(`Schema validation failed loading ${JSON.stringify(
|
|
115
|
+
throw new Error(`Schema validation failed loading ${JSON.stringify(ajv.errors, null, 4)}`);
|
|
118
116
|
}
|
|
119
117
|
await this.runStage('validate');
|
|
120
118
|
}
|
|
@@ -148,6 +146,14 @@ class Auth0 {
|
|
|
148
146
|
data: this.handlers,
|
|
149
147
|
generator: (handler) => (async () => {
|
|
150
148
|
try {
|
|
149
|
+
// Every handler in the dry-run loop is expected to implement dryRunChanges.
|
|
150
|
+
// Guard against non-conforming handlers (e.g. deploy-only handlers that don't
|
|
151
|
+
// extend the base APIHandler) so a missing method skips the handler instead of
|
|
152
|
+
// crashing the whole dry run. Logged loudly so an omitted preview is visible.
|
|
153
|
+
if (typeof handler.dryRunChanges !== 'function') {
|
|
154
|
+
logger_1.default.warn(`Handler "${handler.type}" does not implement dryRunChanges; skipping it in the dry-run preview. Changes for this resource will still be applied during a real import.`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
151
157
|
const detailedChanges = [];
|
|
152
158
|
let created = 0;
|
|
153
159
|
let updated = 0;
|
|
@@ -108,6 +108,13 @@ function normalizeArrayValues(values) {
|
|
|
108
108
|
.map((value) => normalizeValue(value))
|
|
109
109
|
.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Serializes a value to a human-readable string for use in diff messages.
|
|
113
|
+
* Objects and arrays are JSON-stringified to avoid "[object Object]" output.
|
|
114
|
+
*/
|
|
115
|
+
function formatDiffValue(value) {
|
|
116
|
+
return typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value);
|
|
117
|
+
}
|
|
111
118
|
/**
|
|
112
119
|
* Writes the accumulated diff log to a JSON file on disk.
|
|
113
120
|
* Useful for CI pipelines that want a machine-readable dry-run report.
|
|
@@ -179,7 +186,7 @@ function getObjectDifferences(localObj, remoteObj, keyObjPath = '', resourceType
|
|
|
179
186
|
differences.push(...nestedDifferences);
|
|
180
187
|
}
|
|
181
188
|
else if (item !== normalizedRemoteValue[index]) {
|
|
182
|
-
const message = `Array item difference at [${currentPath}[${index}]]: local:${item} vs remote:${normalizedRemoteValue[index]}`;
|
|
189
|
+
const message = `Array item difference at [${currentPath}[${index}]]: local:${formatDiffValue(item)} vs remote:${formatDiffValue(normalizedRemoteValue[index])}`;
|
|
183
190
|
differences.push(message);
|
|
184
191
|
}
|
|
185
192
|
});
|
|
@@ -204,13 +211,10 @@ function getObjectDifferences(localObj, remoteObj, keyObjPath = '', resourceType
|
|
|
204
211
|
differences.push(...nestedDifferences);
|
|
205
212
|
return;
|
|
206
213
|
}
|
|
207
|
-
// Compare primitive values
|
|
214
|
+
// Compare primitive values
|
|
208
215
|
if (localValue !== remoteValue) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const message = `Value difference for [${currentPath}]: local:${localValue} vs remote:${remoteValue}`;
|
|
212
|
-
differences.push(message);
|
|
213
|
-
}
|
|
216
|
+
const message = `Value difference for [${currentPath}]: local:${localValue} vs remote:${remoteValue}`;
|
|
217
|
+
differences.push(message);
|
|
214
218
|
}
|
|
215
219
|
});
|
|
216
220
|
return differences;
|
|
@@ -242,7 +246,7 @@ function calculateDryRunChanges({ type, assets, existing, identifiers = ['id', '
|
|
|
242
246
|
const create = [];
|
|
243
247
|
const conflicts = [];
|
|
244
248
|
const localAssets = (Array.isArray(assets) ? [...assets] : [assets]).map((asset) => type === 'tenant' ? normalizeTenantForDryRun(asset) : asset);
|
|
245
|
-
const remoteAssets = (Array.isArray(existing) ? [...existing] : [existing]).map((asset) => (type === 'tenant' ? normalizeTenantForDryRun(asset) : asset));
|
|
249
|
+
const remoteAssets = (Array.isArray(existing) ? [...existing] : existing ? [existing] : []).map((asset) => (type === 'tenant' ? normalizeTenantForDryRun(asset) : asset));
|
|
246
250
|
// Helper: returns true if a local and remote asset share at least one identifier value
|
|
247
251
|
const assetsMatch = (localAsset, remoteAsset) => identifiers.some((id) => {
|
|
248
252
|
if (Array.isArray(id)) {
|
package/lib/tools/constants.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ declare const constants: {
|
|
|
35
35
|
EMAIL_MFA_OOB_CODE: string;
|
|
36
36
|
EMAIL_USER_INVITATION: string;
|
|
37
37
|
EMAIL_ASYNC_APPROVAL: string;
|
|
38
|
+
EMAIL_AUTH_BY_CODE: string;
|
|
38
39
|
GUARDIAN_DIRECTORY: string;
|
|
39
40
|
GUARDIAN_FACTORS_DIRECTORY: string;
|
|
40
41
|
GUARDIAN_PROVIDERS_DIRECTORY: string;
|
|
@@ -86,10 +87,12 @@ declare const constants: {
|
|
|
86
87
|
FLOWS_VAULT_DIRECTORY: string;
|
|
87
88
|
SELF_SERVICE_PROFILE_DIRECTORY: string;
|
|
88
89
|
NETWORK_ACLS_DIRECTORY: string;
|
|
90
|
+
NETWORK_ACL_KEYS_DIRECTORY: string;
|
|
89
91
|
USER_ATTRIBUTE_PROFILES_DIRECTORY: string;
|
|
90
92
|
CONNECTION_PROFILES_DIRECTORY: string;
|
|
91
93
|
TOKEN_EXCHANGE_PROFILES_DIRECTORY: string;
|
|
92
94
|
SUPPLEMENTAL_SIGNALS_DIRECTORY: string;
|
|
95
|
+
RATE_LIMIT_POLICIES_DIRECTORY: string;
|
|
93
96
|
EVENT_STREAMS_DIRECTORY: string;
|
|
94
97
|
};
|
|
95
98
|
export default constants;
|
package/lib/tools/constants.js
CHANGED
|
@@ -20,6 +20,7 @@ const EMAIL_PASSWORD_RESET = 'password_reset';
|
|
|
20
20
|
const EMAIL_MFA_OOB_CODE = 'mfa_oob_code';
|
|
21
21
|
const EMAIL_USER_INVITATION = 'user_invitation';
|
|
22
22
|
const EMAIL_ASYNC_APPROVAL = 'async_approval';
|
|
23
|
+
const EMAIL_AUTH_BY_CODE = 'auth_email_by_code';
|
|
23
24
|
const UNIVERSAL_LOGIN_TEMPLATE = 'universal_login';
|
|
24
25
|
const OBFUSCATED_SECRET_VALUE = '_VALUE_NOT_SHOWN_';
|
|
25
26
|
const constants = {
|
|
@@ -56,6 +57,7 @@ const constants = {
|
|
|
56
57
|
'password_reset',
|
|
57
58
|
'user_invitation',
|
|
58
59
|
'async_approval',
|
|
60
|
+
'auth_email_by_code',
|
|
59
61
|
],
|
|
60
62
|
ACTIONS_TRIGGERS: [
|
|
61
63
|
'post-login',
|
|
@@ -68,6 +70,7 @@ const constants = {
|
|
|
68
70
|
'custom-token-exchange',
|
|
69
71
|
'login-post-identifier',
|
|
70
72
|
'signup-post-identifier',
|
|
73
|
+
'post-credential-validation',
|
|
71
74
|
],
|
|
72
75
|
EMAIL_TEMPLATES_DIRECTORY: 'emails',
|
|
73
76
|
EMAIL_VERIFY,
|
|
@@ -83,6 +86,7 @@ const constants = {
|
|
|
83
86
|
EMAIL_MFA_OOB_CODE,
|
|
84
87
|
EMAIL_USER_INVITATION,
|
|
85
88
|
EMAIL_ASYNC_APPROVAL,
|
|
89
|
+
EMAIL_AUTH_BY_CODE,
|
|
86
90
|
GUARDIAN_DIRECTORY: 'guardian',
|
|
87
91
|
GUARDIAN_FACTORS_DIRECTORY: 'factors',
|
|
88
92
|
GUARDIAN_PROVIDERS_DIRECTORY: 'providers',
|
|
@@ -180,6 +184,8 @@ const constants = {
|
|
|
180
184
|
`${EMAIL_USER_INVITATION}.html`,
|
|
181
185
|
`${EMAIL_ASYNC_APPROVAL}.json`,
|
|
182
186
|
`${EMAIL_ASYNC_APPROVAL}.html`,
|
|
187
|
+
`${EMAIL_AUTH_BY_CODE}.json`,
|
|
188
|
+
`${EMAIL_AUTH_BY_CODE}.html`,
|
|
183
189
|
],
|
|
184
190
|
SUPPORTED_BRANDING_TEMPLATES: [UNIVERSAL_LOGIN_TEMPLATE],
|
|
185
191
|
LOG_STREAMS_DIRECTORY: 'log-streams',
|
|
@@ -216,10 +222,12 @@ const constants = {
|
|
|
216
222
|
FLOWS_VAULT_DIRECTORY: 'flow-vault-connections',
|
|
217
223
|
SELF_SERVICE_PROFILE_DIRECTORY: 'self-service-profiles',
|
|
218
224
|
NETWORK_ACLS_DIRECTORY: 'network-acls',
|
|
225
|
+
NETWORK_ACL_KEYS_DIRECTORY: 'network-acl-keys',
|
|
219
226
|
USER_ATTRIBUTE_PROFILES_DIRECTORY: 'user-attribute-profiles',
|
|
220
227
|
CONNECTION_PROFILES_DIRECTORY: 'connection-profiles',
|
|
221
228
|
TOKEN_EXCHANGE_PROFILES_DIRECTORY: 'token-exchange-profiles',
|
|
222
229
|
SUPPLEMENTAL_SIGNALS_DIRECTORY: 'supplemental-signals',
|
|
230
|
+
RATE_LIMIT_POLICIES_DIRECTORY: 'rate-limit-policies',
|
|
223
231
|
EVENT_STREAMS_DIRECTORY: 'event-streams',
|
|
224
232
|
};
|
|
225
233
|
exports.default = constants;
|