From d15b9a7e3ce7c529cfd0dd6e3c292dbfce41f0b5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 4 Nov 2021 18:04:06 +0100 Subject: [PATCH 01/11] - do not send error email - return after finally block due to unknown behaviour?! --- backend/src/graphql/resolver/UserResolver.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 6c140bc62..ae8d0f8b1 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -384,11 +384,6 @@ export class UserResolver { emailOptIn.emailOptInTypeId = 2 await queryRunner.manager.save(emailOptIn).catch((error) => { - // TODO: Send error email instead of throw error - // if (!emailOptInModel->insertIntoDB(false)) { - // emailOptInModel->sendErrorsAsEmail(); - // return stateError("insert emailOptIn failed"); - // } // eslint-disable-next-line no-console console.log('Error while saving emailOptIn', error) throw new Error('error saving email opt in') @@ -397,7 +392,6 @@ export class UserResolver { // emailOptIn->setBaseUrl(user->getGroupBaseUrl() + ServerConfig::g_frontend_checkEmailPath); // em->addEmail(new model::Email(emailOptIn, user, model::Email::convertTypeFromInt(emailType))); await queryRunner.commitTransaction() - return 'success' } catch (e) { await queryRunner.rollbackTransaction() await rollbackAutoIncrement(queryRunner, LoginUser, `login_users`) @@ -407,6 +401,7 @@ export class UserResolver { } finally { await queryRunner.release() } + return 'success' } @Query(() => SendPasswordResetEmailResponse) From 854ce12d620ea125d696b27daf248995a55f060d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 4 Nov 2021 18:08:59 +0100 Subject: [PATCH 02/11] removed dangerous code --- backend/src/graphql/resolver/UserResolver.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index ae8d0f8b1..55cf5b81f 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -394,9 +394,11 @@ export class UserResolver { await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() - await rollbackAutoIncrement(queryRunner, LoginUser, `login_users`) - await rollbackAutoIncrement(queryRunner, LoginUserBackup, `login_user_backups`) - await rollbackAutoIncrement(queryRunner, DbUser, `state_users`) + // TODO: Lets not do this?! What if state_users were never updated? + // We would still roll back the autoincrement which would produce duplicate entries?! + // await rollbackAutoIncrement(queryRunner, LoginUser, `login_users`) + // await rollbackAutoIncrement(queryRunner, LoginUserBackup, `login_user_backups`) + // await rollbackAutoIncrement(queryRunner, DbUser, `state_users`) throw e } finally { await queryRunner.release() @@ -573,6 +575,7 @@ export class UserResolver { } } +/* const rollbackAutoIncrement = async ( queryRunner: QueryRunner, entity: typeof BaseEntity, @@ -588,3 +591,4 @@ const rollbackAutoIncrement = async ( throw new Error('Problems with reset auto increment: ' + error) }) } +*/ From f915bd40131cdd5a355f1a04847b39d9ba03718e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 6 Nov 2021 00:45:36 +0100 Subject: [PATCH 03/11] removed unused includes --- backend/src/graphql/resolver/UserResolver.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 55cf5b81f..c3c8e0f14 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -3,7 +3,7 @@ import fs from 'fs' import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } from 'type-graphql' -import { BaseEntity, getConnection, getCustomRepository, QueryRunner } from 'typeorm' +import { getConnection, getCustomRepository } from 'typeorm' import CONFIG from '../../config' import { LoginViaVerificationCode } from '../model/LoginViaVerificationCode' import { SendPasswordResetEmailResponse } from '../model/SendPasswordResetEmailResponse' @@ -25,8 +25,6 @@ import { CheckEmailResponse } from '../model/CheckEmailResponse' import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository' import { Setting } from '../enum/Setting' import { UserRepository } from '../../typeorm/repository/User' -import { LoginUserRepository } from '../../typeorm/repository/LoginUser' -import { LoginUserBackupRepository } from '../../typeorm/repository/LoginUserBackup' import { LoginUser } from '@entity/LoginUser' import { LoginUserBackup } from '@entity/LoginUserBackup' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' From 95265fe9898bcf59f5da4c4b96598110ef9414e7 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 6 Nov 2021 00:55:27 +0100 Subject: [PATCH 04/11] removed @types/libsodium-wrapper package --- backend/package.json | 1 - backend/yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/backend/package.json b/backend/package.json index e47939b41..bc098958f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -40,7 +40,6 @@ "devDependencies": { "@types/express": "^4.17.12", "@types/jsonwebtoken": "^8.5.2", - "@types/libsodium-wrappers": "^0.7.9", "@types/node": "^16.10.3", "@types/nodemailer": "^6.4.4", "@typescript-eslint/eslint-plugin": "^4.28.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 915766619..b411bcf60 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -918,11 +918,6 @@ "@types/koa-compose" "*" "@types/node" "*" -"@types/libsodium-wrappers@^0.7.9": - version "0.7.9" - resolved "https://registry.yarnpkg.com/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz#89c3ad2156d5143e64bce86cfeb0045a983aeccc" - integrity sha512-LisgKLlYQk19baQwjkBZZXdJL0KbeTpdEnrAfz5hQACbklCY0gVFnsKUyjfNWF1UQsCSjw93Sj5jSbiO8RPfdw== - "@types/long@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" From 4e118b65ee8389bca6083f19c2109d3eea414c1f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 6 Nov 2021 00:57:05 +0100 Subject: [PATCH 05/11] - make sendEMails external (utils) - replace fromHex from libsodium-wrapper --- .../graphql/resolver/TransactionResolver.ts | 28 ++----------------- backend/src/util/sendEMail.ts | 26 +++++++++++++++++ 2 files changed, 28 insertions(+), 26 deletions(-) create mode 100644 backend/src/util/sendEMail.ts diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 70dfdc505..afdd87156 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -4,9 +4,9 @@ import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql' import { getCustomRepository, getConnection, QueryRunner } from 'typeorm' -import { createTransport } from 'nodemailer' import CONFIG from '../../config' +import { sendEMail } from '../../util/sendEMail' import { Transaction } from '../model/Transaction' import { TransactionList } from '../model/TransactionList' @@ -33,7 +33,6 @@ import { calculateDecay, calculateDecayWithInterval } from '../../util/decay' import { TransactionTypeId } from '../enum/TransactionTypeId' import { TransactionType } from '../enum/TransactionType' import { hasUserAmount, isHexPublicKey } from '../../util/validate' -import { from_hex as fromHex } from 'libsodium-wrappers' /* # Test @@ -201,29 +200,6 @@ INSERT INTO `transaction_signatures` (`id`, `transaction_id`, `signature`, `pubk (1, 1, 0x60d632479707e5d01cdc32c3326b5a5bae11173a0c06b719ee7b552f9fd644de1a0cd4afc207253329081d39dac1a63421f51571d836995c649fc39afac7480a, 0x48c45cb4fea925e83850f68f2fa8f27a1a4ed1bcba68cdb59fcd86adef3f52ee); */ -const sendEMail = async (emailDef: any): Promise => { - if (!CONFIG.EMAIL) { - // eslint-disable-next-line no-console - console.log('Emails are disabled via config') - return false - } - const transporter = createTransport({ - host: CONFIG.EMAIL_SMTP_URL, - port: Number(CONFIG.EMAIL_SMTP_PORT), - secure: false, // true for 465, false for other ports - requireTLS: true, - auth: { - user: CONFIG.EMAIL_USERNAME, - pass: CONFIG.EMAIL_PASSWORD, - }, - }) - const info = await transporter.sendMail(emailDef) - if (!info.messageId) { - throw new Error('error sending notification email, but transaction succeed') - } - return true -} - // Helper function async function calculateAndAddDecayTransactions( userTransactions: dbUserTransaction[], @@ -622,7 +598,7 @@ export class TransactionResolver { transactionSendCoin.userId = senderUser.id transactionSendCoin.senderPublic = senderUser.pubkey transactionSendCoin.recipiantUserId = recipiantUser.id - transactionSendCoin.recipiantPublic = Buffer.from(fromHex(recipiantPublicKey)) + transactionSendCoin.recipiantPublic = Buffer.from(recipiantPublicKey, 'hex') transactionSendCoin.amount = centAmount transactionSendCoin.senderFinalBalance = senderStateBalance.amount await queryRunner.manager.save(transactionSendCoin).catch((error) => { diff --git a/backend/src/util/sendEMail.ts b/backend/src/util/sendEMail.ts new file mode 100644 index 000000000..e34597419 --- /dev/null +++ b/backend/src/util/sendEMail.ts @@ -0,0 +1,26 @@ +import { createTransport } from 'nodemailer' + +import CONFIG from '../config' + +export const sendEMail = async (emailDef: any): Promise => { + if (!CONFIG.EMAIL) { + // eslint-disable-next-line no-console + console.log('Emails are disabled via config') + return false + } + const transporter = createTransport({ + host: CONFIG.EMAIL_SMTP_URL, + port: Number(CONFIG.EMAIL_SMTP_PORT), + secure: false, // true for 465, false for other ports + requireTLS: true, + auth: { + user: CONFIG.EMAIL_USERNAME, + pass: CONFIG.EMAIL_PASSWORD, + }, + }) + const info = await transporter.sendMail(emailDef) + if (!info.messageId) { + throw new Error('error sending notification email, but transaction succeed') + } + return true +} From 9894b4a91ffd1d064029aa735ff70533b0f3b09e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 6 Nov 2021 01:56:30 +0100 Subject: [PATCH 06/11] fixed email for transactions --- backend/src/graphql/resolver/TransactionResolver.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index afdd87156..755955a7f 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -630,8 +630,8 @@ export class TransactionResolver { // send notification email // TODO: translate await sendEMail({ - from: 'Gradido (nicht antworten) <' + CONFIG.EMAIL_SENDER + '>', - to: recipiantUser.firstName + ' ' + recipiantUser.lastName + ' <' + recipiantUser.email + '>', + from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`, + to: `${recipiantUser.firstName} ${recipiantUser.lastName} <${recipiantUser.email}>`, subject: 'Gradido Überweisung', text: `Hallo ${recipiantUser.firstName} ${recipiantUser.lastName} @@ -642,7 +642,8 @@ export class TransactionResolver { Bitte antworte nicht auf diese E-Mail! - Mit freundlichen Grüßen Gradido Community Server`, + Mit freundlichen Grüßen, + dein Gradido-Team`, }) return 'success' From 0f89cf1c9688fe4caa51cec12b78a3628332942a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 6 Nov 2021 01:56:47 +0100 Subject: [PATCH 07/11] send emails to activate account --- backend/.env.dist | 2 ++ backend/src/config/index.ts | 3 ++ backend/src/graphql/resolver/UserResolver.ts | 33 +++++++++++++++++--- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/backend/.env.dist b/backend/.env.dist index 30c0da2db..b4a91026a 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -18,6 +18,8 @@ DB_DATABASE=gradido_community #EMAIL_SMTP_URL= #EMAIL_SMTP_PORT=587 +#EMAIL_LINK_VERIFICATION=http://localhost/vue/checkEmail/$1 + #KLICKTIPP_USER= #KLICKTIPP_PASSWORD= #KLICKTIPP_APIKEY_DE= diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 7e2059eac..f21082d1d 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -51,6 +51,9 @@ const email = { EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || 'xxx', 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/vue/checkEmail/$1', } // This is needed by graphql-directive-auth diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index c3c8e0f14..6a8eed67f 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -28,6 +28,7 @@ import { UserRepository } from '../../typeorm/repository/User' import { LoginUser } from '@entity/LoginUser' import { LoginUserBackup } from '@entity/LoginUserBackup' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' +import { sendEMail } from '../../util/sendEMail' // TODO apparently the types are cannot be loaded correctly? IDK whats wrong and we have to use require // import { @@ -378,7 +379,7 @@ export class UserResolver { // Store EmailOptIn in DB const emailOptIn = new LoginEmailOptIn() emailOptIn.userId = loginUserId - emailOptIn.verificationCode = random(64) // TODO generate verificationCode + emailOptIn.verificationCode = random(64) emailOptIn.emailOptInTypeId = 2 await queryRunner.manager.save(emailOptIn).catch((error) => { @@ -386,9 +387,33 @@ export class UserResolver { console.log('Error while saving emailOptIn', error) throw new Error('error saving email opt in') }) - // TODO: Send EmailOptIn to user.email - // emailOptIn->setBaseUrl(user->getGroupBaseUrl() + ServerConfig::g_frontend_checkEmailPath); - // em->addEmail(new model::Email(emailOptIn, user, model::Email::convertTypeFromInt(emailType))); + + // Send EMail to user + const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( + /\$1/g, + emailOptIn.verificationCode.toString(), + ) + const emailSent = await sendEMail({ + from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`, + to: `${firstName} ${lastName} <${email}>`, + subject: 'Gradido: E-Mail Überprüfung', + text: `Hallo ${firstName} ${lastName}, + + Deine EMail wurde soeben bei Gradido registriert. + + Klicke bitte auf diesen Link, um die Registrierung abzuschließen und dein Gradido-Konto zu aktivieren: + ${activationLink} + 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(`Account confirmation link: ${activationLink}`) + } await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() From fe9a8e1a7501ce18aa3b2a64a2920d25b40ed96b Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 6 Nov 2021 02:05:21 +0100 Subject: [PATCH 08/11] reduced coverage to 39 --- .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 08e63e6bb..9b10b7250 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -394,7 +394,7 @@ jobs: report_name: Coverage Backend type: lcov result_path: ./backend/coverage/lcov.info - min_coverage: 41 + min_coverage: 39 token: ${{ github.token }} ############################################################################## From 70fa4f49d0a7832ddf6e70f3367fa8689e81f116 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 6 Nov 2021 02:10:35 +0100 Subject: [PATCH 09/11] cleanup --- backend/src/graphql/resolver/UserResolver.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 6a8eed67f..f332a56bf 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -30,17 +30,6 @@ import { LoginUserBackup } from '@entity/LoginUserBackup' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { sendEMail } from '../../util/sendEMail' -// TODO apparently the types are cannot be loaded correctly? IDK whats wrong and we have to use require -// import { -// /* eslint-disable camelcase */ -// randombytes_random, -// crypto_hash_sha512_instance, -// crypto_hash_sha512_BYTES, -// crypto_sign_seed_keypair, -// crypto_sign_PUBLICKEYBYTES, -// crypto_sign_SECRETKEYBYTES, -// /* eslint-enable camelcase */ -// } from 'sodium-native' // eslint-disable-next-line @typescript-eslint/no-var-requires const sodium = require('sodium-native') // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -143,7 +132,6 @@ const KeyPairEd25519Create = (passphrase: string[]): Buffer[] => { } const SecretKeyCryptographyCreateKey = (salt: string, password: string): Buffer[] => { - // TODO: put that in the actual config const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex') const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex') if (configLoginServerKey.length !== sodium.crypto_shorthash_KEYBYTES) { @@ -287,8 +275,6 @@ export class UserResolver { async createUser( @Args() { email, firstName, lastName, password, language, publisherId }: CreateUserArgs, ): Promise { - const username = '' - // TODO: wrong default value (should be null), how does graphql work here? Is it an required field? // default int publisher_id = 0; @@ -306,6 +292,7 @@ export class UserResolver { // Validate username // TODO: never true + const username = '' if (username.length > 3 && !this.checkUsername({ username })) { throw new Error('Username already in use') } From d73728bf8695d4bb42b3791764be9ef07f8b272c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 6 Nov 2021 18:55:04 +0100 Subject: [PATCH 10/11] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: Hannes Heine --- backend/src/graphql/resolver/UserResolver.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index f332a56bf..c0ec4b932 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -584,21 +584,3 @@ export class UserResolver { return result.data.hasElopage } } - -/* -const rollbackAutoIncrement = async ( - queryRunner: QueryRunner, - entity: typeof BaseEntity, - entityName: string, -) => { - const count = await queryRunner.manager.count(entity) - const queryString = 'ALTER TABLE `' + entityName + '` auto_increment = ' + count - // eslint-disable-next-line no-console - console.log('Database AlterTable Query: ', queryString) - await queryRunner.query(queryString).catch((error) => { - // eslint-disable-next-line no-console - console.log('problems with reset auto increment: %o', error) - throw new Error('Problems with reset auto increment: ' + error) - }) -} -*/ From 348075ab386b858cd8a3634f95bc067d464279e5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 6 Nov 2021 18:55:16 +0100 Subject: [PATCH 11/11] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: Hannes Heine --- backend/src/graphql/resolver/UserResolver.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index c0ec4b932..7cc8cbf6d 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -404,11 +404,6 @@ export class UserResolver { await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() - // TODO: Lets not do this?! What if state_users were never updated? - // We would still roll back the autoincrement which would produce duplicate entries?! - // await rollbackAutoIncrement(queryRunner, LoginUser, `login_users`) - // await rollbackAutoIncrement(queryRunner, LoginUserBackup, `login_user_backups`) - // await rollbackAutoIncrement(queryRunner, DbUser, `state_users`) throw e } finally { await queryRunner.release()