[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