Co-authored-by: Young Bu Park <youngp@microsoft.com>
This commit is contained in:
parent
f46249c554
commit
3221db9709
|
|
@ -3,7 +3,7 @@
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
package jsonsecretstore
|
package localsecretstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
@ -18,41 +18,31 @@ import (
|
||||||
"github.com/dapr/dapr/pkg/logger"
|
"github.com/dapr/dapr/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
type localSecretStoreMetaData struct {
|
||||||
/* #nosec */
|
|
||||||
enableSecretStoreVariable = "DAPR_ENABLE_JSON_SECRET_STORE"
|
|
||||||
)
|
|
||||||
|
|
||||||
type jsonSecretStoreMetaData struct {
|
|
||||||
SecretsFile string `json:"secretsFile"`
|
SecretsFile string `json:"secretsFile"`
|
||||||
NestedSeparator string `json:"nestedSeparator"`
|
NestedSeparator string `json:"nestedSeparator"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type jsonSecretStore struct {
|
type localSecretStore struct {
|
||||||
secretsFile string
|
secretsFile string
|
||||||
nestedSeparator string
|
nestedSeparator string
|
||||||
currenContext []string
|
currenContext []string
|
||||||
currentPath string
|
currentPath string
|
||||||
secrets map[string]string
|
secrets map[string]string
|
||||||
readJSONFileFn func(secretsFile string) (map[string]interface{}, error)
|
readLocalFileFn func(secretsFile string) (map[string]interface{}, error)
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewJSONSecretStore returns a new JSON secret store
|
// NewLocalSecretStore returns a new Local secret store
|
||||||
func NewJSONSecretStore(logger logger.Logger) secretstores.SecretStore {
|
func NewLocalSecretStore(logger logger.Logger) secretstores.SecretStore {
|
||||||
return &jsonSecretStore{
|
return &localSecretStore{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init creates a JSON secret store
|
// Init creates a Local secret store
|
||||||
func (j *jsonSecretStore) Init(metadata secretstores.Metadata) error {
|
func (j *localSecretStore) Init(metadata secretstores.Metadata) error {
|
||||||
enable := os.Getenv(enableSecretStoreVariable)
|
meta, err := j.getLocalSecretStoreMetadata(metadata)
|
||||||
if enable != "1" {
|
|
||||||
return fmt.Errorf("jsonsecretstore must be explicitly enabled setting %s environment variable value to 1", enableSecretStoreVariable)
|
|
||||||
}
|
|
||||||
|
|
||||||
meta, err := j.getJSONSecretStoreMetadata(metadata)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -61,13 +51,13 @@ func (j *jsonSecretStore) Init(metadata secretstores.Metadata) error {
|
||||||
j.nestedSeparator = ":"
|
j.nestedSeparator = ":"
|
||||||
}
|
}
|
||||||
|
|
||||||
if j.readJSONFileFn == nil {
|
if j.readLocalFileFn == nil {
|
||||||
j.readJSONFileFn = j.readJSONFile
|
j.readLocalFileFn = j.readLocalFile
|
||||||
}
|
}
|
||||||
|
|
||||||
j.secrets = map[string]string{}
|
j.secrets = map[string]string{}
|
||||||
|
|
||||||
jsonConfig, err := j.readJSONFileFn(meta.SecretsFile)
|
jsonConfig, err := j.readLocalFileFn(meta.SecretsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +68,7 @@ func (j *jsonSecretStore) Init(metadata secretstores.Metadata) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSecret retrieves a secret using a key and returns a map of decrypted string/string values
|
// GetSecret retrieves a secret using a key and returns a map of decrypted string/string values
|
||||||
func (j *jsonSecretStore) GetSecret(req secretstores.GetSecretRequest) (secretstores.GetSecretResponse, error) {
|
func (j *localSecretStore) GetSecret(req secretstores.GetSecretRequest) (secretstores.GetSecretResponse, error) {
|
||||||
secretValue, exists := j.secrets[req.Name]
|
secretValue, exists := j.secrets[req.Name]
|
||||||
if !exists {
|
if !exists {
|
||||||
return secretstores.GetSecretResponse{}, fmt.Errorf("secret %s not found", req.Name)
|
return secretstores.GetSecretResponse{}, fmt.Errorf("secret %s not found", req.Name)
|
||||||
|
|
@ -91,7 +81,7 @@ func (j *jsonSecretStore) GetSecret(req secretstores.GetSecretRequest) (secretst
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jsonSecretStore) visitJSONObject(jsonConfig map[string]interface{}) error {
|
func (j *localSecretStore) visitJSONObject(jsonConfig map[string]interface{}) error {
|
||||||
for key, element := range jsonConfig {
|
for key, element := range jsonConfig {
|
||||||
j.enterContext(key)
|
j.enterContext(key)
|
||||||
err := j.visitProperty(element)
|
err := j.visitProperty(element)
|
||||||
|
|
@ -103,12 +93,12 @@ func (j *jsonSecretStore) visitJSONObject(jsonConfig map[string]interface{}) err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jsonSecretStore) enterContext(context string) {
|
func (j *localSecretStore) enterContext(context string) {
|
||||||
j.currenContext = append(j.currenContext, context)
|
j.currenContext = append(j.currenContext, context)
|
||||||
j.currentPath = j.combine(j.currenContext)
|
j.currentPath = j.combine(j.currenContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jsonSecretStore) visitPrimitive(context string) error {
|
func (j *localSecretStore) visitPrimitive(context string) error {
|
||||||
key := j.currentPath
|
key := j.currentPath
|
||||||
_, exists := j.secrets[key]
|
_, exists := j.secrets[key]
|
||||||
|
|
||||||
|
|
@ -121,7 +111,7 @@ func (j *jsonSecretStore) visitPrimitive(context string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jsonSecretStore) visitArray(array []interface{}) error {
|
func (j *localSecretStore) visitArray(array []interface{}) error {
|
||||||
for i := 0; i < len(array); i++ {
|
for i := 0; i < len(array); i++ {
|
||||||
j.enterContext(strconv.Itoa(i))
|
j.enterContext(strconv.Itoa(i))
|
||||||
err := j.visitProperty(array[i])
|
err := j.visitProperty(array[i])
|
||||||
|
|
@ -133,7 +123,7 @@ func (j *jsonSecretStore) visitArray(array []interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jsonSecretStore) visitProperty(property interface{}) error {
|
func (j *localSecretStore) visitProperty(property interface{}) error {
|
||||||
switch v := property.(type) {
|
switch v := property.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
return j.visitJSONObject(v)
|
return j.visitJSONObject(v)
|
||||||
|
|
@ -146,39 +136,39 @@ func (j *jsonSecretStore) visitProperty(property interface{}) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jsonSecretStore) exitContext() {
|
func (j *localSecretStore) exitContext() {
|
||||||
j.pop()
|
j.pop()
|
||||||
j.currentPath = j.combine(j.currenContext)
|
j.currentPath = j.combine(j.currenContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jsonSecretStore) pop() {
|
func (j *localSecretStore) pop() {
|
||||||
n := len(j.currenContext) - 1 // Top element
|
n := len(j.currenContext) - 1 // Top element
|
||||||
j.currenContext[n] = ""
|
j.currenContext[n] = ""
|
||||||
j.currenContext = j.currenContext[:n] // Pop
|
j.currenContext = j.currenContext[:n] // Pop
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jsonSecretStore) combine(values []string) string {
|
func (j *localSecretStore) combine(values []string) string {
|
||||||
return strings.Join(values, j.nestedSeparator)
|
return strings.Join(values, j.nestedSeparator)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jsonSecretStore) getJSONSecretStoreMetadata(spec secretstores.Metadata) (*jsonSecretStoreMetaData, error) {
|
func (j *localSecretStore) getLocalSecretStoreMetadata(spec secretstores.Metadata) (*localSecretStoreMetaData, error) {
|
||||||
b, err := json.Marshal(spec.Properties)
|
b, err := json.Marshal(spec.Properties)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var meta jsonSecretStoreMetaData
|
var meta localSecretStoreMetaData
|
||||||
err = json.Unmarshal(b, &meta)
|
err = json.Unmarshal(b, &meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if meta.SecretsFile == "" {
|
if meta.SecretsFile == "" {
|
||||||
return nil, fmt.Errorf("missing JSON secrets file in metadata")
|
return nil, fmt.Errorf("missing local secrets file in metadata")
|
||||||
}
|
}
|
||||||
return &meta, nil
|
return &meta, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jsonSecretStore) readJSONFile(secretsFile string) (map[string]interface{}, error) {
|
func (j *localSecretStore) readLocalFile(secretsFile string) (map[string]interface{}, error) {
|
||||||
j.secretsFile = secretsFile
|
j.secretsFile = secretsFile
|
||||||
jsonFile, err := os.Open(secretsFile)
|
jsonFile, err := os.Open(secretsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -2,11 +2,10 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
// Copyright (c) Microsoft Corporation.
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
package jsonsecretstore
|
package localsecretstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/dapr/components-contrib/secretstores"
|
"github.com/dapr/components-contrib/secretstores"
|
||||||
|
|
@ -17,11 +16,10 @@ import (
|
||||||
const secretValue = "secret"
|
const secretValue = "secret"
|
||||||
|
|
||||||
func TestInit(t *testing.T) {
|
func TestInit(t *testing.T) {
|
||||||
os.Setenv(enableSecretStoreVariable, "1")
|
|
||||||
m := secretstores.Metadata{}
|
m := secretstores.Metadata{}
|
||||||
s := jsonSecretStore{
|
s := localSecretStore{
|
||||||
logger: logger.NewLogger("test"),
|
logger: logger.NewLogger("test"),
|
||||||
readJSONFileFn: func(secretsFile string) (map[string]interface{}, error) {
|
readLocalFileFn: func(secretsFile string) (map[string]interface{}, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -40,30 +38,19 @@ func TestInit(t *testing.T) {
|
||||||
}
|
}
|
||||||
err := s.Init(m)
|
err := s.Init(m)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
assert.Equal(t, err, fmt.Errorf("missing JSON secrets file in metadata"))
|
assert.Equal(t, err, fmt.Errorf("missing local secrets file in metadata"))
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Init with disabled store", func(t *testing.T) {
|
|
||||||
os.Setenv(enableSecretStoreVariable, "0")
|
|
||||||
m.Properties = map[string]string{
|
|
||||||
"Dummy": "a",
|
|
||||||
}
|
|
||||||
err := s.Init(m)
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
assert.Equal(t, err, fmt.Errorf("jsonsecretstore must be explicitly enabled setting %s environment variable value to 1", enableSecretStoreVariable))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSecret(t *testing.T) {
|
func TestGetSecret(t *testing.T) {
|
||||||
os.Setenv(enableSecretStoreVariable, "1")
|
|
||||||
m := secretstores.Metadata{}
|
m := secretstores.Metadata{}
|
||||||
m.Properties = map[string]string{
|
m.Properties = map[string]string{
|
||||||
"SecretsFile": "a",
|
"SecretsFile": "a",
|
||||||
"NestedSeparator": "a",
|
"NestedSeparator": "a",
|
||||||
}
|
}
|
||||||
s := jsonSecretStore{
|
s := localSecretStore{
|
||||||
logger: logger.NewLogger("test"),
|
logger: logger.NewLogger("test"),
|
||||||
readJSONFileFn: func(secretsFile string) (map[string]interface{}, error) {
|
readLocalFileFn: func(secretsFile string) (map[string]interface{}, error) {
|
||||||
secrets := make(map[string]interface{})
|
secrets := make(map[string]interface{})
|
||||||
secrets["secret"] = secretValue
|
secrets["secret"] = secretValue
|
||||||
return secrets, nil
|
return secrets, nil
|
||||||
Loading…
Reference in New Issue