comparison roundup/cgi/client.py @ 2005:fc52d57c6c3e

documentation cleanup
author Richard Jones <richard@users.sourceforge.net>
date Wed, 11 Feb 2004 23:55:10 +0000
parents 1782fe36e7b8
children 1b11ffd8015e
comparison
equal deleted inserted replaced
2004:1782fe36e7b8 2005:fc52d57c6c3e
1 # $Id: client.py,v 1.155 2004-02-11 21:34:31 jlgijsbers Exp $ 1 # $Id: client.py,v 1.156 2004-02-11 23:55:09 richard Exp $
2 2
3 __doc__ = """ 3 """WWW request handler (also used in the stand-alone server).
4 WWW request handler (also used in the stand-alone server).
5 """ 4 """
5 __docformat__ = 'restructuredtext'
6 6
7 import os, os.path, cgi, StringIO, urlparse, re, traceback, mimetypes, urllib 7 import os, os.path, cgi, StringIO, urlparse, re, traceback, mimetypes, urllib
8 import binascii, Cookie, time, random, stat, rfc822 8 import binascii, Cookie, time, random, stat, rfc822
9 9
10 from roundup import roundupdb, date, hyperdb, password 10 from roundup import roundupdb, date, hyperdb, password
14 from roundup.cgi.exceptions import * 14 from roundup.cgi.exceptions import *
15 from roundup.cgi.form_parser import FormParser 15 from roundup.cgi.form_parser import FormParser
16 from roundup.mailer import Mailer, MessageSendError 16 from roundup.mailer import Mailer, MessageSendError
17 17
18 def initialiseSecurity(security): 18 def initialiseSecurity(security):
19 ''' Create some Permissions and Roles on the security object 19 '''Create some Permissions and Roles on the security object
20 20
21 This function is directly invoked by security.Security.__init__() 21 This function is directly invoked by security.Security.__init__()
22 as a part of the Security object instantiation. 22 as a part of the Security object instantiation.
23 ''' 23 '''
24 security.addPermission(name="Web Registration", 24 security.addPermission(name="Web Registration",
25 description="User may register through the web") 25 description="User may register through the web")
26 p = security.addPermission(name="Web Access", 26 p = security.addPermission(name="Web Access",
27 description="User may access the web interface") 27 description="User may access the web interface")
44 if ok.has_key(match.group(3).lower()): 44 if ok.has_key(match.group(3).lower()):
45 return match.group(1) 45 return match.group(1)
46 return '&lt;%s&gt;'%match.group(2) 46 return '&lt;%s&gt;'%match.group(2)
47 47
48 class Client: 48 class Client:
49 ''' Instantiate to handle one CGI request. 49 '''Instantiate to handle one CGI request.
50 50
51 See inner_main for request processing. 51 See inner_main for request processing.
52 52
53 Client attributes at instantiation: 53 Client attributes at instantiation:
54 "path" is the PATH_INFO inside the instance (with no leading '/') 54
55 "base" is the base URL for the instance 55 - "path" is the PATH_INFO inside the instance (with no leading '/')
56 "form" is the cgi form, an instance of FieldStorage from the standard 56 - "base" is the base URL for the instance
57 cgi module 57 - "form" is the cgi form, an instance of FieldStorage from the standard
58 "additional_headers" is a dictionary of additional HTTP headers that 58 cgi module
59 should be sent to the client 59 - "additional_headers" is a dictionary of additional HTTP headers that
60 "response_code" is the HTTP response code to send to the client 60 should be sent to the client
61 - "response_code" is the HTTP response code to send to the client
61 62
62 During the processing of a request, the following attributes are used: 63 During the processing of a request, the following attributes are used:
63 "error_message" holds a list of error messages 64
64 "ok_message" holds a list of OK messages 65 - "error_message" holds a list of error messages
65 "session" is the current user session id 66 - "ok_message" holds a list of OK messages
66 "user" is the current user's name 67 - "session" is the current user session id
67 "userid" is the current user's id 68 - "user" is the current user's name
68 "template" is the current :template context 69 - "userid" is the current user's id
69 "classname" is the current class context name 70 - "template" is the current :template context
70 "nodeid" is the current context item id 71 - "classname" is the current class context name
72 - "nodeid" is the current context item id
71 73
72 User Identification: 74 User Identification:
73 If the user has no login cookie, then they are anonymous and are logged 75 If the user has no login cookie, then they are anonymous and are logged
74 in as that user. This typically gives them all Permissions assigned to the 76 in as that user. This typically gives them all Permissions assigned to the
75 Anonymous Role. 77 Anonymous Role.
76 78
77 Once a user logs in, they are assigned a session. The Client instance 79 Once a user logs in, they are assigned a session. The Client instance
78 keeps the nodeid of the session as the "session" attribute. 80 keeps the nodeid of the session as the "session" attribute.
79
80 81
81 Special form variables: 82 Special form variables:
82 Note that in various places throughout this code, special form 83 Note that in various places throughout this code, special form
83 variables of the form :<name> are used. The colon (":") part may 84 variables of the form :<name> are used. The colon (":") part may
84 actually be one of either ":" or "@". 85 actually be one of either ":" or "@".
144 finally: 145 finally:
145 if hasattr(self, 'db'): 146 if hasattr(self, 'db'):
146 self.db.close() 147 self.db.close()
147 148
148 def inner_main(self): 149 def inner_main(self):
149 ''' Process a request. 150 '''Process a request.
150 151
151 The most common requests are handled like so: 152 The most common requests are handled like so:
152 1. figure out who we are, defaulting to the "anonymous" user 153
153 see determine_user 154 1. figure out who we are, defaulting to the "anonymous" user
154 2. figure out what the request is for - the context 155 see determine_user
155 see determine_context 156 2. figure out what the request is for - the context
156 3. handle any requested action (item edit, search, ...) 157 see determine_context
157 see handle_action 158 3. handle any requested action (item edit, search, ...)
158 4. render a template, resulting in HTML output 159 see handle_action
159 160 4. render a template, resulting in HTML output
160 In some situations, exceptions occur: 161
161 - HTTP Redirect (generally raised by an action) 162 In some situations, exceptions occur:
162 - SendFile (generally raised by determine_context) 163
163 serve up a FileClass "content" property 164 - HTTP Redirect (generally raised by an action)
164 - SendStaticFile (generally raised by determine_context) 165 - SendFile (generally raised by determine_context)
165 serve up a file from the tracker "html" directory 166 serve up a FileClass "content" property
166 - Unauthorised (generally raised by an action) 167 - SendStaticFile (generally raised by determine_context)
167 the action is cancelled, the request is rendered and an error 168 serve up a file from the tracker "html" directory
168 message is displayed indicating that permission was not 169 - Unauthorised (generally raised by an action)
169 granted for the action to take place 170 the action is cancelled, the request is rendered and an error
170 - templating.Unauthorised (templating action not permitted) 171 message is displayed indicating that permission was not
171 raised by an attempted rendering of a template when the user 172 granted for the action to take place
172 doesn't have permission 173 - templating.Unauthorised (templating action not permitted)
173 - NotFound (raised wherever it needs to be) 174 raised by an attempted rendering of a template when the user
174 percolates up to the CGI interface that called the client 175 doesn't have permission
176 - NotFound (raised wherever it needs to be)
177 percolates up to the CGI interface that called the client
175 ''' 178 '''
176 self.ok_message = [] 179 self.ok_message = []
177 self.error_message = [] 180 self.error_message = []
178 try: 181 try:
179 # figure out the context and desired content template 182 # figure out the context and desired content template
260 if interval > week: 263 if interval > week:
261 otks.destroy(sessid) 264 otks.destroy(sessid)
262 sessions.set('last_clean', last_use=time.time()) 265 sessions.set('last_clean', last_use=time.time())
263 266
264 def determine_user(self): 267 def determine_user(self):
265 '''Determine who the user is. 268 ''' Determine who the user is
266 ''' 269 '''
267 # open the database as admin 270 # determine the uid to use
268 self.opendb('admin') 271 self.opendb('admin')
269 272
270 # clean age sessions 273 # make sure we have the session Class
271 self.clean_sessions() 274 self.clean_sessions()
272
273 # make sure we have the session Class
274 sessions = self.db.sessions 275 sessions = self.db.sessions
275 276
276 # look up the user session cookie 277 # first up, try the REMOTE_USER var (from HTTP Basic Auth handled
277 cookie = Cookie.SimpleCookie(self.env.get('HTTP_COOKIE', '')) 278 # by a front-end HTTP server)
279 try:
280 user = os.getenv('REMOTE_USER')
281 except KeyError:
282 pass
283
284 # look up the user session cookie (may override the REMOTE_USER)
285 cookie = Cookie.Cookie(self.env.get('HTTP_COOKIE', ''))
278 user = 'anonymous' 286 user = 'anonymous'
279
280 # bump the "revision" of the cookie since the format changed
281 if (cookie.has_key(self.cookie_name) and 287 if (cookie.has_key(self.cookie_name) and
282 cookie[self.cookie_name].value != 'deleted'): 288 cookie[self.cookie_name].value != 'deleted'):
283 289
284 # get the session key from the cookie 290 # get the session key from the cookie
285 self.session = cookie[self.cookie_name].value 291 self.session = cookie[self.cookie_name].value
288 # update the lifetime datestamp 294 # update the lifetime datestamp
289 sessions.set(self.session, last_use=time.time()) 295 sessions.set(self.session, last_use=time.time())
290 sessions.commit() 296 sessions.commit()
291 user = sessions.get(self.session, 'user') 297 user = sessions.get(self.session, 'user')
292 except KeyError: 298 except KeyError:
293 user = 'anonymous' 299 # not valid, ignore id
300 pass
294 301
295 # sanity check on the user still being valid, getting the userid 302 # sanity check on the user still being valid, getting the userid
296 # at the same time 303 # at the same time
297 try: 304 try:
298 self.userid = self.db.user.lookup(user) 305 self.userid = self.db.user.lookup(user)
307 314
308 # reopen the database as the correct user 315 # reopen the database as the correct user
309 self.opendb(self.user) 316 self.opendb(self.user)
310 317
311 def determine_context(self, dre=re.compile(r'([^\d]+)(\d+)')): 318 def determine_context(self, dre=re.compile(r'([^\d]+)(\d+)')):
312 """ Determine the context of this page from the URL: 319 """Determine the context of this page from the URL:
313 320
314 The URL path after the instance identifier is examined. The path 321 The URL path after the instance identifier is examined. The path
315 is generally only one entry long. 322 is generally only one entry long.
316 323
317 - if there is no path, then we are in the "home" context. 324 - if there is no path, then we are in the "home" context.
318 * if the path is "_file", then the additional path entry 325 - if the path is "_file", then the additional path entry
319 specifies the filename of a static file we're to serve up 326 specifies the filename of a static file we're to serve up
320 from the instance "html" directory. Raises a SendStaticFile 327 from the instance "html" directory. Raises a SendStaticFile
321 exception. 328 exception.(*)
322 - if there is something in the path (eg "issue"), it identifies 329 - if there is something in the path (eg "issue"), it identifies
323 the tracker class we're to display. 330 the tracker class we're to display.
324 - if the path is an item designator (eg "issue123"), then we're 331 - if the path is an item designator (eg "issue123"), then we're
325 to display a specific item. 332 to display a specific item.
326 * if the path starts with an item designator and is longer than 333 - if the path starts with an item designator and is longer than
327 one entry, then we're assumed to be handling an item of a 334 one entry, then we're assumed to be handling an item of a
328 FileClass, and the extra path information gives the filename 335 FileClass, and the extra path information gives the filename
329 that the client is going to label the download with (ie 336 that the client is going to label the download with (ie
330 "file123/image.png" is nicer to download than "file123"). This 337 "file123/image.png" is nicer to download than "file123"). This
331 raises a SendFile exception. 338 raises a SendFile exception.(*)
332 339
333 Both of the "*" types of contexts stop before we bother to 340 Both of the "*" types of contexts stop before we bother to
334 determine the template we're going to use. That's because they 341 determine the template we're going to use. That's because they
335 don't actually use templates. 342 don't actually use templates.
336 343
337 The template used is specified by the :template CGI variable, 344 The template used is specified by the :template CGI variable,
338 which defaults to: 345 which defaults to:
339 346
340 only classname suplied: "index" 347 - only classname suplied: "index"
341 full item designator supplied: "item" 348 - full item designator supplied: "item"
342 349
343 We set: 350 We set:
351
344 self.classname - the class to display, can be None 352 self.classname - the class to display, can be None
353
345 self.template - the template to render the current context with 354 self.template - the template to render the current context with
355
346 self.nodeid - the nodeid of the class we're displaying 356 self.nodeid - the nodeid of the class we're displaying
347 """ 357 """
348 # default the optional variables 358 # default the optional variables
349 self.classname = None 359 self.classname = None
350 self.nodeid = None 360 self.nodeid = None

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