ethyca-fides 2.66.1rc0__py2.py3-none-any.whl → 2.66.2b0__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.66.1rc0.dist-info → ethyca_fides-2.66.2b0.dist-info}/METADATA +1 -1
- {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b0.dist-info}/RECORD +257 -248
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/7e9a2b52f498_adding_masking_secrets.py +60 -0
- fides/api/alembic/migrations/versions/d0031087eacb_create_manualtaskconditionaldependency_.py +106 -0
- fides/api/api/v1/endpoints/dataset_config_endpoints.py +13 -5
- fides/api/api/v1/endpoints/drp_endpoints.py +7 -1
- fides/api/api/v1/endpoints/user_endpoints.py +83 -7
- fides/api/app_setup.py +3 -2
- fides/api/common_exceptions.py +4 -0
- fides/api/db/base.py +1 -0
- fides/api/db/database.py +1 -1
- fides/api/graph/execution.py +30 -0
- fides/api/models/manual_task/__init__.py +2 -0
- fides/api/models/manual_task/conditional_dependency.py +144 -0
- fides/api/models/{manual_task.py → manual_task/manual_task.py} +10 -0
- fides/api/models/masking_secret.py +72 -0
- fides/api/models/policy.py +23 -0
- fides/api/models/privacy_request/privacy_request.py +25 -13
- fides/api/oauth/roles.py +2 -0
- fides/api/schemas/application_config.py +11 -1
- fides/api/schemas/masking/masking_secrets.py +1 -1
- fides/api/service/connectors/base_connector.py +1 -0
- fides/api/service/connectors/bigquery_connector.py +67 -19
- fides/api/service/connectors/dynamodb_connector.py +2 -1
- fides/api/service/connectors/fides_connector.py +1 -0
- fides/api/service/connectors/http_connector.py +1 -0
- fides/api/service/connectors/manual_task_connector.py +1 -0
- fides/api/service/connectors/manual_webhook_connector.py +2 -1
- fides/api/service/connectors/mongodb_connector.py +1 -0
- fides/api/service/connectors/okta_connector.py +1 -0
- fides/api/service/connectors/query_configs/bigquery_query_config.py +91 -32
- fides/api/service/connectors/rds_mysql_connector.py +1 -0
- fides/api/service/connectors/rds_postgres_connector.py +1 -0
- fides/api/service/connectors/s3_connector.py +1 -0
- fides/api/service/connectors/saas_connector.py +1 -0
- fides/api/service/connectors/scylla_connector.py +1 -0
- fides/api/service/connectors/sql_connector.py +36 -4
- fides/api/service/connectors/website_connector.py +1 -0
- fides/api/service/privacy_request/request_runner_service.py +26 -0
- fides/api/service/privacy_request/request_service.py +1 -22
- fides/api/task/conditional_dependencies/__init__.py +0 -0
- fides/api/task/conditional_dependencies/evaluator.py +109 -0
- fides/api/task/conditional_dependencies/schemas.py +54 -0
- fides/api/task/deprecated_graph_task.py +24 -6
- fides/api/task/execute_request_tasks.py +88 -11
- fides/api/task/graph_task.py +38 -3
- fides/api/task/manual/manual_task_graph_task.py +1 -0
- fides/api/util/cache.py +5 -0
- fides/api/util/encryption/secrets_util.py +48 -18
- fides/common/api/scope_registry.py +3 -0
- fides/config/utils.py +1 -0
- fides/service/privacy_request/privacy_request_service.py +6 -1
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/QGhYEMWDATmdofDzIl0_2/_buildManifest.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1817-6f35f58cd08b04ae.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/431-ade3e312fac3430b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/6780-4b687168dd8daa84.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/8237-55049f8f5fd5e058.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-39ccb07327c2c5d5.js → _app-65e47e67bee20654.js} +56 -56
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-71579a199158952e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-7e63ac744c45f6da.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-e2d5d7e2a5265e68.js → integrations-f10a7dcf7541c865.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/{table-migration-69ad86b7a8a9a115.js → table-migration-05616e2ae20ff4f8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-f18b2b83c4592f03.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-1daa805a3099c6d4.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-fe58cebba358119d.js +1 -0
- fides/ui-build/static/admin/_next/static/css/{5bfb2473e5701527.css → 23cf870196941c9a.css} +1 -1
- fides/ui-build/static/admin/_next/static/css/b81194f2c3930152.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 +3 -3
- fides/ui-build/static/admin/lib/fides.js +3 -3
- 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/IeU4qLtEtRJo0FsaIFIr8/_buildManifest.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1817-e601e737e3cc7a0e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/431-34f0b91c26f8d9ab.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6780-5bd185892f6af46e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-2265ecb899d45fbc.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-8ab33a1e0272df1f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-32600543eb7b584f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-cbe4c8f9096b6543.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-ff5738706da07801.js +0 -1
- fides/ui-build/static/admin/_next/static/css/2cadb5f62dcd7c2b.css +0 -1
- {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b0.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b0.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b0.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.66.1rc0.dist-info → ethyca_fides-2.66.2b0.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{IeU4qLtEtRJo0FsaIFIr8 → QGhYEMWDATmdofDzIl0_2}/_ssgManifest.js +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{203-5a663f465ba26bb4.js → 203-d9fe5384a6e94799.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{2921-455e6357b74d2f76.js → 2921-86f1547ac40a5cdf.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3450-0ba194991d0cca88.js → 3450-f7bb8d46fbe4e78d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3855-e172870d3e21b0dd.js → 3855-e2fa6db53d32c3de.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3872-46cebf7ec1b31a2b.js → 3872-08c855f3edb230c4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3923-6cc911dafccc5f63.js → 3923-13a6b4da2d51bf8f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{401-1b529d5800aa1f3a.js → 401-3cc1fee61494e3bd.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{409-a257e14acebcd73b.js → 409-45a125437261580c.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{4121-2bc09fc4ddbfe5cb.js → 4121-f50675521dfee6eb.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{4230-60100f7ef3ddcde1.js → 4230-840c287045c88b34.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{4608-bbb7bf511a05c3c2.js → 4608-a8e3100e2806dbff.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{5309-d73339f062763fed.js → 5309-67bdf9001531e972.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{5574-b13021775a15bfd2.js → 5574-a7d8a753d273b11a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6084-7178ff6ea6822475.js → 6084-a872a8bc704c980f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6662-507be5d46e5b719b.js → 6662-4a7805f74af51f1d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6853-2ad3e08fe6f9f5f2.js → 6853-4f8bf6558f8c6a46.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6882-6af16fef26c21e06.js → 6882-85c6277d9531a83a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6954-bb875d9ac89f6030.js → 6954-34e062e4bffc7e71.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{7476-281ee9a8286556f3.js → 7476-cfe41e915f0c4f40.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{7630-9aac73191ed5ed13.js → 7630-7a8039aa37893129.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{787-fb41002f797eb2df.js → 787-d05b25c73f49b6af.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{79-7e87aff851423d4a.js → 79-1952e8aab93b7209.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{796-329a5f823ec258a5.js → 796-d2876f4d09a6d81f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9046-ce9567c9074563e2.js → 9046-54877976a0529de2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9226-746771d47dff6266.js → 9226-7a799c930b73f217.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9826-111aaee8bd8dbd09.js → 9826-b7d5467e3a3225c8.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9951-9b753ad7c3f51bdf.js → 9951-595d0f1588215081.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{404-aece2c920ea14514.js → 404-2d803dab6a00f353.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-98777246bec9dc2a.js → manual-acb59f8b5e97512a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-dc75dc6e37e52f05.js → multiple-8ff7f37913ad736a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-a71c0aff4e0e6535.js → add-systems-f28841affa791975.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-24d226b5a8de5c74.js → add-vendors-d00c9034cdeb0236.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-6a8ef51138ac926a.js → configure-50d21e23b607688b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-1edf582ba3cd3bbb.js → [id]-fc3a011154a2e1de.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-685771e5f7196d87.js → privacy-experience-09d4408014bcfe1c.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-6ccedc70dc447089.js → [id]-d67542783ef5ddac.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-944bca1cc57985b5.js → new-3f20e8a316bb3d5b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-84f4bd14ce8673bc.js → privacy-notices-23e9dcd4590312d2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-6f86ab63a08a6528.js → properties-057cad65e7414a44.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{reporting-afdbd4665657cfa1.js → reporting-8f891957c8944137.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-73d3cbf68f7c3a31.js → consent-509691da6b06f834.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-11d52f1570759c4d.js → [resourceUrn]-99c9092d65c94807.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-6ba9e160dae64695.js → [projectUrn]-80a6cc8e8573514a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-32eac8bbd217615a.js → projects-774fecea22ba8852.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-b83afa5565d0c84e.js → [resourceUrn]-f6bd6aff389cb9fe.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{resources-7648bbd4f6711e4d.js → resources-6c3714ee97a718c1.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-6f630d42ac9fb6b4.js → data-catalog-2f43cfefc869b85f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{action-center-9ddb52ebb7ac4c71.js → action-center-409694b8441bd8fb.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{activity-9aa744d56cdacb0d.js → activity-5af9381f02b2aff6.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-393e20924c83373e.js → [resourceUrn]-31e6c54794a9883e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-8733807dad4bc96e.js → detection-2822a423a7ad0550.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-14bd7500362ff224.js → [resourceUrn]-6421ce247549c5d6.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-9e7dfd5a6acc2e8f.js → discovery-3eac407ac5181a3c.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-7674b97d655c193b.js → datamap-e707dc714452498e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-c0d2bfd465df20e0.js → [...subfieldNames]-1c98bd0959d9570a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-28280a8a39a6e37c.js → [collectionName]-e548cabda7da32c9.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-006b695e5af5ef24.js → [datasetId]-a8e8b5f4ee7af86c.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-82fb246d87e58ebd.js → new-513c862c3a707735.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-20165c31ab1bc7cf.js → dataset-747b7a13289f1cd7.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-b4a6bcc87d126840.js → [id]-3d22525b3c327b2e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-f95d7b0bbfc58f5a.js → new-d2cad97495e86adb.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{datastore-connection-c391c6fad56eec48.js → datastore-connection-6865aa958f3da342.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{index-1919aab9e5834b51.js → index-f127ebaac4689d10.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/integrations/{[id]-eacabe4a80cb8813.js → [id]-8d83a5518c00fcfc.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-53fecfb9dd6a1e0c.js → [id]-5627d0d0668077f9.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-76b01cec5fde10a9.js → add-template-feca66ad5c5fe54a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{messaging-5094ffea13f32ed9.js → messaging-c1bd3e7adbe8d2d3.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{ant-components-5c08e8447c45ce44.js → ant-components-64a322d01aae5ca7.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{AntForm-06ad5f34585480aa.js → AntForm-8bca16a7726e7eb2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikAntFormItem-6f071c2bc9446cb0.js → FormikAntFormItem-b0f246fc3b67ebf7.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikControlled-efcc38c58991ac9e.js → FormikControlled-1a0852b090bfc392.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikField-430ba5c979abfb7c.js → FormikField-11f3de1b45e36583.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{forms-5c561880bf131afb.js → forms-1b73a1c2b6c6285f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{messaging-10ce53ea356f8bad.js → messaging-f9be923340cd99cf.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{storage-5501bbb129fee9c4.js → storage-eb1ccc8a5dbf0fe5.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-d888a69a3bbe040e.js → configure-295bfe6880b209f2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-d3d8e3d7583ec635.js → [id]-dd99183f93763ae4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-1af10ed303815d46.js → add-property-0bdbc1fcbf553b8f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{properties-cebc0dc186be499a.js → properties-e959378bb32b6b73.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/reporting/{datamap-e130c0197362e8f3.js → datamap-2a98bd257edd8f47.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/{alpha-5e1322de868d615e.js → alpha-8152e5828469cf91.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-241f95e372b65d0f.js → about-9839e544924ac1f2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/{[purpose_id]-fc201657f4a782c7.js → [purpose_id]-668d74c041d74650.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-c2d39cba8396ef3a.js → consent-f7a0f8367129cd70.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-d992103cc55901ae.js → custom-fields-10df8d6fdc8bc149.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-41242f805599feda.js → domain-records-4056a3323cdc7042.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-2e885f74c92f669c.js → domains-5a3691559262e874.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-ff112655ad5f41e5.js → email-templates-e1d3c3d0ab878812.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-023e1895552817de.js → locations-6946e78a5d43e654.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-ac403c0886b20e20.js → organization-26df33de21fb11b3.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-86062a18e081a52a.js → regulations-102efd9199e87124.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-7a3396ac819c7904.js → test-datasets-170c9c55f65ffb0f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-8314a819837f5b2a.js → [id]-d4a57ea18935dd70.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{systems-21f423a7c417aa9d.js → systems-2112c006765c66b6.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-6387fcc8cce872eb.js → taxonomy-a53e89319582ce58.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-a2524414e968f862.js → new-bc4eb541906781e6.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-173ac3a1ed2b05a6.js → user-management-45bfa04e45a7d13f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{webpack-ff0cd6bff75588da.js → webpack-ff4c22c9f0840531.js} +0 -0
|
@@ -158,6 +158,7 @@ class RDSMySQLConnector(RDSConnectorMixin, SQLConnector):
|
|
|
158
158
|
privacy_request: PrivacyRequest,
|
|
159
159
|
request_task: RequestTask,
|
|
160
160
|
rows: List[Row],
|
|
161
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
161
162
|
) -> int:
|
|
162
163
|
"""DSR execution not yet supported for RDS MySQL"""
|
|
163
164
|
return 0
|
|
@@ -147,6 +147,7 @@ class RDSPostgresConnector(RDSConnectorMixin, SQLConnector):
|
|
|
147
147
|
privacy_request: PrivacyRequest,
|
|
148
148
|
request_task: RequestTask,
|
|
149
149
|
rows: List[Row],
|
|
150
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
150
151
|
) -> int:
|
|
151
152
|
"""DSR execution not yet supported for RDS Postgres"""
|
|
152
153
|
return 0
|
|
@@ -517,6 +517,7 @@ class SaaSConnector(BaseConnector[AuthenticatedClient], Contextualizable):
|
|
|
517
517
|
privacy_request: PrivacyRequest,
|
|
518
518
|
request_task: RequestTask,
|
|
519
519
|
rows: List[Row],
|
|
520
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
520
521
|
) -> int:
|
|
521
522
|
"""Execute a masking request. Return the number of rows that have been updated."""
|
|
522
523
|
self.set_privacy_request_state(privacy_request, node, request_task)
|
|
@@ -150,6 +150,7 @@ class ScyllaConnector(BaseConnector[Cluster]):
|
|
|
150
150
|
privacy_request: PrivacyRequest,
|
|
151
151
|
request_task: RequestTask,
|
|
152
152
|
rows: List[Row],
|
|
153
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
153
154
|
) -> int:
|
|
154
155
|
"""Execute a masking request"""
|
|
155
156
|
query_config = self.query_config(node)
|
|
@@ -27,11 +27,13 @@ from fides.api.graph.execution import ExecutionNode
|
|
|
27
27
|
from fides.api.models.connectionconfig import ConnectionConfig, ConnectionTestStatus
|
|
28
28
|
from fides.api.models.policy import Policy
|
|
29
29
|
from fides.api.models.privacy_request import PrivacyRequest, RequestTask
|
|
30
|
+
from fides.api.schemas.application_config import SqlDryRunMode
|
|
30
31
|
from fides.api.schemas.connection_configuration import ConnectionConfigSecretsSchema
|
|
31
32
|
from fides.api.service.connectors.base_connector import BaseConnector
|
|
32
33
|
from fides.api.service.connectors.query_configs.query_config import SQLQueryConfig
|
|
33
34
|
from fides.api.util.collection_util import Row
|
|
34
35
|
from fides.config import get_config
|
|
36
|
+
from fides.config.config_proxy import ConfigProxy
|
|
35
37
|
|
|
36
38
|
from fides.api.models.sql_models import ( # type: ignore[attr-defined] # isort: skip
|
|
37
39
|
Dataset as CtlDataset,
|
|
@@ -58,6 +60,23 @@ class SQLConnector(BaseConnector[Engine]):
|
|
|
58
60
|
)
|
|
59
61
|
self.ssh_server: sshtunnel._ForwardServer = None
|
|
60
62
|
|
|
63
|
+
def should_dry_run(self, mode_to_check: SqlDryRunMode) -> bool:
|
|
64
|
+
"""
|
|
65
|
+
Check if SQL dry run is enabled for the specified mode.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
mode_to_check: The SqlDryRunMode to check for
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
bool: True if the current mode matches the mode to check
|
|
72
|
+
"""
|
|
73
|
+
from fides.api.api.deps import get_autoclose_db_session as get_db
|
|
74
|
+
|
|
75
|
+
with get_db() as db:
|
|
76
|
+
config_proxy = ConfigProxy(db)
|
|
77
|
+
current_mode = getattr(config_proxy.execution, "sql_dry_run", None)
|
|
78
|
+
return current_mode == mode_to_check
|
|
79
|
+
|
|
61
80
|
@staticmethod
|
|
62
81
|
def cursor_result_to_rows(results: CursorResult) -> List[Row]:
|
|
63
82
|
"""Convert SQLAlchemy results to a list of dictionaries"""
|
|
@@ -140,6 +159,10 @@ class SQLConnector(BaseConnector[Engine]):
|
|
|
140
159
|
if query is None:
|
|
141
160
|
return []
|
|
142
161
|
|
|
162
|
+
if self.should_dry_run(SqlDryRunMode.access):
|
|
163
|
+
logger.warning(f"SQL DRY RUN - Would execute SQL: {query}")
|
|
164
|
+
return []
|
|
165
|
+
|
|
143
166
|
with client.connect() as connection:
|
|
144
167
|
self.set_schema(connection)
|
|
145
168
|
results = connection.execute(query)
|
|
@@ -160,6 +183,10 @@ class SQLConnector(BaseConnector[Engine]):
|
|
|
160
183
|
if stmt is None:
|
|
161
184
|
return []
|
|
162
185
|
|
|
186
|
+
if self.should_dry_run(SqlDryRunMode.access):
|
|
187
|
+
logger.warning(f"SQL DRY RUN - Would execute SQL: {stmt}")
|
|
188
|
+
return []
|
|
189
|
+
|
|
163
190
|
logger.info("Starting data retrieval for {}", node.address)
|
|
164
191
|
with client.connect() as connection:
|
|
165
192
|
self.set_schema(connection)
|
|
@@ -178,20 +205,25 @@ class SQLConnector(BaseConnector[Engine]):
|
|
|
178
205
|
privacy_request: PrivacyRequest,
|
|
179
206
|
request_task: RequestTask,
|
|
180
207
|
rows: List[Row],
|
|
208
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
181
209
|
) -> int:
|
|
182
210
|
"""Execute a masking request. Returns the number of records masked"""
|
|
183
211
|
query_config = self.query_config(node)
|
|
184
212
|
update_ct = 0
|
|
185
213
|
client = self.client()
|
|
214
|
+
|
|
186
215
|
for row in rows:
|
|
187
216
|
update_stmt: Optional[TextClause] = query_config.generate_update_stmt(
|
|
188
217
|
row, policy, privacy_request
|
|
189
218
|
)
|
|
190
219
|
if update_stmt is not None:
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
220
|
+
if self.should_dry_run(SqlDryRunMode.erasure):
|
|
221
|
+
logger.warning(f"SQL DRY RUN - Would execute SQL: {update_stmt}")
|
|
222
|
+
else:
|
|
223
|
+
with client.connect() as connection:
|
|
224
|
+
self.set_schema(connection)
|
|
225
|
+
results: LegacyCursorResult = connection.execute(update_stmt)
|
|
226
|
+
update_ct = update_ct + results.rowcount
|
|
195
227
|
return update_ct
|
|
196
228
|
|
|
197
229
|
def close(self) -> None:
|
|
@@ -75,6 +75,7 @@ class WebsiteConnector(BaseConnector):
|
|
|
75
75
|
privacy_request: PrivacyRequest,
|
|
76
76
|
request_task: RequestTask,
|
|
77
77
|
rows: List[Row],
|
|
78
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
78
79
|
) -> int:
|
|
79
80
|
"""DSR execution not supported for website connector"""
|
|
80
81
|
return 0
|
|
@@ -14,6 +14,7 @@ from fides.api.common_exceptions import (
|
|
|
14
14
|
ClientUnsuccessfulException,
|
|
15
15
|
IdentityNotFoundException,
|
|
16
16
|
ManualWebhookFieldsUnset,
|
|
17
|
+
MaskingSecretsExpired,
|
|
17
18
|
MessageDispatchException,
|
|
18
19
|
NoCachedManualWebhookEntry,
|
|
19
20
|
PrivacyRequestExit,
|
|
@@ -75,6 +76,7 @@ from fides.api.task.graph_task import (
|
|
|
75
76
|
from fides.api.task.manual.manual_task_utils import create_manual_task_artificial_graphs
|
|
76
77
|
from fides.api.tasks import DatabaseTask, celery_app
|
|
77
78
|
from fides.api.tasks.scheduled.scheduler import scheduler
|
|
79
|
+
from fides.api.util.cache import get_all_masking_secret_keys
|
|
78
80
|
from fides.api.util.collection_util import Row
|
|
79
81
|
from fides.api.util.logger import Pii, _log_exception, _log_warning
|
|
80
82
|
from fides.api.util.logger_context_utils import LoggerContextKeys, log_context
|
|
@@ -531,6 +533,8 @@ def run_privacy_request(
|
|
|
531
533
|
request_checkpoint=CurrentStep.erasure, from_checkpoint=resume_step
|
|
532
534
|
):
|
|
533
535
|
privacy_request.cache_failed_checkpoint_details(CurrentStep.erasure)
|
|
536
|
+
_verify_masking_secrets(policy, privacy_request, resume_step)
|
|
537
|
+
|
|
534
538
|
# We only need to run the erasure once until masking strategies are handled
|
|
535
539
|
erasure_runner(
|
|
536
540
|
privacy_request=privacy_request,
|
|
@@ -1000,3 +1004,25 @@ def run_webhooks_and_report_status(
|
|
|
1000
1004
|
return False
|
|
1001
1005
|
|
|
1002
1006
|
return True
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
def _verify_masking_secrets(
|
|
1010
|
+
policy: Policy, privacy_request: PrivacyRequest, resume_step: Optional[CurrentStep]
|
|
1011
|
+
) -> None:
|
|
1012
|
+
"""
|
|
1013
|
+
Checks that the required masking secrets are still cached for the given request.
|
|
1014
|
+
Raises an exception if masking secrets are needed for the given policy but they don't exist.
|
|
1015
|
+
"""
|
|
1016
|
+
|
|
1017
|
+
if resume_step is None:
|
|
1018
|
+
return
|
|
1019
|
+
|
|
1020
|
+
# if masking can be performed without any masking secrets, we skip the cache check
|
|
1021
|
+
if (
|
|
1022
|
+
policy.generate_masking_secrets()
|
|
1023
|
+
and not get_all_masking_secret_keys(privacy_request.id)
|
|
1024
|
+
and not privacy_request.masking_secrets
|
|
1025
|
+
):
|
|
1026
|
+
raise MaskingSecretsExpired(
|
|
1027
|
+
f"The masking secrets for privacy request ID '{privacy_request.id}' have expired. Please submit a new erasure request."
|
|
1028
|
+
)
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import json
|
|
4
4
|
from asyncio import sleep
|
|
5
5
|
from datetime import datetime, timedelta
|
|
6
|
-
from typing import Any, Dict,
|
|
6
|
+
from typing import Any, Dict, Optional, Set
|
|
7
7
|
|
|
8
8
|
from httpx import AsyncClient
|
|
9
9
|
from loguru import logger
|
|
@@ -11,7 +11,6 @@ from sqlalchemy import text
|
|
|
11
11
|
from sqlalchemy.sql.elements import TextClause
|
|
12
12
|
|
|
13
13
|
from fides.api.common_exceptions import PrivacyRequestNotFound
|
|
14
|
-
from fides.api.models.policy import Policy
|
|
15
14
|
from fides.api.models.privacy_request import (
|
|
16
15
|
EXITED_EXECUTION_LOG_STATUSES,
|
|
17
16
|
PrivacyRequest,
|
|
@@ -19,14 +18,12 @@ from fides.api.models.privacy_request import (
|
|
|
19
18
|
)
|
|
20
19
|
from fides.api.models.worker_task import ExecutionLogStatus
|
|
21
20
|
from fides.api.schemas.drp_privacy_request import DrpPrivacyRequestCreate
|
|
22
|
-
from fides.api.schemas.masking.masking_secrets import MaskingSecretCache
|
|
23
21
|
from fides.api.schemas.policy import ActionType
|
|
24
22
|
from fides.api.schemas.privacy_request import (
|
|
25
23
|
PrivacyRequestResponse,
|
|
26
24
|
PrivacyRequestStatus,
|
|
27
25
|
)
|
|
28
26
|
from fides.api.schemas.redis_cache import Identity
|
|
29
|
-
from fides.api.service.masking.strategy.masking_strategy import MaskingStrategy
|
|
30
27
|
from fides.api.tasks import DSR_QUEUE_NAME, DatabaseTask, celery_app
|
|
31
28
|
from fides.api.tasks.scheduled.scheduler import scheduler
|
|
32
29
|
from fides.api.util.cache import (
|
|
@@ -69,7 +66,6 @@ def build_required_privacy_request_kwargs(
|
|
|
69
66
|
|
|
70
67
|
def cache_data(
|
|
71
68
|
privacy_request: PrivacyRequest,
|
|
72
|
-
policy: Policy,
|
|
73
69
|
identity: Identity,
|
|
74
70
|
encryption_key: Optional[str],
|
|
75
71
|
drp_request_body: Optional[DrpPrivacyRequestCreate],
|
|
@@ -82,23 +78,6 @@ def cache_data(
|
|
|
82
78
|
privacy_request.cache_custom_privacy_request_fields(custom_privacy_request_fields)
|
|
83
79
|
privacy_request.cache_encryption(encryption_key) # handles None already
|
|
84
80
|
|
|
85
|
-
# Store masking secrets in the cache
|
|
86
|
-
logger.info("Caching masking secrets for privacy request {}", privacy_request.id)
|
|
87
|
-
erasure_rules = policy.get_rules_for_action(action_type=ActionType.erasure)
|
|
88
|
-
unique_masking_strategies_by_name: Set[str] = set()
|
|
89
|
-
for rule in erasure_rules:
|
|
90
|
-
strategy_name: str = rule.masking_strategy["strategy"] # type: ignore
|
|
91
|
-
configuration = rule.masking_strategy["configuration"] # type: ignore
|
|
92
|
-
if strategy_name in unique_masking_strategies_by_name:
|
|
93
|
-
continue
|
|
94
|
-
unique_masking_strategies_by_name.add(strategy_name)
|
|
95
|
-
masking_strategy = MaskingStrategy.get_strategy(strategy_name, configuration)
|
|
96
|
-
if masking_strategy.secrets_required():
|
|
97
|
-
masking_secrets: List[MaskingSecretCache] = (
|
|
98
|
-
masking_strategy.generate_secrets_for_cache()
|
|
99
|
-
)
|
|
100
|
-
for masking_secret in masking_secrets:
|
|
101
|
-
privacy_request.cache_masking_secret(masking_secret)
|
|
102
81
|
if drp_request_body:
|
|
103
82
|
privacy_request.cache_drp_request_body(drp_request_body)
|
|
104
83
|
|
|
File without changes
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import operator as py_operator
|
|
2
|
+
from typing import Any, Union
|
|
3
|
+
|
|
4
|
+
from loguru import logger
|
|
5
|
+
from sqlalchemy.orm import Session
|
|
6
|
+
|
|
7
|
+
from fides.api.graph.config import FieldPath
|
|
8
|
+
from fides.api.task.conditional_dependencies.schemas import (
|
|
9
|
+
Condition,
|
|
10
|
+
ConditionGroup,
|
|
11
|
+
ConditionLeaf,
|
|
12
|
+
GroupOperator,
|
|
13
|
+
Operator,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
operator_methods = {
|
|
17
|
+
Operator.exists: lambda a, _: a is not None,
|
|
18
|
+
Operator.not_exists: lambda a, _: a is None,
|
|
19
|
+
Operator.eq: py_operator.eq,
|
|
20
|
+
Operator.neq: py_operator.ne,
|
|
21
|
+
Operator.lt: lambda a, b: a < b if a is not None else False,
|
|
22
|
+
Operator.lte: lambda a, b: a <= b if a is not None else False,
|
|
23
|
+
Operator.gt: lambda a, b: a > b if a is not None else False,
|
|
24
|
+
Operator.gte: lambda a, b: a >= b if a is not None else False,
|
|
25
|
+
Operator.list_contains: lambda a, b: b in a if isinstance(a, list) else False,
|
|
26
|
+
Operator.not_in_list: lambda a, b: a not in b if isinstance(b, list) else True,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ConditionEvaluator:
|
|
31
|
+
"""Evaluates nested conditions for manual task creation"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, db: Session):
|
|
34
|
+
self.db = db
|
|
35
|
+
|
|
36
|
+
def evaluate_rule(self, rule: Condition, data: Union[dict, Any]) -> bool:
|
|
37
|
+
"""Evaluate a nested condition rule against input data"""
|
|
38
|
+
if isinstance(rule, ConditionLeaf):
|
|
39
|
+
return self._evaluate_leaf_condition(rule, data)
|
|
40
|
+
# ConditionGroup
|
|
41
|
+
return self._evaluate_group_condition(rule, data)
|
|
42
|
+
|
|
43
|
+
def _evaluate_leaf_condition(
|
|
44
|
+
self, condition: ConditionLeaf, data: Union[dict, Any]
|
|
45
|
+
) -> bool:
|
|
46
|
+
"""Evaluate a leaf condition against input data"""
|
|
47
|
+
actual_value = self._get_nested_value(data, condition.field_address.split("."))
|
|
48
|
+
# Apply operator and return result
|
|
49
|
+
return self._apply_operator(actual_value, condition.operator, condition.value)
|
|
50
|
+
|
|
51
|
+
def _evaluate_group_condition(
|
|
52
|
+
self, group: ConditionGroup, data: Union[dict, Any]
|
|
53
|
+
) -> bool:
|
|
54
|
+
"""Evaluate a group condition against input data"""
|
|
55
|
+
results = [
|
|
56
|
+
self.evaluate_rule(condition, data) for condition in group.conditions
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
logical_operators = {GroupOperator.and_: all, GroupOperator.or_: any}
|
|
60
|
+
operator_func = logical_operators.get(group.logical_operator)
|
|
61
|
+
|
|
62
|
+
if operator_func is None:
|
|
63
|
+
logger.warning(f"Unknown logical operator: {group.logical_operator}")
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
return operator_func(results)
|
|
67
|
+
|
|
68
|
+
def _get_nested_value(self, data: Union[dict, Any], keys: list[str]) -> Any:
|
|
69
|
+
"""Get nested value from data using dot notation
|
|
70
|
+
|
|
71
|
+
Supports both simple dictionary access and Fides reference structures:
|
|
72
|
+
- Simple dict: data["user"]["name"]
|
|
73
|
+
- Fides FieldAddress: data.get_field_value(FieldAddress("dataset", "collection", "field_address"))
|
|
74
|
+
- Fides Collection: data.get_field_value(FieldPath("field_address", "subfield"))
|
|
75
|
+
"""
|
|
76
|
+
if not keys:
|
|
77
|
+
return data
|
|
78
|
+
|
|
79
|
+
current = data
|
|
80
|
+
|
|
81
|
+
# Try Fides reference structures first
|
|
82
|
+
if hasattr(current, "get_field_value"):
|
|
83
|
+
try:
|
|
84
|
+
field_path = FieldPath(*keys) if len(keys) > 1 else FieldPath(keys[0])
|
|
85
|
+
return current.get_field_value(field_path)
|
|
86
|
+
except (AttributeError, ValueError):
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
# Fall back to dictionary access
|
|
90
|
+
for key in keys:
|
|
91
|
+
if not isinstance(current, dict):
|
|
92
|
+
current = current.get(key, {}) if hasattr(current, "get") else None
|
|
93
|
+
else:
|
|
94
|
+
current = current.get(key, {})
|
|
95
|
+
|
|
96
|
+
return current if current != {} else None
|
|
97
|
+
|
|
98
|
+
def _apply_operator(
|
|
99
|
+
self, actual_value: Any, operator: Operator, expected_value: Any
|
|
100
|
+
) -> bool:
|
|
101
|
+
"""Apply operator to actual and expected values"""
|
|
102
|
+
|
|
103
|
+
# Get the method for the operator and execute it
|
|
104
|
+
operator_method = operator_methods.get(operator)
|
|
105
|
+
if operator_method is None:
|
|
106
|
+
logger.warning(f"Unknown operator: {operator}")
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
return operator_method(actual_value, expected_value)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import List, Optional, Union
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field, model_validator
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Operator(str, Enum):
|
|
8
|
+
eq = "eq"
|
|
9
|
+
neq = "neq"
|
|
10
|
+
lt = "lt"
|
|
11
|
+
lte = "lte"
|
|
12
|
+
gt = "gt"
|
|
13
|
+
gte = "gte"
|
|
14
|
+
exists = "exists"
|
|
15
|
+
not_exists = "not_exists"
|
|
16
|
+
list_contains = "list_contains"
|
|
17
|
+
not_in_list = "not_in_list"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class GroupOperator(str, Enum):
|
|
21
|
+
and_ = "and"
|
|
22
|
+
or_ = "or"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Leaf condition (e.g., "user.name exists", "user.created_at >= 2024-01-01")
|
|
26
|
+
class ConditionLeaf(BaseModel):
|
|
27
|
+
field_address: str = Field(
|
|
28
|
+
description="Field path to check (e.g., 'user.name', 'billing.subscription.status')"
|
|
29
|
+
)
|
|
30
|
+
operator: Operator = Field(description="Operator to apply")
|
|
31
|
+
value: Optional[
|
|
32
|
+
Union[str, int, float, bool, List[Union[str, int, float, bool]]]
|
|
33
|
+
] = Field(default=None, description="Expected value for comparison")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Nested logical condition (AND, OR)
|
|
37
|
+
class ConditionGroup(BaseModel):
|
|
38
|
+
logical_operator: GroupOperator = Field(
|
|
39
|
+
description="Logical operator: 'and' or 'or'"
|
|
40
|
+
)
|
|
41
|
+
conditions: list["Condition"] = Field(
|
|
42
|
+
description="List of conditions or nested groups"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
@model_validator(mode="after")
|
|
46
|
+
def validate_conditions(self) -> "ConditionGroup":
|
|
47
|
+
if not self.conditions:
|
|
48
|
+
raise ValueError("conditions list cannot be empty")
|
|
49
|
+
return self
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Use model_rebuild to allow recursive nesting
|
|
53
|
+
Condition = Union[ConditionLeaf, ConditionGroup]
|
|
54
|
+
ConditionGroup.model_rebuild()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# pylint: disable=too-many-lines
|
|
2
|
+
from functools import partial
|
|
2
3
|
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
|
|
3
4
|
|
|
4
5
|
import dask
|
|
@@ -252,16 +253,33 @@ def run_erasure_request_deprecated( # pylint: disable = too-many-arguments
|
|
|
252
253
|
|
|
253
254
|
access_request_data[ROOT_COLLECTION_ADDRESS.value] = [identity]
|
|
254
255
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
256
|
+
# Build the dask task graph for erasure, ensuring we also pass along the
|
|
257
|
+
# upstream access data that may be required by the connector (e.g. BigQuery
|
|
258
|
+
# delete statements). We accomplish this by partially applying the
|
|
259
|
+
# `inputs` kwarg on each task's `erasure_request` method. The resulting
|
|
260
|
+
# callable accepts the original positional arguments expected by Dask.
|
|
261
|
+
|
|
262
|
+
dsk: Dict[CollectionAddress, Any] = {}
|
|
263
|
+
for k, t in env.items():
|
|
264
|
+
# Collect upstream access data in the same order as the input keys
|
|
265
|
+
upstream_access_data: List[List[Row]] = [
|
|
266
|
+
access_request_data.get(str(input_key), [])
|
|
267
|
+
for input_key in t.execution_node.input_keys
|
|
268
|
+
]
|
|
269
|
+
|
|
270
|
+
# Bind the `inputs` keyword argument so it is supplied when the task executes
|
|
271
|
+
erasure_fn_with_inputs = partial(
|
|
272
|
+
t.erasure_request, inputs=upstream_access_data
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# Build the task tuple: (callable, retrieved_data, *prereqs)
|
|
276
|
+
dsk[k] = (
|
|
277
|
+
erasure_fn_with_inputs,
|
|
258
278
|
access_request_data.get(
|
|
259
279
|
str(k), []
|
|
260
|
-
), #
|
|
280
|
+
), # Data retrieved for this collection
|
|
261
281
|
*_evaluate_erasure_dependencies(t, erasure_end_nodes),
|
|
262
282
|
)
|
|
263
|
-
for k, t in env.items()
|
|
264
|
-
}
|
|
265
283
|
|
|
266
284
|
# root node returns 0 to be consistent with the output of the other erasure tasks
|
|
267
285
|
dsk[ROOT_COLLECTION_ADDRESS] = 0
|
|
@@ -298,17 +298,11 @@ def run_access_node(
|
|
|
298
298
|
)
|
|
299
299
|
# Currently, upstream tasks and "input keys" (which are built by data dependencies)
|
|
300
300
|
# are the same, but they may not be the same in the future.
|
|
301
|
-
|
|
302
|
-
|
|
301
|
+
upstream_access_data: List[List[Row]] = (
|
|
302
|
+
_build_upstream_access_data(
|
|
303
303
|
graph_task.execution_node.input_keys, upstream_results
|
|
304
304
|
)
|
|
305
305
|
)
|
|
306
|
-
# Pass in access data dependencies in the same order as the input keys.
|
|
307
|
-
# If we don't have access data for an upstream node, pass in an empty list
|
|
308
|
-
upstream_access_data: List[List[Row]] = [
|
|
309
|
-
upstream.get_access_data() if upstream else []
|
|
310
|
-
for upstream in ordered_upstream_tasks
|
|
311
|
-
]
|
|
312
306
|
# Run the main access function
|
|
313
307
|
graph_task.access_request(*upstream_access_data)
|
|
314
308
|
|
|
@@ -359,7 +353,7 @@ def run_erasure_node(
|
|
|
359
353
|
session,
|
|
360
354
|
) as resources:
|
|
361
355
|
# Build GraphTask resource to facilitate execution
|
|
362
|
-
|
|
356
|
+
erasure_graph_task: GraphTask = create_graph_task(
|
|
363
357
|
session, request_task, resources
|
|
364
358
|
)
|
|
365
359
|
# Get access data that was saved in the erasure format that was collected from the
|
|
@@ -368,8 +362,24 @@ def run_erasure_node(
|
|
|
368
362
|
request_task.get_data_for_erasures() or []
|
|
369
363
|
)
|
|
370
364
|
|
|
371
|
-
|
|
372
|
-
|
|
365
|
+
upstream_access_data: List[List[Row]] = []
|
|
366
|
+
|
|
367
|
+
try:
|
|
368
|
+
upstream_access_data = (
|
|
369
|
+
get_upstream_access_data_for_erasure_task(
|
|
370
|
+
request_task, session, resources
|
|
371
|
+
)
|
|
372
|
+
)
|
|
373
|
+
except Exception as e:
|
|
374
|
+
logger.error(
|
|
375
|
+
f"Unable to get upstream access data for erasure task {request_task.collection_address}: {e}"
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# Run the main erasure function, passing along the upstream access data.
|
|
379
|
+
# The extra data is currently only needed for generating BigQuery delete statements.
|
|
380
|
+
erasure_graph_task.erasure_request(
|
|
381
|
+
retrieved_data, inputs=upstream_access_data
|
|
382
|
+
)
|
|
373
383
|
|
|
374
384
|
queue_downstream_tasks_with_retries(
|
|
375
385
|
self,
|
|
@@ -487,6 +497,73 @@ def _order_tasks_by_input_key(
|
|
|
487
497
|
return tasks
|
|
488
498
|
|
|
489
499
|
|
|
500
|
+
def get_upstream_access_data_for_erasure_task(
|
|
501
|
+
erasure_request_task: RequestTask,
|
|
502
|
+
session: Session,
|
|
503
|
+
resources: TaskResources,
|
|
504
|
+
) -> List[List[Row]]:
|
|
505
|
+
"""
|
|
506
|
+
Retrieves upstream access data for a given erasure request task.
|
|
507
|
+
|
|
508
|
+
This function finds the corresponding access task for the erasure task,
|
|
509
|
+
creates a GraphTask to extract input keys, and builds the upstream access data
|
|
510
|
+
needed for erasure operations (particularly for BigQuery delete statements).
|
|
511
|
+
|
|
512
|
+
Args:
|
|
513
|
+
erasure_request_task: The erasure task that needs upstream access data
|
|
514
|
+
session: Database session for querying
|
|
515
|
+
resources: Task resources for creating GraphTask
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
List[List[Row]]: Upstream access data ordered by input keys
|
|
519
|
+
|
|
520
|
+
Raises:
|
|
521
|
+
Exception: If the corresponding access task cannot be found
|
|
522
|
+
"""
|
|
523
|
+
# Get the corresponding access task for the current erasure task
|
|
524
|
+
access_request_task = (
|
|
525
|
+
session.query(RequestTask)
|
|
526
|
+
.filter(
|
|
527
|
+
RequestTask.privacy_request_id == erasure_request_task.privacy_request_id,
|
|
528
|
+
RequestTask.collection_address == erasure_request_task.collection_address,
|
|
529
|
+
RequestTask.action_type == ActionType.access,
|
|
530
|
+
)
|
|
531
|
+
.first()
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
if not access_request_task:
|
|
535
|
+
raise Exception(
|
|
536
|
+
f"Unable to find access request task for erasure task {erasure_request_task.collection_address}"
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
# Convert the request task to a GraphTask to get the input_keys
|
|
540
|
+
access_graph_task: GraphTask = create_graph_task(
|
|
541
|
+
session, access_request_task, resources
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
# Build and return the upstream access data
|
|
545
|
+
return _build_upstream_access_data(
|
|
546
|
+
access_graph_task.execution_node.input_keys,
|
|
547
|
+
access_request_task.upstream_tasks_objects(session),
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _build_upstream_access_data(
|
|
552
|
+
input_keys: List[CollectionAddress],
|
|
553
|
+
upstream_tasks: Query,
|
|
554
|
+
) -> List[List[Row]]:
|
|
555
|
+
"""
|
|
556
|
+
Helper function to build the access data for the current node.
|
|
557
|
+
The access data is passed in the same order as the input keys.
|
|
558
|
+
If we don't have access data for an upstream node, return an empty list.
|
|
559
|
+
"""
|
|
560
|
+
|
|
561
|
+
ordered_upstream: List[Optional[RequestTask]] = _order_tasks_by_input_key(
|
|
562
|
+
input_keys, upstream_tasks
|
|
563
|
+
)
|
|
564
|
+
return [task.get_access_data() if task else [] for task in ordered_upstream]
|
|
565
|
+
|
|
566
|
+
|
|
490
567
|
mapping = {
|
|
491
568
|
ActionType.access.value: run_access_node,
|
|
492
569
|
ActionType.erasure.value: run_erasure_node,
|