auth0-deploy-cli 8.26.0 → 8.27.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 +20 -1
- package/lib/tools/auth0/handlers/clientGrants.js +23 -5
- package/lib/tools/auth0/handlers/clients.js +3 -6
- package/lib/tools/auth0/handlers/connections.d.ts +2 -2
- package/lib/tools/auth0/handlers/connections.js +33 -10
- package/lib/tools/auth0/handlers/databases.d.ts +14 -2
- package/lib/tools/auth0/handlers/databases.js +88 -24
- package/lib/tools/auth0/handlers/scimHandler.js +6 -0
- package/lib/tools/auth0/handlers/selfServiceProfiles.d.ts +8 -0
- package/lib/tools/auth0/handlers/selfServiceProfiles.js +9 -0
- package/lib/tools/utils.d.ts +11 -0
- package/lib/tools/utils.js +24 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [8.27.0] - 2026-02-13
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Add support for `custom_password_hash.action_id` in `databases` (Universal Custom Password Hash EA). [#1288]
|
|
15
|
+
- Add support for `allowed_strategies` in `selfServiceProfiles`. [#1298]
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Fix validation handling for `authentication_methods.password.enabled` and `disable_self_service_change_password` in `databases`. [#1297]
|
|
20
|
+
- Fix stripping deprecated `enabled_clients` for `connections` with enhanced client management. [#1294]
|
|
21
|
+
- Fix exclude third-party `clientGrants` when `AUTH0_EXCLUDE_THIRD_PARTY_CLIENTS` is enabled. [#1289]
|
|
22
|
+
|
|
10
23
|
## [8.26.0] - 2026-01-30
|
|
11
24
|
|
|
12
25
|
### Added
|
|
@@ -1634,7 +1647,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1634
1647
|
[#1282]: https://github.com/auth0/auth0-deploy-cli/issues/1282
|
|
1635
1648
|
[#1283]: https://github.com/auth0/auth0-deploy-cli/issues/1283
|
|
1636
1649
|
[#1284]: https://github.com/auth0/auth0-deploy-cli/issues/1284
|
|
1637
|
-
[
|
|
1650
|
+
[#1288]: https://github.com/auth0/auth0-deploy-cli/issues/1288
|
|
1651
|
+
[#1289]: https://github.com/auth0/auth0-deploy-cli/issues/1289
|
|
1652
|
+
[#1294]: https://github.com/auth0/auth0-deploy-cli/issues/1294
|
|
1653
|
+
[#1297]: https://github.com/auth0/auth0-deploy-cli/issues/1297
|
|
1654
|
+
[#1298]: https://github.com/auth0/auth0-deploy-cli/issues/1298
|
|
1655
|
+
[Unreleased]: https://github.com/auth0/auth0-deploy-cli/compare/v8.27.0...HEAD
|
|
1656
|
+
[8.27.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.26.0...v8.27.0
|
|
1638
1657
|
[8.26.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.25.0...v8.26.0
|
|
1639
1658
|
[8.25.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.24.0...v8.25.0
|
|
1640
1659
|
[8.24.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.23.2...v8.24.0
|
|
@@ -117,6 +117,15 @@ class ClientGrantsHandler extends default_1.default {
|
|
|
117
117
|
// As it could cause problems if the grants are deleted or updated etc
|
|
118
118
|
const currentClient = this.config('AUTH0_CLIENT_ID');
|
|
119
119
|
this.existing = this.existing.filter((grant) => grant.client_id !== currentClient);
|
|
120
|
+
// Filter out third-party client grants when AUTH0_EXCLUDE_THIRD_PARTY_CLIENTS is enabled
|
|
121
|
+
if ((0, utils_1.shouldExcludeThirdPartyClients)(this.config)) {
|
|
122
|
+
const clients = await (0, client_1.paginate)(this.client.clients.list, {
|
|
123
|
+
paginate: true,
|
|
124
|
+
is_first_party: true,
|
|
125
|
+
});
|
|
126
|
+
const firstPartyClientIds = new Set(clients.map((c) => c.client_id));
|
|
127
|
+
this.existing = this.existing.filter((grant) => firstPartyClientIds.has(grant.client_id));
|
|
128
|
+
}
|
|
120
129
|
return this.existing;
|
|
121
130
|
}
|
|
122
131
|
// Run after clients are updated so we can convert client_id names to id's
|
|
@@ -140,19 +149,28 @@ class ClientGrantsHandler extends default_1.default {
|
|
|
140
149
|
});
|
|
141
150
|
// Always filter out the client we are using to access Auth0 Management API
|
|
142
151
|
const currentClient = this.config('AUTH0_CLIENT_ID');
|
|
152
|
+
// Build a set of third-party client IDs for efficient lookup
|
|
153
|
+
const thirdPartyClientIds = new Set(clients.filter((c) => c.is_first_party === false).map((c) => c.client_id));
|
|
143
154
|
const { del, update, create, conflicts } = await this.calcChanges({
|
|
144
155
|
...assets,
|
|
145
156
|
clientGrants: formatted,
|
|
146
157
|
});
|
|
147
158
|
const filterGrants = (list) => {
|
|
159
|
+
let filtered = list;
|
|
160
|
+
// Filter out the current client (Auth0 Management API client)
|
|
161
|
+
filtered = filtered.filter((item) => item.client_id !== currentClient);
|
|
162
|
+
// Filter out excluded clients
|
|
148
163
|
if (excludedClients.length) {
|
|
149
|
-
|
|
150
|
-
item.client_id &&
|
|
164
|
+
filtered = filtered.filter((item) => item.client_id &&
|
|
151
165
|
![...excludedClientsByNames, ...excludedClients].includes(item.client_id));
|
|
152
166
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
167
|
+
// Filter out system grants
|
|
168
|
+
filtered = filtered.filter((item) => item.is_system !== true);
|
|
169
|
+
// Filter out third-party client grants when flag is enabled
|
|
170
|
+
if ((0, utils_1.shouldExcludeThirdPartyClients)(this.config)) {
|
|
171
|
+
filtered = filtered.filter((item) => !thirdPartyClientIds.has(item.client_id));
|
|
172
|
+
}
|
|
173
|
+
return filtered;
|
|
156
174
|
};
|
|
157
175
|
const changes = {
|
|
158
176
|
// @ts-ignore because this expects `client_id` and that's not yet typed on Asset
|
|
@@ -10,6 +10,7 @@ const default_1 = __importDefault(require("./default"));
|
|
|
10
10
|
const connectionProfiles_1 = require("./connectionProfiles");
|
|
11
11
|
const userAttributeProfiles_1 = require("./userAttributeProfiles");
|
|
12
12
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
13
|
+
const utils_1 = require("../../utils");
|
|
13
14
|
const multiResourceRefreshTokenPoliciesSchema = {
|
|
14
15
|
type: ['array', 'null'],
|
|
15
16
|
description: 'A collection of policies governing multi-resource refresh token exchange (MRRT), defining how refresh tokens can be used across different resource servers',
|
|
@@ -397,8 +398,6 @@ class ClientHandler extends default_1.default {
|
|
|
397
398
|
return;
|
|
398
399
|
assets.clients = await this.sanitizeMapExpressConfiguration(this.client, clients);
|
|
399
400
|
const excludedClients = (assets.exclude && assets.exclude.clients) || [];
|
|
400
|
-
const excludeThirdPartyClients = this.config('AUTH0_EXCLUDE_THIRD_PARTY_CLIENTS') === 'true' ||
|
|
401
|
-
this.config('AUTH0_EXCLUDE_THIRD_PARTY_CLIENTS') === true;
|
|
402
401
|
const { del, update, create, conflicts } = await this.calcChanges(assets);
|
|
403
402
|
// Always filter out the client we are using to access Auth0 Management API
|
|
404
403
|
// As it could cause problems if it gets deleted or updated etc
|
|
@@ -412,7 +411,7 @@ class ClientHandler extends default_1.default {
|
|
|
412
411
|
const filterClients = (list) => list.filter((item) => item.client_id !== currentClient &&
|
|
413
412
|
item.name &&
|
|
414
413
|
!excludedClients.includes(item.name) &&
|
|
415
|
-
(!
|
|
414
|
+
(!(0, utils_1.shouldExcludeThirdPartyClients)(this.config) || item.is_first_party));
|
|
416
415
|
// Sanitize client fields
|
|
417
416
|
const sanitizeClientFields = (list) => {
|
|
418
417
|
const sanitizedClients = createClientSanitizer(list)
|
|
@@ -447,12 +446,10 @@ class ClientHandler extends default_1.default {
|
|
|
447
446
|
async getType() {
|
|
448
447
|
if (this.existing)
|
|
449
448
|
return this.existing;
|
|
450
|
-
const excludeThirdPartyClients = this.config('AUTH0_EXCLUDE_THIRD_PARTY_CLIENTS') === 'true' ||
|
|
451
|
-
this.config('AUTH0_EXCLUDE_THIRD_PARTY_CLIENTS') === true;
|
|
452
449
|
const clients = await (0, client_1.paginate)(this.client.clients.list, {
|
|
453
450
|
paginate: true,
|
|
454
451
|
is_global: false,
|
|
455
|
-
...(
|
|
452
|
+
...((0, utils_1.shouldExcludeThirdPartyClients)(this.config) && { is_first_party: true }),
|
|
456
453
|
});
|
|
457
454
|
this.existing = createClientSanitizer(clients).sanitizeCrossOriginAuth().get();
|
|
458
455
|
return this.existing;
|
|
@@ -141,7 +141,7 @@ export declare const getConnectionEnabledClients: (auth0Client: Auth0APIClient,
|
|
|
141
141
|
* @returns Promise that resolves to true if the update was successful, false otherwise
|
|
142
142
|
*
|
|
143
143
|
*/
|
|
144
|
-
export declare const updateConnectionEnabledClients: (auth0Client: Auth0APIClient, typeName: string, connectionId: string, enabledClientIds: string[]) => Promise<boolean>;
|
|
144
|
+
export declare const updateConnectionEnabledClients: (auth0Client: Auth0APIClient, typeName: string, connectionId: string, enabledClientIds: string[], existingConnections: Asset[] | Asset | null) => Promise<boolean>;
|
|
145
145
|
/**
|
|
146
146
|
* This function processes enabled clients for create, update, and conflict operations.
|
|
147
147
|
* Note: This function mutates the `create` array by adding IDs to the connection objects after creation.
|
|
@@ -153,7 +153,7 @@ export declare const updateConnectionEnabledClients: (auth0Client: Auth0APIClien
|
|
|
153
153
|
*
|
|
154
154
|
* @returns A Promise that resolves when all enabled client updates are complete
|
|
155
155
|
*/
|
|
156
|
-
export declare const processConnectionEnabledClients: (auth0Client: Auth0APIClient, typeName: string, changes: CalculatedChanges, delayMs?: number) => Promise<void>;
|
|
156
|
+
export declare const processConnectionEnabledClients: (auth0Client: Auth0APIClient, typeName: string, existingConnections: Asset[] | null, changes: CalculatedChanges, delayMs?: number) => Promise<void>;
|
|
157
157
|
export default class ConnectionsHandler extends DefaultAPIHandler {
|
|
158
158
|
existing: Connection[] | null;
|
|
159
159
|
scimHandler: ScimHandler;
|
|
@@ -200,13 +200,36 @@ exports.getConnectionEnabledClients = getConnectionEnabledClients;
|
|
|
200
200
|
* @returns Promise that resolves to true if the update was successful, false otherwise
|
|
201
201
|
*
|
|
202
202
|
*/
|
|
203
|
-
const updateConnectionEnabledClients = async (auth0Client, typeName, connectionId, enabledClientIds) => {
|
|
203
|
+
const updateConnectionEnabledClients = async (auth0Client, typeName, connectionId, enabledClientIds, existingConnections) => {
|
|
204
204
|
if (!connectionId || !Array.isArray(enabledClientIds) || !enabledClientIds.length)
|
|
205
205
|
return false;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
206
|
+
let existingEnabledClients = [];
|
|
207
|
+
if (Array.isArray(existingConnections)) {
|
|
208
|
+
const existingConnection = existingConnections.find((con) => con.id === connectionId);
|
|
209
|
+
existingEnabledClients = existingConnection?.enabled_clients ?? [];
|
|
210
|
+
}
|
|
211
|
+
// Determine which clients to enable vs. disable by comparing the incoming `enabledClientIds` with the `existingEnabledClients`.
|
|
212
|
+
const enabledClientIdSet = new Set(enabledClientIds);
|
|
213
|
+
const existingClientIdSet = new Set(existingEnabledClients);
|
|
214
|
+
// If both sets are identical, skip the update entirely.
|
|
215
|
+
if (enabledClientIdSet.size === existingClientIdSet.size &&
|
|
216
|
+
[...enabledClientIdSet].every((id) => existingClientIdSet.has(id))) {
|
|
217
|
+
logger_1.default.debug(`Enabled clients for ${typeName}: ${connectionId} are unchanged, skipping update`);
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
const clientsToEnable = enabledClientIds;
|
|
221
|
+
// Any client that exists on the tenant but not in the provided `enabledClientIds` should be disabled.
|
|
222
|
+
const clientsToDisable = existingEnabledClients.filter((clientId) => !enabledClientIdSet.has(clientId));
|
|
223
|
+
const enabledClientUpdatePayloads = [
|
|
224
|
+
...clientsToEnable.map((clientId) => ({
|
|
225
|
+
client_id: clientId,
|
|
226
|
+
status: true,
|
|
227
|
+
})),
|
|
228
|
+
...clientsToDisable.map((clientId) => ({
|
|
229
|
+
client_id: clientId,
|
|
230
|
+
status: false,
|
|
231
|
+
})),
|
|
232
|
+
];
|
|
210
233
|
const payloadChunks = (0, lodash_1.chunk)(enabledClientUpdatePayloads, 50);
|
|
211
234
|
try {
|
|
212
235
|
await Promise.all(payloadChunks.map((payload) => auth0Client.connections.clients.update(connectionId, payload)));
|
|
@@ -230,7 +253,7 @@ exports.updateConnectionEnabledClients = updateConnectionEnabledClients;
|
|
|
230
253
|
*
|
|
231
254
|
* @returns A Promise that resolves when all enabled client updates are complete
|
|
232
255
|
*/
|
|
233
|
-
const processConnectionEnabledClients = async (auth0Client, typeName, changes, delayMs = 2500 // Default delay is 2.5 seconds
|
|
256
|
+
const processConnectionEnabledClients = async (auth0Client, typeName, existingConnections, changes, delayMs = 2500 // Default delay is 2.5 seconds
|
|
234
257
|
) => {
|
|
235
258
|
const { create, update, conflicts } = changes;
|
|
236
259
|
let createWithId = [];
|
|
@@ -267,9 +290,9 @@ const processConnectionEnabledClients = async (auth0Client, typeName, changes, d
|
|
|
267
290
|
// Process enabled clients for each change type
|
|
268
291
|
// Delete is handled by the `processChanges` method, removed connection completely
|
|
269
292
|
await Promise.all([
|
|
270
|
-
...createWithId.map((conn) => (0, exports.updateConnectionEnabledClients)(auth0Client, typeName, conn.id, conn.enabled_clients)),
|
|
271
|
-
...update.map((conn) => (0, exports.updateConnectionEnabledClients)(auth0Client, typeName, conn.id, conn.enabled_clients)),
|
|
272
|
-
...conflicts.map((conn) => (0, exports.updateConnectionEnabledClients)(auth0Client, typeName, conn.id, conn.enabled_clients)),
|
|
293
|
+
...createWithId.map((conn) => (0, exports.updateConnectionEnabledClients)(auth0Client, typeName, conn.id, conn.enabled_clients, existingConnections)),
|
|
294
|
+
...update.map((conn) => (0, exports.updateConnectionEnabledClients)(auth0Client, typeName, conn.id, conn.enabled_clients, existingConnections)),
|
|
295
|
+
...conflicts.map((conn) => (0, exports.updateConnectionEnabledClients)(auth0Client, typeName, conn.id, conn.enabled_clients, existingConnections)),
|
|
273
296
|
]);
|
|
274
297
|
};
|
|
275
298
|
exports.processConnectionEnabledClients = processConnectionEnabledClients;
|
|
@@ -543,7 +566,7 @@ class ConnectionsHandler extends default_1.default {
|
|
|
543
566
|
changes = (0, utils_1.filterIncluded)(changes, includedConnections);
|
|
544
567
|
await super.processChanges(assets, changes);
|
|
545
568
|
// process enabled clients
|
|
546
|
-
await (0, exports.processConnectionEnabledClients)(this.client, this.type, changes);
|
|
569
|
+
await (0, exports.processConnectionEnabledClients)(this.client, this.type, await this.existing, changes);
|
|
547
570
|
// process directory provisioning
|
|
548
571
|
await this.processConnectionDirectoryProvisioning(changes);
|
|
549
572
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import DefaultAPIHandler from './default';
|
|
2
|
-
import { CalculatedChanges, Assets
|
|
2
|
+
import { CalculatedChanges, Assets } from '../../../types';
|
|
3
|
+
import { Connection } from './connections';
|
|
4
|
+
import { Action } from './actions';
|
|
3
5
|
export declare const schema: {
|
|
4
6
|
type: string;
|
|
5
7
|
items: {
|
|
@@ -180,6 +182,14 @@ export declare const schema: {
|
|
|
180
182
|
};
|
|
181
183
|
};
|
|
182
184
|
};
|
|
185
|
+
custom_password_hash: {
|
|
186
|
+
type: string;
|
|
187
|
+
properties: {
|
|
188
|
+
action_id: {
|
|
189
|
+
type: string;
|
|
190
|
+
};
|
|
191
|
+
};
|
|
192
|
+
};
|
|
183
193
|
};
|
|
184
194
|
};
|
|
185
195
|
};
|
|
@@ -187,13 +197,15 @@ export declare const schema: {
|
|
|
187
197
|
};
|
|
188
198
|
};
|
|
189
199
|
export default class DatabaseHandler extends DefaultAPIHandler {
|
|
200
|
+
existing: Connection[] | null;
|
|
190
201
|
constructor(config: DefaultAPIHandler);
|
|
191
202
|
objString(db: any): string;
|
|
203
|
+
getFormattedOptions(options: any, actions?: Action[]): any;
|
|
192
204
|
validate(assets: Assets): Promise<void>;
|
|
193
205
|
private validatePasswordlessSettings;
|
|
194
206
|
private validateEmailUniqueConstraints;
|
|
195
207
|
getClientFN(fn: 'create' | 'delete' | 'getAll' | 'update'): Function;
|
|
196
|
-
getType(): Promise<
|
|
208
|
+
getType(): Promise<Connection[]>;
|
|
197
209
|
calcChanges(assets: Assets): Promise<CalculatedChanges>;
|
|
198
210
|
processChanges(assets: Assets): Promise<void>;
|
|
199
211
|
}
|
|
@@ -173,6 +173,12 @@ exports.schema = {
|
|
|
173
173
|
},
|
|
174
174
|
},
|
|
175
175
|
},
|
|
176
|
+
custom_password_hash: {
|
|
177
|
+
type: 'object',
|
|
178
|
+
properties: {
|
|
179
|
+
action_id: { type: 'string' },
|
|
180
|
+
},
|
|
181
|
+
},
|
|
176
182
|
},
|
|
177
183
|
},
|
|
178
184
|
},
|
|
@@ -190,6 +196,22 @@ class DatabaseHandler extends default_1.default {
|
|
|
190
196
|
objString(db) {
|
|
191
197
|
return super.objString({ name: db.name, id: db.id });
|
|
192
198
|
}
|
|
199
|
+
getFormattedOptions(options, actions = []) {
|
|
200
|
+
try {
|
|
201
|
+
const formattedOptions = { ...options };
|
|
202
|
+
// Handle custom_password_hash.action_id conversion
|
|
203
|
+
if (options?.custom_password_hash?.action_id) {
|
|
204
|
+
formattedOptions.custom_password_hash = {
|
|
205
|
+
...options.custom_password_hash,
|
|
206
|
+
action_id: (0, utils_1.convertActionNameToId)(options.custom_password_hash.action_id, actions),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return formattedOptions;
|
|
210
|
+
}
|
|
211
|
+
catch (e) {
|
|
212
|
+
return {};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
193
215
|
async validate(assets) {
|
|
194
216
|
const { databases } = assets;
|
|
195
217
|
// Do nothing if not set
|
|
@@ -198,7 +220,7 @@ class DatabaseHandler extends default_1.default {
|
|
|
198
220
|
// Validate each database
|
|
199
221
|
databases.forEach((database) => {
|
|
200
222
|
this.validateEmailUniqueConstraints(database);
|
|
201
|
-
this.validatePasswordlessSettings(database);
|
|
223
|
+
// this.validatePasswordlessSettings(database); // Enable only the feature is GA PR:#1282
|
|
202
224
|
});
|
|
203
225
|
await super.validate(assets);
|
|
204
226
|
}
|
|
@@ -242,7 +264,12 @@ class DatabaseHandler extends default_1.default {
|
|
|
242
264
|
getClientFN(fn) {
|
|
243
265
|
// Override this as a database is actually a connection but we are treating them as a different object
|
|
244
266
|
if (fn === 'create') {
|
|
245
|
-
return (payload) =>
|
|
267
|
+
return (payload) => {
|
|
268
|
+
// Remove deprecated enabled_clients field
|
|
269
|
+
if ('enabled_clients' in payload)
|
|
270
|
+
delete payload.enabled_clients;
|
|
271
|
+
return this.client.connections.create(payload);
|
|
272
|
+
};
|
|
246
273
|
}
|
|
247
274
|
// If we going to update database, we need to get current options first
|
|
248
275
|
if (fn === 'update') {
|
|
@@ -265,6 +292,9 @@ class DatabaseHandler extends default_1.default {
|
|
|
265
292
|
if (payload.options && Object.keys(payload.options).length === 0) {
|
|
266
293
|
delete payload.options;
|
|
267
294
|
}
|
|
295
|
+
// Remove deprecated enabled_clients field
|
|
296
|
+
if ('enabled_clients' in payload)
|
|
297
|
+
delete payload.enabled_clients;
|
|
268
298
|
return this.client.connections.update(id, payload);
|
|
269
299
|
});
|
|
270
300
|
}
|
|
@@ -273,25 +303,52 @@ class DatabaseHandler extends default_1.default {
|
|
|
273
303
|
async getType() {
|
|
274
304
|
if (this.existing)
|
|
275
305
|
return this.existing;
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
306
|
+
// Fetch connections and actions concurrently
|
|
307
|
+
const [connections, actions] = await Promise.all([
|
|
308
|
+
(0, client_1.paginate)(this.client.connections.list, {
|
|
309
|
+
strategy: [auth0_1.Management.ConnectionStrategyEnum.Auth0],
|
|
310
|
+
checkpoint: true,
|
|
311
|
+
}),
|
|
312
|
+
(0, client_1.paginate)(this.client.actions.list, {
|
|
313
|
+
paginate: true,
|
|
314
|
+
include_totals: true,
|
|
315
|
+
}),
|
|
316
|
+
]);
|
|
280
317
|
const dbConnectionsWithEnabledClients = await Promise.all(connections.map(async (con) => {
|
|
281
318
|
if (!con?.id)
|
|
282
319
|
return con;
|
|
283
320
|
const enabledClients = await (0, connections_1.getConnectionEnabledClients)(this.client, con.id);
|
|
321
|
+
const connection = { ...con };
|
|
284
322
|
if (enabledClients && enabledClients?.length) {
|
|
285
|
-
|
|
323
|
+
connection.enabled_clients = enabledClients;
|
|
286
324
|
}
|
|
287
|
-
return
|
|
325
|
+
return connection;
|
|
288
326
|
}));
|
|
327
|
+
// Convert action ID back to action name for export
|
|
328
|
+
const dbConnectionsWithActionNames = dbConnectionsWithEnabledClients.map((connection) => {
|
|
329
|
+
if (connection.options && 'custom_password_hash' in connection.options) {
|
|
330
|
+
const customPasswordHash = connection.options?.custom_password_hash;
|
|
331
|
+
if (customPasswordHash?.action_id) {
|
|
332
|
+
return {
|
|
333
|
+
...connection,
|
|
334
|
+
options: {
|
|
335
|
+
...connection.options,
|
|
336
|
+
custom_password_hash: {
|
|
337
|
+
...customPasswordHash,
|
|
338
|
+
action_id: (0, utils_1.convertActionIdToName)(customPasswordHash.action_id, actions),
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return connection;
|
|
345
|
+
});
|
|
289
346
|
// If options option is empty for all connection, log the missing options scope.
|
|
290
|
-
const isOptionExists =
|
|
347
|
+
const isOptionExists = dbConnectionsWithActionNames.every((c) => c.options && Object.keys(c.options).length > 0);
|
|
291
348
|
if (!isOptionExists) {
|
|
292
349
|
logger_1.default.warn(`Insufficient scope the read:connections_options scope is required to get ${this.type} options.`);
|
|
293
350
|
}
|
|
294
|
-
this.existing =
|
|
351
|
+
this.existing = dbConnectionsWithActionNames;
|
|
295
352
|
return this.existing;
|
|
296
353
|
}
|
|
297
354
|
async calcChanges(assets) {
|
|
@@ -305,22 +362,29 @@ class DatabaseHandler extends default_1.default {
|
|
|
305
362
|
conflicts: [],
|
|
306
363
|
};
|
|
307
364
|
// Convert enabled_clients by name to the id
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
365
|
+
// Fetch clients, connections, and actions concurrently
|
|
366
|
+
const [clients, existingDatabasesConnections, actions] = await Promise.all([
|
|
367
|
+
(0, client_1.paginate)(this.client.clients.list, {
|
|
368
|
+
paginate: true,
|
|
369
|
+
}),
|
|
370
|
+
(0, client_1.paginate)(this.client.connections.list, {
|
|
371
|
+
strategy: [auth0_1.Management.ConnectionStrategyEnum.Auth0],
|
|
372
|
+
checkpoint: true,
|
|
373
|
+
include_totals: true,
|
|
374
|
+
}),
|
|
375
|
+
(0, client_1.paginate)(this.client.actions.list, {
|
|
376
|
+
paginate: true,
|
|
377
|
+
include_totals: true,
|
|
378
|
+
}),
|
|
379
|
+
]);
|
|
316
380
|
const formatted = databases.map((db) => {
|
|
381
|
+
const { options, ...rest } = db;
|
|
382
|
+
const formattedOptions = this.getFormattedOptions(options, actions);
|
|
383
|
+
const formattedDb = { ...rest, options: formattedOptions };
|
|
317
384
|
if (db.enabled_clients) {
|
|
318
|
-
|
|
319
|
-
...db,
|
|
320
|
-
enabled_clients: (0, utils_1.getEnabledClients)(assets, db, existingDatabasesConnections, clients),
|
|
321
|
-
};
|
|
385
|
+
formattedDb.enabled_clients = (0, utils_1.getEnabledClients)(assets, db, existingDatabasesConnections, clients);
|
|
322
386
|
}
|
|
323
|
-
return
|
|
387
|
+
return formattedDb;
|
|
324
388
|
});
|
|
325
389
|
return super.calcChanges({ ...assets, databases: formatted });
|
|
326
390
|
}
|
|
@@ -339,7 +403,7 @@ class DatabaseHandler extends default_1.default {
|
|
|
339
403
|
const changes = await this.calcChanges(assets);
|
|
340
404
|
await super.processChanges(assets, (0, utils_1.filterExcluded)(changes, excludedConnections));
|
|
341
405
|
// process enabled clients
|
|
342
|
-
await (0, connections_1.processConnectionEnabledClients)(this.client, this.type, (0, utils_1.filterExcluded)(changes, excludedConnections));
|
|
406
|
+
await (0, connections_1.processConnectionEnabledClients)(this.client, this.type, await this.existing, (0, utils_1.filterExcluded)(changes, excludedConnections));
|
|
343
407
|
}
|
|
344
408
|
}
|
|
345
409
|
exports.default = DatabaseHandler;
|
|
@@ -191,6 +191,9 @@ class ScimHandler {
|
|
|
191
191
|
const { scim_configuration: scimBodyParams } = bodyParams;
|
|
192
192
|
delete bodyParams.scim_configuration;
|
|
193
193
|
delete bodyParams.directory_provisioning_configuration;
|
|
194
|
+
// Remove deprecated enabled_clients field
|
|
195
|
+
if ('enabled_clients' in bodyParams)
|
|
196
|
+
delete bodyParams.enabled_clients;
|
|
194
197
|
// First, update `connections`.
|
|
195
198
|
const updated = await this.connectionsManager.update(connectionId, bodyParams);
|
|
196
199
|
const idMapEntry = this.idMap.get(connectionId);
|
|
@@ -225,6 +228,9 @@ class ScimHandler {
|
|
|
225
228
|
const { scim_configuration: scimBodyParams } = bodyParams;
|
|
226
229
|
delete bodyParams.scim_configuration;
|
|
227
230
|
delete bodyParams.directory_provisioning_configuration;
|
|
231
|
+
// Remove deprecated enabled_clients field
|
|
232
|
+
if ('enabled_clients' in bodyParams)
|
|
233
|
+
delete bodyParams.enabled_clients;
|
|
228
234
|
// First, create the new `connection`.
|
|
229
235
|
const data = await this.connectionsManager.create(bodyParams);
|
|
230
236
|
if (data?.id && scimBodyParams && this.scimScopes.create) {
|
|
@@ -48,6 +48,14 @@ export declare const schema: {
|
|
|
48
48
|
};
|
|
49
49
|
};
|
|
50
50
|
};
|
|
51
|
+
allowed_strategies: {
|
|
52
|
+
type: string;
|
|
53
|
+
description: string;
|
|
54
|
+
items: {
|
|
55
|
+
type: string;
|
|
56
|
+
enum: ("pingfederate" | "adfs" | "waad" | "google-apps" | "okta" | "oidc" | "samlp" | "auth0-samlp" | "okta-samlp" | "keycloak-samlp")[];
|
|
57
|
+
};
|
|
58
|
+
};
|
|
51
59
|
branding: {
|
|
52
60
|
type: string;
|
|
53
61
|
properties: {
|
|
@@ -43,6 +43,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.schema = void 0;
|
|
46
|
+
const auth0_1 = require("auth0");
|
|
46
47
|
const lodash_1 = require("lodash");
|
|
47
48
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
48
49
|
const default_1 = __importStar(require("./default"));
|
|
@@ -86,6 +87,14 @@ exports.schema = {
|
|
|
86
87
|
},
|
|
87
88
|
},
|
|
88
89
|
},
|
|
90
|
+
allowed_strategies: {
|
|
91
|
+
type: 'array',
|
|
92
|
+
description: 'List of IdP strategies that will be shown to users during the Self-Service SSO flow.',
|
|
93
|
+
items: {
|
|
94
|
+
type: 'string',
|
|
95
|
+
enum: Object.values(auth0_1.Management.SelfServiceProfileAllowedStrategyEnum),
|
|
96
|
+
},
|
|
97
|
+
},
|
|
89
98
|
branding: {
|
|
90
99
|
type: 'object',
|
|
91
100
|
properties: {
|
package/lib/tools/utils.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export declare function keywordStringReplace(input: string, mappings: KeywordMap
|
|
|
6
6
|
export declare function keywordReplace(input: string | undefined, mappings: KeywordMappings): string;
|
|
7
7
|
export declare function wrapArrayReplaceMarkersInQuotes(body: string, mappings: KeywordMappings): string;
|
|
8
8
|
export declare function convertClientNameToId(name: string, clients: Asset[]): string;
|
|
9
|
+
export declare function convertActionNameToId(name: string, actions: Asset[]): string;
|
|
10
|
+
export declare function convertActionIdToName(id: string, actions: Asset[]): string;
|
|
9
11
|
export declare function convertClientNamesToIds(names: string[], clients: Asset[]): string[];
|
|
10
12
|
export declare function loadFileAndReplaceKeywords(file: string, { mappings, disableKeywordReplacement, }: {
|
|
11
13
|
mappings: KeywordMappings;
|
|
@@ -44,3 +46,12 @@ export declare function maskSecretAtPath({ resourceTypeName, maskedKeyName, mask
|
|
|
44
46
|
maskOnObj: object;
|
|
45
47
|
keyJsonPath: string;
|
|
46
48
|
}): any;
|
|
49
|
+
/**
|
|
50
|
+
* Determines whether third-party clients should be excluded based on configuration.
|
|
51
|
+
* Checks the AUTH0_EXCLUDE_THIRD_PARTY_CLIENTS config value and returns true if it's
|
|
52
|
+
* set to boolean true or string 'true'.
|
|
53
|
+
*
|
|
54
|
+
* @param configFn - The configuration function to retrieve the config value.
|
|
55
|
+
* @returns True if third-party clients should be excluded, false otherwise.
|
|
56
|
+
*/
|
|
57
|
+
export declare const shouldExcludeThirdPartyClients: (configFn: (key: string) => any) => boolean;
|
package/lib/tools/utils.js
CHANGED
|
@@ -36,12 +36,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.isForbiddenFeatureError = exports.isDeprecatedError = exports.detectInsufficientScopeError = exports.stripObfuscatedFieldsFromPayload = exports.obfuscateSensitiveValues = exports.keywordReplaceStringRegExp = exports.keywordReplaceArrayRegExp = void 0;
|
|
39
|
+
exports.shouldExcludeThirdPartyClients = exports.isForbiddenFeatureError = exports.isDeprecatedError = exports.detectInsufficientScopeError = exports.stripObfuscatedFieldsFromPayload = exports.obfuscateSensitiveValues = exports.keywordReplaceStringRegExp = exports.keywordReplaceArrayRegExp = void 0;
|
|
40
40
|
exports.keywordArrayReplace = keywordArrayReplace;
|
|
41
41
|
exports.keywordStringReplace = keywordStringReplace;
|
|
42
42
|
exports.keywordReplace = keywordReplace;
|
|
43
43
|
exports.wrapArrayReplaceMarkersInQuotes = wrapArrayReplaceMarkersInQuotes;
|
|
44
44
|
exports.convertClientNameToId = convertClientNameToId;
|
|
45
|
+
exports.convertActionNameToId = convertActionNameToId;
|
|
46
|
+
exports.convertActionIdToName = convertActionIdToName;
|
|
45
47
|
exports.convertClientNamesToIds = convertClientNamesToIds;
|
|
46
48
|
exports.loadFileAndReplaceKeywords = loadFileAndReplaceKeywords;
|
|
47
49
|
exports.flatten = flatten;
|
|
@@ -115,6 +117,14 @@ function convertClientNameToId(name, clients) {
|
|
|
115
117
|
const found = clients.find((c) => c.name === name);
|
|
116
118
|
return (found && found.client_id) || name;
|
|
117
119
|
}
|
|
120
|
+
function convertActionNameToId(name, actions) {
|
|
121
|
+
const found = actions.find((a) => a.name === name);
|
|
122
|
+
return (found && found.id) || name;
|
|
123
|
+
}
|
|
124
|
+
function convertActionIdToName(id, actions) {
|
|
125
|
+
const found = actions.find((a) => a.id === id);
|
|
126
|
+
return (found && found.name) || id;
|
|
127
|
+
}
|
|
118
128
|
function convertClientNamesToIds(names, clients) {
|
|
119
129
|
const resolvedNames = names.map((name) => ({ name, resolved: false }));
|
|
120
130
|
const result = clients.reduce((acc, client) => {
|
|
@@ -311,3 +321,16 @@ function maskSecretAtPath({ resourceTypeName, maskedKeyName, maskOnObj, keyJsonP
|
|
|
311
321
|
}
|
|
312
322
|
return maskOnObj;
|
|
313
323
|
}
|
|
324
|
+
/**
|
|
325
|
+
* Determines whether third-party clients should be excluded based on configuration.
|
|
326
|
+
* Checks the AUTH0_EXCLUDE_THIRD_PARTY_CLIENTS config value and returns true if it's
|
|
327
|
+
* set to boolean true or string 'true'.
|
|
328
|
+
*
|
|
329
|
+
* @param configFn - The configuration function to retrieve the config value.
|
|
330
|
+
* @returns True if third-party clients should be excluded, false otherwise.
|
|
331
|
+
*/
|
|
332
|
+
const shouldExcludeThirdPartyClients = (configFn) => {
|
|
333
|
+
const value = configFn('AUTH0_EXCLUDE_THIRD_PARTY_CLIENTS');
|
|
334
|
+
return value === 'true' || value === true;
|
|
335
|
+
};
|
|
336
|
+
exports.shouldExcludeThirdPartyClients = shouldExcludeThirdPartyClients;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auth0-deploy-cli",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.27.0",
|
|
4
4
|
"description": "A command line tool for deploying updates to your Auth0 tenant",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"homepage": "https://github.com/auth0/auth0-deploy-cli#readme",
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"ajv": "^6.12.6",
|
|
36
|
-
"auth0": "^5.3.
|
|
36
|
+
"auth0": "^5.3.1",
|
|
37
37
|
"dot-prop": "^5.3.0",
|
|
38
38
|
"fs-extra": "^10.1.0",
|
|
39
39
|
"js-yaml": "^4.1.1",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"nconf": "^0.13.0",
|
|
43
43
|
"promise-pool-executor": "^1.1.1",
|
|
44
44
|
"sanitize-filename": "^1.6.3",
|
|
45
|
-
"undici": "^7.
|
|
45
|
+
"undici": "^7.21.0",
|
|
46
46
|
"winston": "^3.19.0",
|
|
47
47
|
"yargs": "^15.4.1"
|
|
48
48
|
},
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"@types/mocha": "^10.0.10",
|
|
53
53
|
"@types/nconf": "^0.10.7",
|
|
54
54
|
"@eslint/js": "^9.39.2",
|
|
55
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
56
|
-
"@typescript-eslint/parser": "^8.
|
|
55
|
+
"@typescript-eslint/eslint-plugin": "^8.55.0",
|
|
56
|
+
"@typescript-eslint/parser": "^8.55.0",
|
|
57
57
|
"chai": "^4.5.0",
|
|
58
58
|
"chai-as-promised": "^7.1.2",
|
|
59
59
|
"eslint": "^9.39.2",
|