ethyca-fides 2.69.0rc9__py2.py3-none-any.whl → 2.69.1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (206) hide show
  1. {ethyca_fides-2.69.0rc9.dist-info → ethyca_fides-2.69.1.dist-info}/METADATA +2 -2
  2. {ethyca_fides-2.69.0rc9.dist-info → ethyca_fides-2.69.1.dist-info}/RECORD +204 -195
  3. fides/_version.py +3 -3
  4. fides/api/alembic/migrations/versions/78dbe23d8204_adding_privacy_request_redaction_patterns.py +52 -0
  5. fides/api/api/v1/api.py +2 -0
  6. fides/api/api/v1/endpoints/dsr_package_link.py +2 -2
  7. fides/api/api/v1/endpoints/oauth_endpoints.py +20 -6
  8. fides/api/api/v1/endpoints/privacy_request_redaction_patterns_endpoints.py +95 -0
  9. fides/api/api/v1/endpoints/user_endpoints.py +28 -1
  10. fides/api/app_setup.py +16 -2
  11. fides/api/db/base.py +3 -0
  12. fides/api/main.py +22 -0
  13. fides/api/models/client.py +1 -0
  14. fides/api/models/privacy_request_redaction_pattern.py +64 -0
  15. fides/api/oauth/utils.py +117 -6
  16. fides/api/schemas/privacy_request_redaction_patterns.py +55 -0
  17. fides/api/service/privacy_request/dsr_package/dsr_data_preprocessor.py +231 -0
  18. fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +286 -120
  19. fides/api/service/privacy_request/dsr_package/templates/attachments_index.html +4 -2
  20. fides/api/service/privacy_request/dsr_package/templates/collection_index.html +3 -1
  21. fides/api/service/privacy_request/dsr_package/templates/dataset_index.html +1 -1
  22. fides/api/service/privacy_request/dsr_package/utils.py +268 -0
  23. fides/api/service/privacy_request/request_runner_service.py +8 -2
  24. fides/api/service/storage/streaming/smart_open_streaming_storage.py +107 -170
  25. fides/api/service/storage/util.py +579 -0
  26. fides/api/task/manual/manual_task_graph_task.py +11 -9
  27. fides/api/tasks/storage.py +2 -2
  28. fides/api/util/endpoint_utils.py +0 -13
  29. fides/api/util/rate_limit.py +194 -0
  30. fides/common/api/scope_registry.py +8 -0
  31. fides/common/api/v1/urn_registry.py +3 -0
  32. fides/config/redis_settings.py +27 -3
  33. fides/config/security_settings.py +31 -9
  34. fides/ui-build/static/admin/404.html +1 -1
  35. fides/ui-build/static/admin/_next/static/1TigfgzjzHeoVqRLNIMYa/_buildManifest.js +1 -0
  36. fides/ui-build/static/admin/_next/static/chunks/4831-fd99c0b3784de128.js +1 -0
  37. fides/ui-build/static/admin/_next/static/chunks/pages/{_app-ef8e1c986bc5b795.js → _app-fcdad91f6f66292b.js} +1 -1
  38. fides/ui-build/static/admin/_next/static/chunks/pages/settings/privacy-requests-2ecc073f41628f62.js +1 -0
  39. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-de8cb3739ab99c09.js → new-92f52c43f522a350.js} +1 -1
  40. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-05d61c80a556b2d5.js → [id]-64452dfae2c5e614.js} +1 -1
  41. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  42. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  43. fides/ui-build/static/admin/add-systems.html +1 -1
  44. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  45. fides/ui-build/static/admin/consent/configure.html +1 -1
  46. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  47. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  48. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  49. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  50. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  51. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  52. fides/ui-build/static/admin/consent/properties.html +1 -1
  53. fides/ui-build/static/admin/consent/reporting.html +1 -1
  54. fides/ui-build/static/admin/consent.html +1 -1
  55. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  56. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  57. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  58. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  59. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  60. fides/ui-build/static/admin/data-catalog.html +1 -1
  61. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  62. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  63. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  64. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  65. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  66. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  67. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  68. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  69. fides/ui-build/static/admin/datamap.html +1 -1
  70. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  71. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  72. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  73. fides/ui-build/static/admin/dataset/new.html +1 -1
  74. fides/ui-build/static/admin/dataset.html +1 -1
  75. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  76. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  77. fides/ui-build/static/admin/datastore-connection.html +1 -1
  78. fides/ui-build/static/admin/index.html +1 -1
  79. fides/ui-build/static/admin/integrations/[id].html +1 -1
  80. fides/ui-build/static/admin/integrations.html +1 -1
  81. fides/ui-build/static/admin/login/[provider].html +1 -1
  82. fides/ui-build/static/admin/login.html +1 -1
  83. fides/ui-build/static/admin/messaging/[id].html +1 -1
  84. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  85. fides/ui-build/static/admin/messaging.html +1 -1
  86. fides/ui-build/static/admin/poc/ant-components.html +1 -1
  87. fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
  88. fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
  89. fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
  90. fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
  91. fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
  92. fides/ui-build/static/admin/poc/forms.html +1 -1
  93. fides/ui-build/static/admin/poc/table-migration.html +1 -1
  94. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  95. fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
  96. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  97. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  98. fides/ui-build/static/admin/privacy-requests.html +1 -1
  99. fides/ui-build/static/admin/properties/[id].html +1 -1
  100. fides/ui-build/static/admin/properties/add-property.html +1 -1
  101. fides/ui-build/static/admin/properties.html +1 -1
  102. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  103. fides/ui-build/static/admin/settings/about/alpha.html +1 -1
  104. fides/ui-build/static/admin/settings/about.html +1 -1
  105. fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
  106. fides/ui-build/static/admin/settings/consent.html +1 -1
  107. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  108. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  109. fides/ui-build/static/admin/settings/domains.html +1 -1
  110. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  111. fides/ui-build/static/admin/settings/locations.html +1 -1
  112. fides/ui-build/static/admin/settings/organization.html +1 -1
  113. fides/ui-build/static/admin/settings/privacy-requests.html +1 -0
  114. fides/ui-build/static/admin/settings/regulations.html +1 -1
  115. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  116. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  117. fides/ui-build/static/admin/systems.html +1 -1
  118. fides/ui-build/static/admin/taxonomy.html +1 -1
  119. fides/ui-build/static/admin/user-management/new.html +1 -1
  120. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  121. fides/ui-build/static/admin/user-management.html +1 -1
  122. fides/ui-build/static/admin/_next/static/XiHm-6CdVChTC5rbN9GtT/_buildManifest.js +0 -1
  123. fides/ui-build/static/admin/_next/static/chunks/4121-c8d5d717e31899e1.js +0 -1
  124. {ethyca_fides-2.69.0rc9.dist-info → ethyca_fides-2.69.1.dist-info}/WHEEL +0 -0
  125. {ethyca_fides-2.69.0rc9.dist-info → ethyca_fides-2.69.1.dist-info}/entry_points.txt +0 -0
  126. {ethyca_fides-2.69.0rc9.dist-info → ethyca_fides-2.69.1.dist-info}/licenses/LICENSE +0 -0
  127. {ethyca_fides-2.69.0rc9.dist-info → ethyca_fides-2.69.1.dist-info}/top_level.txt +0 -0
  128. /fides/ui-build/static/admin/_next/static/{XiHm-6CdVChTC5rbN9GtT → 1TigfgzjzHeoVqRLNIMYa}/_ssgManifest.js +0 -0
  129. /fides/ui-build/static/admin/_next/static/chunks/{1817-3d9e110e007853f0.js → 1817-0ca16d288fad916d.js} +0 -0
  130. /fides/ui-build/static/admin/_next/static/chunks/{3620-31ebb43dba84cbbd.js → 3620-602eb74dc896d556.js} +0 -0
  131. /fides/ui-build/static/admin/_next/static/chunks/{3729-a1ca1608efc11ac4.js → 3729-c17ac8031a4c4fd1.js} +0 -0
  132. /fides/ui-build/static/admin/_next/static/chunks/{3872-a91143aa35fa8ef8.js → 3872-f78dec02f0d959ae.js} +0 -0
  133. /fides/ui-build/static/admin/_next/static/chunks/{4608-23bbd4c3c4a59f42.js → 4608-be8cba73f5d7c326.js} +0 -0
  134. /fides/ui-build/static/admin/_next/static/chunks/{4786-0827aae7aceadd22.js → 4786-61154adf88e448e1.js} +0 -0
  135. /fides/ui-build/static/admin/_next/static/chunks/{4808-78ca630f2d2503cd.js → 4808-dd4157aa72648068.js} +0 -0
  136. /fides/ui-build/static/admin/_next/static/chunks/{5487-8c635883dcaa9c2a.js → 5487-02d00bad7c6830e0.js} +0 -0
  137. /fides/ui-build/static/admin/_next/static/chunks/{6084-0096d7de64ef8015.js → 6084-c153669d5567e242.js} +0 -0
  138. /fides/ui-build/static/admin/_next/static/chunks/{6954-9d46e2276c461c26.js → 6954-5296188c19d7d0ac.js} +0 -0
  139. /fides/ui-build/static/admin/_next/static/chunks/{7476-d1b0af9ade392e5b.js → 7476-45c5088baa8b66af.js} +0 -0
  140. /fides/ui-build/static/admin/_next/static/chunks/{7630-da0a7ce4e3a0d62c.js → 7630-7ed6c6117775dffe.js} +0 -0
  141. /fides/ui-build/static/admin/_next/static/chunks/{787-3499983fa346b380.js → 787-a8c7eab617e2fceb.js} +0 -0
  142. /fides/ui-build/static/admin/_next/static/chunks/{79-f197fc4db8d530e5.js → 79-65674011d455af4d.js} +0 -0
  143. /fides/ui-build/static/admin/_next/static/chunks/{796-db1e30119ea973c7.js → 796-9e1ca1a4030707c5.js} +0 -0
  144. /fides/ui-build/static/admin/_next/static/chunks/{8002-971e29181f72edd1.js → 8002-24af20d679efc04e.js} +0 -0
  145. /fides/ui-build/static/admin/_next/static/chunks/{9826-b0b3d3cfb13bfbc1.js → 9826-dbae8dee941a7fac.js} +0 -0
  146. /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-9dc7e70ab5b05723.js → manual-ace203dfacacbdc4.js} +0 -0
  147. /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-4b79a1652297ed9a.js → multiple-920fb469e0dda1d2.js} +0 -0
  148. /fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-1632a59203fe8eab.js → add-systems-bd0d82078e67cac3.js} +0 -0
  149. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-1ca9df7ca91bd101.js → add-vendors-406170eaae4329c6.js} +0 -0
  150. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-07bdbc9ae4137db4.js → configure-7207ab23bdb36ce8.js} +0 -0
  151. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-2795cd4115a77c94.js → privacy-experience-9dda4de5ec580279.js} +0 -0
  152. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-e02921dc82dccbb1.js → [id]-b378576cba255609.js} +0 -0
  153. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-98f9e4ba3610628a.js → new-2ca1de7b88094ab0.js} +0 -0
  154. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-17ed82777810d1c6.js → privacy-notices-0d4844d0b808e6e4.js} +0 -0
  155. /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-09610b10923d9268.js → consent-3e8bdefe714254ec.js} +0 -0
  156. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-da1a48336daff6f8.js → [resourceUrn]-2c29ff7a01198f30.js} +0 -0
  157. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-d8e776f1e64e4ba8.js → [projectUrn]-04cfe2cfba7b7cd8.js} +0 -0
  158. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-75b9629b0d9cdf96.js → projects-5f2d7b24804f861f.js} +0 -0
  159. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-470da05db63767cd.js → [resourceUrn]-8eb581024bc0172f.js} +0 -0
  160. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{resources-6c3714ee97a718c1.js → resources-de704de849960f01.js} +0 -0
  161. /fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-6984c033b8fe3a13.js → data-catalog-30108b00ac769fc3.js} +0 -0
  162. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/{[systemId]-2f0a33ef9ba1f1da.js → [systemId]-e1ba213fb666b3f4.js} +0 -0
  163. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/{[monitorId]-e9d4f25b20ff6781.js → [monitorId]-6d133580045abdda.js} +0 -0
  164. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{action-center-9c428d3ef0985915.js → action-center-9a81d42a474e1e48.js} +0 -0
  165. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-c3a97e6721ca0abe.js → [resourceUrn]-8f736b078e9842da.js} +0 -0
  166. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-a0a7de552ef71f5b.js → detection-eb814e3c22807871.js} +0 -0
  167. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-109754fec0755339.js → [resourceUrn]-6875b7783fcfda2f.js} +0 -0
  168. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-88654783b06b3b21.js → discovery-172dbd7740e212ca.js} +0 -0
  169. /fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-89136e6800dc9369.js → datamap-c7390e046b2e2b7f.js} +0 -0
  170. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-8f58192dcb54883d.js → [...subfieldNames]-dfd71c1e9c458b89.js} +0 -0
  171. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-dcb4ab380a77aa1e.js → [collectionName]-7cdc42ec5493b83d.js} +0 -0
  172. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-6f16d43071fb9c11.js → [datasetId]-e12b11ba15bc3fc1.js} +0 -0
  173. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-97f06e21580f1f6a.js → new-e32fccc4ca520d2b.js} +0 -0
  174. /fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-674bb3940f088ecc.js → dataset-7c59a6abf6ba6207.js} +0 -0
  175. /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-6f77d8647fca71e0.js → [id]-927b7e476c4b47d0.js} +0 -0
  176. /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-821dd1269834cfa2.js → new-cbe100d50df34285.js} +0 -0
  177. /fides/ui-build/static/admin/_next/static/chunks/pages/{datastore-connection-23e4caf79faa8106.js → datastore-connection-cce20440b177050b.js} +0 -0
  178. /fides/ui-build/static/admin/_next/static/chunks/pages/{index-23eb64eed81dcb69.js → index-6cd8708106331b8d.js} +0 -0
  179. /fides/ui-build/static/admin/_next/static/chunks/pages/integrations/{[id]-3a4cd3fe9094fba3.js → [id]-4c3c413a2668df53.js} +0 -0
  180. /fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-57e618d7b16ac69a.js → integrations-95402b5001c07ef2.js} +0 -0
  181. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-c9a323eb6a929476.js → [id]-3c6dc2f6e6bae960.js} +0 -0
  182. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-b9bb09e46921a590.js → add-template-4a6d4023a7791be8.js} +0 -0
  183. /fides/ui-build/static/admin/_next/static/chunks/pages/{messaging-82c631a12b5a008c.js → messaging-76b204c9b98d656f.js} +0 -0
  184. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{table-migration-38360083348c3d6c.js → table-migration-48500551fd6a7602.js} +0 -0
  185. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{[id]-0d0bb9eb004a3336.js → [id]-0f25a76dd18c5e20.js} +0 -0
  186. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{messaging-f9320a58f489f5b7.js → messaging-ad6ad3e5bd72765d.js} +0 -0
  187. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{storage-d0cfa8aeddd43a40.js → storage-6032d82f0fc2893d.js} +0 -0
  188. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-72ca94ec5ed85733.js → configure-d83e5bd52a638234.js} +0 -0
  189. /fides/ui-build/static/admin/_next/static/chunks/pages/{privacy-requests-5a5edc8a4aa7c30a.js → privacy-requests-baf31c3e4b081046.js} +0 -0
  190. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-5ec775c4904fdbfe.js → [id]-e784c05d056b2371.js} +0 -0
  191. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-a6812c0916f2949e.js → add-property-0a7a2db148a7561a.js} +0 -0
  192. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/{alpha-3e72e9f91991c119.js → alpha-a82f3df840d5c1b5.js} +0 -0
  193. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-6aab092f4871cecb.js → about-d06fb16487705b9d.js} +0 -0
  194. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-be47008304106395.js → consent-93a978443bf299db.js} +0 -0
  195. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-ae1b57589da7b175.js → custom-fields-9ecb803099082bf4.js} +0 -0
  196. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-23a6d7a921150188.js → domain-records-16fdd91a81074dd1.js} +0 -0
  197. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-2a9e8859ab4d9de6.js → domains-4cdd6001e7cb9aee.js} +0 -0
  198. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-4f9f0fdf9925ae90.js → email-templates-1914de830ce5cfc4.js} +0 -0
  199. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-46f7af35cee4a8bb.js → locations-2e635dcd11b78224.js} +0 -0
  200. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-a596a96cb8d0aa8e.js → organization-f547f1f33c12faf3.js} +0 -0
  201. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-6ed5fc2410e00857.js → regulations-7c02e469d8c5bd74.js} +0 -0
  202. /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-86811e3cda277e77.js → test-datasets-20b1193ed76c56b0.js} +0 -0
  203. /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-5a43f108d8047d5b.js → [id]-6e15332935f6b538.js} +0 -0
  204. /fides/ui-build/static/admin/_next/static/chunks/pages/{systems-045a841e22e85ea8.js → systems-fbc8761ef4d55516.js} +0 -0
  205. /fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-1b3f2d4bcb0e164d.js → taxonomy-4d7827fc9c46b6b8.js} +0 -0
  206. /fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-2cab41659f1ee7da.js → user-management-9cec020f89544426.js} +0 -0
