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

CON-206: Responsys access and erasure #4618

Merged
merged 29 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5ef3ff3
Starting point for Responsys
galvana Feb 9, 2024
5e15784
feat: oracle initial fixtures/tests
RobertKeyser Feb 14, 2024
84d60c4
feat: override for profile_list_recipient_read
RobertKeyser Feb 15, 2024
56fe5ca
feat: override for profile_list_recipient_delete
RobertKeyser Feb 15, 2024
2994673
chore: format request overrides
RobertKeyser Feb 15, 2024
878638c
fix: remove unused fixtures
RobertKeyser Feb 15, 2024
645a5e3
fix: add newlines to end of files
RobertKeyser Feb 15, 2024
0fb87b6
feat: add oracle_responsys dataset
RobertKeyser Feb 16, 2024
716303d
refactor: remove trailing underscore from field names
RobertKeyser Feb 16, 2024
f17959d
refactor: rename overrides for consistency
RobertKeyser Feb 16, 2024
b920a9a
fix: authentication token location
RobertKeyser Feb 16, 2024
2858a4b
feat: working access request
RobertKeyser Feb 23, 2024
5d5f760
feat: working oracle responsys erasure request
RobertKeyser Feb 23, 2024
13a2934
feat: finish tests for Oracle Responsys
RobertKeyser Feb 26, 2024
d44c054
Merge branch 'main' into CON-206-responsys-access-and-erasure
RobertKeyser Feb 26, 2024
de6fbef
feat: add random phone number generator
RobertKeyser Feb 27, 2024
e2de147
feat: finish oracle responsys support for phone numbers
RobertKeyser Feb 27, 2024
3f7208a
fix: add newline to end of file
RobertKeyser Feb 27, 2024
866f10a
fix: remove unused import
RobertKeyser Feb 27, 2024
06f559d
fix: mark tests as skip
RobertKeyser Feb 27, 2024
e7df930
docs: update CHANGELOG.md for Oracle Responsys connector
RobertKeyser Feb 27, 2024
4f99d18
Removing fill on default connector icon
galvana Feb 27, 2024
db69147
feat: add Oracle Responsys logo
RobertKeyser Feb 29, 2024
2f445d3
refactor: replace conditional logic for checking for initial + in pho…
RobertKeyser Feb 29, 2024
0f93bbf
Merge branch 'main' into CON-206-responsys-access-and-erasure
RobertKeyser Feb 29, 2024
94a3397
Updating icon
galvana Mar 1, 2024
1cc20cd
Merge branch 'main' into CON-206-responsys-access-and-erasure
galvana Mar 1, 2024
0d9d486
Updating change log
galvana Mar 1, 2024
d2bbe0f
Re-adding newline
galvana Mar 1, 2024
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 @@ -19,6 +19,7 @@ The types of changes are:

