From cfd091b97fe33a2de39079c321918bd2c7ee183c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Tue, 28 Jan 2020 20:23:25 -0500 Subject: [PATCH 1/8] Add standard dash interface for location change (#1094) --- @plotly/dash-component-plugins/package.json | 2 +- @plotly/dash-component-plugins/src/History.js | 13 +++++++++++++ @plotly/dash-component-plugins/src/index.js | 4 +++- 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 @plotly/dash-component-plugins/src/History.js diff --git a/@plotly/dash-component-plugins/package.json b/@plotly/dash-component-plugins/package.json index f7324754c4..0d51938be5 100644 --- a/@plotly/dash-component-plugins/package.json +++ b/@plotly/dash-component-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@plotly/dash-component-plugins", - "version": "1.0.2", + "version": "1.1.0", "description": "Plugins for Dash Components", "repository": { "type": "git", diff --git a/@plotly/dash-component-plugins/src/History.js b/@plotly/dash-component-plugins/src/History.js new file mode 100644 index 0000000000..7b34bc0e14 --- /dev/null +++ b/@plotly/dash-component-plugins/src/History.js @@ -0,0 +1,13 @@ +const ON_CHANGE = '_dashprivate_historychange'; + +export default class History { + static dispatchChangeEvent() { + window.dispatchEvent(new CustomEvent(ON_CHANGE)); + } + + static onChange(listener) { + window.addEventListener(ON_CHANGE, listener); + + return () => window.removeEventListener(ON_CHANGE, listener); + } +} \ No newline at end of file diff --git a/@plotly/dash-component-plugins/src/index.js b/@plotly/dash-component-plugins/src/index.js index 4a670ca381..4cff6b60a1 100644 --- a/@plotly/dash-component-plugins/src/index.js +++ b/@plotly/dash-component-plugins/src/index.js @@ -1,3 +1,5 @@ import { asyncDecorator, isReady } from './dynamicImport'; +import History from './History'; -export { asyncDecorator, isReady }; \ No newline at end of file +export { asyncDecorator, isReady }; +export { History }; \ No newline at end of file From ca58cc4cbedbae8c38386e930f8f42047012ca2b Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 29 Jan 2020 13:03:47 -0800 Subject: [PATCH 2/8] feat: disable cookie access under restricted sandboxes (#1080) --- CHANGELOG.md | 5 ++ dash-renderer/src/AccessDenied.react.js | 16 +++++-- dash-renderer/src/actions/index.js | 15 ++++-- tests/integration/renderer/test_iframe.py | 57 +++++++++++++++++++++++ 4 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 tests/integration/renderer/test_iframe.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b7e4a3c85..8e055f7c72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] +### Fixed +- [#1080](https://github.com/plotly/dash/pull/1080) Handle case where dash fails to load when used inside an iframe with a sandbox attribute that only has allow-scripts + + ## [1.8.0] - 2020-01-14 ### Added - [#1073](https://github.com/plotly/dash/pull/1073) Two new functions to simplify usage handling URLs and pathnames: `app.get_relative_path` & `app.trim_relative_path`. diff --git a/dash-renderer/src/AccessDenied.react.js b/dash-renderer/src/AccessDenied.react.js index 6dd7ab2808..f70625548a 100644 --- a/dash-renderer/src/AccessDenied.react.js +++ b/dash-renderer/src/AccessDenied.react.js @@ -1,10 +1,13 @@ /* global window:true, document:true */ import React from 'react'; -import {mergeRight} from 'ramda'; +import {mergeRight, once} from 'ramda'; import PropTypes from 'prop-types'; import * as styles from './styles/styles.js'; import * as constants from './constants/constants.js'; +/* eslint-disable-next-line no-console */ +const logWarningOnce = once(console.warn); + function AccessDenied(props) { const {config} = props; const fid = config.fid; @@ -28,9 +31,14 @@ function AccessDenied(props) { { - document.cookie = - `${constants.OAUTH_COOKIE_NAME}=; ` + - 'expires=Thu, 01 Jan 1970 00:00:01 GMT;'; + /* eslint no-empty: ["error", { "allowEmptyCatch": true }] */ + try { + document.cookie = + `${constants.OAUTH_COOKIE_NAME}=; ` + + 'expires=Thu, 01 Jan 1970 00:00:01 GMT;'; + } catch (e) { + logWarningOnce(e); + } window.location.reload(true); }} > diff --git a/dash-renderer/src/actions/index.js b/dash-renderer/src/actions/index.js index c6c9b6c772..fc582775a7 100644 --- a/dash-renderer/src/actions/index.js +++ b/dash-renderer/src/actions/index.js @@ -16,6 +16,7 @@ import { lensPath, mergeLeft, mergeDeepRight, + once, path, pluck, propEq, @@ -54,10 +55,18 @@ export function hydrateInitialOutputs() { }; } +/* eslint-disable-next-line no-console */ +const logWarningOnce = once(console.warn); + export function getCSRFHeader() { - return { - 'X-CSRFToken': cookie.parse(document.cookie)._csrf_token, - }; + try { + return { + 'X-CSRFToken': cookie.parse(document.cookie)._csrf_token, + }; + } catch (e) { + logWarningOnce(e); + return {}; + } } function triggerDefaultState(dispatch, getState) { diff --git a/tests/integration/renderer/test_iframe.py b/tests/integration/renderer/test_iframe.py new file mode 100644 index 0000000000..6a4a93f52c --- /dev/null +++ b/tests/integration/renderer/test_iframe.py @@ -0,0 +1,57 @@ +from multiprocessing import Value + +import dash +from dash.dependencies import Input, Output +from dash.exceptions import PreventUpdate + +import dash_html_components as html + + +def test_rdif001_sandbox_allow_scripts(dash_duo): + app = dash.Dash(__name__) + call_count = Value("i") + + N_OUTPUTS = 50 + + app.layout = html.Div([ + html.Button("click me", id="btn"), + ] + [html.Div(id="output-{}".format(i)) for i in range(N_OUTPUTS)]) + + @app.callback( + [Output("output-{}".format(i), "children") for i in range(N_OUTPUTS)], + [Input("btn", "n_clicks")] + ) + def update_output(n_clicks): + if n_clicks is None: + raise PreventUpdate + + call_count.value += 1 + return ["{}={}".format(i, i + n_clicks) for i in range(N_OUTPUTS)] + + @app.server.after_request + def apply_cors(response): + response.headers["Access-Control-Allow-Origin"] = "*" + response.headers["Access-Control-Allow-Headers"] = "Origin, X-Requested-With, Content-Type, Accept, Authorization" + return response + + dash_duo.start_server(app) + + iframe = """ + + + + + """ + + html_content = iframe.format(dash_duo.server_url) + + dash_duo.driver.get("data:text/html;charset=utf-8," + html_content) + + dash_duo.driver.switch_to.frame(0) + + dash_duo.wait_for_element('#output-0') + dash_duo.wait_for_element_by_id('btn').click() + dash_duo.wait_for_element('#output-0').text == '0=1' + + assert len(dash_duo.get_logs()) != 0 From ece04d57ccbb3b12cefdfb760dd3fd6071155c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Thu, 30 Jan 2020 13:27:01 -0500 Subject: [PATCH 3/8] IE11 compatibility (#1106) --- @plotly/webpack-dash-dynamic-import/package.json | 2 +- @plotly/webpack-dash-dynamic-import/src/index.js | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/@plotly/webpack-dash-dynamic-import/package.json b/@plotly/webpack-dash-dynamic-import/package.json index 824fd5de02..b2504683b2 100644 --- a/@plotly/webpack-dash-dynamic-import/package.json +++ b/@plotly/webpack-dash-dynamic-import/package.json @@ -1,6 +1,6 @@ { "name": "@plotly/webpack-dash-dynamic-import", - "version": "1.1.4", + "version": "1.1.5", "description": "Webpack Plugin for Dynamic Import in Dash", "repository": { "type": "git", diff --git a/@plotly/webpack-dash-dynamic-import/src/index.js b/@plotly/webpack-dash-dynamic-import/src/index.js index 816c6d278f..ff7a14dad1 100644 --- a/@plotly/webpack-dash-dynamic-import/src/index.js +++ b/@plotly/webpack-dash-dynamic-import/src/index.js @@ -20,7 +20,15 @@ var getCurrentScript = function() { if (!script) { /* Shim for IE11 and below */ /* Do not take into account async scripts and inline scripts */ - var scripts = Array.from(document.getElementsByTagName('script')).filter(function(s) { return !s.async && !s.text && !s.textContent; }); + + var doc_scripts = document.getElementsByTagName('script'); + var scripts = []; + + for (var i = 0; i < doc_scripts.length; i++) { + scripts.push(doc_scripts[i]); + } + + scripts = scripts.filter(function(s) { return !s.async && !s.text && !s.textContent; }); script = scripts.slice(-1)[0]; } From 2bbb83860e592d3616844d48e199d527227fb9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Mon, 3 Feb 2020 21:09:08 -0500 Subject: [PATCH 4/8] Add `inheritAsyncDecorator` for wrapper components (#1109) --- @plotly/dash-component-plugins/package.json | 2 +- @plotly/dash-component-plugins/src/dynamicImport.js | 6 ++++++ @plotly/dash-component-plugins/src/index.js | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/@plotly/dash-component-plugins/package.json b/@plotly/dash-component-plugins/package.json index 0d51938be5..eaf967ced9 100644 --- a/@plotly/dash-component-plugins/package.json +++ b/@plotly/dash-component-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@plotly/dash-component-plugins", - "version": "1.1.0", + "version": "1.2.0", "description": "Plugins for Dash Components", "repository": { "type": "git", diff --git a/@plotly/dash-component-plugins/src/dynamicImport.js b/@plotly/dash-component-plugins/src/dynamicImport.js index 29530bc9cf..c4ed18b121 100644 --- a/@plotly/dash-component-plugins/src/dynamicImport.js +++ b/@plotly/dash-component-plugins/src/dynamicImport.js @@ -27,5 +27,11 @@ export const asyncDecorator = (target, promise) => { return state.get; }; +export const inheritAsyncDecorator = (target, source) => { + Object.defineProperty(target, '_dashprivate_isLazyComponentReady', { + get: () => isReady(source) + }); +} + export const isReady = target => target && target._dashprivate_isLazyComponentReady; diff --git a/@plotly/dash-component-plugins/src/index.js b/@plotly/dash-component-plugins/src/index.js index 4cff6b60a1..3448f9e7b8 100644 --- a/@plotly/dash-component-plugins/src/index.js +++ b/@plotly/dash-component-plugins/src/index.js @@ -1,5 +1,5 @@ -import { asyncDecorator, isReady } from './dynamicImport'; +import { asyncDecorator, inheritAsyncDecorator, isReady } from './dynamicImport'; import History from './History'; -export { asyncDecorator, isReady }; +export { asyncDecorator, inheritAsyncDecorator, isReady }; export { History }; \ No newline at end of file From 2868c03a0a4b477bceb69335d800144668e26dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Tue, 4 Feb 2020 11:25:06 -0500 Subject: [PATCH 5/8] bump version, loosen version requirements --- CHANGELOG.md | 3 +-- dash-renderer/package.json | 2 +- dash/version.py | 2 +- requires-install.txt | 6 +++--- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e055f7c72..6028f4751c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,10 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased] +## [1.9.0] - 2020-02-04 ### Fixed - [#1080](https://github.com/plotly/dash/pull/1080) Handle case where dash fails to load when used inside an iframe with a sandbox attribute that only has allow-scripts - ## [1.8.0] - 2020-01-14 ### Added - [#1073](https://github.com/plotly/dash/pull/1073) Two new functions to simplify usage handling URLs and pathnames: `app.get_relative_path` & `app.trim_relative_path`. diff --git a/dash-renderer/package.json b/dash-renderer/package.json index f993949f20..6c5d279b54 100644 --- a/dash-renderer/package.json +++ b/dash-renderer/package.json @@ -1,6 +1,6 @@ { "name": "dash-renderer", - "version": "1.2.3", + "version": "1.2.4", "description": "render dash components in react", "main": "dash_renderer/dash_renderer.min.js", "scripts": { diff --git a/dash/version.py b/dash/version.py index b280975791..e5102d3015 100644 --- a/dash/version.py +++ b/dash/version.py @@ -1 +1 @@ -__version__ = '1.8.0' +__version__ = '1.9.0' diff --git a/requires-install.txt b/requires-install.txt index 291eeb1fd2..c5bfe223ca 100644 --- a/requires-install.txt +++ b/requires-install.txt @@ -1,8 +1,8 @@ Flask>=1.0.2 flask-compress plotly -dash_renderer>=1.2.2 -dash-core-components>=1.6.0 +dash_renderer>=1.2.3 +dash-core-components>=1.7.1 dash-html-components==1.0.2 -dash-table>=4.5.1 +dash-table>=4.6.0 future \ No newline at end of file From 09cea47b6e256c421c6b14dab088551f303e74cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Tue, 4 Feb 2020 11:55:42 -0500 Subject: [PATCH 6/8] dcc>=1.7.0, 1.7.1 was JS only --- requires-install.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requires-install.txt b/requires-install.txt index c5bfe223ca..111655a32b 100644 --- a/requires-install.txt +++ b/requires-install.txt @@ -2,7 +2,7 @@ Flask>=1.0.2 flask-compress plotly dash_renderer>=1.2.3 -dash-core-components>=1.7.1 +dash-core-components>=1.7.0 dash-html-components==1.0.2 dash-table>=4.6.0 future \ No newline at end of file From 7c6376d1b4ef66a409b178d160bdbf1db8c2c98b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Tue, 4 Feb 2020 12:32:07 -0500 Subject: [PATCH 7/8] stricten versions --- requires-install.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requires-install.txt b/requires-install.txt index 111655a32b..c5fc38b7fc 100644 --- a/requires-install.txt +++ b/requires-install.txt @@ -1,8 +1,8 @@ Flask>=1.0.2 flask-compress plotly -dash_renderer>=1.2.3 -dash-core-components>=1.7.0 +dash_renderer==1.2.4 +dash-core-components==1.8.0 dash-html-components==1.0.2 dash-table>=4.6.0 future \ No newline at end of file From 8ee358826cd4318a3edc52fbf71e0bdda2369984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Tue, 4 Feb 2020 12:37:13 -0500 Subject: [PATCH 8/8] stricten table version --- requires-install.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requires-install.txt b/requires-install.txt index c5fc38b7fc..fe1a4fcf66 100644 --- a/requires-install.txt +++ b/requires-install.txt @@ -4,5 +4,5 @@ plotly dash_renderer==1.2.4 dash-core-components==1.8.0 dash-html-components==1.0.2 -dash-table>=4.6.0 +dash-table==4.6.0 future \ No newline at end of file