fides/api/oauth/utils.py CHANGED
@@ -4,16 +4,16 @@ import json
4
4
  from datetime import datetime
5
5
  from functools import update_wrapper
6
6
  from types import FunctionType
7
- from typing import Any, Callable, Dict, List, Optional, Tuple
7
+ from typing import Any, Callable, Dict, List, Optional, Tuple, cast
8
8
 
9
- from fastapi import Depends, HTTPException, Security
9
+ from fastapi import Depends, HTTPException, Request, Security
10
10
  from fastapi.security import SecurityScopes
11
11
  from jose import exceptions, jwe
12
12
  from jose.constants import ALGORITHMS
13
13
  from loguru import logger
14
14
  from pydantic import ValidationError
15
15
  from sqlalchemy.orm import Session
16
- from starlette.status import HTTP_404_NOT_FOUND
16
+ from starlette.status import HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND
17
17
 
18
18
  from fides.api.api.deps import get_db
19
19
  from fides.api.common_exceptions import AuthenticationError, AuthorizationError
@@ -30,7 +30,7 @@ from fides.api.models.fides_user_permissions import FidesUserPermissions
30
30
  from fides.api.models.policy import PolicyPreWebhook
31
31
  from fides.api.models.pre_approval_webhook import PreApprovalWebhook
