mirror of
https://github.com/Kong/insomnia.git
synced 2026-04-21 22:57:59 -04:00
External vault - GCP secert manager intergation[INS-4801] (#4)
* Add GCP secret manager UI * Add GCP auth validation * Unify the error message from external vault function
This commit is contained in:
298
package-lock.json
generated
298
package-lock.json
generated
@@ -2538,6 +2538,18 @@
|
||||
"tweetnacl": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@google-cloud/secret-manager": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/secret-manager/-/secret-manager-5.6.0.tgz",
|
||||
"integrity": "sha512-0daW/OXQEVc6VQKPyJTQNyD+563I/TYQ7GCQJx4dq3lB666R9FUPvqHx9b/o/qQtZ5pfuoCbGZl3krpxgTSW8Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"google-gax": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@grpc/grpc-js": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.0.tgz",
|
||||
@@ -7613,7 +7625,6 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
||||
"integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
@@ -7719,7 +7730,6 @@
|
||||
"version": "0.12.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz",
|
||||
"integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/chai": {
|
||||
@@ -8074,6 +8084,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/long": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
|
||||
"integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/marked": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz",
|
||||
@@ -8290,7 +8306,6 @@
|
||||
"version": "2.48.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz",
|
||||
"integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/caseless": "*",
|
||||
@@ -8367,7 +8382,6 @@
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
|
||||
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
@@ -9953,6 +9967,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bignumber.js": {
|
||||
"version": "9.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
|
||||
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
@@ -10247,7 +10270,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/buffer-fill": {
|
||||
@@ -12291,6 +12313,18 @@
|
||||
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/duplexify": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
|
||||
"integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.4.1",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1",
|
||||
"stream-shift": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
@@ -12301,7 +12335,6 @@
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
@@ -13815,6 +13848,12 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/extract-zip": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
||||
@@ -14181,7 +14220,6 @@
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
|
||||
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
@@ -14381,6 +14419,70 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/gaxios": {
|
||||
"version": "6.7.1",
|
||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
|
||||
"integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"extend": "^3.0.2",
|
||||
"https-proxy-agent": "^7.0.1",
|
||||
"is-stream": "^2.0.0",
|
||||
"node-fetch": "^2.6.9",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/gaxios/node_modules/agent-base": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
||||
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/gaxios/node_modules/https-proxy-agent": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
||||
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/gaxios/node_modules/is-stream": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/gcp-metadata": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
|
||||
"integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"gaxios": "^6.1.1",
|
||||
"google-logging-utils": "^0.0.2",
|
||||
"json-bigint": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
@@ -14593,6 +14695,76 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library": {
|
||||
"version": "9.15.1",
|
||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
|
||||
"integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.0",
|
||||
"ecdsa-sig-formatter": "^1.0.11",
|
||||
"gaxios": "^6.1.1",
|
||||
"gcp-metadata": "^6.1.0",
|
||||
"gtoken": "^7.0.0",
|
||||
"jws": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library/node_modules/jwa": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library/node_modules/jws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jwa": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/google-gax": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz",
|
||||
"integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.10.9",
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
"@types/long": "^4.0.0",
|
||||
"abort-controller": "^3.0.0",
|
||||
"duplexify": "^4.0.0",
|
||||
"google-auth-library": "^9.3.0",
|
||||
"node-fetch": "^2.7.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"proto3-json-serializer": "^2.0.2",
|
||||
"protobufjs": "^7.3.2",
|
||||
"retry-request": "^7.0.0",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/google-logging-utils": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
|
||||
"integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/google-protobuf": {
|
||||
"version": "3.21.4",
|
||||
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz",
|
||||
@@ -14728,6 +14900,40 @@
|
||||
"dev": true,
|
||||
"license": "Standard 'no charge' license: https://gsap.com/standard-license. Club GSAP members get more: https://gsap.com/licensing/. Why GreenSock doesn't employ an MIT license: https://gsap.com/why-license/"
|
||||
},
|
||||
"node_modules/gtoken": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
|
||||
"integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"gaxios": "^6.0.0",
|
||||
"jws": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/gtoken/node_modules/jwa": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/gtoken/node_modules/jws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jwa": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/har-schema": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
@@ -15067,7 +15273,6 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
|
||||
"integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tootallnate/once": "2",
|
||||
@@ -16429,6 +16634,15 @@
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/json-bigint": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bignumber.js": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
@@ -18831,7 +19045,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
||||
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
@@ -20145,6 +20358,18 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/proto3-json-serializer": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz",
|
||||
"integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"protobufjs": "^7.2.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
|
||||
@@ -20739,7 +20964,6 @@
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
@@ -20960,6 +21184,20 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/retry-request": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz",
|
||||
"integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/request": "^2.48.8",
|
||||
"extend": "^3.0.2",
|
||||
"teeny-request": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/reusify": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||
@@ -21916,11 +22154,25 @@
|
||||
"duplexer": "~0.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-events": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
|
||||
"integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"stubs": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-shift": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
|
||||
"integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
@@ -22138,6 +22390,12 @@
|
||||
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stubs": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
|
||||
"integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/style-mod": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
|
||||
@@ -22518,6 +22776,22 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/teeny-request": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz",
|
||||
"integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"http-proxy-agent": "^5.0.0",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"node-fetch": "^2.6.9",
|
||||
"stream-events": "^1.0.5",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/temp-file": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz",
|
||||
@@ -24755,6 +25029,7 @@
|
||||
"@connectrpc/connect": "^1.4.0",
|
||||
"@connectrpc/connect-node": "^1.4.0",
|
||||
"@getinsomnia/node-libcurl": "^2.33.7",
|
||||
"@google-cloud/secret-manager": "^5.6.0",
|
||||
"@grpc/grpc-js": "^1.12.0",
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
"@seald-io/nedb": "^4.0.4",
|
||||
@@ -24770,6 +25045,7 @@
|
||||
"electron-context-menu": "^3.6.1",
|
||||
"electron-log": "^4.4.8",
|
||||
"fastq": "^1.17.1",
|
||||
"google-auth-library": "^9.15.0",
|
||||
"graphql": "^16.8.1",
|
||||
"graphql-ws": "^5.16.0",
|
||||
"grpc-reflection-js": "Kong/grpc-reflection-js#master",
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"@connectrpc/connect": "^1.4.0",
|
||||
"@connectrpc/connect-node": "^1.4.0",
|
||||
"@getinsomnia/node-libcurl": "^2.33.7",
|
||||
"@google-cloud/secret-manager": "^5.6.0",
|
||||
"@grpc/grpc-js": "^1.12.0",
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
"@seald-io/nedb": "^4.0.4",
|
||||
@@ -58,6 +59,7 @@
|
||||
"electron-context-menu": "^3.6.1",
|
||||
"electron-log": "^4.4.8",
|
||||
"fastq": "^1.17.1",
|
||||
"google-auth-library": "^9.15.0",
|
||||
"graphql": "^16.8.1",
|
||||
"graphql-ws": "^5.16.0",
|
||||
"grpc-reflection-js": "Kong/grpc-reflection-js#master",
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as models from '../../../models';
|
||||
import type { AWSTemporaryCredential, BaseCloudCredential, CloudProviderName } from '../../../models/cloud-credential';
|
||||
import { ipcMainHandle, ipcMainOn } from '../electron';
|
||||
import { type AWSGetSecretConfig, AWSService } from './aws-service';
|
||||
import { type GCPGetSecretConfig, GCPService } from './gcp-servcie';
|
||||
import { type MaxAgeUnit, VaultCache } from './vault-cache';
|
||||
|
||||
// in-memory cache for fetched vault secrets
|
||||
@@ -22,7 +23,7 @@ export interface CloudServiceSecretOption<T extends {}> extends CloudServiceAuth
|
||||
secretId: string;
|
||||
config: T;
|
||||
}
|
||||
export type CloudServiceGetSecretConfig = AWSGetSecretConfig;
|
||||
export type CloudServiceGetSecretConfig = AWSGetSecretConfig | GCPGetSecretConfig;
|
||||
|
||||
export function registerCloudServiceHandlers() {
|
||||
ipcMainHandle('cloudService.authenticate', (_event, options) => cloudServiceProviderAuthentication(options));
|
||||
@@ -37,6 +38,8 @@ class ServiceFactory {
|
||||
switch (name) {
|
||||
case 'aws':
|
||||
return new AWSService(credential as AWSTemporaryCredential);
|
||||
case 'gcp':
|
||||
return new GCPService(credential as string);
|
||||
default:
|
||||
throw new Error('Invalid cloud service provider name');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
|
||||
import crypto from 'crypto';
|
||||
import { readFileSync } from 'fs';
|
||||
import { GoogleAuth, type JWTInput } from 'google-auth-library';
|
||||
|
||||
import type { CloudProviderName } from '../../../models/cloud-credential';
|
||||
import { isValidJSONString } from '../../../utils/json';
|
||||
import type { CloudServiceResult, GCPSecretConfig, ICloudService } from './types';
|
||||
|
||||
export const providerName: CloudProviderName = 'gcp';
|
||||
export type GCPGetSecretConfig = Omit<GCPSecretConfig, 'secretName'>;
|
||||
export class GCPService implements ICloudService {
|
||||
_keyPath: string;
|
||||
|
||||
constructor(keyPath: string) {
|
||||
this._keyPath = keyPath;
|
||||
}
|
||||
|
||||
_validateKeyPath(): { isValid: true; credentials: JWTInput } | { isValid: false; errorMessage: string } {
|
||||
const requiredFields = ['project_id', 'private_key_id', 'private_key', 'client_email'];
|
||||
const keyPath = this._keyPath;
|
||||
let isValid = true;
|
||||
let errorMessage = '';
|
||||
|
||||
try {
|
||||
const fileContent = readFileSync(keyPath, 'utf-8');
|
||||
if (isValidJSONString(fileContent)) {
|
||||
const serviceAccountKey = JSON.parse(fileContent.toString()) as JWTInput;
|
||||
isValid = requiredFields.every(field => {
|
||||
isValid = field in serviceAccountKey;
|
||||
if (!isValid) {
|
||||
errorMessage = `Required field: ${field} is missing`;
|
||||
}
|
||||
return isValid;
|
||||
});
|
||||
if (isValid) {
|
||||
return {
|
||||
isValid,
|
||||
credentials: serviceAccountKey,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
isValid = false;
|
||||
errorMessage = `Invalid JSON in file ${keyPath}`;
|
||||
};
|
||||
} catch (error) {
|
||||
isValid = false;
|
||||
errorMessage = error.message || error.toString();
|
||||
};
|
||||
return { isValid, errorMessage };
|
||||
}
|
||||
|
||||
async authenticate(): Promise<CloudServiceResult<{}>> {
|
||||
const validateResult = this._validateKeyPath();
|
||||
if (validateResult.isValid) {
|
||||
const auth = new GoogleAuth({
|
||||
credentials: validateResult.credentials,
|
||||
// General scope for GCP
|
||||
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
|
||||
});
|
||||
try {
|
||||
const client = await auth.getClient();
|
||||
// use get access token to validate credential
|
||||
await client.getAccessToken();
|
||||
return {
|
||||
success: true,
|
||||
result: {},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
result: null,
|
||||
error: { errorMessage: error?.message, errorCode: error?.code },
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
result: null,
|
||||
error: { errorMessage: validateResult.errorMessage, errorCode: '' },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
getUniqueCacheKey(secretName: string, config?: GCPGetSecretConfig) {
|
||||
const keyPath = this._keyPath;
|
||||
const { version = 'latest' } = config || {};
|
||||
const uniqueKey = `${providerName}:${keyPath}:${secretName}:${version}`;
|
||||
const uniqueKeyHash = crypto.createHash('md5').update(uniqueKey).digest('hex');
|
||||
return uniqueKeyHash;
|
||||
}
|
||||
|
||||
async getSecret(secretName: string, config: GCPGetSecretConfig): Promise<CloudServiceResult<{ value: string }>> {
|
||||
const { version } = config;
|
||||
const secretVersion = version || 'latest';
|
||||
const validateResult = this._validateKeyPath();
|
||||
if (validateResult.isValid) {
|
||||
const { credentials } = validateResult;
|
||||
const { project_id } = credentials;
|
||||
const secretClient = new SecretManagerServiceClient({
|
||||
credentials,
|
||||
});
|
||||
const fullPathSecretNamePattern = /^projects\/[a-z0-9-]+\/secrets\/[a-zA-Z0-9_-]+$/;
|
||||
const fullPathSecretNameWithVersionPattern = /^projects\/[a-z0-9-]+\/secrets\/[a-zA-Z0-9_-]+\/versions\/[a-zA-Z0-9_-]+$/;
|
||||
let finalSecretName: string;
|
||||
if (fullPathSecretNamePattern.test(secretName)) {
|
||||
// if secret name in pattern /projects/<project_id>/secrets/<secret_name> which is copied from gcp
|
||||
finalSecretName = `${secretName}/versions/${secretVersion}`;
|
||||
} else if (fullPathSecretNameWithVersionPattern.test(secretName)) {
|
||||
// if secret name with version in pattern /projects/<project_id>/secrets/<secret_name>/versions/<version> which is copied from gcp
|
||||
finalSecretName = secretName;
|
||||
} else {
|
||||
finalSecretName = `projects/${project_id}/secrets/${secretName}/versions/${secretVersion}`;
|
||||
}
|
||||
try {
|
||||
const [versionResponse] = await secretClient.accessSecretVersion({ name: finalSecretName });
|
||||
const secretResult = versionResponse.payload?.data?.toString() || '';
|
||||
return {
|
||||
success: true,
|
||||
result: { value: secretResult },
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return {
|
||||
success: false,
|
||||
result: null,
|
||||
error: { errorMessage: error.toString(), errorCode: error?.code },
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
result: null,
|
||||
error: { errorMessage: validateResult.errorMessage, errorCode: '' },
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -22,4 +22,9 @@ export interface AWSSecretConfig {
|
||||
SecretKey?: string;
|
||||
};
|
||||
|
||||
export type ExternalVaultConfig = AWSSecretConfig;
|
||||
export interface GCPSecretConfig {
|
||||
secretName: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export type ExternalVaultConfig = AWSSecretConfig | GCPSecretConfig;
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface AWSTemporaryCredential {
|
||||
sessionToken: string;
|
||||
region: string;
|
||||
}
|
||||
interface IBaseCloudCredential {
|
||||
export interface IBaseCloudCredential {
|
||||
name: string;
|
||||
provider: CloudProviderName;
|
||||
}
|
||||
@@ -21,7 +21,18 @@ export interface AWSCloudCredential extends IBaseCloudCredential {
|
||||
provider: 'aws';
|
||||
credentials: AWSTemporaryCredential;
|
||||
}
|
||||
export type BaseCloudCredential = AWSCloudCredential;
|
||||
export type CloudeProviderCredentialType = AWSTemporaryCredential;
|
||||
export interface AWSCloudCredential extends IBaseCloudCredential {
|
||||
name: string;
|
||||
provider: 'aws';
|
||||
credentials: AWSTemporaryCredential;
|
||||
}
|
||||
export interface GCPCloudCredential extends IBaseCloudCredential {
|
||||
name: string;
|
||||
provider: 'gcp';
|
||||
credentials: string;
|
||||
}
|
||||
export type BaseCloudCredential = AWSCloudCredential | GCPCloudCredential;
|
||||
export type CloudProviderCredential = BaseModel & BaseCloudCredential;
|
||||
|
||||
export const name = 'Cloud Credential';
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useFetcher } from 'react-router-dom';
|
||||
import { type BaseCloudCredential, type CloudProviderCredential, type CloudProviderName, getProviderDisplayName } from '../../../../models/cloud-credential';
|
||||
import { Icon } from '../../icon';
|
||||
import { AWSCredentialForm } from './aws-credential-form';
|
||||
import { GCPCredentialForm } from './gcp-credential-form';
|
||||
|
||||
export interface CloudCredentialModalProps {
|
||||
provider: CloudProviderName;
|
||||
@@ -87,6 +88,14 @@ export const CloudCredentialModal = (props: CloudCredentialModalProps) => {
|
||||
errorMessage={fetchErrorMessage}
|
||||
/>
|
||||
}
|
||||
{provider === 'gcp' &&
|
||||
<GCPCredentialForm
|
||||
data={providerCredential}
|
||||
isLoading={cloudCredentialFetcher.state !== 'idle'}
|
||||
onSubmit={handleFormSubmit}
|
||||
errorMessage={fetchErrorMessage}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</Dialog>
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button, Input, Label, TextField } from 'react-aria-components';
|
||||
|
||||
import { type BaseCloudCredential, type CloudProviderCredential, type CloudProviderName } from '../../../../models/cloud-credential';
|
||||
import { HelpTooltip } from '../../help-tooltip';
|
||||
import { Icon } from '../../icon';
|
||||
|
||||
export interface GCPCredentialFormProps {
|
||||
data?: CloudProviderCredential;
|
||||
onSubmit: (newData: BaseCloudCredential) => void;
|
||||
isLoading: boolean;
|
||||
errorMessage?: string;
|
||||
}
|
||||
const initialFormValue = {
|
||||
name: '',
|
||||
};
|
||||
export const providerType: CloudProviderName = 'gcp';
|
||||
|
||||
export const GCPCredentialForm = (props: GCPCredentialFormProps) => {
|
||||
const { data, onSubmit, isLoading, errorMessage } = props;
|
||||
const [inputKeyPath, setInputKeyPath] = useState(data?.credentials as string);
|
||||
const isEdit = !!data;
|
||||
const { name } = data || initialFormValue;
|
||||
|
||||
const handleSelectFile = async () => {
|
||||
const { canceled, filePaths } = await window.dialog.showOpenDialog({
|
||||
title: 'Select Service Account Key File',
|
||||
buttonLabel: 'Select',
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: 'JSON File', extensions: ['json'] },
|
||||
],
|
||||
});
|
||||
if (canceled) {
|
||||
return;
|
||||
}
|
||||
const selectedFile = filePaths[0];
|
||||
setInputKeyPath(selectedFile);
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
className='flex flex-col gap-2 flex-shrink-0'
|
||||
onSubmit={e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const { name } = Object.fromEntries(formData.entries()) as Record<string, string>;
|
||||
const newData = {
|
||||
name,
|
||||
provider: providerType,
|
||||
credentials: inputKeyPath!,
|
||||
};
|
||||
onSubmit(newData);
|
||||
}}
|
||||
>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<TextField
|
||||
className="flex flex-col gap-2"
|
||||
defaultValue={name}
|
||||
>
|
||||
<Label className='col-span-4'>
|
||||
Credential Name:
|
||||
</Label>
|
||||
<Input
|
||||
required
|
||||
className='py-1 h-8 w-full pl-2 pr-7 rounded-sm border border-solid border-[--hl-sm] bg-[--color-bg] text-[--color-font] focus:outline-none focus:ring-1 focus:ring-[--hl-md] transition-colors flex-1 placeholder:italic placeholder:opacity-60 col-span-3'
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder="Credential name"
|
||||
/>
|
||||
</TextField>
|
||||
<div>
|
||||
<label>
|
||||
Service Account Key File Path:
|
||||
<HelpTooltip className='ml-2 sapce-left'>Enter the path of your service account key file which is generated in GCP console</HelpTooltip>
|
||||
</label>
|
||||
</div>
|
||||
<div className='mt-2 flex gap-3'>
|
||||
<Input
|
||||
className='py-1 w-4/5 pl-2 pr-7 rounded-sm border border-solid border-[--hl-sm] bg-[--color-bg] text-[--color-font] focus:outline-none focus:ring-1 focus:ring-[--hl-md] transition-colors flex-1 placeholder:italic placeholder:opacity-60 col-span-3'
|
||||
placeholder="Service account key path"
|
||||
aria-label='Input Serice Account Key Path'
|
||||
value={inputKeyPath}
|
||||
onChange={e => setInputKeyPath(e.target.value)}
|
||||
/>
|
||||
<Button
|
||||
className="flex-shrink-0 border-solid border border-[--hl-`sm] py-1 items-center justify-center px-4 aria-pressed:bg-[--hl-sm] aria-selected:bg-[--hl-sm] rounded-sm text-[--color-font] hover:bg-[--hl-xs] focus:ring-inset ring-1 ring-transparent transition-all text-base"
|
||||
onPress={handleSelectFile}
|
||||
>
|
||||
<Icon icon="file" className='mr-2' />
|
||||
<span>Select File</span>
|
||||
</Button>
|
||||
</div>
|
||||
{(errorMessage) &&
|
||||
<p className="notice error margin-top-sm no-margin-bottom">{errorMessage}</p>
|
||||
}
|
||||
<div className='w-full flex flex-row items-center justify-end gap-[--padding-md] pt-[--padding-md]'>
|
||||
<Button
|
||||
className="hover:no-underline text-right bg-[--color-surprise] hover:bg-opacity-90 border border-solid border-[--hl-md] py-2 px-3 text-[--color-font-surprise] transition-colors rounded-sm"
|
||||
type='submit'
|
||||
isDisabled={isLoading || !inputKeyPath}
|
||||
>
|
||||
{isLoading && <Icon icon="spinner" className="text-[--color-font] animate-spin m-auto inline-block mr-2" />}
|
||||
{isEdit ? 'Update' : 'Create'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form >
|
||||
);
|
||||
};
|
||||
@@ -9,7 +9,9 @@ import { Icon } from '../icon';
|
||||
import { showModal } from '../modals';
|
||||
import { AskModal } from '../modals/ask-modal';
|
||||
import { CloudCredentialModal } from '../modals/cloud-credential-modal/cloud-credential-modal';
|
||||
import { SvgIcon } from '../svg-icon';
|
||||
import { UpgradeNotice } from '../upgrade-notice';
|
||||
import { NumberSetting } from './number-setting';
|
||||
|
||||
interface createCredentialItemType {
|
||||
name: string;
|
||||
@@ -22,6 +24,11 @@ const createCredentialItemList: createCredentialItemType[] = [
|
||||
name: getProviderDisplayName('aws'),
|
||||
icon: <i className="ml-1 fa-brands fa-aws" />,
|
||||
},
|
||||
{
|
||||
id: 'gcp',
|
||||
name: getProviderDisplayName('gcp'),
|
||||
icon: <SvgIcon icon='gcp-logo' className='ml-1' />,
|
||||
},
|
||||
];
|
||||
const buttonClassName = 'disabled:opacity-50 h-7 aspect-square aria-pressed:bg-[--hl-sm] rounded-sm text-[--color-font] hover:bg-[--hl-xs] transition-all text-sm py-1 px-2';
|
||||
|
||||
@@ -137,12 +144,14 @@ export const CloudServiceCredentialList = () => {
|
||||
</td>
|
||||
<td className='w-52 whitespace-nowrap'>
|
||||
<div className='flex gap-2'>
|
||||
<Button
|
||||
className={`${buttonClassName} w-16`}
|
||||
onPress={() => setModalState({ show: true, provider: provider!, credential: cloudCred })}
|
||||
>
|
||||
<Icon icon="edit" /> Edit
|
||||
</Button>
|
||||
{(provider === 'aws' || provider === 'gcp') &&
|
||||
<Button
|
||||
className={`${buttonClassName} w-16`}
|
||||
onPress={() => setModalState({ show: true, provider: provider!, credential: cloudCred })}
|
||||
>
|
||||
<Icon icon="edit" /> Edit
|
||||
</Button>
|
||||
}
|
||||
<Button
|
||||
className={`${buttonClassName} w-20`}
|
||||
onPress={() => handleDeleteItem(_id, name)}
|
||||
@@ -157,6 +166,24 @@ export const CloudServiceCredentialList = () => {
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
<div>
|
||||
<h2 className='font-bold pt-5 pb-2 text-lg bg-[--color-bg] z-10'>Cloud Secret Config</h2>
|
||||
<div className="form-row items-end justify-between">
|
||||
<NumberSetting
|
||||
label="Secret Cache Duration(min)"
|
||||
setting="vaultSecretCacheDuration"
|
||||
help="Enter the amount of time in minutes external vault secrets are cached in Insomnia. Enter 0 to disable cache. Click the Reset Cache button to clear all cache."
|
||||
min={0}
|
||||
max={720}
|
||||
/>
|
||||
<button
|
||||
className="w-32 flex items-center gap-2 border border-solid border-[--hl-lg] px-[--padding-md] h-[--line-height-xs] rounded-[--radius-md] hover:bg-[--hl-xs] pointer mb-[--padding-sm] ml-[--padding-sm]"
|
||||
onClick={() => window.main.cloudService.clearCache()}
|
||||
>
|
||||
Reset Cache
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{modalState && modalState.show &&
|
||||
<CloudCredentialModal
|
||||
provider={modalState.provider}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { AWSGetSecretConfig } from '../../../main/ipc/cloud-service-integration/aws-service';
|
||||
import type { CloudServiceSecretOption } from '../../../main/ipc/cloud-service-integration/cloud-service';
|
||||
import type { GCPGetSecretConfig } from '../../../main/ipc/cloud-service-integration/gcp-servcie';
|
||||
import type { GCPSecretConfig } from '../../../main/ipc/cloud-service-integration/types';
|
||||
import type { AWSSecretConfig, ExternalVaultConfig } from '../../../main/ipc/cloud-service-integration/types';
|
||||
import type { CloudProviderCredential, CloudProviderName } from '../../../models/cloud-credential';
|
||||
|
||||
@@ -7,6 +9,8 @@ export const getExternalVault = async (provider: CloudProviderName, providerCred
|
||||
switch (provider) {
|
||||
case 'aws':
|
||||
return getAWSSecret(secretConfig as AWSSecretConfig, providerCredential);
|
||||
case 'gcp':
|
||||
return getGCPSecret(secretConfig as GCPSecretConfig, providerCredential);
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@@ -50,3 +54,23 @@ export const getAWSSecret = async (secretConfig: AWSSecretConfig, providerCreden
|
||||
throw new Error(`Get secret from AWS failed: ${error?.errorMessage}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const getGCPSecret = async (secretConfig: GCPSecretConfig, providerCredential: CloudProviderCredential) => {
|
||||
const { secretName, version } = secretConfig;
|
||||
if (!secretName) {
|
||||
throw new Error('Get secret from GCP failed: Secret Name is required');
|
||||
}
|
||||
const getSecretOption: CloudServiceSecretOption<GCPGetSecretConfig> = {
|
||||
provider: 'gcp',
|
||||
secretId: secretName,
|
||||
credentials: providerCredential.credentials,
|
||||
config: { version },
|
||||
};
|
||||
const secretResult = await window.main.cloudService.getSecret(getSecretOption);
|
||||
const { success, error, result } = secretResult;
|
||||
if (success && result) {
|
||||
return result.value;
|
||||
} else {
|
||||
throw new Error(`Get secret from GCP failed: ${error?.errorMessage}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,12 +2,13 @@ import React, { useState } from 'react';
|
||||
import { Button } from 'react-aria-components';
|
||||
|
||||
import { debounce } from '../../../../common/misc';
|
||||
import type { AWSSecretConfig, ExternalVaultConfig } from '../../../../main/ipc/cloud-service-integration/types';
|
||||
import type { AWSSecretConfig, ExternalVaultConfig, GCPSecretConfig } from '../../../../main/ipc/cloud-service-integration/types';
|
||||
import { type CloudProviderCredential, type CloudProviderName, type } from '../../../../models/cloud-credential';
|
||||
import { Icon } from '../../icon';
|
||||
import { CloudCredentialModal } from '../../modals/cloud-credential-modal/cloud-credential-modal';
|
||||
import type { ArgConfigFormProps } from '../tag-editor-arg-sub-form';
|
||||
import { AWSSecretManagerForm } from './aws-secret-manager-form';
|
||||
import { GCPSecretManagerForm } from './gcp-secret-manager-form';
|
||||
|
||||
export const ExternalVaultForm = (props: ArgConfigFormProps) => {
|
||||
const { onChange, configValue, activeTagData, docs } = props;
|
||||
@@ -34,6 +35,15 @@ export const ExternalVaultForm = (props: ArgConfigFormProps) => {
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case 'gcp':
|
||||
SubForm = (
|
||||
<GCPSecretManagerForm
|
||||
formData={formData as GCPSecretConfig}
|
||||
onChange={handleFormChange}
|
||||
activeTagData={activeTagData}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
SubForm = null;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
|
||||
import type { GCPSecretConfig } from '../../../../main/ipc/cloud-service-integration/types';
|
||||
import type { NunjucksParsedTag } from '../../../../templating/utils';
|
||||
import { HelpTooltip } from '../../help-tooltip';
|
||||
|
||||
export interface GCPSecretManagerFormProps {
|
||||
formData: GCPSecretConfig;
|
||||
onChange: (newConfig: GCPSecretConfig) => void;
|
||||
activeTagData: NunjucksParsedTag;
|
||||
}
|
||||
|
||||
export const GCPSecretManagerForm = (props: GCPSecretManagerFormProps) => {
|
||||
const { formData, onChange } = props;
|
||||
const {
|
||||
secretName,
|
||||
version = 'latest',
|
||||
} = formData;
|
||||
const handleOnChange = (name: keyof GCPSecretConfig, newValue: string) => {
|
||||
const newConfig = {
|
||||
...formData,
|
||||
[name]: newValue,
|
||||
};
|
||||
onChange(newConfig as unknown as GCPSecretConfig);
|
||||
|
||||
};
|
||||
return (
|
||||
<form id='gcp-secret-manager-form'>
|
||||
<div className="form-row">
|
||||
<div className="form-control">
|
||||
<label>
|
||||
Secret Name
|
||||
<input
|
||||
name='secretName'
|
||||
defaultValue={secretName}
|
||||
onChange={e => handleOnChange('secretName', e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-row">
|
||||
<div className="form-control">
|
||||
<label>
|
||||
Version
|
||||
<HelpTooltip className="space-left">
|
||||
Optional version of the secret to retrieve, by default as latest.
|
||||
</HelpTooltip>
|
||||
<input
|
||||
name='version'
|
||||
defaultValue={version}
|
||||
onChange={e => handleOnChange('version', e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@@ -35,6 +35,7 @@ const localTemplatePlugins: { templateTag: PluginTemplateTag }[] = [
|
||||
type: 'enum',
|
||||
options: [
|
||||
{ displayName: 'AWS Secrets Manager', value: 'aws' },
|
||||
{ displayName: 'GCP Secret Manager', value: 'gcp' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
|
||||
import type { BaseModel } from '../../../models';
|
||||
import type { NunjucksParsedTag } from '../../../templating/utils';
|
||||
import { isValidJSONString } from '../../../utils/json';
|
||||
import { ExternalVaultForm } from './external-vault/external-vault-form';
|
||||
|
||||
export interface ArgConfigFormProps {
|
||||
@@ -14,14 +15,13 @@ export interface ArgConfigFormProps {
|
||||
const formTagNameMapping = {
|
||||
'vault': ExternalVaultForm,
|
||||
};
|
||||
const isValidJSONString = (input: string) => {
|
||||
try {
|
||||
const parsedJson = JSON.parse(input);
|
||||
// Check if the parsed JSON is an object and not an array or null
|
||||
return typeof parsedJson === 'object' && parsedJson !== null && !Array.isArray(parsedJson);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
const isValidJSONObjectString = (input: string) => {
|
||||
if (isValidJSONString(input)) {
|
||||
const parsedContent = JSON.parse(input);
|
||||
// Check if the parsed JSON is an real object.
|
||||
return typeof parsedContent === 'object' && parsedContent !== null && !Array.isArray(parsedContent);
|
||||
};
|
||||
return false;
|
||||
};
|
||||
export const couldRenderForm = (name: string) => name in formTagNameMapping;
|
||||
|
||||
@@ -30,7 +30,7 @@ export const ArgConfigSubForm = (props: ArgConfigFormProps) => {
|
||||
const tagName = activeTagDefinition.name as keyof typeof formTagNameMapping;
|
||||
const ConfigForm = formTagNameMapping[tagName];
|
||||
|
||||
if (ConfigForm && isValidJSONString(configValue)) {
|
||||
if (ConfigForm && isValidJSONObjectString(configValue)) {
|
||||
return <ConfigForm {...props} />;
|
||||
}
|
||||
return configValue;
|
||||
|
||||
8
packages/insomnia/src/utils/json.ts
Normal file
8
packages/insomnia/src/utils/json.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const isValidJSONString = (input: string): boolean => {
|
||||
try {
|
||||
JSON.parse(input);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user