[Zope] Unexplained Slowdown
Brian Lloyd
brian@digicool.com
Wed, 17 Jan 2001 16:10:59 -0500
This is a multi-part message in MIME format.
------=_NextPart_000_0021_01C080A0.1469E1E0
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
This is due to an ill-fated attempt to make 'domain-based
authentication' less expensive (but that backfired to some
extent because it can make non domain-based auth more
expensive, especially if reverse DNS lookups are slow for
you). This is fixed for 2.3 b2 - in the meantime you should be
able to copy the attached User.py to fix the problem for
now.
Brian Lloyd brian@digicool.com
Software Engineer 540.371.6909
Digital Creations http://www.digicool.com
> -----Original Message-----
> From: zope-admin@zope.org [mailto:zope-admin@zope.org]On Behalf Of Ian
> Thomas
> Sent: Wednesday, January 17, 2001 3:41 PM
> To: zope@zope.org
> Subject: [Zope] Unexplained Slowdown
>
>
> When I upgraded to Zope 2.2.5 access to my Zope over the web slowed down
> dramatically and I haven't been able to figure out why. I am interested
> in ideas I can try to track it down the problem or at least
> quantify/benchmark the slowness.
>
> Some background.
> 1) I am a computer teacher teaching Zope to my second year web
> development students who have already learned HTML and been introduced
> to JavaScript.
> 2) The server is a my test machine that my students and I use for
> learning.
> 3) The "production" server still running 2.2.4 continues to preform
> normally.
> 4) The machine is Celeron 500 mhz 64 meg ram running Windows 2000 Server
> 5) I run Medusa server that comes with Zope to server the web pages.
> 6) There is no gateway set up on this machine so that it is only visible
> from within the school.
>
> Notes.
> 1) It is much faster if I access via localhost on the server.
>
> What I have tried.
> 1) Installing 2.2.4 on another port. It ran just as slow.
> 2) Today I removed the 2.2.4 installation and installed 2.3beta, again
> it seems just as slow.
>
> What Next?
> The next thing I was thinking was to serve the webpages from IIS using
> PCGI.
> I am interested in other ideas.
>
> I. Thomas
>
>
>
>
>
> _______________________________________________
> Zope maillist - Zope@zope.org
> http://lists.zope.org/mailman/listinfo/zope
> ** No cross posts or HTML encoding! **
> (Related lists -
> http://lists.zope.org/mailman/listinfo/zope-announce
> http://lists.zope.org/mailman/listinfo/zope-dev )
>
>
------=_NextPart_000_0021_01C080A0.1469E1E0
Content-Type: text/plain;
name="User.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="User.py"
#########################################################################=
#####=0A=
# =0A=
# Zope Public License (ZPL) Version 1.0=0A=
# -------------------------------------=0A=
# =0A=
# Copyright (c) Digital Creations. All rights reserved.=0A=
# =0A=
# This license has been certified as Open Source(tm).=0A=
# =0A=
# Redistribution and use in source and binary forms, with or without=0A=
# modification, are permitted provided that the following conditions are=0A=
# met:=0A=
# =0A=
# 1. Redistributions in source code must retain the above copyright=0A=
# notice, this list of conditions, and the following disclaimer.=0A=
# =0A=
# 2. Redistributions in binary form must reproduce the above copyright=0A=
# notice, this list of conditions, and the following disclaimer in=0A=
# the documentation and/or other materials provided with the=0A=
# distribution.=0A=
# =0A=
# 3. Digital Creations requests that attribution be given to Zope=0A=
# in any manner possible. Zope includes a "Powered by Zope"=0A=
# button that is installed by default. While it is not a license=0A=
# violation to remove this button, it is requested that the=0A=
# attribution remain. A significant investment has been put=0A=
# into Zope, and this effort will continue if the Zope community=0A=
# continues to grow. This is one way to assure that growth.=0A=
# =0A=
# 4. All advertising materials and documentation mentioning=0A=
# features derived from or use of this software must display=0A=
# the following acknowledgement:=0A=
# =0A=
# "This product includes software developed by Digital Creations=0A=
# for use in the Z Object Publishing Environment=0A=
# (http://www.zope.org/)."=0A=
# =0A=
# In the event that the product being advertised includes an=0A=
# intact Zope distribution (with copyright and license included)=0A=
# then this clause is waived.=0A=
# =0A=
# 5. Names associated with Zope or Digital Creations must not be used to=0A=
# endorse or promote products derived from this software without=0A=
# prior written permission from Digital Creations.=0A=
# =0A=
# 6. Modified redistributions of any form whatsoever must retain=0A=
# the following acknowledgment:=0A=
# =0A=
# "This product includes software developed by Digital Creations=0A=
# for use in the Z Object Publishing Environment=0A=
# (http://www.zope.org/)."=0A=
# =0A=
# Intact (re-)distributions of any official Zope release do not=0A=
# require an external acknowledgement.=0A=
# =0A=
# 7. Modifications are encouraged but must be packaged separately as=0A=
# patches to official Zope releases. Distributions that do not=0A=
# clearly separate the patches from the original work must be clearly=0A=
# labeled as unofficial distributions. Modifications which do not=0A=
# carry the name Zope may be packaged in any form, as long as they=0A=
# conform to all of the clauses above.=0A=
# =0A=
# =0A=
# Disclaimer=0A=
# =0A=
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY=0A=
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE=0A=
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR=0A=
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS=0A=
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,=0A=
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT=0A=
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF=0A=
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND=0A=
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,=0A=
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT=0A=
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF=0A=
# SUCH DAMAGE.=0A=
# =0A=
# =0A=
# This software consists of contributions made by Digital Creations and=0A=
# many individuals on behalf of Digital Creations. Specific=0A=
# attributions are listed in the accompanying credits file.=0A=
# =0A=
#########################################################################=
#####=0A=
"""Access control package"""=0A=
=0A=
__version__=3D'$Revision: 1.110.4.10 $'[11:-2]=0A=
=0A=
import Globals, socket, regex, SpecialUsers=0A=
from Globals import HTMLFile, MessageDialog, Persistent, =
PersistentMapping=0A=
from string import join,strip,split,lower=0A=
from App.Management import Navigation, Tabs=0A=
from Acquisition import Implicit=0A=
from OFS.SimpleItem import Item=0A=
from base64 import decodestring=0A=
from App.ImageFile import ImageFile=0A=
from Role import RoleManager=0A=
from string import split, join, upper=0A=
from PermissionRole import _what_not_even_god_should_do, =
rolesForPermissionOn=0A=
from AuthEncoding import pw_validate=0A=
=0A=
ListType=3Dtype([])=0A=
NotImplemented=3D'NotImplemented'=0A=
=0A=
_marker=3D[]=0A=
=0A=
class BasicUser(Implicit):=0A=
"""Base class for all User objects"""=0A=
=0A=
# ----------------------------=0A=
# Public User object interface=0A=
# ----------------------------=0A=
=0A=
# Maybe allow access to unprotected attributes. Note that this is=0A=
# temporary to avoid exposing information but without breaking=0A=
# everyone's current code. In the future the security will be=0A=
# clamped down and permission-protected here. Because there are a=0A=
# fair number of user object types out there, this method denies=0A=
# access to names that are private parts of the standard User=0A=
# interface or implementation only. The other approach (only=0A=
# allowing access to public names in the User interface) would=0A=
# probably break a lot of other User implementations with extended=0A=
# functionality that we cant anticipate from the base scaffolding.=0A=
def __allow_access_to_unprotected_subobjects__(self, name, =
value=3DNone):=0A=
deny_names=3D('name', '__', 'roles', 'domains', '_getPassword',=0A=
'authenticate', '_shared_roles')=0A=
if name in deny_names:=0A=
return 0=0A=
return 1=0A=
=0A=
def __init__(self,name,password,roles,domains):=0A=
raise NotImplemented=0A=
=0A=
def getUserName(self):=0A=
"""Return the username of a user"""=0A=
raise NotImplemented=0A=
=0A=
def getId(self):=0A=
"""Get the ID of the user. The ID can be used, at least from=0A=
Python, to get the user from the user's=0A=
UserDatabase"""=0A=
return self.getUserName() =0A=
=0A=
def _getPassword(self):=0A=
"""Return the password of the user."""=0A=
raise NotImplemented=0A=
=0A=
def getRoles(self):=0A=
"""Return the list of roles assigned to a user."""=0A=
raise NotImplemented=0A=
=0A=
def getRolesInContext(self, object):=0A=
"""Return the list of roles assigned to the user,=0A=
including local roles assigned in context of=0A=
the passed in object."""=0A=
name=3Dself.getUserName()=0A=
roles=3Dself.getRoles()=0A=
local=3D{}=0A=
object=3Dgetattr(object, 'aq_inner', object)=0A=
while 1:=0A=
local_roles =3D getattr(object, '__ac_local_roles__', None)=0A=
if local_roles:=0A=
if callable(local_roles):=0A=
local_roles=3Dlocal_roles()=0A=
dict=3Dlocal_roles or {}=0A=
for r in dict.get(name, []):=0A=
local[r]=3D1=0A=
inner =3D getattr(object, 'aq_inner', object)=0A=
parent =3D getattr(inner, 'aq_parent', None)=0A=
if parent is not None:=0A=
object =3D parent=0A=
continue=0A=
if hasattr(object, 'im_self'):=0A=
object=3Dobject.im_self=0A=
object=3Dgetattr(object, 'aq_inner', object)=0A=
continue=0A=
break=0A=
roles=3Dlist(roles) + local.keys()=0A=
return roles=0A=
=0A=
def getDomains(self):=0A=
"""Return the list of domain restrictions for a user"""=0A=
raise NotImplemented=0A=
=0A=
# ------------------------------=0A=
# Internal User object interface=0A=
# ------------------------------=0A=
=0A=
def authenticate(self, password, request):=0A=
passwrd=3Dself._getPassword()=0A=
result =3D pw_validate(passwrd, password) =0A=
domains=3Dself.getDomains()=0A=
if domains:=0A=
return result and domainSpecMatch(domains, request)=0A=
return result=0A=
=0A=
=0A=
def _shared_roles(self, parent):=0A=
r=3D[]=0A=
while 1:=0A=
if hasattr(parent,'__roles__'):=0A=
roles=3Dparent.__roles__=0A=
if roles is None: return 'Anonymous',=0A=
if 'Shared' in roles:=0A=
roles=3Dlist(roles)=0A=
roles.remove('Shared')=0A=
r=3Dr+roles=0A=
else:=0A=
try: return r+list(roles)=0A=
except: return r=0A=
if hasattr(parent, 'aq_parent'):=0A=
while hasattr(parent.aq_self,'aq_self'):=0A=
parent=3Dparent.aq_self=0A=
parent=3Dparent.aq_parent=0A=
else: return r=0A=
=0A=
def allowed(self, parent, roles=3DNone):=0A=
"""Check whether the user has access to parent, assuming that=0A=
parent.__roles__ is the given roles."""=0A=
if roles is None or 'Anonymous' in roles:=0A=
return 1=0A=
usr_roles=3Dself.getRolesInContext(parent)=0A=
for role in roles:=0A=
if role in usr_roles:=0A=
if (hasattr(self,'aq_parent') and=0A=
hasattr(self.aq_parent,'aq_parent')):=0A=
if parent is None: return 1=0A=
if (not hasattr(parent, 'aq_inContextOf') and=0A=
hasattr(parent, 'im_self')):=0A=
# This is a method, grab it's self.=0A=
parent=3Dparent.im_self=0A=
if not =
parent.aq_inContextOf(self.aq_parent.aq_parent,1):=0A=
if 'Shared' in roles:=0A=
# Damn, old role setting. Waaa=0A=
roles=3Dself._shared_roles(parent)=0A=
if 'Anonymous' in roles: return 1=0A=
return None=0A=
return 1=0A=
=0A=
if 'Shared' in roles:=0A=
# Damn, old role setting. Waaa=0A=
roles=3Dself._shared_roles(parent)=0A=
if roles is None or 'Anonymous' in roles: return 1=0A=
while 'Shared' in roles: roles.remove('Shared')=0A=
return self.allowed(parent,roles)=0A=
=0A=
return None=0A=
=0A=
hasRole=3Dallowed=0A=
domains=3D[]=0A=
=0A=
def has_role(self, roles, object=3DNone):=0A=
"""Check to see if a user has a given role or roles."""=0A=
if type(roles)=3D=3Dtype('s'):=0A=
roles=3D[roles]=0A=
if object is not None:=0A=
user_roles =3D self.getRolesInContext(object)=0A=
else:=0A=
# Global roles only...=0A=
user_roles=3Dself.getRoles()=0A=
for role in roles:=0A=
if role in user_roles:=0A=
return 1=0A=
return 0=0A=
=0A=
def has_permission(self, permission, object):=0A=
"""Check to see if a user has a given permission on an object."""=0A=
roles=3DrolesForPermissionOn(permission, object)=0A=
return self.has_role(roles, object)=0A=
=0A=
def __len__(self): return 1=0A=
def __str__(self): return self.getUserName()=0A=
__repr__=3D__str__=0A=
=0A=
=0A=
class SimpleUser(BasicUser):=0A=
"""A very simple user implementation=0A=
=0A=
that doesn't make a database commitment"""=0A=
=0A=
def __init__(self,name,password,roles,domains):=0A=
self.name =3Dname=0A=
self.__ =3Dpassword=0A=
self.roles =3Droles=0A=
self.domains=3Ddomains=0A=
=0A=
def getUserName(self):=0A=
"""Return the username of a user"""=0A=
return self.name=0A=
=0A=
def _getPassword(self):=0A=
"""Return the password of the user."""=0A=
return self.__=0A=
=0A=
def getRoles(self):=0A=
"""Return the list of roles assigned to a user."""=0A=
return tuple(self.roles)=0A=
=0A=
def getDomains(self):=0A=
"""Return the list of domain restrictions for a user"""=0A=
return tuple(self.domains)=0A=
=0A=
class SpecialUser(SimpleUser):=0A=
"""Class for special users, like super and nobody"""=0A=
def getId(self): pass=0A=
=0A=
class User(SimpleUser, Persistent):=0A=
"""Standard User object"""=0A=
=0A=
class Super(SpecialUser):=0A=
"""Super user=0A=
"""=0A=
def allowed(self,parent,roles=3DNone):=0A=
return roles is not _what_not_even_god_should_do=0A=
=0A=
hasRole=3Dallowed=0A=
=0A=
def has_role(self, roles, object=3DNone): return 1=0A=
=0A=
def has_permission(self, permission, object): return 1=0A=
=0A=
_remote_user_mode=3D0=0A=
try:=0A=
f=3Dopen('%s/access' % INSTANCE_HOME, 'r')=0A=
except IOError:=0A=
raise 'InstallError', (=0A=
'No access file found at %s - see INSTALL.txt' % INSTANCE_HOME=0A=
)=0A=
try:=0A=
data=3Dsplit(strip(f.readline()),':')=0A=
f.close()=0A=
_remote_user_mode=3Dnot data[1]=0A=
try: ds=3Dsplit(data[2], ' ')=0A=
except: ds=3D[]=0A=
super=3DSuper(data[0],data[1],('manage',), ds)=0A=
del data=0A=
except:=0A=
raise 'InstallError', 'Invalid format for access file - see =
INSTALL.txt'=0A=
=0A=
=0A=
nobody=3DSpecialUser('Anonymous User','',('Anonymous',), [])=0A=
system=3DSuper('System Processes','',('manage',), [])=0A=
=0A=
# stuff these in a handier place for importing=0A=
SpecialUsers.nobody=3Dnobody=0A=
SpecialUsers.system=3Dsystem=0A=
SpecialUsers.super=3Dsuper=0A=
=0A=
=0A=
class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, =
RoleManager,=0A=
Item):=0A=
"""Base class for UserFolder-like objects"""=0A=
=0A=
meta_type=3D'User Folder'=0A=
id =3D'acl_users'=0A=
title =3D'User Folder'=0A=
=0A=
isPrincipiaFolderish=3D1=0A=
isAUserFolder=3D1=0A=
=0A=
manage_options=3D(=0A=
(=0A=
{'label':'Contents', 'action':'manage_main',=0A=
'help':('OFSP','User-Folder_Contents.stx')},=0A=
)=0A=
+RoleManager.manage_options=0A=
+Item.manage_options=0A=
)=0A=
=0A=
__ac_permissions__=3D(=0A=
('Manage users',=0A=
('manage_users','getUserNames','getUser','getUsers',=0A=
)=0A=
),=0A=
)=0A=
=0A=
=0A=
# ----------------------------------=0A=
# Public UserFolder object interface=0A=
# ----------------------------------=0A=
=0A=
def getUserNames(self):=0A=
"""Return a list of usernames"""=0A=
raise NotImplemented=0A=
=0A=
def getUsers(self):=0A=
"""Return a list of user objects"""=0A=
raise NotImplemented=0A=
=0A=
def getUser(self, name):=0A=
"""Return the named user object or None"""=0A=
raise NotImplemented=0A=
=0A=
def getUserById(self, id, default=3D_marker):=0A=
"""Return the user corresponding to the given id.=0A=
"""=0A=
try: return self.getUser(id)=0A=
except:=0A=
if default is _marker: raise=0A=
return default=0A=
=0A=
def _doAddUser(self, name, password, roles, domains):=0A=
"""Create a new user"""=0A=
raise NotImplemented=0A=
=0A=
def _doChangeUser(self, name, password, roles, domains):=0A=
"""Modify an existing user"""=0A=
raise NotImplemented=0A=
=0A=
def _doDelUsers(self, names):=0A=
"""Delete one or more users"""=0A=
raise NotImplemented=0A=
=0A=
=0A=
# -----------------------------------=0A=
# Private UserFolder object interface=0A=
# -----------------------------------=0A=
=0A=
=0A=
_remote_user_mode=3D_remote_user_mode=0A=
_super=3Dsuper=0A=
_nobody=3Dnobody=0A=
=0A=
def validate(self,request,auth=3D'',roles=3DNone):=0A=
=0A=
if roles is _what_not_even_god_should_do:=0A=
request.response.notFoundError()=0A=
=0A=
parents=3Drequest.get('PARENTS', [])=0A=
if not parents:=0A=
parent=3Dself.aq_parent=0A=
else: parent=3Dparents[0]=0A=
=0A=
if not auth:=0A=
for ob in self.getUsers():=0A=
domains=3Dob.getDomains()=0A=
if domains:=0A=
if ob.authenticate('', request):=0A=
if ob.allowed(parent, roles):=0A=
ob=3Dob.__of__(self)=0A=
return ob=0A=
nobody=3Dself._nobody=0A=
if self._isTop() and nobody.allowed(parent, roles):=0A=
ob=3Dnobody.__of__(self)=0A=
return ob=0A=
return None=0A=
=0A=
# Only do basic authentication=0A=
if lower(auth[:6])!=3D'basic ':=0A=
return None=0A=
name,password=3Dtuple(split(decodestring(split(auth)[-1]), ':', =
1))=0A=
=0A=
# Check for superuser=0A=
super=3Dself._super=0A=
if self._isTop() and (name=3D=3Dsuper.getUserName()) and \=0A=
super.authenticate(password, request):=0A=
return super=0A=
=0A=
# Try to get user=0A=
user=3Dself.getUser(name)=0A=
if user is None:=0A=
=0A=
# If the user was not found and we are the top level user=0A=
# database and the Anonymous user is allowed to access the=0A=
# requested object, return the Anonymous user.=0A=
if self._isTop() and self._nobody.allowed(parent, roles):=0A=
user=3Dself._nobody.__of__(self)=0A=
return user=0A=
=0A=
# Otherwise, return None which will defer to higher level =
user=0A=
# databases or cause an unauthorized to be raised in the=0A=
# publisher layer.=0A=
return None=0A=
=0A=
# Try to authenticate the user=0A=
if not user.authenticate(password, request):=0A=
=0A=
# If no user was authenticated and we are the top level user=0A=
# database and the Anonymous user is allowed to access the=0A=
# requested object, return the Anonymous user.=0A=
if self._isTop() and self._nobody.allowed(parent, roles):=0A=
user=3Dself._nobody.__of__(self)=0A=
return user=0A=
=0A=
# Otherwise, return None which will defer to higher level =
user=0A=
# databases or cause an unauthorized to be raised in the=0A=
# publisher layer.=0A=
return None=0A=
=0A=
# We need the user to be able to acquire!=0A=
user=3Duser.__of__(self)=0A=
=0A=
# Try to authorize user=0A=
if user.allowed(parent, roles):=0A=
return user=0A=
=0A=
return None=0A=
=0A=
=0A=
if _remote_user_mode:=0A=
=0A=
def validate(self,request,auth=3D'',roles=3DNone):=0A=
parent=3Drequest['PARENTS'][0]=0A=
e=3Drequest.environ=0A=
if e.has_key('REMOTE_USER'):=0A=
name=3De['REMOTE_USER']=0A=
else:=0A=
for ob in self.getUsers():=0A=
domains=3Dob.getDomains()=0A=
if domains:=0A=
if ob.authenticate('', request):=0A=
if ob.allowed(parent, roles):=0A=
ob=3Dob.__of__(self)=0A=
return ob=0A=
nobody=3Dself._nobody=0A=
if self._isTop() and nobody.allowed(parent, roles):=0A=
ob=3Dnobody.__of__(self)=0A=
return ob=0A=
return None=0A=
=0A=
# Check for superuser=0A=
super=3Dself._super=0A=
if self._isTop() and (name=3D=3Dsuper.getUserName()):=0A=
return super=0A=
=0A=
# Try to get user=0A=
user=3Dself.getUser(name)=0A=
if user is None:=0A=
return None=0A=
=0A=
# We need the user to be able to acquire!=0A=
user=3Duser.__of__(self)=0A=
=0A=
# Try to authorize user=0A=
if user.allowed(parent, roles):=0A=
return user=0A=
return None=0A=
=0A=
=0A=
def _isTop(self):=0A=
try: return =
self.aq_parent.aq_base.isTopLevelPrincipiaApplicationObject=0A=
except: return 0=0A=
=0A=
def __len__(self):=0A=
return 1=0A=
=0A=
_mainUser=3DHTMLFile('mainUser', globals())=0A=
_add_User=3DHTMLFile('addUser', globals(),=0A=
remote_user_mode__=3D_remote_user_mode)=0A=
_editUser=3DHTMLFile('editUser', globals(),=0A=
remote_user_mode__=3D_remote_user_mode)=0A=
manage=3Dmanage_main=3D_mainUser=0A=
=0A=
def domainSpecValidate(self, spec):=0A=
for ob in spec:=0A=
sz=3Dlen(ob)=0A=
if not ((addr_match(ob) =3D=3D sz) or (host_match(ob) =3D=3D =
sz)):=0A=
return 0=0A=
return 1=0A=
=0A=
def =
_addUser(self,name,password,confirm,roles,domains,REQUEST=3DNone):=0A=
if not name:=0A=
return MessageDialog(=0A=
title =3D'Illegal value', =0A=
message=3D'A username must be specified',=0A=
action =3D'manage_main')=0A=
if not password or not confirm:=0A=
if not domains:=0A=
return MessageDialog(=0A=
title =3D'Illegal value', =0A=
message=3D'Password and confirmation must be =
specified',=0A=
action =3D'manage_main')=0A=
if self.getUser(name) or (name=3D=3Dself._super.getUserName()):=0A=
return MessageDialog(=0A=
title =3D'Illegal value', =0A=
message=3D'A user with the specified name already =
exists',=0A=
action =3D'manage_main')=0A=
if (password or confirm) and (password !=3D confirm):=0A=
return MessageDialog(=0A=
title =3D'Illegal value', =0A=
message=3D'Password and confirmation do not match',=0A=
action =3D'manage_main')=0A=
=0A=
if not roles: roles=3D[]=0A=
if not domains: domains=3D[]=0A=
=0A=
if domains and not self.domainSpecValidate(domains):=0A=
return MessageDialog(=0A=
title =3D'Illegal value', =0A=
message=3D'Illegal domain specification',=0A=
action =3D'manage_main')=0A=
self._doAddUser(name, password, roles, domains) =0A=
if REQUEST: return self._mainUser(self, REQUEST)=0A=
=0A=
=0A=
def =
_changeUser(self,name,password,confirm,roles,domains,REQUEST=3DNone):=0A=
if password =3D=3D 'password' and confirm =3D=3D 'confirm':=0A=
# Protocol for editUser.dtml to indicate unchanged password=0A=
password =3D confirm =3D None=0A=
if not name:=0A=
return MessageDialog(=0A=
title =3D'Illegal value', =0A=
message=3D'A username must be specified',=0A=
action =3D'manage_main')=0A=
if password =3D=3D confirm =3D=3D '':=0A=
if not domains:=0A=
return MessageDialog(=0A=
title =3D'Illegal value', =0A=
message=3D'Password and confirmation must be =
specified',=0A=
action =3D'manage_main')=0A=
if not self.getUser(name):=0A=
return MessageDialog(=0A=
title =3D'Illegal value', =0A=
message=3D'Unknown user',=0A=
action =3D'manage_main')=0A=
if (password or confirm) and (password !=3D confirm):=0A=
return MessageDialog(=0A=
title =3D'Illegal value', =0A=
message=3D'Password and confirmation do not match',=0A=
action =3D'manage_main')=0A=
=0A=
if not roles: roles=3D[]=0A=
if not domains: domains=3D[]=0A=
=0A=
if domains and not self.domainSpecValidate(domains):=0A=
return MessageDialog(=0A=
title =3D'Illegal value', =0A=
message=3D'Illegal domain specification',=0A=
action =3D'manage_main')=0A=
self._doChangeUser(name, password, roles, domains)=0A=
if REQUEST: return self._mainUser(self, REQUEST)=0A=
=0A=
def _delUsers(self,names,REQUEST=3DNone):=0A=
if not names:=0A=
return MessageDialog(=0A=
title =3D'Illegal value', =0A=
message=3D'No users specified',=0A=
action =3D'manage_main')=0A=
self._doDelUsers(names)=0A=
if REQUEST: return self._mainUser(self, REQUEST)=0A=
=0A=
def manage_users(self,submit=3DNone,REQUEST=3DNone,RESPONSE=3DNone):=0A=
""" """=0A=
if submit=3D=3D'Add...':=0A=
return self._add_User(self, REQUEST)=0A=
=0A=
if submit=3D=3D'Edit':=0A=
try: user=3Dself.getUser(reqattr(REQUEST, 'name'))=0A=
except: return MessageDialog(=0A=
title =3D'Illegal value',=0A=
message=3D'The specified user does not exist',=0A=
action =3D'manage_main')=0A=
return =
self._editUser(self,REQUEST,user=3Duser,password=3Duser.__)=0A=
=0A=
if submit=3D=3D'Add':=0A=
name =3Dreqattr(REQUEST, 'name')=0A=
password=3Dreqattr(REQUEST, 'password')=0A=
confirm =3Dreqattr(REQUEST, 'confirm')=0A=
roles =3Dreqattr(REQUEST, 'roles')=0A=
domains =3Dreqattr(REQUEST, 'domains')=0A=
return =
self._addUser(name,password,confirm,roles,domains,REQUEST)=0A=
=0A=
if submit=3D=3D'Change':=0A=
name =3Dreqattr(REQUEST, 'name')=0A=
password=3Dreqattr(REQUEST, 'password')=0A=
confirm =3Dreqattr(REQUEST, 'confirm')=0A=
roles =3Dreqattr(REQUEST, 'roles')=0A=
domains =3Dreqattr(REQUEST, 'domains')=0A=
return self._changeUser(name,password,confirm,roles,=0A=
domains,REQUEST)=0A=
=0A=
if submit=3D=3D'Delete':=0A=
names=3Dreqattr(REQUEST, 'names')=0A=
return self._delUsers(names,REQUEST)=0A=
=0A=
return self._mainUser(self, REQUEST)=0A=
=0A=
def user_names(self):=0A=
return self.getUserNames()=0A=
=0A=
def manage_beforeDelete(self, item, container):=0A=
if item is self:=0A=
try: del container.__allow_groups__=0A=
except: pass=0A=
=0A=
def manage_afterAdd(self, item, container):=0A=
if item is self:=0A=
if hasattr(self, 'aq_base'): self=3Dself.aq_base=0A=
container.__allow_groups__=3Dself=0A=
=0A=
def __creatable_by_super__(self): return 1=0A=
=0A=
def _setId(self, id):=0A=
if id !=3D self.id:=0A=
raise Globals.MessageDialog(=0A=
title=3D'Invalid Id',=0A=
message=3D'Cannot change the id of a UserFolder',=0A=
action =3D'./manage_main',)=0A=
=0A=
=0A=
=0A=
=0A=
=0A=
=0A=
=0A=
=0A=
=0A=
class UserFolder(BasicUserFolder):=0A=
"""Standard UserFolder object=0A=
=0A=
A UserFolder holds User objects which contain information=0A=
about users including name, password domain, and roles.=0A=
UserFolders function chiefly to control access by authenticating=0A=
users and binding them to a collection of roles."""=0A=
=0A=
meta_type=3D'User Folder'=0A=
id =3D'acl_users'=0A=
title =3D'User Folder'=0A=
icon =3D'p_/UserFolder'=0A=
=0A=
def __init__(self):=0A=
self.data=3DPersistentMapping()=0A=
=0A=
def getUserNames(self):=0A=
"""Return a list of usernames"""=0A=
names=3Dself.data.keys()=0A=
names.sort()=0A=
return names=0A=
=0A=
def getUsers(self):=0A=
"""Return a list of user objects"""=0A=
data=3Dself.data=0A=
names=3Ddata.keys()=0A=
names.sort()=0A=
users=3D[]=0A=
f=3Dusers.append=0A=
for n in names:=0A=
f(data[n])=0A=
return users=0A=
=0A=
def getUser(self, name):=0A=
"""Return the named user object or None"""=0A=
return self.data.get(name, None)=0A=
=0A=
def _doAddUser(self, name, password, roles, domains):=0A=
"""Create a new user"""=0A=
self.data[name]=3DUser(name,password,roles,domains)=0A=
=0A=
def _doChangeUser(self, name, password, roles, domains):=0A=
user=3Dself.data[name]=0A=
if password is not None:=0A=
user.__=3Dpassword=0A=
user.roles=3Droles=0A=
user.domains=3Ddomains=0A=
=0A=
def _doDelUsers(self, names):=0A=
for name in names:=0A=
del self.data[name]=0A=
=0A=
=0A=
Globals.default__class_init__(UserFolder)=0A=
=0A=
=0A=
=0A=
=0A=
=0A=
=0A=
def manage_addUserFolder(self,dtself=3DNone,REQUEST=3DNone,**ignored):=0A=
""" """=0A=
f=3DUserFolder()=0A=
self=3Dself.this()=0A=
try: self._setObject('acl_users', f)=0A=
except: return MessageDialog(=0A=
title =3D'Item Exists',=0A=
message=3D'This object already contains a User =
Folder',=0A=
action =3D'%s/manage_main' % REQUEST['URL1'])=0A=
self.__allow_groups__=3Df=0A=
if REQUEST is not None:=0A=
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')=0A=
=0A=
=0A=
def rolejoin(roles, other):=0A=
dict=3D{}=0A=
for role in roles:=0A=
dict[role]=3D1=0A=
for role in other:=0A=
dict[role]=3D1=0A=
roles=3Ddict.keys()=0A=
roles.sort()=0A=
return roles=0A=
=0A=
addr_match=3Dregex.compile('[0-9\.\*]*').match #TS=0A=
host_match=3Dregex.compile('[-A-Za-z0-9\.\*]*').match #TS=0A=
=0A=
=0A=
def domainSpecMatch(spec, request):=0A=
host=3D''=0A=
addr=3D''=0A=
=0A=
if request.has_key('REMOTE_HOST'):=0A=
host=3Drequest['REMOTE_HOST']=0A=
=0A=
if request.has_key('REMOTE_ADDR'):=0A=
addr=3Drequest['REMOTE_ADDR']=0A=
=0A=
if not host and not addr:=0A=
return 0=0A=
=0A=
if not host:=0A=
try: host=3Dsocket.gethostbyaddr(addr)[0]=0A=
except: pass=0A=
if not addr:=0A=
try: addr=3Dsocket.gethostbyname(host)=0A=
except: pass=0A=
=0A=
_host=3Dsplit(host, '.')=0A=
_addr=3Dsplit(addr, '.')=0A=
_hlen=3Dlen(_host)=0A=
_alen=3Dlen(_addr)=0A=
=0A=
for ob in spec:=0A=
sz=3Dlen(ob)=0A=
_ob=3Dsplit(ob, '.')=0A=
_sz=3Dlen(_ob)=0A=
=0A=
if addr_match(ob)=3D=3Dsz:=0A=
fail=3D0=0A=
for i in range(_sz):=0A=
a=3D_addr[i]=0A=
o=3D_ob[i]=0A=
if (o !=3D a) and (o !=3D '*'):=0A=
fail=3D1=0A=
break=0A=
if fail:=0A=
continue=0A=
return 1=0A=
=0A=
if host_match(ob)=3D=3Dsz:=0A=
if _hlen < _sz:=0A=
continue=0A=
elif _hlen > _sz:=0A=
_item=3D_host[-_sz:]=0A=
else:=0A=
_item=3D_host=0A=
fail=3D0=0A=
for i in range(_sz):=0A=
h=3D_item[i]=0A=
o=3D_ob[i]=0A=
if (o !=3D h) and (o !=3D '*'):=0A=
fail=3D1=0A=
break=0A=
if fail:=0A=
continue=0A=
return 1=0A=
return 0=0A=
=0A=
=0A=
def absattr(attr):=0A=
if callable(attr): return attr()=0A=
return attr=0A=
=0A=
def reqattr(request, attr):=0A=
try: return request[attr]=0A=
except: return None=0A=
------=_NextPart_000_0021_01C080A0.1469E1E0--