From ac0e84a46f367657599c88d7f7fda892696306f3 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 Feb 2024 00:24:28 +0100 Subject: [PATCH] Implement initial pinentry --- agent/systemauth/pinentry/go-pinentry.go | 4 +- agent/systemauth/pinentry/keybase-pinentry.go | 4 +- agent/systemauth/pinentry/pinentry.go | 45 +++++ agent/systemauth/pinentry/unimplemented.go | 4 +- agent/unixsocketagent.go | 121 +++++++++++++ agent/virtualagent.go | 169 ------------------ client/client.go | 5 - client/unixsocketclient.go | 71 ++++++-- client/virtualclient.go | 45 ----- cmd/logins.go | 2 +- cmd/root.go | 11 +- cmd/session.go | 30 ++++ ipc/messages/session.go | 77 ++++++++ ui/src/ui/pinentry.py | 65 +++++++ ui/src/ui/pinentry_approval.py | 63 +++++++ 15 files changed, 462 insertions(+), 254 deletions(-) delete mode 100644 agent/virtualagent.go delete mode 100644 client/client.go delete mode 100644 client/virtualclient.go create mode 100644 ui/src/ui/pinentry.py create mode 100644 ui/src/ui/pinentry_approval.py diff --git a/agent/systemauth/pinentry/go-pinentry.go b/agent/systemauth/pinentry/go-pinentry.go index 8e96bca..673950a 100644 --- a/agent/systemauth/pinentry/go-pinentry.go +++ b/agent/systemauth/pinentry/go-pinentry.go @@ -8,7 +8,7 @@ import ( "github.com/twpayne/go-pinentry" ) -func GetPassword(title string, description string) (string, error) { +func getPassword(title string, description string) (string, error) { client, err := pinentry.NewClient( pinentry.WithBinaryNameFromGnuPGAgentConf(), pinentry.WithGPGTTY(), @@ -38,7 +38,7 @@ func GetPassword(title string, description string) (string, error) { } } -func GetApproval(title string, description string) (bool, error) { +func getApproval(title string, description string) (bool, error) { if systemAuthDisabled { return true, nil } diff --git a/agent/systemauth/pinentry/keybase-pinentry.go b/agent/systemauth/pinentry/keybase-pinentry.go index 605016f..88218ef 100644 --- a/agent/systemauth/pinentry/keybase-pinentry.go +++ b/agent/systemauth/pinentry/keybase-pinentry.go @@ -10,7 +10,7 @@ import ( pinentry "github.com/quexten/goldwarden/agent/systemauth/pinentry/keybase-pinentry" ) -func GetPassword(title string, description string) (string, error) { +func getPassword(title string, description string) (string, error) { pinentryInstance := pinentry.New("", logger.New(""), "") result, err := pinentryInstance.Get(keybase1.SecretEntryArg{ Prompt: title, @@ -28,7 +28,7 @@ func GetPassword(title string, description string) (string, error) { return result.Text, nil } -func GetApproval(title string, description string) (bool, error) { +func getApproval(title string, description string) (bool, error) { pinentryInstance := pinentry.New("", logger.New(""), "") result, err := pinentryInstance.Get(keybase1.SecretEntryArg{ Prompt: title, diff --git a/agent/systemauth/pinentry/pinentry.go b/agent/systemauth/pinentry/pinentry.go index 088e1f0..8b0951f 100644 --- a/agent/systemauth/pinentry/pinentry.go +++ b/agent/systemauth/pinentry/pinentry.go @@ -1,6 +1,7 @@ package pinentry import ( + "errors" "os" "github.com/quexten/goldwarden/logging" @@ -9,8 +10,52 @@ import ( var log = logging.GetLogger("Goldwarden", "Pinentry") var systemAuthDisabled = false +type Pinentry struct { + GetPassword func(title string, description string) (string, error) + GetApproval func(title string, description string) (bool, error) +} + +var externalPinentry Pinentry = Pinentry{} + func init() { if os.Getenv("GOLDWARDEN_SYSTEM_AUTH_DISABLED") == "true" { systemAuthDisabled = true } } + +func SetExternalPinentry(pinentry Pinentry) error { + if externalPinentry.GetPassword != nil { + return errors.New("External pinentry already set") + } + + externalPinentry = pinentry + return nil +} + +func GetPassword(title string, description string) (string, error) { + // password, err := getPassword(title, description) + // if err == nil { + // return password, nil + // } + + if externalPinentry.GetPassword != nil { + return externalPinentry.GetPassword(title, description) + } + + return "", errors.New("Not implemented") + // return password, nil +} + +func GetApproval(title string, description string) (bool, error) { + // approval, err := getApproval(title, description) + // if err == nil { + // return approval, nil + // } + + if externalPinentry.GetApproval != nil { + return externalPinentry.GetApproval(title, description) + } + + // return approval, nil + return true, errors.New("Not implemented") +} diff --git a/agent/systemauth/pinentry/unimplemented.go b/agent/systemauth/pinentry/unimplemented.go index 9dd143c..a54a7fa 100644 --- a/agent/systemauth/pinentry/unimplemented.go +++ b/agent/systemauth/pinentry/unimplemented.go @@ -4,12 +4,12 @@ package pinentry import "errors" -func GetPassword(title string, description string) (string, error) { +func getPassword(title string, description string) (string, error) { log.Info("Asking for password is not implemented on this platform") return "", errors.New("Not implemented") } -func GetApproval(title string, description string) (bool, error) { +func getApproval(title string, description string) (bool, error) { log.Info("Asking for approval is not implemented on this platform") return true, errors.New("Not implemented") } diff --git a/agent/unixsocketagent.go b/agent/unixsocketagent.go index 38732eb..f474b8c 100644 --- a/agent/unixsocketagent.go +++ b/agent/unixsocketagent.go @@ -17,6 +17,7 @@ import ( "github.com/quexten/goldwarden/agent/sockets" "github.com/quexten/goldwarden/agent/ssh" "github.com/quexten/goldwarden/agent/systemauth" + "github.com/quexten/goldwarden/agent/systemauth/pinentry" "github.com/quexten/goldwarden/agent/vault" "github.com/quexten/goldwarden/ipc/messages" "github.com/quexten/goldwarden/logging" @@ -62,6 +63,7 @@ func serveAgentSession(c net.Conn, vault *vault.Vault, cfg *config.Config) { continue } + // todo refactor to other file if msg.Type == messages.MessageTypeForEmptyPayload(messages.SessionAuthRequest{}) { log.Info("Received session auth request") req := messages.ParsePayload(msg).(messages.SessionAuthRequest) @@ -93,6 +95,125 @@ func serveAgentSession(c net.Conn, vault *vault.Vault, cfg *config.Config) { continue } + // todo refactor to other file + if msg.Type == messages.MessageTypeForEmptyPayload(messages.PinentryRegistrationRequest{}) { + log.Info("Received pinentry registration request") + + getPasswordChan := make(chan struct { + title string + description string + }) + getPasswordReturnChan := make(chan struct { + password string + err error + }) + getApprovalChan := make(chan struct { + title string + description string + }) + getApprovalReturnChan := make(chan struct { + approved bool + err error + }) + + pe := pinentry.Pinentry{ + GetPassword: func(title string, description string) (string, error) { + getPasswordChan <- struct { + title string + description string + }{title, description} + returnValue := <-getPasswordReturnChan + return returnValue.password, returnValue.err + }, + GetApproval: func(title string, description string) (bool, error) { + getApprovalChan <- struct { + title string + description string + }{title, description} + returnValue := <-getApprovalReturnChan + return returnValue.approved, returnValue.err + }, + } + + pinnentrySetError := pinentry.SetExternalPinentry(pe) + payload := messages.PinentryRegistrationResponse{ + Success: pinnentrySetError == nil, + } + responsePayload, err := messages.IPCMessageFromPayload(payload) + if err != nil { + writeError(c, err) + continue + } + payloadBytes, err := json.Marshal(responsePayload) + if err != nil { + writeError(c, err) + continue + } + + _, err = c.Write(payloadBytes) + if err != nil { + log.Error("Failed writing to socket " + err.Error()) + } + _, err = c.Write([]byte("\n")) + time.Sleep(50 * time.Millisecond) //todo fix properly + + if pinnentrySetError != nil { + return + } + + for { + fmt.Println("Waiting for pinentry request") + select { + case getPasswordRequest := <-getPasswordChan: + log.Info("Received getPassword request") + payload := messages.PinentryPinRequest{ + Message: getPasswordRequest.description, + } + payloadPayload, err := messages.IPCMessageFromPayload(payload) + if err != nil { + writeError(c, err) + continue + } + + payloadBytes, err := json.Marshal(payloadPayload) + if err != nil { + writeError(c, err) + continue + } + + _, err = c.Write(payloadBytes) + if err != nil { + log.Error("Failed writing to socket " + err.Error()) + } + + buf := make([]byte, 1024*1024) + nr, err := c.Read(buf) + if err != nil { + return + } + + data := buf[0:nr] + + var msg messages.IPCMessage + err = json.Unmarshal(data, &msg) + if err != nil { + writeError(c, err) + continue + } + + if msg.Type == messages.MessageTypeForEmptyPayload(messages.PinentryPinResponse{}) { + getPasswordResponse := messages.ParsePayload(msg).(messages.PinentryPinResponse) + getPasswordReturnChan <- struct { + password string + err error + }{getPasswordResponse.Pin, nil} + } + } + } + + continue + } + var responseBytes []byte if action, actionFound := actions.AgentActionsRegistry.Get(msg.Type); actionFound { callingContext := sockets.GetCallingContext(c) diff --git a/agent/virtualagent.go b/agent/virtualagent.go deleted file mode 100644 index f6de33d..0000000 --- a/agent/virtualagent.go +++ /dev/null @@ -1,169 +0,0 @@ -package agent - -import ( - "context" - "encoding/json" - "fmt" - "os/user" - "time" - - "github.com/quexten/goldwarden/agent/actions" - "github.com/quexten/goldwarden/agent/bitwarden" - "github.com/quexten/goldwarden/agent/bitwarden/crypto" - "github.com/quexten/goldwarden/agent/config" - "github.com/quexten/goldwarden/agent/processsecurity" - "github.com/quexten/goldwarden/agent/sockets" - "github.com/quexten/goldwarden/agent/vault" - "github.com/quexten/goldwarden/ipc/messages" -) - -func writeErrorToLog(err error) { - log.Error(err.Error()) -} - -func serveVirtualAgent(recv chan []byte, send chan []byte, ctx context.Context, vault *vault.Vault, cfg *config.Config) { - for { - data := <-recv - - var msg messages.IPCMessage - err := json.Unmarshal(data, &msg) - if err != nil { - writeErrorToLog(err) - continue - } - - responseBytes := []byte{} - if action, actionFound := actions.AgentActionsRegistry.Get(msg.Type); actionFound { - user, _ := user.Current() - process := "goldwarden" - parent := "SINGLE_PROC_MODE" - grandparent := "SINGLE_PROC_MODE" - callingContext := sockets.CallingContext{ - UserName: user.Name, - ProcessName: process, - ParentProcessName: parent, - GrandParentProcessName: grandparent, - } - payload, err := action(msg, cfg, vault, &callingContext) - if err != nil { - writeErrorToLog(err) - continue - } - responseBytes, err = json.Marshal(payload) - if err != nil { - writeErrorToLog(err) - continue - } - } else { - payload := messages.ActionResponse{ - Success: false, - Message: "Action not found", - } - payloadBytes, err := json.Marshal(payload) - if err != nil { - writeErrorToLog(err) - continue - } - responseBytes = payloadBytes - } - - send <- responseBytes - } -} - -func StartVirtualAgent(runtimeConfig config.RuntimeConfig) (chan []byte, chan []byte) { - ctx := context.Background() - - var keyring crypto.Keyring - if runtimeConfig.UseMemguard { - keyring = crypto.NewMemguardKeyring(nil) - } else { - keyring = crypto.NewMemoryKeyring(nil) - } - - var vault = vault.NewVault(&keyring) - cfg, err := config.ReadConfig(runtimeConfig) - if err != nil { - var cfg = config.DefaultConfig(runtimeConfig.UseMemguard) - cfg.WriteConfig() - } - cfg.ConfigFile.RuntimeConfig = runtimeConfig - if cfg.ConfigFile.RuntimeConfig.ApiURI != "" { - cfg.ConfigFile.ApiUrl = cfg.ConfigFile.RuntimeConfig.ApiURI - } - if cfg.ConfigFile.RuntimeConfig.IdentityURI != "" { - cfg.ConfigFile.IdentityUrl = cfg.ConfigFile.RuntimeConfig.IdentityURI - } - if cfg.ConfigFile.RuntimeConfig.NotificationsURI != "" { - cfg.ConfigFile.NotificationsUrl = cfg.ConfigFile.RuntimeConfig.NotificationsURI - } - if cfg.ConfigFile.RuntimeConfig.DeviceUUID != "" { - cfg.ConfigFile.DeviceUUID = cfg.ConfigFile.RuntimeConfig.DeviceUUID - } - - if !cfg.IsLocked() && !cfg.ConfigFile.RuntimeConfig.DoNotPersistConfig { - log.Warn("Config is not locked. SET A PIN!!") - token, err := cfg.GetToken() - if err == nil { - if token.AccessToken != "" { - bitwarden.RefreshToken(ctx, &cfg) - userSymmetricKey, err := cfg.GetUserSymmetricKey() - if err != nil { - fmt.Println(err) - } - var protectedUserSymetricKey crypto.SymmetricEncryptionKey - if keyring.IsMemguard { - protectedUserSymetricKey, err = crypto.MemguardSymmetricEncryptionKeyFromBytes(userSymmetricKey) - } else { - protectedUserSymetricKey, err = crypto.MemorySymmetricEncryptionKeyFromBytes(userSymmetricKey) - } - - err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, &cfg, &protectedUserSymetricKey, true) - if err != nil { - fmt.Println(err) - } - } - } - } - processsecurity.DisableDumpable() - err = processsecurity.MonitorLocks(func() { - cfg.Lock() - vault.Clear() - vault.Keyring.Lock() - }) - if err != nil { - log.Warn("Could not monitor screensaver: %s", err.Error()) - } - - go func() { - for { - time.Sleep(TokenRefreshInterval) - if !cfg.IsLocked() { - bitwarden.RefreshToken(ctx, &cfg) - } - } - }() - - go func() { - for { - time.Sleep(FullSyncInterval) - if !cfg.IsLocked() { - token, err := cfg.GetToken() - if err != nil { - log.Warn("Could not get token: %s", err.Error()) - continue - } - - bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, &cfg, nil, false) - } - } - }() - - recv := make(chan []byte) - send := make(chan []byte) - - go func() { - go serveVirtualAgent(recv, send, ctx, vault, &cfg) - }() - return recv, send -} diff --git a/client/client.go b/client/client.go deleted file mode 100644 index c0ce142..0000000 --- a/client/client.go +++ /dev/null @@ -1,5 +0,0 @@ -package client - -type Client interface { - SendToAgent(request interface{}) (interface{}, error) -} diff --git a/client/unixsocketclient.go b/client/unixsocketclient.go index f24be0d..3bab297 100644 --- a/client/unixsocketclient.go +++ b/client/unixsocketclient.go @@ -3,7 +3,6 @@ package client import ( "encoding/json" "io" - "log" "net" "github.com/quexten/goldwarden/agent/config" @@ -16,13 +15,17 @@ type UnixSocketClient struct { runtimeConfig *config.RuntimeConfig } +type UnixSocketConnection struct { + conn net.Conn +} + func NewUnixSocketClient(runtimeConfig *config.RuntimeConfig) UnixSocketClient { return UnixSocketClient{ runtimeConfig: runtimeConfig, } } -func reader(r io.Reader) interface{} { +func Reader(r io.Reader) interface{} { buf := make([]byte, READ_BUFFER) for { n, err := r.Read(buf[:]) @@ -40,25 +43,55 @@ func reader(r io.Reader) interface{} { } func (client UnixSocketClient) SendToAgent(request interface{}) (interface{}, error) { - c, err := net.Dial("unix", client.runtimeConfig.GoldwardenSocketPath) + c, err := client.Connect() if err != nil { return nil, err } defer c.Close() - - message, err := messages.IPCMessageFromPayload(request) - if err != nil { - panic(err) - } - messageJson, err := json.Marshal(message) - if err != nil { - panic(err) - } - - _, err = c.Write(messageJson) - if err != nil { - log.Fatal("write error:", err) - } - result := reader(c) - return messages.ParsePayload(result.(messages.IPCMessage)), nil + return c.SendCommand(request) +} + +func (client UnixSocketClient) Connect() (UnixSocketConnection, error) { + c, err := net.Dial("unix", client.runtimeConfig.GoldwardenSocketPath) + if err != nil { + return UnixSocketConnection{}, err + } + return UnixSocketConnection{conn: c}, nil +} + +func (conn UnixSocketConnection) SendCommand(request interface{}) (interface{}, error) { + err := conn.WriteMessage(request) + if err != nil { + return nil, err + } + return conn.ReadMessage(), nil +} + +func (conn UnixSocketConnection) ReadMessage() interface{} { + result := Reader(conn.conn) + // fmt.Println("ReadMessag") + // fmt.Println(result) + payload := messages.ParsePayload(result.(messages.IPCMessage)) + // fmt.Println(payload) + return payload +} + +func (conn UnixSocketConnection) WriteMessage(message interface{}) error { + // fmt.Println("WriteMessage") + messagePacket, err := messages.IPCMessageFromPayload(message) + // fmt.Println(messagePacket) + if err != nil { + panic(err) + } + messageJson, err := json.Marshal(messagePacket) + if err != nil { + panic(err) + } + // fmt.Println(messageJson) + _, err = conn.conn.Write(messageJson) + return err +} + +func (conn UnixSocketConnection) Close() { + conn.conn.Close() } diff --git a/client/virtualclient.go b/client/virtualclient.go deleted file mode 100644 index 8113901..0000000 --- a/client/virtualclient.go +++ /dev/null @@ -1,45 +0,0 @@ -package client - -import ( - "encoding/json" - - "github.com/quexten/goldwarden/ipc/messages" -) - -func NewVirtualClient(recv chan []byte, send chan []byte) VirtualClient { - return VirtualClient{ - recv, - send, - } -} - -type VirtualClient struct { - recv chan []byte - send chan []byte -} - -func virtualReader(recv chan []byte) interface{} { - for { - var message messages.IPCMessage - err := json.Unmarshal(<-recv, &message) - if err != nil { - panic(err) - } - return message - } -} - -func (client VirtualClient) SendToAgent(request interface{}) (interface{}, error) { - message, err := messages.IPCMessageFromPayload(request) - if err != nil { - panic(err) - } - messageJson, err := json.Marshal(message) - if err != nil { - panic(err) - } - - client.send <- messageJson - result := virtualReader(client.recv) - return messages.ParsePayload(result.(messages.IPCMessage)), nil -} diff --git a/cmd/logins.go b/cmd/logins.go index c4d8f23..9f96a86 100644 --- a/cmd/logins.go +++ b/cmd/logins.go @@ -89,7 +89,7 @@ var listLoginsCmd = &cobra.Command{ }, } -func ListLogins(client client.Client) ([]messages.DecryptedLoginCipher, error) { +func ListLogins(client client.UnixSocketClient) ([]messages.DecryptedLoginCipher, error) { resp, err := client.SendToAgent(messages.ListLoginsRequest{}) if err != nil { return []messages.DecryptedLoginCipher{}, err diff --git a/cmd/root.go b/cmd/root.go index b1325aa..888326a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,14 +4,13 @@ import ( "fmt" "os" - "github.com/quexten/goldwarden/agent" "github.com/quexten/goldwarden/agent/config" "github.com/quexten/goldwarden/client" "github.com/quexten/goldwarden/ipc/messages" "github.com/spf13/cobra" ) -var commandClient client.Client +var commandClient client.UnixSocketClient var runtimeConfig config.RuntimeConfig var rootCmd = &cobra.Command{ @@ -25,13 +24,7 @@ var rootCmd = &cobra.Command{ func Execute(cfg config.RuntimeConfig) { runtimeConfig = cfg - goldwardenSingleProcess := os.Getenv("GOLDWARDEN_SINGLE_PROCESS") - if goldwardenSingleProcess == "true" { - recv, send := agent.StartVirtualAgent(runtimeConfig) - commandClient = client.NewVirtualClient(send, recv) - } else { - commandClient = client.NewUnixSocketClient(&cfg) - } + commandClient = client.NewUnixSocketClient(&cfg) err := rootCmd.Execute() if err != nil { diff --git a/cmd/session.go b/cmd/session.go index 7b4ee48..42daa1b 100644 --- a/cmd/session.go +++ b/cmd/session.go @@ -34,7 +34,37 @@ var pinentry = &cobra.Command{ Short: "Registers as a pinentry program", Long: `Registers as a pinentry program.`, Run: func(cmd *cobra.Command, args []string) { + conn, err := commandClient.Connect() + if err != nil { + panic(err) + } + defer conn.Close() + _, err = conn.SendCommand(messages.PinentryRegistrationRequest{}) + if err != nil { + panic(err) + } + for { + response := conn.ReadMessage() + switch response.(type) { + case messages.PinentryPinRequest: + fmt.Println("pin-request" + "," + response.(messages.PinentryPinRequest).Message) + case messages.PinentryApprovalRequest: + fmt.Println("approval-request" + "," + response.(messages.PinentryApprovalRequest).Message) + } + + // read line + reader := bufio.NewReader(os.Stdin) + text, _ := reader.ReadString('\n') + text = strings.TrimSuffix(text, "\n") + + switch response.(type) { + case messages.PinentryPinRequest: + err = conn.WriteMessage(messages.PinentryPinResponse{Pin: text}) + case messages.PinentryApprovalRequest: + err = conn.WriteMessage(messages.PinentryApprovalResponse{Approved: text == "true"}) + } + } }, } diff --git a/ipc/messages/session.go b/ipc/messages/session.go index b995897..7b76149 100644 --- a/ipc/messages/session.go +++ b/ipc/messages/session.go @@ -10,6 +10,29 @@ type SessionAuthResponse struct { Verified bool } +type PinentryRegistrationRequest struct { +} + +type PinentryRegistrationResponse struct { + Success bool +} + +type PinentryPinRequest struct { + Message string +} + +type PinentryPinResponse struct { + Pin string +} + +type PinentryApprovalRequest struct { + Message string +} + +type PinentryApprovalResponse struct { + Approved bool +} + func init() { registerPayloadParser(func(payload []byte) (interface{}, error) { var req SessionAuthRequest @@ -28,4 +51,58 @@ func init() { } return req, nil }, SessionAuthResponse{}) + + registerPayloadParser(func(payload []byte) (interface{}, error) { + var req PinentryRegistrationRequest + err := json.Unmarshal(payload, &req) + if err != nil { + panic("Unmarshal: " + err.Error()) + } + return req, nil + }, PinentryRegistrationRequest{}) + + registerPayloadParser(func(payload []byte) (interface{}, error) { + var req PinentryRegistrationResponse + err := json.Unmarshal(payload, &req) + if err != nil { + panic("Unmarshal: " + err.Error()) + } + return req, nil + }, PinentryRegistrationResponse{}) + + registerPayloadParser(func(payload []byte) (interface{}, error) { + var req PinentryPinRequest + err := json.Unmarshal(payload, &req) + if err != nil { + panic("Unmarshal: " + err.Error()) + } + return req, nil + }, PinentryPinRequest{}) + + registerPayloadParser(func(payload []byte) (interface{}, error) { + var req PinentryPinResponse + err := json.Unmarshal(payload, &req) + if err != nil { + panic("Unmarshal: " + err.Error()) + } + return req, nil + }, PinentryPinResponse{}) + + registerPayloadParser(func(payload []byte) (interface{}, error) { + var req PinentryApprovalRequest + err := json.Unmarshal(payload, &req) + if err != nil { + panic("Unmarshal: " + err.Error()) + } + return req, nil + }, PinentryApprovalRequest{}) + + registerPayloadParser(func(payload []byte) (interface{}, error) { + var req PinentryApprovalResponse + err := json.Unmarshal(payload, &req) + if err != nil { + panic("Unmarshal: " + err.Error()) + } + return req, nil + }, PinentryApprovalResponse{}) } diff --git a/ui/src/ui/pinentry.py b/ui/src/ui/pinentry.py new file mode 100644 index 0000000..de2edc5 --- /dev/null +++ b/ui/src/ui/pinentry.py @@ -0,0 +1,65 @@ +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 + +message = sys.stdin.readline() + +class MyApp(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 + +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) + + 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.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) + + # Cancel button + cancel_button = Gtk.Button(label="Cancel") + cancel_button.set_hexpand(True) # Make the button expand horizontally + 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(self.password_entry.get_text()) + 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 Pinentry") + +app = MyApp(application_id="com.quexten.Goldwarden.pinentry") +app.run(sys.argv) \ No newline at end of file diff --git a/ui/src/ui/pinentry_approval.py b/ui/src/ui/pinentry_approval.py new file mode 100644 index 0000000..d1daf52 --- /dev/null +++ b/ui/src/ui/pinentry_approval.py @@ -0,0 +1,63 @@ +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 + +message = sys.stdin.readline() + +class MyApp(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 + +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) + + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + self.stack.add_child(box) + + label = Gtk.Label(label=message) + box.append(label) + + # 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") + 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") + 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) \ No newline at end of file