1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-27 21:00:13 +01:00
metamask-extension/app/scripts/controllers/provider-approval.js

163 lines
5.5 KiB
JavaScript
Raw Normal View History

2018-09-27 20:19:09 +02:00
const ObservableStore = require('obs-store')
const SafeEventEmitter = require('safe-event-emitter')
const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware')
2018-09-27 20:19:09 +02:00
/**
* A controller that services user-approved requests for a full Ethereum provider API
*/
class ProviderApprovalController extends SafeEventEmitter {
2018-10-29 23:44:04 +01:00
/**
* Determines if caching is enabled
*/
2018-10-31 10:07:49 +01:00
caching = true
2018-10-29 23:44:04 +01:00
2018-09-27 20:19:09 +02:00
/**
* Creates a ProviderApprovalController
*
* @param {Object} [config] - Options to configure controller
*/
constructor ({ closePopup, keyringController, openPopup, preferencesController } = {}) {
super()
2018-09-27 20:19:09 +02:00
this.closePopup = closePopup
2018-10-29 22:28:59 +01:00
this.keyringController = keyringController
2018-09-27 20:19:09 +02:00
this.openPopup = openPopup
2018-10-10 20:52:26 +02:00
this.preferencesController = preferencesController
this.store = new ObservableStore({
approvedOrigins: {},
providerRequests: [],
})
}
2018-10-29 22:28:59 +01:00
/**
* Called when a user approves access to a full Ethereum provider API
*
* @param {object} opts - opts for the middleware contains the origin for the middleware
*/
createMiddleware ({ origin, getSiteMetadata }) {
return createAsyncMiddleware(async (req, res, next) => {
// only handle requestAccounts
if (req.method !== 'eth_requestAccounts') return next()
// if already approved or privacy mode disabled, return early
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
if (this.shouldExposeAccounts(origin) && isUnlocked) {
res.result = [this.preferencesController.getSelectedAddress()]
return
}
// register the provider request
const metadata = await getSiteMetadata(origin)
this._handleProviderRequest(origin, metadata.name, metadata.icon)
// wait for resolution of request
const approved = await new Promise(resolve => this.once(`resolvedRequest:${origin}`, ({ approved }) => resolve(approved)))
if (approved) {
res.result = [this.preferencesController.getSelectedAddress()]
} else {
throw new Error('User denied account authorization')
}
})
2018-09-27 20:19:09 +02:00
}
/**
* Called when a tab requests access to a full Ethereum provider API
*
* @param {string} origin - Origin of the window requesting full provider access
* @param {string} siteTitle - The title of the document requesting full provider access
* @param {string} siteImage - The icon of the window requesting full provider access
2018-09-27 20:19:09 +02:00
*/
_handleProviderRequest (origin, siteTitle, siteImage) {
this.store.updateState({ providerRequests: [{ origin, siteTitle, siteImage }] })
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
if (this.store.getState().approvedOrigins[origin] && this.caching && isUnlocked) {
2018-09-27 20:19:09 +02:00
return
}
this.openPopup && this.openPopup()
}
/**
* Called when a user approves access to a full Ethereum provider API
*
* @param {string} origin - origin of the domain that had provider access approved
2018-09-27 20:19:09 +02:00
*/
approveProviderRequestByOrigin (origin) {
if (this.closePopup) {
this.closePopup()
}
const { approvedOrigins, providerRequests } = this.store.getState()
const remainingProviderRequests = providerRequests.filter(request => request.origin !== origin)
this.store.updateState({
approvedOrigins: {
...approvedOrigins,
[origin]: true,
},
providerRequests: remainingProviderRequests,
})
this.emit(`resolvedRequest:${origin}`, { approved: true })
2018-09-27 20:19:09 +02:00
}
/**
* Called when a tab rejects access to a full Ethereum provider API
*
* @param {string} origin - origin of the domain that had provider access approved
2018-09-27 20:19:09 +02:00
*/
rejectProviderRequestByOrigin (origin) {
if (this.closePopup) {
this.closePopup()
}
const { approvedOrigins, providerRequests } = this.store.getState()
const remainingProviderRequests = providerRequests.filter(request => request.origin !== origin)
// We're cloning and deleting keys here because we don't want to keep unneeded keys
const _approvedOrigins = Object.assign({}, approvedOrigins)
delete _approvedOrigins[origin]
this.store.putState({
approvedOrigins: _approvedOrigins,
providerRequests: remainingProviderRequests,
})
this.emit(`resolvedRequest:${origin}`, { approved: false })
2018-09-27 20:19:09 +02:00
}
/**
* Silently approves access to a full Ethereum provider API for the origin
*
* @param {string} origin - origin of the domain that had provider access approved
*/
forceApproveProviderRequestByOrigin (origin) {
const { approvedOrigins, providerRequests } = this.store.getState()
const remainingProviderRequests = providerRequests.filter(request => request.origin !== origin)
this.store.updateState({
approvedOrigins: {
...approvedOrigins,
[origin]: true,
},
providerRequests: remainingProviderRequests,
})
this.emit(`forceResolvedRequest:${origin}`, { approved: true, forced: true })
}
2018-09-27 20:19:09 +02:00
/**
* Clears any cached approvals for user-approved origins
*/
clearApprovedOrigins () {
this.store.updateState({
approvedOrigins: {},
})
2018-09-27 20:19:09 +02:00
}
/**
2018-11-06 20:13:27 +01:00
* Determines if a given origin should have accounts exposed
2018-09-27 20:19:09 +02:00
*
* @param {string} origin - Domain origin to check for approval status
* @returns {boolean} - True if the origin has been approved
*/
2018-11-06 20:13:27 +01:00
shouldExposeAccounts (origin) {
2018-10-10 20:52:26 +02:00
const privacyMode = this.preferencesController.getFeatureFlags().privacyMode
return !privacyMode || Boolean(this.store.getState().approvedOrigins[origin])
2018-09-27 20:19:09 +02:00
}
2018-10-29 23:44:04 +01:00
2018-09-27 20:19:09 +02:00
}
module.exports = ProviderApprovalController