From 794593a71fea3474fad2c4f312342aec50240bf1 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 19 Jan 2024 05:14:25 +0100 Subject: [PATCH] Add timeout to notifications --- agent/actions/login.go | 5 +++-- agent/actions/vault.go | 22 +++++++++++++++++----- agent/bitwarden/auth.go | 26 +++++++++++++------------- agent/bitwarden/sync.go | 2 +- agent/config/config.go | 5 +++-- agent/notify/dbus.go | 22 ++++++++++++++++++++-- agent/ssh/ssh.go | 5 +++-- agent/unixsocketagent.go | 16 +++++++++++++--- 8 files changed, 73 insertions(+), 30 deletions(-) diff --git a/agent/actions/login.go b/agent/actions/login.go index 3d5e165..e49155a 100644 --- a/agent/actions/login.go +++ b/agent/actions/login.go @@ -3,6 +3,7 @@ package actions import ( "context" "fmt" + "time" "github.com/quexten/goldwarden/agent/bitwarden" "github.com/quexten/goldwarden/agent/bitwarden/crypto" @@ -85,7 +86,7 @@ func handleLogin(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vault err = crypto.InitKeyringFromMasterKey(vault.Keyring, profile.Profile.Key, profile.Profile.PrivateKey, orgKeys, masterKey) if err != nil { defer func() { - notify.Notify("Goldwarden", "Could not decrypt. Wrong password?", "", func() {}) + notify.Notify("Goldwarden", "Could not decrypt. Wrong password?", "", 10*time.Second, func() {}) cfg.SetToken(config.LoginToken{}) vault.Clear() }() @@ -112,7 +113,7 @@ func handleLogin(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vault } if err != nil { defer func() { - notify.Notify("Goldwarden", "Could not decrypt. Wrong password?", "", func() {}) + notify.Notify("Goldwarden", "Could not decrypt. Wrong password?", "", 10*time.Second, func() {}) cfg.SetToken(config.LoginToken{}) vault.Clear() }() diff --git a/agent/actions/vault.go b/agent/actions/vault.go index 59b1ef0..23dd188 100644 --- a/agent/actions/vault.go +++ b/agent/actions/vault.go @@ -2,7 +2,6 @@ package actions import ( "context" - "fmt" "github.com/quexten/goldwarden/agent/bitwarden" "github.com/quexten/goldwarden/agent/bitwarden/crypto" @@ -53,16 +52,25 @@ func handleUnlockVault(request messages.IPCMessage, cfg *config.Config, vault *v return } + actionsLog.Info("Unlocking vault...") if cfg.IsLoggedIn() { token, err := cfg.GetToken() if err == nil { if token.AccessToken != "" { ctx := context.Background() - bitwarden.RefreshToken(ctx, cfg) + gotToken := bitwarden.RefreshToken(ctx, cfg) + if gotToken { + actionsLog.Info("Token refreshed") + } else { + actionsLog.Warn("Token refresh failed") + } token, err := cfg.GetToken() + if err != nil { + actionsLog.Error("Could not get token: %s", err.Error()) + } userSymmkey, err := cfg.GetUserSymmetricKey() if err != nil { - fmt.Println(err) + actionsLog.Error("Could not get user symmetric key: %s", err.Error()) } var safeUserSymmkey crypto.SymmetricEncryptionKey @@ -72,14 +80,18 @@ func handleUnlockVault(request messages.IPCMessage, cfg *config.Config, vault *v safeUserSymmkey, err = crypto.MemorySymmetricEncryptionKeyFromBytes(userSymmkey) } if err != nil { - fmt.Println(err) + actionsLog.Error("Could not create safe user symmetric key: %s", err.Error()) } err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, cfg, &safeUserSymmkey, true) if err != nil { - fmt.Println(err) + actionsLog.Error("Could not sync: %s", err.Error()) } + } else { + actionsLog.Warn("Access token is empty") } + } else { + actionsLog.Warn("Could not get token: %s", err.Error()) } } diff --git a/agent/bitwarden/auth.go b/agent/bitwarden/auth.go index 42fa643..f5b5198 100644 --- a/agent/bitwarden/auth.go +++ b/agent/bitwarden/auth.go @@ -71,12 +71,12 @@ func deviceType() string { func LoginWithApiKey(ctx context.Context, email string, cfg *config.Config, vault *vault.Vault) (LoginResponseToken, crypto.MasterKey, string, error) { clientID, err := cfg.GetClientID() if err != nil { - notify.Notify("Goldwarden", fmt.Sprintf("Could not get client ID: %v", err), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Could not get client ID: %v", err), "", 0, func() {}) return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not get client ID: %v", err) } clientSecret, err := cfg.GetClientSecret() if err != nil { - notify.Notify("Goldwarden", fmt.Sprintf("Could not get client secret: %v", err), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Could not get client secret: %v", err), "", 0, func() {}) return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not get client secret: %v", err) } @@ -93,19 +93,19 @@ func LoginWithApiKey(ctx context.Context, email string, cfg *config.Config, vaul var loginResponseToken LoginResponseToken err = authenticatedHTTPPost(ctx, cfg.ConfigFile.IdentityUrl+"/connect/token", &loginResponseToken, values) if err != nil { - notify.Notify("Goldwarden", fmt.Sprintf("Could not login via API key: %v", err), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Could not login via API key: %v", err), "", 0, func() {}) return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not login via API key: %v", err) } password, err := pinentry.GetPassword("Bitwarden Password", "Enter your Bitwarden password") if err != nil { - notify.Notify("Goldwarden", fmt.Sprintf("Could not get password: %v", err), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Could not get password: %v", err), "", 0, func() {}) return LoginResponseToken{}, crypto.MasterKey{}, "", err } masterKey, err := crypto.DeriveMasterKey([]byte(strings.Clone(password)), email, crypto.KDFConfig{Type: crypto.KDFType(loginResponseToken.Kdf), Iterations: uint32(loginResponseToken.KdfIterations), Memory: uint32(loginResponseToken.KdfMemory), Parallelism: uint32(loginResponseToken.KdfParallelism)}) if err != nil { - notify.Notify("Goldwarden", fmt.Sprintf("Could not derive master key: %v", err), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Could not derive master key: %v", err), "", 0, func() {}) return LoginResponseToken{}, crypto.MasterKey{}, "", err } hashedPassword := b64enc.EncodeToString(pbkdf2.Key(masterKey.GetBytes(), []byte(password), 1, 32, sha256.New)) @@ -119,7 +119,7 @@ func LoginWithMasterpassword(ctx context.Context, email string, cfg *config.Conf if err := authenticatedHTTPPost(ctx, cfg.ConfigFile.ApiUrl+"/accounts/prelogin", &preLogin, preLoginRequest{ Email: email, }); err != nil { - notify.Notify("Goldwarden", fmt.Sprintf("Could not pre-login: %v", err), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Could not pre-login: %v", err), "", 0, func() {}) return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not pre-login: %v", err) } @@ -129,13 +129,13 @@ func LoginWithMasterpassword(ctx context.Context, email string, cfg *config.Conf password, err := pinentry.GetPassword("Bitwarden Password", "Enter your Bitwarden password") if err != nil { - notify.Notify("Goldwarden", fmt.Sprintf("Could not get password: %v", err), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Could not get password: %v", err), "", 0, func() {}) return LoginResponseToken{}, crypto.MasterKey{}, "", err } masterKey, err = crypto.DeriveMasterKey([]byte(strings.Clone(password)), email, crypto.KDFConfig{Type: crypto.KDFType(preLogin.KDF), Iterations: uint32(preLogin.KDFIterations), Memory: uint32(preLogin.KDFMemory), Parallelism: uint32(preLogin.KDFParallelism)}) if err != nil { - notify.Notify("Goldwarden", fmt.Sprintf("Could not derive master key: %v", err), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Could not derive master key: %v", err), "", 0, func() {}) return LoginResponseToken{}, crypto.MasterKey{}, "", err } @@ -158,14 +158,14 @@ func LoginWithMasterpassword(ctx context.Context, email string, cfg *config.Conf if ok && bytes.Contains(errsc.body, []byte("TwoFactor")) { loginResponseToken, err = Perform2FA(values, errsc, cfg, ctx) if err != nil { - notify.Notify("Goldwarden", fmt.Sprintf("Could not login via two-factor: %v", err), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Could not login via two-factor: %v", err), "", 0, func() {}) return LoginResponseToken{}, crypto.MasterKey{}, "", err } } else if err != nil && strings.Contains(err.Error(), "Captcha required.") { - notify.Notify("Goldwarden", fmt.Sprintf("Captcha required"), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Captcha required"), "", 0, func() {}) return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("captcha required, please login via the web interface") } else if err != nil { - notify.Notify("Goldwarden", fmt.Sprintf("Could not login via password: %v", err), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Could not login via password: %v", err), "", 0, func() {}) return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not login via password: %v", err) } @@ -278,7 +278,7 @@ func RefreshToken(ctx context.Context, cfg *config.Config) bool { err = authenticatedHTTPPost(ctx, cfg.ConfigFile.IdentityUrl+"/connect/token", &loginResponseToken, values) if err != nil { authLog.Error("Could not refresh token: %s", err.Error()) - notify.Notify("Goldwarden", fmt.Sprintf("Could not refresh token: %v", err), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Could not refresh token: %v", err), "", 0, func() {}) return false } @@ -303,7 +303,7 @@ func RefreshToken(ctx context.Context, cfg *config.Config) bool { )) if err != nil { authLog.Error("Could not refresh token: %s", err.Error()) - notify.Notify("Goldwarden", fmt.Sprintf("Could not refresh token: %v", err), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Could not refresh token: %v", err), "", 0, func() {}) return false } cfg.SetToken(config.LoginToken{ diff --git a/agent/bitwarden/sync.go b/agent/bitwarden/sync.go index 69d6594..c202e8d 100644 --- a/agent/bitwarden/sync.go +++ b/agent/bitwarden/sync.go @@ -35,7 +35,7 @@ func DoFullSync(ctx context.Context, vault *vault.Vault, config *config.Config, sync, err := Sync(ctx, config) if err != nil { log.Error("Could not sync: %v", err) - notify.Notify("Goldwarden", "Could not sync", "", func() {}) + notify.Notify("Goldwarden", "Could not sync", "", 0, func() {}) if allowCache { home, _ := os.UserHomeDir() sync, err = ReadVault(home + path) diff --git a/agent/config/config.go b/agent/config/config.go index 3708d73..79c855b 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -11,6 +11,7 @@ import ( "runtime/debug" "strings" "sync" + "time" "github.com/google/uuid" "github.com/quexten/goldwarden/agent/bitwarden/crypto" @@ -131,7 +132,7 @@ func (c *Config) Unlock(password string) bool { keyBuffer := NewBufferFromBytes(key, c.useMemguard) c.key = &keyBuffer - notify.Notify("Goldwarden", "Vault Unlocked", "", func() {}) + notify.Notify("Goldwarden", "Vault Unlocked", "", 60*time.Second, func() {}) return true } @@ -155,7 +156,7 @@ func (c *Config) Lock() { return } (*c.key).Wipe() - notify.Notify("Goldwarden", "Vault Locked", "", func() {}) + notify.Notify("Goldwarden", "Vault Locked", "", 60*time.Second, func() {}) } func (c *Config) Purge() { diff --git a/agent/notify/dbus.go b/agent/notify/dbus.go index 8cb4b3f..d3ddc22 100644 --- a/agent/notify/dbus.go +++ b/agent/notify/dbus.go @@ -3,12 +3,15 @@ package notify import ( + "time" + "github.com/godbus/dbus/v5" ) var closeListenerMap = make(map[uint32]func()) +var notificationID uint32 = 1000000 -func Notify(title string, body string, actionName string, onclose func()) error { +func Notify(title string, body string, actionName string, timeout time.Duration, onclose func()) error { bus, err := dbus.SessionBus() if err != nil { return err @@ -18,7 +21,10 @@ func Notify(title string, body string, actionName string, onclose func()) error if actionName != "" { actions = append(actions, actionName) } - call := obj.Call("org.freedesktop.Notifications.Notify", 0, "goldwarden", uint32(0), "", title, body, actions, map[string]dbus.Variant{}, int32(60000)) + + notificationID++ + + call := obj.Call("org.freedesktop.Notifications.Notify", 0, "goldwarden", uint32(notificationID), "", title, body, actions, map[string]dbus.Variant{}, int32(60000)) if call.Err != nil { return call.Err } @@ -28,6 +34,18 @@ func Notify(title string, body string, actionName string, onclose func()) error id := call.Body[0].(uint32) closeListenerMap[id] = onclose + if timeout == 0 { + return nil + } else { + go func(id uint32) { + time.Sleep(timeout) + call := obj.Call("org.freedesktop.Notifications.CloseNotification", 0, uint32(id)) + if call.Err != nil { + return + } + }(id) + } + return nil } diff --git a/agent/ssh/ssh.go b/agent/ssh/ssh.go index 512f490..a0f5a85 100644 --- a/agent/ssh/ssh.go +++ b/agent/ssh/ssh.go @@ -7,6 +7,7 @@ import ( "fmt" "net" "os" + "time" "github.com/quexten/goldwarden/agent/config" "github.com/quexten/goldwarden/agent/notify" @@ -125,9 +126,9 @@ func (vaultAgent vaultAgent) Sign(key ssh.PublicKey, data []byte) (*ssh.Signatur var rand = rand.Reader log.Info("Sign Request for key: %s %s accepted", ssh.FingerprintSHA256(key), sshKey.Name) if isGit { - notify.Notify("Goldwarden", fmt.Sprintf("Git Signing Request Approved for %s", sshKey.Name), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("Git Signing Request Approved for %s", sshKey.Name), "", 10*time.Second, func() {}) } else { - notify.Notify("Goldwarden", fmt.Sprintf("SSH Signing Request Approved for %s", sshKey.Name), "", func() {}) + notify.Notify("Goldwarden", fmt.Sprintf("SSH Signing Request Approved for %s", sshKey.Name), "", 10*time.Second, func() {}) } return signer.Sign(rand, data) } diff --git a/agent/unixsocketagent.go b/agent/unixsocketagent.go index 74b59ea..e4fec05 100644 --- a/agent/unixsocketagent.go +++ b/agent/unixsocketagent.go @@ -210,10 +210,14 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error { token, err := cfg.GetToken() if err == nil { if token.AccessToken != "" { - bitwarden.RefreshToken(ctx, &cfg) + gotToken := bitwarden.RefreshToken(ctx, &cfg) + if !gotToken { + log.Warn("Could not get token") + return false + } userSymmetricKey, err := cfg.GetUserSymmetricKey() if err != nil { - fmt.Println(err) + log.Error("Could not get user symmetric key: %s", err.Error()) } var protectedUserSymetricKey crypto.SymmetricEncryptionKey if vault.Keyring.IsMemguard { @@ -224,11 +228,17 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error { err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, &cfg, &protectedUserSymetricKey, true) if err != nil { - fmt.Println(err) + log.Error("Could not sync: %s", err.Error()) } + } else { + log.Warn("Access token is empty") } + } else { + log.Error("Could not get token: %s", err.Error()) } return true + } else { + log.Warn("Could not unlock: %s", err.Error()) } return false })