Skip to content

Commit

Permalink
Functional tests for authenticator_map serialization logic
Browse files Browse the repository at this point in the history
Signed-off-by: Rick Elrod <[email protected]>
  • Loading branch information
relrod committed Oct 31, 2023
1 parent 9173416 commit a5fe380
Show file tree
Hide file tree
Showing 3 changed files with 281 additions and 5 deletions.
6 changes: 3 additions & 3 deletions ansible_base/serializers/authenticator_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def validate(self, data) -> dict:
if map_type == 'team' and not org:
errors["organization"] = "You must specify an organization with the selected map type"

if not data.get('order', None):
if data.get('order', None) is None:
errors['order'] = "Must be a valid integer"

if errors:
Expand All @@ -42,7 +42,7 @@ def validate_trigger_data(self, triggers: dict, definition, error_prefix: str) -
for trigger_type in triggers.keys():
type_definition = definition.get(trigger_type, definition.get('*', None))
if not type_definition:
errors[f'{error_prefix}.{trigger_type}'] = f"Is invalid, can only be one of {list(definition.keys())}"
errors[f'{error_prefix}.{trigger_type}'] = f"Invalid, can only be one of: {', '.join(definition.keys())}"
continue

# Validate the type we got is what we expect
Expand All @@ -55,7 +55,7 @@ def validate_trigger_data(self, triggers: dict, definition, error_prefix: str) -
elif isinstance(triggers[trigger_type], str):
if 'choices' in type_definition:
if triggers[trigger_type] not in type_definition['choices']:
errors[f'{error_prefix}.{trigger_type}'] = f"Invalid, can only be one of {', '.join(type_definition['choices'])}"
errors[f'{error_prefix}.{trigger_type}'] = f"Invalid, can only be one of: {', '.join(type_definition['choices'])}"
elif isinstance(triggers[trigger_type], list):
if 'contents' in type_definition:
for item in triggers[trigger_type]:
Expand Down
276 changes: 276 additions & 0 deletions ansible_base/tests/functional/authentication/test_authenticator_map.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
from django.urls import reverse
import pytest


def test_authenticator_map_list_empty_by_default(admin_api_client):
"""
Test that we can list authenticator maps. No maps are listed by default.
"""
url = reverse("authenticator-map-list")
response = admin_api_client.get(url)
assert response.status_code == 200
assert response.data == []


def test_authenticator_map_list(admin_api_client, local_authenticator_map):
"""
Test that we can list authenticator maps, if they exist.
"""
url = reverse("authenticator-map-list")
response = admin_api_client.get(url)
assert response.status_code == 200
assert len(response.data) == 1
assert response.data[0]['id'] == local_authenticator_map.id
assert response.data[0]['triggers'] == local_authenticator_map.triggers


def test_authenticator_map_detail(admin_api_client, local_authenticator_map):
"""
Test that we can get a single authenticator map.
"""
url = reverse("authenticator-map-detail", kwargs={'pk': local_authenticator_map.id})
response = admin_api_client.get(url)
assert response.status_code == 200
assert response.data['id'] == local_authenticator_map.id
assert response.data['triggers'] == local_authenticator_map.triggers


@pytest.mark.parametrize(
'triggers',
[
{'always': {}},
{'never': {}},
{'groups': {'has_or': ['foobar-group']}},
{'attributes': {'email': {'equals': '[email protected]'}}},
{'attributes': {'email': {'in': ['[email protected]', '[email protected]']}}},
],
)
def test_authenticator_map_create(admin_api_client, local_authenticator, triggers, shut_up_logging):
"""
Test that we can create an authenticator map.
"""
url = reverse("authenticator-map-list")
data = {
'authenticator': local_authenticator.id,
'map_type': 'is_superuser',
'triggers': triggers,
'organization': 'foobar-org',
'team': 'foobar-team',
'order': 1,
}
response = admin_api_client.post(url, data=data, format='json')
assert response.status_code == 201, response.data
assert response.data['id'] == local_authenticator.id
assert response.data['triggers'] == triggers
assert response.data['map_type'] == 'is_superuser'


def test_authenticator_map_invalid_map_type(admin_api_client, local_authenticator, shut_up_logging):
"""
Invalid map_type should be rejected.
"""
url = reverse("authenticator-map-list")
data = {
'authenticator': local_authenticator.id,
'map_type': 'invalid',
'triggers': {'always': {}},
'organization': 'foobar-org',
'team': 'foobar-team',
'order': 1,
}
response = admin_api_client.post(url, data=data, format='json')
assert response.status_code == 400, response.data
assert '"invalid" is not a valid choice.' in response.data['map_type'][0]


