ethyca-fides 2.66.1rc0__py2.py3-none-any.whl → 2.66.2b0__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 (267) hide show
  1. {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b0.dist-info}/METADATA +1 -1
  2. {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b0.dist-info}/RECORD +257 -248
  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/d0031087eacb_create_manualtaskconditionaldependency_.py +106 -0
  6. fides/api/api/v1/endpoints/dataset_config_endpoints.py +13 -5
  7. fides/api/api/v1/endpoints/drp_endpoints.py +7 -1
  8. fides/api/api/v1/endpoints/user_endpoints.py +83 -7
  9. fides/api/app_setup.py +3 -2
  10. fides/api/common_exceptions.py +4 -0
  11. fides/api/db/base.py +1 -0
  12. fides/api/db/database.py +1 -1
  13. fides/api/graph/execution.py +30 -0
  14. fides/api/models/manual_task/__init__.py +2 -0
  15. fides/api/models/manual_task/conditional_dependency.py +144 -0
  16. fides/api/models/{manual_task.py → manual_task/manual_task.py} +10 -0
  17. fides/api/models/masking_secret.py +72 -0
  18. fides/api/models/policy.py +23 -0
  19. fides/api/models/privacy_request/privacy_request.py +25 -13
  20. fides/api/oauth/roles.py +2 -0
  21. fides/api/schemas/application_config.py +11 -1
  22. fides/api/schemas/masking/masking_secrets.py +1 -1
  23. fides/api/service/connectors/base_connector.py +1 -0
  24. fides/api/service/connectors/bigquery_connector.py +67 -19
  25. fides/api/service/connectors/dynamodb_connector.py +2 -1
  26. fides/api/service/connectors/fides_connector.py +1 -0
  27. fides/api/service/connectors/http_connector.py +1 -0
  28. fides/api/service/connectors/manual_task_connector.py +1 -0
  29. fides/api/service/connectors/manual_webhook_connector.py +2 -1
  30. fides/api/service/connectors/mongodb_connector.py +1 -0
  31. fides/api/service/connectors/okta_connector.py +1 -0
  32. fides/api/service/connectors/query_configs/bigquery_query_config.py +91 -32
  33. fides/api/service/connectors/rds_mysql_connector.py +1 -0
  34. fides/api/service/connectors/rds_postgres_connector.py +1 -0
  35. fides/api/service/connectors/s3_connector.py +1 -0
  36. fides/api/service/connectors/saas_connector.py +1 -0
  37. fides/api/service/connectors/scylla_connector.py +1 -0
  38. fides/api/service/connectors/sql_connector.py +36 -4
  39. fides/api/service/connectors/website_connector.py +1 -0
  40. fides/api/service/privacy_request/request_runner_service.py +26 -0
  41. fides/api/service/privacy_request/request_service.py +1 -22
  42. fides/api/task/conditional_dependencies/__init__.py +0 -0
  43. fides/api/task/conditional_dependencies/evaluator.py +109 -0
  44. fides/api/task/conditional_dependencies/schemas.py +54 -0
  45. fides/api/task/deprecated_graph_task.py +24 -6
  46. fides/api/task/execute_request_tasks.py +88 -11
  47. fides/api/task/graph_task.py +38 -3
  48. fides/api/task/manual/manual_task_graph_task.py +1 -0
  49. fides/api/util/cache.py +5 -0
  50. fides/api/util/encryption/secrets_util.py +48 -18
  51. fides/common/api/scope_registry.py +3 -0
  52. fides/config/utils.py +1 -0
  53. fides/service/privacy_request/privacy_request_service.py +6 -1
  54. fides/ui-build/static/admin/404.html +1 -1
  55. fides/ui-build/static/admin/_next/static/QGhYEMWDATmdofDzIl0_2/_buildManifest.js +1 -0
  56. fides/ui-build/static/admin/_next/static/chunks/1817-6f35f58cd08b04ae.js +1 -0
  57. fides/ui-build/static/admin/_next/static/chunks/431-ade3e312fac3430b.js +1 -0
  58. fides/ui-build/static/admin/_next/static/chunks/6780-4b687168dd8daa84.js +1 -0
  59. fides/ui-build/static/admin/_next/static/chunks/8237-55049f8f5fd5e058.js +1 -0
  60. fides/ui-build/static/admin/_next/static/chunks/pages/{_app-39ccb07327c2c5d5.js → _app-65e47e67bee20654.js} +56 -56
  61. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-71579a199158952e.js +1 -0
  62. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-7e63ac744c45f6da.js +1 -0
  63. fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-e2d5d7e2a5265e68.js → integrations-f10a7dcf7541c865.js} +1 -1
  64. fides/ui-build/static/admin/_next/static/chunks/pages/poc/{table-migration-69ad86b7a8a9a115.js → table-migration-05616e2ae20ff4f8.js} +1 -1
  65. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-f18b2b83c4592f03.js +1 -0
  66. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-1daa805a3099c6d4.js +1 -0
  67. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-fe58cebba358119d.js +1 -0
  68. fides/ui-build/static/admin/_next/static/css/{5bfb2473e5701527.css → 23cf870196941c9a.css} +1 -1
  69. fides/ui-build/static/admin/_next/static/css/b81194f2c3930152.css +1 -0
  70. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  71. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  72. fides/ui-build/static/admin/add-systems.html +1 -1
  73. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  74. fides/ui-build/static/admin/consent/configure.html +1 -1
  75. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  76. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  77. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  78. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  79. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  80. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  81. fides/ui-build/static/admin/consent/properties.html +1 -1
  82. fides/ui-build/static/admin/consent/reporting.html +1 -1
  83. fides/ui-build/static/admin/consent.html +1 -1
  84. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  85. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  86. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  87. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  88. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  89. fides/ui-build/static/admin/data-catalog.html +1 -1
  90. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  91. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  92. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  93. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  94. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  95. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  96. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  97. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  98. fides/ui-build/static/admin/datamap.html +1 -1
  99. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  100. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  101. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  102. fides/ui-build/static/admin/dataset/new.html +1 -1
  103. fides/ui-build/static/admin/dataset.html +1 -1
  104. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  105. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  106. fides/ui-build/static/admin/datastore-connection.html +1 -1
  107. fides/ui-build/static/admin/index.html +1 -1
  108. fides/ui-build/static/admin/integrations/[id].html +1 -1
  109. fides/ui-build/static/admin/integrations.html +1 -1
  110. fides/ui-build/static/admin/lib/fides-ext-gpp.js +1 -1
  111. fides/ui-build/static/admin/lib/fides-headless.js +1 -1
  112. fides/ui-build/static/admin/lib/fides-preview.js +1 -1
  113. fides/ui-build/static/admin/lib/fides-tcf.js +3 -3
  114. fides/ui-build/static/admin/lib/fides.js +3 -3
  115. fides/ui-build/static/admin/login/[provider].html +1 -1
  116. fides/ui-build/static/admin/login.html +1 -1
  117. fides/ui-build/static/admin/messaging/[id].html +1 -1
  118. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  119. fides/ui-build/static/admin/messaging.html +1 -1
  120. fides/ui-build/static/admin/poc/ant-components.html +1 -1
  121. fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
  122. fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
  123. fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
  124. fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
  125. fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
  126. fides/ui-build/static/admin/poc/forms.html +1 -1
  127. fides/ui-build/static/admin/poc/table-migration.html +1 -1
  128. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  129. fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
  130. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  131. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  132. fides/ui-build/static/admin/privacy-requests.html +1 -1
  133. fides/ui-build/static/admin/properties/[id].html +1 -1
  134. fides/ui-build/static/admin/properties/add-property.html +1 -1
  135. fides/ui-build/static/admin/properties.html +1 -1
  136. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  137. fides/ui-build/static/admin/settings/about/alpha.html +1 -1
  138. fides/ui-build/static/admin/settings/about.html +1 -1
  139. fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
  140. fides/ui-build/static/admin/settings/consent.html +1 -1
  141. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  142. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  143. fides/ui-build/static/admin/settings/domains.html +1 -1
  144. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  145. fides/ui-build/static/admin/settings/locations.html +1 -1
  146. fides/ui-build/static/admin/settings/organization.html +1 -1
  147. fides/ui-build/static/admin/settings/regulations.html +1 -1
  148. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  149. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  150. fides/ui-build/static/admin/systems.html +1 -1
  151. fides/ui-build/static/admin/taxonomy.html +1 -1
  152. fides/ui-build/static/admin/user-management/new.html +1 -1
  153. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  154. fides/ui-build/static/admin/user-management.html +1 -1
  155. fides/ui-build/static/admin/_next/static/IeU4qLtEtRJo0FsaIFIr8/_buildManifest.js +0 -1
  156. fides/ui-build/static/admin/_next/static/chunks/1817-e601e737e3cc7a0e.js +0 -1
  157. fides/ui-build/static/admin/_next/static/chunks/431-34f0b91c26f8d9ab.js +0 -1
  158. fides/ui-build/static/admin/_next/static/chunks/6780-5bd185892f6af46e.js +0 -1
  159. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-2265ecb899d45fbc.js +0 -1
  160. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-8ab33a1e0272df1f.js +0 -1
  161. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-32600543eb7b584f.js +0 -1
  162. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-cbe4c8f9096b6543.js +0 -1
  163. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-ff5738706da07801.js +0 -1
  164. fides/ui-build/static/admin/_next/static/css/2cadb5f62dcd7c2b.css +0 -1
  165. {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b0.dist-info}/WHEEL +0 -0
  166. {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b0.dist-info}/entry_points.txt +0 -0
  167. {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b0.dist-info}/licenses/LICENSE +0 -0
  168. {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b0.dist-info}/top_level.txt +0 -0
  169. /fides/ui-build/static/admin/_next/static/{IeU4qLtEtRJo0FsaIFIr8 → QGhYEMWDATmdofDzIl0_2}/_ssgManifest.js +0 -0
  170. /fides/ui-build/static/admin/_next/static/chunks/{203-5a663f465ba26bb4.js → 203-d9fe5384a6e94799.js} +0 -0
  171. /fides/ui-build/static/admin/_next/static/chunks/{2921-455e6357b74d2f76.js → 2921-86f1547ac40a5cdf.js} +0 -0
  172. /fides/ui-build/static/admin/_next/static/chunks/{3450-0ba194991d0cca88.js → 3450-f7bb8d46fbe4e78d.js} +0 -0
  173. /fides/ui-build/static/admin/_next/static/chunks/{3855-e172870d3e21b0dd.js → 3855-e2fa6db53d32c3de.js} +0 -0
  174. /fides/ui-build/static/admin/_next/static/chunks/{3872-46cebf7ec1b31a2b.js → 3872-08c855f3edb230c4.js} +0 -0
  175. /fides/ui-build/static/admin/_next/static/chunks/{3923-6cc911dafccc5f63.js → 3923-13a6b4da2d51bf8f.js} +0 -0
  176. /fides/ui-build/static/admin/_next/static/chunks/{401-1b529d5800aa1f3a.js → 401-3cc1fee61494e3bd.js} +0 -0
  177. /fides/ui-build/static/admin/_next/static/chunks/{409-a257e14acebcd73b.js → 409-45a125437261580c.js} +0 -0
  178. /fides/ui-build/static/admin/_next/static/chunks/{4121-2bc09fc4ddbfe5cb.js → 4121-f50675521dfee6eb.js} +0 -0
  179. /fides/ui-build/static/admin/_next/static/chunks/{4230-60100f7ef3ddcde1.js → 4230-840c287045c88b34.js} +0 -0
  180. /fides/ui-build/static/admin/_next/static/chunks/{4608-bbb7bf511a05c3c2.js → 4608-a8e3100e2806dbff.js} +0 -0
  181. /fides/ui-build/static/admin/_next/static/chunks/{5309-d73339f062763fed.js → 5309-67bdf9001531e972.js} +0 -0
  182. /fides/ui-build/static/admin/_next/static/chunks/{5574-b13021775a15bfd2.js → 5574-a7d8a753d273b11a.js} +0 -0
  183. /fides/ui-build/static/admin/_next/static/chunks/{6084-7178ff6ea6822475.js → 6084-a872a8bc704c980f.js} +0 -0
  184. /fides/ui-build/static/admin/_next/static/chunks/{6662-507be5d46e5b719b.js → 6662-4a7805f74af51f1d.js} +0 -0
  185. /fides/ui-build/static/admin/_next/static/chunks/{6853-2ad3e08fe6f9f5f2.js → 6853-4f8bf6558f8c6a46.js} +0 -0
  186. /fides/ui-build/static/admin/_next/static/chunks/{6882-6af16fef26c21e06.js → 6882-85c6277d9531a83a.js} +0 -0
  187. /fides/ui-build/static/admin/_next/static/chunks/{6954-bb875d9ac89f6030.js → 6954-34e062e4bffc7e71.js} +0 -0
  188. /fides/ui-build/static/admin/_next/static/chunks/{7476-281ee9a8286556f3.js → 7476-cfe41e915f0c4f40.js} +0 -0
  189. /fides/ui-build/static/admin/_next/static/chunks/{7630-9aac73191ed5ed13.js → 7630-7a8039aa37893129.js} +0 -0
  190. /fides/ui-build/static/admin/_next/static/chunks/{787-fb41002f797eb2df.js → 787-d05b25c73f49b6af.js} +0 -0
  191. /fides/ui-build/static/admin/_next/static/chunks/{79-7e87aff851423d4a.js → 79-1952e8aab93b7209.js} +0 -0
  192. /fides/ui-build/static/admin/_next/static/chunks/{796-329a5f823ec258a5.js → 796-d2876f4d09a6d81f.js} +0 -0
  193. /fides/ui-build/static/admin/_next/static/chunks/{9046-ce9567c9074563e2.js → 9046-54877976a0529de2.js} +0 -0
  194. /fides/ui-build/static/admin/_next/static/chunks/{9226-746771d47dff6266.js → 9226-7a799c930b73f217.js} +0 -0
  195. /fides/ui-build/static/admin/_next/static/chunks/{9826-111aaee8bd8dbd09.js → 9826-b7d5467e3a3225c8.js} +0 -0
  196. /fides/ui-build/static/admin/_next/static/chunks/{9951-9b753ad7c3f51bdf.js → 9951-595d0f1588215081.js} +0 -0
  197. /fides/ui-build/static/admin/_next/static/chunks/pages/{404-aece2c920ea14514.js → 404-2d803dab6a00f353.js} +0 -0
  198. /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-98777246bec9dc2a.js → manual-acb59f8b5e97512a.js} +0 -0
  199. /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-dc75dc6e37e52f05.js → multiple-8ff7f37913ad736a.js} +0 -0
  200. /fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-a71c0aff4e0e6535.js → add-systems-f28841affa791975.js} +0 -0
  201. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-24d226b5a8de5c74.js → add-vendors-d00c9034cdeb0236.js} +0 -0
  202. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-6a8ef51138ac926a.js → configure-50d21e23b607688b.js} +0 -0
  203. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-1edf582ba3cd3bbb.js → [id]-fc3a011154a2e1de.js} +0 -0
  204. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-685771e5f7196d87.js → privacy-experience-09d4408014bcfe1c.js} +0 -0
  205. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-6ccedc70dc447089.js → [id]-d67542783ef5ddac.js} +0 -0
  206. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-944bca1cc57985b5.js → new-3f20e8a316bb3d5b.js} +0 -0
  207. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-84f4bd14ce8673bc.js → privacy-notices-23e9dcd4590312d2.js} +0 -0
  208. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-6f86ab63a08a6528.js → properties-057cad65e7414a44.js} +0 -0
  209. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{reporting-afdbd4665657cfa1.js → reporting-8f891957c8944137.js} +0 -0
  210. /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-73d3cbf68f7c3a31.js → consent-509691da6b06f834.js} +0 -0
  211. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-11d52f1570759c4d.js → [resourceUrn]-99c9092d65c94807.js} +0 -0
  212. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-6ba9e160dae64695.js → [projectUrn]-80a6cc8e8573514a.js} +0 -0
  213. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-32eac8bbd217615a.js → projects-774fecea22ba8852.js} +0 -0
  214. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-b83afa5565d0c84e.js → [resourceUrn]-f6bd6aff389cb9fe.js} +0 -0
  215. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{resources-7648bbd4f6711e4d.js → resources-6c3714ee97a718c1.js} +0 -0
  216. /fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-6f630d42ac9fb6b4.js → data-catalog-2f43cfefc869b85f.js} +0 -0
  217. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{action-center-9ddb52ebb7ac4c71.js → action-center-409694b8441bd8fb.js} +0 -0
  218. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{activity-9aa744d56cdacb0d.js → activity-5af9381f02b2aff6.js} +0 -0
  219. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-393e20924c83373e.js → [resourceUrn]-31e6c54794a9883e.js} +0 -0
  220. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-8733807dad4bc96e.js → detection-2822a423a7ad0550.js} +0 -0
  221. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-14bd7500362ff224.js → [resourceUrn]-6421ce247549c5d6.js} +0 -0
  222. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-9e7dfd5a6acc2e8f.js → discovery-3eac407ac5181a3c.js} +0 -0
  223. /fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-7674b97d655c193b.js → datamap-e707dc714452498e.js} +0 -0
  224. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-c0d2bfd465df20e0.js → [...subfieldNames]-1c98bd0959d9570a.js} +0 -0
  225. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-28280a8a39a6e37c.js → [collectionName]-e548cabda7da32c9.js} +0 -0
  226. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-006b695e5af5ef24.js → [datasetId]-a8e8b5f4ee7af86c.js} +0 -0
  227. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-82fb246d87e58ebd.js → new-513c862c3a707735.js} +0 -0
  228. /fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-20165c31ab1bc7cf.js → dataset-747b7a13289f1cd7.js} +0 -0
  229. /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-b4a6bcc87d126840.js → [id]-3d22525b3c327b2e.js} +0 -0
  230. /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-f95d7b0bbfc58f5a.js → new-d2cad97495e86adb.js} +0 -0
  231. /fides/ui-build/static/admin/_next/static/chunks/pages/{datastore-connection-c391c6fad56eec48.js → datastore-connection-6865aa958f3da342.js} +0 -0
  232. /fides/ui-build/static/admin/_next/static/chunks/pages/{index-1919aab9e5834b51.js → index-f127ebaac4689d10.js} +0 -0
  233. /fides/ui-build/static/admin/_next/static/chunks/pages/integrations/{[id]-eacabe4a80cb8813.js → [id]-8d83a5518c00fcfc.js} +0 -0
  234. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-53fecfb9dd6a1e0c.js → [id]-5627d0d0668077f9.js} +0 -0
  235. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-76b01cec5fde10a9.js → add-template-feca66ad5c5fe54a.js} +0 -0
  236. /fides/ui-build/static/admin/_next/static/chunks/pages/{messaging-5094ffea13f32ed9.js → messaging-c1bd3e7adbe8d2d3.js} +0 -0
  237. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{ant-components-5c08e8447c45ce44.js → ant-components-64a322d01aae5ca7.js} +0 -0
  238. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{AntForm-06ad5f34585480aa.js → AntForm-8bca16a7726e7eb2.js} +0 -0
  239. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikAntFormItem-6f071c2bc9446cb0.js → FormikAntFormItem-b0f246fc3b67ebf7.js} +0 -0
  240. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikControlled-efcc38c58991ac9e.js → FormikControlled-1a0852b090bfc392.js} +0 -0
  241. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikField-430ba5c979abfb7c.js → FormikField-11f3de1b45e36583.js} +0 -0
  242. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{forms-5c561880bf131afb.js → forms-1b73a1c2b6c6285f.js} +0 -0
  243. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{messaging-10ce53ea356f8bad.js → messaging-f9be923340cd99cf.js} +0 -0
  244. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{storage-5501bbb129fee9c4.js → storage-eb1ccc8a5dbf0fe5.js} +0 -0
  245. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-d888a69a3bbe040e.js → configure-295bfe6880b209f2.js} +0 -0
  246. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-d3d8e3d7583ec635.js → [id]-dd99183f93763ae4.js} +0 -0
  247. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-1af10ed303815d46.js → add-property-0bdbc1fcbf553b8f.js} +0 -0
  248. /fides/ui-build/static/admin/_next/static/chunks/pages/{properties-cebc0dc186be499a.js → properties-e959378bb32b6b73.js} +0 -0
  249. /fides/ui-build/static/admin/_next/static/chunks/pages/reporting/{datamap-e130c0197362e8f3.js → datamap-2a98bd257edd8f47.js} +0 -0
  250. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/{alpha-5e1322de868d615e.js → alpha-8152e5828469cf91.js} +0 -0
  251. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-241f95e372b65d0f.js → about-9839e544924ac1f2.js} +0 -0
  252. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/{[purpose_id]-fc201657f4a782c7.js → [purpose_id]-668d74c041d74650.js} +0 -0
  253. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-c2d39cba8396ef3a.js → consent-f7a0f8367129cd70.js} +0 -0
  254. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-d992103cc55901ae.js → custom-fields-10df8d6fdc8bc149.js} +0 -0
  255. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-41242f805599feda.js → domain-records-4056a3323cdc7042.js} +0 -0
  256. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-2e885f74c92f669c.js → domains-5a3691559262e874.js} +0 -0
  257. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-ff112655ad5f41e5.js → email-templates-e1d3c3d0ab878812.js} +0 -0
  258. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-023e1895552817de.js → locations-6946e78a5d43e654.js} +0 -0
  259. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-ac403c0886b20e20.js → organization-26df33de21fb11b3.js} +0 -0
  260. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-86062a18e081a52a.js → regulations-102efd9199e87124.js} +0 -0
  261. /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-7a3396ac819c7904.js → test-datasets-170c9c55f65ffb0f.js} +0 -0
  262. /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-8314a819837f5b2a.js → [id]-d4a57ea18935dd70.js} +0 -0
  263. /fides/ui-build/static/admin/_next/static/chunks/pages/{systems-21f423a7c417aa9d.js → systems-2112c006765c66b6.js} +0 -0
  264. /fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-6387fcc8cce872eb.js → taxonomy-a53e89319582ce58.js} +0 -0
  265. /fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-a2524414e968f862.js → new-bc4eb541906781e6.js} +0 -0
  266. /fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-173ac3a1ed2b05a6.js → user-management-45bfa04e45a7d13f.js} +0 -0
  267. /fides/ui-build/static/admin/_next/static/chunks/{webpack-ff0cd6bff75588da.js → webpack-ff4c22c9f0840531.js} +0 -0
