[gen] Security: added missing checks at the code level, ensuring that a user can create instances of a given class (root classes, or instances created via an initiator field); bugfixes in the test system, which works again (was broken after deplonization); [shared] XmlUnmarshaller can now be ran in 'non utf-8' mode: if enabled, any marshalled string will no be Python unicode, but simple str.

This commit is contained in:
Gaetan Delannay 2012-06-02 14:36:49 +02:00
parent 0d7afb685f
commit f843d5b7d6
11 changed files with 167 additions and 79 deletions

View file

@ -1,14 +1,14 @@
# ------------------------------------------------------------------------------
import os, os.path, sys
try:
from AccessControl.SecurityManagement import \
newSecurityManager, noSecurityManager
except ImportError:
pass
# ------------------------------------------------------------------------------
class TestMixin:
'''This class is mixed in with any ZopeTestCase.'''
def createUser(self, userId, roles):
'''Creates a user with id p_userId with some p_roles.'''
self.acl_users.addMember(userId, 'password', [], [])
self.setRoles(roles, name=userId)
def changeUser(self, userId):
'''Logs out currently logged user and logs in p_loginName.'''
self.logout()
@ -55,6 +55,16 @@ class TestMixin:
return arg[10:].strip(']')
return None
def login(self, name='admin'):
user = self.app.acl_users.getUserById(name)
newSecurityManager(None, user)
def logout(self):
'''Logs out.'''
noSecurityManager()
def _setup(self): pass
# Functions executed before and after every test -------------------------------
def beforeTest(test):
'''Is executed before every test.'''
@ -64,7 +74,6 @@ def beforeTest(test):
g['appFolder'] = cfg.diskFolder
moduleOrClassName = g['test'].name # Not used yet.
# Initialize the test
test.createUser('admin', ('Member','Manager'))
test.login('admin')
g['t'] = g['test']

View file

@ -898,11 +898,16 @@ class ToolMixin(BaseMixin):
userId = self.getUser().getId()
# Perform the logout in acl_users
rq.RESPONSE.expireCookie('__ac', path='/')
# Invalidate existing sessions.
sdm = self.session_data_manager
session = sdm.getSessionData(create=0)
if session is not None:
session.invalidate()
# Invalidate session.
try:
sdm = self.session_data_manager
except AttributeError, ae:
# When ran in test mode, session_data_manager is not there.
sdm = None
if sdm:
session = sdm.getSessionData(create=0)
if session is not None:
session.invalidate()
self.log('User "%s" has been logged out.' % userId)
# Remove user from variable "loggedUsers"
from appy.gen.installer import loggedUsers

View file

@ -25,6 +25,20 @@ class BaseMixin:
return self
o = property(get_o)
def getInitiatorInfo(self):
'''Gets information about a potential initiator object from the request.
Returns a 3-tuple (initiator, pageName, field):
* initiator is the initiator (Zope) object;
* pageName is the page on the initiator where the origin of the Ref
field lies;
* field is the Ref instance.
'''
if not rq.get('nav', '').startswith('ref.'): return (None,)*4
splitted = rq['nav'].split('.')
initiator = self.tool.getObject(splitted[1])
fieldName, page = splitted[2].split(':')
return (initiator, page, self.getAppyType(fieldName))
def createOrUpdate(self, created, values,
initiator=None, initiatorField=None):
'''This method creates (if p_created is True) or updates an object.
@ -43,10 +57,9 @@ class BaseMixin:
if not initiator:
folder = tool.getPath('/data')
else:
if initiator.isPrincipiaFolderish:
folder = initiator
else:
folder = initiator.getParentNode()
folder = initiator.getCreateFolder()
# Check that the user can add objects through this Ref.
initiatorField.checkAdd(initiator)
obj = createObject(folder, id, obj.portal_type, tool.getAppName())
previousData = None
if not created: previousData = obj.rememberPreviousData()
@ -61,7 +74,7 @@ class BaseMixin:
obj.historizeData(previousData)
# Manage potential link with an initiator object
if created and initiator: initiator.appy().link(initiatorField, obj)
if created and initiator: initiator.appy().link(initiatorField.name,obj)
# Manage "add" permissions and reindex the object
obj._appy_managePermissions()
@ -112,13 +125,16 @@ class BaseMixin:
# Create the params to add to the URL we will redirect the user to
# create the object.
urlParams = {'mode':'edit', 'page':'main', 'nav':''}
if rq.get('nav', None):
initiator, initiatorPage, initiatorField = self.getInitiatorInfo()
if initiator:
# The object to create will be linked to an initiator object through
# a ref field. We create here a new navigation string with one more
# a Ref field. We create here a new navigation string with one more
# item, that will be the currently created item.
splitted = rq.get('nav').split('.')
splitted[-1] = splitted[-2] = str(int(splitted[-1])+1)
urlParams['nav'] = '.'.join(splitted)
# Check that the user can add objects through this Ref field
initiatiorField.checkAdd(initiator)
# Create a temp object in /temp_folder
tool = self.getTool()
id = tool.generateUid(className)
@ -188,13 +204,7 @@ class BaseMixin:
errorMessage = 'Please correct the indicated errors.' # XXX Translate
isNew = rq.get('is_new') == 'True'
# If this object is created from an initiator, get info about him.
initiator = None
initiatorPage = None
initiatorField = None
if rq.get('nav', '').startswith('ref.'):
splitted = rq['nav'].split('.')
initiator = tool.getObject(splitted[1])
initiatorField, initiatorPage = splitted[2].split(':')
initiator, initiatorPage, initiatorField = self.getInitiatorInfo()
# If the user clicked on 'Cancel', go back to the previous page.
if rq.get('buttonCancel.x', None):
if initiator:
@ -434,6 +444,14 @@ class BaseMixin:
# broken on returned object.
return getattr(self, methodName, None)
def getCreateFolder(self):
'''When an object must be created from this one through a Ref field, we
must know where to put the newly create object: within this one if it
is folderish, besides this one in its parent else.
'''
if self.isPrincipiaFolderish: return self
return self.getParentNode()
def getFieldValue(self, name, onlyIfSync=False, layoutType=None,
outerValue=None):
'''Returns the database value of field named p_name for p_self.
@ -534,10 +552,10 @@ class BaseMixin:
if not refs: raise IndexError()
return refs.index(obj.UID())
def mayAddReference(self, name, folder):
def mayAddReference(self, name):
'''May the user add references via Ref field named p_name in
p_folder?'''
return self.getAppyType(name).mayAdd(self, folder)
return self.getAppyType(name).mayAdd(self)
def isDebug(self):
'''Are we in debug mode ?'''
@ -1227,9 +1245,7 @@ class BaseMixin:
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
if not self.isPrincipiaFolderish:
folder = self.getParentNode()
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
@ -1238,12 +1254,12 @@ class BaseMixin:
if appyType.type != 'Ref': continue
if appyType.isBack or appyType.link: continue
# Indeed, no possibility to create objects with such Refs
refType = self.getTool().getPortalType(appyType.klass)
tool = self.getTool()
refType = tool.getPortalType(appyType.klass)
if refType not in addPermissions: continue
# Get roles that may add this content type
creators = getattr(appyType.klass, 'creators', None)
if not creators:
creators = self.getProductConfig().defaultAddRoles
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: