1
0
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:
Sascha Pfeiffer 2023-11-20 08:39:14 +01:00
parent 1b0c426a20
commit 889ac2786f
36 changed files with 552 additions and 610 deletions

View File

@ -7,8 +7,5 @@ module.exports = async () => {
"globals": {
"TARGET": "webclient"
},
"moduleNameMapper": {
'^axios$': require.resolve('axios'),
},
};
};

70
package-lock.json generated
View File

@ -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

View File

@ -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",

View 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
}

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<script src="js/offscreen.js"></script>

View File

@ -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"
}

View File

@ -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)",

View File

@ -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)",

View 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);
});

View File

@ -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();
});

View File

@ -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;

View File

@ -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: {

View File

@ -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>

View File

@ -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);
}
);

View File

@ -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);
}
);

View File

@ -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);
}
);

View File

@ -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);
}
});
});
}

View File

@ -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;

View File

@ -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) {

View File

@ -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);
}
);

View File

@ -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);
}
);

View File

@ -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 = {

View File

@ -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
);
};

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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"],
});

View File

@ -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);
}
/**

View File

@ -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;
}

View 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;

View File

@ -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,

View File

@ -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 }),
};

View File

@ -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);
});
}

View File

@ -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);

View File

@ -249,7 +249,7 @@ const SecurityReportView = (props) => {
],
});
setPasswordDuplicateData({
labels: [t("Duplicate"), t("Unique")],
labels: [t("DUPLICATES"), t("UNIQUE")],
datasets: [
{
label: t("DUPLICATES"),

View File

@ -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({