Showing posts with label Spring. Show all posts
Showing posts with label Spring. Show all posts

Tuesday, May 5, 2009

Mutable authorities with Spring Security and CAS

Recently I worked on an application with the following requisite:

The logged user must select its current role among the roles for which he's authorized.


A simple requisite, and (apparently) it's easy to implement it with Spring Security: write an UserDetails class in which you can select the returned authority(ies). For example:


public class LoggedUserWithSelectableRole extends User {
private GrantedAuthority currentAuthority;

public LoggedUserWithSelectableRole(String username, String password,
boolean enabled, GrantedAuthority[] authorities) throws IllegalArgumentException {
super(username, password, enabled, authorities);
}

public void setCurrentAuthority(GrantedAuthority currentAuthority) {
this.currentAuthority = currentAuthority;
}

@Override
public GrantedAuthority[] getAuthorities() {
if (Arrays.asList(super.getAuthorities()).contains(currentAuthority)) {
return new GrantedAuthority[] {currentAuthority};
} else {
return new GrantedAuthority[0];
}
}

public GrantedAuthority[] getAllAuthorities() {
return super.getAuthorities();
}
}


Now you can select an authority for the logged user (for example, in a controller):


@RequestMapping
public String selectRole(@RequestParam(value = "role") int role) {
LoggedUserWithSelectableRole user =
(LoggedUserWithSelectableRole) SecurityContextHolder.getContext().
getAuthentication().getPrincipal();
user.setCurrentAuthority(user.getAllAuthorities()[role]);
return "redirect:/";
}


Unfortunately this is not sufficient, as the authorities used by Spring Security for checking the user authorization are not (usually) stored in the principal object, but it the Authentication object.

It would be nice to write something like:


/* WARNING: The method setAuthorities doesn't exist */
SecurityContextHolder.getContext().getAuthentication().
setAuthorities(user.getAuthorities());


But the Authentication token is mostly immutable, so the setAuthorities doesn't exist. Worst, in the AbtractAuthenticationToken class, the base class of most of the token implementations, the authorities attribute is private, so you can't easily implement by yourself an alternative token implementation extending the original token class.

In our application we are using CAS. The only solution I found (as far as I know...please send me a line if you see a better solution) was to extend the CasAuthenticationToken, provinding a costructor for coping an existing token (of course of the same type):


public class UpdatableCasAuthenticationToken extends CasAuthenticationToken {

private final int keyHash;

public UpdatableCasAuthenticationToken(CasAuthenticationToken token, GrantedAuthority[] authorities) {
super("BOH", token.getPrincipal(), token.getCredentials(), authorities, token.getUserDetails(), token.getAssertion());
this.keyHash = token.getKeyHash();
}

@Override
public int getKeyHash() {
return this.keyHash;
}

}


As you can see, we also need to hide attributes and override methods not modifiable through the constructor of the base class.

Now I can substitute the original token with the modified one:


