auth0-deploy-cli 8.31.0 → 8.33.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 +46 -1
- package/lib/args.d.ts +4 -0
- package/lib/args.js +18 -0
- package/lib/commands/import.js +40 -3
- package/lib/context/directory/handlers/clients.js +14 -0
- package/lib/context/directory/handlers/connections.js +1 -0
- package/lib/context/directory/handlers/guardianFactors.js +8 -6
- package/lib/context/directory/handlers/tenant.d.ts +1 -6
- package/lib/context/directory/handlers/tenant.js +0 -1
- package/lib/context/index.js +5 -0
- package/lib/context/yaml/handlers/clients.js +12 -0
- package/lib/context/yaml/handlers/connections.js +1 -17
- package/lib/context/yaml/handlers/guardianFactors.js +3 -1
- package/lib/context/yaml/handlers/tenant.js +1 -1
- package/lib/sessionDurationsToMinutes.d.ts +9 -7
- package/lib/sessionDurationsToMinutes.js +11 -10
- package/lib/tools/auth0/handlers/actions.d.ts +1 -0
- package/lib/tools/auth0/handlers/actions.js +68 -29
- package/lib/tools/auth0/handlers/attackProtection.js +15 -0
- package/lib/tools/auth0/handlers/branding.js +7 -0
- package/lib/tools/auth0/handlers/clientGrants.d.ts +5 -0
- package/lib/tools/auth0/handlers/clientGrants.js +31 -9
- package/lib/tools/auth0/handlers/clients.d.ts +62 -2
- package/lib/tools/auth0/handlers/clients.js +187 -22
- package/lib/tools/auth0/handlers/connections.d.ts +18 -0
- package/lib/tools/auth0/handlers/connections.js +86 -5
- package/lib/tools/auth0/handlers/databases.d.ts +91 -0
- package/lib/tools/auth0/handlers/databases.js +162 -2
- package/lib/tools/auth0/handlers/default.d.ts +4 -0
- package/lib/tools/auth0/handlers/default.js +82 -4
- package/lib/tools/auth0/handlers/emailProvider.js +8 -0
- package/lib/tools/auth0/handlers/emailTemplates.js +7 -0
- package/lib/tools/auth0/handlers/flowVaultConnections.js +6 -1
- package/lib/tools/auth0/handlers/flows.js +13 -4
- package/lib/tools/auth0/handlers/forms.js +10 -1
- package/lib/tools/auth0/handlers/guardianFactors.js +7 -1
- package/lib/tools/auth0/handlers/guardianPhoneFactorMessageTypes.js +8 -1
- package/lib/tools/auth0/handlers/guardianPhoneFactorSelectedProvider.js +8 -1
- package/lib/tools/auth0/handlers/guardianPolicies.js +7 -0
- package/lib/tools/auth0/handlers/hooks.d.ts +1 -0
- package/lib/tools/auth0/handlers/hooks.js +10 -0
- package/lib/tools/auth0/handlers/organizations.d.ts +11 -1
- package/lib/tools/auth0/handlers/organizations.js +48 -52
- package/lib/tools/auth0/handlers/pages.js +7 -0
- package/lib/tools/auth0/handlers/phoneProvider.js +7 -0
- package/lib/tools/auth0/handlers/prompts.js +7 -0
- package/lib/tools/auth0/handlers/resourceServers.d.ts +1 -2
- package/lib/tools/auth0/handlers/resourceServers.js +9 -25
- package/lib/tools/auth0/handlers/roles.js +7 -0
- package/lib/tools/auth0/handlers/rules.d.ts +1 -0
- package/lib/tools/auth0/handlers/rules.js +9 -0
- package/lib/tools/auth0/handlers/rulesConfigs.d.ts +1 -0
- package/lib/tools/auth0/handlers/rulesConfigs.js +3 -0
- package/lib/tools/auth0/handlers/scimHandler.js +8 -1
- package/lib/tools/auth0/handlers/selfServiceProfiles.d.ts +1 -1
- package/lib/tools/auth0/handlers/selfServiceProfiles.js +10 -17
- package/lib/tools/auth0/handlers/tenant.d.ts +18 -1
- package/lib/tools/auth0/handlers/tenant.js +48 -4
- package/lib/tools/auth0/handlers/themes.js +11 -2
- package/lib/tools/auth0/handlers/triggers.js +6 -0
- package/lib/tools/auth0/handlers/userAttributeProfiles.js +16 -10
- package/lib/tools/auth0/index.d.ts +3 -0
- package/lib/tools/auth0/index.js +204 -0
- package/lib/tools/calculateChanges.d.ts +2 -2
- package/lib/tools/calculateChanges.js +21 -6
- package/lib/tools/calculateDryRunChanges.d.ts +71 -0
- package/lib/tools/calculateDryRunChanges.js +492 -0
- package/lib/tools/deploy.js +25 -1
- package/lib/tools/utils.d.ts +5 -0
- package/lib/tools/utils.js +64 -3
- package/lib/types.d.ts +16 -0
- package/lib/utils.d.ts +7 -0
- package/lib/utils.js +33 -1
- package/package.json +7 -4
|
@@ -4,6 +4,10 @@ import { Asset, Assets } from '../../../types';
|
|
|
4
4
|
export declare const schema: {
|
|
5
5
|
type: string;
|
|
6
6
|
properties: {
|
|
7
|
+
client_id_metadata_document_supported: {
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
};
|
|
7
11
|
default_token_quota: {
|
|
8
12
|
type: string;
|
|
9
13
|
properties: {
|
|
@@ -65,9 +69,22 @@ export declare const schema: {
|
|
|
65
69
|
type: string[];
|
|
66
70
|
description: string;
|
|
67
71
|
};
|
|
72
|
+
resource_parameter_profile: {
|
|
73
|
+
type: string;
|
|
74
|
+
enum: string[];
|
|
75
|
+
description: string;
|
|
76
|
+
};
|
|
77
|
+
dynamic_client_registration_security_mode: {
|
|
78
|
+
type: string;
|
|
79
|
+
enum: string[];
|
|
80
|
+
description: string;
|
|
81
|
+
};
|
|
68
82
|
};
|
|
69
83
|
};
|
|
70
|
-
export
|
|
84
|
+
export interface Tenant extends Management.GetTenantSettingsResponseContent {
|
|
85
|
+
client_id_metadata_document_supported?: boolean;
|
|
86
|
+
resource_parameter_profile?: Management.TenantSettingsResourceParameterProfile;
|
|
87
|
+
}
|
|
71
88
|
type TenantSettingsFlags = Management.TenantSettingsFlags;
|
|
72
89
|
export declare const allowedTenantFlags: string[];
|
|
73
90
|
export declare const removeUnallowedTenantFlags: (proposedFlags: TenantSettingsFlags | undefined) => TenantSettingsFlags;
|
|
@@ -43,11 +43,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.removeUnallowedTenantFlags = exports.allowedTenantFlags = exports.schema = void 0;
|
|
46
|
+
const lodash_1 = require("lodash");
|
|
47
|
+
const nconf_1 = __importDefault(require("nconf"));
|
|
46
48
|
const validationError_1 = __importDefault(require("../../validationError"));
|
|
47
49
|
const default_1 = __importStar(require("./default"));
|
|
48
50
|
const pages_1 = require("./pages");
|
|
49
51
|
const utils_1 = require("../../utils");
|
|
50
52
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
53
|
+
const sessionDurationsToMinutes_1 = __importDefault(require("../../../sessionDurationsToMinutes"));
|
|
51
54
|
const tokenQuotaConfigurationSchema = {
|
|
52
55
|
type: 'object',
|
|
53
56
|
properties: {
|
|
@@ -76,6 +79,10 @@ const tokenQuotaConfigurationSchema = {
|
|
|
76
79
|
exports.schema = {
|
|
77
80
|
type: 'object',
|
|
78
81
|
properties: {
|
|
82
|
+
client_id_metadata_document_supported: {
|
|
83
|
+
type: 'boolean',
|
|
84
|
+
description: 'Whether the authorization server supports retrieving client metadata from a client_id URL.',
|
|
85
|
+
},
|
|
79
86
|
default_token_quota: {
|
|
80
87
|
type: 'object',
|
|
81
88
|
properties: {
|
|
@@ -89,6 +96,16 @@ exports.schema = {
|
|
|
89
96
|
type: ['boolean', 'null'],
|
|
90
97
|
description: 'Whether to skip the confirmation prompt for non-verifiable callback URIs',
|
|
91
98
|
},
|
|
99
|
+
resource_parameter_profile: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
enum: ['audience', 'compatibility'],
|
|
102
|
+
description: 'OAuth resource parameter compatibility mode for specifying the protected resource.',
|
|
103
|
+
},
|
|
104
|
+
dynamic_client_registration_security_mode: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
enum: ['strict', 'permissive'],
|
|
107
|
+
description: 'Indicates the security mode for new clients created through the Dynamic Client Registration endpoint.',
|
|
108
|
+
},
|
|
92
109
|
},
|
|
93
110
|
};
|
|
94
111
|
const blockPageKeys = [
|
|
@@ -148,8 +165,14 @@ const removeUnallowedTenantFlags = (proposedFlags) => {
|
|
|
148
165
|
};
|
|
149
166
|
}, {});
|
|
150
167
|
if (removedFlags.length > 0) {
|
|
151
|
-
|
|
152
|
-
${removedFlags.length > 1 ? 'These flags' : 'This flag'} can likely be removed from the tenant definition file. If you believe this removal is an error, please report via a Github issue
|
|
168
|
+
const logMsg = `The following tenant flag${removedFlags.length > 1 ? 's have not been' : ' has not been'} updated because deemed incompatible with the target tenant: ${removedFlags.join(', ')}
|
|
169
|
+
${removedFlags.length > 1 ? 'These flags' : 'This flag'} can likely be removed from the tenant definition file. If you believe this removal is an error, please report via a Github issue.`;
|
|
170
|
+
if (nconf_1.default.get('AUTH0_DRY_RUN')) {
|
|
171
|
+
logger_1.default.debug(logMsg);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
logger_1.default.warn(logMsg);
|
|
175
|
+
}
|
|
153
176
|
}
|
|
154
177
|
return filteredFlags;
|
|
155
178
|
};
|
|
@@ -187,6 +210,12 @@ class TenantHandler extends default_1.default {
|
|
|
187
210
|
// Do nothing if not set
|
|
188
211
|
if (!tenant)
|
|
189
212
|
return;
|
|
213
|
+
if ((0, utils_1.isDryRun)(this.config)) {
|
|
214
|
+
const { update } = await this.calcChanges(assets);
|
|
215
|
+
if (update.length === 0) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
190
219
|
const updatedTenant = {
|
|
191
220
|
...tenant,
|
|
192
221
|
flags: tenant.flags
|
|
@@ -204,9 +233,24 @@ class TenantHandler extends default_1.default {
|
|
|
204
233
|
'The flag will still be applied for now but will be removed in a future release.');
|
|
205
234
|
}
|
|
206
235
|
if (updatedTenant && Object.keys(updatedTenant).length > 0) {
|
|
207
|
-
|
|
236
|
+
const sessionDurations = (0, sessionDurationsToMinutes_1.default)({
|
|
237
|
+
session_lifetime: updatedTenant?.session_lifetime,
|
|
238
|
+
idle_session_lifetime: updatedTenant?.idle_session_lifetime,
|
|
239
|
+
ephemeral_session_lifetime: updatedTenant?.ephemeral_session_lifetime,
|
|
240
|
+
idle_ephemeral_session_lifetime: updatedTenant?.idle_ephemeral_session_lifetime,
|
|
241
|
+
});
|
|
242
|
+
let updateTenantPayload = updatedTenant;
|
|
243
|
+
if (!(0, lodash_1.isEmpty)(sessionDurations)) {
|
|
244
|
+
updateTenantPayload = { ...updateTenantPayload, ...sessionDurations };
|
|
245
|
+
// context: https://github.com/auth0/auth0-deploy-cli/pull/471
|
|
246
|
+
delete updateTenantPayload.session_lifetime;
|
|
247
|
+
delete updateTenantPayload.idle_session_lifetime;
|
|
248
|
+
delete updateTenantPayload.ephemeral_session_lifetime;
|
|
249
|
+
delete updateTenantPayload.idle_ephemeral_session_lifetime;
|
|
250
|
+
}
|
|
251
|
+
await this.client.tenants.settings.update(updateTenantPayload);
|
|
208
252
|
this.updated += 1;
|
|
209
|
-
this.didUpdate(
|
|
253
|
+
this.didUpdate(updateTenantPayload);
|
|
210
254
|
}
|
|
211
255
|
}
|
|
212
256
|
}
|
|
@@ -45,6 +45,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
45
45
|
exports.schema = void 0;
|
|
46
46
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
47
47
|
const default_1 = __importStar(require("./default"));
|
|
48
|
+
const utils_1 = require("../../utils");
|
|
48
49
|
/**
|
|
49
50
|
* Schema
|
|
50
51
|
*/
|
|
@@ -480,11 +481,19 @@ class ThemesHandler extends default_1.default {
|
|
|
480
481
|
if (!themes) {
|
|
481
482
|
return;
|
|
482
483
|
}
|
|
484
|
+
if ((0, utils_1.isDryRun)(this.config)) {
|
|
485
|
+
const { del, update } = await this.calcChanges(assets);
|
|
486
|
+
if (update.length === 0 && del.length === 0) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
483
490
|
// Empty array means themes should be deleted
|
|
484
491
|
if (themes.length === 0) {
|
|
485
|
-
|
|
492
|
+
await this.deleteThemes();
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
await this.updateThemes(themes);
|
|
486
496
|
}
|
|
487
|
-
return this.updateThemes(themes);
|
|
488
497
|
}
|
|
489
498
|
async deleteThemes() {
|
|
490
499
|
if (!this.config('AUTH0_ALLOW_DELETE')) {
|
|
@@ -122,6 +122,12 @@ class TriggersHandler extends default_1.default {
|
|
|
122
122
|
// Do nothing if not set
|
|
123
123
|
if (!triggers)
|
|
124
124
|
return;
|
|
125
|
+
if ((0, utils_1.isDryRun)(this.config)) {
|
|
126
|
+
const { update } = await this.calcChanges(assets);
|
|
127
|
+
if (update.length === 0) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
125
131
|
await (0, utils_1.sleep)(2000); // Delay to allow newly-deployed actions to register in backend
|
|
126
132
|
await Promise.all(Object.entries(triggers).map(async ([name, data]) => {
|
|
127
133
|
const bindings = data.map((binding) => ({
|
|
@@ -46,7 +46,7 @@ exports.getUserAttributeProfiles = exports.schema = void 0;
|
|
|
46
46
|
const default_1 = __importStar(require("./default"));
|
|
47
47
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
48
48
|
const client_1 = require("../client");
|
|
49
|
-
const
|
|
49
|
+
const utils_1 = require("../../utils");
|
|
50
50
|
const strategies = ['pingfederate', 'ad', 'adfs', 'waad', 'google-apps', 'okta', 'oidc', 'samlp'];
|
|
51
51
|
const strategyOverrides = {
|
|
52
52
|
type: 'object',
|
|
@@ -147,7 +147,6 @@ exports.schema = {
|
|
|
147
147
|
},
|
|
148
148
|
scim_mapping: {
|
|
149
149
|
type: 'string',
|
|
150
|
-
default: 'externalId',
|
|
151
150
|
},
|
|
152
151
|
},
|
|
153
152
|
},
|
|
@@ -269,14 +268,21 @@ class UserAttributeProfilesHandler extends default_1.default {
|
|
|
269
268
|
// Do nothing if not set
|
|
270
269
|
if (!userAttributeProfiles)
|
|
271
270
|
return;
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
271
|
+
const { del, update, create, conflicts } = await this.calcChanges(assets);
|
|
272
|
+
if ((0, utils_1.isDryRun)(this.config)) {
|
|
273
|
+
if (create.length === 0 &&
|
|
274
|
+
update.length === 0 &&
|
|
275
|
+
del.length === 0 &&
|
|
276
|
+
conflicts.length === 0) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const changes = {
|
|
281
|
+
del,
|
|
282
|
+
update,
|
|
283
|
+
create,
|
|
284
|
+
conflicts,
|
|
285
|
+
};
|
|
280
286
|
logger_1.default.debug(`Start processChanges for userAttributeProfile [delete:${changes.del.length}] [update:${changes.update.length}], [create:${changes.create.length}]`);
|
|
281
287
|
await super.processChanges(assets, changes);
|
|
282
288
|
}
|
package/lib/tools/auth0/index.js
CHANGED
|
@@ -4,9 +4,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const ajv_1 = __importDefault(require("ajv/lib/ajv"));
|
|
7
|
+
const lodash_1 = require("lodash");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const prompts_1 = require("@clack/prompts");
|
|
10
|
+
const promise_pool_executor_1 = require("promise-pool-executor");
|
|
7
11
|
const client_1 = __importDefault(require("./client"));
|
|
8
12
|
const schema_1 = __importDefault(require("./schema"));
|
|
9
13
|
const handlers_1 = __importDefault(require("./handlers"));
|
|
14
|
+
const calculateDryRunChanges_1 = require("../calculateDryRunChanges");
|
|
15
|
+
const utils_1 = require("../../utils");
|
|
16
|
+
const utils_2 = require("../utils");
|
|
10
17
|
/**
|
|
11
18
|
* Sorts handlers by their @order decorator metadata for a given stage.
|
|
12
19
|
* Handlers are sorted in ascending order (lower values execute first).
|
|
@@ -29,6 +36,30 @@ function sortByOrder(toSort, stage) {
|
|
|
29
36
|
});
|
|
30
37
|
return sorted;
|
|
31
38
|
}
|
|
39
|
+
function buildTableBorder(left, middle, right, widths) {
|
|
40
|
+
return `${left}${widths.map((width) => '─'.repeat(width + 2)).join(middle)}${right}`;
|
|
41
|
+
}
|
|
42
|
+
function formatStatusCell(status, width) {
|
|
43
|
+
let statusColor;
|
|
44
|
+
const baseStatus = status.replace(' *', '');
|
|
45
|
+
switch (baseStatus) {
|
|
46
|
+
case 'CREATE':
|
|
47
|
+
statusColor = chalk_1.default.green;
|
|
48
|
+
break;
|
|
49
|
+
case 'UPDATE':
|
|
50
|
+
statusColor = chalk_1.default.yellow;
|
|
51
|
+
break;
|
|
52
|
+
case 'DELETE':
|
|
53
|
+
statusColor = chalk_1.default.red;
|
|
54
|
+
break;
|
|
55
|
+
default:
|
|
56
|
+
statusColor = chalk_1.default.white;
|
|
57
|
+
}
|
|
58
|
+
if (status.includes(' *')) {
|
|
59
|
+
return `${statusColor(baseStatus.padEnd(width - 2))}${chalk_1.default.dim(' *')}`;
|
|
60
|
+
}
|
|
61
|
+
return statusColor(baseStatus.padEnd(width));
|
|
62
|
+
}
|
|
32
63
|
class Auth0 {
|
|
33
64
|
constructor(client, assets, config) {
|
|
34
65
|
this.client = (0, client_1.default)(client);
|
|
@@ -91,5 +122,178 @@ class Auth0 {
|
|
|
91
122
|
async processChanges() {
|
|
92
123
|
await this.runStage('processChanges');
|
|
93
124
|
}
|
|
125
|
+
async dryRun(opts = {}) {
|
|
126
|
+
const isDebug = process.env.AUTH0_DEBUG === 'true';
|
|
127
|
+
const s = (0, prompts_1.spinner)();
|
|
128
|
+
if (isDebug) {
|
|
129
|
+
prompts_1.log.info('Preparing dry run preview...\n');
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
s.start('Preparing dry run preview...');
|
|
133
|
+
}
|
|
134
|
+
// Collect changes from all handlers
|
|
135
|
+
const allChanges = {};
|
|
136
|
+
const savedAssets = this.assets;
|
|
137
|
+
this.assets = await (0, calculateDryRunChanges_1.dryRunFormatAssets)((0, lodash_1.cloneDeep)(this.assets), this.client);
|
|
138
|
+
const dryRunPool = new promise_pool_executor_1.PromisePoolExecutor({
|
|
139
|
+
concurrencyLimit: 2,
|
|
140
|
+
frequencyLimit: 8,
|
|
141
|
+
frequencyWindow: 1000, // 1 sec
|
|
142
|
+
});
|
|
143
|
+
await dryRunPool
|
|
144
|
+
.addEachTask({
|
|
145
|
+
data: this.handlers,
|
|
146
|
+
generator: (handler) => (async () => {
|
|
147
|
+
try {
|
|
148
|
+
const detailedChanges = [];
|
|
149
|
+
let created = 0;
|
|
150
|
+
let updated = 0;
|
|
151
|
+
let deleted = 0;
|
|
152
|
+
if (isDebug) {
|
|
153
|
+
prompts_1.log.info(`Calculating dry run changes for ${handler.type}...`);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
s.message(`Calculating dry run changes for ${handler.type}...`);
|
|
157
|
+
}
|
|
158
|
+
const changes = await handler.dryRunChanges(this.assets);
|
|
159
|
+
if (changes.create) {
|
|
160
|
+
changes.create.forEach((item) => {
|
|
161
|
+
detailedChanges.push({
|
|
162
|
+
action: 'CREATE',
|
|
163
|
+
identifier: handler.getResourceName(item),
|
|
164
|
+
details: item,
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
created = changes.create.length;
|
|
168
|
+
}
|
|
169
|
+
if (changes.update) {
|
|
170
|
+
changes.update.forEach((item) => {
|
|
171
|
+
detailedChanges.push({
|
|
172
|
+
action: 'UPDATE',
|
|
173
|
+
identifier: handler.getResourceName(item),
|
|
174
|
+
details: item,
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
updated = changes.update.length;
|
|
178
|
+
}
|
|
179
|
+
if (changes.del) {
|
|
180
|
+
changes.del.forEach((item) => {
|
|
181
|
+
detailedChanges.push({
|
|
182
|
+
action: 'DELETE',
|
|
183
|
+
identifier: handler.getResourceName(item),
|
|
184
|
+
details: item,
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
deleted = changes.del.length;
|
|
188
|
+
}
|
|
189
|
+
allChanges[handler.type] = {
|
|
190
|
+
created,
|
|
191
|
+
updated,
|
|
192
|
+
deleted,
|
|
193
|
+
changes: detailedChanges,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
err.type = handler.type;
|
|
198
|
+
err.stage = 'dryRun';
|
|
199
|
+
throw err;
|
|
200
|
+
}
|
|
201
|
+
})(),
|
|
202
|
+
})
|
|
203
|
+
.promise();
|
|
204
|
+
// Restore original assets so processChanges() works correctly after dryRun()
|
|
205
|
+
this.assets = savedAssets;
|
|
206
|
+
if (!isDebug) {
|
|
207
|
+
(0, utils_2.printCLIMessage)('');
|
|
208
|
+
s.stop('Done\n');
|
|
209
|
+
}
|
|
210
|
+
// Build formatted table output
|
|
211
|
+
const allowDelete = this.config('AUTH0_ALLOW_DELETE') === 'true' || this.config('AUTH0_ALLOW_DELETE') === true;
|
|
212
|
+
const tenantDomain = this.config('AUTH0_DOMAIN');
|
|
213
|
+
const inputPath = this.config('AUTH0_INPUT_FILE') || './tenant-config-directory/';
|
|
214
|
+
let output = '\n';
|
|
215
|
+
output += chalk_1.default.bold('Auth0 Deploy CLI - Dry Run Preview\n\n');
|
|
216
|
+
output += chalk_1.default.bold('Tenant:') + ` ${tenantDomain}\n`;
|
|
217
|
+
output += chalk_1.default.bold('Input:') + ` ${inputPath}\n\n`;
|
|
218
|
+
output += 'Simulating deployment... The following changes are proposed:\n\n';
|
|
219
|
+
const tableData = [];
|
|
220
|
+
const resourceTypes = Object.keys(allChanges).sort();
|
|
221
|
+
resourceTypes.forEach((type) => {
|
|
222
|
+
const typeChanges = allChanges[type];
|
|
223
|
+
if (typeChanges.changes.length > 0) {
|
|
224
|
+
const typeName = type.charAt(0).toUpperCase() + type.slice(1);
|
|
225
|
+
typeChanges.changes.forEach((change, index) => {
|
|
226
|
+
const deleteNote = change.action === 'DELETE' && !allowDelete ? ' *' : '';
|
|
227
|
+
tableData.push({
|
|
228
|
+
resource: index === 0 ? typeName : '',
|
|
229
|
+
status: change.action + deleteNote,
|
|
230
|
+
name: change.identifier,
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
if (tableData.length > 0) {
|
|
236
|
+
const resourceWidth = Math.max(10, ...tableData.map((d) => d.resource.length));
|
|
237
|
+
const statusWidth = Math.max(8, ...tableData.map((d) => d.status.length));
|
|
238
|
+
const nameWidth = Math.max(25, ...tableData.map((d) => d.name.length));
|
|
239
|
+
const widths = [resourceWidth, statusWidth, nameWidth];
|
|
240
|
+
const topBorder = buildTableBorder('┌', '┬', '┐', widths);
|
|
241
|
+
const headerSeparator = buildTableBorder('├', '┼', '┤', widths);
|
|
242
|
+
const bottomBorder = buildTableBorder('└', '┴', '┘', widths);
|
|
243
|
+
output += `${topBorder}\n`;
|
|
244
|
+
output += `│ ${chalk_1.default.bold('Resource'.padEnd(resourceWidth))} │ ${chalk_1.default.bold('Status'.padEnd(statusWidth))} │ ${chalk_1.default.bold('Name / Identifier'.padEnd(nameWidth))} │\n`;
|
|
245
|
+
output += `${headerSeparator}\n`;
|
|
246
|
+
tableData.forEach((row) => {
|
|
247
|
+
output += `│ ${row.resource.padEnd(resourceWidth)} │ ${formatStatusCell(row.status, statusWidth)} │ ${row.name.padEnd(nameWidth)} │\n`;
|
|
248
|
+
});
|
|
249
|
+
output += `${bottomBorder}\n`;
|
|
250
|
+
if (!allowDelete && tableData.some((d) => d.status.includes('*'))) {
|
|
251
|
+
output += '\n' + chalk_1.default.dim('* Requires AUTH0_ALLOW_DELETE to be enabled');
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
output += chalk_1.default.dim('No changes detected.');
|
|
256
|
+
}
|
|
257
|
+
output += '\n\n';
|
|
258
|
+
output += chalk_1.default.green('Dry Run completed successfully.') + '\n';
|
|
259
|
+
(0, utils_2.printCLIMessage)(output);
|
|
260
|
+
const hasChanges = tableData.length > 0;
|
|
261
|
+
const shouldApplyAfterPreview = (0, utils_1.isTruthy)(this.config('AUTH0_DRY_RUN_APPLY'));
|
|
262
|
+
if (opts.interactive && process.stdout.isTTY && !shouldApplyAfterPreview && hasChanges) {
|
|
263
|
+
(0, prompts_1.intro)(chalk_1.default.inverse(' dry-run '));
|
|
264
|
+
const selectedType = await (0, prompts_1.select)({
|
|
265
|
+
message: 'What would you like to do?',
|
|
266
|
+
options: [
|
|
267
|
+
{ value: 'dry-run-apply', label: 'Apply changes' },
|
|
268
|
+
{
|
|
269
|
+
value: 'dry-run-export',
|
|
270
|
+
label: 'Export changes in a file',
|
|
271
|
+
hint: 'No Apply',
|
|
272
|
+
},
|
|
273
|
+
{ value: 'dry-run-exit', label: 'Exit', hint: '' },
|
|
274
|
+
],
|
|
275
|
+
});
|
|
276
|
+
switch (selectedType) {
|
|
277
|
+
case 'dry-run-apply':
|
|
278
|
+
(0, utils_2.printCLIMessage)('\n' + chalk_1.default.green('Applying changes...') + '\n');
|
|
279
|
+
await this.processChanges();
|
|
280
|
+
break;
|
|
281
|
+
case 'dry-run-export': {
|
|
282
|
+
const fileName = 'dry-run-diff-log.json';
|
|
283
|
+
await (0, calculateDryRunChanges_1.exportDiffLog)(fileName);
|
|
284
|
+
(0, utils_2.printCLIMessage)('\n' + chalk_1.default.cyan(`Exported on ./${fileName}`) + '\n');
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
case 'dry-run-exit':
|
|
288
|
+
(0, utils_2.printCLIMessage)('\n' + chalk_1.default.yellow('Deployment cancelled. No changes were made.') + '\n');
|
|
289
|
+
process.exit(0);
|
|
290
|
+
break;
|
|
291
|
+
default:
|
|
292
|
+
(0, utils_2.printCLIMessage)(chalk_1.default.red('Invalid option selected.'));
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return hasChanges;
|
|
297
|
+
}
|
|
94
298
|
}
|
|
95
299
|
exports.default = Auth0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import APIHandler from '
|
|
1
|
+
import APIHandler from './auth0/handlers/default';
|
|
2
2
|
import { Asset, CalculatedChanges } from '../types';
|
|
3
3
|
/**
|
|
4
4
|
* @template T
|
|
@@ -20,7 +20,7 @@ export declare function processChangedObjectFields({ handler, desiredAssetState,
|
|
|
20
20
|
export declare function calculateChanges({ handler, assets, existing, identifiers, allowDelete, }: {
|
|
21
21
|
handler: APIHandler;
|
|
22
22
|
assets: Asset[];
|
|
23
|
-
existing: Asset[] | null;
|
|
23
|
+
existing: Asset[] | Asset | null;
|
|
24
24
|
identifiers: string[];
|
|
25
25
|
allowDelete: boolean;
|
|
26
26
|
}): CalculatedChanges;
|
|
@@ -67,8 +67,14 @@ function processChangedObjectFields({ handler, desiredAssetState, currentAssetSt
|
|
|
67
67
|
function calculateChanges({ handler, assets, existing, identifiers = ['id', 'name'], allowDelete, }) {
|
|
68
68
|
// Calculate the changes required between two sets of assets.
|
|
69
69
|
const update = [];
|
|
70
|
-
let del = [
|
|
71
|
-
|
|
70
|
+
let del = [];
|
|
71
|
+
if (existing) {
|
|
72
|
+
del = Array.isArray(existing) ? [...existing] : [existing];
|
|
73
|
+
}
|
|
74
|
+
let create = [];
|
|
75
|
+
if (assets) {
|
|
76
|
+
create = Array.isArray(assets) ? [...assets] : [assets];
|
|
77
|
+
}
|
|
72
78
|
const conflicts = [];
|
|
73
79
|
const findByKeyValue = (key, value, arr) => arr.find((e) => {
|
|
74
80
|
if (Array.isArray(key)) {
|
|
@@ -124,10 +130,9 @@ function calculateChanges({ handler, assets, existing, identifiers = ['id', 'nam
|
|
|
124
130
|
// Loop through identifiers (in order) to try match assets to existing
|
|
125
131
|
// If existing then update if not create
|
|
126
132
|
// The remainder will be deleted
|
|
127
|
-
|
|
128
|
-
// eslint-disable-line
|
|
133
|
+
identifiers.forEach((id) => {
|
|
129
134
|
processAssets(id, [...create]);
|
|
130
|
-
}
|
|
135
|
+
});
|
|
131
136
|
// Check if there are assets with names that will conflict with existing names during the update process
|
|
132
137
|
// This will rename those assets to a temp random name first
|
|
133
138
|
// This assumes the first identifiers is the unique identifier
|
|
@@ -138,7 +143,17 @@ function calculateChanges({ handler, assets, existing, identifiers = ['id', 'nam
|
|
|
138
143
|
// If the conflicting item is going to be deleted then skip
|
|
139
144
|
const inDeleted = del.filter((e) => e.name === a.name && e[uniqueID] !== a[uniqueID])[0];
|
|
140
145
|
if (!inDeleted) {
|
|
141
|
-
|
|
146
|
+
let existingArray;
|
|
147
|
+
if (Array.isArray(existing)) {
|
|
148
|
+
existingArray = existing;
|
|
149
|
+
}
|
|
150
|
+
else if (existing) {
|
|
151
|
+
existingArray = [existing];
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
existingArray = [];
|
|
155
|
+
}
|
|
156
|
+
const conflict = existingArray.filter((e) => e.name === a.name && e[uniqueID] !== a[uniqueID])[0];
|
|
142
157
|
if (conflict) {
|
|
143
158
|
const temp = Math.random().toString(36).substr(2, 5);
|
|
144
159
|
conflicts.push({
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dry-run change calculation utilities.
|
|
3
|
+
*
|
|
4
|
+
* This module handles all logic specific to the --dry-run mode:
|
|
5
|
+
* - Comparing local vs. remote assets to detect creates, updates, and deletes
|
|
6
|
+
* - Normalizing asset representations (session durations, client name→ID, profiles)
|
|
7
|
+
* - Tracking and exporting a structured diff log
|
|
8
|
+
*
|
|
9
|
+
* The core deploy-time change calculation lives in calculateChanges.ts.
|
|
10
|
+
*/
|
|
11
|
+
import { Asset, Assets, Auth0APIClient, CalculatedChanges } from '../types';
|
|
12
|
+
export declare const getDiffLog: (resourceTypeName?: string) => string[] | {
|
|
13
|
+
[key: string]: string[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Writes the accumulated diff log to a JSON file on disk.
|
|
17
|
+
* Useful for CI pipelines that want a machine-readable dry-run report.
|
|
18
|
+
*/
|
|
19
|
+
export declare const exportDiffLog: (fileName: string, resourceTypeName?: string) => Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Compares two objects and returns an array of human-readable difference strings.
|
|
22
|
+
* Only considers keys present in `localObj` — extra keys in `remoteObj` are ignored.
|
|
23
|
+
*
|
|
24
|
+
* @param localObj - Local (desired) state
|
|
25
|
+
* @param remoteObj - Remote (current) state
|
|
26
|
+
* @param keyObjPath - Dot-separated JSON path used for nested tracking
|
|
27
|
+
* @param resourceTypeName - Resource type label used in log output
|
|
28
|
+
* @param ignoreDryRunFields - Field paths to skip during comparison
|
|
29
|
+
* @returns Array of difference descriptions, empty if objects are equivalent
|
|
30
|
+
*/
|
|
31
|
+
export declare function getObjectDifferences(localObj: Record<string, any>, remoteObj: Record<string, any>, keyObjPath?: string, resourceTypeName?: string, ignoreDryRunFields?: string[]): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Returns true if `localObj` and `remoteObj` differ in any field present in
|
|
34
|
+
* `localObj`. Side-effect: appends found differences to the diff log.
|
|
35
|
+
*/
|
|
36
|
+
export declare function hasObjectDifferences(localObj: Record<string, any>, remoteObj: Record<string, any>, keyObjPath?: string, resourceTypeName?: string, ignoreDryRunFields?: string[]): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Calculates the changes required between local and remote asset sets for dry-run operations.
|
|
39
|
+
*
|
|
40
|
+
* Unlike `calculateChanges`, this function does not mutate assets for API consumption.
|
|
41
|
+
* It is purely for reporting: determining which assets would be created, updated, or deleted.
|
|
42
|
+
*
|
|
43
|
+
* @param params.type - Resource type label used for logging
|
|
44
|
+
* @param params.assets - Local assets to be deployed
|
|
45
|
+
* @param params.existing - Remote assets currently on the tenant, or null
|
|
46
|
+
* @param params.identifiers - Fields used to match local↔remote assets (default: ['id', 'name'])
|
|
47
|
+
* @param params.ignoreDryRunFields - Fields to exclude from diff comparisons
|
|
48
|
+
*/
|
|
49
|
+
export declare function calculateDryRunChanges({ type, assets, existing, identifiers, ignoreDryRunFields, }: {
|
|
50
|
+
type: string;
|
|
51
|
+
assets: Asset[] | Asset;
|
|
52
|
+
existing: Asset[] | Asset | null;
|
|
53
|
+
identifiers: string[];
|
|
54
|
+
ignoreDryRunFields: string[];
|
|
55
|
+
}): CalculatedChanges;
|
|
56
|
+
/**
|
|
57
|
+
* Normalizes local assets so they can be accurately compared against remote assets.
|
|
58
|
+
*
|
|
59
|
+
* Transformations applied:
|
|
60
|
+
* - Converts client name references to client IDs (clientGrants, databases, connections, resourceServers)
|
|
61
|
+
* - Resolves connection profile and user attribute profile names to IDs (express_configuration)
|
|
62
|
+
* - Decodes base64-encoded SAML certificates to PEM strings
|
|
63
|
+
* - Resolves organization connection names to connection IDs
|
|
64
|
+
* - Normalizes tenant session duration fields
|
|
65
|
+
* - Removes empty branding templates (avoids false diff against remote)
|
|
66
|
+
*
|
|
67
|
+
* @param localAssets - Local assets object (will be mutated)
|
|
68
|
+
* @param authAPIclient - Auth0 management API client for fetching remote reference data
|
|
69
|
+
* @returns The mutated assets object with normalized values
|
|
70
|
+
*/
|
|
71
|
+
export declare function dryRunFormatAssets(localAssets: Assets, authAPIclient: Auth0APIClient): Promise<Assets>;
|