I want to run some tests on a live site (e.g. to test configurations, user problems that only seem to appear on the live site, etc.). For this I have a browser view, that is typacally called by an admin user. But some problems have to do with security, and I want to test under a different user id. Also, code in browser views normally is unrestricted, so security doesn't really apply.
Inspired by kiorky in #plone@freenode and a somewhat related article I came to the following solution:
#inspired by http://lionfacelemonface.wordpress.com/2010/06/17/securitymanager/
from AccessControl.SecurityManagement import newSecurityManager, setSecurityManager, getSecurityManager
from Products.CMFCore.utils import getToolByName
from AccessControl.ZopeGuards import guarded_getattr, get_safe_globals, safe_builtins
from RestrictedPython import compile_restricted
def callAsUser(site,userid,code):
#remember the current SecurityManager
current = getSecurityManager()
#switch to the selected user
acl_users = getToolByName(site, 'acl_users')
user = acl_users.getUserById(userid)
newSecurityManager(None,user)
#set the globals to somewhat safe values (that also do security checks)
globals = get_safe_globals()
globals['__builtins__'] = safe_builtins
globals['_getattr_'] = guarded_getattr
globals['site']=site
#we want to allow printing in the code
newcode = code + '\n\nprintresult=printed'
#the ... magic!
compiled = compile_restricted(newcode,'<string>','exec')
exec(compiled,globals)
#go back to original security manager
setSecurityManager(current)
#variable set in the code are in the returned dict, including printresult
return globals
This would be used like this:
code = """
site.manage_changeProperties(title='foobar')
print site.title
x = 1
"""
output = callAsUser(site,'SomeUser',code)
output['x'] == 1
output['printed'] == 'foobar\n'