mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 20:27:12 +00:00
generate example .env file command
This commit is contained in:
+24
-15
@@ -201,21 +201,30 @@ type GetEncryptedSecretsV2Request struct {
|
|||||||
|
|
||||||
type GetEncryptedSecretsV2Response struct {
|
type GetEncryptedSecretsV2Response struct {
|
||||||
Secrets []struct {
|
Secrets []struct {
|
||||||
ID string `json:"_id"`
|
ID string `json:"_id"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
Workspace string `json:"workspace"`
|
Workspace string `json:"workspace"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Environment string `json:"environment"`
|
Environment string `json:"environment"`
|
||||||
SecretKeyCiphertext string `json:"secretKeyCiphertext"`
|
SecretKeyCiphertext string `json:"secretKeyCiphertext"`
|
||||||
SecretKeyIV string `json:"secretKeyIV"`
|
SecretKeyIV string `json:"secretKeyIV"`
|
||||||
SecretKeyTag string `json:"secretKeyTag"`
|
SecretKeyTag string `json:"secretKeyTag"`
|
||||||
SecretValueCiphertext string `json:"secretValueCiphertext"`
|
SecretValueCiphertext string `json:"secretValueCiphertext"`
|
||||||
SecretValueIV string `json:"secretValueIV"`
|
SecretValueIV string `json:"secretValueIV"`
|
||||||
SecretValueTag string `json:"secretValueTag"`
|
SecretValueTag string `json:"secretValueTag"`
|
||||||
V int `json:"__v"`
|
SecretCommentCiphertext string `json:"secretCommentCiphertext"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
SecretCommentIV string `json:"secretCommentIV"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
SecretCommentTag string `json:"secretCommentTag"`
|
||||||
User string `json:"user,omitempty"`
|
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"`
|
} `json:"secrets"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+182
-1
@@ -6,6 +6,8 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
@@ -22,7 +24,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var secretsCmd = &cobra.Command{
|
var secretsCmd = &cobra.Command{
|
||||||
Example: `infisical secrets"`,
|
Example: `infisical secrets`,
|
||||||
Short: "Used to create, read update and delete secrets",
|
Short: "Used to create, read update and delete secrets",
|
||||||
Use: "secrets",
|
Use: "secrets",
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
@@ -67,6 +69,16 @@ var secretsGetCmd = &cobra.Command{
|
|||||||
Run: getSecretsByNames,
|
Run: getSecretsByNames,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var secretsGenerateExampleEnvCmd = &cobra.Command{
|
||||||
|
Example: `secrets generate-example-env > .example-env`,
|
||||||
|
Short: "Used to generate a example .env file",
|
||||||
|
Use: "generate-example-env",
|
||||||
|
DisableFlagsInUseLine: true,
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
PreRun: toggleDebug,
|
||||||
|
Run: generateExampleEnv,
|
||||||
|
}
|
||||||
|
|
||||||
var secretsSetCmd = &cobra.Command{
|
var secretsSetCmd = &cobra.Command{
|
||||||
Example: `secrets set <secretName=secretValue> <secretName=secretValue>..."`,
|
Example: `secrets set <secretName=secretValue> <secretName=secretValue>..."`,
|
||||||
Short: "Used set secrets",
|
Short: "Used set secrets",
|
||||||
@@ -357,6 +369,171 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
|
|||||||
visualize.PrintAllSecretDetails(requestedSecrets)
|
visualize.PrintAllSecretDetails(requestedSecrets)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateExampleEnv(cmd *cobra.Command, args []string) {
|
||||||
|
environmentName, err := cmd.Flags().GetString("env")
|
||||||
|
if err != nil {
|
||||||
|
util.HandleError(err, "Unable to parse flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
workspaceFileExists := util.WorkspaceConfigFileExistsInCurrentPath()
|
||||||
|
if !workspaceFileExists {
|
||||||
|
util.HandleError(err, "Unable to parse flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
infisicalToken, err := cmd.Flags().GetString("token")
|
||||||
|
if err != nil {
|
||||||
|
util.HandleError(err, "Unable to parse flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken})
|
||||||
|
if err != nil {
|
||||||
|
util.HandleError(err, "To fetch all secrets")
|
||||||
|
}
|
||||||
|
|
||||||
|
tagsHashToSecretKey := make(map[string]int)
|
||||||
|
|
||||||
|
type TagsAndSecrets struct {
|
||||||
|
Secrets []models.SingleEnvironmentVariable
|
||||||
|
Tags []struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Workspace string `json:"workspace"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort secrets by associated tags (most number of tags to least tags)
|
||||||
|
sort.Slice(secrets, func(i, j int) bool {
|
||||||
|
return len(secrets[i].Tags) > len(secrets[j].Tags)
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, secret := range secrets {
|
||||||
|
listOfTagSlugs := []string{}
|
||||||
|
|
||||||
|
for _, tag := range secret.Tags {
|
||||||
|
listOfTagSlugs = append(listOfTagSlugs, tag.Slug)
|
||||||
|
}
|
||||||
|
sort.Strings(listOfTagSlugs)
|
||||||
|
|
||||||
|
tagsHash := util.GetHashFromStringList(listOfTagSlugs)
|
||||||
|
|
||||||
|
tagsHashToSecretKey[tagsHash] += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
finalTagHashToSecretKey := make(map[string]TagsAndSecrets)
|
||||||
|
|
||||||
|
for _, secret := range secrets {
|
||||||
|
listOfTagSlugs := []string{}
|
||||||
|
for _, tag := range secret.Tags {
|
||||||
|
listOfTagSlugs = append(listOfTagSlugs, tag.Slug)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the slug so we get the same hash each time
|
||||||
|
sort.Strings(listOfTagSlugs)
|
||||||
|
|
||||||
|
tagsHash := util.GetHashFromStringList(listOfTagSlugs)
|
||||||
|
occurrence, exists := tagsHashToSecretKey[tagsHash]
|
||||||
|
if exists && occurrence > 0 {
|
||||||
|
|
||||||
|
value, exists2 := finalTagHashToSecretKey[tagsHash]
|
||||||
|
allSecretsForTags := append(value.Secrets, secret)
|
||||||
|
|
||||||
|
// sort the the secrets by keys so that they can later be sorted by the first item in the secrets array
|
||||||
|
sort.Slice(allSecretsForTags, func(i, j int) bool {
|
||||||
|
return allSecretsForTags[i].Key < allSecretsForTags[j].Key
|
||||||
|
})
|
||||||
|
|
||||||
|
if exists2 {
|
||||||
|
finalTagHashToSecretKey[tagsHash] = TagsAndSecrets{
|
||||||
|
Tags: secret.Tags,
|
||||||
|
Secrets: allSecretsForTags,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
finalTagHashToSecretKey[tagsHash] = TagsAndSecrets{
|
||||||
|
Tags: secret.Tags,
|
||||||
|
Secrets: []models.SingleEnvironmentVariable{secret},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tagsHashToSecretKey[tagsHash] -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the fianl result by secret key fo consistent print order
|
||||||
|
listOfsecretDetails := make([]TagsAndSecrets, 0, len(finalTagHashToSecretKey))
|
||||||
|
for _, secretDetails := range finalTagHashToSecretKey {
|
||||||
|
listOfsecretDetails = append(listOfsecretDetails, secretDetails)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the order of the headings by the order of the secrets
|
||||||
|
sort.Slice(listOfsecretDetails, func(i, j int) bool {
|
||||||
|
return len(listOfsecretDetails[i].Tags) < len(listOfsecretDetails[j].Tags)
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, secretDetails := range listOfsecretDetails {
|
||||||
|
listOfKeyValue := []string{}
|
||||||
|
|
||||||
|
for _, secret := range secretDetails.Secrets {
|
||||||
|
re := regexp.MustCompile(`(.*)DEFAULT:(.*)`)
|
||||||
|
match := re.FindStringSubmatch(secret.Comment)
|
||||||
|
defaultValue := ""
|
||||||
|
comment := secret.Comment
|
||||||
|
|
||||||
|
// Case: Only has default value
|
||||||
|
if len(match) == 2 {
|
||||||
|
defaultValue = strings.TrimSpace(match[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case: has a comment and a default value
|
||||||
|
if len(match) == 3 {
|
||||||
|
comment = match[1]
|
||||||
|
defaultValue = match[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
row := ""
|
||||||
|
if comment != "" {
|
||||||
|
comment = addHash(comment)
|
||||||
|
row = fmt.Sprintf("%s \n%s=%s", strings.TrimSpace(comment), strings.TrimSpace(secret.Key), strings.TrimSpace(defaultValue))
|
||||||
|
} else {
|
||||||
|
row = fmt.Sprintf("%s=%s", strings.TrimSpace(secret.Key), strings.TrimSpace(defaultValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
// each secret row to be added to the file
|
||||||
|
listOfKeyValue = append(listOfKeyValue, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
listOfTagNames := []string{}
|
||||||
|
for _, tag := range secretDetails.Tags {
|
||||||
|
listOfTagNames = append(listOfTagNames, tag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
heading := CenterString(strings.Join(listOfTagNames, " & "), 80)
|
||||||
|
|
||||||
|
if len(listOfTagNames) == 0 {
|
||||||
|
fmt.Printf("\n%s \n", strings.Join(listOfKeyValue, "\n \n"))
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\n\n\n%s\n \n%s \n", heading, strings.Join(listOfKeyValue, "\n \n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CenterString(s string, numStars int) string {
|
||||||
|
stars := strings.Repeat("*", numStars)
|
||||||
|
padding := (numStars - len(s)) / 2
|
||||||
|
cenetredTextWithStar := stars[:padding] + " " + strings.ToUpper(s) + " " + stars[padding:]
|
||||||
|
|
||||||
|
hashes := strings.Repeat("#", len(cenetredTextWithStar)+2)
|
||||||
|
return fmt.Sprintf("%s \n# %s \n%s", hashes, cenetredTextWithStar, hashes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addHash(input string) string {
|
||||||
|
lines := strings.Split(input, "\n")
|
||||||
|
for i, line := range lines {
|
||||||
|
lines[i] = "# " + line
|
||||||
|
}
|
||||||
|
return strings.Join(lines, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]models.SingleEnvironmentVariable {
|
func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]models.SingleEnvironmentVariable {
|
||||||
secretMapByName := make(map[string]models.SingleEnvironmentVariable)
|
secretMapByName := make(map[string]models.SingleEnvironmentVariable)
|
||||||
|
|
||||||
@@ -368,6 +545,10 @@ func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]mod
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
|
secretsGenerateExampleEnvCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||||
|
secretsCmd.AddCommand(secretsGenerateExampleEnvCmd)
|
||||||
|
|
||||||
secretsGetCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
secretsGetCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||||
secretsCmd.AddCommand(secretsGetCmd)
|
secretsCmd.AddCommand(secretsGetCmd)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "github.com/99designs/keyring"
|
import (
|
||||||
|
"github.com/99designs/keyring"
|
||||||
|
)
|
||||||
|
|
||||||
type UserCredentials struct {
|
type UserCredentials struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
@@ -19,6 +21,13 @@ type SingleEnvironmentVariable struct {
|
|||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
ID string `json:"_id"`
|
ID string `json:"_id"`
|
||||||
|
Tags []struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Workspace string `json:"workspace"`
|
||||||
|
} `json:"tags"`
|
||||||
|
Comment string `json:"comment"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Workspace struct {
|
type Workspace struct {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -98,3 +99,14 @@ func RequireLocalWorkspaceFile() {
|
|||||||
PrintMessageAndExit("Your project id is missing in your local config file. Please add it or run again [infisical init]")
|
PrintMessageAndExit("Your project id is missing in your local config file. Please add it or run again [infisical init]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetHashFromStringList(list []string) string {
|
||||||
|
hash := sha256.New()
|
||||||
|
|
||||||
|
for _, item := range list {
|
||||||
|
hash.Write([]byte(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
sum := sha256.Sum256(hash.Sum(nil))
|
||||||
|
return fmt.Sprintf("%x", sum)
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func PrintErrorAndExit(exitCode int, err error, messages ...string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func PrintWarning(message string) {
|
func PrintWarning(message string) {
|
||||||
color.Yellow("Warning: %v", message)
|
color.New(color.FgYellow).Fprintf(os.Stderr, "Warning: %v \n", message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintMessageAndExit(messages ...string) {
|
func PrintMessageAndExit(messages ...string) {
|
||||||
|
|||||||
@@ -315,11 +315,34 @@ func GetPlainTextSecrets(key []byte, encryptedSecrets api.GetEncryptedSecretsV2R
|
|||||||
return nil, fmt.Errorf("unable to symmetrically decrypt secret value")
|
return nil, fmt.Errorf("unable to symmetrically decrypt secret value")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decrypt comment
|
||||||
|
comment_iv, err := base64.StdEncoding.DecodeString(secret.SecretCommentIV)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decode secret IV for secret value")
|
||||||
|
}
|
||||||
|
|
||||||
|
comment_tag, err := base64.StdEncoding.DecodeString(secret.SecretCommentTag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decode secret authentication tag for secret value")
|
||||||
|
}
|
||||||
|
|
||||||
|
comment_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretCommentCiphertext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decode secret cipher text for secret key")
|
||||||
|
}
|
||||||
|
|
||||||
|
plainTextComment, err := crypto.DecryptSymmetric(key, comment_ciphertext, comment_tag, comment_iv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to symmetrically decrypt secret comment")
|
||||||
|
}
|
||||||
|
|
||||||
plainTextSecret := models.SingleEnvironmentVariable{
|
plainTextSecret := models.SingleEnvironmentVariable{
|
||||||
Key: string(plainTextKey),
|
Key: string(plainTextKey),
|
||||||
Value: string(plainTextValue),
|
Value: string(plainTextValue),
|
||||||
Type: string(secret.Type),
|
Type: string(secret.Type),
|
||||||
ID: secret.ID,
|
ID: secret.ID,
|
||||||
|
Tags: secret.Tags,
|
||||||
|
Comment: string(plainTextComment),
|
||||||
}
|
}
|
||||||
|
|
||||||
plainTextSecrets = append(plainTextSecrets, plainTextSecret)
|
plainTextSecrets = append(plainTextSecrets, plainTextSecret)
|
||||||
|
|||||||
Reference in New Issue
Block a user