Skip to content

Commit

Permalink
Google Picker API (#345)
Browse files Browse the repository at this point in the history
* Google picker api

* Release 2024.8.2

* Release 2024.8.2
  • Loading branch information
subdavis authored Aug 3, 2024
1 parent 11e6dfe commit 5d0a685
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 72 deletions.
25 changes: 6 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
![Tusk](https://user-images.githubusercontent.com/25948390/45255427-a466f300-b386-11e8-9321-931934faafb4.png "Tusk Logo")

## Back in business
## 🧟 Back from the dead 🧟

[Read the blog post](https://medium.com/@brandon.a.davis/keepass-tusk-is-back-from-the-dead-a367697212fd) and stay tuned for more updates.
[Read the release notes](https://github.com/subdavis/Tusk/releases/tag/v2024.8.2) and stay tuned for more updates!

## Installation

Expand All @@ -32,29 +32,20 @@ This section provides in-depth information about how tusk works under the hood.

Tusk requires:
* `node`
* `npm`
* `yarn`

```bash
# install dependencies
yarn install

# build static DLL resources (optional)
yarn build-dll

# build for production with minification
yarn build
yarn pack:zip

# run the packer script targeted for 'chrome' and 'firefox' after build.
# this step generates the zip archives submitted to Chrome/Firefox addon marketplaces.
yarn bundle

# static reload with file watch for tests
yarn dev-tests
# Hot reload
yarn dev
```

For detailed explanation on how things work, consult the [docs for vue-loader](http://vuejs.github.io/vue-loader).

## Running tests

To run tests, first build them with `yarn build-tests` or `yarn watch-tests` then open `tests/test.html` in a browser.
Expand All @@ -77,11 +68,7 @@ I originally wanted to call it *Elephant*, as in *An elephant never forgets.* I
This is it! CKPX has been rebranded as Tusk to mark its Firefox release. The C in CKPX stood for 'Chrome'.

> Can I donate money?
I don't want your money. Take whatever you would have given me and find a local charity -- a food bank, a women's shelter or an animal shelter, for example. Enjoy your free software.

> So how can I support Tusk?
> How can I support Tusk?
If you ❤️ Tusk, please consider leaving a positive review on [the Firefox Add-on store](https://addons.mozilla.org/en-GB/firefox/addon/keepass-tusk/) or [the Chrome webstore](https://chrome.google.com/webstore/detail/fmhmiaejopepamlcjkncpgpdjichnecm) - I'll be eternally grateful.

Expand Down
6 changes: 3 additions & 3 deletions scripts/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { isDev, log, port, r } from './utils'
const manifest: Manifest.WebExtensionManifest = {
"name": "KeePass Tusk - Password Access and Autofill",
"short_name": "KeePass Tusk",
"version": "2024.7.30",
"version": "2024.8.2",
"manifest_version": 3,
"minimum_chrome_version": "88",
"description": "Readonly KeePass password database integration for Chrome and Firefox",
Expand All @@ -16,7 +16,7 @@ const manifest: Manifest.WebExtensionManifest = {
"oauth2": {
"client_id": "876467817034-al13p9m2bphgregs0rij76n1tumakcqr.apps.googleusercontent.com",
"scopes": [
"https://www.googleapis.com/auth/drive.readonly"
"https://www.googleapis.com/auth/drive.file"
]
},
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoF/A6nYIxSHW2AekTQRJga9QodwEJBTeAA5r0tW9djrTHY3Ei0FdnUE1FrH2Hx03tsj4RjXMWDHtsqMg4REJdFNzndsRKWvliGomXtxE8XByawJf/NGx0/imAtVBrHc846D/Bn4q1dRaRauqkPMKgpcHoPeg+uLTBIfAn5qPgLlvLLqNSKRg6zGYkm0iBYFiyLd1cqWjsDrVhant90W5rE7qmGQPXZudkc2ejtijuMJL4CF9BeQXOVv/9a0XzAwNbArSr+zHnNOicZPyeEnT7mujFDvLRzXvi7OPW+8mdEsm3AeagKZ6bGUuqyzwxs8XlysWqJsXBoX6tjZCGGVpQIDAQAB",
Expand All @@ -34,7 +34,7 @@ const manifest: Manifest.WebExtensionManifest = {
extension_pages: isDev
// this is required on dev for Vite script to load
? `script-src \'self\' http://localhost:${port}; object-src \'self\'`
: 'script-src \'self\'; object-src \'self\'',
: 'script-src \'self\'; object-src \'self\''
},
"options_ui": {
"page": "./dist/options.html",
Expand Down
2 changes: 1 addition & 1 deletion services/googleDrivePasswordFileManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function GoogleDrivePasswordFileManager(settings) {
key: accessTokenType,
accessTokenType: accessTokenType,
supportedFeatures: ['listDatabases'],
authUrl: `https://accounts.google.com/o/oauth2/v2/auth?response_type=token&scope=${encodeURIComponent('https://www.googleapis.com/auth/drive.readonly')}`,
authUrl: `https://accounts.google.com/o/oauth2/v2/auth?response_type=token&scope=${encodeURIComponent('https://www.googleapis.com/auth/drive.file')}`,
origins: [
"https://www.googleapis.com/*",
"https://accounts.google.com/*",
Expand Down
3 changes: 2 additions & 1 deletion services/oauthManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ function OauthManager(settings, oauth) {
state: state,
login: login,
isLoggedIn: isLoggedIn,
logout: logout
logout: logout,
getToken,
};

function getToken() {
Expand Down
73 changes: 73 additions & 0 deletions src/components/GooglePicker.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<script>
import {
ChromePromiseApi
} from '@/lib/chrome-api-promise.js'
import { manifest } from 'webextension-polyfill';
const chromePromise = ChromePromiseApi()
export default {
props: {
settings: Object,
googleDriveManager: Object,
},
data() {
return {
pickerOpen: false,
}
},
methods: {
showPicker() {
this.pickerOpen = true;
chromePromise.runtime.getManifest().then((manifest) => {
const APP_ID = manifest.static_data[this.googleDriveManager.key].client_id
this.googleDriveManager.getToken().then(accessToken => {
const iframe = document.getElementById("pickerFrame").contentWindow;
iframe.postMessage({
m: 'showPicker',
accessToken: accessToken,
appId: APP_ID,
}, '*');
});
});
},
},
mounted() {
window.addEventListener(
"message",
(event) => {
if (event.data.m === 'pickerResult') {
console.info("Picker result", event)
this.$parent.populate()
this.pickerOpen = false;
}
},
false,
);
}
}
</script>

<template>
<div>
<div class="warn pill">
<p><b>Google Drive support has updated!</b> You can now grant Tusk access to each keepass file.</p>
</div>
<div
v-show="!pickerOpen"
style="margin-top: 10px;"
>
<a
class="btn"
@click="showPicker"
>
Choose database file
</a>
</div>
<iframe
id="pickerFrame"
v-show="pickerOpen"
style="width: 100%; height: 480px; border: 4px solid gray;"
src="https://subdavis.com/Tusk/sandbox-picker.html">
</iframe>
</div>
</template>
13 changes: 8 additions & 5 deletions src/components/ManageDatabases.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import SharedLinkProvider from '@/components/SharedLinkProvider.vue'
import LocalPasswordFileProvider from '@/components/LocalPasswordFileProvider.vue'
import WebdavProvider from '@/components/WebdavProvider.vue'
import VirtualRouter from '@/lib/virtual-router.js'
import GooglePicker from '@/components/GooglePicker.vue'
export default {
props: {
dropboxFileManager: Object,
Expand All @@ -19,6 +19,7 @@ export default {
},
components: {
OauthProvider,
GooglePicker,
SharedLinkProvider,
LocalPasswordFileProvider,
WebdavProvider
Expand Down Expand Up @@ -63,10 +64,10 @@ export default {
<p>Tusk
<b>requires</b> that you enable at least one of these cloud storage providers to sync your keepass database with. Once the files appear below, they will be available to unlock within the popup window.</p>

<a class="waves-effect waves-light btn" @click="tabRouter.route('/help/me/choose')">Help me choose</a>
<a class="waves-effect waves-light btn" @click="tabRouter.route('/new/user')">I haven't made a keepass database yet.</a>
<a class="waves-effect waves-light btn mr-10" @click="tabRouter.route('/help/me/choose')">Help me choose</a>
<a class="waves-effect waves-light btn" @click="tabRouter.route('/new/user')">I don't have a KeePass Database</a>

<p id="/help/me/choose" v-show="show.help.visible">If you're unsure which to pick, the developers recommend
<p id="/help/me/choose" v-show="show.help.visible">If you're unsure which to pick, I recommend
<b>Dropbox</b>. It is easy to use and widely supported by other Keepass apps, such as
<a href="https://play.google.com/store/apps/details?id=keepass2android.keepass2android&hl=en">Keepass2Android</a> for iOS or
<a href="https://itunes.apple.com/us/app/keepass-touch/id966759076?mt=8">KeepassTouch</a>. Simply create a Dropbox account, upload your keepass database, and enable the dropbox provider below.</p>
Expand All @@ -79,7 +80,9 @@ export default {
</div>
<oauth-provider :provider-manager="sampleManager" :settings="settings"></oauth-provider>
<oauth-provider :provider-manager="dropboxFileManager" :settings="settings"></oauth-provider>
<oauth-provider :provider-manager="googleDriveManager" :settings="settings"></oauth-provider>
<oauth-provider :provider-manager="googleDriveManager" :settings="settings">
<google-picker v-bind="{ googleDriveManager, settings }" />
</oauth-provider>
<oauth-provider :provider-manager="onedriveManager" :settings="settings"></oauth-provider>
<!-- <oauth-provider :provider-manager="pCloudFileManager" :settings="settings"></oauth-provider> -->
<shared-link-provider :provider-manager="sharedUrlManager" :settings="settings"></shared-link-provider>
Expand Down
6 changes: 5 additions & 1 deletion src/components/OauthProvider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@ export default {

<template>
<div class="box-bar roomy database-manager">
<generic-provider-ui :busy="busy" :databases="databases" :loggedIn="loggedIn" :error="messages.error" :provider-manager="providerManager" :toggle-login="toggleLogin" :removeable="false" :remove-function="undefined"></generic-provider-ui>
<generic-provider-ui :busy="busy" :databases="databases" :loggedIn="loggedIn" :error="messages.error" :provider-manager="providerManager" :toggle-login="toggleLogin" :removeable="false" :remove-function="undefined">
</generic-provider-ui>
<template v-if="loggedIn">
<slot />
</template>
</div>
</template>

Expand Down
65 changes: 29 additions & 36 deletions src/components/SharedLinkProvider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,18 @@ export default {

let direct_link;
if (parsed.host === "drive.google.com") {
let id = getParameterByName("id", parsed.href);
if (!id) {
this.messages.error = "Invalid Google Drive Shared Link";
// Expected URL Structure is https://drive.google.com/file/d/FILE_ID/view?usp=sharing
let id = parsed.pathname.split("/")[3];
if (!id || !parsed.pathname.startsWith("/file/d/")) {
this.messages.error = "Invalid Google Drive Shared Link. Expected format: https://drive.google.com/file/d/FILE_ID";
return;
}
direct_link = "https://docs.google.com/uc?export=download&id=" + id;
} else {
// direct_link = "https://drive.google.com/uc?export=download&id=" + id;
this.messages.error = "Google Drive Shared Links are no longer supported. Please use the Google Drive provider."
return;
} else if (parsed.host.endsWith(".dropbox.com")) {
direct_link = parsed.href.replace("dl=0", "dl=1");
} else {
direct_link = parsed.href;
}

Expand All @@ -102,23 +107,6 @@ export default {
this.messages.error = reason.message;
});
},

/*
* Helper Methods...
*/

// https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
},


},
mounted() {
this.providerManager.isLoggedIn().then(loggedIn => {
Expand All @@ -132,6 +120,11 @@ export default {
<template>
<div class="box-bar roomy database-manager">
<generic-provider-ui :busy="busy" :databases="links" :loggedIn="loggedIn" :error="messages.error" :provider-manager="providerManager" :toggle-login="toggleLogin" :removeable="true" :remove-function="removeLink"></generic-provider-ui>
<ul class="examples">
<li><b>Dropbox URL Example</b> https://www.dropbox.com/scl/fi/FILE_ID/filename.kdbx?rlkey=&st=&dl=1</li>
<li><b>Google Drive and OneDrive</b> shared links no longer work</li>
<li>Other clould provider shared links will likely not work, but direct HTTP file links will.</li>
</ul>
<div class="url-form shared-link-box" v-if="loggedIn">
<input id="shared-link" type="text" v-model="currentUrl" placeholder="Shared Link URL">
<input id="shared-link-name" type="text" v-model="currentUrlTitle" placeholder="Database Name">
Expand All @@ -142,23 +135,23 @@ export default {

<style lang="scss" scoped>
@import "../styles/settings.scss";
.examples {
font-size: 13px;
}
.url-form {
margin-top: 15px;
&.shared-link-box {
display: flex;
justify-content: space-between;
align-content: stretch;
input {
width: 25%;
margin-right: 8px;
margin-bottom: 5px;
}
input#shared-link {
width: 48%;
}
.btn {
margin-top: 6px;
}
display: flex;
justify-content: space-between;
align-content: stretch;
input {
width: 25%;
margin-right: 8px;
margin-bottom: 5px;
}
input#shared-link {
width: 48%;
}
}
}
</style>
11 changes: 5 additions & 6 deletions src/components/Unlock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export default {
this.selectedKeyFile.name :
undefined
passwordKeyPromise.then(passwordKey => {
this.keepassService.getDecryptedData(bufferPromise, passwordKey).then(decryptedData => {
return this.keepassService.getDecryptedData(bufferPromise, passwordKey).then(decryptedData => {
let entries = decryptedData.entries
let version = decryptedData.version
let dbUsage = {
Expand All @@ -235,12 +235,11 @@ export default {
this.showResults(entries)
this.busy = false
this.masterPassword = ""
}).catch(err => {
let errmsg = err.message || "Incorrect password or keyfile"
console.error(errmsg)
this.generalMessages['error'] = errmsg
this.busy = false
})
}).catch((err) => {
console.error(err)
this.generalMessages['error'] = err.message || "invalid keyfile or KDBX file"
this.busy = false
})
}
},
Expand Down
4 changes: 4 additions & 0 deletions src/styles/shared.scss
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,8 @@ button.action-button {
height: 48px;
vertical-align: middle;
}
}

.mr-10 {
margin-right: 10px;
}

0 comments on commit 5d0a685

Please sign in to comment.