32
32
  from fides.api.models.privacy_request import RequestTask
33
- from fides.api.oauth.roles import get_scopes_from_roles
33
+ from fides.api.oauth.roles import ROLES_TO_SCOPES_MAPPING, get_scopes_from_roles
34
34
  from fides.api.request_context import set_user_id
35
35
  from fides.api.schemas.external_https import (
36
36
  DownloadTokenJWE,
@@ -80,6 +80,28 @@ def is_callback_token_expired(issued_at: Optional[datetime]) -> bool:
80
80
  ).total_seconds() / 60.0 > CONFIG.execution.privacy_request_delay_timeout
81
81
 
82
82
 
83
+ def is_token_invalidated(issued_at: datetime, client: ClientDetail) -> bool:
84
+ """
85
+ Return True if the token should be considered invalid due to security events
86
+ (e.g., user password reset) that occurred after the token was issued.
87
+
88
+ Any errors accessing related objects are logged and treated as non-invalidating.
89
+ """
90
+ try:
91
+ if (
92
+ client.user is not None
93
+ and client.user.password_reset_at is not None
94
+ and issued_at < client.user.password_reset_at
95
+ ):
96
+ return True
97
+ return False
98
+ except Exception as exc:
99
+ logger.exception(
100
+ "Unable to evaluate password reset timestamp for client user: {}", exc
101
+ )
102
+ return False
103
+
104
+
83
105
  def _get_webhook_jwe_or_error(
84
106
  security_scopes: SecurityScopes, authorization: str = Security(oauth2_scheme)
85
107
  ) -> WebhookJWE:
