Skip to content
This repository was archived by the owner on Nov 30, 2022. It is now read-only.
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 @@ -51,6 +51,7 @@ The types of changes are:
* Adds ability to send email notification upon privacy request receipt [#1303](https://github.com/ethyca/fidesops/pull/1303)
* Utility to update SaaS config instances based on template updates [#1307](https://github.com/ethyca/fidesops/pull/1307)
* Added generic request sorting button [#1320](https://github.com/ethyca/fidesops/pull/1320)
* Adds ability to send email notification upon privacy request review [#1306](https://github.com/ethyca/fidesops/pull/1306)


### Changed
Expand Down
1 change: 1 addition & 0 deletions data/config/fidesops.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ enabled = true
[notifications]
send_request_completion_notification = true
send_request_receipt_notification = true
send_request_review_notification = true
2 changes: 2 additions & 0 deletions docs/fidesops/docs/guides/configuration_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ The `fidesops.toml` file should specify the following variables:
| Fidesops Notification Variables|---|---|---|---|---|
|`send_request_completion_notification` | `FIDESOPS__NOTIFICATIONS__SEND_REQUEST_COMPLETION_NOTIFICATION` | bool | True | True | Whether a notification will be sent to data subjects upon privacy request completion
|`send_request_receipt_notification` | `FIDESOPS__NOTIFICATIONS__SEND_REQUEST_RECEIPT_NOTIFICATION` | bool | True | True | Whether a notification will be sent to data subjects upon privacy request receipt
|`send_request_review_notification` | `FIDESOPS__NOTIFICATIONS__SEND_REQUEST_REVIEW_NOTIFICATION` | bool | True | True | Whether a notification will be sent to data subjects upon privacy request review


### An example `fidesops.toml` configuration file
Expand Down Expand Up @@ -120,6 +121,7 @@ enabled = true
[notifications]
send_request_completion_notification = true
send_request_receipt_notification = true
send_request_review_notification = true
```

Note: The configuration is case-sensitive, so the variables must be specified in `lowercase`.
Expand Down
1 change: 1 addition & 0 deletions docs/fidesops/docs/guides/email_communications.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Supported modes of use:
- 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.
- Privacy Request Receipt Notification - sends an email to user's email address with privacy request receipt notification.
- Privacy Request Review Notification - sends an email to user's email address upon privacy request review, including rejection reason if applicable.
- Privacy Request Completion Notification - sends an email to user's email address with privacy request completion notification, including a download link to data package, for access requests. For more information on request completion notification, see the [Privacy Requests](privacy_requests.md#request-completion-notification) guide.

## Prerequisites
Expand Down
4 changes: 4 additions & 0 deletions docs/fidesops/docs/guides/privacy_requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ Upon access request completion, an email will be sent to users to notify them of
For security purposes, the data package download link is a one-time link and expires in 24 hrs by default. To change TTL, update the `subject_request_download_link_ttl_seconds`
variable in your `fidesops.toml`.

### Request Review

An email will be sent to users to notify them when their privacy request has been reviewed. If the privacy request was rejected, the email will include rejection reason.


## Approve and deny Privacy Requests

Expand Down
1 change: 1 addition & 0 deletions fidesops.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ enabled = true
[notifications]
send_request_completion_notification = false
send_request_receipt_notification = false
send_request_review_notification = false
60 changes: 54 additions & 6 deletions src/fidesops/ops/api/v1/endpoints/privacy_request_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
PrivacyRequest,
PrivacyRequestStatus,
ProvidedIdentity,
ProvidedIdentityType,
)
from fidesops.ops.schemas.dataset import (
CollectionAddressResponse,
Expand All @@ -90,6 +91,7 @@
EmailActionType,
FidesopsEmail,
RequestReceiptBodyParams,
RequestReviewDenyBodyParams,
SubjectIdentityVerificationBodyParams,
)
from fidesops.ops.schemas.external_https import PrivacyRequestResumeFormat
Expand Down Expand Up @@ -1116,6 +1118,35 @@ def review_privacy_request(
)


def _send_privacy_request_review_email_to_user(
db: Session,
action_type: EmailActionType,
email: Optional[str],
rejection_reason: Optional[str],
) -> None:
"""Helper method to send review notification email to user, shared between approve and deny"""
if not email:
logger.error(
IdentityNotFoundException(
"Identity email was not found, so request review email could not be sent."
)
)
try:
dispatch_email(
db=db,
action_type=action_type,
to_email=email,
email_body_params=RequestReviewDenyBodyParams(
rejection_reason=rejection_reason
)
if action_type is EmailActionType.PRIVACY_REQUEST_REVIEW_DENY
else None,
)
except EmailDispatchException as exc:
# this failure isn't fatal to privacy request
logger.info("Email dispatch failed with exception %s", exc)


@router.post(
PRIVACY_REQUEST_VERIFY_IDENTITY,
status_code=HTTP_200_OK,
Expand Down Expand Up @@ -1194,7 +1225,6 @@ def _approve_request(privacy_request: PrivacyRequest) -> None:
privacy_request.reviewed_at = datetime.utcnow()
privacy_request.reviewed_by = user_id
privacy_request.save(db=db)

AuditLog.create(
db=db,
data={
Expand All @@ -1204,6 +1234,16 @@ def _approve_request(privacy_request: PrivacyRequest) -> None:
"message": "",
},
)
if config.notifications.send_request_review_notification:
_send_privacy_request_review_email_to_user(
db=db,
action_type=EmailActionType.PRIVACY_REQUEST_REVIEW_APPROVE,
email=privacy_request.get_cached_identity_data().get(
ProvidedIdentityType.email.value
),
rejection_reason=None,
)

queue_privacy_request(privacy_request_id=privacy_request.id)

return review_privacy_request(
Expand Down Expand Up @@ -1234,7 +1274,10 @@ def _deny_request(
privacy_request: PrivacyRequest,
) -> None:
"""Method for how to process requests - denied"""

privacy_request.status = PrivacyRequestStatus.denied
privacy_request.reviewed_at = datetime.utcnow()
privacy_request.reviewed_by = user_id
privacy_request.save(db=db)
AuditLog.create(
db=db,
data={
Expand All @@ -1244,10 +1287,15 @@ def _deny_request(
"message": privacy_requests.reason,
},
)
privacy_request.status = PrivacyRequestStatus.denied
privacy_request.reviewed_at = datetime.utcnow()
privacy_request.reviewed_by = user_id
privacy_request.save(db=db)
if config.notifications.send_request_review_notification:
_send_privacy_request_review_email_to_user(
db=db,
action_type=EmailActionType.PRIVACY_REQUEST_REVIEW_DENY,
email=privacy_request.get_cached_identity_data().get(
ProvidedIdentityType.email.value
),
rejection_reason=privacy_requests.reason,
)

return review_privacy_request(
db=db,
Expand Down
1 change: 1 addition & 0 deletions src/fidesops/ops/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ class FidesopsNotificationSettings(FidesSettings):

send_request_completion_notification: Optional[bool] = True
send_request_receipt_notification: Optional[bool] = True
send_request_review_notification: Optional[bool] = True

class Config:
env_prefix = "FIDESOPS__NOTIFICATIONS__"
Expand Down
10 changes: 9 additions & 1 deletion src/fidesops/ops/email_templates/get_email_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
PRIVACY_REQUEST_COMPLETE_ACCESS_TEMPLATE,
PRIVACY_REQUEST_COMPLETE_DELETION_TEMPLATE,
PRIVACY_REQUEST_RECEIPT_TEMPLATE,
PRIVACY_REQUEST_REVIEW_APPROVE_TEMPLATE,
PRIVACY_REQUEST_REVIEW_DENY_TEMPLATE,
SUBJECT_IDENTITY_VERIFICATION_TEMPLATE,
)
from fidesops.ops.schemas.email.email import EmailActionType
Expand All @@ -23,7 +25,9 @@
)


def get_email_template(action_type: EmailActionType) -> Template:
def get_email_template( # pylint: disable=too-many-return-statements
action_type: EmailActionType,
) -> Template:
if action_type == EmailActionType.SUBJECT_IDENTITY_VERIFICATION:
return template_env.get_template(SUBJECT_IDENTITY_VERIFICATION_TEMPLATE)
if action_type == EmailActionType.EMAIL_ERASURE_REQUEST_FULFILLMENT:
Expand All @@ -34,6 +38,10 @@ def get_email_template(action_type: EmailActionType) -> Template:
return template_env.get_template(PRIVACY_REQUEST_COMPLETE_ACCESS_TEMPLATE)
if action_type == EmailActionType.PRIVACY_REQUEST_COMPLETE_DELETION:
return template_env.get_template(PRIVACY_REQUEST_COMPLETE_DELETION_TEMPLATE)
if action_type == EmailActionType.PRIVACY_REQUEST_REVIEW_DENY:
return template_env.get_template(PRIVACY_REQUEST_REVIEW_DENY_TEMPLATE)
if action_type == EmailActionType.PRIVACY_REQUEST_REVIEW_APPROVE:
return template_env.get_template(PRIVACY_REQUEST_REVIEW_APPROVE_TEMPLATE)

logger.error("No corresponding template linked to the %s", action_type)
raise EmailTemplateUnhandledActionType(
Expand Down
2 changes: 2 additions & 0 deletions src/fidesops/ops/email_templates/template_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
PRIVACY_REQUEST_RECEIPT_TEMPLATE = "privacy_request_receipt.html"
PRIVACY_REQUEST_COMPLETE_DELETION_TEMPLATE = "privacy_request_complete_deletion.html"
PRIVACY_REQUEST_COMPLETE_ACCESS_TEMPLATE = "privacy_request_complete_access.html"
PRIVACY_REQUEST_REVIEW_DENY_TEMPLATE = "privacy_request_review_deny.html"
PRIVACY_REQUEST_REVIEW_APPROVE_TEMPLATE = "privacy_request_review_approve.html"
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Privacy Request Approved</title>
</head>
<body>
<main>
<p>
Your privacy request has been approved and is currently processing.
</p>
</main>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Privacy Request Denied</title>
</head>
<body>
<main>
{% if rejection_reason %}
<p>
Your privacy request has been denied for the following reason: {{rejection_reason}}
</p>
{% else %}
<p>
Your privacy request has been denied.
</p>
{% endif %}
</main>
</body>
</html>
8 changes: 8 additions & 0 deletions src/fidesops/ops/schemas/email/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class EmailActionType(str, Enum):
PRIVACY_REQUEST_RECEIPT = "privacy_request_receipt"
PRIVACY_REQUEST_COMPLETE_ACCESS = "privacy_request_complete_access"
PRIVACY_REQUEST_COMPLETE_DELETION = "privacy_request_complete_deletion"
PRIVACY_REQUEST_REVIEW_DENY = "privacy_request_review_deny"
PRIVACY_REQUEST_REVIEW_APPROVE = "privacy_request_review_approve"


class EmailTemplateBodyParams(Enum):
Expand Down Expand Up @@ -71,6 +73,12 @@ class AccessRequestCompleteBodyParams(BaseModel):
download_links: List[str]


class RequestReviewDenyBodyParams(BaseModel):
"""Body params required for privacy request review deny email template"""

rejection_reason: Optional[str]


class EmailForActionType(BaseModel):
"""Email details that depend on action type"""

Expand Down
18 changes: 17 additions & 1 deletion src/fidesops/ops/service/email/email_dispatch_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
EmailServiceType,
FidesopsEmail,
RequestReceiptBodyParams,
RequestReviewDenyBodyParams,
SubjectIdentityVerificationBodyParams,
)
from fidesops.ops.tasks import DatabaseTask, celery_app
Expand Down Expand Up @@ -53,6 +54,7 @@ def dispatch_email(
AccessRequestCompleteBodyParams,
SubjectIdentityVerificationBodyParams,
RequestReceiptBodyParams,
RequestReviewDenyBodyParams,
List[CheckpointActionRequired],
]
] = None,
Expand Down Expand Up @@ -85,7 +87,7 @@ def dispatch_email(
)


def _build_email(
def _build_email( # pylint: disable=too-many-return-statements
action_type: EmailActionType,
body_params: Any,
) -> EmailForActionType:
Expand Down Expand Up @@ -130,6 +132,20 @@ def _build_email(
subject="Your data has been deleted",
body=base_template.render(),
)
if action_type == EmailActionType.PRIVACY_REQUEST_REVIEW_APPROVE:
base_template = get_email_template(action_type)
return EmailForActionType(
subject="Your request has been approved",
body=base_template.render(),
)
if action_type == EmailActionType.PRIVACY_REQUEST_REVIEW_DENY:
base_template = get_email_template(action_type)
return EmailForActionType(
subject="Your request has been denied",
body=base_template.render(
{"rejection_reason": body_params.rejection_reason}
),
)
logger.error("Email action type %s is not implemented", action_type)
raise EmailDispatchException(f"Email action type {action_type} is not implemented")

Expand Down
Loading