Allow integration with keychain
By adding options to change the compute and inverse methods for default fields
This commit is contained in:
parent
e8da1f3d25
commit
398b324b03
|
|
@ -16,7 +16,10 @@ _logger = logging.getLogger(__name__)
|
||||||
class ServerEnvMixin(models.AbstractModel):
|
class ServerEnvMixin(models.AbstractModel):
|
||||||
"""Mixin to add server environment in existing models
|
"""Mixin to add server environment in existing models
|
||||||
|
|
||||||
Usage::
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
class StorageBackend(models.Model):
|
class StorageBackend(models.Model):
|
||||||
_name = "storage.backend"
|
_name = "storage.backend"
|
||||||
|
|
@ -48,6 +51,49 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
Env-computed fields are conditionally editable, based on the absence
|
Env-computed fields are conditionally editable, based on the absence
|
||||||
of their key in environment configuration files. When edited, their
|
of their key in environment configuration files. When edited, their
|
||||||
value is stored in the database.
|
value is stored in the database.
|
||||||
|
|
||||||
|
Integration with keychain
|
||||||
|
-------------------------
|
||||||
|
The keychain addon is used account information, encrypting the password
|
||||||
|
with a key per environment.
|
||||||
|
|
||||||
|
The default behavior of server_environment is to store the default fields
|
||||||
|
in a serialized field, so the password would lend there unencrypted.
|
||||||
|
|
||||||
|
You can benefit from keychain by using custom compute/inverse methods to
|
||||||
|
get/set the password field:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
class StorageBackend(models.Model):
|
||||||
|
_name = 'storage.backend'
|
||||||
|
_inherit = ['keychain.backend', 'collection.base']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _server_env_fields(self):
|
||||||
|
base_fields = super()._server_env_fields
|
||||||
|
sftp_fields = {
|
||||||
|
"sftp_server": {},
|
||||||
|
"sftp_port": {'getter': "getint"},
|
||||||
|
"sftp_login": {},
|
||||||
|
"sftp_password": {
|
||||||
|
"no_default_field": True,
|
||||||
|
"compute_default": "_compute_password",
|
||||||
|
"inverse_default": "_inverse_password",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
sftp_fields.update(base_fields)
|
||||||
|
return sftp_fields
|
||||||
|
|
||||||
|
* ``no_default_field`` means that no new (sparse) field need to be
|
||||||
|
created, it already is provided by keychain
|
||||||
|
* ``compute_default`` is the name of the compute method to get the default
|
||||||
|
value when no key is set in the configuration files.
|
||||||
|
``_compute_password`` is implemented by ``keychain.backend``.
|
||||||
|
* ``inverse_default`` is the name of the compute method to set the default
|
||||||
|
value when it is editable. ``_inverse_password`` is implemented by
|
||||||
|
``keychain.backend``.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_name = 'server.env.mixin'
|
_name = 'server.env.mixin'
|
||||||
|
|
||||||
|
|
@ -64,10 +110,20 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
"getter": "getint",
|
"getter": "getint",
|
||||||
|
"no_default_field": True,
|
||||||
|
"compute_default": "_compute_password",
|
||||||
|
"inverse_default": "_inverse_password",
|
||||||
}
|
}
|
||||||
|
|
||||||
The configparser getter can be one of: get, getbool, getint.
|
* ``getter``: The configparser getter can be one of: get, getbool,
|
||||||
If options is an empty dict, "get" is used.
|
getint. Default is "get".
|
||||||
|
* ``no_default_field``: disable creation of a field for storing
|
||||||
|
the default value, must be used with ``compute_default`` and
|
||||||
|
``inverse_default``
|
||||||
|
* ``compute_default``: name of a compute method to get the default
|
||||||
|
value when no key is present in configuration files
|
||||||
|
* ``inverse_default``: name of an inverse method to set the default
|
||||||
|
value when the value is editable
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
|
|
@ -146,6 +202,23 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
)
|
)
|
||||||
return has_global_config or has_config
|
return has_global_config or has_config
|
||||||
|
|
||||||
|
def _compute_server_env_from_config(self, field_name, options):
|
||||||
|
getter_name = options.get('getter', 'get')
|
||||||
|
value = self._server_env_read_from_config(
|
||||||
|
field_name, getter_name
|
||||||
|
)
|
||||||
|
self[field_name] = value
|
||||||
|
|
||||||
|
def _compute_server_env_from_default(self, field_name, options):
|
||||||
|
if options.get('compute_default'):
|
||||||
|
getattr(self, options['compute_default'])()
|
||||||
|
else:
|
||||||
|
default_field = self._server_env_default_fieldname(
|
||||||
|
field_name
|
||||||
|
)
|
||||||
|
if default_field:
|
||||||
|
self[field_name] = self[default_field]
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _compute_server_env(self):
|
def _compute_server_env(self):
|
||||||
"""Read values from environment configuration files
|
"""Read values from environment configuration files
|
||||||
|
|
@ -156,28 +229,25 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
for record in self:
|
for record in self:
|
||||||
for field_name, options in self._server_env_fields.items():
|
for field_name, options in self._server_env_fields.items():
|
||||||
if record._server_env_has_key_defined(field_name):
|
if record._server_env_has_key_defined(field_name):
|
||||||
getter_name = options.get('getter', 'get')
|
self._compute_server_env_from_config(field_name, options)
|
||||||
value = record._server_env_read_from_config(
|
|
||||||
field_name, getter_name
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
default_field = record._server_env_default_fieldname(
|
self._compute_server_env_from_default(field_name, options)
|
||||||
field_name
|
|
||||||
)
|
|
||||||
value = record[default_field]
|
|
||||||
|
|
||||||
record[field_name] = value
|
|
||||||
|
|
||||||
def _inverse_server_env(self, field_name):
|
def _inverse_server_env(self, field_name):
|
||||||
|
options = self._server_env_fields[field_name]
|
||||||
default_field = self._server_env_default_fieldname(field_name)
|
default_field = self._server_env_default_fieldname(field_name)
|
||||||
is_editable_field = self._server_env_is_editable_fieldname(field_name)
|
is_editable_field = self._server_env_is_editable_fieldname(field_name)
|
||||||
|
|
||||||
for record in self:
|
for record in self:
|
||||||
# when we write in an env-computed field, if it is editable
|
# when we write in an env-computed field, if it is editable
|
||||||
# we update the default value in database
|
# we update the default value in database
|
||||||
|
|
||||||
if record[is_editable_field]:
|
if record[is_editable_field]:
|
||||||
record[default_field] = record[field_name]
|
if options.get('inverse_default'):
|
||||||
|
getattr(record, options['inverse_default'])()
|
||||||
|
elif default_field:
|
||||||
|
record[default_field] = record[field_name]
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _compute_server_env_is_editable(self):
|
def _compute_server_env_is_editable(self):
|
||||||
|
|
@ -233,6 +303,9 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
|
|
||||||
def _server_env_default_fieldname(self, base_field_name):
|
def _server_env_default_fieldname(self, base_field_name):
|
||||||
"""Return the name of the field with default value"""
|
"""Return the name of the field with default value"""
|
||||||
|
options = self._server_env_fields[base_field_name]
|
||||||
|
if options.get('no_default_field'):
|
||||||
|
return ''
|
||||||
return '%s_env_default' % (base_field_name,)
|
return '%s_env_default' % (base_field_name,)
|
||||||
|
|
||||||
def _server_env_is_editable_fieldname(self, base_field_name):
|
def _server_env_is_editable_fieldname(self, base_field_name):
|
||||||
|
|
@ -284,6 +357,8 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
The field is stored in the serialized field ``server_env_defaults``.
|
The field is stored in the serialized field ``server_env_defaults``.
|
||||||
"""
|
"""
|
||||||
fieldname = self._server_env_default_fieldname(base_field.name)
|
fieldname = self._server_env_default_fieldname(base_field.name)
|
||||||
|
if not fieldname:
|
||||||
|
return
|
||||||
if fieldname not in self._fields:
|
if fieldname not in self._fields:
|
||||||
base_field_cls = base_field.__class__
|
base_field_cls = base_field.__class__
|
||||||
field_args = base_field.args.copy()
|
field_args = base_field.args.copy()
|
||||||
|
|
@ -301,7 +376,7 @@ class ServerEnvMixin(models.AbstractModel):
|
||||||
@api.model
|
@api.model
|
||||||
def _setup_base(self):
|
def _setup_base(self):
|
||||||
super()._setup_base()
|
super()._setup_base()
|
||||||
for fieldname in self._server_env_fields:
|
for fieldname, options in self._server_env_fields.items():
|
||||||
field = self._fields[fieldname]
|
field = self._fields[fieldname]
|
||||||
self._server_env_add_default_field(field)
|
self._server_env_add_default_field(field)
|
||||||
self._server_env_transform_field_to_read_from_env(field)
|
self._server_env_transform_field_to_read_from_env(field)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue