This repository has been archived by the owner on Nov 30, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 16
Email Connector: Send Email with Erasure Instructions [#1158] #1246
Merged
eastandwestwind
merged 14 commits into
main
from
fidesops_1158_email_connector_email_send
Sep 7, 2022
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
77a3d3d
Send an email for each email-based dataset at the end of privacy requ…
pattisdr e981ea4
Add more "checkpoints" to privacy request execution - these are locat…
pattisdr 1601293
Don't send an email if the connection config is read only or there ar…
pattisdr f539a01
Don't assume there's a collection when building "resume" details. A f…
pattisdr e1dc1fb
Add a first draft of docs for setting up an email connector.
pattisdr c09b4e7
Moves the email connector send method to the email connector file.
pattisdr c8afe55
Update mock location.
pattisdr b10f6de
Merge remote-tracking branch 'ethyca/main' into fidesops_1158_email_c…
pattisdr d9e169a
Bump downrev.
pattisdr 10ba182
update email connector guides
1e855d0
correct link, broken sentence
d70d71a
Create a new EmailRequestFulfillmentBodyParams type to be used once t…
pattisdr 9a20d02
Fix missed test.
pattisdr 02eb668
Merge remote-tracking branch 'ethyca/main' into fidesops_1158_email_c…
pattisdr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
# Configure Email Communications | ||
## What is email used for? | ||
# Configure Automatic Emails | ||
## What is a fidesops Email Connection? | ||
|
||
Fidesops supports email server configurations for sending processing notices to privacy request subjects. Future updates will support outbound email communications with data processors. | ||
Fidesops supports configuring third party email servers to handle outbound communications. | ||
|
||
Supported modes of use: | ||
|
||
- Subject Identity Verification - for more information on identity verification in subject requests, see the [Privacy Requests](privacy_requests.md#subject-identity-verification) guide. | ||
|
||
- Subject Identity Verification - sends a verification code to the user's email address prior to processing a subject request. for more information on identity verification, see the [Privacy Requests](privacy_requests.md#subject-identity-verification) guide. | ||
- Erasure Request Email Fulfillment - sends an email to configured third parties to process erasures for a given data subject. See [creating email Connectors](#email-third-party-services) for more information. | ||
|
||
## Prerequisites | ||
|
||
|
@@ -16,12 +16,12 @@ Fidesops currently supports Mailgun for email integrations. Ensure you register | |
|
||
Follow the [Mailgun documentation](https://documentation.mailgun.com/en/latest/api-intro.html#authentication-1) to create a new Domain Sending Key for fidesops. | ||
|
||
!!! Note | ||
Mailgun automatically generates a **primary account API key** when you sign up for an account. This key allows you to perform all CRUD operations via Mailgun's API endpoints, and for any of your sending domains. For security purposes, using a new **domain sending key** is recommended over your primary API key. | ||
!!! Note | ||
Mailgun automatically generates a **primary account API key** when you sign up for an account. This key allows you to perform all CRUD operations via Mailgun's API endpoints, and for any of your sending domains. For security purposes, using a new **domain sending key** is recommended over your primary API key. | ||
|
||
## Configuration | ||
|
||
### Create the email configuration | ||
### Create the email config | ||
|
||
```json title="<code>POST api/v1/email/config" | ||
{ | ||
|
@@ -47,7 +47,7 @@ Fidesops currently supports Mailgun for email integrations. Ensure you register | |
|
||
### Add the email configuration secrets | ||
|
||
```json title="<code>POST api/v1/email/config/{{email_config_key}}/secret" | ||
```json title="<code>POST api/v1/email/config/{email_config_key}/secret" | ||
{ | ||
"mailgun_api_key": "nc123849ycnpq98fnu" | ||
} | ||
|
@@ -58,3 +58,102 @@ Fidesops currently supports Mailgun for email integrations. Ensure you register | |
|---|----| | ||
| `mailgun_api_key` | Your Mailgun Domain Sending Key. | | ||
|
||
## Email third-party services | ||
|
||
Once your email server is configured, you can create an email connector to send automatic erasure requests to third-party services. Fidesops will gather details about each collection described in the connector, and send a single email to the service after all collections have been visited. | ||
|
||
!!! Note | ||
Fidesops does not collect confirmation that the erasure was completed by the third party. | ||
|
||
|
||
### Create the connector | ||
|
||
Ensure you have created your [email configuration](#configuration) prior to creating a new email connector. | ||
|
||
```json title="<code>PATCH api/v1/connection</code>" | ||
[ | ||
{ | ||
"name": "Email Connection Config", | ||
"key": "third_party_email_connector", | ||
"connection_type": "email", | ||
"access": "write" | ||
} | ||
] | ||
``` | ||
|
||
| Field | Description | | ||
|----|----| | ||
| `key` | A unique key used to manage your email connector. This is auto-generated from `name` if left blank. Accepted values are alphanumeric, `_`, and `.`. | | ||
| `name` | A unique user-friendly name for your email connector. | | ||
| `connection_type` | Must be `email` to create a new email connector. | | ||
| `access` | Email connectors must be given `write` access in order to send an email. | | ||
|
||
|
||
### Configure notifications | ||
|
||
Once your email connector is created, configure any outbound email addresses: | ||
|
||
```json title="<code>PUT api/v1/connection/{email_connection_config_key}/secret</code>" | ||
{ | ||
"test_email": "[email protected]", | ||
"to_email": "[email protected]" | ||
} | ||
``` | ||
|
||
| Field | Description | | ||
|----|----| | ||
| `{email_connection_config_key}` | The unique key that represents the email connection to use. | | ||
| `to_email` | The user that will be notified via email to complete an erasure request. *Only one `to_email` is supported at this time.* | | ||
| `test_email` | *Optional.* An email to which you have access for verifying your setup. If your email configuration is working, you will receive an email with mock data similar to the one sent to third-party services. | | ||
|
||
### Configure the dataset | ||
|
||
Lastly, configure the collections and fields you would like to request be erased or masked. Fidesops will use these fields to compose an email to the third-party service. | ||
|
||
```json title="<code>PUT api/v1/connection/{email_connection_config_key}/dataset" | ||
[ | ||
{ | ||
"fides_key": "email_dataset", | ||
"name": "Dataset not accessible automatically", | ||
"description": "Third party data - will email to request erasure", | ||
"collections": [ | ||
{ | ||
"name": "daycare_customer", | ||
"fields": [ | ||
{ | ||
"name": "id", | ||
"data_categories": [ | ||
"system.operations" | ||
], | ||
"fidesops_meta": { | ||
"primary_key": true | ||
} | ||
}, | ||
{ | ||
"name": "child_health_concerns", | ||
"data_categories": [ | ||
"user.biometric_health" | ||
] | ||
}, | ||
{ | ||
"name": "user_email", | ||
"data_categories": [ | ||
"user.contact.email" | ||
], | ||
"fidesops_meta": { | ||
"identity": "email" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
} | ||
] | ||
``` | ||
|
||
| Field | Description | | ||
|----|----| | ||
| `fides_key` | A unique key used to manage your email dataset. This is auto-generated from `name` if left blank. Accepted values are alphanumeric, `_`, and `.`. | | ||
| `name` | A unique user-friendly name for your email dataset. | | ||
| `description` | Any additional information used to describe this email dataset. | | ||
| `collections` | Any collections and associated fields belonging to the third party service, similar to a configured fidesops [Dataset](datasets.md). If you do not know the exact data structure of a third party's database, you can configure a single collection with the fields you would like masked. **Note:** A primary key must be specified on each collection. | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -79,7 +79,7 @@ | |
from fidesops.ops.schemas.privacy_request import ( | ||
BulkPostPrivacyRequests, | ||
BulkReviewResponse, | ||
CollectionActionRequired, | ||
CheckpointActionRequired, | ||
DenyPrivacyRequests, | ||
ExecutionLogDetailResponse, | ||
PrivacyRequestCreate, | ||
|
@@ -492,33 +492,33 @@ def attach_resume_instructions(privacy_request: PrivacyRequest) -> None: | |
about how to resume manually if applicable. | ||
""" | ||
resume_endpoint: Optional[str] = None | ||
stopped_collection_details: Optional[CollectionActionRequired] = None | ||
action_required_details: Optional[CheckpointActionRequired] = None | ||
|
||
if privacy_request.status == PrivacyRequestStatus.paused: | ||
stopped_collection_details = privacy_request.get_paused_collection_details() | ||
action_required_details = privacy_request.get_paused_collection_details() | ||
|
||
if stopped_collection_details: | ||
if action_required_details: | ||
# Graph is paused on a specific collection | ||
resume_endpoint = ( | ||
PRIVACY_REQUEST_MANUAL_ERASURE | ||
if stopped_collection_details.step == CurrentStep.erasure | ||
if action_required_details.step == CurrentStep.erasure | ||
else PRIVACY_REQUEST_MANUAL_INPUT | ||
) | ||
else: | ||
# Graph is paused on a pre-processing webhook | ||
resume_endpoint = PRIVACY_REQUEST_RESUME | ||
|
||
elif privacy_request.status == PrivacyRequestStatus.error: | ||
stopped_collection_details = privacy_request.get_failed_collection_details() | ||
action_required_details = privacy_request.get_failed_checkpoint_details() | ||
resume_endpoint = PRIVACY_REQUEST_RETRY | ||
|
||
if stopped_collection_details: | ||
stopped_collection_details.step = stopped_collection_details.step.value # type: ignore | ||
stopped_collection_details.collection = ( | ||
stopped_collection_details.collection.value # type: ignore | ||
if action_required_details: | ||
action_required_details.step = action_required_details.step.value # type: ignore | ||
action_required_details.collection = ( | ||
action_required_details.collection.value if action_required_details.collection else None # type: ignore | ||
) | ||
|
||
privacy_request.stopped_collection_details = stopped_collection_details | ||
privacy_request.action_required_details = action_required_details | ||
# replaces the placeholder in the url with the privacy request id | ||
privacy_request.resume_endpoint = ( | ||
resume_endpoint.format(privacy_request_id=privacy_request.id) | ||
|
@@ -784,7 +784,10 @@ async def resume_privacy_request_with_manual_input( | |
manual_rows: List[Row] = [], | ||
manual_count: Optional[int] = None, | ||
) -> PrivacyRequest: | ||
"""Resume privacy request after validating and caching manual data for an access or an erasure request.""" | ||
"""Resume privacy request after validating and caching manual data for an access or an erasure request. | ||
|
||
This assumes the privacy request is being resumed from a specific collection in the graph. | ||
""" | ||
privacy_request: PrivacyRequest = get_privacy_request_or_error( | ||
db, privacy_request_id | ||
) | ||
|
@@ -796,7 +799,7 @@ async def resume_privacy_request_with_manual_input( | |
) | ||
|
||
paused_details: Optional[ | ||
CollectionActionRequired | ||
CheckpointActionRequired | ||
] = privacy_request.get_paused_collection_details() | ||
if not paused_details: | ||
raise HTTPException( | ||
|
@@ -805,7 +808,7 @@ async def resume_privacy_request_with_manual_input( | |
) | ||
|
||
paused_step: CurrentStep = paused_details.step | ||
paused_collection: CollectionAddress = paused_details.collection | ||
paused_collection: Optional[CollectionAddress] = paused_details.collection | ||
|
||
if paused_step != expected_paused_step: | ||
raise HTTPException( | ||
|
@@ -818,6 +821,12 @@ async def resume_privacy_request_with_manual_input( | |
dataset_graphs = [dataset_config.get_graph() for dataset_config in datasets] | ||
dataset_graph = DatasetGraph(*dataset_graphs) | ||
|
||
if not paused_collection: | ||
raise HTTPException( | ||
status_code=HTTP_422_UNPROCESSABLE_ENTITY, | ||
detail="Cannot save manual data on paused collection. No paused collection saved'.", | ||
) | ||
|
||
Comment on lines
+824
to
+829
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm just adding a new check here now that I've generally removed the requirement that a collection is saved on a (To be clear, this shouldn't ever be hit though) |
||
node: Optional[Node] = dataset_graph.nodes.get(paused_collection) | ||
if not node: | ||
raise HTTPException( | ||
|
@@ -939,16 +948,16 @@ async def restart_privacy_request_from_failure( | |
) | ||
|
||
failed_details: Optional[ | ||
CollectionActionRequired | ||
] = privacy_request.get_failed_collection_details() | ||
CheckpointActionRequired | ||
] = privacy_request.get_failed_checkpoint_details() | ||
if not failed_details: | ||
raise HTTPException( | ||
status_code=HTTP_400_BAD_REQUEST, | ||
detail=f"Cannot restart privacy request from failure '{privacy_request.id}'; no failed step or collection.", | ||
) | ||
|
||
failed_step: CurrentStep = failed_details.step | ||
failed_collection: CollectionAddress = failed_details.collection | ||
failed_collection: Optional[CollectionAddress] = failed_details.collection | ||
|
||
logger.info( | ||
"Restarting failed privacy request '%s' from '%s step, 'collection '%s'", | ||
|
@@ -964,7 +973,7 @@ async def restart_privacy_request_from_failure( | |
from_step=failed_step.value, | ||
) | ||
|
||
privacy_request.cache_failed_collection_details() # Reset failed step and collection to None | ||
privacy_request.cache_failed_checkpoint_details() # Reset failed step and collection to None | ||
|
||
return privacy_request | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
SUBJECT_IDENTITY_VERIFICATION_TEMPLATE = "subject_identity_verification.html" | ||
EMAIL_ERASURE_REQUEST_FULFILLMENT = "erasure_request_email_fulfillment.html" |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice formatting here will try to remember this for future docs drafts @conceptualshark
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching these - ran through a bit fast 😨