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

Adding ability to resubmit DSRs #5658

Merged
merged 25 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3e61c1f
Adding ability to resubmit DSRs
galvana Jan 10, 2025
00298a2
Running static checks
galvana Jan 11, 2025
8955f94
Additional static fixes
galvana Jan 11, 2025
729e32b
Adding missing inits
galvana Jan 11, 2025
e06bb4a
Fixing imports
galvana Jan 11, 2025
7badf89
Fixing mocks in tests
galvana Jan 11, 2025
f84e0b3
Fixing assertions
galvana Jan 11, 2025
f4b53ad
Importing missing fixture
galvana Jan 11, 2025
d1948a8
Renaming services module + misc cleanup
galvana Jan 12, 2025
e4d01fe
Adding privacy request service tests
galvana Jan 12, 2025
26d7263
Removing unused imports
galvana Jan 12, 2025
6c2ff81
Specifying Python version
galvana Jan 13, 2025
66f0d4a
Merge branch 'main' into LA-112-add-ability-to-resubmit-a-DSR
galvana Jan 14, 2025
65a723f
Adding endpoint tests
galvana Jan 14, 2025
105e449
Merge branch 'main' into LA-112-add-ability-to-resubmit-a-DSR
galvana Jan 28, 2025
8cd070e
Fixing downrev
galvana Jan 28, 2025
0466046
Misc cleanup + test updates
galvana Jan 29, 2025
d1455cb
Falling back to resubmit if cache has expired during privacy request …
galvana Jan 29, 2025
7816425
Reverting changes to user fixture
galvana Jan 29, 2025
6953bb2
Merge branch 'main' into LA-112-add-ability-to-resubmit-a-DSR
galvana Jan 29, 2025
3253756
Merge branch 'main' into LA-112-add-ability-to-resubmit-a-DSR
galvana Jan 29, 2025
a27e492
Updating change log
galvana Jan 29, 2025
e28c0da
Changing encryption key in test to avoid pre-commit error
galvana Jan 29, 2025
6bbd5db
Formatting fix
galvana Jan 29, 2025
6787aa7
Merge branch 'main' into LA-112-add-ability-to-resubmit-a-DSR
galvana Jan 29, 2025
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
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ Changes can also be flagged with a GitHub label for tracking purposes. The URL o

## [Unreleased](https://github.com/ethyca/fides/compare/2.53.0...main)

