From 728aaa095f42467dc68092c58616f12e86074615 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 23 Nov 2021 03:04:28 +0100 Subject: [PATCH 01/46] first of several calls regarding password reset --- backend/src/config/index.ts | 2 + .../model/SendPasswordResetEmailResponse.ts | 17 ----- backend/src/graphql/resolver/UserResolver.ts | 68 +++++++++++++++---- frontend/src/graphql/queries.js | 4 +- 4 files changed, 56 insertions(+), 35 deletions(-) delete mode 100644 backend/src/graphql/model/SendPasswordResetEmailResponse.ts diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 7a55de8e8..820261bb1 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -53,6 +53,8 @@ const email = { EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '587', EMAIL_LINK_VERIFICATION: process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/vue/checkEmail/$1', + EMAIL_LINK_SETPASSWORD: + process.env.EMAIL_LINK_SETPASSWORD || 'http://localhost/vue/setPassword/$1', } // This is needed by graphql-directive-auth diff --git a/backend/src/graphql/model/SendPasswordResetEmailResponse.ts b/backend/src/graphql/model/SendPasswordResetEmailResponse.ts deleted file mode 100644 index d387efede..000000000 --- a/backend/src/graphql/model/SendPasswordResetEmailResponse.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { ObjectType, Field } from 'type-graphql' - -@ObjectType() -export class SendPasswordResetEmailResponse { - constructor(json: any) { - this.state = json.state - this.msg = json.msg - } - - @Field(() => String) - state: string - - @Field(() => String) - msg?: string -} diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 25f83bb09..ffa78e8aa 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -6,7 +6,6 @@ import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } import { getConnection, getCustomRepository } from 'typeorm' import CONFIG from '../../config' import { LoginViaVerificationCode } from '../model/LoginViaVerificationCode' -import { SendPasswordResetEmailResponse } from '../model/SendPasswordResetEmailResponse' import { User } from '../model/User' import { User as DbUser } from '@entity/User' import encode from '../../jwt/encode' @@ -30,6 +29,7 @@ import { LoginUserBackup } from '@entity/LoginUserBackup' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { sendEMail } from '../../util/sendEMail' import { LoginElopageBuysRepository } from '../../typeorm/repository/LoginElopageBuys' +import { randomBytes } from 'crypto' // eslint-disable-next-line @typescript-eslint/no-var-requires const sodium = require('sodium-native') @@ -201,8 +201,6 @@ export class UserResolver { @Ctx() context: any, ): Promise { email = email.trim().toLowerCase() - // const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password }) - // UnsecureLogin const loginUserRepository = getCustomRepository(LoginUserRepository) const loginUser = await loginUserRepository.findByEmail(email).catch(() => { throw new Error('No user with this credentials') @@ -440,20 +438,60 @@ export class UserResolver { return 'success' } - @Query(() => SendPasswordResetEmailResponse) - async sendResetPasswordEmail( - @Arg('email') email: string, - ): Promise { - const payload = { - email, - email_text: 7, - email_verification_code_type: 'resetPassword', + @Query(() => Boolean) + async sendResetPasswordEmail(@Arg('email') email: string): Promise { + let emailAlreadySend = false + const EMAIL_OPT_IN_RESET_PASSWORD = 2 + + const loginUser = await LoginUser.findOneOrFail({ email }) + + let optInCode = await LoginEmailOptIn.findOne({ + userId: loginUser.id, + emailOptInTypeId: EMAIL_OPT_IN_RESET_PASSWORD, + }) + if (optInCode) { + emailAlreadySend = true + } else { + optInCode = new LoginEmailOptIn() + optInCode.verificationCode = randomBytes(16).readBigInt64LE() + optInCode.userId = loginUser.id + optInCode.emailOptInTypeId = EMAIL_OPT_IN_RESET_PASSWORD + await optInCode.save() } - const response = await apiPost(CONFIG.LOGIN_API_URL + 'sendEmail', payload) - if (!response.success) { - throw new Error(response.data) + + const link = CONFIG.EMAIL_LINK_SETPASSWORD.replace( + /\$1/g, + optInCode.verificationCode.toString(), + ) + + if (emailAlreadySend) { + const timeElapsed = Date.now() - new Date(optInCode.updatedAt).getTime() + if (timeElapsed < 10 * 60 * 1000) { + throw new Error('email already sent less than a 10 minutes before') + } } - return new SendPasswordResetEmailResponse(response.data) + + const emailSent = await sendEMail({ + from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`, + to: `${loginUser.firstName} ${loginUser.lastName} <${email}>`, + subject: 'Gradido: Reset Password', + text: `Hallo ${loginUser.firstName} ${loginUser.lastName}, + + Du oder jemand anderes hat für dieses Konto ein Zurücksetzen des Passworts angefordert. + Wenn du es warst, klicke bitte auf den Link: ${link} + oder kopiere den obigen Link in Dein Browserfenster. + + Mit freundlichen Grüßen, + dein Gradido-Team`, + }) + + // In case EMails are disabled log the activation link for the user + if (!emailSent) { + // eslint-disable-next-line no-console + console.log(`Reset password link: ${link}`) + } + + return true } @Mutation(() => String) diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 01021f601..bf4d07253 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -67,9 +67,7 @@ export const transactionsQuery = gql` export const sendResetPasswordEmail = gql` query($email: String!) { - sendResetPasswordEmail(email: $email) { - state - } + sendResetPasswordEmail(email: $email) } ` From 87714f0089c395f8d8b72eec9b925303353fea83 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 23 Nov 2021 03:05:29 +0100 Subject: [PATCH 02/46] corrected error typo --- 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 ffa78e8aa..9322ffc52 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -467,7 +467,7 @@ export class UserResolver { if (emailAlreadySend) { const timeElapsed = Date.now() - new Date(optInCode.updatedAt).getTime() if (timeElapsed < 10 * 60 * 1000) { - throw new Error('email already sent less than a 10 minutes before') + throw new Error('email already sent less than 10 minutes before') } } From 1802dbf06da9d0074a7dca4a200a8d37bba9c431 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 23 Nov 2021 03:08:43 +0100 Subject: [PATCH 03/46] use correct optin for register --- 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 9322ffc52..05ada134f 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -394,7 +394,7 @@ export class UserResolver { const emailOptIn = new LoginEmailOptIn() emailOptIn.userId = loginUserId emailOptIn.verificationCode = random(64) - emailOptIn.emailOptInTypeId = 2 + emailOptIn.emailOptInTypeId = 1 await queryRunner.manager.save(emailOptIn).catch((error) => { // eslint-disable-next-line no-console From bead60ac7f4010f53c52e1f977a5b57409c06659 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 23 Nov 2021 03:10:34 +0100 Subject: [PATCH 04/46] use correct constants for email optins --- backend/src/graphql/resolver/UserResolver.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 05ada134f..68788f470 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -31,6 +31,9 @@ import { sendEMail } from '../../util/sendEMail' import { LoginElopageBuysRepository } from '../../typeorm/repository/LoginElopageBuys' import { randomBytes } from 'crypto' +const EMAIL_OPT_IN_RESET_PASSWORD = 2 +const EMAIL_OPT_IN_REGISTER = 1 + // eslint-disable-next-line @typescript-eslint/no-var-requires const sodium = require('sodium-native') // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -394,7 +397,7 @@ export class UserResolver { const emailOptIn = new LoginEmailOptIn() emailOptIn.userId = loginUserId emailOptIn.verificationCode = random(64) - emailOptIn.emailOptInTypeId = 1 + emailOptIn.emailOptInTypeId = EMAIL_OPT_IN_REGISTER await queryRunner.manager.save(emailOptIn).catch((error) => { // eslint-disable-next-line no-console @@ -441,7 +444,6 @@ export class UserResolver { @Query(() => Boolean) async sendResetPasswordEmail(@Arg('email') email: string): Promise { let emailAlreadySend = false - const EMAIL_OPT_IN_RESET_PASSWORD = 2 const loginUser = await LoginUser.findOneOrFail({ email }) From a9941faafb752951d047ca8d8f09bb2183d5ec18 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 23 Nov 2021 09:34:54 +0100 Subject: [PATCH 05/46] Added locale for activate email & reset email. --- frontend/src/locales/de.json | 5 +++++ frontend/src/locales/en.json | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index faa61886d..cc3f781ee 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -145,6 +145,11 @@ "text": "Jetzt kannst du ein neues Passwort speichern, mit dem du dich zukünftig in der Gradido-App anmelden kannst." }, "send_now": "Jetzt senden", + "set": "Passwort festsetzen", + "set-password": { + "not-authenticated": "Leider konnten wir dich nicht authentifizieren. Bitte wende dich an den Support.", + "text": "Jetzt kannst du ein neues Passwort speichern, mit dem du dich zukünftig in der Gradido-App anmelden kannst." + }, "subtitle": "Wenn du dein Passwort vergessen hast, kannst du es hier zurücksetzen." } }, diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 91e25f61d..0598f66ad 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -144,7 +144,12 @@ "not-authenticated": "Unfortunately we could not authenticate you. Please contact the support.", "text": "Now you can save a new password to login to the Gradido-App in the future." }, - "send_now": "Send now", + "send_now": "Jetzt senden", + "set": "Passwort festsetzen", + "set-password": { + "not-authenticated": "Leider konnten wir dich nicht authentifizieren. Bitte wende dich an den Support.", + "text": "Jetzt kannst du ein neues Passwort speichern, mit dem du dich zukünftig in der Gradido-App anmelden kannst." + }, "subtitle": "If you have forgotten your password, you can reset it here." } }, From 3ce3370e80045f9bde028eab6823dd974eaf502f Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 23 Nov 2021 09:35:27 +0100 Subject: [PATCH 06/46] Changed the vue test so that we get information of the locale. --- frontend/src/views/Pages/ResetPassword.spec.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/views/Pages/ResetPassword.spec.js b/frontend/src/views/Pages/ResetPassword.spec.js index 81ea7ed0f..fb607708b 100644 --- a/frontend/src/views/Pages/ResetPassword.spec.js +++ b/frontend/src/views/Pages/ResetPassword.spec.js @@ -72,9 +72,7 @@ describe('ResetPassword', () => { it('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.reset-password.not-authenticated', - ) + expect(wrapper.find('div.header').text()).toContain('settings.password.not-authenticated') }) }) From 7afbc8dc3c1f3cd4b6aaed2247564043864de34e Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 23 Nov 2021 09:36:10 +0100 Subject: [PATCH 07/46] Implemented a switch for reset & checkEmail so that both send to ResetPassword.vue but show different textes. --- frontend/src/views/Pages/ResetPassword.vue | 34 ++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/frontend/src/views/Pages/ResetPassword.vue b/frontend/src/views/Pages/ResetPassword.vue index 81b3d7df7..15c14b5ac 100644 --- a/frontend/src/views/Pages/ResetPassword.vue +++ b/frontend/src/views/Pages/ResetPassword.vue @@ -8,10 +8,10 @@

{{ $t('settings.password.reset') }}

- {{ $t('settings.password.reset-password.text') }} + {{ $t(displaySetup.authenticated) }} - {{ $t('settings.password.reset-password.not-authenticated') }} + {{ $t(displaySetup.notAuthenticated) }}
@@ -29,7 +29,7 @@
- {{ $t('settings.password.reset') }} + {{ $t(displaySetup.button) }}
@@ -38,9 +38,9 @@ - + - {{ $t('back') }} + {{ $t('back') }} @@ -51,6 +51,25 @@ import InputPasswordConfirmation from '../../components/Inputs/InputPasswordConf import { loginViaEmailVerificationCode } from '../../graphql/queries' import { resetPassword } from '../../graphql/mutations' +const textFields = { + reset: { + authenticated: 'settings.password.reset-password.text', + notAuthenticated: 'settings.password.not-authenticated', + button: 'settings.password.reset', + linkTo: '/login', + }, + checkEmail: { + authenticated: 'settings.password.set-password.text', + notAuthenticated: 'settings.password.not-authenticated', + button: 'settings.password.set', + linkTo: '/login', + }, + login: { + headline: 'site.thx.errorTitle', + subtitle: 'site.thx.activateEmail', + }, +} + export default { name: 'ResetPassword', components: { @@ -67,6 +86,7 @@ export default { email: null, pending: true, register: false, + displaySetup: {}, } }, methods: { @@ -111,9 +131,13 @@ export default { loader.hide() this.pending = false }, + setDisplaySetup(from) { + this.displaySetup = textFields[this.$route.params.comingFrom] + }, }, mounted() { this.authenticate() + this.setDisplaySetup() }, } From a1a24d155304f3d05383b793f091d62b85f87634 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 23 Nov 2021 11:24:42 +0100 Subject: [PATCH 08/46] prototype setPassword call --- backend/src/graphql/resolver/UserResolver.ts | 79 +++++++++++++------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 68788f470..82de72c0a 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -275,21 +275,6 @@ export class UserResolver { return user } - @Query(() => LoginViaVerificationCode) - async loginViaEmailVerificationCode( - @Arg('optin') optin: string, - ): Promise { - // I cannot use number as type here. - // The value received is not the same as sent by the query - const result = await apiGet( - CONFIG.LOGIN_API_URL + 'loginViaEmailVerificationCode?emailVerificationCode=' + optin, - ) - if (!result.success) { - throw new Error(result.data) - } - return new LoginViaVerificationCode(result.data) - } - @Authorized() @Query(() => String) async logout(): Promise { @@ -468,7 +453,7 @@ export class UserResolver { if (emailAlreadySend) { const timeElapsed = Date.now() - new Date(optInCode.updatedAt).getTime() - if (timeElapsed < 10 * 60 * 1000) { + if (timeElapsed <= 10 * 60 * 1000) { throw new Error('email already sent less than 10 minutes before') } } @@ -513,6 +498,56 @@ export class UserResolver { return 'success' } + @Query(() => CheckEmailResponse) + @UseMiddleware(klicktippRegistrationMiddleware) + async checkEmail(@Arg('optin') optin: string): Promise { + const result = await apiGet( + CONFIG.LOGIN_API_URL + 'loginViaEmailVerificationCode?emailVerificationCode=' + optin, + ) + if (!result.success) { + throw new Error(result.data) + } + return new CheckEmailResponse(result.data) + } + + @Query(() => Boolean) + async setPassword( + @Arg('code') code: string, + @Arg('password') password: string, + ): Promise { + + const optInCode = await LoginEmailOptIn.findOneOrFail({verificationCode: code}).catch(()=>{ + throw new Error('Could not login with emailVerificationCode') + }) + + // Code is only valid for 10minutes + const timeElapsed = Date.now() - new Date(optInCode.updatedAt).getTime() + if (timeElapsed > 10 * 60 * 1000) { + throw new Error('Code is older than 10 minutes') + } + + // load user + const loginUser = await LoginUser.findOneOrFail({id: optInCode.userId}).catch(()=> { + throw new Error('Could not find corresponding User') + }) + + // Activate EMail + loginUser.emailChecked = true + + // Update Password + + // Save loginUser + await loginUser.save() + + // Sign into Klicktipp + if(optInCode.emailOptInTypeId === EMAIL_OPT_IN_REGISTER){ + // TODO + } + + // Delete Code + await optInCode.remove() + } + @Authorized() @Mutation(() => Boolean) async updateUserInfos( @@ -645,18 +680,6 @@ export class UserResolver { return true } - @Query(() => CheckEmailResponse) - @UseMiddleware(klicktippRegistrationMiddleware) - async checkEmail(@Arg('optin') optin: string): Promise { - const result = await apiGet( - CONFIG.LOGIN_API_URL + 'loginViaEmailVerificationCode?emailVerificationCode=' + optin, - ) - if (!result.success) { - throw new Error(result.data) - } - return new CheckEmailResponse(result.data) - } - @Authorized() @Query(() => Boolean) async hasElopage(@Ctx() context: any): Promise { From d1dfda81ce503578a4aee79534eb2455c6444a97 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 23 Nov 2021 12:00:14 +0100 Subject: [PATCH 09/46] removed password from createUser & implement setPassword --- backend/src/graphql/arg/CreateUserArgs.ts | 3 - .../src/graphql/model/CheckEmailResponse.ts | 29 -------- backend/src/graphql/resolver/UserResolver.ts | 69 ++++++++++--------- frontend/src/graphql/queries.js | 9 --- 4 files changed, 37 insertions(+), 73 deletions(-) delete mode 100644 backend/src/graphql/model/CheckEmailResponse.ts diff --git a/backend/src/graphql/arg/CreateUserArgs.ts b/backend/src/graphql/arg/CreateUserArgs.ts index 3d09e56eb..75897e3fc 100644 --- a/backend/src/graphql/arg/CreateUserArgs.ts +++ b/backend/src/graphql/arg/CreateUserArgs.ts @@ -11,9 +11,6 @@ export default class CreateUserArgs { @Field(() => String) lastName: string - @Field(() => String) - password: string - @Field(() => String) language: string diff --git a/backend/src/graphql/model/CheckEmailResponse.ts b/backend/src/graphql/model/CheckEmailResponse.ts deleted file mode 100644 index 948739722..000000000 --- a/backend/src/graphql/model/CheckEmailResponse.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { ObjectType, Field } from 'type-graphql' - -@ObjectType() -export class CheckEmailResponse { - constructor(json: any) { - this.sessionId = json.session_id - this.email = json.user.email - this.language = json.user.language - this.firstName = json.user.first_name - this.lastName = json.user.last_name - } - - @Field(() => Number) - sessionId: number - - @Field(() => String) - email: string - - @Field(() => String) - firstName: string - - @Field(() => String) - lastName: string - - @Field(() => String) - language: string -} diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 82de72c0a..1c4bc2b50 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -19,7 +19,6 @@ import { klicktippRegistrationMiddleware, klicktippNewsletterStateMiddleware, } from '../../middleware/klicktippMiddleware' -import { CheckEmailResponse } from '../model/CheckEmailResponse' import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository' import { LoginUserRepository } from '../../typeorm/repository/LoginUser' import { Setting } from '../enum/Setting' @@ -288,7 +287,7 @@ export class UserResolver { @Mutation(() => String) async createUser( - @Args() { email, firstName, lastName, password, language, publisherId }: CreateUserArgs, + @Args() { email, firstName, lastName, language, publisherId }: CreateUserArgs, ): Promise { // TODO: wrong default value (should be null), how does graphql work here? Is it an required field? // default int publisher_id = 0; @@ -298,13 +297,6 @@ export class UserResolver { language = DEFAULT_LANGUAGE } - // Validate Password - if (!isPassword(password)) { - throw new Error( - 'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!', - ) - } - // Validate username // TODO: never true const username = '' @@ -322,10 +314,7 @@ export class UserResolver { } const passphrase = PassphraseGenerate() - const keyPair = KeyPairEd25519Create(passphrase) // return pub, priv Key - const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash const emailHash = getEmailHash(email) - const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1]) // Table: login_users const loginUser = new LoginUser() @@ -334,13 +323,13 @@ export class UserResolver { loginUser.lastName = lastName loginUser.username = username loginUser.description = '' - loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash + // loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash loginUser.emailHash = emailHash loginUser.language = language loginUser.groupId = 1 loginUser.publisherId = publisherId - loginUser.pubKey = keyPair[0] - loginUser.privKey = encryptedPrivkey + // loginUser.pubKey = keyPair[0] + // loginUser.privKey = encryptedPrivkey const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() @@ -498,25 +487,12 @@ export class UserResolver { return 'success' } - @Query(() => CheckEmailResponse) - @UseMiddleware(klicktippRegistrationMiddleware) - async checkEmail(@Arg('optin') optin: string): Promise { - const result = await apiGet( - CONFIG.LOGIN_API_URL + 'loginViaEmailVerificationCode?emailVerificationCode=' + optin, - ) - if (!result.success) { - throw new Error(result.data) - } - return new CheckEmailResponse(result.data) - } - @Query(() => Boolean) async setPassword( @Arg('code') code: string, @Arg('password') password: string, ): Promise { - - const optInCode = await LoginEmailOptIn.findOneOrFail({verificationCode: code}).catch(()=>{ + const optInCode = await LoginEmailOptIn.findOneOrFail({ verificationCode: code }).catch(() => { throw new Error('Could not login with emailVerificationCode') }) @@ -527,25 +503,54 @@ export class UserResolver { } // load user - const loginUser = await LoginUser.findOneOrFail({id: optInCode.userId}).catch(()=> { + const loginUser = await LoginUser.findOneOrFail({ id: optInCode.userId }).catch(() => { throw new Error('Could not find corresponding User') }) + const loginUserBackup = await LoginUserBackup.findOneOrFail({ userId: loginUser.id }).catch( + () => { + throw new Error('Could not find corresponding BackupUser') + }, + ) + + const passphrase = loginUserBackup.passphrase.slice(0, -1).split(' ') + if (passphrase.length < PHRASE_WORD_COUNT) { + // TODO if this can happen we cannot recover from that + throw new Error('Could not load a correct passphrase') + } + // Activate EMail loginUser.emailChecked = true + // Validate Password + if (!isPassword(password)) { + throw new Error( + 'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!', + ) + } + // Update Password + const passwordHash = SecretKeyCryptographyCreateKey(loginUser.email, password) // return short and long hash + const keyPair = KeyPairEd25519Create(passphrase) // return pub, priv Key + const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1]) + loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash + loginUser.pubKey = keyPair[0] + loginUser.privKey = encryptedPrivkey // Save loginUser + // TODO transaction await loginUser.save() // Sign into Klicktipp - if(optInCode.emailOptInTypeId === EMAIL_OPT_IN_REGISTER){ - // TODO + if (optInCode.emailOptInTypeId === EMAIL_OPT_IN_REGISTER) { + // TODO klicktippRegistrationMiddleware } // Delete Code + // TODO transaction await optInCode.remove() + + return true } @Authorized() diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index bf4d07253..f1b1ac768 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -95,15 +95,6 @@ export const listGDTEntriesQuery = gql` } ` -export const checkEmailQuery = gql` - query($optin: String!) { - checkEmail(optin: $optin) { - email - sessionId - } - } -` - export const communityInfo = gql` query { getCommunityInfo { From d5b2356caf86b252deb022b15f6a7aef977d7312 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 23 Nov 2021 12:08:23 +0100 Subject: [PATCH 10/46] setPassword in frontend remove resetPassword --- backend/src/graphql/resolver/UserResolver.ts | 17 ------------ frontend/src/graphql/mutations.js | 6 ++--- frontend/src/views/Pages/ResetPassword.vue | 28 +++----------------- 3 files changed, 7 insertions(+), 44 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 1c4bc2b50..8057dec15 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -470,23 +470,6 @@ export class UserResolver { return true } - @Mutation(() => String) - async resetPassword( - @Args() - { sessionId, email, password }: ChangePasswordArgs, - ): Promise { - const payload = { - session_id: sessionId, - email, - password, - } - const result = await apiPost(CONFIG.LOGIN_API_URL + 'resetPassword', payload) - if (!result.success) { - throw new Error(result.data) - } - return 'success' - } - @Query(() => Boolean) async setPassword( @Arg('code') code: string, diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index d1d3d583c..eafffd957 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -12,9 +12,9 @@ export const unsubscribeNewsletter = gql` } ` -export const resetPassword = gql` - mutation($sessionId: Float!, $email: String!, $password: String!) { - resetPassword(sessionId: $sessionId, email: $email, password: $password) +export const setPassword = gql` + mutation($code: String!, $password: String!) { + setPassword(code: $code, password: $password) } ` diff --git a/frontend/src/views/Pages/ResetPassword.vue b/frontend/src/views/Pages/ResetPassword.vue index 81b3d7df7..11bf88194 100644 --- a/frontend/src/views/Pages/ResetPassword.vue +++ b/frontend/src/views/Pages/ResetPassword.vue @@ -49,7 +49,7 @@ - From 30079e2039794f6002d5a21338b690234c7afb8d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 24 Nov 2021 03:56:42 +0100 Subject: [PATCH 26/46] fix createUser call --- frontend/src/graphql/mutations.js | 4 +--- frontend/src/views/Pages/Register.vue | 18 +++--------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index eafffd957..0c6ea51aa 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -42,12 +42,11 @@ export const updateUserInfos = gql` } ` -export const registerUser = gql` +export const createUser = gql` mutation( $firstName: String! $lastName: String! $email: String! - $password: String! $language: String! $publisherId: Int ) { @@ -55,7 +54,6 @@ export const registerUser = gql` email: $email firstName: $firstName lastName: $lastName - password: $password language: $language publisherId: $publisherId ) diff --git a/frontend/src/views/Pages/Register.vue b/frontend/src/views/Pages/Register.vue index ea4000cff..d6e849345 100755 --- a/frontend/src/views/Pages/Register.vue +++ b/frontend/src/views/Pages/Register.vue @@ -85,10 +85,6 @@
- @@ -158,14 +154,13 @@ diff --git a/frontend/src/views/Pages/ResetPassword.vue b/frontend/src/views/Pages/ResetPassword.vue index 8dc7ee5bc..a4d06a914 100644 --- a/frontend/src/views/Pages/ResetPassword.vue +++ b/frontend/src/views/Pages/ResetPassword.vue @@ -77,6 +77,7 @@ export default { password: '', passwordRepeat: '', }, + displaySetup: {}, } }, methods: { @@ -94,20 +95,29 @@ export default { this.$router.push('/thx/reset') }) .catch((error) => { - this.$toasted.error(error.message) + if (error.message.includes('Code is older than 10 minutes')) { + this.$toasted.error(error.message) + this.$router.push('/password/reset') + } else { + this.$toasted.error(error.message) + } }) }, async authenticate() { // TODO validate somehow if present and looks good? // const optin = this.$route.params.optin }, - setDisplaySetup(from) { - this.displaySetup = textFields[this.$route.params.comingFrom] + setDisplaySetup() { + if (!this.$route.params.comingFrom) { + this.displaySetup = textFields.reset + } else { + this.displaySetup = textFields[this.$route.params.comingFrom] + } }, }, - async mounted() { - await this.authenticate() + created() { this.setDisplaySetup() + // await this.authenticate() }, } From 03faac6ecbc4d6d1c3f56eb5349fd9dac00dfb70 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 7 Dec 2021 10:14:52 +0100 Subject: [PATCH 39/46] Case where a user has not activated his email he gets redirected to /thx/login with a message to check his eamils. --- frontend/src/routes/routes.js | 2 +- frontend/src/views/Pages/ForgotPassword.vue | 1 - frontend/src/views/Pages/Login.vue | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/routes/routes.js b/frontend/src/routes/routes.js index 26f21f9cf..418422997 100755 --- a/frontend/src/routes/routes.js +++ b/frontend/src/routes/routes.js @@ -50,7 +50,7 @@ const routes = [ path: '/thx/:comingFrom', component: () => import('../views/Pages/thx.vue'), beforeEnter: (to, from, next) => { - const validFrom = ['password', 'reset', 'register', 'login'] + const validFrom = ['password', 'reset', 'register', 'login', 'Login'] if (!validFrom.includes(from.path.split('/')[1])) { next({ path: '/login' }) } else { diff --git a/frontend/src/views/Pages/ForgotPassword.vue b/frontend/src/views/Pages/ForgotPassword.vue index c27afae6a..c8b31246f 100644 --- a/frontend/src/views/Pages/ForgotPassword.vue +++ b/frontend/src/views/Pages/ForgotPassword.vue @@ -88,7 +88,6 @@ export default { }, }, created() { - console.log('comingFrom', this.$route.params.comingFrom) if (this.$route.params.comingFrom) { this.displaySetup = textFields[this.$route.params.comingFrom] } else { diff --git a/frontend/src/views/Pages/Login.vue b/frontend/src/views/Pages/Login.vue index 45e700099..5f86b1600 100755 --- a/frontend/src/views/Pages/Login.vue +++ b/frontend/src/views/Pages/Login.vue @@ -105,11 +105,11 @@ export default { loader.hide() }) .catch((error) => { - if (!error.message.includes('user email not validated')) { + if (!error.message.includes('User email not validated')) { this.$toasted.error(this.$t('error.no-account')) } else { // : this.$t('error.no-email-verify') - this.$router.push('/thx/login') + this.$router.push('/reset/login') } loader.hide() }) From c7fbb9b0bf4bb7b4ba70276c10f7cbcc231e4c73 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 7 Dec 2021 10:19:29 +0100 Subject: [PATCH 40/46] Changed the Login error handling so that we get the no-account message in case of a specific error, else we send the user to /reset/login --- backend/src/graphql/resolver/UserResolver.ts | 1 - frontend/src/views/Pages/Login.vue | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 9856e1968..bebc32e1e 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -199,7 +199,6 @@ export class UserResolver { throw new Error('No user with this credentials') }) if (!loginUser.emailChecked) { - // TODO we want to catch this on the frontend and ask the user to check his emails or resend code throw new Error('User email not validated') } if (loginUser.password === BigInt(0)) { diff --git a/frontend/src/views/Pages/Login.vue b/frontend/src/views/Pages/Login.vue index 5f86b1600..0ce209551 100755 --- a/frontend/src/views/Pages/Login.vue +++ b/frontend/src/views/Pages/Login.vue @@ -105,7 +105,7 @@ export default { loader.hide() }) .catch((error) => { - if (!error.message.includes('User email not validated')) { + if (error.message.includes('No user with this credentials')) { this.$toasted.error(this.$t('error.no-account')) } else { // : this.$t('error.no-email-verify') From ca6f8076d6f5a7b6319de25fe719ad2150b3749e Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 7 Dec 2021 10:41:43 +0100 Subject: [PATCH 41/46] Logic change on error message included in test. --- frontend/src/views/Pages/Login.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/views/Pages/Login.spec.js b/frontend/src/views/Pages/Login.spec.js index 53bb9446f..a16a8ad54 100644 --- a/frontend/src/views/Pages/Login.spec.js +++ b/frontend/src/views/Pages/Login.spec.js @@ -238,7 +238,7 @@ describe('Login', () => { describe('login fails', () => { beforeEach(() => { apolloQueryMock.mockRejectedValue({ - message: 'Ouch!', + message: '..No user with this credentials', }) }) From 4b49d85d1aa387a10130984b49c2f3578673e7ce Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 7 Dec 2021 10:44:24 +0100 Subject: [PATCH 42/46] Router got a new route test had to be fixed. --- frontend/src/routes/router.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/routes/router.test.js b/frontend/src/routes/router.test.js index 12c028fba..a85c7a291 100644 --- a/frontend/src/routes/router.test.js +++ b/frontend/src/routes/router.test.js @@ -49,8 +49,8 @@ describe('router', () => { expect(routes.find((r) => r.path === '/').redirect()).toEqual({ path: '/login' }) }) - it('has fifteen routes defined', () => { - expect(routes).toHaveLength(15) + it('has sixteen routes defined', () => { + expect(routes).toHaveLength(16) }) describe('overview', () => { From cee7c66a45ff66068685251a2e09ee02f6458fcf Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 7 Dec 2021 10:59:00 +0100 Subject: [PATCH 43/46] ForgotPassword logic change implemented in tests. --- .../src/views/Pages/ForgotPassword.spec.js | 68 ++++++++++++++----- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/frontend/src/views/Pages/ForgotPassword.spec.js b/frontend/src/views/Pages/ForgotPassword.spec.js index 91247d8a6..c77689425 100644 --- a/frontend/src/views/Pages/ForgotPassword.spec.js +++ b/frontend/src/views/Pages/ForgotPassword.spec.js @@ -8,30 +8,52 @@ const localVue = global.localVue const mockRouterPush = jest.fn() + +const mocks = { + $t: jest.fn((t) => t), + $router: { + push: mockRouterPush, + }, + $apollo: { + query: mockAPIcall, + }, +} + +const stubs = { + RouterLink: RouterLinkStub, +} + +const createMockObject = (comingFrom) => { + return { + localVue, + mocks: { + $t: jest.fn((t) => t), + $router: { + push: mockRouterPush, + }, + $apollo: { + query: mockAPIcall, + }, + $route: { + params: { + comingFrom, + }, + }, + }, + stubs, + } +} + describe('ForgotPassword', () => { let wrapper - const mocks = { - $t: jest.fn((t) => t), - $router: { - push: mockRouterPush, - }, - $apollo: { - query: mockAPIcall, - }, - } - - const stubs = { - RouterLink: RouterLinkStub, - } - - const Wrapper = () => { - return mount(ForgotPassword, { localVue, mocks, stubs }) + const Wrapper = (functionN) => { + return mount(ForgotPassword, functionN) } describe('mount', () => { beforeEach(() => { - wrapper = Wrapper() + wrapper = Wrapper(createMockObject()) }) it('renders the component', () => { @@ -41,7 +63,7 @@ describe('ForgotPassword', () => { it('has a title', () => { expect(wrapper.find('h1').text()).toEqual('settings.password.reset') }) - + it('has a subtitle', () => { expect(wrapper.find('p.text-lead').text()).toEqual('settings.password.subtitle') }) @@ -144,5 +166,15 @@ describe('ForgotPassword', () => { }) }) }) + + describe('comingFrom login', () => { + beforeEach(() => { + wrapper = Wrapper(createMockObject('reset')) + }) + + it('has another subtitle', () => { + expect(wrapper.find('p.text-lead').text()).toEqual('settings.password.resend_subtitle') + }) + }) }) }) From 202ab2863ce5915e0409b692b96a49b6d3666860 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 7 Dec 2021 12:28:12 +0100 Subject: [PATCH 44/46] Fixing ResetPassword. --- .../src/views/Pages/ResetPassword.spec.js | 167 +++++++++--------- frontend/src/views/Pages/ResetPassword.vue | 8 +- 2 files changed, 90 insertions(+), 85 deletions(-) diff --git a/frontend/src/views/Pages/ResetPassword.spec.js b/frontend/src/views/Pages/ResetPassword.spec.js index 91306b854..36a2f169c 100644 --- a/frontend/src/views/Pages/ResetPassword.spec.js +++ b/frontend/src/views/Pages/ResetPassword.spec.js @@ -6,59 +6,58 @@ import flushPromises from 'flush-promises' const localVue = global.localVue -const apolloQueryMock = jest.fn().mockRejectedValue({ message: 'error' }) const apolloMutationMock = jest.fn() const toasterMock = jest.fn() const routerPushMock = jest.fn() +const stubs = { + RouterLink: RouterLinkStub, +} + +const createMockObject = (comingFrom) => { + return { + localVue, + mocks: { + $i18n: { + locale: 'en', + }, + $t: jest.fn((t) => t), + $route: { + params: { + optin: '123', + comingFrom, + }, + }, + $toasted: { + error: toasterMock, + }, + $router: { + push: routerPushMock, + }, + $loading: { + show: jest.fn(() => { + return { hide: jest.fn() } + }), + }, + $apollo: { + mutate: apolloMutationMock, + }, + }, + stubs, + } +} + describe('ResetPassword', () => { let wrapper - const mocks = { - $i18n: { - locale: 'en', - }, - $t: jest.fn((t) => t), - $route: { - params: { - optin: '123', - }, - }, - $toasted: { - error: toasterMock, - }, - $router: { - push: routerPushMock, - }, - $loading: { - show: jest.fn(() => { - return { hide: jest.fn() } - }), - }, - $apollo: { - mutate: apolloMutationMock, - query: apolloQueryMock, - }, - } - - const stubs = { - RouterLink: RouterLinkStub, - } - - const Wrapper = () => { - return mount(ResetPassword, { localVue, mocks, stubs }) + const Wrapper = (functionName) => { + return mount(ResetPassword, functionName) } describe('mount', () => { beforeEach(() => { - wrapper = Wrapper() - }) - - it.skip('calls the email verification when created', async () => { - expect(apolloQueryMock).toBeCalledWith( - expect.objectContaining({ variables: { optin: '123' } }), - ) + wrapper = Wrapper(createMockObject()) }) describe('No valid optin', () => { @@ -77,28 +76,12 @@ describe('ResetPassword', () => { }) describe('is authenticated', () => { - beforeEach(() => { - apolloQueryMock.mockResolvedValue({ - data: { - loginViaEmailVerificationCode: { - sessionId: 1, - email: 'user@example.org', - }, - }, - }) - }) - - it.skip('Has sessionId from API call', async () => { - await wrapper.vm.$nextTick() - expect(wrapper.vm.sessionId).toBe(1) - }) - - it.skip('renders the Reset Password form when authenticated', () => { + it('renders the Reset Password form when authenticated', () => { expect(wrapper.find('div.resetpwd-form').exists()).toBeTruthy() }) describe('Register header', () => { - it.skip('has a welcome message', async () => { + it('has a welcome message', async () => { expect(wrapper.find('div.header').text()).toContain('settings.password.reset') expect(wrapper.find('div.header').text()).toContain( 'settings.password.reset-password.text', @@ -107,31 +90,31 @@ describe('ResetPassword', () => { }) describe('links', () => { - it.skip('has a link "Back"', async () => { + it('has a link "Back"', async () => { expect(wrapper.findAllComponents(RouterLinkStub).at(0).text()).toEqual('back') }) - it.skip('links to /login when clicking "Back"', async () => { - expect(wrapper.findAllComponents(RouterLinkStub).at(0).props().to).toBe('/Login') + it('links to /login when clicking "Back"', async () => { + expect(wrapper.findAllComponents(RouterLinkStub).at(0).props().to).toBe('/login') }) }) describe('reset password form', () => { - it.skip('has a register form', async () => { + it('has a register form', async () => { expect(wrapper.find('form').exists()).toBeTruthy() }) - it.skip('has 2 password input fields', async () => { + it('has 2 password input fields', async () => { expect(wrapper.findAll('input[type="password"]').length).toBe(2) }) - it.skip('toggles the first input field to text when eye icon is clicked', async () => { - wrapper.findAll('button').at(0).trigger('click') + it('toggles the first input field to text when eye icon is clicked', async () => { + await wrapper.findAll('button').at(0).trigger('click') await wrapper.vm.$nextTick() expect(wrapper.findAll('input').at(0).attributes('type')).toBe('text') }) - it.skip('toggles the second input field to text when eye icon is clicked', async () => { + it('toggles the second input field to text when eye icon is clicked', async () => { wrapper.findAll('button').at(1).trigger('click') await wrapper.vm.$nextTick() expect(wrapper.findAll('input').at(1).attributes('type')).toBe('text') @@ -140,44 +123,68 @@ describe('ResetPassword', () => { describe('submit form', () => { beforeEach(async () => { - await wrapper.setData({ authenticated: true, sessionId: 1 }) - await wrapper.vm.$nextTick() + // wrapper = Wrapper(createMockObject()) await wrapper.findAll('input').at(0).setValue('Aa123456_') await wrapper.findAll('input').at(1).setValue('Aa123456_') await flushPromises() - await wrapper.find('form').trigger('submit') }) - describe('server response with error', () => { - beforeEach(() => { - apolloMutationMock.mockRejectedValue({ message: 'error' }) + 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() }) - it.skip('toasts an error message', () => { - expect(toasterMock).toHaveBeenCalledWith('error') + + it('toasts an error message', () => { + expect(toasterMock).toHaveBeenCalledWith('...Code is older than 10 minutes') + }) + + it('router pushes to /password/reset', () => { + expect(routerPushMock).toHaveBeenCalledWith('/password/reset') + }) + }) + + describe('server response with error code > 10min', () => { + beforeEach(async () => { + jest.clearAllMocks() + apolloMutationMock.mockRejectedValueOnce({ message: 'Error' }) + await wrapper.find('form').trigger('submit') + await flushPromises() + }) + + it('toasts an error message', () => { + expect(toasterMock).toHaveBeenCalledWith('Error') }) }) describe('server response with success', () => { - beforeEach(() => { + beforeEach(async () => { apolloMutationMock.mockResolvedValue({ data: { resetPassword: 'success', }, }) + wrapper = Wrapper(createMockObject('checkEmail')) + 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.skip('calls the API', () => { + + it('calls the API', () => { expect(apolloMutationMock).toBeCalledWith( expect.objectContaining({ variables: { - sessionId: 1, - email: 'user@example.org', + code: '123', password: 'Aa123456_', }, }), ) }) - it.skip('redirects to "/thx/reset"', () => { + it('redirects to "/thx/reset"', () => { expect(routerPushMock).toHaveBeenCalledWith('/thx/reset') }) }) diff --git a/frontend/src/views/Pages/ResetPassword.vue b/frontend/src/views/Pages/ResetPassword.vue index a4d06a914..9ee3a2fa6 100644 --- a/frontend/src/views/Pages/ResetPassword.vue +++ b/frontend/src/views/Pages/ResetPassword.vue @@ -82,6 +82,7 @@ export default { }, methods: { async onSubmit() { + console.log('OnSubmit', this.$route.params.optin, this.form.password) this.$apollo .mutate({ mutation: setPassword, @@ -91,10 +92,12 @@ export default { }, }) .then(() => { + console.log('then') this.form.password = '' this.$router.push('/thx/reset') }) .catch((error) => { + console.log('catch', error.message) if (error.message.includes('Code is older than 10 minutes')) { this.$toasted.error(error.message) this.$router.push('/password/reset') @@ -103,10 +106,6 @@ export default { } }) }, - async authenticate() { - // TODO validate somehow if present and looks good? - // const optin = this.$route.params.optin - }, setDisplaySetup() { if (!this.$route.params.comingFrom) { this.displaySetup = textFields.reset @@ -117,7 +116,6 @@ export default { }, created() { this.setDisplaySetup() - // await this.authenticate() }, } From 4afd3ffe0a76d7821b20f639052df6b3fae2ae58 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 7 Dec 2021 12:34:10 +0100 Subject: [PATCH 45/46] Remove console.log coverage of frontend set to 86% --- .github/workflows/test.yml | 2 +- frontend/src/views/Pages/ForgotPassword.spec.js | 13 +------------ frontend/src/views/Pages/ResetPassword.vue | 3 --- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 470b3efbb..2996ffeec 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -399,7 +399,7 @@ jobs: report_name: Coverage Frontend type: lcov result_path: ./coverage/lcov.info - min_coverage: 84 + min_coverage: 86 token: ${{ github.token }} ############################################################################## diff --git a/frontend/src/views/Pages/ForgotPassword.spec.js b/frontend/src/views/Pages/ForgotPassword.spec.js index c77689425..47c9fad56 100644 --- a/frontend/src/views/Pages/ForgotPassword.spec.js +++ b/frontend/src/views/Pages/ForgotPassword.spec.js @@ -8,17 +8,6 @@ const localVue = global.localVue const mockRouterPush = jest.fn() - -const mocks = { - $t: jest.fn((t) => t), - $router: { - push: mockRouterPush, - }, - $apollo: { - query: mockAPIcall, - }, -} - const stubs = { RouterLink: RouterLinkStub, } @@ -63,7 +52,7 @@ describe('ForgotPassword', () => { it('has a title', () => { expect(wrapper.find('h1').text()).toEqual('settings.password.reset') }) - + it('has a subtitle', () => { expect(wrapper.find('p.text-lead').text()).toEqual('settings.password.subtitle') }) diff --git a/frontend/src/views/Pages/ResetPassword.vue b/frontend/src/views/Pages/ResetPassword.vue index 9ee3a2fa6..478a8db8c 100644 --- a/frontend/src/views/Pages/ResetPassword.vue +++ b/frontend/src/views/Pages/ResetPassword.vue @@ -82,7 +82,6 @@ export default { }, methods: { async onSubmit() { - console.log('OnSubmit', this.$route.params.optin, this.form.password) this.$apollo .mutate({ mutation: setPassword, @@ -92,12 +91,10 @@ export default { }, }) .then(() => { - console.log('then') this.form.password = '' this.$router.push('/thx/reset') }) .catch((error) => { - console.log('catch', error.message) if (error.message.includes('Code is older than 10 minutes')) { this.$toasted.error(error.message) this.$router.push('/password/reset') From 64adf1ddbb8469af3149f7cd41df6b719f3762d3 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Tue, 7 Dec 2021 13:21:51 +0100 Subject: [PATCH 46/46] Upgrade backend coverage to 37% --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2996ffeec..d5b02cc76 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -491,7 +491,7 @@ jobs: report_name: Coverage Backend type: lcov result_path: ./backend/coverage/lcov.info - min_coverage: 36 + min_coverage: 37 token: ${{ github.token }} ##############################################################################