diff --git a/gui/src/gui/browserbiometrics.blp b/gui/src/gui/browserbiometrics.blp new file mode 100644 index 0000000..af50892 --- /dev/null +++ b/gui/src/gui/browserbiometrics.blp @@ -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; + } + } +} \ No newline at end of file diff --git a/gui/src/gui/browserbiometrics.py b/gui/src/gui/browserbiometrics.py index 3199786..289f1b1 100644 --- a/gui/src/gui/browserbiometrics.py +++ b/gui/src/gui/browserbiometrics.py @@ -1,53 +1,32 @@ +#!/usr/bin/env python3 +import sys 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): +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): 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 + self.load() + self.window.present() -class MainWindow(Gtk.ApplicationWindow): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def load(self): + builder = load_template("browserbiometrics.ui") + self.window = builder.get_object("window") + self.window.set_application(self) - # 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) \ No newline at end of file +if __name__ == "__main__": + app = GoldwardenBrowserBiometricsSetupGuideApp(application_id="com.quexten.Goldwarden.browserbiometrics") + app.run(sys.argv) \ No newline at end of file diff --git a/gui/src/gui/login.blp b/gui/src/gui/login.blp new file mode 100644 index 0000000..0cc752e --- /dev/null +++ b/gui/src/gui/login.blp @@ -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; + } + } +} diff --git a/gui/src/gui/login.py b/gui/src/gui/login.py new file mode 100644 index 0000000..1f82ce0 --- /dev/null +++ b/gui/src/gui/login.py @@ -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) \ No newline at end of file diff --git a/gui/src/gui/pinentry.blp b/gui/src/gui/pinentry.blp new file mode 100644 index 0000000..236d45c --- /dev/null +++ b/gui/src/gui/pinentry.blp @@ -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; + } + } +} diff --git a/gui/src/gui/pinentry.py b/gui/src/gui/pinentry.py index a1abb0a..8cb8b8c 100644 --- a/gui/src/gui/pinentry.py +++ b/gui/src/gui/pinentry.py @@ -5,65 +5,45 @@ 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 -message = sys.stdin.readline() - -class MyApp(Adw.Application): +class GoldwardenPinentryApp(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 + self.load() + self.window.present() -class MainWindow(Gtk.ApplicationWindow): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def load(self): + builder = load_template("pinentry.ui") + self.window = builder.get_object("window") + self.message_label = builder.get_object("message") + self.message_label.set_label(self.message) + + self.cancel_button = builder.get_object("cancel_button") + self.cancel_button.connect("clicked", self.on_cancel_button_clicked) + self.approve_button = builder.get_object("approve_button") + self.approve_button.connect("clicked", self.on_approve_button_clicked) - self.stack = Gtk.Stack() - self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) - self.set_child(self.stack) - - box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) - 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 = builder.get_object("password_entry") 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 - button_box = Gtk.Box(spacing=6) - box.append(button_box) + self.window.set_application(self) - # Cancel button - cancel_button = Gtk.Button(label="Cancel") - cancel_button.set_hexpand(True) # Make the button expand horizontally - 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) + def on_approve_button_clicked(self, button): + print(self.password_entry.get_text(), flush=True) + os._exit(0) - # 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(self.password_entry.get_text(), flush=True) - os._exit(0) - approve_button.connect("clicked", on_approve_button_clicked) - button_box.append(approve_button) + def on_cancel_button_clicked(self, button): + print("", flush=True) + os._exit(0) - self.set_default_size(700, 200) - self.set_title("Goldwarden Pinentry") - -app = MyApp(application_id="com.quexten.Goldwarden.pinentry") -app.run(sys.argv) \ No newline at end of file +if __name__ == "__main__": + app = GoldwardenPinentryApp(application_id="com.quexten.Goldwarden.pinentry") + message = sys.stdin.readline() + app.message = message + app.run(sys.argv) \ No newline at end of file diff --git a/gui/src/gui/pinentry_approval.blp b/gui/src/gui/pinentry_approval.blp new file mode 100644 index 0000000..96285c4 --- /dev/null +++ b/gui/src/gui/pinentry_approval.blp @@ -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; + } + } +} \ No newline at end of file diff --git a/gui/src/gui/pinentry_approval.py b/gui/src/gui/pinentry_approval.py index 17bd2e2..0ecff53 100644 --- a/gui/src/gui/pinentry_approval.py +++ b/gui/src/gui/pinentry_approval.py @@ -5,59 +5,42 @@ 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 -message = sys.stdin.readline() - -class MyApp(Adw.Application): +class GoldwardenPinentryApprovalApp(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 + self.load() + self.window.present() -class MainWindow(Gtk.ApplicationWindow): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def load(self): + builder = load_template("pinentry_approval.ui") + self.window = builder.get_object("window") + self.message_label = builder.get_object("message") + self.message_label.set_label(self.message) + + self.cancel_button = builder.get_object("cancel_button") + self.cancel_button.connect("clicked", self.on_cancel_button_clicked) + self.approve_button = builder.get_object("approve_button") + self.approve_button.connect("clicked", self.on_approve_button_clicked) - self.stack = Gtk.Stack() - self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) - self.set_child(self.stack) + self.window.set_application(self) - box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) - self.stack.add_child(box) + def on_approve_button_clicked(self, button): + print("true", flush=True) + os._exit(0) - label = Gtk.Label(label=message) - box.append(label) + def on_cancel_button_clicked(self, button): + print("false", flush=True) + os._exit(0) - # Create a button box for cancel and approve buttons - button_box = Gtk.Box(spacing=6) - box.append(button_box) - - # Cancel button - cancel_button = Gtk.Button(label="Cancel") - cancel_button.set_hexpand(True) # Make the button expand horizontally - def on_cancel_button_clicked(button): - print("false", flush=True) - 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) \ No newline at end of file +if __name__ == "__main__": + app = GoldwardenPinentryApprovalApp(application_id="com.quexten.Goldwarden.pinentry") + message = sys.stdin.readline() + app.message = message + app.run(sys.argv) \ No newline at end of file diff --git a/gui/src/gui/quickaccess.blp b/gui/src/gui/quickaccess.blp new file mode 100644 index 0000000..688df1b --- /dev/null +++ b/gui/src/gui/quickaccess.blp @@ -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; + } + } +} \ No newline at end of file diff --git a/gui/src/gui/quickaccess.py b/gui/src/gui/quickaccess.py index f6bc976..6f70b81 100644 --- a/gui/src/gui/quickaccess.py +++ b/gui/src/gui/quickaccess.py @@ -7,172 +7,111 @@ import time from gi.repository import Gtk, Adw, GLib, Notify, Gdk from ..services import goldwarden from threading import Thread +from .template_loader import load_template import sys import os from ..services import totp Notify.init("Goldwarden") -# read line from stdin -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): +class GoldwardenQuickAccessApp(Adw.Application): def __init__(self, **kwargs): super().__init__(**kwargs) + self.logins = [] + self.filtered_logins = [] + self.query = "" 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): - self.autofill_window = MainWindow(application=app) - self.autofill_window.logins = [] - self.autofill_window.present() - self.app = app + self.load() + self.window.present() thread = Thread(target=self.update_logins) thread.start() -class MainWindow(Gtk.ApplicationWindow): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def load(self): + builder = load_template("quickaccess.ui") + 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() - self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) - self.set_child(self.stack) + def update(self): + self.update_list() + self.render_list() - self.box = Gtk.Box() - self.box.set_orientation(Gtk.Orientation.VERTICAL) - self.stack.add_named(self.box, "box") + def autotype(self, text): + goldwarden.autotype(text) + time.sleep(0.1) + os._exit(0) - self.text_view = Adw.EntryRow() - self.text_view.set_title("Search") - - def on_type(entry): - if len(entry.get_text()) > 1: - self.results_list.show() - else: - self.results_list.hide() + def set_clipboard(self, 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() + + 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: 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)) - if len( self.filtered_logins) > 10: - self.filtered_logins = self.filtered_logins[0:10] - self.starts_with_logins = list(filter(lambda i: i["name"].lower().startswith(entry.get_text().lower()), self.logins)) - self.other_logins = list(filter(lambda i: i not in self.starts_with_logins , self.filtered_logins)) - self.filtered_logins = None + for i in self.filtered_logins: + action_row = Adw.ActionRow() + action_row.set_title(i["name"]) + action_row.set_subtitle(i["username"]) + 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 - for i in self.starts_with_logins + self.other_logins : - action_row = Adw.ActionRow() - action_row.set_title(i["name"]) - action_row.set_subtitle(i["username"]) - 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) + def on_type(self, entry): + search_query = entry.get_text() + self.query = search_query + self.update() - self.results_list = Gtk.ListBox() - # margin' - self.results_list.set_margin_start(10) - self.results_list.set_margin_end(10) - self.results_list.set_margin_top(10) - self.results_list.set_margin_bottom(10) - self.results_list.hide() - - keycont = Gtk.EventControllerKey() - def handle_keypress(controller, keyval, keycode, state, user_data): - ctrl_pressed = state & Gdk.ModifierType.CONTROL_MASK > 0 - alt_pressed = state & Gdk.ModifierType.ALT_MASK > 0 - - if keycode == 9: - os._exit(0) - - 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) + def update_logins(self): + logins = goldwarden.get_vault_logins() + print(logins) + if logins == None: + os._exit(0) + return + self.logins = logins + self.update() + +if __name__ == "__main__": + # todo add proper method to debug this + # token = sys.stdin.readline() + token = "Test" + goldwarden.create_authenticated_connection(token) + app = GoldwardenQuickAccessApp(application_id="com.quexten.Goldwarden.quickaccess") + app.run(sys.argv) \ No newline at end of file diff --git a/gui/src/gui/settings.blp b/gui/src/gui/settings.blp index 38acfa9..77d341c 100644 --- a/gui/src/gui/settings.blp +++ b/gui/src/gui/settings.blp @@ -1,84 +1,145 @@ using Gtk 4.0; 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"; - Adw.ActionRow { - title: "Vault Status"; - subtitle: "Locked"; - } - Adw.ActionRow { - title: "Last Sync"; - subtitle: "Never"; - icon-name: "emblem-synchronizing-symbolic"; - } - 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"; - } + }; + + [top] + Adw.HeaderBar { + halign: baseline; + + title-widget: Adw.WindowTitle { + title: 'Goldwarden'; + }; + + valign: start; } } } diff --git a/gui/src/gui/settings.py b/gui/src/gui/settings.py index 2bbf039..6197850 100644 --- a/gui/src/gui/settings.py +++ b/gui/src/gui/settings.py @@ -13,249 +13,13 @@ import subprocess from . import components import os -root_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir)) -# token = sys.stdin.readline() -# 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) -# if p.stdin != None: -# 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 - +def run_window(name, token): + gui_path = os.path.dirname(os.path.realpath(__file__)) + cwd = os.path.abspath(os.path.join(gui_path, os.pardir, os.pardir)) + print(f"Running window {name} with path {cwd}") + p = subprocess.Popen(["python3", "-m", "src.gui." + name], stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd, start_new_session=True) + p.stdin.write(f"{token}\n".encode()) + p.stdin.flush() class GoldwardenSettingsApp(Adw.Application): def __init__(self, **kwargs): @@ -263,87 +27,73 @@ class GoldwardenSettingsApp(Adw.Application): self.connect('activate', self.on_activate) def on_activate(self, app): - print("on activate") - self.settings_win = SettingsWindow(application=app) - self.settings_win.present() - self.settings_win.load() + self.load() + self.window.present() + GLib.timeout_add(100, self.update) -# def show_login(): -# dialog = Gtk.Dialog(title="Goldwarden") -# -# auth_preference_group = Adw.PreferencesGroup() -# auth_preference_group.set_title("Authentication") -# 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() + def load(self): + builder = load_template("settings.ui") + self.window = builder.get_object("window") + self.window.set_application(self) + self.stack = builder.get_object("stack") -isflatpak = os.path.exists("/.flatpak-info") -pathprefix = "/app/bin/" if isflatpak else "./" -css_provider = Gtk.CssProvider() -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 -) + self.set_pin_status_box = builder.get_object("set_pin_status") + self.set_pin_button = builder.get_object("set_pin_button") + self.set_pin_button.connect("clicked", lambda x: goldwarden.enable_pin()) -app = GoldwardenSettingsApp(application_id="com.quexten.Goldwarden.settings") -app.run(sys.argv) + self.unlock_status_box = builder.get_object("unlock_status") + 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) diff --git a/gui/src/gui/shortcuts.blp b/gui/src/gui/shortcuts.blp new file mode 100644 index 0000000..ff3994a --- /dev/null +++ b/gui/src/gui/shortcuts.blp @@ -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; + } + } +} + + diff --git a/gui/src/gui/shortcuts.py b/gui/src/gui/shortcuts.py index 1cf4e6a..68b824f 100644 --- a/gui/src/gui/shortcuts.py +++ b/gui/src/gui/shortcuts.py @@ -1,80 +1,32 @@ +#!/usr/bin/env python3 +import sys 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 gi.repository import Gtk, Adw, GLib, Gdk, Gio +from ..services import goldwarden from threading import Thread -import sys -import os +from .template_loader import load_template +import subprocess from . import components +import os -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): +class GoldwardenShortcutsSetupGuideApp(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 + self.load() + self.window.present() -class MainWindow(Gtk.ApplicationWindow): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def load(self): + builder = load_template("shortcuts.ui") + self.window = builder.get_object("window") + self.window.set_application(self) - # 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) \ No newline at end of file +if __name__ == "__main__": + app = GoldwardenShortcutsSetupGuideApp(application_id="com.quexten.Goldwarden.shortcuts") + app.run(sys.argv) \ No newline at end of file diff --git a/gui/src/gui/ssh.blp b/gui/src/gui/ssh.blp new file mode 100644 index 0000000..9233813 --- /dev/null +++ b/gui/src/gui/ssh.blp @@ -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; + } + } +} \ No newline at end of file diff --git a/gui/src/gui/ssh.py b/gui/src/gui/ssh.py index a76a3ac..3681997 100644 --- a/gui/src/gui/ssh.py +++ b/gui/src/gui/ssh.py @@ -1,67 +1,32 @@ +#!/usr/bin/env python3 +import sys 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): +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): 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 + self.load() + self.window.present() -class MainWindow(Gtk.ApplicationWindow): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def load(self): + builder = load_template("ssh.ui") + self.window = builder.get_object("window") + self.window.set_application(self) - # 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 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) \ No newline at end of file +if __name__ == "__main__": + app = GoldwardenSSHSetupGuideApp(application_id="com.quexten.Goldwarden.sshsetup") + app.run(sys.argv) \ No newline at end of file