fdc-shared-kernel 0.0.184__tar.gz → 0.0.186__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/PKG-INFO +1 -1
  2. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/fdc_shared_kernel.egg-info/PKG-INFO +1 -1
  3. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/fdc_shared_kernel.egg-info/SOURCES.txt +5 -0
  4. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/pyproject.toml +1 -1
  5. fdc_shared_kernel-0.0.186/shared_kernel/data_warehouse_handlers/connections/redshift_warehouse_connection.py +302 -0
  6. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/interfaces/__init__.py +1 -0
  7. fdc_shared_kernel-0.0.186/shared_kernel/interfaces/encryption.py +33 -0
  8. fdc_shared_kernel-0.0.186/shared_kernel/security/__init__.py +2 -0
  9. fdc_shared_kernel-0.0.186/shared_kernel/security/encryption/__init__.py +4 -0
  10. fdc_shared_kernel-0.0.186/shared_kernel/security/encryption/manager.py +84 -0
  11. fdc_shared_kernel-0.0.186/shared_kernel/security/encryption/providers.py +96 -0
  12. fdc_shared_kernel-0.0.186/shared_kernel/tests/security/test_encryption.py +105 -0
  13. fdc_shared_kernel-0.0.184/shared_kernel/data_warehouse_handlers/connections/redshift_warehouse_connection.py +0 -178
  14. fdc_shared_kernel-0.0.184/shared_kernel/security/__init__.py +0 -1
  15. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/README.md +0 -0
  16. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/README_pypi.md +0 -0
  17. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/fdc_shared_kernel.egg-info/dependency_links.txt +0 -0
  18. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/fdc_shared_kernel.egg-info/requires.txt +0 -0
  19. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/fdc_shared_kernel.egg-info/top_level.txt +0 -0
  20. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/requirements.txt +0 -0
  21. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/setup.cfg +0 -0
  22. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/__init__.py +0 -0
  23. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/agent_communication_channel/__init__.py +0 -0
  24. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/agent_communication_channel/agent_channel.py +0 -0
  25. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/agent_communication_channel/contexts.py +0 -0
  26. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/agent_communication_channel/event_channel.py +0 -0
  27. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/ai_audit_log/__init__.py +0 -0
  28. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/ai_chat_persistence/__init__.py +0 -0
  29. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/ai_chat_persistence/chat_persistence.py +0 -0
  30. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/async_task_executor/__init__.py +0 -0
  31. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/async_task_executor/async_task_executor.py +0 -0
  32. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/auth/__init__.py +0 -0
  33. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/auth/jwt_helper.py +0 -0
  34. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/auth/token_handler.py +0 -0
  35. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/auth/workbook_permission_handler.py +0 -0
  36. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/batch_job/__init__.py +0 -0
  37. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/batch_job/batch_job_status_tracker/container_job_status_tracker.py +0 -0
  38. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/batch_job/job_handlers/__init__.py +0 -0
  39. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/batch_job/job_handlers/aws_batch_job_handler.py +0 -0
  40. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/batch_job/job_handlers/azure_apps_job_handler.py +0 -0
  41. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/batch_job/job_handlers/job_handler_factory.py +0 -0
  42. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/batch_job/job_handlers/job_status_mapper.py +0 -0
  43. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/batch_job/job_storage_managers/__init__.py +0 -0
  44. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/batch_job/job_storage_managers/azure_blob_storage_manager.py +0 -0
  45. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/batch_job/job_storage_managers/job_storage_manager_factory.py +0 -0
  46. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/batch_job/job_storage_managers/s3_job_storage_manager.py +0 -0
  47. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/config/__init__.py +0 -0
  48. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/constants/constants.py +0 -0
  49. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/__init__.py +0 -0
  50. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/connections/databricks_warehouse_connection.py +0 -0
  51. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/connections/jdbc_warehouse_connection.py +0 -0
  52. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/connections/mssql_warehouse_connection.py +0 -0
  53. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/connections/mysql_warehouse_connection.py +0 -0
  54. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/connections/postgresql_warehouse_connection.py +0 -0
  55. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/connections/snowflake_warehouse_connection.py +0 -0
  56. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/custom_query_builders/databricks_query.py +0 -0
  57. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/databricks_handler.py +0 -0
  58. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/factory.py +0 -0
  59. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/jdbc_driver_registry.py +0 -0
  60. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/jdbc_handler.py +0 -0
  61. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/mssql_handler.py +0 -0
  62. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/mysql_handler.py +0 -0
  63. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/odbc_handler.py +0 -0
  64. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/postgresql_handler.py +0 -0
  65. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/query_executors/databricks_query_executor.py +0 -0
  66. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/query_executors/jdbc_query_executor.py +0 -0
  67. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/query_executors/mssql_query_executor.py +0 -0
  68. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/query_executors/mysql_query_executor.py +0 -0
  69. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/query_executors/postgresql_query_executor.py +0 -0
  70. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/query_executors/redshift_query_executor.py +0 -0
  71. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/query_executors/snowflake_query_executor.py +0 -0
  72. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/redshift_handler.py +0 -0
  73. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/snowflake_handler.py +0 -0
  74. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/utils.py +0 -0
  75. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/data_warehouse_handlers/warehouse_handlers_map.py +0 -0
  76. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/database/__init__.py +0 -0
  77. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/dataclasses/__init__.py +0 -0
  78. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/dataclasses/aws_batch_job_context.py +0 -0
  79. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/dataclasses/azure_container_app_job_context.py +0 -0
  80. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/dataclasses/event_executor.py +0 -0
  81. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/dataclasses/job_context.py +0 -0
  82. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/dataclasses/job_record_dataclasses.py +0 -0
  83. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/dataclasses/warehouse_configs.py +0 -0
  84. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/__init__.py +0 -0
  85. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/__init__.py +0 -0
  86. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/csv.py +0 -0
  87. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/databricks.py +0 -0
  88. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/db2.py +0 -0
  89. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/jdbc.py +0 -0
  90. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/jira.py +0 -0
  91. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/mssql.py +0 -0
  92. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/mysql.py +0 -0
  93. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/oracle.py +0 -0
  94. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/postgres.py +0 -0
  95. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/redshift.py +0 -0
  96. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/salesforce.py +0 -0
  97. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/snowflake.py +0 -0
  98. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/connectors_to_system/sqlalchemy.py +0 -0
  99. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/system_to_warehouse/__init__.py +0 -0
  100. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/system_to_warehouse/postgres.py +0 -0
  101. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/datatype_mappings/system_to_warehouse/redshift.py +0 -0
  102. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/enums/__init__.py +0 -0
  103. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/enums/async_task_executor.py +0 -0
  104. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/enums/connection_engine.py +0 -0
  105. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/enums/status_tracker.py +0 -0
  106. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/event_executor/__init__.py +0 -0
  107. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/event_executor/event_executor.py +0 -0
  108. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/event_executor/job_executor.py +0 -0
  109. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/event_executor/utils.py +0 -0
  110. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/exceptions/__init__.py +0 -0
  111. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/exceptions/batch_job_exceptions.py +0 -0
  112. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/exceptions/configuration_exceptions.py +0 -0
  113. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/exceptions/custom_exceptions.py +0 -0
  114. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/exceptions/data_validation_exceptions.py +0 -0
  115. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/exceptions/http_exceptions.py +0 -0
  116. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/exceptions/infrastructure_exceptions.py +0 -0
  117. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/exceptions/operational_exceptions.py +0 -0
  118. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/exceptions/security_exceptions.py +0 -0
  119. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/exceptions/user_intervention_exception.py +0 -0
  120. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/http/__init__.py +0 -0
  121. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/http/httpx_http_client.py +0 -0
  122. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/http/request_http_client.py +0 -0
  123. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/interfaces/base_job_handler.py +0 -0
  124. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/interfaces/data_warehouse_handler.py +0 -0
  125. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/interfaces/databus.py +0 -0
  126. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/interfaces/http.py +0 -0
  127. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/interfaces/keyvault.py +0 -0
  128. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/interfaces/query_executor.py +0 -0
  129. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/interfaces/storage_manager.py +0 -0
  130. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/interfaces/warehouse_connection.py +0 -0
  131. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/logger/__init__.py +0 -0
  132. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/messaging/__init__.py +0 -0
  133. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/messaging/aws_databus.py +0 -0
  134. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/messaging/azure_databus.py +0 -0
  135. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/messaging/http_databus.py +0 -0
  136. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/messaging/nats_databus.py +0 -0
  137. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/messaging/nats_publisher.py +0 -0
  138. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/messaging/nats_test.py +0 -0
  139. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/messaging/utils/aws_utility.py +0 -0
  140. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/messaging/utils/azure_utility.py +0 -0
  141. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/messaging/utils/event_messages.py +0 -0
  142. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/metrics/__init__.py +0 -0
  143. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/metrics/status_tracker.py +0 -0
  144. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/models/__init__.py +0 -0
  145. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/registries/__init__.py +0 -0
  146. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/registries/schema_onboarding_event_registry.py +0 -0
  147. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/registries/service_event_registry.py +0 -0
  148. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/schema_onboarding_tracker/__init__.py +0 -0
  149. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/schema_onboarding_tracker/base_tracker.py +0 -0
  150. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/schema_onboarding_tracker/enums.py +0 -0
  151. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/schema_onboarding_tracker/exceptions.py +0 -0
  152. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/schema_onboarding_tracker/run_manager.py +0 -0
  153. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/schema_onboarding_tracker/schema_onboarding_tracker.py +0 -0
  154. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/schema_onboarding_tracker/shared_helpers.py +0 -0
  155. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/schema_onboarding_tracker/task_determination.py +0 -0
  156. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/schema_onboarding_tracker/task_helpers.py +0 -0
  157. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/schema_onboarding_tracker/task_manager.py +0 -0
  158. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/schema_onboarding_tracker/template_manager.py +0 -0
  159. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/schema_onboarding_tracker/utility_manager.py +0 -0
  160. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/security/key_vault/__init__.py +0 -0
  161. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/security/key_vault/aws_secret_manager.py +0 -0
  162. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/security/key_vault/azure_keyvault.py +0 -0
  163. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/status_tracker/__init__.py +0 -0
  164. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/status_tracker/status_tracker.py +0 -0
  165. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/storage_handlers/__init__.py +0 -0
  166. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/storage_handlers/blob_file_handler.py +0 -0
  167. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/storage_handlers/s3_storage_handler.py +0 -0
  168. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/__init__.py +0 -0
  169. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/config/__init__.py +0 -0
  170. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/config/test_config.py +0 -0
  171. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/logger/__init__.py +0 -0
  172. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/logger/test_logger.py +0 -0
  173. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/messaging/__init__.py +0 -0
  174. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/messaging/test_aws_databus.py +0 -0
  175. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/messaging/test_event_executor.py +0 -0
  176. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/messaging/test_nats_interface.py +0 -0
  177. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/utils/__init__.py +0 -0
  178. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/utils/test_data_validators.py +0 -0
  179. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/utils/test_date_format_utils.py +0 -0
  180. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/tests/utils/test_string_utils.py +0 -0
  181. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/traceability/__init__.py +0 -0
  182. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/traceability/traceability_handler.py +0 -0
  183. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/utils/__init__.py +0 -0
  184. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/utils/data_validators_utils.py +0 -0
  185. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/utils/date_format_utils.py +0 -0
  186. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/utils/date_utils.py +0 -0
  187. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/utils/string_utils.py +0 -0
  188. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/utils/template_renderer.py +0 -0
  189. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/utils/thread_debug_utils.py +0 -0
  190. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/utils/thread_local_storage.py +0 -0
  191. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/shared_kernel/utils/thread_log_traker.py +0 -0
  192. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/tests/__init__.py +0 -0
  193. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/tests/messaging/__init__.py +0 -0
  194. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/tests/messaging/utils/__init__.py +0 -0
  195. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/tests/messaging/utils/test_aws_utility.py +0 -0
  196. {fdc_shared_kernel-0.0.184 → fdc_shared_kernel-0.0.186}/tests/test_job_executor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fdc_shared_kernel
