Initial port to templates

This commit is contained in:
Bernd Schoolmann 2024-05-03 19:47:04 +02:00
parent 34dd188a98
commit 6da52ba6de
No known key found for this signature in database
16 changed files with 870 additions and 802 deletions

View File

@ -0,0 +1,44 @@
using Gtk 4.0;
using Adw 1;
Adw.Window window {
default-width: 500;
default-height: 500;
Adw.ToolbarView view {
content: Box{
orientation: vertical;
ScrolledWindow {
vexpand: true;
hexpand: true;
child: Box content {
orientation: vertical;
Adw.PreferencesPage preferences_page {
title: "General";
Adw.PreferencesGroup register_browser_biometrics_group {
title: "Register Browser Biometrics";
description: "Run the following command in your terminal to set up the browser biometrics integration";
Adw.ActionRow setup_command_row {
subtitle: "flatpak run --filesystem=home --command=goldwarden com.quexten.Goldwarden setup browserbiometrics";
subtitle-selectable: true;
}
}
}
};
}
};
[top]
Adw.HeaderBar {
halign: baseline;
title-widget: Adw.WindowTitle {
title: 'Goldwarden Browser Biometrics Setup';
};
valign: start;
}
}
}

View File

@ -1,53 +1,32 @@
#!/usr/bin/env python3
import sys
import gi import gi
gi.require_version('Gtk', '4.0') gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1') 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): from gi.repository import Gtk, Adw, GLib, Gdk, Gio
from ..services import goldwarden
from threading import Thread
from .template_loader import load_template
import subprocess
from . import components
import os
class GoldwardenBrowserBiometricsSetupGuideApp(Adw.Application):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.connect('activate', self.on_activate) self.connect('activate', self.on_activate)
def on_activate(self, app): def on_activate(self, app):
self.pinentry_window = MainWindow(application=app) self.load()
self.pinentry_window.present() self.window.present()
self.app = app
class MainWindow(Gtk.ApplicationWindow): def load(self):
def __init__(self, *args, **kwargs): builder = load_template("browserbiometrics.ui")
super().__init__(*args, **kwargs) self.window = builder.get_object("window")
self.window.set_application(self)
# vertical box if __name__ == "__main__":
self.box = Gtk.Box() app = GoldwardenBrowserBiometricsSetupGuideApp(application_id="com.quexten.Goldwarden.browserbiometrics")
self.box.set_orientation(Gtk.Orientation.VERTICAL) app.run(sys.argv)
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)

69
gui/src/gui/login.blp Normal file
View File

@ -0,0 +1,69 @@
using Gtk 4.0;
using Adw 1;
Adw.Window window {
default-width: 400;
default-height: 550;
Adw.ToolbarView view {
content: Box {
orientation: vertical;
spacing: 6;
Adw.PreferencesPage preferences_page {
Adw.PreferencesGroup {
title: "Authentication";
margin-start: 12;
margin-end: 12;
margin-bottom: 12;
Adw.EntryRow email_row {
title: "Email";
text: "";
}
Adw.EntryRow client_id_row {
title: "Client ID (optional)";
text: "";
}
Adw.EntryRow client_secret_row {
title: "Client Secret (optional)";
text: "";
}
}
Adw.PreferencesGroup {
title: "Server";
margin-start: 12;
margin-end: 12;
margin-top: 12;
margin-bottom: 12;
Adw.EntryRow server_row {
title: "Server URL";
text: "https://vault.bitwarden.com";
}
}
Adw.PreferencesGroup {
margin-start: 12;
margin-end: 12;
margin-top: 12;
margin-bottom: 12;
Button login_button {
label: "Login";
styles [
"suggested-action"
]
}
}
}
};
[top]
Adw.HeaderBar {
halign: baseline;
title-widget: Adw.WindowTitle {
title: 'Goldwarden Login';
};
valign: start;
}
}
}

49
gui/src/gui/login.py Normal file
View File

@ -0,0 +1,49 @@
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
from .template_loader import load_template
import sys
import os
from ..services import goldwarden
goldwarden.create_authenticated_connection(None)
class GoldwardenLoginApp(Adw.Application):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.connect('activate', self.on_activate)
def on_activate(self, app):
self.load()
self.window.present()
def load(self):
builder = load_template("login.ui")
self.window = builder.get_object("window")
self.window.set_application(self)
self.email_row = builder.get_object("email_row")
self.client_id_row = builder.get_object("client_id_row")
self.client_secret_row = builder.get_object("client_secret_row")
self.server_row = builder.get_object("server_row")
self.login_button = builder.get_object("login_button")
self.login_button.connect("clicked", lambda x: self.on_login())
def on_login(self):
email = self.email_row.get_text()
client_id = self.client_id_row.get_text()
client_secret = self.client_secret_row.get_text()
server = self.server_row.get_text()
goldwarden.set_url(server)
if client_id != "":
goldwarden.set_client_id(client_id)
if client_secret != "":
goldwarden.set_client_secret(client_secret)
goldwarden.login_with_password(email, "")
if __name__ == "__main__":
app = GoldwardenLoginApp(application_id="com.quexten.Goldwarden.login")
app.run(sys.argv)

48
gui/src/gui/pinentry.blp Normal file
View File

@ -0,0 +1,48 @@
using Gtk 4.0;
using Adw 1;
Adw.Window window {
default-width: 400;
default-height: 700;
Adw.ToolbarView view {
content: Box {
orientation: vertical;
spacing: 6;
Label message {
label: 'Placeholder label';
}
Entry password_entry {
placeholder-text: "Enter your password";
visibility: false;
}
Box button_box {
spacing: 6;
Button cancel_button {
label: "Cancel";
hexpand: true;
}
Button approve_button {
label: "Approve";
hexpand: true;
}
}
};
[top]
Adw.HeaderBar {
halign: baseline;
title-widget: Adw.WindowTitle {
title: 'Goldwarden Pinentry';
};
valign: start;
}
}
}

