[MERGE] forward port of branch 7.0 up to revid 5281 launchpad_translations_on_behalf_of_openerp-20140412094159-mhy3v2prb3ctx32k

bzr revid: jke@openerp.com-20140409153527-mic1a8afcvdhsd27
bzr revid: chs@openerp.com-20140410102422-fcwxhjk40z0oy8x5
bzr revid: chs@openerp.com-20140415133650-y46i0o3qkb2atbvi
This commit is contained in:
Christophe Simonis 2014-04-15 15:36:50 +02:00
commit 3d2e9b4d2b
7 changed files with 92 additions and 68 deletions

View File

@ -31,6 +31,7 @@ import shutil
import tempfile import tempfile
import urllib import urllib
import urllib2 import urllib2
import urlparse
import zipfile import zipfile
import zipimport import zipimport
import lxml.html import lxml.html
@ -41,6 +42,7 @@ except ImportError:
from StringIO import StringIO # NOQA from StringIO import StringIO # NOQA
import openerp import openerp
import openerp.exceptions
from openerp import modules, tools, addons from openerp import modules, tools, addons
from openerp.modules.db import create_categories from openerp.modules.db import create_categories
from openerp.tools.parse_version import parse_version from openerp.tools.parse_version import parse_version
@ -620,40 +622,14 @@ class module(osv.osv):
return res return res
def download(self, cr, uid, ids, download=True, context=None): def download(self, cr, uid, ids, download=True, context=None):
res = [] return []
default_version = modules.adapt_version('1.0')
for mod in self.browse(cr, uid, ids, context=context):
if not mod.url:
continue
match = re.search('-([a-zA-Z0-9\._-]+)(\.zip)', mod.url, re.I)
version = default_version
if match:
version = match.group(1)
if parse_version(mod.installed_version) >= parse_version(version):
continue
res.append(mod.url)
if not download:
continue
zip_content = urllib.urlopen(mod.url).read()
fname = modules.get_module_path(str(mod.name) + '.zip', downloaded=True)
try:
with open(fname, 'wb') as fp:
fp.write(zip_content)
except Exception:
_logger.exception('Error when trying to create module '
'file %s', fname)
raise orm.except_orm(_('Error'), _('Can not create the module file:\n %s') % (fname,))
terp = self.get_module_info(mod.name)
self.write(cr, uid, mod.id, self.get_values_from_terp(terp))
cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s', (mod.id,))
self._update_dependencies(cr, uid, mod, terp.get('depends', []))
self._update_category(cr, uid, mod, terp.get('category', 'Uncategorized'))
# Import module
zimp = zipimport.zipimporter(fname)
zimp.load_module(mod.name)
return res
def install_from_urls(self, cr, uid, urls, context=None): def install_from_urls(self, cr, uid, urls, context=None):
if not self.pool['res.users'].has_group(cr, uid, 'base.group_system'):
raise openerp.exceptions.AccessDenied()
apps_server = urlparse.urlparse(self.get_apps_server(cr, uid, context=context))
OPENERP = 'openerp' OPENERP = 'openerp'
tmp = tempfile.mkdtemp() tmp = tempfile.mkdtemp()
_logger.debug('Install from url: %r', urls) _logger.debug('Install from url: %r', urls)
@ -662,6 +638,11 @@ class module(osv.osv):
for module_name, url in urls.items(): for module_name, url in urls.items():
if not url: if not url:
continue # nothing to download, local version is already the last one continue # nothing to download, local version is already the last one
up = urlparse.urlparse(url)
if up.scheme != apps_server.scheme or up.netloc != apps_server.netloc:
raise openerp.exceptions.AccessDenied()
try: try:
_logger.info('Downloading module `%s` from OpenERP Apps', module_name) _logger.info('Downloading module `%s` from OpenERP Apps', module_name)
content = urllib2.urlopen(url).read() content = urllib2.urlopen(url).read()
@ -726,8 +707,8 @@ class module(osv.osv):
finally: finally:
shutil.rmtree(tmp) shutil.rmtree(tmp)
def install_by_names(self, cr, uid, names, context=None): def get_apps_server(self, cr, uid, context=None):
raise NotImplementedError('# TODO') return tools.config.get('apps_server', 'https://apps.openerp.com/apps')
def _update_dependencies(self, cr, uid, mod_browse, depends=None): def _update_dependencies(self, cr, uid, mod_browse, depends=None):
if depends is None: if depends is None:

View File

@ -63,8 +63,8 @@ openerp.base = function(instance) {
if (instance.base.apps_client) { if (instance.base.apps_client) {
return check_client_available(instance.base.apps_client); return check_client_available(instance.base.apps_client);
} else { } else {
var ICP = new instance.web.Model('ir.config_parameter'); var Mod = new instance.web.Model('ir.module.module');
return ICP.call('get_param', ['apps.server', 'https://apps.openerp.com/apps']).then(function(u) { return Mod.call('get_apps_server').then(function(u) {
var link = $(_.str.sprintf('<a href="%s"></a>', u))[0]; var link = $(_.str.sprintf('<a href="%s"></a>', u))[0];
var host = _.str.sprintf('%s//%s', link.protocol, link.host); var host = _.str.sprintf('%s//%s', link.protocol, link.host);
var dbname = link.pathname; var dbname = link.pathname;

View File

@ -183,7 +183,7 @@ class test_expression(common.TransactionCase):
self.assertIn('res_partner_bank', sql_query[0], self.assertIn('res_partner_bank', sql_query[0],
"_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect main table") "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect main table")
expected = "%s like %s" % (unaccent('"res_partner_bank"."name"'), unaccent('%s')) expected = "%s::text like %s" % (unaccent('"res_partner_bank"."name"'), unaccent('%s'))
self.assertIn(expected, sql_query[1], self.assertIn(expected, sql_query[1],
"_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect where condition") "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect where condition")
@ -223,7 +223,7 @@ class test_expression(common.TransactionCase):
self.assertIn('"res_partner_bank" as "res_partner__bank_ids"', sql_query[0], self.assertIn('"res_partner_bank" as "res_partner__bank_ids"', sql_query[0],
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join") "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join")
expected = "%s like %s" % (unaccent('"res_partner__bank_ids"."name"'), unaccent('%s')) expected = "%s::text like %s" % (unaccent('"res_partner__bank_ids"."name"'), unaccent('%s'))
self.assertIn(expected, sql_query[1], self.assertIn(expected, sql_query[1],
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect where condition") "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect where condition")
@ -305,7 +305,7 @@ class test_expression(common.TransactionCase):
self.assertIn('"res_country"', sql_query[0], self.assertIn('"res_country"', sql_query[0],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table") "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
expected = "%s like %s" % (unaccent('"res_country"."code"'), unaccent('%s')) expected = "%s::text like %s" % (unaccent('"res_country"."code"'), unaccent('%s'))
self.assertIn(expected, sql_query[1], self.assertIn(expected, sql_query[1],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition") "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
@ -339,7 +339,7 @@ class test_expression(common.TransactionCase):
self.assertIn('"res_country" as "res_country_state__country_id"', sql_query[0], self.assertIn('"res_country" as "res_country_state__country_id"', sql_query[0],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join") "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join")
expected = "%s like %s" % (unaccent('"res_country_state__country_id"."code"'), unaccent('%s')) expected = "%s::text like %s" % (unaccent('"res_country_state__country_id"."code"'), unaccent('%s'))
self.assertIn(expected, sql_query[1], self.assertIn(expected, sql_query[1],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition") "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
@ -373,7 +373,7 @@ class test_expression(common.TransactionCase):
self.assertIn('"res_country" as "res_partner__state_id__country_id"', sql_query[0], self.assertIn('"res_country" as "res_partner__state_id__country_id"', sql_query[0],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join") "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
expected = "%s like %s" % (unaccent('"res_partner__state_id__country_id"."code"'), unaccent('%s')) expected = "%s::text like %s" % (unaccent('"res_partner__state_id__country_id"."code"'), unaccent('%s'))
self.assertIn(expected, sql_query[1], self.assertIn(expected, sql_query[1],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition") "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition")
@ -403,7 +403,7 @@ class test_expression(common.TransactionCase):
# Test produced queries that domains effectively present # Test produced queries that domains effectively present
sql_query = self.query_list[0].get_sql() sql_query = self.query_list[0].get_sql()
expected = "%s like %s" % (unaccent('"res_partner__child_ids__bank_ids"."acc_number"'), unaccent('%s')) expected = "%s::text like %s" % (unaccent('"res_partner__child_ids__bank_ids"."acc_number"'), unaccent('%s'))
self.assertIn(expected, sql_query[1], self.assertIn(expected, sql_query[1],
"_auto_join on one2many with domains incorrect result") "_auto_join on one2many with domains incorrect result")
# TDE TODO: check first domain has a correct table name # TDE TODO: check first domain has a correct table name

View File

@ -1182,14 +1182,15 @@ class expression(object):
else: else:
need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike') need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike')
sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator) sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator)
cast = '::text' if sql_operator.endswith('like') else ''
if left in model._columns: if left in model._columns:
format = need_wildcard and '%s' or model._columns[left]._symbol_set[0] format = need_wildcard and '%s' or model._columns[left]._symbol_set[0]
unaccent = self._unaccent if sql_operator.endswith('like') else lambda x: x unaccent = self._unaccent if sql_operator.endswith('like') else lambda x: x
column = '%s.%s' % (table_alias, _quote(left)) column = '%s.%s' % (table_alias, _quote(left))
query = '(%s %s %s)' % (unaccent(column), sql_operator, unaccent(format)) query = '(%s%s %s %s)' % (unaccent(column), cast, sql_operator, unaccent(format))
elif left in MAGIC_COLUMNS: elif left in MAGIC_COLUMNS:
query = "(%s.\"%s\" %s %%s)" % (table_alias, left, sql_operator) query = "(%s.\"%s\"%s %s %%s)" % (table_alias, left, cast, sql_operator)
params = right params = right
else: # Must not happen else: # Must not happen
raise ValueError("Invalid field %r in domain term %r" % (left, leaf)) raise ValueError("Invalid field %r in domain term %r" % (left, leaf))

View File

@ -167,7 +167,7 @@ class Cursor(object):
self.sql_log_count = 0 self.sql_log_count = 0
self.__closed = True # avoid the call of close() (by __del__) if an exception self.__closed = True # avoid the call of close() (by __del__) if an exception
# is raised by any of the following initialisations # is raised by any of the following initialisations
self._pool = pool self.__pool = pool
self.dbname = dbname self.dbname = dbname
# Whether to enable snapshot isolation level for this cursor. # Whether to enable snapshot isolation level for this cursor.
@ -313,7 +313,7 @@ class Cursor(object):
chosen_template = tools.config['db_template'] chosen_template = tools.config['db_template']
templates_list = tuple(set(['template0', 'template1', 'postgres', chosen_template])) templates_list = tuple(set(['template0', 'template1', 'postgres', chosen_template]))
keep_in_pool = self.dbname not in templates_list keep_in_pool = self.dbname not in templates_list
self._pool.give_back(self._cnx, keep_in_pool=keep_in_pool) self.__pool.give_back(self._cnx, keep_in_pool=keep_in_pool)
@check @check
def autocommit(self, on): def autocommit(self, on):
@ -484,12 +484,12 @@ class Connection(object):
def __init__(self, pool, dbname): def __init__(self, pool, dbname):
self.dbname = dbname self.dbname = dbname
self._pool = pool self.__pool = pool
def cursor(self, serialized=True): def cursor(self, serialized=True):
cursor_type = serialized and 'serialized ' or '' cursor_type = serialized and 'serialized ' or ''
_logger.debug('create %scursor to %r', cursor_type, self.dbname) _logger.debug('create %scursor to %r', cursor_type, self.dbname)
return Cursor(self._pool, self.dbname, serialized=serialized) return Cursor(self.__pool, self.dbname, serialized=serialized)
# serialized_cursor is deprecated - cursors are serialized by default # serialized_cursor is deprecated - cursors are serialized by default
serialized_cursor = cursor serialized_cursor = cursor

View File

@ -71,7 +71,8 @@ _SAFE_OPCODES = _EXPR_OPCODES.union(set(opmap[x] for x in [
'CONTINUE_LOOP', 'RAISE_VARARGS', 'CONTINUE_LOOP', 'RAISE_VARARGS',
# New in Python 2.7 - http://bugs.python.org/issue4715 : # New in Python 2.7 - http://bugs.python.org/issue4715 :
'JUMP_IF_FALSE_OR_POP', 'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE', 'JUMP_IF_FALSE_OR_POP', 'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE',
'POP_JUMP_IF_TRUE', 'SETUP_EXCEPT', 'END_FINALLY' 'POP_JUMP_IF_TRUE', 'SETUP_EXCEPT', 'END_FINALLY', 'LOAD_FAST',
'LOAD_GLOBAL', # Only allows access to restricted globals
] if x in opmap)) ] if x in opmap))
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -86,16 +87,65 @@ def _get_opcodes(codeobj):
[100, 100, 23, 100, 100, 102, 103, 83] [100, 100, 23, 100, 100, 102, 103, 83]
""" """
i = 0 i = 0
opcodes = []
byte_codes = codeobj.co_code byte_codes = codeobj.co_code
while i < len(byte_codes): while i < len(byte_codes):
code = ord(byte_codes[i]) code = ord(byte_codes[i])
opcodes.append(code) yield code
if code >= HAVE_ARGUMENT: if code >= HAVE_ARGUMENT:
i += 3 i += 3
else: else:
i += 1 i += 1
return opcodes
def assert_no_dunder_name(code_obj, expr):
""" assert_no_dunder_name(code_obj, expr) -> None
Asserts that the code object does not refer to any "dunder name"
(__$name__), so that safe_eval prevents access to any internal-ish Python
attribute or method (both are loaded via LOAD_ATTR which uses a name, not a
const or a var).
Checks that no such name exists in the provided code object (co_names).
:param code_obj: code object to name-validate
:type code_obj: CodeType
:param str expr: expression corresponding to the code object, for debugging
purposes
:raises NameError: in case a forbidden name (containing two underscores)
is found in ``code_obj``
.. note:: actually forbids every name containing 2 underscores
"""
for name in code_obj.co_names:
if "__" in name:
raise NameError('Access to forbidden name %r (%r)' % (name, expr))
def assert_valid_codeobj(allowed_codes, code_obj, expr):
""" Asserts that the provided code object validates against the bytecode
and name constraints.
Recursively validates the code objects stored in its co_consts in case
lambdas are being created/used (lambdas generate their own separated code
objects and don't live in the root one)
:param allowed_codes: list of permissible bytecode instructions
:type allowed_codes: set(int)
:param code_obj: code object to name-validate
:type code_obj: CodeType
:param str expr: expression corresponding to the code object, for debugging
purposes
:raises ValueError: in case of forbidden bytecode in ``code_obj``
:raises NameError: in case a forbidden name (containing two underscores)
is found in ``code_obj``
"""
assert_no_dunder_name(code_obj, expr)
for opcode in _get_opcodes(code_obj):
if opcode not in allowed_codes:
raise ValueError(
"opcode %s not allowed (%r)" % (opname[opcode], expr))
for const in code_obj.co_consts:
if isinstance(const, CodeType):
assert_valid_codeobj(allowed_codes, const, 'lambda')
def test_expr(expr, allowed_codes, mode="eval"): def test_expr(expr, allowed_codes, mode="eval"):
"""test_expr(expression, allowed_codes[, mode]) -> code_object """test_expr(expression, allowed_codes[, mode]) -> code_object
@ -110,15 +160,13 @@ def test_expr(expr, allowed_codes, mode="eval"):
# eval() does not like leading/trailing whitespace # eval() does not like leading/trailing whitespace
expr = expr.strip() expr = expr.strip()
code_obj = compile(expr, "", mode) code_obj = compile(expr, "", mode)
except (SyntaxError, TypeError): except (SyntaxError, TypeError, ValueError):
raise raise
except Exception, e: except Exception, e:
import sys import sys
exc_info = sys.exc_info() exc_info = sys.exc_info()
raise ValueError, '"%s" while compiling\n%r' % (ustr(e), expr), exc_info[2] raise ValueError, '"%s" while compiling\n%r' % (ustr(e), expr), exc_info[2]
for code in _get_opcodes(code_obj): assert_valid_codeobj(allowed_codes, code_obj, expr)
if code not in allowed_codes:
raise ValueError("opcode %s not allowed (%r)" % (opname[code], expr))
return code_obj return code_obj
@ -187,19 +235,13 @@ def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=Fal
This can be used to e.g. evaluate This can be used to e.g. evaluate
an OpenERP domain expression from an untrusted source. an OpenERP domain expression from an untrusted source.
Throws TypeError, SyntaxError or ValueError (not allowed) accordingly. :throws TypeError: If the expression provided is a code object
:throws SyntaxError: If the expression provided is not valid Python
>>> safe_eval("__import__('sys').modules") :throws NameError: If the expression provided accesses forbidden names
Traceback (most recent call last): :throws ValueError: If the expression provided uses forbidden bytecode
...
ValueError: opcode LOAD_NAME not allowed
""" """
if isinstance(expr, CodeType): if isinstance(expr, CodeType):
raise ValueError("safe_eval does not allow direct evaluation of code objects.") raise TypeError("safe_eval does not allow direct evaluation of code objects.")
if '__subclasses__' in expr:
raise ValueError('expression not allowed (__subclasses__)')
if globals_dict is None: if globals_dict is None:
globals_dict = {} globals_dict = {}

View File

@ -136,7 +136,7 @@ setuptools.setup(
'python-openid', 'python-openid',
'pytz', 'pytz',
'pyusb >= 1.0.0b1', 'pyusb >= 1.0.0b1',
'pywebdav', 'pywebdav < 0.9.8',
'pyyaml', 'pyyaml',
'qrcode', 'qrcode',
'reportlab', # windows binary pypi.python.org/pypi/reportlab 'reportlab', # windows binary pypi.python.org/pypi/reportlab