From c11b65590bbc8dd6de1e5cbddf4cd88cd374668b Mon Sep 17 00:00:00 2001 From: Luke Faraone Date: Mon, 4 Nov 2013 17:16:46 -0500 Subject: [PATCH] SSO / REMOTE_USER support (imported from commit 4f4fad7af5d3c6099cac95d7708338c182626d72) --- zerver/models.py | 5 +++++ zerver/views/__init__.py | 15 +++++++++++++++ zproject/backends.py | 18 +++++++++++++++++- zproject/local_settings.py | 5 +++++ zproject/local_settings_template.py | 11 +++++++++++ zproject/settings.py | 4 +--- zproject/urls.py | 1 + 7 files changed, 55 insertions(+), 4 deletions(-) diff --git a/zerver/models.py b/zerver/models.py index e32b3af4ef..9b54f33715 100644 --- a/zerver/models.py +++ b/zerver/models.py @@ -141,6 +141,11 @@ def email_to_username(email): def email_to_domain(email): return email.split("@")[-1].lower() +def remote_user_to_email(remote_user): + if settings.SSO_APPEND_DOMAIN is not None: + remote_user += "@" + SSO_APPEND_DOMAIN + return remote_user + class RealmEmoji(models.Model): realm = models.ForeignKey(Realm) name = models.TextField() diff --git a/zerver/views/__init__.py b/zerver/views/__init__.py index 8e3689c672..810c7735ad 100644 --- a/zerver/views/__init__.py +++ b/zerver/views/__init__.py @@ -472,6 +472,21 @@ def maybe_send_to_registration(request, email, full_name=''): else: return render_to_response('zerver/accounts_home.html', {'form': form}) +def remote_user_sso(request): + try: + remote_user = request.META["REMOTE_USER"] + except KeyError as e: + raise JsonableError("No REMOTE_USER set.") + + user = authenticate(remote_user=remote_user) + + if user is None: + # Since execution has reached here, REMOTE_USER is defined but no + # user account exists. Send them over to the PreregistrationUser flow. + return maybe_send_to_registration(request, remote_user_to_email(user)) + else: + return HttpResponseRedirect(reverse('zerver.views.accounts_home')) + def handle_openid_errors(request, issue, openid_response=None): if issue == "Unknown user": if openid_response is not None and openid_response.status == openid_SUCCESS: diff --git a/zproject/backends.py b/zproject/backends.py index 9234d291dc..0bedd1a892 100644 --- a/zproject/backends.py +++ b/zproject/backends.py @@ -1,5 +1,8 @@ +from __future__ import absolute_import + +from django.contrib.auth.backends import RemoteUserBackend from zerver.models import UserProfile, get_user_profile_by_id, \ - get_user_profile_by_email + get_user_profile_by_email, remote_user_to_email from openid.consumer.consumer import SUCCESS @@ -53,3 +56,16 @@ class GoogleBackend(ZulipAuthMixin): return user_profile +class ZulipRemoteUserBackend(RemoteUserBackend): + create_unknown_user = False + + def authenticate(self, remote_user): + if not remote_user: + return + + email = remote_user_to_email(remote_user) + + try: + return get_user_profile_by_email(email) + except UserProfile.DoesNotExist: + return None diff --git a/zproject/local_settings.py b/zproject/local_settings.py index fc3eff271e..8b0e24acbc 100644 --- a/zproject/local_settings.py +++ b/zproject/local_settings.py @@ -149,6 +149,11 @@ EMAIL_GATEWAY_IMAP_FOLDER = "INBOX" # must be delivered to the Inbox of the EMAIL_GATEWAY_LOGIN account above EMAIL_GATEWAY_PATTERN = "%s@streams.zulip.com" +SSO_APPEND_DOMAIN = None + +AUTHENTICATION_BACKENDS = ('zproject.backends.EmailAuthBackend', + 'zproject.backends.GoogleBackend') + DROPBOX_APP_KEY = "xxxxxxxxxxxxxxx" diff --git a/zproject/local_settings_template.py b/zproject/local_settings_template.py index 120ecfd617..922df48b08 100644 --- a/zproject/local_settings_template.py +++ b/zproject/local_settings_template.py @@ -37,6 +37,17 @@ TWITTER_CONSUMER_SECRET = '' TWITTER_ACCESS_TOKEN_KEY = '' TWITTER_ACCESS_TOKEN_SECRET = '' +# When using SSO: If REMOTE_USER only provides a username, append this domain +# to the returned value. +SSO_APPEND_DOMAIN = None + +# Enable at least one of the following authentication backends. +AUTHENTICATION_BACKENDS = ( +# 'zproject.backends.EmailAuthBackend', # Email and password +# 'zerver.views.remote_user_sso', # Local SSO +# 'zproject.backends.GoogleBackend', # Google Apps + ) + # The following keys are automatically generated during the install process # PLEASE DO NOT EDIT THEM CAMO_KEY = '' diff --git a/zproject/settings.py b/zproject/settings.py index e6a287051a..02b7bbea31 100644 --- a/zproject/settings.py +++ b/zproject/settings.py @@ -152,9 +152,7 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.auth.middleware.AuthenticationMiddleware', ) -AUTHENTICATION_BACKENDS = ('zproject.backends.EmailAuthBackend', - 'zproject.backends.GoogleBackend', - 'guardian.backends.ObjectPermissionBackend') +AUTHENTICATION_BACKENDS += ('guardian.backends.ObjectPermissionBackend',) ANONYMOUS_USER_ID = None AUTH_USER_MODEL = "zerver.UserProfile" diff --git a/zproject/urls.py b/zproject/urls.py index a0783ea366..f5d42ad59d 100644 --- a/zproject/urls.py +++ b/zproject/urls.py @@ -18,6 +18,7 @@ urlpatterns = patterns('', url(r'^accounts/login/openid/$', 'django_openid_auth.views.login_begin', name='openid-login'), url(r'^accounts/login/openid/done/$', 'zerver.views.process_openid_login', name='openid-complete'), url(r'^accounts/login/openid/done/$', 'django_openid_auth.views.login_complete', name='openid-complete'), + url(r'^accounts/login/sso/$', 'zerver.views.remote_user_sso'), # We have two entries for accounts/login to allow reverses on the Django # view we're wrapping to continue to function. url(r'^accounts/login/', 'zerver.views.login_page', {'template_name': 'zerver/login.html'}),