mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #4845 from mitake/auth-user
*: support adding user in v3 auth
This commit is contained in:
commit
fa98d8d337
379
auth/authpb/auth.pb.go
Normal file
379
auth/authpb/auth.pb.go
Normal 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
17
auth/authpb/auth.proto
Normal 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;
|
||||||
|
}
|
@ -15,13 +15,18 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
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/etcd/storage/backend"
|
||||||
"github.com/coreos/pkg/capnslog"
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
enableFlagKey = []byte("authEnabled")
|
enableFlagKey = []byte("authEnabled")
|
||||||
authBucketName = []byte("auth")
|
authBucketName = []byte("auth")
|
||||||
|
authUsersBucketName = []byte("authUsers")
|
||||||
|
|
||||||
plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "auth")
|
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 recovers the state of auth store from the given backend
|
||||||
Recover(b backend.Backend)
|
Recover(b backend.Backend)
|
||||||
|
|
||||||
|
// UserAdd adds a new user
|
||||||
|
UserAdd(r *pb.UserAddRequest) (*pb.UserAddResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type authStore struct {
|
type authStore struct {
|
||||||
@ -56,10 +64,49 @@ func (as *authStore) Recover(be backend.Backend) {
|
|||||||
// TODO(mitake): recovery process
|
// 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 {
|
func NewAuthStore(be backend.Backend) *authStore {
|
||||||
tx := be.BatchTx()
|
tx := be.BatchTx()
|
||||||
tx.Lock()
|
tx.Lock()
|
||||||
|
|
||||||
tx.UnsafeCreateBucket(authBucketName)
|
tx.UnsafeCreateBucket(authBucketName)
|
||||||
|
tx.UnsafeCreateBucket(authUsersBucketName)
|
||||||
|
|
||||||
tx.Unlock()
|
tx.Unlock()
|
||||||
be.ForceCommit()
|
be.ForceCommit()
|
||||||
|
|
||||||
|
@ -22,11 +22,15 @@ import (
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
AuthEnableResponse pb.AuthEnableResponse
|
AuthEnableResponse pb.AuthEnableResponse
|
||||||
|
UserAddResponse pb.UserAddResponse
|
||||||
)
|
)
|
||||||
|
|
||||||
type Auth interface {
|
type Auth interface {
|
||||||
// AuthEnable enables auth of a etcd cluster.
|
// AuthEnable enables auth of an etcd cluster.
|
||||||
AuthEnable(ctx context.Context) (*AuthEnableResponse, error)
|
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 {
|
type auth struct {
|
||||||
@ -49,3 +53,8 @@ func (auth *auth) AuthEnable(ctx context.Context) (*AuthEnableResponse, error) {
|
|||||||
resp, err := auth.remote.AuthEnable(ctx, &pb.AuthEnableRequest{})
|
resp, err := auth.remote.AuthEnable(ctx, &pb.AuthEnableRequest{})
|
||||||
return (*AuthEnableResponse)(resp), err
|
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
|
||||||
|
}
|
||||||
|
91
etcdctlv3/command/user_command.go
Normal file
91
etcdctlv3/command/user_command.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -77,6 +77,7 @@ func init() {
|
|||||||
command.NewLockCommand(),
|
command.NewLockCommand(),
|
||||||
command.NewAuthCommand(),
|
command.NewAuthCommand(),
|
||||||
command.NewElectCommand(),
|
command.NewElectCommand(),
|
||||||
|
command.NewUserCommand(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
func (as *AuthServer) UserAdd(ctx context.Context, r *pb.UserAddRequest) (*pb.UserAddResponse, error) {
|
||||||
plog.Info("not implemented yet")
|
return as.authenticator.UserAdd(ctx, r)
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (as *AuthServer) UserDelete(ctx context.Context, r *pb.UserDeleteRequest) (*pb.UserDeleteResponse, error) {
|
func (as *AuthServer) UserDelete(ctx context.Context, r *pb.UserDeleteRequest) (*pb.UserDeleteResponse, error) {
|
||||||
|
@ -35,4 +35,6 @@ var (
|
|||||||
ErrMemberNotFound = grpc.Errorf(codes.NotFound, "etcdserver: member not found")
|
ErrMemberNotFound = grpc.Errorf(codes.NotFound, "etcdserver: member not found")
|
||||||
|
|
||||||
ErrRequestTooLarge = grpc.Errorf(codes.InvalidArgument, "etcdserver: request is too large")
|
ErrRequestTooLarge = grpc.Errorf(codes.InvalidArgument, "etcdserver: request is too large")
|
||||||
|
|
||||||
|
ErrUserAlreadyExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: user name already exists")
|
||||||
)
|
)
|
||||||
|
@ -32,6 +32,7 @@ type InternalRaftRequest struct {
|
|||||||
LeaseCreate *LeaseCreateRequest `protobuf:"bytes,8,opt,name=lease_create" json:"lease_create,omitempty"`
|
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"`
|
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"`
|
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{} }
|
func (m *InternalRaftRequest) Reset() { *m = InternalRaftRequest{} }
|
||||||
@ -159,6 +160,16 @@ func (m *InternalRaftRequest) MarshalTo(data []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
i += n9
|
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
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,6 +260,10 @@ func (m *InternalRaftRequest) Size() (n int) {
|
|||||||
l = m.AuthEnable.Size()
|
l = m.AuthEnable.Size()
|
||||||
n += 1 + l + sovRaftInternal(uint64(l))
|
n += 1 + l + sovRaftInternal(uint64(l))
|
||||||
}
|
}
|
||||||
|
if m.UserAdd != nil {
|
||||||
|
l = m.UserAdd.Size()
|
||||||
|
n += 1 + l + sovRaftInternal(uint64(l))
|
||||||
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -616,6 +631,39 @@ func (m *InternalRaftRequest) Unmarshal(data []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
iNdEx = postIndex
|
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:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skipRaftInternal(data[iNdEx:])
|
skippy, err := skipRaftInternal(data[iNdEx:])
|
||||||
|
@ -26,6 +26,7 @@ message InternalRaftRequest {
|
|||||||
LeaseRevokeRequest lease_revoke = 9;
|
LeaseRevokeRequest lease_revoke = 9;
|
||||||
|
|
||||||
AuthEnableRequest auth_enable = 10;
|
AuthEnableRequest auth_enable = 10;
|
||||||
|
UserAddRequest user_add = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
message EmptyResponse {
|
message EmptyResponse {
|
||||||
|
@ -1181,6 +1181,8 @@ func (m *AuthenticateRequest) String() string { return proto.CompactTextString(m
|
|||||||
func (*AuthenticateRequest) ProtoMessage() {}
|
func (*AuthenticateRequest) ProtoMessage() {}
|
||||||
|
|
||||||
type UserAddRequest struct {
|
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{} }
|
func (m *UserAddRequest) Reset() { *m = UserAddRequest{} }
|
||||||
@ -4182,6 +4184,18 @@ func (m *UserAddRequest) MarshalTo(data []byte) (int, error) {
|
|||||||
_ = i
|
_ = i
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = 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
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5438,6 +5452,14 @@ func (m *AuthenticateRequest) Size() (n int) {
|
|||||||
func (m *UserAddRequest) Size() (n int) {
|
func (m *UserAddRequest) Size() (n int) {
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = 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
|
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)
|
return fmt.Errorf("proto: UserAddRequest: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
}
|
}
|
||||||
switch fieldNum {
|
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:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skipRpc(data[iNdEx:])
|
skippy, err := skipRpc(data[iNdEx:])
|
||||||
|
@ -448,6 +448,8 @@ message AuthenticateRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message UserAddRequest {
|
message UserAddRequest {
|
||||||
|
string name = 1;
|
||||||
|
string password = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UserGetRequest {
|
message UserGetRequest {
|
||||||
|
@ -59,6 +59,7 @@ type Lessor interface {
|
|||||||
|
|
||||||
type Authenticator interface {
|
type Authenticator interface {
|
||||||
AuthEnable(ctx context.Context, r *pb.AuthEnableRequest) (*pb.AuthEnableResponse, error)
|
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) {
|
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
|
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 {
|
type applyResult struct {
|
||||||
resp proto.Message
|
resp proto.Message
|
||||||
err error
|
err error
|
||||||
@ -252,6 +261,8 @@ func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) interface{} {
|
|||||||
ar.resp, ar.err = applyLeaseRevoke(le, r.LeaseRevoke)
|
ar.resp, ar.err = applyLeaseRevoke(le, r.LeaseRevoke)
|
||||||
case r.AuthEnable != nil:
|
case r.AuthEnable != nil:
|
||||||
ar.resp, ar.err = applyAuthEnable(s)
|
ar.resp, ar.err = applyAuthEnable(s)
|
||||||
|
case r.UserAdd != nil:
|
||||||
|
ar.resp, ar.err = applyUserAdd(s, r.UserAdd)
|
||||||
default:
|
default:
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
@ -660,3 +671,7 @@ func applyAuthEnable(s *EtcdServer) (*pb.AuthEnableResponse, error) {
|
|||||||
s.AuthStore().AuthEnable()
|
s.AuthStore().AuthEnable()
|
||||||
return &pb.AuthEnableResponse{}, nil
|
return &pb.AuthEnableResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyUserAdd(s *EtcdServer, r *pb.UserAddRequest) (*pb.UserAddResponse, error) {
|
||||||
|
return s.AuthStore().UserAdd(r)
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@ if ! [[ $(protoc --version) =~ "3.0.0" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# directories containing protos to be built
|
# 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
|
# exact version of protoc-gen-gogo to build
|
||||||
SHA="c57e439bad574c2e0877ff18d514badcfced004d"
|
SHA="c57e439bad574c2e0877ff18d514badcfced004d"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user