ethyca-fides 2.63.3rc0__py2.py3-none-any.whl → 2.64.1b0__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 (362) hide show
  1. {ethyca_fides-2.63.3rc0.dist-info → ethyca_fides-2.64.1b0.dist-info}/METADATA +2 -2
  2. {ethyca_fides-2.63.3rc0.dist-info → ethyca_fides-2.64.1b0.dist-info}/RECORD +249 -242
  3. fides/_version.py +3 -3
  4. fides/api/alembic/migrations/versions/ba414a58ba90_create_manual_task_config_tables.py +121 -0
  5. fides/api/api/v1/endpoints/connection_endpoints.py +16 -3
  6. fides/api/api/v1/endpoints/oauth_endpoints.py +8 -2
  7. fides/api/db/base.py +4 -0
  8. fides/api/models/attachment.py +37 -23
  9. fides/api/models/manual_tasks/__init__.py +6 -0
  10. fides/api/models/manual_tasks/manual_task.py +11 -1
  11. fides/api/models/manual_tasks/manual_task_config.py +136 -0
  12. fides/api/models/manual_tasks/manual_task_log.py +13 -9
  13. fides/api/schemas/connection_configuration/connection_config.py +19 -4
  14. fides/api/schemas/manual_tasks/manual_task_config.py +311 -0
  15. fides/api/schemas/manual_tasks/manual_task_schemas.py +1 -1
  16. fides/api/schemas/manual_tasks/manual_task_status.py +1 -1
  17. fides/api/schemas/privacy_request.py +2 -0
  18. fides/api/service/connectors/query_configs/saas_query_config.py +15 -0
  19. fides/api/service/privacy_request/attachment_handling.py +132 -0
  20. fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +264 -46
  21. fides/api/service/privacy_request/dsr_package/templates/attachments_index.html +33 -0
  22. fides/api/service/privacy_request/dsr_package/templates/collection_index.html +34 -9
  23. fides/api/service/privacy_request/dsr_package/templates/main.css +45 -2
  24. fides/api/service/privacy_request/dsr_package/templates/welcome.html +12 -8
  25. fides/api/service/privacy_request/request_runner_service.py +258 -139
  26. fides/api/service/storage/gcs.py +15 -3
  27. fides/api/service/storage/s3.py +28 -14
  28. fides/api/service/storage/util.py +45 -7
  29. fides/api/tasks/csv_utils.py +170 -0
  30. fides/api/tasks/encryption_utils.py +42 -0
  31. fides/api/tasks/storage.py +85 -91
  32. fides/api/util/cache.py +6 -3
  33. fides/api/util/saas_util.py +1 -0
  34. fides/config/redis_settings.py +105 -44
  35. fides/service/manual_tasks/manual_task_config_service.py +370 -0
  36. fides/service/manual_tasks/manual_task_service.py +231 -87
  37. fides/service/manual_tasks/utils.py +185 -0
  38. fides/ui-build/static/admin/404.html +1 -1
  39. fides/ui-build/static/admin/_next/static/chunks/1040-630c7f4284dc6f70.js +1 -0
  40. fides/ui-build/static/admin/_next/static/chunks/1100-d43cb04522a6505c.js +1 -0
  41. fides/ui-build/static/admin/_next/static/chunks/1316-6cc72a45ebf7ff81.js +1 -0
  42. fides/ui-build/static/admin/_next/static/chunks/1817-b4688ba5042ec687.js +1 -0
  43. fides/ui-build/static/admin/_next/static/chunks/2430-b480401d44c55416.js +1 -0
  44. fides/ui-build/static/admin/_next/static/chunks/2599-6c4d22e75028d8b6.js +1 -0
  45. fides/ui-build/static/admin/_next/static/chunks/2921-aabf41bf3d7573f9.js +1 -0
  46. fides/ui-build/static/admin/_next/static/chunks/3450-bdaeb35442d810b4.js +1 -0
  47. fides/ui-build/static/admin/_next/static/chunks/3505-a79256cd851dfab4.js +1 -0
  48. fides/ui-build/static/admin/_next/static/chunks/3513-8677ee280eaef0da.js +1 -0
  49. fides/ui-build/static/admin/_next/static/chunks/3855-0dec3e7d9e886550.js +1 -0
  50. fides/ui-build/static/admin/_next/static/chunks/3872-ffa16c2df7ef0ab6.js +1 -0
  51. fides/ui-build/static/admin/_next/static/chunks/3923-5d580fbb1dd6ae01.js +1 -0
  52. fides/ui-build/static/admin/_next/static/chunks/401-fceaae662cfca5a5.js +1 -0
  53. fides/ui-build/static/admin/_next/static/chunks/4060-71cd041e5a57ca5a.js +1 -0
  54. fides/ui-build/static/admin/_next/static/chunks/409-037cfc3f096150f0.js +1 -0
  55. fides/ui-build/static/admin/_next/static/chunks/4121-78a76e980acbd539.js +1 -0
  56. fides/ui-build/static/admin/_next/static/chunks/4230-1cdb7ea1be370ed3.js +1 -0
  57. fides/ui-build/static/admin/_next/static/chunks/431-f72599f01b98f07d.js +1 -0
  58. fides/ui-build/static/admin/_next/static/chunks/4481-f597a7cf03f8c9e1.js +1 -0
  59. fides/ui-build/static/admin/_next/static/chunks/5309-4511df9708d5a63c.js +1 -0
  60. fides/ui-build/static/admin/_next/static/chunks/5574-3cd33b3a6c937899.js +1 -0
  61. fides/ui-build/static/admin/_next/static/chunks/6277-3fb4c7fc790a6d20.js +1 -0
  62. fides/ui-build/static/admin/_next/static/chunks/6662-cb11881dcaabe5e2.js +1 -0
  63. fides/ui-build/static/admin/_next/static/chunks/6853-a4097260e402980e.js +1 -0
  64. fides/ui-build/static/admin/_next/static/chunks/6882-3cc73d407a088d7d.js +1 -0
  65. fides/ui-build/static/admin/_next/static/chunks/69-6889d6674c95e7b5.js +1 -0
  66. fides/ui-build/static/admin/_next/static/chunks/6954-13a25cef3a8fdd76.js +1 -0
  67. fides/ui-build/static/admin/_next/static/chunks/699-8ca44b0de9fa20f0.js +1 -0
  68. fides/ui-build/static/admin/_next/static/chunks/7553-a95939c32d54b5b7.js +1 -0
  69. fides/ui-build/static/admin/_next/static/chunks/79-5670e31eb65d0a53.js +1 -0
  70. fides/ui-build/static/admin/_next/static/chunks/796-9a6b13c838e25538.js +1 -0
  71. fides/ui-build/static/admin/_next/static/chunks/8433-adb1fcb29d82f1b7.js +1 -0
  72. fides/ui-build/static/admin/_next/static/chunks/9046-fdf53cc7e926a8c1.js +1 -0
  73. fides/ui-build/static/admin/_next/static/chunks/{905-8ab919e7b274ed50.js → 905-742074a074be1055.js} +2 -2
  74. fides/ui-build/static/admin/_next/static/chunks/9226-ba6587e46ec7659a.js +1 -0
  75. fides/ui-build/static/admin/_next/static/chunks/{9392.81edc11e3a175275.js → 9392.25024e070026343d.js} +1 -1
  76. fides/ui-build/static/admin/_next/static/chunks/9676.e60a53f1f5890847.js +1 -0
  77. fides/ui-build/static/admin/_next/static/chunks/9767-e49b065d03ce4e80.js +1 -0
  78. fides/ui-build/static/admin/_next/static/chunks/9951-f9ab5cac7e2c05ab.js +1 -0
  79. fides/ui-build/static/admin/_next/static/chunks/pages/404-ac2f0844e5c4b4cd.js +1 -0
  80. fides/ui-build/static/admin/_next/static/chunks/pages/{_app-f70bffa5a7903c46.js → _app-8ec6e466f3373ac0.js} +10 -10
  81. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/manual-0a5f2310ce6b1059.js +1 -0
  82. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/multiple-00cb904825aad7e3.js +1 -0
  83. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-587e21d14e0a5196.js +1 -0
  84. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/add-vendors-fa7305b88c1afd20.js +1 -0
  85. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-f140ec9d8e8a0f7a.js +1 -0
  86. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-0edb7c92518e7d21.js +1 -0
  87. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-d8d926f0735a2546.js +1 -0
  88. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/[id]-5c949f2e3cef2398.js +1 -0
  89. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/new-130155cfb4a0bcc7.js +1 -0
  90. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-1135ad8924d32c36.js +1 -0
  91. fides/ui-build/static/admin/_next/static/chunks/pages/consent/properties-776855e370414beb.js +1 -0
  92. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-21c23f75ff1135d9.js +1 -0
  93. fides/ui-build/static/admin/_next/static/chunks/pages/consent-4d5ea70a77df1bb8.js +1 -0
  94. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-18e3faf7963962e4.js → [resourceUrn]-c623d6f1a61c8ea9.js} +1 -1
  95. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]-030ee304cbe5e530.js +1 -0
  96. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-cfac259a30641e68.js +1 -0
  97. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-7320524a47104798.js → [resourceUrn]-57bd5cdf784f059f.js} +1 -1
  98. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-7b5aec33da578745.js +1 -0
  99. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-c948f93c833d4358.js +1 -0
  100. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-333f53caab751428.js +1 -0
  101. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-27205f8457a1ecb0.js +1 -0
  102. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-20a1043f6a06198f.js +1 -0
  103. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-4c526db0c30c488a.js → [resourceUrn]-06edce289876dea1.js} +1 -1
  104. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-6f27dbb7c8edc69d.js → detection-faf326a6200637d0.js} +1 -1
  105. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-562d2b8ae90dd1f0.js → [resourceUrn]-64acf269256ee74f.js} +1 -1
  106. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-fe7f51502eda57c9.js → discovery-8c3e4be6d36da66d.js} +1 -1
  107. fides/ui-build/static/admin/_next/static/chunks/pages/datamap-fb50de22f83edd4a.js +1 -0
  108. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/[...subfieldNames]-41ab27c4195cfa93.js +1 -0
  109. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]-6e2caba24b3e78c2.js +1 -0
  110. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-904d43e31157aa55.js +1 -0
  111. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-d4e31a1c4a58800e.js +1 -0
  112. fides/ui-build/static/admin/_next/static/chunks/pages/dataset-8f7d16bc5e9229c8.js +1 -0
  113. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/[id]-4a33dd0371dbaebc.js +1 -0
  114. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-e88509346b2d2851.js +1 -0
  115. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-8f9b8890018e1ea5.js +1 -0
  116. fides/ui-build/static/admin/_next/static/chunks/pages/{fides-js-docs-80b241bf6cddb72e.js → fides-js-docs-5d8fd1af75f19e2f.js} +1 -1
  117. fides/ui-build/static/admin/_next/static/chunks/pages/index-1adb6bcb71701ac2.js +1 -0
  118. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-fdc2964fa7d1d8fd.js +1 -0
  119. fides/ui-build/static/admin/_next/static/chunks/pages/integrations-adc286ff254e7f41.js +1 -0
  120. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/[id]-9b4d1d61c7c97509.js +1 -0
  121. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/add-template-f8fd4795e260887c.js +1 -0
  122. fides/ui-build/static/admin/_next/static/chunks/pages/messaging-aa744ae8b61e5ff2.js +1 -0
  123. fides/ui-build/static/admin/_next/static/chunks/pages/poc/ant-components-3407158757fb3627.js +1 -0
  124. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/AntForm-47e947c02ae90fd0.js +1 -0
  125. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikAntFormItem-24f9a44512ce8827.js +1 -0
  126. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikControlled-e567a69f8c37fb9a.js +1 -0
  127. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikField-35a33f9c7def2220.js +1 -0
  128. fides/ui-build/static/admin/_next/static/chunks/pages/poc/forms-780e18dde8e38099.js +1 -0
  129. fides/ui-build/static/admin/_next/static/chunks/pages/poc/table-migration-7a17dffa515e5560.js +1 -0
  130. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-91dd0039cc2b70bc.js +1 -0
  131. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-b01cbbaac34dadbc.js +1 -0
  132. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-a15410de7f1d0f84.js +1 -0
  133. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure-1a1aa83a3f88844c.js +1 -0
  134. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-c4e1b69fb5bab61a.js +1 -0
  135. fides/ui-build/static/admin/_next/static/chunks/pages/properties/[id]-19737d4f21aadbee.js +1 -0
  136. fides/ui-build/static/admin/_next/static/chunks/pages/properties/add-property-9ccb295110feee20.js +1 -0
  137. fides/ui-build/static/admin/_next/static/chunks/pages/properties-3a1037a2e036212a.js +1 -0
  138. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-4cdbbb8cd1d8698e.js +1 -0
  139. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/alpha-965cc21889b25e44.js +1 -0
  140. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about-0e1c381d488a7ada.js +1 -0
  141. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/[purpose_id]-d0e350d2a2667883.js +1 -0
  142. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-7796fbf458f8f159.js +1 -0
  143. fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-6f866bc799a5f67f.js +1 -0
  144. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domain-records-0b44b2b224077dcd.js +1 -0
  145. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-5d00e1155bd11178.js +1 -0
  146. fides/ui-build/static/admin/_next/static/chunks/pages/settings/email-templates-d2cdbe770683e9df.js +1 -0
  147. fides/ui-build/static/admin/_next/static/chunks/pages/settings/locations-811dadb489f23d7d.js +1 -0
  148. fides/ui-build/static/admin/_next/static/chunks/pages/settings/organization-24108f615ff5e1e7.js +1 -0
  149. fides/ui-build/static/admin/_next/static/chunks/pages/settings/regulations-54f142bc3e4c95ba.js +1 -0
  150. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/test-datasets-fef247a87baeb080.js +1 -0
  151. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]-6eb886e7b7e6785b.js +1 -0
  152. fides/ui-build/static/admin/_next/static/chunks/pages/systems-7b71274334c559a4.js +1 -0
  153. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-18534d2a79a3850b.js +1 -0
  154. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-75d41fde668b9025.js +1 -0
  155. fides/ui-build/static/admin/_next/static/chunks/pages/user-management-dd43755b687c09a7.js +1 -0
  156. fides/ui-build/static/admin/_next/static/chunks/webpack-0a61b5bd21a41fe6.js +1 -0
  157. fides/ui-build/static/admin/_next/static/css/c693338e3bc8dcc6.css +1 -0
  158. fides/ui-build/static/admin/_next/static/nRQ3pmK_d3F5PJE39rP2h/_buildManifest.js +1 -0
  159. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  160. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  161. fides/ui-build/static/admin/add-systems.html +1 -1
  162. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  163. fides/ui-build/static/admin/consent/configure.html +1 -1
  164. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  165. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  166. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  167. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  168. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  169. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  170. fides/ui-build/static/admin/consent/properties.html +1 -1
  171. fides/ui-build/static/admin/consent/reporting.html +1 -1
  172. fides/ui-build/static/admin/consent.html +1 -1
  173. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  174. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  175. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  176. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  177. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  178. fides/ui-build/static/admin/data-catalog.html +1 -1
  179. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  180. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  181. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  182. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  183. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  184. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  185. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  186. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  187. fides/ui-build/static/admin/datamap.html +1 -1
  188. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  189. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  190. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  191. fides/ui-build/static/admin/dataset/new.html +1 -1
  192. fides/ui-build/static/admin/dataset.html +1 -1
  193. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  194. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  195. fides/ui-build/static/admin/datastore-connection.html +1 -1
  196. fides/ui-build/static/admin/images/connector-logos/salesforce.svg +41 -0
  197. fides/ui-build/static/admin/index.html +1 -1
  198. fides/ui-build/static/admin/integrations/[id].html +1 -1
  199. fides/ui-build/static/admin/integrations.html +1 -1
  200. fides/ui-build/static/admin/lib/fides-ext-gpp.js +1 -1
  201. fides/ui-build/static/admin/lib/fides-headless.js +1 -1
  202. fides/ui-build/static/admin/lib/fides-preview.js +1 -1
  203. fides/ui-build/static/admin/lib/fides-tcf.js +2 -2
  204. fides/ui-build/static/admin/lib/fides.js +2 -2
  205. fides/ui-build/static/admin/login/[provider].html +1 -1
  206. fides/ui-build/static/admin/login.html +1 -1
  207. fides/ui-build/static/admin/messaging/[id].html +1 -1
  208. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  209. fides/ui-build/static/admin/messaging.html +1 -1
  210. fides/ui-build/static/admin/poc/ant-components.html +1 -1
  211. fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
  212. fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
  213. fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
  214. fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
  215. fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
  216. fides/ui-build/static/admin/poc/forms.html +1 -1
  217. fides/ui-build/static/admin/poc/table-migration.html +1 -1
  218. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  219. fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
  220. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  221. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  222. fides/ui-build/static/admin/privacy-requests.html +1 -1
  223. fides/ui-build/static/admin/properties/[id].html +1 -1
  224. fides/ui-build/static/admin/properties/add-property.html +1 -1
  225. fides/ui-build/static/admin/properties.html +1 -1
  226. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  227. fides/ui-build/static/admin/settings/about/alpha.html +1 -1
  228. fides/ui-build/static/admin/settings/about.html +1 -1
  229. fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
  230. fides/ui-build/static/admin/settings/consent.html +1 -1
  231. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  232. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  233. fides/ui-build/static/admin/settings/domains.html +1 -1
  234. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  235. fides/ui-build/static/admin/settings/locations.html +1 -1
  236. fides/ui-build/static/admin/settings/organization.html +1 -1
  237. fides/ui-build/static/admin/settings/regulations.html +1 -1
  238. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  239. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  240. fides/ui-build/static/admin/systems.html +1 -1
  241. fides/ui-build/static/admin/taxonomy.html +1 -1
  242. fides/ui-build/static/admin/user-management/new.html +1 -1
  243. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  244. fides/ui-build/static/admin/user-management.html +1 -1
  245. fides/api/service/privacy_request/dsr_package/templates/item.html +0 -37
  246. fides/ui-build/static/admin/_next/static/chunks/1099-667e84655846e78c.js +0 -1
  247. fides/ui-build/static/admin/_next/static/chunks/1100-0b01f500b1eaf60d.js +0 -1
  248. fides/ui-build/static/admin/_next/static/chunks/1817-951f58ddd5d46081.js +0 -1
  249. fides/ui-build/static/admin/_next/static/chunks/1904-964f8eb328dc2715.js +0 -1
  250. fides/ui-build/static/admin/_next/static/chunks/2310-744354001d01f366.js +0 -1
  251. fides/ui-build/static/admin/_next/static/chunks/2921-49af55ad7c631c93.js +0 -1
  252. fides/ui-build/static/admin/_next/static/chunks/3119-e36ae5071c8f27a5.js +0 -1
  253. fides/ui-build/static/admin/_next/static/chunks/3505-113f9c95d34b7aae.js +0 -1
  254. fides/ui-build/static/admin/_next/static/chunks/3513-5c2d412a84a78971.js +0 -1
  255. fides/ui-build/static/admin/_next/static/chunks/3872-45cc725e241211f0.js +0 -1
  256. fides/ui-build/static/admin/_next/static/chunks/3923-b4f701ada3ef0ee0.js +0 -1
  257. fides/ui-build/static/admin/_next/static/chunks/401-7e800aed05537126.js +0 -1
  258. fides/ui-build/static/admin/_next/static/chunks/4060-90a52a9afc655bfc.js +0 -1
  259. fides/ui-build/static/admin/_next/static/chunks/4121-66b0e00d5e7ae817.js +0 -1
  260. fides/ui-build/static/admin/_next/static/chunks/4132-9b1731bfec6ee537.js +0 -1
  261. fides/ui-build/static/admin/_next/static/chunks/4481-7f6710c928bb0cb0.js +0 -1
  262. fides/ui-build/static/admin/_next/static/chunks/5258-2079138c8cc34f6c.js +0 -1
  263. fides/ui-build/static/admin/_next/static/chunks/5404-2694509cf5fa96da.js +0 -1
  264. fides/ui-build/static/admin/_next/static/chunks/5487-2cbd8d2169eb2a65.js +0 -1
  265. fides/ui-build/static/admin/_next/static/chunks/5683-37137111b3e769fb.js +0 -1
  266. fides/ui-build/static/admin/_next/static/chunks/6202-081545c7822d09af.js +0 -1
  267. fides/ui-build/static/admin/_next/static/chunks/6277-459e054b2702c60e.js +0 -1
  268. fides/ui-build/static/admin/_next/static/chunks/641-7e4eef3222cbda70.js +0 -1
  269. fides/ui-build/static/admin/_next/static/chunks/6853-688df0b88fe65fd5.js +0 -1
  270. fides/ui-build/static/admin/_next/static/chunks/69-00cba94689b9e740.js +0 -1
  271. fides/ui-build/static/admin/_next/static/chunks/6954-4e313b4599b8763d.js +0 -1
  272. fides/ui-build/static/admin/_next/static/chunks/7553-08bc3d9f66695111.js +0 -1
  273. fides/ui-build/static/admin/_next/static/chunks/79-fc548561beed136f.js +0 -1
  274. fides/ui-build/static/admin/_next/static/chunks/796-d6586308ed44334b.js +0 -1
  275. fides/ui-build/static/admin/_next/static/chunks/7980-736f9fd1e749ddd2.js +0 -1
  276. fides/ui-build/static/admin/_next/static/chunks/8032-74d94f7bd5b81c89.js +0 -1
  277. fides/ui-build/static/admin/_next/static/chunks/8433-51870336908fb253.js +0 -1
  278. fides/ui-build/static/admin/_next/static/chunks/8499-1fd392d0be9e8ee4.js +0 -1
  279. fides/ui-build/static/admin/_next/static/chunks/8702-d1c8296f9f6afc10.js +0 -1
  280. fides/ui-build/static/admin/_next/static/chunks/9033-fdd87182c15b91e2.js +0 -1
  281. fides/ui-build/static/admin/_next/static/chunks/9187-851756440f79cd75.js +0 -1
  282. fides/ui-build/static/admin/_next/static/chunks/9327-6a53461c9764b16f.js +0 -1
  283. fides/ui-build/static/admin/_next/static/chunks/9494-1ffb888b95289891.js +0 -1
  284. fides/ui-build/static/admin/_next/static/chunks/9676.61cf5f6d6a083175.js +0 -1
  285. fides/ui-build/static/admin/_next/static/chunks/9767-afd674014a1d1152.js +0 -1
  286. fides/ui-build/static/admin/_next/static/chunks/pages/404-ec04f826f3dd79ea.js +0 -1
  287. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/manual-1bec086d567aacff.js +0 -1
  288. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/multiple-f68bf100e4e60e88.js +0 -1
  289. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-effde5cfe49de55f.js +0 -1
  290. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/add-vendors-ec95ed8cb0712828.js +0 -1
  291. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-4cc195e15e0c3cc8.js +0 -1
  292. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-be8972c79fb39705.js +0 -1
  293. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-ccf96207de3b74ea.js +0 -1
  294. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/[id]-b9823cc008372cee.js +0 -1
  295. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/new-373f5772ab41bc93.js +0 -1
  296. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-b9beeee1bde5ca79.js +0 -1
  297. fides/ui-build/static/admin/_next/static/chunks/pages/consent/properties-6ab3a5baafebd199.js +0 -1
  298. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-8562790ae07f17c9.js +0 -1
  299. fides/ui-build/static/admin/_next/static/chunks/pages/consent-42d4d2d6e0253671.js +0 -1
  300. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]-9d1df6d890671b0c.js +0 -1
  301. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-7eb08af333baaa8e.js +0 -1
  302. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-bb57eed84f59a932.js +0 -1
  303. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-47eb34aef3e31ac6.js +0 -1
  304. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-76cefefc53e84c7f.js +0 -1
  305. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-9d8e84a8b4bf568d.js +0 -1
  306. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-f208fef5e72dafd6.js +0 -1
  307. fides/ui-build/static/admin/_next/static/chunks/pages/datamap-db45aa864e9dda8a.js +0 -1
  308. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/[...subfieldNames]-d5cfdebc74654337.js +0 -1
  309. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]-618b89228f83435e.js +0 -1
  310. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-12fc86f15a4c764e.js +0 -1
  311. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-c23d18a9b56c2e3c.js +0 -1
  312. fides/ui-build/static/admin/_next/static/chunks/pages/dataset-e3b2d8e3980ab093.js +0 -1
  313. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/[id]-6e27257eccf65f01.js +0 -1
  314. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-bbac1f624424282d.js +0 -1
  315. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-84eb56f5a07bdf4f.js +0 -1
  316. fides/ui-build/static/admin/_next/static/chunks/pages/index-773182b5e35d0045.js +0 -1
  317. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-eb28c927da5bd653.js +0 -1
  318. fides/ui-build/static/admin/_next/static/chunks/pages/integrations-781808bca01f8048.js +0 -1
  319. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/[id]-b51bee8680d66b20.js +0 -1
  320. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/add-template-1de479533f733fbd.js +0 -1
  321. fides/ui-build/static/admin/_next/static/chunks/pages/messaging-7dc295b3ed3d2224.js +0 -1
  322. fides/ui-build/static/admin/_next/static/chunks/pages/poc/ant-components-f64dac3392f5ded7.js +0 -1
  323. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/AntForm-eb020bfac4bee532.js +0 -1
  324. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikAntFormItem-0bd62e28b539e114.js +0 -1
  325. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikControlled-936877004113c2a6.js +0 -1
  326. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikField-6416f01524482af9.js +0 -1
  327. fides/ui-build/static/admin/_next/static/chunks/pages/poc/forms-3f4e1313d1f2969b.js +0 -1
  328. fides/ui-build/static/admin/_next/static/chunks/pages/poc/table-migration-1db7a54437db7db0.js +0 -1
  329. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-64236fd0141414fd.js +0 -1
  330. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-8cab04871908cfeb.js +0 -1
  331. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-150d40428245ee0c.js +0 -1
  332. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure-b6c876dceb16ad1b.js +0 -1
  333. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-20cdb2c8a03deae1.js +0 -1
  334. fides/ui-build/static/admin/_next/static/chunks/pages/properties/[id]-54bcc875592d1fb9.js +0 -1
  335. fides/ui-build/static/admin/_next/static/chunks/pages/properties/add-property-644dc669b508a79a.js +0 -1
  336. fides/ui-build/static/admin/_next/static/chunks/pages/properties-9a88220d03e7e02f.js +0 -1
  337. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-70dc1081df37ea69.js +0 -1
  338. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/alpha-3c71b4dbcb6fd6c9.js +0 -1
  339. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about-7cb16e0000dd16c9.js +0 -1
  340. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/[purpose_id]-a18d8f12ee8dcf5d.js +0 -1
  341. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-dc220e116ad5c09e.js +0 -1
  342. fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-55ddc5f25b86f28e.js +0 -1
  343. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domain-records-5ca7decded228bc8.js +0 -1
  344. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-a6fafa1be2834c40.js +0 -1
  345. fides/ui-build/static/admin/_next/static/chunks/pages/settings/email-templates-e633750d2b45ddd6.js +0 -1
  346. fides/ui-build/static/admin/_next/static/chunks/pages/settings/locations-3948686cbd372969.js +0 -1
  347. fides/ui-build/static/admin/_next/static/chunks/pages/settings/organization-04ece499c22d23f4.js +0 -1
  348. fides/ui-build/static/admin/_next/static/chunks/pages/settings/regulations-7752305084280cca.js +0 -1
  349. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/test-datasets-9e5f9066be6f218d.js +0 -1
  350. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]-86de9e6d72098e45.js +0 -1
  351. fides/ui-build/static/admin/_next/static/chunks/pages/systems-cac52e6c3abf6a15.js +0 -1
  352. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-60d5930b6855679b.js +0 -1
  353. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-b0f4c34752d32c73.js +0 -1
  354. fides/ui-build/static/admin/_next/static/chunks/pages/user-management-7dee83dd6d9292aa.js +0 -1
  355. fides/ui-build/static/admin/_next/static/chunks/webpack-8457bad7859c44f0.js +0 -1
  356. fides/ui-build/static/admin/_next/static/css/1fdf5c593349dbc6.css +0 -1
  357. fides/ui-build/static/admin/_next/static/f_8LkSZkhtDM14f8gbC0b/_buildManifest.js +0 -1
  358. {ethyca_fides-2.63.3rc0.dist-info → ethyca_fides-2.64.1b0.dist-info}/WHEEL +0 -0
  359. {ethyca_fides-2.63.3rc0.dist-info → ethyca_fides-2.64.1b0.dist-info}/entry_points.txt +0 -0
  360. {ethyca_fides-2.63.3rc0.dist-info → ethyca_fides-2.64.1b0.dist-info}/licenses/LICENSE +0 -0
  361. {ethyca_fides-2.63.3rc0.dist-info → ethyca_fides-2.64.1b0.dist-info}/top_level.txt +0 -0
  362. /fides/ui-build/static/admin/_next/static/{f_8LkSZkhtDM14f8gbC0b → nRQ3pmK_d3F5PJE39rP2h}/_ssgManifest.js +0 -0
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import IO, Any, Dict, Tuple, Union
3
+ from io import BytesIO
4
+ from typing import IO, Any, Dict, Optional, Tuple, Union
4
5
 
