diff --git a/AUTHORS b/AUTHORS index cd9081cf71..e059d2760b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -45,6 +45,7 @@ Fred Palmer Fábio Santos George Whewell Griffith Rees +Guilhem Saurel Guillaume Vincent Guoyu Hao Hatem Nassrat diff --git a/ChangeLog.rst b/ChangeLog.rst index 34570195bc..0568f5bef5 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -5,6 +5,7 @@ Note worthy changes ------------------- - The ``instagram`` provider now extracts the user's full name. +- New provider: NextCloud (OAuth2) 0.39.1 (2019-02-28) diff --git a/allauth/socialaccount/providers/nextcloud/__init__.py b/allauth/socialaccount/providers/nextcloud/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/allauth/socialaccount/providers/nextcloud/provider.py b/allauth/socialaccount/providers/nextcloud/provider.py new file mode 100644 index 0000000000..363ab34ed1 --- /dev/null +++ b/allauth/socialaccount/providers/nextcloud/provider.py @@ -0,0 +1,28 @@ +from allauth.socialaccount.providers.base import ProviderAccount +from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider + + +class NextCloudAccount(ProviderAccount): + pass + + +class NextCloudProvider(OAuth2Provider): + id = 'nextcloud' + name = 'NextCloud' + account_class = NextCloudAccount + + def extract_uid(self, data): + return str(data['id']) + + def extract_common_fields(self, data): + return dict( + username=data['displayname'], + email=data['email'], + ) + + def get_default_scope(self): + scope = ['read'] + return scope + + +provider_classes = [NextCloudProvider] diff --git a/allauth/socialaccount/providers/nextcloud/tests.py b/allauth/socialaccount/providers/nextcloud/tests.py new file mode 100644 index 0000000000..4454d094f5 --- /dev/null +++ b/allauth/socialaccount/providers/nextcloud/tests.py @@ -0,0 +1,63 @@ +from django.test.utils import override_settings + +from allauth.socialaccount.tests import OAuth2TestsMixin +from allauth.tests import MockedResponse, TestCase + +from .provider import NextCloudProvider + + +@override_settings(SOCIALACCOUNT_PROVIDERS={ + 'nextcloud': { + 'SERVER': 'https://nextcloud.example.org' + } +}) +class NextCloudTests(OAuth2TestsMixin, TestCase): + provider_id = NextCloudProvider.id + + def get_login_response_json(self, with_refresh_token=True): + return super(NextCloudTests, self).get_login_response_json( + with_refresh_token=with_refresh_token).replace('uid', 'user_id') + + def get_mocked_response(self): + return MockedResponse( + 200, """ + + + ok + 100 + OK + + + + + 1 + batman + /var/www/html/data/batman + 1553946472000 + Database + + + 1455417655296 + 467191265 + 1455884846561 + 0.03 + -3 + + batman@wayne.com + batman + 7351857301 +
BatCave, Gotham City
+ https://batman.org + @the_batman + + admin + + fr + fr_FR + + 1 + 1 + +
+
+""") diff --git a/allauth/socialaccount/providers/nextcloud/urls.py b/allauth/socialaccount/providers/nextcloud/urls.py new file mode 100644 index 0000000000..b0c4d9a090 --- /dev/null +++ b/allauth/socialaccount/providers/nextcloud/urls.py @@ -0,0 +1,6 @@ +from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns + +from .provider import NextCloudProvider + + +urlpatterns = default_urlpatterns(NextCloudProvider) diff --git a/allauth/socialaccount/providers/nextcloud/views.py b/allauth/socialaccount/providers/nextcloud/views.py new file mode 100644 index 0000000000..f19c4f5a7b --- /dev/null +++ b/allauth/socialaccount/providers/nextcloud/views.py @@ -0,0 +1,36 @@ +import requests +import xml.etree.ElementTree as ET + +from allauth.socialaccount import app_settings +from allauth.socialaccount.providers.oauth2.views import ( + OAuth2Adapter, + OAuth2CallbackView, + OAuth2LoginView, +) + +from .provider import NextCloudProvider + + +class NextCloudAdapter(OAuth2Adapter): + provider_id = NextCloudProvider.id + settings = app_settings.PROVIDERS.get(provider_id, {}) + server = settings.get('SERVER', 'https://nextcloud.example.org') + access_token_url = '{0}/apps/oauth2/api/v1/token'.format(server) + authorize_url = '{0}/apps/oauth2/authorize'.format(server) + profile_url = '{0}/ocs/v1.php/cloud/users/'.format(server) + + def complete_login(self, request, app, token, **kwargs): + extra_data = self.get_user_info(token, kwargs['response']['user_id']) + return self.get_provider().sociallogin_from_response( + request, extra_data) + + def get_user_info(self, token, user_id): + headers = {'Authorization': 'Bearer {0}'.format(self.server)} + resp = requests.get(self.profile_url + user_id, headers=headers) + resp.raise_for_status() + data = ET.fromstring(resp.content.decode())[1] + return {d.tag: d.text.strip() for d in data if d.text is not None} + + +oauth2_login = OAuth2LoginView.adapter_view(NextCloudAdapter) +oauth2_callback = OAuth2CallbackView.adapter_view(NextCloudAdapter) diff --git a/docs/installation.rst b/docs/installation.rst index 6aed40fd11..6b6fa2c70e 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -94,6 +94,7 @@ settings.py (Important - Please note 'django.contrib.sites' is required as INSTA 'allauth.socialaccount.providers.meetup', 'allauth.socialaccount.providers.microsoft', 'allauth.socialaccount.providers.naver', + 'allauth.socialaccount.providers.nextcloud', 'allauth.socialaccount.providers.odnoklassniki', 'allauth.socialaccount.providers.openid', 'allauth.socialaccount.providers.orcid', diff --git a/docs/overview.rst b/docs/overview.rst index ea0106c50d..777e662423 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -123,6 +123,8 @@ Supported Providers - Microsoft (Graph) (OAuth2) +- NextCloud (OAuth2) + - Naver (OAuth2) - Odnoklassniki (OAuth2) diff --git a/docs/providers.rst b/docs/providers.rst index 093540f852..ee41e7ccec 100644 --- a/docs/providers.rst +++ b/docs/providers.rst @@ -960,6 +960,24 @@ Development callback URL http://localhost:8000/accounts/naver/login/callback/ +NextCloud +--------- + +The following NextCloud settings are available: + +.. code-block:: python + + SOCIALACCOUNT_PROVIDERS = { + 'nextcloud': { + 'SERVER': 'https://nextcloud.example.org', + } + } + + +App registration (get your key and secret here) + + https://nextcloud.example.org/settings/admin/security + Odnoklassniki ------------- diff --git a/test_settings.py b/test_settings.py index 066b781b23..4e45a69050 100644 --- a/test_settings.py +++ b/test_settings.py @@ -102,6 +102,7 @@ 'allauth.socialaccount.providers.meetup', 'allauth.socialaccount.providers.microsoft', 'allauth.socialaccount.providers.naver', + 'allauth.socialaccount.providers.nextcloud', 'allauth.socialaccount.providers.odnoklassniki', 'allauth.socialaccount.providers.openid', 'allauth.socialaccount.providers.orcid',