git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/session/object_test.go (about)

     1  package session_test
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/ecdsa"
     6  	"fmt"
     7  	"math"
     8  	"math/rand"
     9  	"testing"
    10  
    11  	"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
    12  	v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
    13  	cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
    14  	frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
    15  	frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
    16  	oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
    17  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
    18  	sessiontest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session/test"
    19  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
    20  	usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
    21  	"github.com/google/uuid"
    22  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  func randSigner() ecdsa.PrivateKey {
    27  	k, err := keys.NewPrivateKey()
    28  	if err != nil {
    29  		panic(fmt.Sprintf("generate private key: %v", err))
    30  	}
    31  
    32  	return k.PrivateKey
    33  }
    34  
    35  func randPublicKey() frostfscrypto.PublicKey {
    36  	k := randSigner().PublicKey
    37  	return (*frostfsecdsa.PublicKey)(&k)
    38  }
    39  
    40  func TestObjectProtocolV2(t *testing.T) {
    41  	var validV2 v2session.Token
    42  
    43  	var body v2session.TokenBody
    44  	validV2.SetBody(&body)
    45  
    46  	// ID
    47  	id := uuid.New()
    48  	binID, err := id.MarshalBinary()
    49  	require.NoError(t, err)
    50  	restoreID := func() {
    51  		body.SetID(binID)
    52  	}
    53  	restoreID()
    54  
    55  	// Owner
    56  	usr := usertest.ID()
    57  	var usrV2 refs.OwnerID
    58  	usr.WriteToV2(&usrV2)
    59  	restoreUser := func() {
    60  		body.SetOwnerID(&usrV2)
    61  	}
    62  	restoreUser()
    63  
    64  	// Lifetime
    65  	var lifetime v2session.TokenLifetime
    66  	lifetime.SetIat(1)
    67  	lifetime.SetNbf(2)
    68  	lifetime.SetExp(3)
    69  	restoreLifetime := func() {
    70  		body.SetLifetime(&lifetime)
    71  	}
    72  	restoreLifetime()
    73  
    74  	// Session key
    75  	signer := randSigner()
    76  	authKey := frostfsecdsa.PublicKey(signer.PublicKey)
    77  	binAuthKey := make([]byte, authKey.MaxEncodedSize())
    78  	binAuthKey = binAuthKey[:authKey.Encode(binAuthKey)]
    79  	restoreAuthKey := func() {
    80  		body.SetSessionKey(binAuthKey)
    81  	}
    82  	restoreAuthKey()
    83  
    84  	// Context
    85  	cnr := cidtest.ID()
    86  	obj1 := oidtest.ID()
    87  	obj2 := oidtest.ID()
    88  	var cnrV2 refs.ContainerID
    89  	cnr.WriteToV2(&cnrV2)
    90  	var obj1V2 refs.ObjectID
    91  	obj1.WriteToV2(&obj1V2)
    92  	var obj2V2 refs.ObjectID
    93  	obj2.WriteToV2(&obj2V2)
    94  	var cObj v2session.ObjectSessionContext
    95  	restoreCtx := func() {
    96  		cObj.SetTarget(&cnrV2, obj1V2, obj2V2)
    97  		body.SetContext(&cObj)
    98  	}
    99  	restoreCtx()
   100  
   101  	// Signature
   102  	var sig refs.Signature
   103  	restoreSig := func() {
   104  		validV2.SetSignature(&sig)
   105  	}
   106  	restoreSig()
   107  
   108  	// TODO(@cthulhu-rider): #260 use functionality for message corruption
   109  
   110  	for _, testcase := range []struct {
   111  		name      string
   112  		corrupt   []func()
   113  		restore   func()
   114  		assert    func(session.Object)
   115  		breakSign func(*v2session.Token)
   116  	}{
   117  		{
   118  			name: "Signature",
   119  			corrupt: []func(){
   120  				func() {
   121  					validV2.SetSignature(nil)
   122  				},
   123  			},
   124  			restore: restoreSig,
   125  		},
   126  		{
   127  			name: "ID",
   128  			corrupt: []func(){
   129  				func() {
   130  					body.SetID([]byte{1, 2, 3})
   131  				},
   132  				func() {
   133  					id, err := uuid.NewDCEPerson()
   134  					require.NoError(t, err)
   135  					bindID, err := id.MarshalBinary()
   136  					require.NoError(t, err)
   137  					body.SetID(bindID)
   138  				},
   139  			},
   140  			restore: restoreID,
   141  			assert: func(val session.Object) {
   142  				require.Equal(t, id, val.ID())
   143  			},
   144  			breakSign: func(m *v2session.Token) {
   145  				id := m.GetBody().GetID()
   146  				id[len(id)-1]++
   147  			},
   148  		},
   149  		{
   150  			name: "User",
   151  			corrupt: []func(){
   152  				func() {
   153  					var brokenUsrV2 refs.OwnerID
   154  					brokenUsrV2.SetValue(append(usrV2.GetValue(), 1))
   155  					body.SetOwnerID(&brokenUsrV2)
   156  				},
   157  			},
   158  			restore: restoreUser,
   159  			assert: func(val session.Object) {
   160  				require.Equal(t, usr, val.Issuer())
   161  			},
   162  			breakSign: func(m *v2session.Token) {
   163  				id := m.GetBody().GetOwnerID().GetValue()
   164  				copy(id, usertest.ID().WalletBytes())
   165  			},
   166  		},
   167  		{
   168  			name: "Lifetime",
   169  			corrupt: []func(){
   170  				func() {
   171  					body.SetLifetime(nil)
   172  				},
   173  			},
   174  			restore: restoreLifetime,
   175  			assert: func(val session.Object) {
   176  				require.True(t, val.InvalidAt(1))
   177  				require.False(t, val.InvalidAt(2))
   178  				require.False(t, val.InvalidAt(3))
   179  				require.True(t, val.InvalidAt(4))
   180  			},
   181  			breakSign: func(m *v2session.Token) {
   182  				lt := m.GetBody().GetLifetime()
   183  				lt.SetIat(lt.GetIat() + 1)
   184  			},
   185  		},
   186  		{
   187  			name: "Auth key",
   188  			corrupt: []func(){
   189  				func() {
   190  					body.SetSessionKey(nil)
   191  				},
   192  				func() {
   193  					body.SetSessionKey([]byte{})
   194  				},
   195  			},
   196  			restore: restoreAuthKey,
   197  			assert: func(val session.Object) {
   198  				require.True(t, val.AssertAuthKey(&authKey))
   199  			},
   200  			breakSign: func(m *v2session.Token) {
   201  				body := m.GetBody()
   202  				key := body.GetSessionKey()
   203  				cp := bytes.Clone(key)
   204  				cp[len(cp)-1]++
   205  				body.SetSessionKey(cp)
   206  			},
   207  		},
   208  		{
   209  			name: "Context",
   210  			corrupt: []func(){
   211  				func() {
   212  					body.SetContext(nil)
   213  				},
   214  				func() {
   215  					cObj.SetTarget(nil)
   216  				},
   217  				func() {
   218  					var brokenCnr refs.ContainerID
   219  					brokenCnr.SetValue(append(cnrV2.GetValue(), 1))
   220  					cObj.SetTarget(&brokenCnr)
   221  				},
   222  				func() {
   223  					var brokenObj refs.ObjectID
   224  					brokenObj.SetValue(append(obj1V2.GetValue(), 1))
   225  					cObj.SetTarget(&cnrV2, brokenObj)
   226  				},
   227  			},
   228  			restore: restoreCtx,
   229  			assert: func(val session.Object) {
   230  				require.True(t, val.AssertContainer(cnr))
   231  				require.False(t, val.AssertContainer(cidtest.ID()))
   232  				require.True(t, val.AssertObject(obj1))
   233  				require.True(t, val.AssertObject(obj2))
   234  				require.False(t, val.AssertObject(oidtest.ID()))
   235  			},
   236  			breakSign: func(m *v2session.Token) {
   237  				cnr := m.GetBody().GetContext().(*v2session.ObjectSessionContext).GetContainer().GetValue()
   238  				cnr[len(cnr)-1]++
   239  			},
   240  		},
   241  	} {
   242  		var val session.Object
   243  
   244  		for i, corrupt := range testcase.corrupt {
   245  			corrupt()
   246  			require.Error(t, val.ReadFromV2(validV2), testcase.name, fmt.Sprintf("corrupt #%d", i))
   247  
   248  			testcase.restore()
   249  			require.NoError(t, val.ReadFromV2(validV2), testcase.name, fmt.Sprintf("corrupt #%d", i))
   250  
   251  			if testcase.assert != nil {
   252  				testcase.assert(val)
   253  			}
   254  
   255  			if testcase.breakSign != nil {
   256  				require.NoError(t, val.Sign(signer), testcase.name)
   257  				require.True(t, val.VerifySignature(), testcase.name)
   258  
   259  				var signedV2 v2session.Token
   260  				val.WriteToV2(&signedV2)
   261  
   262  				var restored session.Object
   263  				require.NoError(t, restored.ReadFromV2(signedV2), testcase.name)
   264  				require.True(t, restored.VerifySignature(), testcase.name)
   265  
   266  				testcase.breakSign(&signedV2)
   267  
   268  				require.NoError(t, restored.ReadFromV2(signedV2), testcase.name)
   269  				require.False(t, restored.VerifySignature(), testcase.name)
   270  			}
   271  		}
   272  	}
   273  }
   274  
   275  func TestObject_WriteToV2(t *testing.T) {
   276  	var val session.Object
   277  
   278  	assert := func(baseAssert func(v2session.Token)) {
   279  		var m v2session.Token
   280  		val.WriteToV2(&m)
   281  		baseAssert(m)
   282  	}
   283  
   284  	// ID
   285  	id := uuid.New()
   286  
   287  	binID, err := id.MarshalBinary()
   288  	require.NoError(t, err)
   289  
   290  	val.SetID(id)
   291  	assert(func(m v2session.Token) {
   292  		require.Equal(t, binID, m.GetBody().GetID())
   293  	})
   294  
   295  	// Owner/Signature
   296  	signer := randSigner()
   297  
   298  	require.NoError(t, val.Sign(signer))
   299  
   300  	var usr user.ID
   301  	user.IDFromKey(&usr, signer.PublicKey)
   302  
   303  	var usrV2 refs.OwnerID
   304  	usr.WriteToV2(&usrV2)
   305  
   306  	assert(func(m v2session.Token) {
   307  		require.Equal(t, &usrV2, m.GetBody().GetOwnerID())
   308  
   309  		sig := m.GetSignature()
   310  		require.NotZero(t, sig.GetKey())
   311  		require.NotZero(t, sig.GetSign())
   312  	})
   313  
   314  	// Lifetime
   315  	const iat, nbf, exp = 1, 2, 3
   316  	val.SetIat(iat)
   317  	val.SetNbf(nbf)
   318  	val.SetExp(exp)
   319  
   320  	assert(func(m v2session.Token) {
   321  		lt := m.GetBody().GetLifetime()
   322  		require.EqualValues(t, iat, lt.GetIat())
   323  		require.EqualValues(t, nbf, lt.GetNbf())
   324  		require.EqualValues(t, exp, lt.GetExp())
   325  	})
   326  
   327  	// Context
   328  	assert(func(m v2session.Token) {
   329  		cCnr, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
   330  		require.True(t, ok)
   331  		require.Zero(t, cCnr.GetContainer())
   332  		require.Zero(t, cCnr.GetObjects())
   333  	})
   334  
   335  	cnr := cidtest.ID()
   336  
   337  	var cnrV2 refs.ContainerID
   338  	cnr.WriteToV2(&cnrV2)
   339  
   340  	obj1 := oidtest.ID()
   341  	obj2 := oidtest.ID()
   342  
   343  	var obj1V2 refs.ObjectID
   344  	obj1.WriteToV2(&obj1V2)
   345  	var obj2V2 refs.ObjectID
   346  	obj2.WriteToV2(&obj2V2)
   347  
   348  	val.BindContainer(cnr)
   349  	val.LimitByObjects(obj1, obj2)
   350  
   351  	assert(func(m v2session.Token) {
   352  		cCnr, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
   353  		require.True(t, ok)
   354  		require.Equal(t, &cnrV2, cCnr.GetContainer())
   355  		require.Equal(t, []refs.ObjectID{obj1V2, obj2V2}, cCnr.GetObjects())
   356  	})
   357  }
   358  
   359  func TestObject_BindContainer(t *testing.T) {
   360  	var val session.Object
   361  	var m v2session.Token
   362  	filled := sessiontest.Object()
   363  
   364  	assertDefaults := func() {
   365  		cCnr, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
   366  		require.True(t, ok)
   367  		require.Zero(t, cCnr.GetContainer())
   368  		require.Zero(t, cCnr.GetObjects())
   369  	}
   370  
   371  	assertBinary := func(baseAssert func()) {
   372  		val2 := filled
   373  
   374  		require.NoError(t, val2.Unmarshal(val.Marshal()))
   375  		baseAssert()
   376  	}
   377  
   378  	assertJSON := func(baseAssert func()) {
   379  		val2 := filled
   380  
   381  		jd, err := val.MarshalJSON()
   382  		require.NoError(t, err)
   383  
   384  		require.NoError(t, val2.UnmarshalJSON(jd))
   385  		baseAssert()
   386  	}
   387  
   388  	val.WriteToV2(&m)
   389  
   390  	assertDefaults()
   391  	assertBinary(assertDefaults)
   392  	assertJSON(assertDefaults)
   393  
   394  	// set value
   395  
   396  	cnr := cidtest.ID()
   397  
   398  	var cnrV2 refs.ContainerID
   399  	cnr.WriteToV2(&cnrV2)
   400  
   401  	val.BindContainer(cnr)
   402  
   403  	val.WriteToV2(&m)
   404  
   405  	assertCnr := func() {
   406  		cObj, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
   407  		require.True(t, ok)
   408  		require.Equal(t, &cnrV2, cObj.GetContainer())
   409  	}
   410  
   411  	assertCnr()
   412  	assertBinary(assertCnr)
   413  	assertJSON(assertCnr)
   414  }
   415  
   416  func TestObject_AssertContainer(t *testing.T) {
   417  	var x session.Object
   418  
   419  	cnr := cidtest.ID()
   420  
   421  	require.False(t, x.AssertContainer(cnr))
   422  
   423  	x.BindContainer(cnr)
   424  
   425  	require.True(t, x.AssertContainer(cnr))
   426  }
   427  
   428  func TestObject_LimitByObjects(t *testing.T) {
   429  	var val session.Object
   430  	var m v2session.Token
   431  	filled := sessiontest.Object()
   432  
   433  	assertDefaults := func() {
   434  		cCnr, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
   435  		require.True(t, ok)
   436  		require.Zero(t, cCnr.GetContainer())
   437  		require.Zero(t, cCnr.GetObjects())
   438  	}
   439  
   440  	assertBinary := func(baseAssert func()) {
   441  		val2 := filled
   442  
   443  		require.NoError(t, val2.Unmarshal(val.Marshal()))
   444  		baseAssert()
   445  	}
   446  
   447  	assertJSON := func(baseAssert func()) {
   448  		val2 := filled
   449  
   450  		jd, err := val.MarshalJSON()
   451  		require.NoError(t, err)
   452  
   453  		require.NoError(t, val2.UnmarshalJSON(jd))
   454  		baseAssert()
   455  	}
   456  
   457  	val.WriteToV2(&m)
   458  
   459  	assertDefaults()
   460  	assertBinary(assertDefaults)
   461  	assertJSON(assertDefaults)
   462  
   463  	// set value
   464  
   465  	obj1 := oidtest.ID()
   466  	obj2 := oidtest.ID()
   467  
   468  	var obj1V2 refs.ObjectID
   469  	obj1.WriteToV2(&obj1V2)
   470  	var obj2V2 refs.ObjectID
   471  	obj2.WriteToV2(&obj2V2)
   472  
   473  	val.LimitByObjects(obj1, obj2)
   474  
   475  	val.WriteToV2(&m)
   476  
   477  	assertObj := func() {
   478  		cObj, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
   479  		require.True(t, ok)
   480  		require.Equal(t, []refs.ObjectID{obj1V2, obj2V2}, cObj.GetObjects())
   481  	}
   482  
   483  	assertObj()
   484  	assertBinary(assertObj)
   485  	assertJSON(assertObj)
   486  }
   487  
   488  func TestObject_AssertObject(t *testing.T) {
   489  	var x session.Object
   490  
   491  	obj1 := oidtest.ID()
   492  	obj2 := oidtest.ID()
   493  	objOther := oidtest.ID()
   494  
   495  	require.True(t, x.AssertObject(obj1))
   496  	require.True(t, x.AssertObject(obj2))
   497  	require.True(t, x.AssertObject(objOther))
   498  
   499  	x.LimitByObjects(obj1, obj2)
   500  
   501  	require.True(t, x.AssertObject(obj1))
   502  	require.True(t, x.AssertObject(obj2))
   503  	require.False(t, x.AssertObject(objOther))
   504  }
   505  
   506  func TestObject_InvalidAt(t *testing.T) {
   507  	var x session.Object
   508  
   509  	nbf := rand.Uint64()
   510  	if nbf == math.MaxUint64 {
   511  		nbf--
   512  	}
   513  
   514  	iat := nbf
   515  	exp := iat + 1
   516  
   517  	x.SetNbf(nbf)
   518  	x.SetIat(iat)
   519  	x.SetExp(exp)
   520  
   521  	require.True(t, x.InvalidAt(nbf-1))
   522  	require.True(t, x.InvalidAt(iat-1))
   523  	require.False(t, x.InvalidAt(iat))
   524  	require.False(t, x.InvalidAt(exp))
   525  	require.True(t, x.InvalidAt(exp+1))
   526  }
   527  
   528  func TestObject_ID(t *testing.T) {
   529  	var x session.Object
   530  
   531  	require.Zero(t, x.ID())
   532  
   533  	id := uuid.New()
   534  
   535  	x.SetID(id)
   536  
   537  	require.Equal(t, id, x.ID())
   538  }
   539  
   540  func TestObject_AssertAuthKey(t *testing.T) {
   541  	var x session.Object
   542  
   543  	key := randPublicKey()
   544  
   545  	require.False(t, x.AssertAuthKey(key))
   546  
   547  	x.SetAuthKey(key)
   548  
   549  	require.True(t, x.AssertAuthKey(key))
   550  }
   551  
   552  func TestObject_ForVerb(t *testing.T) {
   553  	var val session.Object
   554  	var m v2session.Token
   555  	filled := sessiontest.Object()
   556  
   557  	assertDefaults := func() {
   558  		cCnr, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
   559  		require.True(t, ok)
   560  		require.Zero(t, cCnr.GetVerb())
   561  	}
   562  
   563  	assertBinary := func(baseAssert func()) {
   564  		val2 := filled
   565  
   566  		require.NoError(t, val2.Unmarshal(val.Marshal()))
   567  		baseAssert()
   568  	}
   569  
   570  	assertJSON := func(baseAssert func()) {
   571  		val2 := filled
   572  
   573  		jd, err := val.MarshalJSON()
   574  		require.NoError(t, err)
   575  
   576  		require.NoError(t, val2.UnmarshalJSON(jd))
   577  		baseAssert()
   578  	}
   579  
   580  	val.WriteToV2(&m)
   581  
   582  	assertDefaults()
   583  	assertBinary(assertDefaults)
   584  	assertJSON(assertDefaults)
   585  
   586  	// set value
   587  
   588  	assertVerb := func(verb v2session.ObjectSessionVerb) {
   589  		cCnr, ok := m.GetBody().GetContext().(*v2session.ObjectSessionContext)
   590  		require.True(t, ok)
   591  		require.Equal(t, verb, cCnr.GetVerb())
   592  	}
   593  
   594  	for from, to := range map[session.ObjectVerb]v2session.ObjectSessionVerb{
   595  		session.VerbObjectPut:       v2session.ObjectVerbPut,
   596  		session.VerbObjectGet:       v2session.ObjectVerbGet,
   597  		session.VerbObjectHead:      v2session.ObjectVerbHead,
   598  		session.VerbObjectSearch:    v2session.ObjectVerbSearch,
   599  		session.VerbObjectRangeHash: v2session.ObjectVerbRangeHash,
   600  		session.VerbObjectRange:     v2session.ObjectVerbRange,
   601  		session.VerbObjectDelete:    v2session.ObjectVerbDelete,
   602  		session.VerbObjectPatch:     v2session.ObjectVerbPatch,
   603  	} {
   604  		val.ForVerb(from)
   605  
   606  		val.WriteToV2(&m)
   607  
   608  		assertVerb(to)
   609  		assertBinary(func() { assertVerb(to) })
   610  		assertJSON(func() { assertVerb(to) })
   611  	}
   612  }
   613  
   614  func TestObject_AssertVerb(t *testing.T) {
   615  	var x session.Object
   616  
   617  	const v1, v2 = session.VerbObjectGet, session.VerbObjectPut
   618  
   619  	require.False(t, x.AssertVerb(v1, v2))
   620  
   621  	x.ForVerb(v1)
   622  	require.True(t, x.AssertVerb(v1))
   623  	require.False(t, x.AssertVerb(v2))
   624  	require.True(t, x.AssertVerb(v1, v2))
   625  	require.True(t, x.AssertVerb(v2, v1))
   626  }
   627  
   628  func TestObject_Issuer(t *testing.T) {
   629  	var token session.Object
   630  	signer := randSigner()
   631  
   632  	require.Zero(t, token.Issuer())
   633  
   634  	require.NoError(t, token.Sign(signer))
   635  
   636  	var issuer user.ID
   637  
   638  	user.IDFromKey(&issuer, signer.PublicKey)
   639  
   640  	require.True(t, token.Issuer().Equals(issuer))
   641  }
   642  
   643  func TestObject_Sign(t *testing.T) {
   644  	val := sessiontest.Object()
   645  
   646  	require.NoError(t, val.Sign(randSigner()))
   647  
   648  	require.True(t, val.VerifySignature())
   649  }