From 9038b997a078dd113337e2d8756f7833ab7efbd7 Mon Sep 17 00:00:00 2001
From: Alexey Lunin <alexey.lunin@vereign.com>
Date: Tue, 25 Jul 2023 18:06:37 +0300
Subject: [PATCH] Make agent to be a mediator for mobile wallet

---
 .env.example                                  |  5 ++-
 apps/gateway/src/main.ts                      |  1 +
 compose/docker-compose.simple.yml             |  2 +
 compose/docker-compose.yml                    |  2 +
 compose/env/holder.env                        | 10 +++--
 compose/env/holder.simple.env                 |  9 ++--
 compose/env/issuer.env                        |  7 ++-
 compose/env/issuer.simple.env                 |  7 ++-
 libs/askar/src/agent.utils.ts                 | 20 +++++++--
 libs/askar/src/askar/agent.service.ts         |  2 +-
 libs/askar/src/askar/askar.service.ts         | 45 ++++++++++++++++---
 libs/config/src/config/agent.config.ts        | 28 +++++++++++-
 libs/config/src/config/ledgers.config.ts      |  2 +
 .../src/interfaces/agent.config.interface.ts  |  7 ++-
 .../interfaces/ledgers.config.interface.ts    |  2 +
 libs/config/src/schemas/agent.schema.ts       |  1 +
 libs/config/src/schemas/ledgers.schema.ts     |  2 +
 libs/ledgers/src/idunion/idunion.provider.ts  | 19 +++++---
 18 files changed, 141 insertions(+), 30 deletions(-)

diff --git a/.env.example b/.env.example
index 381d0cb6..89977bd5 100644
--- a/.env.example
+++ b/.env.example
@@ -1,10 +1,13 @@
 LEDGERS="BCOVRIN_TEST"
 IDUNION_KEY=#add if you are using IDUNION as a ledger
+IDUNION_BASIC_USER=
+IDUNION_BASIC_PASS=
 
-AGENT_PEER_URL="http://localhost:8001"
+AGENT_PEER_URL=http://localhost:8001,ws://localhost:8002
 AGENT_NAME=EXAMPTTLE_AGENT_45
 AGENT_KEY=EXAMPLE_AGENT_45_KEY
 AGENT_DID_SEED=200000000000000000000000ExampleT21 #random string min 32 chars
+AGENT_RUN_AS_MEDIATOR=false
 
 AGENT_DB_HOST=0.0.0.0:5432
 AGENT_DB_USER=postgres
