diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
index eaa7160ae40..5f94d856b4d 100644
--- a/app/controllers/accounts_controller.rb
+++ b/app/controllers/accounts_controller.rb
@@ -986,6 +986,11 @@ class AccountsController < ApplicationController
MASKED_APP_CENTER_ACCESS_TOKEN: @account.settings[:app_center_access_token].try(:[], 0...5),
PERMISSIONS: {
:create_tool_manually => @account.grants_right?(@current_user, session, :create_tool_manually),
+ },
+ CSP: {
+ :enabled => @account.csp_enabled?,
+ :inherited => @account.csp_inherited?,
+ :settings_locked => @account.csp_locked?,
}
})
js_env(edit_help_links_env, true)
diff --git a/app/jsx/account_settings/__tests__/index.test.js b/app/jsx/account_settings/__tests__/index.test.js
index 09b7eb6ea23..580221845dc 100644
--- a/app/jsx/account_settings/__tests__/index.test.js
+++ b/app/jsx/account_settings/__tests__/index.test.js
@@ -27,10 +27,17 @@ describe('start', () => {
const fixtures = document.createElement('div')
fixtures.setAttribute('id', 'fixtures')
document.body.appendChild(fixtures)
+
+ const fakeAxios = {
+ put: jest.fn(() => ({then() {}})),
+ get: jest.fn(() => ({then() {}}))
+ }
+
expect(() => {
start(fixtures, {
context: 'account',
- contextId: '1'
+ contextId: '1',
+ api: fakeAxios
})
}).not.toThrowError()
})
diff --git a/app/jsx/account_settings/actions.js b/app/jsx/account_settings/actions.js
index b5fdf78a862..51e639723d6 100644
--- a/app/jsx/account_settings/actions.js
+++ b/app/jsx/account_settings/actions.js
@@ -23,6 +23,11 @@ function pluralize(word) {
return word
}
+export const WHITELISTS_LOADED = 'WHITELISTS_LOADED'
+export function setWhitelistsLoaded(value) {
+ return {type: WHITELISTS_LOADED, payload: value}
+}
+
export const SET_DIRTY = 'SET_DIRTY'
export function setDirtyAction(value) {
if (typeof value !== 'boolean') {
@@ -211,6 +216,7 @@ export function getCurrentWhitelist(context, contextId) {
tools: response.data.tools_whitelist || {}
}
dispatch(addDomainBulkAction(addDomainMap))
+ dispatch(setWhitelistsLoaded(true))
})
}
diff --git a/app/jsx/account_settings/components/SecurityPanel.js b/app/jsx/account_settings/components/SecurityPanel.js
index 3bf06deda6c..d77b54aa73f 100644
--- a/app/jsx/account_settings/components/SecurityPanel.js
+++ b/app/jsx/account_settings/components/SecurityPanel.js
@@ -24,6 +24,7 @@ import Heading from '@instructure/ui-elements/lib/components/Heading'
import Text from '@instructure/ui-elements/lib/components/Text'
import View from '@instructure/ui-layout/lib/components/View'
import Checkbox from '@instructure/ui-forms/lib/components/Checkbox'
+import Spinner from '@instructure/ui-elements/lib/components/Spinner'
import Grid, {GridCol, GridRow} from '@instructure/ui-layout/lib/components/Grid'
import {
getCspEnabled,
@@ -47,7 +48,8 @@ export class SecurityPanel extends Component {
getCspInherited: func.isRequired,
setCspInherited: func.isRequired,
getCurrentWhitelist: func.isRequired,
- isSubAccount: bool
+ isSubAccount: bool,
+ whitelistsHaveLoaded: bool
}
static defaultProps = {
@@ -114,12 +116,18 @@ export class SecurityPanel extends Component {
-
+ {!this.props.whitelistsHaveLoaded ? (
+
+
+
+ ) : (
+
+ )}
@@ -132,7 +140,8 @@ function mapStateToProps(state, ownProps) {
return {
...ownProps,
cspEnabled: state.cspEnabled,
- cspInherited: state.cspInherited
+ cspInherited: state.cspInherited,
+ whitelistsHaveLoaded: state.whitelistsHaveLoaded
}
}
diff --git a/app/jsx/account_settings/components/Whitelist.js b/app/jsx/account_settings/components/Whitelist.js
index f8ceeb6abdd..76e808aa573 100644
--- a/app/jsx/account_settings/components/Whitelist.js
+++ b/app/jsx/account_settings/components/Whitelist.js
@@ -193,7 +193,7 @@ export class Whitelist extends Component {
{whitelistToShow.length <= 0 ? (
}
/>
diff --git a/app/jsx/account_settings/components/__tests__/utils.js b/app/jsx/account_settings/components/__tests__/utils.js
index 3e6bd07fd26..7f2548e0318 100644
--- a/app/jsx/account_settings/components/__tests__/utils.js
+++ b/app/jsx/account_settings/components/__tests__/utils.js
@@ -21,13 +21,20 @@ import {Provider} from 'react-redux'
import {render} from 'react-testing-library'
import {configStore} from '../../store'
+const fakeAxios = {
+ delete: jest.fn(() => ({then() {}})),
+ get: jest.fn(() => ({then() {}})),
+ post: jest.fn(() => ({then() {}})),
+ put: jest.fn(() => ({then() {}}))
+}
+
// This is modified from a version by Kent C. Dodds described here:
// https://github.com/kentcdodds/react-testing-library/blob/master/examples/__tests__/react-redux.js
export function renderWithRedux(
ui,
{
initialState,
- store = configStore(initialState, {
+ store = configStore(initialState, fakeAxios, {
disableLogger: true
})
} = {}
diff --git a/app/jsx/account_settings/index.js b/app/jsx/account_settings/index.js
index 26b3998d2f2..f0b999e383d 100644
--- a/app/jsx/account_settings/index.js
+++ b/app/jsx/account_settings/index.js
@@ -27,7 +27,12 @@ export const CONFIG = {
}
export function start(element, props = {}, state = defaultState) {
- const store = configStore(state)
+ const initialState = {...state}
+ if (props.initialCspSettings) {
+ initialState.cspEnabled = props.initialCspSettings.enabled
+ initialState.cspInherited = props.initialCspSettings.inherited
+ }
+ const store = configStore(initialState, props.api)
render(
diff --git a/app/jsx/account_settings/reducers.js b/app/jsx/account_settings/reducers.js
index 6745f674ec2..8296893b6ce 100644
--- a/app/jsx/account_settings/reducers.js
+++ b/app/jsx/account_settings/reducers.js
@@ -28,7 +28,8 @@ import {
SET_CSP_INHERITED,
SET_CSP_INHERITED_OPTIMISTIC,
SET_DIRTY,
- COPY_INHERITED_SUCCESS
+ COPY_INHERITED_SUCCESS,
+ WHITELISTS_LOADED
} from './actions'
export function cspEnabled(state = false, action) {
@@ -60,6 +61,15 @@ export function isDirty(state = false, action) {
}
}
+export function whitelistsHaveLoaded(state = false, action) {
+ switch (action.type) {
+ case WHITELISTS_LOADED:
+ return action.payload
+ default:
+ return state
+ }
+}
+
function getInheritedList(toolsWhiteList, effectiveWhitelist) {
const toolsKeys = Object.keys(toolsWhiteList)
return effectiveWhitelist.filter(domain => !toolsKeys.includes(domain))
@@ -128,5 +138,6 @@ export default combineReducers({
cspEnabled,
cspInherited,
isDirty,
- whitelistedDomains
+ whitelistedDomains,
+ whitelistsHaveLoaded
})
diff --git a/app/jsx/account_settings/store.js b/app/jsx/account_settings/store.js
index 9881bf4aff4..4e827a0c6c5 100644
--- a/app/jsx/account_settings/store.js
+++ b/app/jsx/account_settings/store.js
@@ -19,29 +19,21 @@
import {createStore, applyMiddleware} from 'redux'
import ReduxThunk from 'redux-thunk'
import rootReducer from './reducers'
-import axios from 'axios'
-import {setupCache} from 'axios-cache-adapter'
export const defaultState = {
cspEnabled: false,
cspInherited: false,
isDirty: false,
+ whitelistsHaveLoaded: false,
whitelistedDomains: {
account: [],
effective: [],
+ inherited: [],
tools: {}
}
}
-const cache = setupCache({
- maxAge: 0.5 * 60 * 1000 // Hold onto the data for 30 seconds
-})
-
-const api = axios.create({
- adapter: cache.adapter
-})
-
-export function configStore(initialState, options = {}) {
+export function configStore(initialState, api, options = {}) {
const middleware = [
ReduxThunk.withExtraArgument({axios: api}),
process.env.NODE_ENV !== 'production' &&
diff --git a/app/jsx/bundles/account_settings.js b/app/jsx/bundles/account_settings.js
index 68e3a65fafa..215ac55159f 100644
--- a/app/jsx/bundles/account_settings.js
+++ b/app/jsx/bundles/account_settings.js
@@ -18,8 +18,11 @@
import React from 'react'
import ReactDOM from 'react-dom'
+import I18n from 'i18n!account_settings_jsx_bundle'
import FeatureFlagAdminView from 'compiled/views/feature_flags/FeatureFlagAdminView'
import CustomHelpLinkSettings from '../custom_help_link_settings/CustomHelpLinkSettings'
+import Spinner from '@instructure/ui-elements/lib/components/Spinner'
+import View from '@instructure/ui-layout/lib/components/View'
import 'account_settings'
import 'compiled/bundles/modules/account_quota_settings'
@@ -40,3 +43,15 @@ if (document.getElementById('custom_help_link_settings')) {
)
}
+if (document.getElementById('tab-security')) {
+ ReactDOM.render(
+
+
+ , document.getElementById('tab-security'))
+}
+
diff --git a/public/javascripts/account_settings.js b/public/javascripts/account_settings.js
index 915bb2b9533..4b3dee98002 100644
--- a/public/javascripts/account_settings.js
+++ b/public/javascripts/account_settings.js
@@ -21,6 +21,8 @@ import I18n from 'i18n!account_settings'
import $ from 'jquery'
import htmlEscape from 'str/htmlEscape'
import RichContentEditor from 'jsx/shared/rce/RichContentEditor'
+import axios from 'axios'
+import {setupCache} from 'axios-cache-adapter'
import 'jqueryui/tabs'
import globalAnnouncements from './global_announcements'
import './jquery.ajaxJSON'
@@ -117,25 +119,53 @@ import './vendor/jquery.scrollTo'
globalAnnouncements.augmentView()
globalAnnouncements.bindDomEvents()
- $("#account_settings_tabs").tabs({
+ $('#account_settings_tabs')
+ .tabs({
beforeActivate: (event, ui) => {
if (ui.newTab.context.id === 'tab-security-link') {
- import('jsx/account_settings')
- .then(({start}) => {
- const splitContext = window.ENV.context_asset_string.split('_')
- start(document.getElementById('tab-security'), {
- context: splitContext[0],
- contextId: splitContext[1],
- isSubAccount: !ENV.ACCOUNT.root_account
- });
- }).catch(() => {
- // We really should never get here... but if we do... do something.
- const $message = $('').text('Security Tab failed to load')
- $('tab-security').append($message)
+ // Set up axios and send a prefetch request to get the data we need,
+ // this should make things appear to be much quicker once the bundle
+ // loads in.
+ const cache = setupCache({
+ maxAge: 0.5 * 60 * 1000, // Hold onto the data for 30 seconds
+ debug: true
})
+
+ const api = axios.create({
+ adapter: cache.adapter
+ })
+
+ const splitContext = window.ENV.context_asset_string.split('_')
+
+ api
+ .get(`/api/v1/${splitContext[0]}s/${splitContext[1]}/csp_settings`)
+ .then(() => {
+ // Bring in the actual bundle of files to use
+ import('jsx/account_settings')
+ .then(({ start }) => {
+ start(document.getElementById('tab-security'), {
+ context: splitContext[0],
+ contextId: splitContext[1],
+ isSubAccount: !ENV.ACCOUNT.root_account,
+ initialCspSettings: ENV.CSP,
+ api
+ })
+ })
+ .catch(() => {
+ // We really should never get here... but if we do... do something.
+ $('#tab-security').text(I18n.t('Security Tab failed to load'))
+ })
+ })
+ .catch(() => {
+ // We really should never get here... but if we do... do something.
+ $('#tab-security').text(I18n.t('Security Tab failed to load'))
+ })
}
}
- }).show();
+ })
+ .show()
+
+
$(".add_ip_filter_link").click(function(event) {
event.preventDefault();
var $filter = $(".ip_filter.blank:first").clone(true).removeClass('blank');