From 8dda0f61937c6e1a85afee0345af44b1bfa09c0a Mon Sep 17 00:00:00 2001 From: Peter Somogyvari Date: Fri, 2 Apr 2021 13:32:28 -0700 Subject: [PATCH] feat(api-server): ability to install plugins at runtime #764 The API server will now use npm as a dependency to install plugin packages before trying to import them (via require()) This is helpful in being able to ship a container image that contains the API server and does not need additional chores to be done in order to be able to use plugins (such as starting a new node package with its own dependencies). The security implications here are of course that we are pulling code form the internet at runtime and if npm's verification method for the code fails or if the plugin package is malicious to begin with then that's a full breach but these were security implications of the plugin architecture to begin with so there is not much additional that we can probably do here apart from a few addditional guard rails to protect users from themselves by maybe always requiring explicit versions so that auto-upgrade is out of the question. Fixes #764 Signed-off-by: Peter Somogyvari --- package.json | 1 + .../cactus-cmd-api-server/package-lock.json | 1774 +++++++++++++++++ packages/cactus-cmd-api-server/package.json | 4 +- .../src/main/typescript/api-server.ts | 56 + .../plugin-import-with-npm-install.test.ts | 138 ++ .../runtime-plugin-imports.test.ts | 69 +- .../integration/tap-parallel-not-ok | 0 .../package-lock.json | 67 +- .../cactus-test-cmd-api-server/package.json | 4 +- .../plugin-import-with-npm-install.test.ts | 77 + .../runtime-plugin-imports.test.ts | 3 +- .../integration/tap-parallel-not-ok | 0 12 files changed, 2064 insertions(+), 129 deletions(-) create mode 100644 packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts create mode 100644 packages/cactus-cmd-api-server/src/test/typescript/integration/tap-parallel-not-ok create mode 100644 packages/cactus-test-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts create mode 100644 packages/cactus-test-cmd-api-server/src/test/typescript/integration/tap-parallel-not-ok diff --git a/package.json b/package.json index e66bf05054..ef8d281bdf 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "build:dev:backend": "npm-run-all lint clean tsc", "build:dev:frontend": "lerna run build:dev:frontend", "build:dev:cmd-api-server": "lerna exec --stream --scope '*/*api-server' -- 'del-cli dist/** && tsc --project ./tsconfig.json && webpack --env=dev --target=node --config ../../webpack.config.js'", + "test:cmd-api-server": "tap --timeout=600 \"packages/cactus-*cmd-api-server/src/test/typescript/{unit,integration}/\"", "build:dev:common": "lerna exec --stream --scope '*/*common' -- 'del-cli dist/** && tsc --project ./tsconfig.json && webpack --env=dev --target=node --config ../../webpack.config.js'", "build:dev:core-api": "lerna exec --stream --scope '*/*core-api' -- 'del-cli dist/** && tsc --project ./tsconfig.json && webpack --env=dev --target=node --config ../../webpack.config.js'", "build:dev:test-tooling": "lerna exec --stream --scope '*/*test-tooling' -- 'del-cli dist/** && tsc --project ./tsconfig.json && webpack --env=dev --target=node --config ../../webpack.config.js'", diff --git a/packages/cactus-cmd-api-server/package-lock.json b/packages/cactus-cmd-api-server/package-lock.json index 8104c7db97..2e87a7d7f6 100644 --- a/packages/cactus-cmd-api-server/package-lock.json +++ b/packages/cactus-cmd-api-server/package-lock.json @@ -132,6 +132,15 @@ "@types/node": "*" } }, + "@types/npm": { + "version": "2.0.31", + "resolved": "https://registry.npmjs.org/@types/npm/-/npm-2.0.31.tgz", + "integrity": "sha512-v4JpUx83wVGItleYsnYeZrM8NTLSnYDfTE/iGm4owy6zZPNFNmnsvvrxiYtG3cVHt/XutzTjUBQ9Bh8bnvEkCw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/qs": { "version": "6.9.5", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", @@ -775,6 +784,1771 @@ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" }, + "npm": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-7.8.0.tgz", + "integrity": "sha512-9AC3Dj9OUWaUdmTmEVttE/1MWkfF7+sAKPRo9tKEyjo49AXmHQBn+RC33M9dima91mEMqDIA71xyRm4VmhDipg==", + "requires": { + "@npmcli/arborist": "^2.3.0", + "@npmcli/ci-detect": "^1.2.0", + "@npmcli/config": "^2.1.0", + "@npmcli/run-script": "^1.8.4", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "archy": "~1.0.0", + "byte-size": "^7.0.1", + "cacache": "^15.0.6", + "chalk": "^4.1.0", + "chownr": "^2.0.0", + "cli-columns": "^3.1.2", + "cli-table3": "^0.6.0", + "columnify": "~1.5.4", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "hosted-git-info": "^4.0.2", + "ini": "^2.0.0", + "init-package-json": "^2.0.2", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "leven": "^3.1.0", + "libnpmaccess": "^4.0.1", + "libnpmdiff": "^2.0.4", + "libnpmfund": "^1.0.2", + "libnpmhook": "^6.0.1", + "libnpmorg": "^2.0.1", + "libnpmpack": "^2.0.1", + "libnpmpublish": "^4.0.0", + "libnpmsearch": "^3.1.0", + "libnpmteam": "^2.0.2", + "libnpmversion": "^1.1.0", + "make-fetch-happen": "^8.0.14", + "minipass": "^3.1.3", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^7.1.2", + "nopt": "^5.0.0", + "npm-audit-report": "^2.1.4", + "npm-package-arg": "^8.1.2", + "npm-pick-manifest": "^6.1.1", + "npm-profile": "^5.0.2", + "npm-registry-fetch": "^9.0.0", + "npm-user-validate": "^1.0.1", + "npmlog": "~4.1.2", + "opener": "^1.5.2", + "pacote": "^11.3.1", + "parse-conflict-json": "^1.1.1", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^3.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "ssri": "^8.0.1", + "tar": "^6.1.0", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^1.0.4", + "validate-npm-package-name": "~3.0.0", + "which": "^2.0.2", + "write-file-atomic": "^3.0.3" + }, + "dependencies": { + "@npmcli/arborist": { + "version": "2.3.0", + "bundled": true, + "requires": { + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^1.0.2", + "@npmcli/metavuln-calculator": "^1.1.0", + "@npmcli/move-file": "^1.1.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^1.0.1", + "@npmcli/run-script": "^1.8.2", + "bin-links": "^2.2.1", + "cacache": "^15.0.3", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.2", + "mkdirp-infer-owner": "^2.0.0", + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.1.0", + "npm-pick-manifest": "^6.1.0", + "npm-registry-fetch": "^9.0.0", + "pacote": "^11.2.6", + "parse-conflict-json": "^1.1.1", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "semver": "^7.3.5", + "tar": "^6.1.0", + "treeverse": "^1.0.4", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/ci-detect": { + "version": "1.3.0", + "bundled": true + }, + "@npmcli/config": { + "version": "2.1.0", + "bundled": true, + "requires": { + "ini": "^2.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "semver": "^7.3.4", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/disparity-colors": { + "version": "1.0.1", + "bundled": true, + "requires": { + "ansi-styles": "^4.3.0" + } + }, + "@npmcli/git": { + "version": "2.0.6", + "bundled": true, + "requires": { + "@npmcli/promise-spawn": "^1.1.0", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.3", + "npm-pick-manifest": "^6.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.2", + "unique-filename": "^1.1.1", + "which": "^2.0.2" + } + }, + "@npmcli/installed-package-contents": { + "version": "1.0.7", + "bundled": true, + "requires": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "@npmcli/map-workspaces": { + "version": "1.0.3", + "bundled": true, + "requires": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^7.1.6", + "minimatch": "^3.0.4", + "read-package-json-fast": "^2.0.1" + } + }, + "@npmcli/metavuln-calculator": { + "version": "1.1.1", + "bundled": true, + "requires": { + "cacache": "^15.0.5", + "pacote": "^11.1.11", + "semver": "^7.3.2" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "bundled": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@npmcli/name-from-folder": { + "version": "1.0.1", + "bundled": true + }, + "@npmcli/node-gyp": { + "version": "1.0.2", + "bundled": true + }, + "@npmcli/promise-spawn": { + "version": "1.3.2", + "bundled": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "1.8.4", + "bundled": true, + "requires": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "infer-owner": "^1.0.4", + "node-gyp": "^7.1.0", + "read-package-json-fast": "^2.0.1" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "bundled": true + }, + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "agent-base": { + "version": "6.0.2", + "bundled": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.1.4", + "bundled": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "bundled": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "bundled": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true + }, + "aproba": { + "version": "2.0.0", + "bundled": true + }, + "archy": { + "version": "1.0.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "asap": { + "version": "2.0.6", + "bundled": true + }, + "asn1": { + "version": "0.2.4", + "bundled": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "bundled": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true + }, + "aws-sign2": { + "version": "0.7.0", + "bundled": true + }, + "aws4": { + "version": "1.11.0", + "bundled": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "bundled": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bin-links": { + "version": "2.2.1", + "bundled": true, + "requires": { + "cmd-shim": "^4.0.1", + "mkdirp": "^1.0.3", + "npm-normalize-package-bin": "^1.0.0", + "read-cmd-shim": "^2.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^3.0.3" + } + }, + "binary-extensions": { + "version": "2.2.0", + "bundled": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "builtins": { + "version": "1.0.3", + "bundled": true + }, + "byte-size": { + "version": "7.0.1", + "bundled": true + }, + "cacache": { + "version": "15.0.6", + "bundled": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "caseless": { + "version": "0.12.0", + "bundled": true + }, + "chalk": { + "version": "4.1.0", + "bundled": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chownr": { + "version": "2.0.0", + "bundled": true + }, + "cidr-regex": { + "version": "3.1.1", + "bundled": true, + "requires": { + "ip-regex": "^4.1.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "bundled": true + }, + "cli-columns": { + "version": "3.1.2", + "bundled": true, + "requires": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "cli-table3": { + "version": "0.6.0", + "bundled": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "bundled": true + }, + "string-width": { + "version": "4.2.2", + "bundled": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true + }, + "cmd-shim": { + "version": "4.1.0", + "bundled": true, + "requires": { + "mkdirp-infer-owner": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true + }, + "colors": { + "version": "1.4.0", + "bundled": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.8", + "bundled": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "common-ancestor-path": { + "version": "1.0.1", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "4.3.1", + "bundled": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "bundled": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "requires": { + "clone": "^1.0.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "depd": { + "version": "1.1.2", + "bundled": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "diff": { + "version": "5.0.0", + "bundled": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "bundled": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "bundled": true + }, + "encoding": { + "version": "0.1.13", + "bundled": true, + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "env-paths": { + "version": "2.2.1", + "bundled": true + }, + "err-code": { + "version": "2.0.3", + "bundled": true + }, + "extend": { + "version": "3.0.2", + "bundled": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "bundled": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "bundled": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true + }, + "form-data": { + "version": "2.3.3", + "bundled": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-minipass": { + "version": "2.1.0", + "bundled": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "function-bind": { + "version": "1.1.1", + "bundled": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.6", + "bundled": true + }, + "har-schema": { + "version": "2.0.0", + "bundled": true + }, + "har-validator": { + "version": "5.1.5", + "bundled": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "bundled": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "bundled": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "hosted-git-info": { + "version": "4.0.2", + "bundled": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "bundled": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "bundled": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http-signature": { + "version": "1.2.0", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "bundled": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.6.2", + "bundled": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true + }, + "indent-string": { + "version": "4.0.0", + "bundled": true + }, + "infer-owner": { + "version": "1.0.4", + "bundled": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true + }, + "ini": { + "version": "2.0.0", + "bundled": true + }, + "init-package-json": { + "version": "2.0.2", + "bundled": true, + "requires": { + "glob": "^7.1.1", + "npm-package-arg": "^8.1.0", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "^3.0.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^3.0.0" + } + }, + "ip": { + "version": "1.1.5", + "bundled": true + }, + "ip-regex": { + "version": "4.3.0", + "bundled": true + }, + "is-cidr": { + "version": "4.0.2", + "bundled": true, + "requires": { + "cidr-regex": "^3.1.1" + } + }, + "is-core-module": { + "version": "2.2.0", + "bundled": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "is-lambda": { + "version": "1.0.1", + "bundled": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "bundled": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "bundled": true + }, + "json-stringify-nice": { + "version": "1.1.3", + "bundled": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "just-diff": { + "version": "3.0.2", + "bundled": true + }, + "just-diff-apply": { + "version": "3.0.0", + "bundled": true + }, + "leven": { + "version": "3.1.0", + "bundled": true + }, + "libnpmaccess": { + "version": "4.0.1", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^8.0.0", + "npm-registry-fetch": "^9.0.0" + } + }, + "libnpmdiff": { + "version": "2.0.4", + "bundled": true, + "requires": { + "@npmcli/disparity-colors": "^1.0.1", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.0.0", + "minimatch": "^3.0.4", + "npm-package-arg": "^8.1.1", + "pacote": "^11.3.0", + "tar": "^6.1.0" + } + }, + "libnpmfund": { + "version": "1.0.2", + "bundled": true, + "requires": { + "@npmcli/arborist": "^2.0.0" + } + }, + "libnpmhook": { + "version": "6.0.1", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^9.0.0" + } + }, + "libnpmorg": { + "version": "2.0.1", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^9.0.0" + } + }, + "libnpmpack": { + "version": "2.0.1", + "bundled": true, + "requires": { + "@npmcli/run-script": "^1.8.3", + "npm-package-arg": "^8.1.0", + "pacote": "^11.2.6" + } + }, + "libnpmpublish": { + "version": "4.0.0", + "bundled": true, + "requires": { + "normalize-package-data": "^3.0.0", + "npm-package-arg": "^8.1.0", + "npm-registry-fetch": "^9.0.0", + "semver": "^7.1.3", + "ssri": "^8.0.0" + } + }, + "libnpmsearch": { + "version": "3.1.0", + "bundled": true, + "requires": { + "npm-registry-fetch": "^9.0.0" + } + }, + "libnpmteam": { + "version": "2.0.2", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^9.0.0" + } + }, + "libnpmversion": { + "version": "1.1.0", + "bundled": true, + "requires": { + "@npmcli/git": "^2.0.6", + "@npmcli/run-script": "^1.8.3", + "json-parse-even-better-errors": "^2.3.1", + "semver": "^7.3.4", + "stringify-package": "^1.0.1" + } + }, + "lru-cache": { + "version": "6.0.0", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-fetch-happen": { + "version": "8.0.14", + "bundled": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.0.5", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^5.0.0", + "ssri": "^8.0.0" + } + }, + "mime-db": { + "version": "1.46.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.29", + "bundled": true, + "requires": { + "mime-db": "1.46.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "3.1.3", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "bundled": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.3.3", + "bundled": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "bundled": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-json-stream": { + "version": "1.0.1", + "bundled": true, + "requires": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "bundled": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "bundled": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "bundled": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "bundled": true + }, + "mkdirp-infer-owner": { + "version": "2.0.0", + "bundled": true, + "requires": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + } + }, + "ms": { + "version": "2.1.3", + "bundled": true + }, + "mute-stream": { + "version": "0.0.8", + "bundled": true + }, + "node-gyp": { + "version": "7.1.2", + "bundled": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.3", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "request": "^2.88.2", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "tar": "^6.0.2", + "which": "^2.0.2" + } + }, + "nopt": { + "version": "5.0.0", + "bundled": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "bundled": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-audit-report": { + "version": "2.1.4", + "bundled": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-install-checks": { + "version": "4.0.0", + "bundled": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true + }, + "npm-package-arg": { + "version": "8.1.2", + "bundled": true, + "requires": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "2.1.5", + "bundled": true, + "requires": { + "glob": "^7.1.6", + "ignore-walk": "^3.0.3", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "6.1.1", + "bundled": true, + "requires": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "npm-profile": { + "version": "5.0.2", + "bundled": true, + "requires": { + "npm-registry-fetch": "^9.0.0" + } + }, + "npm-registry-fetch": { + "version": "9.0.0", + "bundled": true, + "requires": { + "@npmcli/ci-detect": "^1.0.0", + "lru-cache": "^6.0.0", + "make-fetch-happen": "^8.0.9", + "minipass": "^3.1.3", + "minipass-fetch": "^1.3.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.0.0", + "npm-package-arg": "^8.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.1", + "bundled": true + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.9.0", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.5.2", + "bundled": true + }, + "p-map": { + "version": "4.0.0", + "bundled": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "pacote": { + "version": "11.3.1", + "bundled": true, + "requires": { + "@npmcli/git": "^2.0.1", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^1.8.2", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^2.1.4", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^9.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + } + }, + "parse-conflict-json": { + "version": "1.1.1", + "bundled": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "just-diff": "^3.0.1", + "just-diff-apply": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "path-parse": { + "version": "1.0.6", + "bundled": true + }, + "performance-now": { + "version": "2.1.0", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.1", + "bundled": true + }, + "promise-all-reject-late": { + "version": "1.0.1", + "bundled": true + }, + "promise-call-limit": { + "version": "1.0.1", + "bundled": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true + }, + "promise-retry": { + "version": "2.0.1", + "bundled": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "requires": { + "read": "1" + } + }, + "psl": { + "version": "1.8.0", + "bundled": true + }, + "punycode": { + "version": "2.1.1", + "bundled": true + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true + }, + "qs": { + "version": "6.5.2", + "bundled": true + }, + "read": { + "version": "1.0.7", + "bundled": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "2.0.0", + "bundled": true + }, + "read-package-json": { + "version": "3.0.1", + "bundled": true, + "requires": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^3.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "read-package-json-fast": { + "version": "2.0.2", + "bundled": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "request": { + "version": "2.88.2", + "bundled": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "bundled": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "resolve": { + "version": "1.20.0", + "bundled": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "retry": { + "version": "0.12.0", + "bundled": true + }, + "rimraf": { + "version": "3.0.2", + "bundled": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true + }, + "semver": { + "version": "7.3.5", + "bundled": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.3", + "bundled": true + }, + "smart-buffer": { + "version": "4.1.0", + "bundled": true + }, + "socks": { + "version": "2.6.0", + "bundled": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "5.0.0", + "bundled": true, + "requires": { + "agent-base": "6", + "debug": "4", + "socks": "^2.3.3" + } + }, + "spdx-correct": { + "version": "3.1.1", + "bundled": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "bundled": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "bundled": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "bundled": true + }, + "sshpk": { + "version": "1.16.1", + "bundled": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "8.0.1", + "bundled": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringify-package": { + "version": "1.0.1", + "bundled": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "bundled": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tar": { + "version": "6.1.0", + "bundled": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true + }, + "treeverse": { + "version": "1.0.4", + "bundled": true + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "bundled": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "unique-filename": { + "version": "1.1.1", + "bundled": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "bundled": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "uri-js": { + "version": "4.4.1", + "bundled": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "uuid": { + "version": "3.4.0", + "bundled": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "bundled": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "bundled": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "walk-up-path": { + "version": "1.0.0", + "bundled": true + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "2.0.2", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "write-file-atomic": { + "version": "3.0.3", + "bundled": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "yallist": { + "version": "4.0.0", + "bundled": true + } + } + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", diff --git a/packages/cactus-cmd-api-server/package.json b/packages/cactus-cmd-api-server/package.json index 44fcd21b7f..a40e0bddbe 100644 --- a/packages/cactus-cmd-api-server/package.json +++ b/packages/cactus-cmd-api-server/package.json @@ -90,8 +90,6 @@ "@hyperledger/cactus-common": "0.4.0", "@hyperledger/cactus-core": "0.4.1", "@hyperledger/cactus-core-api": "0.4.1", - "@hyperledger/cactus-plugin-consortium-manual": "0.4.1", - "@hyperledger/cactus-plugin-keychain-memory": "0.4.1", "axios": "0.21.1", "body-parser": "1.19.0", "compression": "1.7.4", @@ -103,6 +101,7 @@ "express-openapi-validator": "3.10.0", "jose": "1.27.2", "node-forge": "0.10.0", + "npm": "7.8.0", "prom-client": "13.1.0", "semver": "7.3.2", "uuid": "7.0.2" @@ -117,6 +116,7 @@ "@types/express-http-proxy": "1.6.1", "@types/multer": "1.4.5", "@types/node-forge": "0.9.3", + "@types/npm": "2.0.31", "@types/semver": "7.3.1", "@types/uuid": "7.0.2" } diff --git a/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts b/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts index a646758d79..4f2a13472b 100644 --- a/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts +++ b/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts @@ -5,6 +5,7 @@ import tls from "tls"; import { Server, createServer } from "http"; import { Server as SecureServer } from "https"; import { createServer as createSecureServer } from "https"; +import npm from "npm"; import expressHttpProxy from "express-http-proxy"; import express, { Express, @@ -24,6 +25,7 @@ import { IPluginWebService, IPluginFactoryOptions, PluginFactoryFactory, + PluginImport, } from "@hyperledger/cactus-core-api"; import { PluginRegistry } from "@hyperledger/cactus-core"; @@ -203,6 +205,8 @@ export class ApiServer { this.log.info(`Creating plugin from package: ${packageName}`, options); const pluginOptions = { ...options, logLevel, pluginRegistry: registry }; + await this.installPluginPackage(pluginImport); + const pluginPackage = require(/* webpackIgnore: true */ packageName); const createPluginFactory = pluginPackage.createPluginFactory as PluginFactoryFactory; @@ -220,6 +224,58 @@ export class ApiServer { return registry; } + private async installPluginPackage( + pluginImport: PluginImport, + ): Promise { + const fnTag = `ApiServer#installPluginPackage()`; + const { packageName: pkgName } = pluginImport; + + const npmLogHandler = (message: unknown) => { + this.log.debug(`${fnTag} [npm-log]:`, message); + }; + + const cleanUpNpmLogHandler = () => { + npm.off("log", npmLogHandler); + }; + + try { + this.log.info(`Installing ${pkgName} for plugin import`, pluginImport); + npm.on("log", npmLogHandler); + + await new Promise((resolve, reject) => { + npm.load((err?: Error) => { + if (err) { + this.log.error(`${fnTag} npm load fail:`, err); + const { message, stack } = err; + reject(new Error(`${fnTag} npm load fail: ${message}: ${stack}`)); + } else { + npm.config.set("save", false); + npm.config.set("audit", false); + npm.config.set("progress", false); + resolve(); + } + }); + }); + + await new Promise((resolve, reject) => { + const npmInstallHandler = (errInstall?: Error, result?: unknown) => { + if (errInstall) { + this.log.error(`${fnTag} npm install failed:`, errInstall); + const { message: m, stack } = errInstall; + reject(new Error(`${fnTag} npm install fail: ${m}: ${stack}`)); + } else { + this.log.info(`Installed ${pkgName} OK`, result); + resolve(result); + } + }; + + npm.commands.install([pkgName], npmInstallHandler); + }); + } finally { + cleanUpNpmLogHandler(); + } + } + public async shutdown(): Promise { this.log.info(`Shutting down API server ...`); diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts new file mode 100644 index 0000000000..649ecd8b4e --- /dev/null +++ b/packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts @@ -0,0 +1,138 @@ +import test, { Test } from "tape-promise/tape"; +import { v4 as uuidv4 } from "uuid"; +import { JWK } from "jose"; + +import { LogLevelDesc } from "@hyperledger/cactus-common"; + +import { + PluginImportType, + ConsortiumDatabase, + ICactusPlugin, +} from "@hyperledger/cactus-core-api"; + +import { ApiServer, ConfigService } from "../../../main/typescript/public-api"; + +import { K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS } from "../../../main/typescript/prometheus-exporter/metrics"; + +import { DefaultApi as ApiServerApi } from "../../../main/typescript/public-api"; + +const logLevel: LogLevelDesc = "TRACE"; + +test("can instal plugins at runtime based on imports", async (t: Test) => { + // Adding a new plugin to update the prometheus metric K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + const keyPair = await JWK.generate("EC", "secp256k1", { use: "sig" }, true); + const keyPairPem = keyPair.toPEM(true); + const db: ConsortiumDatabase = { + cactusNode: [], + consortium: [], + consortiumMember: [], + ledger: [], + pluginInstance: [], + }; + + const configService = new ConfigService(); + const apiServerOptions = configService.newExampleConfig(); + apiServerOptions.configFile = ""; + apiServerOptions.apiCorsDomainCsv = "*"; + apiServerOptions.apiPort = 0; + apiServerOptions.cockpitPort = 0; + apiServerOptions.apiTlsEnabled = false; + apiServerOptions.plugins = [ + { + packageName: "@hyperledger/cactus-plugin-keychain-memory", + type: PluginImportType.LOCAL, + options: { + instanceId: uuidv4(), + keychainId: uuidv4(), + logLevel, + }, + }, + { + packageName: "@hyperledger/cactus-plugin-consortium-manual", + type: PluginImportType.LOCAL, + options: { + instanceId: uuidv4(), + keyPairPem: keyPairPem, + consortiumDatabase: db, + }, + }, + ]; + const config = configService.newExampleConfigConvict(apiServerOptions); + + const apiServer = new ApiServer({ + config: config.getProperties(), + }); + + const startResponse = apiServer.start(); + await t.doesNotReject( + startResponse, + "failed to start API server with dynamic plugin imports configured for it...", + ); + t.ok(startResponse, "startResponse truthy OK"); + + const addressInfoApi = (await startResponse).addressInfoApi; + const protocol = apiServerOptions.apiTlsEnabled ? "https" : "http"; + const { address, port } = addressInfoApi; + const apiHost = `${protocol}://${address}:${port}`; + t.comment( + `Metrics URL: ${apiHost}/api/v1/api-server/get-prometheus-exporter-metrics`, + ); + + const apiClient = new ApiServerApi({ basePath: apiHost }); + + { + const res = await apiClient.getPrometheusExporterMetricsV1(); + const promMetricsOutput = + "# HELP " + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + " Total number of plugins imported\n" + + "# TYPE " + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + " gauge\n" + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + '{type="' + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + '"} 2'; + t.ok(res); + t.ok(res.data); + t.equal(res.status, 200); + t.true( + res.data.includes(promMetricsOutput), + "Total 2 plugins imported as expected. RESULT OK", + ); + } + + const pluginRegistry = await apiServer.getOrInitPluginRegistry(); + + // this is not a working plugin but we are just testing the monitoring so + // it's okay for this particular test case. Do not copy this to other test + // cases or if you do remove right after you copied it ;-) + pluginRegistry.plugins.push({} as ICactusPlugin); + + { + const res = await apiClient.getPrometheusExporterMetricsV1(); + const promMetricsOutput = + "# HELP " + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + " Total number of plugins imported\n" + + "# TYPE " + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + " gauge\n" + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + '{type="' + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + '"} 3'; + t.ok(res); + t.ok(res.data); + t.equal(res.status, 200); + t.true( + res.data.includes(promMetricsOutput), + "Total 3 plugins imported as expected. RESULT OK", + ); + } + // clean up after ourselves, + // e.g. remove the dummy plugin instance we just pushed + pluginRegistry.plugins.pop(); + + test.onFinish(() => apiServer.shutdown()); +}); diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts index 73ca5a4e3a..bf0d336ca9 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts @@ -1,20 +1,9 @@ import test, { Test } from "tape-promise/tape"; import { v4 as uuidv4 } from "uuid"; -import { JWK } from "jose"; import { LogLevelDesc } from "@hyperledger/cactus-common"; -import { IPluginKeychainMemoryOptions } from "@hyperledger/cactus-plugin-keychain-memory"; - -import { - PluginConsortiumManual, - IPluginConsortiumManualOptions, -} from "@hyperledger/cactus-plugin-consortium-manual"; - -import { - PluginImportType, - ConsortiumDatabase, -} from "@hyperledger/cactus-core-api"; +import { PluginImportType } from "@hyperledger/cactus-core-api"; import { ApiServer, ConfigService } from "../../../main/typescript/public-api"; @@ -40,7 +29,7 @@ test("can import plugins at runtime (CLI)", async (t: Test) => { instanceId: uuidv4(), keychainId: uuidv4(), logLevel, - } as IPluginKeychainMemoryOptions, + }, }, ]; const config = configService.newExampleConfigConvict(apiServerOptions); @@ -50,10 +39,7 @@ test("can import plugins at runtime (CLI)", async (t: Test) => { }); const startResponse = apiServer.start(); - await t.doesNotReject( - startResponse, - "failed to start API server with dynamic plugin imports configured for it...", - ); + await t.doesNotReject(startResponse, "started API server dynamic imports OK"); t.ok(startResponse, "startResponse truthy OK"); const addressInfoApi = (await startResponse).addressInfoApi; @@ -65,9 +51,6 @@ test("can import plugins at runtime (CLI)", async (t: Test) => { ); const apiClient = new ApiServerApi({ basePath: apiHost }); - apiServer.prometheusExporter.setTotalPluginImports( - apiServer.getPluginImportsCount(), - ); { const res = await apiClient.getPrometheusExporterMetricsV1(); @@ -91,51 +74,5 @@ test("can import plugins at runtime (CLI)", async (t: Test) => { ); } - // Adding a new plugin to update the prometheus metric K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS - const keyPair = await JWK.generate("EC", "secp256k1", { use: "sig" }, true); - const keyPairPem = keyPair.toPEM(true); - const db: ConsortiumDatabase = { - cactusNode: [], - consortium: [], - consortiumMember: [], - ledger: [], - pluginInstance: [], - }; - - const options: IPluginConsortiumManualOptions = { - instanceId: uuidv4(), - keyPairPem: keyPairPem, - consortiumDatabase: db, - }; - - const pluginConsortiumManual: PluginConsortiumManual = new PluginConsortiumManual( - options, - ); - - const pluginRegistry = await apiServer.getOrInitPluginRegistry(); - pluginRegistry.add(pluginConsortiumManual); - - { - const res = await apiClient.getPrometheusExporterMetricsV1(); - const promMetricsOutput = - "# HELP " + - K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + - " Total number of plugins imported\n" + - "# TYPE " + - K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + - " gauge\n" + - K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + - '{type="' + - K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + - '"} 2'; - t.ok(res); - t.ok(res.data); - t.equal(res.status, 200); - t.true( - res.data.includes(promMetricsOutput), - "Total 2 plugins imported as expected. RESULT OK", - ); - } - test.onFinish(() => apiServer.shutdown()); }); diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/tap-parallel-not-ok b/packages/cactus-cmd-api-server/src/test/typescript/integration/tap-parallel-not-ok new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cactus-test-cmd-api-server/package-lock.json b/packages/cactus-test-cmd-api-server/package-lock.json index 80fc6293c3..3337f3a415 100644 --- a/packages/cactus-test-cmd-api-server/package-lock.json +++ b/packages/cactus-test-cmd-api-server/package-lock.json @@ -4,65 +4,18 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@types/joi": { - "version": "14.3.4", - "resolved": "https://registry.npmjs.org/@types/joi/-/joi-14.3.4.tgz", - "integrity": "sha512-1TQNDJvIKlgYXGNIABfgFp9y0FziDpuGrd799Q5RcnsDu+krD+eeW/0Fs5PHARvWWFelOhIG2OPCo6KbadBM4A==", - "dev": true - }, - "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" - } - }, - "follow-redirects": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz", - "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==" - }, - "hoek": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", - "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==" - }, - "isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", - "requires": { - "punycode": "2.x.x" - } - }, - "joi": { - "version": "14.3.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-14.3.1.tgz", - "integrity": "sha512-LQDdM+pkOrpAn4Lp+neNIFV3axv1Vna3j38bisbQhETPMANYRbFJFUyOZcOClYvM/hppMhGWuKSFEK9vjrB+bQ==", + "@panva/asn1.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", + "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==" + }, + "jose": { + "version": "1.27.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-1.27.2.tgz", + "integrity": "sha512-zLIwnMa8dh5A2jFo56KvhiXCaW0hFjdNvG0I5GScL8Wro+/r/SnyIYTbnX3fYztPNSfgQp56sDMHUuS9c3e6bw==", "requires": { - "hoek": "6.x.x", - "isemail": "3.x.x", - "topo": "3.x.x" + "@panva/asn1.js": "^1.0.0" } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "topo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", - "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", - "requires": { - "hoek": "6.x.x" - } - }, - "typescript-optional": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/typescript-optional/-/typescript-optional-2.0.1.tgz", - "integrity": "sha512-xuwmqsCjE4OeeMKxbNX3jjNcISGzYh5Q9R1rM5OyxEVNIr94CB5llCkfKW+1nZTKbbUV0axN3QAUuX2fus/DhQ==" } } } diff --git a/packages/cactus-test-cmd-api-server/package.json b/packages/cactus-test-cmd-api-server/package.json index debb98d944..e38f08320b 100644 --- a/packages/cactus-test-cmd-api-server/package.json +++ b/packages/cactus-test-cmd-api-server/package.json @@ -85,8 +85,8 @@ "@hyperledger/cactus-common": "0.4.0", "@hyperledger/cactus-core": "0.4.1", "@hyperledger/cactus-core-api": "0.4.1", - "@hyperledger/cactus-plugin-keychain-memory": "0.4.1", - "@hyperledger/cactus-plugin-keychain-vault": "0.4.1" + "@hyperledger/cactus-plugin-keychain-vault": "0.4.1", + "jose": "1.27.2" }, "devDependencies": { "@hyperledger/cactus-test-tooling": "0.4.0" diff --git a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts new file mode 100644 index 0000000000..20c5d3bfed --- /dev/null +++ b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts @@ -0,0 +1,77 @@ +import test, { Test } from "tape-promise/tape"; +import { v4 as uuidv4 } from "uuid"; +import { JWK } from "jose"; + +import { LogLevelDesc } from "@hyperledger/cactus-common"; + +import { + PluginImportType, + ConsortiumDatabase, +} from "@hyperledger/cactus-core-api"; + +import { ApiServer, ConfigService } from "@hyperledger/cactus-cmd-api-server"; + +const logLevel: LogLevelDesc = "TRACE"; + +test("can instal plugins at runtime based on imports", async (t: Test) => { + // Adding a new plugin to update the prometheus metric K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + const keyPair = await JWK.generate("EC", "secp256k1", { use: "sig" }, true); + const keyPairPem = keyPair.toPEM(true); + const db: ConsortiumDatabase = { + cactusNode: [], + consortium: [], + consortiumMember: [], + ledger: [], + pluginInstance: [], + }; + + const configService = new ConfigService(); + const apiServerOptions = configService.newExampleConfig(); + apiServerOptions.configFile = ""; + apiServerOptions.apiCorsDomainCsv = "*"; + apiServerOptions.apiPort = 0; + apiServerOptions.cockpitPort = 0; + apiServerOptions.apiTlsEnabled = false; + apiServerOptions.plugins = [ + { + packageName: "@hyperledger/cactus-plugin-keychain-memory", + type: PluginImportType.LOCAL, + options: { + instanceId: uuidv4(), + keychainId: uuidv4(), + logLevel, + }, + }, + { + packageName: "@hyperledger/cactus-plugin-consortium-manual", + type: PluginImportType.LOCAL, + options: { + instanceId: uuidv4(), + keyPairPem: keyPairPem, + consortiumDatabase: db, + }, + }, + ]; + const config = configService.newExampleConfigConvict(apiServerOptions); + + const apiServer = new ApiServer({ + config: config.getProperties(), + }); + + const startResponse = apiServer.start(); + await t.doesNotReject( + startResponse, + "failed to start API server with dynamic plugin imports configured for it...", + ); + t.ok(startResponse, "startResponse truthy OK"); + + const addressInfoApi = (await startResponse).addressInfoApi; + const protocol = apiServerOptions.apiTlsEnabled ? "https" : "http"; + const { address, port } = addressInfoApi; + const apiHost = `${protocol}://${address}:${port}`; + t.comment( + `Metrics URL: ${apiHost}/api/v1/api-server/get-prometheus-exporter-metrics`, + ); + + test.onFinish(() => apiServer.shutdown()); +}); diff --git a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts index 7c682e2c64..474be9c40f 100644 --- a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts +++ b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts @@ -4,7 +4,6 @@ import { v4 as uuidv4 } from "uuid"; import { LogLevelDesc } from "@hyperledger/cactus-common"; import { ApiServer, ConfigService } from "@hyperledger/cactus-cmd-api-server"; -import { IPluginKeychainMemoryOptions } from "@hyperledger/cactus-plugin-keychain-memory"; import { PluginImportType } from "@hyperledger/cactus-core-api"; const logLevel: LogLevelDesc = "TRACE"; @@ -27,7 +26,7 @@ test("can import plugins at runtime (CLI)", async (t: Test) => { instanceId: uuidv4(), keychainId: uuidv4(), logLevel, - } as IPluginKeychainMemoryOptions, + }, }, ]; const config = configService.newExampleConfigConvict(apiServerOptions); diff --git a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/tap-parallel-not-ok b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/tap-parallel-not-ok new file mode 100644 index 0000000000..e69de29bb2