Permissions api (#1524)
* Add permissions api * Integrate permissions service * Reduce cyclomatic complexity of the MakeNode function
This commit is contained in:
parent
9a0502fa8f
commit
9723b64827
|
@ -571,7 +571,11 @@ func (b *StatusBackend) SelectAccount(walletAddress, chatAddress, password strin
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return b.startBrowsers(password)
|
err = b.startBrowsers(password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return b.startPermissions(password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *StatusBackend) startWallet(password string) error {
|
func (b *StatusBackend) startWallet(password string) error {
|
||||||
|
@ -609,6 +613,22 @@ func (b *StatusBackend) startBrowsers(password string) error {
|
||||||
return svc.StartDatabase(path, password)
|
return svc.StartDatabase(path, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *StatusBackend) startPermissions(password string) error {
|
||||||
|
if !b.statusNode.Config().PermissionsConfig.Enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
svc, err := b.statusNode.PermissionsService()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
account, err := b.accountManager.SelectedWalletAccount()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
path := path.Join(b.statusNode.Config().DataDir, fmt.Sprintf("permissions-%x.sql", account.Address))
|
||||||
|
return svc.StartDatabase(path, password)
|
||||||
|
}
|
||||||
|
|
||||||
// SendDataNotification sends data push notifications to users.
|
// SendDataNotification sends data push notifications to users.
|
||||||
// dataPayloadJSON is a JSON string that looks like this:
|
// dataPayloadJSON is a JSON string that looks like this:
|
||||||
// {
|
// {
|
||||||
|
|
44
node/node.go
44
node/node.go
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/status-im/status-go/services/browsers"
|
"github.com/status-im/status-go/services/browsers"
|
||||||
"github.com/status-im/status-go/services/incentivisation"
|
"github.com/status-im/status-go/services/incentivisation"
|
||||||
"github.com/status-im/status-go/services/peer"
|
"github.com/status-im/status-go/services/peer"
|
||||||
|
"github.com/status-im/status-go/services/permissions"
|
||||||
"github.com/status-im/status-go/services/personal"
|
"github.com/status-im/status-go/services/personal"
|
||||||
"github.com/status-im/status-go/services/shhext"
|
"github.com/status-im/status-go/services/shhext"
|
||||||
"github.com/status-im/status-go/services/status"
|
"github.com/status-im/status-go/services/status"
|
||||||
|
@ -49,6 +50,7 @@ var (
|
||||||
ErrIncentivisationServiceRegistrationFailure = errors.New("failed to register the Incentivisation service")
|
ErrIncentivisationServiceRegistrationFailure = errors.New("failed to register the Incentivisation service")
|
||||||
ErrWalletServiceRegistrationFailure = errors.New("failed to register the Wallet service")
|
ErrWalletServiceRegistrationFailure = errors.New("failed to register the Wallet service")
|
||||||
ErrBrowsersServiceRegistrationFailure = errors.New("failed to register the Browsers service")
|
ErrBrowsersServiceRegistrationFailure = errors.New("failed to register the Browsers service")
|
||||||
|
ErrPermissionsServiceRegistrationFailure = errors.New("failed to register the Permissions service")
|
||||||
)
|
)
|
||||||
|
|
||||||
// All general log messages in this package should be routed through this logger.
|
// All general log messages in this package should be routed through this logger.
|
||||||
|
@ -80,14 +82,22 @@ func MakeNode(config *params.NodeConfig, db *leveldb.DB) (*node.Node, error) {
|
||||||
return nil, fmt.Errorf(ErrNodeMakeFailureFormat, err.Error())
|
return nil, fmt.Errorf(ErrNodeMakeFailureFormat, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = activateServices(stack, config, db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return stack, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func activateServices(stack *node.Node, config *params.NodeConfig, db *leveldb.DB) error {
|
||||||
// start Ethereum service if we are not expected to use an upstream server
|
// start Ethereum service if we are not expected to use an upstream server
|
||||||
if !config.UpstreamConfig.Enabled {
|
if !config.UpstreamConfig.Enabled {
|
||||||
if err := activateLightEthService(stack, config); err != nil {
|
if err := activateLightEthService(stack, config); err != nil {
|
||||||
return nil, fmt.Errorf("%v: %v", ErrLightEthRegistrationFailure, err)
|
return fmt.Errorf("%v: %v", ErrLightEthRegistrationFailure, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if config.LightEthConfig.Enabled {
|
if config.LightEthConfig.Enabled {
|
||||||
return nil, fmt.Errorf("%v: %v", ErrLightEthRegistrationFailureUpstreamEnabled, err)
|
return ErrLightEthRegistrationFailureUpstreamEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("LES protocol is disabled")
|
logger.Info("LES protocol is disabled")
|
||||||
|
@ -98,39 +108,43 @@ func MakeNode(config *params.NodeConfig, db *leveldb.DB) (*node.Node, error) {
|
||||||
// upstream, we don't start any of these, so we need to start our own
|
// upstream, we don't start any of these, so we need to start our own
|
||||||
// implementation.
|
// implementation.
|
||||||
if err := activatePersonalService(stack, config); err != nil {
|
if err := activatePersonalService(stack, config); err != nil {
|
||||||
return nil, fmt.Errorf("%v: %v", ErrPersonalServiceRegistrationFailure, err)
|
return fmt.Errorf("%v: %v", ErrPersonalServiceRegistrationFailure, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// start Whisper service.
|
// start Whisper service.
|
||||||
if err := activateShhService(stack, config, db); err != nil {
|
if err := activateShhService(stack, config, db); err != nil {
|
||||||
return nil, fmt.Errorf("%v: %v", ErrWhisperServiceRegistrationFailure, err)
|
return fmt.Errorf("%v: %v", ErrWhisperServiceRegistrationFailure, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// start incentivisation service
|
// start incentivisation service
|
||||||
if err := activateIncentivisationService(stack, config); err != nil {
|
if err := activateIncentivisationService(stack, config); err != nil {
|
||||||
return nil, fmt.Errorf("%v: %v", ErrIncentivisationServiceRegistrationFailure, err)
|
return fmt.Errorf("%v: %v", ErrIncentivisationServiceRegistrationFailure, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// start status service.
|
// start status service.
|
||||||
if err := activateStatusService(stack, config); err != nil {
|
if err := activateStatusService(stack, config); err != nil {
|
||||||
return nil, fmt.Errorf("%v: %v", ErrStatusServiceRegistrationFailure, err)
|
return fmt.Errorf("%v: %v", ErrStatusServiceRegistrationFailure, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// start peer service
|
// start peer service
|
||||||
if err := activatePeerService(stack); err != nil {
|
if err := activatePeerService(stack); err != nil {
|
||||||
return nil, fmt.Errorf("%v: %v", ErrPeerServiceRegistrationFailure, err)
|
return fmt.Errorf("%v: %v", ErrPeerServiceRegistrationFailure, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := activateWalletService(stack, config.WalletConfig); err != nil {
|
if err := activateWalletService(stack, config.WalletConfig); err != nil {
|
||||||
return nil, fmt.Errorf("%v: %v", ErrWalletServiceRegistrationFailure, err)
|
return fmt.Errorf("%v: %v", ErrWalletServiceRegistrationFailure, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := activateBrowsersService(stack, config.BrowsersConfig); err != nil {
|
if err := activateBrowsersService(stack, config.BrowsersConfig); err != nil {
|
||||||
return nil, fmt.Errorf("%v: %v", ErrBrowsersServiceRegistrationFailure, err)
|
return fmt.Errorf("%v: %v", ErrBrowsersServiceRegistrationFailure, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return stack, nil
|
if err := activatePermissionsService(stack, config.PermissionsConfig); err != nil {
|
||||||
|
return fmt.Errorf("%v: %v", ErrPermissionsServiceRegistrationFailure, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newGethNodeConfig returns default stack configuration for mobile client node
|
// newGethNodeConfig returns default stack configuration for mobile client node
|
||||||
|
@ -301,6 +315,16 @@ func activateBrowsersService(stack *node.Node, config params.BrowsersConfig) err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func activatePermissionsService(stack *node.Node, config params.PermissionsConfig) error {
|
||||||
|
if !config.Enabled {
|
||||||
|
logger.Info("dapps permissions service is disabled")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return stack.Register(func(*node.ServiceContext) (node.Service, error) {
|
||||||
|
return permissions.NewService(), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func registerMailServer(whisperService *whisper.Whisper, config *params.WhisperConfig) (err error) {
|
func registerMailServer(whisperService *whisper.Whisper, config *params.WhisperConfig) (err error) {
|
||||||
var mailServer mailserver.WMailServer
|
var mailServer mailserver.WMailServer
|
||||||
whisperService.RegisterServer(&mailServer)
|
whisperService.RegisterServer(&mailServer)
|
||||||
|
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"github.com/status-im/status-go/rpc"
|
"github.com/status-im/status-go/rpc"
|
||||||
"github.com/status-im/status-go/services/browsers"
|
"github.com/status-im/status-go/services/browsers"
|
||||||
"github.com/status-im/status-go/services/peer"
|
"github.com/status-im/status-go/services/peer"
|
||||||
|
"github.com/status-im/status-go/services/permissions"
|
||||||
"github.com/status-im/status-go/services/shhext"
|
"github.com/status-im/status-go/services/shhext"
|
||||||
"github.com/status-im/status-go/services/status"
|
"github.com/status-im/status-go/services/status"
|
||||||
"github.com/status-im/status-go/services/wallet"
|
"github.com/status-im/status-go/services/wallet"
|
||||||
|
@ -614,6 +615,17 @@ func (n *StatusNode) BrowsersService() (s *browsers.Service, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PermissionsService returns browsers.Service instance if it was started.
|
||||||
|
func (n *StatusNode) PermissionsService() (s *permissions.Service, err error) {
|
||||||
|
n.mu.RLock()
|
||||||
|
defer n.mu.RUnlock()
|
||||||
|
err = n.gethService(&s)
|
||||||
|
if err == node.ErrServiceUnknown {
|
||||||
|
err = ErrServiceUnknown
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// AccountManager exposes reference to node's accounts manager
|
// AccountManager exposes reference to node's accounts manager
|
||||||
func (n *StatusNode) AccountManager() (*accounts.Manager, error) {
|
func (n *StatusNode) AccountManager() (*accounts.Manager, error) {
|
||||||
n.mu.RLock()
|
n.mu.RLock()
|
||||||
|
|
|
@ -349,6 +349,9 @@ type NodeConfig struct {
|
||||||
// BrowsersConfig extra configuration for browsers.Service.
|
// BrowsersConfig extra configuration for browsers.Service.
|
||||||
BrowsersConfig BrowsersConfig
|
BrowsersConfig BrowsersConfig
|
||||||
|
|
||||||
|
// PermissionsConfig extra configuration for permissions.Service.
|
||||||
|
PermissionsConfig PermissionsConfig
|
||||||
|
|
||||||
// SwarmConfig extra configuration for Swarm and ENS
|
// SwarmConfig extra configuration for Swarm and ENS
|
||||||
SwarmConfig SwarmConfig `json:"SwarmConfig," validate:"structonly"`
|
SwarmConfig SwarmConfig `json:"SwarmConfig," validate:"structonly"`
|
||||||
|
|
||||||
|
@ -374,6 +377,11 @@ type BrowsersConfig struct {
|
||||||
Enabled bool
|
Enabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PermissionsConfig extra configuration for permissions.Service.
|
||||||
|
type PermissionsConfig struct {
|
||||||
|
Enabled bool
|
||||||
|
}
|
||||||
|
|
||||||
// ShhextConfig defines options used by shhext service.
|
// ShhextConfig defines options used by shhext service.
|
||||||
type ShhextConfig struct {
|
type ShhextConfig struct {
|
||||||
PFSEnabled bool
|
PFSEnabled bool
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
Dapps permissions service
|
||||||
|
=========================
|
||||||
|
|
||||||
|
To enable:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"PermissionsConfig": {
|
||||||
|
"Enabled": true,
|
||||||
|
},
|
||||||
|
APIModules: "permissions"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
API
|
||||||
|
---
|
||||||
|
|
||||||
|
#### permissions_addDappPermissions
|
||||||
|
|
||||||
|
Stores provided permissions for dapp. On update replaces previous version of the object.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dapp": "first",
|
||||||
|
"permissions": [
|
||||||
|
"r",
|
||||||
|
"x"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### permissions_getDappPermissions
|
||||||
|
|
||||||
|
Returns all permissions for dapps. Order is not deterministic.
|
||||||
|
|
||||||
|
#### permissions_deleteDappPermissions
|
||||||
|
|
||||||
|
Delete dapp by a name.
|
|
@ -0,0 +1,41 @@
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrServiceNotInitialized returned when permissions is not initialized/started,.
|
||||||
|
ErrServiceNotInitialized = errors.New("permissions service is not initialized")
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewAPI(s *Service) *API {
|
||||||
|
return &API{s}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API is class with methods available over RPC.
|
||||||
|
type API struct {
|
||||||
|
s *Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) AddDappPermissions(ctx context.Context, perms DappPermissions) error {
|
||||||
|
if api.s.db == nil {
|
||||||
|
return ErrServiceNotInitialized
|
||||||
|
}
|
||||||
|
return api.s.db.AddPermissions(perms)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) GetDappPermissions(ctx context.Context) ([]DappPermissions, error) {
|
||||||
|
if api.s.db == nil {
|
||||||
|
return nil, ErrServiceNotInitialized
|
||||||
|
}
|
||||||
|
return api.s.db.GetPermissions()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) DeleteDappPermissions(ctx context.Context, name string) error {
|
||||||
|
if api.s.db == nil {
|
||||||
|
return ErrServiceNotInitialized
|
||||||
|
}
|
||||||
|
return api.s.db.DeletePermission(name)
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupTestDB(t *testing.T) (*Database, func()) {
|
||||||
|
tmpfile, err := ioutil.TempFile("", "perm-tests-")
|
||||||
|
require.NoError(t, err)
|
||||||
|
db, err := InitializeDB(tmpfile.Name(), "perm-tests")
|
||||||
|
require.NoError(t, err)
|
||||||
|
return db, func() {
|
||||||
|
require.NoError(t, db.Close())
|
||||||
|
require.NoError(t, os.Remove(tmpfile.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTestAPI(t *testing.T) (*API, func()) {
|
||||||
|
db, cancel := setupTestDB(t)
|
||||||
|
return &API{s: &Service{db: db}}, cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDappPermissionsStored(t *testing.T) {
|
||||||
|
api, cancel := setupTestAPI(t)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
expected := []DappPermissions{
|
||||||
|
{
|
||||||
|
Name: "first",
|
||||||
|
Permissions: []string{"r", "w"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "second",
|
||||||
|
Permissions: []string{"r", "x"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "third",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, perms := range expected {
|
||||||
|
require.NoError(t, api.AddDappPermissions(context.TODO(), perms))
|
||||||
|
}
|
||||||
|
rst, err := api.GetDappPermissions(context.TODO())
|
||||||
|
require.NoError(t, err)
|
||||||
|
// sort in lexicographic order by name
|
||||||
|
sort.Slice(rst, func(i, j int) bool {
|
||||||
|
return rst[i].Name < rst[j].Name
|
||||||
|
})
|
||||||
|
require.Equal(t, expected, rst)
|
||||||
|
|
||||||
|
data, err := json.Marshal(rst)
|
||||||
|
require.NoError(t, err)
|
||||||
|
fmt.Println(string(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDappPermissionsReplacedOnUpdated(t *testing.T) {
|
||||||
|
api, cancel := setupTestAPI(t)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
perms := DappPermissions{
|
||||||
|
Name: "first",
|
||||||
|
Permissions: []string{"r", "w"},
|
||||||
|
}
|
||||||
|
require.NoError(t, api.AddDappPermissions(context.TODO(), perms))
|
||||||
|
perms.Permissions = append(perms.Permissions, "x")
|
||||||
|
require.NoError(t, api.AddDappPermissions(context.TODO(), perms))
|
||||||
|
rst, err := api.GetDappPermissions(context.TODO())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, rst, 1)
|
||||||
|
require.Equal(t, perms, rst[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDappPermissionsDeleted(t *testing.T) {
|
||||||
|
api, cancel := setupTestAPI(t)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
perms := DappPermissions{
|
||||||
|
Name: "first",
|
||||||
|
}
|
||||||
|
require.NoError(t, api.AddDappPermissions(context.TODO(), perms))
|
||||||
|
rst, err := api.GetDappPermissions(context.TODO())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, rst, 1)
|
||||||
|
require.NoError(t, api.DeleteDappPermissions(context.TODO(), perms.Name))
|
||||||
|
rst, err = api.GetDappPermissions(context.TODO())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, rst, 0)
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/services/permissions/migrations"
|
||||||
|
"github.com/status-im/status-go/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Database sql wrapper for operations with browser objects.
|
||||||
|
type Database struct {
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes database.
|
||||||
|
func (db Database) Close() error {
|
||||||
|
return db.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitializeDB creates db file at a given path and applies migrations.
|
||||||
|
func InitializeDB(path, password string) (*Database, error) {
|
||||||
|
db, err := sqlite.OpenDB(path, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = migrations.Migrate(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Database{db: db}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DappPermissions struct {
|
||||||
|
Name string `json:"dapp"`
|
||||||
|
Permissions []string `json:"permissions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) AddPermissions(perms DappPermissions) (err error) {
|
||||||
|
var (
|
||||||
|
tx *sql.Tx
|
||||||
|
insert *sql.Stmt
|
||||||
|
)
|
||||||
|
tx, err = db.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err == nil {
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = tx.Rollback()
|
||||||
|
}()
|
||||||
|
insert, err = tx.Prepare("INSERT OR REPLACE INTO dapps(name) VALUES(?)")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = insert.Exec(perms.Name)
|
||||||
|
insert.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(perms.Permissions) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
insert, err = tx.Prepare("INSERT INTO permissions(dapp_name, permission) VALUES(?, ?)")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer insert.Close()
|
||||||
|
for _, perm := range perms.Permissions {
|
||||||
|
_, err = insert.Exec(perms.Name, perm)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetPermissions() (rst []DappPermissions, err error) {
|
||||||
|
var (
|
||||||
|
tx *sql.Tx
|
||||||
|
rows *sql.Rows
|
||||||
|
)
|
||||||
|
tx, err = db.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err == nil {
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = tx.Rollback()
|
||||||
|
}()
|
||||||
|
// FULL and RIGHT joins are not supported
|
||||||
|
rows, err = tx.Query("SELECT name FROM dapps")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dapps := map[string]*DappPermissions{}
|
||||||
|
for rows.Next() {
|
||||||
|
perms := DappPermissions{}
|
||||||
|
err = rows.Scan(&perms.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dapps[perms.Name] = &perms
|
||||||
|
}
|
||||||
|
rows, err = tx.Query("SELECT dapp_name, permission from permissions")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
name string
|
||||||
|
permission string
|
||||||
|
)
|
||||||
|
for rows.Next() {
|
||||||
|
err = rows.Scan(&name, &permission)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dapps[name].Permissions = append(dapps[name].Permissions, permission)
|
||||||
|
}
|
||||||
|
rst = make([]DappPermissions, 0, len(dapps))
|
||||||
|
for key := range dapps {
|
||||||
|
rst = append(rst, *dapps[key])
|
||||||
|
}
|
||||||
|
return rst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) DeletePermission(name string) error {
|
||||||
|
_, err := db.db.Exec("DELETE FROM dapps WHERE name = ?", name)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bindata_read(data []byte, name string) ([]byte, error) {
|
||||||
|
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_, err = io.Copy(&buf, gz)
|
||||||
|
gz.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var __0001_permissions_down_sql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x48\x49\x2c\x28\x28\xb6\xe6\x42\x12\x29\x48\x2d\xca\xcd\x2c\x2e\xce\xcc\xcf\x2b\xb6\xe6\x02\x04\x00\x00\xff\xff\xeb\x21\xe7\xd0\x2a\x00\x00\x00")
|
||||||
|
|
||||||
|
func _0001_permissions_down_sql() ([]byte, error) {
|
||||||
|
return bindata_read(
|
||||||
|
__0001_permissions_down_sql,
|
||||||
|
"0001_permissions.down.sql",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var __0001_permissions_up_sql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\xce\x31\x0f\x82\x30\x10\x05\xe0\xbd\xbf\xe2\x8d\x90\xf8\x0f\x9c\x6a\x79\x68\x63\x6d\x4d\x39\x02\x4c\x86\x44\x06\x06\x90\xc8\xff\x4f\x4c\xa3\x91\xc4\xc1\xf5\xee\xdd\x77\xcf\x44\x6a\x21\x44\x1f\x1c\x61\x4b\xf8\x20\x60\x6b\x2b\xa9\x70\xef\x97\x65\x45\xa6\xe6\x7e\x1a\x20\x6c\x05\xd7\x68\x2f\x3a\x76\x38\xb3\x53\x39\x1a\x2b\xa7\x50\x0b\x62\x68\x6c\xb1\x57\xea\x0f\xb5\x0c\xcf\x69\x5c\xd7\xf1\x31\x27\x30\xc1\xb7\x4d\x4d\x39\x5f\x3b\xb7\x53\x5b\xec\x77\x53\x86\x48\x7b\xf4\xe9\x73\xf6\x3d\xcf\x11\x59\x32\xd2\x1b\x7e\xda\x66\xef\x71\xf0\x28\xe8\x28\x84\xd1\x95\xd1\x05\x55\xfe\x0a\x00\x00\xff\xff\x9e\x9a\xc6\xf0\xe8\x00\x00\x00")
|
||||||
|
|
||||||
|
func _0001_permissions_up_sql() ([]byte, error) {
|
||||||
|
return bindata_read(
|
||||||
|
__0001_permissions_up_sql,
|
||||||
|
"0001_permissions.up.sql",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _doc_go = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00")
|
||||||
|
|
||||||
|
func doc_go() ([]byte, error) {
|
||||||
|
return bindata_read(
|
||||||
|
_doc_go,
|
||||||
|
"doc.go",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asset loads and returns the asset for the given name.
|
||||||
|
// It returns an error if the asset could not be found or
|
||||||
|
// could not be loaded.
|
||||||
|
func Asset(name string) ([]byte, error) {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[cannonicalName]; ok {
|
||||||
|
return f()
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetNames returns the names of the assets.
|
||||||
|
func AssetNames() []string {
|
||||||
|
names := make([]string, 0, len(_bindata))
|
||||||
|
for name := range _bindata {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||||
|
var _bindata = map[string]func() ([]byte, error){
|
||||||
|
"0001_permissions.down.sql": _0001_permissions_down_sql,
|
||||||
|
"0001_permissions.up.sql": _0001_permissions_up_sql,
|
||||||
|
"doc.go": doc_go,
|
||||||
|
}
|
||||||
|
// AssetDir returns the file names below a certain
|
||||||
|
// directory embedded in the file by go-bindata.
|
||||||
|
// For example if you run go-bindata on data/... and data contains the
|
||||||
|
// following hierarchy:
|
||||||
|
// data/
|
||||||
|
// foo.txt
|
||||||
|
// img/
|
||||||
|
// a.png
|
||||||
|
// b.png
|
||||||
|
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||||
|
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||||
|
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||||
|
// AssetDir("") will return []string{"data"}.
|
||||||
|
func AssetDir(name string) ([]string, error) {
|
||||||
|
node := _bintree
|
||||||
|
if len(name) != 0 {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
pathList := strings.Split(cannonicalName, "/")
|
||||||
|
for _, p := range pathList {
|
||||||
|
node = node.Children[p]
|
||||||
|
if node == nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.Func != nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
rv := make([]string, 0, len(node.Children))
|
||||||
|
for name := range node.Children {
|
||||||
|
rv = append(rv, name)
|
||||||
|
}
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type _bintree_t struct {
|
||||||
|
Func func() ([]byte, error)
|
||||||
|
Children map[string]*_bintree_t
|
||||||
|
}
|
||||||
|
var _bintree = &_bintree_t{nil, map[string]*_bintree_t{
|
||||||
|
"0001_permissions.down.sql": &_bintree_t{_0001_permissions_down_sql, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"0001_permissions.up.sql": &_bintree_t{_0001_permissions_up_sql, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"doc.go": &_bintree_t{doc_go, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
}}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
bindata "github.com/status-im/migrate/v4/source/go_bindata"
|
||||||
|
"github.com/status-im/status-go/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Migrate applies migrations.
|
||||||
|
func Migrate(db *sql.DB) error {
|
||||||
|
return sqlite.Migrate(db, bindata.Resource(
|
||||||
|
AssetNames(),
|
||||||
|
func(name string) ([]byte, error) {
|
||||||
|
return Asset(name)
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
DROP TABLE dapps;
|
||||||
|
DROP TABLE permissions;
|
|
@ -0,0 +1,9 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS dapps (
|
||||||
|
name TEXT PRIMARY KEY
|
||||||
|
) WITHOUT ROWID;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS permissions (
|
||||||
|
dapp_name TEXT NOT NULL,
|
||||||
|
permission TEXT NOT NULL,
|
||||||
|
FOREIGN KEY(dapp_name) REFERENCES dapps(name) ON DELETE CASCADE
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
package sql
|
||||||
|
|
||||||
|
//go:generate go-bindata -pkg migrations -o ../bindata.go ./
|
|
@ -0,0 +1,62 @@
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewService initializes service instance.
|
||||||
|
func NewService() *Service {
|
||||||
|
return &Service{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
db *Database
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a service.
|
||||||
|
func (s *Service) Start(*p2p.Server) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartDatabase after dbpath and password will become known.
|
||||||
|
func (s *Service) StartDatabase(dbpath, password string) (err error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
s.db, err = InitializeDB(dbpath, password)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) StopDatabase() error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
if s.db != nil {
|
||||||
|
return s.db.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop a service.
|
||||||
|
func (s *Service) Stop() error {
|
||||||
|
return s.StopDatabase()
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIs returns list of available RPC APIs.
|
||||||
|
func (s *Service) APIs() []rpc.API {
|
||||||
|
return []rpc.API{
|
||||||
|
{
|
||||||
|
Namespace: "permissions",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Service: NewAPI(s),
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocols returns list of p2p protocols.
|
||||||
|
func (s *Service) Protocols() []p2p.Protocol {
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue