Skip to content

Instantly share code, notes, and snippets.

@dcramer
Last active August 29, 2015 14:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dcramer/35dd269c17ddac11274a to your computer and use it in GitHub Desktop.
Save dcramer/35dd269c17ddac11274a to your computer and use it in GitHub Desktop.
from __future__ import absolute_import, print_function
from urllib import urlencode
from sentry.auth import Implementation, AuthView
from sentry.http import safe_urlopen, safe_urlread
from sentry.utils import json
from sentry.utils.http import absolute_uri
AUTHORIZE_URL = 'https://accounts.google.com/o/oauth2/auth'
ACCESS_TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'
SCOPE = 'email'
CLIENT_ID = None
CLIENT_SECRET = None
USER_DETAILS_ENDPOINT = 'https://www.googleapis.com/plus/v1/people/me'
class FetchUser(AuthView):
def __init__(self, domain=None, *args, **kwargs):
super(FetchUser, self).__init__(*args, **kwargs)
def dispatch(self, request):
req = safe_urlopen('{0}?{1}'.format(
USER_DETAILS_ENDPOINT,
urlencode({
'access_token': data['access_token'],
}),
))
body = safe_urlread(req)
data = json.loads(body)
if self.domain and domain != data['domain']:
raise NotImplementedError
self.bind_session(request, 'user', data)
# there is actually no reason for us to redirect, but we need a good mechanism for flow
# control that says "im done with this step, but I have no response and you should continue"
return self.redirect(self.get_next_url())
class GoogleOAuth2Implementation(OAuth2Implementation):
def __init__(self, domain=None, **config):
self.domain = domain
super(GoogleOAuth2Implementation, self).__init__(**config)
def get_auth_pipeline(self):
return [
OAuth2Login(
authorize_url=AUTHORIZE_URL,
scope=SCOPE,
client_id=CLIENT_ID,
),
OAuth2Callback(
access_token_url=ACCESS_TOKEN_URL,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
),
FetchUser(domain=self.domain),
]
def get_identity(self, data):
# data.user => {
# "displayName": "David Cramer",
# "emails": [{"value": "david@getsentry.com", "type": "account"}],
# "domain": "getsentry.com",
# "verified": false
# }
user_data = data['user']
return {
# TODO: is there a "correct" email?
'email': user_data['emails'][0]['value'],
'name': user_data['displayName'],
}
@dcramer
Copy link
Author

dcramer commented Jan 6, 2015

My biggest issue right now is how we treat the step flow control, which honestly probably won't even work.

We need a clean way to handle these situations:

  • I need to redirect you (HttpResponse)
  • I need to show you a template (HttpResponse)
  • There is an error (more or less an HttpResponse)
  • Things worked out well, please do whatever you think is right

I don't like returning different data types (in fact, I won't allow that), so the alternative is that we return some kind of tuple (success, [response]), but that's a bit obscure and to me always feels unpythonic. The general alternative to this is using exceptions as flow control (i.e. raise Authenticated) but that's super shitty. Another alternative would be to do something like return self.the_kind_of_action(), but that has scoping issues as we need to bind the implementation instance in this area, and we need the outer controller (which realistically is where the the_kind_of_action would come from).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment