ethyca-fides 2.66.1rc0__py2.py3-none-any.whl → 2.66.2b1__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.

Potentially problematic release.


This version of ethyca-fides might be problematic. Click here for more details.

Files changed (276) hide show
  1. {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b1.dist-info}/METADATA +1 -1
  2. {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b1.dist-info}/RECORD +264 -254
  3. fides/_version.py +3 -3
  4. fides/api/alembic/migrations/versions/7e9a2b52f498_adding_masking_secrets.py +60 -0
  5. fides/api/alembic/migrations/versions/a7065df4dcf1_add_finalized_fields_to_privacy_request.py +65 -0
  6. fides/api/alembic/migrations/versions/d0031087eacb_create_manualtaskconditionaldependency_.py +106 -0
  7. fides/api/api/v1/endpoints/dataset_config_endpoints.py +13 -5
  8. fides/api/api/v1/endpoints/drp_endpoints.py +7 -1
  9. fides/api/api/v1/endpoints/privacy_request_endpoints.py +44 -1
  10. fides/api/api/v1/endpoints/user_endpoints.py +83 -7
  11. fides/api/app_setup.py +3 -2
  12. fides/api/common_exceptions.py +4 -0
  13. fides/api/db/base.py +1 -0
  14. fides/api/db/database.py +1 -1
  15. fides/api/graph/execution.py +30 -0
  16. fides/api/models/manual_task/__init__.py +2 -0
  17. fides/api/models/manual_task/conditional_dependency.py +144 -0
  18. fides/api/models/{manual_task.py → manual_task/manual_task.py} +10 -0
  19. fides/api/models/masking_secret.py +72 -0
  20. fides/api/models/policy.py +23 -0
  21. fides/api/models/privacy_request/execution_log.py +1 -0
  22. fides/api/models/privacy_request/privacy_request.py +31 -13
  23. fides/api/oauth/roles.py +2 -0
  24. fides/api/schemas/application_config.py +11 -1
  25. fides/api/schemas/masking/masking_secrets.py +1 -1
  26. fides/api/schemas/policy.py +1 -0
  27. fides/api/schemas/privacy_request.py +5 -0
  28. fides/api/service/connectors/base_connector.py +1 -0
  29. fides/api/service/connectors/bigquery_connector.py +67 -19
  30. fides/api/service/connectors/dynamodb_connector.py +2 -1
  31. fides/api/service/connectors/fides_connector.py +1 -0
  32. fides/api/service/connectors/http_connector.py +1 -0
  33. fides/api/service/connectors/manual_task_connector.py +1 -0
  34. fides/api/service/connectors/manual_webhook_connector.py +2 -1
  35. fides/api/service/connectors/mongodb_connector.py +1 -0
  36. fides/api/service/connectors/okta_connector.py +1 -0
  37. fides/api/service/connectors/query_configs/bigquery_query_config.py +91 -32
  38. fides/api/service/connectors/rds_mysql_connector.py +1 -0
  39. fides/api/service/connectors/rds_postgres_connector.py +1 -0
  40. fides/api/service/connectors/s3_connector.py +1 -0
  41. fides/api/service/connectors/saas_connector.py +1 -0
  42. fides/api/service/connectors/scylla_connector.py +1 -0
  43. fides/api/service/connectors/sql_connector.py +36 -4
  44. fides/api/service/connectors/website_connector.py +1 -0
  45. fides/api/service/privacy_request/request_runner_service.py +142 -53
  46. fides/api/service/privacy_request/request_service.py +1 -22
  47. fides/api/task/conditional_dependencies/__init__.py +0 -0
  48. fides/api/task/conditional_dependencies/evaluator.py +109 -0
  49. fides/api/task/conditional_dependencies/schemas.py +54 -0
  50. fides/api/task/deprecated_graph_task.py +24 -6
  51. fides/api/task/execute_request_tasks.py +88 -11
  52. fides/api/task/graph_task.py +38 -3
  53. fides/api/task/manual/manual_task_graph_task.py +1 -0
  54. fides/api/util/cache.py +5 -0
  55. fides/api/util/encryption/secrets_util.py +48 -18
  56. fides/common/api/scope_registry.py +3 -0
  57. fides/common/api/v1/urn_registry.py +1 -1
  58. fides/config/execution_settings.py +4 -0
  59. fides/config/utils.py +1 -0
  60. fides/service/privacy_request/privacy_request_service.py +6 -1
  61. fides/ui-build/static/admin/404.html +1 -1
  62. fides/ui-build/static/admin/_next/static/CQD5hIjm-4lH18YYESYuD/_buildManifest.js +1 -0
  63. fides/ui-build/static/admin/_next/static/chunks/1817-6f35f58cd08b04ae.js +1 -0
  64. fides/ui-build/static/admin/_next/static/chunks/{203-5a663f465ba26bb4.js → 203-4e777c324a01dbec.js} +1 -1
  65. fides/ui-build/static/admin/_next/static/chunks/431-ade3e312fac3430b.js +1 -0
  66. fides/ui-build/static/admin/_next/static/chunks/6780-4b687168dd8daa84.js +1 -0
  67. fides/ui-build/static/admin/_next/static/chunks/8237-55049f8f5fd5e058.js +1 -0
  68. fides/ui-build/static/admin/_next/static/chunks/pages/{_app-39ccb07327c2c5d5.js → _app-be7889cec5a0f5cd.js} +56 -56
  69. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-71579a199158952e.js +1 -0
  70. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-7e63ac744c45f6da.js +1 -0
  71. fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-e2d5d7e2a5265e68.js → integrations-f10a7dcf7541c865.js} +1 -1
  72. fides/ui-build/static/admin/_next/static/chunks/pages/poc/{table-migration-69ad86b7a8a9a115.js → table-migration-05616e2ae20ff4f8.js} +1 -1
  73. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-1caa552a8f11f2f2.js +1 -0
  74. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-7498d1d5974a78b0.js +1 -0
  75. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-2d3a2d967767a131.js +1 -0
  76. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-6a9068df48bdee05.js +1 -0
  77. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-fe58cebba358119d.js +1 -0
  78. fides/ui-build/static/admin/_next/static/css/{5bfb2473e5701527.css → 23cf870196941c9a.css} +1 -1
  79. fides/ui-build/static/admin/_next/static/css/b81194f2c3930152.css +1 -0
  80. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  81. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  82. fides/ui-build/static/admin/add-systems.html +1 -1
  83. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  84. fides/ui-build/static/admin/consent/configure.html +1 -1
  85. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  86. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  87. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  88. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  89. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  90. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  91. fides/ui-build/static/admin/consent/properties.html +1 -1
  92. fides/ui-build/static/admin/consent/reporting.html +1 -1
  93. fides/ui-build/static/admin/consent.html +1 -1
  94. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  95. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  96. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  97. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  98. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  99. fides/ui-build/static/admin/data-catalog.html +1 -1
  100. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  101. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  102. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  103. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  104. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  105. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  106. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  107. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  108. fides/ui-build/static/admin/datamap.html +1 -1
  109. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  110. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  111. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  112. fides/ui-build/static/admin/dataset/new.html +1 -1
  113. fides/ui-build/static/admin/dataset.html +1 -1
  114. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  115. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  116. fides/ui-build/static/admin/datastore-connection.html +1 -1
  117. fides/ui-build/static/admin/index.html +1 -1
  118. fides/ui-build/static/admin/integrations/[id].html +1 -1
  119. fides/ui-build/static/admin/integrations.html +1 -1
  120. fides/ui-build/static/admin/lib/fides-ext-gpp.js +1 -1
  121. fides/ui-build/static/admin/lib/fides-headless.js +1 -1
  122. fides/ui-build/static/admin/lib/fides-preview.js +1 -1
  123. fides/ui-build/static/admin/lib/fides-tcf.js +3 -3
  124. fides/ui-build/static/admin/lib/fides.js +3 -3
  125. fides/ui-build/static/admin/login/[provider].html +1 -1
  126. fides/ui-build/static/admin/login.html +1 -1
  127. fides/ui-build/static/admin/messaging/[id].html +1 -1
  128. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  129. fides/ui-build/static/admin/messaging.html +1 -1
  130. fides/ui-build/static/admin/poc/ant-components.html +1 -1
  131. fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
  132. fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
  133. fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
  134. fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
  135. fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
  136. fides/ui-build/static/admin/poc/forms.html +1 -1
  137. fides/ui-build/static/admin/poc/table-migration.html +1 -1
  138. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  139. fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
  140. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  141. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  142. fides/ui-build/static/admin/privacy-requests.html +1 -1
  143. fides/ui-build/static/admin/properties/[id].html +1 -1
  144. fides/ui-build/static/admin/properties/add-property.html +1 -1
  145. fides/ui-build/static/admin/properties.html +1 -1
  146. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  147. fides/ui-build/static/admin/settings/about/alpha.html +1 -1
  148. fides/ui-build/static/admin/settings/about.html +1 -1
  149. fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
  150. fides/ui-build/static/admin/settings/consent.html +1 -1
  151. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  152. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  153. fides/ui-build/static/admin/settings/domains.html +1 -1
  154. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  155. fides/ui-build/static/admin/settings/locations.html +1 -1
  156. fides/ui-build/static/admin/settings/organization.html +1 -1
  157. fides/ui-build/static/admin/settings/regulations.html +1 -1
  158. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  159. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  160. fides/ui-build/static/admin/systems.html +1 -1
  161. fides/ui-build/static/admin/taxonomy.html +1 -1
  162. fides/ui-build/static/admin/user-management/new.html +1 -1
  163. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  164. fides/ui-build/static/admin/user-management.html +1 -1
  165. fides/ui-build/static/admin/_next/static/IeU4qLtEtRJo0FsaIFIr8/_buildManifest.js +0 -1
  166. fides/ui-build/static/admin/_next/static/chunks/1817-e601e737e3cc7a0e.js +0 -1
  167. fides/ui-build/static/admin/_next/static/chunks/431-34f0b91c26f8d9ab.js +0 -1
  168. fides/ui-build/static/admin/_next/static/chunks/6780-5bd185892f6af46e.js +0 -1
  169. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-2265ecb899d45fbc.js +0 -1
  170. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-8ab33a1e0272df1f.js +0 -1
  171. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-32600543eb7b584f.js +0 -1
  172. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-10ce53ea356f8bad.js +0 -1
  173. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-5501bbb129fee9c4.js +0 -1
  174. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-cbe4c8f9096b6543.js +0 -1
  175. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-ff5738706da07801.js +0 -1
  176. fides/ui-build/static/admin/_next/static/css/2cadb5f62dcd7c2b.css +0 -1
  177. {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b1.dist-info}/WHEEL +0 -0
  178. {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b1.dist-info}/entry_points.txt +0 -0
  179. {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b1.dist-info}/licenses/LICENSE +0 -0
  180. {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b1.dist-info}/top_level.txt +0 -0
  181. /fides/ui-build/static/admin/_next/static/{IeU4qLtEtRJo0FsaIFIr8 → CQD5hIjm-4lH18YYESYuD}/_ssgManifest.js +0 -0
  182. /fides/ui-build/static/admin/_next/static/chunks/{2921-455e6357b74d2f76.js → 2921-86f1547ac40a5cdf.js} +0 -0
  183. /fides/ui-build/static/admin/_next/static/chunks/{3450-0ba194991d0cca88.js → 3450-1cc2bb07ed142203.js} +0 -0
  184. /fides/ui-build/static/admin/_next/static/chunks/{3855-e172870d3e21b0dd.js → 3855-e2fa6db53d32c3de.js} +0 -0
  185. /fides/ui-build/static/admin/_next/static/chunks/{3872-46cebf7ec1b31a2b.js → 3872-84b7e380b88b4454.js} +0 -0
  186. /fides/ui-build/static/admin/_next/static/chunks/{3923-6cc911dafccc5f63.js → 3923-13a6b4da2d51bf8f.js} +0 -0
  187. /fides/ui-build/static/admin/_next/static/chunks/{401-1b529d5800aa1f3a.js → 401-3cc1fee61494e3bd.js} +0 -0
  188. /fides/ui-build/static/admin/_next/static/chunks/{409-a257e14acebcd73b.js → 409-45a125437261580c.js} +0 -0
  189. /fides/ui-build/static/admin/_next/static/chunks/{4121-2bc09fc4ddbfe5cb.js → 4121-f50675521dfee6eb.js} +0 -0
  190. /fides/ui-build/static/admin/_next/static/chunks/{4230-60100f7ef3ddcde1.js → 4230-840c287045c88b34.js} +0 -0
  191. /fides/ui-build/static/admin/_next/static/chunks/{4608-bbb7bf511a05c3c2.js → 4608-a8e3100e2806dbff.js} +0 -0
  192. /fides/ui-build/static/admin/_next/static/chunks/{5309-d73339f062763fed.js → 5309-67bdf9001531e972.js} +0 -0
  193. /fides/ui-build/static/admin/_next/static/chunks/{5574-b13021775a15bfd2.js → 5574-9312f97b637d9ee2.js} +0 -0
  194. /fides/ui-build/static/admin/_next/static/chunks/{6084-7178ff6ea6822475.js → 6084-5d7598b7bcb548cf.js} +0 -0
  195. /fides/ui-build/static/admin/_next/static/chunks/{6662-507be5d46e5b719b.js → 6662-efb2cf74641647f2.js} +0 -0
  196. /fides/ui-build/static/admin/_next/static/chunks/{6853-2ad3e08fe6f9f5f2.js → 6853-4f8bf6558f8c6a46.js} +0 -0
  197. /fides/ui-build/static/admin/_next/static/chunks/{6882-6af16fef26c21e06.js → 6882-586b84aeb02d5830.js} +0 -0
  198. /fides/ui-build/static/admin/_next/static/chunks/{6954-bb875d9ac89f6030.js → 6954-34e062e4bffc7e71.js} +0 -0
  199. /fides/ui-build/static/admin/_next/static/chunks/{7476-281ee9a8286556f3.js → 7476-d206c11823c91088.js} +0 -0
  200. /fides/ui-build/static/admin/_next/static/chunks/{7630-9aac73191ed5ed13.js → 7630-b1c93688013ef013.js} +0 -0
  201. /fides/ui-build/static/admin/_next/static/chunks/{787-fb41002f797eb2df.js → 787-cbe2d0bfb513d90a.js} +0 -0
  202. /fides/ui-build/static/admin/_next/static/chunks/{79-7e87aff851423d4a.js → 79-3db1941d274f40c7.js} +0 -0
  203. /fides/ui-build/static/admin/_next/static/chunks/{796-329a5f823ec258a5.js → 796-98d4bd68909fbe1e.js} +0 -0
  204. /fides/ui-build/static/admin/_next/static/chunks/{9046-ce9567c9074563e2.js → 9046-54877976a0529de2.js} +0 -0
  205. /fides/ui-build/static/admin/_next/static/chunks/{9226-746771d47dff6266.js → 9226-72ad691ca57b83ef.js} +0 -0
  206. /fides/ui-build/static/admin/_next/static/chunks/{9826-111aaee8bd8dbd09.js → 9826-3c578665c6d3b21d.js} +0 -0
  207. /fides/ui-build/static/admin/_next/static/chunks/{9951-9b753ad7c3f51bdf.js → 9951-595d0f1588215081.js} +0 -0
  208. /fides/ui-build/static/admin/_next/static/chunks/pages/{404-aece2c920ea14514.js → 404-2d803dab6a00f353.js} +0 -0
  209. /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-98777246bec9dc2a.js → manual-acb59f8b5e97512a.js} +0 -0
  210. /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-dc75dc6e37e52f05.js → multiple-8ff7f37913ad736a.js} +0 -0
  211. /fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-a71c0aff4e0e6535.js → add-systems-0943633a8e422695.js} +0 -0
  212. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-24d226b5a8de5c74.js → add-vendors-d00c9034cdeb0236.js} +0 -0
  213. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-6a8ef51138ac926a.js → configure-0e1ca0f4c8e7f4da.js} +0 -0
  214. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-1edf582ba3cd3bbb.js → [id]-fc3a011154a2e1de.js} +0 -0
  215. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-685771e5f7196d87.js → privacy-experience-09d4408014bcfe1c.js} +0 -0
  216. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-6ccedc70dc447089.js → [id]-d67542783ef5ddac.js} +0 -0
  217. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-944bca1cc57985b5.js → new-3f20e8a316bb3d5b.js} +0 -0
  218. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-84f4bd14ce8673bc.js → privacy-notices-23e9dcd4590312d2.js} +0 -0
  219. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-6f86ab63a08a6528.js → properties-057cad65e7414a44.js} +0 -0
  220. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{reporting-afdbd4665657cfa1.js → reporting-8f891957c8944137.js} +0 -0
  221. /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-73d3cbf68f7c3a31.js → consent-e17c56eec8d91371.js} +0 -0
  222. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-11d52f1570759c4d.js → [resourceUrn]-99c9092d65c94807.js} +0 -0
  223. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-6ba9e160dae64695.js → [projectUrn]-80a6cc8e8573514a.js} +0 -0
  224. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-32eac8bbd217615a.js → projects-774fecea22ba8852.js} +0 -0
  225. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-b83afa5565d0c84e.js → [resourceUrn]-f6bd6aff389cb9fe.js} +0 -0
  226. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{resources-7648bbd4f6711e4d.js → resources-6c3714ee97a718c1.js} +0 -0
  227. /fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-6f630d42ac9fb6b4.js → data-catalog-8a7f9285da66b965.js} +0 -0
  228. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{action-center-9ddb52ebb7ac4c71.js → action-center-85e140788e251272.js} +0 -0
  229. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{activity-9aa744d56cdacb0d.js → activity-5af9381f02b2aff6.js} +0 -0
  230. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-393e20924c83373e.js → [resourceUrn]-31e6c54794a9883e.js} +0 -0
  231. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-8733807dad4bc96e.js → detection-2822a423a7ad0550.js} +0 -0
  232. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-14bd7500362ff224.js → [resourceUrn]-6421ce247549c5d6.js} +0 -0
  233. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-9e7dfd5a6acc2e8f.js → discovery-3eac407ac5181a3c.js} +0 -0
  234. /fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-7674b97d655c193b.js → datamap-d2b275d83089820d.js} +0 -0
  235. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-c0d2bfd465df20e0.js → [...subfieldNames]-1c98bd0959d9570a.js} +0 -0
  236. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-28280a8a39a6e37c.js → [collectionName]-e548cabda7da32c9.js} +0 -0
  237. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-006b695e5af5ef24.js → [datasetId]-a8e8b5f4ee7af86c.js} +0 -0
  238. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-82fb246d87e58ebd.js → new-513c862c3a707735.js} +0 -0
  239. /fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-20165c31ab1bc7cf.js → dataset-747b7a13289f1cd7.js} +0 -0
  240. /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-b4a6bcc87d126840.js → [id]-3d22525b3c327b2e.js} +0 -0
  241. /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-f95d7b0bbfc58f5a.js → new-d2cad97495e86adb.js} +0 -0
  242. /fides/ui-build/static/admin/_next/static/chunks/pages/{datastore-connection-c391c6fad56eec48.js → datastore-connection-0f29b47402292070.js} +0 -0
  243. /fides/ui-build/static/admin/_next/static/chunks/pages/{index-1919aab9e5834b51.js → index-12ac3e317fc86f21.js} +0 -0
  244. /fides/ui-build/static/admin/_next/static/chunks/pages/integrations/{[id]-eacabe4a80cb8813.js → [id]-8d83a5518c00fcfc.js} +0 -0
  245. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-53fecfb9dd6a1e0c.js → [id]-5627d0d0668077f9.js} +0 -0
  246. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-76b01cec5fde10a9.js → add-template-feca66ad5c5fe54a.js} +0 -0
  247. /fides/ui-build/static/admin/_next/static/chunks/pages/{messaging-5094ffea13f32ed9.js → messaging-c1bd3e7adbe8d2d3.js} +0 -0
  248. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{ant-components-5c08e8447c45ce44.js → ant-components-64a322d01aae5ca7.js} +0 -0
  249. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{AntForm-06ad5f34585480aa.js → AntForm-8bca16a7726e7eb2.js} +0 -0
  250. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikAntFormItem-6f071c2bc9446cb0.js → FormikAntFormItem-b0f246fc3b67ebf7.js} +0 -0
  251. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikControlled-efcc38c58991ac9e.js → FormikControlled-1a0852b090bfc392.js} +0 -0
  252. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikField-430ba5c979abfb7c.js → FormikField-11f3de1b45e36583.js} +0 -0
  253. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{forms-5c561880bf131afb.js → forms-1b73a1c2b6c6285f.js} +0 -0
  254. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-d888a69a3bbe040e.js → configure-e551a860ec727802.js} +0 -0
  255. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-d3d8e3d7583ec635.js → [id]-dd99183f93763ae4.js} +0 -0
  256. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-1af10ed303815d46.js → add-property-0bdbc1fcbf553b8f.js} +0 -0
  257. /fides/ui-build/static/admin/_next/static/chunks/pages/{properties-cebc0dc186be499a.js → properties-e959378bb32b6b73.js} +0 -0
  258. /fides/ui-build/static/admin/_next/static/chunks/pages/reporting/{datamap-e130c0197362e8f3.js → datamap-2a98bd257edd8f47.js} +0 -0
  259. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/{alpha-5e1322de868d615e.js → alpha-8f98a4895e74725e.js} +0 -0
  260. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-241f95e372b65d0f.js → about-8155a35a62fdb5ae.js} +0 -0
  261. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/{[purpose_id]-fc201657f4a782c7.js → [purpose_id]-668d74c041d74650.js} +0 -0
  262. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-c2d39cba8396ef3a.js → consent-a989532a12c40dcf.js} +0 -0
  263. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-d992103cc55901ae.js → custom-fields-45bea76ff7eda3cb.js} +0 -0
  264. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-41242f805599feda.js → domain-records-51333dbd21cb37c8.js} +0 -0
  265. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-2e885f74c92f669c.js → domains-bde86e5f6c09da5a.js} +0 -0
  266. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-ff112655ad5f41e5.js → email-templates-4f9a5cc8bea7725b.js} +0 -0
  267. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-023e1895552817de.js → locations-6946e78a5d43e654.js} +0 -0
  268. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-ac403c0886b20e20.js → organization-55a10e01dffc8039.js} +0 -0
  269. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-86062a18e081a52a.js → regulations-102efd9199e87124.js} +0 -0
  270. /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-7a3396ac819c7904.js → test-datasets-f91f22cf96566ed4.js} +0 -0
  271. /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-8314a819837f5b2a.js → [id]-d4a57ea18935dd70.js} +0 -0
  272. /fides/ui-build/static/admin/_next/static/chunks/pages/{systems-21f423a7c417aa9d.js → systems-648a0ff4920579ce.js} +0 -0
  273. /fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-6387fcc8cce872eb.js → taxonomy-0b9d1a24188f65a9.js} +0 -0
  274. /fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-a2524414e968f862.js → new-bc4eb541906781e6.js} +0 -0
  275. /fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-173ac3a1ed2b05a6.js → user-management-45bfa04e45a7d13f.js} +0 -0
  276. /fides/ui-build/static/admin/_next/static/chunks/{webpack-ff0cd6bff75588da.js → webpack-63a0c45b150a1037.js} +0 -0
