From 84fa94c6c1d797a2fde03c791bbab760d2e402f0 Mon Sep 17 00:00:00 2001 From: Milena Zlaticanin <60530402+Zlaticanin@users.noreply.github.com> Date: Wed, 12 Mar 2025 11:08:12 -0700 Subject: [PATCH] Add LIST endpoint to AWS Secrets static roles (#29842) * Add LIST endpoint to AWS Secrets static roles * add test + changelog * Update website/content/api-docs/secret/aws.mdx Co-authored-by: John-Michael Faircloth * Update website/content/api-docs/secret/aws.mdx Co-authored-by: Sarah Chavis <62406755+schavis@users.noreply.github.com> --------- Co-authored-by: John-Michael Faircloth Co-authored-by: Sarah Chavis <62406755+schavis@users.noreply.github.com> --- builtin/logical/aws/backend.go | 1 + builtin/logical/aws/path_static_roles.go | 28 ++++++++++ builtin/logical/aws/path_static_roles_test.go | 56 +++++++++++++++++++ changelog/29842.txt | 3 + website/content/api-docs/secret/aws.mdx | 27 +++++++++ 5 files changed, 115 insertions(+) create mode 100644 changelog/29842.txt diff --git a/builtin/logical/aws/backend.go b/builtin/logical/aws/backend.go index 464601aa04..6877cbc4d7 100644 --- a/builtin/logical/aws/backend.go +++ b/builtin/logical/aws/backend.go @@ -57,6 +57,7 @@ func Backend(_ *logical.BackendConfig) *backend { pathRoles(&b), pathListRoles(&b), pathStaticRoles(&b), + pathListStaticRoles(&b), pathStaticCredentials(&b), pathUser(&b), }, diff --git a/builtin/logical/aws/path_static_roles.go b/builtin/logical/aws/path_static_roles.go index 6612adc4d9..0493784963 100644 --- a/builtin/logical/aws/path_static_roles.go +++ b/builtin/logical/aws/path_static_roles.go @@ -94,6 +94,19 @@ func pathStaticRoles(b *backend) *framework.Path { } } +func pathListStaticRoles(b *backend) *framework.Path { + return &framework.Path{ + Pattern: fmt.Sprintf("%s/?$", pathStaticRole), + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ListOperation: &framework.PathOperation{ + Callback: b.pathStaticRolesList, + }, + }, + HelpSynopsis: pathStaticRolesHelpSyn, + HelpDescription: pathStaticRolesHelpDesc, + } +} + func (b *backend) pathStaticRolesRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { roleName, ok := data.GetOk(paramRoleName) if !ok { @@ -289,6 +302,21 @@ func (b *backend) pathStaticRolesDelete(ctx context.Context, req *logical.Reques return nil, req.Storage.Delete(ctx, formatRoleStoragePath(roleName.(string))) } +func (b *backend) pathStaticRolesList(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + b.roleMutex.RLock() + defer b.roleMutex.RUnlock() + + roles, err := req.Storage.List(ctx, pathStaticRole+"/") + if err != nil { + return nil, fmt.Errorf("error listing static roles at %s: %w", pathStaticRole, err) + } + if len(roles) == 0 { + return &logical.Response{Data: map[string]interface{}{}}, nil + } + + return logical.ListResponse(roles), nil +} + func (b *backend) validateRoleName(name string) error { if name == "" { return errors.New("empty role name attribute given") diff --git a/builtin/logical/aws/path_static_roles_test.go b/builtin/logical/aws/path_static_roles_test.go index 54a365acb0..03d5957a01 100644 --- a/builtin/logical/aws/path_static_roles_test.go +++ b/builtin/logical/aws/path_static_roles_test.go @@ -6,6 +6,7 @@ package aws import ( "context" "errors" + "strconv" "testing" "time" @@ -533,6 +534,61 @@ func TestStaticRoleDelete(t *testing.T) { } } +// TestStaticRolesList validates that we can list all the static roles in the storage backend. +func TestStaticRolesList(t *testing.T) { + config := logical.TestBackendConfig() + config.StorageView = &logical.InmemStorage{} + bgCTX := context.Background() + + staticRoles := []staticRoleEntry{} + for i := 1; i <= 10; i++ { + roles := staticRoleEntry{ + Name: "testrole" + strconv.Itoa(i), + Username: "jane-doe", + RotationPeriod: 24 * time.Hour, + } + staticRoles = append(staticRoles, roles) + } + + for _, role := range staticRoles { + entry, err := logical.StorageEntryJSON(formatRoleStoragePath(role.Name), role) + if err != nil { + t.Fatalf("failed to create storage entry for %s: %v", role.Name, err) + } + err = config.StorageView.Put(bgCTX, entry) + if err != nil { + t.Fatalf("failed to store role %s: %v", role.Name, err) + } + } + + b := Backend(config) + resp, err := b.HandleRequest(bgCTX, &logical.Request{ + Operation: logical.ListOperation, + Path: "static-roles", + Storage: config.StorageView, + }) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("bad: listing roles failed. resp:%#v\n err:%v", resp, err) + } + + if len(resp.Data["keys"].([]string)) != 10 { + t.Fatalf("failed to list all 10 roles") + } + + resp, err = b.HandleRequest(bgCTX, &logical.Request{ + Operation: logical.ListOperation, + Path: "static-roles/", + Storage: config.StorageView, + }) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("bad: listing roles failed. resp:%#v\n err:%v", resp, err) + } + + if len(resp.Data["keys"].([]string)) != 10 { + t.Fatalf("failed to list all 10 roles") + } +} + func staticRoleFieldData(data map[string]interface{}) *framework.FieldData { schema := map[string]*framework.FieldSchema{ paramRoleName: { diff --git a/changelog/29842.txt b/changelog/29842.txt new file mode 100644 index 0000000000..2ab011fdd5 --- /dev/null +++ b/changelog/29842.txt @@ -0,0 +1,3 @@ +```release-note:improvement +secrets/aws: Add LIST endpoint to the AWS secrets engine static roles. +``` \ No newline at end of file diff --git a/website/content/api-docs/secret/aws.mdx b/website/content/api-docs/secret/aws.mdx index 749796da64..83e5e7f276 100644 --- a/website/content/api-docs/secret/aws.mdx +++ b/website/content/api-docs/secret/aws.mdx @@ -777,6 +777,33 @@ $ curl \ } ``` +## List static roles + +Use the list static roles endpoint to fetch all existing static roles in the secrets engine. + +| Method | Path | +| :----- | :------------------ | +| `LIST` | `/aws/static-roles` | + +### Sample request + +```shell-session +$ curl + --header "X-Vault-Token: ..." \ + --request LIST \ + http://127.0.0.1:8200/v1/aws/static-roles +``` + +### Sample response + +```json +{ + "data": { + "keys": ["example-role"] + } +} +``` + ## Delete static role This endpoint deletes the static role definition. The user, having been defined externally,