package v2 import ( "crypto/ecdsa" "crypto/elliptic" "errors" "fmt" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" objectV2 "github.com/nspcc-dev/neofs-api-go/v2/object" refsV2 "github.com/nspcc-dev/neofs-api-go/v2/refs" sessionV2 "github.com/nspcc-dev/neofs-api-go/v2/session" containerIDSDK "github.com/nspcc-dev/neofs-sdk-go/container/id" eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl" oidSDK "github.com/nspcc-dev/neofs-sdk-go/object/id" "github.com/nspcc-dev/neofs-sdk-go/owner" sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/signature" bearerSDK "github.com/nspcc-dev/neofs-sdk-go/token" ) func getContainerIDFromRequest(req interface{}) (id *containerIDSDK.ID, err error) { switch v := req.(type) { case *objectV2.GetRequest: return containerIDSDK.NewFromV2(v.GetBody().GetAddress().GetContainerID()), nil case *objectV2.PutRequest: objPart := v.GetBody().GetObjectPart() if part, ok := objPart.(*objectV2.PutObjectPartInit); ok { return containerIDSDK.NewFromV2(part.GetHeader().GetContainerID()), nil } return nil, errors.New("can't get container ID in chunk") case *objectV2.HeadRequest: return containerIDSDK.NewFromV2(v.GetBody().GetAddress().GetContainerID()), nil case *objectV2.SearchRequest: return containerIDSDK.NewFromV2(v.GetBody().GetContainerID()), nil case *objectV2.DeleteRequest: return containerIDSDK.NewFromV2(v.GetBody().GetAddress().GetContainerID()), nil case *objectV2.GetRangeRequest: return containerIDSDK.NewFromV2(v.GetBody().GetAddress().GetContainerID()), nil case *objectV2.GetRangeHashRequest: return containerIDSDK.NewFromV2(v.GetBody().GetAddress().GetContainerID()), nil default: return nil, errors.New("unknown request type") } } // originalBearerToken goes down to original request meta header and fetches // bearer token from there. func originalBearerToken(header *sessionV2.RequestMetaHeader) *bearerSDK.BearerToken { for header.GetOrigin() != nil { header = header.GetOrigin() } return bearerSDK.NewBearerTokenFromV2(header.GetBearerToken()) } // originalSessionToken goes down to original request meta header and fetches // session token from there. func originalSessionToken(header *sessionV2.RequestMetaHeader) *sessionSDK.Token { for header.GetOrigin() != nil { header = header.GetOrigin() } return sessionSDK.NewTokenFromV2(header.GetSessionToken()) } func getObjectIDFromRequestBody(body interface{}) *oidSDK.ID { switch v := body.(type) { default: return nil case interface { GetObjectID() *refsV2.ObjectID }: return oidSDK.NewIDFromV2(v.GetObjectID()) case interface { GetAddress() *refsV2.Address }: return oidSDK.NewIDFromV2(v.GetAddress().GetObjectID()) } } func getObjectOwnerFromMessage(req interface{}) (id *owner.ID, err error) { switch v := req.(type) { case *objectV2.PutRequest: objPart := v.GetBody().GetObjectPart() if part, ok := objPart.(*objectV2.PutObjectPartInit); ok { return owner.NewIDFromV2(part.GetHeader().GetOwnerID()), nil } return nil, errors.New("can't get container ID in chunk") case *objectV2.GetResponse: objPart := v.GetBody().GetObjectPart() if part, ok := objPart.(*objectV2.GetObjectPartInit); ok { return owner.NewIDFromV2(part.GetHeader().GetOwnerID()), nil } return nil, errors.New("can't get container ID in chunk") default: return nil, errors.New("unsupported request type") } } // sourceVerbOfRequest looks for verb in session token and if it is not found, // returns reqVerb. Second return value is true if operation is unknown. func sourceVerbOfRequest(tok *sessionSDK.Token, reqVerb eaclSDK.Operation) (eaclSDK.Operation, bool) { ctx, ok := tok.Context().(*sessionSDK.ObjectContext) if ok { op := tokenVerbToOperation(ctx) if op != eaclSDK.OperationUnknown { return op, false } } return reqVerb, true } func useObjectIDFromSession(req *RequestInfo, token *sessionSDK.Token) { if token == nil { return } objCtx, ok := token.Context().(*sessionSDK.ObjectContext) if !ok { return } req.oid = objCtx.Address().ObjectID() } func tokenVerbToOperation(ctx *sessionSDK.ObjectContext) eaclSDK.Operation { switch { case ctx.IsForGet(): return eaclSDK.OperationGet case ctx.IsForPut(): return eaclSDK.OperationPut case ctx.IsForHead(): return eaclSDK.OperationHead case ctx.IsForSearch(): return eaclSDK.OperationSearch case ctx.IsForDelete(): return eaclSDK.OperationDelete case ctx.IsForRange(): return eaclSDK.OperationRange case ctx.IsForRangeHash(): return eaclSDK.OperationRangeHash default: return eaclSDK.OperationUnknown } } func ownerFromToken(token *sessionSDK.Token) (*owner.ID, *keys.PublicKey, error) { // 1. First check signature of session token. if !token.VerifySignature() { return nil, nil, fmt.Errorf("%w: invalid session token signature", ErrMalformedRequest) } // 2. Then check if session token owner issued the session token tokenIssuerKey := unmarshalPublicKey(token.Signature().Key()) tokenOwner := token.OwnerID() if !isOwnerFromKey(tokenOwner, tokenIssuerKey) { // TODO: #767 in this case we can issue all owner keys from neofs.id and check once again return nil, nil, fmt.Errorf("%w: invalid session token owner", ErrMalformedRequest) } return tokenOwner, tokenIssuerKey, nil } func originalBodySignature(v *sessionV2.RequestVerificationHeader) *signature.Signature { if v == nil { return nil } for v.GetOrigin() != nil { v = v.GetOrigin() } return signature.NewFromV2(v.GetBodySignature()) } func unmarshalPublicKey(bs []byte) *keys.PublicKey { pub, err := keys.NewPublicKeyFromBytes(bs, elliptic.P256()) if err != nil { return nil } return pub } func isOwnerFromKey(id *owner.ID, key *keys.PublicKey) bool { if id == nil || key == nil { return false } return id.Equal(owner.NewIDFromPublicKey((*ecdsa.PublicKey)(key))) } // isVerbCompatible checks that tokenVerb operation can create auxiliary op operation. func isVerbCompatible(tokenVerb, op eaclSDK.Operation) bool { switch tokenVerb { case eaclSDK.OperationGet: return op == eaclSDK.OperationGet || op == eaclSDK.OperationHead case eaclSDK.OperationDelete: return op == eaclSDK.OperationPut || op == eaclSDK.OperationHead || op == eaclSDK.OperationSearch case eaclSDK.OperationRange, eaclSDK.OperationRangeHash: return op == eaclSDK.OperationRange || op == eaclSDK.OperationHead default: return tokenVerb == op } }