From 3a640d6cf8241c808ef1cde7d09c18a4dfb80e18 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Tue, 30 Jul 2024 19:23:24 -0400 Subject: [PATCH] add single secret fetch for agent --- cli/packages/api/api.go | 28 ++++++++++++++++++++++++++++ cli/packages/api/model.go | 22 ++++++++++++++++++++++ cli/packages/cmd/agent.go | 21 +++++++++++++++++++-- cli/packages/models/cli.go | 1 + cli/packages/util/agent.go | 2 +- cli/packages/util/secrets.go | 30 ++++++++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 3 deletions(-) diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go index 184874f8ae..9eab5e759a 100644 --- a/cli/packages/api/api.go +++ b/cli/packages/api/api.go @@ -434,6 +434,34 @@ func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Reques return getRawSecretsV3Response, nil } +func CallFetchSingleSecretByName(httpClient *resty.Client, request GetRawSecretV3ByNameRequest) (GetRawSecretV3ByNameResponse, error) { + var getRawSecretV3ByNameResponse GetRawSecretV3ByNameResponse + response, err := httpClient. + R(). + SetHeader("User-Agent", USER_AGENT). + SetResult(&getRawSecretV3ByNameResponse). + SetBody(request). + SetQueryParam("expandSecretReferences", "true"). + SetQueryParam("include_imports", "true"). + SetQueryParam("environment", request.Environment). + SetQueryParam("secretPath", request.SecretPath). + SetQueryParam("workspaceId", request.WorkspaceID). + SetQueryParam("type", "shared"). + Get(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName)) + + if err != nil { + return GetRawSecretV3ByNameResponse{}, fmt.Errorf("CallFetchSingleSecretByName: Unable to complete api request [err=%w]", err) + } + + if response.IsError() { + return GetRawSecretV3ByNameResponse{}, fmt.Errorf("CallFetchSingleSecretByName: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String()) + } + + getRawSecretV3ByNameResponse.ETag = response.Header().Get(("etag")) + + return getRawSecretV3ByNameResponse, nil +} + func CallCreateDynamicSecretLeaseV1(httpClient *resty.Client, request CreateDynamicSecretLeaseV1Request) (CreateDynamicSecretLeaseV1Response, error) { var createDynamicSecretLeaseResponse CreateDynamicSecretLeaseV1Response response, err := httpClient. diff --git a/cli/packages/api/model.go b/cli/packages/api/model.go index e09f2275f8..dc577bf9d5 100644 --- a/cli/packages/api/model.go +++ b/cli/packages/api/model.go @@ -590,3 +590,25 @@ type GetRawSecretsV3Response struct { Imports []ImportedRawSecretV3 `json:"imports"` ETag string } + +type GetRawSecretV3ByNameRequest struct { + SecretName string `json:"secretName"` + WorkspaceID string `json:"workspaceId"` + Type string `json:"type,omitempty"` + Environment string `json:"environment"` + SecretPath string `json:"secretPath,omitempty"` +} + +type GetRawSecretV3ByNameResponse struct { + Secret struct { + ID string `json:"_id"` + Version int `json:"version"` + Workspace string `json:"workspace"` + Type string `json:"type"` + Environment string `json:"environment"` + SecretKey string `json:"secretKey"` + SecretValue string `json:"secretValue"` + SecretComment string `json:"secretComment"` + } `json:"secret"` + ETag string +} diff --git a/cli/packages/cmd/agent.go b/cli/packages/cmd/agent.go index 267b22df8b..537131bc29 100644 --- a/cli/packages/cmd/agent.go +++ b/cli/packages/cmd/agent.go @@ -327,6 +327,21 @@ func secretTemplateFunction(accessToken string, existingEtag string, currentEtag } } +func getSingleSecretTemplateFunction(accessToken string, existingEtag string, currentEtag *string) func(string, string, string, string) (models.SingleEnvironmentVariable, error) { + return func(projectID, envSlug, secretPath, secretName string) (models.SingleEnvironmentVariable, error) { + secret, requestEtag, err := util.GetSinglePlainTextSecretByNameV3(accessToken, projectID, envSlug, secretPath, secretName) + if err != nil { + return models.SingleEnvironmentVariable{}, err + } + + if existingEtag != requestEtag { + *currentEtag = requestEtag + } + + return secret, nil + } +} + func dynamicSecretTemplateFunction(accessToken string, dynamicSecretManager *DynamicSecretLeaseManager, templateId int) func(...string) (map[string]interface{}, error) { return func(args ...string) (map[string]interface{}, error) { argLength := len(args) @@ -358,9 +373,11 @@ func ProcessTemplate(templateId int, templatePath string, data interface{}, acce // custom template function to fetch secrets from Infisical secretFunction := secretTemplateFunction(accessToken, existingEtag, currentEtag) dynamicSecretFunction := dynamicSecretTemplateFunction(accessToken, dynamicSecretManager, templateId) + getSingleSecretFunction := getSingleSecretTemplateFunction(accessToken, existingEtag, currentEtag) funcs := template.FuncMap{ - "secret": secretFunction, - "dynamic_secret": dynamicSecretFunction, + "secret": secretFunction, + "dynamic_secret": dynamicSecretFunction, + "getSecretByName": getSingleSecretFunction, "minus": func(a, b int) int { return a - b }, diff --git a/cli/packages/models/cli.go b/cli/packages/models/cli.go index 4b02cb6f84..9bdc26fbc9 100644 --- a/cli/packages/models/cli.go +++ b/cli/packages/models/cli.go @@ -35,6 +35,7 @@ type SingleEnvironmentVariable struct { Workspace string `json:"workspace"` } `json:"tags"` Comment string `json:"comment"` + Etag string `json:"Etag"` } type PlaintextSecretResult struct { diff --git a/cli/packages/util/agent.go b/cli/packages/util/agent.go index 188ae5de25..215e43551b 100644 --- a/cli/packages/util/agent.go +++ b/cli/packages/util/agent.go @@ -24,7 +24,7 @@ func ConvertPollingIntervalToTime(pollingInterval string) (time.Duration, error) switch unit { case "s": if number < 60 { - return 0, fmt.Errorf("polling interval should be at least 60 seconds") + return 0, fmt.Errorf("polling interval must be at least 60 seconds") } return time.Duration(number) * time.Second, nil case "m": diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index 53c11abbf5..c51338d61d 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -118,6 +118,36 @@ func GetPlainTextSecretsV3(accessToken string, workspaceId string, environmentNa }, nil } +func GetSinglePlainTextSecretByNameV3(accessToken string, workspaceId string, environmentName string, secretsPath string, secretName string) (models.SingleEnvironmentVariable, string, error) { + httpClient := resty.New() + httpClient.SetAuthToken(accessToken). + SetHeader("Accept", "application/json") + + getSecretsRequest := api.GetRawSecretV3ByNameRequest{ + WorkspaceID: workspaceId, + Environment: environmentName, + SecretName: secretName, + SecretPath: secretsPath, + } + + rawSecret, err := api.CallFetchSingleSecretByName(httpClient, getSecretsRequest) + + if err != nil { + return models.SingleEnvironmentVariable{}, "", err + } + + formattedSecrets := models.SingleEnvironmentVariable{ + Key: rawSecret.Secret.SecretKey, + WorkspaceId: rawSecret.Secret.Workspace, + Value: rawSecret.Secret.SecretValue, + Type: rawSecret.Secret.Type, + ID: rawSecret.Secret.ID, + Comment: rawSecret.Secret.SecretComment, + } + + return formattedSecrets, rawSecret.ETag, nil +} + func CreateDynamicSecretLease(accessToken string, projectSlug string, environmentName string, secretsPath string, slug string, ttl string) (models.DynamicSecretLease, error) { httpClient := resty.New() httpClient.SetAuthToken(accessToken).