diff --git a/services/connector/api.go b/services/connector/api.go index 58b851b86c5..3ff9f7ecf46 100644 --- a/services/connector/api.go +++ b/services/connector/api.go @@ -22,7 +22,7 @@ type API struct { func NewAPI(s *Service) *API { r := NewCommandRegistry() - c := commands.NewClientSideHandler() + c := commands.NewClientSideHandler(s.db) // Transactions and signing r.Register("eth_sendTransaction", &commands.SendTransactionCommand{ @@ -121,7 +121,7 @@ func (api *API) CallRPC(ctx context.Context, inputJSON string) (interface{}, err func (api *API) RecallDAppPermission(origin string) error { // TODO: close the websocket connection - return persistence.DeleteDApp(api.s.db, origin) + return api.c.RecallDAppPermissions(commands.RecallDAppPermissionsArgs{URL: origin}) } func (api *API) GetPermittedDAppsList() ([]persistence.DApp, error) { diff --git a/services/connector/commands/client_handler.go b/services/connector/commands/client_handler.go index a2114f3451a..a1334d5dfc7 100644 --- a/services/connector/commands/client_handler.go +++ b/services/connector/commands/client_handler.go @@ -2,6 +2,7 @@ package commands import ( "crypto/sha256" + "database/sql" "encoding/hex" "encoding/json" "fmt" @@ -9,6 +10,7 @@ import ( "time" "github.com/status-im/status-go/eth-node/types" + persistence "github.com/status-im/status-go/services/connector/database" "github.com/status-im/status-go/signal" "github.com/status-im/status-go/transactions" ) @@ -23,6 +25,8 @@ var ( ErrPersonalSignRejectedByUser = fmt.Errorf("personal sign was rejected by user") ErrEmptyRequestID = fmt.Errorf("empty requestID") ErrAnotherConnectorOperationIsAwaitingFor = fmt.Errorf("another connector operation is awaiting for user input") + ErrEmptyUrl = fmt.Errorf("empty URL") + ErrDAppDoesNotHavePermissions = fmt.Errorf("dApp does not have permissions") ) type MessageType int @@ -40,12 +44,14 @@ type Message struct { } type ClientSideHandler struct { + Db *sql.DB responseChannel chan Message isRequestRunning int32 } -func NewClientSideHandler() *ClientSideHandler { +func NewClientSideHandler(db *sql.DB) *ClientSideHandler { return &ClientSideHandler{ + Db: db, responseChannel: make(chan Message, 1), // Buffer of 1 to avoid blocking isRequestRunning: 0, } @@ -115,6 +121,33 @@ func (c *ClientSideHandler) RequestAccountsRejected(args RejectedArgs) error { return nil } +func (c *ClientSideHandler) RecallDAppPermissions(args RecallDAppPermissionsArgs) error { + if args.URL == "" { + return ErrEmptyUrl + } + + dApp, err := persistence.SelectDAppByUrl(c.Db, args.URL) + if err != nil { + return err + } + + if dApp == nil { + return ErrDAppDoesNotHavePermissions + } + + err = persistence.DeleteDApp(c.Db, dApp.URL) + if err != nil { + return err + } + + signal.SendConnectorDAppPermissionRevoked(signal.ConnectorDApp{ + URL: dApp.URL, + Name: dApp.Name, + IconURL: dApp.IconURL, + }) + return nil +} + func (c *ClientSideHandler) RequestSendTransaction(dApp signal.ConnectorDApp, chainID uint64, txArgs *transactions.SendTxArgs) (types.Hash, error) { if !c.setRequestRunning() { return types.Hash{}, ErrAnotherConnectorOperationIsAwaitingFor diff --git a/services/connector/commands/client_handler_test.go b/services/connector/commands/client_handler_test.go index c28a344b027..d595dcec392 100644 --- a/services/connector/commands/client_handler_test.go +++ b/services/connector/commands/client_handler_test.go @@ -4,11 +4,17 @@ import ( "testing" "time" + "github.com/status-im/status-go/eth-node/types" + persistence "github.com/status-im/status-go/services/connector/database" + "github.com/stretchr/testify/assert" ) func TestClientHandlerTimeout(t *testing.T) { - clientHandler := NewClientSideHandler() + db, cleanup := createWalletDB(t) + t.Cleanup(cleanup) + + clientHandler := NewClientSideHandler(db) backupWalletResponseMaxInterval := WalletResponseMaxInterval WalletResponseMaxInterval = 1 * time.Millisecond @@ -19,10 +25,42 @@ func TestClientHandlerTimeout(t *testing.T) { } func TestRequestRejectedWhileWaiting(t *testing.T) { - clientHandler := NewClientSideHandler() + db, cleanup := createWalletDB(t) + t.Cleanup(cleanup) + + clientHandler := NewClientSideHandler(db) clientHandler.setRequestRunning() _, _, err := clientHandler.RequestShareAccountForDApp(testDAppData) assert.Equal(t, ErrAnotherConnectorOperationIsAwaitingFor, err) } + +func TestRecallDAppPermission(t *testing.T) { + db, cleanup := createWalletDB(t) + t.Cleanup(cleanup) + + dapp := persistence.DApp{ + Name: "Test DApp", + URL: "http://testDAppURL", + IconURL: "http://testDAppIconUrl", + SharedAccount: types.HexToAddress("0x1234567890"), + ChainID: 0x1, + } + + err := persistence.UpsertDApp(db, &dapp) + assert.NoError(t, err) + + persistedDapp, err := persistence.SelectDAppByUrl(db, dapp.URL) + assert.Equal(t, persistedDapp, &dapp) + assert.NoError(t, err) + + clientHandler := NewClientSideHandler(db) + err = clientHandler.RecallDAppPermissions(RecallDAppPermissionsArgs{URL: dapp.URL}) + assert.NoError(t, err) + + recalledDapp, err := persistence.SelectDAppByUrl(db, dapp.URL) + + assert.Equal(t, recalledDapp, (*persistence.DApp)(nil)) + assert.NoError(t, err) +} diff --git a/services/connector/commands/request_accounts.go b/services/connector/commands/request_accounts.go index 0521a3ede41..1c836230bf6 100644 --- a/services/connector/commands/request_accounts.go +++ b/services/connector/commands/request_accounts.go @@ -62,7 +62,7 @@ func (c *RequestAccountsCommand) Execute(ctx context.Context, request RPCRequest if err != nil { return "", err } - signal.SendConnectorDAppPermissionGranted(connectorDApp) + signal.SendConnectorDAppPermissionGranted(connectorDApp, account, []uint64{chainID}) } return FormatAccountAddressToResponse(dApp.SharedAccount), nil diff --git a/services/connector/commands/rpc_traits.go b/services/connector/commands/rpc_traits.go index 8c229f0893c..0a680021fb6 100644 --- a/services/connector/commands/rpc_traits.go +++ b/services/connector/commands/rpc_traits.go @@ -65,10 +65,15 @@ type RejectedArgs struct { RequestID string `json:"requestId"` } +type RecallDAppPermissionsArgs struct { + URL string `json:"url"` +} + type ClientSideHandlerInterface interface { RequestShareAccountForDApp(dApp signal.ConnectorDApp) (types.Address, uint64, error) RequestAccountsAccepted(args RequestAccountsAcceptedArgs) error RequestAccountsRejected(args RejectedArgs) error + RecallDAppPermissions(args RecallDAppPermissionsArgs) error RequestSendTransaction(dApp signal.ConnectorDApp, chainID uint64, txArgs *transactions.SendTxArgs) (types.Hash, error) SendTransactionAccepted(args SendTransactionAcceptedArgs) error diff --git a/services/connector/commands/test_helpers.go b/services/connector/commands/test_helpers.go index 463d284dbf3..eb58976839a 100644 --- a/services/connector/commands/test_helpers.go +++ b/services/connector/commands/test_helpers.go @@ -81,7 +81,7 @@ func setupCommand(t *testing.T, method string) (state testState, close func()) { }) require.NoError(t, err) - state.handler = NewClientSideHandler() + state.handler = NewClientSideHandler(state.db) state.mockCtrl = gomock.NewController(t) state.rpcClient = mock_rpcclient.NewMockClientInterface(state.mockCtrl) diff --git a/signal/events_connector.go b/signal/events_connector.go index de2f1624786..37707058ea4 100644 --- a/signal/events_connector.go +++ b/signal/events_connector.go @@ -1,5 +1,9 @@ package signal +import ( + "github.com/status-im/status-go/eth-node/types" +) + const ( EventConnectorSendRequestAccounts = "connector.sendRequestAccounts" EventConnectorSendTransaction = "connector.sendTransaction" @@ -29,6 +33,12 @@ type ConnectorSendTransactionSignal struct { TxArgs string `json:"txArgs"` } +type ConnectorSendDappPermissionGrantedSignal struct { + ConnectorDApp + Chains []uint64 `json:"chains"` + SharedAccount types.Address `json:"sharedAccount"` +} + type ConnectorPersonalSignSignal struct { ConnectorDApp RequestID string `json:"requestId"` @@ -66,8 +76,12 @@ func SendConnectorPersonalSign(dApp ConnectorDApp, requestID, challenge, address }) } -func SendConnectorDAppPermissionGranted(dApp ConnectorDApp) { - send(EventConnectorDAppPermissionGranted, dApp) +func SendConnectorDAppPermissionGranted(dApp ConnectorDApp, account types.Address, chains []uint64) { + send(EventConnectorDAppPermissionGranted, ConnectorSendDappPermissionGrantedSignal{ + ConnectorDApp: dApp, + Chains: chains, + SharedAccount: account, + }) } func SendConnectorDAppPermissionRevoked(dApp ConnectorDApp) {