Skip to content

Commit 8a47a67

Browse files
committed
Fixes after review
1 parent 245c79e commit 8a47a67

4 files changed

+172
-97
lines changed

os_specific_posix.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,23 @@ import (
88
"syscall"
99
)
1010

11-
func provideFileOwner(filepath string) (uint32, error) {
11+
func providePathOwner(filepath string) (uint32, error) {
1212
info, err := os.Stat(filepath)
1313
if err != nil {
1414
return 0, err
1515
}
16+
return provideOwnerFromStat(info, filepath)
17+
}
18+
19+
func provideFileOwner(file *os.File) (uint32, error) {
20+
info, err := file.Stat()
21+
if err != nil {
22+
return 0, err
23+
}
24+
return provideOwnerFromStat(info, file.Name())
25+
}
26+
27+
func provideOwnerFromStat(info os.FileInfo, filepath string) (uint32, error) {
1628
nativeStat, ok := info.Sys().(*syscall.Stat_t)
1729
if !ok {
1830
return 0, fmt.Errorf("cannot cast file info for %v to *syscall.Stat_t", filepath)

os_specific_windows.go

+9-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@
22

33
package gosnowflake
44

5-
import "errors"
5+
import (
6+
"errors"
7+
"os"
8+
)
69

7-
func provideFileOwner(filepath string) (uint32, error) {
10+
func providePathOwner(filepath string) (uint32, error) {
11+
return 0, errors.New("providePathOwner is unsupported on windows")
12+
}
13+
14+
func provideFileOwner(file *os.File) (uint32, error) {
815
return 0, errors.New("provideFileOwner is unsupported on windows")
916
}

secure_storage_manager.go

+125-89
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"errors"
1010
"fmt"
1111
"github.com/99designs/keyring"
12+
"io"
1213
"os"
1314
"os/user"
1415
"path/filepath"
@@ -87,7 +88,7 @@ func newSecureStorageManager() secureStorageManager {
8788
case "darwin", "windows":
8889
return &threadSafeSecureStorageManager{&sync.Mutex{}, newKeyringBasedSecureStorageManager()}
8990
default:
90-
logger.Infof("OS %v does not support credentials cache", runtime.GOOS)
91+
logger.Warnf("OS %v does not support credentials cache", runtime.GOOS)
9192
return newNoopSecureStorageManager()
9293
}
9394
}
@@ -132,15 +133,6 @@ func lookupCacheDir(envVar string, pathSegments ...string) (string, error) {
132133
return "", err
133134
}
134135

135-
fileInfo, err = os.Stat(cacheDir)
136-
if err != nil {
137-
return "", fmt.Errorf("failed to stat %s=%s, due to %w", envVar, cacheDir, err)
138-
}
139-
140-
if fileInfo.Mode().Perm()|os.ModeDir != 0o700|os.ModeDir {
141-
return "", fmt.Errorf("incorrect permission to %v", cacheDir)
142-
}
143-
144136
return cacheDir, nil
145137
}
146138

@@ -150,7 +142,7 @@ func buildCredCacheDirPath(confs []cacheDirConf) (string, error) {
150142
if err != nil {
151143
logger.Debugf("Skipping %s in cache directory lookup due to %v", conf.envVar, err)
152144
} else {
153-
logger.Infof("Using %s as cache directory", path)
145+
logger.Debugf("Using %s as cache directory", path)
154146
return path, nil
155147
}
156148
}
@@ -172,6 +164,20 @@ func (ssm *fileBasedSecureStorageManager) getTokens(data map[string]any) map[str
172164
return tokens
173165
}
174166

167+
func (ssm *fileBasedSecureStorageManager) withCacheFile(action func(*os.File)) {
168+
cacheFile, err := os.OpenFile(ssm.credFilePath(), os.O_CREATE|os.O_RDWR, 0600)
169+
if err != nil {
170+
logger.Warn("cannot access %v. %v", ssm.credFilePath(), err)
171+
return
172+
}
173+
defer func(file *os.File) {
174+
if err := file.Close(); err != nil {
175+
logger.Warnf("cannot release file descriptor for %v. %v", ssm.credFilePath(), err)
176+
}
177+
}(cacheFile)
178+
action(cacheFile)
179+
}
180+
175181
func (ssm *fileBasedSecureStorageManager) setCredential(tokenSpec *secureTokenSpec, value string) {
176182
credentialsKey, err := tokenSpec.buildKey()
177183
if err != nil {
@@ -185,14 +191,20 @@ func (ssm *fileBasedSecureStorageManager) setCredential(tokenSpec *secureTokenSp
185191
}
186192
defer ssm.unlockFile()
187193

188-
credCache := ssm.readTemporaryCacheFile()
189-
tokens := ssm.getTokens(credCache)
190-
tokens[credentialsKey] = value
191-
credCache["tokens"] = tokens
192-
err = ssm.writeTemporaryCacheFile(credCache)
193-
if err != nil {
194-
logger.Warnf("Set credential failed. Unable to write cache. %v", err)
195-
}
194+
ssm.withCacheFile(func(cacheFile *os.File) {
195+
credCache, err := ssm.readTemporaryCacheFile(cacheFile)
196+
if err != nil {
197+
logger.Warnf("Error while reading cache file. %v", err)
198+
return
199+
}
200+
tokens := ssm.getTokens(credCache)
201+
tokens[credentialsKey] = value
202+
credCache["tokens"] = tokens
203+
err = ssm.writeTemporaryCacheFile(credCache, cacheFile)
204+
if err != nil {
205+
logger.Warnf("Set credential failed. Unable to write cache. %v", err)
206+
}
207+
})
196208
}
197209

198210
func (ssm *fileBasedSecureStorageManager) lockPath() string {
@@ -203,6 +215,22 @@ func (ssm *fileBasedSecureStorageManager) lockFile() error {
203215
const numRetries = 10
204216
const retryInterval = 100 * time.Millisecond
205217
lockPath := ssm.lockPath()
218+
219+
fileInfo, err := os.Stat(lockPath)
220+
if err != nil && !errors.Is(err, os.ErrNotExist) {
221+
return fmt.Errorf("failed to stat %v and determine if lock is stale. err: %v", lockPath, err)
222+
}
223+
224+
// removing stale lock
225+
now := time.Now()
226+
if !errors.Is(err, os.ErrNotExist) && fileInfo.ModTime().Add(time.Second).UnixNano() < now.UnixNano() {
227+
logger.Debugf("removing credentials cache lock file, stale for %vms", (now.UnixNano()-fileInfo.ModTime().UnixNano())/1000/1000)
228+
err = os.Remove(lockPath)
229+
if err != nil {
230+
return fmt.Errorf("failed to remove %v while trying to remove stale lock. err: %v", lockPath, err)
231+
}
232+
}
233+
206234
locked := false
207235
for i := 0; i < numRetries; i++ {
208236
err := os.Mkdir(lockPath, 0o700)
@@ -216,25 +244,8 @@ func (ssm *fileBasedSecureStorageManager) lockFile() error {
216244
locked = true
217245
break
218246
}
219-
220247
if !locked {
221-
logger.Warnf("failed to lock cache lock. lockPath: %v.", lockPath)
222-
fileInfo, err := os.Stat(lockPath)
223-
if err != nil && !errors.Is(err, os.ErrNotExist) {
224-
return fmt.Errorf("failed to stat %v and determine if lock is stale. err: %v", lockPath, err)
225-
}
226-
227-
if fileInfo.ModTime().Add(time.Second).UnixNano() < time.Now().UnixNano() {
228-
logger.Debugf("removing credentials cache lock file, stale for %v", time.Now().UnixNano()-fileInfo.ModTime().UnixNano())
229-
err := os.Remove(lockPath)
230-
if err != nil {
231-
return fmt.Errorf("failed to remove %v while trying to remove stale lock. err: %v", lockPath, err)
232-
}
233-
err = os.Mkdir(lockPath, 0o700)
234-
if err != nil {
235-
return fmt.Errorf("failed to recreate cache lock after removing stale lock. %v, err: %v", lockPath, err)
236-
}
237-
}
248+
return fmt.Errorf("failed to lock cache. lockPath: %v", lockPath)
238249
}
239250
return nil
240251
}
@@ -255,30 +266,38 @@ func (ssm *fileBasedSecureStorageManager) getCredential(tokenSpec *secureTokenSp
255266
}
256267
err = ssm.lockFile()
257268
if err != nil {
258-
logger.Warn("Failed to lock credential cache file.")
269+
logger.Warnf("Failed to lock credential cache file. %v", err)
259270
return ""
260271
}
272+
defer ssm.unlockFile()
261273

262-
credCache := ssm.readTemporaryCacheFile()
263-
ssm.unlockFile()
264-
cred, ok := ssm.getTokens(credCache)[credentialsKey]
265-
if !ok {
266-
return ""
267-
}
274+
ret := ""
275+
ssm.withCacheFile(func(cacheFile *os.File) {
276+
credCache, err := ssm.readTemporaryCacheFile(cacheFile)
277+
if err != nil {
278+
logger.Warnf("Error while reading cache file. %v", err)
279+
return
280+
}
281+
cred, ok := ssm.getTokens(credCache)[credentialsKey]
282+
if !ok {
283+
return
284+
}
268285

269-
credStr, ok := cred.(string)
270-
if !ok {
271-
return ""
272-
}
286+
credStr, ok := cred.(string)
287+
if !ok {
288+
return
289+
}
273290

274-
return credStr
291+
ret = credStr
292+
})
293+
return ret
275294
}
276295

277296
func (ssm *fileBasedSecureStorageManager) credFilePath() string {
278297
return filepath.Join(ssm.credDirPath, credCacheFileName)
279298
}
280299

281-
func (ssm *fileBasedSecureStorageManager) ensurePermissions() error {
300+
func (ssm *fileBasedSecureStorageManager) ensurePermissions(cacheFile *os.File) error {
282301
dirInfo, err := os.Stat(ssm.credDirPath)
283302
if err != nil {
284303
return err
@@ -288,7 +307,7 @@ func (ssm *fileBasedSecureStorageManager) ensurePermissions() error {
288307
return fmt.Errorf("incorrect permissions(%o, expected 700) for %s", dirInfo.Mode().Perm(), ssm.credDirPath)
289308
}
290309

291-
fileInfo, err := os.Stat(ssm.credFilePath())
310+
fileInfo, err := cacheFile.Stat()
292311
if err != nil {
293312
return err
294313
}
@@ -300,51 +319,68 @@ func (ssm *fileBasedSecureStorageManager) ensurePermissions() error {
300319
return nil
301320
}
302321

303-
func (ssm *fileBasedSecureStorageManager) ensureOwner(filePath string) error {
304-
currentUser, err := user.Current()
305-
if err != nil {
322+
func (ssm *fileBasedSecureStorageManager) ensureOwnerForDir(filePath string) error {
323+
ownerUID, err := providePathOwner(filePath)
324+
if err != nil && !errors.Is(err, os.ErrNotExist) {
306325
return err
307326
}
308-
dirOwnerUID, err := provideFileOwner(filePath)
327+
return ssm.ensureOwner(ownerUID)
328+
}
329+
330+
func (ssm *fileBasedSecureStorageManager) ensureOwnerForFile(file *os.File) error {
331+
ownerUID, err := provideFileOwner(file)
309332
if err != nil && !errors.Is(err, os.ErrNotExist) {
310333
return err
311334
}
335+
return ssm.ensureOwner(ownerUID)
336+
}
337+
338+
func (ssm *fileBasedSecureStorageManager) ensureOwner(ownerId uint32) error {
339+
currentUser, err := user.Current()
340+
if err != nil {
341+
return err
342+
}
312343
if errors.Is(err, os.ErrNotExist) {
313344
return nil
314345
}
315-
if strconv.Itoa(int(dirOwnerUID)) != currentUser.Uid {
346+
if strconv.Itoa(int(ownerId)) != currentUser.Uid {
316347
return errors.New("incorrect owner of " + ssm.credDirPath)
317348
}
318349
return nil
319350
}
320351

321-
func (ssm *fileBasedSecureStorageManager) readTemporaryCacheFile() map[string]any {
322-
if err := ssm.ensurePermissions(); err != nil {
323-
logger.Warnf("Failed to ensure permission for temporary cache file. %v.\n", err)
324-
return map[string]any{}
352+
func (ssm *fileBasedSecureStorageManager) readTemporaryCacheFile(cacheFile *os.File) (map[string]any, error) {
353+
if err := ssm.ensurePermissions(cacheFile); err != nil {
354+
return map[string]any{}, fmt.Errorf("failed to ensure permission for temporary cache file. %v", err)
325355
}
326-
if err := ssm.ensureOwner(ssm.credDirPath); err != nil {
327-
logger.Warn("Failed to ensure owner for %v. %v", ssm.credDirPath, err)
328-
return map[string]any{}
356+
if err := ssm.ensureOwnerForDir(ssm.credDirPath); err != nil {
357+
return map[string]any{}, fmt.Errorf("failed to ensure owner for %v. %v", ssm.credDirPath, err)
329358
}
330-
if err := ssm.ensureOwner(ssm.credFilePath()); err != nil {
331-
logger.Warn("Failed to ensure owner for %v. %v", ssm.credFilePath(), err)
332-
return map[string]any{}
359+
if err := ssm.ensureOwnerForFile(cacheFile); err != nil {
360+
return map[string]any{}, fmt.Errorf("failed to ensure owner for %v. %v", ssm.credFilePath(), err)
333361
}
334362

335-
jsonData, err := os.ReadFile(ssm.credFilePath())
363+
jsonData, err := io.ReadAll(cacheFile)
336364
if err != nil {
337365
logger.Warnf("Failed to read credential cache file. %v.\n", err)
338-
return map[string]any{}
366+
return map[string]any{}, nil
367+
}
368+
if _, err = cacheFile.Seek(0, 0); err != nil {
369+
return map[string]any{}, fmt.Errorf("cannot seek to the beginning of a cache file. %v", err)
370+
}
371+
372+
if len(jsonData) == 0 {
373+
// Happens when the file didn't exist before.
374+
return map[string]any{}, nil
339375
}
340376

341377
credentialsMap := map[string]any{}
342378
err = json.Unmarshal(jsonData, &credentialsMap)
343379
if err != nil {
344-
logger.Warnf("Failed to unmarshal credential cache file. %v.\n", err)
380+
return map[string]any{}, fmt.Errorf("Failed to unmarshal credential cache file. %v.\n", err)
345381
}
346382

347-
return credentialsMap
383+
return credentialsMap, nil
348384
}
349385

350386
func (ssm *fileBasedSecureStorageManager) deleteCredential(tokenSpec *secureTokenSpec) {
@@ -360,20 +396,26 @@ func (ssm *fileBasedSecureStorageManager) deleteCredential(tokenSpec *secureToke
360396
}
361397
defer ssm.unlockFile()
362398

363-
credCache := ssm.readTemporaryCacheFile()
364-
delete(ssm.getTokens(credCache), credentialsKey)
399+
ssm.withCacheFile(func(cacheFile *os.File) {
400+
credCache, err := ssm.readTemporaryCacheFile(cacheFile)
401+
if err != nil {
402+
logger.Warnf("Error while reading cache file. %v", err)
403+
return
404+
}
405+
delete(ssm.getTokens(credCache), credentialsKey)
365406

366-
err = ssm.writeTemporaryCacheFile(credCache)
367-
if err != nil {
368-
logger.Warnf("Set credential failed. Unable to write cache. %v", err)
369-
}
407+
err = ssm.writeTemporaryCacheFile(credCache, cacheFile)
408+
if err != nil {
409+
logger.Warnf("Set credential failed. Unable to write cache. %v", err)
410+
}
411+
})
370412
}
371413

372-
func (ssm *fileBasedSecureStorageManager) writeTemporaryCacheFile(cache map[string]any) error {
373-
if err := ssm.ensureOwner(ssm.credDirPath); err != nil {
414+
func (ssm *fileBasedSecureStorageManager) writeTemporaryCacheFile(cache map[string]any, cacheFile *os.File) error {
415+
if err := ssm.ensureOwnerForDir(ssm.credDirPath); err != nil {
374416
return err
375417
}
376-
if err := ssm.ensureOwner(ssm.credFilePath()); err != nil {
418+
if err := ssm.ensureOwnerForDir(ssm.credFilePath()); err != nil {
377419
logger.Warn("Failed to ensure owner for %v. %v", ssm.credFilePath(), err)
378420
return err
379421
}
@@ -383,20 +425,14 @@ func (ssm *fileBasedSecureStorageManager) writeTemporaryCacheFile(cache map[stri
383425
return fmt.Errorf("failed to marshal credential cache map. %w", err)
384426
}
385427

386-
stat, err := os.Stat(ssm.credFilePath())
387-
if err != nil && !errors.Is(err, os.ErrNotExist) {
388-
return err
389-
}
390-
if err == nil {
391-
if stat.Mode().Perm() != 0o600&os.ModePerm {
392-
return fmt.Errorf("incorrect %v permission, expected 0600, got %v", ssm.credFilePath(), stat.Mode().Perm())
393-
}
428+
if err = cacheFile.Truncate(0); err != nil {
429+
return fmt.Errorf("error while truncating credentials cache. %v", err)
394430
}
395-
396-
err = os.WriteFile(ssm.credFilePath(), bytes, 0600)
431+
_, err = cacheFile.Write(bytes)
397432
if err != nil {
398433
return fmt.Errorf("failed to write the credential cache file: %w", err)
399434
}
435+
cacheFile.Seek(0, 0)
400436
return nil
401437
}
402438

0 commit comments

Comments
 (0)