diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index eb6434c8b..13e339b13 100644 --- a/backend/src/config/index.ts +++ b/backend/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 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_SEND_CONFIRMATION_EMAIL.ts b/backend/src/event/EVENT_ADMIN_USER_ROLE_SET.ts similarity index 59% rename from backend/src/event/EVENT_ADMIN_SEND_CONFIRMATION_EMAIL.ts rename to backend/src/event/EVENT_ADMIN_USER_ROLE_SET.ts index d0e363b05..0fc33da3d 100644 --- a/backend/src/event/EVENT_ADMIN_SEND_CONFIRMATION_EMAIL.ts +++ b/backend/src/event/EVENT_ADMIN_USER_ROLE_SET.ts @@ -3,7 +3,7 @@ import { Event as DbEvent } from '@entity/Event' /* eslint-disable-next-line import/no-cycle */ import { Event, EventType } from './Event' -export const EVENT_ADMIN_SEND_CONFIRMATION_EMAIL = async ( +export const EVENT_ADMIN_USER_ROLE_SET = async ( user: DbUser, moderator: DbUser, -): Promise => Event(EventType.ADMIN_SEND_CONFIRMATION_EMAIL, user, moderator).save() +): 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_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_EMAIL_ADMIN_CONFIRMATION.ts b/backend/src/event/EVENT_EMAIL_ADMIN_CONFIRMATION.ts new file mode 100644 index 000000000..ae10f9fba --- /dev/null +++ b/backend/src/event/EVENT_EMAIL_ADMIN_CONFIRMATION.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_EMAIL_ADMIN_CONFIRMATION = async ( + user: DbUser, + moderator: DbUser, +): Promise => Event(EventType.EMAIL_ADMIN_CONFIRMATION, user, moderator).save() diff --git a/backend/src/event/EVENT_EMAIL_CONFIRMATION.ts b/backend/src/event/EVENT_EMAIL_CONFIRMATION.ts new file mode 100644 index 000000000..9d64207e0 --- /dev/null +++ b/backend/src/event/EVENT_EMAIL_CONFIRMATION.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_CONFIRMATION = async (user: DbUser): Promise => + Event(EventType.EMAIL_CONFIRMATION, user, user).save() 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..f7e328369 --- /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, { id: 0 } as DbUser).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 7c991fba4..000000000 --- a/backend/src/event/EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { User as DbUser } from '@entity/User' -import { Event as DbEvent } from '@entity/Event' -/* eslint-disable-next-line import/no-cycle */ -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 78fc2b867..000000000 --- a/backend/src/event/EVENT_SEND_CONFIRMATION_EMAIL.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { User as DbUser } from '@entity/User' -import { Event as DbEvent } from '@entity/Event' -/* eslint-disable-next-line import/no-cycle */ -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_ACTIVATE_ACCOUNT.ts b/backend/src/event/EVENT_USER_INFO_UPDATE.ts similarity index 62% rename from backend/src/event/EVENT_ACTIVATE_ACCOUNT.ts rename to backend/src/event/EVENT_USER_INFO_UPDATE.ts index 98328a008..b69bdcaf9 100644 --- a/backend/src/event/EVENT_ACTIVATE_ACCOUNT.ts +++ b/backend/src/event/EVENT_USER_INFO_UPDATE.ts @@ -3,5 +3,5 @@ import { Event as DbEvent } from '@entity/Event' /* eslint-disable-next-line import/no-cycle */ import { Event, EventType } from './Event' -export const EVENT_ACTIVATE_ACCOUNT = async (user: DbUser): Promise => - Event(EventType.ACTIVATE_ACCOUNT, user, user).save() +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_LOGIN.ts b/backend/src/event/EVENT_USER_LOGIN.ts similarity index 60% rename from backend/src/event/EVENT_LOGIN.ts rename to backend/src/event/EVENT_USER_LOGIN.ts index f7e5f80d8..e4ae5e330 100644 --- a/backend/src/event/EVENT_LOGIN.ts +++ b/backend/src/event/EVENT_USER_LOGIN.ts @@ -3,5 +3,5 @@ import { Event as DbEvent } from '@entity/Event' /* eslint-disable-next-line import/no-cycle */ import { Event, EventType } from './Event' -export const EVENT_LOGIN = async (user: DbUser): Promise => - Event(EventType.LOGIN, 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_REGISTER.ts b/backend/src/event/EVENT_USER_LOGOUT.ts similarity index 59% rename from backend/src/event/EVENT_REGISTER.ts rename to backend/src/event/EVENT_USER_LOGOUT.ts index 4300bc7f3..6b726f624 100644 --- a/backend/src/event/EVENT_REGISTER.ts +++ b/backend/src/event/EVENT_USER_LOGOUT.ts @@ -3,5 +3,5 @@ import { Event as DbEvent } from '@entity/Event' /* eslint-disable-next-line import/no-cycle */ import { Event, EventType } from './Event' -export const EVENT_REGISTER = async (user: DbUser): Promise => - Event(EventType.REGISTER, 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_USER_REGISTER.ts b/backend/src/event/EVENT_USER_REGISTER.ts new file mode 100644 index 000000000..cdb8b22e2 --- /dev/null +++ b/backend/src/event/EVENT_USER_REGISTER.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_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 cce5946ee..e998042be 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -37,7 +37,6 @@ export const Event = ( export { 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' @@ -47,18 +46,25 @@ 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' export { EVENT_CONTRIBUTION_CREATE } from './EVENT_CONTRIBUTION_CREATE' 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_LOGIN } from './EVENT_LOGIN' -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_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_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 47056f05e..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,28 +9,34 @@ 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', CONTRIBUTION_CREATE = 'CONTRIBUTION_CREATE', CONTRIBUTION_DELETE = 'CONTRIBUTION_DELETE', CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE', CONTRIBUTION_MESSAGE_CREATE = 'CONTRIBUTION_MESSAGE_CREATE', CONTRIBUTION_LINK_REDEEM = 'CONTRIBUTION_LINK_REDEEM', - LOGIN = 'LOGIN', - REGISTER = 'REGISTER', - REDEEM_REGISTER = 'REDEEM_REGISTER', - SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = 'SEND_ACCOUNT_MULTIREGISTRATION_EMAIL', - SEND_CONFIRMATION_EMAIL = 'SEND_CONFIRMATION_EMAIL', + EMAIL_ACCOUNT_MULTIREGISTRATION = 'EMAIL_ACCOUNT_MULTIREGISTRATION', + EMAIL_ADMIN_CONFIRMATION = 'EMAIL_ADMIN_CONFIRMATION', + EMAIL_CONFIRMATION = 'EMAIL_CONFIRMATION', + EMAIL_FORGOT_PASSWORD = 'EMAIL_FORGOT_PASSWORD', 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/arg/SubscribeNewsletterArgs.ts b/backend/src/graphql/arg/SubscribeNewsletterArgs.ts deleted file mode 100644 index 98a3bb2d3..000000000 --- a/backend/src/graphql/arg/SubscribeNewsletterArgs.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ArgsType, Field } from 'type-graphql' - -@ArgsType() -export default class SubscribeNewsletterArgs { - @Field(() => String) - email: string - - @Field(() => String) - language: string -} diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 84dd6cb19..df8524a0c 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -2578,10 +2578,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/KlicktippResolver.ts b/backend/src/graphql/resolver/KlicktippResolver.ts index dd137f07c..31bde0581 100644 --- a/backend/src/graphql/resolver/KlicktippResolver.ts +++ b/backend/src/graphql/resolver/KlicktippResolver.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ -import { Resolver, Query, Authorized, Arg, Mutation, Args } from 'type-graphql' - -import SubscribeNewsletterArgs from '@arg/SubscribeNewsletterArgs' +import { Resolver, Query, Authorized, Arg, Mutation, Ctx } from 'type-graphql' import { getKlickTippUser, @@ -10,6 +8,7 @@ import { klicktippSignIn, } from '@/apis/KlicktippController' import { RIGHTS } from '@/auth/RIGHTS' +import { Context, getUser } from '@/server/context' @Resolver() export class KlicktippResolver { @@ -27,15 +26,15 @@ export class KlicktippResolver { @Authorized([RIGHTS.UNSUBSCRIBE_NEWSLETTER]) @Mutation(() => Boolean) - async unsubscribeNewsletter(@Arg('email') email: string): Promise { - return await unsubscribe(email) + async unsubscribeNewsletter(@Ctx() context: Context): Promise { + const user = getUser(context) + return unsubscribe(user.emailContact.email) } @Authorized([RIGHTS.SUBSCRIBE_NEWSLETTER]) @Mutation(() => Boolean) - async subscribeNewsletter( - @Args() { email, language }: SubscribeNewsletterArgs, - ): Promise { - return await klicktippSignIn(email, language) + async subscribeNewsletter(@Ctx() context: Context): Promise { + const user = getUser(context) + return klicktippSignIn(user.emailContact.email, user.language) } } diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 5a372bf72..aebd0f0eb 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -182,14 +182,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'] }, ) await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.REGISTER, + type: EventType.USER_REGISTER, affectedUserId: userConatct.user.id, actingUserId: userConatct.user.id, }), @@ -216,10 +216,10 @@ describe('UserResolver', () => { }) }) - 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, affectedUserId: user[0].id, actingUserId: user[0].id, }), @@ -258,14 +258,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'] }, ) await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, + type: EventType.EMAIL_ACCOUNT_MULTIREGISTRATION, affectedUserId: userConatct.user.id, actingUserId: 0, }), @@ -363,20 +363,20 @@ describe('UserResolver', () => { ) }) - it('stores the ACTIVATE_ACCOUNT event in the database', async () => { + it('stores the USER_ACTIVATE_ACCOUNT event in the database', async () => { await 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', 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: result.data.createUser.id, actingUserId: result.data.createUser.id, involvedContributionLinkId: link.id, @@ -458,10 +458,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, @@ -687,14 +687,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'] }, ) await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.LOGIN, + type: EventType.USER_LOGIN, affectedUserId: userConatct.user.id, actingUserId: userConatct.user.id, }), @@ -868,6 +868,20 @@ describe('UserResolver', () => { }), ) }) + + it('stores the USER_LOGOUT event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + await expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.USER_LOGOUT, + affectedUserId: userConatct.user.id, + actingUserId: userConatct.user.id, + }), + ) + }) }) }) @@ -941,10 +955,10 @@ describe('UserResolver', () => { ) }) - it('stores the LOGIN event in the database', async () => { + it('stores the USER_LOGIN event in the database', async () => { await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ - type: EventType.LOGIN, + type: EventType.USER_LOGIN, affectedUserId: user[0].id, actingUserId: user[0].id, }), @@ -1023,6 +1037,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'] }, + ) + await expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.EMAIL_FORGOT_PASSWORD, + affectedUserId: userConatct.user.id, + actingUserId: 0, + }), + ) + }) }) describe('request reset password again', () => { @@ -1147,6 +1175,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'] }, + ) + await 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', () => { @@ -1517,6 +1559,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'] }, + ) + await 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', () => { @@ -1702,6 +1762,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'] }, + ) + await 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() @@ -1857,14 +1935,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'] }, ) await 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, }), @@ -1977,6 +2055,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'] }, + ) + await expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.ADMIN_USER_UNDELETE, + affectedUserId: userConatct.user.id, + actingUserId: adminConatct.user.id, + }), + ) + }) }) }) }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 86818eaaa..54d4f583f 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -58,12 +58,18 @@ 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_ADMIN_SEND_CONFIRMATION_EMAIL, + EVENT_USER_LOGIN, + EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION, + EVENT_EMAIL_CONFIRMATION, + EVENT_USER_REGISTER, + EVENT_USER_ACTIVATE_ACCOUNT, + EVENT_EMAIL_ADMIN_CONFIRMATION, + EVENT_USER_LOGOUT, + EVENT_EMAIL_FORGOT_PASSWORD, + EVENT_USER_INFO_UPDATE, + EVENT_ADMIN_USER_ROLE_SET, + EVENT_ADMIN_USER_DELETE, + EVENT_ADMIN_USER_UNDELETE, } from '@/event/Event' import { isValidPassword } from '@/password/EncryptorUtils' import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor' @@ -182,22 +188,16 @@ 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 } @Authorized([RIGHTS.LOGOUT]) @Mutation(() => Boolean) - logout(): boolean { - // 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_USER_LOGOUT(getUser(context)) + // remove user from logger context logger.addContext('user', 'unknown') return true } @@ -251,8 +251,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}>`, @@ -271,7 +270,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, ) @@ -337,7 +336,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}`) @@ -358,7 +357,7 @@ export class UserResolver { eventRegisterRedeem.actingUser = dbUser await eventRegisterRedeem.save() } else { - await EVENT_REGISTER(dbUser) + await EVENT_USER_REGISTER(dbUser) } return new User(dbUser) @@ -411,6 +410,7 @@ export class UserResolver { ) } logger.info(`forgotPassword(${email}) successful...`) + await EVENT_EMAIL_FORGOT_PASSWORD(user) return true } @@ -473,8 +473,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) @@ -492,13 +490,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_USER_ACTIVATE_ACCOUNT(user) return true } @@ -535,21 +529,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) } @@ -561,22 +555,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() @@ -584,7 +578,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) }) @@ -597,6 +591,8 @@ export class UserResolver { await queryRunner.release() } logger.info('updateUserInfos() successfully finished...') + await EVENT_USER_INFO_UPDATE(user) + return true } @@ -722,8 +718,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 @@ -744,6 +740,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 } @@ -760,19 +757,23 @@ 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.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) @@ -781,6 +782,7 @@ export class UserResolver { throw new LogError('User is not deleted') } await user.recover() + await EVENT_ADMIN_USER_UNDELETE(user, getUser(context)) return null } @@ -814,9 +816,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_EMAIL_ADMIN_CONFIRMATION(user, getUser(context)) return true } 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 f149b70b7..ce0c5a9a5 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 diff --git a/frontend/src/components/UserSettings/UserNewsletter.spec.js b/frontend/src/components/UserSettings/UserNewsletter.spec.js index bb2781586..b211ec169 100644 --- a/frontend/src/components/UserSettings/UserNewsletter.spec.js +++ b/frontend/src/components/UserSettings/UserNewsletter.spec.js @@ -60,9 +60,6 @@ describe('UserCard_Newsletter', () => { it('calls the unsubscribe mutation', () => { expect(mockAPIcall).toBeCalledWith({ mutation: unsubscribeNewsletter, - variables: { - email: 'peter@lustig.de', - }, }) }) @@ -89,10 +86,6 @@ describe('UserCard_Newsletter', () => { it('calls the subscribe mutation', () => { expect(mockAPIcall).toBeCalledWith({ mutation: subscribeNewsletter, - variables: { - email: 'peter@lustig.de', - language: 'de', - }, }) }) diff --git a/frontend/src/components/UserSettings/UserNewsletter.vue b/frontend/src/components/UserSettings/UserNewsletter.vue index 39f3a14af..50e442a29 100644 --- a/frontend/src/components/UserSettings/UserNewsletter.vue +++ b/frontend/src/components/UserSettings/UserNewsletter.vue @@ -41,10 +41,6 @@ export default { this.$apollo .mutate({ mutation: this.newsletterState ? subscribeNewsletter : unsubscribeNewsletter, - variables: { - email: this.$store.state.email, - language: this.newsletterState ? this.$store.state.language : undefined, - }, }) .then(() => { this.$store.commit('newsletterState', this.newsletterState) diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index 55858b09b..4b43cade4 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -1,14 +1,14 @@ import gql from 'graphql-tag' export const subscribeNewsletter = gql` - mutation($email: String!, $language: String!) { - subscribeNewsletter(email: $email, language: $language) + mutation { + subscribeNewsletter } ` export const unsubscribeNewsletter = gql` - mutation($email: String!) { - unsubscribeNewsletter(email: $email) + mutation { + unsubscribeNewsletter } `