feat: dapp browser privileges
- Create privilege dialogs dynamically for each privilege requested - Check if a privilege has been granted before to determine if dialog must be shown or not - If dapp is allowed to use privilege, save it in the settings
This commit is contained in:
parent
b6066ef1dd
commit
f85216e7f7
|
@ -25,11 +25,6 @@ type
|
||||||
APIResponse = "api-response",
|
APIResponse = "api-response",
|
||||||
Web3ResponseError = "web3-response-error"
|
Web3ResponseError = "web3-response-error"
|
||||||
|
|
||||||
Permissions {.pure.} = enum
|
|
||||||
Web3 = "web3",
|
|
||||||
ContactCode = "contact-code"
|
|
||||||
Unknown = "unknown"
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Payload = ref object
|
Payload = ref object
|
||||||
id: JsonNode
|
id: JsonNode
|
||||||
|
@ -43,7 +38,7 @@ type
|
||||||
APIRequest = ref object
|
APIRequest = ref object
|
||||||
isAllowed: bool
|
isAllowed: bool
|
||||||
messageId: JsonNode
|
messageId: JsonNode
|
||||||
permission: Permissions
|
permission: Permission
|
||||||
hostname: string
|
hostname: string
|
||||||
|
|
||||||
proc requestType(message: string): RequestTypes =
|
proc requestType(message: string): RequestTypes =
|
||||||
|
@ -67,17 +62,11 @@ proc toWeb3SendAsyncReadOnly(message: string): Web3SendAsyncReadOnly =
|
||||||
|
|
||||||
proc toAPIRequest(message: string): APIRequest =
|
proc toAPIRequest(message: string): APIRequest =
|
||||||
let data = message.parseJson
|
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(
|
result = APIRequest(
|
||||||
messageId: data["messageId"],
|
messageId: data["messageId"],
|
||||||
isAllowed: data{"isAllowed"}.getBool(),
|
isAllowed: data{"isAllowed"}.getBool(),
|
||||||
permission: permission,
|
permission: data["permission"].getStr().toPermission(),
|
||||||
hostname: data{"hostname"}.getStr()
|
hostname: data{"hostname"}.getStr()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -137,17 +126,17 @@ QtObject:
|
||||||
"result": rpcResult.parseJson
|
"result": rpcResult.parseJson
|
||||||
}
|
}
|
||||||
|
|
||||||
proc process*(data: APIRequest): string =
|
proc process*(data: APIRequest, status: Status): string =
|
||||||
var value:JsonNode = case data.permission
|
var value:JsonNode = case data.permission
|
||||||
of Permissions.Web3: %* [status_settings.getSetting[string](Setting.DappsAddress, "0x0000000000000000000000000000000000000000")]
|
of Permission.Web3: %* [status_settings.getSetting[string](Setting.DappsAddress, "0x0000000000000000000000000000000000000000")]
|
||||||
of Permissions.ContactCode: %* status_settings.getSetting[string](Setting.PublicKey, "0x0")
|
of Permission.ContactCode: %* status_settings.getSetting[string](Setting.PublicKey, "0x0")
|
||||||
of Permissions.Unknown: newJNull()
|
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
|
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 $ %* {
|
return $ %* {
|
||||||
"type": ResponseTypes.APIResponse,
|
"type": ResponseTypes.APIResponse,
|
||||||
|
@ -157,11 +146,14 @@ QtObject:
|
||||||
"data": value
|
"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.} =
|
proc postMessage*(self: Web3ProviderView, message: string): string {.slot.} =
|
||||||
case message.requestType():
|
case message.requestType():
|
||||||
of RequestTypes.Web3SendAsyncReadOnly: message.toWeb3SendAsyncReadOnly().process()
|
of RequestTypes.Web3SendAsyncReadOnly: message.toWeb3SendAsyncReadOnly().process()
|
||||||
of RequestTypes.HistoryStateChanged: """{"type":"TODO-IMPLEMENT-THIS"}""" ############# TODO:
|
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:
|
else: """{"type":"TODO-IMPLEMENT-THIS"}""" ##################### TODO:
|
||||||
|
|
||||||
proc getNetworkId*(self: Web3ProviderView): int {.slot.} = getCurrentNetworkDetails().config.networkId
|
proc getNetworkId*(self: Web3ProviderView): int {.slot.} = getCurrentNetworkDetails().config.networkId
|
||||||
|
|
|
@ -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
|
|
@ -2,10 +2,10 @@ import libstatus/accounts as libstatus_accounts
|
||||||
import libstatus/core as libstatus_core
|
import libstatus/core as libstatus_core
|
||||||
import libstatus/settings as libstatus_settings
|
import libstatus/settings as libstatus_settings
|
||||||
import libstatus/types as libstatus_types
|
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
|
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
|
type Status* = ref object
|
||||||
events*: EventEmitter
|
events*: EventEmitter
|
||||||
|
@ -19,6 +19,7 @@ type Status* = ref object
|
||||||
contacts*: ContactModel
|
contacts*: ContactModel
|
||||||
network*: NetworkModel
|
network*: NetworkModel
|
||||||
stickers*: StickersModel
|
stickers*: StickersModel
|
||||||
|
permissions*: PermissionsModel
|
||||||
|
|
||||||
proc newStatusInstance*(): Status =
|
proc newStatusInstance*(): Status =
|
||||||
result = Status()
|
result = Status()
|
||||||
|
@ -34,6 +35,7 @@ proc newStatusInstance*(): Status =
|
||||||
result.contacts = contacts.newContactModel(result.events)
|
result.contacts = contacts.newContactModel(result.events)
|
||||||
result.network = network.newNetworkModel(result.events)
|
result.network = network.newNetworkModel(result.events)
|
||||||
result.stickers = stickers.newStickersModel(result.events)
|
result.stickers = stickers.newStickersModel(result.events)
|
||||||
|
result.permissions = permissions.newPermissionsModel(result.events)
|
||||||
|
|
||||||
proc initNode*(self: Status) =
|
proc initNode*(self: Status) =
|
||||||
libstatus_accounts.initNode()
|
libstatus_accounts.initNode()
|
||||||
|
|
|
@ -25,16 +25,20 @@ Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
property var request: {"hostname": "", "permission": ""}
|
|
||||||
|
|
||||||
|
property Component accessDialogComponent: ModalPopup {
|
||||||
|
id: accessDialog
|
||||||
|
|
||||||
|
property var request: {"hostname": "", "permission": ""}
|
||||||
|
|
||||||
function postMessage(isAllowed){
|
function postMessage(isAllowed){
|
||||||
request.isAllowed = isAllowed;
|
request.isAllowed = isAllowed;
|
||||||
provider.web3Response(web3Provider.postMessage(JSON.stringify(request)));
|
provider.web3Response(web3Provider.postMessage(JSON.stringify(request)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ModalPopup {
|
onClosed: {
|
||||||
id: accessDialog
|
accessDialog.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: design required
|
// TODO: design required
|
||||||
|
|
||||||
|
@ -66,7 +70,7 @@ Item {
|
||||||
switch(request.permission){
|
switch(request.permission){
|
||||||
case Constants.permission_web3: return qsTr("Allowing authorizes this DApp to retrieve your wallet address and enable Web3");
|
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");
|
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);
|
signal web3Response(string data);
|
||||||
|
|
||||||
function postMessage(data){
|
function postMessage(data){
|
||||||
request = JSON.parse(data)
|
var request = JSON.parse(data)
|
||||||
if(request.type === Constants.api_request){
|
if(request.type === Constants.api_request){
|
||||||
// TODO: check if permission has been granted before,
|
if(!web3Provider.hasPermission(request.hostname, request.permission)){
|
||||||
// to not show the dialog
|
var dialog = accessDialogComponent.createObject(browserWindow);
|
||||||
accessDialog.open()
|
dialog.request = request;
|
||||||
|
dialog.open();
|
||||||
|
} else {
|
||||||
|
request.isAllowed = true;
|
||||||
|
web3Response(web3Provider.postMessage(JSON.stringify(request)));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
web3Response(web3Provider.postMessage(data));
|
web3Response(web3Provider.postMessage(data));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue