- Make the OAuth config system centralized
- Add support for Github Enterprise login
This commit is contained in:
parent
6deafe8c86
commit
3e79379942
11 changed files with 196 additions and 83 deletions
6
app.py
6
app.py
|
@ -19,6 +19,7 @@ from util.analytics import Analytics
|
||||||
from util.exceptionlog import Sentry
|
from util.exceptionlog import Sentry
|
||||||
from util.queuemetrics import QueueMetrics
|
from util.queuemetrics import QueueMetrics
|
||||||
from util.names import urn_generator
|
from util.names import urn_generator
|
||||||
|
from util.oauth import GoogleOAuthConfig, GithubOAuthConfig
|
||||||
from data.billing import Billing
|
from data.billing import Billing
|
||||||
from data.buildlogs import BuildLogs
|
from data.buildlogs import BuildLogs
|
||||||
from data.archivedlogs import LogArchive
|
from data.archivedlogs import LogArchive
|
||||||
|
@ -131,6 +132,11 @@ queue_metrics = QueueMetrics(app)
|
||||||
authentication = UserAuthentication(app)
|
authentication = UserAuthentication(app)
|
||||||
userevents = UserEventsBuilderModule(app)
|
userevents = UserEventsBuilderModule(app)
|
||||||
|
|
||||||
|
github_login = GithubOAuthConfig(app, 'GITHUB_LOGIN_CONFIG')
|
||||||
|
github_trigger = GithubOAuthConfig(app, 'GITHUB_TRIGGER_CONFIG')
|
||||||
|
google_login = GoogleOAuthConfig(app, 'GOOGLE_LOGIN_CONFIG')
|
||||||
|
oauth_apps = [github_login, github_trigger, google_login]
|
||||||
|
|
||||||
tf = app.config['DB_TRANSACTION_FACTORY']
|
tf = app.config['DB_TRANSACTION_FACTORY']
|
||||||
image_diff_queue = WorkQueue(app.config['DIFFS_QUEUE_NAME'], tf)
|
image_diff_queue = WorkQueue(app.config['DIFFS_QUEUE_NAME'], tf)
|
||||||
dockerfile_build_queue = WorkQueue(app.config['DOCKERFILE_BUILD_QUEUE_NAME'], tf,
|
dockerfile_build_queue = WorkQueue(app.config['DOCKERFILE_BUILD_QUEUE_NAME'], tf,
|
||||||
|
|
26
config.py
26
config.py
|
@ -15,11 +15,10 @@ def build_requests_session():
|
||||||
|
|
||||||
|
|
||||||
# The set of configuration key names that will be accessible in the client. Since these
|
# The set of configuration key names that will be accessible in the client. Since these
|
||||||
# values are set to the frontend, DO NOT PLACE ANY SECRETS OR KEYS in this list.
|
# values are sent to the frontend, DO NOT PLACE ANY SECRETS OR KEYS in this list.
|
||||||
CLIENT_WHITELIST = ['SERVER_HOSTNAME', 'PREFERRED_URL_SCHEME', 'GITHUB_CLIENT_ID',
|
CLIENT_WHITELIST = ['SERVER_HOSTNAME', 'PREFERRED_URL_SCHEME', 'MIXPANEL_KEY',
|
||||||
'GITHUB_LOGIN_CLIENT_ID', 'MIXPANEL_KEY', 'STRIPE_PUBLISHABLE_KEY',
|
'STRIPE_PUBLISHABLE_KEY', 'ENTERPRISE_LOGO_URL', 'SENTRY_PUBLIC_DSN',
|
||||||
'ENTERPRISE_LOGO_URL', 'SENTRY_PUBLIC_DSN', 'AUTHENTICATION_TYPE',
|
'AUTHENTICATION_TYPE', 'REGISTRY_TITLE', 'REGISTRY_TITLE_SHORT',
|
||||||
'REGISTRY_TITLE', 'REGISTRY_TITLE_SHORT', 'GOOGLE_LOGIN_CLIENT_ID',
|
|
||||||
'CONTACT_INFO']
|
'CONTACT_INFO']
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,22 +107,11 @@ class DefaultConfig(object):
|
||||||
SENTRY_PUBLIC_DSN = None
|
SENTRY_PUBLIC_DSN = None
|
||||||
|
|
||||||
# Github Config
|
# Github Config
|
||||||
GITHUB_TOKEN_URL = 'https://github.com/login/oauth/access_token'
|
GITHUB_LOGIN_CONFIG = None
|
||||||
GITHUB_USER_URL = 'https://api.github.com/user'
|
GITHUB_TRIGGER_CONFIG = None
|
||||||
GITHUB_USER_EMAILS = GITHUB_USER_URL + '/emails'
|
|
||||||
|
|
||||||
GITHUB_CLIENT_ID = ''
|
|
||||||
GITHUB_CLIENT_SECRET = ''
|
|
||||||
|
|
||||||
GITHUB_LOGIN_CLIENT_ID = ''
|
|
||||||
GITHUB_LOGIN_CLIENT_SECRET = ''
|
|
||||||
|
|
||||||
# Google Config.
|
# Google Config.
|
||||||
GOOGLE_TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'
|
GOOGLE_LOGIN_CONFIG = None
|
||||||
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()
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import logging
|
import logging
|
||||||
|
import requests
|
||||||
|
|
||||||
from flask import request, redirect, url_for, Blueprint
|
from flask import request, redirect, url_for, Blueprint
|
||||||
from flask.ext.login import current_user
|
from flask.ext.login import current_user
|
||||||
|
|
||||||
from endpoints.common import render_page_template, common_login, route_show_if
|
from endpoints.common import render_page_template, common_login, route_show_if
|
||||||
from app import app, analytics, get_app_url
|
from app import app, analytics, get_app_url, github_login, google_login, github_trigger
|
||||||
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.validation import generate_valid_usernames
|
||||||
|
@ -29,20 +30,16 @@ def render_ologin_error(service_name,
|
||||||
service_url=get_app_url(),
|
service_url=get_app_url(),
|
||||||
user_creation=features.USER_CREATION)
|
user_creation=features.USER_CREATION)
|
||||||
|
|
||||||
def exchange_code_for_token(code, service_name='GITHUB', for_login=True, form_encode=False,
|
def exchange_code_for_token(code, service, form_encode=False, redirect_suffix=''):
|
||||||
redirect_suffix=''):
|
|
||||||
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[id_config],
|
'client_id': service.client_id(),
|
||||||
'client_secret': app.config[secret_config],
|
'client_secret': service.client_secret(),
|
||||||
'code': code,
|
'code': code,
|
||||||
'grant_type': 'authorization_code',
|
'grant_type': 'authorization_code',
|
||||||
'redirect_uri': '%s://%s/oauth2/%s/callback%s' % (app.config['PREFERRED_URL_SCHEME'],
|
'redirect_uri': '%s://%s/oauth2/%s/callback%s' % (app.config['PREFERRED_URL_SCHEME'],
|
||||||
app.config['SERVER_HOSTNAME'],
|
app.config['SERVER_HOSTNAME'],
|
||||||
service_name.lower(),
|
service.service_name().lower(),
|
||||||
redirect_suffix)
|
redirect_suffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,12 +47,11 @@ def exchange_code_for_token(code, service_name='GITHUB', for_login=True, form_en
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token_url = service.token_endpoint()
|
||||||
if form_encode:
|
if form_encode:
|
||||||
get_access_token = client.post(app.config[service_name + '_TOKEN_URL'],
|
get_access_token = client.post(token_url, data=payload, headers=headers)
|
||||||
data=payload, headers=headers)
|
|
||||||
else:
|
else:
|
||||||
get_access_token = client.post(app.config[service_name + '_TOKEN_URL'],
|
get_access_token = client.post(token_url, params=payload, headers=headers)
|
||||||
params=payload, headers=headers)
|
|
||||||
|
|
||||||
json_data = get_access_token.json()
|
json_data = get_access_token.json()
|
||||||
if not json_data:
|
if not json_data:
|
||||||
|
@ -65,25 +61,20 @@ def exchange_code_for_token(code, service_name='GITHUB', for_login=True, form_en
|
||||||
return token
|
return token
|
||||||
|
|
||||||
|
|
||||||
def get_github_user(token):
|
def get_user(service, token):
|
||||||
token_param = {
|
|
||||||
'access_token': token,
|
|
||||||
}
|
|
||||||
get_user = client.get(app.config['GITHUB_USER_URL'], params=token_param)
|
|
||||||
|
|
||||||
return get_user.json()
|
|
||||||
|
|
||||||
|
|
||||||
def get_google_user(token):
|
|
||||||
token_param = {
|
token_param = {
|
||||||
'access_token': token,
|
'access_token': token,
|
||||||
'alt': 'json',
|
'alt': 'json',
|
||||||
}
|
}
|
||||||
|
get_user = client.get(service.user_endpoint(), params=token_param)
|
||||||
|
if get_user.status_code != requests.codes.ok:
|
||||||
|
return {}
|
||||||
|
|
||||||
get_user = client.get(app.config['GOOGLE_USER_URL'], params=token_param)
|
|
||||||
return get_user.json()
|
return get_user.json()
|
||||||
|
|
||||||
def conduct_oauth_login(service_name, user_id, username, email, metadata={}):
|
|
||||||
|
def conduct_oauth_login(service, user_id, username, email, metadata={}):
|
||||||
|
service_name = service.service_name()
|
||||||
to_login = model.verify_federated_login(service_name.lower(), user_id)
|
to_login = model.verify_federated_login(service_name.lower(), user_id)
|
||||||
if not to_login:
|
if not to_login:
|
||||||
# See if we can create a new user.
|
# See if we can create a new user.
|
||||||
|
@ -138,8 +129,8 @@ def google_oauth_callback():
|
||||||
if error:
|
if error:
|
||||||
return render_ologin_error('Google', error)
|
return render_ologin_error('Google', error)
|
||||||
|
|
||||||
token = exchange_code_for_token(request.args.get('code'), service_name='GOOGLE', form_encode=True)
|
token = exchange_code_for_token(request.args.get('code'), google_login, form_encode=True)
|
||||||
user_data = get_google_user(token)
|
user_data = get_user(google_login, token)
|
||||||
if not user_data or not user_data.get('id', None) or not user_data.get('email', None):
|
if not user_data or not user_data.get('id', None) or not user_data.get('email', None):
|
||||||
return render_ologin_error('Google')
|
return render_ologin_error('Google')
|
||||||
|
|
||||||
|
@ -148,7 +139,7 @@ def google_oauth_callback():
|
||||||
'service_username': user_data['email']
|
'service_username': user_data['email']
|
||||||
}
|
}
|
||||||
|
|
||||||
return conduct_oauth_login('Google', user_data['id'], username, user_data['email'],
|
return conduct_oauth_login(google_login, user_data['id'], username, user_data['email'],
|
||||||
metadata=metadata)
|
metadata=metadata)
|
||||||
|
|
||||||
|
|
||||||
|
@ -159,8 +150,8 @@ def github_oauth_callback():
|
||||||
if error:
|
if error:
|
||||||
return render_ologin_error('GitHub', error)
|
return render_ologin_error('GitHub', error)
|
||||||
|
|
||||||
token = exchange_code_for_token(request.args.get('code'), service_name='GITHUB')
|
token = exchange_code_for_token(request.args.get('code'), github_login)
|
||||||
user_data = get_github_user(token)
|
user_data = get_user(github_login, token)
|
||||||
if not user_data or not 'login' in user_data:
|
if not user_data or not 'login' in user_data:
|
||||||
return render_ologin_error('GitHub')
|
return render_ologin_error('GitHub')
|
||||||
|
|
||||||
|
@ -174,7 +165,7 @@ def github_oauth_callback():
|
||||||
token_param = {
|
token_param = {
|
||||||
'access_token': token,
|
'access_token': token,
|
||||||
}
|
}
|
||||||
get_email = client.get(app.config['GITHUB_USER_EMAILS'], params=token_param,
|
get_email = client.get(github_login.email_endpoint(), params=token_param,
|
||||||
headers=v3_media_type)
|
headers=v3_media_type)
|
||||||
|
|
||||||
# We will accept any email, but we prefer the primary
|
# We will accept any email, but we prefer the primary
|
||||||
|
@ -188,17 +179,17 @@ def github_oauth_callback():
|
||||||
'service_username': username
|
'service_username': username
|
||||||
}
|
}
|
||||||
|
|
||||||
return conduct_oauth_login('github', github_id, username, found_email, metadata=metadata)
|
return conduct_oauth_login(github_login, github_id, username, found_email, metadata=metadata)
|
||||||
|
|
||||||
|
|
||||||
@callback.route('/google/callback/attach', methods=['GET'])
|
@callback.route('/google/callback/attach', methods=['GET'])
|
||||||
@route_show_if(features.GOOGLE_LOGIN)
|
@route_show_if(features.GOOGLE_LOGIN)
|
||||||
@require_session_login
|
@require_session_login
|
||||||
def google_oauth_attach():
|
def google_oauth_attach():
|
||||||
token = exchange_code_for_token(request.args.get('code'), service_name='GOOGLE',
|
token = exchange_code_for_token(request.args.get('code'), google_login,
|
||||||
redirect_suffix='/attach', form_encode=True)
|
redirect_suffix='/attach', form_encode=True)
|
||||||
|
|
||||||
user_data = get_google_user(token)
|
user_data = get_user(google_login, token)
|
||||||
if not user_data or not user_data.get('id', None):
|
if not user_data or not user_data.get('id', None):
|
||||||
return render_ologin_error('Google')
|
return render_ologin_error('Google')
|
||||||
|
|
||||||
|
@ -224,8 +215,8 @@ def google_oauth_attach():
|
||||||
@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_code_for_token(request.args.get('code'), service_name='GITHUB')
|
token = exchange_code_for_token(request.args.get('code'), github_login)
|
||||||
user_data = get_github_user(token)
|
user_data = get_user(github_login, token)
|
||||||
if not user_data:
|
if not user_data:
|
||||||
return render_ologin_error('GitHub')
|
return render_ologin_error('GitHub')
|
||||||
|
|
||||||
|
@ -255,8 +246,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_code_for_token(request.args.get('code'), service_name='GITHUB',
|
token = exchange_code_for_token(request.args.get('code'), github_trigger)
|
||||||
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)
|
||||||
|
|
|
@ -11,7 +11,8 @@ from random import SystemRandom
|
||||||
|
|
||||||
from data import model
|
from data import model
|
||||||
from data.database import db
|
from data.database import db
|
||||||
from app import app, login_manager, dockerfile_build_queue, notification_queue
|
from app import app, login_manager, dockerfile_build_queue, notification_queue, oauth_apps
|
||||||
|
|
||||||
from auth.permissions import QuayDeferredPermissionUser
|
from auth.permissions import QuayDeferredPermissionUser
|
||||||
from auth import scopes
|
from auth import scopes
|
||||||
from endpoints.api.discovery import swagger_route_data
|
from endpoints.api.discovery import swagger_route_data
|
||||||
|
@ -176,6 +177,16 @@ def render_page_template(name, **kwargs):
|
||||||
external_styles = get_external_css(local=not app.config.get('USE_CDN', True))
|
external_styles = get_external_css(local=not app.config.get('USE_CDN', True))
|
||||||
external_scripts = get_external_javascript(local=not app.config.get('USE_CDN', True))
|
external_scripts = get_external_javascript(local=not app.config.get('USE_CDN', True))
|
||||||
|
|
||||||
|
def get_oauth_config():
|
||||||
|
oauth_config = {}
|
||||||
|
for oauth_app in oauth_apps:
|
||||||
|
oauth_config[oauth_app.key_name] = {
|
||||||
|
'CLIENT_ID': oauth_app.client_id(),
|
||||||
|
'AUTHORIZE_ENDPOINT': oauth_app.authorize_endpoint()
|
||||||
|
}
|
||||||
|
|
||||||
|
return oauth_config
|
||||||
|
|
||||||
contact_href = None
|
contact_href = None
|
||||||
if len(app.config.get('CONTACT_INFO', [])) == 1:
|
if len(app.config.get('CONTACT_INFO', [])) == 1:
|
||||||
contact_href = app.config['CONTACT_INFO'][0]
|
contact_href = app.config['CONTACT_INFO'][0]
|
||||||
|
@ -189,6 +200,7 @@ def render_page_template(name, **kwargs):
|
||||||
library_scripts=library_scripts,
|
library_scripts=library_scripts,
|
||||||
feature_set=json.dumps(features.get_features()),
|
feature_set=json.dumps(features.get_features()),
|
||||||
config_set=json.dumps(getFrontendVisibleConfig(app.config)),
|
config_set=json.dumps(getFrontendVisibleConfig(app.config)),
|
||||||
|
oauth_set=json.dumps(get_oauth_config()),
|
||||||
mixpanel_key=app.config.get('MIXPANEL_KEY', ''),
|
mixpanel_key=app.config.get('MIXPANEL_KEY', ''),
|
||||||
google_analytics_key=app.config.get('GOOGLE_ANALYTICS_KEY', ''),
|
google_analytics_key=app.config.get('GOOGLE_ANALYTICS_KEY', ''),
|
||||||
sentry_public_dsn=app.config.get('SENTRY_PUBLIC_DSN', ''),
|
sentry_public_dsn=app.config.get('SENTRY_PUBLIC_DSN', ''),
|
||||||
|
|
|
@ -8,7 +8,7 @@ import re
|
||||||
from github import Github, UnknownObjectException, GithubException
|
from github import Github, UnknownObjectException, GithubException
|
||||||
from tempfile import SpooledTemporaryFile
|
from tempfile import SpooledTemporaryFile
|
||||||
|
|
||||||
from app import app, userfiles as user_files
|
from app import app, userfiles as user_files, github_trigger
|
||||||
from util.tarfileappender import TarfileAppender
|
from util.tarfileappender import TarfileAppender
|
||||||
|
|
||||||
|
|
||||||
|
@ -150,8 +150,8 @@ def raise_unsupported():
|
||||||
class GithubBuildTrigger(BuildTrigger):
|
class GithubBuildTrigger(BuildTrigger):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_client(auth_token):
|
def _get_client(auth_token):
|
||||||
return Github(auth_token, client_id=app.config['GITHUB_CLIENT_ID'],
|
return Github(auth_token, client_id=github_trigger.client_id(),
|
||||||
client_secret=app.config['GITHUB_CLIENT_SECRET'])
|
client_secret=github_trigger.client_secret())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def service_name(cls):
|
def service_name(cls):
|
||||||
|
|
|
@ -2,8 +2,15 @@
|
||||||
<span ng-if="provider == 'github'">
|
<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" ng-disabled="signingIn">
|
<a href="javascript:void(0)" class="btn btn-primary btn-block" quay-require="['GITHUB_LOGIN']" ng-click="startSignin('github')" style="margin-bottom: 10px" ng-disabled="signingIn">
|
||||||
<i class="fa fa-github fa-lg"></i>
|
<i class="fa fa-github fa-lg"></i>
|
||||||
<span ng-if="action != 'attach'">Sign In with GitHub</span>
|
<span ng-if="action != 'attach'">
|
||||||
<span ng-if="action == 'attach'">Attach to GitHub Account</span>
|
Sign In with GitHub
|
||||||
|
<span ng-if="isEnterprise('github')">Enterprise</span>
|
||||||
|
</span>
|
||||||
|
<span ng-if="action == 'attach'">
|
||||||
|
Attach to GitHub
|
||||||
|
<span ng-if="isEnterprise('github')">Enterprise</span>
|
||||||
|
Account
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
|
@ -620,7 +620,8 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|
||||||
$provide.factory('TriggerService', ['UtilService', '$sanitize', function(UtilService, $sanitize) {
|
$provide.factory('TriggerService', ['UtilService', '$sanitize', 'KeyService',
|
||||||
|
function(UtilService, $sanitize, KeyService) {
|
||||||
var triggerService = {};
|
var triggerService = {};
|
||||||
|
|
||||||
var triggerTypes = {
|
var triggerTypes = {
|
||||||
|
@ -639,9 +640,28 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
'type': 'option',
|
'type': 'option',
|
||||||
'name': 'branch_name'
|
'name': 'branch_name'
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
|
||||||
|
'get_redirect_url': function(namespace, repository) {
|
||||||
|
var redirect_uri = KeyService['githubRedirectUri'] + '/trigger/' +
|
||||||
|
namespace + '/' + repository;
|
||||||
|
|
||||||
|
var authorize_url = KeyService['githubTriggerAuthorizeUrl'];
|
||||||
|
var client_id = KeyService['githubTriggerClientId'];
|
||||||
|
|
||||||
|
return authorize_url + 'client_id=' + client_id +
|
||||||
|
'&scope=repo,user:email&redirect_uri=' + redirect_uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerService.getRedirectUrl = function(name, namespace, repository) {
|
||||||
|
var type = triggerTypes[name];
|
||||||
|
if (!type) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return type['get_redirect_url'](namespace, repository);
|
||||||
|
};
|
||||||
|
|
||||||
triggerService.getDescription = function(name, config) {
|
triggerService.getDescription = function(name, config) {
|
||||||
var type = triggerTypes[name];
|
var type = triggerTypes[name];
|
||||||
|
@ -1693,21 +1713,29 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
|
|
||||||
$provide.factory('KeyService', ['$location', 'Config', function($location, Config) {
|
$provide.factory('KeyService', ['$location', 'Config', function($location, Config) {
|
||||||
var keyService = {}
|
var keyService = {}
|
||||||
|
var oauth = window.__oauth;
|
||||||
|
|
||||||
keyService['stripePublishableKey'] = Config['STRIPE_PUBLISHABLE_KEY'];
|
keyService['stripePublishableKey'] = Config['STRIPE_PUBLISHABLE_KEY'];
|
||||||
|
|
||||||
keyService['githubClientId'] = Config['GITHUB_CLIENT_ID'];
|
keyService['githubTriggerClientId'] = oauth['GITHUB_TRIGGER_CONFIG']['CLIENT_ID'];
|
||||||
keyService['githubLoginClientId'] = Config['GITHUB_LOGIN_CLIENT_ID'];
|
keyService['githubLoginClientId'] = oauth['GITHUB_LOGIN_CONFIG']['CLIENT_ID'];
|
||||||
keyService['githubRedirectUri'] = Config.getUrl('/oauth2/github/callback');
|
keyService['googleLoginClientId'] = oauth['GOOGLE_LOGIN_CONFIG']['CLIENT_ID'];
|
||||||
|
|
||||||
keyService['googleLoginClientId'] = Config['GOOGLE_LOGIN_CLIENT_ID'];
|
keyService['githubRedirectUri'] = Config.getUrl('/oauth2/github/callback');
|
||||||
keyService['googleRedirectUri'] = Config.getUrl('/oauth2/google/callback');
|
keyService['googleRedirectUri'] = Config.getUrl('/oauth2/google/callback');
|
||||||
|
|
||||||
keyService['googleLoginUrl'] = 'https://accounts.google.com/o/oauth2/auth?response_type=code&';
|
keyService['githubLoginUrl'] = oauth['GITHUB_LOGIN_CONFIG']['AUTHORIZE_ENDPOINT'];
|
||||||
keyService['githubLoginUrl'] = Config['GITHUB_LOGIN_URL'] + '?';
|
keyService['googleLoginUrl'] = oauth['GOOGLE_LOGIN_CONFIG']['AUTHORIZE_ENDPOINT'];
|
||||||
|
|
||||||
|
keyService['githubTriggerAuthorizeUrl'] = oauth['GITHUB_LOGIN_CONFIG']['AUTHORIZE_ENDPOINT'];
|
||||||
|
|
||||||
keyService['googleLoginScope'] = 'openid email';
|
|
||||||
keyService['githubLoginScope'] = 'user:email';
|
keyService['githubLoginScope'] = 'user:email';
|
||||||
|
keyService['googleLoginScope'] = 'openid email';
|
||||||
|
|
||||||
|
keyService.isEnterprise = function(service) {
|
||||||
|
var isGithubEnterprise = keyService['githubLoginUrl'].indexOf('https://github.com/') < 0;
|
||||||
|
return service == 'github' && isGithubEnterprise;
|
||||||
|
};
|
||||||
|
|
||||||
keyService.getExternalLoginUrl = function(service, action) {
|
keyService.getExternalLoginUrl = function(service, action) {
|
||||||
var state_clause = '';
|
var state_clause = '';
|
||||||
|
@ -2688,6 +2716,8 @@ quayApp.directive('externalLoginButton', function () {
|
||||||
},
|
},
|
||||||
controller: function($scope, $timeout, $interval, ApiService, KeyService, CookieService, Features, Config) {
|
controller: function($scope, $timeout, $interval, ApiService, KeyService, CookieService, Features, Config) {
|
||||||
$scope.signingIn = false;
|
$scope.signingIn = false;
|
||||||
|
$scope.isEnterprise = KeyService.isEnterprise;
|
||||||
|
|
||||||
$scope.startSignin = function(service) {
|
$scope.startSignin = function(service) {
|
||||||
$scope.signInStarted({'service': service});
|
$scope.signInStarted({'service': service});
|
||||||
|
|
||||||
|
|
|
@ -1330,15 +1330,13 @@ function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, TriggerServi
|
||||||
var name = $routeParams.name;
|
var name = $routeParams.name;
|
||||||
|
|
||||||
$scope.Features = Features;
|
$scope.Features = Features;
|
||||||
|
$scope.TriggerService = TriggerService;
|
||||||
|
|
||||||
$scope.permissions = {'team': [], 'user': [], 'loading': 2};
|
$scope.permissions = {'team': [], 'user': [], 'loading': 2};
|
||||||
$scope.logsShown = 0;
|
$scope.logsShown = 0;
|
||||||
$scope.deleting = false;
|
$scope.deleting = false;
|
||||||
|
|
||||||
$scope.permissionCache = {};
|
$scope.permissionCache = {};
|
||||||
|
|
||||||
$scope.githubRedirectUri = KeyService.githubRedirectUri;
|
|
||||||
$scope.githubClientId = KeyService.githubClientId;
|
|
||||||
|
|
||||||
$scope.showTriggerSetupCounter = 0;
|
$scope.showTriggerSetupCounter = 0;
|
||||||
|
|
||||||
$scope.getBadgeFormat = function(format, repo) {
|
$scope.getBadgeFormat = function(format, repo) {
|
||||||
|
@ -2030,12 +2028,10 @@ function V1Ctrl($scope, $location, UserService) {
|
||||||
UserService.updateUserIn($scope);
|
UserService.updateUserIn($scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService, PlanService, KeyService, Features) {
|
function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService, PlanService, TriggerService, Features) {
|
||||||
UserService.updateUserIn($scope);
|
UserService.updateUserIn($scope);
|
||||||
|
|
||||||
$scope.Features = Features;
|
$scope.Features = Features;
|
||||||
$scope.githubRedirectUri = KeyService.githubRedirectUri;
|
|
||||||
$scope.githubClientId = KeyService.githubClientId;
|
|
||||||
|
|
||||||
$scope.repo = {
|
$scope.repo = {
|
||||||
'is_public': 0,
|
'is_public': 0,
|
||||||
|
@ -2114,9 +2110,7 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService
|
||||||
|
|
||||||
// Conduct the Github redirect if applicable.
|
// Conduct the Github redirect if applicable.
|
||||||
if ($scope.repo.initialize == 'github') {
|
if ($scope.repo.initialize == 'github') {
|
||||||
window.location = 'https://github.com/login/oauth/authorize?client_id=' + $scope.githubClientId +
|
window.location = TriggerService.getRedirectUrl('github', repo.namespace, repo.name);
|
||||||
'&scope=repo,user:email&redirect_uri=' + $scope.githubRedirectUri + '/trigger/' +
|
|
||||||
repo.namespace + '/' + repo.name;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -306,7 +306,11 @@
|
||||||
<b class="caret"></b>
|
<b class="caret"></b>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu dropdown-menu-right pull-right">
|
<ul class="dropdown-menu dropdown-menu-right pull-right">
|
||||||
<li><a href="https://github.com/login/oauth/authorize?client_id={{ githubClientId }}&scope=repo,user:email&redirect_uri={{ githubRedirectUri }}/trigger/{{ repo.namespace }}/{{ repo.name }}"><i class="fa fa-github fa-lg"></i>GitHub - Repository Push</a></li>
|
<li>
|
||||||
|
<a href="{{ TriggerService.getRedirectUrl('github', repo.namespace, repo.name) }}">
|
||||||
|
<i class="fa fa-github fa-lg"></i>GitHub - Repository Push
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
window.__endpoints = {{ route_data|safe }}.apis;
|
window.__endpoints = {{ route_data|safe }}.apis;
|
||||||
window.__features = {{ feature_set|safe }};
|
window.__features = {{ feature_set|safe }};
|
||||||
window.__config = {{ config_set|safe }};
|
window.__config = {{ config_set|safe }};
|
||||||
|
window.__oauth = {{ oauth_set|safe }};
|
||||||
window.__token = '{{ csrf_token() }}';
|
window.__token = '{{ csrf_token() }}';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
81
util/oauth.py
Normal file
81
util/oauth.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
import urlparse
|
||||||
|
|
||||||
|
class OAuthConfig(object):
|
||||||
|
def __init__(self, app, key_name):
|
||||||
|
self.key_name = key_name
|
||||||
|
self.config = app.config.get(key_name, {})
|
||||||
|
|
||||||
|
def service_name(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def token_endpoint(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def user_endpoint(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def login_endpoint(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def client_id(self):
|
||||||
|
return self.config.get('CLIENT_ID')
|
||||||
|
|
||||||
|
def client_secret(self):
|
||||||
|
return self.config.get('CLIENT_SECRET')
|
||||||
|
|
||||||
|
def _get_url(self, endpoint, *args):
|
||||||
|
if not endpoint:
|
||||||
|
raise Exception('Missing endpoint configuration for OAuth config %s', self.key_name)
|
||||||
|
|
||||||
|
for arg in args:
|
||||||
|
endpoint = urlparse.urljoin(endpoint, arg)
|
||||||
|
|
||||||
|
return endpoint
|
||||||
|
|
||||||
|
|
||||||
|
class GithubOAuthConfig(OAuthConfig):
|
||||||
|
def __init__(self, app, key_name):
|
||||||
|
super(GithubOAuthConfig, self).__init__(app, key_name)
|
||||||
|
|
||||||
|
def service_name(self):
|
||||||
|
return 'GitHub'
|
||||||
|
|
||||||
|
def authorize_endpoint(self):
|
||||||
|
endpoint = self.config.get('GITHUB_ENDPOINT')
|
||||||
|
return self._get_url(endpoint, '/login/oauth/authorize') + '?'
|
||||||
|
|
||||||
|
def token_endpoint(self):
|
||||||
|
endpoint = self.config.get('GITHUB_ENDPOINT')
|
||||||
|
return self._get_url(endpoint, '/login/oauth/access_token')
|
||||||
|
|
||||||
|
def _api_endpoint(self):
|
||||||
|
endpoint = self.config.get('GITHUB_ENDPOINT')
|
||||||
|
return self.config.get('API_ENDPOINT', self._get_url(endpoint, '/api/v3/'))
|
||||||
|
|
||||||
|
def user_endpoint(self):
|
||||||
|
api_endpoint = self._api_endpoint()
|
||||||
|
return self._get_url(api_endpoint, 'user')
|
||||||
|
|
||||||
|
def email_endpoint(self):
|
||||||
|
api_endpoint = self._api_endpoint()
|
||||||
|
return self._get_url(api_endpoint, 'user/emails')
|
||||||
|
|
||||||
|
|
||||||
|
class GoogleOAuthConfig(OAuthConfig):
|
||||||
|
def __init__(self, app, key_name):
|
||||||
|
super(GoogleOAuthConfig, self).__init__(app, key_name)
|
||||||
|
|
||||||
|
def service_name(self):
|
||||||
|
return 'Google'
|
||||||
|
|
||||||
|
def authorize_endpoint(self):
|
||||||
|
return 'https://accounts.google.com/o/oauth2/auth?response_type=code&'
|
||||||
|
|
||||||
|
def token_endpoint(self):
|
||||||
|
return 'https://accounts.google.com/o/oauth2/token'
|
||||||
|
|
||||||
|
def user_endpoint(self):
|
||||||
|
return 'https://www.googleapis.com/oauth2/v1/userinfo'
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in a new issue