ethyca-fides 2.58.2rc0__py2.py3-none-any.whl → 2.58.3b1__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 (266) hide show
  1. {ethyca_fides-2.58.2rc0.dist-info → ethyca_fides-2.58.3b1.dist-info}/METADATA +20 -11
  2. {ethyca_fides-2.58.2rc0.dist-info → ethyca_fides-2.58.3b1.dist-info}/RECORD +225 -216
  3. {ethyca_fides-2.58.2rc0.dist-info → ethyca_fides-2.58.3b1.dist-info}/WHEEL +1 -1
  4. {ethyca_fides-2.58.2rc0.dist-info → ethyca_fides-2.58.3b1.dist-info}/entry_points.txt +0 -1
  5. fides/_version.py +3 -3
  6. fides/api/alembic/migrations/versions/67d01c4e124e_add_reject_all_mechanism_to_privacy_.py +56 -0
  7. fides/api/alembic/migrations/versions/6e565c16dae1_add_tcf_publisher_restrictions.py +107 -0
  8. fides/api/alembic/migrations/versions/9288f729cac4_add_tcf_configuration_fk_to_experience_.py +62 -0
  9. fides/api/alembic/migrations/versions/99c603c1b8f9_add_password_login_enabled_and_totp_secret_to_fidesuser.py +45 -0
  10. fides/api/api/deps.py +38 -2
  11. fides/api/api/v1/endpoints/user_endpoints.py +8 -12
  12. fides/api/cryptography/identity_salt.py +12 -13
  13. fides/api/custom_types.py +6 -1
  14. fides/api/db/base.py +5 -0
  15. fides/api/db/system.py +1 -3
  16. fides/api/migrations/hash_migration_job.py +2 -2
  17. fides/api/models/attachment.py +80 -11
  18. fides/api/models/comment.py +45 -15
  19. fides/api/models/detection_discovery.py +31 -0
  20. fides/api/models/fides_user.py +26 -9
  21. fides/api/models/fides_user_invite.py +2 -0
  22. fides/api/models/privacy_experience.py +68 -0
  23. fides/api/models/privacy_request/privacy_request.py +23 -6
  24. fides/api/models/tcf_publisher_restrictions.py +465 -0
  25. fides/api/schemas/connection_configuration/connection_config.py +30 -16
  26. fides/api/schemas/user.py +5 -1
  27. fides/api/service/deps.py +9 -0
  28. fides/api/service/storage/s3.py +14 -1
  29. fides/api/task/graph_task.py +1 -1
  30. fides/api/util/collection_util.py +48 -9
  31. fides/cli/commands/pull.py +77 -13
  32. fides/config/database_settings.py +46 -0
  33. fides/core/api.py +2 -1
  34. fides/core/pull.py +38 -7
  35. fides/service/error_handling/__init__.py +0 -0
  36. fides/service/error_handling/error_handler.py +202 -0
  37. fides/service/user/__init__.py +0 -0
  38. fides/service/user/user_service.py +140 -0
  39. fides/ui-build/static/admin/404.html +1 -1
  40. fides/ui-build/static/admin/_next/static/Phr0wJQrVglnj5svYDeUY/_buildManifest.js +1 -0
  41. fides/ui-build/static/admin/_next/static/chunks/1376-f2e68d1cfdacfd48.js +1 -0
  42. fides/ui-build/static/admin/_next/static/chunks/146-9567a3d2f9e21b83.js +1 -0
  43. fides/ui-build/static/admin/_next/static/chunks/1817-5c32a7592d18a859.js +1 -0
  44. fides/ui-build/static/admin/_next/static/chunks/1904-689b67a43153d56c.js +1 -0
  45. fides/ui-build/static/admin/_next/static/chunks/2479-bf5586191c72fb2b.js +1 -0
  46. fides/ui-build/static/admin/_next/static/chunks/3244-c728351365b77871.js +1 -0
  47. fides/ui-build/static/admin/_next/static/chunks/3702-70f90912a76ecef3.js +1 -0
  48. fides/ui-build/static/admin/_next/static/chunks/{3855-b6b7865dedd7bc2a.js → 3855-c02445526594fc1f.js} +1 -1
  49. fides/ui-build/static/admin/_next/static/chunks/{3872-4e053c20d546f027.js → 3872-0b61e674a790491b.js} +1 -1
  50. fides/ui-build/static/admin/_next/static/chunks/401-839481005c1ba95e.js +1 -0
  51. fides/ui-build/static/admin/_next/static/chunks/{4060-8d165e1236ea521a.js → 4060-fd2f97afa5ba80d4.js} +1 -1
  52. fides/ui-build/static/admin/_next/static/chunks/4121-28beb1c0ce3330b6.js +1 -0
  53. fides/ui-build/static/admin/_next/static/chunks/4481-865277e9623e6014.js +1 -0
  54. fides/ui-build/static/admin/_next/static/chunks/5102-626b9ff42e904276.js +1 -0
  55. fides/ui-build/static/admin/_next/static/chunks/{5480-f49696df5e8ae500.js → 5480-ff3e05a015ee2799.js} +1 -1
  56. fides/ui-build/static/admin/_next/static/chunks/{5487-3ad50d21cdbc9209.js → 5487-f281d138cb89b5c9.js} +1 -1
  57. fides/ui-build/static/admin/_next/static/chunks/5973-28d2af853d8498d7.js +1 -0
  58. fides/ui-build/static/admin/_next/static/chunks/{6372-ca9c12ac8902365b.js → 6372-8479ec83d73af02b.js} +1 -1
  59. fides/ui-build/static/admin/_next/static/chunks/6395-4224d6d26d1e8bb7.js +1 -0
  60. fides/ui-build/static/admin/_next/static/chunks/{6853-8941824350c3c1a8.js → 6853-3562089cc16a6799.js} +1 -1
  61. fides/ui-build/static/admin/_next/static/chunks/{6954-3b887fb444f9228c.js → 6954-23438f7f9729748b.js} +1 -1
  62. fides/ui-build/static/admin/_next/static/chunks/{7751-a8f31c062d4cb09d.js → 7751-95349028f1ee3fb5.js} +1 -1
  63. fides/ui-build/static/admin/_next/static/chunks/{79-f9b948ebb186900f.js → 79-488979db197d250c.js} +1 -1
  64. fides/ui-build/static/admin/_next/static/chunks/8934-ffa2b0509bc7a845.js +1 -0
  65. fides/ui-build/static/admin/_next/static/chunks/9572-82484a4dd5ebc57e.js +1 -0
  66. fides/ui-build/static/admin/_next/static/chunks/9767-d1d54cb9b74c0693.js +1 -0
  67. fides/ui-build/static/admin/_next/static/chunks/9951-8425f24ce61496bd.js +1 -0
  68. fides/ui-build/static/admin/_next/static/chunks/{main-24f422f93845a596.js → main-090643377c8254e6.js} +1 -1
  69. fides/ui-build/static/admin/_next/static/chunks/{main-app-94a0711202e08b15.js → main-app-59156a9331ac7bce.js} +1 -1
  70. fides/ui-build/static/admin/_next/static/chunks/pages/{_app-fc89ce7bed454c84.js → _app-fadef5a6a65d3ec4.js} +8 -8
  71. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/manual-58e9256e86916ecd.js +1 -0
  72. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-ba975134a85588f8.js → multiple-2392e3a101fae073.js} +1 -1
  73. fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-d258f0c25fa020bf.js → add-systems-661ff00f91fe62df.js} +1 -1
  74. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-b34e5324461d0c87.js → add-vendors-7dfe37b9f0ff9a3c.js} +1 -1
  75. fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-723cc3d4f5740ea6.js → configure-dba7848b760ba227.js} +1 -1
  76. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-78de4bde88e18b0f.js +1 -0
  77. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{new-a9d9402c219d13e5.js → new-c02b14c50b19bd91.js} +1 -1
  78. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-7dcbcd6f74029d90.js +1 -0
  79. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-c27810fc7d8ad4c0.js +1 -0
  80. fides/ui-build/static/admin/_next/static/chunks/pages/consent/{reporting-788cf0e34829af46.js → reporting-71c8a8557a0fb316.js} +1 -1
  81. fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-900004e402c31797.js → data-catalog-6dae602b509b00b5.js} +1 -1
  82. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-f149a0df946c05db.js +1 -0
  83. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-c5b3283cddb68c19.js +1 -0
  84. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-38476c697da53480.js +1 -0
  85. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-36ce3e322670e082.js +1 -0
  86. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-054ca46a782e99a5.js → [resourceUrn]-1bcaa606739ea1c5.js} +1 -1
  87. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-b75dd3e4306ac18e.js → detection-22f55dc12354b4c6.js} +1 -1
  88. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-1da20aeb6fc995e4.js → [resourceUrn]-86111c25dc30ef2b.js} +1 -1
  89. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-cad50b0cc6d1050c.js → discovery-853be75f08b9ab5c.js} +1 -1
  90. fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-8cb714cdd44ac40e.js → datamap-423172d31de8e9c6.js} +1 -1
  91. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-803c1b577ab17ae3.js → new-afb5d98731bc1bb1.js} +1 -1
  92. fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-fa743ddc7f89d76b.js → dataset-13e06b0a4a8c114c.js} +1 -1
  93. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-bbe1ca2793798e6b.js → [id]-d796cf4c25d0b988.js} +1 -1
  94. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-abc17fef69cd951b.js → new-814849a549132ffa.js} +1 -1
  95. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-be295129568a929c.js +1 -0
  96. fides/ui-build/static/admin/_next/static/chunks/pages/{index-bfaacdb55a5a6c9f.js → index-b0926c4083d4ac88.js} +1 -1
  97. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-dadebdf2d6fbcc64.js +1 -0
  98. fides/ui-build/static/admin/_next/static/chunks/pages/messaging-eef56c95b08aa24c.js +1 -0
  99. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-b1d39bb680cfd6d2.js +1 -0
  100. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-ea8c82f36520e542.js +1 -0
  101. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-2a56dc41a292a468.js +1 -0
  102. fides/ui-build/static/admin/_next/static/chunks/pages/{privacy-requests-d85c0d16ba09ba35.js → privacy-requests-f394d59981a4f50c.js} +1 -1
  103. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/{datamap-afedc48ef4e7f858.js → datamap-4e04234aa5dff9f8.js} +1 -1
  104. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-b359f061d3b9d455.js +1 -0
  105. fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-dcf102352d4d4d98.js +1 -0
  106. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-2f03e981234c40ad.js +1 -0
  107. fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-a08693d0d1e10bc8.js → organization-b208f9fd45ebb829.js} +1 -1
  108. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-151571cff4e85894.js → test-datasets-37c8930711ca2b8e.js} +1 -1
  109. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]-dd053a3bf2a9ca6c.js +1 -0
  110. fides/ui-build/static/admin/_next/static/chunks/pages/{systems-abd68fc5ddde5482.js → systems-4f07a39a7def714a.js} +1 -1
  111. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-ec35b1f86d536b75.js +1 -0
  112. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-be690621a944bfe2.js → new-082c3156175f9267.js} +1 -1
  113. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-963b0dbbf93b9e49.js +1 -0
  114. fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-6c9ad62479a7d03e.js → user-management-cb40808c1509f191.js} +1 -1
  115. fides/ui-build/static/admin/_next/static/css/687135955af5b7e1.css +1 -0
  116. fides/ui-build/static/admin/_next/static/css/b89fc4b36b501cf6.css +1 -0
  117. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  118. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  119. fides/ui-build/static/admin/add-systems.html +1 -1
  120. fides/ui-build/static/admin/ant-poc.html +1 -1
  121. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  122. fides/ui-build/static/admin/consent/configure.html +1 -1
  123. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  124. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  125. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  126. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  127. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  128. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  129. fides/ui-build/static/admin/consent/properties.html +1 -1
  130. fides/ui-build/static/admin/consent/reporting.html +1 -1
  131. fides/ui-build/static/admin/consent.html +1 -1
  132. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  133. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  134. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  135. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  136. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  137. fides/ui-build/static/admin/data-catalog.html +1 -1
  138. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  139. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  140. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  141. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  142. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  143. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  144. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  145. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  146. fides/ui-build/static/admin/datamap.html +1 -1
  147. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  148. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  149. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  150. fides/ui-build/static/admin/dataset/new.html +1 -1
  151. fides/ui-build/static/admin/dataset.html +1 -1
  152. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  153. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  154. fides/ui-build/static/admin/datastore-connection.html +1 -1
  155. fides/ui-build/static/admin/images/connector-logos/website.svg +10 -0
  156. fides/ui-build/static/admin/index.html +1 -1
  157. fides/ui-build/static/admin/integrations/[id].html +1 -1
  158. fides/ui-build/static/admin/integrations.html +1 -1
  159. fides/ui-build/static/admin/lib/fides-ext-gpp.js +1 -1
  160. fides/ui-build/static/admin/lib/fides-headless.js +1 -1
  161. fides/ui-build/static/admin/lib/fides-tcf.js +4 -4
  162. fides/ui-build/static/admin/lib/fides.js +3 -3
  163. fides/ui-build/static/admin/login/[provider].html +1 -1
  164. fides/ui-build/static/admin/login.html +1 -1
  165. fides/ui-build/static/admin/messaging/[id].html +1 -1
  166. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  167. fides/ui-build/static/admin/messaging.html +1 -1
  168. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  169. fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
  170. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  171. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  172. fides/ui-build/static/admin/privacy-requests.html +1 -1
  173. fides/ui-build/static/admin/properties/[id].html +1 -1
  174. fides/ui-build/static/admin/properties/add-property.html +1 -1
  175. fides/ui-build/static/admin/properties.html +1 -1
  176. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  177. fides/ui-build/static/admin/settings/about.html +1 -1
  178. fides/ui-build/static/admin/settings/consent.html +1 -1
  179. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  180. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  181. fides/ui-build/static/admin/settings/domains.html +1 -1
  182. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  183. fides/ui-build/static/admin/settings/locations.html +1 -1
  184. fides/ui-build/static/admin/settings/organization.html +1 -1
  185. fides/ui-build/static/admin/settings/regulations.html +1 -1
  186. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  187. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  188. fides/ui-build/static/admin/systems.html +1 -1
  189. fides/ui-build/static/admin/taxonomy.html +1 -1
  190. fides/ui-build/static/admin/user-management/new.html +1 -1
  191. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  192. fides/ui-build/static/admin/user-management.html +1 -1
  193. fides/api/service/user/fides_user_service.py +0 -128
  194. fides/ui-build/static/admin/_next/static/chunks/1150-035a721a04f4451e.js +0 -1
  195. fides/ui-build/static/admin/_next/static/chunks/1376-5cea5ef9362215e8.js +0 -1
  196. fides/ui-build/static/admin/_next/static/chunks/2397-ee53235fb21b5e97.js +0 -1
  197. fides/ui-build/static/admin/_next/static/chunks/3412-7ec8751b8182e1bf.js +0 -1
  198. fides/ui-build/static/admin/_next/static/chunks/355-8a77c9a1cd027f2e.js +0 -1
  199. fides/ui-build/static/admin/_next/static/chunks/4450-36234280bee624ff.js +0 -1
  200. fides/ui-build/static/admin/_next/static/chunks/4481-aab99ff80f707473.js +0 -1
  201. fides/ui-build/static/admin/_next/static/chunks/4723-0a3c5e2ce143a7d0.js +0 -1
  202. fides/ui-build/static/admin/_next/static/chunks/5258-0658dc2274df6832.js +0 -1
  203. fides/ui-build/static/admin/_next/static/chunks/5973-52aee296edc44f7e.js +0 -1
  204. fides/ui-build/static/admin/_next/static/chunks/6315-fa1519cdf080f42d.js +0 -1
  205. fides/ui-build/static/admin/_next/static/chunks/7453-39761c38da31257e.js +0 -1
  206. fides/ui-build/static/admin/_next/static/chunks/7980-4bd08957448dea32.js +0 -1
  207. fides/ui-build/static/admin/_next/static/chunks/8499-43606100edf42fdf.js +0 -1
  208. fides/ui-build/static/admin/_next/static/chunks/9046-04bd7becea207cb1.js +0 -1
  209. fides/ui-build/static/admin/_next/static/chunks/9767-1a23925d2cb27b51.js +0 -1
  210. fides/ui-build/static/admin/_next/static/chunks/9999-637e0e5341f15f4a.js +0 -1
  211. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/manual-9acaab973dfe86e2.js +0 -1
  212. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-fb75fa0aea77678d.js +0 -1
  213. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-c946b33b0322b8ad.js +0 -1
  214. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-ea57f9d6ad17e957.js +0 -1
  215. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-b66831fdafcdf67c.js +0 -1
  216. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-5f9ef1f99818117c.js +0 -1
  217. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-d001337d1bb73bd1.js +0 -1
  218. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-11b3ce9f61d9bfe9.js +0 -1
  219. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-a78a73b65929853a.js +0 -1
  220. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-d4329043219fed9b.js +0 -1
  221. fides/ui-build/static/admin/_next/static/chunks/pages/messaging-1e60754abec1ee6b.js +0 -1
  222. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-fe765154315782cf.js +0 -1
  223. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-f5f7a8069909ef24.js +0 -1
  224. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-9f7eaad05e5b9292.js +0 -1
  225. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-89524101b7279f6e.js +0 -1
  226. fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-52d030b1db2ca1b9.js +0 -1
  227. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-24cba38685dc872c.js +0 -1
  228. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]-4f5a28226575c976.js +0 -1
  229. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-16b4d75c49276add.js +0 -1
  230. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-78eaf933f755bfe8.js +0 -1
  231. fides/ui-build/static/admin/_next/static/css/113d823fe71f6af0.css +0 -1
  232. fides/ui-build/static/admin/_next/static/css/15fb7d4837a1de34.css +0 -1
  233. fides/ui-build/static/admin/_next/static/o0mKeH0cB6eAYV6qOlVD0/_buildManifest.js +0 -1
  234. {ethyca_fides-2.58.2rc0.dist-info → ethyca_fides-2.58.3b1.dist-info/licenses}/LICENSE +0 -0
  235. {ethyca_fides-2.58.2rc0.dist-info → ethyca_fides-2.58.3b1.dist-info}/top_level.txt +0 -0
  236. /fides/ui-build/static/admin/_next/static/{o0mKeH0cB6eAYV6qOlVD0 → Phr0wJQrVglnj5svYDeUY}/_ssgManifest.js +0 -0
  237. /fides/ui-build/static/admin/_next/static/chunks/{1100-053fc6b76c65a00f.js → 1100-45c0634b4f51d10c.js} +0 -0
  238. /fides/ui-build/static/admin/_next/static/chunks/{3086-b5054ec2c75700b9.js → 3086-d1ba90bc6ac9174b.js} +0 -0
  239. /fides/ui-build/static/admin/_next/static/chunks/{5826-e5dcb4e68cfe6289.js → 5826-1f4f74bf3b5348e4.js} +0 -0
  240. /fides/ui-build/static/admin/_next/static/chunks/{8433-b3008ecaf9834e7f.js → 8433-c4c765833ab9cc07.js} +0 -0
  241. /fides/ui-build/static/admin/_next/static/chunks/{9282-2bfbdca45e84e810.js → 9282-1a48b10b114d01f4.js} +0 -0
  242. /fides/ui-build/static/admin/_next/static/chunks/{9327-2cba327d10586683.js → 9327-4970d356f7000c0b.js} +0 -0
  243. /fides/ui-build/static/admin/_next/static/chunks/pages/{404-b202c0d8f6fc75c3.js → 404-29560aa2e6a60963.js} +0 -0
  244. /fides/ui-build/static/admin/_next/static/chunks/pages/{ant-poc-b9932971a479f3a7.js → ant-poc-404f3c9018952800.js} +0 -0
  245. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-72251b48e2e03a1e.js → [id]-75c2ed6ba3de48cd.js} +0 -0
  246. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-9611dd42856d6062.js → new-487ae57dc7e2ded2.js} +0 -0
  247. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-e5748812ba055a56.js → properties-f67fda6a71d0a46b.js} +0 -0
  248. /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-39d65f13cc8f1cf8.js → consent-75395dfc224cf44b.js} +0 -0
  249. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-a29850536c85d4b8.js → [projectUrn]-fa65c67ec8c4b5e6.js} +0 -0
  250. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-c44ce244122e96d5.js → projects-e76a07ee6ee8d55f.js} +0 -0
  251. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-d79551d4c64c398c.js → [...subfieldNames]-704553f5329fb9d4.js} +0 -0
  252. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-7e5df4a0de7540bb.js → [collectionName]-06c19dca941edb14.js} +0 -0
  253. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-6ba18f92ba561114.js → [datasetId]-eac517f43d5f53a8.js} +0 -0
  254. /fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-ef8000d7388dc915.js → integrations-373fb3a596099fc9.js} +0 -0
  255. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-ad02e019b2467958.js → [id]-66f5fbadd8455805.js} +0 -0
  256. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-f9693cb6a0b7ded8.js → add-template-d441abb1f045940f.js} +0 -0
  257. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-2987edc77388e85a.js → configure-b8c94b10ab90b061.js} +0 -0
  258. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-94e2faa73dd6a3e6.js → [id]-f4fb941df069b7bf.js} +0 -0
  259. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-630a6a3dd6502ba6.js → add-property-bccb6ffab25aa214.js} +0 -0
  260. /fides/ui-build/static/admin/_next/static/chunks/pages/{properties-20ca2f963906674b.js → properties-9a7ac623370b7c00.js} +0 -0
  261. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-a49d0f84cf0cf05e.js → about-4412a7b468b6d4bf.js} +0 -0
  262. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-fa42d8f18df44927.js → domain-records-d9088f5cd9fb2822.js} +0 -0
  263. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-6fd6071e2009b8f2.js → email-templates-cf09ad896c7396a6.js} +0 -0
  264. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-66c757325cb58467.js → locations-759564ca0ae62840.js} +0 -0
  265. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-c6c239996cfa6ae8.js → regulations-2dce4501fca920b3.js} +0 -0
  266. /fides/ui-build/static/admin/_next/static/chunks/{webpack-2c7ccac5843c4d8e.js → webpack-03e375f6d6b2c71b.js} +0 -0
@@ -0,0 +1,465 @@
1
+ from enum import Enum
2
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional
3
+
4
+ from pydantic import BaseModel, Field, ValidationError, model_validator
5
+ from sqlalchemy import Column
6
+ from sqlalchemy import Enum as EnumColumn
7
+ from sqlalchemy import ForeignKey, Index, Integer, String, insert, select, update
8
+ from sqlalchemy.dialects.postgresql import ARRAY, JSONB
9
+ from sqlalchemy.ext.asyncio import AsyncSession
10
+ from sqlalchemy.ext.declarative import declared_attr
11
+ from sqlalchemy.orm import Session, relationship
12
+
13
+ from fides.api.db.base_class import Base
14
+
15
+ if TYPE_CHECKING:
16
+ from fides.api.models.privacy_experience import PrivacyExperienceConfig
17
+
18
+
19
+ class TCFRestrictionType(str, Enum):
20
+ """Enum for TCF restriction types"""
21
+
22
+ purpose_restriction = "purpose_restriction"
23
+ require_consent = "require_consent"
24
+ require_legitimate_interest = "require_legitimate_interest"
25
+
26
+
27
+ class TCFVendorRestriction(str, Enum):
28
+ """Enum for TCF vendor restriction types"""
29
+
30
+ restrict_all_vendors = "restrict_all_vendors"
31
+ allow_specific_vendors = "allow_specific_vendors"
32
+ restrict_specific_vendors = "restrict_specific_vendors"
33
+
34
+
35
+ # This is Pydantic model used for validation, not a database model!
36
+ class RangeEntry(BaseModel):
37
+ """
38
+ Pydantic model that represents a vendor range entry as per the TCF spec,
39
+ used for Publisher Restrictions.
40
+ A range entry must have a start_vendor_id and optionally an end_vendor_id.
41
+ If end_vendor_id is present, it must be greater than start_vendor_id.
42
+ """
43
+
44
+ start_vendor_id: int = Field(description="The starting vendor ID in the range")
45
+ end_vendor_id: Optional[int] = Field(
46
+ default=None, description="The ending vendor ID in the range (inclusive)"
47
+ )
48
+
49
+ @model_validator(mode="after")
50
+ def validate_vendor_range(self) -> "RangeEntry":
51
+ """Validates that end_vendor_id is greater than start_vendor_id if present."""
52
+ if self.end_vendor_id is not None and self.end_vendor_id < self.start_vendor_id:
53
+ raise ValueError(
54
+ "end_vendor_id must be greater than or equal to start_vendor_id"
55
+ )
56
+ return self
57
+
58
+ @property
59
+ def effective_end_vendor_id(self) -> int:
60
+ """Get the effective end of the range."""
61
+ return self.end_vendor_id or self.start_vendor_id
62
+
63
+ def overlaps_with(self, other: "RangeEntry") -> bool:
64
+ """
65
+ Check if this range overlaps with another range.
66
+ Two ranges overlap if the end of the range that starts first is greater than
67
+ or equal to the start of the range that starts second.
68
+ """
69
+ # Sort ranges by start_vendor_id
70
+ first = self if self.start_vendor_id <= other.start_vendor_id else other
71
+ second = other if self.start_vendor_id <= other.start_vendor_id else self
72
+
73
+ # If first range's end is >= second range's start, they overlap
74
+ return first.effective_end_vendor_id >= second.start_vendor_id
75
+
76
+
77
+ class TCFConfiguration(Base):
78
+ """
79
+ Stores TCF Configuration settings.
80
+ """
81
+
82
+ @declared_attr
83
+ def __tablename__(self) -> str:
84
+ return "tcf_configuration"
85
+
86
+ name = Column(String, nullable=False, index=True, unique=True)
87
+
88
+ privacy_experience_configs = relationship(
89
+ "PrivacyExperienceConfig",
90
+ back_populates="tcf_configuration",
91
+ lazy="selectin",
92
+ )
93
+
94
+
95
+ class TCFPublisherRestriction(Base):
96
+ """
97
+ Stores TCF Publisher Restrictions. TCF Publisher Restrictions belong to a TCF Configuration,
98
+ and specify the restriction type and vendor restriction for a given purpose.
99
+ """
100
+
101
+ @declared_attr
102
+ def __tablename__(self) -> str:
103
+ return "tcf_publisher_restriction"
104
+
105
+ tcf_configuration_id = Column(
106
+ String(255),
107
+ ForeignKey("tcf_configuration.id", ondelete="CASCADE"),
108
+ nullable=False,
109
+ index=True,
110
+ )
111
+ purpose_id = Column(Integer, nullable=False)
112
+ restriction_type = Column(EnumColumn(TCFRestrictionType), nullable=False)
113
+ vendor_restriction = Column(EnumColumn(TCFVendorRestriction), nullable=False)
114
+
115
+ # range_entries represents a list of RangeEntry objects as per the TCF spec.
116
+ # A RangeEntry object will have a start_vendor_id and an end_vendor_id (the
117
+ # end vendor id is included in the range).
118
+ # If the range represents a single vendor, then the end_vendor_id can be omitted.
119
+ # TCF spec also includes an IsARange boolean, which we are omitting because it
120
+ # can be inferred from the presence of the end_vendor_id.
121
+ range_entries = Column(
122
+ ARRAY(JSONB),
123
+ nullable=True,
124
+ server_default="{}",
125
+ default=list,
126
+ )
127
+
128
+ __table_args__ = (
129
+ Index(
130
+ "ix_tcf_publisher_restriction_config_purpose",
131
+ "tcf_configuration_id",
132
+ "purpose_id",
133
+ ),
134
+ )
135
+
136
+ def __init__(self, **kwargs: Any) -> None:
137
+ validated_kwargs = self.validate_publisher_restriction_data(kwargs)
138
+ super().__init__(**validated_kwargs)
139
+
140
+ @staticmethod
141
+ def validate_entires_for_vendor_restriction(
142
+ entries: List[RangeEntry], vendor_restriction: TCFVendorRestriction
143
+ ) -> None:
144
+ """
145
+ Validates that if vendor_restriction is restrict_all_vendors, then the entries list is empty.
146
+ If vendor_restriction is not restrict_all_vendors, then there must be at least one entry.
147
+ """
148
+ if vendor_restriction == TCFVendorRestriction.restrict_all_vendors and entries:
149
+ raise ValueError(
150
+ "A restrict_all_vendors restriction cannot have any range entries"
151
+ )
152
+
153
+ if (
154
+ vendor_restriction != TCFVendorRestriction.restrict_all_vendors
155
+ and not entries
156
+ ):
157
+ raise ValueError(
158
+ f"A {vendor_restriction} restriction must have at least one range entry"
159
+ )
160
+
161
+ @staticmethod
162
+ def check_for_overlaps(entries: List[RangeEntry]) -> None:
163
+ """
164
+ Check for overlapping ranges in a list of RangeEntry objects,
165
+ raises a ValueError if any pair of RangeEntry overlaps with each other.
166
+ Sorts the ranges by start_vendor_id and compares adjacent ranges.
167
+ """
168
+ # Sort ranges by start_vendor_id
169
+ sorted_entries = sorted(entries, key=lambda x: x.start_vendor_id)
170
+
171
+ # Compare each range with the next one
172
+ for i in range(len(sorted_entries) - 1):
173
+ current = sorted_entries[i]
174
+ next_entry = sorted_entries[i + 1]
175
+ if current.overlaps_with(next_entry):
176
+ raise ValueError(
177
+ f"Overlapping ranges found: {current.model_dump()} overlaps with {next_entry.model_dump()}"
178
+ )
179
+
180
+ @classmethod
181
+ def validate_publisher_restriction_data(
182
+ cls, data: Dict[str, Any]
183
+ ) -> Dict[str, Any]:
184
+ """
185
+ Validate the restriction data.
186
+ """
187
+ raw_range_entries = data.get("range_entries", [])
188
+
189
+ try:
190
+ # Validate each range entry using the Pydantic model
191
+ validated_entries = [
192
+ RangeEntry.model_validate(entry) for entry in raw_range_entries
193
+ ]
194
+ except ValidationError as e:
195
+ raise ValueError(f"Invalid range entry: {str(e)}")
196
+
197
+ # Validate the entries for the vendor restriction
198
+ cls.validate_entires_for_vendor_restriction(
199
+ validated_entries, data["vendor_restriction"]
200
+ )
201
+ # Check for overlapping ranges
202
+ cls.check_for_overlaps(validated_entries)
203
+
204
+ data["range_entries"] = [entry.model_dump() for entry in validated_entries]
205
+
206
+ return data
207
+
208
+ @classmethod
209
+ async def check_for_restriction_conflicts(
210
+ cls,
211
+ *,
212
+ async_db: AsyncSession,
213
+ configuration_id: str,
214
+ purpose_id: int,
215
+ restriction_type: TCFRestrictionType,
216
+ vendor_restriction: TCFVendorRestriction,
217
+ range_entries: Optional[list[RangeEntry]] = None,
218
+ restriction_id: Optional[str] = None,
219
+ ) -> None:
220
+ """
221
+ Checks that the new restriction data does not conflict with any existing restrictions for the purpose.
222
+ """
223
+ # First, get all the restrictions for the purpose in the given configuration
224
+ query = (
225
+ select(cls) # type: ignore[arg-type]
226
+ .where(cls.tcf_configuration_id == configuration_id)
227
+ .where(cls.purpose_id == purpose_id)
228
+ )
229
+ # If we're updating an existing restriction, exclude it from the list of
230
+ # restrictions so it doesn't conflict with itself
231
+ if restriction_id:
232
+ query = query.where(cls.id != restriction_id)
233
+
234
+ restrictions_result = await async_db.execute(query)
235
+ relevant_restrictions = restrictions_result.scalars().all()
236
+
237
+ if any(
238
+ restriction.vendor_restriction == TCFVendorRestriction.restrict_all_vendors
239
+ for restriction in relevant_restrictions
240
+ ):
241
+ raise ValueError(
242
+ f"Invalid restriction for purpose {purpose_id}: a restrict_all_vendors restriction exists for this purpose."
243
+ )
244
+
245
+ # If we're creating a restrict_all_vendors restriction,
246
+ # then there should be no other restrictions for this purpose
247
+ if vendor_restriction == TCFVendorRestriction.restrict_all_vendors:
248
+ if relevant_restrictions:
249
+ raise ValueError(
250
+ f"Invalid restrict_all_vendors restriction for purpose {purpose_id}: other restrictions already exist for this purpose."
251
+ )
252
+
253
+ return None
254
+
255
+ # If we already have a restriction for that restriction type,
256
+ # then we raise an error
257
+ if any(
258
+ restriction.restriction_type == restriction_type
259
+ for restriction in relevant_restrictions
260
+ ):
261
+ raise ValueError(
262
+ f"Invalid {restriction_type} restriction for purpose {purpose_id}: a restriction of this type already exists for this purpose."
263
+ )
264
+
265
+ # We now need to check for vendor overlaps between all the restrictions.
266
+ # To achieve this, we need to transform allowlist-style restrictions into
267
+ # actual restriction ranges (rather than "allow" ranges).
268
+ new_range_entries = (
269
+ cls.transform_allowlist_restriction(range_entries or [])
270
+ if vendor_restriction == TCFVendorRestriction.allow_specific_vendors
271
+ else (range_entries or [])
272
+ )
273
+
274
+ existing_range_entries = []
275
+ for restriction in relevant_restrictions:
276
+ range_entries = [
277
+ RangeEntry.model_validate(entry) for entry in restriction.range_entries
278
+ ]
279
+ transformed_range_entries = (
280
+ cls.transform_allowlist_restriction(range_entries)
281
+ if restriction.vendor_restriction
282
+ == TCFVendorRestriction.allow_specific_vendors
283
+ else range_entries
284
+ )
285
+ existing_range_entries.extend(transformed_range_entries)
286
+
287
+ all_entries = [*existing_range_entries, *new_range_entries]
288
+ cls.check_for_overlaps(all_entries)
289
+
290
+ @classmethod
291
+ def transform_allowlist_restriction(
292
+ cls, range_entries: list[RangeEntry]
293
+ ) -> list[RangeEntry]:
294
+ """
295
+ Transform allowlist-style restrictions into restriction ranges.
296
+ E.g if you have an "allow specific vendors" restriction with the range_entries
297
+ [
298
+ {start_vendor_id: 5, end_vendor_id: 10},
299
+ {start_vendor_id: 25, end_vendor_id: 40},
300
+ {start_vendor_id: 123 },
301
+ {start_vendor_id: 345, end_vendor_id: 380},
302
+ ],
303
+ the transformed restriction ranges would be:
304
+ [
305
+ {start_vendor_id: 1, end_vendor_id: 4},
306
+ {start_vendor_id: 11, end_vendor_id: 24},
307
+ {start_vendor_id: 41, end_vendor_id: 122},
308
+ {start_vendor_id: 124, end_vendor_id: MAX_GVL_ID},
309
+ ]
310
+ """
311
+ MAX_GVL_ID = 9999 # TODO: get this from the TCF spec
312
+
313
+ # First, we need to sort the range_entries by start_vendor_id
314
+ sorted_range_entries = sorted(range_entries, key=lambda x: x.start_vendor_id)
315
+
316
+ # Now, we need to transform the allowlist-style restrictions into
317
+ # actual restriction ranges.
318
+ transformed_range_entries: list[RangeEntry] = []
319
+
320
+ total_entries = len(sorted_range_entries)
321
+
322
+ # This shouldn't happen, but just in case
323
+ if total_entries == 0:
324
+ raise ValueError(
325
+ "No range entries found for allow_specific_vendors restriction"
326
+ )
327
+
328
+ # If the first range entry starts at a number greater than 1,
329
+ # we need to add a transformed range entry from 1 up to the entry's start
330
+ if sorted_range_entries[0].start_vendor_id > 1:
331
+ transformed_range_entries.append(
332
+ RangeEntry(
333
+ start_vendor_id=1,
334
+ end_vendor_id=sorted_range_entries[0].start_vendor_id - 1,
335
+ )
336
+ )
337
+
338
+ # Iterate through the sorted range_entries and transform them into restriction ranges
339
+ for idx, range_entry in enumerate(sorted_range_entries):
340
+ # For all but the last range entry, we add an entry that corresponds to the numbers
341
+ # between the end of the current range entry and the start of the next range entry
342
+ if idx < total_entries - 1:
343
+ # Only create a range entry if there's actually a gap between the ranges
344
+ next_start = sorted_range_entries[idx + 1].start_vendor_id
345
+ current_end = range_entry.effective_end_vendor_id
346
+ if next_start > current_end + 1:
347
+ transformed_range_entries.append(
348
+ RangeEntry(
349
+ start_vendor_id=current_end + 1,
350
+ end_vendor_id=next_start - 1,
351
+ )
352
+ )
353
+
354
+ # If the last range entry ends at a number less than MAX_GVL_ID,
355
+ # we need to add a transformed range entry from the last entry's end to MAX_GVL_ID
356
+ if sorted_range_entries[-1].effective_end_vendor_id < MAX_GVL_ID:
357
+ transformed_range_entries.append(
358
+ RangeEntry(
359
+ start_vendor_id=sorted_range_entries[-1].effective_end_vendor_id
360
+ + 1,
361
+ end_vendor_id=MAX_GVL_ID,
362
+ )
363
+ )
364
+
365
+ # Return the transformed restriction entries
366
+ return transformed_range_entries
367
+
368
+ @classmethod
369
+ def create(
370
+ cls,
371
+ db: Session,
372
+ *,
373
+ data: Dict[str, Any],
374
+ check_name: bool = True,
375
+ ) -> "TCFPublisherRestriction":
376
+ raise NotImplementedError("Use create_async instead")
377
+
378
+ @classmethod
379
+ async def create_async(
380
+ cls,
381
+ async_db: AsyncSession,
382
+ *,
383
+ data: Dict[str, Any],
384
+ ) -> "TCFPublisherRestriction":
385
+ """
386
+ Create a new TCFPublisherRestriction with validated range_entries.
387
+ """
388
+
389
+ data = cls.validate_publisher_restriction_data(data)
390
+
391
+ await cls.check_for_restriction_conflicts(
392
+ async_db=async_db,
393
+ configuration_id=data["tcf_configuration_id"],
394
+ purpose_id=data["purpose_id"],
395
+ restriction_type=TCFRestrictionType(data["restriction_type"]),
396
+ vendor_restriction=TCFVendorRestriction(data["vendor_restriction"]),
397
+ range_entries=[
398
+ RangeEntry.model_validate(entry) for entry in data["range_entries"]
399
+ ],
400
+ )
401
+
402
+ values = {
403
+ "tcf_configuration_id": data["tcf_configuration_id"],
404
+ "purpose_id": data["purpose_id"],
405
+ "restriction_type": data["restriction_type"],
406
+ "vendor_restriction": data["vendor_restriction"],
407
+ "range_entries": data["range_entries"],
408
+ }
409
+
410
+ # Insert the new restriction
411
+ insert_stmt = insert(cls).values(values) # type: ignore[arg-type]
412
+ result = await async_db.execute(insert_stmt)
413
+ record_id = result.inserted_primary_key.id
414
+
415
+ created_record = await async_db.execute(select(cls).where(cls.id == record_id)) # type: ignore[arg-type]
416
+ return created_record.scalars().first()
417
+
418
+ async def update_async(
419
+ self, async_db: AsyncSession, data: Dict[str, Any]
420
+ ) -> "TCFPublisherRestriction":
421
+ """
422
+ Update a TCFPublisherRestriction with the data.
423
+ Validates the data and checks for vendor overlaps.
424
+ """
425
+ # Create a new dict merging the existing data and the updated data
426
+ updated_data = {
427
+ "id": self.id,
428
+ "tcf_configuration_id": self.tcf_configuration_id,
429
+ "purpose_id": self.purpose_id,
430
+ "restriction_type": self.restriction_type,
431
+ "vendor_restriction": self.vendor_restriction,
432
+ "range_entries": self.range_entries,
433
+ **data,
434
+ }
435
+
436
+ # First validate the data on its own
437
+ data = self.validate_publisher_restriction_data(updated_data)
438
+
439
+ # Then check for conflicts
440
+ await self.check_for_restriction_conflicts(
441
+ async_db=async_db,
442
+ configuration_id=self.tcf_configuration_id,
443
+ purpose_id=self.purpose_id,
444
+ restriction_type=TCFRestrictionType(data["restriction_type"]),
445
+ vendor_restriction=TCFVendorRestriction(data["vendor_restriction"]),
446
+ range_entries=[
447
+ RangeEntry.model_validate(entry) for entry in data["range_entries"]
448
+ ],
449
+ restriction_id=self.id,
450
+ )
451
+
452
+ # Remove the id from the updated
453
+ updated_data.pop("id")
454
+
455
+ # Finally, make the update
456
+ update_query = (
457
+ update(TCFPublisherRestriction) # type: ignore[arg-type]
458
+ .where(TCFPublisherRestriction.id == self.id)
459
+ .values(**updated_data)
460
+ )
461
+ await async_db.execute(update_query)
462
+ await async_db.commit()
463
+ await async_db.refresh(self)
464
+
465
+ return self
@@ -71,29 +71,23 @@ def mask_sensitive_fields(
71
71
  return new_connection_secrets
72
72
 
73
73
 
74
- class ConnectionConfigurationResponse(BaseModel):
74
+ class ConnectionConfigSecretsMixin(BaseModel):
75
75
  """
76
- Describes the returned schema for a ConnectionConfiguration.
76
+ A schema mixin to declare a connection config `secrets` attribute
77
+ and handle masking of sensitive values based on `connection_type`
78
+ and (optionally) a `saas_config`.
77
79
  """
78
80
 
79
- name: Optional[str] = None
80
- key: FidesKey
81
- description: Optional[str] = None
82
81
  connection_type: ConnectionType
83
- access: AccessLevel
84
- created_at: datetime
85
- updated_at: Optional[datetime] = None
86
- disabled: Optional[bool] = False
87
- last_test_timestamp: Optional[datetime] = None
88
- last_test_succeeded: Optional[bool] = None
89
- saas_config: Optional[SaaSConfigBase] = None
90
82
  secrets: Optional[Dict[str, Any]] = None
91
- authorized: Optional[bool] = False
92
- enabled_actions: Optional[List[ActionType]] = None
83
+ saas_config: Optional[SaaSConfigBase] = None
93
84
 
94
85
  @model_validator(mode="after")
95
- def mask_sensitive_values(self) -> "ConnectionConfigurationResponse":
96
- """Mask sensitive values in the response."""
86
+ def mask_sensitive_values(self) -> "ConnectionConfigSecretsMixin":
87
+ """
88
+ Mask sensitive values in the `secrets` attribute based on `connection_type`
89
+ and (optionally) a `saas_config`.
90
+ """
97
91
  if self.secrets is None:
98
92
  return self
99
93
 
@@ -116,6 +110,26 @@ class ConnectionConfigurationResponse(BaseModel):
116
110
  self.secrets = mask_sensitive_fields(cast(dict, self.secrets), secret_schema)
117
111
  return self
118
112
 
113
+
114
+ class ConnectionConfigurationResponse(ConnectionConfigSecretsMixin):
115
+ """
116
+ Describes the returned schema for a ConnectionConfiguration.
117
+
118
+ The mixin base class ensures that `secrets` sensitive values are masked.
119
+ """
120
+
121
+ name: Optional[str] = None
122
+ key: FidesKey
123
+ description: Optional[str] = None
124
+ access: AccessLevel
125
+ created_at: datetime
126
+ updated_at: Optional[datetime] = None
127
+ disabled: Optional[bool] = False
128
+ last_test_timestamp: Optional[datetime] = None
129
+ last_test_succeeded: Optional[bool] = None
130
+ authorized: Optional[bool] = False
131
+ enabled_actions: Optional[List[ActionType]] = None
132
+
119
133
  model_config = ConfigDict(from_attributes=True)
120
134
 
121
135
 
fides/api/schemas/user.py CHANGED
@@ -3,7 +3,7 @@ from datetime import datetime
3
3
  from enum import Enum
4
4
  from typing import Optional
5
5
 
6
- from pydantic import EmailStr, field_validator
6
+ from pydantic import ConfigDict, EmailStr, field_validator
7
7
 
8
8
  from fides.api.cryptography.cryptographic_util import decode_password
9
9
  from fides.api.schemas.base_class import FidesSchema
@@ -27,6 +27,8 @@ class UserCreate(FidesSchema):
27
27
  last_name: Optional[str] = None
28
28
  disabled: bool = False
29
29
 
30
+ model_config = ConfigDict(extra="ignore")
31
+
30
32
  @field_validator("username")
31
33
  @classmethod
32
34
  def validate_username(cls, username: str) -> str:
@@ -140,6 +142,8 @@ class UserUpdate(FidesSchema):
140
142
  first_name: Optional[str] = None
141
143
  last_name: Optional[str] = None
142
144
 
145
+ model_config = ConfigDict(extra="ignore")
146
+
143
147
 
144
148
  class DisabledReason(Enum):
145
149
  """Reasons for why a user is disabled"""
fides/api/service/deps.py CHANGED
@@ -8,6 +8,7 @@ from fides.service.dataset.dataset_config_service import DatasetConfigService
8
8
  from fides.service.dataset.dataset_service import DatasetService
9
9
  from fides.service.messaging.messaging_service import MessagingService
10
10
  from fides.service.privacy_request.privacy_request_service import PrivacyRequestService
11
+ from fides.service.user.user_service import UserService
11
12
 
12
13
 
13
14
  def get_messaging_service(
@@ -32,3 +33,11 @@ def get_dataset_service(db: Session = Depends(get_db)) -> DatasetService:
32
33
 
33
34
  def get_dataset_config_service(db: Session = Depends(get_db)) -> DatasetConfigService:
34
35
  return DatasetConfigService(db)
36
+
37
+
38
+ def get_user_service(
39
+ db: Session = Depends(get_db),
40
+ config: FidesConfig = Depends(get_config),
41
+ config_proxy: ConfigProxy = Depends(get_config_proxy),
42
+ ) -> UserService:
43
+ return UserService(db, config, config_proxy)
@@ -64,7 +64,7 @@ def generic_upload_to_s3( # pylint: disable=R0913
64
64
  size_threshold: int = LARGE_FILE_THRESHOLD, # 5 MB threshold
65
65
  ) -> Optional[AnyHttpUrlString]:
66
66
  """
67
- Uploads arbitrary data to S3 returned from an access request.
67
+ Uploads file like objects to S3.
68
68
  Handles both small and large uploads.
69
69
 
70
70
  :param storage_secrets: S3 storage secrets
@@ -75,6 +75,19 @@ def generic_upload_to_s3( # pylint: disable=R0913
75
75
  """
76
76
  logger.info("Starting S3 Upload of {}", file_key)
77
77
 
78
+ # Validate that the document is a file-like object
79
+ if not hasattr(document, "read") or not hasattr(document, "seek"):
80
+ raise TypeError(
81
+ f"The 'document' parameter must be a file-like object supporting 'read' and 'seek'. "
82
+ f"Received: {type(document)}"
83
+ )
84
+
85
+ # Ensure the file pointer is at the beginning
86
+ try:
87
+ document.seek(0)
88
+ except Exception as e:
89
+ raise ValueError(f"Failed to reset file pointer for document: {e}")
90
+
78
91
  s3_client = maybe_get_s3_client(auth_method, storage_secrets)
79
92
 
80
93
  # Define a transfer configuration for multipart uploads
@@ -10,7 +10,7 @@ from loguru import logger
10
10
  from ordered_set import OrderedSet
11
11
  from sqlalchemy.orm import Session
12
12
 
13
- from fides.api.api.deps import get_db_contextmanager as get_db
13
+ from fides.api.api.deps import get_autoclose_db_session as get_db
14
14
  from fides.api.common_exceptions import (
15
15
  ActionDisabled,
16
16
  AwaitingAsyncTaskCallback,