From 0fe0cf0ffb06ce989ec7932e2ea220d56ec9a09d Mon Sep 17 00:00:00 2001 From: Luke Faraone Date: Wed, 20 Feb 2013 16:26:06 -0500 Subject: [PATCH] [manual] Implement backend support for authenticating a user via Google. This code adds a dependency on python-django-auth-openid, installable as django-openid-auth from PyPI. On prod, one needs to run a syncdb in order to create the required tables. A database *migration* is not required, as these are new tables only. (imported from commit c902a0df8d589d93743b27e480154a04402b2c41) --- humbug/backends.py | 30 ++++++++++++++++++- humbug/settings.py | 9 +++++- humbug/urls.py | 2 ++ .../modules/humbug/manifests/app_frontend.pp | 2 +- templates/openid_error.html | 14 +++++++++ zephyr/openid.py | 8 +++++ 6 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 templates/openid_error.html create mode 100644 zephyr/openid.py diff --git a/humbug/backends.py b/humbug/backends.py index c2853d6c56..3593a5e9d8 100644 --- a/humbug/backends.py +++ b/humbug/backends.py @@ -1,5 +1,8 @@ from django.contrib.auth.models import User from django.conf import settings + +from openid.consumer.consumer import SUCCESS + from zephyr.lib.cache import cache_with_key @cache_with_key(lambda user_id: 'tornado_user:%d' % (user_id,)) @@ -39,6 +42,31 @@ class EmailAuthBackend(object): # any mutable fields from Tornado (just the id) return get_tornado_user(user_id) try: - return User.objects.get(pk=user_id) + return User.objects.get(id=user_id) + except User.DoesNotExist: + return None + +# Adapted from http://djangosnippets.org/snippets/2183/ by user Hangya (September 1, 2010) + +class GoogleBackend: + def authenticate(self, openid_response): + if openid_response is None: + return None + if openid_response.status != SUCCESS: + return None + + google_email = openid_response.getSigned('http://openid.net/srv/ax/1.0', 'value.email') + + try: + user = User.objects.get(email__iexact=google_email) + except User.DoesNotExist: + # create a new user, or send a message to admins, etc. + return None + + return user + + def get_user(self, user_id): + try: + return User.objects.get(id=user_id) except User.DoesNotExist: return None diff --git a/humbug/settings.py b/humbug/settings.py index b0a49bca0f..fca94afc1e 100644 --- a/humbug/settings.py +++ b/humbug/settings.py @@ -2,6 +2,8 @@ import os import platform +from zephyr.openid import openid_failure_handler + DEPLOYED = (('humbughq.com' in platform.node()) or os.path.exists('/etc/humbug-server')) STAGING_DEPLOYED = (platform.node() == 'staging.humbughq.com') @@ -157,7 +159,8 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.auth.middleware.AuthenticationMiddleware', ) -AUTHENTICATION_BACKENDS = ('humbug.backends.EmailAuthBackend',) +AUTHENTICATION_BACKENDS = ('humbug.backends.EmailAuthBackend', + 'humbug.backends.GoogleBackend') TEST_RUNNER = 'zephyr.tests.Runner' @@ -174,6 +177,7 @@ INSTALLED_APPS = ( 'django.contrib.sites', 'django.contrib.staticfiles', 'south', + 'django_openid_auth', 'jstemplate', 'confirmation', 'pipeline', @@ -372,6 +376,9 @@ EMAIL_PORT = 587 DEFAULT_FROM_EMAIL = "Humbug " LOGIN_REDIRECT_URL='/' +OPENID_SSO_SERVER_URL = 'https://www.google.com/accounts/o8/id' +OPENID_CREATE_USERS = True +OPENID_RENDER_FAILURE = openid_failure_handler EVENT_LOG_DIR = 'event_log' diff --git a/humbug/urls.py b/humbug/urls.py index 4e1fea78ae..40cd4c7dc8 100644 --- a/humbug/urls.py +++ b/humbug/urls.py @@ -5,6 +5,8 @@ import zephyr.forms urlpatterns = patterns('', url(r'^$', 'zephyr.views.home'), + url(r'^accounts/login/openid/$', 'django_openid_auth.views.login_begin', name='openid-login'), + url(r'^accounts/login/openid/done/$', 'django_openid_auth.views.login_complete', name='openid-complete'), # 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/', 'zephyr.views.login_page', {'template_name': 'zephyr/login.html'}), diff --git a/servers/puppet/modules/humbug/manifests/app_frontend.pp b/servers/puppet/modules/humbug/manifests/app_frontend.pp index 3f3d346df2..061f6c191a 100644 --- a/servers/puppet/modules/humbug/manifests/app_frontend.pp +++ b/servers/puppet/modules/humbug/manifests/app_frontend.pp @@ -4,7 +4,7 @@ class humbug::app_frontend { $web_packages = [ "nginx", "memcached", "python-pylibmc", "python-tornado", "python-django", "python-pygments", "python-flup", "ipython", "python-psycopg2", - "yui-compressor", ] + "yui-compressor", "python-django-auth-openid"] package { $web_packages: ensure => "installed" } file { "/etc/nginx/nginx.conf": diff --git a/templates/openid_error.html b/templates/openid_error.html new file mode 100644 index 0000000000..fb986d0d79 --- /dev/null +++ b/templates/openid_error.html @@ -0,0 +1,14 @@ +{% extends "zephyr/portico.html" %} + +{% block for_you %} isn't feeling too good. {% endblock %} + +{% block portico_content %} + +
+

We couldn't validate your Google account

+ +

You might want to try logging in via Google again or log in with a username or password.

+ +

If you'd like, you can also drop us a line to let us know what happened.

+ +{% endblock %} diff --git a/zephyr/openid.py b/zephyr/openid.py new file mode 100644 index 0000000000..c94fbe9923 --- /dev/null +++ b/zephyr/openid.py @@ -0,0 +1,8 @@ +# Defer importing until later to avoid circular imports + +def openid_failure_handler(request, message, status=403, template_name=None, exception=None): + # We ignore template_name in this function + + from django_openid_auth.views import default_render_failure + + return default_render_failure(request, message, status=403, template_name="openid_error.html", exception=None)