[Python-modules-commits] [python-social-auth] 05/32: Added an OAuth2 backend for Bitbucket
Wolfgang Borgert
debacle at moszumanska.debian.org
Sat Dec 24 15:13:45 UTC 2016
This is an automated email from the git hooks/post-receive script.
debacle pushed a commit to tag v0.2.12
in repository python-social-auth.
commit 2853cfb2ff11cd3d149ba563ad63fc3a948df254
Author: Mark Adams <mark at markadams.me>
Date: Sun Jun 21 15:06:45 2015 -0500
Added an OAuth2 backend for Bitbucket
---
docs/backends/bitbucket.rst | 37 ++++++----
social/backends/bitbucket.py | 74 +++++++++++++++----
social/backends/oauth.py | 8 +++
social/tests/backends/test_bitbucket.py | 121 ++++++++++++++++++++++++--------
4 files changed, 184 insertions(+), 56 deletions(-)
diff --git a/docs/backends/bitbucket.rst b/docs/backends/bitbucket.rst
index a66478b..00809a3 100644
--- a/docs/backends/bitbucket.rst
+++ b/docs/backends/bitbucket.rst
@@ -1,26 +1,37 @@
Bitbucket
=========
-Bitbucket works similar to Twitter OAuth.
+Bitbucket supports both OAuth2 and OAuth1 logins.
-- Register a new application by emailing ``support at bitbucket.org`` with an
- application name and a bit of a description,
+1. Register a new OAuth Consumer by following the instructions in the
+ Bitbucket documentation: `OAuth on Bitbucket`_
+
+ Note: For OAuth2, your consumer MUST have the "account" scope otherwise
+ the user profile information (username, name, etc.) won't be accessible.
+
+2. Configure the appropriate settings for OAuth2 or OAuth1 (see below).
+
+OAuth2
+------
- Fill ``Consumer Key`` and ``Consumer Secret`` values in the settings::
- SOCIAL_AUTH_BITBUCKET_KEY = ''
- SOCIAL_AUTH_BITBUCKET_SECRET = ''
+ SOCIAL_AUTH_BITBUCKET_OAUTH2_KEY = '<your-consumer-key>'
+ SOCIAL_AUTH_BITBUCKET_OAUTH2_SECRET = '<your-consumer-secret>'
+- If you would like to restrict access to only users with verified e-mail
+ addresses, set ``SOCIAL_AUTH_BITBUCKET_OAUTH2_VERIFIED_EMAILS_ONLY = True``
+OAuth1
+------
-Settings
---------
+- OAuth1 works similarly to OAuth2, but you must fill in the following settings
+ instead::
-Sometimes Bitbucket users don't have a verified email address, making it
-impossible to get the basic user information to continue the auth process.
-It's possible to avoid these users with this setting::
+ SOCIAL_AUTH_BITBUCKET_KEY = '<your-consumer-key>'
+ SOCIAL_AUTH_BITBUCKET_SECRET = '<your-consumer-secret>'
- SOCIAL_AUTH_BITBUCKET_VERIFIED_EMAILS_ONLY = True
+- If you would like to restrict access to only users with verified e-mail
+ addresses, set ``SOCIAL_AUTH_BITBUCKET_VERIFIED_EMAILS_ONLY = True``
-By default the setting is set to ``False`` since it's possible for a project to
-gather this information by other methods.
+.. _OAuth on Bitbucket: https://confluence.atlassian.com/display/BITBUCKET/OAuth+on+Bitbucket
diff --git a/social/backends/bitbucket.py b/social/backends/bitbucket.py
index c057465..fd63880 100644
--- a/social/backends/bitbucket.py
+++ b/social/backends/bitbucket.py
@@ -1,18 +1,12 @@
"""
-Bitbucket OAuth1 backend, docs at:
+Bitbucket OAuth2 and OAuth1 backends, docs at:
http://psa.matiasaguirre.net/docs/backends/bitbucket.html
"""
from social.exceptions import AuthForbidden
-from social.backends.oauth import BaseOAuth1
+from social.backends.oauth import BaseOAuth1, BaseOAuth2
-class BitbucketOAuth(BaseOAuth1):
- """Bitbucket OAuth authentication backend"""
- name = 'bitbucket'
- AUTHORIZATION_URL = 'https://bitbucket.org/api/1.0/oauth/authenticate'
- REQUEST_TOKEN_URL = 'https://bitbucket.org/api/1.0/oauth/request_token'
- ACCESS_TOKEN_URL = 'https://bitbucket.org/api/1.0/oauth/access_token'
-
+class BitbucketOAuthBase(object):
# Bitbucket usernames can change. The account ID should always be the UUID
# See: https://confluence.atlassian.com/display/BITBUCKET/Use+the+Bitbucket+REST+APIs
ID_KEY = 'uuid'
@@ -27,10 +21,9 @@ class BitbucketOAuth(BaseOAuth1):
'first_name': first_name,
'last_name': last_name}
- def user_data(self, access_token):
+ def user_data(self, access_token, *args, **kwargs):
"""Return user data provided"""
- emails = self.get_json('https://api.bitbucket.org/2.0/user/emails',
- auth=self.oauth_auth(access_token))
+ emails = self._get_emails(access_token)
email = None
@@ -44,10 +37,63 @@ class BitbucketOAuth(BaseOAuth1):
self, 'Bitbucket account has no verified email'
)
- user = self.get_json('https://api.bitbucket.org/2.0/user',
- auth=self.oauth_auth(access_token))
+ user = self._get_user(access_token)
if email:
user['email'] = email
return user
+
+ def _get_user(self, access_token=None):
+ raise NotImplementedError
+
+ def _get_emails(self, access_token=None):
+ raise NotImplementedError
+
+
+class BitbucketOAuth2(BitbucketOAuthBase, BaseOAuth2):
+ name = 'bitbucket-oauth2'
+ SCOPE_SEPARATOR = ' '
+ AUTHORIZATION_URL = 'https://bitbucket.org/site/oauth2/authorize'
+ ACCESS_TOKEN_URL = 'https://bitbucket.org/site/oauth2/access_token'
+ ACCESS_TOKEN_METHOD = 'POST'
+ REDIRECT_STATE = False
+ EXTRA_DATA = [
+ ('scopes', 'scopes'),
+ ('expires_in', 'expires'),
+ ('token_type', 'token_type'),
+ ('refresh_token', 'refresh_token')
+ ]
+
+ def auth_complete_credentials(self):
+ return self.get_key_and_secret()
+
+ def _get_user(self, access_token=None):
+ return self.get_json('https://api.bitbucket.org/2.0/user',
+ params={'access_token': access_token})
+
+ def _get_emails(self, access_token=None):
+ return self.get_json('https://api.bitbucket.org/2.0/user/emails',
+ params={'access_token': access_token})
+
+ def refresh_token(self, *args, **kwargs):
+ raise NotImplementedError('Refresh tokens for Bitbucket have not been implemented')
+
+
+class BitbucketOAuth(BitbucketOAuthBase, BaseOAuth1):
+ """Bitbucket OAuth authentication backend"""
+ name = 'bitbucket'
+ AUTHORIZATION_URL = 'https://bitbucket.org/api/1.0/oauth/authenticate'
+ REQUEST_TOKEN_URL = 'https://bitbucket.org/api/1.0/oauth/request_token'
+ ACCESS_TOKEN_URL = 'https://bitbucket.org/api/1.0/oauth/access_token'
+
+ def oauth_auth(self, *args, **kwargs):
+ return super(BitbucketOAuth, self).oauth_auth(*args, **kwargs)
+
+ def _get_user(self, access_token=None):
+ return self.get_json('https://api.bitbucket.org/2.0/user',
+ auth=self.oauth_auth(access_token))
+
+ def _get_emails(self, access_token=None):
+ return self.get_json('https://api.bitbucket.org/2.0/user/emails',
+ auth=self.oauth_auth(access_token))
diff --git a/social/backends/oauth.py b/social/backends/oauth.py
index d154606..0396495 100644
--- a/social/backends/oauth.py
+++ b/social/backends/oauth.py
@@ -350,6 +350,9 @@ class BaseOAuth2(OAuthAuth):
'redirect_uri': self.get_redirect_uri(state)
}
+ def auth_complete_credentials(self):
+ return None
+
def auth_headers(self):
return {'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'}
@@ -371,12 +374,15 @@ class BaseOAuth2(OAuthAuth):
"""Completes loging process, must return user instance"""
state = self.validate_state()
self.process_error(self.data)
+
response = self.request_access_token(
self.access_token_url(),
data=self.auth_complete_params(state),
headers=self.auth_headers(),
+ auth=self.auth_complete_credentials(),
method=self.ACCESS_TOKEN_METHOD
)
+ print(dict(response))
self.process_error(response)
return self.do_auth(response['access_token'], response=response,
*args, **kwargs)
@@ -384,6 +390,8 @@ class BaseOAuth2(OAuthAuth):
@handle_http_errors
def do_auth(self, access_token, *args, **kwargs):
"""Finish the auth process once the access_token was retrieved"""
+ print(args)
+ print(kwargs)
data = self.user_data(access_token, *args, **kwargs)
response = kwargs.get('response') or {}
response.update(data or {})
diff --git a/social/tests/backends/test_bitbucket.py b/social/tests/backends/test_bitbucket.py
index 0c0f912..400ea5f 100644
--- a/social/tests/backends/test_bitbucket.py
+++ b/social/tests/backends/test_bitbucket.py
@@ -4,22 +4,32 @@ from httpretty import HTTPretty
from social.p3 import urlencode
from social.exceptions import AuthForbidden
-from social.tests.backends.oauth import OAuth1Test
+from social.tests.backends.oauth import OAuth1Test, OAuth2Test
-class BitbucketOAuth1Test(OAuth1Test):
- backend_path = 'social.backends.bitbucket.BitbucketOAuth'
+class BitbucketOAuthMixin(object):
user_data_url = 'https://api.bitbucket.org/2.0/user'
expected_username = 'foobar'
- access_token_body = json.dumps({
- 'access_token': 'foobar',
- 'token_type': 'bearer'
- })
- request_token_body = urlencode({
- 'oauth_token_secret': 'foobar-secret',
- 'oauth_token': 'foobar',
- 'oauth_callback_confirmed': 'true'
+ bb_api_user_emails = 'https://api.bitbucket.org/2.0/user/emails'
+
+ user_data_body = json.dumps({
+ u'created_on': u'2012-03-29T18:07:38+00:00',
+ u'display_name': u'Foo Bar',
+ u'links': {
+ u'avatar': {u'href': u'https://bitbucket.org/account/foobar/avatar/32/'},
+ u'followers': {u'href': u'https://api.bitbucket.org/2.0/users/foobar/followers'},
+ u'following': {u'href': u'https://api.bitbucket.org/2.0/users/foobar/following'},
+ u'hooks': {u'href': u'https://api.bitbucket.org/2.0/users/foobar/hooks'},
+ u'html': {u'href': u'https://bitbucket.org/foobar'},
+ u'repositories': {u'href': u'https://api.bitbucket.org/2.0/repositories/foobar'},
+ u'self': {u'href': u'https://api.bitbucket.org/2.0/users/foobar'}},
+ u'location': u'Fooville, Bar',
+ u'type': u'user',
+ u'username': u'foobar',
+ u'uuid': u'{397621dc-0f78-329f-8d6d-727396248e3f}',
+ u'website': u'http://foobar.com'
})
+
emails_body = json.dumps({
u'page': 1,
u'pagelen': 10,
@@ -41,33 +51,31 @@ class BitbucketOAuth1Test(OAuth1Test):
}
]
})
- user_data_body = json.dumps({
- u'created_on': u'2012-03-29T18:07:38+00:00',
- u'display_name': u'Foo Bar',
- u'links': {
- u'avatar': {u'href': u'https://bitbucket.org/account/foobar/avatar/32/'},
- u'followers': {u'href': u'https://api.bitbucket.org/2.0/users/foobar/followers'},
- u'following': {u'href': u'https://api.bitbucket.org/2.0/users/foobar/following'},
- u'hooks': {u'href': u'https://api.bitbucket.org/2.0/users/foobar/hooks'},
- u'html': {u'href': u'https://bitbucket.org/foobar'},
- u'repositories': {u'href': u'https://api.bitbucket.org/2.0/repositories/foobar'},
- u'self': {u'href': u'https://api.bitbucket.org/2.0/users/foobar'}},
- u'location': u'Fooville, Bar',
- u'type': u'user',
- u'username': u'foobar',
- u'uuid': u'{397621dc-0f78-329f-8d6d-727396248e3f}',
- u'website': u'http://foobar.com'
+
+
+class BitbucketOAuth1Test(BitbucketOAuthMixin, OAuth1Test):
+ backend_path = 'social.backends.bitbucket.BitbucketOAuth'
+
+ request_token_body = urlencode({
+ 'oauth_token_secret': 'foobar-secret',
+ 'oauth_token': 'foobar',
+ 'oauth_callback_confirmed': 'true'
+ })
+
+ access_token_body = json.dumps({
+ 'access_token': 'foobar',
+ 'token_type': 'bearer'
})
def test_login(self):
HTTPretty.register_uri(HTTPretty.GET,
- 'https://api.bitbucket.org/2.0/user/emails',
+ self.bb_api_user_emails,
status=200, body=self.emails_body)
self.do_login()
def test_partial_pipeline(self):
HTTPretty.register_uri(HTTPretty.GET,
- 'https://api.bitbucket.org/2.0/user/emails',
+ self.bb_api_user_emails,
status=200, body=self.emails_body)
self.do_partial_pipeline()
@@ -101,3 +109,58 @@ class BitbucketOAuth1FailTest(BitbucketOAuth1Test):
})
with self.assertRaises(AuthForbidden):
super(BitbucketOAuth1FailTest, self).test_partial_pipeline()
+
+
+class BitbucketOAuth2Test(BitbucketOAuthMixin, OAuth2Test):
+ backend_path = 'social.backends.bitbucket.BitbucketOAuth2'
+
+ access_token_body = json.dumps({
+ 'access_token': 'foobar_access',
+ 'scopes': 'foo_scope',
+ 'expires_in': 3600,
+ 'refresh_token': 'foobar_refresh',
+ 'token_type': 'bearer'
+ })
+
+ def test_login(self):
+ HTTPretty.register_uri(HTTPretty.GET,
+ self.bb_api_user_emails,
+ status=200, body=self.emails_body)
+ self.do_login()
+
+ def test_partial_pipeline(self):
+ HTTPretty.register_uri(HTTPretty.GET,
+ self.bb_api_user_emails,
+ status=200, body=self.emails_body)
+ self.do_partial_pipeline()
+
+
+class BitbucketOAuth2FailTest(BitbucketOAuth2Test):
+ emails_body = json.dumps({
+ u'page': 1,
+ u'pagelen': 10,
+ u'size': 1,
+ u'values': [
+ {
+ u'email': u'foo at bar.com',
+ u'is_confirmed': False,
+ u'is_primary': True,
+ u'links': { u'self': {u'href': u'https://api.bitbucket.org/2.0/user/emails/foo@bar.com'}},
+ u'type': u'email'
+ }
+ ]
+ })
+
+ def test_login(self):
+ self.strategy.set_settings({
+ 'SOCIAL_AUTH_BITBUCKET_OAUTH2_VERIFIED_EMAILS_ONLY': True
+ })
+ with self.assertRaises(AuthForbidden):
+ super(BitbucketOAuth2FailTest, self).test_login()
+
+ def test_partial_pipeline(self):
+ self.strategy.set_settings({
+ 'SOCIAL_AUTH_BITBUCKET_OAUTH2_VERIFIED_EMAILS_ONLY': True
+ })
+ with self.assertRaises(AuthForbidden):
+ super(BitbucketOAuth2FailTest, self).test_partial_pipeline()
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-social-auth.git
More information about the Python-modules-commits
mailing list