Fix pending join requests + API to get them (#3902)

Needed for https://github.com/status-im/status-desktop/issues/11851
This commit is contained in:
Jonathan Rainville 2023-08-18 15:52:13 -04:00 committed by GitHub
parent 5272f99b59
commit 3bf0bed78d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 213 additions and 96 deletions

View File

@ -310,6 +310,7 @@ type Subscription struct {
type CommunityResponse struct { type CommunityResponse struct {
Community *Community `json:"community"` Community *Community `json:"community"`
Changes *CommunityChanges `json:"changes"` Changes *CommunityChanges `json:"changes"`
RequestsToJoin []*RequestToJoin `json:"requestsToJoin"`
} }
type CommunityEventsMessageInvalidClockSignal struct { type CommunityEventsMessageInvalidClockSignal struct {
@ -1422,7 +1423,7 @@ func (m *Manager) HandleCommunityEventsMessage(signer *ecdsa.PublicKey, message
return nil, err return nil, err
} }
err = m.handleAdditionalAdminChanges(changes.Community) additionalCommunityResponse, err := m.handleAdditionalAdminChanges(changes.Community)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1451,6 +1452,7 @@ func (m *Manager) HandleCommunityEventsMessage(signer *ecdsa.PublicKey, message
return &CommunityResponse{ return &CommunityResponse{
Community: changes.Community, Community: changes.Community,
Changes: changes, Changes: changes,
RequestsToJoin: additionalCommunityResponse.RequestsToJoin,
}, nil }, nil
} }
@ -1502,37 +1504,7 @@ func (m *Manager) HandleCommunityEventsMessageRejected(signer *ecdsa.PublicKey,
return reapplyEventsMessage, nil return reapplyEventsMessage, nil
} }
func (m *Manager) HandleCommunityPrivilegedUserSyncMessage(signer *ecdsa.PublicKey, message *protobuf.CommunityPrivilegedUserSyncMessage) error { func (m *Manager) HandleRequestToJoinPrivilegedUserSyncMessage(message *protobuf.CommunityPrivilegedUserSyncMessage, communityID types.HexBytes) ([]*RequestToJoin, error) {
if signer == nil {
return errors.New("signer can't be nil")
}
community, err := m.persistence.GetByID(&m.identity.PublicKey, message.CommunityId)
if err != nil {
return err
}
if community == nil {
return ErrOrgNotFound
}
if !community.IsPrivilegedMember(&m.identity.PublicKey) {
return errors.New("user has no permissions to process privileged sync message")
}
err = validateCommunityPrivilegedUserSyncMessage(message)
if err != nil {
return err
}
switch message.Type {
case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN:
fallthrough
case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_REJECT_REQUEST_TO_JOIN:
if !common.IsPubKeyEqual(community.PublicKey(), signer) {
return errors.New("accepted/requested to join sync messages can be send only by the control node")
}
var state RequestToJoinState var state RequestToJoinState
if message.Type == protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN { if message.Type == protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN {
state = RequestToJoinStateAccepted state = RequestToJoinStateAccepted
@ -1540,6 +1512,7 @@ func (m *Manager) HandleCommunityPrivilegedUserSyncMessage(signer *ecdsa.PublicK
state = RequestToJoinStateDeclined state = RequestToJoinStateDeclined
} }
requestsToJoin := make([]*RequestToJoin, 0)
for signer, requestToJoinProto := range message.RequestToJoin { for signer, requestToJoinProto := range message.RequestToJoin {
requestToJoin := &RequestToJoin{ requestToJoin := &RequestToJoin{
PublicKey: signer, PublicKey: signer,
@ -1550,12 +1523,17 @@ func (m *Manager) HandleCommunityPrivilegedUserSyncMessage(signer *ecdsa.PublicK
} }
requestToJoin.CalculateID() requestToJoin.CalculateID()
_, err := m.saveOrUpdateRequestToJoin(signer, community.ID(), requestToJoin) _, err := m.saveOrUpdateRequestToJoin(signer, communityID, requestToJoin)
if err != nil { if err != nil {
return err return nil, err
} }
requestsToJoin = append(requestsToJoin, requestToJoin)
} }
case protobuf.CommunityPrivilegedUserSyncMessage_ADD_COMMUNITY_TOKENS:
return requestsToJoin, nil
}
func (m *Manager) HandleAddCommunityTokenPrivilegedUserSyncMessage(message *protobuf.CommunityPrivilegedUserSyncMessage, community *Community) error {
for _, token := range message.CommunityTokens { for _, token := range message.CommunityTokens {
token := community_token.FromCommunityTokenProtobuf(token) token := community_token.FromCommunityTokenProtobuf(token)
if !community.MemberCanManageToken(community.MemberIdentity(), token) { if !community.MemberCanManageToken(community.MemberIdentity(), token) {
@ -1571,38 +1549,44 @@ func (m *Manager) HandleCommunityPrivilegedUserSyncMessage(signer *ecdsa.PublicK
return m.persistence.AddCommunityToken(token) return m.persistence.AddCommunityToken(token)
} }
} }
}
return nil return nil
} }
func (m *Manager) handleAdditionalAdminChanges(community *Community) error { func (m *Manager) handleAdditionalAdminChanges(community *Community) (*CommunityResponse, error) {
communityResponse := CommunityResponse{
RequestsToJoin: make([]*RequestToJoin, 0),
}
if !(community.IsControlNode() || community.HasPermissionToSendCommunityEvents()) { if !(community.IsControlNode() || community.HasPermissionToSendCommunityEvents()) {
// we're a normal user/member node, so there's nothing for us to do here // we're a normal user/member node, so there's nothing for us to do here
return nil return &communityResponse, nil
} }
for i := range community.config.EventsData.Events { for i := range community.config.EventsData.Events {
communityEvent := &community.config.EventsData.Events[i] communityEvent := &community.config.EventsData.Events[i]
switch communityEvent.Type { switch communityEvent.Type {
case protobuf.CommunityEvent_COMMUNITY_REQUEST_TO_JOIN_ACCEPT: case protobuf.CommunityEvent_COMMUNITY_REQUEST_TO_JOIN_ACCEPT:
err := m.handleCommunityEventRequestAccepted(community, communityEvent) requestsToJoin, err := m.handleCommunityEventRequestAccepted(community, communityEvent)
if err != nil { if err != nil {
return err return nil, err
}
if requestsToJoin != nil {
communityResponse.RequestsToJoin = append(communityResponse.RequestsToJoin, requestsToJoin...)
} }
case protobuf.CommunityEvent_COMMUNITY_REQUEST_TO_JOIN_REJECT: case protobuf.CommunityEvent_COMMUNITY_REQUEST_TO_JOIN_REJECT:
err := m.handleCommunityEventRequestRejected(community, communityEvent) requestsToJoin, err := m.handleCommunityEventRequestRejected(community, communityEvent)
if err != nil { if err != nil {
return err return nil, err
}
if requestsToJoin != nil {
communityResponse.RequestsToJoin = append(communityResponse.RequestsToJoin, requestsToJoin...)
} }
default: default:
} }
} }
return nil return &communityResponse, nil
} }
func (m *Manager) saveOrUpdateRequestToJoin(signer string, communityID types.HexBytes, requestToJoin *RequestToJoin) (bool, error) { func (m *Manager) saveOrUpdateRequestToJoin(signer string, communityID types.HexBytes, requestToJoin *RequestToJoin) (bool, error) {
@ -1636,9 +1620,11 @@ func (m *Manager) saveOrUpdateRequestToJoin(signer string, communityID types.Hex
return updated, nil return updated, nil
} }
func (m *Manager) handleCommunityEventRequestAccepted(community *Community, communityEvent *CommunityEvent) error { func (m *Manager) handleCommunityEventRequestAccepted(community *Community, communityEvent *CommunityEvent) ([]*RequestToJoin, error) {
acceptedRequestsToJoin := make([]types.HexBytes, 0) acceptedRequestsToJoin := make([]types.HexBytes, 0)
requestsToJoin := make([]*RequestToJoin, 0)
for signer, request := range communityEvent.AcceptedRequestsToJoin { for signer, request := range communityEvent.AcceptedRequestsToJoin {
requestToJoin := &RequestToJoin{ requestToJoin := &RequestToJoin{
PublicKey: signer, PublicKey: signer,
@ -1651,7 +1637,7 @@ func (m *Manager) handleCommunityEventRequestAccepted(community *Community, comm
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 err return nil, err
} }
if community.IsControlNode() { if community.IsControlNode() {
@ -1670,7 +1656,7 @@ func (m *Manager) handleCommunityEventRequestAccepted(community *Community, comm
requestUpdated, err := m.saveOrUpdateRequestToJoin(signer, community.ID(), requestToJoin) requestUpdated, err := m.saveOrUpdateRequestToJoin(signer, community.ID(), requestToJoin)
if err != nil { if err != nil {
return err return nil, err
} }
if community.IsControlNode() && requestUpdated { if community.IsControlNode() && requestUpdated {
@ -1680,16 +1666,20 @@ func (m *Manager) handleCommunityEventRequestAccepted(community *Community, comm
// admin nodes), so we don't want to trigger an `AcceptRequestToJoin` in such cases. // admin nodes), so we don't want to trigger an `AcceptRequestToJoin` in such cases.
acceptedRequestsToJoin = append(acceptedRequestsToJoin, requestToJoin.ID) acceptedRequestsToJoin = append(acceptedRequestsToJoin, requestToJoin.ID)
} }
requestsToJoin = append(requestsToJoin, requestToJoin)
} }
if community.IsControlNode() { if community.IsControlNode() {
m.publish(&Subscription{AcceptedRequestsToJoin: acceptedRequestsToJoin}) m.publish(&Subscription{AcceptedRequestsToJoin: acceptedRequestsToJoin})
} }
return nil return requestsToJoin, nil
} }
func (m *Manager) handleCommunityEventRequestRejected(community *Community, communityEvent *CommunityEvent) error { func (m *Manager) handleCommunityEventRequestRejected(community *Community, communityEvent *CommunityEvent) ([]*RequestToJoin, error) {
rejectedRequestsToJoin := make([]types.HexBytes, 0) rejectedRequestsToJoin := make([]types.HexBytes, 0)
requestsToJoin := make([]*RequestToJoin, 0)
for signer, request := range communityEvent.RejectedRequestsToJoin { for signer, request := range communityEvent.RejectedRequestsToJoin {
requestToJoin := &RequestToJoin{ requestToJoin := &RequestToJoin{
PublicKey: signer, PublicKey: signer,
@ -1702,7 +1692,7 @@ func (m *Manager) handleCommunityEventRequestRejected(community *Community, comm
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 err return nil, err
} }
if community.IsControlNode() { if community.IsControlNode() {
@ -1721,17 +1711,19 @@ func (m *Manager) handleCommunityEventRequestRejected(community *Community, comm
requestUpdated, err := m.saveOrUpdateRequestToJoin(signer, community.ID(), requestToJoin) requestUpdated, err := m.saveOrUpdateRequestToJoin(signer, community.ID(), requestToJoin)
if err != nil { if err != nil {
return err return nil, err
} }
if community.IsControlNode() && requestUpdated { if community.IsControlNode() && requestUpdated {
rejectedRequestsToJoin = append(rejectedRequestsToJoin, requestToJoin.ID) rejectedRequestsToJoin = append(rejectedRequestsToJoin, requestToJoin.ID)
} }
requestsToJoin = append(requestsToJoin, requestToJoin)
} }
if community.IsControlNode() { if community.IsControlNode() {
m.publish(&Subscription{RejectedRequestsToJoin: rejectedRequestsToJoin}) m.publish(&Subscription{RejectedRequestsToJoin: rejectedRequestsToJoin})
} }
return nil return requestsToJoin, nil
} }
// markRequestToJoin marks all the pending requests to join as completed // markRequestToJoin marks all the pending requests to join as completed
@ -2110,7 +2102,7 @@ func (m *Manager) HandleCommunityRequestToJoin(signer *ecdsa.PublicKey, request
return nil, err return nil, err
} }
if existingRequestToJoin != nil { if existingRequestToJoin != nil && existingRequestToJoin.State != RequestToJoinStateCanceled {
// request to join was already processed by an admin and waits to get // request to join was already processed by an admin and waits to get
// confirmation for its decision // confirmation for its decision
// //
@ -4636,7 +4628,7 @@ func (m *Manager) HandleCommunityTokensMetadata(communityID string, communityTok
return nil return nil
} }
func validateCommunityPrivilegedUserSyncMessage(message *protobuf.CommunityPrivilegedUserSyncMessage) error { func (m *Manager) ValidateCommunityPrivilegedUserSyncMessage(message *protobuf.CommunityPrivilegedUserSyncMessage) error {
if message == nil { if message == nil {
return errors.New("invalid CommunityPrivilegedUserSyncMessage message") return errors.New("invalid CommunityPrivilegedUserSyncMessage message")
} }

View File

@ -374,7 +374,7 @@ func (p *Persistence) SaveRequestToJoin(request *RequestToJoin) (err error) {
return ErrOldRequestToJoin return ErrOldRequestToJoin
} }
_, err = tx.Exec(`INSERT INTO communities_requests_to_join(id,public_key,clock,ens_name,chat_id,community_id,state) VALUES (?, ?, ?, ?, ?, ?, ?)`, request.ID, request.PublicKey, request.Clock, request.ENSName, request.ChatID, request.CommunityID, request.State) _, err = tx.Exec(`INSERT OR REPLACE INTO communities_requests_to_join(id,public_key,clock,ens_name,chat_id,community_id,state) VALUES (?, ?, ?, ?, ?, ?, ?)`, request.ID, request.PublicKey, request.Clock, request.ENSName, request.ChatID, request.CommunityID, request.State)
return err return err
} }

View File

@ -1308,6 +1308,19 @@ func (m *Messenger) CancelRequestToJoinCommunity(request *requests.CancelRequest
return nil, err return nil, err
} }
if !community.AcceptRequestToJoinAutomatically() {
// send cancelation to community admins also
rawMessage.Payload = payload
privilegedMembers := community.GetPrivilegedMembers()
for _, privilegedMember := range privilegedMembers {
_, err := m.sender.SendPrivate(context.Background(), privilegedMember, &rawMessage)
if err != nil {
return nil, err
}
}
}
response := &MessengerResponse{} response := &MessengerResponse{}
response.AddCommunity(community) response.AddCommunity(community)
response.RequestsToJoinCommunity = append(response.RequestsToJoinCommunity, requestToJoin) response.RequestsToJoinCommunity = append(response.RequestsToJoinCommunity, requestToJoin)
@ -1354,6 +1367,8 @@ func (m *Messenger) AcceptRequestToJoinCommunity(request *requests.AcceptRequest
return nil, err return nil, err
} }
if community.IsControlNode() {
// If we are the control node, we send the response to the user
pk, err := common.HexToPubkey(requestToJoin.PublicKey) pk, err := common.HexToPubkey(requestToJoin.PublicKey)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1364,7 +1379,6 @@ func (m *Messenger) AcceptRequestToJoinCommunity(request *requests.AcceptRequest
return nil, err return nil, err
} }
if community.IsControlNode() {
requestToJoinResponseProto := &protobuf.CommunityRequestToJoinResponse{ requestToJoinResponseProto := &protobuf.CommunityRequestToJoinResponse{
Clock: community.Clock(), Clock: community.Clock(),
Accepted: true, Accepted: true,
@ -1437,6 +1451,7 @@ func (m *Messenger) AcceptRequestToJoinCommunity(request *requests.AcceptRequest
response := &MessengerResponse{} response := &MessengerResponse{}
response.AddCommunity(community) response.AddCommunity(community)
response.AddRequestToJoinCommunity(requestToJoin)
// Activity Center notification // Activity Center notification
notification, err := m.persistence.GetActivityCenterNotificationByID(request.ID) notification, err := m.persistence.GetActivityCenterNotificationByID(request.ID)
@ -1522,9 +1537,10 @@ func (m *Messenger) DeclineRequestToJoinCommunity(request *requests.DeclineReque
} }
response := &MessengerResponse{} response := &MessengerResponse{}
dbRequest, err = m.communitiesManager.GetRequestToJoin(request.ID)
response.AddRequestToJoinCommunity(dbRequest)
if notification != nil { if notification != nil {
dbRequest, err := m.communitiesManager.GetRequestToJoin(request.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -2159,6 +2175,26 @@ func (m *Messenger) PendingRequestsToJoinForCommunity(id types.HexBytes) ([]*com
return m.communitiesManager.PendingRequestsToJoinForCommunity(id) return m.communitiesManager.PendingRequestsToJoinForCommunity(id)
} }
func (m *Messenger) AllPendingRequestsToJoinForCommunity(id types.HexBytes) ([]*communities.RequestToJoin, error) {
pendingRequests, err := m.communitiesManager.PendingRequestsToJoinForCommunity(id)
if err != nil {
return nil, err
}
acceptedPendingRequests, err := m.communitiesManager.AcceptedPendingRequestsToJoinForCommunity(id)
if err != nil {
return nil, err
}
declinedPendingRequests, err := m.communitiesManager.DeclinedPendingRequestsToJoinForCommunity(id)
if err != nil {
return nil, err
}
pendingRequests = append(pendingRequests, acceptedPendingRequests...)
pendingRequests = append(pendingRequests, declinedPendingRequests...)
return pendingRequests, nil
}
func (m *Messenger) DeclinedRequestsToJoinForCommunity(id types.HexBytes) ([]*communities.RequestToJoin, error) { func (m *Messenger) DeclinedRequestsToJoinForCommunity(id types.HexBytes) ([]*communities.RequestToJoin, error) {
return m.communitiesManager.DeclinedRequestsToJoinForCommunity(id) return m.communitiesManager.DeclinedRequestsToJoinForCommunity(id)
} }
@ -2542,6 +2578,7 @@ func (m *Messenger) handleCommunityResponse(state *ReceivedMessageState, communi
state.Response.AddCommunity(community) state.Response.AddCommunity(community)
state.Response.CommunityChanges = append(state.Response.CommunityChanges, communityResponse.Changes) state.Response.CommunityChanges = append(state.Response.CommunityChanges, communityResponse.Changes)
state.Response.AddRequestsToJoinCommunity(communityResponse.RequestsToJoin)
// If we haven't joined the org, nothing to do // If we haven't joined the org, nothing to do
if !community.Joined() { if !community.Joined() {
@ -2608,6 +2645,41 @@ func (m *Messenger) handleCommunityResponse(state *ReceivedMessageState, communi
return err return err
} }
for _, requestToJoin := range communityResponse.RequestsToJoin {
// Activity Center notification
notification, err := m.persistence.GetActivityCenterNotificationByID(requestToJoin.ID)
if err != nil {
return err
}
if notification != nil {
notification.MembershipStatus = ActivityCenterMembershipStatusAccepted
switch requestToJoin.State {
case communities.RequestToJoinStateDeclined:
notification.MembershipStatus = ActivityCenterMembershipStatusDeclined
case communities.RequestToJoinStateAccepted:
notification.MembershipStatus = ActivityCenterMembershipStatusAccepted
case communities.RequestToJoinStateAcceptedPending:
notification.MembershipStatus = ActivityCenterMembershipStatusAcceptedPending
case communities.RequestToJoinStateDeclinedPending:
notification.MembershipStatus = ActivityCenterMembershipStatusDeclinedPending
default:
notification.MembershipStatus = ActivityCenterMembershipStatusPending
}
notification.Read = true
notification.Accepted = true
notification.UpdatedAt = m.getCurrentTimeInMillis()
err = m.addActivityCenterNotification(state.Response, notification)
if err != nil {
m.logger.Error("failed to save notification", zap.Error(err))
return err
}
}
}
return nil return nil
} }
@ -2639,7 +2711,55 @@ func (m *Messenger) handleCommunityEventsMessageRejected(state *ReceivedMessageS
} }
func (m *Messenger) handleCommunityPrivilegedUserSyncMessage(state *ReceivedMessageState, signer *ecdsa.PublicKey, message protobuf.CommunityPrivilegedUserSyncMessage) error { func (m *Messenger) handleCommunityPrivilegedUserSyncMessage(state *ReceivedMessageState, signer *ecdsa.PublicKey, message protobuf.CommunityPrivilegedUserSyncMessage) error {
return m.communitiesManager.HandleCommunityPrivilegedUserSyncMessage(signer, &message) if signer == nil {
return errors.New("signer can't be nil")
}
community, err := m.communitiesManager.GetByID(message.CommunityId)
if err != nil {
return err
}
if community == nil {
return errors.New("community not found")
}
if !community.IsPrivilegedMember(&m.identity.PublicKey) {
return errors.New("user has no permissions to process privileged sync message")
}
isControlNodeMsg := common.IsPubKeyEqual(community.PublicKey(), signer)
if !(isControlNodeMsg || community.IsPrivilegedMember(signer)) {
return errors.New("user has no permissions to send privileged sync message")
}
err = m.communitiesManager.ValidateCommunityPrivilegedUserSyncMessage(&message)
if err != nil {
return err
}
switch message.Type {
case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN:
fallthrough
case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_REJECT_REQUEST_TO_JOIN:
if !isControlNodeMsg {
return errors.New("accepted/requested to join sync messages can be send only by the control node")
}
requestsToJoin, err := m.communitiesManager.HandleRequestToJoinPrivilegedUserSyncMessage(&message, community.ID())
if err != nil {
return nil
}
state.Response.AddRequestsToJoinCommunity(requestsToJoin)
case protobuf.CommunityPrivilegedUserSyncMessage_ADD_COMMUNITY_TOKENS:
// TODO add tokens to the Response
err = m.communitiesManager.HandleAddCommunityTokenPrivilegedUserSyncMessage(&message, community)
if err != nil {
return nil
}
}
return nil
} }
func (m *Messenger) handleSyncCommunity(messageState *ReceivedMessageState, syncCommunity protobuf.SyncCommunity) error { func (m *Messenger) handleSyncCommunity(messageState *ReceivedMessageState, syncCommunity protobuf.SyncCommunity) error {

View File

@ -561,6 +561,11 @@ func (api *PublicAPI) PendingRequestsToJoinForCommunity(id types.HexBytes) ([]*c
return api.service.messenger.PendingRequestsToJoinForCommunity(id) return api.service.messenger.PendingRequestsToJoinForCommunity(id)
} }
// AllPendingRequestsToJoinForCommunity returns the all the pending requests to join, including accepted and rejected ones, for a given community
func (api *PublicAPI) AllPendingRequestsToJoinForCommunity(id types.HexBytes) ([]*communities.RequestToJoin, error) {
return api.service.messenger.AllPendingRequestsToJoinForCommunity(id)
}
// DeclinedRequestsToJoinForCommunity returns the declined requests to join for a given community // DeclinedRequestsToJoinForCommunity returns the declined requests to join for a given community
func (api *PublicAPI) DeclinedRequestsToJoinForCommunity(id types.HexBytes) ([]*communities.RequestToJoin, error) { func (api *PublicAPI) DeclinedRequestsToJoinForCommunity(id types.HexBytes) ([]*communities.RequestToJoin, error) {
return api.service.messenger.DeclinedRequestsToJoinForCommunity(id) return api.service.messenger.DeclinedRequestsToJoinForCommunity(id)