Sudo like tool for Alfresco webscripts
Posted on Monday, November 23rd, 2009 at 22:55
This year we at GetConnected worked a lot on integration solutions based on Alfresco. Integrating customers’ softwares with Alfresco means, first of all, facingĀ with different permissions models: the Alfresco’s one and that of the external software. Most of the times they differ and it couldn’t be otherwise: althought Alfresco is an extendable, general purpose product, external softwares target specific problem and have ad-hoc solutions.
Permission model in Alfresco can be extended defining custom roles. For instance, one can create a WriteOnly role in oder to give write only permissions to users and groups on a folder (write children and contents but no read). This kind of extension should be the first one to be inspected when tackling the integration. I’ll write about it in another post.
Sometimes extending the Alfresco’s permission model is not enough as it only affects the way users access the nodes in a filesystem like fashion. Consider the case where some users are supposed to create groups by calling a custom webscript. Extending the permission model here is pointless. Though, two solutions are feasible:
- running the whole webscript as admin (through the runas attribute in the webscript description document)
- assigning these users to the ALFRESCO_ADMINISTRATORS group
Both solutions seem too coarse grained to me: the former because it will not make any distinction between users, the latter because it will grant these users a too wide set of privileges (create and delete users, access others’ folders, etc.) out of the scope of the webscript.
The solution I adopted in these cases was to use the AuthenticationUtil#runAs method of the Java APIs from within a Javascript backed webscript to execute some arbitrary code with the admin user privileges (which resembles the Unix sudo tool functionality). Lets see it in details.
The central part of the trick is to create a root Javascript object (just like people, companyhome, ecc.) with a sudo method that can be invoked from the webscript. This has to be written in Java and instanced as a Spring bean.
Here is the relevant part of the class (file Sudo.java):
public class Sudo extends BaseScopableProcessorExtension {
public void sudo(final Function func) {
final Context cx = Context.getCurrentContext();
final Scriptable scope = getScope();
RunAsWork<Object> raw = new RunAsWork<Object>() {
public Object doWork() throws Exception {
func.call(cx, scope, scope, new Object[] {});
return null;
}
};
AuthenticationUtil.runAs(raw, AuthenticationUtil.getAdminUserName());
}
}
As you can see, the Sudo#sudo method expects a Rhino Function object in input and calls it using the default context and scope. What does matter here is that the function is called with the administrator privileges by means of the AuthenticationUtil#runAs method. The input function in this example code is not expected to return any result but this can be obviously changed (a Scriptable instance could be returned, for example).
I then declared a Spring bean named Sudo that will represent our javascript root object named sudoUtils. To accomplish this I wrote a file named sudo-script-services-context.xml; this will be deployed in the Alfresco extension folder.
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<bean id="Sudo" parent="baseJavaScriptExtension" class="eu.fabiostrozzi.sudo.ws.js.Sudo">
<property name="extensionName">
<value>sudoUtils</value>
</property>
</bean>
</beans>
To prove how sudoUtils can be used I wrote a simple webscript that creates two things: a folder under the user home space and a group with the same name. The name is passed in the request url. Webscript logic follows:
function main() {
var name = url.templateArgs.name;
// folder is created with the user privileges
var folder = userhome.createFolder(name);
// creates the relative group as admin
if (!people.isAdmin(person)) {
// in case user is not admin use the sudoUtils object
var f = function() { people.createGroup(name); };
sudoUtils.sudo(f);
} else
people.createGroup(name);
model.success = true;
}
main();
As mentioned above, a function is passed to the sudoUtils object. In this case, the function closure will have a reference to the name variable.
Here is the webscript definition:
<webscript>
<shortname>Sudo example</shortname>
<description>Sudo example</description>
<url>/sudo/{name}</url>
<format default="json">argument</format>
<authentication>user</authentication>
<transaction allow="readwrite" buffersize="0">required</transaction>
</webscript>
The required authentication is user which means it’s not necessary to be admin to execute this webscript.
Just to recap, the main arguments in favor of this solution are:
- users do not need to be administrator to execute this webscript
- privileges escalation can be done selectively: webscript is run with minimum privileges with exception of the part that requires to be administrator
You can download the source code of this example; it’s about 12MB because it’s shipped with the jar dependencies of the Alfresco 3.2r2 community edition. Even if project can be opened and compiled with Eclipse, I created some Ant tasks to speed up the deploy phase. To compile it just run ant. Set the path to the Tomcat home folder in build.properties and run ant deploy to deploy everything. Extensions will be deployed in shared/classes/alfresco/extension under the Tomcat folder while the Sudo class will be packed in a jar and deployed in webapps/alfresco/WEB-INF/lib under the Tomcat folder. Now restart Tomcat to make the changes take effect.
If you want to swiftly test the webscript you use Curl. Login into the Alfresco explorer as admin and create a new user. Then call curl as follows:
curl -u user:password -X POST http://localhost:8080/alfresco/s/sudo/something
Be sure to replace user and password in the afore call with the proper values.
If everything is ok you should see this JSON response:
{
success: true
}
Navigate to the user home using the Alfresco explorer. You should see a folder named “something”. If you open the Administration console a group with the same name should be found.
Security aspects
As Peter Monks pointed out there’re risks you’d better be aware of if you intend to use this extension in your projects. These are discussed in another post.




Jan Pfitzner November 25th, 2009 at 10:48
very cool approach!