@@ -0,0 +1,72 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Union
4
+ from urllib.parse import unquote_to_bytes
5
+
6
+ from sqlalchemy import Column, ForeignKey, String
7
+ from sqlalchemy.ext.declarative import declared_attr
8
+ from sqlalchemy.orm import Session, relationship
9
+ from sqlalchemy_utils.types.encrypted.encrypted_type import (
10
+ AesGcmEngine,
11
+ StringEncryptedType,
12
+ )
13
+
14
+ from fides.api.db.base_class import Base, JSONTypeOverride
15
+ from fides.api.util.custom_json_encoder import ENCODED_BYTES_PREFIX
16
+ from fides.config import CONFIG
17
+
18
+ if TYPE_CHECKING:
19
+ from fides.api.models.privacy_request import PrivacyRequest
20
+
21
+
22
+ class MaskingSecret(Base):
23
+ """SQLAlchemy model for storing secret caching information"""
24
+
25
+ @declared_attr
26
+ def __tablename__(self) -> str:
27
+ return "masking_secret"
28
+
29
+ privacy_request_id = Column(
30
+ String, ForeignKey("privacyrequest.id", ondelete="CASCADE"), nullable=False
31
+ )
32
+ secret = Column(
33
+ StringEncryptedType(
34
+ JSONTypeOverride,
35
+ CONFIG.security.app_encryption_key,
36
+ AesGcmEngine,
37
+ "pkcs5",
38
+ ),
39
+ nullable=False,
40
+ )
41
+ masking_strategy = Column(String, nullable=False)
42
+ secret_type = Column(String, nullable=False)
43
+ privacy_request = relationship("PrivacyRequest", back_populates="masking_secrets")
44
+
45
+ def set_secret(self, secret: Union[str, bytes]) -> None:
46
+ """Set the secret value, handling both string and bytes types"""
47
+ if isinstance(secret, str):
48
+ self.secret = secret.encode("utf-8")
49
+ elif isinstance(secret, bytes):
50
+ self.secret = secret
51
+ else:
52
+ raise ValueError("Secret must be either string or bytes")
53
+
54
+ def get_secret(self) -> Union[str, bytes]:
55
+ """Retrieve the secret in its original type"""
56
+ secret = self.secret
57
+ if isinstance(secret, str) and secret.startswith(ENCODED_BYTES_PREFIX):
58
+ return unquote_to_bytes(secret)[len(ENCODED_BYTES_PREFIX) :]
59
+ return secret
60
+
61
+ @classmethod
62
+ def create(
63
+ cls,
64
+ db: Session,
65
+ *,
66
+ data: dict[str, Any],
67
+ check_name: bool = True,
68
+ ) -> "MaskingSecret":
69
+ """
70
+ Create a new masking secret. Handles both string and bytes secrets automatically, encrypting them for storage.
71
+ """
72
+ return super().create(db=db, data=data, check_name=check_name)
@@ -25,7 +25,9 @@ from fides.api.models.client import ClientDetail
25
25
  from fides.api.models.connectionconfig import ConnectionConfig
26
26
  from fides.api.models.sql_models import DataCategory # type: ignore
27
27
  from fides.api.models.storage import StorageConfig, get_active_default_storage_config
28
+ from fides.api.schemas.masking.masking_secrets import MaskingSecretCache
28
29
  from fides.api.schemas.policy import ActionType, DrpAction
30
+ from fides.api.service.masking.strategy.masking_strategy import MaskingStrategy
29
31
  from fides.api.util.data_category import _validate_data_category
30
32
  from fides.config import CONFIG
31
33
 
@@ -140,6 +142,27 @@ class Policy(Base):
140
142
  except IndexError:
141
143
  return None
142
144
 
145
+ def generate_masking_secrets(self) -> Optional[List[MaskingSecretCache]]:
146
+ """
147
+ Returns a list of masking secrets for the masking strategies in the policy.
148
+ """
149
+
150
+ masking_secrets: List[MaskingSecretCache] = []
151
+ erasure_rules = self.get_rules_for_action(action_type=ActionType.erasure)
152
+ unique_masking_strategies_by_name: Set[str] = set()
153
+ for rule in erasure_rules:
154
+ strategy_name: str = rule.masking_strategy["strategy"] # type: ignore
155
+ configuration = rule.masking_strategy["configuration"] # type: ignore
156
+ if strategy_name in unique_masking_strategies_by_name:
157
+ continue
158
+ unique_masking_strategies_by_name.add(strategy_name)
159
+ masking_strategy = MaskingStrategy.get_strategy(
160
+ strategy_name, configuration
161
+ )
162
+ if masking_strategy.secrets_required():
163
+ masking_secrets.extend(masking_strategy.generate_secrets_for_cache())
164
+ return masking_secrets
165
+
143
166
  def applies_to(self, node: "TraversalNode") -> bool:
