diff --git a/tests/common/auth_test.go b/tests/common/auth_test.go new file mode 100644 index 00000000000..bdd92b01783 --- /dev/null +++ b/tests/common/auth_test.go @@ -0,0 +1,67 @@ +// Copyright 2022 The etcd Authors +// +// 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 common + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.etcd.io/etcd/tests/v3/framework/config" + "go.etcd.io/etcd/tests/v3/framework/testutils" +) + +func TestAuthEnable(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.NoErrorf(t, setupAuth(cc, []authRole{}, []authUser{rootUser}), "failed to enable auth") + }) +} + +func TestAuthDisable(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, "hoo", "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))) + + // test-user doesn't have the permission, it must fail + require.Error(t, testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})) + require.NoErrorf(t, rootAuthClient.AuthDisable(ctx), "failed to disable auth") + // now ErrAuthNotEnabled of Authenticate() is simply ignored + require.NoError(t, testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})) + // now the key can be accessed + require.NoError(t, cc.Put(ctx, "hoo", "bar", config.PutOptions{})) + // confirm put succeeded + resp, err := cc.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) + } + }) +} diff --git a/tests/common/auth_util.go b/tests/common/auth_util.go index 1a78a19cf26..c75f20cd312 100644 --- a/tests/common/auth_util.go +++ b/tests/common/auth_util.go @@ -20,7 +20,28 @@ import ( clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/tests/v3/framework/config" - intf "go.etcd.io/etcd/tests/v3/framework/interfaces" + "go.etcd.io/etcd/tests/v3/framework/interfaces" +) + +const ( + rootUserName = "root" + rootRoleName = "root" + rootPassword = "rootPassword" + testUserName = "test-user" + testRoleName = "test-role" + testPassword = "pass" +) + +var ( + rootUser = authUser{user: rootUserName, pass: rootPassword, role: rootRoleName} + testUser = authUser{user: testUserName, pass: testPassword, role: testRoleName} + + testRole = authRole{ + role: testRoleName, + permission: clientv3.PermissionType(clientv3.PermReadWrite), + key: "foo", + keyEnd: "", + } ) type authRole struct { @@ -36,7 +57,7 @@ type authUser struct { role string } -func createRoles(c intf.Client, roles []authRole) error { +func createRoles(c interfaces.Client, roles []authRole) error { for _, r := range roles { // add role if _, err := c.RoleAdd(context.TODO(), r.role); err != nil { @@ -52,7 +73,7 @@ func createRoles(c intf.Client, roles []authRole) error { return nil } -func createUsers(c intf.Client, users []authUser) error { +func createUsers(c interfaces.Client, users []authUser) error { for _, u := range users { // add user if _, err := c.UserAdd(context.TODO(), u.user, u.pass, config.UserAddOptions{}); err != nil { @@ -68,7 +89,7 @@ func createUsers(c intf.Client, users []authUser) error { return nil } -func setupAuth(c intf.Client, roles []authRole, users []authUser) error { +func setupAuth(c interfaces.Client, roles []authRole, users []authUser) error { // create roles if err := createRoles(c, roles); err != nil { return err diff --git a/tests/e2e/ctl_v3_auth_test.go b/tests/e2e/ctl_v3_auth_test.go index bcdc2254ced..8c3d01f91ae 100644 --- a/tests/e2e/ctl_v3_auth_test.go +++ b/tests/e2e/ctl_v3_auth_test.go @@ -26,10 +26,6 @@ import ( "go.etcd.io/etcd/tests/v3/framework/e2e" ) -func TestCtlV3AuthEnable(t *testing.T) { - testCtl(t, authEnableTest) -} -func TestCtlV3AuthDisable(t *testing.T) { testCtl(t, authDisableTest) } func TestCtlV3AuthGracefulDisable(t *testing.T) { testCtl(t, authGracefulDisableTest) } func TestCtlV3AuthStatus(t *testing.T) { testCtl(t, authStatusTest) } func TestCtlV3AuthWriteKey(t *testing.T) { testCtl(t, authCredWriteKeyTest) } @@ -76,14 +72,7 @@ func TestCtlV3AuthJWTExpire(t *testing.T) { testCtl(t, authTestJWTExpire, withCfg(*e2e.NewConfigJWT())) } func TestCtlV3AuthRevisionConsistency(t *testing.T) { testCtl(t, authTestRevisionConsistency) } - -func TestCtlV3AuthTestCacheReload(t *testing.T) { testCtl(t, authTestCacheReload) } - -func authEnableTest(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } -} +func TestCtlV3AuthTestCacheReload(t *testing.T) { testCtl(t, authTestCacheReload) } func authEnable(cx ctlCtx) error { // create root user with root role @@ -104,46 +93,6 @@ func ctlV3AuthEnable(cx ctlCtx) error { return e2e.SpawnWithExpectWithEnv(cmdArgs, cx.envMap, "Authentication Enabled") } -func authDisableTest(cx ctlCtx) { - // a key that isn't granted to test-user - if err := ctlV3Put(cx, "hoo", "a", ""); err != nil { - cx.t.Fatal(err) - } - - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - // test-user doesn't have the permission, it must fail - cx.user, cx.pass = "test-user", "pass" - err := ctlV3PutFailPerm(cx, "hoo", "bar") - require.ErrorContains(cx.t, err, "permission denied") - - cx.user, cx.pass = "root", "root" - if err := ctlV3AuthDisable(cx); err != nil { - cx.t.Fatalf("authDisableTest ctlV3AuthDisable error (%v)", err) - } - - // now ErrAuthNotEnabled of Authenticate() is simply ignored - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3Put(cx, "hoo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - - // now the key can be accessed - cx.user, cx.pass = "", "" - if err := ctlV3Put(cx, "hoo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - // confirm put succeeded - if err := ctlV3Get(cx, []string{"hoo"}, []kv{{"hoo", "bar"}}...); err != nil { - cx.t.Fatal(err) - } -} - func authGracefulDisableTest(cx ctlCtx) { if err := authEnable(cx); err != nil { cx.t.Fatal(err)