ethyca-fides 2.62.1rc0__py2.py3-none-any.whl → 2.63.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. {ethyca_fides-2.62.1rc0.dist-info → ethyca_fides-2.63.0.dist-info}/METADATA +2 -1
  2. {ethyca_fides-2.62.1rc0.dist-info → ethyca_fides-2.63.0.dist-info}/RECORD +250 -238
  3. fides/_version.py +3 -3
  4. fides/api/alembic/migrations/versions/2263583b0e44_add_shared_monitor_config.py +82 -0
  5. fides/api/alembic/migrations/versions/440a5b9a3493_add_fides_user_respondent_email_.py +80 -0
  6. fides/api/alembic/migrations/versions/5474a47c77da_create_staged_resource_ancestor_link_table.py +72 -0
  7. fides/api/alembic/migrations/versions/bf713b5a021d_staged_resource_ancestor_link_data_.py +259 -0
  8. fides/api/alembic/migrations/versions/c586a56c25e7_remove_child_diff_statuses.py +40 -0
  9. fides/api/api/v1/endpoints/privacy_request_endpoints.py +2 -2
  10. fides/api/db/base.py +3 -0
  11. fides/api/main.py +4 -1
  12. fides/api/migrations/post_upgrade_index_creation.py +269 -0
  13. fides/api/models/attachment.py +117 -10
  14. fides/api/models/audit_log.py +0 -3
  15. fides/api/models/detection_discovery.py +189 -24
  16. fides/api/models/fides_user.py +51 -4
  17. fides/api/models/fides_user_permissions.py +25 -7
  18. fides/api/models/fides_user_respondent_email_verification.py +112 -0
  19. fides/api/models/privacy_request/privacy_request.py +40 -1
  20. fides/api/oauth/roles.py +19 -0
  21. fides/api/service/connectors/query_configs/saas_query_config.py +3 -8
  22. fides/api/service/connectors/saas_connector.py +7 -4
  23. fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +4 -2
  24. fides/api/service/privacy_request/request_runner_service.py +47 -18
  25. fides/api/service/privacy_request/request_service.py +4 -15
  26. fides/api/service/storage/s3.py +40 -15
  27. fides/api/tasks/storage.py +6 -5
  28. fides/api/util/lock.py +44 -0
  29. fides/api/util/storage_util.py +14 -8
  30. fides/common/api/scope_registry.py +6 -0
  31. fides/config/config_proxy.py +0 -1
  32. fides/config/execution_settings.py +0 -4
  33. fides/ui-build/static/admin/404.html +1 -1
  34. fides/ui-build/static/admin/_next/static/chunks/1099-667e84655846e78c.js +1 -0
  35. fides/ui-build/static/admin/_next/static/chunks/1100-0b01f500b1eaf60d.js +1 -0
  36. fides/ui-build/static/admin/_next/static/chunks/{1817-721fdeb29c07c6be.js → 1817-951f58ddd5d46081.js} +1 -1
  37. fides/ui-build/static/admin/_next/static/chunks/{1904-7e5354990377a849.js → 1904-361e37a5811ed7fc.js} +1 -1
  38. fides/ui-build/static/admin/_next/static/chunks/2310-744354001d01f366.js +1 -0
  39. fides/ui-build/static/admin/_next/static/chunks/2921-49af55ad7c631c93.js +1 -0
  40. fides/ui-build/static/admin/_next/static/chunks/3119-e36ae5071c8f27a5.js +1 -0
  41. fides/ui-build/static/admin/_next/static/chunks/3505-113f9c95d34b7aae.js +1 -0
  42. fides/ui-build/static/admin/_next/static/chunks/3513-5c2d412a84a78971.js +1 -0
  43. fides/ui-build/static/admin/_next/static/chunks/3662-f6a1ddca5ee42076.js +1 -0
  44. fides/ui-build/static/admin/_next/static/chunks/3670-2abd9b2f17770872.js +1 -0
  45. fides/ui-build/static/admin/_next/static/chunks/3872-45cc725e241211f0.js +1 -0
  46. fides/ui-build/static/admin/_next/static/chunks/3923-b4f701ada3ef0ee0.js +1 -0
  47. fides/ui-build/static/admin/_next/static/chunks/3983-17ae9c232bddc413.js +1 -0
  48. fides/ui-build/static/admin/_next/static/chunks/401-7e800aed05537126.js +1 -0
  49. fides/ui-build/static/admin/_next/static/chunks/4060-90a52a9afc655bfc.js +1 -0
  50. fides/ui-build/static/admin/_next/static/chunks/4121-66b0e00d5e7ae817.js +1 -0
  51. fides/ui-build/static/admin/_next/static/chunks/4132-9b1731bfec6ee537.js +1 -0
  52. fides/ui-build/static/admin/_next/static/chunks/4481-7f6710c928bb0cb0.js +1 -0
  53. fides/ui-build/static/admin/_next/static/chunks/5258-2079138c8cc34f6c.js +1 -0
  54. fides/ui-build/static/admin/_next/static/chunks/5404-2694509cf5fa96da.js +1 -0
  55. fides/ui-build/static/admin/_next/static/chunks/5487-2cbd8d2169eb2a65.js +1 -0
  56. fides/ui-build/static/admin/_next/static/chunks/5683-37137111b3e769fb.js +1 -0
  57. fides/ui-build/static/admin/_next/static/chunks/6202-081545c7822d09af.js +1 -0
  58. fides/ui-build/static/admin/_next/static/chunks/6277-459e054b2702c60e.js +1 -0
  59. fides/ui-build/static/admin/_next/static/chunks/641-7e4eef3222cbda70.js +1 -0
  60. fides/ui-build/static/admin/_next/static/chunks/6659-b2088f525bf13c17.js +1 -0
  61. fides/ui-build/static/admin/_next/static/chunks/{6853-0cee00c9da26a40e.js → 6853-688df0b88fe65fd5.js} +1 -1
  62. fides/ui-build/static/admin/_next/static/chunks/69-00cba94689b9e740.js +1 -0
  63. fides/ui-build/static/admin/_next/static/chunks/6954-4e313b4599b8763d.js +1 -0
  64. fides/ui-build/static/admin/_next/static/chunks/7553-08bc3d9f66695111.js +1 -0
  65. fides/ui-build/static/admin/_next/static/chunks/79-fc548561beed136f.js +1 -0
  66. fides/ui-build/static/admin/_next/static/chunks/796-d6586308ed44334b.js +1 -0
  67. fides/ui-build/static/admin/_next/static/chunks/7980-736f9fd1e749ddd2.js +1 -0
  68. fides/ui-build/static/admin/_next/static/chunks/8032-74d94f7bd5b81c89.js +1 -0
  69. fides/ui-build/static/admin/_next/static/chunks/8433-51870336908fb253.js +1 -0
  70. fides/ui-build/static/admin/_next/static/chunks/8499-1fd392d0be9e8ee4.js +1 -0
  71. fides/ui-build/static/admin/_next/static/chunks/9033-fdd87182c15b91e2.js +1 -0
  72. fides/ui-build/static/admin/_next/static/chunks/9327-6a53461c9764b16f.js +1 -0
  73. fides/ui-build/static/admin/_next/static/chunks/{9392.bfca299d4f39a9e0.js → 9392.81edc11e3a175275.js} +1 -1
  74. fides/ui-build/static/admin/_next/static/chunks/9494-1ffb888b95289891.js +1 -0
  75. fides/ui-build/static/admin/_next/static/chunks/9676.61cf5f6d6a083175.js +1 -0
  76. fides/ui-build/static/admin/_next/static/chunks/9767-afd674014a1d1152.js +1 -0
  77. fides/ui-build/static/admin/_next/static/chunks/pages/404-ec04f826f3dd79ea.js +1 -0
  78. fides/ui-build/static/admin/_next/static/chunks/pages/{_app-371b155655c83a1e.js → _app-33bc23b79bffe07c.js} +69 -69
  79. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/manual-1bec086d567aacff.js +1 -0
  80. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/multiple-f68bf100e4e60e88.js +1 -0
  81. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-effde5cfe49de55f.js +1 -0
  82. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/add-vendors-ec95ed8cb0712828.js +1 -0
  83. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-4cc195e15e0c3cc8.js +1 -0
  84. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-be8972c79fb39705.js +1 -0
  85. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{new-7f38473ce267348a.js → new-a0039f216fb3eb93.js} +1 -1
  86. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-ccf96207de3b74ea.js +1 -0
  87. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/[id]-b9823cc008372cee.js +1 -0
  88. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/new-373f5772ab41bc93.js +1 -0
  89. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-b9beeee1bde5ca79.js +1 -0
  90. fides/ui-build/static/admin/_next/static/chunks/pages/consent/properties-6ab3a5baafebd199.js +1 -0
  91. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-8562790ae07f17c9.js +1 -0
  92. fides/ui-build/static/admin/_next/static/chunks/pages/consent-42d4d2d6e0253671.js +1 -0
  93. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-7ddf45ffd49533b0.js → [resourceUrn]-18e3faf7963962e4.js} +1 -1
  94. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]-9d1df6d890671b0c.js +1 -0
  95. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-7eb08af333baaa8e.js +1 -0
  96. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-ed213bc1b7041aa2.js → [resourceUrn]-7320524a47104798.js} +1 -1
  97. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{resources-1f033ca5a0702018.js → resources-51d99174c8006eb5.js} +1 -1
  98. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-bb57eed84f59a932.js +1 -0
  99. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-47eb34aef3e31ac6.js +1 -0
  100. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-76cefefc53e84c7f.js +1 -0
  101. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-9d8e84a8b4bf568d.js +1 -0
  102. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-c3eaadd88a2e6caa.js +1 -0
  103. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-f38b3ef885a29ea6.js → [resourceUrn]-4c526db0c30c488a.js} +1 -1
  104. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-29fd0d5a73f13a00.js → detection-6f27dbb7c8edc69d.js} +1 -1
  105. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-9a80241f1028c470.js → [resourceUrn]-562d2b8ae90dd1f0.js} +1 -1
  106. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-c58333981b1522c9.js → discovery-fe7f51502eda57c9.js} +1 -1
  107. fides/ui-build/static/admin/_next/static/chunks/pages/datamap-db45aa864e9dda8a.js +1 -0
  108. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/[...subfieldNames]-d5cfdebc74654337.js +1 -0
  109. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]-618b89228f83435e.js +1 -0
  110. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-12fc86f15a4c764e.js +1 -0
  111. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-c23d18a9b56c2e3c.js +1 -0
  112. fides/ui-build/static/admin/_next/static/chunks/pages/dataset-e3b2d8e3980ab093.js +1 -0
  113. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/[id]-6e27257eccf65f01.js +1 -0
  114. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-bbac1f624424282d.js +1 -0
  115. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-84eb56f5a07bdf4f.js +1 -0
  116. fides/ui-build/static/admin/_next/static/chunks/pages/index-773182b5e35d0045.js +1 -0
  117. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-eb28c927da5bd653.js +1 -0
  118. fides/ui-build/static/admin/_next/static/chunks/pages/integrations-781808bca01f8048.js +1 -0
  119. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/[id]-b51bee8680d66b20.js +1 -0
  120. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/add-template-1de479533f733fbd.js +1 -0
  121. fides/ui-build/static/admin/_next/static/chunks/pages/messaging-7dc295b3ed3d2224.js +1 -0
  122. fides/ui-build/static/admin/_next/static/chunks/pages/poc/ant-components-f64dac3392f5ded7.js +1 -0
  123. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/AntForm-eb020bfac4bee532.js +1 -0
  124. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikAntFormItem-0bd62e28b539e114.js +1 -0
  125. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikControlled-936877004113c2a6.js +1 -0
  126. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikField-6416f01524482af9.js +1 -0
  127. fides/ui-build/static/admin/_next/static/chunks/pages/poc/forms-3f4e1313d1f2969b.js +1 -0
  128. fides/ui-build/static/admin/_next/static/chunks/pages/poc/table-migration-1db7a54437db7db0.js +1 -0
  129. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-64236fd0141414fd.js +1 -0
  130. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-8cab04871908cfeb.js +1 -0
  131. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-150d40428245ee0c.js +1 -0
  132. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure-b6c876dceb16ad1b.js +1 -0
  133. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-20cdb2c8a03deae1.js +1 -0
  134. fides/ui-build/static/admin/_next/static/chunks/pages/properties/[id]-54bcc875592d1fb9.js +1 -0
  135. fides/ui-build/static/admin/_next/static/chunks/pages/properties/add-property-644dc669b508a79a.js +1 -0
  136. fides/ui-build/static/admin/_next/static/chunks/pages/properties-9a88220d03e7e02f.js +1 -0
  137. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-70dc1081df37ea69.js +1 -0
  138. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/alpha-3c71b4dbcb6fd6c9.js +1 -0
  139. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about-7cb16e0000dd16c9.js +1 -0
  140. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/[purpose_id]-a18d8f12ee8dcf5d.js +1 -0
  141. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-dc220e116ad5c09e.js +1 -0
  142. fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-55ddc5f25b86f28e.js +1 -0
  143. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domain-records-5ca7decded228bc8.js +1 -0
  144. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-a6fafa1be2834c40.js +1 -0
  145. fides/ui-build/static/admin/_next/static/chunks/pages/settings/email-templates-e633750d2b45ddd6.js +1 -0
  146. fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-4fbab6716326060d.js → locations-3948686cbd372969.js} +1 -1
  147. fides/ui-build/static/admin/_next/static/chunks/pages/settings/organization-04ece499c22d23f4.js +1 -0
  148. fides/ui-build/static/admin/_next/static/chunks/pages/settings/regulations-7752305084280cca.js +1 -0
  149. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/test-datasets-9e5f9066be6f218d.js +1 -0
  150. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]-86de9e6d72098e45.js +1 -0
  151. fides/ui-build/static/admin/_next/static/chunks/pages/systems-cac52e6c3abf6a15.js +1 -0
  152. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-60d5930b6855679b.js +1 -0
  153. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-b0f4c34752d32c73.js +1 -0
  154. fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-c0b2113b44f46112.js → user-management-7dee83dd6d9292aa.js} +1 -1
  155. fides/ui-build/static/admin/_next/static/chunks/{webpack-abd3efcb23c0bb03.js → webpack-8457bad7859c44f0.js} +1 -1
  156. fides/ui-build/static/admin/_next/static/css/1fdf5c593349dbc6.css +1 -0
  157. fides/ui-build/static/admin/_next/static/css/5ded47c57dae5baf.css +1 -0
  158. fides/ui-build/static/admin/_next/static/css/e458b5f1afdbb7fc.css +1 -0
  159. fides/ui-build/static/admin/_next/static/lb6RRZ8JumokYwsENumlw/_buildManifest.js +1 -0
  160. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  161. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  162. fides/ui-build/static/admin/add-systems.html +1 -1
  163. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  164. fides/ui-build/static/admin/consent/configure.html +1 -1
  165. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  166. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  167. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  168. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  169. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  170. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  171. fides/ui-build/static/admin/consent/properties.html +1 -1
  172. fides/ui-build/static/admin/consent/reporting.html +1 -1
  173. fides/ui-build/static/admin/consent.html +1 -1
  174. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  175. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  176. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  177. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  178. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  179. fides/ui-build/static/admin/data-catalog.html +1 -1
  180. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  181. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  182. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  183. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  184. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  185. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  186. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  187. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  188. fides/ui-build/static/admin/datamap.html +1 -1
  189. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  190. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  191. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  192. fides/ui-build/static/admin/dataset/new.html +1 -1
  193. fides/ui-build/static/admin/dataset.html +1 -1
  194. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  195. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  196. fides/ui-build/static/admin/datastore-connection.html +1 -1
  197. fides/ui-build/static/admin/index.html +1 -1
  198. fides/ui-build/static/admin/integrations/[id].html +1 -1
  199. fides/ui-build/static/admin/integrations.html +1 -1
  200. fides/ui-build/static/admin/lib/fides-ext-gpp.js +1 -1
  201. fides/ui-build/static/admin/lib/fides-headless.js +1 -1
  202. fides/ui-build/static/admin/lib/fides-preview.js +1 -1
  203. fides/ui-build/static/admin/lib/fides-tcf.js +2 -2
  204. fides/ui-build/static/admin/lib/fides.js +2 -2
  205. fides/ui-build/static/admin/login/[provider].html +1 -1
  206. fides/ui-build/static/admin/login.html +1 -1
  207. fides/ui-build/static/admin/messaging/[id].html +1 -1
  208. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  209. fides/ui-build/static/admin/messaging.html +1 -1
  210. fides/ui-build/static/admin/poc/ant-components.html +1 -1
  211. fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
  212. fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
  213. fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
  214. fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
  215. fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
  216. fides/ui-build/static/admin/poc/forms.html +1 -1
  217. fides/ui-build/static/admin/poc/table-migration.html +1 -0
  218. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  219. fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
  220. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  221. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  222. fides/ui-build/static/admin/privacy-requests.html +1 -1
  223. fides/ui-build/static/admin/properties/[id].html +1 -1
  224. fides/ui-build/static/admin/properties/add-property.html +1 -1
  225. fides/ui-build/static/admin/properties.html +1 -1
  226. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  227. fides/ui-build/static/admin/settings/about/alpha.html +1 -0
  228. fides/ui-build/static/admin/settings/about.html +1 -1
  229. fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
  230. fides/ui-build/static/admin/settings/consent.html +1 -1
  231. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  232. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  233. fides/ui-build/static/admin/settings/domains.html +1 -1
  234. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  235. fides/ui-build/static/admin/settings/locations.html +1 -1
  236. fides/ui-build/static/admin/settings/organization.html +1 -1
  237. fides/ui-build/static/admin/settings/regulations.html +1 -1
  238. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  239. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  240. fides/ui-build/static/admin/systems.html +1 -1
  241. fides/ui-build/static/admin/taxonomy.html +1 -1
  242. fides/ui-build/static/admin/user-management/new.html +1 -1
  243. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  244. fides/ui-build/static/admin/user-management.html +1 -1
  245. fides/service/error_handling/__init__.py +0 -0
  246. fides/service/error_handling/error_handler.py +0 -202
  247. fides/ui-build/static/admin/_next/static/K1_4-1GhwfL7h4fYYQBvB/_buildManifest.js +0 -1
  248. fides/ui-build/static/admin/_next/static/chunks/1099-98458e8e9ff67508.js +0 -1
  249. fides/ui-build/static/admin/_next/static/chunks/1100-4106d99f5e1ebd75.js +0 -1
  250. fides/ui-build/static/admin/_next/static/chunks/1327-92a38135ee8d55ec.js +0 -1
  251. fides/ui-build/static/admin/_next/static/chunks/2310-0d63c66c2685e83c.js +0 -1
  252. fides/ui-build/static/admin/_next/static/chunks/2921-ca04a0476e2e56c5.js +0 -1
  253. fides/ui-build/static/admin/_next/static/chunks/3119-e441b14cdab4320e.js +0 -1
  254. fides/ui-build/static/admin/_next/static/chunks/3426-e49cb4766324b2dd.js +0 -1
  255. fides/ui-build/static/admin/_next/static/chunks/3505-e58b93f9c1cbdcad.js +0 -1
  256. fides/ui-build/static/admin/_next/static/chunks/3513-87b54d931698986f.js +0 -1
  257. fides/ui-build/static/admin/_next/static/chunks/355-e4340980d72b4faa.js +0 -1
  258. fides/ui-build/static/admin/_next/static/chunks/3615-5e2d062d684b8fa1.js +0 -1
  259. fides/ui-build/static/admin/_next/static/chunks/3872-6b828cb00fb2ce93.js +0 -1
  260. fides/ui-build/static/admin/_next/static/chunks/4060-9593a3f98e600487.js +0 -1
  261. fides/ui-build/static/admin/_next/static/chunks/4121-5c3af879cdd8b3be.js +0 -1
  262. fides/ui-build/static/admin/_next/static/chunks/4294-5b3aa9d605217915.js +0 -1
  263. fides/ui-build/static/admin/_next/static/chunks/4481-8398edf74aabb8ea.js +0 -1
  264. fides/ui-build/static/admin/_next/static/chunks/4723-4c3c7a375e998c15.js +0 -1
  265. fides/ui-build/static/admin/_next/static/chunks/5258-e000d07844eccf7e.js +0 -1
  266. fides/ui-build/static/admin/_next/static/chunks/5487-62955c11bab63734.js +0 -1
  267. fides/ui-build/static/admin/_next/static/chunks/5626-1b636798faad78e1.js +0 -1
  268. fides/ui-build/static/admin/_next/static/chunks/5683-44966a05fa5ad566.js +0 -1
  269. fides/ui-build/static/admin/_next/static/chunks/570-c99f07161bd339cd.js +0 -1
  270. fides/ui-build/static/admin/_next/static/chunks/5826-ce34f25ac2eeb807.js +0 -1
  271. fides/ui-build/static/admin/_next/static/chunks/5973-56d90405c3616068.js +0 -1
  272. fides/ui-build/static/admin/_next/static/chunks/6277-c35c8cc806f7571f.js +0 -1
  273. fides/ui-build/static/admin/_next/static/chunks/6834-dcda0a8ed484775e.js +0 -1
  274. fides/ui-build/static/admin/_next/static/chunks/69-dc52e39d26a4ad14.js +0 -1
  275. fides/ui-build/static/admin/_next/static/chunks/6954-d02d474ba30756b2.js +0 -1
  276. fides/ui-build/static/admin/_next/static/chunks/79-e5884e6878fc1bbb.js +0 -1
  277. fides/ui-build/static/admin/_next/static/chunks/7980-e17dda2f50ec76b8.js +0 -1
  278. fides/ui-build/static/admin/_next/static/chunks/8433-a77c2cd1c1a6887e.js +0 -1
  279. fides/ui-build/static/admin/_next/static/chunks/8499-34a34015c91fc38b.js +0 -1
  280. fides/ui-build/static/admin/_next/static/chunks/9014-eeae6f581158e645.js +0 -1
  281. fides/ui-build/static/admin/_next/static/chunks/9327-a25347d72bfbe680.js +0 -1
  282. fides/ui-build/static/admin/_next/static/chunks/9494-0df032fe6ee531a5.js +0 -1
  283. fides/ui-build/static/admin/_next/static/chunks/9676.97fdbec9c2a48c68.js +0 -1
  284. fides/ui-build/static/admin/_next/static/chunks/9767-74320abc2e9a4550.js +0 -1
  285. fides/ui-build/static/admin/_next/static/chunks/pages/404-c00773c4c6e930af.js +0 -1
  286. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/manual-7d789458fb296270.js +0 -1
  287. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/multiple-90320455052466cd.js +0 -1
  288. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-a69f8946466471aa.js +0 -1
  289. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/add-vendors-bcc515a765c0119d.js +0 -1
  290. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-49b9f1b56262dfdc.js +0 -1
  291. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-d4137bb7fdc1ac6e.js +0 -1
  292. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-5ef03041a530b0e2.js +0 -1
  293. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/[id]-a5a95ac63b1ce206.js +0 -1
  294. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/new-946ce1a2aa1500d4.js +0 -1
  295. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-12f3ba4609bc2ba5.js +0 -1
  296. fides/ui-build/static/admin/_next/static/chunks/pages/consent/properties-ffa15d7ff64a9f05.js +0 -1
  297. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-b959d61c2a3682b3.js +0 -1
  298. fides/ui-build/static/admin/_next/static/chunks/pages/consent-7797e367dd946a18.js +0 -1
  299. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]-ecf23116a845188a.js +0 -1
  300. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-79f8e613827c55c1.js +0 -1
  301. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-e950d41b75f86c85.js +0 -1
  302. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-84ead584a88ab662.js +0 -1
  303. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-90f1975f559fff3e.js +0 -1
  304. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-4b820a3173f35504.js +0 -1
  305. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-beedbc38ee2b0094.js +0 -1
  306. fides/ui-build/static/admin/_next/static/chunks/pages/datamap-2d6f95b2769a0936.js +0 -1
  307. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/[...subfieldNames]-3536ae978741b659.js +0 -1
  308. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]-8b5afe14cf42a4ac.js +0 -1
  309. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-b8dca4298b5bb085.js +0 -1
  310. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-1e26702b1a7e0e12.js +0 -1
  311. fides/ui-build/static/admin/_next/static/chunks/pages/dataset-1f0c4e2e5d0b5bf6.js +0 -1
  312. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/[id]-ebf54ea9874c59ae.js +0 -1
  313. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-c39e8e2269883796.js +0 -1
  314. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-9db81467fa254996.js +0 -1
  315. fides/ui-build/static/admin/_next/static/chunks/pages/index-868b60407eae35da.js +0 -1
  316. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-ebc41649161f7fb9.js +0 -1
  317. fides/ui-build/static/admin/_next/static/chunks/pages/integrations-b89b9f68ccc69c1b.js +0 -1
  318. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/[id]-22812f08e81bd4f3.js +0 -1
  319. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/add-template-ca22869201847ccc.js +0 -1
  320. fides/ui-build/static/admin/_next/static/chunks/pages/messaging-5f41afae411cd8a4.js +0 -1
  321. fides/ui-build/static/admin/_next/static/chunks/pages/poc/ant-components-173bd3ad45ea61c6.js +0 -1
  322. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/AntForm-4dfe984ad80927cd.js +0 -1
  323. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikAntFormItem-f790e1ea5cbc26c0.js +0 -1
  324. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikControlled-af9e682ec620e990.js +0 -1
  325. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikField-6ce407101daa6000.js +0 -1
  326. fides/ui-build/static/admin/_next/static/chunks/pages/poc/forms-9ee8cf14a93eaa4a.js +0 -1
  327. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-928430cd40c35ef2.js +0 -1
  328. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-07ef58b04404479d.js +0 -1
  329. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-595ed9f6ca438020.js +0 -1
  330. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure-25a3add8e7982e43.js +0 -1
  331. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-b3384299166bcd1e.js +0 -1
  332. fides/ui-build/static/admin/_next/static/chunks/pages/properties/[id]-081f13526443e0a6.js +0 -1
  333. fides/ui-build/static/admin/_next/static/chunks/pages/properties/add-property-17fb474e458b63e7.js +0 -1
  334. fides/ui-build/static/admin/_next/static/chunks/pages/properties-f00530710b699afd.js +0 -1
  335. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-6a6016708efb5f4f.js +0 -1
  336. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about-177919b4ccf8f660.js +0 -1
  337. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/[purpose_id]-ba15c12c89ee10f8.js +0 -1
  338. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-0f2daeec241bd1a2.js +0 -1
  339. fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-0401208a3128fa4d.js +0 -1
  340. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domain-records-0daa256958675a23.js +0 -1
  341. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-26810d1ffedcd156.js +0 -1
  342. fides/ui-build/static/admin/_next/static/chunks/pages/settings/email-templates-eb78b645d0c1d4ea.js +0 -1
  343. fides/ui-build/static/admin/_next/static/chunks/pages/settings/organization-9404a6b0b2951992.js +0 -1
  344. fides/ui-build/static/admin/_next/static/chunks/pages/settings/regulations-0a3aa951ef4a44ba.js +0 -1
  345. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/test-datasets-48be14ad09ed4133.js +0 -1
  346. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]-b03403fed4dd8805.js +0 -1
  347. fides/ui-build/static/admin/_next/static/chunks/pages/systems-ae591953b24e2fc9.js +0 -1
  348. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-913efe5371344de6.js +0 -1
  349. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-dc11ba29dbd4708b.js +0 -1
  350. fides/ui-build/static/admin/_next/static/css/92c81f666644c594.css +0 -1
  351. fides/ui-build/static/admin/_next/static/css/b89fc4b36b501cf6.css +0 -1
  352. fides/ui-build/static/admin/_next/static/css/c0c2eb63ad3e7390.css +0 -1
  353. {ethyca_fides-2.62.1rc0.dist-info → ethyca_fides-2.63.0.dist-info}/WHEEL +0 -0
  354. {ethyca_fides-2.62.1rc0.dist-info → ethyca_fides-2.63.0.dist-info}/entry_points.txt +0 -0
  355. {ethyca_fides-2.62.1rc0.dist-info → ethyca_fides-2.63.0.dist-info}/licenses/LICENSE +0 -0
  356. {ethyca_fides-2.62.1rc0.dist-info → ethyca_fides-2.63.0.dist-info}/top_level.txt +0 -0
  357. /fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-69176abe09cf9b52.js → new-b124cc24b930c9e1.js} +0 -0
  358. /fides/ui-build/static/admin/_next/static/{K1_4-1GhwfL7h4fYYQBvB → lb6RRZ8JumokYwsENumlw}/_ssgManifest.js +0 -0
