mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-09 23:28:31 +00:00
Revert "Revert "feat: secret reference graph for understanding how its pulled""
This commit is contained in:
Generated
+111
-115
@@ -5852,12 +5852,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@probot/pino": {
|
"node_modules/@probot/pino": {
|
||||||
"version": "2.4.0",
|
"version": "2.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@probot/pino/-/pino-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@probot/pino/-/pino-2.5.0.tgz",
|
||||||
"integrity": "sha512-KUJ3eK2zLrPny7idWm9eQbBNhCJUjm1A1ttA6U4qiR2/ONWSffVlvr8oR26L59sVhoDkv1DOGmGPZS/bvSFisw==",
|
"integrity": "sha512-I7zI6MWP1wz9qvTY8U3wOWeRXY2NiuTDqf91v/LQl9oiffUHl+Z1YelRvNcvHbaUo/GK7E1mJr+Sw4dHuSGxpg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/node": "^6.0.0",
|
"@sentry/node": "^7.119.2",
|
||||||
"pino-pretty": "^6.0.0",
|
"pino-pretty": "^6.0.0",
|
||||||
"pump": "^3.0.0",
|
"pump": "^3.0.0",
|
||||||
"readable-stream": "^3.6.0",
|
"readable-stream": "^3.6.0",
|
||||||
@@ -6147,118 +6147,85 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@sentry-internal/tracing": {
|
||||||
|
"version": "7.119.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.119.2.tgz",
|
||||||
|
"integrity": "sha512-V2W+STWrafyGJhQv3ulMFXYDwWHiU6wHQAQBShsHVACiFaDrJ2kPRet38FKv4dMLlLlP2xN+ss2e5zv3tYlTiQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/core": "7.119.2",
|
||||||
|
"@sentry/types": "7.119.2",
|
||||||
|
"@sentry/utils": "7.119.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@sentry/core": {
|
"node_modules/@sentry/core": {
|
||||||
"version": "6.19.7",
|
"version": "7.119.2",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.19.7.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.119.2.tgz",
|
||||||
"integrity": "sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw==",
|
"integrity": "sha512-hQr3d2yWq/2lMvoyBPOwXw1IHqTrCjOsU1vYKhAa6w9vGbJZFGhKGGE2KEi/92c3gqGn+gW/PC7cV6waCTDuVA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/hub": "6.19.7",
|
"@sentry/types": "7.119.2",
|
||||||
"@sentry/minimal": "6.19.7",
|
"@sentry/utils": "7.119.2"
|
||||||
"@sentry/types": "6.19.7",
|
|
||||||
"@sentry/utils": "6.19.7",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/core/node_modules/tslib": {
|
"node_modules/@sentry/integrations": {
|
||||||
"version": "1.14.1",
|
"version": "7.119.2",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.119.2.tgz",
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
"integrity": "sha512-dCuXKvbUE3gXVVa696SYMjlhSP6CxpMH/gl4Jk26naEB8Xjsn98z/hqEoXLg6Nab73rjR9c/9AdKqBbwVMHyrQ==",
|
||||||
},
|
"license": "MIT",
|
||||||
"node_modules/@sentry/hub": {
|
|
||||||
"version": "6.19.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.19.7.tgz",
|
|
||||||
"integrity": "sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/types": "6.19.7",
|
"@sentry/core": "7.119.2",
|
||||||
"@sentry/utils": "6.19.7",
|
"@sentry/types": "7.119.2",
|
||||||
"tslib": "^1.9.3"
|
"@sentry/utils": "7.119.2",
|
||||||
|
"localforage": "^1.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/hub/node_modules/tslib": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
|
||||||
},
|
|
||||||
"node_modules/@sentry/minimal": {
|
|
||||||
"version": "6.19.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.19.7.tgz",
|
|
||||||
"integrity": "sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@sentry/hub": "6.19.7",
|
|
||||||
"@sentry/types": "6.19.7",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sentry/minimal/node_modules/tslib": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
|
||||||
},
|
|
||||||
"node_modules/@sentry/node": {
|
"node_modules/@sentry/node": {
|
||||||
"version": "6.19.7",
|
"version": "7.119.2",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.19.7.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.119.2.tgz",
|
||||||
"integrity": "sha512-gtmRC4dAXKODMpHXKfrkfvyBL3cI8y64vEi3fDD046uqYcrWdgoQsffuBbxMAizc6Ez1ia+f0Flue6p15Qaltg==",
|
"integrity": "sha512-TPNnqxh+Myooe4jTyRiXrzrM2SH08R4+nrmBls4T7lKp2E5R/3mDSe/YTn5rRcUt1k1hPx1NgO/taG0DoS5cXA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/core": "6.19.7",
|
"@sentry-internal/tracing": "7.119.2",
|
||||||
"@sentry/hub": "6.19.7",
|
"@sentry/core": "7.119.2",
|
||||||
"@sentry/types": "6.19.7",
|
"@sentry/integrations": "7.119.2",
|
||||||
"@sentry/utils": "6.19.7",
|
"@sentry/types": "7.119.2",
|
||||||
"cookie": "^0.4.1",
|
"@sentry/utils": "7.119.2"
|
||||||
"https-proxy-agent": "^5.0.0",
|
|
||||||
"lru_map": "^0.3.3",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/node/node_modules/cookie": {
|
|
||||||
"version": "0.4.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
|
|
||||||
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sentry/node/node_modules/tslib": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
|
||||||
},
|
|
||||||
"node_modules/@sentry/types": {
|
"node_modules/@sentry/types": {
|
||||||
"version": "6.19.7",
|
"version": "7.119.2",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.19.7.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.119.2.tgz",
|
||||||
"integrity": "sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==",
|
"integrity": "sha512-ydq1tWsdG7QW+yFaTp0gFaowMLNVikIqM70wxWNK+u98QzKnVY/3XTixxNLsUtnAB4Y+isAzFhrc6Vb5GFdFeg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/utils": {
|
"node_modules/@sentry/utils": {
|
||||||
"version": "6.19.7",
|
"version": "7.119.2",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.19.7.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.119.2.tgz",
|
||||||
"integrity": "sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==",
|
"integrity": "sha512-TLdUCvcNgzKP0r9YD7tgCL1PEUp42TObISridsPJ5rhpVGQJvpr+Six0zIkfDUxerLYWZoK8QMm9KgFlPLNQzA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/types": "6.19.7",
|
"@sentry/types": "7.119.2"
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/utils/node_modules/tslib": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
|
||||||
},
|
|
||||||
"node_modules/@serdnam/pino-cloudwatch-transport": {
|
"node_modules/@serdnam/pino-cloudwatch-transport": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@serdnam/pino-cloudwatch-transport/-/pino-cloudwatch-transport-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@serdnam/pino-cloudwatch-transport/-/pino-cloudwatch-transport-1.0.4.tgz",
|
||||||
@@ -9728,9 +9695,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/cookie": {
|
"node_modules/cookie": {
|
||||||
"version": "0.6.0",
|
"version": "0.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
@@ -10863,9 +10830,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express": {
|
"node_modules/express": {
|
||||||
"version": "4.21.0",
|
"version": "4.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||||
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.8",
|
"accepts": "~1.3.8",
|
||||||
@@ -10873,7 +10840,7 @@
|
|||||||
"body-parser": "1.20.3",
|
"body-parser": "1.20.3",
|
||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
"content-type": "~1.0.4",
|
"content-type": "~1.0.4",
|
||||||
"cookie": "0.6.0",
|
"cookie": "0.7.1",
|
||||||
"cookie-signature": "1.0.6",
|
"cookie-signature": "1.0.6",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"depd": "2.0.0",
|
"depd": "2.0.0",
|
||||||
@@ -10905,12 +10872,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express-session": {
|
"node_modules/express-session": {
|
||||||
"version": "1.18.0",
|
"version": "1.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz",
|
||||||
"integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==",
|
"integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==",
|
||||||
|
"license": "MIT",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie": "0.6.0",
|
"cookie": "0.7.2",
|
||||||
"cookie-signature": "1.0.7",
|
"cookie-signature": "1.0.7",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"depd": "~2.0.0",
|
"depd": "~2.0.0",
|
||||||
@@ -10944,6 +10912,15 @@
|
|||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/express/node_modules/cookie": {
|
||||||
|
"version": "0.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||||
|
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/express/node_modules/cookie-signature": {
|
"node_modules/express/node_modules/cookie-signature": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
@@ -12424,6 +12401,12 @@
|
|||||||
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
|
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/immediate": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
@@ -13420,13 +13403,22 @@
|
|||||||
"libsodium": "^0.7.13"
|
"libsodium": "^0.7.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lie": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"immediate": "~3.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/light-my-request": {
|
"node_modules/light-my-request": {
|
||||||
"version": "5.13.0",
|
"version": "5.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.14.0.tgz",
|
||||||
"integrity": "sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==",
|
"integrity": "sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie": "^0.6.0",
|
"cookie": "^0.7.0",
|
||||||
"process-warning": "^3.0.0",
|
"process-warning": "^3.0.0",
|
||||||
"set-cookie-parser": "^2.4.1"
|
"set-cookie-parser": "^2.4.1"
|
||||||
}
|
}
|
||||||
@@ -13505,6 +13497,15 @@
|
|||||||
"url": "https://github.com/sponsors/antfu"
|
"url": "https://github.com/sponsors/antfu"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/localforage": {
|
||||||
|
"version": "1.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
|
||||||
|
"integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"lie": "3.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/locate-path": {
|
"node_modules/locate-path": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||||
@@ -13632,11 +13633,6 @@
|
|||||||
"get-func-name": "^2.0.1"
|
"get-func-name": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lru_map": {
|
|
||||||
"version": "0.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
|
|
||||||
"integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ=="
|
|
||||||
},
|
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
@@ -15261,9 +15257,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
@@ -18865,9 +18861,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.4.8",
|
"version": "5.4.9",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz",
|
||||||
"integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==",
|
"integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -669,6 +669,12 @@ export const RAW_SECRETS = {
|
|||||||
type: "The type of the secret to delete.",
|
type: "The type of the secret to delete.",
|
||||||
projectSlug: "The slug of the project to delete the secret in.",
|
projectSlug: "The slug of the project to delete the secret in.",
|
||||||
workspaceId: "The ID of the project where the secret is located."
|
workspaceId: "The ID of the project where the secret is located."
|
||||||
|
},
|
||||||
|
GET_REFERENCE_TREE: {
|
||||||
|
secretName: "The name of the secret to get the reference tree for.",
|
||||||
|
workspaceId: "The ID of the project where the secret is located.",
|
||||||
|
environment: "The slug of the environment where the the secret is located.",
|
||||||
|
secretPath: "The folder path where the secret is located."
|
||||||
}
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,18 @@ import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
|||||||
|
|
||||||
import { secretRawSchema } from "../sanitizedSchemas";
|
import { secretRawSchema } from "../sanitizedSchemas";
|
||||||
|
|
||||||
|
const SecretReferenceNode = z.object({
|
||||||
|
key: z.string(),
|
||||||
|
value: z.string().optional(),
|
||||||
|
environment: z.string(),
|
||||||
|
secretPath: z.string()
|
||||||
|
});
|
||||||
|
type TSecretReferenceNode = z.infer<typeof SecretReferenceNode> & { children: TSecretReferenceNode[] };
|
||||||
|
|
||||||
|
const SecretReferenceNodeTree: z.ZodType<TSecretReferenceNode> = SecretReferenceNode.extend({
|
||||||
|
children: z.lazy(() => SecretReferenceNodeTree.array())
|
||||||
|
});
|
||||||
|
|
||||||
export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -2102,6 +2114,58 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/raw/:secretName/secret-reference-tree",
|
||||||
|
config: {
|
||||||
|
rateLimit: secretsLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: "Get secret reference tree",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
secretName: z.string().trim().describe(RAW_SECRETS.GET_REFERENCE_TREE.secretName)
|
||||||
|
}),
|
||||||
|
querystring: z.object({
|
||||||
|
workspaceId: z.string().trim().describe(RAW_SECRETS.GET_REFERENCE_TREE.workspaceId),
|
||||||
|
environment: z.string().trim().describe(RAW_SECRETS.GET_REFERENCE_TREE.environment),
|
||||||
|
secretPath: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.default("/")
|
||||||
|
.transform(removeTrailingSlash)
|
||||||
|
.describe(RAW_SECRETS.GET_REFERENCE_TREE.secretPath)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
tree: SecretReferenceNodeTree,
|
||||||
|
value: z.string().optional()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { secretName } = req.params;
|
||||||
|
const { secretPath, environment, workspaceId } = req.query;
|
||||||
|
const { tree, value } = await server.services.secret.getSecretReferenceTree({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
projectId: workspaceId,
|
||||||
|
secretName,
|
||||||
|
secretPath,
|
||||||
|
environment
|
||||||
|
});
|
||||||
|
|
||||||
|
return { tree, value };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/backfill-secret-references",
|
url: "/backfill-secret-references",
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
|
|||||||
import { TFnSecretBulkDelete, TFnSecretBulkInsert, TFnSecretBulkUpdate } from "./secret-v2-bridge-types";
|
import { TFnSecretBulkDelete, TFnSecretBulkInsert, TFnSecretBulkUpdate } from "./secret-v2-bridge-types";
|
||||||
|
|
||||||
const INTERPOLATION_SYNTAX_REG = /\${([^}]+)}/g;
|
const INTERPOLATION_SYNTAX_REG = /\${([^}]+)}/g;
|
||||||
|
// akhilmhdh: JS regex with global save state in .test
|
||||||
|
const INTERPOLATION_SYNTAX_REG_NON_GLOBAL = /\${([^}]+)}/;
|
||||||
|
|
||||||
export const shouldUseSecretV2Bridge = (version: number) => version === 3;
|
export const shouldUseSecretV2Bridge = (version: number) => version === 3;
|
||||||
|
|
||||||
@@ -376,6 +378,13 @@ const formatMultiValueEnv = (val?: string) => {
|
|||||||
return `"${val.replace(/\n/g, "\\n")}"`;
|
return `"${val.replace(/\n/g, "\\n")}"`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type TSecretReferenceTraceNode = {
|
||||||
|
key: string;
|
||||||
|
value?: string;
|
||||||
|
environment: string;
|
||||||
|
secretPath: string;
|
||||||
|
children: TSecretReferenceTraceNode[];
|
||||||
|
};
|
||||||
type TInterpolateSecretArg = {
|
type TInterpolateSecretArg = {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
decryptSecretValue: (encryptedValue?: Buffer | null) => string | undefined;
|
decryptSecretValue: (encryptedValue?: Buffer | null) => string | undefined;
|
||||||
@@ -417,14 +426,21 @@ export const expandSecretReferencesFactory = ({
|
|||||||
return secretCache[cacheKey][secretKey] || { value: "", tags: [] };
|
return secretCache[cacheKey][secretKey] || { value: "", tags: [] };
|
||||||
};
|
};
|
||||||
|
|
||||||
const recursivelyExpandSecret = async (dto: { value?: string; secretPath: string; environment: string }) => {
|
const recursivelyExpandSecret = async (dto: {
|
||||||
if (!dto.value) return "";
|
value?: string;
|
||||||
|
secretPath: string;
|
||||||
|
environment: string;
|
||||||
|
shouldStackTrace?: boolean;
|
||||||
|
}) => {
|
||||||
|
const stackTrace = { ...dto, key: "root", children: [] } as TSecretReferenceTraceNode;
|
||||||
|
|
||||||
const stack = [{ ...dto, depth: 0 }];
|
if (!dto.value) return { expandedValue: "", stackTrace };
|
||||||
|
const stack = [{ ...dto, depth: 0, trace: stackTrace }];
|
||||||
let expandedValue = dto.value;
|
let expandedValue = dto.value;
|
||||||
|
|
||||||
while (stack.length) {
|
while (stack.length) {
|
||||||
const { value, secretPath, environment, depth } = stack.pop()!;
|
const { value, secretPath, environment, depth, trace } = stack.pop()!;
|
||||||
|
|
||||||
// eslint-disable-next-line no-continue
|
// eslint-disable-next-line no-continue
|
||||||
if (depth > MAX_SECRET_REFERENCE_DEPTH) continue;
|
if (depth > MAX_SECRET_REFERENCE_DEPTH) continue;
|
||||||
const refs = value?.match(INTERPOLATION_SYNTAX_REG);
|
const refs = value?.match(INTERPOLATION_SYNTAX_REG);
|
||||||
@@ -437,6 +453,11 @@ export const expandSecretReferencesFactory = ({
|
|||||||
// eslint-disable-next-line no-continue
|
// eslint-disable-next-line no-continue
|
||||||
if (!entities.length) continue;
|
if (!entities.length) continue;
|
||||||
|
|
||||||
|
let referencedSecretPath = "";
|
||||||
|
let referencedSecretKey = "";
|
||||||
|
let referencedSecretEnvironmentSlug = "";
|
||||||
|
let referencedSecretValue = "";
|
||||||
|
|
||||||
if (entities.length === 1) {
|
if (entities.length === 1) {
|
||||||
const [secretKey] = entities;
|
const [secretKey] = entities;
|
||||||
|
|
||||||
@@ -449,17 +470,11 @@ export const expandSecretReferencesFactory = ({
|
|||||||
|
|
||||||
const cacheKey = getCacheUniqueKey(environment, secretPath);
|
const cacheKey = getCacheUniqueKey(environment, secretPath);
|
||||||
secretCache[cacheKey][secretKey] = referredValue;
|
secretCache[cacheKey][secretKey] = referredValue;
|
||||||
if (INTERPOLATION_SYNTAX_REG.test(referredValue.value)) {
|
|
||||||
stack.push({
|
referencedSecretValue = referredValue.value;
|
||||||
value: referredValue.value,
|
referencedSecretKey = secretKey;
|
||||||
secretPath,
|
referencedSecretPath = secretPath;
|
||||||
environment,
|
referencedSecretEnvironmentSlug = environment;
|
||||||
depth: depth + 1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (referredValue) {
|
|
||||||
expandedValue = expandedValue.replaceAll(interpolationSyntax, referredValue.value);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const secretReferenceEnvironment = entities[0];
|
const secretReferenceEnvironment = entities[0];
|
||||||
const secretReferencePath = path.join("/", ...entities.slice(1, entities.length - 1));
|
const secretReferencePath = path.join("/", ...entities.slice(1, entities.length - 1));
|
||||||
@@ -474,24 +489,42 @@ export const expandSecretReferencesFactory = ({
|
|||||||
|
|
||||||
const cacheKey = getCacheUniqueKey(secretReferenceEnvironment, secretReferencePath);
|
const cacheKey = getCacheUniqueKey(secretReferenceEnvironment, secretReferencePath);
|
||||||
secretCache[cacheKey][secretReferenceKey] = referedValue;
|
secretCache[cacheKey][secretReferenceKey] = referedValue;
|
||||||
if (INTERPOLATION_SYNTAX_REG.test(referedValue.value)) {
|
|
||||||
stack.push({
|
|
||||||
value: referedValue.value,
|
|
||||||
secretPath: secretReferencePath,
|
|
||||||
environment: secretReferenceEnvironment,
|
|
||||||
depth: depth + 1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (referedValue) {
|
referencedSecretValue = referedValue.value;
|
||||||
expandedValue = expandedValue.replaceAll(interpolationSyntax, referedValue.value);
|
referencedSecretKey = secretReferenceKey;
|
||||||
|
referencedSecretPath = secretReferencePath;
|
||||||
|
referencedSecretEnvironmentSlug = secretReferenceEnvironment;
|
||||||
|
}
|
||||||
|
|
||||||
|
const node = {
|
||||||
|
value: referencedSecretValue,
|
||||||
|
secretPath: referencedSecretPath,
|
||||||
|
environment: referencedSecretEnvironmentSlug,
|
||||||
|
depth: depth + 1,
|
||||||
|
trace
|
||||||
|
};
|
||||||
|
|
||||||
|
const shouldExpandMore = INTERPOLATION_SYNTAX_REG_NON_GLOBAL.test(referencedSecretValue);
|
||||||
|
if (dto.shouldStackTrace) {
|
||||||
|
const stackTraceNode = { ...node, children: [], key: referencedSecretKey, trace: null };
|
||||||
|
trace?.children.push(stackTraceNode);
|
||||||
|
// if stack trace this would be child node
|
||||||
|
if (shouldExpandMore) {
|
||||||
|
stack.push({ ...node, trace: stackTraceNode });
|
||||||
}
|
}
|
||||||
|
} else if (shouldExpandMore) {
|
||||||
|
// if no stack trace is needed we just keep going with root node
|
||||||
|
stack.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (referencedSecretValue) {
|
||||||
|
expandedValue = expandedValue.replaceAll(interpolationSyntax, referencedSecretValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return expandedValue;
|
return { expandedValue, stackTrace };
|
||||||
};
|
};
|
||||||
|
|
||||||
const expandSecret = async (inputSecret: {
|
const expandSecret = async (inputSecret: {
|
||||||
@@ -505,10 +538,21 @@ export const expandSecretReferencesFactory = ({
|
|||||||
const shouldExpand = Boolean(inputSecret.value?.match(INTERPOLATION_SYNTAX_REG));
|
const shouldExpand = Boolean(inputSecret.value?.match(INTERPOLATION_SYNTAX_REG));
|
||||||
if (!shouldExpand) return inputSecret.value;
|
if (!shouldExpand) return inputSecret.value;
|
||||||
|
|
||||||
const expandedSecretValue = await recursivelyExpandSecret(inputSecret);
|
const { expandedValue } = await recursivelyExpandSecret(inputSecret);
|
||||||
return inputSecret.skipMultilineEncoding ? formatMultiValueEnv(expandedSecretValue) : expandedSecretValue;
|
|
||||||
|
return inputSecret.skipMultilineEncoding ? formatMultiValueEnv(expandedValue) : expandedValue;
|
||||||
};
|
};
|
||||||
return expandSecret;
|
|
||||||
|
const getExpandedSecretStackTrace = async (inputSecret: {
|
||||||
|
value?: string;
|
||||||
|
secretPath: string;
|
||||||
|
environment: string;
|
||||||
|
}) => {
|
||||||
|
const { stackTrace, expandedValue } = await recursivelyExpandSecret({ ...inputSecret, shouldStackTrace: true });
|
||||||
|
return { stackTrace, expandedValue };
|
||||||
|
};
|
||||||
|
|
||||||
|
return { expandSecretReferences: expandSecret, getExpandedSecretStackTrace };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reshapeBridgeSecret = (
|
export const reshapeBridgeSecret = (
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import {
|
|||||||
TDeleteManySecretDTO,
|
TDeleteManySecretDTO,
|
||||||
TDeleteSecretDTO,
|
TDeleteSecretDTO,
|
||||||
TGetASecretDTO,
|
TGetASecretDTO,
|
||||||
|
TGetSecretReferencesTreeDTO,
|
||||||
TGetSecretsDTO,
|
TGetSecretsDTO,
|
||||||
TGetSecretVersionsDTO,
|
TGetSecretVersionsDTO,
|
||||||
TMoveSecretsDTO,
|
TMoveSecretsDTO,
|
||||||
@@ -815,7 +816,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const expandSecretReferences = expandSecretReferencesFactory({
|
const { expandSecretReferences } = expandSecretReferencesFactory({
|
||||||
projectId,
|
projectId,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
@@ -965,7 +966,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const expandSecretReferences = expandSecretReferencesFactory({
|
const { expandSecretReferences } = expandSecretReferencesFactory({
|
||||||
projectId,
|
projectId,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
@@ -1032,6 +1033,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
value: secretValue,
|
value: secretValue,
|
||||||
skipMultilineEncoding: secret.skipMultilineEncoding
|
skipMultilineEncoding: secret.skipMultilineEncoding
|
||||||
});
|
});
|
||||||
|
|
||||||
secretValue = expandedSecretValue || "";
|
secretValue = expandedSecretValue || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1928,6 +1930,88 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getSecretReferenceTree = async ({
|
||||||
|
environment,
|
||||||
|
secretPath,
|
||||||
|
projectId,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
secretName,
|
||||||
|
actorAuthMethod
|
||||||
|
}: TGetSecretReferencesTreeDTO) => {
|
||||||
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
projectId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||||
|
);
|
||||||
|
|
||||||
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
|
if (!folder)
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: "Folder not found for the given environment slug & secret path",
|
||||||
|
name: "Create secret"
|
||||||
|
});
|
||||||
|
const folderId = folder.id;
|
||||||
|
|
||||||
|
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.SecretManager,
|
||||||
|
projectId
|
||||||
|
});
|
||||||
|
|
||||||
|
const secret = await secretDAL.findOne({
|
||||||
|
folderId,
|
||||||
|
key: secretName,
|
||||||
|
type: SecretType.Shared
|
||||||
|
});
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.Secrets, {
|
||||||
|
environment,
|
||||||
|
secretPath,
|
||||||
|
secretName,
|
||||||
|
secretTags: (secret?.tags || []).map((el) => el.slug)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const secretValue = secret.encryptedValue
|
||||||
|
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString()
|
||||||
|
: "";
|
||||||
|
|
||||||
|
const { getExpandedSecretStackTrace } = expandSecretReferencesFactory({
|
||||||
|
projectId,
|
||||||
|
folderDAL,
|
||||||
|
secretDAL,
|
||||||
|
decryptSecretValue: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : undefined),
|
||||||
|
canExpandValue: (expandEnvironment, expandSecretPath, expandSecretName, expandSecretTags) =>
|
||||||
|
permission.can(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.Secrets, {
|
||||||
|
environment: expandEnvironment,
|
||||||
|
secretPath: expandSecretPath,
|
||||||
|
secretName: expandSecretName,
|
||||||
|
secretTags: expandSecretTags
|
||||||
|
})
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
const { expandedValue, stackTrace } = await getExpandedSecretStackTrace({
|
||||||
|
environment,
|
||||||
|
secretPath,
|
||||||
|
value: secretValue
|
||||||
|
});
|
||||||
|
|
||||||
|
return { tree: stackTrace, value: expandedValue };
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createSecret,
|
createSecret,
|
||||||
deleteSecret,
|
deleteSecret,
|
||||||
@@ -1942,6 +2026,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
moveSecrets,
|
moveSecrets,
|
||||||
getSecretsCount,
|
getSecretsCount,
|
||||||
getSecretsCountMultiEnv,
|
getSecretsCountMultiEnv,
|
||||||
getSecretsMultiEnv
|
getSecretsMultiEnv,
|
||||||
|
getSecretReferenceTree
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -278,3 +278,10 @@ export type TAttachSecretTagsDTO = {
|
|||||||
secretPath: string;
|
secretPath: string;
|
||||||
type: SecretType;
|
type: SecretType;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TGetSecretReferencesTreeDTO = {
|
||||||
|
projectId: string;
|
||||||
|
secretName: string;
|
||||||
|
environment: string;
|
||||||
|
secretPath: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ export const secretQueueFactory = ({
|
|||||||
);
|
);
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
const expandSecretReferences = expandSecretReferencesFactory({
|
const { expandSecretReferences } = expandSecretReferencesFactory({
|
||||||
decryptSecretValue: dto.decryptor,
|
decryptSecretValue: dto.decryptor,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
|||||||
import { fnSecretsFromImports } from "../secret-import/secret-import-fns";
|
import { fnSecretsFromImports } from "../secret-import/secret-import-fns";
|
||||||
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
||||||
import { TSecretV2BridgeServiceFactory } from "../secret-v2-bridge/secret-v2-bridge-service";
|
import { TSecretV2BridgeServiceFactory } from "../secret-v2-bridge/secret-v2-bridge-service";
|
||||||
|
import { TGetSecretReferencesTreeDTO } from "../secret-v2-bridge/secret-v2-bridge-types";
|
||||||
import { TSecretDALFactory } from "./secret-dal";
|
import { TSecretDALFactory } from "./secret-dal";
|
||||||
import {
|
import {
|
||||||
decryptSecretRaw,
|
decryptSecretRaw,
|
||||||
@@ -1099,6 +1100,18 @@ export const secretServiceFactory = ({
|
|||||||
return secrets;
|
return secrets;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getSecretReferenceTree = async (dto: TGetSecretReferencesTreeDTO) => {
|
||||||
|
const { shouldUseSecretV2Bridge } = await projectBotService.getBotKey(dto.projectId);
|
||||||
|
|
||||||
|
if (!shouldUseSecretV2Bridge)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Project version does not support secret reference tree",
|
||||||
|
name: "SecretReferenceTreeNotSupported"
|
||||||
|
});
|
||||||
|
|
||||||
|
return secretV2BridgeService.getSecretReferenceTree(dto);
|
||||||
|
};
|
||||||
|
|
||||||
const getSecretsRaw = async ({
|
const getSecretsRaw = async ({
|
||||||
projectId,
|
projectId,
|
||||||
path,
|
path,
|
||||||
@@ -2857,6 +2870,7 @@ export const secretServiceFactory = ({
|
|||||||
startSecretV2Migration,
|
startSecretV2Migration,
|
||||||
getSecretsCount,
|
getSecretsCount,
|
||||||
getSecretsCountMultiEnv,
|
getSecretsCountMultiEnv,
|
||||||
getSecretsRawMultiEnv
|
getSecretsRawMultiEnv,
|
||||||
|
getSecretReferenceTree
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Generated
+213
-14
@@ -26,6 +26,7 @@
|
|||||||
"@radix-ui/react-accordion": "^1.1.2",
|
"@radix-ui/react-accordion": "^1.1.2",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
"@radix-ui/react-checkbox": "^1.0.4",
|
||||||
|
"@radix-ui/react-collapsible": "^1.1.1",
|
||||||
"@radix-ui/react-dialog": "^1.0.5",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
"@radix-ui/react-hover-card": "^1.0.7",
|
"@radix-ui/react-hover-card": "^1.0.7",
|
||||||
@@ -4846,6 +4847,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-collapsible": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/primitive": "1.0.1",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.1",
|
||||||
|
"@radix-ui/react-context": "1.0.1",
|
||||||
|
"@radix-ui/react-id": "1.0.1",
|
||||||
|
"@radix-ui/react-presence": "1.0.1",
|
||||||
|
"@radix-ui/react-primitive": "1.0.3",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.0.1",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-alert-dialog": {
|
"node_modules/@radix-ui/react-alert-dialog": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.0.5.tgz",
|
||||||
@@ -4928,25 +4960,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-collapsible": {
|
"node_modules/@radix-ui/react-collapsible": {
|
||||||
"version": "1.0.3",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz",
|
||||||
"integrity": "sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==",
|
"integrity": "sha512-1///SnrfQHJEofLokyczERxQbWfCGQlQ2XsCZMucVs6it+lq9iw4vXy+uDn1edlb58cOZOWSldnfPAYcT4O/Yg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.13.10",
|
"@radix-ui/primitive": "1.1.0",
|
||||||
"@radix-ui/primitive": "1.0.1",
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
"@radix-ui/react-compose-refs": "1.0.1",
|
"@radix-ui/react-context": "1.1.1",
|
||||||
"@radix-ui/react-context": "1.0.1",
|
"@radix-ui/react-id": "1.1.0",
|
||||||
"@radix-ui/react-id": "1.0.1",
|
"@radix-ui/react-presence": "1.1.1",
|
||||||
"@radix-ui/react-presence": "1.0.1",
|
"@radix-ui/react-primitive": "2.0.0",
|
||||||
"@radix-ui/react-primitive": "1.0.3",
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||||
"@radix-ui/react-use-controllable-state": "1.0.1",
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
"@radix-ui/react-use-layout-effect": "1.0.1"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"@types/react-dom": "*",
|
"@types/react-dom": "*",
|
||||||
"react": "^16.8 || ^17.0 || ^18.0",
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
@@ -4957,6 +4989,173 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-id": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-presence": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-use-controllable-state": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-use-layout-effect": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-collection": {
|
"node_modules/@radix-ui/react-collection": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz",
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
"@radix-ui/react-accordion": "^1.1.2",
|
"@radix-ui/react-accordion": "^1.1.2",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
"@radix-ui/react-checkbox": "^1.0.4",
|
||||||
|
"@radix-ui/react-collapsible": "^1.1.1",
|
||||||
"@radix-ui/react-dialog": "^1.0.5",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
"@radix-ui/react-hover-card": "^1.0.7",
|
"@radix-ui/react-hover-card": "^1.0.7",
|
||||||
|
|||||||
@@ -8,4 +8,8 @@ export {
|
|||||||
useUpdateSecretBatch,
|
useUpdateSecretBatch,
|
||||||
useUpdateSecretV3
|
useUpdateSecretV3
|
||||||
} from "./mutations";
|
} from "./mutations";
|
||||||
export { useGetProjectSecrets, useGetProjectSecretsAllEnv, useGetSecretVersion } from "./queries";
|
export {
|
||||||
|
useGetProjectSecrets,
|
||||||
|
useGetProjectSecretsAllEnv,
|
||||||
|
useGetSecretReferenceTree,
|
||||||
|
useGetSecretVersion} from "./queries";
|
||||||
|
|||||||
@@ -17,14 +17,17 @@ import {
|
|||||||
SecretVersions,
|
SecretVersions,
|
||||||
TGetProjectSecretsAllEnvDTO,
|
TGetProjectSecretsAllEnvDTO,
|
||||||
TGetProjectSecretsDTO,
|
TGetProjectSecretsDTO,
|
||||||
TGetProjectSecretsKey
|
TGetProjectSecretsKey,
|
||||||
|
TGetSecretReferenceTreeDTO,
|
||||||
|
TSecretReferenceTraceNode
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
export const secretKeys = {
|
export const secretKeys = {
|
||||||
// this is also used in secretSnapshot part
|
// this is also used in secretSnapshot part
|
||||||
getProjectSecret: ({ workspaceId, environment, secretPath }: TGetProjectSecretsKey) =>
|
getProjectSecret: ({ workspaceId, environment, secretPath }: TGetProjectSecretsKey) =>
|
||||||
[{ workspaceId, environment, secretPath }, "secrets"] as const,
|
[{ workspaceId, environment, secretPath }, "secrets"] as const,
|
||||||
getSecretVersion: (secretId: string) => [{ secretId }, "secret-versions"] as const
|
getSecretVersion: (secretId: string) => [{ secretId }, "secret-versions"] as const,
|
||||||
|
getSecretReferenceTree: (dto: TGetSecretReferenceTreeDTO) => ["secret-reference-tree", dto]
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchProjectSecrets = async ({
|
export const fetchProjectSecrets = async ({
|
||||||
@@ -227,3 +230,33 @@ export const useGetSecretVersion = (dto: GetSecretVersionsDTO) =>
|
|||||||
return data.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
return data.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
||||||
}, [])
|
}, [])
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const fetchSecretReferenceTree = async ({
|
||||||
|
secretPath,
|
||||||
|
projectId,
|
||||||
|
secretKey,
|
||||||
|
environmentSlug
|
||||||
|
}: TGetSecretReferenceTreeDTO) => {
|
||||||
|
const { data } = await apiRequest.get<{ tree: TSecretReferenceTraceNode; value: string }>(
|
||||||
|
`/api/v3/secrets/raw/${secretKey}/secret-reference-tree`,
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
secretPath,
|
||||||
|
workspaceId: projectId,
|
||||||
|
environment: environmentSlug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetSecretReferenceTree = (dto: TGetSecretReferenceTreeDTO) =>
|
||||||
|
useQuery({
|
||||||
|
enabled:
|
||||||
|
Boolean(dto.environmentSlug) &&
|
||||||
|
Boolean(dto.secretPath) &&
|
||||||
|
Boolean(dto.projectId) &&
|
||||||
|
Boolean(dto.secretKey),
|
||||||
|
queryKey: secretKeys.getSecretReferenceTree(dto),
|
||||||
|
queryFn: () => fetchSecretReferenceTree(dto)
|
||||||
|
});
|
||||||
|
|||||||
@@ -210,3 +210,18 @@ export type TMoveSecretsDTO = {
|
|||||||
secretIds: string[];
|
secretIds: string[];
|
||||||
shouldOverwrite: boolean;
|
shouldOverwrite: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TGetSecretReferenceTreeDTO = {
|
||||||
|
secretKey: string;
|
||||||
|
secretPath: string;
|
||||||
|
environmentSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TSecretReferenceTraceNode = {
|
||||||
|
key: string;
|
||||||
|
value?: string;
|
||||||
|
environment: string;
|
||||||
|
secretPath: string;
|
||||||
|
children: TSecretReferenceTraceNode[];
|
||||||
|
};
|
||||||
|
|||||||
@@ -137,6 +137,27 @@ html {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tree-line::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -16px;
|
||||||
|
top: 1px;
|
||||||
|
bottom: 0;
|
||||||
|
width: 1px;
|
||||||
|
height: 50%;
|
||||||
|
background-color: #cbd5e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-line::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -16px;
|
||||||
|
top: 50%;
|
||||||
|
width: 12px;
|
||||||
|
height: 1px;
|
||||||
|
background-color: #cbd5e0;
|
||||||
|
}
|
||||||
|
|
||||||
.show-tags {
|
.show-tags {
|
||||||
transform: translateY(10px);
|
transform: translateY(10px);
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
|||||||
+3
-3
@@ -393,7 +393,7 @@ export const SecretDetailSidebar = ({
|
|||||||
) : (
|
) : (
|
||||||
<div className="mt-2 ml-1 flex items-center space-x-2">
|
<div className="mt-2 ml-1 flex items-center space-x-2">
|
||||||
<Button
|
<Button
|
||||||
className="px-2 py-1"
|
className="w-full px-2 py-1"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
leftIcon={<FontAwesomeIcon icon={faClock} />}
|
leftIcon={<FontAwesomeIcon icon={faClock} />}
|
||||||
onClick={() => setCreateReminderFormOpen.on()}
|
onClick={() => setCreateReminderFormOpen.on()}
|
||||||
@@ -448,9 +448,9 @@ export const SecretDetailSidebar = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-1 flex items-center space-x-2">
|
<div className="ml-1 flex items-center space-x-4">
|
||||||
<Button
|
<Button
|
||||||
className="px-2 py-1"
|
className="w-full px-2 py-1"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
leftIcon={<FontAwesomeIcon icon={faShare} />}
|
leftIcon={<FontAwesomeIcon icon={faShare} />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -12,6 +12,9 @@ import {
|
|||||||
FormControl,
|
FormControl,
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
|
ModalTrigger,
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
@@ -36,8 +39,8 @@ import { AnimatePresence, motion } from "framer-motion";
|
|||||||
import { memo, useEffect } from "react";
|
import { memo, useEffect } from "react";
|
||||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
import { hasSecretReference, SecretReferenceTree } from "../SecretReferenceDetails";
|
||||||
|
|
||||||
import { CreateReminderForm } from "./CreateReminderForm";
|
|
||||||
import {
|
import {
|
||||||
FontAwesomeSpriteName,
|
FontAwesomeSpriteName,
|
||||||
formSchema,
|
formSchema,
|
||||||
@@ -100,9 +103,6 @@ export const SecretItem = memo(
|
|||||||
|
|
||||||
const secretName = watch("key");
|
const secretName = watch("key");
|
||||||
|
|
||||||
const secretReminderRepeatDays = watch("reminderRepeatDays");
|
|
||||||
const secretReminderNote = watch("reminderNote");
|
|
||||||
|
|
||||||
const overrideAction = watch("overrideAction");
|
const overrideAction = watch("overrideAction");
|
||||||
const hasComment = Boolean(watch("comment"));
|
const hasComment = Boolean(watch("comment"));
|
||||||
|
|
||||||
@@ -139,7 +139,6 @@ export const SecretItem = memo(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [isSecValueCopied, setIsSecValueCopied] = useToggle(false);
|
const [isSecValueCopied, setIsSecValueCopied] = useToggle(false);
|
||||||
const [createReminderFormOpen, setCreateReminderFormOpen] = useToggle(false);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timer: NodeJS.Timeout;
|
let timer: NodeJS.Timeout;
|
||||||
if (isSecValueCopied) {
|
if (isSecValueCopied) {
|
||||||
@@ -202,206 +201,159 @@ export const SecretItem = memo(
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<form onSubmit={handleSubmit(handleFormSubmit)}>
|
||||||
<CreateReminderForm
|
<div
|
||||||
repeatDays={secretReminderRepeatDays}
|
className={twMerge(
|
||||||
note={secretReminderNote}
|
"border-b border-mineshaft-600 bg-mineshaft-800 shadow-none hover:bg-mineshaft-700",
|
||||||
isOpen={createReminderFormOpen}
|
isDirty && "border-primary-400/50"
|
||||||
onOpenChange={(_, data) => {
|
)}
|
||||||
setCreateReminderFormOpen.toggle();
|
>
|
||||||
|
<div className="group flex">
|
||||||
if (data) {
|
<div
|
||||||
setValue("reminderRepeatDays", data.days, { shouldDirty: true });
|
className={twMerge(
|
||||||
setValue("reminderNote", data.note, { shouldDirty: true });
|
"flex h-11 w-11 items-center justify-center px-4 py-3",
|
||||||
}
|
isDirty && "text-primary"
|
||||||
}}
|
)}
|
||||||
/>
|
>
|
||||||
<form onSubmit={handleSubmit(handleFormSubmit)}>
|
<Checkbox
|
||||||
<div
|
id={`checkbox-${secret.id}`}
|
||||||
className={twMerge(
|
isChecked={isSelected}
|
||||||
"border-b border-mineshaft-600 bg-mineshaft-800 shadow-none hover:bg-mineshaft-700",
|
onCheckedChange={() => onToggleSecretSelect(secret)}
|
||||||
isDirty && "border-primary-400/50"
|
className={twMerge("ml-3 hidden group-hover:flex", isSelected && "flex")}
|
||||||
)}
|
/>
|
||||||
>
|
<FontAwesomeSymbol
|
||||||
<div className="group flex">
|
|
||||||
<div
|
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"flex h-11 w-11 items-center justify-center px-4 py-3",
|
"ml-3 block h-3.5 w-3.5 group-hover:hidden",
|
||||||
isDirty && "text-primary"
|
isSelected && "hidden"
|
||||||
)}
|
)}
|
||||||
>
|
symbolName={FontAwesomeSpriteName.SecretKey}
|
||||||
<Checkbox
|
/>
|
||||||
id={`checkbox-${secret.id}`}
|
</div>
|
||||||
isChecked={isSelected}
|
<div className="flex h-11 w-80 flex-shrink-0 items-center px-4 py-2">
|
||||||
onCheckedChange={() => onToggleSecretSelect(secret)}
|
<Controller
|
||||||
className={twMerge("ml-3 hidden group-hover:flex", isSelected && "flex")}
|
name="key"
|
||||||
/>
|
control={control}
|
||||||
<FontAwesomeSymbol
|
render={({ field, fieldState: { error } }) => (
|
||||||
className={twMerge(
|
<Input
|
||||||
"ml-3 block h-3.5 w-3.5 group-hover:hidden",
|
autoComplete="off"
|
||||||
isSelected && "hidden"
|
isReadOnly={isReadOnly}
|
||||||
)}
|
autoCapitalization={currentWorkspace?.autoCapitalization}
|
||||||
symbolName={FontAwesomeSpriteName.SecretKey}
|
variant="plain"
|
||||||
/>
|
isDisabled={isOverriden}
|
||||||
</div>
|
placeholder={error?.message}
|
||||||
<div className="flex h-11 w-80 flex-shrink-0 items-center px-4 py-2">
|
isError={Boolean(error)}
|
||||||
|
onKeyUp={() => trigger("key")}
|
||||||
|
{...field}
|
||||||
|
className="w-full px-0 placeholder:text-red-500 focus:text-bunker-100 focus:ring-transparent"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="flex w-80 flex-grow items-center border-x border-mineshaft-600 py-1 pl-4 pr-2"
|
||||||
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
{isOverriden ? (
|
||||||
<Controller
|
<Controller
|
||||||
name="key"
|
name="valueOverride"
|
||||||
|
key="value-overriden"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field }) => (
|
||||||
<Input
|
<SecretInput
|
||||||
autoComplete="off"
|
key="value-overriden"
|
||||||
|
isVisible={isVisible}
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
autoCapitalization={currentWorkspace?.autoCapitalization}
|
|
||||||
variant="plain"
|
|
||||||
isDisabled={isOverriden}
|
|
||||||
placeholder={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
onKeyUp={() => trigger("key")}
|
|
||||||
{...field}
|
{...field}
|
||||||
className="w-full px-0 placeholder:text-red-500 focus:text-bunker-100 focus:ring-transparent"
|
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
) : (
|
||||||
<div
|
<Controller
|
||||||
className="flex w-80 flex-grow items-center border-x border-mineshaft-600 py-1 pl-4 pr-2"
|
name="value"
|
||||||
tabIndex={0}
|
key="secret-value"
|
||||||
role="button"
|
control={control}
|
||||||
>
|
render={({ field }) => (
|
||||||
{isOverriden ? (
|
<InfisicalSecretInput
|
||||||
<Controller
|
isReadOnly={isReadOnly}
|
||||||
name="valueOverride"
|
key="secret-value"
|
||||||
key="value-overriden"
|
isVisible={isVisible}
|
||||||
control={control}
|
environment={environment}
|
||||||
render={({ field }) => (
|
secretPath={secretPath}
|
||||||
<SecretInput
|
{...field}
|
||||||
key="value-overriden"
|
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2"
|
||||||
isVisible={isVisible}
|
/>
|
||||||
isReadOnly={isReadOnly}
|
)}
|
||||||
{...field}
|
/>
|
||||||
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2"
|
)}
|
||||||
/>
|
<div key="actions" className="flex h-8 flex-shrink-0 self-start transition-all">
|
||||||
)}
|
<Tooltip content="Copy secret">
|
||||||
/>
|
<IconButton
|
||||||
) : (
|
ariaLabel="copy-value"
|
||||||
<Controller
|
variant="plain"
|
||||||
name="value"
|
size="sm"
|
||||||
key="secret-value"
|
className="w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5"
|
||||||
control={control}
|
onClick={copyTokenToClipboard}
|
||||||
render={({ field }) => (
|
>
|
||||||
<InfisicalSecretInput
|
<FontAwesomeSymbol
|
||||||
isReadOnly={isReadOnly}
|
className="h-3.5 w-3"
|
||||||
key="secret-value"
|
symbolName={
|
||||||
isVisible={isVisible}
|
isSecValueCopied
|
||||||
environment={environment}
|
? FontAwesomeSpriteName.Check
|
||||||
secretPath={secretPath}
|
: FontAwesomeSpriteName.ClipboardCopy
|
||||||
{...field}
|
}
|
||||||
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2"
|
/>
|
||||||
/>
|
</IconButton>
|
||||||
)}
|
</Tooltip>
|
||||||
/>
|
<ProjectPermissionCan
|
||||||
)}
|
I={ProjectPermissionActions.Edit}
|
||||||
<div key="actions" className="flex h-8 flex-shrink-0 self-start transition-all">
|
a={subject(ProjectPermissionSub.Secrets, {
|
||||||
<Tooltip content="Copy secret">
|
environment,
|
||||||
<IconButton
|
secretPath,
|
||||||
ariaLabel="copy-value"
|
secretName,
|
||||||
variant="plain"
|
secretTags: selectedTagSlugs
|
||||||
size="sm"
|
})}
|
||||||
className="w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5"
|
>
|
||||||
onClick={copyTokenToClipboard}
|
{(isAllowed) => (
|
||||||
>
|
<Modal>
|
||||||
<FontAwesomeSymbol
|
<ModalTrigger asChild>
|
||||||
className="h-3.5 w-3"
|
<IconButton
|
||||||
symbolName={
|
className="w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5 data-[state=open]:w-6"
|
||||||
isSecValueCopied
|
variant="plain"
|
||||||
? FontAwesomeSpriteName.Check
|
size="md"
|
||||||
: FontAwesomeSpriteName.ClipboardCopy
|
ariaLabel="reference-tree"
|
||||||
}
|
isDisabled={!isAllowed || !hasSecretReference(secret?.value)}
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
<DropdownMenu>
|
|
||||||
<ProjectPermissionCan
|
|
||||||
I={ProjectPermissionActions.Edit}
|
|
||||||
a={subject(ProjectPermissionSub.Secrets, {
|
|
||||||
environment,
|
|
||||||
secretPath,
|
|
||||||
secretName,
|
|
||||||
secretTags: selectedTagSlugs
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
|
||||||
<DropdownMenuTrigger asChild disabled={!isAllowed}>
|
|
||||||
<IconButton
|
|
||||||
ariaLabel="tags"
|
|
||||||
variant="plain"
|
|
||||||
size="sm"
|
|
||||||
className={twMerge(
|
|
||||||
"w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5 data-[state=open]:w-5",
|
|
||||||
hasTagsApplied && "w-5 text-primary"
|
|
||||||
)}
|
|
||||||
isDisabled={!isAllowed}
|
|
||||||
>
|
|
||||||
<Tooltip content="Tags">
|
|
||||||
<FontAwesomeSymbol
|
|
||||||
className="h-3.5 w-3.5"
|
|
||||||
symbolName={FontAwesomeSpriteName.Tags}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</IconButton>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
<DropdownMenuContent align="end">
|
|
||||||
<DropdownMenuLabel>Add tags to this secret</DropdownMenuLabel>
|
|
||||||
{tags.map((tag) => {
|
|
||||||
const { id: tagId, slug, color } = tag;
|
|
||||||
|
|
||||||
const isTagSelected = selectedTagsGroupById?.[tagId];
|
|
||||||
return (
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => handleTagSelect(tag)}
|
|
||||||
key={`${secret.id}-${tagId}`}
|
|
||||||
icon={
|
|
||||||
isTagSelected && (
|
|
||||||
<FontAwesomeSymbol
|
|
||||||
symbolName={FontAwesomeSpriteName.CheckedCircle}
|
|
||||||
className="h-3 w-3"
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
iconPos="right"
|
|
||||||
>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div
|
|
||||||
className="mr-2 h-2 w-2 rounded-full"
|
|
||||||
style={{ background: color || "#bec2c8" }}
|
|
||||||
/>
|
|
||||||
{slug}
|
|
||||||
</div>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<DropdownMenuItem className="px-1.5" asChild>
|
|
||||||
<Button
|
|
||||||
size="xs"
|
|
||||||
className="w-full"
|
|
||||||
colorSchema="primary"
|
|
||||||
variant="outline_bg"
|
|
||||||
leftIcon={
|
|
||||||
<FontAwesomeSymbol
|
|
||||||
symbolName={FontAwesomeSpriteName.Tags}
|
|
||||||
className="h-3 w-3"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onClick={onCreateTag}
|
|
||||||
>
|
>
|
||||||
Create a tag
|
<Tooltip
|
||||||
</Button>
|
content={
|
||||||
</DropdownMenuItem>
|
hasSecretReference(secret?.value)
|
||||||
</DropdownMenuContent>
|
? "Secret Reference Tree"
|
||||||
</DropdownMenu>
|
: "Secret does not contain references"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FontAwesomeSymbol
|
||||||
|
className="h-3.5 w-3.5"
|
||||||
|
symbolName={FontAwesomeSpriteName.SecretReferenceTree}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</IconButton>
|
||||||
|
</ModalTrigger>
|
||||||
|
<ModalContent
|
||||||
|
title="Secret Reference Details"
|
||||||
|
subTitle="Visual breakdown of secrets referenced by this secret."
|
||||||
|
onOpenAutoFocus={(e) => e.preventDefault()} // prevents secret input from displaying value on open
|
||||||
|
>
|
||||||
|
<SecretReferenceTree
|
||||||
|
secretPath={secretPath}
|
||||||
|
environment={environment}
|
||||||
|
secretKey={secret?.key}
|
||||||
|
/>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
<DropdownMenu>
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
I={ProjectPermissionActions.Edit}
|
I={ProjectPermissionActions.Edit}
|
||||||
a={subject(ProjectPermissionSub.Secrets, {
|
a={subject(ProjectPermissionSub.Secrets, {
|
||||||
@@ -410,238 +362,287 @@ export const SecretItem = memo(
|
|||||||
secretName,
|
secretName,
|
||||||
secretTags: selectedTagSlugs
|
secretTags: selectedTagSlugs
|
||||||
})}
|
})}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<DropdownMenuTrigger asChild disabled={!isAllowed}>
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="tags"
|
||||||
|
variant="plain"
|
||||||
|
size="sm"
|
||||||
|
className={twMerge(
|
||||||
|
"w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5 data-[state=open]:w-5",
|
||||||
|
hasTagsApplied && "w-5 text-primary"
|
||||||
|
)}
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
>
|
||||||
|
<Tooltip content="Tags">
|
||||||
|
<FontAwesomeSymbol
|
||||||
|
className="h-3.5 w-3.5"
|
||||||
|
symbolName={FontAwesomeSpriteName.Tags}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</IconButton>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
<DropdownMenuLabel>Add tags to this secret</DropdownMenuLabel>
|
||||||
|
{tags.map((tag) => {
|
||||||
|
const { id: tagId, slug, color } = tag;
|
||||||
|
|
||||||
|
const isTagSelected = selectedTagsGroupById?.[tagId];
|
||||||
|
return (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => handleTagSelect(tag)}
|
||||||
|
key={`${secret.id}-${tagId}`}
|
||||||
|
icon={
|
||||||
|
isTagSelected && (
|
||||||
|
<FontAwesomeSymbol
|
||||||
|
symbolName={FontAwesomeSpriteName.CheckedCircle}
|
||||||
|
className="h-3 w-3"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
iconPos="right"
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div
|
||||||
|
className="mr-2 h-2 w-2 rounded-full"
|
||||||
|
style={{ background: color || "#bec2c8" }}
|
||||||
|
/>
|
||||||
|
{slug}
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<DropdownMenuItem className="px-1.5" asChild>
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
className="w-full"
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="outline_bg"
|
||||||
|
leftIcon={
|
||||||
|
<FontAwesomeSymbol
|
||||||
|
symbolName={FontAwesomeSpriteName.Tags}
|
||||||
|
className="h-3 w-3"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onClick={onCreateTag}
|
||||||
|
>
|
||||||
|
Create a tag
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Edit}
|
||||||
|
a={subject(ProjectPermissionSub.Secrets, {
|
||||||
|
environment,
|
||||||
|
secretPath,
|
||||||
|
secretName,
|
||||||
|
secretTags: selectedTagSlugs
|
||||||
|
})}
|
||||||
|
renderTooltip
|
||||||
|
allowedLabel="Override"
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="override-value"
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
variant="plain"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleOverrideClick}
|
||||||
|
className={twMerge(
|
||||||
|
"w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5",
|
||||||
|
isOverriden && "w-5 text-primary"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<FontAwesomeSymbol
|
||||||
|
symbolName={FontAwesomeSpriteName.Override}
|
||||||
|
className="h-3.5 w-3.5"
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
<Popover>
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Edit}
|
||||||
|
a={subject(ProjectPermissionSub.Secrets, {
|
||||||
|
environment,
|
||||||
|
secretPath,
|
||||||
|
secretName,
|
||||||
|
secretTags: selectedTagSlugs
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<PopoverTrigger asChild disabled={!isAllowed}>
|
||||||
|
<IconButton
|
||||||
|
className={twMerge(
|
||||||
|
"w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5 data-[state=open]:w-6",
|
||||||
|
hasComment && "w-5 text-primary"
|
||||||
|
)}
|
||||||
|
variant="plain"
|
||||||
|
size="md"
|
||||||
|
ariaLabel="add-comment"
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
>
|
||||||
|
<Tooltip content="Comment">
|
||||||
|
<FontAwesomeSymbol
|
||||||
|
className="h-3.5 w-3.5"
|
||||||
|
symbolName={FontAwesomeSpriteName.Comment}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</IconButton>
|
||||||
|
</PopoverTrigger>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
<IconButton
|
||||||
|
className="w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5 data-[state=open]:w-6"
|
||||||
|
variant="plain"
|
||||||
|
size="md"
|
||||||
|
ariaLabel="share-secret"
|
||||||
|
onClick={handleSecretShare}
|
||||||
|
>
|
||||||
|
<Tooltip content="Share Secret">
|
||||||
|
<FontAwesomeSymbol
|
||||||
|
className="h-3.5 w-3.5"
|
||||||
|
symbolName={FontAwesomeSpriteName.ShareSecret}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</IconButton>
|
||||||
|
<PopoverContent
|
||||||
|
className="w-auto border border-mineshaft-600 bg-mineshaft-800 p-2 drop-shadow-2xl"
|
||||||
|
sticky="always"
|
||||||
|
>
|
||||||
|
<FormControl label="Comment" className="mb-0">
|
||||||
|
<TextArea
|
||||||
|
className="border border-mineshaft-600 text-sm"
|
||||||
|
rows={8}
|
||||||
|
cols={30}
|
||||||
|
{...register("comment")}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<AnimatePresence exitBeforeEnter>
|
||||||
|
{!isDirty ? (
|
||||||
|
<motion.div
|
||||||
|
key="options"
|
||||||
|
className="flex h-10 flex-shrink-0 items-center space-x-4 px-[0.64rem]"
|
||||||
|
initial={{ x: 0, opacity: 0 }}
|
||||||
|
animate={{ x: 0, opacity: 1 }}
|
||||||
|
exit={{ x: 10, opacity: 0 }}
|
||||||
|
>
|
||||||
|
<Tooltip content="More">
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="more"
|
||||||
|
variant="plain"
|
||||||
|
size="md"
|
||||||
|
className="h-5 w-4 p-0 opacity-0 group-hover:opacity-100"
|
||||||
|
onClick={() => onDetailViewSecret(secret)}
|
||||||
|
>
|
||||||
|
<FontAwesomeSymbol
|
||||||
|
symbolName={FontAwesomeSpriteName.More}
|
||||||
|
className="h-5 w-4"
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Delete}
|
||||||
|
a={subject(ProjectPermissionSub.Secrets, {
|
||||||
|
environment,
|
||||||
|
secretPath,
|
||||||
|
secretName,
|
||||||
|
secretTags: selectedTagSlugs
|
||||||
|
})}
|
||||||
renderTooltip
|
renderTooltip
|
||||||
allowedLabel="Override"
|
allowedLabel="Delete"
|
||||||
>
|
>
|
||||||
{(isAllowed) => (
|
{(isAllowed) => (
|
||||||
<IconButton
|
<IconButton
|
||||||
ariaLabel="override-value"
|
ariaLabel="delete-value"
|
||||||
isDisabled={!isAllowed}
|
|
||||||
variant="plain"
|
variant="plain"
|
||||||
size="sm"
|
colorSchema="danger"
|
||||||
onClick={handleOverrideClick}
|
size="md"
|
||||||
className={twMerge(
|
className="p-0 opacity-0 group-hover:opacity-100"
|
||||||
"w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5",
|
onClick={() => onDeleteSecret(secret)}
|
||||||
isOverriden && "w-5 text-primary"
|
isDisabled={!isAllowed}
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<FontAwesomeSymbol
|
<FontAwesomeSymbol
|
||||||
symbolName={FontAwesomeSpriteName.Override}
|
symbolName={FontAwesomeSpriteName.Close}
|
||||||
className="h-3.5 w-3.5"
|
className="h-5 w-4"
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
|
</motion.div>
|
||||||
{!isOverriden && (
|
) : (
|
||||||
|
<motion.div
|
||||||
|
key="options-save"
|
||||||
|
className="flex h-10 flex-shrink-0 items-center space-x-4 px-3"
|
||||||
|
initial={{ x: -10, opacity: 0 }}
|
||||||
|
animate={{ x: 0, opacity: 1 }}
|
||||||
|
exit={{ x: -10, opacity: 0 }}
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
Object.keys(errors || {}).length
|
||||||
|
? Object.entries(errors)
|
||||||
|
.map(([key, { message }]) => `Field ${key}: ${message}`)
|
||||||
|
.join("\n")
|
||||||
|
: "Save"
|
||||||
|
}
|
||||||
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
ariaLabel="more"
|
||||||
|
variant="plain"
|
||||||
|
type="submit"
|
||||||
|
size="md"
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5 data-[state=open]:w-6",
|
"p-0 text-primary opacity-0 group-hover:opacity-100",
|
||||||
Boolean(secretReminderRepeatDays) && "w-5 text-primary"
|
isDirty && "opacity-100"
|
||||||
)}
|
)}
|
||||||
variant="plain"
|
isDisabled={isSubmitting || Boolean(errors.key)}
|
||||||
size="md"
|
|
||||||
ariaLabel="add-reminder"
|
|
||||||
onClick={() => setCreateReminderFormOpen.on()}
|
|
||||||
>
|
>
|
||||||
<Tooltip
|
{isSubmitting ? (
|
||||||
content={
|
<Spinner className="m-0 h-4 w-4 p-0" />
|
||||||
secretReminderRepeatDays && secretReminderRepeatDays > 0
|
) : (
|
||||||
? `Every ${secretReminderRepeatDays} day${
|
|
||||||
Number(secretReminderRepeatDays) > 1 ? "s" : ""
|
|
||||||
}
|
|
||||||
`
|
|
||||||
: "Reminder"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FontAwesomeSymbol
|
<FontAwesomeSymbol
|
||||||
className="h-3.5 w-3.5"
|
symbolName={FontAwesomeSpriteName.Check}
|
||||||
symbolName={FontAwesomeSpriteName.Clock}
|
className={twMerge(
|
||||||
|
"h-4 w-4 text-primary",
|
||||||
|
Boolean(Object.keys(errors || {}).length) && "text-red"
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Popover>
|
|
||||||
<ProjectPermissionCan
|
|
||||||
I={ProjectPermissionActions.Edit}
|
|
||||||
a={subject(ProjectPermissionSub.Secrets, {
|
|
||||||
environment,
|
|
||||||
secretPath,
|
|
||||||
secretName,
|
|
||||||
secretTags: selectedTagSlugs
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
|
||||||
<PopoverTrigger asChild disabled={!isAllowed}>
|
|
||||||
<IconButton
|
|
||||||
className={twMerge(
|
|
||||||
"w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5 data-[state=open]:w-6",
|
|
||||||
hasComment && "w-5 text-primary"
|
|
||||||
)}
|
|
||||||
variant="plain"
|
|
||||||
size="md"
|
|
||||||
ariaLabel="add-comment"
|
|
||||||
isDisabled={!isAllowed}
|
|
||||||
>
|
|
||||||
<Tooltip content="Comment">
|
|
||||||
<FontAwesomeSymbol
|
|
||||||
className="h-3.5 w-3.5"
|
|
||||||
symbolName={FontAwesomeSpriteName.Comment}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</IconButton>
|
|
||||||
</PopoverTrigger>
|
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip content="Cancel">
|
||||||
<IconButton
|
<IconButton
|
||||||
className="w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5 data-[state=open]:w-6"
|
ariaLabel="more"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
size="md"
|
size="md"
|
||||||
ariaLabel="share-secret"
|
className={twMerge(
|
||||||
onClick={handleSecretShare}
|
"p-0 opacity-0 group-hover:opacity-100",
|
||||||
>
|
isDirty && "opacity-100"
|
||||||
<Tooltip content="Share Secret">
|
|
||||||
<FontAwesomeSymbol
|
|
||||||
className="h-3.5 w-3.5"
|
|
||||||
symbolName={FontAwesomeSpriteName.ShareSecret}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</IconButton>
|
|
||||||
<PopoverContent
|
|
||||||
className="w-auto border border-mineshaft-600 bg-mineshaft-800 p-2 drop-shadow-2xl"
|
|
||||||
sticky="always"
|
|
||||||
>
|
|
||||||
<FormControl label="Comment" className="mb-0">
|
|
||||||
<TextArea
|
|
||||||
className="border border-mineshaft-600 text-sm"
|
|
||||||
rows={8}
|
|
||||||
cols={30}
|
|
||||||
{...register("comment")}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<AnimatePresence exitBeforeEnter>
|
|
||||||
{!isDirty ? (
|
|
||||||
<motion.div
|
|
||||||
key="options"
|
|
||||||
className="flex h-10 flex-shrink-0 items-center space-x-4 px-[0.64rem]"
|
|
||||||
initial={{ x: 0, opacity: 0 }}
|
|
||||||
animate={{ x: 0, opacity: 1 }}
|
|
||||||
exit={{ x: 10, opacity: 0 }}
|
|
||||||
>
|
|
||||||
<Tooltip content="More">
|
|
||||||
<IconButton
|
|
||||||
ariaLabel="more"
|
|
||||||
variant="plain"
|
|
||||||
size="md"
|
|
||||||
className="h-5 w-4 p-0 opacity-0 group-hover:opacity-100"
|
|
||||||
onClick={() => onDetailViewSecret(secret)}
|
|
||||||
>
|
|
||||||
<FontAwesomeSymbol
|
|
||||||
symbolName={FontAwesomeSpriteName.More}
|
|
||||||
className="h-5 w-4"
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
<ProjectPermissionCan
|
|
||||||
I={ProjectPermissionActions.Delete}
|
|
||||||
a={subject(ProjectPermissionSub.Secrets, {
|
|
||||||
environment,
|
|
||||||
secretPath,
|
|
||||||
secretName,
|
|
||||||
secretTags: selectedTagSlugs
|
|
||||||
})}
|
|
||||||
renderTooltip
|
|
||||||
allowedLabel="Delete"
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
|
||||||
<IconButton
|
|
||||||
ariaLabel="delete-value"
|
|
||||||
variant="plain"
|
|
||||||
colorSchema="danger"
|
|
||||||
size="md"
|
|
||||||
className="p-0 opacity-0 group-hover:opacity-100"
|
|
||||||
onClick={() => onDeleteSecret(secret)}
|
|
||||||
isDisabled={!isAllowed}
|
|
||||||
>
|
|
||||||
<FontAwesomeSymbol
|
|
||||||
symbolName={FontAwesomeSpriteName.Close}
|
|
||||||
className="h-5 w-4"
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
onClick={() => reset()}
|
||||||
</motion.div>
|
isDisabled={isSubmitting}
|
||||||
) : (
|
|
||||||
<motion.div
|
|
||||||
key="options-save"
|
|
||||||
className="flex h-10 flex-shrink-0 items-center space-x-4 px-3"
|
|
||||||
initial={{ x: -10, opacity: 0 }}
|
|
||||||
animate={{ x: 0, opacity: 1 }}
|
|
||||||
exit={{ x: -10, opacity: 0 }}
|
|
||||||
>
|
|
||||||
<Tooltip
|
|
||||||
content={
|
|
||||||
Object.keys(errors || {}).length
|
|
||||||
? Object.entries(errors)
|
|
||||||
.map(([key, { message }]) => `Field ${key}: ${message}`)
|
|
||||||
.join("\n")
|
|
||||||
: "Save"
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<IconButton
|
<FontAwesomeSymbol
|
||||||
ariaLabel="more"
|
symbolName={FontAwesomeSpriteName.Close}
|
||||||
variant="plain"
|
className="h-4 w-4 text-primary"
|
||||||
type="submit"
|
/>
|
||||||
size="md"
|
</IconButton>
|
||||||
className={twMerge(
|
</Tooltip>
|
||||||
"p-0 text-primary opacity-0 group-hover:opacity-100",
|
</motion.div>
|
||||||
isDirty && "opacity-100"
|
)}
|
||||||
)}
|
</AnimatePresence>
|
||||||
isDisabled={isSubmitting || Boolean(errors.key)}
|
|
||||||
>
|
|
||||||
{isSubmitting ? (
|
|
||||||
<Spinner className="m-0 h-4 w-4 p-0" />
|
|
||||||
) : (
|
|
||||||
<FontAwesomeSymbol
|
|
||||||
symbolName={FontAwesomeSpriteName.Check}
|
|
||||||
className={twMerge(
|
|
||||||
"h-4 w-4 text-primary",
|
|
||||||
Boolean(Object.keys(errors || {}).length) && "text-red"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip content="Cancel">
|
|
||||||
<IconButton
|
|
||||||
ariaLabel="more"
|
|
||||||
variant="plain"
|
|
||||||
size="md"
|
|
||||||
className={twMerge(
|
|
||||||
"p-0 opacity-0 group-hover:opacity-100",
|
|
||||||
isDirty && "opacity-100"
|
|
||||||
)}
|
|
||||||
onClick={() => reset()}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
<FontAwesomeSymbol
|
|
||||||
symbolName={FontAwesomeSpriteName.Close}
|
|
||||||
className="h-4 w-4 text-primary"
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</motion.div>
|
|
||||||
)}
|
|
||||||
</AnimatePresence>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { WsTag } from "@app/hooks/api/types";
|
|||||||
import { AddShareSecretModal } from "@app/views/ShareSecretPage/components/AddShareSecretModal";
|
import { AddShareSecretModal } from "@app/views/ShareSecretPage/components/AddShareSecretModal";
|
||||||
|
|
||||||
import { useSelectedSecretActions, useSelectedSecrets } from "../../SecretMainPage.store";
|
import { useSelectedSecretActions, useSelectedSecrets } from "../../SecretMainPage.store";
|
||||||
import { SecretDetailSidebar } from "./SecretDetaiSidebar";
|
import { SecretDetailSidebar } from "./SecretDetailSidebar";
|
||||||
import { SecretItem } from "./SecretItem";
|
import { SecretItem } from "./SecretItem";
|
||||||
import { FontAwesomeSpriteSymbols } from "./SecretListView.utils";
|
import { FontAwesomeSpriteSymbols } from "./SecretListView.utils";
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
faEllipsis,
|
faEllipsis,
|
||||||
faKey,
|
faKey,
|
||||||
faLock,
|
faLock,
|
||||||
|
faProjectDiagram,
|
||||||
faShare,
|
faShare,
|
||||||
faTags
|
faTags
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
@@ -73,7 +74,8 @@ export enum FontAwesomeSpriteName {
|
|||||||
CheckedCircle = "check-circle",
|
CheckedCircle = "check-circle",
|
||||||
ReplicatedSecretKey = "secret-replicated",
|
ReplicatedSecretKey = "secret-replicated",
|
||||||
ShareSecret = "share-secret",
|
ShareSecret = "share-secret",
|
||||||
KeyLock = "key-lock"
|
KeyLock = "key-lock",
|
||||||
|
SecretReferenceTree = "secret-reference-tree"
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is an optimization technique
|
// this is an optimization technique
|
||||||
@@ -91,5 +93,6 @@ export const FontAwesomeSpriteSymbols = [
|
|||||||
{ icon: faCheckCircle, symbol: FontAwesomeSpriteName.CheckedCircle },
|
{ icon: faCheckCircle, symbol: FontAwesomeSpriteName.CheckedCircle },
|
||||||
{ icon: faClone, symbol: FontAwesomeSpriteName.ReplicatedSecretKey },
|
{ icon: faClone, symbol: FontAwesomeSpriteName.ReplicatedSecretKey },
|
||||||
{ icon: faShare, symbol: FontAwesomeSpriteName.ShareSecret },
|
{ icon: faShare, symbol: FontAwesomeSpriteName.ShareSecret },
|
||||||
{ icon: faLock, symbol: FontAwesomeSpriteName.KeyLock }
|
{ icon: faLock, symbol: FontAwesomeSpriteName.KeyLock },
|
||||||
|
{ icon: faProjectDiagram, symbol: FontAwesomeSpriteName.SecretReferenceTree }
|
||||||
];
|
];
|
||||||
|
|||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
/* credits: https://iamkate.com/code/tree-views/ */
|
||||||
|
.tree {
|
||||||
|
--spacing: 1.5rem;
|
||||||
|
--radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree li {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
padding-left: calc(2 * var(--spacing) - var(--radius) - 2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree ul {
|
||||||
|
margin-left: calc(var(--radius) - var(--spacing));
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree ul li {
|
||||||
|
border-left: 2px solid #888;
|
||||||
|
min-height: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree ul li:last-child {
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree ul li::before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: calc(var(--spacing) / -1);
|
||||||
|
left: -2px;
|
||||||
|
width: calc(var(--spacing) + 2px);
|
||||||
|
height: calc(var(--spacing) + 13px);
|
||||||
|
border: solid #888;
|
||||||
|
border-radius: 0 0 0 8px;
|
||||||
|
border-width: 0 0 2px 2px;
|
||||||
|
transition: all 200ms linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details[open] summary ~ * {
|
||||||
|
animation: sweep .5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sweep {
|
||||||
|
0% {opacity: 0; margin-left: -10px}
|
||||||
|
100% {opacity: 1; margin-left: 0px}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree summary {
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
min-height: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree summary::marker,
|
||||||
|
.tree summary::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree summary:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree summary:focus-visible {
|
||||||
|
outline: 1px dotted #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree li::after,
|
||||||
|
.tree summary::before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: calc(var(--spacing) / 2 - var(--radius));
|
||||||
|
left: calc(var(--spacing) - var(--radius) - 1px);
|
||||||
|
width: calc(2 * var(--radius));
|
||||||
|
height: calc(2 * var(--radius));
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree summary::before {
|
||||||
|
z-index: 1;
|
||||||
|
background: #ddd 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree details[open] > summary::before {
|
||||||
|
background-position: calc(-2 * var(--radius)) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsibleContent {
|
||||||
|
/*overflow-y: hidden;*/
|
||||||
|
}
|
||||||
|
.collapsibleContent[data-state="open"] {
|
||||||
|
animation: slideDown 300ms ease-out;
|
||||||
|
}
|
||||||
|
.collapsibleContent[data-state="closed"] {
|
||||||
|
animation: slideUp 300ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
0% {
|
||||||
|
height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
height: var(--radix-collapsible-content-height);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
0% {
|
||||||
|
height: var(--radix-collapsible-content-height);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { faChevronRight, faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
import { FormControl, FormLabel, SecretInput, Spinner, Tooltip } from "@app/components/v2";
|
||||||
|
import { useWorkspace } from "@app/context";
|
||||||
|
import { useGetSecretReferenceTree } from "@app/hooks/api";
|
||||||
|
import { TSecretReferenceTraceNode } from "@app/hooks/api/types";
|
||||||
|
|
||||||
|
import style from "./SecretReferenceDetails.module.css";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
environment: string;
|
||||||
|
secretPath: string;
|
||||||
|
secretKey: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const INTERPOLATION_SYNTAX_REG = /\${([^}]+)}/;
|
||||||
|
export const hasSecretReference = (value: string | undefined) =>
|
||||||
|
value ? INTERPOLATION_SYNTAX_REG.test(value) : false;
|
||||||
|
|
||||||
|
export const SecretReferenceNode = ({
|
||||||
|
node,
|
||||||
|
isRoot,
|
||||||
|
secretKey
|
||||||
|
}: {
|
||||||
|
node: TSecretReferenceTraceNode;
|
||||||
|
isRoot?: boolean;
|
||||||
|
secretKey?: string;
|
||||||
|
}) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const hasChildren = node.children.length > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<Collapsible.Root open={isOpen} className="" onOpenChange={setIsOpen}>
|
||||||
|
<Collapsible.Trigger
|
||||||
|
className={twMerge(
|
||||||
|
hasChildren && " decoration-bunker-4ø00 underline-offset-4 data-[state=open]:underline",
|
||||||
|
"[&>svg]:data-[state=open]:rotate-[90deg] [&>svg]:data-[state=open]:text-yellow-500"
|
||||||
|
)}
|
||||||
|
disabled={!hasChildren}
|
||||||
|
>
|
||||||
|
{hasChildren && (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faChevronRight}
|
||||||
|
className=" d mr-2 text-mineshaft-400 transition-transform duration-300 ease-linear"
|
||||||
|
aria-hidden
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isRoot
|
||||||
|
? secretKey
|
||||||
|
: `${node.environment}${
|
||||||
|
node.secretPath === "/" ? "" : node.secretPath.split("/").join(".")
|
||||||
|
}.${node.key}`}
|
||||||
|
<Tooltip className="max-w-md break-words" content={node.value}>
|
||||||
|
<span
|
||||||
|
className={twMerge(
|
||||||
|
"ml-1 px-1 text-xs text-mineshaft-400",
|
||||||
|
!node.value && "text-red-400"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={node.value ? faEye : faEyeSlash} />
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
</Collapsible.Trigger>
|
||||||
|
<Collapsible.Content className={twMerge("mt-4", style.collapsibleContent)}>
|
||||||
|
{hasChildren && (
|
||||||
|
<ul>
|
||||||
|
{node.children.map((el, index) => (
|
||||||
|
<SecretReferenceNode node={el} key={`${el.key}-${index + 1}`} />
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</Collapsible.Content>
|
||||||
|
</Collapsible.Root>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SecretReferenceTree = ({ secretPath, environment, secretKey }: Props) => {
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const projectId = currentWorkspace?.id || "";
|
||||||
|
|
||||||
|
const { data, isLoading } = useGetSecretReferenceTree({
|
||||||
|
secretPath,
|
||||||
|
environmentSlug: environment,
|
||||||
|
projectId,
|
||||||
|
secretKey
|
||||||
|
});
|
||||||
|
|
||||||
|
const tree = data?.tree;
|
||||||
|
const secretValue = data?.value;
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center py-4">
|
||||||
|
<Spinner size="xs" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormControl label="Expanded value">
|
||||||
|
<SecretInput
|
||||||
|
key="value-overriden"
|
||||||
|
isReadOnly
|
||||||
|
value={secretValue}
|
||||||
|
containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-bunker-700 px-2 py-1.5"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="mb-2" label="Reference Tree" />
|
||||||
|
<div className="thin-scrollbar relative max-h-96 overflow-auto rounded-md border border-mineshaft-600 bg-bunker-700 py-6 text-sm text-mineshaft-200">
|
||||||
|
{tree && (
|
||||||
|
<ul className={style.tree}>
|
||||||
|
<SecretReferenceNode node={tree} isRoot secretKey={secretKey} />
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-sm text-mineshaft-400">
|
||||||
|
Click a secret key to view its sub-references.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { hasSecretReference,SecretReferenceTree } from "./SecretReferenceDetails";
|
||||||
+62
-3
@@ -1,17 +1,34 @@
|
|||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { subject } from "@casl/ability";
|
import { subject } from "@casl/ability";
|
||||||
import { faCheck, faCopy, faTrash, faXmark } from "@fortawesome/free-solid-svg-icons";
|
import {
|
||||||
|
faCheck,
|
||||||
|
faCopy,
|
||||||
|
faProjectDiagram,
|
||||||
|
faTrash,
|
||||||
|
faXmark
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import { DeleteActionModal, IconButton, Tooltip } from "@app/components/v2";
|
import {
|
||||||
|
DeleteActionModal,
|
||||||
|
IconButton,
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
|
ModalTrigger,
|
||||||
|
Tooltip
|
||||||
|
} from "@app/components/v2";
|
||||||
import { InfisicalSecretInput } from "@app/components/v2/InfisicalSecretInput";
|
import { InfisicalSecretInput } from "@app/components/v2/InfisicalSecretInput";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||||
import { useToggle } from "@app/hooks";
|
import { useToggle } from "@app/hooks";
|
||||||
import { SecretType } from "@app/hooks/api/types";
|
import { SecretType } from "@app/hooks/api/types";
|
||||||
|
import {
|
||||||
|
hasSecretReference,
|
||||||
|
SecretReferenceTree
|
||||||
|
} from "@app/views/SecretMainPage/components/SecretReferenceDetails";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
defaultValue?: string | null;
|
defaultValue?: string | null;
|
||||||
@@ -143,7 +160,7 @@ export const SecretEditRow = ({
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"flex w-16 justify-center space-x-3 pl-2 transition-all",
|
"flex w-24 justify-center space-x-3 pl-2 transition-all",
|
||||||
isImportedSecret && "pointer-events-none opacity-0"
|
isImportedSecret && "pointer-events-none opacity-0"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -203,6 +220,48 @@ export const SecretEditRow = ({
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Read}
|
||||||
|
a={ProjectPermissionSub.Secrets}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<div className="opacity-0 group-hover:opacity-100">
|
||||||
|
<Modal>
|
||||||
|
<ModalTrigger asChild>
|
||||||
|
<div className="opacity-0 group-hover:opacity-100">
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
hasSecretReference(defaultValue || "")
|
||||||
|
? "Secret Reference Tree"
|
||||||
|
: "Secret does not contain references"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="reference-tree"
|
||||||
|
className="h-full"
|
||||||
|
isDisabled={!hasSecretReference(defaultValue || "") || !isAllowed}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faProjectDiagram} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</ModalTrigger>
|
||||||
|
<ModalContent
|
||||||
|
title="Secret Reference Details"
|
||||||
|
subTitle="Visual breakdown of secrets referenced by this secret."
|
||||||
|
onOpenAutoFocus={(e) => e.preventDefault()} // prevents secret input from displaying value on open
|
||||||
|
>
|
||||||
|
<SecretReferenceTree
|
||||||
|
secretPath={secretPath}
|
||||||
|
environment={environment}
|
||||||
|
secretKey={secretName}
|
||||||
|
/>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
I={ProjectPermissionActions.Delete}
|
I={ProjectPermissionActions.Delete}
|
||||||
a={subject(ProjectPermissionSub.Secrets, {
|
a={subject(ProjectPermissionSub.Secrets, {
|
||||||
|
|||||||
Reference in New Issue
Block a user