]> git.parisson.com Git - django-social-auth.git/commitdiff
Added Bitbucket backend
authorThomas Whitton <oracal@oracal-EP45-UD3LR.(none)>
Mon, 16 Apr 2012 19:38:35 +0000 (20:38 +0100)
committerThomas Whitton <oracal@oracal-EP45-UD3LR.(none)>
Mon, 16 Apr 2012 19:38:35 +0000 (20:38 +0100)
README.rst
social_auth/backends/contrib/bitbucket.py [new file with mode: 0644]

index 93afa6b9da8b2903f0e2859aee34def7132f0792..7cf60feeac1b5eab591fb456d91e9184c7197114 100644 (file)
@@ -119,6 +119,7 @@ Configuration
         'social_auth.backends.contrib.flickr.FlickrBackend',
         'social_auth.backends.contrib.instagram.InstagramBackend',
         'social_auth.backends.OpenIDBackend',
+        'social_auth.backends.contrib.bitbucket.BitbucketBackend',
         'django.contrib.auth.backends.ModelBackend',
     )
 
@@ -152,6 +153,8 @@ Configuration
     FLICKR_API_SECRET            = ''
     INSTAGRAM_CLIENT_ID          = ''
     INSTAGRAM_CLIENT_SECRET      = ''
+    BITBUCKET_CONSUMER_KEY       = ''
+    BITBUCKET_CONSUMER_SECRET    = ''
 
 - Setup login URLs::
 
@@ -821,6 +824,17 @@ GitHub works similar to Facebook (OAuth).
 - Also it's possible to define extra permissions with::
 
      GITHUB_EXTENDED_PERMISSIONS = [...]
+     
+Bitbucket
+^^^^^^^^^
+Bitbucket works similar to Twitter (OAuth).
+
+- Register a new application by emailing support@bitbucket.org with an application name and a bit of a description,
+
+- Fill ``Consumer Key`` and ``Consumer Secret`` values in the settings::
+
+      BITBUCKET_CONSUMER_KEY = ''
+      BITBUCKET_CONSUMER_SECRET = ''
 
 Dropbox
 ^^^^^^^
diff --git a/social_auth/backends/contrib/bitbucket.py b/social_auth/backends/contrib/bitbucket.py
new file mode 100644 (file)
index 0000000..69d4342
--- /dev/null
@@ -0,0 +1,99 @@
+"""
+Bitbucket OAuth support.
+
+This adds support for Bitbucket OAuth service. An application must
+be registered first on Bitbucket and the settings BITBUCKET_CONSUMER_KEY
+and BITBUCKET_CONSUMER_SECRET must be defined with the corresponding
+values.
+
+By default username, email, token expiration time, first name and last name are stored in extra_data 
+field, check OAuthBackend class for details on how to extend it.
+"""
+from django.utils import simplejson
+from social_auth.backends import ConsumerBasedOAuth, OAuthBackend, USERNAME
+from urllib import urlopen
+from social_auth.utils import setting
+
+# Bitbucket configuration
+BITBUCKET_SERVER = 'bitbucket.org/api/1.0'
+BITBUCKET_REQUEST_TOKEN_URL = 'https://%s/oauth/request_token' % BITBUCKET_SERVER
+BITBUCKET_ACCESS_TOKEN_URL = 'https://%s/oauth/access_token' % BITBUCKET_SERVER
+BITBUCKET_AUTHORIZATION_URL = 'https://%s/oauth/authenticate' % BITBUCKET_SERVER
+BITBUCKET_EMAIL_DATA_URL = 'https://%s/emails/' % BITBUCKET_SERVER
+BITBUCKET_USER_DATA_URL = 'https://%s/users/' % BITBUCKET_SERVER
+
+class BitbucketBackend(OAuthBackend):
+    """Bitbucket OAuth authentication backend"""
+    name = 'bitbucket'
+    EXTRA_DATA = [
+    ('username', 'username'),
+    ('expires', setting('SOCIAL_AUTH_EXPIRATION', 'expires')),
+    ('email', 'email'),
+    ('first_name', 'first_name'),
+    ('last_name', 'last_name')
+    ]
+
+    def get_user_details(self, response):
+        """Return user details from Bitbucket account"""
+        return {USERNAME: response.get('username'),
+                'email': response.get('email'),
+                'fullname': ' '.join((response.get('first_name'), response.get('last_name'))),
+                'first_name': response.get('first_name'),
+                'last_name': response.get('last_name')}    
+        
+    def get_user_id(self, details, response):
+        """Return the user id, Bitbucket only provides username as a unique identifier"""
+        return response['username']
+
+    @classmethod
+    def tokens(cls, instance):
+        """Return the tokens needed to authenticate the access to any API the
+        service might provide. Bitbucket uses a pair of OAuthToken consisting on
+        a oauth_token and oauth_token_secret.
+
+        instance must be a UserSocialAuth instance.
+        """
+        token = super(BitbucketBackend, cls).tokens(instance)
+        if token and 'access_token' in token:
+            token = dict(tok.split('=')
+                            for tok in token['access_token'].split('&'))
+        return token
+
+
+class BitbucketAuth(ConsumerBasedOAuth):
+    """Bitbucket OAuth authentication mechanism"""
+    AUTHORIZATION_URL = BITBUCKET_AUTHORIZATION_URL
+    REQUEST_TOKEN_URL = BITBUCKET_REQUEST_TOKEN_URL
+    ACCESS_TOKEN_URL = BITBUCKET_ACCESS_TOKEN_URL
+    SERVER_URL = BITBUCKET_SERVER
+    AUTH_BACKEND = BitbucketBackend
+    SETTINGS_KEY_NAME = 'BITBUCKET_CONSUMER_KEY'
+    SETTINGS_SECRET_NAME = 'BITBUCKET_CONSUMER_SECRET'
+
+    def user_data(self, access_token):
+        """Return user data provided"""
+        # Bitbucket has a bit of an indirect route to obtain user data from an authenticated query:
+        # First obtain the user's email via an authenticated GET
+        url = BITBUCKET_EMAIL_DATA_URL
+        request = self.oauth_request(access_token, url)
+        response = self.fetch_response(request)
+        try:
+            email = simplejson.loads(response)[0]['email']
+            # Then return the user data using a normal GET with the BITBUCKET_USER_DATA_URL and the user's email
+            response = urlopen(BITBUCKET_USER_DATA_URL+email)
+            user_details = simplejson.load(response)['user']
+            user_details['email']=email
+            return user_details
+        except ValueError:
+            return None
+        return None
+    
+    @classmethod
+    def enabled(cls):
+        """Return backend enabled status by checking basic settings"""
+        return setting('BITBUCKET_CONSUMER_KEY') and setting('BITBUCKET_CONSUMER_SECRET')
+
+# Backend definition
+BACKENDS = {
+    'bitbucket': BitbucketAuth,
+}