├── .gitignore ├── AUTHORS ├── LICENSE ├── README.rst ├── example_project ├── LICENSE ├── __init__.py ├── commentor │ ├── __init__.py │ ├── models.py │ ├── templates │ │ └── commentor │ │ │ └── index.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── example │ ├── README │ ├── __init__.py │ ├── admin.py │ ├── models.py │ ├── templates │ │ ├── comments │ │ │ └── example │ │ │ │ ├── form.html │ │ │ │ └── list.html │ │ └── example │ │ │ ├── post_detail.html │ │ │ └── post_list.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── example_comments │ ├── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── localsettings.example.py ├── manage.py ├── media │ ├── css │ │ ├── openid.css │ │ └── socialauth.css │ ├── images │ │ ├── aol.gif │ │ ├── blogger.ico │ │ ├── claimid.ico │ │ ├── facebook.gif │ │ ├── flickr.ico │ │ ├── google.gif │ │ ├── linkedin.jpg │ │ ├── livejournal.ico │ │ ├── myopenid.ico │ │ ├── openid-inputicon.gif │ │ ├── openid.gif │ │ ├── technorati.ico │ │ ├── twitter.png │ │ ├── verisign.ico │ │ ├── vidoop.ico │ │ ├── wordpress.ico │ │ └── yahoo.gif │ └── js │ │ ├── jquery-1.2.6.min.js │ │ └── openid-jquery.js ├── openid_consumer ├── settings.py ├── socialauth └── urls.py ├── ez_setup.py ├── openid_consumer ├── __init__.py ├── locale │ ├── en │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── sl │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── middleware.py ├── models.py ├── templates │ └── openid_consumer │ │ ├── failure.html │ │ ├── openid_consumer_base.html │ │ └── signin.html ├── util.py └── views.py ├── requirements.txt ├── setup.py └── socialauth ├── __init__.py ├── admin.py ├── auth_backends.py ├── context_processors.py ├── forms.py ├── lib ├── __init__.py ├── facebook.py ├── foursquare.py ├── github.py ├── linkedin.py ├── oauth1.py ├── oauth2.py ├── oauthgoogle.py ├── oauthtwitter.py ├── oauthtwitter2.py ├── oauthyahoo.py └── twitter.py ├── models.py ├── templates ├── openid │ └── index.html └── socialauth │ ├── base.html │ ├── editprofile.html │ ├── login_page.html │ ├── signin_complete.html │ ├── socialauth_base.html │ └── xd_receiver.htm ├── templatetags ├── __init__.py └── socialauth_tags.py ├── test_data.py.example ├── tests.py ├── urls.py └── views.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.pyc 3 | *~ 4 | build/ 5 | localsettings.py 6 | .svn/ 7 | .svn/* 8 | data.db 9 | *.wpr 10 | facebook.py 11 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | AUTHORS 2 | 3 | Shabda Raaj 4 | Lakshman Prasad 5 | Ashok Raavi 6 | 7 | CONTRIBUTORS 8 | 9 | oliland 10 | Benny Daon 11 | Peter Sidebotham 12 | clay 13 | Ken Sternberg 14 | Oliver Kingshott 15 | twidi 16 | Aitzol Naberan 17 | cyrildoussin 18 | zay2 19 | Phui Hock 20 | David Michon 21 | Justin Wong 22 | Rodrigo Chacon 23 | Didier Rano 24 | David Michon 25 | Josu Azpillaga 26 | Hel 27 | Javed Khan 28 | Roman Vorushin 29 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | WARNING: This app is not maintained anymore 2 | ------------------------------------------------- 3 | This repo is here for archive purposes, but unmaintained. There are much better social auth libraries. 4 | 5 | * https://github.com/omab/django-social-auth/ 6 | * https://github.com/pennersr/django-allauth 7 | 8 | 9 | 10 | What it does. 11 | -------------- 12 | 13 | #. Allow logging in via various providers. 14 | #. Import contacts from various third party sites, to find out which of your 15 | friends already use our service. 16 | 17 | Logging In 18 | ---------- 19 | 20 | This is a application to enable authentication via various third party sites. 21 | In particular it allows logging in via 22 | 23 | #. Twitter 24 | #. Gmail 25 | #. Facebook 26 | #. Yahoo(Essentially openid) 27 | #. OpenId 28 | #. Github 29 | #. Foursquare 30 | 31 | Libs you need to install 32 | See requirements.txt 33 | use `pip install -r requirements.txt` to install all dependencies at once 34 | Note that you will probably require git and mercurial installed for pip to 35 | fetch the requirements. 36 | 37 | The API Keys are available from 38 | 39 | * http://www.facebook.com/developers/createapp.php 40 | * https://developer.yahoo.com/dashboard/createKey.html 41 | * https://www.google.com/accounts/ManageDomains 42 | * http://twitter.com/oauth_clients 43 | * https://github.com/settings/applications/new 44 | * https://developer.foursquare.com/overview/auth.html 45 | 46 | How it works. 47 | -------------- 48 | 49 | * **Openid**: Users need to provide their openid providers. Talk to the providers and 50 | login. 51 | * **Yahoo**: Yahoo is an openid provider. Talk to Yahoo endpoints. (Endpoint: http://yahoo.com) 52 | * **Google**: Google is a provider. Talk to them. (Endpoint: https://www.google.com/accounts/o8/id) 53 | * **Facebook**: Facebook connect provides authentication framework. 54 | * **Twitter**: We use Twitter Oauth for authentication. In theory, Oauth shouldn't be 55 | used for authentication. (It is an autorisation framework, not an authentication one), 56 | In practice it works pretty well. Once you have an access_token, and a name, essentially 57 | authenticated. 58 | * **Github**:We use Github Oauth for authentication. As like Twitter, it works 59 | pretty well. 60 | * **Foursquare**:We use Oauth2.0 for authenticating via foursquare. 61 | 62 | References 63 | ---------- 64 | 65 | #. http://openid.net/developers/ 66 | #. http://developer.yahoo.com/openid/ 67 | #. http://code.google.com/apis/accounts/docs/OpenID.html 68 | #. http://apiwiki.twitter.com/OAuth-FAQ 69 | #. http://developers.facebook.com/connect.php 70 | #. http://develop.github.com/p/oauth.html 71 | #. https://developer.foursquare.com/overview/auth.html 72 | 73 | Limitations 74 | ------------ 75 | 76 | As with all APIs, we are limited by the amount of data which the API provider 77 | provides us. For example, both Yahoo and Google provide extremely limited data 78 | about the autheticated subscriber. Twitter and Facebook provide a lot of details, 79 | but not the email. Different Openid providers are free to provide [different 80 | amounts of data](http://openid.net/specs/openid-simple-registration-extension-1_0.html). 81 | 82 | How it works. 83 | -------------- 84 | 85 | #. For all providers(except Facebook) there are two urls and views. (start and done) 86 | #. Start sets up the required tokens, and redirects and hands off to the correct 87 | provider. 88 | #. Provider handles authentication on their ends, and hands off to Us, providing 89 | authorization tokens. 90 | #. In done, we check if the user with these details already exists, if yes, we 91 | log them in. Otherwise we create a new user, and log them in. 92 | 93 | For all of these, we use standard django authenication system, with custom 94 | auth_backends, hence all existing views, and decorators as login_required 95 | will work as expected. 96 | 97 | Urls 98 | ----- 99 | 100 | * /login/ Login page. Has all the login options 101 | * /openid_login/ AND /openid_login/done/ 102 | * /yahoo_login/ AND /yahoo_login/done/ 103 | * /gmail_login/ AND /gmail_login/done/ 104 | * /twitter_login/ AND /twitter_login/done/ 105 | * /facebook_login/done/ We dont have a start url here, as the starting tokens are 106 | set in a popup. 107 | * /github_login/ AND /github_login/done/ 108 | * /foursquare_login/ AND /foursquare_login/done/ 109 | 110 | Implementation 111 | --------------- 112 | 113 | #. Install required libraries. 114 | #. Get tokens and populate in localsettings.py 115 | #. Set the token callback urls correctly at Twitter, Facebook, Github and Foursquare. 116 | #. Set the authentication_backends to the providers you are using. 117 | -------------------------------------------------------------------------------- /example_project/LICENSE: -------------------------------------------------------------------------------- 1 | This work is dual licensed under 2 | 3 | (new) 4 | 5 | Copyright (c) 2009, Shabda Raaj and Contributors 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | * Neither the name of the nor the 16 | names of its contributors may be used to endorse or promote products 17 | derived from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | (To keep the older one around if in some remote chance you prefer this.) 31 | 32 | Copyright (c) 2009 by Shabda Raaj and Contributors 33 | * Usware Technologies 34 | * www.uswaretech.com 35 | "Building Amazing Webapps" 36 | 37 | All Rights Reserved 38 | Modified ATTRIBUTION ASSURANCE LICENSE (adapted from the original BSD license) 39 | Redistribution and use in source and binary forms, with or without 40 | modification, are permitted provided that the conditions below are met. 41 | These conditions require a modest attribution to Author (the 42 | "Shabda Raaj"), who hopes that its promotional value may help justify the 43 | thousands of dollars in otherwise billable time invested in writing 44 | this and other freely available, open-source software. 45 | 46 | 1. Redistributions of source code, in whole or part and with or without 47 | modification (the "Code"), must display this GPG-signed 48 | text in verifiable form. 49 | 2. Redistributions of the Code in binary form must be accompanied by 50 | this GPG-signed text in any documentation and, each time the resulting 51 | executable program or a program dependent thereon is launched, a 52 | modest display (e.g., splash screen or banner text) of the Shabda Raaj's 53 | attribution information, which includes: 54 | (a) Name ("Shabda Raaj"), 55 | (b) Identification ("Usware Technologies"), and 56 | (c) URL ("http://uswaretech.com"). 57 | 3. Neither the name nor any trademark of the Shabda Raaj may be used to 58 | endorse or promote products derived from this software without specific 59 | prior written permission. 60 | 4. Users are entirely responsible, to the exclusion of the Shabda Raaj and 61 | any other persons, for compliance with (1) regulations set by owners or 62 | administrators of employed equipment, (2) licensing terms of any other 63 | software, and (3) local regulations regarding use, including those 64 | regarding import, export, and use of encryption software. 65 | 66 | THIS FREE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND 67 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 68 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 69 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 70 | EVENT SHALL THE AUTHOR OR ANY CONTRIBUTOR BE LIABLE FOR 71 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 72 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 73 | EFFECTS OF UNAUTHORIZED OR MALICIOUS NETWORK ACCESS; 74 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 75 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 76 | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 77 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 78 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 79 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 80 | 81 | This work builds upon the work of many others. No ownership of their 82 | work is claimed or implied. They retain the ownership of their work, 83 | included here for convenience. On being bought to my notice, mentions 84 | would be gratefully provided. 85 | -------------------------------------------------------------------------------- /example_project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/__init__.py -------------------------------------------------------------------------------- /example_project/commentor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/commentor/__init__.py -------------------------------------------------------------------------------- /example_project/commentor/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class Comment(models.Model): 4 | comment = models.TextField() 5 | -------------------------------------------------------------------------------- /example_project/commentor/templates/commentor/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block main_content %} 4 | 5 | Enter Comment 6 | 7 | {% if user.is_authenticated %} 8 | 9 |

You are currently logged in as {{ user.username }}

10 | 11 |
12 | {{ form }} 13 | 14 |
15 | Logout 16 | {% else %} 17 | 18 | Login 19 | 20 | {% endif %} 21 | 22 | 23 | {% for comment in comments %} 24 | 25 |

26 | {{ comment.comment }} 27 |

28 | 29 | {% endfor %} 30 | 31 | {% endblock %} -------------------------------------------------------------------------------- /example_project/commentor/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates two different styles of tests (one doctest and one 3 | unittest). These will both pass when you run "manage.py test". 4 | 5 | Replace these with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | class SimpleTest(TestCase): 11 | def test_basic_addition(self): 12 | """ 13 | Tests that 1 + 1 always equals 2. 14 | """ 15 | self.failUnlessEqual(1 + 1, 2) 16 | 17 | __test__ = {"doctest": """ 18 | Another way to test that 1 + 1 is equal to 2. 19 | 20 | >>> 1 + 1 == 2 21 | True 22 | """} 23 | 24 | -------------------------------------------------------------------------------- /example_project/commentor/urls.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/commentor/urls.py -------------------------------------------------------------------------------- /example_project/commentor/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.decorators import login_required 2 | from django.http import HttpResponseRedirect 3 | from commentor.models import Comment 4 | from django.template import RequestContext 5 | from django import forms 6 | from django.shortcuts import render_to_response 7 | 8 | @login_required 9 | def leave_comment(request): 10 | form = CommentForm() 11 | if request.POST: 12 | form = CommentForm(data = request.POST) 13 | if form.is_valid(): 14 | form.save() 15 | return HttpResponseRedirect('.') 16 | 17 | payload = {'form':form, 'comments':Comment.objects.all()} 18 | return render_to_response('commentor/index.html', payload, RequestContext(request)) 19 | 20 | class CommentForm(forms.ModelForm): 21 | class Meta: 22 | model = Comment -------------------------------------------------------------------------------- /example_project/example/README: -------------------------------------------------------------------------------- 1 | This is an example app using socialauth as authentication backend 2 | We use modified django comments app to allow users to login via socialauth and post comments 3 | 4 | To use this app in your project: 5 | 6 | 1. Copy example and example_comments dirs to your project dir 7 | 2. Add 8 | 'example', 9 | 'example_comments', 10 | to your INSTALLED_APPS 11 | 3. Add 12 | (r'^comments/post/', 'example_comments.views.post_comment'), 13 | (r'comments/', include('django.contrib.comments.urls')), 14 | to your urlpatterns 15 | 4. Add 16 | COMMENTS_APP = 'example_comments' 17 | to your settings 18 | -------------------------------------------------------------------------------- /example_project/example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/example/__init__.py -------------------------------------------------------------------------------- /example_project/example/admin.py: -------------------------------------------------------------------------------- 1 | from models import Post 2 | from django.contrib import admin 3 | 4 | admin.site.register(Post) 5 | 6 | -------------------------------------------------------------------------------- /example_project/example/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | # Create your models here. 4 | 5 | class Post(models.Model): 6 | author = models.ForeignKey(User) 7 | date = models.DateTimeField() 8 | title = models.CharField(max_length=100) 9 | post = models.TextField() 10 | 11 | def __unicode__(self): 12 | return self.title 13 | -------------------------------------------------------------------------------- /example_project/example/templates/comments/example/form.html: -------------------------------------------------------------------------------- 1 | {% load comments i18n %} 2 | 3 |
{% csrf_token %} 4 | {% if next %}{% endif %} 5 | {% for field in form %} 6 | {% if field.is_hidden %} 7 | {{ field }} 8 | {% else %} 9 | {% if field.errors %}{{ field.errors }}{% endif %} 10 | 15 | {% endif %} 16 | {% endfor %} 17 | 18 |

19 | 20 | 21 |

22 |
23 | -------------------------------------------------------------------------------- /example_project/example/templates/comments/example/list.html: -------------------------------------------------------------------------------- 1 |
2 | {% for comment in comment_list %} 3 |
4 |

{{ comment.comment }}

5 |
6 |
7 | on {{ comment.submit_date|date }} at {{ comment.submit_date|time }} by {{ comment.name }} 8 |
9 | {% endfor %} 10 |
11 | -------------------------------------------------------------------------------- /example_project/example/templates/example/post_detail.html: -------------------------------------------------------------------------------- 1 | {% load comments socialauth_tags %} 2 | 3 |

{{ post }}

4 |

{{ post.post }}

5 |
6 | posted by {{ post.author }} on {{ post.date|date }} at {{ post.date|time }} 7 |
8 |
9 | Comments: 10 |
11 | {% render_comment_list for post %} 12 |
13 | {% if request.user.username %} 14 | Comment as {% get_calculated_username request.user %} 15 | (Not {% get_calculated_username request.user %}?) 16 | {% render_comment_form for post %} 17 | {% else %} 18 | Login to comment 19 | {% endif %} 20 | -------------------------------------------------------------------------------- /example_project/example/templates/example/post_list.html: -------------------------------------------------------------------------------- 1 | {% load comments %} 2 | {{ latest }} 3 | {% if object_list %} 4 | {% for post in object_list %} 5 |

{{ post }}

6 |

{{ post.post }}

7 | {% get_comment_count for post as comment_count %} 8 | {{ post.author }} wrote {{ post.post|wordcount }} words on {{ post.date|date }} at {{ post.date|time }} 9 |
10 | 11 | {{ comment_count }} comments 12 | 13 | {% endfor %} 14 | {% else %} 15 | Add a new blog post 16 | {% endif %} 17 | -------------------------------------------------------------------------------- /example_project/example/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates two different styles of tests (one doctest and one 3 | unittest). These will both pass when you run "manage.py test". 4 | 5 | Replace these with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | class SimpleTest(TestCase): 11 | def test_basic_addition(self): 12 | """ 13 | Tests that 1 + 1 always equals 2. 14 | """ 15 | self.failUnlessEqual(1 + 1, 2) 16 | 17 | __test__ = {"doctest": """ 18 | Another way to test that 1 + 1 is equal to 2. 19 | 20 | >>> 1 + 1 == 2 21 | True 22 | """} 23 | 24 | -------------------------------------------------------------------------------- /example_project/example/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls.defaults import * 2 | from models import Post 3 | 4 | urlpatterns = patterns('', 5 | url(r'^(?P\d+)$', 'example.views.post_detail'), 6 | url(r'^$', 'django.views.generic.list_detail.object_list', 7 | { 'queryset' : Post.objects.all() } 8 | ), 9 | ) 10 | -------------------------------------------------------------------------------- /example_project/example/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | from django.shortcuts import render_to_response, get_object_or_404 3 | from django.http import HttpResponseRedirect 4 | from django.contrib.comments.models import Comment 5 | from models import Post 6 | from django.template import RequestContext 7 | 8 | def comment_posted(request): 9 | comment_id = request.GET['c'] 10 | post_id = Comment.objects.get(id=comment_id).content_object.id 11 | return HttpResponseRedirect('/blog/'+str(post_id)) 12 | 13 | def post_detail(request, post_id): 14 | post = get_object_or_404(Post, pk=post_id) 15 | return render_to_response('example/post_detail.html', {'post': post}, context_instance=RequestContext(request)) 16 | -------------------------------------------------------------------------------- /example_project/example_comments/__init__.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.utils.translation import ugettext_lazy as _ 3 | from django.contrib.comments.forms import CommentDetailsForm 4 | 5 | class CommentForm(CommentDetailsForm): 6 | name = forms.CharField(widget=forms.HiddenInput, required=False) 7 | email = forms.EmailField(widget=forms.HiddenInput, required=False) 8 | url = forms.URLField(widget=forms.HiddenInput, required=False) 9 | 10 | 11 | def get_form(): 12 | return CommentForm 13 | -------------------------------------------------------------------------------- /example_project/example_comments/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /example_project/example_comments/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates two different styles of tests (one doctest and one 3 | unittest). These will both pass when you run "manage.py test". 4 | 5 | Replace these with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | class SimpleTest(TestCase): 11 | def test_basic_addition(self): 12 | """ 13 | Tests that 1 + 1 always equals 2. 14 | """ 15 | self.failUnlessEqual(1 + 1, 2) 16 | 17 | __test__ = {"doctest": """ 18 | Another way to test that 1 + 1 is equal to 2. 19 | 20 | >>> 1 + 1 == 2 21 | True 22 | """} 23 | 24 | -------------------------------------------------------------------------------- /example_project/example_comments/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | 3 | from django.contrib.auth.decorators import login_required 4 | from django.contrib.comments.views.comments import post_comment as old_post_comment 5 | 6 | @login_required 7 | def post_comment(request): 8 | return old_post_comment(request) 9 | -------------------------------------------------------------------------------- /example_project/localsettings.example.py: -------------------------------------------------------------------------------- 1 | OPENID_REDIRECT_NEXT = '/accounts/openid/done/' 2 | 3 | OPENID_SREG = {"requred": "nickname, email, fullname", 4 | "optional":"postcode, country", 5 | "policy_url": ""} 6 | 7 | #example should be something more like the real thing, i think 8 | OPENID_AX = [{"type_uri": "http://axschema.org/contact/email", 9 | "count": 1, 10 | "required": True, 11 | "alias": "email"}, 12 | {"type_uri": "http://axschema.org/schema/fullname", 13 | "count":1 , 14 | "required": False, 15 | "alias": "fname"}] 16 | 17 | OPENID_AX_PROVIDER_MAP = {'Google': {'email': 'http://axschema.org/contact/email', 18 | 'firstname': 'http://axschema.org/namePerson/first', 19 | 'lastname': 'http://axschema.org/namePerson/last'}, 20 | 'Default': {'email': 'http://axschema.org/contact/email', 21 | 'fullname': 'http://axschema.org/namePerson', 22 | 'nickname': 'http://axschema.org/namePerson/friendly'} 23 | } 24 | 25 | TWITTER_CONSUMER_KEY = '' 26 | TWITTER_CONSUMER_SECRET = '' 27 | 28 | FACEBOOK_APP_ID = '' 29 | FACEBOOK_API_KEY = '' 30 | FACEBOOK_SECRET_KEY = '' 31 | 32 | LINKEDIN_CONSUMER_KEY = '' 33 | LINKEDIN_CONSUMER_SECRET = '' 34 | 35 | GITHUB_CLIENT_ID = '' 36 | GITHUB_CLIENT_SECRET = '' 37 | 38 | FOURSQUARE_CONSUMER_KEY = '' 39 | FOURSQUARE_CONSUMER_SECRET = '' 40 | FOURSQUARE_REGISTERED_REDIRECT_URI = '' 41 | 42 | ## if any of this information is desired for your app 43 | FACEBOOK_EXTENDED_PERMISSIONS = ( 44 | #'publish_stream', 45 | #'create_event', 46 | #'rsvp_event', 47 | #'sms', 48 | #'offline_access', 49 | #'email', 50 | #'read_stream', 51 | #'user_about_me', 52 | #'user_activites', 53 | #'user_birthday', 54 | #'user_education_history', 55 | #'user_events', 56 | #'user_groups', 57 | #'user_hometown', 58 | #'user_interests', 59 | #'user_likes', 60 | #'user_location', 61 | #'user_notes', 62 | #'user_online_presence', 63 | #'user_photo_video_tags', 64 | #'user_photos', 65 | #'user_relationships', 66 | #'user_religion_politics', 67 | #'user_status', 68 | #'user_videos', 69 | #'user_website', 70 | #'user_work_history', 71 | #'read_friendlists', 72 | #'read_requests', 73 | #'friend_about_me', 74 | #'friend_activites', 75 | #'friend_birthday', 76 | #'friend_education_history', 77 | #'friend_events', 78 | #'friend_groups', 79 | #'friend_hometown', 80 | #'friend_interests', 81 | #'friend_likes', 82 | #'friend_location', 83 | #'friend_notes', 84 | #'friend_online_presence', 85 | #'friend_photo_video_tags', 86 | #'friend_photos', 87 | #'friend_relationships', 88 | #'friend_religion_politics', 89 | #'friend_status', 90 | #'friend_videos', 91 | #'friend_website', 92 | #'friend_work_history', 93 | ) 94 | 95 | 96 | AUTHENTICATION_BACKENDS = ( 97 | 'django.contrib.auth.backends.ModelBackend', 98 | 'socialauth.auth_backends.OpenIdBackend', 99 | 'socialauth.auth_backends.TwitterBackend', 100 | 'socialauth.auth_backends.FacebookBackend', 101 | 'socialauth.auth_backends.LinkedInBackend', 102 | 'socialauth.auth_backends.GithubBackend', 103 | 'socialauth.auth_backends.FoursquareBackend', 104 | ) 105 | -------------------------------------------------------------------------------- /example_project/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from django.core.management import execute_manager 3 | try: 4 | import settings # Assumed to be in the same directory. 5 | except ImportError: 6 | import sys 7 | sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) 8 | sys.exit(1) 9 | 10 | if __name__ == "__main__": 11 | execute_manager(settings) 12 | -------------------------------------------------------------------------------- /example_project/media/css/openid.css: -------------------------------------------------------------------------------- 1 | #openid_form { 2 | width: 400px; 3 | } 4 | #openid_form legend { 5 | font-weight: bold; 6 | } 7 | #openid_choice { 8 | display: none; 9 | } 10 | #openid_input_area { 11 | clear: both; 12 | padding: 10px; 13 | } 14 | #openid_btns, #openid_btns br { 15 | clear: both; 16 | } 17 | #openid_highlight { 18 | padding: 3px; 19 | background-color: #FFFCC9; 20 | float: left; 21 | } 22 | .openid_large_btn { 23 | width: 100px; 24 | height: 60px; 25 | border: 1px solid #DDD; 26 | margin: 3px; 27 | float: left; 28 | } 29 | .openid_small_btn { 30 | width: 24px; 31 | height: 24px; 32 | border: 1px solid #DDD; 33 | margin: 3px; 34 | float: left; 35 | } 36 | a.openid_large_btn:focus { 37 | outline: none; 38 | } 39 | a.openid_large_btn:focus{ 40 | -moz-outline-style: none; 41 | } 42 | .openid_selected { 43 | border: 4px solid #DDD; 44 | } -------------------------------------------------------------------------------- /example_project/media/css/socialauth.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin-left: auto; 3 | margin-right: auto; 4 | width: 950px; 5 | } 6 | #openid_choice{ 7 | margin-top:10px; 8 | display: none; 9 | } 10 | #openid_input_area{ 11 | padding-top:10px; 12 | clear: both; 13 | } 14 | #openid_username{ 15 | margin-right:5px; 16 | } 17 | #openid_btns, #openid_btns br{ 18 | clear: both; 19 | } 20 | #openid_btns #facebook{ 21 | height: 60px; 22 | width: 110px; 23 | float: left; 24 | margin-top: 3px; 25 | display: table-cell; 26 | text-align: center; 27 | vertical-align: middle; 28 | border: 1px solid #ccc; 29 | } 30 | #openid_highlight{ 31 | padding: 3px; 32 | background-color: #FFFCC9; 33 | float: left; 34 | } 35 | .openid_large_btn{ 36 | width: 100px; 37 | height: 60px; 38 | border: 1px solid #DDD; 39 | margin: 3px; 40 | float: left; 41 | } 42 | .openid_small_btn{ 43 | width: 24px; 44 | height: 24px; 45 | border: 1px solid #DDD; 46 | margin: 3px; 47 | float: left; 48 | } 49 | a.openid_large_btn:focus{ 50 | outline: none; 51 | } 52 | a.openid_large_btn:focus{ 53 | -moz-outline-style: none; 54 | } 55 | .openid_selected{ 56 | border: 4px solid #DDD; 57 | } 58 | .linkedin{ 59 | background: #FFF url(/site_media/images/linkedin.jpg) no-repeat center center; 60 | } 61 | .yahoo{ 62 | background: #FFF url(/site_media/images/yahoo.gif) no-repeat center center; 63 | } 64 | .google{ 65 | background: #FFF url(/site_media/images/google.png) no-repeat center center; 66 | } 67 | .openid{ 68 | background: #FFF url(/site_media/images/openid.png) no-repeat center center; 69 | } 70 | .twitter{ 71 | background: #FFF url(/site_media/images/twitter.png) no-repeat center center; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /example_project/media/images/aol.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/aol.gif -------------------------------------------------------------------------------- /example_project/media/images/blogger.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/blogger.ico -------------------------------------------------------------------------------- /example_project/media/images/claimid.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/claimid.ico -------------------------------------------------------------------------------- /example_project/media/images/facebook.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/facebook.gif -------------------------------------------------------------------------------- /example_project/media/images/flickr.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/flickr.ico -------------------------------------------------------------------------------- /example_project/media/images/google.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/google.gif -------------------------------------------------------------------------------- /example_project/media/images/linkedin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/linkedin.jpg -------------------------------------------------------------------------------- /example_project/media/images/livejournal.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/livejournal.ico -------------------------------------------------------------------------------- /example_project/media/images/myopenid.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/myopenid.ico -------------------------------------------------------------------------------- /example_project/media/images/openid-inputicon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/openid-inputicon.gif -------------------------------------------------------------------------------- /example_project/media/images/openid.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/openid.gif -------------------------------------------------------------------------------- /example_project/media/images/technorati.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/technorati.ico -------------------------------------------------------------------------------- /example_project/media/images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/twitter.png -------------------------------------------------------------------------------- /example_project/media/images/verisign.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/verisign.ico -------------------------------------------------------------------------------- /example_project/media/images/vidoop.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/vidoop.ico -------------------------------------------------------------------------------- /example_project/media/images/wordpress.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/wordpress.ico -------------------------------------------------------------------------------- /example_project/media/images/yahoo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/example_project/media/images/yahoo.gif -------------------------------------------------------------------------------- /example_project/media/js/openid-jquery.js: -------------------------------------------------------------------------------- 1 | /* 2 | Simple OpenID Plugin 3 | http://code.google.com/p/openid-selector/ 4 | 5 | This code is licenced under the New BSD License. 6 | */ 7 | 8 | var providers_large = { 9 | google: { 10 | name: 'Google', 11 | url: 'https://www.google.com/accounts/o8/id' 12 | }, 13 | yahoo: { 14 | name: 'Yahoo', 15 | url: 'https://me.yahoo.com/' 16 | }, 17 | aol: { 18 | name: 'AOL', 19 | label: 'Enter your AOL screenname.', 20 | url: 'http://openid.aol.com/{username}/' 21 | }, 22 | openid: { 23 | name: 'OpenID', 24 | label: 'Enter your OpenID.', 25 | url: null 26 | } 27 | }; 28 | var providers_small = { 29 | myopenid: { 30 | name: 'MyOpenID', 31 | label: 'Enter your MyOpenID username.', 32 | url: 'http://{username}.myopenid.com/' 33 | }, 34 | livejournal: { 35 | name: 'LiveJournal', 36 | label: 'Enter your Livejournal username.', 37 | url: 'http://{username}.livejournal.com/' 38 | }, 39 | flickr: { 40 | name: 'Flickr', 41 | label: 'Enter your Flickr username.', 42 | url: 'http://flickr.com/photos/{username}/' 43 | }, 44 | technorati: { 45 | name: 'Technorati', 46 | label: 'Enter your Technorati username.', 47 | url: 'http://technorati.com/people/technorati/{username}/' 48 | }, 49 | wordpress: { 50 | name: 'Wordpress', 51 | label: 'Enter your Wordpress.com username.', 52 | url: 'http://{username}.wordpress.com/' 53 | }, 54 | blogger: { 55 | name: 'Blogger', 56 | label: 'Your Blogger account', 57 | url: 'http://{username}.blogspot.com/' 58 | }, 59 | verisign: { 60 | name: 'Verisign', 61 | label: 'Your Verisign username', 62 | url: 'http://{username}.pip.verisignlabs.com/' 63 | }, 64 | vidoop: { 65 | name: 'Vidoop', 66 | label: 'Your Vidoop username', 67 | url: 'http://{username}.myvidoop.com/' 68 | }, 69 | verisign: { 70 | name: 'Verisign', 71 | label: 'Your Verisign username', 72 | url: 'http://{username}.pip.verisignlabs.com/' 73 | }, 74 | claimid: { 75 | name: 'ClaimID', 76 | label: 'Your ClaimID username', 77 | url: 'http://claimid.com/{username}' 78 | } 79 | }; 80 | var providers = $.extend({}, providers_large, providers_small); 81 | 82 | var openid = { 83 | 84 | cookie_expires: 6*30, // 6 months. 85 | cookie_name: 'openid_provider', 86 | cookie_path: '/', 87 | 88 | img_path: '/site_media/images/', 89 | 90 | input_id: null, 91 | provider_url: null, 92 | 93 | init: function(input_id) { 94 | 95 | var openid_btns = $('#openid_btns'); 96 | 97 | this.input_id = input_id; 98 | 99 | $('#openid_choice').show(); 100 | $('#openid_input_area').empty(); 101 | // add box for each provider 102 | for (id in providers_large) { 103 | 104 | openid_btns.append(this.getBoxHTML(providers_large[id], 'large', '.gif')); 105 | } 106 | if (providers_small) { 107 | openid_btns.append('
'); 108 | 109 | for (id in providers_small) { 110 | 111 | openid_btns.append(this.getBoxHTML(providers_small[id], 'small', '.ico')); 112 | } 113 | } 114 | 115 | $('#openid_form').submit(this.submit); 116 | 117 | var box_id = this.readCookie(); 118 | if (box_id) { 119 | this.signin(box_id, true); 120 | } 121 | }, 122 | getBoxHTML: function(provider, box_size, image_ext) { 123 | 124 | var box_id = provider["name"].toLowerCase(); 125 | return ''; 128 | 129 | }, 130 | /* Provider image click */ 131 | signin: function(box_id, onload) { 132 | 133 | var provider = providers[box_id]; 134 | if (! provider) { 135 | return; 136 | } 137 | 138 | this.highlight(box_id); 139 | this.setCookie(box_id); 140 | 141 | // prompt user for input? 142 | if (provider['label']) { 143 | 144 | this.useInputBox(provider); 145 | this.provider_url = provider['url']; 146 | 147 | } else { 148 | 149 | this.setOpenIdUrl(provider['url']); 150 | if (! onload) { 151 | $('#openid_form').submit(); 152 | } 153 | } 154 | }, 155 | /* Sign-in button click */ 156 | submit: function() { 157 | 158 | var url = openid.provider_url; 159 | if (url) { 160 | url = url.replace('{username}', $('#openid_username').val()); 161 | openid.setOpenIdUrl(url); 162 | } 163 | return true; 164 | }, 165 | setOpenIdUrl: function (url) { 166 | 167 | var hidden = $('#'+this.input_id); 168 | if (hidden.length > 0) { 169 | hidden.value = url; 170 | } else { 171 | $('#openid_form').append(''); 172 | } 173 | }, 174 | highlight: function (box_id) { 175 | 176 | // remove previous highlight. 177 | var highlight = $('#openid_highlight'); 178 | if (highlight) { 179 | highlight.replaceWith($('#openid_highlight a')[0]); 180 | } 181 | // add new highlight. 182 | $('.'+box_id).wrap('
'); 183 | }, 184 | setCookie: function (value) { 185 | 186 | var date = new Date(); 187 | date.setTime(date.getTime()+(this.cookie_expires*24*60*60*1000)); 188 | var expires = "; expires="+date.toGMTString(); 189 | 190 | document.cookie = this.cookie_name+"="+value+expires+"; path=" + this.cookie_path; 191 | }, 192 | readCookie: function () { 193 | var nameEQ = this.cookie_name + "="; 194 | var ca = document.cookie.split(';'); 195 | for(var i=0;i < ca.length;i++) { 196 | var c = ca[i]; 197 | while (c.charAt(0)==' ') c = c.substring(1,c.length); 198 | if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); 199 | } 200 | return null; 201 | }, 202 | useInputBox: function (provider) { 203 | 204 | var input_area = $('#openid_input_area'); 205 | 206 | var html = ''; 207 | var id = 'openid_username'; 208 | var value = ''; 209 | var label = provider['label']; 210 | var style = ''; 211 | 212 | if (label) { 213 | html = '

' + label + '

'; 214 | } 215 | if (provider['name'] == 'OpenID') { 216 | id = this.input_id; 217 | value = 'http://'; 218 | style = 'background:#FFF url('+this.img_path+'openid-inputicon.gif) no-repeat scroll 0 50%; padding-left:18px;'; 219 | } 220 | html += '' + 221 | ''; 222 | 223 | input_area.empty(); 224 | input_area.append(html); 225 | 226 | $('#'+id).focus(); 227 | } 228 | }; 229 | -------------------------------------------------------------------------------- /example_project/openid_consumer: -------------------------------------------------------------------------------- 1 | /home/tuxcanfly/Work/Django-Socialauth/openid_consumer -------------------------------------------------------------------------------- /example_project/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for socialauthdemo project. 2 | 3 | DEBUG = True 4 | TEMPLATE_DEBUG = DEBUG 5 | 6 | ADMINS = ( 7 | # ('Your Name', 'your_email@domain.com'), 8 | ) 9 | 10 | MANAGERS = ADMINS 11 | 12 | DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. 13 | DATABASE_NAME = 'data.db' # Or path to database file if using sqlite3. 14 | DATABASE_USER = '' # Not used with sqlite3. 15 | DATABASE_PASSWORD = '' # Not used with sqlite3. 16 | DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. 17 | DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. 18 | 19 | # Local time zone for this installation. Choices can be found here: 20 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 21 | # although not all choices may be available on all operating systems. 22 | # If running in a Windows environment this must be set to the same as your 23 | # system time zone. 24 | TIME_ZONE = 'America/Chicago' 25 | 26 | # Language code for this installation. All choices can be found here: 27 | # http://www.i18nguy.com/unicode/language-identifiers.html 28 | LANGUAGE_CODE = 'en-us' 29 | 30 | SITE_ID = 1 31 | 32 | # If you set this to False, Django will make some optimizations so as not 33 | # to load the internationalization machinery. 34 | USE_I18N = True 35 | 36 | # Absolute path to the directory that holds media. 37 | # Example: "/home/media/media.lawrence.com/" 38 | MEDIA_ROOT = '' 39 | 40 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 41 | # trailing slash if there is a path component (optional in other cases). 42 | # Examples: "http://media.lawrence.com", "http://example.com/media/" 43 | MEDIA_URL = '' 44 | 45 | # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a 46 | # trailing slash. 47 | # Examples: "http://foo.com/media/", "/media/". 48 | ADMIN_MEDIA_PREFIX = '/media/' 49 | 50 | # Make this unique, and don't share it with anybody. 51 | SECRET_KEY = 'tnrm*t2b80nu51mgz3kc2%_v6bv8e)8rup$03m)dp7xb0)m8t9' 52 | 53 | # List of callables that know how to import templates from various sources. 54 | TEMPLATE_LOADERS = ( 55 | 'django.template.loaders.filesystem.load_template_source', 56 | 'django.template.loaders.app_directories.load_template_source', 57 | # 'django.template.loaders.eggs.load_template_source', 58 | ) 59 | 60 | MIDDLEWARE_CLASSES = ( 61 | 'django.middleware.common.CommonMiddleware', 62 | 'django.contrib.sessions.middleware.SessionMiddleware', 63 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 64 | 'openid_consumer.middleware.OpenIDMiddleware', 65 | 'django.middleware.csrf.CsrfViewMiddleware', 66 | #'socialauth.middleware.FacebookConnectMiddleware' 67 | ) 68 | 69 | TEMPLATE_CONTEXT_PROCESSORS = ( 70 | "socialauth.context_processors.facebook_api_key", 71 | 'django.core.context_processors.media', 72 | "django.contrib.auth.context_processors.auth", 73 | "django.core.context_processors.request", 74 | ) 75 | 76 | ROOT_URLCONF = 'urls' 77 | 78 | TEMPLATE_DIRS = ( 79 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 80 | # Always use forward slashes, even on Windows. 81 | # Don't forget to use absolute paths, not relative paths. 82 | ) 83 | 84 | INSTALLED_APPS = ( 85 | 'django.contrib.auth', 86 | 'django.contrib.contenttypes', 87 | 'django.contrib.sessions', 88 | 'django.contrib.sites', 89 | 'django.contrib.admin', 90 | 'django.contrib.comments', 91 | 'socialauth', 92 | 'openid_consumer', 93 | 'commentor', 94 | 'example', 95 | 'example_comments', 96 | ) 97 | 98 | COMMENTS_APP = 'example_comments' 99 | 100 | LOGIN_REDIRECT_URL = '/' 101 | 102 | LOGOUT_REDIRECT_URL = '/' 103 | 104 | try: 105 | from localsettings import * 106 | except ImportError: 107 | pass 108 | 109 | import os 110 | MEDIA_ROOT = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'media')) 111 | MEDIA_URL = '/site_media/' 112 | 113 | SITE_NAME = 'foobar' 114 | -------------------------------------------------------------------------------- /example_project/socialauth: -------------------------------------------------------------------------------- 1 | /home/tuxcanfly/Work/Django-Socialauth/socialauth -------------------------------------------------------------------------------- /example_project/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls.defaults import * 2 | from commentor.views import leave_comment 3 | 4 | from django.contrib import admin 5 | admin.autodiscover() 6 | 7 | urlpatterns = patterns('', 8 | (r'^blog/', include('example.urls')), 9 | (r'^accounts/', include('socialauth.urls')), 10 | (r'^admin/', admin.site.urls), 11 | #(r'^$', leave_comment), 12 | (r'^$', 'socialauth.views.signin_complete'), 13 | (r'^comments/post/', 'example_comments.views.post_comment'), 14 | (r'comments/', include('django.contrib.comments.urls')), 15 | ) 16 | 17 | from django.conf import settings 18 | if settings.DEBUG: 19 | 20 | urlpatterns += patterns('', 21 | # This is for the CSS and static files: 22 | (r'^site_media/(?P.*)$', 'django.views.static.serve', 23 | {'document_root': settings.MEDIA_ROOT}), 24 | ) 25 | 26 | -------------------------------------------------------------------------------- /ez_setup.py: -------------------------------------------------------------------------------- 1 | #!python 2 | """Bootstrap setuptools installation 3 | 4 | If you want to use setuptools in your package's setup.py, just include this 5 | file in the same directory with it, and add this to the top of your setup.py:: 6 | 7 | from ez_setup import use_setuptools 8 | use_setuptools() 9 | 10 | If you want to require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, you can do so by supplying 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import sys 17 | DEFAULT_VERSION = "0.6c9" 18 | DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] 19 | 20 | md5_data = { 21 | 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', 22 | 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', 23 | 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', 24 | 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', 25 | 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', 26 | 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', 27 | 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', 28 | 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', 29 | 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', 30 | 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', 31 | 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', 32 | 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', 33 | 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', 34 | 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', 35 | 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', 36 | 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', 37 | 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', 38 | 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', 39 | 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', 40 | 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', 41 | 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', 42 | 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', 43 | 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', 44 | 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', 45 | 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', 46 | 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', 47 | 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', 48 | 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', 49 | 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', 50 | 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', 51 | 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03', 52 | 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a', 53 | 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6', 54 | 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a', 55 | } 56 | 57 | import sys, os 58 | try: from hashlib import md5 59 | except ImportError: from md5 import md5 60 | 61 | def _validate_md5(egg_name, data): 62 | if egg_name in md5_data: 63 | digest = md5(data).hexdigest() 64 | if digest != md5_data[egg_name]: 65 | print >>sys.stderr, ( 66 | "md5 validation of %s failed! (Possible download problem?)" 67 | % egg_name 68 | ) 69 | sys.exit(2) 70 | return data 71 | 72 | def use_setuptools( 73 | version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, 74 | download_delay=15 75 | ): 76 | """Automatically find/download setuptools and make it available on sys.path 77 | 78 | `version` should be a valid setuptools version number that is available 79 | as an egg for download under the `download_base` URL (which should end with 80 | a '/'). `to_dir` is the directory where setuptools will be downloaded, if 81 | it is not already available. If `download_delay` is specified, it should 82 | be the number of seconds that will be paused before initiating a download, 83 | should one be required. If an older version of setuptools is installed, 84 | this routine will print a message to ``sys.stderr`` and raise SystemExit in 85 | an attempt to abort the calling script. 86 | """ 87 | was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules 88 | def do_download(): 89 | egg = download_setuptools(version, download_base, to_dir, download_delay) 90 | sys.path.insert(0, egg) 91 | import setuptools; setuptools.bootstrap_install_from = egg 92 | try: 93 | import pkg_resources 94 | except ImportError: 95 | return do_download() 96 | try: 97 | pkg_resources.require("setuptools>="+version); return 98 | except pkg_resources.VersionConflict, e: 99 | if was_imported: 100 | print >>sys.stderr, ( 101 | "The required version of setuptools (>=%s) is not available, and\n" 102 | "can't be installed while this script is running. Please install\n" 103 | " a more recent version first, using 'easy_install -U setuptools'." 104 | "\n\n(Currently using %r)" 105 | ) % (version, e.args[0]) 106 | sys.exit(2) 107 | else: 108 | del pkg_resources, sys.modules['pkg_resources'] # reload ok 109 | return do_download() 110 | except pkg_resources.DistributionNotFound: 111 | return do_download() 112 | 113 | 114 | 115 | def download_setuptools( 116 | version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, 117 | delay = 15 118 | ): 119 | """Download setuptools from a specified location and return its filename 120 | 121 | `version` should be a valid setuptools version number that is available 122 | as an egg for download under the `download_base` URL (which should end 123 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 124 | `delay` is the number of seconds to pause before an actual download attempt. 125 | """ 126 | import urllib2, shutil 127 | egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) 128 | url = download_base + egg_name 129 | saveto = os.path.join(to_dir, egg_name) 130 | src = dst = None 131 | if not os.path.exists(saveto): # Avoid repeated downloads 132 | try: 133 | from distutils import log 134 | if delay: 135 | log.warn(""" 136 | --------------------------------------------------------------------------- 137 | This script requires setuptools version %s to run (even to display 138 | help). I will attempt to download it for you (from 139 | %s), but 140 | you may need to enable firewall access for this script first. 141 | I will start the download in %d seconds. 142 | 143 | (Note: if this machine does not have network access, please obtain the file 144 | 145 | %s 146 | 147 | and place it in this directory before rerunning this script.) 148 | ---------------------------------------------------------------------------""", 149 | version, download_base, delay, url 150 | ); from time import sleep; sleep(delay) 151 | log.warn("Downloading %s", url) 152 | src = urllib2.urlopen(url) 153 | # Read/write all in one block, so we don't create a corrupt file 154 | # if the download is interrupted. 155 | data = _validate_md5(egg_name, src.read()) 156 | dst = open(saveto,"wb"); dst.write(data) 157 | finally: 158 | if src: src.close() 159 | if dst: dst.close() 160 | return os.path.realpath(saveto) 161 | 162 | 163 | def main(argv, version=DEFAULT_VERSION): 164 | """Install or upgrade setuptools and EasyInstall""" 165 | try: 166 | import setuptools 167 | except ImportError: 168 | egg = None 169 | try: 170 | egg = download_setuptools(version, delay=0) 171 | sys.path.insert(0,egg) 172 | from setuptools.command.easy_install import main 173 | return main(list(argv)+[egg]) # we're done here 174 | finally: 175 | if egg and os.path.exists(egg): 176 | os.unlink(egg) 177 | else: 178 | if setuptools.__version__ == '0.0.1': 179 | print >>sys.stderr, ( 180 | "You have an obsolete version of setuptools installed. Please\n" 181 | "remove it from your system entirely before rerunning this script." 182 | ) 183 | sys.exit(2) 184 | 185 | req = "setuptools>="+version 186 | import pkg_resources 187 | try: 188 | pkg_resources.require(req) 189 | except pkg_resources.VersionConflict: 190 | try: 191 | from setuptools.command.easy_install import main 192 | except ImportError: 193 | from easy_install import main 194 | main(list(argv)+[download_setuptools(delay=0)]) 195 | sys.exit(0) # try to force an exit 196 | else: 197 | if argv: 198 | from setuptools.command.easy_install import main 199 | main(argv) 200 | else: 201 | print "Setuptools version",version,"or greater has been installed." 202 | print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' 203 | 204 | 205 | 206 | def update_md5(filenames): 207 | """Update our built-in md5 registry""" 208 | 209 | import re 210 | 211 | for name in filenames: 212 | base = os.path.basename(name) 213 | f = open(name,'rb') 214 | md5_data[base] = md5(f.read()).hexdigest() 215 | f.close() 216 | 217 | data = [" %r: %r,\n" % it for it in md5_data.items()] 218 | data.sort() 219 | repl = "".join(data) 220 | 221 | import inspect 222 | srcfile = inspect.getsourcefile(sys.modules[__name__]) 223 | f = open(srcfile, 'rb'); src = f.read(); f.close() 224 | 225 | match = re.search("\nmd5_data = {\n([^}]+)}", src) 226 | if not match: 227 | print >>sys.stderr, "Internal error!" 228 | sys.exit(2) 229 | 230 | src = src[:match.start(1)] + repl + src[match.end(1):] 231 | f = open(srcfile,'w') 232 | f.write(src) 233 | f.close() 234 | 235 | 236 | if __name__=='__main__': 237 | if len(sys.argv)>2 and sys.argv[1]=='--md5update': 238 | update_md5(sys.argv[2:]) 239 | else: 240 | main(sys.argv[1:]) 241 | 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /openid_consumer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/openid_consumer/__init__.py -------------------------------------------------------------------------------- /openid_consumer/locale/en/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/openid_consumer/locale/en/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /openid_consumer/locale/en/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # English language file for django-openid-consumer package 2 | # Copyright (C) 2008 Matjaž Črnko 3 | # This file is distributed under the same license as the django-openid-consumer package. 4 | # Matjaž Črnko , 2008. 5 | #, fuzzy 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: 0.1\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2008-08-07 13:23+0200\n" 11 | "PO-Revision-Date: 2008-8-1 21:02+0100\n" 12 | "Last-Translator: Matjaž Črnko \n" 13 | "Language-Team: none\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: views.py:91 19 | msgid "i-names are not supported" 20 | msgstr "i-names are not supported" 21 | 22 | #: views.py:97 23 | msgid "The OpenID was invalid" 24 | msgstr "The OpenID was invalid" 25 | 26 | #: views.py:128 27 | msgid "The request was cancelled" 28 | msgstr "The request was cancelled" 29 | 30 | #: views.py:132 31 | msgid "Setup needed" 32 | msgstr "Setup needed" 33 | 34 | #~ msgid "OpenID failed" 35 | #~ msgstr "OpenID failed" 36 | 37 | #~ msgid "Sign in with your OpenID" 38 | #~ msgstr "Sign in with your OpenID" 39 | 40 | #~ msgid "Sign in" 41 | #~ msgstr "Sign in" 42 | -------------------------------------------------------------------------------- /openid_consumer/locale/sl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/openid_consumer/locale/sl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /openid_consumer/locale/sl/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Slovenski prevod django-openid-consumer paketa 2 | # Copyright (C) 2008 Matjaž Črnko 3 | # This file is distributed under the same license as the django-openid-consumer package. 4 | # Matjaž Črnko , 2008. 5 | #, fuzzy 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: 0.1\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2008-08-07 13:23+0200\n" 11 | "PO-Revision-Date: 2008-8-1 21:02+0100\n" 12 | "Last-Translator: Matjaž Črnko \n" 13 | "Language-Team: none\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: views.py:91 19 | msgid "i-names are not supported" 20 | msgstr "i-name ni podprt" 21 | 22 | #: views.py:97 23 | msgid "The OpenID was invalid" 24 | msgstr "OpenID je neveljaven" 25 | 26 | #: views.py:128 27 | msgid "The request was cancelled" 28 | msgstr "Povezava je bila prekinjena" 29 | 30 | #: views.py:132 31 | msgid "Setup needed" 32 | msgstr "Postavitev potrebna" 33 | 34 | #~ msgid "OpenID failed" 35 | #~ msgstr "Napaka pri prijavi z OpenID" 36 | 37 | #~ msgid "Sign in with your OpenID" 38 | #~ msgstr "Prijavite se z OpenID" 39 | 40 | #~ msgid "Sign in" 41 | #~ msgstr "Prijava" 42 | -------------------------------------------------------------------------------- /openid_consumer/middleware.py: -------------------------------------------------------------------------------- 1 | class OpenIDMiddleware(object): 2 | """ 3 | Populate request.openid and request.openids with their openid. This comes 4 | either from their cookie or from their session, depending on the presence 5 | of OPENID_USE_SESSIONS. 6 | """ 7 | def process_request(self, request): 8 | request.openids = request.session.get('openids', []) 9 | if request.openids: 10 | request.openid = request.openids[-1] # Last authenticated OpenID 11 | else: 12 | request.openid = None 13 | -------------------------------------------------------------------------------- /openid_consumer/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class Nonce(models.Model): 4 | server_url = models.URLField() 5 | timestamp = models.IntegerField() 6 | salt = models.CharField( max_length=50 ) 7 | 8 | def __unicode__(self): 9 | return "Nonce: %s" % self.nonce 10 | 11 | class Association(models.Model): 12 | server_url = models.TextField(max_length=2047) 13 | handle = models.CharField(max_length=255) 14 | secret = models.TextField(max_length=255) # Stored base64 encoded 15 | issued = models.IntegerField() 16 | lifetime = models.IntegerField() 17 | assoc_type = models.TextField(max_length=64) 18 | 19 | def __unicode__(self): 20 | return "Association: %s, %s" % (self.server_url, self.handle) 21 | -------------------------------------------------------------------------------- /openid_consumer/templates/openid_consumer/failure.html: -------------------------------------------------------------------------------- 1 | {% extends 'openid_consumer/openid_consumer_base.html' %} 2 | {% load i18n %} 3 | 4 | 5 | {% block title %} 6 | 7 | {{ block.super }} - {% trans "OpenID failed" %} 8 | 9 | {% endblock %} 10 | 11 | {% block main_content %} 12 | 13 |

{% trans "OpenID failed" %}:

14 | 15 |

{{ message|escape }}

16 | 17 |



18 | 19 | 20 | {% endblock %} 21 | 22 | 23 | 24 | {# 25 | {% load i18n %} 26 | 27 | 28 | {% trans "OpenID failed" %} 29 | 30 | 31 | 32 | 33 |

{% trans "OpenID failed" %}:

34 | 35 |

{{ message|escape }}

36 | 37 | 38 | #} 39 | -------------------------------------------------------------------------------- /openid_consumer/templates/openid_consumer/openid_consumer_base.html: -------------------------------------------------------------------------------- 1 | {% extends 'socialauth/base.html' %} 2 | -------------------------------------------------------------------------------- /openid_consumer/templates/openid_consumer/signin.html: -------------------------------------------------------------------------------- 1 | {% extends 'openid_consumer/openid_consumer_base.html' %} 2 | {% load i18n %} 3 | 4 | {% block title %} 5 | 6 | {{ block.super }} - Sign in with your OpenID 7 | 8 | {% endblock %} 9 | 10 | {% block main_content %} 11 | 12 |

{% trans "Sign in with your OpenID" %}

13 | 14 |
15 |

16 |
17 | 18 |



19 | 20 | 21 | {% endblock %} 22 | 23 | 24 | {# 25 | 26 | 27 | {% trans "Sign in with your OpenID" %} 28 | 29 | 30 | 31 | 32 | 33 | 34 | #} 35 | -------------------------------------------------------------------------------- /openid_consumer/util.py: -------------------------------------------------------------------------------- 1 | import openid 2 | if openid.__version__ < '2.1.0': 3 | from openid.sreg import SRegResponse 4 | else: 5 | from openid.extensions.sreg import SRegResponse 6 | from openid.extensions.ax import FetchResponse as AXFetchResponse 7 | try: 8 | from openid.extensions.pape import Response as PapeResponse 9 | except ImportError: 10 | from openid.extensions import pape as openid_pape 11 | PapeResponse = openid_pape.Response 12 | 13 | from openid.store import nonce as oid_nonce 14 | from openid.store.interface import OpenIDStore 15 | from openid.association import Association as OIDAssociation 16 | from openid.yadis import xri 17 | 18 | import time, base64, hashlib 19 | 20 | from django.conf import settings 21 | from models import Association, Nonce 22 | 23 | class OpenID: 24 | def __init__(self, openid, issued, 25 | attrs=None, sreg=None, pape=None, ax=None): 26 | self.openid = openid 27 | self.issued = issued 28 | self.attrs = attrs or {} 29 | self.sreg = sreg or {} 30 | self.pape = pape or {} 31 | self.ax = ax or {} 32 | self.is_iname = (xri.identifierScheme(openid) == 'XRI') 33 | 34 | def __repr__(self): 35 | return '' % self.openid 36 | 37 | def __str__(self): 38 | return self.openid 39 | 40 | class DjangoOpenIDStore(OpenIDStore): 41 | def __init__(self): 42 | self.max_nonce_age = 6 * 60 * 60 # Six hours 43 | 44 | def storeAssociation(self, server_url, association): 45 | assoc = Association( 46 | server_url = server_url, 47 | handle = association.handle, 48 | secret = base64.encodestring(association.secret), 49 | issued = association.issued, 50 | lifetime = association.issued, 51 | assoc_type = association.assoc_type 52 | ) 53 | assoc.save() 54 | 55 | def getAssociation(self, server_url, handle=None): 56 | assocs = [] 57 | if handle is not None: 58 | assocs = Association.objects.filter( 59 | server_url = server_url, handle = handle 60 | ) 61 | else: 62 | assocs = Association.objects.filter( 63 | server_url = server_url 64 | ) 65 | if not assocs: 66 | return None 67 | associations = [] 68 | for assoc in assocs: 69 | association = OIDAssociation( 70 | assoc.handle, base64.decodestring(assoc.secret), assoc.issued, 71 | assoc.lifetime, assoc.assoc_type 72 | ) 73 | if association.getExpiresIn() == 0: 74 | self.removeAssociation(server_url, assoc.handle) 75 | else: 76 | associations.append((association.issued, association)) 77 | if not associations: 78 | return None 79 | return associations[-1][1] 80 | 81 | def removeAssociation(self, server_url, handle): 82 | assocs = list(Association.objects.filter( 83 | server_url = server_url, handle = handle 84 | )) 85 | assocs_exist = len(assocs) > 0 86 | for assoc in assocs: 87 | assoc.delete() 88 | return assocs_exist 89 | 90 | def storeNonce(self, nonce): 91 | nonce, created = Nonce.objects.get_or_create( 92 | nonce = nonce, defaults={'expires': int(time.time())} 93 | ) 94 | 95 | def useNonce(self, server_url, timestamp, salt): 96 | if abs(timestamp - time.time()) > oid_nonce.SKEW: 97 | return False 98 | 99 | try: 100 | nonce = Nonce(server_url=server_url, 101 | timestamp=timestamp, 102 | salt=salt) 103 | nonce.save() 104 | except: 105 | raise 106 | else: 107 | return 1 108 | 109 | def getAuthKey(self): 110 | # Use first AUTH_KEY_LEN characters of md5 hash of SECRET_KEY 111 | return hashlib.md5(settings.SECRET_KEY).hexdigest()[:self.AUTH_KEY_LEN] 112 | 113 | def from_openid_response(openid_response): 114 | issued = int(time.time()) 115 | 116 | openid = OpenID(openid_response.identity_url, issued, 117 | openid_response.signed_fields) 118 | 119 | if getattr(settings, 'OPENID_PAPE', False): 120 | openid.pape = PapeResponse.fromSuccessResponse(openid_response) 121 | 122 | if getattr(settings, 'OPENID_SREG', False): 123 | openid.sreg = SRegResponse.fromSuccessResponse(openid_response) 124 | 125 | if getattr(settings, 'OPENID_AX', False): 126 | openid.ax = AXFetchResponse.fromSuccessResponse(openid_response) 127 | 128 | return openid 129 | -------------------------------------------------------------------------------- /openid_consumer/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse, HttpResponseRedirect 2 | from django.shortcuts import render_to_response as render 3 | from django.template import RequestContext 4 | from django.conf import settings 5 | from django.utils.translation import ugettext_lazy as _ 6 | 7 | import re, time, urllib 8 | 9 | import openid 10 | if openid.__version__ < '2.0.0': 11 | raise ImportError, 'You need python-openid 2.0.0 or newer' 12 | elif openid.__version__ < '2.1.0': 13 | from openid.sreg import SRegRequest 14 | else: 15 | from openid.extensions.sreg import SRegRequest 16 | try: 17 | from openid.extensions.pape import Request as PapeRequest 18 | except ImportError: 19 | from openid.extensions import pape as openid_pape 20 | PapeRequest = openid_pape.Request 21 | from openid.extensions.ax import FetchRequest as AXFetchRequest 22 | from openid.extensions.ax import AttrInfo 23 | 24 | from openid.consumer.consumer import Consumer, \ 25 | SUCCESS, CANCEL, FAILURE, SETUP_NEEDED 26 | from openid.consumer.discover import DiscoveryFailure 27 | from openid.yadis import xri 28 | 29 | from util import OpenID, DjangoOpenIDStore, from_openid_response 30 | from middleware import OpenIDMiddleware 31 | 32 | from django.utils.html import escape 33 | 34 | def get_url_host(request): 35 | if request.is_secure(): 36 | protocol = 'https' 37 | else: 38 | protocol = 'http' 39 | host = escape(request.get_host()) 40 | return '%s://%s' % (protocol, host) 41 | 42 | def get_full_url(request): 43 | return get_url_host(request) + request.get_full_path() 44 | 45 | next_url_re = re.compile('^/[-\w/]+$') 46 | 47 | def is_valid_next_url(next): 48 | # When we allow this: 49 | # /openid/?next=/welcome/ 50 | # For security reasons we want to restrict the next= bit to being a local 51 | # path, not a complete URL. 52 | return bool(next_url_re.match(next)) 53 | 54 | def begin(request, redirect_to=None, on_failure=None, user_url=None, 55 | template_name='openid_consumer/signin.html'): 56 | on_failure = on_failure or default_on_failure 57 | trust_root = getattr( 58 | settings, 'OPENID_TRUST_ROOT', get_url_host(request) + '/' 59 | ) 60 | 61 | 62 | # foo derbis. 63 | redirect_to = redirect_to or getattr( 64 | settings, 'OPENID_REDIRECT_TO', 65 | # If not explicitly set, assume current URL with complete/ appended 66 | get_full_url(request).split('?')[0] + 'complete/' 67 | ) 68 | # In case they were lazy... 69 | if not ( 70 | redirect_to.startswith('http://') 71 | or 72 | redirect_to.startswith('https://')): 73 | redirect_to = get_url_host(request) + redirect_to 74 | 75 | if request.GET.get('next') and is_valid_next_url(request.GET['next']): 76 | if '?' in redirect_to: 77 | join = '&' 78 | else: 79 | join = '?' 80 | redirect_to += join + urllib.urlencode({ 81 | 'next': request.GET['next'] 82 | }) 83 | if not user_url: 84 | user_url = request.REQUEST.get('openid_url', None) 85 | 86 | if not user_url: 87 | request_path = request.path 88 | if request.GET.get('next'): 89 | request_path += '?' + urllib.urlencode({ 90 | 'next': request.GET['next'] 91 | }) 92 | 93 | return render(template_name, { 94 | 'action': request_path, 95 | }, RequestContext(request)) 96 | 97 | if xri.identifierScheme(user_url) == 'XRI' and getattr( 98 | settings, 'OPENID_DISALLOW_INAMES', False 99 | ): 100 | return on_failure(request, _('i-names are not supported')) 101 | 102 | consumer = Consumer(request.session, DjangoOpenIDStore()) 103 | 104 | try: 105 | auth_request = consumer.begin(user_url) 106 | except DiscoveryFailure: 107 | return on_failure(request, _('The OpenID was invalid')) 108 | 109 | sreg = getattr(settings, 'OPENID_SREG', False) 110 | 111 | if sreg: 112 | s = SRegRequest() 113 | for sarg in sreg: 114 | if sarg.lower().lstrip() == "policy_url": 115 | s.policy_url = sreg[sarg] 116 | else: 117 | for v in sreg[sarg].split(','): 118 | s.requestField(field_name=v.lower().lstrip(), 119 | required=(sarg.lower().lstrip() == 120 | "required")) 121 | auth_request.addExtension(s) 122 | 123 | pape = getattr(settings, 'OPENID_PAPE', False) 124 | 125 | if pape: 126 | if openid.__version__ <= '2.0.0' and openid.__version__ >= '2.1.0': 127 | raise (ImportError, 128 | 'For pape extension you need python-openid 2.1.0 or newer') 129 | p = PapeRequest() 130 | for parg in pape: 131 | if parg.lower().strip() == 'policy_list': 132 | for v in pape[parg].split(','): 133 | p.addPolicyURI(v) 134 | elif parg.lower().strip() == 'max_auth_age': 135 | p.max_auth_age = pape[parg] 136 | auth_request.addExtension(p) 137 | 138 | OPENID_AX_PROVIDER_MAP = getattr(settings, 'OPENID_AX_PROVIDER_MAP', {}) 139 | 140 | openid_provider = ('Google' if 141 | 'google' in request.session.get('openid_provider', '') 142 | else 'Default') 143 | ax = OPENID_AX_PROVIDER_MAP.get(openid_provider) 144 | 145 | if ax: 146 | axr = AXFetchRequest() 147 | for attr_name, attr_url in ax.items(): 148 | # axr.add(AttrInfo(i['type_uri'], 149 | # i['count'], i['required'], 150 | # i['alias'])) 151 | 152 | # setting all as required attrs 153 | axr.add(AttrInfo(attr_url, required=True)) 154 | auth_request.addExtension(axr) 155 | 156 | redirect_url = auth_request.redirectURL(trust_root, redirect_to) 157 | 158 | return HttpResponseRedirect(redirect_url) 159 | 160 | def complete(request, on_success=None, on_failure=None, 161 | failure_template='openid_consumer/failure.html'): 162 | 163 | on_success = on_success or default_on_success 164 | on_failure = on_failure or default_on_failure 165 | 166 | consumer = Consumer(request.session, DjangoOpenIDStore()) 167 | #dummydebug 168 | #for r in request.GET.items(): 169 | # print r 170 | 171 | # JanRain library raises a warning if passed unicode objects as the keys, 172 | # so we convert to bytestrings before passing to the library 173 | query_dict = dict([ 174 | (k.encode('utf8'), 175 | v.encode('utf8')) for k, v in request.REQUEST.items() 176 | ]) 177 | 178 | url = get_url_host(request) + request.path 179 | openid_response = consumer.complete(query_dict, url) 180 | if openid_response.status == SUCCESS: 181 | return on_success(request, 182 | openid_response.identity_url, 183 | openid_response) 184 | elif openid_response.status == CANCEL: 185 | return on_failure(request, 186 | _('The request was cancelled'), failure_template) 187 | elif openid_response.status == FAILURE: 188 | return on_failure(request, openid_response.message, failure_template) 189 | elif openid_response.status == SETUP_NEEDED: 190 | return on_failure(request, _('Setup needed'), failure_template) 191 | else: 192 | assert False, "Bad openid status: %s" % openid_response.status 193 | 194 | def default_on_success(request, identity_url, openid_response): 195 | if 'openids' not in request.session.keys(): 196 | request.session['openids'] = [] 197 | 198 | # Eliminate any duplicates 199 | request.session['openids'] = [ 200 | o for o in request.session['openids'] if o.openid != identity_url 201 | ] 202 | request.session['openids'].append(from_openid_response(openid_response)) 203 | 204 | # Set up request.openids and request.openid, reusing middleware logic 205 | OpenIDMiddleware().process_request(request) 206 | 207 | next = request.GET.get('next', '').strip() 208 | if not next or not is_valid_next_url(next): 209 | next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/') 210 | 211 | return HttpResponseRedirect(next) 212 | 213 | def default_on_failure(request, message, 214 | template_name='openid_consumer/failure.html'): 215 | return render(template_name, { 216 | 'message': message 217 | }, RequestContext(request)) 218 | 219 | def signout(request): 220 | request.session['openids'] = [] 221 | next = request.GET.get('next', '/') 222 | if not is_valid_next_url(next): 223 | next = '/' 224 | return HttpResponseRedirect(next) 225 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | python-openid>=2.0.0 2 | oauth>=1.0.0 3 | facebook-sdk 4 | hg+https://pylinkedin.googlecode.com/hg/pylinkedin>=0.1 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import ez_setup 2 | ez_setup.use_setuptools() 3 | from setuptools import setup, find_packages 4 | 5 | setup( 6 | name = "django-socialauth", 7 | version = "0.1.2c", 8 | packages = ['socialauth', 9 | 'socialauth/lib', 10 | 'socialauth/templatetags', 11 | 'openid_consumer'], 12 | package_data = { 'socialauth': [ 'templates/*.html', 13 | 'templates/socialauth/*.html', 14 | 'templates/socialauth/*.htm', 15 | 'templates/openid/*.html'], 16 | 'openid_consumer': ['locale/*/LC_MESSAGES/*.po'] 17 | }, 18 | zip_safe = False, 19 | author = "Agiliq Solutions", 20 | author_email = "info@uswaretech.com", 21 | description = "Allows logging via Facebook, Yahoo, Gmail, Twitter and Openid ", 22 | url = "http://agiliq.com/", 23 | ) 24 | -------------------------------------------------------------------------------- /socialauth/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/socialauth/__init__.py -------------------------------------------------------------------------------- /socialauth/admin.py: -------------------------------------------------------------------------------- 1 | from socialauth.models import AuthMeta, OpenidProfile, TwitterUserProfile, \ 2 | FacebookUserProfile, LinkedInUserProfile, GithubUserProfile, FoursquareUserProfile 3 | 4 | from django.contrib import admin 5 | 6 | admin.site.register(AuthMeta) 7 | admin.site.register(OpenidProfile) 8 | admin.site.register(TwitterUserProfile) 9 | admin.site.register(FacebookUserProfile) 10 | admin.site.register(LinkedInUserProfile) 11 | admin.site.register(GithubUserProfile) 12 | admin.site.register(FoursquareUserProfile) 13 | -------------------------------------------------------------------------------- /socialauth/auth_backends.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.contrib.sites.models import Site 3 | from django.core.urlresolvers import reverse 4 | from django.conf import settings 5 | import facebook 6 | 7 | import urllib 8 | from socialauth.lib import oauthtwitter2 as oauthtwitter 9 | from socialauth.models import OpenidProfile as UserAssociation, \ 10 | TwitterUserProfile, FacebookUserProfile, LinkedInUserProfile, AuthMeta, GithubUserProfile, FoursquareUserProfile 11 | from socialauth.lib.linkedin import * 12 | 13 | import random 14 | from cgi import parse_qs 15 | 16 | TWITTER_CONSUMER_KEY = getattr(settings, 'TWITTER_CONSUMER_KEY', '') 17 | TWITTER_CONSUMER_SECRET = getattr(settings, 'TWITTER_CONSUMER_SECRET', '') 18 | 19 | FACEBOOK_APP_ID = getattr(settings, 'FACEBOOK_APP_ID', '') 20 | FACEBOOK_API_KEY = getattr(settings, 'FACEBOOK_API_KEY', '') 21 | FACEBOOK_SECRET_KEY = getattr(settings, 'FACEBOOK_SECRET_KEY', '') 22 | 23 | # Linkedin 24 | LINKEDIN_CONSUMER_KEY = getattr(settings, 'LINKEDIN_CONSUMER_KEY', '') 25 | LINKEDIN_CONSUMER_SECRET = getattr(settings, 'LINKEDIN_CONSUMER_SECRET', '') 26 | 27 | # OpenId setting map 28 | 29 | OPENID_AX_PROVIDER_MAP = getattr(settings, 'OPENID_AX_PROVIDER_MAP', {}) 30 | 31 | class OpenIdBackend: 32 | supports_object_permissions = False 33 | supports_anonymous_user = False 34 | supports_inactive_user = False 35 | 36 | def authenticate(self, openid_key, request, provider, user=None): 37 | try: 38 | assoc = UserAssociation.objects.get(openid_key=openid_key) 39 | return assoc.user 40 | except UserAssociation.DoesNotExist: 41 | #fetch if openid provider provides any simple registration fields 42 | nickname = None 43 | email = None 44 | firstname = None 45 | lastname = None 46 | 47 | if request.openid and request.openid.sreg: 48 | email = request.openid.sreg.get('email') 49 | nickname = request.openid.sreg.get('nickname') 50 | firstname, lastname = (request.openid.sreg 51 | .get('fullname', ' ') 52 | .split(' ', 1)) 53 | elif request.openid and request.openid.ax: 54 | email = \ 55 | (request.openid.ax 56 | .getSingle('http://axschema.org/contact/email')) 57 | if 'google' in provider: 58 | ax_schema = OPENID_AX_PROVIDER_MAP['Google'] 59 | firstname = (request.openid.ax 60 | .getSingle(ax_schema['firstname'])) 61 | lastname = (request.openid.ax 62 | .getSingle(ax_schema['lastname'])) 63 | nickname = email.split('@')[0] 64 | else: 65 | ax_schema = OPENID_AX_PROVIDER_MAP['Default'] 66 | try: 67 | #should be replaced by correct schema 68 | nickname = (request.openid.ax 69 | .getSingle(ax_schema['nickname'])) 70 | (firstname, 71 | lastname) = (request.openid.ax 72 | .getSingle(ax_schema['fullname']) 73 | .split(' ', 1)) 74 | except: 75 | pass 76 | 77 | if nickname is None : 78 | nickname = ''.join( 79 | [random 80 | .choice('abcdefghijklmnopqrstuvwxyz') 81 | for i in xrange(10)]) 82 | 83 | name_count = (User.objects 84 | .filter(username__startswith=nickname) 85 | .count()) 86 | if name_count: 87 | username = '%s%d' % (nickname, name_count + 1) 88 | else: 89 | username = '%s' % (nickname) 90 | 91 | if email is None : 92 | valid_username = False 93 | email = "%s@socialauth" % (username) 94 | else: 95 | valid_username = True 96 | 97 | if not user: 98 | user = User.objects.create_user(username, email) 99 | 100 | user.first_name = firstname 101 | user.last_name = lastname 102 | user.save() 103 | 104 | #create openid association 105 | assoc = UserAssociation() 106 | assoc.openid_key = openid_key 107 | assoc.user = user 108 | if email: 109 | assoc.email = email 110 | if nickname: 111 | assoc.nickname = nickname 112 | if valid_username: 113 | assoc.is_username_valid = True 114 | assoc.save() 115 | 116 | #Create AuthMeta 117 | # auth_meta = 118 | # AuthMeta(user=user, 119 | # provider=provider, 120 | # provider_model='OpenidProfile', 121 | # provider_id=assoc.pk) 122 | auth_meta = AuthMeta(user=user, provider=provider) 123 | auth_meta.save() 124 | return user 125 | 126 | def GooglesAX(self,openid_response): 127 | email = (openid_response.ax 128 | .getSingle('http://axschema.org/contact/email')) 129 | firstname = (openid_response.ax 130 | .getSingle('http://axschema.org/namePerson/first')) 131 | lastname = (openid_response.ax 132 | .getSingle('http://axschema.org/namePerson/last')) 133 | # country = 134 | # (openid_response.ax 135 | # .getSingle('http://axschema.org/contact/country/home')) 136 | # language = 137 | # openid_response.ax.getSingle('http://axschema.org/pref/language') 138 | return locals() 139 | 140 | def get_user(self, user_id): 141 | try: 142 | user = User.objects.get(pk=user_id) 143 | return user 144 | except User.DoesNotExist: 145 | return None 146 | 147 | class LinkedInBackend: 148 | """LinkedInBackend for authentication""" 149 | def authenticate(self, linkedin_access_token, user=None): 150 | linkedin = LinkedIn(LINKEDIN_CONSUMER_KEY, LINKEDIN_CONSUMER_SECRET) 151 | # get their profile 152 | 153 | profile = (ProfileApi(linkedin) 154 | .getMyProfile(access_token=linkedin_access_token)) 155 | 156 | try: 157 | user_profile = (LinkedInUserProfile.objects 158 | .get(linkedin_uid=profile.id)) 159 | user = user_profile.user 160 | return user 161 | except LinkedInUserProfile.DoesNotExist: 162 | # Create a new user 163 | username = 'LI:%s' % profile.id 164 | 165 | if not user: 166 | user = User(username=username) 167 | user.first_name, user.last_name = (profile.firstname 168 | , profile.lastname) 169 | user.email = '%s@socialauth' % (username) 170 | user.save() 171 | 172 | userprofile = LinkedInUserProfile(user=user, 173 | linkedin_uid=profile.id) 174 | userprofile.save() 175 | 176 | auth_meta = AuthMeta(user=user, provider='LinkedIn').save() 177 | return user 178 | 179 | def get_user(self, user_id): 180 | try: 181 | return User.objects.get(pk=user_id) 182 | except: 183 | return None 184 | 185 | class TwitterBackend: 186 | """TwitterBackend for authentication""" 187 | def authenticate(self, twitter_access_token, user=None): 188 | ''' 189 | authenticates the token by requesting user information 190 | from twitter 191 | ''' 192 | # twitter = oauthtwitter.OAuthApi(TWITTER_CONSUMER_KEY, 193 | # TWITTER_CONSUMER_SECRET, 194 | # twitter_access_token) 195 | twitter = oauthtwitter.TwitterOAuthClient( 196 | settings.TWITTER_CONSUMER_KEY, 197 | settings.TWITTER_CONSUMER_SECRET) 198 | try: 199 | userinfo = twitter.get_user_info(twitter_access_token) 200 | except: 201 | # If we cannot get the user information, 202 | # user cannot be authenticated 203 | raise 204 | 205 | screen_name = userinfo.screen_name 206 | twitter_id = userinfo.id 207 | 208 | try: 209 | user_profile = (TwitterUserProfile.objects 210 | .get(screen_name=screen_name)) 211 | 212 | # Update Twitter Profile 213 | user_profile.url = userinfo.url 214 | user_profile.location = userinfo.location 215 | user_profile.description = userinfo.description 216 | user_profile.profile_image_url = userinfo.profile_image_url 217 | user_profile.save() 218 | 219 | user = user_profile.user 220 | return user 221 | except TwitterUserProfile.DoesNotExist: 222 | # Create new user 223 | if not user: 224 | same_name_count = (User.objects 225 | .filter(username__startswith=screen_name) 226 | .count()) 227 | if same_name_count: 228 | username = '%s%s' % (screen_name, same_name_count + 1) 229 | else: 230 | username = screen_name 231 | user = User(username=username) 232 | name_data = userinfo.name.split() 233 | try: 234 | first_name, last_name = (name_data[0], 235 | ' '.join(name_data[1:])) 236 | except: 237 | first_name, last_name = screen_name, '' 238 | user.first_name, user.last_name = first_name, last_name 239 | #user.email = screen_name + "@socialauth" 240 | #user.email = '%s@example.twitter.com'%(userinfo.screen_name) 241 | user.save() 242 | 243 | user_profile = TwitterUserProfile(user=user, 244 | screen_name=screen_name) 245 | user_profile.access_token = str(twitter_access_token) 246 | user_profile.url = userinfo.url 247 | user_profile.location = userinfo.location 248 | user_profile.description = userinfo.description 249 | user_profile.profile_image_url = userinfo.profile_image_url 250 | user_profile.save() 251 | 252 | auth_meta = AuthMeta(user=user, provider='Twitter').save() 253 | 254 | return user 255 | 256 | def get_user(self, user_id): 257 | try: 258 | return User.objects.get(pk=user_id) 259 | except: 260 | return None 261 | 262 | class FacebookBackend: 263 | def authenticate(self, request, user=None): 264 | cookie = facebook.get_user_from_cookie(request.COOKIES, 265 | FACEBOOK_APP_ID, 266 | FACEBOOK_SECRET_KEY) 267 | if cookie: 268 | uid = cookie['uid'] 269 | access_token = cookie['access_token'] 270 | else: 271 | # if cookie does not exist 272 | # assume logging in normal way 273 | params = {} 274 | params["client_id"] = FACEBOOK_APP_ID 275 | params["client_secret"] = FACEBOOK_SECRET_KEY 276 | params["redirect_uri"] = '%s://%s%s' % ( 277 | 'https' if request.is_secure() else 'http', 278 | Site.objects.get_current().domain, 279 | reverse("socialauth_facebook_login_done")) 280 | params["code"] = request.GET.get('code', '') 281 | 282 | url = ("https://graph.facebook.com/oauth/access_token?" 283 | + urllib.urlencode(params)) 284 | from cgi import parse_qs 285 | userdata = urllib.urlopen(url).read() 286 | res_parse_qs = parse_qs(userdata) 287 | # Could be a bot query 288 | if not res_parse_qs.has_key('access_token'): 289 | return None 290 | 291 | access_token = res_parse_qs['access_token'][-1] 292 | 293 | graph = facebook.GraphAPI(access_token) 294 | uid = graph.get_object('me')['id'] 295 | 296 | try: 297 | fb_user = FacebookUserProfile.objects.get(facebook_uid=uid) 298 | return fb_user.user 299 | 300 | except FacebookUserProfile.DoesNotExist: 301 | 302 | # create new FacebookUserProfile 303 | graph = facebook.GraphAPI(access_token) 304 | fb_data = graph.get_object("me") 305 | 306 | if not fb_data: 307 | return None 308 | 309 | username = uid 310 | if not user: 311 | user = User.objects.create(username=username) 312 | user.first_name = fb_data['first_name'] 313 | user.last_name = fb_data['last_name'] 314 | user.save() 315 | 316 | fb_profile = FacebookUserProfile(facebook_uid=uid, user=user) 317 | fb_profile.save() 318 | 319 | auth_meta = AuthMeta(user=user, provider='Facebook').save() 320 | 321 | return user 322 | 323 | 324 | def get_user(self, user_id): 325 | try: 326 | return User.objects.get(pk=user_id) 327 | except: 328 | return None 329 | 330 | class GithubBackend: 331 | def authenticate(self, github_access_token, user=None): 332 | response_qs = parse_qs(github_access_token) 333 | github_access_token = response_qs['access_token'][0] 334 | try: 335 | github_user = GithubUserProfile.objects.get(access_token=github_access_token) 336 | return github_user.user 337 | except GithubUserProfile.DoesNotExist: 338 | github_user_count = GithubUserProfile.objects.all().count() 339 | username = "GithubUser:" + str(github_user_count+1) 340 | user = User(username=username) 341 | user.save() 342 | github_user = GithubUserProfile(user=user, access_token=github_access_token) 343 | github_user.save() 344 | AuthMeta(user=user, provider='Github').save() 345 | return github_user.user 346 | 347 | def get_user(self, user_id): 348 | try: 349 | return User.objects.get(pk=user_id) 350 | except: 351 | return None 352 | 353 | class FoursquareBackend: 354 | def authenticate(self, foursquare_access_token): 355 | try: 356 | foursquare_profile = FoursquareUserProfile.objects.get(access_token=foursquare_access_token) 357 | return foursquare_profile.user 358 | except FoursquareUserProfile.DoesNotExist: 359 | foursquare_user_count = FoursquareUserProfile.objects.all().count() 360 | username = "FoursquareUser:" + str(foursquare_user_count+1) 361 | user = User(username=username) 362 | user.save() 363 | foursquare_user = FoursquareUserProfile(user=user, access_token=foursquare_access_token) 364 | foursquare_user.save() 365 | AuthMeta(user=user, provider='Foursquare').save() 366 | return foursquare_user.user 367 | 368 | def get_user(self, user_id): 369 | try: 370 | return User.objects.get(pk=user_id) 371 | except: 372 | return None 373 | -------------------------------------------------------------------------------- /socialauth/context_processors.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | 4 | def facebook_api_key(request): 5 | FACEBOOK_APP_ID = getattr(settings, 'FACEBOOK_APP_ID', '') 6 | FACEBOOK_API_KEY = getattr(settings, 'FACEBOOK_API_KEY', '') 7 | FACEBOOK_EXTENDED_PERMISSIONS = getattr(settings, 8 | 'FACEBOOK_EXTENDED_PERMISSIONS', 9 | '') 10 | 11 | if FACEBOOK_APP_ID: 12 | return {'FACEBOOK_APP_ID': FACEBOOK_APP_ID, 13 | 'FACEBOOK_API_KEY': FACEBOOK_API_KEY, 14 | 'login_button_perms': ','.join(FACEBOOK_EXTENDED_PERMISSIONS)} 15 | else: 16 | return {} 17 | -------------------------------------------------------------------------------- /socialauth/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.conf import settings 3 | 4 | from socialauth.models import AuthMeta 5 | 6 | 7 | ALLOW_MULTIPLE_USERNAME_EDITS = getattr(settings, 8 | 'ALLOW_MULTIPLE_USERNAME_EDITS', 9 | False) 10 | 11 | class EditProfileForm(forms.Form): 12 | email = forms.EmailField() 13 | password = forms.CharField(max_length=100, widget=forms.PasswordInput, 14 | required=False, 15 | help_text='If you give a password, you can \ 16 | login via a login form as well.') 17 | password2 = forms.CharField(max_length=100, widget=forms.PasswordInput, 18 | required=False, 19 | label='Repeat password') 20 | first_name = forms.CharField(max_length=100, required=False) 21 | last_name = forms.CharField(max_length=100, required=False) 22 | 23 | def __init__(self, user=None, *args, **kwargs): 24 | super(EditProfileForm, self).__init__(*args, **kwargs) 25 | self.user = user 26 | if self.user: 27 | self.initial = {'email': user.email, \ 28 | 'first_name':user.first_name, 29 | 'last_name':user.last_name} 30 | 31 | def clean(self): 32 | cleaned_data = self.cleaned_data 33 | if 'password' in cleaned_data or 'password2' in cleaned_data: 34 | if 'password' in cleaned_data and 'password2' in cleaned_data: 35 | if cleaned_data['password'] != cleaned_data['password2']: 36 | raise forms.ValidationError('The passwords do not match.') 37 | else: 38 | raise forms.ValidationError('Either ener both or None of the\ 39 | password fields') 40 | 41 | return cleaned_data 42 | 43 | def save(self): 44 | user = self.user 45 | user.email = self.cleaned_data['email'] 46 | user.first_name = self.cleaned_data['first_name'] 47 | user.last_name = self.cleaned_data['last_name'] 48 | if ('password' in self.cleaned_data 49 | and 50 | 'password2' in self.cleaned_data): 51 | user.set_password(self.cleaned_data['password']) 52 | user.save() 53 | try: 54 | authmeta = user.authmeta 55 | authmeta.is_email_filled = True 56 | authmeta.is_profile_modified = True 57 | authmeta.save() 58 | except AuthMeta.DoesNotExist: 59 | pass 60 | return user 61 | 62 | 63 | -------------------------------------------------------------------------------- /socialauth/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/socialauth/lib/__init__.py -------------------------------------------------------------------------------- /socialauth/lib/facebook.py: -------------------------------------------------------------------------------- 1 | import md5 2 | import urllib 3 | import time 4 | try: 5 | import json as simplejson 6 | except: 7 | from django.utils import simplejson 8 | 9 | REST_SERVER = 'http://api.facebook.com/restserver.php' 10 | 11 | 12 | def get_user_info(api_key, api_secret, cookies): 13 | user_info_params = { 14 | 'method': 'Users.getInfo', 15 | 'api_key': api_key, 16 | 'call_id': time.time(), 17 | 'v': '1.0', 18 | 'uids': cookies[api_key + '_user'], 19 | 'fields': 'uid,first_name,last_name,pic_small, name, current_location', 20 | 'format': 'json', 21 | } 22 | 23 | user_info_hash = get_facebook_signature(api_key, api_secret, user_info_params) 24 | user_info_params['sig'] = user_info_hash 25 | user_info_params = urllib.urlencode(user_info_params) 26 | user_info_response = simplejson.load(urllib.urlopen(REST_SERVER, user_info_params)) 27 | return user_info_response 28 | 29 | def get_friends(api_key, api_secret, cookies): 30 | params = { 31 | 'method': 'Friends.get', 32 | 'api_key': api_key, 33 | 'call_id': time.time(), 34 | 'v': '1.0', 35 | 'format': 'json', 36 | } 37 | return talk_to_fb(api_key, api_secret, params) 38 | 39 | def get_friends_via_fql(api_key, api_secret, cookies): 40 | query = 'SELECT name, uid, pic_small FROM user WHERE uid IN (SELECT uid2 FROM friend WHERE uid1 = %s)' % cookies[api_key + '_user'] 41 | params = { 42 | 'method': 'Fql.query', 43 | 'session_key': cookies[api_key + '_session_key'], 44 | 'query': query, 45 | 'api_key': api_key, 46 | 'call_id': time.time(), 47 | 'v': '1.0', 48 | 'uid': cookies[api_key + '_user'], 49 | 'format': 'json', 50 | } 51 | return talk_to_fb(api_key, api_secret, params) 52 | 53 | 54 | def talk_to_fb(api_key, api_secret, params): 55 | sig = get_facebook_signature(api_key, api_secret, params) 56 | params['sig'] = sig 57 | data = urllib.urlencode(params) 58 | response = simplejson.load(urllib.urlopen(REST_SERVER, data)) 59 | return response 60 | 61 | 62 | def get_facebook_signature(api_key, api_secret, values_dict, is_cookie_check=False): 63 | API_KEY = api_key 64 | API_SECRET = api_secret 65 | signature_keys = [] 66 | for key in sorted(values_dict.keys()): 67 | if (is_cookie_check and key.startswith(API_KEY + '_')): 68 | signature_keys.append(key) 69 | elif (is_cookie_check is False): 70 | signature_keys.append(key) 71 | 72 | if (is_cookie_check): 73 | signature_string = ''.join(['%s=%s' % (x.replace(API_KEY + '_',''), values_dict[x]) for x in signature_keys]) 74 | else: 75 | signature_string = ''.join(['%s=%s' % (x, values_dict[x]) for x in signature_keys]) 76 | signature_string = signature_string + API_SECRET 77 | 78 | return md5.new(signature_string).hexdigest() 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /socialauth/lib/foursquare.py: -------------------------------------------------------------------------------- 1 | from oauth import oauth 2 | from django.conf import settings 3 | import httplib 4 | 5 | FOURSQUARE_AUTHENTICATION_URL = 'https://foursquare.com/oauth2/authenticate' 6 | FOURSQUARE_ACCESS_TOKEN_URL = 'https://foursquare.com/oauth2/access_token' 7 | FOURSQUARE_CONSUMER_KEY = getattr(settings, 'FOURSQUARE_CONSUMER_KEY') 8 | FOURSQUARE_CONSUMER_SECRET = getattr(settings, 'FOURSQUARE_CONSUMER_SECRET') 9 | REGISTERED_REDIRECT_URI = getattr(settings, 'FOURSQUARE_REGISTERED_REDIRECT_URI') 10 | 11 | def get_http_connection(): 12 | return httplib.HTTPSConnection('foursquare.com') 13 | 14 | def get_response_body(oauth_request): 15 | http_conn = get_http_connection() 16 | http_conn.request('GET', oauth_request.to_url()) 17 | response = http_conn.getresponse().read() 18 | return response 19 | 20 | 21 | class FourSquareClient(object): 22 | def __init__(self): 23 | self.consumer = oauth.OAuthConsumer(FOURSQUARE_CONSUMER_KEY, FOURSQUARE_CONSUMER_SECRET) 24 | 25 | def get_authentication_url(self): 26 | parameters = {} 27 | parameters['client_id'] = FOURSQUARE_CONSUMER_KEY 28 | parameters['response_type'] = 'code' 29 | parameters['redirect_uri'] = REGISTERED_REDIRECT_URI 30 | oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=FOURSQUARE_AUTHENTICATION_URL, parameters=parameters) 31 | return oauth_request.to_url() 32 | 33 | def get_access_token(self, foursquare_code): 34 | parameters = {} 35 | parameters['client_id'] = FOURSQUARE_CONSUMER_KEY 36 | parameters['client_secret'] = FOURSQUARE_CONSUMER_SECRET 37 | parameters['grant_type'] = 'authorization_code' 38 | parameters['redirect_uri'] = REGISTERED_REDIRECT_URI 39 | parameters['code'] = foursquare_code 40 | oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=FOURSQUARE_ACCESS_TOKEN_URL, parameters=parameters) 41 | access_token_response = get_response_body(oauth_request) 42 | return access_token_response 43 | -------------------------------------------------------------------------------- /socialauth/lib/github.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from oauth.oauth import OAuthConsumer, OAuthRequest 3 | import httplib 4 | 5 | GITHUB_CLIENT_ID = getattr(settings, 'GITHUB_CLIENT_ID') 6 | GITHUB_CLIENT_SECRET = getattr(settings, 'GITHUB_CLIENT_SECRET') 7 | GITHUB_AUTHORIZE_URL = 'https://github.com/login/oauth/authorize' 8 | GITHUB_ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token' 9 | 10 | def get_response_from_url(to_url): 11 | conn = httplib.HTTPSConnection('github.com') 12 | conn.request('GET', to_url) 13 | return conn.getresponse().read() 14 | 15 | class GithubClient(object): 16 | def __init__(self): 17 | self.consumer = OAuthConsumer(GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET) 18 | 19 | def get_authorize_url(self): 20 | parameters = {'client_id':GITHUB_CLIENT_ID} 21 | oauth_request = OAuthRequest.from_consumer_and_token(self.consumer, http_url=GITHUB_AUTHORIZE_URL, parameters=parameters) 22 | return oauth_request.to_url() 23 | 24 | def get_access_token(self, code): 25 | parameters = {} 26 | parameters['client_id'] = GITHUB_CLIENT_ID 27 | parameters['client_secret'] = GITHUB_CLIENT_SECRET 28 | parameters['code'] = code 29 | oauth_request = OAuthRequest.from_consumer_and_token(self.consumer, http_url=GITHUB_ACCESS_TOKEN_URL, parameters=parameters) 30 | access_token = get_response_from_url(oauth_request.to_url()) 31 | return access_token 32 | -------------------------------------------------------------------------------- /socialauth/lib/linkedin.py: -------------------------------------------------------------------------------- 1 | """ 2 | LinkedIn OAuth Api Access 3 | Version: 0.01 4 | License: MIT 5 | Author: Max Lynch 6 | Website: http://mendotasoft.com, http://maxlynch.com 7 | Date Release: 11/23/2009 8 | 9 | Enjoy! 10 | """ 11 | 12 | import hashlib 13 | import urllib2 14 | import httplib 15 | 16 | import time 17 | 18 | from xml.dom.minidom import parseString 19 | 20 | import oauth.oauth as oauth 21 | 22 | class LinkedIn(): 23 | LI_SERVER = "api.linkedin.com" 24 | LI_API_URL = "https://api.linkedin.com" 25 | 26 | REQUEST_TOKEN_URL = LI_API_URL + "/uas/oauth/requestToken" 27 | AUTHORIZE_URL = LI_API_URL + "/uas/oauth/authorize" 28 | ACCESS_TOKEN_URL = LI_API_URL + "/uas/oauth/accessToken" 29 | 30 | 31 | 32 | def __init__(self, api_key, secret_key): 33 | self.api_key = api_key 34 | self.secret_key = secret_key 35 | 36 | self.connection = httplib.HTTPSConnection(self.LI_SERVER) 37 | self.consumer = oauth.OAuthConsumer(api_key, secret_key) 38 | self.sig_method = oauth.OAuthSignatureMethod_HMAC_SHA1() 39 | 40 | self.status_api = StatusApi(self) 41 | self.connections_api = ConnectionsApi(self) 42 | 43 | def getRequestToken(self, callback): 44 | """ 45 | Get a request token from linkedin 46 | """ 47 | oauth_consumer_key = self.api_key 48 | 49 | oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, 50 | callback=callback, 51 | http_url = self.REQUEST_TOKEN_URL) 52 | oauth_request.sign_request(self.sig_method, self.consumer, None) 53 | 54 | 55 | self.connection.request(oauth_request.http_method, 56 | self.REQUEST_TOKEN_URL, headers = oauth_request.to_header()) 57 | response = self.connection.getresponse().read() 58 | 59 | token = oauth.OAuthToken.from_string(response) 60 | return token 61 | 62 | def getAuthorizeUrl(self, token): 63 | """ 64 | Get the URL that we can redirect the user to for authorization of our 65 | application. 66 | """ 67 | oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, http_url = self.AUTHORIZE_URL) 68 | return oauth_request.to_url() 69 | 70 | def getAccessToken(self, token, verifier): 71 | """ 72 | Using the verifier we obtained through the user's authorization of 73 | our application, get an access code. 74 | 75 | Note: token is the request token returned from the call to getRequestToken 76 | 77 | @return an OAuthToken object with the access token. Use it like this: 78 | token.key -> Key 79 | token.secret -> Secret Key 80 | """ 81 | token.verifier = verifier 82 | oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, verifier=verifier, http_url=self.ACCESS_TOKEN_URL) 83 | oauth_request.sign_request(self.sig_method, self.consumer, token) 84 | 85 | # self.connection.request(oauth_request.http_method, self.ACCESS_TOKEN_URL, headers=oauth_request.to_header()) 86 | self.connection.request(oauth_request.http_method, oauth_request.to_url()) 87 | response = self.connection.getresponse().read() 88 | return oauth.OAuthToken.from_string(response) 89 | 90 | """ 91 | More functionality coming soon... 92 | """ 93 | 94 | class LinkedInApi(): 95 | def __init__(self, linkedin): 96 | self.linkedin = linkedin 97 | 98 | def doApiRequest(self, url, access_token): 99 | oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.linkedin.consumer, token=access_token, http_url=url) 100 | oauth_request.sign_request(self.linkedin.sig_method, self.linkedin.consumer, access_token) 101 | self.linkedin.connection.request(oauth_request.http_method, url, headers=oauth_request.to_header()) 102 | return self.linkedin.connection.getresponse().read() 103 | 104 | 105 | class StatusApi(LinkedInApi): 106 | STATUS_SELF_URL = LinkedIn.LI_API_URL + "/v1/people/~:(current-status)" 107 | 108 | def __init__(self, linkedin): 109 | LinkedInApi.__init__(self, linkedin) 110 | 111 | def getMyStatus(self, access_token): 112 | return self.doApiRequest(self.STATUS_SELF_URL, access_token) 113 | 114 | class ProfileApi(LinkedInApi): 115 | """ 116 | Get a LinkedIn Profile 117 | """ 118 | PROFILE_SELF = LinkedIn.LI_API_URL + r"/v1/people/~:(id,first-name,last-name,headline,industry,picture-url,site-standard-profile-request)" 119 | 120 | def __init__(self, linkedin): 121 | LinkedInApi.__init__(self, linkedin) 122 | 123 | def getMyProfile(self, access_token): 124 | xml = self.doApiRequest(self.PROFILE_SELF, access_token) 125 | dom = parseString(xml) 126 | personDom = dom.getElementsByTagName('person') 127 | 128 | p = personDom[0] 129 | 130 | fn=ln=picurl=headline=company=industry=profurl="" 131 | 132 | try: 133 | id = p.getElementsByTagName('id')[0].firstChild.nodeValue 134 | fn = p.getElementsByTagName('first-name')[0].firstChild.nodeValue 135 | ln = p.getElementsByTagName('last-name')[0].firstChild.nodeValue 136 | picurl = p.getElementsByTagName('picture-url')[0].firstChild.nodeValue 137 | 138 | headline = p.getElementsByTagName('headline')[0].firstChild.nodeValue 139 | if ' at ' in headline: 140 | company = headline.split(' at ')[1] 141 | industry = p.getElementsByTagName('industry')[0].firstChild.nodeValue 142 | #location = p.getElementsByTagName('industry')[0].firstChild.nodeValue 143 | profurl = p.getElementsByTagName('url')[0].firstChild.nodeValue 144 | except: 145 | pass 146 | 147 | person = Person() 148 | person.id = id 149 | person.firstname = fn 150 | person.lastname = ln 151 | person.headline = headline 152 | person.company = company 153 | person.industry = industry 154 | person.picture_url = picurl 155 | person.profile_url = profurl 156 | 157 | return person 158 | 159 | class ConnectionsApi(LinkedInApi): 160 | """ 161 | How to get all of a user's connections: 162 | 163 | Note: This should happen after the linkedin redirect. verifier is passed 164 | by LinkedIn back to your redirect page 165 | 166 | li = LinkedIn(LINKEDIN_CONSUMER_KEY, LINKEDIN_CONSUMER_SECRET) 167 | 168 | tokenObj = oauth.OAuthToken(requestTokenKey, requestTokenSecret) 169 | access_token = li.getAccessToken(tokenObj, verifier) 170 | 171 | connections = li.connections_api.getMyConnections(access_token) 172 | 173 | for c in connections: 174 | # Access c.firstname, c.lastname, etc. 175 | """ 176 | 177 | CONNECTIONS_SELF = LinkedIn.LI_API_URL + "/v1/people/~/connections" 178 | def __init__(self, linkedin): 179 | LinkedInApi.__init__(self, linkedin) 180 | 181 | def getMyConnections(self, access_token): 182 | xml = self.doApiRequest(self.CONNECTIONS_SELF, access_token) 183 | dom = parseString(xml) 184 | peopleDom = dom.getElementsByTagName('person') 185 | 186 | people = [] 187 | 188 | for p in peopleDom: 189 | try: 190 | fn = p.getElementsByTagName('first-name')[0].firstChild.nodeValue 191 | ln = p.getElementsByTagName('last-name')[0].firstChild.nodeValue 192 | headline = p.getElementsByTagName('headline')[0].firstChild.nodeValue 193 | company = headline.split(' at ')[1] 194 | industry = p.getElementsByTagName('industry')[0].firstChild.nodeValue 195 | #location = p.getElementsByTagName('industry')[0].firstChild.nodeValue 196 | person = Person() 197 | person.firstname = fn 198 | person.lastname = ln 199 | person.headline = headline 200 | person.company = company 201 | person.industry = industry 202 | people.append(person) 203 | except: 204 | continue 205 | return people 206 | 207 | class Person(): 208 | id = "" 209 | firstname = "" 210 | lastname = "" 211 | headline = "" 212 | company = "" 213 | location = None 214 | industry = "" 215 | picture_url = "" 216 | profile_url = "" 217 | 218 | def __str__(self): 219 | return "%s %s working at %s" % (self.firstname, self.lastname, self.company) 220 | 221 | class Location(): 222 | name = "" 223 | country = "" 224 | -------------------------------------------------------------------------------- /socialauth/lib/oauth1.py: -------------------------------------------------------------------------------- 1 | """ 2 | The MIT License 3 | 4 | Copyright (c) 2007 Leah Culver 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | """ 24 | 25 | import cgi 26 | import urllib 27 | import time 28 | import random 29 | import urlparse 30 | import hmac 31 | import binascii 32 | 33 | 34 | VERSION = '1.0' # Hi Blaine! 35 | HTTP_METHOD = 'GET' 36 | SIGNATURE_METHOD = 'PLAINTEXT' 37 | 38 | 39 | class OAuthError(RuntimeError): 40 | """Generic exception class.""" 41 | def __init__(self, message='OAuth error occured.'): 42 | self.message = message 43 | 44 | def build_authenticate_header(realm=''): 45 | """Optional WWW-Authenticate header (401 error)""" 46 | return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} 47 | 48 | def escape(s): 49 | """Escape a URL including any /.""" 50 | return urllib.quote(s, safe='~') 51 | 52 | def _utf8_str(s): 53 | """Convert unicode to utf-8.""" 54 | if isinstance(s, unicode): 55 | return s.encode("utf-8") 56 | else: 57 | return str(s) 58 | 59 | def generate_timestamp(): 60 | """Get seconds since epoch (UTC).""" 61 | return int(time.time()) 62 | 63 | def generate_nonce(length=8): 64 | """Generate pseudorandom number.""" 65 | return ''.join([str(random.randint(0, 9)) for i in range(length)]) 66 | 67 | def generate_verifier(length=8): 68 | """Generate pseudorandom number.""" 69 | return ''.join([str(random.randint(0, 9)) for i in range(length)]) 70 | 71 | 72 | class OAuthConsumer(object): 73 | """Consumer of OAuth authentication. 74 | 75 | OAuthConsumer is a data type that represents the identity of the Consumer 76 | via its shared secret with the Service Provider. 77 | 78 | """ 79 | key = None 80 | secret = None 81 | 82 | def __init__(self, key, secret): 83 | self.key = key 84 | self.secret = secret 85 | 86 | 87 | class OAuthToken(object): 88 | """OAuthToken is a data type that represents an End User via either an access 89 | or request token. 90 | 91 | key -- the token 92 | secret -- the token secret 93 | 94 | """ 95 | key = None 96 | secret = None 97 | callback = None 98 | callback_confirmed = None 99 | verifier = None 100 | 101 | def __init__(self, key, secret): 102 | self.key = key 103 | self.secret = secret 104 | 105 | def set_callback(self, callback): 106 | self.callback = callback 107 | self.callback_confirmed = 'true' 108 | 109 | def set_verifier(self, verifier=None): 110 | if verifier is not None: 111 | self.verifier = verifier 112 | else: 113 | self.verifier = generate_verifier() 114 | 115 | def get_callback_url(self): 116 | if self.callback and self.verifier: 117 | # Append the oauth_verifier. 118 | parts = urlparse.urlparse(self.callback) 119 | scheme, netloc, path, params, query, fragment = parts[:6] 120 | if query: 121 | query = '%s&oauth_verifier=%s' % (query, self.verifier) 122 | else: 123 | query = 'oauth_verifier=%s' % self.verifier 124 | return urlparse.urlunparse((scheme, netloc, path, params, 125 | query, fragment)) 126 | return self.callback 127 | 128 | def to_string(self): 129 | data = { 130 | 'oauth_token': self.key, 131 | 'oauth_token_secret': self.secret, 132 | } 133 | if self.callback_confirmed is not None: 134 | data['oauth_callback_confirmed'] = self.callback_confirmed 135 | return urllib.urlencode(data) 136 | 137 | def from_string(s): 138 | """ Returns a token from something like: 139 | oauth_token_secret=xxx&oauth_token=xxx 140 | """ 141 | params = cgi.parse_qs(s, keep_blank_values=False) 142 | key = params['oauth_token'][0] 143 | secret = params['oauth_token_secret'][0] 144 | token = OAuthToken(key, secret) 145 | try: 146 | token.callback_confirmed = params['oauth_callback_confirmed'][0] 147 | except KeyError: 148 | pass # 1.0, no callback confirmed. 149 | return token 150 | from_string = staticmethod(from_string) 151 | 152 | def __str__(self): 153 | return self.to_string() 154 | 155 | 156 | class OAuthRequest(object): 157 | """OAuthRequest represents the request and can be serialized. 158 | 159 | OAuth parameters: 160 | - oauth_consumer_key 161 | - oauth_token 162 | - oauth_signature_method 163 | - oauth_signature 164 | - oauth_timestamp 165 | - oauth_nonce 166 | - oauth_version 167 | - oauth_verifier 168 | ... any additional parameters, as defined by the Service Provider. 169 | """ 170 | parameters = None # OAuth parameters. 171 | http_method = HTTP_METHOD 172 | http_url = None 173 | version = VERSION 174 | 175 | def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None): 176 | self.http_method = http_method 177 | self.http_url = http_url 178 | self.parameters = parameters or {} 179 | 180 | def set_parameter(self, parameter, value): 181 | self.parameters[parameter] = value 182 | 183 | def get_parameter(self, parameter): 184 | try: 185 | return self.parameters[parameter] 186 | except: 187 | raise OAuthError('Parameter not found: %s' % parameter) 188 | 189 | def _get_timestamp_nonce(self): 190 | return self.get_parameter('oauth_timestamp'), self.get_parameter( 191 | 'oauth_nonce') 192 | 193 | def get_nonoauth_parameters(self): 194 | """Get any non-OAuth parameters.""" 195 | parameters = {} 196 | for k, v in self.parameters.iteritems(): 197 | # Ignore oauth parameters. 198 | if k.find('oauth_') < 0: 199 | parameters[k] = v 200 | return parameters 201 | 202 | def to_header(self, realm=''): 203 | """Serialize as a header for an HTTPAuth request.""" 204 | auth_header = 'OAuth realm="%s"' % realm 205 | # Add the oauth parameters. 206 | if self.parameters: 207 | for k, v in self.parameters.iteritems(): 208 | if k[:6] == 'oauth_': 209 | auth_header += ', %s="%s"' % (k, escape(str(v))) 210 | return {'Authorization': auth_header} 211 | 212 | def to_postdata(self): 213 | """Serialize as post data for a POST request.""" 214 | return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \ 215 | for k, v in self.parameters.iteritems()]) 216 | 217 | def to_url(self): 218 | """Serialize as a URL for a GET request.""" 219 | return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata()) 220 | 221 | def get_normalized_parameters(self): 222 | """Return a string that contains the parameters that must be signed.""" 223 | params = self.parameters 224 | try: 225 | # Exclude the signature if it exists. 226 | del params['oauth_signature'] 227 | except: 228 | pass 229 | # Escape key values before sorting. 230 | key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \ 231 | for k,v in params.items()] 232 | # Sort lexicographically, first after key, then after value. 233 | key_values.sort() 234 | # Combine key value pairs into a string. 235 | return '&'.join(['%s=%s' % (k, v) for k, v in key_values]) 236 | 237 | def get_normalized_http_method(self): 238 | """Uppercases the http method.""" 239 | return self.http_method.upper() 240 | 241 | def get_normalized_http_url(self): 242 | """Parses the URL and rebuilds it to be scheme://host/path.""" 243 | parts = urlparse.urlparse(self.http_url) 244 | scheme, netloc, path = parts[:3] 245 | # Exclude default port numbers. 246 | if scheme == 'http' and netloc[-3:] == ':80': 247 | netloc = netloc[:-3] 248 | elif scheme == 'https' and netloc[-4:] == ':443': 249 | netloc = netloc[:-4] 250 | return '%s://%s%s' % (scheme, netloc, path) 251 | 252 | def sign_request(self, signature_method, consumer, token): 253 | """Set the signature parameter to the result of build_signature.""" 254 | # Set the signature method. 255 | self.set_parameter('oauth_signature_method', 256 | signature_method.get_name()) 257 | # Set the signature. 258 | self.set_parameter('oauth_signature', 259 | self.build_signature(signature_method, consumer, token)) 260 | 261 | def build_signature(self, signature_method, consumer, token): 262 | """Calls the build signature method within the signature method.""" 263 | return signature_method.build_signature(self, consumer, token) 264 | 265 | def from_request(http_method, http_url, headers=None, parameters=None, 266 | query_string=None): 267 | """Combines multiple parameter sources.""" 268 | if parameters is None: 269 | parameters = {} 270 | 271 | # Headers 272 | if headers and 'Authorization' in headers: 273 | auth_header = headers['Authorization'] 274 | # Check that the authorization header is OAuth. 275 | if auth_header[:6] == 'OAuth ': 276 | auth_header = auth_header[6:] 277 | try: 278 | # Get the parameters from the header. 279 | header_params = OAuthRequest._split_header(auth_header) 280 | parameters.update(header_params) 281 | except: 282 | raise OAuthError('Unable to parse OAuth parameters from ' 283 | 'Authorization header.') 284 | 285 | # GET or POST query string. 286 | if query_string: 287 | query_params = OAuthRequest._split_url_string(query_string) 288 | parameters.update(query_params) 289 | 290 | # URL parameters. 291 | param_str = urlparse.urlparse(http_url)[4] # query 292 | url_params = OAuthRequest._split_url_string(param_str) 293 | parameters.update(url_params) 294 | 295 | if parameters: 296 | return OAuthRequest(http_method, http_url, parameters) 297 | 298 | return None 299 | from_request = staticmethod(from_request) 300 | 301 | def from_consumer_and_token(oauth_consumer, token=None, 302 | callback=None, verifier=None, http_method=HTTP_METHOD, 303 | http_url=None, parameters=None): 304 | if not parameters: 305 | parameters = {} 306 | 307 | defaults = { 308 | 'oauth_consumer_key': oauth_consumer.key, 309 | 'oauth_timestamp': generate_timestamp(), 310 | 'oauth_nonce': generate_nonce(), 311 | 'oauth_version': OAuthRequest.version, 312 | } 313 | 314 | defaults.update(parameters) 315 | parameters = defaults 316 | 317 | if token: 318 | parameters['oauth_token'] = token.key 319 | if token.callback: 320 | parameters['oauth_callback'] = token.callback 321 | # 1.0a support for verifier. 322 | if verifier: 323 | parameters['oauth_verifier'] = verifier 324 | elif callback: 325 | # 1.0a support for callback in the request token request. 326 | parameters['oauth_callback'] = callback 327 | 328 | return OAuthRequest(http_method, http_url, parameters) 329 | from_consumer_and_token = staticmethod(from_consumer_and_token) 330 | 331 | def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD, 332 | http_url=None, parameters=None): 333 | if not parameters: 334 | parameters = {} 335 | 336 | parameters['oauth_token'] = token.key 337 | 338 | if callback: 339 | parameters['oauth_callback'] = callback 340 | 341 | return OAuthRequest(http_method, http_url, parameters) 342 | from_token_and_callback = staticmethod(from_token_and_callback) 343 | 344 | def _split_header(header): 345 | """Turn Authorization: header into parameters.""" 346 | params = {} 347 | parts = header.split(',') 348 | for param in parts: 349 | # Ignore realm parameter. 350 | if param.find('realm') > -1: 351 | continue 352 | # Remove whitespace. 353 | param = param.strip() 354 | # Split key-value. 355 | param_parts = param.split('=', 1) 356 | # Remove quotes and unescape the value. 357 | params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) 358 | return params 359 | _split_header = staticmethod(_split_header) 360 | 361 | def _split_url_string(param_str): 362 | """Turn URL string into parameters.""" 363 | parameters = cgi.parse_qs(param_str, keep_blank_values=False) 364 | for k, v in parameters.iteritems(): 365 | parameters[k] = urllib.unquote(v[0]) 366 | return parameters 367 | _split_url_string = staticmethod(_split_url_string) 368 | 369 | class OAuthServer(object): 370 | """A worker to check the validity of a request against a data store.""" 371 | timestamp_threshold = 300 # In seconds, five minutes. 372 | version = VERSION 373 | signature_methods = None 374 | data_store = None 375 | 376 | def __init__(self, data_store=None, signature_methods=None): 377 | self.data_store = data_store 378 | self.signature_methods = signature_methods or {} 379 | 380 | def set_data_store(self, data_store): 381 | self.data_store = data_store 382 | 383 | def get_data_store(self): 384 | return self.data_store 385 | 386 | def add_signature_method(self, signature_method): 387 | self.signature_methods[signature_method.get_name()] = signature_method 388 | return self.signature_methods 389 | 390 | def fetch_request_token(self, oauth_request): 391 | """Processes a request_token request and returns the 392 | request token on success. 393 | """ 394 | try: 395 | # Get the request token for authorization. 396 | token = self._get_token(oauth_request, 'request') 397 | except OAuthError: 398 | # No token required for the initial token request. 399 | version = self._get_version(oauth_request) 400 | consumer = self._get_consumer(oauth_request) 401 | try: 402 | callback = self.get_callback(oauth_request) 403 | except OAuthError: 404 | callback = None # 1.0, no callback specified. 405 | self._check_signature(oauth_request, consumer, None) 406 | # Fetch a new token. 407 | token = self.data_store.fetch_request_token(consumer, callback) 408 | return token 409 | 410 | def fetch_access_token(self, oauth_request): 411 | """Processes an access_token request and returns the 412 | access token on success. 413 | """ 414 | version = self._get_version(oauth_request) 415 | consumer = self._get_consumer(oauth_request) 416 | try: 417 | verifier = self._get_verifier(oauth_request) 418 | except OAuthError: 419 | verifier = None 420 | # Get the request token. 421 | token = self._get_token(oauth_request, 'request') 422 | self._check_signature(oauth_request, consumer, token) 423 | new_token = self.data_store.fetch_access_token(consumer, token, verifier) 424 | return new_token 425 | 426 | def verify_request(self, oauth_request): 427 | """Verifies an api call and checks all the parameters.""" 428 | # -> consumer and token 429 | version = self._get_version(oauth_request) 430 | consumer = self._get_consumer(oauth_request) 431 | # Get the access token. 432 | token = self._get_token(oauth_request, 'access') 433 | self._check_signature(oauth_request, consumer, token) 434 | parameters = oauth_request.get_nonoauth_parameters() 435 | return consumer, token, parameters 436 | 437 | def authorize_token(self, token, user): 438 | """Authorize a request token.""" 439 | return self.data_store.authorize_request_token(token, user) 440 | 441 | def get_callback(self, oauth_request): 442 | """Get the callback URL.""" 443 | return oauth_request.get_parameter('oauth_callback') 444 | 445 | def build_authenticate_header(self, realm=''): 446 | """Optional support for the authenticate header.""" 447 | return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} 448 | 449 | def _get_version(self, oauth_request): 450 | """Verify the correct version request for this server.""" 451 | try: 452 | version = oauth_request.get_parameter('oauth_version') 453 | except: 454 | version = VERSION 455 | if version and version != self.version: 456 | raise OAuthError('OAuth version %s not supported.' % str(version)) 457 | return version 458 | 459 | def _get_signature_method(self, oauth_request): 460 | """Figure out the signature with some defaults.""" 461 | try: 462 | signature_method = oauth_request.get_parameter( 463 | 'oauth_signature_method') 464 | except: 465 | signature_method = SIGNATURE_METHOD 466 | try: 467 | # Get the signature method object. 468 | signature_method = self.signature_methods[signature_method] 469 | except: 470 | signature_method_names = ', '.join(self.signature_methods.keys()) 471 | raise OAuthError('Signature method %s not supported try one of the ' 472 | 'following: %s' % (signature_method, signature_method_names)) 473 | 474 | return signature_method 475 | 476 | def _get_consumer(self, oauth_request): 477 | consumer_key = oauth_request.get_parameter('oauth_consumer_key') 478 | consumer = self.data_store.lookup_consumer(consumer_key) 479 | if not consumer: 480 | raise OAuthError('Invalid consumer.') 481 | return consumer 482 | 483 | def _get_token(self, oauth_request, token_type='access'): 484 | """Try to find the token for the provided request token key.""" 485 | token_field = oauth_request.get_parameter('oauth_token') 486 | token = self.data_store.lookup_token(token_type, token_field) 487 | if not token: 488 | raise OAuthError('Invalid %s token: %s' % (token_type, token_field)) 489 | return token 490 | 491 | def _get_verifier(self, oauth_request): 492 | return oauth_request.get_parameter('oauth_verifier') 493 | 494 | def _check_signature(self, oauth_request, consumer, token): 495 | timestamp, nonce = oauth_request._get_timestamp_nonce() 496 | self._check_timestamp(timestamp) 497 | self._check_nonce(consumer, token, nonce) 498 | signature_method = self._get_signature_method(oauth_request) 499 | try: 500 | signature = oauth_request.get_parameter('oauth_signature') 501 | except: 502 | raise OAuthError('Missing signature.') 503 | # Validate the signature. 504 | valid_sig = signature_method.check_signature(oauth_request, consumer, 505 | token, signature) 506 | if not valid_sig: 507 | key, base = signature_method.build_signature_base_string( 508 | oauth_request, consumer, token) 509 | raise OAuthError('Invalid signature. Expected signature base ' 510 | 'string: %s' % base) 511 | built = signature_method.build_signature(oauth_request, consumer, token) 512 | 513 | def _check_timestamp(self, timestamp): 514 | """Verify that timestamp is recentish.""" 515 | timestamp = int(timestamp) 516 | now = int(time.time()) 517 | lapsed = abs(now - timestamp) 518 | if lapsed > self.timestamp_threshold: 519 | raise OAuthError('Expired timestamp: given %d and now %s has a ' 520 | 'greater difference than threshold %d' % 521 | (timestamp, now, self.timestamp_threshold)) 522 | 523 | def _check_nonce(self, consumer, token, nonce): 524 | """Verify that the nonce is uniqueish.""" 525 | nonce = self.data_store.lookup_nonce(consumer, token, nonce) 526 | if nonce: 527 | raise OAuthError('Nonce already used: %s' % str(nonce)) 528 | 529 | 530 | class OAuthClient(object): 531 | """OAuthClient is a worker to attempt to execute a request.""" 532 | consumer = None 533 | token = None 534 | 535 | def __init__(self, oauth_consumer, oauth_token): 536 | self.consumer = oauth_consumer 537 | self.token = oauth_token 538 | 539 | def get_consumer(self): 540 | return self.consumer 541 | 542 | def get_token(self): 543 | return self.token 544 | 545 | def fetch_request_token(self, oauth_request): 546 | """-> OAuthToken.""" 547 | raise NotImplementedError 548 | 549 | def fetch_access_token(self, oauth_request): 550 | """-> OAuthToken.""" 551 | raise NotImplementedError 552 | 553 | def access_resource(self, oauth_request): 554 | """-> Some protected resource.""" 555 | raise NotImplementedError 556 | 557 | 558 | class OAuthDataStore(object): 559 | """A database abstraction used to lookup consumers and tokens.""" 560 | 561 | def lookup_consumer(self, key): 562 | """-> OAuthConsumer.""" 563 | raise NotImplementedError 564 | 565 | def lookup_token(self, oauth_consumer, token_type, token_token): 566 | """-> OAuthToken.""" 567 | raise NotImplementedError 568 | 569 | def lookup_nonce(self, oauth_consumer, oauth_token, nonce): 570 | """-> OAuthToken.""" 571 | raise NotImplementedError 572 | 573 | def fetch_request_token(self, oauth_consumer, oauth_callback): 574 | """-> OAuthToken.""" 575 | raise NotImplementedError 576 | 577 | def fetch_access_token(self, oauth_consumer, oauth_token, oauth_verifier): 578 | """-> OAuthToken.""" 579 | raise NotImplementedError 580 | 581 | def authorize_request_token(self, oauth_token, user): 582 | """-> OAuthToken.""" 583 | raise NotImplementedError 584 | 585 | 586 | class OAuthSignatureMethod(object): 587 | """A strategy class that implements a signature method.""" 588 | def get_name(self): 589 | """-> str.""" 590 | raise NotImplementedError 591 | 592 | def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token): 593 | """-> str key, str raw.""" 594 | raise NotImplementedError 595 | 596 | def build_signature(self, oauth_request, oauth_consumer, oauth_token): 597 | """-> str.""" 598 | raise NotImplementedError 599 | 600 | def check_signature(self, oauth_request, consumer, token, signature): 601 | built = self.build_signature(oauth_request, consumer, token) 602 | return built == signature 603 | 604 | 605 | class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod): 606 | 607 | def get_name(self): 608 | return 'HMAC-SHA1' 609 | 610 | def build_signature_base_string(self, oauth_request, consumer, token): 611 | sig = ( 612 | escape(oauth_request.get_normalized_http_method()), 613 | escape(oauth_request.get_normalized_http_url()), 614 | escape(oauth_request.get_normalized_parameters()), 615 | ) 616 | 617 | key = '%s&' % escape(consumer.secret) 618 | if token: 619 | key += escape(token.secret) 620 | raw = '&'.join(sig) 621 | return key, raw 622 | 623 | def build_signature(self, oauth_request, consumer, token): 624 | """Builds the base signature string.""" 625 | key, raw = self.build_signature_base_string(oauth_request, consumer, 626 | token) 627 | 628 | # HMAC object. 629 | try: 630 | import hashlib # 2.5 631 | hashed = hmac.new(key, raw, hashlib.sha1) 632 | except: 633 | import sha # Deprecated 634 | hashed = hmac.new(key, raw, sha) 635 | 636 | # Calculate the digest base 64. 637 | return binascii.b2a_base64(hashed.digest())[:-1] 638 | 639 | 640 | class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod): 641 | 642 | def get_name(self): 643 | return 'PLAINTEXT' 644 | 645 | def build_signature_base_string(self, oauth_request, consumer, token): 646 | """Concatenates the consumer key and secret.""" 647 | sig = '%s&' % escape(consumer.secret) 648 | if token: 649 | sig = sig + escape(token.secret) 650 | return sig, sig 651 | 652 | def build_signature(self, oauth_request, consumer, token): 653 | key, raw = self.build_signature_base_string(oauth_request, consumer, 654 | token) 655 | return key -------------------------------------------------------------------------------- /socialauth/lib/oauth2.py: -------------------------------------------------------------------------------- 1 | """ 2 | The MIT License 3 | 4 | Copyright (c) 2007 Leah Culver 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | """ 24 | 25 | import cgi 26 | import urllib 27 | import time 28 | import random 29 | import urlparse 30 | import hmac 31 | import binascii 32 | 33 | 34 | VERSION = '1.0' # Hi Blaine! 35 | HTTP_METHOD = 'GET' 36 | SIGNATURE_METHOD = 'PLAINTEXT' 37 | 38 | 39 | class OAuthError(RuntimeError): 40 | """Generic exception class.""" 41 | def __init__(self, message='OAuth error occured.'): 42 | self.message = message 43 | 44 | def build_authenticate_header(realm=''): 45 | """Optional WWW-Authenticate header (401 error)""" 46 | return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} 47 | 48 | def escape(s): 49 | """Escape a URL including any /.""" 50 | return urllib.quote(s, safe='~') 51 | 52 | def _utf8_str(s): 53 | """Convert unicode to utf-8.""" 54 | if isinstance(s, unicode): 55 | return s.encode("utf-8") 56 | else: 57 | return str(s) 58 | 59 | def generate_timestamp(): 60 | """Get seconds since epoch (UTC).""" 61 | return int(time.time()) 62 | 63 | def generate_nonce(length=8): 64 | """Generate pseudorandom number.""" 65 | return ''.join([str(random.randint(0, 9)) for i in range(length)]) 66 | 67 | 68 | class OAuthConsumer(object): 69 | """Consumer of OAuth authentication. 70 | 71 | OAuthConsumer is a data type that represents the identity of the Consumer 72 | via its shared secret with the Service Provider. 73 | 74 | """ 75 | key = None 76 | secret = None 77 | 78 | def __init__(self, key, secret): 79 | self.key = key 80 | self.secret = secret 81 | 82 | 83 | class OAuthToken(object): 84 | """OAuthToken is a data type that represents an End User via either an access 85 | or request token. 86 | 87 | key -- the token 88 | secret -- the token secret 89 | 90 | """ 91 | key = None 92 | secret = None 93 | 94 | def __init__(self, key, secret): 95 | self.key = key 96 | self.secret = secret 97 | 98 | def to_string(self): 99 | return urllib.urlencode({'oauth_token': self.key, 100 | 'oauth_token_secret': self.secret}) 101 | 102 | def from_string(s): 103 | """ Returns a token from something like: 104 | oauth_token_secret=xxx&oauth_token=xxx 105 | """ 106 | params = cgi.parse_qs(s, keep_blank_values=False) 107 | key = params['oauth_token'][0] 108 | secret = params['oauth_token_secret'][0] 109 | return OAuthToken(key, secret) 110 | from_string = staticmethod(from_string) 111 | 112 | def __str__(self): 113 | return self.to_string() 114 | 115 | 116 | class OAuthRequest(object): 117 | """OAuthRequest represents the request and can be serialized. 118 | 119 | OAuth parameters: 120 | - oauth_consumer_key 121 | - oauth_token 122 | - oauth_signature_method 123 | - oauth_signature 124 | - oauth_timestamp 125 | - oauth_nonce 126 | - oauth_version 127 | ... any additional parameters, as defined by the Service Provider. 128 | """ 129 | parameters = None # OAuth parameters. 130 | http_method = HTTP_METHOD 131 | http_url = None 132 | version = VERSION 133 | 134 | def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None): 135 | self.http_method = http_method 136 | self.http_url = http_url 137 | self.parameters = parameters or {} 138 | 139 | def set_parameter(self, parameter, value): 140 | self.parameters[parameter] = value 141 | 142 | def get_parameter(self, parameter): 143 | try: 144 | return self.parameters[parameter] 145 | except: 146 | raise OAuthError('Parameter not found: %s' % parameter) 147 | 148 | def _get_timestamp_nonce(self): 149 | return self.get_parameter('oauth_timestamp'), self.get_parameter( 150 | 'oauth_nonce') 151 | 152 | def get_nonoauth_parameters(self): 153 | """Get any non-OAuth parameters.""" 154 | parameters = {} 155 | for k, v in self.parameters.iteritems(): 156 | # Ignore oauth parameters. 157 | if k.find('oauth_') < 0: 158 | parameters[k] = v 159 | return parameters 160 | 161 | def to_header(self, realm=''): 162 | """Serialize as a header for an HTTPAuth request.""" 163 | auth_header = 'OAuth realm="%s"' % realm 164 | # Add the oauth parameters. 165 | if self.parameters: 166 | for k, v in self.parameters.iteritems(): 167 | if k[:6] == 'oauth_': 168 | auth_header += ', %s="%s"' % (k, escape(str(v))) 169 | return {'Authorization': auth_header} 170 | 171 | def to_postdata(self): 172 | """Serialize as post data for a POST request.""" 173 | return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \ 174 | for k, v in self.parameters.iteritems()]) 175 | 176 | def to_url(self): 177 | """Serialize as a URL for a GET request.""" 178 | return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata()) 179 | 180 | def get_normalized_parameters(self): 181 | """Return a string that contains the parameters that must be signed.""" 182 | params = self.parameters 183 | try: 184 | # Exclude the signature if it exists. 185 | del params['oauth_signature'] 186 | except: 187 | pass 188 | # Escape key values before sorting. 189 | key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \ 190 | for k,v in params.items()] 191 | # Sort lexicographically, first after key, then after value. 192 | key_values.sort() 193 | # Combine key value pairs into a string. 194 | return '&'.join(['%s=%s' % (k, v) for k, v in key_values]) 195 | 196 | def get_normalized_http_method(self): 197 | """Uppercases the http method.""" 198 | return self.http_method.upper() 199 | 200 | def get_normalized_http_url(self): 201 | """Parses the URL and rebuilds it to be scheme://host/path.""" 202 | parts = urlparse.urlparse(self.http_url) 203 | scheme, netloc, path = parts[:3] 204 | # Exclude default port numbers. 205 | if scheme == 'http' and netloc[-3:] == ':80': 206 | netloc = netloc[:-3] 207 | elif scheme == 'https' and netloc[-4:] == ':443': 208 | netloc = netloc[:-4] 209 | return '%s://%s%s' % (scheme, netloc, path) 210 | 211 | def sign_request(self, signature_method, consumer, token): 212 | """Set the signature parameter to the result of build_signature.""" 213 | # Set the signature method. 214 | self.set_parameter('oauth_signature_method', 215 | signature_method.get_name()) 216 | # Set the signature. 217 | self.set_parameter('oauth_signature', 218 | self.build_signature(signature_method, consumer, token)) 219 | 220 | def build_signature(self, signature_method, consumer, token): 221 | """Calls the build signature method within the signature method.""" 222 | return signature_method.build_signature(self, consumer, token) 223 | 224 | def from_request(http_method, http_url, headers=None, parameters=None, 225 | query_string=None): 226 | """Combines multiple parameter sources.""" 227 | if parameters is None: 228 | parameters = {} 229 | 230 | # Headers 231 | if headers and 'Authorization' in headers: 232 | auth_header = headers['Authorization'] 233 | # Check that the authorization header is OAuth. 234 | if auth_header.index('OAuth') > -1: 235 | auth_header = auth_header.lstrip('OAuth ') 236 | try: 237 | # Get the parameters from the header. 238 | header_params = OAuthRequest._split_header(auth_header) 239 | parameters.update(header_params) 240 | except: 241 | raise OAuthError('Unable to parse OAuth parameters from ' 242 | 'Authorization header.') 243 | 244 | # GET or POST query string. 245 | if query_string: 246 | query_params = OAuthRequest._split_url_string(query_string) 247 | parameters.update(query_params) 248 | 249 | # URL parameters. 250 | param_str = urlparse.urlparse(http_url)[4] # query 251 | url_params = OAuthRequest._split_url_string(param_str) 252 | parameters.update(url_params) 253 | 254 | if parameters: 255 | return OAuthRequest(http_method, http_url, parameters) 256 | 257 | return None 258 | from_request = staticmethod(from_request) 259 | 260 | def from_consumer_and_token(oauth_consumer, token=None, 261 | http_method=HTTP_METHOD, http_url=None, parameters=None): 262 | if not parameters: 263 | parameters = {} 264 | 265 | defaults = { 266 | 'oauth_consumer_key': oauth_consumer.key, 267 | 'oauth_timestamp': generate_timestamp(), 268 | 'oauth_nonce': generate_nonce(), 269 | 'oauth_version': OAuthRequest.version, 270 | } 271 | 272 | defaults.update(parameters) 273 | parameters = defaults 274 | 275 | if token: 276 | parameters['oauth_token'] = token.key 277 | 278 | return OAuthRequest(http_method, http_url, parameters) 279 | from_consumer_and_token = staticmethod(from_consumer_and_token) 280 | 281 | def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD, 282 | http_url=None, parameters=None): 283 | if not parameters: 284 | parameters = {} 285 | 286 | parameters['oauth_token'] = token.key 287 | 288 | if callback: 289 | parameters['oauth_callback'] = callback 290 | 291 | return OAuthRequest(http_method, http_url, parameters) 292 | from_token_and_callback = staticmethod(from_token_and_callback) 293 | 294 | def _split_header(header): 295 | """Turn Authorization: header into parameters.""" 296 | params = {} 297 | parts = header.split(',') 298 | for param in parts: 299 | # Ignore realm parameter. 300 | if param.find('realm') > -1: 301 | continue 302 | # Remove whitespace. 303 | param = param.strip() 304 | # Split key-value. 305 | param_parts = param.split('=', 1) 306 | # Remove quotes and unescape the value. 307 | params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) 308 | return params 309 | _split_header = staticmethod(_split_header) 310 | 311 | def _split_url_string(param_str): 312 | """Turn URL string into parameters.""" 313 | parameters = cgi.parse_qs(param_str, keep_blank_values=False) 314 | for k, v in parameters.iteritems(): 315 | parameters[k] = urllib.unquote(v[0]) 316 | return parameters 317 | _split_url_string = staticmethod(_split_url_string) 318 | 319 | class OAuthServer(object): 320 | """A worker to check the validity of a request against a data store.""" 321 | timestamp_threshold = 300 # In seconds, five minutes. 322 | version = VERSION 323 | signature_methods = None 324 | data_store = None 325 | 326 | def __init__(self, data_store=None, signature_methods=None): 327 | self.data_store = data_store 328 | self.signature_methods = signature_methods or {} 329 | 330 | def set_data_store(self, data_store): 331 | self.data_store = data_store 332 | 333 | def get_data_store(self): 334 | return self.data_store 335 | 336 | def add_signature_method(self, signature_method): 337 | self.signature_methods[signature_method.get_name()] = signature_method 338 | return self.signature_methods 339 | 340 | def fetch_request_token(self, oauth_request): 341 | """Processes a request_token request and returns the 342 | request token on success. 343 | """ 344 | try: 345 | # Get the request token for authorization. 346 | token = self._get_token(oauth_request, 'request') 347 | except OAuthError: 348 | # No token required for the initial token request. 349 | version = self._get_version(oauth_request) 350 | consumer = self._get_consumer(oauth_request) 351 | self._check_signature(oauth_request, consumer, None) 352 | # Fetch a new token. 353 | token = self.data_store.fetch_request_token(consumer) 354 | return token 355 | 356 | def fetch_access_token(self, oauth_request): 357 | """Processes an access_token request and returns the 358 | access token on success. 359 | """ 360 | version = self._get_version(oauth_request) 361 | consumer = self._get_consumer(oauth_request) 362 | # Get the request token. 363 | token = self._get_token(oauth_request, 'request') 364 | self._check_signature(oauth_request, consumer, token) 365 | new_token = self.data_store.fetch_access_token(consumer, token) 366 | return new_token 367 | 368 | def verify_request(self, oauth_request): 369 | """Verifies an api call and checks all the parameters.""" 370 | # -> consumer and token 371 | version = self._get_version(oauth_request) 372 | consumer = self._get_consumer(oauth_request) 373 | # Get the access token. 374 | token = self._get_token(oauth_request, 'access') 375 | self._check_signature(oauth_request, consumer, token) 376 | parameters = oauth_request.get_nonoauth_parameters() 377 | return consumer, token, parameters 378 | 379 | def authorize_token(self, token, user): 380 | """Authorize a request token.""" 381 | return self.data_store.authorize_request_token(token, user) 382 | 383 | def get_callback(self, oauth_request): 384 | """Get the callback URL.""" 385 | return oauth_request.get_parameter('oauth_callback') 386 | 387 | def build_authenticate_header(self, realm=''): 388 | """Optional support for the authenticate header.""" 389 | return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} 390 | 391 | def _get_version(self, oauth_request): 392 | """Verify the correct version request for this server.""" 393 | try: 394 | version = oauth_request.get_parameter('oauth_version') 395 | except: 396 | version = VERSION 397 | if version and version != self.version: 398 | raise OAuthError('OAuth version %s not supported.' % str(version)) 399 | return version 400 | 401 | def _get_signature_method(self, oauth_request): 402 | """Figure out the signature with some defaults.""" 403 | try: 404 | signature_method = oauth_request.get_parameter( 405 | 'oauth_signature_method') 406 | except: 407 | signature_method = SIGNATURE_METHOD 408 | try: 409 | # Get the signature method object. 410 | signature_method = self.signature_methods[signature_method] 411 | except: 412 | signature_method_names = ', '.join(self.signature_methods.keys()) 413 | raise OAuthError('Signature method %s not supported try one of the ' 414 | 'following: %s' % (signature_method, signature_method_names)) 415 | 416 | return signature_method 417 | 418 | def _get_consumer(self, oauth_request): 419 | consumer_key = oauth_request.get_parameter('oauth_consumer_key') 420 | consumer = self.data_store.lookup_consumer(consumer_key) 421 | if not consumer: 422 | raise OAuthError('Invalid consumer.') 423 | return consumer 424 | 425 | def _get_token(self, oauth_request, token_type='access'): 426 | """Try to find the token for the provided request token key.""" 427 | token_field = oauth_request.get_parameter('oauth_token') 428 | token = self.data_store.lookup_token(token_type, token_field) 429 | if not token: 430 | raise OAuthError('Invalid %s token: %s' % (token_type, token_field)) 431 | return token 432 | 433 | def _check_signature(self, oauth_request, consumer, token): 434 | timestamp, nonce = oauth_request._get_timestamp_nonce() 435 | self._check_timestamp(timestamp) 436 | self._check_nonce(consumer, token, nonce) 437 | signature_method = self._get_signature_method(oauth_request) 438 | try: 439 | signature = oauth_request.get_parameter('oauth_signature') 440 | except: 441 | raise OAuthError('Missing signature.') 442 | # Validate the signature. 443 | valid_sig = signature_method.check_signature(oauth_request, consumer, 444 | token, signature) 445 | if not valid_sig: 446 | key, base = signature_method.build_signature_base_string( 447 | oauth_request, consumer, token) 448 | raise OAuthError('Invalid signature. Expected signature base ' 449 | 'string: %s' % base) 450 | built = signature_method.build_signature(oauth_request, consumer, token) 451 | 452 | def _check_timestamp(self, timestamp): 453 | """Verify that timestamp is recentish.""" 454 | timestamp = int(timestamp) 455 | now = int(time.time()) 456 | lapsed = now - timestamp 457 | if lapsed > self.timestamp_threshold: 458 | raise OAuthError('Expired timestamp: given %d and now %s has a ' 459 | 'greater difference than threshold %d' % 460 | (timestamp, now, self.timestamp_threshold)) 461 | 462 | def _check_nonce(self, consumer, token, nonce): 463 | """Verify that the nonce is uniqueish.""" 464 | nonce = self.data_store.lookup_nonce(consumer, token, nonce) 465 | if nonce: 466 | raise OAuthError('Nonce already used: %s' % str(nonce)) 467 | 468 | 469 | class OAuthClient(object): 470 | """OAuthClient is a worker to attempt to execute a request.""" 471 | consumer = None 472 | token = None 473 | 474 | def __init__(self, oauth_consumer, oauth_token): 475 | self.consumer = oauth_consumer 476 | self.token = oauth_token 477 | 478 | def get_consumer(self): 479 | return self.consumer 480 | 481 | def get_token(self): 482 | return self.token 483 | 484 | def fetch_request_token(self, oauth_request): 485 | """-> OAuthToken.""" 486 | raise NotImplementedError 487 | 488 | def fetch_access_token(self, oauth_request): 489 | """-> OAuthToken.""" 490 | raise NotImplementedError 491 | 492 | def access_resource(self, oauth_request): 493 | """-> Some protected resource.""" 494 | raise NotImplementedError 495 | 496 | 497 | class OAuthDataStore(object): 498 | """A database abstraction used to lookup consumers and tokens.""" 499 | 500 | def lookup_consumer(self, key): 501 | """-> OAuthConsumer.""" 502 | raise NotImplementedError 503 | 504 | def lookup_token(self, oauth_consumer, token_type, token_token): 505 | """-> OAuthToken.""" 506 | raise NotImplementedError 507 | 508 | def lookup_nonce(self, oauth_consumer, oauth_token, nonce): 509 | """-> OAuthToken.""" 510 | raise NotImplementedError 511 | 512 | def fetch_request_token(self, oauth_consumer): 513 | """-> OAuthToken.""" 514 | raise NotImplementedError 515 | 516 | def fetch_access_token(self, oauth_consumer, oauth_token): 517 | """-> OAuthToken.""" 518 | raise NotImplementedError 519 | 520 | def authorize_request_token(self, oauth_token, user): 521 | """-> OAuthToken.""" 522 | raise NotImplementedError 523 | 524 | 525 | class OAuthSignatureMethod(object): 526 | """A strategy class that implements a signature method.""" 527 | def get_name(self): 528 | """-> str.""" 529 | raise NotImplementedError 530 | 531 | def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token): 532 | """-> str key, str raw.""" 533 | raise NotImplementedError 534 | 535 | def build_signature(self, oauth_request, oauth_consumer, oauth_token): 536 | """-> str.""" 537 | raise NotImplementedError 538 | 539 | def check_signature(self, oauth_request, consumer, token, signature): 540 | built = self.build_signature(oauth_request, consumer, token) 541 | return built == signature 542 | 543 | 544 | class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod): 545 | 546 | def get_name(self): 547 | return 'HMAC-SHA1' 548 | 549 | def build_signature_base_string(self, oauth_request, consumer, token): 550 | sig = ( 551 | escape(oauth_request.get_normalized_http_method()), 552 | escape(oauth_request.get_normalized_http_url()), 553 | escape(oauth_request.get_normalized_parameters()), 554 | ) 555 | 556 | key = '%s&' % escape(consumer.secret) 557 | if token: 558 | key += escape(token.secret) 559 | raw = '&'.join(sig) 560 | return key, raw 561 | 562 | def build_signature(self, oauth_request, consumer, token): 563 | """Builds the base signature string.""" 564 | key, raw = self.build_signature_base_string(oauth_request, consumer, 565 | token) 566 | 567 | # HMAC object. 568 | try: 569 | import hashlib # 2.5 570 | hashed = hmac.new(key, raw, hashlib.sha1) 571 | except: 572 | import sha # Deprecated 573 | hashed = hmac.new(key, raw, sha) 574 | 575 | # Calculate the digest base 64. 576 | return binascii.b2a_base64(hashed.digest())[:-1] 577 | 578 | 579 | class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod): 580 | 581 | def get_name(self): 582 | return 'PLAINTEXT' 583 | 584 | def build_signature_base_string(self, oauth_request, consumer, token): 585 | """Concatenates the consumer key and secret.""" 586 | sig = '%s&' % escape(consumer.secret) 587 | if token: 588 | sig = sig + escape(token.secret) 589 | return sig, sig 590 | 591 | def build_signature(self, oauth_request, consumer, token): 592 | key, raw = self.build_signature_base_string(oauth_request, consumer, 593 | token) 594 | return key -------------------------------------------------------------------------------- /socialauth/lib/oauthgoogle.py: -------------------------------------------------------------------------------- 1 | import httplib 2 | import urllib2 3 | import urllib 4 | import time 5 | import oauth2 as oauth 6 | 7 | from django.conf import settings 8 | 9 | 10 | 11 | REQUEST_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetRequestToken' 12 | ACCESS_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetAccessToken' 13 | AUTHORIZATION_URL = 'https://www.google.com/accounts/OAuthAuthorizeToken' 14 | 15 | 16 | class GoogleOAuthClient(oauth.OAuthClient): 17 | 18 | def __init__(self, consumer_key, consumer_secret, request_token_url=REQUEST_TOKEN_URL, access_token_url=ACCESS_TOKEN_URL, authorization_url=AUTHORIZATION_URL): 19 | self.consumer_key = consumer_key 20 | self.consumer_secret = consumer_secret 21 | self.request_token_url = request_token_url 22 | self.access_token_url = access_token_url 23 | self.authorization_url = authorization_url 24 | self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) 25 | self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() 26 | 27 | def fetch_request_token(self, **kwargs): 28 | if not 'scope' in kwargs: 29 | kwargs['scope'] = 'http://www.google.com/m8/feeds' 30 | oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=self.request_token_url, parameters=kwargs) 31 | oauth_request.sign_request(self.signature_method, self.consumer, None) 32 | params = oauth_request.parameters 33 | data = urllib.urlencode(params) 34 | full_url='%s?%s'%(self.request_token_url, data) 35 | response = urllib2.urlopen(full_url) 36 | return oauth.OAuthToken.from_string(response.read()) 37 | 38 | def authorize_token_url(self, token, callback_url=None,): 39 | if not callback_url: 40 | callback_url = CALLBACK_URL 41 | oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, callback=callback_url, http_url=self.authorization_url,) 42 | params = oauth_request.parameters 43 | data = urllib.urlencode(params) 44 | full_url='%s?%s'%(self.authorization_url, data) 45 | return full_url 46 | # response = urllib2.urlopen(full_url) 47 | # return oauth.OAuthToken.from_string(response.read()) 48 | 49 | def fetch_access_token(self, token): 50 | oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=self.access_token_url) 51 | oauth_request.sign_request(self.signature_method, self.consumer, token) 52 | params = oauth_request.parameters 53 | data = urllib.urlencode(params) 54 | full_url='%s?%s'%(self.access_token_url, data) 55 | response = urllib2.urlopen(full_url) 56 | return oauth.OAuthToken.from_string(response.read()) 57 | 58 | 59 | def access_resource(self, url, token, **kwargs): 60 | oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=url, parameters=kwargs) 61 | oauth_request.sign_request(self.signature_method, self.consumer, token) 62 | params = oauth_request.parameters 63 | data = urllib.urlencode(params) 64 | full_url='%s?%s'%(url, data) 65 | #print full_url 66 | response = urllib2.urlopen(full_url) 67 | return response 68 | 69 | def run_example(): 70 | 71 | # setup 72 | print '** OAuth Python Library Example **' 73 | client = GoogleOAuthClient(CONSUMER_KEY, CONSUMER_SECRET) 74 | pause() 75 | 76 | # get request token 77 | print '* Obtain a request token ...' 78 | pause() 79 | 80 | print 'REQUEST (via headers)' 81 | print 'parameters: %s' % str(oauth_request.parameters) 82 | pause() 83 | token = client.fetch_request_token(oauth_request) 84 | print 'GOT' 85 | print 'key: %s' % str(token.key) 86 | print 'secret: %s' % str(token.secret) 87 | pause() 88 | 89 | print '* Authorize the request token ...' 90 | pause() 91 | 92 | print 'REQUEST (via url query string)' 93 | print 'parameters: %s' % str(oauth_request.parameters) 94 | pause() 95 | # this will actually occur only on some callback 96 | url = client.authorize_token(oauth_request, get_url_only=True) 97 | print 'GOT' 98 | print url 99 | pause() 100 | 101 | # get access token 102 | print '* Obtain an access token ...' 103 | pause() 104 | print 'REQUEST (via headers)' 105 | print 'parameters: %s' % str(oauth_request.parameters) 106 | pause() 107 | token = client.fetch_access_token(oauth_request) 108 | print 'GOT' 109 | print 'key: %s' % str(token.key) 110 | print 'secret: %s' % str(token.secret) 111 | pause() 112 | 113 | # access some protected resources 114 | print '* Access protected resources ...' 115 | pause() 116 | parameters = {'file': 'vacation.jpg', 'size': 'original', 'oauth_callback': CALLBACK_URL} # resource specific params 117 | print 'REQUEST (via post body)' 118 | print 'parameters: %s' % str(oauth_request.parameters) 119 | pause() 120 | params = client.access_resource(oauth_request) 121 | print 'GOT' 122 | print 'non-oauth parameters: %s' % params 123 | pause() 124 | 125 | def pause(): 126 | print '' 127 | time.sleep(1) 128 | 129 | if __name__ == '__main__': 130 | run_example() 131 | print 'Done.' -------------------------------------------------------------------------------- /socialauth/lib/oauthtwitter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright under GPLv3 4 | 5 | '''A class the inherits everything from python-twitter and allows oauth based access 6 | 7 | Requires: 8 | python-twitter 9 | simplejson 10 | oauth 11 | ''' 12 | 13 | __author__ = "Hameedullah Khan " 14 | __version__ = "0.1" 15 | 16 | 17 | from twitter import Api, User 18 | try: 19 | import json as simplejson 20 | except: 21 | from django.utils import simplejson 22 | 23 | from oauth import oauth 24 | 25 | 26 | 27 | # Taken from oauth implementation at: http://github.com/harperreed/twitteroauth-python/tree/master 28 | REQUEST_TOKEN_URL = 'https://twitter.com/oauth/request_token' 29 | ACCESS_TOKEN_URL = 'https://twitter.com/oauth/access_token' 30 | AUTHORIZATION_URL = 'http://twitter.com/oauth/authorize' 31 | SIGNIN_URL = 'http://twitter.com/oauth/authenticate' 32 | 33 | 34 | class OAuthApi(Api): 35 | def __init__(self, consumer_key, consumer_secret, access_token=None): 36 | if access_token: 37 | Api.__init__(self,access_token.key, access_token.secret) 38 | else: 39 | Api.__init__(self) 40 | self._Consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) 41 | self._signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() 42 | self._access_token = access_token 43 | 44 | 45 | def _GetOpener(self): 46 | opener = self._urllib.build_opener() 47 | return opener 48 | 49 | def _FetchUrl(self, 50 | url, 51 | post_data=None, 52 | parameters=None, 53 | no_cache=None): 54 | '''Fetch a URL, optionally caching for a specified time. 55 | 56 | Args: 57 | url: The URL to retrieve 58 | post_data: 59 | A dict of (str, unicode) key/value pairs. If set, POST will be used. 60 | parameters: 61 | A dict whose key/value pairs should encoded and added 62 | to the query string. [OPTIONAL] 63 | no_cache: If true, overrides the cache on the current request 64 | 65 | Returns: 66 | A string containing the body of the response. 67 | ''' 68 | # Build the extra parameters dict 69 | extra_params = {} 70 | try: 71 | if self._default_params: 72 | extra_params.update(self._default_params) 73 | except AttributeError: 74 | pass 75 | if parameters: 76 | extra_params.update(parameters) 77 | 78 | # Add key/value parameters to the query string of the url 79 | #url = self._BuildUrl(url, extra_params=extra_params) 80 | 81 | if post_data: 82 | http_method = "POST" 83 | extra_params.update(post_data) 84 | else: 85 | http_method = "GET" 86 | 87 | req = self._makeOAuthRequest(url, parameters=extra_params, 88 | http_method=http_method) 89 | self._signRequest(req, self._signature_method) 90 | 91 | 92 | # Get a url opener that can handle Oauth basic auth 93 | opener = self._GetOpener() 94 | 95 | #encoded_post_data = self._EncodePostData(post_data) 96 | 97 | if post_data: 98 | encoded_post_data = req.to_postdata() 99 | url = req.get_normalized_http_url() 100 | else: 101 | url = req.to_url() 102 | encoded_post_data = "" 103 | 104 | no_cache=True 105 | # Open and return the URL immediately if we're not going to cache 106 | # OR we are posting data 107 | if encoded_post_data or no_cache: 108 | if encoded_post_data: 109 | url_data = opener.open(url, encoded_post_data).read() 110 | else: 111 | url_data = opener.open(url).read() 112 | opener.close() 113 | else: 114 | # Unique keys are a combination of the url and the username 115 | if self._username: 116 | key = self._username + ':' + url 117 | else: 118 | key = url 119 | 120 | # See if it has been cached before 121 | last_cached = self._cache.GetCachedTime(key) 122 | 123 | # If the cached version is outdated then fetch another and store it 124 | if not last_cached or time.time() >= last_cached + self._cache_timeout: 125 | url_data = opener.open(url).read() 126 | opener.close() 127 | self._cache.Set(key, url_data) 128 | else: 129 | url_data = self._cache.Get(key) 130 | 131 | # Always return the latest version 132 | return url_data 133 | 134 | def _makeOAuthRequest(self, url, token=None, 135 | parameters=None, http_method="GET"): 136 | '''Make a OAuth request from url and parameters 137 | 138 | Args: 139 | url: The Url to use for creating OAuth Request 140 | parameters: 141 | The URL parameters 142 | http_method: 143 | The HTTP method to use 144 | Returns: 145 | A OAauthRequest object 146 | ''' 147 | if not token: 148 | token = self._access_token 149 | request = oauth.OAuthRequest.from_consumer_and_token( 150 | self._Consumer, token=token, 151 | http_url=url, parameters=parameters, 152 | http_method=http_method) 153 | return request 154 | 155 | def _signRequest(self, req, signature_method=oauth.OAuthSignatureMethod_HMAC_SHA1()): 156 | '''Sign a request 157 | 158 | Reminder: Created this function so incase 159 | if I need to add anything to request before signing 160 | 161 | Args: 162 | req: The OAuth request created via _makeOAuthRequest 163 | signate_method: 164 | The oauth signature method to use 165 | ''' 166 | req.sign_request(signature_method, self._Consumer, self._access_token) 167 | 168 | 169 | def getAuthorizationURL(self, token, url=AUTHORIZATION_URL, callback_url = None): 170 | '''Create a signed authorization URL 171 | 172 | Returns: 173 | A signed OAuthRequest authorization URL 174 | ''' 175 | if callback_url: 176 | parameters= {'oauth_callback':callback_url} 177 | else: 178 | parameters= {} 179 | req = self._makeOAuthRequest(url, token=token, parameters=parameters) 180 | self._signRequest(req) 181 | return req.to_url() 182 | 183 | def getSigninURL(self, token, url=SIGNIN_URL, callback_url = None): 184 | '''Create a signed Sign-in URL 185 | 186 | Returns: 187 | A signed OAuthRequest Sign-in URL 188 | ''' 189 | 190 | signin_url = self.getAuthorizationURL(token, url, callback_url) 191 | return signin_url 192 | 193 | def getAccessToken(self, url=ACCESS_TOKEN_URL): 194 | token = self._FetchUrl(url, no_cache=True) 195 | return oauth.OAuthToken.from_string(token) 196 | 197 | def getRequestToken(self, url=REQUEST_TOKEN_URL): 198 | '''Get a Request Token from Twitter 199 | 200 | Returns: 201 | A OAuthToken object containing a request token 202 | ''' 203 | resp = self._FetchUrl(url, no_cache=True) 204 | token = oauth.OAuthToken.from_string(resp) 205 | return token 206 | 207 | def GetUserInfo(self, url='https://twitter.com/account/verify_credentials.json'): 208 | '''Get user information from twitter 209 | 210 | Returns: 211 | Returns the twitter.User object 212 | ''' 213 | json = self._FetchUrl(url) 214 | data = simplejson.loads(json) 215 | self._CheckForTwitterError(data) 216 | return User.NewFromJsonDict(data) 217 | 218 | -------------------------------------------------------------------------------- /socialauth/lib/oauthtwitter2.py: -------------------------------------------------------------------------------- 1 | import time 2 | from urllib2 import Request, urlopen 3 | 4 | import oauth1 as oauth 5 | from twitter import User 6 | 7 | from django.utils import simplejson as json 8 | 9 | TWITTER_URL = 'twitter.com' 10 | REQUEST_TOKEN_URL = 'https://api.twitter.com/oauth/request_token' 11 | ACCESS_TOKEN_URL = 'https://api.twitter.com/oauth/access_token' 12 | AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize' 13 | TWITTER_CREDENTIALS_URL = 'https://api.twitter.com/1/account/verify_credentials.json' 14 | 15 | 16 | def oauth_response(req): 17 | response = urlopen(Request(req.http_url, headers=req.to_header())) 18 | return response.read() 19 | 20 | 21 | class TwitterOAuthClient(oauth.OAuthClient): 22 | def __init__(self, consumer_key, consumer_secret, request_token_url=REQUEST_TOKEN_URL, access_token_url=ACCESS_TOKEN_URL, authorization_url=AUTHORIZATION_URL): 23 | self.consumer_secret = consumer_secret 24 | self.consumer_key = consumer_key 25 | self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) 26 | self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() 27 | self.request_token_url = request_token_url 28 | self.access_token_url = access_token_url 29 | self.authorization_url = authorization_url 30 | 31 | def fetch_request_token(self, callback): 32 | oauth_request = oauth.OAuthRequest.from_consumer_and_token( 33 | self.consumer, 34 | http_url=self.request_token_url, 35 | callback=callback 36 | ) 37 | oauth_request.sign_request(self.signature_method, self.consumer, None) 38 | return oauth.OAuthToken.from_string(oauth_response(oauth_request)) 39 | 40 | def authorize_token_url(self, token, callback_url=None): 41 | oauth_request = oauth.OAuthRequest.from_consumer_and_token( 42 | self.consumer, 43 | token=token, 44 | http_url=self.authorization_url, 45 | callback=callback_url 46 | ) 47 | oauth_request.sign_request(self.signature_method, self.consumer, token) 48 | return oauth_request.to_url() 49 | 50 | def fetch_access_token(self, token, verifier): 51 | oauth_request = oauth.OAuthRequest.from_consumer_and_token( 52 | self.consumer, 53 | token=token, 54 | verifier=verifier, 55 | http_url=self.access_token_url 56 | ) 57 | oauth_request.http_method = 'POST' 58 | oauth_request.sign_request(self.signature_method, self.consumer, token) 59 | return oauth.OAuthToken.from_string(oauth_response(oauth_request)) 60 | 61 | def get_user_info(self, token): 62 | try: 63 | oauth_request = oauth.OAuthRequest.from_consumer_and_token( 64 | self.consumer, 65 | token=token, 66 | http_url=TWITTER_CREDENTIALS_URL 67 | ) 68 | oauth_request.sign_request(self.signature_method, self.consumer, token) 69 | return User.NewFromJsonDict(json.loads(oauth_response(oauth_request))) 70 | except: 71 | pass 72 | return None 73 | 74 | def access_resource(self, oauth_request): 75 | # via post body 76 | # -> some protected resources 77 | headers = {'Content-Type': 'application/x-www-form-urlencoded'} 78 | connection = get_connection() 79 | body = oauth_request.to_postdata() 80 | connection.request('POST', RESOURCE_URL, body=body, headers=headers) 81 | response = connection.getresponse().read() 82 | connection.close() 83 | return response 84 | 85 | 86 | def run_example(): 87 | 88 | # setup 89 | print '** OAuth Python Library Example **' 90 | consumer_key = raw_input('Twitter Consumer Key:').strip() 91 | consumer_secret = raw_input('Twitter Consumer Secret:').strip() 92 | callback = 'http://example.com/newaccounts/login/done/' 93 | 94 | client = TwitterOAuthClient(consumer_key, consumer_secret) 95 | 96 | pause() 97 | 98 | # get request token 99 | print '* Obtain a request token ...' 100 | pause() 101 | 102 | token = client.fetch_request_token(callback) 103 | print 'GOT' 104 | print 'key: %s' % str(token.key) 105 | print 'secret: %s' % str(token.secret) 106 | pause() 107 | 108 | print '* Authorize the request token ...' 109 | pause() 110 | # this will actually occur only on some callback 111 | url = client.authorize_token_url(token) 112 | print 'GOT' 113 | print url 114 | pause() 115 | 116 | # get access token 117 | print '* Obtain an access token ...' 118 | pause() 119 | 120 | access_token = client.fetch_access_token(token) 121 | print 'GOT' 122 | print 'key: %s' % str(access_token.key) 123 | print 'secret: %s' % str(access_token.secret) 124 | pause() 125 | 126 | # access some protected resources 127 | print '* Access protected resources ...' 128 | pause() 129 | parameters = { 130 | 'file': 'vacation.jpg', 131 | 'size': 'original', 132 | 'oauth_callback': callback, 133 | } # resource specific params 134 | oauth_request = oauth.OAuthRequest.from_consumer_and_token( 135 | client.consumer, 136 | token=token, 137 | http_method='POST', 138 | http_url=RESOURCE_URL, 139 | parameters=parameters 140 | ) 141 | oauth_request.sign_request(client.signature_method, client.consumer, token) 142 | print 'REQUEST (via post body)' 143 | print 'parameters: %s' % str(oauth_request.parameters) 144 | pause() 145 | params = client.access_resource(oauth_request) 146 | print 'GOT' 147 | print 'non-oauth parameters: %s' % params 148 | pause() 149 | 150 | 151 | def pause(): 152 | print '' 153 | time.sleep(1) 154 | 155 | if __name__ == '__main__': 156 | run_example() 157 | print 'Done.' 158 | -------------------------------------------------------------------------------- /socialauth/lib/oauthyahoo.py: -------------------------------------------------------------------------------- 1 | import httplib 2 | import urllib2 3 | import urllib 4 | import time 5 | import oauth.oauth as oauth 6 | 7 | from django.conf import settings 8 | 9 | 10 | 11 | REQUEST_TOKEN_URL = 'https://api.login.yahoo.com/oauth/v2/get_request_token' 12 | ACCESS_TOKEN_URL = 'https://api.login.yahoo.com/oauth/v2/get_token' 13 | AUTHORIZATION_URL = 'https://api.login.yahoo.com/oauth/v2/request_auth' 14 | 15 | 16 | class YahooOAuthClient(oauth.OAuthClient): 17 | 18 | def __init__(self, consumer_key, consumer_secret, request_token_url=REQUEST_TOKEN_URL, access_token_url=ACCESS_TOKEN_URL, authorization_url=AUTHORIZATION_URL): 19 | self.consumer_key = consumer_key 20 | self.consumer_secret = consumer_secret 21 | self.request_token_url = request_token_url 22 | self.access_token_url = access_token_url 23 | self.authorization_url = authorization_url 24 | self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) 25 | self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() 26 | 27 | def fetch_request_token(self, **kwargs): 28 | oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=self.request_token_url, parameters=kwargs) 29 | oauth_request.sign_request(self.signature_method, self.consumer, None) 30 | params = oauth_request.parameters 31 | data = urllib.urlencode(params) 32 | full_url='%s?%s'%(self.request_token_url, data) 33 | response = urllib2.urlopen(full_url) 34 | return oauth.OAuthToken.from_string(response.read()) 35 | 36 | def authorize_token_url(self, token, callback_url=None): 37 | oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, callback=callback_url, http_url=self.authorization_url) 38 | params = oauth_request.parameters 39 | data = urllib.urlencode(params) 40 | full_url='%s?%s'%(self.authorization_url, data) 41 | return full_url 42 | response = urllib2.urlopen(full_url) 43 | return oauth.OAuthToken.from_string(response.read()) 44 | 45 | def fetch_access_token(self, token, **kwargs): 46 | oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=self.access_token_url, parameters = kwargs) 47 | oauth_request.sign_request(self.signature_method, self.consumer, token) 48 | params = oauth_request.parameters 49 | data = urllib.urlencode(params) 50 | full_url='%s?%s'%(self.access_token_url, data) 51 | response = urllib2.urlopen(full_url) 52 | return response 53 | #return oauth.OAuthToken.from_string(response.read()) 54 | 55 | 56 | def access_resource(self, url, token, **kwargs): 57 | oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=url, parameters = kwargs) 58 | oauth_request.sign_request(self.signature_method, self.consumer, token) 59 | params = oauth_request.parameters 60 | data = urllib.urlencode(params) 61 | full_url='%s?%s'%(url, data) 62 | response = urllib2.urlopen(full_url) 63 | return response 64 | 65 | def run_example(): 66 | 67 | # setup 68 | print '** OAuth Python Library Example **' 69 | client = GoogleOAuthClient(CONSUMER_KEY, CONSUMER_SECRET) 70 | pause() 71 | 72 | # get request token 73 | print '* Obtain a request token ...' 74 | pause() 75 | 76 | print 'REQUEST (via headers)' 77 | print 'parameters: %s' % str(oauth_request.parameters) 78 | pause() 79 | token = client.fetch_request_token(oauth_request) 80 | print 'GOT' 81 | print 'key: %s' % str(token.key) 82 | print 'secret: %s' % str(token.secret) 83 | pause() 84 | 85 | print '* Authorize the request token ...' 86 | pause() 87 | 88 | print 'REQUEST (via url query string)' 89 | print 'parameters: %s' % str(oauth_request.parameters) 90 | pause() 91 | # this will actually occur only on some callback 92 | url = client.authorize_token(oauth_request, get_url_only=True) 93 | print 'GOT' 94 | print url 95 | pause() 96 | 97 | # get access token 98 | print '* Obtain an access token ...' 99 | pause() 100 | print 'REQUEST (via headers)' 101 | print 'parameters: %s' % str(oauth_request.parameters) 102 | pause() 103 | token = client.fetch_access_token(oauth_request) 104 | print 'GOT' 105 | print 'key: %s' % str(token.key) 106 | print 'secret: %s' % str(token.secret) 107 | pause() 108 | 109 | # access some protected resources 110 | print '* Access protected resources ...' 111 | pause() 112 | parameters = {'file': 'vacation.jpg', 'size': 'original', 'oauth_callback': CALLBACK_URL} # resource specific params 113 | print 'REQUEST (via post body)' 114 | print 'parameters: %s' % str(oauth_request.parameters) 115 | pause() 116 | params = client.access_resource(oauth_request) 117 | print 'GOT' 118 | print 'non-oauth parameters: %s' % params 119 | pause() 120 | 121 | def pause(): 122 | print '' 123 | time.sleep(1) 124 | 125 | if __name__ == '__main__': 126 | run_example() 127 | print 'Done.' -------------------------------------------------------------------------------- /socialauth/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | 4 | class AuthMeta(models.Model): 5 | """Metadata for Authentication""" 6 | def __unicode__(self): 7 | return '%s - %s' % (self.user, self.provider) 8 | 9 | user = models.ForeignKey(User) 10 | provider = models.CharField(max_length=200) 11 | is_email_filled = models.BooleanField(default=False) 12 | is_profile_modified = models.BooleanField(default=False) 13 | 14 | class OpenidProfile(models.Model): 15 | """A class associating an User to a Openid""" 16 | openid_key = models.CharField(max_length=200, unique=True, db_index=True) 17 | 18 | user = models.ForeignKey(User, related_name='openid_profiles') 19 | is_username_valid = models.BooleanField(default=False) 20 | #Values which we get from openid.sreg 21 | email = models.EmailField() 22 | nickname = models.CharField(max_length=100) 23 | 24 | 25 | def __unicode__(self): 26 | return unicode(self.openid_key) 27 | 28 | def __repr__(self): 29 | return unicode(self.openid_key) 30 | 31 | class LinkedInUserProfile(models.Model): 32 | """ 33 | For users who login via Linkedin. 34 | """ 35 | linkedin_uid = models.CharField(max_length=50, 36 | unique=True, 37 | db_index=True) 38 | 39 | user = models.ForeignKey(User, related_name='linkedin_profiles') 40 | headline = models.CharField(max_length=120, blank=True, null=True) 41 | company = models.CharField(max_length=255, blank=True, null=True) 42 | location = models.TextField(blank=True, null=True) 43 | industry = models.CharField(max_length=255, blank=True, null=True) 44 | profile_image_url = models.URLField(blank=True, null=True) 45 | url = models.URLField(blank=True, null=True) 46 | access_token = models.CharField(max_length=255, 47 | blank=True, 48 | null=True, 49 | editable=False) 50 | 51 | def __unicode__(self): 52 | return "%s's profile" % self.user 53 | 54 | class TwitterUserProfile(models.Model): 55 | """ 56 | For users who login via Twitter. 57 | """ 58 | screen_name = models.CharField(max_length=200, 59 | unique=True, 60 | db_index=True) 61 | 62 | user = models.ForeignKey(User, related_name='twitter_profiles') 63 | access_token = models.CharField(max_length=255, 64 | blank=True, 65 | null=True, 66 | editable=False) 67 | profile_image_url = models.URLField(blank=True, null=True) 68 | location = models.TextField(blank=True, null=True) 69 | url = models.URLField(blank=True, null=True) 70 | description = models.CharField(max_length=160, blank=True, null=True) 71 | 72 | def __unicode__(self): 73 | return "%s's profile" % self.user 74 | 75 | 76 | class FacebookUserProfile(models.Model): 77 | """ 78 | For users who login via Facebook. 79 | """ 80 | facebook_uid = models.CharField(max_length=20, 81 | unique=True, 82 | db_index=True) 83 | 84 | user = models.ForeignKey(User, related_name='facebook_profiles') 85 | profile_image_url = models.URLField(blank=True, null=True) 86 | profile_image_url_big = models.URLField(blank=True, null=True) 87 | profile_image_url_small = models.URLField(blank=True, null=True) 88 | location = models.TextField(blank=True, null=True) 89 | url = models.URLField(blank=True, null=True) 90 | about_me = models.CharField(max_length=160, blank=True, null=True) 91 | 92 | def __unicode__(self): 93 | return "%s's profile" % self.user 94 | 95 | class GithubUserProfile(models.Model): 96 | user = models.ForeignKey(User) 97 | access_token = models.CharField(max_length=100, blank=True, null=True, editable=False) 98 | 99 | def __unicode__(self): 100 | return "%s's profile" % self.user 101 | 102 | class FoursquareUserProfile(models.Model): 103 | user = models.ForeignKey(User) 104 | access_token = models.CharField(max_length=255, blank=True, null=True, editable=False) 105 | 106 | def __unicode__(self): 107 | return "%s's profile" % self.user 108 | -------------------------------------------------------------------------------- /socialauth/templates/openid/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JQuery Simple OpenID Selector Demo 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 |

JQuery Simple OpenID Selector Demo

29 |

This is a simple example to show how you can include the Javascript into your page.

30 |
31 | 32 | 33 |
34 | 35 | 36 |
37 | Sign-in or Create New Account 38 | 39 |
40 |

Please click your account provider:

41 |
42 |
43 | 44 |
45 | 46 | 47 |
48 | 52 |
53 |
54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /socialauth/templates/socialauth/base.html: -------------------------------------------------------------------------------- 1 | 2 | {% block html_declare %} 3 | 4 | {% endblock %} 5 | 6 | 7 | {% if user.is_authenticated %}Hello {{ user }}{% else %}Please login{% endif %} 8 | 9 | {% block extra_head %} 10 | {% endblock %} 11 | 12 | 13 | 14 |

social-auth

15 | {% if FACEBOOK_APP_ID %} 16 |
17 | 37 | {% endif %} 38 | {% block extra_body %} 39 | {% endblock %} 40 | 41 | {% block main_content %} 42 | {% endblock %} 43 | 44 | {% block content %} 45 | {% endblock %} 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /socialauth/templates/socialauth/editprofile.html: -------------------------------------------------------------------------------- 1 | {% extends 'socialauth/socialauth_base.html' %} 2 | 3 | 4 | 5 | 6 | {% block content %} 7 | 8 |
9 | 10 | {{ edit_form }} 11 |
12 | 13 |
14 | 15 | 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /socialauth/templates/socialauth/login_page.html: -------------------------------------------------------------------------------- 1 | {% extends 'socialauth/base.html' %} 2 | 3 | {% block html_declare %} 4 | 5 | {% endblock %} 6 | 7 | {% block extra_head %} 8 | 9 | 10 | 11 | 12 | 13 | 21 | 22 | {% endblock %} 23 | 24 | {% block extra_body %} 25 | {{ block.super }} 26 | 27 | {% endblock %} 28 | 29 | {% block main_content %} 30 | {% if user.is_authenticated %} 31 |

You are already logged in. Logout?

32 | {% else %} 33 |
34 | 35 |
36 | 37 | 38 |
39 | Sign-in or Create New Account via these openid providers 40 | 41 |
42 |

Please click your account provider:

43 | 44 |
45 | {% if FACEBOOK_APP_ID %} 46 |
47 |

48 |
49 | {% endif %} 50 | 51 |
52 | 53 |
54 | 55 |
56 | 57 |
58 | 59 | 62 |
63 | 64 | 65 | 66 |
67 | 68 | 69 |
70 |
71 | 72 | 79 | 80 |
81 | 82 |
83 | 84 | 85 | 86 | 87 | 88 |
89 |
90 |
91 | 92 |
93 | {% endif %} 94 | {% endblock %} 95 | -------------------------------------------------------------------------------- /socialauth/templates/socialauth/signin_complete.html: -------------------------------------------------------------------------------- 1 | {% extends 'socialauth/base.html' %} 2 | 3 | {% block main_content %} 4 | 5 | You have signed in.
6 | Logout 7 | 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /socialauth/templates/socialauth/socialauth_base.html: -------------------------------------------------------------------------------- 1 | {% extends "socialauth/base.html" %} 2 | {% block head %} 3 | 4 | {% block html_declare %} 5 | 6 | {% endblock %} 7 | {% endblock %} 8 | 9 | {% block content %} 10 | 11 | {% if user.is_authenticated %}Hello {{ user }}{% else %}Please login{% endif %} 12 | {% block extra_head %} 13 | {% endblock %} 14 | 15 | 16 | {% block extra_body %} 17 | {% endblock %} 18 | 19 | {% block main_content %} 20 | {% endblock %} 21 | 22 | 23 | 24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /socialauth/templates/socialauth/xd_receiver.htm: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /socialauth/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiliq/Django-Socialauth/b0b1acb180dd955edcc9b9b9d086470b8c332de6/socialauth/templatetags/__init__.py -------------------------------------------------------------------------------- /socialauth/templatetags/socialauth_tags.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | register = template.Library() 4 | 5 | @register.simple_tag 6 | def get_calculated_username(user): 7 | if hasattr(user, 'openidprofile_set') and user.openidprofile_set.filter().count(): 8 | if user.openidprofile_set.filter(is_username_valid = True).count(): 9 | return user.openidprofile_set.filter(is_username_valid = True)[0].user.username 10 | else: 11 | from django.core.urlresolvers import reverse 12 | editprof_url = reverse('socialauth_editprofile') 13 | return u'Anonymous User. Add name'%editprof_url 14 | else: 15 | return user.username 16 | -------------------------------------------------------------------------------- /socialauth/test_data.py.example: -------------------------------------------------------------------------------- 1 | test_base_url = "http://nothing.uswaretech.net:8000/" 2 | twitter_username = "socialauthtest2" 3 | twitter_password = "" 4 | myopenid_url = "http://socialauthtest2.myopenid.com/" 5 | myopenid_password = "" -------------------------------------------------------------------------------- /socialauth/tests.py: -------------------------------------------------------------------------------- 1 | from selenium import selenium 2 | import unittest 3 | from django.contrib.auth.models import User 4 | 5 | from test_data import * 6 | 7 | class TwitterTester(unittest.TestCase): 8 | def setUp(self): 9 | self.verificationErrors = [] 10 | self.selenium = selenium("localhost", 4444, "*chrome", test_base_url) 11 | self.selenium.start() 12 | 13 | def testTwitter(self): 14 | sel = self.selenium 15 | #Test that a user is created after 16 | # logging in via Twitter for the first time. 17 | initial_user_count = User.objects.count() 18 | sel.open("/accounts/login/") 19 | sel.click("link=Login via twitter") 20 | sel.wait_for_page_to_load("30000") 21 | try: 22 | sel.click("link=Sign out") 23 | sel.wait_for_page_to_load("30000") 24 | except: 25 | pass 26 | sel.type("username_or_email", twitter_username) 27 | sel.type("session[password]", twitter_password) 28 | sel.click("allow") 29 | sel.wait_for_page_to_load("30000") 30 | sel.open("/accounts/login/") 31 | sel.open("/accounts/edit/profile/") 32 | self.assertEqual(initial_user_count + 1, User.objects.count()) 33 | 34 | 35 | def tearDown(self): 36 | self.selenium.stop() 37 | self.assertEqual([], self.verificationErrors) 38 | 39 | 40 | class OpenIdTester(unittest.TestCase): 41 | def setUp(self): 42 | self.verificationErrors = [] 43 | self.selenium = selenium("localhost", 4444, "*chrome", test_base_url) 44 | self.selenium.start() 45 | 46 | def testOpenId(self): 47 | initial_user_count = User.objects.count() 48 | sel = self.selenium 49 | sel.open("/accounts/login/") 50 | sel.click("openid_login_link") 51 | sel.wait_for_page_to_load("30000") 52 | sel.type("openid_url", myopenid_url) 53 | sel.click("//input[@value='Sign in']") 54 | sel.wait_for_page_to_load("30000") 55 | try: 56 | sel.type("password", myopenid_password) 57 | sel.click("signin_button") 58 | sel.wait_for_page_to_load("30000") 59 | except: 60 | sel.click("continue-button") 61 | sel.wait_for_page_to_load("30000") 62 | sel.open("/accounts/login/") 63 | self.assertEqual(initial_user_count + 1, User.objects.count()) 64 | 65 | def tearDown(self): 66 | self.selenium.stop() 67 | self.assertEqual([], self.verificationErrors) 68 | 69 | if __name__ == "__main__": 70 | unittest.main() 71 | 72 | 73 | 74 | if __name__ == "__main__": 75 | unittest.main() 76 | -------------------------------------------------------------------------------- /socialauth/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls.defaults import * 2 | from openid_consumer.views import complete, signout 3 | from django.views.generic.base import TemplateView 4 | 5 | #Login Views 6 | urlpatterns = patterns('socialauth.views', 7 | url(r'^facebook_login/xd_receiver.htm$', TemplateView.as_view(template_name='socialauth/xd_receiver.htm'), name='socialauth_xd_receiver'), 8 | url(r'^facebook_login/$', 'facebook_login', name='socialauth_facebook_login'), 9 | url(r'^facebook_login/done/$', 'facebook_login_done', name='socialauth_facebook_login_done'), 10 | url(r'^login/$', 'login_page', name='socialauth_login_page'), 11 | url(r'^openid_login/$', 'openid_login_page', name='socialauth_openid_login_page'), 12 | url(r'^twitter_login/$', 'twitter_login', name='socialauth_twitter_login'), 13 | url(r'^twitter_login/done/$', 'twitter_login_done', name='socialauth_twitter_login_done'), 14 | url(r'^linkedin_login/$', 'linkedin_login', name='socialauth_linkedin_login'), 15 | url(r'^linkedin_login/done/$', 'linkedin_login_done', name='socialauth_linkedin_login_done'), 16 | url(r'^yahoo_login/$', 'yahoo_login', name='socialauth_yahoo_login'), 17 | url(r'^yahoo_login/complete/$', complete, name='socialauth_yahoo_complete'), 18 | url(r'^gmail_login/$', 'gmail_login', name='socialauth_google_login'), 19 | url(r'^gmail_login/complete/$', complete, name='socialauth_google_complete'), 20 | url(r'^openid/$', 'openid_login', name='socialauth_openid_login'), 21 | url(r'^openid/complete/$', complete, name='socialauth_openid_complete'), 22 | url(r'^openid/signout/$', signout, name='openid_signout'), 23 | url(r'^openid/done/$', 'openid_done', name='openid_openid_done'), 24 | url(r'^github_login/$', 'github_login', name='github_login'), 25 | url(r'github_login/done/$', 'github_login_done', name='github_login_done'), 26 | url(r'^foursquare_login/$', 'foursquare_login', name='foursquare_login'), 27 | url(r'^foursquare_login/done/$', 'foursquare_login_done', name='foursquare_login_done'), 28 | ) 29 | 30 | #Other views. 31 | urlpatterns += patterns('socialauth.views', 32 | url(r'^$', 'login_page', name='socialauth_index'), 33 | url(r'^done/$', 'signin_complete', name='socialauth_signin_complete'), 34 | url(r'^edit/profile/$', 'editprofile', name='socialauth_editprofile'), 35 | url(r'^logout/$', 'social_logout', name='socialauth_social_logout'), 36 | ) 37 | 38 | -------------------------------------------------------------------------------- /socialauth/views.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import urllib 3 | from oauth import oauth 4 | 5 | from django.shortcuts import render_to_response 6 | from django.template import RequestContext 7 | from django.contrib.auth import authenticate, login 8 | from django.http import HttpResponseRedirect 9 | from django.core.urlresolvers import reverse 10 | from django.conf import settings 11 | from django.contrib.auth.decorators import login_required 12 | from django.contrib.auth.views import logout 13 | 14 | from socialauth.models import AuthMeta 15 | from socialauth.forms import EditProfileForm 16 | 17 | from openid_consumer.views import begin 18 | from socialauth.lib import oauthtwitter2 as oauthtwitter 19 | 20 | from socialauth.lib.linkedin import * 21 | from socialauth.lib.github import GithubClient 22 | from socialauth.lib import foursquare 23 | 24 | LINKEDIN_CONSUMER_KEY = getattr(settings, 'LINKEDIN_CONSUMER_KEY', '') 25 | LINKEDIN_CONSUMER_SECRET = getattr(settings, 'LINKEDIN_CONSUMER_SECRET', '') 26 | 27 | ADD_LOGIN_REDIRECT_URL = getattr(settings, 'ADD_LOGIN_REDIRECT_URL', '') 28 | LOGIN_REDIRECT_URL = getattr(settings, 'LOGIN_REDIRECT_URL', '') 29 | LOGIN_URL = getattr(settings, 'LOGIN_URL', '') 30 | 31 | TWITTER_CONSUMER_KEY = getattr(settings, 'TWITTER_CONSUMER_KEY', '') 32 | TWITTER_CONSUMER_SECRET = getattr(settings, 'TWITTER_CONSUMER_SECRET', '') 33 | 34 | FACEBOOK_APP_ID = getattr(settings, 'FACEBOOK_APP_ID', '') 35 | FACEBOOK_API_KEY = getattr(settings, 'FACEBOOK_API_KEY', '') 36 | FACEBOOK_SECRET_KEY = getattr(settings, 'FACEBOOK_SECRET_KEY', '') 37 | 38 | 39 | 40 | def del_dict_key(src_dict, key): 41 | if key in src_dict: 42 | del src_dict[key] 43 | 44 | def login_page(request): 45 | return render_to_response('socialauth/login_page.html', 46 | {'next': request.GET.get('next', LOGIN_REDIRECT_URL)}, 47 | context_instance=RequestContext(request)) 48 | 49 | def linkedin_login(request): 50 | linkedin = LinkedIn(LINKEDIN_CONSUMER_KEY, LINKEDIN_CONSUMER_SECRET) 51 | request_token = linkedin.getRequestToken(callback=request.build_absolute_uri(reverse('socialauth_linkedin_login_done'))) 52 | request.session['linkedin_request_token'] = request_token 53 | signin_url = linkedin.getAuthorizeUrl(request_token) 54 | return HttpResponseRedirect(signin_url) 55 | 56 | def linkedin_login_done(request): 57 | request_token = request.session.get('linkedin_request_token', None) 58 | 59 | # If there is no request_token for session 60 | # Means we didn't redirect user to linkedin 61 | if not request_token: 62 | # Send them to the login page 63 | return HttpResponseRedirect(reverse("socialauth_login_page")) 64 | try: 65 | linkedin = LinkedIn(settings.LINKEDIN_CONSUMER_KEY, settings.LINKEDIN_CONSUMER_SECRET) 66 | verifier = request.GET.get('oauth_verifier', None) 67 | access_token = linkedin.getAccessToken(request_token,verifier) 68 | 69 | request.session['access_token'] = access_token 70 | user = authenticate(linkedin_access_token=access_token) 71 | except: 72 | user = None 73 | 74 | # if user is authenticated then login user 75 | if user: 76 | login(request, user) 77 | else: 78 | # We were not able to authenticate user 79 | # Redirect to login page 80 | del_dict_key(request.session, 'access_token') 81 | del_dict_key(request.session, 'request_token') 82 | return HttpResponseRedirect(reverse('socialauth_login_page')) 83 | 84 | # authentication was successful, user is now logged in 85 | return HttpResponseRedirect(LOGIN_REDIRECT_URL) 86 | 87 | def twitter_login(request): 88 | next = request.GET.get('next', None) 89 | if next: 90 | request.session['twitter_login_next'] = next 91 | 92 | twitter = oauthtwitter.TwitterOAuthClient(settings.TWITTER_CONSUMER_KEY, settings.TWITTER_CONSUMER_SECRET) 93 | request_token = twitter.fetch_request_token(callback=request.build_absolute_uri(reverse('socialauth_twitter_login_done'))) 94 | request.session['request_token'] = request_token.to_string() 95 | signin_url = twitter.authorize_token_url(request_token) 96 | return HttpResponseRedirect(signin_url) 97 | 98 | def twitter_login_done(request): 99 | request_token = request.session.get('request_token', None) 100 | verifier = request.GET.get('oauth_verifier', None) 101 | denied = request.GET.get('denied', None) 102 | 103 | # If we've been denied, put them back to the signin page 104 | # They probably meant to sign in with facebook >:D 105 | if denied: 106 | return HttpResponseRedirect(reverse("socialauth_login_page")) 107 | 108 | # If there is no request_token for session, 109 | # Means we didn't redirect user to twitter 110 | if not request_token: 111 | # Redirect the user to the login page, 112 | return HttpResponseRedirect(reverse("socialauth_login_page")) 113 | 114 | token = oauth.OAuthToken.from_string(request_token) 115 | 116 | # If the token from session and token from twitter does not match 117 | # means something bad happened to tokens 118 | if token.key != request.GET.get('oauth_token', 'no-token'): 119 | del_dict_key(request.session, 'request_token') 120 | # Redirect the user to the login page 121 | return HttpResponseRedirect(reverse("socialauth_login_page")) 122 | 123 | try: 124 | twitter = oauthtwitter.TwitterOAuthClient(settings.TWITTER_CONSUMER_KEY, settings.TWITTER_CONSUMER_SECRET) 125 | access_token = twitter.fetch_access_token(token, verifier) 126 | 127 | request.session['access_token'] = access_token.to_string() 128 | user = authenticate(twitter_access_token=access_token) 129 | except: 130 | user = None 131 | 132 | # if user is authenticated then login user 133 | if user: 134 | login(request, user) 135 | else: 136 | # We were not able to authenticate user 137 | # Redirect to login page 138 | del_dict_key(request.session, 'access_token') 139 | del_dict_key(request.session, 'request_token') 140 | return HttpResponseRedirect(reverse('socialauth_login_page')) 141 | 142 | # authentication was successful, use is now logged in 143 | next = request.session.get('twitter_login_next', None) 144 | if next: 145 | del_dict_key(request.session, 'twitter_login_next') 146 | return HttpResponseRedirect(next) 147 | else: 148 | return HttpResponseRedirect(LOGIN_REDIRECT_URL) 149 | 150 | def openid_login(request): 151 | if 'openid_next' in request.GET: 152 | request.session['openid_next'] = request.GET.get('openid_next') 153 | if 'openid_identifier' in request.GET: 154 | user_url = request.GET.get('openid_identifier') 155 | request.session['openid_provider'] = user_url 156 | return begin(request, user_url=user_url) 157 | else: 158 | request.session['openid_provider'] = 'Openid' 159 | return begin(request) 160 | 161 | def gmail_login(request): 162 | request.session['openid_provider'] = 'Google' 163 | return begin(request, user_url='https://www.google.com/accounts/o8/id') 164 | 165 | def gmail_login_complete(request): 166 | pass 167 | 168 | 169 | def yahoo_login(request): 170 | request.session['openid_provider'] = 'Yahoo' 171 | return begin(request, user_url='https://me.yahoo.com/') 172 | 173 | def openid_done(request, provider=None): 174 | """ 175 | When the request reaches here, the user has completed the Openid 176 | authentication flow. He has authorised us to login via Openid, so 177 | request.openid is populated. 178 | After coming here, we want to check if we are seeing this openid first time. 179 | If we are, we will create a new Django user for this Openid, else login the 180 | existing openid. 181 | """ 182 | 183 | if not provider: 184 | provider = request.session.get('openid_provider', '') 185 | if hasattr(request,'openid') and request.openid: 186 | #check for already existing associations 187 | openid_key = str(request.openid) 188 | 189 | #authenticate and login 190 | try: 191 | user = authenticate(openid_key=openid_key, request=request, provider=provider) 192 | except: 193 | user = None 194 | 195 | if user: 196 | login(request, user) 197 | if 'openid_next' in request.session : 198 | openid_next = request.session.get('openid_next') 199 | if len(openid_next.strip()) > 0 : 200 | return HttpResponseRedirect(openid_next) 201 | return HttpResponseRedirect(LOGIN_REDIRECT_URL) 202 | # redirect_url = reverse('socialauth_editprofile') 203 | # return HttpResponseRedirect(redirect_url) 204 | else: 205 | return HttpResponseRedirect(LOGIN_URL) 206 | else: 207 | return HttpResponseRedirect(LOGIN_URL) 208 | 209 | def facebook_login(request): 210 | """ 211 | Facebook login page 212 | """ 213 | next = request.GET.get('next', None) 214 | if next: 215 | request.session['facebook_login_next'] = next 216 | 217 | if request.REQUEST.get("device"): 218 | device = request.REQUEST.get("device") 219 | else: 220 | device = "user-agent" 221 | 222 | params = {} 223 | params["client_id"] = FACEBOOK_APP_ID 224 | params["redirect_uri"] = request.build_absolute_uri(reverse("socialauth_facebook_login_done")) 225 | 226 | url = "https://graph.facebook.com/oauth/authorize?"+urllib.urlencode(params) 227 | 228 | return HttpResponseRedirect(url) 229 | 230 | def facebook_login_done(request): 231 | user = authenticate(request=request) 232 | 233 | if not user: 234 | request.COOKIES.pop(FACEBOOK_API_KEY + '_session_key', None) 235 | request.COOKIES.pop(FACEBOOK_API_KEY + '_user', None) 236 | 237 | # TODO: maybe the project has its own login page? 238 | logging.debug("SOCIALAUTH: Couldn't authenticate user with Django, redirecting to Login page") 239 | return HttpResponseRedirect(reverse('socialauth_login_page')) 240 | 241 | login(request, user) 242 | 243 | logging.debug("SOCIALAUTH: Successfully logged in with Facebook!") 244 | 245 | next = request.GET.get('next') 246 | if not next: 247 | next = request.session.get('facebook_login_next') 248 | del_dict_key(request.session, 'facebook_login_next') 249 | 250 | if next: 251 | return HttpResponseRedirect(next) 252 | else: 253 | return HttpResponseRedirect(LOGIN_REDIRECT_URL) 254 | 255 | def openid_login_page(request): 256 | return render_to_response('openid/index.html', context_instance=RequestContext(request)) 257 | 258 | @login_required 259 | def signin_complete(request): 260 | return render_to_response('socialauth/signin_complete.html', context_instance=RequestContext(request)) 261 | 262 | @login_required 263 | def editprofile(request): 264 | if request.method == 'POST': 265 | edit_form = EditProfileForm(user=request.user, data=request.POST) 266 | if edit_form.is_valid(): 267 | user = edit_form.save() 268 | try: 269 | user.authmeta.is_profile_modified = True 270 | user.authmeta.save() 271 | except AuthMeta.DoesNotExist: 272 | pass 273 | if hasattr(user,'openidprofile_set') and user.openidprofile_set.count(): 274 | openid_profile = user.openidprofile_set.all()[0] 275 | openid_profile.is_valid_username = True 276 | openid_profile.save() 277 | try: 278 | #If there is a profile. notify that we have set the username 279 | profile = user.get_profile() 280 | profile.is_valid_username = True 281 | profile.save() 282 | except: 283 | pass 284 | request.user.message_set.create(message='Your profile has been updated.') 285 | return HttpResponseRedirect('.') 286 | if request.method == 'GET': 287 | edit_form = EditProfileForm(user=request.user) 288 | 289 | payload = {'edit_form':edit_form} 290 | return render_to_response('socialauth/editprofile.html', payload, RequestContext(request)) 291 | 292 | def social_logout(request): 293 | # Todo 294 | # still need to handle FB cookies, session etc. 295 | 296 | # let the openid_consumer app handle openid-related cleanup 297 | from openid_consumer.views import signout as oid_signout 298 | oid_signout(request) 299 | 300 | # normal logout 301 | logout_response = logout(request) 302 | 303 | if 'next' in request.GET: 304 | response = HttpResponseRedirect(request.GET.get('next')) 305 | elif getattr(settings, 'LOGOUT_REDIRECT_URL', None): 306 | response = HttpResponseRedirect(settings.LOGOUT_REDIRECT_URL) 307 | else: 308 | response = logout_response 309 | 310 | # Delete the facebook cookie 311 | response.delete_cookie("fbs_" + FACEBOOK_APP_ID) 312 | 313 | return response 314 | 315 | def github_login(request): 316 | github_client = GithubClient() 317 | authorize_url = github_client.get_authorize_url() 318 | return HttpResponseRedirect(authorize_url) 319 | 320 | def github_login_done(request): 321 | try: 322 | code = request.GET['code'] 323 | except: 324 | """Either github did not respond properly 325 | or someone is playing with this url""" 326 | return HttpResponseRedirect(LOGIN_URL) 327 | github_client = GithubClient() 328 | access_token = github_client.get_access_token(code) 329 | try: 330 | user = authenticate(github_access_token=access_token) 331 | except: 332 | user = None 333 | if user: 334 | login(request, user) 335 | return HttpResponseRedirect(LOGIN_REDIRECT_URL) 336 | return HttpResponseRedirect(LOGIN_URL) 337 | 338 | def foursquare_login(request): 339 | foursquare_client = foursquare.FourSquareClient() 340 | return HttpResponseRedirect(foursquare_client.get_authentication_url()) 341 | 342 | def foursquare_login_done(request): 343 | try: 344 | code = request.GET.get('code') 345 | except: 346 | """Some error ocurred. 347 | Rediect to login page""" 348 | return HttpResponseRedirect(LOGIN_URL) 349 | request.session['foursquare_code'] = code 350 | foursquare_client = foursquare.FourSquareClient() 351 | access_token_response = foursquare_client.get_access_token(request.session['foursquare_code']) 352 | import json 353 | access_token = json.loads(access_token_response)['access_token'] 354 | try: 355 | user = authenticate(foursquare_access_token=access_token) 356 | except: 357 | user=None 358 | if user: 359 | login(request, user) 360 | return HttpResponseRedirect(LOGIN_REDIRECT_URL) 361 | return HttpResponseRedirect(LOGIN_URL) 362 | --------------------------------------------------------------------------------