diff --git a/.gitignore b/.gitignore index db73830..53cdaf5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vscode goldwarden -__pycache__ \ No newline at end of file +__pycache__ +.flatpak-builder \ No newline at end of file diff --git a/cmd/autofill.go b/cmd/autofill.go index e14a1b3..c569e70 100644 --- a/cmd/autofill.go +++ b/cmd/autofill.go @@ -3,6 +3,8 @@ package cmd import ( + "os" + "github.com/quexten/goldwarden/autotype" "github.com/spf13/cobra" ) @@ -13,7 +15,8 @@ var autofillCmd = &cobra.Command{ Long: `Autotype credentials`, Run: func(cmd *cobra.Command, args []string) { username, _ := cmd.Flags().GetString("username") - password, _ := cmd.Flags().GetString("password") + // get pasword from env + password := os.Getenv("PASSWORD") autotype.TypeString(username + "\t" + password) }, } @@ -21,5 +24,4 @@ var autofillCmd = &cobra.Command{ func init() { rootCmd.AddCommand(autofillCmd) autofillCmd.PersistentFlags().String("username", "", "") - autofillCmd.PersistentFlags().String("password", "", "") } diff --git a/com.quexten.Goldwarden.yml b/com.quexten.Goldwarden.yml index d571d52..a5994ab 100644 --- a/com.quexten.Goldwarden.yml +++ b/com.quexten.Goldwarden.yml @@ -9,6 +9,8 @@ finish-args: - --socket=wayland - --socket=fallback-x11 - --filesystem=home + # todo remove this + - --socket=session-bus modules: - name: goldwarden buildsystem: simple @@ -16,13 +18,11 @@ modules: build-args: - "--share=network" build-commands: - - pip3 install --prefix=/app timeago secretstorage pycryptodome chacha20 + - pip3 install --prefix=/app tendo - install -D main.py /app/bin/main.py - install -D com.quexten.Goldwarden.desktop /app/share/applications/com.quexten.Goldwarden.desktop - install -D goldwarden.svg /app/share/icons/hicolor/scalable/apps/com.quexten.Goldwarden.svg - - cp -R ui/ /app/bin/gui/ - - cp -R backend/ /app/bin/backend/ - - cp -R event_monitors/ /app/bin/event_monitors/ + - cp -R ./ /app/bin/ sources: - type: dir path: ui \ No newline at end of file diff --git a/ui/autofill.py b/ui/autofill.py new file mode 100644 index 0000000..4836769 --- /dev/null +++ b/ui/autofill.py @@ -0,0 +1,108 @@ +import gi +gi.require_version('Gtk', '4.0') +gi.require_version('Adw', '1') +import gc +import time +from gi.repository import Gtk, Adw, GLib +import goldwarden +import clipboard +from threading import Thread +import sys +import os + +class MyApp(Adw.Application): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.connect('activate', self.on_activate) + + def on_activate(self, app): + self.autofill_window = MainWindow(application=app) + self.autofill_window.logins = [] + self.autofill_window.present() + logins = goldwarden.get_vault_logins() + if logins == None: + return + app.autofill_window.logins = logins + +class MainWindow(Gtk.ApplicationWindow): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.stack = Gtk.Stack() + self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) + self.set_child(self.stack) + + self.box = Gtk.Box() + self.box.set_orientation(Gtk.Orientation.VERTICAL) + self.stack.add_named(self.box, "box") + + self.text_view = Adw.EntryRow() + self.text_view.set_title("Search") + # on type func + def on_type(entry): + if len(entry.get_text()) > 1: + self.history_list.show() + else: + self.history_list.hide() + + while self.history_list.get_first_child() != None: + self.history_list.remove(self.history_list.get_first_child()) + + 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.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"] + self.history_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.history_list = Gtk.ListBox() + # margin' + self.history_list.set_margin_start(10) + self.history_list.set_margin_end(10) + self.history_list.set_margin_top(10) + self.history_list.set_margin_bottom(10) + self.history_list.hide() + + keycont = Gtk.EventControllerKey() + def handle_keypress(cotroller, keyval, keycode, state, user_data): + if keycode == 36: + print("enter") + self.hide() + def do_autotype(username, password): + time.sleep(0.5) + goldwarden.autotype(username, password) + GLib.idle_add(lambda: self.show()) + autotypeThread = Thread(target=do_autotype, args=(self.history_list.get_selected_row().username, self.history_list.get_selected_row().password,)) + autotypeThread.start() + print(self.history_list.get_selected_row().get_title()) + if keyval == 112: + print("copy password") + clipboard.write(self.history_list.get_selected_row().password) + elif keyval == 117: + print("copy username") + clipboard.write(self.history_list.get_selected_row().username) + + keycont.connect('key-pressed', handle_keypress, self) + self.add_controller(keycont) + + self.history_list.get_style_context().add_class("boxed-list") + self.box.append(self.history_list) + self.set_default_size(700, 700) + self.set_title("Goldwarden") + +app = MyApp(application_id="com.quexten.Goldwarden.autofill") +app.run(sys.argv) \ No newline at end of file diff --git a/ui/background.py b/ui/background.py new file mode 100644 index 0000000..f684b4f --- /dev/null +++ b/ui/background.py @@ -0,0 +1,58 @@ +import gi +gi.require_version('Gtk', '4.0') +gi.require_version('Adw', '1') +import gc +from gi.repository import Gtk, Adw, GLib, Gio +from random import randint +import time + + +def receive_autostart(self, *args): + print("autostart enabled..!?") + print(args) + +def request_autostart(): + bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) + proxy = Gio.DBusProxy.new_sync( + bus, + Gio.DBusProxyFlags.NONE, + None, + 'org.freedesktop.portal.Desktop', + '/org/freedesktop/portal/desktop', + 'org.freedesktop.portal.Background', + None, + ) + + token = 0 + randint(10000000, 20000000) + options = { + 'handle_token': GLib.Variant('s', f'com/quexten/Goldwarden/{token}'), + 'reason': GLib.Variant('s', ('Autostart Goldwarden in the background.')), + 'autostart': GLib.Variant('b', True), + 'commandline': GLib.Variant('as', ['main.py', '--hidden']), + 'dbus-activatable': GLib.Variant('b', False), + } + + try: + request = proxy.RequestBackground('(sa{sv})', "", options) + if request is None: + raise Exception( + "Registering with background portal failed." + ) + + bus.signal_subscribe( + 'org.freedesktop.portal.Desktop', + 'org.freedesktop.portal.Request', + 'Response', + request, + None, + Gio.DBusSignalFlags.NO_MATCH_RULE, + receive_autostart, + None, + ) + except Exception as e: + print(e) + +request_autostart() + +loop = GLib.MainLoop() +loop.run() diff --git a/ui/com.quexten.Goldwarden.desktop b/ui/com.quexten.Goldwarden.desktop index 1f6cf1e..6f154c5 100644 --- a/ui/com.quexten.Goldwarden.desktop +++ b/ui/com.quexten.Goldwarden.desktop @@ -5,6 +5,6 @@ Keywords=backup;recovery; Exec=main.py Terminal=false Type=Application -Icon=com.quexten.goldwarden.svg +Icon=com.quexten.Goldwarden.svg StartupNotify=true Categories=GNOME;GTK;Password Manager; \ No newline at end of file diff --git a/ui/goldwarden.py b/ui/goldwarden.py index 657c3ee..72ef2f5 100644 --- a/ui/goldwarden.py +++ b/ui/goldwarden.py @@ -1,5 +1,6 @@ import subprocess import json +import os BINARY_PATH = "/home/quexten/go/src/github.com/quexten/goldwarden/goldwarden" @@ -99,7 +100,10 @@ def get_vault_logins(): return None def autotype(username, password): - restic_cmd = f"{BINARY_PATH} autotype --username {username} --password {password}" - result = subprocess.run(restic_cmd.split(), capture_output=True, text=True) + # environment + env = os.environ.copy() + env["PASSWORD"] = password + restic_cmd = f"{BINARY_PATH} autotype --username {username}" + result = subprocess.run(restic_cmd.split(), capture_output=True, text=True, env=env) if result.returncode != 0: raise Exception("Failed to initialize repository, err", result.stderr) \ No newline at end of file diff --git a/ui/goldwarden.svg b/ui/goldwarden.svg new file mode 100644 index 0000000..c26e20f --- /dev/null +++ b/ui/goldwarden.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/main.py b/ui/main.py index 4961adc..c3480a3 100644 --- a/ui/main.py +++ b/ui/main.py @@ -1,307 +1,29 @@ #!/usr/bin/python -import sys -import gi -gi.require_version('Gtk', '4.0') -gi.require_version('Adw', '1') -import gc - -from gi.repository import Gtk, Adw, Gdk, Graphene, Gsk, Gio, GLib, GObject -import monitors.dbus_autofill_monitor -import goldwarden -import clipboard import time -from threading import Thread +import subprocess +from tendo import singleton +import monitors.dbus_autofill_monitor +import sys -class MainWindow(Gtk.ApplicationWindow): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) +try: + subprocess.Popen(["python3", "/app/bin/background.py"], start_new_session=True) +except: + pass - self.stack = Gtk.Stack() - self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) - self.set_child(self.stack) +if "--hidden" not in sys.argv: + try: + subprocess.Popen(["python3", "/app/bin/settings.py"], start_new_session=True) + except: + pass - self.box = Gtk.Box() - self.box.set_orientation(Gtk.Orientation.VERTICAL) - self.stack.add_named(self.box, "box") +try: + me = singleton.SingleInstance() +except: + exit() - - self.text_view = Adw.EntryRow() - self.text_view.set_title("Search") - # on type func - def on_type(entry): - if len(entry.get_text()) > 1: - self.history_list.show() - else: - self.history_list.hide() - - while self.history_list.get_first_child() != None: - self.history_list.remove(self.history_list.get_first_child()) - - 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.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"] - self.history_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.history_list = Gtk.ListBox() - # margin' - self.history_list.set_margin_start(10) - self.history_list.set_margin_end(10) - self.history_list.set_margin_top(10) - self.history_list.set_margin_bottom(10) - self.history_list.hide() - - keycont = Gtk.EventControllerKey() - def handle_keypress(controller, keyval, keycode, state, user_data): - if keycode == 36: - print("enter") - self.hide() - def do_autotype(username, password): - time.sleep(0.5) - goldwarden.autotype(username, password) - GLib.idle_add(lambda: self.show()) - autotypeThread = Thread(target=do_autotype, args=(self.history_list.get_selected_row().username, self.history_list.get_selected_row().password,)) - autotypeThread.start() - print(self.history_list.get_selected_row().get_title()) - if keyval == 112: - print("copy password") - clipboard.write(self.history_list.get_selected_row().password) - elif keyval == 117: - print("copy username") - clipboard.write(self.history_list.get_selected_row().username) - - keycont.connect('key-pressed', handle_keypress, self) - self.add_controller(keycont) - - self.history_list.get_style_context().add_class("boxed-list") - self.box.append(self.history_list) - self.set_default_size(700, 700) - self.set_title("Goldwarden") - - def on_close(window): - while self.history_list.get_first_child() != None: - self.history_list.remove(self.history_list.get_first_child()) - window.hide() - gc.collect() - return True - self.connect("close-request", on_close) - - def show(self): - for i in range(0, 5): - action_row = Adw.ActionRow() - action_row.set_title("aaa") - action_row.set_subtitle("Test") - self.history_list.append(action_row) - - -class SettingsWinvdow(Gtk.ApplicationWindow): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.stack = Gtk.Stack() - self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) - self.set_child(self.stack) - - self.preferences_page = Adw.PreferencesPage() - self.preferences_page.set_title("General") - self.stack.add_named(self.preferences_page, "preferences_page") - - self.preferences_group = Adw.PreferencesGroup() - self.preferences_group.set_title("Services") - self.preferences_page.add(self.preferences_group) - - self.ssh_row = Adw.ActionRow() - self.ssh_row.set_title("SSH Daemon") - self.ssh_row.set_subtitle("Listening at ~/.goldwarden-ssh-agent.sock") - self.preferences_group.add(self.ssh_row) - - self.login_with_device = Adw.ActionRow() - self.login_with_device.set_title("Login with device") - self.login_with_device.set_subtitle("Waiting for requests...") - self.preferences_group.add(self.login_with_device) - - self.autofill_row = Adw.ActionRow() - self.autofill_row.set_title("Autofill Shortcut") - self.autofill_row.set_subtitle("Unavailable, please set up a shortcut in your desktop environment (README)") - self.preferences_group.add(self.autofill_row) - - self.status_row = Adw.ActionRow() - self.status_row.set_title("DBUS Service") - self.status_row.set_subtitle("Listening") - self.preferences_group.add(self.status_row) - - self.status_row = Adw.ActionRow() - self.status_row.set_title("Vault Status") - self.status_row.set_subtitle("Locked") - self.preferences_group.add(self.status_row) - - self.login_row = Adw.ActionRow() - self.login_row.set_title("Vault Login Entries") - self.login_row.set_subtitle("0") - self.preferences_group.add(self.login_row) - - self.notes_row = Adw.ActionRow() - self.notes_row.set_title("Vault Notes") - self.notes_row.set_subtitle("0") - self.preferences_group.add(self.notes_row) - - 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.preferences_group.add(self.login_button) - - self.set_pin_button = Gtk.Button() - self.set_pin_button.set_label("Set Pin") - def set_pin(): - set_pin_thread = Thread(target=goldwarden.enable_pin) - set_pin_thread.start() - 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.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.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.preferences_group.add(self.logout_button) - - def update_labels(): - pin_set = goldwarden.is_pin_enabled() - status = goldwarden.get_vault_status() - 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.status_row.set_subtitle(str("Unlocked" if not locked else "Locked")) - self.login_row.set_subtitle(str(status["loginEntries"])) - self.notes_row.set_subtitle(str(status["noteEntries"])) - self.unlock_button.set_sensitive(True) - self.unlock_button.set_label("Unlock" if locked else "Lock") - GLib.timeout_add(1000, update_labels) - - GLib.timeout_add(1000, update_labels) - self.set_default_size(400, 700) - self.set_title("Goldwarden") - - - #add title buttons - self.title_bar = Gtk.HeaderBar() - self.set_titlebar(self.title_bar) - -class MyApp(Adw.Application): - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.connect('activate', self.on_activate) - - def on_activate(self, app): - if hasattr(self, "win") is False: - app.hold() - self.win = MainWindow(application=app) - self.win.set_hide_on_close(True) - self.win.hide() - self.settings_win = SettingsWinvdow(application=app) - self.settings_win.set_hide_on_close(True) - self.settings_win.present() - -app = MyApp(application_id="com.quexten.Goldwarden") def on_autofill(): - logins = goldwarden.get_vault_logins() - if logins == None: - return - app.win.logins = logins - app.win.show() - app.win.present() + subprocess.Popen(["python3", "/app/bin/autofill.py"], start_new_session=True) monitors.dbus_autofill_monitor.on_autofill = on_autofill -def show_login(): - dialog = Gtk.Dialog(title="Goldwarden") - preference_group = Adw.PreferencesGroup() - preference_group.set_title("Config") - dialog.get_content_area().append(preference_group) - - api_url_entry = Adw.EntryRow() - api_url_entry.set_title("API Url") - # set value - api_url_entry.set_text("https://api.bitwarden.com/") - preference_group.add(api_url_entry) - - identity_url_entry = Adw.EntryRow() - identity_url_entry.set_title("Identity Url") - identity_url_entry.set_text("https://identity.bitwarden.com/") - preference_group.add(identity_url_entry) - - notification_url_entry = Adw.EntryRow() - notification_url_entry.set_title("Notification URL") - notification_url_entry.set_text("https://notifications.bitwarden.com/") - preference_group.add(notification_url_entry) - - auth_preference_group = Adw.PreferencesGroup() - auth_preference_group.set_title("Authentication") - 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) - - dialog.add_button("Login", Gtk.ResponseType.OK) - def on_save(res): - if res != Gtk.ResponseType.OK: - return - goldwarden.set_api_url(api_url_entry.get_text()) - goldwarden.set_identity_url(identity_url_entry.get_text()) - goldwarden.set_notification_url(notification_url_entry.get_text()) - def login(): - res = goldwarden.login_with_password(email_entry.get_text(), "password") - def handle_res(): - print("handle res", res) - if res == "ok": - dialog.close() - print("ok") - 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() - - #ok response - dialog.connect("response", lambda dialog, response: on_save(response)) - dialog.set_default_size(400, 200) - dialog.set_modal(True) - dialog.present() - - -app.run(sys.argv) - +while True: + time.sleep(60) \ No newline at end of file diff --git a/ui/settings.py b/ui/settings.py new file mode 100644 index 0000000..2c823f4 --- /dev/null +++ b/ui/settings.py @@ -0,0 +1,216 @@ +#!/usr/bin/python +import sys +import gi +gi.require_version('Gtk', '4.0') +gi.require_version('Adw', '1') +import gc + +from gi.repository import Gtk, Adw, GLib +import goldwarden +from threading import Thread + +class SettingsWinvdow(Gtk.ApplicationWindow): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.stack = Gtk.Stack() + self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) + self.set_child(self.stack) + + self.preferences_page = Adw.PreferencesPage() + self.preferences_page.set_title("General") + self.stack.add_named(self.preferences_page, "preferences_page") + + self.preferences_group = Adw.PreferencesGroup() + self.preferences_group.set_title("Services") + self.preferences_page.add(self.preferences_group) + + self.ssh_row = Adw.ActionRow() + self.ssh_row.set_title("SSH Daemon") + self.ssh_row.set_subtitle("Listening at ~/.goldwarden-ssh-agent.sock") + self.preferences_group.add(self.ssh_row) + + self.login_with_device = Adw.ActionRow() + self.login_with_device.set_title("Login with device") + self.login_with_device.set_subtitle("Waiting for requests...") + self.preferences_group.add(self.login_with_device) + + self.status_row = Adw.ActionRow() + self.status_row.set_title("DBUS Service") + self.status_row.set_subtitle("Listening") + self.preferences_group.add(self.status_row) + + self.shortcut_preferences_group = Adw.PreferencesGroup() + self.shortcut_preferences_group.set_title("Shortcuts") + self.preferences_page.add(self.shortcut_preferences_group) + + self.autofill_row = Adw.ActionRow() + self.autofill_row.set_title("Autofill Shortcut") + self.autofill_row.set_subtitle("Unavailable, please set up a shortcut in your desktop environment (README)") + self.shortcut_preferences_group.add(self.autofill_row) + + self.copy_username_shortcut_row = Adw.ActionRow() + self.copy_username_shortcut_row.set_title("Copy Username Shortcut") + self.copy_username_shortcut_row.set_subtitle("U") + self.shortcut_preferences_group.add(self.copy_username_shortcut_row) + + self.copy_password_shortcut_row = Adw.ActionRow() + self.copy_password_shortcut_row.set_title("Copy Password Shortcut") + self.copy_password_shortcut_row.set_subtitle("P") + self.shortcut_preferences_group.add(self.copy_password_shortcut_row) + + self.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 = Adw.ActionRow() + self.status_row.set_title("Vault Status") + self.status_row.set_subtitle("Locked") + self.vault_status_preferences_group.add(self.status_row) + + self.login_row = Adw.ActionRow() + self.login_row.set_title("Vault Login Entries") + self.login_row.set_subtitle("0") + self.vault_status_preferences_group.add(self.login_row) + + self.notes_row = Adw.ActionRow() + self.notes_row.set_title("Vault Notes") + self.notes_row.set_subtitle("0") + self.vault_status_preferences_group.add(self.notes_row) + + self.action_preferences_group = Adw.PreferencesGroup() + self.action_preferences_group.set_title("Actions") + self.preferences_page.add(self.action_preferences_group) + + 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") + def set_pin(): + set_pin_thread = Thread(target=goldwarden.enable_pin) + set_pin_thread.start() + 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) + + def update_labels(): + pin_set = goldwarden.is_pin_enabled() + status = goldwarden.get_vault_status() + 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.status_row.set_subtitle(str("Unlocked" if not locked else "Locked")) + self.login_row.set_subtitle(str(status["loginEntries"])) + self.notes_row.set_subtitle(str(status["noteEntries"])) + self.unlock_button.set_sensitive(True) + self.unlock_button.set_label("Unlock" if locked else "Lock") + GLib.timeout_add(1000, update_labels) + + GLib.timeout_add(1000, update_labels) + self.set_default_size(400, 700) + self.set_title("Goldwarden") + + + #add title buttons + self.title_bar = Gtk.HeaderBar() + self.set_titlebar(self.title_bar) + +class MyApp(Adw.Application): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.connect('activate', self.on_activate) + + def on_activate(self, app): + self.settings_win = SettingsWinvdow(application=app) + self.settings_win.present() + +app = MyApp(application_id="com.quexten.Goldwarden") + +def show_login(): + dialog = Gtk.Dialog(title="Goldwarden") + preference_group = Adw.PreferencesGroup() + preference_group.set_title("Config") + dialog.get_content_area().append(preference_group) + + api_url_entry = Adw.EntryRow() + api_url_entry.set_title("API Url") + # set value + api_url_entry.set_text("https://api.bitwarden.com/") + preference_group.add(api_url_entry) + + identity_url_entry = Adw.EntryRow() + identity_url_entry.set_title("Identity Url") + identity_url_entry.set_text("https://identity.bitwarden.com/") + preference_group.add(identity_url_entry) + + notification_url_entry = Adw.EntryRow() + notification_url_entry.set_title("Notification URL") + notification_url_entry.set_text("https://notifications.bitwarden.com/") + preference_group.add(notification_url_entry) + + auth_preference_group = Adw.PreferencesGroup() + auth_preference_group.set_title("Authentication") + 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) + + dialog.add_button("Login", Gtk.ResponseType.OK) + def on_save(res): + if res != Gtk.ResponseType.OK: + return + goldwarden.set_api_url(api_url_entry.get_text()) + goldwarden.set_identity_url(identity_url_entry.get_text()) + goldwarden.set_notification_url(notification_url_entry.get_text()) + def login(): + res = goldwarden.login_with_password(email_entry.get_text(), "password") + def handle_res(): + print("handle res", res) + if res == "ok": + dialog.close() + print("ok") + 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() + + #ok response + dialog.connect("response", lambda dialog, response: on_save(response)) + dialog.set_default_size(400, 200) + dialog.set_modal(True) + dialog.present() + +app.run(sys.argv) \ No newline at end of file