Home

Executing restricted code as a different user in plone

Updated:
Created:

Restricted code executing under a different user id from a browser view in plone

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'