introduced new status js api according latest web3 opt-in api

This commit is contained in:
Andrey Shovkoplyas 2018-09-28 15:40:19 +02:00
parent 81b2afebc1
commit a1d0dcc0ec
No known key found for this signature in database
GPG Key ID: EAAB7C8622D860A4
9 changed files with 237 additions and 262 deletions

View File

@ -6,70 +6,77 @@ function bridgeSend(data){
WebViewBridge.send(JSON.stringify(data));
}
window.addEventListener('message', function (event) {
if (!event.data || !event.data.type) { return; }
if (event.data.type === 'STATUS_API_REQUEST') {
bridgeSend({
type: 'status-api-request',
permissions: event.data.permissions,
host: window.location.hostname
});
}
});
function sendAPIrequest(permission) {
var messageId = callbackId++;
bridgeSend({
type: 'api-request',
permission: permission,
messageId: messageId,
host: window.location.hostname
});
return new Promise(function (resolve, reject) { callbacks[messageId] = {resolve: resolve, reject: reject};});
}
WebViewBridge.onMessage = function (message) {
data = JSON.parse(message);
var id = data.messageId;
var callback = callbacks[id];
if (data.type === "status-api-success")
{
window.dispatchEvent(new CustomEvent('statusapi', { detail: { permissions: data.keys,
data: data.data
} }));
}
if (callback) {
else if (data.type === "web3-send-async-callback")
{
var id = data.messageId;
var callback = callbacks[id];
if (callback) {
if (callback.results)
{
callback.results.push(data.error || data.result);
if (callback.results.length == callback.num)
callback.callback(undefined, callback.results);
}
else
{
callback.callback(data.error, data.result);
}
}
}
else if (data.type === "scan-qr-code-callback")
{
var id = data.data.messageId;
var callback = callbacks[id];
if (callback) {
var result = data.result;
var regex = new RegExp(callback.regex);
if (regex.test(result)) {
if (callback.resolve) {
callback.resolve(result);
}
if (data.type === "api-response") {
if (data.isAllowed) {
callback.resolve(data.data);
} else {
if (callback.reject) {
callback.reject(result);
callback.reject(new Error("Denied"));
}
} else if (data.type === "web3-send-async-callback") {
var id = data.messageId;
var callback = callbacks[id];
if (callback) {
if (callback.results) {
callback.results.push(data.error || data.result);
if (callback.results.length == callback.num)
callback.callback(undefined, callback.results);
} else {
callback.callback(data.error, data.result);
}
}
} else if (data.type === "scan-qr-code-callback") {
var id = data.data.messageId;
var callback = callbacks[id];
if (callback) {
var result = data.result;
var regex = new RegExp(callback.regex);
if (regex.test(result)) {
if (callback.resolve) {
callback.resolve(result);
}
} else {
if (callback.reject) {
callback.reject(result);
}
}
}
}
}
};
var StatusAPI = function () {};
StatusAPI.prototype.getContactCode = function () {
return sendAPIrequest('contact-code');
};
var StatusHttpProvider = function () {};
StatusHttpProvider.prototype.isStatus = true;
StatusHttpProvider.prototype.isConnected = function () { return true; };
StatusHttpProvider.prototype.status = new StatusAPI();
function web3Response (payload, result){
return {id: payload.id,
jsonrpc: "2.0",

View File

@ -1,52 +1,52 @@
if(typeof ReadOnlyProvider === "undefined"){
var callbackId = 0;
var callbacks = {};
var ethereumPromise = {};
function bridgeSend(data){
WebViewBridge.send(JSON.stringify(data));
}
window.addEventListener('message', function (event) {
if (!event.data || !event.data.type) { return; }
if (event.data.type === 'STATUS_API_REQUEST') {
bridgeSend({
type: 'status-api-request',
permissions: event.data.permissions,
host: window.location.hostname
});
}
});
function sendAPIrequest(permission) {
var messageId = callbackId++;
bridgeSend({
type: 'api-request',
permission: permission,
messageId: messageId,
host: window.location.hostname
});
return new Promise(function (resolve, reject) { callbacks[messageId] = {resolve: resolve, reject: reject};});
}
WebViewBridge.onMessage = function (message) {
data = JSON.parse(message);
var id = data.messageId;
var callback = callbacks[id];
if (data.type === "status-api-success")
if (callback)
{
if (data.keys == 'WEB3')
if (data.type === "api-response")
{
ethereumPromise.allowed = true;
window.currentAccountAddress = data.data["WEB3"];
ethereumPromise.resolve();
if (data.isAllowed)
{
if (data.permission == 'web3')
{
window.currentAccountAddress = data.data;
callback.resolve();
}
else
{
callback.resolve(data.data);
}
}
else
{
callback.reject(new Error("Denied"));
}
}
else
else if (data.type === "web3-send-async-callback")
{
window.dispatchEvent(new CustomEvent('statusapi', { detail: { permissions: data.keys,
data: data.data
} }));
}
}
else if (data.type === "web3-permission-request-denied")
{
ethereumPromise.reject(new Error("Denied"));
}
else if (data.type === "web3-send-async-callback")
{
var id = data.messageId;
var callback = callbacks[id];
if (callback) {
if (callback.results)
{
callback.results.push(data.error || data.result);
@ -82,11 +82,19 @@ function getSyncResponse (payload) {
}
}
var StatusAPI = function () {};
StatusAPI.prototype.getContactCode = function () {
return sendAPIrequest('contact-code');
};
var ReadOnlyProvider = function () {};
ReadOnlyProvider.prototype.isStatus = true;
ReadOnlyProvider.prototype.isConnected = function () { return true; };
ReadOnlyProvider.prototype.status = new StatusAPI();
ReadOnlyProvider.prototype.send = function (payload) {
if (payload.method == "eth_uninstallFilter"){
this.sendAsync(payload, function (res, err) {})
@ -100,15 +108,7 @@ ReadOnlyProvider.prototype.send = function (payload) {
};
ReadOnlyProvider.prototype.enable = function () {
bridgeSend({
type: 'status-api-request',
permissions: ['WEB3'],
host: window.location.hostname
});
return new Promise(function (resolve, reject) {
ethereumPromise.resolve = resolve;
ethereumPromise.reject = reject;
});
return sendAPIrequest('web3');
};
ReadOnlyProvider.prototype.sendAsync = function (payload, callback) {

View File

@ -268,7 +268,7 @@
{:keys [browser-id]} options
browser (get browsers browser-id)
data (types/json->clj message)
{{:keys [url]} :navState :keys [type host permissions payload messageId]} data
{{:keys [url]} :navState :keys [type host permission payload messageId]} data
{:keys [dapp? name]} browser
dapp-name (if dapp? name host)]
(cond
@ -290,8 +290,8 @@
{:type constants/scan-qr-code-callback
:data data}))
(= type constants/status-api-request)
(browser.permissions/process-permissions cofx dapp-name permissions))))
(= type constants/api-request)
(browser.permissions/process-permission cofx dapp-name permission messageId))))
(fx/defn handle-message-link
[cofx link]

View File

@ -15,10 +15,7 @@
(spec/def :browser/url-editing? (spec/nilable boolean?))
(spec/def :browser/show-tooltip (spec/nilable keyword?))
(spec/def :browser/show-permission (spec/nilable map?))
(spec/def :browser/pending-permissions set?)
(spec/def :browser/allowed-permissions set?)
(spec/def :browser/requested-permissions set?)
(spec/def :browser/pending-permissions (spec/nilable list?))
(spec/def :browser/options
(spec/nilable
@ -30,8 +27,6 @@
:browser/show-tooltip
:browser/show-permission
:browser/pending-permissions
:browser/allowed-permissions
:browser/requested-permissions
:browser/error?])))
(spec/def :browser/browser

View File

@ -1,6 +1,5 @@
(ns status-im.browser.permissions
(:require [clojure.set :as set]
[status-im.constants :as constants]
(:require [status-im.constants :as constants]
[status-im.data-store.dapp-permissions :as dapp-permissions]
[status-im.i18n :as i18n]
[status-im.utils.ethereum.core :as ethereum]
@ -14,112 +13,77 @@
:description (i18n/label :t/allowing-authorizes-this-dapp)
:icon :icons/wallet-active}})
(defn get-pending-permissions [db]
(get-in db [:browser/options :pending-permissions]))
(defn remove-pending-permission [db pending-permission]
(update-in db [:browser/options :pending-permissions] disj pending-permission))
(defn get-allowed-permissions [db]
(get-in db [:browser/options :allowed-permissions]))
(defn get-requested-permissions [db]
(get-in db [:browser/options :requested-permissions]))
(defn add-allowed-permission [db allowed-permission]
(update-in db [:browser/options :allowed-permissions] conj allowed-permission))
(defn get-permissions-data [allowed-permissions cofx]
(defn get-permission-data [cofx allowed-permission]
(let [account (get-in cofx [:db :account/account])]
(select-keys {constants/dapp-permission-contact-code (:public-key account)
constants/dapp-permission-web3 (ethereum/normalized-address
(:address account))}
(vec allowed-permissions))))
(get {constants/dapp-permission-contact-code (:public-key account)
constants/dapp-permission-web3 (ethereum/normalized-address (:address account))}
allowed-permission)))
(fx/defn send-permissions-data-to-bridge
"If there is granted permissions, return the data to the bridge
If no permission were granted and dapp requested web3 permission,
return `web3-permission-request-denied` message type
Otherwise do nothing"
;;TODO(yenda): this was the behavior of the code prior to refactoring
;;if this is not the intended behavior please create an issue for that
[{:keys [db] :as cofx}]
(let [allowed-permissions (get-allowed-permissions db)
requested-permissions (get-requested-permissions db)
new-db (update db :browser/options dissoc
:pending-permissions
:allowed-permissions
:requested-permissions)]
(cond
(not-empty allowed-permissions)
{:db new-db
:browser/send-to-bridge {:message {:type constants/status-api-success
:data (get-permissions-data allowed-permissions cofx)
:keys (vec allowed-permissions)}
:webview (:webview-bridge db)}}
(and (empty? allowed-permissions)
(requested-permissions constants/dapp-permission-web3))
{:db new-db
:browser/send-to-bridge {:message {:type constants/web3-permission-request-denied}
:webview (:webview-bridge db)}}
:else
{:db new-db})))
(fx/defn send-permission-data-to-bridge
"If there is granted permission, return the data to the bridge"
[{:keys [db] :as cofx} permission message-id allowed?]
{:browser/send-to-bridge {:message (cond-> {:type constants/api-response
:isAllowed allowed?
:permission permission
:messageId message-id}
allowed?
(assoc :data (get-permission-data cofx permission)))
:webview (:webview-bridge db)}})
(fx/defn update-dapp-permissions
[{:keys [db]} dapp-name]
(let [allowed-permissions-set (get-allowed-permissions db)
allowed-permissions {:dapp dapp-name
[{:keys [db]} dapp-name permission allowed?]
(let [dapp-permissions-set (set (get-in db [:dapps/permissions dapp-name :permissions]))
allowed-permissions-set (if allowed?
(conj dapp-permissions-set permission)
(disj dapp-permissions-set permission))
allowed-permissions {:dapp dapp-name
:permissions (vec allowed-permissions-set)}]
(when (not-empty allowed-permissions-set)
{:db (assoc-in db [:dapps/permissions dapp-name] allowed-permissions)
:data-store/tx [(dapp-permissions/save-dapp-permissions allowed-permissions)]})))
{:db (assoc-in db [:dapps/permissions dapp-name] allowed-permissions)
:data-store/tx [(dapp-permissions/save-dapp-permissions allowed-permissions)]}))
(fx/defn process-next-permission
"Process next permission by removing it from pending permissions
and prompting user
"Process next permission by removing it from pending permissions and prompting user
if there is no pending permissions left, save all granted permissions
and return the result to the bridge"
[{:keys [db] :as cofx} dapp-name]
(let [pending-permissions (get-pending-permissions db)
next-permission (first pending-permissions)]
(if next-permission
{:db (-> db
(remove-pending-permission next-permission)
(assoc-in [:browser/options :show-permission]
{:requested-permission next-permission
:dapp-name dapp-name}))}
(fx/merge cofx
{:db (assoc-in db [:browser/options :show-permission] nil)}
(update-dapp-permissions dapp-name)
(send-permissions-data-to-bridge)))))
(if (get-in db [:browser/options :show-permission])
{:db db}
(let [pending-permissions (get-in db [:browser/options :pending-permissions])
next-permission (last pending-permissions)]
(when next-permission
{:db (-> db
(update-in [:browser/options :pending-permissions] butlast)
(assoc-in [:browser/options :show-permission]
{:requested-permission (:permission next-permission)
:message-id (:message-id next-permission)
:dapp-name dapp-name}))}))))
(fx/defn allow-permission
"Add permission to set of allowed permission and process next permission"
[{:keys [db] :as cofx} dapp-name permission]
(fx/merge cofx
{:db (add-allowed-permission db permission)}
(process-next-permission dapp-name)))
[{:keys [db] :as cofx}]
(let [{:keys [requested-permission message-id dapp-name]} (get-in db [:browser/options :show-permission])]
(fx/merge (assoc-in cofx [:db :browser/options :show-permission] nil)
(update-dapp-permissions dapp-name requested-permission true)
(send-permission-data-to-bridge requested-permission message-id true))))
(fx/defn process-permissions
"Process the permissions requested by a dapp
If all supported permissions are already granted, return the result immediatly
to the bridge
(fx/defn deny-permission
"Add permission to set of allowed permission and process next permission"
[{:keys [db] :as cofx}]
(let [{:keys [requested-permission message-id dapp-name]} (get-in db [:browser/options :show-permission])]
(fx/merge (assoc-in cofx [:db :browser/options :show-permission] nil)
(send-permission-data-to-bridge requested-permission message-id false))))
(fx/defn process-permission
"Process the permission requested by a dapp
If supported permission is already granted, return the result immediatly to the bridge
Otherwise process the first permission which will prompt user"
[cofx dapp-name requested-permissions]
(let [requested-permissions-set (set requested-permissions)
current-dapp-permissions (get-in cofx [:db :dapps/permissions dapp-name :permissions])
current-dapp-permissions-set (set current-dapp-permissions)
supported-permissions-set (set (keys supported-permissions))
allowed-permissions (set/intersection requested-permissions-set
current-dapp-permissions-set)
pending-permissions (-> requested-permissions-set
(set/intersection supported-permissions-set)
(set/difference current-dapp-permissions-set))
new-cofx (update-in cofx [:db :browser/options] merge
{:pending-permissions pending-permissions
:allowed-permissions allowed-permissions
:requested-permissions requested-permissions-set})]
(if (empty? pending-permissions)
(send-permissions-data-to-bridge new-cofx)
(process-next-permission new-cofx dapp-name))))
[cofx dapp-name permission message-id]
(let [allowed-permissions (set (get-in cofx [:db :dapps/permissions dapp-name :permissions]))
permission-allowed? (boolean (allowed-permissions permission))
permission-supported? ((set (keys supported-permissions)) permission)]
(if (or permission-allowed? (not permission-supported?))
(send-permission-data-to-bridge cofx permission message-id permission-allowed?)
(process-next-permission (update-in cofx [:db :browser/options :pending-permissions]
conj {:permission permission
:message-id message-id})
dapp-name))))

View File

@ -189,11 +189,10 @@
(def regx-emoji #"^((?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC69\uDC6E\uDC70-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD18-\uDD1C\uDD1E\uDD1F\uDD26\uDD30-\uDD39\uDD3D\uDD3E\uDDD1-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])?|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDEEB\uDEEC\uDEF4-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267B\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])\uFE0F|[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF])+$")
(def ^:const dapp-permission-contact-code "CONTACT_CODE")
(def ^:const dapp-permission-web3 "WEB3")
(def ^:const status-api-success "status-api-success")
(def ^:const status-api-request "status-api-request")
(def ^:const web3-permission-request-denied "web3-permission-request-denied")
(def ^:const dapp-permission-contact-code "contact-code")
(def ^:const dapp-permission-web3 "web3")
(def ^:const api-response "api-response")
(def ^:const api-request "api-request")
(def ^:const history-state-changed "history-state-changed")
(def ^:const web3-send-async "web3-send-async")
(def ^:const web3-send-async-read-only "web3-send-async-read-only")

View File

@ -837,11 +837,16 @@
(handlers/register-handler-fx
:browser.permissions.ui/dapp-permission-allowed
(fn [cofx [_ dapp-name permission]]
(browser.permissions/allow-permission cofx dapp-name permission)))
(fn [cofx _]
(browser.permissions/allow-permission cofx)))
(handlers/register-handler-fx
:browser.permissions.ui/dapp-permission-denied
(fn [cofx _]
(browser.permissions/deny-permission cofx)))
(handlers/register-handler-fx
:browser.permissions.ui/permission-animation-finished
(fn [cofx [_ dapp-name]]
(browser.permissions/process-next-permission cofx dapp-name)))

View File

@ -11,14 +11,19 @@
[status-im.ui.screens.browser.styles :as styles])
(:require-macros [status-im.utils.views :as views]))
(views/defview permissions-panel [{:keys [dapp? name dapp] :as browser} {:keys [requested-permission dapp-name]}]
(views/defview permissions-panel [{:keys [dapp? dapp]} {:keys [requested-permission dapp-name]}]
(views/letsubs [bottom-anim-value (anim/create-value -354)
alpha-value (anim/create-value 0)
hide-panel #(anim/start
(anim/parallel
[(anim/spring bottom-anim-value {:toValue -354})
(anim/timing alpha-value {:toValue 0
:duration 500})]))]
hide-panel (fn []
(js/setTimeout #(re-frame/dispatch
[:browser.permissions.ui/permission-animation-finished
dapp-name])
600)
(anim/start
(anim/parallel
[(anim/spring bottom-anim-value {:toValue -354})
(anim/timing alpha-value {:toValue 0
:duration 500})])))]
{:component-did-mount #(anim/start
(anim/parallel
[(anim/spring bottom-anim-value {:toValue -20})
@ -52,16 +57,13 @@
[react/text {:style styles/permissions-panel-description-label}
description]
[react/view {:flex-direction :row :margin-top 14}
[components.common/button {:on-press #(re-frame/dispatch [:browser.permissions.ui/dapp-permission-denied dapp-name])
:label (i18n/label :t/deny)}]
[components.common/button
{:on-press #(re-frame/dispatch [:browser.permissions.ui/dapp-permission-denied])
:label (i18n/label :t/deny)}]
[react/view {:width 16}]
[components.common/button {:on-press #(re-frame/dispatch [:browser.permissions.ui/dapp-permission-allowed dapp-name requested-permission])
:label (i18n/label :t/allow)}]]
;; TODO (andrey) will be in next PR
#_[react/view {:flex-direction :row :margin-top 19}
[icons/icon :icons/settings {:color colors/blue}]
[react/text {:style styles/permissions-panel-permissions-label}
(i18n/label :t/manage-permissions)]]]])))
[components.common/button
{:on-press #(re-frame/dispatch [:browser.permissions.ui/dapp-permission-allowed])
:label (i18n/label :t/allow)}]]]])))
;; NOTE (andrey) we need this complex function, to show animation before component will be unmounted
(defn permissions-anim-panel [browser show-permission]

View File

@ -5,82 +5,85 @@
[status-im.browser.core :as browser]))
(deftest permissions-test
(let [dapp-name "test.com"
dapp-name2 "test2.org"
cofx {:db (assoc-in (:db (browser/open-url {:db {}} dapp-name))
[:account/account :public-key] "public-key")}]
(let [dapp-name "test.com"
dapp-name2 "test2.org"
cofx {:db (assoc-in (:db (browser/open-url {:db {}} dapp-name))
[:account/account :public-key] "public-key")}]
(testing "dapps permissions are initialized"
(is (zero? (count (get-in cofx [:db :dapps/permissions]))))
(is (= dapp-name (get-in cofx [:db :browser/options :browser-id]))))
(testing "receiving an unsupported permission"
(is (nil? (:browser/send-to-bridge (browser/process-bridge-message cofx
(types/clj->json {:type "status-api-request"
:host dapp-name
:permissions ["FAKE_PERMISSION"]}))))
"nothing should happen"))
(testing "receiving a supported permission and an unsupported one"
(let [result-ask (browser/process-bridge-message cofx
(types/clj->json {:type "status-api-request"
:host dapp-name
:permissions ["CONTACT_CODE" "FAKE_PERMISSION"]}))]
(types/clj->json {:type "api-request"
:host dapp-name
:messageId 0
:permission "FAKE_PERMISSION"}))]
(is (not (get-in result-ask [:browser/send-to-bridge :message :isAllowed])))))
(testing "receiving a supported permission"
(let [result-ask (browser/process-bridge-message cofx
(types/clj->json {:type "api-request"
:host dapp-name
:messageId 1
:permission "contact-code"}))]
(is (= (get-in result-ask [:db :browser/options :show-permission])
{:requested-permission "CONTACT_CODE", :dapp-name "test.com"}))
{:requested-permission "contact-code" :dapp-name "test.com" :message-id 1}))
(is (zero? (count (get-in result-ask [:db :dapps/permissions]))))
(testing "then user accepts the supported permission"
(let [accept-result (permissions/allow-permission {:db (:db result-ask)} dapp-name "CONTACT_CODE")]
(let [accept-result (permissions/allow-permission {:db (:db result-ask)})]
(is (= (get-in accept-result [:browser/send-to-bridge :message])
{:type "status-api-success"
:data {"CONTACT_CODE" "public-key"}
:keys ["CONTACT_CODE"]})
{:type "api-response"
:messageId 1
:isAllowed true
:data "public-key"
:permission "contact-code"})
"the data should have been sent to the bridge")
(is (= (get-in accept-result [:db :dapps/permissions])
{"test.com" {:dapp "test.com", :permissions ["CONTACT_CODE"]}})
{"test.com" {:dapp "test.com", :permissions ["contact-code"]}})
"the dapp should now have CONTACT_CODE permission")
(testing "then dapps asks for permission again"
(testing "then dapp asks for permission again"
(let [result-ask-again (browser/process-bridge-message {:db (:db accept-result)}
(types/clj->json {:type "status-api-request"
:host dapp-name
:permissions ["CONTACT_CODE"]}))]
(types/clj->json {:type "api-request"
:host dapp-name
:messageId 2
:permission "contact-code"}))]
(is (= (get-in result-ask-again
[:browser/send-to-bridge :message])
{:type "status-api-success"
:data {"CONTACT_CODE" "public-key"}
:keys ["CONTACT_CODE"]})
{:type "api-response"
:isAllowed true
:messageId 2
:data "public-key"
:permission "contact-code"})
"the response should be immediatly sent to the bridge")))
(testing "then user switch to another dapp that asks for permissions"
(let [new-dapp (browser/open-url {:db (:db accept-result)} dapp-name2)
(let [new-dapp (browser/open-url {:db (:db accept-result)} dapp-name2)
result-ask2 (browser/process-bridge-message {:db (:db new-dapp)}
(types/clj->json {:type "status-api-request"
:host dapp-name2
:permissions ["CONTACT_CODE" "FAKE_PERMISSION"]}))]
(types/clj->json {:type "api-request"
:host dapp-name2
:messageId 3
:permission "contact-code"}))]
(is (= (get-in result-ask2 [:db :dapps/permissions])
{"test.com" {:dapp "test.com", :permissions ["CONTACT_CODE"]}})
{"test.com" {:dapp "test.com", :permissions ["contact-code"]}})
"there should only be permissions for dapp-name at that point")
(is (nil? (get-in result-ask2
[:browser/send-to-bridge :message]))
"no message should be sent to the bridge")
(testing "then user accepts permission for dapp-name2"
(let [accept-result2 (permissions/allow-permission {:db (:db result-ask2)} dapp-name2 "CONTACT_CODE")]
(let [accept-result2 (permissions/allow-permission {:db (:db result-ask2)})]
(is (= (get-in accept-result2 [:db :dapps/permissions])
{"test.com" {:dapp "test.com" :permissions ["CONTACT_CODE"]}
"test2.org" {:dapp "test2.org" :permissions ["CONTACT_CODE"]}})
{"test.com" {:dapp "test.com" :permissions ["contact-code"]}
"test2.org" {:dapp "test2.org" :permissions ["contact-code"]}})
"there should be permissions for both dapps now")
(is (= (get-in accept-result2
[:browser/send-to-bridge :message])
{:type "status-api-success"
:data {"CONTACT_CODE" "public-key"}
:keys ["CONTACT_CODE"]})
"the response should be sent to the bridge")))))
(testing "then user refuses the permission"
(let [result-refuse (permissions/process-next-permission {:db (:db result-ask)} dapp-name)]
(is (zero? (count (get-in result-refuse [:db :dapps/permissions])))
"no permissions should be granted")
(is (nil? (get-in result-refuse [:browser/send-to-bridge :message]))
"no message should be sent to bridge")))))))))
{:type "api-response"
:isAllowed true
:messageId 3
:data "public-key"
:permission "contact-code"})
"the response should be sent to the bridge")))))))))))