From b0213e6a41f10fccd2d7fdc4950fc9c9db54c2e6 Mon Sep 17 00:00:00 2001 From: Mikhail Rogachev Date: Fri, 7 Jun 2024 18:30:01 +0200 Subject: [PATCH] feat_: delete or update permission when deleting a channel (#5297) * feat_: delete or update channel permissions when deleting a channel * chore_: review fixes * chore_: review fixes (second iteration, squash it!) --- protocol/communities/community_changes.go | 48 +++++++++++++ .../communities/community_token_permission.go | 13 ++++ protocol/communities/manager.go | 38 +++++++++- ...nities_messenger_token_permissions_test.go | 70 +++++++++++++++++++ 4 files changed, 168 insertions(+), 1 deletion(-) diff --git a/protocol/communities/community_changes.go b/protocol/communities/community_changes.go index 551958089..4c2efcbe5 100644 --- a/protocol/communities/community_changes.go +++ b/protocol/communities/community_changes.go @@ -74,6 +74,54 @@ func EmptyCommunityChanges() *CommunityChanges { } } +func (c *CommunityChanges) Merge(other *CommunityChanges) { + for memberID, member := range other.MembersAdded { + c.MembersAdded[memberID] = member + } + for memberID := range other.MembersRemoved { + c.MembersRemoved[memberID] = other.MembersRemoved[memberID] + } + for memberID, banned := range other.MembersBanned { + c.MembersBanned[memberID] = banned + } + for memberID, unbanned := range other.MembersUnbanned { + c.MembersUnbanned[memberID] = unbanned + } + for permissionID, permission := range other.TokenPermissionsAdded { + c.TokenPermissionsAdded[permissionID] = permission + } + for permissionID, permission := range other.TokenPermissionsModified { + c.TokenPermissionsModified[permissionID] = permission + } + for permissionID, permission := range other.TokenPermissionsRemoved { + c.TokenPermissionsRemoved[permissionID] = permission + } + for chatID, chat := range other.ChatsRemoved { + c.ChatsRemoved[chatID] = chat + } + for chatID, chat := range other.ChatsAdded { + c.ChatsAdded[chatID] = chat + } + for chatID, changes := range other.ChatsModified { + c.ChatsModified[chatID] = changes + } + + c.CategoriesRemoved = append(c.CategoriesRemoved, other.CategoriesRemoved...) + + for categoryID, category := range other.CategoriesAdded { + c.CategoriesAdded[categoryID] = category + } + for categoryID, category := range other.CategoriesModified { + c.CategoriesModified[categoryID] = category + } + + c.MemberWalletsRemoved = append(c.MemberWalletsRemoved, other.MemberWalletsRemoved...) + + for walletID, wallets := range other.MemberWalletsAdded { + c.MemberWalletsAdded[walletID] = wallets + } +} + func (c *CommunityChanges) HasNewMember(identity string) bool { if len(c.MembersAdded) == 0 { return false diff --git a/protocol/communities/community_token_permission.go b/protocol/communities/community_token_permission.go index fc34f6aa1..ee78a75f6 100644 --- a/protocol/communities/community_token_permission.go +++ b/protocol/communities/community_token_permission.go @@ -2,6 +2,7 @@ package communities import ( "reflect" + "slices" "github.com/status-im/status-go/protocol/protobuf" ) @@ -46,6 +47,18 @@ func (p *CommunityTokenPermission) Equals(other *CommunityTokenPermission) bool return reflect.DeepEqual(p.ChatIds, other.ChatIds) } +func (p *CommunityTokenPermission) HasChat(chatId string) bool { + return slices.Contains(p.ChatIds, chatId) +} + +func (p *CommunityTokenPermission) ChatIdsAsMap() map[string]struct{} { + chats := map[string]struct{}{} + for _, id := range p.GetChatIds() { + chats[id] = struct{}{} + } + return chats +} + func compareTokenCriteria(a, b *protobuf.TokenCriteria) bool { if a == nil && b == nil { return true diff --git a/protocol/communities/manager.go b/protocol/communities/manager.go index 1f22784df..6fc249ec6 100644 --- a/protocol/communities/manager.go +++ b/protocol/communities/manager.go @@ -1768,14 +1768,50 @@ func (m *Manager) DeleteChat(communityID types.HexBytes, chatID string) (*Commun return nil, nil, err } + // Check for channel permissions + changes := community.emptyCommunityChanges() + for tokenPermissionID, tokenPermission := range community.tokenPermissions() { + chats := tokenPermission.ChatIdsAsMap() + _, hasChat := chats[chatID] + if !hasChat { + continue + } + + if len(chats) == 1 { + // Delete channel permission, if there is only one channel + deletePermissionChanges, err := community.DeleteTokenPermission(tokenPermissionID) + if err != nil { + return nil, nil, err + } + changes.Merge(deletePermissionChanges) + } else { + // Remove the channel from the permission, if there are other channels + delete(chats, chatID) + + var chatIDs []string + for chatID := range chats { + chatIDs = append(chatIDs, chatID) + } + tokenPermission.ChatIds = chatIDs + + updatePermissionChanges, err := community.UpsertTokenPermission(tokenPermission.CommunityTokenPermission) + if err != nil { + return nil, nil, err + } + changes.Merge(updatePermissionChanges) + } + } + // Remove communityID prefix from chatID if exists if strings.HasPrefix(chatID, communityID.String()) { chatID = strings.TrimPrefix(chatID, communityID.String()) } - changes, err := community.DeleteChat(chatID) + + deleteChanges, err := community.DeleteChat(chatID) if err != nil { return nil, nil, err } + changes.Merge(deleteChanges) err = m.saveAndPublish(community) if err != nil { diff --git a/protocol/communities_messenger_token_permissions_test.go b/protocol/communities_messenger_token_permissions_test.go index 260c6bebc..dbd34e8b7 100644 --- a/protocol/communities_messenger_token_permissions_test.go +++ b/protocol/communities_messenger_token_permissions_test.go @@ -2368,3 +2368,73 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestImportDecryptedArchiveMe s.Require().True(ok) s.Require().Equal(messageText1, receivedMessage1.Text) } + +func (s *MessengerCommunitiesTokenPermissionsSuite) TestDeleteChannelWithTokenPermission() { + // Setup community with two permitted channels + community, firstChat := s.createCommunity() + + response, err := s.owner.CreateCommunityChat(community.ID(), &protobuf.CommunityChat{ + Permissions: &protobuf.CommunityPermissions{ + Access: protobuf.CommunityPermissions_AUTO_ACCEPT, + }, + Identity: &protobuf.ChatIdentity{ + DisplayName: "new channel", + Emoji: "", + Description: "chat created after joining the community", + }, + }) + s.Require().NoError(err) + s.Require().Len(response.Chats(), 1) + secondChat := response.Chats()[0] + + channelPermission := &requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL, + ChatIds: []string{firstChat.ID, secondChat.ID}, + TokenCriteria: []*protobuf.TokenCriteria{ + { + Type: protobuf.CommunityTokenType_ERC20, + ContractAddresses: map[uint64]string{testChainID1: "0x124"}, + Symbol: "TEST2", + AmountInWei: "200000000000000000000", + Decimals: uint64(18), + }, + }, + } + + response, err = s.owner.CreateCommunityTokenPermission(channelPermission) + s.Require().NoError(err) + s.Require().NotNil(response) + s.Require().Len(response.Communities(), 1) + + // Make sure both channels are covered with permission + community, err = s.owner.GetCommunityByID(community.ID()) + s.Require().NoError(err) + s.Require().Len(community.Chats(), 2) + s.Require().Len(community.TokenPermissions(), 1) + for _, permission := range community.TokenPermissions() { + s.Require().Len(permission.ChatIds, 2) + s.Require().True(permission.HasChat(firstChat.ID)) + s.Require().True(permission.HasChat(secondChat.ID)) + } + + // Delete first community channel + response, err = s.owner.DeleteCommunityChat(community.ID(), firstChat.ID) + s.Require().NoError(err) + s.Require().Len(response.Communities(), 1) + community = response.Communities()[0] + s.Require().Len(community.Chats(), 1) + for _, permission := range community.TokenPermissions() { + s.Require().Len(permission.ChatIds, 1) + s.Require().False(permission.HasChat(firstChat.ID)) + s.Require().True(permission.HasChat(secondChat.ID)) + } + + // Delete second community channel + response, err = s.owner.DeleteCommunityChat(community.ID(), secondChat.ID) + s.Require().NoError(err) + s.Require().Len(response.Communities(), 1) + community = response.Communities()[0] + s.Require().Len(community.Chats(), 0) + s.Require().Len(community.TokenPermissions(), 0) +}