diff --git a/sandbox/portlet/pom.xml b/sandbox/portlet/pom.xml new file mode 100644 index 0000000000..801b2402e7 --- /dev/null +++ b/sandbox/portlet/pom.xml @@ -0,0 +1,70 @@ + + 4.0.0 + + org.acegisecurity + acegi-security-sandbox + 1.0.4-SNAPSHOT + + acegi-security-portlet + Acegi Security System for Spring - Portlet support + Acegi Security System for Spring - Support for JSR-168 Portlets + 0.1-SNAPSHOT + + + + AcegiMaven + Acegi 3rd party repository + http://acegisecurity.sourceforge.net/maven + + true + + + + + + scm:svn:https://svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity/sandbox/portlet + + + scm:svn:https://svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity/sandbox/portlet + + http://svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/sandbox/portlet/ + + + + + javax.servlet + servlet-api + 2.4 + provided + + + javax.portlet + portlet-api + 1.0 + provided + + + org.springframework + spring-dao + 2.0.4 + + + org.springframework + spring-portlet + 2.0.4 + + + org.springframework + spring-mock + 2.0.4 + true + + + net.sf.ehcache + ehcache + 1.2.4 + + + + diff --git a/sandbox/portlet/src/main/java/org/acegisecurity/context/PortletSessionContextIntegrationInterceptor.java b/sandbox/portlet/src/main/java/org/acegisecurity/context/PortletSessionContextIntegrationInterceptor.java new file mode 100644 index 0000000000..b2ea1008d5 --- /dev/null +++ b/sandbox/portlet/src/main/java/org/acegisecurity/context/PortletSessionContextIntegrationInterceptor.java @@ -0,0 +1,446 @@ +/* + * Copyright 2005-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.context; + +import java.lang.reflect.Method; + +import javax.portlet.ActionRequest; +import javax.portlet.ActionResponse; +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.util.Assert; +import org.springframework.util.ReflectionUtils; +import org.springframework.web.portlet.HandlerInterceptor; +import org.springframework.web.portlet.ModelAndView; + +/** + *

This interceptor populates the {@link SecurityContextHolder} with information obtained from the + * PortletSession. It is applied to both ActionRequests and + * RenderRequests

+ * + *

The PortletSession will be queried to retrieve the SecurityContext that should + * be stored against the SecurityContextHolder for the duration of the portlet request. At the + * end of the request, any updates made to the SecurityContextHolder will be persisted back to the + * PortletSession by this interceptor.

+ * + *

