From b14911d3147515beded27c440a11692529792c2f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Mar 2023 11:05:03 +0100 Subject: [PATCH 01/13] logout event --- backend/src/event/EVENT_LOGOUT.ts | 6 ++++++ backend/src/event/Event.ts | 1 + backend/src/event/EventType.ts | 1 + backend/src/graphql/resolver/UserResolver.ts | 13 ++++--------- 4 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 backend/src/event/EVENT_LOGOUT.ts diff --git a/backend/src/event/EVENT_LOGOUT.ts b/backend/src/event/EVENT_LOGOUT.ts new file mode 100644 index 000000000..1e423359c --- /dev/null +++ b/backend/src/event/EVENT_LOGOUT.ts @@ -0,0 +1,6 @@ +import { User as DbUser } from '@entity/User' +import { Event as DbEvent } from '@entity/Event' +import { Event, EventType } from './Event' + +export const EVENT_LOGOUT = async (user: DbUser): Promise => + Event(EventType.LOGOUT, user, user).save() diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index cdb05748c..19fbc81cd 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -53,6 +53,7 @@ export { EVENT_CONTRIBUTION_UPDATE } from './EVENT_CONTRIBUTION_UPDATE' export { EVENT_CONTRIBUTION_MESSAGE_CREATE } from './EVENT_CONTRIBUTION_MESSAGE_CREATE' export { EVENT_CONTRIBUTION_LINK_REDEEM } from './EVENT_CONTRIBUTION_LINK_REDEEM' export { EVENT_LOGIN } from './EVENT_LOGIN' +export { EVENT_LOGOUT } from './EVENT_LOGOUT' export { EVENT_REGISTER } from './EVENT_REGISTER' export { EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL } from './EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL' export { EVENT_SEND_CONFIRMATION_EMAIL } from './EVENT_SEND_CONFIRMATION_EMAIL' diff --git a/backend/src/event/EventType.ts b/backend/src/event/EventType.ts index 47056f05e..df4a5cc75 100644 --- a/backend/src/event/EventType.ts +++ b/backend/src/event/EventType.ts @@ -17,6 +17,7 @@ export enum EventType { CONTRIBUTION_MESSAGE_CREATE = 'CONTRIBUTION_MESSAGE_CREATE', CONTRIBUTION_LINK_REDEEM = 'CONTRIBUTION_LINK_REDEEM', LOGIN = 'LOGIN', + LOGOUT = 'LOGOUT', REGISTER = 'REGISTER', REDEEM_REGISTER = 'REDEEM_REGISTER', SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = 'SEND_ACCOUNT_MULTIREGISTRATION_EMAIL', diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 2cd40938f..639f59c09 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -57,6 +57,7 @@ import { EVENT_REGISTER, EVENT_ACTIVATE_ACCOUNT, EVENT_ADMIN_SEND_CONFIRMATION_EMAIL, + EVENT_LOGOUT, } from '@/event/Event' import { getUserCreations } from './util/creations' import { isValidPassword } from '@/password/EncryptorUtils' @@ -185,15 +186,9 @@ export class UserResolver { @Authorized([RIGHTS.LOGOUT]) @Mutation(() => String) - async logout(): Promise { - // TODO: Event still missing here!! - // TODO: We dont need this anymore, but might need this in the future in oder to invalidate a valid JWT-Token. - // Furthermore this hook can be useful for tracking user behaviour (did he logout or not? Warn him if he didn't on next login) - // The functionality is fully client side - the client just needs to delete his token with the current implementation. - // we could try to force this by sending `token: null` or `token: ''` with this call. But since it bares no real security - // we should just return true for now. - logger.info('Logout...') - // remove user.pubKey from logger-context to ensure a correct filter on log-messages belonging to the same user + async logout(@Ctx() context: Context): Promise { + await EVENT_LOGOUT(getUser(context)) + // remove user from logger context logger.addContext('user', 'unknown') return true } From feeeca9fe4def82daeafba6ed3af59294019065c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Mar 2023 11:05:41 +0100 Subject: [PATCH 02/13] corrected return type --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 639f59c09..a65049252 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -185,7 +185,7 @@ export class UserResolver { } @Authorized([RIGHTS.LOGOUT]) - @Mutation(() => String) + @Mutation(() => Boolean) async logout(@Ctx() context: Context): Promise { await EVENT_LOGOUT(getUser(context)) // remove user from logger context From 0265834744f546f6804f8150de98f8dc5944c37a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Mar 2023 11:33:53 +0100 Subject: [PATCH 03/13] events for forgot password email and user info update --- .../src/event/EVENT_EMAIL_FORGOT_PASSWORD.ts | 6 ++++ backend/src/event/EVENT_USER_INFO_UPDATE.ts | 6 ++++ backend/src/event/Event.ts | 1 + backend/src/event/EventType.ts | 2 ++ backend/src/graphql/resolver/UserResolver.ts | 34 +++++++++---------- 5 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 backend/src/event/EVENT_EMAIL_FORGOT_PASSWORD.ts create mode 100644 backend/src/event/EVENT_USER_INFO_UPDATE.ts diff --git a/backend/src/event/EVENT_EMAIL_FORGOT_PASSWORD.ts b/backend/src/event/EVENT_EMAIL_FORGOT_PASSWORD.ts new file mode 100644 index 000000000..4160ce244 --- /dev/null +++ b/backend/src/event/EVENT_EMAIL_FORGOT_PASSWORD.ts @@ -0,0 +1,6 @@ +import { User as DbUser } from '@entity/User' +import { Event as DbEvent } from '@entity/Event' +import { Event, EventType } from './Event' + +export const EVENT_EMAIL_FORGOT_PASSWORD = async (user: DbUser): Promise => + Event(EventType.EMAIL_FORGOT_PASSWORD, user, user).save() diff --git a/backend/src/event/EVENT_USER_INFO_UPDATE.ts b/backend/src/event/EVENT_USER_INFO_UPDATE.ts new file mode 100644 index 000000000..681ecd473 --- /dev/null +++ b/backend/src/event/EVENT_USER_INFO_UPDATE.ts @@ -0,0 +1,6 @@ +import { User as DbUser } from '@entity/User' +import { Event as DbEvent } from '@entity/Event' +import { Event, EventType } from './Event' + +export const EVENT_USER_INFO_UPDATE = async (user: DbUser): Promise => + Event(EventType.USER_INFO_UPDATE, user, user).save() diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 19fbc81cd..60da91b70 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -52,6 +52,7 @@ export { EVENT_CONTRIBUTION_DELETE } from './EVENT_CONTRIBUTION_DELETE' export { EVENT_CONTRIBUTION_UPDATE } from './EVENT_CONTRIBUTION_UPDATE' export { EVENT_CONTRIBUTION_MESSAGE_CREATE } from './EVENT_CONTRIBUTION_MESSAGE_CREATE' export { EVENT_CONTRIBUTION_LINK_REDEEM } from './EVENT_CONTRIBUTION_LINK_REDEEM' +export { EVENT_EMAIL_FORGOT_PASSWORD } from './EVENT_EMAIL_FORGOT_PASSWORD' export { EVENT_LOGIN } from './EVENT_LOGIN' export { EVENT_LOGOUT } from './EVENT_LOGOUT' export { EVENT_REGISTER } from './EVENT_REGISTER' diff --git a/backend/src/event/EventType.ts b/backend/src/event/EventType.ts index df4a5cc75..cc277e589 100644 --- a/backend/src/event/EventType.ts +++ b/backend/src/event/EventType.ts @@ -16,6 +16,7 @@ export enum EventType { CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE', CONTRIBUTION_MESSAGE_CREATE = 'CONTRIBUTION_MESSAGE_CREATE', CONTRIBUTION_LINK_REDEEM = 'CONTRIBUTION_LINK_REDEEM', + EMAIL_FORGOT_PASSWORD = 'EMAIL_FORGOT_PASSWORD', LOGIN = 'LOGIN', LOGOUT = 'LOGOUT', REGISTER = 'REGISTER', @@ -27,6 +28,7 @@ export enum EventType { TRANSACTION_LINK_CREATE = 'TRANSACTION_LINK_CREATE', TRANSACTION_LINK_DELETE = 'TRANSACTION_LINK_DELETE', TRANSACTION_LINK_REDEEM = 'TRANSACTION_LINK_REDEEM', + USER_INFO_UPDATE = 'USER_INFO_UPDATE', // VISIT_GRADIDO = 'VISIT_GRADIDO', // VERIFY_REDEEM = 'VERIFY_REDEEM', // INACTIVE_ACCOUNT = 'INACTIVE_ACCOUNT', diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index a65049252..196110a31 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -58,6 +58,8 @@ import { EVENT_ACTIVATE_ACCOUNT, EVENT_ADMIN_SEND_CONFIRMATION_EMAIL, EVENT_LOGOUT, + EVENT_EMAIL_FORGOT_PASSWORD, + EVENT_USER_INFO_UPDATE, } from '@/event/Event' import { getUserCreations } from './util/creations' import { isValidPassword } from '@/password/EncryptorUtils' @@ -402,6 +404,7 @@ export class UserResolver { ) } logger.info(`forgotPassword(${email}) successful...`) + await EVENT_EMAIL_FORGOT_PASSWORD(user) return true } @@ -464,8 +467,6 @@ export class UserResolver { await queryRunner.commitTransaction() logger.info('User and UserContact data written successfully...') - - await EVENT_ACTIVATE_ACCOUNT(user) } catch (e) { await queryRunner.rollbackTransaction() throw new LogError('Error on writing User and User Contact data', e) @@ -483,13 +484,9 @@ export class UserResolver { ) } catch (e) { logger.error('Error subscribing to klicktipp', e) - // TODO is this a problem? - // eslint-disable-next-line no-console - /* uncomment this, when you need the activation link on the console - console.log('Could not subscribe to klicktipp') - */ } } + await EVENT_ACTIVATE_ACCOUNT(user) return true } @@ -526,21 +523,21 @@ export class UserResolver { @Ctx() context: Context, ): Promise { logger.info(`updateUserInfos(${firstName}, ${lastName}, ${language}, ***, ***)...`) - const userEntity = getUser(context) + const user = getUser(context) if (firstName) { - userEntity.firstName = firstName + user.firstName = firstName } if (lastName) { - userEntity.lastName = lastName + user.lastName = lastName } if (language) { if (!isLanguage(language)) { throw new LogError('Given language is not a valid language', language) } - userEntity.language = language + user.language = language i18n.setLocale(language) } @@ -552,22 +549,22 @@ export class UserResolver { ) } - if (!verifyPassword(userEntity, password)) { + if (!verifyPassword(user, password)) { throw new LogError(`Old password is invalid`) } // Save new password hash and newly encrypted private key - userEntity.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID - userEntity.password = encryptPassword(userEntity, passwordNew) + user.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID + user.password = encryptPassword(user, passwordNew) } // Save hideAmountGDD value if (hideAmountGDD !== undefined) { - userEntity.hideAmountGDD = hideAmountGDD + user.hideAmountGDD = hideAmountGDD } // Save hideAmountGDT value if (hideAmountGDT !== undefined) { - userEntity.hideAmountGDT = hideAmountGDT + user.hideAmountGDT = hideAmountGDT } const queryRunner = getConnection().createQueryRunner() @@ -575,7 +572,7 @@ export class UserResolver { await queryRunner.startTransaction('REPEATABLE READ') try { - await queryRunner.manager.save(userEntity).catch((error) => { + await queryRunner.manager.save(user).catch((error) => { throw new LogError('Error saving user', error) }) @@ -588,6 +585,9 @@ export class UserResolver { await queryRunner.release() } logger.info('updateUserInfos() successfully finished...') + + await EVENT_USER_INFO_UPDATE(user) + return true } From 1792d10733cedb62fccd1e5daab70b2ef6525096 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Mar 2023 11:53:10 +0100 Subject: [PATCH 04/13] more events for the user resolver --- backend/src/event/EVENT_ADMIN_USER_DELETE.ts | 6 ++++++ backend/src/event/EVENT_ADMIN_USER_ROLE_SET.ts | 8 ++++++++ backend/src/event/EVENT_ADMIN_USER_UNDELETE.ts | 8 ++++++++ .../src/event/EVENT_EMAIL_FORGOT_PASSWORD.ts | 2 +- backend/src/event/Event.ts | 4 ++++ backend/src/event/EventType.ts | 3 +++ backend/src/graphql/resolver/UserResolver.ts | 17 +++++++++++------ 7 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 backend/src/event/EVENT_ADMIN_USER_DELETE.ts create mode 100644 backend/src/event/EVENT_ADMIN_USER_ROLE_SET.ts create mode 100644 backend/src/event/EVENT_ADMIN_USER_UNDELETE.ts diff --git a/backend/src/event/EVENT_ADMIN_USER_DELETE.ts b/backend/src/event/EVENT_ADMIN_USER_DELETE.ts new file mode 100644 index 000000000..bfd5be740 --- /dev/null +++ b/backend/src/event/EVENT_ADMIN_USER_DELETE.ts @@ -0,0 +1,6 @@ +import { User as DbUser } from '@entity/User' +import { Event as DbEvent } from '@entity/Event' +import { Event, EventType } from './Event' + +export const EVENT_ADMIN_USER_DELETE = async (user: DbUser, moderator: DbUser): Promise => + Event(EventType.ADMIN_USER_DELETE, user, moderator).save() diff --git a/backend/src/event/EVENT_ADMIN_USER_ROLE_SET.ts b/backend/src/event/EVENT_ADMIN_USER_ROLE_SET.ts new file mode 100644 index 000000000..3be825ad4 --- /dev/null +++ b/backend/src/event/EVENT_ADMIN_USER_ROLE_SET.ts @@ -0,0 +1,8 @@ +import { User as DbUser } from '@entity/User' +import { Event as DbEvent } from '@entity/Event' +import { Event, EventType } from './Event' + +export const EVENT_ADMIN_USER_ROLE_SET = async ( + user: DbUser, + moderator: DbUser, +): Promise => Event(EventType.ADMIN_USER_ROLE_SET, user, moderator).save() diff --git a/backend/src/event/EVENT_ADMIN_USER_UNDELETE.ts b/backend/src/event/EVENT_ADMIN_USER_UNDELETE.ts new file mode 100644 index 000000000..eb861dbf1 --- /dev/null +++ b/backend/src/event/EVENT_ADMIN_USER_UNDELETE.ts @@ -0,0 +1,8 @@ +import { User as DbUser } from '@entity/User' +import { Event as DbEvent } from '@entity/Event' +import { Event, EventType } from './Event' + +export const EVENT_ADMIN_USER_UNDELETE = async ( + user: DbUser, + moderator: DbUser, +): Promise => Event(EventType.ADMIN_USER_UNDELETE, user, moderator).save() diff --git a/backend/src/event/EVENT_EMAIL_FORGOT_PASSWORD.ts b/backend/src/event/EVENT_EMAIL_FORGOT_PASSWORD.ts index 4160ce244..f7e328369 100644 --- a/backend/src/event/EVENT_EMAIL_FORGOT_PASSWORD.ts +++ b/backend/src/event/EVENT_EMAIL_FORGOT_PASSWORD.ts @@ -3,4 +3,4 @@ import { Event as DbEvent } from '@entity/Event' import { Event, EventType } from './Event' export const EVENT_EMAIL_FORGOT_PASSWORD = async (user: DbUser): Promise => - Event(EventType.EMAIL_FORGOT_PASSWORD, user, user).save() + Event(EventType.EMAIL_FORGOT_PASSWORD, user, { id: 0 } as DbUser).save() diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 60da91b70..901bc33ff 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -47,6 +47,9 @@ export { EVENT_ADMIN_CONTRIBUTION_LINK_DELETE } from './EVENT_ADMIN_CONTRIBUTION export { EVENT_ADMIN_CONTRIBUTION_LINK_UPDATE } from './EVENT_ADMIN_CONTRIBUTION_LINK_UPDATE' export { EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE } from './EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE' export { EVENT_ADMIN_SEND_CONFIRMATION_EMAIL } from './EVENT_ADMIN_SEND_CONFIRMATION_EMAIL' +export { EVENT_ADMIN_USER_DELETE } from './EVENT_ADMIN_USER_DELETE' +export { EVENT_ADMIN_USER_UNDELETE } from './EVENT_ADMIN_USER_UNDELETE' +export { EVENT_ADMIN_USER_ROLE_SET } from './EVENT_ADMIN_USER_ROLE_SET' export { EVENT_CONTRIBUTION_CREATE } from './EVENT_CONTRIBUTION_CREATE' export { EVENT_CONTRIBUTION_DELETE } from './EVENT_CONTRIBUTION_DELETE' export { EVENT_CONTRIBUTION_UPDATE } from './EVENT_CONTRIBUTION_UPDATE' @@ -63,3 +66,4 @@ export { EVENT_TRANSACTION_RECEIVE } from './EVENT_TRANSACTION_RECEIVE' export { EVENT_TRANSACTION_LINK_CREATE } from './EVENT_TRANSACTION_LINK_CREATE' export { EVENT_TRANSACTION_LINK_DELETE } from './EVENT_TRANSACTION_LINK_DELETE' export { EVENT_TRANSACTION_LINK_REDEEM } from './EVENT_TRANSACTION_LINK_REDEEM' +export { EVENT_USER_INFO_UPDATE } from './EVENT_USER_INFO_UPDATE' diff --git a/backend/src/event/EventType.ts b/backend/src/event/EventType.ts index cc277e589..58d03c84b 100644 --- a/backend/src/event/EventType.ts +++ b/backend/src/event/EventType.ts @@ -11,6 +11,9 @@ export enum EventType { ADMIN_CONTRIBUTION_LINK_UPDATE = 'ADMIN_CONTRIBUTION_LINK_UPDATE', ADMIN_CONTRIBUTION_MESSAGE_CREATE = 'ADMIN_CONTRIBUTION_MESSAGE_CREATE', ADMIN_SEND_CONFIRMATION_EMAIL = 'ADMIN_SEND_CONFIRMATION_EMAIL', + ADMIN_USER_DELETE = 'ADMIN_USER_DELETE', + ADMIN_USER_UNDELETE = 'ADMIN_USER_UNDELETE', + ADMIN_USER_ROLE_SET = 'ADMIN_USER_ROLE_SET', CONTRIBUTION_CREATE = 'CONTRIBUTION_CREATE', CONTRIBUTION_DELETE = 'CONTRIBUTION_DELETE', CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE', diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 196110a31..f51d0fdac 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -60,6 +60,9 @@ import { EVENT_LOGOUT, EVENT_EMAIL_FORGOT_PASSWORD, EVENT_USER_INFO_UPDATE, + EVENT_USER_ROLE_SET, + EVENT_ADMIN_USER_ROLE_SET, + EVENT_ADMIN_USER_DELETE, } from '@/event/Event' import { getUserCreations } from './util/creations' import { isValidPassword } from '@/password/EncryptorUtils' @@ -585,7 +588,6 @@ export class UserResolver { await queryRunner.release() } logger.info('updateUserInfos() successfully finished...') - await EVENT_USER_INFO_UPDATE(user) return true @@ -713,8 +715,8 @@ export class UserResolver { throw new LogError('Could not find user with given ID', userId) } // administrator user changes own role? - const moderatorUser = getUser(context) - if (moderatorUser.id === userId) { + const moderator = getUser(context) + if (moderator.id === userId) { throw new LogError('Administrator can not change his own role') } // change isAdmin @@ -735,6 +737,7 @@ export class UserResolver { break } await user.save() + await EVENT_ADMIN_USER_ROLE_SET(user, moderator) const newUser = await DbUser.findOne({ id: userId }) return newUser ? newUser.isAdmin : null } @@ -751,19 +754,20 @@ export class UserResolver { throw new LogError('Could not find user with given ID', userId) } // moderator user disabled own account? - const moderatorUser = getUser(context) - if (moderatorUser.id === userId) { + const moderator = getUser(context) + if (moderator.id === userId) { throw new LogError('Moderator can not delete his own account') } // soft-delete user await user.softRemove() + await EVENT_ADMIN_USER_DELETE(user, moderator) const newUser = await DbUser.findOne({ id: userId }, { withDeleted: true }) return newUser ? newUser.deletedAt : null } @Authorized([RIGHTS.ADMIN_UNDELETE_USER]) @Mutation(() => Date, { nullable: true }) - async unDeleteUser(@Arg('userId', () => Int) userId: number): Promise { + async unDeleteUser(@Arg('userId', () => Int) userId: number, @Ctx() context: Context,): Promise { const user = await DbUser.findOne({ id: userId }, { withDeleted: true }) if (!user) { throw new LogError('Could not find user with given ID', userId) @@ -772,6 +776,7 @@ export class UserResolver { throw new LogError('User is not deleted') } await user.recover() + await EVENT_ADMIN_USER_UNDELETE(user, getUser(context)) return null } From dfbfefd73af3b6ffd7cc874ee0bd1682c23cf4da Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Mar 2023 11:54:14 +0100 Subject: [PATCH 05/13] always write send confirmation event --- backend/src/graphql/resolver/UserResolver.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index f51d0fdac..1d80c650a 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -63,6 +63,7 @@ import { EVENT_USER_ROLE_SET, EVENT_ADMIN_USER_ROLE_SET, EVENT_ADMIN_USER_DELETE, + EVENT_ADMIN_USER_UNDELETE, } from '@/event/Event' import { getUserCreations } from './util/creations' import { isValidPassword } from '@/password/EncryptorUtils' @@ -767,7 +768,10 @@ export class UserResolver { @Authorized([RIGHTS.ADMIN_UNDELETE_USER]) @Mutation(() => Date, { nullable: true }) - async unDeleteUser(@Arg('userId', () => Int) userId: number, @Ctx() context: Context,): Promise { + async unDeleteUser( + @Arg('userId', () => Int) userId: number, + @Ctx() context: Context, + ): Promise { const user = await DbUser.findOne({ id: userId }, { withDeleted: true }) if (!user) { throw new LogError('Could not find user with given ID', userId) @@ -810,9 +814,8 @@ export class UserResolver { // In case EMails are disabled log the activation link for the user if (!emailSent) { logger.info(`Account confirmation link: ${activationLink}`) - } else { - await EVENT_ADMIN_SEND_CONFIRMATION_EMAIL(user, getUser(context)) } + await EVENT_ADMIN_SEND_CONFIRMATION_EMAIL(user, getUser(context)) return true } From ab4e1cfe1f07c898eae8f369f4cfb01a719a865a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Mar 2023 11:55:25 +0100 Subject: [PATCH 06/13] fixed imports --- backend/src/graphql/resolver/UserResolver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 1d80c650a..13fb74fd5 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -60,7 +60,6 @@ import { EVENT_LOGOUT, EVENT_EMAIL_FORGOT_PASSWORD, EVENT_USER_INFO_UPDATE, - EVENT_USER_ROLE_SET, EVENT_ADMIN_USER_ROLE_SET, EVENT_ADMIN_USER_DELETE, EVENT_ADMIN_USER_UNDELETE, From e79130ea868198680bd30b9a085f52d143e61e7b Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Mar 2023 12:19:53 +0100 Subject: [PATCH 07/13] test all events --- .../src/graphql/resolver/UserResolver.test.ts | 98 ++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index b382b2627..62eea9c0e 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -857,11 +857,25 @@ describe('UserResolver', () => { it('returns true', async () => { await expect(mutate({ mutation: logout })).resolves.toEqual( expect.objectContaining({ - data: { logout: 'true' }, + data: { logout: true }, errors: undefined, }), ) }) + + it('stores the LOGOUT event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.LOGOUT, + affectedUserId: userConatct.user.id, + actingUserId: userConatct.user.id, + }), + ) + }) }) }) @@ -1017,6 +1031,20 @@ describe('UserResolver', () => { }), }) }) + + it('stores the EMAIL_FORGOT_PASSWORD event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.EMAIL_FORGOT_PASSWORD, + affectedUserId: userConatct.user.id, + actingUserId: 0, + }), + ) + }) }) describe('request reset password again', () => { @@ -1141,6 +1169,20 @@ describe('UserResolver', () => { }), ) }) + + it('stores the USER_INFO_UPDATE event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.USER_INFO_UPDATE, + affectedUserId: userConatct.user.id, + actingUserId: userConatct.user.id, + }), + ) + }) }) describe('language is not valid', () => { @@ -1511,6 +1553,24 @@ describe('UserResolver', () => { ) expect(new Date(result.data.setUserRole)).toEqual(expect.any(Date)) }) + + it('stores the ADMIN_USER_ROLE_SET event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + const adminConatct = await UserContact.findOneOrFail( + { email: 'peter@lustig.de' }, + { relations: ['user'] }, + ) + expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.ADMIN_USER_ROLE_SET, + affectedUserId: userConatct.user.id, + actingUserId: adminConatct.user.id, + }), + ) + }) }) describe('to usual user', () => { @@ -1696,6 +1756,24 @@ describe('UserResolver', () => { expect(new Date(result.data.deleteUser)).toEqual(expect.any(Date)) }) + it('stores the ADMIN_USER_DELETE event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'], withDeleted: true }, + ) + const adminConatct = await UserContact.findOneOrFail( + { email: 'peter@lustig.de' }, + { relations: ['user'] }, + ) + expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.ADMIN_USER_DELETE, + affectedUserId: userConatct.user.id, + actingUserId: adminConatct.user.id, + }), + ) + }) + describe('delete deleted user', () => { it('throws an error', async () => { jest.clearAllMocks() @@ -1971,6 +2049,24 @@ describe('UserResolver', () => { }), ) }) + + it('stores the ADMIN_USER_UNDELETE event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + const adminConatct = await UserContact.findOneOrFail( + { email: 'peter@lustig.de' }, + { relations: ['user'] }, + ) + expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.ADMIN_USER_UNDELETE, + affectedUserId: userConatct.user.id, + actingUserId: adminConatct.user.id, + }), + ) + }) }) }) }) From d989e5c48977ddcac6bff943c99955904819dc61 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Mar 2023 13:29:46 +0100 Subject: [PATCH 08/13] rename events --- .../EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION.ts | 6 ++++ ...L.ts => EVENT_EMAIL_ADMIN_CONFIRMATION.ts} | 4 +-- ...ACCOUNT.ts => EVENT_EMAIL_CONFIRMATION.ts} | 4 +-- ...NT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL.ts | 6 ---- .../event/EVENT_SEND_CONFIRMATION_EMAIL.ts | 6 ---- .../src/event/EVENT_USER_ACTIVATE_ACCOUNT.ts | 6 ++++ ...{EVENT_REGISTER.ts => EVENT_USER_LOGIN.ts} | 4 +-- .../{EVENT_LOGIN.ts => EVENT_USER_LOGOUT.ts} | 4 +-- ...EVENT_LOGOUT.ts => EVENT_USER_REGISTER.ts} | 4 +-- backend/src/event/Event.ts | 14 ++++---- backend/src/event/EventType.ts | 17 +++++---- .../resolver/ContributionResolver.test.ts | 4 +-- .../src/graphql/resolver/UserResolver.test.ts | 36 +++++++++---------- backend/src/graphql/resolver/UserResolver.ts | 27 +++++++------- 14 files changed, 70 insertions(+), 72 deletions(-) create mode 100644 backend/src/event/EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION.ts rename backend/src/event/{EVENT_ADMIN_SEND_CONFIRMATION_EMAIL.ts => EVENT_EMAIL_ADMIN_CONFIRMATION.ts} (53%) rename backend/src/event/{EVENT_ACTIVATE_ACCOUNT.ts => EVENT_EMAIL_CONFIRMATION.ts} (50%) delete mode 100644 backend/src/event/EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL.ts delete mode 100644 backend/src/event/EVENT_SEND_CONFIRMATION_EMAIL.ts create mode 100644 backend/src/event/EVENT_USER_ACTIVATE_ACCOUNT.ts rename backend/src/event/{EVENT_REGISTER.ts => EVENT_USER_LOGIN.ts} (53%) rename backend/src/event/{EVENT_LOGIN.ts => EVENT_USER_LOGOUT.ts} (52%) rename backend/src/event/{EVENT_LOGOUT.ts => EVENT_USER_REGISTER.ts} (51%) diff --git a/backend/src/event/EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION.ts b/backend/src/event/EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION.ts new file mode 100644 index 000000000..c16bbfac3 --- /dev/null +++ b/backend/src/event/EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION.ts @@ -0,0 +1,6 @@ +import { User as DbUser } from '@entity/User' +import { Event as DbEvent } from '@entity/Event' +import { Event, EventType } from './Event' + +export const EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION = async (user: DbUser): Promise => + Event(EventType.EMAIL_ACCOUNT_MULTIREGISTRATION, user, { id: 0 } as DbUser).save() diff --git a/backend/src/event/EVENT_ADMIN_SEND_CONFIRMATION_EMAIL.ts b/backend/src/event/EVENT_EMAIL_ADMIN_CONFIRMATION.ts similarity index 53% rename from backend/src/event/EVENT_ADMIN_SEND_CONFIRMATION_EMAIL.ts rename to backend/src/event/EVENT_EMAIL_ADMIN_CONFIRMATION.ts index da4907930..ae10f9fba 100644 --- a/backend/src/event/EVENT_ADMIN_SEND_CONFIRMATION_EMAIL.ts +++ b/backend/src/event/EVENT_EMAIL_ADMIN_CONFIRMATION.ts @@ -2,7 +2,7 @@ import { User as DbUser } from '@entity/User' import { Event as DbEvent } from '@entity/Event' import { Event, EventType } from './Event' -export const EVENT_ADMIN_SEND_CONFIRMATION_EMAIL = async ( +export const EVENT_EMAIL_ADMIN_CONFIRMATION = async ( user: DbUser, moderator: DbUser, -): Promise => Event(EventType.ADMIN_SEND_CONFIRMATION_EMAIL, user, moderator).save() +): Promise => Event(EventType.EMAIL_ADMIN_CONFIRMATION, user, moderator).save() diff --git a/backend/src/event/EVENT_ACTIVATE_ACCOUNT.ts b/backend/src/event/EVENT_EMAIL_CONFIRMATION.ts similarity index 50% rename from backend/src/event/EVENT_ACTIVATE_ACCOUNT.ts rename to backend/src/event/EVENT_EMAIL_CONFIRMATION.ts index 755cc8fe2..9d64207e0 100644 --- a/backend/src/event/EVENT_ACTIVATE_ACCOUNT.ts +++ b/backend/src/event/EVENT_EMAIL_CONFIRMATION.ts @@ -2,5 +2,5 @@ import { User as DbUser } from '@entity/User' import { Event as DbEvent } from '@entity/Event' import { Event, EventType } from './Event' -export const EVENT_ACTIVATE_ACCOUNT = async (user: DbUser): Promise => - Event(EventType.ACTIVATE_ACCOUNT, user, user).save() +export const EVENT_EMAIL_CONFIRMATION = async (user: DbUser): Promise => + Event(EventType.EMAIL_CONFIRMATION, user, user).save() diff --git a/backend/src/event/EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL.ts b/backend/src/event/EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL.ts deleted file mode 100644 index 3110ece1f..000000000 --- a/backend/src/event/EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { User as DbUser } from '@entity/User' -import { Event as DbEvent } from '@entity/Event' -import { Event, EventType } from './Event' - -export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async (user: DbUser): Promise => - Event(EventType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, user, { id: 0 } as DbUser).save() diff --git a/backend/src/event/EVENT_SEND_CONFIRMATION_EMAIL.ts b/backend/src/event/EVENT_SEND_CONFIRMATION_EMAIL.ts deleted file mode 100644 index b387c0e60..000000000 --- a/backend/src/event/EVENT_SEND_CONFIRMATION_EMAIL.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { User as DbUser } from '@entity/User' -import { Event as DbEvent } from '@entity/Event' -import { Event, EventType } from './Event' - -export const EVENT_SEND_CONFIRMATION_EMAIL = async (user: DbUser): Promise => - Event(EventType.SEND_CONFIRMATION_EMAIL, user, user).save() diff --git a/backend/src/event/EVENT_USER_ACTIVATE_ACCOUNT.ts b/backend/src/event/EVENT_USER_ACTIVATE_ACCOUNT.ts new file mode 100644 index 000000000..2e224d550 --- /dev/null +++ b/backend/src/event/EVENT_USER_ACTIVATE_ACCOUNT.ts @@ -0,0 +1,6 @@ +import { User as DbUser } from '@entity/User' +import { Event as DbEvent } from '@entity/Event' +import { Event, EventType } from './Event' + +export const EVENT_USER_ACTIVATE_ACCOUNT = async (user: DbUser): Promise => + Event(EventType.USER_ACTIVATE_ACCOUNT, user, user).save() diff --git a/backend/src/event/EVENT_REGISTER.ts b/backend/src/event/EVENT_USER_LOGIN.ts similarity index 53% rename from backend/src/event/EVENT_REGISTER.ts rename to backend/src/event/EVENT_USER_LOGIN.ts index 73c6bf4f9..351ec5a95 100644 --- a/backend/src/event/EVENT_REGISTER.ts +++ b/backend/src/event/EVENT_USER_LOGIN.ts @@ -2,5 +2,5 @@ import { User as DbUser } from '@entity/User' import { Event as DbEvent } from '@entity/Event' import { Event, EventType } from './Event' -export const EVENT_REGISTER = async (user: DbUser): Promise => - Event(EventType.REGISTER, user, user).save() +export const EVENT_USER_LOGIN = async (user: DbUser): Promise => + Event(EventType.USER_LOGIN, user, user).save() diff --git a/backend/src/event/EVENT_LOGIN.ts b/backend/src/event/EVENT_USER_LOGOUT.ts similarity index 52% rename from backend/src/event/EVENT_LOGIN.ts rename to backend/src/event/EVENT_USER_LOGOUT.ts index 2c1e763ec..4f5650fc6 100644 --- a/backend/src/event/EVENT_LOGIN.ts +++ b/backend/src/event/EVENT_USER_LOGOUT.ts @@ -2,5 +2,5 @@ import { User as DbUser } from '@entity/User' import { Event as DbEvent } from '@entity/Event' import { Event, EventType } from './Event' -export const EVENT_LOGIN = async (user: DbUser): Promise => - Event(EventType.LOGIN, user, user).save() +export const EVENT_USER_LOGOUT = async (user: DbUser): Promise => + Event(EventType.USER_LOGOUT, user, user).save() diff --git a/backend/src/event/EVENT_LOGOUT.ts b/backend/src/event/EVENT_USER_REGISTER.ts similarity index 51% rename from backend/src/event/EVENT_LOGOUT.ts rename to backend/src/event/EVENT_USER_REGISTER.ts index 1e423359c..cdb8b22e2 100644 --- a/backend/src/event/EVENT_LOGOUT.ts +++ b/backend/src/event/EVENT_USER_REGISTER.ts @@ -2,5 +2,5 @@ import { User as DbUser } from '@entity/User' import { Event as DbEvent } from '@entity/Event' import { Event, EventType } from './Event' -export const EVENT_LOGOUT = async (user: DbUser): Promise => - Event(EventType.LOGOUT, user, user).save() +export const EVENT_USER_REGISTER = async (user: DbUser): Promise => + Event(EventType.USER_REGISTER, user, user).save() diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 901bc33ff..dc2db17eb 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -36,7 +36,6 @@ export const Event = ( export { EventType } from './EventType' -export { EVENT_ACTIVATE_ACCOUNT } from './EVENT_ACTIVATE_ACCOUNT' export { EVENT_ADMIN_CONTRIBUTION_CONFIRM } from './EVENT_ADMIN_CONTRIBUTION_CONFIRM' export { EVENT_ADMIN_CONTRIBUTION_CREATE } from './EVENT_ADMIN_CONTRIBUTION_CREATE' export { EVENT_ADMIN_CONTRIBUTION_DELETE } from './EVENT_ADMIN_CONTRIBUTION_DELETE' @@ -46,7 +45,6 @@ export { EVENT_ADMIN_CONTRIBUTION_LINK_CREATE } from './EVENT_ADMIN_CONTRIBUTION export { EVENT_ADMIN_CONTRIBUTION_LINK_DELETE } from './EVENT_ADMIN_CONTRIBUTION_LINK_DELETE' export { EVENT_ADMIN_CONTRIBUTION_LINK_UPDATE } from './EVENT_ADMIN_CONTRIBUTION_LINK_UPDATE' export { EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE } from './EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE' -export { EVENT_ADMIN_SEND_CONFIRMATION_EMAIL } from './EVENT_ADMIN_SEND_CONFIRMATION_EMAIL' export { EVENT_ADMIN_USER_DELETE } from './EVENT_ADMIN_USER_DELETE' export { EVENT_ADMIN_USER_UNDELETE } from './EVENT_ADMIN_USER_UNDELETE' export { EVENT_ADMIN_USER_ROLE_SET } from './EVENT_ADMIN_USER_ROLE_SET' @@ -55,15 +53,17 @@ export { EVENT_CONTRIBUTION_DELETE } from './EVENT_CONTRIBUTION_DELETE' export { EVENT_CONTRIBUTION_UPDATE } from './EVENT_CONTRIBUTION_UPDATE' export { EVENT_CONTRIBUTION_MESSAGE_CREATE } from './EVENT_CONTRIBUTION_MESSAGE_CREATE' export { EVENT_CONTRIBUTION_LINK_REDEEM } from './EVENT_CONTRIBUTION_LINK_REDEEM' +export { EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION } from './EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION' +export { EVENT_EMAIL_ADMIN_CONFIRMATION } from './EVENT_EMAIL_ADMIN_CONFIRMATION' +export { EVENT_EMAIL_CONFIRMATION } from './EVENT_EMAIL_CONFIRMATION' export { EVENT_EMAIL_FORGOT_PASSWORD } from './EVENT_EMAIL_FORGOT_PASSWORD' -export { EVENT_LOGIN } from './EVENT_LOGIN' -export { EVENT_LOGOUT } from './EVENT_LOGOUT' -export { EVENT_REGISTER } from './EVENT_REGISTER' -export { EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL } from './EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL' -export { EVENT_SEND_CONFIRMATION_EMAIL } from './EVENT_SEND_CONFIRMATION_EMAIL' export { EVENT_TRANSACTION_SEND } from './EVENT_TRANSACTION_SEND' export { EVENT_TRANSACTION_RECEIVE } from './EVENT_TRANSACTION_RECEIVE' export { EVENT_TRANSACTION_LINK_CREATE } from './EVENT_TRANSACTION_LINK_CREATE' export { EVENT_TRANSACTION_LINK_DELETE } from './EVENT_TRANSACTION_LINK_DELETE' export { EVENT_TRANSACTION_LINK_REDEEM } from './EVENT_TRANSACTION_LINK_REDEEM' +export { EVENT_USER_ACTIVATE_ACCOUNT } from './EVENT_USER_ACTIVATE_ACCOUNT' export { EVENT_USER_INFO_UPDATE } from './EVENT_USER_INFO_UPDATE' +export { EVENT_USER_LOGIN } from './EVENT_USER_LOGIN' +export { EVENT_USER_LOGOUT } from './EVENT_USER_LOGOUT' +export { EVENT_USER_REGISTER } from './EVENT_USER_REGISTER' diff --git a/backend/src/event/EventType.ts b/backend/src/event/EventType.ts index 58d03c84b..959a848f5 100644 --- a/backend/src/event/EventType.ts +++ b/backend/src/event/EventType.ts @@ -1,5 +1,4 @@ export enum EventType { - ACTIVATE_ACCOUNT = 'ACTIVATE_ACCOUNT', // TODO CONTRIBUTION_CONFIRM = 'CONTRIBUTION_CONFIRM', ADMIN_CONTRIBUTION_CONFIRM = 'ADMIN_CONTRIBUTION_CONFIRM', ADMIN_CONTRIBUTION_CREATE = 'ADMIN_CONTRIBUTION_CREATE', @@ -10,7 +9,6 @@ export enum EventType { ADMIN_CONTRIBUTION_LINK_DELETE = 'ADMIN_CONTRIBUTION_LINK_DELETE', ADMIN_CONTRIBUTION_LINK_UPDATE = 'ADMIN_CONTRIBUTION_LINK_UPDATE', ADMIN_CONTRIBUTION_MESSAGE_CREATE = 'ADMIN_CONTRIBUTION_MESSAGE_CREATE', - ADMIN_SEND_CONFIRMATION_EMAIL = 'ADMIN_SEND_CONFIRMATION_EMAIL', ADMIN_USER_DELETE = 'ADMIN_USER_DELETE', ADMIN_USER_UNDELETE = 'ADMIN_USER_UNDELETE', ADMIN_USER_ROLE_SET = 'ADMIN_USER_ROLE_SET', @@ -19,25 +17,26 @@ export enum EventType { CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE', CONTRIBUTION_MESSAGE_CREATE = 'CONTRIBUTION_MESSAGE_CREATE', CONTRIBUTION_LINK_REDEEM = 'CONTRIBUTION_LINK_REDEEM', + EMAIL_ACCOUNT_MULTIREGISTRATION = 'EMAIL_ACCOUNT_MULTIREGISTRATION', + EMAIL_ADMIN_CONFIRMATION = 'EMAIL_ADMIN_CONFIRMATION', + EMAIL_CONFIRMATION = 'EMAIL_CONFIRMATION', EMAIL_FORGOT_PASSWORD = 'EMAIL_FORGOT_PASSWORD', - LOGIN = 'LOGIN', - LOGOUT = 'LOGOUT', - REGISTER = 'REGISTER', - REDEEM_REGISTER = 'REDEEM_REGISTER', - SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = 'SEND_ACCOUNT_MULTIREGISTRATION_EMAIL', - SEND_CONFIRMATION_EMAIL = 'SEND_CONFIRMATION_EMAIL', TRANSACTION_SEND = 'TRANSACTION_SEND', TRANSACTION_RECEIVE = 'TRANSACTION_RECEIVE', TRANSACTION_LINK_CREATE = 'TRANSACTION_LINK_CREATE', TRANSACTION_LINK_DELETE = 'TRANSACTION_LINK_DELETE', TRANSACTION_LINK_REDEEM = 'TRANSACTION_LINK_REDEEM', + USER_ACTIVATE_ACCOUNT = 'ACTIVATE_ACCOUNT', USER_INFO_UPDATE = 'USER_INFO_UPDATE', + USER_LOGIN = 'USER_LOGIN', + USER_LOGOUT = 'USER_LOGOUT', + USER_REGISTER = 'USER_REGISTER', + USER_REGISTER_REDEEM = 'USER_REGISTER_REDEEM', // VISIT_GRADIDO = 'VISIT_GRADIDO', // VERIFY_REDEEM = 'VERIFY_REDEEM', // INACTIVE_ACCOUNT = 'INACTIVE_ACCOUNT', // CONFIRM_EMAIL = 'CONFIRM_EMAIL', // REGISTER_EMAIL_KLICKTIPP = 'REGISTER_EMAIL_KLICKTIPP', - // LOGOUT = 'LOGOUT', // REDEEM_LOGIN = 'REDEEM_LOGIN', // SEND_FORGOT_PASSWORD_EMAIL = 'SEND_FORGOT_PASSWORD_EMAIL', // PASSWORD_CHANGE = 'PASSWORD_CHANGE', diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 2b59bde8b..1ac1260a5 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -2573,10 +2573,10 @@ describe('ContributionResolver', () => { }) }) - it('stores the SEND_CONFIRMATION_EMAIL event in the database', async () => { + it('stores the EMAIL_CONFIRMATION event in the database', async () => { await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.SEND_CONFIRMATION_EMAIL, + type: EventType.EMAIL_CONFIRMATION, }), ) }) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 62eea9c0e..6843336cc 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -177,14 +177,14 @@ describe('UserResolver', () => { }) }) - it('stores the REGISTER event in the database', async () => { + it('stores the USER_REGISTER event in the database', async () => { const userConatct = await UserContact.findOneOrFail( { email: 'peter@lustig.de' }, { relations: ['user'] }, ) expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.REGISTER, + type: EventType.USER_REGISTER, affectedUserId: userConatct.user.id, actingUserId: userConatct.user.id, }), @@ -211,10 +211,10 @@ describe('UserResolver', () => { }) }) - it('stores the SEND_CONFIRMATION_EMAIL event in the database', () => { + it('stores the EMAIL_CONFIRMATION event in the database', () => { expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.SEND_CONFIRMATION_EMAIL, + type: EventType.EMAIL_CONFIRMATION, affectedUserId: user[0].id, actingUserId: user[0].id, }), @@ -253,14 +253,14 @@ describe('UserResolver', () => { ) }) - it('stores the SEND_ACCOUNT_MULTIREGISTRATION_EMAIL event in the database', async () => { + it('stores the EMAIL_ACCOUNT_MULTIREGISTRATION event in the database', async () => { const userConatct = await UserContact.findOneOrFail( { email: 'peter@lustig.de' }, { relations: ['user'] }, ) expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, + type: EventType.EMAIL_ACCOUNT_MULTIREGISTRATION, affectedUserId: userConatct.user.id, actingUserId: 0, }), @@ -358,20 +358,20 @@ describe('UserResolver', () => { ) }) - it('stores the ACTIVATE_ACCOUNT event in the database', () => { + it('stores the USER_ACTIVATE_ACCOUNT event in the database', () => { expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.ACTIVATE_ACCOUNT, + type: EventType.USER_ACTIVATE_ACCOUNT, affectedUserId: user[0].id, actingUserId: user[0].id, }), ) }) - it('stores the REDEEM_REGISTER event in the database', () => { + it('stores the USER_REGISTER_REDEEM event in the database', () => { expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.REDEEM_REGISTER, + type: EventType.USER_REGISTER_REDEEM, affectedUserId: result.data.createUser.id, actingUserId: result.data.createUser.id, involvedContributionLinkId: link.id, @@ -453,10 +453,10 @@ describe('UserResolver', () => { ) }) - it('stores the REDEEM_REGISTER event in the database', async () => { + it('stores the USER_REGISTER_REDEEM event in the database', async () => { await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.REDEEM_REGISTER, + type: EventType.USER_REGISTER_REDEEM, affectedUserId: newUser.data.createUser.id, actingUserId: newUser.data.createUser.id, involvedTransactionLinkId: transactionLink.id, @@ -682,14 +682,14 @@ describe('UserResolver', () => { expect(headerPushMock).toBeCalledWith({ key: 'token', value: expect.any(String) }) }) - it('stores the LOGIN event in the database', async () => { + it('stores the USER_LOGIN event in the database', async () => { const userConatct = await UserContact.findOneOrFail( { email: 'bibi@bloxberg.de' }, { relations: ['user'] }, ) expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.LOGIN, + type: EventType.USER_LOGIN, affectedUserId: userConatct.user.id, actingUserId: userConatct.user.id, }), @@ -863,14 +863,14 @@ describe('UserResolver', () => { ) }) - it('stores the LOGOUT event in the database', async () => { + it('stores the USER_LOGOUT event in the database', async () => { const userConatct = await UserContact.findOneOrFail( { email: 'bibi@bloxberg.de' }, { relations: ['user'] }, ) expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.LOGOUT, + type: EventType.USER_LOGOUT, affectedUserId: userConatct.user.id, actingUserId: userConatct.user.id, }), @@ -949,10 +949,10 @@ describe('UserResolver', () => { ) }) - it('stores the LOGIN event in the database', () => { + it('stores the USER_LOGIN event in the database', () => { expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.LOGIN, + type: EventType.USER_LOGIN, affectedUserId: user[0].id, actingUserId: user[0].id, }), diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 13fb74fd5..7fc043089 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -51,13 +51,13 @@ import { hasElopageBuys } from '@/util/hasElopageBuys' import { Event, EventType, - EVENT_LOGIN, - EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, - EVENT_SEND_CONFIRMATION_EMAIL, - EVENT_REGISTER, - EVENT_ACTIVATE_ACCOUNT, + EVENT_USER_LOGIN, + EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION, + EVENT_EMAIL_CONFIRMATION, + EVENT_USER_REGISTER, + EVENT_USER_ACTIVATE_ACCOUNT, EVENT_ADMIN_SEND_CONFIRMATION_EMAIL, - EVENT_LOGOUT, + EVENT_USER_LOGOUT, EVENT_EMAIL_FORGOT_PASSWORD, EVENT_USER_INFO_UPDATE, EVENT_ADMIN_USER_ROLE_SET, @@ -184,7 +184,7 @@ export class UserResolver { value: encode(dbUser.gradidoID), }) - await EVENT_LOGIN(dbUser) + await EVENT_USER_LOGIN(dbUser) logger.info(`successful Login: ${JSON.stringify(user, null, 2)}`) return user } @@ -192,7 +192,7 @@ export class UserResolver { @Authorized([RIGHTS.LOGOUT]) @Mutation(() => Boolean) async logout(@Ctx() context: Context): Promise { - await EVENT_LOGOUT(getUser(context)) + await EVENT_USER_LOGOUT(getUser(context)) // remove user from logger context logger.addContext('user', 'unknown') return true @@ -247,8 +247,7 @@ export class UserResolver { email, language: foundUser.language, // use language of the emails owner for sending }) - - await EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL(foundUser) + await EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION(foundUser) logger.info( `sendAccountMultiRegistrationEmail by ${firstName} ${lastName} to ${foundUser.firstName} ${foundUser.lastName} <${email}>`, @@ -267,7 +266,7 @@ export class UserResolver { const gradidoID = await newGradidoID() const eventRegisterRedeem = Event( - EventType.REDEEM_REGISTER, + EventType.USER_REGISTER_REDEEM, { id: 0 } as DbUser, { id: 0 } as DbUser, ) @@ -333,7 +332,7 @@ export class UserResolver { }) logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`) - await EVENT_SEND_CONFIRMATION_EMAIL(dbUser) + await EVENT_EMAIL_CONFIRMATION(dbUser) if (!emailSent) { logger.debug(`Account confirmation link: ${activationLink}`) @@ -354,7 +353,7 @@ export class UserResolver { eventRegisterRedeem.actingUser = dbUser await eventRegisterRedeem.save() } else { - await EVENT_REGISTER(dbUser) + await EVENT_USER_REGISTER(dbUser) } return new User(dbUser) @@ -489,7 +488,7 @@ export class UserResolver { logger.error('Error subscribing to klicktipp', e) } } - await EVENT_ACTIVATE_ACCOUNT(user) + await EVENT_USER_ACTIVATE_ACCOUNT(user) return true } From 247adbc6505ec1ac3b8828d42e72b4992cc43d86 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Mar 2023 13:35:13 +0100 Subject: [PATCH 09/13] database migration 0064 --- backend/src/config/index.ts | 2 +- database/migrations/0064-event_rename.ts | 50 ++++++++++++++++++++++++ dht-node/src/config/index.ts | 2 +- federation/src/config/index.ts | 2 +- 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 database/migrations/0064-event_rename.ts diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index ef537d804..79e513a6c 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -10,7 +10,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0063-event_link_fields', + DB_VERSION: '0064-event_rename', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info diff --git a/database/migrations/0064-event_rename.ts b/database/migrations/0064-event_rename.ts new file mode 100644 index 000000000..992b30de9 --- /dev/null +++ b/database/migrations/0064-event_rename.ts @@ -0,0 +1,50 @@ +/* MIGRATION TO CHANGE EVENT NAMES + * + * This migration renames several events to ensure consistent + * naming conventions. + */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + 'UPDATE `events` SET `type` = "USER_ACTIVATE_ACCOUNT" WHERE `type` = "ACTIVATE_ACCOUNT";', + ) + await queryFn('UPDATE `events` SET `type` = "USER_LOGIN" WHERE `type` = "LOGIN";') + await queryFn('UPDATE `events` SET `type` = "USER_LOGOUT" WHERE `type` = "LOGOUT";') + await queryFn('UPDATE `events` SET `type` = "USER_REGISTER" WHERE `type` = "REGISTER";') + await queryFn( + 'UPDATE `events` SET `type` = "EMAIL_ACCOUNT_MULTIREGISTRATION" WHERE `type` = "SEND_ACCOUNT_MULTIREGISTRATION_EMAIL";', + ) + await queryFn( + 'UPDATE `events` SET `type` = "EMAIL_CONFIRMATION" WHERE `type` = "SEND_CONFIRMATION_EMAIL";', + ) + await queryFn( + 'UPDATE `events` SET `type` = "EMAIL_ADMIN_CONFIRMATION" WHERE `type` = "ADMIN_SEND_CONFIRMATION_EMAIL";', + ) + await queryFn( + 'UPDATE `events` SET `type` = "USER_REGISTER_REDEEM" WHERE `type` = "REDEEM_REGISTER";', + ) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + 'UPDATE `events` SET `type` = "REDEEM_REGISTER" WHERE `type` = "USER_REGISTER_REDEEM";', + ) + await queryFn( + 'UPDATE `events` SET `type` = "ADMIN_SEND_CONFIRMATION_EMAIL" WHERE `type` = "EMAIL_ADMIN_CONFIRMATION";', + ) + await queryFn( + 'UPDATE `events` SET `type` = "SEND_CONFIRMATION_EMAIL" WHERE `type` = "EMAIL_CONFIRMATION";', + ) + await queryFn( + 'UPDATE `events` SET `type` = "SEND_ACCOUNT_MULTIREGISTRATION_EMAIL" WHERE `type` = "EMAIL_ACCOUNT_MULTIREGISTRATION";', + ) + await queryFn('UPDATE `events` SET `type` = "REGISTER" WHERE `type` = "USER_REGISTER";') + await queryFn('UPDATE `events` SET `type` = "LOGOUT" WHERE `type` = "USER_LOGOUT";') + await queryFn('UPDATE `events` SET `type` = "LOGIN" WHERE `type` = "USER_LOGIN";') + await queryFn( + 'UPDATE `events` SET `type` = "ACTIVATE_ACCOUNT" WHERE `type` = "USER_ACTIVATE_ACCOUNT";', + ) +} diff --git a/dht-node/src/config/index.ts b/dht-node/src/config/index.ts index 78f2b162c..078c0fbf3 100644 --- a/dht-node/src/config/index.ts +++ b/dht-node/src/config/index.ts @@ -3,7 +3,7 @@ import dotenv from 'dotenv' dotenv.config() const constants = { - DB_VERSION: '0063-event_link_fields', + DB_VERSION: '0064-event_rename', LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info LOG_LEVEL: process.env.LOG_LEVEL || 'info', diff --git a/federation/src/config/index.ts b/federation/src/config/index.ts index 61204ca37..f9c002d73 100644 --- a/federation/src/config/index.ts +++ b/federation/src/config/index.ts @@ -11,7 +11,7 @@ Decimal.set({ */ const constants = { - DB_VERSION: '0063-event_link_fields', + DB_VERSION: '0064-event_rename', // DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info From 517169a930ff23464a8e733424e481338f4388a2 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Mar 2023 13:38:44 +0100 Subject: [PATCH 10/13] fix event name --- backend/src/graphql/resolver/UserResolver.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 6843336cc..3a356afb5 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -1929,14 +1929,14 @@ describe('UserResolver', () => { }) }) - it('stores the ADMIN_SEND_CONFIRMATION_EMAIL event in the database', async () => { + it('stores the EMAIL_ADMIN_CONFIRMATION event in the database', async () => { const userConatct = await UserContact.findOneOrFail( { email: 'bibi@bloxberg.de' }, { relations: ['user'] }, ) expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.ADMIN_SEND_CONFIRMATION_EMAIL, + type: EventType.EMAIL_ADMIN_CONFIRMATION, affectedUserId: userConatct.user.id, actingUserId: admin.id, }), From ce799541f9997df5934f10ec8d21877f76ed43ec Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 9 Mar 2023 13:39:24 +0100 Subject: [PATCH 11/13] fix event name resolver --- backend/src/graphql/resolver/UserResolver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 7fc043089..4ac7fb8ab 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -56,7 +56,7 @@ import { EVENT_EMAIL_CONFIRMATION, EVENT_USER_REGISTER, EVENT_USER_ACTIVATE_ACCOUNT, - EVENT_ADMIN_SEND_CONFIRMATION_EMAIL, + EVENT_EMAIL_ADMIN_CONFIRMATION, EVENT_USER_LOGOUT, EVENT_EMAIL_FORGOT_PASSWORD, EVENT_USER_INFO_UPDATE, @@ -813,7 +813,7 @@ export class UserResolver { if (!emailSent) { logger.info(`Account confirmation link: ${activationLink}`) } - await EVENT_ADMIN_SEND_CONFIRMATION_EMAIL(user, getUser(context)) + await EVENT_EMAIL_ADMIN_CONFIRMATION(user, getUser(context)) return true } From f6071d6e078620429472b83875c4ec3222e8f638 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 28 Mar 2023 00:25:14 +0200 Subject: [PATCH 12/13] fix linting --- backend/src/graphql/resolver/UserResolver.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index b30e82f13..205f27e94 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -873,7 +873,7 @@ describe('UserResolver', () => { { email: 'bibi@bloxberg.de' }, { relations: ['user'] }, ) - expect(DbEvent.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventType.LOGOUT, affectedUserId: userConatct.user.id, @@ -1042,7 +1042,7 @@ describe('UserResolver', () => { { email: 'bibi@bloxberg.de' }, { relations: ['user'] }, ) - expect(DbEvent.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventType.EMAIL_FORGOT_PASSWORD, affectedUserId: userConatct.user.id, @@ -1180,7 +1180,7 @@ describe('UserResolver', () => { { email: 'bibi@bloxberg.de' }, { relations: ['user'] }, ) - expect(DbEvent.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventType.USER_INFO_UPDATE, affectedUserId: userConatct.user.id, @@ -1568,7 +1568,7 @@ describe('UserResolver', () => { { email: 'peter@lustig.de' }, { relations: ['user'] }, ) - expect(DbEvent.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventType.ADMIN_USER_ROLE_SET, affectedUserId: userConatct.user.id, @@ -1770,7 +1770,7 @@ describe('UserResolver', () => { { email: 'peter@lustig.de' }, { relations: ['user'] }, ) - expect(DbEvent.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventType.ADMIN_USER_DELETE, affectedUserId: userConatct.user.id, @@ -2064,7 +2064,7 @@ describe('UserResolver', () => { { email: 'peter@lustig.de' }, { relations: ['user'] }, ) - expect(DbEvent.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventType.ADMIN_USER_UNDELETE, affectedUserId: userConatct.user.id, From e08c156acfad5122772d5c8ebf051fef143a0c63 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 29 Mar 2023 09:47:51 +0200 Subject: [PATCH 13/13] merge conflict solved --- backend/src/graphql/resolver/UserResolver.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 93dcde73c..d3c78e2af 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -216,7 +216,7 @@ describe('UserResolver', () => { }) }) - it('stores the EMAIL_CONFIRMATION event in the database', () => { + it('stores the EMAIL_CONFIRMATION event in the database', async () => { await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventType.EMAIL_CONFIRMATION, @@ -363,7 +363,7 @@ describe('UserResolver', () => { ) }) - it('stores the USER_ACTIVATE_ACCOUNT event in the database', () => { + it('stores the USER_ACTIVATE_ACCOUNT event in the database', async () => { await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventType.USER_ACTIVATE_ACCOUNT, @@ -373,7 +373,7 @@ describe('UserResolver', () => { ) }) - it('stores the USER_REGISTER_REDEEM event in the database', () => { + it('stores the USER_REGISTER_REDEEM event in the database', async () => { await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventType.USER_REGISTER_REDEEM, @@ -955,7 +955,7 @@ describe('UserResolver', () => { ) }) - it('stores the USER_LOGIN event in the database', () => { + it('stores the USER_LOGIN event in the database', async () => { await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventType.USER_LOGIN,