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
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Union
|
|
4
|
+
from urllib.parse import unquote_to_bytes
|
|
5
|
+
|
|
6
|
+
from sqlalchemy import Column, ForeignKey, String
|
|
7
|
+
from sqlalchemy.ext.declarative import declared_attr
|
|
8
|
+
from sqlalchemy.orm import Session, relationship
|
|
9
|
+
from sqlalchemy_utils.types.encrypted.encrypted_type import (
|
|
10
|
+
AesGcmEngine,
|
|
11
|
+
StringEncryptedType,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from fides.api.db.base_class import Base, JSONTypeOverride
|
|
15
|
+
from fides.api.util.custom_json_encoder import ENCODED_BYTES_PREFIX
|
|
16
|
+
from fides.config import CONFIG
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from fides.api.models.privacy_request import PrivacyRequest
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MaskingSecret(Base):
|
|
23
|
+
"""SQLAlchemy model for storing secret caching information"""
|
|
24
|
+
|
|
25
|
+
@declared_attr
|
|
26
|
+
def __tablename__(self) -> str:
|
|
27
|
+
return "masking_secret"
|
|
28
|
+
|
|
29
|
+
privacy_request_id = Column(
|
|
30
|
+
String, ForeignKey("privacyrequest.id", ondelete="CASCADE"), nullable=False
|
|
31
|
+
)
|
|
32
|
+
secret = Column(
|
|
33
|
+
StringEncryptedType(
|
|
34
|
+
JSONTypeOverride,
|
|
35
|
+
CONFIG.security.app_encryption_key,
|
|
36
|
+
AesGcmEngine,
|
|
37
|
+
"pkcs5",
|
|
38
|
+
),
|
|
39
|
+
nullable=False,
|
|
40
|
+
)
|
|
41
|
+
masking_strategy = Column(String, nullable=False)
|
|
42
|
+
secret_type = Column(String, nullable=False)
|
|
43
|
+
privacy_request = relationship("PrivacyRequest", back_populates="masking_secrets")
|
|
44
|
+
|
|
45
|
+
def set_secret(self, secret: Union[str, bytes]) -> None:
|
|
46
|
+
"""Set the secret value, handling both string and bytes types"""
|
|
47
|
+
if isinstance(secret, str):
|
|
48
|
+
self.secret = secret.encode("utf-8")
|
|
49
|
+
elif isinstance(secret, bytes):
|
|
50
|
+
self.secret = secret
|
|
51
|
+
else:
|
|
52
|
+
raise ValueError("Secret must be either string or bytes")
|
|
53
|
+
|
|
54
|
+
def get_secret(self) -> Union[str, bytes]:
|
|
55
|
+
"""Retrieve the secret in its original type"""
|
|
56
|
+
secret = self.secret
|
|
57
|
+
if isinstance(secret, str) and secret.startswith(ENCODED_BYTES_PREFIX):
|
|
58
|
+
return unquote_to_bytes(secret)[len(ENCODED_BYTES_PREFIX) :]
|
|
59
|
+
return secret
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def create(
|
|
63
|
+
cls,
|
|
64
|
+
db: Session,
|
|
65
|
+
*,
|
|
66
|
+
data: dict[str, Any],
|
|
67
|
+
check_name: bool = True,
|
|
68
|
+
) -> "MaskingSecret":
|
|
69
|
+
"""
|
|
70
|
+
Create a new masking secret. Handles both string and bytes secrets automatically, encrypting them for storage.
|
|
71
|
+
"""
|
|
72
|
+
return super().create(db=db, data=data, check_name=check_name)
|
fides/api/models/policy.py
CHANGED
|
@@ -25,7 +25,9 @@ from fides.api.models.client import ClientDetail
|
|
|
25
25
|
from fides.api.models.connectionconfig import ConnectionConfig
|
|
26
26
|
from fides.api.models.sql_models import DataCategory # type: ignore
|
|
27
27
|
from fides.api.models.storage import StorageConfig, get_active_default_storage_config
|
|
28
|
+
from fides.api.schemas.masking.masking_secrets import MaskingSecretCache
|
|
28
29
|
from fides.api.schemas.policy import ActionType, DrpAction
|
|
30
|
+
from fides.api.service.masking.strategy.masking_strategy import MaskingStrategy
|
|
29
31
|
from fides.api.util.data_category import _validate_data_category
|
|
30
32
|
from fides.config import CONFIG
|
|
31
33
|
|
|
@@ -140,6 +142,27 @@ class Policy(Base):
|
|
|
140
142
|
except IndexError:
|
|
141
143
|
return None
|
|
142
144
|
|
|
145
|
+
def generate_masking_secrets(self) -> Optional[List[MaskingSecretCache]]:
|
|
146
|
+
"""
|
|
147
|
+
Returns a list of masking secrets for the masking strategies in the policy.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
masking_secrets: List[MaskingSecretCache] = []
|
|
151
|
+
erasure_rules = self.get_rules_for_action(action_type=ActionType.erasure)
|
|
152
|
+
unique_masking_strategies_by_name: Set[str] = set()
|
|
153
|
+
for rule in erasure_rules:
|
|
154
|
+
strategy_name: str = rule.masking_strategy["strategy"] # type: ignore
|
|
155
|
+
configuration = rule.masking_strategy["configuration"] # type: ignore
|
|
156
|
+
if strategy_name in unique_masking_strategies_by_name:
|
|
157
|
+
continue
|
|
158
|
+
unique_masking_strategies_by_name.add(strategy_name)
|
|
159
|
+
masking_strategy = MaskingStrategy.get_strategy(
|
|
160
|
+
strategy_name, configuration
|
|
161
|
+
)
|
|
162
|
+
if masking_strategy.secrets_required():
|
|
163
|
+
masking_secrets.extend(masking_strategy.generate_secrets_for_cache())
|
|
164
|
+
return masking_secrets
|
|
165
|
+
|
|
143
166
|
def applies_to(self, node: "TraversalNode") -> bool:
|
|
144
167
|
"""
|
|
145
168
|
Returns True if any data category in the traversal node starts with any of the policy's target categories.
|
|
@@ -50,6 +50,7 @@ from fides.api.models.comment import Comment, CommentReference, CommentReference
|
|
|
50
50
|
from fides.api.models.fides_user import FidesUser
|
|
51
51
|
from fides.api.models.field_types import EncryptedLargeDataDescriptor
|
|
52
52
|
from fides.api.models.manual_webhook import AccessManualWebhook
|
|
53
|
+
from fides.api.models.masking_secret import MaskingSecret
|
|
53
54
|
from fides.api.models.policy import (
|
|
54
55
|
Policy,
|
|
55
56
|
PolicyPreWebhook,
|
|
@@ -98,7 +99,6 @@ from fides.api.util.cache import (
|
|
|
98
99
|
get_drp_request_body_cache_key,
|
|
99
100
|
get_encryption_cache_key,
|
|
100
101
|
get_identity_cache_key,
|
|
101
|
-
get_masking_secret_cache_key,
|
|
102
102
|
)
|
|
103
103
|
from fides.api.util.collection_util import Row, extract_key_for_address
|
|
104
104
|
from fides.api.util.constants import API_DATE_FORMAT
|
|
@@ -289,6 +289,13 @@ class PrivacyRequest(
|
|
|
289
289
|
order_by="RequestTask.created_at",
|
|
290
290
|
)
|
|
291
291
|
|
|
292
|
+
masking_secrets: "RelationshipProperty[List[MaskingSecret]]" = relationship(
|
|
293
|
+
"MaskingSecret",
|
|
294
|
+
back_populates="privacy_request",
|
|
295
|
+
uselist=True,
|
|
296
|
+
passive_deletes="all",
|
|
297
|
+
)
|
|
298
|
+
|
|
292
299
|
@property
|
|
293
300
|
def days_left(self: PrivacyRequest) -> Union[int, None]:
|
|
294
301
|
if self.due_date is None:
|
|
@@ -566,19 +573,24 @@ class PrivacyRequest(
|
|
|
566
573
|
encryption_key,
|
|
567
574
|
)
|
|
568
575
|
|
|
569
|
-
def
|
|
570
|
-
|
|
571
|
-
|
|
576
|
+
def persist_masking_secrets(
|
|
577
|
+
self, masking_secrets: List[MaskingSecretCache]
|
|
578
|
+
) -> None:
|
|
579
|
+
"""Persists masking encryption secrets to database."""
|
|
580
|
+
if not masking_secrets:
|
|
572
581
|
return
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
+
|
|
583
|
+
session = Session.object_session(self)
|
|
584
|
+
for masking_secret in masking_secrets:
|
|
585
|
+
MaskingSecret.create(
|
|
586
|
+
db=session,
|
|
587
|
+
data={
|
|
588
|
+
"privacy_request_id": self.id,
|
|
589
|
+
"secret": masking_secret.secret,
|
|
590
|
+
"masking_strategy": masking_secret.masking_strategy,
|
|
591
|
+
"secret_type": masking_secret.secret_type,
|
|
592
|
+
},
|
|
593
|
+
)
|
|
582
594
|
|
|
583
595
|
def get_cached_identity_data(self) -> Dict[str, Any]:
|
|
584
596
|
"""Retrieves any identity data pertaining to this request from the cache"""
|
fides/api/oauth/roles.py
CHANGED
|
@@ -47,6 +47,7 @@ from fides.common.api.scope_registry import (
|
|
|
47
47
|
SYSTEM_READ,
|
|
48
48
|
USER_PERMISSION_ASSIGN_OWNERS,
|
|
49
49
|
USER_READ,
|
|
50
|
+
USER_READ_OWN,
|
|
50
51
|
WEBHOOK_READ,
|
|
51
52
|
)
|
|
52
53
|
|
|
@@ -126,6 +127,7 @@ viewer_scopes = [ # Intentionally omitted USER_PERMISSION_READ and PRIVACY_REQU
|
|
|
126
127
|
|
|
127
128
|
respondent_scopes = [
|
|
128
129
|
PRIVACY_REQUEST_MANUAL_STEPS_RESPOND, # allows respondents to respond to assigned manual steps
|
|
130
|
+
USER_READ_OWN,
|
|
129
131
|
]
|
|
130
132
|
|
|
131
133
|
external_respondent_scopes = [
|
|
@@ -11,6 +11,14 @@ from fides.api.schemas.messaging.messaging import MessagingServiceType
|
|
|
11
11
|
from fides.config.admin_ui_settings import ErrorNotificationMode
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
class SqlDryRunMode(str, Enum):
|
|
15
|
+
"""SQL dry run mode for controlling execution of SQL statements in privacy requests"""
|
|
16
|
+
|
|
17
|
+
none = "none"
|
|
18
|
+
access = "access"
|
|
19
|
+
erasure = "erasure"
|
|
20
|
+
|
|
21
|
+
|
|
14
22
|
class StorageTypeApiAccepted(Enum):
|
|
15
23
|
"""Enum for storage destination types accepted in API updates"""
|
|
16
24
|
|
|
@@ -62,7 +70,9 @@ class ExecutionApplicationConfig(FidesSchema):
|
|
|
62
70
|
subject_identity_verification_required: Optional[bool] = None
|
|
63
71
|
disable_consent_identity_verification: Optional[bool] = None
|
|
64
72
|
require_manual_request_approval: Optional[bool] = None
|
|
65
|
-
|
|
73
|
+
sql_dry_run: Optional[SqlDryRunMode] = None
|
|
74
|
+
|
|
75
|
+
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
66
76
|
|
|
67
77
|
|
|
68
78
|
class AdminUIConfig(FidesSchema):
|
|
@@ -82,6 +82,7 @@ class BaseConnector(Generic[DB_CONNECTOR_TYPE], ABC):
|
|
|
82
82
|
privacy_request: PrivacyRequest,
|
|
83
83
|
request_task: RequestTask,
|
|
84
84
|
rows: List[Row],
|
|
85
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
85
86
|
) -> int:
|
|
86
87
|
"""Execute a masking request. Return the number of rows that have been updated
|
|
87
88
|
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
from typing import List, Optional
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
2
|
|
|
3
3
|
from loguru import logger
|
|
4
4
|
from sqlalchemy import text
|
|
5
|
-
from sqlalchemy.engine import
|
|
6
|
-
Connection,
|
|
7
|
-
Engine,
|
|
8
|
-
LegacyCursorResult,
|
|
9
|
-
create_engine,
|
|
10
|
-
)
|
|
5
|
+
from sqlalchemy.engine import Connection, Engine, create_engine # type: ignore
|
|
11
6
|
from sqlalchemy.orm import Session
|
|
12
7
|
from sqlalchemy.sql import Executable # type: ignore
|
|
13
8
|
from sqlalchemy.sql.elements import TextClause
|
|
@@ -17,6 +12,7 @@ from fides.api.graph.execution import ExecutionNode
|
|
|
17
12
|
from fides.api.models.connectionconfig import ConnectionTestStatus
|
|
18
13
|
from fides.api.models.policy import Policy
|
|
19
14
|
from fides.api.models.privacy_request import PrivacyRequest, RequestTask
|
|
15
|
+
from fides.api.schemas.application_config import SqlDryRunMode
|
|
20
16
|
from fides.api.schemas.connection_configuration.connection_secrets_bigquery import (
|
|
21
17
|
BigQuerySchema,
|
|
22
18
|
)
|
|
@@ -99,6 +95,16 @@ class BigQueryConnector(SQLConnector):
|
|
|
99
95
|
logger.info(
|
|
100
96
|
f"Executing {len(partition_clauses)} partition queries for node '{query_config.node.address}' in DSR execution"
|
|
101
97
|
)
|
|
98
|
+
|
|
99
|
+
if self.should_dry_run(SqlDryRunMode.access):
|
|
100
|
+
for partition_clause in partition_clauses:
|
|
101
|
+
existing_bind_params = stmt.compile().params
|
|
102
|
+
partitioned_stmt = text(
|
|
103
|
+
f"{stmt} AND ({text(partition_clause)})"
|
|
104
|
+
).params(existing_bind_params)
|
|
105
|
+
logger.warning(f"SQL DRY RUN - Would execute SQL: {partitioned_stmt}")
|
|
106
|
+
return []
|
|
107
|
+
|
|
102
108
|
rows = []
|
|
103
109
|
for partition_clause in partition_clauses:
|
|
104
110
|
logger.debug(
|
|
@@ -135,6 +141,39 @@ class BigQueryConnector(SQLConnector):
|
|
|
135
141
|
logger.exception(f"Error testing connection to remote BigQuery {str(e)}")
|
|
136
142
|
raise ConnectionException(f"Connection error: {e}")
|
|
137
143
|
|
|
144
|
+
def _execute_statements_with_sql_dry_run(
|
|
145
|
+
self,
|
|
146
|
+
statements: List[Executable],
|
|
147
|
+
sql_dry_run_enabled: bool,
|
|
148
|
+
client: Engine,
|
|
149
|
+
) -> int:
|
|
150
|
+
"""
|
|
151
|
+
Execute SQL statements with sql_dry_run support.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
statements: List of SQL statements to execute
|
|
155
|
+
sql_dry_run_enabled: Whether sql_dry_run mode is enabled
|
|
156
|
+
client: Database engine
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
int: Number of affected rows (0 in sql_dry_run mode)
|
|
160
|
+
"""
|
|
161
|
+
if not statements:
|
|
162
|
+
return 0
|
|
163
|
+
|
|
164
|
+
if sql_dry_run_enabled:
|
|
165
|
+
for stmt in statements:
|
|
166
|
+
logger.warning(f"SQL DRY RUN - Would execute SQL: {stmt}")
|
|
167
|
+
return 0
|
|
168
|
+
|
|
169
|
+
row_count = 0
|
|
170
|
+
with client.connect() as connection:
|
|
171
|
+
for stmt in statements:
|
|
172
|
+
results = connection.execute(stmt)
|
|
173
|
+
logger.debug(f"Affected {results.rowcount} rows")
|
|
174
|
+
row_count += results.rowcount
|
|
175
|
+
return row_count
|
|
176
|
+
|
|
138
177
|
def mask_data(
|
|
139
178
|
self,
|
|
140
179
|
node: ExecutionNode,
|
|
@@ -142,22 +181,31 @@ class BigQueryConnector(SQLConnector):
|
|
|
142
181
|
privacy_request: PrivacyRequest,
|
|
143
182
|
request_task: RequestTask,
|
|
144
183
|
rows: List[Row],
|
|
184
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
145
185
|
) -> int:
|
|
146
186
|
"""Execute a masking request. Returns the number of records updated or deleted"""
|
|
187
|
+
|
|
147
188
|
query_config = self.query_config(node)
|
|
148
189
|
update_or_delete_ct = 0
|
|
149
190
|
client = self.client()
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
191
|
+
|
|
192
|
+
if query_config.uses_delete_masking_strategy():
|
|
193
|
+
delete_stmts = query_config.generate_delete(client, input_data or {})
|
|
194
|
+
logger.debug(f"Generated {len(delete_stmts)} DELETE statements")
|
|
195
|
+
update_or_delete_ct += self._execute_statements_with_sql_dry_run(
|
|
196
|
+
delete_stmts, self.should_dry_run(SqlDryRunMode.erasure), client
|
|
155
197
|
)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
198
|
+
else:
|
|
199
|
+
for row in rows:
|
|
200
|
+
update_or_delete_stmts: List[Executable] = query_config.generate_update(
|
|
201
|
+
row, policy, privacy_request, client
|
|
202
|
+
)
|
|
203
|
+
logger.debug(
|
|
204
|
+
f"Generated {len(update_or_delete_stmts)} UPDATE statements"
|
|
205
|
+
)
|
|
206
|
+
update_or_delete_ct += self._execute_statements_with_sql_dry_run(
|
|
207
|
+
update_or_delete_stmts,
|
|
208
|
+
self.should_dry_run(SqlDryRunMode.erasure),
|
|
209
|
+
client,
|
|
210
|
+
)
|
|
163
211
|
return update_or_delete_ct
|
|
@@ -140,8 +140,9 @@ class DynamoDBConnector(BaseConnector[Any]): # type: ignore
|
|
|
140
140
|
privacy_request: PrivacyRequest,
|
|
141
141
|
request_task: RequestTask,
|
|
142
142
|
rows: List[Row],
|
|
143
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
143
144
|
) -> int:
|
|
144
|
-
"""Execute a masking
|
|
145
|
+
"""Execute a masking request for DynamoDB"""
|
|
145
146
|
|
|
146
147
|
query_config = self.query_config(node)
|
|
147
148
|
collection_name = node.address.collection
|
|
@@ -142,6 +142,7 @@ class FidesConnector(BaseConnector[FidesClient]):
|
|
|
142
142
|
privacy_request: PrivacyRequest,
|
|
143
143
|
request_task: RequestTask,
|
|
144
144
|
rows: List[Row],
|
|
145
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
145
146
|
) -> int:
|
|
146
147
|
"""Execute an erasure request on remote fides"""
|
|
147
148
|
identity_data = {
|
|
@@ -90,6 +90,7 @@ class HTTPSConnector(BaseConnector[None]):
|
|
|
90
90
|
privacy_request: PrivacyRequest,
|
|
91
91
|
request_task: RequestTask,
|
|
92
92
|
rows: List[Row],
|
|
93
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
93
94
|
) -> int:
|
|
94
95
|
"""Currently not supported as webhooks are not called at the collection level"""
|
|
95
96
|
raise NotImplementedError(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Dict, List
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
2
|
|
|
3
3
|
from fides.api.graph.execution import ExecutionNode
|
|
4
4
|
from fides.api.models.connectionconfig import ConnectionConfig, ConnectionTestStatus
|
|
@@ -53,6 +53,7 @@ class ManualWebhookConnector(BaseConnector[None]):
|
|
|
53
53
|
privacy_request: PrivacyRequest,
|
|
54
54
|
request_task: RequestTask,
|
|
55
55
|
rows: List[Row],
|
|
56
|
+
input_data: Optional[List[List[Row]]] = None,
|
|
56
57
|
) -> None:
|
|
57
58
|
"""
|
|
58
59
|
Not applicable for a manual webhook. Manual webhooks are not called as part of the traversal.
|
|
@@ -126,6 +126,7 @@ class MongoDBConnector(BaseConnector[MongoClient]):
|
|
|
126
126
|
privacy_request: PrivacyRequest,
|
|
127
127
|
request_task: RequestTask,
|
|
128
128
|
rows: List[Row],
|
|
129
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
129
130
|
) -> int:
|
|
130
131
|
"""Execute a masking request"""
|
|
131
132
|
query_config = self.query_config(node)
|
|
@@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional, Union, cast
|
|
|
3
3
|
import pydash
|
|
4
4
|
from fideslang.models import MaskingStrategies
|
|
5
5
|
from loguru import logger
|
|
6
|
-
from sqlalchemy import MetaData, Table, text
|
|
6
|
+
from sqlalchemy import MetaData, Table, or_, text
|
|
7
7
|
from sqlalchemy.engine import Engine
|
|
8
8
|
from sqlalchemy.sql import Delete, Update
|
|
9
9
|
from sqlalchemy.sql.elements import ColumnElement, TextClause
|
|
@@ -125,6 +125,7 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
|
|
|
125
125
|
policy: Policy,
|
|
126
126
|
request: PrivacyRequest,
|
|
127
127
|
client: Engine,
|
|
128
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
128
129
|
) -> Union[List[Update], List[Delete]]:
|
|
129
130
|
"""
|
|
130
131
|
Generate a masking statement for BigQuery.
|
|
@@ -137,7 +138,7 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
|
|
|
137
138
|
logger.info(
|
|
138
139
|
f"Masking override detected for collection {node.address.value}: {masking_override.strategy.value}"
|
|
139
140
|
)
|
|
140
|
-
return self.generate_delete(
|
|
141
|
+
return self.generate_delete(client, input_data or {})
|
|
141
142
|
return self.generate_update(row, policy, request, client)
|
|
142
143
|
|
|
143
144
|
def generate_update(
|
|
@@ -198,9 +199,16 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
|
|
|
198
199
|
|
|
199
200
|
table = Table(self._generate_table_name(), MetaData(bind=client), autoload=True)
|
|
200
201
|
where_clauses: List[ColumnElement] = [
|
|
201
|
-
|
|
202
|
+
table.c[k] == v for k, v in non_empty_reference_field_keys.items()
|
|
202
203
|
]
|
|
203
204
|
|
|
205
|
+
# Create update values using Column objects as keys to handle column names with spaces
|
|
206
|
+
update_values = {}
|
|
207
|
+
for column_name, value in final_update_map.items():
|
|
208
|
+
# Use bracket notation to access columns with spaces in their names
|
|
209
|
+
column = table.c[column_name]
|
|
210
|
+
update_values[column] = value
|
|
211
|
+
|
|
204
212
|
if self.partitioning:
|
|
205
213
|
partition_clauses = self.get_partition_clauses()
|
|
206
214
|
partitioned_queries = []
|
|
@@ -211,34 +219,37 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
|
|
|
211
219
|
partitioned_queries.append(
|
|
212
220
|
table.update()
|
|
213
221
|
.where(*(where_clauses + [text(partition_clause)]))
|
|
214
|
-
.values(
|
|
222
|
+
.values(update_values)
|
|
215
223
|
)
|
|
216
224
|
|
|
217
225
|
return partitioned_queries
|
|
218
226
|
|
|
219
|
-
return [table.update().where(*where_clauses).values(
|
|
227
|
+
return [table.update().where(*where_clauses).values(update_values)]
|
|
220
228
|
|
|
221
|
-
def generate_delete(
|
|
222
|
-
|
|
229
|
+
def generate_delete(
|
|
230
|
+
self,
|
|
231
|
+
client: Engine,
|
|
232
|
+
input_data: Optional[Dict[str, List[Any]]] = None,
|
|
233
|
+
) -> List[Delete]:
|
|
234
|
+
"""
|
|
235
|
+
Returns a List of SQLAlchemy DELETE statements for BigQuery. Does not actually execute the delete statement.
|
|
223
236
|
|
|
224
237
|
Used when a collection-level masking override is present and the masking strategy is DELETE.
|
|
225
238
|
|
|
226
239
|
A List of multiple DELETE statements are returned for partitioned tables; for a non-partitioned table,
|
|
227
240
|
a single DELETE statement is returned in a List for consistent typing.
|
|
228
|
-
|
|
229
|
-
TODO: DRY up this method and `generate_update` a bit
|
|
230
241
|
"""
|
|
231
242
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
)
|
|
243
|
+
if not input_data:
|
|
244
|
+
logger.warning(
|
|
245
|
+
"No input data provided for node '{}', skipping DELETE statement generation",
|
|
246
|
+
self.node.address,
|
|
247
|
+
)
|
|
248
|
+
return []
|
|
239
249
|
|
|
240
|
-
|
|
241
|
-
|
|
250
|
+
filtered_data = self.node.typed_filtered_values(input_data)
|
|
251
|
+
|
|
252
|
+
if not filtered_data:
|
|
242
253
|
logger.warning(
|
|
243
254
|
"There is not enough data to generate a valid DELETE statement for {}",
|
|
244
255
|
self.node.address,
|
|
@@ -246,9 +257,17 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
|
|
|
246
257
|
return []
|
|
247
258
|
|
|
248
259
|
table = Table(self._generate_table_name(), MetaData(bind=client), autoload=True)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
]
|
|
260
|
+
|
|
261
|
+
# Build individual reference clauses
|
|
262
|
+
where_clauses: List[ColumnElement] = []
|
|
263
|
+
for column_name, values in filtered_data.items():
|
|
264
|
+
if len(values) == 1:
|
|
265
|
+
where_clauses.append(table.c[column_name] == values[0])
|
|
266
|
+
else:
|
|
267
|
+
where_clauses.append(table.c[column_name].in_(values))
|
|
268
|
+
|
|
269
|
+
# Combine reference clauses with OR instead of AND
|
|
270
|
+
combined_reference_clause = or_(*where_clauses)
|
|
252
271
|
|
|
253
272
|
if self.partitioning:
|
|
254
273
|
partition_clauses = self.get_partition_clauses()
|
|
@@ -259,12 +278,25 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
|
|
|
259
278
|
|
|
260
279
|
for partition_clause in partition_clauses:
|
|
261
280
|
partitioned_queries.append(
|
|
262
|
-
table.delete()
|
|
281
|
+
table.delete()
|
|
282
|
+
.where(combined_reference_clause)
|
|
283
|
+
.where(text(partition_clause))
|
|
263
284
|
)
|
|
264
285
|
|
|
265
286
|
return partitioned_queries
|
|
266
287
|
|
|
267
|
-
return [table.delete().where(
|
|
288
|
+
return [table.delete().where(combined_reference_clause)]
|
|
289
|
+
|
|
290
|
+
def uses_delete_masking_strategy(self) -> bool:
|
|
291
|
+
"""Check if this collection uses DELETE masking strategy.
|
|
292
|
+
|
|
293
|
+
Returns True if masking override is present and strategy is DELETE.
|
|
294
|
+
"""
|
|
295
|
+
masking_override = self.node.collection.masking_strategy_override
|
|
296
|
+
return (
|
|
297
|
+
masking_override is not None
|
|
298
|
+
and masking_override.strategy == MaskingStrategies.DELETE
|
|
299
|
+
)
|
|
268
300
|
|
|
269
301
|
def format_fields_for_query(
|
|
270
302
|
self,
|
|
@@ -281,6 +313,25 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
|
|
|
281
313
|
formatted_fields.append(field_path.levels[0])
|
|
282
314
|
return formatted_fields
|
|
283
315
|
|
|
316
|
+
def format_clause_for_query(
|
|
317
|
+
self, string_path: str, operator: str, operand: str
|
|
318
|
+
) -> str:
|
|
319
|
+
"""
|
|
320
|
+
Returns clauses with proper BigQuery backtick escaping for column names.
|
|
321
|
+
Handles column names with spaces and nested fields (dot-separated) by escaping each part individually.
|
|
322
|
+
"""
|
|
323
|
+
# For nested fields (containing dots), escape each part individually
|
|
324
|
+
if "." in string_path:
|
|
325
|
+
parts = string_path.split(".")
|
|
326
|
+
escaped_field = ".".join(f"`{part}`" for part in parts)
|
|
327
|
+
else:
|
|
328
|
+
# For simple fields, wrap the entire name in backticks
|
|
329
|
+
escaped_field = f"`{string_path}`"
|
|
330
|
+
|
|
331
|
+
if operator == "IN":
|
|
332
|
+
return f"{escaped_field} IN ({operand})"
|
|
333
|
+
return f"{escaped_field} {operator} :{operand}"
|
|
334
|
+
|
|
284
335
|
def generate_raw_query_without_tuples(
|
|
285
336
|
self, field_list: List[str], filters: Dict[str, List[Any]]
|
|
286
337
|
) -> Optional[TextClause]:
|
|
@@ -290,27 +341,35 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
|
|
|
290
341
|
|
|
291
342
|
This is an override of the base class method that supports nested fields for BigQuery.
|
|
292
343
|
|
|
293
|
-
Examples with
|
|
344
|
+
Examples with field names containing dots and spaces, notice these are replaced with underscores in the parameter bindings:
|
|
294
345
|
|
|
295
346
|
1. Single value filter:
|
|
296
347
|
field_list = ["id", "name", "email"]
|
|
297
348
|
filters = {"user.id": [123]}
|
|
298
349
|
|
|
299
|
-
Generates: SELECT id, name, email FROM `project_id.dataset_id.table_name` WHERE (user
|
|
350
|
+
Generates: SELECT id, name, email FROM `project_id.dataset_id.table_name` WHERE (`user`.`id` = :user_id)
|
|
300
351
|
With parameter binding: user_id = 123
|
|
301
352
|
|
|
302
|
-
2.
|
|
353
|
+
2. Field with spaces:
|
|
354
|
+
field_list = ["id", "custom id", "email"]
|
|
355
|
+
filters = {"custom id": ["abc123"]}
|
|
356
|
+
|
|
357
|
+
Generates: SELECT id, `custom id`, email FROM `project_id.dataset_id.table_name` WHERE (`custom id` = :custom_id)
|
|
358
|
+
With parameter binding: custom_id = "abc123"
|
|
359
|
+
|
|
360
|
+
3. Multiple value filter with nested field:
|
|
303
361
|
field_list = ["id", "name", "email"]
|
|
304
|
-
filters = {"
|
|
362
|
+
filters = {"contact_info.primary_email": ["active", "pending"]}
|
|
305
363
|
|
|
306
|
-
Generates: SELECT id, name, email FROM `project_id.dataset_id.table_name` WHERE (
|
|
307
|
-
With parameter bindings:
|
|
364
|
+
Generates: SELECT id, name, email FROM `project_id.dataset_id.table_name` WHERE (`contact_info`.`primary_email` IN (:contact_info_primary_email_in_stmt_generated_0, :contact_info_primary_email_in_stmt_generated_1))
|
|
365
|
+
With parameter bindings: contact_info_primary_email_in_stmt_generated_0 = "active", contact_info_primary_email_in_stmt_generated_1 = "pending"
|
|
308
366
|
"""
|
|
309
367
|
clauses = []
|
|
310
368
|
query_data = {}
|
|
311
369
|
for field_name, field_value in filters.items():
|
|
312
|
-
# Replace dots with underscores in field names for parameter binding
|
|
313
|
-
|
|
370
|
+
# Replace dots and spaces with underscores in field names for parameter binding
|
|
371
|
+
# SQLAlchemy parameter names cannot contain spaces or special characters
|
|
372
|
+
field_binding_name = field_name.replace(".", "_").replace(" ", "_")
|
|
314
373
|
data = set(field_value)
|
|
315
374
|
if len(data) == 1:
|
|
316
375
|
clauses.append(
|
|
@@ -333,7 +392,7 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
|
|
|
333
392
|
clauses.append(self.format_clause_for_query(field_name, "IN", operand))
|
|
334
393
|
|
|
335
394
|
if len(clauses) > 0:
|
|
336
|
-
formatted_fields = ", ".join(field_list)
|
|
395
|
+
formatted_fields = ", ".join([f"`{field}`" for field in field_list])
|
|
337
396
|
query_str = self.get_formatted_query_string(formatted_fields, clauses)
|
|
338
397
|
return text(query_str).params(query_data)
|
|
339
398
|
|