This is an automated email from the ASF dual-hosted git repository.
smihaylov pushed a commit to branch create-identity
in repository https://gitbox.apache.org/repos/asf/incubator-milagro-dta.git
The following commit(s) were added to refs/heads/create-identity by this push:
new 19b4a2b KeyStore interface to keep secrets
19b4a2b is described below
commit 19b4a2bb7d508692df1f27d91b7c4b79a2fd8e5d
Author: Stanislav Mihaylov <[email protected]>
AuthorDate: Wed Sep 25 11:06:40 2019 +0300
KeyStore interface to keep secrets
File KeyStore implementation to keep secrets in a json file
---
libs/keystore/filestore.go | 120 ++++++++++++++++++++++++++++++++++++++++
libs/keystore/filestore_test.go | 60 ++++++++++++++++++++
libs/keystore/keystore.go | 35 ++++++++++++
3 files changed, 215 insertions(+)
diff --git a/libs/keystore/filestore.go b/libs/keystore/filestore.go
new file mode 100644
index 0000000..12a31db
--- /dev/null
+++ b/libs/keystore/filestore.go
@@ -0,0 +1,120 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package keystore
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "sync"
+
+ "github.com/pkg/errors"
+)
+
+var (
+ // ErrKeyNotFound is returned when a key is not found in the store
+ ErrKeyNotFound = errors.New("Key not found")
+)
+
+// FileStore is the key Store implementation storing the keys in a file
+type FileStore struct {
+ sync.RWMutex
+ filePath string
+ keys map[string][]byte
+}
+
+// NewFileStore creates a new FileStore
+func NewFileStore(filePath string) (Store, error) {
+ fs := &FileStore{
+ filePath: filePath,
+ keys: map[string][]byte{},
+ }
+
+ if err := fs.loadKeys(); err != nil {
+ return nil, err
+ }
+
+ return fs, nil
+}
+
+// Set stores multiple keys at once
+func (f *FileStore) Set(keys ...Key) error {
+ for _, key := range keys {
+ f.keys[key.Name] = make([]byte, len(key.Key))
+ copy(f.keys[key.Name], key.Key)
+ }
+
+ return f.storeKeys()
+}
+
+// Get retrieves multiple keys
+func (f *FileStore) Get(names ...string) (map[string][]byte, error) {
+ keys := map[string][]byte{}
+ for _, name := range names {
+ k, ok := f.keys[name]
+ if !ok {
+ return nil, ErrKeyNotFound
+ }
+ keys[name] = make([]byte, len(k))
+ copy(keys[name], k)
+ }
+ return keys, nil
+}
+
+// TODO: Lock the file
+
+func (f *FileStore) loadKeys() error {
+ f.RLock()
+ defer f.RUnlock()
+ rawKeys, err := ioutil.ReadFile(f.filePath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return errors.Wrap(err, "Load keys")
+ }
+
+ return json.Unmarshal(rawKeys, &(f).keys)
+}
+
+func (f *FileStore) storeKeys() error {
+ f.Lock()
+ defer f.Unlock()
+ rawKeys, err := json.Marshal(f.keys)
+ if err != nil {
+ return err
+ }
+
+ // Get the file permissions
+ var perm os.FileMode
+ fi, err := os.Stat(f.filePath)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ return errors.Wrap(err, "Get key file permissions")
+ }
+ perm = 0600
+ } else {
+ perm = fi.Mode().Perm()
+ }
+
+ if err := ioutil.WriteFile(f.filePath, rawKeys, perm); err != nil {
+ return errors.Wrap(err, "Store keys")
+ }
+
+ return nil
+}
diff --git a/libs/keystore/filestore_test.go b/libs/keystore/filestore_test.go
new file mode 100644
index 0000000..16251eb
--- /dev/null
+++ b/libs/keystore/filestore_test.go
@@ -0,0 +1,60 @@
+package keystore
+
+import (
+ "bytes"
+ "crypto/rand"
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+)
+
+func TestFileStore(t *testing.T) {
+ keys := []Key{{"key1", []byte{1}}, {"key2", []byte{1, 2}}, {"key3",
[]byte{1, 2, 3}}}
+ keynames := []string{"key1", "key2", "key3"}
+
+ fn := tmpFileName()
+ defer func() {
+ if err := os.Remove(fn); err != nil {
+ t.Logf("Warning! Temp file could not be deleted (%v):
%v", err, fn)
+ }
+ }()
+
+ fs, err := NewFileStore(fn)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fs.Set(keys...)
+ skeys, err := fs.Get(keynames...)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, k := range keys {
+ if !bytes.Equal(k.Key, skeys[k.Name]) {
+ t.Errorf("Key not match: %v. Expected: %v, Found: %v",
k.Name, k.Key, skeys[k.Name])
+ }
+ }
+
+ fs1, err := NewFileStore(fn)
+ if err != nil {
+ t.Fatal(err)
+ }
+ s1keys, err := fs1.Get(keynames...)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, k := range keys {
+ if !bytes.Equal(k.Key, s1keys[k.Name]) {
+ t.Errorf("Key not match: %v. Expected: %v, Found: %v",
k.Name, k.Key, s1keys[k.Name])
+ }
+ }
+}
+
+func tmpFileName() string {
+ rnd := make([]byte, 8)
+ rand.Read(rnd)
+ ts := time.Now().UnixNano()
+
+ return filepath.Join(os.TempDir(), fmt.Sprintf("keystore-%v-%x.tmp",
ts, rnd))
+}
diff --git a/libs/keystore/keystore.go b/libs/keystore/keystore.go
new file mode 100644
index 0000000..7d63571
--- /dev/null
+++ b/libs/keystore/keystore.go
@@ -0,0 +1,35 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/*
+Package keystore - keep secrets
+*/
+package keystore
+
+// Store is the keystore interface
+type Store interface {
+ // Set stores multiple keys at once
+ Set(...Key) error
+ // Get retrieves multiple keys
+ Get(...string) (map[string][]byte, error)
+}
+
+// Key represent a single key
+type Key struct {
+ Name string
+ Key []byte
+}