2022-02-11 12:25:05 +00:00
|
|
|
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"
|
2022-05-12 07:22:02 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
2022-02-11 12:25:05 +00:00
|
|
|
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"
|
|
|
|
sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session"
|
2022-05-17 13:59:46 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/user"
|
2022-02-11 12:25:05 +00:00
|
|
|
)
|
|
|
|
|
2022-05-12 16:37:46 +00:00
|
|
|
var errMissingContainerID = errors.New("missing container ID")
|
|
|
|
|
|
|
|
func getContainerIDFromRequest(req interface{}) (*containerIDSDK.ID, error) {
|
|
|
|
var idV2 *refsV2.ContainerID
|
|
|
|
id := new(containerIDSDK.ID)
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
switch v := req.(type) {
|
|
|
|
case *objectV2.GetRequest:
|
2022-05-12 16:37:46 +00:00
|
|
|
idV2 = v.GetBody().GetAddress().GetContainerID()
|
2022-02-11 12:25:05 +00:00
|
|
|
case *objectV2.PutRequest:
|
2022-05-12 16:37:46 +00:00
|
|
|
part, ok := v.GetBody().GetObjectPart().(*objectV2.PutObjectPartInit)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("can't get container ID in chunk")
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
2022-05-12 16:37:46 +00:00
|
|
|
idV2 = part.GetHeader().GetContainerID()
|
2022-02-11 12:25:05 +00:00
|
|
|
case *objectV2.HeadRequest:
|
2022-05-12 16:37:46 +00:00
|
|
|
idV2 = v.GetBody().GetAddress().GetContainerID()
|
2022-02-11 12:25:05 +00:00
|
|
|
case *objectV2.SearchRequest:
|
2022-05-12 16:37:46 +00:00
|
|
|
idV2 = v.GetBody().GetContainerID()
|
2022-02-11 12:25:05 +00:00
|
|
|
case *objectV2.DeleteRequest:
|
2022-05-12 16:37:46 +00:00
|
|
|
idV2 = v.GetBody().GetAddress().GetContainerID()
|
2022-02-11 12:25:05 +00:00
|
|
|
case *objectV2.GetRangeRequest:
|
2022-05-12 16:37:46 +00:00
|
|
|
idV2 = v.GetBody().GetAddress().GetContainerID()
|
2022-02-11 12:25:05 +00:00
|
|
|
case *objectV2.GetRangeHashRequest:
|
2022-05-12 16:37:46 +00:00
|
|
|
idV2 = v.GetBody().GetAddress().GetContainerID()
|
2022-02-11 12:25:05 +00:00
|
|
|
default:
|
|
|
|
return nil, errors.New("unknown request type")
|
|
|
|
}
|
2022-05-12 16:37:46 +00:00
|
|
|
|
|
|
|
if idV2 == nil {
|
|
|
|
return nil, errMissingContainerID
|
|
|
|
}
|
|
|
|
|
|
|
|
return id, id.ReadFromV2(*idV2)
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// originalBearerToken goes down to original request meta header and fetches
|
|
|
|
// bearer token from there.
|
2022-05-12 07:22:02 +00:00
|
|
|
func originalBearerToken(header *sessionV2.RequestMetaHeader) *bearer.Token {
|
2022-02-11 12:25:05 +00:00
|
|
|
for header.GetOrigin() != nil {
|
|
|
|
header = header.GetOrigin()
|
|
|
|
}
|
|
|
|
|
2022-05-12 07:22:02 +00:00
|
|
|
tokV2 := header.GetBearerToken()
|
|
|
|
if tokV2 == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var tok bearer.Token
|
|
|
|
tok.ReadFromV2(*tokV2)
|
|
|
|
|
|
|
|
return &tok
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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())
|
|
|
|
}
|
|
|
|
|
2022-05-12 16:37:46 +00:00
|
|
|
func getObjectIDFromRequestBody(body interface{}) (*oidSDK.ID, error) {
|
|
|
|
var idV2 *refsV2.ObjectID
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
switch v := body.(type) {
|
|
|
|
default:
|
2022-05-12 16:37:46 +00:00
|
|
|
return nil, nil
|
2022-02-11 12:25:05 +00:00
|
|
|
case interface {
|
|
|
|
GetObjectID() *refsV2.ObjectID
|
|
|
|
}:
|
2022-05-12 16:37:46 +00:00
|
|
|
idV2 = v.GetObjectID()
|
2022-02-11 12:25:05 +00:00
|
|
|
case interface {
|
|
|
|
GetAddress() *refsV2.Address
|
|
|
|
}:
|
2022-05-12 16:37:46 +00:00
|
|
|
idV2 = v.GetAddress().GetObjectID()
|
|
|
|
}
|
|
|
|
|
|
|
|
if idV2 == nil {
|
|
|
|
return nil, nil
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
2022-05-12 16:37:46 +00:00
|
|
|
|
|
|
|
var id oidSDK.ID
|
|
|
|
|
|
|
|
err := id.ReadFromV2(*idV2)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &id, nil
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// sourceVerbOfRequest looks for verb in session token and if it is not found,
|
2022-02-28 12:35:10 +00:00
|
|
|
// 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
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-28 12:35:10 +00:00
|
|
|
return reqVerb, true
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func useObjectIDFromSession(req *RequestInfo, token *sessionSDK.Token) {
|
|
|
|
if token == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
objCtx, ok := token.Context().(*sessionSDK.ObjectContext)
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-05-12 16:37:46 +00:00
|
|
|
id, ok := objCtx.Address().ObjectID()
|
|
|
|
if ok {
|
|
|
|
req.oid = &id
|
|
|
|
}
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-17 13:59:46 +00:00
|
|
|
func ownerFromToken(token *sessionSDK.Token) (*user.ID, *keys.PublicKey, error) {
|
2022-02-11 12:25:05 +00:00
|
|
|
// 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
|
2022-05-16 13:15:31 +00:00
|
|
|
// TODO(@cthulhu-rider): #1387 implement and use another approach to avoid conversion
|
|
|
|
tokV2 := token.ToV2()
|
|
|
|
|
2022-05-17 13:59:46 +00:00
|
|
|
tokenIssuerKey, err := unmarshalPublicKey(tokV2.GetSignature().GetKey())
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("invalid key in session token signature: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-05-16 13:15:31 +00:00
|
|
|
func originalBodySignature(v *sessionV2.RequestVerificationHeader) *refsV2.Signature {
|
2022-02-11 12:25:05 +00:00
|
|
|
if v == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for v.GetOrigin() != nil {
|
|
|
|
v = v.GetOrigin()
|
|
|
|
}
|
|
|
|
|
2022-05-16 13:15:31 +00:00
|
|
|
return v.GetBodySignature()
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
2022-05-17 13:59:46 +00:00
|
|
|
func unmarshalPublicKey(bs []byte) (*keys.PublicKey, error) {
|
|
|
|
return keys.NewPublicKeyFromBytes(bs, elliptic.P256())
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
2022-05-17 13:59:46 +00:00
|
|
|
func isOwnerFromKey(id *user.ID, key *keys.PublicKey) bool {
|
2022-02-11 12:25:05 +00:00
|
|
|
if id == nil || key == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-05-17 13:59:46 +00:00
|
|
|
var id2 user.ID
|
|
|
|
user.IDFromKey(&id2, (ecdsa.PublicKey)(*key))
|
|
|
|
|
|
|
|
return id2.Equals(*id)
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
2022-02-28 12:35:10 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|