Absolutely -- Here are the classes I created for our initial integration of Acegi into the Spring Portlet MVC framework.
The first is simply a class to represent a user who is already logged into the portal -- it gets the user name and other info out of the portlet request:
Here is the AuthenticationProvider that automatically trusts the credentials if they came from a portal:Code:package com.arcanumintl.portlet.acegi; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import javax.portlet.PortletRequest; public class PortalUser implements Serializable { private static final long serialVersionUID = 2L; private Map userInfo; private static final String USER_NAME = "user.name"; private static final String USER_NAME_FULL = "user.name.full"; private static final String USER_EMAIL = "user.email"; private static final String USER_ORGANIZATION = "user.organization"; private static final String USER_TIMEZONE = "user.timezone"; private static final String USER_LOCALE = "user.locale"; public PortalUser(PortletRequest request) { super(); this.userInfo = (Map)request.getAttribute(PortletRequest.USER_INFO); if (this.userInfo == null) this.userInfo = new HashMap(); } public String getUserName() { return (String)userInfo.get(USER_NAME); } public String getUserNameFull() { return (String)userInfo.get(USER_NAME_FULL); } public String getUserEmail() { return (String)userInfo.get(USER_EMAIL); } public String getUserOrganization() { return (String)userInfo.get(USER_ORGANIZATION); } public String getUserTimezone() { return (String)userInfo.get(USER_TIMEZONE); } public String getUserLocale() { return (String)userInfo.get(USER_LOCALE); } public String toString() { return getUserName(); } }
Here is the Portlet version of the SessionContextIntegrationInterceptor:Code:package com.arcanumintl.portlet.acegi; import net.sf.acegisecurity.Authentication; import net.sf.acegisecurity.UserDetails; import net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider; public class PortalAuthenticationProvider extends DaoAuthenticationProvider { /* (non-Javadoc) * @see net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider#isPasswordCorrect(net.sf.acegisecurity.Authentication, net.sf.acegisecurity.UserDetails) */ protected boolean isPasswordCorrect(Authentication authentication, UserDetails user) { if (authentication.getCredentials() instanceof PortalUser) { PortalUser portalUser = (PortalUser)authentication.getCredentials(); return (user.getUsername().equals(portalUser.getUserName())); } return super.isPasswordCorrect(authentication, user); } }
Here is an interface that all our controllers implement that allows us to specify the required roles for executing the controller. I don't love this part of our current configuration, but it works for now.Code:package com.arcanumintl.portlet.acegi; import javax.portlet.PortletException; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import javax.portlet.PortletSession; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.web.portlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import net.sf.acegisecurity.context.Context; import net.sf.acegisecurity.context.ContextHolder; import net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter; public class PortletSessionContextIntegrationInterceptor implements InitializingBean, HandlerInterceptor { //~ Static fields/initializers ============================================= protected static final Log logger = LogFactory.getLog(PortletSessionContextIntegrationInterceptor.class); public static final String ACEGI_SECURITY_CONTEXT_KEY = HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY; private static final String SESSION_EXISTED = PortletSessionContextIntegrationInterceptor.class.getName() + ".SESSION_EXISTED"; //~ Instance fields ======================================================== private Class context; private Object contextObject; /** * Indicates if this interceptor can create a <code>PortletSession</code> if needed * (sessions are always created sparingly, but setting this value to false * will prohibit sessions from ever being created). Defaults to true. */ private boolean allowSessionCreation = true; //~ Methods ================================================================ public void setAllowSessionCreation(boolean allowSessionCreation) { this.allowSessionCreation = allowSessionCreation; } public boolean isAllowSessionCreation() { return allowSessionCreation; } public void setContext(Class secureContext) { this.context = secureContext; } public Class getContext() { return context; } public void afterPropertiesSet() throws Exception { if ((this.context == null) || (!Context.class.isAssignableFrom(this.context))) { throw new IllegalArgumentException( "context must be defined and implement Context (typically use net.sf.acegisecurity.context.security.SecureContextImpl)"); } this.contextObject = generateNewContext(); } /* (non-Javadoc) * @see org.springframework.web.portlet.HandlerInterceptor#preHandle(javax.portlet.PortletRequest, javax.portlet.PortletResponse, java.lang.Object) */ public boolean preHandle(PortletRequest request, PortletResponse response, Object handler) throws Exception { if (ContextHolder.getContext() != null) { if (logger.isWarnEnabled()) { logger.warn( "ContextHolder should have been null but contained: '" + ContextHolder.getContext() + "'; setting to null now"); } ContextHolder.setContext(null); } PortletSession portletSession = null; boolean portletSessionExistedAtStartOfRequest = false; try { portletSession = request.getPortletSession(false); } catch (IllegalStateException ignored) {} if (portletSession != null) { portletSessionExistedAtStartOfRequest = true; Object contextObject = portletSession.getAttribute(ACEGI_SECURITY_CONTEXT_KEY, PortletSession.APPLICATION_SCOPE); if (contextObject != null) { if (contextObject instanceof Context) { if (logger.isDebugEnabled()) { logger.debug( "Obtained from ACEGI_SECURITY_CONTEXT a valid Context and set to ContextHolder: '" + contextObject + "'"); } ContextHolder.setContext((Context) contextObject); } else { if (logger.isWarnEnabled()) { logger.warn( "ACEGI_SECURITY_CONTEXT did not contain a Context but contained: '" + contextObject + "'; are you improperly modifying the PortletSession directly (you should always use ContextHolder) or using the PortletSession attribute reserved for this class?"); } } } else { if (logger.isDebugEnabled()) { logger.debug( "PortletSession returned null object for ACEGI_SECURITY_CONTEXT"); } } } else { if (logger.isDebugEnabled()) { logger.debug("No PortletSession currently exists"); } } if (ContextHolder.getContext() == null) { ContextHolder.setContext(generateNewContext()); if (logger.isDebugEnabled()) { logger.debug( "As ContextHolder null, setup ContextHolder with a fresh new instance: '" + ContextHolder.getContext() + "'"); } } request.setAttribute(SESSION_EXISTED, new Boolean(portletSessionExistedAtStartOfRequest)); return true; } /* (non-Javadoc) * @see org.springframework.web.portlet.HandlerInterceptor#postHandle(javax.portlet.RenderRequest, javax.portlet.RenderResponse, java.lang.Object, org.springframework.web.servlet.ModelAndView) */ public void postHandle(RenderRequest request, RenderResponse response, Object handler, ModelAndView modelAndView) throws Exception { } /* (non-Javadoc) * @see org.springframework.web.portlet.HandlerInterceptor#afterCompletion(javax.portlet.PortletRequest, javax.portlet.PortletResponse, java.lang.Object, java.lang.Exception) */ public void afterCompletion(PortletRequest request, PortletResponse response, Object handler, Exception ex) throws Exception { PortletSession portletSession = null; boolean portletSessionExistedAtStartOfRequest = ((Boolean)request.getAttribute(SESSION_EXISTED)).booleanValue(); // Store context back to PortletSession try { portletSession = request.getPortletSession(false); } catch (IllegalStateException ignored) {} if ((portletSession == null) && portletSessionExistedAtStartOfRequest) { if (logger.isDebugEnabled()) { logger.debug( "PortletSession is now null, but was not null at start of request; session was invalidated, so do not create a new session"); } } // Generate a PortletSession only if we need to if ((portletSession == null) && !portletSessionExistedAtStartOfRequest) { if (!allowSessionCreation) { if (logger.isDebugEnabled()) { logger.debug( "Whilst ContextHolder contents have changed, the PortletSessionContextIntegrationInterceptor is prohibited from creating a PortletSession by the allowSessionCreation property being false"); } } else if (!contextObject.equals(ContextHolder.getContext())) { if (logger.isDebugEnabled()) { logger.debug( "PortletSession being created as ContextHolder contents are non-default"); } try { portletSession = request.getPortletSession(true); } catch (IllegalStateException ignored) {} } else { if (logger.isDebugEnabled()) { logger.debug( "PortletSession still null, but ContextHolder has not changed from default: ' " + ContextHolder.getContext() + "'; not creating PortletSession or storing ContextHolder contents"); } } } // If PortletSession exists, store current ContextHolder contents if (portletSession != null) { portletSession.setAttribute(ACEGI_SECURITY_CONTEXT_KEY, ContextHolder.getContext(), PortletSession.APPLICATION_SCOPE); if (logger.isDebugEnabled()) { logger.debug("Context stored to PortletSession: '" + ContextHolder.getContext() + "'"); } } // Remove ContextHolder contents ContextHolder.setContext(null); if (logger.isDebugEnabled()) { logger.debug( "ContextHolder set to null as request processing completed"); } } public Context generateNewContext() throws PortletException { try { return (Context) this.context.newInstance(); } catch (InstantiationException ie) { throw new PortletException(ie); } catch (IllegalAccessException iae) { throw new PortletException(iae); } } }
Finally, here is the SecurityEnforcementInterceptor that enforces the definitions defined by the SecurePortletController interface. This can easily be modified to support some other way to control the list of required roles.Code:package com.arcanumintl.portlet.acegi; import java.util.Set; public interface SecurePortletController extends org.springframework.web.portlet.mvc.PortletController { public void setRequiredAuthorities(String requiredAuthorities); public Set getRequiredAuthoritiesSet(); }
Code:package com.arcanumintl.portlet.acegi; import java.util.Collection; import java.util.Arrays; import java.util.Set; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import javax.portlet.PortletSecurityException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import net.sf.acegisecurity.Authentication; import net.sf.acegisecurity.AuthenticationException; import net.sf.acegisecurity.AuthenticationManager; import net.sf.acegisecurity.context.Context; import net.sf.acegisecurity.context.ContextHolder; import net.sf.acegisecurity.context.security.SecureContext; import net.sf.acegisecurity.context.security.SecureContextImpl; import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.web.portlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class SecurityEnforcementInterceptor implements HandlerInterceptor, InitializingBean { private static final Log logger = LogFactory.getLog(SecurityEnforcementInterceptor.class); private AuthenticationManager authenticationManager; private Class secureContext = SecureContextImpl.class; public void afterPropertiesSet() throws Exception { if (this.authenticationManager == null) throw new IllegalArgumentException("An AuthenticationManager is required"); } //TODO: Covert this (and supporting classes) to support ObjectDefinitionSource // and AbstractSecurityInterceptor -- would allow use of voters public boolean preHandle(PortletRequest request, PortletResponse response, Object handler) throws Exception { // See if the controller is a secure controller if (!(handler instanceof SecurePortletController)) return true; // Get the required Authorities list for this controller SecurePortletController secureHandler = (SecurePortletController)handler; Set requiredAuthorities = secureHandler.getRequiredAuthoritiesSet(); if (logger.isDebugEnabled()) logger.debug("Roles Required: " + requiredAuthorities); if (requiredAuthorities != null && !requiredAuthorities.isEmpty()) { // Create Authentication Request PortalUser portalUser = new PortalUser(request); if (portalUser.getUserName() == null) throw new PortletSecurityException("No Portal User Defined"); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(portalUser.getUserName(), portalUser); // Check Authentication Authentication authResult; try { authResult = authenticationManager.authenticate(authRequest); } catch (AuthenticationException failed) { // Authentication Failed if (logger.isDebugEnabled()) logger.debug("Authentication request for user: " + portalUser.getUserName() + " failed: " + failed.toString()); throw new PortletSecurityException("User Authentication Failed", failed); } authResult.setAuthenticated(true); // Check Authorization Collection grantedAuthorities = Arrays.asList(authResult.getAuthorities()); if (!grantedAuthorities.containsAll(requiredAuthorities)) { // Authorization Failed if (logger.isDebugEnabled()) logger.debug("Authorization request for user: " + portalUser.getUserName() + " failed." + "Required Authorities:" + requiredAuthorities + " Granted Authorities:" + grantedAuthorities); throw new PortletSecurityException("User Not Authorized for This Action"); } // Authentication & Authorization success SecureContext secureContextObject = (SecureContext)secureContext.newInstance(); secureContextObject.setAuthentication(authResult); ContextHolder.setContext(secureContextObject); if (logger.isDebugEnabled()) logger.debug("Authentication success: " + authResult.toString()); } else { if (logger.isDebugEnabled()) logger.debug("Public request - authentication not attempted"); // Set Authentication object (if it exists) to be unauthenticated if ((ContextHolder.getContext() != null) && ContextHolder.getContext() instanceof SecureContext) { SecureContext context = (SecureContext) ContextHolder.getContext(); if (context.getAuthentication() != null) { if (logger.isDebugEnabled()) logger.debug("Authentication object detected and tagged as unauthenticated"); Authentication authenticated = context.getAuthentication(); authenticated.setAuthenticated(false); context.setAuthentication(authenticated); ContextHolder.setContext((Context)context); } } } return true; } public void postHandle(RenderRequest request, RenderResponse response, Object handler, ModelAndView modelAndView) throws Exception { } public void afterCompletion(PortletRequest request, PortletResponse response, Object handler, Exception ex) throws Exception { } public void setAuthenticationManager(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; } public void setSecureContext(Class secureContext) { this.secureContext = secureContext; } }


. Again, you have done us a service, thanks.
