# XXX This code is not yet ready to be run in production, and should remain disabled until such # XXX time as this notice is removed. import logging import re import jwt.utils import yaml from flask import make_response, request from app import storage from auth.jwt_auth import process_jwt_auth from endpoints.decorators import anon_protect from endpoints.v2 import v2_bp, require_repo_read, require_repo_write, get_input_stream from digest import digest_tools logger = logging.getLogger(__name__) VALID_TAG_PATTERN = r'[\w][\w.-]{0,127}' VALID_TAG_REGEX = re.compile(VALID_TAG_PATTERN) class SignedManifest(object): SIGNATURES_KEY = 'signatures' PROTECTED_KEY = 'protected' FORMAT_LENGTH_KEY = 'formatLength' FORMAT_TAIL_KEY = 'formatTail' REPO_NAME_KEY = 'name' REPO_TAG_KEY = 'tag' def __init__(self, manifest_bytes): self._bytes = manifest_bytes parsed = yaml.safe_load(manifest_bytes) self._signatures = parsed[self.SIGNATURES_KEY] self._namespace, self._repo_name = parsed[self.REPO_NAME_KEY].split('/') self._tag = parsed[self.REPO_TAG_KEY] self._validate() def _validate(self): pass @property def signatures(self): return self._signatures @property def namespace(self): return self._namespace @property def repo_name(self): return self._repo_name @property def tag(self): return self._tag @property def payload(self): protected = self._signatures[0][self.PROTECTED_KEY] parsed_protected = yaml.safe_load(jwt.utils.base64url_decode(protected)) logger.debug('parsed_protected: %s', parsed_protected) signed_content_head = self._bytes[:parsed_protected[self.FORMAT_LENGTH_KEY]] logger.debug('signed content head: %s', signed_content_head) signed_content_tail = jwt.utils.base64url_decode(parsed_protected[self.FORMAT_TAIL_KEY]) logger.debug('signed content tail: %s', signed_content_tail) return signed_content_head + signed_content_tail @v2_bp.route('///manifests/', methods=['GET']) @process_jwt_auth @require_repo_read @anon_protect def fetch_manifest_by_tagname(namespace, repo_name, tag_name): logger.debug('Fetching tag manifest with name: %s', tag_name) return make_response('Manifest {0}'.format(tag_name)) @v2_bp.route('///manifests/', methods=['PUT']) @process_jwt_auth @require_repo_write @anon_protect def write_manifest_by_tagname(namespace, repo_name, tag_name): manifest = SignedManifest(request.data) manifest_digest = digest_tools.sha256_digest(manifest.payload) response = make_response('OK', 202) response.headers['Docker-Content-Digest'] = manifest_digest response.headers['Location'] = 'https://fun.com' return response # @v2_bp.route('///manifests/', # methods=['PUT']) # @process_jwt_auth # @require_repo_write # @anon_protect # def write_manifest(namespace, repo_name, tag_digest): # logger.debug('Writing tag manifest with name: %s', tag_digest) # manifest_path = digest_tools.content_path(tag_digest) # storage.stream_write('local_us', manifest_path, get_input_stream(request)) # return make_response('Manifest {0}'.format(tag_digest))