From b615a5956644aa5f67b04818bc6ad1dfdbd8f87d Mon Sep 17 00:00:00 2001 From: Sam Kuehn Date: Sun, 8 Sep 2013 11:53:29 -0600 Subject: [PATCH 1/5] Ignore .DS_Store files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 457da65c8..bf45b883b 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ local_settings.py sessions/ _build/ fabfile.py + +.DS_Store \ No newline at end of file From a2d615c90da676e4bd2c94cb295907372838f4da Mon Sep 17 00:00:00 2001 From: Sam Kuehn Date: Sun, 8 Sep 2013 14:55:37 -0600 Subject: [PATCH 2/5] Add box.net support --- docs/backends/box.rst | 24 +++++++ examples/django_example/dj/settings.py | 1 + .../django_example/dj/templates/home.html | 1 + examples/flask_example/settings.py | 1 + examples/flask_example/templates/home.html | 1 + examples/webpy_example/app.py | 1 + examples/webpy_example/templates/home.html | 1 + social/backends/box.py | 68 +++++++++++++++++++ tests/backends/box_test.py | 66 ++++++++++++++++++ 9 files changed, 164 insertions(+) create mode 100644 docs/backends/box.rst create mode 100644 social/backends/box.py create mode 100644 tests/backends/box_test.py diff --git a/docs/backends/box.rst b/docs/backends/box.rst new file mode 100644 index 000000000..d9de6580c --- /dev/null +++ b/docs/backends/box.rst @@ -0,0 +1,24 @@ +Box.net +====== + +Box works similar to Facebook (OAuth2). + +- Register an application at `Manage Box Applications`_ + +- Fill the **Consumer Key** and **Consumer Secret** values in your settings:: + + SOCIAL_AUTH_BOX_KEY = '' + SOCIAL_AUTH_BOX_SECRET = '' + +- By default the token is not permanent, it will last an hour:: + + To refresh the access token just do:: + + from social.apps.django_app.utils import load_strategy + + strategy = load_strategy(backend='box') + user = User.objects.get(pk=foo) + social = user.social_auth.filter(provider='box')[0] + social.refresh_token(strategy=strategy) + +.. _Manage Box Applications: https://app.box.com/developers/services diff --git a/examples/django_example/dj/settings.py b/examples/django_example/dj/settings.py index 47b9dc1e4..da78f74e0 100644 --- a/examples/django_example/dj/settings.py +++ b/examples/django_example/dj/settings.py @@ -131,6 +131,7 @@ 'social.backends.angel.AngelOAuth2', 'social.backends.behance.BehanceOAuth2', 'social.backends.bitbucket.BitbucketOAuth', + 'social.backends.box.BoxOAuth2', 'social.backends.linkedin.LinkedinOAuth', 'social.backends.linkedin.LinkedinOAuth2', 'social.backends.github.GithubOAuth2', diff --git a/examples/django_example/dj/templates/home.html b/examples/django_example/dj/templates/home.html index 96333e60c..5e68b0f33 100644 --- a/examples/django_example/dj/templates/home.html +++ b/examples/django_example/dj/templates/home.html @@ -14,6 +14,7 @@ Angel OAuth2
Behance OAuth2
Bitbucket OAuth
+Box.net OAuth2
LinkedIn OAuth
Github OAuth2
Foursquare OAuth2
diff --git a/examples/flask_example/settings.py b/examples/flask_example/settings.py index 54c118773..fc88e0278 100644 --- a/examples/flask_example/settings.py +++ b/examples/flask_example/settings.py @@ -28,6 +28,7 @@ 'social.backends.angel.AngelOAuth2', 'social.backends.behance.BehanceOAuth2', 'social.backends.bitbucket.BitbucketOAuth', + 'social.backends.box.BoxOAuth2', 'social.backends.linkedin.LinkedinOAuth', 'social.backends.github.GithubOAuth2', 'social.backends.foursquare.FoursquareOAuth2', diff --git a/examples/flask_example/templates/home.html b/examples/flask_example/templates/home.html index e56afb3dd..9363653d9 100644 --- a/examples/flask_example/templates/home.html +++ b/examples/flask_example/templates/home.html @@ -13,6 +13,7 @@ Angel OAuth2
Behance OAuth2
Bitbucket OAuth
+Box OAuth2
LinkedIn OAuth
Github OAuth2
Foursquare OAuth2
diff --git a/examples/webpy_example/app.py b/examples/webpy_example/app.py index 01c862d1e..dda2e1e7d 100644 --- a/examples/webpy_example/app.py +++ b/examples/webpy_example/app.py @@ -31,6 +31,7 @@ 'social.backends.angel.AngelOAuth2', 'social.backends.behance.BehanceOAuth2', 'social.backends.bitbucket.BitbucketOAuth', + 'social.backends.box.BoxOAuth2', 'social.backends.linkedin.LinkedinOAuth', 'social.backends.github.GithubOAuth2', 'social.backends.foursquare.FoursquareOAuth2', diff --git a/examples/webpy_example/templates/home.html b/examples/webpy_example/templates/home.html index de5e470e9..feb31e3a2 100644 --- a/examples/webpy_example/templates/home.html +++ b/examples/webpy_example/templates/home.html @@ -13,6 +13,7 @@ Angel OAuth2
Behance OAuth2
Bitbucket OAuth
+Box OAuth2
LinkedIn OAuth
Github OAuth2
Foursquare OAuth2
diff --git a/social/backends/box.py b/social/backends/box.py new file mode 100644 index 000000000..9668486ef --- /dev/null +++ b/social/backends/box.py @@ -0,0 +1,68 @@ +""" +Box.net OAuth support. + +This contribution adds support for GitHub OAuth service. The settings +SOCIAL_AUTH_BOX_KEY and SOCIAL_AUTH_BOX_SECRET must be defined with the values +given by Box.net application registration process. + +Extended permissions are supported by defining BOX_EXTENDED_PERMISSIONS +setting, it must be a list of values to request. + +By default account id and token expiration time are stored in extra_data +field, check OAuthBackend class for details on how to extend it. +""" +from social.backends.oauth import BaseOAuth2 + + +class BoxOAuth2(BaseOAuth2): + """Box.net OAuth authentication backend""" + name = 'box' + AUTHORIZATION_URL = 'https://www.box.com/api/oauth2/authorize' + ACCESS_TOKEN_METHOD = 'POST' + ACCESS_TOKEN_URL = 'https://www.box.com/api/oauth2/token' + REVOKE_TOKEN_URL = 'https://www.box.com/api/oauth2/revoke' + SCOPE_SEPARATOR = ',' + EXTRA_DATA = [ + ('refresh_token', 'refresh_token', True), + ('id', 'id'), + ('expires', 'expires'), + ('created_at', 'created_at'), + ('modified_at', 'modified_at'), + ('status', 'status'), + ('type', 'type'), + ('language', 'language'), + ('avatar_url', 'avatar_url'), + ('max_upload_size', 'max_upload_size'), + ('space_amount', 'space_amount'), + ('space_used', 'space_used'), + ] + + def do_auth(self, access_token, response=None, *args, **kwargs): + response = response or {} + data = self.user_data(access_token) + + data['access_token'] = response.get('access_token') + data['refresh_token'] = response.get('refresh_token') + data['expires'] = response.get('expires_in') + kwargs.update({'backend': self, 'response': data}) + return self.strategy.authenticate(*args, **kwargs) + + def get_user_details(self, response): + """Return user details Box.net account""" + return {'username': response.get('login'), + 'email': response.get('login') or '', + 'first_name': response.get('name')} + + def user_data(self, access_token, *args, **kwargs): + """Loads user data from service""" + params = self.setting('PROFILE_EXTRA_PARAMS', {}) + params['access_token'] = access_token + return self.get_json('https://api.box.com/2.0/users/me', + params=params) + + def refresh_token(self, token, *args, **kwargs): + params = self.refresh_token_params(token, *args, **kwargs) + request = self.request(self.REFRESH_TOKEN_URL or self.ACCESS_TOKEN_URL, + data=params, headers=self.auth_headers(), + method='POST') + return self.process_refresh_token_response(request, *args, **kwargs) diff --git a/tests/backends/box_test.py b/tests/backends/box_test.py new file mode 100644 index 000000000..9e898a77d --- /dev/null +++ b/tests/backends/box_test.py @@ -0,0 +1,66 @@ +from sure import expect +from tests.oauth import OAuth2Test +import json + + +class BoxOAuth2Test(OAuth2Test): + backend_path = 'social.backends.box.BoxOAuth2' + user_data_url = 'https://api.box.com/2.0/users/me' + expected_username = 'sean+awesome@box.com' + access_token_body = json.dumps({ + "access_token": "T9cE5asGnuyYCCqIZFoWjFHvNbvVqHjl", + "expires_in": 3600, + "restricted_to": [], + "token_type": "bearer", + "refresh_token": "J7rxTiWOHMoSC1isKZKBZWizoRXjkQzig5C6jFgCVJ9bUnsUfGMinKBDLZWP9BgR" + }) + user_data_body = json.dumps({ + "type": "user", + "id": "181216415", + "name": "sean rose", + "login": "sean+awesome@box.com", + "created_at": "2012-05-03T21:39:11-07:00", + "modified_at": "2012-11-14T11:21:32-08:00", + "role": "admin", + "language": "en", + "space_amount": 11345156112, + "space_used": 1237009912, + "max_upload_size": 2147483648, + "tracking_codes": [], + "can_see_managed_users": True, + "is_sync_enabled": True, + "status": "active", + "job_title": "", + "phone": "6509241374", + "address": "", + "avatar_url": "https://www.box.com/api/avatar/large/181216415", + "is_exempt_from_device_limits": False, + "is_exempt_from_login_verification": False, + "enterprise": { + "type": "enterprise", + "id": "17077211", + "name": "seanrose enterprise" + } + }) + refresh_token_body = json.dumps({ + "access_token": "T9cE5asGnuyYCCqIZFoWjFHvNbvVqHjl", + "expires_in": 3600, + "restricted_to": [], + "token_type": "bearer", + "refresh_token": "J7rxTiWOHMoSC1isKZKBZWizoRXjkQzig5C6jFgCVJ9bUnsUfGMinKBDLZWP9BgR" + }) + + def test_login(self): + self.do_login() + + def test_partial_pipeline(self): + self.do_partial_pipeline() + + def refresh_token_arguments(self): + uri = self.strategy.build_absolute_uri('/complete/box/') + return {'redirect_uri': uri} + + def test_refresh_token(self): + user, social = self.do_refresh_token() + expect(social.extra_data['access_token']).to.equal('T9cE5asGnuyYCCqIZFoWjFHvNbvVqHjl') + From 6612eff26e0fb369a24d10f51911125391badae6 Mon Sep 17 00:00:00 2001 From: Sam Kuehn Date: Sun, 8 Sep 2013 15:08:59 -0600 Subject: [PATCH 3/5] Add box.net to list off supported providers --- docs/intro.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/intro.rst b/docs/intro.rst index d814b1dc9..81eb494c5 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -35,6 +35,7 @@ or extend current one): * Angel_ OAuth2 * Behance_ OAuth2 * Bitbucket_ OAuth1 + * Box_ OAuth2 * Dailymotion_ OAuth2 * Disqus_ OAuth2 * Douban_ OAuth1 and OAuth2 @@ -105,6 +106,7 @@ suits your project. Check `Authentication Pipeline`_ section. .. _Angel: https://angel.co .. _Behance: https://www.behance.net .. _Bitbucket: https://bitbucket.org +.. _Box: https://www.box.com .. _Dailymotion: https://dailymotion.com .. _Disqus: https://disqus.com .. _Douban: http://www.douban.com From 5f1cb6821c5eb9ee5c025c8a8a167f6d7a8eaff6 Mon Sep 17 00:00:00 2001 From: Sam Kuehn Date: Sun, 8 Sep 2013 15:24:09 -0600 Subject: [PATCH 4/5] Add box.net to readme --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 755c7a7a0..65fc4f137 100644 --- a/README.rst +++ b/README.rst @@ -43,6 +43,7 @@ or extend current one): * Angel_ OAuth2 * Behance_ OAuth2 * Bitbucket_ OAuth1 + * Box_ OAuth2 * Dailymotion_ OAuth2 * Disqus_ OAuth2 * Douban_ OAuth1 and OAuth2 @@ -179,6 +180,7 @@ check `django-social-auth LICENCE`_ for details: .. _Angel: https://angel.co .. _Behance: https://www.behance.net .. _Bitbucket: https://bitbucket.org +.. _Box: https://www.box.com .. _Dailymotion: https://dailymotion.com .. _Disqus: https://disqus.com .. _Douban: http://www.douban.com From 547569ec15ab297285ffbfd513c585fd98a1bbe1 Mon Sep 17 00:00:00 2001 From: Sam Kuehn Date: Sun, 8 Sep 2013 21:06:18 -0600 Subject: [PATCH 5/5] Remove data that should should not be stored in extra_data --- social/backends/box.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/social/backends/box.py b/social/backends/box.py index 9668486ef..dde3b9975 100644 --- a/social/backends/box.py +++ b/social/backends/box.py @@ -26,15 +26,6 @@ class BoxOAuth2(BaseOAuth2): ('refresh_token', 'refresh_token', True), ('id', 'id'), ('expires', 'expires'), - ('created_at', 'created_at'), - ('modified_at', 'modified_at'), - ('status', 'status'), - ('type', 'type'), - ('language', 'language'), - ('avatar_url', 'avatar_url'), - ('max_upload_size', 'max_upload_size'), - ('space_amount', 'space_amount'), - ('space_used', 'space_used'), ] def do_auth(self, access_token, response=None, *args, **kwargs):