Skip to content

Commit

Permalink
Set iframe allowances
Browse files Browse the repository at this point in the history
Closes PLAT-2969

Test Plan:
- Verify that all LTI launches in Canvas now have the following
  allowances set on their iframes:
  "geolocation; microphone; camera; midi; encrypted-media"
- Regression test the tool installation UI

Change-Id: I568fce10b5827736d7d4413e52fdefeed1f645bc
Reviewed-on: https://gerrit.instructure.com/137011
Tested-by: Jenkins
Reviewed-by: Marc Alan Phillips <mphillips@instructure.com>
Reviewed-by: Andrew Butterfield <abutterfield@instructure.com>
QA-Review: August Thornton <august@instructure.com>
Product-Review: Weston Dransfield <wdransfield@instructure.com>
  • Loading branch information
westonkd authored and ktgeek committed Jan 24, 2018
1 parent 5eb4f1f commit fcae49e
Show file tree
Hide file tree
Showing 32 changed files with 396 additions and 177 deletions.
9 changes: 5 additions & 4 deletions app/coffeescripts/gradebook/PostGradesFrameDialog.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@

define [
'jquery'
'jst/PostGradesFrameDialog'
'jqueryui/dialog'
], ($, postGradesFrameDialog) ->
'jst/PostGradesFrameDialog',
'jsx/external_apps/lib/iframeAllowances'
'jqueryui/dialog',
], ($, postGradesFrameDialog, iframeAllowances) ->

class PostGradesFrameDialog
constructor: (options) ->
Expand All @@ -30,7 +31,7 @@ define [
@baseUrl = options.baseUrl

# init dialog
@$dialog = $(postGradesFrameDialog())
@$dialog = $(postGradesFrameDialog({allowances: iframeAllowances()}))
@$iframe = @$dialog.find('.post-grades-frame')
@$dialog.on('dialogopen', @onDialogOpen)
@$dialog.on('dialogclose', @onDialogClose)
Expand Down
5 changes: 3 additions & 2 deletions app/coffeescripts/gradezilla/PostGradesFrameDialog.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
define [
'jquery'
'jst/PostGradesFrameDialog'
'jsx/external_apps/lib/iframeAllowances'
'jqueryui/dialog'
], ($, postGradesFrameDialog) ->
], ($, postGradesFrameDialog, iframeAllowances) ->

class PostGradesFrameDialog
constructor: (options) ->
Expand All @@ -30,7 +31,7 @@ define [
@baseUrl = options.baseUrl

# init dialog
@$dialog = $(postGradesFrameDialog())
@$dialog = $(postGradesFrameDialog({allowances: iframeAllowances()}))
@$iframe = @$dialog.find('.post-grades-frame')
@$dialog.on('dialogopen', @onDialogOpen)
@$dialog.on('dialogclose', @onDialogClose)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
define [
'jquery',
'Backbone',
'jst/ExternalTools/ExternalContentReturnView'
], ($, Backbone, template) ->
'jst/ExternalTools/ExternalContentReturnView',
'jsx/external_apps/lib/iframeAllowances'
], ($, Backbone, template, iframeAllowances) ->

class ExternalContentReturnView extends Backbone.View
template: template
Expand Down Expand Up @@ -52,6 +53,7 @@ define [

toJSON: ->
json = super
json.allowances = iframeAllowances()
json.launch_url = @model.launchUrl(@launchType, @launchParams)
json

Expand Down
11 changes: 8 additions & 3 deletions app/coffeescripts/views/collaborations/CollaborationView.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ define [
'Backbone'
'./CollaboratorPickerView'
'jst/collaborations/edit'
'jst/collaborations/EditIframe'
], (I18n, $, {extend}, {View}, CollaboratorPickerView, editForm, editIframe) ->
'jst/collaborations/EditIframe',
'jsx/external_apps/lib/iframeAllowances'
], (I18n, $, {extend}, {View}, CollaboratorPickerView, editForm, editIframe, iframeAllowances) ->

class CollaborationView extends View
events:
Expand Down Expand Up @@ -67,7 +68,11 @@ define [
@onCloseForm(e)

iframeTemplate: ({url}) ->
$iframe = $(editIframe({id: @id, url: url}))
$iframe = $(editIframe({
id: @id,
url: url,
allowances: iframeAllowances()
}))
$iframe.on 'keydown', (e) =>
if e.which == 27
e.preventDefault()
Expand Down
1 change: 1 addition & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ def js_env(hash = {})
help_link_name: help_link_name,
help_link_icon: help_link_icon,
use_high_contrast: @current_user.try(:prefers_high_contrast?),
LTI_LAUNCH_FRAME_ALLOWANCES: Lti::Launch::FRAME_ALLOWANCES,
SETTINGS: {
open_registration: @domain_root_account.try(:open_registration?),
eportfolios_enabled: (@domain_root_account && @domain_root_account.settings[:enable_eportfolios] != false), # checking all user root accounts is slow
Expand Down
79 changes: 41 additions & 38 deletions app/jsx/assignments/AssignmentConfigurationTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import PropTypes from 'prop-types'
import ReactDOM from 'react-dom'
import I18n from 'i18n!moderated_grading'
import 'compiled/jquery.rails_flash_notifications'
import iframeAllowances from '../external_apps/lib/iframeAllowances'
import OriginalityReportVisibilityPicker from './OriginalityReportVisibilityPicker'

const AssignmentConfigurationTools = React.createClass({
Expand All @@ -41,11 +42,15 @@ import OriginalityReportVisibilityPicker from './OriginalityReportVisibilityPick

componentDidMount() {
this.setToolLaunchUrl();

if (this.iframe) {
this.iframe.setAttribute('allow', iframeAllowances());
}
},

getInitialState() {
return {
toolLaunchUrl: 'none',
toolLaunchUrl: 'about:blank',
toolType: '',
tools: [],
selectedToolValue: `${this.props.selectedToolType}_${this.props.selectedTool}`,
Expand Down Expand Up @@ -82,7 +87,7 @@ import OriginalityReportVisibilityPicker from './OriginalityReportVisibilityPick
this.setState({
tools: data,
toolType: this.props.selectedToolType,
toolLaunchUrl: prevToolLaunch || 'none'
toolLaunchUrl: prevToolLaunch || 'about:blank'
});
}, self),
error: function(xhr) {
Expand Down Expand Up @@ -160,7 +165,7 @@ import OriginalityReportVisibilityPicker from './OriginalityReportVisibilityPick
ref={(c) => { this.similarityDetectionTool = c; }}
value={this.state.selectedToolValue}
>
<option title="Plagiarism Review Tool" data-launch="none" data-type="none">
<option title="Plagiarism Review Tool" data-launch="about:blank" data-type="none">
None
</option>
{
Expand Down Expand Up @@ -192,46 +197,44 @@ import OriginalityReportVisibilityPicker from './OriginalityReportVisibilityPick
},

renderConfigTool() {
if (this.state.toolLaunchUrl !== 'none') {
const beforeAlertStyles = `before_external_content_info_alert ${this.state.beforeExternalContentAlertClass}`
const afterAlertStyles = `after_external_content_info_alert ${this.state.afterExternalContentAlertClass}`
return(
<div>
<div
onFocus={this.handleAlertFocus}
onBlur={this.handleAlertBlur}
className={beforeAlertStyles}
tabIndex="0"
>
<div className="ic-flash-info" style={{ width: 'auto', margin: '20px' }}>
<div className="ic-flash__icon" aria-hidden="true">
<i className="icon-info" />
</div>
{I18n.t('The following content is partner provided')}
const beforeAlertStyles = `before_external_content_info_alert ${this.state.beforeExternalContentAlertClass}`
const afterAlertStyles = `after_external_content_info_alert ${this.state.afterExternalContentAlertClass}`
return(
<div style={{ display: this.state.toolLaunchUrl === 'about:blank' ? 'none' : 'block' }}>
<div
onFocus={this.handleAlertFocus}
onBlur={this.handleAlertBlur}
className={beforeAlertStyles}
tabIndex="0"
>
<div className="ic-flash-info" style={{ width: 'auto', margin: '20px' }}>
<div className="ic-flash__icon" aria-hidden="true">
<i className="icon-info" />
</div>
{I18n.t('The following content is partner provided')}
</div>
<iframe
src={this.state.toolLaunchUrl}
className="tool_launch"
style={this.state.iframeStyle}
ref={(e) => { this.iframe = e; }}
/>
<div
onFocus={this.handleAlertFocus}
onBlur={this.handleAlertBlur}
className={afterAlertStyles}
tabIndex="0"
>
<div className="ic-flash-info" style={{ width: 'auto', margin: '20px' }}>
<div className="ic-flash__icon" aria-hidden="true">
<i className="icon-info" />
</div>
{I18n.t('The preceding content is partner provided')}
</div>
<iframe
src={this.state.toolLaunchUrl}
className="tool_launch"
style={this.state.iframeStyle}
ref={(e) => { this.iframe = e; }}
/>
<div
onFocus={this.handleAlertFocus}
onBlur={this.handleAlertBlur}
className={afterAlertStyles}
tabIndex="0"
>
<div className="ic-flash-info" style={{ width: 'auto', margin: '20px' }}>
<div className="ic-flash__icon" aria-hidden="true">
<i className="icon-info" />
</div>
{I18n.t('The preceding content is partner provided')}
</div>
</div>
);
}
</div>
);
},

render() {
Expand Down
5 changes: 5 additions & 0 deletions app/jsx/collaborations/CollaborationsToolLaunch.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import React from 'react'
import I18n from 'i18n!collaborations'
import iframeAllowances from '../external_apps/lib/iframeAllowances'

let main

Expand All @@ -40,6 +41,10 @@ class CollaborationsToolLaunch extends React.Component {
componentDidMount () {
this.setHeight()
window.addEventListener('resize', this.setHeight)

if (this.iframe) {
this.iframe.setAttribute('allow', iframeAllowances());
}
}

componentWillUnmount () {
Expand Down
80 changes: 42 additions & 38 deletions app/jsx/external_apps/components/ConfigureExternalToolButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import I18n from 'i18n!external_tools'
import React from 'react'
import { shape } from 'prop-types'
import Modal from 'react-modal'
import iframeAllowances from '../lib/iframeAllowances'

const modalOverrides = {
overlay: {
Expand Down Expand Up @@ -97,49 +98,51 @@ export default React.createClass({
},

renderIframe () {
if (this.state.modalIsOpen) {
const beforeAlertStyles = `before_external_content_info_alert ${this.state.beforeExternalContentAlertClass}`
const afterAlertStyles = `after_external_content_info_alert ${this.state.afterExternalContentAlertClass}`

return (
<div>
<div
onFocus={this.handleAlertFocus}
onBlur={this.handleAlertBlur}
className={beforeAlertStyles}
tabIndex="0"
>
<div className="ic-flash-info">
<div className="ic-flash__icon" aria-hidden="true">
<i className="icon-info" />
</div>
{I18n.t('The following content is partner provided')}
const beforeAlertStyles = `before_external_content_info_alert ${this.state.beforeExternalContentAlertClass}`
const afterAlertStyles = `after_external_content_info_alert ${this.state.afterExternalContentAlertClass}`

return (
<div>
<div
onFocus={this.handleAlertFocus}
onBlur={this.handleAlertBlur}
className={beforeAlertStyles}
tabIndex="0"
>
<div className="ic-flash-info">
<div className="ic-flash__icon" aria-hidden="true">
<i className="icon-info" />
</div>
{I18n.t('The following content is partner provided')}
</div>
<iframe
src={this.getLaunchUrl()}
title={I18n.t('Tool Configuration')}
className="tool_launch"
style={this.state.iframeStyle}
ref={(e) => { this.iframe = e; }}
/>
<div
onFocus={this.handleAlertFocus}
onBlur={this.handleAlertBlur}
className={afterAlertStyles}
tabIndex="0"
>
<div className="ic-flash-info">
<div className="ic-flash__icon" aria-hidden="true">
<i className="icon-info" />
</div>
{I18n.t('The preceding content is partner provided')}
</div>
<iframe
src={this.getLaunchUrl()}
title={I18n.t('Tool Configuration')}
className="tool_launch"
style={this.state.iframeStyle}
ref={(e) => { this.iframe = e; }}
/>
<div
onFocus={this.handleAlertFocus}
onBlur={this.handleAlertBlur}
className={afterAlertStyles}
tabIndex="0"
>
<div className="ic-flash-info">
<div className="ic-flash__icon" aria-hidden="true">
<i className="icon-info" />
</div>
{I18n.t('The preceding content is partner provided')}
</div>
</div>
);
} else {
return null;
</div>
);
},

onAfterOpen () {
if (this.iframe) {
this.iframe.setAttribute('allow', iframeAllowances());
}
},

Expand All @@ -163,6 +166,7 @@ export default React.createClass({
style={modalOverrides}
isOpen={this.state.modalIsOpen}
onRequestClose={this.closeModal}
onAfterOpen={this.onAfterOpen}
>
<div className="ReactModal__Layout">
<div className="ReactModal__Header">
Expand Down
5 changes: 5 additions & 0 deletions app/jsx/external_apps/components/Lti2Iframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import I18n from 'i18n!external_tools'
import $ from 'jquery'
import React from 'react'
import PropTypes from 'prop-types'
import iframeAllowances from '../lib/iframeAllowances'

export default React.createClass({
displayName: 'Lti2Iframe',
Expand Down Expand Up @@ -49,6 +50,10 @@ export default React.createClass({
this.props.handleInstall(message, e);
}
}.bind(this), false);

if (this.iframe) {
this.iframe.setAttribute('allow', iframeAllowances());
}
},

getLaunchUrl () {
Expand Down
24 changes: 24 additions & 0 deletions app/jsx/external_apps/lib/iframeAllowances.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2018 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/

const iframeAllowances = () => {
const allowances = ENV.LTI_LAUNCH_FRAME_ALLOWANCES || []
return allowances.join('; ')
}

export default iframeAllowances

0 comments on commit fcae49e

Please sign in to comment.