fix: save revealed accounts regardless of permissions granted (#3609)

This commit is contained in:
Mikhail Rogachev 2023-06-16 11:10:32 +04:00 committed by GitHub
parent e26c2a7095
commit 51e3d800bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 172 additions and 33 deletions

View File

@ -62,7 +62,14 @@ type RecoverParams struct {
Signature string `json:"signature"` Signature string `json:"signature"`
} }
// Manager represents account manager interface. // Interface represents account manager interface
type Interface interface {
GetVerifiedWalletAccount(db *accounts.Database, address, password string) (*SelectedExtKey, error)
Sign(rpcParams SignParams, verifiedAccount *SelectedExtKey) (result types.HexBytes, err error)
Recover(rpcParams RecoverParams) (addr types.Address, err error)
}
// Manager represents account manager implementation
type Manager struct { type Manager struct {
mu sync.RWMutex mu sync.RWMutex
rpcClient *rpc.Client rpcClient *rpc.Client

View File

@ -69,7 +69,7 @@ type Manager struct {
subscriptions []chan *Subscription subscriptions []chan *Subscription
ensVerifier *ens.Verifier ensVerifier *ens.Verifier
identity *ecdsa.PrivateKey identity *ecdsa.PrivateKey
accountsManager *account.GethManager accountsManager account.Interface
tokenManager TokenManager tokenManager TokenManager
logger *zap.Logger logger *zap.Logger
stdoutLogger *zap.Logger stdoutLogger *zap.Logger
@ -122,7 +122,7 @@ func (t *HistoryArchiveDownloadTask) Cancel() {
} }
type managerOptions struct { type managerOptions struct {
accountsManager *account.GethManager accountsManager account.Interface
tokenManager TokenManager tokenManager TokenManager
walletConfig *params.WalletConfig walletConfig *params.WalletConfig
openseaClientBuilder openseaClientBuilder openseaClientBuilder openseaClientBuilder
@ -168,7 +168,7 @@ func (m *DefaultTokenManager) GetBalancesByChain(ctx context.Context, accounts,
type ManagerOption func(*managerOptions) type ManagerOption func(*managerOptions)
func WithAccountManager(accountsManager *account.GethManager) ManagerOption { func WithAccountManager(accountsManager account.Interface) ManagerOption {
return func(opts *managerOptions) { return func(opts *managerOptions) {
opts.accountsManager = accountsManager opts.accountsManager = accountsManager
} }
@ -1571,15 +1571,15 @@ func (m *Manager) AcceptRequestToJoin(request *requests.AcceptRequestToJoinCommu
becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN) becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN)
becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER) becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
revealedAccounts := make([]*protobuf.RevealedAccount, 0)
revealedAccounts, err := m.persistence.GetRequestToJoinRevealedAddresses(dbRequest.ID)
if err != nil {
return nil, err
}
memberRole := protobuf.CommunityMember_ROLE_NONE memberRole := protobuf.CommunityMember_ROLE_NONE
if len(becomeMemberPermissions) > 0 || len(becomeAdminPermissions) > 0 { if len(becomeMemberPermissions) > 0 || len(becomeAdminPermissions) > 0 {
revealedAccounts, err := m.persistence.GetRequestToJoinRevealedAddresses(dbRequest.ID)
if err != nil {
return nil, err
}
accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(revealedAccounts) accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(revealedAccounts)
@ -1845,7 +1845,38 @@ func (m *Manager) HandleCommunityRequestToJoin(signer *ecdsa.PublicKey, request
return nil, err return nil, err
} }
requestToJoin.State = RequestToJoinStateAccepted requestToJoin.State = RequestToJoinStateAccepted
return requestToJoin, nil }
// Save revealed addresses + signatures so they can later be added
// to the community member list when the request is accepted
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),
}
recovered, err := m.accountsManager.Recover(recoverParams)
if err != nil {
return nil, err
}
if recovered.Hex() != revealedAccount.Address {
// if ownership of only one wallet address cannot be verified,
// we mark the request as cancelled and stop
err = m.markRequestToJoinAsCanceled(signer, community)
if err != nil {
return nil, err
}
requestToJoin.State = RequestToJoinStateDeclined
return requestToJoin, nil
}
}
err = m.persistence.SaveRequestToJoinRevealedAddresses(requestToJoin)
if err != nil {
return nil, err
}
} }
return requestToJoin, nil return requestToJoin, nil

