summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphael Collet <rco@openerp.com>2016-08-05 12:42:00 +0200
committerRaphael Collet <rco@openerp.com>2016-08-31 17:21:55 +0200
commit16696b2a05529a7cd3d4b9e20ee89a575f3cd9e2 (patch)
treef9a16a07f8858f83985a60562f270993363028f3
parent604a1c5fcd00674e841f073570979c4c6e5ab621 (diff)
[FIX] openerp, web: make `execute` and `call_kw` invoke methods in the new API
Also refactor the test to prevent private methods to be remotely called.
-rw-r--r--addons/web/controllers/main.py9
-rw-r--r--addons/web/static/src/js/web_client.js7
-rw-r--r--openerp/api.py38
-rw-r--r--openerp/models.py8
-rw-r--r--openerp/service/model.py11
5 files changed, 60 insertions, 13 deletions
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index b479f6d97a9..3fcd074aeb0 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -27,7 +27,7 @@ from cStringIO import StringIO
import odoo
import odoo.modules.registry
-from odoo.api import Environment
+from odoo.api import call_kw, Environment
from odoo.modules import get_resource_path
from odoo.tools import topological_sort
from odoo.tools.translate import _
@@ -35,6 +35,7 @@ from odoo.tools.misc import str2bool, xlwt
from odoo import http
from odoo.http import request, serialize_exception as _serialize_exception, content_disposition
from odoo.exceptions import AccessError
+from odoo.models import check_method_name
_logger = logging.getLogger(__name__)
@@ -840,10 +841,8 @@ class DataSet(http.Controller):
return self._call_kw(model, method, args, {})
def _call_kw(self, model, method, args, kwargs):
- if method.startswith('_'):
- raise AccessError(_("Underscore prefixed methods cannot be remotely called"))
-
- return getattr(request.registry.get(model), method)(request.cr, request.uid, *args, **kwargs)
+ check_method_name(method)
+ return call_kw(request.env[model], method, args, kwargs)
@http.route('/web/dataset/call', type='json', auth="user")
def call(self, model, method, args, domain_id=None, context_id=None):
diff --git a/addons/web/static/src/js/web_client.js b/addons/web/static/src/js/web_client.js
index 5fc1f3f2796..c6033d6cbad 100644
--- a/addons/web/static/src/js/web_client.js
+++ b/addons/web/static/src/js/web_client.js
@@ -67,9 +67,9 @@ return AbstractWebClient.extend({
logo_edit: function(ev) {
var self = this;
ev.preventDefault();
- self.alive(new Model("res.users").get_func("read")(session.uid, ["company_id"])).then(function(res) {
+ self.alive(new Model("res.users").call("read", [[session.uid], ["company_id"]])).then(function(data) {
self.rpc("/web/action/load", { action_id: "base.action_res_company_form" }).done(function(result) {
- result.res_id = res.company_id[0];
+ result.res_id = data[0].company_id[0];
result.target = "new";
result.views = [[false, 'form']];
result.flags = {
@@ -94,7 +94,8 @@ return AbstractWebClient.extend({
var state = $.bbq.getState(true);
if (_.isEmpty(state) || state.action === "login") {
self.menu.is_bound.done(function() {
- new Model("res.users").call("read", [session.uid, ["action_id"]]).done(function(data) {
+ new Model("res.users").call("read", [[session.uid], ["action_id"]]).done(function(result) {
+ var data = result[0];
if(data.action_id) {
self.action_manager.do_action(data.action_id[0]);
self.menu.open_action(data.action_id[0]);
diff --git a/openerp/api.py b/openerp/api.py
index fb71a3c5b5c..d5d258164ae 100644
--- a/openerp/api.py
+++ b/openerp/api.py
@@ -42,6 +42,7 @@ __all__ = [
'cr_uid_ids', 'cr_uid_ids_context',
'cr_uid_records', 'cr_uid_records_context',
'constrains', 'depends', 'onchange', 'returns',
+ 'call_kw',
]
import logging
@@ -76,6 +77,19 @@ WRAPPED_ATTRS = ('__module__', '__name__', '__doc__', '_constrains',
INHERITED_ATTRS = ('_returns',)
+class Params(object):
+ def __init__(self, args, kwargs):
+ self.args = args
+ self.kwargs = kwargs
+ def __str__(self):
+ params = []
+ for arg in self.args:
+ params.append(repr(arg))
+ for item in sorted(self.kwargs.iteritems()):
+ params.append("%s=%r" % item)
+ return ', '.join(params)
+
+
class Meta(type):
""" Metaclass that automatically decorates traditional-style methods by
guessing their API. It also implements the inheritance of the
@@ -643,6 +657,30 @@ def expected(decorator, func):
+def call_kw_model(method, self, args, kwargs):
+ context, args, kwargs = split_context(method, args, kwargs)
+ recs = self.with_context(context or {})
+ _logger.debug("call %s.%s(%s)", recs, method.__name__, Params(args, kwargs))
+ result = method(recs, *args, **kwargs)
+ return downgrade(method, result, recs, args, kwargs)
+
+def call_kw_multi(method, self, args, kwargs):
+ ids, args = args[0], args[1:]
+ context, args, kwargs = split_context(method, args, kwargs)
+ recs = self.with_context(context or {}).browse(ids)
+ _logger.debug("call %s.%s(%s)", recs, method.__name__, Params(args, kwargs))
+ result = method(recs, *args, **kwargs)
+ return downgrade(method, result, recs, args, kwargs)
+
+def call_kw(model, name, args, kwargs):
+ """ Invoke the given method ``name`` on the recordset ``model``. """
+ method = getattr(type(model), name)
+ if getattr(method, '_api', None) == 'model':
+ return call_kw_model(method, model, args, kwargs)
+ else:
+ return call_kw_multi(method, model, args, kwargs)
+
+
class Environment(Mapping):
""" An environment wraps data for ORM records:
diff --git a/openerp/models.py b/openerp/models.py
index 9c7edc15f86..21258d77a98 100644
--- a/openerp/models.py
+++ b/openerp/models.py
@@ -113,6 +113,14 @@ def check_pg_name(name):
if len(name) > 63:
raise ValidationError("Table name %r is too long" % name)
+# match private methods, to prevent their remote invocation
+regex_private = re.compile(r'^(_.*|init)$')
+
+def check_method_name(name):
+ """ Raise an ``AccessError`` if ``name`` is a private method name. """
+ if regex_private.match(name):
+ raise AccessError(_('Private methods (such as %s) cannot be called remotely.') % (name,))
+
POSTGRES_CONFDELTYPES = {
'RESTRICT': 'r',
'NO ACTION': 'a',
diff --git a/openerp/service/model.py b/openerp/service/model.py
index 6cc4a3fbde9..02752355916 100644
--- a/openerp/service/model.py
+++ b/openerp/service/model.py
@@ -9,6 +9,7 @@ import time
import openerp
from openerp.exceptions import UserError, ValidationError, QWebException
+from openerp.models import check_method_name
from openerp.tools.translate import translate
from openerp.tools.translate import _
@@ -164,10 +165,11 @@ def check(f):
return wrapper
def execute_cr(cr, uid, obj, method, *args, **kw):
- object = openerp.registry(cr.dbname).get(obj)
- if object is None:
+ recs = openerp.api.Environment(cr, uid, {}).get(obj)
+ if recs is None:
raise UserError(_("Object %s doesn't exist") % obj)
- return getattr(object, method)(cr, uid, *args, **kw)
+ return openerp.api.call_kw(recs, method, args, kw)
+
def execute_kw(db, uid, obj, method, args, kw=None):
return execute(db, uid, obj, method, *args, **kw or {})
@@ -176,8 +178,7 @@ def execute_kw(db, uid, obj, method, args, kw=None):
def execute(db, uid, obj, method, *args, **kw):
threading.currentThread().dbname = db
with openerp.registry(db).cursor() as cr:
- if method.startswith('_'):
- raise UserError(_('Private methods (such as %s) cannot be called remotely.') % (method,))
+ check_method_name(method)
res = execute_cr(cr, uid, obj, method, *args, **kw)
if res is None:
_logger.info('The method %s of the object %s can not return `None` !', method, obj)