Skip to content

Commit

Permalink
As the consent legal basis filter and legitimate interest legal basis…
Browse files Browse the repository at this point in the history
… filter cannot be defined up front anymore (they need access to the override legal basis subquery), return these filters from the matching privacy declarations method. This method has been renamed to "get_tcf_base_query_and_filters".
  • Loading branch information
pattisdr committed Nov 30, 2023
1 parent 64a14c2 commit 8bb1af3
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 27 deletions.
55 changes: 31 additions & 24 deletions src/fides/api/util/tcf/tcf_experience_contents.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# mypy: disable-error-code="arg-type, attr-defined, assignment"
from enum import Enum
from typing import Dict, List, Optional, Union
from typing import Dict, List, Optional, Tuple, Union

from fideslang.gvl import (
MAPPED_PURPOSES,
Expand Down Expand Up @@ -58,13 +58,6 @@
NOT_AC_SYSTEM_FILTER: BooleanClauseList = or_(
not_(System.vendor_id.startswith(AC_PREFIX)), System.vendor_id.is_(None)
)
CONSENT_LEGAL_BASIS_FILTER: BinaryExpression = (
PrivacyDeclaration.legal_basis_for_processing == LegalBasisForProcessingEnum.CONSENT
)
LEGITIMATE_INTEREST_LEGAL_BASIS_FILTER: BinaryExpression = (
PrivacyDeclaration.legal_basis_for_processing
== LegalBasisForProcessingEnum.LEGITIMATE_INTEREST
)

GVL_DATA_USE_FILTER: BinaryExpression = PrivacyDeclaration.data_use.in_(
ALL_GVL_DATA_USES
Expand Down Expand Up @@ -192,17 +185,28 @@ def get_legal_basis_override_subquery(db: Session) -> Alias:
)


def get_matching_privacy_declarations(db: Session) -> Query:
"""Returns flattened system/privacy declaration records where we have a matching gvl data use AND the
overridden legal basis for processing is "Consent" or "Legitimate interests".
IMPORTANT - We are filtering against the "overridden_legal_basis_for_processing", not the defined "legal_basis_for_processing",
which takes into account potential Fides-wide publisher overrides.
def get_tcf_base_query_and_filters(
db: Session,
) -> Tuple[Query, BinaryExpression, BinaryExpression]:
"""
Returns the base query that contains the foundations of the TCF Experience as well as
two filters to further refine the query when building the Experience.
Only systems that meet this criteria should show up in the TCF overlay.
Rows show up corresponding to systems with GVL data uses and Legal bases of Consent or Legitimate interests.
AC systems are also included here.
Publisher overrides are applied at this stage which may suppress purposes or toggle the legal basis.
"""
legal_basis_override_subquery = get_legal_basis_override_subquery(db)

consent_legal_basis_filter: BinaryExpression = (
legal_basis_override_subquery.c.overridden_legal_basis_for_processing
== LegalBasisForProcessingEnum.CONSENT
)
legitimate_interest_legal_basis_filter: BinaryExpression = (
legal_basis_override_subquery.c.overridden_legal_basis_for_processing
== LegalBasisForProcessingEnum.LEGITIMATE_INTEREST
)

matching_privacy_declarations: Query = (
db.query(
System.id.label("system_id"),
Expand Down Expand Up @@ -234,15 +238,10 @@ def get_matching_privacy_declarations(db: Session) -> Query:
)
.filter(
or_(
and_(GVL_DATA_USE_FILTER, consent_legal_basis_filter),
and_(
GVL_DATA_USE_FILTER,
legal_basis_override_subquery.c.overridden_legal_basis_for_processing
== LegalBasisForProcessingEnum.CONSENT,
),
and_(
GVL_DATA_USE_FILTER,
legal_basis_override_subquery.c.overridden_legal_basis_for_processing
== LegalBasisForProcessingEnum.LEGITIMATE_INTEREST,
legitimate_interest_legal_basis_filter,
NOT_AC_SYSTEM_FILTER,
),
AC_SYSTEM_NO_PRIVACY_DECL_FILTER,
Expand All @@ -258,7 +257,11 @@ def get_matching_privacy_declarations(db: Session) -> Query:
NOT_AC_SYSTEM_FILTER
)

return matching_privacy_declarations
return (
matching_privacy_declarations,
consent_legal_basis_filter,
legitimate_interest_legal_basis_filter,
)


def systems_that_match_tcf_data_uses(
Expand Down Expand Up @@ -295,7 +298,11 @@ def get_relevant_systems_for_tcf_attribute( # pylint: disable=too-many-return-s

# For TCF attributes, we need to first filter to systems/privacy declarations that have a relevant GVL data use
# as well as a legal basis of processing of consent or legitimate interests
starting_privacy_declarations: Query = get_matching_privacy_declarations(db)
(
starting_privacy_declarations,
CONSENT_LEGAL_BASIS_FILTER,
LEGITIMATE_INTEREST_LEGAL_BASIS_FILTER,
) = get_tcf_base_query_and_filters(db)

purpose_data_uses: List[str] = []
if tcf_field in [
Expand Down
15 changes: 15 additions & 0 deletions tests/fixtures/application_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
_create_local_default_storage,
default_storage_config_name,
)
from fides.api.models.tcf_publisher_overrides import TCFPublisherOverride
from fides.api.oauth.roles import APPROVER, VIEWER
from fides.api.schemas.messaging.messaging import (
MessagingServiceDetails,
Expand Down Expand Up @@ -3133,3 +3134,17 @@ def skimbit_system(db):
},
)
return system


@pytest.fixture(scope="function")
def purpose_three_consent_publisher_override(db):
override = TCFPublisherOverride.create(
db,
data={
"purpose": 3,
"is_included": True,
"required_legal_basis": "Consent",
},
)
yield override
override.delete(db)
41 changes: 41 additions & 0 deletions tests/ops/models/test_privacy_preference.py
Original file line number Diff line number Diff line change
Expand Up @@ -1653,6 +1653,47 @@ def test_determine_relevant_systems_for_ac_system_purpose_legitimate_interests(
== []
)

@pytest.mark.usefixtures(
"enable_override_vendor_purposes", "purpose_three_consent_publisher_override"
)
def test_determine_relevant_systems_for_with_publisher_override(
self,
db,
system_with_no_uses,
):
# Add data use to system that corresponds to purpose 3. Also has LI legal basis, but override sets it
# to Consent
pd_1 = PrivacyDeclaration.create(
db=db,
data={
"name": "Collect data for content performance",
"system_id": system_with_no_uses.id,
"data_categories": ["user.device.cookie_id"],
"data_use": "marketing.advertising.profiling",
"data_qualifier": "aggregated.anonymized.unlinked_pseudonymized.pseudonymized.identified",
"data_subjects": ["customer"],
"dataset_references": None,
"legal_basis_for_processing": "Legitimate interests",
"egress": None,
"ingress": None,
},
)

assert PrivacyPreferenceHistory.determine_relevant_systems(
db, tcf_field=TCFComponentType.purpose_consent.value, tcf_value=3
) == [system_with_no_uses.fides_key]

assert (
PrivacyPreferenceHistory.determine_relevant_systems(
db,
tcf_field=TCFComponentType.purpose_legitimate_interests.value,
tcf_value=3,
)
== []
)

pd_1.delete(db)


class TestCurrentPrivacyPreference:
def test_get_preference_by_notice_and_fides_user_device(
Expand Down
6 changes: 3 additions & 3 deletions tests/ops/util/test_tcf_privacy_declarations.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest

from fides.api.models.tcf_publisher_overrides import TCFPublisherOverride
from fides.api.util.tcf.tcf_experience_contents import get_matching_privacy_declarations
from fides.api.util.tcf.tcf_experience_contents import get_tcf_base_query_and_filters


class TestMatchingPrivacyDeclarations:
Expand All @@ -15,7 +15,7 @@ class TestMatchingPrivacyDeclarations:
def test_get_matching_privacy_declarations_enable_purpose_override_is_false(
self, db
):
declarations = get_matching_privacy_declarations(db)
declarations, _, _ = get_tcf_base_query_and_filters(db)

assert declarations.count() == 13

Expand Down Expand Up @@ -86,7 +86,7 @@ def test_privacy_declaration_publisher_overrides(
},
)

declarations = get_matching_privacy_declarations(db)
declarations, _, _ = get_tcf_base_query_and_filters(db)

legal_basis_overrides = {
declaration.purpose: declaration.overridden_legal_basis_for_processing
Expand Down

0 comments on commit 8bb1af3

Please sign in to comment.