@@ -3,15 +3,27 @@ from __future__ import annotations
3
3
  from datetime import datetime
4
4
  from enum import Enum
5
5
  from re import match
6
- from typing import Any, Dict, Iterable, List, Optional, Type
6
+ from typing import Any, Dict, Iterable, List, Optional, Set, Type
7
7
 
8
8
  from loguru import logger
9
- from sqlalchemy import ARRAY, Boolean, Column, DateTime, ForeignKey, String, func
9
+ from sqlalchemy import (
10
+ ARRAY,
11
+ Boolean,
12
+ Column,
13
+ DateTime,
14
+ ForeignKey,
15
+ Index,
16
+ String,
17
+ UniqueConstraint,
18
+ func,
19
+ text,
20
+ )
10
21
  from sqlalchemy.dialects.postgresql import JSONB
11
22
  from sqlalchemy.ext.asyncio import AsyncSession
23
+ from sqlalchemy.ext.declarative import declared_attr
12
24
  from sqlalchemy.ext.mutable import MutableDict
13
25
  from sqlalchemy.future import select
14
- from sqlalchemy.orm import Session, relationship
26
+ from sqlalchemy.orm import RelationshipProperty, Session, relationship
15
27
  from sqlalchemy.orm.query import Query
16
28
 
17
29
  from fides.api.db.base_class import Base, FidesBase
