diff options
Diffstat (limited to 'app/scripts/controllers/provider-approval.js')
-rw-r--r-- | app/scripts/controllers/provider-approval.js | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/app/scripts/controllers/provider-approval.js b/app/scripts/controllers/provider-approval.js new file mode 100644 index 000000000..21d7fd22e --- /dev/null +++ b/app/scripts/controllers/provider-approval.js @@ -0,0 +1,158 @@ +const ObservableStore = require('obs-store') + +/** + * A controller that services user-approved requests for a full Ethereum provider API + */ +class ProviderApprovalController { + /** + * Determines if caching is enabled + */ + caching = true + + /** + * Creates a ProviderApprovalController + * + * @param {Object} [config] - Options to configure controller + */ + constructor ({ closePopup, keyringController, openPopup, platform, preferencesController, publicConfigStore } = {}) { + this.approvedOrigins = {} + this.closePopup = closePopup + this.keyringController = keyringController + this.openPopup = openPopup + this.platform = platform + this.preferencesController = preferencesController + this.publicConfigStore = publicConfigStore + this.store = new ObservableStore() + + if (platform && platform.addMessageListener) { + platform.addMessageListener(({ action = '', force, origin, siteTitle, siteImage }) => { + switch (action) { + case 'init-provider-request': + this._handleProviderRequest(origin, siteTitle, siteImage, force) + break + case 'init-is-approved': + this._handleIsApproved(origin) + break + case 'init-is-unlocked': + this._handleIsUnlocked() + break + case 'init-privacy-request': + this._handlePrivacyRequest() + break + } + }) + } + } + + /** + * 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 + */ + _handleProviderRequest (origin, siteTitle, siteImage, force) { + this.store.updateState({ providerRequests: [{ origin, siteTitle, siteImage }] }) + const isUnlocked = this.keyringController.memStore.getState().isUnlocked + if (!force && this.approvedOrigins[origin] && this.caching && isUnlocked) { + this.approveProviderRequest(origin) + return + } + this.openPopup && this.openPopup() + } + + /** + * Called by a tab to determine if an origin has been approved in the past + * + * @param {string} origin - Origin of the window + */ + _handleIsApproved (origin) { + this.platform && this.platform.sendMessage({ + action: 'answer-is-approved', + isApproved: this.approvedOrigins[origin] && this.caching, + caching: this.caching, + }, { active: true }) + } + + /** + * Called by a tab to determine if MetaMask is currently locked or unlocked + */ + _handleIsUnlocked () { + const isUnlocked = this.keyringController.memStore.getState().isUnlocked + this.platform && this.platform.sendMessage({ action: 'answer-is-unlocked', isUnlocked }, { active: true }) + } + + /** + * Called to check privacy mode; if privacy mode is off, this will automatically enable the provider (legacy behavior) + */ + _handlePrivacyRequest () { + const privacyMode = this.preferencesController.getFeatureFlags().privacyMode + if (!privacyMode) { + this.platform && this.platform.sendMessage({ + action: 'approve-legacy-provider-request', + selectedAddress: this.publicConfigStore.getState().selectedAddress, + }, { active: true }) + this.publicConfigStore.emit('update', this.publicConfigStore.getState()) + } + } + + /** + * Called when a user approves access to a full Ethereum provider API + * + * @param {string} origin - Origin of the target window to approve provider access + */ + approveProviderRequest (origin) { + this.closePopup && this.closePopup() + const requests = this.store.getState().providerRequests || [] + this.platform && this.platform.sendMessage({ + action: 'approve-provider-request', + selectedAddress: this.publicConfigStore.getState().selectedAddress, + }, { active: true }) + this.publicConfigStore.emit('update', this.publicConfigStore.getState()) + const providerRequests = requests.filter(request => request.origin !== origin) + this.store.updateState({ providerRequests }) + this.approvedOrigins[origin] = true + } + + /** + * Called when a tab rejects access to a full Ethereum provider API + * + * @param {string} origin - Origin of the target window to reject provider access + */ + rejectProviderRequest (origin) { + this.closePopup && this.closePopup() + const requests = this.store.getState().providerRequests || [] + this.platform && this.platform.sendMessage({ action: 'reject-provider-request' }, { active: true }) + const providerRequests = requests.filter(request => request.origin !== origin) + this.store.updateState({ providerRequests }) + delete this.approvedOrigins[origin] + } + + /** + * Clears any cached approvals for user-approved origins + */ + clearApprovedOrigins () { + this.approvedOrigins = {} + } + + /** + * Determines if a given origin should have accounts exposed + * + * @param {string} origin - Domain origin to check for approval status + * @returns {boolean} - True if the origin has been approved + */ + shouldExposeAccounts (origin) { + const privacyMode = this.preferencesController.getFeatureFlags().privacyMode + return !privacyMode || this.approvedOrigins[origin] + } + + /** + * Tells all tabs that MetaMask is now locked. This is primarily used to set + * internal flags in the contentscript and inpage script. + */ + setLocked () { + this.platform.sendMessage({ action: 'metamask-set-locked' }) + } +} + +module.exports = ProviderApprovalController |