diff --git a/src/components/tw-security-manager-modal/fetch.jsx b/src/components/tw-security-manager-modal/fetch.jsx index ab7cb5e56..e467f41cd 100644 --- a/src/components/tw-security-manager-modal/fetch.jsx +++ b/src/components/tw-security-manager-modal/fetch.jsx @@ -7,7 +7,7 @@ const FetchModal = props => (
@@ -15,15 +15,14 @@ const FetchModal = props => (

diff --git a/src/components/tw-security-manager-modal/notify.jsx b/src/components/tw-security-manager-modal/notify.jsx new file mode 100644 index 000000000..21fdc14e9 --- /dev/null +++ b/src/components/tw-security-manager-modal/notify.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import {FormattedMessage} from 'react-intl'; + +const Notify = () => ( +
+

+ +

+

+ +

+
+); + +export default Notify; diff --git a/src/components/tw-security-manager-modal/open-window.jsx b/src/components/tw-security-manager-modal/open-window.jsx index cb1b66af4..5f361b1ab 100644 --- a/src/components/tw-security-manager-modal/open-window.jsx +++ b/src/components/tw-security-manager-modal/open-window.jsx @@ -8,7 +8,7 @@ const OpenWindowModal = props => (
@@ -16,7 +16,7 @@ const OpenWindowModal = props => ( ( +
+

+ +

+

+ +

+

+ +

+
+); + +export default ReadClipboard; diff --git a/src/components/tw-security-manager-modal/record-audio.jsx b/src/components/tw-security-manager-modal/record-audio.jsx new file mode 100644 index 000000000..ad94079d6 --- /dev/null +++ b/src/components/tw-security-manager-modal/record-audio.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import {FormattedMessage} from 'react-intl'; + +const RecordAudio = () => ( +
+

+ +

+

+ +

+
+); + +export default RecordAudio; diff --git a/src/components/tw-security-manager-modal/record-video.jsx b/src/components/tw-security-manager-modal/record-video.jsx new file mode 100644 index 000000000..75dab1a6a --- /dev/null +++ b/src/components/tw-security-manager-modal/record-video.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import {FormattedMessage} from 'react-intl'; + +const RecordVideo = () => ( +
+

+ +

+

+ +

+
+); + +export default RecordVideo; diff --git a/src/components/tw-security-manager-modal/redirect.jsx b/src/components/tw-security-manager-modal/redirect.jsx index c1fcace70..e7bdb168f 100644 --- a/src/components/tw-security-manager-modal/redirect.jsx +++ b/src/components/tw-security-manager-modal/redirect.jsx @@ -8,7 +8,7 @@ const RedirectModal = props => (
@@ -16,7 +16,7 @@ const RedirectModal = props => ( ( ) : props.type === SecurityModals.Redirect ? ( + ) : props.type === SecurityModals.RecordAudio ? ( + + ) : props.type === SecurityModals.RecordVideo ? ( + + ) : props.type === SecurityModals.ReadClipboard ? ( + + ) : props.type === SecurityModals.Notify ? ( + ) : null} diff --git a/src/containers/tw-security-manager.jsx b/src/containers/tw-security-manager.jsx index 74e1c6e8a..6acb0d480 100644 --- a/src/containers/tw-security-manager.jsx +++ b/src/containers/tw-security-manager.jsx @@ -98,34 +98,46 @@ const parseURL = url => { return parsed; }; +let allowedAudio = false; +let allowedVideo = false; +let allowedReadClipboard = false; +let allowedNotify = false; + +const SECURITY_MANAGER_METHODS = [ + 'getSandboxMode', + 'canLoadExtensionFromProject', + 'canFetch', + 'canOpenWindow', + 'canRedirect', + 'canRecordAudio', + 'canRecordVideo', + 'canReadClipboard', + 'canNotify' +]; + class TWSecurityManagerComponent extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'getSandboxMode', - 'canLoadExtensionFromProject', - 'canFetch', - 'canOpenWindow', - 'canRedirect', 'handleAllowed', 'handleDenied' ]); + bindAll(this, SECURITY_MANAGER_METHODS); this.nextModalCallbacks = []; this.modalLocked = false; this.state = { type: null, data: null, - callback: null + callback: null, + modalCount: 0 }; } componentDidMount () { const securityManager = this.props.vm.extensionManager.securityManager; - securityManager.getSandboxMode = this.getSandboxMode; - securityManager.canLoadExtensionFromProject = this.canLoadExtensionFromProject; - securityManager.canFetch = this.canFetch; - securityManager.canOpenWindow = this.canOpenWindow; - securityManager.canRedirect = this.canRedirect; + for (const method of SECURITY_MANAGER_METHODS) { + securityManager[method] = this[method]; + } } // eslint-disable-next-line valid-jsdoc @@ -162,11 +174,12 @@ class TWSecurityManagerComponent extends React.Component { const showModal = async (type, data) => { const result = await new Promise(resolve => { - this.setState({ + this.setState(oldState => ({ type, data, - callback: resolve - }); + callback: resolve, + modalCount: oldState.modalCount + 1 + })); }); releaseLock(); return result; @@ -291,6 +304,50 @@ class TWSecurityManagerComponent extends React.Component { }); } + /** + * @returns {Promise} True if audio can be recorded + */ + async canRecordAudio () { + if (!allowedAudio) { + const {showModal} = await this.acquireModalLock(); + allowedAudio = await showModal(SecurityModals.RecordAudio); + } + return allowedAudio; + } + + /** + * @returns {Promise} True if video can be recorded + */ + async canRecordVideo () { + if (!allowedVideo) { + const {showModal} = await this.acquireModalLock(); + allowedVideo = await showModal(SecurityModals.RecordVideo); + } + return allowedVideo; + } + + /** + * @returns {Promise} True if the clipboard can be read + */ + async canReadClipboard () { + if (!allowedReadClipboard) { + const {showModal} = await this.acquireModalLock(); + allowedReadClipboard = await showModal(SecurityModals.ReadClipboard); + } + return allowedReadClipboard; + } + + /** + * @returns {Promise} True if the notifications are allowed + */ + async canNotify () { + if (!allowedNotify) { + const {showModal} = await this.acquireModalLock(); + allowedNotify = await showModal(SecurityModals.Notify); + } + return allowedNotify; + } + render () { if (this.state.type) { return ( @@ -299,6 +356,7 @@ class TWSecurityManagerComponent extends React.Component { data={this.state.data} onAllowed={this.handleAllowed} onDenied={this.handleDenied} + key={this.state.modalCount} /> ); } @@ -309,13 +367,12 @@ class TWSecurityManagerComponent extends React.Component { TWSecurityManagerComponent.propTypes = { vm: PropTypes.shape({ extensionManager: PropTypes.shape({ - securityManager: PropTypes.shape({ - getSandboxMode: PropTypes.func.isRequired, - canLoadExtensionFromProject: PropTypes.func.isRequired, - canFetch: PropTypes.func.isRequired, - canOpenWindow: PropTypes.func.isRequired, - canRedirect: PropTypes.func.isRequired - }).isRequired + securityManager: PropTypes.shape( + SECURITY_MANAGER_METHODS.reduce((obj, method) => { + obj[method] = PropTypes.func.isRequired; + return obj; + }, {}) + ).isRequired }).isRequired }).isRequired }; diff --git a/src/lib/tw-security-manager-constants.js b/src/lib/tw-security-manager-constants.js index 7b0260251..edfab5079 100644 --- a/src/lib/tw-security-manager-constants.js +++ b/src/lib/tw-security-manager-constants.js @@ -2,7 +2,11 @@ const SecurityModals = { LoadExtension: 'LoadExtension', Fetch: 'Fetch', OpenWindow: 'OpenWindow', - Redirect: 'Redirect' + Redirect: 'Redirect', + RecordAudio: 'RecordAudio', + RecordVideo: 'RecordVideo', + ReadClipboard: 'ReadClipboard', + Notify: 'Notify' }; export default SecurityModals;