Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/auth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ Here's an example configuration file with the Github OAuth options:
Replace `<your_client_id>` and `<your_client_secret>` 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
Expand Down
34 changes: 29 additions & 5 deletions flower/views/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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'})

Expand Down
24 changes: 22 additions & 2 deletions tests/unit/views/test_auth.py
Original file line number Diff line number Diff line change
@@ -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


Expand Down Expand Up @@ -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')
Loading