[#1423] session: Upgrade SDK package

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2022-05-18 18:20:08 +03:00 committed by LeL
parent dda56f1319
commit 4c8ec20e32
41 changed files with 740 additions and 663 deletions

View file

@ -84,6 +84,16 @@ func (h headerSource) HeadersOfType(typ eaclSDK.FilterHeaderType) ([]eaclSDK.Hea
}
}
type xHeader session.XHeader
func (x xHeader) Key() string {
return (*session.XHeader)(&x).GetKey()
}
func (x xHeader) Value() string {
return (*session.XHeader)(&x).GetValue()
}
func requestHeaders(msg xHeaderSource) []eaclSDK.Header {
return msg.GetXHeaders()
}

View file

@ -1,8 +1,8 @@
package v2
import (
"github.com/nspcc-dev/neofs-api-go/v2/session"
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session"
)
type xHeaderSource interface {
@ -30,7 +30,7 @@ func (s requestXHeaderSource) GetXHeaders() []eaclSDK.Header {
for meta := s.req.GetMetaHeader(); meta != nil; meta = meta.GetOrigin() {
x := meta.GetXHeaders()
for i := range x {
res = append(res, sessionSDK.NewXHeaderFromV2(&x[i]))
res = append(res, (xHeader)(x[i]))
}
}
@ -39,16 +39,21 @@ func (s requestXHeaderSource) GetXHeaders() []eaclSDK.Header {
func (s responseXHeaderSource) GetXHeaders() []eaclSDK.Header {
ln := 0
xHdrs := make([][]session.XHeader, 0)
for meta := s.req.GetMetaHeader(); meta != nil; meta = meta.GetOrigin() {
ln += len(meta.GetXHeaders())
x := meta.GetXHeaders()
ln += len(x)
xHdrs = append(xHdrs, x)
}
res := make([]eaclSDK.Header, 0, ln)
for meta := s.req.GetMetaHeader(); meta != nil; meta = meta.GetOrigin() {
x := meta.GetXHeaders()
for i := range x {
res = append(res, sessionSDK.NewXHeaderFromV2(&x[i]))
for i := range xHdrs {
for j := range xHdrs[i] {
res = append(res, xHeader(xHdrs[i][j]))
}
}

View file

@ -107,7 +107,7 @@ func (r RequestInfo) RequestRole() eaclSDK.Role {
// verification header and raw API request.
type MetaWithToken struct {
vheader *sessionV2.RequestVerificationHeader
token *sessionSDK.Token
token *sessionSDK.Object
bearer *bearer.Token
src interface{}
}

View file

@ -113,7 +113,10 @@ func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream
return err
}
sTok := originalSessionToken(request.GetMetaHeader())
sTok, err := originalSessionToken(request.GetMetaHeader())
if err != nil {
return err
}
req := MetaWithToken{
vheader: request.GetVerificationHeader(),
@ -164,7 +167,10 @@ func (b Service) Head(
return nil, err
}
sTok := originalSessionToken(request.GetMetaHeader())
sTok, err := originalSessionToken(request.GetMetaHeader())
if err != nil {
return nil, err
}
req := MetaWithToken{
vheader: request.GetVerificationHeader(),
@ -207,9 +213,14 @@ func (b Service) Search(request *objectV2.SearchRequest, stream object.SearchStr
return err
}
sTok, err := originalSessionToken(request.GetMetaHeader())
if err != nil {
return err
}
req := MetaWithToken{
vheader: request.GetVerificationHeader(),
token: originalSessionToken(request.GetMetaHeader()),
token: sTok,
bearer: originalBearerToken(request.GetMetaHeader()),
src: request,
}
@ -245,7 +256,10 @@ func (b Service) Delete(
return nil, err
}
sTok := originalSessionToken(request.GetMetaHeader())
sTok, err := originalSessionToken(request.GetMetaHeader())
if err != nil {
return nil, err
}
req := MetaWithToken{
vheader: request.GetVerificationHeader(),
@ -281,7 +295,10 @@ func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetOb
return err
}
sTok := originalSessionToken(request.GetMetaHeader())
sTok, err := originalSessionToken(request.GetMetaHeader())
if err != nil {
return err
}
req := MetaWithToken{
vheader: request.GetVerificationHeader(),
@ -322,7 +339,10 @@ func (b Service) GetRangeHash(
return nil, err
}
sTok := originalSessionToken(request.GetMetaHeader())
sTok, err := originalSessionToken(request.GetMetaHeader())
if err != nil {
return nil, err
}
req := MetaWithToken{
vheader: request.GetVerificationHeader(),
@ -377,7 +397,16 @@ func (p putStreamBasicChecker) Send(request *objectV2.PutRequest) error {
return fmt.Errorf("invalid object owner: %w", err)
}
sTok := sessionSDK.NewTokenFromV2(request.GetMetaHeader().GetSessionToken())
var sTok *sessionSDK.Object
if tokV2 := request.GetMetaHeader().GetSessionToken(); tokV2 != nil {
sTok = new(sessionSDK.Object)
err = sTok.ReadFromV2(*tokV2)
if err != nil {
return fmt.Errorf("invalid session token: %w", err)
}
}
req := MetaWithToken{
vheader: request.GetVerificationHeader(),
@ -449,14 +478,18 @@ func (b Service) findRequestInfo(
return info, errors.New("missing owner in container descriptor")
}
if req.token != nil && req.token.Exp() != 0 {
if req.token != nil {
currentEpoch, err := b.nm.Epoch()
if err != nil {
return info, errors.New("can't fetch current epoch")
}
if req.token.Exp() < currentEpoch {
return info, fmt.Errorf("%w: token has expired (current epoch: %d, expired at %d)",
ErrMalformedRequest, currentEpoch, req.token.Exp())
if req.token.ExpiredAt(currentEpoch) {
return info, fmt.Errorf("%w: token has expired (current epoch: %d)",
ErrMalformedRequest, currentEpoch)
}
if !assertVerb(*req.token, op) {
return info, ErrInvalidVerb
}
}
@ -470,16 +503,10 @@ func (b Service) findRequestInfo(
return info, ErrUnknownRole
}
// find verb from token if it is present
verb, isUnknown := sourceVerbOfRequest(req.token, op)
if !isUnknown && verb != op && !isVerbCompatible(verb, op) {
return info, ErrInvalidVerb
}
info.basicACL = cnr.BasicACL()
info.requestRole = res.role
info.isInnerRing = res.isIR
info.operation = verb
info.operation = op
info.cnrOwner = cnr.OwnerID()
info.idCnr = cid

View file

@ -75,12 +75,24 @@ func originalBearerToken(header *sessionV2.RequestMetaHeader) *bearer.Token {
// originalSessionToken goes down to original request meta header and fetches
// session token from there.
func originalSessionToken(header *sessionV2.RequestMetaHeader) *sessionSDK.Token {
func originalSessionToken(header *sessionV2.RequestMetaHeader) (*sessionSDK.Object, error) {
for header.GetOrigin() != nil {
header = header.GetOrigin()
}
return sessionSDK.NewTokenFromV2(header.GetSessionToken())
tokV2 := header.GetSessionToken()
if tokV2 == nil {
return nil, nil
}
var tok sessionSDK.Object
err := tok.ReadFromV2(*tokV2)
if err != nil {
return nil, fmt.Errorf("invalid session token: %w", err)
}
return &tok, nil
}
func getObjectIDFromRequestBody(body interface{}) (*oidSDK.ID, error) {
@ -113,58 +125,35 @@ func getObjectIDFromRequestBody(body interface{}) (*oidSDK.ID, error) {
return &id, nil
}
// 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) {
func useObjectIDFromSession(req *RequestInfo, token *sessionSDK.Object) {
if token == nil {
return
}
objCtx, ok := token.Context().(*sessionSDK.ObjectContext)
// TODO(@cthulhu-rider): It'd be nice to not pull object identifiers from
// the token, but assert them. Track #1420
var tokV2 sessionV2.Token
token.WriteToV2(&tokV2)
ctx, ok := tokV2.GetBody().GetContext().(*sessionV2.ObjectSessionContext)
if !ok {
panic(fmt.Sprintf("wrong object session context %T, is it verified?", tokV2.GetBody().GetContext()))
}
idV2 := ctx.GetAddress().GetObjectID()
if idV2 == nil {
return
}
id, ok := objCtx.Address().ObjectID()
if ok {
req.oid = &id
req.oid = new(oidSDK.ID)
err := req.oid.ReadFromV2(*idV2)
if err != nil {
panic(fmt.Sprintf("unexpected protocol violation error after correct session token decoding: %v", err))
}
}
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) (*user.ID, *keys.PublicKey, error) {
func ownerFromToken(token *sessionSDK.Object) (*user.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)
@ -172,21 +161,32 @@ func ownerFromToken(token *sessionSDK.Token) (*user.ID, *keys.PublicKey, error)
// 2. Then check if session token owner issued the session token
// TODO(@cthulhu-rider): #1387 implement and use another approach to avoid conversion
tokV2 := token.ToV2()
var tokV2 sessionV2.Token
token.WriteToV2(&tokV2)
ownerSessionV2 := tokV2.GetBody().GetOwnerID()
if ownerSessionV2 == nil {
return nil, nil, errors.New("missing session owner")
}
var ownerSession user.ID
err := ownerSession.ReadFromV2(*ownerSessionV2)
if err != nil {
return nil, nil, fmt.Errorf("invalid session token: %w", err)
}
tokenIssuerKey, err := unmarshalPublicKey(tokV2.GetSignature().GetKey())
if err != nil {
return nil, nil, fmt.Errorf("invalid key in session token signature: %w", err)
}
tokenOwner := token.OwnerID()
if !isOwnerFromKey(tokenOwner, tokenIssuerKey) {
if !isOwnerFromKey(&ownerSession, 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
return &ownerSession, tokenIssuerKey, nil
}
func originalBodySignature(v *sessionV2.RequestVerificationHeader) *refsV2.Signature {
@ -216,17 +216,30 @@ func isOwnerFromKey(id *user.ID, key *keys.PublicKey) bool {
return id2.Equals(*id)
}
// 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
// assertVerb checks that token verb corresponds to op.
func assertVerb(tok sessionSDK.Object, op eaclSDK.Operation) bool {
//nolint:exhaustive
switch op {
case eaclSDK.OperationPut:
return tok.AssertVerb(sessionSDK.VerbObjectPut, sessionSDK.VerbObjectDelete)
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
return tok.AssertVerb(sessionSDK.VerbObjectDelete)
case eaclSDK.OperationGet:
return tok.AssertVerb(sessionSDK.VerbObjectGet)
case eaclSDK.OperationHead:
return tok.AssertVerb(
sessionSDK.VerbObjectHead,
sessionSDK.VerbObjectGet,
sessionSDK.VerbObjectDelete,
sessionSDK.VerbObjectRange,
sessionSDK.VerbObjectRangeHash)
case eaclSDK.OperationSearch:
return tok.AssertVerb(sessionSDK.VerbObjectSearch, sessionSDK.VerbObjectDelete)
case eaclSDK.OperationRange:
return tok.AssertVerb(sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRangeHash)
case eaclSDK.OperationRangeHash:
return tok.AssertVerb(sessionSDK.VerbObjectRangeHash)
}
return false
}

View file

@ -6,23 +6,28 @@ import (
"github.com/nspcc-dev/neofs-api-go/v2/acl"
acltest "github.com/nspcc-dev/neofs-api-go/v2/acl/test"
"github.com/nspcc-dev/neofs-api-go/v2/session"
sessiontest "github.com/nspcc-dev/neofs-api-go/v2/session/test"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session"
sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test"
"github.com/stretchr/testify/require"
)
func TestOriginalTokens(t *testing.T) {
sToken := sessiontest.GenerateSessionToken(false)
sToken := sessiontest.ObjectSigned()
bTokenV2 := acltest.GenerateBearerToken(false)
var bToken bearer.Token
bToken.ReadFromV2(*bTokenV2)
var sTokenV2 session.Token
sToken.WriteToV2(&sTokenV2)
for i := 0; i < 10; i++ {
metaHeaders := testGenerateMetaHeader(uint32(i), bTokenV2, sToken)
require.Equal(t, sessionSDK.NewTokenFromV2(sToken), originalSessionToken(metaHeaders), i)
metaHeaders := testGenerateMetaHeader(uint32(i), bTokenV2, &sTokenV2)
res, err := originalSessionToken(metaHeaders)
require.NoError(t, err)
require.Equal(t, sToken, res, i)
require.Equal(t, &bToken, originalBearerToken(metaHeaders), i)
}
}
@ -43,38 +48,48 @@ func testGenerateMetaHeader(depth uint32, b *acl.BearerToken, s *session.Token)
func TestIsVerbCompatible(t *testing.T) {
// Source: https://nspcc.ru/upload/neofs-spec-latest.pdf#page=28
table := map[eacl.Operation][]eacl.Operation{
eacl.OperationPut: {eacl.OperationPut},
eacl.OperationDelete: {eacl.OperationPut, eacl.OperationHead, eacl.OperationSearch},
eacl.OperationHead: {eacl.OperationHead},
eacl.OperationRange: {eacl.OperationRange, eacl.OperationHead},
eacl.OperationRangeHash: {eacl.OperationRange, eacl.OperationHead},
eacl.OperationGet: {eacl.OperationGet, eacl.OperationHead},
eacl.OperationSearch: {eacl.OperationSearch},
table := map[eacl.Operation][]sessionSDK.ObjectVerb{
eacl.OperationPut: {sessionSDK.VerbObjectPut, sessionSDK.VerbObjectDelete},
eacl.OperationDelete: {sessionSDK.VerbObjectDelete},
eacl.OperationGet: {sessionSDK.VerbObjectGet},
eacl.OperationHead: {
sessionSDK.VerbObjectHead,
sessionSDK.VerbObjectGet,
sessionSDK.VerbObjectDelete,
sessionSDK.VerbObjectRange,
sessionSDK.VerbObjectRangeHash,
},
eacl.OperationRange: {sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRangeHash},
eacl.OperationRangeHash: {sessionSDK.VerbObjectRangeHash},
eacl.OperationSearch: {sessionSDK.VerbObjectSearch, sessionSDK.VerbObjectDelete},
}
ops := []eacl.Operation{
eacl.OperationPut,
eacl.OperationDelete,
eacl.OperationHead,
eacl.OperationRange,
eacl.OperationRangeHash,
eacl.OperationGet,
eacl.OperationSearch,
verbs := []sessionSDK.ObjectVerb{
sessionSDK.VerbObjectPut,
sessionSDK.VerbObjectDelete,
sessionSDK.VerbObjectHead,
sessionSDK.VerbObjectRange,
sessionSDK.VerbObjectRangeHash,
sessionSDK.VerbObjectGet,
sessionSDK.VerbObjectSearch,
}
for _, opToken := range ops {
for _, op := range ops {
var tok sessionSDK.Object
for op, list := range table {
for _, verb := range verbs {
var contains bool
for _, o := range table[opToken] {
if o == op {
for _, v := range list {
if v == verb {
contains = true
break
}
}
require.Equal(t, contains, isVerbCompatible(opToken, op),
"%s in token, %s executing", opToken, op)
tok.ForVerb(verb)
require.Equal(t, contains, assertVerb(tok, op),
"%v in token, %s executing", verb, op)
}
}
}