144
167
  """
145
168
  Returns True if any data category in the traversal node starts with any of the policy's target categories.
@@ -50,6 +50,7 @@ from fides.api.models.comment import Comment, CommentReference, CommentReference
50
50
  from fides.api.models.fides_user import FidesUser
51
51
  from fides.api.models.field_types import EncryptedLargeDataDescriptor
52
52
  from fides.api.models.manual_webhook import AccessManualWebhook
53
+ from fides.api.models.masking_secret import MaskingSecret
53
54
  from fides.api.models.policy import (
54
55
  Policy,
55
56
  PolicyPreWebhook,
@@ -98,7 +99,6 @@ from fides.api.util.cache import (
98
99
  get_drp_request_body_cache_key,
99
100
  get_encryption_cache_key,
100
101
  get_identity_cache_key,
101
- get_masking_secret_cache_key,
102
102
  )
103
103
  from fides.api.util.collection_util import Row, extract_key_for_address
104
104
  from fides.api.util.constants import API_DATE_FORMAT
@@ -289,6 +289,13 @@ class PrivacyRequest(
289
289
  order_by="RequestTask.created_at",
290
290
  )
291
291
 
292
+ masking_secrets: "RelationshipProperty[List[MaskingSecret]]" = relationship(
293
+ "MaskingSecret",
294
+ back_populates="privacy_request",
295
+ uselist=True,
296
+ passive_deletes="all",
297
+ )
298
+
292
299
  @property
293
300
  def days_left(self: PrivacyRequest) -> Union[int, None]:
294
301
  if self.due_date is None:
@@ -566,19 +573,24 @@ class PrivacyRequest(
566
573
  encryption_key,
567
574
  )
568
575
 
569
- def cache_masking_secret(self, masking_secret: MaskingSecretCache) -> None:
570
- """Sets masking encryption secrets in the Fides app cache if provided"""
571
- if not masking_secret:
576
+ def persist_masking_secrets(
577
+ self, masking_secrets: List[MaskingSecretCache]
578
+ ) -> None:
579
+ """Persists masking encryption secrets to database."""
580
+ if not masking_secrets:
572
581
  return
573
- cache: FidesopsRedis = get_cache()
574
- cache.set_with_autoexpire(
575
- get_masking_secret_cache_key(
576
- self.id,
577
- masking_strategy=masking_secret.masking_strategy,
578
- secret_type=masking_secret.secret_type,
579
- ),
580
- FidesopsRedis.encode_obj(masking_secret.secret),
581
- )
582
+
583
+ session = Session.object_session(self)
584
+ for masking_secret in masking_secrets:
585
+ MaskingSecret.create(
586
+ db=session,
587
+ data={
588
+ "privacy_request_id": self.id,
589
+ "secret": masking_secret.secret,
590
+ "masking_strategy": masking_secret.masking_strategy,
591
+ "secret_type": masking_secret.secret_type,
592
+ },
593
+ )
582
594
 
583
595
  def get_cached_identity_data(self) -> Dict[str, Any]:
584
596
  """Retrieves any identity data pertaining to this request from the cache"""
fides/api/oauth/roles.py CHANGED
@@ -47,6 +47,7 @@ from fides.common.api.scope_registry import (
47
47
  SYSTEM_READ,
48
48
  USER_PERMISSION_ASSIGN_OWNERS,
49
49
  USER_READ,
50
+ USER_READ_OWN,
50
51
  WEBHOOK_READ,
51
52
  )
52
53
 
@@ -126,6 +127,7 @@ viewer_scopes = [ # Intentionally omitted USER_PERMISSION_READ and PRIVACY_REQU
126
127
 
127
128
  respondent_scopes = [
128
129
  PRIVACY_REQUEST_MANUAL_STEPS_RESPOND, # allows respondents to respond to assigned manual steps
130
+ USER_READ_OWN,
129
131
  ]
130
132
 
131
133
  external_respondent_scopes = [
@@ -11,6 +11,14 @@ from fides.api.schemas.messaging.messaging import MessagingServiceType
11
11
  from fides.config.admin_ui_settings import ErrorNotificationMode
12
12
 
13
13
 
14
+ class SqlDryRunMode(str, Enum):
15
+ """SQL dry run mode for controlling execution of SQL statements in privacy requests"""
16
+
17
+ none = "none"
18
+ access = "access"
19
+ erasure = "erasure"
20
+
21
+
14
22
  class StorageTypeApiAccepted(Enum):
15
23
  """Enum for storage destination types accepted in API updates"""
16
24
 
@@ -62,7 +70,9 @@ class ExecutionApplicationConfig(FidesSchema):
62
70
  subject_identity_verification_required: Optional[bool] = None
63
71
  disable_consent_identity_verification: Optional[bool] = None
64
72
  require_manual_request_approval: Optional[bool] = None
65
- model_config = ConfigDict(extra="forbid")
73
+ sql_dry_run: Optional[SqlDryRunMode] = None
74
+
75
+ model_config = ConfigDict(use_enum_values=True, extra="forbid")
66
76
 
67
77
 
68
78
  class AdminUIConfig(FidesSchema):
@@ -5,7 +5,7 @@ from typing import Callable, Generic, TypeVar
5
5
  T = TypeVar("T")
6
6
 
7
7
 
8
- class SecretType(Enum):
8
+ class SecretType(str, Enum):
9
9
  """Enum that holds all possible types of secrets across all masking strategies"""
10
10
 
11
11
  key = "key"
@@ -82,6 +82,7 @@ class BaseConnector(Generic[DB_CONNECTOR_TYPE], ABC):
82
82
  privacy_request: PrivacyRequest,
83
83
  request_task: RequestTask,
84
84
  rows: List[Row],
85
+ input_data: Optional[Dict[str, List[Any]]] = None,
85
86
  ) -> int:
86
87
  """Execute a masking request. Return the number of rows that have been updated
