import logging from functools import wraps from flask import request, make_response, _request_ctx_stack, abort, session from flask.ext.principal import identity_changed, Identity from base64 import b64decode from data import model from app import app from permissions import QuayDeferredPermissionUser from util.names import parse_namespace_repository logger = logging.getLogger(__name__) def get_authenticated_user(): return getattr(_request_ctx_stack.top, 'authenticated_user', None) def get_validated_token(): return getattr(_request_ctx_stack.top, 'validated_token', None) def process_basic_auth(auth): normalized = [part.strip() for part in auth.split(' ') if part] if normalized[0].lower() != 'basic' or len(normalized) != 2: logger.debug('Invalid basic auth format.') return credentials = b64decode(normalized[1]).split(':') if len(credentials) != 2: logger.debug('Invalid basic auth credential formet.') authenticated = model.verify_user(credentials[0], credentials[1]) if authenticated: logger.debug('Successfully validated user: %s' % authenticated.username) ctx = _request_ctx_stack.top ctx.authenticated_user = authenticated new_identity = QuayDeferredPermissionUser(authenticated.username, 'username') identity_changed.send(app, identity=new_identity) return # We weren't able to authenticate via basic auth. logger.debug('Basic auth present but could not be validated.') abort(401) def process_token(auth): normalized = [part.strip() for part in auth.split(' ') if part] if normalized[0].lower() != 'token' or len(normalized) != 2: logger.debug('Invalid token format.') return token_details = normalized[1].split(',') if len(token_details) != 2: logger.debug('Invalid token format.') return token_vals = {val[0]: val[1] for val in (detail.split('=') for detail in token_details)} if ('signature' not in token_vals or 'repository' not in token_vals): logger.debug('Invalid token components.') return unquoted = token_vals['repository'][1:-1] namespace, repository = parse_namespace_repository(unquoted) logger.debug('Validing signature: %s' % token_vals['signature']) validated = model.verify_token(token_vals['signature'], namespace, repository) if validated: session['repository'] = repository session['namespace'] = namespace logger.debug('Successfully validated token: %s' % validated.code) ctx = _request_ctx_stack.top ctx.validated_token = validated identity_changed.send(app, identity=Identity(validated.code, 'token')) return # WE weren't able to authenticate the token logger.debug('Token present but could not be validated.') abort(401) def process_auth(f): @wraps(f) def wrapper(*args, **kwargs): auth = request.headers.get('authorization', '') if auth: logger.debug('Validating auth header: %s' % auth) process_token(auth) process_basic_auth(auth) else: logger.debug('No auth header.') return f(*args, **kwargs) return wrapper def extract_namespace_repo_from_session(f): @wraps(f) def wrapper(*args, **kwargs): if 'namespace' not in session or 'repository' not in session: logger.debug('Unable to load namespace or repository from session.') abort(400) return f(session['namespace'], session['repository'], *args, **kwargs) return wrapper