diff --git a/package-lock.json b/package-lock.json index 78605610..b5798b73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cheqd/credential-service", - "version": "2.10.0", + "version": "2.11.0-develop.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cheqd/credential-service", - "version": "2.10.0", + "version": "2.11.0-develop.1", "license": "Apache-2.0", "dependencies": { "@cheqd/did-provider-cheqd": "^3.6.11", @@ -5157,7 +5157,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "devOptional": true + "optional": true }, "node_modules/@graphql-typed-document-node/core": { "version": "3.2.0", @@ -6897,7 +6897,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "devOptional": true, + "optional": true, "dependencies": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" @@ -6908,7 +6908,7 @@ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", "deprecated": "This functionality has been moved to @npmcli/fs", - "devOptional": true, + "optional": true, "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" @@ -8832,7 +8832,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "devOptional": true, + "optional": true, "engines": { "node": ">= 6" } @@ -11998,7 +11998,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "devOptional": true, + "optional": true, "dependencies": { "humanize-ms": "^1.2.1" }, @@ -13121,7 +13121,8 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/bytes": { "version": "3.1.2", @@ -13135,7 +13136,7 @@ "version": "15.3.0", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "devOptional": true, + "optional": true, "dependencies": { "@npmcli/fs": "^1.0.0", "@npmcli/move-file": "^1.0.1", @@ -15206,7 +15207,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "devOptional": true, + "optional": true, "engines": { "node": ">=6" } @@ -17233,7 +17234,8 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", "integrity": "sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -17251,7 +17253,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "devOptional": true + "optional": true }, "node_modules/http-errors": { "version": "2.0.0", @@ -17312,7 +17314,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "devOptional": true, + "optional": true, "dependencies": { "ms": "^2.0.0" } @@ -17446,7 +17448,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "devOptional": true + "optional": true }, "node_modules/inflight": { "version": "1.0.6", @@ -18056,7 +18058,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "devOptional": true + "optional": true }, "node_modules/is-number": { "version": "7.0.0", @@ -20835,7 +20837,7 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "devOptional": true, + "optional": true, "dependencies": { "agentkeepalive": "^4.1.3", "cacache": "^15.2.0", @@ -20862,7 +20864,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "devOptional": true, + "optional": true, "dependencies": { "debug": "4" }, @@ -20874,7 +20876,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "devOptional": true, + "optional": true, "dependencies": { "@tootallnate/once": "1", "agent-base": "6", @@ -20888,7 +20890,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "devOptional": true, + "optional": true, "dependencies": { "agent-base": "6", "debug": "4" @@ -21874,7 +21876,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "devOptional": true, + "optional": true, "dependencies": { "minipass": "^3.0.0" }, @@ -21886,7 +21888,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", - "devOptional": true, + "optional": true, "dependencies": { "minipass": "^3.1.0", "minipass-sized": "^1.0.3", @@ -21903,7 +21905,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "devOptional": true, + "optional": true, "dependencies": { "minipass": "^3.0.0" }, @@ -21915,7 +21917,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "devOptional": true, + "optional": true, "dependencies": { "minipass": "^3.0.0" }, @@ -21927,7 +21929,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "devOptional": true, + "optional": true, "dependencies": { "minipass": "^3.0.0" }, @@ -22329,7 +22331,7 @@ "version": "8.4.1", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", - "devOptional": true, + "optional": true, "dependencies": { "env-paths": "^2.2.0", "glob": "^7.1.4", @@ -22363,7 +22365,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "devOptional": true, + "optional": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -22376,7 +22378,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "devOptional": true, + "optional": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", @@ -22395,7 +22397,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "devOptional": true, + "optional": true, "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", @@ -22410,7 +22412,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "devOptional": true, + "optional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -22709,7 +22711,8 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-7.0.0.tgz", "integrity": "sha512-xXxr8y5U0kl8dVkz2oK7yZjPBvqM2fwaO5l3Yg13p03v8+E3qQcD0JNhHzjL1vyGgxcKkD0cco+NLR72iuPk3g==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "hosted-git-info": "^3.0.2", "osenv": "^0.1.5", @@ -22721,7 +22724,8 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "devOptional": true, + "optional": true, + "peer": true, "bin": { "semver": "bin/semver" } @@ -26174,7 +26178,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", - "devOptional": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -26183,7 +26188,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "devOptional": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -26192,7 +26198,8 @@ "version": "0.1.5", "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -26365,7 +26372,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "devOptional": true, + "optional": true, "dependencies": { "aggregate-error": "^3.0.0" }, @@ -27474,13 +27481,13 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "devOptional": true + "optional": true }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "devOptional": true, + "optional": true, "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" @@ -27493,7 +27500,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "devOptional": true + "optional": true }, "node_modules/prompts": { "version": "2.4.2", @@ -27660,7 +27667,8 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz", "integrity": "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==", - "devOptional": true, + "optional": true, + "peer": true, "bin": { "qrcode-terminal": "bin/qrcode-terminal.js" } @@ -28720,7 +28728,7 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "devOptional": true, + "optional": true, "engines": { "node": ">= 4" } @@ -29813,7 +29821,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "devOptional": true, + "optional": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -29823,7 +29831,7 @@ "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", - "devOptional": true, + "optional": true, "dependencies": { "ip": "^2.0.0", "smart-buffer": "^4.2.0" @@ -29837,7 +29845,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", - "devOptional": true, + "optional": true, "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", @@ -29851,7 +29859,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "devOptional": true, + "optional": true, "dependencies": { "debug": "4" }, @@ -29863,7 +29871,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", - "devOptional": true + "optional": true }, "node_modules/sonic-boom": { "version": "2.8.0", @@ -30014,7 +30022,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "devOptional": true, + "optional": true, "dependencies": { "minipass": "^3.1.1" }, @@ -31454,7 +31462,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "devOptional": true, + "optional": true, "dependencies": { "unique-slug": "^2.0.0" } @@ -31463,7 +31471,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "devOptional": true, + "optional": true, "dependencies": { "imurmurhash": "^0.1.4" } @@ -31681,7 +31689,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "builtins": "^1.0.3" } @@ -31965,7 +31974,8 @@ "version": "2.4.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", diff --git a/src/controllers/credentials.ts b/src/controllers/credentials.ts index 71e2f270..10c181b9 100644 --- a/src/controllers/credentials.ts +++ b/src/controllers/credentials.ts @@ -7,6 +7,9 @@ import { check, query, validationResult } from 'express-validator'; import { Credentials } from '../services/credentials.js'; import { IdentityServiceStrategySetup } from '../services/identity/index.js'; import jwt_decode from 'jwt-decode'; +import type { ITrackOperation } from '../types/shared.js'; +import { Cheqd } from '@cheqd/did-provider-cheqd'; +import { OPERATION_CATEGORY_NAME_CREDENTIAL } from '../types/constants.js'; export class CredentialController { public static issueValidator = [ @@ -245,7 +248,7 @@ export class CredentialController { if (result.error) { return response.status(StatusCodes.BAD_REQUEST).json({ verified: result.verified, - error: result.error, + error: result.error.message, }); } return response.status(StatusCodes.OK).json(result); @@ -300,18 +303,55 @@ export class CredentialController { if (!result.isEmpty()) { return response.status(StatusCodes.BAD_REQUEST).json({ error: result.array()[0].msg }); } - - const publish = request.query.publish === 'false' ? false : true; try { - return response - .status(StatusCodes.OK) - .json( - await new IdentityServiceStrategySetup(response.locals.customer.customerId).agent.revokeCredentials( - request.body.credential, - publish, - response.locals.customer - ) - ); + const publish = request.query.publish === 'false' ? false : true; + // Get symmetric key + const symmetricKey = request.body.symmetricKey as string; + // Get strategy e.g. psotgres or local + const identityServiceStrategySetup = new IdentityServiceStrategySetup(response.locals.customer.customerId); + const result = await identityServiceStrategySetup.agent.revokeCredentials( + request.body.credential, + publish, + response.locals.customer, + symmetricKey + ); + // Track operation if revocation was successful and publish is true + // Otherwise the StatusList2021 publisher should manually publish the resource + // and it will be tracked there + if (!result.error && result.resourceMetadata && publish) { + // decode credential for getting issuer did + const credential = + typeof request.body.credential === 'string' + ? await Cheqd.decodeCredentialJWT(request.body.credential) + : request.body.credential; + // get issuer did + const issuerDid = + typeof credential.issuer === 'string' + ? credential.issuer + : (credential.issuer as { id: string }).id; + const trackInfo = { + category: OPERATION_CATEGORY_NAME_CREDENTIAL, + operation: 'revoke', + customer: response.locals.customer, + user: response.locals.user, + did: issuerDid, + data: { + encrypted: result.statusList?.metadata?.encrypted, + resource: result.resourceMetadata, + symmetricKey: '', + }, + } as ITrackOperation; + + // Track operation + const trackResult = await identityServiceStrategySetup.agent.trackOperation(trackInfo); + if (trackResult.error) { + return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ + error: trackResult.error, + }); + } + } + // Return Ok response + return response.status(StatusCodes.OK).json(result); } catch (error) { return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ error: `${error}`, @@ -363,13 +403,56 @@ export class CredentialController { } try { - return response - .status(StatusCodes.OK) - .json( - await new IdentityServiceStrategySetup( - response.locals.customer.customerId - ).agent.suspendCredentials(request.body.credential, request.body.publish, response.locals.customer) - ); + const publish = request.query.publish === 'false' ? false : true; + // Get symmetric key + const symmetricKey = request.body.symmetricKey as string; + // Get strategy e.g. psotgres or local + const identityServiceStrategySetup = new IdentityServiceStrategySetup(response.locals.customer.customerId); + + const result = await identityServiceStrategySetup.agent.suspendCredentials( + request.body.credential, + publish, + response.locals.customer, + symmetricKey + ); + + // Track operation if suspension was successful and publish is true + // Otherwise the StatusList2021 publisher should manually publish the resource + // and it will be tracked there + if (!result.error && result.resourceMetadata && publish) { + // decode credential for getting issuer did + const credential = + typeof request.body.credential === 'string' + ? await Cheqd.decodeCredentialJWT(request.body.credential) + : request.body.credential; + // get issuer did + const issuerDid = + typeof credential.issuer === 'string' + ? credential.issuer + : (credential.issuer as { id: string }).id; + const trackInfo = { + category: OPERATION_CATEGORY_NAME_CREDENTIAL, + operation: 'suspend', + customer: response.locals.customer, + user: response.locals.user, + did: issuerDid, + data: { + encrypted: result.statusList?.metadata?.encrypted, + resource: result.resourceMetadata, + symmetricKey: '', + }, + } as ITrackOperation; + + // Track operation + const trackResult = await identityServiceStrategySetup.agent.trackOperation(trackInfo); + if (trackResult.error) { + return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ + error: trackResult.error, + }); + } + } + + return response.status(StatusCodes.OK).json(result); } catch (error) { return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ error: `${error}`, @@ -421,17 +504,54 @@ export class CredentialController { } try { - return response - .status(StatusCodes.OK) - .json( - await new IdentityServiceStrategySetup( - response.locals.customer.customerId - ).agent.reinstateCredentials( - request.body.credential, - request.body.publish, - response.locals.customer - ) - ); + const publish = request.query.publish === 'false' ? false : true; + // Get symmetric key + const symmetricKey = request.body.symmetricKey as string; + // Get strategy e.g. psotgres or local + const identityServiceStrategySetup = new IdentityServiceStrategySetup(response.locals.customer.customerId); + const result = await identityServiceStrategySetup.agent.reinstateCredentials( + request.body.credential, + publish, + response.locals.customer, + symmetricKey + ); + // Track operation if the process of reinstantiating was successful and publish is true + // Otherwise the StatusList2021 publisher should manually publish the resource + // and it will be tracked there + if (!result.error && result.resourceMetadata && publish) { + // decode credential for getting issuer did + const credential = + typeof request.body.credential === 'string' + ? await Cheqd.decodeCredentialJWT(request.body.credential) + : request.body.credential; + // get issuer did + const issuerDid = + typeof credential.issuer === 'string' + ? credential.issuer + : (credential.issuer as { id: string }).id; + const trackInfo = { + category: OPERATION_CATEGORY_NAME_CREDENTIAL, + operation: 'reinstate', + customer: response.locals.customer, + user: response.locals.user, + did: issuerDid, + data: { + encrypted: result.statusList?.metadata?.encrypted, + resource: result.resourceMetadata, + symmetricKey: '', + }, + } as ITrackOperation; + + // Track operation + const trackResult = await identityServiceStrategySetup.agent.trackOperation(trackInfo); + if (trackResult.error) { + return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ + error: trackResult.error, + }); + } + } + // Return Ok response + return response.status(StatusCodes.OK).json(result); } catch (error) { return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ error: `${error}`, diff --git a/src/controllers/issuer.ts b/src/controllers/issuer.ts index 7599bfa7..283b8c39 100644 --- a/src/controllers/issuer.ts +++ b/src/controllers/issuer.ts @@ -23,7 +23,8 @@ import { import { DIDMetadataDereferencingResult, DefaultResolverUrl } from '@cheqd/did-provider-cheqd'; import { bases } from 'multiformats/basics'; import { base64ToBytes } from 'did-jwt'; -import type { CreateDidRequestBody } from '../types/shared.js'; +import type { CreateDidRequestBody, ITrackOperation } from '../types/shared.js'; +import { OPERATION_CATEGORY_NAME_RESOURCE } from '../types/constants.js'; export class IssuerController { // ToDo: improve validation in a "bail" fashion @@ -543,13 +544,12 @@ export class IssuerController { const { did } = request.params; const { data, encoding, name, type, alsoKnownAs, version, network } = request.body; + const identityServiceStrategySetup = new IdentityServiceStrategySetup(response.locals.customer.customerId); let resourcePayload: Partial = {}; try { // check if did is registered on the ledger - const { didDocument, didDocumentMetadata } = await new IdentityServiceStrategySetup( - response.locals.customer.customerId - ).agent.resolveDid(did); + const { didDocument, didDocumentMetadata } = await identityServiceStrategySetup.agent.resolveDid(did); if (!didDocument || !didDocumentMetadata || didDocumentMetadata.deactivated) { return response.status(StatusCodes.BAD_REQUEST).send({ error: `${did} is a either Deactivated or Not found`, @@ -565,18 +565,42 @@ export class IssuerController { version, alsoKnownAs, }; - const result = await new IdentityServiceStrategySetup( - response.locals.customer.customerId - ).agent.createResource(network || did.split(':')[2], resourcePayload, response.locals.customer); + const result = await identityServiceStrategySetup.agent.createResource( + network || did.split(':')[2], + resourcePayload, + response.locals.customer + ); + if (result) { const url = new URL( `${process.env.RESOLVER_URL || DefaultResolverUrl}${did}?` + `resourceId=${resourcePayload.id}&resourceMetadata=true` ); const didDereferencing = (await (await fetch(url)).json()) as DIDMetadataDereferencingResult; + const resource = didDereferencing.contentStream.linkedResourceMetadata[0]; + + // track resource creation + const trackResourceInfo = { + category: OPERATION_CATEGORY_NAME_RESOURCE, + operation: 'createResource', + customer: response.locals.customer, + did, + data: { + resource: resource, + encrypted: false, + symmetricKey: '', + }, + } as ITrackOperation; + + const trackResult = await identityServiceStrategySetup.agent.trackOperation(trackResourceInfo); + if (trackResult.error) { + return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ + error: `${trackResult.error}`, + }); + } return response.status(StatusCodes.CREATED).json({ - resource: didDereferencing.contentStream.linkedResourceMetadata[0], + resource, }); } else { return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ diff --git a/src/controllers/revocation.ts b/src/controllers/revocation.ts index f08b686a..fd2346be 100644 --- a/src/controllers/revocation.ts +++ b/src/controllers/revocation.ts @@ -20,6 +20,7 @@ import { DefaultStatusActionPurposeMap, DefaultStatusActions, FeePaymentOptions, + ITrackOperation, MinimalPaymentCondition, SearchStatusListQuery, SearchStatusListSuccessfulResponseBody, @@ -41,6 +42,8 @@ import { } from '@cheqd/did-provider-cheqd'; import type { AlternativeUri } from '@cheqd/ts-proto/cheqd/resource/v2/resource.js'; import { toNetwork } from '../helpers/helpers.js'; +import { OPERATION_CATEGORY_NAME_CREDENTIAL_STATUS } from '../types/constants.js'; +import { CheqdNetwork } from '@cheqd/sdk'; export class RevocationController { static createUnencryptedValidator = [ @@ -551,12 +554,13 @@ export class RevocationController { // define broadcast mode const data = encodedList ? fromString(encodedList, encoding) : undefined; + // create agent + const identityServiceStrategySetup = new IdentityServiceStrategySetup(response.locals.customer.customerId); + try { // broadcast, if applicable if (data) { - const result = await new IdentityServiceStrategySetup( - response.locals.customer.customerId - ).agent.broadcastStatusList2021( + const result = await identityServiceStrategySetup.agent.broadcastStatusList2021( did, { data, name: statusListName, alsoKnownAs, version: statusListVersion }, { encoding, statusPurpose }, @@ -566,9 +570,7 @@ export class RevocationController { } // create unencrypted status list - const result = (await new IdentityServiceStrategySetup( - response.locals.customer.customerId - ).agent.createUnencryptedStatusList2021( + const result = (await identityServiceStrategySetup.agent.createUnencryptedStatusList2021( did, { name: statusListName, @@ -591,6 +593,27 @@ export class RevocationController { } as CreateUnencryptedStatusListUnsuccessfulResponseBody); } + // Keep track of resources + const trackResourceInfo = { + category: OPERATION_CATEGORY_NAME_CREDENTIAL_STATUS, + operation: 'createUnencryptedStatusList', + customer: response.locals.customer, + user: response.locals.user, + did, + data: { + resource: result.resourceMetadata, + encrypted: result.resource?.metadata?.encrypted, + symmetricKey: '', + }, + } as ITrackOperation; + + const trackResult = await identityServiceStrategySetup.agent.trackOperation(trackResourceInfo); + if (trackResult.error) { + return response + .status(StatusCodes.INTERNAL_SERVER_ERROR) + .json(trackResult as CreateEncryptedStatusListUnsuccessfulResponseBody); + } + // return result return response.status(StatusCodes.OK).json({ ...result, encrypted: undefined }); } catch (error) { @@ -667,12 +690,11 @@ export class RevocationController { // collect request parameters - case: query const { statusPurpose } = request.query as CreateEncryptedStatusListRequestQuery; + const identityServiceStrategySetup = new IdentityServiceStrategySetup(response.locals.customer.customerId); try { // create encrypted status list - const result = (await new IdentityServiceStrategySetup( - response.locals.customer.customerId - ).agent.createEncryptedStatusList2021( + const result = (await identityServiceStrategySetup.agent.createEncryptedStatusList2021( did, { name: statusListName, @@ -698,6 +720,33 @@ export class RevocationController { error: result.error?.message || result.error.toString(), } as CreateEncryptedStatusListUnsuccessfulResponseBody); } + // Keep track of resources + // For now we decided not to store symmetricKey yet + + const trackResourceInfo = { + operation: 'createEncryptedStatusList', + category: OPERATION_CATEGORY_NAME_CREDENTIAL_STATUS, + customer: response.locals.customer, + user: response.locals.user, + did: did, + data: { + resource: result.resourceMetadata, + encrypted: true, + symmetricKey: '', + }, + feePaymentOptions: { + feePaymentAddress: feePaymentAddress, + feePaymentAmount: feePaymentAmount, + feePaymentNetwork: CheqdNetwork.Testnet, + }, + } as ITrackOperation; + const trackResult = await identityServiceStrategySetup.agent.trackOperation(trackResourceInfo); + + if (trackResult.error) { + return response + .status(StatusCodes.INTERNAL_SERVER_ERROR) + .json(trackResult as CreateEncryptedStatusListUnsuccessfulResponseBody); + } // return result return response.status(StatusCodes.OK).json({ ...result, encrypted: undefined }); @@ -855,6 +904,29 @@ export class RevocationController { resourceMetadata: result.resourceMetadata, } satisfies UpdateUnencryptedStatusListSuccessfulResponseBody; + // track resource creation + if (result.resourceMetadata) { + const trackResourceInfo = { + category: OPERATION_CATEGORY_NAME_CREDENTIAL_STATUS, + operation: 'updateUnencryptedStatusList', + customer: response.locals.customer, + user: response.locals.user, + did, + data: { + resource: result.resourceMetadata, + encrypted: false, + symmetricKey: '', + }, + } as ITrackOperation; + const trackResult = await identityServiceStrategySetup.agent.trackOperation(trackResourceInfo); + if (trackResult.error) { + return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ + updated: false, + error: trackResult.error, + } as UpdateUnencryptedStatusListUnsuccessfulResponseBody); + } + } + return response.status(StatusCodes.OK).json(formatted); } catch (error) { // return catch-all error @@ -1026,6 +1098,34 @@ export class RevocationController { symmetricKey: result.symmetricKey, } satisfies UpdateEncryptedStatusListSuccessfulResponseBody; + // track resource creation + if (result.resourceMetadata) { + const trackResourceInfo = { + category: OPERATION_CATEGORY_NAME_CREDENTIAL_STATUS, + operation: 'updateEncryptedStatusList', + customer: response.locals.customer, + user: response.locals.user, + did, + data: { + resource: result.resourceMetadata, + encrypted: true, + symmetricKey: '', + }, + feePaymentOptions: { + feePaymentAddress: feePaymentAddress, + feePaymentAmount: feePaymentAmount, + feePaymentNetwork: CheqdNetwork.Testnet, + }, + } as ITrackOperation; + const trackResult = await identityServiceStrategySetup.agent.trackOperation(trackResourceInfo); + if (trackResult.error) { + return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ + updated: false, + error: trackResult.error, + } as UpdateUnencryptedStatusListUnsuccessfulResponseBody); + } + } + return response.status(StatusCodes.OK).json(formatted); } catch (error) { // return catch-all error diff --git a/src/database/entities/resource.entity.ts b/src/database/entities/resource.entity.ts index aefb3bbc..92ff7772 100644 --- a/src/database/entities/resource.entity.ts +++ b/src/database/entities/resource.entity.ts @@ -1,4 +1,4 @@ -import { BeforeInsert, Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; +import { BeforeInsert, Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm'; import * as dotenv from 'dotenv'; import { Identifier, Key } from '@veramo/data-store'; @@ -9,7 +9,10 @@ dotenv.config(); @Entity('resource') export class ResourceEntity { - @PrimaryGeneratedColumn('uuid') + @PrimaryColumn({ + type: 'uuid', + nullable: false, + }) resourceId!: string; @Column({ @@ -80,18 +83,28 @@ export class ResourceEntity { customer!: CustomerEntity; constructor( - identifier: IdentifierEntity, - key: KeyEntity, + resourceId: string, resourceName: string, resourceType: string, mediaType: string, - customer: CustomerEntity + previousVersionId: string | null, + nextVersionId: string | null, + customer: CustomerEntity, + identifier: IdentifierEntity, + key: KeyEntity, + encrypted: boolean, + symmetricKey: string ) { - this.identifier = identifier; - this.key = key; + this.resourceId = resourceId; this.resourceName = resourceName; this.resourceType = resourceType; this.mediaType = mediaType; + this.previousVersionId = previousVersionId || ''; + this.nextVersionId = nextVersionId || ''; this.customer = customer; + this.identifier = identifier; + this.key = key; + this.encrypted = encrypted; + this.symmetricKey = symmetricKey; } } diff --git a/src/database/migrations/CreateResourceTable.ts b/src/database/migrations/CreateResourceTable.ts index e22c16c1..c11f38dc 100644 --- a/src/database/migrations/CreateResourceTable.ts +++ b/src/database/migrations/CreateResourceTable.ts @@ -18,7 +18,7 @@ export class CreateResourceTable1695740345977 implements MigrationInterface { { name: 'symmetricKey', type: 'text', isNullable: true }, { name: 'createdAt', type: 'timestamptz', isNullable: false }, - { name: 'updatedAt', type: 'timestamptz', isNullable: false }, + { name: 'updatedAt', type: 'timestamptz', isNullable: true }, ], }); await queryRunner.createTable(table, true); diff --git a/src/database/migrations/MigrateData.ts b/src/database/migrations/MigrateData.ts index 69d3d875..3bf0f403 100644 --- a/src/database/migrations/MigrateData.ts +++ b/src/database/migrations/MigrateData.ts @@ -143,7 +143,9 @@ export class MigrateData1695740345977 implements MigrationInterface { for (const kid of oldCustomer.kids) { console.info(`Updating key with kid ${kid}`); const _key = await queryRunner.query( - `UPDATE "key" SET "customerId" = '${customerEntity.customerId}', "createdAt" = '${customerEntity.createdAt.toISOString()}' WHERE kid = '${kid}'` + `UPDATE "key" SET "customerId" = '${ + customerEntity.customerId + }', "createdAt" = '${customerEntity.createdAt.toISOString()}' WHERE kid = '${kid}'` ); if (!_key) { throw new Error(`Failed to update key with kid ${kid}`); diff --git a/src/services/identity/abstract.ts b/src/services/identity/abstract.ts index 96aafce8..51d4233d 100644 --- a/src/services/identity/abstract.ts +++ b/src/services/identity/abstract.ts @@ -29,7 +29,9 @@ import type { CreateUnencryptedStatusListOptions, CredentialRequest, FeePaymentOptions, + ITrackOperation, StatusOptions, + TrackResult, UpdateEncryptedStatusListOptions, UpdateUnencryptedStatusListOptions, VeramoAgent, @@ -142,24 +144,30 @@ export abstract class AbstractIdentityService implements IIdentityService { ): Promise { throw new Error(`Not supported`); } + trackOperation(trackOperation: ITrackOperation): Promise { + throw new Error(`Not supported`); + } revokeCredentials( credential: VerifiableCredential | VerifiableCredential[], publish: boolean, - customer: CustomerEntity + customer: CustomerEntity, + symmetricKey: string ): Promise { throw new Error(`Not supported`); } suspendCredentials( credential: VerifiableCredential | VerifiableCredential[], publish: boolean, - customer: CustomerEntity + customer: CustomerEntity, + symmetricKey: string ): Promise { throw new Error(`Not supported`); } reinstateCredentials( credential: VerifiableCredential | VerifiableCredential[], publish: boolean, - customer: CustomerEntity + customer: CustomerEntity, + symmetricKey: string ): Promise { throw new Error(`Not supported`); } diff --git a/src/services/identity/agent.ts b/src/services/identity/agent.ts index a961cb83..338e565a 100644 --- a/src/services/identity/agent.ts +++ b/src/services/identity/agent.ts @@ -516,7 +516,8 @@ export class Veramo { async revokeCredentials( agent: VeramoAgent, credentials: VerifiableCredential | VerifiableCredential[], - publish = true + publish = true, + symmetricKey = '' ) { if (Array.isArray(credentials)) return await agent.cheqdRevokeCredentials({ @@ -524,27 +525,50 @@ export class Veramo { fetchList: true, publish: true, } satisfies ICheqdRevokeBulkCredentialsWithStatusList2021Args); - return await agent.cheqdRevokeCredential({ credential: credentials, fetchList: true, publish }); + return await agent.cheqdRevokeCredential({ + credential: credentials, + fetchList: true, + publish, + symmetricKey, + returnStatusListMetadata: true, + returnUpdatedStatusList: true, + }); } async suspendCredentials( agent: VeramoAgent, credentials: VerifiableCredential | VerifiableCredential[], - publish = true + publish = true, + symmetricKey = '' ) { if (Array.isArray(credentials)) return await agent.cheqdSuspendCredentials({ credentials, fetchList: true, publish }); - return await agent.cheqdSuspendCredential({ credential: credentials, fetchList: true, publish }); + return await agent.cheqdSuspendCredential({ + credential: credentials, + fetchList: true, + publish, + symmetricKey, + returnStatusListMetadata: true, + returnUpdatedStatusList: true, + }); } async unsuspendCredentials( agent: VeramoAgent, credentials: VerifiableCredential | VerifiableCredential[], - publish = true + publish = true, + symmetricKey = '' ) { if (Array.isArray(credentials)) return await agent.cheqdUnsuspendCredentials({ credentials, fetchList: true, publish }); - return await agent.cheqdUnsuspendCredential({ credential: credentials, fetchList: true, publish }); + return await agent.cheqdUnsuspendCredential({ + credential: credentials, + fetchList: true, + publish, + symmetricKey, + returnStatusListMetadata: true, + returnUpdatedStatusList: true, + }); } async updateUnencryptedStatusList2021( diff --git a/src/services/identity/index.ts b/src/services/identity/index.ts index 83d0cec6..92499703 100644 --- a/src/services/identity/index.ts +++ b/src/services/identity/index.ts @@ -34,8 +34,10 @@ import type { CreateUnencryptedStatusListOptions, CredentialRequest, FeePaymentOptions, + ITrackOperation, SearchStatusListResult, StatusOptions, + TrackResult, UpdateEncryptedStatusListOptions, UpdateUnencryptedStatusListOptions, VeramoAgent, @@ -93,6 +95,7 @@ export interface IIdentityService { statusOptions: CreateEncryptedStatusListOptions, customer: CustomerEntity ): Promise; + trackOperation(trackOperation: ITrackOperation): Promise; updateUnencryptedStatusList2021( did: string, statusOptions: UpdateUnencryptedStatusListOptions, @@ -127,17 +130,20 @@ export interface IIdentityService { revokeCredentials( credential: VerifiableCredential | VerifiableCredential[], publish: boolean, - customer: CustomerEntity + customer: CustomerEntity, + symmetricKey: string ): Promise; suspendCredentials( credential: VerifiableCredential | VerifiableCredential[], publish: boolean, - customer: CustomerEntity + customer: CustomerEntity, + symmetricKey: string ): Promise; reinstateCredentials( credential: VerifiableCredential | VerifiableCredential[], publish: boolean, - customer: CustomerEntity + customer: CustomerEntity, + symmetricKey: string ): Promise; } diff --git a/src/services/identity/postgres.ts b/src/services/identity/postgres.ts index 7742e083..163d5b9b 100644 --- a/src/services/identity/postgres.ts +++ b/src/services/identity/postgres.ts @@ -19,7 +19,7 @@ import { type CreateStatusList2021Result, type StatusCheckResult, DefaultRPCUrls, - type TransactionResult + type TransactionResult, } from '@cheqd/did-provider-cheqd'; import { BroadcastStatusListOptions, @@ -34,6 +34,8 @@ import { CreateEncryptedStatusListOptions, FeePaymentOptions, UpdateEncryptedStatusListOptions, + TrackResult, + ITrackOperation, } from '../../types/shared.js'; import { Connection } from '../../database/connection/connection.js'; import type { CustomerEntity } from '../../database/entities/customer.entity.js'; @@ -45,6 +47,12 @@ import { PaymentAccountService } from '../payment_account.js'; import { CheqdNetwork } from '@cheqd/sdk'; import { IdentifierService } from '../identifier.js'; import type { KeyEntity } from '../../database/entities/key.entity.js'; +import { ResourceService } from '../resource.js'; +import { + OPERATION_CATEGORY_NAME_CREDENTIAL, + OPERATION_CATEGORY_NAME_CREDENTIAL_STATUS, + OPERATION_CATEGORY_NAME_RESOURCE, +} from '../../types/constants.js'; dotenv.config(); @@ -408,31 +416,34 @@ export class PostgresIdentityService extends DefaultIdentityService { async revokeCredentials( credentials: VerifiableCredential | VerifiableCredential[], publish: boolean, - customer: CustomerEntity + customer: CustomerEntity, + symmetricKey: string ) { const agent = await this.createAgent(customer); await this.validateCredentialAccess(credentials, customer); - return await Veramo.instance.revokeCredentials(agent, credentials, publish); + return await Veramo.instance.revokeCredentials(agent, credentials, publish, symmetricKey); } async suspendCredentials( credentials: VerifiableCredential | VerifiableCredential[], publish: boolean, - customer: CustomerEntity + customer: CustomerEntity, + symmetricKey: string ) { const agent = await this.createAgent(customer); await this.validateCredentialAccess(credentials, customer); - return await Veramo.instance.suspendCredentials(agent, credentials, publish); + return await Veramo.instance.suspendCredentials(agent, credentials, publish, symmetricKey); } async reinstateCredentials( credentials: VerifiableCredential | VerifiableCredential[], publish: boolean, - customer: CustomerEntity + customer: CustomerEntity, + symmetricKey: string ) { const agent = await this.createAgent(customer); await this.validateCredentialAccess(credentials, customer); - return await Veramo.instance.unsuspendCredentials(agent, credentials, publish); + return await Veramo.instance.unsuspendCredentials(agent, credentials, publish, symmetricKey); } private async validateCredentialAccess( @@ -458,4 +469,61 @@ export class PostgresIdentityService extends DefaultIdentityService { } } } + + async trackOperation(trackOperation: ITrackOperation): Promise { + // For now it tracks only resource-related operations but in future we will track all other actions + switch (trackOperation.category) { + case OPERATION_CATEGORY_NAME_RESOURCE: + return await this.trackResourceOperation(trackOperation); + case OPERATION_CATEGORY_NAME_CREDENTIAL_STATUS: + return await this.trackResourceOperation(trackOperation); + case OPERATION_CATEGORY_NAME_CREDENTIAL: + return await this.trackResourceOperation(trackOperation); + default: { + return { + created: false, + error: `Category ${trackOperation.category} is not supported`, + }; + } + } + } + + async trackResourceOperation(trackOperation: ITrackOperation): Promise { + const customer = trackOperation.customer; + const did = trackOperation.did; + const resource = trackOperation.data.resource; + const encrypted = trackOperation.data.encrypted; + const symmetricKey = trackOperation.data.symmetricKey; + + const identifier = await IdentifierService.instance.get(did); + if (!identifier) { + throw new Error(`Identifier ${did} not found`); + } + if (!identifier.controllerKeyId) { + throw new Error(`Identifier ${did} does not have link to the controller key...`); + } + const key = await KeyService.instance.get(identifier.controllerKeyId); + if (!key) { + throw new Error(`Key for ${did} not found`); + } + + const resourceEntity = await ResourceService.instance.createFromLinkedResource( + resource, + customer, + key, + identifier, + encrypted, + symmetricKey + ); + if (!resourceEntity) { + return { + created: false, + error: `Resource for ${did} was not tracked`, + }; + } + return { + created: true, + error: '', + }; + } } diff --git a/src/services/key.ts b/src/services/key.ts index c4c0b941..797425f6 100644 --- a/src/services/key.ts +++ b/src/services/key.ts @@ -17,7 +17,7 @@ export class KeyService { this.keyRepository = Connection.instance.dbConnection.getRepository(KeyEntity); } - public async update(kid: string, customer?: CustomerEntity, keyAlias?: string, createdAt? : Date) { + public async update(kid: string, customer?: CustomerEntity, keyAlias?: string, createdAt?: Date) { const existingKey = await this.keyRepository.findOneBy({ kid }); if (!existingKey) { throw new Error(`kid not found`); diff --git a/src/services/operation.ts b/src/services/operation.ts new file mode 100644 index 00000000..0ed4e0b6 --- /dev/null +++ b/src/services/operation.ts @@ -0,0 +1,93 @@ +import type { Repository } from 'typeorm'; + +import { Connection } from '../database/connection/connection.js'; + +import * as dotenv from 'dotenv'; +import { PaymentAccountEntity } from '../database/entities/payment.account.entity.js'; +import type { CustomerEntity } from '../database/entities/customer.entity.js'; +import type { KeyEntity } from '../database/entities/key.entity.js'; +import { getCosmosAccount } from '@cheqd/sdk'; +dotenv.config(); + +export class PaymentAccountService { + public paymentAccountRepository: Repository; + + public static instance = new PaymentAccountService(); + + constructor() { + this.paymentAccountRepository = Connection.instance.dbConnection.getRepository(PaymentAccountEntity); + } + + public async create( + namespace: string, + isDefault: boolean, + customer: CustomerEntity, + key: KeyEntity + ): Promise { + const address = getCosmosAccount(key.kid); + const existing = await this.find({ address: address }); + if (!address) { + throw new Error('Account address is not specified'); + } + if (existing.length > 0) { + throw new Error( + `Cannot create a new payment account since the payment account with same address ${address} already exists` + ); + } + if (!namespace) { + throw new Error('Account namespace is not specified'); + } + if (!customer) { + throw new Error('Customer id is not specified'); + } + if (!key) { + throw new Error('Key id is not specified'); + } + const paymentAccount = new PaymentAccountEntity(address, namespace, isDefault, customer, key); + const paymentAccountEntity = (await this.paymentAccountRepository.insert(paymentAccount)).identifiers[0]; + if (!paymentAccountEntity) throw new Error(`Cannot create a new payment account`); + + return paymentAccount; + } + + public async update( + address: string, + namespace?: string, + isDefault?: boolean, + customer?: CustomerEntity, + key?: KeyEntity + ) { + const existingPaymentAccount = await this.paymentAccountRepository.findOneBy({ address }); + if (!existingPaymentAccount) { + throw new Error(`address not found`); + } + if (customer) { + existingPaymentAccount.customer = customer; + } + if (namespace) { + existingPaymentAccount.namespace = namespace; + } + if (key) { + existingPaymentAccount.key = key; + } + if (isDefault) { + existingPaymentAccount.isDefault = isDefault; + } + + return await this.paymentAccountRepository.save(existingPaymentAccount); + } + + public async get(address: string) { + return await this.paymentAccountRepository.findOne({ + where: { address }, + relations: ['customer', 'key'], + }); + } + + public async find(where: Record) { + return await this.paymentAccountRepository.find({ + where: where, + relations: ['customer', 'key'], + }); + } +} diff --git a/src/services/resource.ts b/src/services/resource.ts new file mode 100644 index 00000000..0908801a --- /dev/null +++ b/src/services/resource.ts @@ -0,0 +1,122 @@ +import type { Repository } from 'typeorm'; + +import { Connection } from '../database/connection/connection.js'; + +import * as dotenv from 'dotenv'; +import { ResourceEntity } from '../database/entities/resource.entity.js'; +import type { IdentifierEntity } from '../database/entities/identifier.entity.js'; +import type { KeyEntity } from '../database/entities/key.entity.js'; +import type { CustomerEntity } from '../database/entities/customer.entity.js'; +import type { LinkedResourceMetadataResolutionResult } from '@cheqd/did-provider-cheqd'; +dotenv.config(); + +export class ResourceService { + public resourceRepository: Repository; + + public static instance = new ResourceService(); + + constructor() { + this.resourceRepository = Connection.instance.dbConnection.getRepository(ResourceEntity); + } + + public async create( + resourceId: string, + resourceName: string, + resourceType: string, + mediaType: string, + previousVersionId: string, + nextVersionId: string, + customer: CustomerEntity, + identifier: IdentifierEntity, + key: KeyEntity, + encrypted: boolean, + symmetricKey: string + ): Promise { + const resourceEntity = new ResourceEntity( + resourceId, + resourceName, + resourceType, + mediaType, + previousVersionId, + nextVersionId, + customer, + identifier, + key, + encrypted, + symmetricKey + ); + const resource = (await this.resourceRepository.insert(resourceEntity)).identifiers[0]; + if (!resource) throw new Error(`Cannot create a new resource`); + return resourceEntity; + } + + public async update( + resourceId: string, + identifier?: IdentifierEntity, + key?: KeyEntity, + resourceName?: string, + resourceType?: string, + mediaType?: string, + previousVersionId?: string, + nextVersionId?: string, + customer?: CustomerEntity, + encrypted?: boolean, + symmetricKey?: string + ) { + const existingResource = await this.resourceRepository.findOneBy({ resourceId }); + if (!existingResource) { + throw new Error(`Resource with id: ${resourceId} not found`); + } + if (identifier) existingResource.identifier = identifier; + if (key) existingResource.key = key; + if (resourceName) existingResource.resourceName = resourceName; + if (resourceType) existingResource.resourceType = resourceType; + if (mediaType) existingResource.mediaType = mediaType; + if (previousVersionId) existingResource.previousVersionId = previousVersionId; + if (nextVersionId) existingResource.nextVersionId = nextVersionId; + if (customer) existingResource.customer = customer; + if (encrypted) existingResource.encrypted = encrypted; + if (symmetricKey) existingResource.symmetricKey = symmetricKey; + return await this.resourceRepository.save(existingResource); + } + + public async get(resourceId: string) { + return await this.resourceRepository.findOne({ + where: { resourceId }, + relations: ['customer', 'key', 'identifier'], + }); + } + + public async find(where: Record) { + return await this.resourceRepository.find({ + where: where, + relations: ['customer', 'key', 'identifier'], + }); + } + + public async createFromLinkedResource( + resource: LinkedResourceMetadataResolutionResult, + customer: CustomerEntity, + key: KeyEntity, + identifier: IdentifierEntity, + encrypted: boolean, + symmetricKey: string + ) { + const resourceEntity = new ResourceEntity( + resource.resourceId, + resource.resourceName, + resource.resourceType, + resource.mediaType, + resource.previousVersionId, + resource.nextVersionId, + customer, + identifier, + key, + encrypted, + symmetricKey + ); + const result = (await this.resourceRepository.insert(resourceEntity)).identifiers[0]; + if (!result) throw new Error(`Cannot create a new resource`); + return resourceEntity; + } +} diff --git a/src/static/swagger.json b/src/static/swagger.json index e815678e..c9dfc995 100644 --- a/src/static/swagger.json +++ b/src/static/swagger.json @@ -1636,6 +1636,10 @@ "type": "string" } ] + }, + "symmetricKey": { + "description": "The symmetric key used to encrypt the StatusList2021 DID-Linked Resource. Required if the StatusList2021 DID-Linked Resource is encrypted.", + "type": "string" } } }, diff --git a/src/types/constants.ts b/src/types/constants.ts index 7db9e775..5fdb6e67 100644 --- a/src/types/constants.ts +++ b/src/types/constants.ts @@ -62,3 +62,7 @@ export const POLYGON_RPC_URL = 'https://rpc-mumbai.maticvigil.com'; export const VERIDA_APP_NAME = 'Cheqd Verida Connector'; // Schema to store a Verifiable Credential on the Verida Network. export const VERIDA_CREDENTIAL_RECORD_SCHEMA = 'https://common.schemas.verida.io/credential/base/v0.2.0/schema.json'; + +export const OPERATION_CATEGORY_NAME_RESOURCE = 'resource'; +export const OPERATION_CATEGORY_NAME_CREDENTIAL_STATUS = 'credential-status'; +export const OPERATION_CATEGORY_NAME_CREDENTIAL = 'credential'; diff --git a/src/types/shared.ts b/src/types/shared.ts index 0858128d..d16e476d 100644 --- a/src/types/shared.ts +++ b/src/types/shared.ts @@ -32,6 +32,8 @@ import type { DataSource } from 'typeorm'; import { CheqdNetwork, MethodSpecificIdAlgo, Service, VerificationMethods } from '@cheqd/sdk'; import type { AlternativeUri } from '@cheqd/ts-proto/cheqd/resource/v2'; import type { DIDDocument } from 'did-resolver'; +import type { CustomerEntity } from '../database/entities/customer.entity'; +import type { UserEntity } from '../database/entities/user.entity'; const DefaultUuidPattern = '([a-zA-Z0-9-]{36})'; const DefaultMethodSpecificIdPattern = `(?:[a-zA-Z0-9]{21,22}|${DefaultUuidPattern})`; @@ -345,19 +347,6 @@ export type FeePaymentOptions = { memo?: string; }; -export interface ResourceMetadata { - collectionId: string; - resourceId: string; - resourceName: string; - resourceVersion: string; - resourceType: string; - mediaType: string; - created?: Date; - checksum: string; - previousVersionId: string; - nextVersionId: string; -} - export type CheckStatusListOptions = Omit; export interface VerificationOptions { @@ -366,3 +355,39 @@ export interface VerificationOptions { domain?: string; verifyStatus?: boolean; } + +export type TrackData = IResourceTrack; + +export interface ITrackOperation { + // function name, e.g. createDid, issueCredential, etc. + operation: string; + // category of the operation, e.g. did, resource, credential, credential-status + category: string; + // data of the operation, e.g. did, resource, credentialStatus + data: TrackData; + // customer who initiated the operation (like organistation) + customer: CustomerEntity; + // user who initiated the operation + user?: UserEntity; + // identifier + did?: string; + // controller's key + key?: string; + // fee payment options + feePaymentOptions?: { + feePaymentAddress: string; + feePaymentAmount: number; + feePaymentNetwork: CheqdNetwork; + }; +} + +export interface IResourceTrack { + resource: LinkedResourceMetadataResolutionResult; + encrypted: boolean; + symmetricKey: string; +} + +export interface TrackResult { + created: boolean; + error?: string; +} diff --git a/src/types/swagger-types.ts b/src/types/swagger-types.ts index 99c62308..3a1dc95f 100644 --- a/src/types/swagger-types.ts +++ b/src/types/swagger-types.ts @@ -208,6 +208,9 @@ * oneOf: * - type: object * - type: string + * symmetricKey: + * description: The symmetric key used to encrypt the StatusList2021 DID-Linked Resource. Required if the StatusList2021 DID-Linked Resource is encrypted. + * type: string * RevocationResult: * properties: * revoked: