diff --git a/package-lock.json b/package-lock.json index 5260e3c214..d3655dcb24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@matrixai/errors": "^1.1.7", "@matrixai/id": "^3.3.3", "@matrixai/logger": "^3.1.0", + "@matrixai/quic": "0.0.7-alpha.0", "@matrixai/resources": "^1.1.4", "@matrixai/timer": "^1.1.0", "@matrixai/workers": "^1.3.6", @@ -2728,6 +2729,65 @@ "resolved": "https://registry.npmjs.org/@matrixai/logger/-/logger-3.1.0.tgz", "integrity": "sha512-C4JWpgbNik3V99bfGfDell5cH3JULD67eEq9CeXl4rYgsvanF8hhuY84ZYvndPhimt9qjA9/Z8uExKGoiv1zVw==" }, + "node_modules/@matrixai/quic": { + "version": "0.0.7-alpha.0", + "resolved": "https://registry.npmjs.org/@matrixai/quic/-/quic-0.0.7-alpha.0.tgz", + "integrity": "sha512-btF0hVAshxk5J1edL+BQ0ZqkLj7TdAE3YYX+FYCCKjEHW36okztbBxSNeyW47M+n9S3LMgJg+myADieyFuMJVw==", + "dependencies": { + "@matrixai/async-init": "^1.8.1", + "@matrixai/async-locks": "^3.1.1", + "@matrixai/errors": "^1.1.2", + "@matrixai/logger": "^3.1.0", + "@matrixai/resources": "^1.1.3", + "@matrixai/workers": "^1.3.5", + "ip-num": "^1.5.0" + }, + "bin": { + "server": "dist/bin/server.js" + }, + "optionalDependencies": { + "@matrixai/quic-darwin-arm64": "0.0.7-alpha.0", + "@matrixai/quic-darwin-x64": "0.0.7-alpha.0", + "@matrixai/quic-linux-x64": "0.0.7-alpha.0", + "@matrixai/quic-win32-x64": "0.0.7-alpha.0" + } + }, + "node_modules/@matrixai/quic-darwin-arm64": { + "version": "0.0.7-alpha.0", + "resolved": "https://registry.npmjs.org/@matrixai/quic-darwin-arm64/-/quic-darwin-arm64-0.0.7-alpha.0.tgz", + "integrity": "sha512-95dsTuHevVpdSbLwcFK9rdT4JJmXktokLIufj7AAwzo7WtZJCfu39T/VVwqG16k18yHn8BUh7QZqndQnL34jWg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@matrixai/quic-darwin-x64": { + "version": "0.0.7-alpha.0", + "resolved": "https://registry.npmjs.org/@matrixai/quic-darwin-x64/-/quic-darwin-x64-0.0.7-alpha.0.tgz", + "integrity": "sha512-h1ixY0g6bc4/DfV8eHlXaxFz1ljPWGJQzDXf0HcwJ5YP6+WrozELneRSiSOhAh/WHJJIhju/wJMgKEnycdm+1g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@matrixai/quic-linux-x64": { + "version": "0.0.7-alpha.0", + "resolved": "https://registry.npmjs.org/@matrixai/quic-linux-x64/-/quic-linux-x64-0.0.7-alpha.0.tgz", + "integrity": "sha512-l3lcRUpdc9igz2SWhGVRQpQHdmHS1PaoFD0lWiZmYXvugGG+1uZLZpHH7kZ1V5t52um1t/xtGGwxJxL+JYtxAQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@matrixai/resources": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@matrixai/resources/-/resources-1.1.4.tgz", @@ -6547,9 +6607,9 @@ } }, "node_modules/ip-num": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ip-num/-/ip-num-1.4.0.tgz", - "integrity": "sha512-MP+gq4uBvrvm+G7EwP14GcJeFK49/p6sZrNOarMUoExLRodULJQM8mnkb/SbT1YKxRsZfh8rgwei2pUJIa35jA==" + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ip-num/-/ip-num-1.5.1.tgz", + "integrity": "sha512-QziFxgxq3mjIf5CuwlzXFYscHxgLqdEdJKRo2UJ5GurL5zrSRMzT/O+nK0ABimoFH8MWF8YwIiwECYsHc1LpUQ==" }, "node_modules/ipaddr.js": { "version": "2.0.1", @@ -13969,6 +14029,42 @@ "resolved": "https://registry.npmjs.org/@matrixai/logger/-/logger-3.1.0.tgz", "integrity": "sha512-C4JWpgbNik3V99bfGfDell5cH3JULD67eEq9CeXl4rYgsvanF8hhuY84ZYvndPhimt9qjA9/Z8uExKGoiv1zVw==" }, + "@matrixai/quic": { + "version": "0.0.7-alpha.0", + "resolved": "https://registry.npmjs.org/@matrixai/quic/-/quic-0.0.7-alpha.0.tgz", + "integrity": "sha512-btF0hVAshxk5J1edL+BQ0ZqkLj7TdAE3YYX+FYCCKjEHW36okztbBxSNeyW47M+n9S3LMgJg+myADieyFuMJVw==", + "requires": { + "@matrixai/async-init": "^1.8.1", + "@matrixai/async-locks": "^3.1.1", + "@matrixai/errors": "^1.1.2", + "@matrixai/logger": "^3.1.0", + "@matrixai/quic-darwin-arm64": "0.0.7-alpha.0", + "@matrixai/quic-darwin-x64": "0.0.7-alpha.0", + "@matrixai/quic-linux-x64": "0.0.7-alpha.0", + "@matrixai/quic-win32-x64": "0.0.7-alpha.0", + "@matrixai/resources": "^1.1.3", + "@matrixai/workers": "^1.3.5", + "ip-num": "^1.5.0" + } + }, + "@matrixai/quic-darwin-arm64": { + "version": "0.0.7-alpha.0", + "resolved": "https://registry.npmjs.org/@matrixai/quic-darwin-arm64/-/quic-darwin-arm64-0.0.7-alpha.0.tgz", + "integrity": "sha512-95dsTuHevVpdSbLwcFK9rdT4JJmXktokLIufj7AAwzo7WtZJCfu39T/VVwqG16k18yHn8BUh7QZqndQnL34jWg==", + "optional": true + }, + "@matrixai/quic-darwin-x64": { + "version": "0.0.7-alpha.0", + "resolved": "https://registry.npmjs.org/@matrixai/quic-darwin-x64/-/quic-darwin-x64-0.0.7-alpha.0.tgz", + "integrity": "sha512-h1ixY0g6bc4/DfV8eHlXaxFz1ljPWGJQzDXf0HcwJ5YP6+WrozELneRSiSOhAh/WHJJIhju/wJMgKEnycdm+1g==", + "optional": true + }, + "@matrixai/quic-linux-x64": { + "version": "0.0.7-alpha.0", + "resolved": "https://registry.npmjs.org/@matrixai/quic-linux-x64/-/quic-linux-x64-0.0.7-alpha.0.tgz", + "integrity": "sha512-l3lcRUpdc9igz2SWhGVRQpQHdmHS1PaoFD0lWiZmYXvugGG+1uZLZpHH7kZ1V5t52um1t/xtGGwxJxL+JYtxAQ==", + "optional": true + }, "@matrixai/resources": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@matrixai/resources/-/resources-1.1.4.tgz", @@ -16907,9 +17003,9 @@ } }, "ip-num": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ip-num/-/ip-num-1.4.0.tgz", - "integrity": "sha512-MP+gq4uBvrvm+G7EwP14GcJeFK49/p6sZrNOarMUoExLRodULJQM8mnkb/SbT1YKxRsZfh8rgwei2pUJIa35jA==" + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ip-num/-/ip-num-1.5.1.tgz", + "integrity": "sha512-QziFxgxq3mjIf5CuwlzXFYscHxgLqdEdJKRo2UJ5GurL5zrSRMzT/O+nK0ABimoFH8MWF8YwIiwECYsHc1LpUQ==" }, "ipaddr.js": { "version": "2.0.1", diff --git a/package.json b/package.json index e95defca7e..348ac3f558 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "@matrixai/resources": "^1.1.4", "@matrixai/timer": "^1.1.0", "@matrixai/workers": "^1.3.6", + "@matrixai/quic": "0.0.7-alpha.0", "@peculiar/asn1-pkcs8": "^2.3.0", "@peculiar/asn1-schema": "^2.3.0", "@peculiar/asn1-x509": "^2.3.0", diff --git a/src/agent-old/GRPCClientAgent.ts b/src/agent-old/GRPCClientAgent.ts deleted file mode 100644 index 1de9c1e609..0000000000 --- a/src/agent-old/GRPCClientAgent.ts +++ /dev/null @@ -1,249 +0,0 @@ -import type { ClientDuplexStream } from '@grpc/grpc-js'; -import type { ClientReadableStream } from '@grpc/grpc-js/build/src/call'; -import type { - AsyncGeneratorReadableStreamClient, - AsyncGeneratorDuplexStreamClient, -} from '../grpc/types'; -import type { NodeId } from '../ids/types'; -import type { Host, Port, ProxyConfig, TLSConfig } from '../network/types'; -import type * as utilsPB from '../proto/js/polykey/v1/utils/utils_pb'; -import type * as vaultsPB from '../proto/js/polykey/v1/vaults/vaults_pb'; -import type * as nodesPB from '../proto/js/polykey/v1/nodes/nodes_pb'; -import type * as notificationsPB from '../proto/js/polykey/v1/notifications/notifications_pb'; -import type { Timer } from '../types'; -import Logger from '@matrixai/logger'; -import { CreateDestroy, ready } from '@matrixai/async-init/dist/CreateDestroy'; -import * as agentErrors from './errors'; -import * as grpcUtils from '../grpc/utils'; -import GRPCClient from '../grpc/GRPCClient'; -import { AgentServiceClient } from '../proto/js/polykey/v1/agent_service_grpc_pb'; - -interface GRPCClientAgent extends CreateDestroy {} -@CreateDestroy() -class GRPCClientAgent extends GRPCClient { - /** - * Creates GRPCClientAgent - * This connects to the agent service - * This connection should not be encrypted with TLS because it - * will go through the network proxies - */ - static async createGRPCClientAgent({ - nodeId, - host, - port, - tlsConfig, - proxyConfig, - timer, - destroyCallback = async () => {}, - logger = new Logger(this.name), - }: { - nodeId: NodeId; - host: Host; - port: Port; - tlsConfig?: Partial; - proxyConfig?: ProxyConfig; - timer?: Timer; - destroyCallback?: () => Promise; - logger?: Logger; - }): Promise { - const { client, serverCertChain, flowCountInterceptor } = - await super.createClient({ - clientConstructor: AgentServiceClient, - nodeId, - host, - port, - tlsConfig, - proxyConfig, - timer, - logger, - }); - const grpcClientAgent = new this({ - client, - nodeId, - host, - port, - tlsConfig, - proxyConfig, - serverCertChain, - flowCountInterceptor, - destroyCallback, - logger, - }); - return grpcClientAgent; - } - - public async destroy({ - timeout, - }: { - timeout?: number; - } = {}) { - await super.destroy({ timeout }); - } - - @ready(new agentErrors.ErrorAgentClientDestroyed()) - public echo(...args) { - return grpcUtils.promisifyUnaryCall( - this.client, - { - nodeId: this.nodeId, - host: this.host, - port: this.port, - command: this.echo.name, - }, - this.client.echo, - )(...args); - } - - @ready(new agentErrors.ErrorAgentClientDestroyed()) - public vaultsGitInfoGet( - ...args - ): AsyncGeneratorReadableStreamClient< - vaultsPB.PackChunk, - ClientReadableStream - > { - return grpcUtils.promisifyReadableStreamCall( - this.client, - { - nodeId: this.nodeId, - host: this.host, - port: this.port, - command: this.vaultsGitInfoGet.name, - }, - this.client.vaultsGitInfoGet, - )(...args); - } - - @ready(new agentErrors.ErrorAgentClientDestroyed()) - public vaultsGitPackGet( - ...args - ): AsyncGeneratorDuplexStreamClient< - vaultsPB.PackChunk, - vaultsPB.PackChunk, - ClientDuplexStream - > { - return grpcUtils.promisifyDuplexStreamCall( - this.client, - { - nodeId: this.nodeId, - host: this.host, - port: this.port, - command: this.vaultsGitPackGet.name, - }, - this.client.vaultsGitPackGet, - )(...args); - } - - @ready(new agentErrors.ErrorAgentClientDestroyed()) - public vaultsScan( - ...args - ): AsyncGeneratorReadableStreamClient< - vaultsPB.List, - ClientReadableStream - > { - return grpcUtils.promisifyReadableStreamCall( - this.client, - { - nodeId: this.nodeId, - host: this.host, - port: this.port, - command: this.vaultsScan.name, - }, - this.client.vaultsScan, - )(...args); - } - - @ready(new agentErrors.ErrorAgentClientDestroyed()) - public nodesClosestLocalNodesGet(...args) { - return grpcUtils.promisifyUnaryCall( - this.client, - { - nodeId: this.nodeId, - host: this.host, - port: this.port, - command: this.nodesClosestLocalNodesGet.name, - }, - this.client.nodesClosestLocalNodesGet, - )(...args); - } - - @ready(new agentErrors.ErrorAgentClientDestroyed()) - public nodesClaimsGet(...args) { - return grpcUtils.promisifyUnaryCall( - this.client, - { - nodeId: this.nodeId, - host: this.host, - port: this.port, - command: this.nodesClaimsGet.name, - }, - this.client.nodesClaimsGet, - )(...args); - } - - @ready(new agentErrors.ErrorAgentClientDestroyed()) - public nodesChainDataGet(...args) { - return grpcUtils.promisifyReadableStreamCall( - this.client, - { - nodeId: this.nodeId, - host: this.host, - port: this.port, - command: this.nodesChainDataGet.name, - }, - this.client.nodesChainDataGet, - )(...args); - } - - @ready(new agentErrors.ErrorAgentClientDestroyed()) - public nodesHolePunchMessageSend(...args) { - return grpcUtils.promisifyUnaryCall( - this.client, - { - nodeId: this.nodeId, - host: this.host, - port: this.port, - command: this.nodesHolePunchMessageSend.name, - }, - this.client.nodesHolePunchMessageSend, - )(...args); - } - - @ready(new agentErrors.ErrorAgentClientDestroyed()) - public notificationsSend(...args) { - return grpcUtils.promisifyUnaryCall( - this.client, - { - nodeId: this.nodeId, - host: this.host, - port: this.port, - command: this.notificationsSend.name, - }, - this.client.notificationsSend, - )(...args); - } - - @ready(new agentErrors.ErrorAgentClientDestroyed()) - public nodesCrossSignClaim( - ...args - ): AsyncGeneratorDuplexStreamClient< - nodesPB.AgentClaim, - nodesPB.AgentClaim, - ClientDuplexStream - > { - return grpcUtils.promisifyDuplexStreamCall< - nodesPB.AgentClaim, - nodesPB.AgentClaim - >( - this.client, - { - nodeId: this.nodeId, - host: this.host, - port: this.port, - command: this.nodesCrossSignClaim.name, - }, - this.client.nodesCrossSignClaim, - )(...args); - } -} - -export default GRPCClientAgent; diff --git a/src/agent-old/errors.ts b/src/agent-old/errors.ts deleted file mode 100644 index b0460055cf..0000000000 --- a/src/agent-old/errors.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ErrorPolykey, sysexits } from '../errors'; - -class ErrorAgent extends ErrorPolykey {} - -class ErrorAgentRunning extends ErrorPolykey { - static description = 'Agent Client is running'; - exitCode = sysexits.USAGE; -} - -class ErrorAgentClientNotStarted extends ErrorAgent { - static description = 'Agent Client is not started'; - exitCode = sysexits.USAGE; -} - -class ErrorAgentClientDestroyed extends ErrorAgent { - static description = 'Agent Client is destroyed'; - exitCode = sysexits.USAGE; -} - -class ErrorConnectionInfoMissing extends ErrorAgent { - static description = 'Vault already exists'; - exitCode = sysexits.UNAVAILABLE; -} - -export { - ErrorAgent, - ErrorAgentClientNotStarted, - ErrorAgentRunning, - ErrorAgentClientDestroyed, - ErrorConnectionInfoMissing, -}; diff --git a/src/agent-old/index.ts b/src/agent-old/index.ts deleted file mode 100644 index 4e55eb824b..0000000000 --- a/src/agent-old/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default as createAgentService, AgentServiceService } from './service'; -export { default as GRPCClientAgent } from './GRPCClientAgent'; -export * as errors from './errors'; -export * as types from './types'; -export * as utils from './utils'; diff --git a/src/agent-old/service/index.ts b/src/agent-old/service/index.ts deleted file mode 100644 index 8637e45fad..0000000000 --- a/src/agent-old/service/index.ts +++ /dev/null @@ -1,70 +0,0 @@ -import type { DB } from '@matrixai/db'; -import type KeyRing from '../../keys/KeyRing'; -import type VaultManager from '../../vaults/VaultManager'; -import type NodeGraph from '../../nodes/NodeGraph'; -import type NodeManager from '../../nodes/NodeManager'; -import type NodeConnectionManager from '../../nodes/NodeConnectionManager'; -import type NotificationsManager from '../../notifications/NotificationsManager'; -import type Sigchain from '../../sigchain/Sigchain'; -import type ACL from '../../acl/ACL'; -import type GestaltGraph from '../../gestalts/GestaltGraph'; -import type { IAgentServiceServer } from '../../proto/js/polykey/v1/agent_service_grpc_pb'; -import type Proxy from '../../network/Proxy'; -import Logger from '@matrixai/logger'; -import echo from './echo'; -import nodesChainDataGet from './nodesChainDataGet'; -import nodesClaimsGet from './nodesClaimsGet'; -import nodesClosestLocalNodesGet from './nodesClosestLocalNodesGet'; -import nodesCrossSignClaim from './nodesCrossSignClaim'; -import nodesHolePunchMessageSend from './nodesHolePunchMessageSend'; -import notificationsSend from './notificationsSend'; -import vaultsGitInfoGet from './vaultsGitInfoGet'; -import vaultsGitPackGet from './vaultsGitPackGet'; -import vaultsScan from './vaultsScan'; -import { AgentServiceService } from '../../proto/js/polykey/v1/agent_service_grpc_pb'; -import * as agentUtils from '../utils'; - -function createService({ - proxy, - db, - logger = new Logger('GRPCClientAgentService'), - ...containerRest -}: { - db: DB; - keyRing: KeyRing; - vaultManager: VaultManager; - nodeConnectionManager: NodeConnectionManager; - nodeManager: NodeManager; - nodeGraph: NodeGraph; - notificationsManager: NotificationsManager; - sigchain: Sigchain; - acl: ACL; - gestaltGraph: GestaltGraph; - proxy: Proxy; - logger?: Logger; -}): IAgentServiceServer { - const connectionInfoGet = agentUtils.connectionInfoGetter(proxy); - const container = { - ...containerRest, - db, - logger, - connectionInfoGet: connectionInfoGet, - }; - const service: IAgentServiceServer = { - echo: echo(container), - nodesChainDataGet: nodesChainDataGet(container), - nodesClaimsGet: nodesClaimsGet(container), - nodesClosestLocalNodesGet: nodesClosestLocalNodesGet(container), - nodesCrossSignClaim: nodesCrossSignClaim(container), - nodesHolePunchMessageSend: nodesHolePunchMessageSend(container), - notificationsSend: notificationsSend(container), - vaultsGitInfoGet: vaultsGitInfoGet(container), - vaultsGitPackGet: vaultsGitPackGet(container), - vaultsScan: vaultsScan(container), - }; - return service; -} - -export default createService; - -export { AgentServiceService }; diff --git a/src/agent-old/service/notificationsSend.ts b/src/agent-old/service/notificationsSend.ts deleted file mode 100644 index ea5cca0e47..0000000000 --- a/src/agent-old/service/notificationsSend.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type * as grpc from '@grpc/grpc-js'; -import type NotificationsManager from '../../notifications/NotificationsManager'; -import type * as notificationsPB from '../../proto/js/polykey/v1/notifications/notifications_pb'; -import type Logger from '@matrixai/logger'; -import type { DB } from '@matrixai/db'; -import type { SignedNotification } from '../../notifications/types'; -import type KeyRing from '../../keys/KeyRing'; -import * as grpcUtils from '../../grpc/utils'; -import * as notificationsUtils from '../../notifications/utils'; -import * as notificationsErrors from '../../notifications/errors'; -import * as utilsPB from '../../proto/js/polykey/v1/utils/utils_pb'; -import * as agentUtils from '../utils'; - -function notificationsSend({ - notificationsManager, - db, - keyRing, - logger, -}: { - notificationsManager: NotificationsManager; - db: DB; - keyRing: KeyRing; - logger: Logger; -}) { - return async ( - call: grpc.ServerUnaryCall< - notificationsPB.AgentNotification, - utilsPB.EmptyMessage - >, - callback: grpc.sendUnaryData, - ): Promise => { - try { - const signedNotification = - call.request.getContent() as SignedNotification; - const notification = await notificationsUtils.verifyAndDecodeNotif( - signedNotification, - keyRing.getNodeId(), - ); - await db.withTransactionF((tran) => - notificationsManager.receiveNotification(notification, tran), - ); - const response = new utilsPB.EmptyMessage(); - callback(null, response); - return; - } catch (e) { - callback(grpcUtils.fromError(e, true)); - !agentUtils.isAgentClientError(e, [ - notificationsErrors.ErrorNotificationsInvalidType, - notificationsErrors.ErrorNotificationsValidationFailed, - notificationsErrors.ErrorNotificationsParse, - notificationsErrors.ErrorNotificationsPermissionsNotFound, - ]) && logger.error(`${notificationsSend.name}:${e}`); - return; - } - }; -} - -export default notificationsSend; diff --git a/src/agent-old/service/vaultsGitInfoGet.ts b/src/agent-old/service/vaultsGitInfoGet.ts deleted file mode 100644 index 0fb18c96ad..0000000000 --- a/src/agent-old/service/vaultsGitInfoGet.ts +++ /dev/null @@ -1,120 +0,0 @@ -import type { DB } from '@matrixai/db'; -import type { VaultName, VaultAction } from '../../vaults/types'; -import type VaultManager from '../../vaults/VaultManager'; -import type ACL from '../../acl/ACL'; -import type { ConnectionInfoGet } from '../../agent/types'; -import type Logger from '@matrixai/logger'; -import * as grpc from '@grpc/grpc-js'; -import * as grpcUtils from '../../grpc/utils'; -import * as vaultsUtils from '../../vaults/utils'; -import * as vaultsErrors from '../../vaults/errors'; -import * as vaultsPB from '../../proto/js/polykey/v1/vaults/vaults_pb'; -import { validateSync } from '../../validation'; -import * as validationUtils from '../../validation/utils'; -import * as nodesUtils from '../../nodes/utils'; -import { matchSync } from '../../utils'; -import * as agentErrors from '../errors'; -import * as agentUtils from '../utils'; - -function vaultsGitInfoGet({ - vaultManager, - acl, - db, - logger, - connectionInfoGet, -}: { - vaultManager: VaultManager; - acl: ACL; - db: DB; - logger: Logger; - connectionInfoGet: ConnectionInfoGet; -}) { - return async ( - call: grpc.ServerWritableStream, - ): Promise => { - const genWritable = grpcUtils.generatorWritable(call, true); - try { - await db.withTransactionF(async (tran) => { - const vaultIdFromName = await vaultManager.getVaultId( - call.request.getVault()?.getNameOrId() as VaultName, - tran, - ); - const vaultId = - vaultIdFromName ?? - vaultsUtils.decodeVaultId(call.request.getVault()?.getNameOrId()); - if (vaultId == null) { - throw new vaultsErrors.ErrorVaultsVaultUndefined(); - } - const { - actionType, - }: { - actionType: VaultAction; - } = validateSync( - (keyPath, value) => { - return matchSync(keyPath)( - [['actionType'], () => validationUtils.parseVaultAction(value)], - () => value, - ); - }, - { - actionType: call.request.getAction(), - }, - ); - const vaultName = (await vaultManager.getVaultMeta(vaultId, tran)) - ?.vaultName; - if (vaultName == null) { - throw new vaultsErrors.ErrorVaultsVaultUndefined(); - } - // Getting the NodeId from the ReverseProxy connection info - const connectionInfo = connectionInfoGet(call); - // If this is getting run the connection exists - // It SHOULD exist here - if (connectionInfo == null) { - throw new agentErrors.ErrorConnectionInfoMissing(); - } - const nodeId = connectionInfo.remoteNodeId; - const nodeIdEncoded = nodesUtils.encodeNodeId(nodeId); - const permissions = await acl.getNodePerm(nodeId, tran); - if (permissions == null) { - throw new vaultsErrors.ErrorVaultsPermissionDenied( - `No permissions found for ${nodeIdEncoded}`, - ); - } - const vaultPerms = permissions.vaults[vaultId]; - if (vaultPerms?.[actionType] !== null) { - throw new vaultsErrors.ErrorVaultsPermissionDenied( - `${nodeIdEncoded} does not have permission to ${actionType} from vault ${vaultsUtils.encodeVaultId( - vaultId, - )}`, - ); - } - const meta = new grpc.Metadata(); - meta.set('vaultName', vaultName); - meta.set('vaultId', vaultsUtils.encodeVaultId(vaultId)); - genWritable.stream.sendMetadata(meta); - const response = new vaultsPB.PackChunk(); - const responseGen = vaultManager.handleInfoRequest(vaultId, tran); - for await (const byte of responseGen) { - if (byte !== null) { - response.setChunk(byte); - await genWritable.next(response); - } else { - await genWritable.next(null); - } - } - }); - await genWritable.next(null); - return; - } catch (e) { - await genWritable.throw(e); - !agentUtils.isAgentClientError(e, [ - vaultsErrors.ErrorVaultsVaultUndefined, - agentErrors.ErrorConnectionInfoMissing, - vaultsErrors.ErrorVaultsPermissionDenied, - ]) && logger.error(`${vaultsGitInfoGet.name}:${e}`); - return; - } - }; -} - -export default vaultsGitInfoGet; diff --git a/src/agent-old/service/vaultsGitPackGet.ts b/src/agent-old/service/vaultsGitPackGet.ts deleted file mode 100644 index 3646fda071..0000000000 --- a/src/agent-old/service/vaultsGitPackGet.ts +++ /dev/null @@ -1,133 +0,0 @@ -import type * as grpc from '@grpc/grpc-js'; -import type { DB } from '@matrixai/db'; -import type { VaultName, VaultAction } from '../../vaults/types'; -import type VaultManager from '../../vaults/VaultManager'; -import type { ConnectionInfoGet } from '../../agent/types'; -import type ACL from '../../acl/ACL'; -import type KeyRing from '../../keys/KeyRing'; -import type Logger from '@matrixai/logger'; -import * as nodesUtils from '../../nodes/utils'; -import * as grpcUtils from '../../grpc/utils'; -import * as vaultsErrors from '../../vaults/errors'; -import * as vaultsUtils from '../../vaults/utils'; -import * as vaultsPB from '../../proto/js/polykey/v1/vaults/vaults_pb'; -import { validateSync } from '../../validation'; -import * as validationUtils from '../../validation/utils'; -import { matchSync } from '../../utils'; -import * as agentErrors from '../errors'; -import * as agentUtils from '../utils'; - -function vaultsGitPackGet({ - vaultManager, - acl, - db, - keyRing, - logger, - connectionInfoGet, -}: { - vaultManager: VaultManager; - acl: ACL; - db: DB; - keyRing: KeyRing; - logger: Logger; - connectionInfoGet: ConnectionInfoGet; -}) { - return async ( - call: grpc.ServerDuplexStream, - ): Promise => { - const nodeId = keyRing.getNodeId(); - const genDuplex = grpcUtils.generatorDuplex( - call, - { nodeId, command: vaultsGitPackGet.name }, - true, - ); - try { - const clientBodyBuffers: Uint8Array[] = []; - const clientRequest = (await genDuplex.read()).value; - clientBodyBuffers.push(clientRequest!.getChunk_asU8()); - const body = Buffer.concat(clientBodyBuffers); - const meta = call.metadata; - // Getting the NodeId from the ReverseProxy connection info - const connectionInfo = connectionInfoGet(call); - // If this is getting run the connection exists - // It SHOULD exist here - if (connectionInfo == null) { - throw new agentErrors.ErrorConnectionInfoMissing(); - } - const nodeId = connectionInfo.remoteNodeId; - const nodeIdEncoded = nodesUtils.encodeNodeId(nodeId); - const nameOrId = meta.get('vaultNameOrId').pop()!.toString(); - await db.withTransactionF(async (tran) => { - const vaultIdFromName = await vaultManager.getVaultId( - nameOrId as VaultName, - tran, - ); - const vaultId = vaultIdFromName ?? vaultsUtils.decodeVaultId(nameOrId); - if (vaultId == null) { - throw new vaultsErrors.ErrorVaultsVaultUndefined(); - } - const { - actionType, - }: { - actionType: VaultAction; - } = validateSync( - (keyPath, value) => { - return matchSync(keyPath)( - [['actionType'], () => validationUtils.parseVaultAction(value)], - () => value, - ); - }, - { - actionType: meta.get('vaultAction').pop()!.toString(), - }, - ); - // Checking permissions - const permissions = await acl.getNodePerm(nodeId, tran); - const vaultPerms = permissions?.vaults[vaultId]; - if (vaultPerms?.[actionType] !== null) { - throw new vaultsErrors.ErrorVaultsPermissionDenied( - `${nodeIdEncoded} does not have permission to ${actionType} from vault ${vaultsUtils.encodeVaultId( - vaultId, - )}`, - ); - } - const response = new vaultsPB.PackChunk(); - const [sideBand, progressStream] = await vaultManager.handlePackRequest( - vaultId, - Buffer.from(body), - tran, - ); - response.setChunk(Buffer.from('0008NAK\n')); - await genDuplex.write(response); - const responseBuffers: Uint8Array[] = []; - await new Promise((resolve, reject) => { - sideBand.on('data', async (data: Uint8Array) => { - responseBuffers.push(data); - }); - sideBand.on('end', async () => { - response.setChunk(Buffer.concat(responseBuffers)); - await genDuplex.write(response); - resolve(); - }); - sideBand.on('error', (err) => { - reject(err); - }); - progressStream.write(Buffer.from('0014progress is at 50%\n')); - progressStream.end(); - }); - }); - await genDuplex.next(null); - return; - } catch (e) { - await genDuplex.throw(e); - !agentUtils.isAgentClientError(e, [ - agentErrors.ErrorConnectionInfoMissing, - vaultsErrors.ErrorVaultsPermissionDenied, - vaultsErrors.ErrorVaultsVaultUndefined, - ]) && logger.error(`${vaultsGitPackGet.name}:${e}`); - return; - } - }; -} - -export default vaultsGitPackGet; diff --git a/src/agent-old/service/vaultsScan.ts b/src/agent-old/service/vaultsScan.ts deleted file mode 100644 index f827191085..0000000000 --- a/src/agent-old/service/vaultsScan.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type * as grpc from '@grpc/grpc-js'; -import type { DB } from '@matrixai/db'; -import type VaultManager from '../../vaults/VaultManager'; -import type * as utilsPB from '../../proto/js/polykey/v1/utils/utils_pb'; -import type { ConnectionInfoGet } from '../../agent/types'; -import type Logger from '@matrixai/logger'; -import * as agentErrors from '../../agent/errors'; -import * as vaultsErrors from '../../vaults/errors'; -import * as vaultsPB from '../../proto/js/polykey/v1/vaults/vaults_pb'; -import * as vaultsUtils from '../../vaults/utils'; -import * as grpcUtils from '../../grpc/utils'; -import * as agentUtils from '../utils'; - -function vaultsScan({ - vaultManager, - logger, - connectionInfoGet, - db, -}: { - vaultManager: VaultManager; - logger: Logger; - connectionInfoGet: ConnectionInfoGet; - db: DB; -}) { - return async ( - call: grpc.ServerWritableStream, - ): Promise => { - const genWritable = grpcUtils.generatorWritable(call, true); - const listMessage = new vaultsPB.List(); - // Getting the NodeId from the ReverseProxy connection info - const connectionInfo = connectionInfoGet(call); - // If this is getting run the connection exists - // It SHOULD exist here - if (connectionInfo == null) { - throw new agentErrors.ErrorConnectionInfoMissing(); - } - const nodeId = connectionInfo.remoteNodeId; - try { - await db.withTransactionF(async (tran) => { - const listResponse = vaultManager.handleScanVaults(nodeId, tran); - for await (const { - vaultId, - vaultName, - vaultPermissions, - } of listResponse) { - listMessage.setVaultId(vaultsUtils.encodeVaultId(vaultId)); - listMessage.setVaultName(vaultName); - listMessage.setVaultPermissionsList(vaultPermissions); - await genWritable.next(listMessage); - } - }); - await genWritable.next(null); - return; - } catch (e) { - await genWritable.throw(e); - !agentUtils.isAgentClientError(e, [ - agentErrors.ErrorConnectionInfoMissing, - vaultsErrors.ErrorVaultsPermissionDenied, - ]) && logger.error(`${vaultsScan.name}:${e}`); - return; - } - }; -} - -export default vaultsScan; diff --git a/src/agent-old/types.ts b/src/agent-old/types.ts deleted file mode 100644 index b6d0e02598..0000000000 --- a/src/agent-old/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { ServerSurfaceCall } from '@grpc/grpc-js/build/src/server-call'; -import type { Class } from '@matrixai/errors'; -import type { ConnectionInfo } from '../network/types'; -import type ErrorPolykey from '../ErrorPolykey'; - -type ConnectionInfoGet = ( - call: ServerSurfaceCall, -) => ConnectionInfo | undefined; - -type AgentClientErrors = Array< - Class> | Array>> ->; - -export type { ConnectionInfoGet, AgentClientErrors }; diff --git a/src/agent-old/utils.ts b/src/agent-old/utils.ts deleted file mode 100644 index 48f9ff59fd..0000000000 --- a/src/agent-old/utils.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { ServerSurfaceCall } from '@grpc/grpc-js/build/src/server-call'; -import type ErrorPolykey from '../ErrorPolykey'; -import type { Host, Port } from '../network/types'; -import type Proxy from '../network/Proxy'; -import type { ConnectionInfoGet, AgentClientErrors } from './types'; -import * as validationErrors from '../validation/errors'; - -/** - * Array of errors that are always considered to be "client errors" - * (4xx errors in HTTP) in the context of the agent service - */ -const defaultClientErrors: AgentClientErrors = [ - validationErrors.ErrorValidation, -]; - -function connectionInfoGetter(proxy: Proxy): ConnectionInfoGet { - return (call: ServerSurfaceCall) => { - let urlString = call.getPeer(); - if (!/^.*:\/\//.test(urlString)) urlString = 'pk://' + urlString; - const url = new URL(urlString); - return proxy.getConnectionInfoByReverse( - url.hostname as Host, - parseInt(url.port) as Port, - ); - }; -} - -/** - * Checks whether an error is a "client error" (4xx errors in HTTP) - * Used by the service handlers since client errors should not be - * reported on the server side - * Additional errors that are considered to be client errors in the - * context of a given handler can be supplied in the `extraClientErrors` - * argument - */ -function isAgentClientError( - thrownError: ErrorPolykey, - extraClientErrors?: AgentClientErrors, -): boolean { - for (const error of defaultClientErrors) { - if (Array.isArray(error)) { - let e = thrownError; - let matches = true; - for (const eType of error) { - if (e == null) { - matches = false; - break; - } - if (!(e instanceof eType)) { - matches = false; - break; - } - e = e.cause; - } - if (matches) return true; - } else if (thrownError instanceof error) { - return true; - } - } - if (extraClientErrors) { - for (const error of extraClientErrors) { - if (Array.isArray(error)) { - let e = thrownError; - let matches = true; - for (const eType of error) { - if (e == null) { - matches = false; - break; - } - if (!(e instanceof eType)) { - matches = false; - break; - } - e = e.cause; - } - if (matches) return true; - } else if (thrownError instanceof error) { - return true; - } - } - } - return false; -} - -export { connectionInfoGetter, isAgentClientError }; diff --git a/src/agent/errors.ts b/src/agent/errors.ts new file mode 100644 index 0000000000..c5eac7392a --- /dev/null +++ b/src/agent/errors.ts @@ -0,0 +1,10 @@ +import { ErrorPolykey, sysexits } from '../errors'; + +class ErrorAgent extends ErrorPolykey {} + +class ErrorConnectionInfoMissing extends ErrorAgent { + static description = 'Connection info was missing from connection metadata'; + exitCode = sysexits.UNAVAILABLE; +} + +export { ErrorConnectionInfoMissing }; diff --git a/src/agent/handlers/clientManifest.ts b/src/agent/handlers/clientManifest.ts index fcd3cf3016..0652d07270 100644 --- a/src/agent/handlers/clientManifest.ts +++ b/src/agent/handlers/clientManifest.ts @@ -1,19 +1,96 @@ -import {ClientRPCRequestParams, ClientRPCResponseResult} from "@/client/types"; -import { EchoMessage } from "./types"; -import { UnaryCaller } from '../../rpc/callers'; - +import type { AgentRPCRequestParams, AgentRPCResponseResult } from '../types'; +import type { EchoMessage } from './types'; +import type { + AgentClaimMessage, + ClaimIdMessage, + GitPackMessage, + HolePunchRelayMessage, + NodeAddressMessage, + NodeIdMessage, + SignedNotificationEncoded, + VaultInfo, + VaultsGitInfoGetMessage, + VaultsGitPackGetMessage, + VaultsScanMessage, +} from './types'; +import { DuplexCaller, ServerCaller, UnaryCaller } from '../../rpc/callers'; const echo = new UnaryCaller< - ClientRPCRequestParams, - ClientRPCResponseResult - >(); + AgentRPCRequestParams, + AgentRPCResponseResult +>(); + +const nodeChainDataGet = new ServerCaller< + AgentRPCRequestParams, + AgentRPCResponseResult +>(); + +// Const nodesClaimsGet = new UnaryCaller< +// AgentRPCRequestParams, +// AgentRPCResponseResult +// >(); + +const nodesClosestLocalNodesGet = new ServerCaller< + AgentRPCRequestParams, + AgentRPCResponseResult +>(); + +// TODO: still to be completed +const nodesCrossSignClaim = new DuplexCaller< + AgentRPCRequestParams, + AgentRPCResponseResult +>(); + +// TODO: still to be completed +const nodesHolePunchMessageSend = new UnaryCaller< + AgentRPCRequestParams, + AgentRPCResponseResult +>(); + +const notificationsSend = new UnaryCaller< + AgentRPCRequestParams, + AgentRPCResponseResult +>(); + +const vaultsGitInfoGet = new ServerCaller< + AgentRPCRequestParams, + AgentRPCResponseResult +>(); + +const vaultsGitPackGet = new ServerCaller< + AgentRPCRequestParams, + AgentRPCResponseResult +>(); + +const vaultsScan = new ServerCaller< + AgentRPCRequestParams, + AgentRPCResponseResult +>(); // No type used here, it will override type inference const clientManifest = { echo, -} + nodeChainDataGet, + // NodeClaimsGet, + nodesClosestLocalNodesGet, + nodesCrossSignClaim, + nodesHolePunchMessageSend, + notificationsSend, + vaultsGitInfoGet, + vaultsGitPackGet, + vaultsScan, +}; export { clientManifest, echo, -} + nodeChainDataGet, + // NodeClaimsGet, + nodesClosestLocalNodesGet, + nodesCrossSignClaim, + nodesHolePunchMessageSend, + notificationsSend, + vaultsGitInfoGet, + vaultsGitPackGet, + vaultsScan, +}; diff --git a/src/agent/handlers/echo.ts b/src/agent/handlers/echo.ts index 8d5937258b..7656888cdc 100644 --- a/src/agent/handlers/echo.ts +++ b/src/agent/handlers/echo.ts @@ -1,18 +1,22 @@ -import { UnaryHandler } from "../../rpc/handlers"; import type { EchoMessage } from './types'; -import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; +import type { + AgentRPCRequestParams, + AgentRPCResponseResult, + NoData, +} from '../types'; +import { UnaryHandler } from '../../rpc/handlers'; class EchoHandler extends UnaryHandler< - {}, + NoData, AgentRPCRequestParams, AgentRPCResponseResult - > { +> { public async handle( input: AgentRPCRequestParams, ): Promise> { return { message: input.message, - } + }; } } diff --git a/src/agent/handlers/nodesChainDataGet.ts b/src/agent/handlers/nodesChainDataGet.ts index 4d14c6cc0e..dec634015e 100644 --- a/src/agent/handlers/nodesChainDataGet.ts +++ b/src/agent/handlers/nodesChainDataGet.ts @@ -1,32 +1,25 @@ -import {ServerHandler} from "../../rpc/handlers"; -import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; -import type Sigchain from "../../sigchain/Sigchain"; -import type { DB } from "@matrixai/db"; -import * as claimsUtils from "@/claims/utils"; -import {ClaimIdMessage, AgentClaimMessage} from "./types"; - - - +import type Sigchain from '../../sigchain/Sigchain'; +import type { DB } from '@matrixai/db'; +import type { ClaimIdMessage, AgentClaimMessage } from './types'; +import type { AgentRPCRequestParams, AgentRPCResponseResult } from '../types'; +import * as claimsUtils from '../../claims/utils'; +import { ServerHandler } from '../../rpc/handlers'; class NodesChainDataGetHandler extends ServerHandler< { - sigchain: Sigchain, - db: DB, + sigchain: Sigchain; + db: DB; }, AgentRPCRequestParams, AgentRPCResponseResult - > { +> { public async *handle( _input: ClaimIdMessage, - _cancel, - _meta, - ctx ): AsyncGenerator> { - const { - sigchain, - db, - } = this.container; - yield* db.withTransactionG(async function *(tran): AsyncGenerator> { + const { sigchain, db } = this.container; + yield* db.withTransactionG(async function* ( + tran, + ): AsyncGenerator> { for await (const [claimId, signedClaim] of sigchain.getSignedClaims( { /* seek: seekClaimId,*/ order: 'asc' }, tran, @@ -35,7 +28,7 @@ class NodesChainDataGetHandler extends ServerHandler< const response: AgentClaimMessage = { claimIdEncoded: claimsUtils.encodeClaimId(claimId), signedTokenEncoded: encodedClaim, - } + }; yield response; } }); diff --git a/src/agent/handlers/nodesClaimsGet.ts b/src/agent/handlers/nodesClaimsGet.ts index 954c33b766..86933281af 100644 --- a/src/agent/handlers/nodesClaimsGet.ts +++ b/src/agent/handlers/nodesClaimsGet.ts @@ -1,6 +1,9 @@ -import { UnaryHandler } from "../../rpc/handlers"; -import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; - +import type { + AgentRPCRequestParams, + AgentRPCResponseResult, + NoData, +} from '../types'; +import { UnaryHandler } from '../../rpc/handlers'; /** * Retrieves all claims (of a specific type) of this node (within its sigchain). @@ -9,14 +12,12 @@ import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; */ class NodesClaimsGetHandler extends UnaryHandler< - {}, + NoData, AgentRPCRequestParams, AgentRPCResponseResult - > { - public async handle( - input: AgentRPCRequestParams, - ): Promise { - return {} +> { + public async handle(): Promise { + return {}; } } diff --git a/src/agent/handlers/nodesClosestLocalNodesGet.ts b/src/agent/handlers/nodesClosestLocalNodesGet.ts index fe81d53f49..5f6d487dbe 100644 --- a/src/agent/handlers/nodesClosestLocalNodesGet.ts +++ b/src/agent/handlers/nodesClosestLocalNodesGet.ts @@ -1,13 +1,13 @@ -import {ServerHandler} from "../../rpc/handlers"; -import type {NodeAddressMessage, NodeIdMessage} from './types'; -import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; -import {NodeId} from "@/ids"; -import {validateSync} from "@/validation"; -import {matchSync} from "@/utils"; -import * as validationUtils from "@/validation/utils"; -import * as nodesUtils from "@/nodes/utils"; -import type {NodeGraph} from "@/nodes"; -import type {DB} from "@matrixai/db"; +import type { NodeAddressMessage, NodeIdMessage } from './types'; +import type { NodeGraph } from '../../nodes'; +import type { DB } from '@matrixai/db'; +import type { AgentRPCRequestParams, AgentRPCResponseResult } from '../types'; +import type { NodeId } from '../../ids'; +import { validateSync } from '../../validation'; +import { matchSync } from '../../utils'; +import * as validationUtils from '../../validation/utils'; +import * as nodesUtils from '../../nodes/utils'; +import { ServerHandler } from '../../rpc/handlers'; class NodesClosestLocalNodesGetHandler extends ServerHandler< { @@ -16,14 +16,11 @@ class NodesClosestLocalNodesGetHandler extends ServerHandler< }, AgentRPCRequestParams, AgentRPCResponseResult - > { +> { public async *handle( input: AgentRPCRequestParams, ): AsyncGenerator> { - const { - nodeGraph, - db, - } = this.container; + const { nodeGraph, db } = this.container; const { nodeId, @@ -41,8 +38,14 @@ class NodesClosestLocalNodesGetHandler extends ServerHandler< }, ); // Get all local nodes that are closest to the target node from the request - return yield* db.withTransactionG(async function *(tran): AsyncGenerator> { - const closestNodes = await nodeGraph.getClosestNodes(nodeId, undefined, tran); + return yield* db.withTransactionG(async function* ( + tran, + ): AsyncGenerator> { + const closestNodes = await nodeGraph.getClosestNodes( + nodeId, + undefined, + tran, + ); for (const [nodeId, nodeData] of closestNodes) { yield { nodeIdEncoded: nodesUtils.encodeNodeId(nodeId), diff --git a/src/agent/handlers/nodesCrossSignClaim.ts b/src/agent/handlers/nodesCrossSignClaim.ts index 84ebf765cf..f8258fb6b1 100644 --- a/src/agent/handlers/nodesCrossSignClaim.ts +++ b/src/agent/handlers/nodesCrossSignClaim.ts @@ -1,12 +1,12 @@ -import {DuplexHandler, UnaryHandler} from "../../rpc/handlers"; import type { EchoMessage } from './types'; -import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; -import * as nodesUtils from "../../nodes/utils"; -import * as nodesErrors from "../../nodes/errors"; -import KeyRing from "../../keys/KeyRing"; -import {NodeId, NodeIdEncoded} from "../../ids"; -import ACL from "../../acl/ACL"; -import NodeManager from "../../nodes/NodeManager"; +import type { AgentRPCRequestParams, AgentRPCResponseResult } from '../types'; +import type KeyRing from '../../keys/KeyRing'; +import type { NodeId } from '../../ids'; +import type ACL from '../../acl/ACL'; +import type NodeManager from '../../nodes/NodeManager'; +import * as nodesErrors from '../../nodes/errors'; +import * as nodesUtils from '../../nodes/utils'; +import { DuplexHandler } from '../../rpc/handlers'; // TODO: come back to this! class NodesCrossSignClaimHandler extends DuplexHandler< @@ -17,16 +17,18 @@ class NodesCrossSignClaimHandler extends DuplexHandler< }, AgentRPCRequestParams, AgentRPCResponseResult - > { +> { public async *handle( input: AsyncIterable>, _, meta, ): AsyncGenerator> { - const { keyRing, acl } = this.container; + const { acl } = this.container; // TODO: get remote info from metadata. dependent on js-quic meta types - const requestingNodeId: NodeId | undefined = nodesUtils.decodeNodeId(meta?.remoteNodeId); - if(requestingNodeId == null) throw Error('TMP invalid nodeId'); + const requestingNodeId: NodeId | undefined = nodesUtils.decodeNodeId( + meta?.remoteNodeId, + ); + if (requestingNodeId == null) throw Error('TMP invalid nodeId'); // Check the ACL for permissions const permissions = await acl.getNodePerm(requestingNodeId); if (permissions?.gestalt.claim !== null) { diff --git a/src/agent/handlers/nodesHolePunchMessageSend.ts b/src/agent/handlers/nodesHolePunchMessageSend.ts index 947252203a..711ebccb09 100644 --- a/src/agent/handlers/nodesHolePunchMessageSend.ts +++ b/src/agent/handlers/nodesHolePunchMessageSend.ts @@ -1,17 +1,17 @@ -import { UnaryHandler } from "../../rpc/handlers"; -import {AgentRPCRequestParams, AgentRPCResponseResult} from "../types"; -import {NodeId} from "@/ids"; -import {validateSync} from "@/validation"; -import {matchSync} from "@/utils"; -import * as validationUtils from "@/validation/utils"; -import * as nodesUtils from "@/nodes/utils"; -import {HolePunchRelayMessage} from "./types"; -import type {DB} from "@matrixai/db"; -import type NodeConnectionManager from "../../nodes/NodeConnectionManager"; -import type KeyRing from "../../keys/KeyRing"; -import type Logger from "@matrixai/logger"; -import {Host, Port} from "../../network/types"; -import NodeManager from "../../nodes/NodeManager"; +import type { DB } from '@matrixai/db'; +import type NodeConnectionManager from '../../nodes/NodeConnectionManager'; +import type KeyRing from '../../keys/KeyRing'; +import type Logger from '@matrixai/logger'; +import type { Host, Port } from '../../network/types'; +import type NodeManager from '../../nodes/NodeManager'; +import type { AgentRPCRequestParams, AgentRPCResponseResult } from '../types'; +import type { NodeId } from '../../ids'; +import type { HolePunchRelayMessage } from './types'; +import { validateSync } from '../../validation'; +import { matchSync } from '../../utils'; +import * as validationUtils from '../../validation/utils'; +import * as nodesUtils from '../../nodes/utils'; +import { UnaryHandler } from '../../rpc/handlers'; class NodesHolePunchMessageSendHandler extends UnaryHandler< { @@ -23,13 +23,14 @@ class NodesHolePunchMessageSendHandler extends UnaryHandler< }, AgentRPCRequestParams, AgentRPCResponseResult - > { +> { public async handle( input: AgentRPCRequestParams, _, meta, ): Promise { - const {db, nodeConnectionManager, keyRing, nodeManager, logger} = this.container + const { db, nodeConnectionManager, keyRing, nodeManager, logger } = + this.container; const { targetId, sourceId, @@ -81,9 +82,9 @@ class NodesHolePunchMessageSendHandler extends UnaryHandler< // If so, ask the nodeManager to relay to the node const targetNodeId = input.dstIdEncoded; const proxyAddress = { - host: connectionInfo!.remoteHost, - port: connectionInfo!.remotePort, - } + host: connectionInfo!.remoteHost, + port: connectionInfo!.remotePort, + }; // Checking if the source and destination are the same if (sourceId?.equals(targetId)) { // Logging and silently dropping operation @@ -91,11 +92,7 @@ class NodesHolePunchMessageSendHandler extends UnaryHandler< return {}; } logger.debug( - `Relaying signaling message from ${srcNodeId}@${ - proxyAddress.host - }:${ - proxyAddress.port - } to ${targetNodeId} with information ${proxyAddress}`, + `Relaying signaling message from ${srcNodeId}@${proxyAddress.host}:${proxyAddress.port} to ${targetNodeId} with information ${proxyAddress}`, ); // TODO: fix call.request.setProxyAddress(proxyAddress); @@ -105,7 +102,7 @@ class NodesHolePunchMessageSendHandler extends UnaryHandler< }); } }); - return {} + return {}; } } diff --git a/src/agent/handlers/notificationsSend.ts b/src/agent/handlers/notificationsSend.ts new file mode 100644 index 0000000000..7abc3502f0 --- /dev/null +++ b/src/agent/handlers/notificationsSend.ts @@ -0,0 +1,34 @@ +import type { DB } from '@matrixai/db'; +import type KeyRing from '../../keys/KeyRing'; +import type NotificationsManager from '../../notifications/NotificationsManager'; +import type { SignedNotification } from '../../notifications/types'; +import type { SignedNotificationEncoded } from './types'; +import type { AgentRPCRequestParams, AgentRPCResponseResult } from '../types'; +import { UnaryHandler } from '../../rpc/handlers'; +import * as notificationsUtils from '../../notifications/utils'; + +class NotificationsSendHandler extends UnaryHandler< + { + db: DB; + keyRing: KeyRing; + notificationsManager: NotificationsManager; + }, + AgentRPCRequestParams, + AgentRPCResponseResult +> { + public async handle( + input: AgentRPCRequestParams, + ): Promise { + const { db, keyRing, notificationsManager } = this.container; + const notification = await notificationsUtils.verifyAndDecodeNotif( + input.signedNotificationEncoded as SignedNotification, + keyRing.getNodeId(), + ); + await db.withTransactionF((tran) => + notificationsManager.receiveNotification(notification, tran), + ); + return {}; + } +} + +export { NotificationsSendHandler }; diff --git a/src/agent/handlers/serverManifest.ts b/src/agent/handlers/serverManifest.ts index b208675d29..3831d70915 100644 --- a/src/agent/handlers/serverManifest.ts +++ b/src/agent/handlers/serverManifest.ts @@ -1,37 +1,48 @@ -import {EchoHandler} from "./echo"; -import {NodesChainDataGetHandler} from "./nodesChainDataGet"; -import type {DB} from "@matrixai/db"; -import type Sigchain from "../../sigchain/Sigchain"; -import {NodesClosestLocalNodesGetHandler} from "@/agent/handlers/nodesClosestLocalNodesGet"; -import type NodeGraph from "../../nodes/NodeGraph"; -import {NodesCrossSignClaimHandler} from "./nodesCrossSignClaim"; -import type ACL from "../../acl/ACL"; -import type NodeManager from "../../nodes/NodeManager"; -import type KeyRing from "../../keys/KeyRing"; -import {NodesHolePunchMessageSendHandler} from "@/agent/handlers/nodesHolePunchMessageSend"; -import type NodeConnectionManager from "../../nodes/NodeConnectionManager"; -import type Logger from "@matrixai/logger"; -// import {NodesClaimsGetHandler} from "./nodesClaimsGet"; - +import type { DB } from '@matrixai/db'; +import type Sigchain from '../../sigchain/Sigchain'; +import type NodeGraph from '../../nodes/NodeGraph'; +import type ACL from '../../acl/ACL'; +import type NodeManager from '../../nodes/NodeManager'; +import type KeyRing from '../../keys/KeyRing'; +import type NodeConnectionManager from '../../nodes/NodeConnectionManager'; +import type Logger from '@matrixai/logger'; +import type { NotificationsManager } from '../../notifications'; +import type { VaultManager } from '../../vaults'; +import { NodesClosestLocalNodesGetHandler } from './nodesClosestLocalNodesGet'; +import { NodesHolePunchMessageSendHandler } from './nodesHolePunchMessageSend'; +import { NodesCrossSignClaimHandler } from './nodesCrossSignClaim'; +// Import {NodesClaimsGetHandler} from "./nodesClaimsGet"; +import { NotificationsSendHandler } from './notificationsSend'; +import { NodesChainDataGetHandler } from './nodesChainDataGet'; +import { EchoHandler } from './echo'; +import { VaultsScanHandler } from './vaultsScan'; +import { VaultsGitInfoGetHandler } from './vaultsGitInfoGet'; +import { VaultsGitPackGetHandler } from './vaultsGitPackGet'; const serverManifest = (container: { - db: DB, - sigchain: Sigchain, - nodeGraph: NodeGraph, - acl: ACL, - nodeManager: NodeManager, - nodeConnectionManager: NodeConnectionManager, - keyRing: KeyRing, - logger: Logger, + db: DB; + sigchain: Sigchain; + nodeGraph: NodeGraph; + acl: ACL; + nodeManager: NodeManager; + nodeConnectionManager: NodeConnectionManager; + keyRing: KeyRing; + logger: Logger; + notificationsManager: NotificationsManager; + vaultManager: VaultManager; }) => { return { echo: new EchoHandler(container), nodesChainDataGet: new NodesChainDataGetHandler(container), - // nodesClaimsGet: new NodesClaimsGetHandler(container), + // NodesClaimsGet: new NodesClaimsGetHandler(container), nodesClosestLocalNodesGet: new NodesClosestLocalNodesGetHandler(container), nodesCrossSignClaim: new NodesCrossSignClaimHandler(container), nodesHolePunchMessageSend: new NodesHolePunchMessageSendHandler(container), - } -} + notificationsSend: new NotificationsSendHandler(container), + VaultsGitInfoGet: new VaultsGitInfoGetHandler(container), + VaultsGitPackGet: new VaultsGitPackGetHandler(container), + vaultsScan: new VaultsScanHandler(container), + }; +}; export { serverManifest }; diff --git a/src/agent/handlers/types.ts b/src/agent/handlers/types.ts index b6f3591a94..a9ff573f0b 100644 --- a/src/agent/handlers/types.ts +++ b/src/agent/handlers/types.ts @@ -1,19 +1,19 @@ -import {SignedTokenEncoded} from "@/tokens/types"; -import {NodeIdEncoded} from "@/ids"; - +import type { SignedTokenEncoded } from '../../tokens/types'; +import type { ClaimIdEncoded, NodeIdEncoded, VaultIdEncoded } from '../../ids'; +import type { VaultAction, VaultName } from '../../vaults/types'; +import type { SignedNotification } from '../../notifications/types'; export type EchoMessage = { - message: string, + message: string; }; export type ClaimIdMessage = { - claimIdEncoded: string; -} + claimIdEncoded: ClaimIdEncoded; +}; -export type AgentClaimMessage = - ClaimIdMessage & { +export type AgentClaimMessage = ClaimIdMessage & { signedTokenEncoded: SignedTokenEncoded; -} +}; export type NodeIdMessage = { nodeIdEncoded: NodeIdEncoded; @@ -24,12 +24,39 @@ export type AddressMessage = { port: number; }; -export type NodeAddressMessage = - NodeIdMessage & - AddressMessage; +export type NodeAddressMessage = NodeIdMessage & AddressMessage; export type HolePunchRelayMessage = { - srcIdEncoded: string, - dstIdEncoded: string, - address: AddressMessage, + srcIdEncoded: NodeIdEncoded; + dstIdEncoded: NodeIdEncoded; + address: AddressMessage; +}; + +export type SignedNotificationEncoded = { + signedNotificationEncoded: SignedNotification; +}; + +export type VaultInfo = { + vaultIdEncoded: VaultIdEncoded; + vaultName: VaultName; +}; + +export type VaultsScanMessage = VaultInfo & { + vaultPermissions: Array; +}; + +export type VaultsGitInfoGetMessage = { + vaultNameOrId: VaultIdEncoded | VaultName; + action: VaultAction; +}; + +export type GitPackMessage = { + /** + * Chunk of data in binary form; + */ + chunk: string; +}; + +export type VaultsGitPackGetMessage = { + body: string; }; diff --git a/src/agent/handlers/vaultsGitInfoGet.ts b/src/agent/handlers/vaultsGitInfoGet.ts new file mode 100644 index 0000000000..4187514fb8 --- /dev/null +++ b/src/agent/handlers/vaultsGitInfoGet.ts @@ -0,0 +1,107 @@ +import type { GitPackMessage, VaultInfo } from './types'; +import type { AgentRPCRequestParams, AgentRPCResponseResult } from '../types'; +import type { DB } from '@matrixai/db'; +import type { VaultManager } from '../../vaults'; +import type { ACL } from '../../acl'; +import type Logger from '@matrixai/logger'; +import type { VaultsGitInfoGetMessage } from './types'; +import type { VaultAction } from '../../vaults/types'; +import * as vaultsUtils from '../../vaults/utils'; +import * as vaultsErrors from '../../vaults/errors'; +import { ServerHandler } from '../../rpc/handlers'; +import { validateSync } from '../../validation'; +import { matchSync } from '../../utils'; +import * as validationUtils from '../../validation/utils'; +import * as agentErrors from '../errors'; +import * as nodesUtils from '../../nodes/utils'; + +class VaultsGitInfoGetHandler extends ServerHandler< + { + db: DB; + vaultManager: VaultManager; + acl: ACL; + logger: Logger; + }, + AgentRPCRequestParams, + AgentRPCResponseResult +> { + public async *handle( + input: AgentRPCRequestParams, + _, + meta, + ): AsyncGenerator { + const { db, vaultManager, acl } = this.container; + yield* db.withTransactionG(async function* ( + tran, + ): AsyncGenerator { + const vaultIdFromName = await vaultManager.getVaultId( + input.vaultNameOrId, + tran, + ); + const vaultId = + vaultIdFromName ?? vaultsUtils.decodeVaultId(input.vaultNameOrId); + if (vaultId == null) { + throw new vaultsErrors.ErrorVaultsVaultUndefined(); + } + const { + actionType, + }: { + actionType: VaultAction; + } = validateSync( + (keyPath, value) => { + return matchSync(keyPath)( + [['actionType'], () => validationUtils.parseVaultAction(value)], + () => value, + ); + }, + { + actionType: input.action, + }, + ); + const vaultName = (await vaultManager.getVaultMeta(vaultId, tran)) + ?.vaultName; + if (vaultName == null) { + throw new vaultsErrors.ErrorVaultsVaultUndefined(); + } + // Getting the NodeId from the ReverseProxy connection info + const connectionInfo = meta; + // If this is getting run the connection exists + // It SHOULD exist here + if (connectionInfo == null) { + throw new agentErrors.ErrorConnectionInfoMissing(); + } + const nodeId = connectionInfo.remoteNodeId; + const nodeIdEncoded = nodesUtils.encodeNodeId(nodeId); + const permissions = await acl.getNodePerm(nodeId, tran); + if (permissions == null) { + throw new vaultsErrors.ErrorVaultsPermissionDenied( + `No permissions found for ${nodeIdEncoded}`, + ); + } + const vaultPerms = permissions.vaults[vaultId]; + if (vaultPerms?.[actionType] !== null) { + throw new vaultsErrors.ErrorVaultsPermissionDenied( + `${nodeIdEncoded} does not have permission to ${actionType} from vault ${vaultsUtils.encodeVaultId( + vaultId, + )}`, + ); + } + + yield { + vaultName: vaultName, + vaultIdEncoded: vaultsUtils.encodeVaultId(vaultId), + }; + for await (const byte of vaultManager.handleInfoRequest(vaultId, tran)) { + if (byte !== null) { + yield { + chunk: byte.toString('binary'), + }; + } else { + return; + } + } + }); + } +} + +export { VaultsGitInfoGetHandler }; diff --git a/src/agent/handlers/vaultsGitPackGet.ts b/src/agent/handlers/vaultsGitPackGet.ts new file mode 100644 index 0000000000..2a0787171d --- /dev/null +++ b/src/agent/handlers/vaultsGitPackGet.ts @@ -0,0 +1,109 @@ +import type { VaultAction, VaultName } from '../../vaults/types'; +import type VaultManager from '../../vaults/VaultManager'; +import type ACL from '../../acl/ACL'; +import type { DB } from '@matrixai/db'; +import type { GitPackMessage, VaultsGitPackGetMessage } from './types'; +import type { AgentRPCRequestParams, AgentRPCResponseResult } from '../types'; +import * as nodesUtils from '../../nodes/utils'; +import * as vaultsUtils from '../../vaults/utils'; +import * as vaultsErrors from '../../vaults/errors'; +import { validateSync } from '../../validation'; +import { matchSync } from '../../utils'; +import * as validationUtils from '../../validation/utils'; +import * as agentErrors from '../errors'; +import { ServerHandler } from '../../rpc/handlers'; + +class VaultsGitPackGetHandler extends ServerHandler< + { + vaultManager: VaultManager; + acl: ACL; + db: DB; + }, + AgentRPCRequestParams, + AgentRPCResponseResult +> { + public async *handle( + input: AgentRPCRequestParams, + _, + meta, + ): AsyncGenerator> { + const { vaultManager, acl, db } = this.container; + // Getting the NodeId from the ReverseProxy connection info + const connectionInfo = meta; + // If this is getting run the connection exists + // It SHOULD exist here + if (connectionInfo == null) { + throw new agentErrors.ErrorConnectionInfoMissing(); + } + const nodeId = connectionInfo.remoteNodeId; + const nodeIdEncoded = nodesUtils.encodeNodeId(nodeId); + const nameOrId = meta.get('vaultNameOrId').pop()!.toString(); + yield* db.withTransactionG(async function* ( + tran, + ): AsyncGenerator> { + const vaultIdFromName = await vaultManager.getVaultId( + nameOrId as VaultName, + tran, + ); + const vaultId = vaultIdFromName ?? vaultsUtils.decodeVaultId(nameOrId); + if (vaultId == null) { + throw new vaultsErrors.ErrorVaultsVaultUndefined(); + } + const { + actionType, + }: { + actionType: VaultAction; + } = validateSync( + (keyPath, value) => { + return matchSync(keyPath)( + [['actionType'], () => validationUtils.parseVaultAction(value)], + () => value, + ); + }, + { + actionType: meta.get('vaultAction').pop()!.toString(), + }, + ); + // Checking permissions + const permissions = await acl.getNodePerm(nodeId, tran); + const vaultPerms = permissions?.vaults[vaultId]; + if (vaultPerms?.[actionType] !== null) { + throw new vaultsErrors.ErrorVaultsPermissionDenied( + `${nodeIdEncoded} does not have permission to ${actionType} from vault ${vaultsUtils.encodeVaultId( + vaultId, + )}`, + ); + } + const [sideBand, progressStream] = await vaultManager.handlePackRequest( + vaultId, + Buffer.from(input.body, 'utf-8'), + tran, + ); + yield { + chunk: Buffer.from('0008NAK\n').toString('binary'), + }; + const responseBuffers: Uint8Array[] = []; + // FIXME: this WHOLE thing needs to change, why are we streaming when we send monolithic messages? + const result = await new Promise((resolve, reject) => { + sideBand.on('data', async (data: Uint8Array) => { + responseBuffers.push(data); + }); + sideBand.on('end', async () => { + const result = Buffer.concat(responseBuffers).toString('binary'); + resolve(result); + }); + sideBand.on('error', (err) => { + reject(err); + }); + progressStream.write(Buffer.from('0014progress is at 50%\n')); + progressStream.end(); + }); + yield { + chunk: result, + }; + }); + return; + } +} + +export { VaultsGitPackGetHandler }; diff --git a/src/agent/handlers/vaultsScan.ts b/src/agent/handlers/vaultsScan.ts new file mode 100644 index 0000000000..8fe8ea6e52 --- /dev/null +++ b/src/agent/handlers/vaultsScan.ts @@ -0,0 +1,50 @@ +import type { VaultsScanMessage } from './types'; +import type { AgentRPCRequestParams, AgentRPCResponseResult } from '../types'; +import type VaultManager from '../../vaults/VaultManager'; +import type { DB } from '@matrixai/db'; +import { ServerHandler } from '../../rpc/handlers'; +import * as agentErrors from '../errors'; +import * as vaultsUtils from '../../vaults/utils'; + +class VaultsScanHandler extends ServerHandler< + { + vaultManager: VaultManager; + db: DB; + }, + AgentRPCRequestParams, + AgentRPCResponseResult +> { + public async *handle( + input: AgentRPCRequestParams, + _, + meta, + ): AsyncGenerator> { + const { vaultManager, db } = this.container; + // Getting the NodeId from the ReverseProxy connection info + const connectionInfo = meta; + // If this is getting run the connection exists + // It SHOULD exist here + if (connectionInfo == null) { + throw new agentErrors.ErrorConnectionInfoMissing(); + } + const nodeId = connectionInfo.remoteNodeId; + yield* db.withTransactionG(async function* ( + tran, + ): AsyncGenerator> { + const listResponse = vaultManager.handleScanVaults(nodeId, tran); + for await (const { + vaultId, + vaultName, + vaultPermissions, + } of listResponse) { + yield { + vaultIdEncoded: vaultsUtils.encodeVaultId(vaultId), + vaultName, + vaultPermissions, + }; + } + }); + } +} + +export { VaultsScanHandler };