mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00

The root reason of flakes, was that server was considered as ready to early. In particular: ``` ../../bin/etcd-2456648: {"level":"info","ts":"2021-01-11T09:56:44.474+0100","caller":"rafthttp/stream.go:274","msg":"established TCP streaming connection with remote peer","stream-writer-type":"stream Message","local-member-id":"ed5f620d34a8e61b","remote-peer-id":"ca50e9357181d758"} ../../bin/etcd-2456648: {"level":"warn","ts":"2021-01-11T09:56:49.040+0100","caller":"etcdserver/server.go:1942","msg":"failed to publish local member to cluster through raft","local-member-id":"ed5f620d34a8e61b","local-member-attributes":"{Name:infra2 ClientURLs:[http://localhost:20030]}","request-path":"/0/members/ed5f620d34a8e61b/attributes","publish-timeout":"7s","error":"etcdserver: request timed out, possibly due to connection lost"} ../../bin/etcd-2456648: {"level":"info","ts":"2021-01-11T09:56:49.049+0100","caller":"etcdserver/server.go:1921","msg":"published local member to cluster through raft","local-member-id":"ed5f620d34a8e61b","local-member-attributes":"{Name:infra2 ClientURLs:[http://localhost:20030]}","request-path":"/0/members/ed5f620d34a8e61b/attributes","cluster-id":"34f27e83b3bc2ff","publish-timeout":"7s"} ``` was taking 5s. If this was happening concurrently with etcdctl, the etcdctl could timeout. The fix, requires servers to report 'ready to serve client requests' to consider them up. Fixed also some whitelisted 'goroutines'.
1209 lines
34 KiB
Go
1209 lines
34 KiB
Go
// 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 (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"go.etcd.io/etcd/client/v3"
|
|
)
|
|
|
|
func TestCtlV3AuthEnable(t *testing.T) {
|
|
testCtl(t, authEnableTest)
|
|
}
|
|
func TestCtlV3AuthDisable(t *testing.T) { testCtl(t, authDisableTest) }
|
|
func TestCtlV3AuthStatus(t *testing.T) { testCtl(t, authStatusTest) }
|
|
func TestCtlV3AuthWriteKey(t *testing.T) { testCtl(t, authCredWriteKeyTest) }
|
|
func TestCtlV3AuthRoleUpdate(t *testing.T) { testCtl(t, authRoleUpdateTest) }
|
|
func TestCtlV3AuthUserDeleteDuringOps(t *testing.T) { testCtl(t, authUserDeleteDuringOpsTest) }
|
|
func TestCtlV3AuthRoleRevokeDuringOps(t *testing.T) { testCtl(t, authRoleRevokeDuringOpsTest) }
|
|
func TestCtlV3AuthTxn(t *testing.T) { testCtl(t, authTestTxn) }
|
|
func TestCtlV3AuthTxnJWT(t *testing.T) { testCtl(t, authTestTxn, withCfg(*newConfigJWT())) }
|
|
func TestCtlV3AuthPrefixPerm(t *testing.T) { testCtl(t, authTestPrefixPerm) }
|
|
func TestCtlV3AuthMemberAdd(t *testing.T) { testCtl(t, authTestMemberAdd) }
|
|
func TestCtlV3AuthMemberRemove(t *testing.T) {
|
|
testCtl(t, authTestMemberRemove, withQuorum(), withNoStrictReconfig())
|
|
}
|
|
func TestCtlV3AuthMemberUpdate(t *testing.T) { testCtl(t, authTestMemberUpdate) }
|
|
func TestCtlV3AuthRevokeWithDelete(t *testing.T) { testCtl(t, authTestRevokeWithDelete) }
|
|
func TestCtlV3AuthInvalidMgmt(t *testing.T) { testCtl(t, authTestInvalidMgmt) }
|
|
func TestCtlV3AuthFromKeyPerm(t *testing.T) { testCtl(t, authTestFromKeyPerm) }
|
|
func TestCtlV3AuthAndWatch(t *testing.T) { testCtl(t, authTestWatch) }
|
|
func TestCtlV3AuthAndWatchJWT(t *testing.T) { testCtl(t, authTestWatch, withCfg(*newConfigJWT())) }
|
|
|
|
func TestCtlV3AuthLeaseTestKeepAlive(t *testing.T) { testCtl(t, authLeaseTestKeepAlive) }
|
|
func TestCtlV3AuthLeaseTestTimeToLiveExpired(t *testing.T) {
|
|
testCtl(t, authLeaseTestTimeToLiveExpired)
|
|
}
|
|
func TestCtlV3AuthLeaseGrantLeases(t *testing.T) { testCtl(t, authLeaseTestLeaseGrantLeases) }
|
|
func TestCtlV3AuthLeaseGrantLeasesJWT(t *testing.T) {
|
|
testCtl(t, authLeaseTestLeaseGrantLeases, withCfg(*newConfigJWT()))
|
|
}
|
|
func TestCtlV3AuthLeaseRevoke(t *testing.T) { testCtl(t, authLeaseTestLeaseRevoke) }
|
|
|
|
func TestCtlV3AuthRoleGet(t *testing.T) { testCtl(t, authTestRoleGet) }
|
|
func TestCtlV3AuthUserGet(t *testing.T) { testCtl(t, authTestUserGet) }
|
|
func TestCtlV3AuthRoleList(t *testing.T) { testCtl(t, authTestRoleList) }
|
|
|
|
func TestCtlV3AuthDefrag(t *testing.T) { testCtl(t, authTestDefrag) }
|
|
func TestCtlV3AuthEndpointHealth(t *testing.T) {
|
|
testCtl(t, authTestEndpointHealth, withQuorum())
|
|
}
|
|
func TestCtlV3AuthSnapshot(t *testing.T) { testCtl(t, authTestSnapshot) }
|
|
func TestCtlV3AuthSnapshotJWT(t *testing.T) { testCtl(t, authTestSnapshot, withCfg(*newConfigJWT())) }
|
|
func TestCtlV3AuthJWTExpire(t *testing.T) { testCtl(t, authTestJWTExpire, withCfg(*newConfigJWT())) }
|
|
func TestCtlV3AuthRevisionConsistency(t *testing.T) { testCtl(t, authTestRevisionConsistency) }
|
|
|
|
func authEnableTest(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authEnable(cx ctlCtx) error {
|
|
// create root user with root role
|
|
if err := ctlV3User(cx, []string{"add", "root", "--interactive=false"}, "User root created", []string{"root"}); err != nil {
|
|
return fmt.Errorf("failed to create root user %v", err)
|
|
}
|
|
if err := ctlV3User(cx, []string{"grant-role", "root", "root"}, "Role root is granted to user root", nil); err != nil {
|
|
return fmt.Errorf("failed to grant root user root role %v", err)
|
|
}
|
|
if err := ctlV3AuthEnable(cx); err != nil {
|
|
return fmt.Errorf("authEnableTest ctlV3AuthEnable error (%v)", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ctlV3AuthEnable(cx ctlCtx) error {
|
|
cmdArgs := append(cx.PrefixArgs(), "auth", "enable")
|
|
return spawnWithExpect(cmdArgs, "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"
|
|
if err := ctlV3PutFailPerm(cx, "hoo", "bar"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
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 ctlV3AuthDisable(cx ctlCtx) error {
|
|
cmdArgs := append(cx.PrefixArgs(), "auth", "disable")
|
|
return spawnWithExpect(cmdArgs, "Authentication Disabled")
|
|
}
|
|
|
|
func authStatusTest(cx ctlCtx) {
|
|
cmdArgs := append(cx.PrefixArgs(), "auth", "status")
|
|
if err := spawnWithExpects(cmdArgs, "Authentication Status: false", "AuthRevision:"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
cmdArgs = append(cx.PrefixArgs(), "auth", "status")
|
|
|
|
if err := spawnWithExpects(cmdArgs, "Authentication Status: true", "AuthRevision:"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cmdArgs = append(cx.PrefixArgs(), "auth", "status", "--write-out", "json")
|
|
if err := spawnWithExpect(cmdArgs, "enabled"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
if err := spawnWithExpect(cmdArgs, "authRevision"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authCredWriteKeyTest(cx ctlCtx) {
|
|
// baseline key to check for failed puts
|
|
if err := ctlV3Put(cx, "foo", "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)
|
|
|
|
// confirm root role can access to all keys
|
|
if err := ctlV3Put(cx, "foo", "bar", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try invalid user
|
|
cx.user, cx.pass = "a", "b"
|
|
if err := ctlV3PutFailAuth(cx, "foo", "bar"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
// confirm put failed
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try good user
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3Put(cx, "foo", "bar2", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
// confirm put succeeded
|
|
if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar2"}}...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try bad password
|
|
cx.user, cx.pass = "test-user", "badpass"
|
|
if err := ctlV3PutFailAuth(cx, "foo", "baz"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
// confirm put failed
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar2"}}...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authRoleUpdateTest(cx ctlCtx) {
|
|
if err := ctlV3Put(cx, "foo", "bar", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
// try put to not granted key
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3PutFailPerm(cx, "hoo", "bar"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// grant a new key
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "hoo", "", false}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try a newly granted key
|
|
cx.user, cx.pass = "test-user", "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)
|
|
}
|
|
|
|
// revoke the newly granted key
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3RoleRevokePermission(cx, "test-role", "hoo", "", false); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try put to the revoked key
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3PutFailPerm(cx, "hoo", "bar"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// confirm a key still granted can be accessed
|
|
if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authUserDeleteDuringOpsTest(cx ctlCtx) {
|
|
if err := ctlV3Put(cx, "foo", "bar", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
// create a key
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3Put(cx, "foo", "bar", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
// confirm put succeeded
|
|
if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// delete the user
|
|
cx.user, cx.pass = "root", "root"
|
|
err := ctlV3User(cx, []string{"delete", "test-user"}, "User test-user deleted", []string{})
|
|
if err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// check the user is deleted
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3PutFailAuth(cx, "foo", "baz"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authRoleRevokeDuringOpsTest(cx ctlCtx) {
|
|
if err := ctlV3Put(cx, "foo", "bar", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
// create a key
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3Put(cx, "foo", "bar", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
// confirm put succeeded
|
|
if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// create a new role
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3Role(cx, []string{"add", "test-role2"}, "Role test-role2 created"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
// grant a new key to the new role
|
|
if err := ctlV3RoleGrantPermission(cx, "test-role2", grantingPerm{true, true, "hoo", "", false}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
// grant the new role to the user
|
|
if err := ctlV3User(cx, []string{"grant-role", "test-user", "test-role2"}, "Role test-role2 is granted to user test-user", nil); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try a newly granted key
|
|
cx.user, cx.pass = "test-user", "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)
|
|
}
|
|
|
|
// revoke a role from the user
|
|
cx.user, cx.pass = "root", "root"
|
|
err := ctlV3User(cx, []string{"revoke-role", "test-user", "test-role"}, "Role test-role is revoked from user test-user", []string{})
|
|
if err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// check the role is revoked and permission is lost from the user
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3PutFailPerm(cx, "foo", "baz"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try a key that can be accessed from the remaining role
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3Put(cx, "hoo", "bar2", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
// confirm put succeeded
|
|
if err := ctlV3Get(cx, []string{"hoo"}, []kv{{"hoo", "bar2"}}...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func ctlV3PutFailAuth(cx ctlCtx, key, val string) error {
|
|
return spawnWithExpect(append(cx.PrefixArgs(), "put", key, val), "authentication failed")
|
|
}
|
|
|
|
func ctlV3PutFailPerm(cx ctlCtx, key, val string) error {
|
|
return spawnWithExpect(append(cx.PrefixArgs(), "put", key, val), "permission denied")
|
|
}
|
|
|
|
func authSetupTestUser(cx ctlCtx) {
|
|
if err := ctlV3User(cx, []string{"add", "test-user", "--interactive=false"}, "User test-user created", []string{"pass"}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
if err := spawnWithExpect(append(cx.PrefixArgs(), "role", "add", "test-role"), "Role test-role created"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
if err := ctlV3User(cx, []string{"grant-role", "test-user", "test-role"}, "Role test-role is granted to user test-user", nil); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
cmd := append(cx.PrefixArgs(), "role", "grant-permission", "test-role", "readwrite", "foo")
|
|
if err := spawnWithExpect(cmd, "Role test-role updated"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authTestTxn(cx ctlCtx) {
|
|
// keys with 1 suffix aren't granted to test-user
|
|
// keys with 2 suffix are granted to test-user
|
|
|
|
keys := []string{"c1", "s1", "f1"}
|
|
grantedKeys := []string{"c2", "s2", "f2"}
|
|
for _, key := range keys {
|
|
if err := ctlV3Put(cx, key, "v", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
for _, key := range grantedKeys {
|
|
if err := ctlV3Put(cx, key, "v", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
// grant keys to test-user
|
|
cx.user, cx.pass = "root", "root"
|
|
for _, key := range grantedKeys {
|
|
if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, key, "", false}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// now test txn
|
|
cx.interactive = true
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
|
|
rqs := txnRequests{
|
|
compare: []string{`version("c2") = "1"`},
|
|
ifSucess: []string{"get s2"},
|
|
ifFail: []string{"get f2"},
|
|
results: []string{"SUCCESS", "s2", "v"},
|
|
}
|
|
if err := ctlV3Txn(cx, rqs); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// a key of compare case isn't granted
|
|
rqs = txnRequests{
|
|
compare: []string{`version("c1") = "1"`},
|
|
ifSucess: []string{"get s2"},
|
|
ifFail: []string{"get f2"},
|
|
results: []string{"Error: etcdserver: permission denied"},
|
|
}
|
|
if err := ctlV3Txn(cx, rqs); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// a key of success case isn't granted
|
|
rqs = txnRequests{
|
|
compare: []string{`version("c2") = "1"`},
|
|
ifSucess: []string{"get s1"},
|
|
ifFail: []string{"get f2"},
|
|
results: []string{"Error: etcdserver: permission denied"},
|
|
}
|
|
if err := ctlV3Txn(cx, rqs); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// a key of failure case isn't granted
|
|
rqs = txnRequests{
|
|
compare: []string{`version("c2") = "1"`},
|
|
ifSucess: []string{"get s2"},
|
|
ifFail: []string{"get f1"},
|
|
results: []string{"Error: etcdserver: permission denied"},
|
|
}
|
|
if err := ctlV3Txn(cx, rqs); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authTestPrefixPerm(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
prefix := "/prefix/" // directory like prefix
|
|
// grant keys to test-user
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, prefix, "", true}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try a prefix granted permission
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
for i := 0; i < 10; i++ {
|
|
key := fmt.Sprintf("%s%d", prefix, i)
|
|
if err := ctlV3Put(cx, key, "val", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
if err := ctlV3PutFailPerm(cx, clientv3.GetPrefixRangeEnd(prefix), "baz"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// grant the entire keys to test-user
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "", "", true}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
prefix2 := "/prefix2/"
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
for i := 0; i < 10; i++ {
|
|
key := fmt.Sprintf("%s%d", prefix2, i)
|
|
if err := ctlV3Put(cx, key, "val", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func authTestMemberAdd(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
peerURL := fmt.Sprintf("http://localhost:%d", etcdProcessBasePort+11)
|
|
// ordinary user cannot add a new member
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3MemberAdd(cx, peerURL, false); err == nil {
|
|
cx.t.Fatalf("ordinary user must not be allowed to add a member")
|
|
}
|
|
|
|
// root can add a new member
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3MemberAdd(cx, peerURL, false); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authTestMemberRemove(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
ep, memIDToRemove, clusterID := cx.memberToRemove()
|
|
|
|
// ordinary user cannot remove a member
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3MemberRemove(cx, ep, memIDToRemove, clusterID); err == nil {
|
|
cx.t.Fatalf("ordinary user must not be allowed to remove a member")
|
|
}
|
|
|
|
// root can remove a member
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3MemberRemove(cx, ep, memIDToRemove, clusterID); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authTestMemberUpdate(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
mr, err := getMemberList(cx)
|
|
if err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// ordinary user cannot update a member
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
peerURL := fmt.Sprintf("http://localhost:%d", etcdProcessBasePort+11)
|
|
memberID := fmt.Sprintf("%x", mr.Members[0].ID)
|
|
if err = ctlV3MemberUpdate(cx, memberID, peerURL); err == nil {
|
|
cx.t.Fatalf("ordinary user must not be allowed to update a member")
|
|
}
|
|
|
|
// root can update a member
|
|
cx.user, cx.pass = "root", "root"
|
|
if err = ctlV3MemberUpdate(cx, memberID, peerURL); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authTestCertCN(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3User(cx, []string{"add", "example.com", "--interactive=false"}, "User example.com created", []string{""}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
if err := spawnWithExpect(append(cx.PrefixArgs(), "role", "add", "test-role"), "Role test-role created"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
if err := ctlV3User(cx, []string{"grant-role", "example.com", "test-role"}, "Role test-role is granted to user example.com", nil); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// grant a new key
|
|
if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "hoo", "", false}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try a granted key
|
|
cx.user, cx.pass = "", ""
|
|
if err := ctlV3Put(cx, "hoo", "bar", ""); err != nil {
|
|
cx.t.Error(err)
|
|
}
|
|
|
|
// try a non granted key
|
|
cx.user, cx.pass = "", ""
|
|
if err := ctlV3PutFailPerm(cx, "baz", "bar"); err != nil {
|
|
cx.t.Error(err)
|
|
}
|
|
}
|
|
|
|
func authTestRevokeWithDelete(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
// create a new role
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3Role(cx, []string{"add", "test-role2"}, "Role test-role2 created"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// grant the new role to the user
|
|
if err := ctlV3User(cx, []string{"grant-role", "test-user", "test-role2"}, "Role test-role2 is granted to user test-user", nil); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// check the result
|
|
if err := ctlV3User(cx, []string{"get", "test-user"}, "Roles: test-role test-role2", nil); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// delete the role, test-role2 must be revoked from test-user
|
|
if err := ctlV3Role(cx, []string{"delete", "test-role2"}, "Role test-role2 deleted"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// check the result
|
|
if err := ctlV3User(cx, []string{"get", "test-user"}, "Roles: test-role", nil); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authTestInvalidMgmt(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
if err := ctlV3Role(cx, []string{"delete", "root"}, "Error: etcdserver: invalid auth management"); err == nil {
|
|
cx.t.Fatal("deleting the role root must not be allowed")
|
|
}
|
|
|
|
if err := ctlV3User(cx, []string{"revoke-role", "root", "root"}, "Error: etcdserver: invalid auth management", []string{}); err == nil {
|
|
cx.t.Fatal("revoking the role root from the user root must not be allowed")
|
|
}
|
|
}
|
|
|
|
func authTestFromKeyPerm(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
// grant keys after z to test-user
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "z", "\x00", false}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try the granted open ended permission
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
for i := 0; i < 10; i++ {
|
|
key := fmt.Sprintf("z%d", i)
|
|
if err := ctlV3Put(cx, key, "val", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
largeKey := ""
|
|
for i := 0; i < 10; i++ {
|
|
largeKey += "\xff"
|
|
if err := ctlV3Put(cx, largeKey, "val", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// try a non granted key
|
|
if err := ctlV3PutFailPerm(cx, "x", "baz"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// revoke the open ended permission
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3RoleRevokePermission(cx, "test-role", "z", "", true); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try the revoked open ended permission
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
for i := 0; i < 10; i++ {
|
|
key := fmt.Sprintf("z%d", i)
|
|
if err := ctlV3PutFailPerm(cx, key, "val"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// grant the entire keys
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "", "\x00", false}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try keys, of course it must be allowed because test-role has a permission of the entire keys
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
for i := 0; i < 10; i++ {
|
|
key := fmt.Sprintf("z%d", i)
|
|
if err := ctlV3Put(cx, key, "val", ""); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// revoke the entire keys
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3RoleRevokePermission(cx, "test-role", "", "", true); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try the revoked entire key permission
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
for i := 0; i < 10; i++ {
|
|
key := fmt.Sprintf("z%d", i)
|
|
if err := ctlV3PutFailPerm(cx, key, "val"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func authLeaseTestKeepAlive(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
// put with TTL 10 seconds and keep-alive
|
|
leaseID, err := ctlV3LeaseGrant(cx, 10)
|
|
if err != nil {
|
|
cx.t.Fatalf("leaseTestKeepAlive: ctlV3LeaseGrant error (%v)", err)
|
|
}
|
|
if err := ctlV3Put(cx, "key", "val", leaseID); err != nil {
|
|
cx.t.Fatalf("leaseTestKeepAlive: ctlV3Put error (%v)", err)
|
|
}
|
|
if err := ctlV3LeaseKeepAlive(cx, leaseID); err != nil {
|
|
cx.t.Fatalf("leaseTestKeepAlive: ctlV3LeaseKeepAlive error (%v)", err)
|
|
}
|
|
if err := ctlV3Get(cx, []string{"key"}, kv{"key", "val"}); err != nil {
|
|
cx.t.Fatalf("leaseTestKeepAlive: ctlV3Get error (%v)", err)
|
|
}
|
|
}
|
|
|
|
func authLeaseTestTimeToLiveExpired(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
ttl := 3
|
|
if err := leaseTestTimeToLiveExpire(cx, ttl); err != nil {
|
|
cx.t.Fatalf("leaseTestTimeToLiveExpire: error (%v)", err)
|
|
}
|
|
}
|
|
|
|
func authLeaseTestLeaseGrantLeases(cx ctlCtx) {
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
if err := leaseTestGrantLeasesList(cx); err != nil {
|
|
cx.t.Fatalf("authLeaseTestLeaseGrantLeases: error (%v)", err)
|
|
}
|
|
}
|
|
|
|
func authLeaseTestLeaseRevoke(cx ctlCtx) {
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
// put with TTL 10 seconds and revoke
|
|
leaseID, err := ctlV3LeaseGrant(cx, 10)
|
|
if err != nil {
|
|
cx.t.Fatalf("ctlV3LeaseGrant error (%v)", err)
|
|
}
|
|
if err := ctlV3Put(cx, "key", "val", leaseID); err != nil {
|
|
cx.t.Fatalf("ctlV3Put error (%v)", err)
|
|
}
|
|
if err := ctlV3LeaseRevoke(cx, leaseID); err != nil {
|
|
cx.t.Fatalf("ctlV3LeaseRevoke error (%v)", err)
|
|
}
|
|
if err := ctlV3GetWithErr(cx, []string{"key"}, []string{"retrying of unary invoker failed"}); err != nil { // expect errors
|
|
cx.t.Fatalf("ctlV3GetWithErr error (%v)", err)
|
|
}
|
|
}
|
|
|
|
func authTestWatch(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
// grant a key range
|
|
if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "key", "key4", false}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
tests := []struct {
|
|
puts []kv
|
|
args []string
|
|
|
|
wkv []kvExec
|
|
want bool
|
|
}{
|
|
{ // watch 1 key, should be successful
|
|
[]kv{{"key", "value"}},
|
|
[]string{"key", "--rev", "1"},
|
|
[]kvExec{{key: "key", val: "value"}},
|
|
true,
|
|
},
|
|
{ // watch 3 keys by range, should be successful
|
|
[]kv{{"key1", "val1"}, {"key3", "val3"}, {"key2", "val2"}},
|
|
[]string{"key", "key3", "--rev", "1"},
|
|
[]kvExec{{key: "key1", val: "val1"}, {key: "key2", val: "val2"}},
|
|
true,
|
|
},
|
|
|
|
{ // watch 1 key, should not be successful
|
|
[]kv{},
|
|
[]string{"key5", "--rev", "1"},
|
|
[]kvExec{},
|
|
false,
|
|
},
|
|
{ // watch 3 keys by range, should not be successful
|
|
[]kv{},
|
|
[]string{"key", "key6", "--rev", "1"},
|
|
[]kvExec{},
|
|
false,
|
|
},
|
|
}
|
|
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
for i, tt := range tests {
|
|
donec := make(chan struct{})
|
|
go func(i int, puts []kv) {
|
|
defer close(donec)
|
|
for j := range puts {
|
|
if err := ctlV3Put(cx, puts[j].key, puts[j].val, ""); err != nil {
|
|
cx.t.Errorf("watchTest #%d-%d: ctlV3Put error (%v)", i, j, err)
|
|
}
|
|
}
|
|
}(i, tt.puts)
|
|
|
|
var err error
|
|
if tt.want {
|
|
err = ctlV3Watch(cx, tt.args, tt.wkv...)
|
|
} else {
|
|
err = ctlV3WatchFailPerm(cx, tt.args)
|
|
}
|
|
|
|
if err != nil {
|
|
if cx.dialTimeout > 0 && !isGRPCTimedout(err) {
|
|
cx.t.Errorf("watchTest #%d: ctlV3Watch error (%v)", i, err)
|
|
}
|
|
}
|
|
|
|
<-donec
|
|
}
|
|
|
|
}
|
|
|
|
func authTestRoleGet(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
expected := []string{
|
|
"Role test-role",
|
|
"KV Read:", "foo",
|
|
"KV Write:", "foo",
|
|
}
|
|
if err := spawnWithExpects(append(cx.PrefixArgs(), "role", "get", "test-role"), expected...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// test-user can get the information of test-role because it belongs to the role
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := spawnWithExpects(append(cx.PrefixArgs(), "role", "get", "test-role"), expected...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// test-user cannot get the information of root because it doesn't belong to the role
|
|
expected = []string{
|
|
"Error: etcdserver: permission denied",
|
|
}
|
|
if err := spawnWithExpects(append(cx.PrefixArgs(), "role", "get", "root"), expected...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authTestUserGet(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
expected := []string{
|
|
"User: test-user",
|
|
"Roles: test-role",
|
|
}
|
|
|
|
if err := spawnWithExpects(append(cx.PrefixArgs(), "user", "get", "test-user"), expected...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// test-user can get the information of test-user itself
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := spawnWithExpects(append(cx.PrefixArgs(), "user", "get", "test-user"), expected...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// test-user cannot get the information of root
|
|
expected = []string{
|
|
"Error: etcdserver: permission denied",
|
|
}
|
|
if err := spawnWithExpects(append(cx.PrefixArgs(), "user", "get", "root"), expected...); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authTestRoleList(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
if err := spawnWithExpect(append(cx.PrefixArgs(), "role", "list"), "test-role"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authTestDefrag(cx ctlCtx) {
|
|
maintenanceInitKeys(cx)
|
|
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
// ordinary user cannot defrag
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3Defrag(cx); err == nil {
|
|
cx.t.Fatal("ordinary user should not be able to issue a defrag request")
|
|
}
|
|
|
|
// root can defrag
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3Defrag(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func authTestSnapshot(cx ctlCtx) {
|
|
maintenanceInitKeys(cx)
|
|
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
fpath := "test-auth.snapshot"
|
|
defer os.RemoveAll(fpath)
|
|
|
|
// ordinary user cannot save a snapshot
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3SnapshotSave(cx, fpath); err == nil {
|
|
cx.t.Fatal("ordinary user should not be able to save a snapshot")
|
|
}
|
|
|
|
// root can save a snapshot
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3SnapshotSave(cx, fpath); err != nil {
|
|
cx.t.Fatalf("snapshotTest ctlV3SnapshotSave error (%v)", err)
|
|
}
|
|
|
|
st, err := getSnapshotStatus(cx, fpath)
|
|
if err != nil {
|
|
cx.t.Fatalf("snapshotTest getSnapshotStatus error (%v)", err)
|
|
}
|
|
if st.Revision != 4 {
|
|
cx.t.Fatalf("expected 4, got %d", st.Revision)
|
|
}
|
|
if st.TotalKey < 3 {
|
|
cx.t.Fatalf("expected at least 3, got %d", st.TotalKey)
|
|
}
|
|
}
|
|
|
|
func authTestEndpointHealth(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
if err := ctlV3EndpointHealth(cx); err != nil {
|
|
cx.t.Fatalf("endpointStatusTest ctlV3EndpointHealth error (%v)", err)
|
|
}
|
|
|
|
// health checking with an ordinary user "succeeds" since permission denial goes through consensus
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3EndpointHealth(cx); err != nil {
|
|
cx.t.Fatalf("endpointStatusTest ctlV3EndpointHealth error (%v)", err)
|
|
}
|
|
|
|
// succeed if permissions granted for ordinary user
|
|
cx.user, cx.pass = "root", "root"
|
|
if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "health", "", false}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3EndpointHealth(cx); err != nil {
|
|
cx.t.Fatalf("endpointStatusTest ctlV3EndpointHealth error (%v)", err)
|
|
}
|
|
}
|
|
|
|
func certCNAndUsername(cx ctlCtx, noPassword bool) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
if noPassword {
|
|
if err := ctlV3User(cx, []string{"add", "example.com", "--no-password"}, "User example.com created", []string{""}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
} else {
|
|
if err := ctlV3User(cx, []string{"add", "example.com", "--interactive=false"}, "User example.com created", []string{""}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
}
|
|
if err := spawnWithExpect(append(cx.PrefixArgs(), "role", "add", "test-role-cn"), "Role test-role-cn created"); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
if err := ctlV3User(cx, []string{"grant-role", "example.com", "test-role-cn"}, "Role test-role-cn is granted to user example.com", nil); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// grant a new key for CN based user
|
|
if err := ctlV3RoleGrantPermission(cx, "test-role-cn", grantingPerm{true, true, "hoo", "", false}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// grant a new key for username based user
|
|
if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "bar", "", false}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// try a granted key for CN based user
|
|
cx.user, cx.pass = "", ""
|
|
if err := ctlV3Put(cx, "hoo", "bar", ""); err != nil {
|
|
cx.t.Error(err)
|
|
}
|
|
|
|
// try a granted key for username based user
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3Put(cx, "bar", "bar", ""); err != nil {
|
|
cx.t.Error(err)
|
|
}
|
|
|
|
// try a non granted key for both of them
|
|
cx.user, cx.pass = "", ""
|
|
if err := ctlV3PutFailPerm(cx, "baz", "bar"); err != nil {
|
|
cx.t.Error(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "test-user", "pass"
|
|
if err := ctlV3PutFailPerm(cx, "baz", "bar"); err != nil {
|
|
cx.t.Error(err)
|
|
}
|
|
}
|
|
|
|
func authTestCertCNAndUsername(cx ctlCtx) {
|
|
certCNAndUsername(cx, false)
|
|
}
|
|
|
|
func authTestCertCNAndUsernameNoPassword(cx ctlCtx) {
|
|
certCNAndUsername(cx, true)
|
|
}
|
|
|
|
func authTestJWTExpire(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
cx.user, cx.pass = "root", "root"
|
|
authSetupTestUser(cx)
|
|
|
|
// try a granted key
|
|
if err := ctlV3Put(cx, "hoo", "bar", ""); err != nil {
|
|
cx.t.Error(err)
|
|
}
|
|
|
|
// wait an expiration of my JWT token
|
|
<-time.After(3 * time.Second)
|
|
|
|
if err := ctlV3Put(cx, "hoo", "bar", ""); err != nil {
|
|
cx.t.Error(err)
|
|
}
|
|
}
|
|
|
|
func authTestRevisionConsistency(cx ctlCtx) {
|
|
if err := authEnable(cx); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
cx.user, cx.pass = "root", "root"
|
|
|
|
// add user
|
|
if err := ctlV3User(cx, []string{"add", "test-user", "--interactive=false"}, "User test-user created", []string{"pass"}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
// delete the same user
|
|
if err := ctlV3User(cx, []string{"delete", "test-user"}, "User test-user deleted", []string{}); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// get node0 auth revision
|
|
node0 := cx.epc.procs[0]
|
|
endpoint := node0.EndpointsV3()[0]
|
|
cli, err := clientv3.New(clientv3.Config{Endpoints: []string{endpoint}, Username: cx.user, Password: cx.pass, DialTimeout: 3 * time.Second})
|
|
if err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
defer cli.Close()
|
|
|
|
sresp, err := cli.AuthStatus(context.TODO())
|
|
if err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
oldAuthRevision := sresp.AuthRevision
|
|
|
|
// restart the node
|
|
node0.WithStopSignal(syscall.SIGINT)
|
|
if err := node0.Restart(); err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
|
|
// get node0 auth revision again
|
|
sresp, err = cli.AuthStatus(context.TODO())
|
|
if err != nil {
|
|
cx.t.Fatal(err)
|
|
}
|
|
newAuthRevision := sresp.AuthRevision
|
|
|
|
// assert AuthRevision equal
|
|
if newAuthRevision != oldAuthRevision {
|
|
cx.t.Fatalf("auth revison shouldn't change when restarting etcd, expected: %d, got: %d", oldAuthRevision, newAuthRevision)
|
|
}
|
|
}
|