3
3
package gosnowflake
4
4
5
5
import (
6
+ "crypto/sha256"
7
+ "encoding/hex"
6
8
"encoding/json"
7
9
"errors"
8
10
"fmt"
9
11
"github.com/99designs/keyring"
10
- "golang.org/x/sys/unix"
11
12
"os"
12
13
"path/filepath"
13
14
"runtime"
@@ -24,9 +25,20 @@ const (
24
25
25
26
const (
26
27
credCacheDirEnv = "SF_TEMPORARY_CREDENTIAL_CACHE_DIR"
27
- credCacheFileName = "temporary_credential .json"
28
+ credCacheFileName = "credential_cache_v1 .json"
28
29
)
29
30
31
+ type cacheDirConf struct {
32
+ envVar string
33
+ pathSegments []string
34
+ }
35
+
36
+ var defaultLinuxCacheDirConf = []cacheDirConf {
37
+ {envVar : credCacheDirEnv , pathSegments : []string {}},
38
+ {envVar : "XDG_CACHE_DIR" , pathSegments : []string {"snowflake" }},
39
+ {envVar : "HOME" , pathSegments : []string {".cache" , "snowflake" }},
40
+ }
41
+
30
42
type secureTokenSpec struct {
31
43
host , user string
32
44
tokenType tokenType
@@ -72,6 +84,7 @@ func newSecureStorageManager() secureStorageManager {
72
84
case "darwin" , "windows" :
73
85
return newKeyringBasedSecureStorageManager ()
74
86
default :
87
+ logger .Infof ("OS %v does not support credentials cache" , runtime .GOOS )
75
88
return newNoopSecureStorageManager ()
76
89
}
77
90
}
@@ -81,27 +94,16 @@ type fileBasedSecureStorageManager struct {
81
94
}
82
95
83
96
func newFileBasedSecureStorageManager () (* fileBasedSecureStorageManager , error ) {
84
- credDirPath := buildCredCacheDirPath ()
85
- if credDirPath == "" {
86
- return nil , fmt . Errorf ( "failed to build cache dir path" )
97
+ credDirPath , err := buildCredCacheDirPath (defaultLinuxCacheDirConf )
98
+ if err != nil {
99
+ return nil , err
87
100
}
88
101
ssm := & fileBasedSecureStorageManager {
89
102
credDirPath : credDirPath ,
90
103
}
91
104
return ssm , nil
92
105
}
93
106
94
- func (ssm * fileBasedSecureStorageManager ) createCacheDir (credCacheDir string ) error {
95
- _ , err := os .Stat (credCacheDir )
96
- if os .IsNotExist (err ) {
97
- if err = os .MkdirAll (credCacheDir , os .ModePerm ); err != nil {
98
- return fmt .Errorf ("failed to create cache directory. %v, err: %v" , credCacheDir , err )
99
- }
100
- return nil
101
- }
102
- return err
103
- }
104
-
105
107
func lookupCacheDir (envVar string , pathSegments ... string ) (string , error ) {
106
108
envVal := os .Getenv (envVar )
107
109
if envVal == "" {
@@ -110,27 +112,21 @@ func lookupCacheDir(envVar string, pathSegments ...string) (string, error) {
110
112
111
113
fileInfo , err := os .Stat (envVal )
112
114
if err != nil {
113
- return "" , fmt .Errorf ("failed to stat %s=%s, due to %w " , envVar , envVal , err )
115
+ return "" , fmt .Errorf ("failed to stat %s=%s, due to %v " , envVar , envVal , err )
114
116
}
115
117
116
118
if ! fileInfo .IsDir () {
117
119
return "" , fmt .Errorf ("environment variable %s=%s is not a directory" , envVar , envVal )
118
120
}
119
121
120
- cacheDir := envVal
122
+ cacheDir := filepath . Join ( envVal , filepath . Join ( pathSegments ... ))
121
123
122
- if len (pathSegments ) > 0 {
123
- for _ , pathSegment := range pathSegments {
124
- err := os .Mkdir (pathSegment , os .ModePerm )
125
- if err != nil {
126
- return "" , fmt .Errorf ("failed to create cache directory. %v, err: %w" , pathSegment , err )
127
- }
128
- cacheDir = filepath .Join (cacheDir , pathSegment )
129
- }
130
- fileInfo , err = os .Stat (cacheDir )
131
- if err != nil {
132
- return "" , fmt .Errorf ("failed to stat %s=%s, due to %w" , envVar , cacheDir , err )
133
- }
124
+ if err = os .MkdirAll (cacheDir , os .FileMode (0o755 )); err != nil {
125
+ return "" , err
126
+ }
127
+ fileInfo , err = os .Stat (cacheDir )
128
+ if err != nil {
129
+ return "" , fmt .Errorf ("failed to stat %s=%s, due to %w" , envVar , cacheDir , err )
134
130
}
135
131
136
132
if fileInfo .Mode ().Perm () != 0o700 {
@@ -143,27 +139,18 @@ func lookupCacheDir(envVar string, pathSegments ...string) (string, error) {
143
139
return cacheDir , nil
144
140
}
145
141
146
- func buildCredCacheDirPath () string {
147
- type cacheDirConf struct {
148
- envVar string
149
- pathSegments []string
150
- }
151
- confs := []cacheDirConf {
152
- {envVar : credCacheDirEnv , pathSegments : []string {}},
153
- {envVar : "XDG_CACHE_DIR" , pathSegments : []string {"snowflake" }},
154
- {envVar : "HOME" , pathSegments : []string {".cache" , "snowflake" }},
155
- }
142
+ func buildCredCacheDirPath (confs []cacheDirConf ) (string , error ) {
156
143
for _ , conf := range confs {
157
144
path , err := lookupCacheDir (conf .envVar , conf .pathSegments ... )
158
145
if err != nil {
159
- logger .Debugf ("Skipping %s in cache directory lookup due to %w " , conf .envVar , err )
146
+ logger .Debugf ("Skipping %s in cache directory lookup due to %v " , conf .envVar , err )
160
147
} else {
161
148
logger .Infof ("Using %s as cache directory" , path )
162
- return path
149
+ return path , nil
163
150
}
164
151
}
165
152
166
- return ""
153
+ return "" , errors . New ( "no credentials cache directory found" )
167
154
}
168
155
169
156
func (ssm * fileBasedSecureStorageManager ) getTokens (data map [string ]any ) map [string ]interface {} {
@@ -198,26 +185,23 @@ func (ssm *fileBasedSecureStorageManager) setCredential(tokenSpec *secureTokenSp
198
185
err = ssm .writeTemporaryCacheFile (credCache )
199
186
if err != nil {
200
187
logger .Warnf ("Set credential failed. Unable to write cache. %v" , err )
201
- return
202
188
}
203
-
204
- return
205
189
}
206
190
207
191
func (ssm * fileBasedSecureStorageManager ) lockPath () string {
208
192
return filepath .Join (ssm .credDirPath , credCacheFileName + ".lck" )
209
193
}
210
194
211
195
func (ssm * fileBasedSecureStorageManager ) lockFile () error {
212
- const NUM_RETRIES = 10
213
- const RETRY_INTERVAL = 100 * time .Millisecond
196
+ const numRetries = 10
197
+ const retryInterval = 100 * time .Millisecond
214
198
lockPath := ssm .lockPath ()
215
199
locked := false
216
- for i := 0 ; i < NUM_RETRIES ; i ++ {
200
+ for i := 0 ; i < numRetries ; i ++ {
217
201
err := os .Mkdir (lockPath , 0o700 )
218
202
if err != nil {
219
203
if errors .Is (err , os .ErrExist ) {
220
- time .Sleep (RETRY_INTERVAL )
204
+ time .Sleep (retryInterval )
221
205
continue
222
206
}
223
207
return fmt .Errorf ("failed to create cache lock: %v, err: %v" , lockPath , err )
@@ -228,13 +212,13 @@ func (ssm *fileBasedSecureStorageManager) lockFile() error {
228
212
229
213
if ! locked {
230
214
logger .Warnf ("failed to lock cache lock. lockPath: %v." , lockPath )
231
- var stat unix.Stat_t
232
- err := unix .Stat (lockPath , & stat )
233
- if err != nil {
215
+ fileInfo , err := os .Stat (lockPath )
216
+ if err != nil && ! errors .Is (err , os .ErrNotExist ) {
234
217
return fmt .Errorf ("failed to stat %v and determine if lock is stale. err: %v" , lockPath , err )
235
218
}
236
219
237
- if stat .Ctim .Nano ()+ time .Second .Nanoseconds () < time .Now ().UnixNano () {
220
+ if fileInfo .ModTime ().Add (time .Second ).UnixNano () < time .Now ().UnixNano () {
221
+ logger .Debugf ("removing credentials cache lock file, stale for %v" , time .Now ().UnixNano ()- fileInfo .ModTime ().UnixNano ())
238
222
err := os .Remove (lockPath )
239
223
if err != nil {
240
224
return fmt .Errorf ("failed to remove %v while trying to remove stale lock. err: %v" , lockPath , err )
@@ -290,7 +274,7 @@ func (ssm *fileBasedSecureStorageManager) ensurePermissions() error {
290
274
}
291
275
292
276
if dirInfo .Mode ().Perm () != 0o700 {
293
- return fmt .Errorf ("incorrect permissions(%o, expected 700) for %s. " , dirInfo .Mode ().Perm (), ssm .credDirPath )
277
+ return fmt .Errorf ("incorrect permissions(%o, expected 700) for %s" , dirInfo .Mode ().Perm (), ssm .credDirPath )
294
278
}
295
279
296
280
fileInfo , err := os .Stat (ssm .credFilePath ())
@@ -302,7 +286,7 @@ func (ssm *fileBasedSecureStorageManager) ensurePermissions() error {
302
286
logger .Debugf ("Incorrect permissions(%o, expected 600) for credential file." , fileInfo .Mode ().Perm ())
303
287
err := os .Chmod (ssm .credFilePath (), 0o600 )
304
288
if err != nil {
305
- return fmt .Errorf ("Failed to chmod credential file: %v" , err )
289
+ return fmt .Errorf ("failed to chmod credential file: %v" , err )
306
290
}
307
291
logger .Debug ("Successfully fixed credential file permissions." )
308
292
}
@@ -324,7 +308,7 @@ func (ssm *fileBasedSecureStorageManager) readTemporaryCacheFile() map[string]an
324
308
}
325
309
326
310
credentialsMap := map [string ]any {}
327
- err = json .Unmarshal ([] byte ( jsonData ) , & credentialsMap )
311
+ err = json .Unmarshal (jsonData , & credentialsMap )
328
312
if err != nil {
329
313
logger .Warnf ("Failed to unmarshal credential cache file. %v.\n " , err )
330
314
}
@@ -347,10 +331,7 @@ func (ssm *fileBasedSecureStorageManager) deleteCredential(tokenSpec *secureToke
347
331
err = ssm .writeTemporaryCacheFile (credCache )
348
332
if err != nil {
349
333
logger .Warnf ("Set credential failed. Unable to write cache. %v" , err )
350
- return
351
334
}
352
-
353
- return
354
335
}
355
336
356
337
func (ssm * fileBasedSecureStorageManager ) writeTemporaryCacheFile (cache map [string ]any ) error {
@@ -359,6 +340,18 @@ func (ssm *fileBasedSecureStorageManager) writeTemporaryCacheFile(cache map[stri
359
340
return fmt .Errorf ("failed to marshal credential cache map. %w" , err )
360
341
}
361
342
343
+ stat , err := os .Stat (ssm .credFilePath ())
344
+ if err != nil && ! errors .Is (err , os .ErrNotExist ) {
345
+ return err
346
+ }
347
+ if err == nil {
348
+ if stat .Mode ().String () != "-rw-------" {
349
+ if err = os .Chmod (ssm .credFilePath (), 0600 ); err != nil {
350
+ return fmt .Errorf ("cannot chmod file %v to 600. %v" , ssm .credFilePath (), err )
351
+ }
352
+ }
353
+ }
354
+
362
355
err = os .WriteFile (ssm .credFilePath (), bytes , 0600 )
363
356
if err != nil {
364
357
return fmt .Errorf ("failed to write the credential cache file: %w" , err )
@@ -462,8 +455,10 @@ func (ssm *keyringSecureStorageManager) deleteCredential(tokenSpec *secureTokenS
462
455
}
463
456
464
457
func buildCredentialsKey (host , user string , credType tokenType ) string {
465
- credTypeStr := string (credType )
466
- return host + ":" + user + ":" + credTypeStr
458
+ plainCredKey := host + ":" + user + ":" + string (credType )
459
+ checksum := sha256 .New ()
460
+ checksum .Write ([]byte (plainCredKey ))
461
+ return hex .EncodeToString (checksum .Sum (nil ))
467
462
}
468
463
469
464
type noopSecureStorageManager struct {
0 commit comments