87
88
 
@@ -1,13 +1,8 @@
1
- from typing import List, Optional
1
+ from typing import Any, Dict, List, Optional
2
2
 
3
3
  from loguru import logger
4
4
  from sqlalchemy import text
5
- from sqlalchemy.engine import ( # type: ignore
6
- Connection,
7
- Engine,
8
- LegacyCursorResult,
9
- create_engine,
10
- )
5
+ from sqlalchemy.engine import Connection, Engine, create_engine # type: ignore
11
6
  from sqlalchemy.orm import Session
12
7
  from sqlalchemy.sql import Executable # type: ignore
13
8
  from sqlalchemy.sql.elements import TextClause
@@ -17,6 +12,7 @@ from fides.api.graph.execution import ExecutionNode
17
12
  from fides.api.models.connectionconfig import ConnectionTestStatus
18
13
  from fides.api.models.policy import Policy
19
14
  from fides.api.models.privacy_request import PrivacyRequest, RequestTask
15
+ from fides.api.schemas.application_config import SqlDryRunMode
20
16
  from fides.api.schemas.connection_configuration.connection_secrets_bigquery import (
21
17
  BigQuerySchema,
22
18
  )
@@ -99,6 +95,16 @@ class BigQueryConnector(SQLConnector):
99
95
  logger.info(
100
96
  f"Executing {len(partition_clauses)} partition queries for node '{query_config.node.address}' in DSR execution"
101
97
  )