diff --git a/apps/gateway/src/main.ts b/apps/gateway/src/main.ts
index 37b744b5..c6f57ec7 100644
--- a/apps/gateway/src/main.ts
+++ b/apps/gateway/src/main.ts
@@ -21,6 +21,7 @@ async function bootstrap() {
   const gatewayConfig = configService.get<IGateway>("gateway")!;
 
   const globalPrefix = "api";
+  app.enableCors();
   app.setGlobalPrefix(globalPrefix);
   app.enableShutdownHooks();
 
diff --git a/compose/docker-compose.simple.yml b/compose/docker-compose.simple.yml
index 0d8d9ad9..0544a788 100644
--- a/compose/docker-compose.simple.yml
+++ b/compose/docker-compose.simple.yml
@@ -21,6 +21,7 @@ services:
     ports:
       - "8080:8080"
       - "8001:8001"
+      - "8002:8002"
     depends_on:
       pg_db:
         condition: service_started
@@ -35,6 +36,7 @@ services:
     ports:
       - "8081:8080"
       - "6001:6001"
+      - "6002:6002"
     depends_on:
       pg_db:
         condition: service_started
diff --git a/compose/docker-compose.yml b/compose/docker-compose.yml
index 3646983b..e9e3bc1f 100644
--- a/compose/docker-compose.yml
+++ b/compose/docker-compose.yml
@@ -36,6 +36,7 @@ services:
     ports:
       - "8080:8080"
       - "8001:8001"
+      - "8002:8002"
     env_file:
       - ./env/issuer.env
     depends_on:
@@ -141,6 +142,7 @@ services:
     ports:
       - "8090:8080"
       - "6001:6001"
+      - "6002:6002"
     env_file:
       - ./env/holder.env
     depends_on:
diff --git a/compose/env/holder.env b/compose/env/holder.env
index c395fb46..538ac094 100644
--- a/compose/env/holder.env
+++ b/compose/env/holder.env
@@ -1,10 +1,13 @@
-LEDGERS="BCOVRIN_TEST"
+LEDGERS=BCOVRIN_TEST
 IDUNION_KEY=
+IDUNION_BASIC_USER=
+IDUNION_BASIC_PASS=
 
-AGENT_PEER_URL="http://agent-holder:6001"
+AGENT_PEER_URL=http://agent-holder:6001,ws://agent-holder:6002
 AGENT_NAME=DEV_AGENT_HOLDER_OCM_4 # this should be changed to company name
 AGENT_KEY=DEV_AGENT_HOLDER_OCM_4 #example random string
-AGENT_DID_SEED=2000000000000000CCA120000000TCuste21jsjs #did private key seed min lenght 32
+AGENT_DID_SEED=2000000000000000CCA120000000TCuste21jsjs #did private key seed min length 32
+AGENT_RUN_AS_MEDIATOR=false
 AGENT_DB_HOST=pg_db:5432
 AGENT_DB_USER=postgres
 AGENT_DB_PASS=postgres
@@ -14,6 +17,7 @@ AGENT_IS_REST=false
 AGENT_MAX_MESSAGES=10
 AGENT_RETE_LIMIT=5
 
+
 NATS_SERVERS=broker-holder:4222
 NATS_STREAM_NAME=ssi_holder_stream
 NATS_SUBJECTS="connections.*,proofs.*,credentials.*,schemas.*,messages.*"
diff --git a/compose/env/holder.simple.env b/compose/env/holder.simple.env
index 0db552de..c023e232 100644
--- a/compose/env/holder.simple.env
+++ b/compose/env/holder.simple.env
@@ -1,10 +1,13 @@
-LEDGERS="BCOVRIN_TEST"
+LEDGERS=BCOVRIN_TEST
 IDUNION_KEY=
+IDUNION_BASIC_USER=
+IDUNION_BASIC_PASS=
 
-AGENT_PEER_URL="http://agent-holder:6001"
+AGENT_PEER_URL=http://agent-holder:6001,ws://agent-holder:6002
 AGENT_NAME=DEV_SIMPLE_AGENT_HOLDER_OCM # this should be changed to company name
 AGENT_KEY=DEV_SIMPLE_AGENT_HOLDER_OCM #example random string
-AGENT_DID_SEED=200000000000000000000000TCuste21xh #did private key seed min lenght 32
+AGENT_DID_SEED=200000000000000000000000TCuste21xh #did private key seed min length 32
+AGENT_RUN_AS_MEDIATOR=false
 AGENT_DB_HOST=pg_db:5432
 AGENT_DB_USER=postgres
 AGENT_DB_PASS=postgres
diff --git a/compose/env/issuer.env b/compose/env/issuer.env
index d3774abd..65a6160a 100644
--- a/compose/env/issuer.env
+++ b/compose/env/issuer.env
@@ -1,10 +1,13 @@
-LEDGERS="BCOVRIN_TEST"
+LEDGERS=BCOVRIN_TEST
 IDUNION_KEY=
+IDUNION_BASIC_USER=
+IDUNION_BASIC_PASS=
 
-AGENT_PEER_URL="http://agent-issuer:8001"
+AGENT_PEER_URL=http://agent-issuer:8001,ws://agent-issuer:8002
 AGENT_NAME=DEV_AGENT_ISSUER_OCM_4 # this should be changed to company name
 AGENT_KEY=DEV_AGENT_ISSUER_OCM_4 #example random string
 AGENT_DID_SEED=20000000000000000000000aca0xxaDTCuste21udhasjs #did private key seed min lenght 32
+AGENT_RUN_AS_MEDIATOR=false
 AGENT_DB_HOST=pg_db:5432
 AGENT_DB_USER=postgres
 AGENT_DB_PASS=postgres
diff --git a/compose/env/issuer.simple.env b/compose/env/issuer.simple.env
index 22c8de61..92156c7b 100644
--- a/compose/env/issuer.simple.env
+++ b/compose/env/issuer.simple.env
@@ -1,10 +1,13 @@
-LEDGERS="BCOVRIN_TEST"
+LEDGERS=BCOVRIN_TEST
 IDUNION_KEY=
+IDUNION_BASIC_USER=
+IDUNION_BASIC_PASS=
 
-AGENT_PEER_URL="http://agent-issuer:8001"
+AGENT_PEER_URL=http://agent-issuer:8001,ws://agent-issuer:8002
 AGENT_NAME=DEV_SIMPLE_AGENT_ISSUER_OCM # this should be changed to company name
 AGENT_KEY=DEV_SIMPLE_AGENT_ISSUER_OCM #example random string
 AGENT_DID_SEED=200000000000000000000000TCuste21js #did private key seed min lenght 32
+AGENT_RUN_AS_MEDIATOR=false
 AGENT_DB_HOST=pg_db:5432
 AGENT_DB_USER=postgres
 AGENT_DB_PASS=postgres
diff --git a/libs/askar/src/agent.utils.ts b/libs/askar/src/agent.utils.ts
index 3eae5e95..76df1159 100644
--- a/libs/askar/src/agent.utils.ts
+++ b/libs/askar/src/agent.utils.ts
@@ -3,6 +3,7 @@ import {
   AutoAcceptCredential,
   AutoAcceptProof,
   ConnectionsModule,
+  MediatorModule,
   CredentialsModule,
   DidsModule,
   Key,
@@ -13,6 +14,8 @@ import {
   V2ProofProtocol,
   WalletError,
   WalletKeyExistsError,
+  KeyDidResolver,
+  KeyDidRegistrar,
 } from "@aries-framework/core";
 import {
   AnonCredsCredentialFormatService,
@@ -93,8 +96,8 @@ export const generateDidFromKey = (key: Key): string => {
 };
 
 //eslint-disable-next-line
-export const getAskarAnonCredsIndyModules = (networks: any) => {
-  return {
+export const getAskarAnonCredsIndyModules = (networks: any, enableMediator: boolean) => {
+  const modules = {
     connections: new ConnectionsModule({
       autoAcceptConnections: true,
     }),
@@ -125,11 +128,20 @@ export const getAskarAnonCredsIndyModules = (networks: any) => {
       networks,
     }),
     dids: new DidsModule({
-      registrars: [new IndyVdrIndyDidRegistrar()],
-      resolvers: [new IndyVdrIndyDidResolver()],
+      registrars: [new IndyVdrIndyDidRegistrar(), new KeyDidRegistrar()],
+      resolvers: [new IndyVdrIndyDidResolver(), new KeyDidResolver()],
     }),
     askar: new AskarModule({
       ariesAskar,
     }),
   } as const;
+
+  if (enableMediator) {
+    const obj = modules as never as { mediator: MediatorModule };
+    obj.mediator = new MediatorModule({
+      autoAcceptMediationRequests: true,
+    });
+  }
+
+  return modules;
 };
diff --git a/libs/askar/src/askar/agent.service.ts b/libs/askar/src/askar/agent.service.ts
index 27fe92a7..581b81ef 100644
--- a/libs/askar/src/askar/agent.service.ts
+++ b/libs/askar/src/askar/agent.service.ts
@@ -33,7 +33,7 @@ export class AgentService {
 
     const i = new CreateInvitationResponseDto();
     i.invitationUrl = outOfBoundRecord.outOfBandInvitation.toUrl({
-      domain: this.askar.agentConfig.agentPeerAddress,
+      domain: this.askar.agentConfig.agentHttpPeerAddress || this.askar.agentConfig.agentWsPeerAddress || '',
     });
 
     return i;
diff --git a/libs/askar/src/askar/askar.service.ts b/libs/askar/src/askar/askar.service.ts
index e07eb52a..4b545005 100644
--- a/libs/askar/src/askar/askar.service.ts
+++ b/libs/askar/src/askar/askar.service.ts
@@ -14,7 +14,11 @@ import {
   TypedArrayEncoder,
   WsOutboundTransport,
 } from "@aries-framework/core";
-import { agentDependencies, HttpInboundTransport } from "@aries-framework/node";
+import {
+  agentDependencies,
+  HttpInboundTransport,
+  WsInboundTransport,
+} from "@aries-framework/node";
 import { ConfigService } from "@nestjs/config";
 import { LedgersService } from "@ocm-engine/ledgers";
 import {
@@ -38,6 +42,12 @@ export class AskarService implements OnModuleInit, OnModuleDestroy {
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     this.agentConfig = configService.get<IConfAgent>("agent")!;
 
+    const endpoints: string[] = [];
+    if (this.agentConfig.agentHttpPeerAddress)
+      endpoints.push(this.agentConfig.agentHttpPeerAddress);
+    if (this.agentConfig.agentWsPeerAddress)
+      endpoints.push(this.agentConfig.agentWsPeerAddress);
+
     const config = {
       label: this.agentConfig.agentName,
       logger: new ConsoleLogger(LogLevel.debug),
@@ -55,7 +65,7 @@ export class AskarService implements OnModuleInit, OnModuleDestroy {
         id: this.agentConfig.agentName,
         key: this.agentConfig.agentKey,
       },
-      endpoints: [this.agentConfig.agentPeerAddress],
+      endpoints,
     } satisfies InitConfig;
 
     this.agent = new Agent({
@@ -63,12 +73,24 @@ export class AskarService implements OnModuleInit, OnModuleDestroy {
       dependencies: agentDependencies,
       modules: getAskarAnonCredsIndyModules(
         this.ledgersSerivce.ledgersConfig(),
+        this.agentConfig.agentRunAsMediator,
       ),
     });
 
-    this.agent.registerInboundTransport(
-      new HttpInboundTransport({ port: this.agentConfig.agentPeerPort }),
-    );
+    if (this.agentConfig.agentWsPeerPort) {
+      this.agent.registerInboundTransport(
+        new WsInboundTransport({
+          port: this.agentConfig.agentWsPeerPort as number,
+        }),
+      );
+    }
+    if (this.agentConfig.agentHttpPeerPort && !this.agentConfig.agentRunAsMediator) {
+      this.agent.registerInboundTransport(
+        new HttpInboundTransport({
+          port: this.agentConfig.agentHttpPeerPort as number,
+        }),
+      );
+    }
 
     this.agent.registerOutboundTransport(new WsOutboundTransport());
     this.agent.registerOutboundTransport(new HttpOutboundTransport());
@@ -84,6 +106,19 @@ export class AskarService implements OnModuleInit, OnModuleDestroy {
       throw new Error("agent not initialized");
     }
 
+    if (this.agentConfig.agentRunAsMediator) {
+      const invitation = await this.agent.oob.createInvitation({
+        label: "Mediator",
+        multiUseInvitation: true,
+        autoAcceptConnection: true,
+      });
+      const url = invitation.outOfBandInvitation.toUrl({
+        domain: this.agentConfig.agentHttpPeerAddress || this.agentConfig.agentWsPeerAddress || 'http://localhost'
+      });
+      this.logger.log("MEDIATOR MODE: agent is running as mediator");
+      this.logger.log(url);
+    }
+
     const dids = await this.agent.dids.getCreatedDids({ method: "indy" });
 
     if (dids.length) {
diff --git a/libs/config/src/config/agent.config.ts b/libs/config/src/config/agent.config.ts
index d71fa86e..7bf9478e 100644
--- a/libs/config/src/config/agent.config.ts
+++ b/libs/config/src/config/agent.config.ts
@@ -2,11 +2,34 @@ import { registerAs } from "@nestjs/config";
 import * as process from "process";
 import { IConfAgent } from "../interfaces/agent.config.interface";
 
+const endpoints = process.env["AGENT_PEER_URL"]!.split(",");
+let agentWsPeerPort: number | null = null;
+let agentWsPeerAddress: string | null = null;
+let agentHttpPeerPort: number | null = null;
+let agentHttpPeerAddress: string | null = null;
+
+const wsEndpoint = endpoints.find(
+  (p) => p.startsWith("ws") || p.startsWith("wss"),
+);
+if (wsEndpoint) {
+  agentWsPeerPort = parseInt(wsEndpoint.split(":")[2]);
+  agentWsPeerAddress = wsEndpoint;
+}
+const httpEndpoint = endpoints.find(
+  (p) => p.startsWith("http") || p.startsWith("https"),
+);
+if (httpEndpoint) {
+  agentHttpPeerPort = parseInt(httpEndpoint.split(":")[2]);
+  agentHttpPeerAddress = httpEndpoint;
+}
+
 export const agentConfig = registerAs(
   "agent",
   (): IConfAgent => ({
-    agentPeerPort: parseInt(process.env["AGENT_PEER_URL"]!.split(":")[2]),
-    agentPeerAddress: process.env["AGENT_PEER_URL"]!,
+    agentWsPeerPort,
+    agentWsPeerAddress,
+    agentHttpPeerPort,
+    agentHttpPeerAddress,
     agentName: process.env["AGENT_NAME"]!,
     agentKey: process.env["AGENT_KEY"]!,
     agentDidSeed: process.env["AGENT_DID_SEED"]!,
@@ -14,6 +37,7 @@ export const agentConfig = registerAs(
     agentDbUser: process.env["AGENT_DB_USER"]!,
     agentDbPass: process.env["AGENT_DB_PASS"]!,
     agentIsRest: process.env["AGENT_IS_REST"] === "true",
+    agentRunAsMediator: process.env["AGENT_RUN_AS_MEDIATOR"] === "true",
     agentConsumerName: process.env["AGENT_CONSUMER_NAME"]!,
     agentConsumerMaxMessagess: parseInt(process.env["AGENT_MAX_MESSAGES"]!),
     agentConsumerRateLimit: parseInt(process.env["AGENT_RETE_LIMIT"]!),
diff --git a/libs/config/src/config/ledgers.config.ts b/libs/config/src/config/ledgers.config.ts
index de79f8a9..8d7c3c67 100644
--- a/libs/config/src/config/ledgers.config.ts
+++ b/libs/config/src/config/ledgers.config.ts
@@ -7,5 +7,7 @@ export const ledgersConfig = registerAs(
   (): ILedgers => ({
     ledgers: process.env["LEDGERS"]!.split(","),
     idUnionApiKey: process.env["IDUNION_KEY"]!,
+    idUnionApiBasicUser: process.env["IDUNION_BASIC_USER"]!,
+    idUnionApiBasicPass: process.env["IDUNION_BASIC_PASS"]!,
   }),
 );
diff --git a/libs/config/src/interfaces/agent.config.interface.ts b/libs/config/src/interfaces/agent.config.interface.ts
index 1f0e7a85..011ecf1f 100644
--- a/libs/config/src/interfaces/agent.config.interface.ts
+++ b/libs/config/src/interfaces/agent.config.interface.ts
@@ -2,12 +2,15 @@ export interface IConfAgent {
   agentDbHost: string;
   agentDbUser: string;
   agentDbPass: string;
-  agentPeerPort: number;
-  agentPeerAddress: string;
+  agentWsPeerPort: number | null;
+  agentWsPeerAddress: string | null;
+  agentHttpPeerPort: number | null;
+  agentHttpPeerAddress: string | null;
   agentName: string;
   agentKey: string;
   agentDidSeed: string;
   agentIsRest: boolean;
+  agentRunAsMediator: boolean;
   agentConsumerName: string;
   agentConsumerMaxMessagess: number;
   agentConsumerRateLimit: number;
diff --git a/libs/config/src/interfaces/ledgers.config.interface.ts b/libs/config/src/interfaces/ledgers.config.interface.ts
index a49c9850..c7af9b9a 100644
--- a/libs/config/src/interfaces/ledgers.config.interface.ts
+++ b/libs/config/src/interfaces/ledgers.config.interface.ts
@@ -1,4 +1,6 @@
 export interface ILedgers {
   ledgers: Array<string>;
   idUnionApiKey: string;
+  idUnionApiBasicUser: string;
+  idUnionApiBasicPass: string;
 }
diff --git a/libs/config/src/schemas/agent.schema.ts b/libs/config/src/schemas/agent.schema.ts
index 9cf56915..ef0a0f93 100644
--- a/libs/config/src/schemas/agent.schema.ts
+++ b/libs/config/src/schemas/agent.schema.ts
@@ -5,6 +5,7 @@ export const agentSchema = Joi.object({
   AGENT_NAME: Joi.string().required(),
   AGENT_KEY: Joi.string().required(),
   AGENT_DID_SEED: Joi.string().required(),
+  AGENT_RUN_AS_MEDIATOR: Joi.string(),
   AGENT_DB_HOST: Joi.string().required(),
   AGENT_DB_USER: Joi.string().required(),
   AGENT_DB_PASS: Joi.string().required(),
diff --git a/libs/config/src/schemas/ledgers.schema.ts b/libs/config/src/schemas/ledgers.schema.ts
index d305f728..67a2cf3f 100644
--- a/libs/config/src/schemas/ledgers.schema.ts
+++ b/libs/config/src/schemas/ledgers.schema.ts
@@ -3,4 +3,6 @@ import Joi from "joi";
 export const ledgersSchema = Joi.object({
   LEDGERS: Joi.string().required(),
   IDUNION_KEY: Joi.string(),
+  IDUNION_BASIC_USER: Joi.string(),
+  IDUNION_BASIC_PASS: Joi.string(),
 });
diff --git a/libs/ledgers/src/idunion/idunion.provider.ts b/libs/ledgers/src/idunion/idunion.provider.ts
index 5c6dcf46..0a5fa67c 100644
--- a/libs/ledgers/src/idunion/idunion.provider.ts
+++ b/libs/ledgers/src/idunion/idunion.provider.ts
@@ -30,11 +30,20 @@ export class IdunionProvider implements IRegistrator {
 
     this.logger.log(`Trying to register ${did} to idunion`);
     try {
-      await axios.post(`${URL}?apiKey=${this.config.idUnionApiKey}`, {
-        role: "ENDORSER",
-        did: unqualifiedIndyDid,
-        verkey,
-      });
+      await axios.post(
+        `${URL}?apiKey=${this.config.idUnionApiKey}`,
+        {
+          role: "ENDORSER",
+          did: unqualifiedIndyDid,
+          verkey,
+        },
+        {
+          auth: {
+            username: this.config.idUnionApiBasicUser,
+            password: this.config.idUnionApiBasicPass,
+          },
+        },
+      );
 
       this.logger.log("Registration successful");
       return did;
-- 
GitLab