@pytest.mark.parametrize(
'map_type, params, error_field, error_message',
[
pytest.param(
'is_superuser',
{'map_type': 'team', 'triggers': {'always': {}}, 'order': 1, 'organization': 'foobar-org'},
'team',
'You must specify a team with the selected map type',
id="map_type=team, missing team param",
),
pytest.param(
'is_superuser',
{'map_type': 'team', 'triggers': {'always': {}}, 'order': 1, 'organization': 'foobar-org', 'team': None},
'team',
'You must specify a team with the selected map type',
id="map_type=team, team param is None",
),
pytest.param(
'is_superuser',
{'map_type': 'team', 'triggers': {'always': {}}, 'order': 1, 'organization': 'foobar-org', 'team': ''},
'team',
"This field may not be blank.",
id="map_type=team, team param is empty string",
),
pytest.param(
'is_superuser',
{'map_type': 'team', 'triggers': {'always': {}}, 'order': 1, 'team': 'foobar-team'},
'organization',
"You must specify an organization with the selected map type",
id="map_type=team, missing organization param",
),
pytest.param(
'is_superuser',
{'map_type': 'team', 'triggers': {'always': {}}, 'order': 1, 'team': 'foobar-team', 'organization': None},
'organization',
"You must specify an organization with the selected map type",
id="map_type=team, organization param is None",
),
pytest.param(
'is_superuser',
{'map_type': 'team', 'triggers': {'always': {}}, 'order': 1, 'team': 'foobar-team', 'organization': ''},
'organization',
"This field may not be blank.",
id="map_type=team, organization param is empty string",
),
pytest.param(
'is_superuser',
{'map_type': 'is_superuser', 'order': 1},
'triggers',
"Triggers must be a valid dict",
id="map_type=is_superuser, missing triggers param",
),
pytest.param(
'is_superuser',
{'map_type': 'is_superuser', 'triggers': {'always': {}}, 'order': "hey"},
'order',
"A valid integer is required.",
id="map_type=is_superuser, order param is a non-digit string",
),
pytest.param(
'is_superuser',
{'map_type': 'is_superuser', 'triggers': {'always': {}}},
'order',
"Must be a valid integer",
id="map_type=is_superuser, missing order param",
),
],
)
def test_authenticator_map_validate(admin_api_client, local_authenticator, shut_up_logging, map_type, params, error_field, error_message):
"""
Create invalid authenticator maps and ensure that they are rejected.
"""
url = reverse("authenticator-map-list")
data = {
'authenticator': local_authenticator.id,
}
data.update(params)
response = admin_api_client.post(url, data=data, format='json')
assert response.status_code == 400, response.data
assert error_message in response.data[error_field]


@pytest.mark.parametrize(
'triggers, error_field, error_message',
[
pytest.param(
None,
"triggers",
"This field may not be null.",
id="triggers is None",
),
pytest.param(
{},
"triggers",
"Triggers must be a valid dict",
id="triggers is empty dict",
),
pytest.param(
{'invalid_key': {}},
"triggers.invalid_key",
"Invalid, can only be one of",
id="triggers is dict with invalid key",
),
pytest.param(
{'always': 'not a dict'},
"triggers.always",
"Expected dict but got str",
id="triggers always is invalid type",
),
pytest.param(
{'never': 'not a dict'},
"triggers.never",
"Expected dict but got str",
id="triggers never is invalid type",
),
pytest.param(
{'groups': 'not a dict'},
"triggers.groups",
"Expected dict but got str",
id="triggers groups is invalid type",
),
pytest.param(
{'attributes': 'not a dict'},
"triggers.attributes",
"Expected dict but got str",
id="triggers attributes is invalid type",
),
pytest.param(
{'attributes': {"email": {"equals": "[email protected]"}, "join_condition": True}},
"triggers.attributes.join_condition",
"Expected str but got bool",
id="triggers attributes join condition is invalid type (recursive validation)",
),
pytest.param(
{'attributes': {"email": {"invalid_predicate": "[email protected]"}, "join_condition": "and"}},
"triggers.attributes.email.invalid_predicate",
"Invalid, can only be one of",
id="triggers attributes invalid predicate is supplied (recursive validation)",
),
pytest.param(
{'attributes': {"email": {"equals": "[email protected]"}, "join_condition": "invalid"}},
"triggers.attributes.join_condition",
"Invalid, can only be one of",
id="triggers attributes invalid join condition is supplied",
),
pytest.param(
{'groups': {"has_or": [1, 2]}},
"triggers.groups.has_or.1",
"Invalid, must be of type str",
id="triggers groups has_or has invalid type elements (int)",
),
pytest.param(
{'groups': {"has_or": [True]}},
"triggers.groups.has_or.True",
"Invalid, must be of type str",
id="triggers groups has_or has invalid type elements (bool)",
),
pytest.param(
{'groups': {"has_or": [None]}},
"triggers.groups.has_or.None",
"Invalid, must be of type str",
id="triggers groups has_or has invalid type elements (None)",
),
pytest.param(
{'groups': {"has_or": "seven"}},
"triggers.groups.has_or",
"Expected list but got str",
id="triggers groups has_or is not list",
),
pytest.param(
{'groups': 1337},
"triggers.groups",
"Expected dict but got int",
id="triggers groups is not dict",
),
],
)
def test_authenticator_map_validate_trigger_data(admin_api_client, local_authenticator, shut_up_logging, triggers, error_field, error_message):
"""
Test trigger validation for authenticator maps.
"""
url = reverse("authenticator-map-list")
data = {
'triggers': triggers,
'map_type': 'is_superuser',
'order': 1,
'authenticator': local_authenticator.id,
}
response = admin_api_client.post(url, data=data, format='json')
assert response.status_code == 400, response.data
assert error_message in response.data[error_field][0]
4 changes: 2 additions & 2 deletions ansible_base/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
name='authenticator-authenticator-map',
),
# Maps
path('authenticator_maps/', views.AuthenticatorMapViewSet.as_view(list_actions), name='authenticator_map-list'),
re_path(r'authenticator_maps/(?P<pk>[0-9]+)/$', views.AuthenticatorMapViewSet.as_view(detail_actions), name='authenticator_map-detail'),
path('authenticator_maps/', views.AuthenticatorMapViewSet.as_view(list_actions), name='authenticator-map-list'),
re_path(r'authenticator_maps/(?P<pk>[0-9]+)/$', views.AuthenticatorMapViewSet.as_view(detail_actions), name='authenticator-map-detail'),
# Plugin List
path('authenticator_plugins/', views.AuthenticatorPluginView.as_view(), name='authenticator_plugin-view'),
# Trigger definition
Expand Down

0 comments on commit a5fe380

Please sign in to comment.