ethyca-fides 2.69.1b1__py2.py3-none-any.whl → 2.69.1rc0__py2.py3-none-any.whl

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 (245) hide show
  1. {ethyca_fides-2.69.1b1.dist-info → ethyca_fides-2.69.1rc0.dist-info}/METADATA +2 -2
  2. {ethyca_fides-2.69.1b1.dist-info → ethyca_fides-2.69.1rc0.dist-info}/RECORD +241 -232
  3. fides/_version.py +3 -3
  4. fides/api/alembic/migrations/versions/78dbe23d8204_adding_privacy_request_redaction_patterns.py +52 -0
  5. fides/api/api/v1/api.py +2 -0
  6. fides/api/api/v1/endpoints/dsr_package_link.py +5 -3
  7. fides/api/api/v1/endpoints/oauth_endpoints.py +19 -7
  8. fides/api/api/v1/endpoints/privacy_request_redaction_patterns_endpoints.py +95 -0
  9. fides/api/api/v1/endpoints/user_endpoints.py +27 -4
  10. fides/api/app_setup.py +16 -2
  11. fides/api/db/base.py +3 -0
  12. fides/api/main.py +22 -0
  13. fides/api/models/client.py +1 -0
  14. fides/api/models/privacy_request_redaction_pattern.py +64 -0
  15. fides/api/oauth/utils.py +126 -12
  16. fides/api/schemas/privacy_request_redaction_patterns.py +55 -0
  17. fides/api/service/privacy_request/dsr_package/dsr_data_preprocessor.py +231 -0
  18. fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +286 -120
  19. fides/api/service/privacy_request/dsr_package/templates/attachments_index.html +4 -2
  20. fides/api/service/privacy_request/dsr_package/templates/collection_index.html +3 -1
  21. fides/api/service/privacy_request/dsr_package/templates/dataset_index.html +1 -1
  22. fides/api/service/privacy_request/dsr_package/utils.py +268 -0
  23. fides/api/service/privacy_request/request_runner_service.py +8 -2
  24. fides/api/service/privacy_request/request_service.py +18 -1
  25. fides/api/service/storage/storage_uploader_service.py +1 -21
  26. fides/api/service/storage/streaming/dsr_storage.py +1 -5
  27. fides/api/service/storage/streaming/s3/s3_storage_client.py +78 -40
  28. fides/api/service/storage/streaming/s3/streaming_s3.py +9 -21
  29. fides/api/service/storage/streaming/smart_open_client.py +8 -7
  30. fides/api/service/storage/streaming/smart_open_streaming_storage.py +110 -197
  31. fides/api/service/storage/streaming/storage_client_factory.py +7 -3
  32. fides/api/service/storage/util.py +579 -0
  33. fides/api/task/manual/manual_task_graph_task.py +11 -9
  34. fides/api/tasks/storage.py +2 -2
  35. fides/api/util/endpoint_utils.py +0 -13
  36. fides/api/util/rate_limit.py +194 -0
  37. fides/common/api/scope_registry.py +8 -0
  38. fides/common/api/v1/urn_registry.py +3 -0
  39. fides/config/redis_settings.py +27 -3
  40. fides/config/security_settings.py +21 -6
  41. fides/ui-build/static/admin/404.html +1 -1
  42. fides/ui-build/static/admin/_next/static/OmXHlY9MvjoZH9jDkAytl/_buildManifest.js +1 -0
  43. fides/ui-build/static/admin/_next/static/chunks/{1345-04e37a66c0d40dc1.js → 1345-5e1c5b66e25c566e.js} +1 -1
  44. fides/ui-build/static/admin/_next/static/chunks/{3729-f5f2976904dce90d.js → 3729-c17ac8031a4c4fd1.js} +1 -1
  45. fides/ui-build/static/admin/_next/static/chunks/{3847-2c0126e6eb54c526.js → 3847-230bf61b053bc706.js} +1 -1
  46. fides/ui-build/static/admin/_next/static/chunks/{3855-9dd54ded74f4036b.js → 3855-ef5194cdb228beb6.js} +1 -1
  47. fides/ui-build/static/admin/_next/static/chunks/4164-355644b916ae0094.js +1 -0
  48. fides/ui-build/static/admin/_next/static/chunks/{4608-d101417a3abb4ad6.js → 4608-be8cba73f5d7c326.js} +1 -1
  49. fides/ui-build/static/admin/_next/static/chunks/{4786-7aafb744445445b2.js → 4786-61154adf88e448e1.js} +1 -1
  50. fides/ui-build/static/admin/_next/static/chunks/{4808-357ca7ea7bbd24c6.js → 4808-dd4157aa72648068.js} +1 -1
  51. fides/ui-build/static/admin/_next/static/chunks/4831-fd99c0b3784de128.js +1 -0
  52. fides/ui-build/static/admin/_next/static/chunks/{4844-707b20a6c4b127cc.js → 4844-46324c3d848b8b6a.js} +1 -1
  53. fides/ui-build/static/admin/_next/static/chunks/{5258-1a8b9f66b97761fc.js → 5258-b0de22a8521686ab.js} +1 -1
  54. fides/ui-build/static/admin/_next/static/chunks/{9046-058a4d8f0b5e08f9.js → 9046-712156d461165f56.js} +1 -1
  55. fides/ui-build/static/admin/_next/static/chunks/{9676.b7d5d1d90b9da224.js → 9676.9fd9552ef744c717.js} +1 -1
  56. fides/ui-build/static/admin/_next/static/chunks/{9951-b954027a046ce553.js → 9951-a88367a129b724ba.js} +1 -1
  57. fides/ui-build/static/admin/_next/static/chunks/pages/{_app-7612a768b9a6304b.js → _app-fcdad91f6f66292b.js} +2 -2
  58. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-20253dd047fb9736.js → manual-ace203dfacacbdc4.js} +1 -1
  59. fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-169099ff7b305cf5.js → add-systems-bd0d82078e67cac3.js} +1 -1
  60. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-28b192e2c074b0f3.js +1 -0
  61. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-adc500a03e239857.js → [resourceUrn]-2c29ff7a01198f30.js} +1 -1
  62. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-c8b3d090e4ba60d3.js → [resourceUrn]-8eb581024bc0172f.js} +1 -1
  63. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/{[systemId]-cfb0b1200bc1a2c9.js → [systemId]-e1ba213fb666b3f4.js} +1 -1
  64. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/{[monitorId]-fed8b879c13c2bf3.js → [monitorId]-6d133580045abdda.js} +1 -1
  65. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{activity-58a110542d6bcd0f.js → activity-b6ae7adb8ef0b525.js} +1 -1
  66. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-22eec362dfbb1d2a.js → [resourceUrn]-8f736b078e9842da.js} +1 -1
  67. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-4decce5ef996e563.js → detection-eb814e3c22807871.js} +1 -1
  68. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-01acdd1ad492fd89.js → [resourceUrn]-6875b7783fcfda2f.js} +1 -1
  69. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-85fdbf4cde60d910.js → discovery-172dbd7740e212ca.js} +1 -1
  70. fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-60e27b673c68bd27.js → datamap-c7390e046b2e2b7f.js} +1 -1
  71. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-2c7b1213b6604d30.js → new-e32fccc4ca520d2b.js} +1 -1
  72. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-7ed3f05700dc397b.js → [id]-927b7e476c4b47d0.js} +1 -1
  73. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-95f6d64f84fc6bf3.js → new-cbe100d50df34285.js} +1 -1
  74. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/{[id]-02ff0dd9d8f1d719.js → [id]-4c3c413a2668df53.js} +1 -1
  75. fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-0f12d5b658c98c9f.js → integrations-95402b5001c07ef2.js} +1 -1
  76. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{[id]-b379873a5771e55b.js → [id]-0f25a76dd18c5e20.js} +1 -1
  77. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{messaging-a9bb257906dcd7e0.js → messaging-ad6ad3e5bd72765d.js} +1 -1
  78. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{storage-de0036c74b78e9b7.js → storage-6032d82f0fc2893d.js} +1 -1
  79. fides/ui-build/static/admin/_next/static/chunks/pages/{privacy-requests-91f578139548652c.js → privacy-requests-baf31c3e4b081046.js} +1 -1
  80. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/{datamap-83019a6753e14857.js → datamap-6903f42a0412bfa6.js} +1 -1
  81. fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-74f0fe9656f4a1f6.js → custom-fields-9ecb803099082bf4.js} +1 -1
  82. fides/ui-build/static/admin/_next/static/chunks/pages/settings/privacy-requests-2ecc073f41628f62.js +1 -0
  83. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-e91da49c0681a300.js → [id]-6e15332935f6b538.js} +1 -1
  84. fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-78c3a5200d362cff.js → taxonomy-4d7827fc9c46b6b8.js} +1 -1
  85. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-de8cb3739ab99c09.js → new-92f52c43f522a350.js} +1 -1
  86. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-4f9001870e2b3aff.js → [id]-64452dfae2c5e614.js} +1 -1
  87. fides/ui-build/static/admin/_next/static/chunks/{webpack-b5eb3e1da37616d2.js → webpack-678e89d68dbcd94f.js} +1 -1
  88. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  89. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  90. fides/ui-build/static/admin/add-systems.html +1 -1
  91. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  92. fides/ui-build/static/admin/consent/configure.html +1 -1
  93. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  94. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  95. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  96. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  97. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  98. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  99. fides/ui-build/static/admin/consent/properties.html +1 -1
  100. fides/ui-build/static/admin/consent/reporting.html +1 -1
  101. fides/ui-build/static/admin/consent.html +1 -1
  102. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  103. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  104. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  105. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  106. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  107. fides/ui-build/static/admin/data-catalog.html +1 -1
  108. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  109. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  110. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  111. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  112. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  113. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  114. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  115. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  116. fides/ui-build/static/admin/datamap.html +1 -1
  117. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  118. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  119. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  120. fides/ui-build/static/admin/dataset/new.html +1 -1
  121. fides/ui-build/static/admin/dataset.html +1 -1
  122. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  123. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  124. fides/ui-build/static/admin/datastore-connection.html +1 -1
  125. fides/ui-build/static/admin/index.html +1 -1
  126. fides/ui-build/static/admin/integrations/[id].html +1 -1
  127. fides/ui-build/static/admin/integrations.html +1 -1
  128. fides/ui-build/static/admin/login/[provider].html +1 -1
  129. fides/ui-build/static/admin/login.html +1 -1
  130. fides/ui-build/static/admin/messaging/[id].html +1 -1
  131. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  132. fides/ui-build/static/admin/messaging.html +1 -1
  133. fides/ui-build/static/admin/poc/ant-components.html +1 -1
  134. fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
  135. fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
  136. fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
  137. fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
  138. fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
  139. fides/ui-build/static/admin/poc/forms.html +1 -1
  140. fides/ui-build/static/admin/poc/table-migration.html +1 -1
  141. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  142. fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
  143. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  144. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  145. fides/ui-build/static/admin/privacy-requests.html +1 -1
  146. fides/ui-build/static/admin/properties/[id].html +1 -1
  147. fides/ui-build/static/admin/properties/add-property.html +1 -1
  148. fides/ui-build/static/admin/properties.html +1 -1
  149. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  150. fides/ui-build/static/admin/settings/about/alpha.html +1 -1
  151. fides/ui-build/static/admin/settings/about.html +1 -1
  152. fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
  153. fides/ui-build/static/admin/settings/consent.html +1 -1
  154. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  155. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  156. fides/ui-build/static/admin/settings/domains.html +1 -1
  157. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  158. fides/ui-build/static/admin/settings/locations.html +1 -1
  159. fides/ui-build/static/admin/settings/organization.html +1 -1
  160. fides/ui-build/static/admin/settings/privacy-requests.html +1 -0
  161. fides/ui-build/static/admin/settings/regulations.html +1 -1
  162. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  163. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  164. fides/ui-build/static/admin/systems.html +1 -1
  165. fides/ui-build/static/admin/taxonomy.html +1 -1
  166. fides/ui-build/static/admin/user-management/new.html +1 -1
  167. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  168. fides/ui-build/static/admin/user-management.html +1 -1
  169. fides/ui-build/static/admin/_next/static/LqjE2O1xWlQKqW38Sy4m3/_buildManifest.js +0 -1
  170. fides/ui-build/static/admin/_next/static/chunks/4121-bd6f467aacd80f91.js +0 -1
  171. fides/ui-build/static/admin/_next/static/chunks/768-7eac4b30d7477b0a.js +0 -1
  172. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-60e3394c887f3d5e.js +0 -1
  173. {ethyca_fides-2.69.1b1.dist-info → ethyca_fides-2.69.1rc0.dist-info}/WHEEL +0 -0
  174. {ethyca_fides-2.69.1b1.dist-info → ethyca_fides-2.69.1rc0.dist-info}/entry_points.txt +0 -0
  175. {ethyca_fides-2.69.1b1.dist-info → ethyca_fides-2.69.1rc0.dist-info}/licenses/LICENSE +0 -0
  176. {ethyca_fides-2.69.1b1.dist-info → ethyca_fides-2.69.1rc0.dist-info}/top_level.txt +0 -0
  177. /fides/ui-build/static/admin/_next/static/{LqjE2O1xWlQKqW38Sy4m3 → OmXHlY9MvjoZH9jDkAytl}/_ssgManifest.js +0 -0
  178. /fides/ui-build/static/admin/_next/static/chunks/{1099-c34f76b4da5f3d15.js → 1099-79646e64f26d62fa.js} +0 -0
  179. /fides/ui-build/static/admin/_next/static/chunks/{1817-e6934e258111a961.js → 1817-0ca16d288fad916d.js} +0 -0
  180. /fides/ui-build/static/admin/_next/static/chunks/{2921-0696287bb8de1f0b.js → 2921-52328140bc420d0f.js} +0 -0
  181. /fides/ui-build/static/admin/_next/static/chunks/{3620-8c0ee3d0b19c342d.js → 3620-602eb74dc896d556.js} +0 -0
  182. /fides/ui-build/static/admin/_next/static/chunks/{3872-72ea3eb040366277.js → 3872-f78dec02f0d959ae.js} +0 -0
  183. /fides/ui-build/static/admin/_next/static/chunks/{3923-c4f2b03706ddbe39.js → 3923-bb2417b8dcade7a4.js} +0 -0
  184. /fides/ui-build/static/admin/_next/static/chunks/{401-959a15ed18a8abdf.js → 401-4af0a912e249d30f.js} +0 -0
  185. /fides/ui-build/static/admin/_next/static/chunks/{5487-fd9724519f31caff.js → 5487-02d00bad7c6830e0.js} +0 -0
  186. /fides/ui-build/static/admin/_next/static/chunks/{549-d3bef0990071230c.js → 549-38ea1d91ee2addaa.js} +0 -0
  187. /fides/ui-build/static/admin/_next/static/chunks/{6084-487d27d017c45e05.js → 6084-c153669d5567e242.js} +0 -0
  188. /fides/ui-build/static/admin/_next/static/chunks/{6853-f7ab74e30abbdeaf.js → 6853-b17673391117c976.js} +0 -0
  189. /fides/ui-build/static/admin/_next/static/chunks/{6954-b0a7b8ac6db238f8.js → 6954-5296188c19d7d0ac.js} +0 -0
  190. /fides/ui-build/static/admin/_next/static/chunks/{7476-9a57db910472b48e.js → 7476-45c5088baa8b66af.js} +0 -0
  191. /fides/ui-build/static/admin/_next/static/chunks/{7630-46807321449479c7.js → 7630-7ed6c6117775dffe.js} +0 -0
  192. /fides/ui-build/static/admin/_next/static/chunks/{787-79e8e558bd80ece6.js → 787-a8c7eab617e2fceb.js} +0 -0
  193. /fides/ui-build/static/admin/_next/static/chunks/{79-3e5047415bee9391.js → 79-65674011d455af4d.js} +0 -0
  194. /fides/ui-build/static/admin/_next/static/chunks/{796-fb2af44165f37ecc.js → 796-9e1ca1a4030707c5.js} +0 -0
  195. /fides/ui-build/static/admin/_next/static/chunks/{8002-c1f66179adabece8.js → 8002-24af20d679efc04e.js} +0 -0
  196. /fides/ui-build/static/admin/_next/static/chunks/{9826-0d9a7f61c08ed88a.js → 9826-dbae8dee941a7fac.js} +0 -0
  197. /fides/ui-build/static/admin/_next/static/chunks/pages/{404-9c9efb820bb6b432.js → 404-471a6b18e712f050.js} +0 -0
  198. /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-caadc3c0e86a7bfe.js → multiple-920fb469e0dda1d2.js} +0 -0
  199. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-acefb9e08c6c4082.js → add-vendors-406170eaae4329c6.js} +0 -0
  200. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-558fbb1667473abb.js → configure-7207ab23bdb36ce8.js} +0 -0
  201. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-b1ff1c9683841815.js → [id]-f80cf2d3966816fd.js} +0 -0
  202. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-674906d2d9a0a3a6.js → privacy-experience-9dda4de5ec580279.js} +0 -0
  203. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-5b7aa7971f070513.js → [id]-b378576cba255609.js} +0 -0
  204. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-285958a12a66575e.js → new-2ca1de7b88094ab0.js} +0 -0
  205. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-dc56bbdb6dd7a670.js → privacy-notices-0d4844d0b808e6e4.js} +0 -0
  206. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-0843757f00eeaaec.js → properties-226efa1dcd41437f.js} +0 -0
  207. /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-5cba58ebecb4511f.js → consent-3e8bdefe714254ec.js} +0 -0
  208. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-0f90cac9f190130c.js → [projectUrn]-04cfe2cfba7b7cd8.js} +0 -0
  209. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-f9117645c941342c.js → projects-5f2d7b24804f861f.js} +0 -0
  210. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{resources-6c3714ee97a718c1.js → resources-de704de849960f01.js} +0 -0
  211. /fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-c566640eecc6f3fe.js → data-catalog-30108b00ac769fc3.js} +0 -0
  212. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{action-center-991659e916ad60b1.js → action-center-9a81d42a474e1e48.js} +0 -0
  213. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-3f4ba87513e030a2.js → [...subfieldNames]-dfd71c1e9c458b89.js} +0 -0
  214. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-a6cd31103deba465.js → [collectionName]-7cdc42ec5493b83d.js} +0 -0
  215. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-491d2c8dd559d0cb.js → [datasetId]-e12b11ba15bc3fc1.js} +0 -0
  216. /fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-c8bcd568d3b0ee7f.js → dataset-7c59a6abf6ba6207.js} +0 -0
  217. /fides/ui-build/static/admin/_next/static/chunks/pages/{datastore-connection-9a33a412a5f36960.js → datastore-connection-cce20440b177050b.js} +0 -0
  218. /fides/ui-build/static/admin/_next/static/chunks/pages/{index-1299410f671fac23.js → index-6cd8708106331b8d.js} +0 -0
  219. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-ba232c4b17576ccb.js → [id]-3c6dc2f6e6bae960.js} +0 -0
  220. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-b97883026fbc5b1e.js → add-template-4a6d4023a7791be8.js} +0 -0
  221. /fides/ui-build/static/admin/_next/static/chunks/pages/{messaging-38189c1058aa4acf.js → messaging-76b204c9b98d656f.js} +0 -0
  222. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{ant-components-3119bdb3811409bf.js → ant-components-bc0e2adf6e0d3ac7.js} +0 -0
  223. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{AntForm-8f9d0434dc3eb8cf.js → AntForm-86ffcc1ad3fa912e.js} +0 -0
  224. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikAntFormItem-a14e51c7f22f3395.js → FormikAntFormItem-ec04f595465bdf69.js} +0 -0
  225. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikControlled-a40645b57822684d.js → FormikControlled-41d309754ff0c1de.js} +0 -0
  226. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikField-c55df6527b700701.js → FormikField-cab1f78cec7808f9.js} +0 -0
  227. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{forms-292d761616b162a0.js → forms-eb6058221403b156.js} +0 -0
  228. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{table-migration-135bcf384b81820a.js → table-migration-48500551fd6a7602.js} +0 -0
  229. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-70efccbe0732786b.js → configure-d83e5bd52a638234.js} +0 -0
  230. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-63c6f0193634add5.js → [id]-e784c05d056b2371.js} +0 -0
  231. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-e76567f0374d5912.js → add-property-0a7a2db148a7561a.js} +0 -0
  232. /fides/ui-build/static/admin/_next/static/chunks/pages/{properties-2050b7dac0413c11.js → properties-da734840e4f9d04b.js} +0 -0
  233. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/{alpha-913f8eab62460f31.js → alpha-a82f3df840d5c1b5.js} +0 -0
  234. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-2ed1ee6017c0656a.js → about-d06fb16487705b9d.js} +0 -0
  235. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/{[purpose_id]-bffd6292e7e0302a.js → [purpose_id]-9495e2eb506606c7.js} +0 -0
  236. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-e54b4a7d80b81800.js → consent-93a978443bf299db.js} +0 -0
  237. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-a649ca07ce51b0fe.js → domain-records-16fdd91a81074dd1.js} +0 -0
  238. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-eb675ba600cea2a8.js → domains-4cdd6001e7cb9aee.js} +0 -0
  239. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-4db2f42f90867890.js → email-templates-1914de830ce5cfc4.js} +0 -0
  240. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-89b404989b4ea21c.js → locations-2e635dcd11b78224.js} +0 -0
  241. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-eb1ecff37fd85c72.js → organization-f547f1f33c12faf3.js} +0 -0
  242. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-07d883b5aaec58f2.js → regulations-7c02e469d8c5bd74.js} +0 -0
  243. /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-61ecb7546830c345.js → test-datasets-20b1193ed76c56b0.js} +0 -0
  244. /fides/ui-build/static/admin/_next/static/chunks/pages/{systems-44d714017dd87e62.js → systems-fbc8761ef4d55516.js} +0 -0
  245. /fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-583a7f0c1bead240.js → user-management-9cec020f89544426.js} +0 -0