@@ -47,6 +59,29 @@ class MonitorFrequency(Enum):
47
59
  QUARTERLY_MONTH_PATTERN = r"^\d+,\d+,\d+,\d+$"
48
60
 
49
61
 
62
+ class SharedMonitorConfig(Base, FidesBase):
63
+ """SQL model for shareable monitor configurations"""
64
+
65
+ @declared_attr
66
+ def __tablename__(self) -> str:
67
+ return "shared_monitor_config"
68
+
69
+ # Basic info
70
+ name = Column(String, nullable=False)
71
+ key = Column(String, unique=True, nullable=False)
72
+ description = Column(String, nullable=True)
73
+
74
+ # Classification parameters (including regex patterns)
75
+ classify_params = Column(
76
+ MutableDict.as_mutable(JSONB),
77
+ index=False,
78
+ unique=False,
79
+ nullable=False,
80
+ server_default="{}",
81
+ default=dict,
82
+ )
83
+
84
+
50
85
  class MonitorConfig(Base):
51
86
  """
52
87
  Monitor configuration used for data detection and discovery.
@@ -87,7 +122,9 @@ class MonitorConfig(Base):
87
122
  ) # stores the cron-based kwargs for scheduling the monitor execution.
88
123
  # see https://apscheduler.readthedocs.io/en/3.x/modules/triggers/cron.html
89
124
 
90
- classify_params = Column(
125
+ # We use _classify_params for the actual column to prevent direct access
126
+ _classify_params = Column(
127
+ "classify_params",
91
128
  MutableDict.as_mutable(JSONB),
92
129
  index=False,
93
130
  unique=False,
@@ -124,6 +161,45 @@ class MonitorConfig(Base):
124
161
  backref="monitor_config",
125
162
  )
126
163
 
164
+ shared_config_id = Column(
165
+ String,
166
+ ForeignKey(SharedMonitorConfig.id_field_path, ondelete="RESTRICT"),
167
+ nullable=True,
168
+ index=True,
169
+ )
170
+
171
+ shared_config = relationship(SharedMonitorConfig)
172
+
173
+ @property
174
+ def classify_params(self) -> dict:
175
+ """
176
+ Returns the merged classify parameters from both the monitor config and
177
+ the shared config (if it exists).
178
+
179
+ The shared config parameters take precedence over the monitor's own parameters,
180
+ but only for values that are not falsy (None, empty, etc.).
181
+ """
182
+ # Start with an empty dict
183
+ merged_params = {}
184
+
185
+ # Add this monitor's params if available
186
+ if self._classify_params:
187
+ merged_params.update(self._classify_params)
188
+
189
+ # Add/override with shared config params if available
190
+ if self.shared_config and self.shared_config.classify_params:
191
+ # Only update with non-falsy values from shared config
192
+ for key, value in self.shared_config.classify_params.items():
193
+ if value: # Only override if the value is not falsy
194
+ merged_params[key] = value
195
+
196
+ return merged_params
197
+
198
+ @classify_params.setter
199
+ def classify_params(self, value: Dict[str, Any]) -> None:
200
+ """Setter for the classify_params to maintain compatibility with existing code"""
201
+ self._classify_params = value
202
+
127
203
  @property
128
204
  def connection_config_key(self) -> str:
129
205
  """Derives the `connection_config_key`"""
@@ -255,6 +331,86 @@ class MonitorConfig(Base):
255
331
  data["monitor_execution_trigger"] = cron_trigger_dict
256
332
 
257
333
 
334
+ class StagedResourceAncestor(Base):
335
+ """
336
+ A simple junction table that is used to store the many-to-many relationship
337
+ between staged resources and their ancestors.
338
+
339
+ This table is used to easily lookup all ancestors of a given staged resource,
340
+ as its indexed by both ancestor and descendant URN columns.
341
+
342
+ Its entries should be deleted when the staged resource is deleted, via cascade.
343
+ """
344
+
345
+ ancestor_urn = Column(
346
+ String,
347
+ ForeignKey("stagedresource.urn", ondelete="CASCADE"),
348
+ primary_key=True,
349
+ nullable=False,
350
+ )
351
+ descendant_urn = Column(
352
+ String,
353
+ ForeignKey("stagedresource.urn", ondelete="CASCADE"),
354
+ primary_key=True,
355
+ nullable=False,
356
+ )
357
+
358
+ ancestor_staged_resource = relationship(
359
+ "StagedResource",
360
+ back_populates="ancestor_links",
361
+ lazy="selectin",
362
+ foreign_keys=[ancestor_urn],
363
+ )
364
+ descendant_staged_resource = relationship(
365
+ "StagedResource",
366
+ back_populates="descendant_links",
367
+ lazy="selectin",
368
+ foreign_keys=[descendant_urn],
369
+ )
370
+
371
+ __table_args__ = (
372
+ UniqueConstraint(
373
+ "ancestor_urn", "descendant_urn", name="uq_staged_resource_ancestor"
374
+ ),
375
+ Index("ix_staged_resource_ancestor_ancestor", "ancestor_urn"),
376
+ Index("ix_staged_resource_ancestor_descendant", "descendant_urn"),
377
+ )
378
+
379
+ @classmethod
380
+ def create_staged_resource_ancestor_links(
381
+ cls,
382
+ db: Session,
383
+ resource_urn: str,
384
+ ancestor_urns: Set[str],
385
+ ) -> None:
386
+ """
387
+ Bulk inserts entries in the StagedResourceAncestor table
388
+ based on the provided resource URN and the set of its ancestor URNs.
389
+
390
+ We execute the bulk INSERT with the provided (synchronous) db session,
391
+ but the transaction is _not_ committed, so the caller must commit the transaction
392
+ to persist the changes.
393
+ """
394
+ links_to_insert = []
395
+
396
+ for ancestor_urn in ancestor_urns:
397
+ links_to_insert.append(
398
+ {"ancestor_urn": ancestor_urn, "descendant_urn": resource_urn}
399
+ )
400
+
401
+ if links_to_insert:
402
+ # Using raw SQL for ON CONFLICT with parameters for safety
403
+ stmt_text = text(
404
+ """
405
+ INSERT INTO stagedresourceancestor (id, ancestor_urn, descendant_urn)
406
+ VALUES ('srl_' || gen_random_uuid(), :ancestor_urn, :descendant_urn)
407
+ ON CONFLICT (ancestor_urn, descendant_urn) DO NOTHING;
408
+ """
409
+ )
410
+
411
+ db.execute(stmt_text, links_to_insert)
412
+
413
+
258
414
  class StagedResource(Base):
259
415
  """
