import logging import os import base64 import urlparse import json from flask import session, make_response, render_template, request from flask.ext.login import login_user, UserMixin, current_user from flask.ext.principal import identity_changed from data import model from data.queue import dockerfile_build_queue from app import app, login_manager from auth.permissions import QuayDeferredPermissionUser from werkzeug.routing import BaseConverter logger = logging.getLogger(__name__) route_data = None class RepoPathConverter(BaseConverter): regex = '[\.a-zA-Z0-9_\-]+/[\.a-zA-Z0-9_\-]+' weight = 200 app.url_map.converters['repopath'] = RepoPathConverter def get_route_data(): global route_data if route_data: return route_data routes = [] for rule in app.url_map.iter_rules(): if rule.endpoint.startswith('api.'): endpoint_method = app.view_functions[rule.endpoint] is_internal = '__internal_call' in dir(endpoint_method) is_org_api = '__user_call' in dir(endpoint_method) methods = list(rule.methods.difference(['HEAD', 'OPTIONS'])) route = { 'name': rule.endpoint[4:], 'methods': methods, 'path': rule.rule, 'parameters': list(rule.arguments) } if is_org_api: route['user_method'] = endpoint_method.__user_call routes.append(route) route_data = { 'endpoints': routes } return route_data def truthy_param(param): return param not in {False, 'false', 'False', '0', 'FALSE', '', 'null'} @login_manager.user_loader def load_user(username): logger.debug('Loading user: %s' % username) return _LoginWrappedDBUser(username) class _LoginWrappedDBUser(UserMixin): def __init__(self, db_username, db_user=None): self._db_username = db_username self._db_user = db_user def db_user(self): if not self._db_user: self._db_user = model.get_user(self._db_username) return self._db_user def is_authenticated(self): return self.db_user() is not None def is_active(self): return self.db_user().verified def get_id(self): return unicode(self._db_username) def common_login(db_user): if login_user(_LoginWrappedDBUser(db_user.username, db_user)): logger.debug('Successfully signed in as: %s' % db_user.username) new_identity = QuayDeferredPermissionUser(db_user.username, 'username') identity_changed.send(app, identity=new_identity) return True else: logger.debug('User could not be logged in, inactive?.') return False @app.errorhandler(model.DataModelException) def handle_dme(ex): logger.exception(ex) return make_response(ex.message, 400) @app.errorhandler(KeyError) def handle_dme_key_error(ex): logger.exception(ex) return make_response('Invalid key: %s' % ex.message, 400) def generate_csrf_token(): if '_csrf_token' not in session: session['_csrf_token'] = base64.b64encode(os.urandom(48)) return session['_csrf_token'] app.jinja_env.globals['csrf_token'] = generate_csrf_token def render_page_template(name, **kwargs): resp = make_response(render_template(name, route_data=get_route_data(), **kwargs)) resp.headers['X-FRAME-OPTIONS'] = 'DENY' return resp def check_repository_usage(user_or_org, plan_found): private_repos = model.get_private_repo_count(user_or_org.username) repos_allowed = plan_found['privateRepos'] if private_repos > repos_allowed: model.create_notification('over_private_usage', user_or_org, {'namespace': user_or_org.username}) else: model.delete_notifications_by_kind(user_or_org, 'over_private_usage') def start_build(repository, dockerfile_id, tags, build_name, subdir, manual, trigger=None): host = urlparse.urlparse(request.url).netloc repo_path = '%s/%s/%s' % (host, repository.namespace, repository.name) token = model.create_access_token(repository, 'write') logger.debug('Creating build %s with repo %s tags %s and dockerfile_id %s', build_name, repo_path, tags, dockerfile_id) job_config = { 'docker_tags': tags, 'repository': repo_path, 'build_subdir': subdir, } build_request = model.create_repository_build(repository, token, job_config, dockerfile_id, build_name, trigger) dockerfile_build_queue.put(json.dumps({ 'build_uuid': build_request.uuid, 'namespace': repository.namespace, 'repository': repository.name, }), retries_remaining=1) metadata = { 'repo': repository.name, 'namespace': repository.namespace, 'fileid': dockerfile_id, 'manual': manual, } if trigger: metadata['trigger_id'] = trigger.uuid metadata['config'] = json.loads(trigger.config) metadata['service'] = trigger.service.name model.log_action('build_dockerfile', repository.namespace, ip=request.remote_addr, metadata=metadata, repository=repository) return build_request