mirror of
https://github.com/quexten/goldwarden.git
synced 2024-11-27 11:02:46 +03:00
Merge pull request #191 from quexten/feature/gtk-blueprints
Feature/gtk blueprints
This commit is contained in:
commit
f415d98f22
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,4 +4,5 @@ __pycache__
|
||||
.flatpak-builder
|
||||
flatpak-pip-generator
|
||||
repo
|
||||
__debug*
|
||||
__debug*
|
||||
.templates
|
||||
|
@ -25,6 +25,13 @@ finish-args:
|
||||
# biometric / user password auth
|
||||
- --system-talk-name=org.freedesktop.PolicyKit1
|
||||
modules:
|
||||
- name: "blueprint-compiler"
|
||||
buildsystem: meson
|
||||
cleanup: ['*']
|
||||
sources:
|
||||
- type: git
|
||||
url: https://gitlab.gnome.org/jwestman/blueprint-compiler
|
||||
tag: v0.12.0
|
||||
- ./gui/python3-requirements.json
|
||||
- name: goldwarden-python-ui
|
||||
buildsystem: simple
|
||||
@ -35,6 +42,7 @@ modules:
|
||||
- install -D ./gui/com.quexten.Goldwarden.desktop /app/share/applications/com.quexten.Goldwarden.desktop
|
||||
- install -D ./gui/goldwarden.svg /app/share/icons/hicolor/scalable/apps/com.quexten.Goldwarden.svg
|
||||
- install -Dm644 ./gui/com.quexten.Goldwarden.metainfo.xml -t /app/share/metainfo/
|
||||
- blueprint-compiler batch-compile /app/bin/src/gui/.templates/ /app/bin/src/gui/ /app/bin/src/gui/*.blp
|
||||
sources:
|
||||
- type: dir
|
||||
path: ./
|
||||
|
44
gui/src/gui/browserbiometrics.blp
Normal file
44
gui/src/gui/browserbiometrics.blp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
if __name__ == "__main__":
|
||||
app = GoldwardenBrowserBiometricsSetupGuideApp(application_id="com.quexten.Goldwarden.browserbiometrics")
|
||||
app.run(sys.argv)
|
69
gui/src/gui/login.blp
Normal file
69
gui/src/gui/login.blp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
66
gui/src/gui/login.py
Normal file
66
gui/src/gui/login.py
Normal file
@ -0,0 +1,66 @@
|
||||
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())
|
||||
|
||||
evk = Gtk.EventControllerKey.new()
|
||||
evk.set_propagation_phase(Gtk.PropagationPhase.CAPTURE)
|
||||
evk.connect("key-pressed", self.key_press)
|
||||
self.window.add_controller(evk)
|
||||
|
||||
def key_press(self, event, keyval, keycode, state):
|
||||
if keyval == Gdk.KEY_Escape:
|
||||
os._exit(0)
|
||||
|
||||
if keyval == Gdk.KEY_Return and state & Gdk.ModifierType.CONTROL_MASK:
|
||||
self.on_login()
|
||||
return True
|
||||
|
||||
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, "")
|
||||
self.window.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
settings = Gtk.Settings.get_default()
|
||||
settings.set_property("gtk-error-bell", False)
|
||||
|
||||
app = GoldwardenLoginApp(application_id="com.quexten.Goldwarden.login")
|
||||
app.run(sys.argv)
|
43
gui/src/gui/main_view.blp
Normal file
43
gui/src/gui/main_view.blp
Normal file
@ -0,0 +1,43 @@
|
||||
using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
Adw.ToolbarView view {
|
||||
content: Box{
|
||||
orientation: vertical;
|
||||
Adw.Banner paused_banner {
|
||||
title: 'Backups paused due to Power Saver Mode';
|
||||
revealed: false;
|
||||
}
|
||||
|
||||
ScrolledWindow {
|
||||
vexpand: true;
|
||||
hexpand: true;
|
||||
margin-top: 20;
|
||||
child: Box content {
|
||||
orientation: vertical;
|
||||
spacing: 6;
|
||||
margin-start: 80;
|
||||
margin-end: 80;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
[top]
|
||||
Adw.HeaderBar {
|
||||
halign: baseline;
|
||||
|
||||
title-widget: Adw.WindowTitle {
|
||||
title: 'Backup View';
|
||||
};
|
||||
|
||||
valign: start;
|
||||
|
||||
Button add_button {
|
||||
label: 'Add Backup';
|
||||
|
||||
styles [
|
||||
"suggested-action",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
48
gui/src/gui/pinentry.blp
Normal file
48
gui/src/gui/pinentry.blp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
if __name__ == "__main__":
|
||||
app = GoldwardenPinentryApp(application_id="com.quexten.Goldwarden.pinentry")
|
||||
message = sys.stdin.readline()
|
||||
app.message = message
|
||||
app.run(sys.argv)
|
43
gui/src/gui/pinentry_approval.blp
Normal file
43
gui/src/gui/pinentry_approval.blp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
if __name__ == "__main__":
|
||||
app = GoldwardenPinentryApprovalApp(application_id="com.quexten.Goldwarden.pinentry")
|
||||
message = sys.stdin.readline()
|
||||
app.message = message
|
||||
app.run(sys.argv)
|
54
gui/src/gui/quickaccess.blp
Normal file
54
gui/src/gui/quickaccess.blp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -7,172 +7,189 @@ 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)
|
||||
class GoldwardenQuickAccessApp(Adw.Application):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.logins = []
|
||||
self.filtered_logins = []
|
||||
self.query = ""
|
||||
self.connect('activate', self.on_activate)
|
||||
self.selected_index = 0
|
||||
|
||||
def autotype(text):
|
||||
goldwarden.autotype(text)
|
||||
time.sleep(0.1)
|
||||
os._exit(0)
|
||||
def on_activate(self, app):
|
||||
self.load()
|
||||
self.window.present()
|
||||
thread = Thread(target=self.update_logins)
|
||||
thread.start()
|
||||
|
||||
def set_clipboard(text):
|
||||
Gdk.Display.get_clipboard(Gdk.Display.get_default()).set_content(
|
||||
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)
|
||||
|
||||
evk = Gtk.EventControllerKey.new()
|
||||
evk.set_propagation_phase(Gtk.PropagationPhase.CAPTURE)
|
||||
evk.connect("key-pressed", self.key_press)
|
||||
self.window.add_controller(evk)
|
||||
|
||||
def key_press(self, event, keyval, keycode, state):
|
||||
if keyval == Gdk.KEY_Escape:
|
||||
os._exit(0)
|
||||
|
||||
if keyval == Gdk.KEY_Tab:
|
||||
return True
|
||||
|
||||
if keyval == Gdk.KEY_Up:
|
||||
self.selected_index = self.selected_index - 1
|
||||
if self.selected_index < 0:
|
||||
self.selected_index = 0
|
||||
self.render_list()
|
||||
return True
|
||||
elif keyval == Gdk.KEY_Down:
|
||||
self.selected_index = self.selected_index + 1
|
||||
if self.selected_index >= len(self.filtered_logins):
|
||||
self.selected_index = len(self.filtered_logins) - 1
|
||||
self.render_list()
|
||||
return True
|
||||
|
||||
if self.selected_index >= len(self.filtered_logins) or self.selected_index < 0:
|
||||
self.selected_index = 0
|
||||
|
||||
auto_type_combo = state & Gdk.ModifierType.CONTROL_MASK and state & Gdk.ModifierType.SHIFT_MASK
|
||||
copy_combo = state & Gdk.ModifierType.CONTROL_MASK and not state & Gdk.ModifierType.SHIFT_MASK
|
||||
|
||||
# totp code
|
||||
if keyval == Gdk.KEY_t or keyval == Gdk.KEY_T:
|
||||
if auto_type_combo:
|
||||
self.autotype(totp.totp(self.filtered_logins[self.selected_index]["totp"]))
|
||||
if copy_combo:
|
||||
self.set_clipboard(totp.totp(self.filtered_logins[self.selected_index]["totp"]))
|
||||
|
||||
if keyval == Gdk.KEY_u or keyval == Gdk.KEY_U:
|
||||
if auto_type_combo:
|
||||
self.autotype(self.filtered_logins[self.selected_index]["username"])
|
||||
if copy_combo:
|
||||
self.set_clipboard(self.filtered_logins[self.selected_index]["username"])
|
||||
|
||||
if keyval == Gdk.KEY_p or keyval == Gdk.KEY_P:
|
||||
if auto_type_combo:
|
||||
self.autotype(self.filtered_logins[self.selected_index]["password"])
|
||||
if copy_combo:
|
||||
self.set_clipboard(self.filtered_logins[self.selected_index]["password"])
|
||||
|
||||
if (keyval == Gdk.KEY_l or keyval == Gdk.KEY_L) and auto_type_combo:
|
||||
Gtk.show_uri(None, self.results_list.get_selected_row().uri, Gdk.CURRENT_TIME)
|
||||
|
||||
if (keyval == Gdk.KEY_v or keyval == Gdk.KEY_V) and auto_type_combo:
|
||||
self.set_clipboard(self.filtered_logins[self.selected_index]["uri"])
|
||||
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)
|
||||
|
||||
if keyval == Gdk.KEY_Return:
|
||||
if auto_type_combo:
|
||||
self.autotype(f"{self.filtered_logins[self.selected_index]['username']}\t{self.filtered_logins[self.selected_index]['password']}")
|
||||
|
||||
def update(self):
|
||||
self.update_list()
|
||||
self.render_list()
|
||||
return True
|
||||
|
||||
def autotype(self, text):
|
||||
def perform_autotype(text):
|
||||
self.window.hide()
|
||||
time.sleep(0.1)
|
||||
goldwarden.autotype(text)
|
||||
time.sleep(0.1)
|
||||
os._exit(0)
|
||||
thread = Thread(target=perform_autotype, args=(text,))
|
||||
thread.start()
|
||||
|
||||
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 kill():
|
||||
time.sleep(0.5)
|
||||
os._exit(0)
|
||||
thread = Thread(target=kill)
|
||||
thread.start()
|
||||
|
||||
class MyApp(Adw.Application):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.connect('activate', self.on_activate)
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
# select the nth item
|
||||
if len(self.filtered_logins) > 0:
|
||||
self.results_list.select_row(self.results_list.get_row_at_index(self.selected_index))
|
||||
self.results_list.set_focus_child(self.results_list.get_row_at_index(self.selected_index))
|
||||
|
||||
self.starts_with_logins = None
|
||||
self.other_logins = None
|
||||
|
||||
def on_type(self, entry):
|
||||
search_query = entry.get_text()
|
||||
self.query = search_query
|
||||
self.update()
|
||||
|
||||
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
|
||||
thread = Thread(target=self.update_logins)
|
||||
thread.start()
|
||||
|
||||
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")
|
||||
self.logins = logins
|
||||
self.update()
|
||||
|
||||
def on_type(entry):
|
||||
if len(entry.get_text()) > 1:
|
||||
self.results_list.show()
|
||||
else:
|
||||
self.results_list.hide()
|
||||
if __name__ == "__main__":
|
||||
settings = Gtk.Settings.get_default()
|
||||
settings.set_property("gtk-error-bell", False)
|
||||
|
||||
while self.results_list.get_first_child() != None:
|
||||
self.results_list.remove(self.results_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"]
|
||||
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()
|
||||
# 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)
|
||||
token = sys.stdin.readline()
|
||||
goldwarden.create_authenticated_connection(token)
|
||||
app = GoldwardenQuickAccessApp(application_id="com.quexten.Goldwarden.quickaccess")
|
||||
app.run(sys.argv)
|
149
gui/src/gui/settings.blp
Normal file
149
gui/src/gui/settings.blp
Normal file
@ -0,0 +1,149 @@
|
||||
using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
|
||||
Adw.ApplicationWindow window {
|
||||
default-width: 400;
|
||||
default-height: 700;
|
||||
|
||||
Adw.ToolbarView view {
|
||||
content: Box{
|
||||
orientation: vertical;
|
||||
|
||||
Stack stack {
|
||||
Box set_pin_status {
|
||||
orientation: vertical;
|
||||
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 login_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";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
[top]
|
||||
Adw.HeaderBar {
|
||||
halign: baseline;
|
||||
|
||||
title-widget: Adw.WindowTitle {
|
||||
title: 'Goldwarden';
|
||||
};
|
||||
|
||||
[end]
|
||||
MenuButton menu_button {
|
||||
icon-name: "open-menu-symbolic";
|
||||
}
|
||||
|
||||
valign: start;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,322 +1,150 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '4.0')
|
||||
gi.require_version('Adw', '1')
|
||||
import gc
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
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()
|
||||
|
||||
def shortcuts_button_clicked():
|
||||
p = subprocess.Popen(["python3", "-m", "src.gui.shortcuts"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=root_path, start_new_session=True)
|
||||
p.stdin.write(f"{token}\n".encode())
|
||||
p.stdin.flush()
|
||||
|
||||
def ssh_button_clicked():
|
||||
p = subprocess.Popen(["python3", "-m", "src.gui.ssh"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=root_path, start_new_session=True)
|
||||
p.stdin.write(f"{token}\n".encode())
|
||||
p.stdin.flush()
|
||||
|
||||
def browserbiometrics_button_clicked():
|
||||
p = subprocess.Popen(["python3", "-m", "src.gui.browserbiometrics"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=root_path, start_new_session=True)
|
||||
p.stdin.write(f"{token}\n".encode())
|
||||
p.stdin.flush()
|
||||
|
||||
def add_action_row(parent, title, subtitle, icon=None):
|
||||
row = Adw.ActionRow()
|
||||
row.set_title(title)
|
||||
row.set_subtitle(subtitle)
|
||||
if icon != None:
|
||||
row.set_icon_name(icon)
|
||||
parent.add(row)
|
||||
return row
|
||||
|
||||
class SettingsWinvdow(Gtk.ApplicationWindow):
|
||||
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")
|
||||
|
||||
class MyApp(Adw.Application):
|
||||
class GoldwardenSettingsApp(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()
|
||||
self.load()
|
||||
self.update()
|
||||
self.window.present()
|
||||
GLib.timeout_add(100, self.update)
|
||||
|
||||
def show_login():
|
||||
dialog = Gtk.Dialog(title="Goldwarden")
|
||||
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")
|
||||
|
||||
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)
|
||||
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())
|
||||
|
||||
email_entry = Adw.EntryRow()
|
||||
email_entry.set_title("Email")
|
||||
email_entry.set_text("")
|
||||
auth_preference_group.add(email_entry)
|
||||
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.login_button = builder.get_object("login_button")
|
||||
self.login_button.connect("clicked", lambda x: run_window("login", self.token))
|
||||
|
||||
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)
|
||||
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", self.token))
|
||||
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")
|
||||
|
||||
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)
|
||||
self.menu_button = builder.get_object("menu_button")
|
||||
menu = Gio.Menu.new()
|
||||
self.popover = Gtk.PopoverMenu()
|
||||
self.popover.set_menu_model(menu)
|
||||
self.menu_button.set_popover(self.popover)
|
||||
|
||||
dialog.add_button("Login", Gtk.ResponseType.OK)
|
||||
def on_save(res):
|
||||
if res != Gtk.ResponseType.OK:
|
||||
action = Gio.SimpleAction.new("shortcuts", None)
|
||||
action.connect("activate", lambda action, parameter: run_window("shortcuts", self.token))
|
||||
self.window.add_action(action)
|
||||
menu.append("Keyboard Shortcuts", "win.shortcuts")
|
||||
|
||||
action = Gio.SimpleAction.new("ssh", None)
|
||||
action.connect("activate", lambda action, parameter: run_window("ssh", self.token))
|
||||
self.window.add_action(action)
|
||||
menu.append("SSH Agent", "win.ssh")
|
||||
|
||||
action = Gio.SimpleAction.new("browserbiometrics", None)
|
||||
action.connect("activate", lambda action, parameter: run_window("browserbiometrics", self.token))
|
||||
self.window.add_action(action)
|
||||
menu.append("Browser Biometrics", "win.browserbiometrics")
|
||||
|
||||
action = Gio.SimpleAction.new("about", None)
|
||||
action.connect("activate", lambda action, parameter: self.show_about())
|
||||
self.window.add_action(action)
|
||||
menu.append("About", "win.about")
|
||||
|
||||
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
|
||||
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()
|
||||
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)
|
||||
|
||||
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)
|
||||
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"]))
|
||||
|
||||
dialog.get_content_area().append(preference_group)
|
||||
def show_about(self):
|
||||
dialog = Adw.AboutWindow(transient_for=app.get_active_window())
|
||||
dialog.set_application_name("Goldwarden")
|
||||
dialog.set_version("dev")
|
||||
dialog.set_developer_name("Bernd Schoolmann (Quexten)")
|
||||
dialog.set_license_type(Gtk.License(Gtk.License.MIT_X11))
|
||||
dialog.set_comments("A Bitwarden compatible password manager")
|
||||
dialog.set_website("https://github.com/quexten/goldwarden")
|
||||
dialog.set_issue_url("https://github.com/quexten/goldwarden/issues")
|
||||
dialog.add_credit_section("Contributors", ["Bernd Schoolmann"])
|
||||
dialog.set_copyright("© 2024 Bernd Schoolmann")
|
||||
dialog.set_developers(["Bernd Schoolmann"])
|
||||
dialog.set_application_icon("com.quexten.Goldwarden")
|
||||
dialog.set_visible(True)
|
||||
|
||||
url_entry = Adw.EntryRow()
|
||||
url_entry.set_title("Base Url")
|
||||
url_entry.set_text("https://vault.bitwarden.com/")
|
||||
preference_group.add(url_entry)
|
||||
if __name__ == "__main__":
|
||||
settings = Gtk.Settings.get_default()
|
||||
settings.set_property("gtk-error-bell", False)
|
||||
|
||||
#ok response
|
||||
dialog.connect("response", lambda dialog, response: on_save(response))
|
||||
dialog.set_default_size(400, 200)
|
||||
dialog.set_modal(True)
|
||||
dialog.present()
|
||||
token = sys.stdin.readline().strip()
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
app = MyApp(application_id="com.quexten.Goldwarden.settings")
|
||||
app.run(sys.argv)
|
||||
goldwarden.create_authenticated_connection(None)
|
||||
app = GoldwardenSettingsApp(application_id="com.quexten.Goldwarden.settings")
|
||||
app.token = token
|
||||
app.run(sys.argv)
|
||||
|
91
gui/src/gui/shortcuts.blp
Normal file
91
gui/src/gui/shortcuts.blp
Normal file
@ -0,0 +1,91 @@
|
||||
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 + SHIFT + U";
|
||||
}
|
||||
Adw.ActionRow {
|
||||
title: "Copy Password Shortcut";
|
||||
subtitle: "CTRL + P";
|
||||
}
|
||||
Adw.ActionRow {
|
||||
title: "Autotype Password Shortcut";
|
||||
subtitle: "CTRL + SHIFT + P";
|
||||
}
|
||||
Adw.ActionRow {
|
||||
title: "Copy TOTP Shortcut";
|
||||
subtitle: "CTRL + T";
|
||||
}
|
||||
Adw.ActionRow {
|
||||
title: "Autotype TOTP Shortcut";
|
||||
subtitle: "CTRL + SHIFT + T";
|
||||
}
|
||||
Adw.ActionRow {
|
||||
title: "Launch URI Shortcut";
|
||||
subtitle: "CTRL + SHIFT + L";
|
||||
}
|
||||
Adw.ActionRow {
|
||||
title: "Launch Web Vault Shortcut";
|
||||
subtitle: "CTRL + SHIFT + V";
|
||||
}
|
||||
Adw.ActionRow {
|
||||
title: "Quit Shortcut";
|
||||
subtitle: "Esc";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
[top]
|
||||
Adw.HeaderBar {
|
||||
halign: baseline;
|
||||
|
||||
title-widget: Adw.WindowTitle {
|
||||
title: 'Settings';
|
||||
};
|
||||
|
||||
valign: start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
if __name__ == "__main__":
|
||||
app = GoldwardenShortcutsSetupGuideApp(application_id="com.quexten.Goldwarden.shortcuts")
|
||||
app.run(sys.argv)
|
57
gui/src/gui/ssh.blp
Normal file
57
gui/src/gui/ssh.blp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
if __name__ == "__main__":
|
||||
app = GoldwardenSSHSetupGuideApp(application_id="com.quexten.Goldwarden.sshsetup")
|
||||
app.run(sys.argv)
|
10
gui/src/gui/template_loader.py
Normal file
10
gui/src/gui/template_loader.py
Normal file
@ -0,0 +1,10 @@
|
||||
import os
|
||||
from gi.repository import Gtk
|
||||
|
||||
isflatpak = os.path.exists("/.flatpak-info")
|
||||
pathprefix = "/app/bin/src/gui/" if isflatpak else "./src/gui/"
|
||||
|
||||
def load_template(path):
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(pathprefix + ".templates/" + path)
|
||||
return builder
|
@ -17,4 +17,8 @@ def hotp(key, counter, digits=6, digest='sha1'):
|
||||
|
||||
|
||||
def totp(key, time_step=30, digits=6, digest='sha1'):
|
||||
if key.startswith('otpauth://'):
|
||||
key = key.split('secret=')[1].split('&')[0]
|
||||
key = key.replace(' ', '')
|
||||
key = key.strip()
|
||||
return hotp(key, int(time.time() / time_step), digits, digest)
|
Loading…
Reference in New Issue
Block a user