From 8874545a1e36ae426c7c677650a272c8721b0c8d Mon Sep 17 00:00:00 2001 From: Hitoshi Mitake Date: Wed, 23 Mar 2016 13:25:48 +0900 Subject: [PATCH] *: support adding user in v3 auth This commit adds a new subcommand "user add" to etcdctlv3. With the command users can create a user for the authentication. Example of usage: $ etcdctlv3 user add user1 Password of user1: Type password of user1 again for confirmation: --- auth/authpb/auth.pb.go | 379 ++++++++++++++++++++ auth/authpb/auth.proto | 17 + auth/store.go | 51 ++- clientv3/auth.go | 11 +- etcdctlv3/command/user_command.go | 91 +++++ etcdctlv3/main.go | 1 + etcdserver/api/v3rpc/auth.go | 3 +- etcdserver/api/v3rpc/rpctypes/error.go | 2 + etcdserver/etcdserverpb/raft_internal.pb.go | 48 +++ etcdserver/etcdserverpb/raft_internal.proto | 1 + etcdserver/etcdserverpb/rpc.pb.go | 80 +++++ etcdserver/etcdserverpb/rpc.proto | 2 + etcdserver/v3demo_server.go | 15 + scripts/genproto.sh | 2 +- 14 files changed, 697 insertions(+), 6 deletions(-) create mode 100644 auth/authpb/auth.pb.go create mode 100644 auth/authpb/auth.proto create mode 100644 etcdctlv3/command/user_command.go diff --git a/auth/authpb/auth.pb.go b/auth/authpb/auth.pb.go new file mode 100644 index 000000000..4fcb774ef --- /dev/null +++ b/auth/authpb/auth.pb.go @@ -0,0 +1,379 @@ +// Code generated by protoc-gen-gogo. +// source: auth.proto +// DO NOT EDIT! + +/* + Package authpb is a generated protocol buffer package. + + It is generated from these files: + auth.proto + + It has these top-level messages: + User +*/ +package authpb + +import ( + "fmt" + + proto "github.com/gogo/protobuf/proto" +) + +import math "math" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// User is a single entry in the bucket authUsers +type User struct { + Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Password []byte `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + Tombstone int64 `protobuf:"varint,3,opt,name=tombstone,proto3" json:"tombstone,omitempty"` +} + +func (m *User) Reset() { *m = User{} } +func (m *User) String() string { return proto.CompactTextString(m) } +func (*User) ProtoMessage() {} + +func init() { + proto.RegisterType((*User)(nil), "authpb.User") +} +func (m *User) 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 *User) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Name != nil { + if len(m.Name) > 0 { + data[i] = 0xa + i++ + i = encodeVarintAuth(data, i, uint64(len(m.Name))) + i += copy(data[i:], m.Name) + } + } + if m.Password != nil { + if len(m.Password) > 0 { + data[i] = 0x12 + i++ + i = encodeVarintAuth(data, i, uint64(len(m.Password))) + i += copy(data[i:], m.Password) + } + } + if m.Tombstone != 0 { + data[i] = 0x18 + i++ + i = encodeVarintAuth(data, i, uint64(m.Tombstone)) + } + return i, nil +} + +func encodeFixed64Auth(data []byte, offset int, v uint64) int { + data[offset] = uint8(v) + data[offset+1] = uint8(v >> 8) + data[offset+2] = uint8(v >> 16) + data[offset+3] = uint8(v >> 24) + data[offset+4] = uint8(v >> 32) + data[offset+5] = uint8(v >> 40) + data[offset+6] = uint8(v >> 48) + data[offset+7] = uint8(v >> 56) + return offset + 8 +} +func encodeFixed32Auth(data []byte, offset int, v uint32) int { + data[offset] = uint8(v) + data[offset+1] = uint8(v >> 8) + data[offset+2] = uint8(v >> 16) + data[offset+3] = uint8(v >> 24) + return offset + 4 +} +func encodeVarintAuth(data []byte, offset int, v uint64) int { + for v >= 1<<7 { + data[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + data[offset] = uint8(v) + return offset + 1 +} +func (m *User) Size() (n int) { + var l int + _ = l + if m.Name != nil { + l = len(m.Name) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + } + if m.Password != nil { + l = len(m.Password) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + } + if m.Tombstone != 0 { + n += 1 + sovAuth(uint64(m.Tombstone)) + } + return n +} + +func sovAuth(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozAuth(x uint64) (n int) { + return sovAuth(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *User) 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: User: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: User: 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 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 + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Password", 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.Password = append(m.Password[:0], data[iNdEx:postIndex]...) + if m.Password == nil { + m.Password = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Tombstone", wireType) + } + m.Tombstone = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + m.Tombstone |= (int64(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 skipAuth(data []byte) (n int, err error) { + l := len(data) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if data[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthAuth + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipAuth(data[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthAuth = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAuth = fmt.Errorf("proto: integer overflow") +) diff --git a/auth/authpb/auth.proto b/auth/authpb/auth.proto new file mode 100644 index 000000000..e0ab5c54c --- /dev/null +++ b/auth/authpb/auth.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package authpb; + +import "gogoproto/gogo.proto"; + +option (gogoproto.marshaler_all) = true; +option (gogoproto.sizer_all) = true; +option (gogoproto.unmarshaler_all) = true; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.goproto_enum_prefix_all) = false; + +// User is a single entry in the bucket authUsers +message User { + bytes name = 1; + bytes password = 2; + int64 tombstone = 3; +} diff --git a/auth/store.go b/auth/store.go index 4b61911d2..8663c8b1a 100644 --- a/auth/store.go +++ b/auth/store.go @@ -15,13 +15,18 @@ package auth import ( + "github.com/coreos/etcd/auth/authpb" + "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes" + pb "github.com/coreos/etcd/etcdserver/etcdserverpb" "github.com/coreos/etcd/storage/backend" "github.com/coreos/pkg/capnslog" + "golang.org/x/crypto/bcrypt" ) var ( - enableFlagKey = []byte("authEnabled") - authBucketName = []byte("auth") + enableFlagKey = []byte("authEnabled") + authBucketName = []byte("auth") + authUsersBucketName = []byte("authUsers") plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "auth") ) @@ -32,6 +37,9 @@ type AuthStore interface { // Recover recovers the state of auth store from the given backend Recover(b backend.Backend) + + // UserAdd adds a new user + UserAdd(r *pb.UserAddRequest) (*pb.UserAddResponse, error) } type authStore struct { @@ -56,10 +64,49 @@ func (as *authStore) Recover(be backend.Backend) { // TODO(mitake): recovery process } +func (as *authStore) UserAdd(r *pb.UserAddRequest) (*pb.UserAddResponse, error) { + plog.Noticef("adding a new user: %s", r.Name) + + hashed, err := bcrypt.GenerateFromPassword([]byte(r.Password), bcrypt.DefaultCost) + if err != nil { + plog.Errorf("failed to hash password: %s", err) + return nil, err + } + + tx := as.be.BatchTx() + tx.Lock() + defer tx.Unlock() + + _, vs := tx.UnsafeRange(authUsersBucketName, []byte(r.Name), nil, 0) + if len(vs) != 0 { + return &pb.UserAddResponse{}, rpctypes.ErrUserAlreadyExist + } + + newUser := authpb.User{ + Name: []byte(r.Name), + Password: hashed, + } + + marshaledUser, merr := newUser.Marshal() + if merr != nil { + plog.Errorf("failed to marshal a new user data: %s", merr) + return nil, merr + } + + tx.UnsafePut(authUsersBucketName, []byte(r.Name), marshaledUser) + + plog.Noticef("added a new user: %s", r.Name) + + return &pb.UserAddResponse{}, nil +} + func NewAuthStore(be backend.Backend) *authStore { tx := be.BatchTx() tx.Lock() + tx.UnsafeCreateBucket(authBucketName) + tx.UnsafeCreateBucket(authUsersBucketName) + tx.Unlock() be.ForceCommit() diff --git a/clientv3/auth.go b/clientv3/auth.go index 5c6819720..352ebf63e 100644 --- a/clientv3/auth.go +++ b/clientv3/auth.go @@ -22,11 +22,15 @@ import ( type ( AuthEnableResponse pb.AuthEnableResponse + UserAddResponse pb.UserAddResponse ) type Auth interface { - // AuthEnable enables auth of a etcd cluster. + // AuthEnable enables auth of an etcd cluster. AuthEnable(ctx context.Context) (*AuthEnableResponse, error) + + // UserAdd adds a new user to an etcd cluster. + UserAdd(ctx context.Context, name string, password string) (*UserAddResponse, error) } type auth struct { @@ -49,3 +53,8 @@ func (auth *auth) AuthEnable(ctx context.Context) (*AuthEnableResponse, error) { resp, err := auth.remote.AuthEnable(ctx, &pb.AuthEnableRequest{}) return (*AuthEnableResponse)(resp), err } + +func (auth *auth) UserAdd(ctx context.Context, name string, password string) (*UserAddResponse, error) { + resp, err := auth.remote.UserAdd(ctx, &pb.UserAddRequest{Name: name, Password: password}) + return (*UserAddResponse)(resp), err +} diff --git a/etcdctlv3/command/user_command.go b/etcdctlv3/command/user_command.go new file mode 100644 index 000000000..77fb79771 --- /dev/null +++ b/etcdctlv3/command/user_command.go @@ -0,0 +1,91 @@ +// 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" + "strings" + + "github.com/bgentry/speakeasy" + "github.com/spf13/cobra" + "golang.org/x/net/context" +) + +// NewUserCommand returns the cobra command for "user". +func NewUserCommand() *cobra.Command { + ac := &cobra.Command{ + Use: "user ", + Short: "user related command", + } + + ac.AddCommand(NewUserAddCommand()) + + return ac +} + +var ( + passwordInteractive bool +) + +func NewUserAddCommand() *cobra.Command { + cmd := cobra.Command{ + Use: "add ", + Short: "add a new user", + Run: userAddCommandFunc, + } + + cmd.Flags().BoolVar(&passwordInteractive, "interactive", true, "read password from stdin instead of interactive terminal") + + return &cmd +} + +// userAddCommandFunc executes the "user add" command. +func userAddCommandFunc(cmd *cobra.Command, args []string) { + if len(args) != 1 { + ExitWithError(ExitBadArgs, fmt.Errorf("user add command requires user name as its argument.")) + } + + var password string + + if !passwordInteractive { + fmt.Scanf("%s", &password) + } else { + prompt1 := fmt.Sprintf("Password of %s: ", args[0]) + password1, err1 := speakeasy.Ask(prompt1) + if err1 != nil { + ExitWithError(ExitBadArgs, fmt.Errorf("failed to ask password: %s.", err1)) + } + + if len(password1) == 0 { + ExitWithError(ExitBadArgs, fmt.Errorf("empty password")) + } + + prompt2 := fmt.Sprintf("Type password of %s again for confirmation: ", args[0]) + password2, err2 := speakeasy.Ask(prompt2) + if err2 != nil { + ExitWithError(ExitBadArgs, fmt.Errorf("failed to ask password: %s.", err2)) + } + + if strings.Compare(password1, password2) != 0 { + ExitWithError(ExitBadArgs, fmt.Errorf("given passwords are different.")) + } + password = password1 + } + + _, err := mustClientFromCmd(cmd).Auth.UserAdd(context.TODO(), args[0], password) + if err != nil { + ExitWithError(ExitError, err) + } +} diff --git a/etcdctlv3/main.go b/etcdctlv3/main.go index 135510bc8..f1f2177d4 100644 --- a/etcdctlv3/main.go +++ b/etcdctlv3/main.go @@ -76,6 +76,7 @@ func init() { command.NewLockCommand(), command.NewAuthCommand(), command.NewElectCommand(), + command.NewUserCommand(), ) } diff --git a/etcdserver/api/v3rpc/auth.go b/etcdserver/api/v3rpc/auth.go index 47d60f757..567ab72f9 100644 --- a/etcdserver/api/v3rpc/auth.go +++ b/etcdserver/api/v3rpc/auth.go @@ -68,8 +68,7 @@ func (as *AuthServer) RoleGrant(ctx context.Context, r *pb.RoleGrantRequest) (*p } func (as *AuthServer) UserAdd(ctx context.Context, r *pb.UserAddRequest) (*pb.UserAddResponse, error) { - plog.Info("not implemented yet") - return nil, nil + return as.authenticator.UserAdd(ctx, r) } func (as *AuthServer) UserDelete(ctx context.Context, r *pb.UserDeleteRequest) (*pb.UserDeleteResponse, error) { diff --git a/etcdserver/api/v3rpc/rpctypes/error.go b/etcdserver/api/v3rpc/rpctypes/error.go index 3b95cc422..00ab1b07d 100644 --- a/etcdserver/api/v3rpc/rpctypes/error.go +++ b/etcdserver/api/v3rpc/rpctypes/error.go @@ -35,4 +35,6 @@ var ( ErrMemberNotFound = grpc.Errorf(codes.NotFound, "etcdserver: member not found") ErrRequestTooLarge = grpc.Errorf(codes.InvalidArgument, "etcdserver: request is too large") + + ErrUserAlreadyExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: user name already exists") ) diff --git a/etcdserver/etcdserverpb/raft_internal.pb.go b/etcdserver/etcdserverpb/raft_internal.pb.go index a5c0a315b..d269791d5 100644 --- a/etcdserver/etcdserverpb/raft_internal.pb.go +++ b/etcdserver/etcdserverpb/raft_internal.pb.go @@ -32,6 +32,7 @@ type InternalRaftRequest struct { LeaseCreate *LeaseCreateRequest `protobuf:"bytes,8,opt,name=lease_create" json:"lease_create,omitempty"` LeaseRevoke *LeaseRevokeRequest `protobuf:"bytes,9,opt,name=lease_revoke" json:"lease_revoke,omitempty"` AuthEnable *AuthEnableRequest `protobuf:"bytes,10,opt,name=auth_enable" json:"auth_enable,omitempty"` + UserAdd *UserAddRequest `protobuf:"bytes,11,opt,name=user_add" json:"user_add,omitempty"` } func (m *InternalRaftRequest) Reset() { *m = InternalRaftRequest{} } @@ -159,6 +160,16 @@ func (m *InternalRaftRequest) MarshalTo(data []byte) (int, error) { } i += n9 } + if m.UserAdd != nil { + data[i] = 0x5a + i++ + i = encodeVarintRaftInternal(data, i, uint64(m.UserAdd.Size())) + n10, err := m.UserAdd.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n10 + } return i, nil } @@ -249,6 +260,10 @@ func (m *InternalRaftRequest) Size() (n int) { l = m.AuthEnable.Size() n += 1 + l + sovRaftInternal(uint64(l)) } + if m.UserAdd != nil { + l = m.UserAdd.Size() + n += 1 + l + sovRaftInternal(uint64(l)) + } return n } @@ -616,6 +631,39 @@ func (m *InternalRaftRequest) Unmarshal(data []byte) error { return err } iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserAdd", 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.UserAdd == nil { + m.UserAdd = &UserAddRequest{} + } + if err := m.UserAdd.Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipRaftInternal(data[iNdEx:]) diff --git a/etcdserver/etcdserverpb/raft_internal.proto b/etcdserver/etcdserverpb/raft_internal.proto index a68cebc82..01e0e7c56 100644 --- a/etcdserver/etcdserverpb/raft_internal.proto +++ b/etcdserver/etcdserverpb/raft_internal.proto @@ -26,6 +26,7 @@ message InternalRaftRequest { LeaseRevokeRequest lease_revoke = 9; AuthEnableRequest auth_enable = 10; + UserAddRequest user_add = 11; } message EmptyResponse { diff --git a/etcdserver/etcdserverpb/rpc.pb.go b/etcdserver/etcdserverpb/rpc.pb.go index bae92b6ee..f2e4abd84 100644 --- a/etcdserver/etcdserverpb/rpc.pb.go +++ b/etcdserver/etcdserverpb/rpc.pb.go @@ -1181,6 +1181,8 @@ func (m *AuthenticateRequest) String() string { return proto.CompactTextString(m func (*AuthenticateRequest) ProtoMessage() {} type UserAddRequest struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` } func (m *UserAddRequest) Reset() { *m = UserAddRequest{} } @@ -4182,6 +4184,18 @@ func (m *UserAddRequest) 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 len(m.Password) > 0 { + data[i] = 0x12 + i++ + i = encodeVarintRpc(data, i, uint64(len(m.Password))) + i += copy(data[i:], m.Password) + } return i, nil } @@ -5438,6 +5452,14 @@ func (m *AuthenticateRequest) Size() (n int) { func (m *UserAddRequest) Size() (n int) { var l int _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovRpc(uint64(l)) + } + l = len(m.Password) + if l > 0 { + n += 1 + l + sovRpc(uint64(l)) + } return n } @@ -9918,6 +9940,64 @@ func (m *UserAddRequest) Unmarshal(data []byte) error { return fmt.Errorf("proto: UserAddRequest: 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 Password", 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.Password = string(data[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipRpc(data[iNdEx:]) diff --git a/etcdserver/etcdserverpb/rpc.proto b/etcdserver/etcdserverpb/rpc.proto index f0e7f2a62..444b004a7 100644 --- a/etcdserver/etcdserverpb/rpc.proto +++ b/etcdserver/etcdserverpb/rpc.proto @@ -448,6 +448,8 @@ message AuthenticateRequest { } message UserAddRequest { + string name = 1; + string password = 2; } message UserGetRequest { diff --git a/etcdserver/v3demo_server.go b/etcdserver/v3demo_server.go index 1ce8bad67..34866b7fb 100644 --- a/etcdserver/v3demo_server.go +++ b/etcdserver/v3demo_server.go @@ -59,6 +59,7 @@ type Lessor interface { type Authenticator interface { AuthEnable(ctx context.Context, r *pb.AuthEnableRequest) (*pb.AuthEnableResponse, error) + UserAdd(ctx context.Context, r *pb.UserAddRequest) (*pb.UserAddResponse, error) } func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) { @@ -185,6 +186,14 @@ func (s *EtcdServer) AuthEnable(ctx context.Context, r *pb.AuthEnableRequest) (* return result.resp.(*pb.AuthEnableResponse), result.err } +func (s *EtcdServer) UserAdd(ctx context.Context, r *pb.UserAddRequest) (*pb.UserAddResponse, error) { + result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{UserAdd: r}) + if err != nil { + return nil, err + } + return result.resp.(*pb.UserAddResponse), result.err +} + type applyResult struct { resp proto.Message err error @@ -252,6 +261,8 @@ func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) interface{} { ar.resp, ar.err = applyLeaseRevoke(le, r.LeaseRevoke) case r.AuthEnable != nil: ar.resp, ar.err = applyAuthEnable(s) + case r.UserAdd != nil: + ar.resp, ar.err = applyUserAdd(s, r.UserAdd) default: panic("not implemented") } @@ -660,3 +671,7 @@ func applyAuthEnable(s *EtcdServer) (*pb.AuthEnableResponse, error) { s.AuthStore().AuthEnable() return &pb.AuthEnableResponse{}, nil } + +func applyUserAdd(s *EtcdServer, r *pb.UserAddRequest) (*pb.UserAddResponse, error) { + return s.AuthStore().UserAdd(r) +} diff --git a/scripts/genproto.sh b/scripts/genproto.sh index a5545dffe..a0b0a064f 100755 --- a/scripts/genproto.sh +++ b/scripts/genproto.sh @@ -17,7 +17,7 @@ if ! [[ $(protoc --version) =~ "3.0.0" ]]; then fi # directories containing protos to be built -DIRS="./wal/walpb ./etcdserver/etcdserverpb ./snap/snappb ./raft/raftpb ./storage/storagepb ./lease/leasepb" +DIRS="./wal/walpb ./etcdserver/etcdserverpb ./snap/snappb ./raft/raftpb ./storage/storagepb ./lease/leasepb ./auth/authpb" # exact version of protoc-gen-gogo to build SHA="c57e439bad574c2e0877ff18d514badcfced004d"