refactor: simplify community requests logic

With the recent introduction of pending states, the community requests
logic became more complex. This commit simplifies the flow and
appropriately delegates logic to its corresponding abstraction levels:
messenger, manager and community. Additionally, it eliminates
redundancies in notifications and request-saving mechanism.
This commit is contained in:
Patryk Osmaczko 2023-10-12 23:42:03 +02:00 committed by osmaczko
parent b932cc97bb
commit a9cde06e44
6 changed files with 186 additions and 208 deletions

View File

@ -1021,12 +1021,19 @@ func (o *Community) ValidateRequestToJoin(signer *ecdsa.PublicKey, request *prot
o.mutex.Lock() o.mutex.Lock()
defer o.mutex.Unlock() defer o.mutex.Unlock()
// If we are not admin, fuggetaboutit if o.IsControlNode() {
if !o.IsControlNode() && !o.HasPermissionToSendCommunityEvents() { // TODO: Enable this once mobile supports revealed addresses.
// if len(request.RevealedAccounts) == 0 {
// return errors.New("no addresses revealed")
// }
} else if o.HasPermissionToSendCommunityEvents() {
if o.AcceptRequestToJoinAutomatically() {
return errors.New("auto-accept community requests can only be processed by the control node")
}
} else {
return ErrNotAdmin return ErrNotAdmin
} }
// If the org is ens name only, then reject if not present
if o.config.CommunityDescription.Permissions.EnsOnly && len(request.EnsName) == 0 { if o.config.CommunityDescription.Permissions.EnsOnly && len(request.EnsName) == 0 {
return ErrCantRequestAccess return ErrCantRequestAccess
} }
@ -1040,6 +1047,19 @@ func (o *Community) ValidateRequestToJoin(signer *ecdsa.PublicKey, request *prot
return err return err
} }
if o.isBanned(signer) {
return ErrCantRequestAccess
}
timeNow := uint64(time.Now().Unix())
requestTimeOutClock, err := AddTimeoutToRequestToJoinClock(request.Clock)
if err != nil {
return err
}
if timeNow >= requestTimeOutClock {
return errors.New("request is expired")
}
return nil return nil
} }

View File