98
+
99
+ if self.should_dry_run(SqlDryRunMode.access):
100
+ for partition_clause in partition_clauses:
101
+ existing_bind_params = stmt.compile().params
102
+ partitioned_stmt = text(
103
+ f"{stmt} AND ({text(partition_clause)})"
104
+ ).params(existing_bind_params)
105
+ logger.warning(f"SQL DRY RUN - Would execute SQL: {partitioned_stmt}")
106
+ return []
107
+
102
108
  rows = []
103
109
  for partition_clause in partition_clauses:
104
110
  logger.debug(
@@ -135,6 +141,39 @@ class BigQueryConnector(SQLConnector):
135
141
  logger.exception(f"Error testing connection to remote BigQuery {str(e)}")
136
142
  raise ConnectionException(f"Connection error: {e}")
137
143
 
144
+ def _execute_statements_with_sql_dry_run(
145
+ self,
146
+ statements: List[Executable],
147
+ sql_dry_run_enabled: bool,
148
+ client: Engine,
149
+ ) -> int:
150
+ """
151
+ Execute SQL statements with sql_dry_run support.
152
+
153
+ Args:
154
+ statements: List of SQL statements to execute
155
+ sql_dry_run_enabled: Whether sql_dry_run mode is enabled
156
+ client: Database engine
157
+
158
+ Returns:
159
+ int: Number of affected rows (0 in sql_dry_run mode)
160
+ """
161
+ if not statements:
162
+ return 0
163
+
164
+ if sql_dry_run_enabled:
165
+ for stmt in statements:
166
+ logger.warning(f"SQL DRY RUN - Would execute SQL: {stmt}")
167
+ return 0
168
+
169
+ row_count = 0
170
+ with client.connect() as connection:
171
+ for stmt in statements:
172
+ results = connection.execute(stmt)
173
+ logger.debug(f"Affected {results.rowcount} rows")
174
+ row_count += results.rowcount
175
+ return row_count
176
+
138
177
  def mask_data(
139
178
  self,
140
179
  node: ExecutionNode,
@@ -142,22 +181,31 @@ class BigQueryConnector(SQLConnector):
142
181
  privacy_request: PrivacyRequest,
143
182
  request_task: RequestTask,
144
183
  rows: List[Row],
184
+ input_data: Optional[Dict[str, List[Any]]] = None,
145
185
  ) -> int:
146
186
  """Execute a masking request. Returns the number of records updated or deleted"""
187
+
147
188
  query_config = self.query_config(node)
148
189
  update_or_delete_ct = 0
149
190
  client = self.client()
150
- for row in rows:
151
- update_or_delete_stmts: List[Executable] = (
152
- query_config.generate_masking_stmt(
153
- node, row, policy, privacy_request, client
154
- )
191
+
192
+ if query_config.uses_delete_masking_strategy():
193
+ delete_stmts = query_config.generate_delete(client, input_data or {})
194
+ logger.debug(f"Generated {len(delete_stmts)} DELETE statements")
195
+ update_or_delete_ct += self._execute_statements_with_sql_dry_run(
196
+ delete_stmts, self.should_dry_run(SqlDryRunMode.erasure), client
155
197
  )
156
- if update_or_delete_stmts:
157
- with client.connect() as connection:
158
- for update_or_delete_stmt in update_or_delete_stmts:
159
- results: LegacyCursorResult = connection.execute(
160
- update_or_delete_stmt
161
- )
162
- update_or_delete_ct = update_or_delete_ct + results.rowcount
198
+ else:
199
+ for row in rows:
200
+ update_or_delete_stmts: List[Executable] = query_config.generate_update(
201
+ row, policy, privacy_request, client
202
+ )
203
+ logger.debug(
204
+ f"Generated {len(update_or_delete_stmts)} UPDATE statements"
205
+ )
206
+ update_or_delete_ct += self._execute_statements_with_sql_dry_run(
207
+ update_or_delete_stmts,
208
+ self.should_dry_run(SqlDryRunMode.erasure),
209
+ client,
210
+ )
163
211
  return update_or_delete_ct
@@ -140,8 +140,9 @@ class DynamoDBConnector(BaseConnector[Any]): # type: ignore
140
140
  privacy_request: PrivacyRequest,
141
141
  request_task: RequestTask,
142
142
  rows: List[Row],
143
+ input_data: Optional[Dict[str, List[Any]]] = None,
143
144
  ) -> int:
144
- """Execute a masking requestfor DynamoDB"""
145
+ """Execute a masking request for DynamoDB"""
145
146
 
146
147
  query_config = self.query_config(node)
147
148
  collection_name = node.address.collection
@@ -142,6 +142,7 @@ class FidesConnector(BaseConnector[FidesClient]):
142
142
  privacy_request: PrivacyRequest,
143
143
  request_task: RequestTask,
144
144
  rows: List[Row],
145
+ input_data: Optional[Dict[str, List[Any]]] = None,
145
146
  ) -> int:
