mirror of
https://gitlab.com/psono/psono-client
synced 2025-04-19 03:22:16 +03:00
Upgraded to Manifest v3
Signed-off-by: Sascha Pfeiffer <sascha.pfeiffer@esaqa.com>
This commit is contained in:
parent
1b0c426a20
commit
889ac2786f
@ -7,8 +7,5 @@ module.exports = async () => {
|
||||
"globals": {
|
||||
"TARGET": "webclient"
|
||||
},
|
||||
"moduleNameMapper": {
|
||||
'^axios$': require.resolve('axios'),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
70
package-lock.json
generated
70
package-lock.json
generated
@ -18,7 +18,6 @@
|
||||
"@openpgp/hkp-client": "^0.0.2",
|
||||
"@sentry/react": "^6.19.6",
|
||||
"@sentry/tracing": "^6.19.6",
|
||||
"axios": "^1.2.1",
|
||||
"chart.js": "^3.7.0",
|
||||
"classnames": "^2.3.2",
|
||||
"clientjs": "^0.2.1",
|
||||
@ -5513,6 +5512,7 @@
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/at-least-node": {
|
||||
@ -5575,29 +5575,6 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz",
|
||||
"integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios/node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/axobject-query": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
|
||||
@ -6526,6 +6503,7 @@
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
@ -7308,6 +7286,7 @@
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
@ -9149,6 +9128,7 @@
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@ -15627,11 +15607,6 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"node_modules/psl": {
|
||||
"version": "1.8.0",
|
||||
"dev": true,
|
||||
@ -23734,7 +23709,8 @@
|
||||
"dev": true
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0"
|
||||
"version": "0.4.0",
|
||||
"dev": true
|
||||
},
|
||||
"at-least-node": {
|
||||
"version": "1.0.0",
|
||||
@ -23774,28 +23750,6 @@
|
||||
"integrity": "sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw==",
|
||||
"dev": true
|
||||
},
|
||||
"axios": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz",
|
||||
"integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"axobject-query": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
|
||||
@ -24494,6 +24448,7 @@
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
@ -25036,7 +24991,8 @@
|
||||
"dev": true
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0"
|
||||
"version": "1.0.0",
|
||||
"dev": true
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
@ -26382,7 +26338,8 @@
|
||||
"follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"dev": true
|
||||
},
|
||||
"font-awesome": {
|
||||
"version": "4.7.0"
|
||||
@ -30922,11 +30879,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"psl": {
|
||||
"version": "1.8.0",
|
||||
"dev": true
|
||||
|
@ -29,7 +29,6 @@
|
||||
"@openpgp/hkp-client": "^0.0.2",
|
||||
"@sentry/react": "^6.19.6",
|
||||
"@sentry/tracing": "^6.19.6",
|
||||
"axios": "^1.2.1",
|
||||
"chart.js": "^3.7.0",
|
||||
"classnames": "^2.3.2",
|
||||
"clientjs": "^0.2.1",
|
||||
|
40
src/chrome/data/js/offscreen.js
Normal file
40
src/chrome/data/js/offscreen.js
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
chrome.runtime.onMessage.addListener(onMessage);
|
||||
|
||||
async function onMessage(request, sender, sendResponse) {
|
||||
const eventFunctions = {
|
||||
"get-offline-cache-encryption-key-offscreen": getOfflineCacheEncryptionKey,
|
||||
"set-offline-cache-encryption-key-offscreen": setOfflineCacheEncryptionKey,
|
||||
};
|
||||
|
||||
if (eventFunctions.hasOwnProperty(request.event)) {
|
||||
return eventFunctions[request.event](request, sender, sendResponse);
|
||||
} else {
|
||||
// not catchable event
|
||||
// console.log(sender.tab);
|
||||
// console.log("offscreen script received (uncaptured) " + request.event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We received a get-offline-cache-encryption-key-offscreen event and as such we respond with the psono_offline_cache_encryption_key
|
||||
*
|
||||
* @param {object} request The message sent by the calling script.
|
||||
* @param {object} sender The sender of the message
|
||||
* @param {function} sendResponse Function to call (at most once) when you have a response.
|
||||
*/
|
||||
function getOfflineCacheEncryptionKey(request, sender, sendResponse) {
|
||||
sendResponse(window.psono_offline_cache_encryption_key || "")
|
||||
}
|
||||
|
||||
/**
|
||||
* We received a set-offline-cache-encryption-key-offscreen event and as such we set the psono_offline_cache_encryption_key
|
||||
*
|
||||
* @param {object} request The message sent by the calling script.
|
||||
* @param {object} sender The sender of the message
|
||||
* @param {function} sendResponse Function to call (at most once) when you have a response.
|
||||
*/
|
||||
function setOfflineCacheEncryptionKey(request, sender, sendResponse) {
|
||||
window.psono_offline_cache_encryption_key = request.data
|
||||
}
|
2
src/chrome/data/offscreen.html
Normal file
2
src/chrome/data/offscreen.html
Normal file
@ -0,0 +1,2 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="js/offscreen.js"></script>
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"manifest_version": 3,
|
||||
|
||||
"name": "Psono - Free Password Manager",
|
||||
"short_name": "Psono - Free Password Manager",
|
||||
@ -9,7 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"homepage_url":"https://psono.com/",
|
||||
|
||||
"browser_action": {
|
||||
"action": {
|
||||
"default_icon": "data/img/icon-32-disabled.png",
|
||||
"default_popup": "data/default_popup.html"
|
||||
},
|
||||
@ -23,7 +23,10 @@
|
||||
"tabs",
|
||||
"clipboardWrite",
|
||||
"webRequest",
|
||||
"webRequestBlocking",
|
||||
"webRequestAuthProvider",
|
||||
"offscreen"
|
||||
],
|
||||
"host_permissions": [
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
@ -41,8 +44,7 @@
|
||||
"keyword": "pp"
|
||||
},
|
||||
"background": {
|
||||
"page": "data/background.html",
|
||||
"persistent": true
|
||||
"service_worker": "data/js/background-chrome.js"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
@ -64,14 +66,17 @@
|
||||
}
|
||||
],
|
||||
"options_page": "data/index.html#!/settings",
|
||||
"web_accessible_resources": [
|
||||
"data/fonts/*.woff2",
|
||||
"data/css/contentscript.css",
|
||||
"data/img/psono-encrypt.png",
|
||||
"data/img/psono-decrypt.png"
|
||||
],
|
||||
"web_accessible_resources": [{
|
||||
"resources": [
|
||||
"data/fonts/*.woff2",
|
||||
"data/css/contentscript.css",
|
||||
"data/img/psono-encrypt.png",
|
||||
"data/img/psono-decrypt.png"
|
||||
],
|
||||
"matches": ["*://*/*"]
|
||||
}],
|
||||
"commands": {
|
||||
"_execute_browser_action": {
|
||||
"_execute_action": {
|
||||
"suggested_key": {
|
||||
"default": "Alt+P"
|
||||
}
|
||||
|
@ -754,6 +754,7 @@
|
||||
"HERE_YOU_CAN_MANAGE_ALL_YOUR_PASSWORD_DATASTORES": "Hier können sie alle ihre Passwort Datenspeicher verwalten",
|
||||
"DATASTORE": "Datenspeicher",
|
||||
"DATASTORE_DESCRIPTION_MUST_BE_UNIQUE": "Die Beschreibung des Datenspeichers muss eindeutig sein. Die gewählte Beschreibung wird bereits von einem anderen Datenspeicher verwendet.",
|
||||
"UNIQUE": "Einzigartig",
|
||||
"DATASTORES": "Datenspeicher",
|
||||
"NEW_SERVER": "Neuer Server",
|
||||
"APPROVE_UNSAFE": "Genehmigen (unsicher)",
|
||||
|
@ -754,6 +754,7 @@
|
||||
"HERE_YOU_CAN_MANAGE_ALL_YOUR_PASSWORD_DATASTORES": "Here you can manage all your password datastores",
|
||||
"DATASTORE": "Datastore",
|
||||
"DATASTORE_DESCRIPTION_MUST_BE_UNIQUE": "The description of a datastore must be unique. The chosen description is already used by another datastore.",
|
||||
"UNIQUE": "Unique",
|
||||
"DATASTORES": "Datastores",
|
||||
"NEW_SERVER": "New Server",
|
||||
"APPROVE_UNSAFE": "Approve (unsafe)",
|
||||
|
11
src/js/background-chrome.js
Normal file
11
src/js/background-chrome.js
Normal file
@ -0,0 +1,11 @@
|
||||
import backgroundService from "./services/background";
|
||||
import {persistStore} from "redux-persist";
|
||||
import store from "./services/store";
|
||||
|
||||
function loadSettingsDatastore(dispatch, getState) {
|
||||
backgroundService.activate()
|
||||
}
|
||||
|
||||
persistStore(store, null, () => {
|
||||
store.dispatch(loadSettingsDatastore);
|
||||
});
|
@ -65,7 +65,7 @@ const DialogGoOffline = (props) => {
|
||||
.then(function (datastore) {
|
||||
closedRequest = closedRequest + 1;
|
||||
openRequests = openRequests + 1;
|
||||
exportService.getAllSecrets(datastore, true).then(function () {
|
||||
exportService.getAllSecrets(datastore, true, true).then(function () {
|
||||
closedRequest = closedRequest + 1;
|
||||
potentiallyCloseDialog();
|
||||
});
|
||||
|
@ -1,49 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Container from '@material-ui/core/Container'
|
||||
import LinearProgress from '@material-ui/core/LinearProgress'
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
bar: ({ animationDuration }) => ({
|
||||
transitionDuration: `${animationDuration}ms`,
|
||||
}),
|
||||
container: ({ animationDuration, isFinished }) => ({
|
||||
opacity: isFinished ? 0 : 1,
|
||||
pointerEvents: 'none',
|
||||
transition: `opacity ${animationDuration}ms linear`,
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
zIndex: 2000,
|
||||
}),
|
||||
}));
|
||||
|
||||
const RequestProgressBar = (props) => {
|
||||
const requestCounterOpen = useSelector((state) => state.transient.requestCounterOpen);
|
||||
const requestCounterClosed = useSelector((state) => state.transient.requestCounterClosed);
|
||||
|
||||
const classes = useStyles(200, requestCounterOpen === 0 || requestCounterClosed === 0);
|
||||
|
||||
if (requestCounterOpen === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let progress = 0;
|
||||
if (requestCounterOpen !== 0 && requestCounterClosed !== 0) {
|
||||
progress = requestCounterClosed / requestCounterOpen * 100;
|
||||
}
|
||||
return (
|
||||
|
||||
<Container classes={{ root: classes.container }} disableGutters={true}>
|
||||
<LinearProgress
|
||||
classes={{ bar1Determinate: classes.bar }}
|
||||
value={progress * 100}
|
||||
variant="determinate"
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default RequestProgressBar;
|
@ -44,6 +44,14 @@ Object.entries(languages).forEach(([key, value]) => {
|
||||
}
|
||||
});
|
||||
|
||||
let loadPath = ''
|
||||
if (typeof window === "undefined") {
|
||||
// chrome backend worker
|
||||
loadPath = "/data/translations/locale-{{lng}}.json"
|
||||
} else {
|
||||
loadPath = "./translations/locale-{{lng}}.json"
|
||||
}
|
||||
|
||||
i18n
|
||||
// load translation using xhr -> see /public/locales
|
||||
// learn more: https://github.com/i18next/i18next-xhr-backend
|
||||
@ -62,7 +70,7 @@ i18n
|
||||
debug: process.env.NODE_ENV === "development",
|
||||
backend: {
|
||||
// for all available options read the backend's repository readme file
|
||||
loadPath: "./translations/locale-{{lng}}.json",
|
||||
loadPath: loadPath,
|
||||
},
|
||||
|
||||
interpolation: {
|
||||
|
@ -19,14 +19,11 @@ import datastoreSettingService from "./services/datastore-setting";
|
||||
import i18n from "./i18n";
|
||||
import theme from "./theme";
|
||||
import { initSentry } from './var/sentry'
|
||||
import { initRequestProgressBar } from './var/request-progress-bar'
|
||||
|
||||
initSentry()
|
||||
initRequestProgressBar()
|
||||
|
||||
import IndexView from "./views/index";
|
||||
import DownloadBanner from "./components/download-banner";
|
||||
import RequestProgressBar from "./components/request-progress-bar";
|
||||
|
||||
/**
|
||||
* Loads the datastore
|
||||
@ -56,7 +53,6 @@ const App = () => {
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<HashRouter history={customHistory} hashType={"hashbang"}>
|
||||
<RequestProgressBar />
|
||||
<DownloadBanner />
|
||||
<IndexView />
|
||||
</HashRouter>
|
||||
|
@ -2,23 +2,16 @@
|
||||
* Service to talk to the AWS S3 and upload or download files
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import converterService from "./converter";
|
||||
|
||||
function call(fileserverUrl, method, endpoint, data, headers, transformRequest, responseType) {
|
||||
if (!transformRequest) {
|
||||
transformRequest = axios.defaults.transformRequest;
|
||||
}
|
||||
function call(signedUrl, method, endpoint, data, headers) {
|
||||
|
||||
const req = {
|
||||
method: method,
|
||||
url: fileserverUrl + endpoint,
|
||||
data: data,
|
||||
transformRequest: transformRequest,
|
||||
responseType: responseType,
|
||||
body: data,
|
||||
};
|
||||
|
||||
req.headers = headers;
|
||||
if (headers) {
|
||||
req.headers = headers;
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
const onSuccess = function (data) {
|
||||
@ -29,14 +22,14 @@ function call(fileserverUrl, method, endpoint, data, headers, transformRequest,
|
||||
return reject(data);
|
||||
};
|
||||
|
||||
axios(req).then(onSuccess, onError);
|
||||
fetch(signedUrl + endpoint, req).then(onSuccess, onError);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax PUT request to upload a file chunk to AWS S3
|
||||
*
|
||||
* @param {string} signedUrl The signed ulr
|
||||
* @param {string} signedUrl The signed url
|
||||
* @param {object} fields Array of fields that need to be part of the request
|
||||
* @param {Blob} chunk The content of the chunk to upload
|
||||
*
|
||||
@ -53,9 +46,7 @@ function upload(signedUrl, fields, chunk) {
|
||||
data.append(field_name, fields[field_name]);
|
||||
}
|
||||
data.append("file", chunk);
|
||||
const headers = {
|
||||
"Content-Type": undefined,
|
||||
};
|
||||
const headers = {};
|
||||
|
||||
return call(signedUrl, method, endpoint, data, headers);
|
||||
}
|
||||
@ -63,7 +54,7 @@ function upload(signedUrl, fields, chunk) {
|
||||
/**
|
||||
* Ajax GET request to download a file chunk from AWS S3
|
||||
*
|
||||
* @param {string} signedUrl The signed ulr
|
||||
* @param {string} signedUrl The signed url
|
||||
*
|
||||
* @returns {Promise} promise with the data
|
||||
*/
|
||||
@ -74,14 +65,13 @@ function download(signedUrl) {
|
||||
|
||||
const headers = {};
|
||||
|
||||
return call(signedUrl, method, endpoint, data, headers, undefined, "arraybuffer").then(
|
||||
function (data) {
|
||||
return data;
|
||||
return call(signedUrl, method, endpoint, data, headers).then(
|
||||
async function (data) {
|
||||
return {
|
||||
data: await data.arrayBuffer()
|
||||
};
|
||||
},
|
||||
function (data) {
|
||||
if (data.status === 400) {
|
||||
data.data = JSON.parse(converterService.bytesToString(data.data));
|
||||
}
|
||||
return Promise.reject(data);
|
||||
}
|
||||
);
|
||||
|
@ -2,23 +2,16 @@
|
||||
* Service to talk to the Azure Blob Storage and upload or download files
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import converterService from "./converter";
|
||||
|
||||
function call(signedUrl, method, endpoint, data, headers, transformRequest, responseType) {
|
||||
if (!transformRequest) {
|
||||
transformRequest = axios.defaults.transformRequest;
|
||||
}
|
||||
function call(signedUrl, method, endpoint, data, headers) {
|
||||
|
||||
const req = {
|
||||
method: method,
|
||||
url: signedUrl + endpoint,
|
||||
data: data,
|
||||
transformRequest: transformRequest,
|
||||
responseType: responseType,
|
||||
body: data,
|
||||
};
|
||||
|
||||
req.headers = headers;
|
||||
if (headers) {
|
||||
req.headers = headers;
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
const onSuccess = function (data) {
|
||||
@ -29,14 +22,14 @@ function call(signedUrl, method, endpoint, data, headers, transformRequest, resp
|
||||
return reject(data);
|
||||
};
|
||||
|
||||
axios(req).then(onSuccess, onError);
|
||||
fetch(signedUrl + endpoint, req).then(onSuccess, onError);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax PUT request to upload a file chunk to Azure Blob Storage
|
||||
*
|
||||
* @param {string} signedUrl The signed ulr
|
||||
* @param {string} signedUrl The signed url
|
||||
* @param {Blob} chunk The content of the chunk to upload
|
||||
*
|
||||
* @returns {Promise} promise
|
||||
@ -44,10 +37,8 @@ function call(signedUrl, method, endpoint, data, headers, transformRequest, resp
|
||||
function upload(signedUrl, chunk) {
|
||||
const endpoint = ""; // the signed url already has everything
|
||||
const method = "PUT";
|
||||
|
||||
const headers = {
|
||||
"x-ms-blob-type": "BlockBlob",
|
||||
"Content-Type": undefined,
|
||||
"x-ms-blob-type": "BlockBlob"
|
||||
};
|
||||
|
||||
return call(signedUrl, method, endpoint, chunk, headers);
|
||||
@ -56,7 +47,7 @@ function upload(signedUrl, chunk) {
|
||||
/**
|
||||
* Ajax GET request to download a file chunk from Azure Blob Storage
|
||||
*
|
||||
* @param {string} signedUrl The signed ulr
|
||||
* @param {string} signedUrl The signed url
|
||||
*
|
||||
* @returns {Promise} promise with the data
|
||||
*/
|
||||
@ -67,14 +58,13 @@ function download(signedUrl) {
|
||||
|
||||
const headers = {};
|
||||
|
||||
return call(signedUrl, method, endpoint, data, headers, undefined, "arraybuffer").then(
|
||||
function (data) {
|
||||
return data;
|
||||
return call(signedUrl, method, endpoint, data, headers).then(
|
||||
async function (data) {
|
||||
return {
|
||||
data: await data.arrayBuffer()
|
||||
};
|
||||
},
|
||||
function (data) {
|
||||
if (data.status === 400) {
|
||||
data.data = JSON.parse(converterService.bytesToString(data.data));
|
||||
}
|
||||
return Promise.reject(data);
|
||||
}
|
||||
);
|
||||
|
@ -1,24 +1,17 @@
|
||||
/**
|
||||
* Service to talk to the Digital Ocean and upload or download files
|
||||
* Service to talk to the Backblaze and upload or download files
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import converterService from "./converter";
|
||||
|
||||
function call(signedUrl, method, endpoint, data, headers, transformRequest, responseType) {
|
||||
if (!transformRequest) {
|
||||
transformRequest = axios.defaults.transformRequest;
|
||||
}
|
||||
function call(signedUrl, method, endpoint, data, headers) {
|
||||
|
||||
const req = {
|
||||
method: method,
|
||||
url: signedUrl + endpoint,
|
||||
data: data,
|
||||
transformRequest: transformRequest,
|
||||
responseType: responseType,
|
||||
body: data,
|
||||
};
|
||||
|
||||
req.headers = headers;
|
||||
if (headers) {
|
||||
req.headers = headers;
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
const onSuccess = function (data) {
|
||||
@ -29,14 +22,14 @@ function call(signedUrl, method, endpoint, data, headers, transformRequest, resp
|
||||
return reject(data);
|
||||
};
|
||||
|
||||
axios(req).then(onSuccess, onError);
|
||||
fetch(signedUrl + endpoint, req).then(onSuccess, onError);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax PUT request to upload a file chunk to AWS S3
|
||||
* Ajax PUT request to upload a file chunk to Backblaze
|
||||
*
|
||||
* @param {string} signedUrl The signed ulr
|
||||
* @param {string} signedUrl The signed url
|
||||
* @param {object} fields Array of fields that need to be part of the request
|
||||
* @param {Blob} chunk The content of the chunk to upload
|
||||
*
|
||||
@ -53,17 +46,15 @@ function upload(signedUrl, fields, chunk) {
|
||||
data.append(field_name, fields[field_name]);
|
||||
}
|
||||
data.append("file", chunk);
|
||||
const headers = {
|
||||
"Content-Type": undefined,
|
||||
};
|
||||
const headers = {};
|
||||
|
||||
return call(signedUrl, method, endpoint, data, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax GET request to download a file chunk from AWS S3
|
||||
* Ajax GET request to download a file chunk from Backblaze
|
||||
*
|
||||
* @param {string} signedUrl The signed ulr
|
||||
* @param {string} signedUrl The signed url
|
||||
*
|
||||
* @returns {Promise} promise with the data
|
||||
*/
|
||||
@ -74,14 +65,13 @@ function download(signedUrl) {
|
||||
|
||||
const headers = {};
|
||||
|
||||
return call(signedUrl, method, endpoint, data, headers, undefined, "arraybuffer").then(
|
||||
function (data) {
|
||||
return data;
|
||||
return call(signedUrl, method, endpoint, data, headers).then(
|
||||
async function (data) {
|
||||
return {
|
||||
data: await data.arrayBuffer()
|
||||
};
|
||||
},
|
||||
function (data) {
|
||||
if (data.status === 400) {
|
||||
data.data = JSON.parse(converterService.bytesToString(data.data));
|
||||
}
|
||||
return Promise.reject(data);
|
||||
}
|
||||
);
|
||||
|
@ -1,7 +1,6 @@
|
||||
/**
|
||||
* Service to talk to the psono REST api
|
||||
*/
|
||||
import axios from "axios";
|
||||
import cryptoLibrary from "./crypto-library";
|
||||
import offlineCache from "./offline-cache";
|
||||
import device from "./device";
|
||||
@ -12,7 +11,7 @@ import i18n from "../i18n";
|
||||
// TODO add later for audit log again
|
||||
//let AUDIT_LOG_HEADER = 'Audit-Log';
|
||||
|
||||
const decryptData = function (sessionSecretKey, data, req) {
|
||||
const decryptData = function (sessionSecretKey, data, url, method) {
|
||||
if (
|
||||
sessionSecretKey &&
|
||||
data !== null &&
|
||||
@ -37,15 +36,15 @@ const decryptData = function (sessionSecretKey, data, req) {
|
||||
) {
|
||||
data.data = JSON.parse(cryptoLibrary.decryptData(data.data.text, data.data.nonce, sessionSecretKey));
|
||||
}
|
||||
offlineCache.set(req, data);
|
||||
offlineCache.set(url, method, data);
|
||||
return data;
|
||||
};
|
||||
|
||||
function call(method, endpoint, data, headers, sessionSecretKey) {
|
||||
function call(method, endpoint, body, headers, sessionSecretKey) {
|
||||
const url = store.getState().server.url + endpoint;
|
||||
|
||||
if (sessionSecretKey && data !== null) {
|
||||
data = cryptoLibrary.encryptData(JSON.stringify(data), sessionSecretKey);
|
||||
if (sessionSecretKey && body !== null) {
|
||||
body = cryptoLibrary.encryptData(JSON.stringify(body), sessionSecretKey);
|
||||
}
|
||||
|
||||
if (sessionSecretKey && headers && headers.hasOwnProperty("Authorization")) {
|
||||
@ -72,79 +71,93 @@ function call(method, endpoint, data, headers, sessionSecretKey) {
|
||||
|
||||
const req = {
|
||||
method,
|
||||
url,
|
||||
data,
|
||||
headers,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...headers
|
||||
}
|
||||
};
|
||||
|
||||
return offlineCache.get(req).then((cached) => {
|
||||
if (body != null) {
|
||||
req['body'] = JSON.stringify(body);
|
||||
}
|
||||
|
||||
return offlineCache.get(url, req.method).then((cached) => {
|
||||
if (cached !== null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const onSuccess = function (data) {
|
||||
let decryptedData
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let rawResponse;
|
||||
try {
|
||||
rawResponse = await fetch(url, req);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
reject({errors: ["SERVER_OFFLINE"]});
|
||||
return;
|
||||
}
|
||||
|
||||
let data = await rawResponse.text();
|
||||
if (data) {
|
||||
try {
|
||||
decryptedData = decryptData(sessionSecretKey, data, req)
|
||||
} catch(e){
|
||||
user.logout(i18n.t("UNENCRYPTED_RESPONSE_RECEIVED"));
|
||||
return reject({ errors: ["UNENCRYPTED_RESPONSE_RECEIVED"] })
|
||||
data = JSON.parse(data);
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
let decryptedData
|
||||
|
||||
// compatibility to old axios library
|
||||
if (data) {
|
||||
data = {
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
if (!rawResponse.ok) {
|
||||
console.log(rawResponse);
|
||||
console.log(data);
|
||||
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
if (rawResponse.status === 401 && user.isLoggedIn()) {
|
||||
// session expired, lets log the user out
|
||||
user.logout(i18n.t("SESSION_EXPIRED"));
|
||||
}
|
||||
if (rawResponse.status === 423 && user.isLoggedIn()) {
|
||||
// server error, lets log the user out
|
||||
user.logout(rawResponse.statusText);
|
||||
}
|
||||
if (rawResponse.status === 502 && user.isLoggedIn()) {
|
||||
// server error, lets log the user out
|
||||
user.logout(rawResponse.statusText);
|
||||
}
|
||||
if (rawResponse.status === 503 && user.isLoggedIn()) {
|
||||
// server error, lets log the user out
|
||||
user.logout(rawResponse.statusText);
|
||||
}
|
||||
if (rawResponse.status >= 500) {
|
||||
if (rawResponse.statusText) {
|
||||
return reject(rawResponse.statusText);
|
||||
}
|
||||
return reject({errors: ["SERVER_OFFLINE"]});
|
||||
}
|
||||
// received error 400. We fall through here and check below with rawResponse.ok whether we have to return
|
||||
// a success or failed response
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
decryptedData = decryptData(sessionSecretKey, data, url, req.method)
|
||||
} catch (e) {
|
||||
user.logout(i18n.t("UNENCRYPTED_RESPONSE_RECEIVED"));
|
||||
return reject({errors: ["UNENCRYPTED_RESPONSE_RECEIVED"]})
|
||||
}
|
||||
if (rawResponse.ok) {
|
||||
return resolve(decryptedData);
|
||||
};
|
||||
|
||||
const onError = function (error) {
|
||||
if (error.response) {
|
||||
console.log(error.response);
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
if (error.response.status === 401 && user.isLoggedIn()) {
|
||||
// session expired, lets log the user out
|
||||
user.logout(i18n.t("SESSION_EXPIRED"));
|
||||
}
|
||||
if (error.response.status === 423 && user.isLoggedIn()) {
|
||||
// server error, lets log the user out
|
||||
user.logout(error.response.statusText);
|
||||
}
|
||||
if (error.response.status === 502 && user.isLoggedIn()) {
|
||||
// server error, lets log the user out
|
||||
user.logout(error.response.statusText);
|
||||
}
|
||||
if (error.response.status === 503 && user.isLoggedIn()) {
|
||||
// server error, lets log the user out
|
||||
user.logout(error.response.statusText);
|
||||
}
|
||||
if (error.response.status >= 500) {
|
||||
if (error.response.statusText) {
|
||||
return reject(error.response.statusText);
|
||||
}
|
||||
return reject({ errors: ["SERVER_OFFLINE"] });
|
||||
}
|
||||
|
||||
let decryptedData
|
||||
try {
|
||||
decryptedData = decryptData(sessionSecretKey, error.response, req)
|
||||
} catch(e){
|
||||
user.logout(i18n.t("UNENCRYPTED_RESPONSE_RECEIVED"));
|
||||
return reject({ errors: ["UNENCRYPTED_RESPONSE_RECEIVED"] })
|
||||
}
|
||||
return reject(decryptedData);
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
console.log(error.request);
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log("Error", error.message);
|
||||
}
|
||||
console.log(error.config);
|
||||
reject({ errors: ["SERVER_OFFLINE"] });
|
||||
};
|
||||
|
||||
axios(req).then(onSuccess, onError);
|
||||
} else {
|
||||
return reject(decryptedData);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,59 +1,42 @@
|
||||
/**
|
||||
* Service to talk to the Digital Ocean and upload or download files
|
||||
* Service to talk to the Digital Ocean Spaces and upload or download files
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import converterService from './converter';
|
||||
|
||||
function call(signed_url, method, endpoint, data, headers, transformRequest, responseType) {
|
||||
|
||||
if (!transformRequest) {
|
||||
transformRequest = axios.defaults.transformRequest;
|
||||
}
|
||||
function call(signedUrl, method, endpoint, data, headers) {
|
||||
|
||||
const req = {
|
||||
method: method,
|
||||
url: signed_url + endpoint,
|
||||
data: data,
|
||||
transformRequest: transformRequest,
|
||||
responseType: responseType
|
||||
body: data,
|
||||
};
|
||||
|
||||
req.headers = headers;
|
||||
if (headers) {
|
||||
req.headers = headers;
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
const onSuccess = function(data) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const onSuccess = function (data) {
|
||||
return resolve(data);
|
||||
};
|
||||
|
||||
const onError = function(data) {
|
||||
const onError = function (data) {
|
||||
return reject(data);
|
||||
};
|
||||
|
||||
axios(req)
|
||||
.then(onSuccess, onError);
|
||||
|
||||
fetch(signedUrl + endpoint, req).then(onSuccess, onError);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @name psonocli.apiDO#upload
|
||||
* @methodOf psonocli.apiDO
|
||||
* Ajax PUT request to upload a file chunk to Digital Ocean Spaces
|
||||
*
|
||||
* @description
|
||||
* Ajax PUT request to upload a file chunk to AWS S3
|
||||
*
|
||||
* @param {string} signed_url The signed ulr
|
||||
* @param {string} signedUrl The signed url
|
||||
* @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) {
|
||||
|
||||
const endpoint = ''; // the signed url already has everything
|
||||
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) {
|
||||
@ -62,49 +45,41 @@ function upload(signed_url, fields, chunk) {
|
||||
}
|
||||
data.append(field_name, fields[field_name]);
|
||||
}
|
||||
data.append('file', chunk);
|
||||
const headers = {
|
||||
'Content-Type': undefined
|
||||
};
|
||||
data.append("file", chunk);
|
||||
const headers = {};
|
||||
|
||||
return call(signed_url, method, endpoint, data, headers);
|
||||
return call(signedUrl, method, endpoint, data, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @name psonocli.apiDO#download
|
||||
* @methodOf psonocli.apiDO
|
||||
* Ajax GET request to download a file chunk from Digital Ocean Spaces
|
||||
*
|
||||
* @description
|
||||
* Ajax GET request to download a file chunk from AWS S3
|
||||
*
|
||||
* @param {string} signed_url The signed ulr
|
||||
* @param {string} signedUrl The signed url
|
||||
*
|
||||
* @returns {Promise} promise with the data
|
||||
*/
|
||||
function download(signed_url) {
|
||||
|
||||
const endpoint = ''; // the signed url already has everything
|
||||
function download(signedUrl) {
|
||||
const endpoint = ""; // the signed url already has everything
|
||||
const method = "GET";
|
||||
const data = null;
|
||||
|
||||
const headers = {
|
||||
};
|
||||
const headers = {};
|
||||
|
||||
return call(signed_url, 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 call(signedUrl, method, endpoint, data, headers).then(
|
||||
async function (data) {
|
||||
return {
|
||||
data: await data.arrayBuffer()
|
||||
};
|
||||
},
|
||||
function (data) {
|
||||
return Promise.reject(data);
|
||||
}
|
||||
return Promise.reject(data)
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const apiDoService = {
|
||||
upload: upload,
|
||||
download: download
|
||||
download: download,
|
||||
};
|
||||
|
||||
export default apiDoService;
|
||||
export default apiDoService;
|
||||
|
@ -2,23 +2,18 @@
|
||||
* Service to talk to the psono REST api
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import converterService from "./converter";
|
||||
|
||||
function call(fileserverUrl, method, endpoint, data, headers, transformRequest, responseType) {
|
||||
if (!transformRequest) {
|
||||
transformRequest = axios.defaults.transformRequest;
|
||||
}
|
||||
function call(fileserverUrl, method, endpoint, data, headers) {
|
||||
|
||||
const req = {
|
||||
method: method,
|
||||
url: fileserverUrl + endpoint,
|
||||
data: data,
|
||||
transformRequest: transformRequest,
|
||||
responseType: responseType,
|
||||
body: data,
|
||||
};
|
||||
|
||||
req.headers = headers;
|
||||
if (headers) {
|
||||
req.headers = headers;
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
const onSuccess = function (data) {
|
||||
@ -29,7 +24,7 @@ function call(fileserverUrl, method, endpoint, data, headers, transformRequest,
|
||||
return reject(data);
|
||||
};
|
||||
|
||||
axios(req).then(onSuccess, onError);
|
||||
fetch(fileserverUrl + endpoint, req).then(onSuccess, onError);
|
||||
});
|
||||
}
|
||||
|
||||
@ -52,9 +47,7 @@ function upload(fileserverUrl, fileTransferId, chunk, ticket, ticketNonce) {
|
||||
data.append("chunk", chunk);
|
||||
data.append("ticket", ticket);
|
||||
data.append("ticket_nonce", ticketNonce);
|
||||
const headers = {
|
||||
"Content-Type": undefined,
|
||||
};
|
||||
const headers = {};
|
||||
|
||||
return call(fileserverUrl, method, endpoint, data, headers);
|
||||
}
|
||||
@ -78,11 +71,15 @@ function download(fileserverUrl, fileTransferId, ticket, ticketNonce) {
|
||||
ticket_nonce: ticketNonce,
|
||||
};
|
||||
|
||||
const headers = {};
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
|
||||
return call(fileserverUrl, method, endpoint, data, headers, undefined, "arraybuffer").then(
|
||||
function (data) {
|
||||
return data;
|
||||
return call(fileserverUrl, method, endpoint, JSON.stringify(data), headers).then(
|
||||
async function (data) {
|
||||
return {
|
||||
data: await data.arrayBuffer()
|
||||
};
|
||||
},
|
||||
function (data) {
|
||||
if (data.status === 400) {
|
||||
|
@ -2,23 +2,16 @@
|
||||
* Service to talk to the Google Cloud Platform and upload or download files
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import converterService from "./converter";
|
||||
|
||||
function call(signed_url, method, endpoint, data, headers, transformRequest, responseType) {
|
||||
if (!transformRequest) {
|
||||
transformRequest = axios.defaults.transformRequest;
|
||||
}
|
||||
function call(signedUrl, method, endpoint, data, headers) {
|
||||
|
||||
const req = {
|
||||
method: method,
|
||||
url: signed_url + endpoint,
|
||||
data: data,
|
||||
transformRequest: transformRequest,
|
||||
responseType: responseType,
|
||||
body: data,
|
||||
};
|
||||
|
||||
req.headers = headers;
|
||||
if (headers) {
|
||||
req.headers = headers;
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
const onSuccess = function (data) {
|
||||
@ -29,14 +22,14 @@ function call(signed_url, method, endpoint, data, headers, transformRequest, res
|
||||
return reject(data);
|
||||
};
|
||||
|
||||
axios(req).then(onSuccess, onError);
|
||||
fetch(signedUrl + endpoint, req).then(onSuccess, onError);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax PUT request to upload a file chunk to GCP storage
|
||||
*
|
||||
* @param {string} signedUrl The signed ulr
|
||||
* @param {string} signedUrl The signed url
|
||||
* @param {Blob} chunk The content of the chunk to upload
|
||||
*
|
||||
* @returns {Promise} promise
|
||||
@ -55,7 +48,7 @@ function upload(signedUrl, chunk) {
|
||||
/**
|
||||
* Ajax GET request to download a file chunk from GCP storage
|
||||
*
|
||||
* @param {string} signedUrl The signed ulr
|
||||
* @param {string} signedUrl The signed url
|
||||
*
|
||||
* @returns {Promise} promise with the data
|
||||
*/
|
||||
@ -66,14 +59,13 @@ function download(signedUrl) {
|
||||
|
||||
const headers = {};
|
||||
|
||||
return call(signedUrl, method, endpoint, data, headers, undefined, "arraybuffer").then(
|
||||
function (data) {
|
||||
return data;
|
||||
return call(signedUrl, method, endpoint, data, headers).then(
|
||||
async function (data) {
|
||||
return {
|
||||
data: await data.arrayBuffer()
|
||||
};
|
||||
},
|
||||
function (data) {
|
||||
if (data.status === 400) {
|
||||
data.data = JSON.parse(converterService.bytesToString(data.data));
|
||||
}
|
||||
return Promise.reject(data);
|
||||
}
|
||||
);
|
||||
|
@ -1,24 +1,17 @@
|
||||
/**
|
||||
* Service to talk to an S3 compatible storage and upload or download files
|
||||
* Service to talk to the AWS S3 and upload or download files
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import converterService from "./converter";
|
||||
|
||||
function call(signed_url, method, endpoint, data, headers, transformRequest, responseType) {
|
||||
if (!transformRequest) {
|
||||
transformRequest = axios.defaults.transformRequest;
|
||||
}
|
||||
function call(signedUrl, method, endpoint, data, headers) {
|
||||
|
||||
const req = {
|
||||
method: method,
|
||||
url: signed_url + endpoint,
|
||||
data: data,
|
||||
transformRequest: transformRequest,
|
||||
responseType: responseType,
|
||||
body: data,
|
||||
};
|
||||
|
||||
req.headers = headers;
|
||||
if (headers) {
|
||||
req.headers = headers;
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
const onSuccess = function (data) {
|
||||
@ -29,20 +22,20 @@ function call(signed_url, method, endpoint, data, headers, transformRequest, res
|
||||
return reject(data);
|
||||
};
|
||||
|
||||
axios(req).then(onSuccess, onError);
|
||||
fetch(signedUrl + endpoint, req).then(onSuccess, onError);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax PUT request to upload a file chunk to AWS S3
|
||||
*
|
||||
* @param {string} signed_url The signed ulr
|
||||
* @param {string} signedUrl The signed url
|
||||
* @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) {
|
||||
function upload(signedUrl, fields, chunk) {
|
||||
const endpoint = ""; // the signed url already has everything
|
||||
const method = "POST";
|
||||
const data = new FormData();
|
||||
@ -53,35 +46,32 @@ function upload(signed_url, fields, chunk) {
|
||||
data.append(field_name, fields[field_name]);
|
||||
}
|
||||
data.append("file", chunk);
|
||||
const headers = {
|
||||
"Content-Type": undefined,
|
||||
};
|
||||
const headers = {};
|
||||
|
||||
return call(signed_url, method, endpoint, data, headers);
|
||||
return call(signedUrl, method, endpoint, data, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax GET request to download a file chunk from AWS S3
|
||||
*
|
||||
* @param {string} signed_url The signed ulr
|
||||
* @param {string} signedUrl The signed url
|
||||
*
|
||||
* @returns {Promise} promise with the data
|
||||
*/
|
||||
function download(signed_url) {
|
||||
function download(signedUrl) {
|
||||
const endpoint = ""; // the signed url already has everything
|
||||
const method = "GET";
|
||||
const data = null;
|
||||
|
||||
const headers = {};
|
||||
|
||||
return call(signed_url, method, endpoint, data, headers, undefined, "arraybuffer").then(
|
||||
function (data) {
|
||||
return data;
|
||||
return call(signedUrl, method, endpoint, data, headers).then(
|
||||
async function (data) {
|
||||
return {
|
||||
data: await data.arrayBuffer()
|
||||
};
|
||||
},
|
||||
function (data) {
|
||||
if (data.status === 400) {
|
||||
data.data = JSON.parse(converterService.bytesToString(data.data));
|
||||
}
|
||||
return Promise.reject(data);
|
||||
}
|
||||
);
|
||||
|
@ -2,29 +2,32 @@
|
||||
* Service to talk to the psono REST api
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
|
||||
function call(connection_type, endpoint, data, headers) {
|
||||
function call(connection_type, endpoint, body, headers) {
|
||||
const backend = "https://api.pwnedpasswords.com";
|
||||
|
||||
const req = {
|
||||
method: connection_type,
|
||||
url: backend + endpoint,
|
||||
data: data,
|
||||
headers: {
|
||||
...headers
|
||||
}
|
||||
};
|
||||
|
||||
req.headers = headers;
|
||||
if (body != null) {
|
||||
req['body'] = JSON.stringify(body);
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
const onSuccess = function (data) {
|
||||
return resolve(data);
|
||||
const onSuccess = async function (data) {
|
||||
return resolve({
|
||||
data: await data.text(),
|
||||
});
|
||||
};
|
||||
|
||||
const onError = function (data) {
|
||||
return reject(data);
|
||||
};
|
||||
|
||||
axios(req).then(onSuccess, onError);
|
||||
fetch(backend + endpoint, req).then(onSuccess, onError);
|
||||
});
|
||||
}
|
||||
|
||||
@ -38,10 +41,10 @@ function call(connection_type, endpoint, data, headers) {
|
||||
function range(hash_chars) {
|
||||
const endpoint = "/range/" + hash_chars;
|
||||
const connection_type = "GET";
|
||||
const data = null;
|
||||
const headers = null;
|
||||
const body = null;
|
||||
const headers = {};
|
||||
|
||||
return call(connection_type, endpoint, data, headers);
|
||||
return call(connection_type, endpoint, body, headers);
|
||||
}
|
||||
|
||||
const apiPwnedpasswordsService = {
|
||||
|
@ -14,6 +14,8 @@ import cryptoLibrary from "./crypto-library";
|
||||
import HKP from "@openpgp/hkp-client";
|
||||
import * as openpgp from "openpgp";
|
||||
import storage from "./storage";
|
||||
import datastoreSettingService from "./datastore-setting";
|
||||
import {persistStore} from "redux-persist";
|
||||
|
||||
let lastLoginCredentials;
|
||||
let activeTabId;
|
||||
@ -29,6 +31,7 @@ let contextMenuChild1Id;
|
||||
let contextMenuChild2Id;
|
||||
let clearFillPasswordTimeout;
|
||||
|
||||
|
||||
function activate() {
|
||||
browserClient.disableBrowserPasswordSaving();
|
||||
|
||||
@ -96,21 +99,36 @@ function activate() {
|
||||
}
|
||||
|
||||
if (typeof chrome.contextMenus !== "undefined") {
|
||||
contextMenuId = chrome.contextMenus.create({ title: "Psono" });
|
||||
contextMenuId = chrome.contextMenus.create({
|
||||
id: "psono-psono",
|
||||
title: "Psono",
|
||||
});
|
||||
contextMenuChild1Id = chrome.contextMenus.create({
|
||||
id: "psono-datastore",
|
||||
title: i18n.t("OPEN_DATASTORE"),
|
||||
contexts: ["all"],
|
||||
parentId: contextMenuId,
|
||||
onclick: openDatastore,
|
||||
});
|
||||
contextMenuChild2Id = chrome.contextMenus.create({
|
||||
id: "psono-recheck-page",
|
||||
title: i18n.t("RECHECK_PAGE"),
|
||||
contexts: ["all"],
|
||||
parentId: contextMenuId,
|
||||
onclick: recheckPage,
|
||||
});
|
||||
|
||||
chrome.contextMenus.onClicked.addListener((info, tab) => {
|
||||
switch (info.menuItemId) {
|
||||
case "psono-datastore":
|
||||
openDatastore()
|
||||
break;
|
||||
case "psono-recheck-page":
|
||||
recheckPage()
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// create the context menu once the translations are loaded
|
||||
i18n.on("loaded", function (loaded) {
|
||||
updateContextMenu();
|
||||
@ -118,12 +136,12 @@ function activate() {
|
||||
|
||||
// set the correct icon on start
|
||||
if (user.isLoggedIn()) {
|
||||
chrome.browserAction.setIcon({
|
||||
path : "img/icon-32.png"
|
||||
browserClient.setIcon({
|
||||
path : "/data/img/icon-32.png"
|
||||
});
|
||||
} else {
|
||||
chrome.browserAction.setIcon({
|
||||
path : "img/icon-32-disabled.png"
|
||||
browserClient.setIcon({
|
||||
path : "/data/img/icon-32-disabled.png"
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -137,7 +155,6 @@ function updateContextMenu() {
|
||||
title: i18n.t("OPEN_DATASTORE"),
|
||||
contexts: ["all"],
|
||||
parentId: contextMenuId,
|
||||
onclick: openDatastore,
|
||||
});
|
||||
}
|
||||
if (contextMenuChild2Id) {
|
||||
@ -145,7 +162,6 @@ function updateContextMenu() {
|
||||
title: i18n.t("RECHECK_PAGE"),
|
||||
contexts: ["all"],
|
||||
parentId: contextMenuId,
|
||||
onclick: recheckPage,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -207,6 +223,8 @@ function onMessage(request, sender, sendResponse) {
|
||||
"set-offline-cache-encryption-key": setOfflineCacheEncryptionKey,
|
||||
"launch-web-auth-flow-in-background": launchWebAuthFlowInBackground,
|
||||
"language-changed": languageChanged,
|
||||
"get-offline-cache-encryption-key-offscreen": () => {}, // dummy as these are handled offscreen
|
||||
"set-offline-cache-encryption-key-offscreen": () => {}, // dummy as these are handled offscreen
|
||||
};
|
||||
|
||||
if (eventFunctions.hasOwnProperty(request.event)) {
|
||||
@ -314,8 +332,8 @@ function savePasswordActiveTab(request, sender, sendResponse) {
|
||||
// don't do anything
|
||||
});
|
||||
}, 500); // delay 500 ms to give the storage a chance to be stored
|
||||
browserClient.openTab(
|
||||
"index.html#!/datastore/edit/" + datastore_object.type + "/" + datastore_object.secret_id
|
||||
browserClient.openTabBg(
|
||||
"/data/index.html#!/datastore/edit/" + datastore_object.type + "/" + datastore_object.secret_id
|
||||
);
|
||||
};
|
||||
|
||||
@ -347,8 +365,8 @@ function bookmarkActiveTab(request, sender, sendResponse) {
|
||||
});
|
||||
}, 500); // delay 500 ms to give the storage a chance to be stored
|
||||
|
||||
browserClient.openTab(
|
||||
"index.html#!/datastore/edit/" + datastore_object.type + "/" + datastore_object.secret_id
|
||||
browserClient.openTabBg(
|
||||
"/data/index.html#!/datastore/edit/" + datastore_object.type + "/" + datastore_object.secret_id
|
||||
);
|
||||
};
|
||||
datastorePasswordService.bookmarkActiveTab().then(onSuccess, onError);
|
||||
@ -374,8 +392,8 @@ function onLogout(request, sender, sendResponse) {
|
||||
|
||||
chrome.tabs.remove(tabids);
|
||||
});
|
||||
chrome.browserAction.setIcon({
|
||||
path : "img/icon-32-disabled.png"
|
||||
browserClient.setIcon({
|
||||
path : "/data/img/icon-32-disabled.png"
|
||||
});
|
||||
}
|
||||
|
||||
@ -411,8 +429,8 @@ function onStorageReload(request, sender, sendResponse) {
|
||||
*/
|
||||
function onLogin(request, sender, sendResponse) {
|
||||
// pass
|
||||
chrome.browserAction.setIcon({
|
||||
path : "img/icon-32.png"
|
||||
browserClient.setIcon({
|
||||
path : "/data/img/icon-32.png"
|
||||
});
|
||||
}
|
||||
|
||||
@ -539,6 +557,7 @@ function onRequestSecret(request, sender, sendResponse) {
|
||||
sendResponse({ event: "return-secret", data: data });
|
||||
},
|
||||
function (value) {
|
||||
console.log(value);
|
||||
// failed
|
||||
sendResponse({ event: "return-secret", data: "fail" });
|
||||
}
|
||||
@ -580,8 +599,8 @@ function onGeneratePassword(request, sender, sendResponse) {
|
||||
// don't do anything
|
||||
});
|
||||
}, 500); // delay 500 ms to give the storage a chance to be stored
|
||||
browserClient.openTab(
|
||||
"index.html#!/datastore/edit/" + datastore_object.type + "/" + datastore_object.secret_id
|
||||
browserClient.openTabBg(
|
||||
"/data/index.html#!/datastore/edit/" + datastore_object.type + "/" + datastore_object.secret_id
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -2,11 +2,11 @@
|
||||
* The browser interface, responsible for the cross browser / platform compatibility.
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import helperService from "./helper";
|
||||
import store from "./store";
|
||||
import deviceService from "./device";
|
||||
|
||||
|
||||
const registrations = {};
|
||||
let config = {};
|
||||
const events = ["login", "logout"];
|
||||
@ -320,17 +320,9 @@ function getBaseUrl() {
|
||||
*
|
||||
* @returns {Promise} promise
|
||||
*/
|
||||
function loadVersion() {
|
||||
return axios({
|
||||
method: "get",
|
||||
url: "VERSION.txt",
|
||||
})
|
||||
.then((result) => {
|
||||
return result.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
async function loadVersion() {
|
||||
const response = await fetch("VERSION.txt");
|
||||
return await response.text();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -453,9 +445,9 @@ function loadConfig() {
|
||||
}
|
||||
|
||||
if (remoteConfigJson === null) {
|
||||
return axios.get("config.json").then((response) => {
|
||||
onSuccess(response);
|
||||
});
|
||||
fetch("config.json").then(async (response) => {
|
||||
return onSuccess({ data: await response.json() });
|
||||
})
|
||||
} else {
|
||||
return onSuccess({ data: remoteConfigJson });
|
||||
}
|
||||
@ -789,7 +781,7 @@ function notify(content) {
|
||||
type: "basic",
|
||||
title: content,
|
||||
message: "",
|
||||
iconUrl: "img/icon-32.png",
|
||||
iconUrl: "/data/img/icon-32.png",
|
||||
});
|
||||
} else if (TARGET === "chrome") {
|
||||
chrome.notifications.getPermissionLevel(function (permissionLevel) {
|
||||
@ -802,7 +794,7 @@ function notify(content) {
|
||||
type: "basic",
|
||||
title: content,
|
||||
message: "",
|
||||
iconUrl: "img/icon-32.png",
|
||||
iconUrl: "/data/img/icon-32.png",
|
||||
});
|
||||
});
|
||||
} else if (TARGET === "electron") {
|
||||
@ -853,19 +845,15 @@ function notify(content) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the background page for the offline cache encryption key
|
||||
* Sets an icon
|
||||
*
|
||||
* @param {function} fnc The callback function
|
||||
* @param {object} icon The callback function
|
||||
*/
|
||||
function getOfflineCacheEncryptionKey(fnc) {
|
||||
function setIcon(icon) {
|
||||
if (TARGET === "firefox") {
|
||||
browser.runtime.getBackgroundPage().then(function (bg) {
|
||||
fnc(bg.psono_offline_cache_encryption_key);
|
||||
});
|
||||
chrome.browserAction.setIcon(icon)
|
||||
} else if (TARGET === "chrome") {
|
||||
chrome.runtime.getBackgroundPage(function (bg) {
|
||||
fnc(bg.psono_offline_cache_encryption_key);
|
||||
});
|
||||
chrome.action.setIcon(icon)
|
||||
} else {
|
||||
//pass, no background page on the website
|
||||
}
|
||||
@ -898,7 +886,7 @@ const browserClientService = {
|
||||
disableBrowserPasswordSaving: disableBrowserPasswordSaving,
|
||||
copyToClipboard: copyToClipboard,
|
||||
notify: notify,
|
||||
getOfflineCacheEncryptionKey: getOfflineCacheEncryptionKey,
|
||||
setIcon: setIcon,
|
||||
};
|
||||
|
||||
export default browserClientService;
|
||||
|
@ -28,6 +28,7 @@ function InvalidRecoveryCodeException(message) {
|
||||
*/
|
||||
function randomBytes(count) {
|
||||
let bs;
|
||||
|
||||
if (typeof module !== "undefined" && module.exports) {
|
||||
// add node.js implementations
|
||||
const crypto = require("crypto");
|
||||
@ -37,6 +38,11 @@ function randomBytes(count) {
|
||||
bs[i] = buf[i];
|
||||
}
|
||||
return bs;
|
||||
} else if (typeof window === "undefined") {
|
||||
// manifest v3 background script without access to window
|
||||
bs = new Uint8Array(count);
|
||||
crypto.getRandomValues(bs);
|
||||
return bs;
|
||||
} else if (window && window.crypto && window.crypto.getRandomValues) {
|
||||
// add in-browser implementation
|
||||
bs = new Uint8Array(count);
|
||||
@ -60,7 +66,12 @@ function randomBytes(count) {
|
||||
function random() {
|
||||
let bs;
|
||||
let byte;
|
||||
if (window && window.crypto && window.crypto.getRandomValues) {
|
||||
if (typeof window === "undefined") {
|
||||
// manifest v3 background script without access to window
|
||||
bs = new Uint32Array(1);
|
||||
crypto.getRandomValues(bs);
|
||||
byte = bs[0];
|
||||
} else if (window && window.crypto && window.crypto.getRandomValues) {
|
||||
// add in-browser implementation
|
||||
bs = new Uint32Array(1);
|
||||
window.crypto.getRandomValues(bs);
|
||||
|
@ -8,7 +8,17 @@ import action from "../actions/bound-action-creators";
|
||||
import store from "./store";
|
||||
|
||||
let fingerprint;
|
||||
const clientJs = new ClientJS();
|
||||
let clientJs;
|
||||
|
||||
activate();
|
||||
|
||||
function activate() {
|
||||
if (typeof window === "undefined") {
|
||||
//we are in a background script and don't have a window
|
||||
return;
|
||||
}
|
||||
clientJs = new ClientJS();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the device fingerprint
|
||||
@ -60,7 +70,7 @@ function isWebclient() {
|
||||
* @returns {boolean} Is this a windows device
|
||||
*/
|
||||
function isWindows() {
|
||||
return clientJs.isWindows()
|
||||
return clientJs && clientJs.isWindows()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,7 +79,7 @@ function isWindows() {
|
||||
* @returns {boolean} Is this a mac device
|
||||
*/
|
||||
function isMac() {
|
||||
return clientJs.isMac()
|
||||
return clientJs && clientJs.isMac()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,7 +88,7 @@ function isMac() {
|
||||
* @returns {boolean} Is this a linux device
|
||||
*/
|
||||
function isLinux() {
|
||||
return clientJs.isLinux()
|
||||
return clientJs && clientJs.isLinux()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,7 +97,7 @@ function isLinux() {
|
||||
* @returns {boolean} Is this an android device
|
||||
*/
|
||||
function isMobileAndroid() {
|
||||
return clientJs.isMobileAndroid();
|
||||
return clientJs && clientJs.isMobileAndroid();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,7 +106,7 @@ function isMobileAndroid() {
|
||||
* @returns {boolean} Is this an ios device
|
||||
*/
|
||||
function isMobileIos() {
|
||||
return clientJs.isMobileIOS();
|
||||
return clientJs && clientJs.isMobileIOS();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,7 +115,7 @@ function isMobileIos() {
|
||||
* @returns {boolean} Is this an ios device
|
||||
*/
|
||||
function isMobile() {
|
||||
return clientJs.isMobile();
|
||||
return clientJs && clientJs.isMobile();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,7 +124,7 @@ function isMobile() {
|
||||
* @returns {boolean} Is this an Chrome user
|
||||
*/
|
||||
function isChrome() {
|
||||
return clientJs.isChrome();
|
||||
return clientJs && clientJs.isChrome();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,7 +133,7 @@ function isChrome() {
|
||||
* @returns {boolean} Is this an Chrome user
|
||||
*/
|
||||
function isSafari() {
|
||||
return clientJs.isSafari();
|
||||
return clientJs && clientJs.isSafari();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -132,7 +142,7 @@ function isSafari() {
|
||||
* @returns {boolean} Is this an Firefox user
|
||||
*/
|
||||
function isFirefox() {
|
||||
return clientJs.isFirefox();
|
||||
return clientJs && clientJs.isFirefox();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,22 +152,22 @@ function isFirefox() {
|
||||
*/
|
||||
function getDeviceDescription() {
|
||||
let description = "";
|
||||
if (typeof clientJs.getDeviceVendor() !== "undefined") {
|
||||
if (clientJs && typeof clientJs.getDeviceVendor() !== "undefined") {
|
||||
description = description + clientJs.getDeviceVendor() + " ";
|
||||
}
|
||||
if (typeof clientJs.getDevice() !== "undefined") {
|
||||
if (clientJs && typeof clientJs.getDevice() !== "undefined") {
|
||||
description = description + clientJs.getDevice() + " ";
|
||||
}
|
||||
if (typeof clientJs.getOS() !== "undefined") {
|
||||
if (clientJs && typeof clientJs.getOS() !== "undefined") {
|
||||
description = description + clientJs.getOS() + " ";
|
||||
}
|
||||
if (typeof clientJs.getOSVersion() !== "undefined") {
|
||||
if (clientJs && typeof clientJs.getOSVersion() !== "undefined") {
|
||||
description = description + clientJs.getOSVersion() + " ";
|
||||
}
|
||||
if (typeof clientJs.getBrowser() !== "undefined") {
|
||||
if (clientJs && typeof clientJs.getBrowser() !== "undefined") {
|
||||
description = description + clientJs.getBrowser() + " ";
|
||||
}
|
||||
if (typeof clientJs.getBrowserVersion() !== "undefined") {
|
||||
if (clientJs && typeof clientJs.getBrowserVersion() !== "undefined") {
|
||||
description = description + clientJs.getBrowserVersion() + " ";
|
||||
}
|
||||
return description;
|
||||
|
@ -16,6 +16,7 @@ import apiDO from "./api-aws";
|
||||
import apiFileserver from "./api-fileserver";
|
||||
import store from "./store";
|
||||
import storage from "./storage";
|
||||
import offlineCache from "./offline-cache";
|
||||
|
||||
const registrations = {};
|
||||
|
||||
@ -508,7 +509,7 @@ function onItemClick(item) {
|
||||
readShards().then(function (data) {
|
||||
storage.upsert("file-downloads", { key: "shards", shards: data });
|
||||
browserClient.openTab("download-file.html#!/file/download/" + item.id).then(function (window) {
|
||||
//window.psono_offline_cache_encryption_key = offlineCache.get_encryption_key();
|
||||
window.psono_offline_cache_encryption_key = offlineCache.getEncryptionKey();
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -722,7 +723,7 @@ function downloadFileFromShard(file, shards, fileTransfer) {
|
||||
downloadFileFromShardHelper(data.shards);
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.log(data);
|
||||
console.log(err);
|
||||
return Promise.reject({
|
||||
non_field_errors: ["NO_FILESERVER_AVAILABLE"],
|
||||
});
|
||||
|
@ -2,7 +2,6 @@
|
||||
* Service to manage the host
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import store from "./store";
|
||||
import cryptoLibrary from "./crypto-library";
|
||||
import helperService from "./helper";
|
||||
@ -157,29 +156,26 @@ function checkHost(server, preApprovedVerifyKey) {
|
||||
* @returns {Promise} Result of the check
|
||||
*/
|
||||
function loadRemoteConfig(web_client_url, server_url) {
|
||||
const req = {
|
||||
method: "GET",
|
||||
url: web_client_url + "/config.json",
|
||||
};
|
||||
|
||||
const onSuccess = function (config) {
|
||||
const onSuccess = async function (data) {
|
||||
const config = await data.json();
|
||||
// we need to preserve the base_url and the backend server as they are optional and the original web
|
||||
// client would create them dynamically
|
||||
if (!config.data.hasOwnProperty("base_url")) {
|
||||
config.data["base_url"] = web_client_url;
|
||||
if (!config.hasOwnProperty("base_url")) {
|
||||
config["base_url"] = web_client_url;
|
||||
}
|
||||
|
||||
if (config.data.hasOwnProperty("backend_servers")) {
|
||||
for (let i = 0; i < config.data["backend_servers"].length; i++) {
|
||||
if (config.data["backend_servers"][i].hasOwnProperty("url")) {
|
||||
if (config.hasOwnProperty("backend_servers")) {
|
||||
for (let i = 0; i < config["backend_servers"].length; i++) {
|
||||
if (config["backend_servers"][i].hasOwnProperty("url")) {
|
||||
continue;
|
||||
}
|
||||
config.data["backend_servers"][i]["url"] = server_url;
|
||||
config["backend_servers"][i]["url"] = server_url;
|
||||
}
|
||||
}
|
||||
|
||||
// we store the loaded configuration
|
||||
action.setRemoteConfigJson(config.data);
|
||||
action.setRemoteConfigJson(config);
|
||||
action.setUserUsername("");
|
||||
action.setServerUrl("");
|
||||
browserClient.clearConfigCache();
|
||||
@ -190,7 +186,7 @@ function loadRemoteConfig(web_client_url, server_url) {
|
||||
return Promise.reject(data);
|
||||
};
|
||||
|
||||
return axios(req).then(onSuccess, onError);
|
||||
return fetch(web_client_url + "/config.json").then(onSuccess, onError);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,9 +1,10 @@
|
||||
/**
|
||||
* Service to talk to the psono REST api
|
||||
* Service to handle the offline cache
|
||||
*/
|
||||
|
||||
import cryptoLibrary from "./crypto-library";
|
||||
import browserClient from "./browser-client";
|
||||
import offscreenDocument from "./offscreen-document";
|
||||
import storage from "./storage";
|
||||
import action from "../actions/bound-action-creators";
|
||||
import store from "./store";
|
||||
@ -14,14 +15,12 @@ const onSetEncryptionKeyRegistrations = [];
|
||||
activate();
|
||||
|
||||
function activate() {
|
||||
if (window.psono_offline_cache_encryption_key) {
|
||||
if (typeof window !== "undefined" && window.psono_offline_cache_encryption_key) {
|
||||
setEncryptionKey(window.psono_offline_cache_encryption_key);
|
||||
}
|
||||
if (browserClient) {
|
||||
browserClient.getOfflineCacheEncryptionKey(function (new_encryption_key) {
|
||||
setEncryptionKey(new_encryption_key);
|
||||
});
|
||||
}
|
||||
offscreenDocument.getOfflineCacheEncryptionKey(function (newEncryptionKey) {
|
||||
setEncryptionKey(newEncryptionKey);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,7 +106,12 @@ function setEncryptionKey(newEncryptionKey) {
|
||||
return;
|
||||
}
|
||||
encryptionKey = newEncryptionKey;
|
||||
window.psono_offline_cache_encryption_key = newEncryptionKey;
|
||||
if (typeof window !== "undefined") {
|
||||
window.psono_offline_cache_encryption_key = newEncryptionKey;
|
||||
} else {
|
||||
// we are in a chrome extension in background service worker, so we store it in offscreen document
|
||||
offscreenDocument.setOfflineCacheEncryptionKey(newEncryptionKey);
|
||||
}
|
||||
for (let i = 0; i < onSetEncryptionKeyRegistrations.length; i++) {
|
||||
onSetEncryptionKeyRegistrations[i]();
|
||||
}
|
||||
@ -134,13 +138,14 @@ function setEncryptionPassword(password) {
|
||||
/**
|
||||
* Sets the request data in cache
|
||||
*
|
||||
* @param {object} request the request
|
||||
* @param {string} url the url of the request
|
||||
* @param {string} method the request method
|
||||
* @param {object} data the data
|
||||
*
|
||||
* @returns {Promise} promise
|
||||
*/
|
||||
function set(request, data) {
|
||||
if (request.method !== "GET" || !isActive()) {
|
||||
function set(url, method, data) {
|
||||
if (method !== "GET" || !isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -150,21 +155,22 @@ function set(request, data) {
|
||||
value = cryptoLibrary.encryptData(value, encryptionKey);
|
||||
}
|
||||
|
||||
storage.upsert("offline-cache", { key: request.url.toLowerCase(), value: value });
|
||||
storage.upsert("offline-cache", { key: url.toLowerCase(), value: value });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cached request
|
||||
*
|
||||
* @param {object} request the request
|
||||
* @param {string} url the request url
|
||||
* @param {string} method the request method
|
||||
*
|
||||
* @returns {Promise} our original request
|
||||
*/
|
||||
function get(request) {
|
||||
function get(url, method) {
|
||||
if (!isActive()) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
if (request.method !== "GET") {
|
||||
if (method !== "GET") {
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
error: ["Leave the offline mode before creating / modifying any content."],
|
||||
@ -172,7 +178,7 @@ function get(request) {
|
||||
});
|
||||
}
|
||||
|
||||
return storage.findKey("offline-cache", request.url.toLowerCase()).then((storageEntry) => {
|
||||
return storage.findKey("offline-cache", url.toLowerCase()).then((storageEntry) => {
|
||||
if (storageEntry === null) {
|
||||
return null;
|
||||
}
|
||||
|
87
src/js/services/offscreen-document.js
Normal file
87
src/js/services/offscreen-document.js
Normal file
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* The browser interface, responsible for the cross browser / platform compatibility.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks whether an offscreen document exists or not
|
||||
*/
|
||||
async function hasOffscreenDocument(offscreenDocumentPath) {
|
||||
if (TARGET === "chrome" && typeof clients !== "undefined") {
|
||||
const offscreenUrl = chrome.runtime.getURL(offscreenDocumentPath);
|
||||
const matchedClients = await clients.matchAll();
|
||||
for (const client of matchedClients) {
|
||||
if (client.url === offscreenUrl) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
//pass, only availble in chrome extensions
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and offscreen document if it doesn't exist yet
|
||||
*/
|
||||
async function createOffscreenDocument() {
|
||||
if (TARGET === "chrome") {
|
||||
const offscreenDocumentPath = 'data/offscreen.html'
|
||||
if (!(await hasOffscreenDocument(offscreenDocumentPath))) {
|
||||
try {
|
||||
await chrome.offscreen.createDocument({
|
||||
url: chrome.runtime.getURL(offscreenDocumentPath),
|
||||
reasons: ['USER_MEDIA'], // tried LOCAL_STORAGE but it's not accepted due to Error at property 'reasons': Error at index 0: Value must be one of AUDIO_PLAYBACK, BLOBS, CLIPBOARD, DISPLAY_MEDIA, DOM_PARSER, DOM_SCRAPING, IFRAME_SCRIPTING, TESTING, USER_MEDIA, WEB_RTC.
|
||||
justification: 'Isolated in-memory storage of the offline cache secret that is wiped when the browser closes.',
|
||||
});
|
||||
} catch(e) {
|
||||
// hasOffscreenDocument doesn't work even so its the documented way to check whether an offscreen page
|
||||
// already exists. https://groups.google.com/a/chromium.org/g/chromium-extensions/c/D5Jg2ukyvUc/m/VaSvEfoHAgAJ
|
||||
// the problem is that clients.matchAll() doesn't include the offscreen page...
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//pass, can only create offscreen documents in chrome extensions
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the offscreen page for the offline cache encryption key
|
||||
*
|
||||
* @param {function} fnc The callback function
|
||||
*/
|
||||
async function getOfflineCacheEncryptionKey(fnc) {
|
||||
if (TARGET === "firefox") {
|
||||
browser.runtime.getBackgroundPage().then(function (bg) {
|
||||
fnc(bg.psono_offline_cache_encryption_key);
|
||||
});
|
||||
} else if (TARGET === "chrome") {
|
||||
await createOffscreenDocument()
|
||||
chrome.runtime.sendMessage({ event: 'get-offline-cache-encryption-key-offscreen', data: null }, fnc);
|
||||
// chrome.runtime.getBackgroundPage(function (bg) {
|
||||
// fnc(bg.psono_offline_cache_encryption_key);
|
||||
// });
|
||||
} else {
|
||||
//pass, no background page on the website
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the offline cache encryption key to the offscreen page for storage
|
||||
*
|
||||
* @param {string} offlineCacheEncryptionKey The new offline cache encryption eky
|
||||
*/
|
||||
async function setOfflineCacheEncryptionKey(offlineCacheEncryptionKey) {
|
||||
if (TARGET === "chrome") {
|
||||
await createOffscreenDocument()
|
||||
chrome.runtime.sendMessage({ event: 'set-offline-cache-encryption-key-offscreen', data: offlineCacheEncryptionKey });
|
||||
} else {
|
||||
//pass, no background page on the website
|
||||
}
|
||||
}
|
||||
|
||||
const browserClientService = {
|
||||
getOfflineCacheEncryptionKey: getOfflineCacheEncryptionKey,
|
||||
setOfflineCacheEncryptionKey: setOfflineCacheEncryptionKey,
|
||||
};
|
||||
|
||||
export default browserClientService;
|
@ -6,62 +6,10 @@ import localforage from "localforage";
|
||||
|
||||
const registrations = {};
|
||||
|
||||
//const loki_storage = new loki("password_manager_local_storage");
|
||||
const dbs = [];
|
||||
|
||||
// const db_config = {
|
||||
// 'config': {
|
||||
// name: 'config',
|
||||
// indices: ['key'],
|
||||
// uniques: ['key']
|
||||
// },
|
||||
// 'persistent': {
|
||||
// name: 'persistent',
|
||||
// indices: ['key'],
|
||||
// uniques: ['key']
|
||||
// },
|
||||
// 'settings': {
|
||||
// name: 'settings',
|
||||
// indices: ['key'],
|
||||
// uniques: ['key']
|
||||
// },
|
||||
// 'offline-cache': {
|
||||
// name: 'offline-cache',
|
||||
// indices: ['key'],
|
||||
// uniques: ['key']
|
||||
// },
|
||||
// 'datastore-password-leafs': {
|
||||
// name: 'datastore-password-leafs',
|
||||
// indices: ['key', 'urlfilter', 'name'],
|
||||
// uniques: ['key']
|
||||
// },
|
||||
// 'datastore-file-leafs': {
|
||||
// name: 'datastore-file-leafs',
|
||||
// indices: ['key'],
|
||||
// uniques: ['key']
|
||||
// },
|
||||
// 'datastore-user-leafs': {
|
||||
// name: 'datastore-user-leafs',
|
||||
// indices: ['key', 'filter', 'name'],
|
||||
// uniques: ['key'],
|
||||
// subscribers: {
|
||||
// update: {
|
||||
// current: 0,
|
||||
// max: 1
|
||||
// },
|
||||
// insert: {
|
||||
// current: 0,
|
||||
// max: 1
|
||||
// },
|
||||
// delete: {
|
||||
// current: 0,
|
||||
// max: 1
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
const dbConfig = {
|
||||
"state": localforage.createInstance({
|
||||
name: "state",
|
||||
}),
|
||||
"file-downloads": localforage.createInstance({
|
||||
name: "datastore-password-leafs",
|
||||
}),
|
||||
@ -95,7 +43,6 @@ function activate() {
|
||||
* @param {object|Array} items One or multiple items to put into the database
|
||||
*/
|
||||
function insert(db, items) {
|
||||
//return dbs[db].insert(items);
|
||||
dbConfig[db].setItem(items["key"], items["value"]);
|
||||
}
|
||||
|
||||
@ -168,7 +115,6 @@ function where(db, filterFunction) {
|
||||
*/
|
||||
function findKey(db, key) {
|
||||
return dbConfig[db].getItem(key);
|
||||
//return dbs[db].findOne({key: key});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,11 +140,24 @@ function removeAll(db) {
|
||||
if (!dbConfig.hasOwnProperty(dbName)) {
|
||||
continue;
|
||||
}
|
||||
if (dbName === "state") {
|
||||
//state contains data that potentially be persisted
|
||||
continue;
|
||||
}
|
||||
dbConfig[dbName].clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* removes all objects in all dbs (excluding the persistent one) or only in the specified one
|
||||
*
|
||||
* @param {string} [db] (optional) The database
|
||||
*/
|
||||
function get(db) {
|
||||
return dbConfig[db]
|
||||
}
|
||||
|
||||
/**
|
||||
* setups an event listener on an event
|
||||
*
|
||||
@ -288,6 +247,7 @@ const storageService = {
|
||||
findKey: findKey,
|
||||
remove: remove,
|
||||
removeAll: removeAll,
|
||||
get: get,
|
||||
on: on,
|
||||
save: save,
|
||||
reload: reload,
|
||||
|
@ -6,13 +6,13 @@ import { createStore, applyMiddleware } from "redux";
|
||||
import thunkMiddleware from "redux-thunk";
|
||||
import { createLogger } from "redux-logger";
|
||||
import { persistReducer, createMigrate } from "redux-persist";
|
||||
import storage from "redux-persist/lib/storage";
|
||||
import { createStateSyncMiddleware, initMessageListener } from "redux-state-sync";
|
||||
import {
|
||||
SET_REQUESTS_IN_PROGRESS,
|
||||
} from "../actions/action-types";
|
||||
|
||||
import rootReducer from "../reducers";
|
||||
import storageService from "./storage";
|
||||
|
||||
const config = {
|
||||
channel: "redux_state_sync",
|
||||
@ -21,7 +21,7 @@ const config = {
|
||||
|
||||
const middlewares = [thunkMiddleware, createStateSyncMiddleware(config)];
|
||||
|
||||
if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
|
||||
if (true || !process.env.NODE_ENV || process.env.NODE_ENV === "development") {
|
||||
const loggerMiddleware = createLogger({
|
||||
predicate: (getState, action) => action.type !== SET_REQUESTS_IN_PROGRESS
|
||||
});
|
||||
@ -75,7 +75,7 @@ const persistConfig = {
|
||||
key: "client",
|
||||
blacklist: ['transient'],
|
||||
version: 2,
|
||||
storage,
|
||||
storage: storageService.get('state'),
|
||||
debug: false,
|
||||
migrate: createMigrate(migrations, { debug: false }),
|
||||
};
|
||||
|
@ -1,36 +0,0 @@
|
||||
|
||||
import axios from 'axios'
|
||||
import action from "../actions/bound-action-creators";
|
||||
|
||||
export function initRequestProgressBar () {
|
||||
|
||||
let requestCounterOpen = 0;
|
||||
let requestCounterClosed = 0;
|
||||
|
||||
axios.interceptors.request.use(function (config) {
|
||||
requestCounterOpen++;
|
||||
action.setRequestsInProgress(requestCounterOpen, requestCounterClosed);
|
||||
return config;
|
||||
}, function (error) {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
// Add a response interceptor
|
||||
axios.interceptors.response.use(function (response) {
|
||||
requestCounterClosed++;
|
||||
if (requestCounterOpen === requestCounterClosed) {
|
||||
requestCounterOpen = 0;
|
||||
requestCounterClosed = 0;
|
||||
}
|
||||
action.setRequestsInProgress(requestCounterOpen, requestCounterClosed);
|
||||
return response;
|
||||
}, function (error) {
|
||||
requestCounterClosed++;
|
||||
if (requestCounterOpen === requestCounterClosed) {
|
||||
requestCounterOpen = 0;
|
||||
requestCounterClosed = 0;
|
||||
}
|
||||
action.setRequestsInProgress(requestCounterOpen, requestCounterClosed);
|
||||
return Promise.reject(error);
|
||||
});
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import axios from "axios";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Container from "@material-ui/core/Container";
|
||||
import Button from "@material-ui/core/Button";
|
||||
@ -33,12 +32,9 @@ const PrivacyPolicyView = (props) => {
|
||||
const [privacyPolicy, setPrivacyPolicy] = useState("");
|
||||
|
||||
React.useEffect(() => {
|
||||
axios({
|
||||
method: "get",
|
||||
url: "privacy-policy-content.html",
|
||||
})
|
||||
.then((result) => {
|
||||
setPrivacyPolicy(result.data);
|
||||
fetch("privacy-policy-content.html")
|
||||
.then(async (result) => {
|
||||
setPrivacyPolicy(await result.text());
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
|
@ -249,7 +249,7 @@ const SecurityReportView = (props) => {
|
||||
],
|
||||
});
|
||||
setPasswordDuplicateData({
|
||||
labels: [t("Duplicate"), t("Unique")],
|
||||
labels: [t("DUPLICATES"), t("UNIQUE")],
|
||||
datasets: [
|
||||
{
|
||||
label: t("DUPLICATES"),
|
||||
|
@ -23,6 +23,7 @@ module.exports = merge(common, {
|
||||
entry: {
|
||||
'chrome/data/js/bundle.min.js': './src/js/index.js',
|
||||
'chrome/data/js/crypto-worker.js': './src/js/crypto-worker.js',
|
||||
'chrome/data/js/background-chrome.js': './src/js/background-chrome.js',
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
|
Loading…
x
Reference in New Issue
Block a user