From 31664422c11ab1e9c34ad6b2eb1dfba2af72e359 Mon Sep 17 00:00:00 2001 From: Martin Geubelle Date: Thu, 10 Jan 2019 11:25:13 +0000 Subject: [PATCH 1/4] [IMP] web, *: remove explicit references to `session_id` Since rev. odoo/odoo@f4d541e the `session_id` cookie uses the `httponly` flag so it cannot be accessed through client side script. But before this rev. the `session_id` was still provided by the server to the webclient (in session_info, mostly) and was stored and accessible. This made XSS injection more dangerous than they should be as it was very easy to steal the `session_id`. As the browser automatically set the `session_id` on every request to the server, the webclient shouldn't need any explicit reference. --- addons/base_import/static/src/xml/base_import.xml | 2 -- addons/mail/static/src/xml/activity.xml | 1 - addons/mail/static/src/xml/composer.xml | 1 - addons/mail/static/src/xml/web_kanban_activity.xml | 1 - addons/project/static/src/xml/project.xml | 1 - addons/web/models/ir_http.py | 1 - addons/web/static/src/js/core/session.js | 2 -- addons/web/static/src/js/fields/basic_fields.js | 1 - addons/web/static/src/xml/base.xml | 4 ---- 9 files changed, 14 deletions(-) diff --git a/addons/base_import/static/src/xml/base_import.xml b/addons/base_import/static/src/xml/base_import.xml index 951002b279f..c475a30036d 100644 --- a/addons/base_import/static/src/xml/base_import.xml +++ b/addons/base_import/static/src/xml/base_import.xml @@ -3,8 +3,6 @@
- diff --git a/addons/mail/static/src/xml/web_kanban_activity.xml b/addons/mail/static/src/xml/web_kanban_activity.xml index 698091e9875..4d6c290f11f 100644 --- a/addons/mail/static/src/xml/web_kanban_activity.xml +++ b/addons/mail/static/src/xml/web_kanban_activity.xml @@ -92,7 +92,6 @@ /web/binary/upload_attachment - diff --git a/addons/project/static/src/xml/project.xml b/addons/project/static/src/xml/project.xml index ea69825c5a7..4be07b70fcd 100644 --- a/addons/project/static/src/xml/project.xml +++ b/addons/project/static/src/xml/project.xml @@ -15,7 +15,6 @@ /web/binary/upload_attachment - diff --git a/addons/web/models/ir_http.py b/addons/web/models/ir_http.py index e42c780d31e..414df12d8c5 100644 --- a/addons/web/models/ir_http.py +++ b/addons/web/models/ir_http.py @@ -23,7 +23,6 @@ class Http(models.AbstractModel): display_switch_company_menu = user.has_group('base.group_multi_company') and len(user.company_ids) > 1 version_info = odoo.service.common.exp_version() return { - "session_id": request.session.sid, "uid": request.session.uid, "is_system": user._is_system() if request.session.uid else False, "is_admin": user._is_admin() if request.session.uid else False, diff --git a/addons/web/static/src/js/core/session.js b/addons/web/static/src/js/core/session.js index 694285cccdc..968496c2a6d 100644 --- a/addons/web/static/src/js/core/session.js +++ b/addons/web/static/src/js/core/session.js @@ -139,7 +139,6 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { if (!result.uid) { return $.Deferred().reject(); } - delete result.session_id; _.extend(self, result); }); }, @@ -290,7 +289,6 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { */ session_reload: function () { var result = _.extend({}, window.odoo.session_info); - delete result.session_id; _.extend(this, result); return $.when(); }, diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index b3cd17a38ee..ae080f45a11 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -1446,7 +1446,6 @@ var AbstractFieldBinary = AbstractField.extend({ self.on_file_uploaded(file.size, file.name, file.type, data); }; } else { - this.$('form.o_form_binary_form input[name=session_id]').val(this.getSession().session_id); this.$('form.o_form_binary_form').submit(); } this.$('.o_form_binary_progress').show(); diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 5e2f27ccc66..dc40c962333 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -378,7 +378,6 @@ - Add... @@ -998,7 +997,6 @@ - @@ -1042,7 +1040,6 @@ - @@ -1567,7 +1564,6 @@ - From 016f9612ba4688fb940910c74f13f9cbd5b115e0 Mon Sep 17 00:00:00 2001 From: Martin Geubelle Date: Thu, 10 Jan 2019 13:20:18 +0000 Subject: [PATCH 2/4] [REF] web: clean ajax and session --- addons/web/static/src/js/core/ajax.js | 8 +- addons/web/static/src/js/core/session.js | 96 +++++------------------- odoo/http.py | 14 +--- 3 files changed, 23 insertions(+), 95 deletions(-) diff --git a/addons/web/static/src/js/core/ajax.js b/addons/web/static/src/js/core/ajax.js index 1821d5a3be4..67b47add1e2 100644 --- a/addons/web/static/src/js/core/ajax.js +++ b/addons/web/static/src/js/core/ajax.js @@ -113,8 +113,6 @@ function jsonpRpc(url, fct_name, params, settings) { var payload_url = $.param({r:payload_str}); var force2step = settings.force2step || false; delete settings.force2step; - var session_id = settings.session_id || null; - delete settings.session_id; if (payload_url.length < 2000 && ! force2step) { return $.ajax(url, _.extend({}, settings, { url: url, @@ -122,10 +120,10 @@ function jsonpRpc(url, fct_name, params, settings) { jsonp: 'jsonp', type: 'GET', cache: false, - data: {r: payload_str, session_id: session_id} + data: {r: payload_str} })); } else { - var args = {session_id: session_id, id: data.id}; + var args = {id: data.id}; var ifid = _.uniqueId('oe_rpc_iframe'); var html = ""; var $iframe = $(html); @@ -156,7 +154,7 @@ function jsonpRpc(url, fct_name, params, settings) { jsonp: 'jsonp', type: 'GET', cache: false, - data: {session_id: session_id, id: data.id} + data: {id: data.id} }).always(function() { cleanUp(); }).done(function() { diff --git a/addons/web/static/src/js/core/session.js b/addons/web/static/src/js/core/session.js index 968496c2a6d..a9479d5a690 100644 --- a/addons/web/static/src/js/core/session.js +++ b/addons/web/static/src/js/core/session.js @@ -22,11 +22,8 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { or `null` if the server to contact is the origin server. @param {Dict} options A dictionary that can contain the following options: - * "override_session": Default to false. If true, the current session object will - not try to re-use a previously created session id stored in a cookie. - * "session_id": Default to null. If specified, the specified session_id will be used - by this session object. Specifying this option automatically implies that the option - "override_session" is set to true. + * "modules" + * "use_cors" */ init: function (parent, origin, options) { mixins.EventDispatcherMixin.init.call(this); @@ -34,8 +31,6 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { options = options || {}; this.module_list = (options.modules && options.modules.slice()) || (window.odoo._modules && window.odoo._modules.slice()) || []; this.server = null; - this.session_id = options.session_id || null; - this.override_session = options.override_session || !!options.session_id || false; this.avoid_recursion = false; this.use_cors = options.use_cors || false; this.setup(origin); @@ -61,7 +56,6 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { this.origin = origin; this.prefix = this.origin; this.server = this.origin; // keep chs happy - this.origin_server = this.origin === window_origin; options = options || {}; if ('use_cors' in options) { this.use_cors = options.use_cors; @@ -275,9 +269,6 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { return this.currencies[currency_id]; }, get_file: function (options) { - if (this.override_session){ - options.data.session_id = this.session_id; - } options.session = this; return ajax.get_file(options); }, @@ -292,25 +283,6 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { _.extend(this, result); return $.when(); }, - check_session_id: function () { - var self = this; - if (this.avoid_recursion) - return $.when(); - if (this.session_id) - return $.when(); // we already have the session id - if (!this.use_cors && (this.override_session || ! this.origin_server)) { - // If we don't use the origin server we consider we should always create a new session. - // Even if some browsers could support cookies when using jsonp that behavior is - // not consistent and the browser creators are tending to removing that feature. - this.avoid_recursion = true; - return this.rpc("/gen_session_id", {}).then(function (result) { - self.session_id = result; - }).always(function () { - self.avoid_recursion = false; - }); - } - return $.when(); - }, /** * Executes an RPC call, registering the provided callbacks. * @@ -331,57 +303,25 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { options.headers["X-Debug-Mode"] = $.deparam($.param.querystring()).debug; } - var deferred = self.check_session_id(); - var aborted = false; - var xhrDef; - deferred.abort = function () { - if (xhrDef) { - xhrDef.abort(); - } else { - aborted = true; - } - }; - - var promise = deferred.then(function () { - if (aborted) { - var def = $.Deferred().reject({message: "XmlHttpRequestError abort"}, $.Event('abort')); - def.abort = function () {}; - return def; - } - // TODO: remove - if (! _.isString(url)) { - _.extend(options, url); - url = url.url; - } - var fct; - if (self.origin_server) { - fct = ajax.jsonRpc; - if (self.override_session) { - options.headers["X-Openerp-Session-Id"] = self.session_id || ''; - } - } else if (self.use_cors) { - fct = ajax.jsonRpc; - url = self.url(url, null); - options.session_id = self.session_id || ''; - if (self.override_session) { - options.headers["X-Openerp-Session-Id"] = self.session_id || ''; - } - } else { - fct = ajax.jsonpRpc; - url = self.url(url, null); - options.session_id = self.session_id || ''; - } - xhrDef = fct(url, "call", params, options); - return xhrDef; - }); - - promise.abort = deferred.abort; - return promise; + // TODO: remove + if (! _.isString(url)) { + _.extend(options, url); + url = url.url; + } + var fct; + if (self.origin_server) { + fct = ajax.jsonRpc; + } else if (self.use_cors) { + fct = ajax.jsonRpc; + url = self.url(url, null); + } else { + fct = ajax.jsonpRpc; + url = self.url(url, null); + } + return fct(url, "call", params, options); }, url: function (path, params) { params = _.extend(params || {}); - if (this.override_session || (! this.origin_server)) - params.session_id = this.session_id; var qs = $.param(params); if (qs.length > 0) qs = "?" + qs; diff --git a/odoo/http.py b/odoo/http.py index 3b44a7d2054..15534b41bc8 100644 --- a/odoo/http.py +++ b/odoo/http.py @@ -302,7 +302,7 @@ class WebRequest(object): def _handle_exception(self, exception): """Called within an except block to allow converting exceptions to abitrary responses. Anything returned (except None) will - be used as response.""" + be used as response.""" self._failed = exception # prevent tx commit if not isinstance(exception, NO_POSTMORTEM) \ and not isinstance(exception, werkzeug.exceptions.HTTPException): @@ -587,7 +587,7 @@ class JsonRequest(WebRequest): self.jsonp = jsonp request = None request_id = args.get('id') - + if jsonp and self.httprequest.method == 'POST': # jsonp 2 steps step1 POST: save call def handler(): @@ -1647,15 +1647,5 @@ def content_disposition(filename): return "attachment; filename*=UTF-8''%s" % escaped -#---------------------------------------------------------- -# RPC controller -#---------------------------------------------------------- -class CommonController(Controller): - - @route('/gen_session_id', type='json', auth="none") - def gen_session_id(self): - nsession = root.session_store.new() - return nsession.sid - # main wsgi handler root = Root() From 06de5640744a4bb5eadd6cf18b7c83e47ced285d Mon Sep 17 00:00:00 2001 From: Martin Geubelle Date: Tue, 15 Jan 2019 09:09:02 +0000 Subject: [PATCH 3/4] [REF] web, *: remove JSONP support JSONP can be entirely replaced by the CORS mechanism, which is simpler. We support CORS in our routes since 8.0 (odoo/odoo@9cce88a), so it's about time to get rid of JSONP. --- addons/web/static/src/js/core/ajax.js | 80 +------------------ addons/web/static/src/js/core/session.js | 12 +-- .../js/website_sale_tour_shop_customize.js | 4 +- odoo/http.py | 40 +--------- 4 files changed, 12 insertions(+), 124 deletions(-) diff --git a/addons/web/static/src/js/core/ajax.js b/addons/web/static/src/js/core/ajax.js index 67b47add1e2..509126498bc 100644 --- a/addons/web/static/src/js/core/ajax.js +++ b/addons/web/static/src/js/core/ajax.js @@ -45,7 +45,6 @@ function genericJsonRpc (fct_name, params, settings, fct) { var def = $.Deferred(); return def.reject.apply(def, ["communication"].concat(_.toArray(arguments))); }); - // FIXME: jsonp? deferred.abort = function () { deferred.reject({message: "XmlHttpRequestError abort"}, $.Event('abort')); if (xhr.abort) { @@ -106,78 +105,6 @@ function jsonRpc(url, fct_name, params, settings) { }); } -function jsonpRpc(url, fct_name, params, settings) { - settings = settings || {}; - return genericJsonRpc(fct_name, params, settings, function(data) { - var payload_str = JSON.stringify(data, time.date_to_utc); - var payload_url = $.param({r:payload_str}); - var force2step = settings.force2step || false; - delete settings.force2step; - if (payload_url.length < 2000 && ! force2step) { - return $.ajax(url, _.extend({}, settings, { - url: url, - dataType: 'jsonp', - jsonp: 'jsonp', - type: 'GET', - cache: false, - data: {r: payload_str} - })); - } else { - var args = {id: data.id}; - var ifid = _.uniqueId('oe_rpc_iframe'); - var html = ""; - var $iframe = $(html); - var nurl = 'jsonp=1&' + $.param(args); - nurl = url.indexOf("?") !== -1 ? url + "&" + nurl : url + "?" + nurl; - var $form = $('') - .attr('method', 'POST') - .attr('target', ifid) - .attr('enctype', "multipart/form-data") - .attr('action', nurl) - .append($('').attr('value', payload_str)) - .hide() - .appendTo($('body')); - var cleanUp = function() { - if ($iframe) { - $iframe.unbind("load").remove(); - } - $form.remove(); - }; - var deferred = $.Deferred(); - // the first bind is fired up when the iframe is added to the DOM - $iframe.bind('load', function() { - // the second bind is fired up when the result of the form submission is received - $iframe.unbind('load').bind('load', function() { - $.ajax({ - url: url, - dataType: 'jsonp', - jsonp: 'jsonp', - type: 'GET', - cache: false, - data: {id: data.id} - }).always(function() { - cleanUp(); - }).done(function() { - deferred.resolve.apply(deferred, arguments); - }).fail(function() { - deferred.reject.apply(deferred, arguments); - }); - }); - // now that the iframe can receive data, we fill and submit the form - $form.submit(); - }); - // append the iframe to the DOM (will trigger the first load) - $form.after($iframe); - if (settings.timeout) { - realSetTimeout(function() { - deferred.reject({}); - }, settings.timeout); - } - return deferred; - } - }); -} - // helper function to make a rpc with a function name hardcoded to 'call' function rpc(url, params, settings) { return jsonRpc(url, 'call', params, settings); @@ -310,7 +237,7 @@ function get_file(options) { xhr.open('POST', options.url); data = new FormData(); _.each(options.data || {}, function (v, k) { - data.append(k, v) + data.append(k, v); }); } data.append('token', 'dummy-because-api-expects-one'); @@ -358,7 +285,7 @@ function get_file(options) { name: String(xhr.status), title: nodes.length > 0 ? nodes[0].textContent : '', } - } + }; } options.error(err); }; @@ -373,7 +300,7 @@ function get_file(options) { } }; if (options.complete) { - xhr.onloadend = function () { options.complete(); } + xhr.onloadend = function () { options.complete(); }; } xhr.send(data); @@ -594,7 +521,6 @@ function loadLibs (libs) { var ajax = { jsonRpc: jsonRpc, - jsonpRpc: jsonpRpc, rpc: rpc, loadCSS: loadCSS, loadJS: loadJS, diff --git a/addons/web/static/src/js/core/session.js b/addons/web/static/src/js/core/session.js index a9479d5a690..7212b8ad76e 100644 --- a/addons/web/static/src/js/core/session.js +++ b/addons/web/static/src/js/core/session.js @@ -308,17 +308,11 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { _.extend(options, url); url = url.url; } - var fct; - if (self.origin_server) { - fct = ajax.jsonRpc; - } else if (self.use_cors) { - fct = ajax.jsonRpc; - url = self.url(url, null); - } else { - fct = ajax.jsonpRpc; + if (self.use_cors) { url = self.url(url, null); } - return fct(url, "call", params, options); + + return ajax.jsonRpc(url, "call", params, options); }, url: function (path, params) { params = _.extend(params || {}); diff --git a/addons/website_sale/static/src/js/website_sale_tour_shop_customize.js b/addons/website_sale/static/src/js/website_sale_tour_shop_customize.js index 009eb44b436..da48b761560 100644 --- a/addons/website_sale/static/src/js/website_sale_tour_shop_customize.js +++ b/addons/website_sale/static/src/js/website_sale_tour_shop_customize.js @@ -47,8 +47,8 @@ odoo.define('website_sale.tour_shop_customize', function (require) { args: ['product.group_product_variant', false], kwargs: {} }; - ajax.jsonpRpc('/web/dataset/call_kw', 'call', get_group_payload).then(function(group_id) { - ajax.jsonpRpc('/web/dataset/call_kw', 'call', { + ajax.jsonRpc('/web/dataset/call_kw', 'call', get_group_payload).then(function(group_id) { + ajax.jsonRpc('/web/dataset/call_kw', 'call', { model: 'res.groups', method: 'write', args: [group_id, {'users': [[4, 1]]}], diff --git a/odoo/http.py b/odoo/http.py index 15534b41bc8..886fecc48ba 100644 --- a/odoo/http.py +++ b/odoo/http.py @@ -579,34 +579,14 @@ class JsonRequest(WebRequest): def __init__(self, *args): super(JsonRequest, self).__init__(*args) - self.jsonp_handler = None self.params = {} args = self.httprequest.args - jsonp = args.get('jsonp') - self.jsonp = jsonp request = None request_id = args.get('id') - if jsonp and self.httprequest.method == 'POST': - # jsonp 2 steps step1 POST: save call - def handler(): - self.session['jsonp_request_%s' % (request_id,)] = self.httprequest.form['r'] - self.session.modified = True - headers=[('Content-Type', 'text/plain; charset=utf-8')] - r = werkzeug.wrappers.Response(request_id, headers=headers) - return r - self.jsonp_handler = handler - return - elif jsonp and args.get('r'): - # jsonp method GET - request = args.get('r') - elif jsonp and request_id: - # jsonp 2 steps step2 GET: run and return result - request = self.session.pop('jsonp_request_%s' % (request_id,), '{}') - else: - # regular jsonrpc2 - request = self.httprequest.get_data().decode(self.httprequest.charset) + # regular jsonrpc2 + request = self.httprequest.get_data().decode(self.httprequest.charset) # Read POST content or POST Form Data named "request" try: @@ -630,16 +610,8 @@ class JsonRequest(WebRequest): if result is not None: response['result'] = result - if self.jsonp: - # If we use jsonp, that's mean we are called from another host - # Some browser (IE and Safari) do no allow third party cookies - # We need then to manage http sessions manually. - response['session_id'] = self.session.sid - mime = 'application/javascript' - body = "%s(%s);" % (self.jsonp, json.dumps(response, default=date_utils.json_default)) - else: - mime = 'application/json' - body = json.dumps(response, default=date_utils.json_default) + mime = 'application/json' + body = json.dumps(response, default=date_utils.json_default) return Response( body, status=error and error.pop('http_status', 200) or 200, @@ -674,8 +646,6 @@ class JsonRequest(WebRequest): return self._json_response(error=error) def dispatch(self): - if self.jsonp_handler: - return self.jsonp_handler() try: rpc_request_flag = rpc_request.isEnabledFor(logging.DEBUG) rpc_response_flag = rpc_response.isEnabledFor(logging.DEBUG) @@ -1388,8 +1358,6 @@ class Root(object): def get_request(self, httprequest): # deduce type of request - if httprequest.args.get('jsonp'): - return JsonRequest(httprequest) if httprequest.mimetype in ("application/json", "application/json-rpc"): return JsonRequest(httprequest) else: From 7abeaf56b3ae544dbe24c4a9fe4fa6d919582aba Mon Sep 17 00:00:00 2001 From: Martin Geubelle Date: Tue, 15 Jan 2019 15:04:03 +0000 Subject: [PATCH 4/4] [REF] im_livechat, *: remove JSONP in favor of CORS When embedding the livechat on an external website, we used to make JSONP calls. As the support of JSONP calls has been dropped, we now use the CORS mechanism instead. --- addons/bus/controllers/main.py | 2 +- addons/im_livechat/controllers/main.py | 26 ++++++++++++++----- .../im_livechat/static/src/js/im_livechat.js | 10 +++---- .../views/im_livechat_channel_templates.xml | 2 +- addons/mail/controllers/bus.py | 4 +-- addons/web/controllers/main.py | 16 ------------ 6 files changed, 27 insertions(+), 33 deletions(-) diff --git a/addons/bus/controllers/main.py b/addons/bus/controllers/main.py index a5a0e3e189b..6ff4d7ac8e0 100644 --- a/addons/bus/controllers/main.py +++ b/addons/bus/controllers/main.py @@ -27,7 +27,7 @@ class BusController(Controller): request._cr = None return dispatch.poll(dbname, channels, last, options) - @route('/longpolling/poll', type="json", auth="public") + @route('/longpolling/poll', type="json", auth="public", cors="*") def poll(self, channels, last, options=None): if options is None: options = {} diff --git a/addons/im_livechat/controllers/main.py b/addons/im_livechat/controllers/main.py index 86553625f4d..b3783d2f59d 100644 --- a/addons/im_livechat/controllers/main.py +++ b/addons/im_livechat/controllers/main.py @@ -3,13 +3,16 @@ import base64 -from odoo import http, _ +from odoo import http,tools, _ from odoo.http import request from odoo.addons.base.models.assetsbundle import AssetsBundle class LivechatController(http.Controller): + # Note: the `cors` attribute on many routes is meant to allow the livechat + # to be embedded in an external website. + @http.route('/im_livechat/external_lib.', type='http', auth='none') def livechat_lib(self, ext, **kwargs): # _get_asset return the bundle html code (script and link list) but we want to use the attachment content @@ -26,6 +29,17 @@ class LivechatController(http.Controller): headers.append(('Content-Length', len(content_base64))) return request.make_response(content_base64, headers) + @http.route('/im_livechat/load_templates', type='json', auth='none', cors="*") + def load_templates(self, **kwargs): + base_url = request.httprequest.base_url + templates = [ + 'mail/static/src/xml/abstract_thread_window.xml', + 'mail/static/src/xml/discuss.xml', + 'mail/static/src/xml/thread.xml', + 'im_livechat/static/src/xml/im_livechat.xml', + ] + return [tools.file_open(tmpl, 'rb').read() for tmpl in templates] + @http.route('/im_livechat/support/', type='http', auth='public') def support_page(self, channel_id, **kwargs): channel = request.env['im_livechat.channel'].sudo().browse(channel_id) @@ -38,7 +52,7 @@ class LivechatController(http.Controller): info = request.env['im_livechat.channel'].get_livechat_info(channel.id, username=username) return request.render('im_livechat.loader', {'info': info, 'web_session_required': True}, headers=[('Content-Type', 'application/javascript')]) - @http.route('/im_livechat/init', type='json', auth="public") + @http.route('/im_livechat/init', type='json', auth="public", cors="*") def livechat_init(self, channel_id): LivechatChannel = request.env['im_livechat.channel'] available = len(LivechatChannel.browse(channel_id).get_available_users()) @@ -66,7 +80,7 @@ class LivechatController(http.Controller): 'rule': rule, } - @http.route('/im_livechat/get_session', type="json", auth='public') + @http.route('/im_livechat/get_session', type="json", auth='public', cors="*") def get_session(self, channel_id, anonymous_name, **kwargs): user_id = None country_id = None @@ -83,7 +97,7 @@ class LivechatController(http.Controller): anonymous_name, country_id = _("%s (%s)") % (anonymous_name, country.name), country.id return request.env["im_livechat.channel"].with_context(lang=False).get_mail_channel(channel_id, anonymous_name, user_id, country_id) - @http.route('/im_livechat/feedback', type='json', auth='public') + @http.route('/im_livechat/feedback', type='json', auth='public', cors="*") def feedback(self, uuid, rate, reason=None, **kwargs): Channel = request.env['mail.channel'] Rating = request.env['rating.rating'] @@ -114,7 +128,7 @@ class LivechatController(http.Controller): return rating.id return False - @http.route('/im_livechat/history', type="json", auth="public") + @http.route('/im_livechat/history', type="json", auth="public", cors="*") def history_pages(self, pid, channel_uuid, page_history=None): partner_ids = (pid, request.env.user.partner_id.id) channel = request.env['mail.channel'].sudo().search([('uuid', '=', channel_uuid), ('channel_partner_ids', 'in', partner_ids)]) @@ -122,7 +136,7 @@ class LivechatController(http.Controller): channel._send_history_message(pid, page_history) return True - @http.route('/im_livechat/notify_typing', type='json', auth='public') + @http.route('/im_livechat/notify_typing', type='json', auth='public', cors="*") def notify_typing(self, uuid, is_typing): """ Broadcast the typing notification of the website user to other channel members :param uuid: (string) the UUID of the livechat channel diff --git a/addons/im_livechat/static/src/js/im_livechat.js b/addons/im_livechat/static/src/js/im_livechat.js index f59a03e2040..964e9ef5d3e 100644 --- a/addons/im_livechat/static/src/js/im_livechat.js +++ b/addons/im_livechat/static/src/js/im_livechat.js @@ -190,15 +190,11 @@ var LivechatButton = Widget.extend({ * @private */ _loadQWebTemplate: function () { - var xml_files = ['/mail/static/src/xml/abstract_thread_window.xml', - '/mail/static/src/xml/thread.xml', - '/im_livechat/static/src/xml/im_livechat.xml']; - var defs = _.map(xml_files, function (tmpl) { - return session.rpc('/web/proxy/load', { path: tmpl }).then(function (xml) { - QWeb.add_template(xml); + return session.rpc('/im_livechat/load_templates').then(function (templates) { + _.each(templates, function (template) { + QWeb.add_template(template); }); }); - return $.when.apply($, defs); }, /** * @private diff --git a/addons/im_livechat/views/im_livechat_channel_templates.xml b/addons/im_livechat/views/im_livechat_channel_templates.xml index 85861a185fa..d029ae32478 100644 --- a/addons/im_livechat/views/im_livechat_channel_templates.xml +++ b/addons/im_livechat/views/im_livechat_channel_templates.xml @@ -194,7 +194,7 @@ odoo.define('web.session', function (require) { var Session = require('web.Session'); var modules = odoo._modules; - return new Session(undefined, "", {modules:modules, use_cors: false}); + return new Session(undefined, "", {modules:modules, use_cors: true}); }); diff --git a/addons/mail/controllers/bus.py b/addons/mail/controllers/bus.py index c900604ab06..912aae3cacd 100644 --- a/addons/mail/controllers/bus.py +++ b/addons/mail/controllers/bus.py @@ -34,7 +34,7 @@ class MailChatController(BusController): # -------------------------- # Anonymous routes (Common Methods) # -------------------------- - @route('/mail/chat_post', type="json", auth="none") + @route('/mail/chat_post', type="json", auth="none", cors="*") def mail_chat_post(self, uuid, message_content, **kwargs): # find the author from the user session, which can be None author_id = False # message_post accept 'False' author_id, but not 'None' @@ -46,7 +46,7 @@ class MailChatController(BusController): message = mail_channel.sudo().with_context(mail_create_nosubscribe=True).message_post(author_id=author_id, email_from=False, body=body, message_type='comment', subtype='mail.mt_comment') return message and message.id or False - @route(['/mail/chat_history'], type="json", auth="none") + @route(['/mail/chat_history'], type="json", auth="none", cors="*") def mail_chat_history(self, uuid, last_id=False, limit=20): channel = request.env["mail.channel"].sudo().search([('uuid', '=', uuid)], limit=1) if not channel: diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 59b427cfe65..cf87240484c 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -640,22 +640,6 @@ class WebClient(http.Controller): class Proxy(http.Controller): - @http.route('/web/proxy/load', type='json', auth="none") - def load(self, path): - """ Proxies an HTTP request through a JSON request. - - It is strongly recommended to not request binary files through this, - as the result will be a binary data blob as well. - - :param path: actual request path - :return: file content - """ - from werkzeug.test import Client - from werkzeug.wrappers import BaseResponse - - base_url = request.httprequest.base_url - return Client(request.httprequest.app, BaseResponse).get(path, base_url=base_url).data - @http.route('/web/proxy/post/', type='http', auth='user', methods=['GET']) def post(self, path): """Effectively execute a POST request that was hooked through user login"""