9
9
"errors"
10
10
"fmt"
11
11
"github.com/99designs/keyring"
12
+ "io"
12
13
"os"
13
14
"os/user"
14
15
"path/filepath"
@@ -87,7 +88,7 @@ func newSecureStorageManager() secureStorageManager {
87
88
case "darwin" , "windows" :
88
89
return & threadSafeSecureStorageManager {& sync.Mutex {}, newKeyringBasedSecureStorageManager ()}
89
90
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 )
91
92
return newNoopSecureStorageManager ()
92
93
}
93
94
}
@@ -132,15 +133,6 @@ func lookupCacheDir(envVar string, pathSegments ...string) (string, error) {
132
133
return "" , err
133
134
}
134
135
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
-
144
136
return cacheDir , nil
145
137
}
146
138
@@ -150,7 +142,7 @@ func buildCredCacheDirPath(confs []cacheDirConf) (string, error) {
150
142
if err != nil {
151
143
logger .Debugf ("Skipping %s in cache directory lookup due to %v" , conf .envVar , err )
152
144
} else {
153
- logger .Infof ("Using %s as cache directory" , path )
145
+ logger .Debugf ("Using %s as cache directory" , path )
154
146
return path , nil
155
147
}
156
148
}
@@ -172,6 +164,20 @@ func (ssm *fileBasedSecureStorageManager) getTokens(data map[string]any) map[str
172
164
return tokens
173
165
}
174
166
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
+
175
181
func (ssm * fileBasedSecureStorageManager ) setCredential (tokenSpec * secureTokenSpec , value string ) {
176
182
credentialsKey , err := tokenSpec .buildKey ()
177
183
if err != nil {
@@ -185,14 +191,20 @@ func (ssm *fileBasedSecureStorageManager) setCredential(tokenSpec *secureTokenSp
185
191
}
186
192
defer ssm .unlockFile ()
187
193
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
+ })
196
208
}
197
209
198
210
func (ssm * fileBasedSecureStorageManager ) lockPath () string {
@@ -203,6 +215,22 @@ func (ssm *fileBasedSecureStorageManager) lockFile() error {
203
215
const numRetries = 10
204
216
const retryInterval = 100 * time .Millisecond
205
217
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
+ time .Sleep (1000 * time .Millisecond )
224
+
225
+ // removing stale lock
226
+ if ! errors .Is (err , os .ErrNotExist ) && fileInfo .ModTime ().Add (time .Second ).UnixNano () < time .Now ().UnixNano () {
227
+ logger .Debugf ("removing credentials cache lock file, stale for %vms" , (time .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
+
206
234
locked := false
207
235
for i := 0 ; i < numRetries ; i ++ {
208
236
err := os .Mkdir (lockPath , 0o700 )
@@ -216,25 +244,8 @@ func (ssm *fileBasedSecureStorageManager) lockFile() error {
216
244
locked = true
217
245
break
218
246
}
219
-
220
247
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 )
238
249
}
239
250
return nil
240
251
}
@@ -255,30 +266,38 @@ func (ssm *fileBasedSecureStorageManager) getCredential(tokenSpec *secureTokenSp
255
266
}
256
267
err = ssm .lockFile ()
257
268
if err != nil {
258
- logger .Warn ("Failed to lock credential cache file." )
269
+ logger .Warnf ("Failed to lock credential cache file. %v" , err )
259
270
return ""
260
271
}
272
+ defer ssm .unlockFile ()
261
273
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
+ }
268
285
269
- credStr , ok := cred .(string )
270
- if ! ok {
271
- return ""
272
- }
286
+ credStr , ok := cred .(string )
287
+ if ! ok {
288
+ return
289
+ }
273
290
274
- return credStr
291
+ ret = credStr
292
+ })
293
+ return ret
275
294
}
276
295
277
296
func (ssm * fileBasedSecureStorageManager ) credFilePath () string {
278
297
return filepath .Join (ssm .credDirPath , credCacheFileName )
279
298
}
280
299
281
- func (ssm * fileBasedSecureStorageManager ) ensurePermissions () error {
300
+ func (ssm * fileBasedSecureStorageManager ) ensurePermissions (cacheFile * os. File ) error {
282
301
dirInfo , err := os .Stat (ssm .credDirPath )
283
302
if err != nil {
284
303
return err
@@ -288,7 +307,7 @@ func (ssm *fileBasedSecureStorageManager) ensurePermissions() error {
288
307
return fmt .Errorf ("incorrect permissions(%o, expected 700) for %s" , dirInfo .Mode ().Perm (), ssm .credDirPath )
289
308
}
290
309
291
- fileInfo , err := os .Stat (ssm . credFilePath () )
310
+ fileInfo , err := cacheFile .Stat ()
292
311
if err != nil {
293
312
return err
294
313
}
@@ -300,51 +319,68 @@ func (ssm *fileBasedSecureStorageManager) ensurePermissions() error {
300
319
return nil
301
320
}
302
321
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 ) {
306
325
return err
307
326
}
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 )
309
332
if err != nil && ! errors .Is (err , os .ErrNotExist ) {
310
333
return err
311
334
}
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
+ }
312
343
if errors .Is (err , os .ErrNotExist ) {
313
344
return nil
314
345
}
315
- if strconv .Itoa (int (dirOwnerUID )) != currentUser .Uid {
346
+ if strconv .Itoa (int (ownerId )) != currentUser .Uid {
316
347
return errors .New ("incorrect owner of " + ssm .credDirPath )
317
348
}
318
349
return nil
319
350
}
320
351
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 )
325
355
}
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 )
329
358
}
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 )
333
361
}
334
362
335
- jsonData , err := os . ReadFile ( ssm . credFilePath () )
363
+ jsonData , err := io . ReadAll ( cacheFile )
336
364
if err != nil {
337
365
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
339
375
}
340
376
341
377
credentialsMap := map [string ]any {}
342
378
err = json .Unmarshal (jsonData , & credentialsMap )
343
379
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 )
345
381
}
346
382
347
- return credentialsMap
383
+ return credentialsMap , nil
348
384
}
349
385
350
386
func (ssm * fileBasedSecureStorageManager ) deleteCredential (tokenSpec * secureTokenSpec ) {
@@ -360,20 +396,26 @@ func (ssm *fileBasedSecureStorageManager) deleteCredential(tokenSpec *secureToke
360
396
}
361
397
defer ssm .unlockFile ()
362
398
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 )
365
406
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
+ })
370
412
}
371
413
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 {
374
416
return err
375
417
}
376
- if err := ssm .ensureOwner (ssm .credFilePath ()); err != nil {
418
+ if err := ssm .ensureOwnerForDir (ssm .credFilePath ()); err != nil {
377
419
logger .Warn ("Failed to ensure owner for %v. %v" , ssm .credFilePath (), err )
378
420
return err
379
421
}
@@ -383,20 +425,14 @@ func (ssm *fileBasedSecureStorageManager) writeTemporaryCacheFile(cache map[stri
383
425
return fmt .Errorf ("failed to marshal credential cache map. %w" , err )
384
426
}
385
427
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 )
394
430
}
395
-
396
- err = os .WriteFile (ssm .credFilePath (), bytes , 0600 )
431
+ _ , err = cacheFile .Write (bytes )
397
432
if err != nil {
398
433
return fmt .Errorf ("failed to write the credential cache file: %w" , err )
399
434
}
435
+ cacheFile .Seek (0 , 0 )
400
436
return nil
401
437
}
402
438
0 commit comments