View File

@ -5,65 +5,45 @@ 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 threading import Thread from threading import Thread
from .template_loader import load_template
import sys import sys
import os import os
message = sys.stdin.readline() class GoldwardenPinentryApp(Adw.Application):
class MyApp(Adw.Application):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.connect('activate', self.on_activate) self.connect('activate', self.on_activate)
def on_activate(self, app): def on_activate(self, app):
self.pinentry_window = MainWindow(application=app) self.load()
self.pinentry_window.present() self.window.present()
self.app = app
class MainWindow(Gtk.ApplicationWindow): def load(self):
def __init__(self, *args, **kwargs): builder = load_template("pinentry.ui")
super().__init__(*args, **kwargs) self.window = builder.get_object("window")
self.message_label = builder.get_object("message")
self.message_label.set_label(self.message)
self.stack = Gtk.Stack() self.cancel_button = builder.get_object("cancel_button")
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) self.cancel_button.connect("clicked", self.on_cancel_button_clicked)
self.set_child(self.stack) self.approve_button = builder.get_object("approve_button")
self.approve_button.connect("clicked", self.on_approve_button_clicked)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) self.password_entry = builder.get_object("password_entry")
self.stack.add_child(box)
label = Gtk.Label(label=message)
box.append(label)
# Create the password entry
self.password_entry = Gtk.Entry()
self.password_entry.set_placeholder_text("Enter your password") self.password_entry.set_placeholder_text("Enter your password")
self.password_entry.set_visibility(False) # Hide the password
box.append(self.password_entry)
# Create a button box for cancel and approve buttons self.window.set_application(self)
button_box = Gtk.Box(spacing=6)
box.append(button_box)
# Cancel button def on_approve_button_clicked(self, button):
cancel_button = Gtk.Button(label="Cancel") print(self.password_entry.get_text(), flush=True)
cancel_button.set_hexpand(True) # Make the button expand horizontally os._exit(0)
def on_cancel_button_clicked(button):
print("", flush=True)
os._exit(0)
cancel_button.connect("clicked", on_cancel_button_clicked)
button_box.append(cancel_button)
# Approve button def on_cancel_button_clicked(self, button):
approve_button = Gtk.Button(label="Approve") print("", flush=True)
approve_button.set_hexpand(True) # Make the button expand horizontally os._exit(0)
def on_approve_button_clicked(button):
print(self.password_entry.get_text(), flush=True)
os._exit(0)
approve_button.connect("clicked", on_approve_button_clicked)
button_box.append(approve_button)
self.set_default_size(700, 200) if __name__ == "__main__":
self.set_title("Goldwarden Pinentry") app = GoldwardenPinentryApp(application_id="com.quexten.Goldwarden.pinentry")
message = sys.stdin.readline()
app = MyApp(application_id="com.quexten.Goldwarden.pinentry") app.message = message
app.run(sys.argv) app.run(sys.argv)

View File

@ -0,0 +1,43 @@
using Gtk 4.0;
using Adw 1;
Adw.Window window {
default-width: 400;
default-height: 200;
Adw.ToolbarView view {
content: Box {
orientation: vertical;
spacing: 6;
Label message {
label: 'Dynamic Message';
}
Box button_box {
spacing: 6;
Button cancel_button {
label: "Cancel";
hexpand: true;
}
Button approve_button {
label: "Approve";
hexpand: true;
}
}
};
[top]
Adw.HeaderBar {
halign: baseline;
title-widget: Adw.WindowTitle {
title: 'Goldwarden Approval';
};
valign: start;
}
}
}

View File

@ -5,59 +5,42 @@ 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 threading import Thread from threading import Thread
from .template_loader import load_template
import sys import sys
import os import os
message = sys.stdin.readline() class GoldwardenPinentryApprovalApp(Adw.Application):
class MyApp(Adw.Application):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.connect('activate', self.on_activate) self.connect('activate', self.on_activate)
def on_activate(self, app): def on_activate(self, app):
self.pinentry_window = MainWindow(application=app) self.load()
self.pinentry_window.present() self.window.present()
self.app = app
class MainWindow(Gtk.ApplicationWindow): def load(self):
def __init__(self, *args, **kwargs): builder = load_template("pinentry_approval.ui")
super().__init__(*args, **kwargs) self.window = builder.get_object("window")
self.message_label = builder.get_object("message")
self.message_label.set_label(self.message)
self.stack = Gtk.Stack() self.cancel_button = builder.get_object("cancel_button")
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) self.cancel_button.connect("clicked", self.on_cancel_button_clicked)
self.set_child(self.stack) self.approve_button = builder.get_object("approve_button")
self.approve_button.connect("clicked", self.on_approve_button_clicked)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) self.window.set_application(self)
self.stack.add_child(box)
label = Gtk.Label(label=message) def on_approve_button_clicked(self, button):
box.append(label) print("true", flush=True)
os._exit(0)
# Create a button box for cancel and approve buttons def on_cancel_button_clicked(self, button):
button_box = Gtk.Box(spacing=6) print("false", flush=True)
box.append(button_box) os._exit(0)
# Cancel button if __name__ == "__main__":
cancel_button = Gtk.Button(label="Cancel") app = GoldwardenPinentryApprovalApp(application_id="com.quexten.Goldwarden.pinentry")
cancel_button.set_hexpand(True) # Make the button expand horizontally message = sys.stdin.readline()
def on_cancel_button_clicked(button): app.message = message
print("false", flush=True) app.run(sys.argv)
os._exit(0)
cancel_button.connect("clicked", on_cancel_button_clicked)
button_box.append(cancel_button)
# Approve button
approve_button = Gtk.Button(label="Approve")
approve_button.set_hexpand(True) # Make the button expand horizontally
def on_approve_button_clicked(button):
print("true", flush=True)
os._exit(0)
approve_button.connect("clicked", on_approve_button_clicked)
button_box.append(approve_button)
self.set_default_size(700, 200)
self.set_title("Goldwarden Approval")
app = MyApp(application_id="com.quexten.Goldwarden.pinentry")
app.run(sys.argv)

View File

@ -0,0 +1,54 @@
using Gtk 4.0;
using Adw 1;
Adw.Window window {
default-width: 400;
default-height: 700;
Adw.ToolbarView view {
content: Box{
orientation: vertical;
Box {
orientation: vertical;
Adw.PreferencesPage preferences_page {
Adw.PreferencesGroup {
Adw.EntryRow search_row {
title: "Search";
}
}
}
Adw.StatusPage status_page {
visible: true;
margin-top: 100;
title: "Type to search";
icon-name: "system-search-symbolic";
}
ListBox results_list {
margin-start: 10;
margin-end: 10;
margin-top: 10;
margin-bottom: 10;
visible: false;
styles [
"boxed-list"
]
}
}
};
[top]
Adw.HeaderBar {
halign: baseline;
title-widget: Adw.WindowTitle {
title: 'QuickAccess';
};
valign: start;
}
}
}

View File

@ -7,172 +7,111 @@ 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 threading import Thread from threading import Thread
from .template_loader import load_template
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 class GoldwardenQuickAccessApp(Adw.Application):
token = sys.stdin.readline()
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):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.logins = []
self.filtered_logins = []
self.query = ""
self.connect('activate', self.on_activate) self.connect('activate', self.on_activate)
def update_logins(self):
logins = goldwarden.get_vault_logins()
if logins == None:
os._exit(0)
return
self.app.autofill_window.logins = logins
def on_activate(self, app): def on_activate(self, app):
self.autofill_window = MainWindow(application=app) self.load()
self.autofill_window.logins = [] self.window.present()
self.autofill_window.present()
self.app = app
thread = Thread(target=self.update_logins) thread = Thread(target=self.update_logins)
thread.start() thread.start()
class MainWindow(Gtk.ApplicationWindow): def load(self):
def __init__(self, *args, **kwargs): builder = load_template("quickaccess.ui")
super().__init__(*args, **kwargs) self.window = builder.get_object("window")
self.results_list = builder.get_object("results_list")
self.status_page = builder.get_object("status_page")
self.text_view = builder.get_object("search_row")
self.text_view.connect("changed", self.on_type)
self.window.set_application(self)
self.stack = Gtk.Stack() def update(self):
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) self.update_list()
self.set_child(self.stack) self.render_list()
self.box = Gtk.Box() def autotype(self, text):
self.box.set_orientation(Gtk.Orientation.VERTICAL) goldwarden.autotype(text)
self.stack.add_named(self.box, "box") time.sleep(0.1)
os._exit(0)
self.text_view = Adw.EntryRow() def set_clipboard(self, text):
self.text_view.set_title("Search") Gdk.Display.get_clipboard(Gdk.Display.get_default()).set_content(
Gdk.ContentProvider.new_for_value(text)
)
def on_type(entry): def kill():
if len(entry.get_text()) > 1: time.sleep(0.5)
self.results_list.show() os._exit(0)
else: thread = Thread(target=kill)
self.results_list.hide() thread.start()
def update_list(self):
if self.query == "":
self.filtered_logins = []
return
self.filtered_logins = list(filter(lambda i: self.query.lower() in i["name"].lower(), self.logins))
self.starts_with_logins = list(filter(lambda i: i["name"].lower().startswith(self.query.lower()), self.filtered_logins))
self.other_logins = list(filter(lambda i: i not in self.starts_with_logins, self.filtered_logins))
self.filtered_logins = self.starts_with_logins + self.other_logins
if len(self.filtered_logins) > 7:
self.filtered_logins = self.filtered_logins[0:7]
def render_list(self):
if len(self.filtered_logins) > 1:
self.results_list.set_visible(True)
while self.results_list.get_first_child() != None: while self.results_list.get_first_child() != None:
self.results_list.remove(self.results_list.get_first_child()) self.results_list.remove(self.results_list.get_first_child())
self.status_page.set_visible(False)
else:
self.results_list.set_visible(False)
self.status_page.set_visible(True)
self.filtered_logins = list(filter(lambda i: entry.get_text().lower() in i["name"].lower(), self.logins)) for i in self.filtered_logins:
if len( self.filtered_logins) > 10: action_row = Adw.ActionRow()
self.filtered_logins = self.filtered_logins[0:10] action_row.set_title(i["name"])
self.starts_with_logins = list(filter(lambda i: i["name"].lower().startswith(entry.get_text().lower()), self.logins)) action_row.set_subtitle(i["username"])
self.other_logins = list(filter(lambda i: i not in self.starts_with_logins , self.filtered_logins)) action_row.set_icon_name("dialog-password")
self.filtered_logins = None action_row.set_activatable(True)
action_row.password = i["password"]
action_row.username = i["username"]
action_row.uuid = i["uuid"]
action_row.uri = i["uri"]
action_row.totp = i["totp"]
self.results_list.append(action_row)
self.starts_with_logins = None
self.other_logins = None
for i in self.starts_with_logins + self.other_logins : def on_type(self, entry):
action_row = Adw.ActionRow() search_query = entry.get_text()
action_row.set_title(i["name"]) self.query = search_query
action_row.set_subtitle(i["username"]) self.update()
action_row.set_icon_name("dialog-password")
action_row.set_activatable(True)
action_row.password = i["password"]
action_row.username = i["username"]
action_row.uuid = i["uuid"]
action_row.uri = i["uri"]
action_row.totp = i["totp"]
self.results_list.append(action_row)
self.starts_with_logins = None
self.other_logins = None
self.text_view.connect("changed", lambda entry: on_type(entry))
self.box.append(self.text_view)
self.results_list = Gtk.ListBox() def update_logins(self):
# margin' logins = goldwarden.get_vault_logins()
self.results_list.set_margin_start(10) print(logins)
self.results_list.set_margin_end(10) if logins == None:
self.results_list.set_margin_top(10) os._exit(0)
self.results_list.set_margin_bottom(10) return
self.results_list.hide() self.logins = logins
self.update()
keycont = Gtk.EventControllerKey() if __name__ == "__main__":
def handle_keypress(controller, keyval, keycode, state, user_data): # todo add proper method to debug this
ctrl_pressed = state & Gdk.ModifierType.CONTROL_MASK > 0 # token = sys.stdin.readline()
alt_pressed = state & Gdk.ModifierType.ALT_MASK > 0 token = "Test"
goldwarden.create_authenticated_connection(token)
if keycode == 9: app = GoldwardenQuickAccessApp(application_id="com.quexten.Goldwarden.quickaccess")
os._exit(0) app.run(sys.argv)
if keyval == 65364:
# focus results
if self.results_list.get_first_child() != None:
self.results_list.get_first_child().grab_focus()
self.results_list.select_row(self.results_list.get_first_child())
if keyval == 113:
return False
if keycode == 36:
self.hide()
autotypeThread = Thread(target=autotype, args=(f"{self.results_list.get_selected_row().username}\t{self.results_list.get_selected_row().password}",))
autotypeThread.start()
if keyval == 112:
print("pass", ctrl_pressed, alt_pressed)
if ctrl_pressed and not alt_pressed:
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:
if ctrl_pressed and not alt_pressed:
set_clipboard(self.results_list.get_selected_row().username)
if ctrl_pressed and alt_pressed:
self.hide()
autotypeThread = Thread(target=autotype, args=(self.results_list.get_selected_row().username,))
autotypeThread.start()
elif keyval == 118:
if ctrl_pressed and alt_pressed:
environment = goldwarden.get_environment()
if environment == None:
return
item_uri = environment["vault"] + "#/vault?itemId=" + self.results_list.get_selected_row().uuid
Gtk.show_uri(None, item_uri, Gdk.CURRENT_TIME)
elif keyval == 108:
if ctrl_pressed and alt_pressed:
Gtk.show_uri(None, self.results_list.get_selected_row().uri, Gdk.CURRENT_TIME)
elif keyval == 116:
totp_code = totp.totp(self.results_list.get_selected_row().totp)
if ctrl_pressed and not alt_pressed:
set_clipboard(totp_code)
if ctrl_pressed and alt_pressed:
self.hide()
autotypeThread = Thread(target=autotype, args=(totp_code,))
autotypeThread.start()
elif keyval == 102:
# focus search
self.text_view.grab_focus()
keycont.connect('key-pressed', handle_keypress, self)
self.add_controller(keycont)
self.results_list.get_style_context().add_class("boxed-list")
self.box.append(self.results_list)
self.set_default_size(700, 700)
self.set_title("Goldwarden Quick Access")
app = MyApp(application_id="com.quexten.Goldwarden.autofill-menu")
app.run(sys.argv)

