Skip to content

Commit 604868a

Browse files
chore: cleanup of update user by oidcsub (#1309)
1 parent 65a2b4e commit 604868a

7 files changed

Lines changed: 5 additions & 272 deletions

File tree

apps/backend/src/datasources/UserDataSource.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ import { Role, Roles } from '../models/Role';
44
import { BasicUserDetails, User, UserRole } from '../models/User';
55
import { AddUserRoleArgs } from '../resolvers/mutations/AddUserRoleMutation';
66
import { CreateUserByEmailInviteArgs } from '../resolvers/mutations/CreateUserByEmailInviteMutation';
7-
import {
8-
UpdateUserByOidcSubArgs,
9-
UpdateUserByIdArgs,
10-
} from '../resolvers/mutations/UpdateUserMutation';
7+
import { UpdateUserByIdArgs } from '../resolvers/mutations/UpdateUserMutation';
118
import { UsersArgs } from '../resolvers/queries/UsersQuery';
129

1310
export interface UserDataSource {
@@ -82,7 +79,6 @@ export interface UserDataSource {
8279
rorId?: string
8380
): Promise<number>;
8481
update(user: UpdateUserByIdArgs): Promise<User>;
85-
updateUserByOidcSub(args: UpdateUserByOidcSubArgs): Promise<User | null>;
8682
setUserRoles(id: number, roles: number[]): Promise<void>;
8783
setUserNotPlaceholder(id: number): Promise<User | null>;
8884
checkScientistToProposal(

apps/backend/src/datasources/mockups/UserDataSource.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ import {
99
} from '../../models/User';
1010
import { AddUserRoleArgs } from '../../resolvers/mutations/AddUserRoleMutation';
1111
import { CreateUserByEmailInviteArgs } from '../../resolvers/mutations/CreateUserByEmailInviteMutation';
12-
import {
13-
UpdateUserByIdArgs,
14-
UpdateUserByOidcSubArgs,
15-
} from '../../resolvers/mutations/UpdateUserMutation';
12+
import { UpdateUserByIdArgs } from '../../resolvers/mutations/UpdateUserMutation';
1613
import { UsersArgs } from '../../resolvers/queries/UsersQuery';
1714
import { UserDataSource } from '../UserDataSource';
1815

@@ -400,17 +397,6 @@ export class UserDataSourceMock implements UserDataSource {
400397
return dummyUser;
401398
}
402399

403-
async updateUserByOidcSub(
404-
args: UpdateUserByOidcSubArgs
405-
): Promise<User | null> {
406-
if (dummyUser.oidcSub === args.oidcSub) {
407-
return { ...dummyUser, ...args };
408-
}
409-
410-
// User not found
411-
return null;
412-
}
413-
414400
async me(id: number) {
415401
return dummyUser;
416402
}

apps/backend/src/datasources/postgres/UserDataSource.ts

Lines changed: 1 addition & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@ import {
1414
} from '../../models/User';
1515
import { AddUserRoleArgs } from '../../resolvers/mutations/AddUserRoleMutation';
1616
import { CreateUserByEmailInviteArgs } from '../../resolvers/mutations/CreateUserByEmailInviteMutation';
17-
import {
18-
UpdateUserByIdArgs,
19-
UpdateUserByOidcSubArgs,
20-
} from '../../resolvers/mutations/UpdateUserMutation';
17+
import { UpdateUserByIdArgs } from '../../resolvers/mutations/UpdateUserMutation';
2118
import { UsersArgs } from '../../resolvers/queries/UsersQuery';
2219
import { UserDataSource } from '../UserDataSource';
2320
import database, { isUniqueConstraintError } from './database';
@@ -126,61 +123,6 @@ export default class PostgresUserDataSource implements UserDataSource {
126123
}
127124
}
128125

129-
async updateUserByOidcSub(
130-
args: UpdateUserByOidcSubArgs
131-
): Promise<User | null> {
132-
const {
133-
firstname,
134-
user_title,
135-
lastname,
136-
preferredname,
137-
gender,
138-
birthdate,
139-
institutionId,
140-
department,
141-
position,
142-
email,
143-
telephone,
144-
placeholder,
145-
oauthRefreshToken,
146-
oauthIssuer,
147-
} = args;
148-
try {
149-
const [userRecord]: UserRecord[] = await database
150-
.update({
151-
firstname,
152-
user_title,
153-
lastname,
154-
preferredname,
155-
gender,
156-
birthdate,
157-
institution_id: institutionId,
158-
department,
159-
position,
160-
email,
161-
telephone,
162-
placeholder,
163-
oauth_refresh_token: oauthRefreshToken,
164-
oauth_issuer: oauthIssuer,
165-
updated_at: new Date(),
166-
})
167-
.from('users')
168-
.where('oidc_sub', args.oidcSub)
169-
.returning(['*']);
170-
171-
if (!userRecord) {
172-
return null;
173-
}
174-
175-
return createUserObject(userRecord);
176-
} catch (error) {
177-
if (isUniqueConstraintError(error)) {
178-
throw new GraphQLError('User already exists');
179-
}
180-
throw new GraphQLError('Could not create user. Check your Inputs.');
181-
}
182-
}
183-
184126
async createInviteUser(args: CreateUserByEmailInviteArgs): Promise<number> {
185127
const { firstname, lastname, email } = args;
186128

apps/backend/src/datasources/stfc/StfcUserDataSource.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ import { Role, Roles } from '../../models/Role';
99
import { BasicUserDetails, User, UserRole } from '../../models/User';
1010
import { AddUserRoleArgs } from '../../resolvers/mutations/AddUserRoleMutation';
1111
import { CreateUserByEmailInviteArgs } from '../../resolvers/mutations/CreateUserByEmailInviteMutation';
12-
import {
13-
UpdateUserByIdArgs,
14-
UpdateUserByOidcSubArgs,
15-
} from '../../resolvers/mutations/UpdateUserMutation';
12+
import { UpdateUserByIdArgs } from '../../resolvers/mutations/UpdateUserMutation';
1613
import { UsersArgs } from '../../resolvers/queries/UsersQuery';
1714
import { Cache } from '../../utils/Cache';
1815
import PostgresUserDataSource from '../postgres/UserDataSource';
@@ -503,12 +500,6 @@ export class StfcUserDataSource implements UserDataSource {
503500
throw new Error('Method not implemented.');
504501
}
505502

506-
async updateUserByOidcSub(
507-
args: UpdateUserByOidcSubArgs
508-
): Promise<User | null> {
509-
throw new Error('Method not implemented.');
510-
}
511-
512503
async me(id: number) {
513504
return this.getUser(id);
514505
}

apps/backend/src/mutations/UserMutations.spec.ts

Lines changed: 1 addition & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
dummyUserOfficerWithRole,
1212
} from '../datasources/mockups/UserDataSource';
1313
import { EmailInviteResponse } from '../models/EmailInviteResponse';
14-
import { isRejection, Rejection } from '../models/Rejection';
14+
import { isRejection } from '../models/Rejection';
1515
import { AuthJwtPayload, User, UserRole } from '../models/User';
1616
import { verifyToken } from '../utils/jwt';
1717
import UserMutations from './UserMutations';
@@ -238,135 +238,6 @@ test('externalTokenLogin supplies a new JWT', async () => {
238238
expect(decoded.user.id).toBe(dummyUser.id);
239239
});
240240

241-
// Tests for updateUserByOidcSub functionality
242-
describe('updateUserByOidcSub', () => {
243-
test('A user can update their own profile by OIDC sub', async () => {
244-
const result = await userMutations.updateUserByOidcSub(dummyUserWithRole, {
245-
oidcSub: dummyUser.oidcSub as string,
246-
firstname: 'UpdatedJane',
247-
lastname: 'UpdatedDoe',
248-
email: 'updated.jane@example.com',
249-
id: dummyUser.id,
250-
});
251-
252-
expect(result).toEqual({
253-
...dummyUser,
254-
firstname: 'UpdatedJane',
255-
lastname: 'UpdatedDoe',
256-
email: 'updated.jane@example.com',
257-
});
258-
});
259-
260-
test('A user officer can update another user by OIDC sub', async () => {
261-
const result = await userMutations.updateUserByOidcSub(
262-
dummyUserOfficerWithRole,
263-
{
264-
oidcSub: dummyUser.oidcSub as string,
265-
firstname: 'OfficerUpdatedJane',
266-
department: 'Updated Department',
267-
id: dummyUser.id,
268-
}
269-
);
270-
271-
expect(result).toEqual({
272-
...dummyUser,
273-
firstname: 'OfficerUpdatedJane',
274-
department: 'Updated Department',
275-
});
276-
});
277-
test('A user cannot update another user by OIDC sub', async () => {
278-
const result = await userMutations.updateUserByOidcSub(
279-
dummyUserNotOnProposalWithRole,
280-
{
281-
oidcSub: dummyUser.oidcSub as string,
282-
firstname: 'ShouldNotUpdate',
283-
id: dummyUser.id,
284-
}
285-
);
286-
287-
expect(isRejection(result)).toBe(true);
288-
expect((result as Rejection).reason).toBe(
289-
'Can not update user because of insufficient permissions'
290-
);
291-
});
292-
293-
test('A not logged in user cannot update a user by OIDC sub', async () => {
294-
const result = await userMutations.updateUserByOidcSub(null, {
295-
oidcSub: dummyUser.oidcSub as string,
296-
firstname: 'ShouldNotUpdate',
297-
id: dummyUser.id,
298-
});
299-
300-
expect(isRejection(result)).toBe(true);
301-
expect((result as Rejection).reason).toBe('NOT_LOGGED_IN');
302-
});
303-
304-
test('A user can update partial profile data by OIDC sub', async () => {
305-
const result = await userMutations.updateUserByOidcSub(dummyUserWithRole, {
306-
oidcSub: dummyUser.oidcSub as string,
307-
telephone: '+1-555-9999',
308-
position: 'Senior Architect',
309-
id: dummyUser.id,
310-
});
311-
312-
expect(result).toEqual({
313-
...dummyUser,
314-
telephone: '+1-555-9999',
315-
position: 'Senior Architect',
316-
});
317-
});
318-
319-
test('A user cannot update someone else profile even with their own OIDC sub when trying to update different user', async () => {
320-
// Simulate user with different OIDC sub trying to update dummyUser
321-
const userWithDifferentOidcSub = {
322-
...dummyUserNotOnProposalWithRole,
323-
oidcSub: 'different-oidc-sub',
324-
};
325-
326-
const result = await userMutations.updateUserByOidcSub(
327-
userWithDifferentOidcSub,
328-
{
329-
oidcSub: dummyUser.oidcSub as string,
330-
firstname: 'ShouldNotUpdate',
331-
id: dummyUser.id,
332-
}
333-
);
334-
335-
expect(isRejection(result)).toBe(true);
336-
expect((result as Rejection).reason).toBe(
337-
'Can not update user because of insufficient permissions'
338-
);
339-
});
340-
341-
test('Empty update object should work', async () => {
342-
const result = await userMutations.updateUserByOidcSub(dummyUserWithRole, {
343-
oidcSub: dummyUser.oidcSub as string,
344-
id: dummyUser.id,
345-
});
346-
347-
expect(result).toEqual(dummyUser);
348-
});
349-
350-
test('Update should preserve original user data for unspecified fields', async () => {
351-
const result = await userMutations.updateUserByOidcSub(dummyUserWithRole, {
352-
oidcSub: dummyUser.oidcSub as string,
353-
firstname: 'OnlyFirstName',
354-
id: dummyUser.id,
355-
});
356-
357-
expect(result).toEqual({
358-
...dummyUser,
359-
firstname: 'OnlyFirstName',
360-
});
361-
362-
// Verify other fields remain unchanged
363-
expect(isRejection(result)).toBe(false);
364-
expect((result as typeof dummyUser).lastname).toBe(dummyUser.lastname);
365-
expect((result as typeof dummyUser).email).toBe(dummyUser.email);
366-
expect((result as typeof dummyUser).department).toBe(dummyUser.department);
367-
});
368-
});
369-
370241
describe('upsertUserByOidcSub', () => {
371242
test('A user can be created if OIDC sub does not exist', async () => {
372243
const newOidcSub = 'new-unique-oidc-sub';

apps/backend/src/mutations/UserMutations.ts

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
import * as bcrypt from 'bcryptjs';
1111
import { DateTime } from 'luxon';
1212
import { inject, injectable } from 'tsyringe';
13-
import { Args } from 'type-graphql';
1413

1514
import { UserAuthorization } from '../auth/UserAuthorization';
1615
import { Tokens } from '../config/Tokens';
@@ -33,7 +32,6 @@ import { AddUserRoleArgs } from '../resolvers/mutations/AddUserRoleMutation';
3332
import { CreateUserByEmailInviteArgs } from '../resolvers/mutations/CreateUserByEmailInviteMutation';
3433
import {
3534
UpdateUserRolesArgs,
36-
UpdateUserByOidcSubArgs,
3735
UpdateUserByIdArgs,
3836
} from '../resolvers/mutations/UpdateUserMutation';
3937
import { UpsertUserByOidcSubArgs } from '../resolvers/mutations/UpsertUserMutation';
@@ -245,49 +243,6 @@ export default class UserMutations {
245243
});
246244
}
247245

248-
@Authorized()
249-
@EventBus(Event.USER_UPDATED)
250-
async updateUserByOidcSub(
251-
agent: UserWithRole | null,
252-
@Args() args: UpdateUserByOidcSubArgs
253-
): Promise<User | Rejection> {
254-
const isUpdatingOwnUser = agent?.oidcSub === args.oidcSub;
255-
if (
256-
!this.userAuth.isApiToken(agent) &&
257-
!this.userAuth.isUserOfficer(agent) &&
258-
!isUpdatingOwnUser
259-
) {
260-
return rejection(
261-
'Can not update user because of insufficient permissions',
262-
{
263-
args,
264-
agent,
265-
code: ApolloServerErrorCodeExtended.INSUFFICIENT_PERMISSIONS,
266-
}
267-
);
268-
}
269-
270-
try {
271-
const updatedUser = await this.dataSource.updateUserByOidcSub(args);
272-
273-
if (!updatedUser) {
274-
return rejection(
275-
'USER_NOT_FOUND',
276-
{ oidcSub: args.oidcSub },
277-
new Error(`User with OIDC sub ${args.oidcSub} not found`)
278-
);
279-
}
280-
281-
return updatedUser;
282-
} catch (error) {
283-
return rejection(
284-
'INTERNAL_ERROR',
285-
{ agent, args },
286-
error instanceof Error ? error : new Error(String(error))
287-
);
288-
}
289-
}
290-
291246
@ValidateArgs(getTokenForUserValidationSchema)
292247
@Authorized()
293248
async getTokenForUser(

apps/backend/src/resolvers/mutations/UpdateUserMutation.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,4 @@ export class UpdateUserMutation {
111111
) {
112112
return context.mutations.user.setUserNotPlaceholder(context.user, id);
113113
}
114-
115-
@Mutation(() => User)
116-
updateUserByOidcSub(
117-
@Args() input: UpdateUserByOidcSubArgs,
118-
@Ctx() context: ResolverContext
119-
) {
120-
return context.mutations.user.updateUserByOidcSub(context.user, input);
121-
}
122114
}

0 commit comments

Comments
 (0)