Skip to content
This repository has been archived by the owner on Nov 30, 2022. It is now read-only.

1009 id verification required endpoint #1221

Merged
merged 12 commits into from
Aug 30, 2022
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ The types of changes are:
### Added
* Adds users and owners configuration for Hubspot connector [#1091](https://github.com/ethyca/fidesops/pull/1091)
* Foundations for a new email connector type [#1142](https://github.com/ethyca/fidesops/pull/1142)
* Adds endpoint for GET identity verification config [#1221](https://github.com/ethyca/fidesops/pull/1221)

## [1.7.1](https://github.com/ethyca/fidesops/compare/1.7.0...1.7.1)

Expand Down
1 change: 1 addition & 0 deletions docs/fidesops/docs/guides/configuration_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ root_password = "Testpassword1!"
[execution]
masking_strict = true
require_manual_request_approval = true
subject_identity_verification_required = false
task_retry_count = 3
task_retry_delay = 20
task_retry_backoff = 2
Expand Down
40 changes: 37 additions & 3 deletions docs/fidesops/docs/postman/Fidesops.postman_collection.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"info": {
"_postman_id": "8d7f46be-8de5-45a5-88e3-a4860feb1b42",
"_postman_id": "20bfd0a9-15a2-4333-8c3c-2cc3d8ed31f5",
"name": "Fidesops",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "1984786"
"_exporter_id": "13396647"
},
"item": [
{
Expand Down Expand Up @@ -73,7 +73,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "[\n \"client:create\",\n \"client:update\",\n \"client:read\",\n \"client:delete\",\n \"config:read\",\n \"connection_type:read\",\n \"connection:read\",\n \"connection:create_or_update\",\n \"connection:delete\",\n \"connection:instantiate\",\n \"dataset:create_or_update\",\n \"dataset:delete\",\n \"dataset:read\",\n \"encryption:exec\",\n \"policy:create_or_update\",\n \"policy:read\",\n \"policy:delete\",\n \"privacy-request:read\",\n \"privacy-request:delete\",\n \"rule:create_or_update\",\n \"rule:read\",\n \"rule:delete\",\n \"scope:read\",\n \"storage:create_or_update\",\n \"storage:delete\",\n \"storage:read\",\n \"privacy-request:resume\",\n \"webhook:create_or_update\",\n \"webhook:read\",\n \"webhook:delete\",\n \"saas_config:create_or_update\",\n \"saas_config:read\",\n \"saas_config:delete\",\n \"privacy-request:review\",\n \"user:create\",\n \"user:delete\"\n]",
"raw": "[\n \"client:create\",\n \"client:update\",\n \"client:read\",\n \"client:delete\",\n \"config:read\",\n \"connection_type:read\",\n \"connection:read\",\n \"connection:create_or_update\",\n \"connection:delete\",\n \"connection:instantiate\",\n \"dataset:create_or_update\",\n \"dataset:delete\",\n \"dataset:read\",\n \"encryption:exec\",\n \"policy:create_or_update\",\n \"policy:read\",\n \"policy:delete\",\n \"privacy-request:read\",\n \"privacy-request:delete\",\n \"identity-verification:read\",\n \"rule:create_or_update\",\n \"rule:read\",\n \"rule:delete\",\n \"scope:read\",\n \"storage:create_or_update\",\n \"storage:delete\",\n \"storage:read\",\n \"privacy-request:resume\",\n \"webhook:create_or_update\",\n \"webhook:read\",\n \"webhook:delete\",\n \"saas_config:create_or_update\",\n \"saas_config:read\",\n \"saas_config:delete\",\n \"privacy-request:review\",\n \"user:create\",\n \"user:delete\"\n]",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -4303,6 +4303,40 @@
"response": []
}
]
},
{
"name": "Identity Verification",
"item": [
{
"name": "Get Identity Verification Config",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{client_token}}",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "{{host}}/id-verification/config/",
"host": [
"{{host}}"
],
"path": [
"id-verification",
"config",
""
]
}
},
"response": []
}
]
}
],
"event": [
Expand Down
1 change: 1 addition & 0 deletions fidesops.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ root_password = "Testpassword1!"
[execution]
masking_strict = true
require_manual_request_approval = false
subject_identity_verification_required = false
task_retry_count = 0
task_retry_delay = 1
task_retry_backoff = 1
Expand Down
2 changes: 2 additions & 0 deletions src/fidesops/ops/api/v1/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
email_endpoints,
encryption_endpoints,
health_endpoints,
identity_verification_endpoints,
masking_endpoints,
oauth_endpoints,
policy_endpoints,
Expand All @@ -32,6 +33,7 @@
api_router.include_router(policy_endpoints.router)
api_router.include_router(policy_webhook_endpoints.router)
api_router.include_router(privacy_request_endpoints.router)
api_router.include_router(identity_verification_endpoints.router)
api_router.include_router(storage_endpoints.router)
api_router.include_router(email_endpoints.router)
api_router.include_router(saas_config_endpoints.router)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import logging
from typing import Optional

from fastapi import Depends, Security
from sqlalchemy.orm import Session

from fidesops.ops.api import deps
from fidesops.ops.api.v1 import urn_registry as urls
from fidesops.ops.api.v1.scope_registry import IDENTITY_VERIFICATION_READ
from fidesops.ops.core.config import config
from fidesops.ops.models.email import EmailConfig
from fidesops.ops.schemas.identity_verification import (
IdentityVerificationConfigResponse,
)
from fidesops.ops.util.api_router import APIRouter
from fidesops.ops.util.oauth_util import verify_oauth_client

logger = logging.getLogger(__name__)
router = APIRouter(tags=["Identity Verification"], prefix=urls.V1_URL_PREFIX)


@router.get(
urls.ID_VERIFICATION_CONFIG,
dependencies=[Security(verify_oauth_client, scopes=[IDENTITY_VERIFICATION_READ])],
response_model=IdentityVerificationConfigResponse,
)
def get_id_verification_config(
*,
db: Session = Depends(deps.get_db),
) -> IdentityVerificationConfigResponse:
"""Returns id verification config."""
email_config: Optional[EmailConfig] = db.query(EmailConfig).first()
return IdentityVerificationConfigResponse(
identity_verification_required=config.execution.subject_identity_verification_required,
valid_email_config_exists=bool(email_config and email_config.secrets),
)
3 changes: 3 additions & 0 deletions src/fidesops/ops/api/v1/scope_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
)
PRIVACY_REQUEST_REVIEW = "privacy-request:review"

