diff --git a/apps/ssi-abstraction/package.json b/apps/ssi-abstraction/package.json index f7770f308e79f6b5d2bcdaa9e11656d3302695a0..3bbea7c653d57e8e8dcf25a8ce994dcef3fcd7c9 100644 --- a/apps/ssi-abstraction/package.json +++ b/apps/ssi-abstraction/package.json @@ -34,8 +34,8 @@ "@ocm/shared": "workspace:*", "axios": "^1.6.2", "express": "^4.17.3", - "form-data": "^4.0.0", "joi": "^17.6.0", + "minio": "^7.1.3", "nats": "^2.18.0", "rxjs": "^7.2.0", "winston": "^3.11.0" diff --git a/apps/ssi-abstraction/src/agent/revocation/TailsFileService.ts b/apps/ssi-abstraction/src/agent/revocation/TailsFileService.ts index 9938242e0ca4c19d0098762a6f03c14f7bd92ce1..2c3dbb831c33aee773c713f35f54d0d8bd6a8b37 100644 --- a/apps/ssi-abstraction/src/agent/revocation/TailsFileService.ts +++ b/apps/ssi-abstraction/src/agent/revocation/TailsFileService.ts @@ -2,52 +2,8 @@ import type { AnonCredsRevocationRegistryDefinition } from '@credo-ts/anoncreds' import type { AgentContext } from '@credo-ts/core'; import { BasicTailsFileService } from '@credo-ts/anoncreds'; -import FormData from 'form-data'; import fs from 'fs'; - -export type UploadToS3Options = { - s3Url: string; - bucketName: string; - fileId: string; - content: Uint8Array; - accessKey: string; - secret: string; -}; - -// Upload to S3 and return the URL to fetch it from -export const uploadToS3 = async ({ - s3Url, - content, - fileId, - bucketName, - accessKey, - secret, -}: UploadToS3Options) => { - // TODO: double check all headers - const headers = new Headers(); - headers.set('Host', s3Url); - headers.set('Date', generateRfc1123Date()); - headers.set('Content-Type', 'application/octet-stream'); - headers.set('Authorization', accessKey); - headers.set('Secret', secret); - - const sanitizedUrl = s3Url.endsWith('/') - ? s3Url.slice(0, s3Url.length - 1) - : s3Url; - - const url = `${sanitizedUrl}/${bucketName}/${fileId}`; - - // TODO: check whether we need to include the sig or not - const result = await axios.put(url, content, { - headers, - }); - - if (result.status > 299) { - throw new Error(`Error uploading to S3. Error: ${JSON.stringify(result)}`); - } - - return url; -}; +import { Client } from 'minio'; export class S3TailsFileService extends BasicTailsFileService { private tailsServerBaseUrl: string; @@ -79,64 +35,35 @@ export class S3TailsFileService extends BasicTailsFileService { revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition; }, ) { - const headers = this.prepareS3Headers( - this.tailsServerBaseUrl, - this.s3Secrets.s3AccessKey, - this.s3Secrets.s3Secret, - ); + const url = new URL(this.tailsServerBaseUrl); + const useSSL = url.protocol === 'https:'; + const [endPoint, port] = url.host.split(':'); + + const client = new Client({ + endPoint, + port: port ? Number(port) : undefined, + useSSL, + accessKey: this.s3Secrets.s3AccessKey, + secretKey: this.s3Secrets.s3Secret, + }); + const revocationRegistryDefinition = options.revocationRegistryDefinition; const localTailsFilePath = revocationRegistryDefinition.value.tailsLocation; const pathParts = localTailsFilePath.split('/'); const tailsFileId = pathParts[pathParts.length - 1]; - const data = new FormData(); const readStream = fs.createReadStream(localTailsFilePath); - data.append('file', readStream); const tailsFileUrl = `${this.tailsServerBaseUrl}/${this.tailsServerBucketName}/${tailsFileId}`; - const response = await agentContext.config.agentDependencies.fetch( - tailsFileUrl, - { - method: 'PUT', - body: data, - headers, - }, + agentContext.config.logger.debug( + `Uploading tails file to: ${tailsFileUrl}`, ); - if (response.status !== 200) { - throw new Error('Cannot upload tails file'); - } + await client.putObject(this.tailsServerBucketName, tailsFileId, readStream); return { tailsFileUrl, }; } - - private prepareS3Headers(url: string, accessKey: string, secret: string) { - const rfc1123Date = - new Date() - .toLocaleString('en-GB', { - timeZone: 'UTC', - hour12: false, - weekday: 'short', - year: 'numeric', - month: 'short', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - }) - .replace(/(?:(\d),)/, '$1') + ' GMT'; - - // TODO: double check all headers - const headers = new Headers(); - headers.set('Host', url); - headers.set('Date', rfc1123Date); - headers.set('Content-Type', 'application/octet-stream'); - headers.set('Authorization', accessKey); - headers.set('Secret', secret); - - return headers; - } } diff --git a/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts b/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts index fdb409391071acaee0554ee8ff53505cf589359c..18e219098fdcdd2ab954d67904608df8fe193abb 100644 --- a/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts +++ b/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts @@ -9,12 +9,12 @@ const mockConfig = (port: number = 3001, withLedger = false): AppConfig => ({ agentHost: '', port: 3000, s3: { - secret: 'some-secret', - accessKey: 'some-access-key', + secret: 'very-long-secret-key', + accessKey: 'ssi-abstraction', }, tailsServer: { - baseUrl: 'http://localhost:8080', - bucketName: 'tails', + baseUrl: 'http://localhost:9000', + bucketName: 'ssi', }, jwtSecret: '', nats: { diff --git a/docker-compose.yml b/docker-compose.yml index 4fd70a2f301fd0d9e32a8548769498da073ec7f3..6422563cd76462d503bb270acc6dc38737663c28 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,13 +6,17 @@ services: ports: - '4222:4222' #Nats server port - '8222:8222' #Nats server Monitoring port - command: [ - "--config", "nats-server.conf", - "--debug", - "--trace", - "--user", "nats_user", - "--pass", "Rw+dYIymAQm9H6ELLNwSuGo1812jqQ==" - ] + command: + [ + '--config', + 'nats-server.conf', + '--debug', + '--trace', + '--user', + 'nats_user', + '--pass', + 'Rw+dYIymAQm9H6ELLNwSuGo1812jqQ==', + ] s3: image: minio/minio @@ -22,7 +26,7 @@ services: environment: MINIO_ROOT_USER: minio MINIO_ROOT_PASSWORD: minio123 - command: ["server", "/data", "--console-address", ":9001"] + command: ['server', '/data', '--console-address', ':9001'] volumes: - /data @@ -33,6 +37,8 @@ services: /usr/bin/mc config host add ssi-s3 http://s3:9000 minio minio123; /usr/bin/mc mb --ignore-existing ssi-s3/ssi; /usr/bin/mc anonymous set download ssi-s3/ssi; + /usr/bin/mc admin user add ssi-s3 ssi-abstraction very-long-secret-key; + /usr/bin/mc admin policy attach ssi-s3 readwrite --user=ssi-abstraction; exit 0; " depends_on: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93bc4abab406c98c62000d7cbcaed1d892f15513..84ed5d7c81df6e198a90e030b08b9b2dfe2af5c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -583,12 +583,12 @@ importers: express: specifier: ^4.17.3 version: 4.18.2 - form-data: - specifier: ^4.0.0 - version: 4.0.0 joi: specifier: ^17.6.0 version: 17.11.1 + minio: + specifier: ^7.1.3 + version: 7.1.3 nats: specifier: ^2.18.0 version: 2.19.0 @@ -5847,6 +5847,12 @@ packages: resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} dev: true + /@zxing/text-encoding@0.9.0: + resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} + requiresBuild: true + dev: false + optional: true + /JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -6497,6 +6503,12 @@ packages: inherits: 2.0.4 readable-stream: 3.6.2 + /block-stream2@2.1.0: + resolution: {integrity: sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==} + dependencies: + readable-stream: 3.6.2 + dev: false + /blueimp-md5@2.19.0: resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} dev: false @@ -6612,6 +6624,10 @@ packages: dependencies: fill-range: 7.0.1 + /browser-or-node@2.1.1: + resolution: {integrity: sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==} + dev: false + /browserslist@4.22.2: resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -6647,6 +6663,10 @@ packages: dev: false optional: true + /buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: false + /buffer-fill@1.0.0: resolution: {integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==} dev: false @@ -8556,7 +8576,6 @@ packages: dependencies: strnum: 1.0.5 dev: false - optional: true /fastq@1.16.0: resolution: {integrity: sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==} @@ -9584,6 +9603,19 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + /ipaddr.js@2.1.0: + resolution: {integrity: sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==} + engines: {node: '>= 10'} + dev: false + + /is-arguments@1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + has-tostringtag: 1.0.0 + dev: false + /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} dependencies: @@ -9687,6 +9719,13 @@ packages: engines: {node: '>=6'} dev: true + /is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: false + /is-glob@2.0.1: resolution: {integrity: sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==} engines: {node: '>=0.10.0'} @@ -10497,6 +10536,10 @@ packages: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true + /json-stream@1.0.0: + resolution: {integrity: sha512-H/ZGY0nIAg3QcOwE1QN/rK/Fa7gJn7Ii5obwp6zyPO4xiPNwpIMjqy2gwjBEGqzkF/vSWEIBQCBuN19hYiL6Qg==} + dev: false + /json-text-sequence@0.3.0: resolution: {integrity: sha512-7khKIYPKwXQem4lWXfpIN/FEnhztCeRPSxH4qm3fVlqulwujrRDD54xAwDDn/qVKpFtV550+QAkcWJcufzqQuA==} engines: {node: '>=10.18.0'} @@ -11482,6 +11525,26 @@ packages: /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + /minio@7.1.3: + resolution: {integrity: sha512-xPrLjWkTT5E7H7VnzOjF//xBp9I40jYB4aWhb2xTFopXXfw+Wo82DDWngdUju7Doy3Wk7R8C4LAgwhLHHnf0wA==} + engines: {node: ^16 || ^18 || >=20} + dependencies: + async: 3.2.5 + block-stream2: 2.1.0 + browser-or-node: 2.1.1 + buffer-crc32: 0.2.13 + fast-xml-parser: 4.3.3 + ipaddr.js: 2.1.0 + json-stream: 1.0.0 + lodash: 4.17.21 + mime-types: 2.1.35 + query-string: 7.1.3 + through2: 4.0.2 + web-encoding: 1.1.5 + xml: 1.0.1 + xml2js: 0.5.0 + dev: false + /minipass-collect@1.0.2: resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} engines: {node: '>= 8'} @@ -13091,7 +13154,6 @@ packages: /sax@1.3.0: resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} dev: false - optional: true /scheduler@0.24.0-canary-efb381bbf-20230505: resolution: {integrity: sha512-ABvovCDe/k9IluqSh4/ISoq8tIJnW8euVAWYt5j/bg6dRnqwQwiGO1F/V4AyK96NGF/FB04FhOUDuWj8IKfABA==} @@ -13663,7 +13725,6 @@ packages: /strnum@1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} dev: false - optional: true /strtok3@7.0.0: resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==} @@ -13964,7 +14025,6 @@ packages: resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} dependencies: readable-stream: 3.6.2 - dev: true /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -14427,6 +14487,16 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + /util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + dependencies: + inherits: 2.0.4 + is-arguments: 1.1.1 + is-generator-function: 1.0.10 + is-typed-array: 1.1.12 + which-typed-array: 1.1.13 + dev: false + /utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -14593,6 +14663,14 @@ packages: - encoding dev: false + /web-encoding@1.1.5: + resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==} + dependencies: + util: 0.12.5 + optionalDependencies: + '@zxing/text-encoding': 0.9.0 + dev: false + /web-streams-polyfill@3.3.2: resolution: {integrity: sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==} engines: {node: '>= 8'} @@ -14906,6 +14984,14 @@ packages: dev: false optional: true + /xml2js@0.5.0: + resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} + engines: {node: '>=4.0.0'} + dependencies: + sax: 1.3.0 + xmlbuilder: 11.0.1 + dev: false + /xml2js@0.6.0: resolution: {integrity: sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==} engines: {node: '>=4.0.0'} @@ -14915,11 +15001,14 @@ packages: dev: false optional: true + /xml@1.0.1: + resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} + dev: false + /xmlbuilder@11.0.1: resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} engines: {node: '>=4.0'} dev: false - optional: true /xmlbuilder@14.0.0: resolution: {integrity: sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==}