Browse Source

auth: add root user and root role

Xiang Li 9 years ago
parent
commit
da2f2a5189

+ 37 - 7
auth/store.go

@@ -37,6 +37,8 @@ var (
 
 	plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "auth")
 
+	ErrRootUserNotExist     = errors.New("auth: root user does not exist")
+	ErrRootRoleNotExist     = errors.New("auth: root user does not have root role")
 	ErrUserAlreadyExist     = errors.New("auth: user already exists")
 	ErrUserNotFound         = errors.New("auth: user not found")
 	ErrRoleAlreadyExist     = errors.New("auth: role already exists")
@@ -47,9 +49,14 @@ var (
 	ErrPermissionNotGranted = errors.New("auth: permission is not granted to the role")
 )
 
+const (
+	rootUser = "root"
+	rootRole = "root"
+)
+
 type AuthStore interface {
 	// AuthEnable turns on the authentication feature
-	AuthEnable()
+	AuthEnable() error
 
 	// AuthDisable turns off the authentication feature
 	AuthDisable()
@@ -112,21 +119,42 @@ type authStore struct {
 	enabledMu sync.RWMutex
 }
 
-func (as *authStore) AuthEnable() {
+func (as *authStore) AuthEnable() error {
 	value := []byte{1}
 
 	b := as.be
 	tx := b.BatchTx()
 	tx.Lock()
+	defer func() {
+		tx.Unlock()
+		b.ForceCommit()
+	}()
+
+	u := getUser(tx, rootUser)
+	if u == nil {
+		return ErrRootUserNotExist
+	}
+
+	rootRoleExist := false
+	for _, r := range u.Roles {
+		if r == rootRole {
+			rootRoleExist = true
+			break
+		}
+	}
+	if !rootRoleExist {
+		return ErrRootRoleNotExist
+	}
+
 	tx.UnsafePut(authBucketName, enableFlagKey, value)
-	tx.Unlock()
-	b.ForceCommit()
 
 	as.enabledMu.Lock()
 	as.enabled = true
 	as.enabledMu.Unlock()
 
 	plog.Noticef("Authentication enabled")
+
+	return nil
 }
 
 func (as *authStore) AuthDisable() {
@@ -280,9 +308,11 @@ func (as *authStore) UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUser
 		return nil, ErrUserNotFound
 	}
 
-	_, vs := tx.UnsafeRange(authRolesBucketName, []byte(r.Role), nil, 0)
-	if len(vs) != 1 {
-		return nil, ErrRoleNotFound
+	if r.Role != rootRole {
+		_, vs := tx.UnsafeRange(authRolesBucketName, []byte(r.Role), nil, 0)
+		if len(vs) != 1 {
+			return nil, ErrRoleNotFound
+		}
 	}
 
 	idx := sort.SearchStrings(user.Roles, r.Role)

+ 9 - 0
e2e/ctl_v3_auth_test.go

@@ -20,6 +20,15 @@ func TestCtlV3AuthEnable(t *testing.T)  { testCtl(t, authEnableTest) }
 func TestCtlV3AuthDisable(t *testing.T) { testCtl(t, authDisableTest) }
 
 func authEnableTest(cx ctlCtx) {
+	// create root user with root role
+	if err := ctlV3User(cx, []string{"add", "root", "--interactive=false"}, "User root created", []string{"root"}); err != nil {
+		cx.t.Fatalf("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 {
+		cx.t.Fatalf("failed to grant root user root role %v", err)
+	}
+
 	if err := ctlV3AuthEnable(cx); err != nil {
 		cx.t.Fatalf("authEnableTest ctlV3AuthEnable error (%v)", err)
 	}

+ 6 - 0
etcdserver/api/v3rpc/rpctypes/error.go

@@ -38,6 +38,8 @@ var (
 
 	ErrGRPCRequestTooLarge = grpc.Errorf(codes.InvalidArgument, "etcdserver: request is too large")
 
+	ErrGRPCRootUserNotExist     = grpc.Errorf(codes.FailedPrecondition, "etcdserver: root user does not exist")
+	ErrGRPCRootRoleNotExist     = grpc.Errorf(codes.FailedPrecondition, "etcdserver: root user does not have root role")
 	ErrGRPCUserAlreadyExist     = grpc.Errorf(codes.FailedPrecondition, "etcdserver: user name already exists")
 	ErrGRPCUserNotFound         = grpc.Errorf(codes.FailedPrecondition, "etcdserver: user name not found")
 	ErrGRPCRoleAlreadyExist     = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role name already exists")
@@ -68,6 +70,8 @@ var (
 
 		grpc.ErrorDesc(ErrGRPCRequestTooLarge): ErrGRPCRequestTooLarge,
 
+		grpc.ErrorDesc(ErrGRPCRootUserNotExist):     ErrGRPCRootUserNotExist,
+		grpc.ErrorDesc(ErrGRPCRootRoleNotExist):     ErrGRPCRootRoleNotExist,
 		grpc.ErrorDesc(ErrGRPCUserAlreadyExist):     ErrGRPCUserAlreadyExist,
 		grpc.ErrorDesc(ErrGRPCUserNotFound):         ErrGRPCUserNotFound,
 		grpc.ErrorDesc(ErrGRPCRoleAlreadyExist):     ErrGRPCRoleAlreadyExist,
@@ -99,6 +103,8 @@ var (
 
 	ErrRequestTooLarge = Error(ErrGRPCRequestTooLarge)
 
+	ErrRootUserNotExist     = Error(ErrGRPCRootUserNotExist)
+	ErrRootRoleNotExist     = Error(ErrGRPCRootRoleNotExist)
 	ErrUserAlreadyExist     = Error(ErrGRPCUserAlreadyExist)
 	ErrUserNotFound         = Error(ErrGRPCUserNotFound)
 	ErrRoleAlreadyExist     = Error(ErrGRPCRoleAlreadyExist)

+ 5 - 0
etcdserver/api/v3rpc/util.go

@@ -37,6 +37,11 @@ func togRPCError(err error) error {
 		return rpctypes.ErrGRPCRequestTooLarge
 	case etcdserver.ErrNoSpace:
 		return rpctypes.ErrGRPCNoSpace
+
+	case auth.ErrRootUserNotExist:
+		return rpctypes.ErrGRPCRootUserNotExist
+	case auth.ErrRootRoleNotExist:
+		return rpctypes.ErrGRPCRootRoleNotExist
 	case auth.ErrUserAlreadyExist:
 		return rpctypes.ErrGRPCUserAlreadyExist
 	case auth.ErrUserNotFound:

+ 4 - 1
etcdserver/apply.go

@@ -521,7 +521,10 @@ func (a *applierV3Capped) LeaseGrant(lc *pb.LeaseGrantRequest) (*pb.LeaseGrantRe
 }
 
 func (a *applierV3backend) AuthEnable() (*pb.AuthEnableResponse, error) {
-	a.s.AuthStore().AuthEnable()
+	err := a.s.AuthStore().AuthEnable()
+	if err != nil {
+		return nil, err
+	}
 	return &pb.AuthEnableResponse{}, nil
 }