Skip to content

Commit

Permalink
delete, get and create via cli
Browse files Browse the repository at this point in the history
  • Loading branch information
maidul98 committed Jan 2, 2023
1 parent f2bd4ae commit ac4b67d
Show file tree
Hide file tree
Showing 9 changed files with 385 additions and 14 deletions.
4 changes: 2 additions & 2 deletions backend/src/routes/v2/secret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ router.post(
}),
param('workspaceId').exists().isMongoId().trim(),
param('environmentName').exists().trim(),
body('secrets').exists().isArray().custom((value) => value.every((item: ISecret) => typeof item === 'object')),
body('secrets').exists().isArray().custom((value) => value.every((item: CreateSecretRequestBody) => typeof item === 'object')),
validateRequest,
async (req: Request, res: Response) => {
const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets;
Expand Down Expand Up @@ -94,7 +94,7 @@ router.delete(
requireAuth,
param('workspaceId').exists().isMongoId().trim(),
param('environmentName').exists().trim(),
body('secretIds').exists().isArray(),
body('secretIds').exists().isArray().custom(array => array.length > 0),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
acceptedStatuses: [COMPLETED, GRANTED]
Expand Down
2 changes: 1 addition & 1 deletion cli/packages/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func Execute() {
func init() {
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
rootCmd.PersistentFlags().BoolVarP(&debugLogging, "debug", "d", false, "Enable verbose logging")
rootCmd.PersistentFlags().StringVar(&util.INFISICAL_URL, "domain", "https://app.infisical.com/api", "Point the CLI to your own backend")
rootCmd.PersistentFlags().StringVar(&util.INFISICAL_URL, "domain", "http://localhost:8080/api", "Point the CLI to your own backend")
// rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
// }
}
173 changes: 166 additions & 7 deletions cli/packages/cmd/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ Copyright © 2022 NAME HERE <EMAIL ADDRESS>
package cmd

import (
"encoding/base64"
"fmt"
"strings"

"crypto/sha256"

"github.com/Infisical/infisical-merge/packages/http"
"github.com/Infisical/infisical-merge/packages/models"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/Infisical/infisical-merge/packages/visualize"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
Expand All @@ -21,14 +27,15 @@ var secretsCmd = &cobra.Command{
PreRun: toggleDebug,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {

secrets, err := util.GetAllEnvironmentVariables("", "dev")
secrets = util.SubstituteSecrets(secrets)
if err != nil {
log.Debugln(err)
return
}

visualize.PrintAllSecretDetails(secrets)

},
}

Expand All @@ -48,9 +55,95 @@ var secretsSetCmd = &cobra.Command{
Use: "set [secrets]",
DisableFlagsInUseLine: true,
PreRun: toggleDebug,
Args: cobra.NoArgs,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("set secret")
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
if err != nil {
log.Error(err)
return
}

if !loggedInUserDetails.IsUserLoggedIn {
log.Error("You are not logged in yet. Please run [infisical login] then try again")
return
}

if loggedInUserDetails.IsUserLoggedIn && loggedInUserDetails.LoginExpired {
log.Error("Your login has expired. Please run [infisical login] then try again")
return
}

httpClient := resty.New().
SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken).
SetHeader("Accept", "application/json")

request := models.GetEncryptedWorkspaceKeyRequest{
WorkspaceId: "63b0c1dbf2a30bdfddcfe1ac",
}

workspaceKeyResponse, err := http.CallGetEncryptedWorkspaceKey(httpClient, request)
if err != nil {
log.Errorf("unable to get your encrypted workspace key. [err=%v]", err)
return
}

encryptedWorkspaceKey, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.LatestKey.EncryptedKey)
encryptedWorkspaceKeySenderPublicKey, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.LatestKey.Sender.PublicKey)
encryptedWorkspaceKeyNonce, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.LatestKey.Nonce)
currentUsersPrivateKey, _ := base64.StdEncoding.DecodeString(loggedInUserDetails.UserCredentials.PrivateKey)

// decrypt workspace key
plainTextEncryptionKey := util.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
secretsToUpload := []models.Secret{}
for _, arg := range args {
splitKeyValueFromArg := strings.SplitN(arg, "=", 2)
if len(splitKeyValueFromArg) < 2 {
splitKeyValueFromArg[1] = ""
}

key := splitKeyValueFromArg[0]
value := splitKeyValueFromArg[1]

encryptedKey, err := util.EncryptSymmetric([]byte(key), []byte(plainTextEncryptionKey))
if err != nil {
log.Errorf("unable to encrypt your secrets [err=%v]", err)
}

hashedKey := fmt.Sprintf("%x", sha256.Sum256([]byte(key)))

encryptedValue, err := util.EncryptSymmetric([]byte(value), []byte(plainTextEncryptionKey))
if err != nil {
log.Errorf("unable to encrypt your secrets [err=%v]", err)
}

hashedValue := fmt.Sprintf("%x", sha256.Sum256([]byte(value)))

fullEncryptedSecret := models.Secret{
SecretKeyCiphertext: base64.StdEncoding.EncodeToString(encryptedKey.CipherText),
SecretKeyIV: base64.StdEncoding.EncodeToString(encryptedKey.Nonce),
SecretKeyTag: base64.StdEncoding.EncodeToString(encryptedKey.AuthTag),
SecretKeyHash: hashedKey,
SecretValueCiphertext: base64.StdEncoding.EncodeToString(encryptedValue.CipherText),
SecretValueIV: base64.StdEncoding.EncodeToString(encryptedValue.Nonce),
SecretValueTag: base64.StdEncoding.EncodeToString(encryptedValue.AuthTag),
SecretValueHash: hashedValue,
Type: "shared",
}
secretsToUpload = append(secretsToUpload, fullEncryptedSecret)
}

batchCreateRequest := models.BatchCreateSecretsByWorkspaceAndEnvRequest{
WorkspaceId: "63b0c1dbf2a30bdfddcfe1ac",
EnvironmentName: "dev",
Secrets: secretsToUpload,
}
err = http.CallBatchCreateSecretsByWorkspaceAndEnv(httpClient, batchCreateRequest)
if err != nil {
log.Errorf("Unable to complete your request because %v", err)
return
}

log.Infof("secret name(s) [%v] have been created", strings.Join(args, ", "))
},
}

