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,9 +94,9 @@ 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 ,
@@ -110,27 +123,21 @@ func lookupCacheDir(envVar string, pathSegments ...string) (string, error) {
110
123
111
124
fileInfo , err := os .Stat (envVal )
112
125
if err != nil {
113
- return "" , fmt .Errorf ("failed to stat %s=%s, due to %w " , envVar , envVal , err )
126
+ return "" , fmt .Errorf ("failed to stat %s=%s, due to %v " , envVar , envVal , err )
114
127
}
115
128
116
129
if ! fileInfo .IsDir () {
117
130
return "" , fmt .Errorf ("environment variable %s=%s is not a directory" , envVar , envVal )
118
131
}
119
132
120
- cacheDir := envVal
133
+ cacheDir := filepath . Join ( envVal , filepath . Join ( pathSegments ... ))
121
134
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
- }
135
+ if err = os .MkdirAll (cacheDir , os .FileMode (0o755 )); err != nil {
136
+ return "" , err
137
+ }
138
+ fileInfo , err = os .Stat (cacheDir )
139
+ if err != nil {
140
+ return "" , fmt .Errorf ("failed to stat %s=%s, due to %w" , envVar , cacheDir , err )
134
141
}
135
142
136
143
if fileInfo .Mode ().Perm () != 0o700 {
@@ -143,27 +150,18 @@ func lookupCacheDir(envVar string, pathSegments ...string) (string, error) {
143
150
return cacheDir , nil
144
151
}
145
152
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
- }
153
+ func buildCredCacheDirPath (confs []cacheDirConf ) (string , error ) {
156
154
for _ , conf := range confs {
157
155
path , err := lookupCacheDir (conf .envVar , conf .pathSegments ... )
158
156
if err != nil {
159
- logger .Debugf ("Skipping %s in cache directory lookup due to %w " , conf .envVar , err )
157
+ logger .Debugf ("Skipping %s in cache directory lookup due to %v " , conf .envVar , err )
160
158
} else {
161
159
logger .Infof ("Using %s as cache directory" , path )
162
- return path
160
+ return path , nil
163
161
}
164
162
}
165
163
166
- return ""
164
+ return "" , errors . New ( "no credentials cache directory found" )
167
165
}
168
166
169
167
func (ssm * fileBasedSecureStorageManager ) getTokens (data map [string ]any ) map [string ]interface {} {
@@ -198,26 +196,23 @@ func (ssm *fileBasedSecureStorageManager) setCredential(tokenSpec *secureTokenSp
198
196
err = ssm .writeTemporaryCacheFile (credCache )
199
197
if err != nil {
200
198
logger .Warnf ("Set credential failed. Unable to write cache. %v" , err )
201
- return
202
199
}
203
-
204
- return
205
200
}
206
201
207
202
func (ssm * fileBasedSecureStorageManager ) lockPath () string {
208
203
return filepath .Join (ssm .credDirPath , credCacheFileName + ".lck" )
209
204
}
210
205
211
206
func (ssm * fileBasedSecureStorageManager ) lockFile () error {
212
- const NUM_RETRIES = 10
213
- const RETRY_INTERVAL = 100 * time .Millisecond
207
+ const numRetries = 10
208
+ const retryInterval = 100 * time .Millisecond
214
209
lockPath := ssm .lockPath ()
215
210
locked := false
216
- for i := 0 ; i < NUM_RETRIES ; i ++ {
211
+ for i := 0 ; i < numRetries ; i ++ {
217
212
err := os .Mkdir (lockPath , 0o700 )
218
213
if err != nil {
219
214
if errors .Is (err , os .ErrExist ) {
220
- time .Sleep (RETRY_INTERVAL )
215
+ time .Sleep (retryInterval )
221
216
continue
222
217
}
223
218
return fmt .Errorf ("failed to create cache lock: %v, err: %v" , lockPath , err )
@@ -228,13 +223,13 @@ func (ssm *fileBasedSecureStorageManager) lockFile() error {
228
223
229
224
if ! locked {
230
225
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 {
226
+ fileInfo , err := os .Stat (lockPath )
227
+ if err != nil && ! errors .Is (err , os .ErrNotExist ) {
234
228
return fmt .Errorf ("failed to stat %v and determine if lock is stale. err: %v" , lockPath , err )
235
229
}
236
230
237
- if stat .Ctim .Nano ()+ time .Second .Nanoseconds () < time .Now ().UnixNano () {
231
+ if fileInfo .ModTime ().Add (time .Second ).UnixNano () < time .Now ().UnixNano () {
232
+ logger .Debugf ("removing credentials cache lock file, stale for %v" , time .Now ().UnixNano ()- fileInfo .ModTime ().UnixNano ())
238
233
err := os .Remove (lockPath )
239
234
if err != nil {
240
235
return fmt .Errorf ("failed to remove %v while trying to remove stale lock. err: %v" , lockPath , err )
@@ -290,7 +285,7 @@ func (ssm *fileBasedSecureStorageManager) ensurePermissions() error {
290
285
}
291
286
292
287
if dirInfo .Mode ().Perm () != 0o700 {
293
- return fmt .Errorf ("incorrect permissions(%o, expected 700) for %s. " , dirInfo .Mode ().Perm (), ssm .credDirPath )
288
+ return fmt .Errorf ("incorrect permissions(%o, expected 700) for %s" , dirInfo .Mode ().Perm (), ssm .credDirPath )
294
289
}
295
290
296
291
fileInfo , err := os .Stat (ssm .credFilePath ())
@@ -302,7 +297,7 @@ func (ssm *fileBasedSecureStorageManager) ensurePermissions() error {
302
297
logger .Debugf ("Incorrect permissions(%o, expected 600) for credential file." , fileInfo .Mode ().Perm ())
303
298
err := os .Chmod (ssm .credFilePath (), 0o600 )
304
299
if err != nil {
305
- return fmt .Errorf ("Failed to chmod credential file: %v" , err )
300
+ return fmt .Errorf ("failed to chmod credential file: %v" , err )
306
301
}
307
302
logger .Debug ("Successfully fixed credential file permissions." )
308
303
}
@@ -324,7 +319,7 @@ func (ssm *fileBasedSecureStorageManager) readTemporaryCacheFile() map[string]an
324
319
}
325
320
326
321
credentialsMap := map [string ]any {}
327
- err = json .Unmarshal ([] byte ( jsonData ) , & credentialsMap )
322
+ err = json .Unmarshal (jsonData , & credentialsMap )
328
323
if err != nil {
329
324
logger .Warnf ("Failed to unmarshal credential cache file. %v.\n " , err )
330
325
}
@@ -347,10 +342,7 @@ func (ssm *fileBasedSecureStorageManager) deleteCredential(tokenSpec *secureToke
347
342
err = ssm .writeTemporaryCacheFile (credCache )
348
343
if err != nil {
349
344
logger .Warnf ("Set credential failed. Unable to write cache. %v" , err )
350
- return
351
345
}
352
-
353
- return
354
346
}
355
347
356
348
func (ssm * fileBasedSecureStorageManager ) writeTemporaryCacheFile (cache map [string ]any ) error {
@@ -359,6 +351,18 @@ func (ssm *fileBasedSecureStorageManager) writeTemporaryCacheFile(cache map[stri
359
351
return fmt .Errorf ("failed to marshal credential cache map. %w" , err )
360
352
}
361
353
354
+ stat , err := os .Stat (ssm .credFilePath ())
355
+ if err != nil && ! errors .Is (err , os .ErrNotExist ) {
356
+ return err
357
+ }
358
+ if err == nil {
359
+ if stat .Mode ().String () != "-rw-------" {
360
+ if err = os .Chmod (ssm .credFilePath (), 0600 ); err != nil {
361
+ return fmt .Errorf ("cannot chmod file %v to 600. %v" , ssm .credFilePath (), err )
362
+ }
363
+ }
364
+ }
365
+
362
366
err = os .WriteFile (ssm .credFilePath (), bytes , 0600 )
363
367
if err != nil {
364
368
return fmt .Errorf ("failed to write the credential cache file: %w" , err )
@@ -462,8 +466,9 @@ func (ssm *keyringSecureStorageManager) deleteCredential(tokenSpec *secureTokenS
462
466
}
463
467
464
468
func buildCredentialsKey (host , user string , credType tokenType ) string {
465
- credTypeStr := string (credType )
466
- return host + ":" + user + ":" + credTypeStr
469
+ plainCredKey := host + ":" + user + ":" + string (credType )
470
+ checksum := sha256 .New ().Sum ([]byte (plainCredKey ))
471
+ return hex .EncodeToString (checksum )
467
472
}
468
473
469
474
type noopSecureStorageManager struct {
0 commit comments