260
416
  Base DB model that represents a staged resource, fields common to all types of staged resources
@@ -331,14 +487,36 @@ class StagedResource(Base):
331
487
  parent = Column(String, nullable=True)
332
488
 
333
489
  # diff-related fields
334
- diff_status = Column(String, nullable=True)
335
- child_diff_statuses = Column(
336
- MutableDict.as_mutable(JSONB),
337
- nullable=False,
338
- server_default="{}",
339
- default=dict,
490
+ diff_status = Column(String, nullable=True, index=True)
491
+
492
+ ancestor_links: RelationshipProperty[List[StagedResourceAncestor]] = relationship(
493
+ "StagedResourceAncestor",
494
+ back_populates="descendant_staged_resource",
495
+ lazy="dynamic",
496
+ cascade="all, delete",
497
+ foreign_keys=[StagedResourceAncestor.descendant_urn],
498
+ )
499
+
500
+ descendant_links: RelationshipProperty[List[StagedResourceAncestor]] = relationship(
501
+ "StagedResourceAncestor",
502
+ back_populates="ancestor_staged_resource",
503
+ lazy="dynamic",
504
+ cascade="all, delete",
505
+ foreign_keys=[StagedResourceAncestor.ancestor_urn],
340
506
  )
341
507
 
508
+ def ancestors(self) -> List[StagedResource]:
509
+ """
510
+ Returns the ancestors of the staged resource
511
+ """
512
+ return [link.ancestor_staged_resource for link in self.ancestor_links]
513
+
514
+ def descendants(self) -> List[StagedResource]:
515
+ """
516
+ Returns the descendants of the staged resource
517
+ """
518
+ return [link.descendant_staged_resource for link in self.descendant_links]
519
+
342
520
  # placeholder for additional attributes
343
521
  meta = Column(
344
522
  MutableDict.as_mutable(JSONB),
@@ -391,28 +569,15 @@ class StagedResource(Base):
391
569
  )
392
570
  return results.scalars().all()
393
571
 
394
- def add_child_diff_status(self, diff_status: DiffStatus) -> None:
395
- """Increments the specified child diff status"""
396
- self.child_diff_statuses[diff_status.value] = (
397
- self.child_diff_statuses.get(diff_status.value, 0) + 1
398
- )
399
-
400
572
  def mark_as_addition(
401
573
  self,
402
574
  db: Session,
403
575
  parent_resource_urns: Iterable[str] = [],
404
576
  ) -> None:
405
577
  """
406
- Marks the resource as an addition and the child diff status of
407
- the given parent resource URNs accordingly
578
+ Marks the resource as an addition
408
579
  """
409
580
  self.diff_status = DiffStatus.ADDITION.value
410
- for parent_resource_urn in parent_resource_urns:
411
- parent_resource: Optional[StagedResource] = StagedResource.get_urn(
412
- db, parent_resource_urn
413
- )
414
- if parent_resource:
415
- parent_resource.add_child_diff_status(DiffStatus.ADDITION)
416
581
 
417
582
 
418
583
  class MonitorExecution(Base):
@@ -23,12 +23,16 @@ from fides.api.db.base_class import Base
23
23
  from fides.api.models.audit_log import AuditLog
24
24
 
25
25
  # Intentionally importing SystemManager here to build the FidesUser.systems relationship
26
- from fides.api.models.system_manager import SystemManager # type: ignore[unused-import]
27
26
  from fides.api.schemas.user import DisabledReason
28
27
  from fides.config import CONFIG
29
28
 
30
29
  if TYPE_CHECKING:
30
+ from fides.api.models.fides_user_permissions import FidesUserPermissions
31
+ from fides.api.models.fides_user_respondent_email_verification import (
32
+ FidesUserRespondentEmailVerification,
33
+ )
31
34
  from fides.api.models.sql_models import System # type: ignore[attr-defined]
35
+ from fides.api.models.system_manager import SystemManager
32
36
 
33
37
 
34
38
  class FidesUser(Base):
@@ -71,6 +75,20 @@ class FidesUser(Base):
71
75
  )
