diff --git a/.env.example b/.env.example index 5434e09a6c663550d900224de4d37d61fa66a33f..c3cd2caa3094daa00407959cfc222e7e39e601c5 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,7 @@ LEDGERS="BCOVRIN_TEST" IDUNION_KEY=#add if you are using IDUNION as a ledger +IDUNION_BASIC_USER=#add if you are using IDUNION as a ledger +IDUNION_BASIC_PASS=#add if you are using IDUNION as a ledger AGENT_PEER_URL="http://localhost:8001" AGENT_NAME=EXAMPTTLE_AGENT_45 diff --git a/CHANGELOG.md b/CHANGELOG.md index d7649651c9c71e7702074b41361e0f740060cd4c..e3a15b48b38ffa0e006f839098acdf1144c9a9ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.8.0](https://code.vereign.com/gaiax/ocm/ocm-engine/compare/v1.7.1...v1.8.0) (2023-09-12) + + +### Features + +* rest agent support for connection and proof change state events ([de04959](https://code.vereign.com/gaiax/ocm/ocm-engine/commit/de04959d975f398fd5ab4ce628fba57a440c9d0b)) + ## [1.7.1](https://code.vereign.com/gaiax/ocm/ocm-engine/compare/v1.7.0...v1.7.1) (2023-08-21) diff --git a/apps/gateway/src/app/managers/attestation.controller.ts b/apps/gateway/src/app/managers/attestation.controller.ts index 88140a189570219c7d9f3fa5af5614d19f701ee5..536fc82cb7564ea2e17f888c4dcf3bd3ddc9d6b8 100644 --- a/apps/gateway/src/app/managers/attestation.controller.ts +++ b/apps/gateway/src/app/managers/attestation.controller.ts @@ -1,12 +1,4 @@ -import { - BadRequestException, - Body, - Controller, - Get, - Param, - Post, - UseFilters, -} from "@nestjs/common"; +import { Body, Controller, Get, Param, Post, UseFilters } from "@nestjs/common"; import { AllExceptionsHandler } from "../exception.handler"; import { AttestationManagerClient } from "@ocm-engine/clients"; import { diff --git a/apps/gateway/src/app/managers/proof.controller.ts b/apps/gateway/src/app/managers/proof.controller.ts index f3df78b61f836c0c63964f9acbefff6c23c86a83..210181d9706c8ac7b900ab42da2eb357dfc52944 100644 --- a/apps/gateway/src/app/managers/proof.controller.ts +++ b/apps/gateway/src/app/managers/proof.controller.ts @@ -1,12 +1,4 @@ -import { - BadRequestException, - Body, - Controller, - Get, - Param, - Post, - UseFilters, -} from "@nestjs/common"; +import { Body, Controller, Get, Param, Post, UseFilters } from "@nestjs/common"; import { AcceptProofRequestDto, DeclineProofRequestDto, @@ -80,7 +72,6 @@ export class ProofController { }); } - @Get("/credentials/proof/:proof_record_id") @ApiResponse({ status: 200, @@ -204,7 +195,7 @@ export class ProofController { }); } - @Post(`/credentials/proof/:proof_record_id/accept`) + @Post(`/credentials/proof/accept`) @ApiResponse({ status: 201, description: @@ -239,15 +230,12 @@ export class ProofController { "Method accept credential proof. The id of the response will be matched when you receive event from the websocket", tags: ["Credentials Proof"], }) - acceptProof(@Param("proof_record_id") proofRecordId: string) { - const data = new AcceptProofRequestDto(); - data.proofRecordId = proofRecordId; - + acceptProof(@Body() acceptProofRequestDto: AcceptProofRequestDto) { return this.pmClient.sendPayload<AcceptProofRequestDto>({ pattern: "proofs", payload: { - source: "/credentials/proofs/:id/accept", - data, + source: "/credentials/proofs/accept", + data: acceptProofRequestDto, type: PROOF_ACCEPT, }, }); diff --git a/apps/proof-manager/src/app/app.controller.ts b/apps/proof-manager/src/app/app.controller.ts index b7e41ea0dda88da0b3225d4bdacb1ba6b9af0f2c..8f80bb48ed6abd241e156069437b38519f8e79a9 100644 --- a/apps/proof-manager/src/app/app.controller.ts +++ b/apps/proof-manager/src/app/app.controller.ts @@ -3,6 +3,7 @@ import { Body, Controller, Logger } from "@nestjs/common"; import { ProducerService } from "@ocm-engine/nats"; import { MessagePattern, RpcException } from "@nestjs/microservices"; import { + AcceptProofRequestDto, CloudEventDto, GatewayAcceptedResponseDto, makeEvent, @@ -19,7 +20,7 @@ export class AppController { async create( @Body() payload: { - data: null; + data: null | AcceptProofRequestDto; type: ProofEvent; source: string; }, diff --git a/compose/docker-compose.simple.yml b/compose/docker-compose.simple.yml index 30d614e481ab7e208ea86f184cf39f4fe029c862..5508f648a5d3d4fa4f0c5e372b7387fd57e73fae 100644 --- a/compose/docker-compose.simple.yml +++ b/compose/docker-compose.simple.yml @@ -1,6 +1,30 @@ version: '3.8' services: + + builder: + privileged: true + image: node:18.16.0-buster-slim + volumes: + - ./../yarn.lock:/app/yarn.lock + - ./../package.json:/app/package.json + - ./../apps:/app/apps + - ./../libs:/app/libs + - ./../nx.json:/app/nx.json + - ./../tsconfig.base.json:/app/tsconfig.base.json + - ./data/node_modules:/app/node_modules + - ./data/dist:/app/dist + working_dir: /app + command: + - sh + - -c + - | + whoami + apt update -y && apt install python3 git make build-essential -y + yarn install + yarn build:agent + exit 0 + pg_db: image: 'postgres:latest' ports: @@ -12,10 +36,13 @@ services: - ./data/db-simple/:/var/lib/postgresql/data/ agent-issuer-simple: + image: node:18.16.0 + volumes: + - ./data/node_modules:/app/node_modules + - ./data/dist/apps/agent:/app/ + working_dir: /app + command: node main.js container_name: agent-issuer-simple - build: - context: "../" - dockerfile: "./apps/agent/deployment/local/Dockerfile" env_file: - ./env/issuer.simple.env ports: @@ -24,12 +51,17 @@ services: depends_on: pg_db: condition: service_started + builder: + condition: service_completed_successfully agent-holder-simple: container_name: agent-holder-simple - build: - context: "../" - dockerfile: "./apps/agent/deployment/local/Dockerfile" + image: node:18.16.0 + volumes: + - ./data/node_modules:/app/node_modules + - ./data/dist/apps/agent:/app/ + working_dir: /app + command: node main.js env_file: - ./env/holder.simple.env ports: @@ -38,3 +70,6 @@ services: depends_on: pg_db: condition: service_started + builder: + condition: service_completed_successfully + diff --git a/compose/env/holder.simple.env b/compose/env/holder.simple.env index 8928d0564da69e5b9af00c27a7c8cd4e6f2f1086..6fb198c52bab9de6d815468f737c1e3c5abfdc5d 100644 --- a/compose/env/holder.simple.env +++ b/compose/env/holder.simple.env @@ -1,14 +1,18 @@ LEDGERS="BCOVRIN_TEST" + IDUNION_KEY= +IDUNION_BASIC_USER= +IDUNION_BASIC_PASS= -AGENT_PEER_URL="http://agent-holder:6001" -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_PEER_URL=http://agent-holder-simple:6001 +AGENT_PEER_PORT=6001 +AGENT_NAME=DEV_SIMPLE_AGENT_HOLDER_OCM_3919 # this should be changed to company name +AGENT_KEY=DEV_SIMPLE_AGENT_HOLDER_OCM_3818 #example random string +AGENT_DID_SEED=jhPctchKaUfyZ1ioz6Fjypoasdhjahjsiscdkd23ozxflxkaKD0RR4TICwx AGENT_DB_HOST=pg_db:5432 AGENT_DB_USER=postgres AGENT_DB_PASS=postgres -AGENT_PORT=8081 +AGENT_PORT=8080 AGENT_CONSUMER_NAME=agent_1 AGENT_IS_REST=true AGENT_MAX_MESSAGES=10 diff --git a/compose/env/issuer.simple.env b/compose/env/issuer.simple.env index 465996055c663cc68da01f3dd997f54f61404aa9..86e1d2bfc69f5b3d6a3309f5adb9f7278ecb31ac 100644 --- a/compose/env/issuer.simple.env +++ b/compose/env/issuer.simple.env @@ -1,14 +1,18 @@ LEDGERS="BCOVRIN_TEST" -IDUNION_KEY= -AGENT_PEER_URL="http://agent-issuer:8001" -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 +IDUNION_KEY=6eb8859f-dd7f-445a-bc37-ba986145d3b6 +IDUNION_BASIC_USER=idunion4ssi +IDUNION_BASIC_PASS=7eDZ+LrUbV5bCRwe6Ki0Gw== + +AGENT_PEER_URL=http://agent-issuer-simple:8001 +AGENT_PEER_PORT=8001 +AGENT_NAME=DEV_SIMPLE_AGENT_ISSUER_OCM_45 # this should be changed to company name +AGENT_KEY=DEV_SIMPLE_AGENT_ISSUER_OCM_45 #example random string +AGENT_DID_SEED=Cd0VanW68R3HCaskjdakjsNpiuadscmsBBPP3DV6pMdwDFdHvPasdasdas AGENT_DB_HOST=pg_db:5432 AGENT_DB_USER=postgres AGENT_DB_PASS=postgres -AGENT_PORT=8081 +AGENT_PORT=8080 AGENT_CONSUMER_NAME=agent_1 AGENT_IS_REST=true AGENT_MAX_MESSAGES=10 diff --git a/gateway-swagger.json b/gateway-swagger.json index 0da4211542ced768ce3d3628b3b896e7011415af..0bf822f3adb58256d8fdca9f0b1d85a79f33c4a6 100644 --- a/gateway-swagger.json +++ b/gateway-swagger.json @@ -1 +1 @@ -{"openapi":"3.0.0","paths":{"/api/v1/invitations":{"post":{"operationId":"ConnectionController_createInvitation","summary":"Create invitation for connection","description":"Method will create invitation url. The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[],"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:4312"}}}}}}}}},"/api/v1/invitations/accept":{"post":{"operationId":"ConnectionController_acceptInvitation","summary":"Accept invitation for connection","description":"Method will accept the invitation and will return connection thought the websocket. The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvitationResponseDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["invitationUrl must be a string","invitationUrl should not be empty"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:4312"}}}}}}}}},"/api/v1/connections":{"get":{"operationId":"ConnectionController_list","summary":"List all connections","description":"The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:4312"}}}}}}}}},"/api/v1/connections/{id}":{"get":{"operationId":"ConnectionController_getById","summary":"Get connection by id","description":"The method will search for connection id, if not found null will be returned. The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:4312"}}}}}}}}},"/api/v1/schemas":{"post":{"operationId":"AttestationController_createSchema","summary":"Create schema","description":"Method will create schema. The id of the response will be matched when you receive event from the websocket","tags":["Schema"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSchemaRequestDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["name must be a string","name should not be empty"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}},"get":{"operationId":"AttestationController_listSchema","summary":"List all schemas","description":"Method will fetch all schemas. The id of the response will be matched when you receive event from the websocket","tags":["Schema"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/schemas-by-id":{"post":{"operationId":"AttestationController_getSchemaById","summary":"Get schema by id","description":"Method will fetch specific schema or return null. The id of the response will be matched when you receive event from the websocket","tags":["Schema"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSchemaRequestDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["schemaId must be a string","schemaId should not be empty"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/credentials/definition":{"post":{"operationId":"AttestationController_createCredentialDefinition","summary":"Create credential definition","description":"Method create credential definition. The id of the response will be matched when you receive event from the websocket","tags":["Credentials"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCredentialDefinitionRequsetDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["schemaId must be a string","schemaId should not be empty","tag must be a string","tag should not be empty"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/credentials/issue":{"post":{"operationId":"AttestationController_issueCredential","summary":"Issue credential","description":"Method issue credential, it will create an offer and send it to specified receiver (connectionId). The id of the response will be matched when you receive event from the websocket","tags":["Credentials"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueCredentialRequestDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["connectionId must be a string","connectionId should not be empty","credentialDefinitionId must be a string","credentialDefinitionId should not be empty"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/credentials":{"get":{"operationId":"AttestationController_credentials","summary":"List all credential","description":"Method list credential definition no filters applied. The id of the response will be matched when you receive event from the websocket","tags":["Credentials"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/credentials/offers":{"get":{"operationId":"AttestationController_getCredentialOffers","summary":"List unaccepted credential offers","description":"Method list offers that are received, but not accepted. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Offers"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/credentials/offers/{credential_record_id}/accept":{"post":{"operationId":"AttestationController_acceptCredential","summary":"Accept credential offers","description":"Method list accept credential offer. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Offers"],"parameters":[{"name":"credential_record_id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"201":{"description":""},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/messages":{"post":{"operationId":"AttestationController_sendMeesage","summary":"Send basic message","description":"Method will send basic message to a connection. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Offers"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MakeBasicMessageRequestDto"}}}},"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"201":{"description":""},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["connectionId must be a string","connectionId should not be empty","message must be a string","message should not be empty"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/credentials/proof":{"get":{"operationId":"ProofController_proofs","summary":"List received unaccepted proofs","description":"Method list all received unaccepted proofs. Status - request-receive. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Proof"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to proof manager. This error shows that proof manager could not convert request to event or proof manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1919"}}}}}}}}},"/api/v1/credentials/proof/issue":{"post":{"operationId":"ProofController_issueProof","summary":"Issue proof for credential","description":"Method will issue proof. If connection id is not passed, the proof will be OOB. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Proof"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueProofRequestDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["attributes must contain at least 1 elements","attributes must be an array"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to proof manager. This error shows that proof manager could not convert request to event or proof manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1919"}}}}}}}}},"/api/v1/credentials/proof/{proof_record_id}/accept":{"post":{"operationId":"ProofController_acceptProof","summary":"Accept credential proof","description":"Method accept credential proof. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Proof"],"parameters":[{"name":"proof_record_id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to proof manager. This error shows that proof manager could not convert request to event or proof manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1919"}}}}}}}}}},"info":{"title":"OCM Gateway","description":"OCM ENGINE GATEWAY API","version":"1.0","contact":{}},"tags":[],"servers":[{"url":"http://0.0.0.0:8081"}],"components":{"schemas":{"CloudEventDto":{"type":"object","properties":{}},"GatewayAcceptedResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"80633e6d-c606-4539-a3df-287fedd09253"}},"required":["id"]},"CreateInvitationResponseDto":{"type":"object","properties":{"invitationUrl":{"type":"string","description":"A list of user's roles","example":"http://0.0.0.0:8001?oob=eyJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvb3V0LW9mLWJhbmQvMS4xL2ludml0YXRpb24iLCJAaWQiOiIzYWExNGIzNC04YTk5LTQxY2UtYTY3NC1jODUxYmVhMTIxMWEiLCJsYWJlbCI6IkRFeGNWYXNkX0FHRU5UXzQ1IiwiYWNjZXB0IjpbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwiaGFuZHNoYWtlX3Byb3RvY29scyI6WyJodHRwczovL2RpZGNvbW0ub3JnL2RpZGV4Y2hhbmdlLzEuMCIsImh0dHBzOi8vZGlkY29tbS5vcmcvY29ubmVjdGlvbnMvMS4wIl0sInNlcnZpY2VzIjpbeyJpZCI6IiNpbmxpbmUtMCIsInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8wLjAuMC4wOjgwMDEiLCJ0eXBlIjoiZGlkLWNvbW11bmljYXRpb24iLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VFcHllc1pNa3k0a1BpQzhEOEplZERlcm55YTFuaTREMUF3ZmdnWWt6YmR4Il0sInJvdXRpbmdLZXlzIjpbXX1dfQ"}},"required":["invitationUrl"]},"CreateSchemaRequestDto":{"type":"object","properties":{"name":{"type":"string","example":"my test schema"},"attributes":{"example":["first_name, last_name"],"type":"array","items":{"type":"string"}},"version":{"type":"string","example":"1.0.2","pattern":"/^(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)$/"}},"required":["name","attributes","version"]},"GetSchemaRequestDto":{"type":"object","properties":{"schemaId":{"type":"string","example":"did:indy:LEDNGER:SXM76gQwRnjkgoz2oBnGjd/anoncreds/v0/SCHEMA/test schema/1.0.2"}},"required":["schemaId"]},"CreateCredentialDefinitionRequsetDto":{"type":"object","properties":{"schemaId":{"type":"string"},"tag":{"type":"string"}},"required":["schemaId","tag"]},"IssueCredentialAttributes":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"required":["name","value"]},"IssueCredentialRequestDto":{"type":"object","properties":{"connectionId":{"type":"string","example":"6464b521-005a-4379-91e0-a3692b31cafd"},"credentialDefinitionId":{"type":"string"},"attributes":{"type":"array","items":{"$ref":"#/components/schemas/IssueCredentialAttributes"}}},"required":["connectionId","credentialDefinitionId","attributes"]},"MakeBasicMessageRequestDto":{"type":"object","properties":{"connectionId":{"type":"string","example":"6464b521-005a-4379-91e0-a3692b31cafd"},"message":{"type":"string","example":"hello world"}},"required":["connectionId","message"]},"IssueProofAttribute":{"type":"object","properties":{"attributeName":{"type":"string"},"credentialDefinitionId":{"type":"string"},"schemaId":{"type":"string"}},"required":["attributeName","credentialDefinitionId","schemaId"]},"IssueProofRequestDto":{"type":"object","properties":{"connectionId":{"type":"string","example":"6464b521-005a-4379-91e0-a3692b31cafd"},"attributes":{"type":"array","items":{"$ref":"#/components/schemas/IssueProofAttribute"}}},"required":["attributes"]}}}} \ No newline at end of file +{"openapi":"3.0.0","paths":{"/api/v1/invitations":{"post":{"operationId":"ConnectionController_createInvitation","summary":"Create invitation for connection","description":"Method will create invitation url. The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[],"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:4312"}}}}}}}}},"/api/v1/invitations/accept":{"post":{"operationId":"ConnectionController_acceptInvitation","summary":"Accept invitation for connection","description":"Method will accept the invitation and will return connection thought the websocket. The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvitationResponseDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["invitationUrl must be a string","invitationUrl should not be empty"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:4312"}}}}}}}}},"/api/v1/connections":{"get":{"operationId":"ConnectionController_list","summary":"List all connections","description":"The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:4312"}}}}}}}}},"/api/v1/connections/{id}":{"get":{"operationId":"ConnectionController_getById","summary":"Get connection by id","description":"The method will search for connection id, if not found null will be returned. The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:4312"}}}}}}}}},"/api/v1/schemas":{"post":{"operationId":"AttestationController_createSchema","summary":"Create schema","description":"Method will create schema. The id of the response will be matched when you receive event from the websocket","tags":["Schema"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSchemaRequestDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["name must be a string","name should not be empty"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}},"get":{"operationId":"AttestationController_listSchema","summary":"List all schemas","description":"Method will fetch all schemas. The id of the response will be matched when you receive event from the websocket","tags":["Schema"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/schemas-by-id":{"post":{"operationId":"AttestationController_getSchemaById","summary":"Get schema by id","description":"Method will fetch specific schema or return null. The id of the response will be matched when you receive event from the websocket","tags":["Schema"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSchemaRequestDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["schemaId must be a string","schemaId should not be empty"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/credentials/definition":{"post":{"operationId":"AttestationController_createCredentialDefinition","summary":"Create credential definition","description":"Method create credential definition. The id of the response will be matched when you receive event from the websocket","tags":["Credentials"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCredentialDefinitionRequsetDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["schemaId must be a string","schemaId should not be empty","tag must be a string","tag should not be empty"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/credentials/issue":{"post":{"operationId":"AttestationController_issueCredential","summary":"Issue credential","description":"Method issue credential, it will create an offer and send it to specified receiver (connectionId). The id of the response will be matched when you receive event from the websocket","tags":["Credentials"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueCredentialRequestDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["connectionId must be a string","connectionId should not be empty","credentialDefinitionId must be a string","credentialDefinitionId should not be empty"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/credentials":{"get":{"operationId":"AttestationController_credentials","summary":"List all credential","description":"Method list credential definition no filters applied. The id of the response will be matched when you receive event from the websocket","tags":["Credentials"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/credentials/offers":{"get":{"operationId":"AttestationController_getCredentialOffers","summary":"List unaccepted credential offers","description":"Method list offers that are received, but not accepted. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Offers"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/credentials/offers/{credential_record_id}/accept":{"post":{"operationId":"AttestationController_acceptCredential","summary":"Accept credential offers","description":"Method list accept credential offer. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Offers"],"parameters":[{"name":"credential_record_id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"201":{"description":""},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/messages":{"post":{"operationId":"AttestationController_sendMeesage","summary":"Send basic message","description":"Method will send basic message to a connection. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Offers"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MakeBasicMessageRequestDto"}}}},"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"201":{"description":""},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["connectionId must be a string","connectionId should not be empty","message must be a string","message should not be empty"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1234"}}}}}}}}},"/api/v1/credentials/proof":{"get":{"operationId":"ProofController_proofs","summary":"List received unaccepted proofs","description":"Method list all received unaccepted proofs. Status - request-receive. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Proof"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to proof manager. This error shows that proof manager could not convert request to event or proof manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1919"}}}}}}}}},"/api/v1/credentials/proof/{proof_record_id}":{"get":{"operationId":"ProofController_getProofById","summary":"Get a single proof record by providing proof record id.","description":"Method get proof by id. Status - request-receive. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Proof Get Id"],"parameters":[{"name":"proof_record_id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to proof manager. This error shows that proof manager could not convert request to event or proof manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1919"}}}}}}}}},"/api/v1/credentials/proof/issue":{"post":{"operationId":"ProofController_issueProof","summary":"Issue proof for credential","description":"Method will issue proof. If connection id is not passed, the proof will be OOB. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Proof"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueProofRequestDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":400},"message":{"type":"array","example":["attributes must contain at least 1 elements","attributes must be an array"]},"error":{"type":"string","example":"Bad Request"}}}}}},"500":{"description":"Error in sending data to proof manager. This error shows that proof manager could not convert request to event or proof manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1919"}}}}}}}}},"/api/v1/credentials/proof/accept":{"post":{"operationId":"ProofController_acceptProof","summary":"Accept credential proof","description":"Method accept credential proof. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Proof"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptProofRequestDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"500":{"description":"Error in sending data to proof manager. This error shows that proof manager could not convert request to event or proof manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1919"}}}}}}}}},"/api/v1/credentials/proof/{proof_record_id}/decline":{"post":{"operationId":"ProofController_declineProofRequest","summary":"Decline a proof request.","description":"Method to decline a proof request by id. Status - request-receive. The id of the response will be matched when you receive event from the websocket","tags":["Credentials Proof Decline Request Id"],"parameters":[{"name":"proof_record_id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"201":{"description":""},"500":{"description":"Error in sending data to proof manager. This error shows that proof manager could not convert request to event or proof manager could not send the event to the broker.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","example":500},"message":{"type":"string","example":"connect ECONNREFUSED 0.0.0.0.0:1919"}}}}}}}}}},"info":{"title":"OCM Gateway","description":"OCM ENGINE GATEWAY API","version":"1.0","contact":{}},"tags":[],"servers":[{"url":"http://undefined:NaN"}],"components":{"schemas":{"CloudEventDto":{"type":"object","properties":{}},"GatewayAcceptedResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"80633e6d-c606-4539-a3df-287fedd09253"}},"required":["id"]},"CreateInvitationResponseDto":{"type":"object","properties":{"invitationUrl":{"type":"string","description":"A list of user's roles","example":"http://0.0.0.0:8001?oob=eyJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvb3V0LW9mLWJhbmQvMS4xL2ludml0YXRpb24iLCJAaWQiOiIzYWExNGIzNC04YTk5LTQxY2UtYTY3NC1jODUxYmVhMTIxMWEiLCJsYWJlbCI6IkRFeGNWYXNkX0FHRU5UXzQ1IiwiYWNjZXB0IjpbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwiaGFuZHNoYWtlX3Byb3RvY29scyI6WyJodHRwczovL2RpZGNvbW0ub3JnL2RpZGV4Y2hhbmdlLzEuMCIsImh0dHBzOi8vZGlkY29tbS5vcmcvY29ubmVjdGlvbnMvMS4wIl0sInNlcnZpY2VzIjpbeyJpZCI6IiNpbmxpbmUtMCIsInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8wLjAuMC4wOjgwMDEiLCJ0eXBlIjoiZGlkLWNvbW11bmljYXRpb24iLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VFcHllc1pNa3k0a1BpQzhEOEplZERlcm55YTFuaTREMUF3ZmdnWWt6YmR4Il0sInJvdXRpbmdLZXlzIjpbXX1dfQ"}},"required":["invitationUrl"]},"CreateSchemaRequestDto":{"type":"object","properties":{"name":{"type":"string","example":"my test schema"},"attributes":{"example":["first_name, last_name"],"type":"array","items":{"type":"string"}},"version":{"type":"string","example":"1.0.2","pattern":"/^(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)$/"}},"required":["name","attributes","version"]},"GetSchemaRequestDto":{"type":"object","properties":{"schemaId":{"type":"string","example":"did:indy:LEDNGER:SXM76gQwRnjkgoz2oBnGjd/anoncreds/v0/SCHEMA/test schema/1.0.2"}},"required":["schemaId"]},"CreateCredentialDefinitionRequsetDto":{"type":"object","properties":{"schemaId":{"type":"string"},"tag":{"type":"string"}},"required":["schemaId","tag"]},"IssueCredentialAttributes":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"required":["name","value"]},"IssueCredentialRequestDto":{"type":"object","properties":{"connectionId":{"type":"string","example":"6464b521-005a-4379-91e0-a3692b31cafd"},"credentialDefinitionId":{"type":"string"},"attributes":{"type":"array","items":{"$ref":"#/components/schemas/IssueCredentialAttributes"}}},"required":["connectionId","credentialDefinitionId","attributes"]},"MakeBasicMessageRequestDto":{"type":"object","properties":{"connectionId":{"type":"string","example":"6464b521-005a-4379-91e0-a3692b31cafd"},"message":{"type":"string","example":"hello world"}},"required":["connectionId","message"]},"IssueProofAttribute":{"type":"object","properties":{"attributeName":{"type":"string"},"credentialDefinitionId":{"type":"string"},"schemaId":{"type":"string"}},"required":["attributeName","credentialDefinitionId","schemaId"]},"IssueProofRequestDto":{"type":"object","properties":{"connectionId":{"type":"string","example":"6464b521-005a-4379-91e0-a3692b31cafd"},"attributes":{"type":"array","items":{"$ref":"#/components/schemas/IssueProofAttribute"}}},"required":["attributes"]},"AcceptProofRequestDto":{"type":"object","properties":{"proofRecordId":{"type":"string"},"proofUrl":{"type":"string"}},"required":["proofRecordId","proofUrl"]}}}} \ No newline at end of file diff --git a/libs/askar/src/agent.utils.ts b/libs/askar/src/agent.utils.ts index 3eae5e959a039310952d4bdaadd5e631d6e54827..9bca784c0838eac47a45b12ac1ca83e3b0f23673 100644 --- a/libs/askar/src/agent.utils.ts +++ b/libs/askar/src/agent.utils.ts @@ -2,12 +2,21 @@ import { Agent, AutoAcceptCredential, AutoAcceptProof, + BaseEvent, ConnectionsModule, + ConnectionStateChangedEvent, CredentialsModule, + DidExchangeRole, DidsModule, + EncryptedMessage, Key, + KeyDidResolver, KeyType, + PeerDidResolver, + ProofEventTypes, ProofsModule, + ProofState, + ProofStateChangedEvent, TypedArrayEncoder, V2CredentialProtocol, V2ProofProtocol, @@ -17,6 +26,7 @@ import { import { AnonCredsCredentialFormatService, AnonCredsModule, + AnonCredsProof, AnonCredsProofFormatService, } from "@aries-framework/anoncreds"; import { @@ -31,6 +41,27 @@ import { indyVdr } from "@hyperledger/indy-vdr-nodejs"; import { AskarModule } from "@aries-framework/askar"; import { ariesAskar } from "@hyperledger/aries-askar-nodejs"; import { Key as C, KeyAlgs } from "@hyperledger/aries-askar-shared"; +import { IConfAgent } from "@ocm-engine/config"; +import axios from "axios"; +import { + catchError, + filter, + lastValueFrom, + map, + Observable, + ReplaySubject, + Subject, + take, + timeout, +} from "rxjs"; +import { SubjectInboundTransport } from "./askar/transports/agent.subject.inbound.transport"; +import { SubjectOutboundTransport } from "./askar/transports/agent.subject.outbound.transport"; + +export type EventReplaySubject = ReplaySubject<BaseEvent>; +export type SubjectMessage = { + message: EncryptedMessage; + replySubject?: Subject<SubjectMessage>; +}; export const importDidsToWallet = async ( agent: Agent, @@ -126,10 +157,231 @@ export const getAskarAnonCredsIndyModules = (networks: any) => { }), dids: new DidsModule({ registrars: [new IndyVdrIndyDidRegistrar()], - resolvers: [new IndyVdrIndyDidResolver()], + resolvers: [ + new IndyVdrIndyDidResolver(), + new KeyDidResolver(), + new PeerDidResolver(), + ], }), askar: new AskarModule({ ariesAskar, }), } as const; }; + +export const setupEventReplaySubjects = ( + agents: Agent[], + eventTypes: string[], +): ReplaySubject<BaseEvent>[] => { + const replaySubjects: EventReplaySubject[] = []; + + for (const agent of agents) { + const replaySubject = new ReplaySubject<BaseEvent>(); + + for (const eventType of eventTypes) { + agent.events.observable(eventType).subscribe(replaySubject); + } + + replaySubjects.push(replaySubject); + } + + return replaySubjects; +}; + +export const setupSubjectTransports = (agents: Agent[]) => { + const subjectMap: Record<string, Subject<SubjectMessage>> = {}; + + for (const agent of agents) { + const messages = new Subject<SubjectMessage>(); + subjectMap[agent.config.endpoints[0]] = messages; + agent.registerInboundTransport(new SubjectInboundTransport(messages)); + agent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)); + } +}; + +export const svdxProofStateChangeHandler = async ( + ev: ProofStateChangedEvent, + agent: Agent, + config?: IConfAgent, +) => { + if (ProofState.Done !== ev.payload.proofRecord.state) { + return; + } + + const presentationMessage = await agent.proofs.findPresentationMessage( + ev.payload.proofRecord.id, + ); + + console.log(JSON.stringify(presentationMessage, null, 2)); + if (!presentationMessage) { + console.log("No presentation message found"); + return; + } + + const attachmentId = presentationMessage.formats[0].attachmentId; + + const attachment = + presentationMessage.getPresentationAttachmentById(attachmentId); + + console.log(JSON.stringify(attachment, null, 2)); + if (!attachment) { + console.log("No attachment found"); + return; + } + + const email = + attachment.getDataAsJson<AnonCredsProof>()?.requested_proof.revealed_attrs[ + "email" + ].raw; + + if (!config?.agentSVDXWebHook) { + console.log("Agent SVDX web hook not set"); + return; + } + + try { + await axios.post( + config?.agentSVDXWebHook, + { + email, + connectionId: ev.payload.proofRecord.connectionId, + }, + { + auth: { + username: config?.agentSVDXBasicUser, + password: config?.agentSVDXBasicPass, + }, + }, + ); + } catch (e) { + console.log(JSON.stringify(e, null, 2)); + } +}; + +export const svdxConnectionStateChangeHandler = async ( + ev: ConnectionStateChangedEvent, + agent: Agent, + config?: IConfAgent, +) => { + if ( + ev.payload.connectionRecord.role === DidExchangeRole.Responder && + ev.payload.connectionRecord.state !== "completed" + ) { + return; + } + + await agent.connections.addConnectionType( + ev.payload.connectionRecord.id, + ev.payload.connectionRecord.theirLabel || "svdx", + ); + + console.log("connection accepted", JSON.stringify(ev, null, 2)); + + const connections = await agent.connections.findAllByConnectionTypes([ + ev.payload.connectionRecord.theirLabel || "svdx", + ]); + + if (connections.length < 2) { + return; + } + + connections.sort( + (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(), + ); + + while (connections.length > 1) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const con = connections.pop()!; + + console.log(`deleting ${con.id}`); + await agent.connections.deleteById(con.id); + } + + try { + await agent.proofs.requestProof({ + protocolVersion: "v2", + connectionId: connections[0].id, + proofFormats: { + anoncreds: { + name: "proof-request", + version: "1.0", + requested_attributes: { + email: { + name: "email", + restrictions: [ + { + schema_id: config?.agentSVDXSchemaId, + cred_def_id: config?.agentSVDXCredDefId, + }, + ], + }, + }, + }, + }, + }); + } catch (e) { + console.log(JSON.stringify(e, null, 2)); + console.log("failed to issue credential"); + } +}; + +export const isProofStateChangedEvent = ( + e: BaseEvent, +): e is ProofStateChangedEvent => e.type === ProofEventTypes.ProofStateChanged; + +export const waitForProofExchangeRecordSubject = ( + subject: ReplaySubject<BaseEvent> | Observable<BaseEvent>, + { + threadId, + parentThreadId, + state, + previousState, + timeoutMs = 10000, + count = 1, + }: { + threadId?: string; + parentThreadId?: string; + state?: ProofState; + previousState?: ProofState | null; + timeoutMs?: number; + count?: number; + }, +) => { + const observable: Observable<BaseEvent> = + subject instanceof ReplaySubject ? subject.asObservable() : subject; + return lastValueFrom( + observable.pipe( + filter(isProofStateChangedEvent), + filter( + (e) => + previousState === undefined || + e.payload.previousState === previousState, + ), + filter( + (e) => + threadId === undefined || e.payload.proofRecord.threadId === threadId, + ), + filter( + (e) => + parentThreadId === undefined || + e.payload.proofRecord.parentThreadId === parentThreadId, + ), + filter( + (e) => state === undefined || e.payload.proofRecord.state === state, + ), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `ProofStateChangedEvent event not emitted within specified timeout: ${timeoutMs} + previousState: ${previousState}, + threadId: ${threadId}, + parentThreadId: ${parentThreadId}, + state: ${state} + }`, + ); + }), + take(count), + map((e) => e.payload.proofRecord), + ), + ); +}; diff --git a/libs/askar/src/askar-nats/event.handler.service.ts b/libs/askar/src/askar-nats/event.handler.service.ts index b384d7660f5980c8dd8e137135a1cb05896f6cb5..2738c57c4f2d7a32c01c7a6c3f2e5f9ffcfe2c46 100644 --- a/libs/askar/src/askar-nats/event.handler.service.ts +++ b/libs/askar/src/askar-nats/event.handler.service.ts @@ -118,8 +118,14 @@ export class EventHandlerService { break; case PROOF_ACCEPT: - dto = event.data as AcceptProofRequestDto; - data = await this.agentService.acceptProof(dto.proofRecordId); + data = await this.agentService.acceptProof( + event.data as AcceptProofRequestDto, + ); + break; + + case PROOF_DECLINE: + dto = event.data as DeclineProofRequestDto; + data = await this.agentService.declineProofRequest(dto.proofRecordId); break; case PROOF_DECLINE: diff --git a/libs/askar/src/askar-rest/askar.rest.module.ts b/libs/askar/src/askar-rest/askar.rest.module.ts index 7390822ad25c59d12e428ba08264ddc9bd9cee82..6aff9fe7d2977452b6a685ddcba21a21537042e1 100644 --- a/libs/askar/src/askar-rest/askar.rest.module.ts +++ b/libs/askar/src/askar-rest/askar.rest.module.ts @@ -1,12 +1,20 @@ -import { Module, ValidationPipe } from "@nestjs/common"; +import { + Module, + ValidationPipe, + MiddlewareConsumer, + RequestMethod, +} from "@nestjs/common"; import { AgentService } from "../askar/agent.service"; -import { ConfigModule } from "@nestjs/config"; +import { ConfigModule, ConfigService } from "@nestjs/config"; import { LedgersModule } from "@ocm-engine/ledgers"; import { APP_PIPE } from "@nestjs/core"; import { RestController } from "./rest.controller"; +import { PassportModule } from "@nestjs/passport"; +import { BasicAuthMiddleware } from "./auth/basic.middleware"; +import { IConfAgent } from "@ocm-engine/config"; @Module({ - imports: [ConfigModule, LedgersModule], + imports: [ConfigModule, LedgersModule, PassportModule], providers: [ AgentService, { @@ -18,4 +26,17 @@ import { RestController } from "./rest.controller"; ], controllers: [RestController], }) -export class AskarRestModule {} +export class AskarRestModule { + constructor(private readonly configService: ConfigService) {} + + configure(consumer: MiddlewareConsumer) { + const config: IConfAgent | undefined = + this.configService.get<IConfAgent>("agent"); + + if (config?.agentIsSVDX) { + consumer + .apply(BasicAuthMiddleware) + .forRoutes({ path: "*messages", method: RequestMethod.ALL }); + } + } +} diff --git a/libs/askar/src/askar-rest/auth/basic.guard.ts b/libs/askar/src/askar-rest/auth/basic.guard.ts new file mode 100644 index 0000000000000000000000000000000000000000..354ea9ed785a36ed534e5536493034a23a7099f0 --- /dev/null +++ b/libs/askar/src/askar-rest/auth/basic.guard.ts @@ -0,0 +1,9 @@ +import { Injectable } from "@nestjs/common"; +import { AuthGuard } from "@nestjs/passport"; + +/** + * Basic guard is NOT currently used, it is left for the time when we implement jwt auth for the rest of the routes + */ + +@Injectable() +export class BasicGuard extends AuthGuard("basic") {} diff --git a/libs/askar/src/askar-rest/auth/basic.middleware.ts b/libs/askar/src/askar-rest/auth/basic.middleware.ts new file mode 100644 index 0000000000000000000000000000000000000000..25099e3a334f4c8448379ac88ed7673d3a1cb158 --- /dev/null +++ b/libs/askar/src/askar-rest/auth/basic.middleware.ts @@ -0,0 +1,48 @@ +import { + Injectable, + Logger, + NestMiddleware, + UnauthorizedException, +} from "@nestjs/common"; +import { Request, Response, NextFunction } from "express"; +import { ConfigService } from "@nestjs/config"; +import { IConfAgent } from "@ocm-engine/config"; + +@Injectable() +export class BasicAuthMiddleware implements NestMiddleware { + private readonly logger: Logger = new Logger(BasicAuthMiddleware.name); + + constructor(private readonly configService: ConfigService) {} + + use(req: Request, res: Response, next: NextFunction) { + const config: IConfAgent | undefined = + this.configService.get<IConfAgent>("agent"); + + if (!config?.agentIsSVDX) { + this.logger.log("Agent is REST only turning of basic auth"); + + return next(); + } + + this.logger.log("Agent is SVDX turning basic auth middleware on"); + + const authHeader = req.headers["authorization"]; + + if (!authHeader) { + return next(new UnauthorizedException()); + } + + const [username, password] = Buffer.from(authHeader.split(" ")[1], "base64") + .toString() + .split(":"); + + if ( + username === config.agentSVDXBasicUser && + password === config.agentSVDXBasicPass + ) { + return next(); + } + + return next(new UnauthorizedException()); + } +} diff --git a/libs/askar/src/askar-rest/auth/basic.strategy.ts b/libs/askar/src/askar-rest/auth/basic.strategy.ts new file mode 100644 index 0000000000000000000000000000000000000000..f454f0af8b0af6da4e6f71d3b7cb3319b22e4b1b --- /dev/null +++ b/libs/askar/src/askar-rest/auth/basic.strategy.ts @@ -0,0 +1,30 @@ +import { ConfigService } from "@nestjs/config"; +import { IConfAgent } from "@ocm-engine/config"; +import { BasicStrategy as Strategy } from "passport-http"; +import { PassportStrategy } from "@nestjs/passport"; +import { Injectable, UnauthorizedException } from "@nestjs/common"; + +/** + * Basic strategy is NOT currently used, it is left for the time when we implement jwt auth for the rest of the routes + */ +@Injectable() +export class BasicStrategy extends PassportStrategy(Strategy, "basic") { + constructor(private readonly configService: ConfigService) { + super(); + } + + async validate(username: string, password: string): Promise<boolean> { + console.log(username, password); + const config: IConfAgent | undefined = + this.configService.get<IConfAgent>("agent"); + + if ( + config?.agentSVDXBasicUser === username && + config?.agentSVDXBasicPass === password + ) { + return true; + } + + throw new UnauthorizedException(); + } +} diff --git a/libs/askar/src/askar-rest/rest.controller.ts b/libs/askar/src/askar-rest/rest.controller.ts index 023783a692ca2f12a7c6fdf2c84257c4a6a2e0e5..8227d7dd6ab466a4ce94aa370eb68f31f7c8a1b5 100644 --- a/libs/askar/src/askar-rest/rest.controller.ts +++ b/libs/askar/src/askar-rest/rest.controller.ts @@ -1,4 +1,5 @@ import { Body, Controller, Get, Param, Post, UseFilters } from "@nestjs/common"; + import { AgentService } from "../askar/agent.service"; import { CreateCredentialDefinitionRequsetDto, @@ -83,9 +84,9 @@ export class RestController { return this.agentService.getProofById(data.proofRecordId); } - @Post(`/credential/proof/:proof_record_id/accept`) - acceptProof(@Param("proof_record_id") proofRecordId: string) { - return this.agentService.acceptProof(proofRecordId); + @Post(`/credential/proof/accept`) + acceptProof(@Body() acceptProofRequestDto: AcceptProofRequestDto) { + return this.agentService.acceptProof(acceptProofRequestDto); } @Post(`/credential/proof/:proof_record_id/decline`) diff --git a/libs/askar/src/askar/agent.event.listener.servce.ts b/libs/askar/src/askar/agent-event-listener.service.ts similarity index 61% rename from libs/askar/src/askar/agent.event.listener.servce.ts rename to libs/askar/src/askar/agent-event-listener.service.ts index 03eaf71d0507327cd89e83b680ae4cdfbcf21cf5..9cb3722871aefcae414e07427051dfe572d6fc69 100644 --- a/libs/askar/src/askar/agent.event.listener.servce.ts +++ b/libs/askar/src/askar/agent-event-listener.service.ts @@ -5,6 +5,10 @@ import { BasicMessageEventTypes, BasicMessageRole, BasicMessageStateChangedEvent, + ConnectionEventTypes, + ConnectionStateChangedEvent, + ProofEventTypes, + ProofStateChangedEvent, } from "@aries-framework/core"; import { MakeBasicMessageResponseDto, @@ -13,11 +17,15 @@ import { } from "@ocm-engine/dtos"; import { IConfAgent } from "@ocm-engine/config"; import { ConfigService } from "@nestjs/config"; +import { + svdxConnectionStateChangeHandler, + svdxProofStateChangeHandler, +} from "../agent.utils"; @Injectable() -export class AgentEventListenerServce implements OnModuleInit { - private agentConfig: IConfAgent; - private readonly logger: Logger = new Logger(AgentEventListenerServce.name); +export class AgentEventListenerService implements OnModuleInit { + private agentConfig: IConfAgent | undefined; + private readonly logger: Logger = new Logger(AgentEventListenerService.name); constructor( private readonly gatewayClient: GatewayClient, @@ -27,8 +35,36 @@ export class AgentEventListenerServce implements OnModuleInit { onModuleInit(): void { this.logger.debug("Agent is listening for AFJ events"); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.agentConfig = this.configService.get<IConfAgent>("agent")!; + this.agentConfig = this.configService.get<IConfAgent>("agent"); + + if (this.agentConfig?.agentIsSVDX && this.agentConfig?.agentIsRest) { + this.askar.agent.events.on( + ConnectionEventTypes.ConnectionStateChanged, + async (ev: ConnectionStateChangedEvent) => { + this.logger.log("connection state event received"); + this.logger.debug(JSON.stringify(ev, null, 2)); + return svdxConnectionStateChangeHandler( + ev, + this.askar.agent, + this.agentConfig, + ); + }, + ); + + this.askar.agent.events.on( + ProofEventTypes.ProofStateChanged, + async (ev: ProofStateChangedEvent) => { + this.logger.log("proof state event received"); + this.logger.debug(JSON.stringify(ev, null, 2)); + + return svdxProofStateChangeHandler( + ev, + this.askar.agent, + this.agentConfig, + ); + }, + ); + } this.askar.agent.events.on( BasicMessageEventTypes.BasicMessageStateChanged, @@ -46,7 +82,7 @@ export class AgentEventListenerServce implements OnModuleInit { dto.connectionId = ev.payload.basicMessageRecord.connectionId; dto.from = connectionInfo?.theirLabel; - if (this.agentConfig.agentIsRest) { + if (this.agentConfig?.agentIsRest) { this.logger.debug( "agent is configured as rest, webhook still not implemented", ); diff --git a/libs/askar/src/askar/agent.service.ts b/libs/askar/src/askar/agent.service.ts index 72359aa5e6bc227a159ca324d2b213215168f654..d03a7226af0f1782e8f513fccc6243526392b396 100644 --- a/libs/askar/src/askar/agent.service.ts +++ b/libs/askar/src/askar/agent.service.ts @@ -2,6 +2,7 @@ import { Injectable } from "@nestjs/common"; import { AskarService } from "./askar.service"; import { AcceptInvitationResponseDto, + AcceptProofRequestDto, ConnectionNotFoundError, CreateCredentialDefinitionRequsetDto, CreateCredentialDefinitionResponseDto, @@ -18,13 +19,19 @@ import { MakeBasicMessageResponseDto, SchemaNotCreatedError, DeclineProofResponseDto, + ListSingleConnectionResponseDto, + SchemaNotFoundError, + ListSingleSchemaResponseDto, } from "@ocm-engine/dtos"; import { + AutoAcceptProof, CredentialState, - ProofExchangeRecord, + JsonEncoder, ProofState, } from "@aries-framework/core"; import { AnonCredsRequestedAttribute } from "@aries-framework/anoncreds"; +import { uuid } from "@aries-framework/core/build/utils/uuid"; +import { waitForProofExchangeRecordSubject } from "../agent.utils"; @Injectable() export class AgentService { @@ -60,20 +67,100 @@ export class AgentService { return r; }; - fetchConnections() { - return this.askar.agent.connections.getAll(); + fetchConnections = async () => { + let responseFromAgent = await this.askar.agent.connections.getAll(); + + if (!responseFromAgent) { + throw new ConnectionNotFoundError(); + } + + type ConnectionList = typeof responseFromAgent; + type ConnectionSingleItem = ConnectionList[0]; + + let listResponseAll = responseFromAgent.map((singleConnection: ConnectionSingleItem) => { + let listResponseSingle = new ListSingleConnectionResponseDto(); + listResponseSingle.connectionId = singleConnection.id; + listResponseSingle.createdAt = singleConnection.createdAt; + listResponseSingle.state = singleConnection.state; + listResponseSingle.role = singleConnection.role; + listResponseSingle.autoAcceptConnection = singleConnection.autoAcceptConnection; + listResponseSingle.outOfBandId = singleConnection?.outOfBandId; + listResponseSingle.updatedAt = singleConnection.updatedAt; + listResponseSingle.did = singleConnection.did; + listResponseSingle.theirLabel = singleConnection.theirLabel; + listResponseSingle.theirDid = singleConnection.theirDid; + + return listResponseSingle; + }) + + return listResponseAll; } - getConnectionById = (id: string) => { - return this.askar.agent.connections.findById(id); + getConnectionById = async (id: string) => { + let responseFromAgent = await this.askar.agent.connections.findById(id); + + if (!responseFromAgent) { + throw new ConnectionNotFoundError(); + } + + let listResponseSingle = new ListSingleConnectionResponseDto(); + listResponseSingle.connectionId = responseFromAgent.id; + listResponseSingle.createdAt = responseFromAgent.createdAt; + listResponseSingle.state = responseFromAgent.state; + listResponseSingle.role = responseFromAgent.role; + listResponseSingle.autoAcceptConnection = responseFromAgent.autoAcceptConnection; + listResponseSingle.outOfBandId = responseFromAgent?.outOfBandId; + listResponseSingle.updatedAt = responseFromAgent.updatedAt; + listResponseSingle.did = responseFromAgent.did; + listResponseSingle.theirLabel = responseFromAgent.theirLabel; + listResponseSingle.theirDid = responseFromAgent.theirDid; + + return [listResponseSingle]; }; - fetchSchemas = () => { - return this.askar.agent.modules.anoncreds.getCreatedSchemas({}); + fetchSchemas = async () => { + let responseFromAgent = await this.askar.agent.modules.anoncreds.getCreatedSchemas({}); + + if (!responseFromAgent) { + throw new SchemaNotFoundError(); + } + + type SchemaList = typeof responseFromAgent; + type SchemaSingleItem = SchemaList[0]; + + let listResponseAll = responseFromAgent.map((singleSchema: SchemaSingleItem) => { + let listResponseSingle = new ListSingleSchemaResponseDto(); + listResponseSingle.schemaId = singleSchema.schemaId; + listResponseSingle.version = singleSchema.schema.version; + listResponseSingle.schemaName = singleSchema.schema.name; + listResponseSingle.updatedAt = singleSchema.updatedAt; + listResponseSingle.attrNames = singleSchema.schema.attrNames; + listResponseSingle.issuerId = singleSchema.schema.issuerId; + listResponseSingle.methodName = singleSchema.methodName; + + return listResponseSingle; + }) + + return listResponseAll; }; - getSchemaById = (schemaId: string) => { - return this.askar.agent.modules.anoncreds.getSchema(schemaId); + getSchemaById = async (schemaId: string) => { + let responseFromAgent = await this.askar.agent.modules.anoncreds.getSchema(schemaId); + + if (!responseFromAgent || !responseFromAgent.schema) { + throw new SchemaNotFoundError(); + } + + let listResponseSingle = new ListSingleSchemaResponseDto(); + listResponseSingle.schemaId = responseFromAgent.schemaId; + listResponseSingle.version = responseFromAgent.schema.version; + listResponseSingle.schemaName = responseFromAgent.schema.name; + listResponseSingle.attrNames = responseFromAgent.schema.attrNames; + // listResponseSingle.updatedAt = responseFromAgent.schema.updatedAt; + listResponseSingle.issuerId = responseFromAgent.schema.issuerId; + // listResponseSingle.methodName = responseFromAgent.methodName; + + return [listResponseSingle]; }; createSchema = async (schema: CreateSchemaRequestDto) => { @@ -230,11 +317,11 @@ export class AgentService { }; issueProof = async (issueProofDto: IssueProofRequestDto) => { - let exchangeRecord: ProofExchangeRecord; + console.log(JSON.stringify(issueProofDto, null, 2)); const requestedAttributes: Record<string, AnonCredsRequestedAttribute> = {}; for (const attr of issueProofDto.attributes) { - requestedAttributes[attr.attributeName] = { + requestedAttributes[uuid()] = { name: attr.attributeName, restrictions: [ { @@ -246,32 +333,45 @@ export class AgentService { } if (!issueProofDto.connectionId) { - const { proofRecord } = await this.askar.agent.proofs.createRequest({ - protocolVersion: "v2", - proofFormats: { - anoncreds: { - name: "proof-request", - version: "1.0", - requested_attributes: requestedAttributes, + console.log("connection Id not detected, creating oob proof"); + const { proofRecord, message } = + await this.askar.agent.proofs.createRequest({ + protocolVersion: "v2", + proofFormats: { + anoncreds: { + name: "proof-request", + version: "1.0", + requested_attributes: requestedAttributes, + }, }, - }, - }); + autoAcceptProof: AutoAcceptProof.ContentApproved, + }); - exchangeRecord = proofRecord; - } else { - exchangeRecord = await this.askar.agent.proofs.requestProof({ - protocolVersion: "v2", - connectionId: issueProofDto.connectionId, - proofFormats: { - anoncreds: { - name: "proof-request", - version: "1.0", - requested_attributes: requestedAttributes, - }, - }, - }); + console.log({ proofRecord }); + const { invitationUrl } = + await this.askar.agent.oob.createLegacyConnectionlessInvitation({ + recordId: proofRecord.id, + message, + domain: this.askar.agentConfig.agentPeerAddress, + }); + + return { proofUrl: invitationUrl }; } + console.log(`${issueProofDto.connectionId} detected, issuing proof`); + + const exchangeRecord = await this.askar.agent.proofs.requestProof({ + protocolVersion: "v2", + connectionId: issueProofDto.connectionId, + proofFormats: { + anoncreds: { + name: "proof-request", + version: "1.0", + requested_attributes: requestedAttributes, + }, + }, + }); + const response = new IssueProofResponseDto(); response.proofId = exchangeRecord.id; response.connectionId = exchangeRecord.connectionId; @@ -304,9 +404,9 @@ export class AgentService { getProofById = async (proofRecordId: string) => { const proofRecord = await this.askar.agent.proofs.findById(proofRecordId); - + if (!proofRecord) { - return proofRecord + return proofRecord; } const proofResponse = new GetProofByIdResponseDto(); @@ -316,38 +416,70 @@ export class AgentService { proofResponse.state = proofRecord.state; proofResponse.updatedAt = proofRecord.updatedAt; proofResponse.createdAt = proofRecord.createdAt; - - - return proofResponse + + return proofResponse; + }; + + acceptProof = async (acceptProofDto: AcceptProofRequestDto) => { + if (acceptProofDto.proofUrl) { + return this.acceptOobProof(acceptProofDto.proofUrl); + } + return this.acceptConnectionProof(acceptProofDto.proofRecordId); + // TODO: map to internal dto }; - acceptProof = async (proofRecordId: string) => { + acceptOobProof = async (url: string) => { + const param = url.split("d_m=")[1]; + + const t = JsonEncoder.fromBase64(param); + + await this.askar.agent.receiveMessage(t); + + const record = await waitForProofExchangeRecordSubject(this.askar.agentR, { + state: ProofState.RequestReceived, + }); + + const response = new IssueProofResponseDto(); + + response.proofId = record.id; + response.state = record.state; + response.updatedAt = record.updatedAt; + response.createdAt = record.createdAt; + + return response; + }; + + acceptConnectionProof = async (proofRecordId: string) => { + console.log(`accepting proof request for ${proofRecordId}`); const requestedCredentials = await this.askar.agent.proofs.selectCredentialsForRequest({ proofRecordId, }); + console.log(JSON.stringify(requestedCredentials, null, 2)); + const proof = await this.askar.agent.proofs.acceptRequest({ proofRecordId, proofFormats: requestedCredentials.proofFormats, }); - const t = new IssueProofResponseDto(); - t.proofId = proof.id; - t.connectionId = proof.connectionId; - t.state = proof.state; - t.updatedAt = proof.updatedAt; - t.createdAt = proof.createdAt; + console.log(JSON.stringify(proof, null, 2)); - return t; - }; + const response = new IssueProofResponseDto(); + response.proofId = proof.id; + response.connectionId = proof.connectionId; + response.state = proof.state; + response.updatedAt = proof.updatedAt; + response.createdAt = proof.createdAt; + return response; + }; declineProofRequest = async (proofRecordId: string) => { const resultFromDecline = await this.askar.agent.proofs.declineRequest({ proofRecordId, // sendProblemReport: false, // REVIEW: do we have a use case for this key? - }) + }); const declineResponse = new DeclineProofResponseDto(); declineResponse.proofId = resultFromDecline.id; @@ -361,6 +493,7 @@ export class AgentService { resolve = async (did: string) => { return this.askar.agent.dids.resolve(did); + // TODO: map to internal dto }; sendMessage = async (message: MakeBasicMessageRequestDto) => { diff --git a/libs/askar/src/askar/askar.module.ts b/libs/askar/src/askar/askar.module.ts index 05a8706a87d787f04c2509461d5db4e9463974e0..c2f542cef457dd735b3ff5514299703b737bf903 100644 --- a/libs/askar/src/askar/askar.module.ts +++ b/libs/askar/src/askar/askar.module.ts @@ -3,7 +3,7 @@ import { AskarService } from "./askar.service"; import { AgentService } from "./agent.service"; import { ConfigModule } from "@nestjs/config"; import { LedgersModule } from "@ocm-engine/ledgers"; -import { AgentEventListenerServce } from "./agent.event.listener.servce"; +import { AgentEventListenerService } from "./agent-event-listener.service"; import { GatewayClient } from "@ocm-engine/clients"; @Global() @@ -12,7 +12,7 @@ import { GatewayClient } from "@ocm-engine/clients"; providers: [ AgentService, AskarService, - AgentEventListenerServce, + AgentEventListenerService, GatewayClient, ], exports: [AgentService, AskarService], diff --git a/libs/askar/src/askar/askar.service.ts b/libs/askar/src/askar/askar.service.ts index e07eb52a9f77a1f1b57ff7e0df2acdeebf9ec5e8..27c5307f0866f3cd1b07980fcac62b63759a9c91 100644 --- a/libs/askar/src/askar/askar.service.ts +++ b/libs/askar/src/askar/askar.service.ts @@ -7,10 +7,12 @@ import { import { Agent, + BaseEvent, ConsoleLogger, HttpOutboundTransport, InitConfig, LogLevel, + ProofEventTypes, TypedArrayEncoder, WsOutboundTransport, } from "@aries-framework/core"; @@ -22,12 +24,16 @@ import { generateKey, getAskarAnonCredsIndyModules, importDidsToWallet, + setupEventReplaySubjects, + setupSubjectTransports, } from "../agent.utils"; import { IConfAgent } from "@ocm-engine/config"; +import { ReplaySubject } from "rxjs"; @Injectable() export class AskarService implements OnModuleInit, OnModuleDestroy { public agent: Agent<ReturnType<typeof getAskarAnonCredsIndyModules>>; + public agentR: ReplaySubject<BaseEvent>; public agentConfig: IConfAgent; private readonly logger: Logger = new Logger(AskarService.name); @@ -73,6 +79,14 @@ export class AskarService implements OnModuleInit, OnModuleDestroy { this.agent.registerOutboundTransport(new WsOutboundTransport()); this.agent.registerOutboundTransport(new HttpOutboundTransport()); + setupSubjectTransports([this.agent]); + + const [agentR] = setupEventReplaySubjects( + [this.agent], + [ProofEventTypes.ProofStateChanged], + ); + + this.agentR = agentR; this.logger.log("Agent setup completed"); } diff --git a/libs/askar/src/askar/transports/agent.subject.inbound.transport.ts b/libs/askar/src/askar/transports/agent.subject.inbound.transport.ts new file mode 100644 index 0000000000000000000000000000000000000000..1a78cda63b11033b8eb096daec3831d1fdfc264d --- /dev/null +++ b/libs/askar/src/askar/transports/agent.subject.inbound.transport.ts @@ -0,0 +1,80 @@ +import { + Agent, + AgentContext, + EncryptedMessage, + InboundTransport, + MessageReceiver, + TransportService, + TransportSession, +} from "@aries-framework/core"; +import { Subject, Subscription } from "rxjs"; +import { uuid } from "@aries-framework/core/build/utils/uuid"; +import { SubjectMessage } from "../../agent.utils"; + +class SubjectTransportSession implements TransportSession { + public id: string; + public readonly type = "subject"; + private replySubject: Subject<SubjectMessage>; + + public constructor(id: string, replySubject: Subject<SubjectMessage>) { + this.id = id; + this.replySubject = replySubject; + } + + public async send( + agentContext: AgentContext, + encryptedMessage: EncryptedMessage, + ): Promise<void> { + this.replySubject.next({ message: encryptedMessage }); + } + + public async close(): Promise<void> { + this.replySubject.complete(); + } +} + +export class SubjectInboundTransport implements InboundTransport { + public readonly ourSubject: Subject<SubjectMessage>; + private subscription?: Subscription; + + public constructor(ourSubject = new Subject<SubjectMessage>()) { + this.ourSubject = ourSubject; + } + + public async start(agent: Agent) { + this.subscribe(agent); + } + + public async stop() { + this.subscription?.unsubscribe(); + } + + private subscribe(agent: Agent) { + const logger = agent.config.logger; + const transportService = agent.dependencyManager.resolve(TransportService); + const messageReceiver = agent.dependencyManager.resolve(MessageReceiver); + + this.subscription = this.ourSubject.subscribe({ + next: async ({ message, replySubject }: SubjectMessage) => { + logger.test("Received message"); + + let session: SubjectTransportSession | undefined; + if (replySubject) { + session = new SubjectTransportSession( + `subject-session-${uuid()}`, + replySubject, + ); + + // When the subject is completed (e.g. when the session is closed), we need to + // remove the session from the transport service so it won't be used for sending messages + // in the future. + replySubject.subscribe({ + complete: () => session && transportService.removeSession(session), + }); + } + + await messageReceiver.receiveMessage(message, { session }); + }, + }); + } +} diff --git a/libs/askar/src/askar/transports/agent.subject.outbound.transport.ts b/libs/askar/src/askar/transports/agent.subject.outbound.transport.ts new file mode 100644 index 0000000000000000000000000000000000000000..4c3d77c2471c4935018874a66e9c13df20329586 --- /dev/null +++ b/libs/askar/src/askar/transports/agent.subject.outbound.transport.ts @@ -0,0 +1,80 @@ +import { + Agent, + AriesFrameworkError, + InjectionSymbols, + MessageReceiver, + OutboundPackage, + OutboundTransport, +} from "@aries-framework/core"; +import { Logger } from "@nestjs/common"; +import { Subject, take, takeUntil } from "rxjs"; +import { SubjectMessage } from "../../agent.utils"; + +export class SubjectOutboundTransport implements OutboundTransport { + private logger!: Logger; + private subjectMap: { [key: string]: Subject<SubjectMessage> | undefined }; + private agent!: Agent; + private stop$!: Subject<boolean>; + + public supportedSchemes = ["rxjs", "wss"]; + + public constructor(subjectMap: { + [key: string]: Subject<SubjectMessage> | undefined; + }) { + this.subjectMap = subjectMap; + } + + public async start(agent: Agent): Promise<void> { + this.agent = agent; + + this.logger = agent.dependencyManager.resolve(InjectionSymbols.Logger); + this.stop$ = agent.dependencyManager.resolve(InjectionSymbols.Stop$); + } + + public async stop(): Promise<void> { + // No logic needed + } + + public async sendMessage(outboundPackage: OutboundPackage) { + const messageReceiver = + this.agent.dependencyManager.resolve(MessageReceiver); + this.logger.debug( + `Sending outbound message to endpoint ${outboundPackage.endpoint}`, + { + endpoint: outboundPackage.endpoint, + }, + ); + const { payload, endpoint } = outboundPackage; + + if (!endpoint) { + throw new AriesFrameworkError( + "Cannot send message to subject without endpoint", + ); + } + + const subject = this.subjectMap[endpoint]; + + if (!subject) { + throw new AriesFrameworkError( + `No subject found for endpoint ${endpoint}`, + ); + } + + // Create a replySubject just for this session. Both ends will be able to close it, + // mimicking a transport like http or websocket. Close session automatically when agent stops + const replySubject = new Subject<SubjectMessage>(); + this.stop$ + .pipe(take(1)) + .subscribe(() => !replySubject.closed && replySubject.complete()); + + replySubject.pipe(takeUntil(this.stop$)).subscribe({ + next: async ({ message }: SubjectMessage) => { + this.logger.debug("Received message"); + + await messageReceiver.receiveMessage(message); + }, + }); + + subject.next({ message: payload, replySubject }); + } +} diff --git a/libs/config/src/config/agent.config.ts b/libs/config/src/config/agent.config.ts index 98e02af930dcaa8bec69d5b7c4b2b14486ca9094..c7fced916bc3e844873b808c453f09678b64f5ef 100644 --- a/libs/config/src/config/agent.config.ts +++ b/libs/config/src/config/agent.config.ts @@ -18,5 +18,11 @@ export const agentConfig = registerAs( agentConsumerMaxMessagess: parseInt(process.env["AGENT_MAX_MESSAGES"]!), agentConsumerRateLimit: parseInt(process.env["AGENT_RETE_LIMIT"]!), agentPort: parseInt(process.env["AGENT_PORT"]!), + agentIsSVDX: process.env["AGENT_IS_SVDX"] === "true", + agentSVDXSchemaId: process.env["AGENT_SVDX_SCHEMA_ID"]!, + agentSVDXCredDefId: process.env["AGENT_SVDX_CRED_DEF_ID"]!, + agentSVDXWebHook: process.env["AGENT_SVDX_WEBHOOK_URL"]!, + agentSVDXBasicUser: process.env["AGENT_SVDX_BASIC_USER"]!, + agentSVDXBasicPass: process.env["AGENT_SVDX_BASIC_PASS"]!, }), ); diff --git a/libs/config/src/interfaces/agent.config.interface.ts b/libs/config/src/interfaces/agent.config.interface.ts index 1f0e7a85caa94710dd23ede1d6ff0d5042870a86..317b645a88d15a6b1eac322618bb706fabc12565 100644 --- a/libs/config/src/interfaces/agent.config.interface.ts +++ b/libs/config/src/interfaces/agent.config.interface.ts @@ -12,4 +12,11 @@ export interface IConfAgent { agentConsumerMaxMessagess: number; agentConsumerRateLimit: number; agentPort: number; + + agentIsSVDX: boolean; + agentSVDXSchemaId: string; + agentSVDXCredDefId: string; + agentSVDXWebHook: string; + agentSVDXBasicUser: string; + agentSVDXBasicPass: string; } diff --git a/libs/config/src/schemas/agent.schema.ts b/libs/config/src/schemas/agent.schema.ts index 9cf569159b843b261c55387a444a11a52c39d2e6..46b36990cd6060f206dfcba92df03df3b4e6c8df 100644 --- a/libs/config/src/schemas/agent.schema.ts +++ b/libs/config/src/schemas/agent.schema.ts @@ -9,8 +9,12 @@ export const agentSchema = Joi.object({ AGENT_DB_USER: Joi.string().required(), AGENT_DB_PASS: Joi.string().required(), AGENT_CONSUMER_NAME: Joi.string(), - AGENT_IS_REST: Joi.string().required(), + AGENT_IS_REST: Joi.string().optional(), AGENT_MAX_MESSAGES: Joi.string().required(), AGENT_RETE_LIMIT: Joi.string().required(), AGENT_PORT: Joi.string().required(), + AGENT_IS_SVDX: Joi.string().optional(), + AGENT_SVDX_SCHEMA_ID: Joi.string().optional(), + AGENT_SVDX_CRED_DEF_ID: Joi.string().optional(), + AGENT_SVDX_WEBHOOK_URL: Joi.string().optional(), }); diff --git a/libs/dtos/src/dtos/requests/accept.proof.request.dto.ts b/libs/dtos/src/dtos/requests/accept.proof.request.dto.ts index 848258238ee20537fbefc58b906bbcf2d7119cc8..6f77a9cb2ccfdb05a7e1f9e8b167fcc2a44c3b4c 100644 --- a/libs/dtos/src/dtos/requests/accept.proof.request.dto.ts +++ b/libs/dtos/src/dtos/requests/accept.proof.request.dto.ts @@ -1,7 +1,13 @@ -import { IsNotEmpty, IsString } from "class-validator"; +import { IsNotEmpty, IsString, ValidateIf } from "class-validator"; export class AcceptProofRequestDto { @IsString() @IsNotEmpty() + @ValidateIf((o) => o.proofUrl === undefined) proofRecordId: string; + + @IsString() + @IsNotEmpty() + @ValidateIf((o) => o.proofRecordId === undefined) + proofUrl: string; } diff --git a/libs/dtos/src/dtos/responses/list.connections.response.dto.ts b/libs/dtos/src/dtos/responses/list.connections.response.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..42302ddd228bc6baf4bcab658c74eff11cd6f082 --- /dev/null +++ b/libs/dtos/src/dtos/responses/list.connections.response.dto.ts @@ -0,0 +1,37 @@ +import { IsBoolean, IsDateString, IsNotEmpty, IsString } from "class-validator"; + +export class ListSingleConnectionResponseDto { + @IsNotEmpty() + @IsString() + connectionId: string; + + @IsNotEmpty() + @IsDateString() + createdAt: Date; + + @IsNotEmpty() + @IsString() + state: string; + + @IsNotEmpty() + @IsString() + role: string; + + @IsBoolean() + autoAcceptConnection?: boolean; + + @IsString() + outOfBandId?: string; + + @IsDateString() + updatedAt?: Date; + + @IsString() + did?: string; + + @IsString() + theirLabel?: string; + + @IsString() + theirDid?: string; +} \ No newline at end of file diff --git a/libs/dtos/src/dtos/responses/list.schema.response.dto.ts b/libs/dtos/src/dtos/responses/list.schema.response.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..833ca1627b82dc3983b29d47690e3687ba82f08b --- /dev/null +++ b/libs/dtos/src/dtos/responses/list.schema.response.dto.ts @@ -0,0 +1,26 @@ +import { IsDateString, IsNotEmpty, IsString } from "class-validator"; + +export class ListSingleSchemaResponseDto { + @IsNotEmpty() + @IsString() + schemaId: string; + + @IsNotEmpty() + @IsString() + version?: string; + + @IsDateString() + updatedAt?: Date; + + @IsString() + issuerId?: string; + + @IsString() + methodName?: string; + + @IsNotEmpty() + attrNames: string[]; + + @IsString() + schemaName?: string; +} diff --git a/libs/dtos/src/errors/schema.not.found.error.ts b/libs/dtos/src/errors/schema.not.found.error.ts new file mode 100644 index 0000000000000000000000000000000000000000..68ba71472c68dcc63b398fa3e2f8bbb2f246d648 --- /dev/null +++ b/libs/dtos/src/errors/schema.not.found.error.ts @@ -0,0 +1,10 @@ +export class SchemaNotFoundError extends Error { + constructor(message = "Schema not found") { + super(message); + this.name = "SchemaNotFoundError"; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, SchemaNotFoundError); + } + } +} \ No newline at end of file diff --git a/libs/dtos/src/events/dtoToEventTransformer.ts b/libs/dtos/src/events/dtoToEventTransformer.ts index 8365ac1b1600e83f6c5fbea1cac3fa4f911295d2..a120dee89f47017180b2ffa1d8a3eeab2bf48fd9 100644 --- a/libs/dtos/src/events/dtoToEventTransformer.ts +++ b/libs/dtos/src/events/dtoToEventTransformer.ts @@ -17,6 +17,7 @@ import { IssueProofRequestDto } from "../dtos/requests/issue.proof.request.dto"; import { AcceptCredentialOfferRequestDto } from "../dtos/requests/accept.credential.offer.request.dto"; import { MakeBasicMessageResponseDto } from "../dtos/responses/make.basic.message.response.dto"; import { MakeBasicMessageRequestDto } from "../dtos/requests/make.basic.message.request.dto"; +import { AcceptProofRequestDto } from "../dtos/requests/accept.proof.request.dto"; import { GetProofByIdResponseDto } from "../dtos/responses/get.proof.response.dto"; import { DeclineProofRequestDto } from '../dtos/requests/decline.proof.request.dto'; import { DeclineProofResponseDto } from "../dtos/responses/decline.proof.response.dto"; @@ -28,6 +29,8 @@ export const makeEvent = (payload: { | AcceptCredentialOfferRequestDto | CreateInvitationResponseDto | GetConnectionRequestDto + | CreateSchemaRequestDto + | AcceptProofRequestDto | DeclineProofRequestDto | DeclineProofResponseDto | CreateSchemaRequestDto diff --git a/libs/dtos/src/index.ts b/libs/dtos/src/index.ts index ce0ac84aac42b0426913d8f6ed022b7e085fc050..4f3bed1be6ef7cff4a2ac3c50d272e18c4807e47 100644 --- a/libs/dtos/src/index.ts +++ b/libs/dtos/src/index.ts @@ -21,12 +21,15 @@ export * from "./dtos/responses/gateway.accepted.response.dto"; export * from "./dtos/responses/make.basic.message.response.dto"; export * from "./dtos/responses/get.proof.response.dto"; export * from "./dtos/responses/decline.proof.response.dto"; +export * from "./dtos/responses/list.connections.response.dto"; +export * from "./dtos/responses/list.schema.response.dto"; export * from "./errors/connection.not.found.error"; export * from "./errors/schema.not.created.error"; export * from "./errors/credential.not.created.error"; export * from "./errors/ledger.provider.fail.registration.error"; export * from "./errors/connection.unsupported.type.error"; +export * from "./errors/schema.not.found.error"; export * from "./events/event"; export * from "./events/types"; diff --git a/package.json b/package.json index 4b178adf4a117e61fe82bbee9f0114b899f7593c..9979d29e05059f82ce25ba14d6862250ac668218 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@nestjs/config": "^2.3.1", "@nestjs/core": "^9.0.0", "@nestjs/microservices": "^9.4.2", + "@nestjs/passport": "^10.0.1", "@nestjs/platform-express": "^9.0.0", "@nestjs/platform-ws": "^10.1.3", "@nestjs/websockets": "^9.4.2", @@ -51,6 +52,9 @@ "cloudevents": "^7.0.0", "joi": "^17.9.2", "nats": "^2.14.0", + "passport": "^0.6.0", + "passport-http": "^0.3.0", + "passport-local": "^1.0.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.0.0", "tslib": "^2.3.0", @@ -83,6 +87,8 @@ "@types/async-retry": "^1.4.5", "@types/jest": "^29.4.0", "@types/node": "~18.7.1", + "@types/passport-http": "^0.3.9", + "@types/passport-local": "^1.0.35", "@typescript-eslint/eslint-plugin": "^5.58.0", "@typescript-eslint/parser": "^5.58.0", "concurrently": "^8.2.0", diff --git a/yarn.lock b/yarn.lock index 9b857ac58a18edcb876f55ba20d6a865edc57975..2766f1095dc46380e23bf57b0170333df647dd1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1676,6 +1676,11 @@ iterare "1.2.1" tslib "2.5.3" +"@nestjs/passport@^10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@nestjs/passport/-/passport-10.0.1.tgz#4a745cb4acf01ef8fd56b9ec1349ac74165b098f" + integrity sha512-hS22LeNj0LByS9toBPkpKyZhyKAXoHACLS1EQrjbAJJEQjhocOskVGwcMwvMlz+ohN+VU804/nMF1Zlya4+TiQ== + "@nestjs/platform-express@^9.0.0": version "9.4.3" resolved "https://registry.yarnpkg.com/@nestjs/platform-express/-/platform-express-9.4.3.tgz#f61b75686bdfce566be3b54fa7bb20a4d87ed619" @@ -2899,6 +2904,38 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/passport-http@^0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@types/passport-http/-/passport-http-0.3.9.tgz#268e483ade820d4f0edb3d35cec090d1990cc081" + integrity sha512-uQ4vyRdvM0jdWuKpLmi6Q6ri9Nwt8YnHmF7kE6snbthxPrsMWcjRCVc5WcPaQ356ODSZTDgiRYURMPIspCkn3Q== + dependencies: + "@types/express" "*" + "@types/passport" "*" + +"@types/passport-local@^1.0.35": + version "1.0.35" + resolved "https://registry.yarnpkg.com/@types/passport-local/-/passport-local-1.0.35.tgz#233d370431b3f93bb43cf59154fb7519314156d9" + integrity sha512-K4eLTJ8R0yYW8TvCqkjB0pTKoqfUSdl5PfZdidTjV2ETV3604fQxtY6BHKjQWAx50WUS0lqzBvKv3LoI1ZBPeA== + dependencies: + "@types/express" "*" + "@types/passport" "*" + "@types/passport-strategy" "*" + +"@types/passport-strategy@*": + version "0.2.35" + resolved "https://registry.yarnpkg.com/@types/passport-strategy/-/passport-strategy-0.2.35.tgz#e52f5212279ea73f02d9b06af67efe9cefce2d0c" + integrity sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g== + dependencies: + "@types/express" "*" + "@types/passport" "*" + +"@types/passport@*": + version "1.0.12" + resolved "https://registry.yarnpkg.com/@types/passport/-/passport-1.0.12.tgz#7dc8ab96a5e895ec13688d9e3a96920a7f42e73e" + integrity sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw== + dependencies: + "@types/express" "*" + "@types/qs@*": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" @@ -8901,6 +8938,34 @@ parseurl@~1.3.2, parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +passport-http@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/passport-http/-/passport-http-0.3.0.tgz#8ee53d4380be9c60df2151925029826f77115603" + integrity sha512-OwK9DkqGVlJfO8oD0Bz1VDIo+ijD3c1ZbGGozIZw+joIP0U60pXY7goB+8wiDWtNqHpkTaQiJ9Ux1jE3Ykmpuw== + dependencies: + passport-strategy "1.x.x" + +passport-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee" + integrity sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow== + dependencies: + passport-strategy "1.x.x" + +passport-strategy@1.x.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" + integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA== + +passport@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/passport/-/passport-0.6.0.tgz#e869579fab465b5c0b291e841e6cc95c005fac9d" + integrity sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug== + dependencies: + passport-strategy "1.x.x" + pause "0.0.1" + utils-merge "^1.0.1" + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -8966,6 +9031,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pause@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d" + integrity sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -11063,7 +11133,7 @@ util@^0.12.4: is-typed-array "^1.1.3" which-typed-array "^1.1.2" -utils-merge@1.0.1: +utils-merge@1.0.1, utils-merge@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==