From 2fb5cf2d81b0f3388197128fac46138b4207a790 Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 21 Mar 2022 10:27:14 +0100 Subject: [PATCH 01/23] delete redeemCode from store --- frontend/src/store/store.js | 5 ----- frontend/src/store/store.test.js | 11 +---------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index fb74624fb..458c8c8de 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -47,9 +47,6 @@ export const mutations = { hasElopage: (state, hasElopage) => { state.hasElopage = hasElopage }, - redeemCode: (state, redeemCode) => { - state.redeemCode = redeemCode - }, } export const actions = { @@ -76,7 +73,6 @@ export const actions = { commit('hasElopage', false) commit('publisherId', null) commit('isAdmin', false) - commit('redeemCode', null) localStorage.clear() }, } @@ -106,7 +102,6 @@ try { }, hasElopage: false, publisherId: null, - redeemCode: null, }, getters: {}, // Syncronous mutation of the state diff --git a/frontend/src/store/store.test.js b/frontend/src/store/store.test.js index 29a703e8f..ea40d9474 100644 --- a/frontend/src/store/store.test.js +++ b/frontend/src/store/store.test.js @@ -26,7 +26,6 @@ const { isAdmin, community, hasElopage, - redeemCode, } = mutations const { login, logout } = actions @@ -142,14 +141,6 @@ describe('Vuex store', () => { hasElopage(state, true) expect(state.hasElopage).toBeTruthy() }) - - describe('redeemCode', () => { - it('sets the state of token', () => { - const state = { redeemCode: null } - redeemCode(state, 'a0000b0000c0000') - expect(state.redeemCode).toEqual('a0000b0000c0000') - }) - }) }) }) @@ -228,7 +219,7 @@ describe('Vuex store', () => { it('calls nine commits', () => { logout({ commit, state }) - expect(commit).toHaveBeenCalledTimes(10) + expect(commit).toHaveBeenCalledTimes(9) }) it('commits token', () => { From 6c87a6f32a05b98ca081e6d9d00704b766b45413 Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 21 Mar 2022 10:28:12 +0100 Subject: [PATCH 02/23] changed code from store to param --- frontend/src/pages/Register.vue | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/frontend/src/pages/Register.vue b/frontend/src/pages/Register.vue index b2df16e0f..4ffe92512 100755 --- a/frontend/src/pages/Register.vue +++ b/frontend/src/pages/Register.vue @@ -118,7 +118,7 @@ {{ messageError }} - +
@@ -130,7 +130,7 @@ readonly id="redeem-code" type="text" - v-model="$store.state.redeemCode" + v-model="redeemCode" >
@@ -234,7 +234,7 @@ export default { messageError: '', register: true, publisherId: this.$store.state.publisherId, - redeemCode: this.$store.state.redeemCode, + redeemCode: this.$route.params.code, } }, methods: { @@ -248,9 +248,6 @@ export default { commitStorePublisherId(val) { this.$store.commit('publisherId', val) }, - commitStoreRedeemCode(val) { - this.$store.commit('redeemCode', val) - }, async onSubmit() { this.$apollo .mutate({ @@ -261,7 +258,7 @@ export default { lastName: this.form.lastname, language: this.language, publisherId: this.$store.state.publisherId, - redeemCode: this.$store.state.redeemCode, + redeemCode: this.redeemCode, }, }) .then(() => { @@ -296,10 +293,5 @@ export default { return !(this.namesFilled && this.emailFilled && this.form.agree && !!this.language) }, }, - created() { - if (this.$route.params.code) { - this.commitStoreRedeemCode(this.$route.params.code) - } - }, } From 1f1cd67b9e76824613993fa850e9a9076559f60a Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 21 Mar 2022 10:29:13 +0100 Subject: [PATCH 03/23] add redeemCode --- backend/src/graphql/arg/CreateUserArgs.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/graphql/arg/CreateUserArgs.ts b/backend/src/graphql/arg/CreateUserArgs.ts index 0d63e76bb..59462aa2a 100644 --- a/backend/src/graphql/arg/CreateUserArgs.ts +++ b/backend/src/graphql/arg/CreateUserArgs.ts @@ -16,4 +16,7 @@ export default class CreateUserArgs { @Field(() => Int, { nullable: true }) publisherId: number + + @Field(() => String, { nullable: true }) + redeemCode: string } From 3992882962fedb69714d651aecffbf1fc0548bd8 Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 21 Mar 2022 10:29:56 +0100 Subject: [PATCH 04/23] add redeemCode on UserResolver.ts --- 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 04c5fdf63..4ee564977 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -313,7 +313,7 @@ export class UserResolver { @Authorized([RIGHTS.CREATE_USER]) @Mutation(() => User) async createUser( - @Args() { email, firstName, lastName, language, publisherId }: CreateUserArgs, + @Args() { email, firstName, lastName, language, publisherId, redeemCode }: CreateUserArgs, ): Promise { // TODO: wrong default value (should be null), how does graphql work here? Is it an required field? // default int publisher_id = 0; From 15e0186381522fb60a8d9efe0852353f46eff643 Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 21 Mar 2022 10:54:56 +0100 Subject: [PATCH 05/23] add redeemCode on register process for createUser --- backend/src/graphql/resolver/UserResolver.ts | 10 +++++++--- backend/src/seeds/graphql/mutations.ts | 2 ++ backend/src/webhook/elopage.ts | 1 + frontend/src/graphql/mutations.js | 2 ++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 4ee564977..80e927da7 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -367,11 +367,15 @@ export class UserResolver { // TODO: this has duplicate code with sendResetPasswordEmail const emailOptIn = await createEmailOptIn(dbUser.id, queryRunner) - const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( + let activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( /{code}/g, emailOptIn.verificationCode.toString(), ) + if (redeemCode !== '') { + activationLink += '/' + redeemCode + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars const emailSent = await sendAccountActivationEmail({ link: activationLink, @@ -380,13 +384,13 @@ export class UserResolver { email, }) - /* uncomment this, when you need the activation link on the console + // uncomment this, when you need the activation link on the console // In case EMails are disabled log the activation link for the user if (!emailSent) { // eslint-disable-next-line no-console console.log(`Account confirmation link: ${activationLink}`) } - */ + await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 19ca2a8d0..cab864c4e 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -45,6 +45,7 @@ export const createUser = gql` $email: String! $language: String! $publisherId: Int + $redeemCode: String ) { createUser( email: $email @@ -52,6 +53,7 @@ export const createUser = gql` lastName: $lastName language: $language publisherId: $publisherId + redeemCode: $redeemCode ) { id } diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts index d5eaef521..ee8c3fc94 100644 --- a/backend/src/webhook/elopage.ts +++ b/backend/src/webhook/elopage.ts @@ -140,6 +140,7 @@ export const elopageWebhook = async (req: any, res: any): Promise => { firstName, lastName, publisherId: loginElopageBuy.publisherId || 0, // This seemed to be the default value if not set + redeemCode: '', }) } catch (error) { // eslint-disable-next-line no-console diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index d3a177866..95c811f8d 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -45,6 +45,7 @@ export const createUser = gql` $email: String! $language: String! $publisherId: Int + $redeemCode: String ) { createUser( email: $email @@ -52,6 +53,7 @@ export const createUser = gql` lastName: $lastName language: $language publisherId: $publisherId + redeemCode: $redeemCode ) { id } From a23777b6ec5dccfd5e11b1a8e29b5c329c9747da Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 21 Mar 2022 12:07:59 +0100 Subject: [PATCH 06/23] uncomment code activationLink --- backend/src/graphql/resolver/UserResolver.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 80e927da7..311916aad 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -384,12 +384,13 @@ export class UserResolver { email, }) - // uncomment this, when you need the activation link on the console + /* uncomment this, when you need the activation link on the console // In case EMails are disabled log the activation link for the user if (!emailSent) { // eslint-disable-next-line no-console console.log(`Account confirmation link: ${activationLink}`) } + */ await queryRunner.commitTransaction() } catch (e) { From cdd13cf24e890760563ff043f483ab13bc61ae8d Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 21 Mar 2022 12:09:07 +0100 Subject: [PATCH 07/23] add params on thx and checkEmail --- frontend/src/routes/routes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/routes/routes.js b/frontend/src/routes/routes.js index 5e0b09c5e..a6586c201 100755 --- a/frontend/src/routes/routes.js +++ b/frontend/src/routes/routes.js @@ -47,7 +47,7 @@ const routes = [ component: () => import('@/pages/Register.vue'), }, { - path: '/thx/:comingFrom', + path: '/thx/:comingFrom/:code?', component: () => import('@/pages/thx.vue'), beforeEnter: (to, from, next) => { const validFrom = ['forgot-password', 'reset-password', 'register', 'login', 'checkEmail'] @@ -79,7 +79,7 @@ const routes = [ component: () => import('@/pages/ResetPassword.vue'), }, { - path: '/checkEmail/:optin', + path: '/checkEmail/:optin/:code?', component: () => import('@/pages/ResetPassword.vue'), }, { From 6d257825e2aee4e5f9710c9e7c73666d2bdeed67 Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 21 Mar 2022 12:11:19 +0100 Subject: [PATCH 08/23] add redeemCode to login button --- frontend/src/pages/thx.vue | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/thx.vue b/frontend/src/pages/thx.vue index 41c63c7d8..308c354fa 100644 --- a/frontend/src/pages/thx.vue +++ b/frontend/src/pages/thx.vue @@ -9,7 +9,11 @@

{{ $t(displaySetup.subtitle) }}


- + + + {{ $t(displaySetup.button) }} + + {{ $t(displaySetup.button) }} @@ -65,6 +69,11 @@ export default { this.displaySetup = textFields[this.$route.params.comingFrom] }, }, + computed: { + redeemLoginLink() { + return this.$route.params.code ? '/login/' + this.$route.params.code : '/login' + }, + }, created() { this.setDisplaySetup() }, From 042741a9aab75f05b0f74857d53ed9b658ee8d67 Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 21 Mar 2022 12:11:59 +0100 Subject: [PATCH 09/23] add code to push --- frontend/src/pages/ResetPassword.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/ResetPassword.vue b/frontend/src/pages/ResetPassword.vue index b0194c0ba..b6de8cb97 100644 --- a/frontend/src/pages/ResetPassword.vue +++ b/frontend/src/pages/ResetPassword.vue @@ -96,7 +96,11 @@ export default { .then(() => { this.form.password = '' if (this.$route.path.includes('checkEmail')) { - this.$router.push('/thx/checkEmail') + if (this.$route.params.code) { + this.$router.push('/thx/checkEmail/' + this.$route.params.code) + } else { + this.$router.push('/thx/checkEmail') + } } else { this.$router.push('/thx/resetPassword') } From f8882fa63a8f052b96188bf36874542cd1d7de76 Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 21 Mar 2022 14:41:50 +0100 Subject: [PATCH 10/23] remove computed --- frontend/src/pages/thx.vue | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/frontend/src/pages/thx.vue b/frontend/src/pages/thx.vue index 308c354fa..ee6338d26 100644 --- a/frontend/src/pages/thx.vue +++ b/frontend/src/pages/thx.vue @@ -9,7 +9,7 @@

{{ $t(displaySetup.subtitle) }}


- + {{ $t(displaySetup.button) }} @@ -69,11 +69,6 @@ export default { this.displaySetup = textFields[this.$route.params.comingFrom] }, }, - computed: { - redeemLoginLink() { - return this.$route.params.code ? '/login/' + this.$route.params.code : '/login' - }, - }, created() { this.setDisplaySetup() }, From 2b7d9d8c1a987eed7164692157fe9fdc94946831 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 21 Mar 2022 17:23:56 +0100 Subject: [PATCH 11/23] save referrer ID when present on register --- backend/src/graphql/resolver/UserResolver.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 33fba47a2..3b3ef394c 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -7,6 +7,7 @@ import { getConnection, getCustomRepository, QueryRunner } from '@dbTools/typeor import CONFIG from '@/config' import { User } from '@model/User' import { User as DbUser } from '@entity/User' +import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' import { encode } from '@/auth/JWT' import CreateUserArgs from '@arg/CreateUserArgs' import UnsecureLoginArgs from '@arg/UnsecureLoginArgs' @@ -338,6 +339,12 @@ export class UserResolver { dbUser.language = language dbUser.publisherId = publisherId dbUser.passphrase = passphrase.join(' ') + if (redeemCode) { + const transactionLink = await dbTransactionLink.findOne({ code: redeemCode }) + if (transactionLink) { + dbUser.referrerId = transactionLink.userId + } + } // TODO this field has no null allowed unlike the loginServer table // dbUser.pubKey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000... // dbUser.pubkey = keyPair[0] From 4675c5679068abafde0e51bdb2252a57a6cbcc1b Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 21 Mar 2022 21:38:28 +0100 Subject: [PATCH 12/23] test router.test.js fixed --- frontend/src/routes/router.test.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/routes/router.test.js b/frontend/src/routes/router.test.js index 85f765c69..925b3ffca 100644 --- a/frontend/src/routes/router.test.js +++ b/frontend/src/routes/router.test.js @@ -112,7 +112,7 @@ describe('router', () => { }) describe('thx', () => { - const thx = routes.find((r) => r.path === '/thx/:comingFrom') + const thx = routes.find((r) => r.path === '/thx/:comingFrom/:code?') it('loads the "Thx" page', async () => { const component = await thx.component() @@ -177,7 +177,9 @@ describe('router', () => { describe('checkEmail', () => { it('loads the "CheckEmail" page', async () => { - const component = await routes.find((r) => r.path === '/checkEmail/:optin').component() + const component = await routes + .find((r) => r.path === '/checkEmail/:optin/:code?') + .component() expect(component.default.name).toBe('ResetPassword') }) }) From 8a2ca07b2edabd8ca7c432dfb8f07f9408b689db Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 22 Mar 2022 07:09:58 +0100 Subject: [PATCH 13/23] test UserResolver.test.js ok, with redeemCode --- backend/src/graphql/resolver/UserResolver.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index f873fd694..a982f3768 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -54,6 +54,7 @@ describe('UserResolver', () => { lastName: 'Lustig', language: 'de', publisherId: 1234, + redeemCode: 'a0000b0000c0000', } let result: any @@ -126,7 +127,7 @@ describe('UserResolver', () => { it('sends an account activation email', () => { const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(/{code}/g, emailOptIn) expect(sendAccountActivationEmail).toBeCalledWith({ - link: activationLink, + link: activationLink + '/a0000b0000c0000', firstName: 'Peter', lastName: 'Lustig', email: 'peter@lustig.de', From fe869e12c4ff64755ed318b52b313c4b238a14d6 Mon Sep 17 00:00:00 2001 From: Alexander Friedland Date: Tue, 22 Mar 2022 12:31:44 +0100 Subject: [PATCH 14/23] Update backend/src/graphql/resolver/UserResolver.test.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/UserResolver.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index a982f3768..ea78c56bd 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -54,7 +54,6 @@ describe('UserResolver', () => { lastName: 'Lustig', language: 'de', publisherId: 1234, - redeemCode: 'a0000b0000c0000', } let result: any From 7929b93964c254889379f7ded46aa658a172fcf4 Mon Sep 17 00:00:00 2001 From: Alexander Friedland Date: Tue, 22 Mar 2022 12:33:10 +0100 Subject: [PATCH 15/23] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: Ulf Gebhardt --- 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 3b3ef394c..6d9e42405 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -306,7 +306,7 @@ export class UserResolver { @Authorized([RIGHTS.CREATE_USER]) @Mutation(() => User) async createUser( - @Args() { email, firstName, lastName, language, publisherId, redeemCode }: CreateUserArgs, + @Args() { email, firstName, lastName, language, publisherId, redeemCode = null}: CreateUserArgs, ): Promise { // TODO: wrong default value (should be null), how does graphql work here? Is it an required field? // default int publisher_id = 0; From eda69bfc47edecf508c7734ba508ec42d41d9e8d Mon Sep 17 00:00:00 2001 From: Alexander Friedland Date: Tue, 22 Mar 2022 12:34:17 +0100 Subject: [PATCH 16/23] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: Ulf Gebhardt --- 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 6d9e42405..0e9e7c867 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -371,7 +371,7 @@ export class UserResolver { emailOptIn.verificationCode.toString(), ) - if (redeemCode !== '') { + if (redeemCode) { activationLink += '/' + redeemCode } From 9c8599528e9fca1470b97306f46cd35122162b51 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Mar 2022 19:24:58 +0100 Subject: [PATCH 17/23] allow null for redeemCode --- backend/src/graphql/arg/CreateUserArgs.ts | 2 +- backend/src/graphql/resolver/UserResolver.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/arg/CreateUserArgs.ts b/backend/src/graphql/arg/CreateUserArgs.ts index 59462aa2a..710c52f6d 100644 --- a/backend/src/graphql/arg/CreateUserArgs.ts +++ b/backend/src/graphql/arg/CreateUserArgs.ts @@ -18,5 +18,5 @@ export default class CreateUserArgs { publisherId: number @Field(() => String, { nullable: true }) - redeemCode: string + redeemCode: string | null } diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 0e9e7c867..6ff3acb17 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -306,7 +306,8 @@ export class UserResolver { @Authorized([RIGHTS.CREATE_USER]) @Mutation(() => User) async createUser( - @Args() { email, firstName, lastName, language, publisherId, redeemCode = null}: CreateUserArgs, + @Args() + { email, firstName, lastName, language, publisherId, redeemCode = null }: CreateUserArgs, ): Promise { // TODO: wrong default value (should be null), how does graphql work here? Is it an required field? // default int publisher_id = 0; From 9b53458655dc9fcd51a85f1293408fe2bd3c5ebf Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Mar 2022 19:38:51 +0100 Subject: [PATCH 18/23] fix test --- backend/src/graphql/resolver/UserResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index ea78c56bd..f873fd694 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -126,7 +126,7 @@ describe('UserResolver', () => { it('sends an account activation email', () => { const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(/{code}/g, emailOptIn) expect(sendAccountActivationEmail).toBeCalledWith({ - link: activationLink + '/a0000b0000c0000', + link: activationLink, firstName: 'Peter', lastName: 'Lustig', email: 'peter@lustig.de', From 1c8e1df1b54bf85ade9b58e24b97493e1944c742 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Mar 2022 20:11:34 +0100 Subject: [PATCH 19/23] 100% test of Reset Password, better naming of variables --- frontend/src/pages/ResetPassword.spec.js | 46 ++++++++++++++++++------ frontend/src/pages/ResetPassword.vue | 12 +++---- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/frontend/src/pages/ResetPassword.spec.js b/frontend/src/pages/ResetPassword.spec.js index f5d672c99..c43f71932 100644 --- a/frontend/src/pages/ResetPassword.spec.js +++ b/frontend/src/pages/ResetPassword.spec.js @@ -54,25 +54,27 @@ describe('ResetPassword', () => { describe('mount', () => { beforeEach(() => { + jest.clearAllMocks() wrapper = Wrapper() }) - describe('No valid optin', () => { - it.skip('does not render the Reset Password form when not authenticated', () => { - expect(wrapper.find('form').exists()).toBeFalsy() + describe('no valid optin', () => { + beforeEach(() => { + jest.clearAllMocks() + apolloQueryMock.mockRejectedValue({ message: 'Your time is up!' }) + wrapper = Wrapper() }) - it.skip('toasts an error when no valid optin is given', () => { - expect(toastErrorSpy).toHaveBeenCalledWith('error') + it('toasts an error when no valid optin is given', () => { + expect(toastErrorSpy).toHaveBeenCalledWith('Your time is up!') }) - it.skip('has a message suggesting to contact the support', () => { - expect(wrapper.find('div.header').text()).toContain('settings.password.reset') - expect(wrapper.find('div.header').text()).toContain('settings.password.not-authenticated') + it('redirects to /forgot-password/resetPassword', () => { + expect(routerPushMock).toBeCalledWith('/forgot-password/resetPassword') }) }) - describe('is authenticated', () => { + describe('valid optin', () => { it('renders the Reset Password form when authenticated', () => { expect(wrapper.find('div.resetpwd-form').exists()).toBeTruthy() }) @@ -148,7 +150,6 @@ describe('ResetPassword', () => { describe('server response with error code > 10min', () => { beforeEach(async () => { - jest.clearAllMocks() apolloMutationMock.mockRejectedValue({ message: '...Code is older than 10 minutes' }) await wrapper.find('form').trigger('submit') await flushPromises() @@ -163,7 +164,7 @@ describe('ResetPassword', () => { }) }) - describe('server response with error code > 10min', () => { + describe('server response with error', () => { beforeEach(async () => { jest.clearAllMocks() apolloMutationMock.mockRejectedValueOnce({ message: 'Error' }) @@ -178,6 +179,7 @@ describe('ResetPassword', () => { describe('server response with success on /checkEmail', () => { beforeEach(async () => { + jest.clearAllMocks() mocks.$route.path.mock = 'checkEmail' apolloMutationMock.mockResolvedValue({ data: { @@ -204,6 +206,28 @@ describe('ResetPassword', () => { it('redirects to "/thx/checkEmail"', () => { expect(routerPushMock).toHaveBeenCalledWith('/thx/checkEmail') }) + + describe('with param code', () => { + beforeEach(async () => { + mocks.$route.params.code = 'the-most-secret-code-ever' + apolloMutationMock.mockResolvedValue({ + data: { + resetPassword: 'success', + }, + }) + wrapper = Wrapper() + await wrapper.findAll('input').at(0).setValue('Aa123456_') + await wrapper.findAll('input').at(1).setValue('Aa123456_') + await wrapper.find('form').trigger('submit') + await flushPromises() + }) + + it('redirects to "/thx/checkEmail/the-most-secret-code-ever"', () => { + expect(routerPushMock).toHaveBeenCalledWith( + '/thx/checkEmail/the-most-secret-code-ever', + ) + }) + }) }) describe('server response with success on /reset-password', () => { diff --git a/frontend/src/pages/ResetPassword.vue b/frontend/src/pages/ResetPassword.vue index d6fc1dd76..7771be5f6 100644 --- a/frontend/src/pages/ResetPassword.vue +++ b/frontend/src/pages/ResetPassword.vue @@ -6,11 +6,11 @@ -

{{ $t(displaySetup.authenticated) }}

+

{{ $t(displaySetup.title) }}

- {{ $t(displaySetup.notAuthenticated) }} + {{ $t(displaySetup.text) }}
@@ -53,14 +53,14 @@ import { queryOptIn } from '@/graphql/queries' const textFields = { reset: { - authenticated: 'settings.password.change-password', - notAuthenticated: 'settings.password.reset-password.text', + title: 'settings.password.change-password', + text: 'settings.password.reset-password.text', button: 'settings.password.change-password', linkTo: '/login', }, checkEmail: { - authenticated: 'settings.password.set', - notAuthenticated: 'settings.password.set-password.text', + title: 'settings.password.set', + text: 'settings.password.set-password.text', button: 'settings.password.set', linkTo: '/login', }, From 767387bb7c16934374a434c5f5eef84d060f8fa8 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 23 Mar 2022 09:05:39 +0100 Subject: [PATCH 20/23] change {code} to {optin} to avoid confusion when redeem code is provided --- backend/.env.dist | 4 ++-- backend/src/config/index.ts | 4 ++-- backend/src/graphql/resolver/UserResolver.test.ts | 2 +- backend/src/graphql/resolver/UserResolver.ts | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/.env.dist b/backend/.env.dist index 3c93f1576..37d0f0e67 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -38,8 +38,8 @@ EMAIL_SENDER=info@gradido.net EMAIL_PASSWORD=xxx EMAIL_SMTP_URL=gmail.com EMAIL_SMTP_PORT=587 -EMAIL_LINK_VERIFICATION=http://localhost/checkEmail/{code} -EMAIL_LINK_SETPASSWORD=http://localhost/reset/{code} +EMAIL_LINK_VERIFICATION=http://localhost/checkEmail/{optin} +EMAIL_LINK_SETPASSWORD=http://localhost/reset/{optin} EMAIL_CODE_VALID_TIME=10 # Webhook diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 754bfbf08..9ef7d29e2 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -62,9 +62,9 @@ const email = { EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'gmail.com', EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '587', EMAIL_LINK_VERIFICATION: - process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/checkEmail/{code}', + process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/checkEmail/{optin}', EMAIL_LINK_SETPASSWORD: - process.env.EMAIL_LINK_SETPASSWORD || 'http://localhost/reset-password/{code}', + process.env.EMAIL_LINK_SETPASSWORD || 'http://localhost/reset-password/{optin}', EMAIL_CODE_VALID_TIME: process.env.EMAIL_CODE_VALID_TIME ? parseInt(process.env.EMAIL_CODE_VALID_TIME) || 10 : 10, diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index f873fd694..a85601648 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -124,7 +124,7 @@ describe('UserResolver', () => { describe('account activation email', () => { it('sends an account activation email', () => { - const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(/{code}/g, emailOptIn) + const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(/{optin}/g, emailOptIn) expect(sendAccountActivationEmail).toBeCalledWith({ link: activationLink, firstName: 'Peter', diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 6ff3acb17..7cf7a0daa 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -368,7 +368,7 @@ export class UserResolver { const emailOptIn = await createEmailOptIn(dbUser.id, queryRunner) let activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( - /{code}/g, + /{optin}/g, emailOptIn.verificationCode.toString(), ) @@ -417,7 +417,7 @@ export class UserResolver { const emailOptIn = await createEmailOptIn(user.id, queryRunner) const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( - /{code}/g, + /{optin}/g, emailOptIn.verificationCode.toString(), ) @@ -456,7 +456,7 @@ export class UserResolver { const optInCode = await getOptInCode(user.id) const link = CONFIG.EMAIL_LINK_SETPASSWORD.replace( - /{code}/g, + /{optin}/g, optInCode.verificationCode.toString(), ) From 807922a8d5f391b713b7652a629c74cc6ad58aab Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 23 Mar 2022 09:17:15 +0100 Subject: [PATCH 21/23] redeem code in email link config --- backend/.env.dist | 2 +- backend/src/config/index.ts | 2 +- backend/src/graphql/resolver/UserResolver.test.ts | 5 ++++- backend/src/graphql/resolver/UserResolver.ts | 8 ++------ 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/backend/.env.dist b/backend/.env.dist index 37d0f0e67..7994daacf 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -38,7 +38,7 @@ EMAIL_SENDER=info@gradido.net EMAIL_PASSWORD=xxx EMAIL_SMTP_URL=gmail.com EMAIL_SMTP_PORT=587 -EMAIL_LINK_VERIFICATION=http://localhost/checkEmail/{optin} +EMAIL_LINK_VERIFICATION=http://localhost/checkEmail/{optin}{code} EMAIL_LINK_SETPASSWORD=http://localhost/reset/{optin} EMAIL_CODE_VALID_TIME=10 diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 9ef7d29e2..e502272d3 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -62,7 +62,7 @@ const email = { EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'gmail.com', EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '587', EMAIL_LINK_VERIFICATION: - process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/checkEmail/{optin}', + process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/checkEmail/{optin}{code}', EMAIL_LINK_SETPASSWORD: process.env.EMAIL_LINK_SETPASSWORD || 'http://localhost/reset-password/{optin}', EMAIL_CODE_VALID_TIME: process.env.EMAIL_CODE_VALID_TIME diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index a85601648..53f39668e 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -124,7 +124,10 @@ describe('UserResolver', () => { describe('account activation email', () => { it('sends an account activation email', () => { - const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(/{optin}/g, emailOptIn) + const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( + /{optin}/g, + emailOptIn, + ).replace(/{code}/g, '') expect(sendAccountActivationEmail).toBeCalledWith({ link: activationLink, firstName: 'Peter', diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 7cf7a0daa..b24aa1b58 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -367,14 +367,10 @@ export class UserResolver { // TODO: this has duplicate code with sendResetPasswordEmail const emailOptIn = await createEmailOptIn(dbUser.id, queryRunner) - let activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( + const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( /{optin}/g, emailOptIn.verificationCode.toString(), - ) - - if (redeemCode) { - activationLink += '/' + redeemCode - } + ).replace(/{code}/g, redeemCode ? '/' + redeemCode : '') // eslint-disable-next-line @typescript-eslint/no-unused-vars const emailSent = await sendAccountActivationEmail({ From 65fb882f6392cfcf977f9daa09a7f3bf7fd63652 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 23 Mar 2022 19:40:36 +0100 Subject: [PATCH 22/23] Update backend/src/webhook/elopage.ts Co-authored-by: Ulf Gebhardt --- backend/src/webhook/elopage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts index ee8c3fc94..d5eaef521 100644 --- a/backend/src/webhook/elopage.ts +++ b/backend/src/webhook/elopage.ts @@ -140,7 +140,6 @@ export const elopageWebhook = async (req: any, res: any): Promise => { firstName, lastName, publisherId: loginElopageBuy.publisherId || 0, // This seemed to be the default value if not set - redeemCode: '', }) } catch (error) { // eslint-disable-next-line no-console From c0c66308730bb1ddc0cc5375fb96392b42a18d92 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Mar 2022 19:59:29 +0100 Subject: [PATCH 23/23] make redeemCode nullable --- backend/src/graphql/arg/CreateUserArgs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/arg/CreateUserArgs.ts b/backend/src/graphql/arg/CreateUserArgs.ts index 710c52f6d..af915b91a 100644 --- a/backend/src/graphql/arg/CreateUserArgs.ts +++ b/backend/src/graphql/arg/CreateUserArgs.ts @@ -18,5 +18,5 @@ export default class CreateUserArgs { publisherId: number @Field(() => String, { nullable: true }) - redeemCode: string | null + redeemCode?: string | null }