|
@@ -84,41 +84,19 @@ type Authenticator interface {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
- var result *applyResult
|
|
|
|
|
- var err error
|
|
|
|
|
-
|
|
|
|
|
if r.Serializable {
|
|
if r.Serializable {
|
|
|
- for {
|
|
|
|
|
- authInfo, err := s.authInfoFromCtx(ctx)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return nil, err
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- hdr := &pb.RequestHeader{}
|
|
|
|
|
- if authInfo != nil {
|
|
|
|
|
- hdr.Username = authInfo.Username
|
|
|
|
|
- hdr.AuthRevision = authInfo.Revision
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- result = s.applyV3.Apply(&pb.InternalRaftRequest{Header: hdr, Range: r})
|
|
|
|
|
-
|
|
|
|
|
- if result.err != nil {
|
|
|
|
|
- if result.err == auth.ErrAuthOldRevision {
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if authInfo == nil || authInfo.Revision == s.authStore.Revision() {
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // The revision that authorized this request is obsolete.
|
|
|
|
|
- // For avoiding TOCTOU problem, retry of the request is required.
|
|
|
|
|
|
|
+ var resp *pb.RangeResponse
|
|
|
|
|
+ var err error
|
|
|
|
|
+ chk := func(ai *auth.AuthInfo) error {
|
|
|
|
|
+ return s.authStore.IsRangePermitted(ai, r.Key, r.RangeEnd)
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- result, err = s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Range: r})
|
|
|
|
|
|
|
+ get := func() { resp, err = s.applyV3Base.Range(noTxn, r) }
|
|
|
|
|
+ if serr := s.doSerialize(ctx, chk, get); serr != nil {
|
|
|
|
|
+ return nil, serr
|
|
|
|
|
+ }
|
|
|
|
|
+ return resp, err
|
|
|
}
|
|
}
|
|
|
|
|
+ result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Range: r})
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
@@ -151,41 +129,19 @@ func (s *EtcdServer) DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (s *EtcdServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error) {
|
|
func (s *EtcdServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error) {
|
|
|
- var result *applyResult
|
|
|
|
|
- var err error
|
|
|
|
|
-
|
|
|
|
|
if isTxnSerializable(r) {
|
|
if isTxnSerializable(r) {
|
|
|
- for {
|
|
|
|
|
- authInfo, err := s.authInfoFromCtx(ctx)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return nil, err
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- hdr := &pb.RequestHeader{}
|
|
|
|
|
- if authInfo != nil {
|
|
|
|
|
- hdr.Username = authInfo.Username
|
|
|
|
|
- hdr.AuthRevision = authInfo.Revision
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- result = s.applyV3.Apply(&pb.InternalRaftRequest{Header: hdr, Txn: r})
|
|
|
|
|
-
|
|
|
|
|
- if result.err != nil {
|
|
|
|
|
- if result.err == auth.ErrAuthOldRevision {
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if authInfo == nil || authInfo.Revision == s.authStore.Revision() {
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // The revision that authorized this request is obsolete.
|
|
|
|
|
- // For avoiding TOCTOU problem, retry of this request is required.
|
|
|
|
|
|
|
+ var resp *pb.TxnResponse
|
|
|
|
|
+ var err error
|
|
|
|
|
+ chk := func(ai *auth.AuthInfo) error {
|
|
|
|
|
+ return checkTxnAuth(s.authStore, ai, r)
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- result, err = s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Txn: r})
|
|
|
|
|
|
|
+ get := func() { resp, err = s.applyV3Base.Txn(r) }
|
|
|
|
|
+ if serr := s.doSerialize(ctx, chk, get); serr != nil {
|
|
|
|
|
+ return nil, serr
|
|
|
|
|
+ }
|
|
|
|
|
+ return resp, err
|
|
|
}
|
|
}
|
|
|
|
|
+ result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Txn: r})
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
@@ -551,6 +507,33 @@ func (s *EtcdServer) authInfoFromCtx(ctx context.Context) (*auth.AuthInfo, error
|
|
|
return authInfo, nil
|
|
return authInfo, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// doSerialize handles the auth logic, with permissions checked by "chk", for a serialized request "get". Returns a non-nil error on authentication failure.
|
|
|
|
|
+func (s *EtcdServer) doSerialize(ctx context.Context, chk func(*auth.AuthInfo) error, get func()) error {
|
|
|
|
|
+ for {
|
|
|
|
|
+ ai, err := s.authInfoFromCtx(ctx)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ if ai == nil {
|
|
|
|
|
+ // chk expects non-nil AuthInfo; use empty credentials
|
|
|
|
|
+ ai = &auth.AuthInfo{}
|
|
|
|
|
+ }
|
|
|
|
|
+ if err = chk(ai); err != nil {
|
|
|
|
|
+ if err == auth.ErrAuthOldRevision {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ // fetch response for serialized request
|
|
|
|
|
+ get()
|
|
|
|
|
+ // empty credentials or current auth info means no need to retry
|
|
|
|
|
+ if ai.Revision == 0 || ai.Revision == s.authStore.Revision() {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ // avoid TOCTOU error, retry of the request is required.
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func (s *EtcdServer) processInternalRaftRequestOnce(ctx context.Context, r pb.InternalRaftRequest) (*applyResult, error) {
|
|
func (s *EtcdServer) processInternalRaftRequestOnce(ctx context.Context, r pb.InternalRaftRequest) (*applyResult, error) {
|
|
|
ai := s.getAppliedIndex()
|
|
ai := s.getAppliedIndex()
|
|
|
ci := s.getCommittedIndex()
|
|
ci := s.getCommittedIndex()
|