View File

@ -1,84 +1,145 @@
using Gtk 4.0; using Gtk 4.0;
using Adw 1; using Adw 1;
Box content {
orientation: vertical;
Adw.Banner paused_banner {
title: 'No pin set, please set it now';
button-label: 'Set Pin';
revealed: false;
}
Adw.PreferencesPage preferences_page {
title: "General";
Adw.PreferencesGroup {
title: "Actions";
Button quick_access_button {
label: "Quick Access";
margin-top: 20;
styles [
"suggested-action"
]
}
Button login_button {
label: "Login";
margin-top: 20;
styles [
"suggested-action"
]
}
Button pin_button {
label: "Set pin";
margin-top: 20;
styles [
"suggested-action"
]
}
Button unlock_button {
label: "Unlock";
sensitive: false;
margin-top: 20;
styles [
"suggested-action"
]
}
Button logout_button {
label: "Logout";
margin-top: 20;
styles [
"destructive-action"
]
}
LinkButton {
uri: "https://github.com/quexten/goldwarden/wiki/Flatpak-Configuration";
label: "Help & Wiki";
Adw.Window window {
default-width: 400;
default-height: 700;
Adw.ToolbarView view {
content: Box{
orientation: vertical;
Stack stack {
Box set_pin_status {
orientation: vertical;
visible: false;
Adw.StatusPage {
margin-top: 100;
title: "Pin required";
icon-name: "dialog-password-symbolic";
}
Button set_pin_button {
label: "Set pin";
margin-start: 20;
margin-end: 20;
styles [
"suggested-action",
"pill"
]
}
}
Box unlock_status {
orientation: vertical;
Adw.StatusPage {
margin-top: 100;
title: "Vault locked";
icon-name: "security-high-symbolic";
}
Button unlock_button {
label: "Unlock";
margin-start: 20;
margin-end: 20;
styles [
"suggested-action",
"pill"
]
}
}
Box login_status {
orientation: vertical;
Adw.StatusPage {
margin-top: 100;
title: "Logged out";
icon-name: "system-users-symbolic";
}
Button {
label: "Log in";
margin-start: 20;
margin-end: 20;
styles [
"suggested-action",
"pill"
]
}
}
ScrolledWindow settings_view {
vexpand: true;
hexpand: true;
child: Box content {
orientation: vertical;
Adw.PreferencesPage preferences_page {
title: "General";
Adw.PreferencesGroup {
title: "Actions";
Button quickaccess_button {
label: "Quick Access";
styles [
"suggested-action"
]
}
Button update_pin_button {
label: "Update pin";
margin-top: 20;
styles [
"suggested-action"
]
}
Button lock_button {
label: "Lock";
margin-top: 20;
styles [
"suggested-action"
]
}
Button logout_button {
label: "Logout";
margin-top: 20;
styles [
"destructive-action"
]
}
}
Adw.PreferencesGroup {
title: "Vault Status";
Adw.ActionRow last_sync_row {
title: "Last Sync";
subtitle: "Never";
icon-name: "emblem-synchronizing-symbolic";
}
Adw.ActionRow websocket_connected_row {
title: "Websocked Connected";
subtitle: "False";
}
Adw.ActionRow logins_row {
title: "Vault Login Entries";
subtitle: "0";
icon-name: "dialog-password-symbolic";
}
Adw.ActionRow notes_row {
title: "Vault Notes";
subtitle: "0";
icon-name: "emblem-documents-symbolic";
}
}
}
};
}
} }
} };
Adw.PreferencesGroup {
title: "Vault Status"; [top]
Adw.ActionRow { Adw.HeaderBar {
title: "Vault Status"; halign: baseline;
subtitle: "Locked";
} title-widget: Adw.WindowTitle {
Adw.ActionRow { title: 'Goldwarden';
title: "Last Sync"; };
subtitle: "Never";
icon-name: "emblem-synchronizing-symbolic"; valign: start;
}
Adw.ActionRow {
title: "Websocked Connected";
subtitle: "False";
}
Adw.ActionRow {
title: "Vault Login Entries";
subtitle: "0";
icon-name: "dialog-password-symbolic";
}
Adw.ActionRow {
title: "Vault Notes";
subtitle: "0";
icon-name: "emblem-documents-symbolic";
}
} }
} }
} }

