diff --git a/src/app/provider/view.nim b/src/app/provider/view.nim index 1836e6090e..8a5d33e248 100644 --- a/src/app/provider/view.nim +++ b/src/app/provider/view.nim @@ -25,11 +25,6 @@ type APIResponse = "api-response", Web3ResponseError = "web3-response-error" - Permissions {.pure.} = enum - Web3 = "web3", - ContactCode = "contact-code" - Unknown = "unknown" - type Payload = ref object id: JsonNode @@ -43,7 +38,7 @@ type APIRequest = ref object isAllowed: bool messageId: JsonNode - permission: Permissions + permission: Permission hostname: string proc requestType(message: string): RequestTypes = @@ -67,17 +62,11 @@ proc toWeb3SendAsyncReadOnly(message: string): Web3SendAsyncReadOnly = proc toAPIRequest(message: string): APIRequest = let data = message.parseJson - var permission = Permissions.Unknown - - try: - permission = parseEnum[Permissions](data["permission"].getStr()) - except: - warn "Unknown permission requested", value=data["permission"].getStr() result = APIRequest( messageId: data["messageId"], isAllowed: data{"isAllowed"}.getBool(), - permission: permission, + permission: data["permission"].getStr().toPermission(), hostname: data{"hostname"}.getStr() ) @@ -137,17 +126,17 @@ QtObject: "result": rpcResult.parseJson } - proc process*(data: APIRequest): string = + proc process*(data: APIRequest, status: Status): string = var value:JsonNode = case data.permission - of Permissions.Web3: %* [status_settings.getSetting[string](Setting.DappsAddress, "0x0000000000000000000000000000000000000000")] - of Permissions.ContactCode: %* status_settings.getSetting[string](Setting.PublicKey, "0x0") - of Permissions.Unknown: newJNull() + of Permission.Web3: %* [status_settings.getSetting[string](Setting.DappsAddress, "0x0000000000000000000000000000000000000000")] + of Permission.ContactCode: %* status_settings.getSetting[string](Setting.PublicKey, "0x0") + of Permission.Unknown: newJNull() - let isAllowed = data.isAllowed and data.permission != Permissions.Unknown + let isAllowed = data.isAllowed and data.permission != Permission.Unknown info "API request received", host=data.hostname, value=data.permission, isAllowed - # TODO: if isAllowed, store permission grant + if isAllowed: status.permissions.addPermission(data.hostname, data.permission) return $ %* { "type": ResponseTypes.APIResponse, @@ -157,11 +146,14 @@ QtObject: "data": value } + proc hasPermission*(self: Web3ProviderView, hostname: string, permission: string): bool {.slot.} = + result = self.status.permissions.hasPermission(hostname, permission.toPermission()) + proc postMessage*(self: Web3ProviderView, message: string): string {.slot.} = case message.requestType(): of RequestTypes.Web3SendAsyncReadOnly: message.toWeb3SendAsyncReadOnly().process() of RequestTypes.HistoryStateChanged: """{"type":"TODO-IMPLEMENT-THIS"}""" ############# TODO: - of RequestTypes.APIRequest: message.toAPIRequest().process() + of RequestTypes.APIRequest: message.toAPIRequest().process(self.status) else: """{"type":"TODO-IMPLEMENT-THIS"}""" ##################### TODO: proc getNetworkId*(self: Web3ProviderView): int {.slot.} = getCurrentNetworkDetails().config.networkId diff --git a/src/status/permissions.nim b/src/status/permissions.nim new file mode 100644 index 0000000000..d436e3da36 --- /dev/null +++ b/src/status/permissions.nim @@ -0,0 +1,62 @@ +import json +import strutils +import sets +import libstatus/core +import libstatus/types +import chronicles +import eventemitter +import sequtils + +type + Permission* {.pure.} = enum + Web3 = "web3", + ContactCode = "contact-code" + Unknown = "unknown" + +logScope: + topics = "permissions-model" + +type + PermissionsModel* = ref object + events*: EventEmitter + +proc toPermission*(value: string): Permission = + result = Permission.Unknown + try: + result = parseEnum[Permission](value) + except: + warn "Unknown permission requested", value + +proc newPermissionsModel*(events: EventEmitter): PermissionsModel = + result = PermissionsModel() + result.events = events + +proc init*(self: PermissionsModel) = + discard + +proc getPermissions*(self: PermissionsModel, dapp: string): HashSet[Permission] = + let response = callPrivateRPC("permissions_getDappPermissions") + result = initHashSet[Permission]() + for dappPermission in response.parseJson["result"].getElems(): + if dappPermission["dapp"].getStr() == dapp: + for permission in dappPermission["permissions"].getElems(): + result.incl(permission.getStr().toPermission()) + +proc hasPermission*(self: PermissionsModel, dapp: string, permission: Permission): bool = + result = self.getPermissions(dapp).contains(permission) + +proc addPermission*(self: PermissionsModel, dapp: string, permission: Permission) = + var permissions = self.getPermissions(dapp) + permissions.incl(permission) + discard callPrivateRPC("permissions_addDappPermissions", %*[{ + "dapp": dapp, + "permissions": permissions.toSeq() + }]) + +proc revokePermission*(self: PermissionsModel, dapp: string, permission: Permission) = + # TODO: implement + discard + +proc clearPermissions*(self: PermissionsModel, dapp: string) = + # TODO: implement + discard \ No newline at end of file diff --git a/src/status/status.nim b/src/status/status.nim index b011f23ea8..bba4f29778 100644 --- a/src/status/status.nim +++ b/src/status/status.nim @@ -2,10 +2,10 @@ import libstatus/accounts as libstatus_accounts import libstatus/core as libstatus_core import libstatus/settings as libstatus_settings import libstatus/types as libstatus_types -import chat, accounts, wallet, node, network, mailservers, messages, contacts, profile, stickers +import chat, accounts, wallet, node, network, mailservers, messages, contacts, profile, stickers, permissions import ../eventemitter -export chat, accounts, node, mailservers, messages, contacts, profile, network +export chat, accounts, node, mailservers, messages, contacts, profile, network, permissions type Status* = ref object events*: EventEmitter @@ -19,6 +19,7 @@ type Status* = ref object contacts*: ContactModel network*: NetworkModel stickers*: StickersModel + permissions*: PermissionsModel proc newStatusInstance*(): Status = result = Status() @@ -34,6 +35,7 @@ proc newStatusInstance*(): Status = result.contacts = contacts.newContactModel(result.events) result.network = network.newNetworkModel(result.events) result.stickers = stickers.newStickersModel(result.events) + result.permissions = permissions.newPermissionsModel(result.events) proc initNode*(self: Status) = libstatus_accounts.initNode() diff --git a/ui/app/AppLayouts/Browser/BrowserLayout.qml b/ui/app/AppLayouts/Browser/BrowserLayout.qml index af55dd7a63..0b1476d424 100644 --- a/ui/app/AppLayouts/Browser/BrowserLayout.qml +++ b/ui/app/AppLayouts/Browser/BrowserLayout.qml @@ -25,17 +25,21 @@ Item { Layout.fillHeight: true Layout.fillWidth: true - property var request: {"hostname": "", "permission": ""} - - function postMessage(isAllowed){ - request.isAllowed = isAllowed; - provider.web3Response(web3Provider.postMessage(JSON.stringify(request))); - } - - ModalPopup { + property Component accessDialogComponent: ModalPopup { id: accessDialog + property var request: {"hostname": "", "permission": ""} + + function postMessage(isAllowed){ + request.isAllowed = isAllowed; + provider.web3Response(web3Provider.postMessage(JSON.stringify(request))); + } + + onClosed: { + accessDialog.destroy(); + } + // TODO: design required StyledText { @@ -66,7 +70,7 @@ Item { switch(request.permission){ case Constants.permission_web3: return qsTr("Allowing authorizes this DApp to retrieve your wallet address and enable Web3"); case Constants.permission_contactCode: return qsTr("Granting access authorizes this DApp to retrieve your chat key"); - default: return qsTr("Unknown permission"); + default: return qsTr("Unknown permission: " + request.permission); } } } @@ -104,11 +108,16 @@ Item { signal web3Response(string data); function postMessage(data){ - request = JSON.parse(data) + var request = JSON.parse(data) if(request.type === Constants.api_request){ - // TODO: check if permission has been granted before, - // to not show the dialog - accessDialog.open() + if(!web3Provider.hasPermission(request.hostname, request.permission)){ + var dialog = accessDialogComponent.createObject(browserWindow); + dialog.request = request; + dialog.open(); + } else { + request.isAllowed = true; + web3Response(web3Provider.postMessage(JSON.stringify(request))); + } } else { web3Response(web3Provider.postMessage(data)); }