mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
Merge branch 'dlt_connector_try_dci' of github.com:gradido/gradido into dlt_connector_try_dci
This commit is contained in:
commit
ae276747d5
60
backend/src/federation/authenticateCommunities.ts
Normal file
60
backend/src/federation/authenticateCommunities.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
import { validate as validateUUID, version as versionUUID } from 'uuid'
|
||||
|
||||
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 })
|
||||
logger.debug(
|
||||
'Authentication: started with foreignFedCom:',
|
||||
foreignFedCom.endPoint,
|
||||
foreignFedCom.publicKey.toString('hex'),
|
||||
)
|
||||
// check if communityUuid is a valid v4Uuid and not still a temporary onetimecode
|
||||
if (
|
||||
foreignCom &&
|
||||
((foreignCom.communityUuid === null && foreignCom.authenticatedAt === null) ||
|
||||
(foreignCom.communityUuid !== null &&
|
||||
!validateUUID(foreignCom.communityUuid) &&
|
||||
versionUUID(foreignCom.communityUuid) !== 4))
|
||||
) {
|
||||
try {
|
||||
const client = AuthenticationClientFactory.getInstance(foreignFedCom)
|
||||
// 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
|
||||
logger.debug(
|
||||
'Authentication: before client.openConnection() args:',
|
||||
homeCom.publicKey.toString('hex'),
|
||||
args.url,
|
||||
)
|
||||
if (await client.openConnection(args)) {
|
||||
logger.debug(`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: 'POST',
|
||||
jsonSerializer: {
|
||||
parse: JSON.parse,
|
||||
stringify: JSON.stringify,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async openConnection(args: OpenConnectionArgs): Promise<boolean | undefined> {
|
||||
logger.debug(`Authentication: openConnection at ${this.endpoint} for args:`, args)
|
||||
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 { Field, InputType } from 'type-graphql'
|
||||
|
||||
@InputType()
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -61,6 +61,7 @@ describe('validate Communities', () => {
|
||||
|
||||
describe('with one Community of api 1_0 but missing pubKey response', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
@ -82,7 +83,7 @@ describe('validate Communities', () => {
|
||||
overwrite: ['end_point', 'last_announced_at'],
|
||||
})
|
||||
.execute()
|
||||
jest.clearAllMocks()
|
||||
// jest.clearAllMocks()
|
||||
await validateCommunities()
|
||||
})
|
||||
|
||||
@ -99,6 +100,7 @@ describe('validate Communities', () => {
|
||||
|
||||
describe('with one Community of api 1_0 and not matching pubKey', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
@ -157,7 +159,7 @@ describe('validate Communities', () => {
|
||||
})
|
||||
.execute()
|
||||
*/
|
||||
jest.clearAllMocks()
|
||||
// jest.clearAllMocks()
|
||||
await validateCommunities()
|
||||
})
|
||||
|
||||
@ -171,7 +173,7 @@ describe('validate Communities', () => {
|
||||
)
|
||||
})
|
||||
it('logs not matching publicKeys', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
expect(logger.debug).toBeCalledWith(
|
||||
'Federation: received not matching publicKey:',
|
||||
'somePubKey',
|
||||
expect.stringMatching('11111111111111111111111111111111'),
|
||||
@ -180,6 +182,7 @@ describe('validate Communities', () => {
|
||||
})
|
||||
describe('with one Community of api 1_0 and matching pubKey', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
@ -208,7 +211,7 @@ describe('validate Communities', () => {
|
||||
})
|
||||
.execute()
|
||||
await DbFederatedCommunity.update({}, { verifiedAt: null })
|
||||
jest.clearAllMocks()
|
||||
// jest.clearAllMocks()
|
||||
await validateCommunities()
|
||||
})
|
||||
|
||||
@ -277,7 +280,7 @@ describe('validate Communities', () => {
|
||||
.execute()
|
||||
|
||||
await DbFederatedCommunity.update({}, { verifiedAt: null })
|
||||
jest.clearAllMocks()
|
||||
// jest.clearAllMocks()
|
||||
await validateCommunities()
|
||||
})
|
||||
it('logs two communities found', () => {
|
||||
@ -299,6 +302,18 @@ describe('validate Communities', () => {
|
||||
describe('with three Communities of api 1_0, 1_1 and 2_0', () => {
|
||||
let dbCom: DbFederatedCommunity
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return {
|
||||
data: {
|
||||
getPublicKey: {
|
||||
publicKey: '11111111111111111111111111111111',
|
||||
},
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
const variables3 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
apiVersion: '2_0',
|
||||
@ -319,7 +334,7 @@ describe('validate Communities', () => {
|
||||
where: { publicKey: variables3.publicKey, apiVersion: variables3.apiVersion },
|
||||
})
|
||||
await DbFederatedCommunity.update({}, { verifiedAt: null })
|
||||
jest.clearAllMocks()
|
||||
// jest.clearAllMocks()
|
||||
await validateCommunities()
|
||||
})
|
||||
it('logs three community found', () => {
|
||||
@ -338,7 +353,7 @@ describe('validate Communities', () => {
|
||||
)
|
||||
})
|
||||
it('logs unsupported api for community with api 2_0 ', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
expect(logger.debug).toBeCalledWith(
|
||||
'Federation: dbCom with unsupported apiVersion',
|
||||
dbCom.endPoint,
|
||||
'2_0',
|
||||
|
||||
@ -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 {
|
||||
@ -50,16 +55,17 @@ export async function validateCommunities(): Promise<void> {
|
||||
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 with:`, dbCom.endPoint)
|
||||
logger.debug(`Federation: verified community with:`, dbCom.endPoint)
|
||||
const pubComInfo = await client.getPublicCommunityInfo()
|
||||
if (pubComInfo) {
|
||||
await writeForeignCommunity(dbCom, pubComInfo)
|
||||
logger.info(`Federation: write publicInfo of community: name=${pubComInfo.name}`)
|
||||
await startCommunityAuthentication(dbCom)
|
||||
logger.debug(`Federation: write publicInfo of community: name=${pubComInfo.name}`)
|
||||
} else {
|
||||
logger.warn('Federation: missing result of getPublicCommunityInfo')
|
||||
logger.debug('Federation: missing result of getPublicCommunityInfo')
|
||||
}
|
||||
} else {
|
||||
logger.warn(
|
||||
logger.debug(
|
||||
'Federation: received not matching publicKey:',
|
||||
pubKey,
|
||||
dbCom.publicKey.toString(),
|
||||
|
||||
@ -6,11 +6,11 @@ import { ObjectType, Field, Int } from 'type-graphql'
|
||||
export class UserAdmin {
|
||||
constructor(user: User, creation: Decimal[], hasElopage: boolean, emailConfirmationSend: string) {
|
||||
this.userId = user.id
|
||||
this.email = user.emailContact.email
|
||||
this.email = user.emailContact?.email
|
||||
this.firstName = user.firstName
|
||||
this.lastName = user.lastName
|
||||
this.creation = creation
|
||||
this.emailChecked = user.emailContact.emailChecked
|
||||
this.emailChecked = user.emailContact?.emailChecked
|
||||
this.hasElopage = hasElopage
|
||||
this.deletedAt = user.deletedAt
|
||||
this.emailConfirmationSend = emailConfirmationSend
|
||||
@ -20,8 +20,8 @@ export class UserAdmin {
|
||||
@Field(() => Int)
|
||||
userId: number
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
@Field(() => String, { nullable: true })
|
||||
email: string | null
|
||||
|
||||
@Field(() => String)
|
||||
firstName: string
|
||||
@ -32,8 +32,8 @@ export class UserAdmin {
|
||||
@Field(() => [Decimal])
|
||||
creation: Decimal[]
|
||||
|
||||
@Field(() => Boolean)
|
||||
emailChecked: boolean
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
emailChecked: boolean | null
|
||||
|
||||
@Field(() => Boolean)
|
||||
hasElopage: boolean
|
||||
|
||||
@ -680,18 +680,18 @@ export class UserResolver {
|
||||
const adminUsers = await Promise.all(
|
||||
users.map(async (user) => {
|
||||
let emailConfirmationSend = ''
|
||||
if (!user.emailContact.emailChecked) {
|
||||
if (user.emailContact.updatedAt) {
|
||||
emailConfirmationSend = user.emailContact.updatedAt.toISOString()
|
||||
if (!user.emailContact?.emailChecked) {
|
||||
if (user.emailContact?.updatedAt) {
|
||||
emailConfirmationSend = user.emailContact?.updatedAt.toISOString()
|
||||
} else {
|
||||
emailConfirmationSend = user.emailContact.createdAt.toISOString()
|
||||
emailConfirmationSend = user.emailContact?.createdAt.toISOString()
|
||||
}
|
||||
}
|
||||
const userCreations = creations.find((c) => c.id === user.id)
|
||||
const adminUser = new UserAdmin(
|
||||
user,
|
||||
userCreations ? userCreations.creations : FULL_CREATION_AVAILABLE,
|
||||
await hasElopageBuys(user.emailContact.email),
|
||||
await hasElopageBuys(user.emailContact?.email),
|
||||
emailConfirmationSend,
|
||||
)
|
||||
return adminUser
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 328 KiB |
@ -6,7 +6,7 @@ module.exports = {
|
||||
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
lines: 77,
|
||||
lines: 68,
|
||||
},
|
||||
},
|
||||
setupFiles: ['<rootDir>/test/testSetup.ts'],
|
||||
|
||||
@ -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": "^3.3.0",
|
||||
"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.5",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"@typescript-eslint/parser": "^5.57.1",
|
||||
"apollo-server-testing": "2.25.2",
|
||||
@ -51,9 +54,10 @@
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-security": "^1.7.1",
|
||||
"eslint-plugin-type-graphql": "^1.0.0",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"jest": "^27.2.4",
|
||||
"nodemon": "^2.0.7",
|
||||
"prettier": "^2.3.1",
|
||||
"prettier": "^2.8.7",
|
||||
"ts-jest": "27.0.5",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.1.1",
|
||||
|
||||
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: 'POST',
|
||||
jsonSerializer: {
|
||||
parse: JSON.parse,
|
||||
stringify: JSON.stringify,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async openConnectionCallback(args: OpenConnectionCallbackArgs): Promise<boolean> {
|
||||
logger.debug('Authentication: openConnectionCallback with endpoint', this.endpoint, args)
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
|
||||
const { data } = await this.client.rawRequest<any>(openConnectionCallback, { args })
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (data && 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)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
async authenticate(args: AuthenticationArgs): Promise<string | null> {
|
||||
logger.debug('Authentication: authenticate with endpoint=', this.endpoint)
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
|
||||
const { data } = await this.client.rawRequest<any>(authenticate, { args })
|
||||
logger.debug('Authentication: after authenticate: data:', data)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
const authUuid: string = data?.authenticate
|
||||
if (authUuid) {
|
||||
logger.debug('Authentication: received authenticated uuid', authUuid)
|
||||
return authUuid
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Authentication: authenticate failed for endpoint', this.endpoint)
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
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: AuthenticationArgs!) {
|
||||
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 { Field, InputType } from 'type-graphql'
|
||||
|
||||
@InputType()
|
||||
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 { Field, InputType } from 'type-graphql'
|
||||
|
||||
@InputType()
|
||||
export class OpenConnectionArgs {
|
||||
@Field(() => String)
|
||||
publicKey: string
|
||||
|
||||
@Field(() => String)
|
||||
url: string
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import { Field, InputType } from 'type-graphql'
|
||||
|
||||
@InputType()
|
||||
export class OpenConnectionCallbackArgs {
|
||||
@Field(() => String)
|
||||
oneTimeCode: string
|
||||
|
||||
@Field(() => String)
|
||||
url: string
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
// 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 { FederatedCommunity as DbFedCommunity } from '@entity/FederatedCommunity'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { OpenConnectionArgs } from '../model/OpenConnectionArgs'
|
||||
import { startAuthentication, startOpenConnectionCallback } from '../util/authenticateCommunity'
|
||||
import { OpenConnectionCallbackArgs } from '../model/OpenConnectionCallbackArgs'
|
||||
import { CONFIG } from '@/config'
|
||||
import { AuthenticationArgs } from '../model/AuthenticationArgs'
|
||||
|
||||
@Resolver()
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export class AuthenticationResolver {
|
||||
@Mutation(() => Boolean)
|
||||
async openConnection(
|
||||
@Arg('data')
|
||||
args: OpenConnectionArgs,
|
||||
): Promise<boolean> {
|
||||
const pubKeyBuf = Buffer.from(args.publicKey, 'hex')
|
||||
logger.debug(`Authentication: openConnection() via apiVersion=1_0:`, args)
|
||||
|
||||
// first find with args.publicKey the community 'comA', which starts openConnection request
|
||||
const comA = await DbCommunity.findOneBy({
|
||||
publicKey: pubKeyBuf, // Buffer.from(args.publicKey),
|
||||
})
|
||||
if (!comA) {
|
||||
throw new LogError(`unknown requesting community with publicKey`, pubKeyBuf.toString('hex'))
|
||||
}
|
||||
logger.debug(`Authentication: found requestedCom:`, comA)
|
||||
// no await to respond immediatly and invoke callback-request asynchron
|
||||
void startOpenConnectionCallback(args, comA, CONFIG.FEDERATION_API)
|
||||
return true
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
async openConnectionCallback(
|
||||
@Arg('data')
|
||||
args: OpenConnectionCallbackArgs,
|
||||
): Promise<boolean> {
|
||||
logger.debug(`Authentication: openConnectionCallback() via apiVersion=1_0 ...`, args)
|
||||
// TODO decrypt args.url with homeCom.privateKey and verify signing with callbackFedCom.publicKey
|
||||
const endPoint = args.url.slice(0, args.url.lastIndexOf('/') + 1)
|
||||
const apiVersion = args.url.slice(args.url.lastIndexOf('/') + 1, args.url.length)
|
||||
logger.debug(`Authentication: search fedComB per:`, endPoint, apiVersion)
|
||||
const fedComB = await DbFedCommunity.findOneBy({ endPoint, apiVersion })
|
||||
if (!fedComB) {
|
||||
throw new LogError(`unknown callback community with url`, args.url)
|
||||
}
|
||||
logger.debug(`Authentication: found fedComB and start authentication:`, fedComB)
|
||||
// no await to respond immediatly and invoke authenticate-request asynchron
|
||||
void startAuthentication(args.oneTimeCode, fedComB)
|
||||
return true
|
||||
}
|
||||
|
||||
@Mutation(() => String)
|
||||
async authenticate(
|
||||
@Arg('data')
|
||||
args: AuthenticationArgs,
|
||||
): Promise<string | null> {
|
||||
logger.debug(`Authentication: authenticate() via apiVersion=1_0 ...`, args)
|
||||
const authCom = await DbCommunity.findOneByOrFail({ communityUuid: args.oneTimeCode })
|
||||
logger.debug('Authentication: found authCom:', authCom)
|
||||
if (authCom) {
|
||||
// TODO decrypt args.uuid with authCom.publicKey
|
||||
authCom.communityUuid = args.uuid
|
||||
authCom.authenticatedAt = new Date()
|
||||
await DbCommunity.save(authCom)
|
||||
logger.debug('Authentication: store authCom.uuid successfully:', authCom)
|
||||
const homeCom = await DbCommunity.findOneByOrFail({ foreign: false })
|
||||
// TODO encrypt homeCom.uuid with homeCom.privateKey
|
||||
if (homeCom.communityUuid) {
|
||||
return homeCom.communityUuid
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@ export class PublicCommunityInfoResolver {
|
||||
logger.debug(`getPublicCommunityInfo() via apiVersion=1_0 ...`)
|
||||
const homeCom = await DbCommunity.findOneByOrFail({ foreign: false })
|
||||
const result = new GetPublicCommunityInfoResult(homeCom)
|
||||
logger.info(`getPublicCommunityInfo()-1_0... return publicInfo=${JSON.stringify(result)}`)
|
||||
logger.debug(`getPublicCommunityInfo()-1_0... return publicInfo=${JSON.stringify(result)}`)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ export class PublicKeyResolver {
|
||||
apiVersion: '1_0',
|
||||
},
|
||||
})
|
||||
logger.info(`getPublicKey()-1_0... return publicKey=${homeCom.publicKey}`)
|
||||
logger.debug(`getPublicKey()-1_0... return publicKey=${homeCom.publicKey}`)
|
||||
return new GetPublicKeyResult(homeCom.publicKey.toString())
|
||||
}
|
||||
}
|
||||
|
||||
99
federation/src/graphql/api/1_0/util/authenticateCommunity.ts
Normal file
99
federation/src/graphql/api/1_0/util/authenticateCommunity.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import { OpenConnectionArgs } from '../model/OpenConnectionArgs'
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
import { FederatedCommunity as DbFedCommunity } 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'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/client/1_0/AuthenticationClient'
|
||||
import { AuthenticationArgs } from '../model/AuthenticationArgs'
|
||||
|
||||
export async function startOpenConnectionCallback(
|
||||
args: OpenConnectionArgs,
|
||||
comA: DbCommunity,
|
||||
api: string,
|
||||
): Promise<void> {
|
||||
logger.debug(`Authentication: startOpenConnectionCallback() with:`, args, comA)
|
||||
try {
|
||||
const homeFedCom = await DbFedCommunity.findOneByOrFail({
|
||||
foreign: false,
|
||||
apiVersion: api,
|
||||
})
|
||||
const fedComA = await DbFedCommunity.findOneByOrFail({
|
||||
foreign: true,
|
||||
apiVersion: api,
|
||||
publicKey: comA.publicKey,
|
||||
})
|
||||
const oneTimeCode = randombytes_random()
|
||||
// store oneTimeCode in requestedCom.community_uuid as authenticate-request-identifier
|
||||
comA.communityUuid = oneTimeCode.toString()
|
||||
await DbCommunity.save(comA)
|
||||
logger.debug(`Authentication: stored oneTimeCode in requestedCom:`, comA)
|
||||
|
||||
const client = AuthenticationClientFactory.getInstance(fedComA)
|
||||
// eslint-disable-next-line camelcase
|
||||
if (client instanceof V1_0_AuthenticationClient) {
|
||||
const callbackArgs = new OpenConnectionCallbackArgs()
|
||||
callbackArgs.oneTimeCode = oneTimeCode.toString()
|
||||
// TODO encrypt callbackArgs.url with requestedCom.publicKey and sign it with homeCom.privateKey
|
||||
callbackArgs.url = homeFedCom.endPoint.endsWith('/')
|
||||
? homeFedCom.endPoint + homeFedCom.apiVersion
|
||||
: homeFedCom.endPoint + '/' + homeFedCom.apiVersion
|
||||
logger.debug(`Authentication: start openConnectionCallback with args:`, callbackArgs)
|
||||
if (await client.openConnectionCallback(callbackArgs)) {
|
||||
logger.debug('Authentication: startOpenConnectionCallback() successful:', callbackArgs)
|
||||
} else {
|
||||
logger.error('Authentication: startOpenConnectionCallback() failed:', callbackArgs)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Authentication: error in startOpenConnectionCallback:', err)
|
||||
}
|
||||
}
|
||||
|
||||
export async function startAuthentication(
|
||||
oneTimeCode: string,
|
||||
fedComB: DbFedCommunity,
|
||||
): Promise<void> {
|
||||
logger.debug(`Authentication: startAuthentication()...`, oneTimeCode, fedComB)
|
||||
try {
|
||||
const homeCom = await DbCommunity.findOneByOrFail({ foreign: false })
|
||||
|
||||
// TODO encrypt homeCom.uuid with homeCom.privateKey and sign it with callbackFedCom.publicKey
|
||||
const client = AuthenticationClientFactory.getInstance(fedComB)
|
||||
// eslint-disable-next-line camelcase
|
||||
if (client instanceof V1_0_AuthenticationClient) {
|
||||
const authenticationArgs = new AuthenticationArgs()
|
||||
authenticationArgs.oneTimeCode = oneTimeCode
|
||||
// TODO encrypt callbackArgs.url with requestedCom.publicKey and sign it with homeCom.privateKey
|
||||
if (homeCom.communityUuid) {
|
||||
authenticationArgs.uuid = homeCom.communityUuid
|
||||
}
|
||||
logger.debug(`Authentication: invoke authenticate() with:`, authenticationArgs)
|
||||
const fedComUuid = await client.authenticate(authenticationArgs)
|
||||
logger.debug(`Authentication: response of authenticate():`, fedComUuid)
|
||||
if (fedComUuid !== null) {
|
||||
logger.debug(
|
||||
`Authentication: received communityUUid for callbackFedCom:`,
|
||||
fedComUuid,
|
||||
fedComB,
|
||||
)
|
||||
const callbackCom = await DbCommunity.findOneByOrFail({
|
||||
foreign: true,
|
||||
publicKey: fedComB.publicKey,
|
||||
})
|
||||
// TODO decrypt fedComUuid with callbackFedCom.publicKey
|
||||
callbackCom.communityUuid = fedComUuid
|
||||
callbackCom.authenticatedAt = new Date()
|
||||
await DbCommunity.save(callbackCom)
|
||||
logger.debug('Authentication: Community Authentication successful:', callbackCom)
|
||||
} else {
|
||||
logger.error('Authentication: Community Authentication failed:', authenticationArgs)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Authentication: error in startOpenConnectionCallback:', err)
|
||||
}
|
||||
}
|
||||
@ -96,7 +96,7 @@ export async function revertSettledReceiveTransaction(
|
||||
await queryRunner.manager.save(DbPendingTransaction, pendingTx)
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
logger.info(`commit revert settlement recipient Transaction successful...`)
|
||||
logger.debug(`commit revert settlement recipient Transaction successful...`)
|
||||
} else {
|
||||
// TODO: if the last TX is not equivelant to pendingTX, the transactions must be corrected in EXPERT-MODE
|
||||
throw new LogError(
|
||||
|
||||
@ -91,7 +91,7 @@ export async function settlePendingReceiveTransaction(
|
||||
await queryRunner.manager.save(DbPendingTransaction, pendingTx)
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
logger.info(`commit recipient Transaction successful...`)
|
||||
logger.debug(`commit recipient Transaction successful...`)
|
||||
|
||||
/*
|
||||
await EVENT_TRANSACTION_SEND(sender, recipient, transactionSend, transactionSend.amount)
|
||||
|
||||
@ -11,6 +11,16 @@ const schema = async (): Promise<GraphQLSchema> => {
|
||||
resolvers: [getApiResolvers()],
|
||||
// authChecker: isAuthorized,
|
||||
scalarsMap: [{ type: Decimal, scalar: DecimalScalar }],
|
||||
/*
|
||||
validate: {
|
||||
validationError: { target: false },
|
||||
skipMissingProperties: true,
|
||||
skipNullProperties: true,
|
||||
skipUndefinedProperties: false,
|
||||
forbidUnknownValues: true,
|
||||
stopAtFirstError: true,
|
||||
},
|
||||
*/
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
|
||||
import { entities } from '@entity/index'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
|
||||
@ -31,8 +32,8 @@ export const cleanDB = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
export const testEnvironment = async (testLogger = logger) => {
|
||||
const server = await createServer(testLogger) // context, testLogger, testI18n)
|
||||
export const testEnvironment = async (testLogger = logger /*, testI18n = i18n */) => {
|
||||
const server = await createServer(/* context, */ testLogger /* , testI18n */)
|
||||
const con = server.con
|
||||
const testClient = createTestClient(server.apollo)
|
||||
const mutate = testClient.mutate
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
// import { CONFIG } from '@/config'
|
||||
// import { i18n } from '@/server/localization'
|
||||
import { federationLogger as logger } from '@/server/logger'
|
||||
|
||||
// CONFIG.EMAIL = true
|
||||
// CONFIG.EMAIL_TEST_MODUS = false
|
||||
|
||||
jest.setTimeout(1000000)
|
||||
|
||||
jest.mock('@/server/logger', () => {
|
||||
const originalModule = jest.requireActual('@/server/logger')
|
||||
const originalModule = jest.requireActual<typeof logger>('@/server/logger')
|
||||
return {
|
||||
__esModule: true,
|
||||
...originalModule,
|
||||
@ -19,4 +24,20 @@ jest.mock('@/server/logger', () => {
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
jest.mock('@/server/localization', () => {
|
||||
const originalModule = jest.requireActual<typeof i18n>('@/server/localization')
|
||||
return {
|
||||
__esModule: true,
|
||||
...originalModule,
|
||||
i18n: {
|
||||
init: jest.fn(),
|
||||
// configure: jest.fn(),
|
||||
// __: jest.fn(),
|
||||
// setLocale: jest.fn(),
|
||||
},
|
||||
}
|
||||
})
|
||||
*/
|
||||
|
||||
export { logger }
|
||||
|
||||
@ -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,12 +1057,19 @@
|
||||
"@types/mime" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/sodium-native@^2.3.5":
|
||||
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"
|
||||
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
|
||||
|
||||
"@types/uuid@8.3.4":
|
||||
"@types/uuid@^8.3.4":
|
||||
version "8.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
|
||||
integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
|
||||
@ -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.0.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-5.0.0.tgz#7504a807d0e11be11a3c448e900f0cc316aa18ef"
|
||||
integrity sha512-SpVEnIo2J5k2+Zf76cUkdvIRaq5FMZvGQYnA4lUWYbc99m+fHh4CZYRRO/Ff4tCLQ613fzCm3SiDT64ubW5Gyw==
|
||||
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"
|
||||
@ -3024,7 +3058,7 @@ graphql-subscriptions@^1.0.0, graphql-subscriptions@^1.1.0:
|
||||
dependencies:
|
||||
iterall "^1.3.0"
|
||||
|
||||
graphql-tag@^2.11.0:
|
||||
graphql-tag@^2.11.0, graphql-tag@^2.12.6:
|
||||
version "2.12.6"
|
||||
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1"
|
||||
integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==
|
||||
@ -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.3.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"
|
||||
@ -4537,10 +4583,10 @@ prettier-linter-helpers@^1.0.0:
|
||||
dependencies:
|
||||
fast-diff "^1.1.2"
|
||||
|
||||
prettier@^2.3.1:
|
||||
version "2.8.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.2.tgz#c4ea1b5b454d7c4b59966db2e06ed7eec5dfd160"
|
||||
integrity sha512-BtRV9BcncDyI2tsuS19zzhzoxD8Dh8LiCx7j7tHzrkz8GFXAexeWFdi22mjE1d16dftH2qNaytVxqiRTGlMfpw==
|
||||
prettier@^2.8.7:
|
||||
version "2.8.8"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
|
||||
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
|
||||
|
||||
pretty-format@^27.0.0, pretty-format@^27.5.1:
|
||||
version "27.5.1"
|
||||
@ -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@^3.3.0:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/sodium-native/-/sodium-native-3.4.1.tgz#44616c07ccecea15195f553af88b3e574b659741"
|
||||
integrity sha512-PaNN/roiFWzVVTL6OqjzYct38NSXewdl2wz8SRB51Br/MLIJPrbM3XexhVWkq7D3UWMysfrhKVf1v1phZq6MeQ==
|
||||
dependencies:
|
||||
node-gyp-build "^4.3.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