Sudo like tool for Alfresco webscripts

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:

  1. running the whole webscript as admin (through the runas attribute in the webscript description document)
  2. 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.

Leave a comment ?

14 Comments.

  1. Twitted by loftux - pingback on November 25, 2009 at 09:19
  2. uberVU - social comments - trackback on November 26, 2009 at 13:00
  3. Great stuff dude…
    I suggest you also post links to it in the Italian/English forum so that we can point the community even better to this nice fine-grained webscript solution…

    Ciao and keep up the good work!
    Gab

  4. Thanks for sharing this tip and the code to implement it. These types of contributions really do help the community!

    Nancy Garrity
    Alfresco Community Manager

  5. Thank you all for your positive comments.

  6. Why not use the built-in “runas” attribute of the “authentication” tag in the descriptor document? See http://wiki.alfresco.com/wiki/Web_Scripts#Creating_a_Description_Document for details.

    This does exactly what you describe, without requiring any coding whatsoever.

  7. Also, this kind of extension tends to have subtle security risks in certain configurations, so it may be worth putting a warning on it recommending that implementers think carefully about how they intend to use it.

    For example, in some cases customers open up write access to some or all of Data Dictionary > Scripts to allow non-admin users to author their own JS scripts. The native Alfresco JS API has been carefully designed to ensure that in this case a malicious script can only damage (eg. delete) those parts of the repository that the person executing the script has write access to.

    With this extension, anyone could author a script that sudos to admin, then deletes anything in the repository, regardless of the ACLs of the person executing the script.

    I’m not trying to belittle the value of an extension such as this, but just want to make sure that implementers are made fully aware of the risks of this kind of extension.

  8. Fabio Strozzi

    Hi Peter, I really appreciate your feedback.
    The extension was meant to run just some part of a (web)script with the admin privileges while letting the rest of it running with the privileges of the authenticated user. If I remember correctly, the runas attribute makes the user act as admin (or another user) for the whole webscript, which is not what I needed.
    Concerning security risks, you’re absolutely right: in the next days I’ll add some notes of warning along with suggestions on how to prevent unpleasant drawbacks.
    Thank you.

  9. Hola Fabio,

    Un post muy interesante y sobre todo, muy Ăștil.

  10. Sudo for Scripts « Open Source ECM/WCM - pingback on April 29, 2011 at 22:41
  11. Hi Fabio, thanks for your work.
    I tried it with Alfresco 3.4.d and it worked correctly. Now I’m trying with alfresco 4 but I have this error:

    {
    “status” :
    {
    “code” : 500,
    “name” : “Internal Error”,
    “description” : “An error inside the HTTP server which prevented it from ful
    filling the request.”
    },

    “message” : “05210006 Wrapped Exception (with status template): 05210017 Faile
    d to execute script ‘classpath*:alfresco\/templates\/webscripts\/eu\/fabiostrozz
    i\/sudo\/create.post.js’: 05210016 ReferenceError: \”sudoUtils\” is not defined.
    (file:\/C:\/Alfresco\/tomcat\/webapps\/alfresco\/WEB-INF\/classes\/alfresco\/te
    mplates\/webscripts\/eu\/fabiostrozzi\/sudo\/create.post.js#11)”,
    “exception” : “org.springframework.extensions.webscripts.WebScriptException –
    05210006 Wrapped Exception (with status template): 05210017 Failed to execute sc
    ript ‘classpath*:alfresco\/templates\/webscripts\/eu\/fabiostrozzi\/sudo\/create
    .post.js’: 05210016 ReferenceError: \”sudoUtils\” is not defined. (file:\/C:\/Al
    fresco\/tomcat\/webapps\/alfresco\/WEB-INF\/classes\/alfresco\/templates\/webscr
    ipts\/eu\/fabiostrozzi\/sudo\/create.post.js#11)”,

    …….

    Any idea?

    Thanks,
    Davide

  12. Hi Davide,

    It has been some years from your question, but the solution is to put the “*-context.xml” file in the following path “WEB-INF/classes/alfresco/extension/bootstrap”.

    The problem you showed is caused because Alfresco is not loading the *-context.xml file so the Java bean with the “sudoUtils” reference is not in the Spring context.

    Hope it could help anyone.

    Bye

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Trackbacks and Pingbacks: