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
8 changes: 8 additions & 0 deletions clients/ops/admin-ui/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ export const USER_PRIVILEGES: UserPrivileges[] = [
privilege: "View roles",
scope: "user-permission:read",
},
{
privilege: "Upload privacy request data",
scope: "privacy-request:upload_data",
},
{
privilege: "View privacy request data",
scope: "privacy-request:view_data",
},
];

// API ROUTES
Expand Down
14 changes: 12 additions & 2 deletions src/fidesops/ops/models/connectionconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

import enum
from datetime import datetime
from typing import Any, Dict, Optional, Type
from typing import TYPE_CHECKING, Any, Dict, Optional, Type

from fideslib.db.base import Base
from fideslib.db.base_class import get_key_from_data
from fideslib.exceptions import KeyOrNameAlreadyExists
from sqlalchemy import Boolean, Column, DateTime, Enum, String, event
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy.orm import Session
from sqlalchemy.orm import Session, relationship
from sqlalchemy_utils.types.encrypted.encrypted_type import (
AesGcmEngine,
StringEncryptedType,
Expand All @@ -20,6 +20,9 @@
from fidesops.ops.db.base_class import JSONTypeOverride
from fidesops.ops.schemas.saas.saas_config import SaaSConfig

if TYPE_CHECKING:
from fidesops.ops.models.manual_webhook import AccessManualWebhook


class ConnectionTestStatus(enum.Enum):
"""Enum for supplying statuses of validating credentials for a Connection Config to the user"""
Expand Down Expand Up @@ -119,6 +122,13 @@ class ConnectionConfig(Base):
MutableDict.as_mutable(JSONB), index=False, unique=False, nullable=True
)

access_manual_webhook = relationship(
"AccessManualWebhook",
back_populates="connection_config",
cascade="delete",
uselist=False,
)

Comment on lines +125 to +131
Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks for the tip on the back_populates @sanders41, was able to immediately use this here!

Copy link
Contributor

Choose a reason for hiding this comment

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

could you explain the difference between backref and back_populates for my benefit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh sure, they are both different ways to define orm relationships in SQLAlchemy - I had a personal preference of using backref and defining the relationships on one side, but Paul pointed out that to me recently that backref was now legacy functionality https://docs.sqlalchemy.org/en/14/orm/backref.html#using-the-legacy-backref-relationship-parameter and we should prefer back_populates

Copy link
Contributor

Choose a reason for hiding this comment

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

oh I see, thanks Dawn!

@classmethod
def create_without_saving(
cls: Type[ConnectionConfig], db: Session, *, data: dict[str, Any]
Expand Down
9 changes: 6 additions & 3 deletions src/fidesops/ops/models/manual_webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sqlalchemy import Column, ForeignKey, String
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.ext.mutable import MutableList
from sqlalchemy.orm import Session, backref, relationship
from sqlalchemy.orm import Session, relationship

from fidesops.ops.models.connectionconfig import ConnectionConfig

Expand All @@ -20,10 +20,13 @@ class AccessManualWebhook(Base):
"""

connection_config_id = Column(
String, ForeignKey(ConnectionConfig.id_field_path), unique=True, nullable=False
String,
ForeignKey(ConnectionConfig.id_field_path),
unique=True,
nullable=False,
)
connection_config = relationship(
ConnectionConfig, backref=backref("access_manual_webhook", uselist=False)
"ConnectionConfig", back_populates="access_manual_webhook", uselist=False
)

fields = Column(MutableList.as_mutable(JSONB), nullable=False)
Expand Down
40 changes: 40 additions & 0 deletions tests/ops/api/v1/endpoints/test_connection_config_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from fidesops.ops.api.v1.urn_registry import CONNECTIONS, SAAS_CONFIG, V1_URL_PREFIX
from fidesops.ops.graph.config import CollectionAddress
from fidesops.ops.models.connectionconfig import ConnectionConfig, ConnectionType
from fidesops.ops.models.manual_webhook import AccessManualWebhook
from fidesops.ops.models.policy import CurrentStep
from fidesops.ops.models.privacy_request import CheckpointActionRequired, ManualAction
from fidesops.ops.schemas.email.email import EmailActionType
Expand Down Expand Up @@ -819,6 +820,45 @@ def test_delete_connection_config(
is None
)

def test_delete_manual_webhook_connection_config(
self,
url,
api_client: TestClient,
db: Session,
generate_auth_header,
integration_manual_webhook_config,
access_manual_webhook,
) -> None:
"""Assert both the connection config and its webhook are deleted"""
assert (
db.query(AccessManualWebhook).filter_by(id=access_manual_webhook.id).first()
is not None
)

assert (
db.query(ConnectionConfig)
.filter_by(key=integration_manual_webhook_config.key)
.first()
is not None
)

url = f"{V1_URL_PREFIX}{CONNECTIONS}/{integration_manual_webhook_config.key}"
auth_header = generate_auth_header(scopes=[CONNECTION_DELETE])
resp = api_client.delete(url, headers=auth_header)
assert resp.status_code == 204

assert (
db.query(AccessManualWebhook).filter_by(id=access_manual_webhook.id).first()
is None
)

assert (
db.query(ConnectionConfig)
.filter_by(key=integration_manual_webhook_config.key)
.first()
is None
)


class TestPutConnectionConfigSecrets:
@pytest.fixture(scope="function")
Expand Down
6 changes: 5 additions & 1 deletion tests/ops/fixtures/manual_webhook_fixtures.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from uuid import uuid4

import pytest
from sqlalchemy.orm.exc import ObjectDeletedError

from fidesops.ops.models.connectionconfig import (
AccessLevel,
Expand All @@ -22,7 +23,10 @@ def integration_manual_webhook_config(db) -> ConnectionConfig:
},
)
yield connection_config
connection_config.delete(db)
try:
connection_config.delete(db)
except ObjectDeletedError:
pass


@pytest.fixture(scope="function")
Expand Down
11 changes: 11 additions & 0 deletions tests/ops/models/test_connectionconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,14 @@ def test_connection_type_human_readable(self):
def test_connection_type_human_readable_invalid(self):
with pytest.raises(ValueError):
ConnectionType("nonmapped_type").human_readable()

def test_manual_webhook(
self, integration_manual_webhook_config, access_manual_webhook
):
assert (
integration_manual_webhook_config.access_manual_webhook
== access_manual_webhook
)
assert (
access_manual_webhook.connection_config == integration_manual_webhook_config
)