diff options
| author | Richard Mathot <rim@odoo.com> | 2018-08-14 14:31:11 +0200 |
|---|---|---|
| committer | Richard Mathot <rim@odoo.com> | 2018-08-14 14:31:11 +0200 |
| commit | c8a4f42ef8d2d03083471370244a8bf3855d9d76 (patch) | |
| tree | 73ab6838ad3ec830f59ea0af724e7aaab3c15c8d /addons/anonymization/wizard | |
| parent | c77d4effc428c03f07888578d760ede6f04d9a70 (diff) | |
[REM] anonymization: end of life of a broken module
task: 51094
Diffstat (limited to 'addons/anonymization/wizard')
| -rw-r--r-- | addons/anonymization/wizard/__init__.py | 4 | ||||
| -rw-r--r-- | addons/anonymization/wizard/anonymize_wizard.py | 332 | ||||
| -rw-r--r-- | addons/anonymization/wizard/anonymize_wizard_views.xml | 44 |
3 files changed, 0 insertions, 380 deletions
diff --git a/addons/anonymization/wizard/__init__.py b/addons/anonymization/wizard/__init__.py deleted file mode 100644 index fe8d7d1c643..00000000000 --- a/addons/anonymization/wizard/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import anonymize_wizard diff --git a/addons/anonymization/wizard/anonymize_wizard.py b/addons/anonymization/wizard/anonymize_wizard.py deleted file mode 100644 index 4ba71b03455..00000000000 --- a/addons/anonymization/wizard/anonymize_wizard.py +++ /dev/null @@ -1,332 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -import base64 -import json -import os -import random - -from lxml import etree -from operator import itemgetter - -from odoo import api, fields, models, _ -from odoo.exceptions import UserError -from odoo.release import version_info -from odoo.tools import pickle -from odoo.tools.safe_eval import safe_eval -from odoo.addons.anonymization.models.anonymization import group - -WIZARD_ANONYMIZATION_STATES = [('clear', 'Clear'), ('anonymized', 'Anonymized'), ('unstable', 'Unstable')] - - -class IrModelFieldsAnonymizeWizard(models.TransientModel): - _name = 'ir.model.fields.anonymize.wizard' - - name = fields.Char('File Name') - summary = fields.Text(compute='_compute_summary') - file_export = fields.Binary('Export') - file_import = fields.Binary('Import', - help="This is the file created by the anonymization process. It should have the extension '.json' or '.pickle'.") - state = fields.Selection(compute='_compute_state', string='Status', selection=WIZARD_ANONYMIZATION_STATES) - msg = fields.Text('Message') - - @api.multi - def _compute_summary(self): - for anonymize_wizard in self: - anonymize_wizard.summary = anonymize_wizard._get_summary_value() - - @api.multi - def _compute_state(self): - for anonymize_wizard in self: - anonymize_wizard.state = anonymize_wizard._get_state_value() - - @api.model - def _get_state_value(self): - return self.env['ir.model.fields.anonymization']._get_global_state() - - @api.model - def _get_summary_value(self): - summary = u'' - for anon_field in self.env['ir.model.fields.anonymization'].search([('state', '!=', 'not_existing')]): - field = anon_field.field_id - if field: - values = { - 'model_name': field.model_id.name, - 'model_code': field.model_id.model, - 'field_code': field.name, - 'field_name': field.field_description, - 'state': anon_field.state, - } - summary += u" * %(model_name)s (%(model_code)s) -> %(field_name)s (%(field_code)s): state: (%(state)s)\n" % values - else: - summary += u"* Missing local model (%s) and field (%s): state: (%s) \n" % (anon_field.model_name, anon_field.field_name, anon_field.state) - return summary - - @api.model - def default_get(self, fields_list): - res = {} - res['name'] = '.json' - res['summary'] = self._get_summary_value() - res['state'] = self._get_state_value() - res['msg'] = _("Before executing the anonymization process, you should make a backup of your database.") - return res - - @api.model - def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False): - state = self.env['ir.model.fields.anonymization']._get_global_state() - step = self.env.context.get('step', 'new_window') - res = super(IrModelFieldsAnonymizeWizard, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu) - eview = etree.fromstring(res['arch']) - placeholder = eview.xpath("group[@name='placeholder1']") - if len(placeholder): - placeholder = placeholder[0] - if step == 'new_window' and state == 'clear': - # clicked in the menu and the fields are not anonymized: warn the admin that backuping the db is very important - placeholder.addnext(etree.Element('field', {'name': 'msg', 'colspan': '4', 'nolabel': '1'})) - placeholder.addnext(etree.Element('newline')) - placeholder.addnext(etree.Element('label', {'string': 'Warning'})) - eview.remove(placeholder) - elif step == 'new_window' and state == 'anonymized': - # clicked in the menu and the fields are already anonymized - placeholder.addnext(etree.Element('newline')) - placeholder.addnext(etree.Element('field', {'name': 'file_import', 'required': "1"})) - placeholder.addnext(etree.Element('label', {'string': 'Anonymization file'})) - eview.remove(placeholder) - elif step == 'just_anonymized': - # we just ran the anonymization process, we need the file export field - placeholder.addnext(etree.Element('newline')) - placeholder.addnext(etree.Element('field', {'name': 'file_export'})) - # we need to remove the button: - buttons = eview.xpath("button") - for button in buttons: - eview.remove(button) - # and add a message: - placeholder.addnext(etree.Element('field', {'name': 'msg', 'colspan': '4', 'nolabel': '1'})) - placeholder.addnext(etree.Element('newline')) - placeholder.addnext(etree.Element('label', {'string': 'Result'})) - # remove the placeholer: - eview.remove(placeholder) - elif step == 'just_desanonymized': - # we just reversed the anonymization process, we don't need any field - # we need to remove the button - buttons = eview.xpath("button") - for button in buttons: - eview.remove(button) - # and add a message - placeholder.addnext(etree.Element('field', {'name': 'msg', 'colspan': '4', 'nolabel': '1'})) - placeholder.addnext(etree.Element('newline')) - placeholder.addnext(etree.Element('label', {'string': 'Result'})) - # remove the placeholer: - eview.remove(placeholder) - else: - raise UserError(_("The database anonymization is currently in an unstable state. Some fields are anonymized," - " while some fields are not anonymized. You should try to solve this problem before trying to do anything else.")) - res['arch'] = etree.tostring(eview, encoding='unicode') - return res - - @api.multi - def anonymize_database(self): - """Sets the 'anonymized' state to defined fields""" - self.ensure_one() - - # create a new history record: - history = self.env['ir.model.fields.anonymization.history'].create({ - 'date': fields.Datetime.now(), - 'state': 'started', - 'direction': 'clear -> anonymized' - }) - - # check that all the defined fields are in the 'clear' state - state = self.env['ir.model.fields.anonymization']._get_global_state() - error_type = _('Error !') - if state == 'anonymized': - raise UserError('%s: %s' % (error_type, _("The database is currently anonymized, you cannot anonymize it again."))) - elif state == 'unstable': - raise UserError('%s: %s' % (error_type, _("The database anonymization is currently in an unstable state. Some fields are anonymized," - " while some fields are not anonymized. You should try to solve this problem before trying to do anything."))) - - # do the anonymization: - dirpath = os.environ.get('HOME') or os.getcwd() - rel_filepath = 'field_anonymization_%s_%s.json' % (self.env.cr.dbname, history.id) - abs_filepath = os.path.abspath(os.path.join(dirpath, rel_filepath)) - - ano_fields = self.env['ir.model.fields.anonymization'].search([('state', '!=', 'not_existing')]) - if not ano_fields: - raise UserError('%s: %s' % (error_type, _("No fields are going to be anonymized."))) - - data = [] - - for field in ano_fields: - model_name = field.model_id.model - field_name = field.field_id.name - field_type = field.field_id.ttype - table_name = self.env[model_name]._table - - # get the current value - self.env.cr.execute('select id, "%s" from "%s"' % (field_name, table_name)) - for record in self.env.cr.dictfetchall(): - data.append({"model_id": model_name, "field_id": field_name, "id": record['id'], "value": record[field_name]}) - - # anonymize the value: - anonymized_value = None - - sid = str(record['id']) - if field_type == 'char': - anonymized_value = 'xxx' + sid - elif field_type == 'selection': - anonymized_value = 'xxx' + sid - elif field_type == 'text': - anonymized_value = 'xxx' + sid - elif field_type == 'html': - anonymized_value = 'xxx' + sid - elif field_type == 'boolean': - anonymized_value = random.choice([True, False]) - elif field_type == 'date': - anonymized_value = '2011-11-11' - elif field_type == 'datetime': - anonymized_value = '2011-11-11 11:11:11' - elif field_type in ('float', 'monetary'): - anonymized_value = 0.0 - elif field_type == 'integer': - anonymized_value = 0 - elif field_type in ['binary', 'many2many', 'many2one', 'one2many', 'reference']: # cannot anonymize these kind of fields - raise UserError('%s: %s' % (error_type, _("Cannot anonymize fields of these types: binary, many2many, many2one, one2many, reference."))) - - if anonymized_value is None: - raise UserError('%s: %s' % (error_type, _("Anonymized value can not be empty."))) - - sql = 'update "%(table)s" set "%(field)s" = %%(anonymized_value)s where id = %%(id)s' % { - 'table': table_name, - 'field': field_name, - } - self.env.cr.execute(sql, { - 'anonymized_value': anonymized_value, - 'id': record['id'] - }) - - # save json file: - with open(abs_filepath, 'w') as fn: - json.dump(data, fn) - - # update the anonymization fields: - ano_fields.write({'state': 'anonymized'}) - - # add a result message in the wizard: - msgs = ["Anonymization successful.", - "", - "Donot forget to save the resulting file to a safe place because you will not be able to revert the anonymization without this file.", - "", - "This file is also stored in the %s directory. The absolute file path is: %s." - ] - msg = '\n'.join(msgs) % (dirpath, abs_filepath) - - with open(abs_filepath, 'rb') as fn: - self.write({ - 'msg': msg, - 'file_export': base64.encodestring(fn.read()), - }) - - # update the history record: - history.write({ - 'field_ids': [[6, 0, ano_fields.ids]], - 'msg': msg, - 'filepath': abs_filepath, - 'state': 'done', - }) - - return { - 'res_id': self.id, - 'view_id': self.env.ref('anonymization.view_ir_model_fields_anonymize_wizard_form').ids, - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.model.fields.anonymize.wizard', - 'type': 'ir.actions.act_window', - 'context': {'step': 'just_anonymized'}, - 'target': 'new' - } - - @api.multi - def reverse_anonymize_database(self): - """Set the 'clear' state to defined fields""" - self.ensure_one() - IrModelFieldsAnonymization = self.env['ir.model.fields.anonymization'] - - # check that all the defined fields are in the 'anonymized' state - state = IrModelFieldsAnonymization._get_global_state() - if state == 'clear': - raise UserError(_("The database is not currently anonymized, you cannot reverse the anonymization.")) - elif state == 'unstable': - raise UserError(_("The database anonymization is currently in an unstable state. Some fields are anonymized," - " while some fields are not anonymized. You should try to solve this problem before trying to do anything.")) - - if not self.file_import: - raise UserError('%s: %s' % (_('Error !'), _("It is not possible to reverse the anonymization process without supplying the anonymization export file."))) - - # reverse the anonymization: - # load the json/pickle file content into a data structure: - content = base64.decodestring(self.file_import) - try: - data = json.loads(content.decode('utf8')) - except Exception: - # backward-compatible mode - data = pickle.loads(content, encoding='utf8') - - fixes = self.env['ir.model.fields.anonymization.migration.fix'].search_read([ - ('target_version', '=', '.'.join(str(v) for v in version_info[:2])) - ], ['model_name', 'field_name', 'query', 'query_type', 'sequence']) - fixes = group(fixes, ('model_name', 'field_name')) - - for line in data: - queries = [] - table_name = self.env[line['model_id']]._table if line['model_id'] in self.env else None - - # check if custom sql exists: - key = (line['model_id'], line['field_id']) - custom_updates = fixes.get(key) - if custom_updates: - custom_updates.sort(key=itemgetter('sequence')) - queries = [(record['query'], record['query_type']) for record in custom_updates if record['query_type']] - elif table_name: - queries = [('update "%(table)s" set "%(field)s" = %%(value)s where id = %%(id)s' % { - 'table': table_name, - 'field': line['field_id'], - }, 'sql')] - - for query in queries: - if query[1] == 'sql': - self.env.cr.execute(query[0], { - 'value': line['value'], - 'id': line['id'] - }) - elif query[1] == 'python': - safe_eval(query[0] % line) - else: - raise Exception("Unknown query type '%s'. Valid types are: sql, python." % (query['query_type'], )) - - # update the anonymization fields: - ano_fields = IrModelFieldsAnonymization.search([('state', '!=', 'not_existing')]) - ano_fields.write({'state': 'clear'}) - - # add a result message in the wizard: - self.msg = '\n'.join(["Successfully reversed the anonymization.", ""]) - - # create a new history record: - history = self.env['ir.model.fields.anonymization.history'].create({ - 'date': fields.Datetime.now(), - 'field_ids': [[6, 0, ano_fields.ids]], - 'msg': self.msg, - 'filepath': False, - 'direction': 'anonymized -> clear', - 'state': 'done' - }) - - return { - 'res_id': self.id, - 'view_id': self.env.ref('anonymization.view_ir_model_fields_anonymize_wizard_form').ids, - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.model.fields.anonymize.wizard', - 'type': 'ir.actions.act_window', - 'context': {'step': 'just_desanonymized'}, - 'target': 'new' - } diff --git a/addons/anonymization/wizard/anonymize_wizard_views.xml b/addons/anonymization/wizard/anonymize_wizard_views.xml deleted file mode 100644 index abe577e855b..00000000000 --- a/addons/anonymization/wizard/anonymize_wizard_views.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<odoo> - - <record id="view_ir_model_fields_anonymize_wizard_form" model="ir.ui.view"> - <field name="name">ir.model.fields.anonymize.wizard.form</field> - <field name="model">ir.model.fields.anonymize.wizard</field> - <field name="arch" type="xml"> - <form string="Database Anonymization"> - <header> - <button name="anonymize_database" - string="Anonymize Database" - type="object" - states="clear" /> - <button name="reverse_anonymize_database" - string="Reverse the Database Anonymization" - type="object" - states="anonymized" /> - <field name="state" widget="statusbar"/> - </header> - <field name="summary" readonly="0"/> - <group name="placeholder1" col="4"> - <field name="file_export" /> - <field name="file_import" /> - <field name="msg" /> - </group> - </form> - </field> - </record> - - <record id="action_ir_model_fields_anonymize_wizard" model="ir.actions.act_window"> - <field name="name">Anonymize Database</field> - <field name="res_model">ir.model.fields.anonymize.wizard</field> - <field name="target">new</field> - <field name="view_type">form</field> - <field name="view_mode">form</field> - </record> - - <menuitem id="menu_administration_anonymization_wizard" - action="action_ir_model_fields_anonymize_wizard" - name="Anonymize database" - parent="anonymization.menu_administration_anonymization" - sequence="30" /> - -</odoo> |