5
6
  from boto3.s3.transfer import TransferConfig
6
7
  from botocore.exceptions import ClientError, ParamValidationError
@@ -34,7 +35,7 @@ def maybe_get_s3_client(
34
35
 
35
36
 
36
37
  def create_presigned_url_for_s3(
37
- s3_client: Any, bucket_name: str, file_key: str
38
+ s3_client: Any, bucket_name: str, file_key: str, ttl_seconds: Optional[int] = None
38
39
  ) -> AnyHttpUrlString:
39
40
  """
40
41
  Generates a presigned URL to share an S3 object
@@ -45,10 +46,16 @@ def create_presigned_url_for_s3(
45
46
  :return: Presigned URL as string.
46
47
  """
47
48
  params = {"Bucket": bucket_name, "Key": file_key}
49
+ if ttl_seconds:
50
+ if ttl_seconds > 604800:
51
+ raise ValueError("TTL must be less than 7 days")
52
+ expires_in = ttl_seconds
53
+ else:
54
+ expires_in = CONFIG.security.subject_request_download_link_ttl_seconds
48
55
  response = s3_client.generate_presigned_url(
49
56
  "get_object",
50
57
  Params=params,
51
- ExpiresIn=CONFIG.security.subject_request_download_link_ttl_seconds,
58
+ ExpiresIn=expires_in,
52
59
  )
53
60
 
54
61
  # The response contains the presigned URL
@@ -68,7 +75,7 @@ def generic_upload_to_s3( # pylint: disable=R0913
68
75
  file_key: str,
69
76
  auth_method: str,
70
77
  document: IO[bytes],
71
- size_threshold: int = LARGE_FILE_THRESHOLD, # 5 MB threshold
78
+ size_threshold: int = LARGE_FILE_THRESHOLD, # 25 MB threshold
72
79
  ) -> Tuple[int, AnyHttpUrlString]:
73
80
  """
74
81
  Uploads file like objects to S3.
@@ -130,7 +137,8 @@ def generic_retrieve_from_s3(
130
137
  file_key: str,
131
138
  auth_method: str,
132
139
  get_content: bool = False,
133
- ) -> Tuple[int, Union[str, bytes]]:
140
+ ttl_seconds: Optional[int] = None,
141
+ ) -> Tuple[int, Union[str, IO[bytes]]]:
134
142
  """
135
143
  Retrieves a file from S3 and returns its size and either a presigned URL or the actual content.
136
144
 
@@ -148,17 +156,23 @@ def generic_retrieve_from_s3(
148
156
  s3_client = get_s3_client(auth_method, storage_secrets)
149
157
 
150
158
  try:
151
- if get_content:
152
- # Get the actual content
153
- response = s3_client.get_object(Bucket=bucket_name, Key=file_key)
154
- content = response["Body"].read()
155
- return response["ContentLength"], content
159
+ # Get file size using head_object
160
+ size_response = s3_client.head_object(Bucket=bucket_name, Key=file_key)
161
+ # If the file is less than 25MB, we can get the content otherwise return the presigned URL
162
+ if get_content and size_response["ContentLength"] <= LARGE_FILE_THRESHOLD:
163
+ # Get the actual content using download_fileobj
164
+ file_obj = BytesIO()
165
+ s3_client.download_fileobj(
166
+ Bucket=bucket_name, Key=file_key, Fileobj=file_obj
167
+ )
168
+ file_obj.seek(0) # Reset file pointer to beginning
169
+ return int(size_response["ContentLength"]), file_obj
156
170
 
157
171
  # Get presigned URL
158
- presigned_url = create_presigned_url_for_s3(s3_client, bucket_name, file_key)
159
- # Get file size
160
- response = s3_client.head_object(Bucket=bucket_name, Key=file_key)
161
- return int(response["ContentLength"]), str(presigned_url)
172
+ presigned_url = create_presigned_url_for_s3(
173
+ s3_client, bucket_name, file_key, ttl_seconds
174
+ )
175
+ return int(size_response["ContentLength"]), str(presigned_url)
162
176
  except ClientError as e:
163
177
  logger.error(f"Error retrieving file from S3: {e}")
164
178
  raise e
@@ -3,6 +3,10 @@ from enum import Enum as EnumType
3
3
 
4
4
  from loguru import logger
5
5
 
6
+ # This is the max file size for downloading the content of an attachment.
7
+ # This is an industry standard used by companies like Google and Microsoft.
8
+ LARGE_FILE_THRESHOLD = 25 * 1024 * 1024 # 25 MB
9
+
6
10
 
7
11
  class AllowedFileType(EnumType):
8
12
  """
@@ -24,15 +28,49 @@ class AllowedFileType(EnumType):
24
28
 
25
29
  LOCAL_FIDES_UPLOAD_DIRECTORY = "fides_uploads"
26
30
 
27
- # Default to 10MB if not specified in environment
28
- LARGE_FILE_THRESHOLD = 10 * 1024 * 1024 # 10 MB threshold
29
-
30
31
 
31
32
  def get_local_filename(file_key: str) -> str:
32
- """Verifies that the local storage directory exists and returns the local filepath"""
33
- if not os.path.exists(LOCAL_FIDES_UPLOAD_DIRECTORY):
34
- os.makedirs(LOCAL_FIDES_UPLOAD_DIRECTORY)
35
- return f"{LOCAL_FIDES_UPLOAD_DIRECTORY}/{file_key}"
33
+ """Verifies that the local storage directory exists and returns the local filepath.
34
+
35
+ This extra security checks are to prevent directory traversal attacks and "complete business and technical destruction".
36
+ Thanks Claude.
37
+
38
+ Args:
39
+ file_key: The key/path for the file
40
+
41
+ Returns:
42
+ The full local filepath
43
+
44
+ Raises:
45
+ ValueError: If the file_key is invalid or would result in a path outside the upload directory
46
+ """
47
+ # Basic validation
48
+ if not file_key:
49
+ raise ValueError("File key cannot be empty")
50
+
51
+ # Security checks before normalization
52
+ if file_key.startswith("/"):
53
+ raise ValueError("Invalid file key: cannot start with '/'")
54
+
55
+ # Normalize the path to handle any path separators consistently
56
+ # First normalize using os.path.normpath to handle any redundant separators
57
+ normalized_key = os.path.normpath(file_key)
58
+ # Then convert all separators to forward slashes for consistency
59
+ normalized_key = normalized_key.replace("\\", "/")
60
+
61
+ # Additional security: ensure the final path is within the upload directory
62
+ final_path = os.path.join(LOCAL_FIDES_UPLOAD_DIRECTORY, normalized_key)
63
+ if not os.path.abspath(final_path).startswith(
64
+ os.path.abspath(LOCAL_FIDES_UPLOAD_DIRECTORY)
65
+ ):
66
+ raise ValueError(
67
+ "Invalid file key: would result in path outside upload directory"
68
+ )
69
+
70
+ # Create all necessary directories
71
+ os.makedirs(os.path.dirname(final_path), exist_ok=True)
72
+
73
+ return final_path
36
74
 
37
75
 
38
76
  def get_allowed_file_type_or_raise(file_key: str) -> str:
@@ -0,0 +1,170 @@
1
+ import zipfile
2
+ from io import BytesIO
3
+ from typing import Any, Optional
4
+
5
+ import pandas as pd
6
+
7
+ from fides.api.tasks.encryption_utils import encrypt_access_request_results
8
+ from fides.config import CONFIG
9
+
10
+
11
+ def create_csv_from_dataframe(df: pd.DataFrame) -> BytesIO:
12
+ """Create a CSV file from a pandas DataFrame.
13
+
14
+ Args:
15
+ df: The DataFrame to convert to CSV
16
+
17
+ Returns:
18
+ BytesIO: A file-like object containing the CSV data
19
+ """
20
+ buffer = BytesIO()
21
+ df.to_csv(buffer, index=False, encoding=CONFIG.security.encoding)
22
+ buffer.seek(0)
23
+ return buffer
24
+
25
+
26
+ def create_attachment_csv(attachments: list[dict[str, Any]]) -> Optional[BytesIO]:
27
+ """Create a CSV file containing attachment metadata.
28
+
29
+ Args:
30
+ attachments: List of attachment dictionaries
31
+ privacy_request_id: The ID of the privacy request for encryption
32
+
33
+ Returns:
34
+ Optional[BytesIO]: A file-like object containing the CSV data, or None if no attachments
35
+ """
36
+ if not attachments:
37
+ return None
38
+
39
+ # Filter out invalid attachments and create a list of valid ones
40
+ valid_attachments = []
41
+ for a in attachments:
42
+ if not isinstance(a, dict):
43
+ continue
44
+
45
+ # Check if the attachment has at least one of the required fields
46
+ if not any(
47
+ key in a
48
+ for key in ["file_name", "file_size", "content_type", "download_url"]
49
+ ):
50
+ continue
51
+
52
+ valid_attachments.append(
53
+ {
54
+ "file_name": a.get("file_name", ""),
55
+ "file_size": a.get("file_size", 0),
56
+ "content_type": a.get("content_type", "application/octet-stream"),
57
+ "download_url": a.get("download_url", ""),
58
+ }
59
+ )
60
+
61
+ # Return None if there are no valid attachments
62
+ if not valid_attachments:
63
+ return None
64
+
65
+ df = pd.DataFrame(valid_attachments)
66
+
67
+ if df.empty:
68
+ return None
69
+
70
+ return create_csv_from_dataframe(df)
71
+
72
+
73
+ def _write_attachment_csv(
74
+ zip_file: zipfile.ZipFile,
75
+ key: str,
76
+ idx: int,
77
+ attachments: list[dict[str, Any]],
78
+ privacy_request_id: str,
79
+ ) -> None:
80
+ """Write attachment data to a CSV file in the zip archive.
81
+
82
+ Args:
83
+ zip_file: The zip file to write to
84
+ key: The key for the data
85
+ idx: The index of the item in the list
86
+ attachments: List of attachment dictionaries
87
+ privacy_request_id: The ID of the privacy request for encryption
88
+ """
89
+ buffer = create_attachment_csv(attachments)
90
+ if buffer:
91
+ zip_file.writestr(
92
+ f"{key}/{idx + 1}/attachments.csv",
93
+ encrypt_access_request_results(buffer.getvalue(), privacy_request_id),
94
+ )
95
+
96
+
97
+ def _write_item_csv(
98
+ zip_file: zipfile.ZipFile,
99
+ key: str,
100
+ items: list[dict[str, Any]],
101
+ privacy_request_id: str,
102
+ ) -> None:
103
+ """Write item data to a CSV file in the zip archive.
104
+
105
+ Args:
106
+ zip_file: The zip file to write to
107
+ key: The key for the data
108
+ items: List of items to write
109
+ privacy_request_id: The ID of the privacy request for encryption
110
+ """
111
+ if items:
112
+ df = pd.DataFrame(items)
113
+ buffer = create_csv_from_dataframe(df)
114
+ zip_file.writestr(
115
+ f"{key}.csv",
116
+ encrypt_access_request_results(buffer.getvalue(), privacy_request_id),
117
+ )
118
+
119
+
120
+ def _write_simple_csv(
121
+ zip_file: zipfile.ZipFile,
122
+ key: str,
123
+ value: Any,
124
+ privacy_request_id: str,
125
+ ) -> None:
126
+ """Write simple key-value data to a CSV file in the zip archive.
127
+
128
+ Args:
129
+ zip_file: The zip file to write to
130
+ key: The key for the data
131
+ value: The value to write
132
+ privacy_request_id: The ID of the privacy request for encryption
133
+ """
134
+ df = pd.json_normalize({key: value})
135
+ buffer = create_csv_from_dataframe(df)
136
+ zip_file.writestr(
137
+ f"{key}.csv",
138
+ encrypt_access_request_results(buffer.getvalue(), privacy_request_id),
139
+ )
140
+
141
+
142
+ def write_csv_to_zip(
143
+ zip_file: zipfile.ZipFile, data: dict[str, Any], privacy_request_id: str
144
+ ) -> None:
145
+ """Write data to a zip file in CSV format.
146
+
147
+ Args:
148
+ zip_file: The zip file to write to
149
+ data: The data to convert to CSV
150
+ privacy_request_id: The ID of the privacy request for encryption
151
+ """
152
+ for key, value in data.items():
153
+ if (
154
+ isinstance(value, list)
155
+ and value
156
+ and all(isinstance(item, dict) for item in value)
157
+ ):
158
+ # Handle lists of dictionaries
159
+ items: list[dict[str, Any]] = []
160
+ for item in value:
161
+ # Extract attachments if they exist
162
+ attachments = item.pop("attachments", [])
163
+ if attachments:
164
+ _write_attachment_csv(
165
+ zip_file, key, len(items), attachments, privacy_request_id
166
+ )
167
+ items.append(item)
168
+ _write_item_csv(zip_file, key, items, privacy_request_id)
169
+ else:
170
+ _write_simple_csv(zip_file, key, value, privacy_request_id)
@@ -0,0 +1,42 @@
1
+ import secrets
2
+ from typing import Optional, Union
3
+
4
+ from fides.api.cryptography.cryptographic_util import bytes_to_b64_str
5
+ from fides.api.util.cache import get_cache, get_encryption_cache_key
6
+ from fides.api.util.encryption.aes_gcm_encryption_scheme import (
7
+ encrypt_to_bytes_verify_secrets_length,
8
+ )
9
+ from fides.config import CONFIG
10
+
11
+
12
+ def encrypt_access_request_results(data: Union[str, bytes], request_id: str) -> str:
13
+ """Encrypt data with encryption key if provided, otherwise return unencrypted data.
14
+
15
+ Args:
16
+ data: The data to encrypt
17
+ request_id: The ID of the privacy request for encryption key lookup
18
+
19
+ Returns:
20
+ str: The encrypted data as a string
21
+ """
22
+ cache = get_cache()
23
+ encryption_cache_key = get_encryption_cache_key(
24
+ privacy_request_id=request_id,
25
+ encryption_attr="key",
26
+ )
27
+ if isinstance(data, bytes):
28
+ data = data.decode(CONFIG.security.encoding)
29
+
30
+ encryption_key: Optional[str] = cache.get(encryption_cache_key)
31
+ if not encryption_key:
32
+ return data
33
+
34
+ bytes_encryption_key: bytes = encryption_key.encode(
35
+ encoding=CONFIG.security.encoding
36
+ )
37
+ nonce: bytes = secrets.token_bytes(CONFIG.security.aes_gcm_nonce_length)
38
+ # b64encode the entire nonce and the encrypted message together
39
+ return bytes_to_b64_str(
40
+ nonce
41
+ + encrypt_to_bytes_verify_secrets_length(data, bytes_encryption_key, nonce)
42
+ )
@@ -1,23 +1,20 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
- import secrets
5
4
  import zipfile
6
5
  from io import BytesIO
7
- from typing import TYPE_CHECKING, Any, Dict, Optional, Union
6
+ from typing import TYPE_CHECKING, Any, Optional
8
7
 
9
- import pandas as pd
10
8
  from botocore.exceptions import ClientError, ParamValidationError
11
9
  from fideslang.validation import AnyHttpUrlString
12
10
  from loguru import logger
13
11
 
14
12
  from fides.api.common_exceptions import StorageUploadError
15
- from fides.api.cryptography.cryptographic_util import bytes_to_b64_str
16
13
  from fides.api.schemas.storage.storage import ResponseFormat, StorageSecrets
17
14
  from fides.api.service.privacy_request.dsr_package.dsr_report_builder import (
18
15
  DsrReportBuilder,
19
16
  )
20
- from fides.api.service.storage.gcs import get_gcs_client
17
+ from fides.api.service.storage.gcs import get_gcs_blob
21
18
  from fides.api.service.storage.s3 import (
22
19
  create_presigned_url_for_s3,
23
20
  generic_upload_to_s3,
@@ -26,11 +23,9 @@ from fides.api.service.storage.util import (
26
23
  LOCAL_FIDES_UPLOAD_DIRECTORY,
27
24
  get_local_filename,
28
25
  )
26
+ from fides.api.tasks.csv_utils import write_csv_to_zip
27
+ from fides.api.tasks.encryption_utils import encrypt_access_request_results
29
28
  from fides.api.util.aws_util import get_s3_client
30
- from fides.api.util.cache import get_cache, get_encryption_cache_key
31
- from fides.api.util.encryption.aes_gcm_encryption_scheme import (
32
- encrypt_to_bytes_verify_secrets_length,
33
- )
34
29
  from fides.api.util.storage_util import StorageJSONEncoder
35
30
  from fides.config import CONFIG
36
31
 
@@ -38,33 +33,8 @@ if TYPE_CHECKING:
38
33
  from fides.api.models.privacy_request import PrivacyRequest
39
34
 
40
35
 
41
- def encrypt_access_request_results(data: Union[str, bytes], request_id: str) -> str:
42
- """Encrypt data with encryption key if provided, otherwise return unencrypted data"""
43
- cache = get_cache()
44
- encryption_cache_key = get_encryption_cache_key(
45
- privacy_request_id=request_id,
46
- encryption_attr="key",
47
- )
48
- if isinstance(data, bytes):
49
- data = data.decode(CONFIG.security.encoding)
50
-
51
- encryption_key: str | None = cache.get(encryption_cache_key)
52
- if not encryption_key:
53
- return data
54
-
55
- bytes_encryption_key: bytes = encryption_key.encode(
56
- encoding=CONFIG.security.encoding
57
- )
58
- nonce: bytes = secrets.token_bytes(CONFIG.security.aes_gcm_nonce_length)
59
- # b64encode the entire nonce and the encrypted message together
60
- return bytes_to_b64_str(
61
- nonce
62
- + encrypt_to_bytes_verify_secrets_length(data, bytes_encryption_key, nonce)
63
- )
64
-
65
-
66
36
  def write_to_in_memory_buffer(
67
- resp_format: str, data: Dict[str, Any], privacy_request: PrivacyRequest
37
+ resp_format: str, data: dict[str, Any], privacy_request: PrivacyRequest
68
38
  ) -> BytesIO:
69
39
  """Write JSON/CSV data to in-memory file-like object to be passed to S3 or GCS. Encrypt data if encryption key/nonce
70
40
  has been cached for the given privacy request id
@@ -73,46 +43,62 @@ def write_to_in_memory_buffer(
73
43
  :param data: Dict
74
44
  :param request_id: str, The privacy request id
75
45
  """
46
+
76
47
  logger.debug("Writing data to in-memory buffer")
48
+ try:
49
+ if resp_format == ResponseFormat.html.value:
50
+ return DsrReportBuilder(
51
+ privacy_request=privacy_request,
52
+ dsr_data=data,
53
+ ).generate()
54
+
55
+ if resp_format == ResponseFormat.json.value:
56
+ return convert_dict_to_encrypted_json(data, privacy_request.id)
57
+
58
+ if resp_format == ResponseFormat.csv.value:
59
+ zipped_csvs = BytesIO()
60
+ with zipfile.ZipFile(zipped_csvs, "w") as f:
61
+ write_csv_to_zip(f, data, privacy_request.id)
62
+ zipped_csvs.seek(0)
63
+ return zipped_csvs
64
+ except Exception as e:
65
+ logger.error(f"Error writing data to in-memory buffer: {str(e)}")
66
+ raise e
67
+
68
+ raise NotImplementedError(f"No handling for response format {resp_format}.")
69
+
70
+
71
+ def convert_dict_to_encrypted_json(
72
+ data: dict[str, Any], privacy_request_id: str
73
+ ) -> BytesIO:
74
+ """Convert data to JSON and encrypt it.
75
+
76
+ Args:
77
+ data: The data to convert and encrypt
78
+ privacy_request_id: The ID of the privacy request for encryption
77
79
 
78
- if resp_format == ResponseFormat.json.value:
80
+ Returns:
81
+ BytesIO: A file-like object containing the encrypted JSON data
82
+
83
+ Raises:
84
+ Exception: If JSON conversion fails
85
+ """
86
+ try:
79
87
  json_str = json.dumps(data, indent=2, default=StorageJSONEncoder().default)
80
88
  return BytesIO(
81
- encrypt_access_request_results(json_str, privacy_request.id).encode(
89
+ encrypt_access_request_results(json_str, privacy_request_id).encode(
82
90
  CONFIG.security.encoding
83
91
  )
84
92
  )
85
-
86
- if resp_format == ResponseFormat.csv.value:
87
- zipped_csvs = BytesIO()
88
- with zipfile.ZipFile(zipped_csvs, "w") as f:
89
- for key in data:
90
- df = pd.json_normalize(data[key])
91
- buffer = BytesIO()
92
- df.to_csv(buffer, index=False, encoding=CONFIG.security.encoding)
93
- buffer.seek(0)
94
- f.writestr(
95
- f"{key}.csv",
96
- encrypt_access_request_results(
97
- buffer.getvalue(), privacy_request.id
98
- ),
99
- )
100
-
101
- zipped_csvs.seek(0)
102
- return zipped_csvs
103
-
104
- if resp_format == ResponseFormat.html.value:
105
- return DsrReportBuilder(
106
- privacy_request=privacy_request,
107
- dsr_data=data,
108
- ).generate()
109
-
110
- raise NotImplementedError(f"No handling for response format {resp_format}.")
93
+ except Exception as e:
94
+ logger.error(f"Error converting data to JSON: {str(e)}")
95
+ logger.error(f"Data that failed to convert: {data}")
96
+ raise
111
97
 
112
98
 
113
99
  def upload_to_s3( # pylint: disable=R0913
114
- storage_secrets: Dict[StorageSecrets, Any],
115
- data: Dict,
100
+ storage_secrets: dict[StorageSecrets, Any],
101
+ data: dict,
116
102
  bucket_name: str,
117
103
  file_key: str,
118
104
  resp_format: str,
@@ -140,18 +126,22 @@ def upload_to_s3( # pylint: disable=R0913
140
126
  "storage", {}
141
127
  ).get("aws_s3_assume_role_arn"),
142
128
  )
129
+ except (ClientError, ParamValidationError) as e:
130
+ logger.error(f"Error getting s3 client: {str(e)}")
131
+ raise StorageUploadError(f"Error getting s3 client: {str(e)}")
143
132
 
144
- # handles file chunking
145
- try:
146
- s3_client.upload_fileobj(
147
- Fileobj=write_to_in_memory_buffer(resp_format, data, privacy_request),
148
- Bucket=bucket_name,
149
- Key=file_key,
150
- )
151
- except Exception as e:
152
- logger.error("Encountered error while uploading s3 object: {}", e)
153
- raise e
133
+ # handles file chunking
134
+ try:
135
+ s3_client.upload_fileobj(
136
+ Fileobj=write_to_in_memory_buffer(resp_format, data, privacy_request),
137
+ Bucket=bucket_name,
138
+ Key=file_key,
139
+ )
140
+ except ClientError as e:
141
+ logger.error("Encountered error while uploading s3 object: {}", e)
142
+ raise StorageUploadError(f"Error uploading to S3: {e}")
154
143
 
144
+ try:
155
145
  presigned_url: AnyHttpUrlString = create_presigned_url_for_s3(
156
146
  s3_client, bucket_name, file_key
157
147
  )
@@ -162,13 +152,11 @@ def upload_to_s3( # pylint: disable=R0913
162
152
  "Encountered error while uploading and generating link for s3 object: {}", e
163
153
  )
164
154
  raise StorageUploadError(f"Error uploading to S3: {e}")
165
- except ParamValidationError as e:
166
- raise StorageUploadError(f"The parameters you provided are incorrect: {e}")
167
155
 
168
156
 
169
157
  def upload_to_gcs(
170
- storage_secrets: Dict,
171
- data: Dict,
158
+ storage_secrets: dict,
159
+ data: dict,
172
160
  bucket_name: str,
173
161
  file_key: str,
174
162
  resp_format: str,
@@ -177,24 +165,30 @@ def upload_to_gcs(
177
165
  ) -> str:
178
166
  """Uploads access request data to a Google Cloud Storage bucket"""
179
167
  logger.info("Starting Google Cloud Storage upload of {}", file_key)
168
+ content_type = {
169
+ ResponseFormat.json.value: "application/json",
170
+ ResponseFormat.csv.value: "application/zip",
171
+ ResponseFormat.html.value: "application/zip",
172
+ }
173
+
174
+ blob = get_gcs_blob(auth_method, storage_secrets, bucket_name, file_key)
175
+ in_memory_file = write_to_in_memory_buffer(resp_format, data, privacy_request)
180
176
 
181
177
  try:
182
- storage_client = get_gcs_client(auth_method, storage_secrets)
183
- bucket = storage_client.bucket(bucket_name)
184
-
185
- blob = bucket.blob(file_key)
186
- in_memory_file = write_to_in_memory_buffer(resp_format, data, privacy_request)
187
- content_type = {
188
- ResponseFormat.json.value: "application/json",
189
- ResponseFormat.csv.value: "application/zip",
190
- ResponseFormat.html.value: "application/zip",
191
- }
192
178
  blob.upload_from_string(
193
179
  in_memory_file.getvalue(), content_type=content_type[resp_format]
194
180
  )
181
+ except Exception as e:
182
+ logger.error("Error uploading to GCS: {}", str(e))
183
+ logger.error(
184
+ "Encountered error while uploading and generating link for Google Cloud Storage object: {}",
185
+ e,
186
+ )
187
+ raise
195
188
 
196
- logger.info("File {} uploaded to {}", file_key, blob.public_url)
189
+ logger.info("File {} uploaded to {}", file_key, blob.public_url)
197
190
 
191
+ try:
198
192
  presigned_url = blob.generate_signed_url(
199
193
  version="v4",
200
194
  expiration=CONFIG.security.subject_request_download_link_ttl_seconds,
@@ -210,7 +204,7 @@ def upload_to_gcs(
210
204
 
211
205
 
212
206
  def upload_to_local(
213
- data: Dict,
207
+ data: dict,
214
208
  file_key: str,
215
209
  privacy_request: PrivacyRequest,
216
210
  resp_format: str = ResponseFormat.json.value,
fides/api/util/cache.py CHANGED
@@ -174,6 +174,7 @@ def _determine_redis_db_index(
174
174
  - If *not* running under xdist, always use DB 1.
175
175
 
176
176
  2. Non-test mode: return the value already present in `CONFIG.redis.db_index`
177
+ (or CONFIG.redis.read_only_db_index` if read_only is True)
177
178
  """
178
179
 
179
180
  # 1. Test mode logic
@@ -261,18 +262,20 @@ def get_read_only_cache() -> FidesopsRedis:
261
262
  ssl_ca_certs=CONFIG.redis.read_only_ssl_ca_certs,
262
263
  ssl_cert_reqs=CONFIG.redis.read_only_ssl_cert_reqs,
263
264
  )
264
- logger.debug("New read-only Redis connection created.")
265
265
 
266
266
  try:
267
+ # Test the connection by attempting to ping the Redis server
267
268
  connected = _read_only_connection.ping()
268
- logger.debug("Read-only Redis connection succeeded.")
269
- except ConnectionErrorFromRedis:
269
+ logger.debug("Read-only Redis connection established successfully.")
270
+ except Exception as e:
271
+ logger.error(f"Failed to connect to read-only Redis: {e}")
270
272
  connected = False
271
273
 
272
274
  if not connected:
273
275
  logger.error(
274
276
  "Unable to establish read-only Redis connection. Returning writeable cache connection instead."
275
277
  )
278
+ # If we can't connect to the read-only cache, fall back to the regular cache
276
279
  return get_cache()
277
280
 
278
281
  return _read_only_connection
@@ -28,6 +28,7 @@ ALL_OBJECT_FIELDS = "all_object_fields"
28
28
  CUSTOM_PRIVACY_REQUEST_FIELDS = "custom_privacy_request_fields"
29
29
  UUID = "uuid"
30
30
  ISO_8601_DATETIME = "iso_8601_datetime"
31
+ FIELD_LIST = "field_list"
31
32
  REPLY_TO = "reply_to"
32
33
  REPLY_TO_TOKEN = "reply_to_token"
33
34