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
|
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
|
// handlePoliciesPasswordSet saves/updates password policies
|
||||||
func (*SystemBackend) handlePoliciesPasswordSet(ctx context.Context, req *logical.Request, data *framework.FieldData) (resp *logical.Response, err error) {
|
func (*SystemBackend) handlePoliciesPasswordSet(ctx context.Context, req *logical.Request, data *framework.FieldData) (resp *logical.Response, err error) {
|
||||||
policyName := data.Get("name").(string)
|
policyName := data.Get("name").(string)
|
||||||
|
|
|
@ -1615,6 +1615,17 @@ func (b *SystemBackend) policyPaths() []*framework.Path {
|
||||||
HelpDescription: strings.TrimSpace(sysHelp["policy"][1]),
|
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$",
|
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) {
|
func TestHandlePoliciesPasswordGenerate(t *testing.T) {
|
||||||
t.Run("errors", func(t *testing.T) {
|
t.Run("errors", func(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
|
|
|
@ -77,6 +77,43 @@ rule "charset" {
|
||||||
$ vault write sys/policies/password/my-policy policy=@my-policy.hcl
|
$ 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
|
## Read Password Policy
|
||||||
|
|
||||||
This endpoint retrieves information about the named password policy.
|
This endpoint retrieves information about the named password policy.
|
||||||
|
|
Loading…
Reference in New Issue