diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md similarity index 71% rename from .github/ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE/BUG_REPORT.md index cd5c86e0..89ea5f6f 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md @@ -1,8 +1,10 @@ -### This issue is a +--- +name: πŸ› Bug Report +about: If something isn't working as expected πŸ€”. -bug / feature / question +--- - +## Bug report ### Steps to reproduce - current behaviour @@ -33,13 +35,13 @@ bug / feature / question **Operating System:** -**Browser:** +**Browser vendor and version:** -**Storage:** +**Storage Provider:** \ No newline at end of file +--> diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md new file mode 100644 index 00000000..3a3ab676 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md @@ -0,0 +1,17 @@ +--- +name: πŸš€ Feature Request +about: I have a suggestion + +--- + +## Feature Request + +### A feature you'd like to see + + +### Alternatives you've considered + + +### Additional remarks + diff --git a/.github/ISSUE_TEMPLATE/OTHER_ISSUE.md b/.github/ISSUE_TEMPLATE/OTHER_ISSUE.md new file mode 100644 index 00000000..022872f0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/OTHER_ISSUE.md @@ -0,0 +1,6 @@ +--- +name: πŸ€” Other +about: My issue doesn't fit other types. + +--- + diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..e88bd52a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.insertSpaces": false, + "editor.tabSize": 2, + "editor.detectIndentation": false, + "vetur.format.defaultFormatter.js": "vscode-typescript", + "vetur.format.defaultFormatter.html": "none", +} \ No newline at end of file diff --git a/README.md b/README.md index 8ca26fc0..624808c6 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,8 @@ yarn build # this step generates the zip archives submitted to Chrome/Firefox addon marketplaces. yarn bundle -# build the tests -yarn build-tests - # static reload with file watch for tests -yarn watch-tests +yarn dev-tests ``` For detailed explanation on how things work, consult the [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/chrome.manifest.json b/chrome.manifest.json index c20c8232..b0d2a328 100644 --- a/chrome.manifest.json +++ b/chrome.manifest.json @@ -1,7 +1,7 @@ { "name": "KeePass Tusk - Password Access and Autofill", "short_name": "KeePass Tusk", - "version": "2018.9.16", + "version": "2018.9.27", "manifest_version": 2, "minimum_chrome_version": "48", "description": "Readonly KeePass password database integration for Chromeβ„’", diff --git a/firefox.manifest.json b/firefox.manifest.json index 29e5523f..6573d526 100644 --- a/firefox.manifest.json +++ b/firefox.manifest.json @@ -1,7 +1,7 @@ { "name": "KeePass Tusk - Password Access and Autofill", "short_name": "KeePass Tusk", - "version": "2018.9.16", + "version": "2018.9.27", "manifest_version": 2, "minimum_chrome_version": "48", "description": "Readonly KeePass password database integration for Firefox", diff --git a/services/dropboxFileManager.js b/services/dropboxFileManager.js index 9cd608cc..a1719c94 100644 --- a/services/dropboxFileManager.js +++ b/services/dropboxFileManager.js @@ -28,7 +28,7 @@ function DropboxFileManager(settings) { chooseDescription: 'Access password files stored on Dropbox. Files will be retrieved from Dropbox each time they are used.', }; - oauth.searchRequestFunction = function(token) { + oauth.searchRequestFunction = function (token) { return axios({ method: 'post', url: 'https://api.dropbox.com/2/files/search', @@ -45,8 +45,8 @@ function DropboxFileManager(settings) { }) } - oauth.searchRequestHandler = function(response) { - return response.data.matches.map(function(fileInfo) { + oauth.searchRequestHandler = function (response) { + return response.data.matches.map(function (fileInfo) { return { title: fileInfo.metadata.path_display }; @@ -54,18 +54,18 @@ function DropboxFileManager(settings) { } //get the minimum information needed to identify this file for future retrieval - oauth.getDatabaseChoiceData = function(dbInfo) { + oauth.getDatabaseChoiceData = function (dbInfo) { return { title: dbInfo.title } } //given minimal file information, retrieve the actual file - oauth.fileRequestFunction = function(dbInfo, token) { + oauth.fileRequestFunction = function (dbInfo, token) { function http_header_safe_json(v) { var charsToEncode = /[\u007f-\uffff]/g; return JSON.stringify(v).replace(charsToEncode, - function(c) { + function (c) { return '\\u' + ('000' + c.charCodeAt(0).toString(16)).slice(-4); } ); @@ -84,11 +84,11 @@ function DropboxFileManager(settings) { }) } - oauth.revokeAuth = function() { + oauth.revokeAuth = function () { return Promise.resolve() } - oauth.handleAuthRedirectURI = function(redirect_url, randomState, resolve, reject) { + oauth.handleAuthRedirectURI = function (redirect_url, randomState, resolve, reject) { var tokenMatches = /access_token=([^&]+)/.exec(redirect_url); var stateMatches = /state=([^&]+)/.exec(redirect_url); @@ -100,7 +100,7 @@ function DropboxFileManager(settings) { var uid = uidMatches[1]; if (checkState === randomState) { state.loggedIn = true; - settings.getSetAccessToken(accessTokenType, access_token).then(function() { + settings.getSetAccessToken(accessTokenType, access_token).then(function () { resolve(access_token); }); } else { diff --git a/services/googleDrivePasswordFileManager.js b/services/googleDrivePasswordFileManager.js index 0c7734c4..ecec85db 100644 --- a/services/googleDrivePasswordFileManager.js +++ b/services/googleDrivePasswordFileManager.js @@ -35,7 +35,7 @@ function GoogleDrivePasswordFileManager(settings) { chooseDescription: 'Access password files stored on Google Drive. Files will be fetched from Google Drive each time they are used.', }; - oauth.searchRequestFunction = function(token) { + oauth.searchRequestFunction = function (token) { var request = { method: 'GET', url: "https://www.googleapis.com/drive/v2/files?q=" + urlencode("fileExtension = 'kdbx' and trashed=false"), @@ -46,8 +46,8 @@ function GoogleDrivePasswordFileManager(settings) { return axios(request) } - oauth.searchRequestHandler = function(response) { - return response.data.items.map(function(entry) { + oauth.searchRequestHandler = function (response) { + return response.data.items.map(function (entry) { return { title: entry.title, url: entry.selfLink @@ -56,7 +56,7 @@ function GoogleDrivePasswordFileManager(settings) { } //get the minimum information needed to identify this file for future retrieval - oauth.getDatabaseChoiceData = function(dbInfo) { + oauth.getDatabaseChoiceData = function (dbInfo) { return { title: dbInfo.title, url: dbInfo.url @@ -64,7 +64,7 @@ function GoogleDrivePasswordFileManager(settings) { } //given minimal file information, retrieve the actual file - oauth.fileRequestFunction = function(dbInfo, token) { + oauth.fileRequestFunction = function (dbInfo, token) { function getFileFromDatabase(attempt) { var requestmeta = { method: "GET", @@ -83,8 +83,8 @@ function GoogleDrivePasswordFileManager(settings) { } }; return axios(requestfile).then(response => { - return response - }) + return response + }) .catch(err => { attempt = attempt || 0; if (attempt == 0) { @@ -99,8 +99,8 @@ function GoogleDrivePasswordFileManager(settings) { return getFileFromDatabase(0) } - oauth.revokeAuth = function() { - return settings.getSetAccessToken(accessTokenType).then(function(accessToken) { + oauth.revokeAuth = function () { + return settings.getSetAccessToken(accessTokenType).then(function (accessToken) { if (accessToken) { var url = 'https://accounts.google.com/o/oauth2/revoke?token=' + accessToken return axios({ @@ -117,7 +117,7 @@ function GoogleDrivePasswordFileManager(settings) { }) } - oauth.handleAuthRedirectURI = function(redirect_url, randomState, resolve, reject) { + oauth.handleAuthRedirectURI = function (redirect_url, randomState, resolve, reject) { var tokenMatches = /access_token=([^&]+)/.exec(redirect_url); var stateMatches = /state=([^&]+)/.exec(redirect_url); @@ -125,7 +125,7 @@ function GoogleDrivePasswordFileManager(settings) { var access_token = tokenMatches[1]; var checkState = decodeURIComponent(stateMatches[1]); if (checkState === randomState) { - settings.getSetAccessToken(accessTokenType, access_token).then(function() { + settings.getSetAccessToken(accessTokenType, access_token).then(function () { resolve(access_token); }); } else { @@ -140,17 +140,17 @@ function GoogleDrivePasswordFileManager(settings) { } } - function chrome_auth (interactive){ + function chrome_auth(interactive) { // chrome_auth is an alternative auth function run when the // browser is Chrome. It uses chrome.identity.getAuthToken rather than // a standard Oauth flow. interactive = !!interactive; - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { chrome.identity.getAuthToken({ - interactive : interactive - }, function(token) { + interactive: interactive + }, function (token) { if (token) - settings.getSetAccessToken(accessTokenType, token).then(function() { + settings.getSetAccessToken(accessTokenType, token).then(function () { resolve(token); }); else { @@ -171,10 +171,10 @@ function GoogleDrivePasswordFileManager(settings) { // If this browser has the getAuthToken function. Hack for #64 try { - if (chrome.identity.getAuthToken !== undefined){ + if (chrome.identity.getAuthToken !== undefined) { oauth['auth'] = chrome_auth; } - } + } catch (e) { console.info("Firefox mobile detected.") } diff --git a/services/keepassHeader.js b/services/keepassHeader.js index a00afd3c..53d51e9f 100644 --- a/services/keepassHeader.js +++ b/services/keepassHeader.js @@ -8,7 +8,7 @@ function KeepassHeader() { }; var AES_CIPHER_UUID = new Uint8Array([0x31, 0xc1, 0xf2, 0xe6, 0xbf, 0x71, 0x43, 0x50, 0xbe, 0x58, 0x05, 0x21, 0x6a, 0xfc, 0x5a, 0xff]); - var littleEndian = (function() { + var littleEndian = (function () { var buffer = new ArrayBuffer(2); new DataView(buffer).setInt16(0, 256, true); return new Int16Array(buffer)[0] === 256; diff --git a/services/keepassReference.js b/services/keepassReference.js index 87988b48..60d19853 100644 --- a/services/keepassReference.js +++ b/services/keepassReference.js @@ -7,14 +7,14 @@ function KeepassReference() { majorVersion: 3 // Defaults to 3, unless told otherwise }; - my.hasReferences = function(fieldValue) { + my.hasReferences = function (fieldValue) { return !!/\{.+\}/.test(fieldValue || ''); } /* * Process all references found in fieldValue to their final values */ - my.processAllReferences = function(majorVersion, fieldValue, currentEntry, allEntries) { + my.processAllReferences = function (majorVersion, fieldValue, currentEntry, allEntries) { my.majorVersion = majorVersion; //update the major version if it changed. var re = /(\{[^\{\}]+\})/g; var expressions = re.exec(fieldValue || ''); @@ -37,7 +37,7 @@ function KeepassReference() { return result; } - my.keewebGetDecryptedFieldValue = function(entry, fieldName) { + my.keewebGetDecryptedFieldValue = function (entry, fieldName) { if (entry.protectedData === undefined || !(fieldName in entry['protectedData'])) { return entry[fieldName] || ""; //not an encrypted field } @@ -46,14 +46,14 @@ function KeepassReference() { entry['protectedData'][fieldName].salt).getText(); } - my.getFieldValue = function(currentEntry, fieldName, allEntries) { + my.getFieldValue = function (currentEntry, fieldName, allEntries) { // entries are JSON serializable. // Convert back to a keeweb.ProtectedValue for parsing. let plainText = my.keewebGetDecryptedFieldValue(currentEntry, fieldName); return my.processAllReferences(my.majorVersion, plainText, currentEntry, allEntries); } - my.resolveReference = function(referenceText, currentEntry, allEntries) { + my.resolveReference = function (referenceText, currentEntry, allEntries) { var localParts = /^\{([a-zA-Z]+)\}$/.exec(referenceText) if (localParts) { // local field @@ -73,7 +73,7 @@ function KeepassReference() { // https://stackoverflow.com/questions/2970525/converting-any-string-into-camel-case let camelize = (str) => { - return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(letter, index) { + return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function (letter, index) { return index == 0 ? letter.toLowerCase() : letter.toUpperCase(); }).replace(/\s+/g, ''); } @@ -89,10 +89,10 @@ function KeepassReference() { var wantedField = getPropertyNameFromCode(refString[1]); var searchIn = getPropertyNameFromCode(refString[2]); var text = refString[3]; - - var matches = allEntries.filter(function(e) { + + var matches = allEntries.filter(function (e) { if (searchIn === '*') { - var customFieldMatches = e.keys.filter(function(key) { + var customFieldMatches = e.keys.filter(function (key) { return String(e[key] || '').indexOf(text) !== -1; }); return customFieldMatches.length > 0; diff --git a/services/keepassService.js b/services/keepassService.js index 3c5a9fb7..4faa5fab 100644 --- a/services/keepassService.js +++ b/services/keepassService.js @@ -13,44 +13,47 @@ import { parseUrl, getValidTokens } from '$lib/utils.js' function KeepassService(keepassHeader, settings, passwordFileStoreRegistry, keepassReference) { var my = {}; - var littleEndian = (function() { + var littleEndian = (function () { var buffer = new ArrayBuffer(2); new DataView(buffer).setInt16(0, 256, true); return new Int16Array(buffer)[0] === 256; })(); - + /** * return Promise(arrayBufer) */ - my.getChosenDatabaseFile = function() { + my.getChosenDatabaseFile = function () { return passwordFileStoreRegistry.getChosenDatabaseFile(settings) } - my.getMasterKey = function(bufferPromise, masterPassword, keyFileInfo) { + my.getMasterKey = function (bufferPromise, masterPassword, keyFileInfo) { /** * Validate that one of the following is true: * (password isn't empty OR keyfile isn't empty) * ELSE * (assume password is the empty string) */ + let protectedMasterPassword; if (masterPassword === undefined && keyFileInfo === undefined) { // Neither keyfile nor password provided. Assume empty string password. - masterPassword = ""; + protectedMasterPassword = kdbxweb.ProtectedValue.fromString(""); } else if (masterPassword === "" && keyFileInfo !== undefined) { // Keyfile but empty password provided. Assume password is unused. // This extension does not support the combo empty string + keyfile. - masterPassword = undefined; + protectedMasterPassword = null; + } else { + protectedMasterPassword = kdbxweb.ProtectedValue.fromString(masterPassword) } var fileKey = keyFileInfo ? Base64.decode(keyFileInfo.encodedKey) : null; - return bufferPromise.then(function(buf) { + return bufferPromise.then(function (buf) { var h = keepassHeader.readHeader(buf); - return getKey(h.kdbx, masterPassword, fileKey); + return getKey(h.kdbx, protectedMasterPassword, fileKey); }); } - my.getDecryptedData = function(bufferPromise, masterKey) { + my.getDecryptedData = function (bufferPromise, masterKey) { var majorVersion; - return bufferPromise.then(function(buf) { + return bufferPromise.then(function (buf) { var h = keepassHeader.readHeader(buf); if (!h) throw new Error('Failed to read file header'); @@ -66,7 +69,7 @@ function KeepassService(keepassHeader, settings, passwordFileStoreRegistry, keep } else { // KDB - we don't support this anymore throw "Unsupported Database Version"; } - }).then(function(entries) { + }).then(function (entries) { return { entries: entries, version: majorVersion @@ -75,17 +78,17 @@ function KeepassService(keepassHeader, settings, passwordFileStoreRegistry, keep } my.rankEntries = (entries, siteUrl, title, siteTokens) => { - entries.forEach(function(entry) { + entries.forEach(function (entry) { //apply a ranking algorithm to find the best matches - var entryOrigins = [ parseUrl(entry.url) ] - - if (entry.keys.indexOf('tuskUrls') >= 0){ + var entryOrigins = [parseUrl(entry.url)] + + if (entry.keys.indexOf('tuskUrls') >= 0) { let others = entry.tuskUrls .split(',') .map(val => { return parseUrl(val) }) - entryOrigins = entryOrigins.concat(others) + entryOrigins = entryOrigins.concat(others) } if (entryOrigins.length && entryOrigins.some(a => a && (a.origin == siteUrl.origin))) entry.matchRank = 100 // perfect match @@ -109,14 +112,14 @@ function KeepassService(keepassHeader, settings, passwordFileStoreRegistry, keep if (token1 == token2) { entry.matchRank += 0.2; } - + } } }) } - function getKey(isKdbx, masterPassword, fileKey) { - var creds = new kdbxweb.Credentials(kdbxweb.ProtectedValue.fromString(masterPassword), fileKey); + function getKey(isKdbx, protectedMasterPassword, fileKey) { + var creds = new kdbxweb.Credentials(protectedMasterPassword, fileKey); return creds.ready.then(() => { return kdbxCredentialsToJson(creds); }); @@ -125,9 +128,9 @@ function KeepassService(keepassHeader, settings, passwordFileStoreRegistry, keep function processReferences(entries, majorVersion) { // In order to fully implement references, majorVersion will need to be known // as there are more capabilities for references in v2+ - entries.forEach(function(entry) { + entries.forEach(function (entry) { if (entry.keys) { - entry.keys.forEach(function(key) { + entry.keys.forEach(function (key) { var fieldRefs = keepassReference.hasReferences(entry[key]); if (fieldRefs) { let value = keepassReference.processAllReferences(majorVersion, entry[key], entry, entries); diff --git a/services/keyFileParser.js b/services/keyFileParser.js index 4e0839b0..cbc52afe 100644 --- a/services/keyFileParser.js +++ b/services/keyFileParser.js @@ -21,7 +21,7 @@ function KeyFileParser() { } } - exports.getKeyFromFile = function(keyFileBytes) { + exports.getKeyFromFile = function (keyFileBytes) { var arr = new Uint8Array(keyFileBytes); if (arr.byteLength == 0) { return Promise.reject(new Error('The key file cannot be empty')); diff --git a/services/links.js b/services/links.js index d33f802f..0eb082c5 100644 --- a/services/links.js +++ b/services/links.js @@ -11,7 +11,7 @@ function Links() { openOptionsDatabases: openOptionsDatabases, open: openGeneric } - + function openGeneric(url) { // Given some URL, open it in a new tab chrome.tabs.create({ diff --git a/services/localChromePasswordFileManager.js b/services/localChromePasswordFileManager.js index a1b17d5f..edf799c0 100644 --- a/services/localChromePasswordFileManager.js +++ b/services/localChromePasswordFileManager.js @@ -17,8 +17,8 @@ function LocalChromePasswordFileManager() { supportedFeatures: ['incognito', 'listDatabases', 'saveDatabase', 'deleteDatabase'], title: 'Local Storage', icon: 'icon-upload', - chooseTitle: 'File System', - chooseDescription: 'Upload files from your local or remote file-system. A one-time copy of the file(s) will be saved in your browser\'s local storage. If you update the database on your local system then you will have to re-upload it in order to see the changes.', + chooseTitle: 'File System (not recommended)', + chooseDescription: 'Upload files from your local or remote file-system. A one-time copy of the file(s) will be saved in your browser\'s local storage. If you update the database on your local system then you will have to re-import it in order to see the changes.', login: enable, logout: disable, isLoggedIn: isEnabled @@ -47,12 +47,12 @@ function LocalChromePasswordFileManager() { var savingLocks = []; //prevent reading while an async save is ongoing function listDatabases() { - return Promise.all(savingLocks).then(function() { + return Promise.all(savingLocks).then(function () { return chromePromise.storage.local.get('passwordFiles') - }).then(function(result) { + }).then(function (result) { var files = result.passwordFiles || []; - return files.filter(function(fi) { + return files.filter(function (fi) { return (fi.storageVersion && fi.storageVersion >= backwardCompatibleVersion); }); }); @@ -67,8 +67,8 @@ function LocalChromePasswordFileManager() { //given minimal file information, retrieve the actual file function getChosenDatabaseFile(dbInfo) { - return listDatabases().then(function(databases) { - var bytes = databases.reduce(function(prev, storedFile) { + return listDatabases().then(function (databases) { + var bytes = databases.reduce(function (prev, storedFile) { if (storedFile.title == dbInfo.title) { var data = Base64.decode(storedFile.data); return data; @@ -86,8 +86,8 @@ function LocalChromePasswordFileManager() { //save the given database to persistent storage function saveDatabase(db) { db.storageVersion = currentVersion; - var p = listDatabases().then(function(existingFiles) { - var index = existingFiles.reduce(function(prev, curr, index) { + var p = listDatabases().then(function (existingFiles) { + var index = existingFiles.reduce(function (prev, curr, index) { if (curr.title == db.title) return index; else @@ -111,8 +111,8 @@ function LocalChromePasswordFileManager() { //remove the database from storage function deleteDatabase(db) { - return listDatabases().then(function(databases) { - databases = databases.filter(function(existing) { + return listDatabases().then(function (databases) { + databases = databases.filter(function (existing) { return (existing.title != db.title); }); diff --git a/services/notifications.js b/services/notifications.js index 15410515..4f5568b8 100644 --- a/services/notifications.js +++ b/services/notifications.js @@ -4,7 +4,7 @@ export class Notifications { } push(data) { const { text, type, expire } = data; - return new Promise ((resolve, reject) => { + return new Promise((resolve, reject) => { this.settings.getSetNotificationsEnabled().then(val => { if (val.indexOf(type) > -1) { chrome.runtime.sendMessage({ diff --git a/services/oauthManager.js b/services/oauthManager.js index 6cf3a602..c95324c8 100644 --- a/services/oauthManager.js +++ b/services/oauthManager.js @@ -58,7 +58,7 @@ function OauthManager(settings, oauth) { function getToken() { return ensureOriginPermissions().then(ensured => { if (ensured) { - return settings.getSetAccessToken(accessTokenType).then(function(stored_token) { + return settings.getSetAccessToken(accessTokenType).then(function (stored_token) { if (stored_token) { state.loggedIn = true; return stored_token; @@ -75,7 +75,7 @@ function OauthManager(settings, oauth) { //lists databases if a token is already stored function listDatabasesSafe() { - return settings.getSetAccessToken(accessTokenType).then(function(stored_token) { + return settings.getSetAccessToken(accessTokenType).then(function (stored_token) { if (stored_token) { return listDatabases(); } else { @@ -131,8 +131,8 @@ function OauthManager(settings, oauth) { } function logout() { - return oauth.revokeAuth().then(function() { - return removeToken().then(function() { + return oauth.revokeAuth().then(function () { + return removeToken().then(function () { state.loggedIn = false }) }) @@ -140,17 +140,17 @@ function OauthManager(settings, oauth) { //given minimal file information, retrieve the actual file function getChosenDatabaseFile(dbInfo, attempt) { - return getToken().then(function(accessToken) { - return oauth.fileRequestFunction(dbInfo, accessToken).then(function(response) { + return getToken().then(function (accessToken) { + return oauth.fileRequestFunction(dbInfo, accessToken).then(function (response) { return response.data - }).catch(function(error) { + }).catch(function (error) { console.error("Get chosen file failure:", error) if (error.response === undefined) - return Promise.reject({message:"No network connection"}) + return Promise.reject({ message: "No network connection" }) if (error.response.status == 401) { //unauthorized, means the token is bad. retry with new token. console.error("Stale token sent for " + oauth.accessTokenType + ": trying passive Oauth Refresh.") - return auth(false).then(function() { + return auth(false).then(function () { return getChosenDatabaseFile(dbInfo); }) } @@ -161,14 +161,14 @@ function OauthManager(settings, oauth) { function ensureOriginPermissions() { return chromePromise.permissions.contains({ origins: oauth.origins - }).then(function() { + }).then(function () { return true; - }).catch(function() { + }).catch(function () { return chromePromise.permissions.request({ origins: oauth.origins - }).then(function() { + }).then(function () { return true; - }).catch(function(err) { + }).catch(function (err) { return false; }) }); @@ -179,10 +179,10 @@ function OauthManager(settings, oauth) { console.info("Authenticating for ", oauth.accessTokenType, interactive) let authfunction = is_interactive => { - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { chromePromise.runtime.getManifest().then(manifest => { //random state, protects against CSRF - var randomState = Base64.encode(window.crypto.getRandomValues(new Uint8Array(16))); + var randomState = Base64.encode(window.crypto.getRandomValues(new Uint8Array(16))); var authUrl = oauth.authUrl + '&client_id=' + manifest.static_data[oauth.accessTokenType].client_id + '&state=' + encodeURIComponent(randomState) + @@ -193,7 +193,7 @@ function OauthManager(settings, oauth) { 'interactive': is_interactive }).then(redirect_url => { oauth.handleAuthRedirectURI(redirect_url, randomState, resolve, reject); - }).catch(function(err) { + }).catch(function (err) { console.error("Error from webauthflow for", oauth.accessTokenType, err); reject(err); }); diff --git a/services/oneDriveFileManager.js b/services/oneDriveFileManager.js index 2d1818c2..b2f7049b 100644 --- a/services/oneDriveFileManager.js +++ b/services/oneDriveFileManager.js @@ -27,7 +27,7 @@ function OneDriveFileManager(settings) { chooseDescription: 'Access password files stored on OneDrive. Files will be retrieved from OneDrive each time they are used.', }; - oauth.searchRequestFunction = function(token) { + oauth.searchRequestFunction = function (token) { var query = 'kdbx'; var filter = encodeURIComponent('file ne null'); var url = 'https://api.onedrive.com/v1.0/drive/root/view.search?q=' + query + '&filter=' + filter; @@ -40,7 +40,7 @@ function OneDriveFileManager(settings) { }) } - oauth.searchRequestHandler = function(response) { + oauth.searchRequestHandler = function (response) { function transformFile(file) { var path = ""; @@ -74,7 +74,7 @@ function OneDriveFileManager(settings) { } // only return files that have a .kdbx extension - var files = response.data.value.filter(function(file) { + var files = response.data.value.filter(function (file) { return file.name && /\.kdbx?$/.exec(file.name); }); @@ -82,7 +82,7 @@ function OneDriveFileManager(settings) { } //get the minimum information needed to identify this file for future retrieval - oauth.getDatabaseChoiceData = function(dbInfo) { + oauth.getDatabaseChoiceData = function (dbInfo) { return { url: dbInfo.url, title: dbInfo.title @@ -90,7 +90,7 @@ function OneDriveFileManager(settings) { } //given minimal file information, retrieve the actual file - oauth.fileRequestFunction = function(dbInfo, token) { + oauth.fileRequestFunction = function (dbInfo, token) { return axios({ method: 'GET', url: dbInfo.url, @@ -101,7 +101,7 @@ function OneDriveFileManager(settings) { }) } - oauth.revokeAuth = function() { + oauth.revokeAuth = function () { return chromePromise.runtime.getManifest().then(manifest => { let url = 'https://login.live.com/oauth20_logout.srf?client_id=' + manifest.static_data.onedrive.client_id @@ -109,7 +109,7 @@ function OneDriveFileManager(settings) { }) } - oauth.handleAuthRedirectURI = function(redirect_url, randomState, resolve, reject) { + oauth.handleAuthRedirectURI = function (redirect_url, randomState, resolve, reject) { function parseAuthInfoFromUrl(url) { var hash = /#(.+)$/.exec(url); @@ -117,7 +117,7 @@ function OneDriveFileManager(settings) { return null; } hash = hash[1]; - return JSON.parse('{"' + hash.replace(/&/g, '","').replace(/=/g, '":"') + '"}', function(key, value) { + return JSON.parse('{"' + hash.replace(/&/g, '","').replace(/=/g, '":"') + '"}', function (key, value) { return key === "" ? value : decodeURIComponent(value); }); } diff --git a/services/pCloudFileManager.js b/services/pCloudFileManager.js index cca4aab3..75d6bdfe 100644 --- a/services/pCloudFileManager.js +++ b/services/pCloudFileManager.js @@ -1,136 +1,132 @@ "use strict"; const Base64 = require('base64-arraybuffer') import axios from 'axios/dist/axios.min.js' -import { - ChromePromiseApi -} from '$lib/chrome-api-promise.js' -import { - OauthManager -} from '$services/oauthManager.js' +import { ChromePromiseApi } from '$lib/chrome-api-promise.js' +import { OauthManager } from '$services/oauthManager.js' const chromePromise = ChromePromiseApi() function PCloudFileManager(settings) { - var accessTokenType = 'pcloud'; + var accessTokenType = 'pcloud'; - var state = { - loggedIn: false - } + var state = { + loggedIn: false + } - var oauth = { - key: accessTokenType, - accessTokenType: accessTokenType, - origins: ['https://*.pcloud.com/'], - authUrl: 'https://my.pcloud.com/oauth2/authorize?response_type=token&force_reapprove=true', - supportedFeatures: ['incognito', 'listDatabases'], - title: 'pCloud', - icon: 'icon-pcloud', - chooseTitle: 'pCloud', - chooseDescription: 'Access password files stored on pCloud. Files will be retrieved from pCould each time they are used.', - }; + var oauth = { + key: accessTokenType, + accessTokenType: accessTokenType, + origins: ['https://*.pcloud.com/'], + authUrl: 'https://my.pcloud.com/oauth2/authorize?response_type=token&force_reapprove=true', + supportedFeatures: ['incognito', 'listDatabases'], + title: 'pCloud', + icon: 'icon-pcloud', + chooseTitle: 'pCloud', + chooseDescription: 'Access password files stored on pCloud. Files will be retrieved from pCould each time they are used.', + }; - oauth.searchRequestFunction = function(token) { - return axios({ - method: 'GET', - url: 'https://api.pcloud.com/listfolder', - params: { - folderid: 0, - recursive: 1, - showdeleted: 0, - nofiles: 0, - noshares: 1, - filtermeta: "isfolder,name,id,folderid,fileid,path" - }, - headers: { - 'Authorization': 'Bearer ' + token - } - }) - } + oauth.searchRequestFunction = function (token) { + return axios({ + method: 'GET', + url: 'https://api.pcloud.com/listfolder', + params: { + folderid: 0, + recursive: 1, + showdeleted: 0, + nofiles: 0, + noshares: 1, + filtermeta: "isfolder,name,id,folderid,fileid,path" + }, + headers: { + 'Authorization': 'Bearer ' + token + } + }) + } - oauth.searchRequestHandler = function(response) { + oauth.searchRequestHandler = function (response) { - var walk = function(files, contents, path) { - contents.forEach( (file) => { - if(file.isfolder) { - walk(files, file.contents, path + file.name + "/") - } - else { - file.path = path + file.name - files.push(file) - } - }) - return files - } + var walk = function (files, contents, path) { + contents.forEach((file) => { + if (file.isfolder) { + walk(files, file.contents, path + file.name + "/") + } + else { + file.path = path + file.name + files.push(file) + } + }) + return files + } - var files = walk([], response.data.metadata.contents, "/") - return files.filter(function(fileInfo) { - return fileInfo.name && /\.kdbx?$/.exec(fileInfo.name) - }).map(function(fileInfo) { - return { - title: fileInfo.path, - id: fileInfo.fileid - }; - }); - } + var files = walk([], response.data.metadata.contents, "/") + return files.filter(function (fileInfo) { + return fileInfo.name && /\.kdbx?$/.exec(fileInfo.name) + }).map(function (fileInfo) { + return { + title: fileInfo.path, + id: fileInfo.fileid + }; + }); + } - //get the minimum information needed to identify this file for future retrieval - oauth.getDatabaseChoiceData = function(dbInfo) { - return { - title: dbInfo.title, - id: dbInfo.id - } - } + //get the minimum information needed to identify this file for future retrieval + oauth.getDatabaseChoiceData = function (dbInfo) { + return { + title: dbInfo.title, + id: dbInfo.id + } + } - //given minimal file information, retrieve the actual file - oauth.fileRequestFunction = function(dbInfo, token) { - return axios({ - method: 'GET', - url: 'https://api.pcloud.com/getfilelink', - params: { - path: dbInfo.title, - }, - headers: { - 'Authorization': 'Bearer ' + token, - }, - }).then(function(response) { - var url = `https://${response.data.hosts[0]}${response.data.path}` - return axios({ - method: 'GET', - url: url, - responseType: 'arraybuffer', - }) - }) - } + //given minimal file information, retrieve the actual file + oauth.fileRequestFunction = function (dbInfo, token) { + return axios({ + method: 'GET', + url: 'https://api.pcloud.com/getfilelink', + params: { + path: dbInfo.title, + }, + headers: { + 'Authorization': 'Bearer ' + token, + }, + }).then(function (response) { + var url = `https://${response.data.hosts[0]}${response.data.path}` + return axios({ + method: 'GET', + url: url, + responseType: 'arraybuffer', + }) + }) + } - oauth.revokeAuth = function() { - return Promise.resolve() - } + oauth.revokeAuth = function () { + return Promise.resolve() + } - oauth.handleAuthRedirectURI = function(redirect_url, randomState, resolve, reject) { - function parseAuthInfoFromUrl(url) { - var hash = /#(.+)$/.exec(url); - if (!hash) { - return null; - } - hash = hash[1]; - return JSON.parse('{"' + hash.replace(/&/g, '","').replace(/=/g, '":"') + '"}', function(key, value) { - return key === "" ? value : decodeURIComponent(value); - }); - } + oauth.handleAuthRedirectURI = function (redirect_url, randomState, resolve, reject) { + function parseAuthInfoFromUrl(url) { + var hash = /#(.+)$/.exec(url); + if (!hash) { + return null; + } + hash = hash[1]; + return JSON.parse('{"' + hash.replace(/&/g, '","').replace(/=/g, '":"') + '"}', function (key, value) { + return key === "" ? value : decodeURIComponent(value); + }); + } - var authInfo = parseAuthInfoFromUrl(redirect_url); - if (authInfo === null) { - reject('Failed to extract authentication information from redirect url'); - } else { - settings.getSetAccessToken(accessTokenType, authInfo.access_token).then(function() { - resolve(authInfo.access_token); - }); - } - } + var authInfo = parseAuthInfoFromUrl(redirect_url); + if (authInfo === null) { + reject('Failed to extract authentication information from redirect url'); + } else { + settings.getSetAccessToken(accessTokenType, authInfo.access_token).then(function () { + resolve(authInfo.access_token); + }); + } + } - return OauthManager(settings, oauth) + return OauthManager(settings, oauth) } export { - PCloudFileManager + PCloudFileManager } diff --git a/services/passwordFileStore.js b/services/passwordFileStore.js index 42ee3164..b2f129df 100644 --- a/services/passwordFileStore.js +++ b/services/passwordFileStore.js @@ -20,14 +20,14 @@ function PasswordFileStoreRegistry() { function listFileManagers(requiredFeature) { if (!requiredFeature) return fileManagers; - return fileManagers.filter(function(fileManager) { + return fileManagers.filter(function (fileManager) { return fileManager.supportedFeatures.indexOf(requiredFeature) > -1; }); } function getChosenDatabaseFile(settings) { - return settings.getCurrentDatabaseChoice().then(function(choice) { - var matches = fileManagers.filter(function(fileManager) { + return settings.getCurrentDatabaseChoice().then(function (choice) { + var matches = fileManagers.filter(function (fileManager) { return fileManager.key == choice.providerKey; }); if (matches.length !== 1) throw new Error('Unable to find file manager for key ' + choice.providerKey); diff --git a/services/protectedMemory.js b/services/protectedMemory.js index 40f214bd..f46f7ef8 100644 --- a/services/protectedMemory.js +++ b/services/protectedMemory.js @@ -35,13 +35,13 @@ function ProtectedMemory() { function getData(key) { var encData = dataMap[key]; - if (encData === undefined || typeof(encData) !== 'string') + if (encData === undefined || typeof (encData) !== 'string') return Promise.resolve(undefined); - return keyPromise.then( key => { + return keyPromise.then(key => { var encBytes = Base64.decode(encData); return window.crypto.subtle.decrypt(AES, key, encBytes); - }).then(function(data) { + }).then(function (data) { var decoder = new TextDecoder(); var decoded = decoder.decode(new Uint8Array(data)); var parsed = JSON.parse(decoded) @@ -53,9 +53,9 @@ function ProtectedMemory() { var preppedData = prepData(data); var encoder = new TextEncoder(); var dataBytes = encoder.encode(JSON.stringify(preppedData)); - return keyPromise.then( key => { + return keyPromise.then(key => { return window.crypto.subtle.encrypt(AES, key, dataBytes); - }).then(function(encData) { + }).then(function (encData) { var dataString = Base64.encode(encData); dataMap[key] = dataString; return Promise.resolve(); @@ -63,8 +63,8 @@ function ProtectedMemory() { } function clearData(key) { - if (key !== undefined){ - delete dataMap[key] + if (key !== undefined) { + delete dataMap[key] } else { dataMap = {}; keyPromise = initNewKey(); @@ -80,7 +80,7 @@ function ProtectedMemory() { } function deserialize(serializedData) { - if (serializedData === undefined || typeof(serializedData) !== 'string' || serializedData === "") + if (serializedData === undefined || typeof (serializedData) !== 'string' || serializedData === "") return undefined; var dataBytes = Base64.decode(serializedData); @@ -96,7 +96,7 @@ function ProtectedMemory() { */ var randomString = "α»°αŸ…" // Base64.encode(window.crypto.getRandomValues(new Uint8Array(4))); function prepData(data) { - if (data === null || data === undefined || typeof(data) !== 'object') + if (data === null || data === undefined || typeof (data) !== 'object') return data; if (data.constructor == ArrayBuffer || data.constructor == Uint8Array) { @@ -117,10 +117,10 @@ function ProtectedMemory() { } function dePrepData(data) { - if (data === null || data === undefined || (typeof(data) !== 'object' && typeof(data) !== 'string')) + if (data === null || data === undefined || (typeof (data) !== 'object' && typeof (data) !== 'string')) return data; - if (typeof(data) === "string") { + if (typeof (data) === "string") { if (data.indexOf(randomString) == 0) { data = data.slice(randomString.length); return new Uint8Array(Base64.decode(data)); diff --git a/services/sampleDatabaseFileManager.js b/services/sampleDatabaseFileManager.js index 589a0564..6cf32212 100644 --- a/services/sampleDatabaseFileManager.js +++ b/services/sampleDatabaseFileManager.js @@ -35,7 +35,7 @@ function SampleDatabaseFileManager() { } function listDatabases() { - return getActive().then(function(flag) { + return getActive().then(function (flag) { if (flag) { return [{ title: 'Sample.kdbx - password is 123' @@ -59,7 +59,7 @@ function SampleDatabaseFileManager() { method: 'GET', url: chrome.extension.getURL('/assets/other/Sample123.kdbx'), responseType: 'arraybuffer' - }).then(function(response) { + }).then(function (response) { return response.data; }); } @@ -74,7 +74,7 @@ function SampleDatabaseFileManager() { } function getActive() { - return chromePromise.storage.local.get('useSampleDatabase').then(function(results) { + return chromePromise.storage.local.get('useSampleDatabase').then(function (results) { return !!results.useSampleDatabase; }); } diff --git a/services/secureCacheMemory.js b/services/secureCacheMemory.js index a053d442..b5f35e3a 100644 --- a/services/secureCacheMemory.js +++ b/services/secureCacheMemory.js @@ -9,16 +9,16 @@ function SecureCacheMemory(protectedMemory) { var awaiting = []; var messageReceived; var notifyReady; - var ready = new Promise(function(resolve) { + var ready = new Promise(function (resolve) { notifyReady = resolve; }); - exports.ready = function() { - return ready.then(function(port) { + exports.ready = function () { + return ready.then(function (port) { return true; }); }; - var promise = new Promise(function(resolve, reject) { + var promise = new Promise(function (resolve, reject) { messageReceived = resolve; }); @@ -26,7 +26,7 @@ function SecureCacheMemory(protectedMemory) { chrome.tabs.query({ active: true, currentWindow: true - }, function(tabs) { + }, function (tabs) { if (tabs !== undefined && tabs.length > 0) { var port = chrome.runtime.connect({ name: "tab" + tabs[0].id @@ -35,36 +35,36 @@ function SecureCacheMemory(protectedMemory) { } }); - ready.then(function(port) { - port.onMessage.addListener(function(serializedSavedState) { + ready.then(function (port) { + port.onMessage.addListener(function (serializedSavedState) { //called from the background when we get a response, i.e. some saved state. var savedState = protectedMemory.hydrate(serializedSavedState); var notifier = awaiting.shift(); notifier(savedState); //notify others }); - port.onDisconnect.addListener(function(p) { + port.onDisconnect.addListener(function (p) { // Nothing to do here yet... }) }); //wake up the background page and get a pipe to send/receive messages: - exports.get = function(key) { - ready.then(function(port) { + exports.get = function (key) { + ready.then(function (port) { port.postMessage({ action: 'get', key: key }); }); - var p = new Promise(function(resolve) { + var p = new Promise(function (resolve) { awaiting.push(resolve); }); return p; //will resolve when we get something } - exports.clear = function(key) { - return ready.then(function(port) { + exports.clear = function (key) { + return ready.then(function (port) { port.postMessage({ action: 'clear', key: key @@ -72,8 +72,8 @@ function SecureCacheMemory(protectedMemory) { }) } - exports.save = function(key, value) { - return ready.then(function(port) { + exports.save = function (key, value) { + return ready.then(function (port) { var serializedValue = protectedMemory.serialize(value); port.postMessage({ action: 'save', @@ -83,7 +83,7 @@ function SecureCacheMemory(protectedMemory) { }) } - exports.forgetStuff = function() { + exports.forgetStuff = function () { // Causes forgetStuff to run in the event page. return ready.then(port => { port.postMessage({ diff --git a/services/settings.js b/services/settings.js index adbd592d..e2730be5 100644 --- a/services/settings.js +++ b/services/settings.js @@ -10,7 +10,7 @@ function Settings(secureCache) { var exports = {} // upgrade old settings. Called on install. - exports.upgrade = function() { + exports.upgrade = function () { // Patch https://subdavis.com/blog/jekyll/update/2017/01/02/ckp-security-flaw.html exports.getSetDatabaseUsages().then(usages => { let keys = Object.keys(usages) @@ -21,7 +21,7 @@ function Settings(secureCache) { }) } - exports.handleProviderError = function(err, provider) { + exports.handleProviderError = function (err, provider) { exports.getCurrentDatabaseChoice().then(info => { let providerKey = provider === undefined ? info.providerKey : provider.key; let errmsg = err.message || "" @@ -32,15 +32,15 @@ function Settings(secureCache) { }) } - exports.getKeyFiles = function() { - return chromePromise.storage.local.get(['keyFiles']).then(function(items) { + exports.getKeyFiles = function () { + return chromePromise.storage.local.get(['keyFiles']).then(function (items) { return items.keyFiles || []; }); } - exports.deleteKeyFile = function(name) { - return exports.getKeyFiles().then(function(keyFiles) { - keyFiles.forEach(function(keyFile, index) { + exports.deleteKeyFile = function (name) { + return exports.getKeyFiles().then(function (keyFiles) { + keyFiles.forEach(function (keyFile, index) { if (keyFile.name === name) { keyFiles.splice(index, 1); } @@ -52,19 +52,19 @@ function Settings(secureCache) { }); } - exports.deleteAllKeyFiles = function() { + exports.deleteAllKeyFiles = function () { return chromePromise.storage.local.remove('keyFiles') } - exports.destroyLocalStorage = function(key) { + exports.destroyLocalStorage = function (key) { if (key.length) { return chromePromise.storage.local.remove(key) } } - exports.addKeyFile = function(name, key) { - return exports.getKeyFiles().then(function(keyFiles) { - var matches = keyFiles.filter(function(keyFile) { + exports.addKeyFile = function (name, key) { + return exports.getKeyFiles().then(function (keyFiles) { + var matches = keyFiles.filter(function (keyFile) { return keyFile.name === name; }) @@ -86,8 +86,8 @@ function Settings(secureCache) { }); } - exports.saveCurrentDatabaseChoice = function(passwordFile, provider) { - let shallowCopy = function(obj) { + exports.saveCurrentDatabaseChoice = function (passwordFile, provider) { + let shallowCopy = function (obj) { let clone = {} clone.prototype = obj.prototype Object.keys(obj).forEach(property => { @@ -106,8 +106,8 @@ function Settings(secureCache) { }); } - exports.getCurrentDatabaseChoice = function() { - return chromePromise.storage.local.get(['selectedDatabase']).then(function(items) { + exports.getCurrentDatabaseChoice = function () { + return chromePromise.storage.local.get(['selectedDatabase']).then(function (items) { if (items.selectedDatabase) { return items.selectedDatabase; } else { @@ -116,7 +116,7 @@ function Settings(secureCache) { }); } - exports.disableDatabaseProvider = function(provider) { + exports.disableDatabaseProvider = function (provider) { return chromePromise.storage.local.get(['selectedDatabase']).then(items => { if (items.selectedDatabase) if (items.selectedDatabase.providerKey === provider.key) @@ -125,7 +125,7 @@ function Settings(secureCache) { }) } - exports.getCurrentMasterPasswordCacheKey = function() { + exports.getCurrentMasterPasswordCacheKey = function () { return exports.getCurrentDatabaseChoice().then(info => { if (info !== null) return info.passwordFile.title + "__" + info.providerKey + ".password" @@ -133,7 +133,7 @@ function Settings(secureCache) { }); } - exports.cacheMasterPassword = function(pw, args) { + exports.cacheMasterPassword = function (pw, args) { return exports.getCurrentMasterPasswordCacheKey().then(key => { return secureCache.save(key, pw).then(nil => { let forgetTime = args['forgetTime'] @@ -145,9 +145,9 @@ function Settings(secureCache) { /* * Sets a time to forget something */ - exports.setForgetTime = function(key, time) { + exports.setForgetTime = function (key, time) { var storageKey = 'forgetTimes'; - return chromePromise.storage.local.get(storageKey).then(function(items) { + return chromePromise.storage.local.get(storageKey).then(function (items) { var forgetTimes = {} if (items[storageKey]) { forgetTimes = items[storageKey]; @@ -162,9 +162,9 @@ function Settings(secureCache) { }); } - exports.getForgetTime = function(key) { + exports.getForgetTime = function (key) { var storageKey = 'forgetTimes'; - return chromePromise.storage.local.get(storageKey).then(function(items) { + return chromePromise.storage.local.get(storageKey).then(function (items) { var forgetTimes = {} if (items[storageKey]) { forgetTimes = items[storageKey]; @@ -174,9 +174,9 @@ function Settings(secureCache) { }); } - exports.getAllForgetTimes = function() { + exports.getAllForgetTimes = function () { var storageKey = 'forgetTimes'; - return chromePromise.storage.local.get(storageKey).then(function(items) { + return chromePromise.storage.local.get(storageKey).then(function (items) { var forgetTimes = {} if (items[storageKey]) { forgetTimes = items[storageKey]; @@ -186,14 +186,14 @@ function Settings(secureCache) { }) } - exports.clearForgetTimes = function(keysArray) { + exports.clearForgetTimes = function (keysArray) { var storageKey = 'forgetTimes'; - return chromePromise.storage.local.get(storageKey).then(function(items) { + return chromePromise.storage.local.get(storageKey).then(function (items) { var forgetTimes = {} if (items[storageKey]) { forgetTimes = items[storageKey]; } - keysArray.forEach(function(key) { + keysArray.forEach(function (key) { if (forgetTimes[key]) delete forgetTimes[key]; }) @@ -208,9 +208,9 @@ function Settings(secureCache) { * Saves information about how the database was opened, so we can optimize the * UI next time by hiding the irrelevant options and remembering the keyfile */ - exports.saveCurrentDatabaseUsage = function(usage) { - return exports.getCurrentDatabaseChoice().then(function(info) { - return exports.getSetDatabaseUsages().then(function(usages) { + exports.saveCurrentDatabaseUsage = function (usage) { + return exports.getCurrentDatabaseChoice().then(function (info) { + return exports.getSetDatabaseUsages().then(function (usages) { var key = info.passwordFile.title + "__" + info.providerKey; usages[key] = usage; @@ -223,9 +223,9 @@ function Settings(secureCache) { * Retrieves information about how the database was opened, so we can optimize the * UI by hiding the irrelevant options and remembering the keyfile */ - exports.getCurrentDatabaseUsage = function() { - return exports.getCurrentDatabaseChoice().then(function(info) { - return exports.getSetDatabaseUsages().then(function(usages) { + exports.getCurrentDatabaseUsage = function () { + return exports.getCurrentDatabaseChoice().then(function (info) { + return exports.getSetDatabaseUsages().then(function (usages) { var key = info.passwordFile.title + "__" + info.providerKey; var usage = usages[key] || {}; @@ -237,63 +237,63 @@ function Settings(secureCache) { }) } - exports.getSharedUrlList = function() { + exports.getSharedUrlList = function () { return chromePromise.storage.local.get('sharedUrlList').then(links => { return links || false; }) } - let keyGetSetter = function(key, val, defaultval, value_type) { + let keyGetSetter = function (key, val, defaultval, value_type) { let update_obj = {} update_obj[key] = val - if (val !== undefined && (typeof(val) === value_type || val === null) ) + if (val !== undefined && (typeof (val) === value_type || val === null)) return chromePromise.storage.local.set(update_obj).then(() => val) else return chromePromise.storage.local.get(key).then(oldval => { if (oldval[key] !== undefined) - if (typeof(oldval[key]) === value_type) + if (typeof (oldval[key]) === value_type) return oldval[key] return defaultval }) } - exports.getSetClipboardExpireInterval = function(interval) { + exports.getSetClipboardExpireInterval = function (interval) { return keyGetSetter('expireInterval', interval, 2, 'number') } - exports.getSetAccessToken = function(type, accessToken) { + exports.getSetAccessToken = function (type, accessToken) { return keyGetSetter(type + 'AccessToken', accessToken, null, 'string') } - exports.getSetDatabaseUsages = function(usages) { + exports.getSetDatabaseUsages = function (usages) { return keyGetSetter('databaseUsages', usages, {}, 'object') } - exports.getSetDefaultRememberPeriod = function(rememberPeriod) { + exports.getSetDefaultRememberPeriod = function (rememberPeriod) { return keyGetSetter('rememberPeriod', rememberPeriod, 0, 'number') } - exports.getSetWebdavServerList = function(serverList) { + exports.getSetWebdavServerList = function (serverList) { return keyGetSetter('webdavServerList', serverList, [], 'object') } - exports.getSetWebdavDirectoryMap = function(dirMap) { + exports.getSetWebdavDirectoryMap = function (dirMap) { return keyGetSetter('webdavDirectoryMap', dirMap, {}, 'object') } - exports.getSetHotkeyNavEnabled = function(enabled) { + exports.getSetHotkeyNavEnabled = function (enabled) { return keyGetSetter('hotkeyNavEnabled', enabled, false, 'boolean') } - exports.getSetStrictModeEnabled = function(enabled) { + exports.getSetStrictModeEnabled = function (enabled) { return keyGetSetter('strictMatchModeEnabled', enabled, false, 'boolean') } - exports.getSetNotificationsEnabled = function(enabledTypes) { - return keyGetSetter('notificationsEnabled', enabledTypes, ['clipboard','expiration'], 'object') + exports.getSetNotificationsEnabled = function (enabledTypes) { + return keyGetSetter('notificationsEnabled', enabledTypes, ['clipboard', 'expiration'], 'object') } - exports.getSetOriginPermissionEnabled = function(enabled) { + exports.getSetOriginPermissionEnabled = function (enabled) { return keyGetSetter('originPermissionsEnabled', enabled, false, 'boolean') } diff --git a/services/sharedUrlFileManager.js b/services/sharedUrlFileManager.js index bd4a884f..8a9f7ae7 100644 --- a/services/sharedUrlFileManager.js +++ b/services/sharedUrlFileManager.js @@ -71,7 +71,7 @@ function SharedUrlFileManager() { url: dbInfo.direct_link, responseType: 'arraybuffer', cache: true - }).then(function(response) { + }).then(function (response) { return response.data; }); } diff --git a/services/unlockedState.js b/services/unlockedState.js index f67769aa..60e92daf 100644 --- a/services/unlockedState.js +++ b/services/unlockedState.js @@ -23,12 +23,12 @@ function UnlockedState($router, keepassReference, protectedMemory, settings, not var cacheTimeoutId; //determine current url: - my.getTabDetails = function() { - return new Promise(function(resolve, reject) { + my.getTabDetails = function () { + return new Promise(function (resolve, reject) { chrome.tabs.query({ active: true, currentWindow: true - }, function(tabs) { + }, function (tabs) { if (tabs && tabs.length) { my.tabId = tabs[0].id; var url = tabs[0].url.split('?'); @@ -39,15 +39,15 @@ function UnlockedState($router, keepassReference, protectedMemory, settings, not my.origin = parsedUrl.protocol + '//' + parsedUrl.hostname + '/'; chromePromise.permissions.contains({ - origins: [my.origin] - }) - .then(function() { + origins: [my.origin] + }) + .then(function () { my.sitePermission = true; }) - .catch(function(err) { + .catch(function (err) { my.sitePermission = false; }) - .then(function() { + .then(function () { resolve(); }) } else { @@ -57,12 +57,12 @@ function UnlockedState($router, keepassReference, protectedMemory, settings, not }); }; - my.clearCache = function() { + my.clearCache = function () { // Destroys an object in memory. function destroy(obj) { for (var prop in obj) { var property = obj[prop]; - if (property != null && typeof(property) == 'object') { + if (property != null && typeof (property) == 'object') { destroy(property); } else { obj[prop] = null; @@ -73,32 +73,32 @@ function UnlockedState($router, keepassReference, protectedMemory, settings, not my.cache = {} } - my.cacheSet = function(key, val) { + my.cacheSet = function (key, val) { // Refresh cache clearTimeout(cacheTimeoutId) - cacheTimeoutId = setTimeout(function() { + cacheTimeoutId = setTimeout(function () { my.clearCache() window.close() }, 120000); my.cache[key] = val; } - my.cacheGet = function(key) { + my.cacheGet = function (key) { // Refresh cache clearTimeout(cacheTimeoutId) - cacheTimeoutId = setTimeout(function() { + cacheTimeoutId = setTimeout(function () { my.clearCache() window.close() }, 120000); return my.cache[key]; } - my.clearClipboardState = function() { + my.clearClipboardState = function () { my.clipboardStatus = ""; } setTimeout(my.clearClipboardState, 60000); //clear backgroundstate after 1 minutes live - we should never be alive that long - my.autofill = function(entry) { + my.autofill = function (entry) { chrome.runtime.sendMessage({ m: "requestPermission", perms: { @@ -121,12 +121,12 @@ function UnlockedState($router, keepassReference, protectedMemory, settings, not return my.getDecryptedAttribute(entry, attr); } - my.copyPassword = function(entry) { + my.copyPassword = function (entry) { copyPart = 'password'; copyEntry = entry; document.execCommand('copy'); } - my.copyUsername = function(entry) { + my.copyUsername = function (entry) { copyPart = 'userName'; copyEntry = entry; document.execCommand('copy'); @@ -136,16 +136,16 @@ function UnlockedState($router, keepassReference, protectedMemory, settings, not copyEntry = entry; document.execCommand('copy'); } - my.gotoDetails = function(entry) { + my.gotoDetails = function (entry) { $router.route('/entry-details/' + entry.id); } - my.getDecryptedAttribute = function(entry, attributeName) { + my.getDecryptedAttribute = function (entry, attributeName) { return keepassReference.getFieldValue(entry, attributeName, my.cache.allEntries); } //listens for the copy event and does the copy - document.addEventListener('copy', function(e) { + document.addEventListener('copy', function (e) { if (!copyEntry && !copyPart) { return; //listener can get registered multiple times } @@ -166,7 +166,7 @@ function UnlockedState($router, keepassReference, protectedMemory, settings, not settings.getSetClipboardExpireInterval().then(interval => { settings.setForgetTime('clearClipboard', Date.now() + interval * 60000); notifications.push({ - text: fieldName +' copied to clipboard. Clipboard will clear in '+ interval +' minute(s).', + text: fieldName + ' copied to clipboard. Clipboard will clear in ' + interval + ' minute(s).', type: 'clipboard', }).then(() => window.close()) }) diff --git a/services/webdavFileManager.js b/services/webdavFileManager.js index 173d771f..fc1632a7 100644 --- a/services/webdavFileManager.js +++ b/services/webdavFileManager.js @@ -80,7 +80,7 @@ function WebdavFileManager(settings) { */ function listDatabases() { return isEnabled().then(enabled => { - if (!enabled ) + if (!enabled) return Promise.resolve([]) return settings.getSetWebdavDirectoryMap().then(dirMap => { let promises = [] @@ -88,7 +88,7 @@ function WebdavFileManager(settings) { for (let serverId in dirMap) dirMap[serverId].forEach(dirInfo => { promises.push(searchDirectory(serverId, dirInfo.path)) - }) + }) return Promise.all(promises).then(results => { // flatten results @@ -107,7 +107,7 @@ function WebdavFileManager(settings) { */ async function searchServer(serverId) { let serverInfo = await getServer(serverId) - if (serverInfo === null){ + if (serverInfo === null) { console.error("serverInfo not found"); return } @@ -117,22 +117,22 @@ function WebdavFileManager(settings) { /** * returns Object:[]DirInfo */ - let bfs = async function() { - let queue = [ '/' ] + let bfs = async function () { + let queue = ['/'] let foundDirectories = [] while (queue.length) { let path = queue.shift() // TODO: Implement depth better - if (path.split('/').length > SEARCH_DEPTH) + if (path.split('/').length > SEARCH_DEPTH) break; // We've exceeded search depth - let contents = await client.getDirectoryContents(path, {credentials: 'omit'}); + let contents = await client.getDirectoryContents(path, { credentials: 'omit' }); let foundKDBXInDir = false; contents.forEach(item => { if (item.type === 'directory') queue.push(item.filename) - else if (item.filename.indexOf('.kdbx') >= 1 && !foundKDBXInDir){ + else if (item.filename.indexOf('.kdbx') >= 1 && !foundKDBXInDir) { foundDirectories.push({ path: path, // the parent. serverId: serverInfo.serverId @@ -152,7 +152,7 @@ function WebdavFileManager(settings) { let dirMap = resolves[1] dirMap[serverInfo.serverId] = foundDirectories return settings.getSetWebdavDirectoryMap(dirMap) - }) + }) } //get the minimum information needed to identify this file for future retrieval @@ -174,7 +174,7 @@ function WebdavFileManager(settings) { throw 'Database no longer exists' let client = createClient(serverInfo.url, serverInfo.username, serverInfo.password) createClient.setFetchMethod(window.fetch) - return client.getFileContents(dbInfo.path, {credentials: 'omit'}) + return client.getFileContents(dbInfo.path, { credentials: 'omit' }) }) } @@ -189,7 +189,7 @@ function WebdavFileManager(settings) { return [] let client = createClient(serverInfo.url, serverInfo.username, serverInfo.password) createClient.setFetchMethod(window.fetch); - return client.getDirectoryContents(directory, {credentials: 'omit'}).then(contents => { + return client.getDirectoryContents(directory, { credentials: 'omit' }).then(contents => { // map from directory contents to DBInfo type. return contents.filter(element => { return element.filename.indexOf('.kdbx') >= 1 @@ -213,7 +213,7 @@ function WebdavFileManager(settings) { createClient.setFetchMethod((a, b) => { return window.fetch(a, b); }) - return client.getDirectoryContents('/', {credentials:'omit'}).then(contents => { + return client.getDirectoryContents('/', { credentials: 'omit' }).then(contents => { // success! let serverInfo = { url: url, @@ -223,9 +223,9 @@ function WebdavFileManager(settings) { return settings.getSetWebdavServerList().then(serverList => { serverList = serverList.length ? serverList : [] let matches = serverList.filter((elem, i, a) => { - return (elem.url == serverInfo.url - && elem.username == serverInfo.username - && elem.password == serverInfo.password) + return (elem.url == serverInfo.url + && elem.username == serverInfo.username + && elem.password == serverInfo.password) }) if (matches.length == 1) { return matches[0].serverId @@ -247,7 +247,7 @@ function WebdavFileManager(settings) { function listServers() { return settings.getSetWebdavServerList() } - + /** * return Promise --> Object:ServerInfo * @param {string} serverId diff --git a/src/Options.vue b/src/Options.vue index d4aaeaff..cd8b59f7 100644 --- a/src/Options.vue +++ b/src/Options.vue @@ -32,157 +32,157 @@ diff --git a/src/Popup.vue b/src/Popup.vue index 9f6c9ccf..14044915 100644 --- a/src/Popup.vue +++ b/src/Popup.vue @@ -25,135 +25,135 @@ diff --git a/src/components/AdvancedSettings.vue b/src/components/AdvancedSettings.vue index 14a80364..074b6dea 100644 --- a/src/components/AdvancedSettings.vue +++ b/src/components/AdvancedSettings.vue @@ -1,6 +1,153 @@ + + - - diff --git a/src/components/EntryDetails.vue b/src/components/EntryDetails.vue index f9b68bb2..442fad85 100644 --- a/src/components/EntryDetails.vue +++ b/src/components/EntryDetails.vue @@ -1,3 +1,113 @@ + + - - \ No newline at end of file diff --git a/src/components/EntryList.vue b/src/components/EntryList.vue index 8b6c1ab3..d594ae63 100644 --- a/src/components/EntryList.vue +++ b/src/components/EntryList.vue @@ -1,68 +1,42 @@ - - + + diff --git a/src/components/EntryListItem.vue b/src/components/EntryListItem.vue index 08f13a36..eedabf6d 100644 --- a/src/components/EntryListItem.vue +++ b/src/components/EntryListItem.vue @@ -1,38 +1,6 @@ - -` + + diff --git a/src/components/FilePicker.vue b/src/components/FilePicker.vue index 08e12486..690c6d25 100644 --- a/src/components/FilePicker.vue +++ b/src/components/FilePicker.vue @@ -1,73 +1,75 @@ + + - - \ No newline at end of file diff --git a/src/components/GenericProviderUi.vue b/src/components/GenericProviderUi.vue index 423af577..824dfcd3 100644 --- a/src/components/GenericProviderUi.vue +++ b/src/components/GenericProviderUi.vue @@ -1,39 +1,43 @@ + + - - \ No newline at end of file + \ No newline at end of file diff --git a/src/components/GoBack.vue b/src/components/GoBack.vue index 43a9c2a0..b6d8116b 100644 --- a/src/components/GoBack.vue +++ b/src/components/GoBack.vue @@ -1,20 +1,20 @@ + + - - \ No newline at end of file diff --git a/src/components/InfoCluster.vue b/src/components/InfoCluster.vue index 2cf48eed..fb79cb75 100644 --- a/src/components/InfoCluster.vue +++ b/src/components/InfoCluster.vue @@ -1,3 +1,12 @@ + + - - - \ No newline at end of file diff --git a/src/components/LocalPasswordFileProvider.vue b/src/components/LocalPasswordFileProvider.vue index 56e66278..49b69def 100644 --- a/src/components/LocalPasswordFileProvider.vue +++ b/src/components/LocalPasswordFileProvider.vue @@ -2,133 +2,134 @@ SharedLinkProvider: Simple http provider that can also handle Dropbox Shared Links --> - - \ No newline at end of file diff --git a/src/components/ManageDatabases.vue b/src/components/ManageDatabases.vue index 5e549924..590a838e 100644 --- a/src/components/ManageDatabases.vue +++ b/src/components/ManageDatabases.vue @@ -1,14 +1,81 @@ + + - - diff --git a/src/components/ManageKeyfiles.vue b/src/components/ManageKeyfiles.vue index c703db1b..5a26eb94 100644 --- a/src/components/ManageKeyfiles.vue +++ b/src/components/ManageKeyfiles.vue @@ -1,86 +1,89 @@ - - + + diff --git a/src/components/Messenger.vue b/src/components/Messenger.vue index 04f527fc..15157c1b 100644 --- a/src/components/Messenger.vue +++ b/src/components/Messenger.vue @@ -3,24 +3,30 @@ Displays given messages for warning, error, and success --> + + - - \ No newline at end of file diff --git a/src/components/Navbar.vue b/src/components/Navbar.vue index c8fc3213..2670b1ee 100644 --- a/src/components/Navbar.vue +++ b/src/components/Navbar.vue @@ -3,6 +3,15 @@ requires a lib/VirtualRouter, and the routes object used to initialize the virtualrouter. Active tab changes automatically based on visible route. --> + + - - \ No newline at end of file diff --git a/src/components/OauthProvider.vue b/src/components/OauthProvider.vue index 098383a0..b94eef32 100644 --- a/src/components/OauthProvider.vue +++ b/src/components/OauthProvider.vue @@ -9,84 +9,84 @@ */ If new providers are added, prefer that they are oauth providers. --> - - + + \ No newline at end of file diff --git a/src/components/OptionsStartup.vue b/src/components/OptionsStartup.vue index fa1c44a7..7f92461b 100644 --- a/src/components/OptionsStartup.vue +++ b/src/components/OptionsStartup.vue @@ -1,6 +1,11 @@ + + - - \ No newline at end of file diff --git a/src/components/Reauthorize.vue b/src/components/Reauthorize.vue index c503671f..e41f2553 100644 --- a/src/components/Reauthorize.vue +++ b/src/components/Reauthorize.vue @@ -1,68 +1,69 @@ + + - - \ No newline at end of file diff --git a/src/components/SharedLinkProvider.vue b/src/components/SharedLinkProvider.vue index 45f5169a..a419068c 100644 --- a/src/components/SharedLinkProvider.vue +++ b/src/components/SharedLinkProvider.vue @@ -2,230 +2,222 @@ SharedLinkProvider: Simple http provider that can also handle Dropbox Shared Links --> - - - \ No newline at end of file diff --git a/src/components/Startup.vue b/src/components/Startup.vue index 5537010a..e169d7e5 100644 --- a/src/components/Startup.vue +++ b/src/components/Startup.vue @@ -1,3 +1,53 @@ + + - - \ No newline at end of file diff --git a/src/components/SvgDefs.vue b/src/components/SvgDefs.vue index bff8b263..0092fdd6 100644 --- a/src/components/SvgDefs.vue +++ b/src/components/SvgDefs.vue @@ -1,132 +1,132 @@ \ No newline at end of file diff --git a/src/components/Unlock.vue b/src/components/Unlock.vue index 4272a31b..1e2624b8 100644 --- a/src/components/Unlock.vue +++ b/src/components/Unlock.vue @@ -1,3 +1,326 @@ + + - - \ No newline at end of file diff --git a/src/components/WebdavProvider.vue b/src/components/WebdavProvider.vue index 78457927..36cb36c6 100644 --- a/src/components/WebdavProvider.vue +++ b/src/components/WebdavProvider.vue @@ -2,6 +2,113 @@ SharedLinkProvider: Simple http provider that can also handle Dropbox Shared Links --> + + - +#webdav-server-input-box { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + box-sizing: border-box; - \ No newline at end of file diff --git a/src/styles/options.scss b/src/styles/options.scss index 564957f3..670075f0 100644 --- a/src/styles/options.scss +++ b/src/styles/options.scss @@ -1,10 +1,10 @@ @import './shared.scss'; -@import "../../node_modules/materialize-css/sass/components/_tabs.scss"; -@import "../../node_modules/materialize-css/sass/components/forms/_forms.scss"; -@import "../../node_modules/materialize-css/sass/components/_navbar.scss"; -@import "../../node_modules/materialize-css/sass/components/_chips.scss"; -@import "../../node_modules/materialize-css/sass/components/_buttons.scss"; +@import "~materialize-css/sass/components/_tabs.scss"; +@import "~materialize-css/sass/components/forms/_switches.scss"; +@import "~materialize-css/sass/components/_navbar.scss"; +@import "~materialize-css/sass/components/_chips.scss"; +@import "~materialize-css/sass/components/_buttons.scss"; nav { background-color: $blue; @@ -58,9 +58,6 @@ h3 { font-size: 12px; font-color: $dark-background-color; } - .switch { - min-width: 122px; - } .between { line-height: 36px; } diff --git a/src/styles/shared.scss b/src/styles/shared.scss index ec6cd16b..883d3e5d 100644 --- a/src/styles/shared.scss +++ b/src/styles/shared.scss @@ -1,9 +1,9 @@ -@import "../../node_modules/materialize-css/sass/components/_color.scss"; -@import "../../node_modules/materialize-css/sass/components/_variables.scss"; -@import "../../node_modules/materialize-css/sass/components/_normalize.scss"; -@import "../../node_modules/materialize-css/sass/components/_global.scss"; -@import "../../node_modules/materialize-css/sass/components/_typography.scss"; -@import "../../node_modules/materialize-css/sass/components/_preloader.scss"; +@import "~materialize-css/sass/components/_color.scss"; +@import "~materialize-css/sass/components/_variables.scss"; +@import "~materialize-css/sass/components/_normalize.scss"; +@import "~materialize-css/sass/components/_global.scss"; +@import "~materialize-css/sass/components/_typography.scss"; +@import "~materialize-css/sass/components/_preloader.scss"; @import "./settings.scss";