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

commit 7b7a02dda794d91aa8339397b4ef29b014dba384
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        | 115 ++++++++++++++++++++++++++++++++++++++
 libs/keystore/filestore_test.go   |  77 +++++++++++++++++++++++++
 libs/keystore/keystore.go         |  42 ++++++++++++++
 libs/keystore/memorystore.go      |  64 +++++++++++++++++++++
 libs/keystore/memorystore_test.go |  44 +++++++++++++++
 5 files changed, 342 insertions(+)

diff --git a/libs/keystore/filestore.go b/libs/keystore/filestore.go
new file mode 100644
index 0000000..9c8f2e9
--- /dev/null
+++ b/libs/keystore/filestore.go
@@ -0,0 +1,115 @@
+// 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"
+)
+
+// 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..0bbb189
--- /dev/null
+++ b/libs/keystore/filestore_test.go
@@ -0,0 +1,77 @@
+// 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 (
+       "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..8f4da80
--- /dev/null
+++ b/libs/keystore/keystore.go
@@ -0,0 +1,42 @@
+// 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
+
+import "github.com/pkg/errors"
+
+var (
+       // ErrKeyNotFound is returned when a key is not found in the store
+       ErrKeyNotFound = errors.New("Key not found")
+)
+
+// 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
+}
diff --git a/libs/keystore/memorystore.go b/libs/keystore/memorystore.go
new file mode 100644
index 0000000..58e4470
--- /dev/null
+++ b/libs/keystore/memorystore.go
@@ -0,0 +1,64 @@
+// 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 (
+       "sync"
+)
+
+// MemoryStore is the in-memory implementation of key store
+type MemoryStore struct {
+       sync.RWMutex
+       keys map[string][]byte
+}
+
+// NewMemoryStore creates a new MemoryStore
+func NewMemoryStore() (Store, error) {
+       return &MemoryStore{
+               keys: map[string][]byte{},
+       }, nil
+}
+
+// Set stores multiple keys at once
+func (f *MemoryStore) Set(keys ...Key) error {
+       f.Lock()
+       defer f.Unlock()
+
+       for _, key := range keys {
+               f.keys[key.Name] = make([]byte, len(key.Key))
+               copy(f.keys[key.Name], key.Key)
+       }
+
+       return nil
+}
+
+// Get retrieves multiple keys
+func (f *MemoryStore) Get(names ...string) (map[string][]byte, error) {
+       f.RLock()
+       defer f.RUnlock()
+       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
+}
diff --git a/libs/keystore/memorystore_test.go 
b/libs/keystore/memorystore_test.go
new file mode 100644
index 0000000..2577102
--- /dev/null
+++ b/libs/keystore/memorystore_test.go
@@ -0,0 +1,44 @@
+// 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 (
+       "bytes"
+       "testing"
+)
+
+func TestMemoryStore(t *testing.T) {
+       keys := []Key{{"key1", []byte{1}}, {"key2", []byte{1, 2}}, {"key3", 
[]byte{1, 2, 3}}}
+       keynames := []string{"key1", "key2", "key3"}
+
+       fs, err := NewMemoryStore()
+       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])
+               }
+       }
+
+}

Reply via email to