added keychain support
This commit is contained in:
@@ -100,18 +100,52 @@ func StartRosettaX87Service(myWindow fyne.Window, updateAllStatuses func()) {
|
||||
// Clean up any existing rosettax87 processes first
|
||||
CleanupExistingServices()
|
||||
|
||||
// Load user preferences
|
||||
prefs, err := utils.LoadPrefs()
|
||||
if err != nil {
|
||||
log.Printf("Failed to load preferences: %v", err)
|
||||
prefs = &utils.UserPrefs{} // Use default prefs
|
||||
}
|
||||
|
||||
// Try to get saved password if the user has enabled saving
|
||||
var savedPassword string
|
||||
if prefs.SaveSudoPassword {
|
||||
savedPassword, _ = utils.GetSudoPassword() // Ignore errors, just use empty string
|
||||
}
|
||||
|
||||
// Show password dialog
|
||||
passwordEntry := widget.NewPasswordEntry()
|
||||
passwordEntry.SetPlaceHolder("Enter your sudo password")
|
||||
passwordEntry.SetText(savedPassword) // Prefill with saved password if available
|
||||
passwordEntry.Resize(fyne.NewSize(300, passwordEntry.MinSize().Height))
|
||||
|
||||
// Create checkbox for saving password
|
||||
savePasswordCheck := widget.NewCheck("Save password securely in keychain", nil)
|
||||
savePasswordCheck.SetChecked(prefs.SaveSudoPassword)
|
||||
|
||||
// Add status label if password is already saved
|
||||
var statusLabel *widget.Label
|
||||
if utils.HasSavedSudoPassword() {
|
||||
statusLabel = widget.NewLabel("✓ Password already saved in keychain")
|
||||
statusLabel.Importance = widget.LowImportance
|
||||
}
|
||||
|
||||
// Create a container with proper sizing
|
||||
passwordForm := widget.NewForm(widget.NewFormItem("Password:", passwordEntry))
|
||||
passwordContainer := container.NewVBox(
|
||||
|
||||
var containerItems []fyne.CanvasObject
|
||||
containerItems = append(containerItems,
|
||||
widget.NewLabel("Enter your sudo password to start the RosettaX87 service:"),
|
||||
passwordForm,
|
||||
savePasswordCheck,
|
||||
)
|
||||
passwordContainer.Resize(fyne.NewSize(400, 100))
|
||||
|
||||
if statusLabel != nil {
|
||||
containerItems = append(containerItems, statusLabel)
|
||||
}
|
||||
|
||||
passwordContainer := container.NewVBox(containerItems...)
|
||||
passwordContainer.Resize(fyne.NewSize(400, 140))
|
||||
|
||||
// Create the dialog variable so we can reference it in the callback
|
||||
var passwordDialog dialog.Dialog
|
||||
@@ -124,6 +158,25 @@ func StartRosettaX87Service(myWindow fyne.Window, updateAllStatuses func()) {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle password saving/deleting based on checkbox state
|
||||
shouldSavePassword := savePasswordCheck.Checked
|
||||
if shouldSavePassword {
|
||||
// Save password to keychain
|
||||
if err := utils.SaveSudoPassword(password); err != nil {
|
||||
log.Printf("Failed to save password to keychain: %v", err)
|
||||
// Don't block the service start, just log the error
|
||||
}
|
||||
} else {
|
||||
// Delete any existing saved password
|
||||
utils.DeleteSudoPassword() // Ignore errors
|
||||
}
|
||||
|
||||
// Update preferences
|
||||
prefs.SaveSudoPassword = shouldSavePassword
|
||||
if err := utils.SavePrefs(prefs); err != nil {
|
||||
log.Printf("Failed to save preferences: %v", err)
|
||||
}
|
||||
|
||||
// Close the dialog
|
||||
passwordDialog.Hide()
|
||||
|
||||
@@ -360,3 +413,24 @@ func CleanupService() {
|
||||
serviceCmd = nil
|
||||
servicePID = 0
|
||||
}
|
||||
|
||||
// ClearSavedPassword removes the saved password and shows a confirmation dialog
|
||||
func ClearSavedPassword(myWindow fyne.Window) {
|
||||
if !utils.HasSavedSudoPassword() {
|
||||
dialog.ShowInformation("Password Status", "No password is currently saved.", myWindow)
|
||||
return
|
||||
}
|
||||
|
||||
dialog.ShowConfirm("Clear Saved Password",
|
||||
"Are you sure you want to remove the saved password from the keychain?",
|
||||
func(confirmed bool) {
|
||||
if confirmed {
|
||||
err := utils.DeleteSudoPassword()
|
||||
if err != nil {
|
||||
dialog.ShowError(fmt.Errorf("failed to clear saved password: %v", err), myWindow)
|
||||
} else {
|
||||
dialog.ShowInformation("Password Cleared", "The saved password has been removed from the keychain.", myWindow)
|
||||
}
|
||||
}
|
||||
}, myWindow)
|
||||
}
|
||||
|
71
pkg/utils/keychain.go
Normal file
71
pkg/utils/keychain.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/zalando/go-keyring"
|
||||
)
|
||||
|
||||
const (
|
||||
serviceName = "TurtleSilicon"
|
||||
accountName = "sudo_password"
|
||||
)
|
||||
|
||||
// SaveSudoPassword securely stores the sudo password in the system keychain
|
||||
func SaveSudoPassword(password string) error {
|
||||
if password == "" {
|
||||
return fmt.Errorf("password cannot be empty")
|
||||
}
|
||||
|
||||
err := keyring.Set(serviceName, accountName, password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save password to keychain: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Password saved securely to keychain")
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSudoPassword retrieves the saved sudo password from the system keychain
|
||||
func GetSudoPassword() (string, error) {
|
||||
password, err := keyring.Get(serviceName, accountName)
|
||||
if err != nil {
|
||||
// If the password doesn't exist, return empty string instead of error
|
||||
if err == keyring.ErrNotFound {
|
||||
return "", nil
|
||||
}
|
||||
return "", fmt.Errorf("failed to retrieve password from keychain: %v", err)
|
||||
}
|
||||
|
||||
return password, nil
|
||||
}
|
||||
|
||||
// DeleteSudoPassword removes the saved sudo password from the system keychain
|
||||
func DeleteSudoPassword() error {
|
||||
err := keyring.Delete(serviceName, accountName)
|
||||
if err != nil {
|
||||
// If the password doesn't exist, that's fine
|
||||
if err == keyring.ErrNotFound {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to delete password from keychain: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Password removed from keychain")
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasSavedSudoPassword checks if a sudo password is saved in the keychain
|
||||
func HasSavedSudoPassword() bool {
|
||||
_, err := keyring.Get(serviceName, accountName)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// GetPasswordStatusText returns a user-friendly status text for password saving
|
||||
func GetPasswordStatusText() string {
|
||||
if HasSavedSudoPassword() {
|
||||
return "Password saved in keychain"
|
||||
}
|
||||
return "No password saved"
|
||||
}
|
@@ -11,6 +11,7 @@ type UserPrefs struct {
|
||||
TurtleWoWPath string `json:"turtlewow_path"`
|
||||
CrossOverPath string `json:"crossover_path"`
|
||||
EnvironmentVariables string `json:"environment_variables"`
|
||||
SaveSudoPassword bool `json:"save_sudo_password"`
|
||||
}
|
||||
|
||||
func getPrefsPath() (string, error) {
|
||||
|
Reference in New Issue
Block a user