Skip to content

Commit

Permalink
Prevent AC vendors from showing up in vendor sections of the TC string.
Browse files Browse the repository at this point in the history
  • Loading branch information
pattisdr committed Oct 11, 2023
1 parent 5756572 commit 7b86c15
Show file tree
Hide file tree
Showing 3 changed files with 259 additions and 11 deletions.
18 changes: 11 additions & 7 deletions src/fides/api/util/tcf/tc_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,17 @@
gvl: Dict = load_gvl()


def universal_vendor_id_to_id(universal_vendor_id: str) -> int:
"""Converts a universal vendor id to a vendor id
def universal_vendor_id_to_gvl_id(universal_vendor_id: str) -> int:
"""Converts a universal gvl vendor id to a vendor id
For example, converts "gvl.42" to integer 42.
Throws a ValueError if the id cannot be converted to an integer.
Throws a ValueError if the id cannot be converted to an integer or if this is an AC Vendor ID
We store vendor ids as a universal vendor id internally, but need to strip this off when building TC strings.
"""
return int(universal_vendor_id.lstrip("gvl.").lstrip("ac."))
if "ac." in universal_vendor_id:
raise ValueError("Skipping AC Vendor ID")
return int(universal_vendor_id.lstrip("gvl."))


class TCModel(FidesSchema):
Expand Down Expand Up @@ -299,7 +301,7 @@ def _build_vendor_consents_and_legitimate_interests(

for vendor_consent in vendor_consents:
try:
vendor_consent_id: int = universal_vendor_id_to_id(vendor_consent.id)
vendor_consent_id: int = universal_vendor_id_to_gvl_id(vendor_consent.id)
except ValueError:
# Early check that filters out non-integer vendor ids. Later we'll run a separate
# check that ensures this id is also in the gvl.
Expand All @@ -312,7 +314,9 @@ def _build_vendor_consents_and_legitimate_interests(

for vendor_legitimate_interest in vendor_legitimate_interests:
try:
vendor_li_id: int = universal_vendor_id_to_id(vendor_legitimate_interest.id)
vendor_li_id: int = universal_vendor_id_to_gvl_id(
vendor_legitimate_interest.id
)
except ValueError:
# Early check that filters out non-integer vendor ids. Later we'll run a separate
# check that ensures this id is also in the gvl.
Expand Down Expand Up @@ -362,7 +366,7 @@ def _build_vendors_disclosed(tcf_contents: TCFExperienceContents) -> List[int]:
for vendor_list in vendor_main_lists:
for vendor in vendor_list:
try:
vendor_id: int = universal_vendor_id_to_id(vendor.id)
vendor_id: int = universal_vendor_id_to_gvl_id(vendor.id)
except ValueError:
continue
if vendor_id in all_vendor_ids:
Expand Down
29 changes: 29 additions & 0 deletions tests/fixtures/application_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -2830,6 +2830,35 @@ def tcf_system(db: Session) -> System:
return system


@pytest.fixture(scope="function")
def ac_system(db: Session) -> System:
"""Test AC System - will be fleshed out further later"""
system = System.create(
db=db,
data={
"fides_key": f"ac_system{uuid.uuid4()}",
"vendor_id": "ac.8",
"name": f"Test AC System",
"organization_fides_key": "default_organization",
"system_type": "Service",
},
)

PrivacyDeclaration.create(
db=db,
data={
"system_id": system.id,
"data_use": "functional.storage",
"legal_basis_for_processing": "Consent",
"features": [
"Match and combine data from other data sources", # Feature 1
"Link different devices", # Feature 2
],
},
)
return system


# Detailed systems with attributes for TC string testing
# Please don't update them!

Expand Down
223 changes: 219 additions & 4 deletions tests/ops/util/test_tc_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from fides.api.util.tcf.tc_model import (
CMP_ID,
convert_tcf_contents_to_tc_model,
universal_vendor_id_to_id,
universal_vendor_id_to_gvl_id,
)
from fides.api.util.tcf.tc_string import (
TCModel,
Expand Down Expand Up @@ -340,7 +340,7 @@ def test_build_tc_string_captify_accept_all(self, db):
assert decoded.interests_vendors == {}
assert decoded.pub_restriction_entries == []

assert (decoded.oob_disclosed_vendors) == {1: False, 2: True}
assert decoded.oob_disclosed_vendors == {1: False, 2: True}

@pytest.mark.usefixtures("emerse_system")
def test_build_tc_string_emerse_accept_all(self, db):
Expand Down Expand Up @@ -1125,6 +1125,221 @@ def test_build_tc_string_generic_reject_all(
num: num == vendor_id for num in range(1, vendor_id + 1)
}

@pytest.mark.usefixtures("captify_technologies_system")
def test_build_tc_string_captify_accept_all(self, db):
tcf_contents = get_tcf_contents(db)
model = convert_tcf_contents_to_tc_model(
tcf_contents, UserConsentPreference.opt_in
)

assert model.cmp_id == 407
assert model.vendor_list_version == 20
assert model.policy_version == 4
assert model.cmp_version == 1
assert model.consent_screen == 1

assert model.vendor_consents == [2]
assert model.vendor_legitimate_interests == []
assert model.purpose_consents == [1, 2, 3, 4, 7, 9, 10]
assert model.purpose_legitimate_interests == []
assert model.special_feature_optins == [2]

tc_str = build_tc_string(model)
decoded = decode_v2(tc_str)

assert decoded.version == 2
assert datetime.utcnow().date() == decoded.created.date()
assert decoded.cmp_id == 407
assert decoded.cmp_version == 1
assert decoded.consent_screen == 1
assert decoded.consent_language == b"EN"
assert decoded.vendor_list_version == 20
assert decoded.tcf_policy_version == 4
assert decoded.is_service_specific is False
assert decoded.use_non_standard_stacks is False
assert decoded.special_features_optin == {
1: False,
2: True,
3: False,
4: False,
5: False,
6: False,
7: False,
8: False,
9: False,
10: False,
11: False,
12: False,
}
assert decoded.purposes_consent == {
1: True,
2: True,
3: True,
4: True,
5: False,
6: False,
7: True,
8: False,
9: True,
10: True,
11: False,
12: False,
13: False,
14: False,
15: False,
16: False,
17: False,
18: False,
19: False,
20: False,
21: False,
22: False,
23: False,
24: False,
}
assert decoded.purposes_legitimate_interests == {
1: False,
2: False,
3: False,
4: False,
5: False,
6: False,
7: False,
8: False,
9: False,
10: False,
11: False,
12: False,
13: False,
14: False,
15: False,
16: False,
17: False,
18: False,
19: False,
20: False,
21: False,
22: False,
23: False,
24: False,
}
assert decoded.purpose_one_treatment is False
assert decoded.publisher_cc == b"AA"
assert decoded.consented_vendors == {1: False, 2: True}
assert decoded.interests_vendors == {}
assert decoded.pub_restriction_entries == []

assert decoded.oob_disclosed_vendors == {1: False, 2: True}

@pytest.mark.usefixtures("ac_system")
def test_ac_system_not_in_tc_string(self, db):
"""System with AC vendor id will not show up in the vendor consents section, but its purpose
with legal basis of consent does show up in purpose consents (this is the same thing we do if we
have a system that is not in the GVL too)"""
tcf_contents = get_tcf_contents(db)
model = convert_tcf_contents_to_tc_model(
tcf_contents, UserConsentPreference.opt_in
)

assert model.cmp_id == 407
assert model.vendor_list_version == 20
assert model.policy_version == 4
assert model.cmp_version == 1
assert model.consent_screen == 1

assert model.vendor_consents == []
assert model.vendor_legitimate_interests == []
assert model.purpose_consents == [1]
assert model.purpose_legitimate_interests == []
assert model.special_feature_optins == []

tc_str = build_tc_string(model)
decoded = decode_v2(tc_str)

assert decoded.version == 2
assert datetime.utcnow().date() == decoded.created.date()
assert decoded.cmp_id == 407
assert decoded.cmp_version == 1
assert decoded.consent_screen == 1
assert decoded.consent_language == b"EN"
assert decoded.vendor_list_version == 20
assert decoded.tcf_policy_version == 4
assert decoded.is_service_specific is False
assert decoded.use_non_standard_stacks is False
assert decoded.special_features_optin == {
1: False,
2: False,
3: False,
4: False,
5: False,
6: False,
7: False,
8: False,
9: False,
10: False,
11: False,
12: False,
}
assert decoded.purposes_consent == {
1: True,
2: False,
3: False,
4: False,
5: False,
6: False,
7: False,
8: False,
9: False,
10: False,
11: False,
12: False,
13: False,
14: False,
15: False,
16: False,
17: False,
18: False,
19: False,
20: False,
21: False,
22: False,
23: False,
24: False,
}
assert decoded.purposes_legitimate_interests == {
1: False,
2: False,
3: False,
4: False,
5: False,
6: False,
7: False,
8: False,
9: False,
10: False,
11: False,
12: False,
13: False,
14: False,
15: False,
16: False,
17: False,
18: False,
19: False,
20: False,
21: False,
22: False,
23: False,
24: False,
}
assert decoded.purpose_one_treatment is False
assert decoded.publisher_cc == b"AA"
assert decoded.consented_vendors == {}
assert decoded.interests_vendors == {}
assert decoded.pub_restriction_entries == []

assert decoded.oob_disclosed_vendors == {}


class TestBuildTCMobileData:
@pytest.mark.usefixtures("captify_technologies_system")
Expand Down Expand Up @@ -1315,7 +1530,7 @@ def test_decode_tc_string_to_preferences(self, db):
)
assert isinstance(pref.id, str)
assert pref.id.startswith("gvl.")
assert universal_vendor_id_to_id(pref.id) in datamap_vendor_consents
assert universal_vendor_id_to_gvl_id(pref.id) in datamap_vendor_consents

assert len(
fides_tcf_preferences.vendor_legitimate_interests_preferences
Expand All @@ -1326,7 +1541,7 @@ def test_decode_tc_string_to_preferences(self, db):
assert isinstance(pref.id, str)
assert pref.id.startswith("gvl.")
assert (
universal_vendor_id_to_id(pref.id)
universal_vendor_id_to_gvl_id(pref.id)
in datamap_vendor_legitimate_interests
)

Expand Down

0 comments on commit 7b86c15

Please sign in to comment.