@@ -103,30 +103,37 @@ class RetireAction(Action):
103103
104104 def handle (self ):
105105 """Retire the context item."""
106- # if we want to view the index template now, then unset the nodeid
106+ # ensure modification comes via POST
107+ if self .client .env ['REQUEST_METHOD' ] != 'POST' :
108+ self .client .error_message .append (self ._ ('Invalid request' ))
109+
110+ # if we want to view the index template now, then unset the itemid
107111 # context info (a special-case for retire actions on the index page)
108- nodeid = self .nodeid
112+ itemid = self .nodeid
109113 if self .template == 'index' :
110114 self .client .nodeid = None
111115
112116 # make sure we don't try to retire admin or anonymous
113117 if self .classname == 'user' and \
114- self .db .user .get (nodeid , 'username' ) in ('admin' , 'anonymous' ):
118+ self .db .user .get (itemid , 'username' ) in ('admin' , 'anonymous' ):
115119 raise ValueError , self ._ (
116120 'You may not retire the admin or anonymous user' )
117121
122+ # check permission
123+ if not self .hasPermission ('Retire' , classname = self .classname ,
124+ itemid = itemid ):
125+ raise exceptions .Unauthorised , self ._ (
126+ 'You do not have permission to retire %(class)s'
127+ ) % {'class' : self .classname }
128+
118129 # do the retire
119- self .db .getclass (self .classname ).retire (nodeid )
130+ self .db .getclass (self .classname ).retire (itemid )
120131 self .db .commit ()
121132
122133 self .client .ok_message .append (
123134 self ._ ('%(classname)s %(itemid)s has been retired' )% {
124- 'classname' : self .classname .capitalize (), 'itemid' : nodeid })
135+ 'classname' : self .classname .capitalize (), 'itemid' : itemid })
125136
126- def hasPermission (self , permission , classname = Action ._marker , itemid = None ):
127- if itemid is None :
128- itemid = self .nodeid
129- return Action .hasPermission (self , permission , classname , itemid )
130137
131138class SearchAction (Action ):
132139 name = 'search'
@@ -274,12 +281,19 @@ def handle(self):
274281 The "rows" CGI var defines the CSV-formatted entries for the class. New
275282 nodes are identified by the ID 'X' (or any other non-existent ID) and
276283 removed lines are retired.
277-
278284 """
285+ # ensure modification comes via POST
286+ if self .client .env ['REQUEST_METHOD' ] != 'POST' :
287+ self .client .error_message .append (self ._ ('Invalid request' ))
288+
289+ # figure the properties list for the class
279290 cl = self .db .classes [self .classname ]
280- idlessprops = cl .getprops (protected = 0 ).keys ()
281- idlessprops .sort ()
282- props = ['id' ] + idlessprops
291+ props_without_id = cl .getprops (protected = 0 ).keys ()
292+
293+ # the incoming CSV data will always have the properties in colums
294+ # sorted and starting with the "id" column
295+ props_without_id .sort ()
296+ props = ['id' ] + props_without_id
283297
284298 # do the edit
285299 rows = StringIO .StringIO (self .form ['rows' ].value )
@@ -293,25 +307,38 @@ def handle(self):
293307 if values == props :
294308 continue
295309
296- # extract the nodeid
297- nodeid , values = values [0 ], values [1 :]
298- found [nodeid ] = 1
310+ # extract the itemid
311+ itemid , values = values [0 ], values [1 :]
312+ found [itemid ] = 1
299313
300314 # see if the node exists
301- if nodeid in ('x' , 'X' ) or not cl .hasnode (nodeid ):
315+ if itemid in ('x' , 'X' ) or not cl .hasnode (itemid ):
302316 exists = 0
317+
318+ # check permission to create this item
319+ if not self .hasPermission ('Create' , classname = self .classname ):
320+ raise exceptions .Unauthorised , self ._ (
321+ 'You do not have permission to create %(class)s'
322+ ) % {'class' : self .classname }
303323 else :
304324 exists = 1
305325
306326 # confirm correct weight
307- if len (idlessprops ) != len (values ):
327+ if len (props_without_id ) != len (values ):
308328 self .client .error_message .append (
309329 self ._ ('Not enough values on line %(line)s' )% {'line' :line })
310330 return
311331
312332 # extract the new values
313333 d = {}
314- for name , value in zip (idlessprops , values ):
334+ for name , value in zip (props_without_id , values ):
335+ # check permission to edit this property on this item
336+ if exists and not self .hasPermission ('Edit' , itemid = itemid ,
337+ classname = self .classname , property = name ):
338+ raise exceptions .Unauthorised , self ._ (
339+ 'You do not have permission to edit %(class)s'
340+ ) % {'class' : self .classname }
341+
315342 prop = cl .properties [name ]
316343 value = value .strip ()
317344 # only add the property if it has a value
@@ -340,15 +367,21 @@ def handle(self):
340367 # perform the edit
341368 if exists :
342369 # edit existing
343- cl .set (nodeid , ** d )
370+ cl .set (itemid , ** d )
344371 else :
345372 # new node
346373 found [cl .create (** d )] = 1
347374
348375 # retire the removed entries
349- for nodeid in cl .list ():
350- if not found .has_key (nodeid ):
351- cl .retire (nodeid )
376+ for itemid in cl .list ():
377+ if not found .has_key (itemid ):
378+ # check permission to retire this item
379+ if not self .hasPermission ('Retire' , itemid = itemid ,
380+ classname = self .classname ):
381+ raise exceptions .Unauthorised , self ._ (
382+ 'You do not have permission to retire %(class)s'
383+ ) % {'class' : self .classname }
384+ cl .retire (itemid )
352385
353386 # all OK
354387 self .db .commit ()
@@ -493,10 +526,8 @@ def editItemPermission(self, props, classname=_cn_marker, itemid=None):
493526 # The user must have permission to edit each of the properties
494527 # being changed.
495528 for p in props :
496- if not self .hasPermission ('Edit' ,
497- itemid = itemid ,
498- classname = classname ,
499- property = p ):
529+ if not self .hasPermission ('Edit' , itemid = itemid ,
530+ classname = classname , property = p ):
500531 return 0
501532 # Since the user has permission to edit all of the properties,
502533 # the edit is OK.
@@ -554,6 +585,10 @@ def handle(self):
554585 See parsePropsFromForm and _editnodes for special variables.
555586
556587 """
588+ # ensure modification comes via POST
589+ if self .client .env ['REQUEST_METHOD' ] != 'POST' :
590+ self .client .error_message .append (self ._ ('Invalid request' ))
591+
557592 user_activity = self .lastUserActivity ()
558593 if user_activity :
559594 props = self .detectCollision (user_activity , self .lastNodeActivity ())
@@ -596,6 +631,10 @@ def handle(self):
596631 This follows the same form as the EditItemAction, with the same
597632 special form values.
598633 '''
634+ # ensure modification comes via POST
635+ if self .client .env ['REQUEST_METHOD' ] != 'POST' :
636+ self .client .error_message .append (self ._ ('Invalid request' ))
637+
599638 # parse the props from the form
600639 try :
601640 props , links = self .client .parsePropsFromForm (create = 1 )
@@ -604,6 +643,11 @@ def handle(self):
604643 % str (message ))
605644 return
606645
646+ # guard against new user creation that would bypass security checks
647+ for key in props :
648+ if 'user' in key :
649+ return
650+
607651 # handle the props - edit or create
608652 try :
609653 # when it hits the None element, it'll set self.nodeid
@@ -773,6 +817,10 @@ def handle(self):
773817
774818 Return 1 on successful login.
775819 """
820+ # ensure modification comes via POST
821+ if self .client .env ['REQUEST_METHOD' ] != 'POST' :
822+ self .client .error_message .append (self ._ ('Invalid request' ))
823+
776824 # parse the props from the form
777825 try :
778826 props , links = self .client .parsePropsFromForm (create = 1 )
@@ -887,6 +935,10 @@ def handle(self):
887935 Sets up a session for the user which contains the login credentials.
888936
889937 """
938+ # ensure modification comes via POST
939+ if self .client .env ['REQUEST_METHOD' ] != 'POST' :
940+ self .client .error_message .append (self ._ ('Invalid request' ))
941+
890942 # we need the username at a minimum
891943 if not self .form .has_key ('__login_name' ):
892944 self .client .error_message .append (self ._ ('Username required' ))
@@ -986,7 +1038,16 @@ def handle(self):
9861038
9871039 # and search
9881040 for itemid in klass .filter (matches , filterspec , sort , group ):
989- self .client ._socket_op (writer .writerow , [str (klass .get (itemid , col )) for col in columns ])
1041+ row = []
1042+ for name in columns :
1043+ # check permission to view this property on this item
1044+ if exists and not self .hasPermission ('View' , itemid = itemid ,
1045+ classname = request .classname , property = name ):
1046+ raise exceptions .Unauthorised , self ._ (
1047+ 'You do not have permission to view %(class)s'
1048+ ) % {'class' : request .classname }
1049+ row .append (str (klass .get (itemid , name )))
1050+ self .client ._socket_op (writer .writerow , row )
9901051
9911052 return '\n '
9921053
0 commit comments