ethyca-fides 2.66.2b0__py2.py3-none-any.whl → 2.66.2b2__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.2b0.dist-info → ethyca_fides-2.66.2b2.dist-info}/METADATA +1 -1
- {ethyca_fides-2.66.2b0.dist-info → ethyca_fides-2.66.2b2.dist-info}/RECORD +156 -154
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/a7065df4dcf1_add_finalized_fields_to_privacy_request.py +65 -0
- fides/api/api/v1/endpoints/privacy_request_endpoints.py +44 -1
- fides/api/graph/traversal.py +1 -1
- fides/api/models/privacy_request/execution_log.py +1 -0
- fides/api/models/privacy_request/privacy_request.py +6 -0
- fides/api/schemas/policy.py +1 -0
- fides/api/schemas/privacy_request.py +5 -0
- fides/api/service/privacy_request/request_runner_service.py +116 -53
- fides/api/task/create_request_tasks.py +1 -1
- fides/api/task/execute_request_tasks.py +1 -1
- fides/api/task/filter_results.py +1 -1
- fides/api/task/manual/manual_task_address.py +46 -0
- fides/api/task/manual/manual_task_graph_task.py +118 -127
- fides/api/task/manual/manual_task_utils.py +52 -105
- fides/common/api/v1/urn_registry.py +1 -1
- fides/config/execution_settings.py +4 -0
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/{QGhYEMWDATmdofDzIl0_2 → EACyrT3Bb5qN9POVQHTCB}/_buildManifest.js +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{203-d9fe5384a6e94799.js → 203-4e777c324a01dbec.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/5309-1172322bf91b5d57.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6780-4b687168dd8daa84.js → 6780-a00c87739acc004d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/9046-c8233981762585b4.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-65e47e67bee20654.js → _app-f2c3d287bac00395.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-7d3115059503b904.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-f9c0eac932188593.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-c73497fc333c324d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-7498d1d5974a78b0.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-2d3a2d967767a131.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-6a9068df48bdee05.js +1 -0
- fides/ui-build/static/admin/_next/static/css/79e296c724c1568c.css +1 -0
- fides/ui-build/static/admin/_next/static/css/{94965f224bc991e9.css → 8bc1833f1fa53ff0.css} +1 -1
- fides/ui-build/static/admin/add-systems/manual.html +1 -1
- fides/ui-build/static/admin/add-systems/multiple.html +1 -1
- fides/ui-build/static/admin/add-systems.html +1 -1
- fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
- fides/ui-build/static/admin/consent/configure.html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
- fides/ui-build/static/admin/consent/properties.html +1 -1
- fides/ui-build/static/admin/consent/reporting.html +1 -1
- fides/ui-build/static/admin/consent.html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
- fides/ui-build/static/admin/data-catalog.html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
- fides/ui-build/static/admin/data-discovery/activity.html +1 -1
- fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-discovery/detection.html +1 -1
- fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
- fides/ui-build/static/admin/datamap.html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
- fides/ui-build/static/admin/dataset/new.html +1 -1
- fides/ui-build/static/admin/dataset.html +1 -1
- fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
- fides/ui-build/static/admin/datastore-connection/new.html +1 -1
- fides/ui-build/static/admin/datastore-connection.html +1 -1
- fides/ui-build/static/admin/index.html +1 -1
- fides/ui-build/static/admin/integrations/[id].html +1 -1
- fides/ui-build/static/admin/integrations.html +1 -1
- fides/ui-build/static/admin/lib/fides-preview.js +1 -1
- fides/ui-build/static/admin/lib/fides-tcf.js +1 -1
- fides/ui-build/static/admin/lib/fides.js +1 -1
- fides/ui-build/static/admin/login/[provider].html +1 -1
- fides/ui-build/static/admin/login.html +1 -1
- fides/ui-build/static/admin/messaging/[id].html +1 -1
- fides/ui-build/static/admin/messaging/add-template.html +1 -1
- fides/ui-build/static/admin/messaging.html +1 -1
- fides/ui-build/static/admin/poc/ant-components.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
- fides/ui-build/static/admin/poc/forms.html +1 -1
- fides/ui-build/static/admin/poc/table-migration.html +1 -1
- fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
- fides/ui-build/static/admin/privacy-requests.html +1 -1
- fides/ui-build/static/admin/properties/[id].html +1 -1
- fides/ui-build/static/admin/properties/add-property.html +1 -1
- fides/ui-build/static/admin/properties.html +1 -1
- fides/ui-build/static/admin/reporting/datamap.html +1 -1
- fides/ui-build/static/admin/settings/about/alpha.html +1 -1
- fides/ui-build/static/admin/settings/about.html +1 -1
- fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
- fides/ui-build/static/admin/settings/consent.html +1 -1
- fides/ui-build/static/admin/settings/custom-fields.html +1 -1
- fides/ui-build/static/admin/settings/domain-records.html +1 -1
- fides/ui-build/static/admin/settings/domains.html +1 -1
- fides/ui-build/static/admin/settings/email-templates.html +1 -1
- fides/ui-build/static/admin/settings/locations.html +1 -1
- fides/ui-build/static/admin/settings/organization.html +1 -1
- fides/ui-build/static/admin/settings/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/chunks/5309-67bdf9001531e972.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9046-54877976a0529de2.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-7e63ac744c45f6da.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-8d83a5518c00fcfc.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-f18b2b83c4592f03.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-f9be923340cd99cf.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-eb1ccc8a5dbf0fe5.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-1daa805a3099c6d4.js +0 -1
- fides/ui-build/static/admin/_next/static/css/b81194f2c3930152.css +0 -1
- {ethyca_fides-2.66.2b0.dist-info → ethyca_fides-2.66.2b2.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.66.2b0.dist-info → ethyca_fides-2.66.2b2.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.66.2b0.dist-info → ethyca_fides-2.66.2b2.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.66.2b0.dist-info → ethyca_fides-2.66.2b2.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{QGhYEMWDATmdofDzIl0_2 → EACyrT3Bb5qN9POVQHTCB}/_ssgManifest.js +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3450-f7bb8d46fbe4e78d.js → 3450-1cc2bb07ed142203.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3872-08c855f3edb230c4.js → 3872-84b7e380b88b4454.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{5574-a7d8a753d273b11a.js → 5574-9312f97b637d9ee2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6084-a872a8bc704c980f.js → 6084-5d7598b7bcb548cf.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6662-4a7805f74af51f1d.js → 6662-efb2cf74641647f2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6882-85c6277d9531a83a.js → 6882-586b84aeb02d5830.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{7476-cfe41e915f0c4f40.js → 7476-d206c11823c91088.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{7630-7a8039aa37893129.js → 7630-b1c93688013ef013.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{787-d05b25c73f49b6af.js → 787-cbe2d0bfb513d90a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{79-1952e8aab93b7209.js → 79-3db1941d274f40c7.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{796-d2876f4d09a6d81f.js → 796-98d4bd68909fbe1e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9226-7a799c930b73f217.js → 9226-72ad691ca57b83ef.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9826-b7d5467e3a3225c8.js → 9826-3c578665c6d3b21d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-f28841affa791975.js → add-systems-0943633a8e422695.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-50d21e23b607688b.js → configure-0e1ca0f4c8e7f4da.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-509691da6b06f834.js → consent-e17c56eec8d91371.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-2f43cfefc869b85f.js → data-catalog-8a7f9285da66b965.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{action-center-409694b8441bd8fb.js → action-center-85e140788e251272.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-e707dc714452498e.js → datamap-d2b275d83089820d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{datastore-connection-6865aa958f3da342.js → datastore-connection-0f29b47402292070.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{index-f127ebaac4689d10.js → index-12ac3e317fc86f21.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-295bfe6880b209f2.js → configure-e551a860ec727802.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/{alpha-8152e5828469cf91.js → alpha-8f98a4895e74725e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-9839e544924ac1f2.js → about-8155a35a62fdb5ae.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-f7a0f8367129cd70.js → consent-a989532a12c40dcf.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-10df8d6fdc8bc149.js → custom-fields-45bea76ff7eda3cb.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-4056a3323cdc7042.js → domain-records-51333dbd21cb37c8.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-5a3691559262e874.js → domains-bde86e5f6c09da5a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-e1d3c3d0ab878812.js → email-templates-4f9a5cc8bea7725b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-26df33de21fb11b3.js → organization-55a10e01dffc8039.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-170c9c55f65ffb0f.js → test-datasets-f91f22cf96566ed4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{systems-2112c006765c66b6.js → systems-648a0ff4920579ce.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-a53e89319582ce58.js → taxonomy-0b9d1a24188f65a9.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{webpack-ff4c22c9f0840531.js → webpack-63a0c45b150a1037.js} +0 -0
fides/_version.py
CHANGED
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-07-
|
|
11
|
+
"date": "2025-07-29T12:07:43-0300",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "2.66.
|
|
14
|
+
"full-revisionid": "4ba0450a533ee5c9b65ed8ae70df97cfb829ef8d",
|
|
15
|
+
"version": "2.66.2b2"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""add finalized fields to privacy request
|
|
2
|
+
|
|
3
|
+
Revision ID: a7065df4dcf1
|
|
4
|
+
Revises: 7e9a2b52f498
|
|
5
|
+
Create Date: 2025-07-01 14:07:36.779437
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
from alembic import op
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = "a7065df4dcf1"
|
|
14
|
+
down_revision = "7e9a2b52f498"
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade():
|
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
21
|
+
op.add_column(
|
|
22
|
+
"privacyrequest",
|
|
23
|
+
sa.Column("finalized_at", sa.DateTime(timezone=True), nullable=True),
|
|
24
|
+
)
|
|
25
|
+
op.add_column(
|
|
26
|
+
"privacyrequest", sa.Column("finalized_by", sa.String(), nullable=True)
|
|
27
|
+
)
|
|
28
|
+
op.create_foreign_key(
|
|
29
|
+
"privacyrequest_fidesuser_id_fkey",
|
|
30
|
+
"privacyrequest",
|
|
31
|
+
"fidesuser",
|
|
32
|
+
["finalized_by"],
|
|
33
|
+
["id"],
|
|
34
|
+
ondelete="SET NULL",
|
|
35
|
+
)
|
|
36
|
+
op.execute(
|
|
37
|
+
"alter type privacyrequeststatus add value 'requires_manual_finalization'"
|
|
38
|
+
)
|
|
39
|
+
# ### end Alembic commands ###
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def downgrade():
|
|
43
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
44
|
+
op.drop_constraint(
|
|
45
|
+
"privacyrequest_fidesuser_id_fkey", "privacyrequest", type_="foreignkey"
|
|
46
|
+
)
|
|
47
|
+
op.drop_column("privacyrequest", "finalized_by")
|
|
48
|
+
op.drop_column("privacyrequest", "finalized_at")
|
|
49
|
+
|
|
50
|
+
op.execute(
|
|
51
|
+
"delete from privacyrequest where status in ('requires_manual_finalization')"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
op.execute("alter type privacyrequeststatus rename to privacyrequeststatus_old")
|
|
55
|
+
op.execute(
|
|
56
|
+
"create type privacyrequeststatus as enum('identity_unverified', 'requires_input', 'pending', 'in_processing', 'complete', 'pending', 'error', 'paused', 'approved', 'denied', 'canceled', 'awaiting_email_send')"
|
|
57
|
+
)
|
|
58
|
+
op.execute(
|
|
59
|
+
(
|
|
60
|
+
"alter table privacyrequest alter column status type privacyrequeststatus using "
|
|
61
|
+
"status::text::privacyrequeststatus"
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
op.execute("drop type privacyrequeststatus_old")
|
|
65
|
+
# ### end Alembic commands ###
|
|
@@ -4,7 +4,7 @@ import csv
|
|
|
4
4
|
import io
|
|
5
5
|
import json
|
|
6
6
|
from collections import defaultdict
|
|
7
|
-
from datetime import datetime
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
8
|
from typing import (
|
|
9
9
|
Annotated,
|
|
10
10
|
Any,
|
|
@@ -146,6 +146,7 @@ from fides.common.api.v1.urn_registry import (
|
|
|
146
146
|
PRIVACY_REQUEST_BULK_SOFT_DELETE,
|
|
147
147
|
PRIVACY_REQUEST_DENY,
|
|
148
148
|
PRIVACY_REQUEST_FILTERED_RESULTS,
|
|
149
|
+
PRIVACY_REQUEST_FINALIZE,
|
|
149
150
|
PRIVACY_REQUEST_MANUAL_WEBHOOK_ACCESS_INPUT,
|
|
150
151
|
PRIVACY_REQUEST_MANUAL_WEBHOOK_ERASURE_INPUT,
|
|
151
152
|
PRIVACY_REQUEST_NOTIFICATIONS,
|
|
@@ -1862,6 +1863,48 @@ def resume_privacy_request_from_requires_input(
|
|
|
1862
1863
|
return privacy_request # type: ignore[return-value]
|
|
1863
1864
|
|
|
1864
1865
|
|
|
1866
|
+
@router.post(
|
|
1867
|
+
PRIVACY_REQUEST_FINALIZE,
|
|
1868
|
+
status_code=HTTP_200_OK,
|
|
1869
|
+
response_model=PrivacyRequestResponse,
|
|
1870
|
+
dependencies=[Security(verify_oauth_client, scopes=[PRIVACY_REQUEST_REVIEW])],
|
|
1871
|
+
)
|
|
1872
|
+
def finalize_privacy_request(
|
|
1873
|
+
privacy_request_id: str,
|
|
1874
|
+
*,
|
|
1875
|
+
db: Session = Depends(deps.get_db),
|
|
1876
|
+
client: ClientDetail = Security(
|
|
1877
|
+
verify_oauth_client,
|
|
1878
|
+
scopes=[PRIVACY_REQUEST_REVIEW],
|
|
1879
|
+
),
|
|
1880
|
+
) -> PrivacyRequestResponse:
|
|
1881
|
+
"""
|
|
1882
|
+
Finalizes a privacy request, moving it from the 'requires_finalization' state to 'complete'.
|
|
1883
|
+
This is done by re-queueing the request, which will then hit the finalization logic in the
|
|
1884
|
+
request runner service. This logic marks the privacy request as complete
|
|
1885
|
+
and sends out any configured messaging to the user.
|
|
1886
|
+
"""
|
|
1887
|
+
privacy_request = get_privacy_request_or_error(db, privacy_request_id)
|
|
1888
|
+
|
|
1889
|
+
if privacy_request.status != PrivacyRequestStatus.requires_manual_finalization:
|
|
1890
|
+
raise HTTPException(
|
|
1891
|
+
status_code=HTTP_400_BAD_REQUEST,
|
|
1892
|
+
detail=f"Cannot manually finalize privacy request '{privacy_request_id}': status is {privacy_request.status}, not requires_manual_finalization.",
|
|
1893
|
+
)
|
|
1894
|
+
|
|
1895
|
+
# Set finalized_by and finalized_at here, so the request runner service knows not to
|
|
1896
|
+
# put the request back into the requires_finalization state.
|
|
1897
|
+
privacy_request.finalized_at = datetime.now(timezone.utc)
|
|
1898
|
+
privacy_request.finalized_by = client.user_id
|
|
1899
|
+
privacy_request.save(db=db)
|
|
1900
|
+
|
|
1901
|
+
queue_privacy_request(
|
|
1902
|
+
privacy_request_id=privacy_request_id,
|
|
1903
|
+
)
|
|
1904
|
+
|
|
1905
|
+
return privacy_request # type: ignore[return-value]
|
|
1906
|
+
|
|
1907
|
+
|
|
1865
1908
|
@router.get(
|
|
1866
1909
|
REQUEST_TASKS,
|
|
1867
1910
|
dependencies=[Security(verify_oauth_client, scopes=[PRIVACY_REQUEST_READ])],
|
fides/api/graph/traversal.py
CHANGED
|
@@ -115,7 +115,7 @@ class BaseTraversal:
|
|
|
115
115
|
)
|
|
116
116
|
|
|
117
117
|
# Ensure manual_task collections execute right after ROOT
|
|
118
|
-
from fides.api.task.manual.
|
|
118
|
+
from fides.api.task.manual.manual_task_address import ManualTaskAddress
|
|
119
119
|
|
|
120
120
|
for addr in self.traversal_node_dict.keys():
|
|
121
121
|
if ManualTaskAddress.is_manual_task_address(addr):
|
|
@@ -145,6 +145,12 @@ class PrivacyRequest(
|
|
|
145
145
|
ForeignKey(FidesUser.id_field_path, ondelete="SET NULL"),
|
|
146
146
|
nullable=True,
|
|
147
147
|
)
|
|
148
|
+
finalized_at = Column(DateTime(timezone=True), nullable=True)
|
|
149
|
+
finalized_by = Column(
|
|
150
|
+
String,
|
|
151
|
+
ForeignKey(FidesUser.id_field_path, ondelete="SET NULL"),
|
|
152
|
+
nullable=True,
|
|
153
|
+
)
|
|
148
154
|
submitted_by = Column(
|
|
149
155
|
String,
|
|
150
156
|
ForeignKey(FidesUser.id_field_path, ondelete="SET NULL"),
|
fides/api/schemas/policy.py
CHANGED
|
@@ -124,6 +124,8 @@ class PrivacyRequestResubmit(PrivacyRequestCreate):
|
|
|
124
124
|
identity_verified_at: Optional[datetime] = None
|
|
125
125
|
custom_privacy_request_fields_approved_at: Optional[datetime] = None
|
|
126
126
|
custom_privacy_request_fields_approved_by: Optional[str] = None
|
|
127
|
+
finalized_at: Optional[datetime] = None
|
|
128
|
+
finalized_by: Optional[str] = None
|
|
127
129
|
|
|
128
130
|
|
|
129
131
|
class ConsentRequestCreate(FidesSchema):
|
|
@@ -287,6 +289,7 @@ class PrivacyRequestStatus(str, EnumType):
|
|
|
287
289
|
complete = "complete"
|
|
288
290
|
paused = "paused"
|
|
289
291
|
awaiting_email_send = "awaiting_email_send"
|
|
292
|
+
requires_manual_finalization = "requires_manual_finalization"
|
|
290
293
|
canceled = "canceled"
|
|
291
294
|
error = "error"
|
|
292
295
|
|
|
@@ -320,6 +323,8 @@ class PrivacyRequestResponse(FidesSchema):
|
|
|
320
323
|
source: Optional[PrivacyRequestSource] = None
|
|
321
324
|
deleted_at: Optional[datetime] = None
|
|
322
325
|
deleted_by: Optional[str] = None
|
|
326
|
+
finalized_at: Optional[datetime] = None
|
|
327
|
+
finalized_by: Optional[str] = None
|
|
323
328
|
model_config = ConfigDict(from_attributes=True, use_enum_values=True)
|
|
324
329
|
|
|
325
330
|
|
|
@@ -480,12 +480,21 @@ def run_privacy_request(
|
|
|
480
480
|
connection_configs
|
|
481
481
|
)
|
|
482
482
|
|
|
483
|
+
# If the privacy request requires manual finalization and has not yet been finalized, we exit here
|
|
484
|
+
if (
|
|
485
|
+
privacy_request.status
|
|
486
|
+
== PrivacyRequestStatus.requires_manual_finalization
|
|
487
|
+
and privacy_request.finalized_at is None
|
|
488
|
+
):
|
|
489
|
+
return
|
|
490
|
+
|
|
483
491
|
# Access CHECKPOINT
|
|
484
492
|
if (
|
|
485
493
|
policy.get_rules_for_action(action_type=ActionType.access)
|
|
486
494
|
or policy.get_rules_for_action(action_type=ActionType.erasure)
|
|
487
495
|
) and can_run_checkpoint(
|
|
488
|
-
request_checkpoint=CurrentStep.access,
|
|
496
|
+
request_checkpoint=CurrentStep.access,
|
|
497
|
+
from_checkpoint=resume_step,
|
|
489
498
|
):
|
|
490
499
|
privacy_request.cache_failed_checkpoint_details(CurrentStep.access)
|
|
491
500
|
access_runner(
|
|
@@ -530,7 +539,8 @@ def run_privacy_request(
|
|
|
530
539
|
if policy.get_rules_for_action(
|
|
531
540
|
action_type=ActionType.erasure
|
|
532
541
|
) and can_run_checkpoint(
|
|
533
|
-
request_checkpoint=CurrentStep.erasure,
|
|
542
|
+
request_checkpoint=CurrentStep.erasure,
|
|
543
|
+
from_checkpoint=resume_step,
|
|
534
544
|
):
|
|
535
545
|
privacy_request.cache_failed_checkpoint_details(CurrentStep.erasure)
|
|
536
546
|
_verify_masking_secrets(policy, privacy_request, resume_step)
|
|
@@ -656,58 +666,111 @@ def run_privacy_request(
|
|
|
656
666
|
if not proceed:
|
|
657
667
|
return
|
|
658
668
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
else MessagingActionType.PRIVACY_REQUEST_COMPLETE_DELETION
|
|
667
|
-
)
|
|
668
|
-
|
|
669
|
-
if message_send_enabled(
|
|
670
|
-
session,
|
|
671
|
-
privacy_request.property_id,
|
|
672
|
-
action_type,
|
|
673
|
-
legacy_request_completion_enabled,
|
|
674
|
-
) and not policy.get_rules_for_action(action_type=ActionType.consent):
|
|
675
|
-
try:
|
|
676
|
-
if not access_result_urls:
|
|
677
|
-
# For DSR 3.0, if the request had both access and erasure rules, this needs to be fetched
|
|
678
|
-
# from the database because the Privacy Request would have exited
|
|
679
|
-
# processing and lost access to the access_result_urls in memory
|
|
680
|
-
access_result_urls = (
|
|
681
|
-
privacy_request.access_result_urls or {}
|
|
682
|
-
).get("access_result_urls", [])
|
|
683
|
-
initiate_privacy_request_completion_email(
|
|
684
|
-
session,
|
|
685
|
-
policy,
|
|
686
|
-
access_result_urls,
|
|
687
|
-
identity_data,
|
|
688
|
-
privacy_request.property_id,
|
|
689
|
-
)
|
|
690
|
-
except (IdentityNotFoundException, MessageDispatchException) as e:
|
|
691
|
-
privacy_request.error_processing(db=session)
|
|
692
|
-
# If dev mode, log traceback
|
|
693
|
-
_log_exception(e, CONFIG.dev_mode)
|
|
694
|
-
return
|
|
695
|
-
|
|
696
|
-
# Only mark as complete if not in error state
|
|
697
|
-
if privacy_request.status != PrivacyRequestStatus.error:
|
|
698
|
-
privacy_request.finished_processing_at = datetime.utcnow()
|
|
699
|
-
AuditLog.create(
|
|
700
|
-
db=session,
|
|
701
|
-
data={
|
|
702
|
-
"user_id": "system",
|
|
703
|
-
"privacy_request_id": privacy_request.id,
|
|
704
|
-
"action": AuditLogAction.finished,
|
|
705
|
-
"message": "",
|
|
706
|
-
},
|
|
669
|
+
# Request finalization CHECKPOINT
|
|
670
|
+
if can_run_checkpoint(
|
|
671
|
+
request_checkpoint=CurrentStep.finalization,
|
|
672
|
+
from_checkpoint=resume_step,
|
|
673
|
+
):
|
|
674
|
+
privacy_request.cache_failed_checkpoint_details(
|
|
675
|
+
CurrentStep.finalization,
|
|
707
676
|
)
|
|
708
|
-
privacy_request.status
|
|
709
|
-
|
|
710
|
-
|
|
677
|
+
if privacy_request.status != PrivacyRequestStatus.error:
|
|
678
|
+
erasure_rules = policy.get_rules_for_action(
|
|
679
|
+
action_type=ActionType.erasure
|
|
680
|
+
)
|
|
681
|
+
if (
|
|
682
|
+
privacy_request.finalized_at is None
|
|
683
|
+
and erasure_rules
|
|
684
|
+
and CONFIG.execution.erasure_request_finalization_required
|
|
685
|
+
):
|
|
686
|
+
logger.info(
|
|
687
|
+
"Marking privacy request '{}' as requires manual finalization.",
|
|
688
|
+
privacy_request.id,
|
|
689
|
+
)
|
|
690
|
+
privacy_request.status = (
|
|
691
|
+
PrivacyRequestStatus.requires_manual_finalization
|
|
692
|
+
)
|
|
693
|
+
privacy_request.save(db=session)
|
|
694
|
+
return
|
|
695
|
+
|
|
696
|
+
# Finally, mark the request as complete
|
|
697
|
+
if privacy_request.finalized_at:
|
|
698
|
+
logger.info(
|
|
699
|
+
"Marking privacy request '{}' as finalized.",
|
|
700
|
+
privacy_request.id,
|
|
701
|
+
)
|
|
702
|
+
privacy_request.add_success_execution_log(
|
|
703
|
+
session,
|
|
704
|
+
connection_key=None,
|
|
705
|
+
dataset_name="Request finalized",
|
|
706
|
+
collection_name=None,
|
|
707
|
+
message=f"Request finalized for privacy request: {privacy_request.id}",
|
|
708
|
+
action_type=privacy_request.policy.get_action_type(), # type: ignore
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
logger.info(
|
|
712
|
+
"Marking privacy request '{}' as complete.",
|
|
713
|
+
privacy_request.id,
|
|
714
|
+
)
|
|
715
|
+
AuditLog.create(
|
|
716
|
+
db=session,
|
|
717
|
+
data={
|
|
718
|
+
"user_id": "system",
|
|
719
|
+
"privacy_request_id": privacy_request.id,
|
|
720
|
+
"action": AuditLogAction.finished,
|
|
721
|
+
"message": "",
|
|
722
|
+
},
|
|
723
|
+
)
|
|
724
|
+
privacy_request.status = PrivacyRequestStatus.complete
|
|
725
|
+
privacy_request.finished_processing_at = datetime.utcnow()
|
|
726
|
+
privacy_request.save(db=session)
|
|
727
|
+
|
|
728
|
+
# Send a final email to the user confirming request completion
|
|
729
|
+
if privacy_request.status == PrivacyRequestStatus.complete:
|
|
730
|
+
legacy_request_completion_enabled = ConfigProxy(
|
|
731
|
+
session
|
|
732
|
+
).notifications.send_request_completion_notification
|
|
733
|
+
|
|
734
|
+
action_type = (
|
|
735
|
+
MessagingActionType.PRIVACY_REQUEST_COMPLETE_ACCESS
|
|
736
|
+
if policy.get_rules_for_action(
|
|
737
|
+
action_type=ActionType.access
|
|
738
|
+
)
|
|
739
|
+
else MessagingActionType.PRIVACY_REQUEST_COMPLETE_DELETION
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
if message_send_enabled(
|
|
743
|
+
session,
|
|
744
|
+
privacy_request.property_id,
|
|
745
|
+
action_type,
|
|
746
|
+
legacy_request_completion_enabled,
|
|
747
|
+
) and not policy.get_rules_for_action(
|
|
748
|
+
action_type=ActionType.consent
|
|
749
|
+
):
|
|
750
|
+
if not access_result_urls:
|
|
751
|
+
# For DSR 3.0, if the request had both access and erasure rules, this needs to be fetched
|
|
752
|
+
# from the database because the Privacy Request would have exited
|
|
753
|
+
# processing and lost access to the access_result_urls in memory
|
|
754
|
+
access_result_urls = (
|
|
755
|
+
privacy_request.access_result_urls or {}
|
|
756
|
+
).get("access_result_urls", [])
|
|
757
|
+
|
|
758
|
+
try:
|
|
759
|
+
initiate_privacy_request_completion_email(
|
|
760
|
+
session,
|
|
761
|
+
policy,
|
|
762
|
+
access_result_urls,
|
|
763
|
+
identity_data,
|
|
764
|
+
privacy_request.property_id,
|
|
765
|
+
)
|
|
766
|
+
except (
|
|
767
|
+
IdentityNotFoundException,
|
|
768
|
+
MessageDispatchException,
|
|
769
|
+
) as e:
|
|
770
|
+
privacy_request.error_processing(db=session)
|
|
771
|
+
# If dev mode, log traceback
|
|
772
|
+
_log_exception(e, CONFIG.dev_mode)
|
|
773
|
+
return
|
|
711
774
|
|
|
712
775
|
|
|
713
776
|
def initiate_privacy_request_completion_email(
|
|
@@ -33,8 +33,8 @@ from fides.api.models.worker_task import ExecutionLogStatus
|
|
|
33
33
|
from fides.api.schemas.policy import ActionType
|
|
34
34
|
from fides.api.task.deprecated_graph_task import format_data_use_map_for_caching
|
|
35
35
|
from fides.api.task.execute_request_tasks import log_task_queued, queue_request_task
|
|
36
|
+
from fides.api.task.manual.manual_task_address import ManualTaskAddress
|
|
36
37
|
from fides.api.task.manual.manual_task_utils import (
|
|
37
|
-
ManualTaskAddress,
|
|
38
38
|
create_manual_task_instances_for_privacy_request,
|
|
39
39
|
)
|
|
40
40
|
from fides.api.util.logger_context_utils import log_context
|
|
@@ -29,8 +29,8 @@ from fides.api.task.graph_task import (
|
|
|
29
29
|
GraphTask,
|
|
30
30
|
mark_current_and_downstream_nodes_as_failed,
|
|
31
31
|
)
|
|
32
|
+
from fides.api.task.manual.manual_task_address import ManualTaskAddress
|
|
32
33
|
from fides.api.task.manual.manual_task_graph_task import ManualTaskGraphTask
|
|
33
|
-
from fides.api.task.manual.manual_task_utils import ManualTaskAddress
|
|
34
34
|
from fides.api.task.task_resources import TaskResources
|
|
35
35
|
from fides.api.tasks import DSR_QUEUE_NAME, DatabaseTask, celery_app
|
|
36
36
|
from fides.api.util.cache import cache_task_tracking_key
|
fides/api/task/filter_results.py
CHANGED
|
@@ -6,7 +6,7 @@ from loguru import logger
|
|
|
6
6
|
|
|
7
7
|
from fides.api.graph.config import CollectionAddress, FieldPath
|
|
8
8
|
from fides.api.graph.graph import DatasetGraph
|
|
9
|
-
from fides.api.task.manual.
|
|
9
|
+
from fides.api.task.manual.manual_task_address import ManualTaskAddress
|
|
10
10
|
from fides.api.util.collection_util import Row
|
|
11
11
|
|
|
12
12
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from fides.api.graph.config import CollectionAddress
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ManualTaskAddress:
|
|
5
|
+
"""Utility class for creating and parsing manual task addresses"""
|
|
6
|
+
|
|
7
|
+
MANUAL_DATA_COLLECTION = "manual_data"
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def create(connection_config_key: str) -> CollectionAddress:
|
|
11
|
+
"""Create a CollectionAddress for manual data: {connection_key}:manual_data"""
|
|
12
|
+
return CollectionAddress(
|
|
13
|
+
dataset=connection_config_key,
|
|
14
|
+
collection=ManualTaskAddress.MANUAL_DATA_COLLECTION,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def _is_manual_data_collection(collection_name: str) -> bool:
|
|
19
|
+
"""Check if collection name represents manual task data"""
|
|
20
|
+
return collection_name == ManualTaskAddress.MANUAL_DATA_COLLECTION
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def is_manual_task_address(address: CollectionAddress) -> bool:
|
|
24
|
+
"""Check if address represents manual task data"""
|
|
25
|
+
if isinstance(address, str):
|
|
26
|
+
# Handle string format "connection_key:collection_name"
|
|
27
|
+
_, _, collection_part = address.partition(":")
|
|
28
|
+
if not collection_part:
|
|
29
|
+
return False
|
|
30
|
+
return ManualTaskAddress._is_manual_data_collection(collection_part)
|
|
31
|
+
|
|
32
|
+
# Handle CollectionAddress object
|
|
33
|
+
return ManualTaskAddress._is_manual_data_collection(address.collection)
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def get_connection_key(address: CollectionAddress) -> str:
|
|
37
|
+
"""Extract connection config key from manual task address"""
|
|
38
|
+
if not ManualTaskAddress.is_manual_task_address(address):
|
|
39
|
+
raise ValueError(f"Not a manual task address: {address}")
|
|
40
|
+
|
|
41
|
+
if isinstance(address, str):
|
|
42
|
+
# Handle string format "connection_key:collection_name"
|
|
43
|
+
return address.split(":")[0]
|
|
44
|
+
|
|
45
|
+
# Handle CollectionAddress object
|
|
46
|
+
return address.dataset
|