More oidc tests test_login_callback_requires_step_up_auth and test_login_callback_no_step_up_auth, lint

This commit is contained in:
Rachid Mrad 2023-12-07 16:33:35 -05:00
parent 1a2b16a3da
commit 1001454a85
No known key found for this signature in database
GPG key ID: EF38E4CEC4A8F3CF
5 changed files with 82 additions and 57 deletions

View file

@ -1,9 +1,9 @@
from unittest.mock import MagicMock, patch
from django.http import HttpResponse, HttpResponseRedirect
from django.http import HttpResponse
from django.test import Client, TestCase, RequestFactory
from django.urls import reverse
from ..views import login_callback, requires_step_up_auth
from ..views import login_callback
from .common import less_console_noise
@ -61,46 +61,32 @@ class ViewsTest(TestCase):
# mock
mock_client.callback.side_effect = self.user_info
# test
with patch("djangooidc.views.requires_step_up_auth", return_value=False), \
less_console_noise():
with patch("djangooidc.views.requires_step_up_auth", return_value=False), less_console_noise():
response = self.client.get(reverse("openid_login_callback"))
# assert
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse("logout"))
def test_login_callback_no_step_up_auth(self, mock_client):
"""Walk through login_callback when requires_step_up_auth returns False
and assert that we have a redirect to /"""
# setup
session = self.client.session
session.save()
# mock
mock_client.callback.side_effect = self.user_info
# test
with patch("djangooidc.views.requires_step_up_auth", return_value=False), \
less_console_noise():
with patch("djangooidc.views.requires_step_up_auth", return_value=False), less_console_noise():
response = self.client.get(reverse("openid_login_callback"))
# assert
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "/")
@patch.object(requires_step_up_auth, return_value=True)
def test_login_callback_requires_step_up_auth(self, mock_client):
# setup
callback_url = reverse("openid_login_callback")
# session = self.client.session
# session.save()
# mock
# mock_client.callback.side_effect = self.user_info
# mock_client.create_authn_request.side_effect = self.say_hi
# test
# with patch("djangooidc.views.requires_step_up_auth", return_value=True):
response = self.client.get(reverse("openid_login_callback"))
# assert
# self.assertEqual(response.status_code, 200)
# self.assertContains(response, "Hi")
def test_requires_step_up_auth(self, mock_client):
"""Invoke login_callback passing it a request when requires_step_up_auth returns True
and assert that session is updated and create_authn_request (mock) is called.
Possibly redundant with test_login_callback_no_step_up_auth"""
# Configure the mock to return an expected value for get_step_up_acr_value
mock_client.return_value.get_step_up_acr_value.return_value = "step_up_acr_value"
@ -110,8 +96,9 @@ class ViewsTest(TestCase):
# Ensure that the CLIENT instance used in login_callback is the mock
# patch requires_step_up_auth to return True
with patch("djangooidc.views.requires_step_up_auth", return_value=True), \
patch("djangooidc.views.CLIENT.create_authn_request", return_value=MagicMock()) as mock_create_authn_request:
with patch("djangooidc.views.requires_step_up_auth", return_value=True), patch(
"djangooidc.views.CLIENT.create_authn_request", return_value=MagicMock()
) as mock_create_authn_request:
login_callback(request)
# Assert that get_step_up_acr_value was called and session was updated
@ -120,14 +107,19 @@ class ViewsTest(TestCase):
mock_create_authn_request.assert_called_once()
def test_does_not_requires_step_up_auth(self, mock_client):
"""Invoke login_callback passing it a request when requires_step_up_auth returns False
and assert that session is not updated and create_authn_request (mock) is not called.
Possibly redundant with test_login_callback_requires_step_up_auth"""
# Create a mock request
request = self.factory.get("/some-url")
request.session = {"acr_value": ""}
# Ensure that the CLIENT instance used in login_callback is the mock
# patch requires_step_up_auth to return False
with patch("djangooidc.views.requires_step_up_auth", return_value=False), \
patch("djangooidc.views.CLIENT.create_authn_request", return_value=MagicMock()) as mock_create_authn_request:
with patch("djangooidc.views.requires_step_up_auth", return_value=False), patch(
"djangooidc.views.CLIENT.create_authn_request", return_value=MagicMock()
) as mock_create_authn_request:
login_callback(request)
# Assert that get_step_up_acr_value was NOT called and session was NOT updated
@ -141,8 +133,7 @@ class ViewsTest(TestCase):
mock_client.callback.side_effect = self.user_info
mock_auth.return_value = None
# test
with patch("djangooidc.views.requires_step_up_auth", return_value=False), \
less_console_noise():
with patch("djangooidc.views.requires_step_up_auth", return_value=False), less_console_noise():
response = self.client.get(reverse("openid_login_callback"))
# assert
self.assertEqual(response.status_code, 401)
@ -189,3 +180,34 @@ class ViewsTest(TestCase):
# assert
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse("logout"))
class ViewsTestUnpatched(TestCase):
def setUp(self):
self.client = Client()
self.factory = RequestFactory()
def say_hi(*args):
return HttpResponse("Hi")
def user_info(*args):
return {
"sub": "TEST",
"email": "test@example.com",
"first_name": "Testy",
"last_name": "Tester",
"phone": "814564000",
}
def test_login_callback_requires_step_up_auth(self):
"""Walk through login_callback when requires_step_up_auth returns True
and assert that create_authn_request is returned."""
with patch("djangooidc.views.requires_step_up_auth", return_value=True), patch(
"djangooidc.views.Client.callback", return_value=self.user_info
), patch("djangooidc.views.Client.create_authn_request", side_effect=self.say_hi):
response = self.client.get(reverse("openid_login_callback"))
# assert
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Hi")