@ -338,21 +338,25 @@ func (s *CommunitySuite) TestValidateRequestToJoin() {
request := &protobuf.CommunityRequestToJoin{ request := &protobuf.CommunityRequestToJoin{
EnsName: "donvanvliet.stateofus.eth", EnsName: "donvanvliet.stateofus.eth",
CommunityId: s.communityID, CommunityId: s.communityID,
Clock: uint64(time.Now().Unix()),
} }
requestWithChatID := &protobuf.CommunityRequestToJoin{ requestWithChatID := &protobuf.CommunityRequestToJoin{
EnsName: "donvanvliet.stateofus.eth", EnsName: "donvanvliet.stateofus.eth",
CommunityId: s.communityID, CommunityId: s.communityID,
ChatId: testChatID1, ChatId: testChatID1,
Clock: uint64(time.Now().Unix()),
} }
requestWithoutENS := &protobuf.CommunityRequestToJoin{ requestWithoutENS := &protobuf.CommunityRequestToJoin{
CommunityId: s.communityID, CommunityId: s.communityID,
Clock: uint64(time.Now().Unix()),
} }
requestWithChatWithoutENS := &protobuf.CommunityRequestToJoin{ requestWithChatWithoutENS := &protobuf.CommunityRequestToJoin{
CommunityId: s.communityID, CommunityId: s.communityID,
ChatId: testChatID1, ChatId: testChatID1,
Clock: uint64(time.Now().Unix()),
} }
// MATRIX // MATRIX

View File

@ -2308,25 +2308,17 @@ func (m *Manager) DeclineRequestToJoin(dbRequest *RequestToJoin) (*Community, er
return community, nil return community, nil
} }
func (m *Manager) isUserRejectedFromCommunity(signer *ecdsa.PublicKey, community *Community, requestClock uint64) (bool, error) { func (m *Manager) shouldUserRetainDeclined(signer *ecdsa.PublicKey, community *Community, requestClock uint64) (bool, error) {
declinedRequestsToJoin, err := m.persistence.DeclinedRequestsToJoinForCommunity(community.ID()) requestID := CalculateRequestID(common.PubkeyToHex(signer), types.HexBytes(community.IDString()))
request, err := m.persistence.GetRequestToJoin(requestID)
if err != nil { if err != nil {
if err == sql.ErrNoRows {
return false, nil
}
return false, err return false, err
} }
for _, req := range declinedRequestsToJoin { return request.ShouldRetainDeclined(requestClock)
if req.PublicKey == common.PubkeyToHex(signer) {
dbRequestTimeOutClock, err := AddTimeoutToRequestToJoinClock(req.Clock)
if err != nil {
return false, err
}
if requestClock < dbRequestTimeOutClock {
return true, nil
}
}
}
return false, nil
} }
func (m *Manager) HandleCommunityCancelRequestToJoin(signer *ecdsa.PublicKey, request *protobuf.CommunityCancelRequestToJoin) (*RequestToJoin, error) { func (m *Manager) HandleCommunityCancelRequestToJoin(signer *ecdsa.PublicKey, request *protobuf.CommunityCancelRequestToJoin) (*RequestToJoin, error) {
@ -2338,11 +2330,11 @@ func (m *Manager) HandleCommunityCancelRequestToJoin(signer *ecdsa.PublicKey, re
return nil, ErrOrgNotFound return nil, ErrOrgNotFound
} }
isUserRejected, err := m.isUserRejectedFromCommunity(signer, community, request.Clock) retainDeclined, err := m.shouldUserRetainDeclined(signer, community, request.Clock)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if isUserRejected { if retainDeclined {
return nil, ErrCommunityRequestAlreadyRejected return nil, ErrCommunityRequestAlreadyRejected
} }
@ -2359,41 +2351,18 @@ func (m *Manager) HandleCommunityCancelRequestToJoin(signer *ecdsa.PublicKey, re
return requestToJoin, nil return requestToJoin, nil
} }
func (m *Manager) HandleCommunityRequestToJoin(signer *ecdsa.PublicKey, receiver *ecdsa.PublicKey, request *protobuf.CommunityRequestToJoin) (*RequestToJoin, error) { func (m *Manager) HandleCommunityRequestToJoin(signer *ecdsa.PublicKey, receiver *ecdsa.PublicKey, request *protobuf.CommunityRequestToJoin) (*Community, *RequestToJoin, error) {
community, err := m.persistence.GetByID(&m.identity.PublicKey, request.CommunityId) community, err := m.persistence.GetByID(&m.identity.PublicKey, request.CommunityId)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
if community == nil { if community == nil {
return nil, ErrOrgNotFound return nil, nil, ErrOrgNotFound
} }
// don't process request as admin if community is configured as auto-accept err = community.ValidateRequestToJoin(signer, request)
if !community.IsControlNode() && community.AcceptRequestToJoinAutomatically() {
return nil, nil
}
// control node must receive requests to join only on community address
// ignore duplicate messages sent to control node pubKey
if community.IsControlNode() && receiver.Equal(m.identity) {
return nil, errors.New("duplicate msg sent to the owner")
}
isUserRejected, err := m.isUserRejectedFromCommunity(signer, community, request.Clock)
if err != nil { if err != nil {
return nil, err return nil, nil, err
}
if isUserRejected {
return nil, ErrCommunityRequestAlreadyRejected
}
// Banned member can't request to join community
if community.isBanned(signer) {
return nil, ErrCantRequestAccess
}
if err := community.ValidateRequestToJoin(signer, request); err != nil {
return nil, err
} }
requestToJoin := &RequestToJoin{ requestToJoin := &RequestToJoin{
@ -2404,57 +2373,73 @@ func (m *Manager) HandleCommunityRequestToJoin(signer *ecdsa.PublicKey, receiver
State: RequestToJoinStatePending, State: RequestToJoinStatePending,
RevealedAccounts: request.RevealedAccounts, RevealedAccounts: request.RevealedAccounts,
} }
requestToJoin.CalculateID() requestToJoin.CalculateID()
existingRequestToJoin, err := m.persistence.GetRequestToJoin(requestToJoin.ID) existingRequestToJoin, err := m.persistence.GetRequestToJoin(requestToJoin.ID)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
return nil, err return nil, nil, err
} }
if existingRequestToJoin == nil || existingRequestToJoin.State == RequestToJoinStateCanceled { if existingRequestToJoin == nil {
if err := m.persistence.SaveRequestToJoin(requestToJoin); err != nil { err = m.SaveRequestToJoin(requestToJoin)
return nil, err if err != nil {
return nil, nil, err
}
} else {
retainDeclined, err := existingRequestToJoin.ShouldRetainDeclined(request.Clock)
if err != nil {
return nil, nil, err
}
if retainDeclined {
return nil, nil, ErrCommunityRequestAlreadyRejected
}
switch existingRequestToJoin.State {
case RequestToJoinStatePending, RequestToJoinStateDeclined, RequestToJoinStateCanceled:
// Another request have been received, save request back to pending state
err = m.SaveRequestToJoin(requestToJoin)
if err != nil {
return nil, nil, err
}
} }
} }
if community.IsControlNode() { if community.IsControlNode() {
// request to join was already processed by an admin and waits to get // verify if revealed addresses indeed belong to requester
// confirmation for its decision for _, revealedAccount := range request.RevealedAccounts {
// recoverParams := account.RecoverParams{
// we're only interested in immediately declining any declined/pending Message: types.EncodeHex(crypto.Keccak256(crypto.CompressPubkey(signer), community.ID(), requestToJoin.ID)),
// requests here, because if it's accepted/pending, we still need to perform Signature: types.EncodeHex(revealedAccount.Signature),
// some checks
if existingRequestToJoin != nil && existingRequestToJoin.State == RequestToJoinStateDeclinedPending {
requestToJoin.State = RequestToJoinStateDeclined
return requestToJoin, nil
}
if len(request.RevealedAccounts) > 0 {
// verify if revealed addresses indeed belong to requester
for _, revealedAccount := range request.RevealedAccounts {
recoverParams := account.RecoverParams{
Message: types.EncodeHex(crypto.Keccak256(crypto.CompressPubkey(signer), community.ID(), requestToJoin.ID)),
Signature: types.EncodeHex(revealedAccount.Signature),
}
matching, err := m.accountsManager.CanRecover(recoverParams, types.HexToAddress(revealedAccount.Address))
if err != nil {
return nil, err
}
if !matching {
// if ownership of only one wallet address cannot be verified,
// we mark the request as cancelled and stop
requestToJoin.State = RequestToJoinStateDeclined
return requestToJoin, nil
}
} }
// Save revealed addresses + signatures so they can later be added matching, err := m.accountsManager.CanRecover(recoverParams, types.HexToAddress(revealedAccount.Address))
// to the control node's local table of known revealed addresses
err = m.persistence.SaveRequestToJoinRevealedAddresses(requestToJoin.ID, requestToJoin.RevealedAccounts)
if err != nil { if err != nil {
return nil, err return nil, nil, err
}
if !matching {
// if ownership of only one wallet address cannot be verified,
// we mark the request as cancelled and stop
requestToJoin.State = RequestToJoinStateDeclined
return community, requestToJoin, nil
}
}
// Save revealed addresses + signatures so they can later be added
// to the control node's local table of known revealed addresses
err = m.persistence.SaveRequestToJoinRevealedAddresses(requestToJoin.ID, requestToJoin.RevealedAccounts)
if err != nil {
return nil, nil, err
}
if existingRequestToJoin != nil {
// request to join was already processed by privileged user
// and waits to get confirmation for its decision
if existingRequestToJoin.State == RequestToJoinStateDeclinedPending {
requestToJoin.State = RequestToJoinStateDeclined
return community, requestToJoin, nil
} else if existingRequestToJoin.State == RequestToJoinStateAcceptedPending {
requestToJoin.State = RequestToJoinStateAccepted
return community, requestToJoin, nil
} }
} }
@ -2462,23 +2447,15 @@ func (m *Manager) HandleCommunityRequestToJoin(signer *ecdsa.PublicKey, receiver
// It may happen when member removes itself from community and then tries to rejoin // It may happen when member removes itself from community and then tries to rejoin
// More specifically, CommunityRequestToLeave may be delivered later than CommunityRequestToJoin, or not delivered at all // More specifically, CommunityRequestToLeave may be delivered later than CommunityRequestToJoin, or not delivered at all
acceptAutomatically := community.AcceptRequestToJoinAutomatically() || community.HasMember(signer) acceptAutomatically := community.AcceptRequestToJoinAutomatically() || community.HasMember(signer)
// If the request to join was already accepted by another admin, if acceptAutomatically {
// we mark it as accepted so it won't be in pending state, even if the community
// is not set to auto-accept
acceptedByAdmin := existingRequestToJoin != nil && existingRequestToJoin.State == RequestToJoinStateAcceptedPending
if acceptAutomatically || acceptedByAdmin {
err = m.markRequestToJoinAsAccepted(signer, community)
if err != nil {
return nil, err
}
// Don't check permissions here, // Don't check permissions here,
// it will be done further in the processing pipeline. // it will be done further in the processing pipeline.
requestToJoin.State = RequestToJoinStateAccepted requestToJoin.State = RequestToJoinStateAccepted
return requestToJoin, nil return community, requestToJoin, nil
} }
} }
return requestToJoin, nil return community, requestToJoin, nil
} }
func (m *Manager) HandleCommunityEditSharedAddresses(signer *ecdsa.PublicKey, request *protobuf.CommunityEditSharedAddresses) error { func (m *Manager) HandleCommunityEditSharedAddresses(signer *ecdsa.PublicKey, request *protobuf.CommunityEditSharedAddresses) error {

View File

@ -74,6 +74,19 @@ func (r *RequestToJoin) Empty() bool {
return len(r.ID)+len(r.PublicKey)+int(r.Clock)+len(r.ENSName)+len(r.ChatID)+len(r.CommunityID)+int(r.State) == 0 return len(r.ID)+len(r.PublicKey)+int(r.Clock)+len(r.ENSName)+len(r.ChatID)+len(r.CommunityID)+int(r.State) == 0
} }
func (r *RequestToJoin) ShouldRetainDeclined(clock uint64) (bool, error) {
if r.State != RequestToJoinStateDeclined {
return false, nil
}
declineExpiryClock, err := AddTimeoutToRequestToJoinClock(r.Clock)
if err != nil {
return false, err
}
return clock < declineExpiryClock, nil
}
func AddTimeoutToRequestToJoinClock(clock uint64) (uint64, error) { func AddTimeoutToRequestToJoinClock(clock uint64) (uint64, error) {
requestToJoinClock, err := strconv.ParseInt(fmt.Sprint(clock), 10, 64) requestToJoinClock, err := strconv.ParseInt(fmt.Sprint(clock), 10, 64)
if err != nil { if err != nil {

View File

@ -1500,16 +1500,7 @@ func (m *Messenger) CancelRequestToJoinCommunity(request *requests.CancelRequest
return response, nil return response, nil
} }
func (m *Messenger) AcceptRequestToJoinCommunity(request *requests.AcceptRequestToJoinCommunity) (*MessengerResponse, error) { func (m *Messenger) acceptRequestToJoinCommunity(requestToJoin *communities.RequestToJoin) (*MessengerResponse, error) {
if err := request.Validate(); err != nil {
return nil, err
}
requestToJoin, err := m.communitiesManager.GetRequestToJoin(request.ID)
if err != nil {
return nil, err
}
community, err := m.communitiesManager.AcceptRequestToJoin(requestToJoin) community, err := m.communitiesManager.AcceptRequestToJoin(requestToJoin)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1577,12 +1568,11 @@ func (m *Messenger) AcceptRequestToJoinCommunity(request *requests.AcceptRequest
response.AddCommunity(community) response.AddCommunity(community)
response.AddRequestToJoinCommunity(requestToJoin) response.AddRequestToJoinCommunity(requestToJoin)
// Activity Center notification // Update existing notification
notification, err := m.persistence.GetActivityCenterNotificationByID(request.ID) notification, err := m.persistence.GetActivityCenterNotificationByID(requestToJoin.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if notification != nil { if notification != nil {
notification.MembershipStatus = ActivityCenterMembershipStatusAccepted notification.MembershipStatus = ActivityCenterMembershipStatusAccepted
if community.HasPermissionToSendCommunityEvents() { if community.HasPermissionToSendCommunityEvents() {
@ -1602,17 +1592,21 @@ func (m *Messenger) AcceptRequestToJoinCommunity(request *requests.AcceptRequest
return response, nil return response, nil
} }
func (m *Messenger) DeclineRequestToJoinCommunity(request *requests.DeclineRequestToJoinCommunity) (*MessengerResponse, error) { func (m *Messenger) AcceptRequestToJoinCommunity(request *requests.AcceptRequestToJoinCommunity) (*MessengerResponse, error) {
if err := request.Validate(); err != nil { if err := request.Validate(); err != nil {
return nil, err return nil, err
} }
dbRequest, err := m.communitiesManager.GetRequestToJoin(request.ID) requestToJoin, err := m.communitiesManager.GetRequestToJoin(request.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
community, err := m.communitiesManager.DeclineRequestToJoin(dbRequest) return m.acceptRequestToJoinCommunity(requestToJoin)
}
func (m *Messenger) declineRequestToJoinCommunity(requestToJoin *communities.RequestToJoin) (*MessengerResponse, error) {
community, err := m.communitiesManager.DeclineRequestToJoin(requestToJoin)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1620,9 +1614,9 @@ func (m *Messenger) DeclineRequestToJoinCommunity(request *requests.DeclineReque
if community.IsControlNode() { if community.IsControlNode() {
// Notify privileged members that request to join was rejected // Notify privileged members that request to join was rejected
// Send request to join without revealed addresses // Send request to join without revealed addresses
dbRequest.RevealedAccounts = make([]*protobuf.RevealedAccount, 0) requestToJoin.RevealedAccounts = make([]*protobuf.RevealedAccount, 0)
declinedRequestsToJoin := make(map[string]*protobuf.CommunityRequestToJoin) declinedRequestsToJoin := make(map[string]*protobuf.CommunityRequestToJoin)
declinedRequestsToJoin[dbRequest.PublicKey] = dbRequest.ToCommunityRequestToJoinProtobuf() declinedRequestsToJoin[requestToJoin.PublicKey] = requestToJoin.ToCommunityRequestToJoinProtobuf()
syncMsg := &protobuf.CommunityPrivilegedUserSyncMessage{ syncMsg := &protobuf.CommunityPrivilegedUserSyncMessage{
Type: protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_REJECT_REQUEST_TO_JOIN, Type: protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_REJECT_REQUEST_TO_JOIN,
@ -1654,25 +1648,16 @@ func (m *Messenger) DeclineRequestToJoinCommunity(request *requests.DeclineReque
} }
} }
// Activity Center notification response := &MessengerResponse{}
notification, err := m.persistence.GetActivityCenterNotificationByID(request.ID) response.AddCommunity(community)
response.AddRequestToJoinCommunity(requestToJoin)
// Update existing notification
notification, err := m.persistence.GetActivityCenterNotificationByID(requestToJoin.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
response := &MessengerResponse{}
dbRequest, err = m.communitiesManager.GetRequestToJoin(request.ID)
response.AddRequestToJoinCommunity(dbRequest)
if notification != nil { if notification != nil {
if err != nil {
return nil, err
}
community, err := m.communitiesManager.GetByID(dbRequest.CommunityID)
if err != nil {
return nil, err
}
notification.MembershipStatus = ActivityCenterMembershipStatusDeclined notification.MembershipStatus = ActivityCenterMembershipStatusDeclined
if community.HasPermissionToSendCommunityEvents() { if community.HasPermissionToSendCommunityEvents() {
notification.MembershipStatus = ActivityCenterMembershipStatusDeclinedPending notification.MembershipStatus = ActivityCenterMembershipStatusDeclinedPending
@ -1691,6 +1676,19 @@ func (m *Messenger) DeclineRequestToJoinCommunity(request *requests.DeclineReque
return response, nil return response, nil
} }
func (m *Messenger) DeclineRequestToJoinCommunity(request *requests.DeclineRequestToJoinCommunity) (*MessengerResponse, error) {
if err := request.Validate(); err != nil {
return nil, err
}
requestToJoin, err := m.communitiesManager.GetRequestToJoin(request.ID)
if err != nil {
return nil, err
}
return m.declineRequestToJoinCommunity(requestToJoin)
}
func (m *Messenger) LeaveCommunity(communityID types.HexBytes) (*MessengerResponse, error) { func (m *Messenger) LeaveCommunity(communityID types.HexBytes) (*MessengerResponse, error) {
notifications, err := m.persistence.DismissAllActivityCenterNotificationsFromCommunity(communityID.String(), m.getCurrentTimeInMillis()) notifications, err := m.persistence.DismissAllActivityCenterNotificationsFromCommunity(communityID.String(), m.getCurrentTimeInMillis())
if err != nil { if err != nil {

View File

@ -1465,76 +1465,22 @@ func (m *Messenger) HandleCommunityCancelRequestToJoin(state *ReceivedMessageSta
// HandleCommunityRequestToJoin handles an community request to join // HandleCommunityRequestToJoin handles an community request to join
func (m *Messenger) HandleCommunityRequestToJoin(state *ReceivedMessageState, requestToJoinProto *protobuf.CommunityRequestToJoin, statusMessage *v1protocol.StatusMessage) error { func (m *Messenger) HandleCommunityRequestToJoin(state *ReceivedMessageState, requestToJoinProto *protobuf.CommunityRequestToJoin, statusMessage *v1protocol.StatusMessage) error {
if requestToJoinProto.CommunityId == nil {
return ErrInvalidCommunityID
}
timeNow := uint64(time.Now().Unix())
requestTimeOutClock, err := communities.AddTimeoutToRequestToJoinClock(requestToJoinProto.Clock)
if err != nil {
return err
}
if timeNow >= requestTimeOutClock {
return errors.New("request is expired")
}
signer := state.CurrentMessageState.PublicKey signer := state.CurrentMessageState.PublicKey
receiver := statusMessage.Dst community, requestToJoin, err := m.communitiesManager.HandleCommunityRequestToJoin(signer, statusMessage.Dst, requestToJoinProto)
requestToJoin, err := m.communitiesManager.HandleCommunityRequestToJoin(signer, receiver, requestToJoinProto)
if err != nil {
return err
}
// not interested, stop further processing
if requestToJoin == nil {
return nil
}
if requestToJoin.State == communities.RequestToJoinStateAccepted {
accept := &requests.AcceptRequestToJoinCommunity{
ID: requestToJoin.ID,
}
_, err = m.AcceptRequestToJoinCommunity(accept)
if err != nil {
if err == communities.ErrNoPermissionToJoin {
// only control node will end up here as it's the only one that
// performed token permission checks
requestToJoin.State = communities.RequestToJoinStateDeclined
} else {
return err
}
}
}
if requestToJoin.State == communities.RequestToJoinStateDeclined {
cancel := &requests.DeclineRequestToJoinCommunity{
ID: requestToJoin.ID,
}
_, err = m.DeclineRequestToJoinCommunity(cancel)
if err != nil {
return err
}
return nil
}
community, err := m.communitiesManager.GetByID(requestToJoinProto.CommunityId)
if err != nil { if err != nil {
return err return err
} }
contactID := contactIDFromPublicKey(signer) switch requestToJoin.State {
case communities.RequestToJoinStatePending:
contact, _ := state.AllContacts.Load(contactIDFromPublicKey(signer))
if len(requestToJoinProto.DisplayName) != 0 {
contact.DisplayName = requestToJoinProto.DisplayName
state.ModifiedContacts.Store(contact.ID, true)
state.AllContacts.Store(contact.ID, contact)
state.ModifiedContacts.Store(contact.ID, true)
}
contact, _ := state.AllContacts.Load(contactID)
if len(requestToJoinProto.DisplayName) != 0 {
contact.DisplayName = requestToJoinProto.DisplayName
state.ModifiedContacts.Store(contact.ID, true)
state.AllContacts.Store(contact.ID, contact)
state.ModifiedContacts.Store(contact.ID, true)
}
if requestToJoin.State == communities.RequestToJoinStatePending {
if state.Response.RequestsToJoinCommunity == nil { if state.Response.RequestsToJoinCommunity == nil {
state.Response.RequestsToJoinCommunity = make([]*communities.RequestToJoin, 0) state.Response.RequestsToJoinCommunity = make([]*communities.RequestToJoin, 0)
} }
@ -1553,32 +1499,52 @@ func (m *Messenger) HandleCommunityRequestToJoin(state *ReceivedMessageState, re
Deleted: false, Deleted: false,
UpdatedAt: m.getCurrentTimeInMillis(), UpdatedAt: m.getCurrentTimeInMillis(),
} }
err = m.addActivityCenterNotification(state.Response, notification) err = m.addActivityCenterNotification(state.Response, notification)
if err != nil { if err != nil {
m.logger.Error("failed to save notification", zap.Error(err)) m.logger.Error("failed to save notification", zap.Error(err))
return err return err
} }
} else {
// Activity Center notification, updating existing for accepted/declined case communities.RequestToJoinStateDeclined:
notification, err := m.persistence.GetActivityCenterNotificationByID(requestToJoin.ID) response, err := m.declineRequestToJoinCommunity(requestToJoin)
if err != nil { if err == nil {
err := state.Response.Merge(response)
if err != nil {
return err
}
} else {
return err return err
} }
if notification != nil { case communities.RequestToJoinStateAccepted:
if requestToJoin.State == communities.RequestToJoinStateAccepted { response, err := m.acceptRequestToJoinCommunity(requestToJoin)
notification.MembershipStatus = ActivityCenterMembershipStatusAccepted if err == nil {
} else { err := state.Response.Merge(response) // new member has been added
notification.MembershipStatus = ActivityCenterMembershipStatusDeclined
}
notification.UpdatedAt = m.getCurrentTimeInMillis()
err = m.addActivityCenterNotification(state.Response, notification)
if err != nil { if err != nil {
m.logger.Error("failed to save notification", zap.Error(err))
return err return err
} }
} else if err == communities.ErrNoPermissionToJoin {
// only control node will end up here as it's the only one that
// performed token permission checks
response, err = m.declineRequestToJoinCommunity(requestToJoin)
if err == nil {
err := state.Response.Merge(response)
if err != nil {
return err
}
} else {
return err
}
} else {
return err
} }
case communities.RequestToJoinStateCanceled:
// cancellation is handled by separate message
fallthrough
case communities.RequestToJoinStateAcceptedPending, communities.RequestToJoinStateDeclinedPending:
// request can be marked as pending only manually
return errors.New("invalid request state")
} }
return nil return nil