comparison roundup/hyperdb.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 198b6e810c67
comparison
equal deleted inserted replaced
5231:8743b7226dc7 5232:462b0f76fce8
21 __docformat__ = 'restructuredtext' 21 __docformat__ = 'restructuredtext'
22 22
23 # standard python modules 23 # standard python modules
24 import os, re, shutil, sys, weakref 24 import os, re, shutil, sys, weakref
25 import traceback 25 import traceback
26 import logging
26 27
27 # roundup modules 28 # roundup modules
28 import date, password 29 import date, password
29 from support import ensureParentsExist, PrioList 30 from support import ensureParentsExist, PrioList
30 from roundup.i18n import _ 31 from roundup.i18n import _
31 from roundup.cgi.exceptions import DetectorError 32 from roundup.cgi.exceptions import DetectorError
33
34 logger = logging.getLogger('roundup.hyperdb')
32 35
33 # 36 #
34 # Types 37 # Types
35 # 38 #
36 class _Type(object): 39 class _Type(object):
779 782
780 def addjournal(self, classname, nodeid, action, params): 783 def addjournal(self, classname, nodeid, action, params):
781 """ Journal the Action 784 """ Journal the Action
782 'action' may be: 785 'action' may be:
783 786
784 'create' or 'set' -- 'params' is a dictionary of property values 787 'set' -- 'params' is a dictionary of property values
788 'create' -- 'params' is an empty dictionary as of
789 Wed Nov 06 11:38:43 2002 +0000
785 'link' or 'unlink' -- 'params' is (classname, nodeid, propname) 790 'link' or 'unlink' -- 'params' is (classname, nodeid, propname)
786 'retire' -- 'params' is None 791 'retired' or 'restored'-- 'params' is None
787 """ 792 """
788 raise NotImplementedError 793 raise NotImplementedError
789 794
790 def getjournal(self, classname, nodeid): 795 def getjournal(self, classname, nodeid):
791 """ get the journal for id 796 """ get the journal for id
989 The node is completely removed from the hyperdb, including all journal 994 The node is completely removed from the hyperdb, including all journal
990 entries. It will no longer be available, and will generally break code 995 entries. It will no longer be available, and will generally break code
991 if there are any references to the node. 996 if there are any references to the node.
992 """ 997 """
993 998
994 def history(self, nodeid): 999 def history(self, nodeid, enforceperm=True, skipquiet=True):
995 """Retrieve the journal of edits on a particular node. 1000 """Retrieve the journal of edits on a particular node.
996 1001
997 'nodeid' must be the id of an existing node of this class or an 1002 'nodeid' must be the id of an existing node of this class or an
998 IndexError is raised. 1003 IndexError is raised.
999 1004
1001 1006
1002 (date, tag, action, params) 1007 (date, tag, action, params)
1003 1008
1004 'date' is a Timestamp object specifying the time of the change and 1009 'date' is a Timestamp object specifying the time of the change and
1005 'tag' is the journaltag specified when the database was opened. 1010 'tag' is the journaltag specified when the database was opened.
1011
1012 If the property to be displayed is a quiet property, it will
1013 not be shown. This can be disabled by setting skipquiet=False.
1014
1015 If the user requesting the history does not have View access
1016 to the property, the journal entry will not be shown. This can
1017 be disabled by setting enforceperm=False.
1006 """ 1018 """
1007 if not self.do_journal: 1019 if not self.do_journal:
1008 raise ValueError('Journalling is disabled for this class') 1020 raise ValueError('Journalling is disabled for this class')
1009 return self.db.getjournal(self.classname, nodeid) 1021
1022 perm = self.db.security.hasPermission
1023 journal = []
1024
1025 debug_logging = logger.isEnabledFor(logging.DEBUG)
1026
1027 for j in self.db.getjournal(self.classname, nodeid):
1028 id, evt_date, user, action, args = j
1029 if debug_logging:
1030 j_repr = "%s"%(j,)
1031 else:
1032 j_repr=''
1033 if args and type(args) == type({}):
1034 for k in args.keys():
1035 if skipquiet and self.properties[k].quiet:
1036 logger.debug("skipping quiet property %s in %s",
1037 k, j_repr)
1038 del j[4][k]
1039 continue
1040 if enforceperm and not perm("View",
1041 self.db.getuid(),
1042 self.classname,
1043 property=k ):
1044 logger.debug("skipping unViewable property %s in %s",
1045 k, j_repr)
1046 del j[4][k]
1047 continue
1048 if not args:
1049 logger.debug("Omitting journal entry for %s%s"
1050 " all props quiet in: %s",
1051 self.classname, nodeid, j_repr)
1052 continue
1053 journal.append(j)
1054 elif action in ['link', 'unlink' ] and type(args) == type(()):
1055 if len(args) == 3:
1056 linkcl, linkid, key = args
1057 cls = self.db.getclass(linkcl)
1058 if skipquiet and cls.properties[key].quiet:
1059 logger.debug("skipping quiet property %s in %s",
1060 key, j_repr)
1061 continue
1062 if enforceperm and not perm("View",
1063 self.db.getuid(),
1064 self.classname,
1065 property=key):
1066 logger.debug("skipping unViewable property %s in",
1067 key, j_repr)
1068 continue
1069 journal.append(j)
1070 else:
1071 logger.error("Invalid %s journal entry for %s%s: %s",
1072 action, self.classname, nodeid, j)
1073 elif action in ['create', 'retired', 'restored']:
1074 journal.append(j)
1075 else:
1076 logger.warning("Possibly malformed journal for %s%s %s",
1077 self.classname, nodeid, j)
1078 return journal
1010 1079
1011 # Locating nodes: 1080 # Locating nodes:
1012 def hasnode(self, nodeid): 1081 def hasnode(self, nodeid):
1013 """Determine if the given nodeid actually exists 1082 """Determine if the given nodeid actually exists
1014 """ 1083 """
1581 return self.cl.set(self.nodeid, **{name: value}) 1650 return self.cl.set(self.nodeid, **{name: value})
1582 except KeyError, value: 1651 except KeyError, value:
1583 raise AttributeError, str(value) 1652 raise AttributeError, str(value)
1584 def __setitem__(self, name, value): 1653 def __setitem__(self, name, value):
1585 self.cl.set(self.nodeid, **{name: value}) 1654 self.cl.set(self.nodeid, **{name: value})
1586 def history(self): 1655 def history(self, enforceperm=True, skipquiet=True):
1587 return self.cl.history(self.nodeid) 1656 return self.cl.history(self.nodeid,
1657 enforceperm=enforceperm,
1658 skipquiet=skipquiet )
1588 def retire(self): 1659 def retire(self):
1589 return self.cl.retire(self.nodeid) 1660 return self.cl.retire(self.nodeid)
1590 1661
1591 1662
1592 def Choice(name, db, *options): 1663 def Choice(name, db, *options):

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