mirror of
https://github.com/ansible/awx.git
synced 2026-01-14 19:30:39 -03:30
encrypt password answers on config save
This commit is contained in:
parent
9c783aa0ce
commit
74bf058d62
@ -44,7 +44,7 @@ from awx.main.fields import ImplicitRoleField
|
||||
from awx.main.utils import (
|
||||
get_type_for_model, get_model_for_type, timestamp_apiformat,
|
||||
camelcase_to_underscore, getattrd, parse_yaml_or_json,
|
||||
has_model_field_prefetched, extract_ansible_vars)
|
||||
has_model_field_prefetched, extract_ansible_vars, encrypt_dict)
|
||||
from awx.main.utils.filters import SmartFilter
|
||||
from awx.main.redact import REPLACE_STR
|
||||
|
||||
@ -3140,7 +3140,6 @@ class LaunchConfigurationBaseSerializer(BaseSerializer):
|
||||
attrs['char_prompts'] = mock_obj.char_prompts
|
||||
|
||||
# Insert survey_passwords to track redacted variables
|
||||
# TODO: perform encryption on save
|
||||
if 'extra_data' in attrs:
|
||||
extra_data = parse_yaml_or_json(attrs.get('extra_data', {}))
|
||||
if hasattr(ujt, 'survey_password_variables'):
|
||||
@ -3150,6 +3149,20 @@ class LaunchConfigurationBaseSerializer(BaseSerializer):
|
||||
password_dict[key] = REPLACE_STR
|
||||
if not self.instance or password_dict != self.instance.survey_passwords:
|
||||
attrs['survey_passwords'] = password_dict
|
||||
if not isinstance(attrs['extra_data'], dict):
|
||||
attrs['extra_data'] = parse_yaml_or_json(attrs['extra_data'])
|
||||
encrypt_dict(attrs['extra_data'], password_dict.keys())
|
||||
if self.instance:
|
||||
db_extra_data = parse_yaml_or_json(self.instance.extra_data)
|
||||
else:
|
||||
db_extra_data = {}
|
||||
for key in password_dict.keys():
|
||||
if attrs['extra_data'].get(key, None) == REPLACE_STR:
|
||||
if key not in db_extra_data:
|
||||
raise serializers.ValidationError(
|
||||
_('Provided variable {} has no database value to replace with.').format(key))
|
||||
else:
|
||||
attrs['extra_data'][key] = db_extra_data[key]
|
||||
return attrs
|
||||
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ from django_celery_results.models import TaskResult
|
||||
from awx.main.models.base import * # noqa
|
||||
from awx.main.models.mixins import ResourceMixin, TaskManagerUnifiedJobMixin
|
||||
from awx.main.utils import (
|
||||
encrypt_value, decrypt_field, _inventory_updates,
|
||||
encrypt_dict, decrypt_field, _inventory_updates,
|
||||
copy_model_by_class, copy_m2m_relationships,
|
||||
get_type_for_model, parse_yaml_or_json
|
||||
)
|
||||
@ -349,11 +349,7 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, Notificatio
|
||||
# automatically encrypt survey fields
|
||||
if hasattr(self, 'survey_spec') and getattr(self, 'survey_enabled', False):
|
||||
password_list = self.survey_password_variables()
|
||||
for key in kwargs.get('extra_vars', {}):
|
||||
if key in password_list:
|
||||
kwargs['extra_vars'][key] = encrypt_value(
|
||||
kwargs['extra_vars'][key]
|
||||
)
|
||||
encrypt_dict(kwargs.get('extra_vars', {}), password_list)
|
||||
|
||||
unified_job_class = self._get_unified_job_class()
|
||||
fields = self._get_unified_job_field_names()
|
||||
|
||||
@ -177,6 +177,8 @@ class TestWorkflowJobTemplateNodeSerializerSurveyPasswords():
|
||||
})
|
||||
assert 'survey_passwords' in attrs
|
||||
assert 'var1' in attrs['survey_passwords']
|
||||
assert attrs['extra_data']['var1'].startswith('$encrypted$')
|
||||
assert len(attrs['extra_data']['var1']) > len('$encrypted$')
|
||||
|
||||
def test_set_survey_passwords_modify(self, jt):
|
||||
serializer = WorkflowJobTemplateNodeSerializer()
|
||||
@ -192,6 +194,25 @@ class TestWorkflowJobTemplateNodeSerializerSurveyPasswords():
|
||||
})
|
||||
assert 'survey_passwords' in attrs
|
||||
assert 'var1' in attrs['survey_passwords']
|
||||
assert attrs['extra_data']['var1'].startswith('$encrypted$')
|
||||
assert len(attrs['extra_data']['var1']) > len('$encrypted$')
|
||||
|
||||
def test_use_db_answer(self, jt):
|
||||
serializer = WorkflowJobTemplateNodeSerializer()
|
||||
wfjt = WorkflowJobTemplate(name='fake-wfjt')
|
||||
serializer.instance = WorkflowJobTemplateNode(
|
||||
workflow_job_template=wfjt,
|
||||
unified_job_template=jt,
|
||||
extra_data={'var1': '$encrypted$foooooo'}
|
||||
)
|
||||
attrs = serializer.validate({
|
||||
'unified_job_template': jt,
|
||||
'workflow_job_template': wfjt,
|
||||
'extra_data': {'var1': '$encrypted$'}
|
||||
})
|
||||
assert 'survey_passwords' in attrs
|
||||
assert 'var1' in attrs['survey_passwords']
|
||||
assert attrs['extra_data']['var1'] == '$encrypted$foooooo'
|
||||
|
||||
|
||||
@mock.patch('awx.api.serializers.WorkflowJobTemplateNodeSerializer.get_related', lambda x,y: {})
|
||||
|
||||
@ -9,8 +9,10 @@ from cryptography.hazmat.backends import default_backend
|
||||
from django.utils.encoding import smart_str
|
||||
|
||||
|
||||
__all__ = ['get_encryption_key', 'encrypt_value', 'encrypt_field',
|
||||
'decrypt_field', 'decrypt_value']
|
||||
__all__ = ['get_encryption_key',
|
||||
'encrypt_field', 'decrypt_field',
|
||||
'encrypt_value', 'decrypt_value',
|
||||
'encrypt_dict']
|
||||
|
||||
logger = logging.getLogger('awx.main.utils.encryption')
|
||||
|
||||
@ -125,3 +127,13 @@ def decrypt_field(instance, field_name, subfield=None):
|
||||
exc_info=True
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
def encrypt_dict(data, fields):
|
||||
'''
|
||||
Encrypts all of the dictionary values in `data` under the keys in `fields`
|
||||
in-place operation on `data`
|
||||
'''
|
||||
encrypt_fields = set(data.keys()).intersection(fields)
|
||||
for key in encrypt_fields:
|
||||
data[key] = encrypt_value(data[key])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user