### Added
- Migration to add the `data_uses` column to `stagedresource` table, prereqs for Data Catalog work in Fidesplus [#5600](https://github.com/ethyca/fides/pull/5600/) https://github.com/ethyca/fides/labels/db-migration
- Added a new endpoint to fully resubmit any errored privacy requests [#5658](https://github.com/ethyca/fides/pull/5658) https://github.com/ethyca/fides/labels/db-migration

### Changed
- Updated UI colors to new brand. Update logo, homepage cards. [#5668](https://github.com/ethyca/fides/pull/5668)
- Privacy request status tags colors have been updated [#5699](https://github.com/ethyca/fides/pull/5699)
Expand All @@ -32,9 +36,6 @@ Changes can also be flagged with a GitHub label for tracking purposes. The URL o
### Developer Experience
- Migrated radio buttons and groups to Ant Design [#5681](https://github.com/ethyca/fides/pull/5681)

### Added
- Migration to add the `data_uses` column to `stagedresource` table, prereqs for Data Catalog work in Fidesplus [#5600](https://github.com/ethyca/fides/pull/5600/) https://github.com/ethyca/fides/labels/db-migration

### Fixed
- Updating mongodb connectors so it can support usernames and password with URL encoded characters [#5682](https://github.com/ethyca/fides/pull/5682)
- After creating a new system, the url is now updated correctly to the new system edit page [#5701](https://github.com/ethyca/fides/pull/5701)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,7 @@ const RequestDetails = ({ subjectRequest }: RequestDetailsProps) => {

return (
<Flex direction="column" gap={4}>
<Heading
color="gray.900"
fontSize="lg"
fontWeight="semibold"
mt={4}
mb={4}
>
<Heading color="gray.900" fontSize="lg" fontWeight="semibold">
Request details
</Heading>
<Divider />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""update privacy request relationships

Revision ID: 1088e8353890
Revises: d9237a0c0d5a
Create Date: 2024-12-26 22:38:37.905571

"""

from alembic import op

# revision identifiers, used by Alembic.
revision = "1088e8353890"
down_revision = "d9237a0c0d5a"
branch_labels = None
depends_on = None


def upgrade():
op.drop_constraint(
"custom_privacy_request_field_privacy_request_id_fkey",
"custom_privacy_request_field",
type_="foreignkey",
)
op.create_foreign_key(
"custom_privacy_request_field_privacy_request_id_fkey",
"custom_privacy_request_field",
"privacyrequest",
["privacy_request_id"],
["id"],
onupdate="CASCADE",
ondelete="CASCADE",
)
op.drop_constraint(
"providedidentity_privacy_request_id_fkey",
"providedidentity",
type_="foreignkey",
)
op.create_foreign_key(
"providedidentity_privacy_request_id_fkey",
"providedidentity",
"privacyrequest",
["privacy_request_id"],
["id"],
onupdate="CASCADE",
ondelete="CASCADE",
)
op.drop_constraint(
"privacyrequesterror_privacy_request_id_fkey",
"privacyrequesterror",
type_="foreignkey",
)
op.create_foreign_key(
"privacyrequesterror_privacy_request_id_fkey",
"privacyrequesterror",
"privacyrequest",
["privacy_request_id"],
["id"],
onupdate="CASCADE",
ondelete="CASCADE",
)


def downgrade():
op.drop_constraint(
"providedidentity_privacy_request_id_fkey",
"providedidentity",
type_="foreignkey",
)
op.create_foreign_key(
"providedidentity_privacy_request_id_fkey",
"providedidentity",
"privacyrequest",
["privacy_request_id"],
["id"],
)
op.drop_constraint(
"custom_privacy_request_field_privacy_request_id_fkey",
"custom_privacy_request_field",
type_="foreignkey",
)
op.create_foreign_key(
"custom_privacy_request_field_privacy_request_id_fkey",
"custom_privacy_request_field",
"privacyrequest",
["privacy_request_id"],
["id"],
)
op.drop_constraint(
"privacyrequesterror_privacy_request_id_fkey",
"privacyrequesterror",
type_="foreignkey",
)
op.create_foreign_key(
"privacyrequesterror_privacy_request_id_fkey",
"privacyrequesterror",
"privacyrequest",
["privacy_request_id"],
["id"],
)
26 changes: 22 additions & 4 deletions src/fides/api/api/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
from fastapi import Depends
from sqlalchemy.orm import Session

from fides.api.common_exceptions import FunctionalityNotConfigured
from fides.api.common_exceptions import RedisNotConfigured
from fides.api.db.session import get_db_engine, get_db_session
from fides.api.util.cache import get_cache as get_redis_connection
from fides.config import CONFIG, FidesConfig
from fides.config import get_config as get_app_config
from fides.config.config_proxy import ConfigProxy
from fides.service.messaging.messaging_service import MessagingService
from fides.service.privacy_request.privacy_request_service import PrivacyRequestService

_engine = None

Expand Down Expand Up @@ -60,9 +62,25 @@ def get_config_proxy(db: Session = Depends(get_db)) -> ConfigProxy:


def get_cache() -> Generator:
"""Return a connection to our redis cache"""
"""Return a connection to our Redis cache"""
if not CONFIG.redis.enabled:
raise FunctionalityNotConfigured(
"Application redis cache required, but it is currently disabled! Please update your application configuration to enable integration with a redis cache."
raise RedisNotConfigured(
"Application redis cache required, but it is currently disabled! Please update your application configuration to enable integration with a Redis cache."
)
yield get_redis_connection()


def get_messaging_service(
db: Session = Depends(get_db),
config: FidesConfig = Depends(get_config),
config_proxy: ConfigProxy = Depends(get_config_proxy),
) -> MessagingService:
return MessagingService(db, config, config_proxy)


def get_privacy_request_service(
db: Session = Depends(get_db),
config_proxy: ConfigProxy = Depends(get_config_proxy),
messaging_service: MessagingService = Depends(get_messaging_service),
) -> PrivacyRequestService:
return PrivacyRequestService(db, config_proxy, messaging_service)
82 changes: 36 additions & 46 deletions src/fides/api/api/v1/endpoints/consent_request_endpoints.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from datetime import datetime
from typing import Dict, List, Optional, Set, Tuple, Union
from typing import List, Optional, Set, Tuple, Union

from fastapi import Depends, HTTPException, Security
from fastapi_pagination import Page, Params
Expand All @@ -19,17 +19,12 @@
HTTP_403_FORBIDDEN,
HTTP_404_NOT_FOUND,
HTTP_422_UNPROCESSABLE_ENTITY,
HTTP_500_INTERNAL_SERVER_ERROR,
)

from fides.api.api.deps import get_config_proxy, get_db
from fides.api.api.v1.endpoints.privacy_request_endpoints import (
create_privacy_request_func,
)
from fides.api.api.deps import get_config_proxy, get_db, get_messaging_service
from fides.api.common_exceptions import (
FunctionalityNotConfigured,
IdentityVerificationException,
MessageDispatchException,
RedisNotConfigured,
)
from fides.api.db.seed import DEFAULT_CONSENT_POLICY
from fides.api.models.messaging import get_messaging_method
Expand All @@ -41,7 +36,7 @@
)
from fides.api.models.property import Property
from fides.api.oauth.utils import verify_oauth_client
from fides.api.schemas.messaging.messaging import MessagingActionType, MessagingMethod
from fides.api.schemas.messaging.messaging import MessagingMethod
from fides.api.schemas.privacy_request import BulkPostPrivacyRequests
from fides.api.schemas.privacy_request import Consent as ConsentSchema
from fides.api.schemas.privacy_request import (
Expand All @@ -55,8 +50,6 @@
VerificationCode,
)
from fides.api.schemas.redis_cache import Identity
from fides.api.service._verification import send_verification_code_to_user
from fides.api.service.messaging.message_dispatch_service import message_send_enabled
from fides.api.util.api_router import APIRouter
from fides.api.util.consent_util import (
get_or_create_fides_user_device_id_provided_identity,
Expand All @@ -73,6 +66,8 @@
)
from fides.config import CONFIG
from fides.config.config_proxy import ConfigProxy
from fides.service.messaging.messaging_service import MessagingService
from fides.service.privacy_request.privacy_request_service import PrivacyRequestService

router = APIRouter(tags=["Consent"], prefix=V1_URL_PREFIX)

Expand Down Expand Up @@ -183,12 +178,13 @@ def create_consent_request(
*,
db: Session = Depends(get_db),
config_proxy: ConfigProxy = Depends(get_config_proxy),
messaging_service: MessagingService = Depends(get_messaging_service),
data: ConsentRequestCreate,
) -> ConsentRequestResponse:
"""Creates a verification code for the user to verify access to manage consent preferences."""
if not CONFIG.redis.enabled:
raise FunctionalityNotConfigured(
"Application redis cache required, but it is currently disabled! Please update your application configuration to enable integration with a redis cache."
raise RedisNotConfigured(
"Application Redis cache required, but it is currently disabled! Please update your application configuration to enable integration with a Redis cache."
)
# TODO: (PROD-2142)- pass in property id here
if data.property_id:
Expand Down Expand Up @@ -228,25 +224,12 @@ def create_consent_request(
consent_request.persist_custom_privacy_request_fields(
db=db, custom_privacy_request_fields=data.custom_privacy_request_fields
)
if message_send_enabled(
db,
data.property_id,
MessagingActionType.SUBJECT_IDENTITY_VERIFICATION,
not config_proxy.execution.disable_consent_identity_verification,
):
try:
send_verification_code_to_user(
db, consent_request, data.identity, data.property_id
)
except MessageDispatchException as exc:
logger.error("Error sending the verification code message: {}", str(exc))
raise HTTPException(
status_code=HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error sending the verification code message: {str(exc)}",
)

messaging_service.send_verification_code(
consent_request, data.identity, data.property_id
)

return ConsentRequestResponse(
identity=identity,
consent_request_id=consent_request.id,
)

Expand Down Expand Up @@ -407,8 +390,8 @@ def queue_privacy_request_to_propagate_consent_old_workflow(
]

# Restrict consent preferences to just those that are executable
executable_consent_preferences: List[Dict] = [
pref.model_dump(mode="json")
executable_consent_preferences: List[ConsentSchema] = [
pref
for pref in consent_preferences.consent or []
if pref.data_use in executable_data_uses
]
Expand All @@ -422,20 +405,27 @@ def queue_privacy_request_to_propagate_consent_old_workflow(
return None

logger.info("Executable consent options: {}", executable_data_uses)
privacy_request_results: BulkPostPrivacyRequests = create_privacy_request_func(
db=db,
config_proxy=ConfigProxy(db),
data=[
PrivacyRequestCreate(
identity=identity,
policy_key=policy,
consent_preferences=executable_consent_preferences,
consent_request_id=consent_request.id,
custom_privacy_request_fields=consent_request.get_persisted_custom_privacy_request_fields(),
source=consent_request.source,
)
],
authenticated=True,

# It's a bit weird to initialize the privacy request service here,
# but this logic is slated for deprecation so it's not worth doing a larger refactor
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Follow up on deprecation

privacy_request_service = PrivacyRequestService(
db, ConfigProxy(db), MessagingService(db, CONFIG, ConfigProxy(db))
)

privacy_request_results: BulkPostPrivacyRequests = (
privacy_request_service.create_bulk_privacy_requests(
[
PrivacyRequestCreate(
identity=identity,
policy_key=policy,
consent_preferences=executable_consent_preferences,
consent_request_id=consent_request.id,
custom_privacy_request_fields=consent_request.get_persisted_custom_privacy_request_fields(),
source=consent_request.source,
)
],
authenticated=True,
)
)

if privacy_request_results.failed or not privacy_request_results.succeeded:
Expand Down
10 changes: 4 additions & 6 deletions src/fides/api/api/v1/endpoints/drp_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@
from fides.api.schemas.privacy_request import PrivacyRequestDRPStatusResponse
from fides.api.schemas.redis_cache import Identity
from fides.api.service.drp.drp_fidesops_mapper import DrpFidesopsMapper
from fides.api.service.messaging.message_dispatch_service import (
check_and_dispatch_error_notifications,
)
from fides.api.service.privacy_request.request_runner_service import (
queue_privacy_request,
)
from fides.api.service.privacy_request.request_service import (
build_required_privacy_request_kwargs,
cache_data,
Expand All @@ -49,6 +43,10 @@
from fides.common.api.v1 import urn_registry as urls
from fides.config import CONFIG
from fides.config.config_proxy import ConfigProxy
from fides.service.messaging.messaging_service import (
check_and_dispatch_error_notifications,
)
from fides.service.privacy_request.privacy_request_service import queue_privacy_request

router = APIRouter(tags=["DRP"], prefix=urls.V1_URL_PREFIX)

Expand Down
Loading
Loading