Skip to content
Snippets Groups Projects
Commit 64d52cf8 authored by Adrian Torres's avatar Adrian Torres Committed by GitHub
Browse files

[IMP] base_import_module: Add layer of security (#19175)

Previous to this commit, anyone could import studio customizations into
a community db, this was troublesome since some people would just
install studio, do their customizations and then move them into a
community db, not paying anything at all.

This commit fixes this by checking whether the exported views were
created with studio or not and if studio is installed
parent c418d734
No related branches found
No related tags found
No related merge requests found
# -*- coding: utf-8 -*-
import ast
import base64
import logging
import lxml
import os
import sys
import zipfile
......@@ -32,8 +34,17 @@ class IrModule(models.Model):
values = self.get_values_from_terp(terp)
unmet_dependencies = set(terp['depends']).difference(installed_mods)
if unmet_dependencies:
raise UserError(_("Unmet module dependencies: %s") % ', '.join(unmet_dependencies))
if _is_studio_custom(path):
err = _("Studio customizations require Studio")
else:
err = _("Unmet module dependencies: %s") % ', '.join(
unmet_dependencies,
)
raise UserError(err)
elif 'web_studio' not in installed_mods and _is_studio_custom(path):
raise UserError(_("Studio customizations require Studio"))
mod = known_mods_names.get(module)
if mod:
......@@ -52,7 +63,7 @@ class IrModule(models.Model):
continue
_logger.info("module %s: loading %s", module, filename)
noupdate = False
if filename.endswith('.csv') and kind in ('init', 'init_xml'):
if ext == '.csv' and kind in ('init', 'init_xml'):
noupdate = True
pathname = opj(path, filename)
idref = {}
......@@ -101,9 +112,9 @@ class IrModule(models.Model):
raise UserError(_("File '%s' exceed maximum allowed file size") % zf.filename)
with tempdir() as module_dir:
import odoo.modules as addons
import odoo.modules.module as module
try:
addons.module.ad_paths.append(module_dir)
module.ad_paths.append(module_dir)
z.extractall(module_dir)
dirs = [d for d in os.listdir(module_dir) if os.path.isdir(opj(module_dir, d))]
for mod_name in dirs:
......@@ -117,8 +128,38 @@ class IrModule(models.Model):
_logger.exception('Error while importing module')
errors[mod_name] = exception_to_unicode(e)
finally:
addons.module.ad_paths.remove(module_dir)
module.ad_paths.remove(module_dir)
r = ["Successfully imported module '%s'" % mod for mod in success]
for mod, error in errors.items():
r.append("Error while importing module '%s': %r" % (mod, error))
return '\n'.join(r), module_names
def _is_studio_custom(path):
"""
Checks the to-be-imported records to see if there are any references to
studio, which would mean that the module was created using studio
Returns True if any of the records contains a context with the key
studio in it, False if none of the records do
"""
path = os.path.join(path, 'data')
filenames = next(iter(os.walk(path)))[2]
filenames = [f for f in filenames if f.lower().endswith('.xml')]
for filename in filenames:
root = lxml.etree.parse(os.path.join(path, filename)).getroot()
for record in root:
# there might not be a context if it's a non-studio module
try:
# ast.literal_eval is like eval(), but safer
# context is a string representing a python dict
ctx = ast.literal_eval(record.get('context'))
# there are no cases in which studio is false
# so just checking for its existence is enough
if ctx and ctx.get('studio'):
return True
except Exception:
continue
return False
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment