Skip to content

Commit 92fff6b

Browse files
committed
issue2551033: actually use the key in hmac generation. Finally add
testing for etag generation to verify that etag is stable and that the key is actually being used.
1 parent 00c1eee commit 92fff6b

File tree

2 files changed

+98
-41
lines changed

2 files changed

+98
-41
lines changed

roundup/rest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def calculate_etag (node, key, classname="Missing", id="0"):
142142
'''
143143

144144
items = node.items(protected=True) # include every item
145-
etag = hmac.new(bs2b(repr(sorted(items)))).hexdigest()
145+
etag = hmac.new(bs2b(key),bs2b(repr(sorted(items)))).hexdigest()
146146
logger.debug("object=%s%s; tag=%s; repr=%s", classname, id,
147147
etag, repr(node.items(protected=True)))
148148
# Quotes are part of ETag spec, normal headers don't have quotes

test/rest_common.py

Lines changed: 97 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ def setUp(self):
8787

8888
self.db.Otk = self.db.getOTKManager()
8989

90+
self.db.config['WEB_SECRET_KEY'] = "XyzzykrnKm45Sd"
91+
9092
def tearDown(self):
9193
self.db.close()
9294
try:
@@ -619,31 +621,58 @@ def testPagination(self):
619621
# page_size < 0
620622
# page_index < 0
621623

622-
def notestEtagGeneration(self):
624+
def testEtagGeneration(self):
623625
''' Make sure etag generation is stable
624626
625-
FIXME need to mock somehow date.Date() when creating
626-
the target to be mocked. The differing dates makes
627-
this test impossible.
627+
This mocks date.Date() when creating the target to be
628+
etagged. Differing dates make this test impossible.
628629
'''
630+
from roundup import date
631+
632+
originalDate = date.Date
633+
634+
dummy=date.Date('2000-06-26.00:34:02.0')
635+
636+
# is a closure the best way to return a static Date object??
637+
def dummyDate(adate=None):
638+
def dummyClosure(adate=None, translator=None):
639+
return dummy
640+
return dummyClosure
641+
642+
date.Date = dummyDate()
643+
629644
newuser = self.db.user.create(
630645
username='john',
631-
password=password.Password('random1'),
646+
password=password.Password('random1', scheme='plaintext'),
632647
address='[email protected]',
633648
realname='JohnRandom',
634649
roles='User,Admin'
635650
)
636651

637-
node = self.db.user.getnode(self.joeid)
638-
etag = calculate_etag(node, "zysjskakss")
652+
# verify etag matches what we calculated in the past
653+
node = self.db.user.getnode(newuser)
654+
etag = calculate_etag(node, self.db.config['WEB_SECRET_KEY'])
639655
items = node.items(protected=True) # include every item
640-
print(repr(items))
656+
print(repr(sorted(items)))
641657
print(etag)
642-
self.assertEqual(etag, "6adf97f83acf6453d4a6a4b1070f3754")
658+
self.assertEqual(etag, '"f2901b2653b813eeb277c0dc84c03ba3"')
643659

644-
etag = calculate_etag(self.db.issue.getnode("1"), "zysjskakss")
660+
# modify key and verify we have a different etag
661+
etag = calculate_etag(node, self.db.config['WEB_SECRET_KEY'] + "a")
662+
items = node.items(protected=True) # include every item
663+
print(repr(sorted(items)))
645664
print(etag)
646-
self.assertEqual(etag, "6adf97f83acf6453d4a6a4b1070f3754")
665+
self.assertNotEqual(etag, '"f2901b2653b813eeb277c0dc84c03ba3"')
666+
667+
# change data and verify we have a different etag
668+
node.username="Paul"
669+
etag = calculate_etag(node, self.db.config['WEB_SECRET_KEY'])
670+
items = node.items(protected=True) # include every item
671+
print(repr(sorted(items)))
672+
print(etag)
673+
self.assertEqual(etag, '"98f8052193220afdb649c6caaaa80e40"')
674+
675+
date.Date = originalDate
647676

648677
def testEtagProcessing(self):
649678
'''
@@ -668,7 +697,7 @@ def testEtagProcessing(self):
668697