### Added
- Add Great Britain as a consent option [#4628](https://github.com/ethyca/fides/pull/4628)
- Access and Erasure support for Oracle Responsys [#4618](https://github.com/ethyca/fides/pull/4628)

## [2.30.1](https://github.com/ethyca/fides/compare/2.30.0...2.30.1)

Expand Down
77 changes: 77 additions & 0 deletions data/saas/config/oracle_responsys_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
saas_config:
fides_key: <instance_fides_key>
name: Oracle Responsys
type: oracle_responsys
description: A sample schema representing the Oracle Responsys connector for Fides
version: 0.0.1

connector_params:
- name: domain
description: Your Oracle Responsys domain
- name: username
description: Your Oracle Responsys username
- name: password
description: Your Oracle Responsys password
sensitive: True

client_config:
protocol: https
host: <domain>
authentication:
strategy: oracle_responsys
configuration:
username: <username>
password: <password>

test_request:
method: GET
path: /rest/api/v1.3/lists

endpoints:
- name: profile_list
requests:
read:
- method: GET
path: /rest/api/v1.3/lists
param_values:
- name: placeholder_email
identity: email
- method: GET
path: /rest/api/v1.3/lists
param_values:
- name: placeholder_phone
identity: phone_number
- name: profile_list_recipient
requests:
read:
- request_override: oracle_responsys_profile_list_recipients_read
param_values:
- name: profile_list_id
references:
- dataset: <instance_fides_key>
field: profile_list.name
direction: from
- name: email
identity: email
- request_override: oracle_responsys_profile_list_recipients_read
param_values:
- name: profile_list_id
references:
- dataset: <instance_fides_key>
field: profile_list.name
direction: from
- name: phone_number
identity: phone_number
delete:
request_override: oracle_responsys_profile_list_recipients_delete
param_values:
- name: profile_list_id
references:
- dataset: <instance_fides_key>
field: profile_list_recipient.profile_list_id
direction: from
- name: responsys_id
references:
- dataset: <instance_fides_key>
field: profile_list_recipient.riid
direction: from
112 changes: 112 additions & 0 deletions data/saas/dataset/oracle_responsys_dataset.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
dataset:
- fides_key: <instance_fides_key>
name: oracle_responsys
description: A sample dataset representing the Oracle Responsys connector for Fides
collections:
- name: profile_list_recipient
fields:
- name: profile_list_id
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: riid
data_categories: [user.unique_id]
fidesops_meta:
primary_key: True
data_type: string
- name: customer_id
data_categories: [user.unique_id]
fidesops_meta:
data_type: string
- name: email_address
data_categories: [user.contact.email]
fidesops_meta:
data_type: string
- name: email_domain
data_categories: [user.contact.email]
fidesops_meta:
data_type: string
- name: email_isp
data_categories: [user.contact.email]
fidesops_meta:
data_type: string
- name: email_format
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: email_permission_status
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: email_deliverability_status
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: email_permission_reason
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: email_md5_hash
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: email_sha256_hash
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: mobile_number
data_categories: [user.contact.phone_number]
fidesops_meta:
data_type: string
- name: mobile_country
data_categories: [user.contact.address.country]
fidesops_meta:
data_type: string
- name: mobile_permission_status
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: mobile_permission_reason
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: mobile_deliverability_status
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: postal_street_1
data_categories: [user.contact.address.street]
fidesops_meta:
data_type: string
- name: postal_street_2
data_categories: [user.contact.address.street]
fidesops_meta:
data_type: string
- name: city
data_categories: [user.contact.address.city]
fidesops_meta:
data_type: string
- name: state
data_categories: [user.contact.address.state]
fidesops_meta:
data_type: string
- name: postal_code
data_categories: [user.contact.address.postal_code]
fidesops_meta:
data_type: string
- name: country
data_categories: [user.contact.address.country]
fidesops_meta:
data_type: string
- name: postal_permission_status
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: postal_permission_reason
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: postal_deliverability_status
data_categories: [system.operations]
fidesops_meta:
data_type: string
2 changes: 1 addition & 1 deletion data/saas/icon/default.svg
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This connector doesn't specify an icon so it falls back to the default ethyca.svg. This is how I noticed that the default icon has an unwanted fill. Just updating this so the entry looks good in the connector dropdown.
image
With that said, do you want to add an icon for Oracle Responsys?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an SVG, but I couldn't change the viewbox to 0 0 32 32 without clipping some of the SVG. @galvana, you have a way to do this, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know about 'right' but typically we work with the icon in the figma page until we get it the way we like, then we can save it out to submit in the PR. We can walk through this pretty easy, but I can't lead just yet as I have requested access but don't have it yet =/

I found this icon that has both Oracle and Responsys in it but since I can't get into figma to verify if it will be easy to use or not =(
responsys_logo_cropped

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from typing import Dict, Optional, cast

from requests import PreparedRequest, post

from fides.api.common_exceptions import FidesopsException
from fides.api.models.connectionconfig import ConnectionConfig
from fides.api.schemas.saas.strategy_configuration import StrategyConfiguration
from fides.api.service.authentication.authentication_strategy import (
AuthenticationStrategy,
)
from fides.api.util.saas_util import assign_placeholders


class OracleResponsysAuthenticationConfiguration(StrategyConfiguration):
"""
Parameters to authorize a Oracle Responsys connection
"""

username: str
password: str


class OracleResponsysAuthenticationStrategy(AuthenticationStrategy):
"""
Generates a token from the provided key and secret.
"""

name = "oracle_responsys"
configuration_model = OracleResponsysAuthenticationConfiguration

def __init__(self, configuration: OracleResponsysAuthenticationConfiguration):
self.username = configuration.username
self.password = configuration.password

Check warning on line 33 in src/fides/api/service/saas_request/override_implementations/authentication_strategy_oracle_responsys.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/authentication_strategy_oracle_responsys.py#L32-L33

Added lines #L32 - L33 were not covered by tests

def add_authentication(
self, request: PreparedRequest, connection_config: ConnectionConfig
) -> PreparedRequest:
"""
Retrieves a token using the provided username and password
"""

secrets = cast(Dict, connection_config.secrets)
domain: Optional[str] = secrets.get("domain")
username: Optional[str] = assign_placeholders(self.username, secrets)
password: Optional[str] = assign_placeholders(self.password, secrets)

Check warning on line 45 in src/fides/api/service/saas_request/override_implementations/authentication_strategy_oracle_responsys.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/authentication_strategy_oracle_responsys.py#L42-L45

Added lines #L42 - L45 were not covered by tests

response = post(

Check warning on line 47 in src/fides/api/service/saas_request/override_implementations/authentication_strategy_oracle_responsys.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/authentication_strategy_oracle_responsys.py#L47

Added line #L47 was not covered by tests
url=f"https://{domain}/rest/api/v1.3/auth/token",
headers={"Content-Type": "application/x-www-form-urlencoded"},
data={"user_name": username, "password": password, "auth_type": "password"},
)

if response.ok:
json_response = response.json()
token = json_response.get("authToken")

Check warning on line 55 in src/fides/api/service/saas_request/override_implementations/authentication_strategy_oracle_responsys.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/authentication_strategy_oracle_responsys.py#L54-L55

Added lines #L54 - L55 were not covered by tests
else:
raise FidesopsException(f"Unable to get token {response.json()}")

Check warning on line 57 in src/fides/api/service/saas_request/override_implementations/authentication_strategy_oracle_responsys.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/authentication_strategy_oracle_responsys.py#L57

Added line #L57 was not covered by tests

request.headers["Authorization"] = token
return request

Check warning on line 60 in src/fides/api/service/saas_request/override_implementations/authentication_strategy_oracle_responsys.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/authentication_strategy_oracle_responsys.py#L59-L60

Added lines #L59 - L60 were not covered by tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
from typing import Any, Dict, List

import json
import pydash

from fides.api.common_exceptions import FidesopsException
from fides.api.graph.traversal import TraversalNode
from fides.api.models.policy import Policy
from fides.api.models.privacy_request import PrivacyRequest
from fides.api.schemas.saas.shared_schemas import HTTPMethod, SaaSRequestParams
from fides.api.service.connectors.saas.authenticated_client import AuthenticatedClient
from fides.api.service.saas_request.saas_request_override_factory import (
SaaSRequestType,
register,
)
from fides.api.util.collection_util import Row
from fides.api.util.saas_util import get_identity


@register("oracle_responsys_profile_list_recipients_read", [SaaSRequestType.READ])
def oracle_responsys_profile_list_recipients_read(
client: AuthenticatedClient,
node: TraversalNode,
policy: Policy,
privacy_request: PrivacyRequest,
input_data: Dict[str, List[Any]],
secrets: Dict[str, Any],
) -> List[Row]:
"""
Retrieve data from each profile list.

The members endpoint returns data in two separate arrays: one for the keys and one for the values for each result.
{
"recordData": {
"fieldNames": [
<list of field names>
],
"records": [
[
<list of field values, corresponding to the fieldNames>
]
]
}
"""
list_ids = input_data.get("profile_list_id", [])
results = []

Check warning on line 46 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L45-L46

Added lines #L45 - L46 were not covered by tests

identity = get_identity(privacy_request)

Check warning on line 48 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L48

Added line #L48 was not covered by tests
if identity == "email":
query_ids = input_data.get("email", [])
query_attribute = "e"

Check warning on line 51 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L50-L51

Added lines #L50 - L51 were not covered by tests
elif identity == "phone_number":
query_ids = input_data.get("phone_number", [])

Check warning on line 53 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L53

Added line #L53 was not covered by tests
# Oracle Responsys doesn't support the initial + in phone numbers
for idx, query_id in enumerate(query_ids):
query_ids[idx] = query_id[1:] if query_id[0] == "+" else query_id
query_attribute = "m"

Check warning on line 57 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L56-L57

Added lines #L56 - L57 were not covered by tests
else:
raise FidesopsException(

Check warning on line 59 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L59

Added line #L59 was not covered by tests
"Unsupported identity type for Oracle Responsys connector. Currently only `email` and `phone_number` are supported"
)

body = {"fieldList": ["all"], "ids": query_ids, "queryAttribute": query_attribute}

Check warning on line 63 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L63

Added line #L63 was not covered by tests
for list_id in list_ids:
members_response = client.send(

Check warning on line 65 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L65

Added line #L65 was not covered by tests
SaaSRequestParams(
method=HTTPMethod.POST,
path=f"/rest/api/v1.3/lists/{list_id}/members",
query_params={"action": "get"},
body=json.dumps(body),
headers={"Content-Type": "application/json"},
),
[404], # Returns a 404 if no list member is found
)
response_data = pydash.get(members_response.json(), "recordData")

Check warning on line 75 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L75

Added line #L75 was not covered by tests
if response_data:
normalized_field_names = [
field.lower().rstrip("_") for field in response_data["fieldNames"]
]
serialized_data = [
dict(zip(normalized_field_names, records))
for records in response_data["records"]
]

for record in serialized_data:
# Filter out the keys with falsy values and append it
filtered_records = {
key: value for key, value in record.items() if value
}
filtered_records["profile_list_id"] = list_id
results.append(filtered_records)

Check warning on line 91 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L90-L91

Added lines #L90 - L91 were not covered by tests
Comment on lines +75 to +91
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be a good idea to break out the response processing into its own function so that we can test out some different scenarios. It's also a good way for someone to understand what's happening here if some sample test cases are provided.


return results

Check warning on line 93 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L93

Added line #L93 was not covered by tests


@register("oracle_responsys_profile_list_recipients_delete", [SaaSRequestType.DELETE])
def oracle_responsys_profile_list_recipients_delete(
client: AuthenticatedClient,
param_values_per_row: List[Dict[str, Any]],
policy: Policy,
privacy_request: PrivacyRequest,
secrets: Dict[str, Any],
) -> int:
"""
Deletes data from each profile list. Upon deletion from the list, PET data is also deleted.
"""
rows_deleted = 0

Check warning on line 107 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L107

Added line #L107 was not covered by tests
# each delete_params dict correspond to a record that needs to be deleted
for row_param_values in param_values_per_row:
# get params to be used in delete request
all_object_fields = row_param_values["all_object_fields"]

Check warning on line 111 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L111

Added line #L111 was not covered by tests

list_id = all_object_fields["profile_list_id"]
responsys_id = row_param_values.get("responsys_id")

Check warning on line 114 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L113-L114

Added lines #L113 - L114 were not covered by tests

if responsys_id:
client.send(

Check warning on line 117 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L117

Added line #L117 was not covered by tests
SaaSRequestParams(
method=HTTPMethod.DELETE,
path=f"/rest/api/v1.3/lists/{list_id}/members/{responsys_id}",
)
)

rows_deleted += 1
return rows_deleted

Check warning on line 125 in src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py

View check run for this annotation

Codecov / codecov/patch

src/fides/api/service/saas_request/override_implementations/oracle_responsys_request_overrides.py#L124-L125

Added lines #L124 - L125 were not covered by tests
Loading
Loading