diff --git a/cli/packages/cmd/agent.go b/cli/packages/cmd/agent.go index 750727df1..db7c81225 100644 --- a/cli/packages/cmd/agent.go +++ b/cli/packages/cmd/agent.go @@ -228,7 +228,9 @@ func secretTemplateFunction(accessToken string, existingEtag string, currentEtag *currentEtag = res.Etag } - return res.Secrets, nil + expandedSecrets := util.ExpandSecrets(res.Secrets, models.ExpandSecretsAuthentication{UniversalAuthAccessToken: accessToken}, "") + + return expandedSecrets, nil } } @@ -622,7 +624,7 @@ var agentCmd = &cobra.Command{ } if !FileExists(configPath) && agentConfigInBase64 == "" { - log.Error().Msgf("No agent config file provided. Please provide a agent config file", configPath) + log.Error().Msgf("No agent config file provided at %v. Please provide a agent config file", configPath) return } diff --git a/cli/packages/cmd/export.go b/cli/packages/cmd/export.go index 26cc45f65..9c3590e89 100644 --- a/cli/packages/cmd/export.go +++ b/cli/packages/cmd/export.go @@ -87,7 +87,9 @@ var exportCmd = &cobra.Command{ var output string if shouldExpandSecrets { - secrets = util.ExpandSecrets(secrets, infisicalToken, "") + secrets = util.ExpandSecrets(secrets, models.ExpandSecretsAuthentication{ + InfisicalToken: infisicalToken, + }, "") } secrets = util.FilterSecretsByTag(secrets, tagSlugs) output, err = formatEnvs(secrets, format) diff --git a/cli/packages/cmd/run.go b/cli/packages/cmd/run.go index 2bb043c26..48eaffea9 100644 --- a/cli/packages/cmd/run.go +++ b/cli/packages/cmd/run.go @@ -110,7 +110,9 @@ var runCmd = &cobra.Command{ } if shouldExpandSecrets { - secrets = util.ExpandSecrets(secrets, infisicalToken, projectConfigDir) + secrets = util.ExpandSecrets(secrets, models.ExpandSecretsAuthentication{ + InfisicalToken: infisicalToken, + }, projectConfigDir) } secretsByKey := getSecretsByKeys(secrets) diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go index 778ac574e..1a53a243e 100644 --- a/cli/packages/cmd/secrets.go +++ b/cli/packages/cmd/secrets.go @@ -7,6 +7,7 @@ import ( "crypto/sha256" "encoding/base64" "fmt" + "os" "regexp" "sort" "strings" @@ -39,6 +40,11 @@ var secretsCmd = &cobra.Command{ } infisicalToken, err := cmd.Flags().GetString("token") + + if infisicalToken == "" { + infisicalToken = os.Getenv(util.INFISICAL_TOKEN_NAME) + } + if err != nil { util.HandleError(err, "Unable to parse flag") } @@ -80,7 +86,9 @@ var secretsCmd = &cobra.Command{ } if shouldExpandSecrets { - secrets = util.ExpandSecrets(secrets, infisicalToken, "") + secrets = util.ExpandSecrets(secrets, models.ExpandSecretsAuthentication{ + InfisicalToken: infisicalToken, + }, "") } visualize.PrintAllSecretDetails(secrets) diff --git a/cli/packages/models/cli.go b/cli/packages/models/cli.go index c3999f68b..576e74909 100644 --- a/cli/packages/models/cli.go +++ b/cli/packages/models/cli.go @@ -21,11 +21,12 @@ type LoggedInUser struct { } type SingleEnvironmentVariable struct { - Key string `json:"key"` - Value string `json:"value"` - Type string `json:"type"` - ID string `json:"_id"` - Tags []struct { + Key string `json:"key"` + WorkspaceId string `json:"workspace"` + Value string `json:"value"` + Type string `json:"type"` + ID string `json:"_id"` + Tags []struct { ID string `json:"_id"` Name string `json:"name"` Slug string `json:"slug"` @@ -68,6 +69,7 @@ type GetAllSecretsParameters struct { Environment string EnvironmentPassedViaFlag bool InfisicalToken string + UniversalAuthAccessToken string TagSlugs string WorkspaceId string SecretsPath string @@ -96,3 +98,8 @@ type DeleteFolderParameters struct { FolderPath string InfisicalToken string } + +type ExpandSecretsAuthentication struct { + InfisicalToken string + UniversalAuthAccessToken string +} diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index cc75681e8..a1764d721 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -179,7 +179,7 @@ func GetPlainTextSecretsViaMachineIdentity(accessToken string, workspaceId strin } for _, secret := range rawSecrets.Secrets { - plainTextSecrets = append(plainTextSecrets, models.SingleEnvironmentVariable{Key: secret.SecretKey, Value: secret.SecretValue}) + plainTextSecrets = append(plainTextSecrets, models.SingleEnvironmentVariable{Key: secret.SecretKey, Value: secret.SecretValue, WorkspaceId: secret.Workspace}) } // if includeImports { @@ -248,11 +248,8 @@ func FilterSecretsByTag(plainTextSecrets []models.SingleEnvironmentVariable, tag } func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectConfigFilePath string) ([]models.SingleEnvironmentVariable, error) { - var infisicalToken string if params.InfisicalToken == "" { - infisicalToken = os.Getenv(INFISICAL_TOKEN_NAME) - } else { - infisicalToken = params.InfisicalToken + params.InfisicalToken = os.Getenv(INFISICAL_TOKEN_NAME) } isConnected := CheckIsConnectedToInternet() @@ -260,7 +257,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo // var serviceTokenDetails api.GetServiceTokenDetailsResponse var errorToReturn error - if infisicalToken == "" { + if params.InfisicalToken == "" && params.UniversalAuthAccessToken == "" { if isConnected { log.Debug().Msg("GetAllEnvironmentVariables: Connected to internet, checking logged in creds") @@ -306,12 +303,6 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo infisicalDotJson.WorkspaceId = params.WorkspaceId } - // // Verify environment - // err = ValidateEnvironmentName(params.Environment, workspaceFile.WorkspaceId, loggedInUserDetails.UserCredentials) - // if err != nil { - // return nil, fmt.Errorf("unable to validate environment name because [err=%s]", err) - // } - secretsToReturn, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, infisicalDotJson.WorkspaceId, params.Environment, params.TagSlugs, params.SecretsPath, params.IncludeImport) log.Debug().Msgf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", errorToReturn) @@ -332,91 +323,19 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo } } else { - log.Debug().Msg("Trying to fetch secrets using service token") - secretsToReturn, _, errorToReturn = GetPlainTextSecretsViaServiceToken(infisicalToken, params.Environment, params.SecretsPath, params.IncludeImport) - } + if params.InfisicalToken != "" { + log.Debug().Msg("Trying to fetch secrets using service token") + secretsToReturn, _, errorToReturn = GetPlainTextSecretsViaServiceToken(params.InfisicalToken, params.Environment, params.SecretsPath, params.IncludeImport) + } else if params.UniversalAuthAccessToken != "" { + log.Debug().Msg("Trying to fetch secrets using universal auth") + res, err := GetPlainTextSecretsViaMachineIdentity(params.UniversalAuthAccessToken, params.WorkspaceId, params.Environment, params.SecretsPath, params.IncludeImport) - return secretsToReturn, errorToReturn -} - -// func ValidateEnvironmentName(environmentName string, workspaceId string, userLoggedInDetails models.UserCredentials) error { -// httpClient := resty.New() -// httpClient.SetAuthToken(userLoggedInDetails.JTWToken). -// SetHeader("Accept", "application/json") - -// response, err := api.CallGetAccessibleEnvironments(httpClient, api.GetAccessibleEnvironmentsRequest{WorkspaceId: workspaceId}) -// if err != nil { -// return err -// } - -// listOfEnvSlugs := []string{} -// mapOfEnvSlugs := make(map[string]interface{}) - -// for _, environment := range response.AccessibleEnvironments { -// listOfEnvSlugs = append(listOfEnvSlugs, environment.Slug) -// mapOfEnvSlugs[environment.Slug] = environment -// } - -// _, exists := mapOfEnvSlugs[environmentName] -// if !exists { -// HandleError(fmt.Errorf("the environment [%s] does not exist in project with [id=%s]. Only [%s] are available", environmentName, workspaceId, strings.Join(listOfEnvSlugs, ","))) -// } - -// return nil - -// } - -func getExpandedEnvVariable(secrets []models.SingleEnvironmentVariable, variableWeAreLookingFor string, hashMapOfCompleteVariables map[string]string, hashMapOfSelfRefs map[string]string) string { - if value, found := hashMapOfCompleteVariables[variableWeAreLookingFor]; found { - return value - } - - for _, secret := range secrets { - if secret.Key == variableWeAreLookingFor { - regex := regexp.MustCompile(`\${([^\}]*)}`) - variablesToPopulate := regex.FindAllString(secret.Value, -1) - - // case: variable is a constant so return its value - if len(variablesToPopulate) == 0 { - return secret.Value - } - - valueToEdit := secret.Value - for _, variableWithSign := range variablesToPopulate { - variableWithoutSign := strings.Trim(variableWithSign, "}") - variableWithoutSign = strings.Trim(variableWithoutSign, "${") - - // case: reference to self - if variableWithoutSign == secret.Key { - hashMapOfSelfRefs[variableWithoutSign] = variableWithoutSign - continue - } else { - var expandedVariableValue string - - if preComputedVariable, found := hashMapOfCompleteVariables[variableWithoutSign]; found { - expandedVariableValue = preComputedVariable - } else { - expandedVariableValue = getExpandedEnvVariable(secrets, variableWithoutSign, hashMapOfCompleteVariables, hashMapOfSelfRefs) - hashMapOfCompleteVariables[variableWithoutSign] = expandedVariableValue - } - - // If after expanding all the vars above, is the current var a self ref? if so no replacement needed for it - if _, found := hashMapOfSelfRefs[variableWithoutSign]; found { - continue - } else { - valueToEdit = strings.ReplaceAll(valueToEdit, variableWithSign, expandedVariableValue) - } - } - } - - return valueToEdit - - } else { - continue + errorToReturn = err + secretsToReturn = res.Secrets } } - return "${" + variableWeAreLookingFor + "}" + return secretsToReturn, errorToReturn } var secRefRegex = regexp.MustCompile(`\${([^\}]*)}`) @@ -428,7 +347,7 @@ func recursivelyExpandSecret(expandedSecs map[string]string, interpolatedSecs ma interpolatedVal, ok := interpolatedSecs[key] if !ok { - HandleError(fmt.Errorf("Could not find refered secret - %s", key), "Kindly check whether its provided") + HandleError(fmt.Errorf("could not find refered secret - %s", key), "Kindly check whether its provided") } refs := secRefRegex.FindAllStringSubmatch(interpolatedVal, -1) @@ -467,7 +386,7 @@ func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]mod return secretMapByName } -func ExpandSecrets(secrets []models.SingleEnvironmentVariable, infisicalToken string, projectConfigPathDir string) []models.SingleEnvironmentVariable { +func ExpandSecrets(secrets []models.SingleEnvironmentVariable, auth models.ExpandSecretsAuthentication, projectConfigPathDir string) []models.SingleEnvironmentVariable { expandedSecs := make(map[string]string) interpolatedSecs := make(map[string]string) // map[env.secret-path][keyname]Secret @@ -499,8 +418,18 @@ func ExpandSecrets(secrets []models.SingleEnvironmentVariable, infisicalToken st uniqKey := fmt.Sprintf("%s.%s", env, secPathDot) if crossRefSec, ok := crossEnvRefSecs[uniqKey]; !ok { + + var refSecs []models.SingleEnvironmentVariable + var err error + // if not in cross reference cache, fetch it from server - refSecs, err := GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: env, InfisicalToken: infisicalToken, SecretsPath: secPath}, projectConfigPathDir) + if auth.InfisicalToken != "" { + refSecs, err = GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: env, InfisicalToken: auth.InfisicalToken, SecretsPath: secPath}, projectConfigPathDir) + } else if auth.UniversalAuthAccessToken != "" { + refSecs, err = GetAllEnvironmentVariables((models.GetAllSecretsParameters{Environment: env, UniversalAuthAccessToken: auth.UniversalAuthAccessToken, SecretsPath: secPath, WorkspaceId: sec.WorkspaceId}), projectConfigPathDir) + } else { + HandleError(errors.New("no authentication provided"), "Please provide authentication to fetch secrets") + } if err != nil { HandleError(err, fmt.Sprintf("Could not fetch secrets in environment: %s secret-path: %s", env, secPath), "If you are using a service token to fetch secrets, please ensure it is valid") } @@ -508,6 +437,7 @@ func ExpandSecrets(secrets []models.SingleEnvironmentVariable, infisicalToken st // save it to avoid calling api again for same environment and folder path crossEnvRefSecs[uniqKey] = refSecsByKey return refSecsByKey[secKey].Value + } else { return crossRefSec[secKey].Value }