View file

@ -70,13 +70,10 @@ def login_callback(request):
userinfo = CLIENT.callback(query, request.session)
# test for need for identity verification and if it is satisfied
# if not satisfied, redirect user to login with stepped up acr_value
logger.info('login_callback start')
if requires_step_up_auth(userinfo):
# add acr_value to request.session
logger.info('login_callback inside requires_step_up_auth')
request.session["acr_value"] = CLIENT.get_step_up_acr_value()
logger.info('login_callback after get_step_up_acr_value')
# return CLIENT.create_authn_request(request.session)
return CLIENT.create_authn_request(request.session)
user = authenticate(request=request, **userinfo)
if user:
login(request, user)
@ -87,15 +84,17 @@ def login_callback(request):
except Exception as err:
return error_page(request, err)
def requires_step_up_auth(userinfo):
""" if User.needs_identity_verification and step_up_acr_value not in
ial returned from callback, return True """
"""if User.needs_identity_verification and step_up_acr_value not in
ial returned from callback, return True"""
step_up_acr_value = CLIENT.get_step_up_acr_value()
acr_value = userinfo.get("ial", "")
uuid = userinfo.get("sub", "")
email = userinfo.get("email", "")
return User.needs_identity_verification(email, uuid) and acr_value != step_up_acr_value
def logout(request, next_page=None):
"""Redirect the user to the authentication provider (OP) logout page."""
try:
@ -125,6 +124,7 @@ def logout(request, next_page=None):
if next_page:
request.session["next"] = next_page
def logout_callback(request):
"""Simple redirection view: after logout, redirect to `next`."""
next = request.session.get("next", "/")

View file

@ -71,14 +71,17 @@ class User(AbstractUser):
"""A method used by our oidc classes to test whether a user needs email/uuid verification
or the full identity PII verification"""
# An existing user who is a domain manager of a domain (that is,
# they have an entry in UserDomainRole for their User)
# An existing user who is a domain manager of a domain (that is,
# they have an entry in UserDomainRole for their User)
try:
existing_user = cls.objects.get(username=uuid)
if existing_user and UserDomainRole.objects.filter(user=existing_user).exists():
return False
except:
except cls.DoesNotExist:
# Do nothing when the user is not found, as we're checking for existence.
pass
except Exception as err:
raise err
# A new incoming user who is a domain manager for one of the domains
# that we inputted from Verisign (that is, their email address appears