From 1bc0e3087aa69224b931b60b3e999f38a877b513 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 29 Aug 2024 13:19:43 +0530 Subject: [PATCH] feat: completed atlas dynamic secret logic for ui --- frontend/package-lock.json | 187 ++++--- frontend/package.json | 1 + .../components/v2/FormControl/FormControl.tsx | 26 +- frontend/src/hooks/api/dynamicSecret/types.ts | 20 +- .../CreateDynamicSecretForm.tsx | 37 +- .../MongoAtlasInputForm.tsx | 455 +++++++++++++++++ .../CreateDynamicSecretLease.tsx | 3 +- .../EditDynamicSecretForm.tsx | 18 + .../EditDynamicSecretMongoAtlasForm.tsx | 465 ++++++++++++++++++ 9 files changed, 1095 insertions(+), 117 deletions(-) create mode 100644 frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/MongoAtlasInputForm.tsx create mode 100644 frontend/src/views/SecretMainPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretMongoAtlasForm.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 74f00b74d..3148f5da2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -4,6 +4,7 @@ "requires": true, "packages": { "": { + "name": "frontend", "dependencies": { "@casl/ability": "^6.5.0", "@casl/react": "^3.1.0", @@ -84,6 +85,7 @@ "react-grid-layout": "^1.3.4", "react-hook-form": "^7.43.0", "react-i18next": "^12.2.2", + "react-icons": "^5.3.0", "react-mailchimp-subscribe": "^2.1.3", "react-markdown": "^8.0.3", "react-redux": "^8.0.2", @@ -8609,26 +8611,6 @@ "integrity": "sha512-AjwI4MvWx3HAOaZqYsjKWyEObT9lcVV0Y0V8nXo6cXzN8ZiMxVhf6F3d/UNvXVGKrEzL/Dluc5p+y9GkzlTWig==", "dev": true }, - "node_modules/@types/eslint": { - "version": "8.56.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", - "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "0.0.51", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", @@ -9298,9 +9280,9 @@ "dev": true }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dev": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", @@ -9320,9 +9302,9 @@ "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { @@ -9343,15 +9325,15 @@ "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" + "@webassemblyjs/wasm-gen": "1.12.1" } }, "node_modules/@webassemblyjs/ieee754": { @@ -9379,28 +9361,28 @@ "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/leb128": "1.11.6", @@ -9408,24 +9390,24 @@ } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", @@ -9434,12 +9416,12 @@ } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, @@ -12933,9 +12915,9 @@ "integrity": "sha512-JGmudTwg7yxMYvR/gWbalqqQiyu7WTFv2Xu3vw4cJHXPFxNgAk0oy8UHaer8nLF4lZJa+rNoj6GsrKIVJTV6Tw==" }, "node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", "dev": true, "dependencies": { "bn.js": "^4.11.9", @@ -12998,9 +12980,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -15867,11 +15849,11 @@ } }, "node_modules/infisical-node/node_modules/axios": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", - "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -18133,12 +18115,12 @@ ] }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -20968,6 +20950,14 @@ } } }, + "node_modules/react-icons": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", + "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -24479,9 +24469,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -24506,34 +24496,33 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { - "version": "5.89.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", - "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { @@ -24666,9 +24655,9 @@ "dev": true }, "node_modules/webpack/node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -24677,10 +24666,10 @@ "node": ">=0.4.0" } }, - "node_modules/webpack/node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "node_modules/webpack/node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "dev": true, "peerDependencies": { "acorn": "^8" diff --git a/frontend/package.json b/frontend/package.json index 994485252..a538e7cf1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -92,6 +92,7 @@ "react-grid-layout": "^1.3.4", "react-hook-form": "^7.43.0", "react-i18next": "^12.2.2", + "react-icons": "^5.3.0", "react-mailchimp-subscribe": "^2.1.3", "react-markdown": "^8.0.3", "react-redux": "^8.0.2", diff --git a/frontend/src/components/v2/FormControl/FormControl.tsx b/frontend/src/components/v2/FormControl/FormControl.tsx index 9711e3e68..8594cfd73 100644 --- a/frontend/src/components/v2/FormControl/FormControl.tsx +++ b/frontend/src/components/v2/FormControl/FormControl.tsx @@ -9,36 +9,42 @@ import { Tooltip } from "../Tooltip"; export type FormLabelProps = { id?: string; isRequired?: boolean; - isOptional?:boolean; + isOptional?: boolean; label?: ReactNode; icon?: ReactNode; className?: string; tooltipText?: string; + tooltipClassName?: string; }; -export const FormLabel = ({ id, label, isRequired, icon, className,isOptional, tooltipText }: FormLabelProps) => ( +export const FormLabel = ({ + id, + label, + isRequired, + icon, + className, + isOptional, + tooltipText, + tooltipClassName +}: FormLabelProps) => ( {label} {isRequired && *} - {isOptional && - Optional} + {isOptional && - Optional} {icon && !tooltipText && ( {icon} )} {tooltipText && ( - - + + )} diff --git a/frontend/src/hooks/api/dynamicSecret/types.ts b/frontend/src/hooks/api/dynamicSecret/types.ts index ff881e506..7076ea929 100644 --- a/frontend/src/hooks/api/dynamicSecret/types.ts +++ b/frontend/src/hooks/api/dynamicSecret/types.ts @@ -20,7 +20,8 @@ export enum DynamicSecretProviders { Cassandra = "cassandra", AwsIam = "aws-iam", Redis = "redis", - AwsElastiCache = "aws-elasticache" + AwsElastiCache = "aws-elasticache", + MongoAtlas = "mongo-db-atlas" } export enum SqlProviders { @@ -96,6 +97,23 @@ export type TDynamicSecretProvider = creationStatement: string; revocationStatement: string; ca?: string | undefined; + } + } +|{ + type: DynamicSecretProviders.MongoAtlas; + inputs: { + adminPublicKey: string; + adminPrivateKey: string; + groupId: string; + roles: { + databaseName: string; + roleName: string; + collectionName?: string; + }[]; + scopes?: { + name: string; + type: string; + }[]; }; }; diff --git a/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/CreateDynamicSecretForm.tsx b/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/CreateDynamicSecretForm.tsx index b28a07116..21a50019b 100644 --- a/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/CreateDynamicSecretForm.tsx +++ b/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/CreateDynamicSecretForm.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { SiApachecassandra,SiMongodb } from "react-icons/si"; import { faAws } from "@fortawesome/free-brands-svg-icons"; import { faDatabase } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -11,6 +12,7 @@ import { AwsElastiCacheInputForm } from "./AwsElastiCacheInputForm"; import { AwsIamInputForm } from "./AwsIamInputForm"; import { CassandraInputForm } from "./CassandraInputForm"; import { RedisInputForm } from "./RedisInputForm"; +import { MongoAtlasInputForm } from "./MongoAtlasInputForm"; import { SqlDatabaseInputForm } from "./SqlDatabaseInputForm"; type Props = { @@ -28,29 +30,34 @@ enum WizardSteps { const DYNAMIC_SECRET_LIST = [ { - icon: faDatabase, + icon: , provider: DynamicSecretProviders.SqlDatabase, title: "SQL\nDatabase" }, { - icon: faDatabase, + icon: , provider: DynamicSecretProviders.Cassandra, title: "Cassandra" }, { - icon: faDatabase, + icon: , provider: DynamicSecretProviders.Redis, title: "Redis" }, { - icon: faAws, + icon: , provider: DynamicSecretProviders.AwsElastiCache, title: "AWS ElastiCache" }, { - icon: faAws, + icon: , provider: DynamicSecretProviders.AwsIam, title: "AWS IAM" + }, + { + icon: , + provider: DynamicSecretProviders.MongoAtlas, + title: "Mongo Atlas" } ]; @@ -105,7 +112,7 @@ export const CreateDynamicSecretForm = ({ } }} > - + {icon}
{title}
))} @@ -202,6 +209,24 @@ export const CreateDynamicSecretForm = ({ /> )} + {wizardStep === WizardSteps.ProviderInputs && + selectedProvider === DynamicSecretProviders.MongoAtlas && ( + + + + )} diff --git a/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/MongoAtlasInputForm.tsx b/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/MongoAtlasInputForm.tsx new file mode 100644 index 000000000..84024e387 --- /dev/null +++ b/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/MongoAtlasInputForm.tsx @@ -0,0 +1,455 @@ +import { Controller, useFieldArray, useForm } from "react-hook-form"; +import { faPlus, faTrash } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { zodResolver } from "@hookform/resolvers/zod"; +import ms from "ms"; +import { z } from "zod"; + +import { TtlFormLabel } from "@app/components/features"; +import { createNotification } from "@app/components/notifications"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, + Button, + FormControl, + FormLabel, + IconButton, + Input, + Select, + SelectItem +} from "@app/components/v2"; +import { useCreateDynamicSecret } from "@app/hooks/api"; +import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types"; + +const formSchema = z.object({ + provider: z.object({ + adminPublicKey: z.string().trim().min(1), + adminPrivateKey: z.string().trim().min(1), + groupId: z.string().trim().min(1), + roles: z + .object({ + collectionName: z.string().optional(), + databaseName: z.string().min(1), + roleName: z.string().min(1) + }) + .array() + .min(1), + scopes: z + .object({ + name: z.string().min(1), + type: z.string().min(1) + }) + .array() + }), + defaultTTL: z.string().superRefine((val, ctx) => { + const valMs = ms(val); + if (valMs < 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" }); + // a day + if (valMs > 24 * 60 * 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); + }), + maxTTL: z + .string() + .optional() + .superRefine((val, ctx) => { + if (!val) return; + const valMs = ms(val); + if (valMs < 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" }); + // a day + if (valMs > 24 * 60 * 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); + }), + name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase") +}); +type TForm = z.infer; + +type Props = { + onCompleted: () => void; + onCancel: () => void; + secretPath: string; + projectSlug: string; + environment: string; +}; + +const ATLAS_SCOPE_TYPES = [ + { + label: "Cluster", + value: "CLUSTER" + }, + { + label: "Data Lake", + value: "DATA_LAKE" + }, + { + label: "Stream", + value: "STREAM" + } +]; + +export const MongoAtlasInputForm = ({ + onCompleted, + onCancel, + environment, + secretPath, + projectSlug +}: Props) => { + const { + control, + getValues, + setValue, + formState: { isSubmitting }, + handleSubmit + } = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + provider: { + roles: [{ databaseName: "", roleName: "" }] + } + } + }); + + const roleFields = useFieldArray({ + control, + name: "provider.roles" + }); + + const scopeFields = useFieldArray({ + control, + name: "provider.scopes" + }); + + const createDynamicSecret = useCreateDynamicSecret(); + + const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => { + // wait till previous request is finished + if (createDynamicSecret.isLoading) return; + try { + await createDynamicSecret.mutateAsync({ + provider: { type: DynamicSecretProviders.MongoAtlas, inputs: provider }, + maxTTL, + name, + path: secretPath, + defaultTTL, + projectSlug, + environmentSlug: environment + }); + onCompleted(); + } catch (err) { + createNotification({ + type: "error", + text: "Failed to create dynamic secret" + }); + } + }; + + return ( +
+
+
+
+
+ ( + + + + )} + /> +
+
+ ( + } + isError={Boolean(error?.message)} + errorText={error?.message} + > + + + )} + /> +
+
+ ( + } + isError={Boolean(error?.message)} + errorText={error?.message} + > + + + )} + /> +
+
+
+
+ Configuration +
+
+
+ ( + + + + )} + /> + ( + + + + )} + /> +
+ ( + + + + )} + /> + +
+ {roleFields.fields.map(({ id: roleFieldId }, i) => ( +
+
+ {i === 0 && Database Name} + ( + + + + )} + /> +
+
+ {i === 0 && ( + + )} + ( + + + + )} + /> +
+
+ {i === 0 && ( + + )} + ( + + + + )} + /> +
+ { + const roles = getValues("provider.roles"); + if (roles && roles?.length > 1) { + roleFields.remove(i); + } else { + setValue("provider.roles", [{ databaseName: "", roleName: "" }]); + } + }} + > + + +
+ ))} +
+ +
+
+ + + Advance + + +
+ {scopeFields.fields.map(({ id: scopeFieldId }, i) => ( +
+
+ {i === 0 && ( + + )} + ( + + + + )} + /> +
+
+ {i === 0 && Type} + ( + + + + )} + /> +
+ { + scopeFields.remove(i); + }} + > + + +
+ ))} +
+ +
+
+
+
+
+
+
+
+
+ + +
+
+
+ ); +}; diff --git a/frontend/src/views/SecretMainPage/components/DynamicSecretListView/CreateDynamicSecretLease.tsx b/frontend/src/views/SecretMainPage/components/DynamicSecretListView/CreateDynamicSecretLease.tsx index 3cb709d59..800cf7747 100644 --- a/frontend/src/views/SecretMainPage/components/DynamicSecretListView/CreateDynamicSecretLease.tsx +++ b/frontend/src/views/SecretMainPage/components/DynamicSecretListView/CreateDynamicSecretLease.tsx @@ -57,7 +57,8 @@ const OutputDisplay = ({ const renderOutputForm = (provider: DynamicSecretProviders, data: unknown) => { if ( provider === DynamicSecretProviders.SqlDatabase || - provider === DynamicSecretProviders.Cassandra + provider === DynamicSecretProviders.Cassandra || + provider === DynamicSecretProviders.MongoAtlas ) { const { DB_PASSWORD, DB_USERNAME } = data as { DB_USERNAME: string; DB_PASSWORD: string }; return ( diff --git a/frontend/src/views/SecretMainPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretForm.tsx b/frontend/src/views/SecretMainPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretForm.tsx index 3309126ee..83500e2a9 100644 --- a/frontend/src/views/SecretMainPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretForm.tsx +++ b/frontend/src/views/SecretMainPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretForm.tsx @@ -8,6 +8,7 @@ import { EditDynamicSecretAwsElastiCacheProviderForm } from "./EditDynamicSecret import { EditDynamicSecretAwsIamForm } from "./EditDynamicSecretAwsIamForm"; import { EditDynamicSecretCassandraForm } from "./EditDynamicSecretCassandraForm"; import { EditDynamicSecretRedisProviderForm } from "./EditDynamicSecretRedisProviderForm"; +import { EditDynamicSecretMongoAtlasForm } from "./EditDynamicSecretMongoAtlasForm"; import { EditDynamicSecretSqlProviderForm } from "./EditDynamicSecretSqlProviderForm"; type Props = { @@ -128,6 +129,23 @@ export const EditDynamicSecretForm = ({ /> )} + {dynamicSecretDetails?.type === DynamicSecretProviders.MongoAtlas && ( + + + + )} ); }; diff --git a/frontend/src/views/SecretMainPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretMongoAtlasForm.tsx b/frontend/src/views/SecretMainPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretMongoAtlasForm.tsx new file mode 100644 index 000000000..8f6e3ba46 --- /dev/null +++ b/frontend/src/views/SecretMainPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretMongoAtlasForm.tsx @@ -0,0 +1,465 @@ +import { Controller, useFieldArray, useForm } from "react-hook-form"; +import { faPlus, faTrash } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { zodResolver } from "@hookform/resolvers/zod"; +import ms from "ms"; +import { z } from "zod"; + +import { TtlFormLabel } from "@app/components/features"; +import { createNotification } from "@app/components/notifications"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, + Button, + FormControl, + FormLabel, + IconButton, + Input, + Select, + SelectItem} from "@app/components/v2"; +import { useUpdateDynamicSecret } from "@app/hooks/api"; +import { TDynamicSecret } from "@app/hooks/api/dynamicSecret/types"; + +const formSchema = z.object({ + inputs: z + .object({ + adminPublicKey: z.string().trim().min(1), + adminPrivateKey: z.string().trim().min(1), + groupId: z.string().trim().min(1), + roles: z + .object({ + collectionName: z.string().optional(), + databaseName: z.string().min(1), + roleName: z.string().min(1) + }) + .array() + .min(1), + scopes: z + .object({ + name: z.string().min(1), + type: z.string().min(1) + }) + .array() + }) + .partial(), + defaultTTL: z.string().superRefine((val, ctx) => { + const valMs = ms(val); + if (valMs < 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" }); + // a day + if (valMs > 24 * 60 * 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); + }), + maxTTL: z + .string() + .optional() + .superRefine((val, ctx) => { + if (!val) return; + const valMs = ms(val); + if (valMs < 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" }); + // a day + if (valMs > 24 * 60 * 60 * 1000) + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); + }) + .nullable(), + newName: z + .string() + .refine((val) => val.toLowerCase() === val, "Must be lowercase") + .optional() +}); +type TForm = z.infer; + +type Props = { + onClose: () => void; + dynamicSecret: TDynamicSecret & { inputs: unknown }; + secretPath: string; + environment: string; + projectSlug: string; +}; + +const ATLAS_SCOPE_TYPES = [ + { + label: "Cluster", + value: "CLUSTER" + }, + { + label: "Data Lake", + value: "DATA_LAKE" + }, + { + label: "Stream", + value: "STREAM" + } +]; + +export const EditDynamicSecretMongoAtlasForm = ({ + onClose, + dynamicSecret, + environment, + secretPath, + projectSlug +}: Props) => { + const { + control, + getValues, + setValue, + formState: { isSubmitting }, + handleSubmit + } = useForm({ + resolver: zodResolver(formSchema), + values: { + defaultTTL: dynamicSecret.defaultTTL, + maxTTL: dynamicSecret.maxTTL, + newName: dynamicSecret.name, + inputs: { + ...(dynamicSecret.inputs as TForm["inputs"]) + } + } + }); + + const roleFields = useFieldArray({ + control, + name: "inputs.roles" + }); + + const scopeFields = useFieldArray({ + control, + name: "inputs.scopes" + }); + + const updateDynamicSecret = useUpdateDynamicSecret(); + + const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => { + // wait till previous request is finished + if (updateDynamicSecret.isLoading) return; + try { + await updateDynamicSecret.mutateAsync({ + name: dynamicSecret.name, + path: secretPath, + projectSlug, + environmentSlug: environment, + data: { + maxTTL: maxTTL || undefined, + defaultTTL, + inputs, + newName: newName === dynamicSecret.name ? undefined : newName + } + }); + onClose(); + createNotification({ + type: "success", + text: "Successfully updated dynamic secret" + }); + } catch (err) { + createNotification({ + type: "error", + text: "Failed to update dynamic secret" + }); + } + }; + + return ( +
+
+
+
+ ( + + + + )} + /> +
+
+ ( + } + isError={Boolean(error?.message)} + errorText={error?.message} + > + + + )} + /> +
+
+ ( + } + isError={Boolean(error?.message)} + errorText={error?.message} + > + + + )} + /> +
+
+
+
+ Configuration +
+
+
+ ( + + + + )} + /> + ( + + + + )} + /> +
+ ( + + + + )} + /> + +
+ {roleFields.fields.map(({ id: roleFieldId }, i) => ( +
+
+ {i === 0 && Database Name} + ( + + + + )} + /> +
+
+ {i === 0 && ( + + )} + ( + + + + )} + /> +
+
+ {i === 0 && ( + + )} + ( + + + + )} + /> +
+ { + const roles = getValues("inputs.roles"); + if (roles && roles?.length > 1) { + roleFields.remove(i); + } else { + setValue("inputs.roles", [{ databaseName: "", roleName: "" }]); + } + }} + > + + +
+ ))} +
+ +
+
+ + + Advance + + +
+ {scopeFields.fields.map(({ id: scopeFieldId }, i) => ( +
+
+ {i === 0 && ( + + )} + ( + + + + )} + /> +
+
+ {i === 0 && Type} + ( + + + + )} + /> +
+ { + scopeFields.remove(i); + }} + > + + +
+ ))} +
+ +
+
+
+
+
+
+
+
+ + +
+
+
+ ); +};