Browse Source

Merge pull request #8164 from mitake/auth-granted-keys

allow users to know their roles and permissions
Xiang Li 8 years ago
parent
commit
894751ef44
3 changed files with 91 additions and 4 deletions
  1. 23 0
      auth/store.go
  2. 46 0
      e2e/ctl_v3_auth_test.go
  3. 22 4
      etcdserver/apply_auth.go

+ 23 - 0
auth/store.go

@@ -165,6 +165,9 @@ type AuthStore interface {
 
 	// WithRoot generates and installs a token that can be used as a root credential
 	WithRoot(ctx context.Context) context.Context
+
+	// HasRole checks that user has role
+	HasRole(user, role string) bool
 }
 
 type TokenProvider interface {
@@ -1097,3 +1100,23 @@ func (as *authStore) WithRoot(ctx context.Context) context.Context {
 	tokenMD := metadata.New(mdMap)
 	return metadata.NewContext(ctx, tokenMD)
 }
+
+func (as *authStore) HasRole(user, role string) bool {
+	tx := as.be.BatchTx()
+	tx.Lock()
+	defer tx.Unlock()
+
+	u := getUser(tx, user)
+	if u == nil {
+		plog.Warningf("tried to check user %s has role %s, but user %s doesn't exist", user, role, user)
+		return false
+	}
+
+	for _, r := range u.Roles {
+		if role == r {
+			return true
+		}
+	}
+
+	return false
+}

+ 46 - 0
e2e/ctl_v3_auth_test.go

@@ -41,6 +41,7 @@ func TestCtlV3AuthFromKeyPerm(t *testing.T)      { testCtl(t, authTestFromKeyPer
 func TestCtlV3AuthAndWatch(t *testing.T)         { testCtl(t, authTestWatch) }
 
 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 authEnableTest(cx ctlCtx) {
@@ -758,6 +759,51 @@ func authTestRoleGet(cx ctlCtx) {
 	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) {

+ 22 - 4
etcdserver/apply_auth.go

@@ -189,6 +189,28 @@ func (aa *authApplierV3) checkLeasePuts(leaseID lease.LeaseID) error {
 	return nil
 }
 
+func (aa *authApplierV3) UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error) {
+	err := aa.as.IsAdminPermitted(&aa.authInfo)
+	if err != nil && r.Name != aa.authInfo.Username {
+		aa.authInfo.Username = ""
+		aa.authInfo.Revision = 0
+		return &pb.AuthUserGetResponse{}, err
+	}
+
+	return aa.applierV3.UserGet(r)
+}
+
+func (aa *authApplierV3) RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error) {
+	err := aa.as.IsAdminPermitted(&aa.authInfo)
+	if err != nil && !aa.as.HasRole(aa.authInfo.Username, r.Role) {
+		aa.authInfo.Username = ""
+		aa.authInfo.Revision = 0
+		return &pb.AuthRoleGetResponse{}, err
+	}
+
+	return aa.applierV3.RoleGet(r)
+}
+
 func needAdminPermission(r *pb.InternalRaftRequest) bool {
 	switch {
 	case r.AuthEnable != nil:
@@ -203,16 +225,12 @@ func needAdminPermission(r *pb.InternalRaftRequest) bool {
 		return true
 	case r.AuthUserGrantRole != nil:
 		return true
-	case r.AuthUserGet != nil:
-		return true
 	case r.AuthUserRevokeRole != nil:
 		return true
 	case r.AuthRoleAdd != nil:
 		return true
 	case r.AuthRoleGrantPermission != nil:
 		return true
-	case r.AuthRoleGet != nil:
-		return true
 	case r.AuthRoleRevokePermission != nil:
 		return true
 	case r.AuthRoleDelete != nil: