Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

migrate 6 auth test cases to common #3 #15320

Merged
merged 1 commit into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
290 changes: 288 additions & 2 deletions tests/common/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,24 @@ package common

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

"github.com/stretchr/testify/require"

clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/tests/v3/framework/config"
"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"))

const (
PermissionDenied = "etcdserver: permission denied"
AuthenticationFailed = "etcdserver: authentication failed, invalid user ID or password"
)

func TestAuthEnable(t *testing.T) {
Expand Down Expand Up @@ -131,3 +142,278 @@ 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)))

require.ErrorContains(t, testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{}), PermissionDenied)
// 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
require.ErrorContains(t, testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{}), PermissionDenied)
// 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{})
require.ErrorContains(t, err, AuthenticationFailed)
})
}

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
require.ErrorContains(t, testUserAuthClient.Put(ctx, "foo", "baz", config.PutOptions{}), PermissionDenied)

// 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 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"))
require.ErrorContains(t, err, AuthenticationFailed)

// 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"))
require.ErrorContains(t, err, AuthenticationFailed)
})
}

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"},
expectResults: []string{"SUCCESS", "s2", "v"},
expectError: false,
},
// a key of compare case isn't granted
{
compare: []string{`version("c1") = "1"`},
ifSuccess: []string{"get s2"},
ifFail: []string{"get f2"},
expectResults: []string{PermissionDenied},
expectError: true,
},
// a key of success case isn't granted
{
compare: []string{`version("c2") = "1"`},
ifSuccess: []string{"get s1"},
ifFail: []string{"get f2"},
expectResults: []string{PermissionDenied},
expectError: true,
},
// a key of failure case isn't granted
{
compare: []string{`version("c2") = "1"`},
ifSuccess: []string{"get s2"},
ifFail: []string{"get f1"},
expectResults: []string{PermissionDenied},
expectError: true,
},
}

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 req.expectError {
require.Contains(t, err.Error(), req.expectResults[0])
} else {
require.NoError(t, err)
require.Equal(t, req.expectResults, getRespValues(resp))
}
}
})
})
}
}

func mustAbsPath(path string) string {
abs, err := filepath.Abs(path)
if err != nil {
panic(err)
}
return abs
}
49 changes: 25 additions & 24 deletions tests/common/txn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,31 @@ import (
)

type txnReq struct {
compare []string
ifSuccess []string
ifFail []string
results []string
compare []string
ifSuccess []string
ifFail []string
expectResults []string
expectError bool
}

func TestTxnSucc(t *testing.T) {
testRunner.BeforeTest(t)
reqs := []txnReq{
{
compare: []string{`value("key1") != "value2"`, `value("key2") != "value1"`},
ifSuccess: []string{"get key1", "get key2"},
results: []string{"SUCCESS", "key1", "value1", "key2", "value2"},
compare: []string{`value("key1") != "value2"`, `value("key2") != "value1"`},
ifSuccess: []string{"get key1", "get key2"},
expectResults: []string{"SUCCESS", "key1", "value1", "key2", "value2"},
},
{
compare: []string{`version("key1") = "1"`, `version("key2") = "1"`},
ifSuccess: []string{"get key1", "get key2", `put "key \"with\" space" "value \x23"`},
ifFail: []string{`put key1 "fail"`, `put key2 "fail"`},
results: []string{"SUCCESS", "key1", "value1", "key2", "value2", "OK"},
compare: []string{`version("key1") = "1"`, `version("key2") = "1"`},
ifSuccess: []string{"get key1", "get key2", `put "key \"with\" space" "value \x23"`},
ifFail: []string{`put key1 "fail"`, `put key2 "fail"`},
expectResults: []string{"SUCCESS", "key1", "value1", "key2", "value2", "OK"},
},
{
compare: []string{`version("key \"with\" space") = "1"`},
ifSuccess: []string{`get "key \"with\" space"`},
results: []string{"SUCCESS", `key "with" space`, "value \x23"},
compare: []string{`version("key \"with\" space") = "1"`},
ifSuccess: []string{`get "key \"with\" space"`},
expectResults: []string{"SUCCESS", `key "with" space`, "value \x23"},
},
}
for _, cfg := range clusterTestCases() {
Expand All @@ -76,7 +77,7 @@ func TestTxnSucc(t *testing.T) {
if err != nil {
t.Errorf("Txn returned error: %s", err)
}
assert.Equal(t, req.results, getRespValues(resp))
assert.Equal(t, req.expectResults, getRespValues(resp))
}
})
})
Expand All @@ -87,16 +88,16 @@ func TestTxnFail(t *testing.T) {
testRunner.BeforeTest(t)
reqs := []txnReq{
{
compare: []string{`version("key") < "0"`},
ifSuccess: []string{`put key "success"`},
ifFail: []string{`put key "fail"`},
results: []string{"FAILURE", "OK"},
compare: []string{`version("key") < "0"`},
ifSuccess: []string{`put key "success"`},
ifFail: []string{`put key "fail"`},
expectResults: []string{"FAILURE", "OK"},
},
{
compare: []string{`value("key1") != "value1"`},
ifSuccess: []string{`put key1 "success"`},
ifFail: []string{`put key1 "fail"`},
results: []string{"FAILURE", "OK"},
compare: []string{`value("key1") != "value1"`},
ifSuccess: []string{`put key1 "success"`},
ifFail: []string{`put key1 "fail"`},
expectResults: []string{"FAILURE", "OK"},
},
}
for _, cfg := range clusterTestCases() {
Expand All @@ -117,7 +118,7 @@ func TestTxnFail(t *testing.T) {
if err != nil {
t.Errorf("Txn returned error: %s", err)
}
assert.Equal(t, req.results, getRespValues(resp))
assert.Equal(t, req.expectResults, getRespValues(resp))
}
})
})
Expand Down
Loading