crypto.py 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. #
  2. # Licensed to the Apache Software Foundation (ASF) under one
  3. # or more contributor license agreements. See the NOTICE file
  4. # distributed with this work for additional information
  5. # regarding copyright ownership. The ASF licenses this file
  6. # to you under the Apache License, Version 2.0 (the
  7. # "License"); you may not use this file except in compliance
  8. # with the License. You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing,
  13. # software distributed under the License is distributed on an
  14. # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. # KIND, either express or implied. See the License for the
  16. # specific language governing permissions and limitations
  17. # under the License.
  18. from __future__ import annotations
  19. import logging
  20. from airflow.configuration import conf
  21. from airflow.exceptions import AirflowException
  22. from airflow.typing_compat import Protocol
  23. log = logging.getLogger(__name__)
  24. class FernetProtocol(Protocol):
  25. """This class is only used for TypeChecking (for IDEs, mypy, etc)."""
  26. def decrypt(self, b):
  27. """Decrypt with Fernet."""
  28. def encrypt(self, b):
  29. """Encrypt with Fernet."""
  30. class NullFernet:
  31. """
  32. A "Null" encryptor class that doesn't encrypt or decrypt but that presents a similar interface to Fernet.
  33. The purpose of this is to make the rest of the code not have to know the
  34. difference, and to only display the message once, not 20 times when
  35. `airflow db migrate` is run.
  36. """
  37. is_encrypted = False
  38. def decrypt(self, b):
  39. """Decrypt with Fernet."""
  40. return b
  41. def encrypt(self, b):
  42. """Encrypt with Fernet."""
  43. return b
  44. _fernet: FernetProtocol | None = None
  45. def get_fernet():
  46. """
  47. Deferred load of Fernet key.
  48. This function could fail either because Cryptography is not installed
  49. or because the Fernet key is invalid.
  50. :return: Fernet object
  51. :raises: airflow.exceptions.AirflowException if there's a problem trying to load Fernet
  52. """
  53. from cryptography.fernet import Fernet, MultiFernet
  54. global _fernet
  55. if _fernet:
  56. return _fernet
  57. try:
  58. fernet_key = conf.get("core", "FERNET_KEY")
  59. if not fernet_key:
  60. log.warning("empty cryptography key - values will not be stored encrypted.")
  61. _fernet = NullFernet()
  62. else:
  63. _fernet = MultiFernet(
  64. [Fernet(fernet_part.encode("utf-8")) for fernet_part in fernet_key.split(",")]
  65. )
  66. _fernet.is_encrypted = True
  67. except (ValueError, TypeError) as value_error:
  68. raise AirflowException(f"Could not create Fernet object: {value_error}")
  69. return _fernet