@@ -225,7 +247,7 @@ async def get_current_user(
225
247
  created_at=datetime.utcnow(),
226
248
  )
227
249
 
228
- return client.user # type: ignore[attr-defined]
250
+ return cast(FidesUser, client.user)
229
251
 
230
252
 
231
253
  def verify_callback_oauth_policy_pre_webhook(
@@ -370,8 +392,10 @@ def extract_token_and_load_client(
370
392
  logger.debug("Auth token expired.")
371
393
  raise AuthorizationError(detail="Not Authorized for this action")
372
394
 
395
+ issued_at_dt = datetime.fromisoformat(issued_at)
396
+
373
397
  if is_token_expired(
374
- datetime.fromisoformat(issued_at),
398
+ issued_at_dt,
375
399
  token_duration_override or CONFIG.security.oauth_access_token_expire_minutes,
376
400
  ):
377
401
  raise AuthorizationError(detail="Not Authorized for this action")
@@ -394,6 +418,12 @@ def extract_token_and_load_client(
394
418
  logger.debug("Auth token belongs to an invalid client_id.")
395
419
  raise AuthorizationError(detail="Not Authorized for this action")
396
420
 
421
+ # Invalidate tokens issued prior to the user's most recent password reset.
422
+ # This ensures any existing sessions are expired immediately after a password change.
423
+ if is_token_invalidated(issued_at_dt, client):
424
+ logger.debug("Auth token issued before latest password reset.")
425
+ raise AuthorizationError(detail="Not Authorized for this action")
426
+
397
427
  # Populate request-scoped context with the authenticated user identifier.
398
428
  # Prefer the linked user_id; fall back to the client id when this is the
399
429
  # special root client (which has no associated FidesUser row).
@@ -476,6 +506,87 @@ def has_scope_subset(user_scopes: List[str], endpoint_scopes: SecurityScopes) ->
476
506
  return set(endpoint_scopes.scopes).issubset(user_scopes)
477
507
 
478
508
 
509
+ def get_client_effective_scopes(client: "ClientDetail") -> List[str]:
510
+ """
511
+ Get all scopes available to a client, including both direct scopes and role-derived scopes.
512
+
513
+ Args:
514
+ client: The ClientDetail instance
515
+
516
+ Returns:
517
+ List of scope strings that the client has access to
518
+ """
519
+ effective_scopes = set()
520
+
521
+ # Add direct scopes
522
+ if client.scopes:
523
+ effective_scopes.update(client.scopes)
524
+
525
+ # Add role-derived scopes
526
+ if client.roles:
527
+ for role in client.roles:
528
+ role_scopes = ROLES_TO_SCOPES_MAPPING.get(role, [])
529
+ effective_scopes.update(role_scopes)
530
+
531
+ # Add user permission scopes if client is associated with a user
532
+ # Note: client.user is available via SQLAlchemy backref from FidesUser.client relationship
533
+ user = getattr(client, "user", None) # Use getattr to avoid mypy attr-defined error
534
+ if user and hasattr(user, "permissions") and user.permissions:
535
+ effective_scopes.update(user.permissions.total_scopes)
536
+
537
+ return sorted(list(effective_scopes))
538
+
539
+
540
+ def verify_client_can_assign_scopes(
541
+ request: "Request",
542
+ requesting_client: "ClientDetail",
543
+ scopes: List[str],
544
+ db: "Session",
545
+ ) -> None:
546
+ """
547
+ Verify that a requesting client has permission to assign the given scopes.
548
+
549
+ Raises HTTPException if the client lacks permission.
550
+ Root client is exempt from this check.
551
+
552
+ Args:
553
+ request: FastAPI request object containing Authorization header
554
+ requesting_client: The client making the request
555
+ scopes: List of scopes to be assigned
556
+ db: Database session
557
+
558
+ Raises:
559
+ HTTPException: If the client lacks permission to assign the scopes
560
+ """
561
+ # Root client can assign any scope
562
+ if requesting_client.id == CONFIG.security.oauth_root_client_id:
563
+ return
564
+
565
+ # Get the actual token scopes (not the client's database scopes)
566
+ authorization = request.headers.get("Authorization", "").replace("Bearer ", "")
567
+ token_data, _ = extract_token_and_load_client(authorization, db)
568
+
569
+ # Get token's effective scopes
570
+ token_scopes = token_data.get("scopes", [])
571
+
572
+ # Check if user has the scopes via roles as well
573
+ has_scope_via_role = has_permissions(
574
+ token_data=token_data,
575
+ client=requesting_client,
576
+ endpoint_scopes=SecurityScopes(scopes),
577
+ )
578
+
579
+ # If they don't have all scopes via direct assignment or roles, check individual scopes
580
+ if not has_scope_via_role:
581
+ unauthorized_scopes = set(scopes) - set(token_scopes)
582
+
583
+ if unauthorized_scopes:
584
+ raise HTTPException(
585
+ status_code=HTTP_403_FORBIDDEN,
586
+ detail=f"Cannot assign scopes that you do not have. Missing scopes: {sorted(unauthorized_scopes)}",
587
+ )
588
+
589
+
479
590
  def create_temporary_user_for_login_flow(config: FidesConfig) -> FidesUser:
480
591
  """
481
592
  Create a temporary FidesUser in-memory with an attached in-memory ClientDetail
@@ -0,0 +1,55 @@
1
+ import re
2
+ from typing import List
3
+
4
+ from pydantic import BaseModel, Field, field_validator
5
+
6
+
7
+ class PrivacyRequestRedactionPatternsRequest(BaseModel):
8
+ """Request schema for updating privacy request redaction patterns."""
9
+
10
+ patterns: List[str] = Field(
11
+ description="List of regex patterns used to redact dataset, collection, and field names in privacy request package reports",
12
+ max_length=100, # Limit number of patterns
13
+ )
14
+
15
+ @field_validator("patterns")
16
+ @classmethod
17
+ def validate_patterns(cls, patterns: List[str]) -> List[str]:
18
+ """Validate regex patterns with ReDoS protection via Pydantic's default rust-regex engine."""
19
+ if not patterns:
20
+ return patterns
21
+
22
+ validated_patterns = []
23
+ for i, pattern in enumerate(patterns):
24
+ if not isinstance(pattern, str):
25
+ raise ValueError(f"Pattern at index {i} must be a string")
26
+
27
+ pattern = pattern.strip()
28
+ if not pattern:
29
+ raise ValueError(f"Pattern at index {i} cannot be empty")
30
+
31
+ # Reasonable length limit for regex patterns
32
+ if len(pattern) > 500:
33
+ raise ValueError(
34
+ f"Pattern at index {i} is too long (max 500 characters)"
35
+ )
36
+
37
+ # Pydantic's rust-regex engine provides ReDoS protection
38
+ try:
39
+ re.compile(pattern, re.IGNORECASE)
40
+ except re.error as e:
41
+ raise ValueError(f"Invalid regex pattern at index {i}: {e}")
42
+
43
+ validated_patterns.append(pattern)
44
+
45
+ return validated_patterns
46
+
47
+
48
+ class PrivacyRequestRedactionPatternsResponse(BaseModel):
49
+ """Response schema for privacy request redaction patterns."""
50
+
51
+ patterns: List[str] = Field(
52
+ description="List of regex patterns used to redact dataset, collection, and field names in privacy request package reports"
53
+ )
54
+
55
+ model_config = {"from_attributes": True}
@@ -0,0 +1,231 @@
1
+ import re
2
+ from typing import Any, Dict, List, Literal, Optional, Set, Tuple
3
+
4
+ from loguru import logger
5
+ from sqlalchemy.orm import Session
6
+
7
+ from fides.api.models.privacy_request_redaction_pattern import (
8
+ PrivacyRequestRedactionPattern,
9
+ )
10
+ from fides.api.service.privacy_request.dsr_package.utils import (
11
+ get_redaction_entities_map_db,
12
+ )
13
+
14
+
15
+ class DSRDataPreprocessor:
16
+ """
17
+ Processes DSR data to apply name redaction before report generation.
18
+ """
19
+
20
+ def __init__(self, db: Session):
21
+ self.db = db
22
+ self.redaction_patterns: List[str] = (
23
+ PrivacyRequestRedactionPattern.get_patterns(db) or []
24
+ )
25
+ self.entities_to_redact: Set[str] = get_redaction_entities_map_db(db)
26
+
27
+ def process_dsr_data(self, dsr_data: dict[str, Any]) -> dict[str, Any]:
28
+ """Process the DSR data to apply all redaction upfront."""
29
+ if not self.redaction_patterns and not self.entities_to_redact:
30
+ return dsr_data
31
+
32
+ # First pass: collect and map dataset names
33
+ dataset_mapping = self._create_dataset_mapping(dsr_data)
34
+
35
+ # Second pass: process data with redaction
36
+ processed_data = {}
37
+ collection_indices: Dict[str, Dict[str, int]] = (
38
+ {}
39
+ ) # Track collection indices within each dataset
40
+
41
+ for key, rows in dsr_data.items():
42
+ # The "attachment" key is used to pass in the privacy request's attachments into `dsr_data`.
43
+ # We don't need to redact these.
44
+ if key == "attachments":
45
+ processed_data[key] = rows
46
+ continue
47
+
48
+ dataset_name, collection_name = self._parse_key(key, rows)
49
+
50
+ # Get redacted dataset name
51
+ redacted_dataset = dataset_mapping.get(dataset_name, dataset_name)
52
+
53
+ # Get redacted collection name (index per dataset)
54
+ if dataset_name not in collection_indices:
55
+ collection_indices[dataset_name] = {}
56
+
57
+ if collection_name not in collection_indices[dataset_name]:
58
+ collection_indices[dataset_name][collection_name] = (
59
+ len(collection_indices[dataset_name]) + 1
60
+ )
61
+
62
+ collection_index = collection_indices[dataset_name][collection_name]
63
+ redacted_collection = self._redact_name(
64
+ "collection", collection_name, collection_index, dataset_name
65
+ )
66
+
67
+ # Process rows
68
+ new_key = f"{redacted_dataset}:{redacted_collection}"
69
+ processed_data[new_key] = [
70
+ self._process_row(row, dataset_name, collection_name) for row in rows
71
+ ]
72
+
73
+ return processed_data
74
+
75
+ def _create_dataset_mapping(self, dsr_data: dict[str, Any]) -> Dict[str, str]:
76
+ """Create dataset name mapping with ordered numbering for redacted datasets."""
77
+
78
+ # Extract unique dataset names in order of appearance
79
+ dataset_names = []
80
+ for key, rows in dsr_data.items():
81
+ if key not in ["attachments", "dataset"]:
82
+ dataset_name, _ = self._parse_key(key, rows)
83
+ dataset_names.append(dataset_name)
84
+
85
+ unique_datasets = list(dict.fromkeys(dataset_names))
86
+
87
+ # Create mapping using position-based numbering for redacted datasets
88
+ mapping = {}
89
+
90
+ # Handle regular datasets
91
+ for index, name in enumerate(unique_datasets, 1):
92
+ hierarchical_key = self._build_hierarchical_key("dataset", name)
93
+ if self._should_redact(name, hierarchical_key):
94
+ mapping[name] = f"dataset_{index}"
95
+ else:
96
+ mapping[name] = name # Keep original name
97
+
98
+ # Special "dataset" and "attachment" cases comes last
99
+ # These keys are reserved for additional data and do not need to be redacted
100
+ if "dataset" in dsr_data.keys():
101
+ mapping["dataset"] = "dataset"
102
+
103
+ if "attachments" in dsr_data.keys():
104
+ mapping["attachments"] = "attachments"
105
+
106
+ return mapping
107
+
108
+ def _parse_key(self, key: str, rows: List[dict]) -> Tuple[str, str]:
109
+ """Parse a key into dataset and collection names."""
110
+ if ":" in key:
111
+ parts = key.split(":", 1)
112
+ return parts[0], parts[1]
113
+
114
+ # Fallback logic
115
+ for row in rows:
116
+ if "system_name" in row:
117
+ return row["system_name"], key
118
+ return "manual", key
119
+
120
+ def _should_redact(self, name: str, hierarchical_key: str) -> bool:
121
+ """Check if a name should be redacted."""
122
+ if hierarchical_key in self.entities_to_redact:
123
+ return True
124
+
125
+ for pattern in self.redaction_patterns:
126
+ try:
127
+ if re.search(pattern, name, re.IGNORECASE):
128
+ return True
129
+ except re.error:
130
+ logger.warning(f"Invalid regex pattern: {pattern}")
131
+
132
+ return False
133
+
134
+ def _redact_name(
135
+ self,
136
+ name_type: Literal["dataset", "collection", "field"],
137
+ name: str,
138
+ index: int,
139
+ dataset_name: Optional[str] = None,
140
+ collection_name: Optional[str] = None,
141
+ ) -> str:
142
+ """Apply redaction to a name based on patterns and configurations."""
143
+ hierarchical_key = self._build_hierarchical_key(
144
+ name_type, name, dataset_name, collection_name
145
+ )
146
+
147
+ if self._should_redact(name, hierarchical_key):
148
+ return f"{name_type}_{index}"
149
+
150
+ return name
151
+
152
+ def _build_hierarchical_key(
153
+ self,
154
+ name_type: Literal["dataset", "collection", "field"],
155
+ name: str,
156
+ dataset_name: Optional[str] = None,
157
+ collection_name: Optional[str] = None,
158
+ ) -> str:
159
+ """Build hierarchical key for entity lookup."""
160
+ if name_type == "dataset":
161
+ return name
162
+ if name_type == "collection" and dataset_name:
163
+ return f"{dataset_name}.{name}"
164
+ if name_type == "field" and dataset_name and collection_name:
165
+ return f"{dataset_name}.{collection_name}.{name}"
166
+ return name
167
+
168
+ def _process_row(
169
+ self, row: dict[str, Any], dataset_name: str, collection_name: str
170
+ ) -> dict[str, Any]:
171
+ """Process a single row with field redaction."""
172
+ processed = {}
173
+
174
+ # Use enumerate for positional indexing (matches original)
175
+ for field_index, (field_name, value) in enumerate(row.items(), start=1):
176
+ redacted_field = self._redact_name(
177
+ "field", field_name, field_index, dataset_name, collection_name
178
+ )
179
+
180
+ # Process nested values
181
+ base_path = f"{dataset_name}.{collection_name}.{field_name}"
182
+ processed[redacted_field] = self._process_nested_value(value, base_path)
183
+
184
+ return processed
185
+
186
+ def _process_nested_value(
187
+ self,
188
+ value: Any,
189
+ current_path: str,
190
+ field_index_counter: Optional[Dict[str, Dict[str, int]]] = None,
191
+ ) -> Any:
192
+ """Recursively process nested values matching original logic."""
193
+ if field_index_counter is None:
194
+ field_index_counter = {}
195
+
196
+ if isinstance(value, dict):
197
+ processed = {}
198
+ level_key = f"{current_path}_fields"
199
+
200
+ if level_key not in field_index_counter:
201
+ field_index_counter[level_key] = {}
202
+
203
+ for field_name, field_value in value.items():
204
+ full_path = f"{current_path}.{field_name}"
205
+
206
+ # Get or create index for this field at this level
207
+ if field_name not in field_index_counter[level_key]:
208
+ field_index_counter[level_key][field_name] = (
209
+ len(field_index_counter[level_key]) + 1
210
+ )
211
+
212
+ if self._should_redact(field_name, full_path):
213
+ redacted_name = (
214
+ f"field_{field_index_counter[level_key][field_name]}"
215
+ )
216
+ else:
217
+ redacted_name = field_name
218
+
219
+ processed[redacted_name] = self._process_nested_value(
220
+ field_value, full_path, field_index_counter
221
+ )
222
+
223
+ return processed
224
+
225
+ if isinstance(value, list):
226
+ return [
227
+ self._process_nested_value(item, current_path, field_index_counter)
228
+ for item in value
229
+ ]
230
+
231
+ return value