This commit is contained in:
commit
8499783206
4
PKGBUILD
4
PKGBUILD
@ -1,8 +1,8 @@
|
||||
pkgname=goldwarden
|
||||
pkgver=0.2.6
|
||||
pkgver=0.2.7
|
||||
pkgrel=1
|
||||
pkgdesc='A feature-packed Bitwarden compatible desktop integration'
|
||||
arch=('x86_64')
|
||||
arch=('x86_64', 'aarch64')
|
||||
url="https://github.com/quexten/$pkgname"
|
||||
license=('MIT')
|
||||
depends=('libfido2')
|
||||
|
@ -55,8 +55,17 @@ func handleSetNotifications(request messages.IPCMessage, cfg *config.Config, vau
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetRuntimeConfig(request messages.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response messages.IPCMessage, err error) {
|
||||
return messages.IPCMessageFromPayload(messages.GetRuntimeConfigResponse{
|
||||
UseMemguard: cfg.ConfigFile.RuntimeConfig.UseMemguard,
|
||||
SSHAgentSocketPath: cfg.ConfigFile.RuntimeConfig.SSHAgentSocketPath,
|
||||
GoldwardenSocketPath: cfg.ConfigFile.RuntimeConfig.GoldwardenSocketPath,
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetIdentityURLRequest{}), handleSetIdentity)
|
||||
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetApiURLRequest{}), handleSetApiURL)
|
||||
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.SetNotificationsURLRequest{}), handleSetNotifications)
|
||||
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.GetRuntimeConfigRequest{}), handleGetRuntimeConfig)
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ type RuntimeConfig struct {
|
||||
Password string
|
||||
Pin string
|
||||
UseMemguard bool
|
||||
SSHAgentSocketPath string
|
||||
GoldwardenSocketPath string
|
||||
}
|
||||
|
||||
type ConfigFile struct {
|
||||
|
@ -144,6 +144,7 @@ func (vaultAgent) Unlock(passphrase []byte) error {
|
||||
type SSHAgentServer struct {
|
||||
vault *vault.Vault
|
||||
config *config.Config
|
||||
runtimeConfig *config.RuntimeConfig
|
||||
unlockRequestAction func() bool
|
||||
}
|
||||
|
||||
@ -151,10 +152,11 @@ func (v *SSHAgentServer) SetUnlockRequestAction(action func() bool) {
|
||||
v.unlockRequestAction = action
|
||||
}
|
||||
|
||||
func NewVaultAgent(vault *vault.Vault, config *config.Config) SSHAgentServer {
|
||||
func NewVaultAgent(vault *vault.Vault, config *config.Config, runtimeConfig *config.RuntimeConfig) SSHAgentServer {
|
||||
return SSHAgentServer{
|
||||
vault: vault,
|
||||
config: config,
|
||||
vault: vault,
|
||||
config: config,
|
||||
runtimeConfig: runtimeConfig,
|
||||
unlockRequestAction: func() bool {
|
||||
log.Info("Unlock Request, but no action defined")
|
||||
return false
|
||||
@ -163,13 +165,7 @@ func NewVaultAgent(vault *vault.Vault, config *config.Config) SSHAgentServer {
|
||||
}
|
||||
|
||||
func (v SSHAgentServer) Serve() {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
path := home + "/.goldwarden-ssh-agent.sock"
|
||||
|
||||
path := v.runtimeConfig.SSHAgentSocketPath
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
if err := os.Remove(path); err != nil {
|
||||
log.Error("Could not remove old socket file: %s", err)
|
||||
|
@ -203,7 +203,7 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
|
||||
}()
|
||||
|
||||
if !runtimeConfig.DisableSSHAgent {
|
||||
vaultAgent := ssh.NewVaultAgent(vault, &cfg)
|
||||
vaultAgent := ssh.NewVaultAgent(vault, &cfg, &runtimeConfig)
|
||||
vaultAgent.SetUnlockRequestAction(func() bool {
|
||||
err := cfg.TryUnlock(vault)
|
||||
if err == nil {
|
||||
|
@ -6,44 +6,88 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/quexten/goldwarden/agent/config"
|
||||
"github.com/quexten/goldwarden/browserbiometrics/logging"
|
||||
)
|
||||
|
||||
var chromiumPaths = []string{
|
||||
"~/.config/google-chrome/",
|
||||
"~/.config/google-chrome-beta/",
|
||||
"~/.config/google-chrome-unstable/",
|
||||
"~/.config/chromium/",
|
||||
"~/.config/BraveSoftware/Brave-Browser/",
|
||||
"~/.config/thorium/",
|
||||
"~/.config/microsoft-edge-beta/",
|
||||
"~/.config/microsoft-edge-dev/",
|
||||
}
|
||||
var mozillaPaths = []string{"~/.mozilla/", "~/.librewolf/", "~/.waterfox/"}
|
||||
|
||||
const appID = "com.quexten.bw-bio-handler"
|
||||
|
||||
var transportKey []byte
|
||||
|
||||
func Main() {
|
||||
if os.Args[1] == "install" {
|
||||
var err error
|
||||
err = detectAndInstallBrowsers(".config")
|
||||
if err != nil {
|
||||
panic("Failed to detect browsers: " + err.Error())
|
||||
}
|
||||
err = detectAndInstallBrowsers(".mozilla")
|
||||
if err != nil {
|
||||
panic("Failed to detect browsers: " + err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Main(rtCfg *config.RuntimeConfig) {
|
||||
logging.Debugf("Starting browserbiometrics")
|
||||
transportKey = generateTransportKey()
|
||||
logging.Debugf("Generated transport key")
|
||||
|
||||
setupCommunication()
|
||||
readLoop()
|
||||
readLoop(rtCfg)
|
||||
}
|
||||
|
||||
func DetectAndInstallBrowsers() error {
|
||||
var err error
|
||||
|
||||
// first, ensure the native messaging hosts dirs exist
|
||||
for _, path := range chromiumPaths {
|
||||
path = strings.ReplaceAll(path, "~", os.Getenv("HOME"))
|
||||
_, err = os.Stat(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = os.Stat(path + "NativeMessagingHosts/")
|
||||
if err == nil {
|
||||
fmt.Println("Native messaging host directory already exists: " + path + "NativeMessagingHosts/")
|
||||
continue
|
||||
}
|
||||
err = os.MkdirAll(path+"NativeMessagingHosts/", 0755)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating native messaging host directory: " + err.Error())
|
||||
} else {
|
||||
fmt.Println("Created native messaging host directory: " + path + "NativeMessagingHosts/")
|
||||
}
|
||||
}
|
||||
for _, path := range mozillaPaths {
|
||||
path = strings.ReplaceAll(path, "~", os.Getenv("HOME"))
|
||||
_, err = os.Stat(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = os.Stat(path + "native-messaging-hosts/")
|
||||
if err == nil {
|
||||
fmt.Println("Native messaging host directory already exists: " + path + "native-messaging-hosts/")
|
||||
continue
|
||||
}
|
||||
err = os.MkdirAll(path+"native-messaging-hosts/", 0755)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating native messaging host directory: " + err.Error())
|
||||
} else {
|
||||
fmt.Println("Created native messaging host directory: " + path + "native-messaging-hosts/")
|
||||
}
|
||||
}
|
||||
|
||||
err = detectAndInstallBrowsers(".config")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = detectAndInstallBrowsers(".mozilla")
|
||||
if err != nil {
|
||||
return err
|
||||
for _, path := range mozillaPaths {
|
||||
path = strings.ReplaceAll(path, "~/", "")
|
||||
err = detectAndInstallBrowsers(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -6,12 +6,16 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/quexten/goldwarden/agent/config"
|
||||
"github.com/quexten/goldwarden/browserbiometrics/logging"
|
||||
"github.com/quexten/goldwarden/client"
|
||||
"github.com/quexten/goldwarden/ipc/messages"
|
||||
)
|
||||
|
||||
func readLoop() {
|
||||
var runtimeConfig *config.RuntimeConfig
|
||||
|
||||
func readLoop(rtCfg *config.RuntimeConfig) {
|
||||
runtimeConfig = rtCfg
|
||||
v := bufio.NewReader(os.Stdin)
|
||||
s := bufio.NewReaderSize(v, bufferSize)
|
||||
|
||||
@ -101,7 +105,7 @@ func handlePayloadMessage(msg PayloadMessage, appID string) {
|
||||
case "biometricUnlock":
|
||||
logging.Debugf("Biometric unlock requested")
|
||||
// logging.Debugf("Biometrics authorized: %t", isAuthorized)
|
||||
result, err := client.NewUnixSocketClient().SendToAgent(messages.GetBiometricsKeyRequest{})
|
||||
result, err := client.NewUnixSocketClient(runtimeConfig).SendToAgent(messages.GetBiometricsKeyRequest{})
|
||||
if err != nil {
|
||||
logging.Errorf("Unable to send message to agent: %s", err.Error())
|
||||
return
|
||||
|
@ -5,18 +5,21 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/quexten/goldwarden/agent/config"
|
||||
"github.com/quexten/goldwarden/ipc/messages"
|
||||
)
|
||||
|
||||
const READ_BUFFER = 1 * 1024 * 1024 // 1MB
|
||||
|
||||
type UnixSocketClient struct {
|
||||
runtimeConfig *config.RuntimeConfig
|
||||
}
|
||||
|
||||
func NewUnixSocketClient() UnixSocketClient {
|
||||
return UnixSocketClient{}
|
||||
func NewUnixSocketClient(runtimeConfig *config.RuntimeConfig) UnixSocketClient {
|
||||
return UnixSocketClient{
|
||||
runtimeConfig: runtimeConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func reader(r io.Reader) interface{} {
|
||||
@ -37,12 +40,7 @@ func reader(r io.Reader) interface{} {
|
||||
}
|
||||
|
||||
func (client UnixSocketClient) SendToAgent(request interface{}) (interface{}, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c, err := net.Dial("unix", home+"/.goldwarden.sock")
|
||||
c, err := net.Dial("unix", client.runtimeConfig.GoldwardenSocketPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/quexten/goldwarden/ipc/messages"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -104,6 +106,32 @@ var setNotificationsURLCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var getRuntimeConfigCmd = &cobra.Command{
|
||||
Use: "get-runtime-config",
|
||||
Short: "Get the runtime config",
|
||||
Long: `Get the runtime config.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
request := messages.GetRuntimeConfigRequest{}
|
||||
|
||||
result, err := commandClient.SendToAgent(request)
|
||||
if err != nil {
|
||||
handleSendToAgentError(err)
|
||||
return
|
||||
}
|
||||
|
||||
switch result := result.(type) {
|
||||
case messages.GetRuntimeConfigResponse:
|
||||
fmt.Println("{")
|
||||
fmt.Println(" \"useMemguard\": " + fmt.Sprintf("%t", result.UseMemguard) + ",")
|
||||
fmt.Println(" \"SSHAgentSocketPath\": \"" + result.SSHAgentSocketPath + "\",")
|
||||
fmt.Println(" \"goldwardenSocketPath\": \"" + result.GoldwardenSocketPath + "\"")
|
||||
fmt.Println("}")
|
||||
default:
|
||||
println("Wrong IPC response type")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var configCmd = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Manage the configuration",
|
||||
@ -115,4 +143,5 @@ func init() {
|
||||
configCmd.AddCommand(setApiUrlCmd)
|
||||
configCmd.AddCommand(setIdentityURLCmd)
|
||||
configCmd.AddCommand(setNotificationsURLCmd)
|
||||
configCmd.AddCommand(getRuntimeConfigCmd)
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package cmd
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
|
||||
"github.com/awnumar/memguard"
|
||||
"github.com/quexten/goldwarden/agent"
|
||||
@ -19,15 +18,6 @@ var daemonizeCmd = &cobra.Command{
|
||||
websocketDisabled := runtimeConfig.WebsocketDisabled
|
||||
sshDisabled := runtimeConfig.DisableSSHAgent
|
||||
|
||||
_, err := os.Stat("/.flatpak-info")
|
||||
isFlatpak := err == nil
|
||||
if isFlatpak {
|
||||
runtimeConfig.ConfigDirectory = "~/.var/app/com.quexten.Goldwarden/config/goldwarden.json"
|
||||
userHome, _ := os.UserHomeDir()
|
||||
runtimeConfig.ConfigDirectory = strings.ReplaceAll(runtimeConfig.ConfigDirectory, "~", userHome)
|
||||
println("Flatpak Config directory: " + runtimeConfig.ConfigDirectory)
|
||||
}
|
||||
|
||||
if websocketDisabled {
|
||||
println("Websocket disabled")
|
||||
}
|
||||
@ -42,11 +32,7 @@ var daemonizeCmd = &cobra.Command{
|
||||
<-signalChannel
|
||||
memguard.SafeExit(0)
|
||||
}()
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = agent.StartUnixAgent(home+"/.goldwarden.sock", runtimeConfig)
|
||||
err := agent.StartUnixAgent(runtimeConfig.GoldwardenSocketPath, runtimeConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ func Execute(cfg config.RuntimeConfig) {
|
||||
recv, send := agent.StartVirtualAgent(runtimeConfig)
|
||||
commandClient = client.NewVirtualClient(send, recv)
|
||||
} else {
|
||||
commandClient = client.NewUnixSocketClient()
|
||||
commandClient = client.NewUnixSocketClient(&cfg)
|
||||
}
|
||||
|
||||
err := rootCmd.Execute()
|
||||
|
@ -14,6 +14,14 @@ type SetNotificationsURLRequest struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
type GetRuntimeConfigRequest struct{}
|
||||
|
||||
type GetRuntimeConfigResponse struct {
|
||||
UseMemguard bool
|
||||
SSHAgentSocketPath string
|
||||
GoldwardenSocketPath string
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||
var req SetApiURLRequest
|
||||
@ -41,4 +49,22 @@ func init() {
|
||||
}
|
||||
return req, nil
|
||||
}, SetNotificationsURLRequest{})
|
||||
|
||||
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||
var req GetRuntimeConfigRequest
|
||||
err := json.Unmarshal(payload, &req)
|
||||
if err != nil {
|
||||
panic("Unmarshal: " + err.Error())
|
||||
}
|
||||
return req, nil
|
||||
}, GetRuntimeConfigRequest{})
|
||||
|
||||
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||
var req GetRuntimeConfigResponse
|
||||
err := json.Unmarshal(payload, &req)
|
||||
if err != nil {
|
||||
panic("Unmarshal: " + err.Error())
|
||||
}
|
||||
return req, nil
|
||||
}, GetRuntimeConfigResponse{})
|
||||
}
|
||||
|
34
main.go
34
main.go
@ -10,11 +10,6 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) > 1 && (strings.Contains(os.Args[1], "com.8bit.bitwarden.json") || strings.Contains(os.Args[1], "chrome-extension://")) {
|
||||
browserbiometrics.Main()
|
||||
return
|
||||
}
|
||||
|
||||
var configPath string
|
||||
if path, found := os.LookupEnv("GOLDWARDEN_CONFIG_DIRECTORY"); found {
|
||||
configPath = path
|
||||
@ -39,10 +34,39 @@ func main() {
|
||||
Password: os.Getenv("GOLDWARDEN_AUTH_PASSWORD"),
|
||||
Pin: os.Getenv("GOLDWARDEN_PIN"),
|
||||
UseMemguard: os.Getenv("GOLDWARDEN_NO_MEMGUARD") != "true",
|
||||
SSHAgentSocketPath: os.Getenv("GOLDWARDEN_SSH_AUTH_SOCK"),
|
||||
GoldwardenSocketPath: os.Getenv("GOLDWARDEN_SOCKET_PATH"),
|
||||
|
||||
ConfigDirectory: configPath,
|
||||
}
|
||||
|
||||
if len(os.Args) > 1 && (strings.Contains(os.Args[1], "com.8bit.bitwarden.json") || strings.Contains(os.Args[1], "chrome-extension://")) {
|
||||
browserbiometrics.Main(&runtimeConfig)
|
||||
return
|
||||
}
|
||||
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if runtimeConfig.SSHAgentSocketPath == "" {
|
||||
runtimeConfig.SSHAgentSocketPath = home + "/.goldwarden-ssh-agent.sock"
|
||||
}
|
||||
if runtimeConfig.GoldwardenSocketPath == "" {
|
||||
runtimeConfig.GoldwardenSocketPath = home + "/.goldwarden.sock"
|
||||
}
|
||||
|
||||
_, err = os.Stat("/.flatpak-info")
|
||||
isFlatpak := err == nil
|
||||
if isFlatpak {
|
||||
userHome, _ := os.UserHomeDir()
|
||||
runtimeConfig.ConfigDirectory = userHome + "/.var/app/com.quexten.Goldwarden/config/goldwarden.json"
|
||||
runtimeConfig.ConfigDirectory = strings.ReplaceAll(runtimeConfig.ConfigDirectory, "~", userHome)
|
||||
println("Flatpak Config directory: " + runtimeConfig.ConfigDirectory)
|
||||
runtimeConfig.SSHAgentSocketPath = userHome + "/.var/app/com.quexten.Goldwarden/data/ssh-auth-sock"
|
||||
runtimeConfig.GoldwardenSocketPath = userHome + "/.var/app/com.quexten.Goldwarden/data/goldwarden.sock"
|
||||
}
|
||||
|
||||
if runtimeConfig.SingleProcess {
|
||||
runtimeConfig.DisablePinRequirement = true
|
||||
runtimeConfig.DisableAuth = true
|
||||
|
@ -3,12 +3,13 @@ gi.require_version('Gtk', '4.0')
|
||||
gi.require_version('Adw', '1')
|
||||
import gc
|
||||
import time
|
||||
from gi.repository import Gtk, Adw, GLib
|
||||
from gi.repository import Gtk, Adw, GLib, Notify
|
||||
import goldwarden
|
||||
import clipboard
|
||||
from threading import Thread
|
||||
import sys
|
||||
import os
|
||||
Notify.init("Goldwarden")
|
||||
|
||||
class MyApp(Adw.Application):
|
||||
def __init__(self, **kwargs):
|
||||
@ -93,9 +94,13 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
if keyval == 112:
|
||||
print("copy password")
|
||||
clipboard.write(self.history_list.get_selected_row().password)
|
||||
Notify.Notification.new("Goldwarden", "Password Copied", "dialog-information").show()
|
||||
elif keyval == 117:
|
||||
print("copy username")
|
||||
clipboard.write(self.history_list.get_selected_row().username)
|
||||
notification=Notify.Notification.new("Goldwarden", "Username Copied", "dialog-information")
|
||||
notification.set_timeout(5)
|
||||
notification.show()
|
||||
|
||||
keycont.connect('key-pressed', handle_keypress, self)
|
||||
self.add_controller(keycont)
|
||||
|
@ -103,6 +103,17 @@ def get_vault_logins():
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return None
|
||||
|
||||
def get_runtime_config():
|
||||
restic_cmd = f"{BINARY_PATH} config get-runtime-config"
|
||||
result = subprocess.run(restic_cmd.split(), capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
return None
|
||||
try:
|
||||
return json.loads(result.stdout)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return None
|
||||
|
||||
def autotype(username, password):
|
||||
# environment
|
||||
|
@ -30,11 +30,15 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
|
||||
|
||||
self.ssh_row = Adw.ActionRow()
|
||||
self.ssh_row.set_title("SSH Daemon")
|
||||
self.ssh_row.set_subtitle("Listening at ~/.goldwarden-ssh-agent.sock")
|
||||
self.ssh_row.set_subtitle("Getting status...")
|
||||
self.ssh_row.set_icon_name("emblem-default")
|
||||
self.preferences_group.add(self.ssh_row)
|
||||
|
||||
self.icon = components.status_icon_ok("emblem-default")
|
||||
self.ssh_row.add_prefix(self.icon)
|
||||
self.goldwarden_daemon_row = Adw.ActionRow()
|
||||
self.goldwarden_daemon_row.set_title("Goldwarden Daemon")
|
||||
self.goldwarden_daemon_row.set_subtitle("Getting status...")
|
||||
self.goldwarden_daemon_row.set_icon_name("emblem-default")
|
||||
self.preferences_group.add(self.goldwarden_daemon_row)
|
||||
|
||||
self.login_with_device = Adw.ActionRow()
|
||||
self.login_with_device.set_title("Login with device")
|
||||
@ -168,6 +172,11 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
|
||||
|
||||
pin_set = goldwarden.is_pin_enabled()
|
||||
status = goldwarden.get_vault_status()
|
||||
runtimeCfg = goldwarden.get_runtime_config()
|
||||
if runtimeCfg != None:
|
||||
self.ssh_row.set_subtitle("Listening at "+runtimeCfg["SSHAgentSocketPath"])
|
||||
self.goldwarden_daemon_row.set_subtitle("Listening at "+runtimeCfg["goldwardenSocketPath"])
|
||||
|
||||
if status != None:
|
||||
if pin_set:
|
||||
self.unlock_button.set_sensitive(True)
|
||||
@ -210,7 +219,7 @@ class SettingsWinvdow(Gtk.ApplicationWindow):
|
||||
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"):
|
||||
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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user