Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
340ef0a
commit 1daa0c1
Showing
13 changed files
with
215 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from django.contrib.auth.models import User | ||
|
||
class GooglePlusBackend: | ||
supports_inactive_user = False | ||
|
||
def authenticate(self, googleplus_user=None): | ||
if googleplus_user is not None: | ||
return googleplus_user.user | ||
else: | ||
return None | ||
|
||
def get_user(self, user_id): | ||
try: | ||
return User.objects.get(id=user_id) | ||
except User.DoesNotExist: | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from django.contrib.auth.models import User | ||
from django.db import models | ||
|
||
class GooglePlusUser(models.Model): | ||
"""Stores Google+ user specific details. | ||
""" | ||
user = models.OneToOneField(User) | ||
access_token = models.TextField(db_index=True) | ||
expiry_at = models.DateTimeField(null=True) | ||
googleplus_id = models.CharField(max_length=100, unique=True, db_index=True) | ||
googleplus_display_name = models.CharField(max_length=100, unique=True, db_index=True) | ||
|
||
def __unicode__(self): | ||
return unicode(self.id) + u' | ' + unicode(self.googleplus_id) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
""" | ||
This file demonstrates writing tests using the unittest module. These will pass | ||
when you run "manage.py test". | ||
Replace this with more appropriate tests for your application. | ||
""" | ||
|
||
from django.test import TestCase | ||
|
||
|
||
class SimpleTest(TestCase): | ||
def test_basic_addition(self): | ||
""" | ||
Tests that 1 + 1 always equals 2. | ||
""" | ||
self.assertEqual(1 + 1, 2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from django.conf.urls.defaults import patterns, include, url | ||
|
||
urlpatterns = patterns('', | ||
url(r'login/$', 'googleplus.views.login_handler'), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import json | ||
import urllib | ||
import urlparse | ||
|
||
def graph_api(path, params, method="GET"): | ||
"""Invokes the Google+ API. | ||
Returns the response as python dictionary. | ||
""" | ||
# Construct the API url. | ||
base_url = 'https://www.googleapis.com/plus/v1/' | ||
url = urlparse.urljoin(base_url, path) | ||
|
||
# Invoke the Google+ API with the specified method. | ||
if method.upper() == "GET": | ||
req = urllib.urlopen(url + '?' + urllib.urlencode(params)) | ||
elif method.upper() == "POST": | ||
req = urllib.urlopen(url, urllib.urlencode(params)) | ||
|
||
response = req.read() | ||
response_dict = json.loads(response) | ||
|
||
return response_dict |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
from django.conf import settings | ||
from django.contrib import messages | ||
from django.contrib.auth import authenticate, login | ||
from django.contrib.auth.models import User | ||
from django.contrib.sites.models import Site | ||
from django.http import HttpResponseRedirect | ||
from django.shortcuts import render_to_response | ||
from django.template.context import RequestContext | ||
import datetime | ||
import json | ||
import urllib | ||
import urlparse | ||
|
||
from googleplus import utils | ||
from googleplus.models import GooglePlusUser | ||
|
||
GOOGLEPLUS_LOGIN_URL = '/googleplus/login/' | ||
REDIRECT_URI = urlparse.urljoin( \ | ||
'http://' + Site.objects.get_current().domain, GOOGLEPLUS_LOGIN_URL | ||
) | ||
|
||
def login_handler(request): | ||
"""Google+ OAuth2 login handler. | ||
""" | ||
if request.user.is_authenticated(): | ||
return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL) | ||
|
||
if 'error' in request.GET: | ||
messages.add_message(request, messages.ERROR, | ||
request.GET['error']) | ||
return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL) | ||
elif 'code' in request.GET: | ||
params = { \ | ||
'client_id': settings.GOOGLEPLUS_CLIENT_ID, \ | ||
'redirect_uri': REDIRECT_URI, \ | ||
'client_secret': settings.GOOGLEPLUS_CLIENT_SECRET, \ | ||
'code': request.GET['code'], \ | ||
'grant_type': 'authorization_code', \ | ||
} | ||
req = urllib.urlopen('https://accounts.google.com/o/oauth2/token', | ||
urllib.urlencode(params) | ||
) | ||
if req.getcode() != 200: | ||
response = render_to_response('500.html', {}, \ | ||
context_instance=RequestContext(request)) | ||
response.status_code = 500 | ||
return response | ||
|
||
response = req.read() | ||
response_query_dict = json.loads(response) | ||
access_token = response_query_dict['access_token'] | ||
expires_in = response_query_dict['expires_in'] | ||
|
||
profile = utils.graph_api('people/me', {'access_token': access_token}) | ||
|
||
googleplus_user = _create_or_update_googleplus_user(profile, access_token, expires_in) | ||
|
||
user = authenticate(googleplus_user=googleplus_user) | ||
if user is not None: | ||
if user.is_active: | ||
login(request, user) | ||
request.session.set_expiry(googleplus_user.expiry_at) | ||
if 'next' in request.GET: | ||
return HttpResponseRedirect(request.GET['next']) | ||
return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL) | ||
else: | ||
messages.add_message(request, messages.ERROR, "Account disabled.") | ||
else: | ||
messages.add_message(request, messages.ERROR, "Login failed.") | ||
else: | ||
params = { \ | ||
'client_id': settings.GOOGLEPLUS_CLIENT_ID, \ | ||
'redirect_uri': REDIRECT_URI, \ | ||
'scope': 'https://www.googleapis.com/auth/plus.me', \ | ||
'response_type': 'code', \ | ||
} | ||
return HttpResponseRedirect('https://accounts.google.com/o/oauth2/auth?' + | ||
urllib.urlencode(params) | ||
) | ||
|
||
def _create_or_update_googleplus_user(profile, access_token, expires_in): | ||
"""Creates or updates a Google+ user profile in local database. | ||
""" | ||
user_is_created = False | ||
try: | ||
googleplus_user = GooglePlusUser.objects.get(googleplus_id=profile['id']) | ||
except GooglePlusUser.DoesNotExist: | ||
first_name, last_name = _get_first_and_last_name(profile['displayName']) | ||
user = User.objects.create( \ | ||
first_name=first_name, | ||
last_name=last_name, | ||
username='googleplus_' + profile['id'] | ||
) | ||
user_is_created = True | ||
|
||
if user_is_created: | ||
googleplus_user = GooglePlusUser() | ||
googleplus_user.googleplus_id = profile['id'] | ||
googleplus_user.user = user | ||
else: | ||
first_name, last_name = _get_first_and_last_name(profile['displayName']) | ||
googleplus_user.user.first_name = first_name | ||
googleplus_user.last_name = last_name | ||
|
||
googleplus_user.googleplus_display_name = profile['displayName'] | ||
googleplus_user.access_token = access_token | ||
googleplus_user.expiry_at = datetime.datetime.now() + \ | ||
datetime.timedelta(seconds=int(expires_in)) | ||
googleplus_user.save() | ||
|
||
return googleplus_user | ||
|
||
def _get_first_and_last_name(display_name): | ||
try: | ||
first_name, last_name = display_name.strip().rsplit(' ', 1) | ||
except ValueError: | ||
first_name = display_name | ||
last_name = '' | ||
return first_name, last_name |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters