Add timeout to notifications

This commit is contained in:
Bernd Schoolmann 2024-01-19 05:14:25 +01:00
parent 715e1eb30a
commit 794593a71f
No known key found for this signature in database
8 changed files with 73 additions and 30 deletions

View File

@ -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()
}()

View File

@ -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())
}
}

View File

@ -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{

View File

@ -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)

View File

@ -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() {

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
})