146
147
  """Execute an erasure request on remote fides"""
147
148
  identity_data = {
@@ -90,6 +90,7 @@ class HTTPSConnector(BaseConnector[None]):
90
90
  privacy_request: PrivacyRequest,
91
91
  request_task: RequestTask,
92
92
  rows: List[Row],
93
+ input_data: Optional[Dict[str, List[Any]]] = None,
93
94
  ) -> int:
94
95
  """Currently not supported as webhooks are not called at the collection level"""
95
96
  raise NotImplementedError(
@@ -80,6 +80,7 @@ class ManualTaskConnector(BaseConnector):
80
80
  privacy_request: PrivacyRequest,
81
81
  request_task: RequestTask,
82
82
  rows: List[Row],
83
+ input_data: Optional[Dict[str, List[Any]]] = None,
83
84
  ) -> int:
84
85
  """
85
86
  Manual tasks don't support erasure operations.
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict, List
1
+ from typing import Any, Dict, List, Optional
2
2
 
3
3
  from fides.api.graph.execution import ExecutionNode
4
4
  from fides.api.models.connectionconfig import ConnectionConfig, ConnectionTestStatus
@@ -53,6 +53,7 @@ class ManualWebhookConnector(BaseConnector[None]):
53
53
  privacy_request: PrivacyRequest,
54
54
  request_task: RequestTask,
55
55
  rows: List[Row],
56
+ input_data: Optional[List[List[Row]]] = None,
56
57
  ) -> None:
57
58
  """
58
59
  Not applicable for a manual webhook. Manual webhooks are not called as part of the traversal.
@@ -126,6 +126,7 @@ class MongoDBConnector(BaseConnector[MongoClient]):
126
126
  privacy_request: PrivacyRequest,
127
127
  request_task: RequestTask,
128
128
  rows: List[Row],
129
+ input_data: Optional[Dict[str, List[Any]]] = None,
129
130
  ) -> int:
130
131
  """Execute a masking request"""
131
132
  query_config = self.query_config(node)
@@ -82,6 +82,7 @@ class OktaConnector(BaseConnector):
82
82
  privacy_request: PrivacyRequest,
83
83
  request_task: RequestTask,
84
84
  rows: List[Row],
85
+ input_data: Optional[Dict[str, List[Any]]] = None,
85
86
  ) -> int:
86
87
  """DSR execution not supported for Okta connector"""
87
88
  return 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