comparison test/db_test_base.py @ 5232:462b0f76fce8

issue2550864 - Potential information leakage via journal/history Fix this by making the hyperdb::Class::history function check for view permissions on the journaled properties. So a user that sees [hidden] for a property in the web interface doesn;t see the property changes in the history. While doing this, relocated the filter for quiet properties from the templating class to the hyperdb. Also added the skipquiet option to the history command in roundup-admin.py to enable filtering of quiet params. Also changed calls to history() in the backend databases to report all items. Changed inline documentation for all history calls that document the actions. The create action (before nov 6 2002) used to record all parameters. After that point the create call uses an empty dictionary. The filtering code depends on the create dictionary being empty. It may not operate properly on very old roundup databases. Changed calls to logging.getLogger to roundup.hyperdb.backends to allow filtering the back end while keeping hyperdb logging. In cgi/templating.py, changed history() function consolidating handiling of link and unlink actions Added tests for quiet property filtering and permission filtering of history.
author John Rouillard <rouilj@ieee.org>
date Fri, 14 Apr 2017 23:24:18 -0400
parents e1e40674a0bc
children ebc27b90aa3b
comparison
equal deleted inserted replaced
5231:8743b7226dc7 5232:462b0f76fce8
123 db.commit() 123 db.commit()
124 124
125 # nosy tests require this 125 # nosy tests require this
126 db.security.addPermissionToRole('User', 'View', 'msg') 126 db.security.addPermissionToRole('User', 'View', 'msg')
127 127
128 # quiet journal tests require this
129 # QuietJournal - reference used later in tests
130 v1 = db.security.addPermission(name='View', klass='user',
131 properties=['username', 'supervisor', 'assignable'],
132 description="Prevent users from seeing roles")
133
134 db.security.addPermissionToRole("User", v1)
128 135
129 class MyTestCase(object): 136 class MyTestCase(object):
130 def tearDown(self): 137 def tearDown(self):
131 if hasattr(self, 'db'): 138 if hasattr(self, 'db'):
132 self.db.close() 139 self.db.close()
970 result=self.db.issue.generateCreateNote(new_issue) 977 result=self.db.issue.generateCreateNote(new_issue)
971 self.assertEqual(result, '\n----------\ndeadline: 2016-07-13.22:39:00\nnosy: admin, fred\ntitle: title2') 978 self.assertEqual(result, '\n----------\ndeadline: 2016-07-13.22:39:00\nnosy: admin, fred\ntitle: title2')
972 self.db.issue.properties['nosy'].quiet=True 979 self.db.issue.properties['nosy'].quiet=True
973 self.db.issue.properties['deadline'].quiet=True 980 self.db.issue.properties['deadline'].quiet=True
974 981
982 def testViewPremJournal(self):
983 pass
984
975 def testQuietJournal(self): 985 def testQuietJournal(self):
976 # FIXME this doesn't work. I need to call 986 # FIXME There should be a test via
977 # template.py::_HTMLItem::history() and verify the output. 987 # template.py::_HTMLItem::history() and verify the output.
978 # not sure how to get there from here. -- rouilj 988 # not sure how to get there from here. -- rouilj
979 # make sure that the quiet properties: "assignable" and "age" are not 989
980 # returned as part of the journal 990 # The Class::history() method now does filtering of quiet
981 # so comment out tests here but leave framework for later. 991 # props. Make sure that the quiet properties: "assignable"
992 # and "age" are not returned as part of the journal
982 new_user=self.db.user.create(username="pete", age=10, assignable=False) 993 new_user=self.db.user.create(username="pete", age=10, assignable=False)
983 new_issue=self.db.issue.create(title="title", deadline=date.Date('2016-6-30.22:39')) 994 new_issue=self.db.issue.create(title="title", deadline=date.Date('2016-6-30.22:39'))
984 995
985 # change all quiet params. Verify they aren't returned in journal. 996 # change all quiet params. Verify they aren't returned in journal.
986 # between this and the issue class every type represented in hyperdb 997 # between this and the issue class every type represented in hyperdb
987 # should be initalized with a quiet parameter. 998 # should be initalized with a quiet parameter.
988 result=self.db.user.set(new_user, username="new", age=20, supervisor='3', assignable=True, 999 result=self.db.user.set(new_user, username="new", age=20,
989 password=password.Password("3456"), rating=4, realname="newname") 1000 supervisor='1', assignable=True,
990 result=self.db.user.history(new_user) 1001 password=password.Password("3456"),
991 #self.assertEqual(result, 20) 1002 rating=4, realname="newname")
992 1003 result=self.db.user.history(new_user, skipquiet=False)
993 # change all quiet params. Verify they aren't returned in object. 1004 '''
994 result=self.db.issue.set(new_issue, title="title2", deadline=date.Date('2016-6-30.22:39'), 1005 [('3', <Date 2017-04-14.02:12:20.922>, '1', 'create', {}),
1006 ('3', <Date 2017-04-14.02:12:20.922>, '1', 'set',
1007 {'username': 'pete', 'assignable': False,
1008 'supervisor': None, 'realname': None, 'rating': None,
1009 'age': 10, 'password': None})]
1010 '''
1011 expected = {'username': 'pete', 'assignable': False,
1012 'supervisor': None, 'realname': None, 'rating': None,
1013 'age': 10, 'password': None}
1014
1015 result.sort()
1016 (id, tx_date, user, action, args) = result[-1]
1017 # check piecewise ignoring date of transaction
1018 self.assertEqual('3', id)
1019 self.assertEqual('1', user)
1020 self.assertEqual('set', action)
1021 self.assertEqual(expected, args)
1022
1023 # change all quiet params on issue.
1024 result=self.db.issue.set(new_issue, title="title2",
1025 deadline=date.Date('2016-07-30.22:39'),
995 assignedto="2", nosy=["3", "2"]) 1026 assignedto="2", nosy=["3", "2"])
996 result=self.db.issue.generateCreateNote(new_issue) 1027 result=self.db.issue.generateCreateNote(new_issue)
997 #self.assertEqual(result, '\n----------\ntitle: title2') 1028 self.assertEqual(result, '\n----------\ntitle: title2')
1029
1030 # check history including quiet properties
1031 result=self.db.issue.history(new_issue, skipquiet=False)
1032 print result
1033 ''' output should be like:
1034 [ ... ('1', <Date 2017-04-14.01:41:08.466>, '1', 'set',
1035 {'assignedto': None, 'nosy': (('+', ['3', '2']),),
1036 'deadline': <Date 2016-06-30.22:39:00.000>,
1037 'title': 'title'})
1038 '''
1039 expected = {'assignedto': None,
1040 'nosy': (('+', ['3', '2']),),
1041 'deadline': date.Date('2016-06-30.22:39'),
1042 'title': 'title'}
1043
1044 result.sort()
1045 print "history include quiet props", result[-1]
1046 (id, tx_date, user, action, args) = result[-1]
1047 # check piecewise ignoring date of transaction
1048 self.assertEqual('1', id)
1049 self.assertEqual('1', user)
1050 self.assertEqual('set', action)
1051 self.assertEqual(expected, args)
1052
1053 # check history removing quiet properties
1054 result=self.db.issue.history(new_issue)
1055 ''' output should be like:
1056 [ ... ('1', <Date 2017-04-14.01:41:08.466>, '1', 'set',
1057 {'title': 'title'})
1058 '''
1059 expected = {'title': 'title'}
1060
1061 result.sort()
1062 print "history remove quiet props", result[-1]
1063 (id, tx_date, user, action, args) = result[-1]
1064 # check piecewise
1065 self.assertEqual('1', id)
1066 self.assertEqual('1', user)
1067 self.assertEqual('set', action)
1068 self.assertEqual(expected, args)
998 1069
999 # also test that we can make a property noisy 1070 # also test that we can make a property noisy
1000 self.db.issue.properties['nosy'].quiet=False 1071 self.db.issue.properties['nosy'].quiet=False
1001 self.db.issue.properties['deadline'].quiet=False 1072 self.db.issue.properties['deadline'].quiet=False
1002 result=self.db.issue.set(new_issue, title="title2", deadline=date.Date('2016-7-13.22:39'), 1073 result=self.db.issue.set(new_issue, title="title2",
1074 deadline=date.Date('2016-7-13.22:39'),
1003 assignedto="2", nosy=["1", "2"]) 1075 assignedto="2", nosy=["1", "2"])
1004 result=self.db.issue.generateCreateNote(new_issue) 1076 result=self.db.issue.generateCreateNote(new_issue)
1005 #self.assertEqual(result, '\n----------\ndeadline: 2016-07-13.22:39:00\nnosy: admin, fred\ntitle: title2') 1077 self.assertEqual(result, '\n----------\ndeadline: 2016-07-13.22:39:00\nnosy: admin, fred\ntitle: title2')
1078
1079
1080 # check history removing the current quiet properties
1081 result=self.db.issue.history(new_issue)
1082 expected = {'nosy': (('+', ['1']), ('-', ['3'])),
1083 'deadline': date.Date("2016-07-30.22:39:00.000")}
1084
1085 result.sort()
1086 (id, tx_date, user, action, args) = result[-1]
1087 # check piecewise
1088 self.assertEqual('1', id)
1089 self.assertEqual('1', user)
1090 self.assertEqual('set', action)
1091 self.assertEqual(expected, args)
1092
1093 # reset quiet props
1006 self.db.issue.properties['nosy'].quiet=True 1094 self.db.issue.properties['nosy'].quiet=True
1007 self.db.issue.properties['deadline'].quiet=True 1095 self.db.issue.properties['deadline'].quiet=True
1096
1097 # Change the role for the new_user.
1098 # If journal is retrieved by admin this adds the role
1099 # change as the last element. If retreived by non-admin
1100 # it should not be returned because the user has no
1101 # View permissons on role..
1102 result=self.db.user.set(new_user, roles="foo, bar")
1103
1104 # Verify last journal entry as admin is a role change
1105 # from None
1106 result=self.db.user.history(new_user, skipquiet=False)
1107 result.sort()
1108 ''' result should end like:
1109 [ ...
1110 ('3', <Date 2017-04-15.02:06:11.482>, '1', 'set',
1111 {'username': 'pete', 'assignable': False,
1112 'supervisor': None, 'realname': None,
1113 'rating': None, 'age': 10, 'password': None}),
1114 ('3', <Date 2017-04-15.02:06:11.482>, '1', 'link',
1115 ('issue', '1', 'nosy')),
1116 ('3', <Date 2017-04-15.02:06:11.482>, '1', 'unlink',
1117 ('issue', '1', 'nosy')),
1118 ('3', <Date 2017-04-15.02:06:11.482>, '1', 'set',
1119 {'roles': None})]
1120 '''
1121 (id, tx_date, user, action, args) = result[-1]
1122 expected= {'roles': None }
1123
1124 self.assertEqual('3', id)
1125 self.assertEqual('1', user)
1126 self.assertEqual('set', action)
1127 self.assertEqual(expected, args)
1128
1129 # set an existing user's role to User so it can
1130 # view some props of the user class (search backwards
1131 # for QuietJournal to see the properties, they should be:
1132 # 'username', 'supervisor', 'assignable' i.e. age is not
1133 # one of them.
1134 id = self.db.user.lookup("fred")
1135 result=self.db.user.set(id, roles="User")
1136 # make the user fred current.
1137 self.db.setCurrentUser('fred')
1138 self.assertEqual(self.db.getuid(), id)
1139
1140 # check history as the user fred
1141 # include quiet properties
1142 # but require View perms
1143 result=self.db.user.history(new_user, skipquiet=False)
1144 result.sort()
1145 ''' result should look like
1146 [('3', <Date 2017-04-15.01:43:26.911>, '1', 'create', {}),
1147 ('3', <Date 2017-04-15.01:43:26.911>, '1', 'set',
1148 {'username': 'pete', 'assignable': False,
1149 'supervisor': None, 'age': 10})]
1150 '''
1151 # analyze last item
1152 (id, tx_date, user, action, args) = result[-1]
1153 expected= {'username': 'pete', 'assignable': False,
1154 'supervisor': None}
1155
1156 self.assertEqual('3', id)
1157 self.assertEqual('1', user)
1158 self.assertEqual('set', action)
1159 self.assertEqual(expected, args)
1160
1161 # reset the user to admin
1162 self.db.setCurrentUser('admin')
1163 self.assertEqual(self.db.getuid(), '1') # admin is always 1
1008 1164
1009 def testJournals(self): 1165 def testJournals(self):
1010 muid = self.db.user.create(username="mary") 1166 muid = self.db.user.create(username="mary")
1011 self.db.user.create(username="pete") 1167 self.db.user.create(username="pete")
1012 self.db.issue.create(title="spam", status='1') 1168 self.db.issue.create(title="spam", status='1')
2189 ' User may view everything (View)\n', 2345 ' User may view everything (View)\n',
2190 ' User may access the web interface (Web Access)\n', 2346 ' User may access the web interface (Web Access)\n',
2191 ' User may manipulate user Roles through the web (Web Roles)\n', 2347 ' User may manipulate user Roles through the web (Web Roles)\n',
2192 ' User may use the email interface (Email Access)\n', 2348 ' User may use the email interface (Email Access)\n',
2193 'Role "anonymous":\n', 'Role "user":\n', 2349 'Role "anonymous":\n', 'Role "user":\n',
2194 ' User is allowed to access msg (View for "msg" only)\n']) 2350 ' User is allowed to access msg (View for "msg" only)\n',
2351 ' Prevent users from seeing roles (View for "user": [\'username\', \'supervisor\', \'assignable\'] only)\n'])
2195 2352
2196 2353
2197 self.nukeAndCreate() 2354 self.nukeAndCreate()
2198 tool = roundup.admin.AdminTool() 2355 tool = roundup.admin.AdminTool()
2199 tool.tracker_home = home 2356 tool.tracker_home = home

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