diff --git a/protocol/communities/community.go b/protocol/communities/community.go index 26ba34531..3e13a2190 100644 --- a/protocol/communities/community.go +++ b/protocol/communities/community.go @@ -507,14 +507,14 @@ func (o *Community) CreateChat(chatID string, chat *protobuf.CommunityChat) (*Co o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotAdmin } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToCreateChannelCommunityEvent(chatID, chat)) if err != nil { return nil, err @@ -526,7 +526,7 @@ func (o *Community) CreateChat(chatID string, chat *protobuf.CommunityChat) (*Co return nil, err } - if isOwner { + if isControlNode { o.increaseClock() } @@ -539,14 +539,14 @@ func (o *Community) EditChat(chatID string, chat *protobuf.CommunityChat) (*Comm o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotAdmin } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToEditChannelCommunityEvent(chatID, chat)) if err != nil { return nil, err @@ -558,7 +558,7 @@ func (o *Community) EditChat(chatID string, chat *protobuf.CommunityChat) (*Comm return nil, err } - if isOwner { + if isControlNode { o.increaseClock() } @@ -574,14 +574,14 @@ func (o *Community) DeleteChat(chatID string) (*CommunityChanges, error) { o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotAdmin } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToDeleteChannelCommunityEvent(chatID)) if err != nil { return nil, err @@ -590,7 +590,7 @@ func (o *Community) DeleteChat(chatID string) (*CommunityChanges, error) { changes := o.deleteChat(chatID) - if isOwner { + if isControlNode { o.increaseClock() } @@ -798,18 +798,18 @@ func (o *Community) RemoveUserFromOrg(pk *ecdsa.PublicKey) (*protobuf.CommunityD o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotAdmin } - if o.IsAdmin() && o.IsMemberOwnerOrAdmin(pk) { + if allowedToSendEvents && o.IsMemberOwnerOrAdmin(pk) { return nil, ErrCannotRemoveOwnerOrAdmin } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToKickCommunityMemberCommunityEvent(common.PubkeyToHex(pk))) if err != nil { return nil, err @@ -818,7 +818,7 @@ func (o *Community) RemoveUserFromOrg(pk *ecdsa.PublicKey) (*protobuf.CommunityD o.removeMemberFromOrg(pk) - if isOwner { + if isControlNode { o.increaseClock() } @@ -841,14 +841,14 @@ func (o *Community) UnbanUserFromCommunity(pk *ecdsa.PublicKey) (*protobuf.Commu o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotAdmin } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToUnbanCommunityMemberCommunityEvent(common.PubkeyToHex(pk))) if err != nil { return nil, err @@ -857,7 +857,7 @@ func (o *Community) UnbanUserFromCommunity(pk *ecdsa.PublicKey) (*protobuf.Commu o.unbanUserFromCommunity(pk) - if isOwner { + if isControlNode { o.increaseClock() } @@ -868,18 +868,18 @@ func (o *Community) BanUserFromCommunity(pk *ecdsa.PublicKey) (*protobuf.Communi o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotAdmin } - if o.IsAdmin() && o.IsMemberOwnerOrAdmin(pk) { + if allowedToSendEvents && o.IsMemberOwnerOrAdmin(pk) { return nil, ErrCannotBanOwnerOrAdmin } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToBanCommunityMemberCommunityEvent(common.PubkeyToHex(pk))) if err != nil { return nil, err @@ -888,7 +888,7 @@ func (o *Community) BanUserFromCommunity(pk *ecdsa.PublicKey) (*protobuf.Communi o.banUserFromCommunity(pk) - if isOwner { + if isControlNode { o.increaseClock() } @@ -1076,7 +1076,7 @@ func (o *Community) ValidateRequestToJoin(signer *ecdsa.PublicKey, request *prot defer o.mutex.Unlock() // If we are not admin, fuggetaboutit - if !o.IsOwnerOrAdmin() { + if !o.IsControlNode() && !o.HasPermissionToSendCommunityEvents() { return ErrNotAdmin } @@ -1103,7 +1103,7 @@ func (o *Community) ValidateEditSharedAddresses(signer *ecdsa.PublicKey, request defer o.mutex.Unlock() // If we are not owner, fuggetaboutit - if !o.IsOwner() { + if !o.IsControlNode() { return ErrNotOwner } @@ -1118,20 +1118,17 @@ func (o *Community) ValidateEditSharedAddresses(signer *ecdsa.PublicKey, request return nil } -func (o *Community) IsOwner() bool { - return o.IsMemberOwner(o.config.MemberIdentity) || o.IsControlNode() -} - +// We treat control node as an owner with community key func (o *Community) IsControlNode() bool { return o.config.PrivateKey != nil } -func (o *Community) IsAdmin() bool { - return o.IsMemberAdmin(o.config.MemberIdentity) +func (o *Community) IsOwnerWithoutCommunityKey() bool { + return o.config.PrivateKey == nil && o.IsMemberOwner(o.config.MemberIdentity) } -func (o *Community) IsOwnerOrAdmin() bool { - return o.IsOwner() || o.IsAdmin() +func (o *Community) HasPermissionToSendCommunityEvents() bool { + return !o.IsControlNode() && o.hasPermission(o.config.MemberIdentity, manageCommunityRolePermissions()) } func (o *Community) IsMemberOwner(publicKey *ecdsa.PublicKey) bool { @@ -1143,11 +1140,18 @@ func (o *Community) IsMemberAdmin(publicKey *ecdsa.PublicKey) bool { } func (o *Community) IsMemberOwnerOrAdmin(publicKey *ecdsa.PublicKey) bool { - return o.hasPermission(publicKey, ownerOrAdminRolePermissions()) + return o.hasPermission(publicKey, manageCommunityRolePermissions()) +} + +func manageCommunityRolePermissions() map[protobuf.CommunityMember_Roles]bool { + roles := make(map[protobuf.CommunityMember_Roles]bool) + roles[protobuf.CommunityMember_ROLE_OWNER] = true + roles[protobuf.CommunityMember_ROLE_ADMIN] = true + return roles } func canManageUsersRolePermissions() map[protobuf.CommunityMember_Roles]bool { - roles := ownerOrAdminRolePermissions() + roles := manageCommunityRolePermissions() roles[protobuf.CommunityMember_ROLE_MANAGE_USERS] = true return roles } @@ -1164,13 +1168,6 @@ func adminRolePermissions() map[protobuf.CommunityMember_Roles]bool { return roles } -func ownerOrAdminRolePermissions() map[protobuf.CommunityMember_Roles]bool { - roles := make(map[protobuf.CommunityMember_Roles]bool) - roles[protobuf.CommunityMember_ROLE_OWNER] = true - roles[protobuf.CommunityMember_ROLE_ADMIN] = true - return roles -} - func (o *Community) MemberRole(pubKey *ecdsa.PublicKey) protobuf.CommunityMember_Roles { if o.IsMemberOwner(pubKey) { return protobuf.CommunityMember_ROLE_OWNER @@ -1186,7 +1183,7 @@ func (o *Community) MemberRole(pubKey *ecdsa.PublicKey) protobuf.CommunityMember } func canDeleteMessageForEveryonePermissions() map[protobuf.CommunityMember_Roles]bool { - roles := ownerOrAdminRolePermissions() + roles := manageCommunityRolePermissions() roles[protobuf.CommunityMember_ROLE_MODERATE_CONTENT] = true return roles } @@ -1312,7 +1309,7 @@ func (o *Community) toBytes() ([]byte, error) { } // If we are not a control node, use the received serialized version - if !o.IsOwner() { + if !o.IsControlNode() { return o.config.MarshaledCommunityDescription, nil } @@ -1436,10 +1433,10 @@ func (o *Community) AddTokenPermission(permission *protobuf.CommunityTokenPermis o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin || (isAdmin && permission.Type == protobuf.CommunityTokenPermission_BECOME_ADMIN) { + if !isControlNode && !allowedToSendEvents || (allowedToSendEvents && permission.Type == protobuf.CommunityTokenPermission_BECOME_ADMIN) { return nil, ErrNotEnoughPermissions } @@ -1448,14 +1445,14 @@ func (o *Community) AddTokenPermission(permission *protobuf.CommunityTokenPermis return nil, err } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToCommunityTokenPermissionChangeCommunityEvent(permission)) if err != nil { return nil, err } } - if isOwner { + if isControlNode { o.increaseClock() } @@ -1466,10 +1463,10 @@ func (o *Community) UpdateTokenPermission(permissionID string, tokenPermission * o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin || (isAdmin && tokenPermission.Type == protobuf.CommunityTokenPermission_BECOME_ADMIN) { + if !isControlNode && !allowedToSendEvents || (allowedToSendEvents && tokenPermission.Type == protobuf.CommunityTokenPermission_BECOME_ADMIN) { return nil, ErrNotEnoughPermissions } @@ -1478,14 +1475,14 @@ func (o *Community) UpdateTokenPermission(permissionID string, tokenPermission * return nil, err } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToCommunityTokenPermissionChangeCommunityEvent(tokenPermission)) if err != nil { return nil, err } } - if isOwner { + if isControlNode { o.increaseClock() } @@ -1502,10 +1499,10 @@ func (o *Community) DeleteTokenPermission(permissionID string) (*CommunityChange return nil, ErrTokenPermissionNotFound } - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin || (isAdmin && permission.Type == protobuf.CommunityTokenPermission_BECOME_ADMIN) { + if !isControlNode && !allowedToSendEvents || (allowedToSendEvents && permission.Type == protobuf.CommunityTokenPermission_BECOME_ADMIN) { return nil, ErrNotEnoughPermissions } @@ -1514,14 +1511,14 @@ func (o *Community) DeleteTokenPermission(permissionID string) (*CommunityChange return nil, err } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToCommunityTokenPermissionDeleteCommunityEvent(permission)) if err != nil { return nil, err } } - if isOwner { + if isControlNode { o.increaseClock() } @@ -1720,7 +1717,7 @@ func (o *Community) CanManageUsers(pk *ecdsa.PublicKey) bool { o.mutex.Lock() defer o.mutex.Unlock() - if o.IsOwner() { + if o.IsControlNode() { return true } @@ -1737,7 +1734,7 @@ func (o *Community) CanDeleteMessageForEveryone(pk *ecdsa.PublicKey) bool { o.mutex.Lock() defer o.mutex.Unlock() - if o.IsOwner() { + if o.IsControlNode() { return true } @@ -1763,7 +1760,7 @@ func (o *Community) canJoin() bool { return false } - if o.IsOwner() { + if o.IsControlNode() { return true } @@ -1808,7 +1805,7 @@ func (o *Community) RequestsToJoin() []*RequestToJoin { } func (o *Community) AddMember(publicKey *ecdsa.PublicKey, roles []protobuf.CommunityMember_Roles) (*CommunityChanges, error) { - if !o.IsOwnerOrAdmin() { + if !o.IsControlNode() && !o.HasPermissionToSendCommunityEvents() { return nil, ErrNotAdmin } @@ -1832,7 +1829,7 @@ func (o *Community) AddMemberToChat(chatID string, publicKey *ecdsa.PublicKey, r o.mutex.Lock() defer o.mutex.Unlock() - if !o.IsOwnerOrAdmin() { + if !o.IsControlNode() && !o.HasPermissionToSendCommunityEvents() { return nil, ErrNotAuthorized } @@ -1875,7 +1872,7 @@ func (o *Community) AddMemberRevealedAccounts(memberID string, accounts []*proto o.mutex.Lock() defer o.mutex.Unlock() - if !o.IsOwnerOrAdmin() { + if !o.IsControlNode() && !o.HasPermissionToSendCommunityEvents() { return nil, ErrNotAdmin } @@ -1896,16 +1893,16 @@ func (o *Community) AddMemberWithRevealedAccounts(dbRequest *RequestToJoin, role o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotAdmin } changes := o.addMemberWithRevealedAccounts(dbRequest.PublicKey, roles, accounts, dbRequest.Clock) - if isAdmin { + if allowedToSendEvents { acceptedRequestsToJoin := make(map[string]*protobuf.CommunityRequestToJoin) acceptedRequestsToJoin[dbRequest.PublicKey] = dbRequest.ToCommunityRequestToJoinProtobuf() @@ -1919,7 +1916,7 @@ func (o *Community) AddMemberWithRevealedAccounts(dbRequest *RequestToJoin, role } } - if isOwner { + if isControlNode { o.increaseClock() } @@ -2171,14 +2168,14 @@ func (o *Community) DeclineRequestToJoin(dbRequest *RequestToJoin) error { o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return ErrNotAdmin } - if isAdmin { + if allowedToSendEvents { rejectedRequestsToJoin := make(map[string]*protobuf.CommunityRequestToJoin) rejectedRequestsToJoin[dbRequest.PublicKey] = dbRequest.ToCommunityRequestToJoinProtobuf() @@ -2192,7 +2189,7 @@ func (o *Community) DeclineRequestToJoin(dbRequest *RequestToJoin) error { } } - if isOwner { + if isControlNode { // typically, community's clock is increased implicitly when making changes // to it, however in this scenario there are no changes in the community, yet // we need to increase the clock to ensure the owner event is processed by other diff --git a/protocol/communities/community_categories.go b/protocol/communities/community_categories.go index 67ebdb8cf..13695c5bb 100644 --- a/protocol/communities/community_categories.go +++ b/protocol/communities/community_categories.go @@ -40,14 +40,14 @@ func (o *Community) CreateCategory(categoryID string, categoryName string, chatI o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotAdmin } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToCreateCategoryCommunityEvent(categoryID, categoryName, chatIDs)) if err != nil { return nil, err @@ -59,7 +59,7 @@ func (o *Community) CreateCategory(categoryID string, categoryName string, chatI return nil, err } - if isOwner { + if isControlNode { o.increaseClock() } @@ -80,14 +80,14 @@ func (o *Community) EditCategory(categoryID string, categoryName string, chatIDs o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotAdmin } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToEditCategoryCommunityEvent(categoryID, categoryName, chatIDs)) if err != nil { return nil, err @@ -100,7 +100,7 @@ func (o *Community) EditCategory(categoryID string, categoryName string, chatIDs return nil, err } - if isOwner { + if isControlNode { o.increaseClock() } @@ -121,14 +121,14 @@ func (o *Community) ReorderCategories(categoryID string, newPosition int) (*Comm o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotAdmin } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToReorderCategoryCommunityEvent(categoryID, newPosition)) if err != nil { return nil, err @@ -140,7 +140,7 @@ func (o *Community) ReorderCategories(categoryID string, newPosition int) (*Comm return nil, err } - if isOwner { + if isControlNode { o.increaseClock() } @@ -161,14 +161,14 @@ func (o *Community) ReorderChat(categoryID string, chatID string, newPosition in o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotAdmin } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToReorderChannelCommunityEvent(categoryID, chatID, newPosition)) if err != nil { return nil, err @@ -180,7 +180,7 @@ func (o *Community) ReorderChat(categoryID string, chatID string, newPosition in return nil, err } - if isOwner { + if isControlNode { o.increaseClock() } @@ -316,14 +316,14 @@ func (o *Community) DeleteCategory(categoryID string) (*CommunityChanges, error) o.mutex.Lock() defer o.mutex.Unlock() - isOwner := o.IsOwner() - isAdmin := o.IsAdmin() + isControlNode := o.IsControlNode() + allowedToSendEvents := o.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotAdmin } - if isAdmin { + if allowedToSendEvents { err := o.addNewCommunityEvent(o.ToDeleteCategoryCommunityEvent(categoryID)) if err != nil { return nil, err @@ -335,7 +335,7 @@ func (o *Community) DeleteCategory(categoryID string) (*CommunityChanges, error) return nil, err } - if isOwner { + if isControlNode { o.increaseClock() } diff --git a/protocol/communities/manager.go b/protocol/communities/manager.go index 8b9cd812e..e84ea4761 100644 --- a/protocol/communities/manager.go +++ b/protocol/communities/manager.go @@ -787,10 +787,10 @@ func (m *Manager) EditCommunity(request *requests.EditCommunity) (*Community, er return nil, ErrOrgNotFound } - isOwner := community.IsOwner() - isAdmin := community.IsAdmin() + isControlNode := community.IsControlNode() + allowedToSendEvents := community.HasPermissionToSendCommunityEvents() - if !isOwner && !isAdmin { + if !isControlNode && !allowedToSendEvents { return nil, ErrNotEnoughPermissions } @@ -823,7 +823,7 @@ func (m *Manager) EditCommunity(request *requests.EditCommunity) (*Community, er return nil, err } - if isAdmin { + if allowedToSendEvents { err := community.addNewCommunityEvent(community.ToCommunityEditCommunityEvent(newDescription)) if err != nil { return nil, err @@ -836,7 +836,7 @@ func (m *Manager) EditCommunity(request *requests.EditCommunity) (*Community, er return nil, err } - if isOwner { + if isControlNode { community.increaseClock() } @@ -1300,9 +1300,9 @@ func (m *Manager) HandleCommunityEventsMessage(signer *ecdsa.PublicKey, message return nil, err } - // Owner cerifies admins events and publish changes - // all other users only apply changes to the community - if changes.Community.IsOwner() { + // Control node cerifies community events and publish changes + // all other nodes only apply changes to the community + if changes.Community.IsControlNode() { changes.Community.increaseClock() err = m.persistence.SaveCommunity(changes.Community) if err != nil { @@ -1798,7 +1798,7 @@ func (m *Manager) HandleCommunityEditSharedAddresses(signer *ecdsa.PublicKey, re return err } - if community.IsOwner() { + if community.IsControlNode() { m.publish(&Subscription{Community: community}) } @@ -2331,13 +2331,13 @@ func (m *Manager) HandleCommunityRequestToJoinResponse(signer *ecdsa.PublicKey, return nil, err } - isAdminSigner := community.IsMemberAdmin(signer) - isOwnerSigner := common.IsPubKeyEqual(community.PublicKey(), signer) - if !isOwnerSigner && !isAdminSigner { + isOwnerOrAdminSigner := community.IsMemberOwnerOrAdmin(signer) + isControlNodeSigner := common.IsPubKeyEqual(community.PublicKey(), signer) + if !isControlNodeSigner && !isOwnerOrAdminSigner { return nil, ErrNotAuthorized } - _, err = community.UpdateCommunityDescription(request.Community, appMetadataMsg, isAdminSigner) + _, err = community.UpdateCommunityDescription(request.Community, appMetadataMsg, isOwnerOrAdminSigner && !isControlNodeSigner) if err != nil { return nil, err } @@ -4012,12 +4012,10 @@ func (m *Manager) saveAndPublish(community *Community) error { return err } - if community.IsOwner() { + if community.IsControlNode() { m.publish(&Subscription{Community: community}) return nil - } - - if community.IsAdmin() { + } else if community.HasPermissionToSendCommunityEvents() { err := m.persistence.SaveCommunityEvents(community) if err != nil { return err diff --git a/protocol/communities_events_owner_without_community_key_test.go b/protocol/communities_events_owner_without_community_key_test.go new file mode 100644 index 000000000..257ed25ac --- /dev/null +++ b/protocol/communities_events_owner_without_community_key_test.go @@ -0,0 +1,280 @@ +package protocol + +import ( + "crypto/ecdsa" + "testing" + + "github.com/stretchr/testify/suite" + "go.uber.org/zap" + + gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/common" + "github.com/status-im/status-go/protocol/protobuf" + "github.com/status-im/status-go/protocol/requests" + "github.com/status-im/status-go/protocol/tt" + "github.com/status-im/status-go/waku" +) + +func TestOwnerWithoutCommunityKeyCommunityEventsSuite(t *testing.T) { + suite.Run(t, new(OwnerWithoutCommunityKeyCommunityEventsSuite)) +} + +type OwnerWithoutCommunityKeyCommunityEventsSuite struct { + suite.Suite + controlNode *Messenger + ownerWithoutCommunityKey *Messenger + alice *Messenger + // If one wants to send messages between different instances of Messenger, + // a single Waku service should be shared. + shh types.Waku + logger *zap.Logger +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) GetControlNode() *Messenger { + return s.controlNode +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) GetEventSender() *Messenger { + return s.ownerWithoutCommunityKey +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) GetMember() *Messenger { + return s.alice +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) GetSuite() *suite.Suite { + return &s.Suite +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) SetupTest() { + s.logger = tt.MustCreateTestLogger() + + config := waku.DefaultConfig + config.MinimumAcceptedPoW = 0 + shh := waku.New(&config, s.logger) + s.shh = gethbridge.NewGethWakuWrapper(shh) + s.Require().NoError(shh.Start()) + + s.controlNode = s.newMessenger() + s.ownerWithoutCommunityKey = s.newMessenger() + s.alice = s.newMessenger() + _, err := s.controlNode.Start() + s.Require().NoError(err) + _, err = s.ownerWithoutCommunityKey.Start() + s.Require().NoError(err) + _, err = s.alice.Start() + s.Require().NoError(err) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TearDownTest() { + s.Require().NoError(s.controlNode.Shutdown()) + s.Require().NoError(s.ownerWithoutCommunityKey.Shutdown()) + s.Require().NoError(s.alice.Shutdown()) + _ = s.logger.Sync() +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger { + messenger, err := newCommunitiesTestMessenger(shh, privateKey, s.logger, nil, nil) + s.Require().NoError(err) + + return messenger +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) newMessenger() *Messenger { + privateKey, err := crypto.GenerateKey() + s.Require().NoError(err) + + return s.newMessengerWithKey(s.shh, privateKey) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerEditCommunityDescription() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + editCommunityDescription(s, community) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerCreateEditDeleteChannels() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + + testCreateEditDeleteChannels(s, community) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerCreateEditDeleteBecomeMemberPermission() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + testCreateEditDeleteBecomeMemberPermission(s, community) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerCannotCreateBecomeAdminPermission() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + + permissionRequest := createTestPermissionRequest(community) + permissionRequest.Type = protobuf.CommunityTokenPermission_BECOME_ADMIN + + response, err := s.ownerWithoutCommunityKey.CreateCommunityTokenPermission(permissionRequest) + s.Require().Nil(response) + s.Require().Error(err) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerCannotEditBecomeAdminPermission() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + permissionRequest := createTestPermissionRequest(community) + permissionRequest.Type = protobuf.CommunityTokenPermission_BECOME_ADMIN + + // control node creates BECOME_ADMIN permission + response, err := s.controlNode.CreateCommunityTokenPermission(permissionRequest) + s.Require().NoError(err) + + var tokenPermissionID string + for id := range response.CommunityChanges[0].TokenPermissionsAdded { + tokenPermissionID = id + } + s.Require().NotEqual(tokenPermissionID, "") + + ownerCommunity, err := s.controlNode.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + assertCheckTokenPermissionCreated(&s.Suite, ownerCommunity) + + // then, ensure event sender receives updated community + _, err = WaitOnMessengerResponse( + s.ownerWithoutCommunityKey, + func(r *MessengerResponse) bool { return len(r.Communities()) > 0 }, + "event sender did not receive updated community", + ) + s.Require().NoError(err) + eventSenderCommunity, err := s.ownerWithoutCommunityKey.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + assertCheckTokenPermissionCreated(&s.Suite, eventSenderCommunity) + + permissionRequest.TokenCriteria[0].Symbol = "UPDATED" + permissionRequest.TokenCriteria[0].Amount = "200" + + permissionEditRequest := &requests.EditCommunityTokenPermission{ + PermissionID: tokenPermissionID, + CreateCommunityTokenPermission: *permissionRequest, + } + + // then, event sender tries to edit permission + response, err = s.ownerWithoutCommunityKey.EditCommunityTokenPermission(permissionEditRequest) + s.Require().Error(err) + s.Require().Nil(response) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerCannotDeleteBecomeAdminPermission() { + + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + permissionRequest := createTestPermissionRequest(community) + permissionRequest.Type = protobuf.CommunityTokenPermission_BECOME_ADMIN + + // control node creates BECOME_ADMIN permission + response, err := s.controlNode.CreateCommunityTokenPermission(permissionRequest) + s.Require().NoError(err) + + var tokenPermissionID string + for id := range response.CommunityChanges[0].TokenPermissionsAdded { + tokenPermissionID = id + } + s.Require().NotEqual(tokenPermissionID, "") + + // then, ensure event sender receives updated community + _, err = WaitOnMessengerResponse( + s.ownerWithoutCommunityKey, + func(r *MessengerResponse) bool { return len(r.Communities()) > 0 }, + "event sender did not receive updated community", + ) + s.Require().NoError(err) + eventSenderCommunity, err := s.ownerWithoutCommunityKey.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + assertCheckTokenPermissionCreated(&s.Suite, eventSenderCommunity) + + deleteTokenPermission := &requests.DeleteCommunityTokenPermission{ + CommunityID: community.ID(), + PermissionID: tokenPermissionID, + } + + // then event sender tries to delete BECOME_ADMIN permission which should fail + response, err = s.ownerWithoutCommunityKey.DeleteCommunityTokenPermission(deleteTokenPermission) + s.Require().Error(err) + s.Require().Nil(response) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerAcceptMemberRequestToJoin() { + community := setUpOnRequestCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + + // set up additional user that will send request to join + user := s.newMessenger() + testAcceptMemberRequestToJoin(s, community, user) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerRejectMemberRequestToJoin() { + community := setUpOnRequestCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + + // set up additional user that will send request to join + user := s.newMessenger() + testRejectMemberRequestToJoin(s, community, user) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerCreateEditDeleteCategories() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + testCreateEditDeleteCategories(s, community) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerReorderChannelsAndCategories() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + testReorderChannelsAndCategories(s, community) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerKickOwnerWithoutCommunityKey() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + testEventSenderKickTheSameRole(s, community) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerKickControlNode() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + testEventSenderKickControlNode(s, community) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerKickMember() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + kickMember(s, community.ID(), common.PubkeyToHex(&s.alice.identity.PublicKey)) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerBanOwnerWithoutCommunityKey() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + testOwnerBanTheSameRole(s, community) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestAdminBanControlNode() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + testOwnerBanControlNode(s, community) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerBanUnbanMember() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + testBanUnbanMember(s, community) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerDeleteAnyMessageInTheCommunity() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + testDeleteAnyMessageInTheCommunity(s, community) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerPinMessage() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + testEventSenderPinMessage(s, community) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerMintToken() { + setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + // TODO owner test: Mint Tokens (rescticted) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerAirdropTokens() { + setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + // TODO owner test: Airdrop Tokens (restricted) +} + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestMemberReceiveOwnerEventsWhenControlNodeOffline() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + testMemberReceiveEventsWhenControlNodeOffline(s, community) +} diff --git a/protocol/communities_events_utils_test.go b/protocol/communities_events_utils_test.go new file mode 100644 index 000000000..86aa430a3 --- /dev/null +++ b/protocol/communities_events_utils_test.go @@ -0,0 +1,1133 @@ +package protocol + +import ( + "context" + "errors" + + "github.com/stretchr/testify/suite" + + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/common" + "github.com/status-im/status-go/protocol/communities" + "github.com/status-im/status-go/protocol/protobuf" + "github.com/status-im/status-go/protocol/requests" +) + +type CommunityEventsTestsInterface interface { + GetControlNode() *Messenger + GetEventSender() *Messenger + GetMember() *Messenger + GetSuite() *suite.Suite +} + +type MessageResponseValidator func(*MessengerResponse) error +type WaitResponseValidator func(*MessengerResponse) bool + +func WaitCommunityCondition(r *MessengerResponse) bool { + return len(r.Communities()) > 0 +} + +func WaitMessageCondition(response *MessengerResponse) bool { + return len(response.Messages()) > 0 +} + +func waitOnMessengerResponse(s *suite.Suite, fnWait WaitResponseValidator, fn MessageResponseValidator, user *Messenger) { + response, err := WaitOnMessengerResponse( + user, + fnWait, + "MessengerResponse data not received", + ) + s.Require().NoError(err) + s.Require().NoError(fn(response)) +} + +func checkClientsReceivedAdminEvent(base CommunityEventsTestsInterface, fnWait WaitResponseValidator, fn MessageResponseValidator) { + s := base.GetSuite() + // Wait and verify Member received community event + waitOnMessengerResponse(s, fnWait, fn, base.GetMember()) + // Wait and verify event sender received his own event + waitOnMessengerResponse(s, fnWait, fn, base.GetEventSender()) + // Wait and verify ControlNode received community event + // ControlNode will publish CommunityDescription update + waitOnMessengerResponse(s, fnWait, fn, base.GetControlNode()) + // Wait and verify Member received the ControlNode CommunityDescription update + waitOnMessengerResponse(s, fnWait, fn, base.GetMember()) + // Wait and verify event sender received the ControlNode CommunityDescription update + waitOnMessengerResponse(s, fnWait, fn, base.GetEventSender()) + // Wait and verify ControlNode received his own CommunityDescription update + waitOnMessengerResponse(s, fnWait, fn, base.GetControlNode()) +} + +func refreshMessengerResponses(base CommunityEventsTestsInterface) { + _, err := WaitOnMessengerResponse(base.GetControlNode(), func(response *MessengerResponse) bool { + return true + }, "community description changed message not received") + base.GetSuite().Require().NoError(err) + + _, err = WaitOnMessengerResponse(base.GetEventSender(), func(response *MessengerResponse) bool { + return true + }, "community description changed message not received") + base.GetSuite().Require().NoError(err) + + _, err = WaitOnMessengerResponse(base.GetMember(), func(response *MessengerResponse) bool { + return true + }, "community description changed message not received") + base.GetSuite().Require().NoError(err) +} + +func setUpCommunityAndRoles(base CommunityEventsTestsInterface, role protobuf.CommunityMember_Roles) *communities.Community { + tcs2, err := base.GetControlNode().communitiesManager.All() + suite := base.GetSuite() + suite.Require().NoError(err, "eventSender.communitiesManager.All") + suite.Len(tcs2, 1, "Must have 1 community") + + // ControlNode creates a community and chat + community := createTestCommunity(base, protobuf.CommunityPermissions_NO_MEMBERSHIP) + refreshMessengerResponses(base) + + // add events sender and member to the community + advertiseCommunityTo(suite, community, base.GetControlNode(), base.GetEventSender()) + advertiseCommunityTo(suite, community, base.GetControlNode(), base.GetMember()) + + request := &requests.RequestToJoinCommunity{CommunityID: community.ID()} + joinCommunity(suite, community, base.GetControlNode(), base.GetEventSender(), request) + joinCommunity(suite, community, base.GetControlNode(), base.GetMember(), request) + + refreshMessengerResponses(base) + + // grant permissions to the event sender + grantPermission(suite, community, base.GetControlNode(), base.GetEventSender(), role) + refreshMessengerResponses(base) + + return community +} + +func createTestCommunity(base CommunityEventsTestsInterface, membershipType protobuf.CommunityPermissions_Access) *communities.Community { + description := &requests.CreateCommunity{ + Membership: membershipType, + Name: "status", + Color: "#ffffff", + Description: "status community description", + PinMessageAllMembersEnabled: false, + } + response, err := base.GetControlNode().CreateCommunity(description, true) + + suite := base.GetSuite() + suite.Require().NoError(err) + suite.Require().NotNil(response) + suite.Require().Len(response.Communities(), 1) + suite.Require().Len(response.Chats(), 1) + + return response.Communities()[0] +} + +func getModifiedCommunity(response *MessengerResponse, communityID string) (*communities.Community, error) { + if len(response.Communities()) == 0 { + return nil, errors.New("community not received") + } + + var modifiedCommmunity *communities.Community = nil + for _, c := range response.Communities() { + if c.IDString() == communityID { + modifiedCommmunity = c + } + } + + if modifiedCommmunity == nil { + return nil, errors.New("couldn't find community in response") + } + + return modifiedCommmunity, nil +} + +func createCommunityChannel(base CommunityEventsTestsInterface, community *communities.Community, newChannel *protobuf.CommunityChat) string { + checkChannelCreated := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) + if err != nil { + return err + } + + for _, chat := range modifiedCommmunity.Chats() { + if chat.GetIdentity().GetDisplayName() == newChannel.GetIdentity().GetDisplayName() { + return nil + } + } + + return errors.New("couldn't find created chat in response") + } + + response, err := base.GetEventSender().CreateCommunityChat(community.ID(), newChannel) + s := base.GetSuite() + s.Require().NoError(err) + s.Require().NoError(checkChannelCreated(response)) + s.Require().Len(response.CommunityChanges, 1) + s.Require().Len(response.CommunityChanges[0].ChatsAdded, 1) + var addedChatID string + for addedChatID = range response.CommunityChanges[0].ChatsAdded { + break + } + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkChannelCreated) + + return addedChatID +} + +func editCommunityChannel(base CommunityEventsTestsInterface, community *communities.Community, editChannel *protobuf.CommunityChat, channelID string) { + checkChannelEdited := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) + if err != nil { + return err + } + + for _, chat := range modifiedCommmunity.Chats() { + if chat.GetIdentity().GetDisplayName() == editChannel.GetIdentity().GetDisplayName() { + return nil + } + } + + return errors.New("couldn't find modified chat in response") + } + + response, err := base.GetEventSender().EditCommunityChat(community.ID(), channelID, editChannel) + s := base.GetSuite() + s.Require().NoError(err) + s.Require().NoError(checkChannelEdited(response)) + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkChannelEdited) +} + +func deleteCommunityChannel(base CommunityEventsTestsInterface, community *communities.Community, channelID string) { + checkChannelDeleted := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) + if err != nil { + return err + } + + if _, exists := modifiedCommmunity.Chats()[channelID]; exists { + return errors.New("channel was not deleted") + } + + return nil + } + + response, err := base.GetEventSender().DeleteCommunityChat(community.ID(), channelID) + s := base.GetSuite() + s.Require().NoError(err) + s.Require().NoError(checkChannelDeleted(response)) + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkChannelDeleted) +} + +func createTestPermissionRequest(community *communities.Community) *requests.CreateCommunityTokenPermission { + return &requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, + TokenCriteria: []*protobuf.TokenCriteria{ + { + Type: protobuf.CommunityTokenType_ERC20, + ContractAddresses: map[uint64]string{uint64(1): "0x123"}, + Symbol: "TEST", + Amount: "100", + Decimals: uint64(18), + }, + }, + } +} + +func createTokenPermission(base CommunityEventsTestsInterface, community *communities.Community, request *requests.CreateCommunityTokenPermission) (string, *requests.CreateCommunityTokenPermission) { + checkTokenPermissionCreation := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) + if err != nil { + return err + } + + if !modifiedCommmunity.HasTokenPermissions() { + return errors.New("new token permission was not found") + } + + return nil + } + + response, err := base.GetEventSender().CreateCommunityTokenPermission(request) + s := base.GetSuite() + s.Require().NoError(err) + s.Require().Nil(checkTokenPermissionCreation(response)) + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkTokenPermissionCreation) + + var tokenPermissionID string + for tokenPermissionID = range response.CommunityChanges[0].TokenPermissionsAdded { + break + } + + s.Require().NotEqual(tokenPermissionID, "") + + return tokenPermissionID, request +} + +func createTestTokenPermission(base CommunityEventsTestsInterface, community *communities.Community) (string, *requests.CreateCommunityTokenPermission) { + createTokenPermissionRequest := createTestPermissionRequest(community) + return createTokenPermission(base, community, createTokenPermissionRequest) +} + +func editTokenPermission(base CommunityEventsTestsInterface, community *communities.Community, request *requests.EditCommunityTokenPermission) { + s := base.GetSuite() + checkTokenPermissionEdit := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) + if err != nil { + return err + } + + assertCheckTokenPermissionEdited(s, modifiedCommmunity) + + return nil + } + + response, err := base.GetEventSender().EditCommunityTokenPermission(request) + s.Require().NoError(err) + s.Require().Nil(checkTokenPermissionEdit(response)) + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkTokenPermissionEdit) +} + +func assertCheckTokenPermissionEdited(s *suite.Suite, community *communities.Community) { + permissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER) + s.Require().Len(permissions, 1) + s.Require().Len(permissions[0].TokenCriteria, 1) + s.Require().Equal(permissions[0].TokenCriteria[0].Type, protobuf.CommunityTokenType_ERC20) + s.Require().Equal(permissions[0].TokenCriteria[0].Symbol, "UPDATED") + s.Require().Equal(permissions[0].TokenCriteria[0].Amount, "200") + s.Require().Equal(permissions[0].TokenCriteria[0].Decimals, uint64(18)) +} + +func deleteTokenPermission(base CommunityEventsTestsInterface, community *communities.Community, request *requests.DeleteCommunityTokenPermission) { + checkTokenPermissionDeleted := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) + if err != nil { + return err + } + + if modifiedCommmunity.HasTokenPermissions() { + return errors.New("token permission was not deleted") + } + + return nil + } + + response, err := base.GetEventSender().DeleteCommunityTokenPermission(request) + s := base.GetSuite() + s.Require().NoError(err) + s.Require().Nil(checkTokenPermissionDeleted(response)) + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkTokenPermissionDeleted) +} + +func assertCheckTokenPermissionCreated(s *suite.Suite, community *communities.Community) { + permissions := make([]*protobuf.CommunityTokenPermission, 0) + tokenPermissions := community.TokenPermissions() + for _, p := range tokenPermissions { + permissions = append(permissions, p) + } + s.Require().Len(permissions, 1) + s.Require().Len(permissions[0].TokenCriteria, 1) + s.Require().Equal(permissions[0].TokenCriteria[0].Type, protobuf.CommunityTokenType_ERC20) + s.Require().Equal(permissions[0].TokenCriteria[0].Symbol, "TEST") + s.Require().Equal(permissions[0].TokenCriteria[0].Amount, "100") + s.Require().Equal(permissions[0].TokenCriteria[0].Decimals, uint64(18)) +} + +func setUpOnRequestCommunityAndRoles(base CommunityEventsTestsInterface, role protobuf.CommunityMember_Roles) *communities.Community { + tcs2, err := base.GetControlNode().communitiesManager.All() + s := base.GetSuite() + s.Require().NoError(err, "eventSender.communitiesManager.All") + s.Len(tcs2, 1, "Must have 1 community") + + // control node creates a community and chat + community := createTestCommunity(base, protobuf.CommunityPermissions_ON_REQUEST) + advertiseCommunityTo(s, community, base.GetControlNode(), base.GetEventSender()) + advertiseCommunityTo(s, community, base.GetControlNode(), base.GetMember()) + + refreshMessengerResponses(base) + + joinOnRequestCommunity(s, community, base.GetControlNode(), base.GetEventSender()) + joinOnRequestCommunity(s, community, base.GetControlNode(), base.GetMember()) + + refreshMessengerResponses(base) + + // grant permissions to event sender + grantPermission(s, community, base.GetControlNode(), base.GetEventSender(), role) + refreshMessengerResponses(base) + + return community +} + +func createCommunityCategory(base CommunityEventsTestsInterface, community *communities.Community, newCategory *requests.CreateCommunityCategory) string { + checkCategoryCreated := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) + if err != nil { + return err + } + + for _, category := range modifiedCommmunity.Categories() { + if category.GetName() == newCategory.CategoryName { + return nil + } + } + + return errors.New("couldn't find created Category in the response") + } + + response, err := base.GetEventSender().CreateCommunityCategory(newCategory) + + s := base.GetSuite() + s.Require().NoError(err) + s.Require().NoError(checkCategoryCreated(response)) + s.Require().Len(response.Communities(), 1) + s.Require().Len(response.CommunityChanges[0].CategoriesAdded, 1) + + var categoryID string + for categoryID = range response.CommunityChanges[0].CategoriesAdded { + break + } + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkCategoryCreated) + + return categoryID +} + +func editCommunityCategory(base CommunityEventsTestsInterface, communityID string, editCategory *requests.EditCommunityCategory) { + checkCategoryEdited := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, communityID) + if err != nil { + return err + } + + for _, category := range modifiedCommmunity.Categories() { + if category.GetName() == editCategory.CategoryName { + return nil + } + } + + return errors.New("couldn't find edited Category in the response") + } + + response, err := base.GetEventSender().EditCommunityCategory(editCategory) + + s := base.GetSuite() + s.Require().NoError(err) + s.Require().NoError(checkCategoryEdited(response)) + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkCategoryEdited) +} + +func deleteCommunityCategory(base CommunityEventsTestsInterface, communityID string, deleteCategory *requests.DeleteCommunityCategory) { + checkCategoryDeleted := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, communityID) + if err != nil { + return err + } + + if _, exists := modifiedCommmunity.Chats()[deleteCategory.CategoryID]; exists { + return errors.New("community was not deleted") + } + + return nil + } + + response, err := base.GetEventSender().DeleteCommunityCategory(deleteCategory) + + s := base.GetSuite() + s.Require().NoError(err) + s.Require().NoError(checkCategoryDeleted(response)) + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkCategoryDeleted) +} + +func reorderCategory(base CommunityEventsTestsInterface, reorderRequest *requests.ReorderCommunityCategories) { + checkCategoryReorder := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, types.EncodeHex(reorderRequest.CommunityID)) + if err != nil { + return err + } + + category, exist := modifiedCommmunity.Categories()[reorderRequest.CategoryID] + if !exist { + return errors.New("couldn't find community category") + } + + if int(category.Position) != reorderRequest.Position { + return errors.New("category was not reordered") + } + + return nil + } + + response, err := base.GetEventSender().ReorderCommunityCategories(reorderRequest) + + s := base.GetSuite() + s.Require().NoError(err) + s.Require().NoError(checkCategoryReorder(response)) + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkCategoryReorder) +} + +func reorderChannel(base CommunityEventsTestsInterface, reorderRequest *requests.ReorderCommunityChat) { + checkChannelReorder := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, types.EncodeHex(reorderRequest.CommunityID)) + if err != nil { + return err + } + + chat, exist := modifiedCommmunity.Chats()[reorderRequest.ChatID] + if !exist { + return errors.New("couldn't find community chat") + } + + if int(chat.Position) != reorderRequest.Position { + return errors.New("chat position was not reordered") + } + + if chat.CategoryId != reorderRequest.CategoryID { + return errors.New("chat category was not reordered") + } + + return nil + } + + response, err := base.GetEventSender().ReorderCommunityChat(reorderRequest) + + s := base.GetSuite() + s.Require().NoError(err) + s.Require().NoError(checkChannelReorder(response)) + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkChannelReorder) +} + +func kickMember(base CommunityEventsTestsInterface, communityID types.HexBytes, pubkey string) { + checkKicked := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, types.EncodeHex(communityID)) + if err != nil { + return err + } + + if modifiedCommmunity.HasMember(&base.GetMember().identity.PublicKey) { + return errors.New("alice was not kicked") + } + + return nil + } + + response, err := base.GetEventSender().RemoveUserFromCommunity( + communityID, + pubkey, + ) + + s := base.GetSuite() + s.Require().NoError(err) + s.Require().Nil(checkKicked(response)) + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkKicked) +} + +func banMember(base CommunityEventsTestsInterface, banRequest *requests.BanUserFromCommunity) { + checkBanned := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, types.EncodeHex(banRequest.CommunityID)) + if err != nil { + return err + } + + if modifiedCommmunity.HasMember(&base.GetMember().identity.PublicKey) { + return errors.New("alice was not removed from the member list") + } + + if !modifiedCommmunity.IsBanned(&base.GetMember().identity.PublicKey) { + return errors.New("alice was not added to the banned list") + } + + return nil + } + + response, err := base.GetEventSender().BanUserFromCommunity(banRequest) + + s := base.GetSuite() + s.Require().NoError(err) + s.Require().Nil(checkBanned(response)) + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkBanned) +} + +func unbanMember(base CommunityEventsTestsInterface, unbanRequest *requests.UnbanUserFromCommunity) { + checkUnbanned := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, types.EncodeHex(unbanRequest.CommunityID)) + if err != nil { + return err + } + + if modifiedCommmunity.IsBanned(&base.GetMember().identity.PublicKey) { + return errors.New("alice was not unbanned") + } + + return nil + } + + response, err := base.GetEventSender().UnbanUserFromCommunity(unbanRequest) + + s := base.GetSuite() + s.Require().NoError(err) + s.Require().Nil(checkUnbanned(response)) + + response, err = WaitOnMessengerResponse( + base.GetControlNode(), + WaitCommunityCondition, + "MessengerResponse data not received", + ) + s.Require().NoError(err) + s.Require().NoError(checkUnbanned(response)) +} + +func controlNodeSendMessage(base CommunityEventsTestsInterface, inputMessage *common.Message) string { + response, err := base.GetControlNode().SendChatMessage(context.Background(), inputMessage) + + s := base.GetSuite() + s.Require().NoError(err) + message := response.Messages()[0] + s.Require().Equal(inputMessage.Text, message.Text) + messageID := message.ID + + response, err = WaitOnMessengerResponse(base.GetEventSender(), WaitMessageCondition, "messages not received") + s.Require().NoError(err) + message = response.Messages()[0] + s.Require().Equal(inputMessage.Text, message.Text) + + response, err = WaitOnMessengerResponse(base.GetMember(), WaitMessageCondition, "messages not received") + s.Require().NoError(err) + message = response.Messages()[0] + s.Require().Equal(inputMessage.Text, message.Text) + + refreshMessengerResponses(base) + + return messageID +} + +func deleteControlNodeMessage(base CommunityEventsTestsInterface, messageID string) { + checkMessageDeleted := func(response *MessengerResponse) error { + if len(response.RemovedMessages()) > 0 { + return nil + } + return errors.New("message was not deleted") + } + + response, err := base.GetEventSender().DeleteMessageAndSend(context.Background(), messageID) + + s := base.GetSuite() + s.Require().NoError(err) + s.Require().NoError(checkMessageDeleted(response)) + + waitMessageCondition := func(response *MessengerResponse) bool { + return len(response.RemovedMessages()) > 0 + } + waitOnMessengerResponse(s, waitMessageCondition, checkMessageDeleted, base.GetMember()) + waitOnMessengerResponse(s, waitMessageCondition, checkMessageDeleted, base.GetControlNode()) + +} + +func pinControlNodeMessage(base CommunityEventsTestsInterface, pinnedMessage *common.PinMessage) { + checkPinned := func(response *MessengerResponse) error { + if len(response.PinMessages()) > 0 { + return nil + } + return errors.New("pin messages was not added") + } + + response, err := base.GetEventSender().SendPinMessage(context.Background(), pinnedMessage) + s := base.GetSuite() + s.Require().NoError(err) + s.Require().NoError(checkPinned(response)) + + waitOnMessengerResponse(s, WaitMessageCondition, checkPinned, base.GetMember()) + waitOnMessengerResponse(s, WaitMessageCondition, checkPinned, base.GetControlNode()) +} + +func editCommunityDescription(base CommunityEventsTestsInterface, community *communities.Community) { + expectedName := "edited community name" + expectedColor := "#000000" + expectedDescr := "edited community description" + + response, err := base.GetEventSender().EditCommunity(&requests.EditCommunity{ + CommunityID: community.ID(), + CreateCommunity: requests.CreateCommunity{ + Membership: protobuf.CommunityPermissions_ON_REQUEST, + Name: expectedName, + Color: expectedColor, + Description: expectedDescr, + }, + }) + + checkCommunityEdit := func(response *MessengerResponse) error { + if len(response.Communities()) == 0 { + return errors.New("community not received") + } + + rCommunities := response.Communities() + if expectedName != rCommunities[0].Name() { + return errors.New("incorrect community name") + } + + if expectedColor != rCommunities[0].Color() { + return errors.New("incorrect community color") + } + + if expectedDescr != rCommunities[0].DescriptionText() { + return errors.New("incorrect community description") + } + + return nil + } + + s := base.GetSuite() + s.Require().NoError(err) + s.Require().Nil(checkCommunityEdit(response)) + + checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkCommunityEdit) +} + +func testCreateEditDeleteChannels(base CommunityEventsTestsInterface, community *communities.Community) { + newChat := &protobuf.CommunityChat{ + Permissions: &protobuf.CommunityPermissions{ + Access: protobuf.CommunityPermissions_NO_MEMBERSHIP, + }, + Identity: &protobuf.ChatIdentity{ + DisplayName: "chat from the event sender", + Emoji: "", + Description: "chat created by an event sender", + }, + } + + newChatID := createCommunityChannel(base, community, newChat) + + newChat.Identity.DisplayName = "modified chat from event sender" + editCommunityChannel(base, community, newChat, newChatID) + deleteCommunityChannel(base, community, newChatID) +} + +func testCreateEditDeleteBecomeMemberPermission(base CommunityEventsTestsInterface, community *communities.Community) { + // first, create token permission + tokenPermissionID, createTokenPermission := createTestTokenPermission(base, community) + + createTokenPermission.TokenCriteria[0].Symbol = "UPDATED" + createTokenPermission.TokenCriteria[0].Amount = "200" + + editTokenPermissionRequest := &requests.EditCommunityTokenPermission{ + PermissionID: tokenPermissionID, + CreateCommunityTokenPermission: *createTokenPermission, + } + + // then, event sender edits the permission + editTokenPermission(base, community, editTokenPermissionRequest) + + deleteTokenPermissionRequest := &requests.DeleteCommunityTokenPermission{ + CommunityID: community.ID(), + PermissionID: tokenPermissionID, + } + + // then, event sender deletes previously created token permission + deleteTokenPermission(base, community, deleteTokenPermissionRequest) +} + +func testAcceptMemberRequestToJoin(base CommunityEventsTestsInterface, community *communities.Community, user *Messenger) { + // set up additional user that will send request to join + _, err := user.Start() + + s := base.GetSuite() + + s.Require().NoError(err) + defer user.Shutdown() // nolint: errcheck + + advertiseCommunityTo(s, community, base.GetControlNode(), user) + + // user sends request to join + requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID()} + response, err := user.RequestToJoinCommunity(requestToJoin) + s.Require().NoError(err) + s.Require().NotNil(response) + s.Require().Len(response.RequestsToJoinCommunity, 1) + _ = response.RequestsToJoinCommunity[0] + + // event sender receives request to join + response, err = WaitOnMessengerResponse( + base.GetEventSender(), + func(r *MessengerResponse) bool { return len(r.RequestsToJoinCommunity) > 0 }, + "event sender did not receive community request to join", + ) + s.Require().NoError(err) + s.Require().Len(response.RequestsToJoinCommunity, 1) + + receivedRequest := response.RequestsToJoinCommunity[0] + + // event sender has not accepted request yet + eventSenderCommunity, err := base.GetEventSender().GetCommunityByID(community.ID()) + s.Require().NoError(err) + s.Require().False(eventSenderCommunity.HasMember(&user.identity.PublicKey)) + + acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: receivedRequest.ID} + response, err = base.GetEventSender().AcceptRequestToJoinCommunity(acceptRequestToJoin) + s.Require().NoError(err) + s.Require().NotNil(response) + s.Require().Len(response.Communities(), 1) + s.Require().True(response.Communities()[0].HasMember(&user.identity.PublicKey)) + + // user receives request to join response + response, err = WaitOnMessengerResponse( + user, + func(r *MessengerResponse) bool { return len(r.Communities()) > 0 }, + "user did not receive community request to join response", + ) + s.Require().NoError(err) + s.Require().Len(response.Communities(), 1) + s.Require().True(response.Communities()[0].HasMember(&user.identity.PublicKey)) + + // control node receives updated community + response, err = WaitOnMessengerResponse( + base.GetControlNode(), + func(r *MessengerResponse) bool { return len(r.Communities()) > 0 }, + "control node did not receive community request to join response", + ) + s.Require().NoError(err) + s.Require().Len(response.Communities(), 1) + + requests, err := base.GetControlNode().AcceptedRequestsToJoinForCommunity(community.ID()) + // there's now two requests to join (event sender and member) + 1 from user + s.Require().NoError(err) + s.Require().Len(requests, 3) + s.Require().True(response.Communities()[0].HasMember(&user.identity.PublicKey)) + + // member receives updated community + response, err = WaitOnMessengerResponse( + base.GetMember(), + func(r *MessengerResponse) bool { return len(r.Communities()) > 0 }, + "alice did not receive community request to join response", + ) + s.Require().NoError(err) + s.Require().Len(response.Communities(), 1) + s.Require().True(response.Communities()[0].HasMember(&user.identity.PublicKey)) +} + +func testRejectMemberRequestToJoin(base CommunityEventsTestsInterface, community *communities.Community, user *Messenger) { + _, err := user.Start() + + s := base.GetSuite() + s.Require().NoError(err) + defer user.Shutdown() // nolint: errcheck + + advertiseCommunityTo(s, community, base.GetControlNode(), user) + + // user sends request to join + requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID()} + response, err := user.RequestToJoinCommunity(requestToJoin) + s.Require().NoError(err) + s.Require().NotNil(response) + s.Require().Len(response.RequestsToJoinCommunity, 1) + + // event sender receives request to join + response, err = WaitOnMessengerResponse( + base.GetEventSender(), + func(r *MessengerResponse) bool { return len(r.RequestsToJoinCommunity) > 0 }, + "event sender did not receive community request to join", + ) + s.Require().NoError(err) + s.Require().Len(response.RequestsToJoinCommunity, 1) + + receivedRequest := response.RequestsToJoinCommunity[0] + + // event sender has not accepted request yet + eventSenderCommunity, err := base.GetEventSender().GetCommunityByID(community.ID()) + s.Require().NoError(err) + s.Require().False(eventSenderCommunity.HasMember(&user.identity.PublicKey)) + + // event sender rejects request to join + rejectRequestToJoin := &requests.DeclineRequestToJoinCommunity{ID: receivedRequest.ID} + _, err = base.GetEventSender().DeclineRequestToJoinCommunity(rejectRequestToJoin) + s.Require().NoError(err) + + eventSenderCommunity, err = base.GetEventSender().GetCommunityByID(community.ID()) + s.Require().NoError(err) + s.Require().False(eventSenderCommunity.HasMember(&user.identity.PublicKey)) + + // control node receives event sender event and stores rejected request to join + response, err = WaitOnMessengerResponse( + base.GetControlNode(), + func(r *MessengerResponse) bool { return len(r.Communities()) > 0 }, + "control node did not receive community request to join update from event sender", + ) + s.Require().NoError(err) + s.Require().False(response.Communities()[0].HasMember(&user.identity.PublicKey)) + + requests, err := base.GetControlNode().DeclinedRequestsToJoinForCommunity(community.ID()) + s.Require().Len(requests, 1) + s.Require().NoError(err) +} + +func testCreateEditDeleteCategories(base CommunityEventsTestsInterface, community *communities.Community) { + newCategory := &requests.CreateCommunityCategory{ + CommunityID: community.ID(), + CategoryName: "event-sender-category-name", + } + categoryID := createCommunityCategory(base, community, newCategory) + + editCategory := &requests.EditCommunityCategory{ + CommunityID: community.ID(), + CategoryID: categoryID, + CategoryName: "edited-event-sender-category-name", + } + + editCommunityCategory(base, community.IDString(), editCategory) + + deleteCategory := &requests.DeleteCommunityCategory{ + CommunityID: community.ID(), + CategoryID: categoryID, + } + + deleteCommunityCategory(base, community.IDString(), deleteCategory) +} + +func testReorderChannelsAndCategories(base CommunityEventsTestsInterface, community *communities.Community) { + newCategory := &requests.CreateCommunityCategory{ + CommunityID: community.ID(), + CategoryName: "event-sender-category-name", + } + _ = createCommunityCategory(base, community, newCategory) + + newCategory.CategoryName = "event-sender-category-name2" + categoryID2 := createCommunityCategory(base, community, newCategory) + + chat := &protobuf.CommunityChat{ + Permissions: &protobuf.CommunityPermissions{ + Access: protobuf.CommunityPermissions_NO_MEMBERSHIP, + }, + Identity: &protobuf.ChatIdentity{ + DisplayName: "chat from event-sender", + Emoji: "", + Description: "chat created by an event-sender", + }, + } + + chatID := createCommunityChannel(base, community, chat) + + reorderCommunityRequest := requests.ReorderCommunityCategories{ + CommunityID: community.ID(), + CategoryID: categoryID2, + Position: 0, + } + + reorderCategory(base, &reorderCommunityRequest) + + reorderChatRequest := requests.ReorderCommunityChat{ + CommunityID: community.ID(), + CategoryID: categoryID2, + ChatID: chatID, + Position: 0, + } + + reorderChannel(base, &reorderChatRequest) +} + +func testEventSenderKickTheSameRole(base CommunityEventsTestsInterface, community *communities.Community) { + // event sender tries to kick the member with the same role + _, err := base.GetEventSender().RemoveUserFromCommunity( + community.ID(), + common.PubkeyToHex(&base.GetEventSender().identity.PublicKey), + ) + + s := base.GetSuite() + s.Require().Error(err) + s.Require().EqualError(err, "not allowed to remove admin or owner") +} + +func testEventSenderKickControlNode(base CommunityEventsTestsInterface, community *communities.Community) { + // event sender tries to kick the control node + _, err := base.GetEventSender().RemoveUserFromCommunity( + community.ID(), + common.PubkeyToHex(&base.GetControlNode().identity.PublicKey), + ) + + s := base.GetSuite() + s.Require().Error(err) + s.Require().EqualError(err, "not allowed to remove admin or owner") +} + +func testOwnerBanTheSameRole(base CommunityEventsTestsInterface, community *communities.Community) { + _, err := base.GetEventSender().BanUserFromCommunity( + &requests.BanUserFromCommunity{ + CommunityID: community.ID(), + User: common.PubkeyToHexBytes(&base.GetEventSender().identity.PublicKey), + }, + ) + + s := base.GetSuite() + s.Require().Error(err) + s.Require().EqualError(err, "not allowed to ban admin or owner") +} + +func testOwnerBanControlNode(base CommunityEventsTestsInterface, community *communities.Community) { + _, err := base.GetEventSender().BanUserFromCommunity( + &requests.BanUserFromCommunity{ + CommunityID: community.ID(), + User: common.PubkeyToHexBytes(&base.GetControlNode().identity.PublicKey), + }, + ) + + s := base.GetSuite() + s.Require().Error(err) + s.Require().EqualError(err, "not allowed to ban admin or owner") +} + +func testBanUnbanMember(base CommunityEventsTestsInterface, community *communities.Community) { + // verify that event sender can't ban a control node + _, err := base.GetEventSender().BanUserFromCommunity( + &requests.BanUserFromCommunity{ + CommunityID: community.ID(), + User: common.PubkeyToHexBytes(&base.GetControlNode().identity.PublicKey), + }, + ) + s := base.GetSuite() + s.Require().Error(err) + + banRequest := &requests.BanUserFromCommunity{ + CommunityID: community.ID(), + User: common.PubkeyToHexBytes(&base.GetMember().identity.PublicKey), + } + + banMember(base, banRequest) + + unbanRequest := &requests.UnbanUserFromCommunity{ + CommunityID: community.ID(), + User: common.PubkeyToHexBytes(&base.GetMember().identity.PublicKey), + } + + unbanMember(base, unbanRequest) +} + +func testDeleteAnyMessageInTheCommunity(base CommunityEventsTestsInterface, community *communities.Community) { + chatID := community.ChatIDs()[0] + + inputMessage := common.Message{} + inputMessage.ChatId = chatID + inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN + inputMessage.Text = "control node text" + + messageID := controlNodeSendMessage(base, &inputMessage) + + deleteControlNodeMessage(base, messageID) +} + +func testEventSenderPinMessage(base CommunityEventsTestsInterface, community *communities.Community) { + s := base.GetSuite() + s.Require().False(community.AllowsAllMembersToPinMessage()) + chatID := community.ChatIDs()[0] + + inputMessage := common.Message{} + inputMessage.ChatId = chatID + inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN + inputMessage.Text = "control node text" + + messageID := controlNodeSendMessage(base, &inputMessage) + + pinnedMessage := common.PinMessage{} + pinnedMessage.MessageId = messageID + pinnedMessage.ChatId = chatID + pinnedMessage.Pinned = true + + pinControlNodeMessage(base, &pinnedMessage) +} + +func testMemberReceiveEventsWhenControlNodeOffline(base CommunityEventsTestsInterface, community *communities.Community) { + // To simulate behavior when control node is offline, we will not use control node for listening new events + // In this scenario member will reveive list of events + + s := base.GetSuite() + member := base.GetMember() + eventSender := base.GetEventSender() + + newAdminChat := &protobuf.CommunityChat{ + Permissions: &protobuf.CommunityPermissions{ + Access: protobuf.CommunityPermissions_NO_MEMBERSHIP, + }, + Identity: &protobuf.ChatIdentity{ + DisplayName: "chat from event sender", + Emoji: "", + Description: "chat created by an event sender", + }, + } + + checkChannelCreated := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) + if err != nil { + return err + } + + for _, chat := range modifiedCommmunity.Chats() { + if chat.GetIdentity().GetDisplayName() == newAdminChat.GetIdentity().GetDisplayName() { + return nil + } + } + + return errors.New("couldn't find created chat in response") + } + + response, err := eventSender.CreateCommunityChat(community.ID(), newAdminChat) + s.Require().NoError(err) + s.Require().NoError(checkChannelCreated(response)) + s.Require().Len(response.CommunityChanges, 1) + s.Require().Len(response.CommunityChanges[0].ChatsAdded, 1) + var addedChatID string + for addedChatID = range response.CommunityChanges[0].ChatsAdded { + break + } + + waitOnMessengerResponse(s, WaitCommunityCondition, checkChannelCreated, member) + waitOnMessengerResponse(s, WaitCommunityCondition, checkChannelCreated, eventSender) + + newAdminChat.Identity.DisplayName = "modified chat from event sender" + + checkChannelEdited := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) + if err != nil { + return err + } + + for _, chat := range modifiedCommmunity.Chats() { + if chat.GetIdentity().GetDisplayName() == newAdminChat.GetIdentity().GetDisplayName() { + return nil + } + } + + return errors.New("couldn't find modified chat in response") + } + + response, err = eventSender.EditCommunityChat(community.ID(), addedChatID, newAdminChat) + s.Require().NoError(err) + s.Require().NoError(checkChannelEdited(response)) + + waitOnMessengerResponse(s, WaitCommunityCondition, checkChannelEdited, member) + waitOnMessengerResponse(s, WaitCommunityCondition, checkChannelEdited, eventSender) + + checkChannelDeleted := func(response *MessengerResponse) error { + modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) + if err != nil { + return err + } + + if _, exists := modifiedCommmunity.Chats()[addedChatID]; exists { + return errors.New("channel was not deleted") + } + + return nil + } + + response, err = eventSender.DeleteCommunityChat(community.ID(), addedChatID) + s.Require().NoError(err) + s.Require().NoError(checkChannelDeleted(response)) + + waitOnMessengerResponse(s, WaitCommunityCondition, checkChannelDeleted, member) + waitOnMessengerResponse(s, WaitCommunityCondition, checkChannelDeleted, eventSender) +} diff --git a/protocol/communities_messenger_admin_test.go b/protocol/communities_messenger_admin_test.go index 89be38be5..6b6a948fb 100644 --- a/protocol/communities_messenger_admin_test.go +++ b/protocol/communities_messenger_admin_test.go @@ -1,38 +1,27 @@ package protocol import ( - "context" "crypto/ecdsa" - "encoding/json" - "errors" - "io/ioutil" "testing" - "github.com/google/uuid" "github.com/stretchr/testify/suite" "go.uber.org/zap" - "github.com/status-im/status-go/account/generator" gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/multiaccounts" - "github.com/status-im/status-go/multiaccounts/settings" - "github.com/status-im/status-go/params" "github.com/status-im/status-go/protocol/common" - "github.com/status-im/status-go/protocol/communities" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/requests" - "github.com/status-im/status-go/protocol/sqlite" "github.com/status-im/status-go/protocol/tt" "github.com/status-im/status-go/waku" ) -func TestAdminMessengerCommunitiesSuite(t *testing.T) { - suite.Run(t, new(AdminMessengerCommunitiesSuite)) +func TestAdminCommunityEventsSuite(t *testing.T) { + suite.Run(t, new(AdminCommunityEventsSuite)) } -type AdminMessengerCommunitiesSuite struct { +type AdminCommunityEventsSuite struct { suite.Suite owner *Messenger admin *Messenger @@ -43,7 +32,23 @@ type AdminMessengerCommunitiesSuite struct { logger *zap.Logger } -func (s *AdminMessengerCommunitiesSuite) SetupTest() { +func (s *AdminCommunityEventsSuite) GetControlNode() *Messenger { + return s.owner +} + +func (s *AdminCommunityEventsSuite) GetEventSender() *Messenger { + return s.admin +} + +func (s *AdminCommunityEventsSuite) GetMember() *Messenger { + return s.alice +} + +func (s *AdminCommunityEventsSuite) GetSuite() *suite.Suite { + return &s.Suite +} + +func (s *AdminCommunityEventsSuite) SetupTest() { s.logger = tt.MustCreateTestLogger() config := waku.DefaultConfig @@ -63,141 +68,45 @@ func (s *AdminMessengerCommunitiesSuite) SetupTest() { s.Require().NoError(err) } -func (s *AdminMessengerCommunitiesSuite) TearDownTest() { +func (s *AdminCommunityEventsSuite) TearDownTest() { s.Require().NoError(s.owner.Shutdown()) s.Require().NoError(s.admin.Shutdown()) s.Require().NoError(s.alice.Shutdown()) _ = s.logger.Sync() } -func (s *AdminMessengerCommunitiesSuite) newMessengerWithOptions(shh types.Waku, privateKey *ecdsa.PrivateKey, options []Option) *Messenger { - m, err := NewMessenger( - "Test", - privateKey, - &testNode{shh: shh}, - uuid.New().String(), - nil, - nil, - options..., - ) +func (s *AdminCommunityEventsSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger { + messenger, err := newCommunitiesTestMessenger(shh, privateKey, s.logger, nil, nil) s.Require().NoError(err) - err = m.Init() - s.Require().NoError(err) - - config := params.NodeConfig{ - NetworkID: 10, - DataDir: "test", - } - - networks := json.RawMessage("{}") - setting := settings.Settings{ - Address: types.HexToAddress("0x1122334455667788990011223344556677889900"), - AnonMetricsShouldSend: false, - CurrentNetwork: "mainnet_rpc", - DappsAddress: types.HexToAddress("0x1122334455667788990011223344556677889900"), - InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51", - KeyUID: "0x1122334455667788990011223344556677889900", - Name: "Test", - Networks: &networks, - PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==", - PreviewPrivacy: false, - PublicKey: "0x04112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900", - SigningPhrase: "yurt joey vibe", - SendPushNotifications: true, - ProfilePicturesVisibility: 1, - DefaultSyncPeriod: 777600, - UseMailservers: true, - LinkPreviewRequestEnabled: true, - SendStatusUpdates: true, - WalletRootAddress: types.HexToAddress("0x1122334455667788990011223344556677889900")} - - _ = m.settings.CreateSettings(setting, config) - - return m + return messenger } -func (s *AdminMessengerCommunitiesSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger { - tmpfile, err := ioutil.TempFile("", "accounts-tests-") - s.Require().NoError(err) - madb, err := multiaccounts.InitializeDB(tmpfile.Name()) - s.Require().NoError(err) - - acc := generator.NewAccount(privateKey, nil) - iai := acc.ToIdentifiedAccountInfo("") - - options := []Option{ - WithCustomLogger(s.logger), - WithDatabaseConfig(":memory:", "somekey", sqlite.ReducedKDFIterationsNumber), - WithMultiAccounts(madb), - WithAccount(iai.ToMultiAccount()), - WithDatasync(), - } - return s.newMessengerWithOptions(shh, privateKey, options) -} - -func (s *AdminMessengerCommunitiesSuite) newMessenger() *Messenger { +func (s *AdminCommunityEventsSuite) newMessenger() *Messenger { privateKey, err := crypto.GenerateKey() s.Require().NoError(err) return s.newMessengerWithKey(s.shh, privateKey) } -func (s *AdminMessengerCommunitiesSuite) TestAdminEditCommunityDescription() { +func (s *AdminCommunityEventsSuite) TestAdminEditCommunityDescription() { // TODO admin test: update to include edit tags, logo, banner, request to join required setting, pin setting, etc... - community := s.setUpCommunityAndRoles() - s.adminEditsCommunityDescription(community) + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + editCommunityDescription(s, community) } -func (s *AdminMessengerCommunitiesSuite) TestAdminCreateEditDeleteChannels() { - community := s.setUpCommunityAndRoles() - - newAdminChat := &protobuf.CommunityChat{ - Permissions: &protobuf.CommunityPermissions{ - Access: protobuf.CommunityPermissions_NO_MEMBERSHIP, - }, - Identity: &protobuf.ChatIdentity{ - DisplayName: "chat from admin", - Emoji: "", - Description: "chat created by an admin", - }, - } - - newChatID := s.adminCreateCommunityChannel(community, newAdminChat) - - newAdminChat.Identity.DisplayName = "modified chat from admin" - s.adminEditCommunityChannel(community, newAdminChat, newChatID) - - s.adminDeleteCommunityChannel(community, newChatID) +func (s *AdminCommunityEventsSuite) TestAdminCreateEditDeleteChannels() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testCreateEditDeleteChannels(s, community) } -func (s *AdminMessengerCommunitiesSuite) TestAdminCreateEditDeleteBecomeMemberPermission() { - community := s.setUpCommunityAndRoles() - // first, create token permission - tokenPermissionID, createTokenPermission := s.adminCreateTestTokenPermission(community) - - createTokenPermission.TokenCriteria[0].Symbol = "UPDATED" - createTokenPermission.TokenCriteria[0].Amount = "200" - - editTokenPermission := &requests.EditCommunityTokenPermission{ - PermissionID: tokenPermissionID, - CreateCommunityTokenPermission: *createTokenPermission, - } - - // then, admin edits the permission - s.adminEditTokenPermission(community, editTokenPermission) - - deleteTokenPermission := &requests.DeleteCommunityTokenPermission{ - CommunityID: community.ID(), - PermissionID: tokenPermissionID, - } - - // then, admin deletes previously created token permission - s.adminDeleteTokenPermission(community, deleteTokenPermission) +func (s *AdminCommunityEventsSuite) TestAdminCreateEditDeleteBecomeMemberPermission() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testCreateEditDeleteBecomeMemberPermission(s, community) } -func (s *AdminMessengerCommunitiesSuite) TestAdminCannotCreateBecomeAdminPermission() { - community := s.setUpCommunityAndRoles() +func (s *AdminCommunityEventsSuite) TestAdminCannotCreateBecomeAdminPermission() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) permissionRequest := createTestPermissionRequest(community) permissionRequest.Type = protobuf.CommunityTokenPermission_BECOME_ADMIN @@ -207,9 +116,8 @@ func (s *AdminMessengerCommunitiesSuite) TestAdminCannotCreateBecomeAdminPermiss s.Require().Error(err) } -func (s *AdminMessengerCommunitiesSuite) TestAdminCannotEditBecomeAdminPermission() { - - community := s.setUpCommunityAndRoles() +func (s *AdminCommunityEventsSuite) TestAdminCannotEditBecomeAdminPermission() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) permissionRequest := createTestPermissionRequest(community) permissionRequest.Type = protobuf.CommunityTokenPermission_BECOME_ADMIN @@ -225,7 +133,7 @@ func (s *AdminMessengerCommunitiesSuite) TestAdminCannotEditBecomeAdminPermissio ownerCommunity, err := s.owner.communitiesManager.GetByID(community.ID()) s.Require().NoError(err) - s.assertAdminTokenPermissionCreated(ownerCommunity) + assertCheckTokenPermissionCreated(&s.Suite, ownerCommunity) // then, ensure admin receives updated community _, err = WaitOnMessengerResponse( @@ -236,7 +144,7 @@ func (s *AdminMessengerCommunitiesSuite) TestAdminCannotEditBecomeAdminPermissio s.Require().NoError(err) adminCommunity, err := s.admin.communitiesManager.GetByID(community.ID()) s.Require().NoError(err) - s.assertAdminTokenPermissionCreated(adminCommunity) + assertCheckTokenPermissionCreated(&s.Suite, adminCommunity) permissionRequest.TokenCriteria[0].Symbol = "UPDATED" permissionRequest.TokenCriteria[0].Amount = "200" @@ -252,9 +160,8 @@ func (s *AdminMessengerCommunitiesSuite) TestAdminCannotEditBecomeAdminPermissio s.Require().Nil(response) } -func (s *AdminMessengerCommunitiesSuite) TestAdminCannotDeleteBecomeAdminPermission() { - - community := s.setUpCommunityAndRoles() +func (s *AdminCommunityEventsSuite) TestAdminCannotDeleteBecomeAdminPermission() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) permissionRequest := createTestPermissionRequest(community) permissionRequest.Type = protobuf.CommunityTokenPermission_BECOME_ADMIN @@ -277,7 +184,7 @@ func (s *AdminMessengerCommunitiesSuite) TestAdminCannotDeleteBecomeAdminPermiss s.Require().NoError(err) adminCommunity, err := s.admin.communitiesManager.GetByID(community.ID()) s.Require().NoError(err) - s.assertAdminTokenPermissionCreated(adminCommunity) + assertCheckTokenPermissionCreated(&s.Suite, adminCommunity) deleteTokenPermission := &requests.DeleteCommunityTokenPermission{ CommunityID: community.ID(), @@ -290,1192 +197,83 @@ func (s *AdminMessengerCommunitiesSuite) TestAdminCannotDeleteBecomeAdminPermiss s.Require().Nil(response) } -func (s *AdminMessengerCommunitiesSuite) TestAdminAcceptMemberRequestToJoin() { - community := s.setUpOnRequestCommunityAndRoles() +func (s *AdminCommunityEventsSuite) TestAdminAcceptMemberRequestToJoin() { + community := setUpOnRequestCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) // set up additional user that will send request to join user := s.newMessenger() - _, err := user.Start() - s.Require().NoError(err) - defer user.Shutdown() // nolint: errcheck - - s.advertiseCommunityTo(community, user) - - // user sends request to join - requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID()} - response, err := user.RequestToJoinCommunity(requestToJoin) - s.Require().NoError(err) - s.Require().NotNil(response) - s.Require().Len(response.RequestsToJoinCommunity, 1) - _ = response.RequestsToJoinCommunity[0] - - // admin receives request to join - response, err = WaitOnMessengerResponse( - s.admin, - func(r *MessengerResponse) bool { return len(r.RequestsToJoinCommunity) > 0 }, - "admin did not receive community request to join", - ) - s.Require().NoError(err) - s.Require().Len(response.RequestsToJoinCommunity, 1) - - receivedRequest := response.RequestsToJoinCommunity[0] - - // admin has not accepted request yet - adminCommunity, err := s.admin.GetCommunityByID(community.ID()) - s.Require().NoError(err) - s.Require().False(adminCommunity.HasMember(&user.identity.PublicKey)) - - acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: receivedRequest.ID} - response, err = s.admin.AcceptRequestToJoinCommunity(acceptRequestToJoin) - s.Require().NoError(err) - s.Require().NotNil(response) - s.Require().Len(response.Communities(), 1) - s.Require().True(response.Communities()[0].HasMember(&user.identity.PublicKey)) - - // user receives request to join response - response, err = WaitOnMessengerResponse( - user, - func(r *MessengerResponse) bool { return len(r.Communities()) > 0 }, - "user did not receive community request to join response", - ) - s.Require().NoError(err) - s.Require().Len(response.Communities(), 1) - s.Require().True(response.Communities()[0].HasMember(&user.identity.PublicKey)) - - // owner receives updated community - response, err = WaitOnMessengerResponse( - s.owner, - func(r *MessengerResponse) bool { return len(r.Communities()) > 0 }, - "owner did not receive community request to join response", - ) - s.Require().NoError(err) - s.Require().Len(response.Communities(), 1) - - requests, err := s.owner.AcceptedRequestsToJoinForCommunity(community.ID()) - // there's now two requests to join (admin and alice) + 1 from user - s.Require().NoError(err) - s.Require().Len(requests, 3) - s.Require().True(response.Communities()[0].HasMember(&user.identity.PublicKey)) - - // alice receives updated community - response, err = WaitOnMessengerResponse( - s.alice, - func(r *MessengerResponse) bool { return len(r.Communities()) > 0 }, - "alice did not receive community request to join response", - ) - s.Require().NoError(err) - s.Require().Len(response.Communities(), 1) - s.Require().True(response.Communities()[0].HasMember(&user.identity.PublicKey)) + testAcceptMemberRequestToJoin(s, community, user) } -func (s *AdminMessengerCommunitiesSuite) TestAdminRejectMemberRequestToJoin() { - community := s.setUpOnRequestCommunityAndRoles() +func (s *AdminCommunityEventsSuite) TestAdminRejectMemberRequestToJoin() { + community := setUpOnRequestCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) // set up additional user that will send request to join user := s.newMessenger() - _, err := user.Start() - s.Require().NoError(err) - defer user.Shutdown() // nolint: errcheck - - s.advertiseCommunityTo(community, user) - - // user sends request to join - requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID()} - response, err := user.RequestToJoinCommunity(requestToJoin) - s.Require().NoError(err) - s.Require().NotNil(response) - s.Require().Len(response.RequestsToJoinCommunity, 1) - - // admin receives request to join - response, err = WaitOnMessengerResponse( - s.admin, - func(r *MessengerResponse) bool { return len(r.RequestsToJoinCommunity) > 0 }, - "admin did not receive community request to join", - ) - s.Require().NoError(err) - s.Require().Len(response.RequestsToJoinCommunity, 1) - - receivedRequest := response.RequestsToJoinCommunity[0] - - // admin has not accepted request yet - adminCommunity, err := s.admin.GetCommunityByID(community.ID()) - s.Require().NoError(err) - s.Require().False(adminCommunity.HasMember(&user.identity.PublicKey)) - - // admin rejects request to join - rejectRequestToJoin := &requests.DeclineRequestToJoinCommunity{ID: receivedRequest.ID} - _, err = s.admin.DeclineRequestToJoinCommunity(rejectRequestToJoin) - s.Require().NoError(err) - - adminCommunity, err = s.admin.GetCommunityByID(community.ID()) - s.Require().NoError(err) - s.Require().False(adminCommunity.HasMember(&user.identity.PublicKey)) - - // owner receives admin event and stores rejected request to join - response, err = WaitOnMessengerResponse( - s.owner, - func(r *MessengerResponse) bool { return len(r.Communities()) > 0 }, - "owner did not receive community request to join update from admin", - ) - s.Require().NoError(err) - s.Require().False(response.Communities()[0].HasMember(&user.identity.PublicKey)) - - requests, err := s.owner.DeclinedRequestsToJoinForCommunity(community.ID()) - s.Require().Len(requests, 1) - s.Require().NoError(err) + testRejectMemberRequestToJoin(s, community, user) } -func (s *AdminMessengerCommunitiesSuite) TestAdminCreateEditDeleteCategories() { - community := s.setUpCommunityAndRoles() - newCategory := &requests.CreateCommunityCategory{ - CommunityID: community.ID(), - CategoryName: "admin-category-name", - } - categoryID := s.adminCreateCommunityCategory(community, newCategory) - - editCategory := &requests.EditCommunityCategory{ - CommunityID: community.ID(), - CategoryID: categoryID, - CategoryName: "edited-admin-category-name", - } - - s.adminEditCommunityCategory(community.IDString(), editCategory) - - deleteCategory := &requests.DeleteCommunityCategory{ - CommunityID: community.ID(), - CategoryID: categoryID, - } - - s.adminDeleteCommunityCategory(community.IDString(), deleteCategory) +func (s *AdminCommunityEventsSuite) TestAdminCreateEditDeleteCategories() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testCreateEditDeleteCategories(s, community) } -func (s *AdminMessengerCommunitiesSuite) TestAdminReorderChannelsAndCategories() { - community := s.setUpCommunityAndRoles() - newCategory := &requests.CreateCommunityCategory{ - CommunityID: community.ID(), - CategoryName: "admin-category-name", - } - _ = s.adminCreateCommunityCategory(community, newCategory) - - newCategory.CategoryName = "admin-category-name2" - categoryID2 := s.adminCreateCommunityCategory(community, newCategory) - - chat := &protobuf.CommunityChat{ - Permissions: &protobuf.CommunityPermissions{ - Access: protobuf.CommunityPermissions_NO_MEMBERSHIP, - }, - Identity: &protobuf.ChatIdentity{ - DisplayName: "chat from admin", - Emoji: "", - Description: "chat created by an admin", - }, - } - - chatID := s.adminCreateCommunityChannel(community, chat) - - reorderCommunityRequest := requests.ReorderCommunityCategories{ - CommunityID: community.ID(), - CategoryID: categoryID2, - Position: 0, - } - - s.adminReorderCategory(&reorderCommunityRequest) - - reorderChatRequest := requests.ReorderCommunityChat{ - CommunityID: community.ID(), - CategoryID: categoryID2, - ChatID: chatID, - Position: 0, - } - - s.adminReorderChannel(&reorderChatRequest) +func (s *AdminCommunityEventsSuite) TestAdminReorderChannelsAndCategories() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testReorderChannelsAndCategories(s, community) } -func (s *AdminMessengerCommunitiesSuite) TestAdminKickAdmin() { - community := s.setUpCommunityAndRoles() - - // admin tries to kick the owner - _, err := s.admin.RemoveUserFromCommunity( - community.ID(), - common.PubkeyToHex(&s.admin.identity.PublicKey), - ) - s.Require().Error(err) - s.Require().EqualError(err, "not allowed to remove admin or owner") +func (s *AdminCommunityEventsSuite) TestAdminKickAdmin() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testEventSenderKickTheSameRole(s, community) } -func (s *AdminMessengerCommunitiesSuite) TestAdminKickMember() { - community := s.setUpCommunityAndRoles() - - // admin tries to kick the owner - _, err := s.admin.RemoveUserFromCommunity( - community.ID(), - common.PubkeyToHex(&s.owner.identity.PublicKey), - ) - s.Require().Error(err) - - s.adminKickAlice(community.ID(), common.PubkeyToHex(&s.alice.identity.PublicKey)) +func (s *AdminCommunityEventsSuite) TestOwnerKickControlNode() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testEventSenderKickControlNode(s, community) } -func (s *AdminMessengerCommunitiesSuite) TestAdminBanAdmin() { - community := s.setUpCommunityAndRoles() - - // verify that admin can't ban an admin - _, err := s.admin.BanUserFromCommunity( - &requests.BanUserFromCommunity{ - CommunityID: community.ID(), - User: common.PubkeyToHexBytes(&s.admin.identity.PublicKey), - }, - ) - s.Require().Error(err) - s.Require().EqualError(err, "not allowed to ban admin or owner") +func (s *AdminCommunityEventsSuite) TestAdminKickMember() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + kickMember(s, community.ID(), common.PubkeyToHex(&s.alice.identity.PublicKey)) } -func (s *AdminMessengerCommunitiesSuite) TestAdminBanUnbanMember() { - community := s.setUpCommunityAndRoles() - - // verify that admin can't ban an owner - _, err := s.admin.BanUserFromCommunity( - &requests.BanUserFromCommunity{ - CommunityID: community.ID(), - User: common.PubkeyToHexBytes(&s.owner.identity.PublicKey), - }, - ) - s.Require().Error(err) - - banRequest := &requests.BanUserFromCommunity{ - CommunityID: community.ID(), - User: common.PubkeyToHexBytes(&s.alice.identity.PublicKey), - } - - s.adminBanAlice(banRequest) - - unbanRequest := &requests.UnbanUserFromCommunity{ - CommunityID: community.ID(), - User: common.PubkeyToHexBytes(&s.alice.identity.PublicKey), - } - - s.adminUnbanAlice(unbanRequest) +func (s *AdminCommunityEventsSuite) TestAdminBanAdmin() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testOwnerBanTheSameRole(s, community) } -func (s *AdminMessengerCommunitiesSuite) TestAdminDeleteAnyMessageInTheCommunity() { - community := s.setUpCommunityAndRoles() - chatID := community.ChatIDs()[0] - - inputMessage := common.Message{} - inputMessage.ChatId = chatID - inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN - inputMessage.Text = "owner text" - - messageID := s.ownerSendMessage(&inputMessage) - - s.adminDeleteMessage(messageID) +func (s *AdminCommunityEventsSuite) TestAdminBanControlNode() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testOwnerBanControlNode(s, community) } -func (s *AdminMessengerCommunitiesSuite) TestAdminPinMessage() { - community := s.setUpCommunityAndRoles() - s.Require().False(community.AllowsAllMembersToPinMessage()) - chatID := community.ChatIDs()[0] - - inputMessage := common.Message{} - inputMessage.ChatId = chatID - inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN - inputMessage.Text = "owner text" - - messageID := s.ownerSendMessage(&inputMessage) - - pinnedMessage := common.PinMessage{} - pinnedMessage.MessageId = messageID - pinnedMessage.ChatId = chatID - pinnedMessage.Pinned = true - - s.adminPinMessage(&pinnedMessage) +func (s *AdminCommunityEventsSuite) TestAdminBanUnbanMember() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testBanUnbanMember(s, community) } -func (s *AdminMessengerCommunitiesSuite) TestAdminMintToken() { - s.setUpCommunityAndRoles() +func (s *AdminCommunityEventsSuite) TestAdminDeleteAnyMessageInTheCommunity() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testDeleteAnyMessageInTheCommunity(s, community) +} + +func (s *AdminCommunityEventsSuite) TestAdminPinMessage() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testEventSenderPinMessage(s, community) +} + +func (s *AdminCommunityEventsSuite) TestAdminMintToken() { + setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) // TODO admin test: Mint Tokens (rescticted) } -func (s *AdminMessengerCommunitiesSuite) TestAdminAirdropTokens() { - s.setUpCommunityAndRoles() +func (s *AdminCommunityEventsSuite) TestAdminAirdropTokens() { + setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) // TODO admin test: Airdrop Tokens (restricted) } -func (s *AdminMessengerCommunitiesSuite) TestMemberReceiveAdminEventsWhenOwnerOffline() { - community := s.setUpCommunityAndRoles() - - // To simulate behavior when owner is offline, we will not use owner for listening new events - // In this scenario member will reveive list of events - - newAdminChat := &protobuf.CommunityChat{ - Permissions: &protobuf.CommunityPermissions{ - Access: protobuf.CommunityPermissions_NO_MEMBERSHIP, - }, - Identity: &protobuf.ChatIdentity{ - DisplayName: "chat from admin", - Emoji: "", - Description: "chat created by an admin", - }, - } - - checkChannelCreated := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - for _, chat := range modifiedCommmunity.Chats() { - if chat.GetIdentity().GetDisplayName() == newAdminChat.GetIdentity().GetDisplayName() { - return nil - } - } - - return errors.New("couldn't find created chat in response") - } - - response, err := s.admin.CreateCommunityChat(community.ID(), newAdminChat) - s.Require().NoError(err) - s.Require().NoError(checkChannelCreated(response)) - s.Require().Len(response.CommunityChanges, 1) - s.Require().Len(response.CommunityChanges[0].ChatsAdded, 1) - var addedChatID string - for addedChatID = range response.CommunityChanges[0].ChatsAdded { - break - } - - s.waitOnMessengerResponse(WaitCommunityCondition, checkChannelCreated, s.alice) - s.waitOnMessengerResponse(WaitCommunityCondition, checkChannelCreated, s.admin) - - newAdminChat.Identity.DisplayName = "modified chat from admin" - - checkChannelEdited := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - for _, chat := range modifiedCommmunity.Chats() { - if chat.GetIdentity().GetDisplayName() == newAdminChat.GetIdentity().GetDisplayName() { - return nil - } - } - - return errors.New("couldn't find modified chat in response") - } - - response, err = s.admin.EditCommunityChat(community.ID(), addedChatID, newAdminChat) - s.Require().NoError(err) - s.Require().NoError(checkChannelEdited(response)) - - s.waitOnMessengerResponse(WaitCommunityCondition, checkChannelEdited, s.alice) - s.waitOnMessengerResponse(WaitCommunityCondition, checkChannelEdited, s.admin) - - checkChannelDeleted := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - if _, exists := modifiedCommmunity.Chats()[addedChatID]; exists { - return errors.New("channel was not deleted") - } - - return nil - } - - response, err = s.admin.DeleteCommunityChat(community.ID(), addedChatID) - s.Require().NoError(err) - s.Require().NoError(checkChannelDeleted(response)) - - s.waitOnMessengerResponse(WaitCommunityCondition, checkChannelDeleted, s.alice) - s.waitOnMessengerResponse(WaitCommunityCondition, checkChannelDeleted, s.admin) -} - -func (s *AdminMessengerCommunitiesSuite) setUpOnRequestCommunityAndRoles() *communities.Community { - tcs2, err := s.owner.communitiesManager.All() - s.Require().NoError(err, "admin.communitiesManager.All") - s.Len(tcs2, 1, "Must have 1 community") - - // owner creates a community and chat - community := s.createCommunity(protobuf.CommunityPermissions_ON_REQUEST) - s.advertiseCommunityTo(community, s.admin) - s.advertiseCommunityTo(community, s.alice) - - s.refreshMessengerResponses() - - s.joinOnRequestCommunity(community, s.admin) - s.joinOnRequestCommunity(community, s.alice) - - s.refreshMessengerResponses() - - // grant admin permissions to the admin - s.grantAdminPermissions(community, s.admin) - return community -} - -func (s *AdminMessengerCommunitiesSuite) setUpCommunityAndRoles() *communities.Community { - tcs2, err := s.owner.communitiesManager.All() - s.Require().NoError(err, "admin.communitiesManager.All") - s.Len(tcs2, 1, "Must have 1 community") - - // owner creates a community and chat - community := s.createCommunity(protobuf.CommunityPermissions_NO_MEMBERSHIP) - s.refreshMessengerResponses() - - // add admin and alice to the community - s.advertiseCommunityTo(community, s.admin) - s.advertiseCommunityTo(community, s.alice) - s.joinCommunity(community, s.admin) - s.joinCommunity(community, s.alice) - - s.refreshMessengerResponses() - - // grant admin permissions to the admin - s.grantAdminPermissions(community, s.admin) - - return community -} - -func (s *AdminMessengerCommunitiesSuite) advertiseCommunityTo(community *communities.Community, user *Messenger) { - chat := CreateOneToOneChat(common.PubkeyToHex(&user.identity.PublicKey), &user.identity.PublicKey, user.transport) - - inputMessage := &common.Message{} - inputMessage.ChatId = chat.ID - inputMessage.Text = "some text" - inputMessage.CommunityID = community.IDString() - - err := s.owner.SaveChat(chat) - s.Require().NoError(err) - _, err = s.owner.SendChatMessage(context.Background(), inputMessage) - s.Require().NoError(err) - - // Ensure community is received - err = tt.RetryWithBackOff(func() error { - response, err := user.RetrieveAll() - if err != nil { - return err - } - if len(response.Communities()) == 0 { - return errors.New("community not received") - } - return nil - }) - s.Require().NoError(err) -} - -func (s *AdminMessengerCommunitiesSuite) joinOnRequestCommunity(community *communities.Community, user *Messenger) { - // Request to join the community - request := &requests.RequestToJoinCommunity{CommunityID: community.ID()} - response, err := user.RequestToJoinCommunity(request) - s.Require().NoError(err) - s.Require().NotNil(response) - s.Require().Len(response.RequestsToJoinCommunity, 1) - - requestToJoin := response.RequestsToJoinCommunity[0] - s.Require().Equal(requestToJoin.PublicKey, common.PubkeyToHex(&user.identity.PublicKey)) - - response, err = WaitOnMessengerResponse( - s.owner, - func(r *MessengerResponse) bool { - return len(r.RequestsToJoinCommunity) > 0 - }, - "owner did not receive community request to join", - ) - s.Require().NoError(err) - - userRequestToJoin := response.RequestsToJoinCommunity[0] - s.Require().Equal(userRequestToJoin.PublicKey, common.PubkeyToHex(&user.identity.PublicKey)) - - // accept join request - acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: requestToJoin.ID} - response, err = s.owner.AcceptRequestToJoinCommunity(acceptRequestToJoin) - s.Require().NoError(err) - s.Require().NotNil(response) - - updatedCommunity := response.Communities()[0] - s.Require().NotNil(updatedCommunity) - s.Require().True(updatedCommunity.HasMember(&user.identity.PublicKey)) - - // receive request to join response - _, err = WaitOnMessengerResponse( - user, - func(r *MessengerResponse) bool { - return len(r.Communities()) > 0 - }, - "user did not receive request to join response", - ) - s.Require().NoError(err) - userCommunity, err := user.GetCommunityByID(community.ID()) - s.Require().NoError(err) - s.Require().True(userCommunity.HasMember(&user.identity.PublicKey)) -} - -func (s *AdminMessengerCommunitiesSuite) joinCommunity(community *communities.Community, user *Messenger) { - // Request to join the community - request := &requests.RequestToJoinCommunity{CommunityID: community.ID()} - response, err := user.RequestToJoinCommunity(request) - s.Require().NoError(err) - s.Require().NotNil(response) - s.Require().Len(response.RequestsToJoinCommunity, 1) - s.Require().Len(response.ActivityCenterNotifications(), 1) - - notification := response.ActivityCenterNotifications()[0] - s.Require().NotNil(notification) - s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest) - s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) - - // Retrieve and accept join request - err = tt.RetryWithBackOff(func() error { - response, err := s.owner.RetrieveAll() - if err != nil { - return err - } - if len(response.Communities()) == 0 { - return errors.New("no communities in response (accept join request)") - } - if !response.Communities()[0].HasMember(&user.identity.PublicKey) { - return errors.New("user not accepted") - } - return nil - }) - s.Require().NoError(err) - - // Retrieve join request response - err = tt.RetryWithBackOff(func() error { - response, err := user.RetrieveAll() - - if err != nil { - return err - } - if len(response.Communities()) == 0 { - return errors.New("no communities in response (join request response)") - } - if !response.Communities()[0].HasMember(&user.identity.PublicKey) { - return errors.New("user not a member") - } - return nil - }) - s.Require().NoError(err) -} - -func (s *AdminMessengerCommunitiesSuite) createCommunity(membershipType protobuf.CommunityPermissions_Access) *communities.Community { - description := &requests.CreateCommunity{ - Membership: membershipType, - Name: "status", - Color: "#ffffff", - Description: "status community description", - PinMessageAllMembersEnabled: false, - } - response, err := s.owner.CreateCommunity(description, true) - - s.Require().NoError(err) - s.Require().NotNil(response) - s.Require().Len(response.Communities(), 1) - s.Require().Len(response.Chats(), 1) - - return response.Communities()[0] -} - -func (s *AdminMessengerCommunitiesSuite) grantAdminPermissions(community *communities.Community, target *Messenger) { - responseAddRole, err := s.owner.AddRoleToMember(&requests.AddRoleToMember{ - CommunityID: community.ID(), - User: common.PubkeyToHexBytes(target.IdentityPublicKey()), - Role: protobuf.CommunityMember_ROLE_ADMIN, - }) - s.Require().NoError(err) - - checkAdminRole := func(response *MessengerResponse) bool { - if len(response.Communities()) == 0 { - return false - } - rCommunities := response.Communities() - s.Require().Len(rCommunities, 1) - s.Require().True(rCommunities[0].IsMemberAdmin(target.IdentityPublicKey())) - return true - } - - checkAdminRole(responseAddRole) - - _, err = WaitOnMessengerResponse(s.admin, func(response *MessengerResponse) bool { - return checkAdminRole(response) - }, "community description changed message not received") - s.Require().NoError(err) - _, err = WaitOnMessengerResponse(s.alice, func(response *MessengerResponse) bool { - return checkAdminRole(response) - }, "community description changed message not received") - s.Require().NoError(err) - - s.refreshMessengerResponses() -} - -func (s *AdminMessengerCommunitiesSuite) adminEditsCommunityDescription(community *communities.Community) { - expectedName := "edited community name" - expectedColor := "#000000" - expectedDescr := "edited community description" - - response, err := s.admin.EditCommunity(&requests.EditCommunity{ - CommunityID: community.ID(), - CreateCommunity: requests.CreateCommunity{ - Membership: protobuf.CommunityPermissions_ON_REQUEST, - Name: expectedName, - Color: expectedColor, - Description: expectedDescr, - }, - }) - - checkCommunityEdit := func(response *MessengerResponse) error { - if len(response.Communities()) == 0 { - return errors.New("community not received") - } - - rCommunities := response.Communities() - if expectedName != rCommunities[0].Name() { - return errors.New("incorrect community name") - } - - if expectedColor != rCommunities[0].Color() { - return errors.New("incorrect community color") - } - - if expectedDescr != rCommunities[0].DescriptionText() { - return errors.New("incorrect community description") - } - - return nil - } - - s.Require().NoError(err) - s.Require().Nil(checkCommunityEdit(response)) - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkCommunityEdit) -} - -func (s *AdminMessengerCommunitiesSuite) refreshMessengerResponses() { - _, err := WaitOnMessengerResponse(s.owner, func(response *MessengerResponse) bool { - return true - }, "community description changed message not received") - s.Require().NoError(err) - - _, err = WaitOnMessengerResponse(s.admin, func(response *MessengerResponse) bool { - return true - }, "community description changed message not received") - s.Require().NoError(err) - - _, err = WaitOnMessengerResponse(s.alice, func(response *MessengerResponse) bool { - return true - }, "community description changed message not received") - s.Require().NoError(err) -} - -type MessageResponseValidator func(*MessengerResponse) error -type WaitResponseValidator func(*MessengerResponse) bool - -func WaitCommunityCondition(r *MessengerResponse) bool { - return len(r.Communities()) > 0 -} - -func WaitMessageCondition(response *MessengerResponse) bool { - return len(response.Messages()) > 0 -} - -func (s *AdminMessengerCommunitiesSuite) waitOnMessengerResponse(fnWait WaitResponseValidator, fn MessageResponseValidator, user *Messenger) { - response, err := WaitOnMessengerResponse( - user, - fnWait, - "MessengerResponse data not received", - ) - s.Require().NoError(err) - s.Require().NoError(fn(response)) -} - -func (s *AdminMessengerCommunitiesSuite) checkClientsReceivedAdminEvent(fnWait WaitResponseValidator, fn MessageResponseValidator) { - // Wait and verify Alice received admin event - s.waitOnMessengerResponse(fnWait, fn, s.alice) - // Wait and verify Admin received his own admin event - s.waitOnMessengerResponse(fnWait, fn, s.admin) - // Wait and verify Owner received admin event - // Owner will publish CommunityDescription update - s.waitOnMessengerResponse(fnWait, fn, s.owner) - // Wait and verify Alice received the Owner CommunityDescription update - s.waitOnMessengerResponse(fnWait, fn, s.alice) - // Wait and verify Admin received the Owner CommunityDescription update - s.waitOnMessengerResponse(fnWait, fn, s.admin) - // Wait and verify Owner received his own CommunityDescription update - s.waitOnMessengerResponse(fnWait, fn, s.owner) -} - -func (s *AdminMessengerCommunitiesSuite) adminCreateTokenPermission(community *communities.Community, request *requests.CreateCommunityTokenPermission, assertFn func(*communities.Community)) (string, *requests.CreateCommunityTokenPermission) { - checkTokenPermissionCreation := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - if !modifiedCommmunity.HasTokenPermissions() { - return errors.New("new token permission was not found") - } - - return nil - } - - response, err := s.admin.CreateCommunityTokenPermission(request) - s.Require().NoError(err) - s.Require().Nil(checkTokenPermissionCreation(response)) - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkTokenPermissionCreation) - - var tokenPermissionID string - for tokenPermissionID = range response.CommunityChanges[0].TokenPermissionsAdded { - break - } - - s.Require().NotEqual(tokenPermissionID, "") - - return tokenPermissionID, request -} - -func (s *AdminMessengerCommunitiesSuite) adminEditTokenPermission(community *communities.Community, request *requests.EditCommunityTokenPermission) { - checkTokenPermissionEdit := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - s.assertAdminTokenPermissionEdited(modifiedCommmunity) - - return nil - } - - response, err := s.admin.EditCommunityTokenPermission(request) - s.Require().NoError(err) - s.Require().Nil(checkTokenPermissionEdit(response)) - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkTokenPermissionEdit) -} - -func (s *AdminMessengerCommunitiesSuite) adminDeleteTokenPermission(community *communities.Community, request *requests.DeleteCommunityTokenPermission) { - checkTokenPermissionDeleted := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - if modifiedCommmunity.HasTokenPermissions() { - return errors.New("token permission was not deleted") - } - - return nil - } - - response, err := s.admin.DeleteCommunityTokenPermission(request) - s.Require().NoError(err) - s.Require().Nil(checkTokenPermissionDeleted(response)) - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkTokenPermissionDeleted) -} - -func createTestPermissionRequest(community *communities.Community) *requests.CreateCommunityTokenPermission { - return &requests.CreateCommunityTokenPermission{ - CommunityID: community.ID(), - Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, - TokenCriteria: []*protobuf.TokenCriteria{ - { - Type: protobuf.CommunityTokenType_ERC20, - ContractAddresses: map[uint64]string{uint64(1): "0x123"}, - Symbol: "TEST", - Amount: "100", - Decimals: uint64(18), - }, - }, - } -} - -func (s *AdminMessengerCommunitiesSuite) adminCreateTestTokenPermission(community *communities.Community) (string, *requests.CreateCommunityTokenPermission) { - createTokenPermission := createTestPermissionRequest(community) - return s.adminCreateTokenPermission(community, createTokenPermission, s.assertAdminTokenPermissionCreated) -} - -func (s *AdminMessengerCommunitiesSuite) assertAdminTokenPermissionCreated(community *communities.Community) { - permissions := make([]*protobuf.CommunityTokenPermission, 0) - tokenPermissions := community.TokenPermissions() - for _, p := range tokenPermissions { - permissions = append(permissions, p) - } - s.Require().Len(permissions, 1) - s.Require().Len(permissions[0].TokenCriteria, 1) - s.Require().Equal(permissions[0].TokenCriteria[0].Type, protobuf.CommunityTokenType_ERC20) - s.Require().Equal(permissions[0].TokenCriteria[0].Symbol, "TEST") - s.Require().Equal(permissions[0].TokenCriteria[0].Amount, "100") - s.Require().Equal(permissions[0].TokenCriteria[0].Decimals, uint64(18)) -} - -func (s *AdminMessengerCommunitiesSuite) assertAdminTokenPermissionEdited(community *communities.Community) { - permissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER) - s.Require().Len(permissions, 1) - s.Require().Len(permissions[0].TokenCriteria, 1) - s.Require().Equal(permissions[0].TokenCriteria[0].Type, protobuf.CommunityTokenType_ERC20) - s.Require().Equal(permissions[0].TokenCriteria[0].Symbol, "UPDATED") - s.Require().Equal(permissions[0].TokenCriteria[0].Amount, "200") - s.Require().Equal(permissions[0].TokenCriteria[0].Decimals, uint64(18)) -} - -func (s *AdminMessengerCommunitiesSuite) adminCreateCommunityChannel(community *communities.Community, newChannel *protobuf.CommunityChat) string { - checkChannelCreated := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - for _, chat := range modifiedCommmunity.Chats() { - if chat.GetIdentity().GetDisplayName() == newChannel.GetIdentity().GetDisplayName() { - return nil - } - } - - return errors.New("couldn't find created chat in response") - } - - response, err := s.admin.CreateCommunityChat(community.ID(), newChannel) - s.Require().NoError(err) - s.Require().NoError(checkChannelCreated(response)) - s.Require().Len(response.CommunityChanges, 1) - s.Require().Len(response.CommunityChanges[0].ChatsAdded, 1) - var addedChatID string - for addedChatID = range response.CommunityChanges[0].ChatsAdded { - break - } - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkChannelCreated) - - return addedChatID -} - -func (s *AdminMessengerCommunitiesSuite) adminEditCommunityChannel(community *communities.Community, editChannel *protobuf.CommunityChat, channelID string) { - checkChannelEdited := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - for _, chat := range modifiedCommmunity.Chats() { - if chat.GetIdentity().GetDisplayName() == editChannel.GetIdentity().GetDisplayName() { - return nil - } - } - - return errors.New("couldn't find modified chat in response") - } - - response, err := s.admin.EditCommunityChat(community.ID(), channelID, editChannel) - s.Require().NoError(err) - s.Require().NoError(checkChannelEdited(response)) - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkChannelEdited) -} - -func (s *AdminMessengerCommunitiesSuite) adminDeleteCommunityChannel(community *communities.Community, channelID string) { - checkChannelDeleted := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - if _, exists := modifiedCommmunity.Chats()[channelID]; exists { - return errors.New("channel was not deleted") - } - - return nil - } - - response, err := s.admin.DeleteCommunityChat(community.ID(), channelID) - s.Require().NoError(err) - s.Require().NoError(checkChannelDeleted(response)) - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkChannelDeleted) -} - -func (s *AdminMessengerCommunitiesSuite) adminCreateCommunityCategory(community *communities.Community, newCategory *requests.CreateCommunityCategory) string { - checkCategoryCreated := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - for _, category := range modifiedCommmunity.Categories() { - if category.GetName() == newCategory.CategoryName { - return nil - } - } - - return errors.New("couldn't find created Category in the response") - } - - response, err := s.admin.CreateCommunityCategory(newCategory) - s.Require().NoError(err) - s.Require().NoError(checkCategoryCreated(response)) - s.Require().Len(response.Communities(), 1) - s.Require().Len(response.CommunityChanges[0].CategoriesAdded, 1) - - var categoryID string - for categoryID = range response.CommunityChanges[0].CategoriesAdded { - break - } - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkCategoryCreated) - - return categoryID -} - -func (s *AdminMessengerCommunitiesSuite) adminEditCommunityCategory(communityID string, editCategory *requests.EditCommunityCategory) { - checkCategoryEdited := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, communityID) - if err != nil { - return err - } - - for _, category := range modifiedCommmunity.Categories() { - if category.GetName() == editCategory.CategoryName { - return nil - } - } - - return errors.New("couldn't find edited Category in the response") - } - - response, err := s.admin.EditCommunityCategory(editCategory) - s.Require().NoError(err) - s.Require().NoError(checkCategoryEdited(response)) - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkCategoryEdited) -} - -func (s *AdminMessengerCommunitiesSuite) adminDeleteCommunityCategory(communityID string, deleteCategory *requests.DeleteCommunityCategory) { - checkCategoryDeleted := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, communityID) - if err != nil { - return err - } - - if _, exists := modifiedCommmunity.Chats()[deleteCategory.CategoryID]; exists { - return errors.New("community was not deleted") - } - - return nil - } - - response, err := s.admin.DeleteCommunityCategory(deleteCategory) - s.Require().NoError(err) - s.Require().NoError(checkCategoryDeleted(response)) - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkCategoryDeleted) -} - -func (s *AdminMessengerCommunitiesSuite) ownerSendMessage(inputMessage *common.Message) string { - response, err := s.owner.SendChatMessage(context.Background(), inputMessage) - s.Require().NoError(err) - message := response.Messages()[0] - s.Require().Equal(inputMessage.Text, message.Text) - messageID := message.ID - - response, err = WaitOnMessengerResponse(s.admin, WaitMessageCondition, "messages not received") - s.Require().NoError(err) - message = response.Messages()[0] - s.Require().Equal(inputMessage.Text, message.Text) - - response, err = WaitOnMessengerResponse(s.alice, WaitMessageCondition, "messages not received") - s.Require().NoError(err) - message = response.Messages()[0] - s.Require().Equal(inputMessage.Text, message.Text) - - s.refreshMessengerResponses() - - return messageID -} - -func (s *AdminMessengerCommunitiesSuite) adminDeleteMessage(messageID string) { - checkMessageDeleted := func(response *MessengerResponse) error { - if len(response.RemovedMessages()) > 0 { - return nil - } - return errors.New("message was not deleted") - } - - response, err := s.admin.DeleteMessageAndSend(context.Background(), messageID) - s.Require().NoError(err) - s.Require().NoError(checkMessageDeleted(response)) - - waitMessageCondition := func(response *MessengerResponse) bool { - return len(response.RemovedMessages()) > 0 - } - s.waitOnMessengerResponse(waitMessageCondition, checkMessageDeleted, s.alice) - s.waitOnMessengerResponse(waitMessageCondition, checkMessageDeleted, s.owner) - -} - -func (s *AdminMessengerCommunitiesSuite) adminPinMessage(pinnedMessage *common.PinMessage) { - checkPinned := func(response *MessengerResponse) error { - if len(response.PinMessages()) > 0 { - return nil - } - return errors.New("pin messages was not added") - } - - response, err := s.admin.SendPinMessage(context.Background(), pinnedMessage) - s.Require().NoError(err) - s.Require().NoError(checkPinned(response)) - - s.waitOnMessengerResponse(WaitMessageCondition, checkPinned, s.alice) - s.waitOnMessengerResponse(WaitMessageCondition, checkPinned, s.owner) -} - -func (s *AdminMessengerCommunitiesSuite) adminBanAlice(banRequest *requests.BanUserFromCommunity) { - checkBanned := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, types.EncodeHex(banRequest.CommunityID)) - if err != nil { - return err - } - - if modifiedCommmunity.HasMember(&s.alice.identity.PublicKey) { - return errors.New("alice was not removed from the member list") - } - - if !modifiedCommmunity.IsBanned(&s.alice.identity.PublicKey) { - return errors.New("alice was not added to the banned list") - } - - return nil - } - - response, err := s.admin.BanUserFromCommunity(banRequest) - s.Require().NoError(err) - s.Require().Nil(checkBanned(response)) - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkBanned) -} - -func (s *AdminMessengerCommunitiesSuite) adminUnbanAlice(unbanRequest *requests.UnbanUserFromCommunity) { - checkUnbanned := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, types.EncodeHex(unbanRequest.CommunityID)) - if err != nil { - return err - } - - if modifiedCommmunity.IsBanned(&s.alice.identity.PublicKey) { - return errors.New("alice was not unbanned") - } - - return nil - } - - response, err := s.admin.UnbanUserFromCommunity(unbanRequest) - s.Require().NoError(err) - s.Require().Nil(checkUnbanned(response)) - - response, err = WaitOnMessengerResponse( - s.owner, - WaitCommunityCondition, - "MessengerResponse data not received", - ) - s.Require().NoError(err) - s.Require().NoError(checkUnbanned(response)) -} - -func (s *AdminMessengerCommunitiesSuite) adminKickAlice(communityID types.HexBytes, pubkey string) { - checkKicked := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, types.EncodeHex(communityID)) - if err != nil { - return err - } - - if modifiedCommmunity.HasMember(&s.alice.identity.PublicKey) { - return errors.New("alice was not kicked") - } - - return nil - } - - response, err := s.admin.RemoveUserFromCommunity( - communityID, - pubkey, - ) - - s.Require().NoError(err) - s.Require().Nil(checkKicked(response)) - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkKicked) -} - -func (s *AdminMessengerCommunitiesSuite) adminReorderCategory(reorderRequest *requests.ReorderCommunityCategories) { - checkCategoryReorder := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, types.EncodeHex(reorderRequest.CommunityID)) - if err != nil { - return err - } - - category, exist := modifiedCommmunity.Categories()[reorderRequest.CategoryID] - if !exist { - return errors.New("couldn't find community category") - } - - if int(category.Position) != reorderRequest.Position { - return errors.New("category was not reordered") - } - - return nil - } - - response, err := s.admin.ReorderCommunityCategories(reorderRequest) - s.Require().NoError(err) - s.Require().NoError(checkCategoryReorder(response)) - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkCategoryReorder) -} - -func (s *AdminMessengerCommunitiesSuite) adminReorderChannel(reorderRequest *requests.ReorderCommunityChat) { - checkChannelReorder := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, types.EncodeHex(reorderRequest.CommunityID)) - if err != nil { - return err - } - - chat, exist := modifiedCommmunity.Chats()[reorderRequest.ChatID] - if !exist { - return errors.New("couldn't find community chat") - } - - if int(chat.Position) != reorderRequest.Position { - return errors.New("chat position was not reordered") - } - - if chat.CategoryId != reorderRequest.CategoryID { - return errors.New("chat category was not reordered") - } - - return nil - } - - response, err := s.admin.ReorderCommunityChat(reorderRequest) - s.Require().NoError(err) - s.Require().NoError(checkChannelReorder(response)) - - s.checkClientsReceivedAdminEvent(WaitCommunityCondition, checkChannelReorder) -} - -func getModifiedCommunity(response *MessengerResponse, communityID string) (*communities.Community, error) { - if len(response.Communities()) == 0 { - return nil, errors.New("community not received") - } - - var modifiedCommmunity *communities.Community = nil - for _, c := range response.Communities() { - if c.IDString() == communityID { - modifiedCommmunity = c - } - } - - if modifiedCommmunity == nil { - return nil, errors.New("couldn't find community in response") - } - - return modifiedCommmunity, nil +func (s *AdminCommunityEventsSuite) TestMemberReceiveAdminEventsWhenOwnerOffline() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testMemberReceiveEventsWhenControlNodeOffline(s, community) } diff --git a/protocol/communities_messenger_helpers_test.go b/protocol/communities_messenger_helpers_test.go index 3f22d2381..778c63516 100644 --- a/protocol/communities_messenger_helpers_test.go +++ b/protocol/communities_messenger_helpers_test.go @@ -204,6 +204,53 @@ func joinCommunity(s *suite.Suite, community *communities.Community, owner *Mess s.Require().NoError(err) } +func joinOnRequestCommunity(s *suite.Suite, community *communities.Community, controlNode *Messenger, user *Messenger) { + // Request to join the community + request := &requests.RequestToJoinCommunity{CommunityID: community.ID()} + response, err := user.RequestToJoinCommunity(request) + s.Require().NoError(err) + s.Require().NotNil(response) + s.Require().Len(response.RequestsToJoinCommunity, 1) + + requestToJoin := response.RequestsToJoinCommunity[0] + s.Require().Equal(requestToJoin.PublicKey, common.PubkeyToHex(&user.identity.PublicKey)) + + response, err = WaitOnMessengerResponse( + controlNode, + func(r *MessengerResponse) bool { + return len(r.RequestsToJoinCommunity) > 0 + }, + "control node did not receive community request to join", + ) + s.Require().NoError(err) + + userRequestToJoin := response.RequestsToJoinCommunity[0] + s.Require().Equal(userRequestToJoin.PublicKey, common.PubkeyToHex(&user.identity.PublicKey)) + + // accept join request + acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: requestToJoin.ID} + response, err = controlNode.AcceptRequestToJoinCommunity(acceptRequestToJoin) + s.Require().NoError(err) + s.Require().NotNil(response) + + updatedCommunity := response.Communities()[0] + s.Require().NotNil(updatedCommunity) + s.Require().True(updatedCommunity.HasMember(&user.identity.PublicKey)) + + // receive request to join response + _, err = WaitOnMessengerResponse( + user, + func(r *MessengerResponse) bool { + return len(r.Communities()) > 0 + }, + "user did not receive request to join response", + ) + s.Require().NoError(err) + userCommunity, err := user.GetCommunityByID(community.ID()) + s.Require().NoError(err) + s.Require().True(userCommunity.HasMember(&user.identity.PublicKey)) +} + func sendChatMessage(s *suite.Suite, sender *Messenger, chatID string, text string) *common.Message { msg := &common.Message{ ChatMessage: protobuf.ChatMessage{ @@ -218,3 +265,29 @@ func sendChatMessage(s *suite.Suite, sender *Messenger, chatID string, text stri return msg } + +func grantPermission(s *suite.Suite, community *communities.Community, controlNode *Messenger, target *Messenger, role protobuf.CommunityMember_Roles) { + responseAddRole, err := controlNode.AddRoleToMember(&requests.AddRoleToMember{ + CommunityID: community.ID(), + User: common.PubkeyToHexBytes(target.IdentityPublicKey()), + Role: protobuf.CommunityMember_ROLE_ADMIN, + }) + s.Require().NoError(err) + + checkRole := func(response *MessengerResponse) bool { + if len(response.Communities()) == 0 { + return false + } + rCommunities := response.Communities() + s.Require().Len(rCommunities, 1) + s.Require().True(rCommunities[0].IsMemberAdmin(target.IdentityPublicKey())) + return true + } + + checkRole(responseAddRole) + + _, err = WaitOnMessengerResponse(target, func(response *MessengerResponse) bool { + return checkRole(response) + }, "community description changed message not received") + s.Require().NoError(err) +} diff --git a/protocol/communities_messenger_test.go b/protocol/communities_messenger_test.go index 21f14c3ca..755335f70 100644 --- a/protocol/communities_messenger_test.go +++ b/protocol/communities_messenger_test.go @@ -627,7 +627,6 @@ func (s *MessengerCommunitiesSuite) TestImportCommunity() { s.Require().Len(response.Communities(), 1) s.Require().Len(response.CommunitiesSettings(), 1) s.Require().True(response.Communities()[0].Joined()) - s.Require().True(response.Communities()[0].IsOwner()) s.Require().True(response.Communities()[0].IsControlNode()) community := response.Communities()[0] @@ -697,7 +696,7 @@ func (s *MessengerCommunitiesSuite) TestRemovePrivateKey() { s.Require().Len(response.Communities(), 1) community := response.Communities()[0] - s.Require().True(community.IsOwner()) + s.Require().True(community.IsControlNode()) s.Require().True(community.IsControlNode()) response, err = s.bob.RemovePrivateKey(community.ID()) @@ -705,7 +704,7 @@ func (s *MessengerCommunitiesSuite) TestRemovePrivateKey() { s.Require().Len(response.Communities(), 1) community = response.Communities()[0] - s.Require().True(community.IsOwner()) + s.Require().True(community.IsOwnerWithoutCommunityKey()) s.Require().False(community.IsControlNode()) } @@ -726,7 +725,7 @@ func (s *MessengerCommunitiesSuite) TestRolesAfterImportCommunity() { s.Require().Len(response.Communities(), 1) s.Require().Len(response.CommunitiesSettings(), 1) s.Require().True(response.Communities()[0].Joined()) - s.Require().True(response.Communities()[0].IsOwner()) + s.Require().True(response.Communities()[0].IsControlNode()) s.Require().True(response.Communities()[0].IsMemberOwner(&s.bob.identity.PublicKey)) s.Require().False(response.Communities()[0].IsMemberOwner(&s.alice.identity.PublicKey)) @@ -2520,8 +2519,12 @@ func (s *MessengerCommunitiesSuite) TestSyncCommunity() { s.Equal(newCommunity.Muted(), tnc.Muted()) s.Equal(newCommunity.Joined(), tnc.Joined()) s.Equal(newCommunity.Spectated(), tnc.Spectated()) - s.Equal(newCommunity.IsOwner(), tnc.IsOwner()) s.Equal(newCommunity.InvitationOnly(), tnc.InvitationOnly()) + + s.True(newCommunity.IsControlNode()) + s.False(newCommunity.IsOwnerWithoutCommunityKey()) + s.True(tnc.IsOwnerWithoutCommunityKey()) + s.False(tnc.IsControlNode()) } // TestSyncCommunity_RequestToJoin tests more complex pairing and syncing scenario where one paired device diff --git a/protocol/messenger.go b/protocol/messenger.go index 6374dfdf2..64f29cb94 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -2314,7 +2314,7 @@ func (m *Messenger) updateChatFirstMessageTimestamp(chat *Chat, timestamp uint32 return err } - if community.IsOwner() && chat.UpdateFirstMessageTimestamp(timestamp) { + if community.IsControlNode() && chat.UpdateFirstMessageTimestamp(timestamp) { community, changes, err := m.communitiesManager.EditChatFirstMessageTimestamp(community.ID(), chat.ID, chat.FirstMessageTimestamp) if err != nil { return err diff --git a/protocol/messenger_communities.go b/protocol/messenger_communities.go index 6456091db..b24cf5d90 100644 --- a/protocol/messenger_communities.go +++ b/protocol/messenger_communities.go @@ -1682,7 +1682,7 @@ func (m *Messenger) CreateCommunityTokenPermission(request *requests.CreateCommu return nil, err } - if community.IsOwner() { + if community.IsControlNode() { // check existing member permission once, then check periodically go func() { err := m.communitiesManager.CheckMemberPermissions(community, true) @@ -1715,7 +1715,7 @@ func (m *Messenger) EditCommunityTokenPermission(request *requests.EditCommunity // BECOME_MEMBER permissions and kick them if necessary // // We do this in a separate routine to not block this function - if community.IsOwner() { + if community.IsControlNode() { go func() { err := m.communitiesManager.CheckMemberPermissions(community, true) if err != nil { @@ -1743,7 +1743,7 @@ func (m *Messenger) DeleteCommunityTokenPermission(request *requests.DeleteCommu // check if members still fulfill the token criteria // We do this in a separate routine to not block this function - if community.IsOwner() { + if community.IsControlNode() { go func() { becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN)