blob: ecd75c5274dee22f2cb2d7a4d4f86b806aafd829 [file] [log] [blame]
from unittest.mock import MagicMock, patch
from django.contrib.auth.models import AnonymousUser
from django.core import mail
from django.test import RequestFactory, TestCase, override_settings
from django_airavata.apps.auth import backends
KEYCLOAK_CLIENT_ID = "kc-client"
KEYCLOAK_CLIENT_SECRET = "kc-secret"
KEYCLOAK_TOKEN_URL = "https://example.org/auth"
KEYCLOAK_USERINFO_URL = "https://example.org/userinfo"
KEYCLOAK_VERIFY_SSL = True
AUTHENTICATION_OPTIONS = {
'external': [
{
'idp_alias': 'oidc',
'name': 'Some OIDC compliant IDP',
}
]
}
GATEWAY_ID = "gateway-id"
@override_settings(
KEYCLOAK_CLIENT_ID=KEYCLOAK_CLIENT_ID,
KEYCLOAK_CLIENT_SECRET=KEYCLOAK_CLIENT_SECRET,
KEYCLOAK_TOKEN_URL=KEYCLOAK_TOKEN_URL,
KEYCLOAK_USERINFO_URL=KEYCLOAK_USERINFO_URL,
KEYCLOAK_VERIFY_SSL=KEYCLOAK_VERIFY_SSL,
AUTHENTICATION_OPTIONS=AUTHENTICATION_OPTIONS,
GATEWAY_ID=GATEWAY_ID,
PORTAL_ADMINS=[('Admin Name', 'admin@example.org')],
)
class KeycloakBackendTestCase(TestCase):
def setUp(self):
self.factory = RequestFactory()
@patch("django_airavata.apps.auth.backends.OAuth2Session")
def test_username_initialized_with_email(self, MockOAuth2Session):
"""Test that username_initialized is set to True when username equals email address."""
# Tests scenario that new user logs in via external IDP and when they
# are assigned a username it is the same as their email address. This is
# normally what happens and is a good outcome for the username so
# username_initialized should be set to True and no alert email should
# be sent to admins.
# Mock out request for redirect flow, and OAuth2Session: token and userinfo
request = self.factory.get("/callback?code=abc123", secure=True)
request.user = AnonymousUser()
request.session = {
'OAUTH2_STATE': 'state',
'OAUTH2_REDIRECT_URI': 'redirect-uri',
}
mock_oauth2_session = MagicMock()
MockOAuth2Session.return_value = mock_oauth2_session
mock_oauth2_session.fetch_token.return_value = {
'access_token': 'the-access-token',
'expires_in': 900,
'refresh_token': 'the-refresh-token',
'refresh_expires_in': 86400,
}
mock_userinfo = MagicMock()
mock_oauth2_session.get.return_value = mock_userinfo
email = 'testuser@example.org'
mock_userinfo.json.return_value = {
'sub': 'sub-123',
'preferred_username': email,
'email': email,
'given_name': 'Test',
'family_name': 'User',
}
# Mock out fetching IDP userinfo: AUTHENTICATION_OPTIONS: request to
# idp_token_url and userinfo_url
backend = backends.KeycloakBackend()
idp_alias = "oidc"
user = backend.authenticate(request, idp_alias=idp_alias)
self.assertTrue(user.user_profile.username_initialized)
self.assertEqual(0, len(mail.outbox))
@patch("django_airavata.apps.auth.backends.OAuth2Session")
def test_username_initialized_with_no_email(self, MockOAuth2Session):
"""Test that username_initialized is set to False when there is no email address."""
# Tests scenario that new user logs in via external IDP and when they
# are assigned a username but don't have an email address. This usually
# means that they also have a randomly generated username and admins
# need to be alerted.
# Mock out request for redirect flow, and OAuth2Session: token and userinfo
request = self.factory.get("/callback?code=abc123", secure=True)
request.user = AnonymousUser()
request.session = {
'OAUTH2_STATE': 'state',
'OAUTH2_REDIRECT_URI': 'redirect-uri',
}
mock_oauth2_session = MagicMock()
MockOAuth2Session.return_value = mock_oauth2_session
mock_oauth2_session.fetch_token.return_value = {
'access_token': 'the-access-token',
'expires_in': 900,
'refresh_token': 'the-refresh-token',
'refresh_expires_in': 86400,
}
mock_userinfo = MagicMock()
mock_oauth2_session.get.return_value = mock_userinfo
# email = 'testuser@example.org'
mock_userinfo.json.return_value = {
'sub': 'sub-123',
'preferred_username': 'some-random-&invalid//-username',
# 'email': email,
'given_name': 'Test',
'family_name': 'User',
}
# Mock out fetching IDP userinfo: AUTHENTICATION_OPTIONS: request to
# idp_token_url and userinfo_url
backend = backends.KeycloakBackend()
idp_alias = "oidc"
user = backend.authenticate(request, idp_alias=idp_alias)
self.assertFalse(user.user_profile.userinfo_set.filter(claim='email').exists())
self.assertFalse(user.user_profile.username_initialized)
self.assertEqual(1, len(mail.outbox))
self.assertTrue(mail.outbox[0].subject.startswith("Please fix username"))
@patch("django_airavata.apps.auth.backends.OAuth2Session")
def test_username_initialized_with_email_not_username(self, MockOAuth2Session):
"""Test that username_initialized is set to False when email address is different from username."""
# Tests scenario that new user logs in via external IDP and when they
# are assigned a username but it's different from their email address.
# This usually means that they also have a randomly generated username
# and admins need to be alerted.
# Mock out request for redirect flow, and OAuth2Session: token and userinfo
request = self.factory.get("/callback?code=abc123", secure=True)
request.user = AnonymousUser()
request.session = {
'OAUTH2_STATE': 'state',
'OAUTH2_REDIRECT_URI': 'redirect-uri',
}
mock_oauth2_session = MagicMock()
MockOAuth2Session.return_value = mock_oauth2_session
mock_oauth2_session.fetch_token.return_value = {
'access_token': 'the-access-token',
'expires_in': 900,
'refresh_token': 'the-refresh-token',
'refresh_expires_in': 86400,
}
mock_userinfo = MagicMock()
mock_oauth2_session.get.return_value = mock_userinfo
email = 'testuser@example.org'
mock_userinfo.json.return_value = {
'sub': 'sub-123',
'preferred_username': 'some-random-&invalid//-username',
'email': email,
'given_name': 'Test',
'family_name': 'User',
}
# Mock out fetching IDP userinfo: AUTHENTICATION_OPTIONS: request to
# idp_token_url and userinfo_url
backend = backends.KeycloakBackend()
idp_alias = "oidc"
user = backend.authenticate(request, idp_alias=idp_alias)
self.assertTrue(user.user_profile.userinfo_set.filter(claim='email').exists())
self.assertFalse(user.user_profile.username_initialized)
self.assertEqual(1, len(mail.outbox))
self.assertTrue(mail.outbox[0].subject.startswith("Please fix username"))