Skip to content

Commit

Permalink
Automatically upgrade values to salted on access if salt exists in Pa…
Browse files Browse the repository at this point in the history
…thMap (hashicorp#2831)
  • Loading branch information
jefferai authored Jun 7, 2017
1 parent 7951a15 commit e5e6f0f
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 7 deletions.
70 changes: 63 additions & 7 deletions logical/framework/path_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,42 +42,98 @@ func (p *PathMap) init() {
}

// pathStruct returns the pathStruct for this mapping
func (p *PathMap) pathStruct(k string) *PathStruct {
func (p *PathMap) pathStruct(s logical.Storage, k string) (*PathStruct, error) {
p.once.Do(p.init)

// If we don't care about casing, store everything lowercase
if !p.CaseSensitive {
k = strings.ToLower(k)
}

// The original key before any salting
origKey := k

// If we have a salt, apply it before lookup
salt := p.Salt
var err error
if p.SaltFunc != nil {
salt, _ = p.SaltFunc()
salt, err = p.SaltFunc()
if err != nil {
return nil, err
}
}
if salt != nil {
k = salt.SaltID(k)
}

return &PathStruct{
Name: fmt.Sprintf("map/%s/%s", p.Name, k),
finalName := fmt.Sprintf("map/%s/%s", p.Name, k)
ps := &PathStruct{
Name: finalName,
Schema: p.Schema,
}

// Check for unsalted version and upgrade if so
if k != origKey {
// Generate the unsalted name
unsaltedName := fmt.Sprintf("map/%s/%s", p.Name, origKey)
// Set the path struct to use the unsalted name
ps.Name = unsaltedName
// Ensure that no matter what happens what is returned is the final
// path
defer func() {
ps.Name = finalName
}()
val, err := ps.Get(s)
if err != nil {
return nil, err
}
// If not nil, we have an unsalted entry -- upgrade it
if val != nil {
// Set the path struct to use the desired final name
ps.Name = finalName
err = ps.Put(s, val)
if err != nil {
return nil, err
}
// Set it back to the old path and delete
ps.Name = unsaltedName
err = ps.Delete(s)
if err != nil {
return nil, err
}
// We'll set this in the deferred function but doesn't hurt here
ps.Name = finalName
}
}

return ps, nil
}

// Get reads a value out of the mapping
func (p *PathMap) Get(s logical.Storage, k string) (map[string]interface{}, error) {
return p.pathStruct(k).Get(s)
ps, err := p.pathStruct(s, k)
if err != nil {
return nil, err
}
return ps.Get(s)
}

// Put writes a value into the mapping
func (p *PathMap) Put(s logical.Storage, k string, v map[string]interface{}) error {
return p.pathStruct(k).Put(s, v)
ps, err := p.pathStruct(s, k)
if err != nil {
return err
}
return ps.Put(s, v)
}

// Delete removes a value from the mapping
func (p *PathMap) Delete(s logical.Storage, k string) error {
return p.pathStruct(k).Delete(s)
ps, err := p.pathStruct(s, k)
if err != nil {
return err
}
return ps.Delete(s)
}

// List reads the keys under a given path
Expand Down
66 changes: 66 additions & 0 deletions logical/framework/path_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,39 @@ func TestPathMap_Salted(t *testing.T) {
if v != nil {
t.Fatalf("bad: %#v", v)
}

// Put in a non-salted version and make sure that after reading it's been
// upgraded
err = storage.Put(&logical.StorageEntry{
Key: "struct/map/foo/b",
Value: []byte(`{"foo": "bar"}`),
})
if err != nil {
t.Fatal("err: %v", err)
}
// A read should transparently upgrade
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.ReadOperation,
Path: "map/foo/b",
Storage: storage,
})
if err != nil {
t.Fatal(err)
}
list, _ := storage.List("struct/map/foo/")
if len(list) != 1 {
t.Fatalf("unexpected number of entries left after upgrade; expected 1, got %d", len(list))
}
found := false
for _, v := range list {
if v == salt.SaltID("b") {
found = true
break
}
}
if !found {
t.Fatal("did not find upgraded value")
}
}

func TestPathMap_SaltFunc(t *testing.T) {
Expand Down Expand Up @@ -376,4 +409,37 @@ func TestPathMap_SaltFunc(t *testing.T) {
if v != nil {
t.Fatalf("bad: %#v", v)
}

// Put in a non-salted version and make sure that after reading it's been
// upgraded
err = storage.Put(&logical.StorageEntry{
Key: "struct/map/foo/b",
Value: []byte(`{"foo": "bar"}`),
})
if err != nil {
t.Fatal("err: %v", err)
}
// A read should transparently upgrade
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.ReadOperation,
Path: "map/foo/b",
Storage: storage,
})
if err != nil {
t.Fatal(err)
}
list, _ := storage.List("struct/map/foo/")
if len(list) != 1 {
t.Fatalf("unexpected number of entries left after upgrade; expected 1, got %d", len(list))
}
found := false
for _, v := range list {
if v == locSalt.SaltID("b") {
found = true
break
}
}
if !found {
t.Fatal("did not find upgraded value")
}
}

0 comments on commit e5e6f0f

Please sign in to comment.