diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index e16e65db888..a7069fbabe0 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -61,7 +61,8 @@ class AuthSignupHome(Home): qcontext['error'] = _("Could not create a new account.") response = request.render('auth_signup.signup', qcontext) - response.headers['X-Frame-Options'] = 'DENY' + response.headers['X-Frame-Options'] = 'SAMEORIGIN' + response.headers['Content-Security-Policy'] = "frame-ancestors 'self'" return response @http.route('/web/reset_password', type='http', auth='public', website=True, sitemap=False) @@ -93,7 +94,8 @@ class AuthSignupHome(Home): qcontext['error'] = str(e) response = request.render('auth_signup.reset_password', qcontext) - response.headers['X-Frame-Options'] = 'DENY' + response.headers['X-Frame-Options'] = 'SAMEORIGIN' + response.headers['Content-Security-Policy'] = "frame-ancestors 'self'" return response def get_auth_signup_config(self): diff --git a/addons/portal/controllers/portal.py b/addons/portal/controllers/portal.py index 880afa26318..07505196952 100644 --- a/addons/portal/controllers/portal.py +++ b/addons/portal/controllers/portal.py @@ -213,7 +213,8 @@ class CustomerPortal(Controller): }) response = request.render("portal.portal_my_details", values) - response.headers['X-Frame-Options'] = 'DENY' + response.headers['X-Frame-Options'] = 'SAMEORIGIN' + response.headers['Content-Security-Policy'] = "frame-ancestors 'self'" return response def on_account_update(self, values, partner): @@ -233,7 +234,8 @@ class CustomerPortal(Controller): )) return request.render('portal.portal_my_security', values, headers={ - 'X-Frame-Options': 'DENY' + 'X-Frame-Options': 'SAMEORIGIN', + 'Content-Security-Policy': "frame-ancestors 'self'" }) def _update_password(self, old, new1, new2): diff --git a/addons/web/controllers/home.py b/addons/web/controllers/home.py index 7c22ac3c3b2..f0f08d30f91 100644 --- a/addons/web/controllers/home.py +++ b/addons/web/controllers/home.py @@ -119,7 +119,8 @@ class Home(http.Controller): values['disable_database_manager'] = True response = request.render('web.login', values) - response.headers['X-Frame-Options'] = 'DENY' + response.headers['X-Frame-Options'] = 'SAMEORIGIN' + response.headers['Content-Security-Policy'] = "frame-ancestors 'self'" return response @http.route('/web/login_successful', type='http', auth='user', website=True, sitemap=False) diff --git a/addons/website/static/src/client_actions/website_preview/website_preview.js b/addons/website/static/src/client_actions/website_preview/website_preview.js index 270b92c929a..ff54e9123b8 100644 --- a/addons/website/static/src/client_actions/website_preview/website_preview.js +++ b/addons/website/static/src/client_actions/website_preview/website_preview.js @@ -3,7 +3,7 @@ import { registry } from '@web/core/registry'; import { useService } from '@web/core/utils/hooks'; -const { Component, onWillStart, useEffect, useRef } = owl; +const { Component, onWillStart, useRef } = owl; export class WebsitePreview extends Component { setup() { @@ -16,15 +16,6 @@ export class WebsitePreview extends Component { const encodedPath = encodeURIComponent(this.path); this.initialUrl = `/website/force/${this.websiteId}?path=${encodedPath}`; }); - - useEffect(() => { - this.iframe.el.addEventListener('load', () => { - // This replaces the browser url (/web#action=website...) with - // the iframe's url (it is clearer for the user). - this.currentUrl = this.iframe.el.contentDocument.location.href; - history.replaceState({}, this.props.action.display_name, this.currentUrl); - }); - }, () => []); } get websiteId() { @@ -37,11 +28,63 @@ export class WebsitePreview extends Component { get path() { let path = this.props.action.context.params && this.props.action.context.params.path; - if (!path) { + if (path) { + const url = new URL(path, window.location.origin); + if (this._isTopWindowURL(url)) { + // If the client action is initialized with a path that + // should not be opened inside the iframe (= something we + // would want to open on the top window), we consider that + // this is not a valid flow. Instead of trying to open it on + // the top window, we initialize the iframe with the + // website homepage... + path = '/'; + } else { + // ... otherwise, the path still needs to be normalized (as + // it would be if the given path was used as an href of a + // element). + path = url.pathname + url.search + url.hash; + } + } else { path = '/'; } return path; } + + /** + * Returns true if the url should be opened in the top + * window. + * + * @param host {string} host of the route. + * @param pathname {string} path of the route. + * @private + */ + _isTopWindowURL({ host, pathname }) { + const backendRoutes = ['/web', '/web/session/logout']; + return host !== window.location.host || (pathname && backendRoutes.includes(pathname)); + } + + _onPageLoaded() { + // This replaces the browser url (/web#action=website...) with + // the iframe's url (it is clearer for the user). + this.currentUrl = this.iframe.el.contentDocument.location.href; + history.replaceState({}, this.props.action.display_name, this.currentUrl); + + // The clicks on the iframe are listened, so that links with external + // redirections can be opened in the top window. + this.iframe.el.contentDocument.addEventListener('click', (ev) => { + const linkEl = ev.target.closest('[href]'); + if (!linkEl) { + return; + } + + const { href, target } = linkEl; + if (href && target !== '_blank' && !this.websiteContext.edition && this._isTopWindowURL(linkEl)) { + ev.preventDefault(); + ev.stopPropagation(); + window.location.replace(href); + } + }); + } } WebsitePreview.template = 'website.WebsitePreview'; diff --git a/addons/website/static/src/client_actions/website_preview/website_preview.xml b/addons/website/static/src/client_actions/website_preview/website_preview.xml index 7840168b132..db9b295f392 100644 --- a/addons/website/static/src/client_actions/website_preview/website_preview.xml +++ b/addons/website/static/src/client_actions/website_preview/website_preview.xml @@ -2,7 +2,10 @@
-