@@ -0,0 +1,268 @@
1
+ from typing import Any, List, Optional
2
+
3
+ from fideslang.models import Dataset, DatasetField
4
+ from loguru import logger
5
+ from sqlalchemy import text
6
+ from sqlalchemy.orm import Session
7
+
8
+ from fides.api.models.datasetconfig import DatasetConfig
9
+ from fides.api.models.privacy_request.privacy_request import PrivacyRequest
10
+ from fides.api.schemas.policy import ActionType
11
+
12
+
13
+ # TODO: keeping this for a bit to help with development and testing
14
+ def get_redaction_entities_map(db: Session) -> set[str]:
15
+ """
16
+ Create a set of hierarchical entity keys that should be redacted based on fides_meta.redact: name.
17
+
18
+ This utility function reads all enabled dataset configurations from the database
19
+ and builds a set of hierarchical entity keys (dataset_name, dataset_name.collection_name,
20
+ dataset_name.collection_name.field_name) that have fides_meta.redact set to "name".
21
+
22
+ Supports deeply nested field structures with unlimited nesting depth.
23
+
24
+ Args:
25
+ db: Database session
26
+
27
+ Returns:
28
+ Set of hierarchical entity keys that should be redacted
29
+ """
30
+ redaction_entities = set()
31
+
32
+ try:
33
+ dataset_configs = DatasetConfig.all(db=db)
34
+
35
+ for dataset_config in dataset_configs:
36
+ ctl_dataset = dataset_config.ctl_dataset
37
+ if not ctl_dataset:
38
+ continue
39
+
40
+ dataset = Dataset.model_validate(dataset_config.ctl_dataset)
41
+ # Intentionally using the fides_key instead of name since it's always provided
42
+ dataset_name = dataset.fides_key
43
+
44
+ # Check dataset level
45
+ if dataset.fides_meta and dataset.fides_meta.redact == "name":
46
+ redaction_entities.add(dataset_name)
47
+
48
+ # Check collection level
49
+ for collection_dict in dataset.collections:
50
+ # Collections are stored as dictionaries in the database
51
+ collection_name = collection_dict.name
52
+ if not collection_name:
53
+ continue
54
+
55
+ collection_path = f"{dataset_name}.{collection_name}"
56
+ collection_fides_meta = collection_dict.fides_meta
57
+
58
+ if collection_fides_meta and collection_fides_meta.redact == "name":
59
+ redaction_entities.add(collection_path)
60
+
61
+ # Check field level (with recursive nested field support)
62
+ _traverse_fields_for_redaction(
63
+ collection_dict.fields, collection_path, redaction_entities
64
+ )
65
+
66
+ except Exception as exc:
67
+ # Log error but don't fail, just return empty set
68
+ logger.warning(f"Error extracting redaction configurations: {exc}")
69
+
70
+ return redaction_entities
71
+
72
+
73
+ def get_redaction_entities_map_db(db: Session) -> set[str]:
74
+ """
75
+ Create a set of hierarchical entity keys that should be redacted based on fides_meta.redact: name.
76
+
77
+ This function uses a hybrid approach:
78
+ 1. First identifies datasets that contain ANY redaction metadata at any level
79
+ 2. Then processes only those datasets with redaction metadata
80
+
81
+
82
+ Args:
83
+ db: Database session
84
+
85
+ Returns:
86
+ Set of hierarchical entity keys that should be redacted
87
+ """
88
+ redaction_entities: set[str] = set()
89
+
90
+ try:
91
+ # Step 1: Pre-filter to find datasets with ANY redaction metadata
92
+ # Simple existence check - no paths needed, just check if redaction exists anywhere
93
+ pre_filter_query = """
94
+ SELECT DISTINCT dc.ctl_dataset_id
95
+ FROM datasetconfig dc
96
+ JOIN ctl_datasets ds ON dc.ctl_dataset_id = ds.id
97
+ WHERE
98
+ -- Dataset-level redaction
99
+ ds.fides_meta->>'redact' = 'name'
100
+ OR
101
+ -- Collection-level redaction
102
+ EXISTS (
103
+ SELECT 1 FROM jsonb_array_elements(ds.collections::jsonb) AS collection
104
+ WHERE collection->'fides_meta'->>'redact' = 'name'
105
+ LIMIT 1
106
+ )
107
+ OR
108
+ -- Field-level redaction using jsonb_path_query
109
+ EXISTS (
110
+ SELECT 1
111
+ FROM jsonb_path_query(ds.collections::jsonb, '$.**.fides_meta') AS fides_meta
112
+ WHERE fides_meta->>'redact' = 'name'
113
+ LIMIT 1
114
+ )
115
+ """
116
+
117
+ candidate_datasets = db.execute(pre_filter_query).fetchall()
118
+
119
+ if not candidate_datasets:
120
+ logger.debug("No datasets found with redaction metadata")
121
+ return redaction_entities
122
+
123
+ logger.debug(
124
+ f"Pre-filtered to {len(candidate_datasets)} datasets with redaction metadata"
125
+ )
126
+
127
+ # Step 2: Process only the candidate datasets with targeted queries
128
+ # Convert to a format we can use in SQL ANY clause
129
+ dataset_ids = [row[0] for row in candidate_datasets]
130
+
131
+ # Query for dataset-level redactions (only on candidate datasets)
132
+ dataset_query = text(
133
+ """
134
+ SELECT ds.fides_key as entity_path
135
+ FROM datasetconfig dc
136
+ JOIN ctl_datasets ds ON dc.ctl_dataset_id = ds.id
137
+ WHERE ds.id = ANY(:dataset_ids)
138
+ AND ds.fides_meta->>'redact' = 'name'
139
+ """
140
+ )
141
+
142
+ dataset_results = db.execute(
143
+ dataset_query, {"dataset_ids": dataset_ids}
144
+ ).fetchall()
145
+ for row in dataset_results:
146
+ redaction_entities.add(row[0])
147
+
148
+ # Query for collection-level redactions (only on candidate datasets)
149
+ collection_query = text(
150
+ """
151
+ SELECT ds.fides_key || '.' || (collection->>'name') as entity_path
152
+ FROM datasetconfig dc
153
+ JOIN ctl_datasets ds ON dc.ctl_dataset_id = ds.id
154
+ CROSS JOIN LATERAL jsonb_array_elements(ds.collections::jsonb) AS collection
155
+ WHERE ds.id = ANY(:dataset_ids)
156
+ AND collection->'fides_meta'->>'redact' = 'name'
157
+ AND collection->>'name' IS NOT NULL
158
+ """
159
+ )
160
+
161
+ collection_results = db.execute(
162
+ collection_query, {"dataset_ids": dataset_ids}
163
+ ).fetchall()
164
+ for row in collection_results:
165
+ redaction_entities.add(row[0])
166
+
167
+ # Query for field-level redactions (including nested fields)
168
+ # This uses a recursive CTE to handle arbitrary nesting levels
169
+ field_query = text(
170
+ """
171
+ WITH RECURSIVE field_hierarchy AS (
172
+ -- Base case: top-level fields in collections (only candidate datasets)
173
+ SELECT
174
+ ds.fides_key || '.' ||
175
+ (collection->>'name') || '.' ||
176
+ (field->>'name') as entity_path,
177
+ field->'fields' as nested_fields,
178
+ field->'fides_meta'->>'redact' as redact_value
179
+ FROM datasetconfig dc
180
+ JOIN ctl_datasets ds ON dc.ctl_dataset_id = ds.id
181
+ CROSS JOIN LATERAL jsonb_array_elements(ds.collections::jsonb) AS collection
182
+ CROSS JOIN LATERAL jsonb_array_elements(collection->'fields') AS field
183
+ WHERE ds.id = ANY(:dataset_ids)
184
+ AND collection->>'name' IS NOT NULL
185
+ AND field->>'name' IS NOT NULL
186
+
187
+ UNION ALL
188
+
189
+ -- Recursive case: nested fields
190
+ SELECT
191
+ fh.entity_path || '.' || (nested_field->>'name') as entity_path,
192
+ nested_field->'fields' as nested_fields,
193
+ nested_field->'fides_meta'->>'redact' as redact_value
194
+ FROM field_hierarchy fh
195
+ CROSS JOIN LATERAL jsonb_array_elements(fh.nested_fields) AS nested_field
196
+ WHERE jsonb_typeof(fh.nested_fields) = 'array'
197
+ AND nested_field->>'name' IS NOT NULL
198
+ )
199
+ SELECT DISTINCT entity_path
200
+ FROM field_hierarchy
201
+ WHERE redact_value = 'name'
202
+ """
203
+ )
204
+
205
+ field_results = db.execute(field_query, {"dataset_ids": dataset_ids}).fetchall()
206
+ for row in field_results:
207
+ redaction_entities.add(row[0])
208
+
209
+ logger.debug(f"Found {len(redaction_entities)} entities requiring redaction")
210
+
211
+ except Exception as exc:
212
+ # Log error but don't fail, just return empty set
213
+ logger.warning(
214
+ f"Error extracting redaction configurations from database: {exc}"
215
+ )
216
+
217
+ return redaction_entities
218
+
219
+
220
+ def map_privacy_request(privacy_request: PrivacyRequest) -> dict[str, Any]:
221
+ """Creates a map with a subset of values from the privacy request"""
222
+ request_data: dict[str, Any] = {}
223
+ request_data["id"] = privacy_request.id
224
+
225
+ action_type: Optional[ActionType] = privacy_request.policy.get_action_type()
226
+ if action_type:
227
+ request_data["type"] = action_type.value
228
+
229
+ request_data["identity"] = {
230
+ key: value
231
+ for key, value in privacy_request.get_persisted_identity()
232
+ .labeled_dict(include_default_labels=True)
233
+ .items()
234
+ if value["value"] is not None
235
+ }
236
+
237
+ if privacy_request.requested_at:
238
+ request_data["requested_at"] = privacy_request.requested_at.strftime(
239
+ "%m/%d/%Y %H:%M %Z"
240
+ )
241
+ return request_data
242
+
243
+
244
+ def _traverse_fields_for_redaction(
245
+ fields: List[DatasetField], current_path: str, redaction_entities: set[str]
246
+ ) -> None:
247
+ """
248
+ Recursively traverse nested fields to find redaction entities.
249
+
250
+ Args:
251
+ fields: List of field dictionaries to traverse
252
+ current_path: Current hierarchical path (e.g., "dataset.collection")
253
+ redaction_entities: Set to add redacted field paths to
254
+ """
255
+ for field in fields:
256
+ field_name = field.name
257
+ if not field_name:
258
+ continue
259
+
260
+ field_path = f"{current_path}.{field_name}"
261
+ field_fides_meta = field.fides_meta
262
+
263
+ if field_fides_meta and field_fides_meta.redact == "name":
264
+ redaction_entities.add(field_path)
265
+
266
+ # Recursively check nested fields
267
+ if field.fields:
268
+ _traverse_fields_for_redaction(field.fields, field_path, redaction_entities)
@@ -312,8 +312,14 @@ def upload_and_save_access_results( # pylint: disable=R0912
312
312
  loaded_attachments = [
313
313
  attachment
314
314
  for attachment in privacy_request.attachments
315
- if AttachmentReferenceType.access_manual_webhook
316
- not in [ref.reference_type for ref in attachment.references]
315
+ if not any(
316
+ ref.reference_type
317
+ in [
318
+ AttachmentReferenceType.access_manual_webhook,
319
+ AttachmentReferenceType.manual_task_submission,
320
+ ]
321
+ for ref in attachment.references
322
+ )
317
323
  ]
318
324
  attachments = get_attachments_content(loaded_attachments)
319
325
  # Process attachments once for both upload and storage
@@ -540,6 +540,7 @@ def _get_request_task_ids_in_progress(
540
540
 
541
541
 
542
542
  # pylint: disable=too-many-branches
543
+ # pylint: disable=too-many-statements
543
544
  @celery_app.task(base=DatabaseTask, bind=True)
544
545
  def requeue_interrupted_tasks(self: DatabaseTask) -> None:
545
546
  """
@@ -660,8 +661,24 @@ def requeue_interrupted_tasks(self: DatabaseTask) -> None:
660
661
  break
661
662
 
662
663
  # If the task ID is not cached, we can't check if it's running
663
- # This means the subtask is stuck - cancel the entire privacy request
664
+ # This means the subtask is stuck - but we need to handle this differently
665
+ # based on the privacy request status
664
666
  if not subtask_id:
667
+ if (
668
+ privacy_request.status
669
+ == PrivacyRequestStatus.requires_input
670
+ ):
671
+ # For requires_input status, don't automatically error the request
672
+ # as it's intentionally waiting for user input
673
+ logger.warning(
674
+ f"No task ID found for request task {request_task_id} "
675
+ f"(privacy request {privacy_request.id}) in requires_input status - "
676
+ f"keeping request in current status as it may be waiting for manual input"
677
+ )
678
+ should_requeue = False
679
+ break
680
+
681
+ # For other statuses, cancel the entire privacy request
665
682
  _cancel_interrupted_tasks_and_error_privacy_request(
666
683
  db,
667
684
  privacy_request,
@@ -51,13 +51,7 @@ def upload(
51
51
  config.secrets is not None,
52
52
  )
53
53
 
54
- if config.secrets:
55
- logger.debug("Storage config secrets type: {}", type(config.secrets))
56
- if isinstance(config.secrets, dict):
57
- logger.debug("Storage config secrets keys: {}", list(config.secrets.keys()))
58
- else:
59
- logger.debug("Storage config secrets is not a dict: {}", config.secrets)
60
- else:
54
+ if not config.secrets:
61
55
  logger.warning("Storage config has no secrets!")
62
56
 
63
57
  uploader: Any = _get_uploader_from_config_type(config.type) # type: ignore
@@ -130,19 +124,6 @@ def _s3_uploader(
130
124
  config.secrets is not None,
131
125
  )
132
126
 
133
- if config.secrets:
134
- logger.debug(
135
- "Config secrets keys: {}",
136
- (
137
- list(config.secrets.keys())
138
- if isinstance(config.secrets, dict)
139
- else "Not a dict"
140
- ),
141
- )
142
- logger.debug("Config secrets type: {}", type(config.secrets))
143
- else:
144
- logger.warning("Config secrets is None or empty!")
145
-
146
127
  enable_streaming = config.details.get(StorageDetails.ENABLE_STREAMING.value, False)
147
128
  file_key: str = _construct_file_key(privacy_request.id, config, enable_streaming)
148
129
 
@@ -154,7 +135,6 @@ def _s3_uploader(
154
135
  file_key = f"{privacy_request.id}.zip"
155
136
  # Use streaming upload for better memory efficiency
156
137
  logger.debug("Using streaming S3 upload for {}", file_key)
157
- logger.debug("Calling upload_to_s3_streaming with secrets: {}", config.secrets)
158
138
  return upload_to_s3_streaming(
159
139
  config.secrets, # type: ignore
160
140
  data,
@@ -17,7 +17,6 @@ def stream_dsr_buffer_to_storage(
17
17
  bucket_name: str,
18
18
  file_key: str,
19
19
  dsr_buffer: BytesIO,
20
- content_type: str = "application/zip",
21
20
  ) -> None:
22
21
  """Stream DSR buffer to storage using smart-open streaming.
23
22
 
@@ -29,15 +28,12 @@ def stream_dsr_buffer_to_storage(
29
28
  bucket_name: Storage bucket name
30
29
  file_key: File key in storage
31
30
  dsr_buffer: Pre-generated DSR report buffer (BytesIO)
32
- content_type: MIME type for the uploaded file (defaults to application/zip)
33
31
  """
34
32
  # Get the content from the buffer
35
33
  content = dsr_buffer.getvalue()
36
34
  try:
37
35
  # Use smart-open's streaming upload for efficient memory usage
38
- with storage_client.stream_upload(
39
- bucket_name, file_key, content_type=content_type
40
- ) as upload_stream:
36
+ with storage_client.stream_upload(bucket_name, file_key) as upload_stream:
41
37
  upload_stream.write(content)
42
38
 
43
39
  except Exception as e:
@@ -7,7 +7,7 @@ from typing import Any, Optional
7
7
  from fideslang.validation import AnyHttpUrlString
8
8
  from loguru import logger
9
9
 
10
- from fides.api.schemas.storage.storage import AWSAuthMethod, StorageSecrets
10
+ from fides.api.schemas.storage.storage import AWSAuthMethod
11
11
  from fides.api.service.storage.s3 import create_presigned_url_for_s3
12
12
  from fides.api.service.storage.streaming.base_storage_client import BaseStorageClient
13
13
  from fides.api.util.aws_util import get_s3_client
@@ -20,17 +20,19 @@ class S3StorageClient(BaseStorageClient):
20
20
  generation for the smart-open storage client.
21
21
  """
22
22
 
23
- def __init__(self, storage_secrets: dict[StorageSecrets, Any]):
23
+ def __init__(self, auth_method: str, storage_secrets: dict[str, Any]):
24
24
  """Initialize the storage client with secrets.
25
25
 
26
26
  Args:
27
- storage_secrets: Provider-specific storage credentials and configuration using StorageSecrets enum keys
27
+ storage_secrets: Provider-specific storage credentials and configuration using string keys
28
+ (e.g., "aws_access_key_id", "region_name") from format_secrets()
28
29
  """
29
30
  super().__init__(storage_secrets)
30
- self.storage_secrets: dict[StorageSecrets, Any] = storage_secrets
31
+ self.storage_secrets: dict[str, Any] = storage_secrets
32
+ self.auth_method = auth_method
31
33
 
32
34
  def build_uri(self, bucket: str, key: str) -> str:
33
- """Build the S3 URI for the storage location.
35
+ """Build S3 URI for the given bucket and key.
34
36
 
35
37
  Args:
36
38
  bucket: S3 bucket name
@@ -45,26 +47,75 @@ class S3StorageClient(BaseStorageClient):
45
47
 
46
48
  def get_transport_params(self) -> dict[str, Any]:
47
49
  """Get S3-specific transport parameters for smart-open.
50
+ Type annotation: get_s3_client returns a boto3 S3 client object, not a Session
51
+ This is what smart-open expects for the "client" transport parameter
48
52
 
49
53
  Returns:
50
54
  Dictionary of S3 transport parameters for smart-open
51
55
  """
52
- params = {}
53
-
54
- if StorageSecrets.AWS_ACCESS_KEY_ID in self.storage_secrets:
55
- params["access_key"] = self.storage_secrets[
56
- StorageSecrets.AWS_ACCESS_KEY_ID
57
- ]
58
- if StorageSecrets.AWS_SECRET_ACCESS_KEY in self.storage_secrets:
59
- params["secret_key"] = self.storage_secrets[
60
- StorageSecrets.AWS_SECRET_ACCESS_KEY
61
- ]
62
- if StorageSecrets.REGION_NAME in self.storage_secrets:
63
- params["region"] = self.storage_secrets[StorageSecrets.REGION_NAME]
64
- if StorageSecrets.AWS_ASSUME_ROLE in self.storage_secrets:
65
- params["assume_role_arn"] = self.storage_secrets[
66
- StorageSecrets.AWS_ASSUME_ROLE
67
- ]
56
+ params: dict[str, Any] = {}
57
+
58
+ # Create S3 client for smart-open
59
+ try:
60
+ # Determine auth method based on available credentials
61
+ if self.auth_method == AWSAuthMethod.AUTOMATIC.value:
62
+
63
+ # For automatic authentication, check if region is available
64
+ if not self.storage_secrets.get("region_name", None):
65
+ logger.warning(
66
+ "No region specified in storage secrets for automatic authentication"
67
+ "This may cause credential issues - consider setting a default region"
68
+ )
69
+
70
+ # Extract assume_role_arn if present
71
+ assume_role_arn = None
72
+ if (
73
+ "assume_role_arn" in self.storage_secrets
74
+ and self.storage_secrets["assume_role_arn"]
75
+ ):
76
+ assume_role_arn = self.storage_secrets["assume_role_arn"]
77
+ logger.debug(f"Using assume role ARN: {assume_role_arn}")
78
+
79
+ # Create S3 client using existing utility
80
+ # get_s3_client returns a boto3 S3 client, not a Session
81
+ s3_client: Any = None
82
+ try:
83
+ s3_client = get_s3_client(
84
+ self.auth_method, self.storage_secrets, assume_role_arn # type: ignore
85
+ )
86
+ logger.debug("Successfully created S3 client")
87
+ except Exception as e:
88
+ # For automatic authentication, try to provide more helpful error messages
89
+ if self.auth_method == AWSAuthMethod.AUTOMATIC.value:
90
+ logger.error(
91
+ f"Failed to create S3 client with automatic authentication: {e}. "
92
+ "This usually means AWS credentials are not available in the environment"
93
+ "Please ensure AWS credentials are configured via environment variables, IAM roles, or AWS profiles"
94
+ )
95
+ raise ValueError(
96
+ f"Automatic AWS authentication failed: {e}. Please check your AWS credential configuration."
97
+ )
98
+ raise
99
+
100
+ params["client"] = s3_client
101
+
102
+ except Exception as e:
103
+ logger.error(f"Failed to create S3 client for smart-open: {e}")
104
+ raise
105
+
106
+ # Include credentials at top level for compatibility
107
+ # Note: When using an S3 client, these credential parameters are not needed
108
+ # and will be ignored by smart-open, causing warnings
109
+ # Only include them if no S3 client is provided (fallback scenario)
110
+ if not params.get("client"):
111
+ for key, transport_key in [
112
+ ("aws_access_key_id", "access_key"),
113
+ ("aws_secret_access_key", "secret_key"),
114
+ ("region_name", "region"),
115
+ ("assume_role_arn", "assume_role_arn"),
116
+ ]:
117
+ if key in self.storage_secrets and self.storage_secrets[key]:
118
+ params[transport_key] = self.storage_secrets[key]
68
119
 
69
120
  return params
70
121
 
@@ -85,28 +136,15 @@ class S3StorageClient(BaseStorageClient):
85
136
  Exception: If presigned URL generation fails
86
137
  """
87
138
  try:
88
- # Storage secrets are already in the right format for get_s3_client
89
- # get_s3_client expects dict[StorageSecrets, Any] with enum keys
90
- s3_secrets = self.storage_secrets
91
-
92
- # Determine auth method based on available credentials
93
- # If AWS credentials are present, use SECRET_KEYS, otherwise use AUTOMATIC
94
- if (
95
- StorageSecrets.AWS_ACCESS_KEY_ID in self.storage_secrets
96
- and StorageSecrets.AWS_SECRET_ACCESS_KEY in self.storage_secrets
97
- and self.storage_secrets[StorageSecrets.AWS_ACCESS_KEY_ID]
98
- and self.storage_secrets[StorageSecrets.AWS_SECRET_ACCESS_KEY]
99
- ):
100
- auth_method = AWSAuthMethod.SECRET_KEYS.value
101
- else:
102
- auth_method = AWSAuthMethod.AUTOMATIC.value
103
-
104
139
  # Extract assume_role_arn if present for role assumption
105
140
  assume_role_arn = None
106
- if StorageSecrets.AWS_ASSUME_ROLE in self.storage_secrets:
107
- assume_role_arn = self.storage_secrets[StorageSecrets.AWS_ASSUME_ROLE]
141
+ if "assume_role_arn" in self.storage_secrets:
142
+ assume_role_arn = self.storage_secrets["assume_role_arn"]
108
143
 
109
- s3_client = get_s3_client(auth_method, s3_secrets, assume_role_arn)
144
+ # get_s3_client returns a boto3 S3 client, not a Session
145
+ s3_client: Any = get_s3_client(
146
+ self.auth_method, self.storage_secrets, assume_role_arn # type: ignore
147
+ )
110
148
  return create_presigned_url_for_s3(s3_client, bucket, key, ttl_seconds)
111
149
  except Exception as e:
112
150
  logger.error(f"Failed to generate S3 presigned URL: {e}")
@@ -54,7 +54,8 @@ def upload_to_s3_streaming(
54
54
  return response
55
55
 
56
56
  try:
57
- storage_client = SmartOpenStorageClient("s3", formatted_secrets)
57
+ logger.debug("Creating SmartOpenStorageClient with formatted secrets")
58
+ storage_client = SmartOpenStorageClient("s3", auth_method, formatted_secrets)
58
59
 
59
60
  # Create upload config for the streaming interface
60
61
  upload_config = StorageUploadConfig(
@@ -92,6 +93,10 @@ def _process_storage_secrets_input(
92
93
  """Process input and convert to string-keyed dictionary."""
93
94
  final_secrets: dict[str, Any] = {}
94
95
 
96
+ logger.debug(
97
+ f"Processing storage secrets input of type: {type(storage_secrets).__name__}"
98
+ )
99
+
95
100
  if isinstance(storage_secrets, StorageSecretsS3):
96
101
  # Convert StorageSecretsS3 model directly to string keys
97
102
  for key, value in storage_secrets.model_dump().items():
@@ -133,6 +138,7 @@ def _validate_aws_credentials(final_secrets: dict[str, Any]) -> None:
133
138
  else:
134
139
  # AUTOMATIC authentication - check if region is provided
135
140
  has_region = "region_name" in final_secrets and final_secrets["region_name"]
141
+
136
142
  if not has_region:
137
143
  raise ValueError(
138
144
  "Missing required region_name for AUTOMATIC authentication. "
@@ -140,13 +146,6 @@ def _validate_aws_credentials(final_secrets: dict[str, Any]) -> None:
140
146
  )
141
147
 
142
148
 
143
- def _set_default_region(final_secrets: dict[str, Any]) -> None:
144
- """Set default region if missing."""
145
- if "region_name" not in final_secrets or not final_secrets["region_name"]:
146
- logger.debug("Setting default region to 'us-east-1'")
147
- final_secrets["region_name"] = "us-east-1"
148
-
149
-
150
149
  def format_secrets(
151
150
  storage_secrets: Union[StorageSecretsS3, dict[StorageSecrets, Any]] # type: ignore[misc]
152
151
  ) -> dict[str, Any]:
@@ -156,8 +155,8 @@ def format_secrets(
156
155
  This function handles multiple credential formats and processes them through several stages:
157
156
  1. Input processing: Accepts either StorageSecretsS3 models (from API) or raw dicts (from database)
158
157
  2. Key normalization: Converts all keys to string format for consistency
159
- 3. Validation: Ensures required AWS credentials are present based on auth method
160
- 4. Default setting: Sets default region if missing (after validation)
158
+ 3. Default setting: Sets default region if missing (before validation for better automatic auth support)
159
+ 4. Validation: Ensures required AWS credentials are present based on auth method
161
160
  5. Return: Returns string-keyed dict ready for S3StorageClient
162
161
 
163
162
  Input formats:
@@ -179,18 +178,7 @@ def format_secrets(
179
178
  Raises:
180
179
  ValueError: If required AWS credentials are missing for the chosen auth method
181
180
  """
182
- logger.debug("format_secrets called with type: {}", type(storage_secrets).__name__)
183
-
184
- # Stage 1: Process input and create final format directly
185
181
  final_secrets = _process_storage_secrets_input(storage_secrets)
186
-
187
- # Stage 2: Validate required credentials BEFORE setting defaults
188
182
  _validate_aws_credentials(final_secrets)
189
183
 
190
- # Stage 3: Set default region if missing (after validation)
191
- _set_default_region(final_secrets)
192
-
193
- logger.debug(
194
- "format_secrets completed successfully with {} keys", len(final_secrets)
195
- )
196
184
  return final_secrets
@@ -29,7 +29,12 @@ class SmartOpenStorageClient:
29
29
 
30
30
  min_part_size: int = MIN_PART_SIZE
31
31
 
32
- def __init__(self, storage_type: str, storage_secrets: Any):
32
+ def __init__(
33
+ self,
34
+ storage_type: str,
35
+ auth_method: Optional[str],
36
+ storage_secrets: Any,
37
+ ):
33
38
  """Initialize the smart-open storage client.
34
39
 
35
40
  Args:
@@ -38,9 +43,10 @@ class SmartOpenStorageClient:
38
43
  Will be passed to the specific storage client implementation.
39
44
  """
40
45
  self.storage_type = StorageClientFactory._normalize_storage_type(storage_type)
46
+ self.auth_method = auth_method
41
47
  self.storage_secrets = storage_secrets
42
48
  self._provider_client = StorageClientFactory.create_client(
43
- storage_type, storage_secrets
49
+ storage_type, auth_method, storage_secrets
44
50
  )
45
51
 
46
52
  def _build_uri(self, bucket: str, key: str) -> str:
@@ -216,7 +222,6 @@ class SmartOpenStorageClient:
216
222
  self,
217
223
  bucket: str,
218
224
  key: str,
219
- content_type: Optional[str] = None,
220
225
  ) -> Any:
221
226
  """Get a writable stream for uploading data.
222
227
 
@@ -225,7 +230,6 @@ class SmartOpenStorageClient:
225
230
  Args:
226
231
  bucket: Storage bucket/container name
227
232
  key: Object key/path
228
- content_type: MIME type of the object
229
233
 
230
234
  Returns:
231
235
  Writable file-like object
@@ -233,9 +237,6 @@ class SmartOpenStorageClient:
233
237
  uri = self._build_uri(bucket, key)
234
238
  transport_params = self._get_transport_params()
235
239
 
236
- if content_type:
237
- transport_params["content_type"] = content_type
238
-
239
240
  return smart_open.open(uri, "wb", transport_params=transport_params)
240
241
 
241
242
  @retry_cloud_storage_operation(