ethyca-fides 2.73.2b0__py2.py3-none-any.whl → 2.76.0rc2__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.
- {ethyca_fides-2.73.2b0.dist-info → ethyca_fides-2.76.0rc2.dist-info}/METADATA +2 -1
- {ethyca_fides-2.73.2b0.dist-info → ethyca_fides-2.76.0rc2.dist-info}/RECORD +349 -336
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/303287c70600_migrate_resource_type_from_enum_to_.py +99 -0
- fides/api/alembic/migrations/versions/795f46f656c0_migrate_field_type_from_enum_to_string.py +61 -0
- fides/api/alembic/migrations/versions/xx_2025_10_29_1659_80d28dea3b6b_added_duplicate_group_table.py +79 -0
- fides/api/alembic/migrations/versions/xx_2025_11_05_0200_f1a2b3c4d5e6_create_staged_resource_error_table.py +82 -0
- fides/api/alembic/migrations/versions/xx_2025_11_07_1709_56fe6fad2d89_add_notice_display_order.py +47 -0
- fides/api/alembic/migrations/versions/xx_2025_11_10_1200_a1b2c3d4e5f6_add_test_datastore_to_connectiontype.py +53 -0
- fides/api/alembic/migrations/versions/xx_2025_11_11_1317_7d82c8fc4c34_store_saas_template_datasets.py +68 -0
- fides/api/alembic/migrations/versions/xx_2025_11_12_1430_b2c3d4e5f6a7_add_default_identity_definitions.py +81 -0
- fides/api/alembic/migrations/versions/xx_2025_11_25_1854_3ff6449c099e_add_index_on_providedidentity_privacy_.py +50 -0
- fides/api/api/v1/api.py +2 -0
- fides/api/api/v1/endpoints/admin.py +39 -0
- fides/api/api/v1/endpoints/connector_template_endpoints.py +167 -0
- fides/api/api/v1/endpoints/generic_overrides.py +6 -6
- fides/api/api/v1/endpoints/pre_approval_webhook_endpoints.py +5 -5
- fides/api/api/v1/endpoints/privacy_request_endpoints.py +101 -21
- fides/api/api/v1/endpoints/saas_config_endpoints.py +3 -44
- fides/api/api/v1/endpoints/storage_endpoints.py +5 -1
- fides/api/api/v1/endpoints/system.py +2 -2
- fides/api/api/v1/endpoints/user_endpoints.py +10 -4
- fides/api/db/base.py +1 -0
- fides/api/db/database.py +228 -1
- fides/api/db/seed.py +1 -1
- fides/api/email_templates/templates/external_user_welcome.html +9 -5
- fides/api/migrations/post_upgrade_index_creation.py +10 -0
- fides/api/models/connectionconfig.py +4 -1
- fides/api/models/detection_discovery/__init__.py +2 -0
- fides/api/models/detection_discovery/core.py +10 -0
- fides/api/models/detection_discovery/staged_resource_error.py +25 -0
- fides/api/models/experience_notices.py +6 -1
- fides/api/models/identity_definition.py +2 -1
- fides/api/models/location_regulation_selections.py +22 -0
- fides/api/models/manual_webhook.py +17 -6
- fides/api/models/messaging_template.py +7 -0
- fides/api/models/policy.py +3 -0
- fides/api/models/privacy_experience.py +20 -0
- fides/api/models/privacy_notice.py +45 -1
- fides/api/models/privacy_preference.py +1 -0
- fides/api/models/privacy_request/duplicate_group.py +84 -0
- fides/api/models/privacy_request/privacy_request.py +84 -9
- fides/api/models/privacy_request/provided_identity.py +22 -1
- fides/api/models/saas_template_dataset.py +63 -0
- fides/api/models/sql_models.py +150 -7
- fides/api/models/taxonomy.py +61 -1
- fides/api/oauth/roles.py +2 -0
- fides/api/schemas/connection_configuration/connection_secrets_mssql.py +5 -0
- fides/api/schemas/messaging/messaging.py +1 -0
- fides/api/schemas/oauth.py +2 -1
- fides/api/schemas/privacy_request.py +38 -6
- fides/api/schemas/saas/connector_template.py +14 -0
- fides/api/service/async_dsr/strategies/async_dsr_strategy_polling.py +19 -7
- fides/api/service/connectors/base_erasure_email_connector.py +7 -0
- fides/api/service/connectors/microsoft_sql_server_connector.py +6 -1
- fides/api/service/connectors/saas/connector_registry_service.py +45 -1
- fides/api/service/messaging/message_dispatch_service.py +44 -4
- fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +3 -3
- fides/api/service/privacy_request/dsr_package/templates/attachments_index.html +41 -30
- fides/api/service/privacy_request/dsr_package/templates/clickme.html +68 -0
- fides/api/service/privacy_request/dsr_package/templates/dataset_index.html +30 -27
- fides/api/service/privacy_request/duplication_detection.py +439 -0
- fides/api/service/privacy_request/email_batch_service.py +2 -1
- fides/api/service/privacy_request/request_runner_service.py +31 -5
- fides/api/service/privacy_request/request_service.py +4 -2
- fides/api/service/saas_request/saas_request_override_factory.py +7 -2
- fides/api/task/conditional_dependencies/privacy_request/__init__.py +0 -0
- fides/api/task/conditional_dependencies/privacy_request/convenience_fields.py +155 -0
- fides/api/task/conditional_dependencies/privacy_request/privacy_request_data.py +140 -0
- fides/api/task/conditional_dependencies/privacy_request/schemas.py +212 -0
- fides/api/task/conditional_dependencies/util.py +111 -0
- fides/api/task/execute_request_tasks.py +17 -4
- fides/api/task/manual/manual_task_conditional_evaluation.py +32 -27
- fides/api/task/manual/manual_task_graph_task.py +4 -1
- fides/api/task/manual/manual_task_utils.py +63 -20
- fides/api/tasks/__init__.py +14 -0
- fides/api/util/cache.py +4 -0
- fides/api/util/connection_util.py +2 -3
- fides/api/util/fuzzy_search_utils.py +7 -1
- fides/api/util/lock.py +13 -3
- fides/api/util/logger.py +58 -4
- fides/api/util/logger_context_utils.py +3 -1
- fides/api/util/memory_watchdog.py +118 -0
- fides/api/util/saas_config_updater.py +12 -2
- fides/api/worker/__init__.py +4 -0
- fides/common/api/scope_registry.py +6 -0
- fides/common/api/v1/urn_registry.py +11 -2
- fides/config/__init__.py +8 -2
- fides/config/celery_settings.py +42 -0
- fides/service/messaging/messaging_service.py +75 -74
- fides/service/privacy_request/privacy_request_service.py +151 -19
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/chunks/1099-688fa865621531cc.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{1115-26393b586775ea29.js → 1115-7fd171dac1eb0e51.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/1276.deb10ae2643f8463.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1438-8a33b3834d6e43f3.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6148-59a59d5c5925344f.js → 1533-84e250d1f26e6d7d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/1821-c1daa160f492aacf.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1840-359ee056e4cf6629.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{1975.afe8cad52f904fcf.js → 1975.bef017bc80e2012c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{2040-7eed8491ca7276ed.js → 2040-70972e15960d9afe.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/2121.321b0fd3932164d4.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{2397-083fc511acb6105d.js → 2397-3434603a97f3f5d6.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/2921-49ed0ed897832958.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3150-da5406b80d25fe6d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3214-90ce0a366b0f461a.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3377-02bf9780fd383d94.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3615-5e2d062d684b8fa1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3655-5e1ba5dd68b5ec48.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3729-31ff8ba51491bf21.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3872-cff30ca0844fe2b1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3931-8bedde156fe83564.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{401-7c345d019bb9bcbd.js → 401-582d9970d89deefe.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/4093-7e47408c28de5375.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{4259.d1507e0db19cbed7.js → 4259.05038c9b78467244.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/4322-f6aeff6880726c83.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4339-04a715ab07122744.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4496-ccbce2459174e0d6.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/454-d5c2c84f1a14e4f1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4589-c1d83c6a8dab4d30.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4809-a8f4a108a42f53ed.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4903-19c0bc07a956dfa8.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4910-d990773601f794c1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{5185-e7f8b81dd3dfbe0b.js → 5185-96423702fba70ced.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/5258-4e308cca01d59367.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/5487-5c3501754bf027ba.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/549-1bdc3e6f3264c020.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/5505-43b9c39491b88e08.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{5783-21775c232dce7af7.js → 5783-016dfcee8e49bf61.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/5826-4db99ea4e5077911.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/590-be447cacf12419dd.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/6084-91badbc6569a0efb.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/6344-026cb323c1d49926.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/6362-ba0e12f2fc4cad94.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6853-de9905d28e5b19b3.js → 6853-882889659769d7b4.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/6882-bb1b469d7d7f5335.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/6954-0dcf22a9aabe39c5.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/7170-46db82bb5b55e856.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/7218-2ace8c82e3e7eb74.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{7245-c9bc628d078c2170.js → 7245-1cdafb35f289861b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/7630-7f75ab7b8df42eb3.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/7654-2e9a8be02e41769a.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{8212-393420e5a9751791.js → 8212-348ddd2b6933db70.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/9014-eeae6f581158e645.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{5596-29a7c8322530b7cf.js → 9195-da717d324917b049.js} +3 -3
- fides/ui-build/static/admin/_next/static/chunks/9450-b7b7bb1d755ecf57.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{9676.7d029a5383595b69.js → 9676.1f395eeb9cc34968.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{9826-a737a9956c1d0905.js → 9826-1078e46f3ac0b688.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/9911-ece086f2230e34f0.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{404-eb019192ce498f32.js → 404-800be6996aaa999c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-ee588a308812715d.js → _app-de4b578c904df772.js} +70 -70
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-7081e0e49f67716c.js → manual-f12020b82dd4bd1a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-a188f84239f4b2a8.js → multiple-a911b7990371704d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-ee9df33ebd471099.js → add-systems-ad585b79953c2753.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-4f9cf087fcee87e6.js → add-vendors-bb263d394ca1c8fa.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-6907c368d8611c44.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-1b02a4991201b7e4.js → [id]-c32f381af358149b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{new-a5e738a234dadc7e.js → new-6efb3c069d8b47dd.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-b08abefec298ccf1.js → privacy-experience-1975c529905eea9b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-8c164c4b8310214e.js → [id]-b05331178928ab52.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-db789002d84c8829.js → new-516834e930bb0d0d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-985717f2565f9d9d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-5cba30eba1e97e56.js → properties-2be773e08498e40c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-850afb74f4192e87.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{consent-614af0a2c2ba966c.js → consent-b63d1e395d879b86.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn]-5b28f0f674ea87bd.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]-676177e2f3c9c2a2.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-794906929efb8e1d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/[resourceUrn]-3ccbf7c0d06507b9.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources-81d1b50585468fb0.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-1ea0b24d306b1e67.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/datastore/[monitorId]-d4861a4a218bb65a.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/infrastructure/[monitorId]-66bd265044daf97d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/infrastructure-c9c79fa8576a4f77.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/website/[monitorId]/[systemId]-60cacc3232c2eead.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/website/[monitorId]-437bd64a3016de36.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-8349248c2da970a6.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-6919a1d6cadaa46b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-b072cf25aefc98f6.js → [resourceUrn]-1a50d421897d3da1.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-fd3e8817d8e6dee4.js → detection-49509414a15e8393.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-146624cf59792bf7.js → [resourceUrn]-1a1bb80b586d0c0f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-9695cc9c85592ec5.js → discovery-49de61df1e8e7fba.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datamap-c2719f5cff20c0f8.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-5f88280db168083e.js → [...subfieldNames]-415015aebab60436.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-bfbcf19c28c794ae.js → [collectionName]-5accb09715b5122d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-aebecca1d8ec5f98.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-9551a82ddec9f909.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset-72f8fe47beef0f09.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/[id]-3a8aa3f633528e88.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-87512616f35ec6da.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-2f1bf4eac7aa55fd.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{fides-js-docs-1f4335dca5c09860.js → fides-js-docs-5235760b3e508d7d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{index-1343fa525a206571.js → index-8d67cd2872cb682a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-7a631df29cd0e31a.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations-ea3bc43cdaf273de.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/new-privacy-requests-5d8632bba1b81cd4.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/notifications/digests/{[id]-63b3be660fb12c0f.js → [id]-92e01822ecde8fb8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/notifications/digests/{new-d3b577962dd33266.js → new-1256cf6d3f6794e0.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/notifications/digests-0daac00911d27617.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/notifications/providers/{[key]-720cde29f81db47f.js → [key]-2d976fe5e8ba0a3a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/notifications/providers/{new-3668866076b53016.js → new-7766ba497b863740.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/notifications/providers-f2880d2ed4734270.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/notifications/templates/{[id]-8063dceb32310c85.js → [id]-669f585c3458faff.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/notifications/templates/{add-template-4931c70bee62232f.js → add-template-c79e7724e4bc3899.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/notifications/templates-891654e8dc13965c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{notifications-93af719dab3bd053.js → notifications-40c8148244c5d347.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/ant-components-1e86f3e28bd23ed6.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/AntForm-9b7bd8c38f02c091.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikAntFormItem-b18a53a940cf9e19.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikControlled-6d028d7450e77578.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikField-8399083ee2cd8cc2.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/forms-0a910125cdb2b3b8.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-acc90b6f7fe915cb.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-bbc42026f2685438.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-fa82cffba448ccd8.js → configure-c08ca6ad21c99799.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-77c2db582f8823bc.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-bc1c289647e52c48.js → [id]-74ccea4868408e3d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-cbfaa23d96f5ed0b.js → add-property-7d9f09bfe9d44dfc.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{properties-a15a3fd0ed88f39c.js → properties-cd77bc30672bb1fa.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-cc3bd9540132d5ed.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/sandbox/privacy-notices-7ce7d720107ab4b5.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/{[purpose_id]-19de0024418a4924.js → [purpose_id]-ae789892343c24f5.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-13ee1b331ced0846.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields/[id]-ee4e43692336a330.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields/new-ca51d794abfcbf25.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-ecd1dc5db8e81409.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-3b87002753b23ca5.js → domain-records-df06f7e2f668c540.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-aacd9d0ad47082d4.js → domains-da0c77bd510c6c51.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-3cdd0b39901190ba.js → email-templates-950b0c115bf673d8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-61076eedbfd137b9.js → locations-7e36cb4756973a9d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-b07b11d6002f8c8c.js → organization-a0e5ed486d24ccf3.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/privacy-requests-3cdebafb6870d3ad.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-b7c0d3b1b754e70f.js → regulations-159aad34f1021320.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/test-datasets-dbd1a64090ad0946.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-59c89489fa32a4cb.js → [id]-7d042497a57a3788.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems-21f1172e73dfc9f0.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-e553161e6338ee48.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-7dce2916cc589c54.js → new-efc4af017723e57a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-5d5a68e555d18693.js → [id]-b152319d67372ee4.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management-dba8692491f7935e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/webpack-09ca52bc7beb0b43.js +1 -0
- fides/ui-build/static/admin/_next/static/css/3d66bb57ddcb0978.css +1 -0
- fides/ui-build/static/admin/_next/static/css/a1e4be9466578ef1.css +1 -0
- fides/ui-build/static/admin/_next/static/l2vgGUHB04Fi4oEVlVMrc/_buildManifest.js +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/datastore/[monitorId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/datastore.html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/infrastructure/[monitorId].html +1 -0
- fides/ui-build/static/admin/data-discovery/action-center/infrastructure.html +1 -0
- fides/ui-build/static/admin/data-discovery/action-center/website/[monitorId]/[systemId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/website/[monitorId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/website.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/new-privacy-requests.html +1 -1
- fides/ui-build/static/admin/notifications/digests/[id].html +1 -1
- fides/ui-build/static/admin/notifications/digests/new.html +1 -1
- fides/ui-build/static/admin/notifications/digests.html +1 -1
- fides/ui-build/static/admin/notifications/providers/[key].html +1 -1
- fides/ui-build/static/admin/notifications/providers/new.html +1 -1
- fides/ui-build/static/admin/notifications/providers.html +1 -1
- fides/ui-build/static/admin/notifications/templates/[id].html +1 -1
- fides/ui-build/static/admin/notifications/templates/add-template.html +1 -1
- fides/ui-build/static/admin/notifications/templates.html +1 -1
- fides/ui-build/static/admin/notifications.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/privacy-requests/[id].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/sandbox/privacy-notices.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/[id].html +1 -1
- fides/ui-build/static/admin/settings/custom-fields/new.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 -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/api/service/privacy_request/dsr_package/templates/welcome.html +0 -66
- fides/ui-build/static/admin/_next/static/chunks/1099-31f9d973bc287df8.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1316-2606e19807c08aa5.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1437-8b1f6c8797c68bfd.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1467-8808ec8836e033f9.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/155-f6302d32cba4cab6.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1817-2d5cf537a2992c79.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/2121.1de2fa060eae34aa.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/2921-34a43f2f8f5e5e69.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/2962-342ad1b4ab402ded.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3377-988ac2f3a2e8d5d4.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3446-f40c352c43ac950c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/346-aa3b88efb85f2e28.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3696-6f90e41a53d22920.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3700-f695f2f6b8251971.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3772-9f1713f9d5f97a10.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3873-d18e47b327445db5.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3923-f0a85dc5c3684fa0.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4496-4ff19366c597ec16.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4817-1f3e6ea38625d8d5.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5163-e682273cd76a7d07.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5258-62d6bc19add60aa6.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5279-bd6cccabdd6ca447.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/549-28537a6de666944b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5619-9b50cec521203989.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5643-3459282d296a3c59.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6277-3759894435cb8569.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6315-e2fb5ea77179a871.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6344-ca66a6e10d128179.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6419-11d67f7fd4e2f247.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6954-84789a4e4fb04eb9.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/699-8ca44b0de9fa20f0.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7059-2bb7c38578549703.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7202.3febe9e89640c712.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7476-a02d970ea4d3f7d0.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7488-cf92601852e3c509.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7630-a11610c2b31ab2ca.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7773-9ae233109bc64ec2.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/796-e36d610066135f8c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/8011-75af8b480fa114e6.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/8373-22b4d20e8cc06b3a.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/8765-f622a35b40a7ec63.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9037-453224ba3ee65b13.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9046-d9c6498368b993d1.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9187-7438242f0d380bb0.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9278-08cc704317fe535e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9330-f753636a31c4ea04.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9682-d1a3afa1394f8304.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9729-fcf6ff4e3534e4a8.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9899-d6437facac926264.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9965-25621dd507e0cfd6.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-9af75caefc74eaca.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-6528eb24165aceb6.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-11f1683aa15e1a62.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn]-32bd7a7c990e5bf6.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]-d60761c20382b259.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-07e7d38ce34e1e6c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/[resourceUrn]-b07a0707f8c2ec0d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources-aed94957009eb3fd.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-127c114dd8f102ed.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/datastore/[monitorId]-5aa7a9fa96160de8.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/website/[monitorId]/[systemId]-899bf30dde8b3292.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/website/[monitorId]-73085f50abb775c0.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-823d0dd77e66585b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-24a82e07a0008516.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datamap-3b100c44ea9e3988.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-6fbe2b584a509226.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-3d1e48f4b95d7f6b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset-d3c6ecf7f29bea6e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/[id]-0a4aa42be2da0255.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-14313e441a13192c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-f139d1ce26404f30.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-b2d3d28b10a758e6.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations-a733e5d7c3ce9bb8.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/new-privacy-requests-df0c95e408c54c7e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/notifications/digests-aed9afd988a48acf.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/notifications/providers-36a0ac36062abd02.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/notifications/templates-a14c876b49422597.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/ant-components-9cfb469de7b4aeab.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/AntForm-e715cc654fb6a5cd.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikAntFormItem-72ae299bcb6adae6.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikControlled-2337f8c81a766eb0.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikField-0af454f55494f6fa.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/forms-d1b90ffa996fbd89.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/table-migration-19724b9e0581b96d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-cdd3754289a28317.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-cc41ae605f2b55ae.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-6e4c535b6d614596.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-632b3ee563d070f2.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/sandbox/privacy-notices-afe921f6e2a526fb.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-5b6807dced8d03c5.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields/[id]-8634aae3259def37.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields/new-2d9dcca17965dc57.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-7dce52bfc1b2652c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/privacy-requests-084a2b4431d35322.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/test-datasets-b72d36243a0a545c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems-cfaa37a0df83674b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-56a5434969cbe9ba.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management-e6a211d8a0401086.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/webpack-c2c11809187b9f84.js +0 -1
- fides/ui-build/static/admin/_next/static/css/d41a048a166d50e4.css +0 -1
- fides/ui-build/static/admin/_next/static/css/d78390d6134d8328.css +0 -1
- fides/ui-build/static/admin/_next/static/wCNFtmYQhEDMaMPeBB4BM/_buildManifest.js +0 -1
- fides/ui-build/static/admin/poc/table-migration.html +0 -1
- {ethyca_fides-2.73.2b0.dist-info → ethyca_fides-2.76.0rc2.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.73.2b0.dist-info → ethyca_fides-2.76.0rc2.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.73.2b0.dist-info → ethyca_fides-2.76.0rc2.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.73.2b0.dist-info → ethyca_fides-2.76.0rc2.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{wCNFtmYQhEDMaMPeBB4BM → l2vgGUHB04Fi4oEVlVMrc}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head>
|
|
3
|
+
<link
|
|
4
|
+
rel="stylesheet"
|
|
5
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600"
|
|
6
|
+
/>
|
|
7
|
+
<link rel="stylesheet" href="data/main.css" />
|
|
8
|
+
<script>
|
|
9
|
+
// format the "requested at" datetime with the user's local date format
|
|
10
|
+
// and timezone if the Intl API is available in their browser, otherwise default to UTC
|
|
11
|
+
window.onload = function () {
|
|
12
|
+
var requestedAt = document.getElementById("requestedAt");
|
|
13
|
+
var dateString = requestedAt.textContent;
|
|
14
|
+
|
|
15
|
+
if (window.Intl) {
|
|
16
|
+
var formattedDate = new Intl.DateTimeFormat(navigator.language, {
|
|
17
|
+
year: "numeric",
|
|
18
|
+
month: "2-digit",
|
|
19
|
+
day: "2-digit",
|
|
20
|
+
hour: "2-digit",
|
|
21
|
+
minute: "2-digit",
|
|
22
|
+
timeZoneName: "short",
|
|
23
|
+
}).format(new Date(dateString));
|
|
24
|
+
|
|
25
|
+
requestedAt.textContent = formattedDate;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
</script>
|
|
29
|
+
</head>
|
|
30
|
+
|
|
31
|
+
<body>
|
|
32
|
+
<div class="container">
|
|
33
|
+
<div class="header"></div>
|
|
34
|
+
<div class="content">
|
|
35
|
+
<h1>Your requested data</h1>
|
|
36
|
+
<div class="info-grid-container">
|
|
37
|
+
<div class="info-grid">
|
|
38
|
+
<div>Request ID:</div>
|
|
39
|
+
<div>{{ request.id }}</div>
|
|
40
|
+
<div>Request type:</div>
|
|
41
|
+
<div>{{ request.type }}</div>
|
|
42
|
+
{% for identity_type, identity_data in request.identity.items() %}
|
|
43
|
+
<div>{{ identity_data.label }}:</div>
|
|
44
|
+
<div>{{ identity_data.value }}</div>
|
|
45
|
+
{% endfor %}
|
|
46
|
+
<div>Requested at:</div>
|
|
47
|
+
<div id="requestedAt">{{ request.requested_at }}</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="dsr-information-text">
|
|
51
|
+
<p>
|
|
52
|
+
This web link contains all data requested as part of your Data
|
|
53
|
+
Subject Request (DSR). Your information has been compiled from the
|
|
54
|
+
following areas. Unzip the archive, then click on each section to
|
|
55
|
+
open and view your data.
|
|
56
|
+
</p>
|
|
57
|
+
</div>
|
|
58
|
+
<div class="dataset-list">
|
|
59
|
+
{% for name, link in data.items() %}
|
|
60
|
+
<div class="dataset-item">
|
|
61
|
+
<a href="{{ link }}" class="dataset-link">{{ name }}</a>
|
|
62
|
+
</div>
|
|
63
|
+
{% endfor %}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</body>
|
|
68
|
+
</html>
|
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
<html>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
</div>
|
|
27
|
-
|
|
2
|
+
<head>
|
|
3
|
+
<link
|
|
4
|
+
rel="stylesheet"
|
|
5
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600"
|
|
6
|
+
/>
|
|
7
|
+
<link rel="stylesheet" href="../main.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<div class="header"></div>
|
|
12
|
+
<div class="content">
|
|
13
|
+
<div class="button-container">
|
|
14
|
+
<a href="../../clickme.html">
|
|
15
|
+
<div class="button"><img src="../back.svg" /></div>
|
|
16
|
+
<span>Back to datasets</span>
|
|
17
|
+
</a>
|
|
18
|
+
</div>
|
|
19
|
+
<h1>{{ heading }}</h1>
|
|
20
|
+
<div class="table table-hover">
|
|
21
|
+
<div class="table-row">
|
|
22
|
+
<div class="table-cell" style="text-align: left">Collection</div>
|
|
23
|
+
</div>
|
|
24
|
+
{% for name, link in data.items() %}
|
|
25
|
+
<a href="{{ link }}" class="table-row">
|
|
26
|
+
<div class="table-cell" style="text-align: left">{{ name }}</div>
|
|
27
|
+
</a>
|
|
28
|
+
{% endfor %}
|
|
29
|
+
</div>
|
|
28
30
|
</div>
|
|
29
|
-
|
|
31
|
+
</div>
|
|
32
|
+
</body>
|
|
30
33
|
</html>
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
from datetime import datetime, timedelta, timezone
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
|
|
5
|
+
from loguru import logger
|
|
6
|
+
from sqlalchemy.orm import Session, defer
|
|
7
|
+
|
|
8
|
+
from fides.api.models.privacy_request.duplicate_group import (
|
|
9
|
+
DuplicateGroup,
|
|
10
|
+
generate_rule_version,
|
|
11
|
+
)
|
|
12
|
+
from fides.api.models.privacy_request.privacy_request import PrivacyRequest
|
|
13
|
+
from fides.api.schemas.policy import ActionType
|
|
14
|
+
from fides.api.schemas.privacy_request import PrivacyRequestStatus
|
|
15
|
+
from fides.api.task.conditional_dependencies.schemas import (
|
|
16
|
+
Condition,
|
|
17
|
+
ConditionGroup,
|
|
18
|
+
ConditionLeaf,
|
|
19
|
+
GroupOperator,
|
|
20
|
+
Operator,
|
|
21
|
+
)
|
|
22
|
+
from fides.api.task.conditional_dependencies.sql_translator import (
|
|
23
|
+
SQLConditionTranslator,
|
|
24
|
+
)
|
|
25
|
+
from fides.config.config_proxy import ConfigProxy
|
|
26
|
+
from fides.config.duplicate_detection_settings import DuplicateDetectionSettings
|
|
27
|
+
|
|
28
|
+
ACTIONED_REQUEST_STATUSES = [
|
|
29
|
+
PrivacyRequestStatus.approved,
|
|
30
|
+
PrivacyRequestStatus.in_processing,
|
|
31
|
+
PrivacyRequestStatus.requires_manual_finalization,
|
|
32
|
+
PrivacyRequestStatus.requires_input,
|
|
33
|
+
PrivacyRequestStatus.paused,
|
|
34
|
+
PrivacyRequestStatus.awaiting_email_send,
|
|
35
|
+
PrivacyRequestStatus.error,
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class DuplicateDetectionService:
|
|
40
|
+
def __init__(self, db: Session):
|
|
41
|
+
self.db = db
|
|
42
|
+
self._config = ConfigProxy(db).privacy_request_duplicate_detection
|
|
43
|
+
|
|
44
|
+
def is_enabled(self) -> bool:
|
|
45
|
+
return self._config.enabled
|
|
46
|
+
|
|
47
|
+
def _create_identity_conditions(
|
|
48
|
+
self, current_request: PrivacyRequest
|
|
49
|
+
) -> list[Condition]:
|
|
50
|
+
"""Creates conditions for matching identity fields.
|
|
51
|
+
|
|
52
|
+
For identity field matching using the EAV pattern in ProvidedIdentity, we need to match both field_name
|
|
53
|
+
and hashed_value. This function creates the required nested conditions for each identity field.
|
|
54
|
+
Also adds a condition for the policy_id to ensure that we are only matching requests for the same policy.
|
|
55
|
+
"""
|
|
56
|
+
conditions: list[Condition] = []
|
|
57
|
+
current_identities: dict[str, str] = {
|
|
58
|
+
pi.field_name: pi.hashed_value
|
|
59
|
+
for pi in current_request.provided_identities # type: ignore [attr-defined]
|
|
60
|
+
if pi.field_name in self._config.match_identity_fields
|
|
61
|
+
}
|
|
62
|
+
if len(current_identities) != len(self._config.match_identity_fields):
|
|
63
|
+
missing_fields = [
|
|
64
|
+
field
|
|
65
|
+
for field in self._config.match_identity_fields
|
|
66
|
+
if field not in current_identities.keys()
|
|
67
|
+
]
|
|
68
|
+
logger.debug(
|
|
69
|
+
f"Some identity fields were not found in the current request: {missing_fields}"
|
|
70
|
+
)
|
|
71
|
+
return []
|
|
72
|
+
|
|
73
|
+
for field_name, hashed_value in current_identities.items():
|
|
74
|
+
identity_condition = ConditionGroup(
|
|
75
|
+
logical_operator=GroupOperator.and_,
|
|
76
|
+
conditions=[
|
|
77
|
+
ConditionLeaf(
|
|
78
|
+
field_address="privacyrequest.provided_identities.field_name",
|
|
79
|
+
operator=Operator.eq,
|
|
80
|
+
value=field_name,
|
|
81
|
+
),
|
|
82
|
+
ConditionLeaf(
|
|
83
|
+
field_address="privacyrequest.provided_identities.hashed_value",
|
|
84
|
+
operator=Operator.eq,
|
|
85
|
+
value=hashed_value,
|
|
86
|
+
),
|
|
87
|
+
],
|
|
88
|
+
)
|
|
89
|
+
conditions.append(identity_condition)
|
|
90
|
+
policy_condition = ConditionLeaf(
|
|
91
|
+
field_address="privacyrequest.policy_id",
|
|
92
|
+
operator=Operator.eq,
|
|
93
|
+
value=current_request.policy_id,
|
|
94
|
+
)
|
|
95
|
+
conditions.append(policy_condition)
|
|
96
|
+
return conditions
|
|
97
|
+
|
|
98
|
+
def _create_time_window_condition(self, time_window_days: int) -> Condition:
|
|
99
|
+
"""Creates a condition for matching requests within a time window."""
|
|
100
|
+
cutoff_date = datetime.now(timezone.utc) - timedelta(days=time_window_days)
|
|
101
|
+
condition = ConditionLeaf(
|
|
102
|
+
field_address="privacyrequest.created_at",
|
|
103
|
+
operator=Operator.gte,
|
|
104
|
+
value=cutoff_date.isoformat(),
|
|
105
|
+
)
|
|
106
|
+
return condition
|
|
107
|
+
|
|
108
|
+
def create_duplicate_detection_conditions(
|
|
109
|
+
self,
|
|
110
|
+
current_request: PrivacyRequest,
|
|
111
|
+
) -> Optional[ConditionGroup]:
|
|
112
|
+
"""
|
|
113
|
+
Create conditions for duplicate detection based on configuration.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
current_request: The current privacy request to find duplicates for
|
|
117
|
+
config: Duplicate detection configuration settings
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
A ConditionGroup with AND operator, or None if no conditions can be created
|
|
121
|
+
"""
|
|
122
|
+
if len(self._config.match_identity_fields) == 0:
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
identity_conditions = self._create_identity_conditions(current_request)
|
|
126
|
+
if not identity_conditions:
|
|
127
|
+
return None # Only proceed if we have identity conditions
|
|
128
|
+
|
|
129
|
+
time_window_condition = self._create_time_window_condition(
|
|
130
|
+
self._config.time_window_days
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Combine all conditions with AND operator
|
|
134
|
+
all_conditions: list[Condition] = [*identity_conditions, time_window_condition]
|
|
135
|
+
return ConditionGroup(
|
|
136
|
+
logical_operator=GroupOperator.and_, conditions=all_conditions
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def find_duplicate_privacy_requests(
|
|
140
|
+
self,
|
|
141
|
+
current_request: PrivacyRequest,
|
|
142
|
+
) -> list[PrivacyRequest]:
|
|
143
|
+
"""
|
|
144
|
+
Find potential duplicate privacy requests based on duplicate detection configuration.
|
|
145
|
+
|
|
146
|
+
Uses the SQLConditionTranslator to build queries from conditions, which handles
|
|
147
|
+
the ProvidedIdentity relationship using SQLAlchemy's .any() method.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
current_request: The privacy request to check for duplicates
|
|
151
|
+
config: Duplicate detection configuration settings
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
List of PrivacyRequest objects that match the duplicate criteria,
|
|
155
|
+
does not include the current request
|
|
156
|
+
"""
|
|
157
|
+
condition = self.create_duplicate_detection_conditions(current_request)
|
|
158
|
+
|
|
159
|
+
if condition is None:
|
|
160
|
+
return []
|
|
161
|
+
|
|
162
|
+
translator = SQLConditionTranslator(self.db)
|
|
163
|
+
query = translator.generate_query_from_condition(condition)
|
|
164
|
+
|
|
165
|
+
query = query.filter(PrivacyRequest.id != current_request.id).filter(
|
|
166
|
+
PrivacyRequest.deleted_at.is_(None)
|
|
167
|
+
)
|
|
168
|
+
# Apply defer options to prevent loading large columns when checking for duplicates
|
|
169
|
+
# pylint: disable=protected-access
|
|
170
|
+
query = query.options(
|
|
171
|
+
defer(PrivacyRequest._filtered_final_upload),
|
|
172
|
+
defer(PrivacyRequest.access_result_urls),
|
|
173
|
+
)
|
|
174
|
+
return query.all()
|
|
175
|
+
|
|
176
|
+
def generate_dedup_key(self, request: PrivacyRequest) -> str:
|
|
177
|
+
"""
|
|
178
|
+
Generate a dedup key for a request based on the duplicate detection settings.
|
|
179
|
+
"""
|
|
180
|
+
current_identities: dict[str, str] = {
|
|
181
|
+
pi.field_name: pi.hashed_value
|
|
182
|
+
for pi in request.provided_identities # type: ignore [attr-defined]
|
|
183
|
+
if pi.field_name in self._config.match_identity_fields
|
|
184
|
+
}
|
|
185
|
+
if len(current_identities) != len(self._config.match_identity_fields):
|
|
186
|
+
raise ValueError(
|
|
187
|
+
"This request does not contain the required identity fields for duplicate detection."
|
|
188
|
+
)
|
|
189
|
+
return "|".join(
|
|
190
|
+
[
|
|
191
|
+
current_identities[field]
|
|
192
|
+
for field in sorted(self._config.match_identity_fields)
|
|
193
|
+
]
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
def update_duplicate_group_ids(
|
|
197
|
+
self,
|
|
198
|
+
request: PrivacyRequest,
|
|
199
|
+
duplicates: list[PrivacyRequest],
|
|
200
|
+
duplicate_group_id: UUID,
|
|
201
|
+
) -> None:
|
|
202
|
+
"""
|
|
203
|
+
Update the duplicate request group ids for a request and its duplicates.
|
|
204
|
+
Args:
|
|
205
|
+
request: The privacy request to update
|
|
206
|
+
duplicates: The list of duplicate requests to update
|
|
207
|
+
duplicate_group_id: The duplicate request group id to update
|
|
208
|
+
"""
|
|
209
|
+
update_all = [request] + duplicates
|
|
210
|
+
try:
|
|
211
|
+
for privacy_request in update_all:
|
|
212
|
+
privacy_request.duplicate_request_group_id = duplicate_group_id # type: ignore [assignment]
|
|
213
|
+
except Exception as e:
|
|
214
|
+
logger.error(f"Failed to update duplicate request group ids: {e}")
|
|
215
|
+
raise e
|
|
216
|
+
|
|
217
|
+
def mark_as_duplicate(self, request: PrivacyRequest, message: str) -> None:
|
|
218
|
+
"""
|
|
219
|
+
Mark a request as a duplicate.
|
|
220
|
+
"""
|
|
221
|
+
request.update(self.db, data={"status": PrivacyRequestStatus.duplicate})
|
|
222
|
+
logger.debug(message)
|
|
223
|
+
request.add_skipped_execution_log(
|
|
224
|
+
self.db,
|
|
225
|
+
connection_key=None,
|
|
226
|
+
dataset_name="Duplicate Request Detection",
|
|
227
|
+
collection_name=None,
|
|
228
|
+
message=message,
|
|
229
|
+
action_type=(request.policy.get_action_type() if request.policy else None)
|
|
230
|
+
or ActionType.access,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
def add_success_execution_log(self, request: PrivacyRequest, message: str) -> None:
|
|
234
|
+
request.add_success_execution_log(
|
|
235
|
+
db=self.db,
|
|
236
|
+
connection_key=None,
|
|
237
|
+
dataset_name="Duplicate Request Detection",
|
|
238
|
+
collection_name=None,
|
|
239
|
+
message=message,
|
|
240
|
+
action_type=(
|
|
241
|
+
request.policy.get_action_type() # type: ignore [arg-type]
|
|
242
|
+
if request.policy
|
|
243
|
+
else ActionType.access
|
|
244
|
+
),
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
def verified_identity_cases(
|
|
248
|
+
self, request: PrivacyRequest, duplicates: list[PrivacyRequest]
|
|
249
|
+
) -> bool:
|
|
250
|
+
"""
|
|
251
|
+
Apply verified identity rules to determine if a request is a duplicate request.
|
|
252
|
+
- If this request does not have a verified identity, it may be a duplicate if another request in the group is verified.
|
|
253
|
+
- If this is the first request to be verified, it is not a duplicate request
|
|
254
|
+
- If other requests identities were verified before this request, it is a duplicate request
|
|
255
|
+
Args:
|
|
256
|
+
request: The privacy request to check
|
|
257
|
+
duplicates: The list of duplicate requests
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
True if the request is a duplicate request, False otherwise
|
|
261
|
+
"""
|
|
262
|
+
verified_in_group = [
|
|
263
|
+
duplicate for duplicate in duplicates if duplicate.identity_verified_at
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
# The request identity is not verified.
|
|
267
|
+
if not request.identity_verified_at:
|
|
268
|
+
if len(verified_in_group) > 0:
|
|
269
|
+
message = f"Request {request.id} is a duplicate: it is duplicating request(s) {[duplicate.id for duplicate in verified_in_group]}."
|
|
270
|
+
self.mark_as_duplicate(request, message)
|
|
271
|
+
return True
|
|
272
|
+
|
|
273
|
+
canonical_request = min(duplicates, key=lambda x: x.created_at) # type: ignore [arg-type, return-value]
|
|
274
|
+
canonical_request_created_at = canonical_request.created_at or datetime.now(
|
|
275
|
+
timezone.utc
|
|
276
|
+
)
|
|
277
|
+
request_created_at = request.created_at or datetime.now(timezone.utc)
|
|
278
|
+
if request_created_at < canonical_request_created_at:
|
|
279
|
+
message = f"Request {request.id} is not a duplicate: it is the first request to be created in the group."
|
|
280
|
+
logger.debug(message)
|
|
281
|
+
self.add_success_execution_log(request, message)
|
|
282
|
+
return False
|
|
283
|
+
|
|
284
|
+
message = f"Request {request.id} is a duplicate: it is duplicating request(s) ['{canonical_request.id}']."
|
|
285
|
+
self.mark_as_duplicate(request, message)
|
|
286
|
+
return True
|
|
287
|
+
|
|
288
|
+
# The request identity is verified.
|
|
289
|
+
if not verified_in_group:
|
|
290
|
+
message = f"Request {request.id} is not a duplicate: it is the first request to be verified in the group."
|
|
291
|
+
logger.debug(message)
|
|
292
|
+
for duplicate in duplicates:
|
|
293
|
+
dup_message = f"Request {duplicate.id} is a duplicate: it is duplicating request(s) ['{request.id}']."
|
|
294
|
+
self.mark_as_duplicate(duplicate, dup_message)
|
|
295
|
+
self.add_success_execution_log(request, message)
|
|
296
|
+
return False
|
|
297
|
+
|
|
298
|
+
# If this request is the first with a verified identity, it is not a duplicate.
|
|
299
|
+
canonical_request = min(verified_in_group, key=lambda x: x.identity_verified_at) # type: ignore [arg-type, return-value]
|
|
300
|
+
canonical_request_verified_at = (
|
|
301
|
+
canonical_request.identity_verified_at or datetime.now(timezone.utc)
|
|
302
|
+
)
|
|
303
|
+
request_verified_at = request.identity_verified_at or datetime.now(timezone.utc)
|
|
304
|
+
if request_verified_at < canonical_request_verified_at:
|
|
305
|
+
message = f"Request {request.id} is not a duplicate: it is the first request to be verified in the group."
|
|
306
|
+
logger.debug(message)
|
|
307
|
+
self.add_success_execution_log(request, message)
|
|
308
|
+
for duplicate in duplicates:
|
|
309
|
+
dup_message = f"Request {duplicate.id} is a duplicate: it is duplicating request(s) ['{request.id}']."
|
|
310
|
+
self.mark_as_duplicate(duplicate, dup_message)
|
|
311
|
+
return False
|
|
312
|
+
message = f"Request {request.id} is a duplicate: it is duplicating request(s) ['{canonical_request.id}']."
|
|
313
|
+
self.mark_as_duplicate(request, message)
|
|
314
|
+
return True
|
|
315
|
+
|
|
316
|
+
# pylint: disable=too-many-return-statements
|
|
317
|
+
def is_duplicate_request(self, request: PrivacyRequest) -> bool:
|
|
318
|
+
"""
|
|
319
|
+
Determine if a request is a duplicate request and assigns a duplicate request group id.
|
|
320
|
+
|
|
321
|
+
The hierarchy is:
|
|
322
|
+
1. Actioned requests: if this request duplicates an actioned request, it is a duplicate.
|
|
323
|
+
2. Verified identity requests:
|
|
324
|
+
a. if this request has a verified identity:
|
|
325
|
+
- If none of the duplicates have a verified identity, it is not a duplicate.
|
|
326
|
+
- If duplicates have verified identities, but this request is the first with a verified identity, it is not a duplicate.
|
|
327
|
+
b. if this request does not have a verified identity:
|
|
328
|
+
- If no duplicates have a verified identity, and this was the first created request, it is not a duplicate.
|
|
329
|
+
3. First created request: if this is the first created request in the group, it is not a duplicate.
|
|
330
|
+
4. If no canonical requests are found (meaning all requests are marked as duplicates), this request is not a duplicate.
|
|
331
|
+
- Could occur if configuration changes and previous requests were already marked as duplicates.
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
request: The privacy request to check
|
|
335
|
+
config: Duplicate detection configuration settings
|
|
336
|
+
Returns:
|
|
337
|
+
True if the request is a duplicate request, False otherwise
|
|
338
|
+
"""
|
|
339
|
+
if request.policy.get_action_type() == ActionType.consent:
|
|
340
|
+
message = f"Consent request {request.id} is not a duplicate."
|
|
341
|
+
logger.info(message)
|
|
342
|
+
self.add_success_execution_log(request, message)
|
|
343
|
+
return False
|
|
344
|
+
duplicates = self.find_duplicate_privacy_requests(request)
|
|
345
|
+
rule_version = generate_rule_version(
|
|
346
|
+
DuplicateDetectionSettings(
|
|
347
|
+
enabled=self._config.enabled,
|
|
348
|
+
time_window_days=self._config.time_window_days,
|
|
349
|
+
match_identity_fields=self._config.match_identity_fields,
|
|
350
|
+
)
|
|
351
|
+
)
|
|
352
|
+
try:
|
|
353
|
+
dedup_key = self.generate_dedup_key(request)
|
|
354
|
+
except ValueError as e:
|
|
355
|
+
message = f"Request {request.id} is not a duplicate: {e}"
|
|
356
|
+
logger.warning(message)
|
|
357
|
+
self.add_success_execution_log(request, message)
|
|
358
|
+
return False
|
|
359
|
+
|
|
360
|
+
_, duplicate_group = DuplicateGroup.get_or_create(
|
|
361
|
+
db=self.db, data={"rule_version": rule_version, "dedup_key": dedup_key}
|
|
362
|
+
)
|
|
363
|
+
if duplicate_group is None:
|
|
364
|
+
message = f"Failed to create duplicate group for request {request.id} with dedup key {dedup_key}"
|
|
365
|
+
logger.error(message)
|
|
366
|
+
request.add_error_execution_log(
|
|
367
|
+
db=self.db,
|
|
368
|
+
connection_key=None,
|
|
369
|
+
dataset_name="Duplicate Request Detection",
|
|
370
|
+
collection_name=None,
|
|
371
|
+
message=message,
|
|
372
|
+
action_type=(
|
|
373
|
+
request.policy.get_action_type() # type: ignore [arg-type]
|
|
374
|
+
if request.policy
|
|
375
|
+
else ActionType.access
|
|
376
|
+
),
|
|
377
|
+
)
|
|
378
|
+
return False
|
|
379
|
+
|
|
380
|
+
self.update_duplicate_group_ids(request, duplicates, duplicate_group.id) # type: ignore [arg-type]
|
|
381
|
+
|
|
382
|
+
if request.status in ACTIONED_REQUEST_STATUSES:
|
|
383
|
+
message = (
|
|
384
|
+
f"Request {request.id} is not a duplicate: it is an actioned request."
|
|
385
|
+
)
|
|
386
|
+
logger.debug(message)
|
|
387
|
+
self.add_success_execution_log(request, message)
|
|
388
|
+
return False
|
|
389
|
+
|
|
390
|
+
# if this is the only request in the group, it is not a duplicate
|
|
391
|
+
if len(duplicates) == 0:
|
|
392
|
+
message = f"Request {request.id} is not a duplicate."
|
|
393
|
+
logger.debug(message)
|
|
394
|
+
self.add_success_execution_log(request, message)
|
|
395
|
+
return False
|
|
396
|
+
|
|
397
|
+
if request.status == PrivacyRequestStatus.duplicate:
|
|
398
|
+
return True
|
|
399
|
+
|
|
400
|
+
# only compare to non-duplicate/complete requests for the following cases
|
|
401
|
+
canonical_requests = [
|
|
402
|
+
duplicate
|
|
403
|
+
for duplicate in duplicates
|
|
404
|
+
if duplicate.status
|
|
405
|
+
not in [
|
|
406
|
+
PrivacyRequestStatus.duplicate,
|
|
407
|
+
PrivacyRequestStatus.complete,
|
|
408
|
+
PrivacyRequestStatus.denied,
|
|
409
|
+
]
|
|
410
|
+
]
|
|
411
|
+
# If no non-duplicate requests are found, this request is not a duplicate.
|
|
412
|
+
if len(canonical_requests) == 0:
|
|
413
|
+
message = f"Request {request.id} is not a duplicate."
|
|
414
|
+
logger.debug(message)
|
|
415
|
+
self.add_success_execution_log(request, message)
|
|
416
|
+
return False
|
|
417
|
+
|
|
418
|
+
# If any requests in group are actioned, this request is a duplicate.
|
|
419
|
+
actioned_in_group = [
|
|
420
|
+
duplicate
|
|
421
|
+
for duplicate in canonical_requests
|
|
422
|
+
if duplicate.status in ACTIONED_REQUEST_STATUSES
|
|
423
|
+
]
|
|
424
|
+
if len(actioned_in_group) > 0:
|
|
425
|
+
message = f"Request {request.id} is a duplicate: it is duplicating actioned request(s) {[duplicate.id for duplicate in actioned_in_group]}."
|
|
426
|
+
self.mark_as_duplicate(request, message)
|
|
427
|
+
return True
|
|
428
|
+
# Check against verified identity rules.
|
|
429
|
+
return self.verified_identity_cases(request, canonical_requests)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def check_for_duplicates(db: Session, privacy_request: PrivacyRequest) -> None:
|
|
433
|
+
duplicate_detection_service = DuplicateDetectionService(db)
|
|
434
|
+
if duplicate_detection_service.is_enabled():
|
|
435
|
+
logger.info(
|
|
436
|
+
"Duplicate detection is enabled. Checking if privacy request is a duplicate."
|
|
437
|
+
)
|
|
438
|
+
if duplicate_detection_service.is_duplicate_request(privacy_request):
|
|
439
|
+
logger.info("Terminating privacy request: request is a duplicate.")
|
|
@@ -50,8 +50,9 @@ def send_email_batch(self: DatabaseTask) -> EmailExitState:
|
|
|
50
50
|
batch_id = str(uuid.uuid4())
|
|
51
51
|
logger.info("Starting batch email send {}...", batch_id)
|
|
52
52
|
with self.get_new_session() as session:
|
|
53
|
+
# Use query_without_large_columns to prevent OOM errors when processing many privacy requests
|
|
53
54
|
privacy_requests: Query = (
|
|
54
|
-
|
|
55
|
+
PrivacyRequest.query_without_large_columns(session)
|
|
55
56
|
.filter(
|
|
56
57
|
PrivacyRequest.status == PrivacyRequestStatus.awaiting_email_send
|
|
57
58
|
)
|
|
@@ -10,7 +10,7 @@ from loguru import logger
|
|
|
10
10
|
# pylint: disable=no-name-in-module
|
|
11
11
|
from psycopg2.errors import InternalError_ # type: ignore[import-untyped]
|
|
12
12
|
from pydantic import ValidationError as PydanticValidationError
|
|
13
|
-
from sqlalchemy.orm import Query, Session
|
|
13
|
+
from sqlalchemy.orm import Query, Session, selectinload
|
|
14
14
|
|
|
15
15
|
from fides.api import common_exceptions
|
|
16
16
|
from fides.api.common_exceptions import (
|
|
@@ -71,6 +71,7 @@ from fides.api.service.privacy_request.attachment_handling import (
|
|
|
71
71
|
get_attachments_content,
|
|
72
72
|
process_attachments_for_upload,
|
|
73
73
|
)
|
|
74
|
+
from fides.api.service.privacy_request.duplication_detection import check_for_duplicates
|
|
74
75
|
from fides.api.service.storage.storage_uploader_service import upload
|
|
75
76
|
from fides.api.task.filter_results import filter_data_categories
|
|
76
77
|
from fides.api.task.graph_runners import access_runner, consent_runner, erasure_runner
|
|
@@ -426,7 +427,11 @@ def run_privacy_request(
|
|
|
426
427
|
logger.info("Resuming privacy request from checkpoint: '{}'", from_step)
|
|
427
428
|
|
|
428
429
|
with self.get_new_session() as session:
|
|
429
|
-
privacy_request =
|
|
430
|
+
privacy_request = (
|
|
431
|
+
PrivacyRequest.query_without_large_columns(session)
|
|
432
|
+
.filter(PrivacyRequest.id == privacy_request_id)
|
|
433
|
+
.first()
|
|
434
|
+
)
|
|
430
435
|
if not privacy_request:
|
|
431
436
|
raise common_exceptions.PrivacyRequestNotFound(
|
|
432
437
|
f"Privacy request with id {privacy_request_id} not found"
|
|
@@ -446,6 +451,10 @@ def run_privacy_request(
|
|
|
446
451
|
logger.info("Terminating privacy request: request deleted.")
|
|
447
452
|
return
|
|
448
453
|
|
|
454
|
+
check_for_duplicates(db=session, privacy_request=privacy_request)
|
|
455
|
+
if privacy_request.status == PrivacyRequestStatus.duplicate:
|
|
456
|
+
return
|
|
457
|
+
|
|
449
458
|
logger.info("Dispatching privacy request")
|
|
450
459
|
privacy_request.start_processing(session)
|
|
451
460
|
|
|
@@ -489,7 +498,15 @@ def run_privacy_request(
|
|
|
489
498
|
)
|
|
490
499
|
|
|
491
500
|
try:
|
|
492
|
-
|
|
501
|
+
# Eager load connection_config and ctl_dataset to avoid N+1 queries
|
|
502
|
+
datasets = (
|
|
503
|
+
session.query(DatasetConfig)
|
|
504
|
+
.options(
|
|
505
|
+
selectinload(DatasetConfig.connection_config),
|
|
506
|
+
selectinload(DatasetConfig.ctl_dataset),
|
|
507
|
+
)
|
|
508
|
+
.all()
|
|
509
|
+
)
|
|
493
510
|
dataset_graphs = [
|
|
494
511
|
dataset_config.get_graph()
|
|
495
512
|
for dataset_config in datasets
|
|
@@ -516,7 +533,12 @@ def run_privacy_request(
|
|
|
516
533
|
key: value["value"] if isinstance(value, dict) else value
|
|
517
534
|
for key, value in privacy_request.get_cached_identity_data().items()
|
|
518
535
|
}
|
|
519
|
-
|
|
536
|
+
# Eager load datasets relationship to avoid N+1 queries in filter_fides_connector_datasets
|
|
537
|
+
connection_configs = (
|
|
538
|
+
session.query(ConnectionConfig)
|
|
539
|
+
.options(selectinload(ConnectionConfig.datasets))
|
|
540
|
+
.all()
|
|
541
|
+
)
|
|
520
542
|
fides_connector_datasets: set[str] = filter_fides_connector_datasets(
|
|
521
543
|
connection_configs
|
|
522
544
|
)
|
|
@@ -934,7 +956,11 @@ def mark_paused_privacy_request_as_expired(privacy_request_id: str) -> None:
|
|
|
934
956
|
"""Mark "paused" PrivacyRequest as "errored" after its associated identity data in the redis cache has expired."""
|
|
935
957
|
SessionLocal = get_db_session(CONFIG)
|
|
936
958
|
db = SessionLocal()
|
|
937
|
-
privacy_request =
|
|
959
|
+
privacy_request = (
|
|
960
|
+
PrivacyRequest.query_without_large_columns(db)
|
|
961
|
+
.filter(PrivacyRequest.id == privacy_request_id)
|
|
962
|
+
.first()
|
|
963
|
+
)
|
|
938
964
|
if not privacy_request:
|
|
939
965
|
logger.info(
|
|
940
966
|
"Attempted to mark as expired. No privacy request with id '{}' found.",
|