CATEGORY {IT}


IT and Code snippets and Java12 Jan 2008 09:54 am

Acegi - the Java security framework

I suppose that you know what is Spring Security (former Acegi project) is, or at least you are aware of its wide securing abilities for a Java web application. Besides lots of features which Acegi offers you out-of-the box, there are many configurable options. This time, I had a task to implement a functionality which allows restricting access to method invocations based not only on the granted roles to the logged in user, but taking in account passed parameters as well. Let’s see the result.

@Secured annotations

Assume that you are developing pretty common web application with standard security model with users, granted roles, etc. Of course, the main goal of the security infrastructure is accepting/denying user access to different parts of your application based on the granted access rights. Acegi offers you two main ways of controlling access: URL based which analyzes the clients http requests to your application and based on the AOP principles which controls method invocations.

With the URL based method you can achieve rough securing coverage very simply. For instance, if you have /admin part, you just deny the access to all users who don’t have ROLE_ADMIN granted role to the URLs which starts from /admin/** string. The part /store you can make available only to users with role ROLE_CUSTOMER, etc. You can cover all you URLs with such rules, but unfortunately, if you want more precise control, you have to write lots of business logic code in your Controllers/Managers which performs additional checks in the methods body.

Let’s consider an idiomatic example (no real DAOs, no Hibernate etc) consisting of:

  • a controller ShoppingCartAddItemController which is mapped to /shop/card/addItem.jsp url,
  • a service class IShoppingCartManager which provides with business logic,
  • ROLE_USER which is granted to Scott user:
public class ShoppingCartAddItemController extends AbstractController {

    private IShoppingCartManager shoppingCartManager;

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request,
        HttpServletResponse response) throws Exception {
        Integer customerId = ServletRequestUtils.getIntParameter(request, "customerId");
        Integer itemId = ServletRequestUtils.getIntParameter(request, "itemId");
        Integer amount = ServletRequestUtils.getIntParameter(request, "amount");

        shoppingCartManager.addItem(customerId, itemId, amount);

        return new ModelAndView("redirect:/shop/item/congratulations.jsp");
    }

    public void setShoppingCartManager(IShoppingCartManager shoppingCartManager) {
        this.shoppingCartManager = shoppingCartManager;
    }
}

public interface IShoppingCartManager {

    @Secured({"ROLE_USER" })
    void addItem(Integer customerId, Integer itemId, Integer amount);

    @Secured({"ROLE_USER" })
    void deleteItem(Integer customerId, Integer itemId);

}

public class ShoppingCartManager implements IShoppingCartManager {

    public void addItem(Integer customerId, Integer itemId, Integer amount) {
        // use DAO
    }

    public void deleteItem(Integer customerId, Integer itemId) {
        // use DAO
    }

}

The ROLE_USER guarantees that the secured method can be invoked from the controller if the logged in user is granted by this role. But in this case, being logged in, I can change the URL parameter customerId to another value and Acegi won’t check it. To prevent it, we need to paste something like this in every Manager method (or Controller or Filter for every URL which matches /shop/**/*?customerId=… pattern):

    public void addItem(Integer customerId, Integer itemId, Integer amount) {
         Integer loggedCustomerId = securityManager.getLoggedCustomerId();
         if (loggedCustomerId != customerId) {
              throw new AccessDeniedException("access denied");
         }
         // business logic
    }

The main question is: how can we minimize coding, if we know that lots of methods requires the same type of access check? The answer is pretty obvious - we need a AOP aspect which would do this routine instead of us. From another point of view, it would be great if we could employ already used @Secured annotation facilities.

Conditional Roles

After brief googling, I caught the first clue. It was a similar question on Spring framework forum, and eventually I came to the topic which lead to Conditional Roles patch SEC-273. It was an implementation by Usama Rashwan of the idea of Karl Moore.

The idea is elegant and straightforward. All tricks with method invocations in Acegi could be done by implementing RoleVoter interface where you can implement your own access decision logic. But writing your own RoleVoter for every scenario is violating DRY principle. So, it was suggested to write one RoleVoter and to use a Scripting Language like OGNL, MVEL to write conditions right in the role names after special delimiter “::”.

In our case, the new @Secured annotation changes to:

    @Secured({"ROLE_USER::authentication.principal.customerId == arg0" })
    void addItem(Integer customerId, Integer itemId, Integer amount);

