From e32f5c0a1032e12831b2cebea74d82b268e289c4 Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Wed, 1 Mar 2017 09:21:57 +0100 Subject: [PATCH 1/8] Higher-order component to force some routes to HTTPS --- client/forceProtocol.jsx | 25 +++++++++++++++++++++++++ client/routes.jsx | 7 ++++++- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 client/forceProtocol.jsx diff --git a/client/forceProtocol.jsx b/client/forceProtocol.jsx new file mode 100644 index 0000000000..9f1f95b0ad --- /dev/null +++ b/client/forceProtocol.jsx @@ -0,0 +1,25 @@ +import React, { PropTypes } from 'react'; + +/** + * A Higher Order Component that forces the protocol to change on mount + * + */ +const forceProtocol = ({ targetProtocol = 'https:' }) => WrappedComponent => ( + class ForceProtocol extends React.Component { + static propTypes = {} + + componentDidMount() { + const currentProtocol = window.location.protocol; + + if (targetProtocol !== currentProtocol) { + window.location = window.location.href.replace(currentProtocol, targetProtocol); + } + } + render() { + return ; + } + } +); + + +export default forceProtocol; diff --git a/client/routes.jsx b/client/routes.jsx index 5015917511..9526e329aa 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -10,6 +10,11 @@ import NewPasswordView from './modules/User/pages/NewPasswordView'; // import SketchListView from './modules/Sketch/pages/SketchListView'; import { getUser } from './modules/User/actions'; +// TODO: Move somewhere else +import forceProtocol from './forceProtocol'; + +const forceToHttps = forceProtocol({ targetProtocol: 'https:' }); + const checkAuth = (store) => { store.dispatch(getUser()); }; @@ -18,7 +23,7 @@ const routes = store => ( - + From cfe53e81a3646016a907628b07a4f2e65b96aa2a Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Wed, 8 Mar 2017 23:13:09 +0100 Subject: [PATCH 2/8] Force all user-management routes to HTTPS --- client/routes.jsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/client/routes.jsx b/client/routes.jsx index 9526e329aa..529ca2113b 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -24,9 +24,12 @@ const routes = store => - - - + + + From 3dcebda1a2a4f364defa18f4381eb5da19245d10 Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Thu, 9 Mar 2017 00:08:13 +0100 Subject: [PATCH 3/8] Redirect to sourceProtocol as route unmounts. By default, no redirection occurs if sourceProtocol is not explicitly defined. --- client/forceProtocol.jsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/client/forceProtocol.jsx b/client/forceProtocol.jsx index 9f1f95b0ad..d722392890 100644 --- a/client/forceProtocol.jsx +++ b/client/forceProtocol.jsx @@ -4,17 +4,28 @@ import React, { PropTypes } from 'react'; * A Higher Order Component that forces the protocol to change on mount * */ -const forceProtocol = ({ targetProtocol = 'https:' }) => WrappedComponent => ( +const forceProtocol = ({ targetProtocol = 'https:', sourceProtocol }) => WrappedComponent => ( class ForceProtocol extends React.Component { static propTypes = {} componentDidMount() { + this.redirectToProtocol(targetProtocol); + } + + componentWillUnmount() { + if (sourceProtocol != null) { + this.redirectToProtocol(sourceProtocol); + } + } + + redirectToProtocol(protocol) { const currentProtocol = window.location.protocol; - if (targetProtocol !== currentProtocol) { - window.location = window.location.href.replace(currentProtocol, targetProtocol); + if (protocol !== currentProtocol) { + window.location = window.location.href.replace(currentProtocol, protocol); } } + render() { return ; } From 1b07b9533305f7873c1173627b8d456de2ed04e2 Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Thu, 9 Mar 2017 00:38:37 +0100 Subject: [PATCH 4/8] Sets serveSecure flag on new projects and usea after forcing protocol The flag is set to `false` on all projects and as the UI has no way to change this, it always redirects to HTTP after a signup/login action. --- client/modules/IDE/reducers/project.js | 3 ++- client/routes.jsx | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/client/modules/IDE/reducers/project.js b/client/modules/IDE/reducers/project.js index e09e72ced5..59f847b31b 100644 --- a/client/modules/IDE/reducers/project.js +++ b/client/modules/IDE/reducers/project.js @@ -5,7 +5,8 @@ const initialState = () => { const generatedString = generate({ words: 2 }).spaced; const generatedName = generatedString.charAt(0).toUpperCase() + generatedString.slice(1); return { - name: generatedName + name: generatedName, + serveSecure: false, }; }; diff --git a/client/routes.jsx b/client/routes.jsx index 529ca2113b..dbd3c9f934 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -13,14 +13,21 @@ import { getUser } from './modules/User/actions'; // TODO: Move somewhere else import forceProtocol from './forceProtocol'; -const forceToHttps = forceProtocol({ targetProtocol: 'https:' }); - const checkAuth = (store) => { store.dispatch(getUser()); }; -const routes = store => - ( +const routes = (store) => { + const sourceProtocol = store.getState().project.serveSecure === true ? + 'https:' : + 'http:'; + + const forceToHttps = forceProtocol({ + targetProtocol: 'https:', + sourceProtocol, + }); + + return ( @@ -38,5 +45,6 @@ const routes = store => ); +}; export default routes; From 0dcef53e34d9866a13639ae96ce2f5cc7eeb3cb9 Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Sun, 12 Mar 2017 23:17:40 +0100 Subject: [PATCH 5/8] Move HoC to be with other top-level components --- client/{ => components}/forceProtocol.jsx | 0 client/routes.jsx | 4 +--- 2 files changed, 1 insertion(+), 3 deletions(-) rename client/{ => components}/forceProtocol.jsx (100%) diff --git a/client/forceProtocol.jsx b/client/components/forceProtocol.jsx similarity index 100% rename from client/forceProtocol.jsx rename to client/components/forceProtocol.jsx diff --git a/client/routes.jsx b/client/routes.jsx index dbd3c9f934..4d87ea418b 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -1,5 +1,6 @@ import { Route, IndexRoute } from 'react-router'; import React from 'react'; +import forceProtocol from './components/forceProtocol'; import App from './modules/App/App'; import IDEView from './modules/IDE/pages/IDEView'; import FullView from './modules/IDE/pages/FullView'; @@ -10,9 +11,6 @@ import NewPasswordView from './modules/User/pages/NewPasswordView'; // import SketchListView from './modules/Sketch/pages/SketchListView'; import { getUser } from './modules/User/actions'; -// TODO: Move somewhere else -import forceProtocol from './forceProtocol'; - const checkAuth = (store) => { store.dispatch(getUser()); }; From 196edbcf61ceba477d6125725da26ff14eec816f Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Sun, 26 Mar 2017 22:50:02 +0200 Subject: [PATCH 6/8] Server should respond to account page request --- server/routes/server.routes.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/routes/server.routes.js b/server/routes/server.routes.js index 000eb7bc48..1b69dbda35 100644 --- a/server/routes/server.routes.js +++ b/server/routes/server.routes.js @@ -54,4 +54,10 @@ router.route('/:username/sketches').get((req, res) => { )); }); +router.route('/:username/account').get((req, res) => { + userExists(req.params.username, exists => ( + exists ? res.send(renderIndex()) : get404Sketch(html => res.send(html)) + )); +}); + export default router; From 5b621c0b40c15efa585b9fda2d2c47174cb381a9 Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Sun, 26 Mar 2017 22:57:47 +0200 Subject: [PATCH 7/8] Serves AccountView over HTTPS --- client/routes.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/routes.jsx b/client/routes.jsx index 38d03c1d2c..d1d8526087 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -41,7 +41,7 @@ const routes = (store) => { - + ); From a3cc21105e52e59f1853f6d67ccc5c635b3b69d4 Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Sun, 26 Mar 2017 23:28:33 +0200 Subject: [PATCH 8/8] Turns HTTPS redirection off in development by default Will log to the browser console any redirection that would have happened. Added a line in the README about how to enable this for testing in development. --- README.md | 4 +++- client/components/forceProtocol.jsx | 12 ++++++++++-- client/routes.jsx | 3 +++ webpack.config.dev.js | 3 +++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 127ef75702..c33fcf7c5e 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,9 @@ This project is currently in development! It will be announced when there is a ( 9. Open and close the Redux DevTools using `ctrl+h`, and move them with `ctrl+w` ###Testing SSL on your local machine -Please refer to [this gist](https://gist.github.com/andrewn/953ffd5cb17ac2634dc969fc7bdaff3f). This allows you to access the editor using both HTTP and HTTPS. Don't worry about this unless you need to make changes or test HTTPS behavior. +Please refer to [this gist](https://gist.github.com/andrewn/953ffd5cb17ac2634dc969fc7bdaff3f). This allows you to access the editor using both HTTP and HTTPS. Don't worry about this unless you need to make changes or test HTTPS behavior. + +The automatic redirection to HTTPS is turned off by default in development. If you need to test this behavior, put `FORCE_TO_HTTPS=true` in your `.env` file. ##Production Installation 1. Clone this repostory and `cd` into it diff --git a/client/components/forceProtocol.jsx b/client/components/forceProtocol.jsx index d722392890..5a583bc438 100644 --- a/client/components/forceProtocol.jsx +++ b/client/components/forceProtocol.jsx @@ -3,8 +3,12 @@ import React, { PropTypes } from 'react'; /** * A Higher Order Component that forces the protocol to change on mount * + * targetProtocol: the protocol to redirect to on mount + * sourceProtocol: the protocol to redirect back to on unmount + * disable: if true, the redirection will not happen but what should + * have happened will be logged to the console */ -const forceProtocol = ({ targetProtocol = 'https:', sourceProtocol }) => WrappedComponent => ( +const forceProtocol = ({ targetProtocol = 'https:', sourceProtocol, disable = false }) => WrappedComponent => ( class ForceProtocol extends React.Component { static propTypes = {} @@ -22,7 +26,11 @@ const forceProtocol = ({ targetProtocol = 'https:', sourceProtocol }) => Wrapped const currentProtocol = window.location.protocol; if (protocol !== currentProtocol) { - window.location = window.location.href.replace(currentProtocol, protocol); + if (disable === true) { + console.info(`forceProtocol: would have redirected from "${currentProtocol}" to "${protocol}"`); + } else { + window.location = window.location.href.replace(currentProtocol, protocol); + } } } diff --git a/client/routes.jsx b/client/routes.jsx index d1d8526087..33b3cf38fe 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -21,9 +21,12 @@ const routes = (store) => { 'https:' : 'http:'; + // If the flag is false, we stay on HTTP const forceToHttps = forceProtocol({ targetProtocol: 'https:', sourceProtocol, + // prints debugging but does not reload page + disable: process.env.FORCE_TO_HTTPS === false, }); return ( diff --git a/webpack.config.dev.js b/webpack.config.dev.js index 79db4136dc..6356c2e211 100644 --- a/webpack.config.dev.js +++ b/webpack.config.dev.js @@ -36,6 +36,9 @@ module.exports = { 'process.env': { API_URL: '"' + process.env.API_URL + '"', CLIENT: JSON.stringify(true), + FORCE_TO_HTTPS: process.env.FORCE_TO_HTTPS === 'true' ? + JSON.stringify(true) : + JSON.stringify(false), 'NODE_ENV': JSON.stringify('development'), 'S3_BUCKET': '"' + process.env.S3_BUCKET + '"' }