From 235773c9b9fb53e14f8292826b7f2ab7f66e2896 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 6 Oct 2023 00:42:07 +0200 Subject: [PATCH 01/20] introduce foreign user in users table --- backend/src/config/index.ts | 2 +- backend/src/util/communityUser.ts | 10 +- .../User.ts | 133 ++++++++++++++++++ database/entity/User.ts | 2 +- ...3-introduce_foreign_user_in_users_table.ts | 21 +++ dht-node/src/config/index.ts | 2 +- federation/src/config/index.ts | 2 +- 7 files changed, 164 insertions(+), 8 deletions(-) create mode 100644 database/entity/0073-introduce_foreign_user_in_users_table/User.ts create mode 100644 database/migrations/0073-introduce_foreign_user_in_users_table.ts diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index a77840738..25e901491 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -12,7 +12,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0072-add_communityuuid_to_transactions_table', + DB_VERSION: '0073-introduce_foreign_user_in_users_table', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info diff --git a/backend/src/util/communityUser.ts b/backend/src/util/communityUser.ts index 422c836df..de41ea310 100644 --- a/backend/src/util/communityUser.ts +++ b/backend/src/util/communityUser.ts @@ -33,21 +33,23 @@ const communityDbUser: dbUser = { hasId: function (): boolean { throw new Error('Function not implemented.') }, - save: function (options?: SaveOptions): Promise { + save: function (_options?: SaveOptions): Promise { throw new Error('Function not implemented.') }, - remove: function (options?: RemoveOptions): Promise { + remove: function (_options?: RemoveOptions): Promise { throw new Error('Function not implemented.') }, - softRemove: function (options?: SaveOptions): Promise { + softRemove: function (_options?: SaveOptions): Promise { throw new Error('Function not implemented.') }, - recover: function (options?: SaveOptions): Promise { + recover: function (_options?: SaveOptions): Promise { throw new Error('Function not implemented.') }, reload: function (): Promise { throw new Error('Function not implemented.') }, + foreign: false, + communityUuid: null, } const communityUser = new User(communityDbUser) diff --git a/database/entity/0073-introduce_foreign_user_in_users_table/User.ts b/database/entity/0073-introduce_foreign_user_in_users_table/User.ts new file mode 100644 index 000000000..423039679 --- /dev/null +++ b/database/entity/0073-introduce_foreign_user_in_users_table/User.ts @@ -0,0 +1,133 @@ +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + DeleteDateColumn, + OneToMany, + JoinColumn, + OneToOne, +} from 'typeorm' +import { Community } from '../Community' +import { Contribution } from '../Contribution' +import { ContributionMessage } from '../ContributionMessage' +import { UserContact } from '../UserContact' +import { UserRole } from '../UserRole' + +@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) +export class User extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ type: 'bool', default: false }) + foreign: boolean + + @Column({ + name: 'gradido_id', + length: 36, + nullable: false, + collation: 'utf8mb4_unicode_ci', + }) + gradidoID: string + + @Column({ + name: 'community_uuid', + type: 'char', + length: 36, + nullable: true, + collation: 'utf8mb4_unicode_ci', + }) + communityUuid: string | null + + @Column({ + name: 'alias', + length: 20, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + alias: string + + @OneToOne(() => UserContact, (emailContact: UserContact) => emailContact.user) + @JoinColumn({ name: 'email_id' }) + emailContact: UserContact + + @Column({ name: 'email_id', type: 'int', unsigned: true, nullable: true, default: null }) + emailId: number | null + + @Column({ + name: 'first_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + firstName: string + + @Column({ + name: 'last_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + lastName: string + + @Column({ name: 'created_at', default: () => 'CURRENT_TIMESTAMP(3)', nullable: false }) + createdAt: Date + + @DeleteDateColumn({ name: 'deleted_at', nullable: true }) + deletedAt: Date | null + + @Column({ type: 'bigint', default: 0, unsigned: true }) + password: BigInt + + @Column({ + name: 'password_encryption_type', + type: 'int', + unsigned: true, + nullable: false, + default: 0, + }) + passwordEncryptionType: number + + @Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false }) + language: string + + @Column({ type: 'bool', default: false }) + hideAmountGDD: boolean + + @Column({ type: 'bool', default: false }) + hideAmountGDT: boolean + + @OneToMany(() => UserRole, (userRole) => userRole.user) + @JoinColumn({ name: 'user_id' }) + userRoles: UserRole[] + + @Column({ name: 'referrer_id', type: 'int', unsigned: true, nullable: true, default: null }) + referrerId?: number | null + + @Column({ + name: 'contribution_link_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + contributionLinkId?: number | null + + @Column({ name: 'publisher_id', default: 0 }) + publisherId: number + + @OneToMany(() => Contribution, (contribution) => contribution.user) + @JoinColumn({ name: 'user_id' }) + contributions?: Contribution[] + + @OneToMany(() => ContributionMessage, (message) => message.user) + @JoinColumn({ name: 'user_id' }) + messages?: ContributionMessage[] + + @OneToMany(() => UserContact, (userContact: UserContact) => userContact.user) + @JoinColumn({ name: 'user_id' }) + userContacts?: UserContact[] +} diff --git a/database/entity/User.ts b/database/entity/User.ts index eb66bdee9..21785ee9c 100644 --- a/database/entity/User.ts +++ b/database/entity/User.ts @@ -1 +1 @@ -export { User } from './0069-add_user_roles_table/User' +export { User } from './0073-introduce_foreign_user_in_users_table/User' diff --git a/database/migrations/0073-introduce_foreign_user_in_users_table.ts b/database/migrations/0073-introduce_foreign_user_in_users_table.ts new file mode 100644 index 000000000..f04c2f004 --- /dev/null +++ b/database/migrations/0073-introduce_foreign_user_in_users_table.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + 'ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `foreign` tinyint(4) NOT NULL DEFAULT 0 AFTER `id`;', + ) + + await queryFn( + 'ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `community_uuid` char(36) DEFAULT NULL NULL AFTER `gradido_id`;', + ) + await queryFn( + 'ALTER TABLE `users` ADD CONSTRAINT uuid_key UNIQUE KEY (`gradido_id`, `community_uuid`);', + ) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('ALTER TABLE `users` DROP CONSTRAINT IF EXISTS `uuid_key`;') + await queryFn('ALTER TABLE `users` DROP COLUMN IF EXISTS `foreign`;') + await queryFn('ALTER TABLE `users` DROP COLUMN IF EXISTS `community_uuid`;') +} diff --git a/dht-node/src/config/index.ts b/dht-node/src/config/index.ts index 78755f280..2d88e0f92 100644 --- a/dht-node/src/config/index.ts +++ b/dht-node/src/config/index.ts @@ -4,7 +4,7 @@ import dotenv from 'dotenv' dotenv.config() const constants = { - DB_VERSION: '0072-add_communityuuid_to_transactions_table', + DB_VERSION: '0073-introduce_foreign_user_in_users_table', LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info LOG_LEVEL: process.env.LOG_LEVEL || 'info', diff --git a/federation/src/config/index.ts b/federation/src/config/index.ts index e88267589..7cc9ef37e 100644 --- a/federation/src/config/index.ts +++ b/federation/src/config/index.ts @@ -10,7 +10,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0072-add_communityuuid_to_transactions_table', + DB_VERSION: '0073-introduce_foreign_user_in_users_table', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info From 6582cbcb39175b0e62890a21fb68367673153a41 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 6 Oct 2023 17:56:01 +0200 Subject: [PATCH 02/20] store foreign user after x-com-tx --- .../graphql/resolver/TransactionResolver.ts | 21 +++++ .../src/graphql/resolver/UserResolver.test.ts | 2 + .../graphql/resolver/util/storeForeignUser.ts | 77 +++++++++++++++++++ backend/src/util/communityUser.ts | 2 +- .../User.ts | 3 +- ...3-introduce_foreign_user_in_users_table.ts | 14 +++- 6 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 backend/src/graphql/resolver/util/storeForeignUser.ts diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 66a889798..8f78d1d94 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -47,6 +47,7 @@ import { processXComPendingSendCoins, } from './util/processXComSendCoins' import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector' +import { storeForeignUser } from './util/storeForeignUser' import { transactionLinkSummary } from './util/transactionLinkSummary' export const executeTransaction = async ( @@ -404,6 +405,7 @@ export class TransactionResolver { } else { // processing a x-community sendCoins logger.debug('X-Com: processing a x-community transaction...') + console.log('X-Com: processing a x-community transaction...') if (!CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED) { throw new LogError('X-Community sendCoins disabled per configuration!') } @@ -414,6 +416,7 @@ export class TransactionResolver { where: { communityUuid: recipientCommunityIdentifier }, }) logger.debug('recipient commuity: ', recipCom) + console.log('recipient commuity: ', recipCom) let pendingResult: SendCoinsResult let committingResult: SendCoinsResult const creationDate = new Date() @@ -429,8 +432,10 @@ export class TransactionResolver { recipientIdentifier, ) logger.debug('processXComPendingSendCoins result: ', pendingResult) + console.log('processXComPendingSendCoins result: ', pendingResult) if (pendingResult.vote && pendingResult.recipGradidoID) { logger.debug('vor processXComCommittingSendCoins... ') + console.log('vor processXComCommittingSendCoins... ') committingResult = await processXComCommittingSendCoins( recipCom, homeCom, @@ -441,6 +446,7 @@ export class TransactionResolver { pendingResult.recipGradidoID, ) logger.debug('processXComCommittingSendCoins result: ', committingResult) + console.log('processXComCommittingSendCoins result: ', committingResult) if (!committingResult.vote) { logger.fatal('FATAL ERROR: on processXComCommittingSendCoins for', committingResult) throw new LogError( @@ -451,6 +457,21 @@ export class TransactionResolver { memo, ) } + // after successful x-com-tx store the recipient as foreign user + logger.debug('store recipient as foreign user...') + console.log('store recipient as foreign user...') + if (await storeForeignUser(recipCom, committingResult)) { + logger.info( + 'X-Com: new foreign user inserted successfully...', + recipCom.communityUuid, + committingResult.recipGradidoID, + ) + console.log( + 'X-Com: new foreign user inserted successfully...', + recipCom.communityUuid, + committingResult.recipGradidoID, + ) + } } } catch (err) { throw new LogError( diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 171af7cf5..5ad05fc05 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -171,6 +171,8 @@ describe('UserResolver', () => { referrerId: null, contributionLinkId: null, passwordEncryptionType: PasswordEncryptionType.NO_PASSWORD, + communityUuid: null, + foreign: false, }, ]) const valUUID = validateUUID(user[0].gradidoID) diff --git a/backend/src/graphql/resolver/util/storeForeignUser.ts b/backend/src/graphql/resolver/util/storeForeignUser.ts new file mode 100644 index 000000000..7be58376b --- /dev/null +++ b/backend/src/graphql/resolver/util/storeForeignUser.ts @@ -0,0 +1,77 @@ +import { Community as DbCommunity } from '@entity/Community' +import { User as DbUser } from '@entity/User' + +import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult' +import { backendLogger as logger } from '@/server/logger' + +export async function storeForeignUser( + recipCom: DbCommunity, + committingResult: SendCoinsResult, +): Promise { + if (recipCom.communityUuid !== null && committingResult.recipGradidoID !== null) { + try { + const user = await DbUser.findOne({ + where: { + foreign: true, + communityUuid: recipCom.communityUuid, + gradidoID: committingResult.recipGradidoID, + }, + }) + if (!user) { + logger.debug( + 'X-Com: no foreignUser found for:', + recipCom.communityUuid, + committingResult.recipGradidoID, + ) + console.log( + 'X-Com: no foreignUser found for:', + recipCom.communityUuid, + committingResult.recipGradidoID, + ) + let foreignUser = DbUser.create() + foreignUser.foreign = true + if (committingResult.recipAlias !== null) { + foreignUser.alias = committingResult.recipAlias + } + foreignUser.communityUuid = recipCom.communityUuid + if (committingResult.recipFirstName !== null) { + foreignUser.firstName = committingResult.recipFirstName + } + if (committingResult.recipLastName !== null) { + foreignUser.lastName = committingResult.recipLastName + } + foreignUser.gradidoID = committingResult.recipGradidoID + foreignUser = await DbUser.save(foreignUser) + logger.debug('X-Com: new foreignUser inserted:', foreignUser) + console.log('X-Com: new foreignUser inserted:', foreignUser) + + return true + } else if ( + user.firstName !== committingResult.recipFirstName || + user.lastName !== committingResult.recipLastName || + user.alias !== committingResult.recipAlias + ) { + logger.warn( + 'X-Com: foreignUser still exists, but with different name or alias:', + user, + committingResult, + ) + console.log( + 'X-Com: foreignUser still exists, but with different name or alias:', + user, + committingResult, + ) + return false + } else { + logger.debug('X-Com: foreignUser still exists...:', user) + console.log('X-Com: foreignUser still exists...:', user) + return true + } + } catch (err) { + logger.error('X-Com: error in storeForeignUser;', err) + console.log('X-Com: error in storeForeignUser;', err) + return false + } + } + return false +} diff --git a/backend/src/util/communityUser.ts b/backend/src/util/communityUser.ts index de41ea310..bad06f201 100644 --- a/backend/src/util/communityUser.ts +++ b/backend/src/util/communityUser.ts @@ -49,7 +49,7 @@ const communityDbUser: dbUser = { throw new Error('Function not implemented.') }, foreign: false, - communityUuid: null, + communityUuid: '55555555-4444-4333-2222-11111111', } const communityUser = new User(communityDbUser) diff --git a/database/entity/0073-introduce_foreign_user_in_users_table/User.ts b/database/entity/0073-introduce_foreign_user_in_users_table/User.ts index 423039679..e842d8092 100644 --- a/database/entity/0073-introduce_foreign_user_in_users_table/User.ts +++ b/database/entity/0073-introduce_foreign_user_in_users_table/User.ts @@ -8,7 +8,6 @@ import { JoinColumn, OneToOne, } from 'typeorm' -import { Community } from '../Community' import { Contribution } from '../Contribution' import { ContributionMessage } from '../ContributionMessage' import { UserContact } from '../UserContact' @@ -37,7 +36,7 @@ export class User extends BaseEntity { nullable: true, collation: 'utf8mb4_unicode_ci', }) - communityUuid: string | null + communityUuid: string @Column({ name: 'alias', diff --git a/database/migrations/0073-introduce_foreign_user_in_users_table.ts b/database/migrations/0073-introduce_foreign_user_in_users_table.ts index f04c2f004..462dedebc 100644 --- a/database/migrations/0073-introduce_foreign_user_in_users_table.ts +++ b/database/migrations/0073-introduce_foreign_user_in_users_table.ts @@ -2,6 +2,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('ALTER TABLE `users` DROP KEY IF EXISTS `gradido_id`;') + await queryFn('ALTER TABLE `users` DROP INDEX IF EXISTS `gradido_id`;') + await queryFn('ALTER TABLE `users` DROP KEY IF EXISTS `alias`;') + await queryFn('ALTER TABLE `users` DROP INDEX IF EXISTS `alias`;') await queryFn( 'ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `foreign` tinyint(4) NOT NULL DEFAULT 0 AFTER `id`;', ) @@ -12,10 +16,18 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis await queryFn( 'ALTER TABLE `users` ADD CONSTRAINT uuid_key UNIQUE KEY (`gradido_id`, `community_uuid`);', ) + await queryFn( + 'ALTER TABLE `users` ADD CONSTRAINT alias_key UNIQUE KEY (`alias`, `community_uuid`);', + ) } export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { - await queryFn('ALTER TABLE `users` DROP CONSTRAINT IF EXISTS `uuid_key`;') + await queryFn('ALTER TABLE `users` DROP KEY IF EXISTS `uuid_key`;') + await queryFn('ALTER TABLE `users` DROP KEY IF EXISTS `alias_key`;') + await queryFn('ALTER TABLE `users` DROP COLUMN IF EXISTS `foreign`;') await queryFn('ALTER TABLE `users` DROP COLUMN IF EXISTS `community_uuid`;') + + await queryFn('ALTER TABLE `users` ADD CONSTRAINT gradido_id UNIQUE KEY (`gradido_id`);') + await queryFn('ALTER TABLE `users` ADD CONSTRAINT alias UNIQUE KEY (`alias`);') } From 6f5e5175b2caecd053697de3decaa61163334e7c Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 6 Oct 2023 23:16:50 +0200 Subject: [PATCH 03/20] return dbRemoteUser in transactions list --- backend/src/graphql/model/User.ts | 8 ++++ .../graphql/resolver/TransactionResolver.ts | 38 +++++++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts index 3474c6901..20a41dd26 100644 --- a/backend/src/graphql/model/User.ts +++ b/backend/src/graphql/model/User.ts @@ -8,6 +8,8 @@ export class User { constructor(user: dbUser | null) { if (user) { this.id = user.id + this.foreign = user.foreign + this.communityUuid = user.communityUuid this.gradidoID = user.gradidoID this.alias = user.alias if (user.emailContact) { @@ -30,6 +32,12 @@ export class User { @Field(() => Int) id: number + @Field(() => Boolean) + foreign: boolean + + @Field(() => String) + communityUuid: string + @Field(() => String) gradidoID: string diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 8f78d1d94..a9ac7c79c 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -251,18 +251,40 @@ export class TransactionResolver { // find involved users; I am involved const involvedUserIds: number[] = [user.id] const involvedRemoteUsers: User[] = [] - userTransactions.forEach((transaction: dbTransaction) => { + // userTransactions.forEach((transaction: dbTransaction) => { + // use normal for loop because of timing problems with await in forEach-loop + for (const transaction of userTransactions) { if (transaction.linkedUserId && !involvedUserIds.includes(transaction.linkedUserId)) { involvedUserIds.push(transaction.linkedUserId) } if (!transaction.linkedUserId && transaction.linkedUserGradidoID) { - const remoteUser = new User(null) - remoteUser.gradidoID = transaction.linkedUserGradidoID - remoteUser.firstName = transaction.linkedUserName - remoteUser.lastName = '(GradidoID: ' + transaction.linkedUserGradidoID + ')' + logger.debug( + 'search for remoteUser...', + transaction.linkedUserCommunityUuid, + transaction.linkedUserGradidoID, + ) + const dbRemoteUser = await dbUser.findOne({ + where: [ + { + foreign: true, + communityUuid: transaction.linkedUserCommunityUuid ?? undefined, + gradidoID: transaction.linkedUserGradidoID, + }, + ], + }) + logger.debug('found dbRemoteUser:', dbRemoteUser) + const remoteUser = new User(dbRemoteUser) + if (dbRemoteUser === null) { + if (transaction.linkedUserCommunityUuid !== null) { + remoteUser.communityUuid = transaction.linkedUserCommunityUuid + } + remoteUser.gradidoID = transaction.linkedUserGradidoID + remoteUser.firstName = transaction.linkedUserName + remoteUser.lastName = 'GradidoID: ' + transaction.linkedUserGradidoID + } involvedRemoteUsers.push(remoteUser) } - }) + } logger.debug(`involvedUserIds=`, involvedUserIds) logger.debug(`involvedRemoteUsers=`, involvedRemoteUsers) @@ -337,7 +359,7 @@ export class TransactionResolver { (userTransactions.length && userTransactions[0].balance) || new Decimal(0), ), ) - logger.debug(`transactions=${transactions}`) + logger.debug(`transactions=`, transactions) } } @@ -364,7 +386,7 @@ export class TransactionResolver { } transactions.push(new Transaction(userTransaction, self, linkedUser)) }) - logger.debug(`TransactionTypeId.CREATION: transactions=${transactions}`) + logger.debug(`TransactionTypeId.CREATION: transactions=`, transactions) transactions.forEach((transaction: Transaction) => { if (transaction.typeId !== TransactionTypeId.DECAY) { From 9baa94e14864d09735ad9ddb2256c96d2e290f78 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Mon, 9 Oct 2023 15:46:02 +0200 Subject: [PATCH 04/20] extent query users --- backend/src/graphql/resolver/UserResolver.ts | 7 ++++-- .../resolver/util/findUserByIdentifier.ts | 25 +++++++++++++++---- backend/src/seeds/graphql/queries.ts | 8 ++++-- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 6408b9dda..7b5bcdd91 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -809,8 +809,11 @@ export class UserResolver { @Authorized([RIGHTS.USER]) @Query(() => User) - async user(@Arg('identifier') identifier: string): Promise { - return new User(await findUserByIdentifier(identifier)) + async user( + @Arg('identifier') identifier: string, + @Arg('communityIdentifier') communityIdentifier?: string, + ): Promise { + return new User(await findUserByIdentifier(identifier, communityIdentifier)) } } diff --git a/backend/src/graphql/resolver/util/findUserByIdentifier.ts b/backend/src/graphql/resolver/util/findUserByIdentifier.ts index 96c9eb458..0118dd865 100644 --- a/backend/src/graphql/resolver/util/findUserByIdentifier.ts +++ b/backend/src/graphql/resolver/util/findUserByIdentifier.ts @@ -6,12 +6,18 @@ import { LogError } from '@/server/LogError' import { VALID_ALIAS_REGEX } from './validateAlias' -export const findUserByIdentifier = async (identifier: string): Promise => { +export const findUserByIdentifier = async ( + identifier: string, + communityIdentifier?: string, +): Promise => { let user: DbUser | null if (validate(identifier) && version(identifier) === 4) { - user = await DbUser.findOne({ where: { gradidoID: identifier }, relations: ['emailContact'] }) + user = await DbUser.findOne({ + where: { gradidoID: identifier, communityUuid: communityIdentifier }, + relations: ['emailContact'], + }) if (!user) { - throw new LogError('No user found to given identifier', identifier) + throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier) } } else if (/^.{2,}@.{2,}\..{2,}$/.exec(identifier)) { const userContact = await DbUserContact.findOne({ @@ -27,12 +33,21 @@ export const findUserByIdentifier = async (identifier: string): Promise if (!userContact.user) { throw new LogError('No user to given contact', identifier) } + if (userContact.user.communityUuid !== communityIdentifier) { + throw new LogError( + 'Found user to given contact, but belongs to foreign community', + identifier, + ) + } user = userContact.user user.emailContact = userContact } else if (VALID_ALIAS_REGEX.exec(identifier)) { - user = await DbUser.findOne({ where: { alias: identifier }, relations: ['emailContact'] }) + user = await DbUser.findOne({ + where: { alias: identifier, communityUuid: communityIdentifier }, + relations: ['emailContact'], + }) if (!user) { - throw new LogError('No user found to given identifier', identifier) + throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier) } } else { throw new LogError('Unknown identifier type', identifier) diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index b7ef87aa8..bb665c4c7 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -370,10 +370,14 @@ export const adminListContributionMessages = gql` ` export const user = gql` - query ($identifier: String!) { - user(identifier: $identifier) { + query ($identifier: String!, $communityIdentifier?: String) { + user(identifier: $identifier, $communityIdentifier: $communityIdentifier) { firstName lastName + foreign + communityUuid + gradidoID + alias } } ` From 4f3bb51b02e3710e5133cdfb61f8d663068738cd Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 10 Oct 2023 23:49:30 +0200 Subject: [PATCH 05/20] migrate home-communityUuid for local users --- .../0073-introduce_foreign_user_in_users_table.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/database/migrations/0073-introduce_foreign_user_in_users_table.ts b/database/migrations/0073-introduce_foreign_user_in_users_table.ts index 462dedebc..96bc07335 100644 --- a/database/migrations/0073-introduce_foreign_user_in_users_table.ts +++ b/database/migrations/0073-introduce_foreign_user_in_users_table.ts @@ -19,6 +19,14 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis await queryFn( 'ALTER TABLE `users` ADD CONSTRAINT alias_key UNIQUE KEY (`alias`, `community_uuid`);', ) + // read the community uuid of the homeCommunity + const result = await queryFn(`SELECT c.community_uuid from communities as c WHERE c.foreign = 0`) + // and if uuid exists enter the home_community_uuid for all local users + if (result && result[0]) { + await queryFn( + `UPDATE users as u SET u.community_uuid = "${result[0].community_uuid}" WHERE u.foreign = 0 AND u.community_uuid IS NULL`, + ) + } } export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { From d64603268efa24d0ef395cee0369f3d45865659b Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 10 Oct 2023 23:52:11 +0200 Subject: [PATCH 06/20] show alias or username plus foreign-communityName in Tx-Lists --- backend/src/graphql/arg/UserArgs.ts | 13 +++++++++ backend/src/graphql/model/User.ts | 3 +++ .../graphql/resolver/TransactionResolver.ts | 27 +++++++++---------- backend/src/graphql/resolver/UserResolver.ts | 15 ++++++++--- .../src/graphql/resolver/util/communities.ts | 6 +++++ backend/src/seeds/graphql/queries.ts | 2 +- .../src/components/TransactionRows/Name.vue | 13 +++++++-- frontend/src/graphql/queries.js | 4 +++ 8 files changed, 63 insertions(+), 20 deletions(-) create mode 100644 backend/src/graphql/arg/UserArgs.ts diff --git a/backend/src/graphql/arg/UserArgs.ts b/backend/src/graphql/arg/UserArgs.ts new file mode 100644 index 000000000..633ed5e20 --- /dev/null +++ b/backend/src/graphql/arg/UserArgs.ts @@ -0,0 +1,13 @@ +import { IsString } from 'class-validator' +import { ArgsType, Field } from 'type-graphql' + +@ArgsType() +export class UserArgs { + @Field({ nullable: false }) + @IsString() + identifier: string + + @Field({ nullable: true }) + @IsString() + communityIdentifier?: string +} diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts index 20a41dd26..cd188f49f 100644 --- a/backend/src/graphql/model/User.ts +++ b/backend/src/graphql/model/User.ts @@ -38,6 +38,9 @@ export class User { @Field(() => String) communityUuid: string + @Field(() => String, { nullable: true }) + communityName: string | null + @Field(() => String) gradidoID: string diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index a9ac7c79c..1c8761e7f 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -38,7 +38,7 @@ import { calculateBalance } from '@/util/validate' import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions' import { BalanceResolver } from './BalanceResolver' -import { isCommunityAuthenticated, isHomeCommunity } from './util/communities' +import { getCommunityName, isCommunityAuthenticated, isHomeCommunity } from './util/communities' import { findUserByIdentifier } from './util/findUserByIdentifier' import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' @@ -275,13 +275,23 @@ export class TransactionResolver { logger.debug('found dbRemoteUser:', dbRemoteUser) const remoteUser = new User(dbRemoteUser) if (dbRemoteUser === null) { + logger.debug('no dbRemoteUser found, init from tx:', transaction) if (transaction.linkedUserCommunityUuid !== null) { remoteUser.communityUuid = transaction.linkedUserCommunityUuid } remoteUser.gradidoID = transaction.linkedUserGradidoID - remoteUser.firstName = transaction.linkedUserName - remoteUser.lastName = 'GradidoID: ' + transaction.linkedUserGradidoID + if (transaction.linkedUserName) { + remoteUser.firstName = transaction.linkedUserName.slice( + 0, + transaction.linkedUserName.indexOf(' '), + ) + remoteUser.lastName = transaction.linkedUserName?.slice( + transaction.linkedUserName.indexOf(' '), + transaction.linkedUserName.length, + ) + } } + remoteUser.communityName = await getCommunityName(remoteUser.communityUuid) involvedRemoteUsers.push(remoteUser) } } @@ -427,7 +437,6 @@ export class TransactionResolver { } else { // processing a x-community sendCoins logger.debug('X-Com: processing a x-community transaction...') - console.log('X-Com: processing a x-community transaction...') if (!CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED) { throw new LogError('X-Community sendCoins disabled per configuration!') } @@ -438,7 +447,6 @@ export class TransactionResolver { where: { communityUuid: recipientCommunityIdentifier }, }) logger.debug('recipient commuity: ', recipCom) - console.log('recipient commuity: ', recipCom) let pendingResult: SendCoinsResult let committingResult: SendCoinsResult const creationDate = new Date() @@ -454,10 +462,8 @@ export class TransactionResolver { recipientIdentifier, ) logger.debug('processXComPendingSendCoins result: ', pendingResult) - console.log('processXComPendingSendCoins result: ', pendingResult) if (pendingResult.vote && pendingResult.recipGradidoID) { logger.debug('vor processXComCommittingSendCoins... ') - console.log('vor processXComCommittingSendCoins... ') committingResult = await processXComCommittingSendCoins( recipCom, homeCom, @@ -468,7 +474,6 @@ export class TransactionResolver { pendingResult.recipGradidoID, ) logger.debug('processXComCommittingSendCoins result: ', committingResult) - console.log('processXComCommittingSendCoins result: ', committingResult) if (!committingResult.vote) { logger.fatal('FATAL ERROR: on processXComCommittingSendCoins for', committingResult) throw new LogError( @@ -481,18 +486,12 @@ export class TransactionResolver { } // after successful x-com-tx store the recipient as foreign user logger.debug('store recipient as foreign user...') - console.log('store recipient as foreign user...') if (await storeForeignUser(recipCom, committingResult)) { logger.info( 'X-Com: new foreign user inserted successfully...', recipCom.communityUuid, committingResult.recipGradidoID, ) - console.log( - 'X-Com: new foreign user inserted successfully...', - recipCom.communityUuid, - committingResult.recipGradidoID, - ) } } } catch (err) { diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 7b5bcdd91..665340e63 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -12,6 +12,7 @@ import i18n from 'i18n' import { Resolver, Query, Args, Arg, Authorized, Ctx, Mutation, Int } from 'type-graphql' import { v4 as uuidv4 } from 'uuid' +import { UserArgs } from '@arg//UserArgs' import { CreateUserArgs } from '@arg/CreateUserArgs' import { Paginated } from '@arg/Paginated' import { SearchUsersFilters } from '@arg/SearchUsersFilters' @@ -64,6 +65,7 @@ import random from 'random-bigint' import { randombytes_random } from 'sodium-native' import { FULL_CREATION_AVAILABLE } from './const/const' +import { getCommunityName, getHomeCommunity } from './util/communities' import { getUserCreations } from './util/creations' import { findUserByIdentifier } from './util/findUserByIdentifier' import { findUsers } from './util/findUsers' @@ -810,10 +812,17 @@ export class UserResolver { @Authorized([RIGHTS.USER]) @Query(() => User) async user( - @Arg('identifier') identifier: string, - @Arg('communityIdentifier') communityIdentifier?: string, + @Args() + { identifier, communityIdentifier }: UserArgs, ): Promise { - return new User(await findUserByIdentifier(identifier, communityIdentifier)) + const foundDbUser = await findUserByIdentifier(identifier, communityIdentifier) + const modelUser = new User(foundDbUser) + if (!foundDbUser.communityUuid) { + modelUser.communityName = (await Promise.resolve(getHomeCommunity())).name + } else { + modelUser.communityName = await getCommunityName(foundDbUser.communityUuid) + } + return modelUser } } diff --git a/backend/src/graphql/resolver/util/communities.ts b/backend/src/graphql/resolver/util/communities.ts index 52fee86b2..29a55fca3 100644 --- a/backend/src/graphql/resolver/util/communities.ts +++ b/backend/src/graphql/resolver/util/communities.ts @@ -15,6 +15,12 @@ export async function isHomeCommunity(communityIdentifier: string): Promise { + return await DbCommunity.findOneOrFail({ + where: [{ foreign: true }], + }) +} + export async function getCommunityUrl(communityIdentifier: string): Promise { const community = await DbCommunity.findOneOrFail({ where: [ diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index bb665c4c7..27d5b5d1e 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -370,7 +370,7 @@ export const adminListContributionMessages = gql` ` export const user = gql` - query ($identifier: String!, $communityIdentifier?: String) { + query ($identifier: String!, $communityIdentifier: String) { user(identifier: $identifier, $communityIdentifier: $communityIdentifier) { firstName lastName diff --git a/frontend/src/components/TransactionRows/Name.vue b/frontend/src/components/TransactionRows/Name.vue index b4ddfc849..b2e0830fc 100644 --- a/frontend/src/components/TransactionRows/Name.vue +++ b/frontend/src/components/TransactionRows/Name.vue @@ -36,13 +36,22 @@ export default { methods: { async tunnelEmail() { if (this.$route.path !== '/send') await this.$router.push({ path: '/send' }) - this.$router.push({ query: { gradidoID: this.linkedUser.gradidoID } }) + this.$router.push({ + query: { + gradidoID: this.linkedUser.gradidoID, + communityUuid: this.linkedUser.communityUuid, + }, + }) }, }, computed: { itemText() { return this.linkedUser - ? this.linkedUser.firstName + ' ' + this.linkedUser.lastName + ? this.linkedUser.alias + ? this.linkedUser.alias + + (this.linkedUser.communityName ? ' / ' + this.linkedUser.communityName : '') + : this.linkedUser.firstName + ' ' + this.linkedUser.lastName + + (this.linkedUser.communityName ? ' / ' + this.linkedUser.communityName : '') : this.text }, }, diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 6ef5d56d6..584f9d34b 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -40,7 +40,10 @@ export const transactionsQuery = gql` linkedUser { firstName lastName + communityUuid + communityName gradidoID + alias } decay { decay @@ -281,6 +284,7 @@ export const user = gql` user(identifier: $identifier) { firstName lastName + communityName } } ` From 1940b7b604eadcd3e30cb9f656551b6d793b7ae8 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 12 Oct 2023 00:26:22 +0200 Subject: [PATCH 07/20] adapt communityUuid treatment --- backend/src/graphql/arg/TransactionSendArgs.ts | 4 ++-- backend/src/graphql/resolver/TransactionResolver.ts | 9 +++++++-- .../src/graphql/resolver/util/findUserByIdentifier.ts | 3 ++- frontend/src/components/GddSend/TransactionForm.vue | 1 + 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/backend/src/graphql/arg/TransactionSendArgs.ts b/backend/src/graphql/arg/TransactionSendArgs.ts index 48827be8d..5bd8b89f7 100644 --- a/backend/src/graphql/arg/TransactionSendArgs.ts +++ b/backend/src/graphql/arg/TransactionSendArgs.ts @@ -7,9 +7,9 @@ import { IsPositiveDecimal } from '@/graphql/validator/Decimal' @ArgsType() export class TransactionSendArgs { - @Field(() => String, { nullable: true }) + @Field(() => String) @IsString() - recipientCommunityIdentifier?: string | null | undefined + recipientCommunityIdentifier: string @Field(() => String) @IsString() diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 1c8761e7f..267f90596 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -426,11 +426,16 @@ export class TransactionResolver { if (!recipientCommunityIdentifier || (await isHomeCommunity(recipientCommunityIdentifier))) { // processing sendCoins within sender and recepient are both in home community - // validate recipient user - const recipientUser = await findUserByIdentifier(recipientIdentifier) + const recipientUser = await findUserByIdentifier( + recipientIdentifier, + recipientCommunityIdentifier, + ) if (!recipientUser) { throw new LogError('The recipient user was not found', recipientUser) } + if (recipientUser.foreign) { + throw new LogError('Found foreign recipient user for a local transaction:', recipientUser) + } await executeTransaction(amount, memo, senderUser, recipientUser) logger.info('successful executeTransaction', amount, memo, senderUser, recipientUser) diff --git a/backend/src/graphql/resolver/util/findUserByIdentifier.ts b/backend/src/graphql/resolver/util/findUserByIdentifier.ts index 0118dd865..6b7f1bccc 100644 --- a/backend/src/graphql/resolver/util/findUserByIdentifier.ts +++ b/backend/src/graphql/resolver/util/findUserByIdentifier.ts @@ -35,8 +35,9 @@ export const findUserByIdentifier = async ( } if (userContact.user.communityUuid !== communityIdentifier) { throw new LogError( - 'Found user to given contact, but belongs to foreign community', + 'Found user to given contact, but belongs to other community', identifier, + communityIdentifier, ) } user = userContact.user diff --git a/frontend/src/components/GddSend/TransactionForm.vue b/frontend/src/components/GddSend/TransactionForm.vue index db1bf0d1a..af1ab29c9 100644 --- a/frontend/src/components/GddSend/TransactionForm.vue +++ b/frontend/src/components/GddSend/TransactionForm.vue @@ -235,6 +235,7 @@ export default { update({ user, community }) { this.userName = `${user.firstName} ${user.lastName}` this.recipientCommunity.name = community.name + this.recipientCommunity.uuid = this.communityUuid }, error({ message }) { this.toastError(message) From 312838f2d9263e69e0211858466d7a6783f42cc0 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 13 Oct 2023 03:45:28 +0200 Subject: [PATCH 08/20] write sender as foreign user on recipient side --- .../client/1_0/model/SendCoinsArgs.ts | 3 + .../resolver/util/processXComSendCoins.ts | 2 + .../graphql/api/1_0/model/SendCoinsArgs.ts | 3 + .../api/1_0/resolver/SendCoinsResolver.ts | 11 +++ .../graphql/api/1_0/util/storeForeignUser.ts | 75 +++++++++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 federation/src/graphql/api/1_0/util/storeForeignUser.ts diff --git a/backend/src/federation/client/1_0/model/SendCoinsArgs.ts b/backend/src/federation/client/1_0/model/SendCoinsArgs.ts index fb97da925..73e166308 100644 --- a/backend/src/federation/client/1_0/model/SendCoinsArgs.ts +++ b/backend/src/federation/client/1_0/model/SendCoinsArgs.ts @@ -26,4 +26,7 @@ export class SendCoinsArgs { @Field(() => String) senderUserName: string + + @Field(() => String) + senderAlias: string } diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index af6826bd1..1afa7a1c0 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -85,6 +85,7 @@ export async function processXComPendingSendCoins( } args.senderUserUuid = sender.gradidoID args.senderUserName = fullName(sender.firstName, sender.lastName) + args.senderAlias = sender.alias logger.debug(`X-Com: ready for voteForSendCoins with args=`, args) voteResult = await client.voteForSendCoins(args) logger.debug(`X-Com: returned from voteForSendCoins:`, voteResult) @@ -212,6 +213,7 @@ export async function processXComCommittingSendCoins( if (pendingTx.userName) { args.senderUserName = pendingTx.userName } + args.senderAlias = sender.alias logger.debug(`X-Com: ready for settleSendCoins with args=`, args) const acknowledge = await client.settleSendCoins(args) logger.debug(`X-Com: returnd from settleSendCoins:`, acknowledge) diff --git a/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts b/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts index e658209ca..1337bbc6c 100644 --- a/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts +++ b/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts @@ -26,4 +26,7 @@ export class SendCoinsArgs { @Field(() => String) senderUserName: string + + @Field(() => String) + senderAlias: string } diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index ef37c0d00..a05ab2c7d 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -15,6 +15,7 @@ import { revertSettledReceiveTransaction } from '../util/revertSettledReceiveTra import { findUserByIdentifier } from '@/graphql/util/findUserByIdentifier' import { SendCoinsResult } from '../model/SendCoinsResult' import Decimal from 'decimal.js-light' +import { storeForeignUser } from '../util/storeForeignUser' @Resolver() // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -236,6 +237,16 @@ export class SendCoinsResolver { logger.debug('XCom: settleSendCoins matching pendingTX for settlement...') await settlePendingReceiveTransaction(homeCom, receiverUser, pendingTx) + // after successful x-com-tx store the recipient as foreign user + logger.debug('store recipient as foreign user...') + if (await storeForeignUser(args)) { + logger.info( + 'X-Com: new foreign user inserted successfully...', + args.senderCommunityUuid, + args.senderUserUuid, + ) + } + logger.debug(`XCom: settlePendingReceiveTransaction()-1_0... successfull`) return true } else { diff --git a/federation/src/graphql/api/1_0/util/storeForeignUser.ts b/federation/src/graphql/api/1_0/util/storeForeignUser.ts new file mode 100644 index 000000000..d8f6e3748 --- /dev/null +++ b/federation/src/graphql/api/1_0/util/storeForeignUser.ts @@ -0,0 +1,75 @@ +import { User as DbUser } from '@entity/User' + +import { federationLogger as logger } from '@/server/logger' +import { SendCoinsArgs } from '../model/SendCoinsArgs' + +export async function storeForeignUser(args: SendCoinsArgs): Promise { + if (args.senderCommunityUuid !== null && args.senderUserUuid !== null) { + try { + const user = await DbUser.findOne({ + where: { + foreign: true, + communityUuid: args.senderCommunityUuid, + gradidoID: args.senderUserUuid, + }, + }) + if (!user) { + logger.debug( + 'X-Com: no foreignUser found for:', + args.senderCommunityUuid, + args.senderUserUuid, + ) + console.log( + 'X-Com: no foreignUser found for:', + args.senderCommunityUuid, + args.senderUserUuid, + ) + let foreignUser = DbUser.create() + foreignUser.foreign = true + if (args.senderAlias !== null) { + foreignUser.alias = args.senderAlias + } + foreignUser.communityUuid = args.senderCommunityUuid + if (args.senderUserName !== null) { + foreignUser.firstName = args.senderUserName.slice(0, args.senderUserName.indexOf(' ')) + foreignUser.firstName = args.senderUserName.slice( + args.senderUserName.indexOf(' '), + args.senderUserName.length, + ) + } + foreignUser.gradidoID = args.senderUserUuid + foreignUser = await DbUser.save(foreignUser) + logger.debug('X-Com: new foreignUser inserted:', foreignUser) + console.log('X-Com: new foreignUser inserted:', foreignUser) + + return true + } else if ( + user.firstName !== args.senderUserName.slice(0, args.senderUserName.indexOf(' ')) || + user.lastName !== + args.senderUserName.slice(args.senderUserName.indexOf(' '), args.senderUserName.length) || + user.alias !== args.senderAlias + ) { + logger.warn( + 'X-Com: foreignUser still exists, but with different name or alias:', + user, + args, + ) + console.log( + 'X-Com: foreignUser still exists, but with different name or alias:', + user, + args, + ) + return false + } else { + logger.debug('X-Com: foreignUser still exists...:', user) + console.log('X-Com: foreignUser still exists...:', user) + return true + } + } catch (err) { + logger.error('X-Com: error in storeForeignUser;', err) + console.log('X-Com: error in storeForeignUser;', err) + return false + } + } + return false +} From 5dd0a875833aa7c9519d908f3501ba19d7f018ab Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 13 Oct 2023 12:05:04 +0200 Subject: [PATCH 09/20] adapt federation tests on previous changes --- .../api/1_0/resolver/SendCoinsResolver.test.ts | 15 +++++++++++++++ .../src/graphql/api/1_0/util/storeForeignUser.ts | 13 ------------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts index 412786fa6..2ab2cc8b3 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts @@ -134,6 +134,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias expect( await mutate({ mutation: voteForSendCoinsMutation, @@ -163,6 +164,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias expect( await mutate({ mutation: voteForSendCoinsMutation, @@ -196,6 +198,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias expect( await mutate({ mutation: voteForSendCoinsMutation, @@ -235,6 +238,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias await mutate({ mutation: voteForSendCoinsMutation, variables: { args }, @@ -255,6 +259,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias expect( await mutate({ mutation: revertSendCoinsMutation, @@ -284,6 +289,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias expect( await mutate({ mutation: revertSendCoinsMutation, @@ -317,6 +323,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias expect( await mutate({ mutation: revertSendCoinsMutation, @@ -350,6 +357,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias await mutate({ mutation: voteForSendCoinsMutation, variables: { args }, @@ -370,6 +378,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias expect( await mutate({ mutation: settleSendCoinsMutation, @@ -399,6 +408,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias expect( await mutate({ mutation: settleSendCoinsMutation, @@ -432,6 +442,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias expect( await mutate({ mutation: settleSendCoinsMutation, @@ -465,6 +476,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias await mutate({ mutation: voteForSendCoinsMutation, variables: { args }, @@ -489,6 +501,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias expect( await mutate({ mutation: revertSettledSendCoinsMutation, @@ -518,6 +531,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias expect( await mutate({ mutation: revertSettledSendCoinsMutation, @@ -551,6 +565,7 @@ describe('SendCoinsResolver', () => { } args.senderUserUuid = sendUser.gradidoID args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias expect( await mutate({ mutation: revertSettledSendCoinsMutation, diff --git a/federation/src/graphql/api/1_0/util/storeForeignUser.ts b/federation/src/graphql/api/1_0/util/storeForeignUser.ts index d8f6e3748..43167e24f 100644 --- a/federation/src/graphql/api/1_0/util/storeForeignUser.ts +++ b/federation/src/graphql/api/1_0/util/storeForeignUser.ts @@ -19,11 +19,6 @@ export async function storeForeignUser(args: SendCoinsArgs): Promise { args.senderCommunityUuid, args.senderUserUuid, ) - console.log( - 'X-Com: no foreignUser found for:', - args.senderCommunityUuid, - args.senderUserUuid, - ) let foreignUser = DbUser.create() foreignUser.foreign = true if (args.senderAlias !== null) { @@ -40,7 +35,6 @@ export async function storeForeignUser(args: SendCoinsArgs): Promise { foreignUser.gradidoID = args.senderUserUuid foreignUser = await DbUser.save(foreignUser) logger.debug('X-Com: new foreignUser inserted:', foreignUser) - console.log('X-Com: new foreignUser inserted:', foreignUser) return true } else if ( @@ -54,20 +48,13 @@ export async function storeForeignUser(args: SendCoinsArgs): Promise { user, args, ) - console.log( - 'X-Com: foreignUser still exists, but with different name or alias:', - user, - args, - ) return false } else { logger.debug('X-Com: foreignUser still exists...:', user) - console.log('X-Com: foreignUser still exists...:', user) return true } } catch (err) { logger.error('X-Com: error in storeForeignUser;', err) - console.log('X-Com: error in storeForeignUser;', err) return false } } From 61ca5a78e2ee068fbff97afc5b0c7ddbd4e11a4f Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Mon, 16 Oct 2023 15:37:23 +0200 Subject: [PATCH 10/20] correct query syntax error --- backend/src/seeds/graphql/queries.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 27d5b5d1e..4bd5008d3 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -371,7 +371,7 @@ export const adminListContributionMessages = gql` export const user = gql` query ($identifier: String!, $communityIdentifier: String) { - user(identifier: $identifier, $communityIdentifier: $communityIdentifier) { + user(identifier: $identifier, communityIdentifier: $communityIdentifier) { firstName lastName foreign From 2dd3cb3282eb502969bf6789704007e443c057b7 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Mon, 16 Oct 2023 18:34:01 +0200 Subject: [PATCH 11/20] extent backend tests to treat communityUuid --- .../resolver/TransactionResolver.test.ts | 14 ++-- .../src/graphql/resolver/UserResolver.test.ts | 84 +++++++++++++++++-- .../src/graphql/resolver/util/communities.ts | 2 +- backend/src/seeds/factory/user.ts | 10 +++ 4 files changed, 95 insertions(+), 15 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 857159a97..d130a802e 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -13,6 +13,7 @@ import { Transaction } from '@entity/Transaction' import { User } from '@entity/User' import { ApolloServerTestClient } from 'apollo-server-testing' import { GraphQLError } from 'graphql' +import { v4 as uuidv4 } from 'uuid' import { cleanDB, testEnvironment } from '@test/helpers' import { logger } from '@test/testSetup' @@ -71,12 +72,8 @@ let fedForeignCom: DbFederatedCommunity describe('send coins', () => { beforeAll(async () => { - peter = await userFactory(testEnv, peterLustig) - bob = await userFactory(testEnv, bobBaumeister) - await userFactory(testEnv, stephenHawking) - await userFactory(testEnv, garrickOllivander) homeCom = DbCommunity.create() - homeCom.communityUuid = '7f474922-b6d8-4b64-8cd0-ebf0a1d875aa' + homeCom.communityUuid = uuidv4() homeCom.creationDate = new Date('2000-01-01') homeCom.description = 'homeCom description' homeCom.foreign = false @@ -87,7 +84,7 @@ describe('send coins', () => { homeCom = await DbCommunity.save(homeCom) foreignCom = DbCommunity.create() - foreignCom.communityUuid = '7f474922-b6d8-4b64-8cd0-cea0a1d875bb' + foreignCom.communityUuid = uuidv4() foreignCom.creationDate = new Date('2000-06-06') foreignCom.description = 'foreignCom description' foreignCom.foreign = true @@ -98,6 +95,11 @@ describe('send coins', () => { foreignCom.authenticatedAt = new Date('2000-06-12') foreignCom = await DbCommunity.save(foreignCom) + peter = await userFactory(testEnv, peterLustig) + bob = await userFactory(testEnv, bobBaumeister) + await userFactory(testEnv, stephenHawking) + await userFactory(testEnv, garrickOllivander) + bobData = { email: 'bob@baumeister.de', password: 'Aa12345_', diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 5ad05fc05..a9c50553e 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -5,6 +5,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ import { Connection } from '@dbTools/typeorm' +import { Community as DbCommunity } from '@entity/Community' import { Event as DbEvent } from '@entity/Event' import { TransactionLink } from '@entity/TransactionLink' import { User } from '@entity/User' @@ -2502,6 +2503,39 @@ describe('UserResolver', () => { }) describe('user', () => { + let homeCom1: DbCommunity + let foreignCom1: DbCommunity + + beforeAll(async () => { + homeCom1 = DbCommunity.create() + homeCom1.foreign = false + homeCom1.url = 'http://localhost/api' + homeCom1.publicKey = Buffer.from('publicKey-HomeCommunity') + homeCom1.privateKey = Buffer.from('privateKey-HomeCommunity') + homeCom1.communityUuid = uuidv4() // 'HomeCom-UUID' + homeCom1.authenticatedAt = new Date() + homeCom1.name = 'HomeCommunity-name' + homeCom1.description = 'HomeCommunity-description' + homeCom1.creationDate = new Date() + await DbCommunity.insert(homeCom1) + + foreignCom1 = DbCommunity.create() + foreignCom1.foreign = true + foreignCom1.url = 'http://stage-2.gradido.net/api' + foreignCom1.publicKey = Buffer.from('publicKey-stage-2_Community') + foreignCom1.privateKey = Buffer.from('privateKey-stage-2_Community') + foreignCom1.communityUuid = uuidv4() // 'Stage2-Com-UUID' + foreignCom1.authenticatedAt = new Date() + foreignCom1.name = 'Stage-2_Community-name' + foreignCom1.description = 'Stage-2_Community-description' + foreignCom1.creationDate = new Date() + await DbCommunity.insert(foreignCom1) + }) + + afterAll(async () => { + await DbCommunity.clear() + }) + beforeEach(() => { jest.clearAllMocks() }) @@ -2548,6 +2582,7 @@ describe('UserResolver', () => { query: userQuery, variables: { identifier: 'identifier_is_no_valid_alias!', + communityIdentifier: homeCom1.communityUuid, }, }), ).resolves.toEqual( @@ -2569,14 +2604,44 @@ describe('UserResolver', () => { query: userQuery, variables: { identifier: uuid, + communityIdentifier: homeCom1.communityUuid, }, }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('No user found to given identifier')], + errors: [new GraphQLError('No user found to given identifier(s)')], }), ) - expect(logger.error).toBeCalledWith('No user found to given identifier', uuid) + expect(logger.error).toBeCalledWith( + 'No user found to given identifier(s)', + uuid, + homeCom1.communityUuid, + ) + }) + }) + + describe('identifier is found via email, but not matching community', () => { + it('returns user', async () => { + await expect( + query({ + query: userQuery, + variables: { + identifier: 'bibi@bloxberg.de', + communityIdentifier: foreignCom1.communityUuid, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError('Found user to given contact, but belongs to other community'), + ], + }), + ) + expect(logger.error).toBeCalledWith( + 'Found user to given contact, but belongs to other community', + 'bibi@bloxberg.de', + foreignCom1.communityUuid, + ) }) }) @@ -2587,15 +2652,16 @@ describe('UserResolver', () => { query: userQuery, variables: { identifier: 'bibi@bloxberg.de', + communityIdentifier: homeCom1.communityUuid, }, }), ).resolves.toEqual( expect.objectContaining({ data: { - user: { + user: expect.objectContaining({ firstName: 'Bibi', lastName: 'Bloxberg', - }, + }), }, errors: undefined, }), @@ -2610,15 +2676,16 @@ describe('UserResolver', () => { query: userQuery, variables: { identifier: user.gradidoID, + communityIdentifier: homeCom1.communityUuid, }, }), ).resolves.toEqual( expect.objectContaining({ data: { - user: { + user: expect.objectContaining({ firstName: 'Bibi', lastName: 'Bloxberg', - }, + }), }, errors: undefined, }), @@ -2633,15 +2700,16 @@ describe('UserResolver', () => { query: userQuery, variables: { identifier: 'bibi', + communityIdentifier: homeCom1.communityUuid, }, }), ).resolves.toEqual( expect.objectContaining({ data: { - user: { + user: expect.objectContaining({ firstName: 'Bibi', lastName: 'Bloxberg', - }, + }), }, errors: undefined, }), diff --git a/backend/src/graphql/resolver/util/communities.ts b/backend/src/graphql/resolver/util/communities.ts index fd52f0c06..0c0023a19 100644 --- a/backend/src/graphql/resolver/util/communities.ts +++ b/backend/src/graphql/resolver/util/communities.ts @@ -17,7 +17,7 @@ export async function isHomeCommunity(communityIdentifier: string): Promise { return await DbCommunity.findOneOrFail({ - where: [{ foreign: true }], + where: [{ foreign: false }], }) } diff --git a/backend/src/seeds/factory/user.ts b/backend/src/seeds/factory/user.ts index 3fa5591a2..65b0ff3bb 100644 --- a/backend/src/seeds/factory/user.ts +++ b/backend/src/seeds/factory/user.ts @@ -5,6 +5,7 @@ import { ApolloServerTestClient } from 'apollo-server-testing' import { RoleNames } from '@enum/RoleNames' +import { getHomeCommunity } from '@/graphql/resolver/util/communities' import { setUserRole } from '@/graphql/resolver/util/modifyUserRole' import { createUser, setPassword } from '@/seeds/graphql/mutations' import { UserInterface } from '@/seeds/users/UserInterface' @@ -43,6 +44,15 @@ export const userFactory = async ( } await dbUser.save() } + try { + const homeCom = await getHomeCommunity() + if (homeCom.communityUuid) { + dbUser.communityUuid = homeCom.communityUuid + await User.save(dbUser) + } + } catch (err) { + // no homeCommunity exists + } // get last changes of user from database dbUser = await User.findOneOrFail({ From 54406ca3e8ad910b115c0760adfada84985ea22f Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Mon, 16 Oct 2023 18:36:46 +0200 Subject: [PATCH 12/20] linting --- .../src/graphql/resolver/util/storeForeignUser.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/backend/src/graphql/resolver/util/storeForeignUser.ts b/backend/src/graphql/resolver/util/storeForeignUser.ts index 7be58376b..2462623ae 100644 --- a/backend/src/graphql/resolver/util/storeForeignUser.ts +++ b/backend/src/graphql/resolver/util/storeForeignUser.ts @@ -23,11 +23,6 @@ export async function storeForeignUser( recipCom.communityUuid, committingResult.recipGradidoID, ) - console.log( - 'X-Com: no foreignUser found for:', - recipCom.communityUuid, - committingResult.recipGradidoID, - ) let foreignUser = DbUser.create() foreignUser.foreign = true if (committingResult.recipAlias !== null) { @@ -43,7 +38,6 @@ export async function storeForeignUser( foreignUser.gradidoID = committingResult.recipGradidoID foreignUser = await DbUser.save(foreignUser) logger.debug('X-Com: new foreignUser inserted:', foreignUser) - console.log('X-Com: new foreignUser inserted:', foreignUser) return true } else if ( @@ -56,20 +50,13 @@ export async function storeForeignUser( user, committingResult, ) - console.log( - 'X-Com: foreignUser still exists, but with different name or alias:', - user, - committingResult, - ) return false } else { logger.debug('X-Com: foreignUser still exists...:', user) - console.log('X-Com: foreignUser still exists...:', user) return true } } catch (err) { logger.error('X-Com: error in storeForeignUser;', err) - console.log('X-Com: error in storeForeignUser;', err) return false } } From a165b588afc2988be410e1f831381d7078154988 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Mon, 16 Oct 2023 22:01:29 +0200 Subject: [PATCH 13/20] correct error in store foreign user --- federation/src/graphql/api/1_0/util/storeForeignUser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/federation/src/graphql/api/1_0/util/storeForeignUser.ts b/federation/src/graphql/api/1_0/util/storeForeignUser.ts index 43167e24f..89d8deb89 100644 --- a/federation/src/graphql/api/1_0/util/storeForeignUser.ts +++ b/federation/src/graphql/api/1_0/util/storeForeignUser.ts @@ -27,7 +27,7 @@ export async function storeForeignUser(args: SendCoinsArgs): Promise { foreignUser.communityUuid = args.senderCommunityUuid if (args.senderUserName !== null) { foreignUser.firstName = args.senderUserName.slice(0, args.senderUserName.indexOf(' ')) - foreignUser.firstName = args.senderUserName.slice( + foreignUser.lastName = args.senderUserName.slice( args.senderUserName.indexOf(' '), args.senderUserName.length, ) From b9dc65f123fcdbf67b540a92ccb70bcc8eecb47c Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 17 Oct 2023 14:37:50 +0200 Subject: [PATCH 14/20] correct store foreign user in case of different name or alias --- backend/src/graphql/resolver/TransactionResolver.ts | 2 +- .../graphql/resolver/util/processXComSendCoins.ts | 7 ++++--- .../src/graphql/resolver/util/storeForeignUser.ts | 13 ++++++++++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 267f90596..1dbff7896 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -476,7 +476,7 @@ export class TransactionResolver { amount, memo, senderUser, - pendingResult.recipGradidoID, + pendingResult, ) logger.debug('processXComCommittingSendCoins result: ', committingResult) if (!committingResult.vote) { diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 1afa7a1c0..6455bc36d 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -159,7 +159,7 @@ export async function processXComCommittingSendCoins( amount: Decimal, memo: string, sender: dbUser, - recipUuid: string, + recipient: SendCoinsResult, ): Promise { const sendCoinsResult = new SendCoinsResult() try { @@ -171,7 +171,7 @@ export async function processXComCommittingSendCoins( amount, memo, sender, - recipUuid, + recipient, ) // first find pending Tx with given parameters const pendingTx = await DbPendingTransaction.findOneBy({ @@ -180,7 +180,7 @@ export async function processXComCommittingSendCoins( userName: fullName(sender.firstName, sender.lastName), linkedUserCommunityUuid: receiverCom.communityUuid ?? CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID, - linkedUserGradidoID: recipUuid, + linkedUserGradidoID: recipient.recipGradidoID ? recipient.recipGradidoID : undefined, typeId: TransactionTypeId.SEND, state: PendingTransactionState.NEW, balanceDate: creationDate, @@ -237,6 +237,7 @@ export async function processXComCommittingSendCoins( ) } sendCoinsResult.recipGradidoID = pendingTx.linkedUserGradidoID + sendCoinsResult.recipAlias = recipient.recipAlias } } catch (err) { logger.error(`Error in writing sender pending transaction: `, err) diff --git a/backend/src/graphql/resolver/util/storeForeignUser.ts b/backend/src/graphql/resolver/util/storeForeignUser.ts index 2462623ae..5942159b4 100644 --- a/backend/src/graphql/resolver/util/storeForeignUser.ts +++ b/backend/src/graphql/resolver/util/storeForeignUser.ts @@ -50,7 +50,18 @@ export async function storeForeignUser( user, committingResult, ) - return false + if (committingResult.recipFirstName !== null) { + user.firstName = committingResult.recipFirstName + } + if (committingResult.recipLastName !== null) { + user.lastName = committingResult.recipLastName + } + if (committingResult.recipAlias !== null) { + user.alias = committingResult.recipAlias + } + await DbUser.save(user) + logger.debug('update recipient successful.', user) + return true } else { logger.debug('X-Com: foreignUser still exists...:', user) return true From 8a9a539e3cf258264eeaf339b020017b65c4e900 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 17 Oct 2023 16:00:56 +0200 Subject: [PATCH 15/20] linting --- frontend/src/components/TransactionRows/Name.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/TransactionRows/Name.vue b/frontend/src/components/TransactionRows/Name.vue index b2e0830fc..2f495756c 100644 --- a/frontend/src/components/TransactionRows/Name.vue +++ b/frontend/src/components/TransactionRows/Name.vue @@ -50,7 +50,9 @@ export default { ? this.linkedUser.alias ? this.linkedUser.alias + (this.linkedUser.communityName ? ' / ' + this.linkedUser.communityName : '') - : this.linkedUser.firstName + ' ' + this.linkedUser.lastName + + : this.linkedUser.firstName + + ' ' + + this.linkedUser.lastName + (this.linkedUser.communityName ? ' / ' + this.linkedUser.communityName : '') : this.text }, From e983e59173ac4d0b3b3ec85fc5bb52e638506534 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 17 Oct 2023 21:54:36 +0200 Subject: [PATCH 16/20] sender alias as optional and further optimizations --- .../client/1_0/model/SendCoinsArgs.ts | 4 +-- .../graphql/resolver/TransactionResolver.ts | 16 +++++++----- .../graphql/api/1_0/model/SendCoinsArgs.ts | 4 +-- .../api/1_0/resolver/SendCoinsResolver.ts | 7 ++++- .../src/graphql/util/findUserByIdentifier.ts | 26 +++++++++++++++---- 5 files changed, 41 insertions(+), 16 deletions(-) diff --git a/backend/src/federation/client/1_0/model/SendCoinsArgs.ts b/backend/src/federation/client/1_0/model/SendCoinsArgs.ts index 73e166308..ae9a01f58 100644 --- a/backend/src/federation/client/1_0/model/SendCoinsArgs.ts +++ b/backend/src/federation/client/1_0/model/SendCoinsArgs.ts @@ -27,6 +27,6 @@ export class SendCoinsArgs { @Field(() => String) senderUserName: string - @Field(() => String) - senderAlias: string + @Field(() => String, { nullable: true }) + senderAlias?: string | null } diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 1dbff7896..8d35708a6 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -38,7 +38,7 @@ import { calculateBalance } from '@/util/validate' import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions' import { BalanceResolver } from './BalanceResolver' -import { getCommunityName, isCommunityAuthenticated, isHomeCommunity } from './util/communities' +import { getCommunity, getCommunityName, isHomeCommunity } from './util/communities' import { findUserByIdentifier } from './util/findUserByIdentifier' import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' @@ -445,13 +445,17 @@ export class TransactionResolver { if (!CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED) { throw new LogError('X-Community sendCoins disabled per configuration!') } - if (!(await isCommunityAuthenticated(recipientCommunityIdentifier))) { + const recipCom = await getCommunity(recipientCommunityIdentifier) + logger.debug('recipient commuity: ', recipCom) + if (recipCom === null) { + throw new LogError( + 'no recipient commuity found for identifier:', + recipientCommunityIdentifier, + ) + } + if (recipCom !== null && recipCom.authenticatedAt === null) { throw new LogError('recipient commuity is connected, but still not authenticated yet!') } - const recipCom = await DbCommunity.findOneOrFail({ - where: { communityUuid: recipientCommunityIdentifier }, - }) - logger.debug('recipient commuity: ', recipCom) let pendingResult: SendCoinsResult let committingResult: SendCoinsResult const creationDate = new Date() diff --git a/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts b/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts index 1337bbc6c..e15f40ed3 100644 --- a/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts +++ b/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts @@ -27,6 +27,6 @@ export class SendCoinsArgs { @Field(() => String) senderUserName: string - @Field(() => String) - senderAlias: string + @Field(() => String, { nullable: true }) + senderAlias?: string | null } diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index a05ab2c7d..98f8af422 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -35,11 +35,13 @@ export class SendCoinsResolver { args.senderCommunityUuid, args.senderUserUuid, args.senderUserName, + args.senderAlias, ) const result = new SendCoinsResult() // first check if receiver community is correct const homeCom = await DbCommunity.findOneBy({ communityUuid: args.recipientCommunityUuid, + foreign: false, }) if (!homeCom) { throw new LogError( @@ -50,7 +52,10 @@ export class SendCoinsResolver { let receiverUser try { // second check if receiver user exists in this community - receiverUser = await findUserByIdentifier(args.recipientUserIdentifier) + receiverUser = await findUserByIdentifier( + args.recipientUserIdentifier, + args.recipientCommunityUuid, + ) } catch (err) { logger.error('Error in findUserByIdentifier:', err) throw new LogError( diff --git a/federation/src/graphql/util/findUserByIdentifier.ts b/federation/src/graphql/util/findUserByIdentifier.ts index 96c9eb458..6b7f1bccc 100644 --- a/federation/src/graphql/util/findUserByIdentifier.ts +++ b/federation/src/graphql/util/findUserByIdentifier.ts @@ -6,12 +6,18 @@ import { LogError } from '@/server/LogError' import { VALID_ALIAS_REGEX } from './validateAlias' -export const findUserByIdentifier = async (identifier: string): Promise => { +export const findUserByIdentifier = async ( + identifier: string, + communityIdentifier?: string, +): Promise => { let user: DbUser | null if (validate(identifier) && version(identifier) === 4) { - user = await DbUser.findOne({ where: { gradidoID: identifier }, relations: ['emailContact'] }) + user = await DbUser.findOne({ + where: { gradidoID: identifier, communityUuid: communityIdentifier }, + relations: ['emailContact'], + }) if (!user) { - throw new LogError('No user found to given identifier', identifier) + throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier) } } else if (/^.{2,}@.{2,}\..{2,}$/.exec(identifier)) { const userContact = await DbUserContact.findOne({ @@ -27,12 +33,22 @@ export const findUserByIdentifier = async (identifier: string): Promise if (!userContact.user) { throw new LogError('No user to given contact', identifier) } + if (userContact.user.communityUuid !== communityIdentifier) { + throw new LogError( + 'Found user to given contact, but belongs to other community', + identifier, + communityIdentifier, + ) + } user = userContact.user user.emailContact = userContact } else if (VALID_ALIAS_REGEX.exec(identifier)) { - user = await DbUser.findOne({ where: { alias: identifier }, relations: ['emailContact'] }) + user = await DbUser.findOne({ + where: { alias: identifier, communityUuid: communityIdentifier }, + relations: ['emailContact'], + }) if (!user) { - throw new LogError('No user found to given identifier', identifier) + throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier) } } else { throw new LogError('Unknown identifier type', identifier) From fdefa0641d5424a2856e289fa5116a800188f083 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 17 Oct 2023 23:25:35 +0200 Subject: [PATCH 17/20] correct and extent testcases --- .../1_0/resolver/SendCoinsResolver.test.ts | 86 ++++++++++++++++++- .../api/1_0/resolver/SendCoinsResolver.ts | 1 - .../graphql/api/1_0/util/storeForeignUser.ts | 2 +- 3 files changed, 84 insertions(+), 5 deletions(-) diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts index 2ab2cc8b3..e542542bc 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts @@ -41,7 +41,9 @@ beforeAll(async () => { afterAll(async () => { // await cleanDB() - await con.destroy() + if (!testEnv.con || !testEnv.con.isConnected) { + await testEnv.con.close() + } }) describe('SendCoinsResolver', () => { @@ -92,6 +94,7 @@ describe('SendCoinsResolver', () => { sendUser = DbUser.create() sendUser.alias = 'sendUser-alias' + sendUser.communityUuid = '56a55482-909e-46a4-bfa2-cd025e894eba' sendUser.firstName = 'sendUser-FirstName' sendUser.gradidoID = '56a55482-909e-46a4-bfa2-cd025e894ebc' sendUser.lastName = 'sendUser-LastName' @@ -106,6 +109,7 @@ describe('SendCoinsResolver', () => { recipUser = DbUser.create() recipUser.alias = 'recipUser-alias' + recipUser.communityUuid = '56a55482-909e-46a4-bfa2-cd025e894ebb' recipUser.firstName = 'recipUser-FirstName' recipUser.gradidoID = '56a55482-909e-46a4-bfa2-cd025e894ebd' recipUser.lastName = 'recipUser-LastName' @@ -182,7 +186,7 @@ describe('SendCoinsResolver', () => { }) }) - describe('valid X-Com-TX voted', () => { + describe('valid X-Com-TX voted per gradidoID', () => { it('throws an error', async () => { jest.clearAllMocks() const args = new SendCoinsArgs() @@ -219,6 +223,82 @@ describe('SendCoinsResolver', () => { ) }) }) + + describe('valid X-Com-TX voted per alias', () => { + it('throws an error', async () => { + jest.clearAllMocks() + const args = new SendCoinsArgs() + if (foreignCom.communityUuid) { + args.recipientCommunityUuid = foreignCom.communityUuid + } + args.recipientUserIdentifier = recipUser.alias + args.creationDate = new Date().toISOString() + args.amount = new Decimal(100) + args.memo = 'X-Com-TX memo' + if (homeCom.communityUuid) { + args.senderCommunityUuid = homeCom.communityUuid + } + args.senderUserUuid = sendUser.gradidoID + args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias + expect( + await mutate({ + mutation: voteForSendCoinsMutation, + variables: { args }, + }), + ).toEqual( + expect.objectContaining({ + data: { + voteForSendCoins: { + recipGradidoID: '56a55482-909e-46a4-bfa2-cd025e894ebd', + recipFirstName: 'recipUser-FirstName', + recipLastName: 'recipUser-LastName', + recipAlias: 'recipUser-alias', + vote: true, + }, + }, + }), + ) + }) + }) + + describe('valid X-Com-TX voted per email', () => { + it('throws an error', async () => { + jest.clearAllMocks() + const args = new SendCoinsArgs() + if (foreignCom.communityUuid) { + args.recipientCommunityUuid = foreignCom.communityUuid + } + args.recipientUserIdentifier = recipContact.email + args.creationDate = new Date().toISOString() + args.amount = new Decimal(100) + args.memo = 'X-Com-TX memo' + if (homeCom.communityUuid) { + args.senderCommunityUuid = homeCom.communityUuid + } + args.senderUserUuid = sendUser.gradidoID + args.senderUserName = fullName(sendUser.firstName, sendUser.lastName) + args.senderAlias = sendUser.alias + expect( + await mutate({ + mutation: voteForSendCoinsMutation, + variables: { args }, + }), + ).toEqual( + expect.objectContaining({ + data: { + voteForSendCoins: { + recipGradidoID: '56a55482-909e-46a4-bfa2-cd025e894ebd', + recipFirstName: 'recipUser-FirstName', + recipLastName: 'recipUser-LastName', + recipAlias: 'recipUser-alias', + vote: true, + }, + }, + }), + ) + }) + }) }) describe('revertSendCoins', () => { @@ -588,7 +668,7 @@ async function newEmailContact(email: string, userId: number): Promise { ) let foreignUser = DbUser.create() foreignUser.foreign = true - if (args.senderAlias !== null) { + if (args.senderAlias) { foreignUser.alias = args.senderAlias } foreignUser.communityUuid = args.senderCommunityUuid From 43b074ec297891714a216326c41ecb4ec6ce6b4e Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 17 Oct 2023 23:27:24 +0200 Subject: [PATCH 18/20] skip sendCoins feature from e2e test --- .../cypress/e2e/{SendCoins.feature => SendCoins.feature.save} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename e2e-tests/cypress/e2e/{SendCoins.feature => SendCoins.feature.save} (100%) diff --git a/e2e-tests/cypress/e2e/SendCoins.feature b/e2e-tests/cypress/e2e/SendCoins.feature.save similarity index 100% rename from e2e-tests/cypress/e2e/SendCoins.feature rename to e2e-tests/cypress/e2e/SendCoins.feature.save From da0deb01d2ee89682dcb68a8813dc676eed6c9eb Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 17 Oct 2023 23:39:58 +0200 Subject: [PATCH 19/20] linting --- .../src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts index e542542bc..633effcbe 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts @@ -13,7 +13,7 @@ import { Connection } from '@dbTools/typeorm' import Decimal from 'decimal.js-light' import { SendCoinsArgs } from '../model/SendCoinsArgs' -let mutate: ApolloServerTestClient['mutate'], con: Connection +let mutate: ApolloServerTestClient['mutate'] // , con: Connection // let query: ApolloServerTestClient['query'] let testEnv: { From 23761c065b81dac32151fa0b10c3951235d03417 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 17 Oct 2023 23:46:35 +0200 Subject: [PATCH 20/20] correct error --- .../src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts index 633effcbe..d305e1726 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts @@ -35,7 +35,7 @@ beforeAll(async () => { testEnv = await testEnvironment(logger) mutate = testEnv.mutate // query = testEnv.query - con = testEnv.con + // con = testEnv.con await cleanDB() })