[gen] Get rid of some Zope-specific security aspects. This is a preparatory work to extend the Appy authentication system to perform ldap authentication as well.
This commit is contained in:
parent
04852360fa
commit
5223af2a62
|
@ -246,14 +246,14 @@ class Field:
|
|||
elif rp and isinstance(rp, basestring):
|
||||
self.readPermission = rp
|
||||
else:
|
||||
self.readPermission = 'View'
|
||||
self.readPermission = 'read'
|
||||
wp = self.specificWritePermission
|
||||
if wp and not isinstance(wp, basestring):
|
||||
self.writePermission = '%s: Write %s %s' % (appName, prefix, name)
|
||||
elif wp and isinstance(wp, basestring):
|
||||
self.writePermission = wp
|
||||
else:
|
||||
self.writePermission = 'Modify portal content'
|
||||
self.writePermission = 'write'
|
||||
if (self.type == 'Ref') and not self.isBack:
|
||||
# We must initialise the corresponding back reference
|
||||
self.back.klass = klass
|
||||
|
|
|
@ -140,7 +140,7 @@ class Ref(Field):
|
|||
folder=zobj.getCreateFolder();
|
||||
tiedClassName=ztool.getPortalType(field.klass);
|
||||
canWrite=not field.isBack and zobj.allows(field.writePermission);
|
||||
showPlusIcon=zobj.mayAddReference(field.name);
|
||||
showPlusIcon=field.mayAdd(zobj);
|
||||
atMostOneRef=(field.multiplicity[1] == 1) and \
|
||||
(len(zobjects)<=1);
|
||||
addConfirmMsg=field.addConfirm and \
|
||||
|
@ -592,12 +592,8 @@ class Ref(Field):
|
|||
# May the user edit this Ref field?
|
||||
if not obj.allows(self.writePermission):
|
||||
return gutils.No('no_write_perm')
|
||||
# Have the user the correct add permission?
|
||||
tool = obj.getTool()
|
||||
addPermission = '%s: Add %s' % (tool.getAppName(),
|
||||
tool.getPortalType(self.klass))
|
||||
folder = obj.getCreateFolder()
|
||||
if not tool.getUser().has_permission(addPermission, folder):
|
||||
# May the user create instances of the referred class?
|
||||
if not obj.getTool().userMayCreate(self.klass):
|
||||
return gutils.No('no_add_perm')
|
||||
return True
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ class UiSearch:
|
|||
pxView = Px('''
|
||||
<div class="portletSearch">
|
||||
<a href=":'%s?className=%s&search=%s' % \
|
||||
(queryUrl, rootClass, search.name)"
|
||||
(queryUrl, className, search.name)"
|
||||
class=":search.name == currentSearch and 'portletCurrent' or ''"
|
||||
title=":search.translatedDescr">:search.translated</a>
|
||||
</div>''')
|
||||
|
|
|
@ -59,12 +59,6 @@ class Import:
|
|||
self.sort = sort
|
||||
|
||||
# Workflow-specific types and default workflows --------------------------------
|
||||
appyToZopePermissions = {
|
||||
'read': ('View', 'Access contents information'),
|
||||
'write': 'Modify portal content',
|
||||
'delete': 'Delete objects',
|
||||
}
|
||||
|
||||
class Role:
|
||||
'''Represents a role.'''
|
||||
zopeRoles = ('Manager', 'Owner', 'Anonymous', 'Authenticated')
|
||||
|
@ -108,7 +102,7 @@ class State:
|
|||
name of a role, this method returns self.usedRoles[role] if it
|
||||
exists, or creates a Role instance, puts it in self.usedRoles and
|
||||
returns it else. If it is a Role instance, the method stores it in
|
||||
self.usedRoles if it not in it yet and returns it.'''
|
||||
self.usedRoles if it is not in it yet and returns it.'''
|
||||
if isinstance(role, basestring):
|
||||
if role in self.usedRoles:
|
||||
return self.usedRoles[role]
|
||||
|
@ -135,61 +129,6 @@ class State:
|
|||
|
||||
def getUsedRoles(self): return self.usedRoles.values()
|
||||
|
||||
def getPermissions(self):
|
||||
'''If you get the permissions mapping through self.permissions, dict
|
||||
values may be of different types (a list of roles, a single role or
|
||||
None). Iy you call this method, you will always get a list which
|
||||
may be empty.'''
|
||||
res = {}
|
||||
for permission, roleValue in self.permissions.iteritems():
|
||||
if roleValue == None:
|
||||
res[permission] = []
|
||||
elif isinstance(roleValue, basestring):
|
||||
res[permission] = [roleValue]
|
||||
else:
|
||||
res[permission] = roleValue
|
||||
return res
|
||||
|
||||
def updatePermission(self, obj, zopePermission, roleNames):
|
||||
'''Updates, on p_obj, list of p_roleNames which are granted a given
|
||||
p_zopePermission. This method returns True if the list has been
|
||||
effectively updated.'''
|
||||
attr = Permission.getZopeAttrName(zopePermission)
|
||||
if not hasattr(obj.aq_base, attr) or \
|
||||
(getattr(obj.aq_base, attr) != roleNames):
|
||||
setattr(obj, attr, roleNames)
|
||||
return True
|
||||
return False
|
||||
|
||||
def updatePermissions(self, wf, obj):
|
||||
'''Zope requires permission-to-roles mappings to be stored as attributes
|
||||
on the object itself. This method does this job, duplicating the info
|
||||
from this state definition on p_obj. p_res is True if at least one
|
||||
change has been effectively performed.'''
|
||||
res = False
|
||||
for permission, roles in self.getPermissions().iteritems():
|
||||
roleNames = tuple([role.name for role in roles])
|
||||
# Compute Zope permission(s) related to this permission.
|
||||
if appyToZopePermissions.has_key(permission):
|
||||
# It is a standard permission (r, w, d)
|
||||
zopePerm = appyToZopePermissions[permission]
|
||||
elif isinstance(permission, basestring):
|
||||
# It is a user-defined permission
|
||||
zopePerm = permission
|
||||
else:
|
||||
# It is a Permission instance
|
||||
appName = obj.getProductConfig().PROJECTNAME
|
||||
zopePerm = permission.getName(wf, appName)
|
||||
# zopePerm contains a single permission or a tuple of permissions
|
||||
if isinstance(zopePerm, basestring):
|
||||
changed = self.updatePermission(obj, zopePerm, roleNames)
|
||||
res = res or changed
|
||||
else:
|
||||
for zPerm in zopePerm:
|
||||
changed = self.updatePermission(obj, zPerm, roleNames)
|
||||
res = res or changed
|
||||
return res
|
||||
|
||||
class Transition:
|
||||
def __init__(self, states, condition=True, action=None, notify=None,
|
||||
show=True, confirm=False):
|
||||
|
@ -345,8 +284,6 @@ class Transition:
|
|||
if not doHistory: comment = '_invisible_'
|
||||
obj.addHistoryEvent(action, review_state=targetStateName,
|
||||
comments=comment)
|
||||
# Update permissions-to-roles attributes
|
||||
targetState.updatePermissions(wf, obj)
|
||||
# Reindex the object if required. Not only security-related indexes
|
||||
# (Allowed, State) need to be updated here.
|
||||
if not obj.isTemporary(): obj.reindex()
|
||||
|
@ -384,8 +321,7 @@ class Permission:
|
|||
self.fieldDescriptor = fieldDescriptor
|
||||
|
||||
def getName(self, wf, appName):
|
||||
'''Returns the name of the Zope permission that corresponds to this
|
||||
permission.'''
|
||||
'''Returns the name of this permission.'''
|
||||
className, fieldName = self.fieldDescriptor.rsplit('.', 1)
|
||||
if className.find('.') == -1:
|
||||
# The related class resides in the same module as the workflow
|
||||
|
@ -398,16 +334,6 @@ class Permission:
|
|||
else: access = 'Write'
|
||||
return '%s: %s %s %s' % (appName, access, fullClassName, fieldName)
|
||||
|
||||
@staticmethod
|
||||
def getZopeAttrName(zopePermission):
|
||||
'''Gets the name of the attribute where Zope stores, on every object,
|
||||
the tuple of roles who are granted a given p_zopePermission.'''
|
||||
res = ''
|
||||
for c in zopePermission:
|
||||
if c in Permission.allowedChars: res += c
|
||||
else: res += '_'
|
||||
return '_%s_Permission' % res
|
||||
|
||||
class ReadPermission(Permission): pass
|
||||
class WritePermission(Permission): pass
|
||||
|
||||
|
@ -475,7 +401,7 @@ class Config:
|
|||
languageSelector = False
|
||||
# People having one of these roles will be able to create instances
|
||||
# of classes defined in your application.
|
||||
defaultCreators = ['Manager', 'Owner']
|
||||
defaultCreators = ['Manager']
|
||||
# Number of translations for every page on a Translation object
|
||||
translationsPerPage = 30
|
||||
# Language that will be used as a basis for translating to other
|
||||
|
|
|
@ -557,12 +557,6 @@ class ZopeGenerator(Generator):
|
|||
if theImport not in imports:
|
||||
imports.append(theImport)
|
||||
repls['imports'] = '\n'.join(imports)
|
||||
# Compute list of add permissions
|
||||
addPermissions = ''
|
||||
for classDescr in classesAll:
|
||||
addPermissions += ' "%s":"%s: Add %s",\n' % (classDescr.name,
|
||||
self.applicationName, classDescr.name)
|
||||
repls['addPermissions'] = addPermissions
|
||||
# Compute root classes
|
||||
repls['rootClasses'] = ','.join(["'%s'" % c.name \
|
||||
for c in classesButTool if c.isRoot()])
|
||||
|
|
|
@ -63,7 +63,6 @@ class ZopeInstaller:
|
|||
self.productName = config.PROJECTNAME
|
||||
self.languages = config.appConfig.languages
|
||||
self.logger = config.logger
|
||||
self.addContentPermissions = config.ADD_CONTENT_PERMISSIONS
|
||||
|
||||
def installUi(self):
|
||||
'''Installs the user interface.'''
|
||||
|
@ -157,14 +156,9 @@ class ZopeInstaller:
|
|||
catalog.reindexIndex('SearchableText', self.app.REQUEST)
|
||||
self.logger.info('Done.')
|
||||
|
||||
def getAddPermission(self, className):
|
||||
'''What is the name of the permission allowing to create instances of
|
||||
class whose name is p_className?'''
|
||||
return self.productName + ': Add ' + className
|
||||
|
||||
def installBaseObjects(self):
|
||||
'''Creates the tool and the root data folder if they do not exist.'''
|
||||
# Create or update the base folder for storing data
|
||||
'''Creates the tool and the base data folder if they do not exist.'''
|
||||
# Create the tool.
|
||||
zopeContent = self.app.objectIds()
|
||||
from OFS.Folder import manage_addFolder
|
||||
|
||||
|
@ -172,29 +166,8 @@ class ZopeInstaller:
|
|||
toolName = '%sTool' % self.productName
|
||||
gutils.createObject(self.app, 'config', toolName, self.productName,
|
||||
wf=False, noSecurity=True)
|
||||
|
||||
if 'data' not in zopeContent:
|
||||
manage_addFolder(self.app, 'data')
|
||||
data = self.app.data
|
||||
tool = self.app.config
|
||||
# Manager has been granted Add permissions for all root classes.
|
||||
# This may not be desired, so remove this.
|
||||
for className in self.config.rootClasses:
|
||||
permission = self.getAddPermission(className)
|
||||
data.manage_permission(permission, (), acquire=0)
|
||||
# All roles defined as creators should be able to create the
|
||||
# corresponding root classes in this folder.
|
||||
i = -1
|
||||
for klass in self.config.appClasses:
|
||||
i += 1
|
||||
if not klass.__dict__.has_key('root') or \
|
||||
not klass.__dict__['root']:
|
||||
continue # It is not a root class
|
||||
className = self.config.appClassNames[i]
|
||||
wrapperClass = tool.getAppyClass(className, wrapper=True)
|
||||
creators = wrapperClass.getCreators(self.config)
|
||||
permission = self.getAddPermission(className)
|
||||
gutils.updateRolesForPermission(permission,tuple(creators),data)
|
||||
# Create the base data folder.
|
||||
if 'data' not in zopeContent: manage_addFolder(self.app, 'data')
|
||||
|
||||
# Remove some default objects created by Zope but not useful to Appy
|
||||
for name in ('standard_html_footer', 'standard_html_header',\
|
||||
|
@ -206,14 +179,13 @@ class ZopeInstaller:
|
|||
inner objects (users, groups, translations, documents).'''
|
||||
tool = self.app.config
|
||||
tool.createOrUpdate(True, None)
|
||||
tool.refreshSecurity()
|
||||
appyTool = tool.appy()
|
||||
appyTool.log('Appy version is "%s".' % appy.version.short)
|
||||
|
||||
# Create the default users if they do not exist.
|
||||
for login, roles in self.defaultUsers.iteritems():
|
||||
if not appyTool.count('User', noSecurity=True, login=login):
|
||||
appyTool.create('users', noSecurity=True, login=login,
|
||||
appyTool.create('users', noSecurity=True, id=login, login=login,
|
||||
password1=login, password2=login,
|
||||
email='%s@appyframework.org'%login, roles=roles)
|
||||
appyTool.log('User "%s" created.' % login)
|
||||
|
@ -332,8 +304,7 @@ class ZopeInstaller:
|
|||
wrapper = klass.wrapperClass
|
||||
exec 'from %s import manage_add%s as ctor' % (module, name)
|
||||
self.zopeContext.registerClass(meta_type=name,
|
||||
constructors = (ctor,),
|
||||
permission = self.addContentPermissions[name])
|
||||
constructors = (ctor,), permission = None)
|
||||
# Create workflow prototypical instances in __instance__ attributes
|
||||
wf = wrapper.getWorkflow()
|
||||
if not hasattr(wf, '__instance__'): wf.__instance__ = wf()
|
||||
|
|
|
@ -199,7 +199,8 @@ class ToolMixin(BaseMixin):
|
|||
|
||||
def getRootClasses(self):
|
||||
'''Returns the list of root classes for this application.'''
|
||||
return self.getProductConfig().rootClasses
|
||||
cfg = self.getProductConfig()
|
||||
return [self.getAppyClass(k) for k in cfg.rootClasses]
|
||||
|
||||
def _appy_getAllFields(self, className):
|
||||
'''Returns the (translated) names of fields of p_className.'''
|
||||
|
@ -265,8 +266,8 @@ class ToolMixin(BaseMixin):
|
|||
p_className.'''
|
||||
appyClass = self.getAppyClass(className)
|
||||
importParams = self.getCreateMeans(appyClass)['import']
|
||||
onElement = importParams['onElement'].__get__('')
|
||||
sortMethod = importParams['sort']
|
||||
onElement = importParams.onElement.__get__('')
|
||||
sortMethod = importParams.sort
|
||||
if sortMethod: sortMethod = sortMethod.__get__('')
|
||||
elems = []
|
||||
importType = self.getAppyType('importPathFor%s' % className)
|
||||
|
@ -280,7 +281,7 @@ class ToolMixin(BaseMixin):
|
|||
elems.append(elemInfo)
|
||||
if sortMethod:
|
||||
elems = sortMethod(elems)
|
||||
return [importParams['headers'], elems]
|
||||
return [importParams.headers, elems]
|
||||
|
||||
def showPortlet(self, context, layoutType):
|
||||
'''When must the portlet be shown?'''
|
||||
|
@ -489,17 +490,13 @@ class ToolMixin(BaseMixin):
|
|||
if wrapper: return zopeClass.wrapperClass
|
||||
else: return zopeClass.wrapperClass.__bases__[-1]
|
||||
|
||||
def getCreateMeans(self, contentTypeOrAppyClass):
|
||||
'''Gets the different ways objects of p_contentTypeOrAppyClass (which
|
||||
can be a Zope content type or a Appy class) can be created
|
||||
(via a web form, by importing external data, etc). Result is a
|
||||
dict whose keys are strings (ie "form", "import"...) and whose
|
||||
values are additional data about the particular mean.'''
|
||||
pythonClass = contentTypeOrAppyClass
|
||||
if isinstance(contentTypeOrAppyClass, basestring):
|
||||
pythonClass = self.getAppyClass(pythonClass)
|
||||
def getCreateMeans(self, klass):
|
||||
'''Gets the different ways objects of p_klass can be created (via a web
|
||||
form, by importing external data, etc). Result is a dict whose keys
|
||||
are strings (ie "form", "import"...) and whose values are additional
|
||||
data about the particular mean.'''
|
||||
res = {}
|
||||
if not pythonClass.__dict__.has_key('create'):
|
||||
if not klass.__dict__.has_key('create'):
|
||||
res['form'] = None
|
||||
# No additional data for this means, which is the default one.
|
||||
else:
|
||||
|
@ -511,23 +508,28 @@ class ToolMixin(BaseMixin):
|
|||
if isinstance(mean, basestring):
|
||||
res[mean] = None
|
||||
else:
|
||||
res[mean.id] = mean.__dict__
|
||||
res[mean.id] = mean
|
||||
else:
|
||||
res[means.id] = means.__dict__
|
||||
res[means.id] = means
|
||||
return res
|
||||
|
||||
def userMaySearch(self, rootClass):
|
||||
'''This method checks if the currently logged user can trigger searches
|
||||
on a given p_rootClass. This is done by calling method "maySearch"
|
||||
on the class. If no such method exists, we return True.'''
|
||||
# When editign a form, one should avoid annoying the user with this.
|
||||
'''May the logged user search among instances of p_rootClass ?'''
|
||||
# When editing a form, one should avoid annoying the user with this.
|
||||
url = self.REQUEST['ACTUAL_URL']
|
||||
if url.endswith('/edit') or url.endswith('/do'): return
|
||||
pythonClass = self.getAppyClass(rootClass)
|
||||
if 'maySearch' in pythonClass.__dict__:
|
||||
return pythonClass.maySearch(self.appy())
|
||||
if 'maySearch' in rootClass.__dict__:
|
||||
return pythonClass.rootClass(self.appy())
|
||||
return True
|
||||
|
||||
def userMayCreate(self, klass):
|
||||
'''May the logged user create instances of p_klass ?'''
|
||||
allowedRoles = getattr(klass, 'creators', None) or \
|
||||
self.getProductConfig().appConfig.defaultCreators
|
||||
for role in self.getUser().getRoles():
|
||||
if role in allowedRoles:
|
||||
return True
|
||||
|
||||
def onImportObjects(self):
|
||||
'''This method is called when the user wants to create objects from
|
||||
external data.'''
|
||||
|
@ -737,23 +739,22 @@ class ToolMixin(BaseMixin):
|
|||
obj = self.getObject(objectUid)
|
||||
return obj, fieldName
|
||||
|
||||
def getGroupedSearches(self, className):
|
||||
def getGroupedSearches(self, klass):
|
||||
'''Returns an object with 2 attributes:
|
||||
* "searches" stores the searches that are defined for p_className;
|
||||
* "searches" stores the searches that are defined for p_klass;
|
||||
* "default" stores the search defined as the default one.
|
||||
Every item representing a search is a dict containing info about a
|
||||
search or about a group of searches.
|
||||
'''
|
||||
appyClass = self.getAppyClass(className)
|
||||
res = []
|
||||
default = None # Also retrieve the default one here.
|
||||
groups = {} # The already encountered groups
|
||||
page = Page('main') # A dummy page required by class UiGroup
|
||||
# Get the searches statically defined on the class
|
||||
searches = ClassDescriptor.getSearches(appyClass, tool=self.appy())
|
||||
searches = ClassDescriptor.getSearches(klass, tool=self.appy())
|
||||
# Get the dynamically computed searches
|
||||
if hasattr(appyClass, 'getDynamicSearches'):
|
||||
searches += appyClass.getDynamicSearches(self.appy())
|
||||
if hasattr(klass, 'getDynamicSearches'):
|
||||
searches += klass.getDynamicSearches(self.appy())
|
||||
for search in searches:
|
||||
# Create the search descriptor
|
||||
uiSearch = UiSearch(search, className, self)
|
||||
|
@ -792,9 +793,8 @@ class ToolMixin(BaseMixin):
|
|||
if ui: res = UiSearch(res, className, self)
|
||||
return res
|
||||
|
||||
def advancedSearchEnabledFor(self, className):
|
||||
def advancedSearchEnabledFor(self, klass):
|
||||
'''Is advanced search visible for p_klass ?'''
|
||||
klass = self.getAppyClass(className)
|
||||
# By default, advanced search is enabled.
|
||||
if not hasattr(klass, 'searchAdvanced'): return True
|
||||
# Evaluate attribute "show" on this Search instance representing the
|
||||
|
@ -1103,29 +1103,26 @@ class ToolMixin(BaseMixin):
|
|||
login = (rq.__class__.__name__ == 'Object') and 'system' or 'anon'
|
||||
# Get the User object from a query in the catalog.
|
||||
user = tool.search1('User', noSecurity=True, login=login)
|
||||
# It is possible that we find no user here: it happens before users
|
||||
# "anon" and "system" are created, at first Zope startup.
|
||||
if not user: return
|
||||
rq.user = user
|
||||
# Precompute some values or this usser for performance reasons
|
||||
# Precompute some values or this user for performance reasons.
|
||||
rq.userRoles = user.getRoles()
|
||||
rq.userLogins = user.getLogins()
|
||||
rq.zopeUser = user.getZopeUser()
|
||||
return user
|
||||
#from AccessControl import getSecurityManager
|
||||
#user = getSecurityManager().getUser()
|
||||
#if not user:
|
||||
# from AccessControl.User import nobody
|
||||
# return nobody
|
||||
#return user
|
||||
|
||||
def getUserLine(self):
|
||||
'''Returns a info about the currently logged user as a 2-tuple: first
|
||||
elem is the one-line user info as shown on every page; second line is
|
||||
the URL to edit user info.'''
|
||||
user = self.getUser()
|
||||
userRoles = self.appy().request.userRoles
|
||||
info = [user.title]
|
||||
rolesToShow = [r for r in userRoles if r != 'Authenticated']
|
||||
if rolesToShow:
|
||||
showable = [r for r in user.getRoles() if r != 'Authenticated']
|
||||
if showable:
|
||||
info.append(', '.join([self.translate('role_%s' % r) \
|
||||
for r in rolesToShow]))
|
||||
for r in showable]))
|
||||
# Edit URL for the user.
|
||||
url = None
|
||||
if user.o.mayEdit():
|
||||
|
@ -1134,17 +1131,17 @@ class ToolMixin(BaseMixin):
|
|||
|
||||
def getUserName(self, login=None, normalized=False):
|
||||
'''Gets the user name corresponding to p_login (or the currently logged
|
||||
login if None), or the p_login itself if the user does not exist
|
||||
user if None), or the p_login itself if the user does not exist
|
||||
anymore. If p_normalized is True, special chars in the first and last
|
||||
names are normalized.'''
|
||||
tool = self.appy()
|
||||
if not login: login = tool.user.login
|
||||
# Manage the special case of an anonymous user.
|
||||
if login == 'Anonymous User':
|
||||
if login == 'anon':
|
||||
name = self.translate('anonymous')
|
||||
if normalized: name = sutils.normalizeString(name)
|
||||
return name
|
||||
# Manage the case of a "real" user.
|
||||
# Manage the case of any other user.
|
||||
user = tool.search1('User', noSecurity=True, login=login)
|
||||
if not user: return login
|
||||
firstName = user.firstName
|
||||
|
|
|
@ -93,9 +93,6 @@ class BaseMixin:
|
|||
# Manage potential link with an initiator object
|
||||
if created and initiator: initiator.appy().link(initiatorField.name,obj)
|
||||
|
||||
# Manage "add" permissions and reindex the object
|
||||
obj._appy_managePermissions()
|
||||
|
||||
# Call the custom "onEdit" if available
|
||||
msg = None # The message to display to the user. It can be set by onEdit
|
||||
if obj.wrapperClass:
|
||||
|
@ -410,7 +407,7 @@ class BaseMixin:
|
|||
return self.goto(tool.getSiteUrl(), msg)
|
||||
# If the user can't access the object anymore, redirect him to the
|
||||
# main site page.
|
||||
if not obj.allows('View'):
|
||||
if not obj.allows('read'):
|
||||
return self.goto(tool.getSiteUrl(), msg)
|
||||
if (buttonClicked == 'save') or saveConfirmed:
|
||||
obj.say(msg)
|
||||
|
@ -484,7 +481,7 @@ class BaseMixin:
|
|||
corresponding Appy wrapper and returns, as XML, the its result.'''
|
||||
self.REQUEST.RESPONSE.setHeader('Content-Type','text/xml;charset=utf-8')
|
||||
# Check if the user is allowed to consult this object
|
||||
if not self.allows('View'):
|
||||
if not self.allows('read'):
|
||||
return XmlMarshaller().marshall('Unauthorized')
|
||||
if not action:
|
||||
marshaller = XmlMarshaller(rootTag=self.getClass().__name__,
|
||||
|
@ -670,11 +667,6 @@ class BaseMixin:
|
|||
if not allValues: return ''
|
||||
return getattr(allValues[rowIndex], name, '')
|
||||
|
||||
def mayAddReference(self, name):
|
||||
'''May the user add references via Ref field named p_name in
|
||||
p_folder?'''
|
||||
return self.getAppyType(name).mayAdd(self)
|
||||
|
||||
def isDebug(self):
|
||||
'''Are we in debug mode ?'''
|
||||
for arg in sys.argv:
|
||||
|
@ -937,30 +929,6 @@ class BaseMixin:
|
|||
stateName = stateName or self.State()
|
||||
return '%s_%s' % (self.getWorkflow(name=True), stateName)
|
||||
|
||||
def refreshSecurity(self):
|
||||
'''Refresh security info on this object. Returns True if the info has
|
||||
effectively been updated.'''
|
||||
wf = self.getWorkflow()
|
||||
try:
|
||||
# Get the state definition of the object's current state.
|
||||
state = getattr(wf, self.State())
|
||||
except AttributeError:
|
||||
# The workflow information for this object does not correspond to
|
||||
# its current workflow attribution. Add a new fake event
|
||||
# representing passage of this object to the initial state of his
|
||||
# currently attributed workflow.
|
||||
stateName = self.State(name=True, initial=True)
|
||||
self.addHistoryEvent(None, review_state=stateName)
|
||||
state = self.State(name=False, initial=True)
|
||||
self.log('Wrong workflow info for a "%s"; is now in state "%s".' % \
|
||||
(self.meta_type, stateName))
|
||||
# Update permission attributes on the object if required
|
||||
updated = state.updatePermissions(wf, self)
|
||||
if updated:
|
||||
# Reindex the object because security-related info is indexed.
|
||||
self.reindex()
|
||||
return updated
|
||||
|
||||
def applyUserIdChange(self, oldId, newId):
|
||||
'''A user whose ID was p_oldId has now p_newId. If the old ID was
|
||||
mentioned in self's local roles, update it to the new ID. This
|
||||
|
@ -1105,14 +1073,14 @@ class BaseMixin:
|
|||
|
||||
def mayDelete(self):
|
||||
'''May the currently logged user delete this object?'''
|
||||
res = self.allows('Delete objects')
|
||||
res = self.allows('delete')
|
||||
if not res: return
|
||||
# An additional, user-defined condition, may refine the base permission.
|
||||
appyObj = self.appy()
|
||||
if hasattr(appyObj, 'mayDelete'): return appyObj.mayDelete()
|
||||
return True
|
||||
|
||||
def mayEdit(self, permission='Modify portal content'):
|
||||
def mayEdit(self, permission='write'):
|
||||
'''May the currently logged user edit this object? p_perm can be a
|
||||
field-specific permission.'''
|
||||
res = self.allows(permission)
|
||||
|
@ -1195,6 +1163,12 @@ class BaseMixin:
|
|||
self.reindex()
|
||||
return self.goto(self.getUrl(rq['HTTP_REFERER']))
|
||||
|
||||
def getRolesFor(self, permission):
|
||||
'''Gets, according to the workflow, the roles that are currently granted
|
||||
p_permission on this object.'''
|
||||
state = self.State(name=False)
|
||||
return [role.name for role in state.permissions[permission]]
|
||||
|
||||
def appy(self):
|
||||
'''Returns a wrapper object allowing to manipulate p_self the Appy
|
||||
way.'''
|
||||
|
@ -1284,18 +1258,17 @@ class BaseMixin:
|
|||
'''Returns the list of roles and users that are allowed to view this
|
||||
object. This index value will be used within catalog queries for
|
||||
filtering objects the user is allowed to see.'''
|
||||
res = set()
|
||||
# Get, from the workflow, roles having permission 'View'.
|
||||
for role in self.getProductConfig().rolesForPermissionOn('View', self):
|
||||
res.add(role)
|
||||
# Add users having, locally, this role on this object.
|
||||
localRoles = getattr(self, '__ac_local_roles__', None)
|
||||
if not localRoles: return list(res)
|
||||
# Get, from the workflow, roles having permission 'read'.
|
||||
res = self.getRolesFor('read')
|
||||
# Add users or groups having, locally, this role on this object.
|
||||
localRoles = getattr(self.aq_base, '__ac_local_roles__', None)
|
||||
if not localRoles: return res
|
||||
for id, roles in localRoles.iteritems():
|
||||
for role in roles:
|
||||
if role in res:
|
||||
res.add('user:%s' % id)
|
||||
return list(res)
|
||||
usr = 'user:%s' % id
|
||||
if usr not in res: res.append(usr)
|
||||
return res
|
||||
|
||||
def showState(self):
|
||||
'''Must I show self's current state ?'''
|
||||
|
@ -1326,39 +1299,6 @@ class BaseMixin:
|
|||
res.append((elem, self.translate(self.getWorkflowLabel(elem))))
|
||||
return res
|
||||
|
||||
def _appy_managePermissions(self):
|
||||
'''When an object is created or updated, we must update "add"
|
||||
permissions accordingly: if the object is a folder, we must set on
|
||||
it permissions that will allow to create, inside it, objects through
|
||||
Ref fields; if it is not a folder, we must update permissions on its
|
||||
parent folder instead.'''
|
||||
# Determine on which folder we need to set "add" permissions
|
||||
folder = self.getCreateFolder()
|
||||
# On this folder, set "add" permissions for every content type that will
|
||||
# be created through reference fields
|
||||
allCreators = {} # One key for every add permission
|
||||
addPermissions = self.getProductConfig().ADD_CONTENT_PERMISSIONS
|
||||
for appyType in self.getAllAppyTypes():
|
||||
if appyType.type != 'Ref': continue
|
||||
if appyType.isBack or appyType.link: continue
|
||||
# Indeed, no possibility to create objects with such Refs
|
||||
tool = self.getTool()
|
||||
refType = tool.getPortalType(appyType.klass)
|
||||
if refType not in addPermissions: continue
|
||||
# Get roles that may add this content type
|
||||
appyWrapper = tool.getAppyClass(refType, wrapper=True)
|
||||
creators = appyWrapper.getCreators(self.getProductConfig())
|
||||
# Add those creators to the list of creators for this meta_type
|
||||
addPermission = addPermissions[refType]
|
||||
if addPermission in allCreators:
|
||||
allCreators[addPermission] = allCreators[\
|
||||
addPermission].union(creators)
|
||||
else:
|
||||
allCreators[addPermission] = set(creators)
|
||||
# Update the permissions
|
||||
for permission, creators in allCreators.iteritems():
|
||||
updateRolesForPermission(permission, tuple(creators), folder)
|
||||
|
||||
getUrlDefaults = {'page':True, 'nav':True}
|
||||
def getUrl(self, base=None, mode='view', **kwargs):
|
||||
'''Returns an URL for this object.
|
||||
|
|
|
@ -13,7 +13,6 @@ from ZPublisher.HTTPRequest import BaseRequest
|
|||
from OFS.Image import File
|
||||
from ZPublisher.HTTPRequest import FileUpload
|
||||
from AccessControl import getSecurityManager
|
||||
from AccessControl.PermissionRole import rolesForPermissionOn
|
||||
from DateTime import DateTime
|
||||
from Products.ExternalMethod.ExternalMethod import ExternalMethod
|
||||
from Products.Transience.Transience import TransientObjectContainer
|
||||
|
@ -24,8 +23,6 @@ logger = logging.getLogger('<!applicationName!>')
|
|||
# Some global variables --------------------------------------------------------
|
||||
PROJECTNAME = '<!applicationName!>'
|
||||
diskFolder = os.path.dirname(<!applicationName!>.__file__)
|
||||
ADD_CONTENT_PERMISSIONS = {
|
||||
<!addPermissions!>}
|
||||
|
||||
# Applications classes, in various formats
|
||||
rootClasses = [<!rootClasses!>]
|
||||
|
@ -34,7 +31,7 @@ appClassNames = [<!appClassNames!>]
|
|||
allClassNames = [<!allClassNames!>]
|
||||
|
||||
# In the following dict, we store, for every Appy class, the ordered list of
|
||||
# appy types (included inherited ones).
|
||||
# fields.
|
||||
attributes = {<!attributes!>}
|
||||
|
||||
# Application roles
|
||||
|
|
27
gen/utils.py
27
gen/utils.py
|
@ -17,17 +17,11 @@ def createObject(folder, id, className, appName, wf=True, noSecurity=False):
|
|||
user = tool.getUser()
|
||||
if not noSecurity:
|
||||
# Check that the user can create objects of className.
|
||||
userRoles = user.getRoles()
|
||||
allowedRoles=ZopeClass.wrapperClass.getCreators(tool.getProductConfig())
|
||||
allowed = False
|
||||
for role in userRoles:
|
||||
if role in allowedRoles:
|
||||
allowed = True
|
||||
break
|
||||
if not allowed:
|
||||
klass = ZopeClass.wrapperClass.__bases__[-1]
|
||||
if not tool.userMayCreate(klass):
|
||||
from AccessControl import Unauthorized
|
||||
raise Unauthorized("User can't create instances of %s" % \
|
||||
ZopeClass.__name__)
|
||||
klass.__name__)
|
||||
obj = ZopeClass(id)
|
||||
folder._objects = folder._objects + ({'id':id, 'meta_type':className},)
|
||||
folder._setOb(id, obj)
|
||||
|
@ -137,21 +131,6 @@ def getClassName(klass, appName=None):
|
|||
res = klass.__module__.replace('.', '_') + '_' + klass.__name__
|
||||
return res
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
def updateRolesForPermission(permission, roles, obj):
|
||||
'''Adds roles from list p_roles to the list of roles that are granted
|
||||
p_permission on p_obj.'''
|
||||
from AccessControl.Permission import Permission
|
||||
# Find existing roles that were granted p_permission on p_obj
|
||||
existingRoles = ()
|
||||
for p in obj.ac_inherited_permissions(1):
|
||||
name, value = p[:2]
|
||||
if name == permission:
|
||||
perm = Permission(name, value, obj)
|
||||
existingRoles = perm.getRoles()
|
||||
allRoles = set(existingRoles).union(roles)
|
||||
obj.manage_permission(permission, tuple(allRoles), acquire=0)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
def callMethod(obj, method, klass=None, cache=True):
|
||||
'''This function is used to call a p_method on some Appy p_obj. m_method
|
||||
|
|
|
@ -30,55 +30,18 @@ class GroupWrapper(AbstractWrapper):
|
|||
'''Inter-field validation.'''
|
||||
return self._callCustom('validate', new, errors)
|
||||
|
||||
def confirm(self, new):
|
||||
'''Use this method for remembering the previous list of users for this
|
||||
group.'''
|
||||
obj = self.o
|
||||
if hasattr(obj.aq_base, '_oldUsers'): del obj.aq_base._oldUsers
|
||||
obj._oldUsers = self.users
|
||||
|
||||
def addUser(self, user):
|
||||
'''Adds a p_user to this group.'''
|
||||
# Update the Ref field.
|
||||
self.link('users', user)
|
||||
# Update the group-related info on the Zope user.
|
||||
zopeUser = user.getZopeUser()
|
||||
zopeUser.groups[self.login] = self.roles
|
||||
|
||||
def removeUser(self, user):
|
||||
'''Removes a p_user from this group.'''
|
||||
self.unlink('users', user)
|
||||
# Update the group-related info on the Zope user.
|
||||
zopeUser = user.getZopeUser()
|
||||
del zopeUser.groups[self.login]
|
||||
|
||||
def onEdit(self, created):
|
||||
# Create or update, on every Zope user of this group, group-related
|
||||
# information.
|
||||
# 1. Remove reference to this group for users that were removed from it
|
||||
newUsers = self.users
|
||||
# The list of previously existing users does not exist when editing a
|
||||
# group from Python. For updating self.users, it is recommended to use
|
||||
# methods m_addUser and m_removeUser above.
|
||||
oldUsers = getattr(self.o.aq_base, '_oldUsers', ())
|
||||
for user in oldUsers:
|
||||
if user not in newUsers:
|
||||
del user.getZopeUser().groups[self.login]
|
||||
self.log('User "%s" removed from group "%s".' % \
|
||||
(user.login, self.login))
|
||||
# 2. Add reference to this group for users that were added to it
|
||||
for user in newUsers:
|
||||
zopeUser = user.getZopeUser()
|
||||
# We refresh group-related info on the Zope user even if the user
|
||||
# was already in the group.
|
||||
zopeUser.groups[self.login] = self.roles
|
||||
if user not in oldUsers:
|
||||
self.log('User "%s" added to group "%s".' % \
|
||||
(user.login, self.login))
|
||||
if hasattr(self.o.aq_base, '_oldUsers'): del self.o._oldUsers
|
||||
# If the group was created by an Anonymous, Anonymous can't stay Owner
|
||||
# of the object.
|
||||
if None in self.o.__ac_local_roles__:
|
||||
del self.o.__ac_local_roles__[None]
|
||||
# If the group was created by anon, anon can't stay its Owner.
|
||||
if 'anon' in self.o.__ac_local_roles__:
|
||||
del self.o.__ac_local_roles__['anon']
|
||||
return self._callCustom('onEdit', created)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -174,8 +174,8 @@ class ToolWrapper(AbstractWrapper):
|
|||
</table>
|
||||
|
||||
<!-- One section for every searchable root class -->
|
||||
<x for="rootClass in [rc for rc in rootClasses \
|
||||
if ztool.userMaySearch(rc)]">
|
||||
<x for="rootClass in rootClasses" if="ztool.userMaySearch(rootClass)"
|
||||
var2="className=ztool.getPortalType(rootClass)">
|
||||
|
||||
<!-- A separator if required -->
|
||||
<div class="portletSep" var="nb=loop.rootClass.nb"
|
||||
|
@ -188,31 +188,30 @@ class ToolWrapper(AbstractWrapper):
|
|||
<a var="queryParam=searchInfo.default and \
|
||||
searchInfo.default.name or ''"
|
||||
href=":'%s?className=%s&search=%s' % \
|
||||
(queryUrl,rootClass,queryParam)"
|
||||
class=":(not currentSearch and (currentClass==rootClass) and \
|
||||
(queryUrl, className, queryParam)"
|
||||
class=":(not currentSearch and (currentClass==className) and \
|
||||
(currentPage=='query')) and \
|
||||
'portletCurrent' or ''">::_(rootClass + '_plural')</a>
|
||||
'portletCurrent' or ''">::_(className + '_plural')</a>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<x var="addPermission='%s: Add %s' % (appName, rootClass);
|
||||
userMayAdd=user.has_permission(addPermission, appFolder);
|
||||
<x var="mayCreate=ztool.userMayCreate(rootClass);
|
||||
createMeans=ztool.getCreateMeans(rootClass)">
|
||||
|
||||
<!-- Create a new object from a web form -->
|
||||
<!-- Create a new object from a web form. -->
|
||||
<input type="button" class="button"
|
||||
if="userMayAdd and ('form' in createMeans)"
|
||||
if="mayCreate and ('form' in createMeans)"
|
||||
style=":url('buttonAdd', bg=True)" value=":_('query_create')"
|
||||
onclick=":'goto(%s)' % \
|
||||
q('%s/do?action=Create&className=%s' % \
|
||||
(toolUrl, rootClass))"/>
|
||||
(toolUrl, className))"/>
|
||||
|
||||
<!-- Create object(s) by importing data -->
|
||||
<input type="button" class="button"
|
||||
if="userMayAdd and ('import' in createMeans)"
|
||||
if="mayCreate and ('import' in createMeans)"
|
||||
style=":url('buttonImport', bg=True)" value=":_('query_import')"
|
||||
onclick=":'goto(%s)' % \
|
||||
q('%s/import?className=%s' % (toolUrl, rootClass))"/>
|
||||
q('%s/import?className=%s' % (toolUrl, className))"/>
|
||||
</x>
|
||||
|
||||
<!-- Searches -->
|
||||
|
@ -221,7 +220,7 @@ class ToolWrapper(AbstractWrapper):
|
|||
<!-- Live search -->
|
||||
<form action=":'%s/do' % toolUrl">
|
||||
<input type="hidden" name="action" value="SearchObjects"/>
|
||||
<input type="hidden" name="className" value=":rootClass"/>
|
||||
<input type="hidden" name="className" value=":className"/>
|
||||
<table cellpadding="0" cellspacing="0">
|
||||
<tr valign="bottom">
|
||||
<td><input type="text" size="14" name="w_SearchableText"
|
||||
|
@ -234,13 +233,13 @@ class ToolWrapper(AbstractWrapper):
|
|||
</form>
|
||||
|
||||
<!-- Advanced search -->
|
||||
<div var="highlighted=(currentClass == rootClass) and \
|
||||
<div var="highlighted=(currentClass == className) and \
|
||||
(currentPage == 'search')"
|
||||
class=":highlighted and 'portletSearch portletCurrent' or \
|
||||
'portletSearch'"
|
||||
align=":dright">
|
||||
<a var="text=_('search_title')" style="font-size: 88%"
|
||||
href=":'%s/search?className=%s' % (toolUrl, rootClass)"
|
||||
href=":'%s/search?className=%s' % (toolUrl, className)"
|
||||
title=":text"><x>:text</x>...</a>
|
||||
</div>
|
||||
</x>
|
||||
|
@ -754,16 +753,6 @@ class ToolWrapper(AbstractWrapper):
|
|||
'''Sends a mail. See doc for appy.gen.mail.sendMail.'''
|
||||
sendMail(self, to, subject, body, attachments=attachments)
|
||||
|
||||
def refreshSecurity(self):
|
||||
'''Refreshes, on every object in the database, security-related,
|
||||
workflow-managed information.'''
|
||||
context = {'nb': 0}
|
||||
for className in self.o.getProductConfig().allClassNames:
|
||||
self.compute(className, context=context, noSecurity=True,
|
||||
expression="ctx['nb'] += int(obj.o.refreshSecurity())")
|
||||
msg = 'Security refresh: %d object(s) updated.' % context['nb']
|
||||
self.log(msg)
|
||||
|
||||
def refreshCatalog(self, startObject=None):
|
||||
'''Reindex all Appy objects. For some unknown reason, method
|
||||
catalog.refreshCatalog is not able to recatalog Appy objects.'''
|
||||
|
|
|
@ -140,8 +140,18 @@ class UserWrapper(AbstractWrapper):
|
|||
else:
|
||||
self.title = self.login
|
||||
|
||||
def ensureAdminIsManager(self):
|
||||
'''User 'admin' must always have role 'Manager'.'''
|
||||
if self.o.id == 'admin':
|
||||
roles = self.roles
|
||||
if 'Manager' not in roles:
|
||||
if not roles: roles = ['Manager']
|
||||
else: roles.append('Manager')
|
||||
self.roles = roles
|
||||
|
||||
def onEdit(self, created):
|
||||
self.updateTitle()
|
||||
self.ensureAdminIsManager()
|
||||
aclUsers = self.o.acl_users
|
||||
login = self.login
|
||||
if created:
|
||||
|
@ -173,19 +183,25 @@ class UserWrapper(AbstractWrapper):
|
|||
self.o.manage_addLocalRoles(login, ('Owner',))
|
||||
# If the user was created by an Anonymous, Anonymous can't stay Owner
|
||||
# of the object.
|
||||
if None in self.o.__ac_local_roles__:
|
||||
del self.o.__ac_local_roles__[None]
|
||||
if 'anon' in self.o.__ac_local_roles__:
|
||||
del self.o.__ac_local_roles__['anon']
|
||||
return self._callCustom('onEdit', created)
|
||||
|
||||
def mayEdit(self):
|
||||
'''No one can edit users "system" and "anon".'''
|
||||
if self.o.id in ('system', 'anon'): return
|
||||
# Call custom "mayEdit" when present.
|
||||
custom = self._getCustomMethod('mayEdit')
|
||||
if custom: return self._callCustom('mayEdit')
|
||||
else: return True
|
||||
return True
|
||||
|
||||
def mayDelete(self):
|
||||
'''No one can delete users "system", "anon" and "admin".'''
|
||||
if self.o.id in ('system', 'anon', 'admin'): return
|
||||
# Call custom "mayDelete" when present.
|
||||
custom = self._getCustomMethod('mayDelete')
|
||||
if custom: return self._callCustom('mayDelete')
|
||||
else: return True
|
||||
return True
|
||||
|
||||
def getZopeUser(self):
|
||||
'''Gets the Zope user corresponding to this user.'''
|
||||
|
@ -198,97 +214,87 @@ class UserWrapper(AbstractWrapper):
|
|||
# Call a custom "onDelete" if any.
|
||||
return self._callCustom('onDelete')
|
||||
|
||||
# Standard Zope user methods -----------------------------------------------
|
||||
def getLogins(self):
|
||||
'''Gets all the logins that can "match" this user: it own login and the
|
||||
logins of all the groups he belongs to.'''
|
||||
# Try first to get those logins from a cache on the request.
|
||||
try:
|
||||
return self.request.userLogins
|
||||
except AttributeError:
|
||||
res = [group.login for group in self.groups]
|
||||
res.append(self.login)
|
||||
return res
|
||||
|
||||
def getRoles(self):
|
||||
'''This method returns all the global roles for this user, not simply
|
||||
self.roles, but also "ungrantable roles" (like Anonymous or
|
||||
Authenticated) and roles inherited from group membership.'''
|
||||
# Try first to get those roles from a cache on the request.
|
||||
try:
|
||||
return self.request.userRoles
|
||||
except AttributeError:
|
||||
res = list(self.roles)
|
||||
# Add ungrantable roles
|
||||
if self.o.id == 'anon':
|
||||
res.append('Anonymous')
|
||||
else:
|
||||
res.append('Authenticated')
|
||||
# Add group global roles
|
||||
for group in self.groups:
|
||||
for role in group.roles:
|
||||
if role not in res: res.append(role)
|
||||
return res
|
||||
|
||||
def getRolesFor(self, obj):
|
||||
'''Gets the roles the user has in the context of p_obj: its global roles
|
||||
+ its roles which are local to p_obj.'''
|
||||
obj = obj.o
|
||||
# Start with user global roles.
|
||||
res = self.getRoles()
|
||||
# Add local roles, granted to the user directly or to one of its groups.
|
||||
localRoles = getattr(obj.aq_base, '__ac_local_roles__', None)
|
||||
if not localRoles: return res
|
||||
# Gets the logins of this user and all its groups.
|
||||
logins = self.getLogins()
|
||||
for login, roles in localRoles.iteritems():
|
||||
# Ignore logins not corresponding to this user.
|
||||
if login not in logins: continue
|
||||
for role in roles:
|
||||
if role not in res: res.append(role)
|
||||
return res
|
||||
|
||||
def has_role(self, role, obj=None):
|
||||
zopeUser = self.request.zopeUser
|
||||
if obj: return zopeUser.has_role(role, obj)
|
||||
return zopeUser.has_role(role)
|
||||
'''Has the logged user some p_role? If p_obj is None, check if the user
|
||||
has p_role globally; else, check if he has this p_role in the context
|
||||
of p_obj.'''
|
||||
if obj:
|
||||
roles = self.getRolesFor(obj)
|
||||
else:
|
||||
roles = self.getRoles()
|
||||
return role in roles
|
||||
|
||||
def has_permission(self, permission, obj):
|
||||
return self.request.zopeUser.has_permission(permission, obj)
|
||||
|
||||
def getRoles(self):
|
||||
'''This method collects all the roles for this user, not simply
|
||||
user.roles, but also roles inherited from group membership.'''
|
||||
return self.getZopeUser().getRoles()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
try:
|
||||
from AccessControl.PermissionRole import _what_not_even_god_should_do, \
|
||||
rolesForPermissionOn
|
||||
from Acquisition import aq_base
|
||||
except ImportError:
|
||||
pass # For those using Appy without Zope
|
||||
|
||||
class ZopeUserPatches:
|
||||
'''This class is a fake one that defines Appy variants of some of Zope's
|
||||
AccessControl.User methods. The idea is to implement the notion of group
|
||||
of users.'''
|
||||
|
||||
def getRoles(self):
|
||||
'''Returns the global roles that this user (or any of its groups)
|
||||
possesses.'''
|
||||
res = list(self.roles)
|
||||
if 'Anonymous' not in res: res.append('Authenticated')
|
||||
# Add group global roles
|
||||
if not hasattr(aq_base(self), 'groups'): return res
|
||||
for roles in self.groups.itervalues():
|
||||
for role in roles:
|
||||
if role not in res: res.append(role)
|
||||
return res
|
||||
|
||||
def getRolesInContext(self, object):
|
||||
'''Return the list of global and local (to p_object) roles granted to
|
||||
this user (or to any of its groups).'''
|
||||
if isinstance(object, AbstractWrapper): object = object.o
|
||||
object = getattr(object, 'aq_inner', object)
|
||||
# Start with user global roles
|
||||
res = self.getRoles()
|
||||
# Add local roles
|
||||
localRoles = getattr(object, '__ac_local_roles__', None)
|
||||
if not localRoles: return res
|
||||
userId = self.getId()
|
||||
groups = getattr(self, 'groups', ())
|
||||
for id, roles in localRoles.iteritems():
|
||||
if (id != userId) and (id not in groups): continue
|
||||
for role in roles:
|
||||
if role not in res: res.append(role)
|
||||
return res
|
||||
|
||||
def allowed(self, object, object_roles=None):
|
||||
'''Checks whether the user has access to p_object. The user (or one of
|
||||
its groups) must have one of the roles in p_object_roles.'''
|
||||
if object_roles is _what_not_even_god_should_do: return 0
|
||||
# If "Anonymous" is among p_object_roles, grant access.
|
||||
if (object_roles is None) or ('Anonymous' in object_roles): return 1
|
||||
# If "Authenticated" is among p_object_roles, grant access if the user
|
||||
# is not anonymous.
|
||||
if 'Authenticated' in object_roles and \
|
||||
(self.getUserName() != 'Anonymous User'):
|
||||
if self._check_context(object): return 1
|
||||
# Try first to grant access based on global user roles
|
||||
'''Has the logged user p_permission on p_obj?'''
|
||||
obj = obj.o
|
||||
# What are the roles which are granted p_permission on p_obj?
|
||||
allowedRoles = obj.getRolesFor(permission)
|
||||
# Grant access if "Anonymous" is among roles.
|
||||
if ('Anonymous' in allowedRoles): return True
|
||||
# Grant access if "Authenticated" is among p_roles and the user is not
|
||||
# anonymous.
|
||||
if ('Authenticated' in allowedRoles) and (self.o.id != 'anon'):
|
||||
return True
|
||||
# Grant access based on global user roles.
|
||||
for role in self.getRoles():
|
||||
if role not in object_roles: continue
|
||||
if self._check_context(object): return 1
|
||||
return
|
||||
# Try then to grant access based on local roles
|
||||
innerObject = getattr(object, 'aq_inner', object)
|
||||
localRoles = getattr(innerObject, '__ac_local_roles__', None)
|
||||
if role in allowedRoles: return True
|
||||
# Grant access based on local roles
|
||||
localRoles = getattr(obj.aq_base, '__ac_local_roles__', None)
|
||||
if not localRoles: return
|
||||
userId = self.getId()
|
||||
groups = getattr(self, 'groups', ())
|
||||
for id, roles in localRoles.iteritems():
|
||||
if (id != userId) and (id not in groups): continue
|
||||
# Gets the logins of this user and all its groups.
|
||||
userLogins = self.getLogins()
|
||||
for login, roles in localRoles.iteritems():
|
||||
# Ignore logins not corresponding to this user.
|
||||
if login not in logins: continue
|
||||
for role in roles:
|
||||
if role not in object_roles: continue
|
||||
if self._check_context(object): return 1
|
||||
return
|
||||
|
||||
try:
|
||||
from AccessControl.User import SimpleUser
|
||||
SimpleUser.getRoles = getRoles
|
||||
SimpleUser.getRolesInContext = getRolesInContext
|
||||
SimpleUser.allowed = allowed
|
||||
except ImportError:
|
||||
pass
|
||||
if role in allowedRoles: return True
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -533,7 +533,7 @@ class AbstractWrapper(object):
|
|||
</table>''')
|
||||
|
||||
pxView = Px('''
|
||||
<x var="x=zobj.allows('View', raiseError=True);
|
||||
<x var="x=zobj.allows('read', raiseError=True);
|
||||
errors=req.get('errors', {});
|
||||
layout=zobj.getPageLayout(layoutType);
|
||||
phaseObj=zobj.getAppyPhases(currentOnly=True, layoutType='view');
|
||||
|
@ -549,7 +549,7 @@ class AbstractWrapper(object):
|
|||
</x>''', template=pxTemplate, hook='content')
|
||||
|
||||
pxEdit = Px('''
|
||||
<x var="x=zobj.allows('Modify portal content', raiseError=True);
|
||||
<x var="x=zobj.allows('write', raiseError=True);
|
||||
errors=req.get('errors', None) or {};
|
||||
layout=zobj.getPageLayout(layoutType);
|
||||
cssJs={};
|
||||
|
@ -625,9 +625,9 @@ class AbstractWrapper(object):
|
|||
def _getParentAttr(klass, attr):
|
||||
'''Gets value of p_attr on p_klass base classes (if this attr exists).
|
||||
Scan base classes in the reverse order as Python does. Used by
|
||||
classmethods m_getWorkflow and m_getCreators below. Scanning base
|
||||
classes in reverse order allows user-defined elements to override
|
||||
default Appy elements.'''
|
||||
classmethod m_getWorkflow below. Scanning base classes in reverse
|
||||
order allows user-defined elements to override default Appy
|
||||
elements.'''
|
||||
i = len(klass.__bases__) - 1
|
||||
res = None
|
||||
while i >= 0:
|
||||
|
@ -643,15 +643,6 @@ class AbstractWrapper(object):
|
|||
if not res: res = WorkflowAnonymous
|
||||
return res
|
||||
|
||||
@classmethod
|
||||
def getCreators(klass, cfg):
|
||||
'''Returns the roles that are allowed to create instances of p_klass.
|
||||
p_cfg is the product config that holds the default value.'''
|
||||
res = klass._getParentAttr('creators')
|
||||
# Return default creators if no creators was found.
|
||||
if not res: res = cfg.appConfig.defaultCreators
|
||||
return res
|
||||
|
||||
@classmethod
|
||||
def getIndexes(klass, includeDefaults=True):
|
||||
'''Returns a dict whose keys are the names of the indexes that are
|
||||
|
@ -823,7 +814,6 @@ class AbstractWrapper(object):
|
|||
if isField:
|
||||
# Link the object to this one
|
||||
appyType.linkObject(self.o, zopeObj)
|
||||
zopeObj._appy_managePermissions()
|
||||
# Call custom initialization
|
||||
if externalData: param = externalData
|
||||
else: param = True
|
||||
|
|
Loading…
Reference in a new issue