Skip to content

Commit

Permalink
chore: Added TokenMaster permission token type (#3848)
Browse files Browse the repository at this point in the history
  • Loading branch information
mprakhov authored Aug 4, 2023
1 parent 078f71a commit 3bd972d
Show file tree
Hide file tree
Showing 17 changed files with 994 additions and 607 deletions.
638 changes: 319 additions & 319 deletions appdatabase/migrations/bindata.go

Large diffs are not rendered by default.

24 changes: 21 additions & 3 deletions protocol/communities/community.go
Original file line number Diff line number Diff line change
Expand Up @@ -836,11 +836,25 @@ func (o *Community) RemoveUserFromOrg(pk *ecdsa.PublicKey) (*protobuf.CommunityD
func (o *Community) AddCommunityTokensMetadata(token *protobuf.CommunityTokenMetadata) (*protobuf.CommunityDescription, error) {
o.mutex.Lock()
defer o.mutex.Unlock()
if !o.IsControlNode() {
return nil, ErrNotControlNode

isControlNode := o.IsControlNode()
allowedToSendTokenEvents := o.IsOwnerWithoutCommunityKey() || o.IsTokenMaster()
if !isControlNode && !allowedToSendTokenEvents {
return nil, ErrInvalidManageTokensPermission
}

if allowedToSendTokenEvents {
err := o.addNewCommunityEvent(o.ToAddTokenMetadataCommunityEvent(token))
if err != nil {
return nil, err
}
}

o.config.CommunityDescription.CommunityTokensMetadata = append(o.config.CommunityDescription.CommunityTokensMetadata, token)
o.increaseClock()

if isControlNode {
o.increaseClock()
}

return o.config.CommunityDescription, nil
}
Expand Down Expand Up @@ -1151,6 +1165,10 @@ func (o *Community) IsOwnerWithoutCommunityKey() bool {
return o.config.PrivateKey == nil && o.IsMemberOwner(o.config.MemberIdentity)
}

func (o *Community) IsTokenMaster() bool {
return o.IsMemberTokenMaster(o.config.MemberIdentity)
}

func (o *Community) GetPrivilegedMembers() []*ecdsa.PublicKey {
privilegedMembers := make([]*ecdsa.PublicKey, 0)
members := o.GetMemberPubkeys()
Expand Down
11 changes: 11 additions & 0 deletions protocol/communities/community_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ func (o *Community) ToCommunityRequestToJoinRejectCommunityEvent(changes *Commun
}
}

func (o *Community) ToAddTokenMetadataCommunityEvent(tokenMetadata *protobuf.CommunityTokenMetadata) *CommunityEvent {
return &CommunityEvent{
CommunityEventClock: o.NewCommunityEventClock(),
Type: protobuf.CommunityEvent_COMMUNITY_TOKEN_ADD,
TokenMetadata: tokenMetadata,
}
}

func (o *Community) UpdateCommunityByEvents(communityEventMessage *CommunityEventsMessage) (*CommunityChanges, error) {
o.mutex.Lock()
defer o.mutex.Unlock()
Expand Down Expand Up @@ -364,6 +372,9 @@ func (o *Community) updateCommunityDescriptionByCommunityEvent(communityEvent Co
return err
}
o.unbanUserFromCommunity(pk)

case protobuf.CommunityEvent_COMMUNITY_TOKEN_ADD:
o.config.CommunityDescription.CommunityTokensMetadata = append(o.config.CommunityDescription.CommunityTokensMetadata, communityEvent.TokenMetadata)
}
return nil
}
Expand Down
8 changes: 8 additions & 0 deletions protocol/communities/community_event_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type CommunityEvent struct {
MembersAdded map[string]*protobuf.CommunityMember `json:"membersAdded,omitempty"`
RejectedRequestsToJoin map[string]*protobuf.CommunityRequestToJoin `json:"rejectedRequestsToJoin,omitempty"`
AcceptedRequestsToJoin map[string]*protobuf.CommunityRequestToJoin `json:"acceptedRequestsToJoin,omitempty"`
TokenMetadata *protobuf.CommunityTokenMetadata `json:"tokenMetadata,omitempty"`
RawPayload []byte `json:"rawPayload"`
}

Expand All @@ -37,6 +38,7 @@ func (e *CommunityEvent) ToProtobuf() *protobuf.CommunityEvent {
MembersAdded: e.MembersAdded,
RejectedRequestsToJoin: e.RejectedRequestsToJoin,
AcceptedRequestsToJoin: e.AcceptedRequestsToJoin,
TokenMetadata: e.TokenMetadata,
}
}

Expand All @@ -59,6 +61,7 @@ func CommunityEventFromProtobuf(raw []byte) (*CommunityEvent, error) {
MembersAdded: decodedEvent.MembersAdded,
RejectedRequestsToJoin: decodedEvent.RejectedRequestsToJoin,
AcceptedRequestsToJoin: decodedEvent.AcceptedRequestsToJoin,
TokenMetadata: decodedEvent.TokenMetadata,
RawPayload: encodedEvent,
}, nil
}
Expand Down Expand Up @@ -212,6 +215,11 @@ func validateCommunityEvent(communityEvent *CommunityEvent) error {
if len(communityEvent.MemberToAction) == 0 {
return errors.New("invalid community member unban event")
}

case protobuf.CommunityEvent_COMMUNITY_TOKEN_ADD:
if communityEvent.TokenMetadata == nil || len(communityEvent.TokenMetadata.ContractAddresses) == 0 {
return errors.New("invalid add community token event")
}
}
return nil
}
Expand Down
1 change: 1 addition & 0 deletions protocol/communities/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ var ErrMemberWalletNotFound = errors.New("member wallet not found")
var ErrNotEnoughPermissions = errors.New("not enough permissions for this community")
var ErrCannotRemoveOwnerOrAdmin = errors.New("not allowed to remove admin or owner")
var ErrCannotBanOwnerOrAdmin = errors.New("not allowed to ban admin or owner")
var ErrInvalidManageTokensPermission = errors.New("no privileges to manage tokens")
152 changes: 93 additions & 59 deletions protocol/communities/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -620,12 +620,12 @@ func (m *Manager) EditCommunityTokenPermission(request *requests.EditCommunityTo
return community, changes, nil
}

