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
12
config.py
12
config.py
|
@ -19,7 +19,7 @@ def build_requests_session():
|
||||||
CLIENT_WHITELIST = ['SERVER_HOSTNAME', 'PREFERRED_URL_SCHEME', 'GITHUB_CLIENT_ID',
|
CLIENT_WHITELIST = ['SERVER_HOSTNAME', 'PREFERRED_URL_SCHEME', 'GITHUB_CLIENT_ID',
|
||||||
'GITHUB_LOGIN_CLIENT_ID', 'MIXPANEL_KEY', 'STRIPE_PUBLISHABLE_KEY',
|
'GITHUB_LOGIN_CLIENT_ID', 'MIXPANEL_KEY', 'STRIPE_PUBLISHABLE_KEY',
|
||||||
'ENTERPRISE_LOGO_URL', 'SENTRY_PUBLIC_DSN', 'AUTHENTICATION_TYPE',
|
'ENTERPRISE_LOGO_URL', 'SENTRY_PUBLIC_DSN', 'AUTHENTICATION_TYPE',
|
||||||
'REGISTRY_TITLE', 'REGISTRY_TITLE_SHORT']
|
'REGISTRY_TITLE', 'REGISTRY_TITLE_SHORT', 'GOOGLE_LOGIN_CLIENT_ID']
|
||||||
|
|
||||||
|
|
||||||
def getFrontendVisibleConfig(config_dict):
|
def getFrontendVisibleConfig(config_dict):
|
||||||
|
@ -115,6 +115,13 @@ class DefaultConfig(object):
|
||||||
GITHUB_LOGIN_CLIENT_ID = ''
|
GITHUB_LOGIN_CLIENT_ID = ''
|
||||||
GITHUB_LOGIN_CLIENT_SECRET = ''
|
GITHUB_LOGIN_CLIENT_SECRET = ''
|
||||||
|
|
||||||
|
# Google Config.
|
||||||
|
GOOGLE_TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'
|
||||||
|
GOOGLE_USER_URL = 'https://www.googleapis.com/oauth2/v1/userinfo'
|
||||||
|
|
||||||
|
GOOGLE_LOGIN_CLIENT_ID = ''
|
||||||
|
GOOGLE_LOGIN_CLIENT_SECRET = ''
|
||||||
|
|
||||||
# Requests based HTTP client with a large request pool
|
# Requests based HTTP client with a large request pool
|
||||||
HTTPCLIENT = build_requests_session()
|
HTTPCLIENT = build_requests_session()
|
||||||
|
|
||||||
|
@ -144,6 +151,9 @@ class DefaultConfig(object):
|
||||||
# Feature Flag: Whether GitHub login is supported.
|
# Feature Flag: Whether GitHub login is supported.
|
||||||
FEATURE_GITHUB_LOGIN = False
|
FEATURE_GITHUB_LOGIN = False
|
||||||
|
|
||||||
|
# Feature Flag: Whether Google login is supported.
|
||||||
|
FEATURE_GOOGLE_LOGIN = False
|
||||||
|
|
||||||
# Feature flag, whether to enable olark chat
|
# Feature flag, whether to enable olark chat
|
||||||
FEATURE_OLARK_CHAT = False
|
FEATURE_OLARK_CHAT = False
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ from endpoints.common import render_page_template, common_login, route_show_if
|
||||||
from app import app, analytics
|
from app import app, analytics
|
||||||
from data import model
|
from data import model
|
||||||
from util.names import parse_repository_name
|
from util.names import parse_repository_name
|
||||||
|
from util.validation import generate_valid_usernames
|
||||||
from util.http import abort
|
from util.http import abort
|
||||||
from auth.permissions import AdministerRepositoryPermission
|
from auth.permissions import AdministerRepositoryPermission
|
||||||
from auth.auth import require_session_login
|
from auth.auth import require_session_login
|
||||||
|
@ -21,19 +22,31 @@ client = app.config['HTTPCLIENT']
|
||||||
callback = Blueprint('callback', __name__)
|
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')
|
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 = {
|
payload = {
|
||||||
'client_id': app.config['GITHUB_LOGIN_CLIENT_ID' if for_login else 'GITHUB_CLIENT_ID'],
|
'client_id': app.config[id_config],
|
||||||
'client_secret': app.config['GITHUB_LOGIN_CLIENT_SECRET' if for_login else 'GITHUB_CLIENT_SECRET'],
|
'client_secret': app.config[secret_config],
|
||||||
'code': code,
|
'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 = {
|
headers = {
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
}
|
}
|
||||||
|
|
||||||
get_access_token = client.post(app.config['GITHUB_TOKEN_URL'],
|
if form_encode:
|
||||||
params=payload, headers=headers)
|
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()
|
json_data = get_access_token.json()
|
||||||
if not json_data:
|
if not json_data:
|
||||||
|
@ -52,17 +65,76 @@ def get_github_user(token):
|
||||||
return get_user.json()
|
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'])
|
@callback.route('/github/callback', methods=['GET'])
|
||||||
@route_show_if(features.GITHUB_LOGIN)
|
@route_show_if(features.GITHUB_LOGIN)
|
||||||
def github_oauth_callback():
|
def github_oauth_callback():
|
||||||
error = request.args.get('error', None)
|
error = request.args.get('error', None)
|
||||||
if error:
|
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)
|
user_data = get_github_user(token)
|
||||||
if not user_data:
|
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']
|
username = user_data['login']
|
||||||
github_id = user_data['id']
|
github_id = user_data['id']
|
||||||
|
@ -84,38 +156,34 @@ def github_oauth_callback():
|
||||||
if user_email['primary']:
|
if user_email['primary']:
|
||||||
break
|
break
|
||||||
|
|
||||||
to_login = model.verify_federated_login('github', github_id)
|
return conduct_oauth_login('github', github_id, username, found_email)
|
||||||
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)
|
|
||||||
|
|
||||||
# Success, tell analytics
|
|
||||||
analytics.track(to_login.username, 'register', {'service': 'github'})
|
|
||||||
|
|
||||||
state = request.args.get('state', None)
|
@callback.route('/google/callback/attach', methods=['GET'])
|
||||||
if state:
|
@route_show_if(features.GOOGLE_LOGIN)
|
||||||
logger.debug('Aliasing with state: %s' % state)
|
@require_session_login
|
||||||
analytics.alias(to_login.username, state)
|
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:
|
google_id = user_data['id']
|
||||||
return render_page_template('githuberror.html', error_message=ex.message)
|
user_obj = current_user.db_user()
|
||||||
|
model.attach_federated_login(user_obj, 'google', google_id)
|
||||||
if common_login(to_login):
|
return redirect(url_for('web.user'))
|
||||||
return redirect(url_for('web.index'))
|
|
||||||
|
|
||||||
return render_page_template('githuberror.html')
|
|
||||||
|
|
||||||
|
|
||||||
@callback.route('/github/callback/attach', methods=['GET'])
|
@callback.route('/github/callback/attach', methods=['GET'])
|
||||||
@route_show_if(features.GITHUB_LOGIN)
|
@route_show_if(features.GITHUB_LOGIN)
|
||||||
@require_session_login
|
@require_session_login
|
||||||
def github_oauth_attach():
|
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)
|
user_data = get_github_user(token)
|
||||||
if not user_data:
|
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']
|
github_id = user_data['id']
|
||||||
user_obj = current_user.db_user()
|
user_obj = current_user.db_user()
|
||||||
|
@ -130,7 +198,7 @@ def github_oauth_attach():
|
||||||
def attach_github_build_trigger(namespace, repository):
|
def attach_github_build_trigger(namespace, repository):
|
||||||
permission = AdministerRepositoryPermission(namespace, repository)
|
permission = AdministerRepositoryPermission(namespace, repository)
|
||||||
if permission.can():
|
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)
|
repo = model.get_repository(namespace, repository)
|
||||||
if not repo:
|
if not repo:
|
||||||
msg = 'Invalid repository: %s/%s' % (namespace, repository)
|
msg = 'Invalid repository: %s/%s' % (namespace, repository)
|
||||||
|
|
|
@ -179,6 +179,8 @@ def initialize_database():
|
||||||
TeamRole.create(name='member')
|
TeamRole.create(name='member')
|
||||||
Visibility.create(name='public')
|
Visibility.create(name='public')
|
||||||
Visibility.create(name='private')
|
Visibility.create(name='private')
|
||||||
|
|
||||||
|
LoginService.create(name='google')
|
||||||
LoginService.create(name='github')
|
LoginService.create(name='github')
|
||||||
LoginService.create(name='quayrobot')
|
LoginService.create(name='quayrobot')
|
||||||
LoginService.create(name='ldap')
|
LoginService.create(name='ldap')
|
||||||
|
|
17
static/directives/external-login-button.html
Normal file
17
static/directives/external-login-button.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<span class="external-login-button-element">
|
||||||
|
<span ng-if="provider == 'github'">
|
||||||
|
<a href="javascript:void(0)" class="btn btn-primary btn-block" quay-require="['GITHUB_LOGIN']" ng-click="startSignin('github')" style="margin-bottom: 10px">
|
||||||
|
<i class="fa fa-github fa-lg"></i>
|
||||||
|
<span ng-if="action != 'attach'">Sign In with GitHub</span>
|
||||||
|
<span ng-if="action == 'attach'">Attach to GitHub Account</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span ng-if="provider == 'google'">
|
||||||
|
<a href="javascript:void(0)" class="btn btn-primary btn-block" quay-require="['GOOGLE_LOGIN']" ng-click="startSignin('google')">
|
||||||
|
<i class="fa fa-google fa-lg"></i>
|
||||||
|
<span ng-if="action != 'attach'">Sign In with Google</span>
|
||||||
|
<span ng-if="action == 'attach'">Attach to Google Account</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</span>
|
|
@ -11,10 +11,8 @@
|
||||||
<span class="inner-text">OR</span>
|
<span class="inner-text">OR</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a id="github-signin-link" class="btn btn-primary btn-lg btn-block" href="javascript:void(0)" ng-click="showGithub()"
|
<div class="external-login-button" provider="github" redirect-url="redirectUrl" sign-in-started="markStarted()"></div>
|
||||||
quay-require="['GITHUB_LOGIN']">
|
<div class="external-login-button" provider="google" redirect-url="redirectUrl" sign-in-started="markStarted()"></div>
|
||||||
<i class="fa fa-github fa-lg"></i> Sign In with GitHub
|
|
||||||
</a>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="alert alert-danger" ng-show="invalidCredentials">Invalid username or password.</div>
|
<div class="alert alert-danger" ng-show="invalidCredentials">Invalid username or password.</div>
|
||||||
|
|
|
@ -18,10 +18,8 @@
|
||||||
<i class="fa fa-circle"></i>
|
<i class="fa fa-circle"></i>
|
||||||
<span class="inner-text">OR</span>
|
<span class="inner-text">OR</span>
|
||||||
</span>
|
</span>
|
||||||
<a href="https://github.com/login/oauth/authorize?client_id={{ githubClientId }}&scope=user:email{{ github_state_clause }}"
|
<div class="external-login-button" provider="github"></div>
|
||||||
class="btn btn-primary btn-block" quay-require="['GITHUB_LOGIN']">
|
<div class="external-login-button" provider="google"></div>
|
||||||
<i class="fa fa-github fa-lg"></i> Sign In with GitHub
|
|
||||||
</a>
|
|
||||||
<p class="help-block" quay-require="['BILLING']">No credit card required.</p>
|
<p class="help-block" quay-require="['BILLING']">No credit card required.</p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
100
static/js/app.js
100
static/js/app.js
|
@ -1278,10 +1278,41 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
var keyService = {}
|
var keyService = {}
|
||||||
|
|
||||||
keyService['stripePublishableKey'] = Config['STRIPE_PUBLISHABLE_KEY'];
|
keyService['stripePublishableKey'] = Config['STRIPE_PUBLISHABLE_KEY'];
|
||||||
|
|
||||||
keyService['githubClientId'] = Config['GITHUB_CLIENT_ID'];
|
keyService['githubClientId'] = Config['GITHUB_CLIENT_ID'];
|
||||||
keyService['githubLoginClientId'] = Config['GITHUB_LOGIN_CLIENT_ID'];
|
keyService['githubLoginClientId'] = Config['GITHUB_LOGIN_CLIENT_ID'];
|
||||||
keyService['githubRedirectUri'] = Config.getUrl('/oauth2/github/callback');
|
keyService['githubRedirectUri'] = Config.getUrl('/oauth2/github/callback');
|
||||||
|
|
||||||
|
keyService['googleLoginClientId'] = Config['GOOGLE_LOGIN_CLIENT_ID'];
|
||||||
|
keyService['googleRedirectUri'] = Config.getUrl('/oauth2/google/callback');
|
||||||
|
|
||||||
|
keyService['googleLoginUrl'] = 'https://accounts.google.com/o/oauth2/auth?response_type=code&';
|
||||||
|
keyService['githubLoginUrl'] = 'https://github.com/login/oauth/authorize?';
|
||||||
|
|
||||||
|
keyService['googleLoginScope'] = 'openid email';
|
||||||
|
keyService['githubLoginScope'] = 'user:email';
|
||||||
|
|
||||||
|
keyService.getExternalLoginUrl = function(service, action) {
|
||||||
|
var state_clause = '';
|
||||||
|
if (Config.MIXPANEL_KEY && window.mixpanel) {
|
||||||
|
if (mixpanel.get_distinct_id !== undefined) {
|
||||||
|
state_clause = "&state=" + encodeURIComponent(mixpanel.get_distinct_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var client_id = keyService[service + 'LoginClientId'];
|
||||||
|
var scope = keyService[service + 'LoginScope'];
|
||||||
|
var redirect_uri = keyService[service + 'RedirectUri'];
|
||||||
|
if (action == 'attach') {
|
||||||
|
redirect_uri += '/attach';
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = keyService[service + 'LoginUrl'] + 'client_id=' + client_id + '&scope=' + scope +
|
||||||
|
'&redirect_uri=' + redirect_uri + state_clause;
|
||||||
|
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
return keyService;
|
return keyService;
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
@ -2150,6 +2181,41 @@ quayApp.directive('userSetup', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
quayApp.directive('externalLoginButton', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/external-login-button.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'signInStarted': '&signInStarted',
|
||||||
|
'redirectUrl': '=redirectUrl',
|
||||||
|
'provider': '@provider',
|
||||||
|
'action': '@action'
|
||||||
|
},
|
||||||
|
controller: function($scope, $location, $timeout, ApiService, KeyService, UserService, CookieService, Features, Config) {
|
||||||
|
$scope.startSignin = function(service) {
|
||||||
|
$scope.signInStarted({'service': service});
|
||||||
|
|
||||||
|
var url = KeyService.getExternalLoginUrl(service, $scope.action || 'login');
|
||||||
|
|
||||||
|
// Save the redirect URL in a cookie so that we can redirect back after the service returns to us.
|
||||||
|
var redirectURL = $scope.redirectUrl || window.location.toString();
|
||||||
|
CookieService.putPermanent('quay.redirectAfterLoad', redirectURL);
|
||||||
|
|
||||||
|
// Needed to ensure that UI work done by the started callback is finished before the location
|
||||||
|
// changes.
|
||||||
|
$timeout(function() {
|
||||||
|
document.location = url;
|
||||||
|
}, 250);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
quayApp.directive('signinForm', function () {
|
quayApp.directive('signinForm', function () {
|
||||||
var directiveDefinitionObject = {
|
var directiveDefinitionObject = {
|
||||||
priority: 0,
|
priority: 0,
|
||||||
|
@ -2163,29 +2229,6 @@ quayApp.directive('signinForm', function () {
|
||||||
'signedIn': '&signedIn'
|
'signedIn': '&signedIn'
|
||||||
},
|
},
|
||||||
controller: function($scope, $location, $timeout, ApiService, KeyService, UserService, CookieService, Features, Config) {
|
controller: function($scope, $location, $timeout, ApiService, KeyService, UserService, CookieService, Features, Config) {
|
||||||
$scope.showGithub = function() {
|
|
||||||
if (!Features.GITHUB_LOGIN) { return; }
|
|
||||||
|
|
||||||
$scope.markStarted();
|
|
||||||
|
|
||||||
var mixpanelDistinctIdClause = '';
|
|
||||||
if (Config.MIXPANEL_KEY && mixpanel.get_distinct_id !== undefined) {
|
|
||||||
$scope.mixpanelDistinctIdClause = "&state=" + encodeURIComponent(mixpanel.get_distinct_id());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the redirect URL in a cookie so that we can redirect back after GitHub returns to us.
|
|
||||||
var redirectURL = $scope.redirectUrl || window.location.toString();
|
|
||||||
CookieService.putPermanent('quay.redirectAfterLoad', redirectURL);
|
|
||||||
|
|
||||||
// Needed to ensure that UI work done by the started callback is finished before the location
|
|
||||||
// changes.
|
|
||||||
$timeout(function() {
|
|
||||||
var url = 'https://github.com/login/oauth/authorize?client_id=' + encodeURIComponent(KeyService.githubLoginClientId) +
|
|
||||||
'&scope=user:email' + mixpanelDistinctIdClause;
|
|
||||||
document.location = url;
|
|
||||||
}, 250);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.markStarted = function() {
|
$scope.markStarted = function() {
|
||||||
if ($scope.signInStarted != null) {
|
if ($scope.signInStarted != null) {
|
||||||
$scope.signInStarted();
|
$scope.signInStarted();
|
||||||
|
@ -2235,18 +2278,9 @@ quayApp.directive('signupForm', function () {
|
||||||
scope: {
|
scope: {
|
||||||
|
|
||||||
},
|
},
|
||||||
controller: function($scope, $location, $timeout, ApiService, KeyService, UserService, Config, UIService) {
|
controller: function($scope, $location, $timeout, ApiService, KeyService, UserService, Config, UIService) {
|
||||||
$('.form-signup').popover();
|
$('.form-signup').popover();
|
||||||
|
|
||||||
if (Config.MIXPANEL_KEY) {
|
|
||||||
angulartics.waitForVendorApi(mixpanel, 500, function(loadedMixpanel) {
|
|
||||||
var mixpanelId = loadedMixpanel.get_distinct_id();
|
|
||||||
$scope.github_state_clause = '&state=' + mixpanelId;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.githubClientId = KeyService.githubLoginClientId;
|
|
||||||
|
|
||||||
$scope.awaitingConfirmation = false;
|
$scope.awaitingConfirmation = false;
|
||||||
$scope.registering = false;
|
$scope.registering = false;
|
||||||
|
|
||||||
|
|
|
@ -1681,6 +1681,10 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
|
||||||
$scope.githubLogin = resp.login;
|
$scope.githubLogin = resp.login;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($scope.cuser.logins[i].service == 'google') {
|
||||||
|
$scope.hasGoogleLogin = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1697,7 +1701,6 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
|
||||||
$scope.convertStep = 0;
|
$scope.convertStep = 0;
|
||||||
$scope.org = {};
|
$scope.org = {};
|
||||||
$scope.githubRedirectUri = KeyService.githubRedirectUri;
|
$scope.githubRedirectUri = KeyService.githubRedirectUri;
|
||||||
$scope.githubClientId = KeyService.githubLoginClientId;
|
|
||||||
$scope.authorizedApps = null;
|
$scope.authorizedApps = null;
|
||||||
|
|
||||||
$scope.logsShown = 0;
|
$scope.logsShown = 0;
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
<li quay-classes="{'!Features.BILLING': 'active'}"><a href="javascript:void(0)" data-toggle="tab" data-target="#email">Account E-mail</a></li>
|
<li quay-classes="{'!Features.BILLING': 'active'}"><a href="javascript:void(0)" data-toggle="tab" data-target="#email">Account E-mail</a></li>
|
||||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#robots">Robot Accounts</a></li>
|
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#robots">Robot Accounts</a></li>
|
||||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#password">Change Password</a></li>
|
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#password">Change Password</a></li>
|
||||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#github" quay-require="['GITHUB_LOGIN']">GitHub Login</a></li>
|
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#external" quay-show="Features.GITHUB_LOGIN || Features.GOOGLE_LOGIN">External Logins</a></li>
|
||||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#authorized" ng-click="loadAuthedApps()">Authorized Applications</a></li>
|
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#authorized" ng-click="loadAuthedApps()">Authorized Applications</a></li>
|
||||||
<li quay-show="Features.USER_LOG_ACCESS || hasPaidBusinessPlan">
|
<li quay-show="Features.USER_LOG_ACCESS || hasPaidBusinessPlan">
|
||||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#logs" ng-click="loadLogs()">Usage Logs</a>
|
<a href="javascript:void(0)" data-toggle="tab" data-target="#logs" ng-click="loadLogs()">Usage Logs</a>
|
||||||
|
@ -162,12 +162,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Github tab -->
|
<!-- External Login tab -->
|
||||||
<div id="github" class="tab-pane" quay-require="['GITHUB_LOGIN']">
|
<div id="external" class="tab-pane" quay-show="Features.GITHUB_LOGIN || Features.GOOGLE_LOGIN">
|
||||||
<div class="loading" ng-show="!cuser">
|
<div class="loading" ng-show="!cuser">
|
||||||
<div class="quay-spinner 3x"></div>
|
<div class="quay-spinner 3x"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" ng-show="cuser">
|
|
||||||
|
<!-- Github -->
|
||||||
|
<div class="row" quay-show="cuser && Features.GITHUB_LOGIN">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<div class="panel-title">GitHub Login:</div>
|
<div class="panel-title">GitHub Login:</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
@ -175,12 +177,28 @@
|
||||||
<i class="fa fa-github fa-lg" style="margin-right: 6px;" data-title="GitHub" bs-tooltip="tooltip.title"></i>
|
<i class="fa fa-github fa-lg" style="margin-right: 6px;" data-title="GitHub" bs-tooltip="tooltip.title"></i>
|
||||||
<b><a href="https://github.com/{{githubLogin}}" target="_blank">{{githubLogin}}</a></b>
|
<b><a href="https://github.com/{{githubLogin}}" target="_blank">{{githubLogin}}</a></b>
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="!githubLogin" class="col-md-8">
|
<div ng-show="!githubLogin" class="col-md-4">
|
||||||
<a href="https://github.com/login/oauth/authorize?client_id={{ githubClientId }}&scope=user:email{{ github_state_clause }}&redirect_uri={{ githubRedirectUri }}/attach" class="btn btn-primary"><i class="fa fa-github fa-lg"></i> Connect with GitHub</a>
|
<span class="external-login-button" provider="github" action="attach"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Google -->
|
||||||
|
<div class="row" quay-show="cuser && Features.GOOGLE_LOGIN">
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel-title">Google Login:</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div ng-show="hasGoogleLogin" class="lead col-md-8">
|
||||||
|
Account tied to your Google account.
|
||||||
|
</div>
|
||||||
|
<div ng-show="!hasGoogleLogin" class="col-md-4">
|
||||||
|
<span class="external-login-button" provider="google" action="attach"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Robot accounts tab -->
|
<!-- Robot accounts tab -->
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
<title>Error Logging in with GitHub · Quay.io</title>
|
<title>Error Logging in with {{ service_name }} · Quay.io</title>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block body_content %}
|
{% block body_content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<h2>There was an error logging in with GitHub.</h2>
|
<h2>There was an error logging in with {{ service_name }}.</h2>
|
||||||
|
|
||||||
{% if error_message %}
|
{% if error_message %}
|
||||||
<div class="alert alert-danger">{{ error_message }}</div>
|
<div class="alert alert-danger">{{ error_message }}</div>
|
||||||
|
@ -16,11 +16,11 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
Please register using the <a href="/">registration form</a> to continue.
|
Please register using the <a href="/">registration form</a> to continue.
|
||||||
You will be able to connect your github account to your Quay.io account
|
You will be able to connect your account to your Quay.io account
|
||||||
in the user settings.
|
in the user settings.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
Reference in a new issue