blob: a3cf86958308bae6b27a0573d60d27690b3cd22a [file]
// 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"
"fmt"
"path/filepath"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/server/v3/storage/mvcc/testutil"
"go.etcd.io/etcd/tests/v3/framework/config"
"go.etcd.io/etcd/tests/v3/framework/testutils"
)
var (
tokenTTL = time.Second * 3
defaultAuthToken = fmt.Sprintf("jwt,pub-key=%s,priv-key=%s,sign-method=RS256,ttl=%s",
mustAbsPath("../fixtures/server.crt"), mustAbsPath("../fixtures/server.key.insecure"), tokenTTL)
defaultKeyPath = mustAbsPath("../fixtures/server.key.insecure")
verifyJWTOnlyAuth = fmt.Sprintf("jwt,pub-key=%s,sign-method=RS256,ttl=%s",
mustAbsPath("../fixtures/server.crt"), tokenTTL)
)
const (
PermissionDenied = "etcdserver: permission denied"
AuthenticationFailed = "etcdserver: authentication failed, invalid user ID or password"
InvalidAuthManagement = "etcdserver: invalid auth management"
testPeerURL = "http://localhost:20011"
)
func TestAuthEnable(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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(t.Context(), 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() {
_, err := cc.Put(ctx, "hoo", "a", config.PutOptions{})
require.NoError(t, 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)))
// test-user doesn't have the permission, it must fail
_, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
require.Error(t, err)
require.NoErrorf(t, rootAuthClient.AuthDisable(ctx), "failed to disable auth")
// now ErrAuthNotEnabled of Authenticate() is simply ignored
_, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
require.NoError(t, err)
// now the key can be accessed
_, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
require.NoError(t, err)
// confirm put succeeded
resp, err := cc.Get(ctx, "hoo", config.GetOptions{})
require.NoError(t, err)
require.Lenf(t, resp.Kvs, 1, "want key value pair 'hoo', 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "hoo", string(resp.Kvs[0].Key), "want key value pair 'hoo', 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "bar", string(resp.Kvs[0].Value), "want key value pair 'hoo', 'bar' but got %+v", resp.Kvs)
})
}
func TestAuthGracefulDisable(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.NoErrorf(t, setupAuth(cc, []authRole{}, []authUser{rootUser}), "failed to enable auth")
donec := make(chan struct{})
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
startedC := make(chan struct{}, 1)
go func() {
defer close(donec)
defer close(startedC)
// sleep a bit to let the watcher connects while auth is still enabled
time.Sleep(time.Second)
// now disable auth...
if err := rootAuthClient.AuthDisable(ctx); err != nil {
t.Errorf("failed to auth disable %v", err)
return
}
// ...and restart the node
clus.Members()[0].Stop()
if err := clus.Members()[0].Start(ctx); err != nil {
t.Errorf("failed to restart member %v", err)
return
}
startedC <- struct{}{}
// the watcher should still work after reconnecting
_, err := rootAuthClient.Put(ctx, "key", "value", config.PutOptions{})
assert.NoErrorf(t, err, "failed to put key value")
}()
<-startedC
wCtx, wCancel := context.WithCancel(ctx)
defer wCancel()
watchCh := rootAuthClient.Watch(wCtx, "key", config.WatchOptions{Revision: 1})
wantedLen := 1
watchTimeout := 10 * time.Second
wanted := []testutils.KV{{Key: "key", Val: "value"}}
kvs, err := testutils.KeyValuesFromWatchChan(watchCh, wantedLen, watchTimeout)
require.NoErrorf(t, err, "failed to get key-values from watch channel %s", err)
require.Equal(t, wanted, kvs)
<-donec
})
}
func TestAuthStatus(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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() {
resp, err := cc.AuthStatus(ctx)
require.NoError(t, err)
require.Falsef(t, resp.Enabled, "want auth not enabled but enabled")
require.NoErrorf(t, setupAuth(cc, []authRole{}, []authUser{rootUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
resp, err = rootAuthClient.AuthStatus(ctx)
require.NoError(t, err)
require.Truef(t, resp.Enabled, "want enabled but got not enabled")
})
}
func TestAuthRoleUpdate(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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() {
_, err := cc.Put(ctx, "foo", "bar", config.PutOptions{})
require.NoError(t, 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)))
_, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
require.ErrorContains(t, err, PermissionDenied)
// grant a new key
_, err = rootAuthClient.RoleGrantPermission(ctx, testRoleName, "hoo", "", clientv3.PermissionType(clientv3.PermReadWrite))
require.NoError(t, err)
// try a newly granted key
_, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
require.NoError(t, err)
// confirm put succeeded
resp, err := testUserAuthClient.Get(ctx, "hoo", config.GetOptions{})
require.NoError(t, err)
require.Lenf(t, resp.Kvs, 1, "want key value pair 'hoo' 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "hoo", string(resp.Kvs[0].Key), "want key value pair 'hoo' 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "bar", string(resp.Kvs[0].Value), "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
_, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
require.ErrorContains(t, err, PermissionDenied)
// confirm a key still granted can be accessed
resp, err = testUserAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
require.Lenf(t, resp.Kvs, 1, "want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "foo", string(resp.Kvs[0].Key), "want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "bar", string(resp.Kvs[0].Value), "want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
})
}
func TestAuthUserDeleteDuringOps(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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() {
_, err := cc.Put(ctx, "foo", "bar", config.PutOptions{})
require.NoError(t, 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)))
// create a key
_, err = testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
require.NoError(t, err)
// confirm put succeeded
resp, err := testUserAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
require.Lenf(t, resp.Kvs, 1, "want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "foo", string(resp.Kvs[0].Key), "want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "bar", string(resp.Kvs[0].Value), "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(t.Context(), 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() {
_, err := cc.Put(ctx, "foo", "bar", config.PutOptions{})
require.NoError(t, 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)))
// create a key
_, err = testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
require.NoError(t, err)
// confirm put succeeded
resp, err := testUserAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
require.Lenf(t, resp.Kvs, 1, "want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "foo", string(resp.Kvs[0].Key), "want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "bar", string(resp.Kvs[0].Value), "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
_, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
require.NoError(t, err)
// confirm put succeeded
resp, err = testUserAuthClient.Get(ctx, "hoo", config.GetOptions{})
require.NoError(t, err)
require.Lenf(t, resp.Kvs, 1, "want key value pair 'hoo' 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "hoo", string(resp.Kvs[0].Key), "want key value pair 'hoo' 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "bar", string(resp.Kvs[0].Value), "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
_, err = testUserAuthClient.Put(ctx, "foo", "baz", config.PutOptions{})
require.ErrorContains(t, err, PermissionDenied)
// try a key that can be accessed from the remaining role
_, err = testUserAuthClient.Put(ctx, "hoo", "bar2", config.PutOptions{})
require.NoError(t, err)
// confirm put succeeded
resp, err = testUserAuthClient.Get(ctx, "hoo", config.GetOptions{})
require.NoError(t, err)
require.Lenf(t, resp.Kvs, 1, "want key value pair 'hoo' 'bar2' but got %+v", resp.Kvs)
require.Equalf(t, "hoo", string(resp.Kvs[0].Key), "want key value pair 'hoo' 'bar2' but got %+v", resp.Kvs)
require.Equalf(t, "bar2", string(resp.Kvs[0].Value), "want key value pair 'hoo' 'bar2' but got %+v", resp.Kvs)
})
}
func TestAuthWriteKey(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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() {
_, err := cc.Put(ctx, "foo", "a", config.PutOptions{})
require.NoError(t, 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)))
// confirm root role can access to all keys
_, err = rootAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
require.NoError(t, err)
resp, err := rootAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
require.Lenf(t, resp.Kvs, 1, "want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "foo", string(resp.Kvs[0].Key), "want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
require.Equalf(t, "bar", string(resp.Kvs[0].Value), "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
_, err = testUserAuthClient.Put(ctx, "foo", "bar2", config.PutOptions{})
require.NoError(t, err)
// confirm put succeeded
resp, err = testUserAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
require.Lenf(t, resp.Kvs, 1, "want key value pair 'foo' 'bar2' but got %+v", resp.Kvs)
require.Equalf(t, "foo", string(resp.Kvs[0].Key), "want key value pair 'foo' 'bar2' but got %+v", resp.Kvs)
require.Equalf(t, "bar2", string(resp.Kvs[0].Value), "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(t.Context(), 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 {
_, err := cc.Put(ctx, key, "v", config.PutOptions{})
require.NoError(t, err)
}
for _, key := range grantedKeys {
_, err := cc.Put(ctx, key, "v", config.PutOptions{})
require.NoError(t, 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 {
_, err := rootAuthClient.RoleGrantPermission(ctx, testRoleName, key, "", clientv3.PermissionType(clientv3.PermReadWrite))
require.NoError(t, err)
}
for _, req := range reqs {
resp, err := testUserAuthClient.Txn(ctx, req.compare, req.ifSuccess, req.ifFail, config.TxnOptions{
Interactive: true,
})
if req.expectError {
require.ErrorContains(t, err, req.expectResults[0])
} else {
require.NoError(t, err)
require.Equal(t, req.expectResults, getRespValues(resp))
}
}
})
})
}
}
func TestAuthPrefixPerm(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.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)))
prefix := "/prefix/" // directory like prefix
// grant keys to test-user
_, err := rootAuthClient.RoleGrantPermission(ctx, "test-role", prefix, clientv3.GetPrefixRangeEnd(prefix), clientv3.PermissionType(clientv3.PermReadWrite))
require.NoError(t, err)
// try a prefix granted permission
for i := 0; i < 10; i++ {
key := fmt.Sprintf("%s%d", prefix, i)
_, err = testUserAuthClient.Put(ctx, key, "val", config.PutOptions{})
require.NoError(t, err)
}
// expect put 'key with prefix end "/prefix0"' value failed
_, err = testUserAuthClient.Put(ctx, clientv3.GetPrefixRangeEnd(prefix), "baz", config.PutOptions{})
require.ErrorContains(t, err, PermissionDenied)
// grant the prefix2 keys to test-user
prefix2 := "/prefix2/"
_, err = rootAuthClient.RoleGrantPermission(ctx, "test-role", prefix2, clientv3.GetPrefixRangeEnd(prefix2), clientv3.PermissionType(clientv3.PermReadWrite))
require.NoError(t, err)
for i := 0; i < 10; i++ {
key := fmt.Sprintf("%s%d", prefix2, i)
_, err = testUserAuthClient.Put(ctx, key, "val", config.PutOptions{})
require.NoError(t, err)
}
})
}
func TestAuthLeaseKeepAlive(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.NoErrorf(t, setupAuth(cc, []authRole{}, []authUser{rootUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
resp, err := rootAuthClient.Grant(ctx, 10)
require.NoError(t, err)
leaseID := resp.ID
_, err = rootAuthClient.Put(ctx, "key", "value", config.PutOptions{LeaseID: leaseID})
require.NoError(t, err)
_, err = rootAuthClient.KeepAliveOnce(ctx, leaseID)
require.NoError(t, err)
gresp, err := rootAuthClient.Get(ctx, "key", config.GetOptions{})
require.NoError(t, err)
require.Lenf(t, gresp.Kvs, 1, "want kv pair ('key', 'value') but got %v", gresp.Kvs)
require.Equalf(t, "key", string(gresp.Kvs[0].Key), "want kv pair ('key', 'value') but got %v", gresp.Kvs)
require.Equalf(t, "value", string(gresp.Kvs[0].Value), "want kv pair ('key', 'value') but got %v", gresp.Kvs)
})
}
func TestAuthRevokeWithDelete(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
// create a new role
newTestRoleName := "test-role2"
_, err := rootAuthClient.RoleAdd(ctx, newTestRoleName)
require.NoError(t, err)
// grant the new role to the user
_, err = rootAuthClient.UserGrantRole(ctx, testUserName, newTestRoleName)
require.NoError(t, err)
// check the result
resp, err := rootAuthClient.UserGet(ctx, testUserName)
require.NoError(t, err)
require.ElementsMatch(t, resp.Roles, []string{testRoleName, newTestRoleName})
// delete the role, test-role2 must be revoked from test-user
_, err = rootAuthClient.RoleDelete(ctx, newTestRoleName)
require.NoError(t, err)
// check the result
resp, err = rootAuthClient.UserGet(ctx, testUserName)
require.NoError(t, err)
require.ElementsMatch(t, resp.Roles, []string{testRoleName})
})
}
func TestAuthLeaseTimeToLiveExpired(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.NoErrorf(t, setupAuth(cc, []authRole{}, []authUser{rootUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
resp, err := rootAuthClient.Grant(ctx, 2)
require.NoError(t, err)
leaseID := resp.ID
_, err = rootAuthClient.Put(ctx, "key", "val", config.PutOptions{LeaseID: leaseID})
require.NoError(t, err)
// eliminate false positive
time.Sleep(3 * time.Second)
tresp, err := rootAuthClient.TimeToLive(ctx, leaseID, config.LeaseOption{})
require.NoError(t, err)
require.Equal(t, int64(-1), tresp.TTL)
gresp, err := rootAuthClient.Get(ctx, "key", config.GetOptions{})
require.NoError(t, err)
require.Empty(t, gresp.Kvs)
})
}
func TestAuthLeaseGrantLeases(t *testing.T) {
testRunner.BeforeTest(t)
tcs := []testCase{
{
name: "NoJWT",
config: config.ClusterConfig{ClusterSize: 1},
},
{
name: "JWT",
config: config.ClusterConfig{ClusterSize: 1, AuthToken: defaultAuthToken},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
resp, err := rootAuthClient.Grant(ctx, 10)
require.NoError(t, err)
leaseID := resp.ID
lresp, err := rootAuthClient.Leases(ctx)
require.NoError(t, err)
require.Lenf(t, lresp.Leases, 1, "want %v leaseID but got %v leases", leaseID, lresp.Leases)
require.Equalf(t, lresp.Leases[0].ID, leaseID, "want %v leaseID but got %v leases", leaseID, lresp.Leases)
anonAuthClient := testutils.MustClient(clus.Client())
_, err = anonAuthClient.Grant(ctx, 10)
require.ErrorContains(t, err, "etcdserver: user name is empty")
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))
_, err = testUserAuthClient.Grant(ctx, 10)
require.NoError(t, err)
})
})
}
}
func TestAuthMemberAdd(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.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)))
_, err := testUserAuthClient.MemberAdd(ctx, "newmember", []string{testPeerURL})
require.ErrorContains(t, err, PermissionDenied)
_, err = rootAuthClient.MemberAdd(ctx, "newmember", []string{testPeerURL})
require.NoError(t, err)
})
}
func TestAuthCompact(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.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)))
_, err := rootAuthClient.Put(ctx, "key", "value", config.PutOptions{})
require.NoError(t, err)
_, err = rootAuthClient.Put(ctx, "key", "value", config.PutOptions{})
require.NoError(t, err)
_, err = testUserAuthClient.Compact(ctx, 1, config.CompactOption{Physical: true})
require.ErrorContains(t, err, PermissionDenied)
_, err = rootAuthClient.Compact(ctx, 1, config.CompactOption{Physical: true})
require.NoError(t, err)
})
}
func TestAuthLeaseLeases(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.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)))
anonAuthClient := testutils.MustClient(clus.Client())
lresp, err := rootAuthClient.Grant(ctx, 90)
require.NoError(t, err)
firstLeaseID := lresp.ID
_, err = rootAuthClient.Put(ctx, "foo", "value", config.PutOptions{LeaseID: firstLeaseID})
require.NoError(t, err)
lresp, err = rootAuthClient.Grant(ctx, 90)
require.NoError(t, err)
secondLeaseID := lresp.ID
_, err = rootAuthClient.Put(ctx, "foo1", "value", config.PutOptions{LeaseID: secondLeaseID})
require.NoError(t, err)
_, err = testUserAuthClient.Leases(ctx)
require.ErrorContains(t, err, PermissionDenied)
_, err = anonAuthClient.Leases(ctx)
require.ErrorContains(t, err, "etcdserver: user name is empty")
resp, err := rootAuthClient.Leases(ctx)
require.NoError(t, err)
require.Lenf(t, resp.Leases, 2, "want 2 leases but got %v", resp.Leases)
leaseIDs := []clientv3.LeaseID{firstLeaseID, secondLeaseID}
for _, lease := range resp.Leases {
require.Containsf(t, leaseIDs, lease.ID, "unexpected lease ID %v, want one of %v", lease.ID, leaseIDs)
}
_, err = rootAuthClient.Revoke(ctx, secondLeaseID)
require.NoError(t, err)
_, err = testUserAuthClient.Leases(ctx)
require.NoError(t, err)
})
}
func TestAuthMemberList(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.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)))
anonAuthClient := testutils.MustClient(clus.Client())
_, err := testUserAuthClient.MemberList(ctx, false)
require.NoError(t, err)
_, err = anonAuthClient.MemberList(ctx, false)
require.Error(t, err)
require.ErrorContains(t, err, "etcdserver: user name is empty")
_, err = rootAuthClient.MemberList(ctx, false)
require.NoError(t, err)
})
}
func TestAuthMemberRemove(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clusterSize := 3
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: clusterSize}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
memberIDToEndpoints := getMemberIDToEndpoints(ctx, t, clus)
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
memberID, clusterID := memberToRemove(ctx, t, rootAuthClient, clusterSize)
delete(memberIDToEndpoints, memberID)
endpoints := make([]string, 0, len(memberIDToEndpoints))
for _, ep := range memberIDToEndpoints {
endpoints = append(endpoints, ep)
}
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))
// ordinary user cannot remove a member
_, err := testUserAuthClient.MemberRemove(ctx, memberID)
require.ErrorContains(t, err, PermissionDenied)
// root can remove a member, building a client excluding removed member endpoint
rootAuthClient2 := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword), WithEndpoints(endpoints)))
resp, err := rootAuthClient2.MemberRemove(ctx, memberID)
require.NoError(t, err)
require.Equal(t, resp.Header.ClusterId, clusterID)
found := false
for _, member := range resp.Members {
if member.ID == memberID {
found = true
break
}
}
require.Falsef(t, found, "expect removed member not found in member remove response")
})
}
func TestAuthTestInvalidMgmt(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.NoErrorf(t, setupAuth(cc, []authRole{}, []authUser{rootUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
_, err := rootAuthClient.UserDelete(ctx, rootUserName)
require.ErrorContains(t, err, InvalidAuthManagement)
_, err = rootAuthClient.UserRevokeRole(ctx, rootUserName, rootRoleName)
require.ErrorContains(t, err, InvalidAuthManagement)
})
}
func TestAuthLeaseRevoke(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
lresp, err := rootAuthClient.Grant(ctx, 10)
require.NoError(t, err)
_, err = rootAuthClient.Put(ctx, "key", "value", config.PutOptions{LeaseID: lresp.ID})
require.NoError(t, err)
_, err = rootAuthClient.Revoke(ctx, lresp.ID)
require.NoError(t, err)
_, err = rootAuthClient.Get(ctx, "key", config.GetOptions{})
require.NoError(t, err)
lresp, err = rootAuthClient.Grant(ctx, 10)
require.NoError(t, err)
annoAuthClient := testutils.MustClient(clus.Client())
_, err = annoAuthClient.Revoke(ctx, lresp.ID)
require.ErrorContainsf(t, err, "etcdserver: user name is empty", "should fail to revoke lease with unauthenticated client")
})
}
func TestAuthRoleGet(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.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)))
resp, err := rootAuthClient.RoleGet(ctx, testRoleName)
require.NoError(t, err)
requireRolePermissionEqual(t, testRole, resp.Perm)
// test-user can get the information of test-role because it belongs to the role
resp, err = testUserAuthClient.RoleGet(ctx, testRoleName)
require.NoError(t, err)
requireRolePermissionEqual(t, testRole, resp.Perm)
// test-user cannot get the information of root because it doesn't belong to the role
_, err = testUserAuthClient.RoleGet(ctx, rootRoleName)
require.ErrorContains(t, err, PermissionDenied)
})
}
func TestAuthUserGet(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.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)))
resp, err := rootAuthClient.UserGet(ctx, testUserName)
require.NoError(t, err)
requireUserRolesEqual(t, testUser, resp.Roles)
// test-user can get the information of test-user itself
resp, err = testUserAuthClient.UserGet(ctx, testUserName)
require.NoError(t, err)
requireUserRolesEqual(t, testUser, resp.Roles)
// test-user cannot get the information of root
_, err = testUserAuthClient.UserGet(ctx, rootUserName)
require.ErrorContains(t, err, PermissionDenied)
})
}
func TestAuthRoleList(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 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.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
resp, err := rootAuthClient.RoleList(ctx)
require.NoError(t, err)
requireUserRolesEqual(t, testUser, resp.Roles)
})
}
func TestAuthJWTExpire(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1, AuthToken: defaultAuthToken}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))
_, err := testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
require.NoError(t, err)
// wait an expiration of my JWT token
<-time.After(3 * tokenTTL)
// e2e test will generate a new token while
// integration test that re-uses the same etcd client will refresh the token on server failure.
_, err = testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
require.NoError(t, err)
})
}
func TestAuthJWTOnly(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1, AuthToken: verifyJWTOnlyAuth}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
authRev, err := setupAuthAndGetRevision(cc, []authRole{testRole}, []authUser{rootUser, testUser})
require.NoErrorf(t, err, "failed to enable auth")
token, err := createSignedJWT(defaultKeyPath, "RS256", testUserName, authRev)
require.NoErrorf(t, err, "failed to create test user JWT")
testUserAuthClient := testutils.MustClient(clus.Client(WithAuthToken(token)))
_, err = testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
require.NoError(t, err)
})
}
// TestAuthRevisionConsistency ensures auth revision is the same after member restarts
func TestAuthRevisionConsistency(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1, AuthToken: defaultAuthToken}))
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")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
// add user
_, err := rootAuthClient.UserAdd(ctx, testUserName, testPassword, config.UserAddOptions{})
require.NoError(t, err)
// delete the same user
_, err = rootAuthClient.UserDelete(ctx, testUserName)
require.NoError(t, err)
// get node0 auth revision
aresp, err := rootAuthClient.AuthStatus(ctx)
require.NoError(t, err)
oldAuthRevision := aresp.AuthRevision
// restart the node
clus.Members()[0].Stop()
require.NoError(t, clus.Members()[0].Start(ctx))
aresp2, err := rootAuthClient.AuthStatus(ctx)
require.NoError(t, err)
newAuthRevision := aresp2.AuthRevision
require.Equal(t, oldAuthRevision, newAuthRevision)
})
}
// TestAuthTestCacheReload ensures permissions are persisted and will be reloaded after member restarts
func TestAuthTestCacheReload(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1, AuthToken: defaultAuthToken}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))
// create foo since that is within the permission set
// expectation is to succeed
_, err := testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
require.NoError(t, err)
// restart the node
clus.Members()[0].Stop()
require.NoError(t, clus.Members()[0].Start(ctx))
// nothing has changed, but it fails without refreshing cache after restart
_, err = testUserAuthClient.Put(ctx, "foo", "bar2", config.PutOptions{})
require.NoError(t, err)
})
}
// TestAuthLeaseTimeToLive gated lease time to live with RBAC control
func TestAuthLeaseTimeToLive(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1, AuthToken: defaultAuthToken}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))
anonAuthClient := testutils.MustClient(clus.Client())
gresp, err := testUserAuthClient.Grant(ctx, 10)
require.NoError(t, err)
leaseID := gresp.ID
_, err = testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{LeaseID: leaseID})
require.NoError(t, err)
_, err = testUserAuthClient.TimeToLive(ctx, leaseID, config.LeaseOption{WithAttachedKeys: true})
require.NoError(t, err)
_, err = anonAuthClient.TimeToLive(ctx, leaseID, config.LeaseOption{WithAttachedKeys: false})
require.ErrorContains(t, err, "etcdserver: user name is empty")
_, err = anonAuthClient.TimeToLive(ctx, leaseID, config.LeaseOption{WithAttachedKeys: true})
require.ErrorContains(t, err, "etcdserver: user name is empty")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
_, err = rootAuthClient.Put(ctx, "bar", "foo", config.PutOptions{LeaseID: leaseID})
require.NoError(t, err)
// the lease is attached to bar, which test-user cannot access
_, err = testUserAuthClient.TimeToLive(ctx, leaseID, config.LeaseOption{WithAttachedKeys: true})
require.Errorf(t, err, "test-user must not be able to access to the lease, because it's attached to the key bar")
// without --keys, access should be allowed
_, err = testUserAuthClient.TimeToLive(ctx, leaseID, config.LeaseOption{WithAttachedKeys: false})
require.NoError(t, err)
})
}
func TestAuthAlarm(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{
ClusterSize: 1,
QuotaBackendBytes: 1024 * 5,
}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
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)))
anonAuthClient := testutils.MustClient(clus.Client())
for i := 0; ; i++ {
_, err := rootAuthClient.Put(ctx,
testutil.PickKey(int64(i)), strings.Repeat("A", 1024), config.PutOptions{})
if err == nil {
continue
}
require.ErrorContains(t, err, "etcdserver: mvcc: database space exceeded")
break
}
memberID := uint64(0)
for i := 0; i < 10; i++ {
resp, rerr := rootAuthClient.AlarmList(ctx)
require.NoError(t, rerr)
if len(resp.Alarms) > 0 {
memberID = resp.Header.MemberId
break
}
time.Sleep(1 * time.Second)
}
require.NotEqualf(t, uint64(0), memberID, "expect to find alarm with non-zero member ID")
_, err := anonAuthClient.AlarmList(ctx)
require.ErrorContains(t, err, "etcdserver: user name is empty")
_, err = testUserAuthClient.AlarmList(ctx)
require.NoError(t, err)
_, err = testUserAuthClient.AlarmDisarm(ctx, &clientv3.AlarmMember{
MemberID: memberID,
Alarm: pb.AlarmType_NOSPACE,
})
require.ErrorContains(t, err, PermissionDenied)
resp, err := rootAuthClient.AlarmDisarm(ctx, &clientv3.AlarmMember{
MemberID: memberID,
Alarm: pb.AlarmType_NOSPACE,
})
require.NoError(t, err)
require.Lenf(t, resp.Alarms, 1, "expect 1 alarm from disarm but got %v", resp.Alarms)
resp, err = rootAuthClient.AlarmList(ctx)
require.NoError(t, err)
require.Emptyf(t, resp.Alarms, "expect no alarm after disarm but got %v", resp.Alarms)
})
}
func mustAbsPath(path string) string {
abs, err := filepath.Abs(path)
if err != nil {
panic(err)
}
return abs
}