@@ -79,6 +79,11 @@ type sessionTokenCommonCheckResult struct {
7979 err error
8080}
8181
82+ type sessionTokenV2CommonCheckResult struct {
83+ token session.TokenV2
84+ err error
85+ }
86+
8287// Server provides NeoFS API Container service.
8388type Server struct {
8489 protocontainer.UnimplementedContainerServiceServer
@@ -87,7 +92,8 @@ type Server struct {
8792 contract Contract
8893 historicN3ScriptRunner
8994
90- sessionTokenCommonCheckCache * lru.Cache [[sha256 .Size ]byte , sessionTokenCommonCheckResult ]
95+ sessionTokenCommonCheckCache * lru.Cache [[sha256 .Size ]byte , sessionTokenCommonCheckResult ]
96+ sessionTokenV2CommonCheckCache * lru.Cache [[sha256 .Size ]byte , sessionTokenV2CommonCheckResult ]
9197}
9298
9399// New provides protocontainer.ContainerServiceServer based on specified
@@ -100,6 +106,10 @@ func New(s *ecdsa.PrivateKey, net netmap.State, fsChain FSChain, c Contract, nc
100106 if err != nil {
101107 panic (fmt .Errorf ("unexpected error in lru.New: %w" , err ))
102108 }
109+ sessionTokenV2CheckCache , err := lru.New [[sha256 .Size ]byte , sessionTokenV2CommonCheckResult ](1000 )
110+ if err != nil {
111+ panic (fmt .Errorf ("unexpected error in lru.New for v2: %w" , err ))
112+ }
103113 return & Server {
104114 signer : s ,
105115 net : net ,
@@ -108,7 +118,8 @@ func New(s *ecdsa.PrivateKey, net netmap.State, fsChain FSChain, c Contract, nc
108118 FSChain : fsChain ,
109119 NetmapContract : nc ,
110120 },
111- sessionTokenCommonCheckCache : sessionTokenCheckCache ,
121+ sessionTokenCommonCheckCache : sessionTokenCheckCache ,
122+ sessionTokenV2CommonCheckCache : sessionTokenV2CheckCache ,
112123 }
113124}
114125
@@ -216,6 +227,101 @@ func (s *Server) checkSessionIssuer(id cid.ID, issuer user.ID) error {
216227 return nil
217228}
218229
230+ func (s * Server ) getVerifiedSessionTokenV2 (mh * protosession.RequestMetaHeader , reqVerb session.ContainerVerb , reqCnr cid.ID ) (* session.TokenV2 , error ) {
231+ for omh := mh .GetOrigin (); omh != nil ; omh = mh .GetOrigin () {
232+ mh = omh
233+ }
234+ m := mh .GetSessionTokenV2 ()
235+ if m == nil {
236+ return nil , nil
237+ }
238+
239+ b := make ([]byte , m .MarshaledSize ())
240+ m .MarshalStable (b )
241+
242+ cacheKey := sha256 .Sum256 (b )
243+ res , ok := s .sessionTokenV2CommonCheckCache .Get (cacheKey )
244+ if ! ok {
245+ res .token , res .err = s .decodeAndVerifySessionTokenV2Common (m )
246+ s .sessionTokenV2CommonCheckCache .Add (cacheKey , res )
247+ }
248+ if res .err != nil {
249+ return nil , res .err
250+ }
251+
252+ if err := s .verifySessionTokenV2AgainstRequest (res .token , reqVerb , reqCnr ); err != nil {
253+ return nil , err
254+ }
255+
256+ return & res .token , nil
257+ }
258+
259+ func (s * Server ) decodeAndVerifySessionTokenV2Common (m * protosession.SessionTokenV2 ) (session.TokenV2 , error ) {
260+ var token session.TokenV2
261+ if err := token .FromProtoMessage (m ); err != nil {
262+ return token , fmt .Errorf ("decode v2: %w" , err )
263+ }
264+
265+ if err := token .Validate (); err != nil {
266+ return token , fmt .Errorf ("invalid v2 token: %w" , err )
267+ }
268+
269+ if ! token .VerifySignature () {
270+ return token , errors .New ("v2 session token signature verification failed" )
271+ }
272+
273+ serverTarget := session .NewTarget (user .NewFromECDSAPublicKey (s .signer .PublicKey ))
274+ if ! token .AssertAuthority (serverTarget ) {
275+ return token , errors .New ("v2 token doesn't authorize this node" )
276+ }
277+
278+ cur := s .net .CurrentEpoch ()
279+ if ! token .ValidAt (cur ) {
280+ return token , fmt .Errorf ("v2 token is invalid at epoch %d" , cur )
281+ }
282+
283+ return token , nil
284+ }
285+
286+ func (s * Server ) verifySessionTokenV2AgainstRequest (token session.TokenV2 , reqVerb session.ContainerVerb , reqCnr cid.ID ) error {
287+ verbV2 := containerVerbToVerbV2 (reqVerb )
288+ if verbV2 == 0 {
289+ return fmt .Errorf ("unsupported container verb for V2: %v" , reqVerb )
290+ }
291+
292+ if ! token .AssertContainer (verbV2 , reqCnr ) {
293+ return errors .New ("v2 session token does not authorize this container operation" )
294+ }
295+
296+ if reqCnr .IsZero () {
297+ return nil
298+ }
299+
300+ issuer := token .Issuer ()
301+ if ! issuer .IsOwnerID () {
302+ return errors .New ("v2 session token issuer must be OwnerID for container operations" )
303+ }
304+
305+ if err := s .checkSessionIssuer (reqCnr , issuer .OwnerID ()); err != nil {
306+ return fmt .Errorf ("verify v2 session issuer: %w" , err )
307+ }
308+
309+ return nil
310+ }
311+
312+ func containerVerbToVerbV2 (verb session.ContainerVerb ) session.VerbV2 {
313+ switch verb {
314+ case session .VerbContainerPut :
315+ return session .VerbV2ContainerPut
316+ case session .VerbContainerDelete :
317+ return session .VerbV2ContainerDelete
318+ case session .VerbContainerSetEACL :
319+ return session .VerbV2ContainerSetEACL
320+ default :
321+ return session .VerbV2Unspecified
322+ }
323+ }
324+
219325func (s * Server ) makePutResponse (body * protocontainer.PutResponse_Body , st * protostatus.Status ) (* protocontainer.PutResponse , error ) {
220326 resp := & protocontainer.PutResponse {
221327 Body : body ,
@@ -307,9 +413,17 @@ func (s *Server) Put(_ context.Context, req *protocontainer.PutRequest) (*protoc
307413 return s .makeFailedPutResponse (fmt .Errorf ("invalid container: %w" , err ))
308414 }
309415
310- st , err := s .getVerifiedSessionToken (req .GetMetaHeader (), session .VerbContainerPut , cid.ID {})
416+ stV2 , err := s .getVerifiedSessionTokenV2 (req .GetMetaHeader (), session .VerbContainerPut , cid.ID {})
311417 if err != nil {
312- return s .makeFailedPutResponse (fmt .Errorf ("verify session token: %w" , err ))
418+ return s .makeFailedPutResponse (fmt .Errorf ("verify session token v2: %w" , err ))
419+ }
420+
421+ var st * session.Container
422+ if stV2 == nil {
423+ st , err = s .getVerifiedSessionToken (req .GetMetaHeader (), session .VerbContainerPut , cid.ID {})
424+ if err != nil {
425+ return s .makeFailedPutResponse (fmt .Errorf ("verify session token: %w" , err ))
426+ }
313427 }
314428
315429 id , err := s .contract .Put (cnr , mSig .Key , mSig .Sign , st )
@@ -353,9 +467,17 @@ func (s *Server) Delete(_ context.Context, req *protocontainer.DeleteRequest) (*
353467 return s .makeDeleteResponse (fmt .Errorf ("invalid ID: %w" , err ))
354468 }
355469
356- st , err := s .getVerifiedSessionToken (req .GetMetaHeader (), session .VerbContainerDelete , id )
470+ stV2 , err := s .getVerifiedSessionTokenV2 (req .GetMetaHeader (), session .VerbContainerDelete , id )
357471 if err != nil {
358- return s .makeDeleteResponse (fmt .Errorf ("verify session token: %w" , err ))
472+ return s .makeDeleteResponse (fmt .Errorf ("verify session token v2: %w" , err ))
473+ }
474+
475+ var st * session.Container
476+ if stV2 == nil {
477+ st , err = s .getVerifiedSessionToken (req .GetMetaHeader (), session .VerbContainerDelete , id )
478+ if err != nil {
479+ return s .makeDeleteResponse (fmt .Errorf ("verify session token: %w" , err ))
480+ }
359481 }
360482
361483 if err := s .contract .Delete (id , mSig .Key , mSig .Sign , st ); err != nil {
@@ -491,9 +613,17 @@ func (s *Server) SetExtendedACL(_ context.Context, req *protocontainer.SetExtend
491613 return s .makeSetEACLResponse (errors .New ("missing container ID in eACL table" ))
492614 }
493615
494- st , err := s .getVerifiedSessionToken (req .GetMetaHeader (), session .VerbContainerSetEACL , cnrID )
616+ stV2 , err := s .getVerifiedSessionTokenV2 (req .GetMetaHeader (), session .VerbContainerSetEACL , cnrID )
495617 if err != nil {
496- return s .makeSetEACLResponse (fmt .Errorf ("verify session token: %w" , err ))
618+ return s .makeSetEACLResponse (fmt .Errorf ("verify session token v2: %w" , err ))
619+ }
620+
621+ var st * session.Container
622+ if stV2 == nil {
623+ st , err = s .getVerifiedSessionToken (req .GetMetaHeader (), session .VerbContainerSetEACL , cnrID )
624+ if err != nil {
625+ return s .makeSetEACLResponse (fmt .Errorf ("verify session token: %w" , err ))
626+ }
497627 }
498628
499629 if err := s .contract .PutEACL (eACL , mSig .Key , mSig .Sign , st ); err != nil {
0 commit comments