diff --git a/cli/packages/cmd/init.go b/cli/packages/cmd/init.go index 55abe6d4f..2789f220d 100644 --- a/cli/packages/cmd/init.go +++ b/cli/packages/cmd/init.go @@ -36,7 +36,7 @@ var initCmd = &cobra.Command{ return } - if util.WorkspaceConfigFileExists() { + if util.WorkspaceConfigFileExistsInCurrentPath() { shouldOverride, err := shouldOverrideWorkspacePrompt() if err != nil { log.Errorln("Unable to parse your answer") diff --git a/cli/packages/cmd/run.go b/cli/packages/cmd/run.go index 75365dead..ec5210b1f 100644 --- a/cli/packages/cmd/run.go +++ b/cli/packages/cmd/run.go @@ -47,18 +47,17 @@ var runCmd = &cobra.Command{ return } - envsFromApi, err := util.GetAllEnvironmentVariables(projectId, envName) + secrets, err := util.GetAllEnvironmentVariables(projectId, envName) if err != nil { - log.Errorln("Something went wrong when pulling secrets using your Infisical token. Double check the token, project id or environment name (dev, prod, ect.)") log.Debugln(err) return } if shouldExpandSecrets { - substitutions := util.SubstituteSecrets(envsFromApi) - execCmd(args[0], args[1:], substitutions) + secretsWithSubstitutions := util.SubstituteSecrets(secrets) + execCmd(args[0], args[1:], secretsWithSubstitutions) } else { - execCmd(args[0], args[1:], envsFromApi) + execCmd(args[0], args[1:], secrets) } }, @@ -73,9 +72,12 @@ func init() { // Credit: inspired by AWS Valut func execCmd(command string, args []string, envs []models.SingleEnvironmentVariable) error { - log.Infof("\x1b[%dm%s\x1b[0m", 32, "\u2713 Injected Infisical secrets into your application process successfully") - log.Debugln("Secrets to inject:", envs) + numberOfSecretsInjected := fmt.Sprintf("\u2713 Injected %v Infisical secrets into your application process successfully", len(envs)) + + log.Infof("\x1b[%dm%s\x1b[0m", 32, numberOfSecretsInjected) log.Debugf("executing command: %s %s \n", command, strings.Join(args, " ")) + log.Debugln("Secrets injected:", envs) + cmd := exec.Command(command, args...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout diff --git a/cli/packages/util/config.go b/cli/packages/util/config.go index c42f26fb5..0ee04b013 100644 --- a/cli/packages/util/config.go +++ b/cli/packages/util/config.go @@ -56,7 +56,7 @@ func ConfigFileExists() bool { } } -func WorkspaceConfigFileExists() bool { +func WorkspaceConfigFileExistsInCurrentPath() bool { if _, err := os.Stat(INFISICAL_WORKSPACE_CONFIG_FILE_NAME); err == nil { return true } else { @@ -90,3 +90,65 @@ func GetFullConfigFilePath() (fullPathToFile string, fullPathToDirectory string, fullDirPath := fmt.Sprintf("%s/%s", homeDir, CONFIG_FOLDER_NAME) return fullPath, fullDirPath, err } + +// Given a path to a workspace config, unmarshal workspace config +func GetWorkspaceConfigByPath(path string) (workspaceConfig models.WorkspaceConfigFile, err error) { + workspaceConfigFileAsBytes, err := os.ReadFile(path) + if err != nil { + return models.WorkspaceConfigFile{}, fmt.Errorf("GetWorkspaceConfigByPath: Unable to read workspace config file because [%s]", err) + } + + var workspaceConfigFile models.WorkspaceConfigFile + err = json.Unmarshal(workspaceConfigFileAsBytes, &workspaceConfigFile) + if err != nil { + return models.WorkspaceConfigFile{}, fmt.Errorf("GetWorkspaceConfigByPath: Unable to unmarshal workspace config file because [%s]", err) + } + + return workspaceConfigFile, nil +} + +// Will get the list of .infisical.json files that are located +// within the root of each sub folder from where the CLI is ran from +func GetAllWorkSpaceConfigsStartingFromCurrentPath() (workspaces []models.WorkspaceConfigFile, err error) { + currentDir, err := os.Getwd() + if err != nil { + return nil, fmt.Errorf("GetAllProjectConfigs: unable to get the current directory because [%s]", err) + } + + files, err := os.ReadDir(currentDir) + if err != nil { + return nil, fmt.Errorf("GetAllProjectConfigs: unable to read the contents of the current directory because [%s]", err) + } + + listOfWorkSpaceConfigs := []models.WorkspaceConfigFile{} + for _, file := range files { + if !file.IsDir() && file.Name() == INFISICAL_WORKSPACE_CONFIG_FILE_NAME { + pathToWorkspaceConfigFile := currentDir + "/" + INFISICAL_WORKSPACE_CONFIG_FILE_NAME + + workspaceConfig, err := GetWorkspaceConfigByPath(pathToWorkspaceConfigFile) + if err != nil { + return nil, fmt.Errorf("GetAllProjectConfigs: Unable to get config file because [%s]", err) + } + + listOfWorkSpaceConfigs = append(listOfWorkSpaceConfigs, workspaceConfig) + + } else if file.IsDir() { + pathToSubFolder := currentDir + "/" + file.Name() + pathToMaybeWorkspaceConfigFile := pathToSubFolder + "/" + INFISICAL_WORKSPACE_CONFIG_FILE_NAME + + _, err := os.Stat(pathToMaybeWorkspaceConfigFile) + if err != nil { + continue // workspace config file doesn't exist + } + + workspaceConfig, err := GetWorkspaceConfigByPath(pathToMaybeWorkspaceConfigFile) + if err != nil { + return nil, fmt.Errorf("GetAllProjectConfigs: Unable to get config file because [%s]", err) + } + + listOfWorkSpaceConfigs = append(listOfWorkSpaceConfigs, workspaceConfig) + } + } + + return listOfWorkSpaceConfigs, nil +} diff --git a/cli/packages/util/crypto.go b/cli/packages/util/crypto.go index b308ea93d..c6eee2d0c 100644 --- a/cli/packages/util/crypto.go +++ b/cli/packages/util/crypto.go @@ -3,12 +3,9 @@ package util import ( "crypto/aes" "crypto/cipher" - - log "github.com/sirupsen/logrus" ) func DecryptSymmetric(key []byte, encryptedPrivateKey []byte, tag []byte, IV []byte) ([]byte, error) { - log.Debugln("Key:", key, "encryptedPrivateKey", encryptedPrivateKey, "tag", tag, "IV", IV) block, err := aes.NewCipher(key) if err != nil { return nil, err diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index de5095d75..d7d4cdc37 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -14,19 +14,7 @@ import ( "golang.org/x/crypto/nacl/box" ) -func GetSecretsFromAPIUsingCurrentLoggedInUser(envName string, userCreds models.UserCredentials) ([]models.SingleEnvironmentVariable, error) { - log.Debugln("envName", envName, "userCreds", userCreds) - // check if user has configured a workspace - workspace, err := GetWorkSpaceFromFile() - if err != nil { - return nil, fmt.Errorf("Unable to read workspace file:", err) - } - - // create http client - httpClient := resty.New(). - SetAuthToken(userCreds.JTWToken). - SetHeader("Accept", "application/json") - +func getSecretsByWorkspaceIdAndEnvName(httpClient resty.Client, envName string, workspace models.WorkspaceConfigFile, userCreds models.UserCredentials) (listOfSecrets []models.SingleEnvironmentVariable, err error) { var pullSecretsRequestResponse models.PullSecretsResponse response, err := httpClient. R(). @@ -35,14 +23,11 @@ func GetSecretsFromAPIUsingCurrentLoggedInUser(envName string, userCreds models. SetResult(&pullSecretsRequestResponse). Get(fmt.Sprintf("%v/v1/secret/%v", INFISICAL_URL, workspace.WorkspaceId)) // need to change workspace id - log.Debugln("Response from get secrets:", response) - if err != nil { return nil, err } if response.StatusCode() > 299 { - log.Debugln(response) return nil, fmt.Errorf(response.Status()) } @@ -67,7 +52,7 @@ func GetSecretsFromAPIUsingCurrentLoggedInUser(envName string, userCreds models. return nil, err } - log.Debugln("workspaceKey", workspaceKey, "nonce", nonce, "senderPublicKey", senderPublicKey, "currentUsersPrivateKey", currentUsersPrivateKey) + // log.Debugln("workspaceKey", workspaceKey, "nonce", nonce, "senderPublicKey", senderPublicKey, "currentUsersPrivateKey", currentUsersPrivateKey) workspaceKeyInBytes, _ := box.Open(nil, workspaceKey, (*[24]byte)(nonce), (*[32]byte)(senderPublicKey), (*[32]byte)(currentUsersPrivateKey)) var listOfEnv []models.SingleEnvironmentVariable @@ -101,6 +86,32 @@ func GetSecretsFromAPIUsingCurrentLoggedInUser(envName string, userCreds models. return listOfEnv, nil } +func GetSecretsFromAPIUsingCurrentLoggedInUser(envName string, userCreds models.UserCredentials) ([]models.SingleEnvironmentVariable, error) { + log.Debugln("GetSecretsFromAPIUsingCurrentLoggedInUser", "envName", envName, "userCreds", userCreds) + // check if user has configured a workspace + workspaces, err := GetAllWorkSpaceConfigsStartingFromCurrentPath() + if err != nil { + return nil, fmt.Errorf("Unable to read workspace file(s):", err) + } + + // create http client + httpClient := resty.New(). + SetAuthToken(userCreds.JTWToken). + SetHeader("Accept", "application/json") + + secrets := []models.SingleEnvironmentVariable{} + for _, workspace := range workspaces { + secretsFromAPI, err := getSecretsByWorkspaceIdAndEnvName(*httpClient, envName, workspace, userCreds) + if err != nil { + return nil, fmt.Errorf("GetSecretsFromAPIUsingCurrentLoggedInUser: Unable to get secrets by workspace id and env name") + } + + secrets = append(secrets, secretsFromAPI...) + } + + return secrets, nil +} + func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, envName string, projectId string) ([]models.SingleEnvironmentVariable, error) { if infisicalToken == "" || projectId == "" || envName == "" { return nil, errors.New("infisical token, project id and or environment name cannot be empty") @@ -127,7 +138,6 @@ func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, envName string, } if response.StatusCode() > 299 { - log.Debugln(response) return nil, fmt.Errorf(response.Status()) } @@ -188,6 +198,7 @@ func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, envName string, func GetAllEnvironmentVariables(projectId string, envName string) ([]models.SingleEnvironmentVariable, error) { var envsFromApi []models.SingleEnvironmentVariable infisicalToken := os.Getenv(INFISICAL_TOKEN_NAME) + if infisicalToken == "" { hasUserLoggedInbefore, loggedInUserEmail, err := IsUserLoggedIn() if err != nil { @@ -208,8 +219,13 @@ func GetAllEnvironmentVariables(projectId string, envName string) ([]models.Sing return envsFromApi, err } - if !WorkspaceConfigFileExists() { - log.Infoln("Your project is not connected to a project yet. Run command [infisical init]") + workspaceConfigs, err := GetAllWorkSpaceConfigsStartingFromCurrentPath() + if err != nil { + return nil, fmt.Errorf("unable to check if you have a %s file in your current directory", INFISICAL_WORKSPACE_CONFIG_FILE_NAME) + } + + if len(workspaceConfigs) == 0 { + log.Infoln("Your local project is not connected to a Infisical project yet. Run command [infisical init]") return envsFromApi, fmt.Errorf("project not initialized") }