auth: Render config error page on configuration error.

We previously used to to redirect to config error page with
a different URL. This commit renders config error in the same
URL where configuration error is encountered. This way when
conifguration error is fixed the user can refresh to continue
normally or go back to login page from the link provided to
choose any other backend auth.

Also moved those URLs to dev_urls.py so that they can be easily
accessed to work on styling etc.

In tests, removed some of the asserts checking status code to be 200
as the function `assert_in_success_response` does that check.
This commit is contained in:
Dinesh 2020-06-17 17:55:25 +05:30 committed by Tim Abbott
parent 8674287192
commit 232eb8b7cf
7 changed files with 69 additions and 79 deletions

View File

@ -97,6 +97,7 @@
{% endif %} {% endif %}
<p>After making your changes, remember to restart <p>After making your changes, remember to restart
the Zulip server.</p> the Zulip server.</p>
<p><a href=""> Refresh</a> to try again or <a href="/login/">click here</a> to go back to the login page.</p>
</div> </div>
</div> </div>

View File

@ -884,16 +884,13 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase):
with self.settings(**{self.CLIENT_KEY_SETTING: None}): with self.settings(**{self.CLIENT_KEY_SETTING: None}):
result = self.social_auth_test(account_data_dict, result = self.social_auth_test(account_data_dict,
subdomain='zulip', next='/user_uploads/image') subdomain='zulip', next='/user_uploads/image')
self.assertEqual(result.status_code, 302) self.assert_in_success_response(["Configuration error"], result)
self.assertEqual(result.url, self.CONFIG_ERROR_URL)
def test_config_error_development(self) -> None: def test_config_error_development(self) -> None:
if hasattr(self, 'CLIENT_KEY_SETTING') and hasattr(self, 'CLIENT_SECRET_SETTING'): if hasattr(self, 'CLIENT_KEY_SETTING') and hasattr(self, 'CLIENT_SECRET_SETTING'):
with self.settings(**{self.CLIENT_KEY_SETTING: None}): with self.settings(**{self.CLIENT_KEY_SETTING: None}):
result = self.client_get(self.LOGIN_URL) result = self.client_get(self.LOGIN_URL)
self.assertEqual(result.status_code, 302) self.assert_in_success_response(["Configuration error"], result)
self.assertEqual(result.url, self.CONFIG_ERROR_URL)
result = self.client_get(result.url)
self.assert_in_success_response([self.CLIENT_KEY_SETTING.lower()], result) self.assert_in_success_response([self.CLIENT_KEY_SETTING.lower()], result)
self.assert_in_success_response([self.CLIENT_SECRET_SETTING.lower()], result) self.assert_in_success_response([self.CLIENT_SECRET_SETTING.lower()], result)
self.assert_in_success_response(["zproject/dev-secrets.conf"], result) self.assert_in_success_response(["zproject/dev-secrets.conf"], result)
@ -907,9 +904,7 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase):
if hasattr(self, 'CLIENT_KEY_SETTING') and hasattr(self, 'CLIENT_SECRET_SETTING'): if hasattr(self, 'CLIENT_KEY_SETTING') and hasattr(self, 'CLIENT_SECRET_SETTING'):
with self.settings(**{self.CLIENT_KEY_SETTING: None}): with self.settings(**{self.CLIENT_KEY_SETTING: None}):
result = self.client_get(self.LOGIN_URL) result = self.client_get(self.LOGIN_URL)
self.assertEqual(result.status_code, 302) self.assert_in_success_response(["Configuration error"], result)
self.assertEqual(result.url, self.CONFIG_ERROR_URL)
result = self.client_get(result.url)
self.assert_in_success_response([self.CLIENT_KEY_SETTING], result) self.assert_in_success_response([self.CLIENT_KEY_SETTING], result)
self.assert_in_success_response(["/etc/zulip/settings.py"], result) self.assert_in_success_response(["/etc/zulip/settings.py"], result)
self.assert_in_success_response([self.CLIENT_SECRET_SETTING.lower()], result) self.assert_in_success_response([self.CLIENT_SECRET_SETTING.lower()], result)
@ -1595,14 +1590,12 @@ class SAMLAuthBackendTest(SocialAuthBase):
with self.settings(SOCIAL_AUTH_SAML_ENABLED_IDPS=None): with self.settings(SOCIAL_AUTH_SAML_ENABLED_IDPS=None):
result = self.social_auth_test(account_data_dict, result = self.social_auth_test(account_data_dict,
subdomain='zulip', next='/user_uploads/image') subdomain='zulip', next='/user_uploads/image')
self.assertEqual(result.status_code, 302) self.assert_in_success_response(["Configuration error", "SAML authentication"], result)
self.assertEqual(result.url, self.CONFIG_ERROR_URL)
# Test the signup path too: # Test the signup path too:
result = self.social_auth_test(account_data_dict, is_signup=True, result = self.social_auth_test(account_data_dict, is_signup=True,
subdomain='zulip', next='/user_uploads/image') subdomain='zulip', next='/user_uploads/image')
self.assertEqual(result.status_code, 302) self.assert_in_success_response(["Configuration error", "SAML authentication"], result)
self.assertEqual(result.url, self.CONFIG_ERROR_URL)
def test_config_error_page(self) -> None: def test_config_error_page(self) -> None:
with self.assertLogs(level='INFO') as info_log: with self.assertLogs(level='INFO') as info_log:
@ -1610,10 +1603,7 @@ class SAMLAuthBackendTest(SocialAuthBase):
self.assertEqual(info_log.output, [ self.assertEqual(info_log.output, [
'INFO:root:Attempted to initiate SAML authentication with wrong idp argument: None' 'INFO:root:Attempted to initiate SAML authentication with wrong idp argument: None'
]) ])
self.assertEqual(result.status_code, 302) self.assert_in_success_response(["Configuration error", "SAML authentication"], result)
self.assertEqual(result.url, '/config-error/saml')
result = self.client_get(result.url)
self.assert_in_success_response(["SAML authentication"], result)
def test_saml_auth_works_without_private_public_keys(self) -> None: def test_saml_auth_works_without_private_public_keys(self) -> None:
with self.settings(SOCIAL_AUTH_SAML_SP_PUBLIC_CERT='', SOCIAL_AUTH_SAML_SP_PRIVATE_KEY=''): with self.settings(SOCIAL_AUTH_SAML_SP_PUBLIC_CERT='', SOCIAL_AUTH_SAML_SP_PRIVATE_KEY=''):
@ -1862,8 +1852,7 @@ class SAMLAuthBackendTest(SocialAuthBase):
with self.assertLogs(level='INFO') as info_log: with self.assertLogs(level='INFO') as info_log:
result = self.client_get(f'/accounts/{action}/social/saml') result = self.client_get(f'/accounts/{action}/social/saml')
# Missing idp argument. # Missing idp argument.
self.assertEqual(result.status_code, 302) self.assert_in_success_response(["Configuration error", "SAML authentication"], result)
self.assertEqual(result.url, '/config-error/saml')
self.assertEqual(info_log.output, [ self.assertEqual(info_log.output, [
'INFO:root:Attempted to initiate SAML authentication with wrong idp argument: None' 'INFO:root:Attempted to initiate SAML authentication with wrong idp argument: None'
]) ])
@ -1871,11 +1860,10 @@ class SAMLAuthBackendTest(SocialAuthBase):
with self.assertLogs(level='INFO') as info_log: with self.assertLogs(level='INFO') as info_log:
result = self.client_get(f'/accounts/{action}/social/saml/nonexistent_idp') result = self.client_get(f'/accounts/{action}/social/saml/nonexistent_idp')
# No such IdP is configured. # No such IdP is configured.
self.assertEqual(result.status_code, 302)
self.assertEqual(result.url, '/config-error/saml')
self.assertEqual(info_log.output, [ self.assertEqual(info_log.output, [
'INFO:root:Attempted to initiate SAML authentication with wrong idp argument: nonexistent_idp' 'INFO:root:Attempted to initiate SAML authentication with wrong idp argument: nonexistent_idp'
]) ])
self.assert_in_success_response(["Configuration error", "SAML authentication"], result)
result = self.client_get(f'/accounts/{action}/social/saml/') result = self.client_get(f'/accounts/{action}/social/saml/')
# No matching url pattern. # No matching url pattern.
@ -3615,7 +3603,7 @@ class TestDevAuthBackend(ZulipTestCase):
data = {'direct_email': email} data = {'direct_email': email}
with self.settings(AUTHENTICATION_BACKENDS=('zproject.backends.EmailAuthBackend',)): with self.settings(AUTHENTICATION_BACKENDS=('zproject.backends.EmailAuthBackend',)):
response = self.client_post('/accounts/login/local/', data) response = self.client_post('/accounts/login/local/', data)
self.assertRedirects(response, reverse('config_error', kwargs={'error_category_name': 'dev'})) self.assert_in_success_response(["Configuration error", "DevAuthBackend"], response)
def test_dev_direct_production_config_error(self) -> None: def test_dev_direct_production_config_error(self) -> None:
result = self.client_get("/config-error/dev") result = self.client_get("/config-error/dev")
@ -3627,7 +3615,7 @@ class TestDevAuthBackend(ZulipTestCase):
data = {'direct_email': email} data = {'direct_email': email}
response = self.client_post('/accounts/login/local/', data) response = self.client_post('/accounts/login/local/', data)
self.assertRedirects(response, reverse('config_error', kwargs={'error_category_name': 'dev'})) self.assert_in_success_response(["Configuration error", "DevAuthBackend"], response)
class TestZulipRemoteUserBackend(DesktopFlowTestingLib, ZulipTestCase): class TestZulipRemoteUserBackend(DesktopFlowTestingLib, ZulipTestCase):
def test_start_remote_user_sso(self) -> None: def test_start_remote_user_sso(self) -> None:
@ -3672,10 +3660,8 @@ class TestZulipRemoteUserBackend(DesktopFlowTestingLib, ZulipTestCase):
def test_login_failure(self) -> None: def test_login_failure(self) -> None:
email = self.example_email("hamlet") email = self.example_email("hamlet")
result = self.client_get('/accounts/login/sso/', REMOTE_USER=email) result = self.client_get('/accounts/login/sso/', REMOTE_USER=email)
self.assertEqual(result.status_code, 302) self.assert_in_success_response(["Configuration error", "Authentication via the REMOTE_USER header is"],
result)
result = self.client_get(result["Location"])
self.assert_in_response("Authentication via the REMOTE_USER header is", result)
self.assert_logged_in_user_id(None) self.assert_logged_in_user_id(None)
def test_login_failure_due_to_nonexisting_user(self) -> None: def test_login_failure_due_to_nonexisting_user(self) -> None:
@ -3695,10 +3681,8 @@ class TestZulipRemoteUserBackend(DesktopFlowTestingLib, ZulipTestCase):
def test_login_failure_due_to_missing_field(self) -> None: def test_login_failure_due_to_missing_field(self) -> None:
with self.settings(AUTHENTICATION_BACKENDS=('zproject.backends.ZulipRemoteUserBackend',)): with self.settings(AUTHENTICATION_BACKENDS=('zproject.backends.ZulipRemoteUserBackend',)):
result = self.client_get('/accounts/login/sso/') result = self.client_get('/accounts/login/sso/')
self.assertEqual(result.status_code, 302) self.assert_in_success_response(["Configuration error", "The REMOTE_USER header is not set."],
result)
result = self.client_get(result["Location"])
self.assert_in_response("The REMOTE_USER header is not set.", result)
def test_login_failure_due_to_wrong_subdomain(self) -> None: def test_login_failure_due_to_wrong_subdomain(self) -> None:
email = self.example_email("hamlet") email = self.example_email("hamlet")
@ -5034,11 +5018,8 @@ class LDAPBackendTest(ZulipTestCase):
mock.patch('django_auth_ldap.backend._LDAPUser._authenticate_user_dn'), \ mock.patch('django_auth_ldap.backend._LDAPUser._authenticate_user_dn'), \
self.assertLogs('django_auth_ldap', 'WARNING') as warn_log: self.assertLogs('django_auth_ldap', 'WARNING') as warn_log:
response = self.client_post('/login/', data) response = self.client_post('/login/', data)
self.assertEqual(response.status_code, 302) self.assert_in_success_response(["Configuration error",
self.assertEqual(response.url, reverse('config_error', kwargs={'error_category_name': 'ldap'})) "You are trying to log in using LDAP without creating an"],
response = self.client_get(response.url)
self.assert_in_response('You are trying to log in using LDAP '
'without creating an',
response) response)
self.assertEqual(warn_log.output, [ self.assertEqual(warn_log.output, [
"WARNING:django_auth_ldap:('Realm is None', 1) while authenticating hamlet" "WARNING:django_auth_ldap:('Realm is None', 1) while authenticating hamlet"

View File

@ -7,7 +7,7 @@ import orjson
from django.test import Client from django.test import Client
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase
from zerver.models import Stream from zerver.models import Realm, Stream
from zproject import urls from zproject import urls
@ -98,6 +98,24 @@ class PublicURLTest(ZulipTestCase):
self.assertEqual('success', data['result']) self.assertEqual('success', data['result'])
self.assertEqual('ABCD', data['google_client_id']) self.assertEqual('ABCD', data['google_client_id'])
def test_config_error_endpoints_dev_env(self) -> None:
'''
The content of these pages is tested separately.
Here we simply sanity-check that all the URLs load
correctly.
'''
auth_types = [auth.lower() for auth in Realm.AUTHENTICATION_FLAGS]
for auth in ['azuread', 'email', 'remoteuser']: # We do not have configerror pages for AzureAD and Email.
auth_types.remove(auth)
auth_types += ['smtp', 'remoteuser/remote_user_backend_disabled',
'remoteuser/remote_user_header_missing']
urls = [f'/config-error/{auth_type}' for auth_type in auth_types]
with self.settings(DEVELOPMENT=True):
for url in urls:
response = self.client_get(url)
self.assert_in_success_response(['Configuration error'], response)
class URLResolutionTest(ZulipTestCase): class URLResolutionTest(ZulipTestCase):
def get_callback_string(self, pattern: django.urls.resolvers.URLPattern) -> Optional[str]: def get_callback_string(self, pattern: django.urls.resolvers.URLPattern) -> Optional[str]:
callback_str = hasattr(pattern, 'lookup_str') and 'lookup_str' callback_str = hasattr(pattern, 'lookup_str') and 'lookup_str'

View File

@ -20,7 +20,6 @@ from django.utils.http import is_safe_url
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_safe from django.views.decorators.http import require_safe
from django.views.generic import TemplateView
from social_django.utils import load_backend, load_strategy from social_django.utils import load_backend, load_strategy
from two_factor.forms import BackupTokenForm from two_factor.forms import BackupTokenForm
from two_factor.views import LoginView as BaseTwoFactorLoginView from two_factor.views import LoginView as BaseTwoFactorLoginView
@ -72,7 +71,6 @@ from zproject.backends import (
dev_auth_enabled, dev_auth_enabled,
ldap_auth_enabled, ldap_auth_enabled,
password_auth_enabled, password_auth_enabled,
redirect_to_config_error,
saml_auth_enabled, saml_auth_enabled,
validate_otp_params, validate_otp_params,
) )
@ -356,12 +354,12 @@ def remote_user_sso(
realm = None realm = None
if not auth_enabled_helper([ZulipRemoteUserBackend.auth_backend_name], realm): if not auth_enabled_helper([ZulipRemoteUserBackend.auth_backend_name], realm):
return redirect_to_config_error("remoteuser/backend_disabled") return config_error(request, "remote_user_backend_disabled")
try: try:
remote_user = request.META["REMOTE_USER"] remote_user = request.META["REMOTE_USER"]
except KeyError: except KeyError:
return redirect_to_config_error("remoteuser/remote_user_header_missing") return config_error(request, "remote_user_header_missing")
# Django invokes authenticate methods by matching arguments, and this # Django invokes authenticate methods by matching arguments, and this
# authentication flow will not invoke LDAP authentication because of # authentication flow will not invoke LDAP authentication because of
@ -505,29 +503,26 @@ def start_social_login(request: HttpRequest, backend: str, extra_arg: Optional[s
backend_url = reverse('social:begin', args=[backend]) backend_url = reverse('social:begin', args=[backend])
extra_url_params: Dict[str, str] = {} extra_url_params: Dict[str, str] = {}
if backend == "saml": if backend == "saml":
result = SAMLAuthBackend.check_config() if not SAMLAuthBackend.check_config():
if result is not None: return config_error(request, 'saml')
return result
# This backend requires the name of the IdP (from the list of configured ones) # This backend requires the name of the IdP (from the list of configured ones)
# to be passed as the parameter. # to be passed as the parameter.
if not extra_arg or extra_arg not in settings.SOCIAL_AUTH_SAML_ENABLED_IDPS: if not extra_arg or extra_arg not in settings.SOCIAL_AUTH_SAML_ENABLED_IDPS:
logging.info("Attempted to initiate SAML authentication with wrong idp argument: %s", logging.info("Attempted to initiate SAML authentication with wrong idp argument: %s",
extra_arg) extra_arg)
return redirect_to_config_error("saml") return config_error(request, "saml")
extra_url_params = {'idp': extra_arg} extra_url_params = {'idp': extra_arg}
if backend == "apple": if backend == "apple" and not AppleAuthBackend.check_config():
result = AppleAuthBackend.check_config() return config_error(request, 'apple')
if result is not None:
return result
# TODO: Add AzureAD also. # TODO: Add AzureAD also.
if backend in ["github", "google", "gitlab"]: if backend in ["github", "google", "gitlab"]:
key_setting = "SOCIAL_AUTH_" + backend.upper() + "_KEY" key_setting = "SOCIAL_AUTH_" + backend.upper() + "_KEY"
secret_setting = "SOCIAL_AUTH_" + backend.upper() + "_SECRET" secret_setting = "SOCIAL_AUTH_" + backend.upper() + "_SECRET"
if not (getattr(settings, key_setting) and getattr(settings, secret_setting)): if not (getattr(settings, key_setting) and getattr(settings, secret_setting)):
return redirect_to_config_error(backend) return config_error(request, backend)
return oauth_redirect_to_root(request, backend_url, 'social', extra_url_params=extra_url_params) return oauth_redirect_to_root(request, backend_url, 'social', extra_url_params=extra_url_params)
@ -537,14 +532,13 @@ def start_social_signup(request: HttpRequest, backend: str, extra_arg: Optional[
backend_url = reverse('social:begin', args=[backend]) backend_url = reverse('social:begin', args=[backend])
extra_url_params: Dict[str, str] = {} extra_url_params: Dict[str, str] = {}
if backend == "saml": if backend == "saml":
result = SAMLAuthBackend.check_config() if not SAMLAuthBackend.check_config():
if result is not None: return config_error(request, 'saml')
return result
if not extra_arg or extra_arg not in settings.SOCIAL_AUTH_SAML_ENABLED_IDPS: if not extra_arg or extra_arg not in settings.SOCIAL_AUTH_SAML_ENABLED_IDPS:
logging.info("Attempted to initiate SAML authentication with wrong idp argument: %s", logging.info("Attempted to initiate SAML authentication with wrong idp argument: %s",
extra_arg) extra_arg)
return redirect_to_config_error("saml") return config_error(request, "saml")
extra_url_params = {'idp': extra_arg} extra_url_params = {'idp': extra_arg}
return oauth_redirect_to_root(request, backend_url, 'social', is_signup=True, return oauth_redirect_to_root(request, backend_url, 'social', is_signup=True,
extra_url_params=extra_url_params) extra_url_params=extra_url_params)
@ -598,9 +592,9 @@ def get_dev_users(realm: Optional[Realm]=None, extra_users_count: int=10) -> Lis
users = list(shakespearian_users) + list(extra_users) users = list(shakespearian_users) + list(extra_users)
return users return users
def redirect_to_misconfigured_ldap_notice(error_type: int) -> HttpResponse: def redirect_to_misconfigured_ldap_notice(request: HttpResponse, error_type: int) -> HttpResponse:
if error_type == ZulipLDAPAuthBackend.REALM_IS_NONE_ERROR: if error_type == ZulipLDAPAuthBackend.REALM_IS_NONE_ERROR:
return redirect_to_config_error('ldap') return config_error(request, 'ldap')
else: else:
raise AssertionError("Invalid error type") raise AssertionError("Invalid error type")
@ -733,7 +727,7 @@ def login_page(
extra_context=extra_context, **kwargs)(request) extra_context=extra_context, **kwargs)(request)
except ZulipLDAPConfigurationError as e: except ZulipLDAPConfigurationError as e:
assert len(e.args) > 1 assert len(e.args) > 1
return redirect_to_misconfigured_ldap_notice(e.args[1]) return redirect_to_misconfigured_ldap_notice(request, e.args[1])
if isinstance(template_response, SimpleTemplateResponse): if isinstance(template_response, SimpleTemplateResponse):
# Only those responses that are rendered using a template have # Only those responses that are rendered using a template have
@ -788,13 +782,13 @@ def dev_direct_login(
if (not dev_auth_enabled()) or settings.PRODUCTION: if (not dev_auth_enabled()) or settings.PRODUCTION:
# This check is probably not required, since authenticate would fail without # This check is probably not required, since authenticate would fail without
# an enabled DevAuthBackend. # an enabled DevAuthBackend.
return redirect_to_config_error('dev') return config_error(request, 'dev')
email = request.POST['direct_email'] email = request.POST['direct_email']
subdomain = get_subdomain(request) subdomain = get_subdomain(request)
realm = get_realm(subdomain) realm = get_realm(subdomain)
user_profile = authenticate(dev_auth_username=email, realm=realm) user_profile = authenticate(dev_auth_username=email, realm=realm)
if user_profile is None: if user_profile is None:
return redirect_to_config_error('dev') return config_error(request, 'dev')
do_login(request, user_profile) do_login(request, user_profile)
redirect_to = get_safe_redirect_to(next, user_profile.realm.uri) redirect_to = get_safe_redirect_to(next, user_profile.realm.uri)
@ -994,7 +988,7 @@ def saml_sp_metadata(request: HttpRequest, **kwargs: Any) -> HttpResponse: # no
Taken from https://python-social-auth.readthedocs.io/en/latest/backends/saml.html Taken from https://python-social-auth.readthedocs.io/en/latest/backends/saml.html
""" """
if not saml_auth_enabled(): if not saml_auth_enabled():
return redirect_to_config_error("saml") return config_error(request, "saml")
complete_url = reverse('social:complete', args=("saml",)) complete_url = reverse('social:complete', args=("saml",))
saml_backend = load_backend(load_strategy(request), "saml", saml_backend = load_backend(load_strategy(request), "saml",
@ -1006,7 +1000,7 @@ def saml_sp_metadata(request: HttpRequest, **kwargs: Any) -> HttpResponse: # no
return HttpResponseServerError(content=', '.join(errors)) return HttpResponseServerError(content=', '.join(errors))
def config_error_view(request: HttpRequest, error_category_name: str) -> HttpResponse: def config_error(request: HttpRequest, error_category_name: str) -> HttpResponse:
contexts = { contexts = {
'apple': {'social_backend_name': 'apple', 'has_markdown_file': True}, 'apple': {'social_backend_name': 'apple', 'has_markdown_file': True},
'google': {'social_backend_name': 'google', 'has_markdown_file': True}, 'google': {'social_backend_name': 'google', 'has_markdown_file': True},
@ -1016,9 +1010,8 @@ def config_error_view(request: HttpRequest, error_category_name: str) -> HttpRes
'dev': {'error_name': 'dev_not_supported_error'}, 'dev': {'error_name': 'dev_not_supported_error'},
'saml': {'social_backend_name': 'saml'}, 'saml': {'social_backend_name': 'saml'},
'smtp': {'error_name': 'smtp_error'}, 'smtp': {'error_name': 'smtp_error'},
'backend_disabled': {'error_name': 'remoteuser_error_backend_disabled'}, 'remote_user_backend_disabled': {'error_name': 'remoteuser_error_backend_disabled'},
'remote_user_header_missing': {'error_name': 'remoteuser_error_remote_user_header_missing'}, 'remote_user_header_missing': {'error_name': 'remoteuser_error_remote_user_header_missing'},
} }
return TemplateView.as_view(template_name='zerver/config_error.html', return render(request, 'zerver/config_error.html', contexts[error_category_name])
extra_context=contexts[error_category_name])(request)

View File

@ -155,9 +155,6 @@ def any_social_backend_enabled(realm: Optional[Realm]=None) -> bool:
for social_auth_subclass in EXTERNAL_AUTH_METHODS] for social_auth_subclass in EXTERNAL_AUTH_METHODS]
return auth_enabled_helper(social_backend_names, realm) return auth_enabled_helper(social_backend_names, realm)
def redirect_to_config_error(error_type: str) -> HttpResponseRedirect:
return HttpResponseRedirect(f"/config-error/{error_type}")
def require_email_format_usernames(realm: Optional[Realm]=None) -> bool: def require_email_format_usernames(realm: Optional[Realm]=None) -> bool:
if ldap_auth_enabled(realm): if ldap_auth_enabled(realm):
if settings.LDAP_EMAIL_ATTR or settings.LDAP_APPEND_DOMAIN: if settings.LDAP_EMAIL_ATTR or settings.LDAP_APPEND_DOMAIN:
@ -1572,7 +1569,7 @@ class AppleAuthBackend(SocialAuthMixin, AppleIdAuth):
SCOPE_SEPARATOR = "%20" # https://github.com/python-social-auth/social-core/issues/470 SCOPE_SEPARATOR = "%20" # https://github.com/python-social-auth/social-core/issues/470
@classmethod @classmethod
def check_config(cls) -> Optional[HttpResponse]: def check_config(cls) -> bool:
obligatory_apple_settings_list = [ obligatory_apple_settings_list = [
settings.SOCIAL_AUTH_APPLE_TEAM, settings.SOCIAL_AUTH_APPLE_TEAM,
settings.SOCIAL_AUTH_APPLE_SERVICES_ID, settings.SOCIAL_AUTH_APPLE_SERVICES_ID,
@ -1580,9 +1577,9 @@ class AppleAuthBackend(SocialAuthMixin, AppleIdAuth):
settings.SOCIAL_AUTH_APPLE_SECRET, settings.SOCIAL_AUTH_APPLE_SECRET,
] ]
if any(not setting for setting in obligatory_apple_settings_list): if any(not setting for setting in obligatory_apple_settings_list):
return redirect_to_config_error("apple") return False
return None return True
def is_native_flow(self) -> bool: def is_native_flow(self) -> bool:
return self.strategy.request_data().get('native_flow', False) return self.strategy.request_data().get('native_flow', False)
@ -1955,7 +1952,7 @@ class SAMLAuthBackend(SocialAuthMixin, SAMLAuth):
return True return True
@classmethod @classmethod
def check_config(cls) -> Optional[HttpResponse]: def check_config(cls) -> bool:
obligatory_saml_settings_list = [ obligatory_saml_settings_list = [
settings.SOCIAL_AUTH_SAML_SP_ENTITY_ID, settings.SOCIAL_AUTH_SAML_SP_ENTITY_ID,
settings.SOCIAL_AUTH_SAML_ORG_INFO, settings.SOCIAL_AUTH_SAML_ORG_INFO,
@ -1964,9 +1961,9 @@ class SAMLAuthBackend(SocialAuthMixin, SAMLAuth):
settings.SOCIAL_AUTH_SAML_ENABLED_IDPS, settings.SOCIAL_AUTH_SAML_ENABLED_IDPS,
] ]
if any(not setting for setting in obligatory_saml_settings_list): if any(not setting for setting in obligatory_saml_settings_list):
return redirect_to_config_error("saml") return False
return None return True
@classmethod @classmethod
def dict_representation(cls, realm: Optional[Realm]=None) -> List[ExternalAuthMethodDictT]: def dict_representation(cls, realm: Optional[Realm]=None) -> List[ExternalAuthMethodDictT]:

View File

@ -9,7 +9,7 @@ from django.urls import path
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django.views.static import serve from django.views.static import serve
from zerver.views.auth import login_page from zerver.views.auth import config_error, login_page
from zerver.views.development.email_log import clear_emails, email_page, generate_all_emails from zerver.views.development.email_log import clear_emails, email_page, generate_all_emails
from zerver.views.development.integrations import ( from zerver.views.development.integrations import (
check_send_webhook_fixture_message, check_send_webhook_fixture_message,
@ -72,6 +72,11 @@ urls = [
send_all_webhook_fixture_messages), send_all_webhook_fixture_messages),
path('devtools/integrations/<integration_name>/fixtures', path('devtools/integrations/<integration_name>/fixtures',
get_fixtures), get_fixtures),
path('config-error/<error_category_name>', config_error,
name='config_error'),
path('config-error/remoteuser/<error_category_name>',
config_error),
] ]
# Serve static assets via the Django server # Serve static assets via the Django server

View File

@ -26,7 +26,6 @@ from zerver.views.auth import (
api_fetch_api_key, api_fetch_api_key,
api_fetch_google_client_id, api_fetch_google_client_id,
api_get_server_settings, api_get_server_settings,
config_error_view,
dev_direct_login, dev_direct_login,
json_fetch_api_key, json_fetch_api_key,
log_into_subdomain, log_into_subdomain,
@ -747,10 +746,6 @@ i18n_urls = [
# Terms of Service and privacy pages. # Terms of Service and privacy pages.
path('terms/', terms_view), path('terms/', terms_view),
path('privacy/', privacy_view), path('privacy/', privacy_view),
path('config-error/<error_category_name>', config_error_view,
name='config_error'),
path('config-error/remoteuser/<error_category_name>',
config_error_view),
] ]
# Make a copy of i18n_urls so that they appear without prefix for english # Make a copy of i18n_urls so that they appear without prefix for english