OpenStack Storage (Swift)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2042 lines
80KB

  1. # Copyright (c) 2011 OpenStack Foundation
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  12. # implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import hmac
  16. import unittest
  17. from hashlib import sha1
  18. from time import time
  19. import six
  20. from six import BytesIO
  21. from swift.common.swob import Request, Response, wsgi_quote
  22. from swift.common.middleware import tempauth, formpost
  23. from swift.common.utils import split_path
  24. from swift.proxy.controllers.base import get_cache_key
  25. def hmac_msg(path, redirect, max_file_size, max_file_count, expires):
  26. msg = '%s\n%s\n%s\n%s\n%s' % (
  27. path, redirect, max_file_size, max_file_count, expires)
  28. if six.PY3:
  29. msg = msg.encode('utf-8')
  30. return msg
  31. class FakeApp(object):
  32. def __init__(self, status_headers_body_iter=None,
  33. check_no_query_string=True):
  34. self.status_headers_body_iter = status_headers_body_iter
  35. if not self.status_headers_body_iter:
  36. self.status_headers_body_iter = iter([('404 Not Found', {
  37. 'x-test-header-one-a': 'value1',
  38. 'x-test-header-two-a': 'value2',
  39. 'x-test-header-two-b': 'value3'}, b'')])
  40. self.requests = []
  41. self.check_no_query_string = check_no_query_string
  42. def __call__(self, env, start_response):
  43. # use wsgi_quote to spot check that it really *is* a WSGI string
  44. wsgi_quote(env['PATH_INFO'])
  45. try:
  46. if self.check_no_query_string and env.get('QUERY_STRING'):
  47. raise Exception('Query string %s should have been discarded!' %
  48. env['QUERY_STRING'])
  49. body = b''
  50. while True:
  51. chunk = env['wsgi.input'].read()
  52. if not chunk:
  53. break
  54. body += chunk
  55. env['wsgi.input'] = BytesIO(body)
  56. self.requests.append(Request.blank('', environ=env))
  57. if env.get('swift.authorize_override') and \
  58. env.get('REMOTE_USER') != '.wsgi.pre_authed':
  59. raise Exception(
  60. 'Invalid REMOTE_USER %r with swift.authorize_override' % (
  61. env.get('REMOTE_USER'),))
  62. if 'swift.authorize' in env:
  63. resp = env['swift.authorize'](self.requests[-1])
  64. if resp:
  65. return resp(env, start_response)
  66. status, headers, body = next(self.status_headers_body_iter)
  67. return Response(status=status, headers=headers,
  68. body=body)(env, start_response)
  69. except EOFError:
  70. start_response('499 Client Disconnect',
  71. [('Content-Type', 'text/plain')])
  72. return [b'Client Disconnect\n']
  73. class TestCappedFileLikeObject(unittest.TestCase):
  74. def test_whole(self):
  75. self.assertEqual(
  76. formpost._CappedFileLikeObject(BytesIO(b'abc'), 10).read(),
  77. b'abc')
  78. def test_exceeded(self):
  79. exc = None
  80. try:
  81. formpost._CappedFileLikeObject(BytesIO(b'abc'), 2).read()
  82. except EOFError as err:
  83. exc = err
  84. self.assertEqual(str(exc), 'max_file_size exceeded')
  85. def test_whole_readline(self):
  86. fp = formpost._CappedFileLikeObject(BytesIO(b'abc\ndef'), 10)
  87. self.assertEqual(fp.readline(), b'abc\n')
  88. self.assertEqual(fp.readline(), b'def')
  89. self.assertEqual(fp.readline(), b'')
  90. def test_exceeded_readline(self):
  91. fp = formpost._CappedFileLikeObject(BytesIO(b'abc\ndef'), 5)
  92. self.assertEqual(fp.readline(), b'abc\n')
  93. exc = None
  94. try:
  95. self.assertEqual(fp.readline(), b'def')
  96. except EOFError as err:
  97. exc = err
  98. self.assertEqual(str(exc), 'max_file_size exceeded')
  99. def test_read_sized(self):
  100. fp = formpost._CappedFileLikeObject(BytesIO(b'abcdefg'), 10)
  101. self.assertEqual(fp.read(2), b'ab')
  102. self.assertEqual(fp.read(2), b'cd')
  103. self.assertEqual(fp.read(2), b'ef')
  104. self.assertEqual(fp.read(2), b'g')
  105. self.assertEqual(fp.read(2), b'')
  106. class TestFormPost(unittest.TestCase):
  107. def setUp(self):
  108. self.app = FakeApp()
  109. self.auth = tempauth.filter_factory({})(self.app)
  110. self.formpost = formpost.filter_factory({})(self.auth)
  111. def _make_request(self, path, tempurl_keys=(), **kwargs):
  112. req = Request.blank(path, **kwargs)
  113. # Fake out the caching layer so that get_account_info() finds its
  114. # data. Include something that isn't tempurl keys to prove we skip it.
  115. meta = {'user-job-title': 'Personal Trainer',
  116. 'user-real-name': 'Jim Shortz'}
  117. for idx, key in enumerate(tempurl_keys):
  118. meta_name = 'temp-url-key' + (("-%d" % (idx + 1) if idx else ""))
  119. if key:
  120. meta[meta_name] = key
  121. _junk, account, _junk, _junk = split_path(path, 2, 4)
  122. req.environ.setdefault('swift.infocache', {})
  123. req.environ['swift.infocache'][get_cache_key(account)] = \
  124. self._fake_cache_env(account, tempurl_keys)
  125. return req
  126. def _fake_cache_env(self, account, tempurl_keys=()):
  127. # Fake out the caching layer so that get_account_info() finds its
  128. # data. Include something that isn't tempurl keys to prove we skip it.
  129. meta = {'user-job-title': 'Personal Trainer',
  130. 'user-real-name': 'Jim Shortz'}
  131. for idx, key in enumerate(tempurl_keys):
  132. meta_name = 'temp-url-key' + ("-%d" % (idx + 1) if idx else "")
  133. if key:
  134. meta[meta_name] = key
  135. return {'status': 204,
  136. 'container_count': '0',
  137. 'total_object_count': '0',
  138. 'bytes': '0',
  139. 'meta': meta}
  140. def _make_sig_env_body(self, path, redirect, max_file_size, max_file_count,
  141. expires, key, user_agent=True):
  142. sig = hmac.new(
  143. key,
  144. hmac_msg(path, redirect, max_file_size, max_file_count, expires),
  145. sha1).hexdigest()
  146. body = [
  147. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  148. 'Content-Disposition: form-data; name="redirect"',
  149. '',
  150. redirect,
  151. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  152. 'Content-Disposition: form-data; name="max_file_size"',
  153. '',
  154. str(max_file_size),
  155. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  156. 'Content-Disposition: form-data; name="max_file_count"',
  157. '',
  158. str(max_file_count),
  159. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  160. 'Content-Disposition: form-data; name="expires"',
  161. '',
  162. str(expires),
  163. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  164. 'Content-Disposition: form-data; name="signature"',
  165. '',
  166. sig,
  167. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  168. 'Content-Disposition: form-data; name="file1"; '
  169. 'filename="testfile1.txt"',
  170. 'Content-Type: text/plain',
  171. '',
  172. 'Test File\nOne\n',
  173. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  174. 'Content-Disposition: form-data; name="file2"; '
  175. 'filename="testfile2.txt"',
  176. 'Content-Type: text/plain',
  177. 'Content-Encoding: gzip',
  178. '',
  179. 'Test\nFile\nTwo\n',
  180. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  181. 'Content-Disposition: form-data; name="file3"; filename=""',
  182. 'Content-Type: application/octet-stream',
  183. '',
  184. '',
  185. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR--',
  186. '',
  187. ]
  188. if six.PY3:
  189. body = [line.encode('utf-8') for line in body]
  190. wsgi_errors = six.StringIO()
  191. env = {
  192. 'CONTENT_TYPE': 'multipart/form-data; '
  193. 'boundary=----WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  194. 'HTTP_ACCEPT_ENCODING': 'gzip, deflate',
  195. 'HTTP_ACCEPT_LANGUAGE': 'en-us',
  196. 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;'
  197. 'q=0.9,*/*;q=0.8',
  198. 'HTTP_CONNECTION': 'keep-alive',
  199. 'HTTP_HOST': 'ubuntu:8080',
  200. 'HTTP_ORIGIN': 'file://',
  201. 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X '
  202. '10_7_2) AppleWebKit/534.52.7 (KHTML, like Gecko) '
  203. 'Version/5.1.2 Safari/534.52.7',
  204. 'PATH_INFO': path,
  205. 'REMOTE_ADDR': '172.16.83.1',
  206. 'REQUEST_METHOD': 'POST',
  207. 'SCRIPT_NAME': '',
  208. 'SERVER_NAME': '172.16.83.128',
  209. 'SERVER_PORT': '8080',
  210. 'SERVER_PROTOCOL': 'HTTP/1.0',
  211. 'swift.infocache': {},
  212. 'wsgi.errors': wsgi_errors,
  213. 'wsgi.multiprocess': False,
  214. 'wsgi.multithread': True,
  215. 'wsgi.run_once': False,
  216. 'wsgi.url_scheme': 'http',
  217. 'wsgi.version': (1, 0),
  218. }
  219. if user_agent is False:
  220. del env['HTTP_USER_AGENT']
  221. return sig, env, body
  222. def test_passthrough(self):
  223. for method in ('HEAD', 'GET', 'PUT', 'POST', 'DELETE'):
  224. resp = self._make_request(
  225. '/v1/a/c/o',
  226. environ={'REQUEST_METHOD': method}).get_response(self.formpost)
  227. self.assertEqual(resp.status_int, 401)
  228. self.assertNotIn(b'FormPost', resp.body)
  229. def test_auth_scheme(self):
  230. # FormPost rejects
  231. key = b'abc'
  232. sig, env, body = self._make_sig_env_body(
  233. '/v1/AUTH_test/container', '', 1024, 10, int(time() - 10), key)
  234. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  235. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  236. self._fake_cache_env('AUTH_test', [key]))
  237. self.app = FakeApp(iter([('201 Created', {}, b''),
  238. ('201 Created', {}, b'')]))
  239. self.auth = tempauth.filter_factory({})(self.app)
  240. self.formpost = formpost.filter_factory({})(self.auth)
  241. status = [None]
  242. headers = [None]
  243. exc_info = [None]
  244. def start_response(s, h, e=None):
  245. status[0] = s
  246. headers[0] = h
  247. exc_info[0] = e
  248. body = b''.join(self.formpost(env, start_response))
  249. status = status[0]
  250. headers = headers[0]
  251. exc_info = exc_info[0]
  252. self.assertEqual(status, '401 Unauthorized')
  253. authenticate_v = None
  254. for h, v in headers:
  255. if h.lower() == 'www-authenticate':
  256. authenticate_v = v
  257. self.assertTrue(b'FormPost: Form Expired' in body)
  258. self.assertEqual('Swift realm="unknown"', authenticate_v)
  259. def test_safari(self):
  260. key = b'abc'
  261. path = '/v1/AUTH_test/container'
  262. redirect = 'http://brim.net'
  263. max_file_size = 1024
  264. max_file_count = 10
  265. expires = int(time() + 86400)
  266. sig = hmac.new(
  267. key,
  268. hmac_msg(path, redirect, max_file_size, max_file_count, expires),
  269. sha1).hexdigest()
  270. wsgi_input = '\r\n'.join([
  271. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  272. 'Content-Disposition: form-data; name="redirect"',
  273. '',
  274. redirect,
  275. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  276. 'Content-Disposition: form-data; name="max_file_size"',
  277. '',
  278. str(max_file_size),
  279. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  280. 'Content-Disposition: form-data; name="max_file_count"',
  281. '',
  282. str(max_file_count),
  283. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  284. 'Content-Disposition: form-data; name="expires"',
  285. '',
  286. str(expires),
  287. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  288. 'Content-Disposition: form-data; name="signature"',
  289. '',
  290. sig,
  291. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  292. 'Content-Disposition: form-data; name="file1"; '
  293. 'filename="testfile1.txt"',
  294. 'Content-Type: text/plain',
  295. '',
  296. 'Test File\nOne\n',
  297. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  298. 'Content-Disposition: form-data; name="file2"; '
  299. 'filename="testfile2.txt"',
  300. 'Content-Type: text/plain',
  301. '',
  302. 'Test\nFile\nTwo\n',
  303. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  304. 'Content-Disposition: form-data; name="file3"; filename=""',
  305. 'Content-Type: application/octet-stream',
  306. '',
  307. '',
  308. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR--',
  309. '',
  310. ])
  311. if six.PY3:
  312. wsgi_input = wsgi_input.encode('utf-8')
  313. wsgi_input = BytesIO(wsgi_input)
  314. wsgi_errors = six.StringIO()
  315. env = {
  316. 'CONTENT_TYPE': 'multipart/form-data; '
  317. 'boundary=----WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  318. 'HTTP_ACCEPT_ENCODING': 'gzip, deflate',
  319. 'HTTP_ACCEPT_LANGUAGE': 'en-us',
  320. 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;'
  321. 'q=0.9,*/*;q=0.8',
  322. 'HTTP_CONNECTION': 'keep-alive',
  323. 'HTTP_HOST': 'ubuntu:8080',
  324. 'HTTP_ORIGIN': 'file://',
  325. 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X '
  326. '10_7_2) AppleWebKit/534.52.7 (KHTML, like Gecko) '
  327. 'Version/5.1.2 Safari/534.52.7',
  328. 'PATH_INFO': path,
  329. 'REMOTE_ADDR': '172.16.83.1',
  330. 'REQUEST_METHOD': 'POST',
  331. 'SCRIPT_NAME': '',
  332. 'SERVER_NAME': '172.16.83.128',
  333. 'SERVER_PORT': '8080',
  334. 'SERVER_PROTOCOL': 'HTTP/1.0',
  335. 'swift.infocache': {
  336. get_cache_key('AUTH_test'): self._fake_cache_env(
  337. 'AUTH_test', [key]),
  338. get_cache_key('AUTH_test', 'container'): {
  339. 'meta': {}}},
  340. 'wsgi.errors': wsgi_errors,
  341. 'wsgi.input': wsgi_input,
  342. 'wsgi.multiprocess': False,
  343. 'wsgi.multithread': True,
  344. 'wsgi.run_once': False,
  345. 'wsgi.url_scheme': 'http',
  346. 'wsgi.version': (1, 0),
  347. }
  348. self.app = FakeApp(iter([('201 Created', {}, b''),
  349. ('201 Created', {}, b'')]))
  350. self.auth = tempauth.filter_factory({})(self.app)
  351. self.formpost = formpost.filter_factory({})(self.auth)
  352. status = [None]
  353. headers = [None]
  354. exc_info = [None]
  355. def start_response(s, h, e=None):
  356. status[0] = s
  357. headers[0] = h
  358. exc_info[0] = e
  359. body = b''.join(self.formpost(env, start_response))
  360. status = status[0]
  361. headers = headers[0]
  362. exc_info = exc_info[0]
  363. self.assertEqual(status, '303 See Other')
  364. location = None
  365. for h, v in headers:
  366. if h.lower() == 'location':
  367. location = v
  368. self.assertEqual(location, 'http://brim.net?status=201&message=')
  369. self.assertIsNone(exc_info)
  370. self.assertTrue(b'http://brim.net?status=201&message=' in body)
  371. self.assertEqual(len(self.app.requests), 2)
  372. self.assertEqual(self.app.requests[0].body, b'Test File\nOne\n')
  373. self.assertEqual(self.app.requests[1].body, b'Test\nFile\nTwo\n')
  374. def test_firefox(self):
  375. key = b'abc'
  376. path = '/v1/AUTH_test/container'
  377. redirect = 'http://brim.net'
  378. max_file_size = 1024
  379. max_file_count = 10
  380. expires = int(time() + 86400)
  381. sig = hmac.new(
  382. key,
  383. hmac_msg(path, redirect, max_file_size, max_file_count, expires),
  384. sha1).hexdigest()
  385. wsgi_input = '\r\n'.join([
  386. '-----------------------------168072824752491622650073',
  387. 'Content-Disposition: form-data; name="redirect"',
  388. '',
  389. redirect,
  390. '-----------------------------168072824752491622650073',
  391. 'Content-Disposition: form-data; name="max_file_size"',
  392. '',
  393. str(max_file_size),
  394. '-----------------------------168072824752491622650073',
  395. 'Content-Disposition: form-data; name="max_file_count"',
  396. '',
  397. str(max_file_count),
  398. '-----------------------------168072824752491622650073',
  399. 'Content-Disposition: form-data; name="expires"',
  400. '',
  401. str(expires),
  402. '-----------------------------168072824752491622650073',
  403. 'Content-Disposition: form-data; name="signature"',
  404. '',
  405. sig,
  406. '-----------------------------168072824752491622650073',
  407. 'Content-Disposition: form-data; name="file1"; '
  408. 'filename="testfile1.txt"',
  409. 'Content-Type: text/plain',
  410. '',
  411. 'Test File\nOne\n',
  412. '-----------------------------168072824752491622650073',
  413. 'Content-Disposition: form-data; name="file2"; '
  414. 'filename="testfile2.txt"',
  415. 'Content-Type: text/plain',
  416. '',
  417. 'Test\nFile\nTwo\n',
  418. '-----------------------------168072824752491622650073',
  419. 'Content-Disposition: form-data; name="file3"; filename=""',
  420. 'Content-Type: application/octet-stream',
  421. '',
  422. '',
  423. '-----------------------------168072824752491622650073--',
  424. ''
  425. ])
  426. if six.PY3:
  427. wsgi_input = wsgi_input.encode('utf-8')
  428. wsgi_input = BytesIO(wsgi_input)
  429. wsgi_errors = six.StringIO()
  430. env = {
  431. 'CONTENT_TYPE': 'multipart/form-data; '
  432. 'boundary=---------------------------168072824752491622650073',
  433. 'HTTP_ACCEPT_CHARSET': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
  434. 'HTTP_ACCEPT_ENCODING': 'gzip, deflate',
  435. 'HTTP_ACCEPT_LANGUAGE': 'en-us,en;q=0.5',
  436. 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;'
  437. 'q=0.9,*/*;q=0.8',
  438. 'HTTP_CONNECTION': 'keep-alive',
  439. 'HTTP_HOST': 'ubuntu:8080',
  440. 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; '
  441. 'rv:8.0.1) Gecko/20100101 Firefox/8.0.1',
  442. 'PATH_INFO': '/v1/AUTH_test/container',
  443. 'REMOTE_ADDR': '172.16.83.1',
  444. 'REQUEST_METHOD': 'POST',
  445. 'SCRIPT_NAME': '',
  446. 'SERVER_NAME': '172.16.83.128',
  447. 'SERVER_PORT': '8080',
  448. 'SERVER_PROTOCOL': 'HTTP/1.0',
  449. 'swift.infocache': {
  450. get_cache_key('AUTH_test'): self._fake_cache_env(
  451. 'AUTH_test', [key]),
  452. get_cache_key('AUTH_test', 'container'): {
  453. 'meta': {}}},
  454. 'wsgi.errors': wsgi_errors,
  455. 'wsgi.input': wsgi_input,
  456. 'wsgi.multiprocess': False,
  457. 'wsgi.multithread': True,
  458. 'wsgi.run_once': False,
  459. 'wsgi.url_scheme': 'http',
  460. 'wsgi.version': (1, 0),
  461. }
  462. self.app = FakeApp(iter([('201 Created', {}, b''),
  463. ('201 Created', {}, b'')]))
  464. self.auth = tempauth.filter_factory({})(self.app)
  465. self.formpost = formpost.filter_factory({})(self.auth)
  466. status = [None]
  467. headers = [None]
  468. exc_info = [None]
  469. def start_response(s, h, e=None):
  470. status[0] = s
  471. headers[0] = h
  472. exc_info[0] = e
  473. body = b''.join(self.formpost(env, start_response))
  474. status = status[0]
  475. headers = headers[0]
  476. exc_info = exc_info[0]
  477. self.assertEqual(status, '303 See Other')
  478. location = None
  479. for h, v in headers:
  480. if h.lower() == 'location':
  481. location = v
  482. self.assertEqual(location, 'http://brim.net?status=201&message=')
  483. self.assertIsNone(exc_info)
  484. self.assertTrue(b'http://brim.net?status=201&message=' in body)
  485. self.assertEqual(len(self.app.requests), 2)
  486. self.assertEqual(self.app.requests[0].body, b'Test File\nOne\n')
  487. self.assertEqual(self.app.requests[1].body, b'Test\nFile\nTwo\n')
  488. def test_chrome(self):
  489. key = b'abc'
  490. path = '/v1/AUTH_test/container'
  491. redirect = 'http://brim.net'
  492. max_file_size = 1024
  493. max_file_count = 10
  494. expires = int(time() + 86400)
  495. sig = hmac.new(
  496. key,
  497. hmac_msg(path, redirect, max_file_size, max_file_count, expires),
  498. sha1).hexdigest()
  499. wsgi_input = '\r\n'.join([
  500. '------WebKitFormBoundaryq3CFxUjfsDMu8XsA',
  501. 'Content-Disposition: form-data; name="redirect"',
  502. '',
  503. redirect,
  504. '------WebKitFormBoundaryq3CFxUjfsDMu8XsA',
  505. 'Content-Disposition: form-data; name="max_file_size"',
  506. '',
  507. str(max_file_size),
  508. '------WebKitFormBoundaryq3CFxUjfsDMu8XsA',
  509. 'Content-Disposition: form-data; name="max_file_count"',
  510. '',
  511. str(max_file_count),
  512. '------WebKitFormBoundaryq3CFxUjfsDMu8XsA',
  513. 'Content-Disposition: form-data; name="expires"',
  514. '',
  515. str(expires),
  516. '------WebKitFormBoundaryq3CFxUjfsDMu8XsA',
  517. 'Content-Disposition: form-data; name="signature"',
  518. '',
  519. sig,
  520. '------WebKitFormBoundaryq3CFxUjfsDMu8XsA',
  521. 'Content-Disposition: form-data; name="file1"; '
  522. 'filename="testfile1.txt"',
  523. 'Content-Type: text/plain',
  524. '',
  525. 'Test File\nOne\n',
  526. '------WebKitFormBoundaryq3CFxUjfsDMu8XsA',
  527. 'Content-Disposition: form-data; name="file2"; '
  528. 'filename="testfile2.txt"',
  529. 'Content-Type: text/plain',
  530. '',
  531. 'Test\nFile\nTwo\n',
  532. '------WebKitFormBoundaryq3CFxUjfsDMu8XsA',
  533. 'Content-Disposition: form-data; name="file3"; filename=""',
  534. 'Content-Type: application/octet-stream',
  535. '',
  536. '',
  537. '------WebKitFormBoundaryq3CFxUjfsDMu8XsA--',
  538. ''
  539. ])
  540. if six.PY3:
  541. wsgi_input = wsgi_input.encode('utf-8')
  542. wsgi_input = BytesIO(wsgi_input)
  543. wsgi_errors = six.StringIO()
  544. env = {
  545. 'CONTENT_TYPE': 'multipart/form-data; '
  546. 'boundary=----WebKitFormBoundaryq3CFxUjfsDMu8XsA',
  547. 'HTTP_ACCEPT_CHARSET': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
  548. 'HTTP_ACCEPT_ENCODING': 'gzip,deflate,sdch',
  549. 'HTTP_ACCEPT_LANGUAGE': 'en-US,en;q=0.8',
  550. 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;'
  551. 'q=0.9,*/*;q=0.8',
  552. 'HTTP_CACHE_CONTROL': 'max-age=0',
  553. 'HTTP_CONNECTION': 'keep-alive',
  554. 'HTTP_HOST': 'ubuntu:8080',
  555. 'HTTP_ORIGIN': 'null',
  556. 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X '
  557. '10_7_2) AppleWebKit/535.7 (KHTML, like Gecko) '
  558. 'Chrome/16.0.912.63 Safari/535.7',
  559. 'PATH_INFO': '/v1/AUTH_test/container',
  560. 'REMOTE_ADDR': '172.16.83.1',
  561. 'REQUEST_METHOD': 'POST',
  562. 'SCRIPT_NAME': '',
  563. 'SERVER_NAME': '172.16.83.128',
  564. 'SERVER_PORT': '8080',
  565. 'SERVER_PROTOCOL': 'HTTP/1.0',
  566. 'swift.infocache': {
  567. get_cache_key('AUTH_test'): self._fake_cache_env(
  568. 'AUTH_test', [key]),
  569. get_cache_key('AUTH_test', 'container'): {
  570. 'meta': {}}},
  571. 'wsgi.errors': wsgi_errors,
  572. 'wsgi.input': wsgi_input,
  573. 'wsgi.multiprocess': False,
  574. 'wsgi.multithread': True,
  575. 'wsgi.run_once': False,
  576. 'wsgi.url_scheme': 'http',
  577. 'wsgi.version': (1, 0),
  578. }
  579. self.app = FakeApp(iter([('201 Created', {}, b''),
  580. ('201 Created', {}, b'')]))
  581. self.auth = tempauth.filter_factory({})(self.app)
  582. self.formpost = formpost.filter_factory({})(self.auth)
  583. status = [None]
  584. headers = [None]
  585. exc_info = [None]
  586. def start_response(s, h, e=None):
  587. status[0] = s
  588. headers[0] = h
  589. exc_info[0] = e
  590. body = b''.join(self.formpost(env, start_response))
  591. status = status[0]
  592. headers = headers[0]
  593. exc_info = exc_info[0]
  594. self.assertEqual(status, '303 See Other')
  595. location = None
  596. for h, v in headers:
  597. if h.lower() == 'location':
  598. location = v
  599. self.assertEqual(location, 'http://brim.net?status=201&message=')
  600. self.assertIsNone(exc_info)
  601. self.assertTrue(b'http://brim.net?status=201&message=' in body)
  602. self.assertEqual(len(self.app.requests), 2)
  603. self.assertEqual(self.app.requests[0].body, b'Test File\nOne\n')
  604. self.assertEqual(self.app.requests[1].body, b'Test\nFile\nTwo\n')
  605. def test_explorer(self):
  606. key = b'abc'
  607. path = '/v1/AUTH_test/container'
  608. redirect = 'http://brim.net'
  609. max_file_size = 1024
  610. max_file_count = 10
  611. expires = int(time() + 86400)
  612. sig = hmac.new(
  613. key,
  614. hmac_msg(path, redirect, max_file_size, max_file_count, expires),
  615. sha1).hexdigest()
  616. wsgi_input = '\r\n'.join([
  617. '-----------------------------7db20d93017c',
  618. 'Content-Disposition: form-data; name="redirect"',
  619. '',
  620. redirect,
  621. '-----------------------------7db20d93017c',
  622. 'Content-Disposition: form-data; name="max_file_size"',
  623. '',
  624. str(max_file_size),
  625. '-----------------------------7db20d93017c',
  626. 'Content-Disposition: form-data; name="max_file_count"',
  627. '',
  628. str(max_file_count),
  629. '-----------------------------7db20d93017c',
  630. 'Content-Disposition: form-data; name="expires"',
  631. '',
  632. str(expires),
  633. '-----------------------------7db20d93017c',
  634. 'Content-Disposition: form-data; name="signature"',
  635. '',
  636. sig,
  637. '-----------------------------7db20d93017c',
  638. 'Content-Disposition: form-data; name="file1"; '
  639. 'filename="C:\\testfile1.txt"',
  640. 'Content-Type: text/plain',
  641. '',
  642. 'Test File\nOne\n',
  643. '-----------------------------7db20d93017c',
  644. 'Content-Disposition: form-data; name="file2"; '
  645. 'filename="C:\\testfile2.txt"',
  646. 'Content-Type: text/plain',
  647. '',
  648. 'Test\nFile\nTwo\n',
  649. '-----------------------------7db20d93017c',
  650. 'Content-Disposition: form-data; name="file3"; filename=""',
  651. 'Content-Type: application/octet-stream',
  652. '',
  653. '',
  654. '-----------------------------7db20d93017c--',
  655. ''
  656. ])
  657. if six.PY3:
  658. wsgi_input = wsgi_input.encode('utf-8')
  659. wsgi_input = BytesIO(wsgi_input)
  660. wsgi_errors = six.StringIO()
  661. env = {
  662. 'CONTENT_TYPE': 'multipart/form-data; '
  663. 'boundary=---------------------------7db20d93017c',
  664. 'HTTP_ACCEPT_ENCODING': 'gzip, deflate',
  665. 'HTTP_ACCEPT_LANGUAGE': 'en-US',
  666. 'HTTP_ACCEPT': 'text/html, application/xhtml+xml, */*',
  667. 'HTTP_CACHE_CONTROL': 'no-cache',
  668. 'HTTP_CONNECTION': 'Keep-Alive',
  669. 'HTTP_HOST': '172.16.83.128:8080',
  670. 'HTTP_USER_AGENT': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT '
  671. '6.1; WOW64; Trident/5.0)',
  672. 'PATH_INFO': '/v1/AUTH_test/container',
  673. 'REMOTE_ADDR': '172.16.83.129',
  674. 'REQUEST_METHOD': 'POST',
  675. 'SCRIPT_NAME': '',
  676. 'SERVER_NAME': '172.16.83.128',
  677. 'SERVER_PORT': '8080',
  678. 'SERVER_PROTOCOL': 'HTTP/1.0',
  679. 'swift.infocache': {
  680. get_cache_key('AUTH_test'): self._fake_cache_env(
  681. 'AUTH_test', [key]),
  682. get_cache_key('AUTH_test', 'container'): {
  683. 'meta': {}}},
  684. 'wsgi.errors': wsgi_errors,
  685. 'wsgi.input': wsgi_input,
  686. 'wsgi.multiprocess': False,
  687. 'wsgi.multithread': True,
  688. 'wsgi.run_once': False,
  689. 'wsgi.url_scheme': 'http',
  690. 'wsgi.version': (1, 0),
  691. }
  692. self.app = FakeApp(iter([('201 Created', {}, b''),
  693. ('201 Created', {}, b'')]))
  694. self.auth = tempauth.filter_factory({})(self.app)
  695. self.formpost = formpost.filter_factory({})(self.auth)
  696. status = [None]
  697. headers = [None]
  698. exc_info = [None]
  699. def start_response(s, h, e=None):
  700. status[0] = s
  701. headers[0] = h
  702. exc_info[0] = e
  703. body = b''.join(self.formpost(env, start_response))
  704. status = status[0]
  705. headers = headers[0]
  706. exc_info = exc_info[0]
  707. self.assertEqual(status, '303 See Other')
  708. location = None
  709. for h, v in headers:
  710. if h.lower() == 'location':
  711. location = v
  712. self.assertEqual(location, 'http://brim.net?status=201&message=')
  713. self.assertIsNone(exc_info)
  714. self.assertTrue(b'http://brim.net?status=201&message=' in body)
  715. self.assertEqual(len(self.app.requests), 2)
  716. self.assertEqual(self.app.requests[0].body, b'Test File\nOne\n')
  717. self.assertEqual(self.app.requests[1].body, b'Test\nFile\nTwo\n')
  718. def test_curl_with_unicode(self):
  719. key = b'abc'
  720. path = u'/v1/AUTH_test/container/let_it_\N{SNOWFLAKE}/'
  721. if six.PY2:
  722. path = path.encode('utf-8')
  723. redirect = 'http://brim.net'
  724. max_file_size = 1024
  725. max_file_count = 10
  726. expires = int(time() + 86400)
  727. sig = hmac.new(
  728. key,
  729. hmac_msg(path, redirect, max_file_size, max_file_count, expires),
  730. sha1).hexdigest()
  731. wsgi_input = '\r\n'.join([
  732. '--------------------------dea19ac8502ca805',
  733. 'Content-Disposition: form-data; name="redirect"',
  734. '',
  735. redirect,
  736. '--------------------------dea19ac8502ca805',
  737. 'Content-Disposition: form-data; name="max_file_size"',
  738. '',
  739. str(max_file_size),
  740. '--------------------------dea19ac8502ca805',
  741. 'Content-Disposition: form-data; name="max_file_count"',
  742. '',
  743. str(max_file_count),
  744. '--------------------------dea19ac8502ca805',
  745. 'Content-Disposition: form-data; name="expires"',
  746. '',
  747. str(expires),
  748. '--------------------------dea19ac8502ca805',
  749. 'Content-Disposition: form-data; name="signature"',
  750. '',
  751. sig,
  752. '--------------------------dea19ac8502ca805',
  753. 'Content-Disposition: form-data; name="file1"; '
  754. 'filename="\xe2\x98\x83.txt"',
  755. 'Content-Type: text/plain',
  756. '',
  757. 'Test File\nOne\n',
  758. '--------------------------dea19ac8502ca805',
  759. 'Content-Disposition: form-data; name="file2"; '
  760. 'filename="testfile2.txt"',
  761. 'Content-Type: text/plain',
  762. '',
  763. 'Test\nFile\nTwo\n',
  764. '--------------------------dea19ac8502ca805',
  765. 'Content-Disposition: form-data; name="file3"; filename=""',
  766. 'Content-Type: application/octet-stream',
  767. '',
  768. '',
  769. '--------------------------dea19ac8502ca805--',
  770. ''
  771. ])
  772. if not six.PY2:
  773. wsgi_input = wsgi_input.encode('latin1')
  774. wsgi_input = BytesIO(wsgi_input)
  775. wsgi_errors = six.StringIO()
  776. env = {
  777. 'CONTENT_LENGTH': str(len(wsgi_input.getvalue())),
  778. 'CONTENT_TYPE': 'multipart/form-data; '
  779. 'boundary=------------------------dea19ac8502ca805',
  780. 'HTTP_ACCEPT': '*/*',
  781. 'HTTP_HOST': 'ubuntu:8080',
  782. 'HTTP_USER_AGENT': 'curl/7.58.0',
  783. 'PATH_INFO': '/v1/AUTH_test/container/let_it_\xE2\x9D\x84/',
  784. 'REMOTE_ADDR': '172.16.83.1',
  785. 'REQUEST_METHOD': 'POST',
  786. 'SCRIPT_NAME': '',
  787. 'SERVER_NAME': '172.16.83.128',
  788. 'SERVER_PORT': '8080',
  789. 'SERVER_PROTOCOL': 'HTTP/1.0',
  790. 'swift.infocache': {
  791. get_cache_key('AUTH_test'): self._fake_cache_env(
  792. 'AUTH_test', [key]),
  793. get_cache_key('AUTH_test', 'container'): {
  794. 'meta': {}}},
  795. 'wsgi.errors': wsgi_errors,
  796. 'wsgi.input': wsgi_input,
  797. 'wsgi.multiprocess': False,
  798. 'wsgi.multithread': True,
  799. 'wsgi.run_once': False,
  800. 'wsgi.url_scheme': 'http',
  801. 'wsgi.version': (1, 0),
  802. }
  803. self.app = FakeApp(iter([('201 Created', {}, b''),
  804. ('201 Created', {}, b'')]))
  805. self.auth = tempauth.filter_factory({})(self.app)
  806. self.formpost = formpost.filter_factory({})(self.auth)
  807. status = [None]
  808. headers = [None]
  809. exc_info = [None]
  810. def start_response(s, h, e=None):
  811. status[0] = s
  812. headers[0] = h
  813. exc_info[0] = e
  814. body = b''.join(self.formpost(env, start_response))
  815. status = status[0]
  816. headers = headers[0]
  817. exc_info = exc_info[0]
  818. self.assertEqual(status, '303 See Other', body)
  819. location = None
  820. for h, v in headers:
  821. if h.lower() == 'location':
  822. location = v
  823. self.assertEqual(location, 'http://brim.net?status=201&message=')
  824. self.assertIsNone(exc_info)
  825. self.assertTrue(b'http://brim.net?status=201&message=' in body)
  826. self.assertEqual(len(self.app.requests), 2)
  827. self.assertEqual(
  828. self.app.requests[0].path,
  829. '/v1/AUTH_test/container/let_it_%E2%9D%84/%E2%98%83.txt')
  830. self.assertEqual(self.app.requests[0].body, b'Test File\nOne\n')
  831. self.assertEqual(
  832. self.app.requests[1].path,
  833. '/v1/AUTH_test/container/let_it_%E2%9D%84/testfile2.txt')
  834. self.assertEqual(self.app.requests[1].body, b'Test\nFile\nTwo\n')
  835. def test_messed_up_start(self):
  836. key = b'abc'
  837. sig, env, body = self._make_sig_env_body(
  838. '/v1/AUTH_test/container', 'http://brim.net', 5, 10,
  839. int(time() + 86400), key)
  840. env['wsgi.input'] = BytesIO(b'XX' + b'\r\n'.join(body))
  841. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  842. self._fake_cache_env('AUTH_test', [key]))
  843. env['swift.infocache'][get_cache_key(
  844. 'AUTH_test', 'container')] = {'meta': {}}
  845. self.app = FakeApp(iter([('201 Created', {}, b''),
  846. ('201 Created', {}, b'')]))
  847. self.auth = tempauth.filter_factory({})(self.app)
  848. self.formpost = formpost.filter_factory({})(self.auth)
  849. def log_assert_int_status(env, response_status_int):
  850. self.assertIsInstance(response_status_int, int)
  851. self.formpost._log_request = log_assert_int_status
  852. status = [None]
  853. headers = [None]
  854. exc_info = [None]
  855. def start_response(s, h, e=None):
  856. status[0] = s
  857. headers[0] = h
  858. exc_info[0] = e
  859. body = b''.join(self.formpost(env, start_response))
  860. status = status[0]
  861. headers = headers[0]
  862. exc_info = exc_info[0]
  863. self.assertEqual(status, '400 Bad Request')
  864. self.assertIsNone(exc_info)
  865. self.assertIn(b'FormPost: invalid starting boundary', body)
  866. self.assertEqual(len(self.app.requests), 0)
  867. def test_max_file_size_exceeded(self):
  868. key = b'abc'
  869. sig, env, body = self._make_sig_env_body(
  870. '/v1/AUTH_test/container', 'http://brim.net', 5, 10,
  871. int(time() + 86400), key)
  872. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  873. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  874. self._fake_cache_env('AUTH_test', [key]))
  875. env['swift.infocache'][get_cache_key(
  876. 'AUTH_test', 'container')] = {'meta': {}}
  877. self.app = FakeApp(iter([('201 Created', {}, b''),
  878. ('201 Created', {}, b'')]))
  879. self.auth = tempauth.filter_factory({})(self.app)
  880. self.formpost = formpost.filter_factory({})(self.auth)
  881. status = [None]
  882. headers = [None]
  883. exc_info = [None]
  884. def start_response(s, h, e=None):
  885. status[0] = s
  886. headers[0] = h
  887. exc_info[0] = e
  888. body = b''.join(self.formpost(env, start_response))
  889. status = status[0]
  890. headers = headers[0]
  891. exc_info = exc_info[0]
  892. self.assertEqual(status, '400 Bad Request')
  893. self.assertIsNone(exc_info)
  894. self.assertIn(b'FormPost: max_file_size exceeded', body)
  895. self.assertEqual(len(self.app.requests), 0)
  896. def test_max_file_count_exceeded(self):
  897. key = b'abc'
  898. sig, env, body = self._make_sig_env_body(
  899. '/v1/AUTH_test/container', 'http://brim.net', 1024, 1,
  900. int(time() + 86400), key)
  901. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  902. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  903. self._fake_cache_env('AUTH_test', [key]))
  904. env['swift.infocache'][get_cache_key(
  905. 'AUTH_test', 'container')] = {'meta': {}}
  906. self.app = FakeApp(iter([('201 Created', {}, b''),
  907. ('201 Created', {}, b'')]))
  908. self.auth = tempauth.filter_factory({})(self.app)
  909. self.formpost = formpost.filter_factory({})(self.auth)
  910. status = [None]
  911. headers = [None]
  912. exc_info = [None]
  913. def start_response(s, h, e=None):
  914. status[0] = s
  915. headers[0] = h
  916. exc_info[0] = e
  917. body = b''.join(self.formpost(env, start_response))
  918. status = status[0]
  919. headers = headers[0]
  920. exc_info = exc_info[0]
  921. self.assertEqual(status, '303 See Other')
  922. location = None
  923. for h, v in headers:
  924. if h.lower() == 'location':
  925. location = v
  926. self.assertEqual(
  927. location,
  928. 'http://brim.net?status=400&message=max%20file%20count%20exceeded')
  929. self.assertIsNone(exc_info)
  930. self.assertTrue(
  931. b'http://brim.net?status=400&message=max%20file%20count%20exceeded'
  932. in body)
  933. self.assertEqual(len(self.app.requests), 1)
  934. self.assertEqual(self.app.requests[0].body, b'Test File\nOne\n')
  935. def test_subrequest_does_not_pass_query(self):
  936. key = b'abc'
  937. sig, env, body = self._make_sig_env_body(
  938. '/v1/AUTH_test/container', '', 1024, 10, int(time() + 86400), key)
  939. env['QUERY_STRING'] = 'this=should&not=get&passed'
  940. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  941. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  942. self._fake_cache_env('AUTH_test', [key]))
  943. env['swift.infocache'][get_cache_key(
  944. 'AUTH_test', 'container')] = {'meta': {}}
  945. self.app = FakeApp(
  946. iter([('201 Created', {}, b''),
  947. ('201 Created', {}, b'')]),
  948. check_no_query_string=True)
  949. self.auth = tempauth.filter_factory({})(self.app)
  950. self.formpost = formpost.filter_factory({})(self.auth)
  951. status = [None]
  952. headers = [None]
  953. exc_info = [None]
  954. def start_response(s, h, e=None):
  955. status[0] = s
  956. headers[0] = h
  957. exc_info[0] = e
  958. body = b''.join(self.formpost(env, start_response))
  959. status = status[0]
  960. headers = headers[0]
  961. exc_info = exc_info[0]
  962. # Make sure we 201 Created, which means we made the final subrequest
  963. # (and FakeApp verifies that no QUERY_STRING got passed).
  964. self.assertEqual(status, '201 Created')
  965. self.assertIsNone(exc_info)
  966. self.assertTrue(b'201 Created' in body)
  967. self.assertEqual(len(self.app.requests), 2)
  968. def test_subrequest_fails(self):
  969. key = b'abc'
  970. sig, env, body = self._make_sig_env_body(
  971. '/v1/AUTH_test/container', 'http://brim.net', 1024, 10,
  972. int(time() + 86400), key)
  973. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  974. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  975. self._fake_cache_env('AUTH_test', [key]))
  976. env['swift.infocache'][get_cache_key(
  977. 'AUTH_test', 'container')] = {'meta': {}}
  978. self.app = FakeApp(iter([('404 Not Found', {}, b''),
  979. ('201 Created', {}, b'')]))
  980. self.auth = tempauth.filter_factory({})(self.app)
  981. self.formpost = formpost.filter_factory({})(self.auth)
  982. status = [None]
  983. headers = [None]
  984. exc_info = [None]
  985. def start_response(s, h, e=None):
  986. status[0] = s
  987. headers[0] = h
  988. exc_info[0] = e
  989. body = b''.join(self.formpost(env, start_response))
  990. status = status[0]
  991. headers = headers[0]
  992. exc_info = exc_info[0]
  993. self.assertEqual(status, '303 See Other')
  994. location = None
  995. for h, v in headers:
  996. if h.lower() == 'location':
  997. location = v
  998. self.assertEqual(location, 'http://brim.net?status=404&message=')
  999. self.assertIsNone(exc_info)
  1000. self.assertTrue(b'http://brim.net?status=404&message=' in body)
  1001. self.assertEqual(len(self.app.requests), 1)
  1002. def test_truncated_attr_value(self):
  1003. key = b'abc'
  1004. redirect = 'a' * formpost.MAX_VALUE_LENGTH
  1005. max_file_size = 1024
  1006. max_file_count = 10
  1007. expires = int(time() + 86400)
  1008. sig, env, body = self._make_sig_env_body(
  1009. '/v1/AUTH_test/container', redirect, max_file_size, max_file_count,
  1010. expires, key)
  1011. # Tack on an extra char to redirect, but shouldn't matter since it
  1012. # should get truncated off on read.
  1013. redirect += 'b'
  1014. wsgi_input = '\r\n'.join([
  1015. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1016. 'Content-Disposition: form-data; name="redirect"',
  1017. '',
  1018. redirect,
  1019. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1020. 'Content-Disposition: form-data; name="max_file_size"',
  1021. '',
  1022. str(max_file_size),
  1023. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1024. 'Content-Disposition: form-data; name="max_file_count"',
  1025. '',
  1026. str(max_file_count),
  1027. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1028. 'Content-Disposition: form-data; name="expires"',
  1029. '',
  1030. str(expires),
  1031. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1032. 'Content-Disposition: form-data; name="signature"',
  1033. '',
  1034. sig,
  1035. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1036. 'Content-Disposition: form-data; name="file1"; '
  1037. 'filename="testfile1.txt"',
  1038. 'Content-Type: text/plain',
  1039. '',
  1040. 'Test File\nOne\n',
  1041. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1042. 'Content-Disposition: form-data; name="file2"; '
  1043. 'filename="testfile2.txt"',
  1044. 'Content-Type: text/plain',
  1045. '',
  1046. 'Test\nFile\nTwo\n',
  1047. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1048. 'Content-Disposition: form-data; name="file3"; filename=""',
  1049. 'Content-Type: application/octet-stream',
  1050. '',
  1051. '',
  1052. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR--',
  1053. '',
  1054. ])
  1055. if six.PY3:
  1056. wsgi_input = wsgi_input.encode('utf-8')
  1057. env['wsgi.input'] = BytesIO(wsgi_input)
  1058. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1059. self._fake_cache_env('AUTH_test', [key]))
  1060. env['swift.infocache'][get_cache_key(
  1061. 'AUTH_test', 'container')] = {'meta': {}}
  1062. self.app = FakeApp(iter([('201 Created', {}, b''),
  1063. ('201 Created', {}, b'')]))
  1064. self.auth = tempauth.filter_factory({})(self.app)
  1065. self.formpost = formpost.filter_factory({})(self.auth)
  1066. status = [None]
  1067. headers = [None]
  1068. exc_info = [None]
  1069. def start_response(s, h, e=None):
  1070. status[0] = s
  1071. headers[0] = h
  1072. exc_info[0] = e
  1073. body = b''.join(self.formpost(env, start_response))
  1074. status = status[0]
  1075. headers = headers[0]
  1076. exc_info = exc_info[0]
  1077. self.assertEqual(status, '303 See Other')
  1078. location = None
  1079. for h, v in headers:
  1080. if h.lower() == 'location':
  1081. location = v
  1082. self.assertEqual(
  1083. location,
  1084. ('a' * formpost.MAX_VALUE_LENGTH) + '?status=201&message=')
  1085. self.assertIsNone(exc_info)
  1086. self.assertIn(
  1087. (b'a' * formpost.MAX_VALUE_LENGTH) + b'?status=201&message=', body)
  1088. self.assertEqual(len(self.app.requests), 2)
  1089. self.assertEqual(self.app.requests[0].body, b'Test File\nOne\n')
  1090. self.assertEqual(self.app.requests[1].body, b'Test\nFile\nTwo\n')
  1091. def test_no_file_to_process(self):
  1092. key = b'abc'
  1093. redirect = 'http://brim.net'
  1094. max_file_size = 1024
  1095. max_file_count = 10
  1096. expires = int(time() + 86400)
  1097. sig, env, body = self._make_sig_env_body(
  1098. '/v1/AUTH_test/container', redirect, max_file_size, max_file_count,
  1099. expires, key)
  1100. wsgi_input = '\r\n'.join([
  1101. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1102. 'Content-Disposition: form-data; name="redirect"',
  1103. '',
  1104. redirect,
  1105. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1106. 'Content-Disposition: form-data; name="max_file_size"',
  1107. '',
  1108. str(max_file_size),
  1109. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1110. 'Content-Disposition: form-data; name="max_file_count"',
  1111. '',
  1112. str(max_file_count),
  1113. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1114. 'Content-Disposition: form-data; name="expires"',
  1115. '',
  1116. str(expires),
  1117. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1118. 'Content-Disposition: form-data; name="signature"',
  1119. '',
  1120. sig,
  1121. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR--',
  1122. '',
  1123. ])
  1124. if six.PY3:
  1125. wsgi_input = wsgi_input.encode('utf-8')
  1126. env['wsgi.input'] = BytesIO(wsgi_input)
  1127. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1128. self._fake_cache_env('AUTH_test', [key]))
  1129. env['swift.infocache'][get_cache_key(
  1130. 'AUTH_test', 'container')] = {'meta': {}}
  1131. self.app = FakeApp(iter([('201 Created', {}, b''),
  1132. ('201 Created', {}, b'')]))
  1133. self.auth = tempauth.filter_factory({})(self.app)
  1134. self.formpost = formpost.filter_factory({})(self.auth)
  1135. status = [None]
  1136. headers = [None]
  1137. exc_info = [None]
  1138. def start_response(s, h, e=None):
  1139. status[0] = s
  1140. headers[0] = h
  1141. exc_info[0] = e
  1142. body = b''.join(self.formpost(env, start_response))
  1143. status = status[0]
  1144. headers = headers[0]
  1145. exc_info = exc_info[0]
  1146. self.assertEqual(status, '303 See Other')
  1147. location = None
  1148. for h, v in headers:
  1149. if h.lower() == 'location':
  1150. location = v
  1151. self.assertEqual(
  1152. location,
  1153. 'http://brim.net?status=400&message=no%20files%20to%20process')
  1154. self.assertIsNone(exc_info)
  1155. self.assertTrue(
  1156. b'http://brim.net?status=400&message=no%20files%20to%20process'
  1157. in body)
  1158. self.assertEqual(len(self.app.requests), 0)
  1159. def test_formpost_without_useragent(self):
  1160. key = b'abc'
  1161. sig, env, body = self._make_sig_env_body(
  1162. '/v1/AUTH_test/container', 'http://redirect', 1024, 10,
  1163. int(time() + 86400), key, user_agent=False)
  1164. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1165. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1166. self._fake_cache_env('AUTH_test', [key]))
  1167. env['swift.infocache'][get_cache_key(
  1168. 'AUTH_test', 'container')] = {'meta': {}}
  1169. self.app = FakeApp(iter([('201 Created', {}, b''),
  1170. ('201 Created', {}, b'')]))
  1171. self.auth = tempauth.filter_factory({})(self.app)
  1172. self.formpost = formpost.filter_factory({})(self.auth)
  1173. def start_response(s, h, e=None):
  1174. pass
  1175. body = b''.join(self.formpost(env, start_response))
  1176. self.assertIn('User-Agent', self.app.requests[0].headers)
  1177. self.assertEqual(self.app.requests[0].headers['User-Agent'],
  1178. 'FormPost')
  1179. def test_formpost_with_origin(self):
  1180. key = b'abc'
  1181. sig, env, body = self._make_sig_env_body(
  1182. '/v1/AUTH_test/container', 'http://redirect', 1024, 10,
  1183. int(time() + 86400), key, user_agent=False)
  1184. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1185. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1186. self._fake_cache_env('AUTH_test', [key]))
  1187. env['swift.infocache'][get_cache_key(
  1188. 'AUTH_test', 'container')] = {'meta': {}}
  1189. env['HTTP_ORIGIN'] = 'http://localhost:5000'
  1190. self.app = FakeApp(iter([('201 Created', {}, b''),
  1191. ('201 Created',
  1192. {'Access-Control-Allow-Origin':
  1193. 'http://localhost:5000'}, b'')]))
  1194. self.auth = tempauth.filter_factory({})(self.app)
  1195. self.formpost = formpost.filter_factory({})(self.auth)
  1196. headers = {}
  1197. def start_response(s, h, e=None):
  1198. for k, v in h:
  1199. headers[k] = v
  1200. pass
  1201. body = b''.join(self.formpost(env, start_response))
  1202. self.assertEqual(headers['Access-Control-Allow-Origin'],
  1203. 'http://localhost:5000')
  1204. def test_formpost_with_multiple_keys(self):
  1205. key = b'ernie'
  1206. sig, env, body = self._make_sig_env_body(
  1207. '/v1/AUTH_test/container', 'http://redirect', 1024, 10,
  1208. int(time() + 86400), key)
  1209. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1210. # Stick it in X-Account-Meta-Temp-URL-Key-2 and make sure we get it
  1211. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1212. self._fake_cache_env('AUTH_test', [key]))
  1213. env['swift.infocache'][get_cache_key(
  1214. 'AUTH_test', 'container')] = {'meta': {}}
  1215. self.app = FakeApp(iter([('201 Created', {}, b''),
  1216. ('201 Created', {}, b'')]))
  1217. self.auth = tempauth.filter_factory({})(self.app)
  1218. self.formpost = formpost.filter_factory({})(self.auth)
  1219. status = [None]
  1220. headers = [None]
  1221. def start_response(s, h, e=None):
  1222. status[0] = s
  1223. headers[0] = h
  1224. body = b''.join(self.formpost(env, start_response))
  1225. self.assertEqual('303 See Other', status[0])
  1226. self.assertEqual(
  1227. 'http://redirect?status=201&message=',
  1228. dict(headers[0]).get('Location'))
  1229. def test_formpost_with_multiple_container_keys(self):
  1230. first_key = b'ernie'
  1231. second_key = b'bert'
  1232. keys = [first_key, second_key]
  1233. meta = {}
  1234. for idx, key in enumerate(keys):
  1235. meta_name = 'temp-url-key' + ("-%d" % (idx + 1) if idx else "")
  1236. if key:
  1237. meta[meta_name] = key
  1238. for key in keys:
  1239. sig, env, body = self._make_sig_env_body(
  1240. '/v1/AUTH_test/container', 'http://redirect', 1024, 10,
  1241. int(time() + 86400), key)
  1242. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1243. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1244. self._fake_cache_env('AUTH_test'))
  1245. # Stick it in X-Container-Meta-Temp-URL-Key-2 and ensure we get it
  1246. env['swift.infocache'][get_cache_key(
  1247. 'AUTH_test', 'container')] = {'meta': meta}
  1248. self.app = FakeApp(iter([('201 Created', {}, b''),
  1249. ('201 Created', {}, b'')]))
  1250. self.auth = tempauth.filter_factory({})(self.app)
  1251. self.formpost = formpost.filter_factory({})(self.auth)
  1252. status = [None]
  1253. headers = [None]
  1254. def start_response(s, h, e=None):
  1255. status[0] = s
  1256. headers[0] = h
  1257. body = b''.join(self.formpost(env, start_response))
  1258. self.assertEqual('303 See Other', status[0])
  1259. self.assertEqual(
  1260. 'http://redirect?status=201&message=',
  1261. dict(headers[0]).get('Location'))
  1262. def test_redirect(self):
  1263. key = b'abc'
  1264. sig, env, body = self._make_sig_env_body(
  1265. '/v1/AUTH_test/container', 'http://redirect', 1024, 10,
  1266. int(time() + 86400), key)
  1267. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1268. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1269. self._fake_cache_env('AUTH_test', [key]))
  1270. env['swift.infocache'][get_cache_key(
  1271. 'AUTH_test', 'container')] = {'meta': {}}
  1272. self.app = FakeApp(iter([('201 Created', {}, b''),
  1273. ('201 Created', {}, b'')]))
  1274. self.auth = tempauth.filter_factory({})(self.app)
  1275. self.formpost = formpost.filter_factory({})(self.auth)
  1276. status = [None]
  1277. headers = [None]
  1278. exc_info = [None]
  1279. def start_response(s, h, e=None):
  1280. status[0] = s
  1281. headers[0] = h
  1282. exc_info[0] = e
  1283. body = b''.join(self.formpost(env, start_response))
  1284. status = status[0]
  1285. headers = headers[0]
  1286. exc_info = exc_info[0]
  1287. self.assertEqual(status, '303 See Other')
  1288. location = None
  1289. for h, v in headers:
  1290. if h.lower() == 'location':
  1291. location = v
  1292. self.assertEqual(location, 'http://redirect?status=201&message=')
  1293. self.assertIsNone(exc_info)
  1294. self.assertTrue(location.encode('utf-8') in body)
  1295. self.assertEqual(len(self.app.requests), 2)
  1296. self.assertEqual(self.app.requests[0].body, b'Test File\nOne\n')
  1297. self.assertEqual(self.app.requests[1].body, b'Test\nFile\nTwo\n')
  1298. def test_redirect_with_query(self):
  1299. key = b'abc'
  1300. sig, env, body = self._make_sig_env_body(
  1301. '/v1/AUTH_test/container', 'http://redirect?one=two', 1024, 10,
  1302. int(time() + 86400), key)
  1303. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1304. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1305. self._fake_cache_env('AUTH_test', [key]))
  1306. env['swift.infocache'][get_cache_key(
  1307. 'AUTH_test', 'container')] = {'meta': {}}
  1308. self.app = FakeApp(iter([('201 Created', {}, b''),
  1309. ('201 Created', {}, b'')]))
  1310. self.auth = tempauth.filter_factory({})(self.app)
  1311. self.formpost = formpost.filter_factory({})(self.auth)
  1312. status = [None]
  1313. headers = [None]
  1314. exc_info = [None]
  1315. def start_response(s, h, e=None):
  1316. status[0] = s
  1317. headers[0] = h
  1318. exc_info[0] = e
  1319. body = b''.join(self.formpost(env, start_response))
  1320. status = status[0]
  1321. headers = headers[0]
  1322. exc_info = exc_info[0]
  1323. self.assertEqual(status, '303 See Other')
  1324. location = None
  1325. for h, v in headers:
  1326. if h.lower() == 'location':
  1327. location = v
  1328. self.assertEqual(location,
  1329. 'http://redirect?one=two&status=201&message=')
  1330. self.assertIsNone(exc_info)
  1331. self.assertTrue(location.encode('utf-8') in body)
  1332. self.assertEqual(len(self.app.requests), 2)
  1333. self.assertEqual(self.app.requests[0].body, b'Test File\nOne\n')
  1334. self.assertEqual(self.app.requests[1].body, b'Test\nFile\nTwo\n')
  1335. def test_no_redirect(self):
  1336. key = b'abc'
  1337. sig, env, body = self._make_sig_env_body(
  1338. '/v1/AUTH_test/container', '', 1024, 10, int(time() + 86400), key)
  1339. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1340. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1341. self._fake_cache_env('AUTH_test', [key]))
  1342. env['swift.infocache'][get_cache_key(
  1343. 'AUTH_test', 'container')] = {'meta': {}}
  1344. self.app = FakeApp(iter([('201 Created', {}, b''),
  1345. ('201 Created', {}, b'')]))
  1346. self.auth = tempauth.filter_factory({})(self.app)
  1347. self.formpost = formpost.filter_factory({})(self.auth)
  1348. status = [None]
  1349. headers = [None]
  1350. exc_info = [None]
  1351. def start_response(s, h, e=None):
  1352. status[0] = s
  1353. headers[0] = h
  1354. exc_info[0] = e
  1355. body = b''.join(self.formpost(env, start_response))
  1356. status = status[0]
  1357. headers = headers[0]
  1358. exc_info = exc_info[0]
  1359. self.assertEqual(status, '201 Created')
  1360. location = None
  1361. for h, v in headers:
  1362. if h.lower() == 'location':
  1363. location = v
  1364. self.assertIsNone(location)
  1365. self.assertIsNone(exc_info)
  1366. self.assertTrue(b'201 Created' in body)
  1367. self.assertEqual(len(self.app.requests), 2)
  1368. self.assertEqual(self.app.requests[0].body, b'Test File\nOne\n')
  1369. self.assertEqual(self.app.requests[1].body, b'Test\nFile\nTwo\n')
  1370. def test_no_redirect_expired(self):
  1371. key = b'abc'
  1372. sig, env, body = self._make_sig_env_body(
  1373. '/v1/AUTH_test/container', '', 1024, 10, int(time() - 10), key)
  1374. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1375. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1376. self._fake_cache_env('AUTH_test', [key]))
  1377. self.app = FakeApp(iter([('201 Created', {}, b''),
  1378. ('201 Created', {}, b'')]))
  1379. self.auth = tempauth.filter_factory({})(self.app)
  1380. self.formpost = formpost.filter_factory({})(self.auth)
  1381. status = [None]
  1382. headers = [None]
  1383. exc_info = [None]
  1384. def start_response(s, h, e=None):
  1385. status[0] = s
  1386. headers[0] = h
  1387. exc_info[0] = e
  1388. body = b''.join(self.formpost(env, start_response))
  1389. status = status[0]
  1390. headers = headers[0]
  1391. exc_info = exc_info[0]
  1392. self.assertEqual(status, '401 Unauthorized')
  1393. location = None
  1394. for h, v in headers:
  1395. if h.lower() == 'location':
  1396. location = v
  1397. self.assertIsNone(location)
  1398. self.assertIsNone(exc_info)
  1399. self.assertTrue(b'FormPost: Form Expired' in body)
  1400. def test_no_redirect_invalid_sig(self):
  1401. key = b'abc'
  1402. sig, env, body = self._make_sig_env_body(
  1403. '/v1/AUTH_test/container', '', 1024, 10, int(time() + 86400), key)
  1404. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1405. # Change key to invalidate sig
  1406. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1407. self._fake_cache_env('AUTH_test', [key + b' is bogus now']))
  1408. self.app = FakeApp(iter([('201 Created', {}, b''),
  1409. ('201 Created', {}, b'')]))
  1410. self.auth = tempauth.filter_factory({})(self.app)
  1411. self.formpost = formpost.filter_factory({})(self.auth)
  1412. status = [None]
  1413. headers = [None]
  1414. exc_info = [None]
  1415. def start_response(s, h, e=None):
  1416. status[0] = s
  1417. headers[0] = h
  1418. exc_info[0] = e
  1419. body = b''.join(self.formpost(env, start_response))
  1420. status = status[0]
  1421. headers = headers[0]
  1422. exc_info = exc_info[0]
  1423. self.assertEqual(status, '401 Unauthorized')
  1424. location = None
  1425. for h, v in headers:
  1426. if h.lower() == 'location':
  1427. location = v
  1428. self.assertIsNone(location)
  1429. self.assertIsNone(exc_info)
  1430. self.assertTrue(b'FormPost: Invalid Signature' in body)
  1431. def test_no_redirect_with_error(self):
  1432. key = b'abc'
  1433. sig, env, body = self._make_sig_env_body(
  1434. '/v1/AUTH_test/container', '', 1024, 10, int(time() + 86400), key)
  1435. env['wsgi.input'] = BytesIO(b'XX' + b'\r\n'.join(body))
  1436. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1437. self._fake_cache_env('AUTH_test', [key]))
  1438. self.app = FakeApp(iter([('201 Created', {}, b''),
  1439. ('201 Created', {}, b'')]))
  1440. self.auth = tempauth.filter_factory({})(self.app)
  1441. self.formpost = formpost.filter_factory({})(self.auth)
  1442. status = [None]
  1443. headers = [None]
  1444. exc_info = [None]
  1445. def start_response(s, h, e=None):
  1446. status[0] = s
  1447. headers[0] = h
  1448. exc_info[0] = e
  1449. body = b''.join(self.formpost(env, start_response))
  1450. status = status[0]
  1451. headers = headers[0]
  1452. exc_info = exc_info[0]
  1453. self.assertEqual(status, '400 Bad Request')
  1454. location = None
  1455. for h, v in headers:
  1456. if h.lower() == 'location':
  1457. location = v
  1458. self.assertIsNone(location)
  1459. self.assertIsNone(exc_info)
  1460. self.assertTrue(b'FormPost: invalid starting boundary' in body)
  1461. def test_no_v1(self):
  1462. key = b'abc'
  1463. sig, env, body = self._make_sig_env_body(
  1464. '/v2/AUTH_test/container', '', 1024, 10, int(time() + 86400), key)
  1465. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1466. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1467. self._fake_cache_env('AUTH_test', [key]))
  1468. self.app = FakeApp(iter([('201 Created', {}, b''),
  1469. ('201 Created', {}, b'')]))
  1470. self.auth = tempauth.filter_factory({})(self.app)
  1471. self.formpost = formpost.filter_factory({})(self.auth)
  1472. status = [None]
  1473. headers = [None]
  1474. exc_info = [None]
  1475. def start_response(s, h, e=None):
  1476. status[0] = s
  1477. headers[0] = h
  1478. exc_info[0] = e
  1479. body = b''.join(self.formpost(env, start_response))
  1480. status = status[0]
  1481. headers = headers[0]
  1482. exc_info = exc_info[0]
  1483. self.assertEqual(status, '401 Unauthorized')
  1484. location = None
  1485. for h, v in headers:
  1486. if h.lower() == 'location':
  1487. location = v
  1488. self.assertIsNone(location)
  1489. self.assertIsNone(exc_info)
  1490. self.assertTrue(b'FormPost: Invalid Signature' in body)
  1491. def test_empty_v1(self):
  1492. key = b'abc'
  1493. sig, env, body = self._make_sig_env_body(
  1494. '//AUTH_test/container', '', 1024, 10, int(time() + 86400), key)
  1495. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1496. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1497. self._fake_cache_env('AUTH_test', [key]))
  1498. self.app = FakeApp(iter([('201 Created', {}, b''),
  1499. ('201 Created', {}, b'')]))
  1500. self.auth = tempauth.filter_factory({})(self.app)
  1501. self.formpost = formpost.filter_factory({})(self.auth)
  1502. status = [None]
  1503. headers = [None]
  1504. exc_info = [None]
  1505. def start_response(s, h, e=None):
  1506. status[0] = s
  1507. headers[0] = h
  1508. exc_info[0] = e
  1509. body = b''.join(self.formpost(env, start_response))
  1510. status = status[0]
  1511. headers = headers[0]
  1512. exc_info = exc_info[0]
  1513. self.assertEqual(status, '401 Unauthorized')
  1514. location = None
  1515. for h, v in headers:
  1516. if h.lower() == 'location':
  1517. location = v
  1518. self.assertIsNone(location)
  1519. self.assertIsNone(exc_info)
  1520. self.assertTrue(b'FormPost: Invalid Signature' in body)
  1521. def test_empty_account(self):
  1522. key = b'abc'
  1523. sig, env, body = self._make_sig_env_body(
  1524. '/v1//container', '', 1024, 10, int(time() + 86400), key)
  1525. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1526. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1527. self._fake_cache_env('AUTH_test', [key]))
  1528. self.app = FakeApp(iter([('201 Created', {}, b''),
  1529. ('201 Created', {}, b'')]))
  1530. self.auth = tempauth.filter_factory({})(self.app)
  1531. self.formpost = formpost.filter_factory({})(self.auth)
  1532. status = [None]
  1533. headers = [None]
  1534. exc_info = [None]
  1535. def start_response(s, h, e=None):
  1536. status[0] = s
  1537. headers[0] = h
  1538. exc_info[0] = e
  1539. body = b''.join(self.formpost(env, start_response))
  1540. status = status[0]
  1541. headers = headers[0]
  1542. exc_info = exc_info[0]
  1543. self.assertEqual(status, '401 Unauthorized')
  1544. location = None
  1545. for h, v in headers:
  1546. if h.lower() == 'location':
  1547. location = v
  1548. self.assertIsNone(location)
  1549. self.assertIsNone(exc_info)
  1550. self.assertTrue(b'FormPost: Invalid Signature' in body)
  1551. def test_wrong_account(self):
  1552. key = b'abc'
  1553. sig, env, body = self._make_sig_env_body(
  1554. '/v1/AUTH_tst/container', '', 1024, 10, int(time() + 86400), key)
  1555. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1556. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1557. self._fake_cache_env('AUTH_test', [key]))
  1558. self.app = FakeApp(iter([
  1559. ('200 Ok', {'x-account-meta-temp-url-key': 'def'}, b''),
  1560. ('201 Created', {}, b''),
  1561. ('201 Created', {}, b'')]))
  1562. self.auth = tempauth.filter_factory({})(self.app)
  1563. self.formpost = formpost.filter_factory({})(self.auth)
  1564. status = [None]
  1565. headers = [None]
  1566. exc_info = [None]
  1567. def start_response(s, h, e=None):
  1568. status[0] = s
  1569. headers[0] = h
  1570. exc_info[0] = e
  1571. body = b''.join(self.formpost(env, start_response))
  1572. status = status[0]
  1573. headers = headers[0]
  1574. exc_info = exc_info[0]
  1575. self.assertEqual(status, '401 Unauthorized')
  1576. location = None
  1577. for h, v in headers:
  1578. if h.lower() == 'location':
  1579. location = v
  1580. self.assertIsNone(location)
  1581. self.assertIsNone(exc_info)
  1582. self.assertTrue(b'FormPost: Invalid Signature' in body)
  1583. def test_no_container(self):
  1584. key = b'abc'
  1585. sig, env, body = self._make_sig_env_body(
  1586. '/v1/AUTH_test', '', 1024, 10, int(time() + 86400), key)
  1587. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1588. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1589. self._fake_cache_env('AUTH_test', [key]))
  1590. self.app = FakeApp(iter([('201 Created', {}, b''),
  1591. ('201 Created', {}, b'')]))
  1592. self.auth = tempauth.filter_factory({})(self.app)
  1593. self.formpost = formpost.filter_factory({})(self.auth)
  1594. status = [None]
  1595. headers = [None]
  1596. exc_info = [None]
  1597. def start_response(s, h, e=None):
  1598. status[0] = s
  1599. headers[0] = h
  1600. exc_info[0] = e
  1601. body = b''.join(self.formpost(env, start_response))
  1602. status = status[0]
  1603. headers = headers[0]
  1604. exc_info = exc_info[0]
  1605. self.assertEqual(status, '401 Unauthorized')
  1606. location = None
  1607. for h, v in headers:
  1608. if h.lower() == 'location':
  1609. location = v
  1610. self.assertIsNone(location)
  1611. self.assertIsNone(exc_info)
  1612. self.assertTrue(b'FormPost: Invalid Signature' in body)
  1613. def test_completely_non_int_expires(self):
  1614. key = b'abc'
  1615. expires = int(time() + 86400)
  1616. sig, env, body = self._make_sig_env_body(
  1617. '/v1/AUTH_test/container', '', 1024, 10, expires, key)
  1618. for i, v in enumerate(body):
  1619. if v.decode('utf-8') == str(expires):
  1620. body[i] = b'badvalue'
  1621. break
  1622. env['wsgi.input'] = BytesIO(b'\r\n'.join(body))
  1623. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1624. self._fake_cache_env('AUTH_test', [key]))
  1625. self.app = FakeApp(iter([('201 Created', {}, b''),
  1626. ('201 Created', {}, b'')]))
  1627. self.auth = tempauth.filter_factory({})(self.app)
  1628. self.formpost = formpost.filter_factory({})(self.auth)
  1629. status = [None]
  1630. headers = [None]
  1631. exc_info = [None]
  1632. def start_response(s, h, e=None):
  1633. status[0] = s
  1634. headers[0] = h
  1635. exc_info[0] = e
  1636. body = b''.join(self.formpost(env, start_response))
  1637. status = status[0]
  1638. headers = headers[0]
  1639. exc_info = exc_info[0]
  1640. self.assertEqual(status, '400 Bad Request')
  1641. location = None
  1642. for h, v in headers:
  1643. if h.lower() == 'location':
  1644. location = v
  1645. self.assertIsNone(location)
  1646. self.assertIsNone(exc_info)
  1647. self.assertTrue(b'FormPost: expired not an integer' in body)
  1648. def test_x_delete_at(self):
  1649. delete_at = int(time() + 100)
  1650. x_delete_body_part = [
  1651. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1652. 'Content-Disposition: form-data; name="x_delete_at"',
  1653. '',
  1654. str(delete_at),
  1655. ]
  1656. if six.PY3:
  1657. x_delete_body_part = [line.encode('utf-8')
  1658. for line in x_delete_body_part]
  1659. key = b'abc'
  1660. sig, env, body = self._make_sig_env_body(
  1661. '/v1/AUTH_test/container', '', 1024, 10, int(time() + 86400), key)
  1662. wsgi_input = b'\r\n'.join(x_delete_body_part + body)
  1663. env['wsgi.input'] = BytesIO(wsgi_input)
  1664. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1665. self._fake_cache_env('AUTH_test', [key]))
  1666. env['swift.infocache'][get_cache_key(
  1667. 'AUTH_test', 'container')] = {'meta': {}}
  1668. self.app = FakeApp(iter([('201 Created', {}, b''),
  1669. ('201 Created', {}, b'')]))
  1670. self.auth = tempauth.filter_factory({})(self.app)
  1671. self.formpost = formpost.filter_factory({})(self.auth)
  1672. status = [None]
  1673. headers = [None]
  1674. exc_info = [None]
  1675. def start_response(s, h, e=None):
  1676. status[0] = s
  1677. headers[0] = h
  1678. exc_info[0] = e
  1679. body = b''.join(self.formpost(env, start_response))
  1680. status = status[0]
  1681. headers = headers[0]
  1682. exc_info = exc_info[0]
  1683. self.assertEqual(status, '201 Created')
  1684. self.assertTrue(b'201 Created' in body)
  1685. self.assertEqual(len(self.app.requests), 2)
  1686. self.assertIn("X-Delete-At", self.app.requests[0].headers)
  1687. self.assertIn("X-Delete-At", self.app.requests[1].headers)
  1688. self.assertEqual(delete_at,
  1689. self.app.requests[0].headers["X-Delete-At"])
  1690. self.assertEqual(delete_at,
  1691. self.app.requests[1].headers["X-Delete-At"])
  1692. def test_x_delete_at_not_int(self):
  1693. delete_at = "2014-07-16"
  1694. x_delete_body_part = [
  1695. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1696. 'Content-Disposition: form-data; name="x_delete_at"',
  1697. '',
  1698. str(delete_at),
  1699. ]
  1700. if six.PY3:
  1701. x_delete_body_part = [line.encode('utf-8')
  1702. for line in x_delete_body_part]
  1703. key = b'abc'
  1704. sig, env, body = self._make_sig_env_body(
  1705. '/v1/AUTH_test/container', '', 1024, 10, int(time() + 86400), key)
  1706. wsgi_input = b'\r\n'.join(x_delete_body_part + body)
  1707. env['wsgi.input'] = BytesIO(wsgi_input)
  1708. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1709. self._fake_cache_env('AUTH_test', [key]))
  1710. self.app = FakeApp(iter([('201 Created', {}, b''),
  1711. ('201 Created', {}, b'')]))
  1712. self.auth = tempauth.filter_factory({})(self.app)
  1713. self.formpost = formpost.filter_factory({})(self.auth)
  1714. status = [None]
  1715. headers = [None]
  1716. exc_info = [None]
  1717. def start_response(s, h, e=None):
  1718. status[0] = s
  1719. headers[0] = h
  1720. exc_info[0] = e
  1721. body = b''.join(self.formpost(env, start_response))
  1722. status = status[0]
  1723. headers = headers[0]
  1724. exc_info = exc_info[0]
  1725. self.assertEqual(status, '400 Bad Request')
  1726. self.assertTrue(b'FormPost: x_delete_at not an integer' in body)
  1727. def test_x_delete_after(self):
  1728. delete_after = 100
  1729. x_delete_body_part = [
  1730. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1731. 'Content-Disposition: form-data; name="x_delete_after"',
  1732. '',
  1733. str(delete_after),
  1734. ]
  1735. if six.PY3:
  1736. x_delete_body_part = [line.encode('utf-8')
  1737. for line in x_delete_body_part]
  1738. key = b'abc'
  1739. sig, env, body = self._make_sig_env_body(
  1740. '/v1/AUTH_test/container', '', 1024, 10, int(time() + 86400), key)
  1741. wsgi_input = b'\r\n'.join(x_delete_body_part + body)
  1742. env['wsgi.input'] = BytesIO(wsgi_input)
  1743. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1744. self._fake_cache_env('AUTH_test', [key]))
  1745. env['swift.infocache'][get_cache_key(
  1746. 'AUTH_test', 'container')] = {'meta': {}}
  1747. self.app = FakeApp(iter([('201 Created', {}, b''),
  1748. ('201 Created', {}, b'')]))
  1749. self.auth = tempauth.filter_factory({})(self.app)
  1750. self.formpost = formpost.filter_factory({})(self.auth)
  1751. status = [None]
  1752. headers = [None]
  1753. exc_info = [None]
  1754. def start_response(s, h, e=None):
  1755. status[0] = s
  1756. headers[0] = h
  1757. exc_info[0] = e
  1758. body = b''.join(self.formpost(env, start_response))
  1759. status = status[0]
  1760. headers = headers[0]
  1761. exc_info = exc_info[0]
  1762. self.assertEqual(status, '201 Created')
  1763. self.assertTrue(b'201 Created' in body)
  1764. self.assertEqual(len(self.app.requests), 2)
  1765. self.assertIn("X-Delete-After", self.app.requests[0].headers)
  1766. self.assertIn("X-Delete-After", self.app.requests[1].headers)
  1767. self.assertEqual(delete_after,
  1768. self.app.requests[0].headers["X-Delete-After"])
  1769. self.assertEqual(delete_after,
  1770. self.app.requests[1].headers["X-Delete-After"])
  1771. def test_x_delete_after_not_int(self):
  1772. delete_after = "2 days"
  1773. x_delete_body_part = [
  1774. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1775. 'Content-Disposition: form-data; name="x_delete_after"',
  1776. '',
  1777. str(delete_after),
  1778. ]
  1779. if six.PY3:
  1780. x_delete_body_part = [line.encode('utf-8')
  1781. for line in x_delete_body_part]
  1782. key = b'abc'
  1783. sig, env, body = self._make_sig_env_body(
  1784. '/v1/AUTH_test/container', '', 1024, 10, int(time() + 86400), key)
  1785. wsgi_input = b'\r\n'.join(x_delete_body_part + body)
  1786. env['wsgi.input'] = BytesIO(wsgi_input)
  1787. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1788. self._fake_cache_env('AUTH_test', [key]))
  1789. self.app = FakeApp(iter([('201 Created', {}, b''),
  1790. ('201 Created', {}, b'')]))
  1791. self.auth = tempauth.filter_factory({})(self.app)
  1792. self.formpost = formpost.filter_factory({})(self.auth)
  1793. status = [None]
  1794. headers = [None]
  1795. exc_info = [None]
  1796. def start_response(s, h, e=None):
  1797. status[0] = s
  1798. headers[0] = h
  1799. exc_info[0] = e
  1800. body = b''.join(self.formpost(env, start_response))
  1801. status = status[0]
  1802. headers = headers[0]
  1803. exc_info = exc_info[0]
  1804. self.assertEqual(status, '400 Bad Request')
  1805. self.assertTrue(b'FormPost: x_delete_after not an integer' in body)
  1806. def test_global_content_type_encoding(self):
  1807. body_part = [
  1808. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1809. 'Content-Disposition: form-data; name="content-encoding"',
  1810. '',
  1811. 'gzip',
  1812. '------WebKitFormBoundaryNcxTqxSlX7t4TDkR',
  1813. 'Content-Disposition: form-data; name="content-type"',
  1814. '',
  1815. 'text/html',
  1816. ]
  1817. if six.PY3:
  1818. body_part = [line.encode('utf-8') for line in body_part]
  1819. key = b'abc'
  1820. sig, env, body = self._make_sig_env_body(
  1821. '/v1/AUTH_test/container', '', 1024, 10, int(time() + 86400), key)
  1822. wsgi_input = b'\r\n'.join(body_part + body)
  1823. env['wsgi.input'] = BytesIO(wsgi_input)
  1824. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1825. self._fake_cache_env('AUTH_test', [key]))
  1826. env['swift.infocache'][get_cache_key(
  1827. 'AUTH_test', 'container')] = {'meta': {}}
  1828. self.app = FakeApp(iter([('201 Created', {}, b''),
  1829. ('201 Created', {}, b'')]))
  1830. self.auth = tempauth.filter_factory({})(self.app)
  1831. self.formpost = formpost.filter_factory({})(self.auth)
  1832. status = [None]
  1833. headers = [None]
  1834. exc_info = [None]
  1835. def start_response(s, h, e=None):
  1836. status[0] = s
  1837. headers[0] = h
  1838. exc_info[0] = e
  1839. body = b''.join(self.formpost(env, start_response))
  1840. status = status[0]
  1841. headers = headers[0]
  1842. exc_info = exc_info[0]
  1843. self.assertEqual(status, '201 Created')
  1844. self.assertTrue(b'201 Created' in body)
  1845. self.assertEqual(len(self.app.requests), 2)
  1846. self.assertIn("Content-Type", self.app.requests[0].headers)
  1847. self.assertIn("Content-Type", self.app.requests[1].headers)
  1848. self.assertIn("Content-Encoding", self.app.requests[0].headers)
  1849. self.assertIn("Content-Encoding", self.app.requests[1].headers)
  1850. self.assertEqual("text/html",
  1851. self.app.requests[0].headers["Content-Type"])
  1852. self.assertEqual("text/html",
  1853. self.app.requests[1].headers["Content-Type"])
  1854. self.assertEqual("gzip",
  1855. self.app.requests[0].headers["Content-Encoding"])
  1856. self.assertEqual("gzip",
  1857. self.app.requests[1].headers["Content-Encoding"])
  1858. def test_single_content_type_encoding(self):
  1859. key = b'abc'
  1860. sig, env, body = self._make_sig_env_body(
  1861. '/v1/AUTH_test/container', '', 1024, 10, int(time() + 86400), key)
  1862. wsgi_input = b'\r\n'.join(body)
  1863. env['wsgi.input'] = BytesIO(wsgi_input)
  1864. env['swift.infocache'][get_cache_key('AUTH_test')] = (
  1865. self._fake_cache_env('AUTH_test', [key]))
  1866. env['swift.infocache'][get_cache_key(
  1867. 'AUTH_test', 'container')] = {'meta': {}}
  1868. self.app = FakeApp(iter([('201 Created', {}, b''),
  1869. ('201 Created', {}, b'')]))
  1870. self.auth = tempauth.filter_factory({})(self.app)
  1871. self.formpost = formpost.filter_factory({})(self.auth)
  1872. status = [None]
  1873. headers = [None]
  1874. exc_info = [None]
  1875. def start_response(s, h, e=None):
  1876. status[0] = s
  1877. headers[0] = h
  1878. exc_info[0] = e
  1879. body = b''.join(self.formpost(env, start_response))
  1880. status = status[0]
  1881. headers = headers[0]
  1882. exc_info = exc_info[0]
  1883. self.assertEqual(status, '201 Created')
  1884. self.assertTrue(b'201 Created' in body)
  1885. self.assertEqual(len(self.app.requests), 2)
  1886. self.assertEqual(self.app.requests[1].body, b'Test\nFile\nTwo\n')
  1887. self.assertIn("Content-Type", self.app.requests[0].headers)
  1888. self.assertIn("Content-Type", self.app.requests[1].headers)
  1889. self.assertEqual("text/plain",
  1890. self.app.requests[0].headers["Content-Type"])
  1891. self.assertEqual("text/plain",
  1892. self.app.requests[1].headers["Content-Type"])
  1893. self.assertFalse("Content-Encoding" in self.app.requests[0].headers)
  1894. self.assertIn("Content-Encoding", self.app.requests[1].headers)
  1895. self.assertEqual("gzip",
  1896. self.app.requests[1].headers["Content-Encoding"])
  1897. if __name__ == '__main__':
  1898. unittest.main()