diff --git a/KeeperSdk/package-lock.json b/KeeperSdk/package-lock.json new file mode 100644 index 0000000..d8c7acd --- /dev/null +++ b/KeeperSdk/package-lock.json @@ -0,0 +1,624 @@ +{ + "name": "@keeper-security/keeper-sdk-javascript", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@keeper-security/keeper-sdk-javascript", + "version": "1.0.0", + "dependencies": { + "@keeper-security/keeperapi": "17.1.0", + "ts-node": "^10.7.0", + "typescript": "^4.6.3" + }, + "devDependencies": { + "@types/node": "^25.6.0", + "prettier": "^3.8.1" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@keeper-security/keeperapi": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@keeper-security/keeperapi/-/keeperapi-17.1.0.tgz", + "integrity": "sha512-BV1o72g1BXz3Agjw0l6kLlS5SOvWCJ0qD2RcEXPAvMQJcq0O8SLaf2KhIG/dGHPHdNZ5Dod4+cI0sAfnpZeYZw==", + "license": "ISC", + "dependencies": { + "@noble/post-quantum": "^0.5.2", + "asmcrypto.js": "^2.3.2", + "faye-websocket": "^0.11.3", + "form-data": "^4.0.4", + "node-rsa": "^1.0.8" + } + }, + "node_modules/@noble/curves": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", + "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.0.1" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/post-quantum": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@noble/post-quantum/-/post-quantum-0.5.4.tgz", + "integrity": "sha512-leww0zzIirrvwaYMPI9fj6aRIlA/c6Y0/lifQQ1YOOyHEr0MNH3yYpjXeiVG+tWdPps4XxGclFWX2INPO3Yo5w==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~2.0.0", + "@noble/hashes": "~2.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.19.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "license": "MIT" + }, + "node_modules/asmcrypto.js": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/asmcrypto.js/-/asmcrypto.js-2.3.2.tgz", + "integrity": "sha512-3FgFARf7RupsZETQ1nHnhLUUvpcttcCq1iZCaVAbJZbCZ5VNRrNyvpDyHTOb0KC3llFcsyOT/a99NZcCbeiEsA==", + "license": "MIT" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-rsa": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-1.1.1.tgz", + "integrity": "sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==", + "license": "MIT", + "dependencies": { + "asn1": "^0.2.4" + } + }, + "node_modules/prettier": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/undici-types": { + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "license": "MIT" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/KeeperSdk/src/index.ts b/KeeperSdk/src/index.ts index 6f95e78..cc9bed2 100644 --- a/KeeperSdk/src/index.ts +++ b/KeeperSdk/src/index.ts @@ -26,6 +26,9 @@ export { SdkDefaults, AuthDefaults, ResultCodes, + AuthErrorCode, + SessionErrorCode, + TeamErrorCode, KEEPER_PUBLIC_HOSTS, isBoolean, isString, @@ -173,6 +176,57 @@ export type { AuthProvider, SharedFolderPermissionsInput } from './folders/Folde export { SharedFolderManager } from './sharedFolders/SharedFolderManager' +export { + listTeams, + formatTeamsTable, + renderTeamsAsciiTable, + formatTeamRestricts, + TeamColumn, + SUPPORTED_TEAM_COLUMNS, + DEFAULT_TEAM_COLUMNS, +} from './teams/listTeams' +export type { + ListTeamsOptions, + ListTeamRow, + TeamColumnInput, + FormattedTeamsTable, + FormatTeamsTableOptions, +} from './teams/listTeams' + +export { + EnterpriseDataInclude, + EnterpriseDataManager, + getEnterpriseData, + getEnterpriseDisplayNames, + getNodePath, +} from './teams/enterpriseData' +export type { + GetEnterpriseDataResponse, + EnterpriseTeamRecord, + EnterpriseTeamUserLink, + EnterpriseRoleUserLink, + EnterpriseRoleTeamLink, + EnterpriseUser, + EnterpriseRole, + EnterpriseNode, + DecryptedNodeNames, + DecryptedRoleNames, + EnterpriseDisplayNames, + NodePathOptions, +} from './teams/enterpriseData' + +export { viewTeam, formatTeamView, teamViewTable } from './teams/viewTeam' +export type { + TeamView, + TeamRoleInfo, + TeamUserInfo, + FormatTeamViewOptions, + FormattedTeamViewTable, + TeamViewTableRow, +} from './teams/viewTeam' + +export { TeamManager } from './teams/TeamManager' + export { Auth, KeeperEnvironment, syncDown, Authentication } from '@keeper-security/keeperapi' export type { diff --git a/KeeperSdk/src/teams/TeamManager.ts b/KeeperSdk/src/teams/TeamManager.ts new file mode 100644 index 0000000..139a3b6 --- /dev/null +++ b/KeeperSdk/src/teams/TeamManager.ts @@ -0,0 +1,61 @@ +import type { Auth } from '@keeper-security/keeperapi' +import { KeeperSdkError, ResultCodes } from '../utils' +import { + formatTeamsTable, + listTeams, + renderTeamsAsciiTable, + type FormatTeamsTableOptions, + type FormattedTeamsTable, + type ListTeamRow, + type ListTeamsOptions, +} from './listTeams' +import { + formatTeamView, + teamViewTable, + viewTeam, + type FormatTeamViewOptions, + type FormattedTeamViewTable, + type TeamView, +} from './viewTeam' + +export type AuthProvider = () => Auth + +export class TeamManager { + private readonly authProvider: AuthProvider + + constructor(authProvider: AuthProvider) { + this.authProvider = authProvider + } + + public async listTeams(options: ListTeamsOptions = {}): Promise { + return listTeams(this.requireAuth(), options) + } + + public formatTeamsTable(rows: ListTeamRow[], options: FormatTeamsTableOptions = {}): FormattedTeamsTable { + return formatTeamsTable(rows, options) + } + + public renderTeamsAsciiTable(table: FormattedTeamsTable, options: { minColWidth?: number } = {}): string { + return renderTeamsAsciiTable(table, options) + } + + public async viewTeam(identifier: string): Promise { + return viewTeam(this.requireAuth(), identifier) + } + + public formatTeamView(view: TeamView, options: FormatTeamViewOptions = {}): FormattedTeamViewTable { + return formatTeamView(view, options) + } + + public teamViewTable(table: FormattedTeamViewTable): string { + return teamViewTable(table) + } + + private requireAuth(): Auth { + const auth = this.authProvider() + if (!auth) { + throw new KeeperSdkError('You are not logged in. Please log in first.', ResultCodes.NOT_LOGGED_IN) + } + return auth + } +} diff --git a/KeeperSdk/src/teams/enterpriseData.ts b/KeeperSdk/src/teams/enterpriseData.ts new file mode 100644 index 0000000..aea2e6f --- /dev/null +++ b/KeeperSdk/src/teams/enterpriseData.ts @@ -0,0 +1,438 @@ +import { + decryptFromStorage, + decryptObjectFromStorage, + Enterprise, + getEnterpriseDataForUserMessage, + normal64Bytes, + platform, + webSafe64FromBytes, + type Auth, + type RestCommand, +} from '@keeper-security/keeperapi' +import { isNumber } from '../utils' + +const DEFAULT_NODE_PATH_SEPARATOR = '\\' +const MAX_CONTINUATIONS = 50 +const LEGACY_ENTERPRISE_DATA_COMMAND = 'get_enterprise_data' + +export enum EnterpriseDataInclude { + Nodes = 'nodes', + Users = 'users', + Roles = 'roles', + RoleUsers = 'role_users', + RoleTeams = 'role_teams', + Teams = 'teams', + TeamUsers = 'team_users', +} + +enum LegacyEnterpriseKeyType { + EncryptedByDataKey = 1, + EncryptedByPublicKey = 2, +} + +const INCLUDE_TO_ENTITY: Record = { + [EnterpriseDataInclude.Nodes]: Enterprise.EnterpriseDataEntity.NODES, + [EnterpriseDataInclude.Users]: Enterprise.EnterpriseDataEntity.USERS, + [EnterpriseDataInclude.Roles]: Enterprise.EnterpriseDataEntity.ROLES, + [EnterpriseDataInclude.RoleUsers]: Enterprise.EnterpriseDataEntity.ROLE_USERS, + [EnterpriseDataInclude.RoleTeams]: Enterprise.EnterpriseDataEntity.ROLE_TEAMS, + [EnterpriseDataInclude.Teams]: Enterprise.EnterpriseDataEntity.TEAMS, + [EnterpriseDataInclude.TeamUsers]: Enterprise.EnterpriseDataEntity.TEAM_USERS, +} + +export type EnterpriseNode = { + node_id: number + parent_id?: number + encrypted_data?: string + displayName?: string +} + +export type EnterpriseUser = { + enterprise_user_id: number + username: string + status?: string + node_id?: number + encrypted_data?: string + full_name?: string + job_title?: string +} + +export type EnterpriseRole = { + role_id: number + node_id?: number + encrypted_data?: string + displayName?: string +} + +export type EnterpriseTeamRecord = { + team_uid: string + name: string + node_id: number + restrict_view?: boolean + restrict_edit?: boolean + restrict_share?: boolean + restrict_sharing?: boolean + encrypted_data?: string +} + +export type EnterpriseTeamUserLink = { + team_uid: string + enterprise_user_id: number + user_type?: string +} + +export type EnterpriseRoleUserLink = { + role_id: number + enterprise_user_id: number +} + +export type EnterpriseRoleTeamLink = { + role_id: number + team_uid: string +} + +export type GetEnterpriseDataResponse = { + enterprise_name?: string + nodes?: EnterpriseNode[] + users?: EnterpriseUser[] + roles?: EnterpriseRole[] + teams?: EnterpriseTeamRecord[] + team_users?: EnterpriseTeamUserLink[] + role_users?: EnterpriseRoleUserLink[] + role_teams?: EnterpriseRoleTeamLink[] +} + +export type NodePathOptions = { + omitRoot?: boolean + separator?: string +} + +export type DecryptedNodeNames = Map +export type DecryptedRoleNames = Map +export type EnterpriseDisplayNames = { + nodes: DecryptedNodeNames + roles: DecryptedRoleNames +} + +type LongLike = number | { toNumber: () => number; toString: () => string } | undefined | null + +type LegacyEnterpriseDataNode = { + node_id: number + parent_id?: number + encrypted_data?: string +} + +type LegacyEnterpriseDataRole = { + role_id: number + encrypted_data?: string +} + +type LegacyEnterpriseDataResponse = { + tree_key?: string + key_type_id?: LegacyEnterpriseKeyType + nodes?: LegacyEnterpriseDataNode[] + roles?: LegacyEnterpriseDataRole[] +} + +export class EnterpriseDataManager { + private readonly auth: Auth + private displayNamesPromise: Promise | null = null + private readonly dataCache = new Map>() + + constructor(auth: Auth) { + this.auth = auth + } + + public async getData(includes: EnterpriseDataInclude[]): Promise { + const key = cacheKeyForIncludes(includes) + let promise = this.dataCache.get(key) + if (!promise) { + promise = getEnterpriseData(this.auth, includes) + this.dataCache.set(key, promise) + } + return promise + } + + public async getDisplayNames(): Promise { + if (!this.displayNamesPromise) { + this.displayNamesPromise = getEnterpriseDisplayNames(this.auth) + } + return this.displayNamesPromise + } + + public clearCache(): void { + this.dataCache.clear() + this.displayNamesPromise = null + } + + public static getNodePath( + nodes: EnterpriseNode[], + nodeId: number, + options?: NodePathOptions + ): string { + return getNodePath(nodes, nodeId, options) + } +} + +function cacheKeyForIncludes(includes: EnterpriseDataInclude[]): string { + return [...includes].sort().join(',') +} + +export function getNodePath( + nodes: EnterpriseNode[], + nodeId: number, + options: NodePathOptions = {} +): string { + const { omitRoot = true, separator = DEFAULT_NODE_PATH_SEPARATOR } = options + const byId = new Map() + for (const node of nodes) byId.set(node.node_id, node) + + const visited = new Set() + const segments: string[] = [] + let currentId: number | undefined = nodeId + while (isNumber(currentId) && currentId > 0 && !visited.has(currentId)) { + visited.add(currentId) + const node = byId.get(currentId) + if (!node) break + const parentId = node.parent_id || 0 + const isRoot = parentId === 0 + if (!isRoot || !omitRoot) { + const segmentName = (node.displayName || '').trim() + if (segmentName) segments.push(segmentName) + } + currentId = parentId + } + segments.reverse() + return segments.join(separator) +} + +function toNumber(value: LongLike): number { + if (value == null) return 0 + if (isNumber(value)) return value + if (typeof value.toNumber === 'function') return value.toNumber() + const parsed = Number(value.toString()) + return Number.isFinite(parsed) ? parsed : 0 +} + +function toUid(bytes: Uint8Array | null | undefined): string { + return bytes && bytes.length > 0 ? webSafe64FromBytes(bytes) : '' +} + +function decodeChunk( + chunkData: Uint8Array[] | null | undefined, + decoder: (bytes: Uint8Array) => T +): T[] { + if (!chunkData || chunkData.length === 0) return [] + const results: T[] = [] + for (const bytes of chunkData) { + try { + results.push(decoder(bytes)) + } catch { + + } + } + return results +} + +function decodeNodeChunk(bytes: Uint8Array): EnterpriseNode { + const message = Enterprise.Node.decode(bytes) + const out: EnterpriseNode = { node_id: toNumber(message.nodeId) } + if (message.parentId != null) out.parent_id = toNumber(message.parentId) + if (message.encryptedData) out.encrypted_data = message.encryptedData + return out +} + +function decodeUserChunk(bytes: Uint8Array): EnterpriseUser { + const message = Enterprise.User.decode(bytes) + const out: EnterpriseUser = { + enterprise_user_id: toNumber(message.enterpriseUserId), + username: message.username || '', + } + if (message.status) out.status = message.status + if (message.nodeId != null) out.node_id = toNumber(message.nodeId) + if (message.encryptedData) out.encrypted_data = message.encryptedData + if (message.fullName) out.full_name = message.fullName + if (message.jobTitle) out.job_title = message.jobTitle + return out +} + +function decodeRoleChunk(bytes: Uint8Array): EnterpriseRole { + const message = Enterprise.Role.decode(bytes) + const out: EnterpriseRole = { role_id: toNumber(message.roleId) } + if (message.nodeId != null) out.node_id = toNumber(message.nodeId) + if (message.encryptedData) out.encrypted_data = message.encryptedData + return out +} + +function decodeTeamChunk(bytes: Uint8Array): EnterpriseTeamRecord { + const message = Enterprise.Team.decode(bytes) + const out: EnterpriseTeamRecord = { + team_uid: toUid(message.teamUid), + name: message.name || '', + node_id: toNumber(message.nodeId), + restrict_view: message.restrictView === true, + restrict_edit: message.restrictEdit === true, + restrict_share: message.restrictShare === true, + } + if (message.encryptedData) out.encrypted_data = message.encryptedData + return out +} + +function decodeTeamUserChunk(bytes: Uint8Array): EnterpriseTeamUserLink { + const message = Enterprise.TeamUser.decode(bytes) + const out: EnterpriseTeamUserLink = { + team_uid: toUid(message.teamUid), + enterprise_user_id: toNumber(message.enterpriseUserId), + } + if (message.userType) out.user_type = message.userType + return out +} + +function decodeRoleUserChunk(bytes: Uint8Array): EnterpriseRoleUserLink { + const message = Enterprise.RoleUser.decode(bytes) + return { + role_id: toNumber(message.roleId), + enterprise_user_id: toNumber(message.enterpriseUserId), + } +} + +function decodeRoleTeamChunk(bytes: Uint8Array): EnterpriseRoleTeamLink { + const message = Enterprise.RoleTeam.decode(bytes) + return { + role_id: toNumber(message.roleId), + team_uid: toUid(message.teamUid), + } +} + +function applyChunk(chunk: Enterprise.IEnterpriseData, target: GetEnterpriseDataResponse): void { + const entity = chunk.entity ?? Enterprise.EnterpriseDataEntity.UNKNOWN + const data = chunk.data || [] + + switch (entity) { + case Enterprise.EnterpriseDataEntity.NODES: + target.nodes = (target.nodes || []).concat(decodeChunk(data, decodeNodeChunk)) + break + case Enterprise.EnterpriseDataEntity.USERS: + target.users = (target.users || []).concat(decodeChunk(data, decodeUserChunk)) + break + case Enterprise.EnterpriseDataEntity.ROLES: + target.roles = (target.roles || []).concat(decodeChunk(data, decodeRoleChunk)) + break + case Enterprise.EnterpriseDataEntity.TEAMS: + target.teams = (target.teams || []).concat(decodeChunk(data, decodeTeamChunk)) + break + case Enterprise.EnterpriseDataEntity.TEAM_USERS: + target.team_users = (target.team_users || []).concat(decodeChunk(data, decodeTeamUserChunk)) + break + case Enterprise.EnterpriseDataEntity.ROLE_USERS: + target.role_users = (target.role_users || []).concat(decodeChunk(data, decodeRoleUserChunk)) + break + case Enterprise.EnterpriseDataEntity.ROLE_TEAMS: + target.role_teams = (target.role_teams || []).concat(decodeChunk(data, decodeRoleTeamChunk)) + break + default: + break + } +} + +export async function getEnterpriseData( + auth: Auth, + includes: EnterpriseDataInclude[] +): Promise { + const interesting = new Set(includes.map((key) => INCLUDE_TO_ENTITY[key])) + const aggregate: GetEnterpriseDataResponse = {} + let continuationToken: Uint8Array | undefined + + for (let iteration = 0; iteration < MAX_CONTINUATIONS; iteration += 1) { + const request: Enterprise.IEnterpriseDataRequest = {} + if (continuationToken) request.continuationToken = continuationToken + + const response = await auth.executeRest(getEnterpriseDataForUserMessage(request)) + + if (response.generalData?.enterpriseName) { + aggregate.enterprise_name = response.generalData.enterpriseName + } + + for (const chunk of response.data || []) { + const entity = chunk.entity ?? Enterprise.EnterpriseDataEntity.UNKNOWN + if (!interesting.has(entity)) continue + applyChunk(chunk, aggregate) + } + + if (!response.hasMore) break + if (!response.continuationToken || response.continuationToken.length === 0) break + continuationToken = response.continuationToken + } + + return aggregate +} + +async function fetchLegacyEnterpriseData(auth: Auth): Promise { + const command: RestCommand<{ include: string[] }, LegacyEnterpriseDataResponse> = { + baseRequest: { command: LEGACY_ENTERPRISE_DATA_COMMAND }, + request: { include: [EnterpriseDataInclude.Nodes, EnterpriseDataInclude.Roles] }, + authorization: {}, + } + + try { + const response = await auth.executeRestCommand(command) + if (response && (response.tree_key || response.nodes || response.roles)) return response + } catch { + } + return null +} + +async function decryptTreeKey(response: LegacyEnterpriseDataResponse, auth: Auth): Promise { + const treeKey = response.tree_key + if (!treeKey) return null + + try { + if (response.key_type_id === LegacyEnterpriseKeyType.EncryptedByDataKey) { + const dataKey = (auth as unknown as { dataKey?: Uint8Array }).dataKey + if (!dataKey) return null + return await decryptFromStorage(treeKey, dataKey) + } + + const privateKey = (auth as unknown as { privateKey?: Uint8Array }).privateKey + if (!privateKey) return null + return platform.privateDecrypt(normal64Bytes(treeKey), privateKey) + } catch { + return null + } +} + +async function decryptDisplayName(encrypted: string, treeKey: Uint8Array): Promise { + try { + const decrypted = await decryptObjectFromStorage<{ displayname?: string }>(encrypted, treeKey) + return (decrypted?.displayname || '').trim() + } catch { + return '' + } +} + +export async function getEnterpriseDisplayNames(auth: Auth): Promise { + const empty: EnterpriseDisplayNames = { nodes: new Map(), roles: new Map() } + const response = await fetchLegacyEnterpriseData(auth) + if (!response) return empty + + const treeKey = await decryptTreeKey(response, auth) + if (!treeKey) return empty + + const nodes: DecryptedNodeNames = new Map() + for (const node of response.nodes || []) { + if (!isNumber(node.node_id)) continue + if (!node.encrypted_data) continue + const display = await decryptDisplayName(node.encrypted_data, treeKey) + if (display) nodes.set(node.node_id, display) + } + + const roles: DecryptedRoleNames = new Map() + for (const role of response.roles || []) { + if (!isNumber(role.role_id)) continue + if (!role.encrypted_data) continue + const display = await decryptDisplayName(role.encrypted_data, treeKey) + if (display) roles.set(role.role_id, display) + } + + return { nodes, roles } +} diff --git a/KeeperSdk/src/teams/listTeams.ts b/KeeperSdk/src/teams/listTeams.ts new file mode 100644 index 0000000..47f115f --- /dev/null +++ b/KeeperSdk/src/teams/listTeams.ts @@ -0,0 +1,371 @@ +import type { Auth } from '@keeper-security/keeperapi' +import { isNumber, TOKEN_SEPARATOR_PATTERN } from '../utils' +import { + EnterpriseDataInclude, + EnterpriseDataManager, + getNodePath, + type DecryptedRoleNames, + type EnterpriseDisplayNames, + type EnterpriseNode, + type EnterpriseRole, + type EnterpriseTeamRecord, + type EnterpriseTeamUserLink, + type EnterpriseUser, + type GetEnterpriseDataResponse, +} from './enterpriseData' + +export enum TeamColumn { + Restricts = 'restricts', + Node = 'node', + UserCount = 'user_count', + Users = 'users', + RoleCount = 'role_count', + Roles = 'roles', +} + +export type TeamColumnInput = TeamColumn | `${TeamColumn}` + +export const SUPPORTED_TEAM_COLUMNS: readonly TeamColumn[] = Object.values(TeamColumn) + +export const DEFAULT_TEAM_COLUMNS: readonly TeamColumn[] = [ + TeamColumn.Restricts, + TeamColumn.Node, + TeamColumn.UserCount, + TeamColumn.RoleCount, +] + +const NODE_PATH_SEPARATOR = '\\' +const MIN_ASCII_COL_WIDTH = 2 +const ALL_COLUMNS_WILDCARD = '*' + +const HEADER_BY_COLUMN: Record = { + [TeamColumn.Restricts]: 'Restricts', + [TeamColumn.Node]: 'Node', + [TeamColumn.UserCount]: 'User Count', + [TeamColumn.Users]: 'Users', + [TeamColumn.RoleCount]: 'Role Count', + [TeamColumn.Roles]: 'Roles', +} + +export type ListTeamsOptions = { + pattern?: string | null + columns?: TeamColumnInput[] | typeof ALL_COLUMNS_WILDCARD | string | null +} + +export type ListTeamRow = { + team_uid: string + name: string + restricts?: string + node?: string + user_count?: number + users?: string[] + role_count?: number + roles?: string[] +} + +export type FormattedTeamsTable = { + headers: string[] + rows: string[][] +} + +export type FormatTeamsTableOptions = { + columns?: ListTeamsOptions['columns'] +} + +type DecorateContext = { + nodePaths: Map + teamUsers: Map> + roleTeams: Map> + usernameById: Map + roleNameById: Map +} + +export function formatTeamRestricts(team: EnterpriseTeamRecord): string { + const r = team.restrict_view === true ? 'R ' : ' ' + const w = team.restrict_edit === true ? 'W ' : ' ' + const restrictShare = team.restrict_share === true || team.restrict_sharing === true + const s = restrictShare ? 'S' : ' ' + return r + w + s +} + +export async function listTeams(auth: Auth, options: ListTeamsOptions = {}): Promise { + const columns = resolveColumns(options.columns) + const includes = includesForColumns(columns) + const wantsDisplayNames = columns.includes(TeamColumn.Node) || columns.includes(TeamColumn.Roles) + + const enterpriseData = new EnterpriseDataManager(auth) + const emptyDisplayNames: EnterpriseDisplayNames = { nodes: new Map(), roles: new Map() } + const [response, displayNames] = await Promise.all([ + enterpriseData.getData(includes), + wantsDisplayNames ? enterpriseData.getDisplayNames() : Promise.resolve(emptyDisplayNames), + ]) + + const teams = response.teams || [] + const nodes = response.nodes || [] + applyDecryptedNodeNames(nodes, displayNames.nodes) + + const context: DecorateContext = { + nodePaths: buildNodePathLookup(nodes), + teamUsers: buildTeamUserMap(response.team_users || []), + roleTeams: buildRoleTeamMap(response), + usernameById: buildUserUsernameMap(response.users || []), + roleNameById: buildRoleNameMap(response.roles || [], displayNames.roles), + } + + const pattern = options.pattern?.trim() || null + const rows: ListTeamRow[] = [] + for (const team of teams) { + const row: ListTeamRow = { + team_uid: team.team_uid, + name: teamDisplayName(team), + } + decorateRow(row, team, columns, context) + if (pattern && !rowMatchesPattern(row, pattern)) continue + rows.push(row) + } + + rows.sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' })) + return rows +} + +export function formatTeamsTable( + rows: ListTeamRow[], + options: FormatTeamsTableOptions = {} +): FormattedTeamsTable { + const columns = resolveColumns(options.columns) + const headers: string[] = ['#', 'Team UID', 'Name', ...columns.map((column) => HEADER_BY_COLUMN[column])] + + const outRows: string[][] = rows.map((row, rowIndex) => { + const cells: string[] = [String(rowIndex + 1), row.team_uid, row.name] + for (const column of columns) cells.push(formatCell(row, column)) + return cells + }) + + return { headers, rows: outRows } +} + +export function renderTeamsAsciiTable( + table: FormattedTeamsTable, + options: { minColWidth?: number } = {} +): string { + const { minColWidth = MIN_ASCII_COL_WIDTH } = options + const { headers, rows } = table + const columnCount = headers.length + + const expandedRows: string[][][] = rows.map((row) => + row.map((cell) => (cell.includes('\n') ? cell.split('\n') : [cell])) + ) + + const columnWidths = new Array(columnCount).fill(0) + for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) { + columnWidths[columnIndex] = Math.max(headers[columnIndex].length, minColWidth) + } + for (const row of expandedRows) { + for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) { + for (const line of row[columnIndex]) { + columnWidths[columnIndex] = Math.max(columnWidths[columnIndex], line.length, minColWidth) + } + } + } + + const padCell = (cell: string, columnIndex: number): string => + cell + ' '.repeat(columnWidths[columnIndex] - cell.length) + const formatPhysicalRow = (cells: string[]): string => + cells.map((cell, columnIndex) => padCell(cell, columnIndex)).join(' ') + + const ruleRow = formatPhysicalRow(columnWidths.map((width) => '-'.repeat(width))) + const lines: string[] = [formatPhysicalRow(headers), ruleRow] + + for (const row of expandedRows) { + const physicalLineCount = Math.max(...row.map((cell) => cell.length)) + for (let lineIndex = 0; lineIndex < physicalLineCount; lineIndex += 1) { + const physicalCells: string[] = row.map((cell) => cell[lineIndex] ?? '') + lines.push(formatPhysicalRow(physicalCells)) + } + } + return lines.join('\n') +} + +function includesForColumns(columns: readonly TeamColumn[]): EnterpriseDataInclude[] { + const set = new Set([EnterpriseDataInclude.Teams]) + for (const column of columns) { + switch (column) { + case TeamColumn.Node: + set.add(EnterpriseDataInclude.Nodes) + break + case TeamColumn.UserCount: + case TeamColumn.Users: + set.add(EnterpriseDataInclude.TeamUsers) + if (column === TeamColumn.Users) set.add(EnterpriseDataInclude.Users) + break + case TeamColumn.RoleCount: + case TeamColumn.Roles: + set.add(EnterpriseDataInclude.RoleTeams) + if (column === TeamColumn.Roles) set.add(EnterpriseDataInclude.Roles) + break + } + } + return Array.from(set) +} + +function resolveColumns(input: ListTeamsOptions['columns']): TeamColumn[] { + if (input == null) return [...DEFAULT_TEAM_COLUMNS] + if (input === ALL_COLUMNS_WILDCARD) return [...SUPPORTED_TEAM_COLUMNS] + + const requested = Array.isArray(input) ? input : input.split(',').map((part) => part.trim()) + const allowed = new Set(SUPPORTED_TEAM_COLUMNS) + const seen = new Set() + for (const column of requested) { + if (column && allowed.has(column)) seen.add(column as TeamColumn) + } + if (seen.size === 0) return [...DEFAULT_TEAM_COLUMNS] + return SUPPORTED_TEAM_COLUMNS.filter((column) => seen.has(column)) +} + +function teamDisplayName(team: { name?: string; team_uid: string }): string { + return (team.name || team.team_uid || '').trim() || team.team_uid +} + +function tokenize(text: string): string[] { + return text.split(TOKEN_SEPARATOR_PATTERN).filter((token) => token.length > 0) +} + +function rowMatchesPattern(row: ListTeamRow, pattern: string): boolean { + const lowered = pattern.toLowerCase() + const tokens: string[] = [] + tokens.push(row.team_uid.toLowerCase()) + tokens.push(...tokenize(row.name.toLowerCase())) + if (row.restricts) tokens.push(row.restricts.toLowerCase()) + if (row.node) tokens.push(...tokenize(row.node.toLowerCase())) + if (row.user_count != null) tokens.push(String(row.user_count)) + if (row.role_count != null) tokens.push(String(row.role_count)) + for (const list of [row.users, row.roles]) { + if (!list) continue + for (const value of list) tokens.push(...tokenize(value.toLowerCase())) + } + return tokens.some((token) => token.includes(lowered)) +} + +function applyDecryptedNodeNames(nodes: EnterpriseNode[], decrypted: Map): void { + if (decrypted.size === 0) return + for (const node of nodes) { + const display = decrypted.get(node.node_id) + if (display) node.displayName = display + } +} + +function buildNodePathLookup(nodes: EnterpriseNode[]): Map { + return new Map( + nodes.map((node) => [node.node_id, getNodePath(nodes, node.node_id, { separator: NODE_PATH_SEPARATOR })]) + ) +} + +function buildTeamUserMap(links: EnterpriseTeamUserLink[]): Map> { + const byTeam = new Map>() + for (const link of links) { + if (!link.team_uid) continue + const set = byTeam.get(link.team_uid) + if (set) set.add(link.enterprise_user_id) + else byTeam.set(link.team_uid, new Set([link.enterprise_user_id])) + } + return byTeam +} + +function buildRoleTeamMap(response: GetEnterpriseDataResponse): Map> { + const map = new Map>() + for (const link of response.role_teams || []) { + if (!link.team_uid) continue + const set = map.get(link.team_uid) + if (set) set.add(link.role_id) + else map.set(link.team_uid, new Set([link.role_id])) + } + return map +} + +function buildUserUsernameMap(users: EnterpriseUser[]): Map { + const map = new Map() + for (const user of users) { + if (isNumber(user.enterprise_user_id ) && user.username) { + map.set(user.enterprise_user_id, user.username) + } + } + return map +} + +function buildRoleNameMap(roles: EnterpriseRole[], decrypted: DecryptedRoleNames): Map { + const map = new Map() + for (const role of roles) { + if (isNumber(role.role_id)) continue + const display = (role.displayName || decrypted.get(role.role_id) || '').trim() + map.set(role.role_id, display || String(role.role_id)) + } + return map +} + +function resolveSortedNames(ids: Set, nameById: Map): string[] { + const result: string[] = [] + for (const id of ids) { + const name = nameById.get(id) + if (name) result.push(name) + } + result.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' })) + return result +} + +function decorateRow( + row: ListTeamRow, + team: EnterpriseTeamRecord, + columns: readonly TeamColumn[], + context: DecorateContext +): void { + const { nodePaths, teamUsers, roleTeams, usernameById, roleNameById } = context + + for (const column of columns) { + switch (column) { + case TeamColumn.Restricts: + row.restricts = formatTeamRestricts(team) + break + case TeamColumn.Node: + row.node = nodePaths.get(team.node_id) || '' + break + case TeamColumn.UserCount: + row.user_count = teamUsers.get(team.team_uid)?.size ?? 0 + break + case TeamColumn.Users: { + const ids = teamUsers.get(team.team_uid) + row.users = ids ? resolveSortedNames(ids, usernameById) : [] + break + } + case TeamColumn.RoleCount: + row.role_count = roleTeams.get(team.team_uid)?.size ?? 0 + break + case TeamColumn.Roles: { + const ids = roleTeams.get(team.team_uid) + row.roles = ids ? resolveSortedNames(ids, roleNameById) : [] + break + } + } + } +} + +function formatListCell(values: string[] | undefined): string { + if (!values || values.length === 0) return '' + return values.join('\n') +} + +function formatCell(row: ListTeamRow, column: TeamColumn): string { + switch (column) { + case TeamColumn.Restricts: + return row.restricts ?? '' + case TeamColumn.Node: + return row.node ?? '' + case TeamColumn.UserCount: + return row.user_count == null ? '' : String(row.user_count) + case TeamColumn.Users: + return formatListCell(row.users) + case TeamColumn.RoleCount: + return row.role_count == null ? '' : String(row.role_count) + case TeamColumn.Roles: + return formatListCell(row.roles) + } +} diff --git a/KeeperSdk/src/teams/viewTeam.ts b/KeeperSdk/src/teams/viewTeam.ts new file mode 100644 index 0000000..f682240 --- /dev/null +++ b/KeeperSdk/src/teams/viewTeam.ts @@ -0,0 +1,248 @@ +import type { Auth } from '@keeper-security/keeperapi' +import { KeeperSdkError, ResultCodes } from '../utils' +import { + EnterpriseDataInclude, + EnterpriseDataManager, + getNodePath, + type EnterpriseDisplayNames, + type EnterpriseRoleTeamLink, + type EnterpriseTeamRecord, + type EnterpriseTeamUserLink, + type EnterpriseUser, +} from './enterpriseData' + +const NODE_PATH_SEPARATOR = '\\' +const VIEW_TEAM_INCLUDES: EnterpriseDataInclude[] = [ + EnterpriseDataInclude.Teams, + EnterpriseDataInclude.TeamUsers, + EnterpriseDataInclude.RoleTeams, + EnterpriseDataInclude.Roles, + EnterpriseDataInclude.Users, + EnterpriseDataInclude.Nodes, +] + +export type TeamRoleInfo = { + role_id: number + role_name: string +} + +export type TeamUserInfo = { + enterprise_user_id: number + username: string +} + +export type TeamView = { + team_uid: string + team_name: string + node_id: number + node_name: string + restrict_view: boolean + restrict_edit: boolean + restrict_share: boolean + team_roles?: TeamRoleInfo[] + team_users?: TeamUserInfo[] +} + +export type FormatTeamViewOptions = { + verbose?: boolean +} + +export type TeamViewTableRow = { + label: string + value: string | string[] + id?: number | number[] +} + +export type FormattedTeamViewTable = { + rows: TeamViewTableRow[] + hasIdColumn: boolean +} + +export async function viewTeam(auth: Auth, identifier: string): Promise { + const enterpriseData = new EnterpriseDataManager(auth) + const [response, displayNames] = await Promise.all([ + enterpriseData.getData(VIEW_TEAM_INCLUDES), + enterpriseData.getDisplayNames(), + ]) + + const team = resolveTeam(response.teams || [], identifier) + const nodePath = resolveNodePath(response.nodes || [], displayNames, team.node_id) + const teamUsers = buildTeamUsers(response.users || [], response.team_users || [], team.team_uid) + const teamRoles = buildTeamRoles(response.role_teams || [], displayNames.roles, team.team_uid) + + const view: TeamView = { + team_uid: team.team_uid, + team_name: team.name, + node_id: team.node_id, + node_name: nodePath, + restrict_view: team.restrict_view === true, + restrict_edit: team.restrict_edit === true, + restrict_share: team.restrict_share === true || team.restrict_sharing === true, + } + if (teamRoles.length > 0) view.team_roles = teamRoles + if (teamUsers.length > 0) view.team_users = teamUsers + return view +} + +export function formatTeamView(view: TeamView, options: FormatTeamViewOptions = {}): FormattedTeamViewTable { + const verbose = options.verbose === true + const rows: TeamViewTableRow[] = [ + { label: 'Team UID', value: view.team_uid }, + { label: 'Team Name', value: view.team_name }, + { + label: 'Node Name', + value: view.node_name, + id: verbose ? view.node_id : undefined, + }, + { label: 'Restrict Edit', value: boolText(view.restrict_edit) }, + { label: 'Restrict Share', value: boolText(view.restrict_share) }, + { label: 'Restrict View', value: boolText(view.restrict_view) }, + ] + + if (view.team_roles && view.team_roles.length > 0) { + rows.push({ + label: 'Role(s)', + value: view.team_roles.map((role) => role.role_name), + id: verbose ? view.team_roles.map((role) => role.role_id) : undefined, + }) + } + + if (view.team_users && view.team_users.length > 0) { + rows.push({ + label: 'User(s)', + value: view.team_users.map((user) => user.username), + id: verbose ? view.team_users.map((user) => user.enterprise_user_id) : undefined, + }) + } + + return { rows, hasIdColumn: verbose } +} + +export function teamViewTable(table: FormattedTeamViewTable): string { + const labelWidth = table.rows.reduce((max, row) => Math.max(max, row.label.length), 0) + const expandedRows = table.rows.map((row) => ({ + label: row.label, + valueLines: asLines(row.value), + idLines: table.hasIdColumn ? asIdLines(row.id) : [], + })) + + const valueWidth = expandedRows.reduce( + (max, row) => Math.max(max, ...row.valueLines.map((line) => line.length)), + 0 + ) + const idWidth = table.hasIdColumn + ? expandedRows.reduce((max, row) => Math.max(max, ...row.idLines.map((line) => line.length)), 0) + : 0 + + const padRight = (text: string, width: number): string => + text + ' '.repeat(Math.max(0, width - text.length)) + const padLeft = (text: string, width: number): string => + ' '.repeat(Math.max(0, width - text.length)) + text + + const lines: string[] = [] + for (const row of expandedRows) { + const lineCount = Math.max(row.valueLines.length, table.hasIdColumn ? row.idLines.length : 0, 1) + for (let lineIndex = 0; lineIndex < lineCount; lineIndex += 1) { + const isFirstLine = lineIndex === 0 + const labelCell = padLeft(isFirstLine ? row.label : '', labelWidth) + const valueCell = padRight(row.valueLines[lineIndex] ?? '', valueWidth) + const cells: string[] = [labelCell, valueCell] + if (table.hasIdColumn) { + const idCell = padRight(row.idLines[lineIndex] ?? '', idWidth) + cells.push(idCell) + } + lines.push(cells.join(' ').trimEnd()) + } + } + return lines.join('\n') +} + +function resolveTeam(teams: EnterpriseTeamRecord[], identifier: string): EnterpriseTeamRecord { + const trimmed = (identifier ?? '').trim() + if (!trimmed) { + throw new KeeperSdkError('Team name or UID is required.', ResultCodes.TEAM_REQUIRED) + } + + const uidMatch = teams.find((team) => team.team_uid === trimmed) + if (uidMatch) return uidMatch + + const lowered = trimmed.toLowerCase() + const nameMatches = teams.filter((team) => (team.name || '').trim().toLowerCase() === lowered) + if (nameMatches.length === 1) return nameMatches[0] + if (nameMatches.length > 1) { + throw new KeeperSdkError( + `Multiple teams match name "${trimmed}". Specify the team UID instead.`, + ResultCodes.MULTIPLE_TEAM_MATCHES + ) + } + throw new KeeperSdkError(`Team "${trimmed}" does not exist.`, ResultCodes.TEAM_NOT_FOUND) +} + +function resolveNodePath( + nodes: Parameters[0], + displayNames: EnterpriseDisplayNames, + nodeId: number +): string { + if (displayNames.nodes.size > 0) { + for (const node of nodes) { + const display = displayNames.nodes.get(node.node_id) + if (display) node.displayName = display + } + } + return getNodePath(nodes, nodeId, { omitRoot: false, separator: NODE_PATH_SEPARATOR }) +} + +function buildTeamUsers( + users: EnterpriseUser[], + teamUserLinks: EnterpriseTeamUserLink[], + teamUid: string +): TeamUserInfo[] { + const userById = new Map() + for (const user of users) userById.set(user.enterprise_user_id, user) + + const memberIds = new Set() + for (const link of teamUserLinks) { + if (link.team_uid === teamUid) memberIds.add(link.enterprise_user_id) + } + + const result: TeamUserInfo[] = [] + for (const id of memberIds) { + const user = userById.get(id) + if (user && user.username) result.push({ enterprise_user_id: id, username: user.username }) + } + result.sort((a, b) => a.username.localeCompare(b.username, undefined, { sensitivity: 'base' })) + return result +} + +function buildTeamRoles( + roleTeamLinks: EnterpriseRoleTeamLink[], + decryptedRoleNames: Map, + teamUid: string +): TeamRoleInfo[] { + const roleIds = new Set() + for (const link of roleTeamLinks) { + if (link.team_uid === teamUid) roleIds.add(link.role_id) + } + + const result: TeamRoleInfo[] = [] + for (const id of roleIds) { + result.push({ role_id: id, role_name: decryptedRoleNames.get(id) || String(id) }) + } + result.sort((a, b) => a.role_name.localeCompare(b.role_name, undefined, { sensitivity: 'base' })) + return result +} + +function boolText(value: boolean): string { + return value ? 'True' : 'False' +} + +function asLines(value: string | string[]): string[] { + if (Array.isArray(value)) return value.length > 0 ? value : [''] + return [value] +} + +function asIdLines(id: number | number[] | undefined): string[] { + if (id == null) return [] + if (Array.isArray(id)) return id.length > 0 ? id.map(String) : [''] + return [String(id)] +} diff --git a/KeeperSdk/src/utils/constants.ts b/KeeperSdk/src/utils/constants.ts index 3748293..d89bdce 100644 --- a/KeeperSdk/src/utils/constants.ts +++ b/KeeperSdk/src/utils/constants.ts @@ -11,19 +11,46 @@ export const AuthDefaults = { CODE_VALIDATION_DELAY_MS: 2_000, } as const +export enum AuthErrorCode { + InvalidCredentials = 'invalid_credentials', + MissingUsername = 'missing_username', + MissingPassword = 'missing_password', + MaxAttemptsExceeded = 'max_attempts_exceeded', + UserCancelled = 'user_cancelled', + Unsupported2FAChannel = 'unsupported_2fa_channel', +} + +export enum SessionErrorCode { + NotLoggedIn = 'not_logged_in', + DeviceNotRegistered = 'device_not_registered', + NoPreviousLogin = 'no_previous_login', + NoCloneCode = 'no_clone_code', + PersistentLoginFailed = 'persistent_login_failed', + SessionTokenExpired = 'session_token_expired', +} + +export enum TeamErrorCode { + TeamRequired = 'team_required', + TeamNotFound = 'team_not_found', + MultipleTeamMatches = 'multiple_team_matches', +} + export const ResultCodes = { - INVALID_CREDENTIALS: 'invalid_credentials', - MISSING_USERNAME: 'missing_username', - MISSING_PASSWORD: 'missing_password', - MAX_ATTEMPTS_EXCEEDED: 'max_attempts_exceeded', - USER_CANCELLED: 'user_cancelled', - NOT_LOGGED_IN: 'not_logged_in', - DEVICE_NOT_REGISTERED: 'device_not_registered', - NO_PREVIOUS_LOGIN: 'no_previous_login', - NO_CLONE_CODE: 'no_clone_code', - PERSISTENT_LOGIN_FAILED: 'persistent_login_failed', - SESSION_TOKEN_EXPIRED: 'session_token_expired', - UNSUPPORTED_2FA_CHANNEL: 'unsupported_2fa_channel', + INVALID_CREDENTIALS: AuthErrorCode.InvalidCredentials, + MISSING_USERNAME: AuthErrorCode.MissingUsername, + MISSING_PASSWORD: AuthErrorCode.MissingPassword, + MAX_ATTEMPTS_EXCEEDED: AuthErrorCode.MaxAttemptsExceeded, + USER_CANCELLED: AuthErrorCode.UserCancelled, + UNSUPPORTED_2FA_CHANNEL: AuthErrorCode.Unsupported2FAChannel, + NOT_LOGGED_IN: SessionErrorCode.NotLoggedIn, + DEVICE_NOT_REGISTERED: SessionErrorCode.DeviceNotRegistered, + NO_PREVIOUS_LOGIN: SessionErrorCode.NoPreviousLogin, + NO_CLONE_CODE: SessionErrorCode.NoCloneCode, + PERSISTENT_LOGIN_FAILED: SessionErrorCode.PersistentLoginFailed, + SESSION_TOKEN_EXPIRED: SessionErrorCode.SessionTokenExpired, + TEAM_REQUIRED: TeamErrorCode.TeamRequired, + TEAM_NOT_FOUND: TeamErrorCode.TeamNotFound, + MULTIPLE_TEAM_MATCHES: TeamErrorCode.MultipleTeamMatches, } as const export const KEEPER_PUBLIC_HOSTS: Record = { diff --git a/KeeperSdk/src/utils/index.ts b/KeeperSdk/src/utils/index.ts index 4d49c62..43509a8 100644 --- a/KeeperSdk/src/utils/index.ts +++ b/KeeperSdk/src/utils/index.ts @@ -1,4 +1,12 @@ -export { SdkDefaults, AuthDefaults, ResultCodes, KEEPER_PUBLIC_HOSTS } from './constants' +export { + SdkDefaults, + AuthDefaults, + ResultCodes, + AuthErrorCode, + SessionErrorCode, + TeamErrorCode, + KEEPER_PUBLIC_HOSTS, +} from './constants' export { Logger, ConsoleLogger, LogLevel, logger, setLogger, getLogger, resetLogger } from './Logger' export type { ILogger } from './Logger' export { KeeperSdkError, isKeeperError, extractErrorMessage, extractResultCode } from './errors' diff --git a/KeeperSdk/src/vault/KeeperVault.ts b/KeeperSdk/src/vault/KeeperVault.ts index f0551e2..f6b2337 100644 --- a/KeeperSdk/src/vault/KeeperVault.ts +++ b/KeeperSdk/src/vault/KeeperVault.ts @@ -56,6 +56,9 @@ import type { ShareFolderInput, ShareFolderResult } from '../sharedFolders/share import { FolderManager } from '../folders/FolderManager' import type { SharedFolderPermissionsInput } from '../folders/FolderManager' import { SharedFolderManager } from '../sharedFolders/SharedFolderManager' +import { TeamManager } from '../teams/TeamManager' +import type { ListTeamRow, ListTeamsOptions } from '../teams/listTeams' +import type { TeamView } from '../teams/viewTeam' import { ConsoleLogger, LogLevel, KeeperSdkError, extractErrorMessage, SdkDefaults, ResultCodes } from '../utils' import type { ILogger } from '../utils' @@ -96,6 +99,7 @@ export class KeeperVault { private readonly folderSession: VaultFolderSession = FolderManager.createSession() private readonly folderManager: FolderManager private readonly sharedFolderManager: SharedFolderManager + private readonly teamManager: TeamManager constructor(config?: KeeperVaultConfig) { this.config = { @@ -116,6 +120,7 @@ export class KeeperVault { const authProvider = () => this.getAuthOrThrow() this.folderManager = new FolderManager(this.storage, this.folderSession, authProvider) this.sharedFolderManager = new SharedFolderManager(this.storage, authProvider) + this.teamManager = new TeamManager(authProvider) } public getFolderManager(): FolderManager { @@ -126,6 +131,10 @@ export class KeeperVault { return this.sharedFolderManager } + public getTeamManager(): TeamManager { + return this.teamManager + } + private async createAuth(options?: { useSessionResumption?: boolean }): Promise { const host = this.config.host const baseDeviceConfig = await this.sessionManager.getDeviceConfig(host) @@ -350,6 +359,14 @@ export class KeeperVault { return this.sharedFolderManager.listSharedFolders(options ?? {}) } + public async listTeams(options?: ListTeamsOptions): Promise { + return this.teamManager.listTeams(options ?? {}) + } + + public async viewTeam(identifier: string): Promise { + return this.teamManager.viewTeam(identifier) + } + public async changeDirectory(path: string): Promise { return this.folderManager.changeDirectory(path) } diff --git a/examples/sdk_example/package-lock.json b/examples/sdk_example/package-lock.json new file mode 100644 index 0000000..052d934 --- /dev/null +++ b/examples/sdk_example/package-lock.json @@ -0,0 +1,650 @@ +{ + "name": "sdk-example", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sdk-example", + "version": "1.0.0", + "dependencies": { + "@keeper-security/keeperapi": "17.1.0", + "@types/node": "^20.9.1", + "ts-node": "^10.7.0", + "tsconfig-paths": "^4.2.0", + "typescript": "^4.6.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@keeper-security/keeperapi": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@keeper-security/keeperapi/-/keeperapi-17.1.0.tgz", + "integrity": "sha512-BV1o72g1BXz3Agjw0l6kLlS5SOvWCJ0qD2RcEXPAvMQJcq0O8SLaf2KhIG/dGHPHdNZ5Dod4+cI0sAfnpZeYZw==", + "license": "ISC", + "dependencies": { + "@noble/post-quantum": "^0.5.2", + "asmcrypto.js": "^2.3.2", + "faye-websocket": "^0.11.3", + "form-data": "^4.0.4", + "node-rsa": "^1.0.8" + } + }, + "node_modules/@noble/curves": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", + "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.0.1" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/post-quantum": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@noble/post-quantum/-/post-quantum-0.5.4.tgz", + "integrity": "sha512-leww0zzIirrvwaYMPI9fj6aRIlA/c6Y0/lifQQ1YOOyHEr0MNH3yYpjXeiVG+tWdPps4XxGclFWX2INPO3Yo5w==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~2.0.0", + "@noble/hashes": "~2.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", + "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "license": "MIT" + }, + "node_modules/asmcrypto.js": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/asmcrypto.js/-/asmcrypto.js-2.3.2.tgz", + "integrity": "sha512-3FgFARf7RupsZETQ1nHnhLUUvpcttcCq1iZCaVAbJZbCZ5VNRrNyvpDyHTOb0KC3llFcsyOT/a99NZcCbeiEsA==", + "license": "MIT" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-rsa": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-1.1.1.tgz", + "integrity": "sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==", + "license": "MIT", + "dependencies": { + "asn1": "^0.2.4" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "license": "MIT" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/examples/sdk_example/package.json b/examples/sdk_example/package.json index 217d0b1..dc77d38 100644 --- a/examples/sdk_example/package.json +++ b/examples/sdk_example/package.json @@ -23,6 +23,8 @@ "folders:tree": "ts-node src/folders/tree.ts", "shared-folders:list-sf": "ts-node src/sharedFolders/list_sf.ts", "shared-folders:share-folder": "ts-node src/sharedFolders/share_folder.ts", + "teams:list": "ts-node src/teams/list_teams.ts", + "teams:view": "ts-node src/teams/view_team.ts", "link-local": "cd ../../KeeperSdk && npm link ../keeperapi && cd ../examples/sdk_example && npm link ../../keeperapi", "types": "tsc --watch", "types:ci": "tsc" diff --git a/examples/sdk_example/src/teams/list_teams.ts b/examples/sdk_example/src/teams/list_teams.ts new file mode 100644 index 0000000..6eda50c --- /dev/null +++ b/examples/sdk_example/src/teams/list_teams.ts @@ -0,0 +1,53 @@ +import { + cleanup, + extractErrorMessage, + formatTeamsTable, + login, + logger, + prompt, + renderTeamsAsciiTable, + suppressLogs, +} from '@keeper-security/keeper-sdk-javascript' +import type { ListTeamRow } from '@keeper-security/keeper-sdk-javascript' +import { runExample } from '../utils/runner' +import { isYes } from '../utils/format' + +async function listTeamsExample() { + const vault = await login() + + try { + const asJson = isYes(await prompt('Output as JSON? [y/N]: ')) + const pattern = (await prompt('Search pattern (Enter to skip): ')).trim() || null + + let rows: ListTeamRow[] + const restore = suppressLogs() + try { + rows = await vault.listTeams({ pattern }) + } finally { + restore() + } + + if (rows.length === 0) { + logger.info(pattern ? `No teams matched "${pattern}".` : 'No teams found in enterprise.') + return + } + + if (asJson) { + logger.info(JSON.stringify(rows, null, 2)) + return + } + + const table = formatTeamsTable(rows) + logger.info('') + logger.info(renderTeamsAsciiTable(table)) + logger.info('') + logger.info(`Total: ${rows.length} team${rows.length === 1 ? '' : 's'}`) + } catch (err) { + logger.error(`Operation failed: ${extractErrorMessage(err)}`) + process.exitCode = 1 + } finally { + cleanup(vault) + } +} + +runExample(listTeamsExample) diff --git a/examples/sdk_example/src/teams/view_team.ts b/examples/sdk_example/src/teams/view_team.ts new file mode 100644 index 0000000..519cd04 --- /dev/null +++ b/examples/sdk_example/src/teams/view_team.ts @@ -0,0 +1,54 @@ +import { + cleanup, + extractErrorMessage, + formatTeamView, + login, + logger, + prompt, + teamViewTable, + suppressLogs, +} from '@keeper-security/keeper-sdk-javascript' +import type { TeamView } from '@keeper-security/keeper-sdk-javascript' +import { runExample } from '../utils/runner' +import { isYes } from '../utils/format' + +async function viewTeamExample() { + const vault = await login() + + try { + const identifier = (await prompt('Team name or UID: ')).trim() + if (!identifier) { + logger.error('Team name or UID is required.') + process.exitCode = 1 + return + } + + const asJson = isYes(await prompt('Output as JSON? [y/N]: ')) + const verbose = !asJson && isYes(await prompt('Show role/user IDs? [y/N]: ')) + + let view: TeamView + const restore = suppressLogs() + try { + view = await vault.viewTeam(identifier) + } finally { + restore() + } + + if (asJson) { + logger.info(JSON.stringify(view, null, 2)) + return + } + + const table = formatTeamView(view, { verbose }) + logger.info('') + logger.info(teamViewTable(table)) + logger.info('') + } catch (err) { + logger.error(`Operation failed: ${extractErrorMessage(err)}`) + process.exitCode = 1 + } finally { + cleanup(vault) + } +} + +runExample(viewTeamExample) diff --git a/keeperapi/package-lock.json b/keeperapi/package-lock.json index 45bcdf5..b9ad96f 100644 --- a/keeperapi/package-lock.json +++ b/keeperapi/package-lock.json @@ -1,12 +1,12 @@ { "name": "@keeper-security/keeperapi", - "version": "17.1.1", + "version": "17.1.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@keeper-security/keeperapi", - "version": "17.1.1", + "version": "17.1.2", "license": "ISC", "dependencies": { "@noble/post-quantum": "^0.5.2",