mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 16:52:38 +00:00
swap out keyring package
This commit is contained in:
@@ -52,7 +52,6 @@ func WriteInitalConfig(userCredentials *models.UserCredentials) error {
|
||||
configFile := models.ConfigFile{
|
||||
LoggedInUserEmail: userCredentials.Email,
|
||||
LoggedInUserDomain: config.INFISICAL_URL,
|
||||
VaultBackendType: existingConfigFile.VaultBackendType,
|
||||
LoggedInUsers: existingConfigFile.LoggedInUsers,
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,16 @@ package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/99designs/keyring"
|
||||
keyringwrapper "github.com/Infisical/infisical-merge/internal"
|
||||
"github.com/Infisical/infisical-merge/packages/api"
|
||||
"github.com/Infisical/infisical-merge/packages/config"
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/zalando/go-keyring"
|
||||
)
|
||||
|
||||
type LoggedInUserDetails struct {
|
||||
@@ -24,17 +26,7 @@ func StoreUserCredsInKeyRing(userCred *models.UserCredentials) error {
|
||||
return fmt.Errorf("StoreUserCredsInKeyRing: something went wrong when marshalling user creds [err=%s]", err)
|
||||
}
|
||||
|
||||
// Get keyring
|
||||
configuredKeyring, err := GetKeyRing()
|
||||
if err != nil {
|
||||
return fmt.Errorf("StoreUserCredsInKeyRing: unable to get keyring instance with [err=%s]", err)
|
||||
}
|
||||
|
||||
err = configuredKeyring.Set(keyring.Item{
|
||||
Key: userCred.Email,
|
||||
Data: []byte(string(userCredMarshalled)),
|
||||
})
|
||||
|
||||
err = keyringwrapper.Set(userCred.Email, string(userCredMarshalled))
|
||||
if err != nil {
|
||||
return fmt.Errorf("StoreUserCredsInKeyRing: unable to store user credentials because [err=%s]", err)
|
||||
}
|
||||
@@ -43,20 +35,20 @@ func StoreUserCredsInKeyRing(userCred *models.UserCredentials) error {
|
||||
}
|
||||
|
||||
func GetUserCredsFromKeyRing(userEmail string) (credentials models.UserCredentials, err error) {
|
||||
// Get keyring
|
||||
configuredKeyring, err := GetKeyRing()
|
||||
credentialsValue, err := keyringwrapper.Get(userEmail)
|
||||
if err != nil {
|
||||
return models.UserCredentials{}, fmt.Errorf("GetUserCredsFromKeyRing: unable to get keyring instance with [err=%s]", err)
|
||||
}
|
||||
|
||||
credentialsValue, err := configuredKeyring.Get(userEmail)
|
||||
if err != nil {
|
||||
return models.UserCredentials{}, fmt.Errorf("GetUserCredsFromKeyRing: unable to get key from Keyring. could not find login credentials in your Keyring. This is common if you have switched vault backend recently. If so, please login in again and retry [err=%s]", err)
|
||||
if err == keyring.ErrUnsupportedPlatform {
|
||||
return models.UserCredentials{}, errors.New("your OS does not support keyring. Consider using a service token https://infisical.com/docs/documentation/platform/token")
|
||||
} else if err == keyring.ErrNotFound {
|
||||
return models.UserCredentials{}, errors.New("credentials not found in system keyring")
|
||||
} else {
|
||||
return models.UserCredentials{}, fmt.Errorf("something went wrong, failed to retrieve value from system keyring [error=%v]", err)
|
||||
}
|
||||
}
|
||||
|
||||
var userCredentials models.UserCredentials
|
||||
|
||||
err = json.Unmarshal([]byte(credentialsValue.Data), &userCredentials)
|
||||
err = json.Unmarshal([]byte(credentialsValue), &userCredentials)
|
||||
if err != nil {
|
||||
return models.UserCredentials{}, fmt.Errorf("getUserCredsFromKeyRing: Something went wrong when unmarshalling user creds [err=%s]", err)
|
||||
}
|
||||
@@ -81,7 +73,11 @@ func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) {
|
||||
|
||||
userCreds, err := GetUserCredsFromKeyRing(configFile.LoggedInUserEmail)
|
||||
if err != nil {
|
||||
return LoggedInUserDetails{}, fmt.Errorf("getCurrentLoggedInUserDetails: unable to your credentials from Keyring [err=%s]", err)
|
||||
if strings.Contains(err.Error(), "credentials not found in system keyring") {
|
||||
return LoggedInUserDetails{}, errors.New("we couldn't find your logged in details, try running [infisical login] then try again")
|
||||
} else {
|
||||
return LoggedInUserDetails{}, fmt.Errorf("failed to fetch creditnals from keyring because [err=%s]", err)
|
||||
}
|
||||
}
|
||||
|
||||
// check to to see if the JWT is still valid
|
||||
@@ -97,19 +93,19 @@ func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) {
|
||||
}
|
||||
|
||||
isAuthenticated := api.CallIsAuthenticated(httpClient)
|
||||
// TODO: add refresh token
|
||||
// if !isAuthenticated {
|
||||
// accessTokenResponse, err := api.CallGetNewAccessTokenWithRefreshToken(httpClient, userCreds.RefreshToken)
|
||||
// if err == nil && accessTokenResponse.Token != "" {
|
||||
// isAuthenticated = true
|
||||
// userCreds.JTWToken = accessTokenResponse.Token
|
||||
// }
|
||||
// }
|
||||
|
||||
if !isAuthenticated {
|
||||
accessTokenResponse, _ := api.CallGetNewAccessTokenWithRefreshToken(httpClient, userCreds.RefreshToken)
|
||||
if accessTokenResponse.Token != "" {
|
||||
isAuthenticated = true
|
||||
userCreds.JTWToken = accessTokenResponse.Token
|
||||
}
|
||||
}
|
||||
|
||||
err = StoreUserCredsInKeyRing(&userCreds)
|
||||
if err != nil {
|
||||
log.Debug().Msg("unable to store your user credentials with new access token")
|
||||
}
|
||||
// err = StoreUserCredsInKeyRing(&userCreds)
|
||||
// if err != nil {
|
||||
// log.Debug().Msg("unable to store your user credentials with new access token")
|
||||
// }
|
||||
|
||||
if !isAuthenticated {
|
||||
return LoggedInUserDetails{
|
||||
|
||||
@@ -45,5 +45,5 @@ func PrintErrorMessageAndExit(messages ...string) {
|
||||
}
|
||||
|
||||
func printError(e error) {
|
||||
color.New(color.FgRed).Fprintf(os.Stderr, "Hmm, we ran into an error: %v\n", e)
|
||||
color.New(color.FgRed).Fprintf(os.Stderr, "error: %v\n", e)
|
||||
}
|
||||
|
||||
@@ -207,6 +207,10 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if loggedInUserDetails.LoginExpired {
|
||||
PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||
}
|
||||
|
||||
workspaceFile, err := GetWorkSpaceFromFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
)
|
||||
|
||||
// References to self should return the value unaltered
|
||||
func Test_SubstituteSecrets_When_ReferenceToSelf(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
Key string
|
||||
Value string
|
||||
ExpectedValue string
|
||||
}{
|
||||
{Key: "A", Value: "${A}", ExpectedValue: "${A}"},
|
||||
{Key: "A", Value: "${A} ${A}", ExpectedValue: "${A} ${A}"},
|
||||
{Key: "A", Value: "${A}${A}", ExpectedValue: "${A}${A}"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
secret := models.SingleEnvironmentVariable{
|
||||
Key: test.Key,
|
||||
Value: test.Value,
|
||||
}
|
||||
|
||||
secrets := []models.SingleEnvironmentVariable{secret}
|
||||
result := SubstituteSecrets(secrets)
|
||||
|
||||
if result[0].Value != test.ExpectedValue {
|
||||
t.Errorf("Test_SubstituteSecrets_When_ReferenceToSelf: expected %s but got %s for input %s", test.ExpectedValue, result[0].Value, test.Value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SubstituteSecrets_When_ReferenceDoesNotExist(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
Key string
|
||||
Value string
|
||||
ExpectedValue string
|
||||
}{
|
||||
{Key: "A", Value: "${X}", ExpectedValue: "${X}"},
|
||||
{Key: "A", Value: "${H}HELLO", ExpectedValue: "${H}HELLO"},
|
||||
{Key: "A", Value: "${L}${S}", ExpectedValue: "${L}${S}"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
secret := models.SingleEnvironmentVariable{
|
||||
Key: test.Key,
|
||||
Value: test.Value,
|
||||
}
|
||||
|
||||
secrets := []models.SingleEnvironmentVariable{secret}
|
||||
result := SubstituteSecrets(secrets)
|
||||
|
||||
if result[0].Value != test.ExpectedValue {
|
||||
t.Errorf("Test_SubstituteSecrets_When_ReferenceToSelf: expected %s but got %s for input %s", test.ExpectedValue, result[0].Value, test.Value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SubstituteSecrets_When_ReferenceDoesNotExist_And_Self_Referencing(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
Key string
|
||||
Value string
|
||||
ExpectedValue string
|
||||
}{
|
||||
{
|
||||
Key: "O",
|
||||
Value: "${P} ==$$ ${X} ${UNKNOWN} ${A}",
|
||||
ExpectedValue: "DOMAIN === ${A} DOMAIN >>> ==$$ DOMAIN ${UNKNOWN} ${A}",
|
||||
},
|
||||
{
|
||||
Key: "X",
|
||||
Value: "DOMAIN",
|
||||
ExpectedValue: "DOMAIN",
|
||||
},
|
||||
{
|
||||
Key: "A",
|
||||
Value: "*${A}* ${X}",
|
||||
ExpectedValue: "*${A}* DOMAIN",
|
||||
},
|
||||
{
|
||||
Key: "H",
|
||||
Value: "${X} >>>",
|
||||
ExpectedValue: "DOMAIN >>>",
|
||||
},
|
||||
{
|
||||
Key: "P",
|
||||
Value: "DOMAIN === ${A} ${H}",
|
||||
ExpectedValue: "DOMAIN === ${A} DOMAIN >>>",
|
||||
},
|
||||
{
|
||||
Key: "T",
|
||||
Value: "${P} ==$$ ${X} ${UNKNOWN} ${A} ${P} ==$$ ${X} ${UNKNOWN} ${A}",
|
||||
ExpectedValue: "DOMAIN === ${A} DOMAIN >>> ==$$ DOMAIN ${UNKNOWN} ${A} DOMAIN === ${A} DOMAIN >>> ==$$ DOMAIN ${UNKNOWN} ${A}",
|
||||
},
|
||||
{
|
||||
Key: "S",
|
||||
Value: "${ SSS$$ ${HEY}",
|
||||
ExpectedValue: "${ SSS$$ ${HEY}",
|
||||
},
|
||||
}
|
||||
|
||||
secrets := []models.SingleEnvironmentVariable{}
|
||||
for _, test := range tests {
|
||||
secrets = append(secrets, models.SingleEnvironmentVariable{Key: test.Key, Value: test.Value})
|
||||
}
|
||||
|
||||
results := SubstituteSecrets(secrets)
|
||||
|
||||
for index, expanded := range results {
|
||||
if expanded.Value != tests[index].ExpectedValue {
|
||||
t.Errorf("Test_SubstituteSecrets_When_ReferenceToSelf: expected [%s] but got [%s] for input [%s]", tests[index].ExpectedValue, expanded.Value, tests[index].Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SubstituteSecrets_When_No_SubstituteNeeded(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
Key string
|
||||
Value string
|
||||
ExpectedValue string
|
||||
}{
|
||||
{
|
||||
Key: "DOMAIN",
|
||||
Value: "infisical.com",
|
||||
ExpectedValue: "infisical.com",
|
||||
},
|
||||
{
|
||||
Key: "API_KEY",
|
||||
Value: "hdgsvjshcgkdckhevdkd",
|
||||
ExpectedValue: "hdgsvjshcgkdckhevdkd",
|
||||
},
|
||||
{
|
||||
Key: "ENV",
|
||||
Value: "PROD",
|
||||
ExpectedValue: "PROD",
|
||||
},
|
||||
}
|
||||
|
||||
secrets := []models.SingleEnvironmentVariable{}
|
||||
for _, test := range tests {
|
||||
secrets = append(secrets, models.SingleEnvironmentVariable{Key: test.Key, Value: test.Value})
|
||||
}
|
||||
|
||||
results := SubstituteSecrets(secrets)
|
||||
|
||||
for index, expanded := range results {
|
||||
if expanded.Value != tests[index].ExpectedValue {
|
||||
t.Errorf("Test_SubstituteSecrets_When_ReferenceToSelf: expected [%s] but got [%s] for input [%s]", tests[index].ExpectedValue, expanded.Value, tests[index].Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Read_Env_From_File(t *testing.T) {
|
||||
type testCase struct {
|
||||
TestFile string
|
||||
ExpectedEnv string
|
||||
}
|
||||
|
||||
var cases = []testCase{
|
||||
{
|
||||
TestFile: "testdata/infisical-default-env.json",
|
||||
ExpectedEnv: "myDefaultEnv",
|
||||
},
|
||||
{
|
||||
TestFile: "testdata/infisical-branch-env.json",
|
||||
ExpectedEnv: "myMainEnv",
|
||||
},
|
||||
{
|
||||
TestFile: "testdata/infisical-no-matching-branch-env.json",
|
||||
ExpectedEnv: "myDefaultEnv",
|
||||
},
|
||||
}
|
||||
|
||||
// create a tmp directory for testing
|
||||
testDir, err := os.MkdirTemp(os.TempDir(), "infisical-test")
|
||||
if err != nil {
|
||||
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to create temp directory: %s", err)
|
||||
}
|
||||
|
||||
// safe the current working directory
|
||||
originalDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to get current working directory: %s", err)
|
||||
}
|
||||
|
||||
// backup the original git command
|
||||
originalGitCmd := getCurrentBranchCmd
|
||||
|
||||
// make sure to clean up after the test
|
||||
t.Cleanup(func() {
|
||||
os.Chdir(originalDir)
|
||||
os.RemoveAll(testDir)
|
||||
getCurrentBranchCmd = originalGitCmd
|
||||
})
|
||||
|
||||
// mock the git command to return "main" as the current branch
|
||||
getCurrentBranchCmd = execCmd{cmd: "echo", args: []string{"main"}}
|
||||
|
||||
for _, c := range cases {
|
||||
// make sure we start in the original directory
|
||||
err = os.Chdir(originalDir)
|
||||
if err != nil {
|
||||
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to change working directory: %s", err)
|
||||
}
|
||||
|
||||
// remove old test file if it exists
|
||||
err = os.Remove(path.Join(testDir, INFISICAL_WORKSPACE_CONFIG_FILE_NAME))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to remove old test file: %s", err)
|
||||
}
|
||||
|
||||
// deploy the test file
|
||||
copyTestFile(t, c.TestFile, path.Join(testDir, INFISICAL_WORKSPACE_CONFIG_FILE_NAME))
|
||||
|
||||
// change the working directory to the tmp directory
|
||||
err = os.Chdir(testDir)
|
||||
if err != nil {
|
||||
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to change working directory: %s", err)
|
||||
}
|
||||
|
||||
// get env from file
|
||||
env := GetEnvFromWorkspaceFile()
|
||||
if env != c.ExpectedEnv {
|
||||
t.Errorf("Test_Read_DefaultEnv_From_File: Expected env to be %s but got %s", c.ExpectedEnv, env)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func copyTestFile(t *testing.T, src, dst string) {
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
t.Errorf("Test_Read_Env_From_File_By_Branch: Failed to open source file: %s", err)
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
dstFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
t.Errorf("Test_Read_Env_From_File_By_Branch: Failed to create destination file: %s", err)
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
_, err = io.Copy(dstFile, srcFile)
|
||||
if err != nil {
|
||||
t.Errorf("Test_Read_Env_From_File_By_Branch: Failed to copy file: %s", err)
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/99designs/keyring"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func GetCurrentVaultBackend() (keyring.BackendType, error) {
|
||||
configFile, err := GetConfigFile()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getCurrentVaultBackend: unable to get config file [err=%s]", err)
|
||||
}
|
||||
|
||||
if configFile.VaultBackendType == "" {
|
||||
return keyring.AvailableBackends()[0], nil
|
||||
}
|
||||
|
||||
return configFile.VaultBackendType, nil
|
||||
}
|
||||
|
||||
func GetKeyRing() (keyring.Keyring, error) {
|
||||
currentVaultBackend, err := GetCurrentVaultBackend()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetKeyRing: unable to get the current vault backend, [err=%s]", err)
|
||||
}
|
||||
|
||||
keyringInstanceConfig := keyring.Config{
|
||||
FilePasswordFunc: fileKeyringPassphrasePrompt,
|
||||
ServiceName: KEYRING_SERVICE_NAME,
|
||||
LibSecretCollectionName: KEYRING_SERVICE_NAME,
|
||||
KWalletAppID: KEYRING_SERVICE_NAME,
|
||||
KWalletFolder: KEYRING_SERVICE_NAME,
|
||||
KeychainName: "login", // default so user will not be prompted
|
||||
KeychainTrustApplication: true,
|
||||
WinCredPrefix: KEYRING_SERVICE_NAME,
|
||||
FileDir: fmt.Sprintf("~/%s-file-vault", KEYRING_SERVICE_NAME),
|
||||
KeychainAccessibleWhenUnlocked: true,
|
||||
}
|
||||
|
||||
// if the user explicitly sets a vault backend, then only use that
|
||||
if currentVaultBackend != "" {
|
||||
keyringInstanceConfig.AllowedBackends = []keyring.BackendType{keyring.BackendType(currentVaultBackend)}
|
||||
}
|
||||
|
||||
keyringInstance, err := keyring.Open(keyringInstanceConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetKeyRing: Unable to create instance of Keyring because of [err=%s]", err)
|
||||
}
|
||||
|
||||
return keyringInstance, nil
|
||||
}
|
||||
|
||||
func fileKeyringPassphrasePrompt(prompt string) (string, error) {
|
||||
if password, ok := os.LookupEnv("VAULT_PASS"); ok {
|
||||
return password, nil
|
||||
} else if password, ok := os.LookupEnv("INFISICAL_VAULT_FILE_PASSPHRASE"); ok {
|
||||
return password, nil
|
||||
} else {
|
||||
fmt.Println("To avoid repeatedly typing your password, set the environment variable `VAULT_PASS` to your password")
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "%s:", prompt)
|
||||
b, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
return string(b), nil
|
||||
}
|
||||
Reference in New Issue
Block a user