Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add local dev secret plugin #109

Merged
merged 1 commit into from
Aug 4, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ require (
github.com/aws/aws-sdk-go v1.36.12
github.com/golang/protobuf v1.5.2
github.com/golang/snappy v0.0.3 // indirect
github.com/google/addlicense v0.0.0-20210727174409-874627749a46
github.com/google/addlicense v0.0.0-20210729153508-ef04bb38a16b
github.com/google/uuid v1.1.2
github.com/googleapis/gax-go/v2 v2.0.5
github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 // indirect
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -155,6 +155,8 @@ github.com/google/addlicense v0.0.0-20210428195630-6d92264d7170 h1:jLUa4MO3autxl
github.com/google/addlicense v0.0.0-20210428195630-6d92264d7170/go.mod h1:EMjYTRimagHs1FwlIqKyX3wAM0u3rA+McvlIIWmSamA=
github.com/google/addlicense v0.0.0-20210727174409-874627749a46 h1:1locMH9PVZH3LXvogcvdTxf2/9J4YT/9W3BSXrTN4/U=
github.com/google/addlicense v0.0.0-20210727174409-874627749a46/go.mod h1:EMjYTRimagHs1FwlIqKyX3wAM0u3rA+McvlIIWmSamA=
github.com/google/addlicense v0.0.0-20210729153508-ef04bb38a16b h1:KwI0NOpYd3rzKojfjeRerF7rzjeTwvJARVsgGf5TWmY=
github.com/google/addlicense v0.0.0-20210729153508-ef04bb38a16b/go.mod h1:EMjYTRimagHs1FwlIqKyX3wAM0u3rA+McvlIIWmSamA=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
123 changes: 123 additions & 0 deletions pkg/plugins/secret/dev/dev.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright 2021 Nitric Pty Ltd.
//
// Licensed 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 secret_service

import (
"bufio"
"fmt"
"io/ioutil"
"os"
"strings"

"github.com/google/uuid"
"github.com/nitric-dev/membrane/pkg/plugins/secret"
"github.com/nitric-dev/membrane/pkg/utils"
)

const DEFAULT_DIR = ".nitric/secrets/"

type DevSecretService struct {
secret.UnimplementedSecretPlugin
secDir string
}

func (s *DevSecretService) secretFileName(sec *secret.Secret, v string) string {
return fmt.Sprintf("%s/%s_%s.txt", s.secDir, sec.Name, v)
}

func (s *DevSecretService) Put(sec *secret.Secret, val []byte) (*secret.SecretPutResponse, error) {
if sec == nil {
return nil, fmt.Errorf("provide non-empty secret")
}
if len(sec.Name) == 0 {
return nil, fmt.Errorf("provide non-blank secret name")
}
if len(val) == 0 {
return nil, fmt.Errorf("provide non-blank secret value")
}

var versionId = uuid.New().String()
//Creates a new file in the form:
// DIR/Name_Version.txt
file, err := os.Create(s.secretFileName(sec, versionId))
if err != nil {
return nil, fmt.Errorf("error creating secret store: %v", err)
}
writer := bufio.NewWriter(file)
writer.WriteString(string(val))
writer.Flush()

//Creates a new file as latest
latestFile, err := os.Create(s.secretFileName(sec, "latest"))
if err != nil {
return nil, fmt.Errorf("error creating latest secret: %v", err)
}
latestWriter := bufio.NewWriter(latestFile)
latestWriter.WriteString(string(val))
latestWriter.WriteString("," + versionId)
latestWriter.Flush()

return &secret.SecretPutResponse{
SecretVersion: &secret.SecretVersion{
Secret: &secret.Secret{
Name: sec.Name,
},
Version: versionId,
},
}, nil
}

func (s *DevSecretService) Access(sv *secret.SecretVersion) (*secret.SecretAccessResponse, error) {
if sv.Secret.Name == "" {
return nil, fmt.Errorf("provide non-blank name")
}
if sv.Version == "" {
return nil, fmt.Errorf("provide non-blank version")
}

content, err := ioutil.ReadFile(s.secretFileName(sv.Secret, sv.Version))
if err != nil {
return nil, fmt.Errorf("error reading secret store: %v", err)
}

splitContent := strings.Split(string(content), ",")
return &secret.SecretAccessResponse{
SecretVersion: &secret.SecretVersion{
Secret: &secret.Secret{
Name: sv.Secret.Name,
},
Version: splitContent[len(splitContent)-1],
},
Value: []byte(splitContent[0]),
}, nil
}

//Create new secret store
func New() (secret.SecretService, error) {
secDir := utils.GetEnv("LOCAL_SEC_DIR", DEFAULT_DIR)

//Check whether file exists
_, err := os.Stat(secDir)
if os.IsNotExist(err) {
//Make directory if not present
err := os.MkdirAll(secDir, 0777)
if err != nil {
return nil, err
}
}
return &DevSecretService{
secDir: secDir,
}, nil
}
27 changes: 27 additions & 0 deletions pkg/plugins/secret/dev/dev_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2021 Nitric Pty Ltd.
//
// Licensed 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 secret_service_test

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestPubsub(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Dev Secret Plugin Suite")
}
148 changes: 148 additions & 0 deletions pkg/plugins/secret/dev/dev_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Copyright 2021 Nitric Pty Ltd.
//
// Licensed 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 secret_service_test

import (
"os"

"github.com/nitric-dev/membrane/pkg/plugins/secret"
secretPlugin "github.com/nitric-dev/membrane/pkg/plugins/secret/dev"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("Dev Secret Manager", func() {
os.Setenv("LOCAL_SEC_DIR", "./.nitric/")

AfterSuite(func() {
// Cleanup default secrect directory
os.RemoveAll("./.nitric/")
})

testSecret := secret.Secret{
Name: "Test",
}
testSecretVal := []byte("Super Secret Message")
When("Put", func() {
When("Putting a secret to a non-existent secret", func() {
secretPlugin, _ := secretPlugin.New()
It("Should successfully store a secret", func() {
response, err := secretPlugin.Put(&testSecret, testSecretVal)
By("Not returning an error")
Expect(err).ShouldNot(HaveOccurred())

By("Returning a non nil response")
Expect(response).ShouldNot(BeNil())
})
})
When("Putting a secret to an existing secret", func() {
secretPlugin, _ := secretPlugin.New()
It("Should succesfully store a secret", func() {
response, err := secretPlugin.Put(&testSecret, testSecretVal)
By("Not returning an error")
Expect(err).ShouldNot(HaveOccurred())

By("Returning a non nil response")
Expect(response).ShouldNot(BeNil())
})
})
When("Putting a secret with an empty name", func() {
secretPlugin, _ := secretPlugin.New()
It("Should throw an error", func() {
emptySecretName := &secret.Secret{}
response, err := secretPlugin.Put(emptySecretName, testSecretVal)
By("Returning an error")
Expect(err).Should(HaveOccurred())

By("Returning a nil response")
Expect(response).Should(BeNil())
})
})
})
tjholm marked this conversation as resolved.
Show resolved Hide resolved
When("Get", func() {
When("Getting a secret that exists", func() {
secretPlugin, _ := secretPlugin.New()
It("Should return the secret", func() {
putResponse, _ := secretPlugin.Put(&testSecret, testSecretVal)
response, err := secretPlugin.Access(putResponse.SecretVersion)
By("Not returning an error")
Expect(err).ShouldNot(HaveOccurred())
By("Returning a response")
Expect(response.SecretVersion.Secret.Name).Should(Equal(testSecret.Name))
Expect(response.Value).Should(Equal(testSecretVal))
})
})
When("Getting the latest secret", func() {
secretPlugin, _ := secretPlugin.New()
It("Should return the latest secret", func() {
putResponse, _ := secretPlugin.Put(&testSecret, testSecretVal)
response, err := secretPlugin.Access(&secret.SecretVersion{
Secret: &secret.Secret{
Name: testSecret.Name,
},
Version: "latest",
})
By("Not returning an error")
Expect(err).ShouldNot(HaveOccurred())
By("Returning a response")
Expect(response.SecretVersion.Secret.Name).Should(Equal(testSecret.Name))
Expect(response.SecretVersion.Version).Should(Equal(putResponse.SecretVersion.Version))
Expect(response.Value).Should(Equal(testSecretVal))
})
})
When("Getting a secret that doesn't exist", func() {
secretPlugin, _ := secretPlugin.New()
It("Should return an error", func() {
response, err := secretPlugin.Access(&secret.SecretVersion{
Secret: &secret.Secret{
Name: "test-id",
},
Version: "test-version-id",
})
By("Returning an error")
Expect(err).Should(HaveOccurred())
By("Returning a nil response")
Expect(response).Should(BeNil())
})
})
When("Getting a secret with an empty id", func() {
secretPlugin, _ := secretPlugin.New()
It("Should return an error", func() {
response, err := secretPlugin.Access(&secret.SecretVersion{
Secret: &secret.Secret{},
Version: "test-version-id",
})
By("Returning an error")
Expect(err).Should(HaveOccurred())
By("Returning a nil response")
Expect(response).Should(BeNil())
})
})
When("Getting a secret with an empty version id", func() {
tjholm marked this conversation as resolved.
Show resolved Hide resolved
secretPlugin, _ := secretPlugin.New()
It("Should return an error", func() {
response, err := secretPlugin.Access(&secret.SecretVersion{
Secret: &secret.Secret{
Name: "test-id",
},
})
By("Returning an error")
Expect(err).Should(HaveOccurred())
By("Returning a nil response")
Expect(response).Should(BeNil())
})
})
})
})
3 changes: 3 additions & 0 deletions pkg/providers/dev/membrane.go
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ import (
gateway_plugin "github.com/nitric-dev/membrane/pkg/plugins/gateway/dev"
queue_service "github.com/nitric-dev/membrane/pkg/plugins/queue/dev"
boltdb_storage_service "github.com/nitric-dev/membrane/pkg/plugins/storage/boltdb"
secret_service "github.com/nitric-dev/membrane/pkg/plugins/secret/dev"
)

func main() {
@@ -35,6 +36,7 @@ func main() {
signal.Notify(term, os.Interrupt, syscall.SIGTERM)
signal.Notify(term, os.Interrupt, syscall.SIGINT)

secretPlugin, _ := secret_service.New()
documentPlugin, _ := boltdb_service.New()
eventsPlugin, _ := events_service.New()
gatewayPlugin, _ := gateway_plugin.New()
@@ -47,6 +49,7 @@ func main() {
GatewayPlugin: gatewayPlugin,
QueuePlugin: queuePlugin,
StoragePlugin: storagePlugin,
SecretPlugin: secretPlugin,
})

if err != nil {