github.com/status-im/status-go@v1.1.0/protocol/communities/community_encryption_key_action_test.go (about) 1 package communities 2 3 import ( 4 "crypto/ecdsa" 5 "reflect" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/suite" 10 11 "github.com/status-im/status-go/eth-node/crypto" 12 "github.com/status-im/status-go/eth-node/types" 13 "github.com/status-im/status-go/protocol/common" 14 "github.com/status-im/status-go/protocol/protobuf" 15 ) 16 17 func createTestCommunity(identity *ecdsa.PrivateKey) (*Community, error) { 18 config := Config{ 19 PrivateKey: identity, 20 CommunityDescription: &protobuf.CommunityDescription{ 21 Members: map[string]*protobuf.CommunityMember{}, 22 Permissions: &protobuf.CommunityPermissions{}, 23 Identity: &protobuf.ChatIdentity{}, 24 Chats: map[string]*protobuf.CommunityChat{}, 25 BanList: []string{}, 26 Categories: map[string]*protobuf.CommunityCategory{}, 27 TokenPermissions: map[string]*protobuf.CommunityTokenPermission{}, 28 CommunityTokensMetadata: []*protobuf.CommunityTokenMetadata{}, 29 }, 30 ID: &identity.PublicKey, 31 ControlNode: &identity.PublicKey, 32 ControlDevice: true, 33 Joined: true, 34 MemberIdentity: identity, 35 } 36 37 return New(config, &TimeSourceStub{}, &DescriptionEncryptorMock{}, nil) 38 } 39 40 func TestCommunityEncryptionKeyActionSuite(t *testing.T) { 41 suite.Run(t, new(CommunityEncryptionKeyActionSuite)) 42 } 43 44 type CommunityEncryptionKeyActionSuite struct { 45 suite.Suite 46 47 identity *ecdsa.PrivateKey 48 communityID []byte 49 50 member1 *ecdsa.PrivateKey 51 member2 *ecdsa.PrivateKey 52 member3 *ecdsa.PrivateKey 53 54 member1Key string 55 member2Key string 56 member3Key string 57 } 58 59 func (s *CommunityEncryptionKeyActionSuite) SetupTest() { 60 identity, err := crypto.GenerateKey() 61 s.Require().NoError(err) 62 s.identity = identity 63 s.communityID = crypto.CompressPubkey(&identity.PublicKey) 64 65 member1, err := crypto.GenerateKey() 66 s.Require().NoError(err) 67 s.member1 = member1 68 69 member2, err := crypto.GenerateKey() 70 s.Require().NoError(err) 71 s.member2 = member2 72 73 member3, err := crypto.GenerateKey() 74 s.Require().NoError(err) 75 s.member3 = member3 76 77 s.member1Key = common.PubkeyToHex(&s.member1.PublicKey) 78 s.member2Key = common.PubkeyToHex(&s.member2.PublicKey) 79 s.member3Key = common.PubkeyToHex(&s.member3.PublicKey) 80 } 81 82 func (s *CommunityEncryptionKeyActionSuite) TestEncryptionKeyNone() { 83 origin, err := createTestCommunity(s.identity) 84 s.Require().NoError(err) 85 86 // if there are no changes there should be no actions 87 actions := EvaluateCommunityEncryptionKeyActions(origin, origin) 88 s.Require().Equal(actions.CommunityKeyAction.ActionType, EncryptionKeyNone) 89 s.Require().Len(actions.ChannelKeysActions, 0) 90 } 91 92 func (s *CommunityEncryptionKeyActionSuite) TestCommunityLevelKeyActions_PermissionsCombinations() { 93 testCases := []struct { 94 name string 95 originPermissions []*protobuf.CommunityTokenPermission 96 modifiedPermissions []*protobuf.CommunityTokenPermission 97 expectedActionType EncryptionKeyActionType 98 }{ 99 { 100 name: "add member permission", 101 originPermissions: []*protobuf.CommunityTokenPermission{}, 102 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 103 &protobuf.CommunityTokenPermission{ 104 Id: "some-id", 105 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 106 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 107 ChatIds: []string{}, 108 }, 109 }, 110 expectedActionType: EncryptionKeyAdd, 111 }, 112 { 113 name: "add member permissions", 114 originPermissions: []*protobuf.CommunityTokenPermission{}, 115 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 116 &protobuf.CommunityTokenPermission{ 117 Id: "some-id-1", 118 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 119 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 120 ChatIds: []string{}, 121 }, 122 &protobuf.CommunityTokenPermission{ 123 Id: "some-id-2", 124 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 125 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 126 ChatIds: []string{}, 127 }, 128 }, 129 expectedActionType: EncryptionKeyAdd, 130 }, 131 { 132 name: "add another member permission", 133 originPermissions: []*protobuf.CommunityTokenPermission{ 134 &protobuf.CommunityTokenPermission{ 135 Id: "some-id-1", 136 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 137 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 138 ChatIds: []string{}, 139 }, 140 }, 141 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 142 &protobuf.CommunityTokenPermission{ 143 Id: "some-id-1", 144 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 145 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 146 ChatIds: []string{}, 147 }, 148 &protobuf.CommunityTokenPermission{ 149 Id: "some-id-2", 150 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 151 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 152 ChatIds: []string{}, 153 }, 154 }, 155 expectedActionType: EncryptionKeyNone, 156 }, 157 { 158 name: "add another member permission and remove previous one", 159 originPermissions: []*protobuf.CommunityTokenPermission{ 160 &protobuf.CommunityTokenPermission{ 161 Id: "some-id-1", 162 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 163 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 164 ChatIds: []string{}, 165 }, 166 }, 167 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 168 &protobuf.CommunityTokenPermission{ 169 Id: "some-id-2", 170 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 171 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 172 ChatIds: []string{}, 173 }, 174 }, 175 expectedActionType: EncryptionKeyNone, 176 }, 177 { 178 name: "remove member permission", 179 originPermissions: []*protobuf.CommunityTokenPermission{ 180 &protobuf.CommunityTokenPermission{ 181 Id: "some-id", 182 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 183 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 184 ChatIds: []string{}, 185 }, 186 }, 187 modifiedPermissions: []*protobuf.CommunityTokenPermission{}, 188 expectedActionType: EncryptionKeyRemove, 189 }, 190 { 191 name: "remove one of member permissions", 192 originPermissions: []*protobuf.CommunityTokenPermission{ 193 &protobuf.CommunityTokenPermission{ 194 Id: "some-id-1", 195 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 196 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 197 ChatIds: []string{}, 198 }, 199 &protobuf.CommunityTokenPermission{ 200 Id: "some-id-2", 201 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 202 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 203 ChatIds: []string{}, 204 }, 205 }, 206 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 207 &protobuf.CommunityTokenPermission{ 208 Id: "some-id-1", 209 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 210 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 211 ChatIds: []string{}, 212 }, 213 }, 214 expectedActionType: EncryptionKeyNone, 215 }, 216 { 217 name: "add channel permission", 218 originPermissions: []*protobuf.CommunityTokenPermission{}, 219 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 220 &protobuf.CommunityTokenPermission{ 221 Id: "some-id", 222 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 223 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 224 ChatIds: []string{"some-chat-id"}, 225 }, 226 }, 227 expectedActionType: EncryptionKeyNone, 228 }, 229 { 230 name: "remove channel permission", 231 originPermissions: []*protobuf.CommunityTokenPermission{ 232 &protobuf.CommunityTokenPermission{ 233 Id: "some-id", 234 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 235 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 236 ChatIds: []string{"some-chat-id"}, 237 }, 238 }, 239 modifiedPermissions: []*protobuf.CommunityTokenPermission{}, 240 expectedActionType: EncryptionKeyNone, 241 }, 242 { 243 name: "add member permission on top of channel permission", 244 originPermissions: []*protobuf.CommunityTokenPermission{ 245 &protobuf.CommunityTokenPermission{ 246 Id: "some-id-1", 247 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 248 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 249 ChatIds: []string{"some-chat-id"}, 250 }, 251 }, 252 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 253 &protobuf.CommunityTokenPermission{ 254 Id: "some-id-1", 255 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 256 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 257 ChatIds: []string{"some-chat-id"}, 258 }, 259 &protobuf.CommunityTokenPermission{ 260 Id: "some-id-2", 261 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 262 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 263 ChatIds: []string{""}, 264 }, 265 }, 266 expectedActionType: EncryptionKeyAdd, 267 }, 268 { 269 name: "add channel permission on top of member permission", 270 originPermissions: []*protobuf.CommunityTokenPermission{ 271 &protobuf.CommunityTokenPermission{ 272 Id: "some-id-1", 273 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 274 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 275 ChatIds: []string{""}, 276 }, 277 }, 278 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 279 &protobuf.CommunityTokenPermission{ 280 Id: "some-id-1", 281 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 282 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 283 ChatIds: []string{""}, 284 }, 285 &protobuf.CommunityTokenPermission{ 286 Id: "some-id-2", 287 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 288 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 289 ChatIds: []string{"some-chat-id"}, 290 }, 291 }, 292 expectedActionType: EncryptionKeyNone, 293 }, 294 { 295 name: "change member permission to channel permission", 296 originPermissions: []*protobuf.CommunityTokenPermission{ 297 &protobuf.CommunityTokenPermission{ 298 Id: "some-id", 299 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 300 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 301 ChatIds: []string{""}, 302 }, 303 }, 304 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 305 &protobuf.CommunityTokenPermission{ 306 Id: "some-id", 307 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 308 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 309 ChatIds: []string{""}, 310 }, 311 }, 312 expectedActionType: EncryptionKeyRemove, 313 }, 314 { 315 name: "change channel permission to member permission", 316 originPermissions: []*protobuf.CommunityTokenPermission{ 317 &protobuf.CommunityTokenPermission{ 318 Id: "some-id", 319 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 320 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 321 ChatIds: []string{""}, 322 }, 323 }, 324 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 325 &protobuf.CommunityTokenPermission{ 326 Id: "some-id", 327 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 328 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 329 ChatIds: []string{""}, 330 }, 331 }, 332 expectedActionType: EncryptionKeyAdd, 333 }, 334 { 335 name: "change channel permission to member permission on top of member permission", 336 originPermissions: []*protobuf.CommunityTokenPermission{ 337 &protobuf.CommunityTokenPermission{ 338 Id: "some-id-1", 339 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 340 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 341 ChatIds: []string{""}, 342 }, 343 &protobuf.CommunityTokenPermission{ 344 Id: "some-id-2", 345 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 346 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 347 ChatIds: []string{""}, 348 }, 349 }, 350 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 351 &protobuf.CommunityTokenPermission{ 352 Id: "some-id-1", 353 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 354 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 355 ChatIds: []string{""}, 356 }, 357 &protobuf.CommunityTokenPermission{ 358 Id: "some-id-2", 359 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 360 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 361 ChatIds: []string{""}, 362 }, 363 }, 364 expectedActionType: EncryptionKeyNone, 365 }, 366 } 367 368 for _, tc := range testCases { 369 s.Run(tc.name, func() { 370 origin, err := createTestCommunity(s.identity) 371 s.Require().NoError(err) 372 modified := origin.CreateDeepCopy() 373 374 for _, permission := range tc.originPermissions { 375 _, err := origin.UpsertTokenPermission(permission) 376 s.Require().NoError(err) 377 } 378 379 for _, permission := range tc.modifiedPermissions { 380 _, err := modified.UpsertTokenPermission(permission) 381 s.Require().NoError(err) 382 } 383 384 actions := EvaluateCommunityEncryptionKeyActions(origin, modified) 385 s.Require().Equal(tc.expectedActionType, actions.CommunityKeyAction.ActionType) 386 }) 387 } 388 } 389 390 func (s *CommunityEncryptionKeyActionSuite) TestCommunityLevelKeyActions_MembersCombinations() { 391 testCases := []struct { 392 name string 393 permissions []*protobuf.CommunityTokenPermission 394 originMembers []*ecdsa.PublicKey 395 modifiedMembers []*ecdsa.PublicKey 396 expectedAction EncryptionKeyAction 397 }{ 398 { 399 name: "add member to open community", 400 permissions: []*protobuf.CommunityTokenPermission{}, 401 originMembers: []*ecdsa.PublicKey{}, 402 modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 403 expectedAction: EncryptionKeyAction{ 404 ActionType: EncryptionKeyNone, 405 Members: map[string]*protobuf.CommunityMember{}, 406 }, 407 }, 408 { 409 name: "remove member from open community", 410 permissions: []*protobuf.CommunityTokenPermission{}, 411 originMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 412 modifiedMembers: []*ecdsa.PublicKey{}, 413 expectedAction: EncryptionKeyAction{ 414 ActionType: EncryptionKeyNone, 415 Members: map[string]*protobuf.CommunityMember{}, 416 }, 417 }, 418 { 419 name: "add member to token-gated community", 420 permissions: []*protobuf.CommunityTokenPermission{ 421 &protobuf.CommunityTokenPermission{ 422 Id: "some-id", 423 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 424 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 425 ChatIds: []string{}, 426 }, 427 }, 428 originMembers: []*ecdsa.PublicKey{}, 429 modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 430 expectedAction: EncryptionKeyAction{ 431 ActionType: EncryptionKeySendToMembers, 432 Members: map[string]*protobuf.CommunityMember{ 433 s.member1Key: &protobuf.CommunityMember{}, 434 }, 435 }, 436 }, 437 { 438 name: "add multiple members to token-gated community", 439 permissions: []*protobuf.CommunityTokenPermission{ 440 &protobuf.CommunityTokenPermission{ 441 Id: "some-id", 442 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 443 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 444 ChatIds: []string{}, 445 }, 446 }, 447 originMembers: []*ecdsa.PublicKey{}, 448 modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey}, 449 expectedAction: EncryptionKeyAction{ 450 ActionType: EncryptionKeySendToMembers, 451 Members: map[string]*protobuf.CommunityMember{ 452 s.member1Key: &protobuf.CommunityMember{}, 453 s.member2Key: &protobuf.CommunityMember{}, 454 }, 455 }, 456 }, 457 { 458 name: "remove member from token-gated community", 459 permissions: []*protobuf.CommunityTokenPermission{ 460 &protobuf.CommunityTokenPermission{ 461 Id: "some-id", 462 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 463 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 464 ChatIds: []string{}, 465 }, 466 }, 467 originMembers: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey}, 468 modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 469 expectedAction: EncryptionKeyAction{ 470 ActionType: EncryptionKeyRekey, 471 Members: map[string]*protobuf.CommunityMember{ 472 s.member1Key: &protobuf.CommunityMember{}, 473 }, 474 }, 475 }, 476 { 477 name: "add and remove members from token-gated community", 478 permissions: []*protobuf.CommunityTokenPermission{ 479 &protobuf.CommunityTokenPermission{ 480 Id: "some-id", 481 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 482 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 483 ChatIds: []string{}, 484 }, 485 }, 486 originMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 487 modifiedMembers: []*ecdsa.PublicKey{&s.member2.PublicKey, &s.member3.PublicKey}, 488 expectedAction: EncryptionKeyAction{ 489 ActionType: EncryptionKeyRekey, 490 Members: map[string]*protobuf.CommunityMember{ 491 s.member2Key: &protobuf.CommunityMember{}, 492 s.member3Key: &protobuf.CommunityMember{}, 493 }, 494 }, 495 }, 496 } 497 498 for _, tc := range testCases { 499 s.Run(tc.name, func() { 500 origin, err := createTestCommunity(s.identity) 501 s.Require().NoError(err) 502 503 for _, permission := range tc.permissions { 504 _, err := origin.UpsertTokenPermission(permission) 505 s.Require().NoError(err) 506 } 507 modified := origin.CreateDeepCopy() 508 509 for _, member := range tc.originMembers { 510 _, err := origin.AddMember(member, []protobuf.CommunityMember_Roles{}, origin.Clock()) 511 s.Require().NoError(err) 512 } 513 514 for _, member := range tc.modifiedMembers { 515 _, err := modified.AddMember(member, []protobuf.CommunityMember_Roles{}, origin.Clock()) 516 s.Require().NoError(err) 517 } 518 519 actions := EvaluateCommunityEncryptionKeyActions(origin, modified) 520 s.Require().Equal(tc.expectedAction.ActionType, actions.CommunityKeyAction.ActionType) 521 s.Require().Len(tc.expectedAction.Members, len(actions.CommunityKeyAction.Members)) 522 for memberKey := range tc.expectedAction.Members { 523 _, exists := actions.CommunityKeyAction.Members[memberKey] 524 s.Require().True(exists) 525 } 526 }) 527 } 528 } 529 530 func (s *CommunityEncryptionKeyActionSuite) TestCommunityLevelKeyActions_PermissionsMembersCombinations() { 531 testCases := []struct { 532 name string 533 originPermissions []*protobuf.CommunityTokenPermission 534 modifiedPermissions []*protobuf.CommunityTokenPermission 535 originMembers []*ecdsa.PublicKey 536 modifiedMembers []*ecdsa.PublicKey 537 expectedActionType EncryptionKeyActionType 538 }{ 539 { 540 name: "add member permission, add members", 541 originPermissions: []*protobuf.CommunityTokenPermission{}, 542 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 543 &protobuf.CommunityTokenPermission{ 544 Id: "some-id", 545 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 546 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 547 ChatIds: []string{}, 548 }, 549 }, 550 originMembers: []*ecdsa.PublicKey{}, 551 modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 552 expectedActionType: EncryptionKeyAdd, 553 }, 554 { 555 name: "add member permission, remove members", 556 originPermissions: []*protobuf.CommunityTokenPermission{}, 557 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 558 &protobuf.CommunityTokenPermission{ 559 Id: "some-id", 560 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 561 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 562 ChatIds: []string{}, 563 }, 564 }, 565 originMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 566 modifiedMembers: []*ecdsa.PublicKey{}, 567 expectedActionType: EncryptionKeyAdd, 568 }, 569 { 570 name: "remove member permission, add members", 571 originPermissions: []*protobuf.CommunityTokenPermission{ 572 &protobuf.CommunityTokenPermission{ 573 Id: "some-id", 574 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 575 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 576 ChatIds: []string{}, 577 }, 578 }, 579 modifiedPermissions: []*protobuf.CommunityTokenPermission{}, 580 originMembers: []*ecdsa.PublicKey{}, 581 modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 582 expectedActionType: EncryptionKeyRemove, 583 }, 584 { 585 name: "remove member permission, remove members", 586 originPermissions: []*protobuf.CommunityTokenPermission{ 587 &protobuf.CommunityTokenPermission{ 588 Id: "some-id", 589 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 590 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 591 ChatIds: []string{}, 592 }, 593 }, 594 modifiedPermissions: []*protobuf.CommunityTokenPermission{}, 595 originMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 596 modifiedMembers: []*ecdsa.PublicKey{}, 597 expectedActionType: EncryptionKeyRemove, 598 }, 599 } 600 601 for _, tc := range testCases { 602 s.Run(tc.name, func() { 603 origin, err := createTestCommunity(s.identity) 604 s.Require().NoError(err) 605 modified := origin.CreateDeepCopy() 606 607 for _, permission := range tc.originPermissions { 608 _, err := origin.UpsertTokenPermission(permission) 609 s.Require().NoError(err) 610 } 611 for _, member := range tc.originMembers { 612 _, err := origin.AddMember(member, []protobuf.CommunityMember_Roles{}, origin.Clock()) 613 s.Require().NoError(err) 614 } 615 616 for _, permission := range tc.modifiedPermissions { 617 _, err := modified.UpsertTokenPermission(permission) 618 s.Require().NoError(err) 619 } 620 for _, member := range tc.modifiedMembers { 621 _, err := modified.AddMember(member, []protobuf.CommunityMember_Roles{}, origin.Clock()) 622 s.Require().NoError(err) 623 } 624 625 actions := EvaluateCommunityEncryptionKeyActions(origin, modified) 626 s.Require().Equal(tc.expectedActionType, actions.CommunityKeyAction.ActionType) 627 }) 628 } 629 } 630 631 func (s *CommunityEncryptionKeyActionSuite) TestChannelLevelKeyActions() { 632 channelID := "1234" 633 chatID := types.EncodeHex(crypto.CompressPubkey(&s.identity.PublicKey)) + channelID 634 testCases := []struct { 635 name string 636 originPermissions []*protobuf.CommunityTokenPermission 637 modifiedPermissions []*protobuf.CommunityTokenPermission 638 originMembers []*ecdsa.PublicKey 639 modifiedMembers []*ecdsa.PublicKey 640 expectedAction EncryptionKeyAction 641 }{ 642 { 643 name: "add channel permission", 644 originPermissions: []*protobuf.CommunityTokenPermission{}, 645 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 646 &protobuf.CommunityTokenPermission{ 647 Id: "some-id", 648 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 649 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 650 ChatIds: []string{chatID}, 651 }, 652 }, 653 originMembers: []*ecdsa.PublicKey{}, 654 modifiedMembers: []*ecdsa.PublicKey{}, 655 expectedAction: EncryptionKeyAction{ 656 ActionType: EncryptionKeyAdd, 657 Members: map[string]*protobuf.CommunityMember{}, 658 }, 659 }, 660 { 661 name: "remove channel permission", 662 originPermissions: []*protobuf.CommunityTokenPermission{ 663 &protobuf.CommunityTokenPermission{ 664 Id: "some-id", 665 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 666 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 667 ChatIds: []string{chatID}, 668 }, 669 }, 670 modifiedPermissions: []*protobuf.CommunityTokenPermission{}, 671 originMembers: []*ecdsa.PublicKey{}, 672 modifiedMembers: []*ecdsa.PublicKey{}, 673 expectedAction: EncryptionKeyAction{ 674 ActionType: EncryptionKeyRemove, 675 Members: map[string]*protobuf.CommunityMember{}, 676 }, 677 }, 678 { 679 name: "add members to token-gated channel", 680 originPermissions: []*protobuf.CommunityTokenPermission{ 681 &protobuf.CommunityTokenPermission{ 682 Id: "some-id", 683 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 684 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 685 ChatIds: []string{chatID}, 686 }, 687 }, 688 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 689 &protobuf.CommunityTokenPermission{ 690 Id: "some-id", 691 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 692 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 693 ChatIds: []string{chatID}, 694 }, 695 }, 696 originMembers: []*ecdsa.PublicKey{}, 697 modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey}, 698 expectedAction: EncryptionKeyAction{ 699 ActionType: EncryptionKeySendToMembers, 700 Members: map[string]*protobuf.CommunityMember{ 701 s.member1Key: &protobuf.CommunityMember{}, 702 s.member2Key: &protobuf.CommunityMember{}, 703 }, 704 }, 705 }, 706 { 707 name: "remove members from token-gated channel", 708 originPermissions: []*protobuf.CommunityTokenPermission{ 709 &protobuf.CommunityTokenPermission{ 710 Id: "some-id", 711 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 712 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 713 ChatIds: []string{chatID}, 714 }, 715 }, 716 modifiedPermissions: []*protobuf.CommunityTokenPermission{ 717 &protobuf.CommunityTokenPermission{ 718 Id: "some-id", 719 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 720 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 721 ChatIds: []string{chatID}, 722 }, 723 }, 724 originMembers: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey}, 725 modifiedMembers: []*ecdsa.PublicKey{}, 726 expectedAction: EncryptionKeyAction{ 727 ActionType: EncryptionKeyRekey, 728 Members: map[string]*protobuf.CommunityMember{}, 729 }, 730 }, 731 { 732 name: "add members to open channel", 733 originPermissions: []*protobuf.CommunityTokenPermission{}, 734 modifiedPermissions: []*protobuf.CommunityTokenPermission{}, 735 originMembers: []*ecdsa.PublicKey{}, 736 modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey}, 737 expectedAction: EncryptionKeyAction{ 738 ActionType: EncryptionKeyNone, 739 Members: map[string]*protobuf.CommunityMember{}, 740 }, 741 }, 742 } 743 744 for _, tc := range testCases { 745 s.Run(tc.name, func() { 746 origin, err := createTestCommunity(s.identity) 747 s.Require().NoError(err) 748 749 _, err = origin.CreateChat(channelID, &protobuf.CommunityChat{ 750 Members: map[string]*protobuf.CommunityMember{}, 751 Permissions: &protobuf.CommunityPermissions{Access: protobuf.CommunityPermissions_AUTO_ACCEPT}, 752 Identity: &protobuf.ChatIdentity{}, 753 }) 754 s.Require().NoError(err) 755 756 modified := origin.CreateDeepCopy() 757 758 for _, permission := range tc.originPermissions { 759 _, err := origin.UpsertTokenPermission(permission) 760 s.Require().NoError(err) 761 } 762 for _, member := range tc.originMembers { 763 _, err := origin.AddMember(member, []protobuf.CommunityMember_Roles{}, origin.Clock()) 764 s.Require().NoError(err) 765 _, err = origin.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{}, protobuf.CommunityMember_CHANNEL_ROLE_POSTER) 766 s.Require().NoError(err) 767 } 768 769 for _, permission := range tc.modifiedPermissions { 770 _, err := modified.UpsertTokenPermission(permission) 771 s.Require().NoError(err) 772 } 773 for _, member := range tc.modifiedMembers { 774 _, err := modified.AddMember(member, []protobuf.CommunityMember_Roles{}, origin.Clock()) 775 s.Require().NoError(err) 776 _, err = modified.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{}, protobuf.CommunityMember_CHANNEL_ROLE_POSTER) 777 s.Require().NoError(err) 778 } 779 780 actions := EvaluateCommunityEncryptionKeyActions(origin, modified) 781 channelAction, ok := actions.ChannelKeysActions[channelID] 782 s.Require().True(ok) 783 s.Require().Equal(tc.expectedAction.ActionType, channelAction.ActionType) 784 s.Require().Len(tc.expectedAction.Members, len(channelAction.Members)) 785 for memberKey := range tc.expectedAction.Members { 786 _, exists := channelAction.Members[memberKey] 787 s.Require().True(exists) 788 } 789 }) 790 } 791 } 792 793 func (s *CommunityEncryptionKeyActionSuite) TestNilOrigin() { 794 newCommunity, err := createTestCommunity(s.identity) 795 s.Require().NoError(err) 796 797 channelID := "0x1234" 798 chatID := types.EncodeHex(crypto.CompressPubkey(&s.identity.PublicKey)) + channelID 799 800 _, err = newCommunity.CreateChat(channelID, &protobuf.CommunityChat{ 801 Members: map[string]*protobuf.CommunityMember{}, 802 Permissions: &protobuf.CommunityPermissions{Access: protobuf.CommunityPermissions_AUTO_ACCEPT}, 803 Identity: &protobuf.ChatIdentity{}, 804 }) 805 s.Require().NoError(err) 806 807 newCommunityPermissions := []*protobuf.CommunityTokenPermission{ 808 &protobuf.CommunityTokenPermission{ 809 Id: "some-id-1", 810 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 811 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 812 ChatIds: []string{}, 813 }, 814 &protobuf.CommunityTokenPermission{ 815 Id: "some-id-2", 816 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 817 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 818 ChatIds: []string{chatID}, 819 }, 820 } 821 for _, permission := range newCommunityPermissions { 822 _, err := newCommunity.UpsertTokenPermission(permission) 823 s.Require().NoError(err) 824 } 825 826 actions := EvaluateCommunityEncryptionKeyActions(nil, newCommunity) 827 s.Require().Equal(actions.CommunityKeyAction.ActionType, EncryptionKeyAdd) 828 s.Require().Len(actions.ChannelKeysActions, 1) 829 s.Require().NotNil(actions.ChannelKeysActions[channelID]) 830 s.Require().Equal(actions.ChannelKeysActions[channelID].ActionType, EncryptionKeyAdd) 831 } 832 833 func (s *CommunityEncryptionKeyActionSuite) TestControlNodeChange() { 834 channelID := "1234" 835 chatID := types.EncodeHex(crypto.CompressPubkey(&s.identity.PublicKey)) + channelID 836 clock := uint64(time.Now().Unix()) 837 testCases := []struct { 838 name string 839 permissions []*protobuf.CommunityTokenPermission 840 members []*ecdsa.PublicKey 841 channelMembers []*ecdsa.PublicKey 842 expectedActions EncryptionKeyActions 843 }{ 844 { 845 name: "change control node in open community", 846 permissions: []*protobuf.CommunityTokenPermission{}, 847 members: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey}, 848 channelMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 849 expectedActions: EncryptionKeyActions{ 850 CommunityKeyAction: EncryptionKeyAction{ 851 ActionType: EncryptionKeyNone, 852 Members: map[string]*protobuf.CommunityMember{}, 853 }, 854 ChannelKeysActions: map[string]EncryptionKeyAction{ 855 channelID: EncryptionKeyAction{ 856 ActionType: EncryptionKeyNone, 857 Members: map[string]*protobuf.CommunityMember{}, 858 }, 859 }, 860 }, 861 }, 862 { 863 name: "change control node in token-gated community", 864 permissions: []*protobuf.CommunityTokenPermission{ 865 &protobuf.CommunityTokenPermission{ 866 Id: "some-id", 867 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 868 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 869 ChatIds: []string{}, 870 }, 871 }, 872 members: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey}, 873 channelMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 874 expectedActions: EncryptionKeyActions{ 875 CommunityKeyAction: EncryptionKeyAction{ 876 ActionType: EncryptionKeyRekey, 877 Members: map[string]*protobuf.CommunityMember{ 878 s.member1Key: &protobuf.CommunityMember{LastUpdateClock: clock}, 879 s.member2Key: &protobuf.CommunityMember{LastUpdateClock: clock}, 880 }, 881 }, 882 ChannelKeysActions: map[string]EncryptionKeyAction{ 883 channelID: EncryptionKeyAction{ 884 ActionType: EncryptionKeyNone, 885 Members: map[string]*protobuf.CommunityMember{}, 886 }, 887 }, 888 }, 889 }, 890 { 891 name: "change control node in open community with token-gated channel", 892 permissions: []*protobuf.CommunityTokenPermission{ 893 &protobuf.CommunityTokenPermission{ 894 Id: "some-id", 895 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 896 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 897 ChatIds: []string{chatID}, 898 }, 899 }, 900 members: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey}, 901 channelMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 902 expectedActions: EncryptionKeyActions{ 903 CommunityKeyAction: EncryptionKeyAction{ 904 ActionType: EncryptionKeyNone, 905 Members: map[string]*protobuf.CommunityMember{}, 906 }, 907 ChannelKeysActions: map[string]EncryptionKeyAction{ 908 channelID: EncryptionKeyAction{ 909 ActionType: EncryptionKeyRekey, 910 Members: map[string]*protobuf.CommunityMember{ 911 s.member1Key: &protobuf.CommunityMember{}, 912 }, 913 }, 914 }, 915 }, 916 }, 917 { 918 name: "change control node in token-gated community with token-gated channel", 919 permissions: []*protobuf.CommunityTokenPermission{ 920 &protobuf.CommunityTokenPermission{ 921 Id: "some-id-1", 922 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 923 TokenCriteria: make([]*protobuf.TokenCriteria, 0), 924 ChatIds: []string{}, 925 }, 926 &protobuf.CommunityTokenPermission{ 927 Id: "some-id-2", 928 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 929 TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}}, 930 ChatIds: []string{chatID}, 931 }, 932 }, 933 members: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey}, 934 channelMembers: []*ecdsa.PublicKey{&s.member1.PublicKey}, 935 expectedActions: EncryptionKeyActions{ 936 CommunityKeyAction: EncryptionKeyAction{ 937 ActionType: EncryptionKeyRekey, 938 Members: map[string]*protobuf.CommunityMember{ 939 s.member1Key: &protobuf.CommunityMember{ 940 LastUpdateClock: clock, 941 }, 942 s.member2Key: &protobuf.CommunityMember{ 943 LastUpdateClock: clock, 944 }, 945 }, 946 }, 947 ChannelKeysActions: map[string]EncryptionKeyAction{ 948 channelID: EncryptionKeyAction{ 949 ActionType: EncryptionKeyRekey, 950 Members: map[string]*protobuf.CommunityMember{ 951 s.member1Key: &protobuf.CommunityMember{}, 952 }, 953 }, 954 }, 955 }, 956 }, 957 } 958 959 for _, tc := range testCases { 960 s.Run(tc.name, func() { 961 origin, err := createTestCommunity(s.identity) 962 s.Require().NoError(err) 963 964 _, err = origin.CreateChat(channelID, &protobuf.CommunityChat{ 965 Members: map[string]*protobuf.CommunityMember{}, 966 Permissions: &protobuf.CommunityPermissions{Access: protobuf.CommunityPermissions_AUTO_ACCEPT}, 967 Identity: &protobuf.ChatIdentity{}, 968 }) 969 s.Require().NoError(err) 970 971 for _, permission := range tc.permissions { 972 _, err := origin.UpsertTokenPermission(permission) 973 s.Require().NoError(err) 974 } 975 for _, member := range tc.members { 976 _, err := origin.AddMember(member, []protobuf.CommunityMember_Roles{}, clock) 977 s.Require().NoError(err) 978 } 979 for _, member := range tc.channelMembers { 980 _, err = origin.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{}, protobuf.CommunityMember_CHANNEL_ROLE_POSTER) 981 s.Require().NoError(err) 982 } 983 984 // change control node to arbitrary member 985 modified := origin.CreateDeepCopy() 986 modified.setControlNode(&s.member1.PublicKey) 987 988 actions := EvaluateCommunityEncryptionKeyActions(origin, modified) 989 s.Require().True(reflect.DeepEqual(tc.expectedActions, *actions)) 990 }) 991 } 992 }