Add list support to userpass users. Remove some unneeded existence

checks. Remove paths from requiring root.

Fixes #911
This commit is contained in:
Jeff Mitchell 2016-04-09 18:28:30 -04:00
parent 363f517232
commit ca221009d4
5 changed files with 52 additions and 25 deletions

View file

@ -16,11 +16,7 @@ func Backend() *framework.Backend {
Help: backendHelp,
PathsSpecial: &logical.Paths{
Root: append([]string{
"users/*",
},
mfa.MFARootPaths()...,
),
Root: mfa.MFARootPaths(),
Unauthenticated: []string{
"login/*",
@ -29,6 +25,7 @@ func Backend() *framework.Backend {
Paths: append([]*framework.Path{
pathUsers(&b),
pathUsersList(&b),
pathUserPolicies(&b),
pathUserPassword(&b),
},

View file

@ -2,6 +2,7 @@ package userpass
import (
"fmt"
"reflect"
"testing"
"time"
@ -83,6 +84,9 @@ func TestBackend_basic(t *testing.T) {
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepUser(t, "web", "password", "foo"),
testAccStepUser(t, "web2", "password", "foo"),
testAccStepUser(t, "web3", "password", "foo"),
testAccStepList(t, []string{"web", "web2", "web3"}),
testAccStepLogin(t, "web", "password", []string{"default", "foo"}),
},
})
@ -105,7 +109,7 @@ func TestBackend_userCrud(t *testing.T) {
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepUser(t, "web", "password", "foo"),
testAccStepReadUser(t, "web", "foo"),
testAccStepReadUser(t, "web", "default,foo"),
testAccStepDeleteUser(t, "web"),
testAccStepReadUser(t, "web", ""),
},
@ -151,7 +155,7 @@ func TestBackend_passwordUpdate(t *testing.T) {
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepUser(t, "web", "password", "foo"),
testAccStepReadUser(t, "web", "foo"),
testAccStepReadUser(t, "web", "default,foo"),
testAccStepLogin(t, "web", "password", []string{"default", "foo"}),
testUpdatePassword(t, "web", "newpassword"),
testAccStepLogin(t, "web", "newpassword", []string{"default", "foo"}),
@ -177,10 +181,10 @@ func TestBackend_policiesUpdate(t *testing.T) {
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepUser(t, "web", "password", "foo"),
testAccStepReadUser(t, "web", "foo"),
testAccStepReadUser(t, "web", "default,foo"),
testAccStepLogin(t, "web", "password", []string{"default", "foo"}),
testUpdatePolicies(t, "web", "foo,bar"),
testAccStepReadUser(t, "web", "foo,bar"),
testAccStepReadUser(t, "web", "bar,default,foo"),
testAccStepLogin(t, "web", "password", []string{"bar", "default", "foo"}),
},
})
@ -237,6 +241,24 @@ func testLoginWrite(t *testing.T, user string, data map[string]interface{}, expe
}
}
func testAccStepList(t *testing.T, users []string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.ListOperation,
Path: "users",
Check: func(resp *logical.Response) error {
if resp.IsError() {
return fmt.Errorf("Got error response: %#v", *resp)
}
exp := []string{"web", "web2", "web3"}
if !reflect.DeepEqual(exp, resp.Data["keys"].([]string)) {
return fmt.Errorf("expected:\n%#v\ngot:\n%#v\n", exp, resp.Data["keys"])
}
return nil
},
}
}
func testAccStepLogin(t *testing.T, user string, pass string, policies []string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,

View file

@ -24,8 +24,6 @@ func pathUserPassword(b *backend) *framework.Path {
},
},
ExistenceCheck: b.userPasswordExistenceCheck,
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: b.pathUserPasswordUpdate,
},
@ -35,12 +33,6 @@ func pathUserPassword(b *backend) *framework.Path {
}
}
// By always returning true, this endpoint will be enforced to be invoked only upon UpdateOperation.
// The existence of user will be checked in the operation handler.
func (b *backend) userPasswordExistenceCheck(req *logical.Request, data *framework.FieldData) (bool, error) {
return true, nil
}
func (b *backend) pathUserPasswordUpdate(
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

View file

@ -22,8 +22,6 @@ func pathUserPolicies(b *backend) *framework.Path {
},
},
ExistenceCheck: b.userPoliciesExistenceCheck,
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: b.pathUserPoliciesUpdate,
},
@ -33,12 +31,6 @@ func pathUserPolicies(b *backend) *framework.Path {
}
}
// By always returning true, this endpoint will be enforced to be invoked only upon UpdateOperation.
// The existence of user will be checked in the operation handler.
func (b *backend) userPoliciesExistenceCheck(req *logical.Request, data *framework.FieldData) (bool, error) {
return true, nil
}
func (b *backend) pathUserPoliciesUpdate(
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

View file

@ -10,6 +10,19 @@ import (
"github.com/hashicorp/vault/logical/framework"
)
func pathUsersList(b *backend) *framework.Path {
return &framework.Path{
Pattern: "users/?",
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.ListOperation: b.pathUserList,
},
HelpSynopsis: pathUserHelpSyn,
HelpDescription: pathUserHelpDesc,
}
}
func pathUsers(b *backend) *framework.Path {
return &framework.Path{
Pattern: "users/" + framework.GenericNameRegex("username"),
@ -93,6 +106,15 @@ func (b *backend) setUser(s logical.Storage, username string, userEntry *UserEnt
return s.Put(entry)
}
func (b *backend) pathUserList(
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
users, err := req.Storage.List("user/")
if err != nil {
return nil, err
}
return logical.ListResponse(users), nil
}
func (b *backend) pathUserDelete(
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
err := req.Storage.Delete("user/" + strings.ToLower(d.Get("username").(string)))
@ -116,6 +138,8 @@ func (b *backend) pathUserRead(
return &logical.Response{
Data: map[string]interface{}{
"policies": strings.Join(user.Policies, ","),
"ttl": user.TTL.Seconds(),
"max_ttl": user.MaxTTL.Seconds(),
},
}, nil
}