ethyca-fides 2.62.0rc0__py2.py3-none-any.whl → 2.62.1b1__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 (267) hide show
  1. {ethyca_fides-2.62.0rc0.dist-info → ethyca_fides-2.62.1b1.dist-info}/METADATA +2 -1
  2. {ethyca_fides-2.62.0rc0.dist-info → ethyca_fides-2.62.1b1.dist-info}/RECORD +218 -216
  3. {ethyca_fides-2.62.0rc0.dist-info → ethyca_fides-2.62.1b1.dist-info}/WHEEL +1 -1
  4. fides/_version.py +3 -3
  5. fides/api/alembic/migrations/versions/2263583b0e44_add_shared_monitor_config.py +82 -0
  6. fides/api/api/v1/endpoints/privacy_request_endpoints.py +2 -2
  7. fides/api/models/attachment.py +117 -10
  8. fides/api/models/detection_discovery.py +66 -1
  9. fides/api/models/privacy_request/privacy_request.py +40 -1
  10. fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +4 -2
  11. fides/api/service/storage/s3.py +40 -15
  12. fides/api/tasks/storage.py +2 -2
  13. fides/api/util/storage_util.py +14 -8
  14. fides/ui-build/static/admin/404.html +1 -1
  15. fides/ui-build/static/admin/_next/static/Sez6kqW5dI0lK60IMwICO/_buildManifest.js +1 -0
  16. fides/ui-build/static/admin/_next/static/chunks/1327-da0303f70d12fb20.js +1 -0
  17. fides/ui-build/static/admin/_next/static/chunks/1817-d2543e460cec0dcb.js +1 -0
  18. fides/ui-build/static/admin/_next/static/chunks/1904-25e5d4f1b8146fb3.js +1 -0
  19. fides/ui-build/static/admin/_next/static/chunks/{2310-0d63c66c2685e83c.js → 2310-6a5fb2f76f7b6491.js} +1 -1
  20. fides/ui-build/static/admin/_next/static/chunks/3119-b5e40d8d24af1e79.js +1 -0
  21. fides/ui-build/static/admin/_next/static/chunks/3426-77fccf2c9a5e10cd.js +1 -0
  22. fides/ui-build/static/admin/_next/static/chunks/{3513-87b54d931698986f.js → 3513-293b1e54699cf3e7.js} +1 -1
  23. fides/ui-build/static/admin/_next/static/chunks/{3872-6b828cb00fb2ce93.js → 3872-99da5bb6140ff810.js} +1 -1
  24. fides/ui-build/static/admin/_next/static/chunks/3923-c6dcaeaa3267f254.js +1 -0
  25. fides/ui-build/static/admin/_next/static/chunks/401-a882199f6c94a4dc.js +1 -0
  26. fides/ui-build/static/admin/_next/static/chunks/4060-c657e58514e91e91.js +1 -0
  27. fides/ui-build/static/admin/_next/static/chunks/4121-ec1d8faa3cf5783c.js +1 -0
  28. fides/ui-build/static/admin/_next/static/chunks/4481-9357e3930d35073b.js +1 -0
  29. fides/ui-build/static/admin/_next/static/chunks/5258-49e73e5998c01254.js +1 -0
  30. fides/ui-build/static/admin/_next/static/chunks/{5487-62955c11bab63734.js → 5487-218e4847b8250fbb.js} +1 -1
  31. fides/ui-build/static/admin/_next/static/chunks/5683-3d5c4194b1b1493e.js +1 -0
  32. fides/ui-build/static/admin/_next/static/chunks/5973-d776c0029db45971.js +1 -0
  33. fides/ui-build/static/admin/_next/static/chunks/6277-db781b6c1dde3c84.js +1 -0
  34. fides/ui-build/static/admin/_next/static/chunks/6853-348fe2ba132b400c.js +1 -0
  35. fides/ui-build/static/admin/_next/static/chunks/{6954-d02d474ba30756b2.js → 6954-04030a21a0c8cf5a.js} +1 -1
  36. fides/ui-build/static/admin/_next/static/chunks/7553-0f3e9e7eaf1a8b62.js +1 -0
  37. fides/ui-build/static/admin/_next/static/chunks/79-d877d3bde220da44.js +1 -0
  38. fides/ui-build/static/admin/_next/static/chunks/796-7424eb6391142ccd.js +1 -0
  39. fides/ui-build/static/admin/_next/static/chunks/7980-041df432c4d3a68b.js +1 -0
  40. fides/ui-build/static/admin/_next/static/chunks/{9494-0df032fe6ee531a5.js → 9494-d1fd4fb83c3d4836.js} +1 -1
  41. fides/ui-build/static/admin/_next/static/chunks/9767-f8ea7d0127f81294.js +1 -0
  42. fides/ui-build/static/admin/_next/static/chunks/9965-92f4a28823a5e623.js +1 -0
  43. fides/ui-build/static/admin/_next/static/chunks/pages/{_app-371b155655c83a1e.js → _app-94950df9c97cd079.js} +69 -69
  44. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-7d789458fb296270.js → manual-1292fd216f2839c7.js} +1 -1
  45. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-90320455052466cd.js → multiple-2ffc37614e9d1fa9.js} +1 -1
  46. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-0234733a90ed5127.js +1 -0
  47. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-bcc515a765c0119d.js → add-vendors-c8033d6560635046.js} +1 -1
  48. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-2ba21b95aa3bb15e.js +1 -0
  49. fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-5ef03041a530b0e2.js → privacy-experience-fd71fb440dbe60e4.js} +1 -1
  50. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-5a63fc903408412e.js +1 -0
  51. fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-ffa15d7ff64a9f05.js → properties-5960170d492acb03.js} +1 -1
  52. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-80bd51f8217edae4.js +1 -0
  53. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-7ddf45ffd49533b0.js → [resourceUrn]-18e3faf7963962e4.js} +1 -1
  54. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-ecf23116a845188a.js → [projectUrn]-a10a0f3e6592f350.js} +1 -1
  55. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-79f8e613827c55c1.js → projects-6971531bb09dff3a.js} +1 -1
  56. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-ed213bc1b7041aa2.js → [resourceUrn]-7320524a47104798.js} +1 -1
  57. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{resources-1f033ca5a0702018.js → resources-51d99174c8006eb5.js} +1 -1
  58. fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-e950d41b75f86c85.js → data-catalog-4dbc71d8be9765f3.js} +1 -1
  59. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-67ddc090cf949af4.js +1 -0
  60. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-07ff657ee49e5742.js +1 -0
  61. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-dbc8c95c22b9f2aa.js +1 -0
  62. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{activity-beedbc38ee2b0094.js → activity-db3853a09933097a.js} +1 -1
  63. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-f38b3ef885a29ea6.js → [resourceUrn]-4c526db0c30c488a.js} +1 -1
  64. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-29fd0d5a73f13a00.js → detection-6f27dbb7c8edc69d.js} +1 -1
  65. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-9a80241f1028c470.js → [resourceUrn]-562d2b8ae90dd1f0.js} +1 -1
  66. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-c58333981b1522c9.js → discovery-fe7f51502eda57c9.js} +1 -1
  67. fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-2d6f95b2769a0936.js → datamap-3aaa99709d83a1f6.js} +1 -1
  68. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-3536ae978741b659.js → [...subfieldNames]-c08363922838022a.js} +1 -1
  69. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-8b5afe14cf42a4ac.js → [collectionName]-f12df2140e309dfb.js} +1 -1
  70. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-b8dca4298b5bb085.js → [datasetId]-3f564c8d6cfb03d4.js} +1 -1
  71. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-157b00d23b651d12.js +1 -0
  72. fides/ui-build/static/admin/_next/static/chunks/pages/dataset-9d29dd6cf109891d.js +1 -0
  73. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-ebf54ea9874c59ae.js → [id]-b078218865a8fd28.js} +1 -1
  74. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-b01f89d250ff52af.js +1 -0
  75. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-9fc15228581786e6.js +1 -0
  76. fides/ui-build/static/admin/_next/static/chunks/pages/{index-868b60407eae35da.js → index-2e6ba5382358fcf8.js} +1 -1
  77. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-43abc599f0768331.js +1 -0
  78. fides/ui-build/static/admin/_next/static/chunks/pages/integrations-d69c9f1007e76046.js +1 -0
  79. fides/ui-build/static/admin/_next/static/chunks/pages/{messaging-5f41afae411cd8a4.js → messaging-71166bf207804754.js} +1 -1
  80. fides/ui-build/static/admin/_next/static/chunks/pages/poc/table-migration-fec150afcfeb3fb5.js +1 -0
  81. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-2862630f0e9e484b.js +1 -0
  82. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-35e58b5ed6b12b2b.js +1 -0
  83. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-bb02092af39446da.js +1 -0
  84. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-6baa485385ce0678.js +1 -0
  85. fides/ui-build/static/admin/_next/static/chunks/pages/{properties-f00530710b699afd.js → properties-4a5e8ec41574c6b5.js} +1 -1
  86. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-707551df97d5eebd.js +1 -0
  87. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/[purpose_id]-e4df123bef1ff2b1.js +1 -0
  88. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-2fd6b0ff9b6741fc.js +1 -0
  89. fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-3a829c53d5d723d2.js +1 -0
  90. fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-0daa256958675a23.js → domain-records-92e8246170b492b5.js} +1 -1
  91. fides/ui-build/static/admin/_next/static/chunks/pages/settings/locations-4194ce716b81480d.js +1 -0
  92. fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-9404a6b0b2951992.js → organization-b235f961f9ca39a6.js} +1 -1
  93. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-48be14ad09ed4133.js → test-datasets-99cd1a12ae0373b0.js} +1 -1
  94. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-b03403fed4dd8805.js → [id]-01006a55ac873135.js} +1 -1
  95. fides/ui-build/static/admin/_next/static/chunks/pages/{systems-ae591953b24e2fc9.js → systems-45990d85a4c5739b.js} +1 -1
  96. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-ec739e169cd5eafe.js +1 -0
  97. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-603c2bdbd8dc00ca.js +1 -0
  98. fides/ui-build/static/admin/_next/static/chunks/pages/user-management-592e497369673e1a.js +1 -0
  99. fides/ui-build/static/admin/_next/static/css/{c0c2eb63ad3e7390.css → 1b227ba7eabbfe2f.css} +1 -1
  100. fides/ui-build/static/admin/_next/static/css/5ded47c57dae5baf.css +1 -0
  101. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  102. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  103. fides/ui-build/static/admin/add-systems.html +1 -1
  104. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  105. fides/ui-build/static/admin/consent/configure.html +1 -1
  106. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  107. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  108. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  109. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  110. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  111. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  112. fides/ui-build/static/admin/consent/properties.html +1 -1
  113. fides/ui-build/static/admin/consent/reporting.html +1 -1
  114. fides/ui-build/static/admin/consent.html +1 -1
  115. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  116. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  117. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  118. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  119. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  120. fides/ui-build/static/admin/data-catalog.html +1 -1
  121. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  122. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  123. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  124. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  125. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  126. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  127. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  128. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  129. fides/ui-build/static/admin/datamap.html +1 -1
  130. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  131. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  132. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  133. fides/ui-build/static/admin/dataset/new.html +1 -1
  134. fides/ui-build/static/admin/dataset.html +1 -1
  135. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  136. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  137. fides/ui-build/static/admin/datastore-connection.html +1 -1
  138. fides/ui-build/static/admin/index.html +1 -1
  139. fides/ui-build/static/admin/integrations/[id].html +1 -1
  140. fides/ui-build/static/admin/integrations.html +1 -1
  141. fides/ui-build/static/admin/lib/fides-ext-gpp.js +1 -1
  142. fides/ui-build/static/admin/lib/fides-headless.js +1 -1
  143. fides/ui-build/static/admin/lib/fides-preview.js +1 -1
  144. fides/ui-build/static/admin/lib/fides-tcf.js +2 -2
  145. fides/ui-build/static/admin/lib/fides.js +2 -2
  146. fides/ui-build/static/admin/login/[provider].html +1 -1
  147. fides/ui-build/static/admin/login.html +1 -1
  148. fides/ui-build/static/admin/messaging/[id].html +1 -1
  149. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  150. fides/ui-build/static/admin/messaging.html +1 -1
  151. fides/ui-build/static/admin/poc/ant-components.html +1 -1
  152. fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
  153. fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
  154. fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
  155. fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
  156. fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
  157. fides/ui-build/static/admin/poc/forms.html +1 -1
  158. fides/ui-build/static/admin/poc/table-migration.html +1 -0
  159. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  160. fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
  161. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  162. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  163. fides/ui-build/static/admin/privacy-requests.html +1 -1
  164. fides/ui-build/static/admin/properties/[id].html +1 -1
  165. fides/ui-build/static/admin/properties/add-property.html +1 -1
  166. fides/ui-build/static/admin/properties.html +1 -1
  167. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  168. fides/ui-build/static/admin/settings/about.html +1 -1
  169. fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
  170. fides/ui-build/static/admin/settings/consent.html +1 -1
  171. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  172. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  173. fides/ui-build/static/admin/settings/domains.html +1 -1
  174. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  175. fides/ui-build/static/admin/settings/locations.html +1 -1
  176. fides/ui-build/static/admin/settings/organization.html +1 -1
  177. fides/ui-build/static/admin/settings/regulations.html +1 -1
  178. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  179. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  180. fides/ui-build/static/admin/systems.html +1 -1
  181. fides/ui-build/static/admin/taxonomy.html +1 -1
  182. fides/ui-build/static/admin/user-management/new.html +1 -1
  183. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  184. fides/ui-build/static/admin/user-management.html +1 -1
  185. fides/service/error_handling/__init__.py +0 -0
  186. fides/service/error_handling/error_handler.py +0 -202
  187. fides/ui-build/static/admin/_next/static/4OMQ0x81J6D4ksHggHXOF/_buildManifest.js +0 -1
  188. fides/ui-build/static/admin/_next/static/chunks/1327-92a38135ee8d55ec.js +0 -1
  189. fides/ui-build/static/admin/_next/static/chunks/1817-721fdeb29c07c6be.js +0 -1
  190. fides/ui-build/static/admin/_next/static/chunks/1904-7e5354990377a849.js +0 -1
  191. fides/ui-build/static/admin/_next/static/chunks/3119-e441b14cdab4320e.js +0 -1
  192. fides/ui-build/static/admin/_next/static/chunks/3426-e49cb4766324b2dd.js +0 -1
  193. fides/ui-build/static/admin/_next/static/chunks/355-e4340980d72b4faa.js +0 -1
  194. fides/ui-build/static/admin/_next/static/chunks/4060-9593a3f98e600487.js +0 -1
  195. fides/ui-build/static/admin/_next/static/chunks/4121-5c3af879cdd8b3be.js +0 -1
  196. fides/ui-build/static/admin/_next/static/chunks/4481-8398edf74aabb8ea.js +0 -1
  197. fides/ui-build/static/admin/_next/static/chunks/4723-4c3c7a375e998c15.js +0 -1
  198. fides/ui-build/static/admin/_next/static/chunks/5258-e000d07844eccf7e.js +0 -1
  199. fides/ui-build/static/admin/_next/static/chunks/5626-1b636798faad78e1.js +0 -1
  200. fides/ui-build/static/admin/_next/static/chunks/5683-44966a05fa5ad566.js +0 -1
  201. fides/ui-build/static/admin/_next/static/chunks/5826-ce34f25ac2eeb807.js +0 -1
  202. fides/ui-build/static/admin/_next/static/chunks/5973-56d90405c3616068.js +0 -1
  203. fides/ui-build/static/admin/_next/static/chunks/6277-c35c8cc806f7571f.js +0 -1
  204. fides/ui-build/static/admin/_next/static/chunks/6853-0cee00c9da26a40e.js +0 -1
  205. fides/ui-build/static/admin/_next/static/chunks/79-e5884e6878fc1bbb.js +0 -1
  206. fides/ui-build/static/admin/_next/static/chunks/7980-e17dda2f50ec76b8.js +0 -1
  207. fides/ui-build/static/admin/_next/static/chunks/9767-74320abc2e9a4550.js +0 -1
  208. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-a69f8946466471aa.js +0 -1
  209. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-49b9f1b56262dfdc.js +0 -1
  210. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-12f3ba4609bc2ba5.js +0 -1
  211. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-b959d61c2a3682b3.js +0 -1
  212. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-84ead584a88ab662.js +0 -1
  213. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-90f1975f559fff3e.js +0 -1
  214. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-4b820a3173f35504.js +0 -1
  215. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-1e26702b1a7e0e12.js +0 -1
  216. fides/ui-build/static/admin/_next/static/chunks/pages/dataset-1f0c4e2e5d0b5bf6.js +0 -1
  217. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-c39e8e2269883796.js +0 -1
  218. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-9db81467fa254996.js +0 -1
  219. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-ebc41649161f7fb9.js +0 -1
  220. fides/ui-build/static/admin/_next/static/chunks/pages/integrations-b89b9f68ccc69c1b.js +0 -1
  221. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-928430cd40c35ef2.js +0 -1
  222. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-07ef58b04404479d.js +0 -1
  223. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-595ed9f6ca438020.js +0 -1
  224. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-b3384299166bcd1e.js +0 -1
  225. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-6a6016708efb5f4f.js +0 -1
  226. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/[purpose_id]-ba15c12c89ee10f8.js +0 -1
  227. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-0f2daeec241bd1a2.js +0 -1
  228. fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-0401208a3128fa4d.js +0 -1
  229. fides/ui-build/static/admin/_next/static/chunks/pages/settings/locations-4fbab6716326060d.js +0 -1
  230. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-913efe5371344de6.js +0 -1
  231. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-dc11ba29dbd4708b.js +0 -1
  232. fides/ui-build/static/admin/_next/static/chunks/pages/user-management-c0b2113b44f46112.js +0 -1
  233. fides/ui-build/static/admin/_next/static/css/b89fc4b36b501cf6.css +0 -1
  234. {ethyca_fides-2.62.0rc0.dist-info → ethyca_fides-2.62.1b1.dist-info}/entry_points.txt +0 -0
  235. {ethyca_fides-2.62.0rc0.dist-info → ethyca_fides-2.62.1b1.dist-info}/licenses/LICENSE +0 -0
  236. {ethyca_fides-2.62.0rc0.dist-info → ethyca_fides-2.62.1b1.dist-info}/top_level.txt +0 -0
  237. /fides/ui-build/static/admin/_next/static/{4OMQ0x81J6D4ksHggHXOF → Sez6kqW5dI0lK60IMwICO}/_ssgManifest.js +0 -0
  238. /fides/ui-build/static/admin/_next/static/chunks/{1099-98458e8e9ff67508.js → 1099-b652e6f9a0cd778b.js} +0 -0
  239. /fides/ui-build/static/admin/_next/static/chunks/{1100-4106d99f5e1ebd75.js → 1100-e19e94b0a6486255.js} +0 -0
  240. /fides/ui-build/static/admin/_next/static/chunks/{2921-ca04a0476e2e56c5.js → 2921-771dc5d9e95dc6b4.js} +0 -0
  241. /fides/ui-build/static/admin/_next/static/chunks/{3505-e58b93f9c1cbdcad.js → 3505-313087fc1744a0ca.js} +0 -0
  242. /fides/ui-build/static/admin/_next/static/chunks/{69-dc52e39d26a4ad14.js → 69-ef4c11c574b4e6dd.js} +0 -0
  243. /fides/ui-build/static/admin/_next/static/chunks/{8433-a77c2cd1c1a6887e.js → 8433-755359b501d32eaa.js} +0 -0
  244. /fides/ui-build/static/admin/_next/static/chunks/{8499-34a34015c91fc38b.js → 8499-06ab15acbfec037c.js} +0 -0
  245. /fides/ui-build/static/admin/_next/static/chunks/{9327-a25347d72bfbe680.js → 9327-b691be8352e31b58.js} +0 -0
  246. /fides/ui-build/static/admin/_next/static/chunks/pages/{404-c00773c4c6e930af.js → 404-e868487119cd14fc.js} +0 -0
  247. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-d4137bb7fdc1ac6e.js → [id]-94391554a7607c5b.js} +0 -0
  248. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-a5a95ac63b1ce206.js → [id]-596dd2a35b455f21.js} +0 -0
  249. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-946ce1a2aa1500d4.js → new-fa7f0d2639e12a5a.js} +0 -0
  250. /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-7797e367dd946a18.js → consent-dd2a0e3b536b8b42.js} +0 -0
  251. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-22812f08e81bd4f3.js → [id]-111b07344f1ee9e4.js} +0 -0
  252. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-ca22869201847ccc.js → add-template-c41f368fb6b35de4.js} +0 -0
  253. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{ant-components-173bd3ad45ea61c6.js → ant-components-3dabaf6828acb2cb.js} +0 -0
  254. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{AntForm-4dfe984ad80927cd.js → AntForm-784081c774042249.js} +0 -0
  255. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikAntFormItem-f790e1ea5cbc26c0.js → FormikAntFormItem-63905f2382f158cd.js} +0 -0
  256. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikControlled-af9e682ec620e990.js → FormikControlled-43f6eab28eae7b56.js} +0 -0
  257. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikField-6ce407101daa6000.js → FormikField-e4a07abc908fc9b5.js} +0 -0
  258. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{forms-9ee8cf14a93eaa4a.js → forms-71a3bfb94280fc49.js} +0 -0
  259. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-25a3add8e7982e43.js → configure-42213855a0a8c209.js} +0 -0
  260. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-081f13526443e0a6.js → [id]-d547a1da0b74f00f.js} +0 -0
  261. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-17fb474e458b63e7.js → add-property-a7de6f8409a76358.js} +0 -0
  262. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-177919b4ccf8f660.js → about-704fc28e61b17247.js} +0 -0
  263. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-26810d1ffedcd156.js → domains-f0e9ee8756a65540.js} +0 -0
  264. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-eb78b645d0c1d4ea.js → email-templates-a4771098466af1ed.js} +0 -0
  265. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-0a3aa951ef4a44ba.js → regulations-8cff97fe57f42ed3.js} +0 -0
  266. /fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-69176abe09cf9b52.js → new-b124cc24b930c9e1.js} +0 -0
  267. /fides/ui-build/static/admin/_next/static/chunks/{webpack-abd3efcb23c0bb03.js → webpack-3a61b934ba2fb620.js} +0 -0
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
fides/_version.py CHANGED
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-05-21T15:50:31-0400",
11
+ "date": "2025-06-02T09:59:07-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "77ac10c983eb727ff13db6b3f220d9de7e98137f",
15
- "version": "2.62.0rc0"
14
+ "full-revisionid": "d7cf6cc495ac932a4b042e8249a4a49c8ae2070c",
15
+ "version": "2.62.1b1"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -0,0 +1,82 @@
1
+ """add shared monitor config
2
+
3
+ Revision ID: 2263583b0e44
4
+ Revises: d0cbfec0b2dd
5
+ Create Date: 2025-05-12 23:44:11.809581
6
+
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ from alembic import op
11
+ from sqlalchemy.dialects import postgresql
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "2263583b0e44"
15
+ down_revision = "d0cbfec0b2dd"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade():
21
+ op.create_table(
22
+ "shared_monitor_config",
23
+ sa.Column("id", sa.String(length=255), nullable=False),
24
+ sa.Column(
25
+ "created_at",
26
+ sa.DateTime(timezone=True),
27
+ server_default=sa.text("now()"),
28
+ nullable=True,
29
+ ),
30
+ sa.Column(
31
+ "updated_at",
32
+ sa.DateTime(timezone=True),
33
+ server_default=sa.text("now()"),
34
+ nullable=True,
35
+ ),
36
+ sa.Column("name", sa.String(), nullable=False),
37
+ sa.Column("key", sa.String(), nullable=False),
38
+ sa.Column("description", sa.String(), nullable=True),
39
+ sa.Column(
40
+ "classify_params",
41
+ postgresql.JSONB(astext_type=sa.Text()),
42
+ server_default="{}",
43
+ nullable=False,
44
+ ),
45
+ sa.PrimaryKeyConstraint("id"),
46
+ sa.UniqueConstraint("key"),
47
+ )
48
+ op.create_index(
49
+ op.f("ix_shared_monitor_config_id"),
50
+ "shared_monitor_config",
51
+ ["id"],
52
+ unique=False,
53
+ )
54
+ op.add_column(
55
+ "monitorconfig", sa.Column("shared_config_id", sa.String(), nullable=True)
56
+ )
57
+ op.create_index(
58
+ op.f("ix_monitorconfig_shared_config_id"),
59
+ "monitorconfig",
60
+ ["shared_config_id"],
61
+ unique=False,
62
+ )
63
+ op.create_foreign_key(
64
+ "fk_monitorconfig_shared_config_id",
65
+ "monitorconfig",
66
+ "shared_monitor_config",
67
+ ["shared_config_id"],
68
+ ["id"],
69
+ ondelete="RESTRICT",
70
+ )
71
+
72
+
73
+ def downgrade():
74
+ op.drop_constraint(
75
+ "fk_monitorconfig_shared_config_id", "monitorconfig", type_="foreignkey"
76
+ )
77
+ op.drop_index(op.f("ix_monitorconfig_shared_config_id"), table_name="monitorconfig")
78
+ op.drop_column("monitorconfig", "shared_config_id")
79
+ op.drop_index(
80
+ op.f("ix_shared_monitor_config_id"), table_name="shared_monitor_config"
81
+ )
82
+ op.drop_table("shared_monitor_config")
@@ -120,7 +120,7 @@ from fides.api.util.collection_util import Row
120
120
  from fides.api.util.endpoint_utils import validate_start_and_end_filters
121
121
  from fides.api.util.enums import ColumnSort
122
122
  from fides.api.util.fuzzy_search_utils import get_decrypted_identities_automaton
123
- from fides.api.util.storage_util import storage_json_encoder
123
+ from fides.api.util.storage_util import StorageJSONEncoder
124
124
  from fides.common.api.scope_registry import (
125
125
  PRIVACY_REQUEST_CALLBACK_RESUME,
126
126
  PRIVACY_REQUEST_CREATE,
@@ -2129,7 +2129,7 @@ def get_test_privacy_request_results(
2129
2129
 
2130
2130
  # Escape datetime and ObjectId values
2131
2131
  raw_data = privacy_request.get_raw_access_results()
2132
- escaped_json = json.dumps(raw_data, indent=2, default=storage_json_encoder)
2132
+ escaped_json = json.dumps(raw_data, indent=2, cls=StorageJSONEncoder)
2133
2133
  results = json.loads(escaped_json)
2134
2134
 
2135
2135
  filtered_results: Dict[str, Any] = filter_access_results(
@@ -12,12 +12,14 @@ from sqlalchemy.orm import Session, relationship
12
12
 
13
13
  from fides.api.db.base_class import Base
14
14
  from fides.api.schemas.storage.storage import StorageDetails, StorageType
15
+ from fides.api.service.storage.gcs import get_gcs_client
15
16
  from fides.api.service.storage.s3 import (
16
17
  generic_delete_from_s3,
17
18
  generic_retrieve_from_s3,
18
19
  generic_upload_to_s3,
19
20
  )
20
- from fides.api.service.storage.util import get_local_filename
21
+ from fides.api.service.storage.util import AllowedFileType, get_local_filename
22
+ from fides.config import CONFIG
21
23
 
22
24
  if TYPE_CHECKING:
23
25
  from fides.api.models.comment import Comment
@@ -115,8 +117,13 @@ class Attachment(Base):
115
117
  uselist=False,
116
118
  )
117
119
 
120
+ @property
121
+ def content_type(self) -> str:
122
+ """Returns the content type of the attachment."""
123
+ return AllowedFileType[self.file_name.split(".")[-1]].value
124
+
118
125
  def upload(self, attachment: IO[bytes]) -> None:
119
- """Uploads an attachment to S3 or local storage."""
126
+ """Uploads an attachment to S3, GCS, or local storage."""
120
127
  if self.config.type == StorageType.s3:
121
128
  bucket_name = f"{self.config.details[StorageDetails.BUCKET.value]}"
122
129
  auth_method = self.config.details[StorageDetails.AUTH_METHOD.value]
@@ -130,6 +137,23 @@ class Attachment(Base):
130
137
  log.info(f"Uploaded {self.file_name} to S3 bucket {bucket_name}/{self.id}")
131
138
  return
132
139
 
140
+ if self.config.type == StorageType.gcs:
141
+ bucket_name = f"{self.config.details[StorageDetails.BUCKET.value]}"
142
+ auth_method = self.config.details[StorageDetails.AUTH_METHOD.value]
143
+ storage_client = get_gcs_client(auth_method, self.config.secrets)
144
+ bucket = storage_client.bucket(bucket_name)
145
+ blob = bucket.blob(f"{self.id}/{self.file_name}")
146
+
147
+ # Reset the file pointer to the beginning
148
+ try:
149
+ attachment.seek(0)
150
+ except Exception as e:
151
+ raise ValueError(f"Failed to reset file pointer for attachment: {e}")
152
+
153
+ blob.upload_from_file(attachment)
154
+ log.info(f"Uploaded {self.file_name} to GCS bucket {bucket_name}/{self.id}")
155
+ return
156
+
133
157
  if self.config.type == StorageType.local:
134
158
  filename = get_local_filename(f"{self.id}/{self.file_name}")
135
159
 
@@ -164,13 +188,16 @@ class Attachment(Base):
164
188
  self,
165
189
  ) -> Tuple[int, AnyHttpUrlString]:
166
190
  """
167
- Retrieves a the size of the attachment and the presigned URL to retrieve it.
191
+ Retrieves the size of the attachment and a presigned/signed URL.
168
192
  - For s3:
169
193
  - the size is retrieved from the s3 object metadata
170
- - the presigned URL is retrieved from the s3 client
194
+ - returns presigned URL
195
+ - For gcs:
196
+ - the size is retrieved from the blob metadata
197
+ - returns signed URL
171
198
  - For local:
172
199
  - the size is retrieved from the file size
173
- - the URL is the local file path
200
+ - returns the local file path
174
201
  """
175
202
  if self.config.type == StorageType.s3:
176
203
  bucket_name = f"{self.config.details[StorageDetails.BUCKET.value]}"
@@ -180,19 +207,81 @@ class Attachment(Base):
180
207
  bucket_name=bucket_name,
181
208
  file_key=f"{self.id}/{self.file_name}",
182
209
  auth_method=auth_method,
210
+ get_content=False,
183
211
  )
184
212
  return size, url
185
213
 
214
+ if self.config.type == StorageType.gcs:
215
+ bucket_name = f"{self.config.details[StorageDetails.BUCKET.value]}"
216
+ auth_method = self.config.details[StorageDetails.AUTH_METHOD.value]
217
+ storage_client = get_gcs_client(auth_method, self.config.secrets)
218
+ bucket = storage_client.bucket(bucket_name)
219
+ blob = bucket.blob(f"{self.id}/{self.file_name}")
220
+
221
+ # Ensure we have the blob metadata
222
+ blob.reload()
223
+ url = blob.generate_signed_url(
224
+ version="v4",
225
+ expiration=CONFIG.security.subject_request_download_link_ttl_seconds,
226
+ method="GET",
227
+ )
228
+ return blob.size, url
229
+
230
+ if self.config.type == StorageType.local:
231
+ filename = get_local_filename(f"{self.id}/{self.file_name}")
232
+ size = os.path.getsize(filename)
233
+ return size, filename
234
+
235
+ raise ValueError(f"Unsupported storage type: {self.config.type}")
236
+
237
+ def retrieve_attachment_content(
238
+ self,
239
+ ) -> Tuple[int, bytes]:
240
+ """
241
+ Retrieves the size of the attachment and its actual content.
242
+ - For s3:
243
+ - the size is retrieved from the s3 object metadata
244
+ - returns the actual content
245
+ - For gcs:
246
+ - the size is retrieved from the blob metadata
247
+ - returns the actual content
248
+ - For local:
249
+ - the size is retrieved from the file size
250
+ - returns the actual content
251
+ """
252
+ if self.config.type == StorageType.s3:
253
+ bucket_name = f"{self.config.details[StorageDetails.BUCKET.value]}"
254
+ auth_method = self.config.details[StorageDetails.AUTH_METHOD.value]
255
+ size, content = generic_retrieve_from_s3(
256
+ storage_secrets=self.config.secrets,
257
+ bucket_name=bucket_name,
258
+ file_key=f"{self.id}/{self.file_name}",
259
+ auth_method=auth_method,
260
+ get_content=True,
261
+ )
262
+ return size, content
263
+
264
+ if self.config.type == StorageType.gcs:
265
+ bucket_name = f"{self.config.details[StorageDetails.BUCKET.value]}"
266
+ auth_method = self.config.details[StorageDetails.AUTH_METHOD.value]
267
+ storage_client = get_gcs_client(auth_method, self.config.secrets)
268
+ bucket = storage_client.bucket(bucket_name)
269
+ blob = bucket.blob(f"{self.id}/{self.file_name}")
270
+
271
+ content = blob.download_as_bytes()
272
+ return len(content), content
273
+
186
274
  if self.config.type == StorageType.local:
187
275
  filename = get_local_filename(f"{self.id}/{self.file_name}")
188
276
  with open(filename, "rb") as file:
189
- size = len(file.read())
190
- return size, filename
277
+ content = file.read()
278
+ size = len(content)
279
+ return size, content
191
280
 
192
281
  raise ValueError(f"Unsupported storage type: {self.config.type}")
193
282
 
194
283
  def delete_attachment_from_storage(self) -> None:
195
- """Deletes an attachment from S3 or local storage."""
284
+ """Deletes an attachment from S3, GCS, or local storage."""
196
285
  if self.config.type == StorageType.s3:
197
286
  bucket_name = f"{self.config.details[StorageDetails.BUCKET.value]}"
198
287
  auth_method = self.config.details[StorageDetails.AUTH_METHOD.value]
@@ -204,9 +293,27 @@ class Attachment(Base):
204
293
  )
205
294
  return
206
295
 
296
+ if self.config.type == StorageType.gcs:
297
+ bucket_name = f"{self.config.details[StorageDetails.BUCKET.value]}"
298
+ auth_method = self.config.details[StorageDetails.AUTH_METHOD.value]
299
+ storage_client = get_gcs_client(auth_method, self.config.secrets)
300
+ bucket = storage_client.bucket(bucket_name)
301
+
302
+ # List and delete all blobs in the folder
303
+ prefix = f"{self.id}/"
304
+ blobs = bucket.list_blobs(prefix=prefix)
305
+ for blob in blobs:
306
+ blob.delete()
307
+ return
308
+
207
309
  if self.config.type == StorageType.local:
208
- filename = get_local_filename(f"{self.id}/{self.file_name}")
209
- os.remove(filename)
310
+ folder_path = os.path.dirname(
311
+ get_local_filename(f"{self.id}/{self.file_name}")
312
+ )
313
+ if os.path.exists(folder_path):
314
+ import shutil
315
+
316
+ shutil.rmtree(folder_path)
210
317
  return
211
318
 
212
319
  raise ValueError(f"Unsupported storage type: {self.config.type}")
@@ -9,6 +9,7 @@ from loguru import logger
9
9
  from sqlalchemy import ARRAY, Boolean, Column, DateTime, ForeignKey, String, func
10
10
  from sqlalchemy.dialects.postgresql import JSONB
11
11
  from sqlalchemy.ext.asyncio import AsyncSession
12
+ from sqlalchemy.ext.declarative import declared_attr
12
13
  from sqlalchemy.ext.mutable import MutableDict
13
14
  from sqlalchemy.future import select
14
15
  from sqlalchemy.orm import Session, relationship
@@ -47,6 +48,29 @@ class MonitorFrequency(Enum):
47
48
  QUARTERLY_MONTH_PATTERN = r"^\d+,\d+,\d+,\d+$"
48
49
 
49
50
 
51
+ class SharedMonitorConfig(Base, FidesBase):
52
+ """SQL model for shareable monitor configurations"""
53
+
54
+ @declared_attr
55
+ def __tablename__(self) -> str:
56
+ return "shared_monitor_config"
57
+
58
+ # Basic info
59
+ name = Column(String, nullable=False)
60
+ key = Column(String, unique=True, nullable=False)
61
+ description = Column(String, nullable=True)
62
+
63
+ # Classification parameters (including regex patterns)
64
+ classify_params = Column(
65
+ MutableDict.as_mutable(JSONB),
66
+ index=False,
67
+ unique=False,
68
+ nullable=False,
69
+ server_default="{}",
70
+ default=dict,
71
+ )
72
+
73
+
50
74
  class MonitorConfig(Base):
51
75
  """
