Skip to content
This repository has been archived by the owner on Mar 12, 2020. It is now read-only.

Commit

Permalink
Merge pull request #14 from adfinis-sygroup/feature/rm_yml_dep
Browse files Browse the repository at this point in the history
Removed dependency on yaml and sorted secrets
  • Loading branch information
winpat authored Feb 2, 2017
2 parents b60e1fd + 9665cf5 commit 95c5a69
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 52 deletions.
80 changes: 34 additions & 46 deletions src/edit.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package main

import (
"bufio"
"fmt"
"io/ioutil"
"os"
"os/exec"
"regexp"
"sort"
"strings"

"github.com/mitchellh/cli"
"gopkg.in/yaml.v2"
)

type EditCommand struct {
Expand All @@ -28,7 +28,6 @@ func (c *EditCommand) Run(args []string) int {
}

path := args[0]

secret, err := vc.Logical().Read(path)
if err != nil {
return 1
Expand All @@ -45,7 +44,7 @@ func (c *EditCommand) Run(args []string) int {
if answer := strings.ToLower(answer); answer == "n" {
return 0
}
data["key"] = "value"

} else {
data = secret.Data
}
Expand Down Expand Up @@ -85,46 +84,59 @@ func (c *EditCommand) Synopsis() string {
return "Edit a secret at specified path"
}

// Processes a secret by unmarshaling and writting it into a tempfile.
// After the file was edit it will reread the tempfile marhsal the data and clean up.
// Processes a secret by writting all k/v pairs into a tempfile. After the file was edited through a
// text editor it will be parsed.
func ProcessSecret(data map[string]interface{}) (map[string]interface{}, error) {

f, err := ioutil.TempFile("", "vaultsecret")
file, err := ioutil.TempFile("", "vaultsecret")
if err != nil {
return nil, err
}

defer os.Remove(f.Name())
defer os.Remove(file.Name())

ymldata, err := yaml.Marshal(&data)
_, err = f.Write(ymldata)
if err != nil {
return nil, err
// Sort secrets lexicographically
var keys []string
for k := range data {
keys = append(keys, k)
}
sort.Strings(keys)

// Write secrets to tempfile in sorted order
for _, k := range keys {
file.WriteString(k + ": " + data[k].(string) + "\n")
}
file.Close()

editedData, err := EditFile(f.Name())
err = EditFile(file.Name())
if err != nil {
return nil, err
}

// Parse secret
parsedData := make(map[string]interface{})

err = yaml.Unmarshal(editedData, parsedData)
editedFile, err := os.Open(file.Name())
if err != nil {
return nil, fmt.Errorf("Unable to parse yaml tempfile: %q", err)
return nil, err
}

err = ValidateData(parsedData)
if err != nil {
return nil, err
scanner := bufio.NewScanner(editedFile)

for scanner.Scan() {
line := scanner.Text()
kv_pair := strings.Split(line, ": ")
if len(kv_pair) == 2 {
parsedData[kv_pair[0]] = kv_pair[1]
} else {
return nil, fmt.Errorf("Unable to parse key/value pair: %q", line)
}
}

return parsedData, nil

}

// Edit a file with the editor specified in $EDITOR or vi as fallback
func EditFile(path string) ([]byte, error) {
func EditFile(path string) error {

var cmdstring []string

Expand All @@ -144,32 +156,8 @@ func EditFile(path string) ([]byte, error) {

err := cmd.Run()
if err != nil {
return nil, err
}

content, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
return err
}

return content, nil
}

// Check if data keys of a secrets contain only valid characters
func ValidateData(data map[string]interface{}) error {

allowedCharacters := "^[A-Za-z0-9-_]*$"

for k := range data {
matched, err := regexp.MatchString(allowedCharacters, k)
if err != nil {
return fmt.Errorf("Unable to validate secret keys: %q", err)
}

if !matched {
return fmt.Errorf("Invalid characters in key %q", k)
}

}
return nil
}
22 changes: 16 additions & 6 deletions src/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"sort"

"github.com/mitchellh/cli"
)
Expand Down Expand Up @@ -36,18 +37,27 @@ func (c *ShowCommand) Run(args []string) int {

// Get length of the largest key in order to calculate the
// "whitespace padded" representation of `show`
max_key_len := 0
MaxKeyLen := 0
for k, _ := range secret.Data {
if key_len := len(k); key_len > max_key_len {
max_key_len = key_len
if KeyLen := len(k); KeyLen > MaxKeyLen {
MaxKeyLen = KeyLen
}
}

// Add an additional X whitespaces between "key:" and "value"
max_key_len += 4
MaxKeyLen += 4

for k, v := range secret.Data {
c.Ui.Output(fmt.Sprintf("%-"+fmt.Sprint(max_key_len)+"v %v", fmt.Sprint(k, ":"), v))
// Sort secrets lexicographically
var keys []string
for k := range secret.Data {
keys = append(keys, k)
}
sort.Strings(keys)

for _, k := range keys {
c.Ui.Output(fmt.Sprintf("%-"+fmt.Sprint(MaxKeyLen)+"v %v",
k+":", // Secret identifier
secret.Data[k])) // Secret value
}

return 0
Expand Down
32 changes: 32 additions & 0 deletions src/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,38 @@ func TestShow(t *testing.T) {
}
})

t.Run("ShowSortedSecrets", func(t *testing.T) {

// Create test secret
data := make(map[string]interface{})
data["a_key"] = "value"
data["c_key"] = "value"
data["b_key"] = "value"

_, err = vc.Logical().Write("secret/secret1", data)
if err != nil {
t.Fatalf("Unable to write test secret: %q", err)
}

args := []string{"secret/secret1"}

if rc := c.Run(args); rc != 0 {
t.Fatalf("Wrong exit code. errors: \n%s", ui.ErrorWriter.String())
}

expectedErr := ""
if actual := ui.ErrorWriter.String(); !strings.Contains(actual, expectedErr) {
t.Fatalf("expected error:\n%s\n\nto include: %q", actual, expectedErr)
}

expectedOutput := `a_key: value
b_key: value
c_key: value`
if actual := ui.OutputWriter.String(); !strings.Contains(actual, expectedOutput) {
t.Fatalf("expected output:\n%s\n\nto include: %q", actual, expectedOutput)
}
})

_, err = vc.Logical().Delete("secret/secret1")
if err != nil {
t.Fatalf("Unable to write test secret: %q", err)
Expand Down

0 comments on commit 95c5a69

Please sign in to comment.