diff --git a/auth/authpb/auth.pb.go b/auth/authpb/auth.pb.go index 6f3793300..ef8e58a65 100644 --- a/auth/authpb/auth.pb.go +++ b/auth/authpb/auth.pb.go @@ -10,6 +10,7 @@ It has these top-level messages: User + Permission Role */ package authpb @@ -29,6 +30,29 @@ var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +type Permission_Type int32 + +const ( + READ Permission_Type = 0 + WRITE Permission_Type = 1 + READWRITE Permission_Type = 2 +) + +var Permission_Type_name = map[int32]string{ + 0: "READ", + 1: "WRITE", + 2: "READWRITE", +} +var Permission_Type_value = map[string]int32{ + "READ": 0, + "WRITE": 1, + "READWRITE": 2, +} + +func (x Permission_Type) String() string { + return proto.EnumName(Permission_Type_name, int32(x)) +} + // User is a single entry in the bucket authUsers type User struct { Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` @@ -40,9 +64,20 @@ func (m *User) Reset() { *m = User{} } func (m *User) String() string { return proto.CompactTextString(m) } func (*User) ProtoMessage() {} +// Permission is a single entity +type Permission struct { + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + PermType Permission_Type `protobuf:"varint,2,opt,name=permType,proto3,enum=authpb.Permission_Type" json:"permType,omitempty"` +} + +func (m *Permission) Reset() { *m = Permission{} } +func (m *Permission) String() string { return proto.CompactTextString(m) } +func (*Permission) 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"` + Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + KeyPermission []*Permission `protobuf:"bytes,2,rep,name=keyPermission" json:"keyPermission,omitempty"` } func (m *Role) Reset() { *m = Role{} } @@ -51,7 +86,9 @@ func (*Role) ProtoMessage() {} func init() { proto.RegisterType((*User)(nil), "authpb.User") + proto.RegisterType((*Permission)(nil), "authpb.Permission") proto.RegisterType((*Role)(nil), "authpb.Role") + proto.RegisterEnum("authpb.Permission_Type", Permission_Type_name, Permission_Type_value) } func (m *User) Marshal() (data []byte, err error) { size := m.Size() @@ -92,6 +129,37 @@ func (m *User) MarshalTo(data []byte) (int, error) { return i, nil } +func (m *Permission) 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 *Permission) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Key != nil { + if len(m.Key) > 0 { + data[i] = 0xa + i++ + i = encodeVarintAuth(data, i, uint64(len(m.Key))) + i += copy(data[i:], m.Key) + } + } + if m.PermType != 0 { + data[i] = 0x10 + i++ + i = encodeVarintAuth(data, i, uint64(m.PermType)) + } + return i, nil +} + func (m *Role) Marshal() (data []byte, err error) { size := m.Size() data = make([]byte, size) @@ -109,12 +177,24 @@ func (m *Role) MarshalTo(data []byte) (int, error) { _ = l if m.Name != nil { if len(m.Name) > 0 { - data[i] = 0x12 + data[i] = 0xa i++ i = encodeVarintAuth(data, i, uint64(len(m.Name))) i += copy(data[i:], m.Name) } } + if len(m.KeyPermission) > 0 { + for _, msg := range m.KeyPermission { + data[i] = 0x12 + i++ + i = encodeVarintAuth(data, i, uint64(msg.Size())) + n, err := msg.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n + } + } return i, nil } @@ -166,6 +246,21 @@ func (m *User) Size() (n int) { return n } +func (m *Permission) Size() (n int) { + var l int + _ = l + if m.Key != nil { + l = len(m.Key) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + } + if m.PermType != 0 { + n += 1 + sovAuth(uint64(m.PermType)) + } + return n +} + func (m *Role) Size() (n int) { var l int _ = l @@ -175,6 +270,12 @@ func (m *Role) Size() (n int) { n += 1 + l + sovAuth(uint64(l)) } } + if len(m.KeyPermission) > 0 { + for _, e := range m.KeyPermission { + l = e.Size() + n += 1 + l + sovAuth(uint64(l)) + } + } return n } @@ -322,6 +423,106 @@ func (m *User) Unmarshal(data []byte) error { } return nil } +func (m *Permission) 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: Permission: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Permission: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", 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.Key = append(m.Key[:0], data[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PermType", wireType) + } + m.PermType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + m.PermType |= (Permission_Type(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + 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 (m *Role) Unmarshal(data []byte) error { l := len(data) iNdEx := 0 @@ -351,7 +552,7 @@ func (m *Role) Unmarshal(data []byte) error { return fmt.Errorf("proto: Role: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 2: + case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } @@ -382,6 +583,37 @@ func (m *Role) Unmarshal(data []byte) error { m.Name = []byte{} } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field KeyPermission", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.KeyPermission = append(m.KeyPermission, &Permission{}) + if err := m.KeyPermission[len(m.KeyPermission)-1].Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipAuth(data[iNdEx:]) diff --git a/auth/authpb/auth.proto b/auth/authpb/auth.proto index 00f94e71d..2e0380670 100644 --- a/auth/authpb/auth.proto +++ b/auth/authpb/auth.proto @@ -16,7 +16,21 @@ message User { int64 tombstone = 3; } +// Permission is a single entity +message Permission { + bytes key = 1; + + enum Type { + READ = 0; + WRITE = 1; + READWRITE = 2; + } + Type permType = 2; +} + // Role is a single entry in the bucket authRoles message Role { - bytes name = 2; + bytes name = 1; + + repeated Permission keyPermission = 2; } diff --git a/auth/store.go b/auth/store.go index 39cf96bb8..793d9ea18 100644 --- a/auth/store.go +++ b/auth/store.go @@ -16,6 +16,7 @@ package auth import ( "errors" + "strings" "github.com/coreos/etcd/auth/authpb" pb "github.com/coreos/etcd/etcdserver/etcdserverpb" @@ -36,6 +37,7 @@ var ( ErrUserAlreadyExist = errors.New("auth: user already exists") ErrUserNotFound = errors.New("auth: user not found") ErrRoleAlreadyExist = errors.New("auth: role already exists") + ErrRoleNotFound = errors.New("auth: role not found") ) type AuthStore interface { @@ -56,6 +58,9 @@ type AuthStore interface { // RoleAdd adds a new role RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) + + // RoleGrant grants a permission to a role + RoleGrant(r *pb.AuthRoleGrantRequest) (*pb.AuthRoleGrantResponse, error) } type authStore struct { @@ -195,6 +200,56 @@ func (as *authStore) RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, return &pb.AuthRoleAddResponse{}, nil } +func (as *authStore) RoleGrant(r *pb.AuthRoleGrantRequest) (*pb.AuthRoleGrantResponse, error) { + tx := as.be.BatchTx() + tx.Lock() + defer tx.Unlock() + + _, vs := tx.UnsafeRange(authRolesBucketName, []byte(r.Name), nil, 0) + if len(vs) != 1 { + return nil, ErrRoleNotFound + } + + role := &authpb.Role{} + err := role.Unmarshal(vs[0]) + if err != nil { + plog.Errorf("failed to unmarshal a role %s: %s", r.Name, err) + return nil, err + } + + if !updateExistingPermission(role.KeyPermission, string(r.Perm.Key), r.Perm.PermType) { + newPerm := &authpb.Permission{ + Key: []byte(r.Perm.Key), + PermType: r.Perm.PermType, + } + + role.KeyPermission = append(role.KeyPermission, newPerm) + } + + marshaledRole, merr := role.Marshal() + if merr != nil { + plog.Errorf("failed to marshal updated role %s: %s", r.Name, merr) + return nil, merr + } + + tx.UnsafePut(authRolesBucketName, []byte(r.Name), marshaledRole) + + plog.Noticef("role %s's permission of key %s is updated as %s", r.Name, r.Perm.Key, authpb.Permission_Type_name[int32(r.Perm.PermType)]) + + return &pb.AuthRoleGrantResponse{}, nil +} + +func updateExistingPermission(perms []*authpb.Permission, key string, newPerm authpb.Permission_Type) bool { + for _, perm := range perms { + if strings.Compare(string(perm.Key), key) == 0 { + perm.PermType = newPerm + return true + } + } + + return false +} + func NewAuthStore(be backend.Backend) *authStore { tx := be.BatchTx() tx.Lock() diff --git a/clientv3/auth.go b/clientv3/auth.go index fceea770b..b5334583c 100644 --- a/clientv3/auth.go +++ b/clientv3/auth.go @@ -15,6 +15,10 @@ package clientv3 import ( + "fmt" + "strings" + + "github.com/coreos/etcd/auth/authpb" pb "github.com/coreos/etcd/etcdserver/etcdserverpb" "golang.org/x/net/context" "google.golang.org/grpc" @@ -26,6 +30,15 @@ type ( AuthUserDeleteResponse pb.AuthUserDeleteResponse AuthUserChangePasswordResponse pb.AuthUserChangePasswordResponse AuthRoleAddResponse pb.AuthRoleAddResponse + AuthRoleGrantResponse pb.AuthRoleGrantResponse + + PermissionType authpb.Permission_Type +) + +const ( + PermRead = authpb.READ + PermWrite = authpb.WRITE + PermReadWrite = authpb.READWRITE ) type Auth interface { @@ -41,8 +54,11 @@ 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 adds a new role to an etcd cluster. RoleAdd(ctx context.Context, name string) (*AuthRoleAddResponse, error) + + // RoleGrant grants a permission to a role. + RoleGrant(ctx context.Context, name string, key string, permType PermissionType) (*AuthRoleGrantResponse, error) } type auth struct { @@ -85,3 +101,20 @@ func (auth *auth) RoleAdd(ctx context.Context, name string) (*AuthRoleAddRespons resp, err := auth.remote.RoleAdd(ctx, &pb.AuthRoleAddRequest{Name: name}) return (*AuthRoleAddResponse)(resp), err } + +func (auth *auth) RoleGrant(ctx context.Context, name string, key string, permType PermissionType) (*AuthRoleGrantResponse, error) { + perm := &authpb.Permission{ + Key: []byte(key), + PermType: authpb.Permission_Type(permType), + } + resp, err := auth.remote.RoleGrant(ctx, &pb.AuthRoleGrantRequest{Name: name, Perm: perm}) + return (*AuthRoleGrantResponse)(resp), err +} + +func StrToPermissionType(s string) (PermissionType, error) { + val, ok := authpb.Permission_Type_value[strings.ToUpper(s)] + if ok { + return PermissionType(val), nil + } + return PermissionType(-1), fmt.Errorf("invalid permission type: %s", s) +} diff --git a/etcdctl/ctlv3/command/role_command.go b/etcdctl/ctlv3/command/role_command.go index 6055b3593..1d2082f48 100644 --- a/etcdctl/ctlv3/command/role_command.go +++ b/etcdctl/ctlv3/command/role_command.go @@ -17,6 +17,7 @@ package command import ( "fmt" + "github.com/coreos/etcd/clientv3" "github.com/spf13/cobra" "golang.org/x/net/context" ) @@ -29,6 +30,7 @@ func NewRoleCommand() *cobra.Command { } ac.AddCommand(newRoleAddCommand()) + ac.AddCommand(newRoleGrantCommand()) return ac } @@ -41,6 +43,14 @@ func newRoleAddCommand() *cobra.Command { } } +func newRoleGrantCommand() *cobra.Command { + return &cobra.Command{ + Use: "grant ", + Short: "grant a key to a role", + Run: roleGrantCommandFunc, + } +} + // roleAddCommandFunc executes the "role add" command. func roleAddCommandFunc(cmd *cobra.Command, args []string) { if len(args) != 1 { @@ -54,3 +64,22 @@ func roleAddCommandFunc(cmd *cobra.Command, args []string) { fmt.Printf("Role %s created\n", args[0]) } + +// roleGrantCommandFunc executes the "role grant" command. +func roleGrantCommandFunc(cmd *cobra.Command, args []string) { + if len(args) != 3 { + ExitWithError(ExitBadArgs, fmt.Errorf("role grant command requires role name, permission type, and key as its argument.")) + } + + perm, err := clientv3.StrToPermissionType(args[1]) + if err != nil { + ExitWithError(ExitBadArgs, err) + } + + _, err = mustClientFromCmd(cmd).Auth.RoleGrant(context.TODO(), args[0], args[2], perm) + if err != nil { + ExitWithError(ExitError, err) + } + + fmt.Printf("Role %s updated\n", args[0]) +} diff --git a/etcdserver/api/v3rpc/auth.go b/etcdserver/api/v3rpc/auth.go index b07112f3f..9e8e30cd6 100644 --- a/etcdserver/api/v3rpc/auth.go +++ b/etcdserver/api/v3rpc/auth.go @@ -70,8 +70,11 @@ func (as *AuthServer) RoleRevoke(ctx context.Context, r *pb.AuthRoleRevokeReques } func (as *AuthServer) RoleGrant(ctx context.Context, r *pb.AuthRoleGrantRequest) (*pb.AuthRoleGrantResponse, error) { - plog.Info("not implemented yet") - return nil, nil + resp, err := as.authenticator.RoleGrant(ctx, r) + if err != nil { + return nil, togRPCError(err) + } + return resp, nil } func (as *AuthServer) UserAdd(ctx context.Context, r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error) { diff --git a/etcdserver/api/v3rpc/rpctypes/error.go b/etcdserver/api/v3rpc/rpctypes/error.go index 5e174b4b3..376a91dcb 100644 --- a/etcdserver/api/v3rpc/rpctypes/error.go +++ b/etcdserver/api/v3rpc/rpctypes/error.go @@ -40,4 +40,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") + ErrRoleNotFound = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role name not found") ) diff --git a/etcdserver/api/v3rpc/util.go b/etcdserver/api/v3rpc/util.go index 982627a00..a4fae02cd 100644 --- a/etcdserver/api/v3rpc/util.go +++ b/etcdserver/api/v3rpc/util.go @@ -43,6 +43,8 @@ func togRPCError(err error) error { return rpctypes.ErrUserNotFound case auth.ErrRoleAlreadyExist: return rpctypes.ErrRoleAlreadyExist + case auth.ErrRoleNotFound: + return rpctypes.ErrRoleNotFound default: return grpc.Errorf(codes.Internal, err.Error()) } diff --git a/etcdserver/apply.go b/etcdserver/apply.go index da3279851..34ec27908 100644 --- a/etcdserver/apply.go +++ b/etcdserver/apply.go @@ -58,6 +58,7 @@ type applierV3 interface { UserDelete(ua *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error) UserChangePassword(ua *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error) RoleAdd(ua *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) + RoleGrant(ua *pb.AuthRoleGrantRequest) (*pb.AuthRoleGrantResponse, error) } type applierV3backend struct { @@ -93,6 +94,8 @@ func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) *applyResult { ar.resp, ar.err = s.applyV3.UserChangePassword(r.AuthUserChangePassword) case r.AuthRoleAdd != nil: ar.resp, ar.err = s.applyV3.RoleAdd(r.AuthRoleAdd) + case r.AuthRoleGrant != nil: + ar.resp, ar.err = s.applyV3.RoleGrant(r.AuthRoleGrant) default: panic("not implemented") } @@ -496,6 +499,10 @@ func (a *applierV3backend) RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddRes return a.s.AuthStore().RoleAdd(r) } +func (a *applierV3backend) RoleGrant(r *pb.AuthRoleGrantRequest) (*pb.AuthRoleGrantResponse, error) { + return a.s.AuthStore().RoleGrant(r) +} + type quotaApplierV3 struct { applierV3 q Quota diff --git a/etcdserver/etcdserverpb/raft_internal.pb.go b/etcdserver/etcdserverpb/raft_internal.pb.go index 4c66129d7..88e265aa0 100644 --- a/etcdserver/etcdserverpb/raft_internal.pb.go +++ b/etcdserver/etcdserverpb/raft_internal.pb.go @@ -36,7 +36,8 @@ type InternalRaftRequest struct { 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"` 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"` + AuthRoleGrant *AuthRoleGrantRequest `protobuf:"bytes,15,opt,name=auth_role_grant" json:"auth_role_grant,omitempty"` + Alarm *AlarmRequest `protobuf:"bytes,16,opt,name=alarm" json:"alarm,omitempty"` } func (m *InternalRaftRequest) Reset() { *m = InternalRaftRequest{} } @@ -204,16 +205,28 @@ func (m *InternalRaftRequest) MarshalTo(data []byte) (int, error) { } i += n13 } - if m.Alarm != nil { + if m.AuthRoleGrant != nil { data[i] = 0x7a i++ - i = encodeVarintRaftInternal(data, i, uint64(m.Alarm.Size())) - n14, err := m.Alarm.MarshalTo(data[i:]) + i = encodeVarintRaftInternal(data, i, uint64(m.AuthRoleGrant.Size())) + n14, err := m.AuthRoleGrant.MarshalTo(data[i:]) if err != nil { return 0, err } i += n14 } + if m.Alarm != nil { + data[i] = 0x82 + i++ + data[i] = 0x1 + i++ + i = encodeVarintRaftInternal(data, i, uint64(m.Alarm.Size())) + n15, err := m.Alarm.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n15 + } return i, nil } @@ -320,9 +333,13 @@ func (m *InternalRaftRequest) Size() (n int) { l = m.AuthRoleAdd.Size() n += 1 + l + sovRaftInternal(uint64(l)) } + if m.AuthRoleGrant != nil { + l = m.AuthRoleGrant.Size() + n += 1 + l + sovRaftInternal(uint64(l)) + } if m.Alarm != nil { l = m.Alarm.Size() - n += 1 + l + sovRaftInternal(uint64(l)) + n += 2 + l + sovRaftInternal(uint64(l)) } return n } @@ -824,6 +841,39 @@ func (m *InternalRaftRequest) Unmarshal(data []byte) error { } iNdEx = postIndex case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleGrant", 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.AuthRoleGrant == nil { + m.AuthRoleGrant = &AuthRoleGrantRequest{} + } + if err := m.AuthRoleGrant.Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 16: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Alarm", wireType) } diff --git a/etcdserver/etcdserverpb/raft_internal.proto b/etcdserver/etcdserverpb/raft_internal.proto index 1fe517f5c..2a6134bd2 100644 --- a/etcdserver/etcdserverpb/raft_internal.proto +++ b/etcdserver/etcdserverpb/raft_internal.proto @@ -30,8 +30,9 @@ message InternalRaftRequest { AuthUserDeleteRequest auth_user_delete = 12; AuthUserChangePasswordRequest auth_user_change_password = 13; AuthRoleAddRequest auth_role_add = 14; + AuthRoleGrantRequest auth_role_grant = 15; - AlarmRequest alarm = 15; + AlarmRequest alarm = 16; } message EmptyResponse { diff --git a/etcdserver/etcdserverpb/rpc.pb.go b/etcdserver/etcdserverpb/rpc.pb.go index ac5de8cfe..7b574020a 100644 --- a/etcdserver/etcdserverpb/rpc.pb.go +++ b/etcdserver/etcdserverpb/rpc.pb.go @@ -11,14 +11,17 @@ import ( math "math" - context "golang.org/x/net/context" + authpb "github.com/coreos/etcd/auth/authpb" - grpc "google.golang.org/grpc" + io "io" ) import storagepb "github.com/coreos/etcd/storage/storagepb" -import io "io" +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -1362,12 +1365,21 @@ func (m *AuthRoleDeleteRequest) String() string { return proto.CompactTextString func (*AuthRoleDeleteRequest) ProtoMessage() {} type AuthRoleGrantRequest struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Perm *authpb.Permission `protobuf:"bytes,2,opt,name=perm" json:"perm,omitempty"` } func (m *AuthRoleGrantRequest) Reset() { *m = AuthRoleGrantRequest{} } func (m *AuthRoleGrantRequest) String() string { return proto.CompactTextString(m) } func (*AuthRoleGrantRequest) ProtoMessage() {} +func (m *AuthRoleGrantRequest) GetPerm() *authpb.Permission { + if m != nil { + return m.Perm + } + return nil +} + type AuthRoleRevokeRequest struct { } @@ -4724,6 +4736,22 @@ func (m *AuthRoleGrantRequest) 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) + } + if m.Perm != nil { + data[i] = 0x12 + i++ + i = encodeVarintRpc(data, i, uint64(m.Perm.Size())) + n31, err := m.Perm.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n31 + } return i, nil } @@ -4764,11 +4792,11 @@ func (m *AuthEnableResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n31, err := m.Header.MarshalTo(data[i:]) + n32, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n31 + i += n32 } return i, nil } @@ -4792,11 +4820,11 @@ func (m *AuthDisableResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n32, err := m.Header.MarshalTo(data[i:]) + n33, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n32 + i += n33 } return i, nil } @@ -4820,11 +4848,11 @@ func (m *AuthenticateResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n33, err := m.Header.MarshalTo(data[i:]) + n34, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n33 + i += n34 } return i, nil } @@ -4848,11 +4876,11 @@ func (m *AuthUserAddResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n34, err := m.Header.MarshalTo(data[i:]) + n35, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n34 + i += n35 } return i, nil } @@ -4876,11 +4904,11 @@ func (m *AuthUserGetResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n35, err := m.Header.MarshalTo(data[i:]) + n36, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n35 + i += n36 } return i, nil } @@ -4904,11 +4932,11 @@ func (m *AuthUserDeleteResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n36, err := m.Header.MarshalTo(data[i:]) + n37, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n36 + i += n37 } return i, nil } @@ -4932,11 +4960,11 @@ func (m *AuthUserChangePasswordResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n37, err := m.Header.MarshalTo(data[i:]) + n38, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n37 + i += n38 } return i, nil } @@ -4960,11 +4988,11 @@ func (m *AuthUserGrantResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n38, err := m.Header.MarshalTo(data[i:]) + n39, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n38 + i += n39 } return i, nil } @@ -4988,11 +5016,11 @@ func (m *AuthUserRevokeResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n39, err := m.Header.MarshalTo(data[i:]) + n40, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n39 + i += n40 } return i, nil } @@ -5016,11 +5044,11 @@ func (m *AuthRoleAddResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n40, err := m.Header.MarshalTo(data[i:]) + n41, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n40 + i += n41 } return i, nil } @@ -5044,11 +5072,11 @@ func (m *AuthRoleGetResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n41, err := m.Header.MarshalTo(data[i:]) + n42, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n41 + i += n42 } return i, nil } @@ -5072,11 +5100,11 @@ func (m *AuthRoleDeleteResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n42, err := m.Header.MarshalTo(data[i:]) + n43, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n42 + i += n43 } return i, nil } @@ -5100,11 +5128,11 @@ func (m *AuthRoleGrantResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n43, err := m.Header.MarshalTo(data[i:]) + n44, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n43 + i += n44 } return i, nil } @@ -5128,11 +5156,11 @@ func (m *AuthRoleRevokeResponse) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintRpc(data, i, uint64(m.Header.Size())) - n44, err := m.Header.MarshalTo(data[i:]) + n45, err := m.Header.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n44 + i += n45 } return i, nil } @@ -5962,6 +5990,14 @@ func (m *AuthRoleDeleteRequest) Size() (n int) { func (m *AuthRoleGrantRequest) Size() (n int) { var l int _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovRpc(uint64(l)) + } + if m.Perm != nil { + l = m.Perm.Size() + n += 1 + l + sovRpc(uint64(l)) + } return n } @@ -11503,6 +11539,68 @@ func (m *AuthRoleGrantRequest) Unmarshal(data []byte) error { return fmt.Errorf("proto: AuthRoleGrantRequest: 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 + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Perm", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthRpc + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Perm == nil { + m.Perm = &authpb.Permission{} + } + if err := m.Perm.Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipRpc(data[iNdEx:]) diff --git a/etcdserver/etcdserverpb/rpc.proto b/etcdserver/etcdserverpb/rpc.proto index b1a501bf2..afd578480 100644 --- a/etcdserver/etcdserverpb/rpc.proto +++ b/etcdserver/etcdserverpb/rpc.proto @@ -3,6 +3,7 @@ package etcdserverpb; import "gogoproto/gogo.proto"; import "etcd/storage/storagepb/kv.proto"; +import "etcd/auth/authpb/auth.proto"; option (gogoproto.marshaler_all) = true; option (gogoproto.unmarshaler_all) = true; @@ -525,6 +526,8 @@ message AuthRoleDeleteRequest { } message AuthRoleGrantRequest { + string name = 1; + authpb.Permission perm = 2; } message AuthRoleRevokeRequest { diff --git a/etcdserver/v3demo_server.go b/etcdserver/v3demo_server.go index eb81e2b89..f5f4d69c4 100644 --- a/etcdserver/v3demo_server.go +++ b/etcdserver/v3demo_server.go @@ -57,6 +57,7 @@ type Authenticator interface { 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) + RoleGrant(ctx context.Context, r *pb.AuthRoleGrantRequest) (*pb.AuthRoleGrantResponse, error) } func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) { @@ -242,6 +243,14 @@ func (s *EtcdServer) RoleAdd(ctx context.Context, r *pb.AuthRoleAddRequest) (*pb return result.resp.(*pb.AuthRoleAddResponse), result.err } +func (s *EtcdServer) RoleGrant(ctx context.Context, r *pb.AuthRoleGrantRequest) (*pb.AuthRoleGrantResponse, error) { + result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{AuthRoleGrant: r}) + if err != nil { + return nil, err + } + return result.resp.(*pb.AuthRoleGrantResponse), result.err +} + func (s *EtcdServer) processInternalRaftRequest(ctx context.Context, r pb.InternalRaftRequest) (*applyResult, error) { r.ID = s.reqIDGen.Next()