def _validate_and_prep_request_headers(req): """ Validate that the value from x-symlink-target header is well formatted and that the x-symlink-target-etag header (if present) does not contain problematic characters. We assume the caller ensures that x-symlink-target header is present in req.headers. :param req: HTTP request object :returns: a tuple, the full versioned path to the object (as a WSGI string) and the X-Symlink-Target-Etag header value which may be None :raise: HTTPPreconditionFailed if x-symlink-target value is not well formatted. :raise: HTTPBadRequest if the x-symlink-target value points to the request path. :raise: HTTPBadRequest if the x-symlink-target-etag value contains a semicolon, double-quote, or backslash. """ # N.B. check_path_header doesn't assert the leading slash and # copy middleware may accept the format. In the symlink, API # says apparently to use "container/object" format so add the # validation first, here. error_body = 'X-Symlink-Target header must be of the form ' \ '<container name>/<object name>' if wsgi_unquote(req.headers[TGT_OBJ_SYMLINK_HDR]).startswith('/'): raise HTTPPreconditionFailed(body=error_body, request=req, content_type='text/plain') # check container and object format container, obj = check_path_header(req, TGT_OBJ_SYMLINK_HDR, 2, error_body) req.headers[TGT_OBJ_SYMLINK_HDR] = wsgi_quote('%s/%s' % (container, obj)) # Check account format if it exists account = check_account_format( req, wsgi_unquote(req.headers[TGT_ACCT_SYMLINK_HDR])) \ if TGT_ACCT_SYMLINK_HDR in req.headers else None # Extract request path _junk, req_acc, req_cont, req_obj = req.split_path(4, 4, True) if account: req.headers[TGT_ACCT_SYMLINK_HDR] = wsgi_quote(account) else: account = req_acc # Check if symlink targets the symlink itself or not if (account, container, obj) == (req_acc, req_cont, req_obj): raise HTTPBadRequest(body='Symlink cannot target itself', request=req, content_type='text/plain') etag = req.headers.get(TGT_ETAG_SYMLINK_HDR, None) if etag and any(c in etag for c in ';"\\'): # See cgi.parse_header for why the above chars are problematic raise HTTPBadRequest(body='Bad %s format' % TGT_ETAG_SYMLINK_HDR.title(), request=req, content_type='text/plain') if not (etag or req.headers.get('Content-Type')): req.headers['Content-Type'] = 'application/symlink' return '/v1/%s/%s/%s' % (account, container, obj), etag
def _check_symlink_header(req): """ Validate that the value from x-symlink-target header is well formatted. We assume the caller ensures that x-symlink-target header is present in req.headers. :param req: HTTP request object :raise: HTTPPreconditionFailed if x-symlink-target value is not well formatted. :raise: HTTPBadRequest if the x-symlink-target value points to the request path. """ # N.B. check_path_header doesn't assert the leading slash and # copy middleware may accept the format. In the symlink, API # says apparently to use "container/object" format so add the # validation first, here. error_body = 'X-Symlink-Target header must be of the form ' \ '<container name>/<object name>' try: if wsgi_unquote(req.headers[TGT_OBJ_SYMLINK_HDR]).startswith('/'): raise HTTPPreconditionFailed(body=error_body, request=req, content_type='text/plain') except TypeError: raise HTTPPreconditionFailed(body=error_body, request=req, content_type='text/plain') # check container and object format container, obj = check_path_header(req, TGT_OBJ_SYMLINK_HDR, 2, error_body) req.headers[TGT_OBJ_SYMLINK_HDR] = wsgi_quote('%s/%s' % (container, obj)) # Check account format if it exists try: account = check_account_format( req, wsgi_unquote(req.headers[TGT_ACCT_SYMLINK_HDR])) \ if TGT_ACCT_SYMLINK_HDR in req.headers else None except TypeError: raise HTTPPreconditionFailed( body='Account name cannot contain slashes', request=req, content_type='text/plain') # Extract request path _junk, req_acc, req_cont, req_obj = req.split_path(4, 4, True) if account: req.headers[TGT_ACCT_SYMLINK_HDR] = wsgi_quote(account) else: account = req_acc # Check if symlink targets the symlink itself or not if (account, container, obj) == (req_acc, req_cont, req_obj): raise HTTPBadRequest(body='Symlink cannot target itself', request=req, content_type='text/plain')
def _check_destination_header(req): """ Validate that the value from destination header is well formatted. We assume the caller ensures that destination header is present in req.headers. :param req: HTTP request object :returns: A tuple with container name and object name :raise HTTPPreconditionFailed: if destination value is not well formatted. """ return check_path_header( req, 'Destination', 2, 'Destination header must be of the form ' '<container name>/<object name>')
def _check_destination_header(req): """ Validate that the value from destination header is well formatted. We assume the caller ensures that destination header is present in req.headers. :param req: HTTP request object :returns: A tuple with container name and object name :raise HTTPPreconditionFailed: if destination value is not well formatted. """ return check_path_header(req, 'Destination', 2, 'Destination header must be of the form ' '<container name>/<object name>')
def _check_symlink_header(req): """ Validate that the value from x-symlink-target header is well formatted. We assume the caller ensures that x-symlink-target header is present in req.headers. :param req: HTTP request object :raise: HTTPPreconditionFailed if x-symlink-target value is not well formatted. :raise: HTTPBadRequest if the x-symlink-target value points to the request path. """ # N.B. check_path_header doesn't assert the leading slash and # copy middleware may accept the format. In the symlink, API # says apparently to use "container/object" format so add the # validation first, here. if unquote(req.headers[TGT_OBJ_SYMLINK_HDR]).startswith('/'): raise HTTPPreconditionFailed( body='X-Symlink-Target header must be of the ' 'form <container name>/<object name>', request=req, content_type='text/plain') # check container and object format container, obj = check_path_header( req, TGT_OBJ_SYMLINK_HDR, 2, 'X-Symlink-Target header must be of the ' 'form <container name>/<object name>') # Check account format if it exists account = check_account_format( req, unquote(req.headers[TGT_ACCT_SYMLINK_HDR])) \ if TGT_ACCT_SYMLINK_HDR in req.headers else None # Extract request path _junk, req_acc, req_cont, req_obj = req.split_path(4, 4, True) if not account: account = req_acc # Check if symlink targets the symlink itself or not if (account, container, obj) == (req_acc, req_cont, req_obj): raise HTTPBadRequest( body='Symlink cannot target itself', request=req, content_type='text/plain')