[Python-modules-commits] [python-social-auth] 09/32: added support for Github Enterprise

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 237bfd08af0ce785d60b0854059149a547079ea9
Author: Igor Serko <igor.serko at gmail.com>
Date:   Sun Jun 28 18:36:55 2015 +0100

    added support for Github Enterprise
---
 .gitignore                                      |   2 +
 docs/backends/github_enterprise.rst             |  53 ++++++
 social/backends/github.py                       |  34 +++-
 social/backends/github_enterprise.py            |  51 +++++
 social/tests/backends/test_github_enterprise.py | 243 ++++++++++++++++++++++++
 5 files changed, 373 insertions(+), 10 deletions(-)

diff --git a/.gitignore b/.gitignore
index cf1280d..e6c34a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,3 +42,5 @@ fabfile.py
 changelog.sh
 
 .DS_Store
+.\#*
+\#*\#
diff --git a/docs/backends/github_enterprise.rst b/docs/backends/github_enterprise.rst
new file mode 100644
index 0000000..33b18ec
--- /dev/null
+++ b/docs/backends/github_enterprise.rst
@@ -0,0 +1,53 @@
+GitHub Enterprise
+=================
+
+GitHub Enterprise works similar to regular Github, which is in turn based on Facebook (OAuth).
+
+- Register a new application on your instance of `GitHub Enterprise Developers`_,
+  set the callback URL to ``http://example.com/complete/github/`` replacing ``example.com``
+  with your domain.
+
+- Fill the ``Client ID`` and ``Client Secret`` values from GitHub in the settings::
+
+      SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY = ''
+      SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET = ''
+
+- Also it's possible to define extra permissions with::
+
+      SOCIAL_AUTH_GITHUB_SCOPE = [...]
+
+
+GitHub Enterprise for Organizations
+------------------------
+
+When defining authentication for organizations, use the
+``GithubEnterpriseOrganizationOAuth2`` backend instead. The settings are the same as
+the non-organization backend, but the names must be::
+
+      SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_*
+
+Be sure to define the organization name using the setting::
+
+      SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME = ''
+
+This name will be used to check that the user really belongs to the given
+organization and discard it if they're not part of it.
+
+
+GitHub Enterprise for Teams
+----------------
+
+Similar to ``GitHub Enterprise for Organizations``, there's a GitHub for Teams backend,
+use the backend ``GithubEnterpriseTeamOAuth2``. The settings are the same as
+the basic backend, but the names must be::
+
+    SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_*
+
+Be sure to define the ``Team ID`` using the setting::
+
+      SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID = ''
+
+This ``id`` will be used to check that the user really belongs to the given
+team and discard it if they're not part of it.
+
+.. _GitHub Enterprise Developers: https://<your_github_enterprise_domain>/settings/applications/new
diff --git a/social/backends/github.py b/social/backends/github.py
index 9ac091c..85b05dc 100644
--- a/social/backends/github.py
+++ b/social/backends/github.py
@@ -4,15 +4,17 @@ Github OAuth2 backend, docs at:
 """
 from requests import HTTPError
 
-from social.exceptions import AuthFailed
+from six.moves.urllib.parse import urljoin
+
 from social.backends.oauth import BaseOAuth2
+from social.exceptions import AuthFailed
 
 
 class GithubOAuth2(BaseOAuth2):
     """Github OAuth authentication backend"""
     name = 'github'
-    AUTHORIZATION_URL = 'https://github.com/login/oauth/authorize'
-    ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token'
+    AUTHORIZATION_URL_SUFFIX = 'login/oauth/authorize'
+    ACCESS_TOKEN_URL_SUFFIX = 'login/oauth/access_token'
     ACCESS_TOKEN_METHOD = 'POST'
     SCOPE_SEPARATOR = ','
     EXTRA_DATA = [
@@ -21,6 +23,18 @@ class GithubOAuth2(BaseOAuth2):
         ('login', 'login')
     ]
 
+    @property
+    def API_URL(self):
+        return 'https://api.github.com/'
+
+    @property
+    def AUTHORIZATION_URL(self):
+        return urljoin('https://github.com/', self.AUTHORIZATION_URL_SUFFIX)
+
+    @property
+    def ACCESS_TOKEN_URL(self):
+        return urljoin('https://github.com/', self.ACCESS_TOKEN_URL_SUFFIX)
+
     def get_user_details(self, response):
         """Return user details from Github account"""
         fullname, first_name, last_name = self.get_user_names(
@@ -55,7 +69,7 @@ class GithubOAuth2(BaseOAuth2):
         return data
 
     def _user_data(self, access_token, path=None):
-        url = 'https://api.github.com/user{0}'.format(path or '')
+        url = urljoin(self.API_URL, 'user{0}'.format(path or ''))
         return self.get_json(url, params={'access_token': access_token})
 
 
@@ -89,9 +103,9 @@ class GithubOrganizationOAuth2(GithubMemberOAuth2):
     no_member_string = 'User doesn\'t belong to the organization'
 
     def member_url(self, user_data):
-        return 'https://api.github.com/orgs/{org}/members/{username}'\
-                    .format(org=self.setting('NAME'),
-                            username=user_data.get('login'))
+        return urljoin(self.API_URL, 'orgs/{org}/members/{username}'.format(
+            org=self.setting('NAME'),
+            username=user_data.get('login')))
 
 
 class GithubTeamOAuth2(GithubMemberOAuth2):
@@ -100,6 +114,6 @@ class GithubTeamOAuth2(GithubMemberOAuth2):
     no_member_string = 'User doesn\'t belong to the team'
 
     def member_url(self, user_data):
-        return 'https://api.github.com/teams/{team_id}/members/{username}'\
-                    .format(team_id=self.setting('ID'),
-                            username=user_data.get('login'))
+        return urljoin(self.API_URL, 'teams/{team_id}/members/{username}'.format(
+            team_id=self.setting('ID'),
+            username=user_data.get('login')))
diff --git a/social/backends/github_enterprise.py b/social/backends/github_enterprise.py
new file mode 100644
index 0000000..6e7344f
--- /dev/null
+++ b/social/backends/github_enterprise.py
@@ -0,0 +1,51 @@
+"""
+Github Enterprise OAuth2 backend, docs at:
+    http://psa.matiasaguirre.net/docs/backends/github_enterprise.html
+"""
+from six.moves.urllib.parse import urljoin
+
+from social.backends.github import (
+    GithubOAuth2, GithubOrganizationOAuth2, GithubTeamOAuth2)
+
+
+def append_slash(url):
+    """Make sure we append a slash at the end of the URL otherwise we have issues with urljoin
+    Example:
+    >>> urlparse.urljoin('http://www.example.com/api/v3', 'user/1/')
+    'http://www.example.com/api/user/1/'
+    """
+    if not url:
+        return url
+    return "%s/" % url if not url.endswith('/') else url
+
+
+class GithubEnterpriseMixin(object):
+
+    @property
+    def API_URL(self):
+        return append_slash(self.setting('API_URL'))
+
+    @property
+    def AUTHORIZATION_URL(self):
+        return urljoin(append_slash(self.setting('URL')), GithubOAuth2.AUTHORIZATION_URL_SUFFIX)
+
+    @property
+    def ACCESS_TOKEN_URL(self):
+        return urljoin(append_slash(self.setting('URL')), GithubOAuth2.ACCESS_TOKEN_URL_SUFFIX)
+
+
+class GithubEnterpriseOAuth2(GithubEnterpriseMixin, GithubOAuth2):
+    """Github Enterprise OAuth authentication backend"""
+    name = 'github-enterprise'
+
+
+class GithubEnterpriseOrganizationOAuth2(GithubEnterpriseMixin, GithubOrganizationOAuth2):
+    """Github Enterprise OAuth2 authentication backend for organizations"""
+    DEFAULT_SCOPE = ['read:org']
+    name = 'github-enterprise-org'
+
+
+class GithubEnterpriseTeamOAuth2(GithubEnterpriseMixin, GithubTeamOAuth2):
+    """Github Enterprise OAuth2 authentication backend for teams"""
+    DEFAULT_SCOPE = ['read:org']
+    name = 'github-enterprise-team'
diff --git a/social/tests/backends/test_github_enterprise.py b/social/tests/backends/test_github_enterprise.py
new file mode 100644
index 0000000..61455b1
--- /dev/null
+++ b/social/tests/backends/test_github_enterprise.py
@@ -0,0 +1,243 @@
+import json
+
+from httpretty import HTTPretty
+
+from social.exceptions import AuthFailed
+
+from social.tests.backends.oauth import OAuth2Test
+
+
+class GithubEnterpriseOAuth2Test(OAuth2Test):
+    backend_path = 'social.backends.github_enterprise.GithubEnterpriseOAuth2'
+    user_data_url = 'https://www.example.com/api/v3/user'
+    expected_username = 'foobar'
+    access_token_body = json.dumps({
+        'access_token': 'foobar',
+        'token_type': 'bearer'
+    })
+    user_data_body = json.dumps({
+        'login': 'foobar',
+        'id': 1,
+        'avatar_url': 'https://www.example.com/images/error/foobar_happy.gif',
+        'gravatar_id': 'somehexcode',
+        'url': 'https://www.example.com/api/v3/users/foobar',
+        'name': 'monalisa foobar',
+        'company': 'GitHub',
+        'blog': 'https://www.example.com/blog',
+        'location': 'San Francisco',
+        'email': 'foo at bar.com',
+        'hireable': False,
+        'bio': 'There once was...',
+        'public_repos': 2,
+        'public_gists': 1,
+        'followers': 20,
+        'following': 0,
+        'html_url': 'https://www.example.com/foobar',
+        'created_at': '2008-01-14T04:33:35Z',
+        'type': 'User',
+        'total_private_repos': 100,
+        'owned_private_repos': 100,
+        'private_gists': 81,
+        'disk_usage': 10000,
+        'collaborators': 8,
+        'plan': {
+            'name': 'Medium',
+            'space': 400,
+            'collaborators': 10,
+            'private_repos': 20
+        }
+    })
+
+    def test_login(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL': 'https://www.example.com/api/v3'})
+        self.do_login()
+
+    def test_partial_pipeline(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL': 'https://www.example.com/api/v3'})
+        self.do_partial_pipeline()
+
+
+class GithubEnterpriseOAuth2NoEmailTest(GithubEnterpriseOAuth2Test):
+    user_data_body = json.dumps({
+        'login': 'foobar',
+        'id': 1,
+        'avatar_url': 'https://www.example.com/images/error/foobar_happy.gif',
+        'gravatar_id': 'somehexcode',
+        'url': 'https://www.example.com/api/v3/users/foobar',
+        'name': 'monalisa foobar',
+        'company': 'GitHub',
+        'blog': 'https://www.example.com/blog',
+        'location': 'San Francisco',
+        'email': '',
+        'hireable': False,
+        'bio': 'There once was...',
+        'public_repos': 2,
+        'public_gists': 1,
+        'followers': 20,
+        'following': 0,
+        'html_url': 'https://www.example.com/foobar',
+        'created_at': '2008-01-14T04:33:35Z',
+        'type': 'User',
+        'total_private_repos': 100,
+        'owned_private_repos': 100,
+        'private_gists': 81,
+        'disk_usage': 10000,
+        'collaborators': 8,
+        'plan': {
+            'name': 'Medium',
+            'space': 400,
+            'collaborators': 10,
+            'private_repos': 20
+        }
+    })
+
+    def test_login(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL': 'https://www.example.com/api/v3'})
+        url = 'https://www.example.com/api/v3/user/emails'
+        HTTPretty.register_uri(HTTPretty.GET, url, status=200,
+                               body=json.dumps(['foo at bar.com']),
+                               content_type='application/json')
+        self.do_login()
+
+    def test_login_next_format(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL': 'https://www.example.com/api/v3'})
+        url = 'https://www.example.com/api/v3/user/emails'
+        HTTPretty.register_uri(HTTPretty.GET, url, status=200,
+                               body=json.dumps([{'email': 'foo at bar.com'}]),
+                               content_type='application/json')
+        self.do_login()
+
+    def test_partial_pipeline(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL': 'https://www.example.com/api/v3'})
+        self.do_partial_pipeline()
+
+
+class GithubEnterpriseOrganizationOAuth2Test(GithubEnterpriseOAuth2Test):
+    backend_path = 'social.backends.github_enterprise.GithubEnterpriseOrganizationOAuth2'
+
+    def auth_handlers(self, start_url):
+        url = 'https://www.example.com/api/v3/orgs/foobar/members/foobar'
+        HTTPretty.register_uri(HTTPretty.GET, url, status=204, body='')
+        return super(GithubEnterpriseOrganizationOAuth2Test, self).auth_handlers(
+            start_url
+        )
+
+    def test_login(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL': 'https://www.example.com/api/v3'})
+        self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME': 'foobar'})
+        self.do_login()
+
+    def test_partial_pipeline(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL': 'https://www.example.com/api/v3'})
+        self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME': 'foobar'})
+        self.do_partial_pipeline()
+
+
+class GithubEnterpriseOrganizationOAuth2FailTest(GithubEnterpriseOAuth2Test):
+    backend_path = 'social.backends.github_enterprise.GithubEnterpriseOrganizationOAuth2'
+
+    def auth_handlers(self, start_url):
+        url = 'https://www.example.com/api/v3/orgs/foobar/members/foobar'
+        HTTPretty.register_uri(HTTPretty.GET, url, status=404,
+                               body='{"message": "Not Found"}',
+                               content_type='application/json')
+        return super(GithubEnterpriseOrganizationOAuth2FailTest, self).auth_handlers(
+            start_url
+        )
+
+    def test_login(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL': 'https://www.example.com/api/v3'})
+        self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME': 'foobar'})
+        with self.assertRaises(AuthFailed):
+            self.do_login()
+
+    def test_partial_pipeline(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL': 'https://www.example.com/api/v3'})
+        self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME': 'foobar'})
+        with self.assertRaises(AuthFailed):
+            self.do_partial_pipeline()
+
+
+class GithubEnterpriseTeamOAuth2Test(GithubEnterpriseOAuth2Test):
+    backend_path = 'social.backends.github_enterprise.GithubEnterpriseTeamOAuth2'
+
+    def auth_handlers(self, start_url):
+        url = 'https://www.example.com/api/v3/teams/123/members/foobar'
+        HTTPretty.register_uri(HTTPretty.GET, url, status=204, body='')
+        return super(GithubEnterpriseTeamOAuth2Test, self).auth_handlers(
+            start_url
+        )
+
+    def test_login(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL': 'https://www.example.com/api/v3'})
+        self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID': '123'})
+        self.do_login()
+
+    def test_partial_pipeline(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL': 'https://www.example.com/api/v3'})
+        self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID': '123'})
+        self.do_partial_pipeline()
+
+
+class GithubEnterpriseTeamOAuth2FailTest(GithubEnterpriseOAuth2Test):
+    backend_path = 'social.backends.github_enterprise.GithubEnterpriseTeamOAuth2'
+
+    def auth_handlers(self, start_url):
+        url = 'https://www.example.com/api/v3/teams/123/members/foobar'
+        HTTPretty.register_uri(HTTPretty.GET, url, status=404,
+                               body='{"message": "Not Found"}',
+                               content_type='application/json')
+        return super(GithubEnterpriseTeamOAuth2FailTest, self).auth_handlers(
+            start_url
+        )
+
+    def test_login(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL': 'https://www.example.com/api/v3'})
+        self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID': '123'})
+        with self.assertRaises(AuthFailed):
+            self.do_login()
+
+    def test_partial_pipeline(self):
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL': 'https://www.example.com'})
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL': 'https://www.example.com/api/v3'})
+        self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID': '123'})
+        with self.assertRaises(AuthFailed):
+            self.do_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