Commit b95fa1f3 authored by Jim Fulton's avatar Jim Fulton

Added:

  - Regex, regex input types,
  - New rule for inheriting allow groups,
  - Support for index_html attribute,
  - Support for relative base refs
  - Added URL as magic variable
  - Added error checking of typed fields
parent 7d9b9dc5
...@@ -86,6 +86,10 @@ Published objects ...@@ -86,6 +86,10 @@ Published objects
fails, then the object publisher raises a '"Not Found"' exception. If fails, then the object publisher raises a '"Not Found"' exception. If
either of the accesses suceeds, then, of course, processing continues. either of the accesses suceeds, then, of course, processing continues.
If the final object encountered when traversing the URL has an
'index_html' attribute, the object traversal will continue to this
attribute. This is useful for providing default methods for objects.
In some cases, a parent object may hold special attributed for a In some cases, a parent object may hold special attributed for a
subobject. This may be the case either when a sub-object cannot have subobject. This may be the case either when a sub-object cannot have
the special attribute or when it is convenience for the parent the special attribute or when it is convenience for the parent
...@@ -104,21 +108,23 @@ Published objects ...@@ -104,21 +108,23 @@ Published objects
Access Control Access Control
Access to an object (and it's sub-objects) may be further Access to an object (and it's sub-objects) may be further restricted
restricted by specifying an object attribute named: by specifying an object attribute named '__allow_groups__'. If set,
'__allow_groups__'. If set, then this attribute should this attribute should contain a collection of authorization groups.
contain a dictionary or sequence. Each of the The '__allow_groups__' attribute may be a mapping object, in which
items in the attribute must be dictionaries that use names case it is a collection of named groups. Alternatively, the
as keys (i.e. sets of names). The values in these dictionaries '__allow_groups__' attribute may be a sequence, in which case it is
may contain passwords for authenticating each of the names. a collection of named groups. Each group must be a dictionary that
Alternatively, passwords may be provided in separate "realm" use names as keys (i.e. sets of names). The values in these
objects. If no realm is provided, then basic authentication dictionaries may contain passwords for authenticating each of the
will be used and the object publisher will attempt to names. Alternatively, passwords may be provided in separate "realm"
authenticate the access to the object using one of the supplied objects. If no realm is provided, then basic authentication will be
name and password pairs. The basic authentication realm name used and the object publisher will attempt to authenticate the
used is 'module_name.server_name', where 'module_name' is the access to the object using one of the supplied name and password
name of the module containing the published objects, and pairs. The basic authentication realm name used is
server_name is the name of the web server. 'module_name.server_name', where 'module_name' is the name of the
module containing the published objects, and server_name is the name
of the web server.
The module used to publish an object may contain it's own The module used to publish an object may contain it's own
'__allow_groups__' attribute, thereby limiting access to all of the '__allow_groups__' attribute, thereby limiting access to all of the
...@@ -128,7 +134,27 @@ Access Control ...@@ -128,7 +134,27 @@ Access Control
attributes, then the '__allow_groups__' attribute from the last attributes, then the '__allow_groups__' attribute from the last
object in the path that has this attribute will be used. The object in the path that has this attribute will be used. The
'__allow_groups__' attribute for a subobject overides '__allow_groups__' attribute for a subobject overides
'__allow_groups__' attributes for containing objects. '__allow_groups__' attributes for containing objects, however, if
named groups are used, group data from containing objects may be
inherited by contained objects. If a published object uses named
groups, then for each named group in the published object, group
data from groups with the same name in container objects will be
inherited from container objects if:
- The contained object uses named groups,
- There is no object that is a sub-object of the container
object, is a container of the published object, and that has
unnamed groups.
If the name of a group is the python object, 'None', then data from
named groups in container objects will be inherited even if the
groupd don't appear in the inheriting object, subject to the
restrictions above.
When group data are inherited, then inherited data is appended to
the existing data. When groups contain names and passwords,
individual user names may have multiple passwords if they appear in
multiple groups.
Note that an object may have an '__allow_groups__' attribute that is Note that an object may have an '__allow_groups__' attribute that is
set to None, in which case the object will be public, even if set to None, in which case the object will be public, even if
...@@ -219,8 +245,23 @@ Function, method, and class objects ...@@ -219,8 +245,23 @@ Function, method, and class objects
If field names in form data are of the form: name:type, then an If field names in form data are of the form: name:type, then an
attempt will be to convert data from from strings to the indicated attempt will be to convert data from from strings to the indicated
type. The data types currently supported are: float, int, long, type. The data types currently supported are:
string, and date. For example, if the name of a field in an input
float -- Python floating point numbers
int -- Python integers
long -- Python long integers
string -- python strings
regex -- Python case-sensitive regular expressions
Regex -- Python case-insensitive regular expressions
date -- Date-time values
For example, if the name of a field in an input
form is age:int, then the field value will be passed in argument, form is age:int, then the field value will be passed in argument,
age, and an attempt will be made to convert the argument value to age, and an attempt will be made to convert the argument value to
an integer. This conversion also works with file upload, so using an integer. This conversion also works with file upload, so using
...@@ -236,11 +277,13 @@ Published objects that are not functions, methods, or classes ...@@ -236,11 +277,13 @@ Published objects that are not functions, methods, or classes
Return types Return types
A published object, or the returned value of a called published A published object, or the returned value of a called published
object can be of any Python type. The returned value will be object can be of any Python type. If the returned value has an
converted to a string and examined to see if it appears to be an 'asHTML' method, then this method will be called to convert the
HTML document. If it appears to be an HTML document, then the object to HTML, otherwise the returned value will be converted to a
response content-type will be set to 'text/html'. Otherwise the string and examined to see if it appears to be an HTML document. If
content-type will be set to 'text/plain'. it appears to be an HTML document, then the response content-type
will be set to 'text/html'. Otherwise the content-type will be set
to 'text/plain'.
A special case is when the returned object is a two-element tuple. A special case is when the returned object is a two-element tuple.
If the return object is a two-element tuple, then the first element If the return object is a two-element tuple, then the first element
...@@ -255,6 +298,18 @@ Return types ...@@ -255,6 +298,18 @@ Return types
be set "No Content", and no body will be returned. On some be set "No Content", and no body will be returned. On some
browsers, this will cause the displayed document to be unchanged. browsers, this will cause the displayed document to be unchanged.
Base References
If result of a request is HTML text and the text does not define a
'base' tag in the 'head' portion of the HTML, then a base reference
will be inserted that is the location of the directory in which the
published module was published, such as a cgi-directory. If the
HTML text contains a base reference that begins with a slash, then
the server URL will be prepended to the reference. If the base
reference is a relative reference beginning with a dot, then an
absolute reference will be constructed from the effective URL used
to access the published object and from the relative reference.
Providing On-Line help Providing On-Line help
On-line help is provided for published objects, both explicitly and On-line help is provided for published objects, both explicitly and
...@@ -316,7 +371,7 @@ Redirection ...@@ -316,7 +371,7 @@ Redirection
The default object The default object
If no object is specified in a URI, then the publisher will try to If no object is specified in a URI, then the publisher will try to
publish the object 'index_html', if it exists, otherwise the module publish the object 'index_html', if it exists, otherwise the module's
doc string will be published. doc string will be published.
Examples Examples
...@@ -462,7 +517,7 @@ Publishing a module using the ILU Requestor (future) ...@@ -462,7 +517,7 @@ Publishing a module using the ILU Requestor (future)
o Configure the web server to call module_name@server_name with o Configure the web server to call module_name@server_name with
the requestor. the requestor.
$Id: Publish.py,v 1.14 1996/08/05 11:33:54 jfulton Exp $""" $Id: Publish.py,v 1.15 1996/08/07 19:37:54 jfulton Exp $"""
#' #'
# Copyright # Copyright
# #
...@@ -515,6 +570,16 @@ $Id: Publish.py,v 1.14 1996/08/05 11:33:54 jfulton Exp $""" ...@@ -515,6 +570,16 @@ $Id: Publish.py,v 1.14 1996/08/05 11:33:54 jfulton Exp $"""
# (540) 371-6909 # (540) 371-6909
# #
# $Log: Publish.py,v $ # $Log: Publish.py,v $
# Revision 1.15 1996/08/07 19:37:54 jfulton
# Added:
#
# - Regex, regex input types,
# - New rule for inheriting allow groups,
# - Support for index_html attribute,
# - Support for relative base refs
# - Added URL as magic variable
# - Added error checking of typed fields
#
# Revision 1.14 1996/08/05 11:33:54 jfulton # Revision 1.14 1996/08/05 11:33:54 jfulton
# Added first cut at group composition. # Added first cut at group composition.
# #
...@@ -576,7 +641,7 @@ $Id: Publish.py,v 1.14 1996/08/05 11:33:54 jfulton Exp $""" ...@@ -576,7 +641,7 @@ $Id: Publish.py,v 1.14 1996/08/05 11:33:54 jfulton Exp $"""
# #
# #
# #
__version__='$Revision: 1.14 $'[11:-2] __version__='$Revision: 1.15 $'[11:-2]
def main(): def main():
...@@ -659,7 +724,7 @@ class ModulePublisher: ...@@ -659,7 +724,7 @@ class ModulePublisher:
if module_name[-4:]=='.cgi': module_name=module_name[:-4] if module_name[-4:]=='.cgi': module_name=module_name[:-4]
self.module_name=module_name self.module_name=module_name
response=self.response response=self.response
response.setBase(self.base) response.setBase(self.base,'')
default_realm_name="%s.%s" % (self.module_name,self.request.SERVER_NAME) default_realm_name="%s.%s" % (self.module_name,self.request.SERVER_NAME)
realm_name=default_realm_name realm_name=default_realm_name
...@@ -678,16 +743,23 @@ class ModulePublisher: ...@@ -678,16 +743,23 @@ class ModulePublisher:
self.module=theModule self.module=theModule
# Try to get realm from module # Try to get realm from module
try: realm=theModule.__realm__ try: realm=theModule.__realm__
except: realm=None except: realm=None
# Do authorization check, if need be: # Get initial group data:
try: groups=theModule.__allow_groups__ default_inherited_groups={None:None}
inherited_groups=default_inherited_groups
try:
groups=theModule.__allow_groups__
inherited_groups=allow_group_composition(
inherited_groups,groups,default_inherited_groups)
except: groups=None except: groups=None
# Get a nice clean path list: # Get a nice clean path list:
path=(string.strip(self.env('PATH_INFO')) or '/')[1:] path=(string.strip(self.env('PATH_INFO')))
if path[:1]=='/': path=path[1:]
path=string.splitfields(path,'/') path=string.splitfields(path,'/')
while path and not path[0]: path = path[1:] while path and not path[0]: path = path[1:]
...@@ -712,23 +784,26 @@ class ModulePublisher: ...@@ -712,23 +784,26 @@ class ModulePublisher:
except: pass except: pass
if not path: path = ['help'] if not path: path = ['help']
URL=self.base
parents=[] parents=[]
while path: while path:
entry_name,path=path[0], path[1:] entry_name,path=path[0], path[1:]
URL="%s/%s" % (URL,entry_name)
default_realm_name="%s.%s" % (entry_name,default_realm_name) default_realm_name="%s.%s" % (entry_name,default_realm_name)
if entry_name: if entry_name:
try: try:
subobject=getattr(object,entry_name) subobject=getattr(object,entry_name)
try: try:
g=subobject.__allow_groups__ groups=subobject.__allow_groups__
groups=allow_group_composition( inherited_groups=allow_group_composition(
subobject.__allow_groups__, inherited_groups,groups,default_inherited_groups)
groups)
except: except:
try: try:
groups=allow_group_composition( groups=getattr(object,
getattr(object, entry_name+'__allow_groups__'), entry_name+'__allow_groups__')
groups) inherited_groups=allow_group_composition(
inherited_groups,groups,
default_inherited_groups)
except: pass except: pass
try: doc=subobject.__doc__ try: doc=subobject.__doc__
except: except:
...@@ -746,14 +821,16 @@ class ModulePublisher: ...@@ -746,14 +821,16 @@ class ModulePublisher:
try: try:
subobject=object[entry_name] subobject=object[entry_name]
try: try:
groups=allow_group_composition( groups=subobject.__allow_groups__
subobject.__allow_groups__, inherited_groups=allow_group_composition(
groups) inherited_groups,groups,
default_inherited_groups)
except: except:
try: try:
groups=allow_group_composition( groups=object[entry_name+'__allow_groups__']
object[entry_name+'__allow_groups__'], inherited_groups=allow_group_composition(
groups) inherited_groups,groups,
default_inherited_groups)
except: pass except: pass
try: doc=subobject.__doc__ try: doc=subobject.__doc__
except: except:
...@@ -794,8 +871,13 @@ class ModulePublisher: ...@@ -794,8 +871,13 @@ class ModulePublisher:
parents.append(object) parents.append(object)
object=subobject object=subobject
# Check for index_html:
if not path and hasattr(object,'index_html'):
path=['index_html']
# Do authorization checks # Do authorization checks
if groups is not None: if groups is not None:
groups=allow_group_composition(groups,inherited_groups)
if not groups: self.forbiddenError() if not groups: self.forbiddenError()
try: try:
if realm.name is None: if realm.name is None:
...@@ -839,19 +921,14 @@ class ModulePublisher: ...@@ -839,19 +921,14 @@ class ModulePublisher:
except: except:
return response.setBody(object) return response.setBody(object)
#elif type(object_as_function) is types.FunctionType:
# defaults=object_as_function.func_defaults
# argument_names=object_as_function.func_code.co_varnames[
# :object_as_function.func_code.co_argcount]
#else:
# return response.setBody(object)
query=self.request query=self.request
query['RESPONSE']=response query['RESPONSE']=response
query['URL']=URL
if parents: if parents:
parents.reverse() parents.reverse()
query['self']=parents[0] query['self']=parents[0]
query['PARENTS']=parents query['PARENTS']=parents
response.setBase(self.base,URL)
args=[] args=[]
nrequired=len(argument_names) - (len(defaults or [])) nrequired=len(argument_names) - (len(defaults or []))
...@@ -860,9 +937,12 @@ class ModulePublisher: ...@@ -860,9 +937,12 @@ class ModulePublisher:
try: try:
v=query[argument_name] v=query[argument_name]
args.append(v) args.append(v)
except: except (KeyError,AttributeError,IndexError):
if name_index < nrequired: if name_index < nrequired:
self.badRequestError(argument_name) self.badRequestError(argument_name)
except:
raise 'BadRequest', ('<strong>Invalid entry for %s </strong>'
% argument_name)
if args: result=apply(object,tuple(args)) if args: result=apply(object,tuple(args))
...@@ -954,6 +1034,16 @@ def field2long(v): ...@@ -954,6 +1034,16 @@ def field2long(v):
except: v=str(v) except: v=str(v)
return string.atol(v) return string.atol(v)
def field2Regex(v):
try: v=v.read()
except: v=str(v)
if v: return regex.compile(v)
def field2regex(v):
try: v=v.read()
except: v=str(v)
if v: return regex.compile(v,regex.casefold)
def field2date(v): def field2date(v):
from DateTime import DateTime from DateTime import DateTime
try: v=v.read() try: v=v.read()
...@@ -1037,6 +1127,8 @@ class Request: ...@@ -1037,6 +1127,8 @@ class Request:
'string': field2string, 'string': field2string,
'date': field2date, 'date': field2date,
'list': field2list, 'list': field2list,
'regex': field2regex,
'Regex': field2Regex,
} }
__http_colon=regex.compile("\(:\|\(%3[aA]\)\)") __http_colon=regex.compile("\(:\|\(%3[aA]\)\)")
...@@ -1081,12 +1173,11 @@ class Request: ...@@ -1081,12 +1173,11 @@ class Request:
tf[k[:l]]=form[k],k[l+len(group(1)):] tf[k[:l]]=form[k],k[l+len(group(1)):]
v,t=tf[key] v,t=tf[key]
try: try: converter=self.__type_converters[t]
converter=self.__type_converters[t]
except: pass except: pass
v=flatten_field(v,converter) v=flatten_field(v,converter)
return v return v
except: pass except (KeyError,AttributeError,IndexError): pass
if not self.__dict__.has_key('cookies'): if not self.__dict__.has_key('cookies'):
cookies=self.cookies={} cookies=self.cookies={}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment