diff --git a/app/_locales/cs/messages.json b/app/_locales/cs/messages.json
index 9e43ccb65188..9f645afa4e72 100644
--- a/app/_locales/cs/messages.json
+++ b/app/_locales/cs/messages.json
@@ -2,16 +2,16 @@
"confirmClear": {
"message": "Naozaj chcete vymazať schválené webové stránky?"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Schválené údaje webových stránek byly úspěšně zrušeny."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Údaje o schválení"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Vymažte schválené údaje webových stránek, aby všechny weby znovu požádaly o schválení."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Jasné údaje o schválení"
},
"reject": {
diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json
index b270e61dc0af..2a1540e05d93 100644
--- a/app/_locales/de/messages.json
+++ b/app/_locales/de/messages.json
@@ -11,16 +11,16 @@
"contractInteraction": {
"message": "Vertragsinteraktion"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Genehmigte Website-Daten wurden erfolgreich gelöscht."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Genehmigungsdaten"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Löschen Sie die genehmigten Website-Daten, damit alle Websites erneut eine Genehmigung anfordern müssen."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Genehmigungsdaten löschen"
},
"reject": {
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 6322e97255b4..7db92150e717 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -5,7 +5,7 @@
"showIncomingTransactionsDescription": {
"message": "Select this to use Etherscan to show incoming transactions in the transactions list"
},
- "caveat:filterParams": {
+ "caveat_filterParams": {
"message": "Only with: "
},
"caveat_filterResponse": {
@@ -41,27 +41,57 @@
"contractInteraction": {
"message": "Contract Interaction"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Permissions cleared successfully."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Manage Permissions"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "All currently granted permissions, by dapp/website. Deselect permissions and click \"Update Permissions\" to remove them."
},
- "permissionsDataEmpty": {
+ "permissionsEmpty": {
"message": "No permissions found."
},
- "clearPermissionsDataDescription": {
- "message": "Clear permissions so that all dapps/websites must request access again."
- },
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Clear Permissions"
},
- "updatePermissionsData": {
+ "clearPermissionsDescription": {
+ "message": "Clear permissions so that all dapps/websites must request access again."
+ },
+ "updatePermissions": {
"message": "Update Permissions"
},
+ "permissionsActivitySettings": {
+ "message": "Permissions Activity"
+ },
+ "permissionsActivityDescription": {
+ "message": "A log of the most recent permissions activity, including restricted method calls."
+ },
+ "permissionsActivityEmpty": {
+ "message": "No permissions activity found."
+ },
+ "clearPermissionsActivity": {
+ "message": "Clear Permissions Activity"
+ },
+ "clearPermissionsActivityDescription": {
+ "message": "Clear the permissions activity log."
+ },
+ "permissionsHistorySettings": {
+ "message": "Permissions History"
+ },
+ "permissionsHistoryDescription": {
+ "message": "A list of dapps/domains and all permissions they've ever had."
+ },
+ "permissionsHistoryEmpty": {
+ "message": "No permissions history found."
+ },
+ "clearPermissionsHistory": {
+ "message": "Clear Permissions History"
+ },
+ "clearPermissionsHistoryDescription": {
+ "message": "Clear the permissions history."
+ },
"reject": {
"message": "Reject"
},
diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json
index 5c36d21e28a1..edf6d5688a82 100644
--- a/app/_locales/es/messages.json
+++ b/app/_locales/es/messages.json
@@ -11,16 +11,16 @@
"contractInteraction": {
"message": "Interacción con contrato"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Los datos aprobados del sitio web se borraron con éxito."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Datos de aprobación"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Borrar la información privada de modo que todos los sitios deban volver a requerir acceso para acceder a los datos de la cuenta."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Borrar datos de aprobación"
},
"reject": {
diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json
index 79e595d34f6e..c44a057eb4bd 100644
--- a/app/_locales/fr/messages.json
+++ b/app/_locales/fr/messages.json
@@ -11,16 +11,16 @@
"contractInteraction": {
"message": "Interaction avec un contrat"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Les données de site Web approuvées ont été supprimées."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Données d'approbation"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Effacer les données de site Web approuvées afin que tous les sites doivent à nouveau demander l'approbation."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Effacer les données d'approbation"
},
"reject": {
diff --git a/app/_locales/hn/messages.json b/app/_locales/hn/messages.json
index 893927879245..660aed6a661b 100644
--- a/app/_locales/hn/messages.json
+++ b/app/_locales/hn/messages.json
@@ -2,16 +2,16 @@
"confirmClear": {
"message": "क्या आप वाकई अनुमोदित वेबसाइटों को साफ़ करना चाहते हैं?"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "स्वीकृत वेबसाइट डेटा सफलतापूर्वक मंजूरी दे दी।"
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "स्वीकृति डेटा"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "अनुमोदित वेबसाइट डेटा साफ़ करें ताकि सभी साइटों को फिर से अनुमोदन का अनुरोध करना होगा।"
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "अनुमोदन डेटा साफ़ करें"
},
"approve": {
diff --git a/app/_locales/ht/messages.json b/app/_locales/ht/messages.json
index ac54658107d2..8358ce5a133b 100644
--- a/app/_locales/ht/messages.json
+++ b/app/_locales/ht/messages.json
@@ -2,16 +2,16 @@
"confirmClear": {
"message": "Èske ou sèten ou vle klè sitwèb apwouve?"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Done sou sit wèb apwouve yo te klarifye avèk siksè."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Done sou vi prive"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Done sou vi prive klè pou tout sit entènèt yo dwe mande aksè pou wè enfòmasyon kont ankò."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Klè Done sou vi prive"
},
"providerRequestInfo": {
diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json
index f3c061ce77e2..639b67685841 100644
--- a/app/_locales/it/messages.json
+++ b/app/_locales/it/messages.json
@@ -11,16 +11,16 @@
"contractInteraction": {
"message": "Interazione Contratto"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Dati del sito Web approvati cancellati correttamente."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Dati di approvazione"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Cancella i dati del sito web approvati, quindi tutti i siti devono richiedere nuovamente l'approvazione."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Cancella i dati di approvazione"
},
"reject": {
diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json
index 367b016830c6..643da3a84ded 100644
--- a/app/_locales/ja/messages.json
+++ b/app/_locales/ja/messages.json
@@ -11,16 +11,16 @@
"contractInteraction": {
"message": "コントラクトへのアクセス"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "承認されたウェブサイトデータが正常に消去されました。"
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "承認データ"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "承認されたウェブサイトのデータをクリアすると、すべてのサイトで承認を再度要求する必要があります"
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "承認データのクリア"
},
"reject": {
diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json
index ad92c487fad8..d55fb027b059 100644
--- a/app/_locales/ko/messages.json
+++ b/app/_locales/ko/messages.json
@@ -11,16 +11,16 @@
"contractInteraction": {
"message": "계약 상호 작용"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "승인 된 웹 사이트 데이터가 성공적으로 삭제되었습니다."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "승인 데이터"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "승인 된 웹 사이트 데이터를 삭제하여 모든 사이트에서 승인을 다시 요청해야합니다."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "승인 데이터 삭제"
},
"reject": {
diff --git a/app/_locales/nl/messages.json b/app/_locales/nl/messages.json
index 83eade80b51f..c1e2290fa6af 100644
--- a/app/_locales/nl/messages.json
+++ b/app/_locales/nl/messages.json
@@ -2,16 +2,16 @@
"confirmClear": {
"message": "Weet je zeker dat je goedgekeurde websites wilt wissen?"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Goedgekeurde websitegegevens zijn met succes gewist."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Goedkeuringsgegevens"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Goedgekeurde websitegegevens wissen zodat alle sites opnieuw goedkeuring moeten aanvragen."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Gegevens over goedkeuring wissen"
},
"reject": {
diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json
index 7d506dea8d0c..2bc94990d823 100644
--- a/app/_locales/ph/messages.json
+++ b/app/_locales/ph/messages.json
@@ -2,16 +2,16 @@
"confirmClear": {
"message": "Sigurado ka bang gusto mong i-clear ang mga naaprubahang website?"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Matagumpay na na-clear ang data ng aprubadong website."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Data ng Pag-apruba"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "I-clear ang naaprubahang data ng website upang ang lahat ng site ay dapat humiling muli ng pag-apruba"
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Tanggalin ang data ng pag-apruba"
},
"appName": {
diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json
index 440d8acbca27..78f31df8343f 100644
--- a/app/_locales/pt/messages.json
+++ b/app/_locales/pt/messages.json
@@ -2,16 +2,16 @@
"confirmClear": {
"message": "Tem certeza de que deseja limpar sites aprovados?"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Dados aprovados do website foram limpos com sucesso."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Dados de aprovação"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Limpe os dados aprovados do website para que todos os sites solicitem aprovação novamente."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Limpar dados de aprovação"
},
"reject": {
diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json
index 6c8a6d57d807..04107f6b7490 100644
--- a/app/_locales/ru/messages.json
+++ b/app/_locales/ru/messages.json
@@ -2,16 +2,16 @@
"confirmClear": {
"message": "Вы уверены, что хотите очистить утвержденные веб-сайты?Tem certeza de que deseja limpar sites aprovados?"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Утвержденные данные веб-сайта успешно удалены."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Данные об утверждении"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Очистите утвержденные данные веб-сайта, чтобы все сайты снова запросили подтверждение."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Четкие данные об утверждении"
},
"reject": {
diff --git a/app/_locales/sk/messages.json b/app/_locales/sk/messages.json
index 035b5a751d57..c2be7a75fbab 100644
--- a/app/_locales/sk/messages.json
+++ b/app/_locales/sk/messages.json
@@ -11,16 +11,16 @@
"contractInteraction": {
"message": "Zmluvná interakcia"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Schválené údaje webových stránek byly úspěšně zrušeny."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Údaje o schválení"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Vymažte schválené údaje webových stránek, aby všechny weby znovu požádaly o schválení."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Jasné údaje o schválení"
},
"reject": {
diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json
index b71fd6fcf2f8..6b34e70ba040 100644
--- a/app/_locales/sl/messages.json
+++ b/app/_locales/sl/messages.json
@@ -11,16 +11,16 @@
"contractInteraction": {
"message": "Interakcija s pogodbo"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Odobrene spletne strani uspešno počiščene."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Podatki o odobritvi"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Počistite seznam odobrenih spletnih strani, tako da bodo morale ponovno zahtevati odobritev."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Počisti podatke o odobritvi"
},
"reject": {
diff --git a/app/_locales/ta/messages.json b/app/_locales/ta/messages.json
index 53911403e808..9dc4e8d930d1 100644
--- a/app/_locales/ta/messages.json
+++ b/app/_locales/ta/messages.json
@@ -2,16 +2,16 @@
"confirmClear": {
"message": "அங்கீகரிக்கப்பட்ட வலைத்தளங்களை நிச்சயமாக நீக்க விரும்புகிறீர்களா?"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "அங்கீகரிக்கப்பட்ட வலைத்தள தரவு வெற்றிகரமாக அழிக்கப்பட்டது."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "ஒப்புதல் தரவு"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "அங்கீகரிக்கப்பட்ட வலைத்தள தரவை அழிக்கவும், அனைத்து தளங்களும் ஒப்புதல் மீண்டும் கோர வேண்டும்."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "ஒப்புதல் தரவை அழி"
},
"approve": {
diff --git a/app/_locales/th/messages.json b/app/_locales/th/messages.json
index b2d300187ecc..16b608225536 100644
--- a/app/_locales/th/messages.json
+++ b/app/_locales/th/messages.json
@@ -2,16 +2,16 @@
"confirmClear": {
"message": "คุณแน่ใจหรือไม่ว่าต้องการล้างเว็บไซต์ที่ผ่านการอนุมัติ"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "อนุมัติข้อมูลเว็บไซต์ที่ได้รับอนุมัติแล้ว"
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "ข้อมูลการอนุมัติ"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "ล้างข้อมูลเว็บไซต์ที่ได้รับการอนุมัติเพื่อให้ทุกไซต์ต้องขออนุมัติอีกครั้ง"
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "ล้างข้อมูลการอนุมัติ"
},
"reject": {
diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json
index 8e14fca59dd7..4ffcc06636a4 100644
--- a/app/_locales/tr/messages.json
+++ b/app/_locales/tr/messages.json
@@ -2,16 +2,16 @@
"confirmClear": {
"message": "Onaylanmış web sitelerini silmek istediğinizden emin misiniz?"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Onaylanan web sitesi verileri başarıyla temizlendi."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Onay Verileri"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Onaylanan web sitesi verilerini temizle, tüm sitelerin tekrar onay isteğinde bulunması gerekir."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Onay verilerini temizle"
},
"reject": {
diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json
index 29f85705c010..ab7eb1c113e9 100644
--- a/app/_locales/vi/messages.json
+++ b/app/_locales/vi/messages.json
@@ -2,16 +2,16 @@
"confirmClear": {
"message": "Bạn có chắc chắn muốn xóa các trang web được phê duyệt không?"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "Đã xóa thành công dữ liệu trang web được phê duyệt."
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "Dữ liệu phê duyệt"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "Xóa dữ liệu trang web được phê duyệt để tất cả các trang web phải yêu cầu phê duyệt lại."
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "Xóa dữ liệu phê duyệt"
},
"reject": {
diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json
index ce39353e6d5e..0639d6bdbc02 100644
--- a/app/_locales/zh_CN/messages.json
+++ b/app/_locales/zh_CN/messages.json
@@ -11,16 +11,16 @@
"contractInteraction": {
"message": "合约交互"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "已批准的网站数据已成功清除。"
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "审批数据"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "清除已批准的网站数据,以便所有网站都必须再次申请"
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "清除批准数据"
},
"reject": {
diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json
index a3616e408122..6e4c40034f8f 100644
--- a/app/_locales/zh_TW/messages.json
+++ b/app/_locales/zh_TW/messages.json
@@ -11,16 +11,16 @@
"contractInteraction": {
"message": "合約互動"
},
- "clearPermissionsDataSuccess": {
+ "clearPermissionsSuccess": {
"message": "已批准的網站紀錄已成功清除。"
},
- "permissionsData": {
+ "permissionsSettings": {
"message": "審核紀錄"
},
- "permissionsDataDescription": {
+ "permissionsDescription": {
"message": "清除之前已批准過的網站審核紀錄,所有網站都必須再次申請"
},
- "clearPermissionsData": {
+ "clearPermissions": {
"message": "清除批准數據"
},
"reject": {
diff --git a/app/scripts/controllers/permissions/index.js b/app/scripts/controllers/permissions/index.js
new file mode 100644
index 000000000000..8f677ac7eb76
--- /dev/null
+++ b/app/scripts/controllers/permissions/index.js
@@ -0,0 +1 @@
+module.exports = require('./permissions')
diff --git a/app/scripts/controllers/permissions/loggerMiddleware.js b/app/scripts/controllers/permissions/loggerMiddleware.js
new file mode 100644
index 000000000000..73bd367a0904
--- /dev/null
+++ b/app/scripts/controllers/permissions/loggerMiddleware.js
@@ -0,0 +1,151 @@
+
+const clone = require('clone')
+const { isValidAddress } = require('ethereumjs-util')
+
+const LOG_LIMIT = 100
+
+/**
+ * Create middleware for logging requests and responses to restricted and
+ * permissions-related methods.
+ */
+module.exports = function createLoggerMiddleware ({
+ walletPrefix, restrictedMethods, store, logStoreKey, historyStoreKey,
+}) {
+ return (req, res, next, _end) => {
+
+ let activityEntry, requestedMethods
+ const { origin, method } = req
+ const isInternal = method.startsWith(walletPrefix)
+
+ if (isInternal || restrictedMethods.includes(method)) {
+ activityEntry = logActivity(req, isInternal)
+ if (method === `${walletPrefix}requestPermissions`) {
+ requestedMethods = getRequestedMethods(req)
+ }
+ } else {
+ return next()
+ }
+
+ next(cb => {
+ const time = Date.now()
+ addResponse(activityEntry, res, time)
+ if (!res.error && requestedMethods) {
+ logHistory(requestedMethods, origin, res.result, time)
+ }
+ cb()
+ })
+ }
+
+ function logActivity (request, isInternal) {
+ const activityEntry = {
+ id: request.id,
+ method: request.method,
+ methodType: isInternal ? 'internal' : 'restricted',
+ origin: request.origin,
+ request: cloneObj(request),
+ requestTime: Date.now(),
+ response: null,
+ responseTime: null,
+ success: null,
+ }
+ commitActivity(activityEntry)
+ return activityEntry
+ }
+
+ function addResponse (activityEntry, response, time) {
+ if (!response) return
+ activityEntry.response = cloneObj(response)
+ activityEntry.responseTime = time
+ activityEntry.success = !response.error
+ }
+
+ function commitActivity (entry) {
+ const logs = store.getState()[logStoreKey]
+ if (logs.length > LOG_LIMIT - 2) {
+ logs.pop()
+ }
+ logs.push(entry)
+ store.updateState({ [logStoreKey]: logs })
+ }
+
+ function getRequestedMethods (request) {
+ if (
+ !request.params ||
+ typeof request.params[0] !== 'object' ||
+ Array.isArray(request.params[0])
+ ) return null
+ return Object.keys(request.params[0])
+ }
+
+ function logHistory (requestedMethods, origin, result, time) {
+
+ let accounts
+ const entries = result
+ .map(perm => {
+ if (perm.parentCapability === 'eth_accounts') {
+ accounts = getAccountsFromPermission(perm)
+ }
+ return perm.parentCapability
+ })
+ .reduce((acc, m) => {
+ if (requestedMethods.includes(m)) {
+ acc[m] = {
+ lastApproved: time,
+ }
+ if (m === 'eth_accounts') {
+ acc[m].accounts = accounts
+ }
+ }
+ return acc
+ }, {})
+
+ if (Object.keys(entries).length > 0) {
+ commitHistory(origin, entries, accounts)
+ }
+ }
+
+ function commitHistory (origin, entries, accounts) {
+
+ const history = store.getState()[historyStoreKey]
+
+ if (Array.isArray(accounts)) {
+ if (history[origin] && history[origin]['eth_accounts']) {
+ history[origin]['eth_accounts']['accounts']
+ .filter(acc => !accounts.includes(acc))
+ .forEach(acc => accounts.unshift(acc))
+ }
+ accounts.sort()
+ }
+
+ history[origin] = {
+ ...history[origin],
+ ...entries,
+ }
+ store.updateState({ [historyStoreKey]: history })
+ }
+}
+
+// the call to clone is set to disallow circular references
+// we attempt cloning at a depth of 3 and 2, then return a
+// shallow copy of the object
+function cloneObj (obj) {
+ for (let i = 3; i > 1; i--) {
+ try {
+ return clone(obj, false, i)
+ } catch (_) {}
+ }
+ return { ...obj }
+}
+
+function getAccountsFromPermission (perm) {
+ if (perm.parentCapability !== 'eth_accounts' || !perm.caveats) return []
+ const accounts = {}
+ for (const c of perm.caveats) {
+ if (c.type === 'filterResponse' && Array.isArray(c.value)) {
+ for (const v of c.value) {
+ if (isValidAddress(v)) accounts[v] = true
+ }
+ }
+ }
+ return Object.keys(accounts)
+}
diff --git a/app/scripts/lib/permissions-safe-methods.json b/app/scripts/controllers/permissions/permissions-safe-methods.json
similarity index 100%
rename from app/scripts/lib/permissions-safe-methods.json
rename to app/scripts/controllers/permissions/permissions-safe-methods.json
diff --git a/app/scripts/controllers/permissions.js b/app/scripts/controllers/permissions/permissions.js
similarity index 60%
rename from app/scripts/controllers/permissions.js
rename to app/scripts/controllers/permissions/permissions.js
index 2d6b073f687d..807341f3fce4 100644
--- a/app/scripts/controllers/permissions.js
+++ b/app/scripts/controllers/permissions/permissions.js
@@ -1,83 +1,65 @@
const JsonRpcEngine = require('json-rpc-engine')
const asMiddleware = require('json-rpc-engine/src/asMiddleware')
-const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware')
const ObservableStore = require('obs-store')
const RpcCap = require('json-rpc-capabilities-middleware').CapabilitiesController
-const { errors: rpcErrors } = require('eth-json-rpc-errors')
+
+const getRestrictedMethods = require('./restrictedMethods')
+const createRequestMiddleware = require('./requestMiddleware')
+const createLoggerMiddleware = require('./loggerMiddleware')
// Methods that do not require any permissions to use:
-const SAFE_METHODS = require('../lib/permissions-safe-methods.json')
+const SAFE_METHODS = require('./permissions-safe-methods.json')
-const METHOD_PREFIX = 'wallet_'
+const METADATA_STORE_KEY = 'siteMetadata'
+const LOG_STORE_KEY = 'permissionsLog'
+const HISTORY_STORE_KEY = 'permissionsHistory'
+const WALLET_METHOD_PREFIX = 'wallet_'
const INTERNAL_METHOD_PREFIX = 'metamask_'
function prefix (method) {
- return METHOD_PREFIX + method
+ return WALLET_METHOD_PREFIX + method
}
// class PermissionsController extends SafeEventEmitter {
class PermissionsController {
- constructor ({
- openPopup, closePopup, keyringController,
- } = {}, restoredPermissions = {}, restoredState = { siteMetadata: {} }) {
+ constructor (
+ { openPopup, closePopup, keyringController} = {},
+ restoredPermissions = {},
+ restoredState = {}) {
this.store = new ObservableStore({
- siteMetadata: { ...restoredState.siteMetadata },
+ [METADATA_STORE_KEY]: restoredState[METADATA_STORE_KEY] || {},
+ [LOG_STORE_KEY]: restoredState[LOG_STORE_KEY] || [],
+ [HISTORY_STORE_KEY]: restoredState[HISTORY_STORE_KEY] || {},
})
this._openPopup = openPopup
this._closePopup = closePopup
this.keyringController = keyringController
+ this._restrictedMethods = getRestrictedMethods(this)
this._initializePermissions(restoredPermissions)
}
createMiddleware (options) {
const { origin } = options
const engine = new JsonRpcEngine()
- engine.push(this.createRequestMiddleware(options))
+ engine.push(createRequestMiddleware({
+ internalPrefix: INTERNAL_METHOD_PREFIX,
+ store: this.store,
+ storeKey: METADATA_STORE_KEY,
+ }))
+ engine.push(createLoggerMiddleware({
+ walletPrefix: WALLET_METHOD_PREFIX,
+ restrictedMethods: Object.keys(this._restrictedMethods),
+ store: this.store,
+ logStoreKey: LOG_STORE_KEY,
+ historyStoreKey: HISTORY_STORE_KEY,
+ }))
engine.push(this.permissions.providerMiddlewareFunction.bind(
this.permissions, { origin }
))
return asMiddleware(engine)
}
- /**
- * Create middleware for preprocessing permissions requests.
- */
- createRequestMiddleware () {
- return createAsyncMiddleware(async (req, res, next) => {
-
- if (typeof req.method !== 'string') {
- res.error = rpcErrors.invalidRequest(null, req)
- return // TODO:json-rpc-engine
- }
-
- if (req.method.startsWith(INTERNAL_METHOD_PREFIX)) {
- switch (req.method.split(INTERNAL_METHOD_PREFIX)[1]) {
- case 'sendSiteMetadata':
- if (
- req.siteMetadata &&
- typeof req.siteMetadata.name === 'string'
- ) {
- this.store.putState({
- siteMetadata: {
- ...this.store.getState().siteMetadata,
- [req.origin]: req.siteMetadata,
- },
- })
- }
- break
- default:
- res.error = rpcErrors.methodNotFound(null, req.method)
- break
- }
- if (!res.error) res.result = true
- return
- }
-
- return next()
- })
- }
-
/**
* Returns the accounts that should be exposed for the given origin domain,
* if any. This method exists for when a trusted context needs to know
@@ -124,6 +106,24 @@ class PermissionsController {
this.permissions.clearDomains()
}
+ /**
+ * Clears the permissions log.
+ */
+ clearLog () {
+ this.store.updateState({
+ [LOG_STORE_KEY]: [],
+ })
+ }
+
+ /**
+ * Clears the permissions history.
+ */
+ clearHistory () {
+ this.store.updateState({
+ [HISTORY_STORE_KEY]: {},
+ })
+ }
+
/**
* User approval callback.
* @param {object} approved the approved request object
@@ -172,44 +172,9 @@ class PermissionsController {
safeMethods: SAFE_METHODS,
// optional prefix for internal methods
- methodPrefix: METHOD_PREFIX,
-
- restrictedMethods: {
-
- 'eth_accounts': {
- description: 'View Ethereum accounts',
- method: (_, res, __, end) => {
- this.keyringController.getAccounts()
- .then((accounts) => {
- res.result = accounts
- end()
- })
- .catch((reason) => {
- res.error = reason
- end(reason)
- })
- },
- },
-
- // Restricted methods themselves are defined as
- // json-rpc-engine middleware functions.
- 'readYourProfile': {
- description: 'Read from your profile',
- method: (_req, res, _next, end) => {
- res.result = this.testProfile
- end()
- },
- },
- 'writeToYourProfile': {
- description: 'Write to your profile.',
- method: (req, res, _next, end) => {
- const [ key, value ] = req.params
- this.testProfile[key] = value
- res.result = this.testProfile
- return end()
- },
- },
- },
+ methodPrefix: WALLET_METHOD_PREFIX,
+
+ restrictedMethods: this._restrictedMethods,
/**
* A promise-returning callback used to determine whether to approve
@@ -229,12 +194,7 @@ class PermissionsController {
return new Promise((resolve, reject) => {
this.pendingApprovals[id] = { resolve, reject }
- },
- // TODO: This should be persisted/restored state.
- {})
-
- // TODO: Attenuate requested permissions in approval screen.
- // Like selecting the account to display.
+ })
},
}, initState)
}
diff --git a/app/scripts/controllers/permissions/requestMiddleware.js b/app/scripts/controllers/permissions/requestMiddleware.js
new file mode 100644
index 000000000000..446c66a92756
--- /dev/null
+++ b/app/scripts/controllers/permissions/requestMiddleware.js
@@ -0,0 +1,41 @@
+
+const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware')
+const { errors: rpcErrors } = require('eth-json-rpc-errors')
+
+/**
+ * Create middleware for preprocessing permissions requests.
+ */
+module.exports = function createRequestMiddleware ({
+ internalPrefix, store, storeKey,
+}) {
+ return createAsyncMiddleware(async (req, res, next) => {
+
+ if (typeof req.method !== 'string') {
+ res.error = rpcErrors.invalidRequest(null, req)
+ return
+ }
+
+ if (req.method.startsWith(internalPrefix)) {
+ switch (req.method.split(internalPrefix)[1]) {
+ case 'sendSiteMetadata':
+ if (
+ req.siteMetadata &&
+ typeof req.siteMetadata.name === 'string'
+ ) {
+ store.updateState({
+ [storeKey]: {
+ ...store.getState()[storeKey],
+ [req.origin]: req.siteMetadata,
+ },
+ })
+ }
+ res.result = true
+ return
+ default:
+ break
+ }
+ }
+
+ return next()
+ })
+}
diff --git a/app/scripts/controllers/permissions/restrictedMethods.js b/app/scripts/controllers/permissions/restrictedMethods.js
new file mode 100644
index 000000000000..de917c2addd6
--- /dev/null
+++ b/app/scripts/controllers/permissions/restrictedMethods.js
@@ -0,0 +1,37 @@
+
+module.exports = function getRestrictedMethods (permissionsController) {
+ return {
+ 'eth_accounts': {
+ description: 'View Ethereum accounts',
+ method: (_, res, __, end) => {
+ permissionsController.keyringController.getAccounts()
+ .then((accounts) => {
+ res.result = accounts
+ end()
+ })
+ .catch((reason) => {
+ res.error = reason
+ end(reason)
+ })
+ },
+ },
+
+ 'readYourProfile': {
+ description: 'Read from your profile',
+ method: (_req, res, _next, end) => {
+ res.result = permissionsController.testProfile
+ end()
+ },
+ },
+
+ 'writeToYourProfile': {
+ description: 'Write to your profile.',
+ method: (req, res, _next, end) => {
+ const [ key, value ] = req.params
+ permissionsController.testProfile[key] = value
+ res.result = permissionsController.testProfile
+ return end()
+ },
+ },
+ }
+}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 99797fd647ea..d245f2bb1cf0 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -36,7 +36,7 @@ const TypedMessageManager = require('./lib/typed-message-manager')
const TransactionController = require('./controllers/transactions')
const TokenRatesController = require('./controllers/token-rates')
const DetectTokensController = require('./controllers/detect-tokens')
-const { PermissionsController } = require('./controllers/permissions')
+const { PermissionsController } = require('./controllers/permissions/permissions')
const nodeify = require('./lib/nodeify')
const accountImporter = require('./account-import-strategies')
const getBuyEthUrl = require('./lib/buy-eth-url')
@@ -265,7 +265,7 @@ module.exports = class MetamaskController extends EventEmitter {
openPopup: opts.openPopup,
closePopup: opts.closePopup,
},
- initState.PermissionsController, initState.SiteMetadata)
+ initState.PermissionsController, initState.PermissionsMetadata)
this.store.updateStructure({
AppStateController: this.appStateController.store,
@@ -281,7 +281,7 @@ module.exports = class MetamaskController extends EventEmitter {
OnboardingController: this.onboardingController.store,
IncomingTransactionsController: this.incomingTransactionsController.store,
PermissionsController: this.permissionsController.permissions,
- SiteMetadata: this.permissionsController.store,
+ PermissionsMetadata: this.permissionsController.store,
ThreeBoxController: this.threeBoxController.store,
})
@@ -305,7 +305,7 @@ module.exports = class MetamaskController extends EventEmitter {
OnboardingController: this.onboardingController.store,
IncomingTransactionsController: this.incomingTransactionsController.store,
PermissionsController: this.permissionsController.permissions,
- SiteMetadata: this.permissionsController.store,
+ PermissionsMetadata: this.permissionsController.store,
ThreeBoxController: this.threeBoxController.store,
})
this.memStore.subscribe(this.sendUpdate.bind(this))
@@ -368,9 +368,7 @@ module.exports = class MetamaskController extends EventEmitter {
}
function updatePublicConfigStore (memState) {
- selectPublicState(memState).then(publicState => {
- publicConfigStore.putState(publicState)
- })
+ publicConfigStore.putState(selectPublicState(memState))
}
function selectPublicState ({ isUnlocked, network, completedOnboarding, provider }) {
@@ -528,10 +526,12 @@ module.exports = class MetamaskController extends EventEmitter {
// permissions
approvePermissionsRequest: nodeify(this.permissionsController.approvePermissionsRequest, this.permissionsController),
- rejectPermissionsRequest: nodeify(this.permissionsController.rejectPermissionsRequest, this.permissionsController),
- removePermissionsFor: this.permissionsController.removePermissionsFor.bind(this.permissionsController),
clearPermissions: this.permissionsController.clearPermissions.bind(this.permissionsController),
+ clearPermissionsHistory: this.permissionsController.clearHistory.bind(this.permissionsController),
+ clearPermissionsLog: this.permissionsController.clearLog.bind(this.permissionsController),
getApprovedAccounts: nodeify(this.permissionsController.getAccounts.bind(this.permissionsController)),
+ rejectPermissionsRequest: nodeify(this.permissionsController.rejectPermissionsRequest, this.permissionsController),
+ removePermissionsFor: this.permissionsController.removePermissionsFor.bind(this.permissionsController),
}
}
diff --git a/package.json b/package.json
index 612a04a1560e..f1ebec08c2c2 100644
--- a/package.json
+++ b/package.json
@@ -167,6 +167,7 @@
"request-promise": "^4.2.1",
"reselect": "^3.0.1",
"safe-event-emitter": "^1.0.1",
+ "safe-json-stringify": "^1.2.0",
"single-call-balance-checker-abi": "^1.0.0",
"string.prototype.matchall": "^3.0.1",
"swappable-obj-proxy": "^1.1.0",
diff --git a/ui/app/components/app/modals/clear-permissions-activity/clear-permissions-activity.component.js b/ui/app/components/app/modals/clear-permissions-activity/clear-permissions-activity.component.js
new file mode 100644
index 000000000000..47ea494accba
--- /dev/null
+++ b/ui/app/components/app/modals/clear-permissions-activity/clear-permissions-activity.component.js
@@ -0,0 +1,39 @@
+import React, { PureComponent } from 'react'
+import PropTypes from 'prop-types'
+import Modal, { ModalContent } from '../../modal'
+
+export default class ClearPermissionsActivity extends PureComponent {
+ static propTypes = {
+ hideModal: PropTypes.func.isRequired,
+ clearPermissionsLog: PropTypes.func.isRequired,
+ }
+
+ static contextTypes = {
+ t: PropTypes.func,
+ }
+
+ handleClear = () => {
+ const { clearPermissionsLog, hideModal } = this.props
+ clearPermissionsLog()
+ hideModal()
+ }
+
+ render () {
+ const { t } = this.context
+
+ return (
+