mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
first ongoing draft of community-authentication
This commit is contained in:
parent
53b2fe33a0
commit
841979a360
42
backend/src/federation/authenticateCommunities.ts
Normal file
42
backend/src/federation/authenticateCommunities.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/federation/client/1_0/AuthenticationClient'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { OpenConnectionArgs } from './client/1_0/model/OpenConnectionArgs'
|
||||
import { AuthenticationClientFactory } from './client/AuthenticationClientFactory'
|
||||
|
||||
export async function startCommunityAuthentication(
|
||||
foreignFedCom: DbFederatedCommunity,
|
||||
): Promise<void> {
|
||||
const homeCom = await DbCommunity.findOneByOrFail({ foreign: false })
|
||||
const homeFedCom = await DbFederatedCommunity.findOneByOrFail({
|
||||
foreign: false,
|
||||
apiVersion: CONFIG.FEDERATION_BACKEND_SEND_ON_API,
|
||||
})
|
||||
const foreignCom = await DbCommunity.findOneByOrFail({ publicKey: foreignFedCom.publicKey })
|
||||
if (foreignCom && foreignCom.communityUuid === null && foreignCom.authenticatedAt === null) {
|
||||
try {
|
||||
const client = AuthenticationClientFactory.getInstance(homeFedCom)
|
||||
// eslint-disable-next-line camelcase
|
||||
if (client instanceof V1_0_AuthenticationClient) {
|
||||
const args = new OpenConnectionArgs()
|
||||
args.publicKey = homeCom.publicKey.toString('hex')
|
||||
// TODO encrypt url with foreignCom.publicKey and sign it with homeCom.privateKey
|
||||
args.url = homeFedCom.endPoint.endsWith('/')
|
||||
? homeFedCom.endPoint
|
||||
: homeFedCom.endPoint + '/' + homeFedCom.apiVersion
|
||||
if (await client.openConnection(args)) {
|
||||
logger.info(`Authentication: successful initiated at community:`, foreignFedCom.endPoint)
|
||||
} else {
|
||||
logger.error(`Authentication: can't initiate at community:`, foreignFedCom.endPoint)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`Error:`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
50
backend/src/federation/client/1_0/AuthenticationClient.ts
Normal file
50
backend/src/federation/client/1_0/AuthenticationClient.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { OpenConnectionArgs } from './model/OpenConnectionArgs'
|
||||
import { openConnection } from './query/openConnection'
|
||||
|
||||
export class AuthenticationClient {
|
||||
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,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async openConnection(args: OpenConnectionArgs): Promise<boolean | undefined> {
|
||||
logger.debug('Authentication: openConnection with endpoint', this.endpoint)
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { data } = await this.client.rawRequest(openConnection, { args })
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (!data?.openConnection) {
|
||||
logger.warn(
|
||||
'Authentication: openConnection without response data from endpoint',
|
||||
this.endpoint,
|
||||
)
|
||||
return false
|
||||
}
|
||||
logger.debug(
|
||||
'Authentication: openConnection successfully started with endpoint',
|
||||
this.endpoint,
|
||||
)
|
||||
return true
|
||||
} catch (err) {
|
||||
logger.error('Authentication: error on openConnection', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class OpenConnectionArgs {
|
||||
@Field(() => String)
|
||||
publicKey: string
|
||||
|
||||
@Field(() => String)
|
||||
url: string
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
import { gql } from 'graphql-request'
|
||||
|
||||
export const openConnection = gql`
|
||||
mutation ($args: OpenConnectionArgs!) {
|
||||
openConnection(data: $args)
|
||||
}
|
||||
`
|
||||
@ -0,0 +1,5 @@
|
||||
// eslint-disable-next-line camelcase
|
||||
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/federation/client/1_0/AuthenticationClient'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export class AuthenticationClient extends V1_0_AuthenticationClient {}
|
||||
62
backend/src/federation/client/AuthenticationClientFactory.ts
Normal file
62
backend/src/federation/client/AuthenticationClientFactory.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/federation/client/1_0/AuthenticationClient'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { AuthenticationClient as V1_1_AuthenticationClient } from '@/federation/client/1_1/AuthenticationClient'
|
||||
import { ApiVersionType } from '@/federation/enum/apiVersionType'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
type AuthenticationClient = V1_0_AuthenticationClient | V1_1_AuthenticationClient
|
||||
|
||||
interface AuthenticationClientInstance {
|
||||
id: number
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
client: AuthenticationClient
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||
export class AuthenticationClientFactory {
|
||||
private static instanceArray: AuthenticationClientInstance[] = []
|
||||
|
||||
/**
|
||||
* 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 createAuthenticationClient = (dbCom: DbFederatedCommunity) => {
|
||||
switch (dbCom.apiVersion) {
|
||||
case ApiVersionType.V1_0:
|
||||
return new V1_0_AuthenticationClient(dbCom)
|
||||
case ApiVersionType.V1_1:
|
||||
return new V1_1_AuthenticationClient(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): AuthenticationClient | null {
|
||||
const instance = AuthenticationClientFactory.instanceArray.find(
|
||||
(instance) => instance.id === dbCom.id,
|
||||
)
|
||||
if (instance) {
|
||||
return instance.client
|
||||
}
|
||||
const client = AuthenticationClientFactory.createAuthenticationClient(dbCom)
|
||||
if (client) {
|
||||
AuthenticationClientFactory.instanceArray.push({
|
||||
id: dbCom.id,
|
||||
client,
|
||||
} as AuthenticationClientInstance)
|
||||
}
|
||||
return client
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@ import { PublicCommunityInfo } from '@/federation/client/1_0/model/PublicCommuni
|
||||
import { FederationClientFactory } from '@/federation/client/FederationClientFactory'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { startCommunityAuthentication } from './authenticateCommunities'
|
||||
import { ApiVersionType } from './enum/apiVersionType'
|
||||
|
||||
export async function startValidateCommunities(timerInterval: number): Promise<void> {
|
||||
@ -40,7 +41,11 @@ export async function validateCommunities(): Promise<void> {
|
||||
const apiValueStrings: string[] = Object.values(ApiVersionType)
|
||||
logger.debug(`suppported ApiVersions=`, apiValueStrings)
|
||||
if (!apiValueStrings.includes(dbCom.apiVersion)) {
|
||||
logger.warn('Federation: dbCom with unsupported apiVersion', dbCom.endPoint, dbCom.apiVersion)
|
||||
logger.debug(
|
||||
'Federation: dbCom with unsupported apiVersion',
|
||||
dbCom.endPoint,
|
||||
dbCom.apiVersion,
|
||||
)
|
||||
continue
|
||||
}
|
||||
try {
|
||||
@ -54,7 +59,8 @@ export async function validateCommunities(): Promise<void> {
|
||||
const pubComInfo = await client.getPublicCommunityInfo()
|
||||
if (pubComInfo) {
|
||||
await writeForeignCommunity(dbCom, pubComInfo)
|
||||
logger.info(`Federation: write publicInfo of community: name=${pubComInfo.name}`)
|
||||
void startCommunityAuthentication(dbCom)
|
||||
logger.debug(`Federation: write publicInfo of community: name=${pubComInfo.name}`)
|
||||
} else {
|
||||
logger.warn('Federation: missing result of getPublicCommunityInfo')
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 328 KiB |
@ -16,7 +16,6 @@
|
||||
"lint": "eslint --max-warnings=0 --ext .js,.ts ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/uuid": "8.3.4",
|
||||
"apollo-server-express": "^2.25.2",
|
||||
"await-semaphore": "0.1.3",
|
||||
"class-validator": "^0.13.2",
|
||||
@ -26,9 +25,11 @@
|
||||
"dotenv": "10.0.0",
|
||||
"express": "4.17.1",
|
||||
"graphql": "15.5.1",
|
||||
"graphql-request": "^5.0.0",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"log4js": "^6.7.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"sodium-native": "^4.0.4",
|
||||
"type-graphql": "^1.1.1",
|
||||
"uuid": "8.3.2"
|
||||
},
|
||||
@ -37,6 +38,8 @@
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/lodash.clonedeep": "^4.5.6",
|
||||
"@types/node": "^16.10.3",
|
||||
"@types/sodium-native": "^2.3.7",
|
||||
"@types/uuid": "8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"@typescript-eslint/parser": "^5.57.1",
|
||||
"apollo-server-testing": "2.25.2",
|
||||
|
||||
70
federation/src/client/1_0/AuthenticationClient.ts
Normal file
70
federation/src/client/1_0/AuthenticationClient.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import { federationLogger as logger } from '@/server/logger'
|
||||
|
||||
import { OpenConnectionCallbackArgs } from '@/graphql/api/1_0/model/OpenConnectionCallbackArgs'
|
||||
import { openConnectionCallback } from './query/openConnectionCallback'
|
||||
import { AuthenticationArgs } from '@/graphql/api/1_0/model/AuthenticationArgs'
|
||||
import { authenticate } from './query/authenticate'
|
||||
|
||||
|
||||
export class AuthenticationClient {
|
||||
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,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async openConnectionCallback(args: OpenConnectionCallbackArgs): Promise<boolean | undefined> {
|
||||
logger.debug('Authentication: openConnectionCallback with endpoint', this.endpoint, args)
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { data } = await this.client.rawRequest(openConnectionCallback, { args })
|
||||
if (!data?.openConnectionCallback) {
|
||||
logger.warn(
|
||||
'Authentication: openConnectionCallback without response data from endpoint',
|
||||
this.endpoint,
|
||||
)
|
||||
return false
|
||||
}
|
||||
logger.debug(
|
||||
'Authentication: openConnectionCallback successfully started with endpoint',
|
||||
this.endpoint,
|
||||
)
|
||||
return true
|
||||
} catch (err) {
|
||||
logger.error('Authentication: error on openConnectionCallback', err)
|
||||
}
|
||||
}
|
||||
|
||||
async authenticate(args: AuthenticationArgs): Promise<string | undefined> {
|
||||
logger.debug('Authentication: authenticate with endpoint=', this.endpoint)
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { data } = await this.client.rawRequest(authenticate, {})
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (!data?.authenticate) {
|
||||
logger.warn(
|
||||
'Authentication: authenticate without response data from endpoint',
|
||||
this.endpoint,
|
||||
)
|
||||
return
|
||||
}
|
||||
const
|
||||
} catch (err) {
|
||||
logger.error('Authentication: authenticate failed for endpoint', this.endpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
7
federation/src/client/1_0/query/authenticate.ts
Normal file
7
federation/src/client/1_0/query/authenticate.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { gql } from 'graphql-request'
|
||||
|
||||
export const authenticate = gql`
|
||||
mutation ($args: AuthenticateArgs!) {
|
||||
authenticate(data: $args)
|
||||
}
|
||||
`
|
||||
@ -0,0 +1,7 @@
|
||||
import { gql } from 'graphql-request'
|
||||
|
||||
export const openConnectionCallback = gql`
|
||||
mutation ($args: OpenConnectionCallbackArgs!) {
|
||||
openConnectionCallback(data: $args)
|
||||
}
|
||||
`
|
||||
5
federation/src/client/1_1/AuthenticationClient.ts
Normal file
5
federation/src/client/1_1/AuthenticationClient.ts
Normal file
@ -0,0 +1,5 @@
|
||||
// eslint-disable-next-line camelcase
|
||||
import { AuthenticationClient as V1_0_AuthenticationClient } from '../1_0/AuthenticationClient'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export class AuthenticationClient extends V1_0_AuthenticationClient {}
|
||||
61
federation/src/client/AuthenticationClientFactory.ts
Normal file
61
federation/src/client/AuthenticationClientFactory.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { AuthenticationClient as V1_0_AuthenticationClient } from './1_0/AuthenticationClient'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { AuthenticationClient as V1_1_AuthenticationClient } from './1_1/AuthenticationClient'
|
||||
import { ApiVersionType } from './enum/apiVersionType'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
type AuthenticationClient = V1_0_AuthenticationClient | V1_1_AuthenticationClient
|
||||
|
||||
interface AuthenticationClientInstance {
|
||||
id: number
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
client: AuthenticationClient
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||
export class AuthenticationClientFactory {
|
||||
private static instanceArray: AuthenticationClientInstance[] = []
|
||||
|
||||
/**
|
||||
* 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 createAuthenticationClient = (dbCom: DbFederatedCommunity) => {
|
||||
switch (dbCom.apiVersion) {
|
||||
case ApiVersionType.V1_0:
|
||||
return new V1_0_AuthenticationClient(dbCom)
|
||||
case ApiVersionType.V1_1:
|
||||
return new V1_1_AuthenticationClient(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): AuthenticationClient | null {
|
||||
const instance = AuthenticationClientFactory.instanceArray.find(
|
||||
(instance) => instance.id === dbCom.id,
|
||||
)
|
||||
if (instance) {
|
||||
return instance.client
|
||||
}
|
||||
const client = AuthenticationClientFactory.createAuthenticationClient(dbCom)
|
||||
if (client) {
|
||||
AuthenticationClientFactory.instanceArray.push({
|
||||
id: dbCom.id,
|
||||
client,
|
||||
} as AuthenticationClientInstance)
|
||||
}
|
||||
return client
|
||||
}
|
||||
}
|
||||
4
federation/src/client/enum/ApiVersionType.ts
Normal file
4
federation/src/client/enum/ApiVersionType.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export enum ApiVersionType {
|
||||
V1_0 = '1_0',
|
||||
V1_1 = '1_1',
|
||||
}
|
||||
10
federation/src/graphql/api/1_0/model/AuthenticationArgs.ts
Normal file
10
federation/src/graphql/api/1_0/model/AuthenticationArgs.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class AuthenticationArgs {
|
||||
@Field(() => String)
|
||||
oneTimeCode: string
|
||||
|
||||
@Field(() => String)
|
||||
uuid: string
|
||||
}
|
||||
10
federation/src/graphql/api/1_0/model/OpenConnectionArgs.ts
Normal file
10
federation/src/graphql/api/1_0/model/OpenConnectionArgs.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class OpenConnectionArgs {
|
||||
@Field(() => String)
|
||||
publicKey: string
|
||||
|
||||
@Field(() => String)
|
||||
url: string
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class OpenConnectionCallbackArgs {
|
||||
@Field(() => String)
|
||||
oneTimeCode: string
|
||||
|
||||
@Field(() => String)
|
||||
publicKey: string
|
||||
|
||||
@Field(() => String)
|
||||
url: string
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { Arg, Mutation, Resolver } from 'type-graphql'
|
||||
import { federationLogger as logger } from '@/server/logger'
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { OpenConnectionArgs } from '../model/OpenConnectionArgs'
|
||||
import {
|
||||
startOpenConnectionCallback,
|
||||
startOpenConnectionRedirect,
|
||||
} from '../util/authenticateCommunity'
|
||||
import { OpenConnectionCallbackArgs } from '../model/OpenConnectionCallbackArgs'
|
||||
import { ApiVersionType } from '@/client/enum/apiVersionType'
|
||||
|
||||
@Resolver()
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export class AuthenticationResolver {
|
||||
@Mutation(() => Boolean)
|
||||
async openConnection(
|
||||
@Arg('data')
|
||||
args: OpenConnectionArgs,
|
||||
): Promise<boolean> {
|
||||
logger.debug(`Authentication: openConnection() via apiVersion=1_0 ...`, args)
|
||||
|
||||
// first find with args.publicKey the community, which starts openConnection request
|
||||
const requestedCom = await DbCommunity.findOneBy({
|
||||
publicKey: Buffer.from(args.publicKey),
|
||||
})
|
||||
if (!requestedCom) {
|
||||
throw new LogError(`unknown requesting community with publicKey`, args.publicKey)
|
||||
}
|
||||
void startOpenConnectionRedirect(args, requestedCom, ApiVersionType.V1_0)
|
||||
return true
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
async openConnectionCallback(
|
||||
@Arg('data')
|
||||
args: OpenConnectionCallbackArgs,
|
||||
): Promise<boolean> {
|
||||
logger.debug(`Authentication: openConnectionCallback() via apiVersion=1_0 ...`, args)
|
||||
// first find with args.publicKey the community, which invokes openConnectionCallback
|
||||
const callbackCom = await DbCommunity.findOneBy({
|
||||
publicKey: Buffer.from(args.publicKey),
|
||||
})
|
||||
if (!callbackCom) {
|
||||
throw new LogError(`unknown callback community with publicKey`, args.publicKey)
|
||||
}
|
||||
void startOpenConnectionCallback(args, callbackCom)
|
||||
return true
|
||||
}
|
||||
}
|
||||
75
federation/src/graphql/api/1_0/util/authenticateCommunity.ts
Normal file
75
federation/src/graphql/api/1_0/util/authenticateCommunity.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { OpenConnectionArgs } from '../model/OpenConnectionArgs'
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
import { federationLogger as logger } from '@/server/logger'
|
||||
import { OpenConnectionCallbackArgs } from '../model/OpenConnectionCallbackArgs'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { randombytes_random } from 'sodium-native'
|
||||
import { AuthenticationClientFactory } from '@/client/AuthenticationClientFactory'
|
||||
import { ApiVersionType } from '@/client/enum/apiVersionType'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/client/1_0/AuthenticationClient'
|
||||
|
||||
export async function startOpenConnectionRedirect(
|
||||
args: OpenConnectionArgs,
|
||||
requestedCom: DbCommunity,
|
||||
api: ApiVersionType,
|
||||
): Promise<void> {
|
||||
logger.debug(
|
||||
`Authentication: startOpenConnectionRedirect()...`,
|
||||
args.publicKey,
|
||||
args.url,
|
||||
requestedCom,
|
||||
)
|
||||
try {
|
||||
// TODO verify signing of args.url with requestedCom.publicKey and decrypt with homeCom.privateKey
|
||||
const homeCom = await DbCommunity.findOneByOrFail({ foreign: false })
|
||||
const homeFedCom = await DbFederatedCommunity.findOneByOrFail({
|
||||
foreign: false,
|
||||
apiVersion: api,
|
||||
})
|
||||
const oneTimeCode = randombytes_random()
|
||||
// store oneTimeCode in requestedCom.community_uuid for authenticate-request-identifier
|
||||
requestedCom.communityUuid = oneTimeCode.toString()
|
||||
await DbCommunity.save(requestedCom)
|
||||
|
||||
const client = AuthenticationClientFactory.getInstance(homeFedCom)
|
||||
// eslint-disable-next-line camelcase
|
||||
if (client instanceof V1_0_AuthenticationClient) {
|
||||
const callbackArgs = new OpenConnectionCallbackArgs()
|
||||
callbackArgs.oneTimeCode = oneTimeCode.toString()
|
||||
callbackArgs.publicKey = homeCom.publicKey.toString('hex')
|
||||
// TODO signing of callbackArgs.url with requestedCom.publicKey and decrypt with homeCom.privateKey
|
||||
callbackArgs.url = homeFedCom.endPoint.endsWith('/')
|
||||
? homeFedCom.endPoint
|
||||
: homeFedCom.endPoint + '/' + homeFedCom.apiVersion
|
||||
if (await client.openConnectionCallback(callbackArgs)) {
|
||||
logger.debug('Authentication: startOpenConnectionRedirect() successful:', callbackArgs)
|
||||
} else {
|
||||
logger.error('Authentication: startOpenConnectionRedirect() failed:', callbackArgs)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Authentication: error in startOpenConnectionRedirect:', err)
|
||||
}
|
||||
}
|
||||
|
||||
export async function startOpenConnectionCallback(
|
||||
args: OpenConnectionCallbackArgs,
|
||||
callbackCom: DbCommunity,
|
||||
): Promise<void> {
|
||||
logger.debug(
|
||||
`Authentication: startOpenConnectionCallback()...`,
|
||||
args.publicKey,
|
||||
args.url,
|
||||
callbackCom,
|
||||
)
|
||||
try {
|
||||
// TODO verify signing of args.url with requestedCom.publicKey and decrypt with homeCom.privateKey
|
||||
const homeCom = await DbCommunity.findOneByOrFail({ foreign: false })
|
||||
|
||||
|
||||
} catch (err) {
|
||||
logger.error('Authentication: error in startOpenConnectionCallback:', err)
|
||||
}
|
||||
}
|
||||
@ -374,6 +374,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.42.0.tgz#484a1d638de2911e6f5a30c12f49c7e4a3270fb6"
|
||||
integrity sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==
|
||||
|
||||
"@graphql-typed-document-node/core@^3.1.1":
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
|
||||
integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==
|
||||
|
||||
"@humanwhocodes/config-array@^0.11.10":
|
||||
version "0.11.10"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2"
|
||||
@ -1052,6 +1057,13 @@
|
||||
"@types/mime" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/sodium-native@^2.3.7":
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/sodium-native/-/sodium-native-2.3.7.tgz#fdcbd026e9a730e574e69ccb85fd36fd50220a8c"
|
||||
integrity sha512-VlwblVfVHizegm0QJX0Hgna+w7P9z5Gy+LYkO7EWlOj7tew2kj1csq8ziGMiruL+dm/WjRwaoGuE6STV+0bN2g==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/stack-utils@^2.0.0":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
||||
@ -1948,6 +1960,13 @@ cross-env@^7.0.3:
|
||||
dependencies:
|
||||
cross-spawn "^7.0.1"
|
||||
|
||||
cross-fetch@^3.1.5:
|
||||
version "3.1.8"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82"
|
||||
integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==
|
||||
dependencies:
|
||||
node-fetch "^2.6.12"
|
||||
|
||||
cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||
@ -2678,6 +2697,11 @@ express@^4.17.1:
|
||||
utils-merge "1.0.1"
|
||||
vary "~1.1.2"
|
||||
|
||||
extract-files@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a"
|
||||
integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==
|
||||
|
||||
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
@ -3017,6 +3041,16 @@ graphql-query-complexity@^0.7.0:
|
||||
dependencies:
|
||||
lodash.get "^4.4.2"
|
||||
|
||||
graphql-request@^5.0.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-5.2.0.tgz#a05fb54a517d91bb2d7aefa17ade4523dc5ebdca"
|
||||
integrity sha512-pLhKIvnMyBERL0dtFI3medKqWOz/RhHdcgbZ+hMMIb32mEPa5MJSzS4AuXxfI4sRAu6JVVk5tvXuGfCWl9JYWQ==
|
||||
dependencies:
|
||||
"@graphql-typed-document-node/core" "^3.1.1"
|
||||
cross-fetch "^3.1.5"
|
||||
extract-files "^9.0.0"
|
||||
form-data "^3.0.0"
|
||||
|
||||
graphql-subscriptions@^1.0.0, graphql-subscriptions@^1.1.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/graphql-subscriptions/-/graphql-subscriptions-1.2.1.tgz#2142b2d729661ddf967b7388f7cf1dd4cf2e061d"
|
||||
@ -4228,6 +4262,18 @@ node-fetch@^2.6.1:
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-fetch@^2.6.12:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
|
||||
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-gyp-build@^4.6.0:
|
||||
version "4.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.1.tgz#24b6d075e5e391b8d5539d98c7fc5c210cac8a3e"
|
||||
integrity sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==
|
||||
|
||||
node-int64@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
|
||||
@ -4943,6 +4989,13 @@ slash@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
|
||||
integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
|
||||
|
||||
sodium-native@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/sodium-native/-/sodium-native-4.0.4.tgz#561b7c39c97789f8202d6fd224845fe2e8cd6879"
|
||||
integrity sha512-faqOKw4WQKK7r/ybn6Lqo1F9+L5T6NlBJJYvpxbZPetpWylUVqz449mvlwIBKBqxEHbWakWuOlUt8J3Qpc4sWw==
|
||||
dependencies:
|
||||
node-gyp-build "^4.6.0"
|
||||
|
||||
source-map-support@^0.5.6:
|
||||
version "0.5.21"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user