func (m *Manager) ReevaluateMembers(community *Community, removeAdmins bool) error {
func (m *Manager) ReevaluateMembers(community *Community) error {
becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN)
becomeTokenMasterPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER)

hasMemberPermissions := len(becomeMemberPermissions) > 0
hasAdminPermissions := len(becomeAdminPermissions) > 0

for memberKey, member := range community.Members() {
memberPubKey, err := common.HexToPubkey(memberKey)
Expand All @@ -637,12 +637,13 @@ func (m *Manager) ReevaluateMembers(community *Community, removeAdmins bool) err
continue
}

isTokenMaster := community.IsMemberTokenMaster(memberPubKey)
isAdmin := community.IsMemberAdmin(memberPubKey)
memberHasWallet := len(member.RevealedAccounts) > 0

// Check if user was not treated as an admin without wallet in open community
// Check if user has privilege role without sharing the account to controlNode
// or user treated as a member without wallet in closed community
if !memberHasWallet && (hasMemberPermissions || isAdmin) {
if !memberHasWallet && (hasMemberPermissions || isAdmin || isTokenMaster) {
_, err = community.RemoveUserFromOrg(memberPubKey)
if err != nil {
return err
Expand All @@ -652,49 +653,32 @@ func (m *Manager) ReevaluateMembers(community *Community, removeAdmins bool) err

accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(member.RevealedAccounts)

// Check if user is still an admin or can become an admin and do update of member role
removeAdminRole := false
if hasAdminPermissions {
permissionResponse, err := m.checkPermissionToJoin(becomeAdminPermissions, accountsAndChainIDs, true)
if err != nil {
return err
} else if permissionResponse.Satisfied && !isAdmin {
_, err = community.AddRoleToMember(memberPubKey, protobuf.CommunityMember_ROLE_ADMIN)
if err != nil {
return err
}
isAdmin = true
} else if !permissionResponse.Satisfied && isAdmin {
removeAdminRole = true
isAdmin = false
}
isTokenMaster, err = m.ReevaluatePrivelegedMember(community, becomeTokenMasterPermissions, accountsAndChainIDs, memberPubKey,
protobuf.CommunityMember_ROLE_TOKEN_MASTER, isTokenMaster)

if err != nil {
return err
}

// Remove admin role if user does not pass admin permissions or we do not have admin permissions but have an admin role
if removeAdminRole || isAdmin && removeAdmins {
_, err = community.RemoveRoleFromMember(memberPubKey, protobuf.CommunityMember_ROLE_ADMIN)
if err != nil {
return err
}
isAdmin = false
if isTokenMaster {
// Skip further validation if user has TokenMaster permissions
continue
}

isAdmin, err = m.ReevaluatePrivelegedMember(community, becomeAdminPermissions, accountsAndChainIDs, memberPubKey,
protobuf.CommunityMember_ROLE_ADMIN, isAdmin)

if err != nil {
return err
}

if isAdmin {
// Make sure admin is added to every channel
for channelID := range community.Chats() {
if !community.IsMemberInChat(memberPubKey, channelID) {
_, err = community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{protobuf.CommunityMember_ROLE_ADMIN})
if err != nil {
return err
}
}
}
// Skip further validation if user has admin permissions
// Skip further validation if user has Admin permissions
continue
}

if hasMemberPermissions {
permissionResponse, err := m.checkPermissionToJoin(becomeMemberPermissions, accountsAndChainIDs, true)
permissionResponse, err := m.checkPermissions(becomeMemberPermissions, accountsAndChainIDs, true)
if err != nil {
return err
}
Expand Down Expand Up @@ -776,7 +760,7 @@ func (m *Manager) ReevaluateMembersPeriodically(communityID types.HexBytes) {
m.periodicMembersReevaluationTasks.Delete(communityID.String())
}

err = m.ReevaluateMembers(community, true)
err = m.ReevaluateMembers(community)
if err != nil {
m.logger.Debug("failed to check member permissions", zap.Error(err))
}
Expand Down Expand Up @@ -1525,8 +1509,10 @@ func (m *Manager) CheckPermissionToJoin(id []byte, addresses []gethcommon.Addres

becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN)
becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
becomeTokenMasterPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER)

permissionsToJoin := append(becomeAdminPermissions, becomeMemberPermissions...)
permissionsToJoin = append(permissionsToJoin, becomeTokenMasterPermissions...)

allChainIDs, err := m.tokenManager.GetAllChainIDs()
if err != nil {
Expand All @@ -1545,28 +1531,32 @@ func (m *Manager) CheckPermissionToJoin(id []byte, addresses []gethcommon.Addres
}
return response, nil
}
return m.checkPermissionToJoin(permissionsToJoin, accountsAndChainIDs, false)
return m.checkPermissions(permissionsToJoin, accountsAndChainIDs, false)
}

func (m *Manager) accountsSatisfyPermissionsToJoin(community *Community, accounts []*protobuf.RevealedAccount) (satisfy bool, isAdmin bool, err error) {
func (m *Manager) accountsSatisfyPermissionsToJoin(community *Community, accounts []*protobuf.RevealedAccount) (bool, protobuf.CommunityMember_Roles, error) {
accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(accounts)
becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN)
becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
becomeTokenMasterPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER)

if len(becomeAdminPermissions) > 0 && m.accountsHasAdminPermission(becomeAdminPermissions, accountsAndChainIDs) {
return true, true, nil
if m.accountsHasPrivilegedPermission(becomeTokenMasterPermissions, accountsAndChainIDs) {
return true, protobuf.CommunityMember_ROLE_TOKEN_MASTER, nil
}
if m.accountsHasPrivilegedPermission(becomeAdminPermissions, accountsAndChainIDs) {
return true, protobuf.CommunityMember_ROLE_ADMIN, nil
}

if len(becomeMemberPermissions) > 0 {
permissionResponse, err := m.checkPermissionToJoin(becomeMemberPermissions, accountsAndChainIDs, true)
permissionResponse, err := m.checkPermissions(becomeMemberPermissions, accountsAndChainIDs, true)
if err != nil {
return false, false, err
return false, protobuf.CommunityMember_ROLE_NONE, err
}

return permissionResponse.Satisfied, false, nil
return permissionResponse.Satisfied, protobuf.CommunityMember_ROLE_NONE, nil
}

return true, false, nil
return true, protobuf.CommunityMember_ROLE_NONE, nil
}

func (m *Manager) accountsSatisfyPermissionsToJoinChannels(community *Community, accounts []*protobuf.RevealedAccount) (map[string]*protobuf.CommunityChat, error) {
Expand Down Expand Up @@ -1611,7 +1601,7 @@ func (m *Manager) AcceptRequestToJoin(request *requests.AcceptRequestToJoinCommu
return nil, err
}

permissionsSatisfied, isAdmin, err := m.accountsSatisfyPermissionsToJoin(community, revealedAccounts)
permissionsSatisfied, role, err := m.accountsSatisfyPermissionsToJoin(community, revealedAccounts)
if err != nil {
return nil, err
}
Expand All @@ -1621,8 +1611,8 @@ func (m *Manager) AcceptRequestToJoin(request *requests.AcceptRequestToJoinCommu
}

memberRoles := []protobuf.CommunityMember_Roles{}
if isAdmin {
memberRoles = []protobuf.CommunityMember_Roles{protobuf.CommunityMember_ROLE_ADMIN}
if role != protobuf.CommunityMember_ROLE_NONE {
memberRoles = []protobuf.CommunityMember_Roles{role}
}

pk, err := common.HexToPubkey(dbRequest.PublicKey)
Expand Down Expand Up @@ -1906,7 +1896,7 @@ type AccountChainIDsCombination struct {
ChainIDs []uint64 `json:"chainIds"`
}

func (c *CheckPermissionToJoinResponse) calculateSatisfied() {
func (c *CheckPermissionsResponse) calculateSatisfied() {
if len(c.Permissions) == 0 {
c.Satisfied = true
return
Expand Down Expand Up @@ -1945,10 +1935,6 @@ func calculateChainIDsSet(accountsAndChainIDs []*AccountChainIDsCombination, req
return revealedAccountsChainIDs
}

func (m *Manager) checkPermissionToJoin(permissions []*protobuf.CommunityTokenPermission, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckPermissionToJoinResponse, error) {
return m.checkPermissions(permissions, accountsAndChainIDs, shortcircuit)
}

// checkPermissions will retrieve balances and check whether the user has
// permission to join the community, if shortcircuit is true, it will stop as soon
// as we know the answer
Expand Down Expand Up @@ -4065,11 +4051,11 @@ func revealedAccountsToAccountsAndChainIDsCombination(revealedAccounts []*protob
return accountsAndChainIDs
}

func (m *Manager) accountsHasAdminPermission(becomeAdminPermissions []*protobuf.CommunityTokenPermission, accounts []*AccountChainIDsCombination) bool {
if len(becomeAdminPermissions) > 0 {
permissionResponse, err := m.checkPermissionToJoin(becomeAdminPermissions, accounts, true)
func (m *Manager) accountsHasPrivilegedPermission(privilegedPermissions []*protobuf.CommunityTokenPermission, accounts []*AccountChainIDsCombination) bool {
if len(privilegedPermissions) > 0 {
permissionResponse, err := m.checkPermissions(privilegedPermissions, accounts, true)
if err != nil {
m.logger.Warn("check admin permission failed: %v", zap.Error(err))
m.logger.Warn("check privileged permission failed: %v", zap.Error(err))
return false
}
return permissionResponse.Satisfied
Expand Down Expand Up @@ -4129,3 +4115,51 @@ func (m *Manager) fixupChannelMembers() error {

return nil
}

func (m *Manager) ReevaluatePrivelegedMember(community *Community, tokenPermissions []*protobuf.CommunityTokenPermission,
accountsAndChainIDs []*AccountChainIDsCombination, memberPubKey *ecdsa.PublicKey,
privilegedRole protobuf.CommunityMember_Roles, alreadyHasPrivilegedRole bool) (bool, error) {

hasPrivilegedRolePermissions := len(tokenPermissions) > 0
removeCurrentRole := false

if hasPrivilegedRolePermissions {
permissionResponse, err := m.checkPermissions(tokenPermissions, accountsAndChainIDs, true)
if err != nil {
return alreadyHasPrivilegedRole, err
} else if permissionResponse.Satisfied && !alreadyHasPrivilegedRole {
_, err = community.AddRoleToMember(memberPubKey, privilegedRole)
if err != nil {
return alreadyHasPrivilegedRole, err
}
alreadyHasPrivilegedRole = true
} else if !permissionResponse.Satisfied && alreadyHasPrivilegedRole {
removeCurrentRole = true
alreadyHasPrivilegedRole = false
}
}

// Remove privileged role if user does not pass role permissions check or
// Community does not have permissions but user has a role
if removeCurrentRole || (!hasPrivilegedRolePermissions && alreadyHasPrivilegedRole) {
_, err := community.RemoveRoleFromMember(memberPubKey, privilegedRole)
if err != nil {
return alreadyHasPrivilegedRole, err
}
alreadyHasPrivilegedRole = false
}

if alreadyHasPrivilegedRole {
// Make sure privileged user is added to every channel
for channelID := range community.Chats() {
if !community.IsMemberInChat(memberPubKey, channelID) {
_, err := community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{privilegedRole})
if err != nil {
return alreadyHasPrivilegedRole, err
}
}
}
}

return alreadyHasPrivilegedRole, nil
}
Loading

0 comments on commit 3bd972d

Please sign in to comment.