@RequestMapping
public String selectRole(@RequestParam(value = "role") int role) {
LoggedUserWithSelectableRole user =
(LoggedUserWithSelectableRole) SecurityContextHolder.getContext().
getAuthentication().getPrincipal();
user.setCurrentAuthority(user.getAllAuthorities()[role]);
SecurityContextHolder.getContext().setAuthentication(
new UpdatableCasAuthenticationToken(
(CasAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(),
user.getAuthorities()));
return "redirect:/";
}


As CAS ha no concerns with the user roles, I think CasAuthenticationToken should provide a way for updating authorities, and maybe the authorites attribute of AbstractAuthenticationToken should be declared as protected.

Thursday, December 11, 2008

Using Tiles 2 in a Prancoe project

First of all add the Tiles dependency to your pom.xml:


<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-core</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-jsp</artifactId>
<version>2.1.0</version>
</dependency>


Specify the places of the Tiles configurations and the Tiles view resolver in your parancoe-servlet.xml file:


<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles/defs/default.xml</value>
</list>
</property>
</bean>

<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
<!-- Default values for prefix and suffix
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
-->
</bean>


Add the /WEB-INF/tiles/defs/default.xml file with the definitions of your views:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
"http://tiles.apache.org/dtds/tiles-config_2_0.dtd">
<tiles-definitions>

<definition name="template.main" template="/WEB-INF/tiles/templates/main.jsp">
<put-attribute name="header" value="/WEB-INF/jsp/header.jsp"/>
<put-attribute name="menu" value="/WEB-INF/jsp/menu.jsp"/>
<put-attribute name="footer" value="/WEB-INF/jsp/footer.jsp"/>
</definition>

<definition name="login" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/login.jsp"/>
</definition>

<definition name="admin/conf" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/admin/conf.jsp"/>
</definition>

<definition name="admin/index" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/admin/index.jsp"/>
</definition>

<definition name="admin/logs" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/admin/logs.jsp"/>
</definition>

<definition name="admin/spring" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/admin/spring.jsp"/>
</definition>

<definition name="admin/users/list" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/admin/users/list.jsp"/>
</definition>

<definition name="admin/users/edit" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/admin/users/edit.jsp"/>
</definition>

<definition name="message" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/message.jsp"/>
</definition>

<definition name="genericError" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/genericError.jsp"/>
</definition>

<definition name="accessDenied" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/accessDenied.jsp"/>
</definition>

<definition name="welcome" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/welcome.jsp"/>
</definition>

<definition name="404" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/404.jsp"/>
</definition>

<definition name="500" extends="template.main">
<put-attribute name="main" value="/WEB-INF/jsp/500.jsp"/>
</definition>

</tiles-definitions>


Add the template in /WEB-INF/tiles/templates/main.jsp:


<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<%@ include file="/WEB-INF/jsp/common.jspf" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<%@ include file="/WEB-INF/jsp/head.jspf" %>
</head>
<body>
<div id="nonFooter">
<tiles:insertAttribute name="header"/>
<div id="content">
<div id="content_main">
<tiles:insertAttribute name="main"/>
</div>
<tiles:insertAttribute name="menu"/>
</div>
</div>
<tiles:insertAttribute name="footer"/>
</body>
</html>


Now You can simplify your JSP pages, removing from them all the layout-related parts. For example the welcome page (/WEB-INF/jsp/welcome.jsp) was:


<%@ include file="common.jspf" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<%@ include file="head.jspf" %>
</head>
<body>
<div id="nonFooter">
<jsp:include page="header.jsp"/>
<div id="content">
<div id="content_main">
<c:choose>
<c:when test="${requestScope.lang eq 'it'}">
Questa è l'applicazione template per il framework
<a href="http://wwww.parancoe.org">Parancoe</a>.<br />
<br />
Da questo punto di partenza puoi iniziare a costruire la
tua nuova applicazione, sfruttando tutti i benefici che
derivano dall'uso di Parancoe.<br />
<br />
Per maggiori informazioni visita il sito di Parancoe:<br />
<br />
<a href="http://wwww.parancoe.org">http://wwww.parancoe.org</a>.<br />
</c:when>
<c:otherwise>
This is the template application of the
<a href="http://wwww.parancoe.org">Parancoe</a> framework.<br />
<br />
From this starting point you can build your own application,
with all benefits of the using of the Parancoe Framework.<br />
<br />
For more infos, visit the Parancoe framework Web site:<br />
<br />
<a href="http://wwww.parancoe.org">http://wwww.parancoe.org</a>.<br />
</c:otherwise>
</c:choose>
</div>
<jsp:include page="menu.jsp"/>
</div>
</div>
<jsp:include page="footer.jsp"/>
</body>
</html>


Now it's just:


<%@ include file="/WEB-INF/jsp/common.jspf" %>
<c:choose>
<c:when test="${requestScope.lang eq 'it'}">
Questa è l'applicazione template per il framework
<a href="http://wwww.parancoe.org">Parancoe</a>.<br />
<br />
Da questo punto di partenza puoi iniziare a costruire la
tua nuova applicazione, sfruttando tutti i benefici che
derivano dall'uso di Parancoe.<br />
<br />
Per maggiori informazioni visita il sito di Parancoe:<br />
<br />
<a href="http://wwww.parancoe.org">http://wwww.parancoe.org</a>.<br />
</c:when>
<c:otherwise>
This is the template application of the
<a href="http://wwww.parancoe.org">Parancoe</a> framework.<br />
<br />
From this starting point you can build your own application,
with all benefits of the using of the Parancoe Framework.<br />
<br />
For more infos, visit the Parancoe framework Web site:<br />
<br />
<a href="http://wwww.parancoe.org">http://wwww.parancoe.org</a>.<br />
</c:otherwise>
</c:choose>

Thursday, July 19, 2007

Spring Meeting 2007

Periodo intenso di conferenze. L'ultima a cui ho partecipato è stata a Cagliari, Spring Framework Meeting 2007, organizzata dallo Spring Framework Italian User Group in collaborazione con il JUG Sardegna.

In quest'occasione ho presentato un seminario dal titolo "Parancoe: usare i DAO senza implementarli", in cui ho mostrato come con Parancoe, che usa pesantemente lo Springframework, assieme ad Hibernate, si possa realizzare in pochissimo tempo e con pochissimo sforzo il layer di persistenza di una propria applicazione.

Qui le trasparenze della mia presentazione: http://snipurl.com/sm2007

Al meeting, in veste di speaker, erano presenti altri due membri del JUG Padova: Paolo Donà ed Enrico Giurin. Il primo ha svolto una presentazione su due caratteristiche recenti di Parancoe che ha sviluppato, ispirandosi a Ruby e RoR: fixtures, per il caricamento di dati nel DB durante i test e in installazione, e plugin, per aggiungere semplicemente funzionalità alle applicazioni sviluppate con Parancoe. Enrico invece ha descritto le funzionalità di ACEGI e il modo in cui tale libreria viene usata nel Plugin Security di Parancoe.

Il meeting, forse anche a causa del bel tempo che ha invogliato molti ad andare al mare, a sofferto di alcuni problemi organizzativi. Alcuni suggerimenti per gli organizzatori, per il prossimo anno:
  • pensare ad una giornata meno vacanziera
  • indicare meglio la sede, in modo che riesca a trovarla anche chi non è del posto
  • distribuire ai partecipanti un volantino con il programma (orario) della giornata, e magari le descrizioni dei vari seminari
  • ridurre i tempi di pausa
  • se i partecipanti previsti non sono molti, com'è stato, usare un'unica sala
  • pensare ad un "cane da guardia" per i relatori, in modo che inizino puntuali e non sforino (io, ad esempio, credo di avere abbondantemente sforato...ma in realtà non lo so, dato che non c'era nessuno che controllava il tempo, nemmeno io)
Ad ogni modo, un meeting in Sardegna vale la pena anche solo per la bellezza del luogo.
Purtroppo le mie foto sono pochissime e scattate con un telefonino. Decisamente meglio quelle di Paolo, oppure le foto di Massimiliano Dessì.

Spero che l'anno prossimo venga organizzato ancora, facendo tesoro dell'esperienza di quest'anno...e di riuscire di nuovo a parteciparvi. In bocca al lupo, e W la Sardegna!

PS: devo anche ringraziare Paolo che ci ha ospitato a casa sua, e la sua fantastica nonnina per i buonissimi carciofini sottolio. Un grazie anche a Massimiliano per il suo entusiasmo e per la volontà nell'organizzare questo evento.