Saturday, August 11, 2007

Dynamic roles management in acegi security: Part 2

Last time we stopped at a problem, and that was the fact that the bean holding the secure URL patterns is loaded only once at application startup. Now, when we need to alter the access rights of roles, we will need to restart the application, and this is really not very flexible.

There are a couple of solutions to this problem. The first is to reload the filter invocation interceptor, and set the object definition source in it with an instance that holds the new values, like the following example:






UrlPatternRolesDefinitionSource myRoles = (UrlPatternRolesDefinitionSource) applicationContext.getBean ("objectDefinitionSource");



FilterSecurityInterceptor filterSecurityInterceptor = (FilterSecurityInterceptor) applicationContext.getBean ("filterInvocationInterceptor");



filterSecurityInterceptor.setObjectDefinitionSource (myRoles);



Only here we have a trick that should be done. The bean objectDefinitionSource should be declared as non-singleton in the XML configuration, either by specifying its scope="prototype" or by specifying singleton="false". This solution has a problem, and it is that the whole list of roles and secure URL patterns will be reloaded from the database (or whatever your datasource is).

The other solution is to create your own ConfigAttributeDefinition that extends the one supplied by acegi. The main reason for doing this is that the one supplied by acegi doesn't have a removeAttribute method.I suggest overriding this implementation to add a removeAttribute method. I also prefer if you would use an ArrayList or a Map instead of the Vector used by the acegi implementation to represent the configAttributes

This ConfigAttributeDefinition should be the one you use in the UrlPatternDefinitionSource that we created in part one of this article.

In the class(es) that will be manipulating the the roles, you should inject the UrlPatternDefinitionSource instance that acegi creates (using spring's configuration files). You then get the ConfigAttributeDefinition for your role using the lookupAttribute method in the UrlPatternDefinitionSource class. A code snippet is shown below.






UrlPatternRolesDefinitionSource uprds = definitionSource;


ConfigAttribute config = new SecurityConfig(role.getName());


Set patternsSet = role.getUrlPatterns();


List list = urlPatternDao.listUrlPatternsOfRole(role);


List oldUrlPatternsOfRole = new ArrayList();


//get list of old url patterns of role


for (UrlPattern urlWithRole : list) {


oldUrlPatternsOfRole.add(urlWithRole);
}


//update role in database


roleDao.updateRole(role);


//do not allow the role to access old url patterns


for (UrlPattern pattern : oldUrlPatternsOfRole) {


MyConfigAttributeDefinition configDefinition = (MyConfigAttributeDefinition) uprds.lookupAttributes(pattern.getUrlPattern());


configDefinition.removeConfigAttribute(config);


}


//allow the role to access the new set of url patterns


for (UrlPattern pattern : patternsSet) {


//get the pattern itself from DB, since all we have is the id of the pattern only


pattern = urlPatternDao.retrieveUrlPattern(pattern.getId());


MyConfigAttributeDefinition configDefinition = (MyConfigAttributeDefinition) uprds.lookupAttributes(pattern.getUrlPattern());


configDefinition.addConfigAttribute(config);


}




The previous code may need some optimization, but it was just to give you a general idea about how to implement dynamic role management.

I hope that this article was useful, and I apologize once more for the delay.

3 comments:

Mustafa said...

Could you supply a full demo or source code of classes you defined?

Alaa Nassef said...

I think that will not be able to do that soon, due to my time constraints. I think I might do so in the future. Feel free to contact me at alan (dot) 125 (at) gmail (dot) com if you need any help.

cyril said...

Thanks Alaa for that great post. I really appreciate it!