This repository has been archived on 2020-03-24. You can view files and clone it, but cannot push or open issues or pull requests.
quay/oauth/login.py
Joseph Schorr 19f7acf575 Lay foundation for truly dynamic external logins
Moves all the external login services into a set of classes that share as much code as possible. These services are then registered on both the client and server, allowing us in the followup change to dynamically register new handlers
2017-01-20 15:21:08 -05:00

81 lines
3.3 KiB
Python

import logging
import features
from oauth.base import OAuthService, OAuthExchangeCodeException, OAuthGetUserInfoException
logger = logging.getLogger(__name__)
class OAuthLoginException(Exception):
""" Exception raised if a login operation fails. """
pass
class OAuthLoginService(OAuthService):
""" A base class for defining an OAuth-compliant service that can be used for, amongst other
things, login and authentication. """
def get_login_service_id(self, user_info):
""" Returns the internal ID for the given user under this login service. """
raise NotImplementedError
def get_login_service_username(self, user_info):
""" Returns the username for the given user under this login service. """
raise NotImplementedError
def get_verified_user_email(self, app_config, http_client, token, user_info):
""" Returns the verified email address for the given user, if any or None if none. """
raise NotImplementedError
def get_icon(self):
""" Returns the icon to display for this login service. """
raise NotImplementedError
def get_login_scopes(self):
""" Returns the list of scopes for login for this service. """
raise NotImplementedError
def service_verify_user_info_for_login(self, app_config, http_client, token, user_info):
""" Performs service-specific verification of user information for login. On failure, a service
should raise a OAuthLoginService.
"""
# By default, does nothing.
pass
def exchange_code_for_login(self, app_config, http_client, code, redirect_suffix):
""" Exchanges the given OAuth access code for user information on behalf of a user trying to
login or attach their account. Raises a OAuthLoginService exception on failure. Returns
a tuple consisting of (service_id, service_username, email)
"""
# Retrieve the token for the OAuth code.
try:
token = self.exchange_code_for_token(app_config, http_client, code,
redirect_suffix=redirect_suffix,
form_encode=self.requires_form_encoding())
except OAuthExchangeCodeException as oce:
raise OAuthLoginException(oce.message)
# Retrieve the user's information with the token.
try:
user_info = self.get_user_info(http_client, token)
except OAuthGetUserInfoException as oge:
raise OAuthLoginException(oge.message)
if user_info.get('id', None) is None:
logger.debug('Got user info response %s', user_info)
raise OAuthLoginException('Missing `id` column in returned user information')
# Perform any custom verification for this login service.
self.service_verify_user_info_for_login(app_config, http_client, token, user_info)
# Retrieve the user's email address (if necessary).
email_address = self.get_verified_user_email(app_config, http_client, token, user_info)
if features.MAILING and email_address is None:
raise OAuthLoginException('A verified email address is required to login with this service')
service_user_id = self.get_login_service_id(user_info)
service_username = self.get_login_service_username(user_info)
logger.debug('Completed successful exchange for service %s: %s, %s, %s',
self.service_id(), service_user_id, service_username, email_address)
return (service_user_id, service_username, email_address)