diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index 8188b3daa..abcbda0ff 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -27,14 +27,15 @@ export enum RIGHTS { GDT_BALANCE = 'GDT_BALANCE', // Admin SEARCH_USERS = 'SEARCH_USERS', + SET_USER_ROLE = 'SET_USER_ROLE', + DELETE_USER = 'DELETE_USER', + UNDELETE_USER = 'UNDELETE_USER', CREATE_PENDING_CREATION = 'CREATE_PENDING_CREATION', UPDATE_PENDING_CREATION = 'UPDATE_PENDING_CREATION', SEARCH_PENDING_CREATION = 'SEARCH_PENDING_CREATION', DELETE_PENDING_CREATION = 'DELETE_PENDING_CREATION', CONFIRM_PENDING_CREATION = 'CONFIRM_PENDING_CREATION', SEND_ACTIVATION_EMAIL = 'SEND_ACTIVATION_EMAIL', - DELETE_USER = 'DELETE_USER', - UNDELETE_USER = 'UNDELETE_USER', CREATION_TRANSACTION_LIST = 'CREATION_TRANSACTION_LIST', LIST_TRANSACTION_LINKS_ADMIN = 'LIST_TRANSACTION_LINKS_ADMIN', } diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index acf880efb..979acf952 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -13,6 +13,7 @@ import { peterLustig } from '@/seeds/users/peter-lustig' import { stephenHawking } from '@/seeds/users/stephen-hawking' import { garrickOllivander } from '@/seeds/users/garrick-ollivander' import { + setUserRole, deleteUser, unDeleteUser, createPendingCreation, @@ -64,6 +65,173 @@ let user: User let creation: AdminPendingCreation | void describe('AdminResolver', () => { + describe('set user role', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + mutate({ mutation: setUserRole, variables: { userId: 1, isAdmin: true } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated', () => { + describe('without admin rights', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + it('returns an error', async () => { + await expect( + mutate({ mutation: setUserRole, variables: { userId: user.id + 1, isAdmin: true } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('with admin rights', () => { + beforeAll(async () => { + admin = await userFactory(testEnv, peterLustig) + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('user to get a new role does not exist', () => { + it('throws an error', async () => { + await expect( + mutate({ mutation: setUserRole, variables: { userId: admin.id + 1, isAdmin: true } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError(`Could not find user with userId: ${admin.id + 1}`)], + }), + ) + }) + }) + + describe('change role with success', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + }) + + it('returns date string', async () => { + const result = await mutate({ + mutation: setUserRole, + variables: { userId: user.id, isAdmin: true }, + }) + expect(result).toEqual( + expect.objectContaining({ + data: { + setUserRole: expect.any(String), + }, + }), + ) + expect(new Date(result.data.setUserRole)).toEqual(expect.any(Date)) + }) + + describe('user gets new role', () => { + describe('is usual user', () => { + it('returns string', async () => { + await expect( + mutate({ mutation: setUserRole, variables: { userId: user.id, isAdmin: false } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + setUserRole: expect.any(String), + }, + }), + ) + }) + }) + + describe('is admin', () => { + it('returns string', async () => { + await expect( + mutate({ + mutation: setUserRole, + variables: { userId: user.id, isAdmin: true }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + setUserRole: expect.any(String), + }, + }), + ) + }) + }) + }) + }) + + // Wolle + describe.skip('change role with error', () => { + describe('is own role', () => { + it('throws an error', async () => { + await expect( + mutate({ mutation: setUserRole, variables: { userId: admin.id, isAdmin: false } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Administrator can not change his own role!')], + }), + ) + }) + }) + + describe('user has already role to be set', () => { + describe('is admin', () => { + it('throws an error', async () => { + await expect( + mutate({ mutation: setUserRole, variables: { userId: user.id, isAdmin: true } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('User is already admin!')], + }), + ) + }) + }) + + describe('is usual user', () => { + it('throws an error', async () => { + await mutate({ + mutation: setUserRole, + variables: { userId: user.id, isAdmin: false }, + }) + await expect( + mutate({ mutation: setUserRole, variables: { userId: user.id, isAdmin: false } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('User is already a usual user!')], + }), + ) + }) + }) + }) + }) + }) + }) + }) + describe('delete user', () => { describe('unauthenticated', () => { it('returns an error', async () => { diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 78dcfb7e1..426e77ffa 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -135,6 +135,48 @@ export class AdminResolver { } } + @Authorized([RIGHTS.SET_USER_ROLE]) + @Mutation(() => Date, { nullable: true }) + async setUserRole( + @Arg('userId', () => Int) + userId: number, + @Arg('isAdmin', () => Boolean) + isAdmin: boolean, + @Ctx() + context: Context, + ): Promise { + const user = await dbUser.findOne({ id: userId }) + // user exists ? + if (!user) { + throw new Error(`Could not find user with userId: ${userId}`) + } + // administrator user changes own role? + const moderatorUser = getUser(context) + if (moderatorUser.id === userId) { + throw new Error('Administrator can not change his own role!') + } + // change isAdmin + switch (user.isAdmin) { + case null: + if (isAdmin === true) { + user.isAdmin = new Date() + } else { + throw new Error('User is already a usual user!') + } + break + default: + if (isAdmin === false) { + user.isAdmin = null + } else { + throw new Error('User is already admin!') + } + break + } + await user.save() + const newUser = await dbUser.findOne({ id: userId }) + return newUser ? newUser.isAdmin : null + } + @Authorized([RIGHTS.DELETE_USER]) @Mutation(() => Date, { nullable: true }) async deleteUser( diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index e66827566..efab89559 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -93,6 +93,12 @@ export const confirmPendingCreation = gql` } ` +export const setUserRole = gql` + mutation ($userId: Int!, $isAdmin: Boolean!) { + setUserRole(userId: $userId, isAdmin: $isAdmin) + } +` + export const deleteUser = gql` mutation ($userId: Int!) { deleteUser(userId: $userId)