View File

@ -18,6 +18,10 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"go.uber.org/zap" "go.uber.org/zap"
gethcommon "github.com/ethereum/go-ethereum/common"
hexutil "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/account/generator" "github.com/status-im/status-go/account/generator"
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" 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/crypto"
@ -39,6 +43,47 @@ import (
"github.com/status-im/status-go/waku" "github.com/status-im/status-go/waku"
) )
const AdminPassword = "123456"
const AlicePassword = "qwerty"
const BobPassword = "bob123"
var walletAddress = "0x0100000000000000000000000000000000000000"
type AccountManagerMock struct {
AccountsMap map[string]string
}
type TestTokenManager struct {
}
func (m *TestTokenManager) GetAllChainIDs() ([]uint64, error) {
return []uint64{5}, nil
}
func (m *TestTokenManager) GetBalancesByChain(ctx context.Context, accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) (map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big, error) {
return nil, nil
}
func (m *AccountManagerMock) GetVerifiedWalletAccount(db *accounts.Database, address, password string) (*account.SelectedExtKey, error) {
if truePassword, ok := m.AccountsMap[address]; ok {
if password == truePassword {
return &account.SelectedExtKey{
Address: types.HexToAddress(address),
}, nil
}
return nil, errors.New("password doesn't match")
}
return nil, errors.New("address doesn't exist")
}
func (m *AccountManagerMock) Recover(rpcParams account.RecoverParams) (addr types.Address, err error) {
return types.HexToAddress(walletAddress), nil
}
func (m *AccountManagerMock) Sign(rpcParams account.SignParams, verifiedAccount *account.SelectedExtKey) (result types.HexBytes, err error) {
return types.HexBytes{}, nil
}
func TestMessengerCommunitiesSuite(t *testing.T) { func TestMessengerCommunitiesSuite(t *testing.T) {
suite.Run(t, new(MessengerCommunitiesSuite)) suite.Run(t, new(MessengerCommunitiesSuite))
} }
@ -63,9 +108,9 @@ func (s *MessengerCommunitiesSuite) SetupTest() {
s.shh = gethbridge.NewGethWakuWrapper(shh) s.shh = gethbridge.NewGethWakuWrapper(shh)
s.Require().NoError(shh.Start()) s.Require().NoError(shh.Start())
s.admin = s.newMessenger() s.admin = s.newMessenger(AdminPassword)
s.bob = s.newMessenger() s.bob = s.newMessenger(BobPassword)
s.alice = s.newMessenger() s.alice = s.newMessenger(AlicePassword)
_, err := s.admin.Start() _, err := s.admin.Start()
s.Require().NoError(err) s.Require().NoError(err)
_, err = s.bob.Start() _, err = s.bob.Start()
@ -81,14 +126,18 @@ func (s *MessengerCommunitiesSuite) TearDownTest() {
_ = s.logger.Sync() _ = s.logger.Sync()
} }
func (s *MessengerCommunitiesSuite) newMessengerWithOptions(shh types.Waku, privateKey *ecdsa.PrivateKey, options []Option) *Messenger { func (s *MessengerCommunitiesSuite) newMessengerWithOptions(shh types.Waku, privateKey *ecdsa.PrivateKey, password string, options []Option) *Messenger {
accountsManager := &AccountManagerMock{}
accountsManager.AccountsMap = make(map[string]string)
accountsManager.AccountsMap[walletAddress] = types.EncodeHex(crypto.Keccak256([]byte(password)))
m, err := NewMessenger( m, err := NewMessenger(
"Test", "Test",
privateKey, privateKey,
&testNode{shh: shh}, &testNode{shh: shh},
uuid.New().String(), uuid.New().String(),
nil, nil,
nil, accountsManager,
options..., options...,
) )
s.Require().NoError(err) s.Require().NoError(err)
@ -126,10 +175,21 @@ func (s *MessengerCommunitiesSuite) newMessengerWithOptions(shh types.Waku, priv
_ = m.settings.CreateSettings(setting, config) _ = m.settings.CreateSettings(setting, config)
// add wallet account with keypair
kp := accounts.GetProfileKeypairForTest(false, true, false)
kp.Accounts[0].Address = types.HexToAddress(walletAddress)
err = m.settings.SaveOrUpdateKeypair(kp)
s.Require().NoError(err)
walletAccounts, err := m.settings.GetAccounts()
s.Require().NoError(err)
s.Require().Len(walletAccounts, 1)
s.Require().Equal(walletAccounts[0].Type, accounts.AccountTypeGenerated)
return m return m
} }
func (s *MessengerCommunitiesSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger { func (s *MessengerCommunitiesSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey, password string) *Messenger {
tmpfile, err := ioutil.TempFile("", "accounts-tests-") tmpfile, err := ioutil.TempFile("", "accounts-tests-")
s.Require().NoError(err) s.Require().NoError(err)
madb, err := multiaccounts.InitializeDB(tmpfile.Name()) madb, err := multiaccounts.InitializeDB(tmpfile.Name())
@ -137,6 +197,7 @@ func (s *MessengerCommunitiesSuite) newMessengerWithKey(shh types.Waku, privateK
acc := generator.NewAccount(privateKey, nil) acc := generator.NewAccount(privateKey, nil)
iai := acc.ToIdentifiedAccountInfo("") iai := acc.ToIdentifiedAccountInfo("")
tm := &TestTokenManager{}
options := []Option{ options := []Option{
WithCustomLogger(s.logger), WithCustomLogger(s.logger),
@ -144,15 +205,22 @@ func (s *MessengerCommunitiesSuite) newMessengerWithKey(shh types.Waku, privateK
WithMultiAccounts(madb), WithMultiAccounts(madb),
WithAccount(iai.ToMultiAccount()), WithAccount(iai.ToMultiAccount()),
WithDatasync(), WithDatasync(),
WithTokenManager(tm),
} }
return s.newMessengerWithOptions(shh, privateKey, options) return s.newMessengerWithOptions(shh, privateKey, password, options)
} }
func (s *MessengerCommunitiesSuite) newMessenger() *Messenger { func (s *MessengerCommunitiesSuite) newMessenger(password string) *Messenger {
privateKey, err := crypto.GenerateKey() privateKey, err := crypto.GenerateKey()
s.Require().NoError(err) s.Require().NoError(err)
return s.newMessengerWithKey(s.shh, privateKey) return s.newMessengerWithKey(s.shh, privateKey, password)
}
func (s *MessengerCommunitiesSuite) requestToJoinCommunity(user *Messenger, communityID types.HexBytes, password string) (*MessengerResponse, error) {
passwdHash := types.EncodeHex(crypto.Keccak256([]byte(password)))
request := &requests.RequestToJoinCommunity{CommunityID: communityID, Password: passwdHash}
return user.RequestToJoinCommunity(request)
} }
func (s *MessengerCommunitiesSuite) TestCreateCommunity() { func (s *MessengerCommunitiesSuite) TestCreateCommunity() {
@ -187,7 +255,7 @@ func (s *MessengerCommunitiesSuite) TestCreateCommunity_WithoutDefaultChannel()
} }
func (s *MessengerCommunitiesSuite) TestRetrieveCommunity() { func (s *MessengerCommunitiesSuite) TestRetrieveCommunity() {
alice := s.newMessenger() alice := s.newMessenger(AlicePassword)
description := &requests.CreateCommunity{ description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_NO_MEMBERSHIP, Membership: protobuf.CommunityPermissions_NO_MEMBERSHIP,
@ -502,10 +570,10 @@ func (s *MessengerCommunitiesSuite) advertiseCommunityTo(community *communities.
s.Require().NoError(err) s.Require().NoError(err)
} }
func (s *MessengerCommunitiesSuite) joinCommunity(community *communities.Community, user *Messenger) { func (s *MessengerCommunitiesSuite) joinCommunity(community *communities.Community, user *Messenger, password string) {
// Request to join the community // Request to join the community
request := &requests.RequestToJoinCommunity{CommunityID: community.ID()} response, err := s.requestToJoinCommunity(user, community.ID(), password)
response, err := user.RequestToJoinCommunity(request)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(response) s.Require().NotNil(response)
s.Require().Len(response.RequestsToJoinCommunity, 1) s.Require().Len(response.RequestsToJoinCommunity, 1)
@ -564,8 +632,8 @@ func (s *MessengerCommunitiesSuite) TestCommunityContactCodeAdvertisement() {
s.advertiseCommunityTo(community, s.bob) s.advertiseCommunityTo(community, s.bob)
s.advertiseCommunityTo(community, s.alice) s.advertiseCommunityTo(community, s.alice)
s.joinCommunity(community, s.bob) s.joinCommunity(community, s.bob, BobPassword)
s.joinCommunity(community, s.alice) s.joinCommunity(community, s.alice, AlicePassword)
// Trigger ContactCodeAdvertisement // Trigger ContactCodeAdvertisement
err = s.bob.SetDisplayName("bobby") err = s.bob.SetDisplayName("bobby")
@ -2354,8 +2422,8 @@ func (s *MessengerCommunitiesSuite) TestLeaveAndRejoinCommunity() {
s.advertiseCommunityTo(community, s.alice) s.advertiseCommunityTo(community, s.alice)
s.advertiseCommunityTo(community, s.bob) s.advertiseCommunityTo(community, s.bob)
s.joinCommunity(community, s.alice) s.joinCommunity(community, s.alice, AlicePassword)
s.joinCommunity(community, s.bob) s.joinCommunity(community, s.bob, BobPassword)
joinedCommunities, err := s.admin.communitiesManager.Joined() joinedCommunities, err := s.admin.communitiesManager.Joined()
s.Require().NoError(err) s.Require().NoError(err)
@ -2418,7 +2486,7 @@ func (s *MessengerCommunitiesSuite) TestLeaveAndRejoinCommunity() {
s.Require().Equal(3, numberInactiveChats) s.Require().Equal(3, numberInactiveChats)
// alice can rejoin // alice can rejoin
s.joinCommunity(community, s.alice) s.joinCommunity(community, s.alice, AlicePassword)
joinedCommunities, err = s.admin.communitiesManager.Joined() joinedCommunities, err = s.admin.communitiesManager.Joined()
s.Require().NoError(err) s.Require().NoError(err)
@ -2884,7 +2952,7 @@ func (s *MessengerCommunitiesSuite) TestSyncCommunity_RequestToJoin() {
response, err = s.alice.RequestToJoinCommunity(&requests.RequestToJoinCommunity{CommunityID: community.ID()}) response, err = s.alice.RequestToJoinCommunity(&requests.RequestToJoinCommunity{CommunityID: community.ID()})
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(response) s.Require().NotNil(response)
s.Len(response.RequestsToJoinCommunity, 1) s.Require().Len(response.RequestsToJoinCommunity, 1)
s.Require().Len(response.ActivityCenterNotifications(), 1) s.Require().Len(response.ActivityCenterNotifications(), 1)
@ -3433,7 +3501,8 @@ func (s *MessengerCommunitiesSuite) TestCommunityBanUserRequesToJoin() {
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Len(response.Communities(), 1) s.Require().Len(response.Communities(), 1)
request := &requests.RequestToJoinCommunity{CommunityID: community.ID()} passwdHash := types.EncodeHex(crypto.Keccak256([]byte(AlicePassword)))
request := &requests.RequestToJoinCommunity{CommunityID: community.ID(), Password: passwdHash}
// We try to join the org // We try to join the org
_, rtj, err := s.alice.communitiesManager.RequestToJoin(&s.alice.identity.PublicKey, request) _, rtj, err := s.alice.communitiesManager.RequestToJoin(&s.alice.identity.PublicKey, request)
@ -3585,3 +3654,24 @@ func (s *MessengerCommunitiesSuite) TestHandleImport() {
s.Require().NotNil(chat) s.Require().NotNil(chat)
s.Require().Equal(0, int(chat.UnviewedMessagesCount)) s.Require().Equal(0, int(chat.UnviewedMessagesCount))
} }
func (s *MessengerCommunitiesSuite) TestJoinedCommunityMembersSharedAddress() {
community := s.createCommunity()
s.advertiseCommunityTo(community, s.alice)
s.advertiseCommunityTo(community, s.bob)
s.joinCommunity(community, s.alice, AlicePassword)
s.joinCommunity(community, s.bob, BobPassword)
community, err := s.admin.GetCommunityByID(community.ID())
s.Require().NoError(err)
s.Require().Equal(3, community.MembersCount())
for pubKey, member := range community.Members() {
if pubKey != common.PubkeyToHex(&s.admin.identity.PublicKey) {
s.Require().Len(member.RevealedAccounts, 1)
s.Require().Equal(member.RevealedAccounts[0].Address, walletAddress)
}
}
}

View File

@ -111,7 +111,7 @@ type Messenger struct {
pushNotificationClient *pushnotificationclient.Client pushNotificationClient *pushnotificationclient.Client
pushNotificationServer *pushnotificationserver.Server pushNotificationServer *pushnotificationserver.Server
communitiesManager *communities.Manager communitiesManager *communities.Manager
accountsManager *account.GethManager accountsManager account.Interface
mentionsManager *MentionManager mentionsManager *MentionManager
logger *zap.Logger logger *zap.Logger
@ -255,7 +255,7 @@ func NewMessenger(
node types.Node, node types.Node,
installationID string, installationID string,
peerStore *mailservers.PeerStore, peerStore *mailservers.PeerStore,
accountsManager *account.GethManager, accountsManager account.Interface,
opts ...Option, opts ...Option,
) (*Messenger, error) { ) (*Messenger, error) {
var messenger *Messenger var messenger *Messenger
@ -425,7 +425,10 @@ func NewMessenger(
managerOptions := []communities.ManagerOption{ managerOptions := []communities.ManagerOption{
communities.WithAccountManager(accountsManager), communities.WithAccountManager(accountsManager),
} }
if c.rpcClient != nil {
if c.tokenManager != nil {
managerOptions = append(managerOptions, communities.WithTokenManager(c.tokenManager))
} else if c.rpcClient != nil {
tokenManager := token.NewTokenManager(database, c.rpcClient, c.rpcClient.NetworkManager) tokenManager := token.NewTokenManager(database, c.rpcClient, c.rpcClient.NetworkManager)
managerOptions = append(managerOptions, communities.WithTokenManager(communities.NewDefaultTokenManager(tokenManager))) managerOptions = append(managerOptions, communities.WithTokenManager(communities.NewDefaultTokenManager(tokenManager)))
} }

View File

@ -90,6 +90,7 @@ type config struct {
walletService *wallet.Service walletService *wallet.Service
httpServer *server.MediaServer httpServer *server.MediaServer
rpcClient *rpc.Client rpcClient *rpc.Client
tokenManager communities.TokenManager
verifyTransactionClient EthClient verifyTransactionClient EthClient
verifyENSURL string verifyENSURL string
@ -334,3 +335,10 @@ func WithWalletService(s *wallet.Service) Option {
return nil return nil
} }
} }
func WithTokenManager(tokenManager communities.TokenManager) Option {
return func(c *config) error {
c.tokenManager = tokenManager
return nil
}
}