@@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional, Union, cast
3
3
  import pydash
4
4
  from fideslang.models import MaskingStrategies
5
5
  from loguru import logger
6
- from sqlalchemy import MetaData, Table, text
6
+ from sqlalchemy import MetaData, Table, or_, text
7
7
  from sqlalchemy.engine import Engine
8
8
  from sqlalchemy.sql import Delete, Update
9
9
  from sqlalchemy.sql.elements import ColumnElement, TextClause
@@ -125,6 +125,7 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
125
125
  policy: Policy,
126
126
  request: PrivacyRequest,
127
127
  client: Engine,
128
+ input_data: Optional[Dict[str, List[Any]]] = None,
128
129
  ) -> Union[List[Update], List[Delete]]:
129
130
  """
130
131
  Generate a masking statement for BigQuery.
@@ -137,7 +138,7 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
137
138
  logger.info(
138
139
  f"Masking override detected for collection {node.address.value}: {masking_override.strategy.value}"
139
140
  )
140
- return self.generate_delete(row, client)
141
+ return self.generate_delete(client, input_data or {})
141
142
  return self.generate_update(row, policy, request, client)
142
143
 
143
144
  def generate_update(
@@ -198,9 +199,16 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
198
199
 
199
200
  table = Table(self._generate_table_name(), MetaData(bind=client), autoload=True)
200
201
  where_clauses: List[ColumnElement] = [
201
- getattr(table.c, k) == v for k, v in non_empty_reference_field_keys.items()
202
+ table.c[k] == v for k, v in non_empty_reference_field_keys.items()
202
203
  ]
203
204
 
205
+ # Create update values using Column objects as keys to handle column names with spaces
206
+ update_values = {}
207
+ for column_name, value in final_update_map.items():
208
+ # Use bracket notation to access columns with spaces in their names
209
+ column = table.c[column_name]
210
+ update_values[column] = value
211
+
204
212
  if self.partitioning:
205
213
  partition_clauses = self.get_partition_clauses()
206
214
  partitioned_queries = []
@@ -211,34 +219,37 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
211
219
  partitioned_queries.append(
212
220
  table.update()
213
221
  .where(*(where_clauses + [text(partition_clause)]))
214
- .values(**final_update_map)
222
+ .values(update_values)
215
223
  )
216
224
 
217
225
  return partitioned_queries
218
226
 
219
- return [table.update().where(*where_clauses).values(**final_update_map)]
227
+ return [table.update().where(*where_clauses).values(update_values)]
220
228
 
221
- def generate_delete(self, row: Row, client: Engine) -> List[Delete]:
222
- """Returns a List of SQLAlchemy DELETE statements for BigQuery. Does not actually execute the delete statement.
229
+ def generate_delete(
230
+ self,
231
+ client: Engine,
232
+ input_data: Optional[Dict[str, List[Any]]] = None,
233
+ ) -> List[Delete]:
234
+ """
235
+ Returns a List of SQLAlchemy DELETE statements for BigQuery. Does not actually execute the delete statement.
223
236
 
224
237
  Used when a collection-level masking override is present and the masking strategy is DELETE.
225
238
 
226
239
  A List of multiple DELETE statements are returned for partitioned tables; for a non-partitioned table,
227
240
  a single DELETE statement is returned in a List for consistent typing.
228
-
229
- TODO: DRY up this method and `generate_update` a bit
230
241
  """
