From 4a51231bf9bd0782b2e165d209314caf321310a4 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 18 Feb 2022 12:44:35 +0100 Subject: [PATCH 1/6] admin resolver - use entity instead of repository - include deleted users in our query for pending transactions - throw an error when trying to add or confirm a pending transaction for a deleted user --- backend/src/graphql/resolver/AdminResolver.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index c314b5bb7..eed395926 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -23,6 +23,7 @@ import { calculateDecay } from '../../util/decay' import { AdminPendingCreation } from '@entity/AdminPendingCreation' import { hasElopageBuys } from '../../util/hasElopageBuys' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' +import { User } from '@entity/User' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? @@ -82,8 +83,10 @@ export class AdminResolver { async createPendingCreation( @Args() { email, amount, memo, creationDate, moderator }: CreatePendingCreationArgs, ): Promise { - const userRepository = getCustomRepository(UserRepository) - const user = await userRepository.findByEmail(email) + const user = await User.findOne({ email }) + if (!user) { + throw new Error(`Could not find user with email: ${email}`) + } if (!user.emailChecked) { throw new Error('Creation could not be saved, Email is not activated') } @@ -134,8 +137,13 @@ export class AdminResolver { async updatePendingCreation( @Args() { id, email, amount, memo, creationDate, moderator }: UpdatePendingCreationArgs, ): Promise { - const userRepository = getCustomRepository(UserRepository) - const user = await userRepository.findByEmail(email) + const user = await User.findOne({ email }, { withDeleted: true }) + if (!user) { + throw new Error(`Could not find user with email: ${email}`) + } + if (user.deletedAt) { + throw new Error(`User was deleted (${email})`) + } const pendingCreationToUpdate = await AdminPendingCreation.findOneOrFail({ id }) From 8eaed23af40f9e94fe5e3970198a1477105ccd11 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 18 Feb 2022 12:47:33 +0100 Subject: [PATCH 2/6] transactionResolver - remove unnecessary function - use of the enity model directly - corrected spelling - show specific error when sending to a deleted user --- .../graphql/resolver/TransactionResolver.ts | 41 +++++++------------ 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 622bf37fe..8d9e0c6c3 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -272,16 +272,6 @@ async function addUserTransaction( }) } -async function getPublicKey(email: string): Promise { - const user = await dbUser.findOne({ email: email }) - // User not found - if (!user) { - return null - } - - return user.pubKey.toString('hex') -} - @Resolver() export class TransactionResolver { @Authorized([RIGHTS.TRANSACTION_LIST]) @@ -301,7 +291,7 @@ export class TransactionResolver { const userRepository = getCustomRepository(UserRepository) let userEntity: dbUser | undefined if (userId) { - userEntity = await userRepository.findOneOrFail({ id: userId }) + userEntity = await userRepository.findOneOrFail({ id: userId }, { withDeleted: true }) } else { userEntity = await userRepository.findByPubkeyHex(context.pubKey) } @@ -357,18 +347,15 @@ export class TransactionResolver { // validate recipient user // TODO: the detour over the public key is unnecessary - const recipiantPublicKey = await getPublicKey(email) - if (!recipiantPublicKey) { + const recipientUser = await dbUser.findOne({ email: email }, { withDeleted: true }) + if (!recipientUser) { throw new Error('recipient not known') } - if (!isHexPublicKey(recipiantPublicKey)) { - throw new Error('invalid recipiant public key') + if (recipientUser.deletedAt) { + throw new Error('The recipient account was deleted') } - const recipiantUser = await userRepository.findByPubkeyHex(recipiantPublicKey) - if (!recipiantUser) { - throw new Error('Cannot find recipiant user by local send coins transaction') - } else if (recipiantUser.deletedAt) { - throw new Error('recipiant user account is disabled') + if (!isHexPublicKey(recipientUser.pubKey.toString('hex'))) { + throw new Error('invalid recipient public key') } // validate amount @@ -405,7 +392,7 @@ export class TransactionResolver { // Insert Transaction: recipient + amount const recipiantUserTransactionBalance = await addUserTransaction( - recipiantUser, + recipientUser, transaction, centAmount, queryRunner, @@ -421,7 +408,7 @@ export class TransactionResolver { // Update Balance: recipiant + amount const recipiantStateBalance = await updateStateBalance( - recipiantUser, + recipientUser, centAmount, transaction.received, queryRunner, @@ -439,8 +426,8 @@ export class TransactionResolver { transactionSendCoin.transactionId = transaction.id transactionSendCoin.userId = senderUser.id transactionSendCoin.senderPublic = senderUser.pubKey - transactionSendCoin.recipiantUserId = recipiantUser.id - transactionSendCoin.recipiantPublic = Buffer.from(recipiantPublicKey, 'hex') + transactionSendCoin.recipiantUserId = recipientUser.id + transactionSendCoin.recipiantPublic = recipientUser.pubKey transactionSendCoin.amount = centAmount transactionSendCoin.senderFinalBalance = senderStateBalance.amount await queryRunner.manager.save(transactionSendCoin).catch((error) => { @@ -474,9 +461,9 @@ export class TransactionResolver { await sendTransactionReceivedEmail({ senderFirstName: senderUser.firstName, senderLastName: senderUser.lastName, - recipientFirstName: recipiantUser.firstName, - recipientLastName: recipiantUser.lastName, - email: recipiantUser.email, + recipientFirstName: recipientUser.firstName, + recipientLastName: recipientUser.lastName, + email: recipientUser.email, amount, memo, }) From 90bdca04a669b60ff99da066441032df9669ea54 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 18 Feb 2022 12:49:05 +0100 Subject: [PATCH 3/6] UserResolver - on login have a specific message for deleted users - when trying to register also check for deleted users and prevent registration --- backend/src/graphql/resolver/UserResolver.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 5bc30f6b4..fdae2a14e 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -250,9 +250,12 @@ export class UserResolver { @Ctx() context: any, ): Promise { email = email.trim().toLowerCase() - const dbUser = await DbUser.findOneOrFail({ email }).catch(() => { + const dbUser = await DbUser.findOneOrFail({ email }, { withDeleted: true }).catch(() => { throw new Error('No user with this credentials') }) + if (dbUser.deletedAt) { + throw new Error('This user was permanently disabled. Contact support for questions.') + } if (!dbUser.emailChecked) { throw new Error('User email not validated') } @@ -335,9 +338,9 @@ export class UserResolver { // Validate email unique // TODO: i can register an email in upper/lower case twice - const userRepository = getCustomRepository(UserRepository) - const usersFound = await userRepository.count({ email }) - if (usersFound !== 0) { + // TODO we cannot use repository.count(), since it does not allow to specify if you want to include the soft deletes + const userFound = await DbUser.findOne({ email }, { withDeleted: true }) + if (userFound) { // TODO: this is unsecure, but the current implementation of the login server. This way it can be queried if the user with given EMail is existent. throw new Error(`User already exists.`) } From 18b7fd73d0d32ac30fb6791a89b77c0646c65204 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 18 Feb 2022 12:49:56 +0100 Subject: [PATCH 4/6] Admin: search for users include deleted remove unnecessary function --- backend/src/typeorm/repository/User.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/backend/src/typeorm/repository/User.ts b/backend/src/typeorm/repository/User.ts index 59d6ff465..3ac2fbe93 100644 --- a/backend/src/typeorm/repository/User.ts +++ b/backend/src/typeorm/repository/User.ts @@ -3,6 +3,7 @@ import { User } from '@entity/User' @EntityRepository(User) export class UserRepository extends Repository { + async findByPubkeyHex(pubkeyHex: string): Promise { return this.createQueryBuilder('user') .where('hex(user.pubKey) = :pubkeyHex', { pubkeyHex }) @@ -14,10 +15,6 @@ export class UserRepository extends Repository { return await this.findByPubkeyHex(pubKeyString) } - async findByEmail(email: string): Promise { - return this.createQueryBuilder('user').where('user.email = :email', { email }).getOneOrFail() - } - async getUsersIndiced(userIds: number[]): Promise { if (!userIds.length) return [] const users = await this.createQueryBuilder('user') @@ -33,6 +30,7 @@ export class UserRepository extends Repository { async findBySearchCriteria(searchCriteria: string): Promise { return await this.createQueryBuilder('user') + .withDeleted() .where( 'user.firstName like :name or user.lastName like :lastName or user.email like :email', { From ed09e998b915178cbc818ea26e603130869d818f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 18 Feb 2022 12:59:55 +0100 Subject: [PATCH 5/6] admin: have a specific error message when trying to add a pending creation on a deleted user --- backend/src/graphql/resolver/AdminResolver.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index eed395926..7430868b1 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -83,10 +83,13 @@ export class AdminResolver { async createPendingCreation( @Args() { email, amount, memo, creationDate, moderator }: CreatePendingCreationArgs, ): Promise { - const user = await User.findOne({ email }) + const user = await User.findOne({ email }, { withDeleted: true }) if (!user) { throw new Error(`Could not find user with email: ${email}`) } + if (user.deletedAt) { + throw new Error('This user was deleted. Cannot make a creation.') + } if (!user.emailChecked) { throw new Error('Creation could not be saved, Email is not activated') } From fc9829bc8489ea28587427ddda638349a7491d1f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 18 Feb 2022 13:06:28 +0100 Subject: [PATCH 6/6] lint fixes --- backend/src/typeorm/repository/User.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/typeorm/repository/User.ts b/backend/src/typeorm/repository/User.ts index 3ac2fbe93..88467fc95 100644 --- a/backend/src/typeorm/repository/User.ts +++ b/backend/src/typeorm/repository/User.ts @@ -3,7 +3,6 @@ import { User } from '@entity/User' @EntityRepository(User) export class UserRepository extends Repository { - async findByPubkeyHex(pubkeyHex: string): Promise { return this.createQueryBuilder('user') .where('hex(user.pubKey) = :pubkeyHex', { pubkeyHex })