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 }