diff --git a/backend/src/controllers/v3/secretsController.ts b/backend/src/controllers/v3/secretsController.ts index 8a9350a361..182d7f14f4 100644 --- a/backend/src/controllers/v3/secretsController.ts +++ b/backend/src/controllers/v3/secretsController.ts @@ -34,7 +34,7 @@ export const getSecretsRaw = async (req: Request, res: Response) => { secret, key }); - + return rep; }) }); @@ -88,7 +88,7 @@ export const createSecretRaw = async (req: Request, res: Response) => { secretComment, secretPath = "/" } = req.body; - + const key = await BotService.getWorkspaceKeyWithBot({ workspaceId: new Types.ObjectId(workspaceId) }); @@ -102,12 +102,12 @@ export const createSecretRaw = async (req: Request, res: Response) => { plaintext: secretValue, key }); - + const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8({ plaintext: secretComment, key - }); - + }); + const secret = await SecretService.createSecret({ secretName, workspaceId: new Types.ObjectId(workspaceId), @@ -135,7 +135,7 @@ export const createSecretRaw = async (req: Request, res: Response) => { const secretWithoutBlindIndex = secret.toObject(); delete secretWithoutBlindIndex.secretBlindIndex; - + return res.status(200).send({ secret: repackageSecretToRaw({ secret: secretWithoutBlindIndex, @@ -202,11 +202,11 @@ export const updateSecretByNameRaw = async (req: Request, res: Response) => { */ export const deleteSecretByNameRaw = async (req: Request, res: Response) => { const { secretName } = req.params; - const { - workspaceId, - environment, - type, - secretPath = "/" + const { + workspaceId, + environment, + type, + secretPath = "/" } = req.body; const { secret } = await SecretService.deleteSecret({ @@ -391,11 +391,11 @@ export const updateSecretByName = async (req: Request, res: Response) => { */ export const deleteSecretByName = async (req: Request, res: Response) => { const { secretName } = req.params; - const { - workspaceId, - environment, - type, - secretPath = "/" + const { + workspaceId, + environment, + type, + secretPath = "/" } = req.body; const { secret } = await SecretService.deleteSecret({ diff --git a/backend/src/helpers/secrets.ts b/backend/src/helpers/secrets.ts index d7cc31cdae..7c8c69e972 100644 --- a/backend/src/helpers/secrets.ts +++ b/backend/src/helpers/secrets.ts @@ -57,7 +57,7 @@ import { getFolderIdFromServiceToken } from "../services/FolderService"; export const repackageSecretToRaw = ({ secret, key -}:{ +}: { secret: ISecret; key: string; }) => { @@ -76,8 +76,8 @@ export const repackageSecretToRaw = ({ key }); - let secretComment: string = ''; - + let secretComment: string = ''; + if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) { secretComment = decryptSymmetric128BitHexKeyUTF8({ ciphertext: secret.secretCommentCiphertext, @@ -86,7 +86,7 @@ export const repackageSecretToRaw = ({ key }); } - + return ({ _id: secret._id, version: secret.version, @@ -503,7 +503,7 @@ export const getSecretsHelper = async ({ folder: folderId, type: SECRET_PERSONAL, ...getAuthDataPayloadUserObj(authData), - }).lean(); + }).populate("tags").lean(); // concat with shared secrets secrets = secrets.concat( @@ -515,7 +515,7 @@ export const getSecretsHelper = async ({ secretBlindIndex: { $nin: secrets.map((secret) => secret.secretBlindIndex), }, - }).lean() + }).populate("tags").lean() ); // (EE) create (audit) log @@ -553,7 +553,7 @@ export const getSecretsHelper = async ({ }, }); } - + return secrets; }; @@ -652,7 +652,7 @@ export const getSecretHelper = async ({ }, }); } - + return secret; }; @@ -843,7 +843,7 @@ export const deleteSecretHelper = async ({ // if using service token filter towards the folderId by secretpath if (authData.authPayload instanceof ServiceTokenData) { const { secretPath: serviceTkScopedSecretPath } = authData.authPayload; - + if (secretPath !== serviceTkScopedSecretPath) { throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); } @@ -909,12 +909,12 @@ export const deleteSecretHelper = async ({ }); action && (await EELogService.createLog({ - ...getAuthDataPayloadIdObj(authData), - workspaceId, - actions: [action], - channel: authData.authChannel, - ipAddress: authData.authIP, - })); + ...getAuthDataPayloadIdObj(authData), + workspaceId, + actions: [action], + channel: authData.authChannel, + ipAddress: authData.authIP, + })); // (EE) take a secret snapshot await EESecretService.takeSecretSnapshot({ @@ -941,7 +941,7 @@ export const deleteSecretHelper = async ({ }, }); } - + return ({ secrets, secret diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go index 87832a5378..a94be0ab0a 100644 --- a/cli/packages/api/api.go +++ b/cli/packages/api/api.go @@ -11,63 +11,6 @@ import ( const USER_AGENT = "cli" -func CallBatchModifySecretsByWorkspaceAndEnv(httpClient *resty.Client, request BatchModifySecretsByWorkspaceAndEnvRequest) error { - endpoint := fmt.Sprintf("%v/v2/secrets", config.INFISICAL_URL) - response, err := httpClient. - R(). - SetBody(request). - SetHeader("User-Agent", USER_AGENT). - Patch(endpoint) - - if err != nil { - return fmt.Errorf("CallBatchModifySecretsByWorkspaceAndEnv: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return fmt.Errorf("CallBatchModifySecretsByWorkspaceAndEnv: Unsuccessful response: [response=%s]", response) - } - - return nil -} - -func CallBatchCreateSecretsByWorkspaceAndEnv(httpClient *resty.Client, request BatchCreateSecretsByWorkspaceAndEnvRequest) error { - endpoint := fmt.Sprintf("%v/v2/secrets/", config.INFISICAL_URL) - response, err := httpClient. - R(). - SetBody(request). - SetHeader("User-Agent", USER_AGENT). - Post(endpoint) - - if err != nil { - return fmt.Errorf("CallBatchCreateSecretsByWorkspaceAndEnv: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return fmt.Errorf("CallBatchCreateSecretsByWorkspaceAndEnv: Unsuccessful response: [response=%s]", response) - } - - return nil -} - -func CallBatchDeleteSecretsByWorkspaceAndEnv(httpClient *resty.Client, request BatchDeleteSecretsBySecretIdsRequest) error { - endpoint := fmt.Sprintf("%v/v2/secrets", config.INFISICAL_URL) - response, err := httpClient. - R(). - SetBody(request). - SetHeader("User-Agent", USER_AGENT). - Delete(endpoint) - - if err != nil { - return fmt.Errorf("CallBatchDeleteSecretsByWorkspaceAndEnv: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return fmt.Errorf("CallBatchDeleteSecretsByWorkspaceAndEnv: Unsuccessful response: [response=%s]", response) - } - - return nil -} - func CallGetEncryptedWorkspaceKey(httpClient *resty.Client, request GetEncryptedWorkspaceKeyRequest) (GetEncryptedWorkspaceKeyResponse, error) { endpoint := fmt.Sprintf("%v/v2/workspace/%v/encrypted-key", config.INFISICAL_URL, request.WorkspaceId) var result GetEncryptedWorkspaceKeyResponse @@ -107,28 +50,6 @@ func CallGetServiceTokenDetailsV2(httpClient *resty.Client) (GetServiceTokenDeta return tokenDetailsResponse, nil } -func CallGetSecretsV2(httpClient *resty.Client, request GetEncryptedSecretsV2Request) (GetEncryptedSecretsV2Response, error) { - var secretsResponse GetEncryptedSecretsV2Response - response, err := httpClient. - R(). - SetResult(&secretsResponse). - SetHeader("User-Agent", USER_AGENT). - SetQueryParam("environment", request.Environment). - SetQueryParam("workspaceId", request.WorkspaceId). - SetQueryParam("tagSlugs", request.TagSlugs). - Get(fmt.Sprintf("%v/v2/secrets", config.INFISICAL_URL)) - - if err != nil { - return GetEncryptedSecretsV2Response{}, fmt.Errorf("CallGetSecretsV2: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return GetEncryptedSecretsV2Response{}, fmt.Errorf("CallGetSecretsV2: Unsuccessful response: [response=%s]", response) - } - - return secretsResponse, nil -} - func CallLogin1V2(httpClient *resty.Client, request GetLoginOneV2Request) (GetLoginOneV2Response, error) { var loginOneV2Response GetLoginOneV2Response response, err := httpClient. @@ -303,3 +224,110 @@ func CallGetNewAccessTokenWithRefreshToken(httpClient *resty.Client, refreshToke return newAccessToken, nil } + +func CallGetSecretsV3(httpClient *resty.Client, request GetEncryptedSecretsV3Request) (GetEncryptedSecretsV3Response, error) { + var secretsResponse GetEncryptedSecretsV3Response + + httpRequest := httpClient. + R(). + SetResult(&secretsResponse). + SetHeader("User-Agent", USER_AGENT). + SetQueryParam("environment", request.Environment). + SetQueryParam("workspaceId", request.WorkspaceId) + + if request.SecretPath != "" { + httpRequest.SetQueryParam("secretPath", request.SecretPath) + } + + response, err := httpRequest.Get(fmt.Sprintf("%v/v3/secrets", config.INFISICAL_URL)) + + if err != nil { + return GetEncryptedSecretsV3Response{}, fmt.Errorf("CallGetSecretsV3: Unable to complete api request [err=%s]", err) + } + + if response.IsError() { + return GetEncryptedSecretsV3Response{}, fmt.Errorf("CallGetSecretsV3: Unsuccessful response. Please make sure your secret path, workspace and environment name are all correct [response=%s]", response) + } + + return secretsResponse, nil +} + +func CallCreateSecretsV3(httpClient *resty.Client, request CreateSecretV3Request) error { + var secretsResponse GetEncryptedSecretsV3Response + response, err := httpClient. + R(). + SetResult(&secretsResponse). + SetHeader("User-Agent", USER_AGENT). + SetBody(request). + Post(fmt.Sprintf("%v/v3/secrets/%s", config.INFISICAL_URL, request.SecretName)) + + if err != nil { + return fmt.Errorf("CallCreateSecretsV3: Unable to complete api request [err=%s]", err) + } + + if response.IsError() { + return fmt.Errorf("CallCreateSecretsV3: Unsuccessful response. Please make sure your secret path, workspace and environment name are all correct [response=%s]", response) + } + + return nil +} + +func CallDeleteSecretsV3(httpClient *resty.Client, request DeleteSecretV3Request) error { + var secretsResponse GetEncryptedSecretsV3Response + response, err := httpClient. + R(). + SetResult(&secretsResponse). + SetHeader("User-Agent", USER_AGENT). + SetBody(request). + Delete(fmt.Sprintf("%v/v3/secrets/%s", config.INFISICAL_URL, request.SecretName)) + + if err != nil { + return fmt.Errorf("CallDeleteSecretsV3: Unable to complete api request [err=%s]", err) + } + + if response.IsError() { + return fmt.Errorf("CallDeleteSecretsV3: Unsuccessful response. Please make sure your secret path, workspace and environment name are all correct [response=%s]", response) + } + + return nil +} + +func CallUpdateSecretsV3(httpClient *resty.Client, request UpdateSecretByNameV3Request) error { + var secretsResponse GetEncryptedSecretsV3Response + response, err := httpClient. + R(). + SetResult(&secretsResponse). + SetHeader("User-Agent", USER_AGENT). + SetBody(request). + Patch(fmt.Sprintf("%v/v3/secrets/%s", config.INFISICAL_URL, request.SecretName)) + + if err != nil { + return fmt.Errorf("CallUpdateSecretsV3: Unable to complete api request [err=%s]", err) + } + + if response.IsError() { + return fmt.Errorf("CallUpdateSecretsV3: Unsuccessful response. Please make sure your secret path, workspace and environment name are all correct [response=%s]", response) + } + + return nil +} + +func CallGetSingleSecretByNameV3(httpClient *resty.Client, request CreateSecretV3Request) error { + var secretsResponse GetEncryptedSecretsV3Response + response, err := httpClient. + R(). + SetResult(&secretsResponse). + SetHeader("User-Agent", USER_AGENT). + SetBody(request). + Post(fmt.Sprintf("%v/v3/secrets/%s", config.INFISICAL_URL, request.SecretName)) + + if err != nil { + return fmt.Errorf("CallGetSingleSecretByNameV3: Unable to complete api request [err=%s]", err) + } + + if response.IsError() { + return fmt.Errorf("CallGetSingleSecretByNameV3: Unsuccessful response. Please make sure your secret path, workspace and environment name are all correct [response=%s]", response) + } + + return nil +} diff --git a/cli/packages/api/model.go b/cli/packages/api/model.go index 30896b819d..7de7a962b2 100644 --- a/cli/packages/api/model.go +++ b/cli/packages/api/model.go @@ -143,24 +143,7 @@ type Secret struct { SecretCommentHash string `json:"secretCommentHash,omitempty"` Type string `json:"type,omitempty"` ID string `json:"id,omitempty"` -} - -type BatchCreateSecretsByWorkspaceAndEnvRequest struct { - Environment string `json:"environment"` - WorkspaceId string `json:"workspaceId"` - Secrets []Secret `json:"secrets"` -} - -type BatchModifySecretsByWorkspaceAndEnvRequest struct { - Environment string `json:"environment"` - WorkspaceId string `json:"workspaceId"` - Secrets []Secret `json:"secrets"` -} - -type BatchDeleteSecretsBySecretIdsRequest struct { - EnvironmentName string `json:"environmentName"` - WorkspaceId string `json:"workspaceId"` - SecretIds []string `json:"secretIds"` + PlainTextKey string `json:"plainTextKey"` } type GetEncryptedWorkspaceKeyRequest struct { @@ -194,41 +177,6 @@ type GetSecretsByWorkspaceIdAndEnvironmentRequest struct { WorkspaceId string `json:"workspaceId"` } -type GetEncryptedSecretsV2Request struct { - Environment string `json:"environment"` - WorkspaceId string `json:"workspaceId"` - TagSlugs string `json:"tagSlugs"` -} - -type GetEncryptedSecretsV2Response struct { - Secrets []struct { - ID string `json:"_id"` - Version int `json:"version"` - Workspace string `json:"workspace"` - Type string `json:"type"` - Environment string `json:"environment"` - SecretKeyCiphertext string `json:"secretKeyCiphertext"` - SecretKeyIV string `json:"secretKeyIV"` - SecretKeyTag string `json:"secretKeyTag"` - SecretValueCiphertext string `json:"secretValueCiphertext"` - SecretValueIV string `json:"secretValueIV"` - SecretValueTag string `json:"secretValueTag"` - SecretCommentCiphertext string `json:"secretCommentCiphertext"` - SecretCommentIV string `json:"secretCommentIV"` - SecretCommentTag string `json:"secretCommentTag"` - V int `json:"__v"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - User string `json:"user,omitempty"` - Tags []struct { - ID string `json:"_id"` - Name string `json:"name"` - Slug string `json:"slug"` - Workspace string `json:"workspace"` - } `json:"tags"` - } `json:"secrets"` -} - type GetServiceTokenDetailsResponse struct { ID string `json:"_id"` Name string `json:"name"` @@ -320,3 +268,109 @@ type VerifyMfaTokenErrorResponse struct { type GetNewAccessTokenWithRefreshTokenResponse struct { Token string `json:"token"` } + +type GetEncryptedSecretsV3Request struct { + Environment string `json:"environment"` + WorkspaceId string `json:"workspaceId"` + SecretPath string `json:"secretPath"` +} + +type GetEncryptedSecretsV3Response struct { + Secrets []struct { + ID string `json:"_id"` + Version int `json:"version"` + Workspace string `json:"workspace"` + Type string `json:"type"` + Tags []struct { + ID string `json:"_id"` + Name string `json:"name"` + Slug string `json:"slug"` + Workspace string `json:"workspace"` + } `json:"tags"` + Environment string `json:"environment"` + SecretKeyCiphertext string `json:"secretKeyCiphertext"` + SecretKeyIV string `json:"secretKeyIV"` + SecretKeyTag string `json:"secretKeyTag"` + SecretValueCiphertext string `json:"secretValueCiphertext"` + SecretValueIV string `json:"secretValueIV"` + SecretValueTag string `json:"secretValueTag"` + SecretCommentCiphertext string `json:"secretCommentCiphertext"` + SecretCommentIV string `json:"secretCommentIV"` + SecretCommentTag string `json:"secretCommentTag"` + Algorithm string `json:"algorithm"` + KeyEncoding string `json:"keyEncoding"` + Folder string `json:"folder"` + V int `json:"__v"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + } `json:"secrets"` +} + +type CreateSecretV3Request struct { + SecretName string `json:"secretName"` + WorkspaceID string `json:"workspaceId"` + Type string `json:"type"` + Environment string `json:"environment"` + SecretKeyCiphertext string `json:"secretKeyCiphertext"` + SecretKeyIV string `json:"secretKeyIV"` + SecretKeyTag string `json:"secretKeyTag"` + SecretValueCiphertext string `json:"secretValueCiphertext"` + SecretValueIV string `json:"secretValueIV"` + SecretValueTag string `json:"secretValueTag"` + SecretCommentCiphertext string `json:"secretCommentCiphertext"` + SecretCommentIV string `json:"secretCommentIV"` + SecretCommentTag string `json:"secretCommentTag"` + SecretPath string `json:"secretPath"` +} + +type DeleteSecretV3Request struct { + SecretName string `json:"secretName"` + WorkspaceId string `json:"workspaceId"` + Environment string `json:"environment"` + Type string `json:"type"` + SecretPath string `json:"secretPath"` +} + +type UpdateSecretByNameV3Request struct { + SecretName string `json:"secretName"` + WorkspaceID string `json:"workspaceId"` + Environment string `json:"environment"` + Type string `json:"type"` + SecretPath string `json:"secretPath"` + SecretValueCiphertext string `json:"secretValueCiphertext"` + SecretValueIV string `json:"secretValueIV"` + SecretValueTag string `json:"secretValueTag"` +} + +type GetSingleSecretByNameV3Request struct { + SecretName string `json:"secretName"` + WorkspaceId string `json:"workspaceId"` + Environment string `json:"environment"` + Type string `json:"type"` + SecretPath string `json:"secretPath"` +} + +type GetSingleSecretByNameSecretResponse struct { + Secrets []struct { + ID string `json:"_id"` + Version int `json:"version"` + Workspace string `json:"workspace"` + Type string `json:"type"` + Environment string `json:"environment"` + SecretKeyCiphertext string `json:"secretKeyCiphertext"` + SecretKeyIV string `json:"secretKeyIV"` + SecretKeyTag string `json:"secretKeyTag"` + SecretValueCiphertext string `json:"secretValueCiphertext"` + SecretValueIV string `json:"secretValueIV"` + SecretValueTag string `json:"secretValueTag"` + SecretCommentCiphertext string `json:"secretCommentCiphertext"` + SecretCommentIV string `json:"secretCommentIV"` + SecretCommentTag string `json:"secretCommentTag"` + Algorithm string `json:"algorithm"` + KeyEncoding string `json:"keyEncoding"` + Folder string `json:"folder"` + V int `json:"__v"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + } `json:"secrets"` +} diff --git a/cli/packages/cmd/run.go b/cli/packages/cmd/run.go index afce399d5d..8b82faa65c 100644 --- a/cli/packages/cmd/run.go +++ b/cli/packages/cmd/run.go @@ -82,7 +82,12 @@ var runCmd = &cobra.Command{ util.HandleError(err, "Unable to parse flag") } - secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs}) + secretsPath, err := cmd.Flags().GetString("path") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath}) if err != nil { util.HandleError(err, "Could not fetch secrets", "If you are using a service token to fetch secrets, please ensure it is valid") @@ -184,6 +189,7 @@ func init() { runCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets") runCmd.Flags().StringP("command", "c", "", "chained commands to execute (e.g. \"npm install && npm run dev; echo ...\")") runCmd.Flags().StringP("tags", "t", "", "filter secrets by tag slugs ") + runCmd.Flags().String("path", "/", "get secrets within a folder path") } // Will execute a single command and pass in the given secrets into the process diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go index 26835eff1d..0d6181a565 100644 --- a/cli/packages/cmd/secrets.go +++ b/cli/packages/cmd/secrets.go @@ -44,6 +44,11 @@ var secretsCmd = &cobra.Command{ util.HandleError(err, "Unable to parse flag") } + secretsPath, err := cmd.Flags().GetString("path") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + shouldExpandSecrets, err := cmd.Flags().GetBool("expand") if err != nil { util.HandleError(err) @@ -54,7 +59,7 @@ var secretsCmd = &cobra.Command{ util.HandleError(err, "Unable to parse flag") } - secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs}) + secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath}) if err != nil { util.HandleError(err) } @@ -103,6 +108,11 @@ var secretsSetCmd = &cobra.Command{ } } + secretsPath, err := cmd.Flags().GetString("path") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + workspaceFile, err := util.GetWorkSpaceFromFile() if err != nil { util.HandleError(err, "Unable to get your local config details") @@ -140,7 +150,7 @@ var secretsSetCmd = &cobra.Command{ plainTextEncryptionKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey) // pull current secrets - secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName}) + secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, SecretsPath: secretsPath}) if err != nil { util.HandleError(err, "unable to retrieve secrets") } @@ -191,6 +201,8 @@ var secretsSetCmd = &cobra.Command{ SecretValueIV: base64.StdEncoding.EncodeToString(encryptedValue.Nonce), SecretValueTag: base64.StdEncoding.EncodeToString(encryptedValue.AuthTag), SecretValueHash: hashedValue, + PlainTextKey: key, + Type: existingSecret.Type, } // Only add to modifications if the value is different @@ -222,6 +234,7 @@ var secretsSetCmd = &cobra.Command{ SecretValueTag: base64.StdEncoding.EncodeToString(encryptedValue.AuthTag), SecretValueHash: hashedValue, Type: util.SECRET_TYPE_SHARED, + PlainTextKey: key, } secretsToCreate = append(secretsToCreate, encryptedSecretDetails) secretOperations = append(secretOperations, SecretSetOperation{ @@ -232,30 +245,43 @@ var secretsSetCmd = &cobra.Command{ } } - if len(secretsToCreate) > 0 { - batchCreateRequest := api.BatchCreateSecretsByWorkspaceAndEnvRequest{ - WorkspaceId: workspaceFile.WorkspaceId, - Environment: environmentName, - Secrets: secretsToCreate, + for _, secret := range secretsToCreate { + createSecretRequest := api.CreateSecretV3Request{ + WorkspaceID: workspaceFile.WorkspaceId, + Environment: environmentName, + SecretName: secret.PlainTextKey, + SecretKeyCiphertext: secret.SecretKeyCiphertext, + SecretKeyIV: secret.SecretKeyIV, + SecretKeyTag: secret.SecretKeyTag, + SecretValueCiphertext: secret.SecretValueCiphertext, + SecretValueIV: secret.SecretValueIV, + SecretValueTag: secret.SecretValueTag, + Type: secret.Type, + SecretPath: secretsPath, } - err = api.CallBatchCreateSecretsByWorkspaceAndEnv(httpClient, batchCreateRequest) + err = api.CallCreateSecretsV3(httpClient, createSecretRequest) if err != nil { util.HandleError(err, "Unable to process new secret creations") return } } - if len(secretsToModify) > 0 { - batchModifyRequest := api.BatchModifySecretsByWorkspaceAndEnvRequest{ - WorkspaceId: workspaceFile.WorkspaceId, - Environment: environmentName, - Secrets: secretsToModify, + for _, secret := range secretsToModify { + updateSecretRequest := api.UpdateSecretByNameV3Request{ + WorkspaceID: workspaceFile.WorkspaceId, + Environment: environmentName, + SecretName: secret.PlainTextKey, + SecretValueCiphertext: secret.SecretValueCiphertext, + SecretValueIV: secret.SecretValueIV, + SecretValueTag: secret.SecretValueTag, + Type: secret.Type, + SecretPath: secretsPath, } - err = api.CallBatchModifySecretsByWorkspaceAndEnv(httpClient, batchModifyRequest) + err = api.CallUpdateSecretsV3(httpClient, updateSecretRequest) if err != nil { - util.HandleError(err, "Unable to process the modifications to your secrets") + util.HandleError(err, "Unable to process secret update request") return } } @@ -288,6 +314,16 @@ var secretsDeleteCmd = &cobra.Command{ } } + secretsPath, err := cmd.Flags().GetString("path") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + secretType, err := cmd.Flags().GetString("type") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails() if err != nil { util.HandleError(err, "Unable to authenticate") @@ -298,46 +334,28 @@ var secretsDeleteCmd = &cobra.Command{ util.HandleError(err, "Unable to get local project details") } - secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName}) - if err != nil { - util.HandleError(err, "Unable to fetch secrets") - } - - secretByKey := getSecretsByKeys(secrets) - validSecretIdsToDelete := []string{} - invalidSecretNamesThatDoNotExist := []string{} - - for _, secretKeyFromArg := range args { - if value, ok := secretByKey[strings.ToUpper(secretKeyFromArg)]; ok { - validSecretIdsToDelete = append(validSecretIdsToDelete, value.ID) - } else { - invalidSecretNamesThatDoNotExist = append(invalidSecretNamesThatDoNotExist, secretKeyFromArg) + for _, secretName := range args { + request := api.DeleteSecretV3Request{ + WorkspaceId: workspaceFile.WorkspaceId, + Environment: environmentName, + SecretName: secretName, + Type: secretType, + SecretPath: secretsPath, } - } - if len(invalidSecretNamesThatDoNotExist) != 0 { - message := fmt.Sprintf("secret name(s) [%v] does not exist in your project. To see which secrets exist run [infisical secrets]", strings.Join(invalidSecretNamesThatDoNotExist, ", ")) - util.PrintErrorMessageAndExit(message) - } + httpClient := resty.New(). + SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken). + SetHeader("Accept", "application/json") - request := api.BatchDeleteSecretsBySecretIdsRequest{ - WorkspaceId: workspaceFile.WorkspaceId, - EnvironmentName: environmentName, - SecretIds: validSecretIdsToDelete, - } - - httpClient := resty.New(). - SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken). - SetHeader("Accept", "application/json") - - err = api.CallBatchDeleteSecretsByWorkspaceAndEnv(httpClient, request) - if err != nil { - util.HandleError(err, "Unable to complete your batch delete request") + err = api.CallDeleteSecretsV3(httpClient, request) + if err != nil { + util.HandleError(err, "Unable to complete your delete request") + } } fmt.Printf("secret name(s) [%v] have been deleted from your project \n", strings.Join(args, ", ")) - Telemetry.CaptureEvent("cli-command:secrets delete", posthog.NewProperties().Set("secretCount", len(secrets)).Set("version", util.CLI_VERSION)) + Telemetry.CaptureEvent("cli-command:secrets delete", posthog.NewProperties().Set("secretCount", len(args)).Set("version", util.CLI_VERSION)) }, } @@ -611,11 +629,15 @@ func init() { secretsCmd.AddCommand(secretsGetCmd) secretsCmd.AddCommand(secretsSetCmd) + secretsSetCmd.Flags().String("path", "/", "get secrets within a folder path") + secretsSetCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { util.RequireLogin() util.RequireLocalWorkspaceFile() } + secretsDeleteCmd.Flags().String("type", "personal", "the type of secret to delete: personal or shared (default: personal)") + secretsDeleteCmd.Flags().String("path", "/", "get secrets within a folder path") secretsCmd.AddCommand(secretsDeleteCmd) secretsDeleteCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { util.RequireLogin() @@ -626,5 +648,6 @@ func init() { secretsCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on") secretsCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets") secretsCmd.PersistentFlags().StringP("tags", "t", "", "filter secrets by tag slugs") + secretsCmd.Flags().String("path", "/", "get secrets within a folder path") rootCmd.AddCommand(secretsCmd) } diff --git a/cli/packages/models/cli.go b/cli/packages/models/cli.go index 22f72e2da2..18a64750fd 100644 --- a/cli/packages/models/cli.go +++ b/cli/packages/models/cli.go @@ -64,4 +64,5 @@ type GetAllSecretsParameters struct { InfisicalToken string TagSlugs string WorkspaceId string + SecretsPath string } diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index a713abddbb..eef691fdca 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -34,7 +34,7 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string) ([]models.Singl return nil, api.GetServiceTokenDetailsResponse{}, fmt.Errorf("unable to get service token details. [err=%v]", err) } - encryptedSecrets, err := api.CallGetSecretsV2(httpClient, api.GetEncryptedSecretsV2Request{ + encryptedSecrets, err := api.CallGetSecretsV3(httpClient, api.GetEncryptedSecretsV3Request{ WorkspaceId: serviceTokenDetails.Workspace, Environment: serviceTokenDetails.Environment, }) @@ -61,7 +61,7 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string) ([]models.Singl return plainTextSecrets, serviceTokenDetails, nil } -func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, workspaceId string, environmentName string, tagSlugs string) ([]models.SingleEnvironmentVariable, error) { +func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, workspaceId string, environmentName string, tagSlugs string, secretsPath string) ([]models.SingleEnvironmentVariable, error) { httpClient := resty.New() httpClient.SetAuthToken(JTWToken). SetHeader("Accept", "application/json") @@ -102,11 +102,17 @@ func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, work plainTextWorkspaceKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey) - encryptedSecrets, err := api.CallGetSecretsV2(httpClient, api.GetEncryptedSecretsV2Request{ + getSecretsRequest := api.GetEncryptedSecretsV3Request{ WorkspaceId: workspaceId, Environment: environmentName, - TagSlugs: tagSlugs, - }) + // TagSlugs: tagSlugs, + } + + if secretsPath != "" { + getSecretsRequest.SecretPath = secretsPath + } + + encryptedSecrets, err := api.CallGetSecretsV3(httpClient, getSecretsRequest) if err != nil { return nil, err @@ -162,7 +168,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models return nil, fmt.Errorf("unable to validate environment name because [err=%s]", err) } - secretsToReturn, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, params.Environment, params.TagSlugs) + secretsToReturn, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, params.Environment, params.TagSlugs, params.SecretsPath) log.Debug().Msgf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", errorToReturn) backupSecretsEncryptionKey := []byte(loggedInUserDetails.UserCredentials.PrivateKey)[0:32] @@ -333,7 +339,7 @@ func OverrideSecrets(secrets []models.SingleEnvironmentVariable, secretType stri return secretsToReturn } -func GetPlainTextSecrets(key []byte, encryptedSecrets api.GetEncryptedSecretsV2Response) ([]models.SingleEnvironmentVariable, error) { +func GetPlainTextSecrets(key []byte, encryptedSecrets api.GetEncryptedSecretsV3Response) ([]models.SingleEnvironmentVariable, error) { plainTextSecrets := []models.SingleEnvironmentVariable{} for _, secret := range encryptedSecrets.Secrets { // Decrypt key