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 <fairclothjm@users.noreply.github.com>

* 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 <fairclothjm@users.noreply.github.com>
Co-authored-by: Sarah Chavis <62406755+schavis@users.noreply.github.com>
This commit is contained in:
Milena Zlaticanin 2025-03-12 11:08:12 -07:00 committed by GitHub
parent 404356a805
commit 84fa94c6c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 115 additions and 0 deletions

View file

@ -57,6 +57,7 @@ func Backend(_ *logical.BackendConfig) *backend {
pathRoles(&b),
pathListRoles(&b),
pathStaticRoles(&b),
pathListStaticRoles(&b),
pathStaticCredentials(&b),
pathUser(&b),
},

View file

@ -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")

View file

@ -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: {

3
changelog/29842.txt Normal file
View file

@ -0,0 +1,3 @@
```release-note:improvement
secrets/aws: Add LIST endpoint to the AWS secrets engine static roles.
```

View file

@ -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,