comparison test/rest_common.py @ 5726:e199d0ae4a25

issue2551033: prevent reverse engineering hidden data by using etags as an oracle to identify when the right data has been guessed. Identified by Joseph Myers who also suggested remediation methods. Implemented John Rouillard.
author John Rouillard <rouilj@ieee.org>
date Thu, 23 May 2019 18:56:57 -0400
parents aea2cc142c1b
children 8b5171f353eb
comparison
equal deleted inserted replaced
5725:6923225fd781 5726:e199d0ae4a25
633 realname='JohnRandom', 633 realname='JohnRandom',
634 roles='User,Admin' 634 roles='User,Admin'
635 ) 635 )
636 636
637 node = self.db.user.getnode(self.joeid) 637 node = self.db.user.getnode(self.joeid)
638 etag = calculate_etag(node) 638 etag = calculate_etag(node, "zysjskakss")
639 items = node.items(protected=True) # include every item 639 items = node.items(protected=True) # include every item
640 print(repr(items)) 640 print(repr(items))
641 print(etag) 641 print(etag)
642 self.assertEqual(etag, "6adf97f83acf6453d4a6a4b1070f3754") 642 self.assertEqual(etag, "6adf97f83acf6453d4a6a4b1070f3754")
643 643
644 etag = calculate_etag(self.db.issue.getnode("1")) 644 etag = calculate_etag(self.db.issue.getnode("1"), "zysjskakss")
645 print(etag) 645 print(etag)
646 self.assertEqual(etag, "6adf97f83acf6453d4a6a4b1070f3754") 646 self.assertEqual(etag, "6adf97f83acf6453d4a6a4b1070f3754")
647 647
648 def testEtagProcessing(self): 648 def testEtagProcessing(self):
649 ''' 649 '''
665 del(self.headers) 665 del(self.headers)
666 except AttributeError: 666 except AttributeError:
667 pass 667 pass
668 668
669 form = cgi.FieldStorage() 669 form = cgi.FieldStorage()
670 etag = calculate_etag(self.db.user.getnode(self.joeid)) 670 etag = calculate_etag(self.db.user.getnode(self.joeid),
671 "zysjskakss")
671 form.list = [ 672 form.list = [
672 cgi.MiniFieldStorage('data', 'Joe Doe Doe'), 673 cgi.MiniFieldStorage('data', 'Joe Doe Doe'),
673 ] 674 ]
674 675
675 if mode == 'header': 676 if mode == 'header':
773 """ 774 """
774 # TEST #1 775 # TEST #1
775 # PUT: joe's 'realname' using json data. 776 # PUT: joe's 'realname' using json data.
776 # simulate: /rest/data/user/<id>/realname 777 # simulate: /rest/data/user/<id>/realname
777 # use etag in header 778 # use etag in header
778 etag = calculate_etag(self.db.user.getnode(self.joeid)) 779 etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
779 body=b'{ "data": "Joe Doe 1" }' 780 body=b'{ "data": "Joe Doe 1" }'
780 env = { "CONTENT_TYPE": "application/json", 781 env = { "CONTENT_TYPE": "application/json",
781 "CONTENT_LENGTH": len(body), 782 "CONTENT_LENGTH": len(body),
782 "REQUEST_METHOD": "PUT" 783 "REQUEST_METHOD": "PUT"
783 } 784 }
822 823
823 # TEST #2 824 # TEST #2
824 # Set joe's 'realname' using json data. 825 # Set joe's 'realname' using json data.
825 # simulate: /rest/data/user/<id>/realname 826 # simulate: /rest/data/user/<id>/realname
826 # use etag in payload 827 # use etag in payload
827 etag = calculate_etag(self.db.user.getnode(self.joeid)) 828 etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
828 etagb = etag.strip ('"') 829 etagb = etag.strip ('"')
829 body=s2b('{ "@etag": "\\"%s\\"", "data": "Joe Doe 2" }'%etagb) 830 body=s2b('{ "@etag": "\\"%s\\"", "data": "Joe Doe 2" }'%etagb)
830 env = { "CONTENT_TYPE": "application/json", 831 env = { "CONTENT_TYPE": "application/json",
831 "CONTENT_LENGTH": len(body), 832 "CONTENT_LENGTH": len(body),
832 "REQUEST_METHOD": "PUT", 833 "REQUEST_METHOD": "PUT",
861 # FieldStorage(None, None, []) 862 # FieldStorage(None, None, [])
862 # use etag from header 863 # use etag from header
863 # 864 #
864 # Also use GET on the uri via the dispatch to retrieve 865 # Also use GET on the uri via the dispatch to retrieve
865 # the results from the db. 866 # the results from the db.
866 etag = calculate_etag(self.db.user.getnode(self.joeid)) 867 etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
867 headers={"if-match": etag, 868 headers={"if-match": etag,
868 "accept": "application/vnd.json.test-v1+json", 869 "accept": "application/vnd.json.test-v1+json",
869 } 870 }
870 form = cgi.FieldStorage() 871 form = cgi.FieldStorage()
871 form.list = [ 872 form.list = [
899 # save address so we can use it later 900 # save address so we can use it later
900 stored_results = self.server.get_element('user', self.joeid, 901 stored_results = self.server.get_element('user', self.joeid,
901 self.empty_form) 902 self.empty_form)
902 self.assertEqual(self.dummy_client.response_code, 200) 903 self.assertEqual(self.dummy_client.response_code, 200)
903 904
904 etag = calculate_etag(self.db.user.getnode(self.joeid)) 905 etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
905 etagb = etag.strip ('"') 906 etagb = etag.strip ('"')
906 body=s2b('{ "address": "demo2@example.com", "@etag": "\\"%s\\""}'%etagb) 907 body=s2b('{ "address": "demo2@example.com", "@etag": "\\"%s\\""}'%etagb)
907 env = { "CONTENT_TYPE": "application/json", 908 env = { "CONTENT_TYPE": "application/json",
908 "CONTENT_LENGTH": len(body), 909 "CONTENT_LENGTH": len(body),
909 "REQUEST_METHOD": "PATCH" 910 "REQUEST_METHOD": "PATCH"
927 self.assertEqual(self.dummy_client.response_code, 200) 928 self.assertEqual(self.dummy_client.response_code, 200)
928 self.assertEqual(results['data']['attributes']['address'], 929 self.assertEqual(results['data']['attributes']['address'],
929 'demo2@example.com') 930 'demo2@example.com')
930 931
931 # and set it back reusing env and headers from last test 932 # and set it back reusing env and headers from last test
932 etag = calculate_etag(self.db.user.getnode(self.joeid)) 933 etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
933 etagb = etag.strip ('"') 934 etagb = etag.strip ('"')
934 body=s2b('{ "address": "%s", "@etag": "\\"%s\\""}'%( 935 body=s2b('{ "address": "%s", "@etag": "\\"%s\\""}'%(
935 stored_results['data']['attributes']['address'], 936 stored_results['data']['attributes']['address'],
936 etagb)) 937 etagb))
937 # reuse env and headers from prior test. 938 # reuse env and headers from prior test.
1047 self.assertEqual(msg, "Must provide the 'name' property.") 1048 self.assertEqual(msg, "Must provide the 'name' property.")
1048 1049
1049 1050
1050 # TEST #8 1051 # TEST #8
1051 # DELETE: delete issue 1 1052 # DELETE: delete issue 1
1052 etag = calculate_etag(self.db.issue.getnode("1")) 1053 etag = calculate_etag(self.db.issue.getnode("1"), "zysjskakss")
1053 etagb = etag.strip ('"') 1054 etagb = etag.strip ('"')
1054 env = {"CONTENT_TYPE": "application/json", 1055 env = {"CONTENT_TYPE": "application/json",
1055 "CONTENT_LEN": 0, 1056 "CONTENT_LEN": 0,
1056 "REQUEST_METHOD": "DELETE" } 1057 "REQUEST_METHOD": "DELETE" }
1057 # use text/plain header and request json output by appending 1058 # use text/plain header and request json output by appending
1348 self.assertEqual(self.dummy_client.response_code, 200) 1349 self.assertEqual(self.dummy_client.response_code, 200)
1349 self.assertEqual(results['data']['data'], 'Joe Random') 1350 self.assertEqual(results['data']['data'], 'Joe Random')
1350 1351
1351 # change Joe's realname via attribute uri - etag in header 1352 # change Joe's realname via attribute uri - etag in header
1352 form = cgi.FieldStorage() 1353 form = cgi.FieldStorage()
1353 etag = calculate_etag(self.db.user.getnode(self.joeid)) 1354 etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
1354 form.list = [ 1355 form.list = [
1355 cgi.MiniFieldStorage('data', 'Joe Doe Doe'), 1356 cgi.MiniFieldStorage('data', 'Joe Doe Doe'),
1356 ] 1357 ]
1357 1358
1358 self.headers = {'if-match': etag } # use etag in header 1359 self.headers = {'if-match': etag } # use etag in header
1371 # Also try to set protected items. The protected items should 1372 # Also try to set protected items. The protected items should
1372 # be ignored on put_element to make it easy to get the item 1373 # be ignored on put_element to make it easy to get the item
1373 # with all fields, change one field and put the result without 1374 # with all fields, change one field and put the result without
1374 # having to filter out protected items. 1375 # having to filter out protected items.
1375 form = cgi.FieldStorage() 1376 form = cgi.FieldStorage()
1376 etag = calculate_etag(self.db.user.getnode(self.joeid)) 1377 etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
1377 form.list = [ 1378 form.list = [
1378 cgi.MiniFieldStorage('creator', '3'), 1379 cgi.MiniFieldStorage('creator', '3'),
1379 cgi.MiniFieldStorage('realname', 'Joe Doe'), 1380 cgi.MiniFieldStorage('realname', 'Joe Doe'),
1380 cgi.MiniFieldStorage('@etag', etag) 1381 cgi.MiniFieldStorage('@etag', etag)
1381 ] 1382 ]
1392 1393
1393 # Try to reset joe's 'realname' and add a broken prop. 1394 # Try to reset joe's 'realname' and add a broken prop.
1394 # This should result in no change to the name and 1395 # This should result in no change to the name and
1395 # a 400 UsageError stating prop does not exist. 1396 # a 400 UsageError stating prop does not exist.
1396 form = cgi.FieldStorage() 1397 form = cgi.FieldStorage()
1397 etag = calculate_etag(self.db.user.getnode(self.joeid)) 1398 etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
1398 form.list = [ 1399 form.list = [
1399 cgi.MiniFieldStorage('JustKidding', '3'), 1400 cgi.MiniFieldStorage('JustKidding', '3'),
1400 cgi.MiniFieldStorage('realname', 'Joe Doe'), 1401 cgi.MiniFieldStorage('realname', 'Joe Doe'),
1401 cgi.MiniFieldStorage('@etag', etag) 1402 cgi.MiniFieldStorage('@etag', etag)
1402 ] 1403 ]
1416 def testPutAttribute(self): 1417 def testPutAttribute(self):
1417 # put protected property 1418 # put protected property
1418 # make sure we don't have permission issues 1419 # make sure we don't have permission issues
1419 self.db.setCurrentUser('admin') 1420 self.db.setCurrentUser('admin')
1420 form = cgi.FieldStorage() 1421 form = cgi.FieldStorage()
1421 etag = calculate_etag(self.db.user.getnode(self.joeid)) 1422 etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
1422 form.list = [ 1423 form.list = [
1423 cgi.MiniFieldStorage('data', '3'), 1424 cgi.MiniFieldStorage('data', '3'),
1424 cgi.MiniFieldStorage('@etag', etag) 1425 cgi.MiniFieldStorage('@etag', etag)
1425 ] 1426 ]
1426 results = self.server.put_attribute( 1427 results = self.server.put_attribute(
1438 1439
1439 # put invalid property 1440 # put invalid property
1440 # make sure we don't have permission issues 1441 # make sure we don't have permission issues
1441 self.db.setCurrentUser('admin') 1442 self.db.setCurrentUser('admin')
1442 form = cgi.FieldStorage() 1443 form = cgi.FieldStorage()
1443 etag = calculate_etag(self.db.user.getnode(self.joeid)) 1444 etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
1444 form.list = [ 1445 form.list = [
1445 cgi.MiniFieldStorage('data', '3'), 1446 cgi.MiniFieldStorage('data', '3'),
1446 cgi.MiniFieldStorage('@etag', etag) 1447 cgi.MiniFieldStorage('@etag', etag)
1447 ] 1448 ]
1448 results = self.server.put_attribute( 1449 results = self.server.put_attribute(
1580 self.assertEqual(len(results['attributes']['nosy']), 1) 1581 self.assertEqual(len(results['attributes']['nosy']), 1)
1581 self.assertListEqual(results['attributes']['nosy'], 1582 self.assertListEqual(results['attributes']['nosy'],
1582 [{'id': '1', 'link': self.url_pfx + 'user/1'}]) 1583 [{'id': '1', 'link': self.url_pfx + 'user/1'}])
1583 1584
1584 form = cgi.FieldStorage() 1585 form = cgi.FieldStorage()
1585 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1586 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1586 form.list.append(cgi.MiniFieldStorage('@etag', etag)) 1587 form.list.append(cgi.MiniFieldStorage('@etag', etag))
1587 # remove the title and nosy 1588 # remove the title and nosy
1588 results = self.server.delete_attribute( 1589 results = self.server.delete_attribute(
1589 'issue', issue_id, 'title', form 1590 'issue', issue_id, 'title', form
1590 ) 1591 )
1591 self.assertEqual(self.dummy_client.response_code, 200) 1592 self.assertEqual(self.dummy_client.response_code, 200)
1592 1593
1593 del(form.list[-1]) 1594 del(form.list[-1])
1594 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1595 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1595 form.list.append(cgi.MiniFieldStorage('@etag', etag)) 1596 form.list.append(cgi.MiniFieldStorage('@etag', etag))
1596 results = self.server.delete_attribute( 1597 results = self.server.delete_attribute(
1597 'issue', issue_id, 'nosy', form 1598 'issue', issue_id, 'nosy', form
1598 ) 1599 )
1599 self.assertEqual(self.dummy_client.response_code, 200) 1600 self.assertEqual(self.dummy_client.response_code, 200)
1605 self.assertEqual(len(results['attributes']['nosy']), 0) 1606 self.assertEqual(len(results['attributes']['nosy']), 0)
1606 self.assertListEqual(results['attributes']['nosy'], []) 1607 self.assertListEqual(results['attributes']['nosy'], [])
1607 self.assertEqual(results['attributes']['title'], None) 1608 self.assertEqual(results['attributes']['title'], None)
1608 1609
1609 # delete protected property 1610 # delete protected property
1610 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1611 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1611 form.list.append(cgi.MiniFieldStorage('@etag', etag)) 1612 form.list.append(cgi.MiniFieldStorage('@etag', etag))
1612 results = self.server.delete_attribute( 1613 results = self.server.delete_attribute(
1613 'issue', issue_id, 'creator', form 1614 'issue', issue_id, 'creator', form
1614 ) 1615 )
1615 expected= {'error': { 1616 expected= {'error': {
1622 self.assertEqual(type(results['error']['msg']), 1623 self.assertEqual(type(results['error']['msg']),
1623 type(expected['error']['msg'])) 1624 type(expected['error']['msg']))
1624 self.assertEqual(self.dummy_client.response_code, 405) 1625 self.assertEqual(self.dummy_client.response_code, 405)
1625 1626
1626 # delete required property 1627 # delete required property
1627 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1628 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1628 form.list.append(cgi.MiniFieldStorage('@etag', etag)) 1629 form.list.append(cgi.MiniFieldStorage('@etag', etag))
1629 results = self.server.delete_attribute( 1630 results = self.server.delete_attribute(
1630 'issue', issue_id, 'requireme', form 1631 'issue', issue_id, 'requireme', form
1631 ) 1632 )
1632 expected= {'error': {'status': 400, 1633 expected= {'error': {'status': 400,
1640 self.assertEqual(str(results['error']['msg']), 1641 self.assertEqual(str(results['error']['msg']),
1641 str(expected['error']['msg'])) 1642 str(expected['error']['msg']))
1642 self.assertEqual(self.dummy_client.response_code, 400) 1643 self.assertEqual(self.dummy_client.response_code, 400)
1643 1644
1644 # delete bogus property 1645 # delete bogus property
1645 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1646 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1646 form.list.append(cgi.MiniFieldStorage('@etag', etag)) 1647 form.list.append(cgi.MiniFieldStorage('@etag', etag))
1647 results = self.server.delete_attribute( 1648 results = self.server.delete_attribute(
1648 'issue', issue_id, 'nosuchprop', form 1649 'issue', issue_id, 'nosuchprop', form
1649 ) 1650 )
1650 expected= {'error': {'status': 400, 1651 expected= {'error': {'status': 400,
1674 cgi.MiniFieldStorage('nosy', '2') 1675 cgi.MiniFieldStorage('nosy', '2')
1675 ] 1676 ]
1676 results = self.server.patch_element('issue', issue_id, form) 1677 results = self.server.patch_element('issue', issue_id, form)
1677 self.assertEqual(self.dummy_client.response_code, 412) 1678 self.assertEqual(self.dummy_client.response_code, 412)
1678 1679
1679 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1680 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1680 form = cgi.FieldStorage() 1681 form = cgi.FieldStorage()
1681 form.list = [ 1682 form.list = [
1682 cgi.MiniFieldStorage('@op', 'add'), 1683 cgi.MiniFieldStorage('@op', 'add'),
1683 cgi.MiniFieldStorage('nosy', '2'), 1684 cgi.MiniFieldStorage('nosy', '2'),
1684 cgi.MiniFieldStorage('@etag', etag) 1685 cgi.MiniFieldStorage('@etag', etag)
1691 results = results['data'] 1692 results = results['data']
1692 self.assertEqual(self.dummy_client.response_code, 200) 1693 self.assertEqual(self.dummy_client.response_code, 200)
1693 self.assertEqual(len(results['attributes']['nosy']), 2) 1694 self.assertEqual(len(results['attributes']['nosy']), 2)
1694 self.assertListEqual(results['attributes']['nosy'], ['1', '2']) 1695 self.assertListEqual(results['attributes']['nosy'], ['1', '2'])
1695 1696
1696 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1697 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1697 form = cgi.FieldStorage() 1698 form = cgi.FieldStorage()
1698 form.list = [ 1699 form.list = [
1699 cgi.MiniFieldStorage('@op', 'add'), 1700 cgi.MiniFieldStorage('@op', 'add'),
1700 cgi.MiniFieldStorage('data', '3'), 1701 cgi.MiniFieldStorage('data', '3'),
1701 cgi.MiniFieldStorage('@etag', etag) 1702 cgi.MiniFieldStorage('@etag', etag)
1710 self.assertEqual(len(results['attributes']['nosy']), 3) 1711 self.assertEqual(len(results['attributes']['nosy']), 3)
1711 self.assertListEqual(results['attributes']['nosy'], ['1', '2', '3']) 1712 self.assertListEqual(results['attributes']['nosy'], ['1', '2', '3'])
1712 1713
1713 1714
1714 # patch invalid property 1715 # patch invalid property
1715 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1716 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1716 form = cgi.FieldStorage() 1717 form = cgi.FieldStorage()
1717 form.list = [ 1718 form.list = [
1718 cgi.MiniFieldStorage('@op', 'add'), 1719 cgi.MiniFieldStorage('@op', 'add'),
1719 cgi.MiniFieldStorage('data', '3'), 1720 cgi.MiniFieldStorage('data', '3'),
1720 cgi.MiniFieldStorage('@etag', etag) 1721 cgi.MiniFieldStorage('@etag', etag)
1756 self.assertEqual(results['attributes']['status'], '1') 1757 self.assertEqual(results['attributes']['status'], '1')
1757 self.assertEqual(len(results['attributes']['nosy']), 1) 1758 self.assertEqual(len(results['attributes']['nosy']), 1)
1758 self.assertListEqual(results['attributes']['nosy'], ['1']) 1759 self.assertListEqual(results['attributes']['nosy'], ['1'])
1759 1760
1760 # replace userid 2 to the nosy list and status = 3 1761 # replace userid 2 to the nosy list and status = 3
1761 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1762 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1762 form = cgi.FieldStorage() 1763 form = cgi.FieldStorage()
1763 form.list = [ 1764 form.list = [
1764 cgi.MiniFieldStorage('@op', 'replace'), 1765 cgi.MiniFieldStorage('@op', 'replace'),
1765 cgi.MiniFieldStorage('nosy', '2'), 1766 cgi.MiniFieldStorage('nosy', '2'),
1766 cgi.MiniFieldStorage('status', '3'), 1767 cgi.MiniFieldStorage('status', '3'),
1775 self.assertEqual(results['attributes']['status'], '3') 1776 self.assertEqual(results['attributes']['status'], '3')
1776 self.assertEqual(len(results['attributes']['nosy']), 1) 1777 self.assertEqual(len(results['attributes']['nosy']), 1)
1777 self.assertListEqual(results['attributes']['nosy'], ['2']) 1778 self.assertListEqual(results['attributes']['nosy'], ['2'])
1778 1779
1779 # replace status = 2 using status attribute 1780 # replace status = 2 using status attribute
1780 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1781 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1781 form = cgi.FieldStorage() 1782 form = cgi.FieldStorage()
1782 form.list = [ 1783 form.list = [
1783 cgi.MiniFieldStorage('@op', 'replace'), 1784 cgi.MiniFieldStorage('@op', 'replace'),
1784 cgi.MiniFieldStorage('data', '2'), 1785 cgi.MiniFieldStorage('data', '2'),
1785 cgi.MiniFieldStorage('@etag', etag) 1786 cgi.MiniFieldStorage('@etag', etag)
1792 results = results['data'] 1793 results = results['data']
1793 self.assertEqual(self.dummy_client.response_code, 200) 1794 self.assertEqual(self.dummy_client.response_code, 200)
1794 self.assertEqual(results['attributes']['status'], '2') 1795 self.assertEqual(results['attributes']['status'], '2')
1795 1796
1796 # try to set a protected prop. It should fail. 1797 # try to set a protected prop. It should fail.
1797 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1798 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1798 form = cgi.FieldStorage() 1799 form = cgi.FieldStorage()
1799 form.list = [ 1800 form.list = [
1800 cgi.MiniFieldStorage('@op', 'replace'), 1801 cgi.MiniFieldStorage('@op', 'replace'),
1801 cgi.MiniFieldStorage('creator', '2'), 1802 cgi.MiniFieldStorage('creator', '2'),
1802 cgi.MiniFieldStorage('@etag', etag) 1803 cgi.MiniFieldStorage('@etag', etag)
1813 str(expected['error']['msg'])) 1814 str(expected['error']['msg']))
1814 self.assertEqual(self.dummy_client.response_code, 400) 1815 self.assertEqual(self.dummy_client.response_code, 400)
1815 1816
1816 # try to set a protected prop using patch_attribute. It should 1817 # try to set a protected prop using patch_attribute. It should
1817 # fail with a 405 bad/unsupported method. 1818 # fail with a 405 bad/unsupported method.
1818 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1819 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1819 form = cgi.FieldStorage() 1820 form = cgi.FieldStorage()
1820 form.list = [ 1821 form.list = [
1821 cgi.MiniFieldStorage('@op', 'replace'), 1822 cgi.MiniFieldStorage('@op', 'replace'),
1822 cgi.MiniFieldStorage('data', '2'), 1823 cgi.MiniFieldStorage('data', '2'),
1823 cgi.MiniFieldStorage('@etag', etag) 1824 cgi.MiniFieldStorage('@etag', etag)
1859 self.assertEqual(len(results['attributes']['nosy']), 2) 1860 self.assertEqual(len(results['attributes']['nosy']), 2)
1860 self.assertEqual(results['attributes']['nosy'], ['1', '2']) 1861 self.assertEqual(results['attributes']['nosy'], ['1', '2'])
1861 1862
1862 # remove the nosy list and the title 1863 # remove the nosy list and the title
1863 form = cgi.FieldStorage() 1864 form = cgi.FieldStorage()
1864 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1865 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1865 form.list = [ 1866 form.list = [
1866 cgi.MiniFieldStorage('@op', 'remove'), 1867 cgi.MiniFieldStorage('@op', 'remove'),
1867 cgi.MiniFieldStorage('nosy', ''), 1868 cgi.MiniFieldStorage('nosy', ''),
1868 cgi.MiniFieldStorage('title', ''), 1869 cgi.MiniFieldStorage('title', ''),
1869 cgi.MiniFieldStorage('@etag', etag) 1870 cgi.MiniFieldStorage('@etag', etag)
1878 self.assertEqual(results['attributes']['title'], None) 1879 self.assertEqual(results['attributes']['title'], None)
1879 self.assertEqual(len(results['attributes']['nosy']), 0) 1880 self.assertEqual(len(results['attributes']['nosy']), 0)
1880 self.assertEqual(results['attributes']['nosy'], []) 1881 self.assertEqual(results['attributes']['nosy'], [])
1881 1882
1882 # try to remove a protected prop. It should fail. 1883 # try to remove a protected prop. It should fail.
1883 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1884 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1884 form = cgi.FieldStorage() 1885 form = cgi.FieldStorage()
1885 form.list = [ 1886 form.list = [
1886 cgi.MiniFieldStorage('@op', 'remove'), 1887 cgi.MiniFieldStorage('@op', 'remove'),
1887 cgi.MiniFieldStorage('creator', '2'), 1888 cgi.MiniFieldStorage('creator', '2'),
1888 cgi.MiniFieldStorage('@etag', etag) 1889 cgi.MiniFieldStorage('@etag', etag)
1898 self.assertEqual(str(results['error']['msg']), 1899 self.assertEqual(str(results['error']['msg']),
1899 str(expected['error']['msg'])) 1900 str(expected['error']['msg']))
1900 self.assertEqual(self.dummy_client.response_code, 400) 1901 self.assertEqual(self.dummy_client.response_code, 400)
1901 1902
1902 # try to remove a required prop. it should fail 1903 # try to remove a required prop. it should fail
1903 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1904 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1904 form.list = [ 1905 form.list = [
1905 cgi.MiniFieldStorage('@op', 'remove'), 1906 cgi.MiniFieldStorage('@op', 'remove'),
1906 cgi.MiniFieldStorage('requireme', ''), 1907 cgi.MiniFieldStorage('requireme', ''),
1907 cgi.MiniFieldStorage('@etag', etag) 1908 cgi.MiniFieldStorage('@etag', etag)
1908 ] 1909 ]
1937 self.assertEqual(self.dummy_client.response_code, 412) 1938 self.assertEqual(self.dummy_client.response_code, 412)
1938 self.assertFalse(self.db.issue.is_retired(issue_id)) 1939 self.assertFalse(self.db.issue.is_retired(issue_id))
1939 1940
1940 # execute action retire 1941 # execute action retire
1941 form = cgi.FieldStorage() 1942 form = cgi.FieldStorage()
1942 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1943 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1943 form.list = [ 1944 form.list = [
1944 cgi.MiniFieldStorage('@op', 'action'), 1945 cgi.MiniFieldStorage('@op', 'action'),
1945 cgi.MiniFieldStorage('@action_name', 'retire'), 1946 cgi.MiniFieldStorage('@action_name', 'retire'),
1946 cgi.MiniFieldStorage('@etag', etag) 1947 cgi.MiniFieldStorage('@etag', etag)
1947 ] 1948 ]
1973 self.assertEqual(len(results['attributes']['nosy']), 3) 1974 self.assertEqual(len(results['attributes']['nosy']), 3)
1974 self.assertEqual(results['attributes']['nosy'], ['1', '2', '3']) 1975 self.assertEqual(results['attributes']['nosy'], ['1', '2', '3'])
1975 1976
1976 # remove the nosy list and the title 1977 # remove the nosy list and the title
1977 form = cgi.FieldStorage() 1978 form = cgi.FieldStorage()
1978 etag = calculate_etag(self.db.issue.getnode(issue_id)) 1979 etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1979 form.list = [ 1980 form.list = [
1980 cgi.MiniFieldStorage('@op', 'remove'), 1981 cgi.MiniFieldStorage('@op', 'remove'),
1981 cgi.MiniFieldStorage('nosy', '1, 2'), 1982 cgi.MiniFieldStorage('nosy', '1, 2'),
1982 cgi.MiniFieldStorage('@etag', etag) 1983 cgi.MiniFieldStorage('@etag', etag)
1983 ] 1984 ]

Roundup Issue Tracker: http://roundup-tracker.org/