From 1c347b7ff71def8943c1899579b0ab00942272d4 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 14 Oct 2025 12:32:57 +0200 Subject: [PATCH] remove join from communityHandshakeState, await save direct, to prevent drag away errors --- .../src/federation/authenticateCommunities.ts | 5 +- .../src/entity/CommunityHandshakeState.ts | 7 +-- database/src/entity/FederatedCommunity.ts | 6 -- .../CommunityHandshakeStateLogging.view.ts | 6 +- .../src/queries/communityHandshakes.test.ts | 60 ++++++++----------- database/src/queries/communityHandshakes.ts | 8 +-- .../1_0/resolver/AuthenticationResolver.ts | 32 +++------- .../api/1_0/util/authenticateCommunity.ts | 29 +++------ 8 files changed, 49 insertions(+), 104 deletions(-) diff --git a/backend/src/federation/authenticateCommunities.ts b/backend/src/federation/authenticateCommunities.ts index c548be8e7..9226d652c 100644 --- a/backend/src/federation/authenticateCommunities.ts +++ b/backend/src/federation/authenticateCommunities.ts @@ -55,7 +55,7 @@ export async function startCommunityAuthentication( ) // check if a authentication is already in progress - const existingState = await findPendingCommunityHandshake(fedComBPublicKey, fedComB.apiVersion, false) + const existingState = await findPendingCommunityHandshake(fedComBPublicKey, fedComB.apiVersion) if (existingState) { const stateLogic = new CommunityHandshakeStateLogic(existingState) // retry on timeout or failure @@ -77,7 +77,7 @@ export async function startCommunityAuthentication( state.apiVersion = fedComB.apiVersion state.status = CommunityHandshakeStateType.START_COMMUNITY_AUTHENTICATION state.handshakeId = parseInt(handshakeID) - const stateSaveResolver = state.save() + await state.save() //create JWT with url in payload encrypted by foreignCom.publicJwtKey and signed with homeCom.privateJwtKey const payload = new OpenConnectionJwtPayloadType(handshakeID, @@ -92,7 +92,6 @@ export async function startCommunityAuthentication( args.publicKey = homeComAPublicKey.asHex() args.jwt = jws args.handshakeID = handshakeID - await stateSaveResolver methodLogger.debug('before client.openConnection() args:', args) const result = await client.openConnection(args) if (result) { diff --git a/database/src/entity/CommunityHandshakeState.ts b/database/src/entity/CommunityHandshakeState.ts index f3a27b834..eca9c07e7 100644 --- a/database/src/entity/CommunityHandshakeState.ts +++ b/database/src/entity/CommunityHandshakeState.ts @@ -1,6 +1,5 @@ import { CommunityHandshakeStateType } from '../enum' -import { BaseEntity, Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' -import { FederatedCommunity } from './FederatedCommunity' +import { BaseEntity, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' @Entity('community_handshake_states') export class CommunityHandshakeState extends BaseEntity { @@ -35,8 +34,4 @@ export class CommunityHandshakeState extends BaseEntity { @UpdateDateColumn({ name: 'updated_at', type: 'datetime', precision: 3 }) updatedAt: Date - - @ManyToOne(() => FederatedCommunity, (federatedCommunity) => federatedCommunity.communityHandshakeStates) - @JoinColumn({ name: 'public_key', referencedColumnName: 'publicKey' }) - federatedCommunity: FederatedCommunity } \ No newline at end of file diff --git a/database/src/entity/FederatedCommunity.ts b/database/src/entity/FederatedCommunity.ts index 8993f0663..a6eaee80f 100644 --- a/database/src/entity/FederatedCommunity.ts +++ b/database/src/entity/FederatedCommunity.ts @@ -5,12 +5,10 @@ import { Entity, JoinColumn, ManyToOne, - OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm' import { Community } from './Community' -import { CommunityHandshakeState } from './CommunityHandshakeState' @Entity('federated_communities') export class FederatedCommunity extends BaseEntity { @@ -62,8 +60,4 @@ export class FederatedCommunity extends BaseEntity { ) @JoinColumn({ name: 'public_key', referencedColumnName: 'publicKey' }) community?: Community - - @OneToMany(() => CommunityHandshakeState, (communityHandshakeState) => communityHandshakeState.federatedCommunity) - @JoinColumn({ name: 'public_key', referencedColumnName: 'publicKey' }) - communityHandshakeStates: CommunityHandshakeState[] } diff --git a/database/src/logging/CommunityHandshakeStateLogging.view.ts b/database/src/logging/CommunityHandshakeStateLogging.view.ts index 5345474a0..b7df2452d 100644 --- a/database/src/logging/CommunityHandshakeStateLogging.view.ts +++ b/database/src/logging/CommunityHandshakeStateLogging.view.ts @@ -1,6 +1,5 @@ import { CommunityHandshakeState } from '..' import { AbstractLoggingView } from './AbstractLogging.view' -import { FederatedCommunityLoggingView } from './FederatedCommunityLogging.view' export class CommunityHandshakeStateLoggingView extends AbstractLoggingView { public constructor(private self: CommunityHandshakeState) { @@ -16,10 +15,7 @@ export class CommunityHandshakeStateLoggingView extends AbstractLoggingView { status: this.self.status, lastError: this.self.lastError, createdAt: this.dateToString(this.self.createdAt), - updatedAt: this.dateToString(this.self.updatedAt), - federatedCommunity: this.self.federatedCommunity - ? new FederatedCommunityLoggingView(this.self.federatedCommunity) - : undefined, + updatedAt: this.dateToString(this.self.updatedAt), } } } \ No newline at end of file diff --git a/database/src/queries/communityHandshakes.test.ts b/database/src/queries/communityHandshakes.test.ts index badf5ad96..32eb31c4b 100644 --- a/database/src/queries/communityHandshakes.test.ts +++ b/database/src/queries/communityHandshakes.test.ts @@ -3,14 +3,13 @@ import { CommunityHandshakeState as DbCommunityHandshakeState, Community as DbCommunity, FederatedCommunity as DbFederatedCommunity, - getHomeCommunityWithFederatedCommunityOrFail, - getCommunityByPublicKeyOrFail, findPendingCommunityHandshake, CommunityHandshakeStateType } from '..' import { describe, expect, it, beforeEach, beforeAll, afterAll } from 'vitest' import { createCommunity, createVerifiedFederatedCommunity } from '../seeds/community' import { Ed25519PublicKey } from 'shared' +import { randomBytes } from 'node:crypto' const db = AppDatabase.getInstance() @@ -21,6 +20,15 @@ afterAll(async () => { await db.destroy() }) +async function createCommunityHandshakeState(publicKey: Buffer) { + const state = new DbCommunityHandshakeState() + state.publicKey = publicKey + state.apiVersion = '1_0' + state.status = CommunityHandshakeStateType.START_COMMUNITY_AUTHENTICATION + state.handshakeId = 1 + await state.save() +} + describe('communityHandshakes', () => { // clean db for every test case beforeEach(async () => { @@ -32,48 +40,32 @@ describe('communityHandshakes', () => { it('should find pending community handshake by public key', async () => { const com1 = await createCommunity(false) await createVerifiedFederatedCommunity('1_0', 100, com1) - const state = new DbCommunityHandshakeState() - state.publicKey = com1.publicKey - state.apiVersion = '1_0' - state.status = CommunityHandshakeStateType.OPEN_CONNECTION_CALLBACK - state.handshakeId = 1 - await state.save() + await createCommunityHandshakeState(com1.publicKey) const communityHandshakeState = await findPendingCommunityHandshake(new Ed25519PublicKey(com1.publicKey), '1_0') expect(communityHandshakeState).toBeDefined() expect(communityHandshakeState).toMatchObject({ publicKey: com1.publicKey, apiVersion: '1_0', - status: CommunityHandshakeStateType.OPEN_CONNECTION_CALLBACK, + status: CommunityHandshakeStateType.START_COMMUNITY_AUTHENTICATION, handshakeId: 1 }) }) - it('try to use returned public key for loading community', async () => { - // test this explicit case, because in federation.authentication it lead to server crash - const com1 = await createCommunity(false) - await createVerifiedFederatedCommunity('1_0', 100, com1) - const state = new DbCommunityHandshakeState() - state.publicKey = com1.publicKey - state.apiVersion = '1_0' - state.status = CommunityHandshakeStateType.OPEN_CONNECTION_CALLBACK - state.handshakeId = 1 - await state.save() - const communityHandshakeState = await findPendingCommunityHandshake(new Ed25519PublicKey(com1.publicKey), '1_0') + it('update state', async () => { + const publicKey = new Ed25519PublicKey(randomBytes(32)) + await createCommunityHandshakeState(publicKey.asBuffer()) + const communityHandshakeState = await findPendingCommunityHandshake(publicKey, '1_0') expect(communityHandshakeState).toBeDefined() - expect(communityHandshakeState?.federatedCommunity?.community).toBeDefined() - const ed25519PublicKey = new Ed25519PublicKey(communityHandshakeState?.federatedCommunity?.community?.publicKey) - const community = await DbCommunity.findOneBy({ publicKey: ed25519PublicKey.asBuffer() }) - expect(community).toBeDefined() - expect(community).toMatchObject({ - communityUuid: com1.communityUuid, - name: com1.name, - description: com1.description, - url: com1.url, - creationDate: com1.creationDate, - authenticatedAt: com1.authenticatedAt, - foreign: com1.foreign, - publicKey: com1.publicKey, - privateKey: com1.privateKey + communityHandshakeState!.status = CommunityHandshakeStateType.OPEN_CONNECTION_CALLBACK + await communityHandshakeState!.save() + const communityHandshakeState2 = await findPendingCommunityHandshake(publicKey, '1_0') + const states = await DbCommunityHandshakeState.find() + expect(communityHandshakeState2).toBeDefined() + expect(communityHandshakeState2).toMatchObject({ + publicKey: publicKey.asBuffer(), + apiVersion: '1_0', + status: CommunityHandshakeStateType.OPEN_CONNECTION_CALLBACK, + handshakeId: 1 }) }) }) \ No newline at end of file diff --git a/database/src/queries/communityHandshakes.ts b/database/src/queries/communityHandshakes.ts index 9181cfbb3..23fe4f0d1 100644 --- a/database/src/queries/communityHandshakes.ts +++ b/database/src/queries/communityHandshakes.ts @@ -5,12 +5,10 @@ import { Ed25519PublicKey } from 'shared' /** * Find a pending community handshake by public key. * @param publicKey The public key of the community. - * @param withRelations Whether to include the federated community and community in the result, default true. + * @param apiVersion The API version of the community. * @returns The CommunityHandshakeState with associated federated community and community. */ -export function findPendingCommunityHandshake( - publicKey: Ed25519PublicKey, apiVersion: string, withRelations = true -): Promise { +export function findPendingCommunityHandshake(publicKey: Ed25519PublicKey, apiVersion: string): Promise { return CommunityHandshakeState.findOne({ where: { publicKey: publicKey.asBuffer(), @@ -21,7 +19,6 @@ export function findPendingCommunityHandshake( CommunityHandshakeStateType.SUCCESS ])) }, - relations: withRelations ? { federatedCommunity: { community: true } } : undefined, }) } @@ -30,7 +27,6 @@ export function findPendingCommunityHandshakeOrFailByOneTimeCode( ): Promise { return CommunityHandshakeState.findOneOrFail({ where: { oneTimeCode }, - relations: { federatedCommunity: { community: true } }, }) } \ No newline at end of file diff --git a/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts b/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts index 0c9e5e2e5..0df45f794 100644 --- a/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts @@ -11,6 +11,7 @@ import { getHomeCommunity, findPendingCommunityHandshakeOrFailByOneTimeCode, Community as DbCommunity, + getCommunityByPublicKeyOrFail, } from 'database' import { getLogger } from 'log4js' import { @@ -126,7 +127,6 @@ export class AuthenticationResolver { methodLogger.addContext('handshakeID', args.handshakeID) methodLogger.debug(`authenticate() via apiVersion=1_0 ...`, args) let state: DbCommunityHandshakeState | null = null - let stateSaveResolver: Promise | undefined = undefined const argsPublicKey = new Ed25519PublicKey(args.publicKey) try { const authArgs = await interpretEncryptedTransferArgs(args) as AuthenticationJwtPayloadType @@ -150,11 +150,10 @@ export class AuthenticationResolver { throw new Error('No valid pending community handshake found') } state.status = CommunityHandshakeStateType.SUCCESS - // stateSaveResolver = state.save() await state.save() - + methodLogger.debug(`search community per oneTimeCode:`, authArgs.oneTimeCode) - const authCom = state.federatedCommunity.community + const authCom = await getCommunityByPublicKeyOrFail(argsPublicKey) if (authCom) { methodLogger.debug('found authCom:', new CommunityLoggingView(authCom)) const authComPublicKey = new Ed25519PublicKey(authCom.publicKey) @@ -171,21 +170,11 @@ export class AuthenticationResolver { `invalid uuid: ${authArgs.uuid} for community with publicKey ${authComPublicKey.asHex()}` ) } - methodLogger.debug('before updating auth community again from db') - // need to use query builder, loading from db, changing and save lead to server crash with this error: - // TypeError [ERR_INVALID_ARG_TYPE]: The "otherBuffer" argument must be of type Buffer or Uint8Array. Received an instance of Object - // seems to be a typeorm problem with Buffer, even if I give a freshly created Buffer for public_key - /*await DbCommunity.createQueryBuilder() - .update(DbCommunity) - .set({ - communityUuid: communityUuid.data, - authenticatedAt: new Date(), - }) - .where({ id: authCom.id }) - .execute() + authCom.communityUuid = communityUuid.data + authCom.authenticatedAt = new Date() + await authCom.save() methodLogger.debug('update authCom.uuid successfully') - */ - methodLogger.debug('skipped community update') + const homeComB = await getHomeCommunity() if (homeComB?.communityUuid) { const responseArgs = new AuthenticationResponseJwtPayloadType(args.handshakeID,homeComB.communityUuid) @@ -205,16 +194,11 @@ export class AuthenticationResolver { methodLogger.info(`state: ${new CommunityHandshakeStateLoggingView(state)}`) state.status = CommunityHandshakeStateType.FAILED state.lastError = errorString - stateSaveResolver = state.save() + await state.save() } methodLogger.error(`failed: ${errorString}`) // no infos to the caller return null - } finally { - if (stateSaveResolver) { - await stateSaveResolver - } } - } } diff --git a/federation/src/graphql/api/1_0/util/authenticateCommunity.ts b/federation/src/graphql/api/1_0/util/authenticateCommunity.ts index 00c140d9c..a2c9136b0 100644 --- a/federation/src/graphql/api/1_0/util/authenticateCommunity.ts +++ b/federation/src/graphql/api/1_0/util/authenticateCommunity.ts @@ -51,7 +51,7 @@ export async function startOpenConnectionCallback( methodLogger.debug(`Authentication: startOpenConnectionCallback() with:`, { publicKey: publicKey.asHex(), }) - const pendingState = await findPendingCommunityHandshake(publicKey, api, false) + const pendingState = await findPendingCommunityHandshake(publicKey, api) if (pendingState) { const stateLogic = new CommunityHandshakeStateLogic(pendingState) // retry on timeout or failure @@ -61,7 +61,6 @@ export async function startOpenConnectionCallback( return } } - let stateSaveResolver: Promise | undefined = undefined const state = new DbCommunityHandshakeState() try { const [homeComB, comA] = await Promise.all([ @@ -84,7 +83,7 @@ export async function startOpenConnectionCallback( state.status = CommunityHandshakeStateType.START_OPEN_CONNECTION_CALLBACK state.handshakeId = parseInt(handshakeID) state.oneTimeCode = oneTimeCode - stateSaveResolver = state.save() + await state.save() methodLogger.debug( `Authentication: store oneTimeCode in CommunityHandshakeState:`, new CommunityHandshakeStateLoggingView(state), @@ -103,13 +102,12 @@ export async function startOpenConnectionCallback( args.publicKey = new Ed25519PublicKey(homeComB.publicKey).asHex() args.jwt = jwt args.handshakeID = handshakeID - await stateSaveResolver const result = await client.openConnectionCallback(args) if (result) { methodLogger.debug(`startOpenConnectionCallback() successful: ${jwt}`) } else { methodLogger.debug(`jwt: ${jwt}`) - stateSaveResolver = errorState('startOpenConnectionCallback() failed', methodLogger, state) + await errorState('startOpenConnectionCallback() failed', methodLogger, state) } } } catch (err) { @@ -119,11 +117,7 @@ export async function startOpenConnectionCallback( } else { errorString = String(err) } - stateSaveResolver = errorState(`error in startOpenConnectionCallback: ${errorString}`, methodLogger, state) - } finally { - if (stateSaveResolver) { - await stateSaveResolver - } + await errorState(`error in startOpenConnectionCallback: ${errorString}`, methodLogger, state) } } @@ -139,7 +133,6 @@ export async function startAuthentication( fedComB: new FederatedCommunityLoggingView(fedComB), }) let state: DbCommunityHandshakeState | null = null - let stateSaveResolver: Promise | undefined = undefined const fedComBPublicKey = new Ed25519PublicKey(fedComB.publicKey) try { const homeComA = await getHomeCommunity() @@ -150,7 +143,7 @@ export async function startAuthentication( if (!comB.publicJwtKey) { throw new Error('Public JWT key still not exist for foreign community') } - state = await findPendingCommunityHandshake(fedComBPublicKey, fedComB.apiVersion, false) + state = await findPendingCommunityHandshake(fedComBPublicKey, fedComB.apiVersion) if (!state) { throw new Error('No pending community handshake found') } @@ -163,7 +156,7 @@ export async function startAuthentication( throw new Error('No valid pending community handshake found') } state.status = CommunityHandshakeStateType.START_AUTHENTICATION - stateSaveResolver = state.save() + await state.save() const client = AuthenticationClientFactory.getInstance(fedComB) @@ -196,12 +189,12 @@ export async function startAuthentication( comB.authenticatedAt = new Date() await DbCommunity.save(comB) state.status = CommunityHandshakeStateType.SUCCESS - stateSaveResolver = state.save() + await state.save() methodLogger.debug('Community Authentication successful:', new CommunityLoggingView(comB)) } else { state.status = CommunityHandshakeStateType.FAILED state.lastError = 'Community Authentication failed, empty response' - stateSaveResolver = state.save() + await state.save() methodLogger.error('Community Authentication failed:', authenticationArgs) } } @@ -215,12 +208,8 @@ export async function startAuthentication( if (state) { state.status = CommunityHandshakeStateType.FAILED state.lastError = errorString - stateSaveResolver = state.save() + await state.save() } methodLogger.error('error in startAuthentication:', errorString) - } finally { - if (stateSaveResolver) { - await stateSaveResolver - } } }