Add LIST support to sys/policies/password (#12787)
* Add read support to sys/policies/password Closes https://github.com/hashicorp/vault/issues/12562 * Add changelog * Empty commit to trigger CI * Add optional / Co-authored-by: Calvin Leung Huang <1883212+calvn@users.noreply.github.com> * Use a ListOperation Co-authored-by: Calvin Leung Huang <1883212+calvn@users.noreply.github.com>
This commit is contained in:
parent
6474e73c97
commit
d6a4a3b53c
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
core: Add support to list password policies at `sys/policies/password`
|
||||
```
|
|
@ -2345,6 +2345,16 @@ const (
|
|||
maxPasswordLength = 100
|
||||
)
|
||||
|
||||
// handlePoliciesPasswordList returns the list of password policies
|
||||
func (*SystemBackend) handlePoliciesPasswordList(ctx context.Context, req *logical.Request, data *framework.FieldData) (resp *logical.Response, err error) {
|
||||
keys, err := req.Storage.List(ctx, "password_policy/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return logical.ListResponse(keys), nil
|
||||
}
|
||||
|
||||
// handlePoliciesPasswordSet saves/updates password policies
|
||||
func (*SystemBackend) handlePoliciesPasswordSet(ctx context.Context, req *logical.Request, data *framework.FieldData) (resp *logical.Response, err error) {
|
||||
policyName := data.Get("name").(string)
|
||||
|
|
|
@ -1615,6 +1615,17 @@ func (b *SystemBackend) policyPaths() []*framework.Path {
|
|||
HelpDescription: strings.TrimSpace(sysHelp["policy"][1]),
|
||||
},
|
||||
|
||||
{
|
||||
Pattern: "policies/password/?$",
|
||||
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.ListOperation: &framework.PathOperation{
|
||||
Callback: b.handlePoliciesPasswordList,
|
||||
Summary: "List the existing password policies.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Pattern: "policies/password/(?P<name>.+)/generate$",
|
||||
|
||||
|
|
|
@ -3645,6 +3645,107 @@ func TestHandlePoliciesPasswordDelete(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHandlePoliciesPasswordList(t *testing.T) {
|
||||
type testCase struct {
|
||||
storage logical.Storage
|
||||
|
||||
expectErr bool
|
||||
expectedResp *logical.Response
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"no policies": {
|
||||
storage: new(logical.InmemStorage),
|
||||
|
||||
expectedResp: &logical.Response{
|
||||
Data: map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
"one policy": {
|
||||
storage: makeStorage(t,
|
||||
&logical.StorageEntry{
|
||||
Key: getPasswordPolicyKey("testpolicy"),
|
||||
Value: toJson(t,
|
||||
passwordPolicyConfig{
|
||||
HCLPolicy: "length = 18\n" +
|
||||
"rule \"charset\" {\n" +
|
||||
" charset=\"ABCDEFGHIJ\"\n" +
|
||||
"}",
|
||||
}),
|
||||
},
|
||||
),
|
||||
|
||||
expectedResp: &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"keys": []string{"testpolicy"},
|
||||
},
|
||||
},
|
||||
},
|
||||
"two policies": {
|
||||
storage: makeStorage(t,
|
||||
&logical.StorageEntry{
|
||||
Key: getPasswordPolicyKey("testpolicy"),
|
||||
Value: toJson(t,
|
||||
passwordPolicyConfig{
|
||||
HCLPolicy: "length = 18\n" +
|
||||
"rule \"charset\" {\n" +
|
||||
" charset=\"ABCDEFGHIJ\"\n" +
|
||||
"}",
|
||||
}),
|
||||
},
|
||||
&logical.StorageEntry{
|
||||
Key: getPasswordPolicyKey("unrelated_policy"),
|
||||
Value: toJson(t,
|
||||
passwordPolicyConfig{
|
||||
HCLPolicy: "length = 20\n" +
|
||||
"rule \"charset\" {\n" +
|
||||
" charset=\"abcdefghij\"\n" +
|
||||
"}",
|
||||
}),
|
||||
},
|
||||
),
|
||||
|
||||
expectedResp: &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"keys": []string{
|
||||
"testpolicy",
|
||||
"unrelated_policy",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"storage failure": {
|
||||
storage: new(logical.InmemStorage).FailList(true),
|
||||
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
req := &logical.Request{
|
||||
Storage: test.storage,
|
||||
}
|
||||
|
||||
b := &SystemBackend{}
|
||||
|
||||
actualResp, err := b.handlePoliciesPasswordList(ctx, req, nil)
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(actualResp, test.expectedResp) {
|
||||
t.Fatalf("Actual response: %#v\nExpected response: %#v", actualResp, test.expectedResp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlePoliciesPasswordGenerate(t *testing.T) {
|
||||
t.Run("errors", func(t *testing.T) {
|
||||
type testCase struct {
|
||||
|
|
|
@ -77,6 +77,43 @@ rule "charset" {
|
|||
$ vault write sys/policies/password/my-policy policy=@my-policy.hcl
|
||||
```
|
||||
|
||||
## List Password Policies
|
||||
|
||||
This endpoints list the password policies.
|
||||
|
||||
| Method | Path |
|
||||
| :------ | :--------------------------------- |
|
||||
| `LIST` | `/sys/policies/password` |
|
||||
| `GET` | `/sys/policies/password?list=true` |
|
||||
|
||||
### Sample Request
|
||||
|
||||
```shell
|
||||
$ curl \
|
||||
--header "X-Vault-Token: ..." \
|
||||
--request LIST \
|
||||
http://127.0.0.1:8200/v1/sys/policies/password
|
||||
```
|
||||
|
||||
### Sample Response
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id": "58e2540f-8c51-6390-46de-38e279e75468",
|
||||
"lease_id": "",
|
||||
"renewable": false,
|
||||
"lease_duration": 0,
|
||||
"data": {
|
||||
"keys": [
|
||||
"my-policy"
|
||||
]
|
||||
},
|
||||
"wrap_info": null,
|
||||
"warnings": null,
|
||||
"auth": null
|
||||
}
|
||||
```
|
||||
|
||||
## Read Password Policy
|
||||
|
||||
This endpoint retrieves information about the named password policy.
|
||||
|
|
Loading…
Reference in New Issue