mirror of
https://github.com/zulip/zulip.git
synced 2025-11-04 14:03:30 +00:00
tests: Extract SocialAuthBase class.
Extracts out common tests so that future social-auth backends can be tested without duplicating tests. I have been careful to not change any testing logic.
This commit is contained in:
committed by
Tim Abbott
parent
61ebee6993
commit
ae3881b472
@@ -10,6 +10,7 @@ from typing import Any, Callable, Dict, List, Optional, Set, Tuple
|
||||
from oauth2client.crypt import AppIdentityError
|
||||
from django.core import signing
|
||||
from django.urls import reverse
|
||||
|
||||
import httpretty
|
||||
import os
|
||||
|
||||
@@ -404,12 +405,25 @@ class ResponseMock:
|
||||
def text(self) -> str:
|
||||
return "Response text"
|
||||
|
||||
class GitHubAuthBackendTest(ZulipTestCase):
|
||||
class SocialAuthBase(ZulipTestCase):
|
||||
"""This is a base class for testing social-auth backends. Following
|
||||
methods can be overrided by subclasses as per the backend:
|
||||
|
||||
registerExtraEndpoints() - If the backend being tested calls some extra
|
||||
endpoints then they can be added here.
|
||||
|
||||
get_account_data_dict() - Return the data returned by the user info endpoint
|
||||
according to the respective backend.
|
||||
"""
|
||||
# Don't run base class tests, make sure to set it to False
|
||||
# in subclass otherwise its tests will not run.
|
||||
__unittest_skip__ = True
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.user_profile = self.example_user('hamlet')
|
||||
self.email = self.user_profile.email
|
||||
self.name = 'Hamlet'
|
||||
self.backend = GitHubAuthBackend()
|
||||
self.backend = self.BACKEND_CLASS
|
||||
self.backend.strategy = DjangoStrategy(storage=BaseDjangoStorage())
|
||||
self.user_profile.backend = self.backend
|
||||
|
||||
@@ -421,13 +435,13 @@ class GitHubAuthBackendTest(ZulipTestCase):
|
||||
from social_core.backends.utils import load_backends
|
||||
load_backends(settings.AUTHENTICATION_BACKENDS, force_load=True)
|
||||
|
||||
def github_oauth2_test(self, account_data_dict: Dict[str, str],
|
||||
def social_auth_test(self, account_data_dict: Dict[str, str],
|
||||
*, subdomain: Optional[str]=None,
|
||||
mobile_flow_otp: Optional[str]=None,
|
||||
is_signup: Optional[str]=None,
|
||||
email_data: Optional[List[Dict[str, Any]]]=None,
|
||||
next: str='') -> HttpResponse:
|
||||
url = "/accounts/login/social/github"
|
||||
next: str='',
|
||||
**extra_data: Any) -> HttpResponse:
|
||||
url = self.LOGIN_URL
|
||||
params = {}
|
||||
headers = {}
|
||||
if subdomain is not None:
|
||||
@@ -436,89 +450,70 @@ class GitHubAuthBackendTest(ZulipTestCase):
|
||||
params['mobile_flow_otp'] = mobile_flow_otp
|
||||
headers['HTTP_USER_AGENT'] = "ZulipAndroid"
|
||||
if is_signup is not None:
|
||||
url = "/accounts/register/social/github"
|
||||
url = self.SIGNUP_URL
|
||||
params['next'] = next
|
||||
if len(params) > 0:
|
||||
url += "?%s" % (urllib.parse.urlencode(params))
|
||||
|
||||
result = self.client_get(url, **headers)
|
||||
|
||||
expected_result_url_prefix = 'http://testserver/login/github/'
|
||||
expected_result_url_prefix = 'http://testserver/login/%s/' % (self.backend.name,)
|
||||
if settings.SOCIAL_AUTH_SUBDOMAIN is not None:
|
||||
expected_result_url_prefix = ('http://%s.testserver/login/github/' %
|
||||
settings.SOCIAL_AUTH_SUBDOMAIN)
|
||||
expected_result_url_prefix = ('http://%s.testserver/login/%s/' %
|
||||
(settings.SOCIAL_AUTH_SUBDOMAIN, self.backend.name,))
|
||||
|
||||
if result.status_code != 302 or not result.url.startswith(expected_result_url_prefix):
|
||||
return result
|
||||
|
||||
result = self.client_get(result.url, **headers)
|
||||
self.assertEqual(result.status_code, 302)
|
||||
assert 'https://github.com/login/oauth/authorize' in result.url
|
||||
assert self.AUTHORIZATION_URL in result.url
|
||||
|
||||
self.client.cookies = result.cookies
|
||||
|
||||
# Next, the browser requests result["Location"], and gets
|
||||
# redirected back to /complete/github.
|
||||
# redirected back to the registered redirect uri.
|
||||
|
||||
token_data_dict = {
|
||||
'access_token': 'foobar',
|
||||
'token_type': 'bearer'
|
||||
}
|
||||
|
||||
if not email_data:
|
||||
# Keeping a verified email before the primary email makes sure
|
||||
# get_verified_emails puts the primary email at the start of the
|
||||
# email list returned as social_associate_user_helper assumes the
|
||||
# first email as the primary email.
|
||||
email_data = [
|
||||
dict(email="notprimary@example.com",
|
||||
verified=True),
|
||||
dict(email=account_data_dict["email"],
|
||||
verified=True,
|
||||
primary=True),
|
||||
dict(email="ignored@example.com",
|
||||
verified=False),
|
||||
]
|
||||
# We register callbacks for the key URLs on github.com that
|
||||
# /complete/github will call
|
||||
# We register callbacks for the key URLs on Identity Provider that
|
||||
# auth completion url will call
|
||||
httpretty.enable()
|
||||
httpretty.register_uri(
|
||||
httpretty.POST,
|
||||
"https://github.com/login/oauth/access_token",
|
||||
self.ACCESS_TOKEN_URL,
|
||||
match_querystring=False,
|
||||
status=200,
|
||||
body=json.dumps(token_data_dict))
|
||||
httpretty.register_uri(
|
||||
httpretty.GET,
|
||||
"https://api.github.com/user",
|
||||
self.USER_INFO_URL,
|
||||
status=200,
|
||||
body=json.dumps(account_data_dict)
|
||||
)
|
||||
httpretty.register_uri(
|
||||
httpretty.GET,
|
||||
"https://api.github.com/user/emails",
|
||||
status=200,
|
||||
body=json.dumps(email_data)
|
||||
)
|
||||
self.registerExtraEndpoints(account_data_dict, **extra_data)
|
||||
|
||||
parsed_url = urllib.parse.urlparse(result.url)
|
||||
csrf_state = urllib.parse.parse_qs(parsed_url.query)['state']
|
||||
result = self.client_get("/complete/github/",
|
||||
result = self.client_get(self.AUTH_FINISH_URL,
|
||||
dict(state=csrf_state), **headers)
|
||||
httpretty.disable()
|
||||
return result
|
||||
|
||||
@override_settings(SOCIAL_AUTH_GITHUB_KEY=None)
|
||||
def test_github_oauth2_no_key(self) -> None:
|
||||
account_data_dict = dict(email=self.email, name=self.name)
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
def test_social_auth_no_key(self) -> None:
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name=self.name)
|
||||
with self.settings(**{self.CLIENT_KEY_SETTING: None}):
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip', next='/user_uploads/image')
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, "/config-error/github")
|
||||
self.assertEqual(result.url, self.CONFIG_ERROR_URL)
|
||||
|
||||
def test_github_oauth2_success(self) -> None:
|
||||
account_data_dict = dict(email=self.email, name=self.name)
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
def test_social_auth_success(self) -> None:
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name=self.name)
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip', next='/user_uploads/image')
|
||||
data = load_subdomain_token(result)
|
||||
self.assertEqual(data['email'], self.example_email("hamlet"))
|
||||
@@ -532,10 +527,11 @@ class GitHubAuthBackendTest(ZulipTestCase):
|
||||
self.assertTrue(uri.startswith('http://zulip.testserver/accounts/login/subdomain/'))
|
||||
|
||||
@override_settings(SOCIAL_AUTH_SUBDOMAIN=None)
|
||||
def test_github_when_social_auth_subdomain_is_not_set(self) -> None:
|
||||
account_data_dict = dict(email=self.email, name=self.name)
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
subdomain='zulip', next='/user_uploads/image')
|
||||
def test_when_social_auth_subdomain_is_not_set(self) -> None:
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name=self.name)
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip',
|
||||
next='/user_uploads/image')
|
||||
data = load_subdomain_token(result)
|
||||
self.assertEqual(data['email'], self.example_email("hamlet"))
|
||||
self.assertEqual(data['name'], 'Hamlet')
|
||||
@@ -547,107 +543,43 @@ class GitHubAuthBackendTest(ZulipTestCase):
|
||||
parsed_url.path)
|
||||
self.assertTrue(uri.startswith('http://zulip.testserver/accounts/login/subdomain/'))
|
||||
|
||||
def test_github_oauth2_email_not_verified(self) -> None:
|
||||
account_data_dict = dict(email=self.email, name=self.name)
|
||||
email_data = [
|
||||
dict(email=account_data_dict["email"],
|
||||
verified=False,
|
||||
primary=True),
|
||||
]
|
||||
with mock.patch('logging.warning') as mock_warning:
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
subdomain='zulip',
|
||||
email_data=email_data)
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, "/login/")
|
||||
mock_warning.assert_called_once_with("Social auth (GitHub) failed "
|
||||
"because user has no verified emails")
|
||||
|
||||
@override_settings(SOCIAL_AUTH_GITHUB_TEAM_ID='zulip-webapp')
|
||||
def test_github_oauth2_github_team_not_member_failed(self) -> None:
|
||||
account_data_dict = dict(email=self.email, name=self.name)
|
||||
with mock.patch('social_core.backends.github.GithubTeamOAuth2.user_data',
|
||||
side_effect=AuthFailed('Not found')), \
|
||||
mock.patch('logging.info') as mock_info:
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
subdomain='zulip')
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, "/login/")
|
||||
mock_info.assert_called_once_with("GitHub user is not member of required team")
|
||||
|
||||
@override_settings(SOCIAL_AUTH_GITHUB_TEAM_ID='zulip-webapp')
|
||||
def test_github_oauth2_github_team_member_success(self) -> None:
|
||||
account_data_dict = dict(email=self.email, name=self.name)
|
||||
with mock.patch('social_core.backends.github.GithubTeamOAuth2.user_data',
|
||||
return_value=account_data_dict):
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
subdomain='zulip')
|
||||
data = load_subdomain_token(result)
|
||||
self.assertEqual(data['email'], self.example_email("hamlet"))
|
||||
self.assertEqual(data['name'], 'Hamlet')
|
||||
self.assertEqual(data['subdomain'], 'zulip')
|
||||
|
||||
@override_settings(SOCIAL_AUTH_GITHUB_ORG_NAME='Zulip')
|
||||
def test_github_oauth2_github_organization_not_member_failed(self) -> None:
|
||||
account_data_dict = dict(email=self.email, name=self.name)
|
||||
with mock.patch('social_core.backends.github.GithubOrganizationOAuth2.user_data',
|
||||
side_effect=AuthFailed('Not found')), \
|
||||
mock.patch('logging.info') as mock_info:
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
subdomain='zulip')
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, "/login/")
|
||||
mock_info.assert_called_once_with("GitHub user is not member of required organization")
|
||||
|
||||
@override_settings(SOCIAL_AUTH_GITHUB_ORG_NAME='Zulip')
|
||||
def test_github_oauth2_github_organization_member_success(self) -> None:
|
||||
account_data_dict = dict(email=self.email, name=self.name)
|
||||
with mock.patch('social_core.backends.github.GithubOrganizationOAuth2.user_data',
|
||||
return_value=account_data_dict):
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
subdomain='zulip')
|
||||
data = load_subdomain_token(result)
|
||||
self.assertEqual(data['email'], self.example_email("hamlet"))
|
||||
self.assertEqual(data['name'], 'Hamlet')
|
||||
self.assertEqual(data['subdomain'], 'zulip')
|
||||
|
||||
def test_github_oauth2_deactivated_user(self) -> None:
|
||||
def test_social_auth_deactivated_user(self) -> None:
|
||||
user_profile = self.example_user("hamlet")
|
||||
do_deactivate_user(user_profile)
|
||||
account_data_dict = dict(email=self.email, name=self.name)
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name=self.name)
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip')
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, "/login/")
|
||||
# TODO: verify whether we provide a clear error message
|
||||
|
||||
def test_github_oauth2_invalid_realm(self) -> None:
|
||||
account_data_dict = dict(email=self.email, name=self.name)
|
||||
def test_social_auth_invalid_realm(self) -> None:
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name=self.name)
|
||||
with mock.patch('zerver.middleware.get_realm', return_value=get_realm("zulip")):
|
||||
# This mock.patch case somewhat hackishly arranges it so
|
||||
# that we switch realms halfway through the test
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='invalid', next='/user_uploads/image')
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, "/accounts/login/?subdomain=1")
|
||||
|
||||
def test_github_oauth2_invalid_email(self) -> None:
|
||||
account_data_dict = dict(email="invalid", name=self.name)
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
def test_social_auth_invalid_email(self) -> None:
|
||||
account_data_dict = self.get_account_data_dict(email="invalid", name=self.name)
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip', next='/user_uploads/image')
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, "/login/?next=/user_uploads/image")
|
||||
|
||||
def test_user_cannot_log_into_nonexisting_realm(self) -> None:
|
||||
account_data_dict = dict(email=self.email, name=self.name)
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name=self.name)
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='nonexistent')
|
||||
self.assert_in_success_response(["There is no Zulip organization hosted at this subdomain."],
|
||||
result)
|
||||
|
||||
def test_user_cannot_log_into_wrong_subdomain(self) -> None:
|
||||
account_data_dict = dict(email=self.email, name=self.name)
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name=self.name)
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zephyr')
|
||||
self.assertTrue(result.url.startswith("http://zephyr.testserver/accounts/login/subdomain/"))
|
||||
result = self.client_get(result.url.replace('http://zephyr.testserver', ''),
|
||||
@@ -655,24 +587,24 @@ class GitHubAuthBackendTest(ZulipTestCase):
|
||||
self.assert_in_success_response(['Your email address, hamlet@zulip.com, is not in one of the domains ',
|
||||
'that are allowed to register for accounts in this organization.'], result)
|
||||
|
||||
def test_github_oauth2_mobile_success(self) -> None:
|
||||
def test_social_auth_mobile_success(self) -> None:
|
||||
mobile_flow_otp = '1234abcd' * 8
|
||||
account_data_dict = dict(email=self.email, name='Full Name')
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name='Full Name')
|
||||
self.assertEqual(len(mail.outbox), 0)
|
||||
self.user_profile.date_joined = timezone_now() - datetime.timedelta(seconds=JUST_CREATED_THRESHOLD + 1)
|
||||
self.user_profile.save()
|
||||
|
||||
with self.settings(SEND_LOGIN_EMAILS=True):
|
||||
# Verify that the right thing happens with an invalid-format OTP
|
||||
result = self.github_oauth2_test(account_data_dict, subdomain='zulip',
|
||||
result = self.social_auth_test(account_data_dict, subdomain='zulip',
|
||||
mobile_flow_otp="1234")
|
||||
self.assert_json_error(result, "Invalid OTP")
|
||||
result = self.github_oauth2_test(account_data_dict, subdomain='zulip',
|
||||
result = self.social_auth_test(account_data_dict, subdomain='zulip',
|
||||
mobile_flow_otp="invalido" * 8)
|
||||
self.assert_json_error(result, "Invalid OTP")
|
||||
|
||||
# Now do it correctly
|
||||
result = self.github_oauth2_test(account_data_dict, subdomain='zulip',
|
||||
result = self.social_auth_test(account_data_dict, subdomain='zulip',
|
||||
mobile_flow_otp=mobile_flow_otp)
|
||||
self.assertEqual(result.status_code, 302)
|
||||
redirect_url = result['Location']
|
||||
@@ -687,12 +619,12 @@ class GitHubAuthBackendTest(ZulipTestCase):
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertIn('Zulip on Android', mail.outbox[0].body)
|
||||
|
||||
def test_github_oauth2_registration_existing_account(self) -> None:
|
||||
def test_social_auth_registration_existing_account(self) -> None:
|
||||
"""If the user already exists, signup flow just logs them in"""
|
||||
email = "hamlet@zulip.com"
|
||||
name = 'Full Name'
|
||||
account_data_dict = dict(email=email, name=name)
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
account_data_dict = self.get_account_data_dict(email=email, name=name)
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip', is_signup='1')
|
||||
data = load_subdomain_token(result)
|
||||
self.assertEqual(data['email'], self.example_email("hamlet"))
|
||||
@@ -707,13 +639,13 @@ class GitHubAuthBackendTest(ZulipTestCase):
|
||||
# Name wasn't changed at all
|
||||
self.assertEqual(hamlet.full_name, "King Hamlet")
|
||||
|
||||
def test_github_oauth2_registration(self) -> None:
|
||||
"""If the user doesn't exist yet, GitHub auth can be used to register an account"""
|
||||
def test_social_auth_registration(self) -> None:
|
||||
"""If the user doesn't exist yet, social auth can be used to register an account"""
|
||||
email = "newuser@zulip.com"
|
||||
name = 'Full Name'
|
||||
realm = get_realm("zulip")
|
||||
account_data_dict = dict(email=email, name=name)
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
account_data_dict = self.get_account_data_dict(email=email, name=name)
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip', is_signup='1')
|
||||
|
||||
data = load_subdomain_token(result)
|
||||
@@ -755,12 +687,12 @@ class GitHubAuthBackendTest(ZulipTestCase):
|
||||
user_profile = get_user(email, realm)
|
||||
self.assertEqual(get_session_dict_user(self.client.session), user_profile.id)
|
||||
|
||||
def test_github_oauth2_registration_without_is_signup(self) -> None:
|
||||
"""If the user doesn't exist yet, GitHub auth can be used to register an account"""
|
||||
def test_social_auth_registration_without_is_signup(self) -> None:
|
||||
"""If `is_signup` is not set then a new account isn't created"""
|
||||
email = "newuser@zulip.com"
|
||||
name = 'Full Name'
|
||||
account_data_dict = dict(email=email, name=name)
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
account_data_dict = self.get_account_data_dict(email=email, name=name)
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip')
|
||||
self.assertEqual(result.status_code, 302)
|
||||
data = load_subdomain_token(result)
|
||||
@@ -777,12 +709,12 @@ class GitHubAuthBackendTest(ZulipTestCase):
|
||||
self.assertEqual(result.status_code, 200)
|
||||
self.assert_in_response("No account found for newuser@zulip.com.", result)
|
||||
|
||||
def test_github_oauth2_registration_without_is_signup_closed_realm(self) -> None:
|
||||
def test_social_auth_registration_without_is_signup_closed_realm(self) -> None:
|
||||
"""If the user doesn't exist yet in closed realm, give an error"""
|
||||
email = "nonexisting@phantom.com"
|
||||
name = 'Full Name'
|
||||
account_data_dict = dict(email=email, name=name)
|
||||
result = self.github_oauth2_test(account_data_dict,
|
||||
account_data_dict = self.get_account_data_dict(email=email, name=name)
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip')
|
||||
self.assertEqual(result.status_code, 302)
|
||||
data = load_subdomain_token(result)
|
||||
@@ -802,21 +734,126 @@ class GitHubAuthBackendTest(ZulipTestCase):
|
||||
'in one of the domains that are allowed to register '
|
||||
'for accounts in this organization.'.format(email), result)
|
||||
|
||||
def test_github_complete(self) -> None:
|
||||
def test_social_auth_complete(self) -> None:
|
||||
with mock.patch('social_core.backends.oauth.BaseOAuth2.process_error',
|
||||
side_effect=AuthFailed('Not found')):
|
||||
result = self.client_get(reverse('social:complete', args=['github']))
|
||||
result = self.client_get(reverse('social:complete', args=[self.backend.name]))
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertIn('login', result.url)
|
||||
|
||||
def test_github_complete_when_base_exc_is_raised(self) -> None:
|
||||
def test_social_auth_complete_when_base_exc_is_raised(self) -> None:
|
||||
with mock.patch('social_core.backends.oauth.BaseOAuth2.auth_complete',
|
||||
side_effect=AuthStateForbidden('State forbidden')), \
|
||||
mock.patch('zproject.backends.logging.warning'):
|
||||
result = self.client_get(reverse('social:complete', args=['github']))
|
||||
result = self.client_get(reverse('social:complete', args=[self.backend.name]))
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertIn('login', result.url)
|
||||
|
||||
class GitHubAuthBackendTest(SocialAuthBase):
|
||||
__unittest_skip__ = False
|
||||
|
||||
BACKEND_CLASS = GitHubAuthBackend
|
||||
CLIENT_KEY_SETTING = "SOCIAL_AUTH_GITHUB_KEY"
|
||||
LOGIN_URL = "/accounts/login/social/github"
|
||||
SIGNUP_URL = "/accounts/register/social/github"
|
||||
AUTHORIZATION_URL = "https://github.com/login/oauth/authorize"
|
||||
ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token"
|
||||
USER_INFO_URL = "https://api.github.com/user"
|
||||
AUTH_FINISH_URL = "/complete/github/"
|
||||
CONFIG_ERROR_URL = "/config-error/github"
|
||||
|
||||
def registerExtraEndpoints(self,
|
||||
account_data_dict: Dict[str, str],
|
||||
**extra_data: Any) -> None:
|
||||
# Keeping a verified email before the primary email makes sure
|
||||
# get_verified_emails puts the primary email at the start of the
|
||||
# email list returned as social_associate_user_helper assumes the
|
||||
# first email as the primary email.
|
||||
email_data = [
|
||||
dict(email="notprimary@example.com",
|
||||
verified=True),
|
||||
dict(email=account_data_dict["email"],
|
||||
verified=True,
|
||||
primary=True),
|
||||
dict(email="ignored@example.com",
|
||||
verified=False),
|
||||
]
|
||||
email_data = extra_data.get("email_data", email_data)
|
||||
|
||||
httpretty.register_uri(
|
||||
httpretty.GET,
|
||||
"https://api.github.com/user/emails",
|
||||
status=200,
|
||||
body=json.dumps(email_data)
|
||||
)
|
||||
|
||||
def get_account_data_dict(self, email: str, name: str) -> Dict[str, Any]:
|
||||
return dict(email=email, name=name)
|
||||
|
||||
def test_social_auth_email_not_verified(self) -> None:
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name=self.name)
|
||||
email_data = [
|
||||
dict(email=account_data_dict["email"],
|
||||
verified=False,
|
||||
primary=True),
|
||||
]
|
||||
with mock.patch('logging.warning') as mock_warning:
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip',
|
||||
email_data=email_data)
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, "/login/")
|
||||
mock_warning.assert_called_once_with("Social auth (GitHub) failed "
|
||||
"because user has no verified emails")
|
||||
|
||||
@override_settings(SOCIAL_AUTH_GITHUB_TEAM_ID='zulip-webapp')
|
||||
def test_social_auth_github_team_not_member_failed(self) -> None:
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name=self.name)
|
||||
with mock.patch('social_core.backends.github.GithubTeamOAuth2.user_data',
|
||||
side_effect=AuthFailed('Not found')), \
|
||||
mock.patch('logging.info') as mock_info:
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip')
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, "/login/")
|
||||
mock_info.assert_called_once_with("GitHub user is not member of required team")
|
||||
|
||||
@override_settings(SOCIAL_AUTH_GITHUB_TEAM_ID='zulip-webapp')
|
||||
def test_social_auth_github_team_member_success(self) -> None:
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name=self.name)
|
||||
with mock.patch('social_core.backends.github.GithubTeamOAuth2.user_data',
|
||||
return_value=account_data_dict):
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip')
|
||||
data = load_subdomain_token(result)
|
||||
self.assertEqual(data['email'], self.example_email("hamlet"))
|
||||
self.assertEqual(data['name'], 'Hamlet')
|
||||
self.assertEqual(data['subdomain'], 'zulip')
|
||||
|
||||
@override_settings(SOCIAL_AUTH_GITHUB_ORG_NAME='Zulip')
|
||||
def test_social_auth_github_organization_not_member_failed(self) -> None:
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name=self.name)
|
||||
with mock.patch('social_core.backends.github.GithubOrganizationOAuth2.user_data',
|
||||
side_effect=AuthFailed('Not found')), \
|
||||
mock.patch('logging.info') as mock_info:
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip')
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, "/login/")
|
||||
mock_info.assert_called_once_with("GitHub user is not member of required organization")
|
||||
|
||||
@override_settings(SOCIAL_AUTH_GITHUB_ORG_NAME='Zulip')
|
||||
def test_social_auth_github_organization_member_success(self) -> None:
|
||||
account_data_dict = self.get_account_data_dict(email=self.email, name=self.name)
|
||||
with mock.patch('social_core.backends.github.GithubOrganizationOAuth2.user_data',
|
||||
return_value=account_data_dict):
|
||||
result = self.social_auth_test(account_data_dict,
|
||||
subdomain='zulip')
|
||||
data = load_subdomain_token(result)
|
||||
self.assertEqual(data['email'], self.example_email("hamlet"))
|
||||
self.assertEqual(data['name'], 'Hamlet')
|
||||
self.assertEqual(data['subdomain'], 'zulip')
|
||||
|
||||
def test_github_auth_enabled(self) -> None:
|
||||
with self.settings(AUTHENTICATION_BACKENDS=('zproject.backends.GitHubAuthBackend',)):
|
||||
self.assertTrue(github_auth_enabled())
|
||||
|
||||
Reference in New Issue
Block a user