Skip to content

Commit

Permalink
Implement secret-rotate hook
Browse files Browse the repository at this point in the history
  • Loading branch information
wallyworld committed Sep 17, 2021
1 parent 2a2ad2a commit ef86ca6
Show file tree
Hide file tree
Showing 53 changed files with 1,526 additions and 321 deletions.
56 changes: 49 additions & 7 deletions api/secretsmanager/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
package secretsmanager

import (
"strings"
"time"

"github.com/juju/errors"

"github.com/juju/juju/api/base"
Expand Down Expand Up @@ -71,7 +74,11 @@ func (c *Client) Create(cfg *secrets.SecretConfig, secretType secrets.SecretType
}

// Update updates an existing secret value and/or config like rotate interval.
func (c *Client) Update(URL *secrets.URL, cfg *secrets.SecretConfig, value secrets.SecretValue) (string, error) {
func (c *Client) Update(url string, cfg *secrets.SecretConfig, value secrets.SecretValue) (string, error) {
secretUrl, err := secrets.ParseURL(url)
if err != nil {
return "", errors.Trace(err)
}
if err := cfg.Validate(); err != nil {
return "", errors.Trace(err)
}
Expand All @@ -87,7 +94,7 @@ func (c *Client) Update(URL *secrets.URL, cfg *secrets.SecretConfig, value secre
var results params.StringResults

arg := params.UpdateSecretArg{
URL: URL.ID(),
URL: secretUrl.ID(),
RotateInterval: cfg.RotateInterval,
Description: cfg.Description,
Tags: cfg.Tags,
Expand All @@ -113,15 +120,22 @@ func (c *Client) Update(URL *secrets.URL, cfg *secrets.SecretConfig, value secre
}

// GetValue returns the value of a secret.
func (c *Client) GetValue(ID string) (secrets.SecretValue, error) {
//TODO(wallyworld) - validate ID format
func (c *Client) GetValue(urlOrId string) (secrets.SecretValue, error) {
arg := params.GetSecretArg{}
if strings.HasPrefix(urlOrId, secrets.SecretScheme+"://") {
secretUrl, err := secrets.ParseURL(urlOrId)
if err != nil {
return nil, errors.Trace(err)
}
arg.URL = secretUrl.ID()
} else {
arg.ID = urlOrId
}

var results params.SecretValueResults

if err := c.facade.FacadeCall("GetSecretValues", params.GetSecretArgs{
Args: []params.GetSecretArg{{
ID: ID,
}},
Args: []params.GetSecretArg{arg},
}, &results); err != nil {
return nil, errors.Trace(err)
}
Expand Down Expand Up @@ -156,3 +170,31 @@ func (c *Client) WatchSecretsRotationChanges(ownerTag string) (watcher.SecretRot
w := apiwatcher.NewSecretsRotationWatcher(c.facade.RawAPICaller(), result)
return w, nil
}

// SecretRotated records when a secret was last rotated.
func (c *Client) SecretRotated(url string, when time.Time) error {
secretUrl, err := secrets.ParseURL(url)
if err != nil {
return errors.Trace(err)
}

var results params.ErrorResults
args := params.SecretRotatedArgs{
Args: []params.SecretRotatedArg{{
URL: secretUrl.ID(),
When: when,
}},
}
err = c.facade.FacadeCall("SecretsRotated", args, &results)
if err != nil {
return errors.Trace(err)
}
if len(results.Results) != 1 {
return errors.Errorf("expected 1 result, got %d", len(results.Results))
}
result := results.Results[0]
if result.Error != nil {
return result.Error
}
return nil
}
62 changes: 56 additions & 6 deletions api/secretsmanager/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,7 @@ func (s *SecretsSuite) TestUpdateSecret(c *gc.C) {
cfg.Description = stringPtr("my secret")
cfg.Status = statusPtr(secrets.StatusActive)
cfg.Tags = tagPtr(map[string]string{"foo": "bar"})
URL := secrets.NewSimpleURL("app/foo")
result, err := client.Update(URL, cfg, value)
result, err := client.Update("secret://app/foo", cfg, value)
c.Assert(err, jc.ErrorIsNil)
c.Assert(result, gc.Equals, "secret://app/foo?revision=2")
}
Expand All @@ -157,21 +156,20 @@ func (s *SecretsSuite) TestUpdateSecretsError(c *gc.C) {
})
client := secretsmanager.NewClient(apiCaller)
value := secrets.NewSecretValue(nil)
URL := secrets.NewSimpleURL("app/foo")
result, err := client.Update(URL, secrets.NewSecretConfig("app", "password"), value)
result, err := client.Update("secret://app/foo", secrets.NewSecretConfig("app", "password"), value)
c.Assert(err, gc.ErrorMatches, "boom")
c.Assert(result, gc.Equals, "")
}

func (s *SecretsSuite) TestGetSecret(c *gc.C) {
func (s *SecretsSuite) TestGetSecretByURL(c *gc.C) {
apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
c.Check(objType, gc.Equals, "SecretsManager")
c.Check(version, gc.Equals, 0)
c.Check(id, gc.Equals, "")
c.Check(request, gc.Equals, "GetSecretValues")
c.Check(arg, gc.DeepEquals, params.GetSecretArgs{
Args: []params.GetSecretArg{{
ID: "secret://app/foo",
URL: "secret://app/foo",
}},
})
c.Assert(result, gc.FitsTypeOf, &params.SecretValueResults{})
Expand All @@ -189,6 +187,32 @@ func (s *SecretsSuite) TestGetSecret(c *gc.C) {
c.Assert(result, jc.DeepEquals, value)
}

func (s *SecretsSuite) TestGetSecretByID(c *gc.C) {
apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
c.Check(objType, gc.Equals, "SecretsManager")
c.Check(version, gc.Equals, 0)
c.Check(id, gc.Equals, "")
c.Check(request, gc.Equals, "GetSecretValues")
c.Check(arg, gc.DeepEquals, params.GetSecretArgs{
Args: []params.GetSecretArg{{
ID: "1234",
}},
})
c.Assert(result, gc.FitsTypeOf, &params.SecretValueResults{})
*(result.(*params.SecretValueResults)) = params.SecretValueResults{
[]params.SecretValueResult{{
Data: map[string]string{"foo": "bar"},
}},
}
return nil
})
client := secretsmanager.NewClient(apiCaller)
result, err := client.GetValue("1234")
c.Assert(err, jc.ErrorIsNil)
value := secrets.NewSecretValue(map[string]string{"foo": "bar"})
c.Assert(result, jc.DeepEquals, value)
}

func (s *SecretsSuite) TestGetSecretsError(c *gc.C) {
apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
*(result.(*params.SecretValueResults)) = params.SecretValueResults{
Expand Down Expand Up @@ -225,3 +249,29 @@ func (s *SecretsSuite) TestWatchSecretsRotationChanges(c *gc.C) {
_, err := client.WatchSecretsRotationChanges("application-app")
c.Assert(err, gc.ErrorMatches, "FAIL")
}

func (s *SecretsSuite) TestSecretRotated(c *gc.C) {
now := time.Now()
apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
c.Check(objType, gc.Equals, "SecretsManager")
c.Check(version, gc.Equals, 0)
c.Check(id, gc.Equals, "")
c.Check(request, gc.Equals, "SecretsRotated")
c.Check(arg, gc.DeepEquals, params.SecretRotatedArgs{
Args: []params.SecretRotatedArg{{
URL: "secret://app/foo",
When: now,
}},
})
c.Assert(result, gc.FitsTypeOf, &params.ErrorResults{})
*(result.(*params.ErrorResults)) = params.ErrorResults{
[]params.ErrorResult{{
Error: &params.Error{Message: "boom"},
}},
}
return nil
})
client := secretsmanager.NewClient(apiCaller)
err := client.SecretRotated("secret://app/foo", now)
c.Assert(err, gc.ErrorMatches, "boom")
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

This file was deleted.

18 changes: 9 additions & 9 deletions apiserver/facades/agent/secretsmanager/package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ func TestPackage(t *testing.T) {
}

//go:generate go run github.com/golang/mock/mockgen -package mocks -destination mocks/secretservice.go github.com/juju/juju/secrets SecretsService
//go:generate go run github.com/golang/mock/mockgen -package mocks -destination mocks/secretswatcherservice.go github.com/juju/juju/apiserver/facades/agent/secretsmanager SecretsWatcher
//go:generate go run github.com/golang/mock/mockgen -package mocks -destination mocks/secretsrotationservice.go github.com/juju/juju/apiserver/facades/agent/secretsmanager SecretsRotation
//go:generate go run github.com/golang/mock/mockgen -package mocks -destination mocks/secretsrotationwatcher.go github.com/juju/juju/state SecretsRotationWatcher

func NewTestAPI(
authorizer facade.Authorizer,
resources facade.Resources,
service secrets.SecretsService,
secretsWatcher SecretsWatcher,
secretsRotation SecretsRotation,
accessSecret common.GetAuthFunc,
ownerTag names.Tag,
) (*SecretsManagerAPI, error) {
Expand All @@ -37,12 +37,12 @@ func NewTestAPI(
}

return &SecretsManagerAPI{
authOwner: ownerTag,
controllerUUID: coretesting.ControllerTag.Id(),
modelUUID: coretesting.ModelTag.Id(),
resources: resources,
secretsService: service,
secretsWatcher: secretsWatcher,
accessSecret: accessSecret,
authOwner: ownerTag,
controllerUUID: coretesting.ControllerTag.Id(),
modelUUID: coretesting.ModelTag.Id(),
resources: resources,
secretsService: service,
secretsRotation: secretsRotation,
accessSecret: accessSecret,
}, nil
}
Loading

0 comments on commit ef86ca6

Please sign in to comment.