3
- Version: 0.0.184
3
+ Version: 0.0.186
4
4
  Summary: Shared library for microservice
5
5
  Author-email: Shikhil S <shikhil.s@dbizsolution.com>, Ahammed Akdham N <ahammedakdham.n@dbizsolution.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fdc_shared_kernel
3
- Version: 0.0.184
3
+ Version: 0.0.186
4
4
  Summary: Shared library for microservice
5
5
  Author-email: Shikhil S <shikhil.s@dbizsolution.com>, Ahammed Akdham N <ahammedakdham.n@dbizsolution.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -113,6 +113,7 @@ shared_kernel/interfaces/__init__.py
113
113
  shared_kernel/interfaces/base_job_handler.py
114
114
  shared_kernel/interfaces/data_warehouse_handler.py
115
115
  shared_kernel/interfaces/databus.py
116
+ shared_kernel/interfaces/encryption.py
116
117
  shared_kernel/interfaces/http.py
117
118
  shared_kernel/interfaces/keyvault.py
118
119
  shared_kernel/interfaces/query_executor.py
@@ -148,6 +149,9 @@ shared_kernel/schema_onboarding_tracker/task_manager.py
148
149
  shared_kernel/schema_onboarding_tracker/template_manager.py
149
150
  shared_kernel/schema_onboarding_tracker/utility_manager.py
