From ca221009d43f40d71bb406f2126d00254dcf2234 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 9 Apr 2016 18:28:30 -0400 Subject: [PATCH] Add list support to userpass users. Remove some unneeded existence checks. Remove paths from requiring root. Fixes #911 --- builtin/credential/userpass/backend.go | 7 ++--- builtin/credential/userpass/backend_test.go | 30 ++++++++++++++++--- .../credential/userpass/path_user_password.go | 8 ----- .../credential/userpass/path_user_policies.go | 8 ----- builtin/credential/userpass/path_users.go | 24 +++++++++++++++ 5 files changed, 52 insertions(+), 25 deletions(-) diff --git a/builtin/credential/userpass/backend.go b/builtin/credential/userpass/backend.go index 5505a1ab5e..70515ef0ac 100644 --- a/builtin/credential/userpass/backend.go +++ b/builtin/credential/userpass/backend.go @@ -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), }, diff --git a/builtin/credential/userpass/backend_test.go b/builtin/credential/userpass/backend_test.go index 85a61ccdb7..7b633f025b 100644 --- a/builtin/credential/userpass/backend_test.go +++ b/builtin/credential/userpass/backend_test.go @@ -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, diff --git a/builtin/credential/userpass/path_user_password.go b/builtin/credential/userpass/path_user_password.go index 22c08239a8..1353f78b1c 100644 --- a/builtin/credential/userpass/path_user_password.go +++ b/builtin/credential/userpass/path_user_password.go @@ -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) { diff --git a/builtin/credential/userpass/path_user_policies.go b/builtin/credential/userpass/path_user_policies.go index 9b586c6191..6165c189f3 100644 --- a/builtin/credential/userpass/path_user_policies.go +++ b/builtin/credential/userpass/path_user_policies.go @@ -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) { diff --git a/builtin/credential/userpass/path_users.go b/builtin/credential/userpass/path_users.go index 8a2f67edfb..62399259a0 100644 --- a/builtin/credential/userpass/path_users.go +++ b/builtin/credential/userpass/path_users.go @@ -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 }