where authentication is SecurityContextHolder.getContext().getAuthentication() object which usually holds all logged in user details. This object is put to MVEL context by AbstractInvocationChecker. Besides this object, you can operate by arguments passed to the secured method, here arg0 is the first parameter passed to the method. The MVEL scripting language is very flexible, it can cope with JavaBeans, operations on collections, etc. Of course you can’t do there rocket science computations, but for our needs it is enough.

I suggest the next way of usage:

  • Extend UserDetails class with information required in decision point when the secured method is invoked. It could be ids of objects which is allowed to be manipulated by logged in user.
  • Populate UserDetails with all needed information while a user logging in by UserDetailsService. This object is available as “authentication.principal” object in MVEL context.
  • Write conditional roles based on this objects mixing with passed arguments.
  • Update the SecurityContextHolder.getContext().getAuthentication() if necessary during the user activity.

Buzz

If you are interested in this topic - just download the patch, it has a comprehensive example of usage and clear source code. The JIRA issue stated that this patch goes to 2.0.0 M2 which is going to be released soon.

IT15 Oct 2007 12:28 pm

I never thought that I would use phone activation/registration of any software product, especially Windows. But it happened. Firstly I was kindly asked to assist in choosing new PC for parents of my wife. There weren’t any special requirements, so I decided to buy it in one of the local computer stores rather than assemble it from components. We did short ride in some shops, and chose one model which seemed perfect as home PC (1.8 Core Duo, 1G RAM, 200G HDD, ASUS motherboard).
There was special discount - if you buy it with pre-installed Windows XP it becomes even cheaper than without OS. When we purchased and delivered it to home I have to enter license code printed on the recovery disk. It was usual and easy process. But then, I was asked to activate it, and encountered a problem - parents don’t have an Internet access yet. Hmm. I had to make a choice: to be asked “what’s wrong with that damn PC? it always asks something unclear..” or pass activation by phone to eliminate this irritating pop-up. Actually I had no choice, and started to call the number which appeared in the activation wizard. Frankly speaking I expected that I wouldn’t be answered because of Sunday, or I will talk to sleepy assistant. To my great surprise, I was greeted by answering machine, or rather voice-automate like every mobile operator or cinema have. The voice was charming and welcoming but then I realized that I had to type in my phone 9 groups of 6 digits generated by wizard (54 in total) :( Moreover, after this operation, I had to type the answer which was shorter but also too long. To get to this part I had to listen all rules and repeat actions correctly.
After I successfully activated the Windows, I was asked by robot to answer some questions about quality of phone activation, I answered only on first three questions (did anybody reach the last?). I chose variants that I passed from first time but I didn’t like that boring procedure. Next questions seemed as boring as the whole process, so I dropped the line.
I can’t say that the process was bad, but I still feel that I wasted 20 minutes of time.
I think that there is only one way to improve activation by phone - just remove that process at all. I don’t understant why should a customer activate the product if it was bought with licensed CD/DVD with printed secure label with license code. Anyway, this measures don’t work against pirated disks, because they already ships with well cracked $oft\/\/are.
Or, as an option, it can be activated in the shop where it was installed. For somebody the best is to use open software like Ubuntu :)

IT and Blogging24 Apr 2007 09:08 am

OpenId - is a one of the many global unique person identifier. The idea is not novel, but there are bunch of open-sourced, not vendor-dependent, collaborated implementations. Basically, the idea is in using your any well-known account, e.g. LiveJournal as a login or your ID in other systems. It is very useful, because you don’t need to create an account on another OpenID supported system, to make a simple action like leave a comment to a photo of your friend. As I run my own WordPress driven blog, I always wanted a some way to comment my friends posts on LiveJournal. But creating an account only for that reason, seemed to me meaningless. And at that moment appeared a genius and bright implementation of that unique id MyOpenId. Moreover, the LJ started to support this authentication method. For me only one inconvenience remained. I didn’t want sign my comments as jdev.myopenid.com (it is my MyOpenId account), because nobody knows who is behind this account. I needed a solution which allows me to associate my blog with my openId. Today I bumped into a great recipe in the How to Use Your Own URL, Not WordPress.com’s, As Your OpenID article.
Basically you need to do this few simple steps:

  • Get your MyOpenId.com account (or on any other OpenId provider)
  • Log in into your WordPress driven blog as admin
  • Go to Presentation -> Theme Editor -> Main Index Template
  • Add to head section of the index.php file such simple strings:
    <link rel="openid.server" href="http://www.myopenid.com/server" />
    <link rel="openid.delegate" href="http://yourAccount.myopenid.com/" />

Now, I can and do a LJ post comments of my friends :)