ethyca-fides 2.68.1b3__py2.py3-none-any.whl → 2.69.0rc0__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.68.1b3.dist-info → ethyca_fides-2.69.0rc0.dist-info}/METADATA +1 -1
- {ethyca_fides-2.68.1b3.dist-info → ethyca_fides-2.69.0rc0.dist-info}/RECORD +231 -225
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/b1a2c3d4e5f6_add_location_to_privacy_request.py +26 -0
- fides/api/api/v1/api.py +2 -0
- fides/api/api/v1/endpoints/dsr_package_link.py +167 -0
- fides/api/api/v1/endpoints/privacy_request_endpoints.py +32 -1
- fides/api/api/v1/endpoints/user_endpoints.py +4 -0
- fides/api/models/privacy_request/privacy_request.py +1 -0
- fides/api/models/privacy_request/webhook.py +33 -1
- fides/api/oauth/utils.py +122 -57
- fides/api/schemas/application_config.py +7 -0
- fides/api/schemas/connection_configuration/connection_type_system_map.py +6 -0
- fides/api/schemas/enums/__init__.py +0 -0
- fides/api/schemas/enums/connection_category.py +20 -0
- fides/api/schemas/enums/integration_feature.py +26 -0
- fides/api/schemas/external_https.py +9 -0
- fides/api/schemas/privacy_request.py +16 -0
- fides/api/schemas/saas/display_info.py +19 -0
- fides/api/schemas/saas/saas_config.py +2 -0
- fides/api/schemas/storage/storage.py +2 -0
- fides/api/service/privacy_request/request_runner_service.py +44 -4
- fides/api/util/connection_type.py +20 -0
- fides/api/util/text.py +51 -0
- fides/common/api/v1/urn_registry.py +3 -0
- fides/service/privacy_request/privacy_request_service.py +83 -0
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/0uUBOyZCwjdyYdhNzpRkz/_buildManifest.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1099-db533dd22b80bca6.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{1345-ab756811e19ff4fc.js → 1345-6986414ab6ad3389.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{1817-fd21f1f5ef0faffa.js → 1817-fcb2fd28034d5070.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{1975.16126463309143e3.js → 1975.78e719130cfe3fd6.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{2921-0e5cc63a82e31830.js → 2921-8a63ec72fa8e1bc0.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{3620-6cceae71bae5b531.js → 3620-1fba74849114e70f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/3729-79255d8b0d22922d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3847-a868f83c065f54fb.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{3855-64541570e2f838fb.js → 3855-3154d56888ac46ed.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{3872-7a18d18a5e287e4e.js → 3872-25eec062abd0f847.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{3923-5c87b3d7f1626678.js → 3923-3be9b867f33e524c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{401-3902e3e98790d401.js → 401-029e2ff644cae40c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{4121-64ef70ef906bbdd0.js → 4121-bbce1026a2ffe962.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{4608-70521532195124de.js → 4608-99e453c666a7b89a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/4786-289c505d7d835feb.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4808-5b799757d4564807.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{4844-351f99b6644b654e.js → 4844-a1784e2823ccd0ee.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{5258-c6f96dc740eb5fb1.js → 5258-45ec38e6c3780f24.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{5487-338800277d36b8d7.js → 5487-aad0e8a240a1979b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/549-81279a1a816487a7.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6084-da63f20d9416a982.js → 6084-3379ca3c1fc77aa9.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{6853-1d947b75eb07188c.js → 6853-04b15a7b02b1fc19.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{6954-24f9a4f27d67b732.js → 6954-0f5d6c1757013eb7.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{7476-a0dd03bfccf60d0c.js → 7476-2227e21f574bee70.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{7630-9fbe06cfb98266fe.js → 7630-267de03b93bbae8d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/768-24269f30db223720.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{787-3dd31844cf7fec55.js → 787-0aa4d9802c976f92.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{79-dcd20e8b09501c17.js → 79-0102f2711b8455cd.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{796-8773e04b64ce2260.js → 796-53f0ea567d1da2b9.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/8002-98ecd56535cdefa3.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{9046-57eab238570b8bf4.js → 9046-a349c22448593f94.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{9676.bf0a8a6ff6dfd2af.js → 9676.b7d5d1d90b9da224.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{9826-756c958aecab59a2.js → 9826-f4755a75d00c1c8c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{9951-cdf73904a3adb27b.js → 9951-07989276d153c848.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{404-dd625a559ada46ca.js → 404-fde1f56225d1ef4f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-b6b09b2878b77b21.js → _app-36bec04369a1a7c4.js} +11 -11
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-92cf5e313be1f9e2.js → manual-aff671eda846719f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-d6c525ee731a2993.js → multiple-04894ddffc2a66fc.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-5664a3ea796e5ffb.js → add-systems-e98c4154535bf37d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-78f13de90111fd80.js → add-vendors-b2f80cdb75183bfa.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-0fc678f3d6d2fcec.js → configure-207cabf1b5c453fc.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-126db59dc25ca326.js → [id]-b3db5610ae7e94fb.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-289605267d6cce7e.js → privacy-experience-3009481bdd479fd7.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-e9fd9b28ac9705af.js → [id]-72e3cd6740c5c5de.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-28c003b6043bd16c.js → new-2aa4d13fb26b59ca.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-c643eff04525298e.js → privacy-notices-6b0885a8c871804f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-3ef5d01779a26455.js → properties-20bc3cf67727dfd3.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{reporting-baa4a2f8f08ac224.js → reporting-77b491108663a2de.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{consent-8d4be9e7ec7d2a35.js → consent-b3c8efbd960f4e34.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-f27ec4578c674181.js → [resourceUrn]-adc500a03e239857.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-27b6c255bd9e73b6.js → [projectUrn]-fe5f6fb33e34baa7.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-0f66dac32040519c.js → projects-3584c90cf3670146.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-3b938562df81c4b0.js → [resourceUrn]-c8b3d090e4ba60d3.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-a6cecf48e4e27645.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-844abdb5ea7d78af.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-215d6e2fefc9a5e1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-7721508cc2a9ab3a.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{activity-21c141279e66237a.js → activity-6169e49c3787a9bf.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-3bc6a207693fd175.js → [resourceUrn]-22eec362dfbb1d2a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-da16e73df395ad1d.js → detection-4decce5ef996e563.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-04b242632a114405.js → [resourceUrn]-01acdd1ad492fd89.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-900fe50183a40d72.js → discovery-85fdbf4cde60d910.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-4f1f7c3a9531a8f4.js → datamap-e17dd4ffcaa4836d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-343294dcb10d9532.js → [...subfieldNames]-4af946dc8c6c967f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-1c097a0809fa5b6f.js → [collectionName]-a65a8ea3872800e7.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-b47fa2498b534719.js → [datasetId]-ccc844caef652a7e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-a31f881cab25704a.js → new-3b65c6e9af65ea2e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-858c59c9e67e318d.js → dataset-0566189a0236078d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-16c28d272225afb6.js → [id]-38a7b8826e0abb01.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-c8d2599814eefd35.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-00ba79f6b47bd161.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{index-fec557d99211f577.js → index-053db342725dc127.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-3db44d6839e8dbd9.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations-5f34de3c24e67368.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-4a08ca7762a19700.js → [id]-707b72e959233112.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-343a965dcdb3d11e.js → add-template-c68b413f3c9cf318.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{messaging-3ade4c54b1c8a11e.js → messaging-d95588f3ef137030.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/{ant-components-9103bfb854f71410.js → ant-components-36cac6f4379587ea.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{AntForm-3b97029bd4d3c3ea.js → AntForm-d44606c0cf203b47.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikAntFormItem-9d9beb8f0d8a278c.js → FormikAntFormItem-341b571fd8da4142.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikControlled-84a4d8fc60f839ed.js → FormikControlled-2e99b5eb12982aeb.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikField-1fccf542ab2e33bf.js → FormikField-c7d62113680196dd.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/{forms-aa75263ae1ba67bb.js → forms-89a24591c3d9e7a8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/{table-migration-db334a1cbb102255.js → table-migration-c54bcfb6b0de8a71.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{[id]-11c1e4545c8f528c.js → [id]-7e3f8b6eb83c5142.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{messaging-192a986f61c23268.js → messaging-4c8e1a7e407d3c7f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{storage-9216ac993d71387e.js → storage-faf42d1a9cd6a861.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-e55ec84d5380401d.js → configure-3823ee301161be4f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-c9c1c02d7e68c4f8.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-a74b51b704b80cb2.js → [id]-8973618b9ba28a50.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-8d23f0c55ff6510a.js → add-property-b449da44de56fd3f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{properties-77acceac4f99e7af.js → properties-5bb059b7057b5583.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-6b26740aeb247bc8.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/{alpha-6aad3f563ed03b3f.js → alpha-98c1492dca52f4f8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-c1b8f3606d160bb1.js → about-d176ec152b70f82d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/{[purpose_id]-d9f7f78810d58d08.js → [purpose_id]-ea5077aca3d01dfb.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-ee2c7dde99b1dafb.js → consent-68b812269e38c8c3.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-79cdee37ceb8665f.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-31c270d228e00581.js → domain-records-056f36a8a0509006.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-996b3f250dd3ea1f.js → domains-fea15607939cab70.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-ee94981326ddcbf4.js → email-templates-cce73ddc0012b089.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-0b831c58966782b8.js → locations-8b0a408ed7e3ff28.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-94271ba4a224a353.js → organization-a01838a53677d69e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-41b8136e50320fd3.js → regulations-43e9cafaeb869069.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-52b45569cbc82e60.js → test-datasets-ea1e2483ddf6af6c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-36d74e93e54aabaf.js → [id]-7856e694b009ded3.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{systems-24dfc8e2279ced2e.js → systems-2c6dca19bf0a4f0c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-d9675cf5e6083b27.js → taxonomy-ede8ddce10d7d055.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-866826d7959df487.js → [id]-af9bf43612c9676c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-e63b61a8f99ccd57.js → user-management-3b7fea8781dc18b5.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{webpack-6d0a487039bcf30c.js → webpack-aa7ff73977a15cb3.js} +1 -1
- fides/ui-build/static/admin/_next/static/css/{dbcf63488933a4d5.css → 98fab0b3e6aa43ed.css} +1 -1
- fides/ui-build/static/admin/_next/static/css/abf2e162b1cd0e61.css +1 -0
- 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/lib/fides-ext-gpp.js +1 -1
- fides/ui-build/static/admin/lib/fides-headless.js +1 -1
- fides/ui-build/static/admin/lib/fides-preview.js +1 -1
- fides/ui-build/static/admin/lib/fides-tcf.js +2 -2
- fides/ui-build/static/admin/lib/fides.js +2 -2
- 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/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/_BLI2ArqQzY5XnXbrcxa2/_buildManifest.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1099-7b2085a3931da9e4.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1138-0d846ffef62c580f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3729-7d2d52400f1f7413.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4786-53ef1662f2d0d98c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4808-8713433c84a62efe.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/549-e6453a3526023e85.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/602-80d113e801d7407d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/8002-dcd02da6e5649a1c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-ebf5e7fa4e2ffb49.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-b27c660039d951c9.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-8ce5d24af470888e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-8e35e33928abbcdc.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-68f502d8b0b5792c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-1eb9acb17b133fd1.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-e613543818d6cbd2.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations-8069f7c33695fd45.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-48f447b31c786b80.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-e60d398e255f4e00.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-a4dad8ca9de2d07b.js +0 -1
- fides/ui-build/static/admin/_next/static/css/92441453b27e9c34.css +0 -1
- {ethyca_fides-2.68.1b3.dist-info → ethyca_fides-2.69.0rc0.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.68.1b3.dist-info → ethyca_fides-2.69.0rc0.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.68.1b3.dist-info → ethyca_fides-2.69.0rc0.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.68.1b3.dist-info → ethyca_fides-2.69.0rc0.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{_BLI2ArqQzY5XnXbrcxa2 → 0uUBOyZCwjdyYdhNzpRkz}/_ssgManifest.js +0 -0
fides/_version.py
CHANGED
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-08-
|
|
11
|
+
"date": "2025-08-28T05:01:43-0700",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "2.
|
|
14
|
+
"full-revisionid": "01dddf60bc67c9798b38acc7eeec07cc78d49ddc",
|
|
15
|
+
"version": "2.69.0rc0"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""add location to privacy request
|
|
2
|
+
|
|
3
|
+
Revision ID: b1a2c3d4e5f6
|
|
4
|
+
Revises: 2f3c1a2d6b10
|
|
5
|
+
Create Date: 2025-08-06 18:15:00.000000
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
from alembic import op
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = "b1a2c3d4e5f6"
|
|
14
|
+
down_revision = "90502bcda282"
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade():
|
|
20
|
+
# Add location column to privacyrequest table
|
|
21
|
+
op.add_column("privacyrequest", sa.Column("location", sa.String(), nullable=True))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def downgrade():
|
|
25
|
+
# Remove location column from privacyrequest table
|
|
26
|
+
op.drop_column("privacyrequest", "location")
|
fides/api/api/v1/api.py
CHANGED
|
@@ -5,6 +5,7 @@ from fides.api.api.v1.endpoints import (
|
|
|
5
5
|
consent_request_endpoints,
|
|
6
6
|
dataset_config_endpoints,
|
|
7
7
|
drp_endpoints,
|
|
8
|
+
dsr_package_link,
|
|
8
9
|
encryption_endpoints,
|
|
9
10
|
identity_verification_endpoints,
|
|
10
11
|
manual_webhook_endpoints,
|
|
@@ -32,6 +33,7 @@ api_router.include_router(connection_endpoints.router)
|
|
|
32
33
|
api_router.include_router(consent_request_endpoints.router)
|
|
33
34
|
api_router.include_router(dataset_config_endpoints.router)
|
|
34
35
|
api_router.include_router(drp_endpoints.router)
|
|
36
|
+
api_router.include_router(dsr_package_link.router)
|
|
35
37
|
api_router.include_router(encryption_endpoints.router)
|
|
36
38
|
api_router.include_router(masking_endpoints.router)
|
|
37
39
|
api_router.include_router(oauth_endpoints.router)
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
from fastapi import Depends, HTTPException, Request, Response
|
|
4
|
+
from fastapi.responses import RedirectResponse
|
|
5
|
+
from sqlalchemy.orm import Session
|
|
6
|
+
from starlette.status import (
|
|
7
|
+
HTTP_302_FOUND,
|
|
8
|
+
HTTP_400_BAD_REQUEST,
|
|
9
|
+
HTTP_401_UNAUTHORIZED,
|
|
10
|
+
HTTP_403_FORBIDDEN,
|
|
11
|
+
HTTP_404_NOT_FOUND,
|
|
12
|
+
HTTP_422_UNPROCESSABLE_ENTITY,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from fides.api.api.deps import get_db
|
|
16
|
+
from fides.api.common_exceptions import AuthenticationError, AuthorizationError
|
|
17
|
+
from fides.api.models.privacy_request import PrivacyRequest
|
|
18
|
+
from fides.api.models.storage import get_active_default_storage_config
|
|
19
|
+
from fides.api.oauth.utils import validate_download_token
|
|
20
|
+
from fides.api.schemas.privacy_request import PrivacyRequestStatus
|
|
21
|
+
from fides.api.schemas.storage.storage import StorageType
|
|
22
|
+
from fides.api.service.storage.streaming.s3 import S3StorageClient
|
|
23
|
+
from fides.api.util.api_router import APIRouter
|
|
24
|
+
from fides.api.util.endpoint_utils import fides_limiter
|
|
25
|
+
from fides.common.api.v1.urn_registry import PRIVACY_CENTER_DSR_PACKAGE, V1_URL_PREFIX
|
|
26
|
+
from fides.config import CONFIG
|
|
27
|
+
|
|
28
|
+
router = APIRouter(tags=["Privacy Center"], prefix=V1_URL_PREFIX)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_privacy_request_or_error(
|
|
32
|
+
privacy_request_id: str, db: Session
|
|
33
|
+
) -> PrivacyRequest:
|
|
34
|
+
"""Load the privacy request or throw a 404"""
|
|
35
|
+
# Note: UUID format validation is now done earlier in the endpoint
|
|
36
|
+
privacy_request = PrivacyRequest.get(db, object_id=privacy_request_id)
|
|
37
|
+
|
|
38
|
+
if not privacy_request:
|
|
39
|
+
raise HTTPException(
|
|
40
|
+
status_code=HTTP_404_NOT_FOUND,
|
|
41
|
+
detail=f"No privacy request found with id '{privacy_request_id}'.",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if privacy_request.deleted_at is not None:
|
|
45
|
+
raise HTTPException(
|
|
46
|
+
status_code=HTTP_422_UNPROCESSABLE_ENTITY,
|
|
47
|
+
detail=f"Privacy request with id {privacy_request_id} has been deleted.",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return privacy_request
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def raise_error(status_code: int, detail: str) -> None:
|
|
54
|
+
"""Raise an HTTPException with the given status code and detail"""
|
|
55
|
+
raise HTTPException(
|
|
56
|
+
status_code=status_code,
|
|
57
|
+
detail=detail,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@router.get(
|
|
62
|
+
PRIVACY_CENTER_DSR_PACKAGE,
|
|
63
|
+
status_code=HTTP_302_FOUND,
|
|
64
|
+
)
|
|
65
|
+
@fides_limiter.limit(CONFIG.security.public_request_rate_limit)
|
|
66
|
+
def get_access_results_urls(
|
|
67
|
+
privacy_request_id: str,
|
|
68
|
+
token: str,
|
|
69
|
+
db: Session = Depends(get_db),
|
|
70
|
+
*,
|
|
71
|
+
request: Request, # required for rate limiting
|
|
72
|
+
response: Response, # required for rate limiting
|
|
73
|
+
) -> RedirectResponse:
|
|
74
|
+
"""
|
|
75
|
+
Public endpoint for retrieving access results URLs for a privacy request.
|
|
76
|
+
This endpoint generates fresh presigned URLs and redirects to the first available result.
|
|
77
|
+
This endpoint is designed to be accessible via email links sent to end users.
|
|
78
|
+
No authentication is required, but a valid download token is required for security.
|
|
79
|
+
Rate limiting is applied for additional security.
|
|
80
|
+
|
|
81
|
+
privacy_request_id parameter is required in the URL path.
|
|
82
|
+
token parameter is required as a query parameter for security.
|
|
83
|
+
"""
|
|
84
|
+
# --------------Security checks--------------
|
|
85
|
+
# First validate the privacy request ID format to prevent SSRF attacks
|
|
86
|
+
# This is a simple string check that doesn't require database access
|
|
87
|
+
if not privacy_request_id.startswith("pri_"):
|
|
88
|
+
raise_error(
|
|
89
|
+
HTTP_400_BAD_REQUEST,
|
|
90
|
+
f"Invalid privacy request ID format: '{privacy_request_id}'. Must start with 'pri_' followed by a valid UUID v4.",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Extract the UUID part after the prefix
|
|
94
|
+
uuid_part = privacy_request_id[4:] # Remove "pri_" prefix
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
uuid.UUID(uuid_part, version=4)
|
|
98
|
+
except ValueError:
|
|
99
|
+
raise_error(
|
|
100
|
+
HTTP_400_BAD_REQUEST,
|
|
101
|
+
f"Invalid privacy request ID format: '{privacy_request_id}'. Must start with 'pri_' followed by a valid UUID v4.",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
# Validate the download token before proceeding
|
|
106
|
+
validate_download_token(token, privacy_request_id)
|
|
107
|
+
except AuthenticationError as e:
|
|
108
|
+
raise_error(HTTP_401_UNAUTHORIZED, str(e.detail))
|
|
109
|
+
except AuthorizationError as e:
|
|
110
|
+
raise_error(HTTP_403_FORBIDDEN, str(e.detail))
|
|
111
|
+
|
|
112
|
+
# --------------Data checks--------------
|
|
113
|
+
storage_config = get_active_default_storage_config(db)
|
|
114
|
+
privacy_request = get_privacy_request_or_error(privacy_request_id, db)
|
|
115
|
+
|
|
116
|
+
if not storage_config:
|
|
117
|
+
raise_error(
|
|
118
|
+
HTTP_400_BAD_REQUEST, "No active default storage configuration found."
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if privacy_request.status != PrivacyRequestStatus.complete:
|
|
122
|
+
raise_error(
|
|
123
|
+
HTTP_400_BAD_REQUEST,
|
|
124
|
+
f"Access results for privacy request '{privacy_request.id}' are not available because the request is not complete.",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
if (
|
|
128
|
+
not privacy_request.access_result_urls
|
|
129
|
+
or not privacy_request.access_result_urls.get("access_result_urls")
|
|
130
|
+
):
|
|
131
|
+
raise_error(
|
|
132
|
+
HTTP_404_NOT_FOUND,
|
|
133
|
+
f"No access results found for privacy request '{privacy_request.id}'.",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# --------------Processing--------------
|
|
137
|
+
file_name = f"{privacy_request.id}.zip"
|
|
138
|
+
|
|
139
|
+
# At this point, storage_config is guaranteed to exist due to earlier validation
|
|
140
|
+
# and we've already checked that it's not None above
|
|
141
|
+
assert storage_config is not None
|
|
142
|
+
|
|
143
|
+
if storage_config.type != StorageType.s3:
|
|
144
|
+
# Handle all other storage types (transcend, ethyca, local, etc.)
|
|
145
|
+
raise_error(
|
|
146
|
+
HTTP_400_BAD_REQUEST,
|
|
147
|
+
f"Storage type '{storage_config.type}' is not supported for download redirects. "
|
|
148
|
+
"Only S3 storage is supported for this endpoint.",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Get bucket name from storage config
|
|
152
|
+
bucket_name = storage_config.details.get("bucket")
|
|
153
|
+
if not bucket_name:
|
|
154
|
+
raise_error(HTTP_400_BAD_REQUEST, "S3 bucket name not found in storage config.")
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
# Use S3StorageClient for cleaner presigned URL generation
|
|
158
|
+
s3_storage_client = S3StorageClient(storage_config.secrets)
|
|
159
|
+
result_url = s3_storage_client.generate_presigned_url(
|
|
160
|
+
bucket=bucket_name,
|
|
161
|
+
key=file_name,
|
|
162
|
+
)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
raise_error(HTTP_400_BAD_REQUEST, f"Failed to generate presigned URL: {str(e)}")
|
|
165
|
+
|
|
166
|
+
# Convert the URL to string for RedirectResponse
|
|
167
|
+
return RedirectResponse(url=str(result_url), status_code=HTTP_302_FOUND)
|
|
@@ -86,7 +86,7 @@ from fides.api.oauth.utils import (
|
|
|
86
86
|
from fides.api.schemas.api import ResponseWithMessage
|
|
87
87
|
from fides.api.schemas.dataset import CollectionAddressResponse, DryRunDatasetResponse
|
|
88
88
|
from fides.api.schemas.external_https import PrivacyRequestResumeFormat
|
|
89
|
-
from fides.api.schemas.policy import ActionType
|
|
89
|
+
from fides.api.schemas.policy import ActionType, CurrentStep
|
|
90
90
|
from fides.api.schemas.privacy_request import (
|
|
91
91
|
BulkPostPrivacyRequests,
|
|
92
92
|
BulkReviewResponse,
|
|
@@ -124,6 +124,7 @@ from fides.api.util.endpoint_utils import validate_start_and_end_filters
|
|
|
124
124
|
from fides.api.util.enums import ColumnSort
|
|
125
125
|
from fides.api.util.fuzzy_search_utils import get_decrypted_identities_automaton
|
|
126
126
|
from fides.api.util.storage_util import StorageJSONEncoder
|
|
127
|
+
from fides.api.util.text import normalize_location_code
|
|
127
128
|
from fides.common.api.scope_registry import (
|
|
128
129
|
PRIVACY_REQUEST_CALLBACK_RESUME,
|
|
129
130
|
PRIVACY_REQUEST_CREATE,
|
|
@@ -403,6 +404,7 @@ def _filter_privacy_request_queryset(
|
|
|
403
404
|
errored_lt: Optional[datetime] = None,
|
|
404
405
|
errored_gt: Optional[datetime] = None,
|
|
405
406
|
external_id: Optional[str] = None,
|
|
407
|
+
location: Optional[str] = None,
|
|
406
408
|
action_type: Optional[ActionType] = None,
|
|
407
409
|
include_consent_webhook_requests: Optional[bool] = False,
|
|
408
410
|
include_deleted_requests: Optional[bool] = False,
|
|
@@ -539,6 +541,29 @@ def _filter_privacy_request_queryset(
|
|
|
539
541
|
query = query.filter(PrivacyRequest.id.ilike(f"%{request_id}%"))
|
|
540
542
|
if external_id:
|
|
541
543
|
query = query.filter(PrivacyRequest.external_id.ilike(f"{external_id}%"))
|
|
544
|
+
if location:
|
|
545
|
+
# Support filtering by exact location match or country prefix
|
|
546
|
+
# e.g., "US" matches both "US" and "US-CA", "US-NY", etc.
|
|
547
|
+
# "US-CA" matches only "US-CA"
|
|
548
|
+
# Also normalize input to handle underscores and case insensitivity
|
|
549
|
+
|
|
550
|
+
try:
|
|
551
|
+
normalized_location = normalize_location_code(location)
|
|
552
|
+
except ValueError:
|
|
553
|
+
# If normalization fails, treat as no results to prevent errors
|
|
554
|
+
query = query.filter(False)
|
|
555
|
+
else:
|
|
556
|
+
if "-" in normalized_location:
|
|
557
|
+
# Exact match for subdivision codes
|
|
558
|
+
query = query.filter(PrivacyRequest.location == normalized_location)
|
|
559
|
+
else:
|
|
560
|
+
# Country code - match country or any subdivision of that country
|
|
561
|
+
query = query.filter(
|
|
562
|
+
or_(
|
|
563
|
+
PrivacyRequest.location == normalized_location,
|
|
564
|
+
PrivacyRequest.location.ilike(f"{normalized_location}-%"),
|
|
565
|
+
)
|
|
566
|
+
)
|
|
542
567
|
if status:
|
|
543
568
|
query = query.filter(PrivacyRequest.status.in_(status))
|
|
544
569
|
if created_lt:
|
|
@@ -685,6 +710,7 @@ def _shared_privacy_request_search(
|
|
|
685
710
|
errored_lt: Optional[datetime] = None,
|
|
686
711
|
errored_gt: Optional[datetime] = None,
|
|
687
712
|
external_id: Optional[str] = None,
|
|
713
|
+
location: Optional[str] = None,
|
|
688
714
|
action_type: Optional[ActionType] = None,
|
|
689
715
|
verbose: Optional[bool] = False,
|
|
690
716
|
include_identities: Optional[bool] = False,
|
|
@@ -723,6 +749,7 @@ def _shared_privacy_request_search(
|
|
|
723
749
|
errored_lt,
|
|
724
750
|
errored_gt,
|
|
725
751
|
external_id,
|
|
752
|
+
location,
|
|
726
753
|
action_type,
|
|
727
754
|
None,
|
|
728
755
|
include_deleted_requests,
|
|
@@ -796,6 +823,7 @@ def get_request_status(
|
|
|
796
823
|
errored_lt: Optional[datetime] = None,
|
|
797
824
|
errored_gt: Optional[datetime] = None,
|
|
798
825
|
external_id: Optional[str] = None,
|
|
826
|
+
location: Optional[str] = None,
|
|
799
827
|
action_type: Optional[ActionType] = None,
|
|
800
828
|
verbose: Optional[bool] = False,
|
|
801
829
|
include_identities: Optional[bool] = False,
|
|
@@ -836,6 +864,7 @@ def get_request_status(
|
|
|
836
864
|
errored_lt=errored_lt,
|
|
837
865
|
errored_gt=errored_gt,
|
|
838
866
|
external_id=external_id,
|
|
867
|
+
location=location,
|
|
839
868
|
action_type=action_type,
|
|
840
869
|
verbose=verbose,
|
|
841
870
|
include_identities=include_identities,
|
|
@@ -890,6 +919,7 @@ def privacy_request_search(
|
|
|
890
919
|
errored_lt=privacy_request_filter.errored_lt,
|
|
891
920
|
errored_gt=privacy_request_filter.errored_gt,
|
|
892
921
|
external_id=privacy_request_filter.external_id,
|
|
922
|
+
location=privacy_request_filter.location,
|
|
893
923
|
action_type=privacy_request_filter.action_type,
|
|
894
924
|
verbose=privacy_request_filter.verbose,
|
|
895
925
|
include_identities=privacy_request_filter.include_identities,
|
|
@@ -1900,6 +1930,7 @@ def finalize_privacy_request(
|
|
|
1900
1930
|
|
|
1901
1931
|
queue_privacy_request(
|
|
1902
1932
|
privacy_request_id=privacy_request_id,
|
|
1933
|
+
from_step=CurrentStep.finalize_erasure.value,
|
|
1903
1934
|
)
|
|
1904
1935
|
|
|
1905
1936
|
return privacy_request # type: ignore[return-value]
|
|
@@ -250,6 +250,10 @@ def logout_oauth_client(
|
|
|
250
250
|
if authorization is None:
|
|
251
251
|
raise AuthenticationError(detail="Authentication Failure")
|
|
252
252
|
|
|
253
|
+
# Validate that the token looks like a valid JWE token (5 segments separated by dots)
|
|
254
|
+
if not authorization or authorization.count(".") != 4:
|
|
255
|
+
return None
|
|
256
|
+
|
|
253
257
|
try:
|
|
254
258
|
token_data = json.loads(
|
|
255
259
|
extract_payload(authorization, CONFIG.security.app_encryption_key)
|
|
@@ -222,6 +222,7 @@ class PrivacyRequest(
|
|
|
222
222
|
canceled_at = Column(DateTime(timezone=True), nullable=True)
|
|
223
223
|
consent_preferences = Column(MutableList.as_mutable(JSONB), nullable=True)
|
|
224
224
|
source = Column(EnumColumn(PrivacyRequestSource), nullable=True)
|
|
225
|
+
location = Column(String, nullable=True)
|
|
225
226
|
|
|
226
227
|
# A PrivacyRequest can be soft deleted, so we store when it was deleted
|
|
227
228
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
|
@@ -13,12 +13,17 @@ from fides.api.models.policy import PolicyPreWebhook, WebhookDirection
|
|
|
13
13
|
from fides.api.models.pre_approval_webhook import PreApprovalWebhook
|
|
14
14
|
from fides.api.models.privacy_request.request_task import RequestTask
|
|
15
15
|
from fides.api.oauth.jwt import generate_jwe
|
|
16
|
-
from fides.api.schemas.external_https import
|
|
16
|
+
from fides.api.schemas.external_https import (
|
|
17
|
+
DownloadTokenJWE,
|
|
18
|
+
RequestTaskJWE,
|
|
19
|
+
WebhookJWE,
|
|
20
|
+
)
|
|
17
21
|
from fides.api.schemas.policy import ActionType
|
|
18
22
|
from fides.api.schemas.privacy_request import PrivacyRequestStatus
|
|
19
23
|
from fides.api.schemas.redis_cache import Identity
|
|
20
24
|
from fides.common.api.scope_registry import (
|
|
21
25
|
PRIVACY_REQUEST_CALLBACK_RESUME,
|
|
26
|
+
PRIVACY_REQUEST_READ_ACCESS_RESULTS,
|
|
22
27
|
PRIVACY_REQUEST_REVIEW,
|
|
23
28
|
)
|
|
24
29
|
from fides.config import CONFIG
|
|
@@ -92,3 +97,30 @@ def generate_request_task_callback_jwe(request_task: RequestTask) -> str:
|
|
|
92
97
|
json.dumps(jwe.model_dump(mode="json")),
|
|
93
98
|
CONFIG.security.app_encryption_key,
|
|
94
99
|
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def generate_privacy_request_download_token(privacy_request_id: str) -> str:
|
|
103
|
+
"""
|
|
104
|
+
Generate a JWE token for users to download their privacy request access package.
|
|
105
|
+
This is currently used for the DSR package link endpoint which provides a redirect
|
|
106
|
+
to a presigned URL for the access results.
|
|
107
|
+
This token expires based on the configured TTL for security.
|
|
108
|
+
"""
|
|
109
|
+
from datetime import timedelta
|
|
110
|
+
|
|
111
|
+
now = datetime.now()
|
|
112
|
+
# Use the configured TTL from security settings
|
|
113
|
+
expiration = now + timedelta(
|
|
114
|
+
seconds=CONFIG.security.subject_request_download_link_ttl_seconds
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
jwe = DownloadTokenJWE(
|
|
118
|
+
privacy_request_id=privacy_request_id,
|
|
119
|
+
scopes=[PRIVACY_REQUEST_READ_ACCESS_RESULTS],
|
|
120
|
+
iat=now.isoformat(),
|
|
121
|
+
exp=expiration.isoformat(),
|
|
122
|
+
)
|
|
123
|
+
return generate_jwe(
|
|
124
|
+
json.dumps(jwe.model_dump(mode="json")),
|
|
125
|
+
CONFIG.security.app_encryption_key,
|
|
126
|
+
)
|
fides/api/oauth/utils.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
from datetime import datetime
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
5
|
from functools import update_wrapper
|
|
6
6
|
from types import FunctionType
|
|
7
7
|
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
@@ -32,7 +32,11 @@ from fides.api.models.pre_approval_webhook import PreApprovalWebhook
|
|
|
32
32
|
from fides.api.models.privacy_request import RequestTask
|
|
33
33
|
from fides.api.oauth.roles import get_scopes_from_roles
|
|
34
34
|
from fides.api.request_context import set_user_id
|
|
35
|
-
from fides.api.schemas.external_https import
|
|
35
|
+
from fides.api.schemas.external_https import (
|
|
36
|
+
DownloadTokenJWE,
|
|
37
|
+
RequestTaskJWE,
|
|
38
|
+
WebhookJWE,
|
|
39
|
+
)
|
|
36
40
|
from fides.api.schemas.oauth import OAuth2ClientCredentialsBearer
|
|
37
41
|
from fides.common.api.v1.urn_registry import TOKEN, V1_URL_PREFIX
|
|
38
42
|
from fides.config import CONFIG, FidesConfig
|
|
@@ -49,63 +53,28 @@ oauth2_scheme = OAuth2ClientCredentialsBearer(
|
|
|
49
53
|
|
|
50
54
|
def extract_payload(jwe_string: str, encryption_key: str) -> str:
|
|
51
55
|
"""Given a jwe, extracts the payload and returns it in string form."""
|
|
52
|
-
|
|
56
|
+
try:
|
|
57
|
+
decrypted_payload = jwe.decrypt(jwe_string, encryption_key)
|
|
58
|
+
return decrypted_payload.decode("utf-8")
|
|
59
|
+
except exceptions.JWEError as e:
|
|
60
|
+
logger.debug("Failed to decrypt JWE: {}", e)
|
|
61
|
+
raise e
|
|
53
62
|
|
|
54
63
|
|
|
55
|
-
def is_token_expired(
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
def is_token_expired(
|
|
65
|
+
issued_at: Optional[datetime], token_duration_minutes: int
|
|
66
|
+
) -> bool:
|
|
67
|
+
"""Check if a token has expired based on its issued_at timestamp and duration."""
|
|
68
|
+
if issued_at is None:
|
|
58
69
|
return True
|
|
70
|
+
expiration_time = issued_at + timedelta(minutes=token_duration_minutes)
|
|
71
|
+
return datetime.now() > expiration_time
|
|
59
72
|
|
|
60
|
-
return (datetime.now() - issued_at).total_seconds() / 60.0 > token_duration_min
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def copy_func(source_function: Callable) -> Callable:
|
|
64
|
-
"""Based on http://stackoverflow.com/a/6528148/190597 (Glenn Maynard)"""
|
|
65
|
-
target_function = FunctionType(
|
|
66
|
-
source_function.__code__,
|
|
67
|
-
source_function.__globals__,
|
|
68
|
-
name=source_function.__name__,
|
|
69
|
-
argdefs=source_function.__defaults__,
|
|
70
|
-
closure=source_function.__closure__,
|
|
71
|
-
)
|
|
72
|
-
updated_target_function: Callable = update_wrapper(target_function, source_function)
|
|
73
|
-
updated_target_function.__kwdefaults__ = source_function.__kwdefaults__
|
|
74
|
-
return updated_target_function
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
async def get_current_user(
|
|
78
|
-
security_scopes: SecurityScopes,
|
|
79
|
-
authorization: str = Security(oauth2_scheme),
|
|
80
|
-
db: Session = Depends(get_db),
|
|
81
|
-
) -> FidesUser:
|
|
82
|
-
"""A wrapper around verify_oauth_client that returns that client's user if one exists."""
|
|
83
|
-
client = await verify_oauth_client(
|
|
84
|
-
security_scopes=security_scopes,
|
|
85
|
-
authorization=authorization,
|
|
86
|
-
db=db,
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
if client.id == CONFIG.security.oauth_root_client_id:
|
|
90
|
-
return FidesUser(
|
|
91
|
-
id=CONFIG.security.oauth_root_client_id,
|
|
92
|
-
username=CONFIG.security.root_username,
|
|
93
|
-
created_at=datetime.utcnow(),
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
return client.user # type: ignore[attr-defined]
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def is_callback_token_expired(issued_at: datetime | None) -> bool:
|
|
100
|
-
"""Returns True if the token is older than the expiration of the redis cache. We
|
|
101
|
-
can't resume executing the privacy request if the identity data is gone.
|
|
102
|
-
"""
|
|
103
|
-
if not issued_at:
|
|
104
|
-
return True
|
|
105
73
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
74
|
+
def is_callback_token_expired(issued_at: datetime) -> bool:
|
|
75
|
+
"""Check if a callback token has expired (24 hours)."""
|
|
76
|
+
expiration_time = issued_at + timedelta(hours=24)
|
|
77
|
+
return datetime.now() > expiration_time
|
|
109
78
|
|
|
110
79
|
|
|
111
80
|
def _get_webhook_jwe_or_error(
|
|
@@ -114,9 +83,13 @@ def _get_webhook_jwe_or_error(
|
|
|
114
83
|
if authorization is None:
|
|
115
84
|
raise AuthenticationError(detail="Authentication Failure")
|
|
116
85
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
86
|
+
try:
|
|
87
|
+
token_data = json.loads(
|
|
88
|
+
extract_payload(authorization, CONFIG.security.app_encryption_key)
|
|
89
|
+
)
|
|
90
|
+
except exceptions.JWEError:
|
|
91
|
+
raise AuthorizationError(detail="Not Authorized for this action")
|
|
92
|
+
|
|
120
93
|
try:
|
|
121
94
|
token = WebhookJWE(**token_data)
|
|
122
95
|
except ValidationError:
|
|
@@ -160,6 +133,98 @@ def _get_request_task_jwe_or_error(
|
|
|
160
133
|
return token
|
|
161
134
|
|
|
162
135
|
|
|
136
|
+
def validate_download_token(token: str, privacy_request_id: str) -> DownloadTokenJWE:
|
|
137
|
+
"""
|
|
138
|
+
Validate a download token for accessing privacy request packages.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
token: The JWE token to validate
|
|
142
|
+
privacy_request_id: The privacy request ID the token should grant access to
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
The validated DownloadTokenJWE object
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
AuthenticationError: If token is invalid or expired
|
|
149
|
+
AuthorizationError: If token doesn't grant access to the requested privacy request
|
|
150
|
+
"""
|
|
151
|
+
if not token:
|
|
152
|
+
raise AuthenticationError(detail="Download token is required")
|
|
153
|
+
|
|
154
|
+
# Check if token looks like a JWE (should have 5 parts separated by dots)
|
|
155
|
+
if token.count(".") != 4:
|
|
156
|
+
raise AuthenticationError(detail="Invalid download token format")
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
token_data = json.loads(
|
|
160
|
+
extract_payload(token, CONFIG.security.app_encryption_key)
|
|
161
|
+
)
|
|
162
|
+
except exceptions.JWEError:
|
|
163
|
+
raise AuthenticationError(detail="Invalid download token format")
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
download_token = DownloadTokenJWE(**token_data)
|
|
167
|
+
except ValidationError:
|
|
168
|
+
raise AuthenticationError(detail="Invalid download token structure")
|
|
169
|
+
|
|
170
|
+
# Verify the token grants access to the requested privacy request
|
|
171
|
+
if download_token.privacy_request_id != privacy_request_id:
|
|
172
|
+
raise AuthorizationError(
|
|
173
|
+
detail="Download token does not grant access to this privacy request"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Verify the token has the required scope
|
|
177
|
+
required_scope = "privacy-request-access-results:read"
|
|
178
|
+
if required_scope not in download_token.scopes:
|
|
179
|
+
raise AuthorizationError(detail="Download token lacks required permissions")
|
|
180
|
+
|
|
181
|
+
# Check if the token has expired
|
|
182
|
+
try:
|
|
183
|
+
expiration_time = datetime.fromisoformat(download_token.exp)
|
|
184
|
+
if datetime.now() > expiration_time:
|
|
185
|
+
raise AuthenticationError(detail="Download token has expired")
|
|
186
|
+
except (ValueError, TypeError):
|
|
187
|
+
raise AuthenticationError(detail="Invalid token expiration format")
|
|
188
|
+
|
|
189
|
+
return download_token
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def copy_func(source_function: Callable) -> Callable:
|
|
193
|
+
"""Based on http://stackoverflow.com/a/6528148/190597 (Glenn Maynard)"""
|
|
194
|
+
target_function = FunctionType(
|
|
195
|
+
source_function.__code__,
|
|
196
|
+
source_function.__globals__,
|
|
197
|
+
name=source_function.__name__,
|
|
198
|
+
argdefs=source_function.__defaults__,
|
|
199
|
+
closure=source_function.__closure__,
|
|
200
|
+
)
|
|
201
|
+
updated_target_function: Callable = update_wrapper(target_function, source_function)
|
|
202
|
+
updated_target_function.__kwdefaults__ = source_function.__kwdefaults__
|
|
203
|
+
return updated_target_function
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
async def get_current_user(
|
|
207
|
+
security_scopes: SecurityScopes,
|
|
208
|
+
authorization: str = Security(oauth2_scheme),
|
|
209
|
+
db: Session = Depends(get_db),
|
|
210
|
+
) -> FidesUser:
|
|
211
|
+
"""A wrapper around verify_oauth_client that returns that client's user if one exists."""
|
|
212
|
+
client = await verify_oauth_client(
|
|
213
|
+
security_scopes=security_scopes,
|
|
214
|
+
authorization=authorization,
|
|
215
|
+
db=db,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if client.id == CONFIG.security.oauth_root_client_id:
|
|
219
|
+
return FidesUser(
|
|
220
|
+
id=CONFIG.security.oauth_root_client_id,
|
|
221
|
+
username=CONFIG.security.root_username,
|
|
222
|
+
created_at=datetime.utcnow(),
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
return client.user # type: ignore[attr-defined]
|
|
226
|
+
|
|
227
|
+
|
|
163
228
|
def verify_callback_oauth_policy_pre_webhook(
|
|
164
229
|
security_scopes: SecurityScopes,
|
|
165
230
|
authorization: str = Security(oauth2_scheme),
|
|
@@ -87,6 +87,12 @@ class AdminUIConfig(FidesSchema):
|
|
|
87
87
|
model_config = ConfigDict(extra="forbid")
|
|
88
88
|
|
|
89
89
|
|
|
90
|
+
class PrivacyCenterConfig(FidesSchema):
|
|
91
|
+
url: SerializeAsAny[Optional[AnyHttpUrlStringRemovesSlash]] = None
|
|
92
|
+
|
|
93
|
+
model_config = ConfigDict(extra="forbid")
|
|
94
|
+
|
|
95
|
+
|
|
90
96
|
class ConsentConfig(FidesSchema):
|
|
91
97
|
override_vendor_purposes: Optional[bool]
|
|
92
98
|
model_config = ConfigDict(extra="forbid")
|
|
@@ -119,6 +125,7 @@ class ApplicationConfig(FidesSchema):
|
|
|
119
125
|
security: Optional[SecurityApplicationConfig] = None
|
|
120
126
|
consent: Optional[ConsentConfig] = None
|
|
121
127
|
admin_ui: Optional[AdminUIConfig] = None
|
|
128
|
+
privacy_center: Optional[PrivacyCenterConfig] = None
|
|
122
129
|
|
|
123
130
|
@model_validator(mode="before")
|
|
124
131
|
@classmethod
|
|
@@ -4,6 +4,8 @@ from pydantic import BaseModel, ConfigDict
|
|
|
4
4
|
|
|
5
5
|
from fides.api.models.connectionconfig import ConnectionType
|
|
6
6
|
from fides.api.schemas.connection_configuration.enums.system_type import SystemType
|
|
7
|
+
from fides.api.schemas.enums.connection_category import ConnectionCategory
|
|
8
|
+
from fides.api.schemas.enums.integration_feature import IntegrationFeature
|
|
7
9
|
from fides.api.schemas.policy import ActionType
|
|
8
10
|
|
|
9
11
|
|
|
@@ -19,4 +21,8 @@ class ConnectionSystemTypeMap(BaseModel):
|
|
|
19
21
|
authorization_required: Optional[bool] = False
|
|
20
22
|
user_guide: Optional[str] = None
|
|
21
23
|
supported_actions: List[ActionType]
|
|
24
|
+
# New fields for enhanced display information
|
|
25
|
+
category: Optional[ConnectionCategory] = None
|
|
26
|
+
tags: Optional[List[str]] = None
|
|
27
|
+
enabled_features: Optional[List[IntegrationFeature]] = None
|
|
22
28
|
model_config = ConfigDict(use_enum_values=True, from_attributes=True)
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ConnectionCategory(str, Enum):
|
|
5
|
+
"""
|
|
6
|
+
Categories for connection types, matching frontend ConnectionCategory enum
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
DATA_CATALOG = "DATA_CATALOG"
|
|
10
|
+
DATA_WAREHOUSE = "DATA_WAREHOUSE"
|
|
11
|
+
DATABASE = "DATABASE"
|
|
12
|
+
IDENTITY_PROVIDER = "IDENTITY_PROVIDER"
|
|
13
|
+
WEBSITE = "WEBSITE"
|
|
14
|
+
CRM = "CRM"
|
|
15
|
+
MANUAL = "MANUAL"
|
|
16
|
+
MARKETING = "MARKETING"
|
|
17
|
+
ANALYTICS = "ANALYTICS"
|
|
18
|
+
ECOMMERCE = "ECOMMERCE"
|
|
19
|
+
COMMUNICATION = "COMMUNICATION"
|
|
20
|
+
CUSTOM = "CUSTOM" # Fallback for uncategorized/custom uploaded integrations
|