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.
- {ethyca_fides-2.69.0rc9.dist-info → ethyca_fides-2.69.1.dist-info}/METADATA +2 -2
- {ethyca_fides-2.69.0rc9.dist-info → ethyca_fides-2.69.1.dist-info}/RECORD +204 -195
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/78dbe23d8204_adding_privacy_request_redaction_patterns.py +52 -0
- fides/api/api/v1/api.py +2 -0
- fides/api/api/v1/endpoints/dsr_package_link.py +2 -2
- fides/api/api/v1/endpoints/oauth_endpoints.py +20 -6
- fides/api/api/v1/endpoints/privacy_request_redaction_patterns_endpoints.py +95 -0
- fides/api/api/v1/endpoints/user_endpoints.py +28 -1
- fides/api/app_setup.py +16 -2
- fides/api/db/base.py +3 -0
- fides/api/main.py +22 -0
- fides/api/models/client.py +1 -0
- fides/api/models/privacy_request_redaction_pattern.py +64 -0
- fides/api/oauth/utils.py +117 -6
- fides/api/schemas/privacy_request_redaction_patterns.py +55 -0
- fides/api/service/privacy_request/dsr_package/dsr_data_preprocessor.py +231 -0
- fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +286 -120
- fides/api/service/privacy_request/dsr_package/templates/attachments_index.html +4 -2
- fides/api/service/privacy_request/dsr_package/templates/collection_index.html +3 -1
- fides/api/service/privacy_request/dsr_package/templates/dataset_index.html +1 -1
- fides/api/service/privacy_request/dsr_package/utils.py +268 -0
- fides/api/service/privacy_request/request_runner_service.py +8 -2
- fides/api/service/storage/streaming/smart_open_streaming_storage.py +107 -170
- fides/api/service/storage/util.py +579 -0
- fides/api/task/manual/manual_task_graph_task.py +11 -9
- fides/api/tasks/storage.py +2 -2
- fides/api/util/endpoint_utils.py +0 -13
- fides/api/util/rate_limit.py +194 -0
- fides/common/api/scope_registry.py +8 -0
- fides/common/api/v1/urn_registry.py +3 -0
- fides/config/redis_settings.py +27 -3
- fides/config/security_settings.py +31 -9
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/1TigfgzjzHeoVqRLNIMYa/_buildManifest.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4831-fd99c0b3784de128.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-ef8e1c986bc5b795.js → _app-fcdad91f6f66292b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/privacy-requests-2ecc073f41628f62.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-de8cb3739ab99c09.js → new-92f52c43f522a350.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-05d61c80a556b2d5.js → [id]-64452dfae2c5e614.js} +1 -1
- fides/ui-build/static/admin/add-systems/manual.html +1 -1
- fides/ui-build/static/admin/add-systems/multiple.html +1 -1
- fides/ui-build/static/admin/add-systems.html +1 -1
- fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
- fides/ui-build/static/admin/consent/configure.html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
- fides/ui-build/static/admin/consent/properties.html +1 -1
- fides/ui-build/static/admin/consent/reporting.html +1 -1
- fides/ui-build/static/admin/consent.html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
- fides/ui-build/static/admin/data-catalog.html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
- fides/ui-build/static/admin/data-discovery/activity.html +1 -1
- fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-discovery/detection.html +1 -1
- fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
- fides/ui-build/static/admin/datamap.html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
- fides/ui-build/static/admin/dataset/new.html +1 -1
- fides/ui-build/static/admin/dataset.html +1 -1
- fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
- fides/ui-build/static/admin/datastore-connection/new.html +1 -1
- fides/ui-build/static/admin/datastore-connection.html +1 -1
- fides/ui-build/static/admin/index.html +1 -1
- fides/ui-build/static/admin/integrations/[id].html +1 -1
- fides/ui-build/static/admin/integrations.html +1 -1
- fides/ui-build/static/admin/login/[provider].html +1 -1
- fides/ui-build/static/admin/login.html +1 -1
- fides/ui-build/static/admin/messaging/[id].html +1 -1
- fides/ui-build/static/admin/messaging/add-template.html +1 -1
- fides/ui-build/static/admin/messaging.html +1 -1
- fides/ui-build/static/admin/poc/ant-components.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
- fides/ui-build/static/admin/poc/forms.html +1 -1
- fides/ui-build/static/admin/poc/table-migration.html +1 -1
- fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
- fides/ui-build/static/admin/privacy-requests.html +1 -1
- fides/ui-build/static/admin/properties/[id].html +1 -1
- fides/ui-build/static/admin/properties/add-property.html +1 -1
- fides/ui-build/static/admin/properties.html +1 -1
- fides/ui-build/static/admin/reporting/datamap.html +1 -1
- fides/ui-build/static/admin/settings/about/alpha.html +1 -1
- fides/ui-build/static/admin/settings/about.html +1 -1
- fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
- fides/ui-build/static/admin/settings/consent.html +1 -1
- fides/ui-build/static/admin/settings/custom-fields.html +1 -1
- fides/ui-build/static/admin/settings/domain-records.html +1 -1
- fides/ui-build/static/admin/settings/domains.html +1 -1
- fides/ui-build/static/admin/settings/email-templates.html +1 -1
- fides/ui-build/static/admin/settings/locations.html +1 -1
- fides/ui-build/static/admin/settings/organization.html +1 -1
- fides/ui-build/static/admin/settings/privacy-requests.html +1 -0
- fides/ui-build/static/admin/settings/regulations.html +1 -1
- fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
- fides/ui-build/static/admin/systems/configure/[id].html +1 -1
- fides/ui-build/static/admin/systems.html +1 -1
- fides/ui-build/static/admin/taxonomy.html +1 -1
- fides/ui-build/static/admin/user-management/new.html +1 -1
- fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
- fides/ui-build/static/admin/user-management.html +1 -1
- fides/ui-build/static/admin/_next/static/XiHm-6CdVChTC5rbN9GtT/_buildManifest.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4121-c8d5d717e31899e1.js +0 -1
- {ethyca_fides-2.69.0rc9.dist-info → ethyca_fides-2.69.1.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.69.0rc9.dist-info → ethyca_fides-2.69.1.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.69.0rc9.dist-info → ethyca_fides-2.69.1.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.69.0rc9.dist-info → ethyca_fides-2.69.1.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{XiHm-6CdVChTC5rbN9GtT → 1TigfgzjzHeoVqRLNIMYa}/_ssgManifest.js +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{1817-3d9e110e007853f0.js → 1817-0ca16d288fad916d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3620-31ebb43dba84cbbd.js → 3620-602eb74dc896d556.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3729-a1ca1608efc11ac4.js → 3729-c17ac8031a4c4fd1.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3872-a91143aa35fa8ef8.js → 3872-f78dec02f0d959ae.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{4608-23bbd4c3c4a59f42.js → 4608-be8cba73f5d7c326.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{4786-0827aae7aceadd22.js → 4786-61154adf88e448e1.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{4808-78ca630f2d2503cd.js → 4808-dd4157aa72648068.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{5487-8c635883dcaa9c2a.js → 5487-02d00bad7c6830e0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6084-0096d7de64ef8015.js → 6084-c153669d5567e242.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6954-9d46e2276c461c26.js → 6954-5296188c19d7d0ac.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{7476-d1b0af9ade392e5b.js → 7476-45c5088baa8b66af.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{7630-da0a7ce4e3a0d62c.js → 7630-7ed6c6117775dffe.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{787-3499983fa346b380.js → 787-a8c7eab617e2fceb.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{79-f197fc4db8d530e5.js → 79-65674011d455af4d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{796-db1e30119ea973c7.js → 796-9e1ca1a4030707c5.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{8002-971e29181f72edd1.js → 8002-24af20d679efc04e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9826-b0b3d3cfb13bfbc1.js → 9826-dbae8dee941a7fac.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-9dc7e70ab5b05723.js → manual-ace203dfacacbdc4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-4b79a1652297ed9a.js → multiple-920fb469e0dda1d2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-1632a59203fe8eab.js → add-systems-bd0d82078e67cac3.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-1ca9df7ca91bd101.js → add-vendors-406170eaae4329c6.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-07bdbc9ae4137db4.js → configure-7207ab23bdb36ce8.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-2795cd4115a77c94.js → privacy-experience-9dda4de5ec580279.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-e02921dc82dccbb1.js → [id]-b378576cba255609.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-98f9e4ba3610628a.js → new-2ca1de7b88094ab0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-17ed82777810d1c6.js → privacy-notices-0d4844d0b808e6e4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-09610b10923d9268.js → consent-3e8bdefe714254ec.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-da1a48336daff6f8.js → [resourceUrn]-2c29ff7a01198f30.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-d8e776f1e64e4ba8.js → [projectUrn]-04cfe2cfba7b7cd8.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-75b9629b0d9cdf96.js → projects-5f2d7b24804f861f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-470da05db63767cd.js → [resourceUrn]-8eb581024bc0172f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{resources-6c3714ee97a718c1.js → resources-de704de849960f01.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-6984c033b8fe3a13.js → data-catalog-30108b00ac769fc3.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/{[systemId]-2f0a33ef9ba1f1da.js → [systemId]-e1ba213fb666b3f4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/{[monitorId]-e9d4f25b20ff6781.js → [monitorId]-6d133580045abdda.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{action-center-9c428d3ef0985915.js → action-center-9a81d42a474e1e48.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-c3a97e6721ca0abe.js → [resourceUrn]-8f736b078e9842da.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-a0a7de552ef71f5b.js → detection-eb814e3c22807871.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-109754fec0755339.js → [resourceUrn]-6875b7783fcfda2f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-88654783b06b3b21.js → discovery-172dbd7740e212ca.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-89136e6800dc9369.js → datamap-c7390e046b2e2b7f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-8f58192dcb54883d.js → [...subfieldNames]-dfd71c1e9c458b89.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-dcb4ab380a77aa1e.js → [collectionName]-7cdc42ec5493b83d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-6f16d43071fb9c11.js → [datasetId]-e12b11ba15bc3fc1.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-97f06e21580f1f6a.js → new-e32fccc4ca520d2b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-674bb3940f088ecc.js → dataset-7c59a6abf6ba6207.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-6f77d8647fca71e0.js → [id]-927b7e476c4b47d0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-821dd1269834cfa2.js → new-cbe100d50df34285.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{datastore-connection-23e4caf79faa8106.js → datastore-connection-cce20440b177050b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{index-23eb64eed81dcb69.js → index-6cd8708106331b8d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/integrations/{[id]-3a4cd3fe9094fba3.js → [id]-4c3c413a2668df53.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-57e618d7b16ac69a.js → integrations-95402b5001c07ef2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-c9a323eb6a929476.js → [id]-3c6dc2f6e6bae960.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-b9bb09e46921a590.js → add-template-4a6d4023a7791be8.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{messaging-82c631a12b5a008c.js → messaging-76b204c9b98d656f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{table-migration-38360083348c3d6c.js → table-migration-48500551fd6a7602.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{[id]-0d0bb9eb004a3336.js → [id]-0f25a76dd18c5e20.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{messaging-f9320a58f489f5b7.js → messaging-ad6ad3e5bd72765d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{storage-d0cfa8aeddd43a40.js → storage-6032d82f0fc2893d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-72ca94ec5ed85733.js → configure-d83e5bd52a638234.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{privacy-requests-5a5edc8a4aa7c30a.js → privacy-requests-baf31c3e4b081046.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-5ec775c4904fdbfe.js → [id]-e784c05d056b2371.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-a6812c0916f2949e.js → add-property-0a7a2db148a7561a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/{alpha-3e72e9f91991c119.js → alpha-a82f3df840d5c1b5.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-6aab092f4871cecb.js → about-d06fb16487705b9d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-be47008304106395.js → consent-93a978443bf299db.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-ae1b57589da7b175.js → custom-fields-9ecb803099082bf4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-23a6d7a921150188.js → domain-records-16fdd91a81074dd1.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-2a9e8859ab4d9de6.js → domains-4cdd6001e7cb9aee.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-4f9f0fdf9925ae90.js → email-templates-1914de830ce5cfc4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-46f7af35cee4a8bb.js → locations-2e635dcd11b78224.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-a596a96cb8d0aa8e.js → organization-f547f1f33c12faf3.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-6ed5fc2410e00857.js → regulations-7c02e469d8c5bd74.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-86811e3cda277e77.js → test-datasets-20b1193ed76c56b0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-5a43f108d8047d5b.js → [id]-6e15332935f6b538.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{systems-045a841e22e85ea8.js → systems-fbc8761ef4d55516.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-1b3f2d4bcb0e164d.js → taxonomy-4d7827fc9c46b6b8.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-2cab41659f1ee7da.js → user-management-9cec020f89544426.js} +0 -0
fides/_version.py
CHANGED
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-09-
|
|
11
|
+
"date": "2025-09-03T13:05:01-0700",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "2.69.
|
|
14
|
+
"full-revisionid": "239cb598ba6d808dd1d221506b13f5a2912c8357",
|
|
15
|
+
"version": "2.69.1"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
fides/api/alembic/migrations/versions/78dbe23d8204_adding_privacy_request_redaction_patterns.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""adding privacy requeste redaction patterns
|
|
2
|
+
|
|
3
|
+
Revision ID: 78dbe23d8204
|
|
4
|
+
Revises: b1a2c3d4e5f6
|
|
5
|
+
Create Date: 2025-08-30 05:40:17.816172
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
from alembic import op
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = "78dbe23d8204"
|
|
14
|
+
down_revision = "b1a2c3d4e5f6"
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade():
|
|
20
|
+
op.create_table(
|
|
21
|
+
"privacy_request_redaction_pattern",
|
|
22
|
+
sa.Column("id", sa.String(length=255), nullable=False),
|
|
23
|
+
sa.Column(
|
|
24
|
+
"created_at",
|
|
25
|
+
sa.DateTime(timezone=True),
|
|
26
|
+
server_default=sa.text("now()"),
|
|
27
|
+
nullable=True,
|
|
28
|
+
),
|
|
29
|
+
sa.Column(
|
|
30
|
+
"updated_at",
|
|
31
|
+
sa.DateTime(timezone=True),
|
|
32
|
+
server_default=sa.text("now()"),
|
|
33
|
+
nullable=True,
|
|
34
|
+
),
|
|
35
|
+
sa.Column("pattern", sa.String(), nullable=False),
|
|
36
|
+
sa.PrimaryKeyConstraint("id"),
|
|
37
|
+
sa.UniqueConstraint("pattern"),
|
|
38
|
+
)
|
|
39
|
+
op.create_index(
|
|
40
|
+
op.f("ix_privacy_request_redaction_pattern_id"),
|
|
41
|
+
"privacy_request_redaction_pattern",
|
|
42
|
+
["id"],
|
|
43
|
+
unique=False,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def downgrade():
|
|
48
|
+
op.drop_index(
|
|
49
|
+
op.f("ix_privacy_request_redaction_pattern_id"),
|
|
50
|
+
table_name="privacy_request_redaction_pattern",
|
|
51
|
+
)
|
|
52
|
+
op.drop_table("privacy_request_redaction_pattern")
|
fides/api/api/v1/api.py
CHANGED
|
@@ -17,6 +17,7 @@ from fides.api.api.v1.endpoints import (
|
|
|
17
17
|
policy_webhook_endpoints,
|
|
18
18
|
pre_approval_webhook_endpoints,
|
|
19
19
|
privacy_request_endpoints,
|
|
20
|
+
privacy_request_redaction_patterns_endpoints,
|
|
20
21
|
registration_endpoints,
|
|
21
22
|
saas_config_endpoints,
|
|
22
23
|
storage_endpoints,
|
|
@@ -41,6 +42,7 @@ api_router.include_router(policy_endpoints.router)
|
|
|
41
42
|
api_router.include_router(policy_webhook_endpoints.router)
|
|
42
43
|
api_router.include_router(pre_approval_webhook_endpoints.router)
|
|
43
44
|
api_router.include_router(privacy_request_endpoints.router)
|
|
45
|
+
api_router.include_router(privacy_request_redaction_patterns_endpoints.router)
|
|
44
46
|
api_router.include_router(identity_verification_endpoints.router)
|
|
45
47
|
api_router.include_router(storage_endpoints.router)
|
|
46
48
|
api_router.include_router(messaging_endpoints.router)
|
|
@@ -21,7 +21,7 @@ from fides.api.schemas.privacy_request import PrivacyRequestStatus
|
|
|
21
21
|
from fides.api.schemas.storage.storage import StorageType
|
|
22
22
|
from fides.api.service.storage.streaming.s3 import S3StorageClient
|
|
23
23
|
from fides.api.util.api_router import APIRouter
|
|
24
|
-
from fides.api.util.
|
|
24
|
+
from fides.api.util.rate_limit import fides_limiter
|
|
25
25
|
from fides.common.api.v1.urn_registry import PRIVACY_CENTER_DSR_PACKAGE, V1_URL_PREFIX
|
|
26
26
|
from fides.config import CONFIG
|
|
27
27
|
|
|
@@ -62,7 +62,7 @@ def raise_error(status_code: int, detail: str) -> None:
|
|
|
62
62
|
PRIVACY_CENTER_DSR_PACKAGE,
|
|
63
63
|
status_code=HTTP_302_FOUND,
|
|
64
64
|
)
|
|
65
|
-
@fides_limiter.limit(CONFIG.security.
|
|
65
|
+
@fides_limiter.limit(CONFIG.security.request_rate_limit)
|
|
66
66
|
def get_access_results_urls(
|
|
67
67
|
privacy_request_id: str,
|
|
68
68
|
token: str,
|
|
@@ -8,8 +8,8 @@ from loguru import logger
|
|
|
8
8
|
from sqlalchemy.orm import Session
|
|
9
9
|
from starlette.status import (
|
|
10
10
|
HTTP_400_BAD_REQUEST,
|
|
11
|
+
HTTP_403_FORBIDDEN,
|
|
11
12
|
HTTP_404_NOT_FOUND,
|
|
12
|
-
HTTP_422_UNPROCESSABLE_ENTITY,
|
|
13
13
|
)
|
|
14
14
|
|
|
15
15
|
from fides.api.api.deps import get_db
|
|
@@ -26,7 +26,7 @@ from fides.api.models.client import ClientDetail
|
|
|
26
26
|
from fides.api.models.connectionconfig import ConnectionConfig, ConnectionTestStatus
|
|
27
27
|
from fides.api.models.fides_user import FidesUser
|
|
28
28
|
from fides.api.oauth.roles import ROLES_TO_SCOPES_MAPPING
|
|
29
|
-
from fides.api.oauth.utils import verify_oauth_client
|
|
29
|
+
from fides.api.oauth.utils import verify_client_can_assign_scopes, verify_oauth_client
|
|
30
30
|
from fides.api.schemas.client import ClientCreatedResponse
|
|
31
31
|
from fides.api.schemas.oauth import AccessToken, OAuth2ClientCredentialsRequestForm
|
|
32
32
|
from fides.api.service.authentication.authentication_strategy import (
|
|
@@ -37,6 +37,7 @@ from fides.api.service.authentication.authentication_strategy_oauth2_authorizati
|
|
|
37
37
|
)
|
|
38
38
|
from fides.api.util.api_router import APIRouter
|
|
39
39
|
from fides.api.util.connection_util import connection_status
|
|
40
|
+
from fides.api.util.rate_limit import fides_limiter
|
|
40
41
|
from fides.common.api.scope_registry import (
|
|
41
42
|
CLIENT_CREATE,
|
|
42
43
|
CLIENT_DELETE,
|
|
@@ -65,6 +66,7 @@ router = APIRouter(tags=["OAuth"], prefix=V1_URL_PREFIX)
|
|
|
65
66
|
TOKEN,
|
|
66
67
|
response_model=AccessToken,
|
|
67
68
|
)
|
|
69
|
+
@fides_limiter.limit(CONFIG.security.auth_rate_limit)
|
|
68
70
|
async def acquire_access_token(
|
|
69
71
|
request: Request,
|
|
70
72
|
response: Response,
|
|
@@ -121,22 +123,28 @@ async def acquire_access_token(
|
|
|
121
123
|
|
|
122
124
|
@router.post(
|
|
123
125
|
CLIENT,
|
|
124
|
-
dependencies=[Security(verify_oauth_client, scopes=[CLIENT_CREATE])],
|
|
125
126
|
response_model=ClientCreatedResponse,
|
|
126
127
|
)
|
|
127
128
|
def create_client(
|
|
128
129
|
*,
|
|
130
|
+
request: Request,
|
|
129
131
|
db: Session = Depends(get_db),
|
|
130
132
|
scopes: List[str] = Body([]),
|
|
133
|
+
requesting_client: ClientDetail = Security(
|
|
134
|
+
verify_oauth_client, scopes=[CLIENT_CREATE]
|
|
135
|
+
),
|
|
131
136
|
) -> ClientCreatedResponse:
|
|
132
137
|
"""Creates a new client and returns the credentials. Only direct scopes can be added to the client via this endpoint."""
|
|
133
138
|
logger.info("Creating new client")
|
|
134
139
|
if not all(scope in SCOPE_REGISTRY for scope in scopes):
|
|
135
140
|
raise HTTPException(
|
|
136
|
-
status_code=
|
|
141
|
+
status_code=HTTP_403_FORBIDDEN,
|
|
137
142
|
detail=f"Invalid Scope. Scopes must be one of {SCOPE_REGISTRY}.",
|
|
138
143
|
)
|
|
139
144
|
|
|
145
|
+
# Security check: Verify that the requesting client has all the scopes they're trying to assign
|
|
146
|
+
verify_client_can_assign_scopes(request, requesting_client, scopes, db)
|
|
147
|
+
|
|
140
148
|
client, secret = ClientDetail.create_client_and_secret(
|
|
141
149
|
db,
|
|
142
150
|
CONFIG.security.oauth_client_id_length_bytes,
|
|
@@ -178,13 +186,16 @@ def get_client_scopes(client_id: str, db: Session = Depends(get_db)) -> List[str
|
|
|
178
186
|
|
|
179
187
|
@router.put(
|
|
180
188
|
CLIENT_SCOPE,
|
|
181
|
-
dependencies=[Security(verify_oauth_client, scopes=[CLIENT_UPDATE])],
|
|
182
189
|
response_model=None,
|
|
183
190
|
)
|
|
184
191
|
def set_client_scopes(
|
|
185
192
|
client_id: str,
|
|
186
193
|
scopes: List[str],
|
|
194
|
+
request: Request,
|
|
187
195
|
db: Session = Depends(get_db),
|
|
196
|
+
requesting_client: ClientDetail = Security(
|
|
197
|
+
verify_oauth_client, scopes=[CLIENT_UPDATE]
|
|
198
|
+
),
|
|
188
199
|
) -> None:
|
|
189
200
|
"""Overwrites the client's directly-assigned scopes with those provided.
|
|
190
201
|
Roles cannot be edited via this endpoint.
|
|
@@ -195,10 +206,13 @@ def set_client_scopes(
|
|
|
195
206
|
|
|
196
207
|
if not all(elem in SCOPE_REGISTRY for elem in scopes):
|
|
197
208
|
raise HTTPException(
|
|
198
|
-
status_code=
|
|
209
|
+
status_code=HTTP_403_FORBIDDEN,
|
|
199
210
|
detail=f"Invalid Scope. Scopes must be one of {SCOPE_REGISTRY}.",
|
|
200
211
|
)
|
|
201
212
|
|
|
213
|
+
# Security check: Verify that the requesting client has all the scopes they're trying to assign
|
|
214
|
+
verify_client_can_assign_scopes(request, requesting_client, scopes, db)
|
|
215
|
+
|
|
202
216
|
logger.info("Updating client scopes")
|
|
203
217
|
client.update(db, data={"scopes": scopes})
|
|
204
218
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from fastapi import Depends, HTTPException, Security
|
|
2
|
+
from fastapi.routing import APIRouter
|
|
3
|
+
from loguru import logger
|
|
4
|
+
from sqlalchemy.orm import Session
|
|
5
|
+
from starlette.status import HTTP_200_OK
|
|
6
|
+
|
|
7
|
+
from fides.api.api.deps import get_db
|
|
8
|
+
from fides.api.models.privacy_request_redaction_pattern import (
|
|
9
|
+
PrivacyRequestRedactionPattern,
|
|
10
|
+
)
|
|
11
|
+
from fides.api.oauth.utils import verify_oauth_client
|
|
12
|
+
from fides.api.schemas.privacy_request_redaction_patterns import (
|
|
13
|
+
PrivacyRequestRedactionPatternsRequest,
|
|
14
|
+
PrivacyRequestRedactionPatternsResponse,
|
|
15
|
+
)
|
|
16
|
+
from fides.common.api.scope_registry import (
|
|
17
|
+
PRIVACY_REQUEST_REDACTION_PATTERNS_READ,
|
|
18
|
+
PRIVACY_REQUEST_REDACTION_PATTERNS_UPDATE,
|
|
19
|
+
)
|
|
20
|
+
from fides.common.api.v1.urn_registry import (
|
|
21
|
+
PRIVACY_REQUEST_REDACTION_PATTERNS,
|
|
22
|
+
V1_URL_PREFIX,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
router = APIRouter(
|
|
26
|
+
tags=["Privacy Request Redaction Patterns"],
|
|
27
|
+
prefix=V1_URL_PREFIX,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@router.get(
|
|
32
|
+
PRIVACY_REQUEST_REDACTION_PATTERNS,
|
|
33
|
+
status_code=HTTP_200_OK,
|
|
34
|
+
response_model=PrivacyRequestRedactionPatternsResponse,
|
|
35
|
+
dependencies=[
|
|
36
|
+
Security(verify_oauth_client, scopes=[PRIVACY_REQUEST_REDACTION_PATTERNS_READ])
|
|
37
|
+
],
|
|
38
|
+
)
|
|
39
|
+
def get_privacy_request_redaction_patterns(
|
|
40
|
+
*, db: Session = Depends(get_db)
|
|
41
|
+
) -> PrivacyRequestRedactionPatternsResponse:
|
|
42
|
+
"""
|
|
43
|
+
Get the current privacy request redaction patterns configuration.
|
|
44
|
+
|
|
45
|
+
Returns the list of regex patterns used to mask dataset, collection,
|
|
46
|
+
and field names in privacy request package reports.
|
|
47
|
+
"""
|
|
48
|
+
logger.info("Getting privacy request redaction patterns configuration")
|
|
49
|
+
|
|
50
|
+
patterns = PrivacyRequestRedactionPattern.get_patterns(db)
|
|
51
|
+
return PrivacyRequestRedactionPatternsResponse(patterns=patterns)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@router.put(
|
|
55
|
+
PRIVACY_REQUEST_REDACTION_PATTERNS,
|
|
56
|
+
status_code=HTTP_200_OK,
|
|
57
|
+
response_model=PrivacyRequestRedactionPatternsResponse,
|
|
58
|
+
dependencies=[
|
|
59
|
+
Security(
|
|
60
|
+
verify_oauth_client, scopes=[PRIVACY_REQUEST_REDACTION_PATTERNS_UPDATE]
|
|
61
|
+
)
|
|
62
|
+
],
|
|
63
|
+
)
|
|
64
|
+
def update_privacy_request_redaction_patterns(
|
|
65
|
+
*,
|
|
66
|
+
db: Session = Depends(get_db),
|
|
67
|
+
request: PrivacyRequestRedactionPatternsRequest,
|
|
68
|
+
) -> PrivacyRequestRedactionPatternsResponse:
|
|
69
|
+
"""
|
|
70
|
+
Update the privacy request redaction patterns configuration.
|
|
71
|
+
|
|
72
|
+
This is a complete replacement of the patterns list. To clear all patterns,
|
|
73
|
+
send an empty list.
|
|
74
|
+
"""
|
|
75
|
+
logger.info(
|
|
76
|
+
"Updating privacy request redaction patterns configuration with {} patterns",
|
|
77
|
+
len(request.patterns),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
updated = PrivacyRequestRedactionPattern.replace_patterns(
|
|
82
|
+
db=db, patterns=request.patterns
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
logger.info("Successfully updated privacy request redaction patterns")
|
|
86
|
+
return PrivacyRequestRedactionPatternsResponse(patterns=updated)
|
|
87
|
+
|
|
88
|
+
except Exception as exc:
|
|
89
|
+
logger.exception(
|
|
90
|
+
"Failed to update privacy request redaction patterns: {}", str(exc)
|
|
91
|
+
)
|
|
92
|
+
raise HTTPException(
|
|
93
|
+
status_code=500,
|
|
94
|
+
detail="Failed to update privacy request redaction patterns",
|
|
95
|
+
) from exc
|
|
@@ -5,7 +5,7 @@ from datetime import datetime
|
|
|
5
5
|
from typing import List, Optional
|
|
6
6
|
|
|
7
7
|
import jose.exceptions
|
|
8
|
-
from fastapi import Depends, HTTPException, Security
|
|
8
|
+
from fastapi import Depends, HTTPException, Request, Response, Security
|
|
9
9
|
from fastapi.security import SecurityScopes
|
|
10
10
|
from fastapi_pagination import Page, Params
|
|
11
11
|
from fastapi_pagination.bases import AbstractPage
|
|
@@ -59,6 +59,7 @@ from fides.api.schemas.user import (
|
|
|
59
59
|
)
|
|
60
60
|
from fides.api.service.deps import get_user_service
|
|
61
61
|
from fides.api.util.api_router import APIRouter
|
|
62
|
+
from fides.api.util.rate_limit import fides_limiter
|
|
62
63
|
from fides.common.api.scope_registry import (
|
|
63
64
|
SCOPE_REGISTRY,
|
|
64
65
|
SYSTEM_MANAGER_DELETE,
|
|
@@ -207,6 +208,17 @@ def update_user_password(
|
|
|
207
208
|
|
|
208
209
|
current_user.update_password(db=db, new_password=data.new_password)
|
|
209
210
|
|
|
211
|
+
# Delete the user's associated OAuth client to invalidate all existing sessions
|
|
212
|
+
if current_user.client:
|
|
213
|
+
try:
|
|
214
|
+
current_user.client.delete(db)
|
|
215
|
+
except Exception as exc:
|
|
216
|
+
logger.exception(
|
|
217
|
+
"Unable to delete user client during password reset for user {}: {}",
|
|
218
|
+
current_user.id,
|
|
219
|
+
exc,
|
|
220
|
+
)
|
|
221
|
+
|
|
210
222
|
logger.info("Updated user with id: '{}'.", current_user.id)
|
|
211
223
|
return current_user
|
|
212
224
|
|
|
@@ -235,6 +247,18 @@ def force_update_password(
|
|
|
235
247
|
)
|
|
236
248
|
|
|
237
249
|
user.update_password(db=db, new_password=data.new_password)
|
|
250
|
+
|
|
251
|
+
# Delete the user's associated OAuth client to invalidate all existing sessions
|
|
252
|
+
if user.client:
|
|
253
|
+
try:
|
|
254
|
+
user.client.delete(db)
|
|
255
|
+
except Exception as exc:
|
|
256
|
+
logger.exception(
|
|
257
|
+
"Unable to delete user client during admin-forced password reset for user {}: {}",
|
|
258
|
+
user.id,
|
|
259
|
+
exc,
|
|
260
|
+
)
|
|
261
|
+
|
|
238
262
|
logger.info("Updated user with id: '{}'.", user.id)
|
|
239
263
|
return user
|
|
240
264
|
|
|
@@ -617,8 +641,11 @@ def get_users(
|
|
|
617
641
|
status_code=HTTP_200_OK,
|
|
618
642
|
response_model=UserLoginResponse,
|
|
619
643
|
)
|
|
644
|
+
@fides_limiter.limit(CONFIG.security.auth_rate_limit)
|
|
620
645
|
def user_login(
|
|
621
646
|
*,
|
|
647
|
+
request: Request,
|
|
648
|
+
response: Response,
|
|
622
649
|
db: Session = Depends(get_db),
|
|
623
650
|
config: FidesConfig = Depends(get_config),
|
|
624
651
|
user_data: UserLogin,
|
fides/api/app_setup.py
CHANGED
|
@@ -48,9 +48,13 @@ from fides.api.service.saas_request.override_implementations import *
|
|
|
48
48
|
from fides.api.util.api_router import APIRouter
|
|
49
49
|
from fides.api.util.cache import get_cache
|
|
50
50
|
from fides.api.util.consent_util import create_default_tcf_purpose_overrides_on_startup
|
|
51
|
-
from fides.api.util.endpoint_utils import fides_limiter
|
|
52
51
|
from fides.api.util.errors import FidesError
|
|
53
52
|
from fides.api.util.logger import setup as setup_logging
|
|
53
|
+
from fides.api.util.rate_limit import (
|
|
54
|
+
RateLimitIPValidationMiddleware,
|
|
55
|
+
fides_limiter,
|
|
56
|
+
is_rate_limit_enabled,
|
|
57
|
+
)
|
|
54
58
|
from fides.config import CONFIG
|
|
55
59
|
from fides.config.config_proxy import ConfigProxy
|
|
56
60
|
|
|
@@ -88,7 +92,17 @@ def create_fides_app(
|
|
|
88
92
|
for handler in ExceptionHandlers.get_handlers():
|
|
89
93
|
# Starlette bug causing this to fail mypy
|
|
90
94
|
fastapi_app.add_exception_handler(RedisNotConfigured, handler) # type: ignore
|
|
91
|
-
|
|
95
|
+
|
|
96
|
+
if is_rate_limit_enabled:
|
|
97
|
+
# Validate header before SlowAPI processes the request
|
|
98
|
+
fastapi_app.add_middleware(RateLimitIPValidationMiddleware)
|
|
99
|
+
# Required for default rate limiting to work
|
|
100
|
+
fastapi_app.add_middleware(SlowAPIMiddleware)
|
|
101
|
+
else:
|
|
102
|
+
logger.warning(
|
|
103
|
+
"Rate limiting client IPs is disabled because the FIDES__SECURITY__RATE_LIMIT_CLIENT_IP_HEADER env var is not configured."
|
|
104
|
+
)
|
|
105
|
+
|
|
92
106
|
fastapi_app.add_middleware(
|
|
93
107
|
GZipMiddleware, minimum_size=1000, compresslevel=5
|
|
94
108
|
) # minimum_size is in bytes
|
fides/api/db/base.py
CHANGED
|
@@ -66,6 +66,9 @@ from fides.api.models.privacy_preference import (
|
|
|
66
66
|
ServedNoticeHistory,
|
|
67
67
|
)
|
|
68
68
|
from fides.api.models.privacy_request import PrivacyRequest
|
|
69
|
+
from fides.api.models.privacy_request_redaction_pattern import (
|
|
70
|
+
PrivacyRequestRedactionPattern,
|
|
71
|
+
)
|
|
69
72
|
from fides.api.models.property import (
|
|
70
73
|
MessagingTemplateToProperty,
|
|
71
74
|
PrivacyExperienceConfigProperty,
|
fides/api/main.py
CHANGED
|
@@ -18,6 +18,8 @@ from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
|
|
|
18
18
|
from fideslog.sdk.python.event import AnalyticsEvent
|
|
19
19
|
from loguru import logger
|
|
20
20
|
from pyinstrument import Profiler
|
|
21
|
+
from slowapi import _rate_limit_exceeded_handler
|
|
22
|
+
from slowapi.errors import RateLimitExceeded
|
|
21
23
|
from starlette.background import BackgroundTask
|
|
22
24
|
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
|
|
23
25
|
from uvicorn import Config, Server
|
|
@@ -60,6 +62,7 @@ from fides.api.ui import (
|
|
|
60
62
|
)
|
|
61
63
|
from fides.api.util.endpoint_utils import API_PREFIX
|
|
62
64
|
from fides.api.util.logger import _log_exception
|
|
65
|
+
from fides.api.util.rate_limit import safe_rate_limit_key
|
|
63
66
|
from fides.cli.utils import FIDES_ASCII_ART
|
|
64
67
|
from fides.config import CONFIG, check_required_webserver_config_values
|
|
65
68
|
|
|
@@ -388,3 +391,22 @@ async def request_validation_exception_handler(
|
|
|
388
391
|
"detail": jsonable_encoder(exc.errors(), exclude={"input", "url", "ctx"})
|
|
389
392
|
},
|
|
390
393
|
)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
@app.exception_handler(RateLimitExceeded)
|
|
397
|
+
async def rate_limit_handler(request: Request, exc: RateLimitExceeded) -> Response:
|
|
398
|
+
"""Log rate limit violations and delegate to default handler."""
|
|
399
|
+
client_ip = safe_rate_limit_key(
|
|
400
|
+
request
|
|
401
|
+
) # non exception-raising, falls back to source IP
|
|
402
|
+
|
|
403
|
+
# Log the rate limit event
|
|
404
|
+
logger.warning(
|
|
405
|
+
"Rate limit exceeded - IP: %s, Path: %s, Method: %s",
|
|
406
|
+
client_ip,
|
|
407
|
+
request.url.path,
|
|
408
|
+
request.method,
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
# Use the default handler to generate the proper response
|
|
412
|
+
return _rate_limit_exceeded_handler(request, exc)
|
fides/api/models/client.py
CHANGED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from typing import List, Set
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import Column, String
|
|
4
|
+
from sqlalchemy.ext.declarative import declared_attr
|
|
5
|
+
from sqlalchemy.orm import Session
|
|
6
|
+
|
|
7
|
+
from fides.api.db.base_class import Base
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PrivacyRequestRedactionPattern(Base):
|
|
11
|
+
"""
|
|
12
|
+
Stores one regex pattern per row for masking dataset, collection, and field names
|
|
13
|
+
in DSR package reports.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@declared_attr
|
|
17
|
+
def __tablename__(self) -> str:
|
|
18
|
+
return "privacy_request_redaction_pattern"
|
|
19
|
+
|
|
20
|
+
# One pattern per row; unique to prevent duplicates
|
|
21
|
+
pattern = Column(String, nullable=False, unique=True)
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def get_patterns(cls, db: Session) -> List[str]:
|
|
25
|
+
"""
|
|
26
|
+
Get the current list of masking patterns ordered by creation time.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
List of regex pattern strings, or None if no patterns are configured
|
|
30
|
+
"""
|
|
31
|
+
rows = db.query(cls).order_by(cls.created_at.asc()).all()
|
|
32
|
+
if not rows:
|
|
33
|
+
return []
|
|
34
|
+
return [row.pattern for row in rows]
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def replace_patterns(cls, db: Session, patterns: List[str]) -> List[str]:
|
|
38
|
+
"""
|
|
39
|
+
Replace the set of stored patterns with the provided list using set reconciliation.
|
|
40
|
+
|
|
41
|
+
- Adds patterns that are not present
|
|
42
|
+
- Removes patterns that are no longer desired
|
|
43
|
+
- Returns the resulting canonical list in deterministic order
|
|
44
|
+
"""
|
|
45
|
+
# Normalize: trim whitespace, remove empties and duplicates
|
|
46
|
+
desired: Set[str] = {p.strip() for p in patterns if p and p.strip()}
|
|
47
|
+
|
|
48
|
+
# Fetch existing patterns from DB as a set
|
|
49
|
+
existing: Set[str] = set(p for (p,) in db.query(cls.pattern).all())
|
|
50
|
+
|
|
51
|
+
to_add = desired - existing
|
|
52
|
+
to_remove = existing - desired
|
|
53
|
+
|
|
54
|
+
if to_remove:
|
|
55
|
+
db.query(cls).filter(cls.pattern.in_(list(to_remove))).delete(
|
|
56
|
+
synchronize_session=False
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if to_add:
|
|
60
|
+
db.bulk_save_objects([cls(pattern=p) for p in to_add])
|
|
61
|
+
|
|
62
|
+
db.commit()
|
|
63
|
+
|
|
64
|
+
return sorted(desired)
|