Skip to content

Commit 0b45726

Browse files
authored
fix: update the token exchange profiles handling (#1253)
* fix: update token exchange profiles schema and handling - src/tools/auth0/handlers/tokenExchangeProfiles.ts: remove unnecessary fields from schema - src/tools/auth0/handlers/tokenExchangeProfiles.ts: adjust fields to strip during updates and creations - src/tools/auth0/handlers/tokenExchangeProfiles.ts: sanitize profiles before processing changes - src/tools/auth0/handlers/tokenExchangeProfiles.ts: ensure required fields are validated during profile creation - src/tools/auth0/handlers/tokenExchangeProfiles.ts: simplify update logic by removing unused fields * fix: enhance token exchange profiles validation and error handling - test/tools/auth0/handlers/tokenExchangeProfiles.tests.js: add checks for undefined fields in token exchange profile creation - test/tools/auth0/handlers/tokenExchangeProfiles.tests.js: implement error handling for missing required fields during profile creation * fix: update token exchange profiles handling and validation - src/tools/auth0/handlers/tokenExchangeProfiles.ts: change actions type from Asset to Action - src/tools/auth0/handlers/tokenExchangeProfiles.ts: update identifiers to use subject_token_type - src/tools/auth0/handlers/tokenExchangeProfiles.ts: ensure actions are fetched only if not already available * fix: update token exchange profile handling in tests - test/tools/auth0/handlers/tokenExchangeProfiles.tests.js: rename test to reflect update behavior - test/tools/auth0/handlers/tokenExchangeProfiles.tests.js: modify update function to include execution order tracking - test/tools/auth0/handlers/tokenExchangeProfiles.tests.js: update expectation to match new execution order
1 parent fffdcf8 commit 0b45726

3 files changed

Lines changed: 82 additions & 47 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ Makefile
1717
.github/chatmodes/*
1818
.github/prompts/*
1919
.github/agents/*
20+
.github/skills/*

src/tools/auth0/handlers/tokenExchangeProfiles.ts

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import DefaultHandler, { order } from './default';
33
import { Asset, Assets } from '../../../types';
44
import { paginate } from '../client';
55
import log from '../../../logger';
6+
import { Action } from './actions';
67

78
// Define TokenExchangeProfile type
89
export type TokenExchangeProfile = Management.TokenExchangeProfileResponseContent;
@@ -16,10 +17,6 @@ export const schema = {
1617
type: 'string',
1718
description: 'The name of the token exchange profile',
1819
},
19-
id: {
20-
type: 'string',
21-
description: 'The unique identifier of the token exchange profile',
22-
},
2320
subject_token_type: {
2421
type: 'string',
2522
description: 'The URI representing the subject token type',
@@ -33,16 +30,6 @@ export const schema = {
3330
enum: ['custom_authentication'],
3431
description: 'The type of token exchange profile',
3532
},
36-
created_at: {
37-
type: 'string',
38-
format: 'date-time',
39-
description: 'The timestamp when the profile was created',
40-
},
41-
updated_at: {
42-
type: 'string',
43-
format: 'date-time',
44-
description: 'The timestamp when the profile was last updated',
45-
},
4633
},
4734
required: ['name', 'subject_token_type', 'action', 'type'],
4835
},
@@ -51,21 +38,24 @@ export const schema = {
5138
export default class TokenExchangeProfilesHandler extends DefaultHandler {
5239
existing: TokenExchangeProfile[];
5340

54-
private actions: Asset[] | null;
41+
private actions: Action[] | null;
5542

5643
constructor(config: DefaultHandler) {
5744
super({
5845
...config,
5946
type: 'tokenExchangeProfiles',
6047
id: 'id',
61-
identifiers: ['id', 'name'],
48+
identifiers: ['id', 'subject_token_type'],
6249
// Only name and subject_token_type can be updated
63-
stripUpdateFields: ['id', 'created_at', 'updated_at', 'action_id', 'type'],
64-
stripCreateFields: ['id', 'created_at', 'updated_at'],
50+
stripUpdateFields: ['created_at', 'updated_at', 'action_id', 'type'],
51+
stripCreateFields: ['created_at', 'updated_at'],
6552
});
6653
}
6754

68-
private sanitizeForExport(profile: TokenExchangeProfile, actions: Asset[]): TokenExchangeProfile {
55+
private sanitizeForExport(
56+
profile: TokenExchangeProfile,
57+
actions: Action[]
58+
): TokenExchangeProfile {
6959
if (profile.action_id) {
7060
const action = actions?.find((a) => a.id === profile.action_id);
7161
if (action) {
@@ -126,10 +116,12 @@ export default class TokenExchangeProfilesHandler extends DefaultHandler {
126116
);
127117

128118
// Fetch all actions to map action_id to action name
129-
const actions = await this.getActions();
119+
this.actions = await this.getActions();
130120

131121
// Map action_id to action name for each profile
132-
this.existing = profiles.map((profile) => this.sanitizeForExport(profile, actions));
122+
this.existing = profiles.map((profile) =>
123+
this.sanitizeForExport(profile, this.actions ?? [])
124+
);
133125

134126
return this.existing;
135127
} catch (err) {
@@ -150,46 +142,53 @@ export default class TokenExchangeProfilesHandler extends DefaultHandler {
150142
// Do nothing if not set
151143
if (!tokenExchangeProfiles) return;
152144

153-
// Fetch actions to resolve action names to IDs
154-
const actions = await this.getActions();
155-
156-
// Map action names to action_ids before processing
157-
const sanitizedProfiles = tokenExchangeProfiles.map((profile) =>
158-
this.sanitizeForAPI(profile as TokenExchangeProfile, actions)
159-
);
160-
161-
// Create modified assets with sanitized profiles
162-
const modifiedAssets = {
163-
...assets,
164-
tokenExchangeProfiles: sanitizedProfiles as TokenExchangeProfile[],
165-
};
166-
167145
// Calculate changes
168-
const { del, update, create, conflicts } = await this.calcChanges(modifiedAssets);
146+
const { del, update, create, conflicts } = await this.calcChanges(assets);
169147

170148
log.debug(
171149
`Start processChanges for tokenExchangeProfiles [delete:${del.length}] [update:${update.length}], [create:${create.length}], [conflicts:${conflicts.length}]`
172150
);
173151

152+
// Fetch actions to resolve action names to IDs
153+
if (!this.actions || this.actions.length === 0) {
154+
this.actions = await this.getActions();
155+
}
156+
174157
// Process changes in order: delete, create, update
175158
if (del.length > 0) {
176-
await this.deleteTokenExchangeProfiles(del);
159+
await this.deleteTokenExchangeProfiles(
160+
del.map((profile) => this.sanitizeForAPI(profile, this.actions ?? []))
161+
);
177162
}
178163

179164
if (create.length > 0) {
180-
await this.createTokenExchangeProfiles(create);
165+
await this.createTokenExchangeProfiles(
166+
create.map((profile) => this.sanitizeForAPI(profile, this.actions ?? []))
167+
);
181168
}
182169

183170
if (update.length > 0) {
184-
await this.updateTokenExchangeProfiles(update);
171+
await this.updateTokenExchangeProfiles(
172+
update.map((profile) => this.sanitizeForAPI(profile, this.actions ?? []))
173+
);
185174
}
186175
}
187176

188177
async createTokenExchangeProfile(profile: TokenExchangeProfile): Promise<TokenExchangeProfile> {
189-
const { id, created_at, updated_at, ...createParams } = profile;
190-
const created = await this.client.tokenExchangeProfiles.create(
191-
createParams as Management.CreateTokenExchangeProfileRequestContent
192-
);
178+
if (!profile.name || !profile.subject_token_type || !profile.action_id || !profile.type) {
179+
throw new Error(`Cannot create token exchange profile missing required fields`);
180+
}
181+
182+
const createParams: Management.CreateTokenExchangeProfileRequestContent & {
183+
type: Management.TokenExchangeProfileTypeEnum;
184+
} = {
185+
name: profile.name,
186+
subject_token_type: profile.subject_token_type,
187+
action_id: profile.action_id,
188+
type: profile.type,
189+
};
190+
191+
const created = await this.client.tokenExchangeProfiles.create(createParams);
193192
return created;
194193
}
195194

@@ -211,12 +210,17 @@ export default class TokenExchangeProfilesHandler extends DefaultHandler {
211210
}
212211

213212
async updateTokenExchangeProfile(profile: TokenExchangeProfile): Promise<void> {
214-
const { id, created_at, updated_at, action_id, type, ...updateParams } = profile;
213+
const { id, name, subject_token_type } = profile;
215214

216215
if (!id) {
217216
throw new Error(`Cannot update token exchange profile "${profile.name}" - missing id`);
218217
}
219218

219+
const updateParams: Management.UpdateTokenExchangeProfileRequestContent = {
220+
name,
221+
subject_token_type,
222+
};
223+
220224
await this.client.tokenExchangeProfiles.update(id, updateParams);
221225
}
222226

test/tools/auth0/handlers/tokenExchangeProfiles.tests.js

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ describe('#tokenExchangeProfiles handler', () => {
225225
expect(data.name).to.equal('CIS token exchange');
226226
expect(data.subject_token_type).to.equal('https://acme.com/cis-token');
227227
expect(data.action_id).to.equal('action_123'); // Should be mapped to action_id
228+
expect(data.type).to.equal('custom_authentication');
229+
expect(data.id).to.be.undefined;
230+
expect(data.created_at).to.be.undefined;
231+
expect(data.updated_at).to.be.undefined;
228232
return Promise.resolve({
229233
data: {
230234
...data,
@@ -268,6 +272,29 @@ describe('#tokenExchangeProfiles handler', () => {
268272
]);
269273
});
270274

275+
it('should throw when creating token exchange profile without required fields', async () => {
276+
const auth0 = {
277+
tokenExchangeProfiles: {
278+
create: () => Promise.resolve({}),
279+
},
280+
pool,
281+
};
282+
283+
const handler = new tokenExchangeProfiles.default({ client: pageClient(auth0), config });
284+
285+
try {
286+
await handler.createTokenExchangeProfile({
287+
name: 'Incomplete token exchange',
288+
subject_token_type: 'https://acme.com/cis-token',
289+
type: 'custom_authentication',
290+
// action_id missing
291+
});
292+
expect.fail('Should have thrown an error');
293+
} catch (err) {
294+
expect(err.message).to.include('missing required fields');
295+
}
296+
});
297+
271298
it('should update token exchange profile', async () => {
272299
const auth0 = {
273300
tokenExchangeProfiles: {
@@ -358,15 +385,18 @@ describe('#tokenExchangeProfiles handler', () => {
358385
await stageFn.apply(handler, [{ tokenExchangeProfiles: [] }]);
359386
});
360387

361-
it('should process deletes before creates to avoid race conditions', async () => {
388+
it('should update when subject_token_type matches existing profile', async () => {
362389
const executionOrder = [];
363390
const auth0 = {
364391
tokenExchangeProfiles: {
365392
create: function (data) {
366393
executionOrder.push('create');
367394
return Promise.resolve({ data: { ...data, id: 'tep_new' } });
368395
},
369-
update: () => Promise.resolve({ data: [] }),
396+
update: function (id, data) {
397+
executionOrder.push('update');
398+
return Promise.resolve({ data: { id, ...data } });
399+
},
370400
delete: function (id) {
371401
executionOrder.push('delete');
372402
return new Promise((resolve) => setTimeout(() => resolve({ id }), 10));
@@ -417,7 +447,7 @@ describe('#tokenExchangeProfiles handler', () => {
417447
},
418448
]);
419449

420-
expect(executionOrder).to.deep.equal(['delete', 'create']);
450+
expect(executionOrder).to.deep.equal(['update']);
421451
});
422452

423453
it('should throw error when action is not found during create', async () => {

0 commit comments

Comments
 (0)