IDENTITY_VERIFICATION_READ = "identity-verification:read"
sanders41 marked this conversation as resolved.
Show resolved Hide resolved

WEBHOOK_CREATE_OR_UPDATE = "webhook:create_or_update"
WEBHOOK_READ = "webhook:read"
WEBHOOK_DELETE = "webhook:delete"
Expand Down Expand Up @@ -82,6 +84,7 @@
POLICY_READ,
POLICY_DELETE,
PRIVACY_REQUEST_REVIEW,
IDENTITY_VERIFICATION_READ,
PRIVACY_REQUEST_READ,
PRIVACY_REQUEST_DELETE,
PRIVACY_REQUEST_CALLBACK_RESUME,
Expand Down
3 changes: 3 additions & 0 deletions src/fidesops/ops/api/v1/urn_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
PRIVACY_REQUEST_RETRY = "/privacy-request/{privacy_request_id}/retry"
REQUEST_PREVIEW = "/privacy-request/preview"

# Identity Verification URLs
ID_VERIFICATION_CONFIG = "/id-verification/config"

# Rule URLs
RULE_LIST = "/policy/{policy_key}/rule"
RULE_DETAIL = "/policy/{policy_key}/rule/{rule_key}"
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 @@ -220,6 +220,7 @@ def log_all_config_values(self) -> None:
"task_retry_delay",
"task_retry_backoff",
"require_manual_request_approval",
"subject_identity_verification_required",
],
}

Expand Down
2 changes: 1 addition & 1 deletion src/fidesops/ops/graph/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ class ObjectField(Field):
fields: Dict[str, Field]

