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

Adobe sign access request test #3504

Merged
merged 17 commits into from
Aug 15, 2023
Merged
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ The types of changes are:

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

### Changed
- Removed deprecated fields from the taxonomy editor [#3909](https://github.com/ethyca/fides/pull/3909)
### Added
- Access support for Adobe Sign [#3504](https://github.com/ethyca/fides/pull/3504)

### Fixed
- Fixed issue when generating masked values for invalid data paths [#3906](https://github.com/ethyca/fides/pull/3906)
Expand All @@ -31,6 +31,7 @@ The types of changes are:

- Systems and Privacy Declaration schema and data migration to support the Dictionary [#3901](https://github.com/ethyca/fides/pull/3901)
- The integration search dropdown is now case-insensitive [#3916](https://github.com/ethyca/fides/pull/3916)
- Removed deprecated fields from the taxonomy editor [#3909](https://github.com/ethyca/fides/pull/3909)

## [2.18.0](https://github.com/ethyca/fides/compare/2.17.0...2.18.0)

Expand Down
104 changes: 104 additions & 0 deletions data/saas/config/adobe_sign_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
saas_config:
fides_key: <instance_fides_key>
name: Adobe Sign
type: adobe_sign
description: A sample schema representing the Adobe Sign connector for Fides
version: 0.1.0

connector_params:
- name: domain
default_value: api.na3.adobesign.com
- name: client_id
label: Client ID
- name: client_secret
sensitive: True
- name: redirect_uri
label: Redirect URI

client_config:
protocol: https
host: <domain>
authentication:
strategy: oauth2_authorization_code
configuration:
authorization_request:
method: GET
client_config:
protocol: https
host: secure.na3.adobesign.com
path: /public/oauth/v2
query_params:
- name: client_id
value: <client_id>
- name: redirect_uri
value: <redirect_uri>
- name: response_type
value: code
- name: scope
value: user_read:account user_write:account agreement_read:account agreement_write:account
- name: state
value: <state>
token_request:
method: POST
path: /oauth/v2/token
headers:
- name: Content-Type
value: application/x-www-form-urlencoded
body: |
{
"client_id": "<client_id>",
"client_secret": "<client_secret>",
"grant_type": "authorization_code",
"code": "<code>",
"redirect_uri": "<redirect_uri>"
}
refresh_request:
method: POST
path: /oauth/v2/refresh
headers:
- name: Content-Type
value: application/x-www-form-urlencoded
body: |
{
"client_id": "<client_id>",
"client_secret": "<client_secret>",
"grant_type": "refresh_token",
"refresh_token": "<refresh_token>",
"redirect_uri": "<redirect_uri>"
}

test_request:
method: GET
path: /api/rest/v6/users

endpoints:
- name: users
requests:
read:
method: GET
path: /api/rest/v6/users
param_values:
- name: placeholder
identity: email
data_path: userInfoList
postprocessors:
- strategy: filter
configuration:
field: email
value:
identity: email
- name: agreements
requests:
read:
method: GET
path: /api/rest/v6/agreements
param_values:
- name: placeholder
identity: email
data_path: userAgreementList
postprocessors:
- strategy: filter
configuration:
field: displayParticipantSetInfos.displayUserSetMemberInfos.email
value:
identity: email
95 changes: 95 additions & 0 deletions data/saas/dataset/adobe_sign_dataset.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
dataset:
- fides_key: <instance_fides_key>
name: Adobe Sign Dataset
description: A sample dataset representing the Adobe Sign connector for Fides
collections:
- name: users
fields:
- name: email
data_categories: [user.contact.email]
fidesops_meta:
data_type: string
- name: company
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: id
data_categories: [user.unique_id]
fidesops_meta:
data_type: string
primary_key: True
- name: firstName
data_categories: [user.name]
fidesops_meta:
data_type: string
- name: lastName
data_categories: [user.name]
fidesops_meta:
data_type: string
- name: isAccountAdmin
data_categories: [system.operations]
fidesops_meta:
data_type: boolean
- name: accountId
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: agreements
fields:
- name: id
data_categories: [system.operations]
fidesops_meta:
data_type: string
primary_key: True
- name: type
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: name
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: groupId
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: displayDate
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: displayParticipantSetInfos
fidesops_meta:
data_type: 'object[]'
fields:
- name: displayUserSetMemberInfos
fidesops_meta:
data_type: 'object[]'
fields:
- name: fullName
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: email
data_categories: [user.contact.email]
fidesops_meta:
data_type: string
- name: company
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: latestVersionId
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: status
data_categories: [system.operations]
fidesops_meta:
data_type: string
- name: esign
data_categories: [system.operations]
fidesops_meta:
data_type: boolean
- name: hidden
data_categories: [system.operations]
fidesops_meta:
data_type: boolean
15 changes: 15 additions & 0 deletions data/saas/icon/adobe_sign.svg
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
Expand Up @@ -84,7 +84,7 @@ def process(
self.exact,
self.case_sensitive,
filter_value, # type: ignore
pydash.get(item, self.field),
self._get_nested_values(item, self.field),
)
]
return (
Expand All @@ -93,7 +93,7 @@ def process(
self.exact,
self.case_sensitive,
filter_value, # type: ignore
pydash.get(data, self.field),
self._get_nested_values(data, self.field),
)
else []
)
Expand Down Expand Up @@ -154,3 +154,40 @@ def _matches(

# base case, compare filter_value to a single string
return filter_value == target if exact else filter_value in target

def _get_nested_values(self, data: Dict[str, Any], path: str) -> Any:
"""
Extracts nested values from a dictionary based on a dot-separated path string.

This function handles cases where the path leads to a list of dictionaries.
Instead of returning the list, it iterates through each dictionary in the list,
applies the remaining path, and returns a flattened list of the results.

Parameters:
data (dict): The dictionary from which to extract the value.
path (str): The dot-separated string path indicating the nested value.

Returns:
The value(s) at the nested path within the dictionary. If the path
leads to a list of dictionaries, it returns a flattened list of values.

Raises:
KeyError: If the path does not exist in the dictionary.

Example:
For a dictionary {"a": {"b": [{"c": 1}, {"c": 2}]}} and path "a.b.c", the function
returns [1, 2].
"""
components = path.split(".")
value = data[components[0]]
if len(components) > 1:
if isinstance(value, dict):
return self._get_nested_values(value, ".".join(components[1:]))
elif isinstance(value, list):
return pydash.flatten(
[
self._get_nested_values(item, ".".join(components[1:]))
for item in value
]
)
return value
39 changes: 39 additions & 0 deletions tests/fixtures/saas/adobe_sign_fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from typing import Any, Dict

import pydash
import pytest

from tests.ops.integration_tests.saas.connector_runner import ConnectorRunner
from tests.ops.test_helpers.vault_client import get_secrets

secrets = get_secrets("adobe_sign")


@pytest.fixture(scope="session")
def adobe_sign_secrets(saas_config) -> Dict[str, Any]:
return {
"domain": pydash.get(saas_config, "adobe_sign.domain") or secrets["domain"],
"client_id": pydash.get(saas_config, "adobe_sign.client_id")
or secrets["client_id"],
"client_secret": pydash.get(saas_config, "adobe_sign.client_secret")
or secrets["client_secret"],
"redirect_uri": pydash.get(saas_config, "adobe_sign.redirect_uri")
or secrets["redirect_uri"],
}


@pytest.fixture(scope="session")
def adobe_sign_identity_email(saas_config) -> str:
return (
pydash.get(saas_config, "adobe_sign.identity_email")
or secrets["identity_email"]
)


@pytest.fixture
def adobe_sign_runner(
db,
cache,
adobe_sign_secrets,
) -> ConnectorRunner:
return ConnectorRunner(db, cache, "adobe_sign", adobe_sign_secrets)
20 changes: 20 additions & 0 deletions tests/ops/integration_tests/saas/test_adobe_sign_task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import pytest

from tests.ops.integration_tests.saas.connector_runner import ConnectorRunner


@pytest.mark.skip(reason="Currently unable to test OAuth2 connectors")
@pytest.mark.integration_saas
class TestAdobeSignConnector:
def test_connection(self, adobe_sign_runner: ConnectorRunner):
adobe_sign_runner.test_connection()

async def test_access_request(
self, adobe_sign_runner: ConnectorRunner, policy, adobe_sign_identity_email: str
):
access_results = await adobe_sign_runner.access_request(
access_policy=policy, identities={"email": adobe_sign_identity_email}
)

for users in access_results["adobe_sign_instance:users"]:
assert users["email"] == adobe_sign_identity_email
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,33 @@ def test_filter_invalid_field_value_array():
assert str(exc.value) == (
"Every value in the 'attribute.email_contacts' list must be a string"
)


def test_nested_array_path():
config = FilterPostProcessorConfiguration(
field="agreement.orgs.members.email", value="[email protected]"
)
data = [
{
"agreement": {
"id": 1,
"orgs": [{"members": [{"email": "[email protected]"}]}],
}
},
{
"agreement": {
"id": 2,
"orgs": [{"members": [{"email": "[email protected]"}]}],
}
},
]
processor = FilterPostProcessorStrategy(configuration=config)
result = processor.process(data)
assert result == [
{
"agreement": {
"id": 1,
"orgs": [{"members": [{"email": "[email protected]"}]}],
}
}
]