Compare commits

...

17 Commits

Author SHA1 Message Date
Bernd Schoolmann
9d5514a976
Update com.quexten.Goldwarden.metainfo.xml 2024-02-17 14:53:14 +01:00
Bernd Schoolmann
715d04c5ab
Update release.yml 2024-02-17 14:29:03 +01:00
Bernd Schoolmann
93dc64ae36
Update PKGBUILD to 0.2.12 2024-02-17 14:24:58 +01:00
Bernd Schoolmann
8b95af6458
Fix broken arch ci due to maintainer account renaming 2024-02-17 14:17:27 +01:00
Bernd Schoolmann
969ee1ff49
Update com.quexten.Goldwarden.metainfo.xml 2024-02-17 14:03:09 +01:00
Bernd Schoolmann
f99d618b33
Fix flatpak build 2024-02-17 13:44:00 +01:00
Bernd Schoolmann
f894af5fb3
Update PKGBUILD 2024-02-17 13:39:47 +01:00
Bernd Schoolmann
29bd40384b
Fix autotype and remove debug logging 2024-02-17 13:38:54 +01:00
Bernd Schoolmann
6ee67d6569
Use gtk clipboard instead of wl clipboard 2024-02-17 13:32:58 +01:00
Bernd Schoolmann
13bd29dbf9
Add browser biometrics setup window 2024-02-17 12:48:26 +01:00
Bernd Schoolmann
623fdcb719
Add flatpak biometric setup 2024-02-17 12:28:04 +01:00
Bernd Schoolmann
3b2d1fcf11
Improve biometric socket path detection 2024-02-17 12:11:30 +01:00
Bernd Schoolmann
5cc502580f
Increase read buffer to 4 MiB 2024-02-17 11:30:16 +01:00
Bernd Schoolmann
f7da373fa0
Fix crash in ui 2024-02-17 11:22:49 +01:00
Bernd Schoolmann
c15447aab3
Add ssh key and keyboard shortcut windows 2024-02-17 11:00:33 +01:00
Bernd Schoolmann
9ac1d8100a
Grant other sessions while pin session is active 2024-02-17 09:52:19 +01:00
Bernd Schoolmann
606dd176ef
Improve quickaccess keyboard navigation 2024-02-17 09:51:41 +01:00
17 changed files with 399 additions and 222 deletions

View File

@ -39,12 +39,6 @@ jobs:
with: with:
files: './goldwarden_linux_x86_64' files: './goldwarden_linux_x86_64'
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate ArchLinux PKGBUILD
uses: hapakaien/archlinux-package-action@v2
with:
flags: ''
namcap: true
updpkgsums: true
- name: Publish AUR package - name: Publish AUR package
uses: KSXGitHub/github-actions-deploy-aur@v2.7.0 uses: KSXGitHub/github-actions-deploy-aur@v2.7.0
with: with:

View File

@ -1,5 +1,5 @@
pkgname=goldwarden pkgname=goldwarden
pkgver=0.2.10 pkgver=0.2.12
pkgrel=1 pkgrel=1
pkgdesc='A feature-packed Bitwarden compatible desktop integration' pkgdesc='A feature-packed Bitwarden compatible desktop integration'
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')

View File

