Add support for login with Google. Note that this CL is not complete
This commit is contained in:
parent
b9c6c4c2f2
commit
2597bcef3f
10 changed files with 231 additions and 83 deletions
|
@ -7,6 +7,7 @@ from endpoints.common import render_page_template, common_login, route_show_if
|
|||
from app import app, analytics
|
||||
from data import model
|
||||
from util.names import parse_repository_name
|
||||
from util.validation import generate_valid_usernames
|
||||
from util.http import abort
|
||||
from auth.permissions import AdministerRepositoryPermission
|
||||
from auth.auth import require_session_login
|
||||
|
@ -21,19 +22,31 @@ client = app.config['HTTPCLIENT']
|
|||
callback = Blueprint('callback', __name__)
|
||||
|
||||
|
||||
def exchange_github_code_for_token(code, for_login=True):
|
||||
def exchange_code_for_token(code, service_name='GITHUB', for_login=True, form_encode=False):
|
||||
code = request.args.get('code')
|
||||
id_config = service_name + '_LOGIN_CLIENT_ID' if for_login else service_name + '_CLIENT_ID'
|
||||
secret_config = service_name + '_LOGIN_CLIENT_SECRET' if for_login else service_name + '_CLIENT_SECRET'
|
||||
|
||||
payload = {
|
||||
'client_id': app.config['GITHUB_LOGIN_CLIENT_ID' if for_login else 'GITHUB_CLIENT_ID'],
|
||||
'client_secret': app.config['GITHUB_LOGIN_CLIENT_SECRET' if for_login else 'GITHUB_CLIENT_SECRET'],
|
||||
'client_id': app.config[id_config],
|
||||
'client_secret': app.config[secret_config],
|
||||
'code': code,
|
||||
'grant_type': 'authorization_code',
|
||||
'redirect_uri': '%s://%s/oauth2/%s/callback' % (app.config['PREFERRED_URL_SCHEME'],
|
||||
app.config['SERVER_HOSTNAME'],
|
||||
service_name.lower())
|
||||
}
|
||||
|
||||
headers = {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
|
||||
get_access_token = client.post(app.config['GITHUB_TOKEN_URL'],
|
||||
params=payload, headers=headers)
|
||||
if form_encode:
|
||||
get_access_token = client.post(app.config[service_name + '_TOKEN_URL'],
|
||||
data=payload, headers=headers)
|
||||
else:
|
||||
get_access_token = client.post(app.config[service_name + '_TOKEN_URL'],
|
||||
params=payload, headers=headers)
|
||||
|
||||
json_data = get_access_token.json()
|
||||
if not json_data:
|
||||
|
@ -52,17 +65,76 @@ def get_github_user(token):
|
|||
return get_user.json()
|
||||
|
||||
|
||||
def get_google_user(token):
|
||||
token_param = {
|
||||
'access_token': token,
|
||||
'alt': 'json',
|
||||
}
|
||||
|
||||
get_user = client.get(app.config['GOOGLE_USER_URL'], params=token_param)
|
||||
return get_user.json()
|
||||
|
||||
def conduct_oauth_login(service_name, user_id, username, email):
|
||||
to_login = model.verify_federated_login(service_name.lower(), user_id)
|
||||
if not to_login:
|
||||
# try to create the user
|
||||
try:
|
||||
valid = next(generate_valid_usernames(username))
|
||||
to_login = model.create_federated_user(valid, email, service_name.lower(),
|
||||
user_id, set_password_notification=True)
|
||||
|
||||
# Success, tell analytics
|
||||
analytics.track(to_login.username, 'register', {'service': service_name.lower()})
|
||||
|
||||
state = request.args.get('state', None)
|
||||
if state:
|
||||
logger.debug('Aliasing with state: %s' % state)
|
||||
analytics.alias(to_login.username, state)
|
||||
|
||||
except model.DataModelException, ex:
|
||||
return render_page_template('ologinerror.html', service_name=service_name,
|
||||
error_message=ex.message)
|
||||
|
||||
if common_login(to_login):
|
||||
return redirect(url_for('web.index'))
|
||||
|
||||
return render_page_template('ologinerror.html', service_name=service_name,
|
||||
error_message='Unknown error')
|
||||
|
||||
|
||||
@callback.route('/google/callback', methods=['GET'])
|
||||
@route_show_if(features.GOOGLE_LOGIN)
|
||||
def google_oauth_callback():
|
||||
error = request.args.get('error', None)
|
||||
if error:
|
||||
return render_page_template('ologinerror.html', service_name='Google', error_message=error)
|
||||
|
||||
token = exchange_code_for_token(request.args.get('code'), service_name='GOOGLE', form_encode=True)
|
||||
user_data = get_google_user(token)
|
||||
if not user_data or not user_data.get('id', None) or not user_data.get('email', None):
|
||||
return render_page_template('ologinerror.html', service_name = 'Google',
|
||||
error_message='Could not load user data')
|
||||
|
||||
username = user_data['email']
|
||||
at = username.find('@')
|
||||
if at > 0:
|
||||
username = username[0:at]
|
||||
|
||||
return conduct_oauth_login('Google', user_data['id'], username, user_data['email'])
|
||||
|
||||
|
||||
@callback.route('/github/callback', methods=['GET'])
|
||||
@route_show_if(features.GITHUB_LOGIN)
|
||||
def github_oauth_callback():
|
||||
error = request.args.get('error', None)
|
||||
if error:
|
||||
return render_page_template('githuberror.html', error_message=error)
|
||||
return render_page_template('ologinerror.html', service_name = 'GitHub', error_message=error)
|
||||
|
||||
token = exchange_github_code_for_token(request.args.get('code'))
|
||||
token = exchange_code_for_token(request.args.get('code'), service_name='GITHUB')
|
||||
user_data = get_github_user(token)
|
||||
if not user_data:
|
||||
return render_page_template('githuberror.html', error_message='Could not load user data')
|
||||
return render_page_template('ologinerror.html', service_name = 'GitHub',
|
||||
error_message='Could not load user data')
|
||||
|
||||
username = user_data['login']
|
||||
github_id = user_data['id']
|
||||
|
@ -84,38 +156,34 @@ def github_oauth_callback():
|
|||
if user_email['primary']:
|
||||
break
|
||||
|
||||
to_login = model.verify_federated_login('github', github_id)
|
||||
if not to_login:
|
||||
# try to create the user
|
||||
try:
|
||||
to_login = model.create_federated_user(username, found_email, 'github',
|
||||
github_id, set_password_notification=True)
|
||||
return conduct_oauth_login('github', github_id, username, found_email)
|
||||
|
||||
# Success, tell analytics
|
||||
analytics.track(to_login.username, 'register', {'service': 'github'})
|
||||
|
||||
state = request.args.get('state', None)
|
||||
if state:
|
||||
logger.debug('Aliasing with state: %s' % state)
|
||||
analytics.alias(to_login.username, state)
|
||||
@callback.route('/google/callback/attach', methods=['GET'])
|
||||
@route_show_if(features.GOOGLE_LOGIN)
|
||||
@require_session_login
|
||||
def google_oauth_attach():
|
||||
token = exchange_code_for_token(request.args.get('code'), service_name='GOOGLE')
|
||||
user_data = get_google_user(token)
|
||||
if not user_data or not user_data.get('id', None):
|
||||
return render_page_template('ologinerror.html', service_name = 'Google',
|
||||
error_message='Could not load user data')
|
||||
|
||||
except model.DataModelException, ex:
|
||||
return render_page_template('githuberror.html', error_message=ex.message)
|
||||
|
||||
if common_login(to_login):
|
||||
return redirect(url_for('web.index'))
|
||||
|
||||
return render_page_template('githuberror.html')
|
||||
google_id = user_data['id']
|
||||
user_obj = current_user.db_user()
|
||||
model.attach_federated_login(user_obj, 'google', google_id)
|
||||
return redirect(url_for('web.user'))
|
||||
|
||||
|
||||
@callback.route('/github/callback/attach', methods=['GET'])
|
||||
@route_show_if(features.GITHUB_LOGIN)
|
||||
@require_session_login
|
||||
def github_oauth_attach():
|
||||
token = exchange_github_code_for_token(request.args.get('code'))
|
||||
token = exchange_code_for_token(request.args.get('code'), service_name='GITHUB')
|
||||
user_data = get_github_user(token)
|
||||
if not user_data:
|
||||
return render_page_template('githuberror.html', error_message='Could not load user data')
|
||||
return render_page_template('ologinerror.html', service_name = 'GitHub',
|
||||
error_message='Could not load user data')
|
||||
|
||||
github_id = user_data['id']
|
||||
user_obj = current_user.db_user()
|
||||
|
@ -130,7 +198,7 @@ def github_oauth_attach():
|
|||
def attach_github_build_trigger(namespace, repository):
|
||||
permission = AdministerRepositoryPermission(namespace, repository)
|
||||
if permission.can():
|
||||
token = exchange_github_code_for_token(request.args.get('code'), for_login=False)
|
||||
token = exchange_code_for_token(request.args.get('code'), service_name='GITHUB', for_login=False)
|
||||
repo = model.get_repository(namespace, repository)
|
||||
if not repo:
|
||||
msg = 'Invalid repository: %s/%s' % (namespace, repository)
|
||||
|
|
Reference in a new issue