Dear Wiki user, You have subscribed to a wiki page or wiki category on "Tapestry Wiki" for change notification.
The following page has been changed by UlrichStaerk: http://wiki.apache.org/tapestry/Tapestry5HowToSpringSecurityAndOpenId The comment on the change is: intermittent save, have to go to lunch ;-) New page: [[TableOfContents]] = Motivation = OpenID is a service providing single-sign-on features. At our institute we are operating an OpenID provider that delegates authentication to our ActiveDirectory where all our students are registered. For a course planning system I was assessing Tapestrys integration possibilities with this OpenID provider and found out, that the tapestry-spring-security project already has much of the stuff needed for integration with our provider on board. This is a description of what you have to do in order to have OpenID authentication in your Tapestry apps. = Background = Spring Security already comes with everything needed for authenticating users against an OpenID provider (although this is somewhat incomplete regarding additional attributes that can be provided by the OpenID provider). But the tapestry-spring-security lacks the configuration needed to use this out of the box. In particular it lacks the filter configuration for the OpenIDAuthenticationProcessingFilter that processes incoming OpenID authentications. In a "normal" setup this is just another Filter that you configure in your web.xml. In a Tapestry setup though, Tapestry receives all incoming requests and processes them in the HttpServletRequestHandler pipeline. Fortunately this pipeline can be extended by contributing HttpServletRequestFilters. So all we need to do is wrap the OpenIDAuthenticationProcessingFilter and contribute it to the HttpServletRequestHandler pipeline. Such a wrapper is provided by the tapestry-spring-security project. With that in place we can process incoming OpenID authentication requests. Next, we have to look up the authenticated user in our local database where we store the roles assigned to a user. For this we have to implement a UserDetailsService which retrieves a user and his roles from some data store. The user object that is returned has to implement the UserDetails interface and the roles he holds have to implement the GrantedAuthority interface. If you don't want to grant access to some OpenID authenticated user, you have to do that in the UserDetailsService. The implementation I will show you grants accesss to EVERY OpenID authenticated user so don't use this in a "real" environment. = Requirements = tapestry-hibernate and tapestry-spring-security have to be in your pom.xml (or their jars and all of their dependencies have to be in your classpath). Configuring tapestry-hibernate is outside the scope of this article, please consult it's own documentation. = Implementation = Let's begin with the User and Role objects that implement the UserDetails and GrantedAuthority interfaces, respectively. These are used to store users and their roles in a local database using Hibernate. The User is a minimalistic implementation that only stores an id and a username. {{{ package org.mygroup.myapp.entities; import java.util.HashSet; import java.util.Set; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.Transient; import org.springframework.security.GrantedAuthority; import org.springframework.security.GrantedAuthorityImpl; import org.springframework.security.userdetails.UserDetails; /** * A minimalistic UserDetails implementation providing a username only. Storing * a password is not necessary since the OpenID provider will do the authentication. * * @author Ulrich Stärk */ @Entity public class User implements UserDetails { private static final long serialVersionUID = 4068206679084877888L; private int id; private String username; private Set<Role> roles; @Id @GeneratedValue(strategy = GenerationType.AUTO) public int getId() { return id; } public void setId(int id) { this.id = id; } @ManyToMany public Set<Role> getRoles() { return roles; } public void setRoles(Set<Role> roles) { this.roles = roles; } @Transient public GrantedAuthority[] getAuthorities() { Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>(); for (Role role : getRoles()) { authorities.add(new GrantedAuthorityImpl(role.getAuthority())); } return authorities.toArray(new GrantedAuthority[authorities.size()]); } @Transient public String getPassword() { return "notused"; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Transient public boolean isAccountNonExpired() { return true; } @Transient public boolean isAccountNonLocked() { return true; } @Transient public boolean isCredentialsNonExpired() { return true; } @Transient public boolean isEnabled() { return true; } } }}} {{{ package org.mygroup.myapp.entities; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import org.springframework.security.GrantedAuthority; /** * A GrantedAuthority (aka Role) implementation. hashCode, equals and compareTo are * a bit messy (auto-generated), you might want to change this. * * @author Ulrich Stärk */ @Entity public class Role implements GrantedAuthority { private static final long serialVersionUID = -117212611936641518L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String authority; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAuthority() { return authority; } public void setAuthority(String authority) { this.authority = authority; } @Override public int hashCode() { return authority.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (obj.getClass() == String.class) return obj.equals(authority); if (getClass() != obj.getClass()) return false; final Role other = (Role) obj; if (authority == null) { if (other.authority != null) return false; } else if (!authority.equals(other.authority)) return false; return true; } public int compareTo(Object o) { if (this == o) return 0; if (o == null) return -1; if (o.getClass() == String.class) return authority.compareTo((String) o); if (getClass() != o.getClass()) return -1; final Role other = (Role) o; if (authority == null) { if (other.authority != null) return 1; } else return authority.compareTo(other.authority); return -1; } } }}} Next, we need a UserDetailsService implementation, that finds users and their roles in our local database: {{{ package org.mygroup.myapp.services.impl; import java.util.HashSet; import org.apache.tapestry5.hibernate.HibernateSessionManager; import org.hibernate.Session; import org.hibernate.criterion.Restrictions; import org.slf4j.Logger; import org.springframework.dao.DataAccessException; import org.springframework.security.userdetails.UserDetails; import org.springframework.security.userdetails.UserDetailsService; import org.springframework.security.userdetails.UsernameNotFoundException; import de.spielviel.mailadmin.entities.Role; import de.spielviel.mailadmin.entities.User; /** * @author Ulrich Stärk */ public class UserDetailsServiceImpl implements UserDetailsService { private HibernateSessionManager sessionManager; private Logger logger; public UserDetailsServiceImpl(HibernateSessionManager sessionManager, Logger logger) { this.sessionManager = sessionManager; this.logger = logger; } /** * Try to find the given user in the local database. Since we are using OpenID that user * might not exist in our database yet. If it doesn't, create a new user and store it. * * WARNING: This implementation will permit EVERY OpenID authenticated user to log in. In * a real environment you want to restrict this to trusted OpenID providers or you have * to restrict those users to non-sensible information (by means of roles). */ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { logger.debug("trying to find user " + username); Session session = sessionManager.getSession(); User u = (User) session.createCriteria(User.class).add( Restrictions.eq("username", username)).uniqueResult(); if (u == null) { logger.debug("user not found, creating"); Role r = (Role) session.createCriteria(Role.class).add( Restrictions.eq("authority", "ROLE_USER")).uniqueResult(); if (r == null) { logger.debug("role not found, creating"); r = new Role(); r.setAuthority("ROLE_USER"); session.saveOrUpdate(r); } u = new User(); u.setUsername(username); u.setRoles(new HashSet<Role>()); u.getRoles().add(r); session.saveOrUpdate(u); } logger.debug("returning user " + u.getUsername()); sessionManager.commit(); return u; } } }}} That's it. The rest is setting this all up in your AppModule. = Tying it together = --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
