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] Save and Retrieve Privacy Preferences by Fides User Device Id #3132

Merged
merged 8 commits into from
Apr 25, 2023
16 changes: 16 additions & 0 deletions .fides/db_dataset.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ dataset:
data_categories:
- system.operations
data_qualifier: aggregated.anonymized.unlinked_pseudonymized.pseudonymized.identified
- name: fides_user_device_provided_identity_id
data_categories:
- system.operations
data_qualifier: aggregated.anonymized.unlinked_pseudonymized.pseudonymized.identified
- name: id
data_categories:
- system.operations
Expand Down Expand Up @@ -1879,6 +1883,18 @@ dataset:
data_categories:
- user
data_qualifier: aggregated.anonymized.unlinked_pseudonymized.pseudonymized.identified
- name: fides_user_device
data_categories:
- system.operations
data_qualifier: aggregated.anonymized.unlinked_pseudonymized.pseudonymized.identified
- name: fides_user_device_provided_identity_id
data_categories:
- system.operations
data_qualifier: aggregated.anonymized.unlinked_pseudonymized.pseudonymized.identified
- name: hashed_fides_user_device
data_categories:
- system.operations
data_qualifier: aggregated.anonymized.unlinked_pseudonymized.pseudonymized.identified
- name: id
data_categories:
- system.operations
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ The types of changes are:
- Side navigation bar can now also have children navigation links [#3099](https://github.com/ethyca/fides/pull/3099)
- Endpoints for consent reporting [#3095](https://github.com/ethyca/fides/pull/3095)
- Custom fields table [#3097](https://github.com/ethyca/fides/pull/3097)
- Endpoints to save the new-style Privacy Preferences with respect to a fides user device id [#3132](https://github.com/ethyca/fides/pull/3132)

### Changed

Expand Down
66 changes: 66 additions & 0 deletions docs/fides/docs/development/postman/Fides.postman_collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -5486,6 +5486,67 @@
},
"response": []
},
{
"name": "Save Privacy Preferences for Device Id",
"request": {
"method": "PATCH",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"browser_identity\": {\n \"ga_client_id\": \"UA-XXXXXXXXX\",\n \"ljt_readerID\": \"test_sovrn_id\",\n \"fides_user_device_id\": \"{{fides_user_device_id}}\"\n },\n \"preferences\": [{\n \"privacy_notice_history_id\": \"{{privacy_notice_history_id}}\",\n \"preference\": \"opt_out\"\n }],\n \"request_origin\": \"privacy_center\",\n \"url_recorded\": \"example.com/privacy_center\",\n \"user_agent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/324.42 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/425.24\",\n \"user_geography\": \"us_ca\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{host}}/privacy-preferences",
"host": [
"{{host}}"
],
"path": [
"privacy-preferences"
]
}
},
"response": []
},
{
"name": "Privacy Preferences by Fides User Device Id",
"protocolProfileBehavior": {
"disableBodyPruning": true
},
"request": {
"method": "GET",
"header": [],
"body": {
"mode": "raw",
"raw": "",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{host}}/privacy-preferences?fides_user_device_id={{fides_user_device_id}}",
"host": [
"{{host}}"
],
"path": [
"privacy-preferences"
],
"query": [
{
"key": "fides_user_device_id",
"value": "{{fides_user_device_id}}"
}
]
}
},
"response": []
},
{
"name": "Get Historical Preferences",
"request": {
Expand Down Expand Up @@ -5872,6 +5933,11 @@
"key": "oauth_connector_key",
"value": "",
"type": "string"
},
{
"key": "fides_user_device_id",
"value": "",
"type": "string"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"""
from alembic import op


# revision identifiers, used by Alembic.
revision = "3842d1acac5f"
down_revision = "8342453518cc"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""save privacy preferences on device id

Revision ID: 451684a726a5
Revises: 3842d1acac5f
Create Date: 2023-04-23 16:06:19.788074

"""
import sqlalchemy as sa
import sqlalchemy_utils
from alembic import op

# revision identifiers, used by Alembic.
revision = "451684a726a5"
down_revision = "3842d1acac5f"
branch_labels = None
depends_on = None


def upgrade():
op.add_column(
"currentprivacypreference",
sa.Column("fides_user_device_provided_identity_id", sa.String(), nullable=True),
)
op.create_unique_constraint(
"fides_user_device_identity_privacy_notice",
"currentprivacypreference",
["fides_user_device_provided_identity_id", "privacy_notice_id"],
)
op.create_index(
op.f("ix_currentprivacypreference_fides_user_device_provided_identity_id"),
"currentprivacypreference",
["fides_user_device_provided_identity_id"],
unique=False,
)
op.create_foreign_key(
"currentprivacypreference_fides_user_device_provided_identi_fkey",
"currentprivacypreference",
"providedidentity",
["fides_user_device_provided_identity_id"],
["id"],
)
op.add_column(
"privacypreferencehistory",
sa.Column(
"fides_user_device",
sqlalchemy_utils.types.encrypted.encrypted_type.StringEncryptedType(),
nullable=True,
),
)
op.add_column(
"privacypreferencehistory",
sa.Column("fides_user_device_provided_identity_id", sa.String(), nullable=True),
)
op.add_column(
"privacypreferencehistory",
sa.Column("hashed_fides_user_device", sa.String(), nullable=True),
)
op.create_index(
op.f("ix_privacypreferencehistory_fides_user_device_provided_identity_id"),
"privacypreferencehistory",
["fides_user_device_provided_identity_id"],
unique=False,
)
op.create_index(
op.f("ix_privacypreferencehistory_hashed_fides_user_device"),
"privacypreferencehistory",
["hashed_fides_user_device"],
unique=False,
)
op.create_foreign_key(
"privacypreferencehistory_fides_user_device_provided_identi_fkey",
"privacypreferencehistory",
"providedidentity",
["fides_user_device_provided_identity_id"],
["id"],
)


def downgrade():
op.drop_constraint(
"privacypreferencehistory_fides_user_device_provided_identi_fkey",
"privacypreferencehistory",
type_="foreignkey",
)
op.drop_index(
op.f("ix_privacypreferencehistory_hashed_fides_user_device"),
table_name="privacypreferencehistory",
)
op.drop_index(
op.f("ix_privacypreferencehistory_fides_user_device_provided_identity_id"),
table_name="privacypreferencehistory",
)
op.drop_column("privacypreferencehistory", "hashed_fides_user_device")
op.drop_column("privacypreferencehistory", "fides_user_device_provided_identity_id")
op.drop_column("privacypreferencehistory", "fides_user_device")
op.drop_constraint(
"currentprivacypreference_fides_user_device_provided_identi_fkey",
"currentprivacypreference",
type_="foreignkey",
)
op.drop_index(
op.f("ix_currentprivacypreference_fides_user_device_provided_identity_id"),
table_name="currentprivacypreference",
)
op.drop_constraint(
"fides_user_device_identity_privacy_notice",
"currentprivacypreference",
type_="unique",
)
op.drop_column("currentprivacypreference", "fides_user_device_provided_identity_id")
57 changes: 57 additions & 0 deletions src/fides/api/ops/api/v1/endpoints/consent_request_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,63 @@ def set_consent_preferences(
return consent_preferences


def _get_fides_user_device_id_provided_identity(
db: Session, fides_user_device_id: Optional[str]
) -> Optional[ProvidedIdentity]:
"""Look up a fides user device id that is not attached to a privacy request if it exists

There can be many fides user device ids attached to privacy requests, but we should try to keep them
unique for consent requests.
"""
if not fides_user_device_id:
return None

return ProvidedIdentity.filter(
db=db,
conditions=(
(ProvidedIdentity.field_name == ProvidedIdentityType.fides_user_device_id)
& (
ProvidedIdentity.hashed_value
== ProvidedIdentity.hash_value(fides_user_device_id)
)
& (ProvidedIdentity.privacy_request_id.is_(None))
),
).first()


def _get_or_create_fides_user_device_id_provided_identity(
db: Session,
identity_data: Identity,
) -> ProvidedIdentity:
"""Gets an existing fides user device id provided identity or creates one if it doesn't exist.
Raises an error if no fides user device id is supplied.
"""
if not identity_data.fides_user_device_id:
raise HTTPException(
HTTP_422_UNPROCESSABLE_ENTITY,
detail="Fides user device id not found in identity data",
)

identity = _get_fides_user_device_id_provided_identity(
db, identity_data.fides_user_device_id
)

if not identity:
identity = ProvidedIdentity.create(
db,
data={
"privacy_request_id": None,
"field_name": ProvidedIdentityType.fides_user_device_id.value,
"hashed_value": ProvidedIdentity.hash_value(
identity_data.fides_user_device_id
),
"encrypted_value": {"value": identity_data.fides_user_device_id},
},
)

return identity # type: ignore[return-value]


def _get_or_create_provided_identity(
db: Session,
identity_data: Identity,
Expand Down
Loading