001: /*
002: * Copyright 2005-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.acegisecurity.providers.portlet;
018:
019: import java.security.Principal;
020:
021: import javax.portlet.PortletRequest;
022:
023: import org.acegisecurity.Authentication;
024: import org.acegisecurity.AuthenticationException;
025: import org.acegisecurity.AuthenticationServiceException;
026: import org.acegisecurity.BadCredentialsException;
027: import org.acegisecurity.providers.AuthenticationProvider;
028: import org.acegisecurity.providers.portlet.cache.NullUserCache;
029: import org.acegisecurity.userdetails.UserDetails;
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032: import org.springframework.beans.factory.InitializingBean;
033: import org.springframework.util.Assert;
034:
035: /**
036: * <p>Processes a JSR-168 Portlet authentication request. The request will typically
037: * originate from {@link org.acegisecurity.ui.portlet.PortletProcessingInterceptor}.</p>
038: *
039: * <p>Be aware that this provider is trusting the portal and portlet container to handle
040: * actual authentication. If a valid {@link PortletAuthenticationToken} is presented with
041: * non-null principal and credentials, then the {@link #authenticate} method will succeed.</p>
042: *
043: * <p>If the <code>details</code> property of the requesting <code>Authentication</code>
044: * object happens to be the <code>PortletRequest</code>, then this provider will place
045: * the contents of the <code>USER_INFO</code> map from of the request attributes into
046: * the <code>details</code> property of the authentication result.</p>
047: *
048: * @author John A. Lewis
049: * @since 2.0
050: * @version $Id$
051: */
052: public class PortletAuthenticationProvider implements
053: AuthenticationProvider, InitializingBean {
054:
055: //~ Static fields/initializers =====================================================================================
056:
057: private static final Log logger = LogFactory
058: .getLog(PortletAuthenticationProvider.class);
059:
060: //~ Instance fields ================================================================================================
061:
062: private PortletAuthoritiesPopulator portletAuthoritiesPopulator;
063: private UserCache userCache = new NullUserCache();
064:
065: //~ Methods ========================================================================================================
066:
067: public void afterPropertiesSet() throws Exception {
068: Assert.notNull(this .portletAuthoritiesPopulator,
069: "An authorities populator must be set");
070: Assert.notNull(this .userCache, "A user cache must be set");
071: }
072:
073: public boolean supports(Class authentication) {
074: return PortletAuthenticationToken.class
075: .isAssignableFrom(authentication);
076: }
077:
078: public Authentication authenticate(Authentication authentication)
079: throws AuthenticationException {
080:
081: // make sure we support the authentication
082: if (!supports(authentication.getClass())) {
083: return null;
084: }
085:
086: if (logger.isDebugEnabled())
087: logger.debug("portlet authentication request: "
088: + authentication);
089:
090: // make sure there is a valid principal in the authentication attempt
091: Object principal = authentication.getPrincipal();
092: if (principal == null) {
093: throw new BadCredentialsException(
094: "No principal presented - user is not authenticated");
095: }
096:
097: // make sure there are valid credentials in the authentication attempt
098: Object credentials = authentication.getCredentials();
099: if (credentials == null) {
100: throw new BadCredentialsException(
101: "No credentials presented - user is not authenticated");
102: }
103:
104: // determine the username string from the principal
105: String username = getUsernameFromPrincipal(principal);
106: if (username == null) {
107: throw new BadCredentialsException(
108: "No username available - user is not authenticated");
109: }
110:
111: // try to retrieve the user from the cache
112: UserDetails user = this .userCache.getUserFromCache(username);
113:
114: // if the user is null then it wasn't in the cache so go get it
115: if (user == null) {
116:
117: if (logger.isDebugEnabled())
118: logger.debug("user not found in the cache");
119:
120: // get the user from the authorities populator
121: user = this .portletAuthoritiesPopulator
122: .getUserDetails(authentication);
123:
124: if (user == null) {
125: throw new AuthenticationServiceException(
126: "portletAuthoritiesPopulator returned null, which is an interface contract violation");
127: }
128:
129: // store the result back in the cache
130: this .userCache.putUserInCache(user);
131:
132: } else {
133:
134: if (logger.isDebugEnabled())
135: logger.debug("got user from the cache");
136: }
137:
138: // build the resulting successful authentication token
139: PortletAuthenticationToken result = new PortletAuthenticationToken(
140: user, authentication.getCredentials(), user
141: .getAuthorities());
142:
143: // see if the detail property on the request is the PortletRequest
144: if (authentication.getDetails() instanceof PortletRequest) {
145: // place the USER_INFO map into the details property of the result
146: PortletRequest request = (PortletRequest) authentication
147: .getDetails();
148: result.setDetails(request
149: .getAttribute(PortletRequest.USER_INFO));
150: } else {
151: // copy any other details information forward
152: result.setDetails(authentication.getDetails());
153: }
154:
155: if (logger.isDebugEnabled())
156: logger.debug("portlet authentication succeeded: "
157: + authentication);
158:
159: return result;
160: }
161:
162: /**
163: * This method attempt to determine the username string from the principal object.
164: * If the principal object is a {@link UserDetails} object then it will use the
165: * {@link UserDetails#getUsername() method. If the principal object is a
166: * {@link Principal} object then it will use the {@link Principal#getName()}
167: * method. Otherwise it will simply call the <code>toString()<code> method
168: * on the principal object and return that.
169: * @param principal the principal object to inspect for a username
170: * @return the determined username, or null if no principal is passed
171: */
172: public static final String getUsernameFromPrincipal(Object principal) {
173: if (principal == null) {
174: return null;
175: }
176: if (principal instanceof UserDetails) {
177: return ((UserDetails) principal).getUsername();
178: }
179: if (principal instanceof Principal) {
180: return ((Principal) principal).getName();
181: }
182: return principal.toString();
183: }
184:
185: public PortletAuthoritiesPopulator getPortletAuthoritiesPopulator() {
186: return this .portletAuthoritiesPopulator;
187: }
188:
189: public void setPortletAuthoritiesPopulator(
190: PortletAuthoritiesPopulator portletAuthoritiesPopulator) {
191: this .portletAuthoritiesPopulator = portletAuthoritiesPopulator;
192: }
193:
194: public UserCache getUserCache() {
195: return userCache;
196: }
197:
198: public void setUserCache(UserCache userCache) {
199: this.userCache = userCache;
200: }
201:
202: }
|