[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.
This commit is contained in:
Raphael Collet
2016-08-31 17:21:55 +02:00
parent 604a1c5fcd
commit 16696b2a05
5 changed files with 60 additions and 13 deletions
+4 -5
View File
@@ -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):
+4 -3
View File
@@ -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]);
+38
View File
@@ -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:
+8
View File
@@ -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',
+6 -5
View File
@@ -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)