Switch our temporary token lookups for signed grants which will not require DB access.

This commit is contained in:
Jake Moshenko 2015-02-19 16:54:23 -05:00
parent 4e5d671349
commit 78c8354174
4 changed files with 81 additions and 54 deletions

View file

@ -9,12 +9,13 @@ from collections import OrderedDict
from data import model
from data.model import oauth
from app import app, authentication, userevents, storage
from auth.auth import process_auth
from auth.auth import process_auth, generate_signed_token
from auth.auth_context import get_authenticated_user, get_validated_token, get_validated_oauth_token
from util.names import parse_repository_name
from util.useremails import send_confirmation_email
from auth.permissions import (ModifyRepositoryPermission, UserAdminPermission,
ReadRepositoryPermission, CreateRepositoryPermission)
ReadRepositoryPermission, CreateRepositoryPermission,
AlwaysFailPermission, repository_read_grant, repository_write_grant)
from util.http import abort
from endpoints.trackhelper import track_and_log
@ -26,7 +27,13 @@ logger = logging.getLogger(__name__)
index = Blueprint('index', __name__)
def generate_headers(role='read'):
class GrantType(object):
READ_REPOSITORY = 'read'
WRITE_REPOSITORY = 'write'
def generate_headers(scope=GrantType.READ_REPOSITORY):
def decorator_method(f):
@wraps(f)
def wrapper(namespace, repository, *args, **kwargs):
@ -35,12 +42,6 @@ def generate_headers(role='read'):
# Setting session namespace and repository
session['namespace'] = namespace
session['repository'] = repository
if get_authenticated_user():
session['username'] = get_authenticated_user().username
else:
session.pop('username', None)
# We run our index and registry on the same hosts for now
registry_server = urlparse.urlparse(request.url).netloc
response.headers['X-Docker-Endpoints'] = registry_server
@ -48,16 +49,23 @@ def generate_headers(role='read'):
has_token_request = request.headers.get('X-Docker-Token', '')
if has_token_request:
repo = model.get_repository(namespace, repository)
if repo:
token = model.create_access_token(repo, role, 'pushpull-token')
token_str = 'signature=%s' % token.code
response.headers['WWW-Authenticate'] = token_str
response.headers['X-Docker-Token'] = token_str
else:
logger.info('Token request in non-existing repo: %s/%s' %
(namespace, repository))
permission = AlwaysFailPermission()
grants = []
if scope == GrantType.READ_REPOSITORY:
permission = ReadRepositoryPermission(namespace, repository)
grants.append(repository_read_grant(namespace, repository))
elif scope == GrantType.WRITE_REPOSITORY:
permission = ModifyRepositoryPermission(namespace, repository)
grants.append(repository_write_grant(namespace, repository))
if permission.can():
# Generate a signed grant which expires here
signature = generate_signed_token(grants)
response.headers['WWW-Authenticate'] = signature
response.headers['X-Docker-Token'] = signature
else:
logger.warning('Registry request with invalid credentials on repository: %s/%s',
namespace, repository)
return response
return wrapper
return decorator_method
@ -186,7 +194,7 @@ def update_user(username):
@index.route('/repositories/<path:repository>', methods=['PUT'])
@process_auth
@parse_repository_name
@generate_headers(role='write')
@generate_headers(scope=GrantType.WRITE_REPOSITORY)
def create_repository(namespace, repository):
logger.debug('Parsing image descriptions')
image_descriptions = json.loads(request.data.decode('utf8'))
@ -228,7 +236,7 @@ def create_repository(namespace, repository):
@index.route('/repositories/<path:repository>/images', methods=['PUT'])
@process_auth
@parse_repository_name
@generate_headers(role='write')
@generate_headers(scope=GrantType.WRITE_REPOSITORY)
def update_images(namespace, repository):
permission = ModifyRepositoryPermission(namespace, repository)
@ -273,7 +281,7 @@ def update_images(namespace, repository):
@index.route('/repositories/<path:repository>/images', methods=['GET'])
@process_auth
@parse_repository_name
@generate_headers(role='read')
@generate_headers(scope=GrantType.READ_REPOSITORY)
def get_repository_images(namespace, repository):
permission = ReadRepositoryPermission(namespace, repository)
@ -307,7 +315,7 @@ def get_repository_images(namespace, repository):
@index.route('/repositories/<path:repository>/images', methods=['DELETE'])
@process_auth
@parse_repository_name
@generate_headers(role='write')
@generate_headers(scope=GrantType.WRITE_REPOSITORY)
def delete_repository_images(namespace, repository):
abort(501, 'Not Implemented', issue='not-implemented')