mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #13819 from endocrimes/dani/auth_test.go
migrate e2e/users tests to common framework
This commit is contained in:
commit
c4d055fe7b
366
tests/common/user_test.go
Normal file
366
tests/common/user_test.go
Normal file
@ -0,0 +1,366 @@
|
||||
// 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 (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.etcd.io/etcd/tests/v3/framework/config"
|
||||
"go.etcd.io/etcd/tests/v3/framework/testutils"
|
||||
)
|
||||
|
||||
func TestUserAdd_Simple(t *testing.T) {
|
||||
testRunner.BeforeTest(t)
|
||||
tcs := []struct {
|
||||
name string
|
||||
config config.ClusterConfig
|
||||
}{
|
||||
{
|
||||
name: "NoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1},
|
||||
},
|
||||
{
|
||||
name: "PeerTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "PeerAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
nestedCases := []struct {
|
||||
name string
|
||||
username string
|
||||
password string
|
||||
noPassword bool
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "empty_username_not_allowed",
|
||||
username: "",
|
||||
password: "foobar",
|
||||
// Very Vague error expectation because the CLI and the API return very
|
||||
// different error structures.
|
||||
expectedError: "user name",
|
||||
},
|
||||
{
|
||||
// Can create a user with no password, restricted to CN auth
|
||||
name: "no_password_with_noPassword_set",
|
||||
username: "foo",
|
||||
password: "",
|
||||
noPassword: true,
|
||||
},
|
||||
{
|
||||
// Can create a user with no password, but not restricted to CN auth
|
||||
name: "no_password_without_noPassword_set",
|
||||
username: "foo",
|
||||
password: "",
|
||||
noPassword: false,
|
||||
},
|
||||
{
|
||||
name: "regular_user_with_password",
|
||||
username: "foo",
|
||||
password: "bar",
|
||||
},
|
||||
}
|
||||
for _, nc := range nestedCases {
|
||||
t.Run(tc.name+"/"+nc.name, func(t *testing.T) {
|
||||
clus := testRunner.NewCluster(t, tc.config)
|
||||
defer clus.Close()
|
||||
cc := clus.Client()
|
||||
|
||||
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
|
||||
resp, err := cc.UserAdd(nc.username, nc.password, config.UserAddOptions{NoPassword: nc.noPassword})
|
||||
if nc.expectedError != "" {
|
||||
if err != nil {
|
||||
assert.Contains(t, err.Error(), nc.expectedError)
|
||||
return
|
||||
}
|
||||
|
||||
t.Fatalf("expected user creation to fail")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, err: %v", err)
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
t.Fatalf("unexpected nil response to successful user creation")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserAdd_DuplicateUserNotAllowed(t *testing.T) {
|
||||
testRunner.BeforeTest(t)
|
||||
tcs := []struct {
|
||||
name string
|
||||
config config.ClusterConfig
|
||||
}{
|
||||
{
|
||||
name: "NoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1},
|
||||
},
|
||||
{
|
||||
name: "PeerTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "PeerAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
clus := testRunner.NewCluster(t, tc.config)
|
||||
defer clus.Close()
|
||||
cc := clus.Client()
|
||||
|
||||
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
|
||||
user := "barb"
|
||||
password := "rhubarb"
|
||||
|
||||
_, err := cc.UserAdd(user, password, config.UserAddOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("first user creation should succeed, err: %v", err)
|
||||
}
|
||||
|
||||
_, err = cc.UserAdd(user, password, config.UserAddOptions{})
|
||||
if err == nil {
|
||||
t.Fatalf("duplicate user creation should fail")
|
||||
}
|
||||
assert.Contains(t, err.Error(), "etcdserver: user name already exists")
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserList(t *testing.T) {
|
||||
testRunner.BeforeTest(t)
|
||||
tcs := []struct {
|
||||
name string
|
||||
config config.ClusterConfig
|
||||
}{
|
||||
{
|
||||
name: "NoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1},
|
||||
},
|
||||
{
|
||||
name: "PeerTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "PeerAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
clus := testRunner.NewCluster(t, tc.config)
|
||||
defer clus.Close()
|
||||
cc := clus.Client()
|
||||
|
||||
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
|
||||
// No Users Yet
|
||||
resp, err := cc.UserList()
|
||||
if err != nil {
|
||||
t.Fatalf("user listing should succeed, err: %v", err)
|
||||
}
|
||||
if len(resp.Users) != 0 {
|
||||
t.Fatalf("expected no pre-existing users, found: %q", resp.Users)
|
||||
}
|
||||
|
||||
user := "barb"
|
||||
password := "rhubarb"
|
||||
|
||||
_, err = cc.UserAdd(user, password, config.UserAddOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("user creation should succeed, err: %v", err)
|
||||
}
|
||||
|
||||
// Users!
|
||||
resp, err = cc.UserList()
|
||||
if err != nil {
|
||||
t.Fatalf("user listing should succeed, err: %v", err)
|
||||
}
|
||||
if len(resp.Users) != 1 {
|
||||
t.Fatalf("expected one user, found: %q", resp.Users)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserDelete(t *testing.T) {
|
||||
testRunner.BeforeTest(t)
|
||||
tcs := []struct {
|
||||
name string
|
||||
config config.ClusterConfig
|
||||
}{
|
||||
{
|
||||
name: "NoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1},
|
||||
},
|
||||
{
|
||||
name: "PeerTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "PeerAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
clus := testRunner.NewCluster(t, tc.config)
|
||||
defer clus.Close()
|
||||
cc := clus.Client()
|
||||
|
||||
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
|
||||
user := "barb"
|
||||
password := "rhubarb"
|
||||
|
||||
_, err := cc.UserAdd(user, password, config.UserAddOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("user creation should succeed, err: %v", err)
|
||||
}
|
||||
|
||||
resp, err := cc.UserList()
|
||||
if err != nil {
|
||||
t.Fatalf("user listing should succeed, err: %v", err)
|
||||
}
|
||||
if len(resp.Users) != 1 {
|
||||
t.Fatalf("expected one user, found: %q", resp.Users)
|
||||
}
|
||||
|
||||
// Delete barb, sorry barb!
|
||||
_, err = cc.UserDelete(user)
|
||||
if err != nil {
|
||||
t.Fatalf("user deletion should succeed at first, err: %v", err)
|
||||
}
|
||||
|
||||
resp, err = cc.UserList()
|
||||
if err != nil {
|
||||
t.Fatalf("user listing should succeed, err: %v", err)
|
||||
}
|
||||
if len(resp.Users) != 0 {
|
||||
t.Fatalf("expected no users after deletion, found: %q", resp.Users)
|
||||
}
|
||||
|
||||
// Try to delete barb again
|
||||
_, err = cc.UserDelete(user)
|
||||
if err == nil {
|
||||
t.Fatalf("deleting a non-existent user should fail")
|
||||
}
|
||||
assert.Contains(t, err.Error(), "user name not found")
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserChangePassword(t *testing.T) {
|
||||
testRunner.BeforeTest(t)
|
||||
tcs := []struct {
|
||||
name string
|
||||
config config.ClusterConfig
|
||||
}{
|
||||
{
|
||||
name: "NoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1},
|
||||
},
|
||||
{
|
||||
name: "PeerTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "PeerAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
clus := testRunner.NewCluster(t, tc.config)
|
||||
defer clus.Close()
|
||||
cc := clus.Client()
|
||||
|
||||
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
|
||||
user := "barb"
|
||||
password := "rhubarb"
|
||||
newPassword := "potato"
|
||||
|
||||
_, err := cc.UserAdd(user, password, config.UserAddOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("user creation should succeed, err: %v", err)
|
||||
}
|
||||
|
||||
err = cc.UserChangePass(user, newPassword)
|
||||
if err != nil {
|
||||
t.Fatalf("user password change should succeed, err: %v", err)
|
||||
}
|
||||
|
||||
err = cc.UserChangePass("non-existent-user", newPassword)
|
||||
if err == nil {
|
||||
t.Fatalf("user password change for non-existent user should fail")
|
||||
}
|
||||
assert.Contains(t, err.Error(), "user name not found")
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@ -1305,3 +1305,24 @@ func ctlV3EndpointHealth(cx ctlCtx) error {
|
||||
}
|
||||
return e2e.SpawnWithExpects(cmdArgs, cx.envMap, lines...)
|
||||
}
|
||||
|
||||
func ctlV3User(cx ctlCtx, args []string, expStr string, stdIn []string) error {
|
||||
cmdArgs := append(cx.PrefixArgs(), "user")
|
||||
cmdArgs = append(cmdArgs, args...)
|
||||
|
||||
proc, err := e2e.SpawnCmd(cmdArgs, cx.envMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer proc.Close()
|
||||
|
||||
// Send 'stdIn' strings as input.
|
||||
for _, s := range stdIn {
|
||||
if err = proc.Send(s + "\r"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = proc.Expect(expStr)
|
||||
return err
|
||||
}
|
||||
|
@ -1,209 +0,0 @@
|
||||
// Copyright 2016 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 e2e
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.etcd.io/etcd/tests/v3/framework/e2e"
|
||||
)
|
||||
|
||||
func TestCtlV3UserAdd(t *testing.T) { testCtl(t, userAddTest) }
|
||||
func TestCtlV3UserAddNoTLS(t *testing.T) { testCtl(t, userAddTest, withCfg(*e2e.NewConfigNoTLS())) }
|
||||
func TestCtlV3UserAddClientTLS(t *testing.T) {
|
||||
testCtl(t, userAddTest, withCfg(*e2e.NewConfigClientTLS()))
|
||||
}
|
||||
func TestCtlV3UserAddPeerTLS(t *testing.T) { testCtl(t, userAddTest, withCfg(*e2e.NewConfigPeerTLS())) }
|
||||
func TestCtlV3UserAddTimeout(t *testing.T) { testCtl(t, userAddTest, withDialTimeout(0)) }
|
||||
func TestCtlV3UserAddClientAutoTLS(t *testing.T) {
|
||||
testCtl(t, userAddTest, withCfg(*e2e.NewConfigClientAutoTLS()))
|
||||
}
|
||||
func TestCtlV3UserList(t *testing.T) { testCtl(t, userListTest) }
|
||||
func TestCtlV3UserListNoTLS(t *testing.T) { testCtl(t, userListTest, withCfg(*e2e.NewConfigNoTLS())) }
|
||||
func TestCtlV3UserListClientTLS(t *testing.T) {
|
||||
testCtl(t, userListTest, withCfg(*e2e.NewConfigClientTLS()))
|
||||
}
|
||||
func TestCtlV3UserListPeerTLS(t *testing.T) {
|
||||
testCtl(t, userListTest, withCfg(*e2e.NewConfigPeerTLS()))
|
||||
}
|
||||
func TestCtlV3UserListClientAutoTLS(t *testing.T) {
|
||||
testCtl(t, userListTest, withCfg(*e2e.NewConfigClientAutoTLS()))
|
||||
}
|
||||
func TestCtlV3UserDelete(t *testing.T) { testCtl(t, userDelTest) }
|
||||
func TestCtlV3UserDeleteNoTLS(t *testing.T) { testCtl(t, userDelTest, withCfg(*e2e.NewConfigNoTLS())) }
|
||||
func TestCtlV3UserDeleteClientTLS(t *testing.T) {
|
||||
testCtl(t, userDelTest, withCfg(*e2e.NewConfigClientTLS()))
|
||||
}
|
||||
func TestCtlV3UserDeletePeerTLS(t *testing.T) {
|
||||
testCtl(t, userDelTest, withCfg(*e2e.NewConfigPeerTLS()))
|
||||
}
|
||||
func TestCtlV3UserDeleteClientAutoTLS(t *testing.T) {
|
||||
testCtl(t, userDelTest, withCfg(*e2e.NewConfigClientAutoTLS()))
|
||||
}
|
||||
func TestCtlV3UserPasswd(t *testing.T) { testCtl(t, userPasswdTest) }
|
||||
func TestCtlV3UserPasswdNoTLS(t *testing.T) {
|
||||
testCtl(t, userPasswdTest, withCfg(*e2e.NewConfigNoTLS()))
|
||||
}
|
||||
func TestCtlV3UserPasswdClientTLS(t *testing.T) {
|
||||
testCtl(t, userPasswdTest, withCfg(*e2e.NewConfigClientTLS()))
|
||||
}
|
||||
func TestCtlV3UserPasswdPeerTLS(t *testing.T) {
|
||||
testCtl(t, userPasswdTest, withCfg(*e2e.NewConfigPeerTLS()))
|
||||
}
|
||||
func TestCtlV3UserPasswdClientAutoTLS(t *testing.T) {
|
||||
testCtl(t, userPasswdTest, withCfg(*e2e.NewConfigClientAutoTLS()))
|
||||
}
|
||||
|
||||
type userCmdDesc struct {
|
||||
args []string
|
||||
expectedStr string
|
||||
stdIn []string
|
||||
}
|
||||
|
||||
func userAddTest(cx ctlCtx) {
|
||||
cmdSet := []userCmdDesc{
|
||||
// Adds a user name.
|
||||
{
|
||||
args: []string{"add", "username", "--interactive=false"},
|
||||
expectedStr: "User username created",
|
||||
stdIn: []string{"password"},
|
||||
},
|
||||
// Adds a user name using the usertest:password syntax.
|
||||
{
|
||||
args: []string{"add", "usertest:password"},
|
||||
expectedStr: "User usertest created",
|
||||
stdIn: []string{},
|
||||
},
|
||||
// Tries to add a user with empty username.
|
||||
{
|
||||
args: []string{"add", ":password"},
|
||||
expectedStr: "empty user name is not allowed",
|
||||
stdIn: []string{},
|
||||
},
|
||||
// Tries to add a user name that already exists.
|
||||
{
|
||||
args: []string{"add", "username", "--interactive=false"},
|
||||
expectedStr: "user name already exists",
|
||||
stdIn: []string{"password"},
|
||||
},
|
||||
// Adds a user without password.
|
||||
{
|
||||
args: []string{"add", "userwopasswd", "--no-password"},
|
||||
expectedStr: "User userwopasswd created",
|
||||
stdIn: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for i, cmd := range cmdSet {
|
||||
if err := ctlV3User(cx, cmd.args, cmd.expectedStr, cmd.stdIn); err != nil {
|
||||
if cx.dialTimeout > 0 && !isGRPCTimedout(err) {
|
||||
cx.t.Fatalf("userAddTest #%d: ctlV3User error (%v)", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func userListTest(cx ctlCtx) {
|
||||
cmdSet := []userCmdDesc{
|
||||
// Adds a user name.
|
||||
{
|
||||
args: []string{"add", "username", "--interactive=false"},
|
||||
expectedStr: "User username created",
|
||||
stdIn: []string{"password"},
|
||||
},
|
||||
// List user name
|
||||
{
|
||||
args: []string{"list"},
|
||||
expectedStr: "username",
|
||||
},
|
||||
}
|
||||
|
||||
for i, cmd := range cmdSet {
|
||||
if err := ctlV3User(cx, cmd.args, cmd.expectedStr, cmd.stdIn); err != nil {
|
||||
cx.t.Fatalf("userListTest #%d: ctlV3User error (%v)", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func userDelTest(cx ctlCtx) {
|
||||
cmdSet := []userCmdDesc{
|
||||
// Adds a user name.
|
||||
{
|
||||
args: []string{"add", "username", "--interactive=false"},
|
||||
expectedStr: "User username created",
|
||||
stdIn: []string{"password"},
|
||||
},
|
||||
// Deletes the user name just added.
|
||||
{
|
||||
args: []string{"delete", "username"},
|
||||
expectedStr: "User username deleted",
|
||||
},
|
||||
// Deletes a user name that is not present.
|
||||
{
|
||||
args: []string{"delete", "username"},
|
||||
expectedStr: "user name not found",
|
||||
},
|
||||
}
|
||||
|
||||
for i, cmd := range cmdSet {
|
||||
if err := ctlV3User(cx, cmd.args, cmd.expectedStr, cmd.stdIn); err != nil {
|
||||
cx.t.Fatalf("userDelTest #%d: ctlV3User error (%v)", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func userPasswdTest(cx ctlCtx) {
|
||||
cmdSet := []userCmdDesc{
|
||||
// Adds a user name.
|
||||
{
|
||||
args: []string{"add", "username", "--interactive=false"},
|
||||
expectedStr: "User username created",
|
||||
stdIn: []string{"password"},
|
||||
},
|
||||
// Changes the password.
|
||||
{
|
||||
args: []string{"passwd", "username", "--interactive=false"},
|
||||
expectedStr: "Password updated",
|
||||
stdIn: []string{"password1"},
|
||||
},
|
||||
}
|
||||
|
||||
for i, cmd := range cmdSet {
|
||||
if err := ctlV3User(cx, cmd.args, cmd.expectedStr, cmd.stdIn); err != nil {
|
||||
cx.t.Fatalf("userPasswdTest #%d: ctlV3User error (%v)", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ctlV3User(cx ctlCtx, args []string, expStr string, stdIn []string) error {
|
||||
cmdArgs := append(cx.PrefixArgs(), "user")
|
||||
cmdArgs = append(cmdArgs, args...)
|
||||
|
||||
proc, err := e2e.SpawnCmd(cmdArgs, cx.envMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer proc.Close()
|
||||
|
||||
// Send 'stdIn' strings as input.
|
||||
for _, s := range stdIn {
|
||||
if err = proc.Send(s + "\r"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = proc.Expect(expStr)
|
||||
return err
|
||||
}
|
@ -55,3 +55,7 @@ type DefragOption struct {
|
||||
type LeaseOption struct {
|
||||
WithAttachedKeys bool
|
||||
}
|
||||
|
||||
type UserAddOptions struct {
|
||||
NoPassword bool
|
||||
}
|
||||
|
@ -378,3 +378,90 @@ func (ctl *EtcdctlV3) AlarmDisarm(_ *clientv3.AlarmMember) (*clientv3.AlarmRespo
|
||||
err = json.Unmarshal([]byte(line), &resp)
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
func (ctl *EtcdctlV3) UserAdd(name, password string, opts config.UserAddOptions) (*clientv3.AuthUserAddResponse, error) {
|
||||
args := ctl.cmdArgs()
|
||||
args = append(args, "user", "add")
|
||||
if password == "" {
|
||||
args = append(args, name)
|
||||
} else {
|
||||
args = append(args, fmt.Sprintf("%s:%s", name, password))
|
||||
}
|
||||
|
||||
if opts.NoPassword {
|
||||
args = append(args, "--no-password")
|
||||
}
|
||||
|
||||
args = append(args, "--interactive=false", "-w", "json")
|
||||
|
||||
cmd, err := SpawnCmd(args, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If no password is provided, and NoPassword isn't set, the CLI will always
|
||||
// wait for a password, send an enter in this case for an "empty" password.
|
||||
if !opts.NoPassword && password == "" {
|
||||
err := cmd.Send("\n")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var resp clientv3.AuthUserAddResponse
|
||||
line, err := cmd.Expect("header")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal([]byte(line), &resp)
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
func (ctl *EtcdctlV3) UserList() (*clientv3.AuthUserListResponse, error) {
|
||||
args := ctl.cmdArgs()
|
||||
args = append(args, "user", "list", "-w", "json")
|
||||
cmd, err := SpawnCmd(args, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resp clientv3.AuthUserListResponse
|
||||
line, err := cmd.Expect("header")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal([]byte(line), &resp)
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
func (ctl *EtcdctlV3) UserDelete(name string) (*clientv3.AuthUserDeleteResponse, error) {
|
||||
args := ctl.cmdArgs()
|
||||
args = append(args, "user", "delete", name, "-w", "json")
|
||||
cmd, err := SpawnCmd(args, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resp clientv3.AuthUserDeleteResponse
|
||||
line, err := cmd.Expect("header")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal([]byte(line), &resp)
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
func (ctl *EtcdctlV3) UserChangePass(user, newPass string) error {
|
||||
args := ctl.cmdArgs()
|
||||
args = append(args, "user", "passwd", user, "--interactive=false")
|
||||
cmd, err := SpawnCmd(args, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cmd.Send(newPass + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cmd.Expect("Password updated")
|
||||
return err
|
||||
}
|
||||
|
@ -289,3 +289,25 @@ func (c integrationClient) LeaseRevoke(id clientv3.LeaseID) (*clientv3.LeaseRevo
|
||||
|
||||
return c.Client.Revoke(ctx, id)
|
||||
}
|
||||
|
||||
func (c integrationClient) UserAdd(name, password string, opts config.UserAddOptions) (*clientv3.AuthUserAddResponse, error) {
|
||||
ctx := context.Background()
|
||||
return c.Client.UserAddWithOptions(ctx, name, password, &clientv3.UserAddOptions{
|
||||
NoPassword: opts.NoPassword,
|
||||
})
|
||||
}
|
||||
|
||||
func (c integrationClient) UserList() (*clientv3.AuthUserListResponse, error) {
|
||||
ctx := context.Background()
|
||||
return c.Client.UserList(ctx)
|
||||
}
|
||||
|
||||
func (c integrationClient) UserDelete(name string) (*clientv3.AuthUserDeleteResponse, error) {
|
||||
ctx := context.Background()
|
||||
return c.Client.UserDelete(ctx, name)
|
||||
}
|
||||
|
||||
func (c integrationClient) UserChangePass(user, newPass string) error {
|
||||
_, err := c.Client.UserChangePassword(context.Background(), user, newPass)
|
||||
return err
|
||||
}
|
||||
|
@ -55,4 +55,9 @@ type Client interface {
|
||||
LeaseList() (*clientv3.LeaseLeasesResponse, error)
|
||||
LeaseKeepAliveOnce(id clientv3.LeaseID) (*clientv3.LeaseKeepAliveResponse, error)
|
||||
LeaseRevoke(id clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error)
|
||||
|
||||
UserAdd(name, password string, opts config.UserAddOptions) (*clientv3.AuthUserAddResponse, error)
|
||||
UserList() (*clientv3.AuthUserListResponse, error)
|
||||
UserDelete(name string) (*clientv3.AuthUserDeleteResponse, error)
|
||||
UserChangePass(user, newPass string) error
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user