import logging import peewee from flask import Flask from app import app, config_provider, get_app_url, OVERRIDE_CONFIG_DIRECTORY from auth.auth_context import get_authenticated_user from bitbucket import BitBucket from data.database import validate_database_url from data.users import LDAP_CERT_FILENAME from oauth.services.github import GithubOAuthService from oauth.services.google import GoogleOAuthService from oauth.services.gitlab import GitLabOAuthService from util.config.validators.validate_database import DatabaseValidator from util.config.validators.validate_redis import RedisValidator from util.config.validators.validate_storage import StorageValidator from util.config.validators.validate_email import EmailValidator from util.config.validators.validate_ldap import LDAPValidator from util.config.validators.validate_keystone import KeystoneValidator from util.config.validators.validate_jwt import JWTAuthValidator from util.config.validators.validate_secscan import SecurityScannerValidator from util.config.validators.validate_signer import SignerValidator from util.config.validators.validate_torrent import BittorrentValidator from util.config.validators.validate_ssl import SSLValidator, SSL_FILENAMES logger = logging.getLogger(__name__) class ConfigValidationException(Exception): """ Exception raised when the configuration fails to validate for a known reason. """ pass # Note: Only add files required for HTTPS to the SSL_FILESNAMES list. DB_SSL_FILENAMES = ['database.pem'] JWT_FILENAMES = ['jwt-authn.cert'] ACI_CERT_FILENAMES = ['signing-public.gpg', 'signing-private.gpg'] LDAP_FILENAMES = [LDAP_CERT_FILENAME] CONFIG_FILENAMES = (SSL_FILENAMES + DB_SSL_FILENAMES + JWT_FILENAMES + ACI_CERT_FILENAMES + LDAP_FILENAMES) EXTRA_CA_DIRECTORY = 'extra_ca_certs' def validate_service_for_config(service, config, password=None): """ Attempts to validate the configuration for the given service. """ if not service in VALIDATORS: return { 'status': False } try: VALIDATORS[service](config, get_authenticated_user(), password) return { 'status': True } except Exception as ex: logger.exception('Validation exception') return { 'status': False, 'reason': str(ex) } def _validate_database(config, user_obj, _): """ Validates connecting to the database. """ try: validate_database_url(config['DB_URI'], config.get('DB_CONNECTION_ARGS', {})) except peewee.OperationalError as ex: if ex.args and len(ex.args) > 1: raise ConfigValidationException(ex.args[1]) else: raise ex def _validate_gitlab(config, user_obj, _): """ Validates the OAuth credentials and API endpoint for a GitLab service. """ github_config = config.get('GITLAB_TRIGGER_CONFIG') if not github_config: raise ConfigValidationException('Missing GitLab client id and client secret') endpoint = github_config.get('GITLAB_ENDPOINT') if not endpoint: raise ConfigValidationException('Missing GitLab Endpoint') if endpoint.find('http://') != 0 and endpoint.find('https://') != 0: raise ConfigValidationException('GitLab Endpoint must start with http:// or https://') if not github_config.get('CLIENT_ID'): raise ConfigValidationException('Missing Client ID') if not github_config.get('CLIENT_SECRET'): raise ConfigValidationException('Missing Client Secret') client = app.config['HTTPCLIENT'] oauth = GitLabOAuthService(config, 'GITLAB_TRIGGER_CONFIG') result = oauth.validate_client_id_and_secret(client, app.config) if not result: raise ConfigValidationException('Invalid client id or client secret') def _validate_github(config_key): return lambda config, user_obj, _: _validate_github_with_key(config_key, config) 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 ConfigValidationException('Missing GitHub client id and client secret') endpoint = github_config.get('GITHUB_ENDPOINT') if not endpoint: raise ConfigValidationException('Missing GitHub Endpoint') if endpoint.find('http://') != 0 and endpoint.find('https://') != 0: raise ConfigValidationException('Github Endpoint must start with http:// or https://') if not github_config.get('CLIENT_ID'): raise ConfigValidationException('Missing Client ID') if not github_config.get('CLIENT_SECRET'): raise ConfigValidationException('Missing Client Secret') if github_config.get('ORG_RESTRICT') and not github_config.get('ALLOWED_ORGANIZATIONS'): raise ConfigValidationException('Organization restriction must have at least one allowed ' + 'organization') client = app.config['HTTPCLIENT'] oauth = GithubOAuthService(config, config_key) result = oauth.validate_client_id_and_secret(client, app.config) if not result: raise ConfigValidationException('Invalid client id or client secret') if github_config.get('ALLOWED_ORGANIZATIONS'): for org_id in github_config.get('ALLOWED_ORGANIZATIONS'): if not oauth.validate_organization(org_id, client): raise ConfigValidationException('Invalid organization: %s' % org_id) def _validate_bitbucket(config, user_obj, _): """ Validates the config for BitBucket. """ trigger_config = config.get('BITBUCKET_TRIGGER_CONFIG') if not trigger_config: raise ConfigValidationException('Missing client ID and client secret') if not trigger_config.get('CONSUMER_KEY'): raise ConfigValidationException('Missing Consumer Key') if not trigger_config.get('CONSUMER_SECRET'): raise ConfigValidationException('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 ConfigValidationException('Invalid consumer key or secret') def _validate_google_login(config, user_obj, _): """ Validates the Google Login client ID and secret. """ google_login_config = config.get('GOOGLE_LOGIN_CONFIG') if not google_login_config: raise ConfigValidationException('Missing client ID and client secret') if not google_login_config.get('CLIENT_ID'): raise ConfigValidationException('Missing Client ID') if not google_login_config.get('CLIENT_SECRET'): raise ConfigValidationException('Missing Client Secret') client = app.config['HTTPCLIENT'] oauth = GoogleOAuthService(config, 'GOOGLE_LOGIN_CONFIG') result = oauth.validate_client_id_and_secret(client, app.config) if not result: raise ConfigValidationException('Invalid client id or client secret') VALIDATORS = { DatabaseValidator.name: DatabaseValidator.validate, RedisValidator.name: RedisValidator.validate, StorageValidator.name: StorageValidator.validate, EmailValidator.name: EmailValidator.validate, '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, SSLValidator.name: SSLValidator.validate, LDAPValidator.name: LDAPValidator.validate, JWTAuthValidator.name: JWTAuthValidator.validate, KeystoneValidator.name: KeystoneValidator.validate, SignerValidator.name: SignerValidator.validate, SecurityScannerValidator.name: SecurityScannerValidator.validate, BittorrentValidator.name: BittorrentValidator.validate, }