@ -86,19 +86,21 @@ func GetPermission(sessionType SessionType, ctx sockets.CallingContext, config *
if sessionStore.verifySession(ctx, sessionType) { if sessionStore.verifySession(ctx, sessionType) {
log.Info("Permission granted from cached session") log.Info("Permission granted from cached session")
} else { } else {
if biometrics.BiometricsWorking() { if !sessionStore.verifySession(ctx, Pin) {
biometricsApproval := biometrics.CheckBiometrics(biometricsApprovalType) if biometrics.BiometricsWorking() {
if !biometricsApproval { biometricsApproval := biometrics.CheckBiometrics(biometricsApprovalType)
return false, nil if !biometricsApproval {
} return false, nil
} else { }
log.Warn("Biometrics is not available, asking for pin") } else {
pin, err := pinentry.GetPassword("Enter PIN", "Biometrics is not available. Enter your pin to authorize this action. "+message) log.Warn("Biometrics is not available, asking for pin")
if err != nil { pin, err := pinentry.GetPassword("Enter PIN", "Biometrics is not available. Enter your pin to authorize this action. "+message)
return false, err if err != nil {
} return false, err
if !config.VerifyPin(pin) { }
return false, nil if !config.VerifyPin(pin) {
return false, nil
}
} }
} }

View File

@ -108,19 +108,40 @@ func detectAndInstallBrowsers(startPath string) error {
return nil return nil
} }
binPath, err := os.Executable()
if err != nil {
return err
}
if info.IsDir() && info.Name() == "native-messaging-hosts" { if info.IsDir() && info.Name() == "native-messaging-hosts" {
fmt.Printf("Found mozilla-like browser: %s\n", path) fmt.Printf("Found mozilla-like browser: %s\n", path)
manifest := strings.Replace(templateMozilla, "PATH", binPath, 1)
err = os.WriteFile(path+"/com.8bit.bitwarden.json", []byte(manifest), 0644) os.Chown(path+"/com.8bit.bitwarden.json", 7, 7)
os.Remove(path + "/com.8bit.bitwarden.json")
os.Chown(path+"/goldwarden-proxy.sh", 7, 7)
os.Remove(path + "/goldwarden-proxy.sh")
manifest := strings.Replace(templateMozilla, "PATH", path+"/goldwarden-proxy.sh", 1)
err = os.WriteFile(path+"/com.8bit.bitwarden.json", []byte(manifest), 0444)
if err != nil {
return err
}
err = os.WriteFile(path+"/goldwarden-proxy.sh", []byte(proxyScript), 0755)
if err != nil {
return err
}
} else if info.IsDir() && info.Name() == "NativeMessagingHosts" { } else if info.IsDir() && info.Name() == "NativeMessagingHosts" {
fmt.Printf("Found chrome-like browser: %s\n", path) fmt.Printf("Found chrome-like browser: %s\n", path)
manifest := strings.Replace(templateChrome, "PATH", binPath, 1)
err = os.WriteFile(path+"/com.8bit.bitwarden.json", []byte(manifest), 0644) os.Chown(path+"/com.8bit.bitwarden.json", 7, 7)
os.Remove(path + "/com.8bit.bitwarden.json")
os.Chown(path+"/goldwarden-proxy.sh", 7, 7)
os.Remove(path + "/goldwarden-proxy.sh")
manifest := strings.Replace(templateChrome, "PATH", path+"/goldwarden-proxy.sh", 1)
err = os.WriteFile(path+"/com.8bit.bitwarden.json", []byte(manifest), 0444)
if err != nil {
return err
}
err = os.WriteFile(path+"/goldwarden-proxy.sh", []byte(proxyScript), 0755)
if err != nil {
return err
}
} }
return err return err

View File

@ -21,3 +21,14 @@ const templateChrome = `{
"chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/" "chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/"
] ]
}` }`
const proxyScript = `#!/bin/bash
# Check if the "com.quexten.Goldwarden" Flatpak is installed
if flatpak list | grep -q "com.quexten.Goldwarden"; then
flatpak run --command=goldwarden com.quexten.Goldwarden "$@"
else
# If not installed, attempt to run the local version
goldwarden "$@"
fi
`

View File

@ -9,7 +9,7 @@ import (
"github.com/quexten/goldwarden/ipc/messages" "github.com/quexten/goldwarden/ipc/messages"
) )
const READ_BUFFER = 1 * 1024 * 1024 // 1MB const READ_BUFFER = 4 * 1024 * 1024 // 16MB
type UnixSocketClient struct { type UnixSocketClient struct {
runtimeConfig *config.RuntimeConfig runtimeConfig *config.RuntimeConfig

View File

@ -3,6 +3,8 @@
package cmd package cmd
import ( import (
"bufio"
"encoding/hex"
"os" "os"
"github.com/quexten/goldwarden/autotype" "github.com/quexten/goldwarden/autotype"
@ -15,14 +17,13 @@ var autofillCmd = &cobra.Command{
Short: "Autotype credentials", Short: "Autotype credentials",
Long: `Autotype credentials`, Long: `Autotype credentials`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
username, _ := cmd.Flags().GetString("username") reader := bufio.NewReader(os.Stdin)
// get pasword from env textHex, _ := reader.ReadString('\n')
password := os.Getenv("PASSWORD") text, _ := hex.DecodeString(textHex)
autotype.TypeString(username + "\t" + password) autotype.TypeString(string(text))
}, },
} }
func init() { func init() {
rootCmd.AddCommand(autofillCmd) rootCmd.AddCommand(autofillCmd)
autofillCmd.PersistentFlags().String("username", "", "")
} }

View File

@ -29,17 +29,10 @@ finish-args:
- --system-talk-name=org.freedesktop.PolicyKit1 - --system-talk-name=org.freedesktop.PolicyKit1
modules: modules:
- ./gui/python3-requirements.json - ./gui/python3-requirements.json
- name: wl-clipboard
buildsystem: meson
config-opts:
- -Dfishcompletiondir=no
sources:
- type: git
url: https://github.com/bugaevc/wl-clipboard.git
tag: v2.2.1
- name: goldwarden-python-ui - name: goldwarden-python-ui
buildsystem: simple buildsystem: simple
build-commands: build-commands:
- mkdir -p /app/bin
- cp -R ./gui/* /app/bin - cp -R ./gui/* /app/bin
- chmod +x /app/bin/goldwarden_ui_main.py - chmod +x /app/bin/goldwarden_ui_main.py
- install -D ./gui/com.quexten.Goldwarden.desktop /app/share/applications/com.quexten.Goldwarden.desktop - install -D ./gui/com.quexten.Goldwarden.desktop /app/share/applications/com.quexten.Goldwarden.desktop

View File

@ -36,6 +36,7 @@
<developer_name>Bernd Schoolmann</developer_name> <developer_name>Bernd Schoolmann</developer_name>
<update_contact>mail@quexten.com</update_contact> <update_contact>mail@quexten.com</update_contact>
<releases> <releases>
<release version="0.2.12" date="2024-02-17"/>
<release version="0.2.9" date="2024-01-04"/> <release version="0.2.9" date="2024-01-04"/>
<release version="0.2.7" date="2023-12-30"/> <release version="0.2.7" date="2023-12-30"/>
<release version="0.2.6" date="2023-12-30"/> <release version="0.2.6" date="2023-12-30"/>

View File

@ -0,0 +1,53 @@
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
import gc
import time
from gi.repository import Gtk, Adw, GLib, Notify, Gdk
from threading import Thread
import sys
import os
from . import components
class MyApp(Adw.Application):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.connect('activate', self.on_activate)
def on_activate(self, app):
self.pinentry_window = MainWindow(application=app)
self.pinentry_window.present()
self.app = app
class MainWindow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# vertical box
self.box = Gtk.Box()
self.box.set_orientation(Gtk.Orientation.VERTICAL)
self.set_child(self.box)
self.stack = Gtk.Stack()
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
self.box.append(self.stack)
self.preferences_page = Adw.PreferencesPage()
self.preferences_page.set_title("General")
self.stack.add_named(self.preferences_page, "preferences_page")
self.register_browser_biometrics_group = Adw.PreferencesGroup()
self.register_browser_biometrics_group.set_title("Register Browser Biometrics")
self.register_browser_biometrics_group.set_description("Run the following command in your terminal to set up the browser biometrics integration")
self.preferences_page.add(self.register_browser_biometrics_group)
self.setup_command_row = Adw.ActionRow()
self.setup_command_row.set_subtitle("flatpak run --filesystem=home --command=goldwarden com.quexten.Goldwarden setup browserbiometrics")
self.setup_command_row.set_subtitle_selectable(True)
self.register_browser_biometrics_group.add(self.setup_command_row)
self.set_default_size(700, 400)
self.set_title("Goldwarden Browser Biometrics Setup")
app = MyApp(application_id="com.quexten.Goldwarden.browserbiometrics")
app.run(sys.argv)

View File

@ -6,16 +6,32 @@ import gc
import time import time
from gi.repository import Gtk, Adw, GLib, Notify, Gdk from gi.repository import Gtk, Adw, GLib, Notify, Gdk
from ..services import goldwarden from ..services import goldwarden
from ..linux import clipboard
from threading import Thread from threading import Thread
import sys import sys
import os import os
from ..services import totp from ..services import totp
Notify.init("Goldwarden") Notify.init("Goldwarden")
# read line from stdin
token = sys.stdin.readline() token = sys.stdin.readline()
goldwarden.create_authenticated_connection(token) goldwarden.create_authenticated_connection(token)
def autotype(text):
goldwarden.autotype(text)
time.sleep(0.1)
os._exit(0)
def set_clipboard(text):
Gdk.Display.get_clipboard(Gdk.Display.get_default()).set_content(
Gdk.ContentProvider.new_for_value(text)
)
def kill():
time.sleep(0.5)
os._exit(0)
thread = Thread(target=kill)
thread.start()
class MyApp(Adw.Application): class MyApp(Adw.Application):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
@ -50,7 +66,7 @@ class MainWindow(Gtk.ApplicationWindow):
self.text_view = Adw.EntryRow() self.text_view = Adw.EntryRow()
self.text_view.set_title("Search") self.text_view.set_title("Search")
# on type func
def on_type(entry): def on_type(entry):
if len(entry.get_text()) > 1: if len(entry.get_text()) > 1:
self.results_list.show() self.results_list.show()
@ -94,12 +110,10 @@ class MainWindow(Gtk.ApplicationWindow):
keycont = Gtk.EventControllerKey() keycont = Gtk.EventControllerKey()
def handle_keypress(cotroller, keyval, keycode, state, user_data): def handle_keypress(cotroller, keyval, keycode, state, user_data):
# if ctrl is pressed ctrl_pressed = state & Gdk.ModifierType.CONTROL_MASK > 0
if state == 4: alt_pressed = state & Gdk.ModifierType.ALT_MASK > 0
print("ctrl")
if keycode == 9: if keycode == 9:
print("esc")
os._exit(0) os._exit(0)
if keyval == 65364: if keyval == 65364:
@ -112,43 +126,42 @@ class MainWindow(Gtk.ApplicationWindow):
return False return False
if keycode == 36: if keycode == 36:
print("enter")
self.hide() self.hide()
def do_autotype(username, password): autotypeThread = Thread(target=autotype, args=(f"{self.results_list.get_selected_row().username}\t{self.results_list.get_selected_row().password}",))
time.sleep(0.5)
goldwarden.autotype(username, password)
os._exit(0)
autotypeThread = Thread(target=do_autotype, args=(self.results_list.get_selected_row().username, self.results_list.get_selected_row().password,))
autotypeThread.start() autotypeThread.start()
print(self.results_list.get_selected_row().get_title())
if keyval == 112: if keyval == 112:
print("copy password") print("pass", ctrl_pressed, alt_pressed)
clipboard.write(self.results_list.get_selected_row().password) if ctrl_pressed and not alt_pressed:
Notify.Notification.new("Goldwarden", "Password Copied", "dialog-information").show() set_clipboard(self.results_list.get_selected_row().password)
if ctrl_pressed and alt_pressed:
self.hide()
autotypeThread = Thread(target=autotype, args=(self.results_list.get_selected_row().password,))
autotypeThread.start()
elif keyval == 117: elif keyval == 117:
print("copy username") if ctrl_pressed and not alt_pressed:
clipboard.write(self.results_list.get_selected_row().username) set_clipboard(self.results_list.get_selected_row().username)
notification=Notify.Notification.new("Goldwarden", "Username Copied", "dialog-information") if ctrl_pressed and alt_pressed:
notification.set_timeout(5) self.hide()
notification.show() autotypeThread = Thread(target=autotype, args=(self.results_list.get_selected_row().username,))
autotypeThread.start()
elif keyval == 118: elif keyval == 118:
print("open web vault") if ctrl_pressed and alt_pressed:
environment = goldwarden.get_environment() environment = goldwarden.get_environment()
if environment == None: if environment == None:
return return
item_uri = environment["vault"] + "#/vault?itemId=" + self.results_list.get_selected_row().uuid item_uri = environment["vault"] + "#/vault?itemId=" + self.results_list.get_selected_row().uuid
Gtk.show_uri(None, item_uri, Gdk.CURRENT_TIME) Gtk.show_uri(None, item_uri, Gdk.CURRENT_TIME)
elif keyval == 108: elif keyval == 108:
print("launch") if ctrl_pressed and alt_pressed:
print(self.results_list.get_selected_row().uri) Gtk.show_uri(None, self.results_list.get_selected_row().uri, Gdk.CURRENT_TIME)
Gtk.show_uri(None, self.results_list.get_selected_row().uri, Gdk.CURRENT_TIME)
elif keyval == 116: elif keyval == 116:
print("copy totp") totp_code = totp.totp(self.resuts_list.get_selected_row().totp)
totp_code = totp.totp(self.results_list.get_selected_row().totp) if ctrl_pressed and not alt_pressed:
clipboard.write(totp_code) set_clipboard(totp_code)
notification=Notify.Notification.new("Goldwarden", "Totp Copied", "dialog-information") if ctrl_pressed and alt_pressed:
notification.set_timeout(5) self.hide()
notification.show() autotypeThread = Thread(target=autotype, args=(totp_code,))
autotypeThread.start()
elif keyval == 102: elif keyval == 102:
# focus search # focus search
self.text_view.grab_focus() self.text_view.grab_focus()

View File

@ -5,7 +5,7 @@ gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1') gi.require_version('Adw', '1')
import gc import gc
from gi.repository import Gtk, Adw, GLib, Gdk from gi.repository import Gtk, Adw, GLib, Gdk, Gio
from ..services import goldwarden from ..services import goldwarden
from threading import Thread from threading import Thread
import subprocess import subprocess
@ -16,6 +16,35 @@ root_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file_
token = sys.stdin.readline() token = sys.stdin.readline()
goldwarden.create_authenticated_connection(None) goldwarden.create_authenticated_connection(None)
def quickaccess_button_clicked():
p = subprocess.Popen(["python3", "-m", "src.gui.quickaccess"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=root_path, start_new_session=True)
p.stdin.write(f"{token}\n".encode())
p.stdin.flush()
def shortcuts_button_clicked():
p = subprocess.Popen(["python3", "-m", "src.gui.shortcuts"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=root_path, start_new_session=True)
p.stdin.write(f"{token}\n".encode())
p.stdin.flush()
def ssh_button_clicked():
p = subprocess.Popen(["python3", "-m", "src.gui.ssh"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=root_path, start_new_session=True)
p.stdin.write(f"{token}\n".encode())
p.stdin.flush()
def browserbiometrics_button_clicked():
p = subprocess.Popen(["python3", "-m", "src.gui.browserbiometrics"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=root_path, start_new_session=True)
p.stdin.write(f"{token}\n".encode())
p.stdin.flush()
def add_action_row(parent, title, subtitle, icon=None):
row = Adw.ActionRow()
row.set_title(title)
row.set_subtitle(subtitle)
if icon != None:
row.set_icon_name(icon)
parent.add(row)
return row
class SettingsWinvdow(Gtk.ApplicationWindow): class SettingsWinvdow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -50,10 +79,7 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
self.autotype_button = Gtk.Button() self.autotype_button = Gtk.Button()
self.autotype_button.set_label("Quick Access") self.autotype_button.set_label("Quick Access")
self.autotype_button.set_margin_top(10) self.autotype_button.set_margin_top(10)
def quickaccess_button_clicked():
p = subprocess.Popen(["python3", "-m", "src.gui.quickaccess"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=root_path, start_new_session=True)
p.stdin.write(f"{token}\n".encode())
p.stdin.flush()
self.autotype_button.connect("clicked", lambda button: quickaccess_button_clicked()) self.autotype_button.connect("clicked", lambda button: quickaccess_button_clicked())
self.autotype_button.get_style_context().add_class("suggested-action") self.autotype_button.get_style_context().add_class("suggested-action")
self.action_preferences_group.add(self.autotype_button) self.action_preferences_group.add(self.autotype_button)
@ -98,128 +124,58 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
self.wiki_button.set_margin_top(10) self.wiki_button.set_margin_top(10)
self.action_preferences_group.add(self.wiki_button) self.action_preferences_group.add(self.wiki_button)
self.shortcut_preferences_group = Adw.PreferencesGroup()
self.shortcut_preferences_group.set_title("Shortcuts")
self.preferences_page.add(self.shortcut_preferences_group)
self.autofill_row = Adw.ActionRow()
self.autofill_row.set_title("Autofill Shortcut")
self.autofill_row.set_subtitle("Unavailable, please set up a shortcut in your desktop environment (README)")
self.shortcut_preferences_group.add(self.autofill_row)
self.autofill_icon = components.StatusIcon()
self.autofill_icon.set_icon("dialog-warning", "warning")
self.autofill_row.add_prefix(self.autofill_icon)
self.copy_username_shortcut_row = Adw.ActionRow()
self.copy_username_shortcut_row.set_title("Copy Username Shortcut")
self.copy_username_shortcut_row.set_subtitle("U")
self.shortcut_preferences_group.add(self.copy_username_shortcut_row)
self.copy_password_shortcut_row = Adw.ActionRow()
self.copy_password_shortcut_row.set_title("Copy Password Shortcut")
self.copy_password_shortcut_row.set_subtitle("P")
self.shortcut_preferences_group.add(self.copy_password_shortcut_row)
self.copy_totp_shortcut_row = Adw.ActionRow()
self.copy_totp_shortcut_row.set_title("Copy TOTP Shortcut")
self.copy_totp_shortcut_row.set_subtitle("T")
self.shortcut_preferences_group.add(self.copy_totp_shortcut_row)
self.launch_uri_shortcut_row = Adw.ActionRow()
self.launch_uri_shortcut_row.set_title("Launch URI Shortcut")
self.launch_uri_shortcut_row.set_subtitle("L")
self.shortcut_preferences_group.add(self.launch_uri_shortcut_row)
self.launch_web_vault_shortcut_row = Adw.ActionRow()
self.launch_web_vault_shortcut_row.set_title("Launch Web Vault Shortcut")
self.launch_web_vault_shortcut_row.set_subtitle("V")
self.shortcut_preferences_group.add(self.launch_web_vault_shortcut_row)
self.focus_search_shortcut_row = Adw.ActionRow()
self.focus_search_shortcut_row.set_title("Focus Search Shortcut")
self.focus_search_shortcut_row.set_subtitle("F")
self.shortcut_preferences_group.add(self.focus_search_shortcut_row)
self.quit_shortcut_row = Adw.ActionRow()
self.quit_shortcut_row.set_title("Quit Shortcut")
self.quit_shortcut_row.set_subtitle("Esc")
self.shortcut_preferences_group.add(self.quit_shortcut_row)
self.vault_status_preferences_group = Adw.PreferencesGroup() self.vault_status_preferences_group = Adw.PreferencesGroup()
self.vault_status_preferences_group.set_title("Vault Status") self.vault_status_preferences_group.set_title("Vault Status")
self.preferences_page.add(self.vault_status_preferences_group) self.preferences_page.add(self.vault_status_preferences_group)
self.status_row = Adw.ActionRow() self.status_row = add_action_row(self.vault_status_preferences_group, "Vault Status", "Locked")
self.status_row.set_title("Vault Status")
self.status_row.set_subtitle("Locked")
self.vault_status_preferences_group.add(self.status_row)
self.vault_status_icon = components.StatusIcon() self.vault_status_icon = components.StatusIcon()
self.vault_status_icon.set_icon("dialog-error", "error") self.vault_status_icon.set_icon("dialog-error", "error")
self.status_row.add_prefix(self.vault_status_icon) self.status_row.add_prefix(self.vault_status_icon)
self.last_sync_row = Adw.ActionRow() self.last_sync_row = add_action_row(self.vault_status_preferences_group, "Last Sync", "Never", "emblem-synchronizing-symbolic")
self.last_sync_row.set_title("Last Sync") self.websocket_connected_row = add_action_row(self.vault_status_preferences_group, "Websocket Connected", "False")
self.last_sync_row.set_subtitle("Never")
self.last_sync_row.set_icon_name("emblem-synchronizing-symbolic")
self.vault_status_preferences_group.add(self.last_sync_row)
self.websocket_connected_row = Adw.ActionRow()
self.websocket_connected_row.set_title("Websocket Connected")
self.websocket_connected_row.set_subtitle("False")
self.vault_status_preferences_group.add(self.websocket_connected_row)
self.websocket_connected_status_icon = components.StatusIcon() self.websocket_connected_status_icon = components.StatusIcon()
self.websocket_connected_status_icon.set_icon("dialog-error", "error") self.websocket_connected_status_icon.set_icon("dialog-error", "error")
self.websocket_connected_row.add_prefix(self.websocket_connected_status_icon) self.websocket_connected_row.add_prefix(self.websocket_connected_status_icon)
self.login_row = add_action_row(self.vault_status_preferences_group, "Vault Login Entries", "0", "dialog-password-symbolic")
self.notes_row = add_action_row(self.vault_status_preferences_group, "Vault Notes", "0", "emblem-documents-symbolic")
self.header = Gtk.HeaderBar()
self.set_titlebar(self.header)
action = Gio.SimpleAction.new("shortcuts", None)
action.connect("activate", lambda action, parameter: shortcuts_button_clicked())
self.add_action(action)
menu = Gio.Menu.new()
menu.append("Keyboard Shortcuts", "win.shortcuts")
self.popover = Gtk.PopoverMenu()
self.popover.set_menu_model(menu)
action = Gio.SimpleAction.new("ssh", None)
action.connect("activate", lambda action, parameter: ssh_button_clicked())
self.add_action(action)
menu.append("SSH Agent", "win.ssh")
action = Gio.SimpleAction.new("browserbiometrics", None)
action.connect("activate", lambda action, parameter: browserbiometrics_button_clicked())
self.add_action(action)
menu.append("Browser Biometrics", "win.browserbiometrics")
self.login_row = Adw.ActionRow() self.hamburger = Gtk.MenuButton()
self.login_row.set_title("Vault Login Entries") self.hamburger.set_popover(self.popover)
self.login_row.set_subtitle("0") self.hamburger.set_icon_name("open-menu-symbolic")
self.login_row.set_icon_name("dialog-password-symbolic") self.header.pack_start(self.hamburger)
self.vault_status_preferences_group.add(self.login_row)
self.notes_row = Adw.ActionRow()
self.notes_row.set_title("Vault Notes")
self.notes_row.set_subtitle("0")
self.notes_row.set_icon_name("emblem-documents-symbolic")
self.vault_status_preferences_group.add(self.notes_row)
self.preferences_group = Adw.PreferencesGroup()
self.preferences_group.set_title("Services")
self.preferences_page.add(self.preferences_group)
self.ssh_row = Adw.ActionRow()
self.ssh_row.set_title("SSH Daemon")
self.ssh_row.set_subtitle("Getting status...")
self.ssh_row.set_icon_name("emblem-default")
self.preferences_group.add(self.ssh_row)
self.goldwarden_daemon_row = Adw.ActionRow()
self.goldwarden_daemon_row.set_title("Goldwarden Daemon")
self.goldwarden_daemon_row.set_subtitle("Getting status...")
self.goldwarden_daemon_row.set_icon_name("emblem-default")
self.preferences_group.add(self.goldwarden_daemon_row)
self.login_with_device = Adw.ActionRow()
self.login_with_device.set_title("Login with device")
self.login_with_device.set_subtitle("Waiting for requests...")
self.preferences_group.add(self.login_with_device)
self.status_row = Adw.ActionRow()
self.status_row.set_title("DBUS Service")
self.status_row.set_subtitle("Listening")
self.preferences_group.add(self.status_row)
def update_labels(): def update_labels():
pin_set = goldwarden.is_pin_enabled() pin_set = goldwarden.is_pin_enabled()
status = goldwarden.get_vault_status() status = goldwarden.get_vault_status()
print("status", status) print("status", status)
runtimeCfg = goldwarden.get_runtime_config() runtimeCfg = goldwarden.get_runtime_config()
if runtimeCfg != None:
self.ssh_row.set_subtitle("Listening at "+runtimeCfg["SSHAgentSocketPath"])
self.goldwarden_daemon_row.set_subtitle("Listening at "+runtimeCfg["goldwardenSocketPath"])
if status != None: if status != None:
if pin_set: if pin_set:
@ -230,15 +186,11 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
self.banner.set_revealed(True) self.banner.set_revealed(True)
logged_in = status["loggedIn"] logged_in = status["loggedIn"]
if logged_in and not status["locked"]: if logged_in and not status["locked"]:
self.preferences_group.set_visible(True)
self.shortcut_preferences_group.set_visible(True)
self.autotype_button.set_visible(True) self.autotype_button.set_visible(True)
self.login_row.set_sensitive(True) self.login_row.set_sensitive(True)
self.notes_row.set_sensitive(True) self.notes_row.set_sensitive(True)
self.websocket_connected_row.set_sensitive(True) self.websocket_connected_row.set_sensitive(True)
else: else:
self.preferences_group.set_visible(False)
self.shortcut_preferences_group.set_visible(False)
self.autotype_button.set_visible(False) self.autotype_button.set_visible(False)
self.websocket_connected_row.set_sensitive(False) self.websocket_connected_row.set_sensitive(False)
self.login_row.set_sensitive(False) self.login_row.set_sensitive(False)
@ -276,16 +228,10 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
GLib.timeout_add(5000, update_labels) GLib.timeout_add(5000, update_labels)
GLib.timeout_add(1000, update_labels) GLib.timeout_add(1000, update_labels)
self.set_default_size(400, 700) self.set_default_size(400, 700)
self.set_title("Goldwarden") self.set_title("Goldwarden")
#add title buttons
self.title_bar = Gtk.HeaderBar()
self.set_titlebar(self.title_bar)
class MyApp(Adw.Application): class MyApp(Adw.Application):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
@ -372,6 +318,5 @@ Gtk.StyleContext.add_provider_for_display(
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
) )
app = MyApp(application_id="com.quexten.Goldwarden.settings") app = MyApp(application_id="com.quexten.Goldwarden.settings")
app.run(sys.argv) app.run(sys.argv)

80
gui/src/gui/shortcuts.py Normal file
View File

@ -0,0 +1,80 @@
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
import gc
import time
from gi.repository import Gtk, Adw, GLib, Notify, Gdk
from threading import Thread
import sys
import os
from . import components
def add_action_row(parent, title, subtitle, icon=None):
row = Adw.ActionRow()
row.set_title(title)
row.set_subtitle(subtitle)
if icon != None:
row.set_icon_name(icon)
parent.add(row)
return row
class MyApp(Adw.Application):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.connect('activate', self.on_activate)
def on_activate(self, app):
self.pinentry_window = MainWindow(application=app)
self.pinentry_window.present()
self.app = app
class MainWindow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# vertical box
self.box = Gtk.Box()
self.box.set_orientation(Gtk.Orientation.VERTICAL)
self.set_child(self.box)
self.stack = Gtk.Stack()
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
self.box.append(self.stack)
self.preferences_page = Adw.PreferencesPage()
self.preferences_page.set_title("General")
self.stack.add_named(self.preferences_page, "preferences_page")
self.global_preferences_group = Adw.PreferencesGroup()
self.global_preferences_group.set_title("Global Shortcuts")
self.preferences_page.add(self.global_preferences_group)
self.autofill_row = Adw.ActionRow()
self.autofill_row.set_title("Autofill Shortcut")
self.autofill_row.set_subtitle("Not implemented - check the wiki for manual setup")
self.global_preferences_group.add(self.autofill_row)
self.autofill_icon = components.StatusIcon()
self.autofill_icon.set_icon("dialog-warning", "warning")
self.autofill_row.add_prefix(self.autofill_icon)
self.quickaccess_preferences_group = Adw.PreferencesGroup()
self.quickaccess_preferences_group.set_title("Quick Access Shortcuts")
self.preferences_page.add(self.quickaccess_preferences_group)
add_action_row(self.quickaccess_preferences_group, "Copy Username Shortcut", "CTRL + U")
add_action_row(self.quickaccess_preferences_group, "Autotype Username Shortcut", "CTRL + ALT + U")
add_action_row(self.quickaccess_preferences_group, "Copy Password Shortcut", "CTRL + P")
add_action_row(self.quickaccess_preferences_group, "Autotype Password Shortcut", "CTRL + ALT + P")
add_action_row(self.quickaccess_preferences_group, "Copy TOTP Shortcut", "CTRL + T")
add_action_row(self.quickaccess_preferences_group, "Autotype TOTP Shortcut", "CTRL + ALT + T")
add_action_row(self.quickaccess_preferences_group, "Launch URI Shortcut", "CTRL+L")
add_action_row(self.quickaccess_preferences_group, "Launch Web Vault Shortcut", "CTRL+V")
add_action_row(self.quickaccess_preferences_group, "Focus Search Shortcut", "F")
add_action_row(self.quickaccess_preferences_group, "Quit Shortcut", "Esc")
self.set_default_size(700, 700)
self.set_title("Goldwarden Shortcuts")
app = MyApp(application_id="com.quexten.Goldwarden.shortcuts")
app.run(sys.argv)

67
gui/src/gui/ssh.py Normal file
View File

@ -0,0 +1,67 @@
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
import gc
import time
from gi.repository import Gtk, Adw, GLib, Notify, Gdk
from threading import Thread
import sys
import os
from . import components
class MyApp(Adw.Application):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.connect('activate', self.on_activate)
def on_activate(self, app):
self.pinentry_window = MainWindow(application=app)
self.pinentry_window.present()
self.app = app
class MainWindow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# vertical box
self.box = Gtk.Box()
self.box.set_orientation(Gtk.Orientation.VERTICAL)
self.set_child(self.box)
self.stack = Gtk.Stack()
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
self.box.append(self.stack)
self.preferences_page = Adw.PreferencesPage()
self.preferences_page.set_title("General")
self.stack.add_named(self.preferences_page, "preferences_page")
self.add_ssh_key_group = Adw.PreferencesGroup()
self.add_ssh_key_group.set_title("Add an SSH Key")
self.preferences_page.add(self.add_ssh_key_group)
self.add_ssh_key_row = Adw.ActionRow()
self.add_ssh_key_row.set_subtitle("flatpak run --command=goldwarden com.quexten.Goldwarden ssh add --name MY_KEY_NAME")
self.add_ssh_key_row.set_subtitle_selectable(True)
self.add_ssh_key_group.add(self.add_ssh_key_row)
self.ssh_socket_path_group = Adw.PreferencesGroup()
self.ssh_socket_path_group.set_title("SSH Socket Path")
self.ssh_socket_path_group.set_description("Add this to your your enviorment variables")
self.preferences_page.add(self.ssh_socket_path_group)
self.ssh_socket_path_row = Adw.ActionRow()
self.ssh_socket_path_row.set_subtitle("export SSH_AUTH_SOCK=/home/$USER/.var/app/com.quexten.Goldwarden/data/ssh-auth-sock")
self.ssh_socket_path_row.set_subtitle_selectable(True)
self.ssh_socket_path_group.add(self.ssh_socket_path_row)
self.git_signing_group = Adw.PreferencesGroup()
self.git_signing_group.set_title("Git Signing")
self.git_signing_group.set_description("Check the wiki for more information")
self.preferences_page.add(self.git_signing_group)
self.set_default_size(400, 700)
self.set_title("Goldwarden SSH Setup")
app = MyApp(application_id="com.quexten.Goldwarden.sshsetup")
app.run(sys.argv)

View File

@ -1,9 +0,0 @@
import subprocess
import os
def write(text):
# set path
env = os.environ.copy()
env["PATH"] = env["PATH"] + ":/app/bin"
process = subprocess.Popen(["/bin/sh", "-c", "wl-copy"], stdin=subprocess.PIPE, env=env)
process.communicate(text.encode('utf-8'))

View File

@ -131,16 +131,13 @@ def get_runtime_config():
except Exception as e: except Exception as e:
return None return None
def autotype(username, password): def autotype(text):
env = os.environ.copy() goldwarden_cmd = f"{BINARY_PATH} autotype"
# todo convert to stdin process = subprocess.Popen(goldwarden_cmd.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
env["PASSWORD"] = password text_hex = text.encode("utf-8").hex()
goldwarden_cmd = f"{BINARY_PATH} autotype --username {username}" process.stdin.write(text_hex + "\n")
result = subprocess.run(goldwarden_cmd.split(), capture_output=True, text=True, env=env) process.stdin.flush()
print(result.stderr) process.wait()
print(result.stdout)
if result.returncode != 0:
raise Exception("Failed to initialize repository, err", result.stderr)
def is_daemon_running(): def is_daemon_running():
result = send_authenticated_command(f"vault status") result = send_authenticated_command(f"vault status")

22
main.go
View File

@ -41,15 +41,18 @@ func main() {
panic(err) panic(err)
} }
if runtimeConfig.SSHAgentSocketPath == "" { if runtimeConfig.SSHAgentSocketPath == "" {
runtimeConfig.SSHAgentSocketPath = home + "/.goldwarden-ssh-agent.sock" if _, err := os.Stat(home + "/.ssh-agent-socket"); err == nil {
runtimeConfig.SSHAgentSocketPath = home + "/.ssh-agent-socket"
} else if _, err := os.Stat(home + "/.var/app/com.quexten.Goldwarden/data/ssh-auth-sock"); err == nil {
runtimeConfig.SSHAgentSocketPath = home + "/.var/app/com.quexten.Goldwarden/data/ssh-auth-sock"
}
} }
if runtimeConfig.GoldwardenSocketPath == "" { if runtimeConfig.GoldwardenSocketPath == "" {
runtimeConfig.GoldwardenSocketPath = home + "/.goldwarden.sock" if _, err := os.Stat(home + "/.goldwarden.sock"); err == nil {
} runtimeConfig.GoldwardenSocketPath = home + "/.goldwarden.sock"
} else if _, err := os.Stat(home + "/.var/app/com.quexten.Goldwarden/data/goldwarden.sock"); err == nil {
if len(os.Args) > 1 && (strings.Contains(os.Args[1], "com.8bit.bitwarden.json") || strings.Contains(os.Args[1], "chrome-extension://")) { runtimeConfig.GoldwardenSocketPath = home + "/.var/app/com.quexten.Goldwarden/data/goldwarden.sock"
browserbiometrics.Main(&runtimeConfig) }
return
} }
_, err = os.Stat("/.flatpak-info") _, err = os.Stat("/.flatpak-info")
@ -62,5 +65,10 @@ func main() {
runtimeConfig.GoldwardenSocketPath = userHome + "/.var/app/com.quexten.Goldwarden/data/goldwarden.sock" runtimeConfig.GoldwardenSocketPath = userHome + "/.var/app/com.quexten.Goldwarden/data/goldwarden.sock"
} }
if len(os.Args) > 1 && (strings.Contains(os.Args[1], "com.8bit.bitwarden.json") || strings.Contains(os.Args[1], "chrome-extension://")) {
browserbiometrics.Main(&runtimeConfig)
return
}
cmd.Execute(runtimeConfig) cmd.Execute(runtimeConfig)
} }