52
76
  Monitor configuration used for data detection and discovery.
@@ -87,7 +111,9 @@ class MonitorConfig(Base):
87
111
  ) # stores the cron-based kwargs for scheduling the monitor execution.
88
112
  # see https://apscheduler.readthedocs.io/en/3.x/modules/triggers/cron.html
89
113
 
90
- classify_params = Column(
114
+ # We use _classify_params for the actual column to prevent direct access
115
+ _classify_params = Column(
116
+ "classify_params",
91
117
  MutableDict.as_mutable(JSONB),
92
118
  index=False,
93
119
  unique=False,
@@ -124,6 +150,45 @@ class MonitorConfig(Base):
124
150
  backref="monitor_config",
125
151
  )
126
152
 
153
+ shared_config_id = Column(
154
+ String,
155
+ ForeignKey(SharedMonitorConfig.id_field_path, ondelete="RESTRICT"),
156
+ nullable=True,
157
+ index=True,
158
+ )
159
+
160
+ shared_config = relationship(SharedMonitorConfig)
161
+
162
+ @property
163
+ def classify_params(self) -> dict:
164
+ """
165
+ Returns the merged classify parameters from both the monitor config and
166
+ the shared config (if it exists).
167
+
168
+ The shared config parameters take precedence over the monitor's own parameters,
169
+ but only for values that are not falsy (None, empty, etc.).
170
+ """
171
+ # Start with an empty dict
172
+ merged_params = {}
173
+
174
+ # Add this monitor's params if available
175
+ if self._classify_params:
176
+ merged_params.update(self._classify_params)
177
+
178
+ # Add/override with shared config params if available
179
+ if self.shared_config and self.shared_config.classify_params:
180
+ # Only update with non-falsy values from shared config
181
+ for key, value in self.shared_config.classify_params.items():
182
+ if value: # Only override if the value is not falsy
183
+ merged_params[key] = value
184
+
185
+ return merged_params
186
+
187
+ @classify_params.setter
188
+ def classify_params(self, value: Dict[str, Any]) -> None:
189
+ """Setter for the classify_params to maintain compatibility with existing code"""
190
+ self._classify_params = value
191
+
127
192
  @property
128
193
  def connection_config_key(self) -> str:
129
194
  """Derives the `connection_config_key`"""
@@ -1089,6 +1089,46 @@ class PrivacyRequest(
1089
1089
  if attachment:
1090
1090
  attachment.delete(db)
1091
1091
 
1092
+ def _get_manual_webhook_attachments(
1093
+ self, db: Session, manual_webhook_id: str, reference_type: str
1094
+ ) -> List[Attachment]:
1095
+ """Helper method to get attachments that have references to both this privacy request and the specified manual webhook"""
1096
+ query = """
1097
+ SELECT DISTINCT a.*
1098
+ FROM attachment a
1099
+ INNER JOIN attachment_reference ar1 ON a.id = ar1.attachment_id
1100
+ INNER JOIN attachment_reference ar2 ON a.id = ar2.attachment_id
1101
+ WHERE ar1.reference_id = :privacy_request_id
1102
+ AND ar1.reference_type = 'privacy_request'
1103
+ AND ar2.reference_id = :manual_webhook_id
1104
+ AND ar2.reference_type = :reference_type
1105
+ """
1106
+ result = db.execute(
1107
+ query,
1108
+ {
1109
+ "privacy_request_id": self.id,
1110
+ "manual_webhook_id": manual_webhook_id,
1111
+ "reference_type": reference_type,
1112
+ },
1113
+ )
1114
+ return [Attachment(**row) for row in result]
1115
+
1116
+ def get_access_manual_webhook_attachments(
1117
+ self, db: Session, manual_webhook_id: str
1118
+ ) -> List[Attachment]:
1119
+ """Get all attachments that have references to both this privacy request and the specified access manual webhook"""
1120
+ return self._get_manual_webhook_attachments(
1121
+ db, manual_webhook_id, "access_manual_webhook"
1122
+ )
1123
+
1124
+ def get_erasure_manual_webhook_attachments(
1125
+ self, db: Session, manual_webhook_id: str
1126
+ ) -> List[Attachment]:
1127
+ """Get all attachments that have references to both this privacy request and the specified erasure manual webhook"""
1128
+ return self._get_manual_webhook_attachments(
1129
+ db, manual_webhook_id, "erasure_manual_webhook"
1130
+ )
1131
+
1092
1132
  def get_existing_request_task(
1093
1133
  self,
1094
1134
  db: Session,
@@ -1228,7 +1268,6 @@ class PrivacyRequest(
1228
1268
  """
1229
1269
  if not self.policy.get_rules_for_action(action_type=ActionType.access):
1230
1270
  return None
1231
-
1232
1271
  self.filtered_final_upload = results
1233
1272
  self.save(db)
1234
1273
 
@@ -11,7 +11,7 @@ from jinja2 import Environment, FileSystemLoader
11
11
 
12
12
  from fides.api.models.privacy_request import PrivacyRequest
13
13
  from fides.api.schemas.policy import ActionType
14
- from fides.api.util.storage_util import storage_json_encoder
14
+ from fides.api.util.storage_util import StorageJSONEncoder
15
15
 
16
16
  DSR_DIRECTORY = Path(__file__).parent.resolve()
17
17
 
@@ -41,7 +41,9 @@ class DsrReportBuilder:
41
41
 
42
42
  # Jinja template environment initialization
43
43
  def pretty_print(value: str, indent: int = 4) -> str:
44
- return json.dumps(value, indent=indent, default=storage_json_encoder)
44
+ return json.dumps(
45
+ value, indent=indent, default=StorageJSONEncoder().default
46
+ )
45
47
 
46
48
  jinja2.filters.FILTERS["pretty_print"] = pretty_print
47
49
  self.template_loader = Environment(
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import IO, Any, Dict, Tuple
3
+ from typing import IO, Any, Dict, Tuple, Union
4
4
 
5
5
  from boto3.s3.transfer import TransferConfig
6
6
  from botocore.exceptions import ClientError, ParamValidationError
@@ -129,25 +129,38 @@ def generic_retrieve_from_s3(
129
129
  bucket_name: str,
130
130
  file_key: str,
131
131
  auth_method: str,
132
- ) -> Tuple[int, AnyHttpUrlString]:
132
+ get_content: bool = False,
133
+ ) -> Tuple[int, Union[str, bytes]]:
133
134
  """
134
- Retrieves file metadata and generates a presigned URL for downloading an S3 object.
135
+ Retrieves a file from S3 and returns its size and either a presigned URL or the actual content.
135
136
 
136
- :param storage_secrets: S3 storage secrets
137
- :param bucket_name: Name of the S3 bucket
138
- :param file_key: Key of the file in the bucket
139
- :param auth_method: Authentication method for S3
140
- :return: Tuple containing (file_size, presigned_url)
137
+ Args:
138
+ storage_secrets: Dictionary containing S3 credentials
139
+ bucket_name: Name of the S3 bucket
140
+ file_key: Key of the file in the bucket
141
+ auth_method: Authentication method to use
142
+ get_content: If True, returns the actual content instead of a presigned URL
143
+
144
+ Returns:
145
+ Tuple containing (file_size, presigned_url_or_content)
141
146
  """
142
147
  logger.info("Retrieving S3 object: {}", file_key)
148
+ s3_client = get_s3_client(auth_method, storage_secrets)
143
149
 
144
- s3_client = maybe_get_s3_client(auth_method, storage_secrets)
145
150
  try:
146
- get_allowed_file_type_or_raise(file_key)
147
- file_size = get_file_size(s3_client, bucket_name, file_key)
148
- return file_size, create_presigned_url_for_s3(s3_client, bucket_name, file_key)
149
- except Exception as e:
150
- logger.error("Encountered error while retrieving S3 object: {}", e)
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
156
+
157
+ # 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)
162
+ except ClientError as e:
163
+ logger.error(f"Error retrieving file from S3: {e}")
151
164
  raise e
152
165
 
153
166
 
@@ -158,7 +171,8 @@ def generic_delete_from_s3(
158
171
  auth_method: str,
159
172
  ) -> None:
160
173
  """
161
- Deletes arbitrary data from s3
174
+ Deletes arbitrary data from s3. If file_key ends with a filename, deletes just that file.
175
+ If file_key ends with a '/', deletes all objects with that prefix.
162
176
 
163
177
  :param storage_secrets: S3 storage secrets
164
178
  :param bucket_name: Name of the S3 bucket
@@ -169,6 +183,17 @@ def generic_delete_from_s3(
169
183
 
170
184
  s3_client = maybe_get_s3_client(auth_method, storage_secrets)
171
185
  try:
186
+ # If the file_key ends with a '/', it's a folder prefix
187
+ if file_key.endswith("/"):
188
+ # List all objects with the prefix, handling pagination
189
+ paginator = s3_client.get_paginator("list_objects_v2")
190
+ for page in paginator.paginate(Bucket=bucket_name, Prefix=file_key):
191
+ if "Contents" in page:
192
+ for obj in page["Contents"]:
193
+ s3_client.delete_object(Bucket=bucket_name, Key=obj["Key"])
194
+ return
195
+
196
+ # Delete single object
172
197
  s3_client.delete_object(Bucket=bucket_name, Key=file_key)
173
198
  except Exception as e:
174
199
  logger.error("Encountered error while deleting s3 object: {}", e)
@@ -30,7 +30,7 @@ from fides.api.util.cache import get_cache, get_encryption_cache_key
30
30
  from fides.api.util.encryption.aes_gcm_encryption_scheme import (
31
31
  encrypt_to_bytes_verify_secrets_length,
32
32
  )
33
- from fides.api.util.storage_util import storage_json_encoder
33
+ from fides.api.util.storage_util import StorageJSONEncoder
34
34
  from fides.config import CONFIG
35
35
 
36
36
  if TYPE_CHECKING:
@@ -75,7 +75,7 @@ def write_to_in_memory_buffer(
75
75
  logger.debug("Writing data to in-memory buffer")
76
76
 
77
77
  if resp_format == ResponseFormat.json.value:
78
- json_str = json.dumps(data, indent=2, default=storage_json_encoder)
78
+ json_str = json.dumps(data, indent=2, default=StorageJSONEncoder().default)
79
79
  return BytesIO(
80
80
  encrypt_access_request_results(json_str, privacy_request.id).encode(
81
81
  CONFIG.security.encoding
@@ -1,5 +1,5 @@
1
1
  from datetime import datetime
2
- from typing import Any, Dict, Union
2
+ from typing import Any, Union
3
3
 
4
4
  from bson import ObjectId
5
5
  from pydantic import ValidationError
@@ -11,6 +11,7 @@ from fides.api.schemas.storage.storage import (
11
11
  StorageType,
12
12
  )
13
13
  from fides.api.schemas.storage.storage_secrets_docs_only import possible_storage_secrets
14
+ from fides.api.util.custom_json_encoder import CustomJSONEncoder
14
15
 
15
16
 
16
17
  def get_schema_for_secrets(
@@ -48,10 +49,15 @@ def get_schema_for_secrets(
48
49
  raise ValueError(errors)
49
50
 
50
51
 
51
- def storage_json_encoder(field: Any) -> Union[str, Dict[str, str]]:
52
- """Specify str format for datetime objects"""
53
- if isinstance(field, datetime):
54
- return field.strftime("%Y-%m-%dT%H:%M:%S")
55
- if isinstance(field, ObjectId):
56
- return {"$oid": str(field)}
57
- return field
52
+ class StorageJSONEncoder(CustomJSONEncoder):
53
+ """
54
+ A JSON encoder specifically for storage operations that maintains the original
55
+ format for datetime and ObjectId while inheriting other functionality from CustomJSONEncoder.
56
+ """
57
+
58
+ def default(self, o: Any) -> Any:
59
+ if isinstance(o, datetime):
60
+ return o.strftime("%Y-%m-%dT%H:%M:%S")
61
+ if isinstance(o, ObjectId):
62
+ return {"$oid": str(o)}
63
+ return super().default(o)
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/c0c2eb63ad3e7390.css" as="style"/><link rel="stylesheet" href="/_next/static/css/c0c2eb63ad3e7390.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-abd3efcb23c0bb03.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-371b155655c83a1e.js" defer=""></script><script src="/_next/static/chunks/pages/404-c00773c4c6e930af.js" defer=""></script><script src="/_next/static/4OMQ0x81J6D4ksHggHXOF/_buildManifest.js" defer=""></script><script src="/_next/static/4OMQ0x81J6D4ksHggHXOF/_ssgManifest.js" defer=""></script><style>.data-ant-cssinjs-cache-path{content:"";}</style></head><body><div id="__next"><div style="height:100%;display:flex"></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/404","query":{},"buildId":"4OMQ0x81J6D4ksHggHXOF","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
1
+ <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/1b227ba7eabbfe2f.css" as="style"/><link rel="stylesheet" href="/_next/static/css/1b227ba7eabbfe2f.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-3a61b934ba2fb620.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-94950df9c97cd079.js" defer=""></script><script src="/_next/static/chunks/pages/404-e868487119cd14fc.js" defer=""></script><script src="/_next/static/Sez6kqW5dI0lK60IMwICO/_buildManifest.js" defer=""></script><script src="/_next/static/Sez6kqW5dI0lK60IMwICO/_ssgManifest.js" defer=""></script><style>.data-ant-cssinjs-cache-path{content:"";}</style></head><body><div id="__next"><div style="height:100%;display:flex"></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/404","query":{},"buildId":"Sez6kqW5dI0lK60IMwICO","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>