@validator("data_categories")
def validate_data_categories(
def validate_data_categories( # type: ignore
Copy link
Contributor Author

Choose a reason for hiding this comment

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

mypy check was giving the following error:

src/fidesops/ops/graph/config.py:309: error: The erased type of self
"fidesops.ops.graph.config.ObjectField" is not a supertype of its class
"Type[fidesops.ops.graph.config.ObjectField]"  [misc]
        def validate_data_categories(
        ^
Found 1 error in 1 file (checked [19](https://github.com/ethyca/fidesops/runs/8097316393?check_suite_focus=true#step:5:20)8 source files)

eastandwestwind marked this conversation as resolved.
Show resolved Hide resolved
cls: "ObjectField", value: Optional[List[FidesOpsKey]]
) -> Optional[List[FidesOpsKey]]:
"""To prevent mismatches between data categories on an ObjectField and a nested ScalarField, only
Expand Down
8 changes: 8 additions & 0 deletions src/fidesops/ops/schemas/identity_verification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from fidesops.ops.schemas.base_class import BaseSchema


class IdentityVerificationConfigResponse(BaseSchema):
"""Response for identity verification config info"""

identity_verification_required: bool
valid_email_config_exists: bool
96 changes: 96 additions & 0 deletions tests/ops/api/v1/endpoints/test_identity_verification_endpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import pytest
from starlette.testclient import TestClient

from fidesops.ops.api.v1.scope_registry import EMAIL_READ, IDENTITY_VERIFICATION_READ
from fidesops.ops.api.v1.urn_registry import ID_VERIFICATION_CONFIG, V1_URL_PREFIX
from fidesops.ops.core.config import config


class TestGetIdentityVerificationConfig:
@pytest.fixture(scope="function")
def url(self) -> str:
return V1_URL_PREFIX + ID_VERIFICATION_CONFIG

@pytest.fixture(scope="function")
def subject_identity_verification_required(self):
"""Override autouse fixture to enable identity verification for tests"""
original_value = config.execution.subject_identity_verification_required
config.execution.subject_identity_verification_required = True
yield
config.execution.subject_identity_verification_required = original_value
sanders41 marked this conversation as resolved.
Show resolved Hide resolved

def test_get_id_verification_config_not_authenticated(
self, api_client: TestClient, url
):
response = api_client.get(url, headers={})
assert 401 == response.status_code

def test_get_id_verification_config_incorrect_scope(
self,
api_client: TestClient,
url,
generate_auth_header,
):
auth_header = generate_auth_header([EMAIL_READ])
response = api_client.get(url, headers=auth_header)
assert 403 == response.status_code

def test_get_config_with_verification_required_no_email_config(
self,
url,
db,
api_client: TestClient,
subject_identity_verification_required,
generate_auth_header,
):
auth_header = generate_auth_header([IDENTITY_VERIFICATION_READ])
resp = api_client.get(url, headers=auth_header)
assert resp.status_code == 200
response_data = resp.json()
assert response_data["identity_verification_required"] is True
assert response_data["valid_email_config_exists"] is False

def test_get_config_with_verification_required_with_email_config(
self,
url,
db,
api_client: TestClient,
email_config,
subject_identity_verification_required,
generate_auth_header,
):
auth_header = generate_auth_header([IDENTITY_VERIFICATION_READ])
resp = api_client.get(url, headers=auth_header)
assert resp.status_code == 200
response_data = resp.json()
assert response_data["identity_verification_required"] is True
assert response_data["valid_email_config_exists"] is True

def test_get_config_with_verification_not_required_with_email_config(
self,
url,
db,
api_client: TestClient,
email_config,
generate_auth_header,
):
auth_header = generate_auth_header([IDENTITY_VERIFICATION_READ])
resp = api_client.get(url, headers=auth_header)
assert resp.status_code == 200
response_data = resp.json()
assert response_data["identity_verification_required"] is False
assert response_data["valid_email_config_exists"] is True

def test_get_config_with_verification_not_required_with_no_email_config(
self,
url,
db,
api_client: TestClient,
generate_auth_header,
):
auth_header = generate_auth_header([IDENTITY_VERIFICATION_READ])
resp = api_client.get(url, headers=auth_header)
assert resp.status_code == 200
response_data = resp.json()
assert response_data["identity_verification_required"] is False
assert response_data["valid_email_config_exists"] is False
4 changes: 2 additions & 2 deletions tests/ops/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,8 @@ def require_manual_request_approval():
config.execution.require_manual_request_approval = original_value


@pytest.fixture(autouse=True, scope="session")
def subject_identity_verification_required():
@pytest.fixture(autouse=True, scope="function")
def subject_identity_verification_not_required():
"""Disable identity verification for most tests unless overridden"""
original_value = config.execution.subject_identity_verification_required
config.execution.subject_identity_verification_required = False
Expand Down