Skip to content

Commit

Permalink
Changes to support consent signals (#5190)
Browse files Browse the repository at this point in the history
Co-authored-by: eastandwestwind <[email protected]>
  • Loading branch information
galvana and eastandwestwind authored Aug 14, 2024
1 parent 71ae68c commit 1d6aa2c
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The types of changes are:
### Changed
- Removed PRIVACY_REQUEST_READ scope from Viewer role [#5184](https://github.com/ethyca/fides/pull/5184)
- Asynchronously load GVL translations in FidesJS instead of blocking UI rendering [#5187](https://github.com/ethyca/fides/pull/5187)
- Model changes to support consent signals (Fidesplus) [#5190](https://github.com/ethyca/fides/pull/5190)


### Developer Experience
Expand Down
2 changes: 1 addition & 1 deletion src/fides/api/models/connectionconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ class ConnectionConfig(Base):
system = relationship(System, back_populates="connection_configs", uselist=False)

consent_automation: RelationshipProperty[Optional[ConsentAutomation]] = (
relationship(ConsentAutomation, cascade="all, delete-orphan")
relationship(ConsentAutomation, uselist=False, cascade="all, delete-orphan")
)

# Identifies the privacy actions needed from this connection by the associated system.
Expand Down
19 changes: 17 additions & 2 deletions src/fides/api/schemas/consentable_item.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from typing import List, Optional
from typing import Any, Dict, List, Optional

from pydantic import Field
from pydantic import BaseModel, Field

from fides.api.models.consent_automation import ConsentableItem as ConsentableItemModel
from fides.api.models.privacy_notice import UserConsentPreference
from fides.api.schemas.base_class import FidesSchema


Expand Down Expand Up @@ -75,3 +76,17 @@ def build_consent_item_hierarchy(
for item in consentable_items
if item.parent_id is None
]


class ConsentWebhookResult(BaseModel):
"""
A wrapper class for the identity map and notice map values returned from a `PROCESS_CONSENT_WEBHOOK` function.
"""

identity_map: Dict[str, Any] = {}
notice_map: Dict[str, UserConsentPreference] = {}

@property
def success(self) -> bool:
"""Returns true if both the identity map and notice map are not empty."""
return bool(self.identity_map) and bool(self.notice_map)
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
InvalidSaaSRequestOverrideException,
NoSuchSaaSRequestOverrideException,
)
from fides.api.schemas.consentable_item import ConsentableItem
from fides.api.schemas.consentable_item import ConsentableItem, ConsentWebhookResult
from fides.api.util.collection_util import Row


Expand All @@ -31,7 +31,7 @@ class SaaSRequestType(Enum):


RequestOverrideFunction = Callable[
..., Union[List[Row], List[ConsentableItem], int, bool, None]
..., Union[ConsentWebhookResult, List[Row], List[ConsentableItem], int, bool, None]
]


Expand Down Expand Up @@ -244,7 +244,15 @@ def validate_update_consent_function(f: Callable) -> None:


def validate_process_consent_webhook_function(f: Callable) -> None:
pass
sig: Signature = signature(f)
if sig.return_annotation is not ConsentWebhookResult:
raise InvalidSaaSRequestOverrideException(
"Provided SaaS process consent webhook function must return a ConsentWebhookResult"
)
if len(sig.parameters) < 4:
raise InvalidSaaSRequestOverrideException(
"Provided SaaS process consent webhook function must declare at least 4 parameters"
)


# TODO: Avoid running this on import?
Expand Down
9 changes: 8 additions & 1 deletion tests/ops/models/test_consent_automation.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from sqlalchemy import and_
from sqlalchemy.orm import Session

from fides.api.models.connectionconfig import ConnectionConfig
from fides.api.models.consent_automation import ConsentableItem, ConsentAutomation


class TestConsentAutomation:

def test_create_consent_automation(self, db: Session, connection_config):
def test_create_consent_automation(
self, db: Session, connection_config: ConnectionConfig
):
consentable_items = [
{
"type": "Channel",
Expand Down Expand Up @@ -36,6 +39,10 @@ def test_create_consent_automation(self, db: Session, connection_config):
assert consent_automation.connection_config_id == connection_config.id
assert len(consent_automation.consentable_items) == 2

# test link from connection_config
db.refresh(connection_config)
assert len(connection_config.consent_automation.consentable_items) == 2

def test_update_consent_automation_add_consentable_items(
self, db: Session, connection_config, privacy_notice
):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from fides.api.models.policy import Policy
from fides.api.models.privacy_notice import UserConsentPreference
from fides.api.models.privacy_request import PrivacyRequest
from fides.api.schemas.consentable_item import ConsentWebhookResult
from fides.api.service.connectors.saas.authenticated_client import AuthenticatedClient
from fides.api.service.saas_request.saas_request_override_factory import (
SaaSRequestOverrideFactory,
Expand Down Expand Up @@ -101,6 +102,19 @@ def valid_consent_update_override(
return True


def valid_process_consent_webhook_override(
client: AuthenticatedClient,
secrets: Dict[str, Any],
payload: Any,
notice_id_to_preference_map: Dict[str, UserConsentPreference],
consentable_items: List[ConsentableItem],
) -> ConsentWebhookResult:
"""
A sample override function for process consent webhook requests with a valid function signature
"""
return ConsentWebhookResult()


@pytest.mark.unit_saas
class TestSaasRequestOverrideFactory:
"""
Expand Down Expand Up @@ -213,6 +227,26 @@ def test_register_update_consent_override(self):
SaaSRequestOverrideFactory.get_override(f_id, SaaSRequestType.READ)
assert f"Custom SaaS override '{f_id}' does not exist." in str(exc.value)

def test_register_process_consent_webhook_override(self):
"""
Test registering a valid `process_consent_webhook` override function
"""

f_id = uuid()
register(f_id, SaaSRequestType.PROCESS_CONSENT_WEBHOOK)(
valid_process_consent_webhook_override
)
assert (
valid_process_consent_webhook_override
== SaaSRequestOverrideFactory.get_override(
f_id, SaaSRequestType.PROCESS_CONSENT_WEBHOOK
)
)

with pytest.raises(NoSuchSaaSRequestOverrideException) as exc:
SaaSRequestOverrideFactory.get_override(f_id, SaaSRequestType.READ)
assert f"Custom SaaS override '{f_id}' does not exist." in str(exc.value)

def test_reregister_override(self):
"""
Test that registering a new override with the same ID and same request type
Expand Down

0 comments on commit 1d6aa2c

Please sign in to comment.