From f4b2aa89d30a0e362003aaf672fd7a66e0de0f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Fri, 7 Feb 2025 12:32:39 +0100 Subject: [PATCH 1/4] Update cache dir lookup --- secure_storage_manager.go | 70 +++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/secure_storage_manager.go b/secure_storage_manager.go index 6be71a579..0b4f89a54 100644 --- a/secure_storage_manager.go +++ b/secure_storage_manager.go @@ -109,18 +109,68 @@ func (ssm *fileBasedSecureStorageManager) createCacheDir(credCacheDir string) er return err } +func lookupCacheDir(envVar string, pathSegments ...string) (string, error) { + envVal := os.Getenv(envVar) + if envVal == "" { + return "", fmt.Errorf("environment variable %s not set", envVar) + } + + fileInfo, err := os.Stat(envVal) + if err != nil { + return "", fmt.Errorf("failed to stat %s=%s, due to %w", envVar, envVal, err) + } + + if !fileInfo.IsDir() { + return "", fmt.Errorf("environment variable %s=%s is not a directory", envVar, envVal) + } + + cacheDir := envVal + + if len(pathSegments) > 0 { + for _, pathSegment := range pathSegments { + err := os.Mkdir(pathSegment, os.ModePerm) + if err != nil { + return "", fmt.Errorf("failed to create cache directory. %v, err: %w", pathSegment, err) + } + cacheDir = filepath.Join(cacheDir, pathSegment) + } + fileInfo, err = os.Stat(cacheDir) + if err != nil { + return "", fmt.Errorf("failed to stat %s=%s, due to %w", envVar, cacheDir, err) + } + } + + if fileInfo.Mode()&os.ModePerm != 0o700 { + err := os.Chmod(cacheDir, 0o700) + if err != nil { + return "", fmt.Errorf("failed to chmod cache directory. %v, err: %w", cacheDir, err) + } + } + + return cacheDir, nil +} + func (ssm *fileBasedSecureStorageManager) buildCredCacheDirPath() string { - credCacheDir := os.Getenv(credCacheDirEnv) - if credCacheDir != "" { - return credCacheDir + type cacheDirConf struct { + envVar string + pathSegments []string } - home := os.Getenv("HOME") - if home == "" { - logger.Info("HOME is blank") - return "" + confs := []cacheDirConf{ + {envVar: credCacheDirEnv, pathSegments: []string{}}, + {envVar: "XDG_CACHE_DIR", pathSegments: []string{"snowflake"}}, + {envVar: "HOME", pathSegments: []string{".cache", "snowflake"}}, } - credCacheDir = filepath.Join(home, ".cache", "snowflake") - return credCacheDir + for _, conf := range confs { + path, err := lookupCacheDir(conf.envVar, conf.pathSegments...) + if err != nil { + logger.Debugf("Skipping %s in cache directory lookup due to %w", conf.envVar, err) + } else { + logger.Infof("Using %s as cache directory", path) + return path + } + } + + return "" } func (ssm *fileBasedSecureStorageManager) setCredential(tokenSpec *secureTokenSpec, value string) { @@ -345,7 +395,7 @@ func buildCredentialsKey(host, user string, credType tokenType) string { host = strings.ToUpper(host) user = strings.ToUpper(user) credTypeStr := strings.ToUpper(string(credType)) - return host + ":" + user + ":" + driverName + ":" + credTypeStr + return host + ":" + user + ":" + credTypeStr } type noopSecureStorageManager struct { From 201c5f4f4a19959e01e92f16e886ece873e6b4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Fri, 7 Feb 2025 15:30:45 +0100 Subject: [PATCH 2/4] Refactor file based secure storage manager methods --- secure_storage_manager.go | 230 +++++++++++++++++++++----------------- 1 file changed, 125 insertions(+), 105 deletions(-) diff --git a/secure_storage_manager.go b/secure_storage_manager.go index 0b4f89a54..6caaa62a2 100644 --- a/secure_storage_manager.go +++ b/secure_storage_manager.go @@ -5,14 +5,11 @@ package gosnowflake import ( "encoding/json" "fmt" + "github.com/99designs/keyring" "os" "path/filepath" "runtime" "strings" - "sync" - "time" - - "github.com/99designs/keyring" ) type tokenType string @@ -78,23 +75,17 @@ func newSecureStorageManager() secureStorageManager { } type fileBasedSecureStorageManager struct { - credCacheFilePath string - localCredCache map[string]string - credCacheLock sync.RWMutex + credDirPath string } func newFileBasedSecureStorageManager() (*fileBasedSecureStorageManager, error) { - ssm := &fileBasedSecureStorageManager{ - localCredCache: map[string]string{}, - credCacheLock: sync.RWMutex{}, + credDirPath := buildCredCacheDirPath() + if credDirPath == "" { + return nil, fmt.Errorf("failed to build cache dir path") } - credCacheDir := ssm.buildCredCacheDirPath() - if err := ssm.createCacheDir(credCacheDir); err != nil { - return nil, err + ssm := &fileBasedSecureStorageManager{ + credDirPath: credDirPath, } - credCacheFilePath := filepath.Join(credCacheDir, credCacheFileName) - logger.Infof("Credentials cache path: %v", credCacheFilePath) - ssm.credCacheFilePath = credCacheFilePath return ssm, nil } @@ -140,7 +131,7 @@ func lookupCacheDir(envVar string, pathSegments ...string) (string, error) { } } - if fileInfo.Mode()&os.ModePerm != 0o700 { + if fileInfo.Mode().Perm() != 0o700 { err := os.Chmod(cacheDir, 0o700) if err != nil { return "", fmt.Errorf("failed to chmod cache directory. %v, err: %w", cacheDir, err) @@ -150,7 +141,7 @@ func lookupCacheDir(envVar string, pathSegments ...string) (string, error) { return cacheDir, nil } -func (ssm *fileBasedSecureStorageManager) buildCredCacheDirPath() string { +func buildCredCacheDirPath() string { type cacheDirConf struct { envVar string pathSegments []string @@ -174,126 +165,155 @@ func (ssm *fileBasedSecureStorageManager) buildCredCacheDirPath() string { } func (ssm *fileBasedSecureStorageManager) setCredential(tokenSpec *secureTokenSpec, value string) { - if value == "" { - logger.Debug("no token provided") - } else { - credentialsKey := tokenSpec.buildKey() - ssm.credCacheLock.Lock() - defer ssm.credCacheLock.Unlock() - ssm.localCredCache[credentialsKey] = value - - j, err := json.Marshal(ssm.localCredCache) - if err != nil { - logger.Warnf("failed to convert credential to JSON.") - return - } + credentialsKey := tokenSpec.buildKey() + err := ssm.lockFile() + if err != nil { + logger.Warnf("Set credential failed. Unable to lock cache. %v", err) + return + } + defer ssm.unlockFile() - logger.Debugf("writing credential cache file. %v\n", ssm.credCacheFilePath) - credCacheLockFileName := ssm.credCacheFilePath + ".lck" - logger.Debugf("Creating lock file. %v", credCacheLockFileName) - err = os.Mkdir(credCacheLockFileName, 0600) + credCache, err := ssm.readTemporaryCacheFile() + if err != nil { + logger.Warnf("Set credential failed. Unable to read cache. %v", err) + return + } - switch { - case os.IsExist(err): - statinfo, err := os.Stat(credCacheLockFileName) - if err != nil { - logger.Debugf("failed to write credential cache file. file: %v, err: %v. ignored.\n", ssm.credCacheFilePath, err) - return - } - if time.Since(statinfo.ModTime()) < 15*time.Minute { - logger.Debugf("other process locks the cache file. %v. ignored.\n", ssm.credCacheFilePath) - return - } - if err = os.Remove(credCacheLockFileName); err != nil { - logger.Debugf("failed to delete lock file. file: %v, err: %v. ignored.\n", credCacheLockFileName, err) - return - } - if err = os.Mkdir(credCacheLockFileName, 0600); err != nil { - logger.Debugf("failed to delete lock file. file: %v, err: %v. ignored.\n", credCacheLockFileName, err) - return - } - } - defer os.RemoveAll(credCacheLockFileName) + credCache["tokens"][credentialsKey] = value - if err = os.WriteFile(ssm.credCacheFilePath, j, 0644); err != nil { - logger.Debugf("Failed to write the cache file. File: %v err: %v.", ssm.credCacheFilePath, err) - } + err = ssm.writeTemporaryCacheFile(credCache) + if err != nil { + logger.Warnf("Set credential failed. Unable to write cache. %v", err) + return } + + return +} + +func (ssm *fileBasedSecureStorageManager) lockFile() error { + // TODO Implement locks + return nil +} + +func (ssm *fileBasedSecureStorageManager) unlockFile() { + // TODO Implement locks } func (ssm *fileBasedSecureStorageManager) getCredential(tokenSpec *secureTokenSpec) string { credentialsKey := tokenSpec.buildKey() - ssm.credCacheLock.Lock() - defer ssm.credCacheLock.Unlock() - localCredCache := ssm.readTemporaryCacheFile() - cred := localCredCache[credentialsKey] + credCache := map[string]map[string]string{} + + err := ssm.lockFile() + if err != nil { + logger.Warn("Failed to lock credential cache file.") + return "" + } + + credCache, err = ssm.readTemporaryCacheFile() + ssm.unlockFile() + if err != nil { + logger.Warnf("Failed to read temporary cache file. %v.\n", err) + return "" + } + + cred := credCache["tokens"][credentialsKey] if cred != "" { logger.Debug("Successfully read token. Returning as string") } else { logger.Debug("Returned credential is empty") } + return cred } -func (ssm *fileBasedSecureStorageManager) readTemporaryCacheFile() map[string]string { - jsonData, err := os.ReadFile(ssm.credCacheFilePath) +func (ssm *fileBasedSecureStorageManager) credFilePath() string { + return filepath.Join(ssm.credDirPath, credCacheFileName) +} + +func (ssm *fileBasedSecureStorageManager) ensurePermissions() error { + dirInfo, err := os.Stat(ssm.credDirPath) if err != nil { - logger.Debugf("Failed to read credential file: %v", err) - return nil + return err } - err = json.Unmarshal([]byte(jsonData), &ssm.localCredCache) + + if dirInfo.Mode().Perm() != 0o700 { + return fmt.Errorf("incorrect permissions(%o, expected 700) for %s.", dirInfo.Mode().Perm(), ssm.credDirPath) + } + + fileInfo, err := os.Stat(ssm.credFilePath()) if err != nil { - logger.Debugf("failed to read JSON. Err: %v", err) - return nil + return err + } + + if fileInfo.Mode().Perm() != 0o600 { + logger.Debugf("Incorrect permissions(%o, expected 600) for credential file.", fileInfo.Mode().Perm()) + err := os.Chmod(ssm.credFilePath(), 0o600) + if err != nil { + return fmt.Errorf("Failed to chmod credential file: %v", err) + } + logger.Debug("Successfully fixed credential file permissions.") } - return ssm.localCredCache + return nil +} + +func (ssm *fileBasedSecureStorageManager) readTemporaryCacheFile() (map[string]map[string]string, error) { + err := ssm.ensurePermissions() + if err != nil { + return nil, err + } + + jsonData, err := os.ReadFile(ssm.credFilePath()) + if err != nil { + return nil, fmt.Errorf("failed to read credential cache file: %w", err) + } + + credentialsMap := map[string]map[string]string{} + err = json.Unmarshal([]byte(jsonData), &credentialsMap) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal credential cache file: %w", err) + } + + return credentialsMap, nil } func (ssm *fileBasedSecureStorageManager) deleteCredential(tokenSpec *secureTokenSpec) { - ssm.credCacheLock.Lock() - defer ssm.credCacheLock.Unlock() credentialsKey := tokenSpec.buildKey() - delete(ssm.localCredCache, credentialsKey) - j, err := json.Marshal(ssm.localCredCache) + err := ssm.lockFile() if err != nil { - logger.Warnf("failed to convert credential to JSON.") + logger.Warnf("Set credential failed. Unable to lock cache. %v", err) return } - ssm.writeTemporaryCacheFile(j) -} + defer ssm.unlockFile() -func (ssm *fileBasedSecureStorageManager) writeTemporaryCacheFile(input []byte) { - logger.Debugf("writing credential cache file. %v\n", ssm.credCacheFilePath) - credCacheLockFileName := ssm.credCacheFilePath + ".lck" - err := os.Mkdir(credCacheLockFileName, 0600) - logger.Debugf("Creating lock file. %v", credCacheLockFileName) + credCache, err := ssm.readTemporaryCacheFile() + if err != nil { + logger.Warnf("Set credential failed. Unable to read cache. %v", err) + return + } - switch { - case os.IsExist(err): - statinfo, err := os.Stat(credCacheLockFileName) - if err != nil { - logger.Debugf("failed to write credential cache file. file: %v, err: %v. ignored.\n", ssm.credCacheFilePath, err) - return - } - if time.Since(statinfo.ModTime()) < 15*time.Minute { - logger.Debugf("other process locks the cache file. %v. ignored.\n", ssm.credCacheFilePath) - return - } - if err = os.Remove(credCacheLockFileName); err != nil { - logger.Debugf("failed to delete lock file. file: %v, err: %v. ignored.\n", credCacheLockFileName, err) - return - } - if err = os.Mkdir(credCacheLockFileName, 0600); err != nil { - logger.Debugf("failed to delete lock file. file: %v, err: %v. ignored.\n", credCacheLockFileName, err) - return - } + delete(credCache["tokens"], credentialsKey) + + err = ssm.writeTemporaryCacheFile(credCache) + if err != nil { + logger.Warnf("Set credential failed. Unable to write cache. %v", err) + return + } + + return +} + +func (ssm *fileBasedSecureStorageManager) writeTemporaryCacheFile(cache map[string]map[string]string) error { + bytes, err := json.Marshal(cache) + if err != nil { + return fmt.Errorf("failed to marshal credential cache map. %w", err) } - defer os.RemoveAll(credCacheLockFileName) - if err = os.WriteFile(ssm.credCacheFilePath, input, 0644); err != nil { - logger.Debugf("Failed to write the cache file. File: %v err: %v.", ssm.credCacheFilePath, err) + err = os.WriteFile(ssm.credFilePath(), bytes, 0600) + if err != nil { + return fmt.Errorf("failed to write the credential cache file: %w", err) } + return nil } type keyringSecureStorageManager struct { From 09c3baa79c87c2dfa1d166af9a1148f7f805491c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Tue, 11 Feb 2025 17:24:50 +0100 Subject: [PATCH 3/4] Implement file locks --- secure_storage_manager.go | 52 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/secure_storage_manager.go b/secure_storage_manager.go index 6caaa62a2..113aa0d7a 100644 --- a/secure_storage_manager.go +++ b/secure_storage_manager.go @@ -4,12 +4,15 @@ package gosnowflake import ( "encoding/json" + "errors" "fmt" "github.com/99designs/keyring" + "golang.org/x/sys/unix" "os" "path/filepath" "runtime" "strings" + "time" ) type tokenType string @@ -190,13 +193,56 @@ func (ssm *fileBasedSecureStorageManager) setCredential(tokenSpec *secureTokenSp return } +func (ssm *fileBasedSecureStorageManager) lockPath() string { + return filepath.Join(ssm.credDirPath, credCacheFileName+".lck") +} + func (ssm *fileBasedSecureStorageManager) lockFile() error { - // TODO Implement locks + const NUM_RETRIES = 10 + const RETRY_INTERVAL = 100 * time.Millisecond + lockPath := ssm.lockPath() + locked := false + for i := 0; i < NUM_RETRIES; i++ { + err := os.Mkdir(lockPath, 0o700) + if err != nil { + if errors.Is(err, os.ErrExist) { + time.Sleep(RETRY_INTERVAL) + continue + } + return fmt.Errorf("failed to create cache lock: %v, err: %v", lockPath, err) + } + locked = true + } + + if !locked { + logger.Warnf("failed to lock cache lock. lockPath: %v.", lockPath) + var stat unix.Stat_t + err := unix.Stat(lockPath, &stat) + if err != nil { + return fmt.Errorf("failed to stat %v and determine if lock is stale. err: %v", lockPath, err) + } + + if stat.Ctim.Nano()+time.Second.Nanoseconds() < time.Now().UnixNano() { + err := os.Remove(lockPath) + if err != nil { + return fmt.Errorf("failed to remove %v while trying to remove stale lock. err: %v", lockPath, err) + } + err = os.Mkdir(lockPath, 0o700) + if err != nil { + return fmt.Errorf("failed to recreate cache lock after removing stale lock. %v, err: %v", lockPath, err) + } + } + return fmt.Errorf("failed to lock cache lock %v", lockPath) + } return nil } func (ssm *fileBasedSecureStorageManager) unlockFile() { - // TODO Implement locks + lockPath := ssm.lockPath() + err := os.Remove(lockPath) + if err != nil { + logger.Warnf("Failed to unlock cache lock: %v. %v", lockPath, err) + } } func (ssm *fileBasedSecureStorageManager) getCredential(tokenSpec *secureTokenSpec) string { @@ -432,5 +478,5 @@ func (ssm *noopSecureStorageManager) getCredential(_ *secureTokenSpec) string { return "" } -func (ssm *noopSecureStorageManager) deleteCredential(_ *secureTokenSpec) { //TODO implement me +func (ssm *noopSecureStorageManager) deleteCredential(_ *secureTokenSpec) { } From 323caafb544b15f408aac2bfea99b7796a72764e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Wed, 12 Feb 2025 15:08:42 +0100 Subject: [PATCH 4/4] Fix --- secure_storage_manager.go | 76 +++++++++++++++++----------------- secure_storage_manager_test.go | 38 +++++++++++++++++ util_test.go | 6 +++ 3 files changed, 83 insertions(+), 37 deletions(-) diff --git a/secure_storage_manager.go b/secure_storage_manager.go index 113aa0d7a..e27add725 100644 --- a/secure_storage_manager.go +++ b/secure_storage_manager.go @@ -23,7 +23,6 @@ const ( ) const ( - driverName = "SNOWFLAKE-GO-DRIVER" credCacheDirEnv = "SF_TEMPORARY_CREDENTIAL_CACHE_DIR" credCacheFileName = "temporary_credential.json" ) @@ -167,6 +166,23 @@ func buildCredCacheDirPath() string { return "" } +func (ssm *fileBasedSecureStorageManager) getTokens(data map[string]any) map[string]interface{} { + val, ok := data["tokens"] + emptyMap := map[string]interface{}{} + if !ok { + data["tokens"] = emptyMap + return emptyMap + } + + tokens, ok := val.(map[string]interface{}) + if !ok { + data["tokens"] = emptyMap + return emptyMap + } + + return tokens +} + func (ssm *fileBasedSecureStorageManager) setCredential(tokenSpec *secureTokenSpec, value string) { credentialsKey := tokenSpec.buildKey() err := ssm.lockFile() @@ -176,13 +192,8 @@ func (ssm *fileBasedSecureStorageManager) setCredential(tokenSpec *secureTokenSp } defer ssm.unlockFile() - credCache, err := ssm.readTemporaryCacheFile() - if err != nil { - logger.Warnf("Set credential failed. Unable to read cache. %v", err) - return - } - - credCache["tokens"][credentialsKey] = value + credCache := ssm.readTemporaryCacheFile() + ssm.getTokens(credCache)[credentialsKey] = value err = ssm.writeTemporaryCacheFile(credCache) if err != nil { @@ -212,6 +223,7 @@ func (ssm *fileBasedSecureStorageManager) lockFile() error { return fmt.Errorf("failed to create cache lock: %v, err: %v", lockPath, err) } locked = true + break } if !locked { @@ -232,7 +244,6 @@ func (ssm *fileBasedSecureStorageManager) lockFile() error { return fmt.Errorf("failed to recreate cache lock after removing stale lock. %v, err: %v", lockPath, err) } } - return fmt.Errorf("failed to lock cache lock %v", lockPath) } return nil } @@ -247,29 +258,25 @@ func (ssm *fileBasedSecureStorageManager) unlockFile() { func (ssm *fileBasedSecureStorageManager) getCredential(tokenSpec *secureTokenSpec) string { credentialsKey := tokenSpec.buildKey() - credCache := map[string]map[string]string{} - err := ssm.lockFile() if err != nil { logger.Warn("Failed to lock credential cache file.") return "" } - credCache, err = ssm.readTemporaryCacheFile() + credCache := ssm.readTemporaryCacheFile() ssm.unlockFile() - if err != nil { - logger.Warnf("Failed to read temporary cache file. %v.\n", err) + cred, ok := ssm.getTokens(credCache)[credentialsKey] + if !ok { return "" } - cred := credCache["tokens"][credentialsKey] - if cred != "" { - logger.Debug("Successfully read token. Returning as string") - } else { - logger.Debug("Returned credential is empty") + credStr, ok := cred.(string) + if !ok { + return "" } - return cred + return credStr } func (ssm *fileBasedSecureStorageManager) credFilePath() string { @@ -303,24 +310,26 @@ func (ssm *fileBasedSecureStorageManager) ensurePermissions() error { return nil } -func (ssm *fileBasedSecureStorageManager) readTemporaryCacheFile() (map[string]map[string]string, error) { +func (ssm *fileBasedSecureStorageManager) readTemporaryCacheFile() map[string]any { err := ssm.ensurePermissions() if err != nil { - return nil, err + logger.Warnf("Failed to ensure permission for temporary cache file. %v.\n", err) + return map[string]any{} } jsonData, err := os.ReadFile(ssm.credFilePath()) if err != nil { - return nil, fmt.Errorf("failed to read credential cache file: %w", err) + logger.Warnf("Failed to read credential cache file. %v.\n", err) + return map[string]any{} } - credentialsMap := map[string]map[string]string{} + credentialsMap := map[string]any{} err = json.Unmarshal([]byte(jsonData), &credentialsMap) if err != nil { - return nil, fmt.Errorf("failed to unmarshal credential cache file: %w", err) + logger.Warnf("Failed to unmarshal credential cache file. %v.\n", err) } - return credentialsMap, nil + return credentialsMap } func (ssm *fileBasedSecureStorageManager) deleteCredential(tokenSpec *secureTokenSpec) { @@ -332,13 +341,8 @@ func (ssm *fileBasedSecureStorageManager) deleteCredential(tokenSpec *secureToke } defer ssm.unlockFile() - credCache, err := ssm.readTemporaryCacheFile() - if err != nil { - logger.Warnf("Set credential failed. Unable to read cache. %v", err) - return - } - - delete(credCache["tokens"], credentialsKey) + credCache := ssm.readTemporaryCacheFile() + delete(ssm.getTokens(credCache), credentialsKey) err = ssm.writeTemporaryCacheFile(credCache) if err != nil { @@ -349,7 +353,7 @@ func (ssm *fileBasedSecureStorageManager) deleteCredential(tokenSpec *secureToke return } -func (ssm *fileBasedSecureStorageManager) writeTemporaryCacheFile(cache map[string]map[string]string) error { +func (ssm *fileBasedSecureStorageManager) writeTemporaryCacheFile(cache map[string]any) error { bytes, err := json.Marshal(cache) if err != nil { return fmt.Errorf("failed to marshal credential cache map. %w", err) @@ -458,9 +462,7 @@ func (ssm *keyringSecureStorageManager) deleteCredential(tokenSpec *secureTokenS } func buildCredentialsKey(host, user string, credType tokenType) string { - host = strings.ToUpper(host) - user = strings.ToUpper(user) - credTypeStr := strings.ToUpper(string(credType)) + credTypeStr := string(credType) return host + ":" + user + ":" + credTypeStr } diff --git a/secure_storage_manager_test.go b/secure_storage_manager_test.go index 22299708b..6b09e0828 100644 --- a/secure_storage_manager_test.go +++ b/secure_storage_manager_test.go @@ -3,9 +3,47 @@ package gosnowflake import ( + "os" "testing" ) +type EnvOverride struct { + env string + oldValue string +} + +func (e *EnvOverride) rollback() { + if e.oldValue != "" { + os.Setenv(e.env, e.oldValue) + } else { + os.Unsetenv(e.env) + } +} + +func override_env(env string, value string) EnvOverride { + oldValue := os.Getenv(env) + os.Setenv(env, value) + return EnvOverride{env, oldValue} +} + +func TestSnowflakeFileBasedSecureStorageManager(t *testing.T) { + //skipOnNonLinux(t, "Not supported on non-linux") + os.Mkdir("./testdata", 0777) + credCacheDirEnvOverride := override_env(credCacheDirEnv, "./testdata") + defer credCacheDirEnvOverride.rollback() + fbss, err := newFileBasedSecureStorageManager() + if err != nil { + t.Fatal(err) + } + + tokenSpec := newMfaTokenSpec("host.xd", "johndoe") + cred := "token123" + fbss.setCredential(tokenSpec, cred) + assertEqualE(t, fbss.getCredential(tokenSpec), cred) + fbss.deleteCredential(tokenSpec) + assertEqualE(t, fbss.getCredential(tokenSpec), "") +} + func TestSetAndGetCredentialMfa(t *testing.T) { for _, tokenSpec := range []*secureTokenSpec{ newMfaTokenSpec("testhost", "testuser"), diff --git a/util_test.go b/util_test.go index f67912dbb..6f686b6ca 100644 --- a/util_test.go +++ b/util_test.go @@ -404,6 +404,12 @@ func skipOnMac(t *testing.T, reason string) { } } +func skipOnNonLinux(t *testing.T, reason string) { + if runtime.GOOS != "linux" { + t.Skip("skipped on non-linux OS: " + reason) + } +} + func randomString(n int) string { r := rand.New(rand.NewSource(time.Now().UnixNano())) alpha := []rune("abcdefghijklmnopqrstuvwxyz")