150
151
  shared_kernel/security/__init__.py
152
+ shared_kernel/security/encryption/__init__.py
153
+ shared_kernel/security/encryption/manager.py
154
+ shared_kernel/security/encryption/providers.py
151
155
  shared_kernel/security/key_vault/__init__.py
152
156
  shared_kernel/security/key_vault/aws_secret_manager.py
153
157
  shared_kernel/security/key_vault/azure_keyvault.py
@@ -165,6 +169,7 @@ shared_kernel/tests/messaging/__init__.py
165
169
  shared_kernel/tests/messaging/test_aws_databus.py
166
170
  shared_kernel/tests/messaging/test_event_executor.py
167
171
  shared_kernel/tests/messaging/test_nats_interface.py
172
+ shared_kernel/tests/security/test_encryption.py
168
173
  shared_kernel/tests/utils/__init__.py
169
174
  shared_kernel/tests/utils/test_data_validators.py
170
175
  shared_kernel/tests/utils/test_date_format_utils.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fdc_shared_kernel"
7
- version = "0.0.184"
7
+ version = "0.0.186"
8
8
  requires-python = ">=3.7"
9
9
  readme = "README_pypi.md"
10
10
  description = "Shared library for microservice"
@@ -0,0 +1,302 @@
1
+ import errno
2
+ import hashlib
3
+ import time
4
+ import threading
5
+
6
+ from contextlib import contextmanager
7
+ from typing import Dict
8
+
9
+ from sqlalchemy import create_engine, text
10
+ from sqlalchemy.engine import Engine
11
+ from sqlalchemy.exc import InterfaceError, OperationalError, ProgrammingError
12
+
13
+ from shared_kernel.dataclasses.warehouse_configs import RedshiftConfig
14
+ from shared_kernel.exceptions.http_exceptions import BadRequest
15
+ from shared_kernel.interfaces.warehouse_connection import DataWarehouseConnection
16
+ from shared_kernel.logger import Logger
17
+ from shared_kernel.config import Config
18
+
19
+ config = Config()
20
+ logger = Logger(config.get("APP_NAME"))
21
+
22
+
23
+ class RedshiftWarehouseConnection(DataWarehouseConnection):
24
+
25
+ _engines: Dict[str, Engine] = {}
26
+ _lock = threading.Lock()
27
+ _last_dispose_time: Dict[str, float] = {}
28
+ _monkeypatched = False
29
+
30
+ @classmethod
31
+ def _apply_monkeypatch(cls):
32
+ """
33
+ Dynamically applies the redshift_connector integer OID deserialization patch.
34
+
35
+ WHY THIS IS CRITICAL FOR PRODUCTION:
36
+ - When Redshift executes constant simplification/folding (e.g. 'COUNT(x) * 0'),
37
+ its query optimizer decides to send the integer over the wire as a 4-byte payload.
38
+ - However, the schema definition (RowDescription) declares the column's OID as BIGINT (OID 20).
39
+ - By default, redshift-connector registers OID 20 to the `int8_recv` function, which performs
40
+ a rigid 8-byte struct unpack (`q_unpack`).
41
+ - Because the payload is only 4 bytes, `struct.unpack` fails with a `struct.error` stating
42
+ that the buffer is too small, crashing query executions.
43
+
44
+ HOW THIS MONKEYPATCH RESOLVES IT:
45
+ - We hijack `int8_recv`, `int4_recv`, and `int2_recv` within redshift-connector.
46
+ - Rather than relying on a fixed-width struct unpack, we check the actual length of the
47
+ bytes available in the network packet.
48
+ - We convert the bytes dynamically using `int.from_bytes(..., byteorder="big", signed=True)`
49
+ to safely parse any integer size (1, 2, 4, or 8 bytes) sent by the Redshift engine.
50
+ - We apply this patch at the module-level registry AND wrap the core Connection initialization
51
+ to force every active connection instance to use these patched deserializers, bypassing
52
+ driver version rigidity.
53
+ """
54
+ try:
55
+ # Lazy import driver modules so we only modify namespaces when the connection is used
56
+ import redshift_connector
57
+ import redshift_connector.utils.type_utils as type_utils
58
+ from redshift_connector.utils.type_utils import RedshiftOID, FC_BINARY
59
+
60
+ # Keep references to the original functions to allow safe fallbacks if unpack fails
61
+ original_int8_recv = type_utils.int8_recv
62
+ original_int4_recv = type_utils.int4_recv
63
+ original_int2_recv = type_utils.int2_recv
64
+
65
+ def safe_unpack_int(data: bytes, offset: int, length: int, original_recv_func) -> int:
66
+ """
67
+ A resilient integer parsing helper that reads integer payloads of any length dynamically.
68
+
69
+ Args:
70
+ data (bytes): The raw network packet buffer.
71
+ offset (int): The current offset index in the buffer.
72
+ length (int): The declared byte length of the value.
73
+ original_recv_func (callable): The original fallback driver function.
74
+ """
75
+ try:
76
+ if length > 0:
77
+ # Calculate exactly how many bytes remain in the packet from the current offset
78
+ available = len(data) - offset
79
+ # Determine the maximum safe byte-length we can extract without buffer overflow
80
+ actual_len = min(length, available)
81
+ if actual_len > 0:
82
+ # Redshift transmits integers in network byte order (big-endian).
83
+ # standard python int.from_bytes allows us to parse integers of any byte-length
84
+ # (1, 2, 4, or 8 bytes) safely and dynamically.
85
+ return int.from_bytes(data[offset : offset + actual_len], byteorder="big", signed=True)
86
+
87
+ # Fallback to original driver function if length parameters are missing or zero
88
+ return original_recv_func(data, offset, length)
89
+ except Exception:
90
+ # In case of any unexpected errors during dynamic parsing, attempt the original function
91
+ try:
92
+ return original_recv_func(data, offset, length)
93
+ except Exception:
94
+ # Final safe guard to prevent the service from crashing on malformed columns
95
+ return 0
96
+
97
+ # Wrap each receiver function to utilize our safe_unpack_int helper
98
+ def patched_int8_recv(data: bytes, offset: int, length: int) -> int:
99
+ return safe_unpack_int(data, offset, length, original_int8_recv)
100
+
101
+ def patched_int4_recv(data: bytes, offset: int, length: int) -> int:
102
+ return safe_unpack_int(data, offset, length, original_int4_recv)
103
+
104
+ def patched_int2_recv(data: bytes, offset: int, length: int) -> int:
105
+ return safe_unpack_int(data, offset, length, original_int2_recv)
106
+
107
+ # 1. Patch the global type utility receiver functions directly
108
+ type_utils.int8_recv = patched_int8_recv
109
+ type_utils.int4_recv = patched_int4_recv
110
+ type_utils.int2_recv = patched_int2_recv
111
+
112
+ # 2. Update the global module-level type mapping registry dictionary
113
+ type_utils.redshift_types[RedshiftOID.BIGINT] = (FC_BINARY, patched_int8_recv)
114
+ type_utils.redshift_types[RedshiftOID.INTEGER] = (FC_BINARY, patched_int4_recv)
115
+ type_utils.redshift_types[RedshiftOID.SMALLINT] = (FC_BINARY, patched_int2_recv)
116
+
117
+ # 3. Patch core Connection module configuration to handle connection pooling & instances
118
+ try:
119
+ import redshift_connector.core as core
120
+ # Overwrite REDSHIFT_TYPES map in core module
121
+ if hasattr(core, "REDSHIFT_TYPES"):
122
+ core.REDSHIFT_TYPES[RedshiftOID.BIGINT] = (FC_BINARY, patched_int8_recv)
123
+ core.REDSHIFT_TYPES[RedshiftOID.INTEGER] = (FC_BINARY, patched_int4_recv)
124
+ core.REDSHIFT_TYPES[RedshiftOID.SMALLINT] = (FC_BINARY, patched_int2_recv)
125
+
126
+ # Intercept Connection initialization to force connection instance registries
127
+ if hasattr(core, "Connection"):
128
+ original_conn_init = core.Connection.__init__
129
+ def patched_conn_init(self, *args, **kwargs):
130
+ # Execute the original constructor
131
+ original_conn_init(self, *args, **kwargs)
132
+ # Re-bind instance-specific deserialization mapping dictionaries
133
+ self.redshift_types[RedshiftOID.BIGINT] = (FC_BINARY, patched_int8_recv)
134
+ self.redshift_types[RedshiftOID.INTEGER] = (FC_BINARY, patched_int4_recv)
135
+ self.redshift_types[RedshiftOID.SMALLINT] = (FC_BINARY, patched_int2_recv)
136
+ core.Connection.__init__ = patched_conn_init
137
+ except Exception as core_err:
138
+ logger.warning(f"Could not patch core.REDSHIFT_TYPES or Connection.__init__: {core_err}")
139
+
140
+ logger.info("Successfully applied production-level redshift_connector integer OID deserialization patch")
141
+ except Exception as e:
142
+ logger.error(f"Failed to patch redshift_connector type deserialization: {e}")
143
+
144
+ def __init__(
145
+ self,
146
+ source_config: RedshiftConfig,
147
+ set_default_schema: bool = True,
148
+ ):
149
+ if source_config is None:
150
+ raise ValueError(
151
+ "Missing required parameter: source_config (RedshiftConfig)"
152
+ )
153
+
154
+ super().__init__(source_config)
155
+ self._redshift_config = source_config
156
+ self.org_schema = source_config.schema if source_config.schema else None
157
+ self.set_default_schema = set_default_schema
158
+
159
+ # Apply the connection protocol patch thread-safely on the very first class instantiation
160
+ with self.__class__._lock:
161
+ if not self.__class__._monkeypatched:
162
+ cls = self.__class__
163
+ cls._apply_monkeypatch()
164
+ cls._monkeypatched = True
165
+
166
+
167
+
168
+ def _engine_key(self, cfg: RedshiftConfig) -> str:
169
+ pwd_hash = hashlib.sha256(cfg.password.encode()).hexdigest()
170
+ return f"{cfg.host}|{cfg.port}|{cfg.database}|{cfg.username}|{pwd_hash}"
171
+
172
+ def invalidate_pool(self):
173
+ key = self._engine_key(self._redshift_config)
174
+ with self.__class__._lock:
175
+ now = time.time()
176
+ last_time = self._last_dispose_time.get(key, 0)
177
+ # Throttle (5 seconds)
178
+ if now - last_time < 5:
179
+ return
180
+ engine = self._engines.get(key)
181
+ if engine:
182
+ logger.warning(f"Disposing engine pool for {key} due to stale OID")
183
+ engine.dispose()
184
+ self._last_dispose_time[key] = now
185
+
186
+ def _get_engine(self) -> Engine:
187
+ cfg = self._redshift_config
188
+ key = self._engine_key(cfg)
189
+
190
+ if key not in self._engines:
191
+ with self.__class__._lock: # thread-safe
192
+ if key not in self._engines: # recheck
193
+ url = (
194
+ f"redshift+redshift_connector://{cfg.username}:{cfg.password}"
195
+ f"@{cfg.host}:{cfg.port}/{cfg.database}"
196
+ )
197
+ self._engines[key] = create_engine(
198
+ url,
199
+ pool_size=10,
200
+ max_overflow=5,
201
+ pool_timeout=30,
202
+ pool_recycle=1800,
203
+ pool_pre_ping=True,
204
+ isolation_level="AUTOCOMMIT",
205
+ )
206
+ logger.info(f"Created new engine for {cfg.host}:{cfg.port}/{cfg.database}")
207
+
208
+ return self._engines[key]
209
+
210
+ def _is_maintenance_error(self, exc: Exception) -> bool:
211
+ """Return True when the error looks like a Redshift cluster reboot or maintenance."""
212
+ err = exc
213
+ while hasattr(err, "__cause__") and err.__cause__:
214
+ err = err.__cause__
215
+
216
+ if isinstance(err, ConnectionRefusedError):
217
+ return err.errno in (errno.ECONNREFUSED, 10061)
218
+
219
+ if isinstance(err, InterfaceError) and "ConnectionRefusedError" in str(err):
220
+ return True
221
+
222
+ return False
223
+
224
+ @contextmanager
225
+ def get_connection(self, retries: int = 3, delay: int = 5, query_timeout: int = 30):
226
+ """Context manager for database connections with retry mechanism"""
227
+ engine = self._get_engine()
228
+ last_exception = None
229
+
230
+ for attempt in range(retries):
231
+ conn = None
232
+ try:
233
+ conn = engine.connect()
234
+ conn.execute(text("SET enable_case_sensitive_identifier TO on"))
235
+ conn.execute(text(f"SET statement_timeout = {query_timeout * 1000}"))
236
+ if self.org_schema and self.set_default_schema:
237
+ conn.execute(text(f"SET search_path TO {self.org_schema}"))
238
+ logger.info(f"Search path set to: {self.org_schema}")
239
+
240
+ yield conn
241
+ return
242
+
243
+ except (OperationalError, InterfaceError, ProgrammingError) as e:
244
+ last_exception = e
245
+ if conn is not None:
246
+ try:
247
+ conn.rollback()
248
+ except:
249
+ pass
250
+ conn.close()
251
+ conn = None
252
+
253
+ if self._is_maintenance_error(e):
254
+ logger.warning(f"Redshift maintenance detected (attempt {attempt + 1}/{retries}): {e}")
255
+ else:
256
+ logger.warning(f"Connection failed (attempt {attempt + 1}/{retries}): {e}")
257
+
258
+ if attempt < retries - 1:
259
+ time.sleep(delay)
260
+ delay += 5
261
+ continue
262
+
263
+ break
264
+
265
+ finally:
266
+ if conn is not None:
267
+ conn.close()
268
+
269
+ raise BadRequest(
270
+ f"Failed to connect to Redshift after {retries} attempts : {str(last_exception)}"
271
+ ) from last_exception
272
+
273
+ def close_connection(self):
274
+ """Dispose all pooled engines managed by this class."""
275
+ for engine in self._engines.values():
276
+ engine.dispose()
277
+ self._engines.clear()
278
+ logger.info("All Redshift engine pools disposed.")
279
+
280
+ def is_valid_connection(self) -> bool:
281
+ """Pool health is managed by pool_pre_ping; no manual check needed."""
282
+ return True
283
+
284
+ def test_connection(self) -> bool:
285
+ """Verify the connection is reachable. Raises ConnectionError on failure."""
286
+ try:
287
+ with self.get_connection():
288
+ return True
289
+ except Exception as e:
290
+ error_msg = str(e)
291
+ if "Name or service not known" in error_msg:
292
+ user_msg = "Host not found. Please check the host address."
293
+ elif "Connection refused" in error_msg:
294
+ user_msg = "Connection refused. Please check the host and port."
295
+ elif "database" in error_msg.lower() and "does not exist" in error_msg:
296
+ user_msg = "Database does not exist. Please check the database name."
297
+ elif "password authentication failed" in error_msg.lower():
298
+ user_msg = "Authentication failed. Please check your username and password."
299
+ else:
300
+ user_msg = f"Redshift connection test failed: {error_msg}"
301
+
302
+ raise ConnectionError(user_msg) from e
@@ -2,3 +2,4 @@ from shared_kernel.interfaces.databus import *
2
2
  from shared_kernel.interfaces.data_warehouse_handler import *
