diff --git a/src/clientRPC/handlers/agentLockAll.ts b/src/clientRPC/handlers/agentLockAll.ts index a79c5897e0..2cb3a8dc31 100644 --- a/src/clientRPC/handlers/agentLockAll.ts +++ b/src/clientRPC/handlers/agentLockAll.ts @@ -1,4 +1,3 @@ -import type Logger from '@matrixai/logger'; import type { RPCRequestParams, RPCResponseResult } from '../types'; import type { DB } from '@matrixai/db'; import type SessionManager from '../../sessions/SessionManager'; @@ -14,15 +13,13 @@ class AgentLockAllHandler extends UnaryHandler< { sessionManager: SessionManager; db: DB; - logger: Logger; }, RPCRequestParams, RPCResponseResult > { public async handle(): Promise { - await this.container.db.withTransactionF((tran) => - this.container.sessionManager.resetKey(tran), - ); + const { db, sessionManager } = this.container; + await db.withTransactionF((tran) => sessionManager.resetKey(tran)); return {}; } } diff --git a/src/clientRPC/handlers/agentStatus.ts b/src/clientRPC/handlers/agentStatus.ts index 8e53106f67..0415d6fe0f 100644 --- a/src/clientRPC/handlers/agentStatus.ts +++ b/src/clientRPC/handlers/agentStatus.ts @@ -1,6 +1,4 @@ import type KeyRing from '../../keys/KeyRing'; -import type CertManager from '../../keys/CertManager'; -import type Logger from '@matrixai/logger'; import type { NodeIdEncoded } from '../../ids'; import type { RPCRequestParams, RPCResponseResult } from '../types'; import * as nodesUtils from '../../nodes/utils'; @@ -22,18 +20,17 @@ const agentStatusCaller = new UnaryCaller< class AgentStatusHandler extends UnaryHandler< { keyRing: KeyRing; - certManager: CertManager; - logger: Logger; }, RPCRequestParams, RPCResponseResult > { public async handle(): Promise> { + const { keyRing } = this.container; return { pid: process.pid, - nodeId: nodesUtils.encodeNodeId(this.container.keyRing.getNodeId()), + nodeId: nodesUtils.encodeNodeId(keyRing.getNodeId()), publicJwk: JSON.stringify( - keysUtils.publicKeyToJWK(this.container.keyRing.keyPair.publicKey), + keysUtils.publicKeyToJWK(keyRing.keyPair.publicKey), ), }; } diff --git a/src/clientRPC/handlers/agentStop.ts b/src/clientRPC/handlers/agentStop.ts index 368a2e7d3d..d1ebd53a32 100644 --- a/src/clientRPC/handlers/agentStop.ts +++ b/src/clientRPC/handlers/agentStop.ts @@ -16,7 +16,7 @@ class AgentStopHandler extends UnaryHandler< RPCResponseResult > { public async handle(): Promise { - const pkAgent = this.container.pkAgent; + const { pkAgent } = this.container; // If not running or in stopping status, then respond successfully if (!pkAgent[running] || pkAgent[status] === 'stopping') { return {}; diff --git a/src/clientRPC/handlers/agentUnlock.ts b/src/clientRPC/handlers/agentUnlock.ts index e0c6756df9..de50f41e3e 100644 --- a/src/clientRPC/handlers/agentUnlock.ts +++ b/src/clientRPC/handlers/agentUnlock.ts @@ -1,5 +1,5 @@ -import type Logger from '@matrixai/logger'; import type { RPCRequestParams, RPCResponseResult } from '../types'; +import type { ContainerType } from 'RPC/types'; import { UnaryHandler } from '../../RPC/handlers'; import { UnaryCaller } from '../../RPC/callers'; @@ -9,7 +9,7 @@ const agentUnlockCaller = new UnaryCaller< >(); class AgentUnlockHandler extends UnaryHandler< - { logger: Logger }, + ContainerType, RPCRequestParams, RPCResponseResult > { diff --git a/src/clientRPC/handlers/gestaltsActionsGetByIdentity.ts b/src/clientRPC/handlers/gestaltsActionsGetByIdentity.ts new file mode 100644 index 0000000000..865ad74b76 --- /dev/null +++ b/src/clientRPC/handlers/gestaltsActionsGetByIdentity.ts @@ -0,0 +1,78 @@ +import type { RPCRequestParams, RPCResponseResult } from '../types'; +import type GestaltGraph from 'gestalts/GestaltGraph'; +import type { DB } from '@matrixai/db'; +import type { IdentityId, ProviderId } from 'ids/index'; +import type { GestaltAction } from 'gestalts/types'; +import { UnaryCaller } from '../../RPC/callers'; +import { UnaryHandler } from '../../RPC/handlers'; +import { validateSync } from '../../validation/index'; +import { matchSync } from '../../utils/index'; +import * as validationUtils from '../../validation/utils'; + +const gestaltsActionsGetByIdentityCaller = new UnaryCaller< + RPCRequestParams<{ + providerId: string; + identityId: string; + }>, + RPCResponseResult<{ + actionsList: Array; + }> +>(); + +class GestaltsActionsGetByIdentityHandler extends UnaryHandler< + { + gestaltGraph: GestaltGraph; + db: DB; + }, + RPCRequestParams<{ + providerId: string; + identityId: string; + }>, + RPCResponseResult<{ + actionsList: Array; + }> +> { + public async handle( + input: RPCRequestParams<{ + providerId: string; + identityId: string; + }>, + ): Promise< + RPCResponseResult<{ + actionsList: Array; + }> + > { + const { db, gestaltGraph } = this.container; + const { + providerId, + identityId, + }: { providerId: ProviderId; identityId: IdentityId } = validateSync( + (keyPath, value) => { + return matchSync(keyPath)( + [['providerId'], () => validationUtils.parseProviderId(value)], + [['identityId'], () => validationUtils.parseIdentityId(value)], + () => value, + ); + }, + { + providerId: input.providerId, + identityId: input.identityId, + }, + ); + const result = await db.withTransactionF((tran) => + gestaltGraph.getGestaltActions( + ['identity', [providerId, identityId]], + tran, + ), + ); + + return { + actionsList: Object.keys(result) as Array, + }; + } +} + +export { + gestaltsActionsGetByIdentityCaller, + GestaltsActionsGetByIdentityHandler, +}; diff --git a/src/clientRPC/handlers/gestaltsActionsGetByNode.ts b/src/clientRPC/handlers/gestaltsActionsGetByNode.ts new file mode 100644 index 0000000000..edc4e4bb09 --- /dev/null +++ b/src/clientRPC/handlers/gestaltsActionsGetByNode.ts @@ -0,0 +1,64 @@ +import type { RPCRequestParams, RPCResponseResult } from '../types'; +import type GestaltGraph from 'gestalts/GestaltGraph'; +import type { DB } from '@matrixai/db'; +import type { GestaltAction } from 'gestalts/types'; +import type { NodeId, NodeIdEncoded } from 'ids/index'; +import { UnaryCaller } from '../../RPC/callers'; +import { UnaryHandler } from '../../RPC/handlers'; +import { validateSync } from '../../validation/index'; +import { matchSync } from '../../utils/index'; +import * as validationUtils from '../../validation/utils'; + +const gestaltsActionsGetByNodeCaller = new UnaryCaller< + RPCRequestParams<{ + nodeIdEncoded: NodeIdEncoded; + }>, + RPCResponseResult<{ + actionsList: Array; + }> +>(); + +class GestaltsActionsGetByNodeHandler extends UnaryHandler< + { + gestaltGraph: GestaltGraph; + db: DB; + }, + RPCRequestParams<{ + nodeIdEncoded: NodeIdEncoded; + }>, + RPCResponseResult<{ + actionsList: Array; + }> +> { + public async handle( + input: RPCRequestParams<{ + nodeIdEncoded: NodeIdEncoded; + }>, + ): Promise< + RPCResponseResult<{ + actionsList: Array; + }> + > { + const { db, gestaltGraph } = this.container; + const { nodeId }: { nodeId: NodeId } = validateSync( + (keyPath, value) => { + return matchSync(keyPath)( + [['nodeId'], () => validationUtils.parseNodeId(value)], + () => value, + ); + }, + { + nodeId: input.nodeIdEncoded, + }, + ); + const result = await db.withTransactionF((tran) => + gestaltGraph.getGestaltActions(['node', nodeId], tran), + ); + + return { + actionsList: Object.keys(result) as Array, + }; + } +} + +export { gestaltsActionsGetByNodeCaller, GestaltsActionsGetByNodeHandler }; diff --git a/src/clientRPC/handlers/gestaltsActionsSetByIdentity.ts b/src/clientRPC/handlers/gestaltsActionsSetByIdentity.ts new file mode 100644 index 0000000000..fd8d93c72d --- /dev/null +++ b/src/clientRPC/handlers/gestaltsActionsSetByIdentity.ts @@ -0,0 +1,78 @@ +import type { RPCRequestParams, RPCResponseResult } from '../types'; +import type { DB } from '@matrixai/db'; +import type { IdentityId, ProviderId } from 'ids/index'; +import type { GestaltAction } from '../../gestalts/types'; +import type GestaltGraph from 'gestalts/GestaltGraph'; +import { UnaryCaller } from '../../RPC/callers'; +import { UnaryHandler } from '../../RPC/handlers'; +import { validateSync } from '../../validation/index'; +import { matchSync } from '../../utils/index'; +import * as validationUtils from '../../validation/utils'; + +const gestaltsActionsSetByIdentityCaller = new UnaryCaller< + RPCRequestParams<{ + action: GestaltAction; + providerId: string; + identityId: string; + }>, + RPCResponseResult +>(); + +class GestaltsActionsSetByIdentityHandler extends UnaryHandler< + { + gestaltGraph: GestaltGraph; + db: DB; + }, + RPCRequestParams<{ + action: GestaltAction; + providerId: string; + identityId: string; + }>, + RPCResponseResult +> { + public async handle( + input: RPCRequestParams<{ + action: GestaltAction; + providerId: string; + identityId: string; + }>, + ): Promise { + const { db, gestaltGraph } = this.container; + const { + action, + providerId, + identityId, + }: { + action: GestaltAction; + providerId: ProviderId; + identityId: IdentityId; + } = validateSync( + (keyPath, value) => { + return matchSync(keyPath)( + [['action'], () => validationUtils.parseGestaltAction(value)], + [['providerId'], () => validationUtils.parseProviderId(value)], + [['identityId'], () => validationUtils.parseIdentityId(value)], + () => value, + ); + }, + { + action: input.action, + providerId: input.providerId, + identityId: input.identityId, + }, + ); + await db.withTransactionF((tran) => + gestaltGraph.setGestaltAction( + ['identity', [providerId, identityId]], + action, + tran, + ), + ); + return {}; + } +} + +export { + gestaltsActionsSetByIdentityCaller, + GestaltsActionsSetByIdentityHandler, +}; diff --git a/src/clientRPC/handlers/gestaltsActionsSetByNode.ts b/src/clientRPC/handlers/gestaltsActionsSetByNode.ts new file mode 100644 index 0000000000..849016cb55 --- /dev/null +++ b/src/clientRPC/handlers/gestaltsActionsSetByNode.ts @@ -0,0 +1,59 @@ +import type { RPCRequestParams, RPCResponseResult } from '../types'; +import type { DB } from '@matrixai/db'; +import type { GestaltAction } from '../../gestalts/types'; +import type GestaltGraph from 'gestalts/GestaltGraph'; +import type { NodeId } from 'ids/index'; +import { UnaryCaller } from '../../RPC/callers'; +import { UnaryHandler } from '../../RPC/handlers'; +import { validateSync } from '../../validation/index'; +import { matchSync } from '../../utils/index'; +import * as validationUtils from '../../validation/utils'; + +const gestaltsActionsSetByNodeCaller = new UnaryCaller< + RPCRequestParams<{ + action: GestaltAction; + nodeIdEncoded: string; + }>, + RPCResponseResult +>(); + +class GestaltsActionsSetByNodeHandler extends UnaryHandler< + { + gestaltGraph: GestaltGraph; + db: DB; + }, + RPCRequestParams<{ + action: GestaltAction; + nodeIdEncoded: string; + }>, + RPCResponseResult +> { + public async handle( + input: RPCRequestParams<{ + action: GestaltAction; + nodeIdEncoded: string; + }>, + ): Promise { + const { db, gestaltGraph } = this.container; + const { nodeId, action }: { nodeId: NodeId; action: GestaltAction } = + validateSync( + (keyPath, value) => { + return matchSync(keyPath)( + [['nodeId'], () => validationUtils.parseNodeId(value)], + [['action'], () => validationUtils.parseGestaltAction(value)], + () => value, + ); + }, + { + nodeId: input.nodeIdEncoded, + action: input.action, + }, + ); + await db.withTransactionF((tran) => + gestaltGraph.setGestaltAction(['node', nodeId], action, tran), + ); + return {}; + } +} + +export { gestaltsActionsSetByNodeCaller, GestaltsActionsSetByNodeHandler }; diff --git a/src/clientRPC/handlers/gestaltsActionsUnsetByIdentity.ts b/src/clientRPC/handlers/gestaltsActionsUnsetByIdentity.ts new file mode 100644 index 0000000000..1c1ac6214b --- /dev/null +++ b/src/clientRPC/handlers/gestaltsActionsUnsetByIdentity.ts @@ -0,0 +1,78 @@ +import type { RPCRequestParams, RPCResponseResult } from '../types'; +import type { DB } from '@matrixai/db'; +import type { IdentityId, ProviderId } from 'ids/index'; +import type { GestaltAction } from '../../gestalts/types'; +import type GestaltGraph from 'gestalts/GestaltGraph'; +import { UnaryCaller } from '../../RPC/callers'; +import { UnaryHandler } from '../../RPC/handlers'; +import { validateSync } from '../../validation/index'; +import { matchSync } from '../../utils/index'; +import * as validationUtils from '../../validation/utils'; + +const gestaltsActionsUnsetByIdentityCaller = new UnaryCaller< + RPCRequestParams<{ + action: GestaltAction; + providerId: string; + identityId: string; + }>, + RPCResponseResult +>(); + +class GestaltsActionsUnsetByIdentityHandler extends UnaryHandler< + { + gestaltGraph: GestaltGraph; + db: DB; + }, + RPCRequestParams<{ + action: GestaltAction; + providerId: string; + identityId: string; + }>, + RPCResponseResult +> { + public async handle( + input: RPCRequestParams<{ + action: GestaltAction; + providerId: string; + identityId: string; + }>, + ): Promise { + const { db, gestaltGraph } = this.container; + const { + action, + providerId, + identityId, + }: { + action: GestaltAction; + providerId: ProviderId; + identityId: IdentityId; + } = validateSync( + (keyPath, value) => { + return matchSync(keyPath)( + [['action'], () => validationUtils.parseGestaltAction(value)], + [['providerId'], () => validationUtils.parseProviderId(value)], + [['identityId'], () => validationUtils.parseIdentityId(value)], + () => value, + ); + }, + { + action: input.action, + providerId: input.providerId, + identityId: input.identityId, + }, + ); + await db.withTransactionF((tran) => + gestaltGraph.unsetGestaltAction( + ['identity', [providerId, identityId]], + action, + tran, + ), + ); + return {}; + } +} + +export { + gestaltsActionsUnsetByIdentityCaller, + GestaltsActionsUnsetByIdentityHandler, +}; diff --git a/src/clientRPC/handlers/gestaltsActionsUnsetByNode.ts b/src/clientRPC/handlers/gestaltsActionsUnsetByNode.ts new file mode 100644 index 0000000000..5427c67e8b --- /dev/null +++ b/src/clientRPC/handlers/gestaltsActionsUnsetByNode.ts @@ -0,0 +1,59 @@ +import type { RPCRequestParams, RPCResponseResult } from '../types'; +import type { DB } from '@matrixai/db'; +import type { GestaltAction } from '../../gestalts/types'; +import type GestaltGraph from 'gestalts/GestaltGraph'; +import type { NodeId } from 'ids/index'; +import { UnaryCaller } from '../../RPC/callers'; +import { UnaryHandler } from '../../RPC/handlers'; +import { validateSync } from '../../validation/index'; +import { matchSync } from '../../utils/index'; +import * as validationUtils from '../../validation/utils'; + +const gestaltsActionsUnsetByNodeCaller = new UnaryCaller< + RPCRequestParams<{ + action: GestaltAction; + nodeIdEncoded: string; + }>, + RPCResponseResult +>(); + +class GestaltsActionsUnsetByNodeHandler extends UnaryHandler< + { + gestaltGraph: GestaltGraph; + db: DB; + }, + RPCRequestParams<{ + action: GestaltAction; + nodeIdEncoded: string; + }>, + RPCResponseResult +> { + public async handle( + input: RPCRequestParams<{ + action: GestaltAction; + nodeIdEncoded: string; + }>, + ): Promise { + const { db, gestaltGraph } = this.container; + const { nodeId, action }: { nodeId: NodeId; action: GestaltAction } = + validateSync( + (keyPath, value) => { + return matchSync(keyPath)( + [['nodeId'], () => validationUtils.parseNodeId(value)], + [['action'], () => validationUtils.parseGestaltAction(value)], + () => value, + ); + }, + { + nodeId: input.nodeIdEncoded, + action: input.action, + }, + ); + await db.withTransactionF((tran) => + gestaltGraph.unsetGestaltAction(['node', nodeId], action, tran), + ); + return {}; + } +} + +export { gestaltsActionsUnsetByNodeCaller, GestaltsActionsUnsetByNodeHandler }; diff --git a/src/clientRPC/handlers/index.ts b/src/clientRPC/handlers/index.ts index 148f8f734d..ddb28a768d 100644 --- a/src/clientRPC/handlers/index.ts +++ b/src/clientRPC/handlers/index.ts @@ -4,10 +4,35 @@ import type KeyRing from '../../keys/KeyRing'; import type CertManager from '../../keys/CertManager'; import type PolykeyAgent from '../../PolykeyAgent'; import type { DB } from '@matrixai/db'; -import { agentStatusCaller, AgentStatusHandler } from './agentStatus'; -import { agentStopCaller, AgentStopHandler } from './agentStop'; -import { agentUnlockCaller, AgentUnlockHandler } from './agentUnlock'; +import type GestaltGraph from '../../gestalts/GestaltGraph'; +import { + gestaltsActionsGetByIdentityCaller, + GestaltsActionsGetByIdentityHandler, +} from 'clientRPC/handlers/gestaltsActionsGetByIdentity'; +import { + gestaltsActionsGetByNodeCaller, + GestaltsActionsGetByNodeHandler, +} from 'clientRPC/handlers/gestaltsActionsGetByNode'; +import { + gestaltsActionsSetByIdentityCaller, + GestaltsActionsSetByIdentityHandler, +} from 'clientRPC/handlers/gestaltsActionsSetByIdentity'; +import { + gestaltsActionsSetByNodeCaller, + GestaltsActionsSetByNodeHandler, +} from 'clientRPC/handlers/gestaltsActionsSetByNode'; +import { + gestaltsActionsUnsetByIdentityCaller, + GestaltsActionsUnsetByIdentityHandler, +} from 'clientRPC/handlers/gestaltsActionsUnsetByIdentity'; +import { + gestaltsActionsUnsetByNodeCaller, + GestaltsActionsUnsetByNodeHandler, +} from 'clientRPC/handlers/gestaltsActionsUnsetByNode'; import { agentLockAllCaller, AgentLockAllHandler } from './agentLockAll'; +import { agentUnlockCaller, AgentUnlockHandler } from './agentUnlock'; +import { agentStopCaller, AgentStopHandler } from './agentStop'; +import { agentStatusCaller, AgentStatusHandler } from './agentStatus'; const serverManifest = (container: { pkAgent: PolykeyAgent; @@ -15,6 +40,7 @@ const serverManifest = (container: { certManager: CertManager; db: DB; sessionManager: SessionManager; + gestaltGraph: GestaltGraph; logger: Logger; }) => { // No type used here, it will override type inference @@ -23,6 +49,20 @@ const serverManifest = (container: { agentStatus: new AgentStatusHandler(container), agentStop: new AgentStopHandler(container), agentUnlock: new AgentUnlockHandler(container), + GestaltsActionsGetByIdentity: new GestaltsActionsGetByIdentityHandler( + container, + ), + GestaltsActionsGetByNode: new GestaltsActionsGetByNodeHandler(container), + GestaltsActionsSetByIdentity: new GestaltsActionsSetByIdentityHandler( + container, + ), + GestaltsActionsSetByNode: new GestaltsActionsSetByNodeHandler(container), + GestaltsActionsUnsetByIdentity: new GestaltsActionsUnsetByIdentityHandler( + container, + ), + GestaltsActionsUnsetByNode: new GestaltsActionsUnsetByNodeHandler( + container, + ), }; }; @@ -32,6 +72,12 @@ const clientManifest = { agentStatus: agentStatusCaller, agentStop: agentStopCaller, agentUnlock: agentUnlockCaller, + gestaltsActionsGetByIdentity: gestaltsActionsGetByIdentityCaller, + gestaltsActionsGetByNode: gestaltsActionsGetByNodeCaller, + gestaltsActionsSetByIdentity: gestaltsActionsSetByIdentityCaller, + gestaltsActionsSetByNode: gestaltsActionsSetByNodeCaller, + gestaltsActionsUnsetByIdentity: gestaltsActionsUnsetByIdentityCaller, + gestaltsActionsUnsetByNode: gestaltsActionsUnsetByNodeCaller, }; export { serverManifest, clientManifest }; diff --git a/tests/clientRPC/handlers/agentLockAll.test.ts b/tests/clientRPC/handlers/agentLockAll.test.ts index 44093d2898..a331b05898 100644 --- a/tests/clientRPC/handlers/agentLockAll.test.ts +++ b/tests/clientRPC/handlers/agentLockAll.test.ts @@ -14,8 +14,8 @@ import { } from '@/clientRPC/handlers/agentLockAll'; import RPCClient from '@/RPC/RPCClient'; import { SessionManager } from '@/sessions'; -import ClientServer from '@/clientRPC/ClientServer'; -import ClientClient from '@/clientRPC/ClientClient'; +import WebSocketServer from '@/websockets/WebSocketServer'; +import WebSocketClient from '@/websockets/WebSocketClient'; import * as testsUtils from '../../utils'; describe('agentLockAll', () => { @@ -31,8 +31,8 @@ describe('agentLockAll', () => { let keyRing: KeyRing; let taskManager: TaskManager; let sessionManager: SessionManager; - let clientClient: ClientClient; - let clientServer: ClientServer; + let webSocketClient: WebSocketClient; + let webSocketServer: WebSocketServer; let tlsConfig: TLSConfig; beforeEach(async () => { @@ -62,8 +62,8 @@ describe('agentLockAll', () => { tlsConfig = await testsUtils.createTLSConfig(keyRing.keyPair); }); afterEach(async () => { - await clientServer.stop(true); - await clientClient.destroy(true); + await webSocketServer.stop(true); + await webSocketClient.destroy(true); await taskManager.stop(); await keyRing.stop(); await db.stop(); @@ -84,24 +84,24 @@ describe('agentLockAll', () => { }, logger, }); - clientServer = await ClientServer.createClientServer({ + webSocketServer = await WebSocketServer.createWebSocketServer({ connectionCallback: (streamPair, connectionInfo) => rpcServer.handleStream(streamPair, connectionInfo), host, tlsConfig, logger: logger.getChild('server'), }); - clientClient = await ClientClient.createClientClient({ + webSocketClient = await WebSocketClient.createWebSocketClient({ expectedNodeIds: [keyRing.getNodeId()], host, logger: logger.getChild('client'), - port: clientServer.port, + port: webSocketServer.port, }); const rpcClient = await RPCClient.createRPCClient({ manifest: { agentLockAll: agentLockAllCaller, }, - streamPairCreateCallback: async () => clientClient.startConnection(), + streamPairCreateCallback: async () => webSocketClient.startConnection(), logger: logger.getChild('clientRPC'), }); diff --git a/tests/clientRPC/handlers/agentStop.test.ts b/tests/clientRPC/handlers/agentStop.test.ts index cd248815b8..fc06baf13d 100644 --- a/tests/clientRPC/handlers/agentStop.test.ts +++ b/tests/clientRPC/handlers/agentStop.test.ts @@ -14,8 +14,8 @@ import { AgentStopHandler, } from '@/clientRPC/handlers/agentStop'; import RPCClient from '@/RPC/RPCClient'; -import ClientServer from '@/clientRPC/ClientServer'; -import ClientClient from '@/clientRPC/ClientClient'; +import WebSocketServer from '@/websockets/WebSocketServer'; +import WebSocketClient from '@/websockets/WebSocketClient'; import config from '@/config'; import PolykeyAgent from '@/PolykeyAgent'; import * as testsUtils from '../../utils'; @@ -34,8 +34,8 @@ describe('agentStop', () => { let db: DB; let keyRing: KeyRing; let taskManager: TaskManager; - let clientClient: ClientClient; - let clientServer: ClientServer; + let webSocketClient: WebSocketClient; + let webSocketServer: WebSocketServer; let tlsConfig: TLSConfig; let pkAgent: PolykeyAgent; @@ -72,8 +72,8 @@ describe('agentStop', () => { }); }); afterEach(async () => { - await clientServer.stop(true); - await clientClient.destroy(true); + await webSocketServer.stop(true); + await webSocketClient.destroy(true); await taskManager.stop(); await keyRing.stop(); await db.stop(); @@ -93,24 +93,24 @@ describe('agentStop', () => { }, logger, }); - clientServer = await ClientServer.createClientServer({ + webSocketServer = await WebSocketServer.createWebSocketServer({ connectionCallback: (streamPair, connectionInfo) => rpcServer.handleStream(streamPair, connectionInfo), host, tlsConfig, logger: logger.getChild('server'), }); - clientClient = await ClientClient.createClientClient({ + webSocketClient = await WebSocketClient.createWebSocketClient({ expectedNodeIds: [keyRing.getNodeId()], host, logger: logger.getChild('client'), - port: clientServer.port, + port: webSocketServer.port, }); const rpcClient = await RPCClient.createRPCClient({ manifest: { agentStop: agentStopCaller, }, - streamPairCreateCallback: async () => clientClient.startConnection(), + streamPairCreateCallback: async () => webSocketClient.startConnection(), logger: logger.getChild('clientRPC'), }); diff --git a/tests/clientRPC/handlers/gestaltsActionsSetUnsetGetByIdentity.test.ts b/tests/clientRPC/handlers/gestaltsActionsSetUnsetGetByIdentity.test.ts new file mode 100644 index 0000000000..b46c4a5d66 --- /dev/null +++ b/tests/clientRPC/handlers/gestaltsActionsSetUnsetGetByIdentity.test.ts @@ -0,0 +1,197 @@ +import type { TLSConfig } from '@/network/types'; +import type { ClaimLinkIdentity } from '@/claims/payloads/index'; +import type { + ClaimIdEncoded, + IdentityId, + ProviderId, + ProviderIdentityClaimId, +} from '@/ids/index'; +import type { GestaltIdentityInfo, GestaltNodeInfo } from '@/gestalts/types'; +import fs from 'fs'; +import path from 'path'; +import os from 'os'; +import Logger, { formatting, LogLevel, StreamHandler } from '@matrixai/logger'; +import { DB } from '@matrixai/db'; +import KeyRing from '@/keys/KeyRing'; +import * as keysUtils from '@/keys/utils'; +import RPCServer from '@/RPC/RPCServer'; +import TaskManager from '@/tasks/TaskManager'; +import { + gestaltsActionsGetByIdentityCaller, + GestaltsActionsGetByIdentityHandler, +} from '@/clientRPC/handlers/gestaltsActionsGetByIdentity'; +import { + gestaltsActionsSetByIdentityCaller, + GestaltsActionsSetByIdentityHandler, +} from '@/clientRPC/handlers/gestaltsActionsSetByIdentity'; +import { + gestaltsActionsUnsetByIdentityCaller, + GestaltsActionsUnsetByIdentityHandler, +} from '@/clientRPC/handlers/gestaltsActionsUnsetByIdentity'; +import RPCClient from '@/RPC/RPCClient'; +import WebSocketServer from '@/websockets/WebSocketServer'; +import WebSocketClient from '@/websockets/WebSocketClient'; +import GestaltGraph from '@/gestalts/GestaltGraph'; +import ACL from '@/acl/ACL'; +import * as nodesUtils from '@/nodes/utils'; +import { encodeProviderIdentityId } from '@/ids/index'; +import Token from '@/tokens/Token'; +import * as testsUtils from '../../utils'; + +describe('gestaltsActionsByIdentity', () => { + const logger = new Logger('agentUnlock test', LogLevel.WARN, [ + new StreamHandler( + formatting.format`${formatting.level}:${formatting.keys}:${formatting.msg}`, + ), + ]); + const password = 'helloWorld'; + const host = '127.0.0.1'; + let dataDir: string; + let db: DB; + let keyRing: KeyRing; + let taskManager: TaskManager; + let webSocketClient: WebSocketClient; + let webSocketServer: WebSocketServer; + let tlsConfig: TLSConfig; + let acl: ACL; + let gestaltGraph: GestaltGraph; + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), 'polykey-test-'), + ); + const keysPath = path.join(dataDir, 'keys'); + const dbPath = path.join(dataDir, 'db'); + db = await DB.createDB({ + dbPath, + logger, + }); + keyRing = await KeyRing.createKeyRing({ + password, + keysPath, + logger, + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }); + taskManager = await TaskManager.createTaskManager({ db, logger }); + tlsConfig = await testsUtils.createTLSConfig(keyRing.keyPair); + acl = await ACL.createACL({ + db, + logger, + }); + gestaltGraph = await GestaltGraph.createGestaltGraph({ + acl, + db, + logger, + }); + }); + afterEach(async () => { + await webSocketServer.stop(true); + await webSocketClient.destroy(true); + await acl.stop(); + await gestaltGraph.stop(); + await taskManager.stop(); + await keyRing.stop(); + await db.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + test('sets/unsets/gets actions by identity', async () => { + // Setup + const rpcServer = await RPCServer.createRPCServer({ + manifest: { + gestaltsActionsGetByIdentity: new GestaltsActionsGetByIdentityHandler({ + db, + gestaltGraph, + }), + gestaltsActionsSetByIdentity: new GestaltsActionsSetByIdentityHandler({ + db, + gestaltGraph, + }), + gestaltsActionsUnsetByIdentity: + new GestaltsActionsUnsetByIdentityHandler({ + db, + gestaltGraph, + }), + }, + logger, + }); + webSocketServer = await WebSocketServer.createWebSocketServer({ + connectionCallback: (streamPair, connectionInfo) => + rpcServer.handleStream(streamPair, connectionInfo), + host, + tlsConfig, + logger: logger.getChild('server'), + }); + webSocketClient = await WebSocketClient.createWebSocketClient({ + expectedNodeIds: [keyRing.getNodeId()], + host, + logger: logger.getChild('client'), + port: webSocketServer.port, + }); + const rpcClient = await RPCClient.createRPCClient({ + manifest: { + gestaltsActionsGetByIdentity: gestaltsActionsGetByIdentityCaller, + gestaltsActionsSetByIdentity: gestaltsActionsSetByIdentityCaller, + gestaltsActionsUnsetByIdentity: gestaltsActionsUnsetByIdentityCaller, + }, + streamPairCreateCallback: async () => webSocketClient.startConnection(), + logger: logger.getChild('clientRPC'), + }); + + // Doing the test + const nodeId = keyRing.getNodeId(); + const node: GestaltNodeInfo = { + nodeId: nodeId, + }; + const identity: GestaltIdentityInfo = { + identityId: 'identityId' as IdentityId, + providerId: 'providerId' as ProviderId, + }; + const dummyClaim: ClaimLinkIdentity = { + typ: 'ClaimLinkIdentity', + iss: nodesUtils.encodeNodeId(nodeId), + sub: encodeProviderIdentityId([identity.providerId, identity.identityId]), + jti: '' as ClaimIdEncoded, + iat: 0, + nbf: 0, + exp: 0, + aud: '', + seq: 0, + prevClaimId: null, + prevDigest: null, + }; + const token = Token.fromPayload(dummyClaim); + token.signWithPrivateKey(keyRing.keyPair); + const signedClaim = token.toSigned(); + await gestaltGraph.linkNodeAndIdentity(node, identity, { + claim: signedClaim, + meta: { providerIdentityClaimId: '' as ProviderIdentityClaimId }, + }); + + const action = 'notify' as const; + const providerMessage = { + providerId: identity.providerId, + identityId: identity.identityId, + }; + const setActionMessage = { + ...providerMessage, + action, + }; + await rpcClient.methods.gestaltsActionsSetByIdentity(setActionMessage); + // Check for permission + const getSetResponse = await rpcClient.methods.gestaltsActionsGetByIdentity( + providerMessage, + ); + expect(getSetResponse.actionsList).toContainEqual(action); + // Unset permission + await rpcClient.methods.gestaltsActionsUnsetByIdentity(setActionMessage); + // Check permission was removed + const getUnsetResponse = + await rpcClient.methods.gestaltsActionsGetByIdentity(providerMessage); + expect(getUnsetResponse.actionsList).toHaveLength(0); + }); +}); diff --git a/tests/clientRPC/handlers/gestaltsActionsSetUnsetGetByNode.test.ts b/tests/clientRPC/handlers/gestaltsActionsSetUnsetGetByNode.test.ts new file mode 100644 index 0000000000..f09fb0a7a2 --- /dev/null +++ b/tests/clientRPC/handlers/gestaltsActionsSetUnsetGetByNode.test.ts @@ -0,0 +1,164 @@ +import type { TLSConfig } from '@/network/types'; +import type { GestaltNodeInfo } from '@/gestalts/types'; +import fs from 'fs'; +import path from 'path'; +import os from 'os'; +import Logger, { formatting, LogLevel, StreamHandler } from '@matrixai/logger'; +import { DB } from '@matrixai/db'; +import KeyRing from '@/keys/KeyRing'; +import * as keysUtils from '@/keys/utils'; +import RPCServer from '@/RPC/RPCServer'; +import TaskManager from '@/tasks/TaskManager'; +import { + gestaltsActionsGetByNodeCaller, + GestaltsActionsGetByNodeHandler, +} from '@/clientRPC/handlers/gestaltsActionsGetByNode'; +import { + gestaltsActionsSetByNodeCaller, + GestaltsActionsSetByNodeHandler, +} from '@/clientRPC/handlers/gestaltsActionsSetByNode'; +import { + gestaltsActionsUnsetByNodeCaller, + GestaltsActionsUnsetByNodeHandler, +} from '@/clientRPC/handlers/gestaltsActionsUnsetByNode'; +import RPCClient from '@/RPC/RPCClient'; +import WebSocketServer from '@/websockets/WebSocketServer'; +import WebSocketClient from '@/websockets/WebSocketClient'; +import GestaltGraph from '@/gestalts/GestaltGraph'; +import ACL from '@/acl/ACL'; +import * as nodesUtils from '@/nodes/utils'; +import * as testsUtils from '../../utils'; + +describe('gestaltsActionsByIdentity', () => { + const logger = new Logger('agentUnlock test', LogLevel.WARN, [ + new StreamHandler( + formatting.format`${formatting.level}:${formatting.keys}:${formatting.msg}`, + ), + ]); + const password = 'helloWorld'; + const host = '127.0.0.1'; + let dataDir: string; + let db: DB; + let keyRing: KeyRing; + let taskManager: TaskManager; + let webSocketClient: WebSocketClient; + let webSocketServer: WebSocketServer; + let tlsConfig: TLSConfig; + let acl: ACL; + let gestaltGraph: GestaltGraph; + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), 'polykey-test-'), + ); + const keysPath = path.join(dataDir, 'keys'); + const dbPath = path.join(dataDir, 'db'); + db = await DB.createDB({ + dbPath, + logger, + }); + keyRing = await KeyRing.createKeyRing({ + password, + keysPath, + logger, + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }); + taskManager = await TaskManager.createTaskManager({ db, logger }); + tlsConfig = await testsUtils.createTLSConfig(keyRing.keyPair); + acl = await ACL.createACL({ + db, + logger, + }); + gestaltGraph = await GestaltGraph.createGestaltGraph({ + acl, + db, + logger, + }); + }); + afterEach(async () => { + await webSocketServer.stop(true); + await webSocketClient.destroy(true); + await acl.stop(); + await gestaltGraph.stop(); + await taskManager.stop(); + await keyRing.stop(); + await db.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + test('sets/unsets/gets actions by identity', async () => { + // Setup + const rpcServer = await RPCServer.createRPCServer({ + manifest: { + gestaltsActionsGetByNode: new GestaltsActionsGetByNodeHandler({ + db, + gestaltGraph, + }), + gestaltsActionsSetByNode: new GestaltsActionsSetByNodeHandler({ + db, + gestaltGraph, + }), + gestaltsActionsUnsetByNode: new GestaltsActionsUnsetByNodeHandler({ + db, + gestaltGraph, + }), + }, + logger, + }); + webSocketServer = await WebSocketServer.createWebSocketServer({ + connectionCallback: (streamPair, connectionInfo) => + rpcServer.handleStream(streamPair, connectionInfo), + host, + tlsConfig, + logger: logger.getChild('server'), + }); + webSocketClient = await WebSocketClient.createWebSocketClient({ + expectedNodeIds: [keyRing.getNodeId()], + host, + logger: logger.getChild('client'), + port: webSocketServer.port, + }); + const rpcClient = await RPCClient.createRPCClient({ + manifest: { + gestaltsActionsGetByNode: gestaltsActionsGetByNodeCaller, + gestaltsActionsSetByNode: gestaltsActionsSetByNodeCaller, + gestaltsActionsUnsetByNode: gestaltsActionsUnsetByNodeCaller, + }, + streamPairCreateCallback: async () => webSocketClient.startConnection(), + logger: logger.getChild('clientRPC'), + }); + + // Doing the test + const nodeId = keyRing.getNodeId(); + const node: GestaltNodeInfo = { + nodeId: nodeId, + }; + await gestaltGraph.setNode(node); + // Set permission + const nodeMessage = { + nodeIdEncoded: nodesUtils.encodeNodeId(nodeId), + }; + const action = 'notify' as const; + const requestMessage = { + ...nodeMessage, + action, + }; + await rpcClient.methods.gestaltsActionsSetByNode(requestMessage); + // Check for permission + const getSetResponse = await rpcClient.methods.gestaltsActionsGetByNode( + nodeMessage, + ); + expect(getSetResponse.actionsList).toContainEqual(action); + // Unset permission + await rpcClient.methods.gestaltsActionsUnsetByNode(requestMessage); + // Check permission was removed + const getUnsetResponse = await rpcClient.methods.gestaltsActionsGetByNode( + nodeMessage, + ); + expect(getUnsetResponse.actionsList).toHaveLength(0); + }); +});