From b65b7ec7aa76737b15e759377bdd7ce81e173907 Mon Sep 17 00:00:00 2001 From: Jagpreet Singh Sasan Date: Mon, 17 May 2021 15:40:07 +0530 Subject: [PATCH] feat(aws-sm): added keychain plugin for aws secret manager Primary Change --- 1. Added new package cactus-plugin-keychain-aws-sm under packages/ 2. Added LocalStack under cactus-test-tooling/src/main/typescript for local aws secret manager deployment and testing Refactorings that were also necessary to incorporate 1) and 2) --- 3. Updated public-api.ts under packages/cactus-test-tooling/src/main/typescript for exporting the Localstack class, its interface and constants Resolves #912 Signed-off-by: Jagpreet Singh Sasan --- .../cactus-plugin-keychain-aws-sm/README.md | 3 + .../package-lock.json | 478 ++++++++++++++++++ .../package.json | 102 ++++ .../src/main/json/openapi.json | 89 ++++ .../.openapi-generator-ignore | 27 + .../typescript-axios/.openapi-generator/FILES | 4 + .../.openapi-generator/VERSION | 1 + .../generated/openapi/typescript-axios/api.ts | 284 +++++++++++ .../openapi/typescript-axios/base.ts | 71 +++ .../openapi/typescript-axios/configuration.ts | 76 +++ .../openapi/typescript-axios/index.ts | 18 + .../src/main/typescript/index.ts | 1 + .../src/main/typescript/index.web.ts | 1 + .../typescript/plugin-factory-keychain.ts | 49 ++ .../plugin-keychain-aws-sm-remote-adapter.ts | 154 ++++++ .../main/typescript/plugin-keychain-aws-sm.ts | 279 ++++++++++ .../src/main/typescript/public-api.ts | 18 + .../get-keychain-entry-endpoint-v1.ts | 76 +++ .../set-keychain-entry-endpoint-v1.ts | 76 +++ .../src/test/typescript/integration/a.ts | 15 + .../plugin-keychain-aws-sm.test.ts | 143 ++++++ .../src/test/typescript/unit/api-surface.ts | 7 + .../tsconfig.json | 10 + .../main/typescript/localstack/localstack.ts | 152 ++++++ .../src/main/typescript/public-api.ts | 8 + 25 files changed, 2142 insertions(+) create mode 100644 packages/cactus-plugin-keychain-aws-sm/README.md create mode 100644 packages/cactus-plugin-keychain-aws-sm/package-lock.json create mode 100644 packages/cactus-plugin-keychain-aws-sm/package.json create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/json/openapi.json create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/api.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/base.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/configuration.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/index.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/index.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/index.web.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/plugin-factory-keychain.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/plugin-keychain-aws-sm-remote-adapter.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/plugin-keychain-aws-sm.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/public-api.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/webservices/get-keychain-entry-endpoint-v1.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/main/typescript/webservices/set-keychain-entry-endpoint-v1.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/a.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/plugin-keychain-aws-sm.test.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/src/test/typescript/unit/api-surface.ts create mode 100644 packages/cactus-plugin-keychain-aws-sm/tsconfig.json create mode 100644 packages/cactus-test-tooling/src/main/typescript/localstack/localstack.ts diff --git a/packages/cactus-plugin-keychain-aws-sm/README.md b/packages/cactus-plugin-keychain-aws-sm/README.md new file mode 100644 index 00000000000..b04e984095d --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/README.md @@ -0,0 +1,3 @@ +# `@hyperledger/cactus-plugin-keychain-aws-sm` + +## TO-DO \ No newline at end of file diff --git a/packages/cactus-plugin-keychain-aws-sm/package-lock.json b/packages/cactus-plugin-keychain-aws-sm/package-lock.json new file mode 100644 index 00000000000..c09190ec58c --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/package-lock.json @@ -0,0 +1,478 @@ +{ + "name": "@hyperledger/cactus-plugin-keychain-aws-sm", + "version": "0.4.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true + }, + "@types/connect": { + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", + "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/express": { + "version": "4.17.8", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.8.tgz", + "integrity": "sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.17.tgz", + "integrity": "sha512-YYlVaCni5dnHc+bLZfY908IG1+x5xuibKZMGv8srKkvtul3wUuanYvpIj9GXXoWkQbaAdR+kgX46IETKUALWNQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "@types/node": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz", + "integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==", + "dev": true + }, + "@types/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "@types/request": { + "version": "2.48.5", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", + "integrity": "sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==", + "dev": true, + "requires": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/serve-static": { + "version": "1.13.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", + "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sdk": { + "version": "2.903.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.903.0.tgz", + "integrity": "sha512-BP/giYLP8QJ63Jta59kph1F76oPITxRt/wNr3BdoEs9BtshWlGKk149UaseDB4wJtI+0TER5jtzBIUBcP6E+wA==", + "requires": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.3.2", + "xml2js": "0.4.19" + }, + "dependencies": { + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "execa": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", + "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + } + } + }, + "follow-redirects": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.0.tgz", + "integrity": "sha512-0vRwd7RKQBTt+mgu87mtYeofLFZpTas2S9zY+jIeuLJMNvudIgF52nr19q40HOwH5RrhWIPuj9puybzSJiRrVg==" + }, + "http-status-codes": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.1.4.tgz", + "integrity": "sha512-MZVIsLKGVOVE1KEnldppe6Ij+vmemMuApDfjhVSLzyYP+td0bREEYyAoIw9yFePoBXManCuBqmiNP5FqJS5Xkg==" + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "internal-ip": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", + "integrity": "sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==", + "dev": true, + "requires": { + "default-gateway": "^6.0.0", + "ipaddr.js": "^1.9.1", + "is-ip": "^3.1.0", + "p-event": "^4.2.0" + } + }, + "ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "dev": true, + "requires": { + "ip-regex": "^4.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "mime-db": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", + "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", + "dev": true + }, + "mime-types": { + "version": "2.1.30", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", + "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "dev": true, + "requires": { + "mime-db": "1.47.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "openapi-types": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-7.2.2.tgz", + "integrity": "sha512-MJribYRLdEcnKX7SN+CKJfI0cZaPxk4mp71uE9gUWkh+6xnR9LDq2otVUly4/vtULa/wwvmOsx29lQCPy2jyAA==", + "dev": true + }, + "p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dev": true, + "requires": { + "p-timeout": "^3.1.0" + }, + "dependencies": { + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + } + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "typescript-optional": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/typescript-optional/-/typescript-optional-2.0.1.tgz", + "integrity": "sha512-xuwmqsCjE4OeeMKxbNX3jjNcISGzYh5Q9R1rM5OyxEVNIr94CB5llCkfKW+1nZTKbbUV0axN3QAUuX2fus/DhQ==" + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + } + } +} diff --git a/packages/cactus-plugin-keychain-aws-sm/package.json b/packages/cactus-plugin-keychain-aws-sm/package.json new file mode 100644 index 00000000000..9c659b0d545 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/package.json @@ -0,0 +1,102 @@ +{ + "name": "@hyperledger/cactus-plugin-keychain-aws-sm", + "version": "0.4.1", + "description": "A keychain implementation storing its entries in AWS Secret Manger.", + "main": "dist/lib/main/typescript/index.js", + "mainMinified": "dist/cactus-plugin-keychain-aws-sm.node.umd.min.js", + "browser": "dist/cactus-plugin-keychain-aws-sm.web.umd.js", + "browserMinified": "dist/cactus-plugin-keychain-aws-sm.web.umd.min.js", + "module": "dist/lib/main/typescript/index.js", + "types": "dist/types/main/typescript/index.d.ts", + "files": [ + "dist/*" + ], + "scripts": { + "generate-sdk": "openapi-generator generate --input-spec src/main/json/openapi.json -g typescript-axios -o ./src/main/typescript/generated/openapi/typescript-axios/", + "tsc": "tsc --project ./tsconfig.json", + "watch": "npm-watch", + "pretsc": "npm run generate-sdk", + "webpack": "npm-run-all webpack:dev webpack:prod", + "webpack:dev": "npm-run-all webpack:dev:node webpack:dev:web", + "webpack:dev:web": "webpack --env=dev --target=web --config ../../webpack.config.js", + "webpack:dev:node": "webpack --env=dev --target=node --config ../../webpack.config.js", + "webpack:prod": "npm-run-all webpack:prod:node webpack:prod:web", + "webpack:prod:web": "webpack --env=prod --target=web --config ../../webpack.config.js", + "webpack:prod:node": "webpack --env=prod --target=node --config ../../webpack.config.js" + }, + "watch": { + "tsc": { + "patterns": [ + "src/", + "src/*/json/**/openapi*" + ], + "ignore": [ + "src/**/generated/*" + ], + "extensions": [ + "ts", + "json" + ], + "quiet": true, + "verbose": false, + "runOnChangeOnly": true + } + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hyperledger/cactus.git" + }, + "keywords": [ + "Hyperledger", + "Cactus", + "Integration", + "Blockchain", + "Distributed Ledger Technology" + ], + "author": { + "name": "Hyperledger Cactus Contributors", + "email": "cactus@lists.hyperledger.org", + "url": "https://www.hyperledger.org/use/cactus" + }, + "contributors": [ + { + "name": "Please add yourself to the list of contributors", + "email": "your.name@example.com", + "url": "https://example.com" + }, + { + "name": "Jagpreet Singh Sasan", + "email": "jagpreet.singh.sasan@accenture.com", + "url": "https://accenture.com" + } + ], + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/hyperledger/cactus/issues" + }, + "homepage": "https://github.com/hyperledger/cactus#readme", + "dependencies": { + "@hyperledger/cactus-common": "0.4.0", + "@hyperledger/cactus-core": "0.4.1", + "@hyperledger/cactus-core-api": "0.4.1", + "aws-sdk": "2.903.0", + "axios": "0.21.1", + "http-status-codes": "2.1.4", + "typescript-optional": "2.0.1" + }, + "devDependencies": { + "@hyperledger/cactus-test-tooling": "0.4.0", + "@types/express-serve-static-core": "4.17.17", + "@types/request": "2.48.5", + "@types/express": "4.17.8", + "internal-ip": "6.2.0", + "openapi-types": "7.2.2" + } +} diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/json/openapi.json b/packages/cactus-plugin-keychain-aws-sm/src/main/json/openapi.json new file mode 100644 index 00000000000..ad18ce37f10 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/json/openapi.json @@ -0,0 +1,89 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Hyperledger Cactus - Keychain API", + "description": "Contains/describes the Keychain API types/paths for Hyperledger Cactus.", + "version": "0.3.0", + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "components": { + "schemas": { + "GetSecretRequest": { + "type": "string", + "nullable": false + }, + "GetSecretResponse": { + "type": "string", + "nullable": false + } + } + }, + "paths": { + "/api/v1/plugins/@hyperledger/cactus-plugin-keychain-aws-sm/get-keychain-entry": { + "post": { + "x-hyperledger-cactus": { + "http": { + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-keychain-aws-sm/get-keychain-entry", + "verbLowerCase": "get" + } + }, + "operationId": "getKeychainEntry", + "summary": "Retrieves the contents of a keychain entry from the backend.", + "parameters": [], + "requestBody": { + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/requestBodies/keychain_get_entry_request_body" + }, + "responses": { + "200": { + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_200" + }, + "400": { + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_400" + }, + "401": { + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_401" + }, + "404": { + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_404" + }, + "500": { + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_500" + } + } + } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-keychain-aws-sm/set-keychain-entry": { + "post": { + "x-hyperledger-cactus": { + "http": { + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-keychain-aws-sm/set-keychain-entry", + "verbLowerCase": "post" + } + }, + "operationId": "setKeychainEntry", + "summary": "Sets a value under a key on the keychain backend.", + "parameters": [], + "requestBody": { + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/requestBodies/keychain_set_entry_request_body" + }, + "responses": { + "200": { + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_set_entry_200" + }, + "400": { + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_set_entry_400" + }, + "401": { + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_set_entry_401" + }, + "500": { + "$ref": "../../../../cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_set_entry_500" + } + } + } + } + } +} \ No newline at end of file diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore new file mode 100644 index 00000000000..6ff76cf80a2 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator-ignore @@ -0,0 +1,27 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md + +.npmignore +.gitignore +git_push.sh \ No newline at end of file diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES new file mode 100644 index 00000000000..c123dd7d454 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/FILES @@ -0,0 +1,4 @@ +api.ts +base.ts +configuration.ts +index.ts diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION new file mode 100644 index 00000000000..1a487e1a2e3 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/.openapi-generator/VERSION @@ -0,0 +1 @@ +5.0.0-beta2 \ No newline at end of file diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/api.ts new file mode 100644 index 00000000000..812dd525d35 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -0,0 +1,284 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cactus - Keychain API + * Contains/describes the Keychain API types/paths for Hyperledger Cactus. + * + * The version of the OpenAPI document: 0.3.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import { Configuration } from './configuration'; +import globalAxios, { AxiosPromise, AxiosInstance } from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from './base'; + +/** + * + * @export + * @interface GetKeychainEntryRequest + */ +export interface GetKeychainEntryRequest { + /** + * The key for the entry to get from the keychain. + * @type {string} + * @memberof GetKeychainEntryRequest + */ + key: string; +} +/** + * + * @export + * @interface GetKeychainEntryResponse + */ +export interface GetKeychainEntryResponse { + /** + * The key that was used to retrieve the value from the keychain. + * @type {string} + * @memberof GetKeychainEntryResponse + */ + key: string; + /** + * The value associated with the requested key on the keychain. + * @type {string} + * @memberof GetKeychainEntryResponse + */ + value: string; +} +/** + * + * @export + * @interface SetKeychainEntryRequest + */ +export interface SetKeychainEntryRequest { + /** + * The key for the entry to set on the keychain. + * @type {string} + * @memberof SetKeychainEntryRequest + */ + key: string; + /** + * The value that will be associated with the key on the keychain. + * @type {string} + * @memberof SetKeychainEntryRequest + */ + value: string; +} +/** + * + * @export + * @interface SetKeychainEntryResponse + */ +export interface SetKeychainEntryResponse { + /** + * The key that was used to set the value on the keychain. + * @type {string} + * @memberof SetKeychainEntryResponse + */ + key: string; +} + +/** + * DefaultApi - axios parameter creator + * @export + */ +export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary Retrieves the contents of a keychain entry from the backend. + * @param {GetKeychainEntryRequest} getKeychainEntryRequest Requst body to obtain a keychain entry via its key + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getKeychainEntry: async (getKeychainEntryRequest: GetKeychainEntryRequest, options: any = {}): Promise => { + // verify required parameter 'getKeychainEntryRequest' is not null or undefined + if (getKeychainEntryRequest === null || getKeychainEntryRequest === undefined) { + throw new RequiredError('getKeychainEntryRequest','Required parameter getKeychainEntryRequest was null or undefined when calling getKeychainEntry.'); + } + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-keychain-aws-sm/get-keychain-entry`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com'); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + const query = new URLSearchParams(localVarUrlObj.search); + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]); + } + for (const key in options.query) { + query.set(key, options.query[key]); + } + localVarUrlObj.search = (new URLSearchParams(query)).toString(); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + const needsSerialization = (typeof getKeychainEntryRequest !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json'; + localVarRequestOptions.data = needsSerialization ? JSON.stringify(getKeychainEntryRequest !== undefined ? getKeychainEntryRequest : {}) : (getKeychainEntryRequest || ""); + + return { + url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Sets a value under a key on the keychain backend. + * @param {SetKeychainEntryRequest} setKeychainEntryRequest Requst body to write/update a keychain entry via its key + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + setKeychainEntry: async (setKeychainEntryRequest: SetKeychainEntryRequest, options: any = {}): Promise => { + // verify required parameter 'setKeychainEntryRequest' is not null or undefined + if (setKeychainEntryRequest === null || setKeychainEntryRequest === undefined) { + throw new RequiredError('setKeychainEntryRequest','Required parameter setKeychainEntryRequest was null or undefined when calling setKeychainEntry.'); + } + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-keychain-aws-sm/set-keychain-entry`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com'); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + const query = new URLSearchParams(localVarUrlObj.search); + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]); + } + for (const key in options.query) { + query.set(key, options.query[key]); + } + localVarUrlObj.search = (new URLSearchParams(query)).toString(); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + const needsSerialization = (typeof setKeychainEntryRequest !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json'; + localVarRequestOptions.data = needsSerialization ? JSON.stringify(setKeychainEntryRequest !== undefined ? setKeychainEntryRequest : {}) : (setKeychainEntryRequest || ""); + + return { + url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * DefaultApi - functional programming interface + * @export + */ +export const DefaultApiFp = function(configuration?: Configuration) { + return { + /** + * + * @summary Retrieves the contents of a keychain entry from the backend. + * @param {GetKeychainEntryRequest} getKeychainEntryRequest Requst body to obtain a keychain entry via its key + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getKeychainEntry(getKeychainEntryRequest: GetKeychainEntryRequest, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await DefaultApiAxiosParamCreator(configuration).getKeychainEntry(getKeychainEntryRequest, options); + return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url}; + return axios.request(axiosRequestArgs); + }; + }, + /** + * + * @summary Sets a value under a key on the keychain backend. + * @param {SetKeychainEntryRequest} setKeychainEntryRequest Requst body to write/update a keychain entry via its key + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async setKeychainEntry(setKeychainEntryRequest: SetKeychainEntryRequest, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await DefaultApiAxiosParamCreator(configuration).setKeychainEntry(setKeychainEntryRequest, options); + return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url}; + return axios.request(axiosRequestArgs); + }; + }, + } +}; + +/** + * DefaultApi - factory interface + * @export + */ +export const DefaultApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + return { + /** + * + * @summary Retrieves the contents of a keychain entry from the backend. + * @param {GetKeychainEntryRequest} getKeychainEntryRequest Requst body to obtain a keychain entry via its key + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getKeychainEntry(getKeychainEntryRequest: GetKeychainEntryRequest, options?: any): AxiosPromise { + return DefaultApiFp(configuration).getKeychainEntry(getKeychainEntryRequest, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary Sets a value under a key on the keychain backend. + * @param {SetKeychainEntryRequest} setKeychainEntryRequest Requst body to write/update a keychain entry via its key + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + setKeychainEntry(setKeychainEntryRequest: SetKeychainEntryRequest, options?: any): AxiosPromise { + return DefaultApiFp(configuration).setKeychainEntry(setKeychainEntryRequest, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * DefaultApi - object-oriented interface + * @export + * @class DefaultApi + * @extends {BaseAPI} + */ +export class DefaultApi extends BaseAPI { + /** + * + * @summary Retrieves the contents of a keychain entry from the backend. + * @param {GetKeychainEntryRequest} getKeychainEntryRequest Requst body to obtain a keychain entry via its key + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public getKeychainEntry(getKeychainEntryRequest: GetKeychainEntryRequest, options?: any) { + return DefaultApiFp(this.configuration).getKeychainEntry(getKeychainEntryRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Sets a value under a key on the keychain backend. + * @param {SetKeychainEntryRequest} setKeychainEntryRequest Requst body to write/update a keychain entry via its key + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public setKeychainEntry(setKeychainEntryRequest: SetKeychainEntryRequest, options?: any) { + return DefaultApiFp(this.configuration).setKeychainEntry(setKeychainEntryRequest, options).then((request) => request(this.axios, this.basePath)); + } +} + + diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/base.ts b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/base.ts new file mode 100644 index 00000000000..5bdd7d5a901 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/base.ts @@ -0,0 +1,71 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cactus - Keychain API + * Contains/describes the Keychain API types/paths for Hyperledger Cactus. + * + * The version of the OpenAPI document: 0.3.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import { Configuration } from "./configuration"; +// Some imports not used depending on template conditions +// @ts-ignore +import globalAxios, { AxiosPromise, AxiosInstance } from 'axios'; + +export const BASE_PATH = "http://localhost".replace(/\/+$/, ""); + +/** + * + * @export + */ +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +/** + * + * @export + * @interface RequestArgs + */ +export interface RequestArgs { + url: string; + options: any; +} + +/** + * + * @export + * @class BaseAPI + */ +export class BaseAPI { + protected configuration: Configuration | undefined; + + constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) { + if (configuration) { + this.configuration = configuration; + this.basePath = configuration.basePath || this.basePath; + } + } +}; + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/configuration.ts b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/configuration.ts new file mode 100644 index 00000000000..bb3cb03b746 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/configuration.ts @@ -0,0 +1,76 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cactus - Keychain API + * Contains/describes the Keychain API types/paths for Hyperledger Cactus. + * + * The version of the OpenAPI document: 0.3.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface ConfigurationParameters { + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + username?: string; + password?: string; + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + basePath?: string; + baseOptions?: any; +} + +export class Configuration { + /** + * parameter for apiKey security + * @param name security name + * @memberof Configuration + */ + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + username?: string; + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + password?: string; + /** + * parameter for oauth2 security + * @param name security name + * @param scopes oauth2 scope + * @memberof Configuration + */ + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + /** + * override base path + * + * @type {string} + * @memberof Configuration + */ + basePath?: string; + /** + * base options for axios calls + * + * @type {any} + * @memberof Configuration + */ + baseOptions?: any; + + constructor(param: ConfigurationParameters = {}) { + this.apiKey = param.apiKey; + this.username = param.username; + this.password = param.password; + this.accessToken = param.accessToken; + this.basePath = param.basePath; + this.baseOptions = param.baseOptions; + } +} diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/index.ts b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/index.ts new file mode 100644 index 00000000000..dece72213b3 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/generated/openapi/typescript-axios/index.ts @@ -0,0 +1,18 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Hyperledger Cactus - Keychain API + * Contains/describes the Keychain API types/paths for Hyperledger Cactus. + * + * The version of the OpenAPI document: 0.3.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export * from "./api"; +export * from "./configuration"; + diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/index.ts b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/index.ts new file mode 100644 index 00000000000..87cb558397c --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/index.ts @@ -0,0 +1 @@ +export * from "./public-api"; diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/index.web.ts b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/index.web.ts new file mode 100644 index 00000000000..bdf54028d23 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/index.web.ts @@ -0,0 +1 @@ +export * from "./generated/openapi/typescript-axios/index"; diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/plugin-factory-keychain.ts b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/plugin-factory-keychain.ts new file mode 100644 index 00000000000..12d93e70c1a --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/plugin-factory-keychain.ts @@ -0,0 +1,49 @@ +import { Checks } from "@hyperledger/cactus-common"; +import { + IPluginFactoryOptions, + IPluginKeychain, + PluginFactory, + PluginImportType, +} from "@hyperledger/cactus-core-api"; +import { + Configuration, + DefaultApi, +} from "./generated/openapi/typescript-axios"; + +import { + IPluginKeychainAwsSmOptions, + PluginKeychainAwsSm, +} from "./plugin-keychain-aws-sm"; + +import { PluginKeychainAwsSmRemoteAdapter } from "./plugin-keychain-aws-sm-remote-adapter"; + +export class PluginFactoryKeychain extends PluginFactory< + IPluginKeychain, + IPluginKeychainAwsSmOptions, + IPluginFactoryOptions +> { + async create(options: any): Promise { + const fnTag = "PluginFactoryKeychain#create()"; + + const { pluginImportType } = this.options; + Checks.truthy(options, `${fnTag}:options`); + + switch (pluginImportType) { + case PluginImportType.LOCAL: { + return new PluginKeychainAwsSm(options); + } + case PluginImportType.REMOTE: { + const { remoteConfig } = options; + Checks.truthy(remoteConfig, `${fnTag}:options.remoteConfig`); + Checks.truthy(remoteConfig.basePath, `${fnTag}:remoteConfig.basePath`); + const configuration: Configuration = options.remoteConfig; + const backend = new DefaultApi(configuration); + const optionsDecorated = { ...options, backend }; + return new PluginKeychainAwsSmRemoteAdapter(optionsDecorated); + } + default: { + throw new Error(`${fnTag} No PluginImportType: ${pluginImportType}`); + } + } + } +} diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/plugin-keychain-aws-sm-remote-adapter.ts b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/plugin-keychain-aws-sm-remote-adapter.ts new file mode 100644 index 00000000000..4a265b4c02c --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/plugin-keychain-aws-sm-remote-adapter.ts @@ -0,0 +1,154 @@ +import type { Server } from "http"; +import type { Server as SecureServer } from "https"; + +import type { Express } from "express"; +import { Optional } from "typescript-optional"; + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, +} from "@hyperledger/cactus-common"; +import { + ICactusPlugin, + ICactusPluginOptions, + IPluginWebService, + IWebServiceEndpoint, + PluginAspect, +} from "@hyperledger/cactus-core-api"; + +import { DefaultApi } from "./generated/openapi/typescript-axios"; + +export interface IPluginKeychainAwsSmRemoteAdapterOptions + extends ICactusPluginOptions { + backend: DefaultApi; + keychainId: string; + logLevel?: LogLevelDesc; +} + +/** + * Class responsible for ecapsulating an API client object and then acting as + * an adapter (ta-da) between said API client object and the calling code to + * which it is (should be) transparent whether it is talking to an in-process + * plugin instance of the keychain plugin or an adapter backed by an API client + * object in which scenario the real keychain plugin object can be anywhere + * else on the network and also can be written in any programming language that + * the author so desires. + */ +export class PluginKeychainAwsSmRemoteAdapter + implements ICactusPlugin, IPluginWebService { + public static readonly CLASS_NAME = "PluginKeychainAwsSmRemoteAdapter"; + + private readonly instanceId: string; + private readonly keychainId: string; + private readonly log: Logger; + private readonly backend: DefaultApi; + + public get className(): string { + return PluginKeychainAwsSmRemoteAdapter.CLASS_NAME; + } + + constructor(public readonly opts: IPluginKeychainAwsSmRemoteAdapterOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(opts, `${fnTag} arg options`); + Checks.truthy(opts.keychainId, `${fnTag} arg options.keychainId`); + Checks.truthy(opts.instanceId, `${fnTag} options.instanceId`); + Checks.nonBlankString(opts.keychainId, `${fnTag} options.keychainId`); + Checks.truthy(opts.backend, `${fnTag} options.backend`); + Checks.truthy( + opts.backend instanceof DefaultApi, + `${fnTag} opts.backend instanceof DefaultApi`, + ); + + const level = this.opts.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.instanceId = this.opts.instanceId; + this.backend = opts.backend; + this.keychainId = opts.keychainId; + + this.log.info(`Created ${this.className}. KeychainID=${opts.keychainId}`); + } + + /** + * Dummy implementation that wires no web services on the host API server + * because there is no need. All the functionality is implemented somewhere + * else on a host that's accessible through the network to this object + * (because this class is a remote adapter not an actual plugin impl.). + * + * @param _expressApp + */ + public async getOrCreateWebServices(): Promise { + return []; + } + + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + public registerWebServices(app: Express): Promise { + return this.getOrCreateWebServices(); + } + + public getHttpServer(): Optional { + return Optional.empty(); + } + + public async shutdown(): Promise { + return; + } + + public rotateEncryptionKeys(): Promise { + throw new Error("Method not implemented."); + } + + public getEncryptionAlgorithm(): string { + throw new Error("Method not implemented."); + } + + public getKeychainId(): string { + return this.keychainId; + } + + public async has(key: string): Promise { + try { + await this.backend.getKeychainEntry({ key }); + return true; + } catch (ex) { + // FIXME check for errors being thrown due to something other than + // the key not being present... + return false; + } + } + + public async get(key: string): Promise { + const { data } = await this.backend.getKeychainEntry({ key }); + // FIXME what to do here? Does it make any sense to have the get() method + // of the keychain be generically parameterizable when we know we can only + // return a string anyway? + return data.value as any; + } + + public async set(key: string, value: T): Promise { + // FIXME Does it make any sense to have the set() method be generic? + await this.backend.setKeychainEntry({ key, value: value as any }); + } + + public async delete(key: string): Promise { + // FIXME Pretty sure aws secret manager can do delete so we don't have to hack it like this + // but it cannot be done in this code until the rust code has been updated + // to have that endpoint as well... + await this.backend.setKeychainEntry({ key, value: "" }); + } + + public getInstanceId(): string { + return this.instanceId; + } + + public getPackageName(): string { + return `@hyperledger/cactus-plugin-keychain-aws-sm`; + } + + public getAspect(): PluginAspect { + return PluginAspect.KEYCHAIN; + } +} diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/plugin-keychain-aws-sm.ts b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/plugin-keychain-aws-sm.ts new file mode 100644 index 00000000000..c30173faa5b --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/plugin-keychain-aws-sm.ts @@ -0,0 +1,279 @@ +import type { Server } from "http"; +import type { Server as SecureServer } from "https"; + +import { + SharedIniFileCredentials, + config as awsConfig, + SecretsManager, + Credentials, +} from "aws-sdk"; + +import type { Express } from "express"; +import { Optional } from "typescript-optional"; + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, +} from "@hyperledger/cactus-common"; +import { + ICactusPlugin, + ICactusPluginOptions, + IPluginWebService, + IWebServiceEndpoint, + PluginAspect, +} from "@hyperledger/cactus-core-api"; + +import { homedir } from "os"; + +export enum AwsCredentialType { + LocalFile = "LOCAL_FILE", + InMemory = "IN_MEMORY", +} + +export interface IPluginKeychainAwsSmOptions extends ICactusPluginOptions { + logLevel?: LogLevelDesc; + keychainId: string; + awsProfile: string; + awsRegion: string; + awsEndpoint: string; + awsCredentialType: AwsCredentialType; + /* + * awsCredentialFilePath field optional and necessary only when + * awsCredentialType == AwsCredentialType.LocalFile + * This field defaults to $HOME/.aws/credentials + */ + awsCredentialFilePath?: string; + /* + * awsAccessKeyId field optional and neccessary only when + * awsCredentialType == AwsCredentialType.InMemory + */ + awsAccessKeyId?: string; + /* + * awsSecretAccessKey field optional and neccessary only when + * awsCredentialType == AwsCredentialType.InMemory + */ + awsSecretAccessKey?: string; +} + +const SECRETMANAGER_STATUS_KEY_NOT_FOUND = + "Secrets Manager can't find the specified secret."; +export class PluginKeychainAwsSm implements ICactusPlugin, IPluginWebService { + public static readonly CLASS_NAME = "PluginKeychainAwsSm"; + + private readonly log: Logger; + private readonly instanceId: string; + /* + * awsProfile variable is used to refer to the aws profile to be consumed + * while running the plugin. The profile is located at + * ~/.aws/credentials by default and is setup when you run "aws configure" + * on the machine running this plugin. + * You can pass custom aws credential file path by defining the awsCredentialFilePath + * variable + */ + private readonly awsProfile: string; + private readonly awsRegion: string; + private readonly awsEndpoint: string; + private readonly awsClient: SecretsManager; + private endpoints: IWebServiceEndpoint[] | undefined; + private awsCredentialType: AwsCredentialType; + + public get className() { + return PluginKeychainAwsSm.CLASS_NAME; + } + + constructor(public readonly opts: IPluginKeychainAwsSmOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(opts, `${fnTag} arg options`); + Checks.truthy(opts.keychainId, `${fnTag} arg options.keychainId`); + Checks.truthy(opts.instanceId, `${fnTag} options.instanceId`); + Checks.truthy(opts.awsProfile, `${fnTag} options.awsProfile`); + Checks.truthy(opts.awsRegion, `${fnTag} options.awsRegion`); + Checks.truthy(opts.awsEndpoint, `${fnTag} options.awsEndpoint`); + Checks.truthy(opts.awsCredentialType, `${fnTag} options.awsCredentialType`); + Checks.nonBlankString(opts.keychainId, `${fnTag} options.keychainId`); + Checks.nonBlankString(opts.awsProfile, `${fnTag} options.awsProfile`); + Checks.nonBlankString(opts.awsRegion, `${fnTag} options.awsRegion`); + Checks.nonBlankString(opts.awsEndpoint, `${fnTag} options.awsEndpoint`); + if (opts.awsCredentialType == AwsCredentialType.InMemory) { + Checks.nonBlankString( + opts.awsAccessKeyId, + `${fnTag} options.awsAccessKeyId`, + ); + Checks.nonBlankString( + opts.awsSecretAccessKey, + `${fnTag} options.awsSecretAccessKey`, + ); + } + + const level = this.opts.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.instanceId = this.opts.instanceId; + this.awsProfile = this.opts.awsProfile; + this.awsRegion = this.opts.awsRegion; + this.awsEndpoint = this.opts.awsEndpoint; + this.awsCredentialType = this.opts.awsCredentialType; + + if (this.awsCredentialType == AwsCredentialType.LocalFile) { + const credentials = new SharedIniFileCredentials({ + profile: this.awsProfile, + filename: + this.opts.awsCredentialFilePath || `${homedir()}/.aws/credentials`, + }); + awsConfig.credentials = credentials; + } else if (this.awsCredentialType == AwsCredentialType.InMemory) { + const credentials = new Credentials({ + accessKeyId: this.opts.awsAccessKeyId || "", + secretAccessKey: this.opts.awsSecretAccessKey || "", + }); + awsConfig.credentials = credentials; + } + + awsConfig.region = this.awsRegion; + this.awsClient = new SecretsManager({ + endpoint: this.awsEndpoint, + }); + + this.log.info(`Created ${this.className}. KeychainID=${opts.keychainId}`); + } + + public getAwsClient(): SecretsManager { + return this.awsClient; + } + + async registerWebServices(app: Express): Promise { + const webServices = await this.getOrCreateWebServices(); + await Promise.all(webServices.map((ws) => ws.registerExpress(app))); + return webServices; + } + + public async getOrCreateWebServices(): Promise { + if (Array.isArray(this.endpoints)) { + return this.endpoints; + } + const endpoints: IWebServiceEndpoint[] = []; + + this.endpoints = endpoints; + + return endpoints; + } + + public getHttpServer(): Optional { + return Optional.empty(); + } + + public async shutdown(): Promise { + throw new Error("Method not implemented."); + } + + public getInstanceId(): string { + return this.instanceId; + } + + public getKeychainId(): string { + return this.opts.keychainId; + } + + public getPackageName(): string { + return `@hyperledger/cactus-plugin-keychain-aws-sm`; + } + + public getAspect(): PluginAspect { + return PluginAspect.KEYCHAIN; + } + + async rotateEncryptionKeys(): Promise { + throw new Error("Method not implemented."); + } + + public getEncryptionAlgorithm(): string { + return null as any; + } + + async get(key: string): Promise { + const fnTag = `${this.className}#get(key: string)`; + const awsClient = this.getAwsClient(); + try { + const res = (await awsClient + .getSecretValue({ + SecretId: key, + }) + .promise()) as any; + if (res.SecretString) { + return res.SecretString; + } else { + throw new Error( + `${fnTag}: Invalid response recieved from AWS SecretsManager. Expected "response.SecretString" property chain to be truthy`, + ); + } + } catch (ex) { + const errorStatus = (ex as Error).message.includes( + SECRETMANAGER_STATUS_KEY_NOT_FOUND, + ); + if (errorStatus) { + return (null as unknown) as T; + } else { + this.log.error(`Error retriving secret value for the key "${key}"`); + throw ex; + } + } + } + + async has(key: string): Promise { + const fnTag = `${this.className}#has(key: string)`; + const awsClient = this.getAwsClient(); + try { + (await awsClient + .describeSecret({ + SecretId: key, + }) + .promise()) as any; + return true; + } catch (ex) { + const errorStatus = (ex as Error).message.includes( + SECRETMANAGER_STATUS_KEY_NOT_FOUND, + ); + if (errorStatus) { + return false; + } else { + this.log.error(`${fnTag}: Presence check of "${key}" crashed:`, ex); + throw ex; + } + } + } + + async set(key: string, value: T): Promise { + const fnTag = `${this.className}#set(key: string)`; + const awsClient = this.getAwsClient(); + try { + (await awsClient + .createSecret({ + Name: key, + SecretString: value as any, + }) + .promise()) as any; + } catch (ex) { + this.log.error(` ${fnTag}: Error writing secret "${key}"`); + throw ex; + } + } + + async delete(key: string): Promise { + const fnTag = `${this.className}#delete(key: string)`; + const awsClient = this.getAwsClient(); + try { + (await awsClient + .deleteSecret({ + SecretId: key, + ForceDeleteWithoutRecovery: true, + }) + .promise()) as any; + } catch (ex) { + this.log.error(`${fnTag} Error deleting secret "${key}"`); + throw ex; + } + } +} diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/public-api.ts b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/public-api.ts new file mode 100644 index 00000000000..d7392209155 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/public-api.ts @@ -0,0 +1,18 @@ +export * from "./generated/openapi/typescript-axios/index"; + +import { IPluginFactoryOptions } from "@hyperledger/cactus-core-api"; + +import { PluginFactoryKeychain } from "./plugin-factory-keychain"; +export { PluginFactoryKeychain } from "./plugin-factory-keychain"; + +export { + IPluginKeychainAwsSmOptions, + PluginKeychainAwsSm, + AwsCredentialType, +} from "./plugin-keychain-aws-sm"; + +export async function createPluginFactory( + pluginFactoryOptions: IPluginFactoryOptions, +): Promise { + return new PluginFactoryKeychain(pluginFactoryOptions); +} diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/webservices/get-keychain-entry-endpoint-v1.ts b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/webservices/get-keychain-entry-endpoint-v1.ts new file mode 100644 index 00000000000..9c3a8b5d6f7 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/webservices/get-keychain-entry-endpoint-v1.ts @@ -0,0 +1,76 @@ +import type { Express } from "express"; + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, +} from "@hyperledger/cactus-common"; +import { + IEndpointAuthzOptions, + IExpressRequestHandler, + IWebServiceEndpoint, +} from "@hyperledger/cactus-core-api"; +import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import OAS from "../../json/openapi.json"; + +export interface IGetKeychainEntryEndpointV1Options { + logLevel?: LogLevelDesc; +} + +export class GetKeychainEntryEndpointV1 implements IWebServiceEndpoint { + public static readonly CLASS_NAME = "GetKeychainEntryEndpointV1"; + + private readonly log: Logger; + + public get className(): string { + return GetKeychainEntryEndpointV1.CLASS_NAME; + } + + constructor(public readonly options: IGetKeychainEntryEndpointV1Options) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + this.log.debug(`Instantiated ${this.className} OK`); + } + + private getOperation() { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-keychain-aws-sm/get-keychain-entry" + ].post; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + public getVerbLowerCase(): string { + return this.getOperation()["x-hyperledger-cactus"].http.verbLowerCase; + } + + public getPath(): string { + return this.getOperation()["x-hyperledger-cactus"].http.path; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + throw new Error("Method not implemented."); + } +} diff --git a/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/webservices/set-keychain-entry-endpoint-v1.ts b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/webservices/set-keychain-entry-endpoint-v1.ts new file mode 100644 index 00000000000..6c3514e42b0 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/main/typescript/webservices/set-keychain-entry-endpoint-v1.ts @@ -0,0 +1,76 @@ +import type { Express } from "express"; + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, +} from "@hyperledger/cactus-common"; +import { + IEndpointAuthzOptions, + IExpressRequestHandler, + IWebServiceEndpoint, +} from "@hyperledger/cactus-core-api"; +import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; + +import OAS from "../../json/openapi.json"; + +export interface ISetKeychainEntryEndpointV1Options { + logLevel?: LogLevelDesc; +} + +export class SetKeychainEntryEndpointV1 implements IWebServiceEndpoint { + public static readonly CLASS_NAME = "SetKeychainEntryEndpointV1"; + + private readonly log: Logger; + + public get className(): string { + return SetKeychainEntryEndpointV1.CLASS_NAME; + } + + constructor(public readonly options: ISetKeychainEntryEndpointV1Options) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + this.log.debug(`Instantiated ${this.className} OK`); + } + + private getOperation() { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-keychain-aws-sm/set-keychain-entry" + ].post; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + public getVerbLowerCase(): string { + return this.getOperation()["x-hyperledger-cactus"].http.verbLowerCase; + } + + public getPath(): string { + return this.getOperation()["x-hyperledger-cactus"].http.path; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + throw new Error("Method not implemented."); + } +} diff --git a/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/a.ts b/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/a.ts new file mode 100644 index 00000000000..333b853447b --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/a.ts @@ -0,0 +1,15 @@ +// import fs from "fs" +// import path from "path" +// import os from "os" + +// const relTmpDirPath = fs.mkdtempSync(path.join(os.tmpdir(), "cactus-")) +// const absTmpDirPath = path.resolve(relTmpDirPath) +// const writestream = fs.createWriteStream(`${absTmpDirPath}/credentials`) +// writestream.write("[default]\naws_secret_access_key = test\naws_access_key_id = test") +// writestream.end(); + +// console.log(relTmpDirPath) +// console.log(absTmpDirPath) + +// fs.unlinkSync(`${absTmpDirPath}/credentials`) +// fs.rmdirSync(`${absTmpDirPath}`) diff --git a/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/plugin-keychain-aws-sm.test.ts b/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/plugin-keychain-aws-sm.test.ts new file mode 100644 index 00000000000..af7e3785df5 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/plugin-keychain-aws-sm.test.ts @@ -0,0 +1,143 @@ +import test, { Test } from "tape-promise/tape"; +import { v4 as internalIpV4 } from "internal-ip"; + +import { + Containers, + LocalStackContainer, + K_DEFAULT_LOCALSTACK_HTTP_PORT, +} from "@hyperledger/cactus-test-tooling"; + +import { v4 as uuidv4 } from "uuid"; + +import { LogLevelDesc } from "@hyperledger/cactus-common"; + +import { + IPluginKeychainAwsSmOptions, + PluginKeychainAwsSm, + AwsCredentialType, +} from "../../../main/typescript/public-api"; + +import fs from "fs"; +import path from "path"; +import os from "os"; + +const logLevel: LogLevelDesc = "TRACE"; + +test("get,set,has,delete alters state as expected", async (t: Test) => { + const localStackContainer = new LocalStackContainer({ + logLevel: logLevel, + }); + await localStackContainer.start(); + + const ci = await Containers.getById(localStackContainer.containerId); + const localstackIpAddr = await internalIpV4(); + const hostPort = await Containers.getPublicPort( + K_DEFAULT_LOCALSTACK_HTTP_PORT, + ci, + ); + const localstackHost = `http://${localstackIpAddr}:${hostPort}`; + + test.onFinish(async () => { + await localStackContainer.stop(); + await localStackContainer.destroy(); + }); + + // Using awsCredentialType: AwsCredentialType.FromAwsCredentialFile + { + // Create aws credential file in a local directory + const tmpDirPath = fs.mkdtempSync(path.join(os.tmpdir(), "cactus-")); + const writestream = fs.createWriteStream(`${tmpDirPath}/credentials`); + writestream.write( + "[default]\naws_secret_access_key = test\naws_access_key_id = test", + ); + writestream.end(); + await new Promise((resolve) => setTimeout(resolve, 1000)); + + const options1: IPluginKeychainAwsSmOptions = { + instanceId: uuidv4(), + keychainId: uuidv4(), + awsEndpoint: localstackHost, + awsRegion: "us-east-1", + awsProfile: "default", + awsCredentialType: AwsCredentialType.LocalFile, + awsCredentialFilePath: `${tmpDirPath}/credentials`, + logLevel: logLevel, + }; + const plugin1 = new PluginKeychainAwsSm(options1); + + t.equal(plugin1.getKeychainId(), options1.keychainId, "Keychain ID set OK"); + t.equal(plugin1.getInstanceId(), options1.instanceId, "Instance ID set OK"); + + const key1 = uuidv4(); + const value1 = uuidv4(); + + const hasPrior1 = await plugin1.has(key1); + + t.false(hasPrior1, "hasPrior1 === false OK"); + + await plugin1.set(key1, value1); + + const hasAfter1 = await plugin1.has(key1); + t.true(hasAfter1, "hasAfter1 === true OK"); + + const valueAfter1 = await plugin1.get(key1); + t.ok(valueAfter1, "valueAfter1 truthy OK"); + t.equal(valueAfter1, value1, "valueAfter1 === value OK"); + + await plugin1.delete(key1); + + const hasAfterDelete1 = await plugin1.has(key1); + t.false(hasAfterDelete1, "hasAfterDelete1 === false OK"); + + const valueAfterDelete1 = await plugin1.get(key1); + t.notok(valueAfterDelete1, "valueAfterDelete1 falsy OK"); + + fs.unlinkSync(`${tmpDirPath}/credentials`); + fs.rmdirSync(`${tmpDirPath}`); + } + + // Using awsCredentialType: AwsCredentialType.FromCodeVariable + { + const options2: IPluginKeychainAwsSmOptions = { + instanceId: uuidv4(), + keychainId: uuidv4(), + awsEndpoint: localstackHost, + awsRegion: "us-east-1", + awsProfile: "default", + awsCredentialType: AwsCredentialType.InMemory, + awsAccessKeyId: "fake", + awsSecretAccessKey: "fake", + logLevel: logLevel, + }; + const plugin2 = new PluginKeychainAwsSm(options2); + + t.equal(plugin2.getKeychainId(), options2.keychainId, "Keychain ID set OK"); + t.equal(plugin2.getInstanceId(), options2.instanceId, "Instance ID set OK"); + + const key2 = uuidv4(); + const value2 = uuidv4(); + + const hasPrior2 = await plugin2.has(key2); + + t.false(hasPrior2, "hasPrior2 === false OK"); + + await plugin2.set(key2, value2); + + const hasAfter2 = await plugin2.has(key2); + t.true(hasAfter2, "hasAfter2 === true OK"); + + const valueAfter2 = await plugin2.get(key2); + t.ok(valueAfter2, "valueAfter2 truthy OK"); + t.equal(valueAfter2, value2, "valueAfter2 === value OK"); + + await plugin2.delete(key2); + + const hasAfterDelete2 = await plugin2.has(key2); + t.false(hasAfterDelete2, "hasAfterDelete2 === false OK"); + + const valueAfterDelete2 = await plugin2.get(key2); + t.notok(valueAfterDelete2, "valueAfterDelete2 falsy OK"); + } + + t.end(); +}); diff --git a/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/unit/api-surface.ts b/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/unit/api-surface.ts new file mode 100644 index 00000000000..a44efd79231 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/unit/api-surface.ts @@ -0,0 +1,7 @@ +import test, { Test } from "tape"; +import * as publicApi from "../../../main/typescript/public-api"; + +test("Library can be loaded", (assert: Test) => { + assert.ok(publicApi); + assert.end(); +}); diff --git a/packages/cactus-plugin-keychain-aws-sm/tsconfig.json b/packages/cactus-plugin-keychain-aws-sm/tsconfig.json new file mode 100644 index 00000000000..2e4c5375146 --- /dev/null +++ b/packages/cactus-plugin-keychain-aws-sm/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist/lib/", /* Redirect output structure to the directory. */ + "declarationDir": "dist/types", + }, + "include": [ + "./src" + ] + } \ No newline at end of file diff --git a/packages/cactus-test-tooling/src/main/typescript/localstack/localstack.ts b/packages/cactus-test-tooling/src/main/typescript/localstack/localstack.ts new file mode 100644 index 00000000000..0ec0f54b05a --- /dev/null +++ b/packages/cactus-test-tooling/src/main/typescript/localstack/localstack.ts @@ -0,0 +1,152 @@ +import { EventEmitter } from "events"; + +import Docker, { Container } from "dockerode"; + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, +} from "@hyperledger/cactus-common"; + +import { Containers } from "../common/containers"; + +export interface ILocalStackContainerOptions { + imageVersion?: string; + imageName?: string; + logLevel?: LogLevelDesc; +} + +export const K_DEFAULT_LOCALSTACK_IMAGE_NAME = "localstack/localstack"; +export const K_DEFAULT_LOCALSTACK_IMAGE_VERSION = "0.12.10"; +export const K_DEFAULT_LOCALSTACK_HTTP_PORT = 4566; + +/** + * > **Do not use this image in production.** + * + * Class responsible for programmatically managing a container that is running + * the official LocalStack image in development mode. + */ +export class LocalStackContainer { + public static readonly CLASS_NAME = "LocalStackContainer"; + + private readonly log: Logger; + private readonly imageName: string; + private readonly imageVersion: string; + + private _container: Container | undefined; + private _containerId: string | undefined; + + public get containerId(): string { + Checks.nonBlankString(this._containerId, `${this.className}:_containerId`); + return this._containerId as string; + } + + public get container(): Container { + Checks.nonBlankString(this._container, `${this.className}:_container`); + return this._container as Container; + } + + public get className(): string { + return LocalStackContainer.CLASS_NAME; + } + + public get imageFqn(): string { + return `${this.imageName}:${this.imageVersion}`; + } + + constructor(public readonly options: ILocalStackContainerOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.imageName = this.options.imageName || K_DEFAULT_LOCALSTACK_IMAGE_NAME; + this.imageVersion = + this.options.imageVersion || K_DEFAULT_LOCALSTACK_IMAGE_VERSION; + + this.log.info(`Created ${this.className} OK. Image FQN: ${this.imageFqn}`); + } + + public async start(): Promise { + if (this._container) { + await this._container.stop(); + await this._container.remove(); + } + const docker = new Docker(); + + await Containers.pullImage(this.imageFqn); + + return new Promise((resolve, reject) => { + const eventEmitter: EventEmitter = docker.run( + this.imageFqn, + [], + [], + { + ExposedPorts: { + [`${K_DEFAULT_LOCALSTACK_HTTP_PORT}/tcp`]: {}, + }, + // This is a workaround needed for macOS which has issues with routing + // to docker container's IP addresses directly... + // https://stackoverflow.com/a/39217691 + PublishAllPorts: true, + HostConfig: { + // NetworkMode: "host", + // CapAdd: ["IPC_LOCK"], + PublishAllPorts: true, + }, + Healthcheck: { + Test: [ + "CMD-SHELL", + 'curl -s localhost:4566/health | grep \'"secretsmanager": "running"\'', + ], + Interval: 100 * 1000000, + }, + }, + {}, + (err: Error) => { + if (err) { + reject(err); + } + }, + ); + + eventEmitter.once("start", async (container: Container) => { + this._container = container; + this._containerId = container.id; + try { + await Containers.waitForHealthCheck(this._containerId); + resolve(container); + } catch (ex) { + reject(ex); + } + }); + }); + } + + public async stop(): Promise { + await Containers.stop(this._container as Docker.Container); + } + + public destroy(): Promise { + const fnTag = `${this.className}#destroy()`; + if (this._container) { + return this._container.remove(); + } else { + const ex = new Error(`${fnTag} Container not found, nothing to destroy.`); + return Promise.reject(ex); + } + } + + public async getHostPortHttp(): Promise { + const fnTag = `${this.className}#getHostPortHttp()`; + if (this._containerId) { + const cInfo = await Containers.getById(this._containerId); + return Containers.getPublicPort(K_DEFAULT_LOCALSTACK_HTTP_PORT, cInfo); + } else { + throw new Error(`${fnTag} Container ID not set. Did you call start()?`); + } + } +} diff --git a/packages/cactus-test-tooling/src/main/typescript/public-api.ts b/packages/cactus-test-tooling/src/main/typescript/public-api.ts index 1de8186042d..a8213032682 100755 --- a/packages/cactus-test-tooling/src/main/typescript/public-api.ts +++ b/packages/cactus-test-tooling/src/main/typescript/public-api.ts @@ -62,6 +62,14 @@ export { K_DEFAULT_VAULT_DEV_ROOT_TOKEN, } from "./vault-test-server/vault-test-server"; +export { + ILocalStackContainerOptions, + LocalStackContainer, + K_DEFAULT_LOCALSTACK_HTTP_PORT, + K_DEFAULT_LOCALSTACK_IMAGE_NAME, + K_DEFAULT_LOCALSTACK_IMAGE_VERSION, +} from "./localstack/localstack"; + export { CORDA_CONNECTOR_DEFAULT_OPTIONS, CORDA_CONNECTOR_OPTIONS_JOI_SCHEMA,