3
3
  from shared_kernel.interfaces.keyvault import *
4
4
  from shared_kernel.interfaces.http import *
5
+ from shared_kernel.interfaces.encryption import *
@@ -0,0 +1,33 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class EncryptionProviderInterface(ABC):
5
+ """
6
+ Abstract interface for encryption/decryption providers.
7
+ """
8
+
9
+ @abstractmethod
10
+ def encrypt(self, plaintext: str) -> str:
11
+ """
12
+ Encrypts the plaintext string and returns the base64-encoded ciphertext.
13
+
14
+ Args:
15
+ plaintext (str): The plaintext string to encrypt.
16
+
17
+ Returns:
18
+ str: The base64-encoded ciphertext string.
19
+ """
20
+ pass
21
+
22
+ @abstractmethod
23
+ def decrypt(self, ciphertext: str) -> str:
24
+ """
25
+ Decrypts the base64-encoded ciphertext and returns the plaintext string.
26
+
27
+ Args:
28
+ ciphertext (str): The base64-encoded ciphertext string.
29
+
30
+ Returns:
31
+ str: The decrypted plaintext string.
32
+ """
33
+ pass
@@ -0,0 +1,2 @@
1
+ from shared_kernel.security.key_vault import *
2
+ from shared_kernel.security.encryption import *
@@ -0,0 +1,4 @@
1
+ from shared_kernel.security.encryption.manager import EncryptionManager
2
+ from shared_kernel.interfaces.encryption import EncryptionProviderInterface
3
+
4
+ __all__ = ["EncryptionManager", "EncryptionProviderInterface"]
@@ -0,0 +1,84 @@
1
+ from typing import Dict, Any, Optional
2
+ from shared_kernel.interfaces.encryption import EncryptionProviderInterface
3
+ from shared_kernel.exceptions import EncryptionError
4
+ from shared_kernel.security.encryption.providers import (
5
+ BasicEncryptionProvider,
6
+ AwsKmsProvider,
7
+ AzureKeyVaultCryptoProvider,
8
+ )
9
+
10
+
11
+ class EncryptionManager:
12
+ """
13
+ Factory manager for creating encryption providers and orchestrating
14
+ encryption and decryption operations.
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ method: str,
20
+ key: Optional[str] = None,
21
+ config: Optional[Dict[str, Any]] = None,
22
+ ):
23
+ """
24
+ Initializes the EncryptionManager with the specified method, key, and config.
25
+
26
+ Args:
27
+ method (str): The encryption method (e.g., 'BASIC', 'AWS_KMS', 'AZURE_KEYVAULT').
28
+ key (str, optional): The encryption key or identifier.
29
+ config (dict, optional): Additional configuration for cloud provider clients.
30
+ """
31
+ self._provider = self._resolve_provider(method, key, config or {})
32
+
33
+ def _resolve_provider(
34
+ self, method: str, key: Optional[str], config: Dict[str, Any]
35
+ ) -> EncryptionProviderInterface:
36
+ """
37
+ Resolves and instantiates the proper encryption provider.
38
+ """
39
+ method_upper = method.upper()
40
+
41
+ if method_upper in ["BASIC", "SYMMETRIC", "FERNET"]:
42
+ return BasicEncryptionProvider(key, config)
43
+ # elif method_upper == "AWS_KMS":
44
+ # return AwsKmsProvider(key, config)
45
+ # elif method_upper in ["AZURE_KEYVAULT", "AZURE_KMS"]:
46
+ # return AzureKeyVaultCryptoProvider(key, config)
47
+ else:
48
+ raise ValueError(f"Unsupported encryption method: {method}")
49
+
50
+ def encrypt(self, plaintext: str) -> str:
51
+ """
52
+ Encrypts the plaintext string using the configured provider.
53
+
54
+ Args:
55
+ plaintext (str): The string to encrypt.
56
+
57
+ Returns:
58
+ str: Base64-encoded encrypted ciphertext.
59
+ """
60
+ if not isinstance(plaintext, str):
61
+ raise TypeError("Plaintext must be a string")
62
+
63
+ try:
64
+ return self._provider.encrypt(plaintext)
65
+ except Exception as e:
66
+ raise EncryptionError("Encryption failed", detail=str(e))
67
+
68
+ def decrypt(self, ciphertext: str) -> str:
69
+ """
70
+ Decrypts the ciphertext string using the configured provider.
71
+
72
+ Args:
73
+ ciphertext (str): The base64-encoded ciphertext to decrypt.
74
+
75
+ Returns:
76
+ str: Decrypted plaintext string.
77
+ """
78
+ if not isinstance(ciphertext, str):
79
+ raise TypeError("Ciphertext must be a string")
80
+
81
+ try:
82
+ return self._provider.decrypt(ciphertext)
83
+ except Exception as e:
84
+ raise EncryptionError("Decryption failed", detail=str(e))
@@ -0,0 +1,96 @@
1
+ import base64
2
+ import hashlib
3
+ import os
4
+ from typing import Dict, Any, Optional
5
+ from cryptography.fernet import Fernet
6
+ from shared_kernel.interfaces.encryption import EncryptionProviderInterface
7
+
8
+
9
+ class BasicEncryptionProvider(EncryptionProviderInterface):
10
+ """
11
+ Symmetric encryption provider using cryptography Fernet.
12
+ """
13
+
14
+ def __init__(self, key: Optional[str] = None, config: Optional[Dict[str, Any]] = None):
15
+ """
16
+ Initializes the BasicEncryptionProvider with a symmetric key.
17
+
18
+ Args:
19
+ key (str, optional): The encryption key. If not provided,
20
+ it will attempt to read config or environment.
21
+ config (dict, optional): Configuration dictionary.
22
+ """
23
+ cfg = config or {}
24
+ raw_key = key or cfg.get("encryption_key") or os.getenv("FDC_ENCRYPTION_KEY")
25
+
26
+ if not raw_key:
27
+ raise ValueError("Encryption key must be provided for basic encryption.")
28
+
29
+ self._fernet = self._setup_fernet(raw_key)
30
+
31
+ def _setup_fernet(self, raw_key: str) -> Fernet:
32
+ """
33
+ Sets up the Fernet instance, deriving a key if the raw key is not valid.
34
+ """
35
+ try:
36
+ key_bytes = raw_key.encode("utf-8")
37
+ decoded = base64.urlsafe_b64decode(key_bytes)
38
+ if len(decoded) == 32:
39
+ return Fernet(key_bytes)
40
+ except Exception:
41
+ pass
42
+
43
+ # Derive a 32-byte key using SHA-256 for non-Fernet formatted keys
44
+ derived = hashlib.sha256(raw_key.encode("utf-8")).digest()
45
+ fernet_key = base64.urlsafe_b64encode(derived)
46
+ return Fernet(fernet_key)
47
+
48
+ def encrypt(self, plaintext: str) -> str:
49
+ """
50
+ Encrypts the plaintext string.
51
+ """
52
+ if not plaintext:
53
+ return ""
54
+ data_bytes = plaintext.encode("utf-8")
55
+ token = self._fernet.encrypt(data_bytes)
56
+ return token.decode("utf-8")
57
+
58
+ def decrypt(self, ciphertext: str) -> str:
59
+ """
60
+ Decrypts the ciphertext string.
61
+ """
62
+ if not ciphertext:
63
+ return ""
64
+ token_bytes = ciphertext.encode("utf-8")
65
+ decrypted = self._fernet.decrypt(token_bytes)
66
+ return decrypted.decode("utf-8")
67
+
68
+
69
+ class AwsKmsProvider(EncryptionProviderInterface):
70
+ """
71
+ AWS KMS encryption provider stub.
72
+ """
73
+
74
+ def __init__(self, key: Optional[str] = None, config: Optional[Dict[str, Any]] = None):
75
+ raise NotImplementedError("AwsKmsProvider is not implemented yet.")
76
+
77
+ def encrypt(self, plaintext: str) -> str:
78
+ raise NotImplementedError("AwsKmsProvider is not implemented yet.")
79
+
80
+ def decrypt(self, ciphertext: str) -> str:
81
+ raise NotImplementedError("AwsKmsProvider is not implemented yet.")
82
+
83
+
84
+ class AzureKeyVaultCryptoProvider(EncryptionProviderInterface):
85
+ """
86
+ Azure Key Vault Keys Cryptography provider stub.
87
+ """
88
+
89
+ def __init__(self, key: Optional[str] = None, config: Optional[Dict[str, Any]] = None):
90
+ raise NotImplementedError("AzureKeyVaultCryptoProvider is not implemented yet.")
91
+
92
+ def encrypt(self, plaintext: str) -> str:
93
+ raise NotImplementedError("AzureKeyVaultCryptoProvider is not implemented yet.")
94
+
95
+ def decrypt(self, ciphertext: str) -> str:
96
+ raise NotImplementedError("AzureKeyVaultCryptoProvider is not implemented yet.")