From: Stas Kravets Date: Fri, 9 Mar 2012 09:01:31 +0000 (+0400) Subject: Merge remote-tracking branch 'upstream/master' X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=c4b7e717d1840f0345d37c4ed8969b737ebf57b4;p=django-social-auth.git Merge remote-tracking branch 'upstream/master' Conflicts: README.rst doc/backends/google.rst doc/configuration.rst doc/miscellaneous.rst example/app/pipeline.py example/app/views.py example/local_settings.py.template example/settings.py example/templates/home.html social_auth/__init__.py social_auth/backends/__init__.py social_auth/backends/browserid.py social_auth/backends/contrib/github.py social_auth/backends/contrib/instagram.py social_auth/backends/facebook.py social_auth/backends/google.py social_auth/backends/pipeline/misc.py social_auth/backends/twitter.py social_auth/tests/google.py social_auth/utils.py social_auth/views.py --- c4b7e717d1840f0345d37c4ed8969b737ebf57b4 diff --cc README.rst index e7d1cfb,23eb502..2d167e1 --- a/README.rst +++ b/README.rst @@@ -894,9 -917,10 +920,9 @@@ Some particular use cases are listed be Miscellaneous ------------- - Join to django-social-auth_ community on Convore_ and bring any questions or - suggestions that will improve this app. - + Join to `django-social-auth discussion list`_ and bring any questions or suggestions + that would improve this application. Convore_ discussion group is deprecated since + the service is going to be shut down on April 1st. - If defining a custom user model, do not import social_auth from any models.py that would finally import from the models.py that defines your User class or it will make your project fail with a recursive import because social_auth uses diff --cc doc/backends/google.rst index c73cc7e,eef9676..65d3b1f --- a/doc/backends/google.rst +++ b/doc/backends/google.rst @@@ -65,16 -73,20 +73,30 @@@ To enable OAuth2 support Check which applications can be included in their `Google Data Protocol Directory`_ + Google OpenID + ------------- + + Configurable settings: + + - Supply a list of Google Apps account domain strings to be checked:: + + GOOGLE_WHITE_LISTED_DOMAINS = ['mygoogleappsdomain.com'] + + - Supply a list of Google Apps or Gmail email strings to be checked:: + + GOOGLE_WHITE_LISTED_EMAILS = ['me@mygoogleappsdomain.com', 'you@gmail.com'] + + +Google OpenID +------------- + +Configurable settings: + +- Supply a list of domain strings to be checked. The default (empty list) allows all domains. If a list is provided and a user attempts to sign in with a Google account that is not in the list, then a ValueError will be raised and the user will be redirected to your login error page:: + + GOOGLE_WHITE_LISTED_DOMAINS = ['mydomain.com'] + + Orkut ----- diff --cc doc/configuration.rst index 3b01620,01e9204..0b44880 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@@ -243,14 -229,54 +229,62 @@@ Configuratio Defaults to ``LOGIN_ERROR_URL``. + - The application catches any exception and logs errors to ``logger`` or + ``django.contrib.messagess`` application by default. But it's possible to + override the default behavior by defining a function to process the + exceptions using this setting:: + + SOCIAL_AUTH_PROCESS_EXCEPTIONS = 'social_auth.utils.process_exceptions' + + The function parameters will ``request`` holding the current request object, + ``backend`` with the current backend and ``err`` which is the exception + instance. + + Recently this set of exceptions were introduce to describe the situations + a bit more than the old ``ValueError`` usually raised:: + + AuthException - Base exception class + AuthFailed - Authentication failed for some reason + AuthCanceled - Authentication was canceled by the user + AuthUnknownError - An unknown error stoped the authentication + process + AuthTokenError - Unauthorized or access token error, it was + invalid, impossible to authenticate or user + removed permissions to it. + AuthMissingParameter - A needed parameter to continue the process was + missing, usually raised by the services that + need some POST data like myOpenID + + These are a subclass of ``ValueError`` to keep backward compatibility. + + Having tracebacks is really useful when debugging, for that purpose this + setting was defined:: + + SOCIAL_AUTH_RAISE_EXCEPTIONS = DEBUG + + It's default value is ``DEBUG``, so you need to set it to ``False`` to avoid + tracebacks when ``DEBUG = True``. + + + Some settings can be tweak by backend by adding the backend name prefix (all + uppercase and replace ``-`` with ``_``), here's the supported settings so far:: + + LOGIN_ERROR_URL + SOCIAL_AUTH_BACKEND_ERROR_URL + SOCIAL_AUTH_NEW_ASSOCIATION_REDIRECT_URL + SOCIAL_AUTH_DISCONNECT_REDIRECT_URL + SOCIAL_AUTH_NEW_USER_REDIRECT_URL + SOCIAL_AUTH_LOGIN_REDIRECT_URL + SOCIAL_AUTH_INACTIVE_USER_URL + +- The app catches any exception and logs errors to ``logger`` or + ``django.contrib.messagess`` app. Having tracebacks is really useful when + debugging, for that purpose this setting was defined:: + + SOCIAL_AUTH_RAISE_EXCEPTIONS = DEBUG + + It's default value is ``DEBUG``, so you need to set it to ``False`` to avoid + tracebacks when ``DEBUG = True``. .. _Model Manager: http://docs.djangoproject.com/en/dev/topics/db/managers/#managers .. _Login URL: http://docs.djangoproject.com/en/dev/ref/settings/?from=olddocs#login-url diff --cc social_auth/backends/facebook.py index 92df670,9943e47..b8dcaa9 --- a/social_auth/backends/facebook.py +++ b/social_auth/backends/facebook.py @@@ -13,11 -13,7 +13,11 @@@ field, check OAuthBackend class for det """ import cgi from urllib import urlencode - from urllib2 import urlopen + from urllib2 import urlopen, HTTPError +import base64 +import hmac +import hashlib +import time from django.utils import simplejson from django.contrib.auth import authenticate @@@ -79,46 -82,37 +86,55 @@@ class FacebookAuth(BaseOAuth2) def auth_complete(self, *args, **kwargs): """Completes loging process, must return user instance""" - if 'code' not in self.data: - if self.data.get('error') == 'access_denied': - raise AuthCanceled(self) - else: - raise AuthException(self) + access_token = None + expires = None + + if 'code' in self.data: - url = 'https://graph.facebook.com/oauth/access_token?' + \ - urlencode({'client_id': setting('FACEBOOK_APP_ID'), - 'redirect_uri': self.redirect_uri, - 'client_secret': setting('FACEBOOK_API_SECRET'), - 'code': self.data['code']}) - response = cgi.parse_qs(urlopen(url).read()) ++ url = ACCESS_TOKEN + urlencode({ ++ 'client_id': setting('FACEBOOK_APP_ID'), ++ 'redirect_uri': self.redirect_uri, ++ 'client_secret': setting('FACEBOOK_API_SECRET'), ++ 'code': self.data['code'] ++ }) ++ try: ++ response = cgi.parse_qs(urlopen(url).read()) ++ except HTTPError: ++ raise AuthFailed(self, 'There was an error authenticating the app') ++ + access_token = response['access_token'][0] + if 'expires' in response: + expires = response['expires'][0] - url = ACCESS_TOKEN + urlencode({ - 'client_id': setting('FACEBOOK_APP_ID'), - 'redirect_uri': self.redirect_uri, - 'client_secret': setting('FACEBOOK_API_SECRET'), - 'code': self.data['code'] - }) - try: - response = cgi.parse_qs(urlopen(url).read()) - except HTTPError: - raise AuthFailed(self, 'There was an error authenticating the app') + if 'signed_request' in self.data: + response = load_signed_request(self.data.get('signed_request')) - access_token = response['access_token'][0] - data = self.user_data(access_token) + if response is not None: + access_token = response.get('access_token') or response.get('oauth_token') \ + or self.data.get('access_token') - if data is not None: - data['access_token'] = access_token - # expires will not be part of response if offline access - # premission was requested - if 'expires' in response: - data['expires'] = response['expires'][0] + if 'expires' in response: + expires = response['expires'] + + if access_token: + data = self.user_data(access_token) + - kwargs.update({'auth': self, - 'response': data, - self.AUTH_BACKEND.name: True}) - return authenticate(*args, **kwargs) + if data is not None: - if 'error' in data: - error = self.data.get('error') or 'unknown error' - raise ValueError('Authentication error: %s' % error) + data['access_token'] = access_token + # expires will not be part of response if offline access - # premission was requested ++ # premission was requested + if expires: - data['expires'] = expires - kwargs.update({'response': data, self.AUTH_BACKEND.name: True}) ++ data['expires'] = response['expires'][0] ++ ++ kwargs.update({'auth': self, ++ 'response': data, ++ self.AUTH_BACKEND.name: True}) ++ + return authenticate(*args, **kwargs) + else: - error = self.data.get('error') or 'unknown error' - raise ValueError('Authentication error: %s' % error) ++ if self.data.get('error') == 'access_denied': ++ raise AuthCanceled(self) ++ else: ++ raise AuthException(self) @classmethod def enabled(cls): diff --cc social_auth/backends/google.py index 88896ab,c596c49..24e9083 --- a/social_auth/backends/google.py +++ b/social_auth/backends/google.py @@@ -80,11 -82,8 +82,11 @@@ class GoogleBackend(OpenIDBackend) is unique enought to flag a single user. Email comes from schema: http://axschema.org/contact/email """ - # White listed domains (accepts all if list is empty) - domains = setting('GOOGLE_WHITE_LISTED_DOMAINS', []) + validate_whitelists(self, details['email']) + + if domains and details['email'].split('@', 1)[1] not in domains: + raise ValueError('Domain not allowed') + return details['email']