View File

@ -13,249 +13,13 @@ import subprocess
from . import components from . import components
import os import os
root_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir)) def run_window(name, token):
# token = sys.stdin.readline() gui_path = os.path.dirname(os.path.realpath(__file__))
# goldwarden.create_authenticated_connection(None) cwd = os.path.abspath(os.path.join(gui_path, os.pardir, os.pardir))
# print(f"Running window {name} with path {cwd}")
# def quickaccess_button_clicked(): p = subprocess.Popen(["python3", "-m", "src.gui." + name], stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd, start_new_session=True)
# 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())
# if p.stdin != None: p.stdin.flush()
# 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 SettingsWindow(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)
#
# def set_pin():
# set_pin_thread = Thread(target=goldwarden.enable_pin)
# set_pin_thread.start()
#
# self.banner = Adw.Banner()
# self.banner.set_title("No pin set, please set it now")
# self.banner.set_button_label("Set Pin")
# self.banner.connect("button-clicked", lambda banner: set_pin())
# self.box.append(self.banner)
#
# 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.action_preferences_group = Adw.PreferencesGroup()
# self.action_preferences_group.set_title("Actions")
# self.preferences_page.add(self.action_preferences_group)
#
# self.autotype_button = Gtk.Button()
# self.autotype_button.set_label("Quick Access")
# self.autotype_button.set_margin_top(10)
#
# self.autotype_button.connect("clicked", lambda button: quickaccess_button_clicked())
# self.autotype_button.get_style_context().add_class("suggested-action")
# self.action_preferences_group.add(self.autotype_button)
#
# self.login_button = Gtk.Button()
# self.login_button.set_label("Login")
# self.login_button.connect("clicked", lambda button: show_login())
# self.login_button.set_sensitive(False)
# self.login_button.set_margin_top(10)
# self.login_button.get_style_context().add_class("suggested-action")
# self.action_preferences_group.add(self.login_button)
#
# self.set_pin_button = Gtk.Button()
# self.set_pin_button.set_label("Set Pin")
# self.set_pin_button.connect("clicked", lambda button: set_pin())
# self.set_pin_button.set_margin_top(10)
# self.set_pin_button.set_sensitive(False)
# self.set_pin_button.get_style_context().add_class("suggested-action")
# self.action_preferences_group.add(self.set_pin_button)
#
# self.unlock_button = Gtk.Button()
# self.unlock_button.set_label("Unlock")
# self.unlock_button.set_margin_top(10)
# def unlock_button_clicked():
# action = goldwarden.unlock if self.unlock_button.get_label() == "Unlock" else goldwarden.lock
# unlock_thread = Thread(target=action)
# unlock_thread.start()
# self.unlock_button.connect("clicked", lambda button: unlock_button_clicked())
# # set disabled
# self.unlock_button.set_sensitive(False)
# self.action_preferences_group.add(self.unlock_button)
#
# self.logout_button = Gtk.Button()
# self.logout_button.set_label("Logout")
# self.logout_button.set_margin_top(10)
# self.logout_button.connect("clicked", lambda button: goldwarden.purge())
# self.logout_button.get_style_context().add_class("destructive-action")
# self.action_preferences_group.add(self.logout_button)
#
# self.wiki_button = Gtk.LinkButton(uri="https://github.com/quexten/goldwarden/wiki/Flatpak-Configuration")
# self.wiki_button.set_label("Help & Wiki")
# self.wiki_button.set_margin_top(10)
# self.action_preferences_group.add(self.wiki_button)
#
# self.vault_status_preferences_group = Adw.PreferencesGroup()
# self.vault_status_preferences_group.set_title("Vault Status")
# self.preferences_page.add(self.vault_status_preferences_group)
#
# self.status_row = add_action_row(self.vault_status_preferences_group, "Vault Status", "Locked")
#
# self.vault_status_icon = components.StatusIcon()
# self.vault_status_icon.set_icon("dialog-error", "error")
# self.status_row.add_prefix(self.vault_status_icon)
#
# self.last_sync_row = add_action_row(self.vault_status_preferences_group, "Last Sync", "Never", "emblem-synchronizing-symbolic")
# self.websocket_connected_row = add_action_row(self.vault_status_preferences_group, "Websocket Connected", "False")
#
# self.websocket_connected_status_icon = components.StatusIcon()
# self.websocket_connected_status_icon.set_icon("dialog-error", "error")
# 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.hamburger = Gtk.MenuButton()
# self.hamburger.set_popover(self.popover)
# self.hamburger.set_icon_name("open-menu-symbolic")
# self.header.pack_start(self.hamburger)
#
#
# def update_labels():
# pin_set = goldwarden.is_pin_enabled()
# status = goldwarden.get_vault_status()
# print("status", status)
# runtimeCfg = goldwarden.get_runtime_config()
#
# if status != None:
# if pin_set:
# self.unlock_button.set_sensitive(True)
# self.banner.set_revealed(False)
# else:
# self.unlock_button.set_sensitive(False)
# self.banner.set_revealed(True)
# logged_in = status["loggedIn"]
# if logged_in and not status["locked"]:
# self.autotype_button.set_visible(True)
# self.login_row.set_sensitive(True)
# self.notes_row.set_sensitive(True)
# self.websocket_connected_row.set_sensitive(True)
# else:
# self.autotype_button.set_visible(False)
# self.websocket_connected_row.set_sensitive(False)
# self.login_row.set_sensitive(False)
# self.notes_row.set_sensitive(False)
#
# locked = status["locked"]
# self.login_button.set_sensitive(pin_set and not locked)
# self.set_pin_button.set_sensitive(not pin_set or not locked)
# self.autotype_button.set_sensitive(not locked)
# self.status_row.set_subtitle(str("Logged in" if (logged_in and not locked) else "Logged out") if not locked else "Locked")
# if locked or not logged_in:
# self.vault_status_icon.set_icon("dialog-warning", "warning")
# else:
# self.vault_status_icon.set_icon("emblem-default", "ok")
# if not logged_in:
# self.logout_button.set_sensitive(False)
# else:
# self.logout_button.set_sensitive(True)
# self.login_row.set_subtitle(str(status["loginEntries"]))
# self.notes_row.set_subtitle(str(status["noteEntries"]))
# self.websocket_connected_row.set_subtitle("Connected" if status["websocketConnected"] else "Disconnected")
# if status["websocketConnected"]:
# self.websocket_connected_status_icon.set_icon("emblem-default", "ok")
# else:
# self.websocket_connected_status_icon.set_icon("dialog-error", "error")
# self.last_sync_row.set_subtitle(str(status["lastSynced"]))
# if status["lastSynced"].startswith("1970") or status["lastSynced"].startswith("1969"):
# self.last_sync_row.set_subtitle("Never")
# self.unlock_button.set_label("Unlock" if locked else "Lock")
# else:
# is_daemon_running = goldwarden.is_daemon_running()
# if not is_daemon_running:
# self.status_row.set_subtitle("Daemon not running")
# self.vault_status_icon.set_icon("dialog-error", "error")
#
# GLib.timeout_add(5000, update_labels)
#
# GLib.timeout_add(1000, update_labels)
self.set_default_size(400, 700)
self.set_title("Goldwarden")
def load(self):
builder = load_template("settings.ui")
self.edit_view = builder.get_object("window")
self.content = builder.get_object("content")
print(self.content)
# self.back_button = builder.get_object("back_button")
# self.back_button.connect("clicked", lambda _: self.navigate_callback("main", None))
# self.id_row = builder.get_object("id_row")
# self.name_row = builder.get_object("name_row")
# self.rclone_path_row = builder.get_object("rclone_path_row")
# self.password_row = builder.get_object("password_row")
# self.rclone_button = builder.get_object("rclone_row")
# self.rclone_button.connect("activated", lambda _: self.edit_rclone())
# self.save_button = builder.get_object("save_button")
# self.save_button.connect("clicked", lambda _: self.save())
# self.remove_button = builder.get_object("remove_button")
# self.remove_button.connect("clicked", lambda _: self.remove())
print(self.box)
print(self.content)
self.box.append(self.content)
return self.edit_view
class GoldwardenSettingsApp(Adw.Application): class GoldwardenSettingsApp(Adw.Application):
def __init__(self, **kwargs): def __init__(self, **kwargs):
@ -263,87 +27,73 @@ class GoldwardenSettingsApp(Adw.Application):
self.connect('activate', self.on_activate) self.connect('activate', self.on_activate)
def on_activate(self, app): def on_activate(self, app):
print("on activate") self.load()
self.settings_win = SettingsWindow(application=app) self.window.present()
self.settings_win.present() GLib.timeout_add(100, self.update)
self.settings_win.load()
# def show_login(): def load(self):
# dialog = Gtk.Dialog(title="Goldwarden") builder = load_template("settings.ui")
# self.window = builder.get_object("window")
# auth_preference_group = Adw.PreferencesGroup() self.window.set_application(self)
# auth_preference_group.set_title("Authentication") self.stack = builder.get_object("stack")
# auth_preference_group.set_margin_top(10)
# auth_preference_group.set_margin_bottom(10)
# auth_preference_group.set_margin_start(10)
# auth_preference_group.set_margin_end(10)
# dialog.get_content_area().append(auth_preference_group)
#
# email_entry = Adw.EntryRow()
# email_entry.set_title("Email")
# email_entry.set_text("")
# auth_preference_group.add(email_entry)
#
# client_id_entry = Adw.EntryRow()
# client_id_entry.set_title("Client ID (optional)")
# client_id_entry.set_text("")
# auth_preference_group.add(client_id_entry)
#
# client_secret_entry = Adw.EntryRow()
# client_secret_entry.set_title("Client Secret (optional)")
# client_secret_entry.set_text("")
# auth_preference_group.add(client_secret_entry)
#
# dialog.add_button("Login", Gtk.ResponseType.OK)
# def on_save(res):
# if res != Gtk.ResponseType.OK:
# return
# goldwarden.set_url(url_entry.get_text())
# goldwarden.set_client_id(client_id_entry.get_text())
# goldwarden.set_client_secret(client_secret_entry.get_text())
# def login():
# res = goldwarden.login_with_password(email_entry.get_text(), "password")
# def handle_res():
# if res == "ok":
# dialog.close()
# # elif res == "badpass":
# # bad_pass_diag = Gtk.MessageDialog(transient_for=dialog, modal=True, message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, text="Bad password")
# # bad_pass_diag.connect("response", lambda dialog, response: bad_pass_diag.close())
# # bad_pass_diag.present()
# GLib.idle_add(handle_res)
#
# login_thread = Thread(target=login)
# login_thread.start()
#
# preference_group = Adw.PreferencesGroup()
# preference_group.set_title("Config")
# preference_group.set_margin_top(10)
# preference_group.set_margin_bottom(10)
# preference_group.set_margin_start(10)
# preference_group.set_margin_end(10)
#
# dialog.get_content_area().append(preference_group)
#
# url_entry = Adw.EntryRow()
# url_entry.set_title("Base Url")
# url_entry.set_text("https://vault.bitwarden.com/")
# preference_group.add(url_entry)
#
# #ok response
# dialog.connect("response", lambda dialog, response: on_save(response))
# dialog.set_default_size(400, 200)
# dialog.set_modal(True)
# dialog.present()
isflatpak = os.path.exists("/.flatpak-info") self.set_pin_status_box = builder.get_object("set_pin_status")
pathprefix = "/app/bin/" if isflatpak else "./" self.set_pin_button = builder.get_object("set_pin_button")
css_provider = Gtk.CssProvider() self.set_pin_button.connect("clicked", lambda x: goldwarden.enable_pin())
css_provider.load_from_path(pathprefix+"style.css")
Gtk.StyleContext.add_provider_for_display(
Gdk.Display.get_default(),
css_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
app = GoldwardenSettingsApp(application_id="com.quexten.Goldwarden.settings") self.unlock_status_box = builder.get_object("unlock_status")
app.run(sys.argv) self.unlock_button = builder.get_object("unlock_button")
self.unlock_button.connect("clicked", lambda x: goldwarden.unlock())
self.login_status_box = builder.get_object("login_status")
self.settings_view = builder.get_object("settings_view")
self.lock_button = builder.get_object("lock_button")
self.lock_button.connect("clicked", lambda x: goldwarden.lock())
self.logout_button = builder.get_object("logout_button")
self.logout_button.connect("clicked", lambda x: goldwarden.purge())
self.update_pin_button = builder.get_object("update_pin_button")
self.update_pin_button.connect("clicked", lambda x: goldwarden.enable_pin())
self.quickaccess_button = builder.get_object("quickaccess_button")
self.quickaccess_button.connect("clicked", lambda x: run_window("quickaccess", "Test"))
self.last_sync_row = builder.get_object("last_sync_row")
self.websocket_connected_row = builder.get_object("websocket_connected_row")
self.logins_row = builder.get_object("logins_row")
self.notes_row = builder.get_object("notes_row")
def update(self):
self.render()
return True
def render(self):
pin_set = goldwarden.is_pin_enabled()
status = goldwarden.get_vault_status()
runtimeCfg = goldwarden.get_runtime_config()
if status == None:
is_daemon_running = goldwarden.is_daemon_running()
if not is_daemon_running:
self.status_row.set_subtitle("Daemon not running")
self.vault_status_icon.set_icon("dialog-error", "error")
return
logged_in = status["loggedIn"]
unlocked = not status["locked"]
if not pin_set:
self.stack.set_visible_child(self.set_pin_status_box)
return
if not unlocked:
self.stack.set_visible_child(self.unlock_status_box)
return
if not logged_in:
self.stack.set_visible_child(self.login_status_box)
return
self.stack.set_visible_child(self.settings_view)
self.last_sync_row.set_subtitle(status["lastSynced"])
self.websocket_connected_row.set_subtitle("Yes" if status["websocketConnected"] else "No")
self.logins_row.set_subtitle(str(status["loginEntries"]))
self.notes_row.set_subtitle(str(status["noteEntries"]))
if __name__ == "__main__":
goldwarden.create_authenticated_connection(None)
app = GoldwardenSettingsApp(application_id="com.quexten.Goldwarden.settings")
app.run(sys.argv)

95
gui/src/gui/shortcuts.blp Normal file
View File

@ -0,0 +1,95 @@
using Gtk 4.0;
using Adw 1;
Adw.Window window {
default-width: 400;
default-height: 700;
Adw.ToolbarView view {
content: Box{
orientation: vertical;
Adw.Banner pin_banner {
title: 'No pin set, please set it now';
button-label: 'Set Pin';
revealed: false;
}
ScrolledWindow {
vexpand: true;
hexpand: true;
child: Box content {
orientation: vertical;
Adw.PreferencesPage preferences_page {
title: "General";
Adw.PreferencesGroup global_preferences_group {
title: "Global Shortcuts";
Adw.ActionRow autofill_row {
title: "Autofill Shortcut";
subtitle: "Not implemented - check the wiki for manual setup";
}
}
Adw.PreferencesGroup quick_access_preferences_group {
title: "Quick Access Shortcuts";
Adw.ActionRow {
title: "Copy Username Shortcut";
subtitle: "CTRL + U";
}
Adw.ActionRow {
title: "Autotype Username Shortcut";
subtitle: "CTRL + ALT + U";
}
Adw.ActionRow {
title: "Copy Password Shortcut";
subtitle: "CTRL + P";
}
Adw.ActionRow {
title: "Autotype Password Shortcut";
subtitle: "CTRL + ALT + P";
}
Adw.ActionRow {
title: "Copy TOTP Shortcut";
subtitle: "CTRL + T";
}
Adw.ActionRow {
title: "Autotype TOTP Shortcut";
subtitle: "CTRL + ALT + T";
}
Adw.ActionRow {
title: "Launch URI Shortcut";
subtitle: "CTRL + L";
}
Adw.ActionRow {
title: "Launch Web Vault Shortcut";
subtitle: "CTRL + V";
}
Adw.ActionRow {
title: "Focus Search Shortcut";
subtitle: "F";
}
Adw.ActionRow {
title: "Quit Shortcut";
subtitle: "Esc";
}
}
}
};
}
};
[top]
Adw.HeaderBar {
halign: baseline;
title-widget: Adw.WindowTitle {
title: 'Settings';
};
valign: start;
}
}
}

View File

@ -1,80 +1,32 @@
#!/usr/bin/env python3
import sys
import gi import gi
gi.require_version('Gtk', '4.0') gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1') gi.require_version('Adw', '1')
import gc
import time from gi.repository import Gtk, Adw, GLib, Gdk, Gio
from gi.repository import Gtk, Adw, GLib, Notify, Gdk from ..services import goldwarden
from threading import Thread from threading import Thread
import sys from .template_loader import load_template
import os import subprocess
from . import components from . import components
import os
def add_action_row(parent, title, subtitle, icon=None): class GoldwardenShortcutsSetupGuideApp(Adw.Application):
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): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.connect('activate', self.on_activate) self.connect('activate', self.on_activate)
def on_activate(self, app): def on_activate(self, app):
self.pinentry_window = MainWindow(application=app) self.load()
self.pinentry_window.present() self.window.present()
self.app = app
class MainWindow(Gtk.ApplicationWindow): def load(self):
def __init__(self, *args, **kwargs): builder = load_template("shortcuts.ui")
super().__init__(*args, **kwargs) self.window = builder.get_object("window")
self.window.set_application(self)
# vertical box if __name__ == "__main__":
self.box = Gtk.Box() app = GoldwardenShortcutsSetupGuideApp(application_id="com.quexten.Goldwarden.shortcuts")
self.box.set_orientation(Gtk.Orientation.VERTICAL) app.run(sys.argv)
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)

57
gui/src/gui/ssh.blp Normal file
View File

@ -0,0 +1,57 @@
using Gtk 4.0;
using Adw 1;
Adw.Window window {
default-width: 400;
default-height: 700;
Adw.ToolbarView view {
content: Box{
orientation: vertical;
ScrolledWindow {
vexpand: true;
hexpand: true;
child: Box content {
orientation: vertical;
Adw.PreferencesPage {
title: "General";
Adw.PreferencesGroup add_ssh_key_group {
title: "Add an SSH Key";
Adw.ActionRow add_ssh_key_row {
subtitle: "flatpak run --command=goldwarden com.quexten.Goldwarden ssh add --name MY_KEY_NAME";
subtitle-selectable: true;
}
}
Adw.PreferencesGroup ssh_socket_path_group {
title: "SSH Socket Path";
description: "Add this to your environment variables";
Adw.ActionRow ssh_socket_path_row {
subtitle: "export SSH_AUTH_SOCK=/home/$USER/.var/app/com.quexten.Goldwarden/data/ssh-auth-sock";
subtitle-selectable: true;
}
}
Adw.PreferencesGroup git_signing_group {
title: "Git Signing";
description: "Check the wiki for more information";
}
}
};
}
};
[top]
Adw.HeaderBar {
halign: baseline;
title-widget: Adw.WindowTitle {
title: 'SSH Configuration';
};
valign: start;
}
}
}

View File

@ -1,67 +1,32 @@
#!/usr/bin/env python3
import sys
import gi import gi
gi.require_version('Gtk', '4.0') gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1') 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): from gi.repository import Gtk, Adw, GLib, Gdk, Gio
from ..services import goldwarden
from threading import Thread
from .template_loader import load_template
import subprocess
from . import components
import os
class GoldwardenSSHSetupGuideApp(Adw.Application):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.connect('activate', self.on_activate) self.connect('activate', self.on_activate)
def on_activate(self, app): def on_activate(self, app):
self.pinentry_window = MainWindow(application=app) self.load()
self.pinentry_window.present() self.window.present()
self.app = app
class MainWindow(Gtk.ApplicationWindow): def load(self):
def __init__(self, *args, **kwargs): builder = load_template("ssh.ui")
super().__init__(*args, **kwargs) self.window = builder.get_object("window")
self.window.set_application(self)
# vertical box if __name__ == "__main__":
self.box = Gtk.Box() app = GoldwardenSSHSetupGuideApp(application_id="com.quexten.Goldwarden.sshsetup")
self.box.set_orientation(Gtk.Orientation.VERTICAL) app.run(sys.argv)
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 environment 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)