1
0
mirror of https://github.com/getsops/sops.git synced 2026-02-05 12:45:21 +01:00
Files
sops/stores/yaml/store.go
Adrian Utrilla 13b70024d0 Refactor metadata marshalling
**IMPORTANT** This breaks compatibility of the file format in 1.x for
json files, due to the version being encoded as a number in json files.
The fix for this is easy, however. One can either use a previous version
of sops in the range [2.0.0, 2.0.9] to edit the file, or one can manually edit
the encrypted file and change the version from a number to a string

Previously we basically hand-converted the metadata struct into a map
which we then passed to the stores. Now, we convert the metadata struct
to a "serialization" struct, which the stores serialize
2017-08-23 17:10:39 -07:00

152 lines
4.2 KiB
Go

package yaml //import "go.mozilla.org/sops/stores/yaml"
import (
"fmt"
"github.com/mozilla-services/yaml"
"go.mozilla.org/sops"
"go.mozilla.org/sops/stores"
)
// Store handles storage of YAML data
type Store struct {
}
func (store Store) mapSliceToTreeBranch(in yaml.MapSlice) sops.TreeBranch {
branch := make(sops.TreeBranch, 0)
for _, item := range in {
if comment, ok := item.Key.(yaml.Comment); ok {
// Convert the yaml comment to a generic sops comment
branch = append(branch, sops.TreeItem{
Key: sops.Comment{
Value: comment.Value,
},
Value: nil,
})
} else {
branch = append(branch, sops.TreeItem{
Key: item.Key,
Value: store.yamlValueToTreeValue(item.Value),
})
}
}
return branch
}
// Unmarshal takes a YAML document as input and unmarshals it into a sops tree, returning the tree
func (store Store) Unmarshal(in []byte) (sops.TreeBranch, error) {
var data yaml.MapSlice
if err := (yaml.CommentUnmarshaler{}).Unmarshal(in, &data); err != nil {
return nil, fmt.Errorf("Error unmarshaling input YAML: %s", err)
}
for i, item := range data {
if item.Key == "sops" {
data = append(data[:i], data[i+1:]...)
}
}
return store.mapSliceToTreeBranch(data), nil
}
func (store Store) yamlValueToTreeValue(in interface{}) interface{} {
switch in := in.(type) {
case map[interface{}]interface{}:
return store.yamlMapToTreeBranch(in)
case yaml.MapSlice:
return store.mapSliceToTreeBranch(in)
case []interface{}:
return store.yamlSliceToTreeValue(in)
case yaml.Comment:
return sops.Comment{Value: in.Value}
default:
return in
}
}
func (store *Store) yamlSliceToTreeValue(in []interface{}) []interface{} {
for i, v := range in {
in[i] = store.yamlValueToTreeValue(v)
}
return in
}
func (store *Store) yamlMapToTreeBranch(in map[interface{}]interface{}) sops.TreeBranch {
branch := make(sops.TreeBranch, 0)
for k, v := range in {
branch = append(branch, sops.TreeItem{
Key: k.(string),
Value: store.yamlValueToTreeValue(v),
})
}
return branch
}
func (store Store) treeValueToYamlValue(in interface{}) interface{} {
switch in := in.(type) {
case sops.TreeBranch:
return store.treeBranchToYamlMap(in)
case []interface{}:
var out []interface{}
for _, v := range in {
out = append(out, store.treeValueToYamlValue(v))
}
return out
default:
return in
}
}
func (store Store) treeBranchToYamlMap(in sops.TreeBranch) yaml.MapSlice {
branch := make(yaml.MapSlice, 0)
for _, item := range in {
if comment, ok := item.Key.(sops.Comment); ok {
branch = append(branch, yaml.MapItem{
Key: yaml.Comment{Value: comment.Value},
Value: nil,
})
} else {
branch = append(branch, yaml.MapItem{
Key: item.Key,
Value: store.treeValueToYamlValue(item.Value),
})
}
}
return branch
}
// Marshal takes a sops tree branch and marshals it into a yaml document
func (store Store) Marshal(tree sops.TreeBranch) ([]byte, error) {
yamlMap := store.treeBranchToYamlMap(tree)
out, err := (&yaml.YAMLMarshaler{Indent: 4}).Marshal(yamlMap)
if err != nil {
return nil, fmt.Errorf("Error marshaling to yaml: %s", err)
}
return out, nil
}
// MarshalWithMetadata takes a sops tree branch and metadata and marshals them into a yaml document
func (store Store) MarshalWithMetadata(tree sops.TreeBranch, metadata sops.Metadata) ([]byte, error) {
yamlMap := store.treeBranchToYamlMap(tree)
yamlMap = append(yamlMap, yaml.MapItem{Key: "sops", Value: stores.MetadataFromInternal(metadata)})
out, err := (&yaml.YAMLMarshaler{Indent: 4}).Marshal(yamlMap)
if err != nil {
return nil, fmt.Errorf("Error marshaling to yaml: %s", err)
}
return out, nil
}
// MarshalInterface takes any value and marshals it into a yaml document
func (store Store) MarshalValue(v interface{}) ([]byte, error) {
v = store.treeValueToYamlValue(v)
return (&yaml.YAMLMarshaler{Indent: 4}).Marshal(v)
}
// UnmarshalMetadata takes a yaml document as a string and extracts sops' metadata from it
func (s *Store) UnmarshalMetadata(in []byte) (*sops.Metadata, error) {
file := stores.SopsFile{}
err := yaml.Unmarshal(in, &file)
if err != nil {
return nil, fmt.Errorf("Error unmarshalling input yaml: %s", err)
}
return file.Metadata.ToInternal()
}