1
0
mirror of https://gitlab.com/psono/psono-client synced 2025-04-19 03:22:16 +03:00

some more progress

Signed-off-by: Sascha Pfeiffer <sascha.pfeiffer@psono.com>
This commit is contained in:
Sascha Pfeiffer 2021-12-21 10:28:37 +01:00
parent 9b9ede2f21
commit 78698f0001
105 changed files with 5200 additions and 1695 deletions

View File

@ -1,8 +1,10 @@
{
"presets": [
"@babel/preset-env", "@babel/preset-react"
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
["@babel/plugin-transform-runtime"],
[
"@babel/plugin-proposal-class-properties"
]

168
package-lock.json generated
View File

@ -9,11 +9,13 @@
"version": "0.1.0",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.16.7",
"@date-io/date-fns": "^1.3.13",
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",
"@material-ui/pickers": "^3.3.10",
"@openpgp/hkp-client": "^0.0.2",
"axios": "^0.24.0",
"babel-preset-react-app": "3",
"clientjs": "^0.2.1",
@ -33,11 +35,13 @@
"js-sha512": "^0.8.0",
"localforage": "^1.10.0",
"mui-datatables": "^3.8.2",
"openpgp": "^5.0.1",
"otpauth": "^7.0.7",
"papaparse": "^5.3.1",
"qrcode": "^1.5.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-fastclick": "^3.0.2",
"react-fontawesome": "^1.6.1",
"react-i18next": "^11.13.0",
"react-redux": "^7.2.6",
@ -51,6 +55,7 @@
"uuid-js": "^0.7.5"
},
"devDependencies": {
"@babel/plugin-transform-runtime": "^7.16.7",
"@babel/preset-env": "^7.16.0",
"@babel/preset-react": "^7.16.0",
"babel-cli": "^6.26.0",
@ -457,10 +462,11 @@
}
},
"node_modules/@babel/helper-module-imports": {
"version": "7.16.0",
"license": "MIT",
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
"integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
"dependencies": {
"@babel/types": "^7.16.0"
"@babel/types": "^7.16.7"
},
"engines": {
"node": ">=6.9.0"
@ -496,9 +502,9 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
"version": "7.16.5",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz",
"integrity": "sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ==",
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz",
"integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==",
"engines": {
"node": ">=6.9.0"
}
@ -562,8 +568,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.15.7",
"license": "MIT",
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
"integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==",
"engines": {
"node": ">=6.9.0"
}
@ -1674,12 +1681,12 @@
}
},
"node_modules/@babel/plugin-transform-runtime": {
"version": "7.16.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.5.tgz",
"integrity": "sha512-gxpfS8XQWDbQ8oP5NcmpXxtEgCJkbO+W9VhZlOhr0xPyVaRjAQPOv7ZDj9fg0d5s9+NiVvMCE6gbkEkcsxwGRw==",
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.7.tgz",
"integrity": "sha512-2FoHiSAWkdq4L06uaDN3rS43i6x28desUVxq+zAFuE6kbWYQeiLPJI5IC7Sg9xKYVcrBKSQkVUfH6aeQYbl9QA==",
"dependencies": {
"@babel/helper-module-imports": "^7.16.0",
"@babel/helper-plugin-utils": "^7.16.5",
"@babel/helper-module-imports": "^7.16.7",
"@babel/helper-plugin-utils": "^7.16.7",
"babel-plugin-polyfill-corejs2": "^0.3.0",
"babel-plugin-polyfill-corejs3": "^0.4.0",
"babel-plugin-polyfill-regenerator": "^0.3.0",
@ -1959,9 +1966,9 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.16.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz",
"integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==",
"dependencies": {
"regenerator-runtime": "^0.13.4"
},
@ -2047,10 +2054,11 @@
"license": "MIT"
},
"node_modules/@babel/types": {
"version": "7.16.0",
"license": "MIT",
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.7.tgz",
"integrity": "sha512-E8HuV7FO9qLpx6OtoGfUQ2cjIYnbFwvZWYBS+87EwtdMvmUPJSwykpovFB+8insbpF0uJcpr8KMUi64XZntZcg==",
"dependencies": {
"@babel/helper-validator-identifier": "^7.15.7",
"@babel/helper-validator-identifier": "^7.16.7",
"to-fast-properties": "^2.0.0"
},
"engines": {
@ -3249,6 +3257,14 @@
"node": ">= 8"
}
},
"node_modules/@openpgp/hkp-client": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/@openpgp/hkp-client/-/hkp-client-0.0.2.tgz",
"integrity": "sha512-hA71RhqfLfNltZsy/USTQehE2QAVB3eK4xx8p76XtFJy5Zg6gK2XbZvOC/x/yG8i2Ipbyul1DMTMxH9v8rfPKw==",
"dependencies": {
"node-fetch": "^2.6.1"
}
},
"node_modules/@pmmmwh/react-refresh-webpack-plugin": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.3.tgz",
@ -4882,6 +4898,17 @@
"safer-buffer": "~2.1.0"
}
},
"node_modules/asn1.js": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
"dependencies": {
"bn.js": "^4.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"safer-buffer": "^2.1.0"
}
},
"node_modules/assert-plus": {
"version": "1.0.0",
"dev": true,
@ -6245,6 +6272,11 @@
"version": "3.7.2",
"license": "MIT"
},
"node_modules/bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
},
"node_modules/body-parser": {
"version": "1.19.0",
"license": "MIT",
@ -17419,6 +17451,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/openpgp": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/openpgp/-/openpgp-5.0.1.tgz",
"integrity": "sha512-J9HGIcXumwczJwX3JvgshWYtkhsOJHm5ZPd1ipJ1BqrZL06NgqV/EfJyF3ThOlNV2rY0MGWdS8L8/kKyeo3sXg==",
"dependencies": {
"asn1.js": "^5.0.0"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@ -19799,6 +19842,17 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz",
"integrity": "sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA=="
},
"node_modules/react-fastclick": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/react-fastclick/-/react-fastclick-3.0.2.tgz",
"integrity": "sha1-KZTGAIjNqQsLLL+sS258a8c9ajo=",
"engines": {
"node": "*"
},
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-fontawesome": {
"version": "1.6.1",
"license": "MIT",
@ -25007,9 +25061,11 @@
}
},
"@babel/helper-module-imports": {
"version": "7.16.0",
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
"integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
"requires": {
"@babel/types": "^7.16.0"
"@babel/types": "^7.16.7"
}
},
"@babel/helper-module-transforms": {
@ -25036,9 +25092,9 @@
}
},
"@babel/helper-plugin-utils": {
"version": "7.16.5",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz",
"integrity": "sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ=="
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz",
"integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA=="
},
"@babel/helper-remap-async-to-generator": {
"version": "7.16.5",
@ -25081,7 +25137,9 @@
}
},
"@babel/helper-validator-identifier": {
"version": "7.15.7"
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
"integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
},
"@babel/helper-validator-option": {
"version": "7.14.5"
@ -25768,12 +25826,12 @@
}
},
"@babel/plugin-transform-runtime": {
"version": "7.16.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.5.tgz",
"integrity": "sha512-gxpfS8XQWDbQ8oP5NcmpXxtEgCJkbO+W9VhZlOhr0xPyVaRjAQPOv7ZDj9fg0d5s9+NiVvMCE6gbkEkcsxwGRw==",
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.7.tgz",
"integrity": "sha512-2FoHiSAWkdq4L06uaDN3rS43i6x28desUVxq+zAFuE6kbWYQeiLPJI5IC7Sg9xKYVcrBKSQkVUfH6aeQYbl9QA==",
"requires": {
"@babel/helper-module-imports": "^7.16.0",
"@babel/helper-plugin-utils": "^7.16.5",
"@babel/helper-module-imports": "^7.16.7",
"@babel/helper-plugin-utils": "^7.16.7",
"babel-plugin-polyfill-corejs2": "^0.3.0",
"babel-plugin-polyfill-corejs3": "^0.4.0",
"babel-plugin-polyfill-regenerator": "^0.3.0",
@ -25973,9 +26031,9 @@
}
},
"@babel/runtime": {
"version": "7.16.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz",
"integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==",
"requires": {
"regenerator-runtime": "^0.13.4"
},
@ -26037,9 +26095,11 @@
}
},
"@babel/types": {
"version": "7.16.0",
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.7.tgz",
"integrity": "sha512-E8HuV7FO9qLpx6OtoGfUQ2cjIYnbFwvZWYBS+87EwtdMvmUPJSwykpovFB+8insbpF0uJcpr8KMUi64XZntZcg==",
"requires": {
"@babel/helper-validator-identifier": "^7.15.7",
"@babel/helper-validator-identifier": "^7.16.7",
"to-fast-properties": "^2.0.0"
},
"dependencies": {
@ -26852,6 +26912,14 @@
"fastq": "^1.6.0"
}
},
"@openpgp/hkp-client": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/@openpgp/hkp-client/-/hkp-client-0.0.2.tgz",
"integrity": "sha512-hA71RhqfLfNltZsy/USTQehE2QAVB3eK4xx8p76XtFJy5Zg6gK2XbZvOC/x/yG8i2Ipbyul1DMTMxH9v8rfPKw==",
"requires": {
"node-fetch": "^2.6.1"
}
},
"@pmmmwh/react-refresh-webpack-plugin": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.3.tgz",
@ -27919,6 +27987,17 @@
"safer-buffer": "~2.1.0"
}
},
"asn1.js": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
"requires": {
"bn.js": "^4.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"safer-buffer": "^2.1.0"
}
},
"assert-plus": {
"version": "1.0.0",
"dev": true
@ -29005,6 +29084,11 @@
"bluebird": {
"version": "3.7.2"
},
"bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
},
"body-parser": {
"version": "1.19.0",
"requires": {
@ -36764,6 +36848,14 @@
"is-wsl": "^2.2.0"
}
},
"openpgp": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/openpgp/-/openpgp-5.0.1.tgz",
"integrity": "sha512-J9HGIcXumwczJwX3JvgshWYtkhsOJHm5ZPd1ipJ1BqrZL06NgqV/EfJyF3ThOlNV2rY0MGWdS8L8/kKyeo3sXg==",
"requires": {
"asn1.js": "^5.0.0"
}
},
"optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@ -38281,6 +38373,12 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz",
"integrity": "sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA=="
},
"react-fastclick": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/react-fastclick/-/react-fastclick-3.0.2.tgz",
"integrity": "sha1-KZTGAIjNqQsLLL+sS258a8c9ajo=",
"requires": {}
},
"react-fontawesome": {
"version": "1.6.1",
"requires": {

View File

@ -22,11 +22,13 @@
"keywords": [],
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.16.7",
"@date-io/date-fns": "^1.3.13",
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",
"@material-ui/pickers": "^3.3.10",
"@openpgp/hkp-client": "^0.0.2",
"axios": "^0.24.0",
"babel-preset-react-app": "3",
"clientjs": "^0.2.1",
@ -46,11 +48,13 @@
"js-sha512": "^0.8.0",
"localforage": "^1.10.0",
"mui-datatables": "^3.8.2",
"openpgp": "^5.0.1",
"otpauth": "^7.0.7",
"papaparse": "^5.3.1",
"qrcode": "^1.5.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-fastclick": "^3.0.2",
"react-fontawesome": "^1.6.1",
"react-i18next": "^11.13.0",
"react-redux": "^7.2.6",
@ -64,6 +68,7 @@
"uuid-js": "^0.7.5"
},
"devDependencies": {
"@babel/plugin-transform-runtime": "^7.16.7",
"@babel/preset-env": "^7.16.0",
"@babel/preset-react": "^7.16.0",
"babel-cli": "^6.26.0",

View File

@ -84,8 +84,7 @@ body {
border-color: #151f2b; }
.path_box_parent {
margin-bottom: 5px;
font-family: 'Fira Code', monospace; }
margin-bottom: 5px; }
.path_box_parent .path_box_breadcrumb {
cursor: pointer; }
.path_box_parent .disabled {

View File

@ -1,7 +1,7 @@
/*fbfbfc*/
/* Generic styles */
html {
height: 100%; }
height: 100%;}
body {
font-family: 'Open Sans', sans-serif;
position: relative;
@ -14,6 +14,10 @@ body {
overflow: hidden;
}
html, body {
-webkit-tap-highlight-color: transparent;
}
h1, h2 {
font-size: 20px;
margin: 0; }
@ -48,9 +52,6 @@ a {
.btn-link {
color: #151f2b; }
.monospace {
font-family: 'Fira Code', monospace; }
p.horizontalline {
width: 100%;
text-align: center;
@ -313,10 +314,6 @@ p.horizontalline span {
border-top-color: #2ba880;
border-left-color: #2ba880; }
.text-center {
text-align: center; }
.progress-box {
width: 340px;
padding: 20px;

View File

@ -1,4 +1,14 @@
{
"EDIT_WEBSITE_PASSWORD": "Webseiten Passwort bearbeiten",
"EDIT_APPLICATION_PASSWORD": "Applikations Passwort bearbeiten",
"EDIT_TOTP_AUTHENTICATOR": "TOTP Authenticator bearbeiten",
"EDIT_NOTE": "Notiz bearbeiten",
"EDIT_ENVIRONMENT_VARIABLES": "Umgebungsvariablen bearbeiten",
"EDIT_GPG_KEY": "GPG Key bearbeiten",
"EDIT_BOOKMARK": "Bookmark bearbeiten",
"EDIT_FILE": "Datei bearbeiten",
"USER_NOT_FOUND": "Benutzer nicht gefunden",
"ACCEPT_SHARE": "Share annehmen",
"DATE_TIME_YYYY_MM_DD_HH_MM": "dd.MM.yyyy HH:mm",
"TABLE_BODY_NO_MATCH": "Entschulding, keine übereinstimmenden Einträge gefunden",
"TABLE_BODY_TOOL_TIP": "Sortieren",

View File

@ -1,4 +1,14 @@
{
"EDIT_WEBSITE_PASSWORD": "Edit Website Password",
"EDIT_APPLICATION_PASSWORD": "Edit Application Password",
"EDIT_TOTP_AUTHENTICATOR": "Edit TOTP Authenticator",
"EDIT_NOTE": "Edit Note",
"EDIT_ENVIRONMENT_VARIABLES": "Edit Environment Variables",
"EDIT_GPG_KEY": "Edit GPG Key",
"EDIT_BOOKMARK": "Edit Bookmark",
"EDIT_FILE": "Edit File",
"USER_NOT_FOUND": "User not found",
"ACCEPT_SHARE": "Accept Share",
"DATE_TIME_YYYY_MM_DD_HH_MM": "yyyy/MM/dd HH:mm",
"TABLE_BODY_NO_MATCH": "Sorry, no matching records found",
"TABLE_BODY_TOOL_TIP": "Sort",

View File

@ -23,6 +23,7 @@ import {
SET_REMOTE_CONFIG_JSON,
SET_FINGERPRINT,
SET_EMAIL,
SET_HIDE_DOWNLOAD_BANNER,
} from "./action-types";
import datastoreSettingService from "../services/datastore-setting";
@ -163,6 +164,14 @@ function setDisableBrowserPm(disableBrowserPm) {
});
};
}
function setHideDownloadBanner(hideDownloadBanner) {
return (dispatch) => {
dispatch({
type: SET_HIDE_DOWNLOAD_BANNER,
hideDownloadBanner,
});
};
}
function settingsDatastoreLoaded(data) {
return (dispatch) => {
dispatch({
@ -285,6 +294,7 @@ const actionCreators = {
enableOfflineMode,
setNotificationOnCopy,
setDisableBrowserPm,
setHideDownloadBanner,
setPasswordConfig,
setGpgConfig,
settingsDatastoreLoaded,

View File

@ -16,6 +16,7 @@ export const ENABLE_OFFLINE_MODE = "ENABLE_OFFLINE_MODE";
export const DISABLE_OFFLINE_MODE = "DISABLE_OFFLINE_MODE";
export const SET_NOTIFICATION_ON_COPY = "SET_NOTIFICATION_ON_COPY";
export const SET_DISABLE_BROWSER_PM = "SET_DISABLE_BROWSER_PM";
export const SET_HIDE_DOWNLOAD_BANNER = "SET_HIDE_DOWNLOAD_BANNER";
export const SETTINGS_DATASTORE_LOADED = "SETTINGS_DATASTORE_LOADED";
export const SET_PASSWORD_CONFIG = "SET_PASSWORD_CONFIG";
export const SET_GPG_CONFIG = "SET_GPG_CONFIG";

View File

@ -12,28 +12,34 @@ import { PersistGate } from "redux-persist/integration/react";
import { Provider } from "react-redux";
import { MuiPickersUtilsProvider } from "@material-ui/pickers";
import DateFnsUtils from "@date-io/date-fns";
import initReactFastclick from "react-fastclick";
import store from "./services/store";
import datastoreSettingService from "./services/datastore-setting";
import i18n from "./i18n";
import theme from "./theme";
import IndexView from "./views/index";
import DownloadBanner from "./containers/download-banner";
/**
* Loads the datastore
* @param dispatch
* @param getState
*/
function fetchTodos(dispatch, getState) {
datastoreSettingService.getSettingsDatastore();
function loadSettingsDatastore(dispatch, getState) {
const state = getState();
if (state.user.isLoggedIn) {
datastoreSettingService.getSettingsDatastore();
}
}
let persistor = persistStore(store, null, () => {
store.dispatch(fetchTodos);
store.dispatch(loadSettingsDatastore);
});
const customHistory = createBrowserHistory();
initReactFastclick();
const App = () => {
return (
<MuiPickersUtilsProvider utils={DateFnsUtils}>
@ -44,6 +50,7 @@ const App = () => {
<ThemeProvider theme={theme}>
<CssBaseline />
<HashRouter history={customHistory} hashType={"hashbang"}>
<DownloadBanner />
<IndexView />
</HashRouter>
</ThemeProvider>

View File

@ -13,6 +13,7 @@ import Typography from "@material-ui/core/Typography";
import EditIcon from "@material-ui/icons/Edit";
import CreateNewFolderIcon from "@material-ui/icons/CreateNewFolder";
import AddIcon from "@material-ui/icons/Add";
import PersonAddIcon from "@material-ui/icons/PersonAdd";
import OpenWithIcon from "@material-ui/icons/OpenWith";
import DeleteIcon from "@material-ui/icons/Delete";
import Divider from "@material-ui/core/Divider";
@ -28,14 +29,18 @@ const useStyles = makeStyles((theme) => ({
icon: {
fontSize: "18px",
},
listItemIcon: {
minWidth: theme.spacing(4),
},
}));
const DatastoreTreeFolder = (props) => {
const { t } = useTranslation();
const { content, search, offline, isExpandedDefault } = props;
const { content, offline, isExpandedDefault, nodePath } = props;
const classes = useStyles();
const [isExpanded, setIsExpanded] = React.useState(isExpandedDefault);
const [anchorEl, setAnchorEl] = React.useState(null);
const isSelectable = props.isSelectable ? props.isSelectable(content) : true;
const openMenu = (event) => {
event.preventDefault();
@ -56,7 +61,7 @@ const DatastoreTreeFolder = (props) => {
}
registrations["read_share_rights"](content.share_id).then(function (share_details) {
var modalInstance = $uibModal.open({
const modalInstance = $uibModal.open({
templateUrl: "view/modal/display-share-rights.html",
controller: "ModalDisplayShareRightsCtrl",
backdrop: "static",
@ -78,7 +83,7 @@ const DatastoreTreeFolder = (props) => {
const onEdit = (event) => {
handleClose(event);
// TODO editNode
props.onEditFolder(content, content.path);
};
const onNewFolder = (event) => {
@ -93,7 +98,12 @@ const DatastoreTreeFolder = (props) => {
const onNewEntry = (event) => {
handleClose(event);
// TODO newEntryNode
props.onNewEntry(content, content.path);
};
const onNewUser = (event) => {
handleClose(event);
props.onNewUser(content, content.path);
};
const onMove = (event) => {
@ -104,27 +114,32 @@ const DatastoreTreeFolder = (props) => {
const selectNode = (event) => {
event.stopPropagation();
setIsExpanded(!isExpanded);
if (props.onSelectNode && isSelectable) {
props.onSelectNode(content, content.path, nodePath);
}
};
React.useEffect(() => {
setIsExpanded(isExpandedDefault);
}, [isExpandedDefault]);
const hideShare = offline || (content.hasOwnProperty("share_rights") && content.share_rights.grant === false);
const hideShare = offline || (content.hasOwnProperty("share_rights") && content.share_rights.grant === false) || !props.onNewShare;
const hideRightsOverview =
offline ||
(content.hasOwnProperty("share_rights") && content.share_rights.grant === false) ||
!content.hasOwnProperty("share_id") ||
typeof content.share_id === "undefined";
const hideEdit = offline || content.share_rights.write === false;
const hideNewFolder = offline || content.share_rights.write === false;
const hideNewEntry = offline || content.share_rights.write === false;
const hideMove = offline || content.share_rights.delete === false;
const hideDelete = offline || content.share_rights.delete === false;
const hideEdit = offline || (content.hasOwnProperty("share_rights") && content.share_rights.write === false) || !props.onEditFolder;
const hideNewFolder = offline || (content.hasOwnProperty("share_rights") && content.share_rights.write === false) || !props.onNewFolder;
const hideNewEntry = offline || (content.hasOwnProperty("share_rights") && content.share_rights.write === false) || !props.onNewEntry;
const hideNewUser = offline || (content.hasOwnProperty("share_rights") && content.share_rights.write === false) || !props.onNewUser;
const hideMove = offline || (content.hasOwnProperty("share_rights") && content.share_rights.delete === false);
const hideDelete = offline || (content.hasOwnProperty("share_rights") && content.share_rights.delete === false);
const disableMenu = hideShare && hideRightsOverview && hideEdit && hideNewFolder && hideNewEntry && hideNewUser && hideMove && hideDelete;
return (
<div className={"tree-folder"}>
<div className={"tree-folder-title"}>
<div className={"tree-folder-header"} onClick={selectNode}>
<div className={"tree-folder-header" + (isSelectable ? "" : " notSelectable")} onClick={selectNode}>
<span className="fa-stack">
{isExpanded && <i className="fa fa-folder-open" />}
{!isExpanded && <i className="fa fa-folder" />}
@ -133,14 +148,14 @@ const DatastoreTreeFolder = (props) => {
</span>
<span className="tree-folder-name ng-binding">{content.name}</span>
<ButtonGroup variant="text" aria-label="text button group" className={"node-open-link"}>
<Button aria-label="settings" onClick={openMenu}>
<Button aria-label="settings" onClick={openMenu} disabled={disableMenu}>
<SettingsIcon fontSize="small" />
</Button>
</ButtonGroup>
<Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
{!hideShare && onNewShare && (
<MenuItem onClick={onNewShare}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<ShareIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -150,7 +165,7 @@ const DatastoreTreeFolder = (props) => {
)}
{!hideRightsOverview && (
<MenuItem onClick={onRightsOverview}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<ListIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -161,7 +176,7 @@ const DatastoreTreeFolder = (props) => {
{(!hideShare || !hideRightsOverview) && <Divider className={classes.divider} />}
{!hideEdit && (
<MenuItem onClick={onEdit}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<EditIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -171,7 +186,7 @@ const DatastoreTreeFolder = (props) => {
)}
{!hideNewFolder && (
<MenuItem onClick={onNewFolder}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<CreateNewFolderIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -181,7 +196,7 @@ const DatastoreTreeFolder = (props) => {
)}
{!hideNewEntry && (
<MenuItem onClick={onNewEntry}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<AddIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -189,9 +204,19 @@ const DatastoreTreeFolder = (props) => {
</Typography>
</MenuItem>
)}
{!hideNewUser && (
<MenuItem onClick={onNewUser}>
<ListItemIcon className={classes.listItemIcon}>
<PersonAddIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
{t("NEW_USER")}
</Typography>
</MenuItem>
)}
{!hideMove && (
<MenuItem onClick={onMove}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<OpenWithIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -202,7 +227,7 @@ const DatastoreTreeFolder = (props) => {
{!hideDelete && <Divider className={classes.divider} />}
{!hideDelete && (
<MenuItem onClick={onMove}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<DeleteIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -219,11 +244,22 @@ const DatastoreTreeFolder = (props) => {
content.folders
.filter((folder) => !folder["hidden"] && !folder["deleted"])
.map(function (content, i) {
const nodePathClone = Array.from(nodePath);
nodePathClone.push(content);
return (
<DatastoreTreeFolder
isSelectable={props.isSelectable}
onSelectItem={props.onSelectItem}
onSelectNode={props.onSelectNode}
onEditFolder={props.onEditFolder}
onEditEntry={props.onEditEntry}
onLinkItem={props.onLinkItem}
onNewFolder={props.onNewFolder}
search={search}
onNewUser={props.onNewUser}
onNewEntry={props.onNewEntry}
onNewShare={props.onNewShare}
key={i}
nodePath={nodePathClone}
content={content}
offline={offline}
isExpandedDefault={Boolean(content["is_expanded"])}
@ -234,7 +270,21 @@ const DatastoreTreeFolder = (props) => {
content.items
.filter((item) => !item["hidden"] && !item["deleted"])
.map(function (content, i) {
return <DatastoreTreeItem onNewShare={props.onNewShare} search={search} key={i} content={content} offline={offline} />;
const nodePathClone = Array.from(nodePath);
nodePathClone.push(content);
return (
<DatastoreTreeItem
isSelectable={props.isSelectable}
onSelectItem={props.onSelectItem}
onEditEntry={props.onEditEntry}
onLinkItem={props.onLinkItem}
onNewShare={props.onNewShare}
key={i}
nodePath={nodePathClone}
content={content}
offline={offline}
/>
);
})}
</div>
)}
@ -243,12 +293,20 @@ const DatastoreTreeFolder = (props) => {
};
DatastoreTreeFolder.propTypes = {
search: PropTypes.string.isRequired,
isSelectable: PropTypes.func,
isExpandedDefault: PropTypes.bool.isRequired,
content: PropTypes.object,
nodePath: PropTypes.array.isRequired,
offline: PropTypes.bool.isRequired,
onNewFolder: PropTypes.func.isRequired,
onNewShare: PropTypes.func,
onNewUser: PropTypes.func,
onNewEntry: PropTypes.func,
onEditEntry: PropTypes.func,
onLinkItem: PropTypes.func,
onEditFolder: PropTypes.func,
onSelectNode: PropTypes.func,
onSelectItem: PropTypes.func,
};
export default DatastoreTreeFolder;

View File

@ -14,6 +14,7 @@ import ShareIcon from "@material-ui/icons/Share";
import Typography from "@material-ui/core/Typography";
import LinkIcon from "@material-ui/icons/Link";
import EditIcon from "@material-ui/icons/Edit";
import VisibilityIcon from "@material-ui/icons/Visibility";
import OpenWithIcon from "@material-ui/icons/OpenWith";
import DeleteIcon from "@material-ui/icons/Delete";
import FileCopyIcon from "@material-ui/icons/FileCopy";
@ -23,7 +24,6 @@ import { makeStyles } from "@material-ui/core/styles";
import ContentCopy from "./icons/ContentCopy";
import secretService from "../services/secret";
import fileTransferService from "../services/file-transfer";
import store from "../services/store";
import widgetService from "../services/widget";
@ -35,13 +35,17 @@ const useStyles = makeStyles((theme) => ({
icon: {
fontSize: "18px",
},
listItemIcon: {
minWidth: theme.spacing(4),
},
}));
const DatastoreTreeItem = (props) => {
const { t } = useTranslation();
const { content, search, offline } = props;
const { content, offline } = props;
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState(null);
const isSelectable = props.isSelectable ? props.isSelectable(content) : true;
const openMenu = (event) => {
event.preventDefault();
@ -66,11 +70,11 @@ const DatastoreTreeItem = (props) => {
*
* @param content
*/
var on_modal_close_success = function (content) {
const on_modal_close_success = function (content) {
console.log(content);
};
var modalInstance = $uibModal.open({
const modalInstance = $uibModal.open({
templateUrl: "view/modal/create-link-share.html",
controller: "ModalCreateLinkShareCtrl",
backdrop: "static",
@ -94,7 +98,7 @@ const DatastoreTreeItem = (props) => {
}
registrations["read_share_rights"](content.share_id).then(function (share_details) {
var modalInstance = $uibModal.open({
const modalInstance = $uibModal.open({
templateUrl: "view/modal/display-share-rights.html",
controller: "ModalDisplayShareRightsCtrl",
backdrop: "static",
@ -131,7 +135,7 @@ const DatastoreTreeItem = (props) => {
const onEdit = (event) => {
handleClose(event);
// TODO editNode
props.onEditEntry(content, content.path, props.nodePath);
};
const onNewShare = (event) => {
@ -143,20 +147,26 @@ const DatastoreTreeItem = (props) => {
handleClose(event);
// TODO moveNode
};
const clickItem = function () {
if (content.type === "file") {
return fileTransferService.onItemClick(content, content.path);
} else {
return secretService.onItemClick(content);
const selectItem = function (event) {
event.stopPropagation();
if (props.onSelectItem && isSelectable) {
props.onSelectItem(content, content.path, props.nodePath);
}
};
const linkItem = function (event) {
event.stopPropagation();
if (props.onLinkItem) {
props.onLinkItem(content, content.path, props.nodePath);
}
};
const hideShare = offline || (content.hasOwnProperty("share_rights") && content.share_rights.grant === false);
const hideShare = offline || (content.hasOwnProperty("share_rights") && content.share_rights.grant === false) || content.type === "user";
const hideLinkShare =
offline ||
!content.hasOwnProperty("type") ||
(content.hasOwnProperty("share_rights") && content.share_rights.grant === false) ||
store.getState().server.complianceDisableLinkShares;
store.getState().server.complianceDisableLinkShares ||
content.type === "user";
const hideRightsOverview =
offline ||
(content.hasOwnProperty("share_rights") && content.share_rights.grant === false) ||
@ -174,7 +184,8 @@ const DatastoreTreeItem = (props) => {
(content.hasOwnProperty("share_rights") && content.share_rights.read !== true) ||
!content.hasOwnProperty("type") ||
!["website_password", "application_password"].includes(content["type"]);
const hideEdit = offline || content.share_rights.write === false;
const hideEdit = offline || content.share_rights.write === false || !props.onEditEntry;
const hideShow = !hideEdit || content.share_rights.read === false || !props.onEditEntry;
const hideClone =
offline || content.share_rights.write === false || content.share_rights.read === false || content.type === "file" || content.type === "user";
const hideMove = offline || content.share_rights.delete === false;
@ -182,7 +193,7 @@ const DatastoreTreeItem = (props) => {
return (
<div className={"tree-item"}>
<div className={"tree-item-object"}>
<div className={"tree-item-object" + (isSelectable ? "" : " notSelectable")} onClick={selectItem}>
<span className="fa-stack">
<i className={widgetService.itemIcon(content)} />
{content.share_id && <i className="fa fa-circle fa-stack-2x text-danger is-shared" />}
@ -190,13 +201,13 @@ const DatastoreTreeItem = (props) => {
</span>
<span className="tree-item-name">{content.name}</span>
<ButtonGroup variant="text" aria-label="outlined button group" className={"node-open-link"}>
{["bookmark", "website_password"].indexOf(content.type) !== -1 && (
<Button aria-label="open" onClick={clickItem}>
{Boolean(props.onLinkItem) && ["bookmark", "website_password"].indexOf(content.type) !== -1 && (
<Button aria-label="open" onClick={linkItem}>
<OpenInNewIcon fontSize="small" />
</Button>
)}
{["file"].indexOf(content.type) !== -1 && (
<Button aria-label="open" onClick={clickItem}>
{Boolean(props.onLinkItem) && ["file"].indexOf(content.type) !== -1 && (
<Button aria-label="open" onClick={linkItem}>
<GetAppIcon fontSize="small" />
</Button>
)}
@ -207,7 +218,7 @@ const DatastoreTreeItem = (props) => {
<Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
{!hideShare && (
<MenuItem onClick={onNewShare}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<ShareIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -217,7 +228,7 @@ const DatastoreTreeItem = (props) => {
)}
{!hideLinkShare && (
<MenuItem onClick={onLinkShare}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<LinkIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -227,7 +238,7 @@ const DatastoreTreeItem = (props) => {
)}
{!hideRightsOverview && (
<MenuItem onClick={onRightsOverview}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<ListIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -237,7 +248,7 @@ const DatastoreTreeItem = (props) => {
)}
{!hideCopyTotpToken && (
<MenuItem onClick={onCopyTotpToken}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<ContentCopy className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -247,7 +258,7 @@ const DatastoreTreeItem = (props) => {
)}
{!hideCopyUsername && (
<MenuItem onClick={onCopyUsername}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<ContentCopy className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -257,7 +268,7 @@ const DatastoreTreeItem = (props) => {
)}
{!hideCopyPassword && (
<MenuItem onClick={onCopyPassword}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<ContentCopy className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -265,20 +276,32 @@ const DatastoreTreeItem = (props) => {
</Typography>
</MenuItem>
)}
<Divider className={classes.divider} />
{(!hideShare || !hideLinkShare || !hideRightsOverview || !hideCopyTotpToken || !hideCopyUsername || !hideCopyPassword) && (
<Divider className={classes.divider} />
)}
{!hideEdit && (
<MenuItem onClick={onEdit}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<EditIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
{content.type === "user" ? t("SHOW") : t("SHOW_OR_EDIT")}
{t("SHOW_OR_EDIT")}
</Typography>
</MenuItem>
)}
{!hideShow && (
<MenuItem onClick={onEdit}>
<ListItemIcon className={classes.listItemIcon}>
<VisibilityIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
{t("SHOW")}
</Typography>
</MenuItem>
)}
{!hideClone && (
<MenuItem onClick={onEdit}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<FileCopyIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -288,7 +311,7 @@ const DatastoreTreeItem = (props) => {
)}
{!hideMove && (
<MenuItem onClick={onMove}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<OpenWithIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -299,7 +322,7 @@ const DatastoreTreeItem = (props) => {
{!hideDelete && <Divider className={classes.divider} />}
{!hideDelete && (
<MenuItem onClick={onMove}>
<ListItemIcon>
<ListItemIcon className={classes.listItemIcon}>
<DeleteIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
@ -314,9 +337,13 @@ const DatastoreTreeItem = (props) => {
};
DatastoreTreeItem.propTypes = {
search: PropTypes.string.isRequired,
isSelectable: PropTypes.func,
nodePath: PropTypes.array.isRequired,
content: PropTypes.object,
offline: PropTypes.bool.isRequired,
onNewShare: PropTypes.func,
onEditEntry: PropTypes.func,
onLinkItem: PropTypes.func,
onSelectItem: PropTypes.func,
};
export default DatastoreTreeItem;

View File

@ -3,11 +3,14 @@ import PropTypes from "prop-types";
import offlineCache from "../services/offline-cache";
import DatastoreTreeItem from "./datastore-tree-item";
import DatastoreTreeFolder from "./datastore-tree-folder";
import datastorePassword from "../services/datastore-password";
const DatastoreTree = (props) => {
const { datastore, search, onNewFolder, onNewShare } = props;
const { datastore, search } = props;
const offline = offlineCache.isActive();
datastorePassword.modifyTreeForSearch(search, datastore);
return (
<div className={"tree"}>
{datastore.folders &&
@ -16,10 +19,18 @@ const DatastoreTree = (props) => {
.map(function (content, i) {
return (
<DatastoreTreeFolder
onNewFolder={onNewFolder}
onNewShare={onNewShare}
search={search}
isSelectable={props.isSelectable}
onSelectItem={props.onSelectItem}
onSelectNode={props.onSelectNode}
onEditFolder={props.onEditFolder}
onEditEntry={props.onEditEntry}
onLinkItem={props.onLinkItem}
onNewFolder={props.onNewFolder}
onNewUser={props.onNewUser}
onNewEntry={props.onNewEntry}
onNewShare={props.onNewShare}
key={i}
nodePath={[content]}
content={content}
offline={offline}
isExpandedDefault={Boolean(content["is_expanded"])}
@ -30,17 +41,37 @@ const DatastoreTree = (props) => {
datastore.items
.filter((item) => !item["hidden"] && !item["deleted"])
.map(function (content, i) {
return <DatastoreTreeItem onNewShare={onNewShare} search={search} key={i} content={content} offline={offline} />;
return (
<DatastoreTreeItem
isSelectable={props.isSelectable}
onSelectItem={props.onSelectItem}
onEditEntry={props.onEditEntry}
onLinkItem={props.onLinkItem}
onNewShare={props.onNewShare}
key={i}
nodePath={[content]}
content={content}
offline={offline}
/>
);
})}
</div>
);
};
DatastoreTree.propTypes = {
search: PropTypes.string.isRequired,
search: PropTypes.string,
datastore: PropTypes.object.isRequired,
onNewFolder: PropTypes.func.isRequired,
onNewUser: PropTypes.func,
onNewShare: PropTypes.func,
onNewEntry: PropTypes.func,
onEditEntry: PropTypes.func,
onLinkItem: PropTypes.func,
onEditFolder: PropTypes.func,
onSelectItem: PropTypes.func,
onSelectNode: PropTypes.func,
isSelectable: PropTypes.func,
};
export default DatastoreTree;

View File

@ -0,0 +1,374 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import { makeStyles } from "@material-ui/core/styles";
import Dialog from "@material-ui/core/Dialog";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";
import DialogActions from "@material-ui/core/DialogActions";
import Button from "@material-ui/core/Button";
import { Grid } from "@material-ui/core";
import TextField from "@material-ui/core/TextField";
import MuiAlert from "@material-ui/lab/Alert";
import datastoreUserService from "../../services/datastore-user";
import cryptoLibrary from "../../services/crypto-library";
import DatastoreTree from "../datastore-tree";
import widget from "../../services/widget";
import datastorePassword from "../../services/datastore-password";
import DialogNewFolder from "./new-folder";
import TextFieldPath from "../text-field-path";
import shareService from "../../services/share";
const useStyles = makeStyles((theme) => ({
textField: {
width: "100%",
},
checked: {
color: "#9c27b0",
},
checkedIcon: {
width: "20px",
height: "20px",
border: "1px solid #666",
borderRadius: "3px",
},
uncheckedIcon: {
width: "0px",
height: "0px",
padding: "9px",
border: "1px solid #666",
borderRadius: "3px",
},
tree: {
marginTop: "8px",
marginBottom: "8px",
},
}));
const DialogAcceptShare = (props) => {
const { open, onClose, item, hideUser } = props;
const { t } = useTranslation();
const classes = useStyles();
const [path, setPath] = useState([]);
const [newFolderOpen, setNewFolderOpen] = useState(false);
const [newFolderData, setNewFolderData] = useState({});
const [datastore, setDatastore] = useState(null);
const [userIsTrusted, setUserIsTrusted] = useState(false);
const [user, setUser] = useState({
data: {
user_name: "",
user_username: "",
user_public_key: "",
},
});
let isSubscribed = true;
React.useEffect(() => {
datastorePassword.getPasswordDatastore().then(onNewDatastoreLoaded);
datastoreUserService.searchUserDatastore(item.share_right_create_user_id, item.share_right_create_user_username).then(function (user) {
if (!isSubscribed) {
return;
}
if (user !== null) {
setUserIsTrusted(true);
setUser(user);
return;
}
const onSuccess = function (data) {
const users = data.data;
if (Object.prototype.toString.call(users) === "[object Array]") {
users.map((user) => {
if (user.username === item.share_right_create_user_username) {
setUser({
data: {
user_id: user.id,
user_username: user.username,
user_public_key: user.public_key,
},
name: user.username,
});
}
});
} else {
setUser({
data: {
user_id: users.id,
user_username: users.username,
user_public_key: users.public_key,
},
name: users.username,
});
}
};
const onError = function (data) {
//pass
};
return datastoreUserService.searchUser(item.share_right_create_user_username).then(onSuccess, onError);
});
// cancel subscription to useEffect
return () => (isSubscribed = false);
}, []);
const onNewDatastoreLoaded = (data) => {
if (!isSubscribed) {
return;
}
setDatastore(data);
};
const trust = () => {
const onSuccess = function (user_data_store) {
if (typeof user_data_store.items === "undefined") {
user_data_store.items = [];
}
const userObject = {
id: cryptoLibrary.generateUuid(),
name: "",
type: "user",
data: user.data,
};
if (user.data.user_name) {
userObject.name += user.data.user_name;
} else {
userObject.name += user.data.user_username;
}
userObject.name += " (" + user.data.user_public_key + ")";
user_data_store.items.push(userObject);
datastoreUserService.saveDatastoreContent(user_data_store);
setUserIsTrusted(true);
};
const onError = function (data) {
//pass
};
datastoreUserService.getUserDatastore().then(onSuccess, onError);
};
const onNewFolderCreate = (name) => {
// called once someone clicked the CREATE button in the dialog closes with the new name
setNewFolderOpen(false);
widget.openNewFolder(newFolderData["parent"], newFolderData["path"], datastore, datastorePassword, name);
};
const onNewFolder = (parent, path) => {
// called whenever someone clicks on a new folder Icon
setNewFolderOpen(true);
setNewFolderData({
parent: parent,
path: path,
});
};
const onSelectNode = (parent, path, nodePath) => {
setPath(Array.from(nodePath));
};
const isSelectable = (node) => {
// filter out all targets that are a share if the item is not allowed to be shared
if (!item.share_right_grant && node.share_id) {
return false;
}
// filter out all targets that are inside of a share if the item is not allowed to be shared
if (!item.share_right_grant && node.parent_share_id) {
return false;
}
//
if (!node.hasOwnProperty("share_rights")) {
return true;
}
// we need both read and write permission on the target folder in order to update it with the new content
if (!!(node.share_rights.read && node.share_rights.write)) {
return true;
}
return false;
};
const onConfirm = () => {
const onSuccess = function (datastore) {
const breadcrumbs = { id_breadcrumbs: path.map((node) => node.id) };
console.log(breadcrumbs);
const analyzedBreadcrumbs = datastorePassword.analyzeBreadcrumbs(breadcrumbs, datastore);
if (item.share_right_grant === false && typeof analyzedBreadcrumbs["parent_share_id"] !== "undefined") {
// No grant right, yet the parent is a a share?!?
alert("Wups, this should not happen. Error: 781f3da7-d38b-470e-a3c8-dd5787642230");
}
const onSuccess = function (share) {
if (typeof share.name === "undefined") {
share.name = item.share_right_title;
}
const shares = [share];
const onSuccess = function () {
onClose();
};
const onError = function (data) {
console.log(data);
};
return datastorePassword
.createShareLinksInDatastore(
shares,
analyzedBreadcrumbs["target"],
analyzedBreadcrumbs["parent_path"],
analyzedBreadcrumbs["path"],
analyzedBreadcrumbs["parent_share_id"],
analyzedBreadcrumbs["parent_datastore_id"],
datastore,
analyzedBreadcrumbs["parent_share"]
)
.then(onSuccess, onError);
};
const onError = function (data) {
//pass
console.log(data);
};
console.log(item);
return shareService
.acceptShareRight(item.share_right_id, item.share_right_key, item.share_right_key_nonce, user.data.user_public_key)
.then(onSuccess, onError);
};
const onError = function (data) {
//pass
console.log(data);
};
return datastorePassword.getPasswordDatastore().then(onSuccess, onError);
};
return (
<Dialog
fullWidth
maxWidth={"sm"}
open={open}
onClose={() => {
onClose();
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{t("ACCEPT_SHARE")}</DialogTitle>
<DialogContent>
<Grid container>
<Grid item xs={12} sm={12} md={12}>
<TextFieldPath className={classes.textField} variant="outlined" margin="dense" value={path} setPath={setPath} />
</Grid>
<Grid item xs={12} sm={12} md={12} className={classes.tree}>
{datastore && <DatastoreTree datastore={datastore} onNewFolder={onNewFolder} onSelectNode={onSelectNode} isSelectable={isSelectable} />}
{newFolderOpen && <DialogNewFolder open={newFolderOpen} onClose={() => setNewFolderOpen(false)} onCreate={onNewFolderCreate} />}
</Grid>
{!hideUser && (
<Grid item xs={12} sm={12} md={12}>
{t("SHARED_BY")}:
</Grid>
)}
{!hideUser && Boolean(user.data.user_name) && (
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="username"
label={t("USERNAME") + " " + (userIsTrusted ? "" : t("NOT_TRUSTED_BRACKETS"))}
name="username"
autoComplete="username"
value={user.data.user_name}
disabled
/>
</Grid>
)}
{!hideUser && !Boolean(user.data.user_name) && (
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="username"
label={t("USERNAME") + " " + (userIsTrusted ? "" : t("NOT_TRUSTED_BRACKETS"))}
name="username"
autoComplete="username"
value={user.data.user_username}
disabled
/>
</Grid>
)}
{!hideUser && (
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="publicKey"
label={t("PUBLIC_KEY") + " " + (userIsTrusted ? "" : t("NOT_TRUSTED_BRACKETS"))}
name="publicKey"
autoComplete="publicKey"
value={user.data.user_public_key}
disabled
/>
</Grid>
)}
{!userIsTrusted && !hideUser && (
<Grid item xs={12} sm={12} md={12}>
<MuiAlert
severity="warning"
style={{
marginBottom: "5px",
marginTop: "5px",
}}
>
{t("YOU_NEVER_CONFIRMED_THIS_USERS_IDENTITY")}{" "}
<a
href="#"
onClick={(event) => {
event.preventDefault();
trust();
}}
>
{t("ADD_TO_TRUSTED_USERS")}
</a>
</MuiAlert>
</Grid>
)}
</Grid>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
onClose();
}}
>
{t("CLOSE")}
</Button>
<Button onClick={onConfirm} variant="contained" color="primary">
{t("OK")}
</Button>
</DialogActions>
</Dialog>
);
};
DialogAcceptShare.defaultProps = {
hideUser: false,
};
DialogAcceptShare.propTypes = {
onClose: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
item: PropTypes.object.isRequired,
hideUser: PropTypes.bool.isRequired,
};
export default DialogAcceptShare;

View File

@ -0,0 +1,166 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import Dialog from "@material-ui/core/Dialog";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";
import DialogActions from "@material-ui/core/DialogActions";
import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField";
import { makeStyles } from "@material-ui/core/styles";
import { Grid } from "@material-ui/core";
import HKP from "@openpgp/hkp-client";
import * as openpgp from "openpgp";
import { BarLoader } from "react-spinners";
import GridContainerErrors from "../grid-container-errors";
import store from "../../services/store";
import datastorePasswordService from "../../services/datastore-password";
const useStyles = makeStyles((theme) => ({
textField: {
width: "100%",
},
}));
const DialogDecryptGpgMessage = (props) => {
const classes = useStyles();
const { open, onClose } = props;
const { t } = useTranslation();
const [encryptedMessage, setEncryptedMessage] = useState("");
const [decryptedMessage, setDecryptedMessage] = useState("");
const [decrypting, setDecrypting] = useState(false);
const [decryptingComplete, setDecryptingComplete] = useState(false);
const [errors, setErrors] = useState([]);
const decrypt = () => {
setDecrypting(true);
const pgpSender = [];
function decrypt(publicKey) {
return datastorePasswordService.getAllOwnPgpKeys().then(async function (privateKeys) {
const privateKeysArray = [];
for (let i = 0; i < privateKeys.length; i++) {
const privateKey = await openpgp.readPrivateKey({ armoredKey: privateKeys[i] });
privateKeysArray.push(privateKey);
}
//console.log(pgpSender);
const message = await openpgp.readMessage({
armoredMessage: encryptedMessage, // parse armored message
});
let options;
if (publicKey) {
options = {
message: message, // parse armored message
verificationKeys: await openpgp.readKey({ armoredKey: publicKey }),
decryptionKeys: privateKeysArray,
};
} else {
options = {
message: message, // parse armored message
decryptionKeys: privateKeysArray,
};
}
openpgp.decrypt(options).then(
function (plaintext) {
setDecryptedMessage(plaintext.data);
setDecryptingComplete(true);
setDecrypting(false);
},
function (error) {
console.log(error);
setErrors([error.message]);
setDecrypting(false);
}
);
});
}
const gpgHkpSearch = store.getState().settingsDatastore.gpgHkpSearch;
if (gpgHkpSearch && pgpSender && pgpSender.length) {
const hkp = new HKP(store.getState().settingsDatastore.gpgHkpKeyServer);
const options = {
query: pgpSender,
};
hkp.lookup(options).then(
function (public_key) {
decrypt(public_key);
},
function (error) {
console.log(error);
console.log(error.message);
decrypt();
}
);
} else {
decrypt();
}
};
return (
<Dialog fullWidth maxWidth={"sm"} open={open} onClose={onClose} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description">
<DialogTitle id="alert-dialog-title">{t("DECRYPT_MESSAGE")}</DialogTitle>
<DialogContent>
<Grid container>
<GridContainerErrors errors={errors} setErrors={setErrors} />
{!decryptingComplete && (
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="encryptedMessage"
label={t("ENCRYPTED_MESSAGE")}
name="encryptedMessage"
autoComplete="encryptedMessage"
value={encryptedMessage}
onChange={(event) => {
setEncryptedMessage(event.target.value);
}}
disabled={decrypting}
multiline
minRows={3}
/>
</Grid>
)}
{decryptingComplete && (
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="decryptedMessage"
label={t("DECRYPTED_MESSAGE")}
name="decryptedMessage"
autoComplete="decryptedMessage"
value={decryptedMessage}
multiline
readonly
minRows={3}
/>
</Grid>
)}
</Grid>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>{t("CLOSE")}</Button>
<Button onClick={decrypt} variant="contained" color="primary" disabled={!encryptedMessage || decrypting}>
<span style={!decrypting ? {} : { display: "none" }}>{t("DECRYPT")}</span>
<BarLoader color={"#FFF"} height={17} width={37} loading={decrypting} />
</Button>
</DialogActions>
</Dialog>
);
};
DialogDecryptGpgMessage.propTypes = {
onClose: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
};
export default DialogDecryptGpgMessage;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,104 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import { makeStyles } from "@material-ui/core/styles";
import Dialog from "@material-ui/core/Dialog";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";
import DialogActions from "@material-ui/core/DialogActions";
import Button from "@material-ui/core/Button";
import { Grid } from "@material-ui/core";
import TextField from "@material-ui/core/TextField";
const useStyles = makeStyles((theme) => ({
textField: {
width: "100%",
},
checked: {
color: "#9c27b0",
},
checkedIcon: {
width: "20px",
height: "20px",
border: "1px solid #666",
borderRadius: "3px",
},
uncheckedIcon: {
width: "0px",
height: "0px",
padding: "9px",
border: "1px solid #666",
borderRadius: "3px",
},
}));
const DialogEditFolder = (props) => {
const { open, onClose, node } = props;
const { t } = useTranslation();
const classes = useStyles();
const [folderName, setFolderName] = useState(node.name);
const onSave = (event) => {
node.name = folderName;
props.onSave(node);
};
return (
<Dialog
fullWidth
maxWidth={"sm"}
open={open}
onClose={() => {
onClose();
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{t("EDIT_FOLDER")}</DialogTitle>
<DialogContent>
<Grid container>
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="folderName"
label={t("FOLDER_NAME")}
name="folderName"
autoComplete="folderName"
value={folderName}
required
onChange={(event) => {
setFolderName(event.target.value);
}}
/>
</Grid>
<Grid item xs={12} sm={12} md={12}>
{t("FOLDER_LINK")}: <a href={"index.html#!/datastore/search/" + node.id}>{node.id}</a>
</Grid>
</Grid>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
onClose();
}}
>
{t("CLOSE")}
</Button>
<Button onClick={onSave} variant="contained" color="primary" disabled={!folderName}>
{t("SAVE")}
</Button>
</DialogActions>
</Dialog>
);
};
DialogEditFolder.propTypes = {
onClose: PropTypes.func.isRequired,
onSave: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
node: PropTypes.object.isRequired,
};
export default DialogEditFolder;

View File

@ -0,0 +1,164 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import { makeStyles } from "@material-ui/core/styles";
import Dialog from "@material-ui/core/Dialog";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";
import DialogActions from "@material-ui/core/DialogActions";
import Button from "@material-ui/core/Button";
import CheckBoxOutlineBlankIcon from "@material-ui/icons/CheckBoxOutlineBlank";
import { Grid } from "@material-ui/core";
import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment";
import IconButton from "@material-ui/core/IconButton";
import GridContainerErrors from "../grid-container-errors";
import Table from "../table";
import helperService from "../../services/helper";
import store from "../../services/store";
import browserClient from "../../services/browser-client";
import datastoreUserService from "../../services/datastore-user";
import cryptoLibrary from "../../services/crypto-library";
const useStyles = makeStyles((theme) => ({
textField: {
width: "100%",
},
checked: {
color: "#9c27b0",
},
checkedIcon: {
width: "20px",
height: "20px",
border: "1px solid #666",
borderRadius: "3px",
},
uncheckedIcon: {
width: "0px",
height: "0px",
padding: "9px",
border: "1px solid #666",
borderRadius: "3px",
},
}));
const DialogEditUser = (props) => {
const { open, onClose, item } = props;
const { t } = useTranslation();
const classes = useStyles();
const [errors, setErrors] = useState([]);
const [visualUsername, setVisualUsername] = useState(item.data.user_name || "");
const [username, setUsername] = useState(item.data.user_username);
const [userId, setFoundUserId] = useState(item.data.user_id);
const [publicKey, setFoundPublicKey] = useState(item.data.user_public_key);
const onSave = (event) => {
if (item.data.user_name) {
delete item.data.user_name;
}
if (visualUsername) {
item.data.user_name = visualUsername;
}
item.name = "";
if (item.data.user_name) {
item.name += item.data.user_name;
} else {
item.name += item.data.user_username;
}
item.name += " (" + item.data.user_public_key + ")";
props.onSave(item);
};
return (
<Dialog
fullWidth
maxWidth={"sm"}
open={open}
onClose={() => {
onClose();
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{t("EDIT_USER")}</DialogTitle>
<DialogContent>
<Grid container>
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="visualUsername"
label={t("NAME_OPTIONAL")}
name="visualUsername"
autoComplete="visualUsername"
value={visualUsername}
onChange={(event) => {
setVisualUsername(event.target.value);
}}
/>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="username"
label={t("USERNAME")}
name="username"
autoComplete="username"
value={username}
disabled
/>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="publicKey"
label={t("PUBLIC_KEY")}
name="publicKey"
autoComplete="publicKey"
helperText={t("TO_VERIFY_PUBLIC_KEY")}
value={publicKey}
disabled
/>
</Grid>
</Grid>
<GridContainerErrors errors={errors} setErrors={setErrors} />
</DialogContent>
<DialogActions>
<Button
onClick={() => {
onClose();
}}
>
{t("CLOSE")}
</Button>
<Button
onClick={() => {
onSave(visualUsername, userId, username, publicKey);
}}
variant="contained"
color="primary"
disabled={!userId || !username || !publicKey}
>
{t("SAVE")}
</Button>
</DialogActions>
</Dialog>
);
};
DialogEditUser.propTypes = {
onClose: PropTypes.func.isRequired,
onSave: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
item: PropTypes.object.isRequired,
};
export default DialogEditUser;

View File

@ -0,0 +1,114 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import { makeStyles } from "@material-ui/core/styles";
import Dialog from "@material-ui/core/Dialog";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";
import DialogActions from "@material-ui/core/DialogActions";
import Button from "@material-ui/core/Button";
import { Grid } from "@material-ui/core";
import TextField from "@material-ui/core/TextField";
import SelectFieldEntryType from "../select-field/entry-type";
const useStyles = makeStyles((theme) => ({
textField: {
width: "100%",
},
checked: {
color: "#9c27b0",
},
checkedIcon: {
width: "20px",
height: "20px",
border: "1px solid #666",
borderRadius: "3px",
},
uncheckedIcon: {
width: "0px",
height: "0px",
padding: "9px",
border: "1px solid #666",
borderRadius: "3px",
},
}));
const DialogNewEntry = (props) => {
const { open, onClose, onCreate } = props;
const { t } = useTranslation();
const classes = useStyles();
const [folderName, setDescription] = useState("");
const [entryType, setEntryType] = useState("website_password");
return (
<Dialog
fullWidth
maxWidth={"sm"}
open={open}
onClose={() => {
onClose();
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{t("NEW_ENTRY")}</DialogTitle>
<DialogContent>
<Grid container>
<Grid item xs={12} sm={12} md={12}>
<SelectFieldEntryType
className={classes.textField}
variant="outlined"
margin="dense"
required
value={entryType}
onChange={setEntryType}
/>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="folderName"
label={t("FOLDER_NAME")}
name="folderName"
autoComplete="folderName"
value={folderName}
required
onChange={(event) => {
setDescription(event.target.value);
}}
/>
</Grid>
</Grid>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
onClose();
}}
>
{t("CLOSE")}
</Button>
<Button
onClick={() => {
onCreate(folderName);
}}
variant="contained"
color="primary"
disabled={!folderName}
>
{t("CREATE")}
</Button>
</DialogActions>
</Dialog>
);
};
DialogNewEntry.propTypes = {
onClose: PropTypes.func.isRequired,
onCreate: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
};
export default DialogNewEntry;

View File

@ -0,0 +1,373 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
import { makeStyles } from "@material-ui/core/styles";
import Dialog from "@material-ui/core/Dialog";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";
import DialogActions from "@material-ui/core/DialogActions";
import Button from "@material-ui/core/Button";
import CheckBoxOutlineBlankIcon from "@material-ui/icons/CheckBoxOutlineBlank";
import { Grid } from "@material-ui/core";
import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment";
import IconButton from "@material-ui/core/IconButton";
import GridContainerErrors from "../grid-container-errors";
import Table from "../table";
import helperService from "../../services/helper";
import store from "../../services/store";
import browserClient from "../../services/browser-client";
import datastoreUserService from "../../services/datastore-user";
import cryptoLibrary from "../../services/crypto-library";
const useStyles = makeStyles((theme) => ({
textField: {
width: "100%",
},
checked: {
color: "#9c27b0",
},
checkedIcon: {
width: "20px",
height: "20px",
border: "1px solid #666",
borderRadius: "3px",
},
uncheckedIcon: {
width: "0px",
height: "0px",
padding: "9px",
border: "1px solid #666",
borderRadius: "3px",
},
}));
const DialogNewUser = (props) => {
const { open, onClose } = props;
const { t } = useTranslation();
const classes = useStyles();
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [domain, setDomain] = useState("");
const allowUserSearchByUsernamePartial = store.getState().server.allowUserSearchByUsernamePartial;
const allowUserSearchByEmail = store.getState().server.allowUserSearchByEmail;
const [errors, setErrors] = useState([]);
const [visualUsername, setVisualUsername] = useState("");
const [foundUsername, setFoundUsername] = useState("");
const [foundUserId, setFoundUserId] = useState("");
const [foundPublicKey, setFoundPublicKey] = useState("");
const [users, setUsers] = useState([]);
let isSubscribed = true;
React.useEffect(() => {
const onError = function (data) {
console.log(data);
};
browserClient.getConfig().then(onNewConfigLoaded, onError);
return () => (isSubscribed = false);
}, []);
const onNewConfigLoaded = (configJson) => {
if (!isSubscribed) {
return;
}
const domain = configJson["backend_servers"][0]["domain"];
setDomain(domain);
};
const showUser = (userId, username, publicKey) => {
setUsers([]);
setFoundUserId(userId);
setFoundUsername(username);
setFoundPublicKey(publicKey);
};
const onSearch = (event) => {
setErrors([]);
setVisualUsername("");
setFoundUserId("");
setFoundUsername("");
setFoundPublicKey("");
let searchUsername = username;
let searchEmail = email;
if (!allowUserSearchByUsernamePartial) {
searchUsername = helperService.formFullUsername(searchUsername, domain);
}
const onSuccess = function (data) {
data = data.data;
if (Object.prototype.toString.call(data) === "[object Array]") {
setUsers(
data.map((user) => {
return [user.id, user.username, user.public_key];
})
);
} else {
showUser(data.id, data.username, data.public_key);
}
};
const onError = function (data) {
if (data.status === 400) {
setErrors(["USER_NOT_FOUND"]);
} else {
console.log(data);
}
};
datastoreUserService.searchUser(searchUsername, searchEmail).then(onSuccess, onError);
};
const onCreate = (event) => {
const userObject = {
id: cryptoLibrary.generateUuid(),
type: "user",
name: "",
data: {
user_id: foundUserId,
user_public_key: foundPublicKey,
user_username: foundUsername,
},
};
if (visualUsername) {
userObject["data"]["user_name"] = visualUsername;
}
if (userObject.data.user_name) {
userObject.name += userObject.data.user_name;
} else {
userObject.name += userObject.data.user_username;
}
userObject.name += " (" + userObject.data.user_public_key + ")";
props.onCreate(userObject);
};
if (users.length > 0) {
const columns = [
{ name: t("ID"), options: { display: false } },
{
name: "",
options: {
filter: false,
sort: false,
empty: false,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<IconButton
onClick={() => {
showUser(tableMeta.rowData[0], tableMeta.rowData[1], tableMeta.rowData[2]);
}}
>
<CheckBoxOutlineBlankIcon />
</IconButton>
);
},
},
},
{
name: t("USERNAME"),
options: {
filter: true,
sort: true,
empty: false,
customBodyRender: (value, tableMeta, updateValue) => {
let username = tableMeta.rowData[1].substring(0, 20);
if (tableMeta.rowData[1].length > 20) {
username = username + "...";
}
return username;
},
},
},
{
name: t("PUBLIC_KEY"),
options: {
filter: true,
sort: true,
empty: false,
customBodyRender: (value, tableMeta, updateValue) => {
let publicKey = tableMeta.rowData[2].substring(0, 50);
if (tableMeta.rowData[2].length > 50) {
publicKey = publicKey + "...";
}
return publicKey;
},
},
},
];
const options = {
filterType: "checkbox",
};
return (
<Dialog
fullWidth
maxWidth={"sm"}
open={open}
onClose={() => {
setUsers([]);
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{t("PICK_USER")}</DialogTitle>
<DialogContent>
<Table data={users} columns={columns} options={options} />
</DialogContent>
<DialogActions>
<Button
onClick={() => {
setUsers([]);
}}
>
{t("CLOSE")}
</Button>
</DialogActions>
</Dialog>
);
} else {
return (
<Dialog
fullWidth
maxWidth={"sm"}
open={open}
onClose={() => {
onClose();
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{t("NEW_USER")}</DialogTitle>
<DialogContent>
<Grid container>
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="username"
label={t("USERNAME")}
InputProps={{
endAdornment:
domain && !allowUserSearchByUsernamePartial && !username.includes("@") ? (
<InputAdornment position="end">{"@" + domain}</InputAdornment>
) : null,
}}
name="username"
autoComplete="username"
value={username}
onChange={(event) => {
setUsername(event.target.value);
}}
/>
</Grid>
{allowUserSearchByEmail && (
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="email"
label={t("EMAIL")}
name="email"
autoComplete="email"
value={email}
onChange={(event) => {
setEmail(event.target.value);
}}
/>
</Grid>
)}
<Grid item xs={12} sm={12} md={12} style={{ marginBottom: "8px" }}>
<Button onClick={onSearch} variant="contained" color="primary" disabled={!username && !email}>
{t("SEARCH")}
</Button>
</Grid>
{Boolean(foundUserId) && (
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="visualUsername"
label={t("NAME_OPTIONAL")}
name="visualUsername"
autoComplete="visualUsername"
value={visualUsername}
onChange={(event) => {
setVisualUsername(event.target.value);
}}
/>
</Grid>
)}
{Boolean(foundUserId) && (
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="foundUsername"
label={t("USERNAME")}
name="foundUsername"
autoComplete="foundUsername"
value={foundUsername}
disabled
/>
</Grid>
)}
{Boolean(foundUserId) && (
<Grid item xs={12} sm={12} md={12}>
<TextField
className={classes.textField}
variant="outlined"
margin="dense"
id="foundPublicKey"
label={t("PUBLIC_KEY")}
name="foundPublicKey"
autoComplete="foundPublicKey"
helperText={t("TO_VERIFY_PUBLIC_KEY")}
value={foundPublicKey}
disabled
/>
</Grid>
)}
</Grid>
<GridContainerErrors errors={errors} setErrors={setErrors} />
</DialogContent>
<DialogActions>
<Button
onClick={() => {
onClose();
}}
>
{t("CLOSE")}
</Button>
<Button
onClick={() => {
onCreate(visualUsername, foundUserId, foundUsername, foundPublicKey);
}}
variant="contained"
color="primary"
disabled={!foundUserId || !foundUsername || !foundPublicKey}
>
{t("CREATE")}
</Button>
</DialogActions>
</Dialog>
);
}
};
DialogNewUser.propTypes = {
onClose: PropTypes.func.isRequired,
onCreate: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
};
export default DialogNewUser;

View File

@ -9,7 +9,7 @@ import MuiAlert from "@material-ui/lab/Alert";
import Button from "@material-ui/core/Button";
import { Grid } from "@material-ui/core";
const VerifyDialog = (props) => {
const DialogVerify = (props) => {
const { open, onClose, onConfirm, entries, affectedEntriesText, title, description } = props;
const { t } = useTranslation();
@ -55,7 +55,7 @@ const VerifyDialog = (props) => {
);
};
VerifyDialog.propTypes = {
DialogVerify.propTypes = {
title: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
affectedEntriesText: PropTypes.string,
@ -65,4 +65,4 @@ VerifyDialog.propTypes = {
open: PropTypes.bool.isRequired,
};
export default VerifyDialog;
export default DialogVerify;

View File

@ -0,0 +1,9 @@
import * as React from "react";
import { createSvgIcon } from "@material-ui/core/utils";
export default createSvgIcon(
<React.Fragment>
<path d="M16.54,11L13,7.46l1.41-1.41l2.12,2.12l4.24-4.24l1.41,1.41L16.54,11z M11,7H2v2h9V7z M21,13.41L19.59,12L17,14.59 L14.41,12L13,13.41L15.59,16L13,18.59L14.41,20L17,17.41L19.59,20L21,18.59L18.41,16L21,13.41z M11,15H2v2h9V15z" />
</React.Fragment>,
"ContentCopy"
);

View File

@ -0,0 +1,98 @@
import React from "react";
import TextField from "@material-ui/core/TextField";
import Autocomplete from "@material-ui/lab/Autocomplete";
import { useTranslation } from "react-i18next";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import itemBlueprintService from "../../services/item-blueprint";
const useStyles = makeStyles((theme) => ({
option: {
fontSize: 15,
"& > span": {
marginRight: 10,
fontSize: 18,
},
},
}));
const SelectFieldEntryType = (props) => {
const classes = useStyles();
const { t } = useTranslation();
const { fullWidth, variant, margin, helperText, error, required, onChange, value, className } = props;
const entryTypes = itemBlueprintService.getEntryTypes();
let defaultValue = null;
if (value) {
defaultValue = entryTypes.find(function (country) {
return country.value === value;
});
}
return (
<Autocomplete
options={entryTypes}
classes={{
option: classes.option,
}}
autoHighlight
getOptionLabel={(option) => {
return option ? t(option.title) : "";
}}
renderOption={(option) => <>{option ? t(option.title) : ""}</>}
onChange={(event, newValue) => {
if (newValue) {
onChange(newValue.value);
} else {
onChange("");
}
}}
getOptionSelected={(option, value) => {
if (option) {
return option.value === value.value;
} else {
return "";
}
}}
value={defaultValue}
renderInput={(params) => (
<TextField
className={className}
{...params}
label={t("TYPE")}
required={required}
margin={margin}
variant={variant}
helperText={helperText}
error={error}
fullWidth={fullWidth}
inputProps={{
...params.inputProps,
autoComplete: "new-password", // disable autocomplete and autofill
}}
/>
)}
/>
);
};
SelectFieldEntryType.defaultProps = {
error: false,
};
SelectFieldEntryType.propTypes = {
value: PropTypes.string,
fullWidth: PropTypes.bool,
error: PropTypes.bool,
required: PropTypes.bool,
helperText: PropTypes.string,
variant: PropTypes.string,
margin: PropTypes.string,
onChange: PropTypes.func,
className: PropTypes.string,
};
export default SelectFieldEntryType;

View File

@ -3,7 +3,7 @@ import TextField from "@material-ui/core/TextField";
import Autocomplete from "@material-ui/lab/Autocomplete";
import { useTranslation } from "react-i18next";
import PropTypes from "prop-types";
import fileRepository from "../services/file-repository";
import fileRepository from "../../services/file-repository";
import { makeStyles } from "@material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
@ -16,7 +16,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
const FileRepositoryTypeSelectField = (props) => {
const SelectFieldFileRepositoryType = (props) => {
const classes = useStyles();
const { t } = useTranslation();
@ -78,11 +78,11 @@ const FileRepositoryTypeSelectField = (props) => {
);
};
FileRepositoryTypeSelectField.defaultProps = {
SelectFieldFileRepositoryType.defaultProps = {
error: false,
};
FileRepositoryTypeSelectField.propTypes = {
SelectFieldFileRepositoryType.propTypes = {
value: PropTypes.string,
fullWidth: PropTypes.bool,
error: PropTypes.bool,
@ -94,4 +94,4 @@ FileRepositoryTypeSelectField.propTypes = {
className: PropTypes.string,
};
export default FileRepositoryTypeSelectField;
export default SelectFieldFileRepositoryType;

View File

@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import { languages } from "../i18n";
import { languages } from "../../i18n";
const useStyles = makeStyles((theme) => ({
option: {
@ -17,12 +17,10 @@ const useStyles = makeStyles((theme) => ({
},
}));
const LanguageSelectField = (props) => {
const SelectFieldLanguage = (props) => {
const classes = useStyles();
const { t } = useTranslation();
//const lngs = fileRepository.getPossibleTypes();
const lngs = [];
Object.entries(languages).forEach(([key, value]) => {
@ -33,9 +31,9 @@ const LanguageSelectField = (props) => {
const { fullWidth, variant, margin, helperText, error, required, onChange, value, className } = props;
let defaulValue = null;
let defaultValue = null;
if (value && lngs && lngs.length) {
defaulValue = lngs.find(function (country) {
defaultValue = lngs.find(function (country) {
return country.value === value;
});
}
@ -65,7 +63,7 @@ const LanguageSelectField = (props) => {
return "";
}
}}
value={defaulValue}
value={defaultValue}
renderInput={(params) => (
<TextField
className={className}
@ -87,11 +85,11 @@ const LanguageSelectField = (props) => {
);
};
LanguageSelectField.defaultProps = {
SelectFieldLanguage.defaultProps = {
error: false,
};
LanguageSelectField.propTypes = {
SelectFieldLanguage.propTypes = {
value: PropTypes.string,
fullWidth: PropTypes.bool,
error: PropTypes.bool,
@ -103,4 +101,4 @@ LanguageSelectField.propTypes = {
className: PropTypes.string,
};
export default LanguageSelectField;
export default SelectFieldLanguage;

View File

@ -22,7 +22,7 @@ class Table extends Component {
};
componentDidMount() {
if (this.props.dataFunction) {
var rowsPerPage = 10;
let rowsPerPage = 10;
if (this.props.options.hasOwnProperty("rowsPerPage")) {
rowsPerPage = this.props.options["rowsPerPage"];
}
@ -99,9 +99,9 @@ class Table extends Component {
defaultOptions["serverSide"] = true;
defaultOptions["count"] = this.state.count;
defaultOptions["onTableChange"] = (action, tableState) => {
var ordering;
let ordering;
if (tableState.sortOrder.hasOwnProperty("name")) {
for (var i = 0; i < tableState.columns.length; i++) {
for (let i = 0; i < tableState.columns.length; i++) {
if (tableState.columns[i].name === tableState.sortOrder["name"]) {
if (tableState.sortOrder["direction"] === "asc") {
ordering = tableState.columns[i].id;
@ -113,7 +113,7 @@ class Table extends Component {
}
}
if (["changePage", "sort", "search", "changeRowsPerPage", "changePage", "filterChange"].includes(action)) {
var params = {
const params = {
page: tableState.page,
page_size: tableState.rowsPerPage,
};

View File

@ -0,0 +1,65 @@
import * as React from "react";
import PropTypes from "prop-types";
import InputLabel from "@material-ui/core/InputLabel";
import FormControl from "@material-ui/core/FormControl";
import InputAdornment from "@material-ui/core/InputAdornment";
import { useTranslation } from "react-i18next";
import OutlinedInput from "@material-ui/core/OutlinedInput";
import IconButton from "@material-ui/core/IconButton";
import ClearIcon from "@material-ui/icons/Clear";
const TextFieldPath = (props) => {
const { value, setPath, ...other } = props;
const { t } = useTranslation();
return (
<FormControl {...other}>
<InputLabel shrink htmlFor="component-simple">
{t("PATH")}
</InputLabel>
<OutlinedInput
id="component-simple"
margin="dense"
inputComponent="div"
notched
value
label={t("PATH")}
inputProps={{
children: (
<>
\
{value.map((path, index) => {
return (
<span
key={index}
onClick={() => {
setPath(value.slice(0, index + 1));
}}
>
{path.name + "\\"}
</span>
);
})}
</>
),
}}
endAdornment={
value.length > 0 && (
<InputAdornment position="end">
<IconButton aria-label="clear path" onClick={() => setPath([])} edge="end">
<ClearIcon />
</IconButton>
</InputAdornment>
)
}
/>
</FormControl>
);
};
TextFieldPath.propTypes = {
value: PropTypes.array.isRequired,
setPath: PropTypes.func.isRequired,
};
export default TextFieldPath;

View File

@ -0,0 +1,52 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import CircularProgress from "@material-ui/core/CircularProgress";
import Button from "@material-ui/core/Button";
import Box from "@material-ui/core/Box";
import ContentCopy from "./icons/ContentCopy";
import cryptoLibraryService from "../services/crypto-library";
import browserClientService from "../services/browser-client";
const TotpCircle = (props) => {
const { period, digits, algorithm, code, ...rest } = props;
const [progress, setProgress] = React.useState(10);
const [token, setToken] = useState("");
React.useEffect(() => {
const timer = setInterval(() => {
setToken(cryptoLibraryService.getTotpToken(code, period, algorithm, digits));
const percentage = 100 - (((period || 30) - (Math.round(new Date().getTime() / 1000.0) % (period || 30))) / (period || 30)) * 100;
setProgress(percentage);
}, 500);
return () => {
clearInterval(timer);
};
}, []);
return (
<Box position="relative" display="inline-flex" {...rest}>
<CircularProgress variant="determinate" value={progress} size={"100%"} />
<Box top={0} left={0} bottom={0} right={0} position="absolute" display="flex" alignItems="center" justifyContent="center">
<Button endIcon={<ContentCopy>copy</ContentCopy>} onClick={() => browserClientService.copyToClipboard(token)}>
{token}
</Button>
</Box>
</Box>
);
};
TotpCircle.defaultProps = {
period: 30,
digits: 6,
algorithm: "SHA1",
code: "",
};
TotpCircle.propTypes = {
period: PropTypes.number,
digits: PropTypes.number,
algorithm: PropTypes.string,
code: PropTypes.string,
};
export default TotpCircle;

View File

@ -0,0 +1,146 @@
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";
import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import GetAppIcon from "@material-ui/icons/GetApp";
import browserClient from "../services/browser-client";
import deviceService from "../services/device";
import action from "../actions/bound-action-creators";
const useStyles = makeStyles((theme) => ({
overlay: {
width: "100%",
position: "fixed",
zIndex: 1400,
top: 0,
left: 0,
backgroundColor: "#2dbb93",
overflowY: "hidden",
transition: "0.5s",
"& a": {
padding: "8px",
textDecoration: "none",
display: "block",
transition: "0.3s",
},
"& a:hover": {
color: "#fff",
},
"& a:focus": {
color: "#fff",
},
},
overlayContent: {
position: "relative",
width: "90%",
textAlign: "center",
paddingLeft: "5%",
},
closeBtn: {
position: "absolute",
top: 0,
right: "5px",
},
wrapIcon: {
verticalAlign: "middle",
display: "inline-flex",
},
downloadIcon: {
fontSize: "1.2rem",
},
}));
const DownloadBanner = (props) => {
const classes = useStyles();
const { t } = useTranslation();
const [disableDownloadBanner, setDisableDownloadBanner] = useState(false);
const isDownloadBannerHidden = useSelector((state) => state.client.hideDownloadBanner);
const showAndroidDownload = deviceService.isMobileAndroid();
const showIosDownload = deviceService.isMobileIos();
const showChromeDownload = !deviceService.isMobile() && deviceService.isChrome() && browserClient.getClientType() === "webclient";
const showFirefoxDownload = !deviceService.isMobile() && deviceService.isFirefox() && browserClient.getClientType() === "webclient";
React.useEffect(() => {
browserClient.getConfig().then(onNewConfigLoaded);
// cancel subscription to useEffect
return () => (isSubscribed = false);
}, []);
let isSubscribed = true;
const onNewConfigLoaded = (configJson) => {
if (!isSubscribed) {
return;
}
setDisableDownloadBanner(Boolean(configJson["disable_download_bar"]));
};
const hideDownloadBanner = (event) => {
action.setHideDownloadBanner(true);
};
return (
<div id="PsonoDownloadBanner">
{!disableDownloadBanner && !isDownloadBannerHidden && showAndroidDownload && (
<div className={classes.overlay}>
<a href="#" className={classes.closeBtn} onClick={hideDownloadBanner}>
<i className="fa fa-times" aria-hidden="true" />
</a>
<div className={classes.overlayContent}>
<a href="https://play.google.com/store/apps/details?id=com.psono.psono" target="_blank" rel="noopener">
{t("DOWNLOAD_PSONO")}
<i className="fa fa-download" aria-hidden="true" />
</a>
</div>
</div>
)}
{!disableDownloadBanner && !isDownloadBannerHidden && showIosDownload && (
<div className={classes.overlay}>
<a href="#" className={classes.closeBtn} onClick={hideDownloadBanner}>
<i className="fa fa-times" aria-hidden="true" />
</a>
<div className={classes.overlayContent}>
<a href="https://apps.apple.com/us/app/psono-password-manager/id1545581224" target="_blank" rel="noopener">
{t("DOWNLOAD_PSONO")}
<i className="fa fa-download" aria-hidden="true" />
</a>
</div>
</div>
)}
{!disableDownloadBanner && !isDownloadBannerHidden && showChromeDownload && (
<div className={classes.overlay}>
<a href="#" className={classes.closeBtn} onClick={hideDownloadBanner}>
<i className="fa fa-times" aria-hidden="true" />
</a>
<div className={classes.overlayContent}>
<a
href="https://chrome.google.com/webstore/detail/psono-free-password-manag/eljmjmgjkbmpmfljlmklcfineebidmlo"
target="_blank"
rel="noopener"
>
<Typography variant="body2" className={classes.wrapIcon}>
{t("DOWNLOAD_PSONO")} <GetAppIcon className={classes.downloadIcon} />
</Typography>
</a>
</div>
</div>
)}
{!disableDownloadBanner && !isDownloadBannerHidden && showFirefoxDownload && (
<div className={classes.overlay}>
<a href="#" className={classes.closeBtn} onClick={hideDownloadBanner}>
<i className="fa fa-times" aria-hidden="true" />
</a>
<div className={classes.overlayContent}>
<a href="https://addons.mozilla.org/de/firefox/addon/psono-pw-password-manager/" target="_blank" rel="noopener">
{t("DOWNLOAD_PSONO")}
<i className="fa fa-download" aria-hidden="true" />
</a>
</div>
</div>
)}
</div>
);
};
export default DownloadBanner;

View File

@ -1,394 +0,0 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import datastorePassword from "../services/datastore-password";
import widget from "../services/widget";
import { ClipLoader } from "react-spinners";
import DatastoreTree from "../components/datastore-tree";
import DialogNewFolder from "../components/dialogs/new-folder";
import DialogNewShare from "../components/dialogs/new-share";
const useStyles = makeStyles((theme) => ({
loader: {
marginTop: "30px",
marginBottom: "30px",
margin: "auto",
},
}));
const PasswordDatastore = (props) => {
const classes = useStyles();
const { search } = props;
let isSubscribed = true;
const [datastore, setDatastore] = useState(null);
const [newFolderOpen, setNewFolderOpen] = useState(false);
const [newFolderData, setNewFolderData] = useState({});
const [newShareOpen, setNewShareOpen] = useState(false);
React.useEffect(() => {
datastorePassword.getPasswordDatastore().then(onNewDatastoreLoaded);
return () => (isSubscribed = false);
}, []);
const onNewDatastoreLoaded = (data) => {
if (!isSubscribed) {
return;
}
setDatastore(data);
};
const onNewFolderCreate = (name) => {
// called once someone clicked the CREATE button in the dialog closes with the new name
setNewFolderOpen(false);
widget.openNewFolder(newFolderData["parent"], newFolderData["path"], datastore, datastorePassword, name);
};
const onNewFolder = (parent, path) => {
// called whenever someone clicks on a new folder Icon
setNewFolderOpen(true);
setNewFolderData({
parent: parent,
path: path,
});
};
const onNewShareCreate = (name) => {
// called once someone clicked the CREATE button in the dialog closes with the new name
setNewFolderOpen(false);
// const onShare = (event) => {
// handleClose(event);
// if (content.hasOwnProperty("share_rights") && content.share_rights.grant === false) {
// return;
// }
//
// /**
// * little wrapper to create the share rights from the selected users / groups and rights for a given nonce and
// * a given share_id and key
// *
// * @param share_id
// * @param share_secret_key
// * @param node
// * @param users
// * @param groups
// * @param selected_users
// * @param selected_groups
// * @param selected_rights
// */
// var create_share_rights = function (share_id, share_secret_key, node, users, groups, selected_users, selected_groups, selected_rights) {
// var i;
// var modalInstance;
//
// // found a user that has been selected, lets create the rights for him
// var rights = {
// read: selected_rights.indexOf("read") > -1,
// write: selected_rights.indexOf("write") > -1,
// grant: selected_rights.indexOf("grant") > -1,
// };
//
// // generate the title
// // TODO create form field with this default value and read value from form
//
// var title = "";
// if (typeof node.type === "undefined") {
// // we have a folder
// title = "Folder with title '" + node.name + "'";
// } else {
// // we have an item
// title = _blueprints[node.type].name + " with title '" + node.name + "'";
// }
//
// // get the type
// var type = "";
// if (typeof node.type === "undefined") {
// // we have a folder
// type = "folder";
// } else {
// // we have an item
// type = node.type;
// }
//
// function create_user_share_right(user) {
// var onSuccess = function (data) {
// // pass
// };
// var onError = function (result) {
// var title;
// var description;
// if (result.data === null) {
// title = "UNKNOWN_ERROR";
// description = "UNKNOWN_ERROR_CHECK_BROWSER_CONSOLE";
// } else if (
// result.data.hasOwnProperty("non_field_errors") &&
// (result.data["non_field_errors"].indexOf("USER_DOES_NOT_EXIST_PROBABLY_DELETED") !== -1 ||
// result.data["non_field_errors"].indexOf("Target user does not exist.") !== -1)
// ) {
// title = "UNKNOWN_USER";
// description = _translations.USER_DOES_NOT_EXIST_PROBABLY_DELETED + " " + user.name;
// } else if (result.data.hasOwnProperty("non_field_errors")) {
// title = "ERROR";
// description = result.data["non_field_errors"][0];
// } else {
// title = "UNKNOWN_ERROR";
// description = "UNKNOWN_ERROR_CHECK_BROWSER_CONSOLE";
// }
//
// modalInstance = $uibModal.open({
// templateUrl: "view/modal/error.html",
// controller: "ModalErrorCtrl",
// resolve: {
// title: function () {
// return title;
// },
// description: function () {
// return description;
// },
// },
// });
//
// modalInstance.result.then(
// function (breadcrumbs) {
// // pass
// },
// function () {
// // cancel triggered
// }
// );
// };
//
// return registrations["create_share_right"](
// title,
// type,
// share_id,
// user.data.user_id,
// undefined,
// user.data.user_public_key,
// undefined,
// share_secret_key,
// rights["read"],
// rights["write"],
// rights["grant"]
// ).then(onSuccess, onError);
// }
//
// for (i = 0; i < users.length; i++) {
// if (selected_users.indexOf(users[i].id) < 0) {
// continue;
// }
// create_user_share_right(users[i]);
// }
//
// function create_group_share_right(group) {
// var onSuccess = function (data) {
// // pass
// };
// var onError = function (result) {
// var title;
// var description;
// if (result.data === null) {
// title = "UNKNOWN_ERROR";
// description = "UNKNOWN_ERROR_CHECK_BROWSER_CONSOLE";
// } else if (result.data.hasOwnProperty("non_field_errors")) {
// title = "ERROR";
// description = result.data["non_field_errors"][0];
// } else {
// title = "UNKNOWN_ERROR";
// description = "UNKNOWN_ERROR_CHECK_BROWSER_CONSOLE";
// }
//
// modalInstance = $uibModal.open({
// templateUrl: "view/modal/error.html",
// controller: "ModalErrorCtrl",
// resolve: {
// title: function () {
// return title;
// },
// description: function () {
// return description;
// },
// },
// });
//
// modalInstance.result.then(
// function (breadcrumbs) {
// // pass
// },
// function () {
// // cancel triggered
// }
// );
// };
//
// var group_secret_key = registrations["get_group_secret_key"](
// group.group_id,
// group.secret_key,
// group.secret_key_nonce,
// group.secret_key_type,
// group.public_key
// );
// return registrations["create_share_right"](
// title,
// type,
// share_id,
// undefined,
// group.group_id,
// undefined,
// group_secret_key,
// share_secret_key,
// rights["read"],
// rights["write"],
// rights["grant"]
// ).then(onSuccess, onError);
// }
//
// for (i = 0; i < groups.length; i++) {
// if (selected_groups.indexOf(groups[i].group_id) < 0) {
// continue;
// }
// create_group_share_right(groups[i]);
// }
// };
//
// /**
// * Users and or / shares have been selected in the modal and the final "Share Now" button was
// * clicked
// *
// * @param content
// */
// var on_modal_close_success = function (content) {
// // content = { node: "...", path: "...", selected_users: "...", users: "..."}
//
// var has_no_users = !content.users || content.users.length < 1 || !content.selected_users || content.selected_users.length < 1;
//
// var has_no_groups = !content.groups || content.groups.length < 1 || !content.selected_groups || content.selected_groups.length < 1;
//
// if (has_no_users && has_no_groups) {
// // TODO echo not shared message because no user / group selected
// return;
// }
//
// if (content.node.hasOwnProperty("share_id")) {
// // its already a share, so generate only the share_rights
//
// create_share_rights(
// content.node.share_id,
// content.node.share_secret_key,
// content.node,
// content.users,
// content.groups,
// content.selected_users,
// content.selected_groups,
// content.selected_rights
// );
// } else {
// // its not yet a share, so generate the share, generate the share_rights and update
// // the datastore
//
// registrations["get_password_datastore"]().then(function (datastore) {
// var path = content.path.slice();
// var closest_share_info = registrations["get_closest_parent_share"](path, datastore, null, 1);
// var parent_share = closest_share_info["closest_share"];
// var parent_share_id;
// var parent_datastore_id;
//
// if (parent_share !== false && parent_share !== null) {
// parent_share_id = parent_share.share_id;
// } else {
// parent_datastore_id = datastore.datastore_id;
// }
//
// // create the share
// registrations["create_share"](content.node, parent_share_id, parent_datastore_id, content.node.id).then(function (share_details) {
// var item_path = content.path.slice();
// var item_path_copy = content.path.slice();
// var item_path_copy2 = content.path.slice();
//
// // create the share right
// create_share_rights(
// share_details.share_id,
// share_details.secret_key,
// content.node,
// content.users,
// content.groups,
// content.selected_users,
// content.selected_groups,
// content.selected_rights
// );
//
// // update datastore and / or possible parent shares
// var search = registrations["find_in_datastore"](item_path, datastore);
//
// if (typeof content.node.type === "undefined") {
// // we have an item
// delete search[0][search[1]].secret_id;
// delete search[0][search[1]].secret_key;
// }
// search[0][search[1]].share_id = share_details.share_id;
// search[0][search[1]].share_secret_key = share_details.secret_key;
//
// // update node in our displayed datastore
// content.node.share_id = share_details.share_id;
// content.node.share_secret_key = share_details.secret_key;
//
// var changed_paths = registrations["on_share_added"](share_details.share_id, item_path_copy, datastore, 1);
//
// var parent_path = item_path_copy2.slice();
// parent_path.pop();
//
// changed_paths.push(parent_path);
//
// registrations["save_datastore_content"](datastore, changed_paths);
// });
// });
// }
// };
//
// var modalInstance = $uibModal.open({
// templateUrl: "view/modal/share-entry.html",
// controller: "ModalShareEntryCtrl",
// backdrop: "static",
// resolve: {
// node: function () {
// return item;
// },
// path: function () {
// return path;
// },
// },
// });
//
// // User clicked the final share button
// modalInstance.result.then(on_modal_close_success, function () {
// // cancel triggered
// });
// };
};
const onNewShare = (parent, path) => {
// called whenever someone clicks on a new folder Icon
setNewFolderOpen(true);
setNewFolderData({
parent: parent,
path: path,
});
};
datastorePassword.modifyTreeForSearch(search, datastore);
return (
<>
{!datastore && (
<div className={classes.loader}>
<ClipLoader />
</div>
)}
{datastore && <DatastoreTree datastore={datastore} search={search} onNewFolder={onNewFolder} onNewShare={onNewShare} />}
{newFolderOpen && <DialogNewFolder open={newFolderOpen} onClose={() => setNewFolderOpen(false)} onCreate={onNewFolderCreate} />}
{newShareOpen && <DialogNewShare open={newShareOpen} onClose={() => setNewShareOpen(false)} onShare={onNewShareCreate} />}
</>
);
};
PasswordDatastore.propTypes = {
search: PropTypes.string.isRequired,
};
export default PasswordDatastore;

View File

@ -8,7 +8,6 @@ import HomeIcon from "@material-ui/icons/Home";
import ShareIcon from "@material-ui/icons/Share";
import PersonIcon from "@material-ui/icons/Person";
import GroupIcon from "@material-ui/icons/Group";
import AssignmentIcon from "@material-ui/icons/Assignment";
import LinkIcon from "@material-ui/icons/Link";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
@ -23,6 +22,7 @@ import { Link } from "react-router-dom";
import { useLocation } from "react-router-dom";
import PropTypes from "prop-types";
import RuleIcon from "../components/icons/Rule";
import browserClient from "../services/browser-client";
const drawerWidth = 240;
@ -76,7 +76,7 @@ const useStyles = makeStyles((theme) => ({
listItemText: {
fontSize: "14px",
"& .MuiBadge-badge": {
fontSize: "0.50rem",
fontSize: "0.75rem",
height: "15px",
minWidth: "15px",
color: "#fff",
@ -86,11 +86,11 @@ const useStyles = makeStyles((theme) => ({
},
listItemIcon: {
color: "#b1b6c1",
minWidth: "36px",
minWidth: theme.spacing(4),
},
listItemIconSelected: {
color: "#fff",
minWidth: "36px",
minWidth: theme.spacing(4),
},
icon: {
fontSize: "18px",
@ -222,7 +222,7 @@ const Sidebar = (props) => {
selected={isSelected(/^\/security-report$/)}
>
<ListItemIcon className={`${isSelected(/^\/security-report$/) ? classes.listItemIconSelected : classes.listItemIcon}`}>
<AssignmentIcon className={classes.icon} />
<RuleIcon className={classes.icon} />
</ListItemIcon>
<ListItemText
classes={{ primary: classes.listItemText }}

View File

@ -56,7 +56,7 @@ const useStyles = makeStyles((theme) => ({
},
},
listItemIcon: {
minWidth: "36px",
minWidth: theme.spacing(4),
},
icon: {
fontSize: "18px",

View File

@ -1,72 +0,0 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import datastoreUser from "../services/datastore-user";
import datastorePassword from "../services/datastore-password";
import { ClipLoader } from "react-spinners";
import DatastoreTree from "../components/datastore-tree";
import widget from "../services/widget";
import DialogNewFolder from "../components/dialogs/new-folder";
const useStyles = makeStyles((theme) => ({
loader: {
marginTop: "30px",
marginBottom: "30px",
margin: "auto",
},
}));
const UserDatastore = (props) => {
const classes = useStyles();
const { search } = props;
let isSubscribed = true;
const [datastore, setDatastore] = useState(null);
const [newFolderOpen, setNewFolderOpen] = useState(false);
const [newFolderData, setNewFolderData] = useState({});
React.useEffect(() => {
datastoreUser.getUserDatastore().then(onNewDatastoreLoaded);
return () => (isSubscribed = false);
}, []);
const onNewDatastoreLoaded = (data) => {
if (!isSubscribed) {
return;
}
setDatastore(data);
};
const onNewFolderCreate = (name) => {
// called once someone clicked the CREATE button in the dialog closes with the new name
setNewFolderOpen(false);
widget.openNewFolder(newFolderData["parent"], newFolderData["path"], datastore, datastorePassword, name);
};
const onNewFolder = (parent, path) => {
// called whenever someone clicks on a new folder Icon
setNewFolderOpen(true);
setNewFolderData({
parent: parent,
path: path,
});
};
datastorePassword.modifyTreeForSearch(search, datastore);
return (
<>
{!datastore && (
<div className={classes.loader}>
<ClipLoader />
</div>
)}
{datastore && <DatastoreTree datastore={datastore} search={search} onNewFolder={onNewFolder} />}
{newFolderOpen && <DialogNewFolder open={newFolderOpen} onClose={() => setNewFolderOpen(false)} onCreate={onNewFolderCreate} />}
</>
);
};
UserDatastore.propTypes = {
search: PropTypes.string.isRequired,
};
export default UserDatastore;

View File

@ -1,4 +1,12 @@
import { LOGOUT, SET_CLIENT_URL, ENABLE_OFFLINE_MODE, DISABLE_OFFLINE_MODE, SET_NOTIFICATION_ON_COPY, SET_DISABLE_BROWSER_PM } from "../actions/action-types";
import {
LOGOUT,
SET_CLIENT_URL,
ENABLE_OFFLINE_MODE,
DISABLE_OFFLINE_MODE,
SET_NOTIFICATION_ON_COPY,
SET_DISABLE_BROWSER_PM,
SET_HIDE_DOWNLOAD_BANNER,
} from "../actions/action-types";
const default_url = "";
@ -8,6 +16,7 @@ function client(
offlineMode: false,
notificationOnCopy: true,
disableBrowserPm: true,
hideDownloadBanner: false,
},
action
) {
@ -36,6 +45,10 @@ function client(
return Object.assign({}, state, {
disableBrowserPm: action.disableBrowserPm,
});
case SET_HIDE_DOWNLOAD_BANNER:
return Object.assign({}, state, {
hideDownloadBanner: action.hideDownloadBanner,
});
default:
return state;
}

View File

@ -5,7 +5,7 @@
import axios from "axios";
import converterService from "./converter";
const call = function (fileserverUrl, method, endpoint, data, headers, transformRequest, responseType) {
function call(fileserverUrl, method, endpoint, data, headers, transformRequest, responseType) {
if (!transformRequest) {
transformRequest = $http.defaults.transformRequest;
}
@ -31,7 +31,7 @@ const call = function (fileserverUrl, method, endpoint, data, headers, transform
axios(req).then(onSuccess, onError);
});
};
}
/**
* Ajax PUT request to upload a file chunk to AWS S3
@ -42,7 +42,7 @@ const call = function (fileserverUrl, method, endpoint, data, headers, transform
*
* @returns {Promise} promise
*/
const upload = function (signedUrl, fields, chunk) {
function upload(signedUrl, fields, chunk) {
const endpoint = ""; // the signed url already has everything
const method = "POST";
const data = new FormData();
@ -58,7 +58,7 @@ const upload = function (signedUrl, fields, chunk) {
};
return call(signedUrl, method, endpoint, data, headers, angular.identity);
};
}
/**
* Ajax GET request to download a file chunk from AWS S3
@ -67,7 +67,7 @@ const upload = function (signedUrl, fields, chunk) {
*
* @returns {Promise} promise with the data
*/
const download = function (signedUrl) {
function download(signedUrl) {
const endpoint = ""; // the signed url already has everything
const method = "GET";
const data = null;
@ -82,14 +82,14 @@ const download = function (signedUrl) {
if (data.status === 400) {
data.data = JSON.parse(converterService.bytesToString(data.data));
}
return $q.reject(data);
return Promise.reject(data);
}
);
};
}
const service = {
const apiAwsService = {
upload: upload,
download: download,
};
export default service;
export default apiAwsService;

View File

@ -5,14 +5,14 @@
import axios from "axios";
import converterService from "./converter";
function call(signed_url, method, endpoint, data, headers, transformRequest, responseType) {
function call(signedUrl, method, endpoint, data, headers, transformRequest, responseType) {
if (!transformRequest) {
transformRequest = $http.defaults.transformRequest;
}
const req = {
method: method,
url: signed_url + endpoint,
url: signedUrl + endpoint,
data: data,
transformRequest: transformRequest,
responseType: responseType,
@ -36,38 +36,38 @@ function call(signed_url, method, endpoint, data, headers, transformRequest, res
/**
* Ajax PUT request to upload a file chunk to Azure Blob Storage
*
* @param {string} signed_url The signed ulr
* @param {string} signedUrl The signed ulr
* @param {Blob} chunk The content of the chunk to upload
*
* @returns {Promise} promise
*/
function upload(signed_url, chunk) {
var endpoint = ""; // the signed url already has everything
var method = "PUT";
function upload(signedUrl, chunk) {
const endpoint = ""; // the signed url already has everything
const method = "PUT";
var headers = {
const headers = {
"x-ms-blob-type": "BlockBlob",
"Content-Type": undefined,
};
return call(signed_url, method, endpoint, chunk, headers, angular.identity);
return call(signedUrl, method, endpoint, chunk, headers, angular.identity);
}
/**
* Ajax GET request to download a file chunk from Azure Blob Storage
*
* @param {string} signed_url The signed ulr
* @param {string} signedUrl The signed ulr
*
* @returns {Promise} promise with the data
*/
function download(signed_url) {
var endpoint = ""; // the signed url already has everything
var method = "GET";
var data = null;
function download(signedUrl) {
const endpoint = ""; // the signed url already has everything
const method = "GET";
const data = null;
var headers = {};
const headers = {};
return call(signed_url, method, endpoint, data, headers, undefined, "arraybuffer").then(
return call(signedUrl, method, endpoint, data, headers, undefined, "arraybuffer").then(
function (data) {
return data;
},
@ -75,14 +75,14 @@ function download(signed_url) {
if (data.status === 400) {
data.data = JSON.parse(converterService.bytesToString(data.data));
}
return $q.reject(data);
return Promise.reject(data);
}
);
}
const service = {
const apiAzureBlobService = {
upload: upload,
download: download,
};
export default service;
export default apiAzureBlobService;

View File

@ -5,14 +5,14 @@
import axios from "axios";
import converterService from "./converter";
function call(signed_url, method, endpoint, data, headers, transformRequest, responseType) {
function call(signedUrl, method, endpoint, data, headers, transformRequest, responseType) {
if (!transformRequest) {
transformRequest = $http.defaults.transformRequest;
}
const req = {
method: method,
url: signed_url + endpoint,
url: signedUrl + endpoint,
data: data,
transformRequest: transformRequest,
responseType: responseType,
@ -36,16 +36,16 @@ function call(signed_url, method, endpoint, data, headers, transformRequest, res
/**
* Ajax PUT request to upload a file chunk to AWS S3
*
* @param {string} signed_url The signed ulr
* @param {string} signedUrl The signed ulr
* @param {object} fields Array of fields that need to be part of the request
* @param {Blob} chunk The content of the chunk to upload
*
* @returns {Promise} promise
*/
function upload(signed_url, fields, chunk) {
var endpoint = ""; // the signed url already has everything
var method = "POST";
var data = new FormData();
function upload(signedUrl, fields, chunk) {
const endpoint = ""; // the signed url already has everything
const method = "POST";
const data = new FormData();
for (let field_name in fields) {
if (!fields.hasOwnProperty(field_name)) {
continue;
@ -53,28 +53,28 @@ function upload(signed_url, fields, chunk) {
data.append(field_name, fields[field_name]);
}
data.append("file", chunk);
var headers = {
const headers = {
"Content-Type": undefined,
};
return call(signed_url, method, endpoint, data, headers, angular.identity);
return call(signedUrl, method, endpoint, data, headers, angular.identity);
}
/**
* Ajax GET request to download a file chunk from AWS S3
*
* @param {string} signed_url The signed ulr
* @param {string} signedUrl The signed ulr
*
* @returns {Promise} promise with the data
*/
function download(signed_url) {
var endpoint = ""; // the signed url already has everything
var method = "GET";
var data = null;
function download(signedUrl) {
const endpoint = ""; // the signed url already has everything
const method = "GET";
const data = null;
var headers = {};
const headers = {};
return call(signed_url, method, endpoint, data, headers, undefined, "arraybuffer").then(
return call(signedUrl, method, endpoint, data, headers, undefined, "arraybuffer").then(
function (data) {
return data;
},
@ -82,14 +82,14 @@ function download(signed_url) {
if (data.status === 400) {
data.data = JSON.parse(converterService.bytesToString(data.data));
}
return $q.reject(data);
return Promise.reject(data);
}
);
}
const service = {
const apiBackblazeService = {
upload: upload,
download: download,
};
export default service;
export default apiBackblazeService;

View File

@ -2964,7 +2964,7 @@ const sendSecurityReport = function (token, sessionSecretKey, entries, check_hav
return call(method, endpoint, data, headers, sessionSecretKey);
};
const service = {
const apiClientService = {
info: info,
login: login,
samlInitiateLogin: samlInitiateLogin,
@ -3070,4 +3070,4 @@ const service = {
sendSecurityReport: sendSecurityReport,
};
export default service;
export default apiClientService;

View File

@ -53,9 +53,9 @@ function call(signed_url, method, endpoint, data, headers, transformRequest, res
*/
function upload(signed_url, fields, chunk) {
var endpoint = ''; // the signed url already has everything
var method = "POST";
var data = new FormData();
const endpoint = ''; // the signed url already has everything
const method = "POST";
const data = new FormData();
for (let field_name in fields) {
if (!fields.hasOwnProperty(field_name)) {
continue;
@ -63,12 +63,12 @@ function upload(signed_url, fields, chunk) {
data.append(field_name, fields[field_name]);
}
data.append('file', chunk);
var headers = {
const headers = {
'Content-Type': undefined
};
return call(signed_url, method, endpoint, data, headers, angular.identity);
};
}
/**
* @ngdoc
@ -84,11 +84,11 @@ function upload(signed_url, fields, chunk) {
*/
function download(signed_url) {
var endpoint = ''; // the signed url already has everything
var method = "GET";
var data = null;
const endpoint = ''; // the signed url already has everything
const method = "GET";
const data = null;
var headers = {
const headers = {
};
return call(signed_url, method, endpoint, data, headers, undefined, 'arraybuffer').then(function(data) {
@ -97,14 +97,14 @@ function download(signed_url) {
if (data.status === 400) {
data.data = JSON.parse(converterService.bytesToString(data.data));
}
return $q.reject(data)
return Promise.reject(data)
});
};
}
const service = {
const apiDoService = {
upload: upload,
download: download
};
export default service;
export default apiDoService;

View File

@ -5,7 +5,7 @@
import axios from "axios";
import converterService from './converter';
function call(fileserver_url, method, endpoint, data, headers, transformRequest, responseType) {
function call(fileserverUrl, method, endpoint, data, headers, transformRequest, responseType) {
if (!transformRequest) {
transformRequest = $http.defaults.transformRequest;
@ -13,7 +13,7 @@ function call(fileserver_url, method, endpoint, data, headers, transformRequest,
const req = {
method: method,
url: fileserver_url + endpoint,
url: fileserverUrl + endpoint,
data: data,
transformRequest: transformRequest,
responseType: responseType
@ -40,84 +40,84 @@ function call(fileserver_url, method, endpoint, data, headers, transformRequest,
/**
* Ajax POST request to upload a file chunk
*
* @param {string} fileserver_url The url of the target fileserver
* @param {string} file_transfer_id The file transfer id
* @param {string} fileserverUrl The url of the target fileserver
* @param {string} fileTransferId The file transfer id
* @param {Blob} chunk The content of the chunk to upload
* @param {string} ticket The ticket to authenticate the upload
* @param {string} ticket_nonce The nonce of the ticket
* @param {string} ticketNonce The nonce of the ticket
*
* @returns {Promise} promise
*/
function upload(fileserver_url, file_transfer_id, chunk, ticket, ticket_nonce) {
function upload(fileserverUrl, fileTransferId, chunk, ticket, ticketNonce) {
var endpoint = '/upload/';
var method = "POST";
var data = new FormData();
data.append('file_transfer_id', file_transfer_id);
const endpoint = '/upload/';
const method = "POST";
const data = new FormData();
data.append('file_transfer_id', fileTransferId);
data.append('chunk', chunk);
data.append('ticket', ticket);
data.append('ticket_nonce', ticket_nonce);
var headers = {
data.append('ticket_nonce', ticketNonce);
const headers = {
'Content-Type': undefined
};
return call(fileserver_url, method, endpoint, data, headers, angular.identity);
};
return call(fileserverUrl, method, endpoint, data, headers, angular.identity);
}
/**
* Ajax POST request to download a file chunk
*
* @param {string} fileserver_url The url of the target fileserver
* @param {string} file_transfer_id The file transfer id
* @param {string} fileserverUrl The url of the target fileserver
* @param {string} fileTransferId The file transfer id
* @param {string} ticket The ticket to authenticate the download
* @param {string} ticket_nonce The nonce of the ticket
* @param {string} ticketNonce The nonce of the ticket
*
* @returns {Promise} promise
*/
function download(fileserver_url, file_transfer_id, ticket, ticket_nonce) {
function download(fileserverUrl, fileTransferId, ticket, ticketNonce) {
var endpoint = '/download/';
var method = "POST";
var data = {
file_transfer_id: file_transfer_id,
const endpoint = '/download/';
const method = "POST";
const data = {
file_transfer_id: fileTransferId,
ticket: ticket,
ticket_nonce: ticket_nonce
ticket_nonce: ticketNonce
};
var headers = {
const headers = {
};
return call(fileserver_url, method, endpoint, data, headers, undefined, 'arraybuffer').then(function(data) {
return call(fileserverUrl, method, endpoint, data, headers, undefined, 'arraybuffer').then(function(data) {
return data
},function(data) {
if (data.status === 400) {
data.data = JSON.parse(converterService.bytesToString(data.data));
}
return $q.reject(data)
return Promise.reject(data)
});
};
}
/**
* Ajax GET request to get the server info
*
* @param {string} fileserver_url The url of the target fileserver
* @param {string} fileserverUrl The url of the target fileserver
*
* @returns {Promise} promise
*/
var info = function (fileserver_url) {
function info(fileserverUrl) {
var endpoint = '/info/';
var method = "GET";
var data = null;
var headers = null;
const endpoint = '/info/';
const method = "GET";
const data = null;
const headers = null;
return call(fileserver_url, method, endpoint, data, headers);
};
return call(fileserverUrl, method, endpoint, data, headers);
}
const service = {
const apiFileserverService = {
info: info,
upload: upload,
download: download
};
export default service;
export default apiFileserverService;

View File

@ -36,37 +36,37 @@ function call(signed_url, method, endpoint, data, headers, transformRequest, res
/**
* Ajax PUT request to upload a file chunk to GCP storage
*
* @param {string} signed_url The signed ulr
* @param {string} signedUrl The signed ulr
* @param {Blob} chunk The content of the chunk to upload
*
* @returns {Promise} promise
*/
function upload(signed_url, chunk) {
var endpoint = ""; // the signed url already has everything
var method = "PUT";
function upload(signedUrl, chunk) {
const endpoint = ""; // the signed url already has everything
const method = "PUT";
var headers = {
const headers = {
"Content-Type": "application/octet-stream",
};
return call(signed_url, method, endpoint, chunk, headers, angular.identity);
return call(signedUrl, method, endpoint, chunk, headers, angular.identity);
}
/**
* Ajax GET request to download a file chunk from GCP storage
*
* @param {string} signed_url The signed ulr
* @param {string} signedUrl The signed ulr
*
* @returns {Promise} promise with the data
*/
function download(signed_url) {
var endpoint = ""; // the signed url already has everything
var method = "GET";
var data = null;
function download(signedUrl) {
const endpoint = ""; // the signed url already has everything
const method = "GET";
const data = null;
var headers = {};
const headers = {};
return call(signed_url, method, endpoint, data, headers, undefined, "arraybuffer").then(
return call(signedUrl, method, endpoint, data, headers, undefined, "arraybuffer").then(
function (data) {
return data;
},
@ -79,9 +79,9 @@ function download(signed_url) {
);
}
const service = {
const apiGcpService = {
upload: upload,
download: download,
};
export default service;
export default apiGcpService;

View File

@ -140,19 +140,19 @@ function createApiKey(title, restrictToSecrets, allowInsecureAccess, allowReadAc
const token = store.getState().user.token;
const sessionSecretKey = store.getState().user.sessionSecretKey;
var apiKeySecretKey = cryptoLibrary.generateSecretKey();
var api_key_public_private_key_pair = cryptoLibrary.generatePublicPrivateKeypair();
const apiKeySecretKey = cryptoLibrary.generateSecretKey();
const api_key_public_private_key_pair = cryptoLibrary.generatePublicPrivateKeypair();
var api_key_private_key_enc = cryptoLibrary.encryptSecretKey(api_key_public_private_key_pair.private_key);
var apiKeySecretKey_enc = cryptoLibrary.encryptSecretKey(apiKeySecretKey);
const api_key_private_key_enc = cryptoLibrary.encryptSecretKey(api_key_public_private_key_pair.private_key);
const apiKeySecretKey_enc = cryptoLibrary.encryptSecretKey(apiKeySecretKey);
var user_private_key_enc = cryptoLibrary.encryptData(store.getState().user.userPrivateKey, apiKeySecretKey);
var user_secret_key_enc = cryptoLibrary.encryptData(store.getState().user.userSecretKey, apiKeySecretKey);
const user_private_key_enc = cryptoLibrary.encryptData(store.getState().user.userPrivateKey, apiKeySecretKey);
const user_secret_key_enc = cryptoLibrary.encryptData(store.getState().user.userSecretKey, apiKeySecretKey);
var verify_key = cryptoLibrary.getVerifyKey(api_key_public_private_key_pair.private_key);
const verify_key = cryptoLibrary.getVerifyKey(api_key_public_private_key_pair.private_key);
const onSuccess = function (result) {
var apiKeyId = result.data["api_key_id"];
const apiKeyId = result.data["api_key_id"];
return addSecretsToApiKey(apiKeyId, apiKeySecretKey, secrets).then(function () {
return {
api_key_id: apiKeyId,
@ -266,7 +266,7 @@ function getServerParameter() {
};
}
const service = {
const apiKeysService = {
readApiKey: readApiKey,
readApiKeys: readApiKeys,
readApiKeySecrets: readApiKeySecrets,
@ -279,4 +279,4 @@ const service = {
apiKeysDisabled: apiKeysDisabled,
getServerParameter: getServerParameter,
};
export default service;
export default apiKeysService;

View File

@ -43,9 +43,9 @@ function call(signed_url, method, endpoint, data, headers, transformRequest, res
* @returns {Promise} promise
*/
function upload(signed_url, fields, chunk) {
var endpoint = ""; // the signed url already has everything
var method = "POST";
var data = new FormData();
const endpoint = ""; // the signed url already has everything
const method = "POST";
const data = new FormData();
for (let field_name in fields) {
if (!fields.hasOwnProperty(field_name)) {
continue;
@ -53,7 +53,7 @@ function upload(signed_url, fields, chunk) {
data.append(field_name, fields[field_name]);
}
data.append("file", chunk);
var headers = {
const headers = {
"Content-Type": undefined,
};
@ -68,11 +68,11 @@ function upload(signed_url, fields, chunk) {
* @returns {Promise} promise with the data
*/
function download(signed_url) {
var endpoint = ""; // the signed url already has everything
var method = "GET";
var data = null;
const endpoint = ""; // the signed url already has everything
const method = "GET";
const data = null;
var headers = {};
const headers = {};
return call(signed_url, method, endpoint, data, headers, undefined, "arraybuffer").then(
function (data) {
@ -82,14 +82,14 @@ function download(signed_url) {
if (data.status === 400) {
data.data = JSON.parse(converterService.bytesToString(data.data));
}
return $q.reject(data);
return Promise.reject(data);
}
);
}
const service = {
const apiOtherS3Service = {
upload: upload,
download: download,
};
export default service;
export default apiOtherS3Service;

View File

@ -54,8 +54,8 @@ function range(hash_chars) {
}
const service = {
const apiPwnedpasswordsService = {
range: range
};
export default service;
export default apiPwnedpasswordsService;

View File

@ -338,10 +338,12 @@ function loadConfig() {
if (newConfig.hasOwnProperty("backend_servers")) {
for (let i = 0; i < newConfig["backend_servers"].length; i++) {
if (newConfig["backend_servers"][i].hasOwnProperty("url")) {
continue;
if (!newConfig["backend_servers"][i].hasOwnProperty("url")) {
newConfig["backend_servers"][i]["url"] = parsed_url["base_url"] + "/server";
}
if (!newConfig["backend_servers"][i].hasOwnProperty("domain")) {
newConfig["backend_servers"][i]["domain"] = helperService.getDomain(newConfig["backend_servers"][i]["url"]);
}
newConfig["backend_servers"][i]["url"] = parsed_url["base_url"] + "/server";
}
}
@ -777,7 +779,7 @@ function getOfflineCacheEncryptionKey(fnc) {
}
}
const service = {
const browserClientService = {
registerAuthRequiredListener: registerAuthRequiredListener,
getClientType: getClientType,
openTab: openTab,
@ -806,4 +808,4 @@ const service = {
getOfflineCacheEncryptionKey: getOfflineCacheEncryptionKey,
};
export default service;
export default browserClientService;

View File

@ -413,7 +413,7 @@ function bytesToString(srcBytes, encoding) {
return new TextDecoder(tmp_encoding).decode(srcBytes);
}
const service = {
const converterService = {
// Conversion functions
encodeUtf8: encodeUtf8,
encodeLatin1: encodeLatin1,
@ -434,4 +434,4 @@ const service = {
bytesToString: bytesToString,
};
export default service;
export default converterService;

View File

@ -565,7 +565,7 @@ function decryptSecretKey(text, nonce) {
return decryptData(text, nonce, store.getState().user.userSecretKey);
}
const service = {
const cryptoLibraryService = {
random: random,
randomBytes: randomBytes,
sha1: sha1,
@ -597,4 +597,4 @@ const service = {
decryptSecretKey: decryptSecretKey,
};
export default service;
export default cryptoLibraryService;

View File

@ -22,10 +22,10 @@ describe('Service: cryptoLibraryService test suite #2', function() {
/*
it("randomBytes doesn't return the the same in 1000 repetitions", function () {
var num, numbers, random_numbers;
let num, numbers, random_numbers;
numbers = 1000;
random_numbers = (function () {
var i, ref, results;
let i, ref, results;
results = [];
for (num = i = 1, ref = numbers; 1 <= ref ? i <= ref : i >= ref; num = 1 <= ref ? ++i : --i) {
results.push(cryptoLibraryService.randomBytes(32));

View File

@ -565,6 +565,7 @@ function saveDatastoreContent(datastore, paths) {
}
}
const promises = [];
for (let prop in closest_shares) {
if (!closest_shares.hasOwnProperty(prop)) {
continue;
@ -574,7 +575,7 @@ function saveDatastoreContent(datastore, paths) {
hideSubShareContent(duplicate);
if (prop === "datastore") {
datastoreService.saveDatastoreContent(type, description, duplicate);
promises.push(datastoreService.saveDatastoreContent(type, description, duplicate));
} else {
const share_id = duplicate.share_id;
const secret_key = duplicate.share_secret_key;
@ -583,9 +584,11 @@ function saveDatastoreContent(datastore, paths) {
delete duplicate.secret_key;
delete duplicate.share_rights;
shareService.writeShare(share_id, duplicate, secret_key);
promises.push(shareService.writeShare(share_id, duplicate, secret_key));
}
}
return Promise.all(promises);
}
/**
@ -1262,7 +1265,7 @@ function analyzeBreadcrumbs(breadcrumbs, datastore) {
let parent_share_id;
let parent_datastore_id;
if (typeof breadcrumbs.id_breadcrumbs !== "undefined") {
if (typeof breadcrumbs.id_breadcrumbs !== "undefined" && breadcrumbs.id_breadcrumbs.length > 0) {
path = breadcrumbs.id_breadcrumbs.slice();
const path_copy = breadcrumbs.id_breadcrumbs.slice();
parent_path = breadcrumbs.id_breadcrumbs.slice();
@ -1509,7 +1512,7 @@ function getAllOwnPgpKeys() {
};
for (let i = 0; i < own_pgp_secrets.length; i++) {
secretService.read_secret(own_pgp_secrets[i].secret_id, own_pgp_secrets[i].secret_key).then(onSuccess, onError);
secretService.readSecret(own_pgp_secrets[i].secret_id, own_pgp_secrets[i].secret_key).then(onSuccess, onError);
}
});
});
@ -1523,7 +1526,7 @@ function getAllOwnPgpKeys() {
shareService.register("get_all_child_shares", getAllChildShares);
settingsService.register("get_password_datastore", getPasswordDatastore);
const service = {
const datastorePasswordService = {
generate: generate,
getPasswordDatastore: getPasswordDatastore,
getDatastoreWithId: getDatastoreWithId,
@ -1553,4 +1556,4 @@ const service = {
updatePathsRecursive: updatePathsRecursive,
};
export default service;
export default datastorePasswordService;

View File

@ -41,8 +41,8 @@ function saveSettingsDatastore(content) {
return datastore.saveDatastoreContent(type, description, content);
}
const service = {
const datastoreSettingService = {
getSettingsDatastore: getSettingsDatastore,
saveSettingsDatastore: saveSettingsDatastore,
};
export default service;
export default datastoreSettingService;

File diff suppressed because it is too large Load Diff

View File

@ -599,7 +599,7 @@ function onLogout() {
browserClient.on("logout", onLogout);
const service = {
const datastoreService = {
getDatastoreOverview: getDatastoreOverview,
getDatastoreId: getDatastoreId,
getDatastoreWithId: getDatastoreWithId,
@ -618,4 +618,4 @@ const service = {
filterDatastoreContent: filterDatastoreContent,
register: register,
};
export default service;
export default datastoreService;

View File

@ -105,7 +105,7 @@ function isOpera() {
* @returns {string} Returns the device's description
*/
function getDeviceDescription() {
var description = "";
let description = "";
if (typeof clientJs.getDeviceVendor() !== "undefined") {
description = description + clientJs.getDeviceVendor() + " ";
}
@ -127,7 +127,7 @@ function getDeviceDescription() {
return description;
}
const service = {
const deviceService = {
getDeviceFingerprint: getDeviceFingerprint,
isMobileAndroid: isMobileAndroid,
isMobileIos: isMobileIos,
@ -139,4 +139,4 @@ const service = {
isOpera: isOpera,
getDeviceDescription: getDeviceDescription,
};
export default service;
export default deviceService;

View File

@ -89,11 +89,11 @@ function deleteDuo(duoId) {
return apiClient.deleteDuo(token, sessionSecretKey, duoId).then(onSuccess, onError);
}
const service = {
const duoService = {
createDuo,
readDuo,
activateDuo,
deleteDuo,
};
export default service;
export default duoService;

View File

@ -83,10 +83,10 @@ function deleteEmergencyCode(emergencyCodeId) {
return apiClient.deleteEmergencyCode(token, sessionSecretKey, emergencyCodeId).then(onSuccess, onError);
}
const service = {
const emergencyCodeService = {
readEmergencyCodes,
createEmergencyCode,
deleteEmergencyCode,
};
export default service;
export default emergencyCodeService;

View File

@ -472,7 +472,7 @@ function getDoSpacesRegions() {
return doSpacesRegions;
}
const service = {
const fileRepositoryService = {
accept: accept,
decline: decline,
createFileRepositoryRight: createFileRepositoryRight,
@ -489,4 +489,4 @@ const service = {
getAwsRegions: getAwsRegions,
getDoSpacesRegions: getDoSpacesRegions,
};
export default service;
export default fileRepositoryService;

View File

@ -718,7 +718,7 @@ function register(key, func) {
registrations[key] = func;
}
const service = {
const fileTransferService = {
readFile: readFile,
createFile: createFile,
upload: upload,
@ -729,4 +729,4 @@ const service = {
downloadFile: downloadFile,
register: register,
};
export default service;
export default fileTransferService;

View File

@ -90,11 +90,11 @@ function deleteGa(googleAuthenticatorId) {
return apiClientService.deleteGa(token, sessionSecretKey, googleAuthenticatorId).then(onSuccess, onError);
}
const service = {
const googleAuthenticatorService = {
createGa,
readGa,
activateGa,
deleteGa,
};
export default service;
export default googleAuthenticatorService;

View File

@ -511,7 +511,7 @@ function declineMembership(c) {
//itemBlueprint.register('getGroupSecretKey', getGroupSecretKey);
const service = {
const groupsService = {
getGroupSecretKey: getGroupSecretKey,
getGroupPrivateKey: getGroupPrivateKey,
decryptSecretKey: decryptSecretKey,
@ -530,4 +530,4 @@ const service = {
acceptMembership: acceptMembership,
declineMembership: declineMembership,
};
export default service;
export default groupsService;

View File

@ -523,7 +523,7 @@ function getPasswordFilter(test) {
return filter;
}
const service = {
const helperService = {
parseUrl: parseUrl,
isValidUrl: isValidUrl,
isValidJson: isValidJson,
@ -543,4 +543,4 @@ const service = {
getPasswordFilter: getPasswordFilter,
};
export default service;
export default helperService;

View File

@ -236,7 +236,7 @@ function deleteKnownHost(fingerprint) {
updateKnownHosts(known_hosts);
}
const service = {
const hostService = {
getKnownHosts: getKnownHosts,
getCurrentHost: getCurrentHost,
getCurrentHostUrl: getCurrentHostUrl,
@ -249,4 +249,4 @@ const service = {
updateKnownHosts: updateKnownHosts,
};
export default service;
export default hostService;

View File

@ -22,7 +22,7 @@ let INDEX_TYPE = 5;
*/
function identifyRows(line) {
for (let i = 0; i < line.length; i++) {
var column_description = line[i].toLowerCase();
const column_description = line[i].toLowerCase();
if (column_description === "notes") {
INDEX_NOTES = i;
} else if(column_description === "password") {
@ -55,7 +55,7 @@ function identifyRows(line) {
* @returns {string} Returns the appropriate type (note or website_password)
*/
function getType(line) {
var type = line[INDEX_TYPE].toLowerCase();
const type = line[INDEX_TYPE].toLowerCase();
if (type === 'secure note') {
return 'note'
}
@ -89,7 +89,7 @@ function getType(line) {
*/
function transferIntoNote(line) {
var note_notes = '';
let note_notes = '';
if (line[INDEX_USERNAME]) {
note_notes = note_notes + line[INDEX_USERNAME] + "\n";
}
@ -125,7 +125,7 @@ function transferIntoNote(line) {
*/
function transferIntoWebsitePassword(line) {
var parsed_url = helperService.parseUrl(line[INDEX_URL]);
const parsed_url = helperService.parseUrl(line[INDEX_URL]);
return {
id : cryptoLibrary.generateUuid(),
@ -169,7 +169,7 @@ function transferIntoApplicationPassword(line) {
* @returns {*} The secrets object
*/
function transformToSecret(line) {
var type = getType(line);
const type = getType(line);
if (type === 'note') {
return transferIntoNote(line);
} else if (type === 'website_password') {
@ -189,7 +189,7 @@ function transformToSecret(line) {
*/
function gather_secrets(datastore, secrets, csv) {
var line;
let line;
for (let i = 0; i < csv.length; i++) {
line = csv[i];
@ -203,7 +203,7 @@ function gather_secrets(datastore, secrets, csv) {
continue
}
var secret = transformToSecret(line);
const secret = transformToSecret(line);
if (secret === null) {
//empty line
@ -221,7 +221,7 @@ function gather_secrets(datastore, secrets, csv) {
* @returns {Array} The array of arrays representing the CSV
*/
function parse_csv(data) {
var csv = Papa.parse(data);
const csv = Papa.parse(data);
if (csv['errors'].length > 0) {
throw new Error(csv['errors'][0]['message']);
@ -246,18 +246,19 @@ function parse_csv(data) {
*/
function parser(data) {
var d = new Date();
var n = d.toISOString();
const d = new Date();
const n = d.toISOString();
var secrets = [];
var datastore = {
const secrets = [];
const datastore = {
'id': cryptoLibrary.generateUuid(),
'name': 'Import ' + n,
'items': []
};
let csv;
try {
var csv = parse_csv(data);
csv = parse_csv(data);
} catch(err) {
return null;
}
@ -270,8 +271,8 @@ function parser(data) {
}
}
const service = {
const import1passwordCsvService = {
parser,
};
export default service;
export default import1passwordCsvService;

View File

@ -18,8 +18,8 @@ const INDEX_PASSWORD = 3;
*
* @returns {*} The secrets object
*/
var transform_to_secret = function (line) {
var parsed_url = helperService.parseUrl(line[INDEX_URL]);
function transformToSecret(line) {
const parsed_url = helperService.parseUrl(line[INDEX_URL]);
return {
id: cryptoLibrary.generateUuid(),
@ -33,7 +33,7 @@ var transform_to_secret = function (line) {
website_password_url: line[INDEX_URL],
website_password_title: line[INDEX_NAME],
};
};
}
/**
* Fills the datastore with their content, together with the secrets object
@ -42,8 +42,8 @@ var transform_to_secret = function (line) {
* @param {[]} secrets The array containing all the found secrets
* @param {[]} csv The array containing all the found secrets
*/
function gather_secrets(datastore, secrets, csv) {
var line;
function gatherSecrets(datastore, secrets, csv) {
let line;
for (let i = 0; i < csv.length; i++) {
line = csv[i];
if (i === 0) {
@ -53,7 +53,7 @@ function gather_secrets(datastore, secrets, csv) {
continue;
}
var secret = transform_to_secret(line);
const secret = transformToSecret(line);
if (secret === null) {
//empty line
continue;
@ -69,8 +69,8 @@ function gather_secrets(datastore, secrets, csv) {
* @param {string} data The raw data to parse
* @returns {Array} The array of arrays representing the CSV
*/
function parse_csv(data) {
var csv = Papa.parse(data);
function parseCsv(data) {
const csv = Papa.parse(data);
if (csv["errors"].length > 0) {
throw new Error(csv["errors"][0]["message"]);
@ -94,24 +94,25 @@ function parse_csv(data) {
* @returns {{datastore, secrets: Array} | null}
*/
function parser(data) {
var d = new Date();
var n = d.toISOString();
const d = new Date();
const n = d.toISOString();
var secrets = [];
var datastore = {
const secrets = [];
const datastore = {
id: cryptoLibrary.generateUuid(),
name: "Import " + n,
folders: [],
items: [],
};
let csv;
try {
var csv = parse_csv(data);
csv = parseCsv(data);
} catch (err) {
return null;
}
gather_secrets(datastore, secrets, csv);
gatherSecrets(datastore, secrets, csv);
return {
datastore: datastore,
@ -119,8 +120,8 @@ function parser(data) {
};
}
const service = {
const importChromeCsvService = {
parser,
};
export default service;
export default importChromeCsvService;

View File

@ -46,8 +46,8 @@ function detect_type(item) {
*
* @param {object} item The item to analyze
*/
function contains_totp(item) {
var contains_totp_field = false;
function containsTotp(item) {
let contains_totp_field = false;
if (item.hasOwnProperty("fields")) {
for (let i = 0; i < item["fields"].length; i++) {
if (item["fields"][i]["type"] === "totp" && item["fields"][i]["value"] !== "") {
@ -65,15 +65,15 @@ function contains_totp(item) {
*
* @returns {*} The secrets object
*/
var transform_to_website_password = function (item) {
var name = "";
var urlfilter = "";
var website_password_url_filter = "";
var website_password_password = "";
var website_password_username = "";
var website_password_notes = "";
var website_password_url = "";
var website_password_title = "";
function transformToWebsitePassword(item) {
let name = "";
let urlfilter = "";
let website_password_url_filter = "";
let website_password_password = "";
let website_password_username = "";
let website_password_notes = "";
let website_password_url = "";
let website_password_title = "";
if (item.hasOwnProperty("title")) {
name = item["title"];
@ -114,7 +114,7 @@ var transform_to_website_password = function (item) {
website_password_url: website_password_url,
website_password_title: website_password_title,
};
};
}
/**
* Takes an item and transforms it into a application password entry
@ -123,12 +123,12 @@ var transform_to_website_password = function (item) {
*
* @returns {*} The secrets object
*/
var transform_to_application_password = function (item) {
var name = "";
var application_password_password = "";
var application_password_username = "";
var application_password_notes = "";
var application_password_title = "";
function transformToApplicationPassword(item) {
let name = "";
let application_password_password = "";
let application_password_username = "";
let application_password_notes = "";
let application_password_title = "";
if (item.hasOwnProperty("title")) {
name = item["title"];
@ -164,7 +164,7 @@ var transform_to_application_password = function (item) {
application_password_notes: application_password_notes,
application_password_title: application_password_title,
};
};
}
/**
* Takes an item and transforms it into a bookmark entry
@ -173,13 +173,13 @@ var transform_to_application_password = function (item) {
*
* @returns {*} The secrets object
*/
var transform_to_bookmark = function (item) {
var name = "";
var urlfilter = "";
var bookmark_url_filter = "";
var bookmark_notes = "";
var bookmark_url = "";
var bookmark_title = "";
function transformToBookmark(item) {
let name = "";
let urlfilter = "";
let bookmark_url_filter = "";
let bookmark_notes = "";
let bookmark_url = "";
let bookmark_title = "";
if (item.hasOwnProperty("title")) {
name = item["title"];
@ -214,7 +214,7 @@ var transform_to_bookmark = function (item) {
bookmark_url: bookmark_url,
bookmark_title: bookmark_title,
};
};
}
/**
* Takes an item and transforms it into a note
@ -223,10 +223,10 @@ var transform_to_bookmark = function (item) {
*
* @returns {*} The secrets object
*/
var transform_to_note = function (item) {
var name = "";
var note_notes = "";
var note_title = "";
function transformToNote(item) {
let name = "";
let note_notes = "";
let note_title = "";
if (item.hasOwnProperty("title")) {
name = item["title"];
@ -256,7 +256,7 @@ var transform_to_note = function (item) {
note_notes: note_notes,
note_title: note_title,
};
};
}
/**
* Takes an item and transforms it into a note
@ -265,11 +265,11 @@ var transform_to_note = function (item) {
*
* @returns {*} The secrets object
*/
var transform_to_totp_code = function (item) {
var name = "";
var totp_notes = "";
var totp_code = "";
var totp_title = "";
function transformToTotpCode(item) {
let name = "";
let totp_notes = "";
let totp_code = "";
let totp_title = "";
if (item.hasOwnProperty("title")) {
name = item["title"];
@ -300,7 +300,7 @@ var transform_to_totp_code = function (item) {
totp_code: totp_code,
totp_title: totp_title,
};
};
}
/**
* Searches a given parsedData recursive and puts them all into the provided secrets array
@ -309,9 +309,9 @@ var transform_to_totp_code = function (item) {
* @param {[]} secrets The array containing all the found secrets
* @param {object} parsedData The objects
*/
function gather_secrets(datastore, secrets, parsedData) {
var i;
var folder_index = {};
function gatherSecrets(datastore, secrets, parsedData) {
let i;
const folder_index = {};
if (parsedData.hasOwnProperty("folders")) {
for (i = 0; i < parsedData["folders"].length; i++) {
@ -325,26 +325,26 @@ function gather_secrets(datastore, secrets, parsedData) {
if (parsedData.hasOwnProperty("items")) {
for (i = 0; i < parsedData["items"].length; i++) {
var detected_type = detect_type(parsedData["items"][i]);
var contains_totp_code = contains_totp(parsedData["items"][i]);
const detected_type = detect_type(parsedData["items"][i]);
const contains_totp_code = containsTotp(parsedData["items"][i]);
var crafted_secrets = [];
const crafted_secrets = [];
if (detected_type === "website_password") {
crafted_secrets.push(transform_to_website_password(parsedData["items"][i]));
crafted_secrets.push(transformToWebsitePassword(parsedData["items"][i]));
} else if (detected_type === "application_password") {
crafted_secrets.push(transform_to_application_password(parsedData["items"][i]));
crafted_secrets.push(transformToApplicationPassword(parsedData["items"][i]));
} else if (detected_type === "bookmark") {
crafted_secrets.push(transform_to_bookmark(parsedData["items"][i]));
crafted_secrets.push(transformToBookmark(parsedData["items"][i]));
} else {
crafted_secrets.push(transform_to_note(parsedData["items"][i]));
crafted_secrets.push(transformToNote(parsedData["items"][i]));
}
if (contains_totp_code) {
crafted_secrets.push(transform_to_totp_code(parsedData["items"][i]));
crafted_secrets.push(transformToTotpCode(parsedData["items"][i]));
}
var parent_folder = null;
let parent_folder = null;
if (parsedData["items"][i].hasOwnProperty("folders")) {
for (let ii = 0; ii < parsedData["items"][i]["folders"].length; ii++) {
if (folder_index.hasOwnProperty(parsedData["items"][i]["folders"][ii])) {
@ -384,24 +384,25 @@ function gather_secrets(datastore, secrets, parsedData) {
* @returns {{datastore, secrets: Array} | null}
*/
function parser(data) {
var d = new Date();
var n = d.toISOString();
const d = new Date();
const n = d.toISOString();
var secrets = [];
var datastore = {
const secrets = [];
const datastore = {
id: cryptoLibrary.generateUuid(),
name: "Import " + n,
folders: [],
items: [],
};
let parsedData;
try {
var parsedData = JSON.parse(data);
parsedData = JSON.parse(data);
} catch (err) {
return null;
}
gather_secrets(datastore, secrets, parsedData);
gatherSecrets(datastore, secrets, parsedData);
return {
datastore: datastore,
@ -409,8 +410,8 @@ function parser(data) {
};
}
const service = {
const importEnpassJsonService = {
parser,
};
export default service;
export default importEnpassJsonService;

View File

@ -19,8 +19,8 @@ const INDEX_COMMENTS = 4;
*
* @returns {*} The secrets object
*/
var transform_to_secret = function (line) {
var parsed_url = helperService.parseUrl(line[INDEX_WEB_SITE]);
function transformToSecret(line) {
const parsed_url = helperService.parseUrl(line[INDEX_WEB_SITE]);
return {
id: cryptoLibrary.generateUuid(),
@ -34,7 +34,7 @@ var transform_to_secret = function (line) {
website_password_url: line[INDEX_WEB_SITE],
website_password_title: line[INDEX_ACCOUNT].replace(/(?:\\(.))/g, "$1"),
};
};
}
/**
* Fills the datastore with folders their content and together with the secrets object
@ -44,7 +44,7 @@ var transform_to_secret = function (line) {
* @param {[]} csv The array containing all the found secrets
*/
function gather_secrets(datastore, secrets, csv) {
var line;
let line;
for (let i = 0; i < csv.length; i++) {
line = csv[i];
@ -55,7 +55,7 @@ function gather_secrets(datastore, secrets, csv) {
continue;
}
var secret = transform_to_secret(line);
const secret = transformToSecret(line);
if (secret === null) {
//empty line
continue;
@ -72,7 +72,7 @@ function gather_secrets(datastore, secrets, csv) {
* @returns {Array} The array of arrays representing the CSV
*/
function parse_csv(data) {
var csv = Papa.parse(data);
const csv = Papa.parse(data);
if (csv["errors"].length > 0) {
throw new Error(csv["errors"][0]["message"]);
@ -96,18 +96,19 @@ function parse_csv(data) {
* @returns {{datastore, secrets: Array} | null}
*/
function parser(data) {
var d = new Date();
var n = d.toISOString();
const d = new Date();
const n = d.toISOString();
var secrets = [];
var datastore = {
const secrets = [];
const datastore = {
id: cryptoLibrary.generateUuid(),
name: "Import " + n,
items: [],
};
let csv;
try {
var csv = parse_csv(data);
csv = parse_csv(data);
} catch (err) {
console.log(data);
console.log(err);
@ -122,8 +123,8 @@ function parser(data) {
};
}
const service = {
const importKeepassInfoCsv = {
parser,
};
export default service;
export default importKeepassInfoCsv;

View File

@ -15,7 +15,7 @@ describe('Service: importKeePassInfoCsv test suite', function () {
cryptoLibrary.generateUuid = jest.fn();
cryptoLibrary.generateUuid.mockImplementation(() => generic_uuid);
var input = "\"Account\",\"Login Name\",\"Password\",\"Web Site\",\"Comments\"\n" +
const input = "\"Account\",\"Login Name\",\"Password\",\"Web Site\",\"Comments\"\n" +
"\"Sample Entry Title\",\"Greg\",\"ycXfARD2G1AOBzLlhtbn\",\"http://www.somepage.net\",\"Some notes...\"\n" +
"\"Yet Another Sample Entry\",\"Michael\",\"qgyXFZ1iGgNqzg+eZter\",\"http://www.anotherpage.org\",\"More notes...\"\n" +
"\"Entry To Test Special Characters\",\"!§$%&/()=?´`_#²³{[]}\\\\\",\"öäüÖÄÜ߀@<>µ©®\",\"http://www.website.com\",\"The user name and password fields contain special characters.\"\n" +
@ -28,9 +28,9 @@ describe('Service: importKeePassInfoCsv test suite', function () {
"This is a multi-line comment.\"\n" +
"";
var output = importKeePassInfoCsv.parser(input);
const output = importKeePassInfoCsv.parser(input);
var expected_output = {
const expected_output = {
"datastore": {
"id": generic_uuid,
"name": output.datastore.name,

View File

@ -5,7 +5,7 @@ import cryptoLibrary from "./crypto-library";
import helperService from "./helper";
const fastXmlParser = require("fast-xml-parser");
function unescape_value(value) {
function unescapeValue(value) {
value = value.replace(/&lt;/g, "<");
value = value.replace(/&gt;/g, ">");
value = value.replace(/&amp;/g, "&");
@ -20,11 +20,11 @@ function unescape_value(value) {
*
* @returns {*} The secrets object
*/
var transform_to_secret = function (line) {
function transformToSecret(line) {
if (!line.hasOwnProperty("String") || !line.String) {
return null;
}
var secret = {
const secret = {
id: cryptoLibrary.generateUuid(),
type: "website_password",
name: "",
@ -38,15 +38,15 @@ var transform_to_secret = function (line) {
};
for (let i = 0; i < line.String.length; i++) {
var value = line.String[i];
const value = line.String[i];
if (!value.hasOwnProperty("Key")) {
continue;
}
if (!value.hasOwnProperty("Value")) {
continue;
}
var key = value["Key"];
var val = unescape_value(value["Value"]);
const key = value["Key"];
const val = unescapeValue(value["Value"]);
if (key === "Notes") {
secret["website_password_notes"] = val;
@ -62,7 +62,7 @@ var transform_to_secret = function (line) {
}
if (key === "URL") {
var parsed_url = helperService.parseUrl(val);
const parsed_url = helperService.parseUrl(val);
secret["urlfilter"] = parsed_url.authority || "";
secret["website_password_url_filter"] = parsed_url.authority || "";
secret["website_password_url"] = val;
@ -74,7 +74,7 @@ var transform_to_secret = function (line) {
}
return secret;
};
}
/**
* Fills the datastore with folders their content and together with the secrets object
@ -83,10 +83,10 @@ var transform_to_secret = function (line) {
* @param {[]} secrets The array containing all the found secrets
* @param {Document} xml The parsed XML document
*/
function gather_secrets(datastore, secrets, xml) {
var i;
var next_folder;
var entries;
function gatherSecrets(datastore, secrets, xml) {
let i;
let next_folder;
let entries;
if (xml.hasOwnProperty("Entry")) {
if (Object.prototype.toString.call(xml.Entry) === "[object Array]") {
entries = xml.Entry;
@ -95,7 +95,7 @@ function gather_secrets(datastore, secrets, xml) {
}
for (i = 0; i < entries.length; i++) {
var secret = transform_to_secret(entries[i]);
const secret = transformToSecret(entries[i]);
if (secret === null) {
//empty line
continue;
@ -122,7 +122,7 @@ function gather_secrets(datastore, secrets, xml) {
folders: [],
items: [],
};
gather_secrets(next_folder, secrets, entries[i]);
gatherSecrets(next_folder, secrets, entries[i]);
datastore["folders"].push(next_folder);
}
}
@ -136,9 +136,9 @@ function gather_secrets(datastore, secrets, xml) {
* @param {string} xmlString The raw data to parse
* @returns {object} The array of arrays representing the XML
*/
function parse_xml(xmlString) {
function parseXml(xmlString) {
fastXmlParser.validate(xmlString); // Throws sometimes an error if its no valid xml
var parsed_xml = fastXmlParser.parse(xmlString, { parseNodeValue: false });
const parsed_xml = fastXmlParser.parse(xmlString, { parseNodeValue: false });
if (
!parsed_xml.hasOwnProperty("KeePassFile") ||
!parsed_xml["KeePassFile"].hasOwnProperty("Root") ||
@ -164,24 +164,25 @@ function parse_xml(xmlString) {
* @returns {{datastore, secrets: Array} | null}
*/
function parser(data) {
var d = new Date();
var n = d.toISOString();
const d = new Date();
const n = d.toISOString();
var secrets = [];
var datastore = {
const secrets = [];
const datastore = {
id: cryptoLibrary.generateUuid(),
name: "Import " + n,
items: [],
folders: [],
};
let xml;
try {
var xml = parse_xml(data);
xml = parseXml(data);
} catch (err) {
return null;
}
gather_secrets(datastore, secrets, xml);
gatherSecrets(datastore, secrets, xml);
return {
datastore: datastore,
@ -189,8 +190,8 @@ function parser(data) {
};
}
const service = {
const importKeepassInfoXmlService = {
parser,
};
export default service;
export default importKeepassInfoXmlService;

View File

@ -22,9 +22,9 @@ const INDEX_NOTES = 5;
*
* @returns {*} Returns the specified folder object, containing items and folders
*/
function get_folder_helper(path, folder) {
var next_folder_name;
var next_folder;
function getFolderHelper(path, folder) {
let next_folder_name;
let next_folder;
if (path.length === 0) {
return folder;
@ -48,7 +48,7 @@ function get_folder_helper(path, folder) {
folder["folders"].push(next_folder);
}
return get_folder_helper(path, next_folder);
return getFolderHelper(path, next_folder);
}
/**
@ -59,11 +59,11 @@ function get_folder_helper(path, folder) {
*
* @returns {object} Returns the folder
*/
function get_folder(line, datastore) {
var path = line[INDEX_GROUP].split("/");
function getFolder(line, datastore) {
let path = line[INDEX_GROUP].split("/");
path.shift(); // Drop "Root" element
return get_folder_helper(path, datastore);
return getFolderHelper(path, datastore);
}
/**
@ -73,8 +73,8 @@ function get_folder(line, datastore) {
*
* @returns {*} The secrets object
*/
var transform_to_secret = function (line) {
var parsed_url = helperService.parseUrl(line[INDEX_URL]);
function transformToSecret(line) {
const parsed_url = helperService.parseUrl(line[INDEX_URL]);
return {
id: cryptoLibrary.generateUuid(),
@ -88,7 +88,7 @@ var transform_to_secret = function (line) {
website_password_url: line[INDEX_URL],
website_password_title: line[INDEX_TITLE],
};
};
}
/**
* Fills the datastore with folders their content and together with the secrets object
@ -98,8 +98,8 @@ var transform_to_secret = function (line) {
* @param {[]} csv The array containing all the found secrets
*/
function gather_secrets(datastore, secrets, csv) {
var line;
var folder;
let line;
let folder;
for (let i = 0; i < csv.length; i++) {
line = csv[i];
@ -110,8 +110,8 @@ function gather_secrets(datastore, secrets, csv) {
continue;
}
folder = get_folder(line, datastore);
var secret = transform_to_secret(line);
folder = getFolder(line, datastore);
const secret = transformToSecret(line);
if (secret === null) {
//empty line
continue;
@ -127,8 +127,8 @@ function gather_secrets(datastore, secrets, csv) {
* @param {string} data The raw data to parse
* @returns {Array} The array of arrays representing the CSV
*/
function parse_csv(data) {
var csv = Papa.parse(data);
function parseCsv(data) {
const csv = Papa.parse(data);
if (csv["errors"].length > 0) {
throw new Error(csv["errors"][0]["message"]);
@ -152,19 +152,20 @@ function parse_csv(data) {
* @returns {{datastore, secrets: Array} | null}
*/
function parser(data) {
var d = new Date();
var n = d.toISOString();
const d = new Date();
const n = d.toISOString();
var secrets = [];
var datastore = {
const secrets = [];
const datastore = {
id: cryptoLibrary.generateUuid(),
name: "Import " + n,
folders: [],
items: [],
};
let csv;
try {
var csv = parse_csv(data);
csv = parseCsv(data);
} catch (err) {
return null;
}
@ -177,8 +178,8 @@ function parser(data) {
};
}
const service = {
const importKeepassxOrgCsvService = {
parser,
};
export default service;
export default importKeepassxOrgCsvService;

View File

@ -13,7 +13,7 @@ describe('Service: importKeePassXOrgCsv test suite', function () {
cryptoLibrary.generateUuid = jest.fn();
cryptoLibrary.generateUuid.mockImplementation(() => generic_uuid);
var input = "\"Group\",\"Title\",\"Username\",\"Password\",\"URL\",\"Notes\"\n" +
const input = "\"Group\",\"Title\",\"Username\",\"Password\",\"URL\",\"Notes\"\n" +
"\"Root\",\"home dir \",\"meldron\",\"xxxx\",\"\",\"xxx\"\n" +
"\"Root\",\"CA\",\"\",\"xxx\"\"xxx\",\"\",\"\"\n" +
"\"Root/Internet\",\"tunnelbroker\",\"xxx\",\"xxx\",\"tunnelbroker.net\",\"\"\n" +
@ -36,9 +36,9 @@ describe('Service: importKeePassXOrgCsv test suite', function () {
"\"Root/Company/ABC API\",\"MusicService_v1\",\"abctest\",\"5/a5F3asdas\",\"https://park.company-technologies.com/AWasdasdS/asdas\",\"SOAP\"\n" +
"";
var output = importKeePassXOrgCsv.parser(input);
const output = importKeePassXOrgCsv.parser(input);
var expected_output = {
const expected_output = {
"datastore": {
"id": generic_uuid,
"name": output.datastore.name,

View File

@ -35,7 +35,7 @@ function get_folder_name(line) {
*
* @returns {string} Returns the appropriate type (note or website_password)
*/
var get_type = function (line) {
function getType(line) {
if (line[INDEX_URL] === "http://sn") {
// its a license, so lets return a note as we don't have this object class yet
return "note";
@ -47,7 +47,7 @@ var get_type = function (line) {
// Should have an url, so should be a password
return "website_password";
}
};
}
/**
* Takes a line that should represent a note and transforms it into a proper secret object
@ -56,8 +56,8 @@ var get_type = function (line) {
*
* @returns {*} The note secret object
*/
var transfor_into_note = function (line) {
var note_notes = "";
function transformIntoNote(line) {
let note_notes = "";
if (line[INDEX_USERNAME]) {
note_notes = note_notes + line[INDEX_USERNAME] + "\n";
}
@ -79,7 +79,7 @@ var transfor_into_note = function (line) {
note_title: line[INDEX_NAME],
note_notes: note_notes,
};
};
}
/**
* Takes a line that should represent a website passwords and transforms it into a proper secret object
@ -88,8 +88,8 @@ var transfor_into_note = function (line) {
*
* @returns {*} The website_password secret object
*/
var transfor_into_website_password = function (line) {
var parsed_url = helperService.parseUrl(line[INDEX_URL]);
function transformIntoWebsitePassword(line) {
const parsed_url = helperService.parseUrl(line[INDEX_URL]);
return {
id: cryptoLibrary.generateUuid(),
@ -103,7 +103,7 @@ var transfor_into_website_password = function (line) {
website_password_url: line[INDEX_URL],
website_password_title: line[INDEX_NAME],
};
};
}
/**
* Takes a line, checks its type and transforms it into a proper secret object
@ -112,14 +112,14 @@ var transfor_into_website_password = function (line) {
*
* @returns {*} The secrets object
*/
var transform_to_secret = function (line) {
var type = get_type(line);
function transformToSecret(line) {
const type = getType(line);
if (type === "note") {
return transfor_into_note(line);
return transformIntoNote(line);
} else {
return transfor_into_website_password(line);
return transformIntoWebsitePassword(line);
}
};
}
/**
* Fills the datastore with folders their content and together with the secrets object
@ -128,10 +128,10 @@ var transform_to_secret = function (line) {
* @param {[]} secrets The array containing all the found secrets
* @param {[]} csv The array containing all the found secrets
*/
function gather_secrets(datastore, secrets, csv) {
var line;
var folder_name;
var folder_index = {};
function gatherSecrets(datastore, secrets, csv) {
let line;
let folder_name;
const folder_index = {};
for (let i = 0; i < csv.length; i++) {
line = csv[i];
@ -140,7 +140,7 @@ function gather_secrets(datastore, secrets, csv) {
}
folder_name = get_folder_name(line);
var secret = transform_to_secret(line);
const secret = transformToSecret(line);
if (secret === null) {
//empty line
continue;
@ -168,8 +168,8 @@ function gather_secrets(datastore, secrets, csv) {
* @param {string} data The raw data to parse
* @returns {Array} The array of arrays representing the CSV
*/
function parse_csv(data) {
var csv = Papa.parse(data);
function parseCsv(data) {
const csv = Papa.parse(data);
if (csv["errors"].length > 0) {
throw new Error(csv["errors"][0]["message"]);
@ -193,23 +193,24 @@ function parse_csv(data) {
* @returns {{datastore, secrets: Array} | null}
*/
function parser(data) {
var d = new Date();
var n = d.toISOString();
const d = new Date();
const n = d.toISOString();
var secrets = [];
var datastore = {
const secrets = [];
const datastore = {
id: cryptoLibrary.generateUuid(),
name: "Import " + n,
folders: [],
};
let csv;
try {
var csv = parse_csv(data);
csv = parseCsv(data);
} catch (err) {
return null;
}
gather_secrets(datastore, secrets, csv);
gatherSecrets(datastore, secrets, csv);
return {
datastore: datastore,
@ -217,8 +218,8 @@ function parser(data) {
};
}
const service = {
const importLastpassComCsvService = {
parser,
};
export default service;
export default importLastpassComCsvService;

View File

@ -10,8 +10,8 @@ import cryptoLibrary from "./crypto-library";
* @param {[]} secrets The array containing all the found secrets
*/
function gather_secrets(folder, secrets) {
var i;
var subitem;
let i;
let subitem;
folder["id"] = cryptoLibrary.generateUuid();
@ -46,15 +46,16 @@ function gather_secrets(folder, secrets) {
* @returns {{datastore, secrets: Array} | null}
*/
function parser(data) {
let datastore;
try {
var datastore = JSON.parse(data);
datastore = JSON.parse(data);
} catch (err) {
return null;
}
var secrets = [];
const secrets = [];
var d = new Date();
var n = d.toISOString();
const d = new Date();
const n = d.toISOString();
datastore["name"] = "Import " + n;
gather_secrets(datastore, secrets);
@ -65,8 +66,8 @@ function parser(data) {
};
}
const service = {
const importPsonoPwJsonService = {
parser,
};
export default service;
export default importPsonoPwJsonService;

View File

@ -15,11 +15,11 @@ describe('Service: importPsonoPwJson test suite', function () {
cryptoLibrary.generateUuid = jest.fn();
cryptoLibrary.generateUuid.mockImplementation(() => generic_uuid);
var input = '{"folders":[{"name":"A Folder"},{"name":"Company Passwords","folders":[{"name":"bla","items":[{"type":"website_password","urlfilter":"facebook.com","name":"Facebook","website_password_url_filter":"facebook.com","website_password_auto_submit":true,"website_password_password":"mypassword","website_password_username":"myusername","website_password_url":"https://de-de.facebook.com/","website_password_title":"Facebook"}]}]}],"items":[{"type":"website_password","urlfilter":"amazon.de","name":"Amazon.de","website_password_url_filter":"amazon.de","website_password_password":"mypw","website_password_username":"myuser","website_password_url":"https://www.amazon.de","website_password_title":"Amazon.de"},{"type":"note","name":"My secret note","note_notes":"Some nice secrets go in here!","note_title":"My secret note"}]}';
const input = '{"folders":[{"name":"A Folder"},{"name":"Company Passwords","folders":[{"name":"bla","items":[{"type":"website_password","urlfilter":"facebook.com","name":"Facebook","website_password_url_filter":"facebook.com","website_password_auto_submit":true,"website_password_password":"mypassword","website_password_username":"myusername","website_password_url":"https://de-de.facebook.com/","website_password_title":"Facebook"}]}]}],"items":[{"type":"website_password","urlfilter":"amazon.de","name":"Amazon.de","website_password_url_filter":"amazon.de","website_password_password":"mypw","website_password_username":"myuser","website_password_url":"https://www.amazon.de","website_password_title":"Amazon.de"},{"type":"note","name":"My secret note","note_notes":"Some nice secrets go in here!","note_title":"My secret note"}]}';
var output = importPsonoPwJson.parser(input);
const output = importPsonoPwJson.parser(input);
var expected_output = {
const expected_output = {
"datastore": {
"folders": [{
"name": "A Folder",

View File

@ -17,7 +17,7 @@ const INDEX_INFORMATIONS = 4;
*
* @returns {*} The secrets object
*/
var transform_to_secret = function (line) {
function transformToSecret(line) {
return {
id: cryptoLibrary.generateUuid(),
type: "website_password",
@ -30,7 +30,7 @@ var transform_to_secret = function (line) {
website_password_url: "",
website_password_title: line[INDEX_DESCRIPTION],
};
};
}
/**
* Creates the folder if it doesn't exists and returns it.
@ -40,9 +40,9 @@ var transform_to_secret = function (line) {
*
* @returns {object} Returns the folder
*/
function get_folder(line, datastore) {
var next_folder_name;
var next_folder;
function getFolder(line, datastore) {
let next_folder_name;
let next_folder;
next_folder_name = line[INDEX_ORGANISATION_UNIT];
@ -73,9 +73,9 @@ function get_folder(line, datastore) {
* @param {[]} secrets The array containing all the found secrets
* @param {[]} csv The array containing all the found secrets
*/
function gather_secrets(datastore, secrets, csv) {
var line;
var folder;
function gatherSecrets(datastore, secrets, csv) {
let line;
let folder;
for (let i = 0; i < csv.length; i++) {
line = csv[i];
@ -86,8 +86,8 @@ function gather_secrets(datastore, secrets, csv) {
continue;
}
folder = get_folder(line, datastore);
var secret = transform_to_secret(line);
folder = getFolder(line, datastore);
const secret = transformToSecret(line);
if (secret === null) {
//empty line
continue;
@ -103,8 +103,8 @@ function gather_secrets(datastore, secrets, csv) {
* @param {string} data The raw data to parse
* @returns {Array} The array of arrays representing the CSV
*/
function parse_csv(data) {
var csv = Papa.parse(data);
function parseCsv(data) {
const csv = Papa.parse(data);
if (csv["errors"].length > 0) {
throw new Error(csv["errors"][0]["message"]);
@ -128,24 +128,25 @@ function parse_csv(data) {
* @returns {{datastore, secrets: Array} | null}
*/
function parser(data) {
var d = new Date();
var n = d.toISOString();
const d = new Date();
const n = d.toISOString();
var secrets = [];
var datastore = {
const secrets = [];
const datastore = {
id: cryptoLibrary.generateUuid(),
name: "Import " + n,
items: [],
folders: [],
};
let csv;
try {
var csv = parse_csv(data);
csv = parseCsv(data);
} catch (err) {
return null;
}
gather_secrets(datastore, secrets, csv);
gatherSecrets(datastore, secrets, csv);
return {
datastore: datastore,
@ -153,8 +154,8 @@ function parser(data) {
};
}
const service = {
const importPwsafeOrgCsvService = {
parser,
};
export default service;
export default importPwsafeOrgCsvService;

View File

@ -14,7 +14,7 @@ const INDEX_PASSWORD = 5;
const INDEX_NOTES = 6;
const INDEX_TAGS = 7;
const INDEX_CUSTOM_FIELDS = 8;
var CUSTOM_FIELD_OFFSET = 9;
const CUSTOM_FIELD_OFFSET = 9;
/**
* Takes a line and returns the correct description
@ -23,13 +23,13 @@ var CUSTOM_FIELD_OFFSET = 9;
*
* @returns {*} The description
*/
var get_description = function (line) {
var description = line[INDEX_DESCRIPTION];
function getDescription(line) {
let description = line[INDEX_DESCRIPTION];
if (line[INDEX_TAGS]) {
description = description + " (" + line[INDEX_TAGS] + ")";
}
return description;
};
}
/**
* Takes a line and returns the correct notes
@ -40,15 +40,15 @@ var get_description = function (line) {
*
* @returns {*} The description
*/
var get_notes = function (line) {
var notes = line[INDEX_NOTES] + "\n";
function getNotes(line) {
let notes = line[INDEX_NOTES] + "\n";
if (line[INDEX_EMAIL]) {
notes = notes + "Email: " + line[INDEX_EMAIL] + "\n";
}
if (line[INDEX_CUSTOM_FIELDS]) {
var custom_fields = line[INDEX_CUSTOM_FIELDS].split(",");
const custom_fields = line[INDEX_CUSTOM_FIELDS].split(",");
for (let i = 0; i < custom_fields.length; i++) {
//checks if the corresponding value is not empty
if (!line[CUSTOM_FIELD_OFFSET + i]) {
@ -59,7 +59,7 @@ var get_notes = function (line) {
}
return notes;
};
}
/**
* Takes a line and transforms it into a website password entry
@ -68,11 +68,11 @@ var get_notes = function (line) {
*
* @returns {*} The secrets object
*/
var transform_to_website_password = function (line) {
var parsed_url = helperService.parseUrl(line[INDEX_URL]);
function transformToWebsitePassword(line) {
const parsed_url = helperService.parseUrl(line[INDEX_URL]);
var description = get_description(line);
var notes = get_notes(line);
const description = getDescription(line);
const notes = getNotes(line);
return {
id: cryptoLibrary.generateUuid(),
@ -86,7 +86,7 @@ var transform_to_website_password = function (line) {
website_password_url: line[INDEX_URL],
website_password_title: description,
};
};
}
/**
* Takes a line and transforms it into a application password entry
@ -95,9 +95,9 @@ var transform_to_website_password = function (line) {
*
* @returns {*} The secrets object
*/
var transform_to_application_password = function (line) {
var description = get_description(line);
var notes = get_notes(line);
function transformToApplicationPassword(line) {
const description = getDescription(line);
const notes = getNotes(line);
return {
id: cryptoLibrary.generateUuid(),
@ -108,7 +108,7 @@ var transform_to_application_password = function (line) {
application_password_notes: notes,
application_password_title: description,
};
};
}
/**
* Takes a line and transforms it into a bookmark entry
@ -117,11 +117,11 @@ var transform_to_application_password = function (line) {
*
* @returns {*} The secrets object
*/
var transform_to_bookmark = function (line) {
var parsed_url = helperService.parseUrl(line[INDEX_URL]);
function transformToBookmark(line) {
const parsed_url = helperService.parseUrl(line[INDEX_URL]);
var description = get_description(line);
var notes = get_notes(line);
const description = getDescription(line);
const notes = getNotes(line);
return {
id: cryptoLibrary.generateUuid(),
@ -133,7 +133,7 @@ var transform_to_bookmark = function (line) {
bookmark_url: line[INDEX_URL],
bookmark_title: description,
};
};
}
/**
* Takes a line and transforms it into a note entry
@ -142,9 +142,9 @@ var transform_to_bookmark = function (line) {
*
* @returns {*} The secrets object
*/
var transform_to_note = function (line) {
var description = get_description(line);
var notes = get_notes(line);
function transformToNote(line) {
const description = getDescription(line);
const notes = getNotes(line);
return {
id: cryptoLibrary.generateUuid(),
@ -153,17 +153,17 @@ var transform_to_note = function (line) {
note_notes: notes,
note_title: description,
};
};
}
/**
* Analyzes an item and return its types
*
* @param {object} line The line to analyze
*/
function detect_type(line) {
var contains_url = line[INDEX_URL];
var contains_username = line[INDEX_USERNAME];
var contains_password = line[INDEX_PASSWORD];
function detectType(line) {
const contains_url = line[INDEX_URL];
const contains_username = line[INDEX_USERNAME];
const contains_password = line[INDEX_PASSWORD];
if (contains_url && (contains_username || contains_password)) {
return "website_password";
@ -186,9 +186,9 @@ function detect_type(line) {
*
* @returns {object} Returns the folder
*/
function get_folder(line, datastore) {
var next_folder_name;
var next_folder;
function getFolder(line, datastore) {
let next_folder_name;
let next_folder;
next_folder_name = line[INDEX_PROJECT_NAME];
@ -219,9 +219,9 @@ function get_folder(line, datastore) {
* @param {[]} secrets The array containing all the found secrets
* @param {[]} csv The array containing all the found secrets
*/
function gather_secrets(datastore, secrets, csv) {
var line;
var folder;
function gatherSecrets(datastore, secrets, csv) {
let line;
let folder;
for (let i = 0; i < csv.length; i++) {
line = csv[i];
@ -232,19 +232,19 @@ function gather_secrets(datastore, secrets, csv) {
continue;
}
folder = get_folder(line, datastore);
folder = getFolder(line, datastore);
var detected_type = detect_type(line);
const detected_type = detectType(line);
var secret = null;
let secret = null;
if (detected_type === "website_password") {
secret = transform_to_website_password(line);
secret = transformToWebsitePassword(line);
} else if (detected_type === "application_password") {
secret = transform_to_application_password(line);
secret = transformToApplicationPassword(line);
} else if (detected_type === "bookmark") {
secret = transform_to_bookmark(line);
secret = transformToBookmark(line);
} else {
secret = transform_to_note(line);
secret = transformToNote(line);
}
if (secret === null) {
@ -262,8 +262,8 @@ function gather_secrets(datastore, secrets, csv) {
* @param {string} data The raw data to parse
* @returns {Array} The array of arrays representing the CSV
*/
function parse_csv(data) {
var csv = Papa.parse(data);
function parseCsv(data) {
const csv = Papa.parse(data);
if (csv["errors"].length > 0) {
throw new Error(csv["errors"][0]["message"]);
@ -287,32 +287,33 @@ function parse_csv(data) {
* @returns {{datastore, secrets: Array} | null}
*/
function parser(data) {
var d = new Date();
var n = d.toISOString();
const d = new Date();
const n = d.toISOString();
var secrets = [];
var datastore = {
const secrets = [];
const datastore = {
id: cryptoLibrary.generateUuid(),
name: "Import " + n,
items: [],
folders: [],
};
let csv;
try {
var csv = parse_csv(data);
csv = parseCsv(data);
} catch (err) {
return null;
}
gather_secrets(datastore, secrets, csv);
gatherSecrets(datastore, secrets, csv);
return {
datastore: datastore,
secrets: secrets,
};
}
const service = {
const importTeampassNetCsvService = {
parser,
};
export default service;
export default importTeampassNetCsvService;

View File

@ -13,17 +13,17 @@ describe('Service: importTeampassNetCsv test suite', function () {
const generic_uuid = '1fce01f4-6411-47a9-885c-a80bf4c654aa'
cryptoLibrary.generateUuid = jest.fn();
cryptoLibrary.generateUuid.mockImplementation(() => generic_uuid);
var input = "\"Project name\",\"Name\",\"Access information\",\"Username\",\"E-mail\",\"Password\",\"Notes\",\"Tags\",\"Custom fields\",\"Custom 1\",\"Custom 2\",\"Custom 4\",\"Custom 5\",\"Custom 6\",\"Custom 7\",\"Custom 8\",\"Custom 9\",\"Custom 10\",\"Expiry date (mm-dd-yyyy)\"\n" +
const input = "\"Project name\",\"Name\",\"Access information\",\"Username\",\"E-mail\",\"Password\",\"Notes\",\"Tags\",\"Custom fields\",\"Custom 1\",\"Custom 2\",\"Custom 4\",\"Custom 5\",\"Custom 6\",\"Custom 7\",\"Custom 8\",\"Custom 9\",\"Custom 10\",\"Expiry date (mm-dd-yyyy)\"\n" +
"\"Software\",\"Database user\",\"http://192.168.0.34/phpma\",\"john\",\"john@company.com\",\"doe\",\"\",\"db,mydb\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"10-01-2014\"\n" +
"\"Software\",\"Server admin\",\"192.168.0.34\",\"admin\",\"\",\"test\",\"sample notes\",\"tag1,tag2\",\"custom1,custom2\",\"custom value 1\",\"custom value 2\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"\n" +
"\"Hardware\",\"Router\",\"192.168.0.1\",\"admin\",\"\",\"easypwd\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"10-30-2015\"\n" +
"\"Hardware\",\"Server room code\",\"\",\"\",\"\",\"1234\",\"\",\"pin\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\""
var output = importTeampassNetCsv.parser(input);
var expected_output = {
const output = importTeampassNetCsv.parser(input);
const expected_output = {
"datastore": {
"id": generic_uuid,
"name": output.datastore.name,

View File

@ -0,0 +1,35 @@
/**
* linkShare collects all functions to edit / update / create link shares and to work with them.
*/
import helperService from "./helper";
import store from "./store";
const _entryTypes = [
{ value: "website_password", title: "WEBSITE_PASSWORD", edit_title: "EDIT_WEBSITE_PASSWORD" },
{ value: "application_password", title: "APPLICATION_PASSWORD", edit_title: "EDIT_APPLICATION_PASSWORD" },
{ value: "totp", title: "TOTP_AUTHENTICATOR", edit_title: "EDIT_TOTP_AUTHENTICATOR" },
{ value: "note", title: "NOTE", edit_title: "EDIT_NOTE" },
{ value: "environment_variables", title: "ENVIRONMENT_VARIABLES", edit_title: "EDIT_ENVIRONMENT_VARIABLES" },
{ value: "mail_gpg_own_key", title: "GPG_KEY", edit_title: "EDIT_GPG_KEY" },
{ value: "bookmark", title: "BOOKMARK", edit_title: "EDIT_BOOKMARK" },
];
/**
* Returns all entry types
*
* @returns {array} List of all entry types
*/
function getEntryTypes() {
const entryTypes = helperService.duplicateObject(_entryTypes);
if (store.getState().server.files) {
entryTypes.push({ value: "file", title: "FILE", edit_title: "EDIT_FILE" });
}
return entryTypes;
}
const itemBlueprintService = {
getEntryTypes: getEntryTypes,
};
export default itemBlueprintService;

View File

@ -62,9 +62,9 @@ function readLinkShares() {
*/
function readSecretWithLinkShare(encryptedSecret, shareLinkData) {
// normal secret
var secret_data = JSON.parse(cryptoLibrary.decrypt_data(encryptedSecret.secret_data, encryptedSecret.secret_data_nonce, shareLinkData.secret_key));
const secret_data = JSON.parse(cryptoLibrary.decrypt_data(encryptedSecret.secret_data, encryptedSecret.secret_data_nonce, shareLinkData.secret_key));
var modalInstance = $uibModal.open({
const modalInstance = $uibModal.open({
templateUrl: "view/modal/show-entry.html",
controller: "ModalEditEntryCtrl",
backdrop: "static",
@ -114,7 +114,7 @@ function readFileWithLinkShare(encryptedFileMeta, shareLinkData) {
*/
function linkShareAccess(linkShareId, linkShareSecret, passphrase) {
const onSuccess = function (result) {
var share_link_data = JSON.parse(cryptoLibrary.decrypt_data(result.data.node, result.data.node_nonce, linkShareSecret));
const share_link_data = JSON.parse(cryptoLibrary.decrypt_data(result.data.node, result.data.node_nonce, linkShareSecret));
if (share_link_data.type === "file") {
return readFileWithLinkShare(result.data, share_link_data);

View File

@ -64,7 +64,7 @@ function push(notificationType, notificationContent) {
}
}
const service = {
const notificationService = {
infoSend,
errorSend,
reset,
@ -72,4 +72,4 @@ const service = {
push,
};
export default service;
export default notificationService;

View File

@ -225,7 +225,7 @@ function onSetEncryptionKey(fnc) {
onSetEncryptionKeyRegistrations.push(fnc);
}
const service = {
const offlineCacheService = {
isActive: isActive,
get: get,
set: set,
@ -240,4 +240,4 @@ const service = {
save: save,
onSetEncryptionKey: onSetEncryptionKey,
};
export default service;
export default offlineCacheService;

View File

@ -38,8 +38,8 @@ function recoveryGenerateInformation() {
return apiClient.writeRecoverycode(token, sessionSecretKey, recoveryAuthkey, recovery_data.text, recovery_data.nonce, recoverySauce).then(onSuccess);
}
const service = {
const passwordRecoveryCodeService = {
recoveryGenerateInformation,
};
export default service;
export default passwordRecoveryCodeService;

View File

@ -133,7 +133,7 @@ function onSecretDeleted(linkId) {
return deleteSecretLink(linkId);
}
const service = {
const secretLinkService = {
moveSecretLinks: moveSecretLinks,
resetSecretLinkTimeout: resetSecretLinkTimeout,
moveSecretLink: moveSecretLink,
@ -141,4 +141,4 @@ const service = {
onSecretMoved: onSecretMoved,
onSecretDeleted: onSecretDeleted,
};
export default service;
export default secretLinkService;

View File

@ -172,7 +172,7 @@ function redirectSecret(type, secretId) {
}
/**
* Handles item clicks and triggers behaviour
* Handles item clicks and triggers redirects for website passwords and bookmarks
*
* @param {object} item The item one has clicked on
*/

View File

@ -305,7 +305,7 @@ function register(key, func) {
registrations[key] = func;
}
const service = {
const settingsService = {
getSetting: getSetting,
getSettings: getSettings,
setSettings: setSettings,
@ -313,4 +313,4 @@ const service = {
register: register,
};
export default service;
export default settingsService;

View File

@ -3,18 +3,21 @@
*/
import apiClient from "./api-client";
import store from "./store";
/**
* Create a link between a share and a datastore or another (parent-)share
*
* @param {uuid} link_id the link id
* @param {uuid} share_id the share ID
* @param {uuid|undefined} [parent_share_id=null] (optional) parent share ID, necessary if no datastore_id is provided
* @param {uuid|undefined} [parent_datastore_id=null] (optional) datastore ID, necessary if no parent_share_id is provided
* @param {uuid} linkId the link id
* @param {uuid} shareId the share ID
* @param {uuid|undefined} [parentShareId=null] (optional) parent share ID, necessary if no datastore_id is provided
* @param {uuid|undefined} [parentDatastoreId=null] (optional) datastore ID, necessary if no parentShareId is provided
*
* @returns {promise} Returns a promise withe the new share link id
*/
function createShareLink(link_id, share_id, parent_share_id, parent_datastore_id) {
function createShareLink(linkId, shareId, parentShareId, parentDatastoreId) {
const token = store.getState().user.token;
const sessionSecretKey = store.getState().user.sessionSecretKey;
const onError = function (result) {
// pass
};
@ -23,21 +26,21 @@ function createShareLink(link_id, share_id, parent_share_id, parent_datastore_id
return result;
};
return apiClient
.createShareLink(managerBase.get_token(), managerBase.get_session_secret_key(), link_id, share_id, parent_share_id, parent_datastore_id)
.then(onSuccess, onError);
return apiClient.createShareLink(token, sessionSecretKey, linkId, shareId, parentShareId, parentDatastoreId).then(onSuccess, onError);
}
/**
* Moves a link between a share and a datastore or another (parent-)share
*
* @param {uuid} link_id The link id
* @param {uuid|undefined} [new_parent_share_id] (optional) new parent share ID, necessary if no new_datastore_id is provided
* @param {uuid|undefined} [new_parent_datastore_id] (optional) new datastore ID, necessary if no new_parent_share_id is provided
* @param {uuid} linkId The link id
* @param {uuid|undefined} [newParentShareId] (optional) new parent share ID, necessary if no new_datastore_id is provided
* @param {uuid|undefined} [newParentDatastoreId] (optional) new datastore ID, necessary if no newParentShareId is provided
*
* @returns {promise} Returns a promise with the status of the move
*/
function moveShareLink(link_id, new_parent_share_id, new_parent_datastore_id) {
function moveShareLink(linkId, newParentShareId, newParentDatastoreId) {
const token = store.getState().user.token;
const sessionSecretKey = store.getState().user.sessionSecretKey;
const onError = function (result) {
// pass
};
@ -46,18 +49,18 @@ function moveShareLink(link_id, new_parent_share_id, new_parent_datastore_id) {
return result;
};
return apiClient
.moveShareLink(managerBase.get_token(), managerBase.get_session_secret_key(), link_id, new_parent_share_id, new_parent_datastore_id)
.then(onSuccess, onError);
return apiClient.moveShareLink(token, sessionSecretKey, linkId, newParentShareId, newParentDatastoreId).then(onSuccess, onError);
}
/**
* Delete a share link
*
* @param {uuid} link_id The link id one wants to delete
* @param {uuid} linkId The link id one wants to delete
* @returns {promise} Returns a promise with the status of the delete operation
*/
function deleteShareLink(link_id) {
function deleteShareLink(linkId) {
const token = store.getState().user.token;
const sessionSecretKey = store.getState().user.sessionSecretKey;
const onError = function (result) {
// pass
};
@ -66,19 +69,19 @@ function deleteShareLink(link_id) {
return result;
};
return apiClient.deleteShareLink(managerBase.get_token(), managerBase.get_session_secret_key(), link_id).then(onSuccess, onError);
return apiClient.deleteShareLink(token, sessionSecretKey, linkId).then(onSuccess, onError);
}
/**
* triggered once a share moved. handles the update of links
*
* @param {uuid} link_id The link id that has moved
* @param {uuid} linkId The link id that has moved
* @param {TreeObject} parent The parent (either a share or a datastore)
*
* @returns {promise} Returns a promise with the status of the move
*/
function onShareMoved(link_id, parent) {
var new_parent_share_id = undefined,
function onShareMoved(linkId, parent) {
let new_parent_share_id = undefined,
new_parent_datastore_id = undefined;
if (parent.hasOwnProperty("share_id")) {
@ -92,7 +95,7 @@ function onShareMoved(link_id, parent) {
});
}
return moveShareLink(link_id, new_parent_share_id, new_parent_datastore_id);
return moveShareLink(linkId, new_parent_share_id, new_parent_datastore_id);
}
/**
@ -104,11 +107,11 @@ function onShareDeleted(link_id) {
return deleteShareLink(link_id);
}
const service = {
const shareLinkService = {
createShareLink: createShareLink,
moveShareLink: moveShareLink,
deleteShareLink: deleteShareLink,
onShareMoved: onShareMoved,
onShareDeleted: onShareDeleted,
};
export default service;
export default shareLinkService;

View File

@ -322,7 +322,7 @@ function deleteShareRight(userShareRightId, groupShareRightId) {
* @returns {object} The decrypted share
*/
function decryptShare(encryptedShare, secretKey) {
const share = {};
let share = {};
if (typeof encryptedShare.share_data !== "undefined") {
share = JSON.parse(cryptoLibrary.decryptData(encryptedShare.share_data, encryptedShare.share_data_nonce, secretKey));
@ -469,7 +469,7 @@ function register(key, func) {
// itemBlueprint.register('create_share_right', create_share_right);
// itemBlueprint.register('get_closest_parent_share', get_closest_parent_share);
const service = {
const shareService = {
readShare: readShare,
readShares: readShares,
writeShare: writeShare,
@ -485,4 +485,4 @@ const service = {
getClosestParentShare: getClosestParentShare,
register: register,
};
export default service;
export default shareService;

View File

@ -270,7 +270,7 @@ function emit(key, payload) {
}
}
const service = {
const storageService = {
insert: insert,
update: update,
upsert: upsert,
@ -285,4 +285,4 @@ const service = {
register: register,
emit: emit,
};
export default service;
export default storageService;

View File

@ -615,8 +615,8 @@ function updateUser(email, authkey, authkeyOld, privateKey, privateKeyNonce, sec
function saveNewPassword(newPassword, newPasswordRepeat, oldPassword) {
return host.info().then(
function (info) {
var authkeyOld, newAuthkey, userPrivateKey, userSecretKey, userSauce, privKeyEnc, secretKeyEnc, onSuccess, onError;
var test_error = helperService.isValidPassword(
let authkeyOld, newAuthkey, userPrivateKey, userSecretKey, userSauce, privKeyEnc, secretKeyEnc, onSuccess, onError;
const test_error = helperService.isValidPassword(
newPassword,
newPasswordRepeat,
info.data["decoded_info"]["compliance_min_master_password_length"],
@ -695,7 +695,7 @@ function recoveryEnable(username, recoveryCode, server) {
action.setServerUrl(server);
const onSuccess = function (data) {
var recovery_data = JSON.parse(
const recovery_data = JSON.parse(
cryptoLibrary.decryptSecret(data.data.recovery_data, data.data.recovery_data_nonce, recoveryCode, data.data.recovery_sauce)
);
@ -747,7 +747,7 @@ function setPassword(username, recoveryCode, password, userPrivateKey, userSecre
return data;
};
var recovery_authkey = cryptoLibrary.generateAuthkey(username, recoveryCode);
const recovery_authkey = cryptoLibrary.generateAuthkey(username, recoveryCode);
return apiClientService.setPassword(username, recovery_authkey, updateRequestEnc.text, updateRequestEnc.nonce).then(onSuccess, onError);
}
@ -796,7 +796,7 @@ function armEmergencyCode(username, emergencyCode, server, serverInfo, verifyKey
const update_request_enc = cryptoLibrary.encryptDataPublicKey(loginInfo, data.data.verifier_public_key, emergency_data.user_private_key);
const onSuccess = function (data) {
var loginInfo = JSON.parse(
const loginInfo = JSON.parse(
cryptoLibrary.decryptDataPublicKey(data.data.login_info, data.data.login_info_nonce, serverInfo["public_key"], sessionKey.private_key)
);
@ -856,7 +856,7 @@ function deleteSession(sessionId) {
return apiClientService.logout(token, sessionSecretKey, sessionId).then(onSuccess, onError);
}
const service = {
const userService = {
initiateLogin,
samlLogin,
initiateSamlLogin,
@ -885,4 +885,4 @@ const service = {
deleteSession,
};
export default service;
export default userService;

View File

@ -17,15 +17,15 @@ import datastoreUserService from "./datastore-user";
*
* @param {TreeObject} parent The parent of the new folder
* @param {Array} path The path to the parent of the new folder
* @param {TreeObject} data_structure the data structure
* @param {TreeObject} dataStructure the data structure
* @param {Object} manager manager responsible for
* @param {String} name The name of the new folder
*/
function openNewFolder(parent, path, data_structure, manager, name) {
function openNewFolder(parent, path, dataStructure, manager, name) {
let onSuccess, onError;
if (typeof parent === "undefined") {
parent = data_structure;
parent = dataStructure;
}
if (typeof parent.folders === "undefined") {
@ -41,7 +41,7 @@ function openNewFolder(parent, path, data_structure, manager, name) {
parent["expanded"] = true;
const closest_share_info = shareService.getClosestParentShare(path.slice(), data_structure, data_structure, 0);
const closest_share_info = shareService.getClosestParentShare(path.slice(), dataStructure, dataStructure, 0);
const closest_share = closest_share_info["closest_share"];
@ -67,7 +67,7 @@ function openNewFolder(parent, path, data_structure, manager, name) {
};
}
datastorePasswordService.updatePathsRecursive(data_structure, []);
datastorePasswordService.updatePathsRecursive(dataStructure, []);
if (closest_share.hasOwnProperty("share_id")) {
// refresh share content before updating the share
@ -86,7 +86,7 @@ function openNewFolder(parent, path, data_structure, manager, name) {
parent.folders.push(datastore_object);
shareService.writeShare(closest_share["share_id"], content.data, closest_share["share_secret_key"]);
manager.handleDatastoreContentChanged(data_structure);
manager.handleDatastoreContentChanged(dataStructure);
};
onError = function (e) {
@ -114,7 +114,7 @@ function openNewFolder(parent, path, data_structure, manager, name) {
parent.folders.push(datastore_object);
manager.saveDatastoreContent(datastore, [path]);
manager.handleDatastoreContentChanged(data_structure);
manager.handleDatastoreContentChanged(dataStructure);
};
return manager.getDatastoreWithId(closest_share["datastore_id"]).then(onSuccess, onError);
@ -126,82 +126,56 @@ function openNewFolder(parent, path, data_structure, manager, name) {
*
* @param {object} node The node you want to edit
* @param {Array} path The path to the node
* @param {TreeObject} data_structure the data structure
* @param {TreeObject} dataStructure the data structure
* @param {Object} manager manager responsible for
* @param {string} size The size of the modal
*/
function openEditFolder(node, path, data_structure, manager, size) {
const modalInstance = $uibModal.open({
templateUrl: "view/modal/edit-folder.html",
controller: "ModalEditFolderCtrl",
backdrop: "static",
size: size,
resolve: {
node: function () {
return node;
},
path: function () {
return path;
},
},
});
function openEditFolder(node, path, dataStructure, manager) {
let onSuccess, onError;
modalInstance.result.then(
function (name) {
let onSuccess, onError;
const closest_share_info = shareService.getClosestParentShare(path.slice(), dataStructure, dataStructure, 0);
const closest_share = closest_share_info["closest_share"];
// change visual representation
node.name = name;
const closest_share_info = shareService.getClosestParentShare(path.slice(), data_structure, data_structure, 0);
const closest_share = closest_share_info["closest_share"];
if (closest_share.hasOwnProperty("share_id")) {
// refresh share content before updating the share
onSuccess = function (content) {
let folder;
if (closest_share_info["relative_path"].length === 0) {
folder = content.data;
} else {
const search = datastorePasswordService.findInDatastore(closest_share_info["relative_path"], content.data);
folder = search[0][search[1]];
}
folder.name = name;
shareService.writeShare(closest_share["share_id"], content.data, closest_share["share_secret_key"]);
manager.handleDatastoreContentChanged(data_structure);
};
onError = function (e) {
// pass
};
shareService.readShare(closest_share["share_id"], closest_share["share_secret_key"]).then(onSuccess, onError);
if (closest_share.hasOwnProperty("share_id")) {
// refresh share content before updating the share
onSuccess = function (content) {
let folder;
if (closest_share_info["relative_path"].length === 0) {
folder = content.data;
} else {
// refresh datastore content before updating it
onError = function (result) {
// pass
};
onSuccess = function (datastore) {
let folder;
if (closest_share_info["relative_path"].length === 0) {
folder = datastore;
} else {
const search = datastorePasswordService.findInDatastore(closest_share_info["relative_path"], datastore);
folder = search[0][search[1]];
}
folder.name = name;
manager.saveDatastoreContent(datastore, [path]);
manager.handleDatastoreContentChanged(data_structure);
};
return datastoreService.getDatastoreWithId(closest_share["datastore_id"]).then(onSuccess, onError);
const search = datastorePasswordService.findInDatastore(closest_share_info["relative_path"], content.data);
folder = search[0][search[1]];
}
},
function () {
// cancel triggered
}
);
folder.name = node.name;
shareService.writeShare(closest_share["share_id"], content.data, closest_share["share_secret_key"]);
manager.handleDatastoreContentChanged(dataStructure);
};
onError = function (e) {
// pass
};
shareService.readShare(closest_share["share_id"], closest_share["share_secret_key"]).then(onSuccess, onError);
} else {
// refresh datastore content before updating it
onError = function (result) {
// pass
};
onSuccess = function (datastore) {
let folder;
if (closest_share_info["relative_path"].length === 0) {
folder = datastore;
} else {
const search = datastorePasswordService.findInDatastore(closest_share_info["relative_path"], datastore);
folder = search[0][search[1]];
}
folder.name = node.name;
manager.saveDatastoreContent(datastore, [path]);
manager.handleDatastoreContentChanged(dataStructure);
};
return datastoreService.getDatastoreWithId(closest_share["datastore_id"]).then(onSuccess, onError);
}
}
/**
@ -274,11 +248,11 @@ function openNewItem(datastore, parent, path, size, manager) {
// pass
};
var closest_share_info = shareService.getClosestParentShare(path.slice(), datastore, datastore, 0);
const closest_share_info = shareService.getClosestParentShare(path.slice(), datastore, datastore, 0);
var closest_share = closest_share_info["closest_share"];
const closest_share = closest_share_info["closest_share"];
var parent_share_id, parent_datastore_id;
let parent_share_id, parent_datastore_id;
if (closest_share.hasOwnProperty("share_id")) {
parent_share_id = closest_share["share_id"];
@ -304,7 +278,7 @@ function openNewItem(datastore, parent, path, size, manager) {
};
}
var save_datastore = function () {
const save_datastore = function () {
let onSuccess, onError;
// update visual representation
@ -318,11 +292,11 @@ function openNewItem(datastore, parent, path, size, manager) {
if (closest_share.hasOwnProperty("share_id")) {
// refresh share content before updating the share
onSuccess = function (content) {
var parent;
let parent;
if (closest_share_info["relative_path"].length === 0) {
parent = content.data;
} else {
var search = datastorePasswordService.findInDatastore(closest_share_info["relative_path"], content.data);
const search = datastorePasswordService.findInDatastore(closest_share_info["relative_path"], content.data);
parent = search[0][search[1]];
}
@ -346,11 +320,11 @@ function openNewItem(datastore, parent, path, size, manager) {
};
onSuccess = function (datastore) {
var parent;
let parent;
if (closest_share_info["relative_path"].length === 0) {
parent = datastore;
} else {
var search = datastorePasswordService.findInDatastore(closest_share_info["relative_path"], datastore);
const search = datastorePasswordService.findInDatastore(closest_share_info["relative_path"], datastore);
parent = search[0][search[1]];
}
@ -420,7 +394,7 @@ function openEditItem(datastore, node, path, size, manager) {
const onSuccess = function (data) {
function onSave(new_content) {
// update visual representation
var secret_object = {};
const secret_object = {};
for (let i = new_content.fields.length - 1; i >= 0; i--) {
if (!new_content.fields[i].hasOwnProperty("value")) {
continue;
@ -444,14 +418,14 @@ function openEditItem(datastore, node, path, size, manager) {
const onSuccess = function (e) {
let onSuccess, onError;
var closest_share_info = shareService.getClosestParentShare(path.slice(), datastore, datastore, 0);
const closest_share_info = shareService.getClosestParentShare(path.slice(), datastore, datastore, 0);
var closest_share = closest_share_info["closest_share"];
const closest_share = closest_share_info["closest_share"];
if (closest_share.hasOwnProperty("share_id")) {
// refresh share content before updating the share
onSuccess = function (content) {
var search = datastorePasswordService.findInDatastore(closest_share_info["relative_path"], content.data);
const search = datastorePasswordService.findInDatastore(closest_share_info["relative_path"], content.data);
node = search[0][search[1]];
for (let i = new_content.fields.length - 1; i >= 0; i--) {
@ -730,17 +704,17 @@ function check_if_parent_changed(element, target) {
* Move an item (or folder) from one position to anther
*
* @param {TreeObject} datastore The datastore
* @param {Array} item_path the current path of the item
* @param {Array} target_path the path where we want to put the item
* @param {Array} itemPath the current path of the item
* @param {Array} targetPath the path where we want to put the item
* @param {string} type type of the item ('items' or 'folders')
* @param {string} datastore_type The type of the datastore (e.g. 'password' or 'user')
* @param {string} datastoreType The type of the datastore (e.g. 'password' or 'user')
*/
function moveItem(datastore, item_path, target_path, type, datastore_type) {
function moveItem(datastore, itemPath, targetPath, type, datastoreType) {
let i;
let closest_parent;
let closest_share_info;
const orig_item_path = item_path.slice();
const orig_item_path = itemPath.slice();
orig_item_path.pop();
let orig_target_path;
@ -750,23 +724,23 @@ function moveItem(datastore, item_path, target_path, type, datastore_type) {
}
const onSuccess = function (datastore) {
if (target_path === null || typeof target_path === "undefined") {
if (targetPath === null || typeof targetPath === "undefined") {
orig_target_path = [];
} else {
orig_target_path = target_path.slice();
orig_target_path = targetPath.slice();
}
let target = datastore;
if (target_path !== null && typeof target_path !== "undefined") {
if (targetPath !== null && typeof targetPath !== "undefined") {
// find drop zone
const val1 = datastorePasswordService.findInDatastore(target_path, datastore);
const val1 = datastorePasswordService.findInDatastore(targetPath, datastore);
target = val1[0][val1[1]];
}
let element;
// find element
try {
const val2 = datastorePasswordService.findInDatastore(item_path, datastore);
const val2 = datastorePasswordService.findInDatastore(itemPath, datastore);
element = val2[0][val2[1]];
} catch (e) {
return;
@ -824,7 +798,7 @@ function moveItem(datastore, item_path, target_path, type, datastore_type) {
datastorePasswordService.updatePathsRecursive(datastore, []);
// and save everything (before we update the links and might lose some necessary rights)
if (datastore_type === "password") {
if (datastoreType === "password") {
datastorePasswordService.handleDatastoreContentChanged(datastore);
datastorePasswordService.saveDatastoreContent(datastore, [orig_item_path, orig_target_path]);
} else {
@ -893,7 +867,7 @@ function moveItem(datastore, item_path, target_path, type, datastore_type) {
datastorePasswordService.updateParents(element, new_parent_share_id, new_parent_datastore_id);
};
if (datastore_type === "password") {
if (datastoreType === "password") {
return datastorePasswordService.getPasswordDatastore(datastore.datastore_id).then(onSuccess);
} else {
return datastoreService.getDatastoreWithId(datastore.datastore_id).then(onSuccess);
@ -912,13 +886,13 @@ function moveItem(datastore, item_path, target_path, type, datastore_type) {
* @param {TreeObject} datastore The datastore
* @param {object} item The item to delete
* @param {Array} path The path to the item
* @param {string} datastore_type The type of the datastore (e.g. 'password' or 'user')
* @param {string} datastoreType The type of the datastore (e.g. 'password' or 'user')
*/
function deleteItem(datastore, item, path, datastore_type) {
if (datastore_type === "user" || (item.hasOwnProperty("deleted") && item["deleted"])) {
delete_item_permanent(datastore, item, path, datastore_type);
function deleteItem(datastore, item, path, datastoreType) {
if (datastoreType === "user" || (item.hasOwnProperty("deleted") && item["deleted"])) {
deleteItemPermanent(datastore, item, path, datastoreType);
} else {
mark_item_as_deleted(datastore, item, path, datastore_type);
markItemAsDeleted(datastore, item, path, datastoreType);
}
}
@ -928,9 +902,9 @@ function deleteItem(datastore, item, path, datastore_type) {
* @param {TreeObject} datastore The datastore
* @param {object} item The item to delete
* @param {Array} path The path to the item
* @param {string} datastore_type The type of the datastore (e.g. 'password' or 'user')
* @param {string} datastoreType The type of the datastore (e.g. 'password' or 'user')
*/
function mark_item_as_deleted(datastore, item, path, datastore_type) {
function markItemAsDeleted(datastore, item, path, datastoreType) {
let onSuccess, onError;
const element_path_that_changed = path.slice();
element_path_that_changed.pop();
@ -944,7 +918,7 @@ function mark_item_as_deleted(datastore, item, path, datastore_type) {
const closest_share = closest_share_info["closest_share"];
if (datastore_type === "password") {
if (datastoreType === "password") {
if (closest_share.hasOwnProperty("share_id")) {
// refresh share content before updating the share
onSuccess = function (content) {
@ -979,7 +953,7 @@ function mark_item_as_deleted(datastore, item, path, datastore_type) {
datastoreService.getDatastoreWithId(closest_share["datastore_id"]).then(onSuccess, onError);
}
} else if (datastore_type === "user") {
} else if (datastoreType === "user") {
datastoreUserService.saveDatastoreContent(datastore, [element_path_that_changed]);
}
}
@ -990,9 +964,9 @@ function mark_item_as_deleted(datastore, item, path, datastore_type) {
* @param {TreeObject} datastore The datastore
* @param {object} item The item to delete
* @param {Array} path The path to the item
* @param {string} datastore_type The type of the datastore (e.g. 'password' or 'user')
* @param {string} datastoreType The type of the datastore (e.g. 'password' or 'user')
*/
function reverseMarkItemAsDeleted(datastore, item, path, datastore_type) {
function reverseMarkItemAsDeleted(datastore, item, path, datastoreType) {
let onSuccess, onError;
const element_path_that_changed = path.slice();
element_path_that_changed.pop();
@ -1006,7 +980,7 @@ function reverseMarkItemAsDeleted(datastore, item, path, datastore_type) {
const closest_share = closest_share_info["closest_share"];
if (datastore_type === "password") {
if (datastoreType === "password") {
if (closest_share.hasOwnProperty("share_id")) {
// refresh share content before updating the share
onSuccess = function (content) {
@ -1041,7 +1015,7 @@ function reverseMarkItemAsDeleted(datastore, item, path, datastore_type) {
datastoreService.getDatastoreWithId(closest_share["datastore_id"]).then(onSuccess, onError);
}
} else if (datastore_type === "user") {
} else if (datastoreType === "user") {
datastoreUserService.saveDatastoreContent(datastore, [element_path_that_changed]);
}
}
@ -1197,9 +1171,9 @@ function cloneItem(datastore, item, path) {
* @param {TreeObject} datastore The datastore
* @param {object} item The item to delete
* @param {Array} path The path to the item
* @param {string} datastore_type The type of the datastore (e.g. 'password' or 'user')
* @param {string} datastoreType The type of the datastore (e.g. 'password' or 'user')
*/
function delete_item_permanent(datastore, item, path, datastore_type) {
function deleteItemPermanent(datastore, item, path, datastoreType) {
let i;
let onSuccess, onError;
@ -1222,7 +1196,7 @@ function delete_item_permanent(datastore, item, path, datastore_type) {
const child_shares = [];
// and save everything (before we update the links and might lose some necessary rights)
if (datastore_type === "password") {
if (datastoreType === "password") {
if (closest_share.hasOwnProperty("share_id")) {
// refresh share content before updating the share
onSuccess = function (content) {
@ -1372,7 +1346,7 @@ function delete_item_permanent(datastore, item, path, datastore_type) {
return datastoreService.getDatastoreWithId(closest_share["datastore_id"]).then(onSuccess, onError);
}
} else if (datastore_type === "user") {
} else if (datastoreType === "user") {
return datastoreUserService.saveDatastoreContent(datastore, [element_path_that_changed]);
}
}
@ -1514,7 +1488,7 @@ function itemIcon(item) {
return iconClassMap[ext] || defaultIconClass;
}
const service = {
const widgetService = {
openNewFolder: openNewFolder,
openEditFolder: openEditFolder,
findInStructure: findInStructure,
@ -1527,4 +1501,4 @@ const service = {
itemIcon: itemIcon,
};
export default service;
export default widgetService;

View File

@ -86,11 +86,11 @@ function deleteYubikeyOtp(yubikeyOtpId) {
return apiClient.deleteYubikeyOtp(token, sessionSecretKey, yubikeyOtpId).then(onSuccess, onError);
}
const service = {
const yubikeyOtpService = {
createYubikeyOtp,
readYubikeyOtp,
activateYubikeyOtp,
deleteYubikeyOtp,
};
export default service;
export default yubikeyOtpService;

View File

@ -25,6 +25,10 @@ const useStyles = makeStyles((theme) => ({
textField: {
width: "100%",
},
code: {
fontFamily: "'Fira Code', monospace",
textAlign: "center",
},
}));
const EmergencyCodesDialog = (props) => {
@ -207,25 +211,25 @@ const EmergencyCodesDialog = (props) => {
<Grid item xs={12} sm={12} md={12}>
<strong>{t("URL")}</strong>
</Grid>
<Grid item xs={12} sm={12} md={12} className="text-center monospace">
<Grid item xs={12} sm={12} md={12} className={classes.code}>
<p>{newEmergencyCode.url}</p>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<strong>{t("USERNAME")}</strong>
</Grid>
<Grid item xs={12} sm={12} md={12} className="text-center monospace">
<Grid item xs={12} sm={12} md={12} className={classes.code}>
<p>{newEmergencyCode.username}</p>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<strong>{t("CODE")}</strong>
</Grid>
<Grid item xs={12} sm={12} md={12} className="text-center monospace">
<Grid item xs={12} sm={12} md={12} className={classes.code}>
<p>{newEmergencyCode.emergency_password}</p>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<strong>{t("OR")}</strong>
</Grid>
<Grid item xs={12} sm={12} md={12} className="text-center monospace">
<Grid item xs={12} sm={12} md={12} className={classes.code}>
<p>{newEmergencyCode.emergency_words}</p>
</Grid>
<Grid item xs={12} sm={12} md={12}>

View File

@ -1,10 +1,9 @@
import React, { useState } from "react";
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { bindActionCreators } from "redux";
import { compose } from "redux";
import { useTranslation } from "react-i18next";
import Box from "@material-ui/core/Box";
import { makeStyles } from "@material-ui/core/styles";
import Dialog from "@material-ui/core/Dialog";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";
@ -16,8 +15,16 @@ import actionCreators from "../../actions/action-creators";
import passwordRecoveryCode from "../../services/password-recovery-code";
import MuiAlert from "@material-ui/lab/Alert";
const useStyles = makeStyles((theme) => ({
code: {
fontFamily: "'Fira Code', monospace",
textAlign: "center",
},
}));
const PasswordRecoveryCodesDialog = (props) => {
const { open, onClose } = props;
const classes = useStyles();
const { t } = useTranslation();
const [newPasswordRecoveryCode, setNewPasswordRecoveryCode] = React.useState({});
@ -40,19 +47,19 @@ const PasswordRecoveryCodesDialog = (props) => {
<Grid item xs={12} sm={12} md={12}>
<strong>{t("USERNAME")}</strong>
</Grid>
<Grid item xs={12} sm={12} md={12} className="text-center monospace">
<Grid item xs={12} sm={12} md={12} className={classes.code}>
<p>{newPasswordRecoveryCode.username}</p>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<strong>{t("CODE")}</strong>
</Grid>
<Grid item xs={12} sm={12} md={12} className="text-center monospace">
<Grid item xs={12} sm={12} md={12} className={classes.code}>
<p>{newPasswordRecoveryCode.recovery_password}</p>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<strong>{t("OR")}</strong>
</Grid>
<Grid item xs={12} sm={12} md={12} className="text-center monospace">
<Grid item xs={12} sm={12} md={12} className={classes.code}>
<p>{newPasswordRecoveryCode.recovery_words}</p>
</Grid>
<Grid item xs={12} sm={12} md={12}>

View File

@ -1,8 +1,9 @@
import React from "react";
import React, { useState } from "react";
import { connect, useSelector } from "react-redux";
import { bindActionCreators } from "redux";
import { useTranslation } from "react-i18next";
import { differenceInSeconds } from "date-fns";
import { ClipLoader } from "react-spinners";
import { alpha, makeStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper";
import AppBar from "@material-ui/core/AppBar";
@ -14,22 +15,49 @@ import MenuOpenIcon from "@material-ui/icons/MenuOpen";
import Divider from "@material-ui/core/Divider";
import ClearIcon from "@material-ui/icons/Clear";
import MuiAlert from "@material-ui/lab/Alert";
import Menu from "@material-ui/core/Menu";
import MenuItem from "@material-ui/core/MenuItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import Typography from "@material-ui/core/Typography";
import CreateNewFolderIcon from "@material-ui/icons/CreateNewFolder";
import AddIcon from "@material-ui/icons/Add";
import actionCreators from "../../actions/action-creators";
import PasswordDatastore from "../../containers/password-datastore";
import Base from "../../containers/base";
import BaseTitle from "../../containers/base-title";
import BaseContent from "../../containers/base-content";
import widget from "../../services/widget";
import datastorePassword from "../../services/datastore-password";
import DialogNewFolder from "../../components/dialogs/new-folder";
import DialogNewEntry from "../../components/dialogs/new-entry";
import DatastoreTree from "../../components/datastore-tree";
import DialogNewShare from "../../components/dialogs/new-share";
import DialogEditFolder from "../../components/dialogs/edit-folder";
import DialogEditEntry from "../../components/dialogs/edit-entry";
import fileTransferService from "../../services/file-transfer";
import secretService from "../../services/secret";
import { useParams } from "react-router-dom";
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
padding: "15px",
},
loader: {
marginTop: "30px",
marginBottom: "30px",
margin: "auto",
},
toolbarRoot: {
display: "flex",
},
toolbarTitle: {
display: "none",
[theme.breakpoints.up("sm")]: {
display: "block",
},
},
search: {
borderRadius: theme.shape.borderRadius,
backgroundColor: alpha(theme.palette.common.white, 0.25),
@ -37,6 +65,8 @@ const useStyles = makeStyles((theme) => ({
backgroundColor: alpha(theme.palette.common.white, 0.45),
},
marginLeft: "auto",
position: "absolute",
right: 0,
[theme.breakpoints.up("sm")]: {
marginRight: theme.spacing(1),
},
@ -52,7 +82,7 @@ const useStyles = makeStyles((theme) => ({
transition: theme.transitions.create("width"),
width: "100%",
[theme.breakpoints.up("sm")]: {
width: "12ch",
width: "10ch",
"&:focus": {
width: "20ch",
},
@ -71,14 +101,52 @@ const useStyles = makeStyles((theme) => ({
topMessage: {
marginBottom: 20,
},
icon: {
fontSize: "18px",
},
listItemIcon: {
minWidth: theme.spacing(4),
},
}));
const DatastoreView = (props) => {
let { defaultSearch } = useParams();
const serverStatus = useSelector((state) => state.server.status);
const recurrenceInterval = useSelector((state) => state.server.complianceCentralSecurityReportsRecurrenceInterval);
const classes = useStyles();
const { t } = useTranslation();
const [search, setSearch] = React.useState("");
const [search, setSearch] = useState(defaultSearch || "");
const [anchorEl, setAnchorEl] = useState(null);
const [newFolderOpen, setNewFolderOpen] = useState(false);
const [newFolderData, setNewFolderData] = useState({});
const [newEntryOpen, setNewEntryOpen] = useState(false);
const [newEntryData, setNewEntryData] = useState({});
const [editFolderOpen, setEditFolderOpen] = useState(false);
const [editFolderData, setEditFolderData] = useState({});
const [editEntryOpen, setEditEntryOpen] = useState(false);
const [editEntryData, setEditEntryData] = useState({});
const [newShareOpen, setNewShareOpen] = useState(false);
const [newShareData, setNewShareData] = useState({});
const [datastore, setDatastore] = useState(null);
let isSubscribed = true;
React.useEffect(() => {
datastorePassword.getPasswordDatastore().then(onNewDatastoreLoaded);
return () => (isSubscribed = false);
}, []);
const onNewDatastoreLoaded = (data) => {
if (!isSubscribed) {
return;
}
setDatastore(data);
};
const onClear = () => {
setSearch("");
@ -96,8 +164,8 @@ const DatastoreView = (props) => {
if (lastSecurityReportAgeSeconds > recurrenceInterval) {
newSecurityReport = "REQUIRED";
} else {
var days_28 = 28 * 24 * 3600;
var days_14 = 14 * 24 * 3600;
const days_28 = 28 * 24 * 3600;
const days_14 = 14 * 24 * 3600;
if (recurrenceInterval >= days_28 && lastSecurityReportAgeSeconds > recurrenceInterval - days_14) {
newSecurityReport = "SOON_REQUIRED";
} else {
@ -109,6 +177,400 @@ const DatastoreView = (props) => {
}
}
const onNewShareCreate = (name) => {
// called once someone clicked the CREATE button in the dialog closes with the new name
setNewShareOpen(false);
// const onShare = (event) => {
// handleClose(event);
// if (content.hasOwnProperty("share_rights") && content.share_rights.grant === false) {
// return;
// }
//
// /**
// * little wrapper to create the share rights from the selected users / groups and rights for a given nonce and
// * a given share_id and key
// *
// * @param share_id
// * @param share_secret_key
// * @param node
// * @param users
// * @param groups
// * @param selected_users
// * @param selected_groups
// * @param selected_rights
// */
// var create_share_rights = function (share_id, share_secret_key, node, users, groups, selected_users, selected_groups, selected_rights) {
// var i;
// var modalInstance;
//
// // found a user that has been selected, lets create the rights for him
// var rights = {
// read: selected_rights.indexOf("read") > -1,
// write: selected_rights.indexOf("write") > -1,
// grant: selected_rights.indexOf("grant") > -1,
// };
//
// // generate the title
// // TODO create form field with this default value and read value from form
//
// var title = "";
// if (typeof node.type === "undefined") {
// // we have a folder
// title = "Folder with title '" + node.name + "'";
// } else {
// // we have an item
// title = _blueprints[node.type].name + " with title '" + node.name + "'";
// }
//
// // get the type
// var type = "";
// if (typeof node.type === "undefined") {
// // we have a folder
// type = "folder";
// } else {
// // we have an item
// type = node.type;
// }
//
// function create_user_share_right(user) {
// var onSuccess = function (data) {
// // pass
// };
// var onError = function (result) {
// var title;
// var description;
// if (result.data === null) {
// title = "UNKNOWN_ERROR";
// description = "UNKNOWN_ERROR_CHECK_BROWSER_CONSOLE";
// } else if (
// result.data.hasOwnProperty("non_field_errors") &&
// (result.data["non_field_errors"].indexOf("USER_DOES_NOT_EXIST_PROBABLY_DELETED") !== -1 ||
// result.data["non_field_errors"].indexOf("Target user does not exist.") !== -1)
// ) {
// title = "UNKNOWN_USER";
// description = _translations.USER_DOES_NOT_EXIST_PROBABLY_DELETED + " " + user.name;
// } else if (result.data.hasOwnProperty("non_field_errors")) {
// title = "ERROR";
// description = result.data["non_field_errors"][0];
// } else {
// title = "UNKNOWN_ERROR";
// description = "UNKNOWN_ERROR_CHECK_BROWSER_CONSOLE";
// }
//
// modalInstance = $uibModal.open({
// templateUrl: "view/modal/error.html",
// controller: "ModalErrorCtrl",
// resolve: {
// title: function () {
// return title;
// },
// description: function () {
// return description;
// },
// },
// });
//
// modalInstance.result.then(
// function (breadcrumbs) {
// // pass
// },
// function () {
// // cancel triggered
// }
// );
// };
//
// return registrations["create_share_right"](
// title,
// type,
// share_id,
// user.data.user_id,
// undefined,
// user.data.user_public_key,
// undefined,
// share_secret_key,
// rights["read"],
// rights["write"],
// rights["grant"]
// ).then(onSuccess, onError);
// }
//
// for (i = 0; i < users.length; i++) {
// if (selected_users.indexOf(users[i].id) < 0) {
// continue;
// }
// create_user_share_right(users[i]);
// }
//
// function create_group_share_right(group) {
// var onSuccess = function (data) {
// // pass
// };
// var onError = function (result) {
// var title;
// var description;
// if (result.data === null) {
// title = "UNKNOWN_ERROR";
// description = "UNKNOWN_ERROR_CHECK_BROWSER_CONSOLE";
// } else if (result.data.hasOwnProperty("non_field_errors")) {
// title = "ERROR";
// description = result.data["non_field_errors"][0];
// } else {
// title = "UNKNOWN_ERROR";
// description = "UNKNOWN_ERROR_CHECK_BROWSER_CONSOLE";
// }
//
// modalInstance = $uibModal.open({
// templateUrl: "view/modal/error.html",
// controller: "ModalErrorCtrl",
// resolve: {
// title: function () {
// return title;
// },
// description: function () {
// return description;
// },
// },
// });
//
// modalInstance.result.then(
// function (breadcrumbs) {
// // pass
// },
// function () {
// // cancel triggered
// }
// );
// };
//
// var group_secret_key = registrations["get_group_secret_key"](
// group.group_id,
// group.secret_key,
// group.secret_key_nonce,
// group.secret_key_type,
// group.public_key
// );
// return registrations["create_share_right"](
// title,
// type,
// share_id,
// undefined,
// group.group_id,
// undefined,
// group_secret_key,
// share_secret_key,
// rights["read"],
// rights["write"],
// rights["grant"]
// ).then(onSuccess, onError);
// }
//
// for (i = 0; i < groups.length; i++) {
// if (selected_groups.indexOf(groups[i].group_id) < 0) {
// continue;
// }
// create_group_share_right(groups[i]);
// }
// };
//
// /**
// * Users and or / shares have been selected in the modal and the final "Share Now" button was
// * clicked
// *
// * @param content
// */
// var on_modal_close_success = function (content) {
// // content = { node: "...", path: "...", selected_users: "...", users: "..."}
//
// var has_no_users = !content.users || content.users.length < 1 || !content.selected_users || content.selected_users.length < 1;
//
// var has_no_groups = !content.groups || content.groups.length < 1 || !content.selected_groups || content.selected_groups.length < 1;
//
// if (has_no_users && has_no_groups) {
// // TODO echo not shared message because no user / group selected
// return;
// }
//
// if (content.node.hasOwnProperty("share_id")) {
// // its already a share, so generate only the share_rights
//
// create_share_rights(
// content.node.share_id,
// content.node.share_secret_key,
// content.node,
// content.users,
// content.groups,
// content.selected_users,
// content.selected_groups,
// content.selected_rights
// );
// } else {
// // its not yet a share, so generate the share, generate the share_rights and update
// // the datastore
//
// registrations["get_password_datastore"]().then(function (datastore) {
// var path = content.path.slice();
// var closest_share_info = registrations["get_closest_parent_share"](path, datastore, null, 1);
// var parent_share = closest_share_info["closest_share"];
// var parent_share_id;
// var parent_datastore_id;
//
// if (parent_share !== false && parent_share !== null) {
// parent_share_id = parent_share.share_id;
// } else {
// parent_datastore_id = datastore.datastore_id;
// }
//
// // create the share
// registrations["create_share"](content.node, parent_share_id, parent_datastore_id, content.node.id).then(function (share_details) {
// var item_path = content.path.slice();
// var item_path_copy = content.path.slice();
// var item_path_copy2 = content.path.slice();
//
// // create the share right
// create_share_rights(
// share_details.share_id,
// share_details.secret_key,
// content.node,
// content.users,
// content.groups,
// content.selected_users,
// content.selected_groups,
// content.selected_rights
// );
//
// // update datastore and / or possible parent shares
// var search = registrations["find_in_datastore"](item_path, datastore);
//
// if (typeof content.node.type === "undefined") {
// // we have an item
// delete search[0][search[1]].secret_id;
// delete search[0][search[1]].secret_key;
// }
// search[0][search[1]].share_id = share_details.share_id;
// search[0][search[1]].share_secret_key = share_details.secret_key;
//
// // update node in our displayed datastore
// content.node.share_id = share_details.share_id;
// content.node.share_secret_key = share_details.secret_key;
//
// var changed_paths = registrations["on_share_added"](share_details.share_id, item_path_copy, datastore, 1);
//
// var parent_path = item_path_copy2.slice();
// parent_path.pop();
//
// changed_paths.push(parent_path);
//
// registrations["save_datastore_content"](datastore, changed_paths);
// });
// });
// }
// };
//
// var modalInstance = $uibModal.open({
// templateUrl: "view/modal/share-entry.html",
// controller: "ModalShareEntryCtrl",
// backdrop: "static",
// resolve: {
// node: function () {
// return item;
// },
// path: function () {
// return path;
// },
// },
// });
//
// // User clicked the final share button
// modalInstance.result.then(on_modal_close_success, function () {
// // cancel triggered
// });
// };
};
const onNewShare = (parent, path) => {
// called whenever someone clicks on a new folder Icon
setNewShareOpen(true);
setNewShareData({
parent: parent,
path: path,
});
};
const onNewFolderCreate = (name) => {
// called once someone clicked the CREATE button in the dialog closes with the new name
setNewFolderOpen(false);
widget.openNewFolder(newFolderData["parent"], newFolderData["path"], datastore, datastorePassword, name);
};
const onNewFolder = (parent, path) => {
setAnchorEl(null);
// called whenever someone clicks on a new folder Icon
setNewFolderOpen(true);
setNewFolderData({
parent: parent,
path: path,
});
};
const onNewEntryCreate = (name) => {
// called once someone clicked the CREATE button in the dialog closes with the new name
setNewEntryOpen(false);
widget.openNewEntry(newEntryData["parent"], newEntryData["path"], datastore, datastorePassword, name);
};
const onNewEntry = (parent, path) => {
setAnchorEl(null);
// called whenever someone clicks on a new folder Icon
setNewEntryOpen(true);
setNewEntryData({
parent: parent,
path: path,
});
};
const onEditFolderSave = (node) => {
setEditFolderOpen(false);
widget.openEditFolder(node, editFolderData.path, datastore, datastorePassword);
};
const onEditFolder = (node, path) => {
setEditFolderData({
node: node,
path: path,
});
setEditFolderOpen(true);
};
const onEditEntrySave = (node) => {
setEditEntryOpen(false);
widget.openEditEntry(node, editEntryData.path, datastore, datastorePassword);
};
const onEditEntry = (item, path) => {
setEditEntryData({
item: item,
path: path,
});
setEditEntryOpen(true);
};
const onLinkItem = (item, path) => {
if (item.type === "file") {
return fileTransferService.onItemClick(item);
} else {
return secretService.onItemClick(item);
}
};
const openMenu = (event) => {
event.preventDefault();
event.stopPropagation();
setAnchorEl(event.currentTarget);
};
const handleClose = (event) => {
event.preventDefault();
event.stopPropagation();
setAnchorEl(null);
};
return (
<Base {...props}>
<BaseTitle>{t("DATASTORE")}</BaseTitle>
@ -129,7 +591,7 @@ const DatastoreView = (props) => {
<Paper square>
<AppBar elevation={0} position="static" color="default">
<Toolbar className={classes.toolbarRoot}>
{t("DATASTORE")}
<span className={classes.toolbarTitle}>{t("DATASTORE")}</span>
<div className={classes.search}>
<InputBase
placeholder={t("SEARCH")}
@ -147,9 +609,27 @@ const DatastoreView = (props) => {
<ClearIcon />
</IconButton>
<Divider className={classes.divider} orientation="vertical" />
<IconButton color="primary" className={classes.iconButton} aria-label="menu">
<IconButton color="primary" className={classes.iconButton} aria-label="menu" onClick={openMenu}>
<MenuOpenIcon />
</IconButton>
<Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
<MenuItem onClick={() => onNewFolder(datastore, [])}>
<ListItemIcon className={classes.listItemIcon}>
<CreateNewFolderIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
{t("NEW_FOLDER")}
</Typography>
</MenuItem>
<MenuItem onClick={() => onNewEntry(datastore, [])}>
<ListItemIcon className={classes.listItemIcon}>
<AddIcon className={classes.icon} fontSize="small" />
</ListItemIcon>
<Typography variant="body2" noWrap>
{t("NEW_ENTRY")}
</Typography>
</MenuItem>
</Menu>
<Divider className={classes.divider} orientation="vertical" />
<IconButton className={classes.iconButton} aria-label="trash bin">
<DeleteSweepIcon />
@ -158,7 +638,38 @@ const DatastoreView = (props) => {
</Toolbar>
</AppBar>
<div className={classes.root}>
<PasswordDatastore search={search} />
{!datastore && (
<div className={classes.loader}>
<ClipLoader />
</div>
)}
{datastore && (
<DatastoreTree
datastore={datastore}
search={search}
onNewFolder={onNewFolder}
onNewEntry={onNewEntry}
onNewShare={onNewShare}
onEditEntry={onEditEntry}
onEditFolder={onEditFolder}
onSelectItem={onEditEntry}
onLinkItem={onLinkItem}
/>
)}
{newShareOpen && <DialogNewShare open={newShareOpen} onClose={() => setNewShareOpen(false)} onShare={onNewShareCreate} />}
{newFolderOpen && <DialogNewFolder open={newFolderOpen} onClose={() => setNewFolderOpen(false)} onCreate={onNewFolderCreate} />}
{editFolderOpen && (
<DialogEditFolder
open={editFolderOpen}
onClose={() => setEditFolderOpen(false)}
onSave={onEditFolderSave}
node={editFolderData.node}
/>
)}
{editEntryOpen && (
<DialogEditEntry open={editEntryOpen} onClose={() => setEditEntryOpen(false)} onSave={onEditEntrySave} item={editEntryData.item} />
)}
{newEntryOpen && <DialogNewEntry open={newEntryOpen} onClose={() => setNewEntryOpen(false)} onCreate={onNewEntryCreate} />}
</div>
</Paper>
</BaseContent>
@ -172,4 +683,4 @@ function mapStateToProps(state) {
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actionCreators, dispatch) };
}
export default connect(mapStateToProps, mapDispatchToProps)(DatastoreView);
export default DatastoreView;

View File

@ -1,5 +1,6 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { makeStyles } from "@material-ui/core/styles";
import MuiAlert from "@material-ui/lab/Alert";
import Box from "@material-ui/core/Box";
import LinearProgress from "@material-ui/core/LinearProgress";
@ -7,8 +8,15 @@ import { useParams } from "react-router-dom";
import store from "../../services/store";
import fileTransferService from "../../services/file-transfer";
const useStyles = makeStyles((theme) => ({
textCenter: {
textAlign: "center",
},
}));
const DownloadFileView = (props) => {
const { t } = useTranslation();
const classes = useStyles();
const [percentageComplete, setPercentageComplete] = React.useState(0);
const [nextStep, setNextStep] = React.useState("");
const [processing, setProcessing] = React.useState(false);
@ -65,7 +73,7 @@ const DownloadFileView = (props) => {
// }, []);
return (
<div className="progress-box text-center">
<div className={"progress-box " + classes.textCenter}>
<img src="img/logo.png" alt="Psono Web Client" id="logo" />
<a href="https://psono.com/" target="_blank" rel="noopener" className="infolabel">
<i className="fa fa-info-circle" aria-hidden="true" />

View File

@ -76,10 +76,11 @@ const EmergencyCodeViewForm = (props) => {
const onNewConfigLoaded = (configJson) => {
const serverUrl = configJson["backend_servers"][0]["url"];
const domain = configJson["backend_servers"][0]["domain"];
const allowCustomServer = configJson.allow_custom_server;
setServer(serverUrl);
setDomain(helperService.getDomain(serverUrl));
setDomain(domain);
setAllowCustomServer(allowCustomServer);
};

View File

@ -16,7 +16,7 @@ import Table from "../../components/table";
import groupsService from "../../services/groups";
import format from "../../services/date";
import CreateGroupDialog from "./create-group-dialog";
import VerifyDialog from "../../containers/verify-dialog";
import DialogVerify from "../../components/dialogs/verify";
const useStyles = makeStyles((theme) => ({
root: {
@ -300,7 +300,7 @@ const GroupsView = (props) => {
</Paper>
{createOpen && <CreateGroupDialog {...props} open={createOpen} onClose={closeCreateModal} />}
{verifyDeleteGroupOpen && (
<VerifyDialog
<DialogVerify
title={"DELETE_GROUP"}
description={"DELETE_GROUP_WARNING"}
entries={[groupNameBeingDeleted]}

View File

@ -116,6 +116,9 @@ const IndexView = (props) => {
<Route path="/active-link-shares">
<ActiveLinkShareView {...props} />
</Route>
<Route path="/datastore/search/:defaultSearch">
<DatastoreView {...props} />
</Route>
<Route path="/">
<DatastoreView {...props} />
</Route>

View File

@ -357,6 +357,7 @@ const LoginViewForm = (props) => {
const onNewConfigLoaded = (configJson) => {
const serverUrl = configJson["backend_servers"][0]["url"];
const domain = configJson["backend_servers"][0]["domain"];
const allowRegistration =
!configJson.hasOwnProperty("allow_registration") || (configJson.hasOwnProperty("allow_registration") && configJson["allow_registration"]);
const allowLostPassword =
@ -375,7 +376,7 @@ const LoginViewForm = (props) => {
setAllowLostPassword(allowLostPassword);
setAllowRegistration(allowRegistration);
setServer(serverUrl);
setDomain(helperService.getDomain(serverUrl));
setDomain(domain);
setSamlProvider(samlProvider);
setOidcProvider(oidcProvider);
setAuthenticationMethods(authenticationMethods);

View File

@ -124,6 +124,7 @@ const LostPasswordViewForm = (props) => {
const onNewConfigLoaded = (configJson) => {
const serverUrl = configJson["backend_servers"][0]["url"];
const domain = configJson["backend_servers"][0]["domain"];
const allowLostPassword =
(!configJson.hasOwnProperty("allow_lost_password") || (configJson.hasOwnProperty("allow_lost_password") && configJson["allow_lost_password"])) &&
configJson["authentication_methods"].indexOf("AUTHKEY") !== -1;
@ -131,7 +132,7 @@ const LostPasswordViewForm = (props) => {
setAllowLostPassword(allowLostPassword);
setServer(serverUrl);
setDomain(helperService.getDomain(serverUrl));
setDomain(domain);
setAllowCustomServer(allowCustomServer);
};

View File

@ -16,7 +16,7 @@ import actionCreators from "../../actions/action-creators";
import GridContainerErrors from "../../components/grid-container-errors";
import fileRepository from "../../services/file-repository";
import helperService from "../../services/helper";
import FileRepositoryTypeSelectField from "../../containers/file-repository-type-select-field";
import SelectFieldFileRepositoryType from "../../components/select-field/file-repository-type";
import TextFieldAWSRegion from "../../components/text-field-aws-region";
import TextFieldDoRegion from "../../components/text-field-do-region";
@ -156,7 +156,7 @@ const CreateFileRepositoriesDialog = (props) => {
/>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<FileRepositoryTypeSelectField
<SelectFieldFileRepositoryType
className={classes.textField}
variant="outlined"
margin="dense"

View File

@ -2,10 +2,8 @@ import React, { useState } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { bindActionCreators } from "redux";
import { compose } from "redux";
import { useTranslation } from "react-i18next";
import { makeStyles } from "@material-ui/core/styles";
import Box from "@material-ui/core/Box";
import Dialog from "@material-ui/core/Dialog";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";

Some files were not shown because too many files have changed in this diff Show More