Skip to content

Commit

Permalink
feat: add feature flag for checking allowed data fields
Browse files Browse the repository at this point in the history
Adds setting for allowed data field check named as:
ENABLE_ALLOWED_DATA_FIELDS_RESTRICTION
The setting defaults False.
When False, app logs a warning message when there is a query
trying to access a field which is not in service's allowed_data_fields

Refs HP-2319
  • Loading branch information
nicobav committed May 23, 2024
1 parent 457a90c commit 95193fe
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 3 deletions.
20 changes: 17 additions & 3 deletions open_city_profile/graphene.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import uuid
from functools import partial

Expand Down Expand Up @@ -189,11 +190,24 @@ def resolve(self, next, root, info, **kwargs):
field_name = to_snake_case(getattr(info, "field_name", ""))

if not getattr(info.context, "service", False):
raise ServiceNotIdentifiedError("Service not identified")
if settings.ENABLE_ALLOWED_DATA_FIELDS_RESTRICTION:
raise ServiceNotIdentifiedError("Service not identified")

logging.warning(
"Allowed data field exception would occur: Service not identified. Field name: %s",
field_name,
)

if not root.is_field_allowed_for_service(field_name, info.context.service):
raise FieldNotAllowedError(
"Field is not allowed for service.", field_name=field_name
if settings.ENABLE_ALLOWED_DATA_FIELDS_RESTRICTION:
raise FieldNotAllowedError(
"Field is not allowed for service.", field_name=field_name
)

logging.warning(
"Allowed data field exception would occur. Field (%s) is not allowed for service %s.",
field_name,
info.context.service,
)

return next(root, info, **kwargs)
3 changes: 3 additions & 0 deletions open_city_profile/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
AUDIT_LOG_LOGGER_FILENAME=(str, ""),
AUDIT_LOG_TO_DB_ENABLED=(bool, False),
OPEN_CITY_PROFILE_LOG_LEVEL=(str, None),
ENABLE_ALLOWED_DATA_FIELDS_RESTRICTION=(bool, False),
ENABLE_GRAPHIQL=(bool, False),
ENABLE_GRAPHQL_INTROSPECTION=(bool, False),
GRAPHQL_QUERY_DEPTH_LIMIT=(int, 12),
Expand Down Expand Up @@ -174,6 +175,8 @@

GRAPHQL_QUERY_DEPTH_LIMIT = env("GRAPHQL_QUERY_DEPTH_LIMIT")

ENABLE_ALLOWED_DATA_FIELDS_RESTRICTION = env("ENABLE_ALLOWED_DATA_FIELDS_RESTRICTION")

INSTALLED_APPS = [
"helusers.apps.HelusersConfig",
"open_city_profile.apps.OpenCityProfileAdminConfig",
Expand Down
5 changes: 5 additions & 0 deletions open_city_profile/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,8 @@ def unix_timestamp_now():
@pytest.fixture(params=[None, ""])
def empty_string_value(request):
return request.param


@pytest.fixture(autouse=True)
def enable_allowed_data_fields_restriction(settings):
settings.ENABLE_ALLOWED_DATA_FIELDS_RESTRICTION = True
59 changes: 59 additions & 0 deletions profiles/tests/test_allowed_data_field_restriction_flag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from unittest import mock

from profiles.tests.factories import ProfileFactory, SensitiveDataFactory
from services.tests.factories import AllowedDataFieldFactory, ServiceConnectionFactory


def test_enable_allowed_data_fields_restriction_flag_false_shows_data(
user_gql_client, service, settings
):
settings.ENABLE_ALLOWED_DATA_FIELDS_RESTRICTION = False
profile = ProfileFactory(user=user_gql_client.user)
ServiceConnectionFactory(profile=profile, service=service)
service.allowed_data_fields.add(AllowedDataFieldFactory(field_name="name"))
SensitiveDataFactory(profile=profile)

query = """
{
myProfile {
firstName
sensitivedata {
ssn
}
}
}
"""

executed = user_gql_client.execute(query, service=service)

assert executed["data"]["myProfile"]["firstName"] == profile.first_name
assert (
executed["data"]["myProfile"]["sensitivedata"]["ssn"]
== profile.sensitivedata.ssn
)


@mock.patch("logging.warning")
def test_enable_allowed_data_fields_restriction_flag_logs_warning_if_access_to_restricted_field(
mock_log, user_gql_client, service, settings
):
settings.ENABLE_ALLOWED_DATA_FIELDS_RESTRICTION = False
profile = ProfileFactory(user=user_gql_client.user)
ServiceConnectionFactory(profile=profile, service=service)
service.allowed_data_fields.add(AllowedDataFieldFactory(field_name="name"))
SensitiveDataFactory(profile=profile)

query = """
{
myProfile {
firstName
sensitivedata {
ssn
}
}
}
"""

user_gql_client.execute(query, service=service)

assert mock_log.call_count == 1

0 comments on commit 95193fe

Please sign in to comment.