From f7ad28045afd4da8c7981db08ab26772232eff0c Mon Sep 17 00:00:00 2001 From: Tyler Creller Date: Mon, 5 Feb 2024 15:26:25 -0500 Subject: [PATCH] OCM-4965 | feat: Secure Store additional error handling and removal of keys --- authentication/securestore/main.go | 66 +++++++++++++++++++++++++----- examples/secure_store.go | 18 ++++++-- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/authentication/securestore/main.go b/authentication/securestore/main.go index 6001f819..39e24ac9 100644 --- a/authentication/securestore/main.go +++ b/authentication/securestore/main.go @@ -17,16 +17,21 @@ const ( MaxWindowsByteSize = 2500 // Windows Credential Manager has a 2500 byte limit ) +var ( + ErrNoBackendsAvailable = fmt.Errorf("no backends available, expected one of %v", allowedBackends) + // The order of the backends is important. The first backend in the list is the first one + // that will attempt to be used. + allowedBackends = []keyring.BackendType{ + keyring.WinCredBackend, + keyring.KeychainBackend, + keyring.SecretServiceBackend, + keyring.PassBackend, + } +) + func getKeyringConfig() keyring.Config { return keyring.Config{ - // The order of the backends is important. The first backend in the list is the first one - // that will attempt to be used. - AllowedBackends: []keyring.BackendType{ - keyring.WinCredBackend, - keyring.KeychainBackend, - keyring.SecretServiceBackend, - keyring.PassBackend, - }, + AllowedBackends: allowedBackends, // Generic ServiceName: ItemKey, // MacOS @@ -48,9 +53,16 @@ func getKeyringConfig() keyring.Config { // The first backend in the slice is the first one that will be used. func AvailableBackends() []string { b := []string{} - for _, k := range keyring.AvailableBackends() { - b = append(b, string(k)) + + // Intersection between available backends from OS and allowed backends + for _, avail := range keyring.AvailableBackends() { + for _, allowed := range allowedBackends { + if avail == allowed { + b = append(b, string(allowed)) + } + } } + return b } @@ -58,6 +70,10 @@ func AvailableBackends() []string { // // Note: CGO_ENABLED=1 is required for OSX Keychain and darwin builds func UpsertConfigToKeyring(creds []byte) error { + if err := validateBackends(); err != nil { + return err + } + ring, err := keyring.Open(getKeyringConfig()) if err != nil { return err @@ -84,10 +100,32 @@ func UpsertConfigToKeyring(creds []byte) error { return err } +// RemoveConfigFromKeyring will remove the credentials from the first priority OS secure store. +// +// Note: CGO_ENABLED=1 is required for OSX Keychain and darwin builds +func RemoveConfigFromKeyring() error { + if err := validateBackends(); err != nil { + return err + } + + ring, err := keyring.Open(getKeyringConfig()) + if err != nil { + return err + } + + err = ring.Remove(ItemKey) + + return err +} + // GetConfigFromKeyring will retrieve the credentials from the first priority OS secure store. // // Note: CGO_ENABLED=1 is required for OSX Keychain and darwin builds func GetConfigFromKeyring() ([]byte, error) { + if err := validateBackends(); err != nil { + return nil, err + } + credentials := []byte("") ring, err := keyring.Open(getKeyringConfig()) @@ -118,6 +156,14 @@ func GetConfigFromKeyring() ([]byte, error) { } +// Validates that at least one backend is available +func validateBackends() error { + if len(AvailableBackends()) == 0 { + return ErrNoBackendsAvailable + } + return nil +} + // Compresses credential bytes to help ensure all OS secure stores can store the data. // Windows Credential Manager has a 2500 byte limit. func compressConfig(creds []byte) ([]byte, error) { diff --git a/examples/secure_store.go b/examples/secure_store.go index dd2f954b..7bee815f 100644 --- a/examples/secure_store.go +++ b/examples/secure_store.go @@ -27,14 +27,26 @@ func main() { config := []byte("mybytestringagain") // Upsert to keyring - securestore.UpsertConfigToKeyring(config) + err = securestore.UpsertConfigToKeyring(config) + if err != nil { + fmt.Fprintf(os.Stderr, "Error upserting to keyring: %v", err) + os.Exit(1) + } // Upsert again to keyring config = []byte(token) - securestore.UpsertConfigToKeyring(config) + err = securestore.UpsertConfigToKeyring(config) + if err != nil { + fmt.Fprintf(os.Stderr, "Error upserting to keyring: %v", err) + os.Exit(1) + } // Read bytes back from Keyring - readVal, _ := securestore.GetConfigFromKeyring() + readVal, err := securestore.GetConfigFromKeyring() + if err != nil { + fmt.Fprintf(os.Stderr, "Error reading from keyring: %v", err) + os.Exit(1) + } // Should be a token fmt.Printf("Read from keyring: %s\n", string(readVal)) }