diff --git a/airflow/migrations/versions/bba5a7cfc896_add_a_column_to_track_the_encryption_.py b/airflow/migrations/versions/bba5a7cfc896_add_a_column_to_track_the_encryption_.py new file mode 100644 index 0000000000000..cee0426ba3ebb --- /dev/null +++ b/airflow/migrations/versions/bba5a7cfc896_add_a_column_to_track_the_encryption_.py @@ -0,0 +1,24 @@ +"""Add a column to track the encryption state of the 'Extra' field in connection + +Revision ID: bba5a7cfc896 +Revises: bbc73705a13e +Create Date: 2016-01-29 15:10:32.656425 + +""" + +# revision identifiers, used by Alembic. +revision = 'bba5a7cfc896' +down_revision = 'bbc73705a13e' +branch_labels = None +depends_on = None + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('connection', sa.Column('is_extra_encrypted', sa.Boolean,default=False)) + + +def downgrade(): + op.drop_column('connection', 'is_extra_encrypted') diff --git a/airflow/models.py b/airflow/models.py index 373637391cd04..f0578c1906b7d 100644 --- a/airflow/models.py +++ b/airflow/models.py @@ -389,7 +389,8 @@ class Connection(Base): _password = Column('password', String(5000)) port = Column(Integer()) is_encrypted = Column(Boolean, unique=False, default=False) - extra = Column(String(5000)) + is_extra_encrypted = Column(Boolean, unique=False, default=False) + _extra = Column('extra', String(5000)) def __init__( self, conn_id=None, conn_type=None, @@ -446,6 +447,29 @@ def password(cls): return synonym('_password', descriptor=property(cls.get_password, cls.set_password)) + def get_extra(self): + if self._extra and self.is_extra_encrypted: + if not ENCRYPTION_ON: + raise AirflowException( + "Can't decrypt `extra`, configuration is missing") + return FERNET.decrypt(bytes(self._extra, 'utf-8')).decode() + else: + return self._extra + + def set_extra(self, value): + if value: + try: + self._extra = FERNET.encrypt(bytes(value, 'utf-8')).decode() + self.is_extra_encrypted = True + except NameError: + self._extra = value + self.is_extra_encrypted = False + + @declared_attr + def extra(cls): + return synonym('_extra', + descriptor=property(cls.get_extra, cls.set_extra)) + def get_hook(self): from airflow import hooks from airflow.contrib import hooks as contrib_hooks diff --git a/airflow/www/views.py b/airflow/www/views.py index 8fc8108ff0b62..0d5620b8a8f49 100644 --- a/airflow/www/views.py +++ b/airflow/www/views.py @@ -2045,9 +2045,10 @@ class ConnectionModelView(wwwutils.SuperUserMixin, AirflowModelView): verbose_name = "Connection" verbose_name_plural = "Connections" column_default_sort = ('conn_id', False) - column_list = ('conn_id', 'conn_type', 'host', 'port', 'is_encrypted',) + column_list = ('conn_id', 'conn_type', 'host', 'port', 'is_encrypted', 'is_extra_encrypted',) form_overrides = dict(_password=PasswordField) form_widget_args = { + 'is_extra_encrypted': {'disabled': True}, 'is_encrypted': {'disabled': True}, } # Used to customized the form, the forms elements get rendered @@ -2099,7 +2100,7 @@ def alert_fernet_key(cls): def is_secure(self): """ Used to display a message in the Connection list view making it clear - that the passwords can't be encrypted. + that the passwords and `extra` field can't be encrypted. """ is_secure = False try: