Merge pull request #4845 from mitake/auth-user

*: support adding user in v3 auth
release-3.0
Xiang Li 2016-03-27 07:51:10 -07:00
commit fa98d8d337
14 changed files with 697 additions and 6 deletions

379
auth/authpb/auth.pb.go Normal file
View File

@ -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")
)

17
auth/authpb/auth.proto Normal file
View File

@ -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;
}

View File

@ -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()

View File

@ -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
}

View File

@ -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 <subcommand>",
Short: "user related command",
}
ac.AddCommand(NewUserAddCommand())
return ac
}
var (
passwordInteractive bool
)
func NewUserAddCommand() *cobra.Command {
cmd := cobra.Command{
Use: "add <user name>",
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)
}
}

View File

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

View File

@ -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) {

View File

@ -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")
)

View File

@ -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:])

View File

@ -26,6 +26,7 @@ message InternalRaftRequest {
LeaseRevokeRequest lease_revoke = 9;
AuthEnableRequest auth_enable = 10;
UserAddRequest user_add = 11;
}
message EmptyResponse {

View File

@ -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:])

View File

@ -448,6 +448,8 @@ message AuthenticateRequest {
}
message UserAddRequest {
string name = 1;
string password = 2;
}
message UserGetRequest {

View File

@ -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)
}

View File

@ -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"