Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/util/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ func WriteToFile(fileName string, dataToWrite []byte, filePerm os.FileMode) erro
}

func ValidateInfisicalAPIConnection() (ok bool) {
_, err := http.Get(fmt.Sprintf("%v/status", config.INFISICAL_URL))
return err == nil
resp, err := http.Get(fmt.Sprintf("%v/status", config.INFISICAL_URL))
Comment thread
jakehulberg marked this conversation as resolved.
if err != nil {
return false
}
defer resp.Body.Close()
return resp.StatusCode >= 200 && resp.StatusCode < 300
Comment thread
jakehulberg marked this conversation as resolved.
}

func GetRestyClientWithCustomHeaders() (*resty.Client, error) {
Expand Down
57 changes: 47 additions & 10 deletions packages/util/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,40 @@ import (
"gopkg.in/yaml.v3"
)

func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment string, secretPath string, includeImports bool, recursive bool, tagSlugs string, expandSecretReferences bool, includePersonalOverrides bool) ([]models.SingleEnvironmentVariable, error) {
func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment string, secretPath string, includeImports bool, recursive bool, tagSlugs string, expandSecretReferences bool, includePersonalOverrides bool) ([]models.SingleEnvironmentVariable, string, error) {
serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4)
if len(serviceTokenParts) < 4 {
return nil, fmt.Errorf("invalid service token entered. Please double check your service token and try again")
return nil, "", fmt.Errorf("invalid service token entered. Please double check your service token and try again")
}

serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2])

httpClient, err := GetRestyClientWithCustomHeaders()
if err != nil {
return nil, fmt.Errorf("unable to get client with custom headers [err=%v]", err)
return nil, "", fmt.Errorf("unable to get client with custom headers [err=%w]", err)
}

httpClient.SetAuthToken(serviceToken).
SetHeader("Accept", "application/json")

serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient)
if err != nil {
return nil, fmt.Errorf("unable to get service token details. [err=%v]", err)
return nil, "", fmt.Errorf("unable to get service token details. [err=%w]", err)
}

workspaceId := serviceTokenDetails.Workspace

// if multiple scopes are there then user needs to specify which environment and secret path
if environment == "" {
if len(serviceTokenDetails.Scopes) != 1 {
return nil, fmt.Errorf("you need to provide the --env for multiple environment scoped token")
return nil, workspaceId, fmt.Errorf("you need to provide the --env for multiple environment scoped token")
} else {
environment = serviceTokenDetails.Scopes[0].Environment
}
}

rawSecrets, err := api.CallGetSecretsV4(httpClient, api.GetSecretsV4Request{
WorkspaceId: serviceTokenDetails.Workspace,
WorkspaceId: workspaceId,
Environment: environment,
SecretPath: secretPath,
IncludeImport: includeImports,
Expand All @@ -62,7 +64,7 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment str
})

if err != nil {
return nil, err
return nil, workspaceId, err
}

plainTextSecrets := []models.SingleEnvironmentVariable{}
Expand All @@ -74,11 +76,11 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment str
if includeImports {
plainTextSecrets, err = InjectRawImportedSecret(plainTextSecrets, rawSecrets.Imports)
if err != nil {
return nil, err
return nil, workspaceId, err
}
}

return plainTextSecrets, nil
return plainTextSecrets, workspaceId, nil

}

Expand Down Expand Up @@ -361,9 +363,17 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo
}

} else {
// workspaceId for caching β€” use params.WorkspaceId if provided (via --projectId),
// otherwise populated from the service token details.
cacheWorkspaceId := params.WorkspaceId

if params.InfisicalToken != "" {
log.Debug().Msg("Trying to fetch secrets using service token")
secretsToReturn, errorToReturn = GetPlainTextSecretsViaServiceToken(params.InfisicalToken, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive, params.TagSlugs, params.ExpandSecretReferences, params.IncludePersonalOverrides)
var tokenWorkspaceId string
secretsToReturn, tokenWorkspaceId, errorToReturn = GetPlainTextSecretsViaServiceToken(params.InfisicalToken, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive, params.TagSlugs, params.ExpandSecretReferences, params.IncludePersonalOverrides)
if cacheWorkspaceId == "" {
cacheWorkspaceId = tokenWorkspaceId
}
} else if params.UniversalAuthAccessToken != "" {

if params.WorkspaceId == "" {
Expand All @@ -376,6 +386,33 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo
errorToReturn = err
secretsToReturn = res.Secrets
}

// cache secrets on success, fallback to cached secrets on connection/server failure
if errorToReturn == nil && cacheWorkspaceId != "" {
if backupEncryptionKey, err := GetBackupEncryptionKey(); err == nil {
WriteBackupSecrets(cacheWorkspaceId, params.Environment, params.SecretsPath, backupEncryptionKey, secretsToReturn)
}
} else if errorToReturn != nil && cacheWorkspaceId != "" {
// Only fall back to cache for connection errors or server errors (5xx).
// Do not mask client errors (4xx) like 401/403 which indicate auth issues.
shouldFallback := true
var apiErr *api.APIError
if errors.As(errorToReturn, &apiErr) && apiErr.StatusCode >= 400 && apiErr.StatusCode < 500 {
shouldFallback = false
}
Comment thread
jakehulberg marked this conversation as resolved.

if shouldFallback {
backupEncryptionKey, _ := GetBackupEncryptionKey()
if backupEncryptionKey != nil {
backedUpSecrets, err := ReadBackupSecrets(cacheWorkspaceId, params.Environment, params.SecretsPath, backupEncryptionKey)
if len(backedUpSecrets) > 0 {
PrintWarning("Unable to fetch the latest secret(s) due to connection error, serving secrets from last successful fetch. For more info, run with --debug")
secretsToReturn = backedUpSecrets
errorToReturn = err
}
}
}
}
}

return secretsToReturn, errorToReturn
Expand Down
Loading