diff --git a/swift/common/middleware/swift3.py b/swift/common/middleware/swift3.py index de66a8d677..979c007538 100644 --- a/swift/common/middleware/swift3.py +++ b/swift/common/middleware/swift3.py @@ -18,7 +18,7 @@ import rfc822 import hmac import base64 import errno -import binascii +from xml.sax.saxutils import escape as xml_escape from webob import Request, Response from webob.exc import HTTPNotFound @@ -43,16 +43,20 @@ def get_err_response(code): 'NoSuchBucket': (404, 'The specified bucket does not exist'), 'SignatureDoesNotMatch': - (403, 'The calculated request signature does not match your provided one'), + (403, 'The calculated request signature does not match '\ + 'your provided one'), 'NoSuchKey': (404, 'The resource you requested does not exist')} resp = Response(content_type='text/xml') resp.status = error_table[code][0] resp.body = error_table[code][1] - resp.body = """\r\n\r\n %s\r\n %s\r\n\r\n""" % (code, error_table[code][1]) + resp.body = '\r\n\r\n ' \ + '%s\r\n %s\r\n\r\n' \ + % (code, error_table[code][1]) return resp + class Controller(object): def __init__(self, app): self.app = app @@ -61,6 +65,7 @@ class Controller(object): def do_start_response(self, *args): self.response_args.extend(args) + class ServiceController(Controller): def __init__(self, env, app, account_name, token, **kwargs): Controller.__init__(self, app) @@ -85,16 +90,25 @@ class ServiceController(Controller): resp.status = 200 # we don't keep the creation time of a backet (s3cmd doesn't # work without that) so we use something bogus. - resp.body = """%s""" % ("".join(['%s2009-02-03T16:45:09.000Z' % i['name'] for i in containers])) + resp.body = '' \ + '' \ + '%s' \ + '' \ + % ("".join(['%s' \ + '2009-02-03T16:45:09.000Z' % + xml_escape(i['name']) for i in containers])) return resp + class BucketController(Controller): - def __init__(self, env, app, account_name, token, container_name, **kwargs): + def __init__(self, env, app, account_name, token, container_name, + **kwargs): Controller.__init__(self, app) self.container_name = unquote(container_name) env['HTTP_X_AUTH_TOKEN'] = token env['PATH_INFO'] = '/v1/%s/%s' % (account_name, container_name) - + def GET(self, env, start_response): env['QUERY_STRING'] = 'format=json' body_iter = self.app(env, self.do_start_response) @@ -113,7 +127,18 @@ class BucketController(Controller): objects = loads(''.join(list(body_iter))) resp = Response(content_type='text/xml') resp.status = 200 - resp.body = """%s%s""" % (self.container_name, "".join(['%s%s%s%sSTANDARD' % (i['name'], i['last_modified'], i['hash'], i['bytes']) for i in objects])) + resp.body = '' \ + '' \ + '%s' \ + '%s' \ + '' % \ + (self.container_name, + "".join(['%s%s'\ + '%s%sSTANDARD'\ + '' % + (xml_escape(i['name']), i['last_modified'], i['hash'], + i['bytes']) for i in objects])) return resp def PUT(self, env, start_response): @@ -155,12 +180,15 @@ class BucketController(Controller): resp.status = 204 return resp + class ObjectController(Controller): - def __init__(self, env, app, account_name, token, container_name, object_name, **kwargs): + def __init__(self, env, app, account_name, token, container_name, + object_name, **kwargs): Controller.__init__(self, app) self.container_name = unquote(container_name) env['HTTP_X_AUTH_TOKEN'] = token - env['PATH_INFO'] = '/v1/%s/%s/%s' % (account_name, container_name, object_name) + env['PATH_INFO'] = '/v1/%s/%s/%s' % (account_name, container_name, + object_name) def GETorHEAD(self, env, start_response): app_iter = self.app(env, self.do_start_response) @@ -170,9 +198,11 @@ class ObjectController(Controller): if 200 <= status < 300: new_hdrs = {} for key, val in headers.iteritems(): - if key.startswith('x-object-meta-'): + _key = key.lower() + if _key.startswith('x-object-meta-'): new_hdrs['x-amz-meta-' + key[14:]] = val - elif key in ('Content-Length', 'Content-Type', 'Content-Encoding', 'etag', 'last-modified'): + elif _key in ('content-length', 'content-type', + 'content-encoding', 'etag', 'last-modified'): new_hdrs[key] = val return Response(status=status, headers=new_hdrs, app_iter=app_iter) elif status == 401: @@ -187,7 +217,7 @@ class ObjectController(Controller): def GET(self, env, start_response): return self.GETorHEAD(env, start_response) - + def PUT(self, env, start_response): body_iter = self.app(env, self.do_start_response) status = int(self.response_args[0].split()[0]) @@ -211,7 +241,7 @@ class ObjectController(Controller): body_iter = self.app(env, self.do_start_response) status = int(self.response_args[0].split()[0]) headers = dict(self.response_args[1]) - + if status != 204: if status == 401: return get_err_response('AccessDenied') @@ -225,6 +255,7 @@ class ObjectController(Controller): resp.status = 204 return resp + class Swift3Middleware(object): def __init__(self, app, conf, *args, **kwargs): self.app = app @@ -238,7 +269,7 @@ class Swift3Middleware(object): elif container: return BucketController, d return ServiceController, d - + def get_account_info(self, env, req): if req.headers.get("content-md5"): md5 = req.headers.get("content-md5") @@ -258,7 +289,7 @@ class Swift3Middleware(object): h = req.method + "\n" + md5 + "\n" + content_type + "\n" + date + "\n" for header in req.headers: if header.startswith("X-Amz-"): - h += header.lower()+":"+str(req.headers[header])+"\n" + h += header.lower() + ":" + str(req.headers[header]) + "\n" h += req.path try: account, _ = req.headers['Authorization'].split(' ')[-1].split(':') @@ -279,18 +310,22 @@ class Swift3Middleware(object): account_name, token = self.get_account_info(env, req) if not account_name: return get_err_response('InvalidArgument')(env, start_response) - - controller = controller(env, self.app, account_name, token, **path_parts) + + controller = controller(env, self.app, account_name, token, + **path_parts) if hasattr(controller, req.method): res = getattr(controller, req.method)(env, start_response) else: return get_err_response('InvalidURI')(env, start_response) - + return res(env, start_response) + def filter_factory(global_conf, **local_conf): conf = global_conf.copy() conf.update(local_conf) + def swift3_filter(app): return Swift3Middleware(app, conf) + return swift3_filter