Expand All @@ -60,9 +153,65 @@ var secretsDeleteCmd = &cobra.Command{
Use: "delete [secrets]",
DisableFlagsInUseLine: true,
PreRun: toggleDebug,
Args: cobra.NoArgs,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Delete secret")
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
if err != nil {
log.Error(err)
return
}

if !loggedInUserDetails.IsUserLoggedIn {
log.Error("You are not logged in yet. Please run [infisical login] then try again")
return
}

if loggedInUserDetails.IsUserLoggedIn && loggedInUserDetails.LoginExpired {
log.Error("Your login has expired. Please run [infisical login] then try again")
return
}

secrets, err := util.GetAllEnvironmentVariables("", "dev")
if err != nil {
log.Error("Unable to retrieve secrets. Run with -d to see full logs")
log.Debug(err)
}

secretByKey := getSecretsByKeys(secrets)
validSecretIdsToDelete := []string{}
invalidSecretNamesThatDoNotExist := []string{}

for _, secretKeyFromArg := range args {
if value, ok := secretByKey[secretKeyFromArg]; ok {
validSecretIdsToDelete = append(validSecretIdsToDelete, value.ID)
} else {
invalidSecretNamesThatDoNotExist = append(invalidSecretNamesThatDoNotExist, secretKeyFromArg)
}
}

if len(invalidSecretNamesThatDoNotExist) != 0 {
log.Errorf("secret name(s) [%v] does not exist in your project. Please remove and re-run the command", strings.Join(invalidSecretNamesThatDoNotExist, ", "))
return
}

request := models.BatchDeleteSecretsBySecretIdsRequest{
WorkspaceId: "63b0c1dbf2a30bdfddcfe1ac",
EnvironmentName: "dev",
SecretIds: validSecretIdsToDelete,
}

httpClient := resty.New().
SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken).
SetHeader("Accept", "application/json")

err = http.CallBatchDeleteSecretsByWorkspaceAndEnv(httpClient, request)
if err != nil {
log.Errorf("Unable to complete your request because %v", err)
return
}

