etcdserver/auth: check empty password in merge

Fix https://github.com/coreos/etcd/issues/5182.
This commit is contained in:
Gyu-Ho Lee 2016-04-26 12:28:07 -07:00
parent 7161eeed8b
commit 07685bcf97
2 changed files with 39 additions and 26 deletions

View File

@ -281,13 +281,7 @@ func (s *store) UpdateUser(user User) (User, error) {
return old, err return old, err
} }
hash, err := s.HashPassword(user.Password) newUser, err := old.merge(user, s.PasswordStore)
if err != nil {
return old, err
}
user.Password = hash
newUser, err := old.merge(user)
if err != nil { if err != nil {
return old, err return old, err
} }
@ -448,29 +442,33 @@ func (s *store) DisableAuth() error {
// is called and returns a new User with these modifications applied. Think of // is called and returns a new User with these modifications applied. Think of
// all Users as immutable sets of data. Merge allows you to perform the set // all Users as immutable sets of data. Merge allows you to perform the set
// operations (desired grants and revokes) atomically // operations (desired grants and revokes) atomically
func (u User) merge(n User) (User, error) { func (ou User) merge(nu User, s PasswordStore) (User, error) {
var out User var out User
if u.User != n.User { if ou.User != nu.User {
return out, authErr(http.StatusConflict, "Merging user data with conflicting usernames: %s %s", u.User, n.User) return out, authErr(http.StatusConflict, "Merging user data with conflicting usernames: %s %s", ou.User, nu.User)
} }
out.User = u.User out.User = ou.User
if n.Password != "" { if nu.Password != "" {
out.Password = n.Password hash, err := s.HashPassword(nu.Password)
if err != nil {
return ou, err
}
out.Password = hash
} else { } else {
out.Password = u.Password out.Password = ou.Password
} }
currentRoles := types.NewUnsafeSet(u.Roles...) currentRoles := types.NewUnsafeSet(ou.Roles...)
for _, g := range n.Grant { for _, g := range nu.Grant {
if currentRoles.Contains(g) { if currentRoles.Contains(g) {
plog.Noticef("granting duplicate role %s for user %s", g, n.User) plog.Noticef("granting duplicate role %s for user %s", g, nu.User)
return User{}, authErr(http.StatusConflict, fmt.Sprintf("Granting duplicate role %s for user %s", g, n.User)) return User{}, authErr(http.StatusConflict, fmt.Sprintf("Granting duplicate role %s for user %s", g, nu.User))
} }
currentRoles.Add(g) currentRoles.Add(g)
} }
for _, r := range n.Revoke { for _, r := range nu.Revoke {
if !currentRoles.Contains(r) { if !currentRoles.Contains(r) {
plog.Noticef("revoking ungranted role %s for user %s", r, n.User) plog.Noticef("revoking ungranted role %s for user %s", r, nu.User)
return User{}, authErr(http.StatusConflict, fmt.Sprintf("Revoking ungranted role %s for user %s", r, n.User)) return User{}, authErr(http.StatusConflict, fmt.Sprintf("Revoking ungranted role %s for user %s", r, nu.User))
} }
currentRoles.Remove(r) currentRoles.Remove(r)
} }

View File

@ -26,6 +26,21 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
) )
type fakeDoer struct{}
func (_ fakeDoer) Do(context.Context, etcdserverpb.Request) (etcdserver.Response, error) {
return etcdserver.Response{}, nil
}
func TestCheckPassword(t *testing.T) {
st := NewStore(fakeDoer{}, 5*time.Second)
u := User{Password: "$2a$10$I3iddh1D..EIOXXQtsra4u8AjOtgEa2ERxVvYGfXFBJDo1omXwP.q"}
matched := st.CheckPassword(u, "foo")
if matched {
t.Fatalf("expected false, got %v", matched)
}
}
const testTimeout = time.Millisecond const testTimeout = time.Millisecond
func TestMergeUser(t *testing.T) { func TestMergeUser(t *testing.T) {
@ -71,16 +86,16 @@ func TestMergeUser(t *testing.T) {
User{User: "foo", Roles: []string{"role1", "role2"}}, User{User: "foo", Roles: []string{"role1", "role2"}},
false, false,
}, },
{ { // empty password will not overwrite the previous password
User{User: "foo"}, User{User: "foo", Password: "foo", Roles: []string{}},
User{User: "foo", Password: "$2a$10$aUPOdbOGNawaVSusg3g2wuC3AH6XxIr9/Ms4VgDvzrAVOJPYzZILa"}, User{User: "foo", Password: ""},
User{User: "foo", Roles: []string{}, Password: "$2a$10$aUPOdbOGNawaVSusg3g2wuC3AH6XxIr9/Ms4VgDvzrAVOJPYzZILa"}, User{User: "foo", Password: "foo", Roles: []string{}},
false, false,
}, },
} }
for i, tt := range tbl { for i, tt := range tbl {
out, err := tt.input.merge(tt.merge) out, err := tt.input.merge(tt.merge, passwordStore{})
if err != nil && !tt.iserr { if err != nil && !tt.iserr {
t.Fatalf("Got unexpected error on item %d", i) t.Fatalf("Got unexpected error on item %d", i)
} }