diff --git a/app.py b/app.py index 33eabf1f8..cd5c6698b 100644 --- a/app.py +++ b/app.py @@ -26,7 +26,7 @@ from data.queue import WorkQueue from util.analytics import Analytics from util.exceptionlog import Sentry from util.names import urn_generator -from util.oauth import GoogleOAuthConfig, GithubOAuthConfig +from util.oauth import GoogleOAuthConfig, GithubOAuthConfig, GitLabOAuthConfig from util.signing import Signer from util.queuemetrics import QueueMetrics from util.config.provider import FileConfigProvider, TestConfigProvider @@ -124,8 +124,9 @@ tf = app.config['DB_TRANSACTION_FACTORY'] github_login = GithubOAuthConfig(app.config, 'GITHUB_LOGIN_CONFIG') github_trigger = GithubOAuthConfig(app.config, 'GITHUB_TRIGGER_CONFIG') +gitlab_trigger = GitLabOAuthConfig(app.config, 'GITLAB_TRIGGER_CONFIG') google_login = GoogleOAuthConfig(app.config, 'GOOGLE_LOGIN_CONFIG') -oauth_apps = [github_login, github_trigger, google_login] +oauth_apps = [github_login, github_trigger, gitlab_trigger, google_login] image_diff_queue = WorkQueue(app.config['DIFFS_QUEUE_NAME'], tf) dockerfile_build_queue = WorkQueue(app.config['DOCKERFILE_BUILD_QUEUE_NAME'], tf, diff --git a/endpoints/gitlabtrigger.py b/endpoints/gitlabtrigger.py new file mode 100644 index 000000000..f9323f7ab --- /dev/null +++ b/endpoints/gitlabtrigger.py @@ -0,0 +1,50 @@ +import logging + +from flask import Blueprint, request, redirect, url_for +from flask.ext.login import current_user + +from app import app, gitlab_trigger +from auth.auth import require_session_login +from auth.permissions import AdministerRepositoryPermission +from data import model +from endpoints.common import route_show_if +from util.http import abort +from util.names import parse_repository_name + +import features + + +logger = logging.getLogger(__name__) +client = app.config['HTTPCLIENT'] +gitlabtrigger = Blueprint('gitlab', __name__) + +@gitlabtrigger.route('/gitlab/callback/trigger', methods=['GET']) +@route_show_if(features.GITLAB_BUILD) +@require_session_login +def attach_gitlab_build_trigger(): + state = request.args.get('state', None) + if not state: + abort(400) + state = state[len('repo:'):] + try: + [namespace, repository] = state.split('/') + except ValueError: + abort(400) + + permission = AdministerRepositoryPermission(namespace, repository) + if permission.can(): + code = request.args.get('code') + token = gitlab_trigger.exchange_code_for_token(app.config, client, code) + repo = model.get_repository(namespace, repository) + if not repo: + msg = 'Invalid repository: %s/%s' % (namespace, repository) + abort(404, message=msg) + + trigger = model.create_build_trigger(repo, 'gitlab', token, current_user.db_user()) + repo_path = '%s/%s' % (namespace, repository) + full_url = '%s%s%s' % (url_for('web.repository', path=repo_path), '?tab=builds&newtrigger=', trigger.uuid) + + logger.debug('Redirecting to full url: %s', full_url) + return redirect(full_url) + + abort(403) diff --git a/endpoints/oauthlogin.py b/endpoints/oauthlogin.py index 7f32ca552..269659a42 100644 --- a/endpoints/oauthlogin.py +++ b/endpoints/oauthlogin.py @@ -235,4 +235,4 @@ def github_oauth_attach(): return render_ologin_error('GitHub', err) - return redirect(url_for('web.user')) \ No newline at end of file + return redirect(url_for('web.user')) diff --git a/initdb.py b/initdb.py index f99547527..b67d0440e 100644 --- a/initdb.py +++ b/initdb.py @@ -205,6 +205,7 @@ def initialize_database(): BuildTriggerService.create(name='github') BuildTriggerService.create(name='custom-git') BuildTriggerService.create(name='bitbucket') + BuildTriggerService.create(name='gitlab') AccessTokenKind.create(name='build-worker') AccessTokenKind.create(name='pushpull-token') diff --git a/static/js/services/key-service.js b/static/js/services/key-service.js index 3f373a294..12b02b41d 100644 --- a/static/js/services/key-service.js +++ b/static/js/services/key-service.js @@ -13,7 +13,7 @@ angular.module('quay').factory('KeyService', ['$location', 'Config', function($l keyService['githubLoginClientId'] = oauth['GITHUB_LOGIN_CONFIG']['CLIENT_ID']; keyService['googleLoginClientId'] = oauth['GOOGLE_LOGIN_CONFIG']['CLIENT_ID']; - keyService['gitlabRedirectUri'] = Config.getURL('/oauth2/gitlab/callback'); + keyService['gitlabRedirectUri'] = Config.getUrl('/oauth2/gitlab/callback'); keyService['githubRedirectUri'] = Config.getUrl('/oauth2/github/callback'); keyService['googleRedirectUri'] = Config.getUrl('/oauth2/google/callback'); @@ -25,7 +25,7 @@ angular.module('quay').factory('KeyService', ['$location', 'Config', function($l keyService['githubTriggerEndpoint'] = oauth['GITHUB_TRIGGER_CONFIG']['GITHUB_ENDPOINT']; keyService['githubTriggerAuthorizeUrl'] = oauth['GITHUB_TRIGGER_CONFIG']['AUTHORIZE_ENDPOINT']; - keySerivce['gitlabTriggerEndpoint'] = oauth['GITLAB_TRIGGER_CONFIG']['GITLAB_ENDPOINT']; + keyService['gitlabTriggerEndpoint'] = oauth['GITLAB_TRIGGER_CONFIG']['GITLAB_ENDPOINT']; keyService['gitlabTriggerAuthorizeUrl'] = oauth['GITLAB_TRIGGER_CONFIG']['AUTHORIZE_ENDPOINT']; keyService['githubLoginScope'] = 'user:email'; diff --git a/static/js/services/trigger-service.js b/static/js/services/trigger-service.js index 42202ee34..9ba657967 100644 --- a/static/js/services/trigger-service.js +++ b/static/js/services/trigger-service.js @@ -104,12 +104,11 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K } ], 'get_redirect_url': function(namespace, repository) { - var redirect_uri = KeyService['gitlabRedirectUri'] + '/trigger/' + - namespace + '/' + repository; + var redirect_uri = KeyService['gitlabRedirectUri'] + '/trigger'; var authorize_url = KeyService['gitlabTriggerAuthorizeUrl']; var client_id = KeyService['gitlabTriggerClientId']; - return authorize_url + 'client_id=' + client_id + '&redirect_uri=' + redirect_uri; + return authorize_url + '?client_id=' + client_id + '&redirect_uri=' + redirect_uri + '&response_type=code&state=repo:' + namespace + '/' + repository; }, 'is_external': false, 'is_enabled': function() { diff --git a/test/data/test.db b/test/data/test.db index 52b190882..c90d531f0 100644 Binary files a/test/data/test.db and b/test/data/test.db differ diff --git a/util/oauth.py b/util/oauth.py index 3e920110a..a9a571eae 100644 --- a/util/oauth.py +++ b/util/oauth.py @@ -15,9 +15,6 @@ class OAuthConfig(object): def user_endpoint(self): raise NotImplementedError - def login_endpoint(self): - raise NotImplementedError - def validate_client_id_and_secret(self, http_client): raise NotImplementedError @@ -200,4 +197,31 @@ class GoogleOAuthConfig(OAuthConfig): } +class GitLabOAuthConfig(OAuthConfig): + def __init__(self, config, key_name): + super(GitLabOAuthConfig, self).__init__(config, key_name) + def _endpoint(self): + endpoint = self.config.get('GITLAB_ENDPOINT', 'https://gitlab.com') + if not endpoint.endswith('/'): + endpoint = endpoint + '/' + return endpoint + + def service_name(self): + return 'GitLab' + + def authorize_endpoint(self): + return self._get_url(self._endpoint(), '/oauth/authorize') + + def token_endpoint(self): + return self._get_url(self._endpoint(), '/oauth/token') + + def validate_client_id_and_secret(self, http_client): + pass + + def get_public_config(self): + return { + 'CLIENT_ID': self.client_id(), + 'AUTHORIZE_ENDPOINT': self.authorize_endpoint(), + 'GITLAB_ENDPOINT': self._endpoint(), + } diff --git a/web.py b/web.py index f90eecc9f..b71884583 100644 --- a/web.py +++ b/web.py @@ -1,6 +1,3 @@ -import logging -import logging.config - from app import app as application from endpoints.api import api_bp @@ -9,10 +6,12 @@ from endpoints.webhooks import webhooks from endpoints.realtime import realtime from endpoints.oauthlogin import oauthlogin from endpoints.githubtrigger import githubtrigger +from endpoints.gitlabtrigger import gitlabtrigger from endpoints.bitbuckettrigger import bitbuckettrigger application.register_blueprint(web) application.register_blueprint(githubtrigger, url_prefix='/oauth2') +application.register_blueprint(gitlabtrigger, url_prefix='/oauth2') application.register_blueprint(oauthlogin, url_prefix='/oauth2') application.register_blueprint(bitbuckettrigger, url_prefix='/oauth1') application.register_blueprint(api_bp, url_prefix='/api')