@@ -5,33 +5,32 @@ import (
5
5
"compress/gzip"
6
6
"fmt"
7
7
"io"
8
+ "strings"
8
9
9
10
"github.com/99designs/keyring"
10
11
)
11
12
12
13
const (
13
- SecureStoreConfigKey = "securestore" // OCM_CONFIG key to enable secure OS store
14
14
KindInternetPassword = "Internet password" // MacOS Keychain item kind
15
15
ItemKey = "RedHatSSO"
16
16
CollectionName = "login" // Common OS default collection name
17
17
MaxWindowsByteSize = 2500 // Windows Credential Manager has a 2500 byte limit
18
18
)
19
19
20
20
var (
21
- ErrNoBackendsAvailable = fmt .Errorf ("no backends available, expected one of %v" , allowedBackends )
22
- // The order of the backends is important. The first backend in the list is the first one
23
- // that will attempt to be used.
24
- allowedBackends = []keyring.BackendType {
25
- keyring .WinCredBackend ,
26
- keyring .KeychainBackend ,
27
- keyring .SecretServiceBackend ,
28
- keyring .PassBackend ,
21
+ ErrKeyringUnavailable = fmt .Errorf ("keyring is valid but is not available on the current OS" )
22
+ ErrKeyringInvalid = fmt .Errorf ("keyring is invalid, expected one of: [%v]" , strings .Join (AllowedBackends , ", " ))
23
+ AllowedBackends = []string {
24
+ string (keyring .WinCredBackend ),
25
+ string (keyring .KeychainBackend ),
26
+ string (keyring .SecretServiceBackend ),
27
+ string (keyring .PassBackend ),
29
28
}
30
29
)
31
30
32
- func getKeyringConfig () keyring.Config {
31
+ func getKeyringConfig (backend string ) keyring.Config {
33
32
return keyring.Config {
34
- AllowedBackends : allowedBackends ,
33
+ AllowedBackends : []keyring. BackendType { keyring . BackendType ( backend )} ,
35
34
// Generic
36
35
ServiceName : ItemKey ,
37
36
// MacOS
@@ -46,35 +45,51 @@ func getKeyringConfig() keyring.Config {
46
45
}
47
46
}
48
47
49
- // AvailableBackends provides a slice of all available backend keys on the current OS.
48
+ // IsBackendAvailable provides validation that the desired backend is available on the current OS.
50
49
//
51
- // Note: CGO_ENABLED=1 is required for OSX Keychain and darwin builds
50
+ // Note: CGO_ENABLED=1 is required for darwin builds (enables OSX Keychain)
51
+ func IsBackendAvailable (backend string ) (isAvailable bool ) {
52
+ if backend == "" {
53
+ return false
54
+ }
55
+
56
+ for _ , avail := range AvailableBackends () {
57
+ if avail == backend {
58
+ isAvailable = true
59
+ break
60
+ }
61
+ }
62
+
63
+ return isAvailable
64
+ }
65
+
66
+ // AvailableBackends provides a slice of all available backend keys on the current OS.
52
67
//
53
- // The first backend in the slice is the first one that will be used.
68
+ // Note: CGO_ENABLED=1 is required for darwin builds (enables OSX Keychain)
54
69
func AvailableBackends () []string {
55
70
b := []string {}
56
71
57
72
// Intersection between available backends from OS and allowed backends
58
73
for _ , avail := range keyring .AvailableBackends () {
59
- for _ , allowed := range allowedBackends {
60
- if avail == allowed {
61
- b = append (b , string ( allowed ) )
74
+ for _ , allowed := range AllowedBackends {
75
+ if string ( avail ) == allowed {
76
+ b = append (b , allowed )
62
77
}
63
78
}
64
79
}
65
80
66
81
return b
67
82
}
68
83
69
- // UpsertConfigToKeyring will upsert the provided credentials to first priority OS secure store.
84
+ // UpsertConfigToKeyring will upsert the provided credentials to the desired OS secure store.
70
85
//
71
- // Note: CGO_ENABLED=1 is required for OSX Keychain and darwin builds
72
- func UpsertConfigToKeyring (creds []byte ) error {
73
- if err := validateBackends ( ); err != nil {
86
+ // Note: CGO_ENABLED=1 is required for darwin builds (enables OSX Keychain)
87
+ func UpsertConfigToKeyring (backend string , creds []byte ) error {
88
+ if err := ValidateBackend ( backend ); err != nil {
74
89
return err
75
90
}
76
91
77
- ring , err := keyring .Open (getKeyringConfig ())
92
+ ring , err := keyring .Open (getKeyringConfig (backend ))
78
93
if err != nil {
79
94
return err
80
95
}
@@ -86,7 +101,7 @@ func UpsertConfigToKeyring(creds []byte) error {
86
101
87
102
// check if available backend contains windows credential manager and exceeds the byte limit
88
103
if len (compressed ) > MaxWindowsByteSize &&
89
- keyring . AvailableBackends ()[ 0 ] == keyring .WinCredBackend {
104
+ backend == string ( keyring .WinCredBackend ) {
90
105
return fmt .Errorf ("credentials are too large for Windows Credential Manager: %d bytes (max %d)" , len (compressed ), MaxWindowsByteSize )
91
106
}
92
107
@@ -103,32 +118,42 @@ func UpsertConfigToKeyring(creds []byte) error {
103
118
// RemoveConfigFromKeyring will remove the credentials from the first priority OS secure store.
104
119
//
105
120
// Note: CGO_ENABLED=1 is required for OSX Keychain and darwin builds
106
- func RemoveConfigFromKeyring () error {
107
- if err := validateBackends ( ); err != nil {
121
+ func RemoveConfigFromKeyring (backend string ) error {
122
+ if err := ValidateBackend ( backend ); err != nil {
108
123
return err
109
124
}
110
125
111
- ring , err := keyring .Open (getKeyringConfig ())
126
+ ring , err := keyring .Open (getKeyringConfig (backend ))
112
127
if err != nil {
113
128
return err
114
129
}
115
130
116
131
err = ring .Remove (ItemKey )
132
+ if err != nil {
133
+ if err == keyring .ErrKeyNotFound {
134
+ // Ignore not found errors, key is already removed
135
+ return nil
136
+ }
137
+
138
+ if strings .Contains (err .Error (), "Keychain Error. (-25244)" ) {
139
+ return fmt .Errorf ("%s\n This application may not have permission to delete from the Keychain. Please check the permissions in the Keychain and try again" , err .Error ())
140
+ }
141
+ }
117
142
118
143
return err
119
144
}
120
145
121
146
// GetConfigFromKeyring will retrieve the credentials from the first priority OS secure store.
122
147
//
123
- // Note: CGO_ENABLED=1 is required for OSX Keychain and darwin builds
124
- func GetConfigFromKeyring () ([]byte , error ) {
125
- if err := validateBackends ( ); err != nil {
148
+ // Note: CGO_ENABLED=1 is required for darwin builds (enables OSX Keychain)
149
+ func GetConfigFromKeyring (backend string ) ([]byte , error ) {
150
+ if err := ValidateBackend ( backend ); err != nil {
126
151
return nil , err
127
152
}
128
153
129
154
credentials := []byte ("" )
130
155
131
- ring , err := keyring .Open (getKeyringConfig ())
156
+ ring , err := keyring .Open (getKeyringConfig (backend ))
132
157
if err != nil {
133
158
return nil , err
134
159
}
@@ -156,11 +181,29 @@ func GetConfigFromKeyring() ([]byte, error) {
156
181
157
182
}
158
183
159
- // Validates that at least one backend is available
160
- func validateBackends () error {
161
- if len (AvailableBackends ()) == 0 {
162
- return ErrNoBackendsAvailable
184
+ // Validates that the requested backend is valid and available, returns an error if not.
185
+ //
186
+ // Note: CGO_ENABLED=1 is required for darwin builds (enables OSX Keychain)
187
+ func ValidateBackend (backend string ) error {
188
+ if backend == "" {
189
+ return ErrKeyringInvalid
190
+ } else {
191
+ isAllowedBackend := false
192
+ for _ , allowed := range AllowedBackends {
193
+ if allowed == backend {
194
+ isAllowedBackend = true
195
+ break
196
+ }
197
+ }
198
+ if ! isAllowedBackend {
199
+ return ErrKeyringInvalid
200
+ }
201
+ }
202
+
203
+ if ! IsBackendAvailable (backend ) {
204
+ return ErrKeyringUnavailable
163
205
}
206
+
164
207
return nil
165
208
}
166
209
0 commit comments