231
242
 
232
- non_empty_reference_field_keys: Dict[str, Field] = filter_nonempty_values(
233
- {
234
- fpath.string_path: fld.cast(row[fpath.string_path])
235
- for fpath, fld in self.reference_field_paths.items()
236
- if fpath.string_path in row
237
- }
238
- )
243
+ if not input_data:
244
+ logger.warning(
245
+ "No input data provided for node '{}', skipping DELETE statement generation",
246
+ self.node.address,
247
+ )
248
+ return []
239
249
 
240
- valid = len(non_empty_reference_field_keys) > 0
241
- if not valid:
250
+ filtered_data = self.node.typed_filtered_values(input_data)
251
+
252
+ if not filtered_data:
242
253
  logger.warning(
243
254
  "There is not enough data to generate a valid DELETE statement for {}",
244
255
  self.node.address,
@@ -246,9 +257,17 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
246
257
  return []
247
258
 
248
259
  table = Table(self._generate_table_name(), MetaData(bind=client), autoload=True)
249
- where_clauses: List[ColumnElement] = [
250
- getattr(table.c, k) == v for k, v in non_empty_reference_field_keys.items()
251
- ]
260
+
261
+ # Build individual reference clauses
262
+ where_clauses: List[ColumnElement] = []
263
+ for column_name, values in filtered_data.items():
264
+ if len(values) == 1:
265
+ where_clauses.append(table.c[column_name] == values[0])
266
+ else:
267
+ where_clauses.append(table.c[column_name].in_(values))
268
+
269
+ # Combine reference clauses with OR instead of AND
270
+ combined_reference_clause = or_(*where_clauses)
252
271
 
253
272
  if self.partitioning:
254
273
  partition_clauses = self.get_partition_clauses()
@@ -259,12 +278,25 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
259
278
 
260
279
  for partition_clause in partition_clauses:
261
280
  partitioned_queries.append(
262
- table.delete().where(*(where_clauses + [text(partition_clause)]))
281
+ table.delete()
282
+ .where(combined_reference_clause)
283
+ .where(text(partition_clause))
263
284
  )
264
285
 
265
286
  return partitioned_queries
266
287
 
267
- return [table.delete().where(*where_clauses)]
288
+ return [table.delete().where(combined_reference_clause)]
289
+
290
+ def uses_delete_masking_strategy(self) -> bool:
291
+ """Check if this collection uses DELETE masking strategy.
292
+
293
+ Returns True if masking override is present and strategy is DELETE.
294
+ """
295
+ masking_override = self.node.collection.masking_strategy_override
296
+ return (
297
+ masking_override is not None
298
+ and masking_override.strategy == MaskingStrategies.DELETE
299
+ )
268
300
 
269
301
  def format_fields_for_query(
270
302
  self,
@@ -281,6 +313,25 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
281
313
  formatted_fields.append(field_path.levels[0])
282
314
  return formatted_fields
283
315
 
316
+ def format_clause_for_query(
317
+ self, string_path: str, operator: str, operand: str
318
+ ) -> str:
319
+ """
320
+ Returns clauses with proper BigQuery backtick escaping for column names.
321
+ Handles column names with spaces and nested fields (dot-separated) by escaping each part individually.
322
+ """
323
+ # For nested fields (containing dots), escape each part individually
324
+ if "." in string_path:
325
+ parts = string_path.split(".")
326
+ escaped_field = ".".join(f"`{part}`" for part in parts)
327
+ else:
328
+ # For simple fields, wrap the entire name in backticks
329
+ escaped_field = f"`{string_path}`"
330
+
331
+ if operator == "IN":
332
+ return f"{escaped_field} IN ({operand})"
333
+ return f"{escaped_field} {operator} :{operand}"
334
+
284
335
  def generate_raw_query_without_tuples(
285
336
  self, field_list: List[str], filters: Dict[str, List[Any]]
286
337
  ) -> Optional[TextClause]:
@@ -290,27 +341,35 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
290
341
 
291
342
  This is an override of the base class method that supports nested fields for BigQuery.
292
343
 
293
- Examples with dot-delimited field names, notice the periods are replaced with underscores in the parameter bindings:
344
+ Examples with field names containing dots and spaces, notice these are replaced with underscores in the parameter bindings:
294
345
 
295
346
  1. Single value filter:
296
347
  field_list = ["id", "name", "email"]
297
348
  filters = {"user.id": [123]}
298
349
 
299
- Generates: SELECT id, name, email FROM `project_id.dataset_id.table_name` WHERE (user.id = :user_id)
350
+ Generates: SELECT id, name, email FROM `project_id.dataset_id.table_name` WHERE (`user`.`id` = :user_id)
300
351
  With parameter binding: user_id = 123
301
352
 
302
- 2. Multiple value filter:
353
+ 2. Field with spaces:
354
+ field_list = ["id", "custom id", "email"]
355
+ filters = {"custom id": ["abc123"]}
356
+
357
+ Generates: SELECT id, `custom id`, email FROM `project_id.dataset_id.table_name` WHERE (`custom id` = :custom_id)
358
+ With parameter binding: custom_id = "abc123"
359
+
360
+ 3. Multiple value filter with nested field:
303
361
  field_list = ["id", "name", "email"]
304
- filters = {"user.status": ["active", "pending"]}
362
+ filters = {"contact_info.primary_email": ["active", "pending"]}
305
363
 
306
- Generates: SELECT id, name, email FROM `project_id.dataset_id.table_name` WHERE (user.status IN (:user_status_in_stmt_generated_0, :user_status_in_stmt_generated_1))
307
- With parameter bindings: user_status_in_stmt_generated_0 = "active", user_status_in_stmt_generated_1 = "pending"
364
+ Generates: SELECT id, name, email FROM `project_id.dataset_id.table_name` WHERE (`contact_info`.`primary_email` IN (:contact_info_primary_email_in_stmt_generated_0, :contact_info_primary_email_in_stmt_generated_1))
365
+ With parameter bindings: contact_info_primary_email_in_stmt_generated_0 = "active", contact_info_primary_email_in_stmt_generated_1 = "pending"
308
366
  """
309
367
  clauses = []
310
368
  query_data = {}
311
369
  for field_name, field_value in filters.items():
312
- # Replace dots with underscores in field names for parameter binding
313
- field_binding_name = field_name.replace(".", "_")
370
+ # Replace dots and spaces with underscores in field names for parameter binding
371
+ # SQLAlchemy parameter names cannot contain spaces or special characters
372
+ field_binding_name = field_name.replace(".", "_").replace(" ", "_")
314
373
  data = set(field_value)
315
374
  if len(data) == 1:
316
375
  clauses.append(
@@ -333,7 +392,7 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
333
392
  clauses.append(self.format_clause_for_query(field_name, "IN", operand))
334
393
 
335
394
  if len(clauses) > 0:
336
- formatted_fields = ", ".join(field_list)
395
+ formatted_fields = ", ".join([f"`{field}`" for field in field_list])
337
396
  query_str = self.get_formatted_query_string(formatted_fields, clauses)
338
397
  return text(query_str).params(query_data)
339
398
 
@@ -158,6 +158,7 @@ class RDSMySQLConnector(RDSConnectorMixin, SQLConnector):
158
158
  privacy_request: PrivacyRequest,
159
159
  request_task: RequestTask,
160
160
  rows: List[Row],
161
+ input_data: Optional[Dict[str, List[Any]]] = None,
161
162
  ) -> int:
162
163
  """DSR execution not yet supported for RDS MySQL"""
163
164
  return 0
@@ -147,6 +147,7 @@ class RDSPostgresConnector(RDSConnectorMixin, SQLConnector):
147
147
  privacy_request: PrivacyRequest,
148
148
  request_task: RequestTask,
149
149
  rows: List[Row],
150
+ input_data: Optional[Dict[str, List[Any]]] = None,
150
151
  ) -> int:
151
152
  """DSR execution not yet supported for RDS Postgres"""
152
153
  return 0
@@ -66,6 +66,7 @@ class S3Connector(BaseConnector):
66
66
  privacy_request: PrivacyRequest,
67
67
  request_task: RequestTask,
68
68
  rows: List[Row],
69
+ input_data: Optional[Dict[str, List[Any]]] = None,
69
70
  ) -> int:
70
71
  """DSR execution not yet supported for S3"""
71
72
  return 0
@@ -517,6 +517,7 @@ class SaaSConnector(BaseConnector[AuthenticatedClient], Contextualizable):
517
517
  privacy_request: PrivacyRequest,
518
518
  request_task: RequestTask,
519
519
  rows: List[Row],
520
+ input_data: Optional[Dict[str, List[Any]]] = None,
520
521
  ) -> int:
521
522
  """Execute a masking request. Return the number of rows that have been updated."""
522
523
  self.set_privacy_request_state(privacy_request, node, request_task)
@@ -150,6 +150,7 @@ class ScyllaConnector(BaseConnector[Cluster]):
150
150
  privacy_request: PrivacyRequest,
151
151
  request_task: RequestTask,
152
152
  rows: List[Row],
153
+ input_data: Optional[Dict[str, List[Any]]] = None,
153
154
  ) -> int:
154
155
  """Execute a masking request"""
155
156
  query_config = self.query_config(node)
@@ -27,11 +27,13 @@ from fides.api.graph.execution import ExecutionNode
27
27
  from fides.api.models.connectionconfig import ConnectionConfig, ConnectionTestStatus
28
28
  from fides.api.models.policy import Policy
29
29
  from fides.api.models.privacy_request import PrivacyRequest, RequestTask
30
+ from fides.api.schemas.application_config import SqlDryRunMode
30
31
  from fides.api.schemas.connection_configuration import ConnectionConfigSecretsSchema
31
32
  from fides.api.service.connectors.base_connector import BaseConnector
32
33
  from fides.api.service.connectors.query_configs.query_config import SQLQueryConfig
33
34
  from fides.api.util.collection_util import Row
34
35
  from fides.config import get_config
36
+ from fides.config.config_proxy import ConfigProxy
35
37
 
36
38
  from fides.api.models.sql_models import ( # type: ignore[attr-defined] # isort: skip
37
39
  Dataset as CtlDataset,
@@ -58,6 +60,23 @@ class SQLConnector(BaseConnector[Engine]):
58
60
  )
59
61
  self.ssh_server: sshtunnel._ForwardServer = None
60
62
 
63
+ def should_dry_run(self, mode_to_check: SqlDryRunMode) -> bool:
64
+ """
65
+ Check if SQL dry run is enabled for the specified mode.
66
+
67
+ Args:
68
+ mode_to_check: The SqlDryRunMode to check for
69
+
70
+ Returns:
71
+ bool: True if the current mode matches the mode to check
72
+ """
73
+ from fides.api.api.deps import get_autoclose_db_session as get_db
74
+
75
+ with get_db() as db:
76
+ config_proxy = ConfigProxy(db)
77
+ current_mode = getattr(config_proxy.execution, "sql_dry_run", None)
78
+ return current_mode == mode_to_check
79
+
61
80
  @staticmethod
62
81
  def cursor_result_to_rows(results: CursorResult) -> List[Row]:
63
82
  """Convert SQLAlchemy results to a list of dictionaries"""
@@ -140,6 +159,10 @@ class SQLConnector(BaseConnector[Engine]):
140
159
  if query is None:
141
160
  return []
142
161
 
162
+ if self.should_dry_run(SqlDryRunMode.access):
163
+ logger.warning(f"SQL DRY RUN - Would execute SQL: {query}")
164
+ return []
165
+
143
166
  with client.connect() as connection:
144
167
  self.set_schema(connection)
145
168
  results = connection.execute(query)
@@ -160,6 +183,10 @@ class SQLConnector(BaseConnector[Engine]):
160
183
  if stmt is None:
161
184
  return []
162
185
 
186
+ if self.should_dry_run(SqlDryRunMode.access):
187
+ logger.warning(f"SQL DRY RUN - Would execute SQL: {stmt}")
188
+ return []
189
+
163
190
  logger.info("Starting data retrieval for {}", node.address)
164
191
  with client.connect() as connection:
165
192
  self.set_schema(connection)
@@ -178,20 +205,25 @@ class SQLConnector(BaseConnector[Engine]):
178
205
  privacy_request: PrivacyRequest,
179
206
  request_task: RequestTask,
180
207
  rows: List[Row],
208
+ input_data: Optional[Dict[str, List[Any]]] = None,
181
209
  ) -> int:
182
210
  """Execute a masking request. Returns the number of records masked"""
183
211
  query_config = self.query_config(node)
184
212
  update_ct = 0
185
213
  client = self.client()
214
+
186
215
  for row in rows:
187
216
  update_stmt: Optional[TextClause] = query_config.generate_update_stmt(
188
217
  row, policy, privacy_request
189
218
  )
190
219
  if update_stmt is not None:
191
- with client.connect() as connection:
192
- self.set_schema(connection)
193
- results: LegacyCursorResult = connection.execute(update_stmt)
194
- update_ct = update_ct + results.rowcount
220
+ if self.should_dry_run(SqlDryRunMode.erasure):
221
+ logger.warning(f"SQL DRY RUN - Would execute SQL: {update_stmt}")
222
+ else:
223
+ with client.connect() as connection:
224
+ self.set_schema(connection)
225
+ results: LegacyCursorResult = connection.execute(update_stmt)
226
+ update_ct = update_ct + results.rowcount
195
227
  return update_ct
196
228
 
197
229
  def close(self) -> None:
@@ -75,6 +75,7 @@ class WebsiteConnector(BaseConnector):
75
75
  privacy_request: PrivacyRequest,
76
76
  request_task: RequestTask,
77
77
  rows: List[Row],
78
+ input_data: Optional[Dict[str, List[Any]]] = None,
78
79
  ) -> int:
79
80
  """DSR execution not supported for website connector"""
80
81
  return 0
@@ -14,6 +14,7 @@ from fides.api.common_exceptions import (
14
14
  ClientUnsuccessfulException,
15
15
  IdentityNotFoundException,
16
16
  ManualWebhookFieldsUnset,
17
+ MaskingSecretsExpired,
17
18
  MessageDispatchException,
18
19
  NoCachedManualWebhookEntry,
19
20
  PrivacyRequestExit,
@@ -75,6 +76,7 @@ from fides.api.task.graph_task import (
75
76
  from fides.api.task.manual.manual_task_utils import create_manual_task_artificial_graphs
76
77
  from fides.api.tasks import DatabaseTask, celery_app
77
78
  from fides.api.tasks.scheduled.scheduler import scheduler
79
+ from fides.api.util.cache import get_all_masking_secret_keys
78
80
  from fides.api.util.collection_util import Row
79
81
  from fides.api.util.logger import Pii, _log_exception, _log_warning
80
82
  from fides.api.util.logger_context_utils import LoggerContextKeys, log_context
@@ -478,12 +480,21 @@ def run_privacy_request(
478
480
  connection_configs
479
481
  )
480
482
 
483
+ # If the privacy request requires manual finalization and has not yet been finalized, we exit here
484
+ if (
485
+ privacy_request.status
486
+ == PrivacyRequestStatus.requires_manual_finalization
487
+ and privacy_request.finalized_at is None
488
+ ):
489
+ return
490
+
481
491
  # Access CHECKPOINT
482
492
  if (
483
493
  policy.get_rules_for_action(action_type=ActionType.access)
484
494
  or policy.get_rules_for_action(action_type=ActionType.erasure)
485
495
  ) and can_run_checkpoint(
486
- request_checkpoint=CurrentStep.access, from_checkpoint=resume_step
496
+ request_checkpoint=CurrentStep.access,
497
+ from_checkpoint=resume_step,
487
498
  ):
488
499
  privacy_request.cache_failed_checkpoint_details(CurrentStep.access)
489
500
  access_runner(
@@ -528,9 +539,12 @@ def run_privacy_request(
528
539
  if policy.get_rules_for_action(
529
540
  action_type=ActionType.erasure
530
541
  ) and can_run_checkpoint(
531
- request_checkpoint=CurrentStep.erasure, from_checkpoint=resume_step
542
+ request_checkpoint=CurrentStep.erasure,
543
+ from_checkpoint=resume_step,
532
544
  ):
533
545
  privacy_request.cache_failed_checkpoint_details(CurrentStep.erasure)
546
+ _verify_masking_secrets(policy, privacy_request, resume_step)
547
+
534
548
  # We only need to run the erasure once until masking strategies are handled
535
549
  erasure_runner(
536
550
  privacy_request=privacy_request,
@@ -652,58 +666,111 @@ def run_privacy_request(
652
666
  if not proceed:
653
667
  return
654
668
 
655
- legacy_request_completion_enabled = ConfigProxy(
656
- session
657
- ).notifications.send_request_completion_notification
658
-
659
- action_type = (
660
- MessagingActionType.PRIVACY_REQUEST_COMPLETE_ACCESS
661
- if policy.get_rules_for_action(action_type=ActionType.access)
662
- else MessagingActionType.PRIVACY_REQUEST_COMPLETE_DELETION
663
- )
664
-
665
- if message_send_enabled(
666
- session,
667
- privacy_request.property_id,
668
- action_type,
669
- legacy_request_completion_enabled,
670
- ) and not policy.get_rules_for_action(action_type=ActionType.consent):
671
- try:
672
- if not access_result_urls:
673
- # For DSR 3.0, if the request had both access and erasure rules, this needs to be fetched
674
- # from the database because the Privacy Request would have exited
675
- # processing and lost access to the access_result_urls in memory
676
- access_result_urls = (
677
- privacy_request.access_result_urls or {}
678
- ).get("access_result_urls", [])
679
- initiate_privacy_request_completion_email(
680
- session,
681
- policy,
682
- access_result_urls,
683
- identity_data,
684
- privacy_request.property_id,
685
- )
686
- except (IdentityNotFoundException, MessageDispatchException) as e:
687
- privacy_request.error_processing(db=session)
688
- # If dev mode, log traceback
689
- _log_exception(e, CONFIG.dev_mode)
690
- return
691
-
692
- # Only mark as complete if not in error state
693
- if privacy_request.status != PrivacyRequestStatus.error:
694
- privacy_request.finished_processing_at = datetime.utcnow()
695
- AuditLog.create(
696
- db=session,
697
- data={
698
- "user_id": "system",
699
- "privacy_request_id": privacy_request.id,
700
- "action": AuditLogAction.finished,
701
- "message": "",
702
- },
669
+ # Request finalization CHECKPOINT
670
+ if can_run_checkpoint(
671
+ request_checkpoint=CurrentStep.finalization,
672
+ from_checkpoint=resume_step,
673
+ ):
674
+ privacy_request.cache_failed_checkpoint_details(
675
+ CurrentStep.finalization,
703
676
  )
704
- privacy_request.status = PrivacyRequestStatus.complete
705
- logger.info("Privacy request run completed.")
706
- privacy_request.save(db=session)
677
+ if privacy_request.status != PrivacyRequestStatus.error:
678
+ erasure_rules = policy.get_rules_for_action(
679
+ action_type=ActionType.erasure
680
+ )
681
+ if (
682
+ privacy_request.finalized_at is None
683
+ and erasure_rules
684
+ and CONFIG.execution.erasure_request_finalization_required
685
+ ):
686
+ logger.info(
687
+ "Marking privacy request '{}' as requires manual finalization.",
688
+ privacy_request.id,
689
+ )
690
+ privacy_request.status = (
691
+ PrivacyRequestStatus.requires_manual_finalization
692
+ )
693
+ privacy_request.save(db=session)
694
+ return
695
+
696
+ # Finally, mark the request as complete
697
+ if privacy_request.finalized_at:
698
+ logger.info(
699
+ "Marking privacy request '{}' as finalized.",
700
+ privacy_request.id,
701
+ )
702
+ privacy_request.add_success_execution_log(
703
+ session,
704
+ connection_key=None,
705
+ dataset_name="Request finalized",
706
+ collection_name=None,
707
+ message=f"Request finalized for privacy request: {privacy_request.id}",
708
+ action_type=privacy_request.policy.get_action_type(), # type: ignore
709
+ )
710
+
711
+ logger.info(
712
+ "Marking privacy request '{}' as complete.",
713
+ privacy_request.id,
714
+ )
715
+ AuditLog.create(
716
+ db=session,
717
+ data={
718
+ "user_id": "system",
719
+ "privacy_request_id": privacy_request.id,
720
+ "action": AuditLogAction.finished,
721
+ "message": "",
722
+ },
723
+ )
724
+ privacy_request.status = PrivacyRequestStatus.complete
725
+ privacy_request.finished_processing_at = datetime.utcnow()
726
+ privacy_request.save(db=session)
727
+
728
+ # Send a final email to the user confirming request completion
729
+ if privacy_request.status == PrivacyRequestStatus.complete:
730
+ legacy_request_completion_enabled = ConfigProxy(
731
+ session
732
+ ).notifications.send_request_completion_notification
733
+
734
+ action_type = (
735
+ MessagingActionType.PRIVACY_REQUEST_COMPLETE_ACCESS
736
+ if policy.get_rules_for_action(
737
+ action_type=ActionType.access
738
+ )
739
+ else MessagingActionType.PRIVACY_REQUEST_COMPLETE_DELETION
740
+ )
741
+
742
+ if message_send_enabled(
743
+ session,
744
+ privacy_request.property_id,
745
+ action_type,
746
+ legacy_request_completion_enabled,
747
+ ) and not policy.get_rules_for_action(
748
+ action_type=ActionType.consent
749
+ ):
750
+ if not access_result_urls:
751
+ # For DSR 3.0, if the request had both access and erasure rules, this needs to be fetched
752
+ # from the database because the Privacy Request would have exited
753
+ # processing and lost access to the access_result_urls in memory
754
+ access_result_urls = (
755
+ privacy_request.access_result_urls or {}
756
+ ).get("access_result_urls", [])
757
+
758
+ try:
759
+ initiate_privacy_request_completion_email(
760
+ session,
761
+ policy,
762
+ access_result_urls,
763
+ identity_data,
764
+ privacy_request.property_id,
765
+ )
766
+ except (
767
+ IdentityNotFoundException,
768
+ MessageDispatchException,
769
+ ) as e:
770
+ privacy_request.error_processing(db=session)
771
+ # If dev mode, log traceback
772
+ _log_exception(e, CONFIG.dev_mode)
773
+ return
707
774
 
708
775
 
709
776
  def initiate_privacy_request_completion_email(
@@ -1000,3 +1067,25 @@ def run_webhooks_and_report_status(
1000
1067
  return False
1001
1068
 
1002
1069
  return True
1070
+
1071
+
1072
+ def _verify_masking_secrets(
1073
+ policy: Policy, privacy_request: PrivacyRequest, resume_step: Optional[CurrentStep]
1074
+ ) -> None:
1075
+ """
1076
+ Checks that the required masking secrets are still cached for the given request.
1077
+ Raises an exception if masking secrets are needed for the given policy but they don't exist.
1078
+ """
1079
+
1080
+ if resume_step is None:
1081
+ return
1082
+
1083
+ # if masking can be performed without any masking secrets, we skip the cache check
1084
+ if (
1085
+ policy.generate_masking_secrets()
1086
+ and not get_all_masking_secret_keys(privacy_request.id)
1087
+ and not privacy_request.masking_secrets
1088
+ ):
1089
+ raise MaskingSecretsExpired(
1090
+ f"The masking secrets for privacy request ID '{privacy_request.id}' have expired. Please submit a new erasure request."
1091
+ )
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import json
4
4
  from asyncio import sleep
5
5
  from datetime import datetime, timedelta
6
- from typing import Any, Dict, List, Optional, Set
6
+ from typing import Any, Dict, Optional, Set
7
7
 
8
8
  from httpx import AsyncClient
9
9
  from loguru import logger
@@ -11,7 +11,6 @@ from sqlalchemy import text
11
11
  from sqlalchemy.sql.elements import TextClause
12
12
 
13
13
  from fides.api.common_exceptions import PrivacyRequestNotFound
14
- from fides.api.models.policy import Policy
15
14
  from fides.api.models.privacy_request import (
16
15
  EXITED_EXECUTION_LOG_STATUSES,
17
16
  PrivacyRequest,
@@ -19,14 +18,12 @@ from fides.api.models.privacy_request import (
19
18
  )
20
19
  from fides.api.models.worker_task import ExecutionLogStatus
21
20
  from fides.api.schemas.drp_privacy_request import DrpPrivacyRequestCreate
22
- from fides.api.schemas.masking.masking_secrets import MaskingSecretCache
23
21
  from fides.api.schemas.policy import ActionType
24
22
  from fides.api.schemas.privacy_request import (
25
23
  PrivacyRequestResponse,
26
24
  PrivacyRequestStatus,
27
25
  )
28
26
  from fides.api.schemas.redis_cache import Identity
29
- from fides.api.service.masking.strategy.masking_strategy import MaskingStrategy
30
27
  from fides.api.tasks import DSR_QUEUE_NAME, DatabaseTask, celery_app
31
28
  from fides.api.tasks.scheduled.scheduler import scheduler
32
29
  from fides.api.util.cache import (
@@ -69,7 +66,6 @@ def build_required_privacy_request_kwargs(
69
66
 
70
67
  def cache_data(
71
68
  privacy_request: PrivacyRequest,
72
- policy: Policy,
73
69
  identity: Identity,
74
70
  encryption_key: Optional[str],
75
71
  drp_request_body: Optional[DrpPrivacyRequestCreate],
@@ -82,23 +78,6 @@ def cache_data(
82
78
  privacy_request.cache_custom_privacy_request_fields(custom_privacy_request_fields)
83
79
  privacy_request.cache_encryption(encryption_key) # handles None already
84
80
 
85
- # Store masking secrets in the cache
86
- logger.info("Caching masking secrets for privacy request {}", privacy_request.id)
87
- erasure_rules = policy.get_rules_for_action(action_type=ActionType.erasure)
88
- unique_masking_strategies_by_name: Set[str] = set()
89
- for rule in erasure_rules:
90
- strategy_name: str = rule.masking_strategy["strategy"] # type: ignore
91
- configuration = rule.masking_strategy["configuration"] # type: ignore
92
- if strategy_name in unique_masking_strategies_by_name:
93
- continue
94
- unique_masking_strategies_by_name.add(strategy_name)
95
- masking_strategy = MaskingStrategy.get_strategy(strategy_name, configuration)
96
- if masking_strategy.secrets_required():
97
- masking_secrets: List[MaskingSecretCache] = (
98
- masking_strategy.generate_secrets_for_cache()
99
- )
100
- for masking_secret in masking_secrets:
101
- privacy_request.cache_masking_secret(masking_secret)
102
81
  if drp_request_body:
103
82
  privacy_request.cache_drp_request_body(drp_request_body)
104
83