If a valid SecurityContext cannot be obtained from the PortletSession for + * whatever reason, a fresh SecurityContext will be created and used instead. The created object + * will be of the instance defined by the {@link #setContext(Class)} method (which defaults to + * {@link org.acegisecurity.context.SecurityContextImpl}.

+ * + *

A PortletSession may be created by this interceptor if one does not already exist. If at the + * end of the portlet request the PortletSession does not exist, one will only be created if + * the current contents of the SecurityContextHolder are not the {@link java.lang.Object#equals} + * to a new instance of {@link #context}. This avoids needless PortletSession creation, + * and automates the storage of changes made to the SecurityContextHolder. There is one exception to + * this rule, that is if the {@link #forceEagerSessionCreation} property is true, in which case + * sessions will always be created irrespective of normal session-minimization logic (the default is + * false, as this is resource intensive and not recommended).

+ * + *

If for whatever reason no PortletSession should ever be created, the + * {@link #allowSessionCreation} property should be set to false. Only do this if you really need + * to conserve server memory and ensure all classes using the SecurityContextHolder are designed to + * have no persistence of the SecurityContext between web requests. Please note that if + * {@link #forceEagerSessionCreation} is true, the allowSessionCreation must also be + * true (setting it to false will cause a startup-time error).

+ + *

This interceptor must be executed before

any authentication processing mechanisms. These + * mechanisms (specifically {@link org.acegisecurity.ui.portlet.PortletProcessingInterceptor}) expect the + * SecurityContextHolder to contain a valid SecurityContext by the time they execute.

+ * + *

An important nuance to this interceptor is that (by default) the SecurityContext is stored + * into the APPLICATION_SCOPE of the PortletSession. This doesn't just mean you will be + * sharing it with all the other portlets in your webapp (which is generally a good idea). It also means that (if + * you have done all the other appropriate magic), you will share this SecurityContext with servlets in + * your webapp. This is very useful if you have servlets serving images or processing AJAX calls from your portlets + * since they can now use the {@link HttpSessionContextIntegrationFilter} to access the same SecurityContext + * object from the session. This allows these calls to be secured as well as the portlet calls.

+ * + * Much of the logic of this interceptor comes from the {@link HttpSessionContextIntegrationFilter} class which + * fills the same purpose on the servlet side. Ben Alex and Patrick Burlson are listed as authors here because they + * are the authors of that class and there are blocks of code that essentially identical between the two. (Making this + * a good candidate for refactoring someday.) + * + *

Unlike HttpSessionContextIntegrationFilter, this interceptor does not check to see if it is + * getting applied multiple times. This shouldn't be a problem since the application of interceptors is under the + * control of the Spring Portlet MVC framework and tends to be more explicit and more predictable than the application + * of filters. However, you should still be careful to only apply this inteceptor to your request once.

+ * + * @author John A. Lewis + * @author Ben Alex + * @author Patrick Burleson + * @since 2.0 + * @version $Id$ + */ +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"; + private static final String CONTEXT_HASHCODE = PortletSessionContextIntegrationInterceptor.class.getName() + ".CONTEXT_HASHCODE"; + + //~ Instance fields ================================================================================================ + + private Class context = SecurityContextImpl.class; + + private Object contextObject; + + /** + * Indicates if this interceptor can create a PortletSession if + * needed (sessions are always created sparingly, but setting this value to + * false will prohibit sessions from ever being created). + * Defaults to true. Do not set to false if + * you are have set {@link #forceEagerSessionCreation} to true, + * as the properties would be in conflict. + */ + private boolean allowSessionCreation = true; + + /** + * Indicates if this interceptor is required to create a PortletSession + * for every request before proceeding through the request process, even if the + * PortletSession would not ordinarily have been created. By + * default this is false, which is entirely appropriate for + * most circumstances as you do not want a PortletSession + * created unless the interceptor actually needs one. It is envisaged the main + * situation in which this property would be set to true is + * if using other interceptors that depend on a PortletSession + * already existing. This is only required in specialized cases, so leave it set to + * false unless you have an actual requirement and aware of the + * session creation overhead. + */ + private boolean forceEagerSessionCreation = false; + + /** + * Indicates whether the SecurityContext will be cloned from + * the PortletSession. The default is to simply reference + * (the default is false). The default may cause issues if + * concurrent threads need to have a different security identity from other + * threads being concurrently processed that share the same + * PortletSession. In most normal environments this does not + * represent an issue, as changes to the security identity in one thread is + * allowed to affect the security identity in other threads associated with + * the same PortletSession. For unusual cases where this is not + * permitted, change this value to true and ensure the + * {@link #context} is set to a SecurityContext that + * implements {@link Cloneable} and overrides the clone() + * method. + */ + private boolean cloneFromPortletSession = false; + + /** + * Indicates wether the APPLICATION_SCOPE mode of the + * PortletSession should be used for storing the + * SecurityContext. The default is
true
. + * This allows it to be shared between the portlets in the webapp and + * potentially with servlets in the webapp as well. If this is set to + * false, then the PORTLET_SCOPE will be used + * instead. + */ + private boolean useApplicationScopePortletSession = true; + + + //~ Constructors =================================================================================================== + + public PortletSessionContextIntegrationInterceptor() throws PortletException { + this.contextObject = generateNewContext(); + } + + //~ Methods ======================================================================================================== + + public void afterPropertiesSet() throws Exception { + + // check that the value of context is legal + if ((this.context == null) || (!SecurityContext.class.isAssignableFrom(this.context))) { + throw new IllegalArgumentException("context must be defined and implement SecurityContext " + + "(typically use org.acegisecurity.context.SecurityContextImpl; existing class is " + + this.context + ")"); + } + + // check that session creation options make sense + if ((forceEagerSessionCreation == true) && (allowSessionCreation == false)) { + throw new IllegalArgumentException( + "If using forceEagerSessionCreation, you must set allowSessionCreation to also be true"); + } + } + + public boolean preHandleAction(ActionRequest request, ActionResponse response, + Object handler) throws Exception { + // call to common preHandle method + return preHandle(request, response, handler); + } + + public boolean preHandleRender(RenderRequest request, RenderResponse response, + Object handler) throws Exception { + // call to common preHandle method + return preHandle(request, response, handler); + } + + public void postHandleRender(RenderRequest request, RenderResponse response, + Object handler, ModelAndView modelAndView) throws Exception { + // no-op + } + + public void afterActionCompletion(ActionRequest request, ActionResponse response, + Object handler, Exception ex) throws Exception { + // call to common afterCompletion method + afterCompletion(request, response, handler, ex); + } + + public void afterRenderCompletion(RenderRequest request, RenderResponse response, + Object handler, Exception ex) throws Exception { + // call to common afterCompletion method + afterCompletion(request, response, handler, ex); + } + + + private boolean preHandle(PortletRequest request, PortletResponse response, + Object handler) throws Exception { + + // make sure the holder is clear + if (SecurityContextHolder.getContext() != null) { + if (logger.isWarnEnabled()) + logger.warn("SecurityContextHolder should have been null but contained: '" + + SecurityContextHolder.getContext() + "'; setting to null now"); + SecurityContextHolder.clearContext(); + } + + PortletSession portletSession = null; + boolean portletSessionExistedAtStartOfRequest = false; + + // see if the portlet session already exists (or should be eagerly created) + try { + portletSession = request.getPortletSession(forceEagerSessionCreation); + } catch (IllegalStateException ignored) {} + + // if there is a session, then see if there is a context to bring in + if (portletSession != null) { + + // remember that the session already existed + portletSessionExistedAtStartOfRequest = true; + + // attempt to retrieve the context from the session + Object contextFromSessionObject = portletSession.getAttribute(ACEGI_SECURITY_CONTEXT_KEY, portletSessionScope()); + + // if we got a context then place it into the holder + if (contextFromSessionObject != null) { + + // if we are supposed to clone it, then do so + if (cloneFromPortletSession) { + Assert.isInstanceOf(Cloneable.class, contextFromSessionObject, + "Context must implement Clonable and provide a Object.clone() method"); + try { + Method m = contextFromSessionObject.getClass().getMethod("clone", new Class[] {}); + if (!m.isAccessible()) { + m.setAccessible(true); + } + contextFromSessionObject = m.invoke(contextFromSessionObject, new Object[] {}); + } + catch (Exception ex) { + ReflectionUtils.handleReflectionException(ex); + } + } + + // if what we got is a valid context then place it into the holder, otherwise create a new one + if (contextFromSessionObject instanceof SecurityContext) { + if (logger.isDebugEnabled()) + logger.debug("Obtained from ACEGI_SECURITY_CONTEXT a valid SecurityContext and " + + "set to SecurityContextHolder: '" + contextFromSessionObject + "'"); + SecurityContextHolder.setContext((SecurityContext) contextFromSessionObject); + } else { + if (logger.isWarnEnabled()) + logger.warn("ACEGI_SECURITY_CONTEXT did not contain a SecurityContext but contained: '" + + contextFromSessionObject + + "'; are you improperly modifying the PortletSession directly " + + "(you should always use SecurityContextHolder) or using the PortletSession attribute " + + "reserved for this class? - new SecurityContext instance associated with " + + "SecurityContextHolder"); + SecurityContextHolder.setContext(generateNewContext()); + } + + } else { + + // there was no context in the session, so create a new context and put it in the holder + if (logger.isDebugEnabled()) + logger.debug("PortletSession returned null object for ACEGI_SECURITY_CONTEXT - new " + + "SecurityContext instance associated with SecurityContextHolder"); + SecurityContextHolder.setContext(generateNewContext()); + } + + } else { + + // there was no session, so create a new context and place it in the holder + if (logger.isDebugEnabled()) + logger.debug("No PortletSession currently exists - new SecurityContext instance " + + "associated with SecurityContextHolder"); + SecurityContextHolder.setContext(generateNewContext()); + + } + + // place attributes onto the request to remember if the session existed and the hashcode of the context + request.setAttribute(SESSION_EXISTED, new Boolean(portletSessionExistedAtStartOfRequest)); + request.setAttribute(CONTEXT_HASHCODE, new Integer(SecurityContextHolder.getContext().hashCode())); + + return true; + } + + private void afterCompletion(PortletRequest request, PortletResponse response, + Object handler, Exception ex) throws Exception { + + PortletSession portletSession = null; + + // retrieve the attributes that remember if the session existed and the hashcode of the context + boolean portletSessionExistedAtStartOfRequest = ((Boolean)request.getAttribute(SESSION_EXISTED)).booleanValue(); + int oldContextHashCode = ((Integer)request.getAttribute(CONTEXT_HASHCODE)).intValue(); + + // try to retrieve an existing portlet session + try { + portletSession = request.getPortletSession(false); + } catch (IllegalStateException ignored) {} + + // if there is now no session but there was one at the beginning then it must have been invalidated + 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"); + } + + // create a new portlet session if we need to + if ((portletSession == null) && !portletSessionExistedAtStartOfRequest) { + + // if we're not allowed to create a new session, then report that + if (!allowSessionCreation) { + if (logger.isDebugEnabled()) + logger.debug("The PortletSession is currently null, and the " + + "PortletSessionContextIntegrationInterceptor is prohibited from creating a PortletSession " + + "(because the allowSessionCreation property is false) - SecurityContext thus not " + + "stored for next request"); + } + // if the context was changed during the request, then go ahead and create a session + else if (!contextObject.equals(SecurityContextHolder.getContext())) { + if (logger.isDebugEnabled()) + logger.debug("PortletSession being created as SecurityContextHolder contents are non-default"); + try { + portletSession = request.getPortletSession(true); + } catch (IllegalStateException ignored) {} + } + // if nothing in the context changed, then don't bother to create a session + else { + if (logger.isDebugEnabled()) + logger.debug("PortletSession is null, but SecurityContextHolder has not changed from default: ' " + + SecurityContextHolder.getContext() + + "'; not creating PortletSession or storing SecurityContextHolder contents"); + } + } + + // if the session exists and the context has changes, then store the context back into the session + if ((portletSession != null) + && (SecurityContextHolder.getContext().hashCode() != oldContextHashCode)) { + portletSession.setAttribute(ACEGI_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext(), portletSessionScope()); + if (logger.isDebugEnabled()) + logger.debug("SecurityContext stored to PortletSession: '" + + SecurityContextHolder.getContext() + "'"); + } + + // remove the contents of the holder + SecurityContextHolder.clearContext(); + if (logger.isDebugEnabled()) + logger.debug("SecurityContextHolder set to new context, as request processing completed"); + + } + + + /** + * Creates a new SecurityContext object. The specific class is + * determined by the setting of the {@link #context} property. + * @return the new SecurityContext + * @throws PortletException if the creation throws an InstantiationException or + * an IllegalAccessException, then this method will wrap them in a + * PortletException + */ + public SecurityContext generateNewContext() throws PortletException { + try { + return (SecurityContext) this.context.newInstance(); + } catch (InstantiationException ie) { + throw new PortletException(ie); + } catch (IllegalAccessException iae) { + throw new PortletException(iae); + } + } + + + private int portletSessionScope() { + // return the appropriate scope setting based on our property value + return (this.useApplicationScopePortletSession ? + PortletSession.APPLICATION_SCOPE : PortletSession.PORTLET_SCOPE); + } + + + public Class getContext() { + return context; + } + + public void setContext(Class secureContext) { + this.context = secureContext; + } + + public boolean isAllowSessionCreation() { + return allowSessionCreation; + } + + public void setAllowSessionCreation(boolean allowSessionCreation) { + this.allowSessionCreation = allowSessionCreation; + } + + public boolean isForceEagerSessionCreation() { + return forceEagerSessionCreation; + } + + public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) { + this.forceEagerSessionCreation = forceEagerSessionCreation; + } + + public boolean isCloneFromPortletSession() { + return cloneFromPortletSession; + } + + public void setCloneFromPortletSession(boolean cloneFromPortletSession) { + this.cloneFromPortletSession = cloneFromPortletSession; + } + + public boolean isUseApplicationScopePortletSession() { + return useApplicationScopePortletSession; + } + + public void setUseApplicationScopePortletSession( + boolean useApplicationScopePortletSession) { + this.useApplicationScopePortletSession = useApplicationScopePortletSession; + } + +} diff --git a/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/PortletAuthenticationProvider.java b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/PortletAuthenticationProvider.java new file mode 100644 index 0000000000..6f8fa92148 --- /dev/null +++ b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/PortletAuthenticationProvider.java @@ -0,0 +1,190 @@ +/* + * Copyright 2005-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.providers.portlet; + +import java.security.Principal; + +import javax.portlet.PortletRequest; + +import org.acegisecurity.Authentication; +import org.acegisecurity.AuthenticationException; +import org.acegisecurity.AuthenticationServiceException; +import org.acegisecurity.BadCredentialsException; +import org.acegisecurity.providers.AuthenticationProvider; +import org.acegisecurity.providers.portlet.cache.NullUserCache; +import org.acegisecurity.userdetails.UserDetails; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; + +/** + *

Processes a JSR-168 Portlet authentication request. The request will typically + * originate from {@link org.acegisecurity.ui.portlet.PortletProcessingInterceptor}.

+ * + *

Be aware that this provider is trusting the portal and portlet container to handle + * actual authentication. If a valid {@link PortletAuthenticationToken} is presented with + * non-null principal and credentials, then the {@link #authenticate} method will succeed.

+ * + *

If the details property of the requesting Authentication + * object happens to be the PortletRequest, then this provider will place + * the contents of the USER_INFO map from of the request attributes into + * the details property of the authentication result.

+ * + * @author John A. Lewis + * @since 2.0 + * @version $Id$ + */ +public class PortletAuthenticationProvider + implements AuthenticationProvider, InitializingBean { + + //~ Static fields/initializers ===================================================================================== + + private static final Log logger = LogFactory.getLog(PortletAuthenticationProvider.class); + + //~ Instance fields ================================================================================================ + + private PortletAuthoritiesPopulator portletAuthoritiesPopulator; + private UserCache userCache = new NullUserCache(); + + //~ Methods ======================================================================================================== + + public void afterPropertiesSet() throws Exception { + Assert.notNull(this.portletAuthoritiesPopulator, "An authorities populator must be set"); + Assert.notNull(this.userCache, "A user cache must be set"); + } + + public boolean supports(Class authentication) { + return PortletAuthenticationToken.class.isAssignableFrom(authentication); + } + + public Authentication authenticate(Authentication authentication) + throws AuthenticationException { + + // make sure we support the authentication + if (!supports(authentication.getClass())) { + return null; + } + + if (logger.isDebugEnabled()) + logger.debug("portlet authentication request: " + authentication); + + // make sure there is a valid principal in the authentication attempt + Object principal = authentication.getPrincipal(); + if (principal == null) { + throw new BadCredentialsException("No principal presented - user is not authenticated"); + } + + // make sure there are valid credentials in the authentication attempt + Object credentials = authentication.getCredentials(); + if (credentials == null) { + throw new BadCredentialsException("No credentials presented - user is not authenticated"); + } + + // determine the username string from the principal + String username = getUsernameFromPrincipal(principal); + if (username == null) { + throw new BadCredentialsException("No username available - user is not authenticated"); + } + + // try to retrieve the user from the cache + UserDetails user = this.userCache.getUserFromCache(username); + + // if the user is null then it wasn't in the cache so go get it + if (user == null) { + + if (logger.isDebugEnabled()) + logger.debug("user not found in the cache"); + + // get the user from the authorities populator + user = this.portletAuthoritiesPopulator.getUserDetails(authentication); + + if (user == null) { + throw new AuthenticationServiceException( + "portletAuthoritiesPopulator returned null, which is an interface contract violation"); + } + + // store the result back in the cache + this.userCache.putUserInCache(user); + + } else { + + if (logger.isDebugEnabled()) + logger.debug("got user from the cache"); + } + + // build the resulting successful authentication token + PortletAuthenticationToken result = new PortletAuthenticationToken( + user, authentication.getCredentials(), user.getAuthorities()); + + // see if the detail property on the request is the PortletRequest + if (authentication.getDetails() instanceof PortletRequest) { + // place the USER_INFO map into the details property of the result + PortletRequest request = (PortletRequest)authentication.getDetails(); + result.setDetails(request.getAttribute(PortletRequest.USER_INFO)); + } else { + // copy any other details information forward + result.setDetails(authentication.getDetails()); + } + + if (logger.isDebugEnabled()) + logger.debug("portlet authentication succeeded: " + authentication); + + return result; + } + + /** + * This method attempt to determine the username string from the principal object. + * If the principal object is a {@link UserDetails} object then it will use the + * {@link UserDetails#getUsername() method. If the principal object is a + * {@link Principal} object then it will use the {@link Principal#getName()} + * method. Otherwise it will simply call the toString() method + * on the principal object and return that. + * @param principal the principal object to inspect for a username + * @return the determined username, or null if no principal is passed + */ + public static final String getUsernameFromPrincipal(Object principal) { + if (principal == null) { + return null; + } + if (principal instanceof UserDetails) { + return ((UserDetails)principal).getUsername(); + } + if (principal instanceof Principal) { + return ((Principal)principal).getName(); + } + return principal.toString(); + } + + + public PortletAuthoritiesPopulator getPortletAuthoritiesPopulator() { + return this.portletAuthoritiesPopulator; + } + + public void setPortletAuthoritiesPopulator(PortletAuthoritiesPopulator portletAuthoritiesPopulator) { + this.portletAuthoritiesPopulator = portletAuthoritiesPopulator; + } + + public UserCache getUserCache() { + return userCache; + } + + public void setUserCache(UserCache userCache) { + this.userCache = userCache; + } + +} diff --git a/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/PortletAuthenticationToken.java b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/PortletAuthenticationToken.java new file mode 100644 index 0000000000..172ac121d3 --- /dev/null +++ b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/PortletAuthenticationToken.java @@ -0,0 +1,57 @@ +/* + * Copyright 2005-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.providers.portlet; + +import org.acegisecurity.GrantedAuthority; +import org.acegisecurity.providers.AbstractAuthenticationToken; + +/** + * Authentication implementation for JSR-168 Portlet authentication.

The + * corresponding authentication provider is {@link PortletAuthenticationProvider}.

+ * + * @author John A. Lewis + * @since 2.0 + * @version $Id$ + */ +public class PortletAuthenticationToken extends AbstractAuthenticationToken { + + //~ Instance fields ================================================================================================ + + private static final long serialVersionUID = 1L; + + private Object principal; + private Object credentials; + + //~ Constructors =================================================================================================== + + public PortletAuthenticationToken(Object principal, Object credentials, GrantedAuthority[] authorities) { + super(authorities); + this.principal = principal; + this.credentials = credentials; + } + + //~ Methods ======================================================================================================== + + public Object getPrincipal() { + return this.principal; + } + + public Object getCredentials() { + return this.credentials; + } + +} diff --git a/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/PortletAuthoritiesPopulator.java b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/PortletAuthoritiesPopulator.java new file mode 100644 index 0000000000..a4b4f5440b --- /dev/null +++ b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/PortletAuthoritiesPopulator.java @@ -0,0 +1,45 @@ +/* + * Copyright 2005-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.providers.portlet; + +import org.acegisecurity.Authentication; +import org.acegisecurity.AuthenticationException; +import org.acegisecurity.userdetails.UserDetails; + +/** + * Populates the UserDetails associated with the + * portlet user presented by the portlet container. + * + * @author John A. Lewis + * @since 2.0 + * @version $Id$ + */ +public interface PortletAuthoritiesPopulator { + + //~ Methods ======================================================================================================== + + /** + * Obtains the granted authorities for the specified Authentication object. + *

May throw any AuthenticationException or return null + * if the authorities are unavailable.

+ * @param authentication the authentication object seeking authorities + * @return the details of the indicated user (at minimum the granted authorities and the username) + * @throws AuthenticationException if the user details are not available or the authentication is not valid for some reason + */ + public UserDetails getUserDetails(Authentication authentication) throws AuthenticationException; + +} diff --git a/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/UserCache.java b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/UserCache.java new file mode 100644 index 0000000000..90e3193d54 --- /dev/null +++ b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/UserCache.java @@ -0,0 +1,39 @@ +/* + * Copyright 2005-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.providers.portlet; + +import org.acegisecurity.userdetails.UserDetails; + +/** + * Provides a cache of {@link UserDetails} objects for the + * {@link PortletAuthenticationProvider}. + * + * @author John A. Lewis + * @since 2.0 + * @version $Id$ + */ +public interface UserCache { + + //~ Methods ======================================================================================================== + + public UserDetails getUserFromCache(String username); + + public void putUserInCache(UserDetails user); + + public void removeUserFromCache(String username); + +} diff --git a/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/cache/EhCacheBasedUserCache.java b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/cache/EhCacheBasedUserCache.java new file mode 100644 index 0000000000..742e248d01 --- /dev/null +++ b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/cache/EhCacheBasedUserCache.java @@ -0,0 +1,102 @@ +/* + * Copyright 2005-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.providers.portlet.cache; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheException; +import net.sf.ehcache.Element; + +import org.acegisecurity.providers.portlet.UserCache; +import org.acegisecurity.userdetails.UserDetails; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.dao.DataRetrievalFailureException; +import org.springframework.util.Assert; + +/** + * UserCache implementation for portlets that uses an injected + * ehcache. + * + * @author John A. Lewis + * @since 2.0 + * @version $Id$ + */ +public class EhCacheBasedUserCache + implements UserCache, InitializingBean { + + //~ Static fields/initializers ===================================================================================== + + private static final Log logger = LogFactory.getLog(EhCacheBasedUserCache.class); + + //~ Instance fields ================================================================================================ + + private Cache cache; + + //~ Methods ======================================================================================================== + + public void afterPropertiesSet() throws Exception { + Assert.notNull(cache, "cache mandatory"); + } + + public UserDetails getUserFromCache(String username) { + + Element element = null; + + try { + element = cache.get(username); + } catch (CacheException cacheException) { + throw new DataRetrievalFailureException("Cache failure: " + + cacheException.getMessage()); + } + + if (logger.isDebugEnabled()) + logger.debug("Cache hit: " + (element != null) + "; username: " + username); + + return (element != null ? (UserDetails) element.getValue() : null); + } + + public void putUserInCache(UserDetails user) { + + Element element = new Element(user.getUsername(), user); + + if (logger.isDebugEnabled()) + logger.debug("Cache put: " + element.getKey()); + + cache.put(element); + } + + public void removeUserFromCache(UserDetails user) { + this.removeUserFromCache(user.getUsername()); + } + + public void removeUserFromCache(String username) { + if (logger.isDebugEnabled()) + logger.debug("Cache remove: " + username); + cache.remove(username); + } + + + public Cache getCache() { + return cache; + } + + public void setCache(Cache cache) { + this.cache = cache; + } + +} diff --git a/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/cache/NullUserCache.java b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/cache/NullUserCache.java new file mode 100644 index 0000000000..aa5083b5a4 --- /dev/null +++ b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/cache/NullUserCache.java @@ -0,0 +1,41 @@ +/* + * Copyright 2005-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.providers.portlet.cache; + +import org.acegisecurity.providers.portlet.UserCache; +import org.acegisecurity.userdetails.UserDetails; + +/** + * UserCache implementation for portlets that does nothing. + * + * @author John A. Lewis + * @since 2.0 + * @version $Id$ + */ +public class NullUserCache implements UserCache { + + //~ Methods ======================================================================================================== + + public UserDetails getUserFromCache(String username) { + return null; + } + + public void putUserInCache(UserDetails user) {} + + public void removeUserFromCache(String username) {} + +} diff --git a/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/populator/ContainerPortletAuthoritiesPopulator.java b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/populator/ContainerPortletAuthoritiesPopulator.java new file mode 100644 index 0000000000..a1200520a2 --- /dev/null +++ b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/populator/ContainerPortletAuthoritiesPopulator.java @@ -0,0 +1,136 @@ +/* + * Copyright 2005-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.providers.portlet.populator; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.portlet.PortletRequest; + +import org.acegisecurity.Authentication; +import org.acegisecurity.AuthenticationException; +import org.acegisecurity.AuthenticationServiceException; +import org.acegisecurity.GrantedAuthority; +import org.acegisecurity.GrantedAuthorityImpl; +import org.acegisecurity.providers.portlet.PortletAuthenticationProvider; +import org.acegisecurity.providers.portlet.PortletAuthoritiesPopulator; +import org.acegisecurity.userdetails.User; +import org.acegisecurity.userdetails.UserDetails; + +/** + * Populates the portlet authorities via role information from the portlet container. + * Primarily it uses the PortletRequest.isUserInRole(role) method to + * check if the user is in a list of configured roles. + *

This bean has the following configurable properties:

+ *
    + *
  • rolesToCheck : A list of strings containing names of roles to check. + * These roles must also be properly declared in a <security-role-ref> element + * of the portlet descriptor in the portlet.xml file.
  • + *
  • rolePrefix : The prefix to be added onto each role name that as it is + * added to the list of authorities. The default value is 'ROLE_'.
  • + *
  • userRole : The role that all authenticated users will automatically be + * granted. The default value is 'ROLE_USER'.
  • + *
+ * + * @author John A. Lewis + * @since 2.0 + * @version $Id$ + */ +public class ContainerPortletAuthoritiesPopulator + implements PortletAuthoritiesPopulator { + + //~ Static fields/initializers ===================================================================================== + + private static final String defaultRolePrefix = "ROLE_"; + private static final String defaultUserRole = "ROLE_USER"; + + //~ Instance fields ================================================================================================ + + private List rolesToCheck; + private String rolePrefix = defaultRolePrefix; + private String userRole = defaultUserRole; + + //~ Methods ======================================================================================================== + + public UserDetails getUserDetails(Authentication authentication) + throws AuthenticationException { + + // get the username and password for the authentication + String username = PortletAuthenticationProvider.getUsernameFromPrincipal(authentication.getPrincipal()); + String password = authentication.getCredentials().toString(); + + // see if we can load authorities from the portlet request + Object details = authentication.getDetails(); + if (!(details instanceof PortletRequest)) { + throw new AuthenticationServiceException("expected getDetails() to return the PortletRequest object"); + } + GrantedAuthority[] authorities = loadGrantedAuthorities((PortletRequest)details); + + // construct and return the new user + return new User(username, password, true, true, true, true, authorities); + } + + private GrantedAuthority[] loadGrantedAuthorities(PortletRequest request) { + + // start the list and add the standard user role + ArrayList authorities = new ArrayList(); + authorities.add(new GrantedAuthorityImpl(getUserRole())); + + // iterate through the configured list of roles to check (if there is one) + if (this.rolesToCheck != null) { + for(Iterator i = this.rolesToCheck.iterator(); i.hasNext(); ) { + String role = (String)i.next(); + + // if the request says the user has that role, then add it + if (request.isUserInRole(role)) { + authorities.add(new GrantedAuthorityImpl(getRolePrefix() + role)); + } + + } + } + + // return the array of GrantedAuthority objects + return (GrantedAuthority[])authorities.toArray(new GrantedAuthority[authorities.size()]); + } + + + public List getRolesToCheck() { + return rolesToCheck; + } + + public void setRolesToCheck(List rolesToCheck) { + this.rolesToCheck = rolesToCheck; + } + + public String getRolePrefix() { + return rolePrefix; + } + + public void setRolePrefix(String rolePrefix) { + this.rolePrefix = rolePrefix; + } + + public String getUserRole() { + return userRole; + } + + public void setUserRole(String userRole) { + this.userRole = userRole; + } + +} diff --git a/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/populator/DaoPortletAuthoritiesPopulator.java b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/populator/DaoPortletAuthoritiesPopulator.java new file mode 100644 index 0000000000..f84416658c --- /dev/null +++ b/sandbox/portlet/src/main/java/org/acegisecurity/providers/portlet/populator/DaoPortletAuthoritiesPopulator.java @@ -0,0 +1,74 @@ +/* + * Copyright 2005-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.providers.portlet.populator; + +import org.acegisecurity.Authentication; +import org.acegisecurity.AuthenticationException; +import org.acegisecurity.AuthenticationServiceException; +import org.acegisecurity.providers.portlet.PortletAuthenticationProvider; +import org.acegisecurity.providers.portlet.PortletAuthoritiesPopulator; +import org.acegisecurity.userdetails.UserDetails; +import org.acegisecurity.userdetails.UserDetailsService; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; + +/** + * Populates the portlet authorities via a {@link UserDetailsService}. + * + * @author John A. Lewis + * @since 2.0 + * @version $Id$ + */ +public class DaoPortletAuthoritiesPopulator + implements PortletAuthoritiesPopulator, InitializingBean { + + //~ Instance fields ================================================================================================ + + private UserDetailsService userDetailsService; + + //~ Methods ======================================================================================================== + + public void afterPropertiesSet() throws Exception { + Assert.notNull(this.userDetailsService, "A userDetailsService must be set"); + } + + public UserDetails getUserDetails(Authentication authentication) + throws AuthenticationException { + + // make sure the Authentication object is valid + if (authentication == null || authentication.getPrincipal() == null) { + throw new AuthenticationServiceException( + "must pass valid Authentication object with non-null principal"); + } + + // get the username from the principal + String username = PortletAuthenticationProvider.getUsernameFromPrincipal(authentication.getPrincipal()); + + // call the UserDetailsService with the username + return this.userDetailsService.loadUserByUsername(username); + } + + + public UserDetailsService getUserDetailsService() { + return userDetailsService; + } + + public void setUserDetailsService(UserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + +} diff --git a/sandbox/portlet/src/main/java/org/acegisecurity/ui/portlet/PortletProcessingInterceptor.java b/sandbox/portlet/src/main/java/org/acegisecurity/ui/portlet/PortletProcessingInterceptor.java new file mode 100644 index 0000000000..97eb5fa674 --- /dev/null +++ b/sandbox/portlet/src/main/java/org/acegisecurity/ui/portlet/PortletProcessingInterceptor.java @@ -0,0 +1,293 @@ +/* + * Copyright 2005-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.ui.portlet; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.portlet.ActionRequest; +import javax.portlet.ActionResponse; +import javax.portlet.PortletRequest; +import javax.portlet.PortletResponse; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; + +import org.acegisecurity.Authentication; +import org.acegisecurity.AuthenticationException; +import org.acegisecurity.AuthenticationManager; +import org.acegisecurity.context.SecurityContext; +import org.acegisecurity.context.SecurityContextHolder; +import org.acegisecurity.providers.portlet.PortletAuthenticationProvider; +import org.acegisecurity.providers.portlet.PortletAuthenticationToken; +import org.acegisecurity.providers.portlet.populator.ContainerPortletAuthoritiesPopulator; +import org.acegisecurity.ui.AbstractProcessingFilter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; +import org.springframework.web.portlet.HandlerInterceptor; +import org.springframework.web.portlet.ModelAndView; + +/** + *

This interceptor is responsible for processing portlet authentication requests. This + * is the portlet equivalent of the AuthenticationProcessingFilter used for + * traditional servlet-based web applications. It is applied to both ActionRequests + * and RenderRequests alike. If authentication is successful, the resulting + * {@link Authentication} object will be placed into the SecurityContext, which + * is guaranteed to have already been created by an earlier interceptor. If authentication + * fails, the AuthenticationException will be placed into the + * PortletSession with the attribute defined by + * {@link AbstractProcessingFilter#ACEGI_SECURITY_LAST_EXCEPTION_KEY}.

+ * + *

Some portals do not properly provide the identity of the current user via the + * getRemoteUser() or getUserPrincipal() methods of the + * PortletRequest. In these cases they sometimes make it available in the + * USER_INFO map provided as one of the attributes of the request. If this is + * the case in your portal, you can specify a list of USER_INFO attributes + * to check for the username via the userNameAttributes property of this bean. + * You can also completely override the {@link #getPrincipalFromRequest(PortletRequest)} + * and {@link #getCredentialsFromRequest(PortletRequest)} methods to suit the particular + * behavior of your portal.

+ * + *

This interceptor will put the PortletRequest object into the + * details property of the Authentication object that is sent + * as a request to the AuthenticationManager. This is done so that the request + * is available to classes like {@link ContainerPortletAuthoritiesPopulator} that need + * access to information from the portlet container. The {@link PortletAuthenticationProvider} + * will replace this with the USER_INFO map in the resulting Authentication + * object.

+ * + * @see org.acegisecurity.ui.AbstractProcessingFilter + * @see org.acegisecurity.ui.webapp.AuthenticationProcessingFilter + * @author John A. Lewis + * @since 2.0 + * @version $Id$ + */ +public class PortletProcessingInterceptor implements + HandlerInterceptor, InitializingBean { + + //~ Static fields/initializers ===================================================================================== + + private static final Log logger = LogFactory.getLog(PortletProcessingInterceptor.class); + + //~ Instance fields ================================================================================================ + + private AuthenticationManager authenticationManager; + + private List userNameAttributes; + + //~ Methods ======================================================================================================== + + public void afterPropertiesSet() throws Exception { + Assert.notNull(authenticationManager, "An AuthenticationManager must be set"); + } + + public boolean preHandleAction(ActionRequest request, ActionResponse response, + Object handler) throws Exception { + return preHandle(request, response, handler); + } + + public boolean preHandleRender(RenderRequest request, + RenderResponse response, Object handler) throws Exception { + return preHandle(request, response, handler); + } + + public void postHandleRender(RenderRequest request, RenderResponse response, + Object handler, ModelAndView modelAndView) throws Exception { + } + + public void afterActionCompletion(ActionRequest request, ActionResponse response, + Object handler, Exception ex) throws Exception { + } + + public void afterRenderCompletion(RenderRequest request, RenderResponse response, + Object handler, Exception ex) throws Exception { + } + + /** + * Common preHandle method for both the action and render phases of the interceptor. + */ + private boolean preHandle(PortletRequest request, PortletResponse response, + Object handler) throws Exception { + + // get the SecurityContext + SecurityContext ctx = SecurityContextHolder.getContext(); + + if (logger.isDebugEnabled()) + logger.debug("Checking secure context token: " + ctx.getAuthentication()); + + // if there is no existing Authentication object, then lets create one + if (ctx.getAuthentication() == null) { + + try { + + // build the authentication request from the PortletRequest + PortletAuthenticationToken authRequest = new PortletAuthenticationToken( + getPrincipalFromRequest(request), + getCredentialsFromRequest(request), + null); + + // put the PortletRequest into the authentication request as the "details" + authRequest.setDetails(request); + + if (logger.isDebugEnabled()) + logger.debug("Beginning authentication request for user '" + authRequest.getName() + "'"); + + onPreAuthentication(request, response); + + // ask the authentication manager to authenticate the request + // it will throw an AuthenticationException if it fails, otherwise it succeeded + Authentication authResult = authenticationManager.authenticate(authRequest); + + // process a successful authentication + if (logger.isDebugEnabled()) + logger.debug("Authentication success: " + authResult); + ctx.setAuthentication(authResult); + onSuccessfulAuthentication(request, response, authResult); + + } catch (AuthenticationException failed) { + + // process an unsuccessful authentication + if (logger.isDebugEnabled()) + logger.debug("Authentication failed - updating ContextHolder to contain null Authentication"); + ctx.setAuthentication(null); + request.getPortletSession().setAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed); + onUnsuccessfulAuthentication(request, response, failed); + } + } + + return true; + } + + /** + * This method attempts to extract a principal from the portlet request. + * According to the JSR-168 spec, the PortletRequest should return the name + * of the user in the getRemoteUser() method. It should also provide a + * java.security.Principal object from the getUserPrincipal() + * method. We will first try these to come up with a valid username. + *

Unfortunately, some portals do not properly return these values for authenticated + * users. So, if neither of those succeeds and if the userNameAttributes + * property has been populated, then we will search through the USER_INFO + * map from the request to see if we can find a valid username. + *

This method can be overridden by subclasses to provide special handling + * for portals with weak support for the JSR-168 spec.

+ * @param request the portlet request object + * @return the determined principal object, or null if none found + */ + protected Object getPrincipalFromRequest(PortletRequest request) { + + // first try getRemoteUser() + Object principal = request.getRemoteUser(); + if (principal != null) { + return principal; + } + + // next try getUserPrincipal() + principal = request.getUserPrincipal(); + if (principal != null) { + return principal; + } + + // last try entries in USER_INFO if any attributes were defined + if (this.userNameAttributes != null) { + Map userInfo = (Map)request.getAttribute(PortletRequest.USER_INFO); + if (userInfo != null) { + Iterator i = this.userNameAttributes.iterator(); + while(i.hasNext()) { + principal = (String)userInfo.get(i.next()); + if (principal != null) { + return principal; + } + } + } + } + + // none found so return null + return null; + } + + /** + * This method attempts to extract a credentials from the portlet request. + * We are trusting the portal framework to authenticate the user, so all + * we are really doing is trying to put something intelligent in here to + * indicate the user is authenticated. According to the JSR-168 spec, + * PortletRequest.getAuthType() should return a non-null value if the + * user is authenticated and should be null if not authenticated. So we + * will use this as the credentials and the token will be trusted as + * authenticated if the credentials are not null. + *

This method can be overridden by subclasses to provide special handling + * for portals with weak support for the JSR-168 spec. If that is done, + * be sure the value is non-null for authenticated users and null for + * non-authenticated users.

+ * @param request the portlet request object + * @return the determined credentials object, or null if none found + */ + protected Object getCredentialsFromRequest(PortletRequest request) { + return request.getAuthType(); + } + + + /** + * Callback for custom processing prior to the authentication attempt. + * @param request the portlet request to be authenticated + * @param response the portlet response to be authenticated + * @throws AuthenticationException to indicate that authentication attempt is not valid and should be terminated + * @throws IOException + */ + protected void onPreAuthentication(PortletRequest request, PortletResponse response) + throws AuthenticationException, IOException {} + + /** + * Callback for custom processing after a successful authentication attempt. + * @param request the portlet request that was authenticated + * @param response the portlet response that was authenticated + * @param authResult the resulting Authentication object + * @throws IOException + */ + protected void onSuccessfulAuthentication(PortletRequest request, PortletResponse response, Authentication authResult) + throws IOException {} + + /** + * Callback for custom processing after an unsuccessful authentication attempt. + * @param request the portlet request that failed authentication + * @param response the portlet response that failed authentication + * @param failed the AuthenticationException that occurred + * @throws IOException + */ + protected void onUnsuccessfulAuthentication(PortletRequest request, PortletResponse response, AuthenticationException failed) + throws IOException {} + + + public AuthenticationManager getAuthenticationManager() { + return authenticationManager; + } + + public void setAuthenticationManager(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + public List getUserNameAttributes() { + return userNameAttributes; + } + + public void setUserNameAttributes(List userNameAttributes) { + this.userNameAttributes = userNameAttributes; + } + +}