浏览代码

*: support adding role in auth v3

Hitoshi Mitake 9 年之前
父节点
当前提交
2b17a3919c

+ 130 - 0
auth/authpb/auth.pb.go

@@ -10,6 +10,7 @@
 
 	It has these top-level messages:
 		User
+		Role
 */
 package authpb
 
@@ -39,8 +40,18 @@ func (m *User) Reset()         { *m = User{} }
 func (m *User) String() string { return proto.CompactTextString(m) }
 func (*User) ProtoMessage()    {}
 
+// Role is a single entry in the bucket authRoles
+type Role struct {
+	Name []byte `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
+}
+
+func (m *Role) Reset()         { *m = Role{} }
+func (m *Role) String() string { return proto.CompactTextString(m) }
+func (*Role) ProtoMessage()    {}
+
 func init() {
 	proto.RegisterType((*User)(nil), "authpb.User")
+	proto.RegisterType((*Role)(nil), "authpb.Role")
 }
 func (m *User) Marshal() (data []byte, err error) {
 	size := m.Size()
@@ -81,6 +92,32 @@ func (m *User) MarshalTo(data []byte) (int, error) {
 	return i, nil
 }
 
+func (m *Role) Marshal() (data []byte, err error) {
+	size := m.Size()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Role) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if m.Name != nil {
+		if len(m.Name) > 0 {
+			data[i] = 0x12
+			i++
+			i = encodeVarintAuth(data, i, uint64(len(m.Name)))
+			i += copy(data[i:], m.Name)
+		}
+	}
+	return i, nil
+}
+
 func encodeFixed64Auth(data []byte, offset int, v uint64) int {
 	data[offset] = uint8(v)
 	data[offset+1] = uint8(v >> 8)
@@ -129,6 +166,18 @@ func (m *User) Size() (n int) {
 	return n
 }
 
+func (m *Role) Size() (n int) {
+	var l int
+	_ = l
+	if m.Name != nil {
+		l = len(m.Name)
+		if l > 0 {
+			n += 1 + l + sovAuth(uint64(l))
+		}
+	}
+	return n
+}
+
 func sovAuth(x uint64) (n int) {
 	for {
 		n++
@@ -273,6 +322,87 @@ func (m *User) Unmarshal(data []byte) error {
 	}
 	return nil
 }
+func (m *Role) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowAuth
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Role: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Role: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAuth
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				byteLen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthAuth
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Name = append(m.Name[:0], data[iNdEx:postIndex]...)
+			if m.Name == nil {
+				m.Name = []byte{}
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipAuth(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthAuth
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func skipAuth(data []byte) (n int, err error) {
 	l := len(data)
 	iNdEx := 0

+ 5 - 0
auth/authpb/auth.proto

@@ -15,3 +15,8 @@ message User {
   bytes password = 2;
   int64 tombstone = 3;
 }
+
+// Role is a single entry in the bucket authRoles
+message Role {
+  bytes name = 2;
+}

+ 34 - 1
auth/store.go

@@ -25,14 +25,17 @@ import (
 )
 
 var (
-	enableFlagKey       = []byte("authEnabled")
+	enableFlagKey = []byte("authEnabled")
+
 	authBucketName      = []byte("auth")
 	authUsersBucketName = []byte("authUsers")
+	authRolesBucketName = []byte("authRoles")
 
 	plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "auth")
 
 	ErrUserAlreadyExist = errors.New("auth: user already exists")
 	ErrUserNotFound     = errors.New("auth: user not found")
+	ErrRoleAlreadyExist = errors.New("auth: role already exists")
 )
 
 type AuthStore interface {
@@ -50,6 +53,9 @@ type AuthStore interface {
 
 	// UserChangePassword changes a password of a user
 	UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error)
+
+	// RoleAdd adds a new role
+	RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error)
 }
 
 type authStore struct {
@@ -163,12 +169,39 @@ func (as *authStore) UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*p
 	return &pb.AuthUserChangePasswordResponse{}, nil
 }
 
+func (as *authStore) RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) {
+	tx := as.be.BatchTx()
+	tx.Lock()
+	defer tx.Unlock()
+
+	_, vs := tx.UnsafeRange(authRolesBucketName, []byte(r.Name), nil, 0)
+	if len(vs) != 0 {
+		return nil, ErrRoleAlreadyExist
+	}
+
+	newRole := &authpb.Role{
+		Name: []byte(r.Name),
+	}
+
+	marshaledRole, err := newRole.Marshal()
+	if err != nil {
+		return nil, err
+	}
+
+	tx.UnsafePut(authRolesBucketName, []byte(r.Name), marshaledRole)
+
+	plog.Noticef("Role %s is created", r.Name)
+
+	return &pb.AuthRoleAddResponse{}, nil
+}
+
 func NewAuthStore(be backend.Backend) *authStore {
 	tx := be.BatchTx()
 	tx.Lock()
 
 	tx.UnsafeCreateBucket(authBucketName)
 	tx.UnsafeCreateBucket(authUsersBucketName)
+	tx.UnsafeCreateBucket(authRolesBucketName)
 
 	tx.Unlock()
 	be.ForceCommit()

+ 9 - 0
clientv3/auth.go

@@ -25,6 +25,7 @@ type (
 	AuthUserAddResponse            pb.AuthUserAddResponse
 	AuthUserDeleteResponse         pb.AuthUserDeleteResponse
 	AuthUserChangePasswordResponse pb.AuthUserChangePasswordResponse
+	AuthRoleAddResponse            pb.AuthRoleAddResponse
 )
 
 type Auth interface {
@@ -39,6 +40,9 @@ type Auth interface {
 
 	// UserChangePassword changes a password of a user.
 	UserChangePassword(ctx context.Context, name string, password string) (*AuthUserChangePasswordResponse, error)
+
+	// RoleAdd adds a new user to an etcd cluster.
+	RoleAdd(ctx context.Context, name string) (*AuthRoleAddResponse, error)
 }
 
 type auth struct {
@@ -76,3 +80,8 @@ func (auth *auth) UserChangePassword(ctx context.Context, name string, password
 	resp, err := auth.remote.UserChangePassword(ctx, &pb.AuthUserChangePasswordRequest{Name: name, Password: password})
 	return (*AuthUserChangePasswordResponse)(resp), err
 }
+
+func (auth *auth) RoleAdd(ctx context.Context, name string) (*AuthRoleAddResponse, error) {
+	resp, err := auth.remote.RoleAdd(ctx, &pb.AuthRoleAddRequest{Name: name})
+	return (*AuthRoleAddResponse)(resp), err
+}

+ 56 - 0
etcdctl/ctlv3/command/role_command.go

@@ -0,0 +1,56 @@
+// Copyright 2016 Nippon Telegraph and Telephone Corporation.
+//
+// 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 command
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+	"golang.org/x/net/context"
+)
+
+// NewRoleCommand returns the cobra command for "role".
+func NewRoleCommand() *cobra.Command {
+	ac := &cobra.Command{
+		Use:   "role <subcommand>",
+		Short: "role related command",
+	}
+
+	ac.AddCommand(newRoleAddCommand())
+
+	return ac
+}
+
+func newRoleAddCommand() *cobra.Command {
+	return &cobra.Command{
+		Use:   "add <role name>",
+		Short: "add a new role",
+		Run:   roleAddCommandFunc,
+	}
+}
+
+// roleAddCommandFunc executes the "role add" command.
+func roleAddCommandFunc(cmd *cobra.Command, args []string) {
+	if len(args) != 1 {
+		ExitWithError(ExitBadArgs, fmt.Errorf("role add command requires role name as its argument."))
+	}
+
+	_, err := mustClientFromCmd(cmd).Auth.RoleAdd(context.TODO(), args[0])
+	if err != nil {
+		ExitWithError(ExitError, err)
+	}
+
+	fmt.Printf("Role %s created\n", args[0])
+}

+ 1 - 0
etcdctl/ctlv3/ctl.go

@@ -80,6 +80,7 @@ func init() {
 		command.NewAuthCommand(),
 		command.NewElectCommand(),
 		command.NewUserCommand(),
+		command.NewRoleCommand(),
 	)
 }
 

+ 5 - 2
etcdserver/api/v3rpc/auth.go

@@ -47,8 +47,11 @@ func (as *AuthServer) Authenticate(ctx context.Context, r *pb.AuthenticateReques
 }
 
 func (as *AuthServer) RoleAdd(ctx context.Context, r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) {
-	plog.Info("not implemented yet")
-	return nil, nil
+	resp, err := as.authenticator.RoleAdd(ctx, r)
+	if err != nil {
+		return nil, togRPCError(err)
+	}
+	return resp, nil
 }
 
 func (as *AuthServer) RoleDelete(ctx context.Context, r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error) {

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

@@ -39,4 +39,5 @@ var (
 
 	ErrUserAlreadyExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: user name already exists")
 	ErrUserNotFound     = grpc.Errorf(codes.FailedPrecondition, "etcdserver: user name not found")
+	ErrRoleAlreadyExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role name already exists")
 )

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

@@ -41,6 +41,8 @@ func togRPCError(err error) error {
 		return rpctypes.ErrUserAlreadyExist
 	case auth.ErrUserNotFound:
 		return rpctypes.ErrUserNotFound
+	case auth.ErrRoleAlreadyExist:
+		return rpctypes.ErrRoleAlreadyExist
 	default:
 		return grpc.Errorf(codes.Internal, err.Error())
 	}

+ 7 - 0
etcdserver/apply.go

@@ -57,6 +57,7 @@ type applierV3 interface {
 	UserAdd(ua *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error)
 	UserDelete(ua *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error)
 	UserChangePassword(ua *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error)
+	RoleAdd(ua *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error)
 }
 
 type applierV3backend struct {
@@ -90,6 +91,8 @@ func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) *applyResult {
 		ar.resp, ar.err = s.applyV3.UserDelete(r.AuthUserDelete)
 	case r.AuthUserChangePassword != nil:
 		ar.resp, ar.err = s.applyV3.UserChangePassword(r.AuthUserChangePassword)
+	case r.AuthRoleAdd != nil:
+		ar.resp, ar.err = s.applyV3.RoleAdd(r.AuthRoleAdd)
 	default:
 		panic("not implemented")
 	}
@@ -489,6 +492,10 @@ func (a *applierV3backend) UserChangePassword(r *pb.AuthUserChangePasswordReques
 	return a.s.AuthStore().UserChangePassword(r)
 }
 
+func (a *applierV3backend) RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) {
+	return a.s.AuthStore().RoleAdd(r)
+}
+
 type quotaApplierV3 struct {
 	applierV3
 	q Quota

+ 52 - 4
etcdserver/etcdserverpb/raft_internal.pb.go

@@ -35,7 +35,8 @@ type InternalRaftRequest struct {
 	AuthUserAdd            *AuthUserAddRequest            `protobuf:"bytes,11,opt,name=auth_user_add" json:"auth_user_add,omitempty"`
 	AuthUserDelete         *AuthUserDeleteRequest         `protobuf:"bytes,12,opt,name=auth_user_delete" json:"auth_user_delete,omitempty"`
 	AuthUserChangePassword *AuthUserChangePasswordRequest `protobuf:"bytes,13,opt,name=auth_user_change_password" json:"auth_user_change_password,omitempty"`
-	Alarm                  *AlarmRequest                  `protobuf:"bytes,14,opt,name=alarm" json:"alarm,omitempty"`
+	AuthRoleAdd            *AuthRoleAddRequest            `protobuf:"bytes,14,opt,name=auth_role_add" json:"auth_role_add,omitempty"`
+	Alarm                  *AlarmRequest                  `protobuf:"bytes,15,opt,name=alarm" json:"alarm,omitempty"`
 }
 
 func (m *InternalRaftRequest) Reset()         { *m = InternalRaftRequest{} }
@@ -193,16 +194,26 @@ func (m *InternalRaftRequest) MarshalTo(data []byte) (int, error) {
 		}
 		i += n12
 	}
-	if m.Alarm != nil {
+	if m.AuthRoleAdd != nil {
 		data[i] = 0x72
 		i++
-		i = encodeVarintRaftInternal(data, i, uint64(m.Alarm.Size()))
-		n13, err := m.Alarm.MarshalTo(data[i:])
+		i = encodeVarintRaftInternal(data, i, uint64(m.AuthRoleAdd.Size()))
+		n13, err := m.AuthRoleAdd.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
 		i += n13
 	}
+	if m.Alarm != nil {
+		data[i] = 0x7a
+		i++
+		i = encodeVarintRaftInternal(data, i, uint64(m.Alarm.Size()))
+		n14, err := m.Alarm.MarshalTo(data[i:])
+		if err != nil {
+			return 0, err
+		}
+		i += n14
+	}
 	return i, nil
 }
 
@@ -305,6 +316,10 @@ func (m *InternalRaftRequest) Size() (n int) {
 		l = m.AuthUserChangePassword.Size()
 		n += 1 + l + sovRaftInternal(uint64(l))
 	}
+	if m.AuthRoleAdd != nil {
+		l = m.AuthRoleAdd.Size()
+		n += 1 + l + sovRaftInternal(uint64(l))
+	}
 	if m.Alarm != nil {
 		l = m.Alarm.Size()
 		n += 1 + l + sovRaftInternal(uint64(l))
@@ -776,6 +791,39 @@ func (m *InternalRaftRequest) Unmarshal(data []byte) error {
 			}
 			iNdEx = postIndex
 		case 14:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleAdd", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowRaftInternal
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthRaftInternal
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.AuthRoleAdd == nil {
+				m.AuthRoleAdd = &AuthRoleAddRequest{}
+			}
+			if err := m.AuthRoleAdd.Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 15:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Alarm", wireType)
 			}

+ 2 - 1
etcdserver/etcdserverpb/raft_internal.proto

@@ -29,8 +29,9 @@ message InternalRaftRequest {
   AuthUserAddRequest auth_user_add = 11;
   AuthUserDeleteRequest auth_user_delete = 12;
   AuthUserChangePasswordRequest auth_user_change_password = 13;
+  AuthRoleAddRequest auth_role_add = 14;
 
-  AlarmRequest alarm = 14;
+  AlarmRequest alarm = 15;
 }
 
 message EmptyResponse {

+ 40 - 0
etcdserver/etcdserverpb/rpc.pb.go

@@ -1341,6 +1341,7 @@ func (m *AuthUserRevokeRequest) String() string { return proto.CompactTextString
 func (*AuthUserRevokeRequest) ProtoMessage()    {}
 
 type AuthRoleAddRequest struct {
+	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
 }
 
 func (m *AuthRoleAddRequest) Reset()         { *m = AuthRoleAddRequest{} }
@@ -4664,6 +4665,12 @@ func (m *AuthRoleAddRequest) MarshalTo(data []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
+	if len(m.Name) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintRpc(data, i, uint64(len(m.Name)))
+		i += copy(data[i:], m.Name)
+	}
 	return i, nil
 }
 
@@ -5934,6 +5941,10 @@ func (m *AuthUserRevokeRequest) Size() (n int) {
 func (m *AuthRoleAddRequest) Size() (n int) {
 	var l int
 	_ = l
+	l = len(m.Name)
+	if l > 0 {
+		n += 1 + l + sovRpc(uint64(l))
+	}
 	return n
 }
 
@@ -11314,6 +11325,35 @@ func (m *AuthRoleAddRequest) Unmarshal(data []byte) error {
 			return fmt.Errorf("proto: AuthRoleAddRequest: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowRpc
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthRpc
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Name = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipRpc(data[iNdEx:])

+ 1 - 0
etcdserver/etcdserverpb/rpc.proto

@@ -515,6 +515,7 @@ message AuthUserRevokeRequest {
 }
 
 message AuthRoleAddRequest {
+  string name = 1;
 }
 
 message AuthRoleGetRequest {

+ 9 - 0
etcdserver/v3demo_server.go

@@ -56,6 +56,7 @@ type Authenticator interface {
 	UserAdd(ctx context.Context, r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error)
 	UserDelete(ctx context.Context, r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error)
 	UserChangePassword(ctx context.Context, r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error)
+	RoleAdd(ctx context.Context, r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error)
 }
 
 func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) {
@@ -215,6 +216,14 @@ func (s *EtcdServer) UserChangePassword(ctx context.Context, r *pb.AuthUserChang
 	return result.resp.(*pb.AuthUserChangePasswordResponse), result.err
 }
 
+func (s *EtcdServer) RoleAdd(ctx context.Context, r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) {
+	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{AuthRoleAdd: r})
+	if err != nil {
+		return nil, err
+	}
+	return result.resp.(*pb.AuthRoleAddResponse), result.err
+}
+
 func (s *EtcdServer) processInternalRaftRequest(ctx context.Context, r pb.InternalRaftRequest) (*applyResult, error) {
 	r.ID = s.reqIDGen.Next()