From cab0c822776f52af5cd96b7ec1ef8c71b73c91de Mon Sep 17 00:00:00 2001 From: JM Faircloth Date: Fri, 3 May 2024 16:26:36 -0500 Subject: [PATCH] enable hierarchical path for rotate-role endpoint --- path_rotate.go | 3 +- path_rotate_test.go | 136 +++++++++++++++++++++----------------------- 2 files changed, 66 insertions(+), 73 deletions(-) diff --git a/path_rotate.go b/path_rotate.go index eb780ee..2b8886e 100644 --- a/path_rotate.go +++ b/path_rotate.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "strings" "time" "github.com/hashicorp/vault/sdk/framework" @@ -48,7 +49,7 @@ func (b *backend) pathRotateCredentials() []*framework.Path { "(binddn) used by Vault to manage LDAP.", }, { - Pattern: rotateRolePath + framework.GenericNameRegex("name"), + Pattern: strings.TrimSuffix(rotateRolePath, "/") + GenericNameWithForwardSlashRegex("name"), DisplayAttrs: &framework.DisplayAttributes{ OperationPrefix: operationPrefixLDAP, OperationVerb: "rotate", diff --git a/path_rotate_test.go b/path_rotate_test.go index cd39fff..7d31b7b 100644 --- a/path_rotate_test.go +++ b/path_rotate_test.go @@ -10,14 +10,15 @@ import ( "time" "github.com/go-ldap/ldif" + "github.com/hashicorp/go-secure-stdlib/strutil" "github.com/hashicorp/vault-plugin-secrets-openldap/client" "github.com/hashicorp/vault/sdk/helper/ldaputil" "github.com/hashicorp/vault/sdk/logical" "github.com/stretchr/testify/assert" ) -func TestManualRotate(t *testing.T) { - t.Run("rotate root", func(t *testing.T) { +func TestManualRotateRoot(t *testing.T) { + t.Run("happy path rotate root", func(t *testing.T) { b, storage := getBackend(false) defer b.Cleanup(context.Background()) @@ -83,107 +84,99 @@ func TestManualRotate(t *testing.T) { t.Fatal("should have got error, didn't") } }) +} - t.Run("rotate role", func(t *testing.T) { +func TestManualRotateRole(t *testing.T) { + t.Run("happy path rotate role", func(t *testing.T) { b, storage := getBackend(false) defer b.Cleanup(context.Background()) - data := map[string]interface{}{ - "binddn": "tester", - "bindpass": "pa$$w0rd", - "url": "ldap://138.91.247.105", - "certificate": validCertificate, - } + roleName := "hashicorp" + configureOpenLDAPMount(t, b, storage) + createRole(t, b, storage, roleName) - req := &logical.Request{ - Operation: logical.CreateOperation, - Path: configPath, - Storage: storage, - Data: data, - } + resp, _ := readStaticCred(t, b, storage, roleName) - resp, err := b.HandleRequest(context.Background(), req) - if err != nil || (resp != nil && resp.IsError()) { - t.Fatalf("err:%s resp:%#v\n", err, resp) + if resp.Data["password"] == "" { + t.Fatal("expected password to be set, it wasn't") } + oldPassword := resp.Data["password"] - req = &logical.Request{ + req := &logical.Request{ Operation: logical.UpdateOperation, - Path: rotateRootPath, + Path: rotateRolePath + roleName, Storage: storage, Data: nil, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(context.Background(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } - data = map[string]interface{}{ - "username": "hashicorp", - "dn": "uid=hashicorp,ou=users,dc=hashicorp,dc=com", - "rotation_period": "60s", - } + resp, _ = readStaticCred(t, b, storage, roleName) - req = &logical.Request{ - Operation: logical.CreateOperation, - Path: staticRolePath + "hashicorp", - Storage: storage, - Data: data, + if resp.Data["password"] == "" { + t.Fatal("expected password to be set after rotate, it wasn't") } - resp, err = b.HandleRequest(context.Background(), req) - if err != nil || (resp != nil && resp.IsError()) { - t.Fatalf("err:%s resp:%#v\n", err, resp) + if oldPassword == resp.Data["password"] { + t.Fatal("expected passwords to be different after rotation, they weren't") } + }) - req = &logical.Request{ - Operation: logical.ReadOperation, - Path: staticCredPath + "hashicorp", - Storage: storage, - Data: nil, - } + t.Run("happy path rotate role with hierarchical path", func(t *testing.T) { + b, storage := getBackend(false) + defer b.Cleanup(context.Background()) - resp, err = b.HandleRequest(context.Background(), req) - if err != nil || (resp != nil && resp.IsError()) { - t.Fatalf("err:%s resp:%#v\n", err, resp) - } + configureOpenLDAPMount(t, b, storage) - if resp.Data["password"] == "" { - t.Fatal("expected password to be set, it wasn't") - } - oldPassword := resp.Data["password"] + roles := []string{"org/secure", "org/platform/dev", "org/platform/support"} - req = &logical.Request{ - Operation: logical.UpdateOperation, - Path: rotateRolePath + "hashicorp", - Storage: storage, - Data: nil, + // create all the roles + for _, role := range roles { + data := getTestStaticRoleConfig(role) + createStaticRoleWithData(t, b, storage, role, data) } - resp, err = b.HandleRequest(context.Background(), req) - if err != nil || (resp != nil && resp.IsError()) { - t.Fatalf("err:%s resp:%#v\n", err, resp) - } + passwords := make([]string, 0) + // rotate all the creds + for _, role := range roles { + resp, _ := readStaticCred(t, b, storage, role) - req = &logical.Request{ - Operation: logical.ReadOperation, - Path: staticCredPath + "hashicorp", - Storage: storage, - Data: nil, - } + if resp.Data["password"] == "" { + t.Fatal("expected password to be set, it wasn't") + } + oldPassword := resp.Data["password"] - resp, err = b.HandleRequest(context.Background(), req) - if err != nil || (resp != nil && resp.IsError()) { - t.Fatalf("err:%s resp:%#v\n", err, resp) - } + req := &logical.Request{ + Operation: logical.UpdateOperation, + Path: rotateRolePath + role, + Storage: storage, + Data: nil, + } - if resp.Data["password"] == "" { - t.Fatal("expected password to be set after rotate, it wasn't") + resp, err := b.HandleRequest(context.Background(), req) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("err:%s resp:%#v\n", err, resp) + } + + resp, _ = readStaticCred(t, b, storage, role) + + newPassword := resp.Data["password"] + if newPassword == "" { + t.Fatal("expected password to be set after rotate, it wasn't") + } + + if oldPassword == newPassword { + t.Fatal("expected passwords to be different after rotation, they weren't") + } + passwords = append(passwords, newPassword.(string)) } - if oldPassword == resp.Data["password"] { - t.Fatal("expected passwords to be different after rotation, they weren't") + // extra pendantic check that the hierarchical paths don't return the same data + if len(passwords) != len(strutil.RemoveDuplicates(passwords, false)) { + t.Fatal("expected unique static-role paths to return unique passwords") } }) @@ -284,7 +277,6 @@ func TestRollbackPassword(t *testing.T) { } assert.Equal(t, testCase.expectedPassword, fclient.password) assert.Equal(t, testCase.expectedRollbackCalls, fclient.count) - }) } }