Add setup UI for the new trigger types (bitbucket and gitlab) and add validation
This commit is contained in:
parent
0b990677a0
commit
4f2a1b3734
4 changed files with 229 additions and 20 deletions
|
@ -12,9 +12,10 @@ from flask import Flask
|
|||
from flask.ext.mail import Mail, Message
|
||||
from data.database import validate_database_url, User
|
||||
from storage import get_storage_driver
|
||||
from app import app, CONFIG_PROVIDER
|
||||
from app import app, CONFIG_PROVIDER, get_app_url
|
||||
from auth.auth_context import get_authenticated_user
|
||||
from util.oauth import GoogleOAuthConfig, GithubOAuthConfig
|
||||
from util.oauth import GoogleOAuthConfig, GithubOAuthConfig, GitLabOAuthConfig
|
||||
from bitbucket import BitBucket
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -99,6 +100,32 @@ def _validate_mailing(config):
|
|||
test_mail.send(test_msg)
|
||||
|
||||
|
||||
def _validate_gitlab(config):
|
||||
""" Validates the OAuth credentials and API endpoint for a GitLab service. """
|
||||
github_config = config.get('GITLAB_TRIGGER_CONFIG')
|
||||
if not github_config:
|
||||
raise Exception('Missing GitLab client id and client secret')
|
||||
|
||||
endpoint = github_config.get('GITLAB_ENDPOINT')
|
||||
if not endpoint:
|
||||
raise Exception('Missing GitLab Endpoint')
|
||||
|
||||
if endpoint.find('http://') != 0 and endpoint.find('https://') != 0:
|
||||
raise Exception('GitLab Endpoint must start with http:// or https://')
|
||||
|
||||
if not github_config.get('CLIENT_ID'):
|
||||
raise Exception('Missing Client ID')
|
||||
|
||||
if not github_config.get('CLIENT_SECRET'):
|
||||
raise Exception('Missing Client Secret')
|
||||
|
||||
client = app.config['HTTPCLIENT']
|
||||
oauth = GitLabOAuthConfig(config, 'GITLAB_TRIGGER_CONFIG')
|
||||
result = oauth.validate_client_id_and_secret(client, app.config)
|
||||
if not result:
|
||||
raise Exception('Invalid client id or client secret')
|
||||
|
||||
|
||||
def _validate_github(config_key):
|
||||
return lambda config: _validate_github_with_key(config_key, config)
|
||||
|
||||
|
@ -107,11 +134,11 @@ def _validate_github_with_key(config_key, config):
|
|||
""" Validates the OAuth credentials and API endpoint for a Github service. """
|
||||
github_config = config.get(config_key)
|
||||
if not github_config:
|
||||
raise Exception('Missing Github client id and client secret')
|
||||
raise Exception('Missing GitHub client id and client secret')
|
||||
|
||||
endpoint = github_config.get('GITHUB_ENDPOINT')
|
||||
if not endpoint:
|
||||
raise Exception('Missing Github Endpoint')
|
||||
raise Exception('Missing GitHub Endpoint')
|
||||
|
||||
if endpoint.find('http://') != 0 and endpoint.find('https://') != 0:
|
||||
raise Exception('Github Endpoint must start with http:// or https://')
|
||||
|
@ -127,7 +154,7 @@ def _validate_github_with_key(config_key, config):
|
|||
|
||||
client = app.config['HTTPCLIENT']
|
||||
oauth = GithubOAuthConfig(config, config_key)
|
||||
result = oauth.validate_client_id_and_secret(client)
|
||||
result = oauth.validate_client_id_and_secret(client, app.config)
|
||||
if not result:
|
||||
raise Exception('Invalid client id or client secret')
|
||||
|
||||
|
@ -137,6 +164,28 @@ def _validate_github_with_key(config_key, config):
|
|||
raise Exception('Invalid organization: %s' % org_id)
|
||||
|
||||
|
||||
def _validate_bitbucket(config):
|
||||
""" Validates the config for BitBucket. """
|
||||
trigger_config = config.get('BITBUCKET_TRIGGER_CONFIG')
|
||||
if not trigger_config:
|
||||
raise Exception('Missing client ID and client secret')
|
||||
|
||||
if not trigger_config.get('CONSUMER_KEY'):
|
||||
raise Exception('Missing Consumer Key')
|
||||
|
||||
if not trigger_config.get('CONSUMER_SECRET'):
|
||||
raise Exception('Missing Consumer Secret')
|
||||
|
||||
key = trigger_config['CONSUMER_KEY']
|
||||
secret = trigger_config['CONSUMER_SECRET']
|
||||
callback_url = '%s/oauth1/bitbucket/callback/trigger/' % (get_app_url())
|
||||
|
||||
bitbucket_client = BitBucket(key, secret, callback_url)
|
||||
(result, _, _) = bitbucket_client.get_authorization_url()
|
||||
if not result:
|
||||
raise Exception('Invaid consumer key or secret')
|
||||
|
||||
|
||||
def _validate_google_login(config):
|
||||
""" Validates the Google Login client ID and secret. """
|
||||
google_login_config = config.get('GOOGLE_LOGIN_CONFIG')
|
||||
|
@ -151,7 +200,7 @@ def _validate_google_login(config):
|
|||
|
||||
client = app.config['HTTPCLIENT']
|
||||
oauth = GoogleOAuthConfig(config, 'GOOGLE_LOGIN_CONFIG')
|
||||
result = oauth.validate_client_id_and_secret(client)
|
||||
result = oauth.validate_client_id_and_secret(client, app.config)
|
||||
if not result:
|
||||
raise Exception('Invalid client id or client secret')
|
||||
|
||||
|
@ -261,6 +310,8 @@ _VALIDATORS = {
|
|||
'mail': _validate_mailing,
|
||||
'github-login': _validate_github('GITHUB_LOGIN_CONFIG'),
|
||||
'github-trigger': _validate_github('GITHUB_TRIGGER_CONFIG'),
|
||||
'gitlab-trigger': _validate_gitlab,
|
||||
'bitbucket-trigger': _validate_bitbucket,
|
||||
'google-login': _validate_google_login,
|
||||
'ssl': _validate_ssl,
|
||||
'ldap': _validate_ldap,
|
||||
|
|
|
@ -15,7 +15,7 @@ class OAuthConfig(object):
|
|||
def user_endpoint(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def validate_client_id_and_secret(self, http_client):
|
||||
def validate_client_id_and_secret(self, http_client, app_config):
|
||||
raise NotImplementedError
|
||||
|
||||
def client_id(self):
|
||||
|
@ -30,6 +30,13 @@ class OAuthConfig(object):
|
|||
|
||||
return endpoint
|
||||
|
||||
def get_redirect_uri(self, app_config, redirect_suffix=''):
|
||||
return '%s://%s/oauth2/%s/callback%s' % (app_config['PREFERRED_URL_SCHEME'],
|
||||
app_config['SERVER_HOSTNAME'],
|
||||
self.service_name().lower(),
|
||||
redirect_suffix)
|
||||
|
||||
|
||||
def exchange_code_for_token(self, app_config, http_client, code, form_encode=False,
|
||||
redirect_suffix=''):
|
||||
payload = {
|
||||
|
@ -37,10 +44,7 @@ class OAuthConfig(object):
|
|||
'client_secret': self.client_secret(),
|
||||
'code': code,
|
||||
'grant_type': 'authorization_code',
|
||||
'redirect_uri': '%s://%s/oauth2/%s/callback%s' % (app_config['PREFERRED_URL_SCHEME'],
|
||||
app_config['SERVER_HOSTNAME'],
|
||||
self.service_name().lower(),
|
||||
redirect_suffix)
|
||||
'redirect_uri': self.get_redirect_uri(app_config, redirect_suffix)
|
||||
}
|
||||
|
||||
headers = {
|
||||
|
@ -114,7 +118,7 @@ class GithubOAuthConfig(OAuthConfig):
|
|||
api_endpoint = self._api_endpoint()
|
||||
return self._get_url(api_endpoint, 'user/orgs')
|
||||
|
||||
def validate_client_id_and_secret(self, http_client):
|
||||
def validate_client_id_and_secret(self, http_client, app_config):
|
||||
# First: Verify that the github endpoint is actually Github by checking for the
|
||||
# X-GitHub-Request-Id here.
|
||||
api_endpoint = self._api_endpoint()
|
||||
|
@ -176,7 +180,7 @@ class GoogleOAuthConfig(OAuthConfig):
|
|||
def user_endpoint(self):
|
||||
return 'https://www.googleapis.com/oauth2/v1/userinfo'
|
||||
|
||||
def validate_client_id_and_secret(self, http_client):
|
||||
def validate_client_id_and_secret(self, http_client, app_config):
|
||||
# To verify the Google client ID and secret, we hit the
|
||||
# https://www.googleapis.com/oauth2/v3/token endpoint with an invalid request. If the client
|
||||
# ID or secret are invalid, we get returned a 403 Unauthorized. Otherwise, we get returned
|
||||
|
@ -219,8 +223,24 @@ class GitLabOAuthConfig(OAuthConfig):
|
|||
def token_endpoint(self):
|
||||
return self._get_url(self._endpoint(), '/oauth/token')
|
||||
|
||||
def validate_client_id_and_secret(self, http_client):
|
||||
pass
|
||||
def validate_client_id_and_secret(self, http_client, app_config):
|
||||
url = self.token_endpoint()
|
||||
redirect_uri = self.get_redirect_uri(app_config, redirect_suffix='trigger')
|
||||
data = {
|
||||
'code': 'fakecode',
|
||||
'client_id': self.client_id(),
|
||||
'client_secret': self.client_secret(),
|
||||
'grant_type': 'authorization_code',
|
||||
'redirect_uri': redirect_uri
|
||||
}
|
||||
|
||||
# We validate by checking the error code we receive from this call.
|
||||
result = http_client.post(url, data=data, timeout=5)
|
||||
value = result.json()
|
||||
if not value:
|
||||
return False
|
||||
|
||||
return value.get('error', '') != 'invalid_client'
|
||||
|
||||
def get_public_config(self):
|
||||
return {
|
||||
|
|
Reference in a new issue