mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Add frontend, backend and CLI
This commit is contained in:
29
cli/packages/util/common.go
Normal file
29
cli/packages/util/common.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
CONFIG_FILE_NAME = "infisical-config.json"
|
||||
CONFIG_FOLDER_NAME = ".infisical"
|
||||
INFISICAL_WORKSPACE_CONFIG_FILE_NAME = ".infisical.json"
|
||||
INFISICAL_SERVICE_TOKEN = "INFISICAL_SERVICE_TOKEN"
|
||||
)
|
||||
|
||||
var INFISICAL_URL = "https://api.infisical.com"
|
||||
|
||||
func GetHomeDir() (string, error) {
|
||||
directory, err := os.UserHomeDir()
|
||||
return directory, err
|
||||
}
|
||||
|
||||
func WriteToFile(fileName string, dataToWrite []byte, filePerm os.FileMode) error {
|
||||
err := os.WriteFile(fileName, dataToWrite, filePerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to wrote to file", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
92
cli/packages/util/config.go
Normal file
92
cli/packages/util/config.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func WriteInitalConfig(userCredentials *models.UserCredentials) error {
|
||||
fullConfigFilePath, fullConfigFileDirPath, err := GetFullConfigFilePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create directory
|
||||
if _, err := os.Stat(fullConfigFileDirPath); errors.Is(err, os.ErrNotExist) {
|
||||
err := os.Mkdir(fullConfigFileDirPath, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
configFile := models.ConfigFile{
|
||||
LoggedInUserEmail: userCredentials.Email,
|
||||
}
|
||||
|
||||
configFileMarshalled, err := json.Marshal(configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create file in directory
|
||||
err = WriteToFile(fullConfigFilePath, configFileMarshalled, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func ConfigFileExists() bool {
|
||||
fullConfigFileURI, _, err := GetFullConfigFilePath()
|
||||
if err != nil {
|
||||
log.Debugln("There was an error when creating the full path to config file", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if _, err := os.Stat(fullConfigFileURI); err == nil {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func WorkspaceConfigFileExists() bool {
|
||||
if _, err := os.Stat(INFISICAL_WORKSPACE_CONFIG_FILE_NAME); err == nil {
|
||||
return true
|
||||
} else {
|
||||
log.Debugln(err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func GetWorkSpaceFromFile() (models.WorkspaceConfigFile, error) {
|
||||
configFileAsBytes, err := os.ReadFile(INFISICAL_WORKSPACE_CONFIG_FILE_NAME)
|
||||
if err != nil {
|
||||
return models.WorkspaceConfigFile{}, err
|
||||
}
|
||||
|
||||
var workspaceConfigFile models.WorkspaceConfigFile
|
||||
err = json.Unmarshal(configFileAsBytes, &workspaceConfigFile)
|
||||
if err != nil {
|
||||
return models.WorkspaceConfigFile{}, err
|
||||
}
|
||||
|
||||
return workspaceConfigFile, nil
|
||||
}
|
||||
|
||||
func GetFullConfigFilePath() (fullPathToFile string, fullPathToDirectory string, err error) {
|
||||
homeDir, err := GetHomeDir()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
fullPath := fmt.Sprintf("%s/%s/%s", homeDir, CONFIG_FOLDER_NAME, CONFIG_FILE_NAME)
|
||||
fullDirPath := fmt.Sprintf("%s/%s", homeDir, CONFIG_FOLDER_NAME)
|
||||
return fullPath, fullDirPath, err
|
||||
}
|
||||
99
cli/packages/util/credentials.go
Normal file
99
cli/packages/util/credentials.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/zalando/go-keyring"
|
||||
)
|
||||
|
||||
const SERVICE_NAME = "infisical"
|
||||
|
||||
// To do: what happens if the user doesn't have a keyring in their system?
|
||||
func StoreUserCredsInKeyRing(userCred *models.UserCredentials) error {
|
||||
userCredMarshalled, err := json.Marshal(userCred)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Something went wrong when marshalling user creds:", err)
|
||||
}
|
||||
|
||||
err = keyring.Set(SERVICE_NAME, userCred.Email, string(userCredMarshalled))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to store user credentials:", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func GetUserCredsFromKeyRing(userEmail string) (credentials models.UserCredentials, err error) {
|
||||
credentialsString, err := keyring.Get(SERVICE_NAME, userEmail)
|
||||
if err != nil {
|
||||
return models.UserCredentials{}, fmt.Errorf("Unable to get key from Keyring:", err)
|
||||
}
|
||||
|
||||
var userCredentials models.UserCredentials
|
||||
|
||||
err = json.Unmarshal([]byte(credentialsString), &userCredentials)
|
||||
if err != nil {
|
||||
return models.UserCredentials{}, fmt.Errorf("Something went wrong when unmarshalling user creds:", err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return models.UserCredentials{}, fmt.Errorf("Unable to store user credentials", err)
|
||||
}
|
||||
|
||||
return userCredentials, err
|
||||
}
|
||||
|
||||
func IsUserLoggedIn() (hasUserLoggedIn bool, theUsersEmail string, err error) {
|
||||
if ConfigFileExists() {
|
||||
fullConfigFilePath, _, err := GetFullConfigFilePath()
|
||||
if err != nil {
|
||||
log.Debugln("Error gettting full path:", err)
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
configFileAsBytes, err := os.ReadFile(fullConfigFilePath)
|
||||
if err != nil {
|
||||
log.Debugln("Unable to read config file:", err)
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
var configFile models.ConfigFile
|
||||
err = json.Unmarshal(configFileAsBytes, &configFile)
|
||||
if err != nil {
|
||||
log.Debugln("Unable to unmarshal config file:", err)
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
userCreds, err := GetUserCredsFromKeyRing(configFile.LoggedInUserEmail)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
// check to to see if the JWT is still valid
|
||||
httpClient := resty.New().
|
||||
SetAuthToken(userCreds.JTWToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
response, err := httpClient.
|
||||
R().
|
||||
Post(fmt.Sprintf("%v/%v", INFISICAL_URL, "checkAuth"))
|
||||
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
log.Infoln("Login expired, please login again.")
|
||||
return false, "", fmt.Errorf("Login expired, please login again.")
|
||||
}
|
||||
|
||||
return true, configFile.LoggedInUserEmail, nil
|
||||
} else {
|
||||
return false, "", nil
|
||||
}
|
||||
}
|
||||
31
cli/packages/util/crypto.go
Normal file
31
cli/packages/util/crypto.go
Normal file
@@ -0,0 +1,31 @@
|
||||
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
|
||||
}
|
||||
|
||||
aesgcm, err := cipher.NewGCMWithNonceSize(block, len(IV))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nonce = IV
|
||||
var ciphertext = append(encryptedPrivateKey, tag...)
|
||||
|
||||
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
205
cli/packages/util/secrets.go
Normal file
205
cli/packages/util/secrets.go
Normal file
@@ -0,0 +1,205 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/nacl/box"
|
||||
)
|
||||
|
||||
func GetSecretsFromAPIUsingCurrentLoggedInUser(stageName string, userCreds models.UserCredentials) ([]models.SingleEnvironmentVariable, error) {
|
||||
log.Debugln("stageName", stageName, "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")
|
||||
|
||||
var pullSecretsRequestResponse models.PullSecretsResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetQueryParam("environment", stageName).
|
||||
SetQueryParam("channel", "cli").
|
||||
SetResult(&pullSecretsRequestResponse).
|
||||
Get(fmt.Sprintf("%v/%v/%v", INFISICAL_URL, "secret", workspace.WorkspaceId)) // need to change workspace id
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
log.Debugln(response)
|
||||
return nil, fmt.Errorf(response.Status())
|
||||
}
|
||||
|
||||
// Get workspace key
|
||||
workspaceKey, err := base64.StdEncoding.DecodeString(pullSecretsRequestResponse.Key.EncryptedKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce, err := base64.StdEncoding.DecodeString(pullSecretsRequestResponse.Key.Nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
senderPublicKey, err := base64.StdEncoding.DecodeString(pullSecretsRequestResponse.Key.Sender.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
currentUsersPrivateKey, err := base64.StdEncoding.DecodeString(userCreds.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
for _, secret := range pullSecretsRequestResponse.Secrets {
|
||||
key_iv, _ := base64.StdEncoding.DecodeString(secret.SecretKeyIV)
|
||||
key_tag, _ := base64.StdEncoding.DecodeString(secret.SecretKeyTag)
|
||||
key_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretKeyCiphertext)
|
||||
|
||||
plainTextKey, err := DecryptSymmetric(workspaceKeyInBytes, key_ciphertext, key_tag, key_iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value_iv, _ := base64.StdEncoding.DecodeString(secret.SecretValueIV)
|
||||
value_tag, _ := base64.StdEncoding.DecodeString(secret.SecretValueTag)
|
||||
value_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretValueCiphertext)
|
||||
|
||||
plainTextValue, err := DecryptSymmetric(workspaceKeyInBytes, value_ciphertext, value_tag, value_iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
env := models.SingleEnvironmentVariable{
|
||||
Key: string(plainTextKey),
|
||||
Value: string(plainTextValue),
|
||||
}
|
||||
|
||||
listOfEnv = append(listOfEnv, env)
|
||||
}
|
||||
|
||||
return listOfEnv, nil
|
||||
}
|
||||
|
||||
func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, stageName string, projectId string) ([]models.SingleEnvironmentVariable, error) {
|
||||
if infisicalToken == "" || projectId == "" || stageName == "" {
|
||||
return nil, errors.New("infisical token, project id and or stage name cannot be empty")
|
||||
}
|
||||
splitToken := strings.Split(infisicalToken, ",")
|
||||
JTWToken := splitToken[0]
|
||||
temPrivateKey := splitToken[1]
|
||||
|
||||
// create http client
|
||||
httpClient := resty.New().
|
||||
SetAuthToken(JTWToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
var pullSecretsByInfisicalTokenResponse models.PullSecretsByInfisicalTokenResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetQueryParam("environment", stageName).
|
||||
SetQueryParam("channel", "cli").
|
||||
SetResult(&pullSecretsByInfisicalTokenResponse).
|
||||
Get(fmt.Sprintf("%v/secret/%v/service-token", INFISICAL_URL, projectId))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
log.Debugln(response)
|
||||
return nil, fmt.Errorf(response.Status())
|
||||
}
|
||||
|
||||
// Get workspace key
|
||||
workspaceKey, err := base64.StdEncoding.DecodeString(pullSecretsByInfisicalTokenResponse.Key.EncryptedKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce, err := base64.StdEncoding.DecodeString(pullSecretsByInfisicalTokenResponse.Key.Nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
senderPublicKey, err := base64.StdEncoding.DecodeString(pullSecretsByInfisicalTokenResponse.Key.Sender.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
currentUsersPrivateKey, err := base64.StdEncoding.DecodeString(temPrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
workspaceKeyInBytes, _ := box.Open(nil, workspaceKey, (*[24]byte)(nonce), (*[32]byte)(senderPublicKey), (*[32]byte)(currentUsersPrivateKey))
|
||||
var listOfEnv []models.SingleEnvironmentVariable
|
||||
|
||||
for _, secret := range pullSecretsByInfisicalTokenResponse.Secrets {
|
||||
key_iv, _ := base64.StdEncoding.DecodeString(secret.SecretKey.Iv)
|
||||
key_tag, _ := base64.StdEncoding.DecodeString(secret.SecretKey.Tag)
|
||||
key_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretKey.Ciphertext)
|
||||
|
||||
plainTextKey, err := DecryptSymmetric(workspaceKeyInBytes, key_ciphertext, key_tag, key_iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value_iv, _ := base64.StdEncoding.DecodeString(secret.SecretValue.Iv)
|
||||
value_tag, _ := base64.StdEncoding.DecodeString(secret.SecretValue.Tag)
|
||||
value_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretValue.Ciphertext)
|
||||
|
||||
plainTextValue, err := DecryptSymmetric(workspaceKeyInBytes, value_ciphertext, value_tag, value_iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
env := models.SingleEnvironmentVariable{
|
||||
Key: string(plainTextKey),
|
||||
Value: string(plainTextValue),
|
||||
}
|
||||
|
||||
listOfEnv = append(listOfEnv, env)
|
||||
}
|
||||
|
||||
return listOfEnv, nil
|
||||
}
|
||||
|
||||
func GetWorkSpacesFromAPI(userCreds models.UserCredentials) (workspaces []models.Workspace, err error) {
|
||||
// create http client
|
||||
httpClient := resty.New().
|
||||
SetAuthToken(userCreds.JTWToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
var getWorkSpacesResponse models.GetWorkSpacesResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&getWorkSpacesResponse).
|
||||
Get(fmt.Sprintf("%v/%v", INFISICAL_URL, "workspace"))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return nil, fmt.Errorf("ops, unsuccessful response code. [response=%v]", response)
|
||||
}
|
||||
|
||||
return getWorkSpacesResponse.Workspaces, nil
|
||||
}
|
||||
Reference in New Issue
Block a user