mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 12:28:56 +00:00
Merge branch 'main' of github.com:LemmyMwaura/infisical into secrets-dash-jumping-bug-#51
This commit is contained in:
@@ -40,6 +40,7 @@ SITE_URL=http://localhost:8080
|
|||||||
# Required to send emails
|
# Required to send emails
|
||||||
# By default, SMTP_HOST is set to smtp.gmail.com
|
# By default, SMTP_HOST is set to smtp.gmail.com
|
||||||
SMTP_HOST=smtp.gmail.com
|
SMTP_HOST=smtp.gmail.com
|
||||||
|
SMTP_PORT=587
|
||||||
SMTP_NAME=Team
|
SMTP_NAME=Team
|
||||||
SMTP_USERNAME=[email protected]
|
SMTP_USERNAME=[email protected]
|
||||||
SMTP_PASSWORD=
|
SMTP_PASSWORD=
|
||||||
|
|||||||
Executable
+5
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npx lint-staged
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 80,
|
||||||
|
"useTabs": false
|
||||||
|
}
|
||||||
@@ -311,4 +311,4 @@ Looking to report a security vulnerability? Please don't post about it in GitHub
|
|||||||
<!-- prettier-ignore-start -->
|
<!-- prettier-ignore-start -->
|
||||||
<!-- markdownlint-disable -->
|
<!-- markdownlint-disable -->
|
||||||
|
|
||||||
<a href="https://github.com/dangtony98"><img src="https://avatars.githubusercontent.com/u/25857006?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/mv-turtle"><img src="https://avatars.githubusercontent.com/u/78047717?s=96&v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/maidul98"><img src="https://avatars.githubusercontent.com/u/9300960?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/gangjun06"><img src="https://avatars.githubusercontent.com/u/50910815?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/reginaldbondoc"><img src="https://avatars.githubusercontent.com/u/7693108?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/SH5H"><img src="https://avatars.githubusercontent.com/u/25437192?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/asharonbaltazar"><img src="https://avatars.githubusercontent.com/u/58940073?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/edgarrmondragon"><img src="https://avatars.githubusercontent.com/u/16805946?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/arjunyel"><img src="https://avatars.githubusercontent.com/u/11153289?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/hanywang2"><img src="https://avatars.githubusercontent.com/u/44352119?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/tobias-mintlify"><img src="https://avatars.githubusercontent.com/u/110702161?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/0xflotus"><img src="https://avatars.githubusercontent.com/u/26602940?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/wanjohiryan"><img src="https://avatars.githubusercontent.com/u/71614375?v=4" width="50" height="50" alt=""/></a>
|
<a href="https://github.com/dangtony98"><img src="https://avatars.githubusercontent.com/u/25857006?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/mv-turtle"><img src="https://avatars.githubusercontent.com/u/78047717?s=96&v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/maidul98"><img src="https://avatars.githubusercontent.com/u/9300960?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/gangjun06"><img src="https://avatars.githubusercontent.com/u/50910815?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/reginaldbondoc"><img src="https://avatars.githubusercontent.com/u/7693108?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/SH5H"><img src="https://avatars.githubusercontent.com/u/25437192?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/asharonbaltazar"><img src="https://avatars.githubusercontent.com/u/58940073?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/edgarrmondragon"><img src="https://avatars.githubusercontent.com/u/16805946?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/arjunyel"><img src="https://avatars.githubusercontent.com/u/11153289?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/adrianmarinwork"><img src="https://avatars.githubusercontent.com/u/118568289?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/hanywang2"><img src="https://avatars.githubusercontent.com/u/44352119?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/tobias-mintlify"><img src="https://avatars.githubusercontent.com/u/110702161?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/0xflotus"><img src="https://avatars.githubusercontent.com/u/26602940?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/wanjohiryan"><img src="https://avatars.githubusercontent.com/u/71614375?v=4" width="50" height="50" alt=""/></a>
|
||||||
|
|||||||
+2
-7
@@ -1,10 +1,6 @@
|
|||||||
{
|
{
|
||||||
"root": true,
|
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"plugins": [
|
"plugins": ["@typescript-eslint", "prettier"],
|
||||||
"@typescript-eslint",
|
|
||||||
"prettier"
|
|
||||||
],
|
|
||||||
"extends": [
|
"extends": [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:@typescript-eslint/eslint-recommended",
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
@@ -12,7 +8,6 @@
|
|||||||
"prettier"
|
"prettier"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": 2,
|
"no-console": 2
|
||||||
"prettier/prettier": 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"semi": true,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"singleQuote": true,
|
|
||||||
"printWidth": 80,
|
|
||||||
"useTabs": true
|
|
||||||
}
|
|
||||||
Generated
+15
-94
@@ -28,7 +28,7 @@
|
|||||||
"mongoose": "^6.7.2",
|
"mongoose": "^6.7.2",
|
||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.8.0",
|
||||||
"posthog-node": "^2.1.0",
|
"posthog-node": "^2.1.0",
|
||||||
"query-string": "^7.1.1",
|
"query-string": "^7.1.3",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"stripe": "^10.7.0",
|
"stripe": "^10.7.0",
|
||||||
"tweetnacl": "^1.0.3",
|
"tweetnacl": "^1.0.3",
|
||||||
@@ -50,7 +50,6 @@
|
|||||||
"eslint": "^8.26.0",
|
"eslint": "^8.26.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"husky": "^8.0.1",
|
|
||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
"nodemon": "^2.0.19",
|
"nodemon": "^2.0.19",
|
||||||
@@ -2594,19 +2593,6 @@
|
|||||||
"@maxmind/geoip2-node": "^3.4.0"
|
"@maxmind/geoip2-node": "^3.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/core": {
|
|
||||||
"version": "7.17.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.17.4.tgz",
|
|
||||||
"integrity": "sha512-U3ABSJBKGK8dJ01nEG2+qNOb6Wv7U3VqoajiZxfV4lpPWNFGCoEhiTytxBlFTOCmdUH8209zSZiWJZaDLy+TSA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@sentry/types": "7.17.4",
|
|
||||||
"@sentry/utils": "7.17.4",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sentry/node": {
|
"node_modules/@sentry/node": {
|
||||||
"version": "7.19.0",
|
"version": "7.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz",
|
||||||
@@ -2704,26 +2690,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/types": {
|
|
||||||
"version": "7.17.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.17.4.tgz",
|
|
||||||
"integrity": "sha512-QJj8vO4AtxuzQfJIzDnECSmoxwnS+WJsm1Ta2Cwdy+TUCBJyWpW7aIJJGta76zb9gNPGb3UcAbeEjhMJBJeRMQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sentry/utils": {
|
|
||||||
"version": "7.17.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.17.4.tgz",
|
|
||||||
"integrity": "sha512-ioG0ANy8uiWzig82/e7cc+6C9UOxkyBzJDi1luoQVDH6P0/PvM8GzVU+1iUVUipf8+OL1Jh09GrWnd5wLm3XNQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@sentry/types": "7.17.4",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sinclair/typebox": {
|
"node_modules/@sinclair/typebox": {
|
||||||
"version": "0.24.51",
|
"version": "0.24.51",
|
||||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz",
|
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz",
|
||||||
@@ -4035,9 +4001,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/decode-uri-component": {
|
"node_modules/decode-uri-component": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
||||||
"integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==",
|
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
@@ -5146,21 +5112,6 @@
|
|||||||
"node": ">=10.17.0"
|
"node": ">=10.17.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/husky": {
|
|
||||||
"version": "8.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/husky/-/husky-8.0.1.tgz",
|
|
||||||
"integrity": "sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"husky": "lib/bin.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/typicode"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
@@ -9703,11 +9654,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/query-string": {
|
"node_modules/query-string": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
|
||||||
"integrity": "sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w==",
|
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"decode-uri-component": "^0.2.0",
|
"decode-uri-component": "^0.2.2",
|
||||||
"filter-obj": "^1.1.0",
|
"filter-obj": "^1.1.0",
|
||||||
"split-on-first": "^1.0.0",
|
"split-on-first": "^1.0.0",
|
||||||
"strict-uri-encode": "^2.0.0"
|
"strict-uri-encode": "^2.0.0"
|
||||||
@@ -13113,16 +13064,6 @@
|
|||||||
"@maxmind/geoip2-node": "^3.4.0"
|
"@maxmind/geoip2-node": "^3.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/core": {
|
|
||||||
"version": "7.17.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.17.4.tgz",
|
|
||||||
"integrity": "sha512-U3ABSJBKGK8dJ01nEG2+qNOb6Wv7U3VqoajiZxfV4lpPWNFGCoEhiTytxBlFTOCmdUH8209zSZiWJZaDLy+TSA==",
|
|
||||||
"requires": {
|
|
||||||
"@sentry/types": "7.17.4",
|
|
||||||
"@sentry/utils": "7.17.4",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@sentry/node": {
|
"@sentry/node": {
|
||||||
"version": "7.19.0",
|
"version": "7.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz",
|
||||||
@@ -13200,20 +13141,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/types": {
|
|
||||||
"version": "7.17.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.17.4.tgz",
|
|
||||||
"integrity": "sha512-QJj8vO4AtxuzQfJIzDnECSmoxwnS+WJsm1Ta2Cwdy+TUCBJyWpW7aIJJGta76zb9gNPGb3UcAbeEjhMJBJeRMQ=="
|
|
||||||
},
|
|
||||||
"@sentry/utils": {
|
|
||||||
"version": "7.17.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.17.4.tgz",
|
|
||||||
"integrity": "sha512-ioG0ANy8uiWzig82/e7cc+6C9UOxkyBzJDi1luoQVDH6P0/PvM8GzVU+1iUVUipf8+OL1Jh09GrWnd5wLm3XNQ==",
|
|
||||||
"requires": {
|
|
||||||
"@sentry/types": "7.17.4",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@sinclair/typebox": {
|
"@sinclair/typebox": {
|
||||||
"version": "0.24.51",
|
"version": "0.24.51",
|
||||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz",
|
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz",
|
||||||
@@ -14219,9 +14146,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"decode-uri-component": {
|
"decode-uri-component": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
||||||
"integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og=="
|
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
|
||||||
},
|
},
|
||||||
"dedent": {
|
"dedent": {
|
||||||
"version": "0.7.0",
|
"version": "0.7.0",
|
||||||
@@ -15034,12 +14961,6 @@
|
|||||||
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"husky": {
|
|
||||||
"version": "8.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/husky/-/husky-8.0.1.tgz",
|
|
||||||
"integrity": "sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"iconv-lite": {
|
"iconv-lite": {
|
||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
@@ -18324,11 +18245,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"query-string": {
|
"query-string": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
|
||||||
"integrity": "sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w==",
|
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"decode-uri-component": "^0.2.0",
|
"decode-uri-component": "^0.2.2",
|
||||||
"filter-obj": "^1.1.0",
|
"filter-obj": "^1.1.0",
|
||||||
"split-on-first": "^1.0.0",
|
"split-on-first": "^1.0.0",
|
||||||
"strict-uri-encode": "^2.0.0"
|
"strict-uri-encode": "^2.0.0"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"mongoose": "^6.7.2",
|
"mongoose": "^6.7.2",
|
||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.8.0",
|
||||||
"posthog-node": "^2.1.0",
|
"posthog-node": "^2.1.0",
|
||||||
"query-string": "^7.1.1",
|
"query-string": "^7.1.3",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"stripe": "^10.7.0",
|
"stripe": "^10.7.0",
|
||||||
"tweetnacl": "^1.0.3",
|
"tweetnacl": "^1.0.3",
|
||||||
@@ -35,7 +35,8 @@
|
|||||||
"build": "rimraf ./build && tsc && cp -R ./src/templates ./src/json ./build",
|
"build": "rimraf ./build && tsc && cp -R ./src/templates ./src/json ./build",
|
||||||
"lint": "eslint . --ext .ts",
|
"lint": "eslint . --ext .ts",
|
||||||
"lint-and-fix": "eslint . --ext .ts --fix",
|
"lint-and-fix": "eslint . --ext .ts --fix",
|
||||||
"prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write"
|
"prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write",
|
||||||
|
"lint-staged": "lint-staged"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -63,7 +64,6 @@
|
|||||||
"eslint": "^8.26.0",
|
"eslint": "^8.26.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"husky": "^8.0.1",
|
|
||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
"nodemon": "^2.0.19",
|
"nodemon": "^2.0.19",
|
||||||
|
|||||||
@@ -13,12 +13,15 @@ const NODE_ENV = process.env.NODE_ENV! || 'production';
|
|||||||
const OAUTH_CLIENT_SECRET_HEROKU = process.env.OAUTH_CLIENT_SECRET_HEROKU!;
|
const OAUTH_CLIENT_SECRET_HEROKU = process.env.OAUTH_CLIENT_SECRET_HEROKU!;
|
||||||
const OAUTH_TOKEN_URL_HEROKU = process.env.OAUTH_TOKEN_URL_HEROKU!;
|
const OAUTH_TOKEN_URL_HEROKU = process.env.OAUTH_TOKEN_URL_HEROKU!;
|
||||||
const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com';
|
const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com';
|
||||||
const POSTHOG_PROJECT_API_KEY = process.env.POSTHOG_PROJECT_API_KEY! || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
|
const POSTHOG_PROJECT_API_KEY =
|
||||||
|
process.env.POSTHOG_PROJECT_API_KEY! ||
|
||||||
|
'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
|
||||||
const PRIVATE_KEY = process.env.PRIVATE_KEY!;
|
const PRIVATE_KEY = process.env.PRIVATE_KEY!;
|
||||||
const PUBLIC_KEY = process.env.PUBLIC_KEY!;
|
const PUBLIC_KEY = process.env.PUBLIC_KEY!;
|
||||||
const SENTRY_DSN = process.env.SENTRY_DSN!;
|
const SENTRY_DSN = process.env.SENTRY_DSN!;
|
||||||
const SITE_URL = process.env.SITE_URL!;
|
const SITE_URL = process.env.SITE_URL!;
|
||||||
const SMTP_HOST = process.env.SMTP_HOST! || 'smtp.gmail.com';
|
const SMTP_HOST = process.env.SMTP_HOST! || 'smtp.gmail.com';
|
||||||
|
const SMTP_PORT = process.env.SMTP_PORT! || 587;
|
||||||
const SMTP_NAME = process.env.SMTP_NAME!;
|
const SMTP_NAME = process.env.SMTP_NAME!;
|
||||||
const SMTP_USERNAME = process.env.SMTP_USERNAME!;
|
const SMTP_USERNAME = process.env.SMTP_USERNAME!;
|
||||||
const SMTP_PASSWORD = process.env.SMTP_PASSWORD!;
|
const SMTP_PASSWORD = process.env.SMTP_PASSWORD!;
|
||||||
@@ -28,7 +31,7 @@ const STRIPE_PRODUCT_STARTER = process.env.STRIPE_PRODUCT_STARTER!;
|
|||||||
const STRIPE_PUBLISHABLE_KEY = process.env.STRIPE_PUBLISHABLE_KEY!;
|
const STRIPE_PUBLISHABLE_KEY = process.env.STRIPE_PUBLISHABLE_KEY!;
|
||||||
const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY!;
|
const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY!;
|
||||||
const STRIPE_WEBHOOK_SECRET = process.env.STRIPE_WEBHOOK_SECRET!;
|
const STRIPE_WEBHOOK_SECRET = process.env.STRIPE_WEBHOOK_SECRET!;
|
||||||
const TELEMETRY_ENABLED = (process.env.TELEMETRY_ENABLED! !== 'false') && true;
|
const TELEMETRY_ENABLED = process.env.TELEMETRY_ENABLED! !== 'false' && true;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
PORT,
|
PORT,
|
||||||
@@ -52,6 +55,7 @@ export {
|
|||||||
SENTRY_DSN,
|
SENTRY_DSN,
|
||||||
SITE_URL,
|
SITE_URL,
|
||||||
SMTP_HOST,
|
SMTP_HOST,
|
||||||
|
SMTP_PORT,
|
||||||
SMTP_NAME,
|
SMTP_NAME,
|
||||||
SMTP_USERNAME,
|
SMTP_USERNAME,
|
||||||
SMTP_PASSWORD,
|
SMTP_PASSWORD,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
@@ -37,7 +38,6 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
email
|
email
|
||||||
}).select('+salt +verifier');
|
}).select('+salt +verifier');
|
||||||
|
|
||||||
|
|
||||||
if (!user) throw new Error('Failed to find user');
|
if (!user) throw new Error('Failed to find user');
|
||||||
|
|
||||||
const server = new jsrp.server();
|
const server = new jsrp.server();
|
||||||
@@ -54,7 +54,6 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
serverBInt: bigintConversion.bigintToBuf(server.bInt)
|
serverBInt: bigintConversion.bigintToBuf(server.bInt)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serverPublicKey,
|
serverPublicKey,
|
||||||
salt: user.salt
|
salt: user.salt
|
||||||
@@ -105,7 +104,7 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
res.cookie('jid', tokens.refreshToken, {
|
res.cookie('jid', tokens.refreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/token',
|
path: '/token',
|
||||||
sameSite: "strict",
|
sameSite: 'strict',
|
||||||
secure: NODE_ENV === 'production' ? true : false
|
secure: NODE_ENV === 'production' ? true : false
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -149,7 +148,7 @@ export const logout = async (req: Request, res: Response) => {
|
|||||||
res.cookie('jid', '', {
|
res.cookie('jid', '', {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/token',
|
path: '/token',
|
||||||
sameSite: "strict",
|
sameSite: 'strict',
|
||||||
secure: NODE_ENV === 'production' ? true : false
|
secure: NODE_ENV === 'production' ? true : false
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -213,7 +212,6 @@ export const getNewToken = async (req: Request, res: Response) => {
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
token
|
token
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
const { email, code } = req.body;
|
const { email, code } = req.body;
|
||||||
|
|
||||||
user = await User.findOne({ email });
|
user = await User.findOne({ email }).select('+publicKey');
|
||||||
if (user && user?.publicKey) {
|
if (user && user?.publicKey) {
|
||||||
// case: user has already completed account
|
// case: user has already completed account
|
||||||
return res.status(403).send({
|
return res.status(403).send({
|
||||||
@@ -257,7 +257,7 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
error: 'Failed email magic link confirmation'
|
error: 'Failed email magic link verification for organization invitation'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,121 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
|
import crypto from 'crypto';
|
||||||
const jsrp = require('jsrp');
|
const jsrp = require('jsrp');
|
||||||
import * as bigintConversion from 'bigint-conversion';
|
import * as bigintConversion from 'bigint-conversion';
|
||||||
import { User, BackupPrivateKey } from '../models';
|
import { User, Token, BackupPrivateKey } from '../models';
|
||||||
|
import { checkEmailVerification } from '../helpers/signup';
|
||||||
|
import { createToken } from '../helpers/auth';
|
||||||
|
import { sendMail } from '../helpers/nodemailer';
|
||||||
|
import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, SITE_URL } from '../config';
|
||||||
|
|
||||||
const clientPublicKeys: any = {};
|
const clientPublicKeys: any = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password reset step 1: Send email verification link to email [email]
|
||||||
|
* for account recovery.
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const emailPasswordReset = async (req: Request, res: Response) => {
|
||||||
|
let email: string;
|
||||||
|
try {
|
||||||
|
email = req.body.email;
|
||||||
|
|
||||||
|
const user = await User.findOne({ email }).select('+publicKey');
|
||||||
|
if (!user || !user?.publicKey) {
|
||||||
|
// case: user has already completed account
|
||||||
|
|
||||||
|
return res.status(403).send({
|
||||||
|
error: 'Failed to send email verification for password reset'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = crypto.randomBytes(16).toString('hex');
|
||||||
|
|
||||||
|
await Token.findOneAndUpdate(
|
||||||
|
{ email },
|
||||||
|
{
|
||||||
|
email,
|
||||||
|
token,
|
||||||
|
createdAt: new Date()
|
||||||
|
},
|
||||||
|
{ upsert: true, new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
await sendMail({
|
||||||
|
template: 'passwordReset.handlebars',
|
||||||
|
subjectLine: 'Infisical password reset',
|
||||||
|
recipients: [email],
|
||||||
|
substitutions: {
|
||||||
|
email,
|
||||||
|
token,
|
||||||
|
callback_url: SITE_URL + '/password-reset'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
Sentry.setUser(null);
|
||||||
|
Sentry.captureException(err);
|
||||||
|
return res.status(400).send({
|
||||||
|
message: 'Failed to send email for account recovery'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
message: `Sent an email for account recovery to ${email}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password reset step 2: Verify email verification link sent to email [email]
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const emailPasswordResetVerify = async (req: Request, res: Response) => {
|
||||||
|
let user, token;
|
||||||
|
try {
|
||||||
|
const { email, code } = req.body;
|
||||||
|
|
||||||
|
user = await User.findOne({ email }).select('+publicKey');
|
||||||
|
if (!user || !user?.publicKey) {
|
||||||
|
// case: user doesn't exist with email [email] or
|
||||||
|
// hasn't even completed their account
|
||||||
|
return res.status(403).send({
|
||||||
|
error: 'Failed email verification for password reset'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await checkEmailVerification({
|
||||||
|
email,
|
||||||
|
code
|
||||||
|
});
|
||||||
|
|
||||||
|
// generate temporary password-reset token
|
||||||
|
token = createToken({
|
||||||
|
payload: {
|
||||||
|
userId: user._id.toString()
|
||||||
|
},
|
||||||
|
expiresIn: JWT_SIGNUP_LIFETIME,
|
||||||
|
secret: JWT_SIGNUP_SECRET
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
Sentry.setUser(null);
|
||||||
|
Sentry.captureException(err);
|
||||||
|
return res.status(400).send({
|
||||||
|
message: 'Failed email verification for password reset'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
message: 'Successfully verified email',
|
||||||
|
user,
|
||||||
|
token
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol
|
* Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol
|
||||||
* @param req
|
* @param req
|
||||||
@@ -43,7 +153,7 @@ export const srp1 = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser({ email: req.user.email });
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
error: 'Failed to start change password process'
|
error: 'Failed to start change password process'
|
||||||
@@ -110,7 +220,7 @@ export const changePassword = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser({ email: req.user.email });
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
error: 'Failed to change password. Try again?'
|
error: 'Failed to change password. Try again?'
|
||||||
@@ -180,10 +290,73 @@ export const createBackupPrivateKey = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser({ email: req.user.email });
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to update backup private key'
|
message: 'Failed to update backup private key'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return backup private key for user
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getBackupPrivateKey = async (req: Request, res: Response) => {
|
||||||
|
let backupPrivateKey;
|
||||||
|
try {
|
||||||
|
backupPrivateKey = await BackupPrivateKey.findOne({
|
||||||
|
user: req.user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!backupPrivateKey) throw new Error('Failed to find backup private key');
|
||||||
|
} catch (err) {
|
||||||
|
Sentry.setUser({ email: req.user.email});
|
||||||
|
Sentry.captureException(err);
|
||||||
|
return res.status(400).send({
|
||||||
|
message: 'Failed to get backup private key'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
backupPrivateKey
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const resetPassword = async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
encryptedPrivateKey,
|
||||||
|
iv,
|
||||||
|
tag,
|
||||||
|
salt,
|
||||||
|
verifier,
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
await User.findByIdAndUpdate(
|
||||||
|
req.user._id.toString(),
|
||||||
|
{
|
||||||
|
encryptedPrivateKey,
|
||||||
|
iv,
|
||||||
|
tag,
|
||||||
|
salt,
|
||||||
|
verifier
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
Sentry.setUser({ email: req.user.email});
|
||||||
|
Sentry.captureException(err);
|
||||||
|
return res.status(400).send({
|
||||||
|
message: 'Failed to get backup private key'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
message: 'Successfully reset password'
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -2,21 +2,40 @@ import fs from 'fs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import handlebars from 'handlebars';
|
import handlebars from 'handlebars';
|
||||||
import nodemailer from 'nodemailer';
|
import nodemailer from 'nodemailer';
|
||||||
import { SMTP_HOST, SMTP_NAME, SMTP_USERNAME, SMTP_PASSWORD } from '../config';
|
import {
|
||||||
|
SMTP_HOST,
|
||||||
|
SMTP_PORT,
|
||||||
|
SMTP_NAME,
|
||||||
|
SMTP_USERNAME,
|
||||||
|
SMTP_PASSWORD
|
||||||
|
} from '../config';
|
||||||
|
import SMTPConnection from 'nodemailer/lib/smtp-connection';
|
||||||
|
import * as Sentry from '@sentry/node';
|
||||||
|
|
||||||
// create nodemailer transporter
|
const mailOpts: SMTPConnection.Options = {
|
||||||
const transporter = nodemailer.createTransport({
|
|
||||||
host: SMTP_HOST,
|
host: SMTP_HOST,
|
||||||
port: 587,
|
port: SMTP_PORT as number
|
||||||
auth: {
|
};
|
||||||
|
if (SMTP_USERNAME && SMTP_PASSWORD) {
|
||||||
|
mailOpts.auth = {
|
||||||
user: SMTP_USERNAME,
|
user: SMTP_USERNAME,
|
||||||
pass: SMTP_PASSWORD
|
pass: SMTP_PASSWORD
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
// create nodemailer transporter
|
||||||
|
const transporter = nodemailer.createTransport(mailOpts);
|
||||||
transporter
|
transporter
|
||||||
.verify()
|
.verify()
|
||||||
.then(() => console.log('SMTP - Successfully connected'))
|
.then(() => {
|
||||||
.catch((err) => console.log('SMTP - Failed to connect'));
|
Sentry.setUser(null);
|
||||||
|
Sentry.captureMessage('SMTP - Successfully connected');
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
Sentry.setUser(null);
|
||||||
|
Sentry.captureException(
|
||||||
|
`SMTP - Failed to connect to ${SMTP_HOST}:${SMTP_PORT} \n\t${err}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
@@ -51,7 +70,8 @@ const sendMail = async ({
|
|||||||
html: htmlToSend
|
html: htmlToSend
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
Sentry.setUser(null);
|
||||||
|
Sentry.captureException(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const sendEmailVerification = async ({ email }: { email: string }) => {
|
|||||||
// send mail
|
// send mail
|
||||||
await sendMail({
|
await sendMail({
|
||||||
template: 'emailVerification.handlebars',
|
template: 'emailVerification.handlebars',
|
||||||
subjectLine: 'Infisical workspace invitation',
|
subjectLine: 'Infisical confirmation code',
|
||||||
recipients: [email],
|
recipients: [email],
|
||||||
substitutions: {
|
substitutions: {
|
||||||
code: token
|
code: token
|
||||||
|
|||||||
@@ -5,11 +5,7 @@ import { requireAuth, validateRequest } from '../middleware';
|
|||||||
import { authController } from '../controllers';
|
import { authController } from '../controllers';
|
||||||
import { loginLimiter } from '../helpers/rateLimiter';
|
import { loginLimiter } from '../helpers/rateLimiter';
|
||||||
|
|
||||||
router.post(
|
router.post('/token', validateRequest, authController.getNewToken);
|
||||||
'/token',
|
|
||||||
validateRequest,
|
|
||||||
authController.getNewToken
|
|
||||||
);
|
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/login1',
|
'/login1',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { body } from 'express-validator';
|
import { body } from 'express-validator';
|
||||||
import { requireAuth, validateRequest } from '../middleware';
|
import { requireAuth, requireSignupAuth, validateRequest } from '../middleware';
|
||||||
import { passwordController } from '../controllers';
|
import { passwordController } from '../controllers';
|
||||||
import { passwordLimiter } from '../helpers/rateLimiter';
|
import { passwordLimiter } from '../helpers/rateLimiter';
|
||||||
|
|
||||||
@@ -27,6 +27,33 @@ router.post(
|
|||||||
passwordController.changePassword
|
passwordController.changePassword
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// NEW
|
||||||
|
router.post(
|
||||||
|
'/email/password-reset',
|
||||||
|
passwordLimiter,
|
||||||
|
body('email').exists().trim().notEmpty(),
|
||||||
|
validateRequest,
|
||||||
|
passwordController.emailPasswordReset
|
||||||
|
);
|
||||||
|
|
||||||
|
// NEW
|
||||||
|
router.post(
|
||||||
|
'/email/password-reset-verify',
|
||||||
|
passwordLimiter,
|
||||||
|
body('email').exists().trim().notEmpty().isEmail(),
|
||||||
|
body('code').exists().trim().notEmpty(),
|
||||||
|
validateRequest,
|
||||||
|
passwordController.emailPasswordResetVerify
|
||||||
|
);
|
||||||
|
|
||||||
|
// NEW
|
||||||
|
router.get(
|
||||||
|
'/backup-private-key',
|
||||||
|
passwordLimiter,
|
||||||
|
requireSignupAuth,
|
||||||
|
passwordController.getBackupPrivateKey
|
||||||
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/backup-private-key',
|
'/backup-private-key',
|
||||||
passwordLimiter,
|
passwordLimiter,
|
||||||
@@ -41,4 +68,17 @@ router.post(
|
|||||||
passwordController.createBackupPrivateKey
|
passwordController.createBackupPrivateKey
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// NEW
|
||||||
|
router.post(
|
||||||
|
'/password-reset',
|
||||||
|
requireSignupAuth,
|
||||||
|
body('encryptedPrivateKey').exists().trim().notEmpty(), // private key encrypted under new pwd
|
||||||
|
body('iv').exists().trim().notEmpty(), // new iv for private key
|
||||||
|
body('tag').exists().trim().notEmpty(), // new tag for private key
|
||||||
|
body('salt').exists().trim().notEmpty(), // part of new pwd
|
||||||
|
body('verifier').exists().trim().notEmpty(), // part of new pwd
|
||||||
|
validateRequest,
|
||||||
|
passwordController.resetPassword
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
<title>Email Verification</title>
|
<title>Organization Invitation</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h2>Infisical</h2>
|
<h2>Infisical</h2>
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
|
<title>Account Recovery</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>Infisical</h2>
|
||||||
|
<h2>Reset your password</h2>
|
||||||
|
<p>Someone requested a password reset.</p>
|
||||||
|
<a href="{{callback_url}}?token={{token}}&to={{email}}">Reset password</a>
|
||||||
|
<p>If you didn't initiate this request, please contact us immediately at [email protected]</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
<title>Email Verification</title>
|
<title>Project Invitation</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h2>Infisical</h2>
|
<h2>Infisical</h2>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
- mongo
|
- mongo
|
||||||
|
- smtp-server
|
||||||
build:
|
build:
|
||||||
context: ./backend
|
context: ./backend
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
@@ -84,6 +85,18 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- infisical-dev
|
- infisical-dev
|
||||||
|
|
||||||
|
smtp-server:
|
||||||
|
container_name: infisical-dev-smtp-server
|
||||||
|
image: mailhog/mailhog
|
||||||
|
restart: always
|
||||||
|
logging:
|
||||||
|
driver: 'none' # disable saving logs
|
||||||
|
ports:
|
||||||
|
- 1025:1025 # SMTP server
|
||||||
|
- 8025:8025 # Web UI
|
||||||
|
networks:
|
||||||
|
- infisical-dev
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
mongo-data:
|
mongo-data:
|
||||||
driver: local
|
driver: local
|
||||||
|
|||||||
+11
-16
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "Install"
|
title: 'Install'
|
||||||
---
|
---
|
||||||
|
|
||||||
Prerequisite: Set up an account with [Infisical Cloud](https://app.infisical.com) or via a [self-hosted installation](/self-hosting/overview).
|
Prerequisite: Set up an account with [Infisical Cloud](https://app.infisical.com) or via a [self-hosted installation](/self-hosting/overview).
|
||||||
@@ -13,11 +13,7 @@ The Infisical CLI provides a way to inject environment variables from the platfo
|
|||||||
Use [brew](https://brew.sh/) package manager
|
Use [brew](https://brew.sh/) package manager
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# install
|
|
||||||
brew install infisical/get-cli/infisical
|
brew install infisical/get-cli/infisical
|
||||||
|
|
||||||
# check version
|
|
||||||
infisical --version
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Updates
|
## Updates
|
||||||
@@ -31,12 +27,11 @@ The Infisical CLI provides a way to inject environment variables from the platfo
|
|||||||
Use [Scoop](https://scoop.sh/) package manager
|
Use [Scoop](https://scoop.sh/) package manager
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# install
|
|
||||||
scoop bucket add org https://github.com/Infisical/scoop-infisical.git
|
scoop bucket add org https://github.com/Infisical/scoop-infisical.git
|
||||||
scoop install infisical
|
```
|
||||||
|
|
||||||
# check version
|
```bash
|
||||||
infisical --version
|
scoop install infisical
|
||||||
```
|
```
|
||||||
|
|
||||||
## Updates
|
## Updates
|
||||||
@@ -49,33 +44,33 @@ The Infisical CLI provides a way to inject environment variables from the platfo
|
|||||||
<Tab title="Alpine">
|
<Tab title="Alpine">
|
||||||
Install prerequisite
|
Install prerequisite
|
||||||
```bash
|
```bash
|
||||||
$ sudo apk add --no-cache bash sudo
|
sudo apk add --no-cache bash sudo
|
||||||
```
|
```
|
||||||
|
|
||||||
Add Infisical repository
|
Add Infisical repository
|
||||||
```bash
|
```bash
|
||||||
$ curl -1sLf \
|
curl -1sLf \
|
||||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' \
|
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' \
|
||||||
| sudo -E bash
|
| sudo -E bash
|
||||||
```
|
```
|
||||||
|
|
||||||
Then install CLI
|
Then install CLI
|
||||||
```bash
|
```bash
|
||||||
$ sudo apk update && sudo apk add infisical
|
sudo apk update && sudo apk add infisical
|
||||||
```
|
```
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="RedHat/CentOs/Amazon">
|
<Tab title="RedHat/CentOs/Amazon">
|
||||||
Add Infisical repository
|
Add Infisical repository
|
||||||
```bash
|
```bash
|
||||||
$ curl -1sLf \
|
curl -1sLf \
|
||||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.rpm.sh' \
|
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.rpm.sh' \
|
||||||
| sudo -E bash
|
| sudo -E bash
|
||||||
```
|
```
|
||||||
|
|
||||||
Then install CLI
|
Then install CLI
|
||||||
```bash
|
```bash
|
||||||
$ sudo yum install infisical
|
sudo yum install infisical
|
||||||
```
|
```
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
@@ -83,14 +78,14 @@ The Infisical CLI provides a way to inject environment variables from the platfo
|
|||||||
Add Infisical repository
|
Add Infisical repository
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ curl -1sLf \
|
curl -1sLf \
|
||||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' \
|
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' \
|
||||||
| sudo -E bash
|
| sudo -E bash
|
||||||
```
|
```
|
||||||
|
|
||||||
Then install CLI
|
Then install CLI
|
||||||
```bash
|
```bash
|
||||||
$ sudo apt-get update && sudo apt-get install -y infisical
|
sudo apt-get update && sudo apt-get install -y infisical
|
||||||
```
|
```
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
---
|
---
|
||||||
title: "Frequently Asked Questions"
|
title: 'Frequently Asked Questions'
|
||||||
description: "Have any questions? [Join our Slack community](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g)."
|
description: 'Have any questions? [Join our Slack community](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g).'
|
||||||
---
|
---
|
||||||
|
|
||||||
## Problem with SMTP
|
## Problem with SMTP
|
||||||
|
|
||||||
|
If you opt for actual SMTP server (not the local MailHog), you have to have the right environment variables set.
|
||||||
|
|
||||||
You can normally populate `SMTP_USERNAME` and `SMTP_PASSWORD` with your usual login and password (you could also create a 'burner' email). Sometimes, there still are problems.
|
You can normally populate `SMTP_USERNAME` and `SMTP_PASSWORD` with your usual login and password (you could also create a 'burner' email). Sometimes, there still are problems.
|
||||||
|
|
||||||
You can go to your Gmail account settings > security and enable “less secure apps”. This would allow Infisical to use your Gmail to send emails.
|
You can go to your Gmail account settings > security and enable “less secure apps”. This would allow Infisical to use your Gmail to send emails.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "Developing"
|
title: 'Developing'
|
||||||
description: "This guide will help you set up and run Infisical in development mode."
|
description: 'This guide will help you set up and run Infisical in development mode.'
|
||||||
---
|
---
|
||||||
|
|
||||||
## Clone the repo
|
## Clone the repo
|
||||||
@@ -16,17 +16,65 @@ cd infisical
|
|||||||
|
|
||||||
## Set up environment variables
|
## Set up environment variables
|
||||||
|
|
||||||
Tweak the `.env` according to your preferences. Refer to the available [environment variables](/self-hosting/configuration/envars).
|
Before running the docker-compose we have to generate the .env file with the environment variables, you can create your own file or start with the
|
||||||
|
`.env.example` as an example guide.
|
||||||
|
|
||||||
|
Mandatory variables in the `.env` file:
|
||||||
|
|
||||||
|
1. Keys and JWT variables
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The `.env.example` has these variables empty, you can self generate the `JWT and ENCRYPTION_KEY` with this [32-byte random hex strings generator](https://www.browserling.com/tools/random-hex).
|
||||||
|
|
||||||
|
For the `PRIVATE_KEY and PUBLIC_KEY` you can use the ones shown in the screenshot:
|
||||||
|
|
||||||
```bash
|
|
||||||
cp .env.example .env
|
|
||||||
```
|
```
|
||||||
|
PRIVATE_KEY='oGVv5rThrpZ7WLgQW27chY1cXngr4wLQIZnGfSKgHPk='
|
||||||
|
PUBLIC_KEY='ldr6JaC7AY+tun3omGLdE4SWpkJbtVBOI54KfUP53Xc='
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Mongo variables and site URL
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
These variables are used to connect the MongoDB and set the URL for the localhost.
|
||||||
|
|
||||||
|
For development, you can use `root` for the `MONGO_USERNAME` and `example` for the `MONGO_PASSWORD` as shown in the screenshot.
|
||||||
|
|
||||||
|
Take into account that if you use your own `MONGO_USERNAME` and `MONGO_PASSWORD`, you also have to change the `MONGO_URL` with the form of `MONGO_USERNAME:MONGO_PASSWORD` after the `//` part of the URL.
|
||||||
|
|
||||||
|
3. Mail SMTP service variables
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If you want to receive actual emails (e.g. you want to test how the email message will look like), take note of the following.
|
||||||
|
|
||||||
|
For the `SMTP_USERNAME` variable, you will need an email with 2-steps-verification.
|
||||||
|
|
||||||
|
For the `SMTP_PASSWORD` variable, you will need to [generate an app password](https://support.google.com/mail/answer/185833?hl=en) with the email you used in the `SMTP_USERNAME` variable.
|
||||||
|
|
||||||
|
Otherwise, a local SMTP server (MailHog) is available for testing purposes. Set the following values to use this:
|
||||||
|
|
||||||
|
```
|
||||||
|
SMTP_HOST=smtp-server
|
||||||
|
SMTP_PORT=1025
|
||||||
|
SMTP_NAME=<whatever you like>
|
||||||
|
[email protected]
|
||||||
|
SMTP_PASSWORD=
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure to leave the `SMTP_PASSWORD` blank so the backend will be able to connect to MailHog
|
||||||
|
|
||||||
|
You can browse `http://localhost:8025/` to browse email messages sent by the backend.
|
||||||
|
|
||||||
|
With these environment variables, you will be ready to run the docker-compose.
|
||||||
|
|
||||||
## Docker for development
|
## Docker for development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# build and start the services
|
# build and start the services
|
||||||
docker-compose -f docker-compose.dev.yml up --build
|
docker-compose -f docker-compose.dev.yml up --build --force-recreate
|
||||||
```
|
```
|
||||||
|
|
||||||
Then browse http://localhost:8080
|
Then browse http://localhost:8080
|
||||||
|
|||||||
+2
-2
@@ -9,13 +9,13 @@
|
|||||||
"plugins": ["simple-import-sort", "@typescript-eslint"],
|
"plugins": ["simple-import-sort", "@typescript-eslint"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"react-hooks/exhaustive-deps": "off",
|
"react-hooks/exhaustive-deps": "off",
|
||||||
"no-unused-vars": "off",
|
"no-unused-vars": "warn",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "warn",
|
||||||
"@typescript-eslint/no-unused-vars": "off",
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
"@typescript-eslint/no-var-requires": "off",
|
||||||
"@typescript-eslint/no-empty-function": "off",
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
"@typescript-eslint/no-non-null-assertion": "off",
|
"@typescript-eslint/no-non-null-assertion": "off",
|
||||||
|
|
||||||
"simple-import-sort/exports": "warn",
|
"simple-import-sort/exports": "warn",
|
||||||
"simple-import-sort/imports": [
|
"simple-import-sort/imports": [
|
||||||
"warn",
|
"warn",
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"tabWidth": 2,
|
|
||||||
"useTabs": false
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from 'react';
|
||||||
import Image from "next/image";
|
import Image from 'next/image';
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
import { publicPaths } from "~/const";
|
import { publicPaths } from '~/const';
|
||||||
import checkAuth from "~/pages/api/auth/CheckAuth";
|
import checkAuth from '~/pages/api/auth/CheckAuth';
|
||||||
|
|
||||||
// #TODO: finish spinner only when the data loads fully
|
// #TODO: finish spinner only when the data loads fully
|
||||||
// #TODO: Redirect somewhere if the page does not exist
|
// #TODO: Redirect somewhere if the page does not exist
|
||||||
@@ -22,16 +22,16 @@ export default function RouteGuard({ children }) {
|
|||||||
// #TODO: add the loading page when not yet authorized.
|
// #TODO: add the loading page when not yet authorized.
|
||||||
const hideContent = () => setAuthorized(false);
|
const hideContent = () => setAuthorized(false);
|
||||||
// const onError = () => setAuthorized(true)
|
// const onError = () => setAuthorized(true)
|
||||||
router.events.on("routeChangeStart", hideContent);
|
router.events.on('routeChangeStart', hideContent);
|
||||||
// router.events.on("routeChangeError", onError);
|
// router.events.on("routeChangeError", onError);
|
||||||
|
|
||||||
// on route change complete - run auth check
|
// on route change complete - run auth check
|
||||||
router.events.on("routeChangeComplete", authCheck);
|
router.events.on('routeChangeComplete', authCheck);
|
||||||
|
|
||||||
// unsubscribe from events in useEffect return function
|
// unsubscribe from events in useEffect return function
|
||||||
return () => {
|
return () => {
|
||||||
router.events.off("routeChangeStart", hideContent);
|
router.events.off('routeChangeStart', hideContent);
|
||||||
router.events.off("routeChangeComplete", authCheck);
|
router.events.off('routeChangeComplete', authCheck);
|
||||||
// router.events.off("routeChangeError", onError);
|
// router.events.off("routeChangeError", onError);
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -43,7 +43,7 @@ export default function RouteGuard({ children }) {
|
|||||||
*/
|
*/
|
||||||
async function authCheck(url) {
|
async function authCheck(url) {
|
||||||
// Make sure that we don't redirect when the user is on the following pages.
|
// Make sure that we don't redirect when the user is on the following pages.
|
||||||
const path = "/" + url.split("?")[0].split("/")[1];
|
const path = '/' + url.split('?')[0].split('/')[1];
|
||||||
|
|
||||||
// Check if the user is authenticated
|
// Check if the user is authenticated
|
||||||
const response = await checkAuth();
|
const response = await checkAuth();
|
||||||
@@ -51,16 +51,16 @@ export default function RouteGuard({ children }) {
|
|||||||
if (!publicPaths.includes(path)) {
|
if (!publicPaths.includes(path)) {
|
||||||
try {
|
try {
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
router.push("/login");
|
router.push('/login');
|
||||||
console.log("Unauthorized to access.");
|
console.log('Unauthorized to access.');
|
||||||
setAuthorized(false);
|
setAuthorized(false);
|
||||||
} else {
|
} else {
|
||||||
setAuthorized(true);
|
setAuthorized(true);
|
||||||
console.log("Authorized to access.");
|
console.log('Authorized to access.');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(
|
console.log(
|
||||||
"Error (probably the authCheck route is stuck again...):",
|
'Error (probably the authCheck route is stuck again...):',
|
||||||
error
|
error
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import posthog from "posthog-js";
|
import posthog from 'posthog-js';
|
||||||
|
|
||||||
import {
|
import { ENV, POSTHOG_API_KEY, POSTHOG_HOST } from '../utilities/config';
|
||||||
ENV,
|
|
||||||
POSTHOG_API_KEY,
|
|
||||||
POSTHOG_HOST,
|
|
||||||
} from "../utilities/config";
|
|
||||||
|
|
||||||
export const initPostHog = () => {
|
export const initPostHog = () => {
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== 'undefined') {
|
||||||
if (ENV == "production" && TELEMETRY_CAPTURING_ENABLED) { // eslint-disable-line
|
// eslint-disable-next-line
|
||||||
|
if (ENV == 'production' && TELEMETRY_CAPTURING_ENABLED) {
|
||||||
posthog.init(POSTHOG_API_KEY, {
|
posthog.init(POSTHOG_API_KEY, {
|
||||||
api_host: POSTHOG_HOST,
|
api_host: POSTHOG_HOST
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
|
/* eslint-disable no-undef */
|
||||||
|
import posthog from 'posthog-js';
|
||||||
|
|
||||||
|
import { ENV, POSTHOG_API_KEY, POSTHOG_HOST } from '../utilities/config';
|
||||||
|
|
||||||
|
export const initPostHog = () => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// @ts-ignore
|
||||||
|
if (ENV == 'production' && TELEMETRY_CAPTURING_ENABLED) {
|
||||||
|
posthog.init(POSTHOG_API_KEY, {
|
||||||
|
api_host: POSTHOG_HOST
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return posthog;
|
||||||
|
};
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from 'react';
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from 'next/router';
|
||||||
import { faCircle, faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons";
|
import { faCircle, faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
import guidGenerator from "../utilities/randomId";
|
import guidGenerator from '../utilities/randomId';
|
||||||
|
|
||||||
interface InputFieldProps {
|
interface InputFieldProps {
|
||||||
static?: boolean;
|
static?: boolean;
|
||||||
@@ -23,7 +23,7 @@ interface InputFieldProps {
|
|||||||
|
|
||||||
const InputField = (
|
const InputField = (
|
||||||
props: InputFieldProps &
|
props: InputFieldProps &
|
||||||
Pick<JSX.IntrinsicElements["input"], "autoComplete" | "id">
|
Pick<JSX.IntrinsicElements['input'], 'autoComplete' | 'id'>
|
||||||
) => {
|
) => {
|
||||||
const [passwordVisible, setPasswordVisible] = useState(false);
|
const [passwordVisible, setPasswordVisible] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -75,28 +75,28 @@ const InputField = (
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`group relative flex flex-col justify-center w-full max-w-2xl border ${
|
className={`group relative flex flex-col justify-center w-full max-w-2xl border ${
|
||||||
props.error ? "border-red" : "border-mineshaft-500"
|
props.error ? 'border-red' : 'border-mineshaft-500'
|
||||||
} rounded-md`}
|
} rounded-md`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
onChange={(e) => props.onChangeHandler(e.target.value)}
|
onChange={(e) => props.onChangeHandler(e.target.value)}
|
||||||
type={passwordVisible === false ? props.type : "text"}
|
type={passwordVisible === false ? props.type : 'text'}
|
||||||
placeholder={props.placeholder}
|
placeholder={props.placeholder}
|
||||||
value={props.value}
|
value={props.value}
|
||||||
required={props.isRequired}
|
required={props.isRequired}
|
||||||
className={`${
|
className={`${
|
||||||
props.blurred
|
props.blurred
|
||||||
? "text-bunker-800 group-hover:text-gray-400 focus:text-gray-400 active:text-gray-400"
|
? 'text-bunker-800 group-hover:text-gray-400 focus:text-gray-400 active:text-gray-400'
|
||||||
: ""
|
: ''
|
||||||
} ${
|
} ${
|
||||||
props.error ? "focus:ring-red/50" : "focus:ring-primary/50"
|
props.error ? 'focus:ring-red/50' : 'focus:ring-primary/50'
|
||||||
} relative peer bg-bunker-800 rounded-md text-gray-400 text-md p-2 w-full min-w-16 outline-none focus:ring-4 duration-200`}
|
} relative peer bg-bunker-800 rounded-md text-gray-400 text-md p-2 w-full min-w-16 outline-none focus:ring-4 duration-200`}
|
||||||
name={props.name}
|
name={props.name}
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
autoComplete={props.autoComplete}
|
autoComplete={props.autoComplete}
|
||||||
id={props.id}
|
id={props.id}
|
||||||
/>
|
/>
|
||||||
{props.label?.includes("Password") && (
|
{props.label?.includes('Password') && (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPasswordVisible(!passwordVisible);
|
setPasswordVisible(!passwordVisible);
|
||||||
@@ -114,7 +114,7 @@ const InputField = (
|
|||||||
<div className="peer group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible absolute h-10 w-fit max-w-xl rounded-md flex items-center text-gray-400/50 text-clip overflow-hidden">
|
<div className="peer group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible absolute h-10 w-fit max-w-xl rounded-md flex items-center text-gray-400/50 text-clip overflow-hidden">
|
||||||
<p className="ml-2"></p>
|
<p className="ml-2"></p>
|
||||||
{props.value
|
{props.value
|
||||||
.split("")
|
.split('')
|
||||||
.slice(0, 54)
|
.slice(0, 54)
|
||||||
.map(() => (
|
.map(() => (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
|
|||||||
@@ -1,37 +1,38 @@
|
|||||||
/* eslint-disable no-unexpected-multiline */
|
/* eslint-disable no-unexpected-multiline */
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from 'react';
|
||||||
import Link from "next/link";
|
import Link from 'next/link';
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from 'next/router';
|
||||||
import {
|
import {
|
||||||
faBookOpen,
|
faBookOpen,
|
||||||
faGear,
|
faGear,
|
||||||
faKey,
|
faKey,
|
||||||
faMobile,
|
faMobile,
|
||||||
faPlug,
|
faPlug,
|
||||||
faUser,
|
faUser
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
import { faPlus } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
import getOrganizations from "~/pages/api/organization/getOrgs";
|
import getOrganizations from '~/pages/api/organization/getOrgs';
|
||||||
import getOrganizationUserProjects from "~/pages/api/organization/GetOrgUserProjects";
|
import getOrganizationUserProjects from '~/pages/api/organization/GetOrgUserProjects';
|
||||||
import getOrganizationUsers from "~/pages/api/organization/GetOrgUsers";
|
import getOrganizationUsers from '~/pages/api/organization/GetOrgUsers';
|
||||||
import addUserToWorkspace from "~/pages/api/workspace/addUserToWorkspace";
|
import checkUserAction from '~/pages/api/userActions/checkUserAction';
|
||||||
import createWorkspace from "~/pages/api/workspace/createWorkspace";
|
import addUserToWorkspace from '~/pages/api/workspace/addUserToWorkspace';
|
||||||
import getWorkspaces from "~/pages/api/workspace/getWorkspaces";
|
import createWorkspace from '~/pages/api/workspace/createWorkspace';
|
||||||
import uploadKeys from "~/pages/api/workspace/uploadKeys";
|
import getWorkspaces from '~/pages/api/workspace/getWorkspaces';
|
||||||
import checkUserAction from "~/pages/api/userActions/checkUserAction";
|
import uploadKeys from '~/pages/api/workspace/uploadKeys';
|
||||||
|
|
||||||
import NavBarDashboard from "../navigation/NavBarDashboard";
|
import NavBarDashboard from '../navigation/NavBarDashboard';
|
||||||
import { tempLocalStorage } from "../utilities/checks/tempLocalStorage";
|
import onboardingCheck from '../utilities/checks/OnboardingCheck';
|
||||||
|
import { tempLocalStorage } from '../utilities/checks/tempLocalStorage';
|
||||||
import {
|
import {
|
||||||
decryptAssymmetric,
|
decryptAssymmetric,
|
||||||
encryptAssymmetric,
|
encryptAssymmetric
|
||||||
} from "../utilities/cryptography/crypto";
|
} from '../utilities/cryptography/crypto';
|
||||||
import Button from "./buttons/Button";
|
import Button from './buttons/Button';
|
||||||
import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog";
|
import AddWorkspaceDialog from './dialog/AddWorkspaceDialog';
|
||||||
import Listbox from "./Listbox";
|
import Listbox from './Listbox';
|
||||||
|
|
||||||
interface LayoutProps {
|
interface LayoutProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -41,16 +42,13 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [workspaceList, setWorkspaceList] = useState([]);
|
const [workspaceList, setWorkspaceList] = useState([]);
|
||||||
const [workspaceMapping, setWorkspaceMapping] = useState([{ 1: 2 }]);
|
const [workspaceMapping, setWorkspaceMapping] = useState([{ 1: 2 }]);
|
||||||
const [workspaceSelected, setWorkspaceSelected] = useState("∞");
|
const [workspaceSelected, setWorkspaceSelected] = useState('∞');
|
||||||
const [newWorkspaceName, setNewWorkspaceName] = useState("");
|
const [newWorkspaceName, setNewWorkspaceName] = useState('');
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
const [hasUserClickedSlack, setHasUserClickedSlack] = useState(false);
|
const [totalOnboardingActionsDone, setTotalOnboardingActionsDone] =
|
||||||
const [hasUserClickedIntro, setHasUserClickedIntro] = useState(false);
|
useState(0);
|
||||||
const [hasUserStarred, setHasUserStarred] = useState(false);
|
|
||||||
const [usersInOrg, setUsersInOrg] = useState(false);
|
|
||||||
const [totalOnboardingActionsDone, setTotalOnboardingActionsDone] = useState(0);
|
|
||||||
|
|
||||||
function closeModal() {
|
function closeModal() {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
@@ -77,35 +75,35 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
if (!currentWorkspaces.includes(workspaceName)) {
|
if (!currentWorkspaces.includes(workspaceName)) {
|
||||||
const newWorkspace = await createWorkspace({
|
const newWorkspace = await createWorkspace({
|
||||||
workspaceName,
|
workspaceName,
|
||||||
organizationId: tempLocalStorage("orgData.id"),
|
organizationId: tempLocalStorage('orgData.id')
|
||||||
});
|
});
|
||||||
const newWorkspaceId = newWorkspace._id;
|
const newWorkspaceId = newWorkspace._id;
|
||||||
|
|
||||||
if (addAllUsers) {
|
if (addAllUsers) {
|
||||||
const orgUsers = await getOrganizationUsers({
|
const orgUsers = await getOrganizationUsers({
|
||||||
orgId: tempLocalStorage("orgData.id"),
|
orgId: tempLocalStorage('orgData.id')
|
||||||
});
|
});
|
||||||
orgUsers.map(async (user: any) => {
|
orgUsers.map(async (user: any) => {
|
||||||
if (user.status == "accepted") {
|
if (user.status == 'accepted') {
|
||||||
const result = await addUserToWorkspace(
|
const result = await addUserToWorkspace(
|
||||||
user.user.email,
|
user.user.email,
|
||||||
newWorkspaceId
|
newWorkspaceId
|
||||||
);
|
);
|
||||||
if (result?.invitee && result?.latestKey) {
|
if (result?.invitee && result?.latestKey) {
|
||||||
const PRIVATE_KEY = tempLocalStorage("PRIVATE_KEY");
|
const PRIVATE_KEY = tempLocalStorage('PRIVATE_KEY');
|
||||||
|
|
||||||
// assymmetrically decrypt symmetric key with local private key
|
// assymmetrically decrypt symmetric key with local private key
|
||||||
const key = decryptAssymmetric({
|
const key = decryptAssymmetric({
|
||||||
ciphertext: result.latestKey.encryptedKey,
|
ciphertext: result.latestKey.encryptedKey,
|
||||||
nonce: result.latestKey.nonce,
|
nonce: result.latestKey.nonce,
|
||||||
publicKey: result.latestKey.sender.publicKey,
|
publicKey: result.latestKey.sender.publicKey,
|
||||||
privateKey: PRIVATE_KEY,
|
privateKey: PRIVATE_KEY
|
||||||
});
|
});
|
||||||
|
|
||||||
const { ciphertext, nonce } = encryptAssymmetric({
|
const { ciphertext, nonce } = encryptAssymmetric({
|
||||||
plaintext: key,
|
plaintext: key,
|
||||||
publicKey: result.invitee.publicKey,
|
publicKey: result.invitee.publicKey,
|
||||||
privateKey: PRIVATE_KEY,
|
privateKey: PRIVATE_KEY
|
||||||
}) as { ciphertext: string; nonce: string };
|
}) as { ciphertext: string; nonce: string };
|
||||||
|
|
||||||
uploadKeys(
|
uploadKeys(
|
||||||
@@ -118,11 +116,11 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
router.push("/dashboard/" + newWorkspaceId + "?Development");
|
router.push('/dashboard/' + newWorkspaceId + '?Development');
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
setNewWorkspaceName("");
|
setNewWorkspaceName('');
|
||||||
} else {
|
} else {
|
||||||
console.error("A project with this name already exists.");
|
console.error('A project with this name already exists.');
|
||||||
setError(true);
|
setError(true);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -136,59 +134,59 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
const menuItems = [
|
const menuItems = [
|
||||||
{
|
{
|
||||||
href:
|
href:
|
||||||
"/dashboard/" +
|
'/dashboard/' +
|
||||||
workspaceMapping[workspaceSelected as any] +
|
workspaceMapping[workspaceSelected as any] +
|
||||||
"?Development",
|
'?Development',
|
||||||
title: "Secrets",
|
title: 'Secrets',
|
||||||
emoji: <FontAwesomeIcon icon={faKey} />,
|
emoji: <FontAwesomeIcon icon={faKey} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "/users/" + workspaceMapping[workspaceSelected as any],
|
href: '/users/' + workspaceMapping[workspaceSelected as any],
|
||||||
title: "Members",
|
title: 'Members',
|
||||||
emoji: <FontAwesomeIcon icon={faUser} />,
|
emoji: <FontAwesomeIcon icon={faUser} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "/integrations/" + workspaceMapping[workspaceSelected as any],
|
href: '/integrations/' + workspaceMapping[workspaceSelected as any],
|
||||||
title: "Integrations",
|
title: 'Integrations',
|
||||||
emoji: <FontAwesomeIcon icon={faPlug} />,
|
emoji: <FontAwesomeIcon icon={faPlug} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "/settings/project/" + workspaceMapping[workspaceSelected as any],
|
href: '/settings/project/' + workspaceMapping[workspaceSelected as any],
|
||||||
title: "Project Settings",
|
title: 'Project Settings',
|
||||||
emoji: <FontAwesomeIcon icon={faGear} />,
|
emoji: <FontAwesomeIcon icon={faGear} />
|
||||||
},
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Put a user in a workspace if they're not in one yet
|
// Put a user in a workspace if they're not in one yet
|
||||||
const putUserInWorkSpace = async () => {
|
const putUserInWorkSpace = async () => {
|
||||||
if (tempLocalStorage("orgData.id") === "") {
|
if (tempLocalStorage('orgData.id') === '') {
|
||||||
const userOrgs = await getOrganizations();
|
const userOrgs = await getOrganizations();
|
||||||
localStorage.setItem("orgData.id", userOrgs[0]._id);
|
localStorage.setItem('orgData.id', userOrgs[0]._id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const orgUserProjects = await getOrganizationUserProjects({
|
const orgUserProjects = await getOrganizationUserProjects({
|
||||||
orgId: tempLocalStorage("orgData.id"),
|
orgId: tempLocalStorage('orgData.id')
|
||||||
});
|
});
|
||||||
const userWorkspaces = orgUserProjects;
|
const userWorkspaces = orgUserProjects;
|
||||||
if (
|
if (
|
||||||
userWorkspaces.length == 0 &&
|
userWorkspaces.length == 0 &&
|
||||||
router.asPath != "/noprojects" &&
|
router.asPath != '/noprojects' &&
|
||||||
!router.asPath.includes("settings")
|
!router.asPath.includes('settings')
|
||||||
) {
|
) {
|
||||||
router.push("/noprojects");
|
router.push('/noprojects');
|
||||||
} else if (router.asPath != "/noprojects") {
|
} else if (router.asPath != '/noprojects') {
|
||||||
const intendedWorkspaceId = router.asPath
|
const intendedWorkspaceId = router.asPath
|
||||||
.split("/")
|
.split('/')
|
||||||
[router.asPath.split("/").length - 1].split("?")[0];
|
[router.asPath.split('/').length - 1].split('?')[0];
|
||||||
// If a user is not a member of a workspace they are trying to access, just push them to one of theirs
|
// If a user is not a member of a workspace they are trying to access, just push them to one of theirs
|
||||||
if (
|
if (
|
||||||
intendedWorkspaceId != "heroku" &&
|
intendedWorkspaceId != 'heroku' &&
|
||||||
!userWorkspaces
|
!userWorkspaces
|
||||||
.map((workspace: { _id: string }) => workspace._id)
|
.map((workspace: { _id: string }) => workspace._id)
|
||||||
.includes(intendedWorkspaceId)
|
.includes(intendedWorkspaceId)
|
||||||
) {
|
) {
|
||||||
router.push("/dashboard/" + userWorkspaces[0]._id + "?Development");
|
router.push('/dashboard/' + userWorkspaces[0]._id + '?Development');
|
||||||
} else {
|
} else {
|
||||||
setWorkspaceList(
|
setWorkspaceList(
|
||||||
userWorkspaces.map((workspace: any) => workspace.name)
|
userWorkspaces.map((workspace: any) => workspace.name)
|
||||||
@@ -197,7 +195,7 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
userWorkspaces.map((workspace: any) => [
|
userWorkspaces.map((workspace: any) => [
|
||||||
workspace.name,
|
workspace.name,
|
||||||
workspace._id,
|
workspace._id
|
||||||
])
|
])
|
||||||
) as any
|
) as any
|
||||||
);
|
);
|
||||||
@@ -205,58 +203,19 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
userWorkspaces.map((workspace: any) => [
|
userWorkspaces.map((workspace: any) => [
|
||||||
workspace._id,
|
workspace._id,
|
||||||
workspace.name,
|
workspace.name
|
||||||
])
|
])
|
||||||
)[
|
)[
|
||||||
router.asPath
|
router.asPath
|
||||||
.split("/")
|
.split('/')
|
||||||
[router.asPath.split("/").length - 1].split("?")[0]
|
[router.asPath.split('/').length - 1].split('?')[0]
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
putUserInWorkSpace();
|
putUserInWorkSpace();
|
||||||
|
onboardingCheck({ setTotalOnboardingActionsDone });
|
||||||
const checkUserActionsFunction = async () => {
|
|
||||||
let countActions = 0;
|
|
||||||
const userActionSlack = await checkUserAction({
|
|
||||||
action: "slack_cta_clicked",
|
|
||||||
});
|
|
||||||
setHasUserClickedSlack(userActionSlack ? true : false);
|
|
||||||
if (userActionSlack) {
|
|
||||||
countActions = countActions + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const userActionIntro = await checkUserAction({
|
|
||||||
action: "intro_cta_clicked",
|
|
||||||
});
|
|
||||||
setHasUserClickedIntro(userActionIntro ? true : false);
|
|
||||||
if (userActionIntro) {
|
|
||||||
countActions = countActions + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const userActionStar = await checkUserAction({
|
|
||||||
action: "star_cta_clicked",
|
|
||||||
});
|
|
||||||
setHasUserStarred(userActionStar ? true : false);
|
|
||||||
if (userActionStar) {
|
|
||||||
countActions = countActions + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const orgId = localStorage.getItem("orgData.id");
|
|
||||||
const orgUsers = await getOrganizationUsers({
|
|
||||||
orgId: orgId ? orgId : "",
|
|
||||||
});
|
|
||||||
setUsersInOrg(orgUsers.length > 1)
|
|
||||||
if (orgUsers.length > 1) {
|
|
||||||
countActions = countActions + 1;
|
|
||||||
}
|
|
||||||
console.log(123, countActions)
|
|
||||||
setTotalOnboardingActionsDone(countActions);
|
|
||||||
};
|
|
||||||
console.log(`images/progress-${totalOnboardingActionsDone == 0 ? "0" : ""}${totalOnboardingActionsDone == 1 ? "14" : ""}${totalOnboardingActionsDone == 1 ? "28" : ""}${totalOnboardingActionsDone == 3 ? "43" : ""}${totalOnboardingActionsDone == 4 ? "57" : ""}.svg`)
|
|
||||||
checkUserActionsFunction();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -265,16 +224,16 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
workspaceMapping[Number(workspaceSelected)] &&
|
workspaceMapping[Number(workspaceSelected)] &&
|
||||||
`${workspaceMapping[Number(workspaceSelected)]}` !==
|
`${workspaceMapping[Number(workspaceSelected)]}` !==
|
||||||
router.asPath
|
router.asPath
|
||||||
.split("/")
|
.split('/')
|
||||||
[router.asPath.split("/").length - 1].split("?")[0]
|
[router.asPath.split('/').length - 1].split('?')[0]
|
||||||
) {
|
) {
|
||||||
router.push(
|
router.push(
|
||||||
"/dashboard/" +
|
'/dashboard/' +
|
||||||
workspaceMapping[Number(workspaceSelected)] +
|
workspaceMapping[Number(workspaceSelected)] +
|
||||||
"?Development"
|
'?Development'
|
||||||
);
|
);
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"projectData.id",
|
'projectData.id',
|
||||||
`${workspaceMapping[Number(workspaceSelected)]}`
|
`${workspaceMapping[Number(workspaceSelected)]}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -286,7 +245,10 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="fixed w-full hidden md:block flex flex-col h-screen">
|
<div className="fixed w-full hidden md:block flex flex-col h-screen">
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.2.2/cdn.js" defer></script>
|
<script
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.2.2/cdn.js"
|
||||||
|
defer
|
||||||
|
></script>
|
||||||
<NavBarDashboard />
|
<NavBarDashboard />
|
||||||
<div className="flex flex-col md:flex-row flex-1">
|
<div className="flex flex-col md:flex-row flex-1">
|
||||||
<aside className="bg-bunker-600 border-r border-mineshaft-500 w-full md:w-60 h-screen">
|
<aside className="bg-bunker-600 border-r border-mineshaft-500 w-full md:w-60 h-screen">
|
||||||
@@ -320,11 +282,11 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
{workspaceList.length > 0 &&
|
{workspaceList.length > 0 &&
|
||||||
menuItems.map(({ href, title, emoji }) => (
|
menuItems.map(({ href, title, emoji }) => (
|
||||||
<li className="mt-0.5 mx-2" key={title}>
|
<li className="mt-0.5 mx-2" key={title}>
|
||||||
{router.asPath.split("/")[1] === href.split("/")[1] &&
|
{router.asPath.split('/')[1] === href.split('/')[1] &&
|
||||||
(["project", "billing", "org", "personal"].includes(
|
(['project', 'billing', 'org', 'personal'].includes(
|
||||||
router.asPath.split("/")[2]
|
router.asPath.split('/')[2]
|
||||||
)
|
)
|
||||||
? router.asPath.split("/")[2] === href.split("/")[2]
|
? router.asPath.split('/')[2] === href.split('/')[2]
|
||||||
: true) ? (
|
: true) ? (
|
||||||
<div
|
<div
|
||||||
className={`flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10`}
|
className={`flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10`}
|
||||||
@@ -335,7 +297,7 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
</p>
|
</p>
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
) : router.asPath == "/noprojects" ? (
|
) : router.asPath == '/noprojects' ? (
|
||||||
<div
|
<div
|
||||||
className={`flex p-2.5 text-white text-sm rounded`}
|
className={`flex p-2.5 text-white text-sm rounded`}
|
||||||
>
|
>
|
||||||
@@ -361,7 +323,7 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full mt-40 mb-4 px-2">
|
<div className="w-full mt-40 mb-4 px-2">
|
||||||
{router.asPath.split("/")[1] === "home" ? (
|
{router.asPath.split('/')[1] === 'home' ? (
|
||||||
<div
|
<div
|
||||||
className={`flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10`}
|
className={`flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10`}
|
||||||
>
|
>
|
||||||
@@ -371,7 +333,13 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
</p>
|
</p>
|
||||||
Infisical Guide
|
Infisical Guide
|
||||||
<img
|
<img
|
||||||
src={`/images/progress-${totalOnboardingActionsDone == 0 ? "0" : ""}${totalOnboardingActionsDone == 1 ? "14" : ""}${totalOnboardingActionsDone == 1 ? "28" : ""}${totalOnboardingActionsDone == 3 ? "43" : ""}${totalOnboardingActionsDone == 4 ? "57" : ""}.svg`}
|
src={`/images/progress-${
|
||||||
|
totalOnboardingActionsDone == 0 ? '0' : ''
|
||||||
|
}${totalOnboardingActionsDone == 1 ? '14' : ''}${
|
||||||
|
totalOnboardingActionsDone == 2 ? '28' : ''
|
||||||
|
}${totalOnboardingActionsDone == 3 ? '43' : ''}${
|
||||||
|
totalOnboardingActionsDone == 4 ? '57' : ''
|
||||||
|
}${totalOnboardingActionsDone == 5 ? '71' : ''}.svg`}
|
||||||
height={58}
|
height={58}
|
||||||
width={58}
|
width={58}
|
||||||
alt="progress bar"
|
alt="progress bar"
|
||||||
@@ -390,7 +358,13 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
</p>
|
</p>
|
||||||
Infisical Guide
|
Infisical Guide
|
||||||
<img
|
<img
|
||||||
src="/images/progress-75.svg"
|
src={`/images/progress-${
|
||||||
|
totalOnboardingActionsDone == 0 ? '0' : ''
|
||||||
|
}${totalOnboardingActionsDone == 1 ? '14' : ''}${
|
||||||
|
totalOnboardingActionsDone == 2 ? '28' : ''
|
||||||
|
}${totalOnboardingActionsDone == 3 ? '43' : ''}${
|
||||||
|
totalOnboardingActionsDone == 4 ? '57' : ''
|
||||||
|
}${totalOnboardingActionsDone == 5 ? '71' : ''}.svg`}
|
||||||
height={58}
|
height={58}
|
||||||
width={58}
|
width={58}
|
||||||
alt="progress bar"
|
alt="progress bar"
|
||||||
@@ -420,9 +394,9 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
className="text-gray-300 text-7xl mb-8"
|
className="text-gray-300 text-7xl mb-8"
|
||||||
/>
|
/>
|
||||||
<p className="text-gray-200 px-6 text-center text-lg max-w-sm">
|
<p className="text-gray-200 px-6 text-center text-lg max-w-sm">
|
||||||
{" "}
|
{' '}
|
||||||
To use Infisical, please log in through a device with larger
|
To use Infisical, please log in through a device with larger
|
||||||
dimensions.{" "}
|
dimensions.{' '}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
import { Fragment, useState } from "react";
|
import { Fragment, useState } from 'react';
|
||||||
import { useRouter } from "next/router";
|
import { faCheck, faCopy } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons";
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { Dialog, Transition } from '@headlessui/react';
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import nacl from 'tweetnacl';
|
||||||
import nacl from "tweetnacl";
|
|
||||||
|
|
||||||
import addServiceToken from "~/pages/api/serviceToken/addServiceToken";
|
import addServiceToken from '~/pages/api/serviceToken/addServiceToken';
|
||||||
import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";
|
import getLatestFileKey from '~/pages/api/workspace/getLatestFileKey';
|
||||||
|
|
||||||
import { envMapping } from "../../../public/data/frequentConstants";
|
import { envMapping } from '../../../public/data/frequentConstants';
|
||||||
import {
|
import {
|
||||||
decryptAssymmetric,
|
decryptAssymmetric,
|
||||||
encryptAssymmetric,
|
encryptAssymmetric
|
||||||
} from "../../utilities/cryptography/crypto";
|
} from '../../utilities/cryptography/crypto';
|
||||||
import Button from "../buttons/Button";
|
import Button from '../buttons/Button';
|
||||||
import InputField from "../InputField";
|
import InputField from '../InputField';
|
||||||
import ListBox from "../Listbox";
|
import ListBox from '../Listbox';
|
||||||
|
|
||||||
const expiryMapping = {
|
const expiryMapping = {
|
||||||
"1 day": 86400,
|
'1 day': 86400,
|
||||||
"7 days": 604800,
|
'7 days': 604800,
|
||||||
"1 month": 2592000,
|
'1 month': 2592000,
|
||||||
|
'6 months': 15552000,
|
||||||
|
'12 months': 31104000
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddServiceTokenDialog = ({
|
const AddServiceTokenDialog = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
closeModal,
|
closeModal,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
workspaceName,
|
workspaceName
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const [serviceToken, setServiceToken] = useState('');
|
||||||
const [serviceToken, setServiceToken] = useState("");
|
const [serviceTokenName, setServiceTokenName] = useState('');
|
||||||
const [serviceTokenName, setServiceTokenName] = useState("");
|
const [serviceTokenEnv, setServiceTokenEnv] = useState('Development');
|
||||||
const [serviceTokenEnv, setServiceTokenEnv] = useState("Development");
|
const [serviceTokenExpiresIn, setServiceTokenExpiresIn] = useState('1 day');
|
||||||
const [serviceTokenExpiresIn, setServiceTokenExpiresIn] = useState("1 day");
|
|
||||||
const [serviceTokenCopied, setServiceTokenCopied] = useState(false);
|
const [serviceTokenCopied, setServiceTokenCopied] = useState(false);
|
||||||
|
|
||||||
const generateServiceToken = async () => {
|
const generateServiceToken = async () => {
|
||||||
@@ -43,7 +43,7 @@ const AddServiceTokenDialog = ({
|
|||||||
ciphertext: latestFileKey.latestKey.encryptedKey,
|
ciphertext: latestFileKey.latestKey.encryptedKey,
|
||||||
nonce: latestFileKey.latestKey.nonce,
|
nonce: latestFileKey.latestKey.nonce,
|
||||||
publicKey: latestFileKey.latestKey.sender.publicKey,
|
publicKey: latestFileKey.latestKey.sender.publicKey,
|
||||||
privateKey: localStorage.getItem("PRIVATE_KEY"),
|
privateKey: localStorage.getItem('PRIVATE_KEY')
|
||||||
});
|
});
|
||||||
|
|
||||||
// generate new public/private key pair
|
// generate new public/private key pair
|
||||||
@@ -55,7 +55,7 @@ const AddServiceTokenDialog = ({
|
|||||||
const { ciphertext: encryptedKey, nonce } = encryptAssymmetric({
|
const { ciphertext: encryptedKey, nonce } = encryptAssymmetric({
|
||||||
plaintext: key,
|
plaintext: key,
|
||||||
publicKey,
|
publicKey,
|
||||||
privateKey,
|
privateKey
|
||||||
});
|
});
|
||||||
|
|
||||||
let newServiceToken = await addServiceToken({
|
let newServiceToken = await addServiceToken({
|
||||||
@@ -65,16 +65,16 @@ const AddServiceTokenDialog = ({
|
|||||||
expiresIn: expiryMapping[serviceTokenExpiresIn],
|
expiresIn: expiryMapping[serviceTokenExpiresIn],
|
||||||
publicKey,
|
publicKey,
|
||||||
encryptedKey,
|
encryptedKey,
|
||||||
nonce,
|
nonce
|
||||||
});
|
});
|
||||||
|
|
||||||
const serviceToken = newServiceToken + "," + privateKey;
|
const serviceToken = newServiceToken + ',' + privateKey;
|
||||||
setServiceToken(serviceToken);
|
setServiceToken(serviceToken);
|
||||||
};
|
};
|
||||||
|
|
||||||
function copyToClipboard() {
|
function copyToClipboard() {
|
||||||
// Get the text field
|
// Get the text field
|
||||||
var copyText = document.getElementById("serviceToken");
|
var copyText = document.getElementById('serviceToken');
|
||||||
|
|
||||||
// Select the text field
|
// Select the text field
|
||||||
copyText.select();
|
copyText.select();
|
||||||
@@ -91,8 +91,8 @@ const AddServiceTokenDialog = ({
|
|||||||
|
|
||||||
const closeAddServiceTokenModal = () => {
|
const closeAddServiceTokenModal = () => {
|
||||||
closeModal();
|
closeModal();
|
||||||
setServiceTokenName("");
|
setServiceTokenName('');
|
||||||
setServiceToken("");
|
setServiceToken('');
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -122,7 +122,7 @@ const AddServiceTokenDialog = ({
|
|||||||
leaveFrom="opacity-100 scale-100"
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo="opacity-0 scale-95"
|
leaveTo="opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
{serviceToken == "" ? (
|
{serviceToken == '' ? (
|
||||||
<Dialog.Panel className="w-full max-w-md transform rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
|
<Dialog.Panel className="w-full max-w-md transform rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
|
||||||
<Dialog.Title
|
<Dialog.Title
|
||||||
as="h3"
|
as="h3"
|
||||||
@@ -155,12 +155,12 @@ const AddServiceTokenDialog = ({
|
|||||||
selected={serviceTokenEnv}
|
selected={serviceTokenEnv}
|
||||||
onChange={setServiceTokenEnv}
|
onChange={setServiceTokenEnv}
|
||||||
data={[
|
data={[
|
||||||
"Development",
|
'Development',
|
||||||
"Staging",
|
'Staging',
|
||||||
"Production",
|
'Production',
|
||||||
"Testing",
|
'Testing'
|
||||||
]}
|
]}
|
||||||
width="full"
|
isFull={true}
|
||||||
text="Environment: "
|
text="Environment: "
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -168,8 +168,14 @@ const AddServiceTokenDialog = ({
|
|||||||
<ListBox
|
<ListBox
|
||||||
selected={serviceTokenExpiresIn}
|
selected={serviceTokenExpiresIn}
|
||||||
onChange={setServiceTokenExpiresIn}
|
onChange={setServiceTokenExpiresIn}
|
||||||
data={["1 day", "7 days", "1 month"]}
|
data={[
|
||||||
width="full"
|
'1 day',
|
||||||
|
'7 days',
|
||||||
|
'1 month',
|
||||||
|
'6 months',
|
||||||
|
'12 months'
|
||||||
|
]}
|
||||||
|
isFull={true}
|
||||||
text="Expires in: "
|
text="Expires in: "
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -181,7 +187,7 @@ const AddServiceTokenDialog = ({
|
|||||||
text="Add Service Token"
|
text="Add Service Token"
|
||||||
textDisabled="Add Service Token"
|
textDisabled="Add Service Token"
|
||||||
size="md"
|
size="md"
|
||||||
active={serviceTokenName == "" ? false : true}
|
active={serviceTokenName == '' ? false : true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from 'next/router';
|
||||||
import { faX } from "@fortawesome/free-solid-svg-icons";
|
import { faX } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
import { reverseEnvMapping } from "../../../public/data/frequentConstants";
|
import { reverseEnvMapping } from '../../../public/data/frequentConstants';
|
||||||
import guidGenerator from "../../utilities/randomId";
|
import guidGenerator from '../../utilities/randomId';
|
||||||
import Button from "../buttons/Button";
|
import Button from '../buttons/Button';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the component that we utilize for the user table - in future, can reuse it for some other purposes too.
|
* This is the component that we utilize for the user table - in future, can reuse it for some other purposes too.
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from 'next/router';
|
||||||
import { faX } from "@fortawesome/free-solid-svg-icons";
|
import { faX } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
import deleteUserFromOrganization from "~/pages/api/organization/deleteUserFromOrganization";
|
import deleteUserFromOrganization from '~/pages/api/organization/deleteUserFromOrganization';
|
||||||
import changeUserRoleInWorkspace from "~/pages/api/workspace/changeUserRoleInWorkspace";
|
import changeUserRoleInWorkspace from '~/pages/api/workspace/changeUserRoleInWorkspace';
|
||||||
import deleteUserFromWorkspace from "~/pages/api/workspace/deleteUserFromWorkspace";
|
import deleteUserFromWorkspace from '~/pages/api/workspace/deleteUserFromWorkspace';
|
||||||
import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";
|
import getLatestFileKey from '~/pages/api/workspace/getLatestFileKey';
|
||||||
import uploadKeys from "~/pages/api/workspace/uploadKeys";
|
import uploadKeys from '~/pages/api/workspace/uploadKeys';
|
||||||
|
|
||||||
import guidGenerator from "../../utilities/randomId";
|
import guidGenerator from '../../utilities/randomId';
|
||||||
import Button from "../buttons/Button";
|
import Button from '../buttons/Button';
|
||||||
import Listbox from "../Listbox";
|
import Listbox from '../Listbox';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
decryptAssymmetric,
|
decryptAssymmetric,
|
||||||
encryptAssymmetric,
|
encryptAssymmetric
|
||||||
} = require("../../utilities/cryptography/crypto");
|
} = require('../../utilities/cryptography/crypto');
|
||||||
const nacl = require("tweetnacl");
|
const nacl = require('tweetnacl');
|
||||||
nacl.util = require("tweetnacl-util");
|
nacl.util = require('tweetnacl-util');
|
||||||
|
|
||||||
const roles = ["admin", "user"];
|
const roles = ['admin', 'user'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the component that we utilize for the user table - in future, can reuse it for some other purposes too.
|
* This is the component that we utilize for the user table - in future, can reuse it for some other purposes too.
|
||||||
@@ -36,13 +36,13 @@ const UserTable = ({
|
|||||||
isOrg,
|
isOrg,
|
||||||
onClick,
|
onClick,
|
||||||
deleteUser,
|
deleteUser,
|
||||||
setUserIdToBeDeleted,
|
setUserIdToBeDeleted
|
||||||
}) => {
|
}) => {
|
||||||
const [roleSelected, setRoleSelected] = useState(
|
const [roleSelected, setRoleSelected] = useState(
|
||||||
Array(userData?.length).fill(userData.map((user) => user.role))
|
Array(userData?.length).fill(userData.map((user) => user.role))
|
||||||
);
|
);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [myRole, setMyRole] = useState("member");
|
const [myRole, setMyRole] = useState('member');
|
||||||
|
|
||||||
// Delete the row in the table (e.g. a user)
|
// Delete the row in the table (e.g. a user)
|
||||||
// #TODO: Add a pop-up that warns you that the user is going to be deleted.
|
// #TODO: Add a pop-up that warns you that the user is going to be deleted.
|
||||||
@@ -57,7 +57,7 @@ const UserTable = ({
|
|||||||
changeData(userData.filter((v, i) => i !== index));
|
changeData(userData.filter((v, i) => i !== index));
|
||||||
setRoleSelected([
|
setRoleSelected([
|
||||||
...roleSelected.slice(0, index),
|
...roleSelected.slice(0, index),
|
||||||
...roleSelected.slice(index + 1, userData?.length),
|
...roleSelected.slice(index + 1, userData?.length)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -76,10 +76,10 @@ const UserTable = ({
|
|||||||
status: userData[index].status,
|
status: userData[index].status,
|
||||||
userId: userData[index].userId,
|
userId: userData[index].userId,
|
||||||
membershipId: userData[index].membershipId,
|
membershipId: userData[index].membershipId,
|
||||||
publicKey: userData[index].publicKey,
|
publicKey: userData[index].publicKey
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
...userData.slice(index + 1, userData?.length),
|
...userData.slice(index + 1, userData?.length)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,20 +90,20 @@ const UserTable = ({
|
|||||||
const grantAccess = async (id, publicKey) => {
|
const grantAccess = async (id, publicKey) => {
|
||||||
let result = await getLatestFileKey({ workspaceId: router.query.id });
|
let result = await getLatestFileKey({ workspaceId: router.query.id });
|
||||||
|
|
||||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
|
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY');
|
||||||
|
|
||||||
// assymmetrically decrypt symmetric key with local private key
|
// assymmetrically decrypt symmetric key with local private key
|
||||||
const key = decryptAssymmetric({
|
const key = decryptAssymmetric({
|
||||||
ciphertext: result.latestKey.encryptedKey,
|
ciphertext: result.latestKey.encryptedKey,
|
||||||
nonce: result.latestKey.nonce,
|
nonce: result.latestKey.nonce,
|
||||||
publicKey: result.latestKey.sender.publicKey,
|
publicKey: result.latestKey.sender.publicKey,
|
||||||
privateKey: PRIVATE_KEY,
|
privateKey: PRIVATE_KEY
|
||||||
});
|
});
|
||||||
|
|
||||||
const { ciphertext, nonce } = encryptAssymmetric({
|
const { ciphertext, nonce } = encryptAssymmetric({
|
||||||
plaintext: key,
|
plaintext: key,
|
||||||
publicKey: publicKey,
|
publicKey: publicKey,
|
||||||
privateKey: PRIVATE_KEY,
|
privateKey: PRIVATE_KEY
|
||||||
});
|
});
|
||||||
|
|
||||||
uploadKeys(router.query.id, id, ciphertext, nonce);
|
uploadKeys(router.query.id, id, ciphertext, nonce);
|
||||||
@@ -158,24 +158,24 @@ const UserTable = ({
|
|||||||
</td>
|
</td>
|
||||||
<td className="flex flex-row justify-end pr-8 py-2 border-t border-0.5 border-mineshaft-700">
|
<td className="flex flex-row justify-end pr-8 py-2 border-t border-0.5 border-mineshaft-700">
|
||||||
<div className="flex justify-end mr-6 w-3/4 mx-2 w-full h-full flex flex-row items-center">
|
<div className="flex justify-end mr-6 w-3/4 mx-2 w-full h-full flex flex-row items-center">
|
||||||
{row.status == "granted" &&
|
{row.status == 'granted' &&
|
||||||
((myRole == "admin" && row.role != "owner") ||
|
((myRole == 'admin' && row.role != 'owner') ||
|
||||||
myRole == "owner") &&
|
myRole == 'owner') &&
|
||||||
myUser !== row.email ? (
|
myUser !== row.email ? (
|
||||||
<Listbox
|
<Listbox
|
||||||
selected={row.role}
|
selected={row.role}
|
||||||
onChange={(e) => handleRoleUpdate(index, e)}
|
onChange={(e) => handleRoleUpdate(index, e)}
|
||||||
data={
|
data={
|
||||||
myRole == "owner"
|
myRole == 'owner'
|
||||||
? ["owner", "admin", "member"]
|
? ['owner', 'admin', 'member']
|
||||||
: ["admin", "member"]
|
: ['admin', 'member']
|
||||||
}
|
}
|
||||||
text="Role: "
|
text="Role: "
|
||||||
membershipId={row.membershipId}
|
membershipId={row.membershipId}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
row.status != "invited" &&
|
row.status != 'invited' &&
|
||||||
row.status != "verified" && (
|
row.status != 'verified' && (
|
||||||
<Listbox
|
<Listbox
|
||||||
selected={row.role}
|
selected={row.role}
|
||||||
text="Role: "
|
text="Role: "
|
||||||
@@ -183,8 +183,8 @@ const UserTable = ({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
{(row.status == "invited" ||
|
{(row.status == 'invited' ||
|
||||||
row.status == "verified") && (
|
row.status == 'verified') && (
|
||||||
<div className="w-full pl-9">
|
<div className="w-full pl-9">
|
||||||
<Button
|
<Button
|
||||||
onButtonPressed={() =>
|
onButtonPressed={() =>
|
||||||
@@ -199,7 +199,7 @@ const UserTable = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{row.status == "completed" && myUser !== row.email && (
|
{row.status == 'completed' && myUser !== row.email && (
|
||||||
<div className="border border-mineshaft-700 rounded-md bg-white/5 hover:bg-primary text-white hover:text-black duration-200">
|
<div className="border border-mineshaft-700 rounded-md bg-white/5 hover:bg-primary text-white hover:text-black duration-200">
|
||||||
<Button
|
<Button
|
||||||
onButtonPressed={() =>
|
onButtonPressed={() =>
|
||||||
@@ -214,7 +214,7 @@ const UserTable = ({
|
|||||||
</div>
|
</div>
|
||||||
{myUser !== row.email &&
|
{myUser !== row.email &&
|
||||||
// row.role != "admin" &&
|
// row.role != "admin" &&
|
||||||
myRole != "member" ? (
|
myRole != 'member' ? (
|
||||||
<div className="opacity-50 hover:opacity-100 flex items-center">
|
<div className="opacity-50 hover:opacity-100 flex items-center">
|
||||||
<Button
|
<Button
|
||||||
onButtonPressed={(e) =>
|
onButtonPressed={(e) =>
|
||||||
|
|||||||
@@ -1,14 +1,29 @@
|
|||||||
import React from "react";
|
import React from 'react';
|
||||||
|
|
||||||
import StripeRedirect from "~/pages/api/organization/StripeRedirect";
|
import StripeRedirect from '~/pages/api/organization/StripeRedirect';
|
||||||
|
|
||||||
export default function Plan({ plan }) {
|
import { tempLocalStorage } from '../utilities/checks/tempLocalStorage';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
plan: {
|
||||||
|
name: string;
|
||||||
|
price: string;
|
||||||
|
priceExplanation: string;
|
||||||
|
text: string;
|
||||||
|
subtext: string;
|
||||||
|
buttonTextMain: string;
|
||||||
|
buttonTextSecondary: string;
|
||||||
|
current: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Plan({ plan }: Props) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`relative flex flex-col justify-between border border-2 min-w-fit w-96 rounded-lg h-68 mr-4 bg-mineshaft-800 ${
|
className={`relative flex flex-col justify-between border-2 min-w-fit w-96 rounded-lg h-68 mr-4 bg-mineshaft-800 ${
|
||||||
(plan.name != "Starter") & (plan.current == true)
|
plan.name != 'Starter' && plan.current == true
|
||||||
? "border-primary"
|
? 'border-primary'
|
||||||
: "border-chicago-700"
|
: 'border-chicago-700'
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
@@ -36,7 +51,7 @@ export default function Plan({ plan }) {
|
|||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
{plan.current == false ? (
|
{plan.current == false ? (
|
||||||
<>
|
<>
|
||||||
{plan.buttonTextMain == "Schedule a Demo" ? (
|
{plan.buttonTextMain == 'Schedule a Demo' ? (
|
||||||
<a href="/scheduledemo" target='_blank rel="noopener"'>
|
<a href="/scheduledemo" target='_blank rel="noopener"'>
|
||||||
<div className="relative z-10 mx-5 mt-3 mb-4 py-2 px-4 border border-1 border-gray-600 hover:text-black hover:border-primary text-gray-400 font-semibold hover:bg-primary bg-bunker duration-200 cursor-pointer rounded-md flex w-max">
|
<div className="relative z-10 mx-5 mt-3 mb-4 py-2 px-4 border border-1 border-gray-600 hover:text-black hover:border-primary text-gray-400 font-semibold hover:bg-primary bg-bunker duration-200 cursor-pointer rounded-md flex w-max">
|
||||||
{plan.buttonTextMain}
|
{plan.buttonTextMain}
|
||||||
@@ -45,15 +60,15 @@ export default function Plan({ plan }) {
|
|||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={`relative z-10 mx-5 mt-3 mb-4 py-2 px-4 border border-1 border-gray-600 text-gray-400 font-semibold ${
|
className={`relative z-10 mx-5 mt-3 mb-4 py-2 px-4 border border-1 border-gray-600 text-gray-400 font-semibold ${
|
||||||
plan.buttonTextMain == "Downgrade"
|
plan.buttonTextMain == 'Downgrade'
|
||||||
? "hover:bg-red hover:text-white hover:border-red"
|
? 'hover:bg-red hover:text-white hover:border-red'
|
||||||
: "hover:bg-primary hover:text-black hover:border-primary"
|
: 'hover:bg-primary hover:text-black hover:border-primary'
|
||||||
} bg-bunker duration-200 cursor-pointer rounded-md flex w-max`}
|
} bg-bunker duration-200 cursor-pointer rounded-md flex w-max`}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
StripeRedirect({
|
StripeRedirect({
|
||||||
orgId: localStorage.getItem("orgData.id"),
|
orgId: tempLocalStorage('orgData.id')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -61,7 +76,10 @@ export default function Plan({ plan }) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<a href="https://infisical.com/pricing" target='_blank rel="noopener"'>
|
<a
|
||||||
|
href="https://infisical.com/pricing"
|
||||||
|
target='_blank rel="noopener"'
|
||||||
|
>
|
||||||
<div className="relative z-10 text-gray-400 font-semibold hover:text-primary duration-200 cursor-pointer mb-0.5">
|
<div className="relative z-10 text-gray-400 font-semibold hover:text-primary duration-200 cursor-pointer mb-0.5">
|
||||||
{plan.buttonTextSecondary}
|
{plan.buttonTextSecondary}
|
||||||
</div>
|
</div>
|
||||||
@@ -70,9 +88,9 @@ export default function Plan({ plan }) {
|
|||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={`h-8 w-full rounded-b-md flex justify-center items-center z-10 ${
|
className={`h-8 w-full rounded-b-md flex justify-center items-center z-10 ${
|
||||||
(plan.name != "Starter") & (plan.current == true)
|
plan.name != 'Starter' && plan.current == true
|
||||||
? "bg-primary"
|
? 'bg-primary'
|
||||||
: "bg-chicago-700"
|
: 'bg-chicago-700'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<p className="text-xs text-black font-semibold">CURRENT PLAN</p>
|
<p className="text-xs text-black font-semibold">CURRENT PLAN</p>
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from 'react';
|
||||||
import { faXmarkCircle } from "@fortawesome/free-solid-svg-icons";
|
import { faX } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import classnames from "classnames";
|
|
||||||
|
|
||||||
import { Notification as NotificationType } from "./NotificationProvider";
|
import { Notification as NotificationType } from './NotificationProvider';
|
||||||
|
|
||||||
interface NotificationProps {
|
interface NotificationProps {
|
||||||
notification: Required<NotificationType>;
|
notification: Required<NotificationType>;
|
||||||
@@ -12,7 +11,7 @@ interface NotificationProps {
|
|||||||
|
|
||||||
const Notification = ({
|
const Notification = ({
|
||||||
notification,
|
notification,
|
||||||
clearNotification,
|
clearNotification
|
||||||
}: NotificationProps) => {
|
}: NotificationProps) => {
|
||||||
const timeout = useRef<number>();
|
const timeout = useRef<number>();
|
||||||
|
|
||||||
@@ -37,22 +36,29 @@ const Notification = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames(
|
className="relative w-full flex items-center justify-between px-4 py-6 rounded-md border border-bunker-500 pointer-events-auto bg-bunker-500"
|
||||||
"w-full flex items-center justify-between px-4 py-3 rounded pointer-events-auto",
|
|
||||||
{
|
|
||||||
"bg-green-600": notification.type === "success",
|
|
||||||
"bg-red-500": notification.type === "error",
|
|
||||||
"bg-blue-500": notification.type === "info",
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
role="alert"
|
role="alert"
|
||||||
>
|
>
|
||||||
<p className="text-white text-sm font-bold">{notification.text}</p>
|
{notification.type === 'error' && (
|
||||||
|
<div className="absolute w-full h-1 bg-red top-0 left-0 rounded-t-md"></div>
|
||||||
|
)}
|
||||||
|
{notification.type === 'success' && (
|
||||||
|
<div className="absolute w-full h-1 bg-green top-0 left-0 rounded-t-md"></div>
|
||||||
|
)}
|
||||||
|
{notification.type === 'info' && (
|
||||||
|
<div className="absolute w-full h-1 bg-yellow top-0 left-0 rounded-t-md"></div>
|
||||||
|
)}
|
||||||
|
<p className="text-bunker-200 text-sm font-semibold mt-0.5">
|
||||||
|
{notification.text}
|
||||||
|
</p>
|
||||||
<button
|
<button
|
||||||
className="bg-white/5 rounded-lg p-3"
|
className="rounded-lg"
|
||||||
onClick={() => clearNotification(notification.text)}
|
onClick={() => clearNotification(notification.text)}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon className="text-white" icon={faXmarkCircle} />
|
<FontAwesomeIcon
|
||||||
|
className="text-white w-4 h-3 hover:text-red"
|
||||||
|
icon={faX}
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { createContext, ReactNode, useContext, useState } from "react";
|
import { createContext, ReactNode, useContext, useState } from 'react';
|
||||||
|
|
||||||
import Notifications from "./Notifications";
|
import Notifications from './Notifications';
|
||||||
|
|
||||||
type NotificationType = "success" | "error" | "info";
|
type NotificationType = 'success' | 'error' | 'info';
|
||||||
|
|
||||||
export type Notification = {
|
export type Notification = {
|
||||||
text: string;
|
text: string;
|
||||||
@@ -15,7 +15,7 @@ type NotificationContextState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const NotificationContext = createContext<NotificationContextState>({
|
const NotificationContext = createContext<NotificationContextState>({
|
||||||
createNotification: () => console.log("createNotification not set!"),
|
createNotification: () => console.log('createNotification not set!')
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useNotificationContext = () => useContext(NotificationContext);
|
export const useNotificationContext = () => useContext(NotificationContext);
|
||||||
@@ -37,8 +37,8 @@ const NotificationProvider = ({ children }: NotificationProviderProps) => {
|
|||||||
|
|
||||||
const createNotification = ({
|
const createNotification = ({
|
||||||
text,
|
text,
|
||||||
type = "success",
|
type = 'success',
|
||||||
timeoutMs = 2000,
|
timeoutMs = 5000
|
||||||
}: Notification) => {
|
}: Notification) => {
|
||||||
const doesNotifExist = notifications.some((notif) => notif.text === text);
|
const doesNotifExist = notifications.some((notif) => notif.text === text);
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ const NotificationProvider = ({ children }: NotificationProviderProps) => {
|
|||||||
return (
|
return (
|
||||||
<NotificationContext.Provider
|
<NotificationContext.Provider
|
||||||
value={{
|
value={{
|
||||||
createNotification,
|
createNotification
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Notifications
|
<Notifications
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Notification from "./Notification";
|
import Notification from './Notification';
|
||||||
import { Notification as NotificationType } from "./NotificationProvider";
|
import { Notification as NotificationType } from './NotificationProvider';
|
||||||
|
|
||||||
interface NoticationsProps {
|
interface NoticationsProps {
|
||||||
notifications: Required<NotificationType>[];
|
notifications: Required<NotificationType>[];
|
||||||
@@ -8,14 +8,14 @@ interface NoticationsProps {
|
|||||||
|
|
||||||
const Notifications = ({
|
const Notifications = ({
|
||||||
notifications,
|
notifications,
|
||||||
clearNotification,
|
clearNotification
|
||||||
}: NoticationsProps) => {
|
}: NoticationsProps) => {
|
||||||
if (!notifications.length) {
|
if (!notifications.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="hidden fixed z-50 md:flex md:flex-col-reverse bottom-1 gap-y-2 w-96 h-full right-1 pointer-events-none">
|
<div className="hidden fixed z-50 md:flex md:flex-col-reverse bottom-1 gap-y-2 w-96 h-full right-2 bottom-2 pointer-events-none">
|
||||||
{notifications.map((notif) => (
|
{notifications.map((notif) => (
|
||||||
<Notification
|
<Notification
|
||||||
key={notif.text}
|
key={notif.text}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { SyntheticEvent, useRef } from "react";
|
import React, { SyntheticEvent, useRef } from 'react';
|
||||||
import { faCircle } from "@fortawesome/free-solid-svg-icons";
|
import { faCircle } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
import guidGenerator from "../utilities/randomId";
|
import guidGenerator from '../utilities/randomId';
|
||||||
|
|
||||||
const REGEX = /([$]{.*?})/g;
|
const REGEX = /([$]{.*?})/g;
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ interface DashboardInputFieldProps {
|
|||||||
position: number;
|
position: number;
|
||||||
onChangeHandler: (value: string, position: number) => void;
|
onChangeHandler: (value: string, position: number) => void;
|
||||||
value: string;
|
value: string;
|
||||||
type: "varName" | "value";
|
type: 'varName' | 'value';
|
||||||
blurred: boolean;
|
blurred: boolean;
|
||||||
duplicates: string[];
|
duplicates: string[];
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ const DashboardInputField = ({
|
|||||||
type,
|
type,
|
||||||
value,
|
value,
|
||||||
blurred,
|
blurred,
|
||||||
duplicates,
|
duplicates
|
||||||
}: DashboardInputFieldProps) => {
|
}: DashboardInputFieldProps) => {
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
const syncScroll = (e: SyntheticEvent<HTMLDivElement>) => {
|
const syncScroll = (e: SyntheticEvent<HTMLDivElement>) => {
|
||||||
@@ -43,8 +43,8 @@ const DashboardInputField = ({
|
|||||||
ref.current.scrollLeft = e.currentTarget.scrollLeft;
|
ref.current.scrollLeft = e.currentTarget.scrollLeft;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (type === "varName") {
|
if (type === 'varName') {
|
||||||
const startsWithNumber = !isNaN(Number(value.charAt(0))) && value != "";
|
const startsWithNumber = !isNaN(Number(value.charAt(0))) && value != '';
|
||||||
const hasDuplicates = duplicates?.includes(value);
|
const hasDuplicates = duplicates?.includes(value);
|
||||||
const error = startsWithNumber || hasDuplicates;
|
const error = startsWithNumber || hasDuplicates;
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ const DashboardInputField = ({
|
|||||||
<div className="flex-col w-full">
|
<div className="flex-col w-full">
|
||||||
<div
|
<div
|
||||||
className={`group relative flex flex-col justify-center w-full max-w-2xl border ${
|
className={`group relative flex flex-col justify-center w-full max-w-2xl border ${
|
||||||
error ? "border-red" : "border-mineshaft-500"
|
error ? 'border-red' : 'border-mineshaft-500'
|
||||||
} rounded-md`}
|
} rounded-md`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
@@ -62,7 +62,7 @@ const DashboardInputField = ({
|
|||||||
type={type}
|
type={type}
|
||||||
value={value}
|
value={value}
|
||||||
className={`z-10 peer font-mono ph-no-capture bg-bunker-800 rounded-md caret-white text-gray-400 text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 ${
|
className={`z-10 peer font-mono ph-no-capture bg-bunker-800 rounded-md caret-white text-gray-400 text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 ${
|
||||||
error ? "focus:ring-red/50" : "focus:ring-primary/50"
|
error ? 'focus:ring-red/50' : 'focus:ring-primary/50'
|
||||||
} duration-200`}
|
} duration-200`}
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
/>
|
/>
|
||||||
@@ -79,7 +79,7 @@ const DashboardInputField = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (type === "value") {
|
} else if (type === 'value') {
|
||||||
return (
|
return (
|
||||||
<div className="flex-col w-full">
|
<div className="flex-col w-full">
|
||||||
<div
|
<div
|
||||||
@@ -91,8 +91,8 @@ const DashboardInputField = ({
|
|||||||
onScroll={syncScroll}
|
onScroll={syncScroll}
|
||||||
className={`${
|
className={`${
|
||||||
blurred
|
blurred
|
||||||
? "text-transparent group-hover:text-transparent focus:text-transparent active:text-transparent"
|
? 'text-transparent group-hover:text-transparent focus:text-transparent active:text-transparent'
|
||||||
: ""
|
: ''
|
||||||
} z-10 peer font-mono ph-no-capture bg-transparent rounded-md caret-white text-transparent text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 focus:ring-primary/50 duration-200 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
} z-10 peer font-mono ph-no-capture bg-transparent rounded-md caret-white text-transparent text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 focus:ring-primary/50 duration-200 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
/>
|
/>
|
||||||
@@ -100,8 +100,8 @@ const DashboardInputField = ({
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={`${
|
className={`${
|
||||||
blurred
|
blurred
|
||||||
? "text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-400 peer-active:text-gray-400"
|
? 'text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-400 peer-active:text-gray-400'
|
||||||
: ""
|
: ''
|
||||||
} absolute flex flex-row whitespace-pre font-mono z-0 ph-no-capture max-w-2xl overflow-x-scroll bg-bunker-800 h-9 rounded-md text-gray-400 text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 focus:ring-primary/50 duration-100 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
} absolute flex flex-row whitespace-pre font-mono z-0 ph-no-capture max-w-2xl overflow-x-scroll bg-bunker-800 h-9 rounded-md text-gray-400 text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 focus:ring-primary/50 duration-100 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
||||||
>
|
>
|
||||||
{value.split(REGEX).map((word, id) => {
|
{value.split(REGEX).map((word, id) => {
|
||||||
@@ -112,7 +112,7 @@ const DashboardInputField = ({
|
|||||||
<span className="ph-no-capture text-yellow-200/80">
|
<span className="ph-no-capture text-yellow-200/80">
|
||||||
{word.slice(2, word.length - 1)}
|
{word.slice(2, word.length - 1)}
|
||||||
</span>
|
</span>
|
||||||
{word.slice(word.length - 1, word.length) == "}" ? (
|
{word.slice(word.length - 1, word.length) == '}' ? (
|
||||||
<span className="ph-no-capture text-yellow">
|
<span className="ph-no-capture text-yellow">
|
||||||
{word.slice(word.length - 1, word.length)}
|
{word.slice(word.length - 1, word.length)}
|
||||||
</span>
|
</span>
|
||||||
@@ -135,7 +135,7 @@ const DashboardInputField = ({
|
|||||||
{blurred && (
|
{blurred && (
|
||||||
<div className="absolute flex flex-row items-center z-20 peer pr-2 bg-bunker-800 group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible h-9 w-full max-w-2xl rounded-md text-gray-400/50 text-clip">
|
<div className="absolute flex flex-row items-center z-20 peer pr-2 bg-bunker-800 group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible h-9 w-full max-w-2xl rounded-md text-gray-400/50 text-clip">
|
||||||
<div className="px-2 flex flex-row items-center overflow-x-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
|
<div className="px-2 flex flex-row items-center overflow-x-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
|
||||||
{value.split("").map(() => (
|
{value.split('').map(() => (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
key={guidGenerator()}
|
key={guidGenerator()}
|
||||||
className="text-xxs mx-0.5"
|
className="text-xxs mx-0.5"
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { type ChangeEvent, type DragEvent, useState } from "react";
|
import { type ChangeEvent, type DragEvent, useState } from 'react';
|
||||||
import Image from "next/image";
|
import Image from 'next/image';
|
||||||
import { faUpload } from "@fortawesome/free-solid-svg-icons";
|
import { faUpload } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
import Button from "../basic/buttons/Button";
|
import Button from '../basic/buttons/Button';
|
||||||
import Error from "../basic/Error";
|
import Error from '../basic/Error';
|
||||||
import parse from "../utilities/file";
|
import parse from '../utilities/file';
|
||||||
import guidGenerator from "../utilities/randomId";
|
import guidGenerator from '../utilities/randomId';
|
||||||
|
|
||||||
interface DropZoneProps {
|
interface DropZoneProps {
|
||||||
// TODO: change Data type from any
|
// TODO: change Data type from any
|
||||||
@@ -26,7 +26,7 @@ const DropZone = ({
|
|||||||
errorDragAndDrop,
|
errorDragAndDrop,
|
||||||
setButtonReady,
|
setButtonReady,
|
||||||
keysExist,
|
keysExist,
|
||||||
numCurrentRows,
|
numCurrentRows
|
||||||
}: DropZoneProps) => {
|
}: DropZoneProps) => {
|
||||||
const handleDragEnter = (e: DragEvent) => {
|
const handleDragEnter = (e: DragEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -43,7 +43,7 @@ const DropZone = ({
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
// set dropEffect to copy i.e copy of the source item
|
// set dropEffect to copy i.e copy of the source item
|
||||||
e.dataTransfer.dropEffect = "copy";
|
e.dataTransfer.dropEffect = 'copy';
|
||||||
};
|
};
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -54,7 +54,7 @@ const DropZone = ({
|
|||||||
setTimeout(() => setLoading(false), 5000);
|
setTimeout(() => setLoading(false), 5000);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.dataTransfer.dropEffect = "copy";
|
e.dataTransfer.dropEffect = 'copy';
|
||||||
|
|
||||||
const file = e.dataTransfer.files[0];
|
const file = e.dataTransfer.files[0];
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
@@ -68,7 +68,7 @@ const DropZone = ({
|
|||||||
numCurrentRows + index,
|
numCurrentRows + index,
|
||||||
key,
|
key,
|
||||||
keyPairs[key as keyof typeof keyPairs],
|
keyPairs[key as keyof typeof keyPairs],
|
||||||
"shared",
|
'shared'
|
||||||
]);
|
]);
|
||||||
setData(newData);
|
setData(newData);
|
||||||
setButtonReady(true);
|
setButtonReady(true);
|
||||||
@@ -94,15 +94,15 @@ const DropZone = ({
|
|||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
if (event.target === null || event.target.result === null) return;
|
if (event.target === null || event.target.result === null) return;
|
||||||
const { result } = event.target;
|
const { result } = event.target;
|
||||||
if (typeof result === "string") {
|
if (typeof result === 'string') {
|
||||||
const newData = result
|
const newData = result
|
||||||
.split("\n")
|
.split('\n')
|
||||||
.map((line: string, index: number) => [
|
.map((line: string, index: number) => [
|
||||||
guidGenerator(),
|
guidGenerator(),
|
||||||
numCurrentRows + index,
|
numCurrentRows + index,
|
||||||
line.split("=")[0],
|
line.split('=')[0],
|
||||||
line.split("=").slice(1, line.split("=").length).join("="),
|
line.split('=').slice(1, line.split('=').length).join('='),
|
||||||
"shared",
|
'shared'
|
||||||
]);
|
]);
|
||||||
setData(newData);
|
setData(newData);
|
||||||
setButtonReady(true);
|
setButtonReady(true);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
/* eslint-disable react/jsx-key */
|
/* eslint-disable react/jsx-key */
|
||||||
import React, { Fragment, useEffect, useState } from "react";
|
import React, { Fragment, useEffect, useState } from 'react';
|
||||||
import Image from "next/image";
|
import Image from 'next/image';
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from 'next/router';
|
||||||
import { faGithub, faSlack } from "@fortawesome/free-brands-svg-icons";
|
import { faGithub, faSlack } from '@fortawesome/free-brands-svg-icons';
|
||||||
import { faCircleQuestion } from "@fortawesome/free-regular-svg-icons";
|
import { faCircleQuestion } from '@fortawesome/free-regular-svg-icons';
|
||||||
import {
|
import {
|
||||||
faAngleDown,
|
faAngleDown,
|
||||||
faBook,
|
faBook,
|
||||||
@@ -12,39 +12,39 @@ import {
|
|||||||
faEnvelope,
|
faEnvelope,
|
||||||
faGear,
|
faGear,
|
||||||
faPlus,
|
faPlus,
|
||||||
faRightFromBracket,
|
faRightFromBracket
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { Menu, Transition } from "@headlessui/react";
|
import { Menu, Transition } from '@headlessui/react';
|
||||||
|
|
||||||
import logout from "~/pages/api/auth/Logout";
|
import logout from '~/pages/api/auth/Logout';
|
||||||
import getOrganization from "~/pages/api/organization/GetOrg";
|
import getOrganization from '~/pages/api/organization/GetOrg';
|
||||||
import getOrganizations from "~/pages/api/organization/getOrgs";
|
import getOrganizations from '~/pages/api/organization/getOrgs';
|
||||||
import getUser from "~/pages/api/user/getUser";
|
import getUser from '~/pages/api/user/getUser';
|
||||||
|
|
||||||
import guidGenerator from "../utilities/randomId";
|
import guidGenerator from '../utilities/randomId';
|
||||||
|
|
||||||
const supportOptions = [
|
const supportOptions = [
|
||||||
[
|
[
|
||||||
<FontAwesomeIcon className="text-lg pl-1.5 pr-3" icon={faSlack} />,
|
<FontAwesomeIcon className="text-lg pl-1.5 pr-3" icon={faSlack} />,
|
||||||
"Join Slack Forum",
|
'Join Slack Forum',
|
||||||
"https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g",
|
'https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
<FontAwesomeIcon className="text-lg pl-1.5 pr-3" icon={faBook} />,
|
<FontAwesomeIcon className="text-lg pl-1.5 pr-3" icon={faBook} />,
|
||||||
"Read Docs",
|
'Read Docs',
|
||||||
"https://infisical.com/docs/getting-started/introduction",
|
'https://infisical.com/docs/getting-started/introduction'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
<FontAwesomeIcon className="text-lg pl-1.5 pr-3" icon={faGithub} />,
|
<FontAwesomeIcon className="text-lg pl-1.5 pr-3" icon={faGithub} />,
|
||||||
"Open a GitHub Issue",
|
'Open a GitHub Issue',
|
||||||
"https://github.com/Infisical/infisical-cli/issues",
|
'https://github.com/Infisical/infisical-cli/issues'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
<FontAwesomeIcon className="text-lg pl-1.5 pr-3" icon={faEnvelope} />,
|
<FontAwesomeIcon className="text-lg pl-1.5 pr-3" icon={faEnvelope} />,
|
||||||
"Send us an Email",
|
'Send us an Email',
|
||||||
"mailto:[email protected]",
|
'mailto:[email protected]'
|
||||||
],
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
export interface ICurrentOrg {
|
export interface ICurrentOrg {
|
||||||
@@ -75,16 +75,16 @@ export default function Navbar() {
|
|||||||
const orgsData = await getOrganizations();
|
const orgsData = await getOrganizations();
|
||||||
setOrgs(orgsData);
|
setOrgs(orgsData);
|
||||||
const currentOrg = await getOrganization({
|
const currentOrg = await getOrganization({
|
||||||
orgId: String(localStorage.getItem("orgData.id")),
|
orgId: String(localStorage.getItem('orgData.id'))
|
||||||
});
|
});
|
||||||
setCurrentOrg(currentOrg);
|
setCurrentOrg(currentOrg);
|
||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const closeApp = async () => {
|
const closeApp = async () => {
|
||||||
console.log("Logging out...");
|
console.log('Logging out...');
|
||||||
await logout();
|
await logout();
|
||||||
router.push("/login");
|
router.push('/login');
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -163,7 +163,7 @@ export default function Navbar() {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push("/settings/personal/" + router.query.id)
|
router.push('/settings/personal/' + router.query.id)
|
||||||
}
|
}
|
||||||
className="flex flex-row items-center px-1 mx-1 my-1 hover:bg-white/5 cursor-pointer rounded-md"
|
className="flex flex-row items-center px-1 mx-1 my-1 hover:bg-white/5 cursor-pointer rounded-md"
|
||||||
>
|
>
|
||||||
@@ -173,11 +173,11 @@ export default function Navbar() {
|
|||||||
<div className="flex items-center justify-between w-full">
|
<div className="flex items-center justify-between w-full">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-gray-300 px-2 pt-1 text-sm">
|
<p className="text-gray-300 px-2 pt-1 text-sm">
|
||||||
{" "}
|
{' '}
|
||||||
{user?.firstName} {user?.lastName}
|
{user?.firstName} {user?.lastName}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-gray-400 px-2 pb-1 text-xs">
|
<p className="text-gray-400 px-2 pb-1 text-xs">
|
||||||
{" "}
|
{' '}
|
||||||
{user?.email}
|
{user?.email}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -194,7 +194,7 @@ export default function Navbar() {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push("/settings/org/" + router.query.id)
|
router.push('/settings/org/' + router.query.id)
|
||||||
}
|
}
|
||||||
className="flex flex-row items-center px-2 mt-2 py-1 hover:bg-white/5 cursor-pointer rounded-md"
|
className="flex flex-row items-center px-2 mt-2 py-1 hover:bg-white/5 cursor-pointer rounded-md"
|
||||||
>
|
>
|
||||||
@@ -217,7 +217,7 @@ export default function Navbar() {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push("/settings/billing/" + router.query.id)
|
router.push('/settings/billing/' + router.query.id)
|
||||||
}
|
}
|
||||||
className="mt-1 relative flex justify-start cursor-pointer select-none py-2 px-2 rounded-md text-gray-400 hover:bg-white/5 duration-200 hover:text-gray-200"
|
className="mt-1 relative flex justify-start cursor-pointer select-none py-2 px-2 rounded-md text-gray-400 hover:bg-white/5 duration-200 hover:text-gray-200"
|
||||||
>
|
>
|
||||||
@@ -235,7 +235,7 @@ export default function Navbar() {
|
|||||||
<div
|
<div
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push(
|
router.push(
|
||||||
"/settings/org/" + router.query.id + "?invite"
|
'/settings/org/' + router.query.id + '?invite'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
className="relative flex justify-start cursor-pointer select-none py-2 pl-10 pr-4 rounded-md text-gray-400 hover:bg-primary/100 duration-200 hover:text-black hover:font-semibold mt-1"
|
className="relative flex justify-start cursor-pointer select-none py-2 pl-10 pr-4 rounded-md text-gray-400 hover:bg-primary/100 duration-200 hover:text-black hover:font-semibold mt-1"
|
||||||
@@ -255,13 +255,14 @@ export default function Navbar() {
|
|||||||
<div className="flex flex-col items-start px-1 mt-3 mb-2">
|
<div className="flex flex-col items-start px-1 mt-3 mb-2">
|
||||||
{orgs
|
{orgs
|
||||||
.filter(
|
.filter(
|
||||||
(org : { _id: string }) => org._id != localStorage.getItem("orgData.id")
|
(org: { _id: string }) =>
|
||||||
|
org._id != localStorage.getItem('orgData.id')
|
||||||
)
|
)
|
||||||
.map((org : { _id: string; name: string; }) => (
|
.map((org: { _id: string; name: string }) => (
|
||||||
<div
|
<div
|
||||||
key={guidGenerator()}
|
key={guidGenerator()}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
localStorage.setItem("orgData.id", org._id);
|
localStorage.setItem('orgData.id', org._id);
|
||||||
router.reload();
|
router.reload();
|
||||||
}}
|
}}
|
||||||
className="flex flex-row justify-start items-center hover:bg-white/5 w-full p-1.5 cursor-pointer rounded-md"
|
className="flex flex-row justify-start items-center hover:bg-white/5 w-full p-1.5 cursor-pointer rounded-md"
|
||||||
@@ -286,8 +287,8 @@ export default function Navbar() {
|
|||||||
onClick={closeApp}
|
onClick={closeApp}
|
||||||
className={`${
|
className={`${
|
||||||
active
|
active
|
||||||
? "bg-red font-semibold text-white"
|
? 'bg-red font-semibold text-white'
|
||||||
: "text-gray-400"
|
: 'text-gray-400'
|
||||||
} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
|
} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
|
||||||
>
|
>
|
||||||
<div className="relative flex justify-start items-center cursor-pointer select-none">
|
<div className="relative flex justify-start items-center cursor-pointer select-none">
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import token from "~/pages/api/auth/Token";
|
|
||||||
|
|
||||||
export default class SecurityClient {
|
|
||||||
static #token = "";
|
|
||||||
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
static setToken(token) {
|
|
||||||
this.#token = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async fetchCall(resource, options) {
|
|
||||||
let req = new Request(resource, options);
|
|
||||||
|
|
||||||
if (this.#token == "") {
|
|
||||||
this.setToken(await token());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.#token) {
|
|
||||||
req.headers.set("Authorization", "Bearer " + this.#token);
|
|
||||||
return fetch(req);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import token from '~/pages/api/auth/Token';
|
||||||
|
|
||||||
|
export default class SecurityClient {
|
||||||
|
static #token = '';
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
static setToken(token: string) {
|
||||||
|
this.#token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCall(
|
||||||
|
resource: RequestInfo,
|
||||||
|
options?: RequestInit | undefined
|
||||||
|
) {
|
||||||
|
const req = new Request(resource, options);
|
||||||
|
|
||||||
|
if (this.#token == '') {
|
||||||
|
this.setToken(await token());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#token) {
|
||||||
|
req.headers.set('Authorization', 'Bearer ' + this.#token);
|
||||||
|
return fetch(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
import Aes256Gcm from "~/components/utilities/cryptography/aes-256-gcm";
|
import Aes256Gcm from '~/components/utilities/cryptography/aes-256-gcm';
|
||||||
import login1 from "~/pages/api/auth/Login1";
|
import login1 from '~/pages/api/auth/Login1';
|
||||||
import login2 from "~/pages/api/auth/Login2";
|
import login2 from '~/pages/api/auth/Login2';
|
||||||
import getOrganizations from "~/pages/api/organization/getOrgs";
|
import getOrganizations from '~/pages/api/organization/getOrgs';
|
||||||
import getOrganizationUserProjects from "~/pages/api/organization/GetOrgUserProjects";
|
import getOrganizationUserProjects from '~/pages/api/organization/GetOrgUserProjects';
|
||||||
|
|
||||||
import pushKeys from "./secrets/pushKeys";
|
import pushKeys from './secrets/pushKeys';
|
||||||
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
|
import Telemetry from './telemetry/Telemetry';
|
||||||
import SecurityClient from "./SecurityClient";
|
import { saveTokenToLocalStorage } from './saveTokenToLocalStorage';
|
||||||
import Telemetry from "./telemetry/Telemetry";
|
import SecurityClient from './SecurityClient';
|
||||||
|
|
||||||
const nacl = require("tweetnacl");
|
const nacl = require('tweetnacl');
|
||||||
nacl.util = require("tweetnacl-util");
|
nacl.util = require('tweetnacl-util');
|
||||||
const jsrp = require("jsrp");
|
const jsrp = require('jsrp');
|
||||||
const client = new jsrp.client();
|
const client = new jsrp.client();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,7 +37,7 @@ const attemptLogin = async (
|
|||||||
client.init(
|
client.init(
|
||||||
{
|
{
|
||||||
username: email,
|
username: email,
|
||||||
password: password,
|
password: password
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
const clientPublicKey = client.getPublicKey();
|
const clientPublicKey = client.getPublicKey();
|
||||||
@@ -54,54 +54,53 @@ const attemptLogin = async (
|
|||||||
await login2(email, clientProof);
|
await login2(email, clientProof);
|
||||||
SecurityClient.setToken(token);
|
SecurityClient.setToken(token);
|
||||||
|
|
||||||
const privateKey = Aes256Gcm.decrypt(
|
const privateKey = Aes256Gcm.decrypt({
|
||||||
encryptedPrivateKey,
|
ciphertext: encryptedPrivateKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
password
|
secret: password
|
||||||
.slice(0, 32)
|
.slice(0, 32)
|
||||||
.padStart(
|
.padStart(
|
||||||
32 + (password.slice(0, 32).length - new Blob([password]).size),
|
32 + (password.slice(0, 32).length - new Blob([password]).size),
|
||||||
"0"
|
'0'
|
||||||
)
|
)
|
||||||
);
|
});
|
||||||
|
|
||||||
saveTokenToLocalStorage({
|
saveTokenToLocalStorage({
|
||||||
token,
|
|
||||||
publicKey,
|
publicKey,
|
||||||
encryptedPrivateKey,
|
encryptedPrivateKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
privateKey,
|
privateKey
|
||||||
});
|
});
|
||||||
|
|
||||||
const userOrgs = await getOrganizations();
|
const userOrgs = await getOrganizations();
|
||||||
const userOrgsData = userOrgs.map((org) => org._id);
|
const userOrgsData = userOrgs.map((org) => org._id);
|
||||||
|
|
||||||
let orgToLogin;
|
let orgToLogin;
|
||||||
if (userOrgsData.includes(localStorage.getItem("orgData.id"))) {
|
if (userOrgsData.includes(localStorage.getItem('orgData.id'))) {
|
||||||
orgToLogin = localStorage.getItem("orgData.id");
|
orgToLogin = localStorage.getItem('orgData.id');
|
||||||
} else {
|
} else {
|
||||||
orgToLogin = userOrgsData[0];
|
orgToLogin = userOrgsData[0];
|
||||||
localStorage.setItem("orgData.id", orgToLogin);
|
localStorage.setItem('orgData.id', orgToLogin);
|
||||||
}
|
}
|
||||||
|
|
||||||
let orgUserProjects = await getOrganizationUserProjects({
|
let orgUserProjects = await getOrganizationUserProjects({
|
||||||
orgId: orgToLogin,
|
orgId: orgToLogin
|
||||||
});
|
});
|
||||||
|
|
||||||
orgUserProjects = orgUserProjects?.map((project) => project._id);
|
orgUserProjects = orgUserProjects?.map((project) => project._id);
|
||||||
let projectToLogin;
|
let projectToLogin;
|
||||||
if (
|
if (
|
||||||
orgUserProjects.includes(localStorage.getItem("projectData.id"))
|
orgUserProjects.includes(localStorage.getItem('projectData.id'))
|
||||||
) {
|
) {
|
||||||
projectToLogin = localStorage.getItem("projectData.id");
|
projectToLogin = localStorage.getItem('projectData.id');
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
projectToLogin = orgUserProjects[0];
|
projectToLogin = orgUserProjects[0];
|
||||||
localStorage.setItem("projectData.id", projectToLogin);
|
localStorage.setItem('projectData.id', projectToLogin);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("ERROR: User likely has no projects. ", error);
|
console.log('ERROR: User likely has no projects. ', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,38 +109,35 @@ const attemptLogin = async (
|
|||||||
await pushKeys({
|
await pushKeys({
|
||||||
obj: {
|
obj: {
|
||||||
DATABASE_URL: [
|
DATABASE_URL: [
|
||||||
"mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net",
|
'mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net',
|
||||||
"personal",
|
'personal'
|
||||||
],
|
],
|
||||||
DB_USERNAME: ["user1234", "personal"],
|
DB_USERNAME: ['user1234', 'personal'],
|
||||||
DB_PASSWORD: ["ah8jak3hk8dhiu4dw7whxwe1l", "personal"],
|
DB_PASSWORD: ['example_password', 'personal'],
|
||||||
TWILIO_AUTH_TOKEN: [
|
TWILIO_AUTH_TOKEN: ['example_twillion_token', 'shared'],
|
||||||
"hgSIwDAKvz8PJfkj6xkzYqzGmAP3HLuG",
|
WEBSITE_URL: ['http://localhost:3000', 'shared'],
|
||||||
"shared",
|
STRIPE_SECRET_KEY: ['sk_test_7348oyho4hfq398HIUOH78', 'shared']
|
||||||
],
|
|
||||||
WEBSITE_URL: ["http://localhost:3000", "shared"],
|
|
||||||
STRIPE_SECRET_KEY: ["sk_test_7348oyho4hfq398HIUOH78", "shared"],
|
|
||||||
},
|
},
|
||||||
workspaceId: projectToLogin,
|
workspaceId: projectToLogin,
|
||||||
env: "Development",
|
env: 'Development'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (email) {
|
if (email) {
|
||||||
telemetry.identify(email);
|
telemetry.identify(email);
|
||||||
telemetry.capture("User Logged In");
|
telemetry.capture('User Logged In');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLogin) {
|
if (isLogin) {
|
||||||
router.push("/dashboard/");
|
router.push('/dashboard/');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setErrorLogin(true);
|
setErrorLogin(true);
|
||||||
console.log("Login response not available");
|
console.log('Login response not available');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Something went wrong during authentication");
|
console.log('Something went wrong during authentication');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import getOrganizationUsers from '~/pages/api/organization/GetOrgUsers';
|
||||||
|
import checkUserAction from '~/pages/api/userActions/checkUserAction';
|
||||||
|
|
||||||
|
interface OnboardingCheckProps {
|
||||||
|
setTotalOnboardingActionsDone?: (value: number) => void;
|
||||||
|
setHasUserClickedSlack?: (value: boolean) => void;
|
||||||
|
setHasUserClickedIntro?: (value: boolean) => void;
|
||||||
|
setHasUserStarred?: (value: boolean) => void;
|
||||||
|
setHasUserPushedSecrets?: (value: boolean) => void;
|
||||||
|
setUsersInOrg?: (value: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function checks which onboarding steps a user has already finished.
|
||||||
|
*/
|
||||||
|
const onboardingCheck = async ({
|
||||||
|
setTotalOnboardingActionsDone,
|
||||||
|
setHasUserClickedSlack,
|
||||||
|
setHasUserClickedIntro,
|
||||||
|
setHasUserStarred,
|
||||||
|
setHasUserPushedSecrets,
|
||||||
|
setUsersInOrg
|
||||||
|
}: OnboardingCheckProps) => {
|
||||||
|
let countActions = 0;
|
||||||
|
const userActionSlack = await checkUserAction({
|
||||||
|
action: 'slack_cta_clicked'
|
||||||
|
});
|
||||||
|
if (userActionSlack) {
|
||||||
|
countActions = countActions + 1;
|
||||||
|
}
|
||||||
|
setHasUserClickedSlack &&
|
||||||
|
setHasUserClickedSlack(userActionSlack ? true : false);
|
||||||
|
|
||||||
|
const userActionSecrets = await checkUserAction({
|
||||||
|
action: 'first_time_secrets_pushed'
|
||||||
|
});
|
||||||
|
if (userActionSecrets) {
|
||||||
|
countActions = countActions + 1;
|
||||||
|
}
|
||||||
|
setHasUserPushedSecrets &&
|
||||||
|
setHasUserPushedSecrets(userActionSecrets ? true : false);
|
||||||
|
|
||||||
|
const userActionIntro = await checkUserAction({
|
||||||
|
action: 'intro_cta_clicked'
|
||||||
|
});
|
||||||
|
if (userActionIntro) {
|
||||||
|
countActions = countActions + 1;
|
||||||
|
}
|
||||||
|
setHasUserClickedIntro &&
|
||||||
|
setHasUserClickedIntro(userActionIntro ? true : false);
|
||||||
|
|
||||||
|
const userActionStar = await checkUserAction({
|
||||||
|
action: 'star_cta_clicked'
|
||||||
|
});
|
||||||
|
if (userActionStar) {
|
||||||
|
countActions = countActions + 1;
|
||||||
|
}
|
||||||
|
setHasUserStarred && setHasUserStarred(userActionStar ? true : false);
|
||||||
|
|
||||||
|
const orgId = localStorage.getItem('orgData.id');
|
||||||
|
const orgUsers = await getOrganizationUsers({
|
||||||
|
orgId: orgId ? orgId : ''
|
||||||
|
});
|
||||||
|
if (orgUsers.length > 1) {
|
||||||
|
countActions = countActions + 1;
|
||||||
|
}
|
||||||
|
setUsersInOrg && setUsersInOrg(orgUsers.length > 1);
|
||||||
|
setTotalOnboardingActionsDone && setTotalOnboardingActionsDone(countActions);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default onboardingCheck;
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileoverview Provides easy encryption/decryption methods using AES 256 GCM.
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const crypto = require("crypto");
|
|
||||||
|
|
||||||
const ALGORITHM = "aes-256-gcm";
|
|
||||||
const BLOCK_SIZE_BYTES = 16; // 128 bit
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides easy encryption/decryption methods using AES 256 GCM.
|
|
||||||
*/
|
|
||||||
class Aes256Gcm {
|
|
||||||
/**
|
|
||||||
* No need to run the constructor. The class only has static methods.
|
|
||||||
*/
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts text with AES 256 GCM.
|
|
||||||
* @param {string} text - Cleartext to encode.
|
|
||||||
* @param {string} secret - Shared secret key, must be 32 bytes.
|
|
||||||
* @returns {object}
|
|
||||||
*/
|
|
||||||
static encrypt(text, secret) {
|
|
||||||
const iv = crypto.randomBytes(BLOCK_SIZE_BYTES);
|
|
||||||
const cipher = crypto.createCipheriv(ALGORITHM, secret, iv);
|
|
||||||
|
|
||||||
let ciphertext = cipher.update(text, "utf8", "base64");
|
|
||||||
ciphertext += cipher.final("base64");
|
|
||||||
return {
|
|
||||||
ciphertext,
|
|
||||||
iv: iv.toString("base64"),
|
|
||||||
tag: cipher.getAuthTag().toString("base64"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts AES 256 CGM encrypted text.
|
|
||||||
* @param {string} ciphertext - Base64-encoded ciphertext.
|
|
||||||
* @param {string} iv - The base64-encoded initialization vector.
|
|
||||||
* @param {string} tag - The base64-encoded authentication tag generated by getAuthTag().
|
|
||||||
* @param {string} secret - Shared secret key, must be 32 bytes.
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
static decrypt(ciphertext, iv, tag, secret) {
|
|
||||||
const decipher = crypto.createDecipheriv(
|
|
||||||
ALGORITHM,
|
|
||||||
secret,
|
|
||||||
Buffer.from(iv, "base64")
|
|
||||||
);
|
|
||||||
decipher.setAuthTag(Buffer.from(tag, "base64"));
|
|
||||||
|
|
||||||
let cleartext = decipher.update(ciphertext, "base64", "utf8");
|
|
||||||
cleartext += decipher.final("utf8");
|
|
||||||
|
|
||||||
return cleartext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Aes256Gcm;
|
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Provides easy encryption/decryption methods using AES 256 GCM.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
const ALGORITHM = 'aes-256-gcm';
|
||||||
|
const BLOCK_SIZE_BYTES = 16; // 128 bit
|
||||||
|
|
||||||
|
interface EncryptProps {
|
||||||
|
text: string;
|
||||||
|
secret: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DecryptProps {
|
||||||
|
ciphertext: string;
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
secret: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EncryptOutputProps {
|
||||||
|
ciphertext: string;
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides easy encryption/decryption methods using AES 256 GCM.
|
||||||
|
*/
|
||||||
|
class Aes256Gcm {
|
||||||
|
/**
|
||||||
|
* No need to run the constructor. The class only has static methods.
|
||||||
|
*/
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts text with AES 256 GCM.
|
||||||
|
* @param {object} obj
|
||||||
|
* @param {string} obj.text - Cleartext to encode.
|
||||||
|
* @param {string} obj.secret - Shared secret key, must be 32 bytes.
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
// { ciphertext: string; iv: string; tag: string; }
|
||||||
|
static encrypt({ text, secret }: EncryptProps): EncryptOutputProps {
|
||||||
|
const iv = crypto.randomBytes(BLOCK_SIZE_BYTES);
|
||||||
|
const cipher = crypto.createCipheriv(ALGORITHM, secret, iv);
|
||||||
|
|
||||||
|
let ciphertext = cipher.update(text, 'utf8', 'base64');
|
||||||
|
ciphertext += cipher.final('base64');
|
||||||
|
return {
|
||||||
|
ciphertext,
|
||||||
|
iv: iv.toString('base64'),
|
||||||
|
tag: cipher.getAuthTag().toString('base64')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts AES 256 CGM encrypted text.
|
||||||
|
* @param {object} obj
|
||||||
|
* @param {string} obj.ciphertext - Base64-encoded ciphertext.
|
||||||
|
* @param {string} obj.iv - The base64-encoded initialization vector.
|
||||||
|
* @param {string} obj.tag - The base64-encoded authentication tag generated by getAuthTag().
|
||||||
|
* @param {string} obj.secret - Shared secret key, must be 32 bytes.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
static decrypt({ ciphertext, iv, tag, secret }: DecryptProps): string {
|
||||||
|
const decipher = crypto.createDecipheriv(
|
||||||
|
ALGORITHM,
|
||||||
|
secret,
|
||||||
|
Buffer.from(iv, 'base64')
|
||||||
|
);
|
||||||
|
decipher.setAuthTag(Buffer.from(tag, 'base64'));
|
||||||
|
|
||||||
|
let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
|
||||||
|
cleartext += decipher.final('utf8');
|
||||||
|
|
||||||
|
return cleartext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Aes256Gcm;
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import changePassword2 from "~/pages/api/auth/ChangePassword2";
|
import changePassword2 from '~/pages/api/auth/ChangePassword2';
|
||||||
import SRP1 from "~/pages/api/auth/SRP1";
|
import SRP1 from '~/pages/api/auth/SRP1';
|
||||||
|
|
||||||
import Aes256Gcm from "./aes-256-gcm";
|
import Aes256Gcm from './aes-256-gcm';
|
||||||
|
|
||||||
const nacl = require("tweetnacl");
|
const nacl = require('tweetnacl');
|
||||||
nacl.util = require("tweetnacl-util");
|
nacl.util = require('tweetnacl-util');
|
||||||
const jsrp = require("jsrp");
|
const jsrp = require('jsrp');
|
||||||
const clientOldPassword = new jsrp.client();
|
const clientOldPassword = new jsrp.client();
|
||||||
const clientNewPassword = new jsrp.client();
|
const clientNewPassword = new jsrp.client();
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ const changePassword = async (
|
|||||||
clientOldPassword.init(
|
clientOldPassword.init(
|
||||||
{
|
{
|
||||||
username: email,
|
username: email,
|
||||||
password: currentPassword,
|
password: currentPassword
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
const clientPublicKey = clientOldPassword.getPublicKey();
|
const clientPublicKey = clientOldPassword.getPublicKey();
|
||||||
@@ -42,13 +42,13 @@ const changePassword = async (
|
|||||||
let serverPublicKey, salt;
|
let serverPublicKey, salt;
|
||||||
try {
|
try {
|
||||||
const res = await SRP1({
|
const res = await SRP1({
|
||||||
clientPublicKey: clientPublicKey,
|
clientPublicKey: clientPublicKey
|
||||||
});
|
});
|
||||||
serverPublicKey = res.serverPublicKey;
|
serverPublicKey = res.serverPublicKey;
|
||||||
salt = res.salt;
|
salt = res.salt;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setCurrentPasswordError(true);
|
setCurrentPasswordError(true);
|
||||||
console.log("Wrong current password", err, 1);
|
console.log('Wrong current password', err, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
clientOldPassword.setSalt(salt);
|
clientOldPassword.setSalt(salt);
|
||||||
@@ -58,27 +58,27 @@ const changePassword = async (
|
|||||||
clientNewPassword.init(
|
clientNewPassword.init(
|
||||||
{
|
{
|
||||||
username: email,
|
username: email,
|
||||||
password: newPassword,
|
password: newPassword
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
clientNewPassword.createVerifier(async (err, result) => {
|
clientNewPassword.createVerifier(async (err, result) => {
|
||||||
// The Blob part here is needed to account for symbols that count as 2+ bytes (e.g., é, å, ø)
|
// The Blob part here is needed to account for symbols that count as 2+ bytes (e.g., é, å, ø)
|
||||||
let { ciphertext, iv, tag } = Aes256Gcm.encrypt(
|
const { ciphertext, iv, tag } = Aes256Gcm.encrypt({
|
||||||
localStorage.getItem("PRIVATE_KEY"),
|
text: localStorage.getItem('PRIVATE_KEY'),
|
||||||
newPassword
|
secret: newPassword
|
||||||
.slice(0, 32)
|
.slice(0, 32)
|
||||||
.padStart(
|
.padStart(
|
||||||
32 +
|
32 +
|
||||||
(newPassword.slice(0, 32).length -
|
(newPassword.slice(0, 32).length -
|
||||||
new Blob([newPassword]).size),
|
new Blob([newPassword]).size),
|
||||||
"0"
|
'0'
|
||||||
)
|
)
|
||||||
);
|
});
|
||||||
|
|
||||||
if (ciphertext) {
|
if (ciphertext) {
|
||||||
localStorage.setItem("encryptedPrivateKey", ciphertext);
|
localStorage.setItem('encryptedPrivateKey', ciphertext);
|
||||||
localStorage.setItem("iv", iv);
|
localStorage.setItem('iv', iv);
|
||||||
localStorage.setItem("tag", tag);
|
localStorage.setItem('tag', tag);
|
||||||
|
|
||||||
let res;
|
let res;
|
||||||
try {
|
try {
|
||||||
@@ -88,14 +88,14 @@ const changePassword = async (
|
|||||||
tag,
|
tag,
|
||||||
salt: result.salt,
|
salt: result.salt,
|
||||||
verifier: result.verifier,
|
verifier: result.verifier,
|
||||||
clientProof,
|
clientProof
|
||||||
});
|
});
|
||||||
if (res.status == 400) {
|
if (res.status == 400) {
|
||||||
setCurrentPasswordError(true);
|
setCurrentPasswordError(true);
|
||||||
} else if (res.status == 200) {
|
} else if (res.status == 200) {
|
||||||
setPasswordChanged(true);
|
setPasswordChanged(true);
|
||||||
setCurrentPassword("");
|
setCurrentPassword('');
|
||||||
setNewPassword("");
|
setNewPassword('');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setCurrentPasswordError(true);
|
setCurrentPasswordError(true);
|
||||||
@@ -108,7 +108,7 @@ const changePassword = async (
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Something went wrong during changing the password");
|
console.log('Something went wrong during changing the password');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
const nacl = require("tweetnacl");
|
const nacl = require('tweetnacl');
|
||||||
nacl.util = require("tweetnacl-util");
|
nacl.util = require('tweetnacl-util');
|
||||||
const aes = require("./aes-256-gcm");
|
import aes from './aes-256-gcm';
|
||||||
|
|
||||||
type encryptAsymmetricProps = {
|
type encryptAsymmetricProps = {
|
||||||
plaintext: string;
|
plaintext: string;
|
||||||
publicKey: string;
|
publicKey: string;
|
||||||
privateKey: string;
|
privateKey: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return assymmetrically encrypted [plaintext] using [publicKey] where
|
* Return assymmetrically encrypted [plaintext] using [publicKey] where
|
||||||
@@ -19,7 +19,11 @@ type encryptAsymmetricProps = {
|
|||||||
* @returns {String} ciphertext - base64-encoded ciphertext
|
* @returns {String} ciphertext - base64-encoded ciphertext
|
||||||
* @returns {String} nonce - base64-encoded nonce
|
* @returns {String} nonce - base64-encoded nonce
|
||||||
*/
|
*/
|
||||||
const encryptAssymmetric = ({ plaintext, publicKey, privateKey }: encryptAsymmetricProps): object => {
|
const encryptAssymmetric = ({
|
||||||
|
plaintext,
|
||||||
|
publicKey,
|
||||||
|
privateKey
|
||||||
|
}: encryptAsymmetricProps): object => {
|
||||||
const nonce = nacl.randomBytes(24);
|
const nonce = nacl.randomBytes(24);
|
||||||
const ciphertext = nacl.box(
|
const ciphertext = nacl.box(
|
||||||
nacl.util.decodeUTF8(plaintext),
|
nacl.util.decodeUTF8(plaintext),
|
||||||
@@ -30,7 +34,7 @@ const encryptAssymmetric = ({ plaintext, publicKey, privateKey }: encryptAsymmet
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
ciphertext: nacl.util.encodeBase64(ciphertext),
|
ciphertext: nacl.util.encodeBase64(ciphertext),
|
||||||
nonce: nacl.util.encodeBase64(nonce),
|
nonce: nacl.util.encodeBase64(nonce)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,7 +43,7 @@ type decryptAsymmetricProps = {
|
|||||||
nonce: string;
|
nonce: string;
|
||||||
publicKey: string;
|
publicKey: string;
|
||||||
privateKey: string;
|
privateKey: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return assymmetrically decrypted [ciphertext] using [privateKey] where
|
* Return assymmetrically decrypted [ciphertext] using [privateKey] where
|
||||||
@@ -49,9 +53,13 @@ type decryptAsymmetricProps = {
|
|||||||
* @param {String} obj.nonce - nonce
|
* @param {String} obj.nonce - nonce
|
||||||
* @param {String} obj.publicKey - base64-encoded public key of the sender
|
* @param {String} obj.publicKey - base64-encoded public key of the sender
|
||||||
* @param {String} obj.privateKey - base64-encoded private key of the receiver (current user)
|
* @param {String} obj.privateKey - base64-encoded private key of the receiver (current user)
|
||||||
* @param {String} plaintext - UTF8 plaintext
|
|
||||||
*/
|
*/
|
||||||
const decryptAssymmetric = ({ ciphertext, nonce, publicKey, privateKey }: decryptAsymmetricProps): string => {
|
const decryptAssymmetric = ({
|
||||||
|
ciphertext,
|
||||||
|
nonce,
|
||||||
|
publicKey,
|
||||||
|
privateKey
|
||||||
|
}: decryptAsymmetricProps): string => {
|
||||||
const plaintext = nacl.box.open(
|
const plaintext = nacl.box.open(
|
||||||
nacl.util.decodeBase64(ciphertext),
|
nacl.util.decodeBase64(ciphertext),
|
||||||
nacl.util.decodeBase64(nonce),
|
nacl.util.decodeBase64(nonce),
|
||||||
@@ -65,7 +73,7 @@ const decryptAssymmetric = ({ ciphertext, nonce, publicKey, privateKey }: decryp
|
|||||||
type encryptSymmetricProps = {
|
type encryptSymmetricProps = {
|
||||||
plaintext: string;
|
plaintext: string;
|
||||||
key: string;
|
key: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return symmetrically encrypted [plaintext] using [key].
|
* Return symmetrically encrypted [plaintext] using [key].
|
||||||
@@ -73,15 +81,18 @@ type encryptSymmetricProps = {
|
|||||||
* @param {String} obj.plaintext - plaintext to encrypt
|
* @param {String} obj.plaintext - plaintext to encrypt
|
||||||
* @param {String} obj.key - 16-byte hex key
|
* @param {String} obj.key - 16-byte hex key
|
||||||
*/
|
*/
|
||||||
const encryptSymmetric = ({ plaintext, key }: encryptSymmetricProps): object => {
|
const encryptSymmetric = ({
|
||||||
|
plaintext,
|
||||||
|
key
|
||||||
|
}: encryptSymmetricProps): object => {
|
||||||
let ciphertext, iv, tag;
|
let ciphertext, iv, tag;
|
||||||
try {
|
try {
|
||||||
const obj = aes.encrypt(plaintext, key);
|
const obj = aes.encrypt({ text: plaintext, secret: key });
|
||||||
ciphertext = obj.ciphertext;
|
ciphertext = obj.ciphertext;
|
||||||
iv = obj.iv;
|
iv = obj.iv;
|
||||||
tag = obj.tag;
|
tag = obj.tag;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Failed to perform encryption");
|
console.log('Failed to perform encryption');
|
||||||
console.log(err);
|
console.log(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@@ -89,7 +100,7 @@ const encryptSymmetric = ({ plaintext, key }: encryptSymmetricProps): object =>
|
|||||||
return {
|
return {
|
||||||
ciphertext,
|
ciphertext,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -98,7 +109,7 @@ type decryptSymmetricProps = {
|
|||||||
iv: string;
|
iv: string;
|
||||||
tag: string;
|
tag: string;
|
||||||
key: string;
|
key: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return symmetrically decypted [ciphertext] using [iv], [tag],
|
* Return symmetrically decypted [ciphertext] using [iv], [tag],
|
||||||
@@ -110,12 +121,17 @@ type decryptSymmetricProps = {
|
|||||||
* @param {String} obj.key - 32-byte hex key
|
* @param {String} obj.key - 32-byte hex key
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
const decryptSymmetric = ({ ciphertext, iv, tag, key }: decryptSymmetricProps): string => {
|
const decryptSymmetric = ({
|
||||||
|
ciphertext,
|
||||||
|
iv,
|
||||||
|
tag,
|
||||||
|
key
|
||||||
|
}: decryptSymmetricProps): string => {
|
||||||
let plaintext;
|
let plaintext;
|
||||||
try {
|
try {
|
||||||
plaintext = aes.decrypt(ciphertext, iv, tag, key);
|
plaintext = aes.decrypt({ ciphertext, iv, tag, secret: key });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Failed to perform decryption");
|
console.log('Failed to perform decryption');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,5 +142,5 @@ export {
|
|||||||
decryptAssymmetric,
|
decryptAssymmetric,
|
||||||
decryptSymmetric,
|
decryptSymmetric,
|
||||||
encryptAssymmetric,
|
encryptAssymmetric,
|
||||||
encryptSymmetric,
|
encryptSymmetric
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
import issueBackupPrivateKey from "~/pages/api/auth/IssueBackupPrivateKey";
|
|
||||||
import SRP1 from "~/pages/api/auth/SRP1";
|
|
||||||
|
|
||||||
import generateBackupPDF from "../generateBackupPDF";
|
|
||||||
import Aes256Gcm from "./aes-256-gcm";
|
|
||||||
|
|
||||||
const nacl = require("tweetnacl");
|
|
||||||
nacl.util = require("tweetnacl-util");
|
|
||||||
const jsrp = require("jsrp");
|
|
||||||
const clientPassword = new jsrp.client();
|
|
||||||
const clientKey = new jsrp.client();
|
|
||||||
const crypto = require("crypto");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function loggs in the user (whether it's right after signup, or a normal login)
|
|
||||||
* @param {*} email
|
|
||||||
* @param {*} password
|
|
||||||
* @param {*} setErrorLogin
|
|
||||||
* @param {*} router
|
|
||||||
* @param {*} isSignUp
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const issueBackupKey = async ({
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
personalName,
|
|
||||||
setBackupKeyError,
|
|
||||||
setBackupKeyIssued,
|
|
||||||
}) => {
|
|
||||||
try {
|
|
||||||
setBackupKeyError(false);
|
|
||||||
setBackupKeyIssued(false);
|
|
||||||
clientPassword.init(
|
|
||||||
{
|
|
||||||
username: email,
|
|
||||||
password: password,
|
|
||||||
},
|
|
||||||
async () => {
|
|
||||||
const clientPublicKey = clientPassword.getPublicKey();
|
|
||||||
|
|
||||||
let serverPublicKey, salt;
|
|
||||||
try {
|
|
||||||
const res = await SRP1({
|
|
||||||
clientPublicKey: clientPublicKey,
|
|
||||||
});
|
|
||||||
serverPublicKey = res.serverPublicKey;
|
|
||||||
salt = res.salt;
|
|
||||||
} catch (err) {
|
|
||||||
setBackupKeyError(true);
|
|
||||||
console.log("Wrong current password", err, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
clientPassword.setSalt(salt);
|
|
||||||
clientPassword.setServerPublicKey(serverPublicKey);
|
|
||||||
const clientProof = clientPassword.getProof(); // called M1
|
|
||||||
|
|
||||||
const generatedKey = crypto.randomBytes(16).toString("hex");
|
|
||||||
|
|
||||||
clientKey.init(
|
|
||||||
{
|
|
||||||
username: email,
|
|
||||||
password: generatedKey,
|
|
||||||
},
|
|
||||||
async () => {
|
|
||||||
clientKey.createVerifier(async (err, result) => {
|
|
||||||
let { ciphertext, iv, tag } = Aes256Gcm.encrypt(
|
|
||||||
localStorage.getItem("PRIVATE_KEY"),
|
|
||||||
generatedKey
|
|
||||||
);
|
|
||||||
|
|
||||||
const res = await issueBackupPrivateKey({
|
|
||||||
encryptedPrivateKey: ciphertext,
|
|
||||||
iv,
|
|
||||||
tag,
|
|
||||||
salt: result.salt,
|
|
||||||
verifier: result.verifier,
|
|
||||||
clientProof,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.status == 400) {
|
|
||||||
setBackupKeyError(true);
|
|
||||||
} else if (res.status == 200) {
|
|
||||||
generateBackupPDF(personalName, email, generatedKey);
|
|
||||||
setBackupKeyIssued(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
setBackupKeyError(true);
|
|
||||||
console.log("Failed to issue a backup key");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default issueBackupKey;
|
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import issueBackupPrivateKey from '~/pages/api/auth/IssueBackupPrivateKey';
|
||||||
|
import SRP1 from '~/pages/api/auth/SRP1';
|
||||||
|
|
||||||
|
import generateBackupPDF from '../generateBackupPDF';
|
||||||
|
import Aes256Gcm from './aes-256-gcm';
|
||||||
|
|
||||||
|
const nacl = require('tweetnacl');
|
||||||
|
nacl.util = require('tweetnacl-util');
|
||||||
|
const jsrp = require('jsrp');
|
||||||
|
const clientPassword = new jsrp.client();
|
||||||
|
const clientKey = new jsrp.client();
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
interface BackupKeyProps {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
personalName: string;
|
||||||
|
setBackupKeyError: (value: boolean) => void;
|
||||||
|
setBackupKeyIssued: (value: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function issue a backup key for a user
|
||||||
|
* @param {obkect} obj
|
||||||
|
* @param {string} obj.email - email of a user issuing a backup key
|
||||||
|
* @param {string} obj.password - password of a user issuing a backup key
|
||||||
|
* @param {string} obj.personalName - name of a user issuing a backup key
|
||||||
|
* @param {function} obj.setBackupKeyError - state function that turns true if there is an erorr with a backup key
|
||||||
|
* @param {function} obj.setBackupKeyIssued - state function that turns true if a backup key was issued correctly
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const issueBackupKey = async ({
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
personalName,
|
||||||
|
setBackupKeyError,
|
||||||
|
setBackupKeyIssued
|
||||||
|
}: BackupKeyProps) => {
|
||||||
|
try {
|
||||||
|
setBackupKeyError(false);
|
||||||
|
setBackupKeyIssued(false);
|
||||||
|
clientPassword.init(
|
||||||
|
{
|
||||||
|
username: email,
|
||||||
|
password: password
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
const clientPublicKey = clientPassword.getPublicKey();
|
||||||
|
|
||||||
|
let serverPublicKey, salt;
|
||||||
|
try {
|
||||||
|
const res = await SRP1({
|
||||||
|
clientPublicKey: clientPublicKey
|
||||||
|
});
|
||||||
|
serverPublicKey = res.serverPublicKey;
|
||||||
|
salt = res.salt;
|
||||||
|
} catch (err) {
|
||||||
|
setBackupKeyError(true);
|
||||||
|
console.log('Wrong current password', err, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
clientPassword.setSalt(salt);
|
||||||
|
clientPassword.setServerPublicKey(serverPublicKey);
|
||||||
|
const clientProof = clientPassword.getProof(); // called M1
|
||||||
|
|
||||||
|
const generatedKey = crypto.randomBytes(16).toString('hex');
|
||||||
|
|
||||||
|
clientKey.init(
|
||||||
|
{
|
||||||
|
username: email,
|
||||||
|
password: generatedKey
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
clientKey.createVerifier(
|
||||||
|
async (err: any, result: { salt: string; verifier: string }) => {
|
||||||
|
const { ciphertext, iv, tag } = Aes256Gcm.encrypt({
|
||||||
|
text: String(localStorage.getItem('PRIVATE_KEY')),
|
||||||
|
secret: generatedKey
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await issueBackupPrivateKey({
|
||||||
|
encryptedPrivateKey: ciphertext,
|
||||||
|
iv,
|
||||||
|
tag,
|
||||||
|
salt: result.salt,
|
||||||
|
verifier: result.verifier,
|
||||||
|
clientProof
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res?.status == 400) {
|
||||||
|
setBackupKeyError(true);
|
||||||
|
} else if (res?.status == 200) {
|
||||||
|
generateBackupPDF({
|
||||||
|
personalName,
|
||||||
|
personalEmail: email,
|
||||||
|
generatedKey
|
||||||
|
});
|
||||||
|
setBackupKeyIssued(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
setBackupKeyError(true);
|
||||||
|
console.log('Failed to issue a backup key');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default issueBackupKey;
|
||||||
@@ -6,21 +6,21 @@ const LINE =
|
|||||||
* @param {Buffer} src - source buffer
|
* @param {Buffer} src - source buffer
|
||||||
* @returns {String} text - text of buffer
|
* @returns {String} text - text of buffer
|
||||||
*/
|
*/
|
||||||
function parse(src) {
|
function parse(src: Buffer) {
|
||||||
const obj = {};
|
const obj: Record<string, string> = {};
|
||||||
|
|
||||||
// Convert buffer to string
|
// Convert buffer to string
|
||||||
let lines = src.toString();
|
let lines = src.toString();
|
||||||
|
|
||||||
// Convert line breaks to same format
|
// Convert line breaks to same format
|
||||||
lines = lines.replace(/\r\n?/gm, "\n");
|
lines = lines.replace(/\r\n?/gm, '\n');
|
||||||
|
|
||||||
let match;
|
let match;
|
||||||
while ((match = LINE.exec(lines)) != null) {
|
while ((match = LINE.exec(lines)) != null) {
|
||||||
const key = match[1];
|
const key = match[1];
|
||||||
|
|
||||||
// Default undefined or null to empty string
|
// Default undefined or null to empty string
|
||||||
let value = match[2] || "";
|
let value = match[2] || '';
|
||||||
|
|
||||||
// Remove whitespace
|
// Remove whitespace
|
||||||
value = value.trim();
|
value = value.trim();
|
||||||
@@ -29,12 +29,12 @@ function parse(src) {
|
|||||||
const maybeQuote = value[0];
|
const maybeQuote = value[0];
|
||||||
|
|
||||||
// Remove surrounding quotes
|
// Remove surrounding quotes
|
||||||
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");
|
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, '$2');
|
||||||
|
|
||||||
// Expand newlines if double quoted
|
// Expand newlines if double quoted
|
||||||
if (maybeQuote === '"') {
|
if (maybeQuote === '"') {
|
||||||
value = value.replace(/\\n/g, "\n");
|
value = value.replace(/\\n/g, '\n');
|
||||||
value = value.replace(/\\r/g, "\r");
|
value = value.replace(/\\r/g, '\r');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to object
|
// Add to object
|
||||||
+59
-49
File diff suppressed because one or more lines are too long
+5
-5
@@ -3,19 +3,19 @@
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const guidGenerator = () => {
|
const guidGenerator = () => {
|
||||||
var S4 = function () {
|
const S4 = function () {
|
||||||
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
|
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
S4() +
|
S4() +
|
||||||
S4() +
|
S4() +
|
||||||
"-" +
|
'-' +
|
||||||
S4() +
|
S4() +
|
||||||
"-" +
|
'-' +
|
||||||
S4() +
|
S4() +
|
||||||
"-" +
|
'-' +
|
||||||
S4() +
|
S4() +
|
||||||
"-" +
|
'-' +
|
||||||
S4() +
|
S4() +
|
||||||
S4() +
|
S4() +
|
||||||
S4()
|
S4()
|
||||||
+33
-27
@@ -1,22 +1,30 @@
|
|||||||
import getSecrets from "~/pages/api/files/GetSecrets";
|
import getSecrets from '~/pages/api/files/GetSecrets';
|
||||||
|
|
||||||
import { envMapping } from "../../../public/data/frequentConstants";
|
import { envMapping } from '../../../public/data/frequentConstants';
|
||||||
import guidGenerator from "../randomId";
|
import guidGenerator from '../randomId';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
decryptAssymmetric,
|
decryptAssymmetric,
|
||||||
decryptSymmetric,
|
decryptSymmetric
|
||||||
} = require("../cryptography/crypto");
|
} = require('../cryptography/crypto');
|
||||||
const nacl = require("tweetnacl");
|
const nacl = require('tweetnacl');
|
||||||
nacl.util = require("tweetnacl-util");
|
nacl.util = require('tweetnacl-util');
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
env: keyof typeof envMapping;
|
||||||
|
setFileState: any;
|
||||||
|
setIsKeyAvailable: any;
|
||||||
|
setData: any;
|
||||||
|
workspaceId: string;
|
||||||
|
}
|
||||||
|
|
||||||
const getSecretsForProject = async ({
|
const getSecretsForProject = async ({
|
||||||
env,
|
env,
|
||||||
setFileState,
|
setFileState,
|
||||||
setIsKeyAvailable,
|
setIsKeyAvailable,
|
||||||
setData,
|
setData,
|
||||||
workspaceId,
|
workspaceId
|
||||||
}) => {
|
}: Props) => {
|
||||||
try {
|
try {
|
||||||
let file;
|
let file;
|
||||||
try {
|
try {
|
||||||
@@ -24,44 +32,42 @@ const getSecretsForProject = async ({
|
|||||||
|
|
||||||
setFileState(file);
|
setFileState(file);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("ERROR: Not able to access the latest file");
|
console.log('ERROR: Not able to access the latest file');
|
||||||
}
|
}
|
||||||
// This is called isKeyAvilable but what it really means is if a person is able to create new key pairs
|
// This is called isKeyAvilable but what it really means is if a person is able to create new key pairs
|
||||||
setIsKeyAvailable(
|
setIsKeyAvailable(!file.key ? file.secrets.length == 0 : true);
|
||||||
!file.key ? (file.secrets.length == 0 ? true : false) : true
|
|
||||||
);
|
|
||||||
|
|
||||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
|
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY');
|
||||||
|
|
||||||
let tempFileState = [];
|
const tempFileState: { key: string; value: string; type: string }[] = [];
|
||||||
if (file.key) {
|
if (file.key) {
|
||||||
// assymmetrically decrypt symmetric key with local private key
|
// assymmetrically decrypt symmetric key with local private key
|
||||||
const key = decryptAssymmetric({
|
const key = decryptAssymmetric({
|
||||||
ciphertext: file.key.encryptedKey,
|
ciphertext: file.key.encryptedKey,
|
||||||
nonce: file.key.nonce,
|
nonce: file.key.nonce,
|
||||||
publicKey: file.key.sender.publicKey,
|
publicKey: file.key.sender.publicKey,
|
||||||
privateKey: PRIVATE_KEY,
|
privateKey: PRIVATE_KEY
|
||||||
});
|
});
|
||||||
|
|
||||||
file.secrets.map((secretPair) => {
|
file.secrets.map((secretPair: any) => {
|
||||||
// decrypt .env file with symmetric key
|
// decrypt .env file with symmetric key
|
||||||
const plainTextKey = decryptSymmetric({
|
const plainTextKey = decryptSymmetric({
|
||||||
ciphertext: secretPair.secretKey.ciphertext,
|
ciphertext: secretPair.secretKey.ciphertext,
|
||||||
iv: secretPair.secretKey.iv,
|
iv: secretPair.secretKey.iv,
|
||||||
tag: secretPair.secretKey.tag,
|
tag: secretPair.secretKey.tag,
|
||||||
key,
|
key
|
||||||
});
|
});
|
||||||
|
|
||||||
const plainTextValue = decryptSymmetric({
|
const plainTextValue = decryptSymmetric({
|
||||||
ciphertext: secretPair.secretValue.ciphertext,
|
ciphertext: secretPair.secretValue.ciphertext,
|
||||||
iv: secretPair.secretValue.iv,
|
iv: secretPair.secretValue.iv,
|
||||||
tag: secretPair.secretValue.tag,
|
tag: secretPair.secretValue.tag,
|
||||||
key,
|
key
|
||||||
});
|
});
|
||||||
tempFileState.push({
|
tempFileState.push({
|
||||||
key: plainTextKey,
|
key: plainTextKey,
|
||||||
value: plainTextValue,
|
value: plainTextValue,
|
||||||
type: secretPair.type,
|
type: secretPair.type
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -72,9 +78,9 @@ const getSecretsForProject = async ({
|
|||||||
return {
|
return {
|
||||||
id: guidGenerator(),
|
id: guidGenerator(),
|
||||||
pos: index,
|
pos: index,
|
||||||
key: line["key"],
|
key: line['key'],
|
||||||
value: line["value"],
|
value: line['value'],
|
||||||
type: line["type"]
|
type: line['type']
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -82,12 +88,12 @@ const getSecretsForProject = async ({
|
|||||||
return tempFileState.map((line, index) => [
|
return tempFileState.map((line, index) => [
|
||||||
guidGenerator(),
|
guidGenerator(),
|
||||||
index,
|
index,
|
||||||
line["key"],
|
line['key'],
|
||||||
line["value"],
|
line['value'],
|
||||||
line["type"],
|
line['type']
|
||||||
]);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Something went wrong during accessing or decripting secrets.");
|
console.log('Something went wrong during accessing or decripting secrets.');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import publicKeyInfical from "~/pages/api/auth/publicKeyInfisical";
|
|
||||||
import changeHerokuConfigVars from "~/pages/api/integrations/ChangeHerokuConfigVars";
|
|
||||||
|
|
||||||
const crypto = require("crypto");
|
|
||||||
const {
|
|
||||||
encryptSymmetric,
|
|
||||||
encryptAssymmetric,
|
|
||||||
} = require("../cryptography/crypto");
|
|
||||||
const nacl = require("tweetnacl");
|
|
||||||
nacl.util = require("tweetnacl-util");
|
|
||||||
|
|
||||||
const pushKeysIntegration = async ({ obj, integrationId }) => {
|
|
||||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
|
|
||||||
|
|
||||||
let randomBytes = crypto.randomBytes(16).toString("hex");
|
|
||||||
|
|
||||||
const secrets = Object.keys(obj).map((key) => {
|
|
||||||
// encrypt key
|
|
||||||
const {
|
|
||||||
ciphertext: ciphertextKey,
|
|
||||||
iv: ivKey,
|
|
||||||
tag: tagKey,
|
|
||||||
} = encryptSymmetric({
|
|
||||||
plaintext: key,
|
|
||||||
key: randomBytes,
|
|
||||||
});
|
|
||||||
|
|
||||||
// encrypt value
|
|
||||||
const {
|
|
||||||
ciphertext: ciphertextValue,
|
|
||||||
iv: ivValue,
|
|
||||||
tag: tagValue,
|
|
||||||
} = encryptSymmetric({
|
|
||||||
plaintext: obj[key],
|
|
||||||
key: randomBytes,
|
|
||||||
});
|
|
||||||
|
|
||||||
const visibility = "shared";
|
|
||||||
|
|
||||||
return {
|
|
||||||
ciphertextKey,
|
|
||||||
ivKey,
|
|
||||||
tagKey,
|
|
||||||
hashKey: crypto.createHash("sha256").update(key).digest("hex"),
|
|
||||||
ciphertextValue,
|
|
||||||
ivValue,
|
|
||||||
tagValue,
|
|
||||||
hashValue: crypto.createHash("sha256").update(obj[key]).digest("hex"),
|
|
||||||
type: visibility,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// obtain public keys of all receivers (i.e. members in workspace)
|
|
||||||
let publicKeyInfisical = await publicKeyInfical();
|
|
||||||
|
|
||||||
publicKeyInfisical = (await publicKeyInfisical.json()).publicKey;
|
|
||||||
|
|
||||||
// assymmetrically encrypt key with each receiver public keys
|
|
||||||
|
|
||||||
const { ciphertext, nonce } = encryptAssymmetric({
|
|
||||||
plaintext: randomBytes,
|
|
||||||
publicKey: publicKeyInfisical,
|
|
||||||
privateKey: PRIVATE_KEY,
|
|
||||||
});
|
|
||||||
|
|
||||||
const key = {
|
|
||||||
encryptedKey: ciphertext,
|
|
||||||
nonce,
|
|
||||||
};
|
|
||||||
|
|
||||||
changeHerokuConfigVars({ integrationId, key, secrets });
|
|
||||||
};
|
|
||||||
|
|
||||||
export default pushKeysIntegration;
|
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import publicKeyInfical from '~/pages/api/auth/publicKeyInfisical';
|
||||||
|
import changeHerokuConfigVars from '~/pages/api/integrations/ChangeHerokuConfigVars';
|
||||||
|
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const {
|
||||||
|
encryptSymmetric,
|
||||||
|
encryptAssymmetric
|
||||||
|
} = require('../cryptography/crypto');
|
||||||
|
const nacl = require('tweetnacl');
|
||||||
|
nacl.util = require('tweetnacl-util');
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
obj: Record<string, string>;
|
||||||
|
integrationId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushKeysIntegration = async ({ obj, integrationId }: Props) => {
|
||||||
|
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY');
|
||||||
|
|
||||||
|
const randomBytes = crypto.randomBytes(16).toString('hex');
|
||||||
|
|
||||||
|
const secrets = Object.keys(obj).map((key) => {
|
||||||
|
// encrypt key
|
||||||
|
const {
|
||||||
|
ciphertext: ciphertextKey,
|
||||||
|
iv: ivKey,
|
||||||
|
tag: tagKey
|
||||||
|
} = encryptSymmetric({
|
||||||
|
plaintext: key,
|
||||||
|
key: randomBytes
|
||||||
|
});
|
||||||
|
|
||||||
|
// encrypt value
|
||||||
|
const {
|
||||||
|
ciphertext: ciphertextValue,
|
||||||
|
iv: ivValue,
|
||||||
|
tag: tagValue
|
||||||
|
} = encryptSymmetric({
|
||||||
|
plaintext: obj[key],
|
||||||
|
key: randomBytes
|
||||||
|
});
|
||||||
|
|
||||||
|
const visibility = 'shared';
|
||||||
|
|
||||||
|
return {
|
||||||
|
ciphertextKey,
|
||||||
|
ivKey,
|
||||||
|
tagKey,
|
||||||
|
hashKey: crypto.createHash('sha256').update(key).digest('hex'),
|
||||||
|
ciphertextValue,
|
||||||
|
ivValue,
|
||||||
|
tagValue,
|
||||||
|
hashValue: crypto.createHash('sha256').update(obj[key]).digest('hex'),
|
||||||
|
type: visibility
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// obtain public keys of all receivers (i.e. members in workspace)
|
||||||
|
const publicKeyInfisical = await publicKeyInfical();
|
||||||
|
|
||||||
|
const publicKey = (await publicKeyInfisical.json()).publicKey;
|
||||||
|
|
||||||
|
// assymmetrically encrypt key with each receiver public keys
|
||||||
|
|
||||||
|
const { ciphertext, nonce } = encryptAssymmetric({
|
||||||
|
plaintext: randomBytes,
|
||||||
|
publicKey,
|
||||||
|
privateKey: PRIVATE_KEY
|
||||||
|
});
|
||||||
|
|
||||||
|
const key = {
|
||||||
|
encryptedKey: ciphertext,
|
||||||
|
nonce
|
||||||
|
};
|
||||||
|
|
||||||
|
changeHerokuConfigVars({ integrationId, key, secrets });
|
||||||
|
};
|
||||||
|
|
||||||
|
export default pushKeysIntegration;
|
||||||
Generated
+15
-15
@@ -29,7 +29,7 @@
|
|||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
"next": "^12.2.5",
|
"next": "^12.2.5",
|
||||||
"posthog-js": "^1.34.0",
|
"posthog-js": "^1.34.0",
|
||||||
"query-string": "^7.1.1",
|
"query-string": "^7.1.3",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
"react-code-input": "^3.10.1",
|
"react-code-input": "^3.10.1",
|
||||||
@@ -2467,9 +2467,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/decode-uri-component": {
|
"node_modules/decode-uri-component": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
||||||
"integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==",
|
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
@@ -5826,11 +5826,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/query-string": {
|
"node_modules/query-string": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
|
||||||
"integrity": "sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w==",
|
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"decode-uri-component": "^0.2.0",
|
"decode-uri-component": "^0.2.2",
|
||||||
"filter-obj": "^1.1.0",
|
"filter-obj": "^1.1.0",
|
||||||
"split-on-first": "^1.0.0",
|
"split-on-first": "^1.0.0",
|
||||||
"strict-uri-encode": "^2.0.0"
|
"strict-uri-encode": "^2.0.0"
|
||||||
@@ -9126,9 +9126,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"decode-uri-component": {
|
"decode-uri-component": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
||||||
"integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og=="
|
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
|
||||||
},
|
},
|
||||||
"deep-is": {
|
"deep-is": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
@@ -11477,11 +11477,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"query-string": {
|
"query-string": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
|
||||||
"integrity": "sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w==",
|
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"decode-uri-component": "^0.2.0",
|
"decode-uri-component": "^0.2.2",
|
||||||
"filter-obj": "^1.1.0",
|
"filter-obj": "^1.1.0",
|
||||||
"split-on-first": "^1.0.0",
|
"split-on-first": "^1.0.0",
|
||||||
"strict-uri-encode": "^2.0.0"
|
"strict-uri-encode": "^2.0.0"
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
"next": "^12.2.5",
|
"next": "^12.2.5",
|
||||||
"posthog-js": "^1.34.0",
|
"posthog-js": "^1.34.0",
|
||||||
"query-string": "^7.1.1",
|
"query-string": "^7.1.3",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
"react-code-input": "^3.10.1",
|
"react-code-input": "^3.10.1",
|
||||||
|
|||||||
+19
-10
@@ -1,4 +1,13 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
encryptedPrivateKey: string;
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
salt: string;
|
||||||
|
verifier: string;
|
||||||
|
clientProof: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the second step of the change password process (pake)
|
* This is the second step of the change password process (pake)
|
||||||
@@ -11,12 +20,12 @@ const changePassword2 = ({
|
|||||||
tag,
|
tag,
|
||||||
salt,
|
salt,
|
||||||
verifier,
|
verifier,
|
||||||
clientProof,
|
clientProof
|
||||||
}) => {
|
}: Props) => {
|
||||||
return SecurityClient.fetchCall("/api/v1/password/change-password", {
|
return SecurityClient.fetchCall('/api/v1/password/change-password', {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
clientProof: clientProof,
|
clientProof: clientProof,
|
||||||
@@ -24,13 +33,13 @@ const changePassword2 = ({
|
|||||||
iv: iv,
|
iv: iv,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
salt: salt,
|
salt: salt,
|
||||||
verifier: verifier,
|
verifier: verifier
|
||||||
}),
|
})
|
||||||
}).then(async (res) => {
|
}).then(async (res) => {
|
||||||
if (res.status == 200) {
|
if (res && res.status == 200) {
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
console.log("Failed to change the password");
|
console.log('Failed to change the password');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient.js";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is used to check if the user is authenticated.
|
|
||||||
* To do that, we get their tokens from cookies, and verify if they are good.
|
|
||||||
* @param {*} req
|
|
||||||
* @param {*} res
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const checkAuth = async (req, res) => {
|
|
||||||
return SecurityClient.fetchCall("/api/v1/auth/checkAuth", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}).then((res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
console.log("Not authorized");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default checkAuth;
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to check if the user is authenticated.
|
||||||
|
* To do that, we get their tokens from cookies, and verify if they are good.
|
||||||
|
*/
|
||||||
|
const checkAuth = async () => {
|
||||||
|
return SecurityClient.fetchCall('/api/v1/auth/checkAuth', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}).then((res) => {
|
||||||
|
if (res && res.status == 200) {
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
console.log('Not authorized');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default checkAuth;
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* This route check the verification code from the email that user just recieved
|
|
||||||
* @param {*} email
|
|
||||||
* @param {*} code
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const checkEmailVerificationCode = (email, code) => {
|
|
||||||
return fetch("/api/v1/signup/email/verify", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email: email,
|
|
||||||
code: code,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default checkEmailVerificationCode;
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
interface Props {
|
||||||
|
email: string;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This route check the verification code from the email that user just recieved
|
||||||
|
* @param {object} obj
|
||||||
|
* @param {string} obj.email
|
||||||
|
* @param {string} obj.code
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const checkEmailVerificationCode = ({ email, code }: Props) => {
|
||||||
|
return fetch('/api/v1/signup/email/verify', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: email,
|
||||||
|
code: code
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default checkEmailVerificationCode;
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
/**
|
|
||||||
* This function is called in the end of the signup process.
|
|
||||||
* It sends all the necessary nformation to the server.
|
|
||||||
* @param {*} email
|
|
||||||
* @param {*} firstName
|
|
||||||
* @param {*} lastName
|
|
||||||
* @param {*} workspace
|
|
||||||
* @param {*} publicKey
|
|
||||||
* @param {*} ciphertext
|
|
||||||
* @param {*} iv
|
|
||||||
* @param {*} tag
|
|
||||||
* @param {*} salt
|
|
||||||
* @param {*} verifier
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const completeAccountInformationSignup = ({
|
|
||||||
email,
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
organizationName,
|
|
||||||
publicKey,
|
|
||||||
ciphertext,
|
|
||||||
iv,
|
|
||||||
tag,
|
|
||||||
salt,
|
|
||||||
verifier,
|
|
||||||
token,
|
|
||||||
}) => {
|
|
||||||
return fetch("/api/v1/signup/complete-account/signup", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: "Bearer " + token,
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email,
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
publicKey,
|
|
||||||
encryptedPrivateKey: ciphertext,
|
|
||||||
organizationName,
|
|
||||||
iv,
|
|
||||||
tag,
|
|
||||||
salt,
|
|
||||||
verifier,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default completeAccountInformationSignup;
|
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
interface Props {
|
||||||
|
email: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
publicKey: string;
|
||||||
|
ciphertext: string;
|
||||||
|
organizationName: string;
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
salt: string;
|
||||||
|
verifier: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called in the end of the signup process.
|
||||||
|
* It sends all the necessary nformation to the server.
|
||||||
|
* @param {object} obj
|
||||||
|
* @param {string} obj.email - email of the user completing signup
|
||||||
|
* @param {string} obj.firstName - first name of the user completing signup
|
||||||
|
* @param {string} obj.lastName - last name of the user completing sign up
|
||||||
|
* @param {string} obj.organizationName - organization name for this user (usually, [FIRST_NAME]'s organization)
|
||||||
|
* @param {string} obj.publicKey - public key of the user completing signup
|
||||||
|
* @param {string} obj.ciphertext
|
||||||
|
* @param {string} obj.iv
|
||||||
|
* @param {string} obj.tag
|
||||||
|
* @param {string} obj.salt
|
||||||
|
* @param {string} obj.verifier
|
||||||
|
* @param {string} obj.token - token that confirms a user's identity
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const completeAccountInformationSignup = ({
|
||||||
|
email,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
organizationName,
|
||||||
|
publicKey,
|
||||||
|
ciphertext,
|
||||||
|
iv,
|
||||||
|
tag,
|
||||||
|
salt,
|
||||||
|
verifier,
|
||||||
|
token
|
||||||
|
}: Props) => {
|
||||||
|
return fetch('/api/v1/signup/complete-account/signup', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: 'Bearer ' + token
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
email,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
publicKey,
|
||||||
|
encryptedPrivateKey: ciphertext,
|
||||||
|
organizationName,
|
||||||
|
iv,
|
||||||
|
tag,
|
||||||
|
salt,
|
||||||
|
verifier
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default completeAccountInformationSignup;
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
/**
|
|
||||||
* This function is called in the end of the signup process.
|
|
||||||
* It sends all the necessary nformation to the server.
|
|
||||||
* @param {*} email
|
|
||||||
* @param {*} firstName
|
|
||||||
* @param {*} lastName
|
|
||||||
* @param {*} publicKey
|
|
||||||
* @param {*} ciphertext
|
|
||||||
* @param {*} iv
|
|
||||||
* @param {*} tag
|
|
||||||
* @param {*} salt
|
|
||||||
* @param {*} verifier
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const completeAccountInformationSignupInvite = ({
|
|
||||||
email,
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
publicKey,
|
|
||||||
ciphertext,
|
|
||||||
iv,
|
|
||||||
tag,
|
|
||||||
salt,
|
|
||||||
verifier,
|
|
||||||
token,
|
|
||||||
}) => {
|
|
||||||
return fetch("/api/v1/signup/complete-account/invite", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: "Bearer " + token,
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email: email,
|
|
||||||
firstName: firstName,
|
|
||||||
lastName: lastName,
|
|
||||||
publicKey: publicKey,
|
|
||||||
encryptedPrivateKey: ciphertext,
|
|
||||||
iv: iv,
|
|
||||||
tag: tag,
|
|
||||||
salt: salt,
|
|
||||||
verifier: verifier,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default completeAccountInformationSignupInvite;
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
interface Props {
|
||||||
|
email: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
publicKey: string;
|
||||||
|
ciphertext: string;
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
salt: string;
|
||||||
|
verifier: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called in the end of the signup process.
|
||||||
|
* It sends all the necessary nformation to the server.
|
||||||
|
* @param {object} obj
|
||||||
|
* @param {string} obj.email - email of the user completing signupinvite flow
|
||||||
|
* @param {string} obj.firstName - first name of the user completing signupinvite flow
|
||||||
|
* @param {string} obj.lastName - last name of the user completing signupinvite flow
|
||||||
|
* @param {string} obj.publicKey - public key of the user completing signupinvite flow
|
||||||
|
* @param {string} obj.ciphertext
|
||||||
|
* @param {string} obj.iv
|
||||||
|
* @param {string} obj.tag
|
||||||
|
* @param {string} obj.salt
|
||||||
|
* @param {string} obj.verifier
|
||||||
|
* @param {string} obj.token - token that confirms a user's identity
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const completeAccountInformationSignupInvite = ({
|
||||||
|
email,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
publicKey,
|
||||||
|
ciphertext,
|
||||||
|
iv,
|
||||||
|
tag,
|
||||||
|
salt,
|
||||||
|
verifier,
|
||||||
|
token
|
||||||
|
}: Props) => {
|
||||||
|
return fetch('/api/v1/signup/complete-account/invite', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: 'Bearer ' + token
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: email,
|
||||||
|
firstName: firstName,
|
||||||
|
lastName: lastName,
|
||||||
|
publicKey: publicKey,
|
||||||
|
encryptedPrivateKey: ciphertext,
|
||||||
|
iv: iv,
|
||||||
|
tag: tag,
|
||||||
|
salt: salt,
|
||||||
|
verifier: verifier
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default completeAccountInformationSignupInvite;
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the route that issues a backup private key that will afterwards be added into a pdf
|
|
||||||
*/
|
|
||||||
const issueBackupPrivateKey = ({
|
|
||||||
encryptedPrivateKey,
|
|
||||||
iv,
|
|
||||||
tag,
|
|
||||||
salt,
|
|
||||||
verifier,
|
|
||||||
clientProof,
|
|
||||||
}) => {
|
|
||||||
return SecurityClient.fetchCall(
|
|
||||||
"/api/v1/password/backup-private-key",
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
clientProof: clientProof,
|
|
||||||
encryptedPrivateKey: encryptedPrivateKey,
|
|
||||||
iv: iv,
|
|
||||||
tag: tag,
|
|
||||||
salt: salt,
|
|
||||||
verifier: verifier,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
).then((res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to issue the backup key");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default issueBackupPrivateKey;
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
encryptedPrivateKey: string;
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
salt: string;
|
||||||
|
verifier: string;
|
||||||
|
clientProof: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the route that issues a backup private key that will afterwards be added into a pdf
|
||||||
|
* @param {object} obj
|
||||||
|
* @param {string} obj.encryptedPrivateKey
|
||||||
|
* @param {string} obj.iv
|
||||||
|
* @param {string} obj.tag
|
||||||
|
* @param {string} obj.salt
|
||||||
|
* @param {string} obj.verifier
|
||||||
|
* @param {string} obj.clientProof
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const issueBackupPrivateKey = ({
|
||||||
|
encryptedPrivateKey,
|
||||||
|
iv,
|
||||||
|
tag,
|
||||||
|
salt,
|
||||||
|
verifier,
|
||||||
|
clientProof
|
||||||
|
}: Props) => {
|
||||||
|
return SecurityClient.fetchCall('/api/v1/password/backup-private-key', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
clientProof: clientProof,
|
||||||
|
encryptedPrivateKey: encryptedPrivateKey,
|
||||||
|
iv: iv,
|
||||||
|
tag: tag,
|
||||||
|
salt: salt,
|
||||||
|
verifier: verifier
|
||||||
|
})
|
||||||
|
}).then((res) => {
|
||||||
|
if (res?.status !== 200) {
|
||||||
|
console.log('Failed to issue the backup key');
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default issueBackupPrivateKey;
|
||||||
@@ -1,29 +1,29 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This route logs the user out. Note: the user should authorized to do this.
|
* This route logs the user out. Note: the user should authorized to do this.
|
||||||
* We first try to log out - if the authorization fails (response.status = 401), we refetch the new token, and then retry
|
* We first try to log out - if the authorization fails (response.status = 401), we refetch the new token, and then retry
|
||||||
*/
|
*/
|
||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
return SecurityClient.fetchCall("/api/v1/auth/logout", {
|
return SecurityClient.fetchCall('/api/v1/auth/logout', {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
credentials: "include",
|
credentials: 'include'
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (res?.status == 200) {
|
if (res?.status == 200) {
|
||||||
SecurityClient.setToken("");
|
SecurityClient.setToken('');
|
||||||
// Delete the cookie by not setting a value; Alternatively clear the local storage
|
// Delete the cookie by not setting a value; Alternatively clear the local storage
|
||||||
localStorage.setItem("publicKey", "");
|
localStorage.setItem('publicKey', '');
|
||||||
localStorage.setItem("encryptedPrivateKey", "");
|
localStorage.setItem('encryptedPrivateKey', '');
|
||||||
localStorage.setItem("iv", "");
|
localStorage.setItem('iv', '');
|
||||||
localStorage.setItem("tag", "");
|
localStorage.setItem('tag', '');
|
||||||
localStorage.setItem("PRIVATE_KEY", "");
|
localStorage.setItem('PRIVATE_KEY', '');
|
||||||
console.log("User logged out", res);
|
console.log('User logged out', res);
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
console.log("Failed to log out");
|
console.log('Failed to log out');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the first step of the change password process (pake)
|
|
||||||
* @param {*} clientPublicKey
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const SRP1 = ({ clientPublicKey }) => {
|
|
||||||
return SecurityClient.fetchCall("/api/v1/password/srp1", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
clientPublicKey,
|
|
||||||
}),
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return await res.json();
|
|
||||||
} else {
|
|
||||||
console.log("Failed to do the first step of SRP");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SRP1;
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
clientPublicKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the first step of the change password process (pake)
|
||||||
|
* @param {string} clientPublicKey
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const SRP1 = ({ clientPublicKey }: Props) => {
|
||||||
|
return SecurityClient.fetchCall('/api/v1/password/srp1', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
clientPublicKey
|
||||||
|
})
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res && res.status == 200) {
|
||||||
|
return await res.json();
|
||||||
|
} else {
|
||||||
|
console.log('Failed to do the first step of SRP');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SRP1;
|
||||||
+6
-6
@@ -2,15 +2,15 @@
|
|||||||
* This route send the verification email to the user's email (contains a 6-digit verification code)
|
* This route send the verification email to the user's email (contains a 6-digit verification code)
|
||||||
* @param {*} email
|
* @param {*} email
|
||||||
*/
|
*/
|
||||||
const sendVerificationEmail = (email) => {
|
const sendVerificationEmail = (email: string) => {
|
||||||
fetch("/api/v1/signup/email/signup", {
|
fetch('/api/v1/signup/email/signup', {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
email: email,
|
email: email
|
||||||
}),
|
})
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
const token = async (req, res) => {
|
|
||||||
return fetch("/api/v1/auth/token", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
credentials: "include",
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return (await res.json()).token;
|
|
||||||
} else {
|
|
||||||
console.log("Getting a new token failed");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default token;
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
const token = async () => {
|
||||||
|
return fetch('/api/v1/auth/token', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
credentials: 'include'
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status == 200) {
|
||||||
|
return (await res.json()).token;
|
||||||
|
} else {
|
||||||
|
console.log('Getting a new token failed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default token;
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* This route verifies the signup invite link
|
|
||||||
* @param {*} email
|
|
||||||
* @param {*} code
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const verifySignupInvite = ({ email, code }) => {
|
|
||||||
return fetch("/api/v1/invite-org/verify", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email,
|
|
||||||
code,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default verifySignupInvite;
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
interface Props {
|
||||||
|
email: string;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This route verifies the signup invite link
|
||||||
|
* @param {object} obj
|
||||||
|
* @param {string} obj.email - email that a user is trying to verify
|
||||||
|
* @param {string} obj.code - code that a user received to the abovementioned email
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const verifySignupInvite = ({ email, code }: Props) => {
|
||||||
|
return fetch('/api/v1/invite-org/verify', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
email,
|
||||||
|
code
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default verifySignupInvite;
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
/**
|
|
||||||
* This route lets us get the public key of infisical. Th euser doesn't have to be authenticated since this is just the public key.
|
|
||||||
* @param {*} req
|
|
||||||
* @param {*} res
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const publicKeyInfisical = (req, res) => {
|
|
||||||
return fetch("/api/v1/key/publicKey/infisical", {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default publicKeyInfisical;
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
const publicKeyInfisical = () => {
|
||||||
|
return fetch('/api/v1/key/publicKey/infisical', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default publicKeyInfisical;
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient.js";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function fetches the encrypted secrets from the .env file
|
|
||||||
* @param {*} workspaceId
|
|
||||||
* @param {*} env
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const getSecrets = async (workspaceId, env) => {
|
|
||||||
return SecurityClient.fetchCall(
|
|
||||||
"/api/v1/secret/" +
|
|
||||||
workspaceId +
|
|
||||||
"?" +
|
|
||||||
new URLSearchParams({
|
|
||||||
environment: env,
|
|
||||||
channel: "web",
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return await res.json();
|
|
||||||
} else {
|
|
||||||
console.log("Failed to get project secrets");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getSecrets;
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function fetches the encrypted secrets from the .env file
|
||||||
|
* @param {string} workspaceId - project is for which a user is trying to get secrets
|
||||||
|
* @param {string} env - environment of a project for which a user is trying ot get secrets
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const getSecrets = async (workspaceId: string, env: string) => {
|
||||||
|
return SecurityClient.fetchCall(
|
||||||
|
'/api/v1/secret/' +
|
||||||
|
workspaceId +
|
||||||
|
'?' +
|
||||||
|
new URLSearchParams({
|
||||||
|
environment: env,
|
||||||
|
channel: 'web'
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).then(async (res) => {
|
||||||
|
if (res && res.status == 200) {
|
||||||
|
return await res.json();
|
||||||
|
} else {
|
||||||
|
console.log('Failed to get project secrets');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getSecrets;
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function uploads the encrypted .env file
|
|
||||||
* @param {*} req
|
|
||||||
* @param {*} res
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const uploadSecrets = async ({ workspaceId, secrets, keys, environment }) => {
|
|
||||||
return SecurityClient.fetchCall("/api/v1/secret/" + workspaceId, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
secrets,
|
|
||||||
keys,
|
|
||||||
environment,
|
|
||||||
channel: "web",
|
|
||||||
}),
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to push secrets");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default uploadSecrets;
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
workspaceId: string;
|
||||||
|
secrets: any;
|
||||||
|
keys: string;
|
||||||
|
environment: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function uploads the encrypted .env file
|
||||||
|
* @param {object} obj
|
||||||
|
* @param {string} obj.workspaceId
|
||||||
|
* @param {} obj.secrets
|
||||||
|
* @param {} obj.keys
|
||||||
|
* @param {string} obj.environment
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const uploadSecrets = async ({
|
||||||
|
workspaceId,
|
||||||
|
secrets,
|
||||||
|
keys,
|
||||||
|
environment
|
||||||
|
}: Props) => {
|
||||||
|
return SecurityClient.fetchCall('/api/v1/secret/' + workspaceId, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
secrets,
|
||||||
|
keys,
|
||||||
|
environment,
|
||||||
|
channel: 'web'
|
||||||
|
})
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res && res.status == 200) {
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
console.log('Failed to push secrets');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default uploadSecrets;
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
|
||||||
|
|
||||||
const changeHerokuConfigVars = ({ integrationId, key, secrets }) => {
|
|
||||||
return SecurityClient.fetchCall(
|
|
||||||
"/api/v1/integration/" + integrationId + "/sync",
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
key,
|
|
||||||
secrets,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to sync secrets to Heroku");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default changeHerokuConfigVars;
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
integrationId: string;
|
||||||
|
key: { encryptedKey: any; nonce: any };
|
||||||
|
secrets: {
|
||||||
|
ciphertextKey: any;
|
||||||
|
ivKey: any;
|
||||||
|
tagKey: any;
|
||||||
|
hashKey: any;
|
||||||
|
ciphertextValue: any;
|
||||||
|
ivValue: any;
|
||||||
|
tagValue: any;
|
||||||
|
hashValue: any;
|
||||||
|
type: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeHerokuConfigVars = ({ integrationId, key, secrets }: Props) => {
|
||||||
|
return SecurityClient.fetchCall(
|
||||||
|
'/api/v1/integration/' + integrationId + '/sync',
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
key,
|
||||||
|
secrets
|
||||||
|
})
|
||||||
|
}
|
||||||
|
).then(async (res) => {
|
||||||
|
if (res && res.status == 200) {
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
console.log('Failed to sync secrets to Heroku');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default changeHerokuConfigVars;
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route deletes an integration from a certain project
|
|
||||||
* @param {*} integrationId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const deleteIntegration = ({ integrationId }) => {
|
|
||||||
return SecurityClient.fetchCall(
|
|
||||||
"/api/v1/integration/" + integrationId,
|
|
||||||
{
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return (await res.json()).workspace;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to delete an integration");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default deleteIntegration;
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
integrationId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This route deletes an integration from a certain project
|
||||||
|
* @param {*} integrationId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const deleteIntegration = ({ integrationId }: Props) => {
|
||||||
|
return SecurityClient.fetchCall('/api/v1/integration/' + integrationId, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res && res.status == 200) {
|
||||||
|
return (await res.json()).workspace;
|
||||||
|
} else {
|
||||||
|
console.log('Failed to delete an integration');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default deleteIntegration;
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route deletes an integration authorization from a certain project
|
|
||||||
* @param {*} integrationAuthId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const deleteIntegrationAuth = ({ integrationAuthId }) => {
|
|
||||||
return SecurityClient.fetchCall(
|
|
||||||
"/api/v1/integration-auth/" + integrationAuthId,
|
|
||||||
{
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to delete an integration authorization");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default deleteIntegrationAuth;
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
integrationAuthId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This route deletes an integration authorization from a certain project
|
||||||
|
* @param {*} integrationAuthId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const deleteIntegrationAuth = ({ integrationAuthId }: Props) => {
|
||||||
|
return SecurityClient.fetchCall(
|
||||||
|
'/api/v1/integration-auth/' + integrationAuthId,
|
||||||
|
{
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).then(async (res) => {
|
||||||
|
if (res && res.status == 200) {
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
console.log('Failed to delete an integration authorization');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default deleteIntegrationAuth;
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
|
||||||
|
|
||||||
const getIntegrationApps = ({ integrationAuthId }) => {
|
|
||||||
return SecurityClient.fetchCall(
|
|
||||||
"/api/v1/integration-auth/" + integrationAuthId + "/apps",
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return (await res.json()).apps;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to get available apps for an integration");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getIntegrationApps;
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
integrationAuthId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getIntegrationApps = ({ integrationAuthId }: Props) => {
|
||||||
|
return SecurityClient.fetchCall(
|
||||||
|
'/api/v1/integration-auth/' + integrationAuthId + '/apps',
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).then(async (res) => {
|
||||||
|
if (res && res.status == 200) {
|
||||||
|
return (await res.json()).apps;
|
||||||
|
} else {
|
||||||
|
console.log('Failed to get available apps for an integration');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getIntegrationApps;
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
|
||||||
|
|
||||||
const getIntegrations = () => {
|
|
||||||
return SecurityClient.fetchCall("/api/v1/integration/integrations", {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return (await res.json()).integrations;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to get project integrations");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getIntegrations;
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
const getIntegrations = () => {
|
||||||
|
return SecurityClient.fetchCall('/api/v1/integration/integrations', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res && res.status == 200) {
|
||||||
|
return (await res.json()).integrations;
|
||||||
|
} else {
|
||||||
|
console.log('Failed to get project integrations');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getIntegrations;
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route starts the integration after teh default one if gonna set up.
|
|
||||||
* @param {*} integrationId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const startIntegration = ({ integrationId, appName, environment }) => {
|
|
||||||
return SecurityClient.fetchCall(
|
|
||||||
"/api/v1/integration/" + integrationId,
|
|
||||||
{
|
|
||||||
method: "PATCH",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
update: {
|
|
||||||
app: appName,
|
|
||||||
environment,
|
|
||||||
isActive: true,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to start an integration");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default startIntegration;
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
integrationId: string;
|
||||||
|
appName: string;
|
||||||
|
environment: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This route starts the integration after teh default one if gonna set up.
|
||||||
|
* @param {*} integrationId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const startIntegration = ({ integrationId, appName, environment }: Props) => {
|
||||||
|
return SecurityClient.fetchCall('/api/v1/integration/' + integrationId, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
update: {
|
||||||
|
app: appName,
|
||||||
|
environment,
|
||||||
|
isActive: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res && res.status == 200) {
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
console.log('Failed to start an integration');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default startIntegration;
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the first step of the change password process (pake)
|
|
||||||
* @param {*} clientPublicKey
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const AuthorizeIntegration = ({ workspaceId, code, integration }) => {
|
|
||||||
return SecurityClient.fetchCall(
|
|
||||||
"/api/v1/integration-auth/oauth-token",
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
workspaceId,
|
|
||||||
code,
|
|
||||||
integration,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to authorize the integration");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AuthorizeIntegration;
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
workspaceId: string;
|
||||||
|
code: string;
|
||||||
|
integration: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This is the first step of the change password process (pake)
|
||||||
|
* @param {object} obj
|
||||||
|
* @param {object} obj.workspaceId - project id for which we want to authorize the integration
|
||||||
|
* @param {object} obj.code
|
||||||
|
* @param {object} obj.integration - integration which a user is trying to turn on
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const AuthorizeIntegration = ({ workspaceId, code, integration }: Props) => {
|
||||||
|
return SecurityClient.fetchCall('/api/v1/integration-auth/oauth-token', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
workspaceId,
|
||||||
|
code,
|
||||||
|
integration
|
||||||
|
})
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res && res.status == 200) {
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
console.log('Failed to authorize the integration');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AuthorizeIntegration;
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route gets authorizations of a certain project (Heroku, etc.)
|
|
||||||
* @param {*} workspaceId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const getWorkspaceAuthorizations = ({ workspaceId }) => {
|
|
||||||
return SecurityClient.fetchCall(
|
|
||||||
"/api/v1/workspace/" + workspaceId + "/authorizations",
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return (await res.json()).authorizations;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to get project authorizations");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getWorkspaceAuthorizations;
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import SecurityClient from '~/utilities/SecurityClient';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
workspaceId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This route gets authorizations of a certain project (Heroku, etc.)
|
||||||
|
* @param {*} workspaceId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const getWorkspaceAuthorizations = ({ workspaceId }: Props) => {
|
||||||
|
return SecurityClient.fetchCall(
|
||||||
|
'/api/v1/workspace/' + workspaceId + '/authorizations',
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).then(async (res) => {
|
||||||
|
if (res && res.status == 200) {
|
||||||
|
return (await res.json()).authorizations;
|
||||||
|
} else {
|
||||||
|
console.log('Failed to get project authorizations');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getWorkspaceAuthorizations;
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import SecurityClient from "~/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route gets integrations of a certain project (Heroku, etc.)
|
|
||||||
* @param {*} workspaceId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const getWorkspaceIntegrations = ({ workspaceId }) => {
|
|
||||||
return SecurityClient.fetchCall(
|
|
||||||
"/api/v1/workspace/" + workspaceId + "/integrations",
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
).then(async (res) => {
|
|
||||||
if (res.status == 200) {
|
|
||||||
return (await res.json()).integrations;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to get the project integrations");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getWorkspaceIntegrations;
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user