72
76
 
73
77
  systems = relationship("System", secondary="systemmanager", back_populates="data_stewards") # type: ignore
78
+ # permissions relationship is defined via backref in FidesUserPermissions
79
+ email_verifications = relationship(
80
+ "FidesUserRespondentEmailVerification",
81
+ back_populates="user",
82
+ cascade="all,delete",
83
+ lazy="dynamic",
84
+ foreign_keys="[FidesUserRespondentEmailVerification.user_id]",
85
+ )
86
+ permissions = relationship(
87
+ "FidesUserPermissions",
88
+ back_populates="user",
89
+ cascade="all,delete",
90
+ uselist=False,
91
+ )
74
92
 
75
93
  @property
76
94
  def system_ids(self) -> List[str]:
@@ -92,12 +110,11 @@ class FidesUser(Base):
92
110
  ) -> FidesUser:
93
111
  """Create a FidesUser by hashing the password with a generated salt
94
112
  and storing the hashed password and the salt"""
113
+ hashed_password = None
114
+ salt = None
95
115
 
96
116
  if password := data.get("password"):
97
117
  hashed_password, salt = FidesUser.hash_password(password)
98
- else:
99
- hashed_password = None
100
- salt = None
101
118
 
102
119
  user = super().create(
103
120
  db,
@@ -117,6 +134,18 @@ class FidesUser(Base):
117
134
 
118
135
  return user # type: ignore
119
136
 
137
+ @classmethod
138
+ def create_respondent(cls, db: Session, data: dict[str, Any]) -> FidesUser:
139
+ """Create a respondent user. This user will not be able to login with a password and
140
+ requires an email address to be provided.
141
+ """
142
+ if not data.get("email_address"):
143
+ raise ValueError("Email address is required for external respondents")
144
+ if data.get("password"):
145
+ raise ValueError("Password login is not allowed for external respondents")
146
+ data["password_login_enabled"] = False
147
+ return cls.create(db, data)
148
+
120
149
  def credentials_valid(self, password: str, encoding: str = "UTF-8") -> bool:
121
150
  """Verifies that the provided password is correct."""
122
151
  if self.salt is None:
@@ -134,6 +163,9 @@ class FidesUser(Base):
134
163
 
135
164
  No validations are performed on the old/existing password within this function.
136
165
  """
166
+ if self.permissions is not None:
167
+ if self.permissions.is_respondent():
168
+ raise ValueError("Password changes are not allowed for respondents")
137
169
 
138
170
  hashed_password, salt = FidesUser.hash_password(new_password)
139
171
  self.hashed_password = hashed_password # type: ignore
@@ -141,6 +173,17 @@ class FidesUser(Base):
141
173
  self.password_reset_at = datetime.utcnow() # type: ignore
142
174
  self.save(db)
143
175
 
176
+ def update_email_address(self, db: Session, new_email_address: str) -> None:
177
+ """Updates the user's email address to the specified value."""
178
+ if self.permissions is not None:
179
+ if self.permissions.is_respondent():
180
+ raise ValueError(
181
+ "Email address changes are not allowed for respondents"
182
+ )
183
+
184
+ self.email_address = new_email_address # type: ignore
185
+ self.save(db)
186
+
144
187
  def set_as_system_manager(self, db: Session, system: System) -> None:
145
188
  """Add a user as one of the system managers for the given system
146
189
  If applicable, also update the systems on the user's client
@@ -157,6 +200,10 @@ class FidesUser(Base):
157
200
  f"User '{self.username}' is already a system manager of '{system.name}'."
158
201
  )
159
202
 
203
+ if self.permissions is not None:
204
+ if self.permissions.is_respondent():
205
+ raise SystemManagerException("Respondents cannot be system managers.")
206
+
160
207
  self.systems.append(system)
161
208
  self.save(db=db)
162
209
 
@@ -1,11 +1,15 @@
1
- from typing import List
1
+ from typing import List, cast
2
2
 
3
3
  from sqlalchemy import ARRAY, Column, ForeignKey, String
4
- from sqlalchemy.orm import backref, relationship
4
+ from sqlalchemy.orm import Session, relationship
5
5
 
6
6
  from fides.api.db.base_class import Base
7
7
  from fides.api.models.fides_user import FidesUser
8
- from fides.api.oauth.roles import ROLES_TO_SCOPES_MAPPING
8
+ from fides.api.oauth.roles import (
9
+ EXTERNAL_RESPONDENT,
10
+ RESPONDENT,
11
+ ROLES_TO_SCOPES_MAPPING,
12
+ )
9
13
 
10
14
 
11
15
  class FidesUserPermissions(Base):
@@ -13,10 +17,7 @@ class FidesUserPermissions(Base):
13
17
 
14
18
  user_id = Column(String, ForeignKey(FidesUser.id), nullable=False, unique=True)
15
19
  roles = Column(ARRAY(String), nullable=False, server_default="{}", default=dict)
16
- user = relationship(
17
- FidesUser,
18
- backref=backref("permissions", cascade="all,delete", uselist=False),
19
- )
20
+ user = relationship(FidesUser, back_populates="permissions", uselist=False)
20
21
 
21
22
  @property
22
23
  def total_scopes(self) -> List[str]:
@@ -26,3 +27,20 @@ class FidesUserPermissions(Base):
26
27
  all_scopes += ROLES_TO_SCOPES_MAPPING.get(role, [])
27
28
 
28
29
  return sorted(list(set(all_scopes)))
30
+
31
+ def is_respondent(self) -> bool:
32
+ """Check if the user is a respondent."""
33
+ return any(role in self.roles for role in [RESPONDENT, EXTERNAL_RESPONDENT])
34
+
35
+ def update_roles(self, db: Session, new_roles: List[str]) -> None:
36
+ """Update the user's roles if allowed.
37
+ Raises ValueError if role changes are not allowed."""
38
+ if self.is_respondent():
39
+ raise ValueError("Role changes are not allowed for respondents")
40
+
41
+ self.roles = new_roles
42
+ self.save(db)
43
+
44
+ def update(self, db: Session, *, data: dict) -> "FidesUserPermissions":
45
+ """Update the user permissions with the provided data."""
46
+ return cast(FidesUserPermissions, super().update(db, data=data))
@@ -0,0 +1,112 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime, timedelta, timezone
4
+ from typing import TYPE_CHECKING, Any, Optional
5
+
6
+ from sqlalchemy import Column, DateTime, ForeignKey, String
7
+ from sqlalchemy.ext.declarative import declared_attr
8
+ from sqlalchemy.orm import Session, relationship
9
+
10
+ from fides.api.cryptography.cryptographic_util import generate_secure_random_string
11
+ from fides.api.db.base_class import Base
12
+ from fides.api.util.identity_verification import IdentityVerificationMixin
13
+ from fides.config import get_config
14
+
15
+ CONFIG = get_config()
16
+
17
+
18
+ if TYPE_CHECKING:
19
+ from fides.api.models.fides_user import FidesUser
20
+
21
+ # Access links stay active for 45 days - the same as the DSR expiration. A new link is generated for each email.
22
+ # The emails are created for new DSRs which are assigned to the respondent.
23
+ ACCESS_LINK_TTL_DAYS = 45
24
+
25
+
26
+ class FidesUserRespondentEmailVerification(Base, IdentityVerificationMixin):
27
+ """Model for handling email verification for external respondents.
28
+
29
+ This handles two types of verification:
30
+ 1. Access links - long-lived (45 days) for initial access
31
+ 2. Verification codes - short-lived (1 hour) for actual verification
32
+
33
+ When an email is sent to an external respondent, a new verification is created with a new access token is created.
34
+ When a respondent clicks the link in the email, the access token is verified and a verification code is generated.
35
+ The verification code is sent to the respondent's email address and the respondent is prompted to enter the code.
36
+ Verification is handled by the `IdentityVerificationMixin` class.
37
+ """
38
+
39
+ @declared_attr
40
+ def __tablename__(self) -> str:
41
+ return "fides_user_respondent_email_verification"
42
+
43
+ user_id = Column(
44
+ String,
45
+ ForeignKey("fidesuser.id", ondelete="CASCADE"),
46
+ nullable=False,
47
+ index=True,
48
+ )
49
+
50
+ access_token = Column(
51
+ String, nullable=False, unique=True, index=True
52
+ ) # Token for the access link
53
+ access_token_expires_at = Column(DateTime(timezone=True), nullable=False)
54
+ identity_verified_at = Column(DateTime(timezone=True), nullable=True)
55
+
56
+ user = relationship(
57
+ "FidesUser",
58
+ back_populates="email_verifications",
59
+ foreign_keys=[user_id],
60
+ )
61
+
62
+ @classmethod
63
+ def create(
64
+ cls, db: Session, *, data: dict[str, Any], check_name: bool = False
65
+ ) -> FidesUserRespondentEmailVerification:
66
+ """
67
+ Create a FidesUserEmailVerification record with a new access token.
68
+ The verification code will be generated when the access link is used.
69
+ """
70
+ # Generate a secure token for the access link
71
+ access_token = generate_secure_random_string(32)
72
+ expires_at = datetime.now(timezone.utc) + timedelta(days=ACCESS_LINK_TTL_DAYS)
73
+
74
+ verification = super().create(
75
+ db,
76
+ data={
77
+ "user_id": data["user_id"],
78
+ "access_token": access_token,
79
+ "access_token_expires_at": expires_at,
80
+ },
81
+ )
82
+
83
+ return verification
84
+
85
+ def is_access_token_expired(self) -> bool:
86
+ """Check if the access token has expired."""
87
+ if not self.access_token_expires_at:
88
+ return True
89
+
90
+ current_time_utc = datetime.now(timezone.utc)
91
+ return current_time_utc > self.access_token_expires_at
92
+
93
+ def verify_access_token(self, token: str) -> bool:
94
+ """Verify the access token and generate a verification code if valid."""
95
+ if self.is_access_token_expired():
96
+ return False
97
+
98
+ return self.access_token == token
99
+
100
+ def verify_identity(
101
+ self,
102
+ db: Session,
103
+ provided_code: Optional[str] = None,
104
+ ) -> None:
105
+ """A method to call the internal identity verification method provided by the
106
+ `IdentityVerificationMixin`."""
107
+ if self.is_access_token_expired():
108
+ raise ValueError("Access token has expired.")
109
+
110
+ self._verify_identity(provided_code=provided_code)
111
+ self.identity_verified_at = datetime.now(timezone.utc)
112
+ self.save(db)
@@ -1089,6 +1089,46 @@ class PrivacyRequest(
1089
1089
  if attachment:
1090
1090
  attachment.delete(db)
1091
1091
 
1092
+ def _get_manual_webhook_attachments(
1093
+ self, db: Session, manual_webhook_id: str, reference_type: str
1094
+ ) -> List[Attachment]:
1095
+ """Helper method to get attachments that have references to both this privacy request and the specified manual webhook"""
1096
+ query = """
1097
+ SELECT DISTINCT a.*
1098
+ FROM attachment a
1099
+ INNER JOIN attachment_reference ar1 ON a.id = ar1.attachment_id
1100
+ INNER JOIN attachment_reference ar2 ON a.id = ar2.attachment_id
1101
+ WHERE ar1.reference_id = :privacy_request_id
1102
+ AND ar1.reference_type = 'privacy_request'
1103
+ AND ar2.reference_id = :manual_webhook_id
1104
+ AND ar2.reference_type = :reference_type
1105
+ """
1106
+ result = db.execute(
1107
+ query,
1108
+ {
1109
+ "privacy_request_id": self.id,
1110
+ "manual_webhook_id": manual_webhook_id,
1111
+ "reference_type": reference_type,
1112
+ },
1113
+ )
1114
+ return [Attachment(**row) for row in result]
1115
+
1116
+ def get_access_manual_webhook_attachments(
1117
+ self, db: Session, manual_webhook_id: str
1118
+ ) -> List[Attachment]:
1119
+ """Get all attachments that have references to both this privacy request and the specified access manual webhook"""
1120
+ return self._get_manual_webhook_attachments(
1121
+ db, manual_webhook_id, "access_manual_webhook"
1122
+ )
1123
+
1124
+ def get_erasure_manual_webhook_attachments(
1125
+ self, db: Session, manual_webhook_id: str
1126
+ ) -> List[Attachment]:
1127
+ """Get all attachments that have references to both this privacy request and the specified erasure manual webhook"""
1128
+ return self._get_manual_webhook_attachments(
1129
+ db, manual_webhook_id, "erasure_manual_webhook"
1130
+ )
1131
+
1092
1132
  def get_existing_request_task(
1093
1133
  self,
1094
1134
  db: Session,
@@ -1228,7 +1268,6 @@ class PrivacyRequest(
1228
1268
  """
1229
1269
  if not self.policy.get_rules_for_action(action_type=ActionType.access):
1230
1270
  return None
1231
-
1232
1271
  self.filtered_final_upload = results
1233
1272
  self.save(db)
1234
1273
 
fides/api/oauth/roles.py CHANGED
@@ -27,6 +27,8 @@ from fides.common.api.scope_registry import (
27
27
  PRIVACY_NOTICE_READ,
28
28
  PRIVACY_REQUEST_CALLBACK_RESUME,
29
29
  PRIVACY_REQUEST_DELETE,
30
+ PRIVACY_REQUEST_MANUAL_STEPS_RESPOND,
31
+ PRIVACY_REQUEST_MANUAL_STEPS_REVIEW,
30
32
  PRIVACY_REQUEST_NOTIFICATIONS_CREATE_OR_UPDATE,
31
33
  PRIVACY_REQUEST_NOTIFICATIONS_READ,
32
34
  PRIVACY_REQUEST_READ,
@@ -52,6 +54,8 @@ CONTRIBUTOR = "contributor"
52
54
  OWNER = "owner"
53
55
  VIEWER = "viewer"
54
56
  VIEWER_AND_APPROVER = "viewer_and_approver"
57
+ RESPONDENT = "respondent"
58
+ EXTERNAL_RESPONDENT = "external_respondent"
55
59
 
56
60
 
57
61
  class RoleRegistryEnum(Enum):
@@ -62,6 +66,8 @@ class RoleRegistryEnum(Enum):
62
66
  Approver - Limited viewer but can approve Privacy Requests
63
67
  Viewer + Approver = Full View and can approve Privacy Requests
64
68
  Contributor - Can't configure storage and messaging
69
+ Respondent - Internal user who can respond to manual steps
70
+ External Respondent - External user who can only respond to assigned manual steps
65
71
  """
66
72
 
67
73
  owner = OWNER
@@ -69,6 +75,8 @@ class RoleRegistryEnum(Enum):
69
75
  viewer = VIEWER
70
76
  approver = APPROVER
71
77
  contributor = CONTRIBUTOR
78
+ respondent = RESPONDENT
79
+ external_respondent = EXTERNAL_RESPONDENT
72
80
 
73
81
 
74
82
  approver_scopes = [
@@ -79,6 +87,7 @@ approver_scopes = [
79
87
  PRIVACY_REQUEST_VIEW_DATA,
80
88
  PRIVACY_REQUEST_DELETE,
81
89
  USER_READ, # allows approver to view user management table and update their own password
90
+ PRIVACY_REQUEST_MANUAL_STEPS_REVIEW, # allows approvers to see all manual steps
82
91
  ]
83
92
 
84
93
 
@@ -114,6 +123,14 @@ viewer_scopes = [ # Intentionally omitted USER_PERMISSION_READ and PRIVACY_REQU
114
123
  USER_READ,
115
124
  ]
116
125
 
126
+ respondent_scopes = [
127
+ PRIVACY_REQUEST_MANUAL_STEPS_RESPOND, # allows respondents to respond to assigned manual steps
128
+ ]
129
+
130
+ external_respondent_scopes = [
131
+ PRIVACY_REQUEST_MANUAL_STEPS_RESPOND, # allows external respondents to respond to assigned manual steps
132
+ ]
133
+
117
134
  not_contributor_scopes = [
118
135
  CONNECTOR_TEMPLATE_REGISTER,
119
136
  STORAGE_CREATE_OR_UPDATE,
@@ -130,6 +147,8 @@ ROLES_TO_SCOPES_MAPPING: Dict[str, List] = {
130
147
  VIEWER: sorted(viewer_scopes),
131
148
  APPROVER: sorted(approver_scopes),
132
149
  CONTRIBUTOR: sorted(list(set(SCOPE_REGISTRY) - set(not_contributor_scopes))),
150
+ RESPONDENT: sorted(respondent_scopes),
151
+ EXTERNAL_RESPONDENT: sorted(external_respondent_scopes),
133
152
  }
134
153
 
135
154