Source code for velruse.providers.google_hybrid

from __future__ import absolute_import

from openid.extensions import ax

import requests
from requests_oauthlib import OAuth1

from pyramid.security import NO_PERMISSION_REQUIRED

from ..api import register_provider
from ..compat import parse_qsl

from .oid_extensions import OAuthRequest
from .oid_extensions import UIRequest
from .openid import (
    attributes,
    OpenIDAuthenticationComplete,
    OpenIDConsumer,
)


log = __import__('logging').getLogger(__name__)


GOOGLE_OAUTH = 'https://www.google.com/accounts/OAuthGetAccessToken'


[docs]class GoogleAuthenticationComplete(OpenIDAuthenticationComplete): """Google auth complete"""
[docs]def includeme(config): """Activate the ``google_hybrid`` Pyramid plugin via ``config.include('velruse.providers.google_hybrid')``. After included, a new method will be available to configure new providers. ``config.add_google_hybrid_login()`` See :func:`~velruse.providers.google_hybrid.add_google_login` for the supported options. """ config.add_directive('add_google_hybrid_login', add_google_login)
[docs]def add_google_login(config, attrs=None, realm=None, storage=None, consumer_key=None, consumer_secret=None, scope=None, login_path='/login/google', callback_path='/login/google/callback', name='google'): """ Add a Google login provider to the application using the OpenID+OAuth hybrid protocol. This protocol can be configured for purely authentication by specifying only OpenID parameters. If you also wish to authorize your application to access the user's information you may specify OAuth credentials. - OpenID parameters + ``attrs`` + ``realm`` + ``storage`` - OAuth parameters + ``consumer_key`` + ``consumer_secret`` + ``scope`` """ provider = GoogleConsumer( name, attrs, realm, storage, consumer_key, consumer_secret, scope) config.add_route(provider.login_route, login_path) config.add_view(provider, attr='login', route_name=provider.login_route, permission=NO_PERMISSION_REQUIRED) config.add_route(provider.callback_route, callback_path, use_global_views=True, factory=provider.callback) register_provider(config, name, provider)
class GoogleConsumer(OpenIDConsumer): openid_attributes = [ 'country', 'email', 'first_name', 'last_name', 'language', ] def __init__(self, name, attrs=None, realm=None, storage=None, oauth_key=None, oauth_secret=None, oauth_scope=None): """Handle Google Auth This also handles making an OAuth request during the OpenID authentication. """ OpenIDConsumer.__init__(self, name, 'google_hybrid', realm, storage, context=GoogleAuthenticationComplete) self.oauth_key = oauth_key self.oauth_secret = oauth_secret self.oauth_scope = oauth_scope if attrs is not None: self.openid_attributes = attrs def _lookup_identifier(self, request, identifier): """Return the Google OpenID directed endpoint""" return "https://www.google.com/accounts/o8/id" def _update_authrequest(self, request, authrequest): """Update the authrequest with Attribute Exchange and optionally OAuth To optionally request OAuth, the request POST must include an ``oauth_scope`` parameter that indicates what Google Apps should have access requested. """ ax_request = ax.FetchRequest() for attr in self.openid_attributes: ax_request.add(ax.AttrInfo(attributes[attr], required=True)) authrequest.addExtension(ax_request) # Add OAuth request? oauth_scope = self.oauth_scope if 'oauth_scope' in request.POST: oauth_scope = request.POST['oauth_scope'] if oauth_scope: oauth_request = OAuthRequest(consumer=self.oauth_key, scope=oauth_scope) authrequest.addExtension(oauth_request) if 'popup_mode' in request.POST: kw_args = {'mode': request.POST['popup_mode']} if 'popup_icon' in request.POST: kw_args['icon'] = request.POST['popup_icon'] ui_request = UIRequest(**kw_args) authrequest.addExtension(ui_request) def _update_profile_data(self, request, profile, credentials): """Update the user data with profile information from Google Contacts This only works if the oauth_scope included access to Google Contacts i.e. the scope needs:: http://www-opensocial.googleusercontent.com/api/people """ if self.oauth_key is None: return # setup oauth for general api calls oauth = OAuth1( self.oauth_key, client_secret=self.oauth_secret, resource_owner_key=credentials['oauthAccessToken'], resource_owner_secret=credentials['oauthAccessTokenSecret']) profile_url = \ 'https://www-opensocial.googleusercontent.com/api/people/@me/@self' resp = requests.get(profile_url, auth=oauth) if resp.status_code != 200: return data = resp.json() if 'entry' in data: profile.update(data['entry']) # Strip out the id and add it as the user id profile['accounts'][0]['userid'] = profile.pop('id', None) def _get_access_token(self, request_token): """Retrieve the access token if OAuth hybrid was used""" oauth = OAuth1( self.oauth_key, client_secret=self.oauth_secret, resource_owner_key=request_token) resp = requests.post(GOOGLE_OAUTH, auth=oauth) if resp.status_code != 200: log.error( 'OAuth token validation failed. Status: %d, Content: %s', resp.status_code, resp.content) else: access_token = dict(parse_qsl(resp.content)) return { 'oauthAccessToken': access_token['oauth_token'], 'oauthAccessTokenSecret': access_token['oauth_token_secret'], }