ethyca-fides 2.58.2b3__py2.py3-none-any.whl → 2.58.2rc0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ethyca_fides-2.58.2b3.dist-info → ethyca_fides-2.58.2rc0.dist-info}/METADATA +1 -1
- {ethyca_fides-2.58.2b3.dist-info → ethyca_fides-2.58.2rc0.dist-info}/RECORD +172 -181
- fides/_version.py +3 -3
- fides/api/api/deps.py +2 -8
- fides/api/api/v1/endpoints/user_endpoints.py +12 -8
- fides/api/cryptography/identity_salt.py +13 -12
- fides/api/custom_types.py +1 -6
- fides/api/db/base.py +0 -5
- fides/api/db/system.py +3 -1
- fides/api/migrations/hash_migration_job.py +2 -2
- fides/api/models/attachment.py +11 -80
- fides/api/models/comment.py +15 -45
- fides/api/models/detection_discovery.py +0 -31
- fides/api/models/fides_user.py +9 -26
- fides/api/models/fides_user_invite.py +0 -2
- fides/api/models/privacy_experience.py +0 -42
- fides/api/models/privacy_request/privacy_request.py +6 -23
- fides/api/schemas/connection_configuration/connection_config.py +16 -30
- fides/api/schemas/user.py +1 -5
- fides/api/service/deps.py +0 -9
- fides/api/service/storage/s3.py +1 -14
- fides/api/service/user/fides_user_service.py +128 -0
- fides/api/task/graph_task.py +1 -1
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{1150-2642cd9cdc8a52f6.js → 1150-035a721a04f4451e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/1376-5cea5ef9362215e8.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{2397-0d1c289b788fcc11.js → 2397-ee53235fb21b5e97.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/3412-7ec8751b8182e1bf.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{3855-63495367531cb776.js → 3855-b6b7865dedd7bc2a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{3872-472bb47eb34d8fdb.js → 3872-4e053c20d546f027.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{4060-53a5c6347690a8fa.js → 4060-8d165e1236ea521a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/4450-36234280bee624ff.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4481-aab99ff80f707473.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{5258-cf7b27ef51f38392.js → 5258-0658dc2274df6832.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{5480-52dc446be40725f5.js → 5480-f49696df5e8ae500.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{5487-8678d75ee1d1ef09.js → 5487-3ad50d21cdbc9209.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/5973-52aee296edc44f7e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6315-1adb10a8b98b4a13.js → 6315-fa1519cdf080f42d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{6372-e0bb9f8d07cc3b04.js → 6372-ca9c12ac8902365b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{6853-09e831e9dff7fd3b.js → 6853-8941824350c3c1a8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{6954-baa1d873abfe8b77.js → 6954-3b887fb444f9228c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/7453-39761c38da31257e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{7751-a70fe0e5f67f5538.js → 7751-a8f31c062d4cb09d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{79-8e060d36d36c752c.js → 79-f9b948ebb186900f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{7980-72f745bff9fabcc9.js → 7980-4bd08957448dea32.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{9046-8a5fdd335a76d224.js → 9046-04bd7becea207cb1.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/9767-1a23925d2cb27b51.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{main-090643377c8254e6.js → main-24f422f93845a596.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{main-app-59156a9331ac7bce.js → main-app-94a0711202e08b15.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-fbe3db87623c87a0.js → _app-fc89ce7bed454c84.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-9f9500c639362aa6.js → manual-9acaab973dfe86e2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-fac606150b65d494.js → add-systems-d258f0c25fa020bf.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-fb75fa0aea77678d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{new-c02b14c50b19bd91.js → new-a9d9402c219d13e5.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-c946b33b0322b8ad.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-ea57f9d6ad17e957.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{reporting-100234c23a85d235.js → reporting-788cf0e34829af46.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-49bffaf07973fead.js → data-catalog-900004e402c31797.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/{[systemId]-727f1f5d06be674b.js → [systemId]-b66831fdafcdf67c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-5f9ef1f99818117c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{action-center-719949074f10bd6e.js → action-center-d001337d1bb73bd1.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-11b3ce9f61d9bfe9.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-4d4a31d0186a4a5b.js → new-803c1b577ab17ae3.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-29317d18ce34adfe.js → dataset-fa743ddc7f89d76b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-14c57e5069e9cccd.js → [id]-bbe1ca2793798e6b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-f0a4e385c1ad8fee.js → new-abc17fef69cd951b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-a78a73b65929853a.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{index-b70def65e264270e.js → index-bfaacdb55a5a6c9f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-d4329043219fed9b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{[id]-72d64a02b7ef175e.js → [id]-fe765154315782cf.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{messaging-5e2687ab5ab10275.js → messaging-f5f7a8069909ef24.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{storage-2914aade73dcaecc.js → storage-9f7eaad05e5b9292.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{privacy-requests-b0f801d66e79a31a.js → privacy-requests-d85c0d16ba09ba35.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/{datamap-f035cddf17f4d898.js → datamap-afedc48ef4e7f858.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-89524101b7279f6e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-dfcd7a4b6aa773bd.js → custom-fields-52d030b1db2ca1b9.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-0e0aa552f520f913.js → organization-a08693d0d1e10bc8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-1d83d5178b3eb216.js → test-datasets-151571cff4e85894.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-a158fa77df288523.js → [id]-4f5a28226575c976.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{systems-8490aaaee9d76a4a.js → systems-abd68fc5ddde5482.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-be1ffe267b1602e1.js → taxonomy-16b4d75c49276add.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-c0378fd1a26a71da.js → [id]-78eaf933f755bfe8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-3ca3c687e72d1364.js → user-management-6c9ad62479a7d03e.js} +1 -1
- fides/ui-build/static/admin/_next/static/css/{cf2744a40308fc4a.css → 113d823fe71f6af0.css} +1 -1
- fides/ui-build/static/admin/_next/static/o0mKeH0cB6eAYV6qOlVD0/_buildManifest.js +1 -0
- fides/ui-build/static/admin/add-systems/manual.html +1 -1
- fides/ui-build/static/admin/add-systems/multiple.html +1 -1
- fides/ui-build/static/admin/add-systems.html +1 -1
- fides/ui-build/static/admin/ant-poc.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-tcf.js +4 -4
- 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/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.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/api/alembic/migrations/versions/67d01c4e124e_add_reject_all_mechanism_to_privacy_.py +0 -56
- fides/api/alembic/migrations/versions/6e565c16dae1_add_tcf_publisher_restrictions.py +0 -107
- fides/api/alembic/migrations/versions/99c603c1b8f9_add_password_login_enabled_and_totp_secret_to_fidesuser.py +0 -45
- fides/api/models/tcf_publisher_restrictions.py +0 -304
- fides/service/error_handling/__init__.py +0 -0
- fides/service/error_handling/error_handler.py +0 -202
- fides/service/user/__init__.py +0 -0
- fides/service/user/user_service.py +0 -140
- fides/ui-build/static/admin/_next/static/S7gURhIaHGAv7MFBTEOOS/_buildManifest.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1376-03e7f50e708b7589.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/146-0ae2d30ec71fce09.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1817-f82105a9608bba1a.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3938-6a1c07d06a80cf4c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4450-6a8aa0d7358ac26f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4481-275aa9f4c10bce53.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5973-d3d3872692c1d0fa.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9767-8179ce7336727141.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-ab3ef485f6101697.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-ba5325035c71a97e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-ef31c181cac86c64.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-53efbed54d230f07.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-4892603e743cd6ab.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-d2f88a8fc68944db.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-b75ab4ee677f118d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-3ac1e5d3de5dd4a7.js +0 -1
- fides/ui-build/static/admin/images/connector-logos/website.svg +0 -10
- {ethyca_fides-2.58.2b3.dist-info → ethyca_fides-2.58.2rc0.dist-info}/LICENSE +0 -0
- {ethyca_fides-2.58.2b3.dist-info → ethyca_fides-2.58.2rc0.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.58.2b3.dist-info → ethyca_fides-2.58.2rc0.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.58.2b3.dist-info → ethyca_fides-2.58.2rc0.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{4723-1dd1d16f404d56a2.js → 4723-0a3c5e2ce143a7d0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{5826-ef0aa43ffad83acc.js → 5826-e5dcb4e68cfe6289.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-a4e636eecaba5324.js → configure-723cc3d4f5740ea6.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-606457ef5bd1eb04.js → [id]-72251b48e2e03a1e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-2ae030f3a28f057a.js → about-a49d0f84cf0cf05e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-72ec54ee8755a503.js → domain-records-fa42d8f18df44927.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-f8bca2e322ddf252.js → new-be690621a944bfe2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/{S7gURhIaHGAv7MFBTEOOS → o0mKeH0cB6eAYV6qOlVD0}/_ssgManifest.js +0 -0
@@ -1,107 +0,0 @@
|
|
1
|
-
"""Add TCF Publisher Restrictions
|
2
|
-
|
3
|
-
Revision ID: 6e565c16dae1
|
4
|
-
Revises: 67d01c4e124e
|
5
|
-
Create Date: 2025-04-02 12:35:34.105607
|
6
|
-
|
7
|
-
"""
|
8
|
-
|
9
|
-
import sqlalchemy as sa
|
10
|
-
from alembic import op
|
11
|
-
from sqlalchemy.dialects import postgresql
|
12
|
-
|
13
|
-
# revision identifiers, used by Alembic.
|
14
|
-
revision = "6e565c16dae1"
|
15
|
-
down_revision = "67d01c4e124e"
|
16
|
-
branch_labels = None
|
17
|
-
depends_on = None
|
18
|
-
|
19
|
-
|
20
|
-
def upgrade():
|
21
|
-
# Create tcf_configuration table
|
22
|
-
op.create_table(
|
23
|
-
"tcf_configuration",
|
24
|
-
sa.Column("id", sa.String(length=255), nullable=False),
|
25
|
-
sa.Column(
|
26
|
-
"created_at",
|
27
|
-
sa.DateTime(timezone=True),
|
28
|
-
server_default=sa.text("now()"),
|
29
|
-
nullable=True,
|
30
|
-
),
|
31
|
-
sa.Column(
|
32
|
-
"updated_at",
|
33
|
-
sa.DateTime(timezone=True),
|
34
|
-
server_default=sa.text("now()"),
|
35
|
-
nullable=True,
|
36
|
-
),
|
37
|
-
sa.Column("name", sa.String(), nullable=False),
|
38
|
-
sa.PrimaryKeyConstraint("id"),
|
39
|
-
sa.UniqueConstraint("name"),
|
40
|
-
)
|
41
|
-
# Create tcf_publisher_restriction table
|
42
|
-
op.create_table(
|
43
|
-
"tcf_publisher_restriction",
|
44
|
-
sa.Column("id", sa.String(length=255), nullable=False),
|
45
|
-
sa.Column(
|
46
|
-
"created_at",
|
47
|
-
sa.DateTime(timezone=True),
|
48
|
-
server_default=sa.text("now()"),
|
49
|
-
nullable=True,
|
50
|
-
),
|
51
|
-
sa.Column(
|
52
|
-
"updated_at",
|
53
|
-
sa.DateTime(timezone=True),
|
54
|
-
server_default=sa.text("now()"),
|
55
|
-
nullable=True,
|
56
|
-
),
|
57
|
-
sa.Column("tcf_configuration_id", sa.String(255), nullable=False),
|
58
|
-
sa.Column("purpose_id", sa.Integer(), nullable=False),
|
59
|
-
sa.Column(
|
60
|
-
"restriction_type",
|
61
|
-
sa.Enum(
|
62
|
-
"purpose_restriction",
|
63
|
-
"require_consent",
|
64
|
-
"require_legitimate_interest",
|
65
|
-
name="tcfrestrictiontype",
|
66
|
-
),
|
67
|
-
nullable=False,
|
68
|
-
),
|
69
|
-
sa.Column(
|
70
|
-
"vendor_restriction",
|
71
|
-
sa.Enum(
|
72
|
-
"restrict_all_vendors",
|
73
|
-
"allow_specific_vendors",
|
74
|
-
"restrict_specific_vendors",
|
75
|
-
name="tcfvendorrestriction",
|
76
|
-
),
|
77
|
-
nullable=False,
|
78
|
-
),
|
79
|
-
sa.Column(
|
80
|
-
"range_entries",
|
81
|
-
postgresql.ARRAY(postgresql.JSONB(astext_type=sa.Text())),
|
82
|
-
server_default="{}",
|
83
|
-
nullable=False,
|
84
|
-
),
|
85
|
-
sa.ForeignKeyConstraint(
|
86
|
-
["tcf_configuration_id"],
|
87
|
-
["tcf_configuration.id"],
|
88
|
-
ondelete="CASCADE",
|
89
|
-
),
|
90
|
-
sa.PrimaryKeyConstraint("id"),
|
91
|
-
)
|
92
|
-
op.create_index(
|
93
|
-
op.f("ix_tcf_publisher_restriction_config_purpose"),
|
94
|
-
"tcf_publisher_restriction",
|
95
|
-
["tcf_configuration_id", "purpose_id"],
|
96
|
-
unique=False,
|
97
|
-
)
|
98
|
-
|
99
|
-
|
100
|
-
def downgrade():
|
101
|
-
# Drop tables
|
102
|
-
op.drop_table("tcf_publisher_restriction")
|
103
|
-
op.drop_table("tcf_configuration")
|
104
|
-
|
105
|
-
# Drop enums
|
106
|
-
op.execute("DROP TYPE tcfrestrictiontype")
|
107
|
-
op.execute("DROP TYPE tcfvendorrestriction")
|
@@ -1,45 +0,0 @@
|
|
1
|
-
"""add password login enabled and totp secret to fidesuser
|
2
|
-
|
3
|
-
Revision ID: 99c603c1b8f9
|
4
|
-
Revises: 6e565c16dae1
|
5
|
-
Create Date: 2025-04-02 01:55:57.890545
|
6
|
-
|
7
|
-
"""
|
8
|
-
|
9
|
-
import sqlalchemy as sa
|
10
|
-
import sqlalchemy_utils
|
11
|
-
from alembic import op
|
12
|
-
|
13
|
-
# revision identifiers, used by Alembic.
|
14
|
-
revision = "99c603c1b8f9"
|
15
|
-
down_revision = "6e565c16dae1"
|
16
|
-
branch_labels = None
|
17
|
-
depends_on = None
|
18
|
-
|
19
|
-
|
20
|
-
def upgrade():
|
21
|
-
op.add_column(
|
22
|
-
"fidesuser",
|
23
|
-
sa.Column(
|
24
|
-
"password_login_enabled",
|
25
|
-
sa.Boolean(),
|
26
|
-
nullable=True,
|
27
|
-
),
|
28
|
-
)
|
29
|
-
op.add_column(
|
30
|
-
"fidesuser",
|
31
|
-
sa.Column(
|
32
|
-
"totp_secret",
|
33
|
-
sqlalchemy_utils.types.encrypted.encrypted_type.StringEncryptedType(),
|
34
|
-
nullable=True,
|
35
|
-
),
|
36
|
-
)
|
37
|
-
op.alter_column("fidesuser", "hashed_password", nullable=True)
|
38
|
-
op.alter_column("fidesuser", "salt", nullable=True)
|
39
|
-
|
40
|
-
|
41
|
-
def downgrade():
|
42
|
-
op.alter_column("fidesuser", "hashed_password", nullable=False)
|
43
|
-
op.alter_column("fidesuser", "salt", nullable=False)
|
44
|
-
op.drop_column("fidesuser", "totp_secret")
|
45
|
-
op.drop_column("fidesuser", "password_login_enabled")
|
@@ -1,304 +0,0 @@
|
|
1
|
-
from enum import Enum
|
2
|
-
from typing import Any, Dict, List, Optional
|
3
|
-
|
4
|
-
from pydantic import BaseModel, Field, ValidationError, model_validator
|
5
|
-
from sqlalchemy import Column
|
6
|
-
from sqlalchemy import Enum as EnumColumn
|
7
|
-
from sqlalchemy import ForeignKey, Index, Integer, String, insert, select, update
|
8
|
-
from sqlalchemy.dialects.postgresql import ARRAY, JSONB
|
9
|
-
from sqlalchemy.ext.asyncio import AsyncSession
|
10
|
-
from sqlalchemy.ext.declarative import declared_attr
|
11
|
-
from sqlalchemy.orm import Session
|
12
|
-
|
13
|
-
from fides.api.db.base_class import Base
|
14
|
-
|
15
|
-
|
16
|
-
class TCFRestrictionType(str, Enum):
|
17
|
-
"""Enum for TCF restriction types"""
|
18
|
-
|
19
|
-
purpose_restriction = "purpose_restriction"
|
20
|
-
require_consent = "require_consent"
|
21
|
-
require_legitimate_interest = "require_legitimate_interest"
|
22
|
-
|
23
|
-
|
24
|
-
class TCFVendorRestriction(str, Enum):
|
25
|
-
"""Enum for TCF vendor restriction types"""
|
26
|
-
|
27
|
-
restrict_all_vendors = "restrict_all_vendors"
|
28
|
-
allow_specific_vendors = "allow_specific_vendors"
|
29
|
-
restrict_specific_vendors = "restrict_specific_vendors"
|
30
|
-
|
31
|
-
|
32
|
-
# This is Pydantic model used for validation, not a database model!
|
33
|
-
class RangeEntry(BaseModel):
|
34
|
-
"""
|
35
|
-
Pydantic model that represents a vendor range entry as per the TCF spec,
|
36
|
-
used for Publisher Restrictions.
|
37
|
-
A range entry must have a start_vendor_id and optionally an end_vendor_id.
|
38
|
-
If end_vendor_id is present, it must be greater than start_vendor_id.
|
39
|
-
"""
|
40
|
-
|
41
|
-
start_vendor_id: int = Field(description="The starting vendor ID in the range")
|
42
|
-
end_vendor_id: Optional[int] = Field(
|
43
|
-
default=None, description="The ending vendor ID in the range (inclusive)"
|
44
|
-
)
|
45
|
-
|
46
|
-
@model_validator(mode="after")
|
47
|
-
def validate_vendor_range(self) -> "RangeEntry":
|
48
|
-
"""Validates that end_vendor_id is greater than start_vendor_id if present."""
|
49
|
-
if (
|
50
|
-
self.end_vendor_id is not None
|
51
|
-
and self.end_vendor_id <= self.start_vendor_id
|
52
|
-
):
|
53
|
-
raise ValueError("end_vendor_id must be greater than start_vendor_id")
|
54
|
-
return self
|
55
|
-
|
56
|
-
def get_end(self) -> int:
|
57
|
-
"""Get the effective end of the range."""
|
58
|
-
return self.end_vendor_id or self.start_vendor_id
|
59
|
-
|
60
|
-
def overlaps_with(self, other: "RangeEntry") -> bool:
|
61
|
-
"""
|
62
|
-
Check if this range overlaps with another range.
|
63
|
-
Two ranges overlap if the end of the range that starts first is greater than
|
64
|
-
or equal to the start of the range that starts second.
|
65
|
-
"""
|
66
|
-
# Sort ranges by start_vendor_id
|
67
|
-
first = self if self.start_vendor_id <= other.start_vendor_id else other
|
68
|
-
second = other if self.start_vendor_id <= other.start_vendor_id else self
|
69
|
-
|
70
|
-
# If first range's end is >= second range's start, they overlap
|
71
|
-
return first.get_end() >= second.start_vendor_id
|
72
|
-
|
73
|
-
|
74
|
-
class TCFConfiguration(Base):
|
75
|
-
"""
|
76
|
-
Stores TCF Configuration settings.
|
77
|
-
"""
|
78
|
-
|
79
|
-
@declared_attr
|
80
|
-
def __tablename__(self) -> str:
|
81
|
-
return "tcf_configuration"
|
82
|
-
|
83
|
-
name = Column(String, nullable=False, index=True, unique=True)
|
84
|
-
|
85
|
-
|
86
|
-
class TCFPublisherRestriction(Base):
|
87
|
-
"""
|
88
|
-
Stores TCF Publisher Restrictions. TCF Publisher Restrictions belong to a TCF Configuration,
|
89
|
-
and specify the restriction type and vendor restriction for a given purpose.
|
90
|
-
"""
|
91
|
-
|
92
|
-
@declared_attr
|
93
|
-
def __tablename__(self) -> str:
|
94
|
-
return "tcf_publisher_restriction"
|
95
|
-
|
96
|
-
tcf_configuration_id = Column(
|
97
|
-
String(255),
|
98
|
-
ForeignKey("tcf_configuration.id", ondelete="CASCADE"),
|
99
|
-
nullable=False,
|
100
|
-
index=True,
|
101
|
-
)
|
102
|
-
purpose_id = Column(Integer, nullable=False)
|
103
|
-
restriction_type = Column(EnumColumn(TCFRestrictionType), nullable=False)
|
104
|
-
vendor_restriction = Column(EnumColumn(TCFVendorRestriction), nullable=False)
|
105
|
-
|
106
|
-
# range_entries represents a list of RangeEntry objects as per the TCF spec.
|
107
|
-
# A RangeEntry object will have a start_vendor_id and an end_vendor_id (the
|
108
|
-
# end vendor id is included in the range).
|
109
|
-
# If the range represents a single vendor, then the end_vendor_id can be omitted.
|
110
|
-
# TCF spec also includes an IsARange boolean, which we are omitting because it
|
111
|
-
# can be inferred from the presence of the end_vendor_id.
|
112
|
-
range_entries = Column(
|
113
|
-
ARRAY(JSONB),
|
114
|
-
nullable=True,
|
115
|
-
server_default="{}",
|
116
|
-
default=list,
|
117
|
-
)
|
118
|
-
|
119
|
-
__table_args__ = (
|
120
|
-
Index(
|
121
|
-
"ix_tcf_publisher_restriction_config_purpose",
|
122
|
-
"tcf_configuration_id",
|
123
|
-
"purpose_id",
|
124
|
-
),
|
125
|
-
)
|
126
|
-
|
127
|
-
def __init__(self, **kwargs: Any) -> None:
|
128
|
-
validated_kwargs = self.validate_publisher_restriction_data(kwargs)
|
129
|
-
super().__init__(**validated_kwargs)
|
130
|
-
|
131
|
-
@staticmethod
|
132
|
-
def validate_entires_for_vendor_restriction(
|
133
|
-
entries: List[RangeEntry], vendor_restriction: TCFVendorRestriction
|
134
|
-
) -> None:
|
135
|
-
"""
|
136
|
-
Validates that if vendor_restriction is restrict_all_vendors, then the entries list is empty.
|
137
|
-
"""
|
138
|
-
if vendor_restriction == TCFVendorRestriction.restrict_all_vendors and entries:
|
139
|
-
raise ValueError("restrict_all_vendors cannot have any range entries")
|
140
|
-
|
141
|
-
@staticmethod
|
142
|
-
def check_for_overlaps(entries: List[RangeEntry]) -> None:
|
143
|
-
"""
|
144
|
-
Check for overlapping ranges in a list of RangeEntry objects,
|
145
|
-
raises a ValueError if any pair of RangeEntry overlaps with each other.
|
146
|
-
Sorts the ranges by start_vendor_id and compares adjacent ranges.
|
147
|
-
"""
|
148
|
-
# Sort ranges by start_vendor_id
|
149
|
-
sorted_entries = sorted(entries, key=lambda x: x.start_vendor_id)
|
150
|
-
|
151
|
-
# Compare each range with the next one
|
152
|
-
for i in range(len(sorted_entries) - 1):
|
153
|
-
current = sorted_entries[i]
|
154
|
-
next_entry = sorted_entries[i + 1]
|
155
|
-
if current.overlaps_with(next_entry):
|
156
|
-
raise ValueError(
|
157
|
-
f"Overlapping ranges found: {current.model_dump()} overlaps with {next_entry.model_dump()}"
|
158
|
-
)
|
159
|
-
|
160
|
-
@classmethod
|
161
|
-
def validate_publisher_restriction_data(
|
162
|
-
cls, data: Dict[str, Any]
|
163
|
-
) -> Dict[str, Any]:
|
164
|
-
"""
|
165
|
-
Validate the restriction data.
|
166
|
-
"""
|
167
|
-
raw_range_entries = data.get("range_entries", [])
|
168
|
-
|
169
|
-
try:
|
170
|
-
# Validate each range entry using the Pydantic model
|
171
|
-
validated_entries = [
|
172
|
-
RangeEntry.model_validate(entry) for entry in raw_range_entries
|
173
|
-
]
|
174
|
-
except ValidationError as e:
|
175
|
-
raise ValueError(f"Invalid range entry: {str(e)}")
|
176
|
-
|
177
|
-
# Validate the entries for the vendor restriction
|
178
|
-
cls.validate_entires_for_vendor_restriction(
|
179
|
-
validated_entries, data["vendor_restriction"]
|
180
|
-
)
|
181
|
-
# Check for overlapping ranges
|
182
|
-
cls.check_for_overlaps(validated_entries)
|
183
|
-
|
184
|
-
data["range_entries"] = [entry.model_dump() for entry in validated_entries]
|
185
|
-
|
186
|
-
return data
|
187
|
-
|
188
|
-
@classmethod
|
189
|
-
async def validate_vendor_overlaps_for_purpose(
|
190
|
-
cls,
|
191
|
-
async_db: AsyncSession,
|
192
|
-
configuration_id: str,
|
193
|
-
purpose_id: int,
|
194
|
-
new_data: dict,
|
195
|
-
) -> None:
|
196
|
-
"""
|
197
|
-
Validates that the new vendor ranges do not overlap with any existing vendor ranges for the purpose
|
198
|
-
in the given configuration.
|
199
|
-
Raises a ValueError if any vendor ranges overlap.
|
200
|
-
"""
|
201
|
-
# First, get all the restrictions for the purpose in the given configuration
|
202
|
-
query = (
|
203
|
-
select(cls) # type: ignore[arg-type]
|
204
|
-
.where(cls.tcf_configuration_id == configuration_id)
|
205
|
-
.where(cls.purpose_id == purpose_id)
|
206
|
-
)
|
207
|
-
restrictions = await async_db.execute(query)
|
208
|
-
restrictions = restrictions.scalars().all()
|
209
|
-
|
210
|
-
# If we have an existing id, we need to exclude the current restriction from the list of existing restrictions
|
211
|
-
# so that the new_data restrictions don't overlap with themselves
|
212
|
-
if "id" in new_data:
|
213
|
-
existing_entries = [
|
214
|
-
entry
|
215
|
-
for r in restrictions
|
216
|
-
for entry in r.range_entries
|
217
|
-
if r.id != new_data["id"]
|
218
|
-
]
|
219
|
-
else:
|
220
|
-
existing_entries = [
|
221
|
-
entry for r in restrictions for entry in r.range_entries
|
222
|
-
]
|
223
|
-
|
224
|
-
all_entries = [*existing_entries, *new_data.get("range_entries", [])]
|
225
|
-
cls.check_for_overlaps(
|
226
|
-
[RangeEntry.model_validate(entry) for entry in all_entries]
|
227
|
-
)
|
228
|
-
|
229
|
-
@classmethod
|
230
|
-
def create(
|
231
|
-
cls,
|
232
|
-
db: Session,
|
233
|
-
*,
|
234
|
-
data: Dict[str, Any],
|
235
|
-
check_name: bool = True,
|
236
|
-
) -> "TCFPublisherRestriction":
|
237
|
-
raise NotImplementedError("Use create_async instead")
|
238
|
-
|
239
|
-
@classmethod
|
240
|
-
async def create_async(
|
241
|
-
cls,
|
242
|
-
async_db: AsyncSession,
|
243
|
-
*,
|
244
|
-
data: Dict[str, Any],
|
245
|
-
) -> "TCFPublisherRestriction":
|
246
|
-
"""
|
247
|
-
Create a new TCFPublisherRestriction with validated range_entries.
|
248
|
-
"""
|
249
|
-
|
250
|
-
data = cls.validate_publisher_restriction_data(data)
|
251
|
-
|
252
|
-
values = {
|
253
|
-
"tcf_configuration_id": data["tcf_configuration_id"],
|
254
|
-
"purpose_id": data["purpose_id"],
|
255
|
-
"restriction_type": data["restriction_type"],
|
256
|
-
"vendor_restriction": data["vendor_restriction"],
|
257
|
-
"range_entries": data["range_entries"],
|
258
|
-
}
|
259
|
-
|
260
|
-
# Validate that the new vendor ranges do not overlap with any existing vendor ranges for the purpose
|
261
|
-
await cls.validate_vendor_overlaps_for_purpose(
|
262
|
-
async_db=async_db,
|
263
|
-
configuration_id=data["tcf_configuration_id"],
|
264
|
-
purpose_id=data["purpose_id"],
|
265
|
-
new_data=values,
|
266
|
-
)
|
267
|
-
|
268
|
-
# Insert the new restriction
|
269
|
-
insert_stmt = insert(cls).values(values) # type: ignore[arg-type]
|
270
|
-
result = await async_db.execute(insert_stmt)
|
271
|
-
record_id = result.inserted_primary_key.id
|
272
|
-
|
273
|
-
created_record = await async_db.execute(select(cls).where(cls.id == record_id)) # type: ignore[arg-type]
|
274
|
-
return created_record.scalars().first()
|
275
|
-
|
276
|
-
async def update_async(
|
277
|
-
self, async_db: AsyncSession, data: Dict[str, Any]
|
278
|
-
) -> "TCFPublisherRestriction":
|
279
|
-
"""
|
280
|
-
Update a TCFPublisherRestriction with the data.
|
281
|
-
Validates the data and checks for vendor overlaps.
|
282
|
-
"""
|
283
|
-
# Validate the data on its own
|
284
|
-
data = self.validate_publisher_restriction_data(data)
|
285
|
-
|
286
|
-
# Validate that the new vendor ranges do not overlap with any existing vendor ranges for the purpose
|
287
|
-
await self.validate_vendor_overlaps_for_purpose(
|
288
|
-
async_db=async_db,
|
289
|
-
configuration_id=self.tcf_configuration_id,
|
290
|
-
purpose_id=self.purpose_id,
|
291
|
-
new_data={**data, "id": self.id}, # Pass in id explicitly
|
292
|
-
)
|
293
|
-
|
294
|
-
# Finally, make the update
|
295
|
-
update_query = (
|
296
|
-
update(TCFPublisherRestriction) # type: ignore[arg-type]
|
297
|
-
.where(TCFPublisherRestriction.id == self.id)
|
298
|
-
.values(**data)
|
299
|
-
)
|
300
|
-
await async_db.execute(update_query)
|
301
|
-
await async_db.commit()
|
302
|
-
await async_db.refresh(self)
|
303
|
-
|
304
|
-
return self
|
File without changes
|
@@ -1,202 +0,0 @@
|
|
1
|
-
from functools import wraps
|
2
|
-
from typing import Any, Callable, Optional, TypeVar
|
3
|
-
|
4
|
-
from fastapi import HTTPException
|
5
|
-
from loguru import logger
|
6
|
-
from starlette.status import HTTP_400_BAD_REQUEST, HTTP_422_UNPROCESSABLE_ENTITY
|
7
|
-
|
8
|
-
T = TypeVar("T")
|
9
|
-
|
10
|
-
|
11
|
-
class ErrorHandler:
|
12
|
-
"""Utility class for handling errors consistently throughout the application.
|
13
|
-
|
14
|
-
Usage Examples:
|
15
|
-
-----------------------------------------------------------------------------
|
16
|
-
|
17
|
-
1. Basic Validation:
|
18
|
-
```python
|
19
|
-
from fastapi import FastAPI
|
20
|
-
from fides.service.error_handling.error_handler import ErrorHandler
|
21
|
-
|
22
|
-
app = FastAPI()
|
23
|
-
|
24
|
-
@app.post("/users")
|
25
|
-
def create_user(age: int):
|
26
|
-
# Simple validation
|
27
|
-
ErrorHandler.validate(age >= 18, "User must be 18 or older")
|
28
|
-
return {"message": "User created"}
|
29
|
-
|
30
|
-
@app.get("/items/{item_id}")
|
31
|
-
def get_item(item_id: str):
|
32
|
-
# Direct error raising
|
33
|
-
if not item_id:
|
34
|
-
ErrorHandler.raise_error("Item ID is required", status_code=400)
|
35
|
-
return {"item_id": item_id}
|
36
|
-
```
|
37
|
-
|
38
|
-
2. Exception Handling Decorator:
|
39
|
-
```python
|
40
|
-
@app.post("/orders")
|
41
|
-
@ErrorHandler.handle_exceptions("Failed to create order")
|
42
|
-
def create_order(order_data: dict):
|
43
|
-
if not order_data.get("items"):
|
44
|
-
raise ValueError("Order must contain items")
|
45
|
-
# Process order...
|
46
|
-
return {"message": "Order created"}
|
47
|
-
|
48
|
-
@app.get("/products/{product_id}")
|
49
|
-
@ErrorHandler.handle_exceptions("Failed to fetch product", status_code=404)
|
50
|
-
def get_product(product_id: str):
|
51
|
-
product = database.get_product(product_id)
|
52
|
-
if not product:
|
53
|
-
raise ValueError("Product not found")
|
54
|
-
return product
|
55
|
-
```
|
56
|
-
|
57
|
-
3. Complex Validation:
|
58
|
-
```python
|
59
|
-
@app.post("/payments")
|
60
|
-
@ErrorHandler.handle_exceptions("Payment processing failed")
|
61
|
-
def process_payment(payment: dict):
|
62
|
-
# Validate amount
|
63
|
-
ErrorHandler.validate(
|
64
|
-
payment.get("amount", 0) > 0,
|
65
|
-
"Payment amount must be positive",
|
66
|
-
HTTP_400_BAD_REQUEST
|
67
|
-
)
|
68
|
-
|
69
|
-
# Validate currency
|
70
|
-
ErrorHandler.validate(
|
71
|
-
payment.get("currency") in ["USD", "EUR"],
|
72
|
-
"Invalid currency",
|
73
|
-
HTTP_400_BAD_REQUEST,
|
74
|
-
"Unsupported currency provided" # Optional log message
|
75
|
-
)
|
76
|
-
|
77
|
-
# Custom error for insufficient funds
|
78
|
-
if payment.get("amount", 0) > get_balance():
|
79
|
-
ErrorHandler.raise_error(
|
80
|
-
"Insufficient funds",
|
81
|
-
status_code=HTTP_400_BAD_REQUEST,
|
82
|
-
log_message="User attempted payment exceeding balance"
|
83
|
-
)
|
84
|
-
|
85
|
-
return {"status": "payment processed"}
|
86
|
-
```
|
87
|
-
|
88
|
-
4. Error Logging:
|
89
|
-
```python
|
90
|
-
@app.post("/imports")
|
91
|
-
@ErrorHandler.handle_exceptions("Import failed")
|
92
|
-
def import_data(data: dict):
|
93
|
-
try:
|
94
|
-
process_import(data)
|
95
|
-
except Exception as e:
|
96
|
-
# Log error with custom message before raising
|
97
|
-
ErrorHandler.raise_error(
|
98
|
-
"Import failed: invalid format",
|
99
|
-
status_code=400,
|
100
|
-
log_message=f"Import failed with error: {str(e)}"
|
101
|
-
)
|
102
|
-
return {"status": "import complete"}
|
103
|
-
```
|
104
|
-
|
105
|
-
Key Features:
|
106
|
-
- Simple validation with custom error messages
|
107
|
-
- Consistent error handling across endpoints
|
108
|
-
- Built-in error logging
|
109
|
-
- HTTP status code customization
|
110
|
-
- Exception handling decorator for common patterns
|
111
|
-
"""
|
112
|
-
|
113
|
-
@staticmethod
|
114
|
-
def raise_error(
|
115
|
-
detail: str,
|
116
|
-
status_code: int = HTTP_422_UNPROCESSABLE_ENTITY,
|
117
|
-
log_message: Optional[str] = None,
|
118
|
-
) -> None:
|
119
|
-
"""Raise an HTTPException with consistent logging.
|
120
|
-
|
121
|
-
Args:
|
122
|
-
detail: Error message to include in the HTTPException
|
123
|
-
status_code: HTTP status code to use (default: 422)
|
124
|
-
log_message: Optional message to log before raising the exception
|
125
|
-
|
126
|
-
Raises:
|
127
|
-
HTTPException: Always raised with the provided details
|
128
|
-
"""
|
129
|
-
if log_message:
|
130
|
-
logger.error(log_message)
|
131
|
-
raise HTTPException(status_code=status_code, detail=detail)
|
132
|
-
|
133
|
-
@staticmethod
|
134
|
-
def validate(
|
135
|
-
condition: bool,
|
136
|
-
detail: str,
|
137
|
-
status_code: int = HTTP_400_BAD_REQUEST,
|
138
|
-
log_message: Optional[str] = None,
|
139
|
-
) -> None:
|
140
|
-
"""Validate a condition and raise an error if it's False.
|
141
|
-
|
142
|
-
Args:
|
143
|
-
condition: The condition to check
|
144
|
-
detail: Error message if condition is False
|
145
|
-
status_code: HTTP status code to use if condition is False
|
146
|
-
log_message: Optional message to log before raising the exception
|
147
|
-
|
148
|
-
Raises:
|
149
|
-
HTTPException: If the condition is False
|
150
|
-
"""
|
151
|
-
if not condition:
|
152
|
-
ErrorHandler.raise_error(detail, status_code, log_message)
|
153
|
-
|
154
|
-
@classmethod
|
155
|
-
def handle_exceptions(
|
156
|
-
cls, error_message: str, status_code: int = HTTP_422_UNPROCESSABLE_ENTITY
|
157
|
-
) -> Callable[[Callable[..., T]], Callable[..., T]]:
|
158
|
-
"""Decorator to handle exceptions consistently.
|
159
|
-
|
160
|
-
Args:
|
161
|
-
error_message: Base error message to use
|
162
|
-
status_code: HTTP status code to use for unexpected errors
|
163
|
-
|
164
|
-
Returns:
|
165
|
-
Callable: The decorated function that handles exceptions
|
166
|
-
|
167
|
-
Note:
|
168
|
-
This decorator will catch specific exceptions and convert them to HTTPExceptions.
|
169
|
-
HTTPExceptions are re-raised as is, while other exceptions are wrapped with
|
170
|
-
additional context.
|
171
|
-
"""
|
172
|
-
|
173
|
-
def decorator(func: Callable[..., T]) -> Callable[..., T]:
|
174
|
-
@wraps(func)
|
175
|
-
def wrapper(*args: Any, **kwargs: Any) -> Optional[T]:
|
176
|
-
try:
|
177
|
-
return func(*args, **kwargs)
|
178
|
-
except HTTPException:
|
179
|
-
# Re-raise HTTP exceptions without modification
|
180
|
-
raise
|
181
|
-
except (ValueError, TypeError, AttributeError, KeyError) as e:
|
182
|
-
# Handle common validation and data access errors
|
183
|
-
cls.raise_error(
|
184
|
-
f"{error_message}: {str(e)}",
|
185
|
-
status_code,
|
186
|
-
f"{error_message}: {e}",
|
187
|
-
)
|
188
|
-
except Exception as e: # pylint: disable=broad-except
|
189
|
-
# Log unexpected errors with full context but present a sanitized message
|
190
|
-
logger.error(
|
191
|
-
f"Unexpected error in {func.__name__}: {str(e)}", exc_info=True
|
192
|
-
)
|
193
|
-
cls.raise_error(
|
194
|
-
f"{error_message}: An unexpected error occurred",
|
195
|
-
status_code,
|
196
|
-
f"{error_message}: Unexpected {type(e).__name__}",
|
197
|
-
)
|
198
|
-
return None # This line is never reached but satisfies the return type checker
|
199
|
-
|
200
|
-
return wrapper # type: ignore[return-value]
|
201
|
-
|
202
|
-
return decorator
|
fides/service/user/__init__.py
DELETED
File without changes
|