Skip to content

Commit

Permalink
feat: kick all members after ownership change and auto-accept after s…
Browse files Browse the repository at this point in the history
…haring the address
  • Loading branch information
mprakhov committed Oct 25, 2023
1 parent b081258 commit 587fdf2
Show file tree
Hide file tree
Showing 11 changed files with 534 additions and 459 deletions.
1 change: 1 addition & 0 deletions protocol/activity_center.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const (
ActivityCenterMembershipStatusDeclined
ActivityCenterMembershipStatusAcceptedPending
ActivityCenterMembershipStatusDeclinedPending
ActivityCenterMembershipOwnershipChanged
)

type ActivityCenterQueryParamsRead uint
Expand Down
31 changes: 31 additions & 0 deletions protocol/communities/community.go
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,37 @@ func (o *Community) RemoveUserFromOrg(pk *ecdsa.PublicKey) (*protobuf.CommunityD
return o.config.CommunityDescription, nil
}

func (o *Community) RemoveAllUsersFromOrg() *CommunityChanges {
o.increaseClock()

myPublicKey := common.PubkeyToHex(o.config.MemberIdentity)
member := o.config.CommunityDescription.Members[myPublicKey]

membersToRemove := o.config.CommunityDescription.Members
delete(membersToRemove, myPublicKey)

changes := o.emptyCommunityChanges()
changes.MembersRemoved = membersToRemove

o.config.CommunityDescription.Members = make(map[string]*protobuf.CommunityMember)
o.config.CommunityDescription.Members[myPublicKey] = member

for chatID, chat := range o.config.CommunityDescription.Chats {
chatMembersToRemove := chat.Members
delete(chatMembersToRemove, myPublicKey)

chat.Members = make(map[string]*protobuf.CommunityMember)
chat.Members[myPublicKey] = member

changes.ChatsModified[chatID] = &CommunityChatChanges{
ChatModified: chat,
MembersRemoved: chatMembersToRemove,
}
}

return changes
}