log.Infof("secret name(s) [%v] have been deleted from your project", strings.Join(args, ", "))

},
}

Expand Down Expand Up @@ -93,11 +242,21 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
} else {
requestedSecrets = append(requestedSecrets, models.SingleEnvironmentVariable{
Key: secretKeyFromArg,
Type: "NOT FOUND",
Value: "NOT FOUND",
Type: "*not found*",
Value: "*not found*",
})
}
}

visualize.PrintAllSecretDetails(requestedSecrets)
}

func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]models.SingleEnvironmentVariable {
secretMapByName := make(map[string]models.SingleEnvironmentVariable)

for _, secret := range secrets {
secretMapByName[secret.Key] = secret
}

return secretMapByName
}
82 changes: 82 additions & 0 deletions cli/packages/http/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package http

import (
"fmt"

"github.com/Infisical/infisical-merge/packages/models"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/go-resty/resty/v2"
)

func CallBatchModifySecretsByWorkspaceAndEnv(httpClient *resty.Client, request models.BatchModifySecretsByWorkspaceAndEnvRequest) error {
endpoint := fmt.Sprintf("%v/v2/secret/batch-modify/workspace/%v/environment/%v", util.INFISICAL_URL, request.WorkspaceId, request.EnvironmentName)
response, err := httpClient.
R().
SetBody(request).
Patch(endpoint)

if err != nil {
return fmt.Errorf("CallBatchModifySecretsByWorkspaceAndEnv: Unable to complete api request [err=%s]", err)
}

if response.StatusCode() > 299 {
return fmt.Errorf("CallBatchModifySecretsByWorkspaceAndEnv: Unsuccessful response: [response=%s]", response)
}

return nil
}

func CallBatchCreateSecretsByWorkspaceAndEnv(httpClient *resty.Client, request models.BatchCreateSecretsByWorkspaceAndEnvRequest) error {
endpoint := fmt.Sprintf("%v/v2/secret/batch-create/workspace/%v/environment/%v", util.INFISICAL_URL, request.WorkspaceId, request.EnvironmentName)
response, err := httpClient.
R().
SetBody(request).
Post(endpoint)

if err != nil {
return fmt.Errorf("CallBatchCreateSecretsByWorkspaceAndEnv: Unable to complete api request [err=%s]", err)
}

if response.StatusCode() > 299 {
return fmt.Errorf("CallBatchCreateSecretsByWorkspaceAndEnv: Unsuccessful response: [response=%s]", response)
}

return nil
}

func CallBatchDeleteSecretsByWorkspaceAndEnv(httpClient *resty.Client, request models.BatchDeleteSecretsBySecretIdsRequest) error {
endpoint := fmt.Sprintf("%v/v2/secret/batch/workspace/%v/environment/%v", util.INFISICAL_URL, request.WorkspaceId, request.EnvironmentName)
response, err := httpClient.
R().
SetBody(request).
Delete(endpoint)

if err != nil {
return fmt.Errorf("CallBatchDeleteSecretsByWorkspaceAndEnv: Unable to complete api request [err=%s]", err)
}

if response.StatusCode() > 299 {
return fmt.Errorf("CallBatchDeleteSecretsByWorkspaceAndEnv: Unsuccessful response: [response=%s]", response)
}

return nil
}

func CallGetEncryptedWorkspaceKey(httpClient *resty.Client, request models.GetEncryptedWorkspaceKeyRequest) (models.GetEncryptedWorkspaceKeyResponse, error) {
endpoint := fmt.Sprintf("%v/v1/key/%v/latest", util.INFISICAL_URL, request.WorkspaceId)
var result models.GetEncryptedWorkspaceKeyResponse
response, err := httpClient.
R().
SetResult(&result).
Get(endpoint)

if err != nil {
return models.GetEncryptedWorkspaceKeyResponse{}, fmt.Errorf("CallGetEncryptedWorkspaceKey: Unable to complete api request [err=%s]", err)
}

if response.StatusCode() > 299 {
return models.GetEncryptedWorkspaceKeyResponse{}, fmt.Errorf("CallGetEncryptedWorkspaceKey: Unsuccessful response: [response=%s]", response)
}

return result, nil
}
Loading

0 comments on commit ac4b67d

Please sign in to comment.