Merge pull request #1092 from jzelinskie/gitlab
Allow for custom Gitlab host
This commit is contained in:
commit
408ac90346
6 changed files with 62 additions and 38 deletions
|
@ -1,8 +1,9 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from urlparse import urljoin
|
||||||
|
|
||||||
from app import app
|
from app import app, gitlab_trigger
|
||||||
|
|
||||||
from jsonschema import validate
|
from jsonschema import validate
|
||||||
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
||||||
|
@ -127,9 +128,8 @@ class GitLabBuildTrigger(BuildTriggerHandler):
|
||||||
return 'gitlab'
|
return 'gitlab'
|
||||||
|
|
||||||
def _get_authorized_client(self):
|
def _get_authorized_client(self):
|
||||||
host = app.config.get('GITLAB_TRIGGER_CONFIG', {}).get('GITLAB_ENDPOINT', '')
|
|
||||||
auth_token = self.auth_token or 'invalid'
|
auth_token = self.auth_token or 'invalid'
|
||||||
return gitlab.Gitlab(host, oauth_token=auth_token, timeout=5)
|
return gitlab.Gitlab(gitlab_trigger.api_endpoint(), oauth_token=auth_token, timeout=5)
|
||||||
|
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
return 'hook_id' in self.config
|
return 'hook_id' in self.config
|
||||||
|
@ -318,7 +318,7 @@ class GitLabBuildTrigger(BuildTriggerHandler):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_repository_url(self):
|
def get_repository_url(self):
|
||||||
return 'https://gitlab.com/%s' % self.config['build_source']
|
return gitlab_trigger.get_public_url(self.config['build_source'])
|
||||||
|
|
||||||
@_catch_timeouts
|
@_catch_timeouts
|
||||||
def lookup_user(self, email):
|
def lookup_user(self, email):
|
||||||
|
|
|
@ -116,6 +116,9 @@ class DefaultConfig(object):
|
||||||
# Bitbucket Config.
|
# Bitbucket Config.
|
||||||
BITBUCKET_TRIGGER_CONFIG = None
|
BITBUCKET_TRIGGER_CONFIG = None
|
||||||
|
|
||||||
|
# Gitlab Config.
|
||||||
|
GITLAB_TRIGGER_CONFIG = None
|
||||||
|
|
||||||
# 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()
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from semantic_version import Version, Spec
|
||||||
from util.validation import generate_valid_usernames
|
from util.validation import generate_valid_usernames
|
||||||
from util.registry.generatorfile import GeneratorFile
|
from util.registry.generatorfile import GeneratorFile
|
||||||
from util.registry.dockerver import docker_version
|
from util.registry.dockerver import docker_version
|
||||||
|
from util.string import slash_join
|
||||||
|
|
||||||
class TestGeneratorFile(unittest.TestCase):
|
class TestGeneratorFile(unittest.TestCase):
|
||||||
def sample_generator(self):
|
def sample_generator(self):
|
||||||
|
@ -174,6 +175,21 @@ class TestDockerVersionParsing(unittest.TestCase):
|
||||||
self.assertTrue(spec.match(Version(match_case)),
|
self.assertTrue(spec.match(Version(match_case)),
|
||||||
'Spec: %s Case: %s' % (spec, match_case))
|
'Spec: %s Case: %s' % (spec, match_case))
|
||||||
|
|
||||||
|
|
||||||
|
class TestSlashJoining(unittest.TestCase):
|
||||||
|
def test_joining(self):
|
||||||
|
test_cases = [
|
||||||
|
(['https://github.com', '/coreos-inc/' 'quay/pull/1092/files'],
|
||||||
|
'https://github.com/coreos-inc/quay/pull/1092/files'),
|
||||||
|
(['https://', 'github.com/', '/coreos-inc', '/quay/pull/1092/files/'],
|
||||||
|
'https://github.com/coreos-inc/quay/pull/1092/files'),
|
||||||
|
]
|
||||||
|
|
||||||
|
for args, url in test_cases:
|
||||||
|
joined_url = slash_join(*args)
|
||||||
|
self.assertEquals(url, joined_url)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
def get_app_url(config):
|
def get_app_url(config):
|
||||||
""" Returns the application's URL, based on the given config. """
|
""" Returns the application's URL, based on the given config. """
|
||||||
return '%s://%s' % (config['PREFERRED_URL_SCHEME'], config['SERVER_HOSTNAME'])
|
return '%s://%s' % (config['PREFERRED_URL_SCHEME'], config['SERVER_HOSTNAME'])
|
||||||
|
|
||||||
|
|
||||||
|
def slash_join(*args):
|
||||||
|
"""
|
||||||
|
Joins together strings and guarantees there is only one '/' in between the
|
||||||
|
each string joined. Double slashes ('//') are assumed to be intentional and
|
||||||
|
are not deduplicated.
|
||||||
|
"""
|
||||||
|
def rmslash(path):
|
||||||
|
path = path[1:] if path[0] == '/' else path
|
||||||
|
path = path[:-1] if path[-1] == '/' else path
|
||||||
|
return path
|
||||||
|
|
||||||
|
args = [rmslash(path) for path in args]
|
||||||
|
return '/'.join(args)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import time
|
||||||
|
|
||||||
from cachetools import TTLCache
|
from cachetools import TTLCache
|
||||||
from jwkest.jwk import KEYS
|
from jwkest.jwk import KEYS
|
||||||
|
from util import slash_join
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -31,12 +32,6 @@ class OAuthConfig(object):
|
||||||
def client_secret(self):
|
def client_secret(self):
|
||||||
return self.config.get('CLIENT_SECRET')
|
return self.config.get('CLIENT_SECRET')
|
||||||
|
|
||||||
def _get_url(self, endpoint, *args):
|
|
||||||
for arg in args:
|
|
||||||
endpoint = urlparse.urljoin(endpoint, arg)
|
|
||||||
|
|
||||||
return endpoint
|
|
||||||
|
|
||||||
def get_redirect_uri(self, app_config, redirect_suffix=''):
|
def get_redirect_uri(self, app_config, redirect_suffix=''):
|
||||||
return '%s://%s/oauth2/%s/callback%s' % (app_config['PREFERRED_URL_SCHEME'],
|
return '%s://%s/oauth2/%s/callback%s' % (app_config['PREFERRED_URL_SCHEME'],
|
||||||
app_config['SERVER_HOSTNAME'],
|
app_config['SERVER_HOSTNAME'],
|
||||||
|
@ -95,40 +90,34 @@ class GithubOAuthConfig(OAuthConfig):
|
||||||
return [org.lower() for org in allowed]
|
return [org.lower() for org in allowed]
|
||||||
|
|
||||||
def get_public_url(self, suffix):
|
def get_public_url(self, suffix):
|
||||||
return '%s%s' % (self._endpoint(), suffix)
|
return slash_join(self._endpoint(), suffix)
|
||||||
|
|
||||||
def _endpoint(self):
|
def _endpoint(self):
|
||||||
endpoint = self.config.get('GITHUB_ENDPOINT', 'https://github.com')
|
return self.config.get('GITHUB_ENDPOINT', 'https://github.com')
|
||||||
if not endpoint.endswith('/'):
|
|
||||||
endpoint = endpoint + '/'
|
|
||||||
return endpoint
|
|
||||||
|
|
||||||
def is_enterprise(self):
|
def is_enterprise(self):
|
||||||
return self._endpoint().find('.github.com') < 0
|
return self._endpoint().find('.github.com') < 0
|
||||||
|
|
||||||
def authorize_endpoint(self):
|
def authorize_endpoint(self):
|
||||||
return self._get_url(self._endpoint(), '/login/oauth/authorize') + '?'
|
return slash_join(self._endpoint(), '/login/oauth/authorize') + '?'
|
||||||
|
|
||||||
def token_endpoint(self):
|
def token_endpoint(self):
|
||||||
return self._get_url(self._endpoint(), '/login/oauth/access_token')
|
return slash_join(self._endpoint(), '/login/oauth/access_token')
|
||||||
|
|
||||||
def _api_endpoint(self):
|
def _api_endpoint(self):
|
||||||
return self.config.get('API_ENDPOINT', self._get_url(self._endpoint(), '/api/v3/'))
|
return self.config.get('API_ENDPOINT', slash_join(self._endpoint(), '/api/v3/'))
|
||||||
|
|
||||||
def api_endpoint(self):
|
def api_endpoint(self):
|
||||||
return self._api_endpoint()[0:-1]
|
return self._api_endpoint()[0:-1]
|
||||||
|
|
||||||
def user_endpoint(self):
|
def user_endpoint(self):
|
||||||
api_endpoint = self._api_endpoint()
|
return slash_join(self._api_endpoint(), 'user')
|
||||||
return self._get_url(api_endpoint, 'user')
|
|
||||||
|
|
||||||
def email_endpoint(self):
|
def email_endpoint(self):
|
||||||
api_endpoint = self._api_endpoint()
|
return slash_join(self._api_endpoint(), 'user/emails')
|
||||||
return self._get_url(api_endpoint, 'user/emails')
|
|
||||||
|
|
||||||
def orgs_endpoint(self):
|
def orgs_endpoint(self):
|
||||||
api_endpoint = self._api_endpoint()
|
return slash_join(self._api_endpoint(), 'user/orgs')
|
||||||
return self._get_url(api_endpoint, 'user/orgs')
|
|
||||||
|
|
||||||
def validate_client_id_and_secret(self, http_client, app_config):
|
def validate_client_id_and_secret(self, http_client, app_config):
|
||||||
# First: Verify that the github endpoint is actually Github by checking for the
|
# First: Verify that the github endpoint is actually Github by checking for the
|
||||||
|
@ -150,18 +139,17 @@ class GithubOAuthConfig(OAuthConfig):
|
||||||
# password:
|
# password:
|
||||||
# - If the {client_id, client_secret} pair is invalid in some way, we get a 401 error.
|
# - If the {client_id, client_secret} pair is invalid in some way, we get a 401 error.
|
||||||
# - If the pair is valid, then we get a 404 because the 'foo' token does not exists.
|
# - If the pair is valid, then we get a 404 because the 'foo' token does not exists.
|
||||||
validate_endpoint = self._get_url(api_endpoint, 'applications/%s/tokens/foo' % self.client_id())
|
validate_endpoint = slash_join(api_endpoint, 'applications/%s/tokens/foo' % self.client_id())
|
||||||
result = http_client.get(validate_endpoint, auth=(self.client_id(), self.client_secret()),
|
result = http_client.get(validate_endpoint, auth=(self.client_id(), self.client_secret()),
|
||||||
timeout=5)
|
timeout=5)
|
||||||
return result.status_code == 404
|
return result.status_code == 404
|
||||||
|
|
||||||
def validate_organization(self, organization_id, http_client):
|
def validate_organization(self, organization_id, http_client):
|
||||||
api_endpoint = self._api_endpoint()
|
org_endpoint = slash_join(self._api_endpoint(), 'orgs/%s' % organization_id.lower())
|
||||||
org_endpoint = self._get_url(api_endpoint, 'orgs/%s' % organization_id.lower())
|
|
||||||
|
|
||||||
result = http_client.get(org_endpoint,
|
result = http_client.get(org_endpoint,
|
||||||
headers={'Accept': 'application/vnd.github.moondragon+json'},
|
headers={'Accept': 'application/vnd.github.moondragon+json'},
|
||||||
timeout=5)
|
timeout=5)
|
||||||
|
|
||||||
return result.status_code == 200
|
return result.status_code == 200
|
||||||
|
|
||||||
|
@ -221,19 +209,22 @@ class GitLabOAuthConfig(OAuthConfig):
|
||||||
super(GitLabOAuthConfig, self).__init__(config, key_name)
|
super(GitLabOAuthConfig, self).__init__(config, key_name)
|
||||||
|
|
||||||
def _endpoint(self):
|
def _endpoint(self):
|
||||||
endpoint = self.config.get('GITLAB_ENDPOINT', 'https://gitlab.com')
|
return self.config.get('GITLAB_ENDPOINT', 'https://gitlab.com')
|
||||||
if not endpoint.endswith('/'):
|
|
||||||
endpoint = endpoint + '/'
|
def api_endpoint(self):
|
||||||
return endpoint
|
return self._endpoint()
|
||||||
|
|
||||||
|
def get_public_url(self, suffix):
|
||||||
|
return slash_join(self._endpoint(), suffix)
|
||||||
|
|
||||||
def service_name(self):
|
def service_name(self):
|
||||||
return 'GitLab'
|
return 'GitLab'
|
||||||
|
|
||||||
def authorize_endpoint(self):
|
def authorize_endpoint(self):
|
||||||
return self._get_url(self._endpoint(), '/oauth/authorize')
|
return slash_join(self._endpoint(), '/oauth/authorize')
|
||||||
|
|
||||||
def token_endpoint(self):
|
def token_endpoint(self):
|
||||||
return self._get_url(self._endpoint(), '/oauth/token')
|
return slash_join(self._endpoint(), '/oauth/token')
|
||||||
|
|
||||||
def validate_client_id_and_secret(self, http_client, app_config):
|
def validate_client_id_and_secret(self, http_client, app_config):
|
||||||
url = self.token_endpoint()
|
url = self.token_endpoint()
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import re
|
|
||||||
import string
|
import string
|
||||||
import anunidecode
|
import re
|
||||||
|
|
||||||
|
|
||||||
INVALID_PASSWORD_MESSAGE = 'Invalid password, password must be at least ' + \
|
INVALID_PASSWORD_MESSAGE = 'Invalid password, password must be at least ' + \
|
||||||
|
|
Reference in a new issue