669698
form = cgi.FieldStorage()
670699
etag = calculate_etag(self.db.user.getnode(self.joeid),
671-
"zysjskakss")
700+
self.db.config['WEB_SECRET_KEY'])
672701
form.list = [
673702
cgi.MiniFieldStorage('data', 'Joe Doe Doe'),
674703
]
@@ -776,7 +805,8 @@ def testDispatch(self):
776805
# PUT: joe's 'realname' using json data.
777806
# simulate: /rest/data/user/<id>/realname
778807
# use etag in header
779-
etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
808+
etag = calculate_etag(self.db.user.getnode(self.joeid),
809+
self.db.config['WEB_SECRET_KEY'])
780810
body=b'{ "data": "Joe Doe 1" }'
781811
env = { "CONTENT_TYPE": "application/json",
782812
"CONTENT_LENGTH": len(body),
@@ -825,7 +855,8 @@ def testDispatch(self):
825855
# Set joe's 'realname' using json data.
826856
# simulate: /rest/data/user/<id>/realname
827857
# use etag in payload
828-
etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
858+
etag = calculate_etag(self.db.user.getnode(self.joeid),
859+
self.db.config['WEB_SECRET_KEY'])
829860
etagb = etag.strip ('"')
830861
body=s2b('{ "@etag": "\\"%s\\"", "data": "Joe Doe 2" }'%etagb)
831862
env = { "CONTENT_TYPE": "application/json",
@@ -864,7 +895,8 @@ def testDispatch(self):
864895
#
865896
# Also use GET on the uri via the dispatch to retrieve
866897
# the results from the db.
867-
etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
898+
etag = calculate_etag(self.db.user.getnode(self.joeid),
899+
self.db.config['WEB_SECRET_KEY'])
868900
headers={"if-match": etag,
869901
"accept": "application/vnd.json.test-v1+json",
870902
}
@@ -902,7 +934,8 @@ def testDispatch(self):
902934
self.empty_form)
903935
self.assertEqual(self.dummy_client.response_code, 200)
904936

905-
etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
937+
etag = calculate_etag(self.db.user.getnode(self.joeid),
938+
self.db.config['WEB_SECRET_KEY'])
906939
etagb = etag.strip ('"')
907940
body=s2b('{ "address": "[email protected]", "@etag": "\\"%s\\""}'%etagb)
908941
env = { "CONTENT_TYPE": "application/json",
@@ -930,7 +963,8 @@ def testDispatch(self):
930963
931964

932965
# and set it back reusing env and headers from last test
933-
etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
966+
etag = calculate_etag(self.db.user.getnode(self.joeid),
967+
self.db.config['WEB_SECRET_KEY'])
934968
etagb = etag.strip ('"')
935969
body=s2b('{ "address": "%s", "@etag": "\\"%s\\""}'%(
936970
stored_results['data']['attributes']['address'],
@@ -1050,7 +1084,8 @@ def testDispatch(self):
10501084

10511085
# TEST #8
10521086
# DELETE: delete issue 1
1053-
etag = calculate_etag(self.db.issue.getnode("1"), "zysjskakss")
1087+
etag = calculate_etag(self.db.issue.getnode("1"),
1088+
self.db.config['WEB_SECRET_KEY'])
10541089
etagb = etag.strip ('"')
10551090
env = {"CONTENT_TYPE": "application/json",
10561091
"CONTENT_LEN": 0,
@@ -1351,7 +1386,8 @@ def testPutElement(self):
13511386

13521387
# change Joe's realname via attribute uri - etag in header
13531388
form = cgi.FieldStorage()
1354-
etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
1389+
etag = calculate_etag(self.db.user.getnode(self.joeid),
1390+
self.db.config['WEB_SECRET_KEY'])
13551391
form.list = [
13561392
cgi.MiniFieldStorage('data', 'Joe Doe Doe'),
13571393
]
@@ -1374,7 +1410,8 @@ def testPutElement(self):
13741410
# with all fields, change one field and put the result without
13751411
# having to filter out protected items.
13761412
form = cgi.FieldStorage()
1377-
etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
1413+
etag = calculate_etag(self.db.user.getnode(self.joeid),
1414+
self.db.config['WEB_SECRET_KEY'])
13781415
form.list = [
13791416
cgi.MiniFieldStorage('creator', '3'),
13801417
cgi.MiniFieldStorage('realname', 'Joe Doe'),
@@ -1395,7 +1432,8 @@ def testPutElement(self):
13951432
# This should result in no change to the name and
13961433
# a 400 UsageError stating prop does not exist.
13971434
form = cgi.FieldStorage()
1398-
etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
1435+
etag = calculate_etag(self.db.user.getnode(self.joeid),
1436+
self.db.config['WEB_SECRET_KEY'])
13991437
form.list = [
14001438
cgi.MiniFieldStorage('JustKidding', '3'),
14011439
cgi.MiniFieldStorage('realname', 'Joe Doe'),
@@ -1419,7 +1457,8 @@ def testPutAttribute(self):
14191457
# make sure we don't have permission issues
14201458
self.db.setCurrentUser('admin')
14211459
form = cgi.FieldStorage()
1422-
etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
1460+
etag = calculate_etag(self.db.user.getnode(self.joeid),
1461+
self.db.config['WEB_SECRET_KEY'])
14231462
form.list = [
14241463
cgi.MiniFieldStorage('data', '3'),
14251464
cgi.MiniFieldStorage('@etag', etag)
@@ -1441,7 +1480,8 @@ def testPutAttribute(self):
14411480
# make sure we don't have permission issues
14421481
self.db.setCurrentUser('admin')
14431482
form = cgi.FieldStorage()
1444-
etag = calculate_etag(self.db.user.getnode(self.joeid), "zysjskakss")
1483+
etag = calculate_etag(self.db.user.getnode(self.joeid),
1484+
self.db.config['WEB_SECRET_KEY'])
14451485
form.list = [
14461486
cgi.MiniFieldStorage('data', '3'),
14471487
cgi.MiniFieldStorage('@etag', etag)
@@ -1583,7 +1623,8 @@ def testDeleteAttributeUri(self):
15831623
[{'id': '1', 'link': self.url_pfx + 'user/1'}])
15841624

15851625
form = cgi.FieldStorage()
1586-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1626+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1627+
self.db.config['WEB_SECRET_KEY'])
15871628
form.list.append(cgi.MiniFieldStorage('@etag', etag))
15881629
# remove the title and nosy
15891630
results = self.server.delete_attribute(
@@ -1592,7 +1633,8 @@ def testDeleteAttributeUri(self):
15921633
self.assertEqual(self.dummy_client.response_code, 200)
15931634

15941635
del(form.list[-1])
1595-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1636+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1637+
self.db.config['WEB_SECRET_KEY'])
15961638
form.list.append(cgi.MiniFieldStorage('@etag', etag))
15971639
results = self.server.delete_attribute(
15981640
'issue', issue_id, 'nosy', form
@@ -1608,7 +1650,8 @@ def testDeleteAttributeUri(self):
16081650
self.assertEqual(results['attributes']['title'], None)
16091651

16101652
# delete protected property
1611-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1653+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1654+
self.db.config['WEB_SECRET_KEY'])
16121655
form.list.append(cgi.MiniFieldStorage('@etag', etag))
16131656
results = self.server.delete_attribute(
16141657
'issue', issue_id, 'creator', form
@@ -1625,7 +1668,8 @@ def testDeleteAttributeUri(self):
16251668
self.assertEqual(self.dummy_client.response_code, 405)
16261669

16271670
# delete required property
1628-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1671+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1672+
self.db.config['WEB_SECRET_KEY'])
16291673
form.list.append(cgi.MiniFieldStorage('@etag', etag))
16301674
results = self.server.delete_attribute(
16311675
'issue', issue_id, 'requireme', form
@@ -1643,7 +1687,8 @@ def testDeleteAttributeUri(self):
16431687
self.assertEqual(self.dummy_client.response_code, 400)
16441688

16451689
# delete bogus property
1646-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1690+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1691+
self.db.config['WEB_SECRET_KEY'])
16471692
form.list.append(cgi.MiniFieldStorage('@etag', etag))
16481693
results = self.server.delete_attribute(
16491694
'issue', issue_id, 'nosuchprop', form
@@ -1677,7 +1722,8 @@ def testPatchAdd(self):
16771722
results = self.server.patch_element('issue', issue_id, form)
16781723
self.assertEqual(self.dummy_client.response_code, 412)
16791724

1680-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1725+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1726+
self.db.config['WEB_SECRET_KEY'])
16811727
form = cgi.FieldStorage()
16821728
form.list = [
16831729
cgi.MiniFieldStorage('@op', 'add'),
@@ -1694,7 +1740,8 @@ def testPatchAdd(self):
16941740
self.assertEqual(len(results['attributes']['nosy']), 2)
16951741
self.assertListEqual(results['attributes']['nosy'], ['1', '2'])
16961742

1697-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1743+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1744+
self.db.config['WEB_SECRET_KEY'])
16981745
form = cgi.FieldStorage()
16991746
form.list = [
17001747
cgi.MiniFieldStorage('@op', 'add'),
@@ -1713,7 +1760,8 @@ def testPatchAdd(self):
17131760

17141761

17151762
# patch invalid property
1716-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1763+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1764+
self.db.config['WEB_SECRET_KEY'])
17171765
form = cgi.FieldStorage()
17181766
form.list = [
17191767
cgi.MiniFieldStorage('@op', 'add'),
@@ -1759,7 +1807,8 @@ def testPatchReplace(self):
17591807
self.assertListEqual(results['attributes']['nosy'], ['1'])
17601808

17611809
# replace userid 2 to the nosy list and status = 3
1762-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1810+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1811+
self.db.config['WEB_SECRET_KEY'])
17631812
form = cgi.FieldStorage()
17641813
form.list = [
17651814
cgi.MiniFieldStorage('@op', 'replace'),
@@ -1778,7 +1827,8 @@ def testPatchReplace(self):
17781827
self.assertListEqual(results['attributes']['nosy'], ['2'])
17791828

17801829
# replace status = 2 using status attribute
1781-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1830+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1831+
self.db.config['WEB_SECRET_KEY'])
17821832
form = cgi.FieldStorage()
17831833
form.list = [
17841834
cgi.MiniFieldStorage('@op', 'replace'),
@@ -1795,7 +1845,8 @@ def testPatchReplace(self):
17951845
self.assertEqual(results['attributes']['status'], '2')
17961846

17971847
# try to set a protected prop. It should fail.
1798-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1848+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1849+
self.db.config['WEB_SECRET_KEY'])
17991850
form = cgi.FieldStorage()
18001851
form.list = [
18011852
cgi.MiniFieldStorage('@op', 'replace'),
@@ -1816,7 +1867,8 @@ def testPatchReplace(self):
18161867

18171868
# try to set a protected prop using patch_attribute. It should
18181869
# fail with a 405 bad/unsupported method.
1819-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1870+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1871+
self.db.config['WEB_SECRET_KEY'])
18201872
form = cgi.FieldStorage()
18211873
form.list = [
18221874
cgi.MiniFieldStorage('@op', 'replace'),
@@ -1862,7 +1914,8 @@ def testPatchRemoveAll(self):
18621914

18631915
# remove the nosy list and the title
18641916
form = cgi.FieldStorage()
1865-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1917+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1918+
self.db.config['WEB_SECRET_KEY'])
18661919
form.list = [
18671920
cgi.MiniFieldStorage('@op', 'remove'),
18681921
cgi.MiniFieldStorage('nosy', ''),
@@ -1881,7 +1934,8 @@ def testPatchRemoveAll(self):
18811934
self.assertEqual(results['attributes']['nosy'], [])
18821935

18831936
# try to remove a protected prop. It should fail.
1884-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1937+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1938+
self.db.config['WEB_SECRET_KEY'])
18851939
form = cgi.FieldStorage()
18861940
form.list = [
18871941
cgi.MiniFieldStorage('@op', 'remove'),
@@ -1901,7 +1955,8 @@ def testPatchRemoveAll(self):
19011955
self.assertEqual(self.dummy_client.response_code, 400)
19021956

19031957
# try to remove a required prop. it should fail
1904-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1958+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1959+
self.db.config['WEB_SECRET_KEY'])
19051960
form.list = [
19061961
cgi.MiniFieldStorage('@op', 'remove'),
19071962
cgi.MiniFieldStorage('requireme', ''),
@@ -1940,7 +1995,8 @@ def testPatchAction(self):
19401995

19411996
# execute action retire
19421997
form = cgi.FieldStorage()
1943-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
1998+
etag = calculate_etag(self.db.issue.getnode(issue_id),
1999+
self.db.config['WEB_SECRET_KEY'])
19442000
form.list = [
19452001
cgi.MiniFieldStorage('@op', 'action'),
19462002
cgi.MiniFieldStorage('@action_name', 'retire'),
@@ -1976,7 +2032,8 @@ def testPatchRemove(self):
19762032

19772033
# remove the nosy list and the title
19782034
form = cgi.FieldStorage()
1979-
etag = calculate_etag(self.db.issue.getnode(issue_id), "zysjskakss")
2035+
etag = calculate_etag(self.db.issue.getnode(issue_id),
2036+
self.db.config['WEB_SECRET_KEY'])
19802037
form.list = [
19812038
cgi.MiniFieldStorage('@op', 'remove'),
19822039
cgi.MiniFieldStorage('nosy', '1, 2'),

0 commit comments

Comments
 (0)