Skip to content

Commit

Permalink
HV-1702 create postgres rotating secret (#192)
Browse files Browse the repository at this point in the history
* add create postgres rotating secret

* bump sdk version + mocks

* bump sdk version

* update postgres rotating secrets

* changelog

* Update internal/commands/vaultsecrets/secrets/create.go

Co-authored-by: Nick Cabatoff <[email protected]>

* postgres usernames as list

* postgres rotating secret create/update tests

* lint

* Apply suggestions from code review

Co-authored-by: Anton Averchenkov <[email protected]>
Co-authored-by: Nick Cabatoff <[email protected]>

* update tests

---------

Co-authored-by: Nick Cabatoff <[email protected]>
Co-authored-by: Anton Averchenkov <[email protected]>
  • Loading branch information
3 people authored Nov 20, 2024
1 parent 4377279 commit 88f300f
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 68 deletions.
3 changes: 3 additions & 0 deletions .changelog/192.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
hvs: support postgres rotating secret CRUDL
```
34 changes: 34 additions & 0 deletions internal/commands/vaultsecrets/secrets/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,14 @@ var (
},
}

postgresRotatingSecretTemplate = map[string]any{
"integration_name": "",
"rotation_policy_name": "",
"postgres_params": map[string]any{
"usernames": []string{},
},
}

awsDynamicSecretTemplate = map[string]any{
"integration_name": "",
"default_ttl": "",
Expand Down Expand Up @@ -365,6 +373,31 @@ func createRun(opts *CreateOpts) error {
return fmt.Errorf("failed to create secret with name %q: %w", opts.SecretName, err)
}

case integrations.Postgres:
req := preview_secret_service.NewCreatePostgresRotatingSecretParamsWithContext(opts.Ctx)
req.OrganizationID = opts.Profile.OrganizationID
req.ProjectID = opts.Profile.ProjectID
req.AppName = opts.AppName

var postgresBody preview_models.SecretServiceCreatePostgresRotatingSecretBody
detailBytes, err := json.Marshal(internalConfig.Details)
if err != nil {
return fmt.Errorf("error marshaling details config: %w", err)
}

err = postgresBody.UnmarshalBinary(detailBytes)
if err != nil {
return fmt.Errorf("error marshaling details config: %w", err)
}

postgresBody.Name = opts.SecretName
req.Body = &postgresBody

_, err = opts.PreviewClient.CreatePostgresRotatingSecret(req, nil)
if err != nil {
return fmt.Errorf("failed to create secret with name %q: %w", opts.SecretName, err)
}

default:
return fmt.Errorf("unsupported rotating secret provider type")
}
Expand Down Expand Up @@ -538,6 +571,7 @@ var availableRotatingSecretProviders = map[string]map[string]any{
string(integrations.MongoDBAtlas): mongoDBAtlasRotatingSecretTemplate,
string(integrations.AWS): awsRotatingSecretTemplate,
string(integrations.GCP): gcpRotatingSecretTemplate,
string(integrations.Postgres): postgresRotatingSecretTemplate,
}

var availableDynamicSecretProviders = map[string]map[string]any{
Expand Down
121 changes: 87 additions & 34 deletions internal/commands/vaultsecrets/secrets/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"path/filepath"
"testing"

"github.com/hashicorp/hcp/internal/commands/vaultsecrets/integrations"

"github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
"github.com/stretchr/testify/mock"
Expand Down Expand Up @@ -134,6 +136,7 @@ func TestCreateRun(t *testing.T) {
ErrMsg string
MockCalled bool
AugmentOpts func(*CreateOpts)
Provider integrations.IntegrationType
Input []byte
}{
{
Expand Down Expand Up @@ -186,6 +189,7 @@ func TestCreateRun(t *testing.T) {
o.Type = secretTypeRotating
},
MockCalled: true,
Provider: integrations.MongoDBAtlas,
Input: []byte(`type = "mongodb-atlas"
details = {
integration_name = "mongo-db-integration"
Expand All @@ -203,6 +207,23 @@ details = {
"collection_name" = "cn2"
}]
}
}`),
},
{
Name: "Success: Create a Postgres rotating secret",
RespErr: false,
AugmentOpts: func(o *CreateOpts) {
o.Type = secretTypeRotating
},
MockCalled: true,
Provider: integrations.Postgres,
Input: []byte(`type = "postgres"
details = {
integration_name = "postgres-integration"
rotation_policy_name = "built-in:60-days-2-active"
postgres_params = {
usernames = ["postgres_user_1", "postgres_user_2"]
}
}`),
},
{
Expand Down Expand Up @@ -310,45 +331,77 @@ details = {
}
} else if opts.Type == secretTypeRotating {
if c.MockCalled {
if c.RespErr {
pvs.EXPECT().CreateMongoDBAtlasRotatingSecret(mock.Anything, mock.Anything).Return(nil, errors.New(c.ErrMsg)).Once()
} else {
pvs.EXPECT().CreateMongoDBAtlasRotatingSecret(&preview_secret_service.CreateMongoDBAtlasRotatingSecretParams{
OrganizationID: testProfile(t).OrganizationID,
ProjectID: testProfile(t).ProjectID,
AppName: testProfile(t).VaultSecrets.AppName,
Body: &preview_models.SecretServiceCreateMongoDBAtlasRotatingSecretBody{
Name: opts.SecretName,
IntegrationName: "mongo-db-integration",
RotationPolicyName: "built-in:60-days-2-active",
SecretDetails: &preview_models.Secrets20231128MongoDBAtlasSecretDetails{
MongodbGroupID: "mbdgi",
MongodbRoles: []*preview_models.Secrets20231128MongoDBRole{
{
RoleName: "rn1",
DatabaseName: "dn1",
CollectionName: "cn1",
},
{
RoleName: "rn2",
DatabaseName: "dn2",
CollectionName: "cn2",
switch c.Provider {
case integrations.MongoDBAtlas:
if c.RespErr {
pvs.EXPECT().CreateMongoDBAtlasRotatingSecret(mock.Anything, mock.Anything).Return(nil, errors.New(c.ErrMsg)).Once()
} else {
pvs.EXPECT().CreateMongoDBAtlasRotatingSecret(&preview_secret_service.CreateMongoDBAtlasRotatingSecretParams{
OrganizationID: testProfile(t).OrganizationID,
ProjectID: testProfile(t).ProjectID,
AppName: testProfile(t).VaultSecrets.AppName,
Body: &preview_models.SecretServiceCreateMongoDBAtlasRotatingSecretBody{
Name: opts.SecretName,
IntegrationName: "mongo-db-integration",
RotationPolicyName: "built-in:60-days-2-active",
SecretDetails: &preview_models.Secrets20231128MongoDBAtlasSecretDetails{
MongodbGroupID: "mbdgi",
MongodbRoles: []*preview_models.Secrets20231128MongoDBRole{
{
RoleName: "rn1",
DatabaseName: "dn1",
CollectionName: "cn1",
},
{
RoleName: "rn2",
DatabaseName: "dn2",
CollectionName: "cn2",
},
},
},
},
},
Context: opts.Ctx,
}, mock.Anything).Return(&preview_secret_service.CreateMongoDBAtlasRotatingSecretOK{
Payload: &preview_models.Secrets20231128CreateMongoDBAtlasRotatingSecretResponse{
Config: &preview_models.Secrets20231128RotatingSecretConfig{
AppName: opts.AppName,
CreatedAt: dt,
IntegrationName: "mongo-db-integration",
RotationPolicyName: "built-in:60-days-2-active",
Context: opts.Ctx,
}, mock.Anything).Return(&preview_secret_service.CreateMongoDBAtlasRotatingSecretOK{
Payload: &preview_models.Secrets20231128CreateMongoDBAtlasRotatingSecretResponse{
Config: &preview_models.Secrets20231128RotatingSecretConfig{
AppName: opts.AppName,
CreatedAt: dt,
IntegrationName: "mongo-db-integration",
RotationPolicyName: "built-in:60-days-2-active",
Name: opts.SecretName,
},
},
}, nil).Once()
}
case integrations.Postgres:
if c.RespErr {
pvs.EXPECT().CreatePostgresRotatingSecret(mock.Anything, mock.Anything).Return(nil, errors.New(c.ErrMsg)).Once()
} else {
println(testProfile(t).ProjectID)
pvs.EXPECT().CreatePostgresRotatingSecret(&preview_secret_service.CreatePostgresRotatingSecretParams{
OrganizationID: testProfile(t).OrganizationID,
ProjectID: testProfile(t).ProjectID,
AppName: testProfile(t).VaultSecrets.AppName,
Body: &preview_models.SecretServiceCreatePostgresRotatingSecretBody{
Name: opts.SecretName,
IntegrationName: "postgres-integration",
RotationPolicyName: "built-in:60-days-2-active",
PostgresParams: &preview_models.Secrets20231128PostgresParams{Usernames: []string{"postgres_user_1", "postgres_user_2"}},
},
},
}, nil).Once()
Context: opts.Ctx,
}, mock.Anything).Return(&preview_secret_service.CreatePostgresRotatingSecretOK{
Payload: &preview_models.Secrets20231128CreatePostgresRotatingSecretResponse{
Config: &preview_models.Secrets20231128PostgresRotatingSecretConfig{
AppName: opts.AppName,
CreatedAt: dt,
IntegrationName: "postgres-integration",
RotationPolicyName: "built-in:60-days-2-active",
Name: opts.SecretName,
PostgresParams: &preview_models.Secrets20231128PostgresParams{Usernames: []string{"postgres_user_1", "postgres_user_2"}},
},
},
}, nil).Once()
}
}
}
} else if opts.Type == secretTypeDynamic {
Expand Down
25 changes: 25 additions & 0 deletions internal/commands/vaultsecrets/secrets/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,31 @@ func updateRun(opts *UpdateOpts) error {
if err != nil {
return fmt.Errorf("failed to update secret with name %q: %w", opts.SecretName, err)
}

case integrations.Postgres:
req := preview_secret_service.NewUpdatePostgresRotatingSecretParamsWithContext(opts.Ctx)
req.OrganizationID = opts.Profile.OrganizationID
req.ProjectID = opts.Profile.ProjectID
req.AppName = opts.AppName
req.Name = opts.SecretName

var postgresBody preview_models.SecretServiceUpdatePostgresRotatingSecretBody
detailBytes, err := json.Marshal(internalConfig.Details)
if err != nil {
return fmt.Errorf("error marshaling details config: %w", err)
}

err = postgresBody.UnmarshalBinary(detailBytes)
if err != nil {
return fmt.Errorf("error unmarshaling details config: %w", err)
}

req.Body = &postgresBody

_, err = opts.PreviewClient.UpdatePostgresRotatingSecret(req, nil)
if err != nil {
return fmt.Errorf("failed to update secret with name %q: %w", opts.SecretName, err)
}
}

case secretTypeDynamic:
Expand Down
Loading

0 comments on commit 88f300f

Please sign in to comment.