Skip to content

Commit

Permalink
migrate auto tests to common #3
Browse files Browse the repository at this point in the history
Signed-off-by: Chao Chen <chaochn@amazon.com>
  • Loading branch information
chaochn47 committed Feb 15, 2023
1 parent 50532c9 commit 7b579e1
Show file tree
Hide file tree
Showing 2 changed files with 297 additions and 303 deletions.
297 changes: 295 additions & 2 deletions tests/common/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,24 @@ package common

import (
"context"
"fmt"
"path/filepath"
"strings"
"testing"
"time"

"github.com/stretchr/testify/require"

"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/tests/v3/framework/config"
"go.etcd.io/etcd/tests/v3/framework/interfaces"
"go.etcd.io/etcd/tests/v3/framework/testutils"

"github.com/stretchr/testify/require"
)

var defaultAuthToken = fmt.Sprintf("jwt,pub-key=%s,priv-key=%s,sign-method=RS256,ttl=1s",
mustAbsPath("../fixtures/server.crt"), mustAbsPath("../fixtures/server.key.insecure"))

func TestAuthEnable(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
Expand Down Expand Up @@ -131,3 +140,287 @@ func TestAuthStatus(t *testing.T) {
require.Truef(t, resp.Enabled, "want enabled but got not enabled")
})
}

func TestAuthRoleUpdate(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
require.NoError(t, cc.Put(ctx, "foo", "bar", config.PutOptions{}))
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")

rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))

putFailPerm(ctx, testUserAuthClient, "hoo", "bar", t)
// grant a new key
_, err := rootAuthClient.RoleGrantPermission(ctx, testRoleName, "hoo", "", clientv3.PermissionType(clientv3.PermReadWrite))
require.NoError(t, err)
// try a newly granted key
require.NoError(t, testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{}))
// confirm put succeeded
resp, err := testUserAuthClient.Get(ctx, "hoo", config.GetOptions{})
require.NoError(t, err)
if len(resp.Kvs) != 1 || string(resp.Kvs[0].Key) != "hoo" || string(resp.Kvs[0].Value) != "bar" {
t.Fatalf("want key value pair 'hoo' 'bar' but got %+v", resp.Kvs)
}
// revoke the newly granted key
_, err = rootAuthClient.RoleRevokePermission(ctx, testRoleName, "hoo", "")
require.NoError(t, err)
// try put to the revoked key
putFailPerm(ctx, testUserAuthClient, "hoo", "bar", t)
// confirm a key still granted can be accessed
resp, err = testUserAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
if len(resp.Kvs) != 1 || string(resp.Kvs[0].Key) != "foo" || string(resp.Kvs[0].Value) != "bar" {
t.Fatalf("want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
}
})
}

func TestAuthUserDeleteDuringOps(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
require.NoError(t, cc.Put(ctx, "foo", "bar", config.PutOptions{}))
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")

rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))

// create a key
require.NoError(t, testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{}))
// confirm put succeeded
resp, err := testUserAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
if len(resp.Kvs) != 1 || string(resp.Kvs[0].Key) != "foo" || string(resp.Kvs[0].Value) != "bar" {
t.Fatalf("want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
}
// delete the user
_, err = rootAuthClient.UserDelete(ctx, testUserName)
require.NoError(t, err)
// check the user is deleted
err = testUserAuthClient.Put(ctx, "foo", "baz", config.PutOptions{})
if err == nil || !strings.Contains(err.Error(), rpctypes.ErrAuthFailed.Error()) {
t.Errorf("want error %s but got %v", rpctypes.ErrAuthFailed.Error(), err)
}
})
}

func TestAuthRoleRevokeDuringOps(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
require.NoError(t, cc.Put(ctx, "foo", "bar", config.PutOptions{}))
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")

rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))

// create a key
require.NoError(t, testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{}))
// confirm put succeeded
resp, err := testUserAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
if len(resp.Kvs) != 1 || string(resp.Kvs[0].Key) != "foo" || string(resp.Kvs[0].Value) != "bar" {
t.Fatalf("want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
}
// create a new role
_, err = rootAuthClient.RoleAdd(ctx, "test-role2")
require.NoError(t, err)
// grant a new key to the new role
_, err = rootAuthClient.RoleGrantPermission(ctx, "test-role2", "hoo", "", clientv3.PermissionType(clientv3.PermReadWrite))
require.NoError(t, err)
// grant the new role to the user
_, err = rootAuthClient.UserGrantRole(ctx, testUserName, "test-role2")
require.NoError(t, err)

// try a newly granted key
require.NoError(t, testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{}))
// confirm put succeeded
resp, err = testUserAuthClient.Get(ctx, "hoo", config.GetOptions{})
require.NoError(t, err)
if len(resp.Kvs) != 1 || string(resp.Kvs[0].Key) != "hoo" || string(resp.Kvs[0].Value) != "bar" {
t.Fatalf("want key value pair 'hoo' 'bar' but got %+v", resp.Kvs)
}
// revoke a role from the user
_, err = rootAuthClient.UserRevokeRole(ctx, testUserName, testRoleName)
require.NoError(t, err)
// check the role is revoked and permission is lost from the user
putFailPerm(ctx, testUserAuthClient, "foo", "baz", t)

