git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/session/container_test.go (about) 1 package session_test 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "fmt" 7 "math" 8 mrand "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 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session" 17 sessiontest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session/test" 18 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" 19 usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test" 20 "github.com/google/uuid" 21 "github.com/stretchr/testify/require" 22 ) 23 24 func TestContainerProtocolV2(t *testing.T) { 25 var validV2 v2session.Token 26 27 var body v2session.TokenBody 28 validV2.SetBody(&body) 29 30 // ID 31 id := uuid.New() 32 binID, err := id.MarshalBinary() 33 require.NoError(t, err) 34 restoreID := func() { 35 body.SetID(binID) 36 } 37 restoreID() 38 39 // Owner 40 usr := usertest.ID() 41 var usrV2 refs.OwnerID 42 usr.WriteToV2(&usrV2) 43 restoreUser := func() { 44 body.SetOwnerID(&usrV2) 45 } 46 restoreUser() 47 48 // Lifetime 49 var lifetime v2session.TokenLifetime 50 lifetime.SetIat(1) 51 lifetime.SetNbf(2) 52 lifetime.SetExp(3) 53 restoreLifetime := func() { 54 body.SetLifetime(&lifetime) 55 } 56 restoreLifetime() 57 58 // Session key 59 signer := randSigner() 60 authKey := frostfsecdsa.PublicKey(signer.PublicKey) 61 binAuthKey := make([]byte, authKey.MaxEncodedSize()) 62 binAuthKey = binAuthKey[:authKey.Encode(binAuthKey)] 63 restoreAuthKey := func() { 64 body.SetSessionKey(binAuthKey) 65 } 66 restoreAuthKey() 67 68 // Context 69 cnr := cidtest.ID() 70 var cnrV2 refs.ContainerID 71 cnr.WriteToV2(&cnrV2) 72 var cCnr v2session.ContainerSessionContext 73 restoreCtx := func() { 74 cCnr.SetContainerID(&cnrV2) 75 cCnr.SetWildcard(false) 76 body.SetContext(&cCnr) 77 } 78 restoreCtx() 79 80 // Signature 81 var sig refs.Signature 82 restoreSig := func() { 83 validV2.SetSignature(&sig) 84 } 85 restoreSig() 86 87 // TODO(@cthulhu-rider): #260 use functionality for message corruption 88 89 for _, testcase := range []struct { 90 name string 91 corrupt []func() 92 restore func() 93 assert func(session.Container) 94 breakSign func(*v2session.Token) 95 }{ 96 { 97 name: "Signature", 98 corrupt: []func(){ 99 func() { 100 validV2.SetSignature(nil) 101 }, 102 }, 103 restore: restoreSig, 104 }, 105 { 106 name: "ID", 107 corrupt: []func(){ 108 func() { 109 body.SetID([]byte{1, 2, 3}) 110 }, 111 func() { 112 id, err := uuid.NewDCEPerson() 113 require.NoError(t, err) 114 bindID, err := id.MarshalBinary() 115 require.NoError(t, err) 116 body.SetID(bindID) 117 }, 118 }, 119 restore: restoreID, 120 assert: func(val session.Container) { 121 require.Equal(t, id, val.ID()) 122 }, 123 breakSign: func(m *v2session.Token) { 124 id := m.GetBody().GetID() 125 id[len(id)-1]++ 126 }, 127 }, 128 { 129 name: "User", 130 corrupt: []func(){ 131 func() { 132 var brokenUsrV2 refs.OwnerID 133 brokenUsrV2.SetValue(append(usrV2.GetValue(), 1)) 134 body.SetOwnerID(&brokenUsrV2) 135 }, 136 }, 137 restore: restoreUser, 138 assert: func(val session.Container) { 139 require.Equal(t, usr, val.Issuer()) 140 }, 141 breakSign: func(m *v2session.Token) { 142 id := m.GetBody().GetOwnerID().GetValue() 143 copy(id, usertest.ID().WalletBytes()) 144 }, 145 }, 146 { 147 name: "Lifetime", 148 corrupt: []func(){ 149 func() { 150 body.SetLifetime(nil) 151 }, 152 }, 153 restore: restoreLifetime, 154 assert: func(val session.Container) { 155 require.True(t, val.InvalidAt(1)) 156 require.False(t, val.InvalidAt(2)) 157 require.False(t, val.InvalidAt(3)) 158 require.True(t, val.InvalidAt(4)) 159 }, 160 breakSign: func(m *v2session.Token) { 161 lt := m.GetBody().GetLifetime() 162 lt.SetIat(lt.GetIat() + 1) 163 }, 164 }, 165 { 166 name: "Auth key", 167 corrupt: []func(){ 168 func() { 169 body.SetSessionKey(nil) 170 }, 171 func() { 172 body.SetSessionKey([]byte{}) 173 }, 174 }, 175 restore: restoreAuthKey, 176 assert: func(val session.Container) { 177 require.True(t, val.AssertAuthKey(&authKey)) 178 }, 179 breakSign: func(m *v2session.Token) { 180 body := m.GetBody() 181 key := body.GetSessionKey() 182 cp := bytes.Clone(key) 183 cp[len(cp)-1]++ 184 body.SetSessionKey(cp) 185 }, 186 }, 187 { 188 name: "Context", 189 corrupt: []func(){ 190 func() { 191 body.SetContext(nil) 192 }, 193 func() { 194 cCnr.SetWildcard(true) 195 }, 196 func() { 197 cCnr.SetContainerID(nil) 198 }, 199 func() { 200 var brokenCnr refs.ContainerID 201 brokenCnr.SetValue(append(cnrV2.GetValue(), 1)) 202 cCnr.SetContainerID(&brokenCnr) 203 }, 204 }, 205 restore: restoreCtx, 206 assert: func(val session.Container) { 207 require.True(t, val.AppliedTo(cnr)) 208 require.False(t, val.AppliedTo(cidtest.ID())) 209 }, 210 breakSign: func(m *v2session.Token) { 211 cnr := m.GetBody().GetContext().(*v2session.ContainerSessionContext).ContainerID().GetValue() 212 cnr[len(cnr)-1]++ 213 }, 214 }, 215 } { 216 var val session.Container 217 218 for i, corrupt := range testcase.corrupt { 219 corrupt() 220 require.Error(t, val.ReadFromV2(validV2), testcase.name, fmt.Sprintf("corrupt #%d", i)) 221 222 testcase.restore() 223 require.NoError(t, val.ReadFromV2(validV2), testcase.name) 224 225 if testcase.assert != nil { 226 testcase.assert(val) 227 } 228 229 if testcase.breakSign != nil { 230 require.NoError(t, val.Sign(signer), testcase.name) 231 require.True(t, val.VerifySignature(), testcase.name) 232 233 var signedV2 v2session.Token 234 val.WriteToV2(&signedV2) 235 236 var restored session.Container 237 require.NoError(t, restored.ReadFromV2(signedV2), testcase.name) 238 require.True(t, restored.VerifySignature(), testcase.name) 239 240 testcase.breakSign(&signedV2) 241 242 require.NoError(t, restored.ReadFromV2(signedV2), testcase.name) 243 require.False(t, restored.VerifySignature(), testcase.name) 244 } 245 } 246 } 247 } 248 249 func TestContainer_WriteToV2(t *testing.T) { 250 var val session.Container 251 252 assert := func(baseAssert func(v2session.Token)) { 253 var m v2session.Token 254 val.WriteToV2(&m) 255 baseAssert(m) 256 } 257 258 // ID 259 id := uuid.New() 260 261 binID, err := id.MarshalBinary() 262 require.NoError(t, err) 263 264 val.SetID(id) 265 assert(func(m v2session.Token) { 266 require.Equal(t, binID, m.GetBody().GetID()) 267 }) 268 269 // Owner/Signature 270 signer := randSigner() 271 272 require.NoError(t, val.Sign(signer)) 273 274 var usr user.ID 275 user.IDFromKey(&usr, signer.PublicKey) 276 277 var usrV2 refs.OwnerID 278 usr.WriteToV2(&usrV2) 279 280 assert(func(m v2session.Token) { 281 require.Equal(t, &usrV2, m.GetBody().GetOwnerID()) 282 283 sig := m.GetSignature() 284 require.NotZero(t, sig.GetKey()) 285 require.NotZero(t, sig.GetSign()) 286 }) 287 288 // Lifetime 289 const iat, nbf, exp = 1, 2, 3 290 val.SetIat(iat) 291 val.SetNbf(nbf) 292 val.SetExp(exp) 293 294 assert(func(m v2session.Token) { 295 lt := m.GetBody().GetLifetime() 296 require.EqualValues(t, iat, lt.GetIat()) 297 require.EqualValues(t, nbf, lt.GetNbf()) 298 require.EqualValues(t, exp, lt.GetExp()) 299 }) 300 301 // Context 302 assert(func(m v2session.Token) { 303 cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext) 304 require.True(t, ok) 305 require.True(t, cCnr.Wildcard()) 306 require.Zero(t, cCnr.ContainerID()) 307 }) 308 309 cnr := cidtest.ID() 310 311 var cnrV2 refs.ContainerID 312 cnr.WriteToV2(&cnrV2) 313 314 val.ApplyOnlyTo(cnr) 315 316 assert(func(m v2session.Token) { 317 cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext) 318 require.True(t, ok) 319 require.False(t, cCnr.Wildcard()) 320 require.Equal(t, &cnrV2, cCnr.ContainerID()) 321 }) 322 } 323 324 func TestContainer_ApplyOnlyTo(t *testing.T) { 325 var val session.Container 326 var m v2session.Token 327 filled := sessiontest.Container() 328 329 assertDefaults := func() { 330 cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext) 331 require.True(t, ok) 332 require.True(t, cCnr.Wildcard()) 333 require.Zero(t, cCnr.ContainerID()) 334 } 335 336 assertBinary := func(baseAssert func()) { 337 val2 := filled 338 339 require.NoError(t, val2.Unmarshal(val.Marshal())) 340 baseAssert() 341 } 342 343 assertJSON := func(baseAssert func()) { 344 val2 := filled 345 346 jd, err := val.MarshalJSON() 347 require.NoError(t, err) 348 349 require.NoError(t, val2.UnmarshalJSON(jd)) 350 baseAssert() 351 } 352 353 val.WriteToV2(&m) 354 355 assertDefaults() 356 assertBinary(assertDefaults) 357 assertJSON(assertDefaults) 358 359 // set value 360 361 cnr := cidtest.ID() 362 363 var cnrV2 refs.ContainerID 364 cnr.WriteToV2(&cnrV2) 365 366 val.ApplyOnlyTo(cnr) 367 368 val.WriteToV2(&m) 369 370 assertCnr := func() { 371 cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext) 372 require.True(t, ok) 373 require.False(t, cCnr.Wildcard()) 374 require.Equal(t, &cnrV2, cCnr.ContainerID()) 375 } 376 377 assertCnr() 378 assertBinary(assertCnr) 379 assertJSON(assertCnr) 380 } 381 382 func TestContainer_AppliedTo(t *testing.T) { 383 var x session.Container 384 385 cnr1 := cidtest.ID() 386 cnr2 := cidtest.ID() 387 388 require.True(t, x.AppliedTo(cnr1)) 389 require.True(t, x.AppliedTo(cnr2)) 390 391 x.ApplyOnlyTo(cnr1) 392 393 require.True(t, x.AppliedTo(cnr1)) 394 require.False(t, x.AppliedTo(cnr2)) 395 } 396 397 func TestContainer_InvalidAt(t *testing.T) { 398 var x session.Container 399 400 nbf := mrand.Uint64() 401 if nbf == math.MaxUint64 { 402 nbf-- 403 } 404 405 iat := nbf 406 exp := iat + 1 407 408 x.SetNbf(nbf) 409 x.SetIat(iat) 410 x.SetExp(exp) 411 412 require.True(t, x.InvalidAt(nbf-1)) 413 require.True(t, x.InvalidAt(iat-1)) 414 require.False(t, x.InvalidAt(iat)) 415 require.False(t, x.InvalidAt(exp)) 416 require.True(t, x.InvalidAt(exp+1)) 417 } 418 419 func TestContainer_ID(t *testing.T) { 420 var x session.Container 421 422 require.Zero(t, x.ID()) 423 424 id := uuid.New() 425 426 x.SetID(id) 427 428 require.Equal(t, id, x.ID()) 429 } 430 431 func TestContainer_AssertAuthKey(t *testing.T) { 432 var x session.Container 433 434 key := randPublicKey() 435 436 require.False(t, x.AssertAuthKey(key)) 437 438 x.SetAuthKey(key) 439 440 require.True(t, x.AssertAuthKey(key)) 441 } 442 443 func TestContainer_ForVerb(t *testing.T) { 444 var val session.Container 445 var m v2session.Token 446 filled := sessiontest.Container() 447 448 assertDefaults := func() { 449 cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext) 450 require.True(t, ok) 451 require.Zero(t, cCnr.Verb()) 452 } 453 454 assertBinary := func(baseAssert func()) { 455 val2 := filled 456 457 require.NoError(t, val2.Unmarshal(val.Marshal())) 458 baseAssert() 459 } 460 461 assertJSON := func(baseAssert func()) { 462 val2 := filled 463 464 jd, err := val.MarshalJSON() 465 require.NoError(t, err) 466 467 require.NoError(t, val2.UnmarshalJSON(jd)) 468 baseAssert() 469 } 470 471 val.WriteToV2(&m) 472 473 assertDefaults() 474 assertBinary(assertDefaults) 475 assertJSON(assertDefaults) 476 477 // set value 478 479 assertVerb := func(verb v2session.ContainerSessionVerb) { 480 cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext) 481 require.True(t, ok) 482 require.Equal(t, verb, cCnr.Verb()) 483 } 484 485 for from, to := range map[session.ContainerVerb]v2session.ContainerSessionVerb{ 486 session.VerbContainerPut: v2session.ContainerVerbPut, 487 session.VerbContainerDelete: v2session.ContainerVerbDelete, 488 session.VerbContainerSetEACL: v2session.ContainerVerbSetEACL, 489 } { 490 val.ForVerb(from) 491 492 val.WriteToV2(&m) 493 494 assertVerb(to) 495 assertBinary(func() { assertVerb(to) }) 496 assertJSON(func() { assertVerb(to) }) 497 } 498 } 499 500 func TestContainer_AssertVerb(t *testing.T) { 501 var x session.Container 502 503 const v1, v2 = session.VerbContainerPut, session.VerbContainerDelete 504 505 require.False(t, x.AssertVerb(v1)) 506 require.False(t, x.AssertVerb(v2)) 507 508 x.ForVerb(v1) 509 require.True(t, x.AssertVerb(v1)) 510 require.False(t, x.AssertVerb(v2)) 511 } 512 513 func TestIssuedBy(t *testing.T) { 514 var ( 515 token session.Container 516 issuer user.ID 517 signer = randSigner() 518 ) 519 520 user.IDFromKey(&issuer, signer.PublicKey) 521 522 require.False(t, session.IssuedBy(token, issuer)) 523 524 require.NoError(t, token.Sign(signer)) 525 require.True(t, session.IssuedBy(token, issuer)) 526 } 527 528 func TestContainer_Issuer(t *testing.T) { 529 var token session.Container 530 signer := randSigner() 531 532 require.Zero(t, token.Issuer()) 533 534 require.NoError(t, token.Sign(signer)) 535 536 var issuer user.ID 537 538 user.IDFromKey(&issuer, signer.PublicKey) 539 540 require.True(t, token.Issuer().Equals(issuer)) 541 } 542 543 func TestContainer_Sign(t *testing.T) { 544 val := sessiontest.Container() 545 546 require.NoError(t, val.Sign(randSigner())) 547 548 require.True(t, val.VerifySignature()) 549 } 550 551 func TestContainer_VerifyDataSignature(t *testing.T) { 552 signer := randSigner() 553 554 var tok session.Container 555 556 data := make([]byte, 100) 557 rand.Read(data) 558 559 var sig frostfscrypto.Signature 560 require.NoError(t, sig.Calculate(frostfsecdsa.SignerRFC6979(signer), data)) 561 562 var sigV2 refs.Signature 563 sig.WriteToV2(&sigV2) 564 565 require.False(t, tok.VerifySessionDataSignature(data, sigV2.GetSign())) 566 567 tok.SetAuthKey((*frostfsecdsa.PublicKeyRFC6979)(&signer.PublicKey)) 568 require.True(t, tok.VerifySessionDataSignature(data, sigV2.GetSign())) 569 require.False(t, tok.VerifySessionDataSignature(append(data, 1), sigV2.GetSign())) 570 require.False(t, tok.VerifySessionDataSignature(data, append(sigV2.GetSign(), 1))) 571 }