diff --git a/docs/auth.rst b/docs/auth.rst index 8227b6453..175bb1b68 100644 --- a/docs/auth.rst +++ b/docs/auth.rst @@ -86,6 +86,8 @@ Here's an example configuration file with the Github OAuth options: Replace `` and `` with the actual Client ID and secret obtained from the Github Settings. +If using Github Enterprise, you can set the `FLOWER_GITHUB_OAUTH_DOMAIN` environment variable to the base URL of your Github Enterprise instance. + See `GitHub OAuth API`_ docs for more info. .. _Github Settings: https://github.com/settings/applications/new diff --git a/flower/views/auth.py b/flower/views/auth.py index b93ad185b..fb00c1072 100644 --- a/flower/views/auth.py +++ b/flower/views/auth.py @@ -88,13 +88,37 @@ def __new__(cls, *args, **kwargs): class GithubLoginHandler(BaseHandler, tornado.auth.OAuth2Mixin): - _OAUTH_DOMAIN = os.getenv( - "FLOWER_GITHUB_OAUTH_DOMAIN", "github.com") - _OAUTH_AUTHORIZE_URL = f'https://{_OAUTH_DOMAIN}/login/oauth/authorize' - _OAUTH_ACCESS_TOKEN_URL = f'https://{_OAUTH_DOMAIN}/login/oauth/access_token' + _OAUTH_DOMAIN = "github.com" + _OAUTH_API_URL = 'https://api.github.com/user/emails' + _OAUTH_AUTHORIZE_URL = 'https://github.com/login/oauth/authorize' + _OAUTH_ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token' _OAUTH_NO_CALLBACKS = False _OAUTH_SETTINGS_KEY = 'oauth' + @classmethod + def _get_oauth_urls(cls, oauth_domain): + if oauth_domain == 'github.com': + return ( + f'https://api.{oauth_domain}/user/emails', + f'https://{oauth_domain}/login/oauth/authorize', + f'https://{oauth_domain}/login/oauth/access_token', + ) + return ( + f'https://{oauth_domain}/api/v3/user/emails', + f'https://{oauth_domain}/oauth/authorize', + f'https://{oauth_domain}/oauth/access_token', + ) + + def initialize(self, *args, **kwargs): + super().initialize(*args, **kwargs) + oauth_domain = os.getenv('FLOWER_GITHUB_OAUTH_DOMAIN', 'github.com') + ( + self._OAUTH_API_URL, + self._OAUTH_AUTHORIZE_URL, + self._OAUTH_ACCESS_TOKEN_URL, + ) = self._get_oauth_urls(oauth_domain) + self._OAUTH_DOMAIN = oauth_domain + async def get_authenticated_user(self, redirect_uri, code): body = urlencode({ "redirect_uri": redirect_uri, @@ -138,7 +162,7 @@ async def _on_auth(self, user): access_token = user['access_token'] response = await self.get_auth_http_client().fetch( - f'https://api.{self._OAUTH_DOMAIN}/user/emails', + self._OAUTH_API_URL, headers={'Authorization': 'token ' + access_token, 'User-agent': 'Tornado auth'}) diff --git a/tests/unit/views/test_auth.py b/tests/unit/views/test_auth.py index 941ed4aa1..4562dfff0 100644 --- a/tests/unit/views/test_auth.py +++ b/tests/unit/views/test_auth.py @@ -1,4 +1,4 @@ -from flower.views.auth import authenticate, validate_auth_option +from flower.views.auth import authenticate, validate_auth_option, GithubLoginHandler from tests.unit import AsyncHTTPTestCase @@ -59,4 +59,24 @@ def test_authenticate_wildcard_email(self): self.assertTrue(authenticate("one.*@example.com", "one.two@example.com")) self.assertFalse(authenticate(".*@example.com", "attacker@example.com.attacker.com")) self.assertFalse(authenticate(".*@corp.example.com", "attacker@corpZexample.com")) - self.assertFalse(authenticate(".*@corp\.example\.com", "attacker@corpZexample.com")) + self.assertFalse(authenticate(r".*@corp\.example\.com", "attacker@corpZexample.com")) + + +class OAuthTests(AsyncHTTPTestCase): + def test_get_oauth_urls_for_github_com(self): + api_url, authorize_url, access_token_url = GithubLoginHandler._get_oauth_urls('github.com') + self.assertEqual(api_url, 'https://api.github.com/user/emails') + self.assertEqual(authorize_url, 'https://github.com/login/oauth/authorize') + self.assertEqual(access_token_url, 'https://github.com/login/oauth/access_token') + + def test_get_oauth_urls_for_github_enterprise(self): + api_url, authorize_url, access_token_url = GithubLoginHandler._get_oauth_urls('github.example.com') + self.assertEqual(api_url, 'https://github.example.com/api/v3/user/emails') + self.assertEqual(authorize_url, 'https://github.example.com/oauth/authorize') + self.assertEqual(access_token_url, 'https://github.example.com/oauth/access_token') + + def test_github_login_handler_defaults_remain_github(self): + self.assertEqual(GithubLoginHandler._OAUTH_DOMAIN, 'github.com') + self.assertEqual(GithubLoginHandler._OAUTH_API_URL, 'https://api.github.com/user/emails') + self.assertEqual(GithubLoginHandler._OAUTH_AUTHORIZE_URL, 'https://github.com/login/oauth/authorize') + self.assertEqual(GithubLoginHandler._OAUTH_ACCESS_TOKEN_URL, 'https://github.com/login/oauth/access_token')