// try a key that can be accessed from the remaining role
require.NoError(t, testUserAuthClient.Put(ctx, "hoo", "bar2", config.PutOptions{}))
// confirm put succeeded
resp, err = testUserAuthClient.Get(ctx, "hoo", config.GetOptions{})
require.NoError(t, err)
if len(resp.Kvs) != 1 || string(resp.Kvs[0].Key) != "hoo" || string(resp.Kvs[0].Value) != "bar2" {
t.Fatalf("want key value pair 'hoo' 'bar2' but got %+v", resp.Kvs)
}
})
}

func putFailPerm(ctx context.Context, cc interfaces.Client, key, val string, t *testing.T) {
err := cc.Put(ctx, key, val, config.PutOptions{})
if err == nil || !strings.Contains(err.Error(), rpctypes.ErrPermissionDenied.Error()) {
t.Errorf("want error %s but got %v", rpctypes.ErrPermissionDenied.Error(), err)
}
}

func TestAuthWriteKey(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
require.NoError(t, cc.Put(ctx, "foo", "a", config.PutOptions{}))
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")

rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))

// confirm root role can access to all keys
require.NoError(t, rootAuthClient.Put(ctx, "foo", "bar", config.PutOptions{}))
resp, err := rootAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
if len(resp.Kvs) != 1 || string(resp.Kvs[0].Key) != "foo" || string(resp.Kvs[0].Value) != "bar" {
t.Fatalf("want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
}
// try invalid user
_, err = clus.Client(WithAuth("a", "b"))
if err == nil || !strings.Contains(err.Error(), rpctypes.ErrAuthFailed.Error()) {
t.Errorf("want error %s but got %v", rpctypes.ErrAuthFailed.Error(), err)
}

// try good user
require.NoError(t, testUserAuthClient.Put(ctx, "foo", "bar2", config.PutOptions{}))
// confirm put succeeded
resp, err = testUserAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
if len(resp.Kvs) != 1 || string(resp.Kvs[0].Key) != "foo" || string(resp.Kvs[0].Value) != "bar2" {
t.Fatalf("want key value pair 'foo' 'bar2' but got %+v", resp.Kvs)
}

// try bad password
_, err = clus.Client(WithAuth(testUserName, "badpass"))
if err == nil || !strings.Contains(err.Error(), rpctypes.ErrAuthFailed.Error()) {
t.Errorf("want error %s but got %v", rpctypes.ErrAuthFailed.Error(), err)
}
})
}

func TestAuthTxn(t *testing.T) {
tcs := []struct {
name string
cfg config.ClusterConfig
}{
{
"NoJWT",
config.ClusterConfig{ClusterSize: 1},
},
{
"JWT",
config.ClusterConfig{ClusterSize: 1, AuthToken: defaultAuthToken},
},
}

reqs := []txnReq{
{
compare: []string{`version("c2") = "1"`},
ifSuccess: []string{"get s2"},
ifFail: []string{"get f2"},
results: []string{"SUCCESS", "s2", "v"},
},
// a key of compare case isn't granted
{
compare: []string{`version("c1") = "1"`},
ifSuccess: []string{"get s2"},
ifFail: []string{"get f2"},
results: []string{"etcdserver: permission denied"},
},
// a key of success case isn't granted
{
compare: []string{`version("c2") = "1"`},
ifSuccess: []string{"get s1"},
ifFail: []string{"get f2"},
results: []string{"etcdserver: permission denied"},
},
// a key of failure case isn't granted
{
compare: []string{`version("c2") = "1"`},
ifSuccess: []string{"get s2"},
ifFail: []string{"get f1"},
results: []string{"etcdserver: permission denied"},
},
}

for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.cfg))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
// keys with 1 suffix aren't granted to test-user
keys := []string{"c1", "s1", "f1"}
// keys with 2 suffix are granted to test-user, see Line 399
grantedKeys := []string{"c2", "s2", "f2"}
for _, key := range keys {
if err := cc.Put(ctx, key, "v", config.PutOptions{}); err != nil {
t.Fatal(err)
}
}
for _, key := range grantedKeys {
if err := cc.Put(ctx, key, "v", config.PutOptions{}); err != nil {
t.Fatal(err)
}
}

require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))

// grant keys to test-user
for _, key := range grantedKeys {
if _, err := rootAuthClient.RoleGrantPermission(ctx, testRoleName, key, "", clientv3.PermissionType(clientv3.PermReadWrite)); err != nil {
t.Fatal(err)
}
}
for _, req := range reqs {
resp, err := testUserAuthClient.Txn(ctx, req.compare, req.ifSuccess, req.ifFail, config.TxnOptions{
Interactive: true,
})
if strings.Contains(req.results[0], "denied") {
require.Contains(t, err.Error(), req.results[0])
} else {
require.NoError(t, err)
require.Equal(t, req.results, getRespValues(resp))
}
}
})
})
}
}

func mustAbsPath(path string) string {
abs, err := filepath.Abs(path)
if err != nil {
panic(err)
}
return abs
}
Loading

0 comments on commit 7b579e1

Please sign in to comment.