Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backend] Add Systems Applicable Filter to Privacy Experience List #3654

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ The types of changes are:

## [Unreleased](https://github.com/ethyca/fides/compare/2.15.0...main)

### Fixed

- Add Systems Applicable Filter to Privacy Experience List [#3654](https://github.com/ethyca/fides/pull/3654)


## [2.15.0](https://github.com/ethyca/fides/compare/2.14.1...2.15.0)

### Added
Expand Down
2 changes: 1 addition & 1 deletion clients/admin-ui/src/features/system/system.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const systemApi = baseApi.injectEndpoints({
params: { resource_type: "system" },
method: "DELETE",
}),
invalidatesTags: ["System", "Datastore Connection"],
invalidatesTags: ["System", "Datastore Connection", "Privacy Notices"],
}),
upsertSystems: build.mutation<UpsertResponse, System[]>({
query: (systems) => ({
Expand Down
1 change: 1 addition & 0 deletions clients/fides-js/src/services/fides/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const fetchExperience = async (
component: ComponentType.OVERLAY,
has_notices: "true",
has_config: "true",
systems_applicable: "true",
fides_user_device_id: fidesUserDeviceId,
});
const response = await fetch(
Expand Down
2 changes: 2 additions & 0 deletions clients/privacy-center/features/consent/consent.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export const consentApi = baseApi.injectEndpoints({
component: ComponentType.PRIVACY_CENTER,
has_notices: true,
show_disabled: false,
has_config: true,
pattisdr marked this conversation as resolved.
Show resolved Hide resolved
systems_applicable: true,
...payload,
},
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def privacy_experience_list(
has_notices: Optional[bool] = None,
has_config: Optional[bool] = None,
fides_user_device_id: Optional[str] = None,
systems_applicable: Optional[bool] = False,
request: Request, # required for rate limiting
response: Response, # required for rate limiting
) -> AbstractPage[PrivacyExperience]:
Expand Down Expand Up @@ -130,7 +131,7 @@ def privacy_experience_list(
privacy_notices: List[
PrivacyNotice
] = privacy_experience.get_related_privacy_notices(
db, show_disabled, fides_user_provided_identity
db, show_disabled, systems_applicable, fides_user_provided_identity
)
if should_unescape:
# Unescape both the experience config and the embedded privacy notices
Expand Down
8 changes: 8 additions & 0 deletions src/fides/api/models/privacy_experience.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
)
from fides.api.models.privacy_preference import CurrentPrivacyPreference
from fides.api.models.privacy_request import ProvidedIdentity
from fides.api.models.sql_models import System # type: ignore[attr-defined]

BANNER_CONSENT_MECHANISMS: Set[ConsentMechanism] = {
ConsentMechanism.notice_only,
Expand Down Expand Up @@ -243,6 +244,7 @@ def get_related_privacy_notices(
self,
db: Session,
show_disabled: Optional[bool] = True,
systems_applicable: Optional[bool] = False,
fides_user_provided_identity: Optional[ProvidedIdentity] = None,
) -> List[PrivacyNotice]:
"""Return privacy notices that overlap on at least one region
Expand All @@ -260,6 +262,12 @@ def get_related_privacy_notices(
PrivacyNotice.disabled.is_(False)
)

if systems_applicable:
data_uses: set[str] = System.get_data_uses(
System.all(db), include_parents=True
)
privacy_notice_query = privacy_notice_query.filter(PrivacyNotice.data_uses.overlap(data_uses)) # type: ignore

if not fides_user_provided_identity:
return privacy_notice_query.order_by(PrivacyNotice.created_at.desc()).all()

Expand Down
56 changes: 56 additions & 0 deletions tests/ops/api/v1/endpoints/test_privacy_experience_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,62 @@ def test_filter_on_notices_and_region(
assert notices[1]["id"] == privacy_notice.id
assert notices[1]["displayed_in_privacy_center"]

@pytest.mark.usefixtures(
"privacy_notice_us_co_provide_service_operations", # not displayed in overlay or privacy center
"privacy_notice_eu_cy_provide_service_frontend_only", # doesn't overlap with any regions,
"privacy_experience_overlay", # us_ca
"privacy_notice_eu_fr_provide_service_frontend_only", # eu_fr
"privacy_notice_us_ca_provide", # us_ca
"privacy_experience_privacy_center",
)
def test_filter_on_systems_applicable(
self,
api_client: TestClient,
url,
privacy_experience_privacy_center,
privacy_notice,
system,
privacy_notice_us_co_third_party_sharing,
):
"""For systems applicable filter, notices are only embedded if they are relevant to a system"""
resp = api_client.get(
url + "?region=us_co",
)
assert resp.status_code == 200
data = resp.json()

assert data["total"] == 1
assert len(data["items"]) == 1

notices = data["items"][0]["privacy_notices"]
assert len(notices) == 2
assert notices[0]["regions"] == ["us_co"]
assert notices[0]["id"] == privacy_notice_us_co_third_party_sharing.id
assert notices[0]["displayed_in_privacy_center"]
assert notices[0]["data_uses"] == ["third_party_sharing"]

assert notices[1]["regions"] == ["us_ca", "us_co"]
assert notices[1]["id"] == privacy_notice.id
assert notices[1]["displayed_in_privacy_center"]
assert notices[1]["data_uses"] == [
"marketing.advertising",
"third_party_sharing",
]

resp = api_client.get(
url + "?region=us_co&systems_applicable=True",
)
notices = resp.json()["items"][0]["privacy_notices"]
assert len(notices) == 1
assert notices[0]["regions"] == ["us_ca", "us_co"]
assert notices[0]["id"] == privacy_notice.id
assert notices[0]["displayed_in_privacy_center"]
assert notices[0]["data_uses"] == [
"marketing.advertising",
"third_party_sharing",
]
assert system.privacy_declarations[0].data_use == "marketing.advertising"

@pytest.mark.usefixtures(
"privacy_notice_us_co_provide_service_operations", # not displayed in overlay or privacy center
"privacy_notice_eu_cy_provide_service_frontend_only", # doesn't overlap with any regions,
Expand Down
19 changes: 18 additions & 1 deletion tests/ops/models/test_privacy_experience.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ def test_update_privacy_experience(self, db, experience_config_overlay):

exp.delete(db)

def test_get_related_privacy_notices(self, db):
def test_get_related_privacy_notices(self, db, system):
"""Test PrivacyExperience.get_related_privacy_notices that are embedded in PrivacyExperience request"""
privacy_experience = PrivacyExperience.create(
db=db,
Expand Down Expand Up @@ -369,6 +369,23 @@ def test_get_related_privacy_notices(self, db):
== []
)

# Privacy notice is applicable to a system - they share a data use
assert privacy_experience.get_related_privacy_notices(
db, systems_applicable=True
) == [privacy_notice]

system.privacy_declarations[0].delete(db)
db.refresh(system)

# Privacy notice is no longer applicable to any systems
assert (
privacy_experience.get_related_privacy_notices(db, systems_applicable=True)
== []
)

privacy_notice.histories[0].delete(db)
privacy_notice.delete(db)

def test_get_should_show_banner(self, db):
"""Test PrivacyExperience.get_should_show_banner that is calculated at runtime"""
privacy_experience = PrivacyExperience.create(
Expand Down