diff --git a/addons/cetmix_tower_server/models/cx_tower_key_value.py b/addons/cetmix_tower_server/models/cx_tower_key_value.py new file mode 100644 index 0000000..faa4115 --- /dev/null +++ b/addons/cetmix_tower_server/models/cx_tower_key_value.py @@ -0,0 +1,112 @@ +# Copyright (C) 2022 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class CxTowerKeyValue(models.Model): + """Secret value storage""" + + _name = "cx.tower.key.value" + _inherit = [ + "cx.tower.reference.mixin", + "cx.tower.vault.mixin", + ] + _description = "Cetmix Tower Secret Value Storage" + + SECRET_FIELDS = ["secret_value"] + + name = fields.Char(related="key_id.name", readonly=False) + key_id = fields.Many2one( + comodel_name="cx.tower.key", + string="Key", + required=True, + ondelete="cascade", + domain="[('key_type', '=', 's')]", + ) + server_id = fields.Many2one( + comodel_name="cx.tower.server", + ondelete="cascade", + help="Server to which the key belongs", + ) + partner_id = fields.Many2one( + comodel_name="res.partner", + ondelete="cascade", + help="Partner to which the key belongs", + ) + is_global = fields.Boolean( + string="Global", + compute="_compute_is_global", + help="This value is applicable to all servers and partners", + ) + secret_value = fields.Text() + + @api.depends("server_id", "partner_id") + def _compute_is_global(self): + for record in self: + record.is_global = not record.server_id and not record.partner_id + + @api.constrains("key_id", "server_id", "partner_id") + def _check_key_id(self): + for rec in self: + if not rec.key_id: + continue + # Only keys of type 'secret' can have custom secret values + if rec.key_id.key_type != "s": + raise ValidationError( + _( + "Custom secret values can be defined" + " only for key type 'secret'" + ) + ) + # Only one global secret value can be defined for a key + global_values = rec.key_id.value_ids.filtered( + lambda x, rec=rec: not x.server_id and not x.partner_id + ) + if len(global_values) > 1: + raise ValidationError( + _("Only one global secret value can be defined for a key") + ) + # Only one secret value can be defined for a server and partner + server_partner_values = rec.key_id.value_ids.filtered( + lambda x, rec=rec: x.server_id == rec.server_id + and x.partner_id == rec.partner_id + ) + if len(server_partner_values) > 1: + raise ValidationError( + _( + "Only one secret value can be defined" + " for a server and partner" + ) + ) + # Only one secret value can be defined for a server + server_values = rec.key_id.value_ids.filtered( + lambda x, rec=rec: x.server_id == rec.server_id and not x.partner_id + ) + if len(server_values) > 1: + raise ValidationError( + _("Only one secret value can be defined for a server") + ) + # Only one secret value can be defined for a partner + partner_values = rec.key_id.value_ids.filtered( + lambda x, rec=rec: x.partner_id == rec.partner_id and not x.server_id + ) + if len(partner_values) > 1: + raise ValidationError( + _("Only one secret value can be defined for a partner") + ) + + @api.returns("self", lambda value: value.id) + def copy(self, default=None): + """Copy key value. Ensure secret value is copied. + + Args: + default (dict, optional): Default values. Defaults to None. + + Returns: + self: Copied key value + """ + default = default or {} + default["secret_value"] = self._get_secret_value("secret_value") + return super().copy(default=default)