mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge remote-tracking branch 'origin/master' into
2946-feature-x-com-3-introduce-business-communities
This commit is contained in:
commit
827c719eba
@ -1,46 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { gql } from 'graphql-request'
|
||||
|
||||
import { GraphQLGetClient } from '@/federation/client/GraphQLGetClient'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
export class FederationClient {
|
||||
public async requestGetPublicKey(dbCom: DbFederatedCommunity): Promise<string | undefined> {
|
||||
let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/'
|
||||
endpoint = `${endpoint}${dbCom.apiVersion}/`
|
||||
logger.info(`requestGetPublicKey with endpoint='${endpoint}'...`)
|
||||
|
||||
const graphQLClient = GraphQLGetClient.getInstance(endpoint)
|
||||
logger.debug(`graphQLClient=${JSON.stringify(graphQLClient)}`)
|
||||
const query = gql`
|
||||
query {
|
||||
getPublicKey {
|
||||
publicKey
|
||||
}
|
||||
}
|
||||
`
|
||||
const variables = {}
|
||||
try {
|
||||
const { data, errors, extensions, headers, status } = await graphQLClient.rawRequest(
|
||||
query,
|
||||
variables,
|
||||
)
|
||||
logger.debug(`Response-Data:`, data, errors, extensions, headers, status)
|
||||
if (data) {
|
||||
logger.debug(`Response-PublicKey:`, data.getPublicKey.publicKey)
|
||||
logger.info(`requestGetPublicKey processed successfully`)
|
||||
return data.getPublicKey.publicKey
|
||||
}
|
||||
logger.warn(`requestGetPublicKey processed without response data`)
|
||||
} catch (err) {
|
||||
if (err instanceof GraphQLError) {
|
||||
logger.error(`RawRequest-Error on ${endpoint} with message ${err.message}`)
|
||||
}
|
||||
throw new Error(`Request-Error in requestGetPublicKey.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
|
||||
import { FederationClient as FedClient } from '@/federation/client/1_0/FederationClient'
|
||||
// import { GraphQLError } from 'graphql'
|
||||
// import { gql } from 'graphql-request'
|
||||
|
||||
// import { GraphQLGetClient } from '@/federation/client/GraphQLGetClient'
|
||||
// import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
export class FederationClient {
|
||||
async requestGetPublicKey(dbCom: DbFederatedCommunity): Promise<string | undefined> {
|
||||
return await new FedClient().requestGetPublicKey(dbCom)
|
||||
}
|
||||
}
|
||||
58
backend/src/federation/client/Client.ts
Normal file
58
backend/src/federation/client/Client.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
|
||||
import { ApiVersionType } from '@/federation/enum/apiVersionType'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
import { Client_1_0 } from './Client_1_0'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { Client_1_1 } from './Client_1_1'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
type FederationClient = Client_1_0 | Client_1_1
|
||||
|
||||
interface ClientInstance {
|
||||
id: number
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
client: FederationClient
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||
export class Client {
|
||||
private static instanceArray: ClientInstance[] = []
|
||||
|
||||
/**
|
||||
* The Singleton's constructor should always be private to prevent direct
|
||||
* construction calls with the `new` operator.
|
||||
*/
|
||||
// eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function
|
||||
private constructor() {}
|
||||
|
||||
private static createFederationClient = (dbCom: DbFederatedCommunity) => {
|
||||
switch (dbCom.apiVersion) {
|
||||
case ApiVersionType.V1_0:
|
||||
return new Client_1_0(dbCom)
|
||||
case ApiVersionType.V1_1:
|
||||
return new Client_1_1(dbCom)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The static method that controls the access to the singleton instance.
|
||||
*
|
||||
* This implementation let you subclass the Singleton class while keeping
|
||||
* just one instance of each subclass around.
|
||||
*/
|
||||
public static getInstance(dbCom: DbFederatedCommunity): FederationClient | null {
|
||||
const instance = Client.instanceArray.find((instance) => instance.id === dbCom.id)
|
||||
if (instance) {
|
||||
return instance.client
|
||||
}
|
||||
const client = Client.createFederationClient(dbCom)
|
||||
if (client) {
|
||||
Client.instanceArray.push({ id: dbCom.id, client } as ClientInstance)
|
||||
}
|
||||
return client
|
||||
}
|
||||
}
|
||||
49
backend/src/federation/client/Client_1_0.ts
Normal file
49
backend/src/federation/client/Client_1_0.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
|
||||
import { getPublicKey } from '@/federation/query/getPublicKey'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export class Client_1_0 {
|
||||
dbCom: DbFederatedCommunity
|
||||
endpoint: string
|
||||
client: GraphQLClient
|
||||
|
||||
constructor(dbCom: DbFederatedCommunity) {
|
||||
this.dbCom = dbCom
|
||||
this.endpoint = `${dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/'}${
|
||||
dbCom.apiVersion
|
||||
}/`
|
||||
this.client = new GraphQLClient(this.endpoint, {
|
||||
method: 'GET',
|
||||
jsonSerializer: {
|
||||
parse: JSON.parse,
|
||||
stringify: JSON.stringify,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
getPublicKey = async (): Promise<string | undefined> => {
|
||||
logger.info('Federation: getPublicKey from endpoint', this.endpoint)
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { data } = await this.client.rawRequest(getPublicKey, {})
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (!data?.getPublicKey?.publicKey) {
|
||||
logger.warn('Federation: getPublicKey without response data from endpoint', this.endpoint)
|
||||
return
|
||||
}
|
||||
logger.info(
|
||||
'Federation: getPublicKey successful from endpoint',
|
||||
this.endpoint,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
data.getPublicKey.publicKey,
|
||||
)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
||||
return data.getPublicKey.publicKey
|
||||
} catch (err) {
|
||||
logger.warn('Federation: getPublicKey failed for endpoint', this.endpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
5
backend/src/federation/client/Client_1_1.ts
Normal file
5
backend/src/federation/client/Client_1_1.ts
Normal file
@ -0,0 +1,5 @@
|
||||
// eslint-disable-next-line camelcase
|
||||
import { Client_1_0 } from './Client_1_0'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export class Client_1_1 extends Client_1_0 {}
|
||||
@ -1,43 +0,0 @@
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import { PatchedRequestInit } from 'graphql-request/dist/types'
|
||||
|
||||
interface ClientInstance {
|
||||
url: string
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
client: GraphQLGetClient
|
||||
}
|
||||
|
||||
export class GraphQLGetClient extends GraphQLClient {
|
||||
private static instanceArray: ClientInstance[] = []
|
||||
|
||||
/**
|
||||
* The Singleton's constructor should always be private to prevent direct
|
||||
* construction calls with the `new` operator.
|
||||
*/
|
||||
// eslint-disable-next-line no-useless-constructor
|
||||
private constructor(url: string, options?: PatchedRequestInit) {
|
||||
super(url, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* The static method that controls the access to the singleton instance.
|
||||
*
|
||||
* This implementation let you subclass the Singleton class while keeping
|
||||
* just one instance of each subclass around.
|
||||
*/
|
||||
public static getInstance(url: string): GraphQLGetClient {
|
||||
const instance = GraphQLGetClient.instanceArray.find((instance) => instance.url === url)
|
||||
if (instance) {
|
||||
return instance.client
|
||||
}
|
||||
const client = new GraphQLGetClient(url, {
|
||||
method: 'GET',
|
||||
jsonSerializer: {
|
||||
parse: JSON.parse,
|
||||
stringify: JSON.stringify,
|
||||
},
|
||||
})
|
||||
GraphQLGetClient.instanceArray.push({ url, client } as ClientInstance)
|
||||
return client
|
||||
}
|
||||
}
|
||||
9
backend/src/federation/query/getPublicKey.ts
Normal file
9
backend/src/federation/query/getPublicKey.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { gql } from 'graphql-request'
|
||||
|
||||
export const getPublicKey = gql`
|
||||
query {
|
||||
getPublicKey {
|
||||
publicKey
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -84,7 +84,8 @@ describe('validate Communities', () => {
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
`requestGetPublicKey with endpoint='http//localhost:5001/api/1_0/'...`,
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -114,12 +115,14 @@ describe('validate Communities', () => {
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
`requestGetPublicKey with endpoint='http//localhost:5001/api/1_0/'...`,
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_1 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
`requestGetPublicKey with endpoint='http//localhost:5001/api/1_1/'...`,
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_1/',
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -152,18 +155,21 @@ describe('validate Communities', () => {
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
`requestGetPublicKey with endpoint='http//localhost:5001/api/1_0/'...`,
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_1 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
`requestGetPublicKey with endpoint='http//localhost:5001/api/1_1/'...`,
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_1/',
|
||||
)
|
||||
})
|
||||
it('logs unsupported api for community with api 2_0 ', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
`Federation: dbCom: ${dbCom.id} with unsupported apiVersion=2_0; supported versions`,
|
||||
['1_0', '1_1'],
|
||||
'Federation: dbCom with unsupported apiVersion',
|
||||
dbCom.endPoint,
|
||||
'2_0',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -5,10 +5,7 @@ import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCom
|
||||
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
import { FederationClient as FederationClient_V1_0 } from './client/1_0/FederationClient'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { FederationClient as FederationClient_V1_1 } from './client/1_1/FederationClient'
|
||||
import { Client } from './client/Client'
|
||||
import { ApiVersionType } from './enum/apiVersionType'
|
||||
|
||||
export function startValidateCommunities(timerInterval: number): void {
|
||||
@ -35,50 +32,25 @@ export async function validateCommunities(): Promise<void> {
|
||||
logger.debug('Federation: dbCom', dbCom)
|
||||
const apiValueStrings: string[] = Object.values(ApiVersionType)
|
||||
logger.debug(`suppported ApiVersions=`, apiValueStrings)
|
||||
if (apiValueStrings.includes(dbCom.apiVersion)) {
|
||||
logger.debug(
|
||||
`Federation: validate publicKey for dbCom: ${dbCom.id} with apiVersion=${dbCom.apiVersion}`,
|
||||
)
|
||||
try {
|
||||
const pubKey = await invokeVersionedRequestGetPublicKey(dbCom)
|
||||
logger.info(
|
||||
'Federation: received publicKey from endpoint',
|
||||
if (!apiValueStrings.includes(dbCom.apiVersion)) {
|
||||
logger.warn('Federation: dbCom with unsupported apiVersion', dbCom.endPoint, dbCom.apiVersion)
|
||||
continue
|
||||
}
|
||||
try {
|
||||
const client = Client.getInstance(dbCom)
|
||||
const pubKey = await client?.getPublicKey()
|
||||
if (pubKey && pubKey === dbCom.publicKey.toString()) {
|
||||
await DbFederatedCommunity.update({ id: dbCom.id }, { verifiedAt: new Date() })
|
||||
logger.info('Federation: verified community', dbCom)
|
||||
} else {
|
||||
logger.warn(
|
||||
'Federation: received not matching publicKey:',
|
||||
pubKey,
|
||||
`${dbCom.endPoint}/${dbCom.apiVersion}`,
|
||||
dbCom.publicKey.toString(),
|
||||
)
|
||||
if (pubKey && pubKey === dbCom.publicKey.toString()) {
|
||||
logger.info(`Federation: matching publicKey: ${pubKey}`)
|
||||
await DbFederatedCommunity.update({ id: dbCom.id }, { verifiedAt: new Date() })
|
||||
logger.debug(`Federation: updated dbCom: ${JSON.stringify(dbCom)}`)
|
||||
} else {
|
||||
logger.warn(
|
||||
`Federation: received not matching publicKey -> received: ${
|
||||
pubKey ?? 'null'
|
||||
}, expected: ${dbCom.publicKey.toString()} `,
|
||||
)
|
||||
// DbCommunity.delete({ id: dbCom.id })
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`Error:`, err)
|
||||
}
|
||||
} else {
|
||||
logger.warn(
|
||||
`Federation: dbCom: ${dbCom.id} with unsupported apiVersion=${dbCom.apiVersion}; supported versions`,
|
||||
apiValueStrings,
|
||||
)
|
||||
} catch (err) {
|
||||
logger.error(`Error:`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function invokeVersionedRequestGetPublicKey(
|
||||
dbCom: DbFederatedCommunity,
|
||||
): Promise<string | undefined> {
|
||||
switch (dbCom.apiVersion) {
|
||||
case ApiVersionType.V1_0:
|
||||
return new FederationClient_V1_0().requestGetPublicKey(dbCom)
|
||||
case ApiVersionType.V1_1:
|
||||
return new FederationClient_V1_1().requestGetPublicKey(dbCom)
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,8 +23,8 @@ const setHeadersPlugin = {
|
||||
|
||||
const filterVariables = (variables: any) => {
|
||||
const vars = clonedeep(variables)
|
||||
if (vars.password) vars.password = '***'
|
||||
if (vars.passwordNew) vars.passwordNew = '***'
|
||||
if (vars && vars.password) vars.password = '***'
|
||||
if (vars && vars.passwordNew) vars.passwordNew = '***'
|
||||
return vars
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user