diff --git a/awx/conf/migrations/_reencrypt.py b/awx/conf/migrations/_reencrypt.py index ca19a9234c..7e3fe893b1 100644 --- a/awx/conf/migrations/_reencrypt.py +++ b/awx/conf/migrations/_reencrypt.py @@ -102,4 +102,6 @@ def encrypt_field(instance, field_name, ask=False, subfield=None, skip_utf8=Fals def should_decrypt_field(value): - return value.startswith('$encrypted$') and '$AESCBC$' not in value + if hasattr(value, 'startswith'): + return value.startswith('$encrypted$') and '$AESCBC$' not in value + return False diff --git a/awx/conf/settings.py b/awx/conf/settings.py index 68f63070e3..57bd9b26fe 100644 --- a/awx/conf/settings.py +++ b/awx/conf/settings.py @@ -22,6 +22,7 @@ from awx.main.utils import encrypt_field, decrypt_field from awx.main.utils.db import get_tower_migration_version from awx.conf import settings_registry from awx.conf.models import Setting +from awx.conf.migrations._reencrypt import decrypt_field as old_decrypt_field # FIXME: Gracefully handle when settings are accessed before the database is # ready (or during migrations). @@ -245,7 +246,13 @@ class SettingsWrapper(UserSettingsHolder): if settings_to_cache[setting.key] != SETTING_CACHE_NOTSET: continue if self.registry.is_setting_encrypted(setting.key): - value = decrypt_field(setting, 'value') + try: + value = decrypt_field(setting, 'value') + except ValueError, e: + #TODO: Remove in Tower 3.3 + logger.debug('encountered error decrypting field: %s - attempting fallback to old', e) + value = old_decrypt_field(setting, 'value') + else: value = setting.value settings_to_cache[setting.key] = get_cache_value(value) diff --git a/awx/main/migrations/_reencrypt.py b/awx/main/migrations/_reencrypt.py index b246ea64c8..03fc2e3d93 100644 --- a/awx/main/migrations/_reencrypt.py +++ b/awx/main/migrations/_reencrypt.py @@ -1,13 +1,32 @@ +from django.utils.translation import ugettext_lazy as _ + from awx.main import utils from awx.conf.migrations._reencrypt import ( decrypt_field, should_decrypt_field, ) +from awx.main.notifications.email_backend import CustomEmailBackend +from awx.main.notifications.slack_backend import SlackBackend +from awx.main.notifications.twilio_backend import TwilioBackend +from awx.main.notifications.pagerduty_backend import PagerDutyBackend +from awx.main.notifications.hipchat_backend import HipChatBackend +from awx.main.notifications.webhook_backend import WebhookBackend +from awx.main.notifications.irc_backend import IrcBackend +from awx.main.models.credential import Credential __all__ = ['replace_aesecb_fernet'] +NOTIFICATION_TYPES = [('email', _('Email'), CustomEmailBackend), + ('slack', _('Slack'), SlackBackend), + ('twilio', _('Twilio'), TwilioBackend), + ('pagerduty', _('Pagerduty'), PagerDutyBackend), + ('hipchat', _('HipChat'), HipChatBackend), + ('webhook', _('Webhook'), WebhookBackend), + ('irc', _('IRC'), IrcBackend)] + + def replace_aesecb_fernet(apps, schema_editor): _notification_templates(apps) _credentials(apps) @@ -17,8 +36,10 @@ def replace_aesecb_fernet(apps, schema_editor): def _notification_templates(apps): NotificationTemplate = apps.get_model('main', 'NotificationTemplate') for nt in NotificationTemplate.objects.all(): - for field in filter(lambda x: nt.notification_class.init_parameters[x]['type'] == "password", - nt.notification_class.init_parameters): + CLASS_FOR_NOTIFICATION_TYPE = dict([(x[0], x[2]) for x in NOTIFICATION_TYPES]) + notification_class = CLASS_FOR_NOTIFICATION_TYPE[nt.notification_type] + for field in filter(lambda x: notification_class.init_parameters[x]['type'] == "password", + notification_class.init_parameters): if should_decrypt_field(nt.notification_configuration[field]): value = decrypt_field(nt, 'notification_configuration', subfield=field) nt.notification_configuration[field] = value @@ -26,21 +47,14 @@ def _notification_templates(apps): def _credentials(apps): - # this monkey-patch is necessary to make the implicit role generation save - # signal use the correct Role model (the version active at this point in - # migration, not the one at HEAD) - orig_current_apps = utils.get_current_apps - try: - utils.get_current_apps = lambda: apps - for credential in apps.get_model('main', 'Credential').objects.all(): - for field_name, value in credential.inputs.items(): - if should_decrypt_field(value): - value = decrypt_field(credential, field_name) - credential.inputs[field_name] = value - credential.save() - finally: - utils.get_current_apps = orig_current_apps - + # TODO: Try to not use the model directly imported from our + # source (should use apps.get_model) to make the migration less britle. + for credential in Credential.objects.all(): + for field_name, value in credential.inputs.items(): + if should_decrypt_field(value): + value = decrypt_field(credential, field_name) + credential.inputs[field_name] = value + credential.save() def _unified_jobs(apps):