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

Changes to support consent signals #5190

Merged
merged 4 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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
Loading