func (o *Community) AddCommunityTokensMetadata(token *protobuf.CommunityTokenMetadata) (*protobuf.CommunityDescription, error) {
o.mutex.Lock()
defer o.mutex.Unlock()
Expand Down
76 changes: 68 additions & 8 deletions protocol/communities/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,15 +465,20 @@ func (m *Manager) runOwnerVerificationLoop() {
response, err := m.HandleCommunityDescriptionMessage(signer, description, communityToValidate.payload, nil, ownerPK)
if err != nil {
m.logger.Error("failed to handle community", zap.Error(err))
err = m.persistence.DeleteCommunityToValidate(communityToValidate.id, communityToValidate.clock)
if err != nil {
m.logger.Error("failed to delete community to validate", zap.Error(err))
}
continue
}

if response != nil {

m.logger.Info("community validated", zap.String("id", types.EncodeHex(communityToValidate.id)), zap.String("signer", common.PubkeyToHex(signer)))
m.publish(&Subscription{TokenCommunityValidated: response})
err := m.persistence.DeleteCommunitiesToValidateByCommunityID(communityToValidate.id)
if err != nil {
m.logger.Error("failed to delete community to validate", zap.Error(err))
m.logger.Error("failed to delete communities to validate", zap.Error(err))
}
break
}
Expand Down Expand Up @@ -1518,6 +1523,27 @@ func (m *Manager) DeleteCategory(request *requests.DeleteCommunityCategory) (*Co
return changes.Community, changes, nil
}

func (m *Manager) GenerateRequestsToJoinForAutoApprovalOnNewOwnership(communityID types.HexBytes, kickedMembers map[string]*protobuf.CommunityMember) error {
var requestsToJoin []*RequestToJoin
clock := uint64(time.Now().Unix())
for pubKeyStr := range kickedMembers {
requestToJoin := &RequestToJoin{
PublicKey: pubKeyStr,
Clock: clock,
CommunityID: communityID,
State: RequestToJoinStateAwaitingAddresses,
Our: true,
RevealedAccounts: make([]*protobuf.RevealedAccount, 0),
}

requestToJoin.CalculateID()

requestsToJoin = append(requestsToJoin, requestToJoin)
}

return m.persistence.SaveRequestsToJoin(requestsToJoin)
}

func (m *Manager) Queue(signer *ecdsa.PublicKey, community *Community, clock uint64, payload []byte) error {

m.logger.Info("queuing community", zap.String("id", community.IDString()), zap.String("signer", common.PubkeyToHex(signer)))
Expand Down Expand Up @@ -2459,13 +2485,19 @@ func (m *Manager) HandleCommunityRequestToJoin(signer *ecdsa.PublicKey, receiver
} else if existingRequestToJoin.State == RequestToJoinStateAcceptedPending {
requestToJoin.State = RequestToJoinStateAccepted
return community, requestToJoin, nil

} else if existingRequestToJoin.State == RequestToJoinStateAwaitingAddresses {
// community ownership changed, accept request automatically
requestToJoin.State = RequestToJoinStateAccepted
return community, requestToJoin, nil
}
}

// If user is already a member, then accept request automatically
// 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
acceptAutomatically := community.AcceptRequestToJoinAutomatically() || community.HasMember(signer)

if acceptAutomatically {
// Don't check permissions here,
// it will be done further in the processing pipeline.
Expand Down Expand Up @@ -3295,6 +3327,11 @@ func (m *Manager) GetPubsubTopic(communityID string) (string, error) {
return transport.GetPubsubTopic(community.Shard().TransportShard()), nil
}

func (m *Manager) RequestsToJoinForCommunityAwaitingAddresses(id types.HexBytes) ([]*RequestToJoin, error) {
m.logger.Info("fetching ownership changed invitations", zap.String("community-id", id.String()))
return m.persistence.RequestsToJoinForCommunityAwaitingAddresses(id)
}

func (m *Manager) CanPost(pk *ecdsa.PublicKey, communityID string, chatID string, grant []byte) (bool, error) {
community, err := m.GetByIDString(communityID)
if err != nil {
Expand Down Expand Up @@ -4467,7 +4504,7 @@ func (m *Manager) AddCommunityToken(token *community_token.CommunityToken, clock
}

if token.PrivilegesLevel == community_token.OwnerLevel {
err = m.promoteSelfToControlNode(community, clock)
_, err = m.promoteSelfToControlNode(community, clock)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -4793,7 +4830,7 @@ func (m *Manager) createCommunityTokenPermission(request *requests.CreateCommuni

}

func (m *Manager) PromoteSelfToControlNode(communityID types.HexBytes, clock uint64) (*Community, error) {
func (m *Manager) PromoteSelfToControlNode(communityID types.HexBytes, clock uint64) (*CommunityChanges, error) {
community, err := m.GetByID(communityID)
if err != nil {
return nil, err
Expand All @@ -4803,17 +4840,23 @@ func (m *Manager) PromoteSelfToControlNode(communityID types.HexBytes, clock uin
return nil, ErrOrgNotFound
}

err = m.promoteSelfToControlNode(community, clock)
ownerChanged, err := m.promoteSelfToControlNode(community, clock)
if err != nil {
return nil, err
}

return community, m.saveAndPublish(community)
if ownerChanged {
return community.RemoveAllUsersFromOrg(), m.saveAndPublish(community)
}

return community.emptyCommunityChanges(), m.saveAndPublish(community)
}

func (m *Manager) promoteSelfToControlNode(community *Community, clock uint64) error {
func (m *Manager) promoteSelfToControlNode(community *Community, clock uint64) (bool, error) {
ownerChanged := false
community.setPrivateKey(m.identity)
if !community.ControlNode().Equal(&m.identity.PublicKey) {
ownerChanged = true
community.setControlNode(&m.identity.PublicKey)
}

Expand All @@ -4822,15 +4865,21 @@ func (m *Manager) promoteSelfToControlNode(community *Community, clock uint64) e
Clock: clock,
InstallationId: m.installationID,
}

err := m.SaveSyncControlNode(community.ID(), syncControlNode)
if err != nil {
return err
return false, err
}
community.config.ControlDevice = true

_, err = community.AddRoleToMember(&m.identity.PublicKey, protobuf.CommunityMember_ROLE_OWNER)
if err != nil {
return false, err
}

community.increaseClock()

return nil
return ownerChanged, nil
}

func (m *Manager) shareRequestsToJoinWithNewPrivilegedMembers(community *Community, newPrivilegedMembers map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey) error {
Expand Down Expand Up @@ -4989,3 +5038,14 @@ func (m *Manager) SetSyncControlNode(id types.HexBytes, syncControlNode *protobu

return nil
}

func (m *Manager) GetCommunityRequestToJoinWithRevealedAddresses(pubKey string, communityID types.HexBytes) (*RequestToJoin, error) {
return m.persistence.GetCommunityRequestToJoinWithRevealedAddresses(pubKey, communityID)
}

func (m *Manager) SafeGetSignerPubKey(chainID uint64, communityID string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()

return m.ownerVerifier.SafeGetSignerPubKey(ctx, chainID, communityID)
}
69 changes: 68 additions & 1 deletion protocol/communities/persistence.go
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,10 @@ func (p *Persistence) DeclinedPendingRequestsToJoinForCommunity(id []byte) ([]*R
return p.RequestsToJoinForCommunityWithState(id, RequestToJoinStateDeclinedPending)
}

func (p *Persistence) RequestsToJoinForCommunityAwaitingAddresses(id []byte) ([]*RequestToJoin, error) {
return p.RequestsToJoinForCommunityWithState(id, RequestToJoinStateAwaitingAddresses)
}

func (p *Persistence) SetRequestToJoinState(pk string, communityID []byte, state RequestToJoinState) error {
_, err := p.db.Exec(`UPDATE communities_requests_to_join SET state = ? WHERE community_id = ? AND public_key = ?`, state, communityID, pk)
return err
Expand Down Expand Up @@ -1397,7 +1401,7 @@ func (p *Persistence) GetCommunityRequestsToJoinWithRevealedAddresses(communityI
SELECT r.id, r.public_key, r.clock, r.ens_name, r.chat_id, r.state, r.community_id, a.address, a.chain_ids, a.is_airdrop_address
FROM communities_requests_to_join r
LEFT JOIN communities_requests_to_join_revealed_addresses a ON r.id = a.request_id
WHERE community_id = ?`, communityID)
WHERE r.community_id = ? AND r.state != ?`, communityID, RequestToJoinStateAwaitingAddresses)

if err != nil {
return nil, err
Expand Down Expand Up @@ -1528,6 +1532,11 @@ func (p *Persistence) DeleteCommunitiesToValidateByCommunityID(communityID []byt
return err
}

func (p *Persistence) DeleteCommunityToValidate(communityID []byte, clock uint64) error {
_, err := p.db.Exec(`DELETE FROM communities_validate_signer WHERE id = ? AND clock = ?`, communityID, clock)
return err
}

func (p *Persistence) GetSyncControlNode(communityID types.HexBytes) (*protobuf.SyncCommunityControlNode, error) {
result := &protobuf.SyncCommunityControlNode{}

Expand Down Expand Up @@ -1560,3 +1569,61 @@ func (p *Persistence) SaveSyncControlNode(communityID types.HexBytes, clock uint
)
return err
}

func (p *Persistence) GetCommunityRequestToJoinWithRevealedAddresses(pubKey string, communityID []byte) (*RequestToJoin, error) {
requestToJoin, err := p.GetRequestToJoinByPkAndCommunityID(pubKey, communityID)
if err != nil {
return nil, err
}

revealedAccounts, err := p.GetRequestToJoinRevealedAddresses(requestToJoin.ID)
if err != nil {
return nil, err
}

requestToJoin.RevealedAccounts = revealedAccounts

return requestToJoin, nil
}

func (p *Persistence) SaveRequestsToJoin(requests []*RequestToJoin) (err error) {
tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
if err != nil {
return err
}

defer func() {
if err != nil {
// Rollback the transaction on error
_ = tx.Rollback()
}
}()

stmt, err := tx.Prepare(`INSERT OR REPLACE INTO communities_requests_to_join(id,public_key,clock,ens_name,chat_id,community_id,state)
VALUES (?, ?, ?, ?, ?, ?, ?)`)
if err != nil {
return err
}
defer stmt.Close()

for _, request := range requests {
var clock uint64
// Fetch any existing request to join
err = tx.QueryRow(`SELECT clock FROM communities_requests_to_join WHERE public_key = ? AND community_id = ?`, request.PublicKey, request.CommunityID).Scan(&clock)
if err != nil && err != sql.ErrNoRows {
return err
}

if clock >= request.Clock {
return ErrOldRequestToJoin
}

_, err = stmt.Exec(request.ID, request.PublicKey, request.Clock, request.ENSName, request.ChatID, request.CommunityID, request.State)
if err != nil {
return err
}
}

err = tx.Commit()
return err
}
1 change: 1 addition & 0 deletions protocol/communities/request_to_join.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
RequestToJoinStateCanceled
RequestToJoinStateAcceptedPending
RequestToJoinStateDeclinedPending
RequestToJoinStateAwaitingAddresses
)

type RequestToJoin struct {
Expand Down
10 changes: 9 additions & 1 deletion protocol/communities_messenger_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,16 @@ func newCommunitiesTestMessenger(shh types.Waku, privateKey *ecdsa.PrivateKey, l
}

func createCommunity(s *suite.Suite, owner *Messenger) (*communities.Community, *Chat) {
return createCommunityConfigurable(s, owner, protobuf.CommunityPermissions_NO_MEMBERSHIP)
}

func createOnRequestCommunity(s *suite.Suite, owner *Messenger) (*communities.Community, *Chat) {
return createCommunityConfigurable(s, owner, protobuf.CommunityPermissions_ON_REQUEST)
}

func createCommunityConfigurable(s *suite.Suite, owner *Messenger, permission protobuf.CommunityPermissions_Access) (*communities.Community, *Chat) {
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_NO_MEMBERSHIP,
Membership: permission,
Name: "status",
Color: "#ffffff",
Description: "status community description",
Expand Down
Loading

0 comments on commit 587fdf2

Please sign in to comment.