Skip to content

Commit

Permalink
feat: adding the ability to specify multiple paths for secrets (#438)
Browse files Browse the repository at this point in the history
* feat: adding the ability to specify multiple paths for secrets

* fix: making linter happy

* fix: adding some more tests

* fix: making sure that a single path can be unmarshaled into path

* fix: linter

* fix: linter

* fix: adding test for single path

* fix: deleting commented out test

---------

Co-authored-by: Claire.Nicholas <[email protected]>
  • Loading branch information
claire1618 and Claire.Nicholas authored Jan 17, 2024
1 parent 96b6108 commit ae54723
Show file tree
Hide file tree
Showing 5 changed files with 317 additions and 43 deletions.
6 changes: 1 addition & 5 deletions cmd/secret-vault/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,11 @@ import (
"github.com/go-vela/secret-vault/vault"
)

func TestVault_Plugin_Exec(t *testing.T) {
// TODO write this test
}

func TestVault_Plugin_Validate(t *testing.T) {
// setup types
items, _ := json.Marshal([]Item{
{
Path: "foobar",
Path: []string{"foobar"},
Source: "/path/to/secret",
},
})
Expand Down
79 changes: 48 additions & 31 deletions cmd/secret-vault/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"

"github.com/go-vela/secret-vault/vault"
"github.com/go-vela/types/raw"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
)
Expand All @@ -32,7 +33,7 @@ var (
appFS = afero.NewOsFs()

// SecretVolume defines volume that stores secrets during a build execution
// nolint: gosec // false pos
//nolint: gosec // false pos
SecretVolume = "/vela/secrets/%s/"
)

Expand All @@ -49,8 +50,8 @@ type (
Item struct {
// is the path to where the secret is stored in Vault
Source string
// is the path to store the key in Vela
Path string
// are the paths to store the key in Vela
Path raw.StringSlice
}
)

Expand All @@ -64,41 +65,43 @@ func (r *Read) Exec(v *vault.Client) error {
}

for _, item := range r.Items {
// remove any leading slashes from path
path := strings.TrimPrefix(item.Path, "/")
for _, pth := range item.Path {
// remove any leading slashes from path
path := strings.TrimPrefix(pth, "/")

// remove any trailing slashes from path
path = strings.TrimSuffix(path, "/")
// remove any trailing slashes from path
path = strings.TrimSuffix(path, "/")

// read data from the vault provider
logrus.Tracef("reading data from path %s", item.Source)
// read data from the vault provider
logrus.Tracef("reading data from path %s", item.Source)

secret, err := v.Read(item.Source)
if err != nil {
return err
}
secret, err := v.Read(item.Source)
if err != nil {
return err
}

// set the location of where to write the secret
target := fmt.Sprintf(SecretVolume, path)
// set the location of where to write the secret
target := fmt.Sprintf(SecretVolume, path)

// send Filesystem call to create directory path for .netrc file
logrus.Tracef("creating directories in path %s", path)
// send Filesystem call to create directory path for .netrc file
logrus.Tracef("creating directories in path %s", path)

err = a.Fs.MkdirAll(filepath.Dir(target), 0777)
if err != nil {
return err
}
err = a.Fs.MkdirAll(filepath.Dir(target), 0777)
if err != nil {
return err
}

// loop through keys in vault secret
for k, v := range secret.Data {
path = target + k
// loop through keys in vault secret
for k, v := range secret.Data {
path = target + k

// set the secret in the Vela temp build volume
logrus.Tracef("write data to file %s", path)
// set the secret in the Vela temp build volume
logrus.Tracef("write data to file %s", path)

err = a.WriteFile(path, []byte(v.(string)), 0600)
if err != nil {
return err
err = a.WriteFile(path, []byte(v.(string)), 0600)
if err != nil {
return err
}
}
}
}
Expand Down Expand Up @@ -132,9 +135,23 @@ func (r *Read) Validate() error {
}

for i, item := range r.Items {
// verify path is provided
// verify that at least one path was provided
if len(item.Path) == 0 {
return fmt.Errorf("%w for item %d", ErrNoPathProvided, i)
return fmt.Errorf("%w for item %d %s", ErrNoPathProvided, i, r.RawItems)
}

noPath := 0

for _, path := range item.Path {
// verify that at least one non-nil path was provided
if len(path) != 0 {
noPath = 1
break
}
}

if noPath == 0 {
return fmt.Errorf("%w for item %d %s", ErrNoPathProvided, i, r.RawItems)
}

// verify source is provided
Expand Down
102 changes: 96 additions & 6 deletions cmd/secret-vault/read_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ func TestVault_Read_Exec(t *testing.T) {
// step types
vault, _ := vault.NewMock(t)
source := "/secret/foo"
path := []string{"foobar", "foobar2"}
r := &Read{
Items: []*Item{
{
Path: "foobar",
Path: path,
Source: source,
},
},
Expand All @@ -27,14 +28,43 @@ func TestVault_Read_Exec(t *testing.T) {
appFS = afero.NewMemMapFs()

// initialize vault with test data
// nolint: errcheck // error check not needed
//nolint: errcheck // error check not needed
vault.Vault.Logical().Write(source, map[string]interface{}{
"secret": "bar",
})

err := r.Exec(vault)
if err != nil {
t.Errorf("Validate returned err: %v", err)
t.Errorf("Exec returned err: %v", err)
}
}

func TestVault_Read_Exec_Fail(t *testing.T) {
// step types
vault, _ := vault.NewMock(t)
source := ""
path := []string{"foobar", "foobar2"}
r := &Read{
Items: []*Item{
{
Path: path,
Source: source,
},
},
}

// setup filesystem
appFS = afero.NewMemMapFs()

// initialize vault with test data
//nolint: errcheck // error check not needed
vault.Vault.Logical().Write(source, map[string]interface{}{
"secret": "bar",
})

err := r.Exec(vault)
if err == nil {
t.Errorf("Exec should have returned err: %v", err)
}
}

Expand All @@ -49,7 +79,7 @@ func TestVault_Read_Validate_success(t *testing.T) {
read: &Read{
Items: []*Item{
{
Path: "foobar",
Path: []string{"foobar", "foobar2"},
Source: "/path/to/secret",
},
},
Expand All @@ -73,6 +103,13 @@ func TestVault_Read_Validate_failure(t *testing.T) {
read *Read
err error
}{
{
// error with no items
read: &Read{
Items: []*Item{},
},
err: ErrNoItemsProvided,
},
{
// error with no path
read: &Read{
Expand All @@ -89,7 +126,19 @@ func TestVault_Read_Validate_failure(t *testing.T) {
read: &Read{
Items: []*Item{
{
Path: "foobar",
Path: []string{"foobar", "foobar2"},
},
},
},
err: ErrNoSourceProvided,
},
{
// error with nil path
read: &Read{
Items: []*Item{
{
Source: "/path/to/secret",
Path: []string{""},
},
},
},
Expand All @@ -107,6 +156,32 @@ func TestVault_Read_Validate_failure(t *testing.T) {
}

func TestVault_Read_Unmarshal(t *testing.T) {
// setup types
r := &Read{
RawItems: `
[
{"path":["foo", "foo2"],"source":"secret/vela/hello_world"}
]
`}

want := []*Item{
{
Path: []string{"foo", "foo2"},
Source: "secret/vela/hello_world",
},
}

err := r.Unmarshal()
if err != nil {
t.Errorf("Unmarshal returned err: %v", err)
}

if !reflect.DeepEqual(r.Items, want) {
t.Errorf("Unmarshal is %v, want %v", r.Items, want)
}
}

func TestVault_Read_Unmarshal_Single_Path(t *testing.T) {
// setup types
r := &Read{
RawItems: `
Expand All @@ -117,7 +192,7 @@ func TestVault_Read_Unmarshal(t *testing.T) {

want := []*Item{
{
Path: "foo",
Path: []string{"foo"},
Source: "secret/vela/hello_world",
},
}
Expand All @@ -131,3 +206,18 @@ func TestVault_Read_Unmarshal(t *testing.T) {
t.Errorf("Unmarshal is %v, want %v", r.Items, want)
}
}

func TestVault_Read_Unmarshal_Fail(t *testing.T) {
// setup types
r := &Read{
RawItems: `
[
{"path":"foo,"source":"secret/vela/hello_world"}
]
`}

err := r.Unmarshal()
if err == nil {
t.Errorf("Unmarshal should have returned err: %v", err)
}
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/go-vela/secret-vault

go 1.20
go 1.21

require (
github.com/hashicorp/vault v1.14.1
Expand Down Expand Up @@ -78,6 +78,7 @@ require (
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/go-test/deep v1.1.0 // indirect
github.com/go-vela/types v0.22.0
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
Expand Down
Loading

0 comments on commit ae54723

Please sign in to comment.