mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
111 lines
5.2 KiB
TypeScript
111 lines
5.2 KiB
TypeScript
import {
|
|
CommunityHandshakeState as DbCommunityHandshakeState,
|
|
CommunityHandshakeStateLoggingView,
|
|
FederatedCommunity as DbFederatedCommunity,
|
|
findPendingCommunityHandshake,
|
|
getHomeCommunityWithFederatedCommunityOrFail,
|
|
CommunityHandshakeStateType,
|
|
getCommunityByPublicKeyOrFail,
|
|
} from 'database'
|
|
import { randombytes_random } from 'sodium-native'
|
|
|
|
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/federation/client/1_0/AuthenticationClient'
|
|
import { ensureUrlEndsWithSlash, getFederatedCommunityWithApiOrFail } from 'core'
|
|
|
|
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
|
import { communityAuthenticatedSchema, encryptAndSign, OpenConnectionJwtPayloadType } from 'shared'
|
|
import { getLogger } from 'log4js'
|
|
import { AuthenticationClientFactory } from './client/AuthenticationClientFactory'
|
|
import { EncryptedTransferArgs } from 'core'
|
|
import { CommunityHandshakeStateLogic } from 'core'
|
|
import { Ed25519PublicKey } from 'shared'
|
|
|
|
const createLogger = (functionName: string) => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.authenticateCommunities.${functionName}`)
|
|
|
|
export enum StartCommunityAuthenticationResult {
|
|
ALREADY_AUTHENTICATED = 'already authenticated',
|
|
ALREADY_IN_PROGRESS = 'already in progress',
|
|
SUCCESSFULLY_STARTED = 'successfully started',
|
|
}
|
|
|
|
export async function startCommunityAuthentication(
|
|
fedComB: DbFederatedCommunity,
|
|
): Promise<StartCommunityAuthenticationResult> {
|
|
const methodLogger = createLogger('startCommunityAuthentication')
|
|
const handshakeID = randombytes_random().toString()
|
|
const fedComBPublicKey = new Ed25519PublicKey(fedComB.publicKey)
|
|
methodLogger.addContext('handshakeID', handshakeID)
|
|
methodLogger.debug(`start with public key ${fedComBPublicKey.asHex()}`)
|
|
const homeComA = await getHomeCommunityWithFederatedCommunityOrFail(fedComB.apiVersion)
|
|
// methodLogger.debug('homeComA', new CommunityLoggingView(homeComA))
|
|
const homeFedComA = getFederatedCommunityWithApiOrFail(homeComA, fedComB.apiVersion)
|
|
|
|
const comB = await getCommunityByPublicKeyOrFail(fedComBPublicKey)
|
|
// methodLogger.debug('started with comB:', new CommunityLoggingView(comB))
|
|
// check if communityUuid is not a valid v4Uuid
|
|
|
|
// communityAuthenticatedSchema.safeParse return true
|
|
// - if communityUuid is a valid v4Uuid and
|
|
// - if authenticatedAt is a valid date
|
|
if (communityAuthenticatedSchema.safeParse(comB).success) {
|
|
methodLogger.debug(`comB.communityUuid is already a valid v4Uuid ${ comB.communityUuid || 'null' } and was authenticated at ${ comB.authenticatedAt || 'null'}`)
|
|
return StartCommunityAuthenticationResult.ALREADY_AUTHENTICATED
|
|
}
|
|
/*methodLogger.debug('comB.uuid is null or is a not valid v4Uuid...',
|
|
comB.communityUuid || 'null', comB.authenticatedAt || 'null'
|
|
)*/
|
|
|
|
// check if a authentication is already in progress
|
|
const existingState = await findPendingCommunityHandshake(fedComBPublicKey, fedComB.apiVersion)
|
|
if (existingState) {
|
|
const stateLogic = new CommunityHandshakeStateLogic(existingState)
|
|
// retry on timeout or failure
|
|
if (!(await stateLogic.isTimeoutUpdate())) {
|
|
// authentication with community and api version is still in progress and it is not timeout yet
|
|
methodLogger.debug('existingState, so we exit here', new CommunityHandshakeStateLoggingView(existingState))
|
|
return StartCommunityAuthenticationResult.ALREADY_IN_PROGRESS
|
|
}
|
|
}
|
|
|
|
const client = AuthenticationClientFactory.getInstance(fedComB)
|
|
|
|
if (client instanceof V1_0_AuthenticationClient) {
|
|
if (!comB.publicJwtKey) {
|
|
throw new Error(`Public JWT key still not exist for comB ${comB.name}`)
|
|
}
|
|
const state = new DbCommunityHandshakeState()
|
|
state.publicKey = fedComBPublicKey.asBuffer()
|
|
state.apiVersion = fedComB.apiVersion
|
|
state.status = CommunityHandshakeStateType.START_COMMUNITY_AUTHENTICATION
|
|
state.handshakeId = parseInt(handshakeID)
|
|
await state.save()
|
|
methodLogger.debug('[START_COMMUNITY_AUTHENTICATION] community handshake state created')
|
|
|
|
//create JWT with url in payload encrypted by foreignCom.publicJwtKey and signed with homeCom.privateJwtKey
|
|
const payload = new OpenConnectionJwtPayloadType(handshakeID,
|
|
ensureUrlEndsWithSlash(homeFedComA.endPoint).concat(homeFedComA.apiVersion),
|
|
)
|
|
// methodLogger.debug('payload', payload)
|
|
const jws = await encryptAndSign(payload, homeComA!.privateJwtKey!, comB.publicJwtKey!)
|
|
// methodLogger.debug('jws', jws)
|
|
// prepare the args for the client invocation
|
|
const args = new EncryptedTransferArgs()
|
|
const homeComAPublicKey = new Ed25519PublicKey(homeComA!.publicKey)
|
|
args.publicKey = homeComAPublicKey.asHex()
|
|
args.jwt = jws
|
|
args.handshakeID = handshakeID
|
|
// methodLogger.debug('before client.openConnection() args:', args)
|
|
const result = await client.openConnection(args)
|
|
if (result) {
|
|
methodLogger.debug(`successful initiated at community:`, fedComB.endPoint)
|
|
} else {
|
|
const errorMsg = `can't initiate at community: ${fedComB.endPoint}`
|
|
methodLogger.error(errorMsg)
|
|
state.status = CommunityHandshakeStateType.FAILED
|
|
state.lastError = errorMsg
|
|
}
|
|
await state.save()
|
|
}
|
|
return StartCommunityAuthenticationResult.SUCCESSFULLY_STARTED
|
|
}
|