1// Copyright 2016 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package e2e
16
17import (
18	"fmt"
19	"testing"
20)
21
22func TestCtlV3RoleAdd(t *testing.T)          { testCtl(t, roleAddTest) }
23func TestCtlV3RoleAddNoTLS(t *testing.T)     { testCtl(t, roleAddTest, withCfg(configNoTLS)) }
24func TestCtlV3RoleAddClientTLS(t *testing.T) { testCtl(t, roleAddTest, withCfg(configClientTLS)) }
25func TestCtlV3RoleAddPeerTLS(t *testing.T)   { testCtl(t, roleAddTest, withCfg(configPeerTLS)) }
26func TestCtlV3RoleAddTimeout(t *testing.T)   { testCtl(t, roleAddTest, withDialTimeout(0)) }
27
28func TestCtlV3RoleGrant(t *testing.T) { testCtl(t, roleGrantTest) }
29
30func roleAddTest(cx ctlCtx) {
31	cmdSet := []struct {
32		args        []string
33		expectedStr string
34	}{
35		// Add a role.
36		{
37			args:        []string{"add", "root"},
38			expectedStr: "Role root created",
39		},
40		// Try adding the same role.
41		{
42			args:        []string{"add", "root"},
43			expectedStr: "role name already exists",
44		},
45	}
46
47	for i, cmd := range cmdSet {
48		if err := ctlV3Role(cx, cmd.args, cmd.expectedStr); err != nil {
49			if cx.dialTimeout > 0 && !isGRPCTimedout(err) {
50				cx.t.Fatalf("roleAddTest #%d: ctlV3Role error (%v)", i, err)
51			}
52		}
53	}
54}
55
56func roleGrantTest(cx ctlCtx) {
57	cmdSet := []struct {
58		args        []string
59		expectedStr string
60	}{
61		// Add a role.
62		{
63			args:        []string{"add", "root"},
64			expectedStr: "Role root created",
65		},
66		// Grant read permission to the role.
67		{
68			args:        []string{"grant", "root", "read", "foo"},
69			expectedStr: "Role root updated",
70		},
71		// Grant write permission to the role.
72		{
73			args:        []string{"grant", "root", "write", "foo"},
74			expectedStr: "Role root updated",
75		},
76		// Grant rw permission to the role.
77		{
78			args:        []string{"grant", "root", "readwrite", "foo"},
79			expectedStr: "Role root updated",
80		},
81		// Try granting invalid permission to the role.
82		{
83			args:        []string{"grant", "root", "123", "foo"},
84			expectedStr: "invalid permission type",
85		},
86	}
87
88	for i, cmd := range cmdSet {
89		if err := ctlV3Role(cx, cmd.args, cmd.expectedStr); err != nil {
90			cx.t.Fatalf("roleGrantTest #%d: ctlV3Role error (%v)", i, err)
91		}
92	}
93}
94
95func ctlV3Role(cx ctlCtx, args []string, expStr string) error {
96	cmdArgs := append(cx.PrefixArgs(), "role")
97	cmdArgs = append(cmdArgs, args...)
98
99	return spawnWithExpect(cmdArgs, expStr)
100}
101
102func ctlV3RoleGrantPermission(cx ctlCtx, rolename string, perm grantingPerm) error {
103	cmdArgs := append(cx.PrefixArgs(), "role", "grant-permission")
104	if perm.prefix {
105		cmdArgs = append(cmdArgs, "--prefix")
106	} else if len(perm.rangeEnd) == 1 && perm.rangeEnd[0] == '\x00' {
107		cmdArgs = append(cmdArgs, "--from-key")
108	}
109
110	cmdArgs = append(cmdArgs, rolename)
111	cmdArgs = append(cmdArgs, grantingPermToArgs(perm)...)
112
113	proc, err := spawnCmd(cmdArgs)
114	if err != nil {
115		return err
116	}
117
118	expStr := fmt.Sprintf("Role %s updated", rolename)
119	_, err = proc.Expect(expStr)
120	return err
121}
122
123func ctlV3RoleRevokePermission(cx ctlCtx, rolename string, key, rangeEnd string, fromKey bool) error {
124	cmdArgs := append(cx.PrefixArgs(), "role", "revoke-permission")
125	cmdArgs = append(cmdArgs, rolename)
126	cmdArgs = append(cmdArgs, key)
127	var expStr string
128	if len(rangeEnd) != 0 {
129		cmdArgs = append(cmdArgs, rangeEnd)
130		expStr = fmt.Sprintf("Permission of range [%s, %s) is revoked from role %s", key, rangeEnd, rolename)
131	} else if fromKey {
132		cmdArgs = append(cmdArgs, "--from-key")
133		expStr = fmt.Sprintf("Permission of range [%s, <open ended> is revoked from role %s", key, rolename)
134	} else {
135		expStr = fmt.Sprintf("Permission of key %s is revoked from role %s", key, rolename)
136	}
137
138	proc, err := spawnCmd(cmdArgs)
139	if err != nil {
140		return err
141	}
142
143	_, err = proc.Expect(expStr)
144	return err
145}
146
147type grantingPerm struct {
148	read     bool
149	write    bool
150	key      string
151	rangeEnd string
152	prefix   bool
153}
154
155func grantingPermToArgs(perm grantingPerm) []string {
156	permstr := ""
157
158	if perm.read {
159		permstr += "read"
160	}
161
162	if perm.write {
163		permstr += "write"
164	}
165
166	if len(permstr) == 0 {
167		panic("invalid granting permission")
168	}
169
170	if len(perm.rangeEnd) == 0 {
171		return []string{permstr, perm.key}
172	}
173
174	if len(perm.rangeEnd) == 1 && perm.rangeEnd[0] == '\x00' {
175		return []string{permstr, perm.key}
176	}
177
178	return []string{permstr, perm.key, perm.rangeEnd}
179}
180