mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
349 lines
8.2 KiB
Go
349 lines
8.2 KiB
Go
// Copyright 2015 CoreOS, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package auth
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt"
|
|
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
|
|
"github.com/coreos/etcd/etcdserver"
|
|
"github.com/coreos/etcd/etcdserver/etcdserverpb"
|
|
"github.com/coreos/etcd/store"
|
|
)
|
|
|
|
func TestMergeUser(t *testing.T) {
|
|
tbl := []struct {
|
|
input User
|
|
merge User
|
|
expect User
|
|
iserr bool
|
|
}{
|
|
{
|
|
User{User: "foo"},
|
|
User{User: "bar"},
|
|
User{},
|
|
true,
|
|
},
|
|
{
|
|
User{User: "foo"},
|
|
User{User: "foo"},
|
|
User{User: "foo", Roles: []string{}},
|
|
false,
|
|
},
|
|
{
|
|
User{User: "foo"},
|
|
User{User: "foo", Grant: []string{"role1"}},
|
|
User{User: "foo", Roles: []string{"role1"}},
|
|
false,
|
|
},
|
|
{
|
|
User{User: "foo", Roles: []string{"role1"}},
|
|
User{User: "foo", Grant: []string{"role1"}},
|
|
User{},
|
|
true,
|
|
},
|
|
{
|
|
User{User: "foo", Roles: []string{"role1"}},
|
|
User{User: "foo", Revoke: []string{"role2"}},
|
|
User{},
|
|
true,
|
|
},
|
|
{
|
|
User{User: "foo", Roles: []string{"role1"}},
|
|
User{User: "foo", Grant: []string{"role2"}},
|
|
User{User: "foo", Roles: []string{"role1", "role2"}},
|
|
false,
|
|
},
|
|
{
|
|
User{User: "foo"},
|
|
User{User: "foo", Password: "bar"},
|
|
User{User: "foo", Roles: []string{}, Password: "$2a$10$aUPOdbOGNawaVSusg3g2wuC3AH6XxIr9/Ms4VgDvzrAVOJPYzZILa"},
|
|
false,
|
|
},
|
|
}
|
|
|
|
for i, tt := range tbl {
|
|
out, err := tt.input.Merge(tt.merge)
|
|
if err != nil && !tt.iserr {
|
|
t.Fatalf("Got unexpected error on item %d", i)
|
|
}
|
|
if !tt.iserr {
|
|
err := bcrypt.CompareHashAndPassword([]byte(out.Password), []byte(tt.merge.Password))
|
|
if err == nil {
|
|
tt.expect.Password = out.Password
|
|
}
|
|
if !reflect.DeepEqual(out, tt.expect) {
|
|
t.Errorf("Unequal merge expectation on item %d: got: %#v, expect: %#v", i, out, tt.expect)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMergeRole(t *testing.T) {
|
|
tbl := []struct {
|
|
input Role
|
|
merge Role
|
|
expect Role
|
|
iserr bool
|
|
}{
|
|
{
|
|
Role{Role: "foo"},
|
|
Role{Role: "bar"},
|
|
Role{},
|
|
true,
|
|
},
|
|
{
|
|
Role{Role: "foo"},
|
|
Role{Role: "foo", Grant: &Permissions{KV: rwPermission{Read: []string{"/foodir"}, Write: []string{"/foodir"}}}},
|
|
Role{Role: "foo", Permissions: Permissions{KV: rwPermission{Read: []string{"/foodir"}, Write: []string{"/foodir"}}}},
|
|
false,
|
|
},
|
|
{
|
|
Role{Role: "foo", Permissions: Permissions{KV: rwPermission{Read: []string{"/foodir"}, Write: []string{"/foodir"}}}},
|
|
Role{Role: "foo", Revoke: &Permissions{KV: rwPermission{Read: []string{"/foodir"}, Write: []string{"/foodir"}}}},
|
|
Role{Role: "foo", Permissions: Permissions{KV: rwPermission{Read: []string{}, Write: []string{}}}},
|
|
false,
|
|
},
|
|
{
|
|
Role{Role: "foo", Permissions: Permissions{KV: rwPermission{Read: []string{"/bardir"}}}},
|
|
Role{Role: "foo", Revoke: &Permissions{KV: rwPermission{Read: []string{"/foodir"}}}},
|
|
Role{},
|
|
true,
|
|
},
|
|
}
|
|
for i, tt := range tbl {
|
|
out, err := tt.input.Merge(tt.merge)
|
|
if err != nil && !tt.iserr {
|
|
t.Fatalf("Got unexpected error on item %d", i)
|
|
}
|
|
if !tt.iserr {
|
|
if !reflect.DeepEqual(out, tt.expect) {
|
|
t.Errorf("Unequal merge expectation on item %d: got: %#v, expect: %#v", i, out, tt.expect)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
type testDoer struct {
|
|
get []etcdserver.Response
|
|
index int
|
|
}
|
|
|
|
func (td *testDoer) Do(_ context.Context, req etcdserverpb.Request) (etcdserver.Response, error) {
|
|
if req.Method == "GET" {
|
|
res := td.get[td.index]
|
|
td.index++
|
|
return res, nil
|
|
}
|
|
return etcdserver.Response{}, nil
|
|
}
|
|
|
|
func TestAllUsers(t *testing.T) {
|
|
d := &testDoer{
|
|
get: []etcdserver.Response{
|
|
{
|
|
Event: &store.Event{
|
|
Action: store.Get,
|
|
Node: &store.NodeExtern{
|
|
Nodes: store.NodeExterns{
|
|
&store.NodeExtern{
|
|
Key: StorePermsPrefix + "/users/cat",
|
|
},
|
|
&store.NodeExtern{
|
|
Key: StorePermsPrefix + "/users/dog",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
expected := []string{"cat", "dog"}
|
|
|
|
s := Store{d, time.Second, false}
|
|
users, err := s.AllUsers()
|
|
if err != nil {
|
|
t.Error("Unexpected error", err)
|
|
}
|
|
if !reflect.DeepEqual(users, expected) {
|
|
t.Error("AllUsers doesn't match given store. Got", users, "expected", expected)
|
|
}
|
|
}
|
|
|
|
func TestGetAndDeleteUser(t *testing.T) {
|
|
data := `{"user": "cat", "roles" : ["animal"]}`
|
|
d := &testDoer{
|
|
get: []etcdserver.Response{
|
|
{
|
|
Event: &store.Event{
|
|
Action: store.Get,
|
|
Node: &store.NodeExtern{
|
|
Key: StorePermsPrefix + "/users/cat",
|
|
Value: &data,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
expected := User{User: "cat", Roles: []string{"animal"}}
|
|
|
|
s := Store{d, time.Second, false}
|
|
out, err := s.GetUser("cat")
|
|
if err != nil {
|
|
t.Error("Unexpected error", err)
|
|
}
|
|
if !reflect.DeepEqual(out, expected) {
|
|
t.Error("GetUser doesn't match given store. Got", out, "expected", expected)
|
|
}
|
|
err = s.DeleteUser("cat")
|
|
if err != nil {
|
|
t.Error("Unexpected error", err)
|
|
}
|
|
}
|
|
|
|
func TestAllRoles(t *testing.T) {
|
|
d := &testDoer{
|
|
get: []etcdserver.Response{
|
|
{
|
|
Event: &store.Event{
|
|
Action: store.Get,
|
|
Node: &store.NodeExtern{
|
|
Nodes: store.NodeExterns{
|
|
&store.NodeExtern{
|
|
Key: StorePermsPrefix + "/roles/animal",
|
|
},
|
|
&store.NodeExtern{
|
|
Key: StorePermsPrefix + "/roles/human",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
expected := []string{"animal", "guest", "human", "root"}
|
|
|
|
s := Store{d, time.Second, false}
|
|
out, err := s.AllRoles()
|
|
if err != nil {
|
|
t.Error("Unexpected error", err)
|
|
}
|
|
if !reflect.DeepEqual(out, expected) {
|
|
t.Error("AllRoles doesn't match given store. Got", out, "expected", expected)
|
|
}
|
|
}
|
|
|
|
func TestGetAndDeleteRole(t *testing.T) {
|
|
data := `{"role": "animal"}`
|
|
d := &testDoer{
|
|
get: []etcdserver.Response{
|
|
{
|
|
Event: &store.Event{
|
|
Action: store.Get,
|
|
Node: &store.NodeExtern{
|
|
Key: StorePermsPrefix + "/roles/animal",
|
|
Value: &data,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
expected := Role{Role: "animal"}
|
|
|
|
s := Store{d, time.Second, false}
|
|
out, err := s.GetRole("animal")
|
|
if err != nil {
|
|
t.Error("Unexpected error", err)
|
|
}
|
|
if !reflect.DeepEqual(out, expected) {
|
|
t.Error("GetRole doesn't match given store. Got", out, "expected", expected)
|
|
}
|
|
err = s.DeleteRole("animal")
|
|
if err != nil {
|
|
t.Error("Unexpected error", err)
|
|
}
|
|
}
|
|
|
|
func TestEnsure(t *testing.T) {
|
|
d := &testDoer{
|
|
get: []etcdserver.Response{
|
|
{
|
|
Event: &store.Event{
|
|
Action: store.Set,
|
|
Node: &store.NodeExtern{
|
|
Key: StorePermsPrefix,
|
|
Dir: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Event: &store.Event{
|
|
Action: store.Set,
|
|
Node: &store.NodeExtern{
|
|
Key: StorePermsPrefix + "/users/",
|
|
Dir: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Event: &store.Event{
|
|
Action: store.Set,
|
|
Node: &store.NodeExtern{
|
|
Key: StorePermsPrefix + "/roles/",
|
|
Dir: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
s := Store{d, time.Second, false}
|
|
err := s.ensureSecurityDirectories()
|
|
if err != nil {
|
|
t.Error("Unexpected error", err)
|
|
}
|
|
}
|
|
|
|
func TestSimpleMatch(t *testing.T) {
|
|
role := Role{Role: "foo", Permissions: Permissions{KV: rwPermission{Read: []string{"/foodir/*", "/fookey"}, Write: []string{"/bardir/*", "/barkey"}}}}
|
|
if !role.HasKeyAccess("/foodir/foo/bar", false) {
|
|
t.Fatal("role lacks expected access")
|
|
}
|
|
if !role.HasKeyAccess("/fookey", false) {
|
|
t.Fatal("role lacks expected access")
|
|
}
|
|
if role.HasKeyAccess("/bardir/bar/foo", false) {
|
|
t.Fatal("role has unexpected access")
|
|
}
|
|
if role.HasKeyAccess("/barkey", false) {
|
|
t.Fatal("role has unexpected access")
|
|
}
|
|
if role.HasKeyAccess("/foodir/foo/bar", true) {
|
|
t.Fatal("role has unexpected access")
|
|
}
|
|
if role.HasKeyAccess("/fookey", true) {
|
|
t.Fatal("role has unexpected access")
|
|
}
|
|
if !role.HasKeyAccess("/bardir/bar/foo", true) {
|
|
t.Fatal("role lacks expected access")
|
|
}
|
|
if !role.HasKeyAccess("/barkey", true) {
|
|
t.Fatal("role lacks expected access")
|
|
}
|
|
}
|