001: /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015:
016: package org.acegisecurity.providers.cas;
017:
018: import org.acegisecurity.AcegiMessageSource;
019: import org.acegisecurity.Authentication;
020: import org.acegisecurity.AuthenticationException;
021: import org.acegisecurity.BadCredentialsException;
022:
023: import org.acegisecurity.providers.AuthenticationProvider;
024: import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
025: import org.acegisecurity.providers.cas.cache.NullStatelessTicketCache;
026:
027: import org.acegisecurity.ui.cas.CasProcessingFilter;
028:
029: import org.acegisecurity.userdetails.UserDetails;
030:
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033:
034: import org.springframework.beans.factory.InitializingBean;
035:
036: import org.springframework.context.MessageSource;
037: import org.springframework.context.MessageSourceAware;
038: import org.springframework.context.support.MessageSourceAccessor;
039:
040: import org.springframework.util.Assert;
041:
042: /**
043: * An {@link AuthenticationProvider} implementation that integrates with JA-SIG Central Authentication Service
044: * (CAS).<p>This <code>AuthenticationProvider</code> is capable of validating {@link
045: * UsernamePasswordAuthenticationToken} requests which contain a <code>principal</code> name equal to either {@link
046: * CasProcessingFilter#CAS_STATEFUL_IDENTIFIER} or {@link CasProcessingFilter#CAS_STATELESS_IDENTIFIER}. It can also
047: * validate a previously created {@link CasAuthenticationToken}.</p>
048: *
049: * @author Ben Alex
050: * @version $Id: CasAuthenticationProvider.java 2237 2007-11-07 21:55:59Z sbattaglia $
051: */
052: public class CasAuthenticationProvider implements
053: AuthenticationProvider, InitializingBean, MessageSourceAware {
054: //~ Static fields/initializers =====================================================================================
055:
056: private static final Log logger = LogFactory
057: .getLog(CasAuthenticationProvider.class);
058:
059: //~ Instance fields ================================================================================================
060:
061: private CasAuthoritiesPopulator casAuthoritiesPopulator;
062: private CasProxyDecider casProxyDecider;
063: protected MessageSourceAccessor messages = AcegiMessageSource
064: .getAccessor();
065: private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
066: private String key;
067: private TicketValidator ticketValidator;
068:
069: //~ Methods ========================================================================================================
070:
071: public void afterPropertiesSet() throws Exception {
072: Assert.notNull(this .casAuthoritiesPopulator,
073: "A casAuthoritiesPopulator must be set");
074: Assert.notNull(this .ticketValidator,
075: "A ticketValidator must be set");
076: Assert.notNull(this .casProxyDecider,
077: "A casProxyDecider must be set");
078: Assert.notNull(this .statelessTicketCache,
079: "A statelessTicketCache must be set");
080: Assert
081: .hasText(
082: this .key,
083: "A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
084: Assert.notNull(this .messages, "A message source must be set");
085: }
086:
087: public Authentication authenticate(Authentication authentication)
088: throws AuthenticationException {
089: if (!supports(authentication.getClass())) {
090: return null;
091: }
092:
093: if (authentication instanceof UsernamePasswordAuthenticationToken
094: && (!CasProcessingFilter.CAS_STATEFUL_IDENTIFIER
095: .equals(authentication.getPrincipal()
096: .toString()) && !CasProcessingFilter.CAS_STATELESS_IDENTIFIER
097: .equals(authentication.getPrincipal()
098: .toString()))) {
099: // UsernamePasswordAuthenticationToken not CAS related
100: return null;
101: }
102:
103: // If an existing CasAuthenticationToken, just check we created it
104: if (authentication instanceof CasAuthenticationToken) {
105: if (this .key.hashCode() == ((CasAuthenticationToken) authentication)
106: .getKeyHash()) {
107: return authentication;
108: } else {
109: throw new BadCredentialsException(
110: messages
111: .getMessage(
112: "CasAuthenticationProvider.incorrectKey",
113: "The presented CasAuthenticationToken does not contain the expected key"));
114: }
115: }
116:
117: // Ensure credentials are presented
118: if ((authentication.getCredentials() == null)
119: || "".equals(authentication.getCredentials())) {
120: throw new BadCredentialsException(
121: messages
122: .getMessage(
123: "CasAuthenticationProvider.noServiceTicket",
124: "Failed to provide a CAS service ticket to validate"));
125: }
126:
127: boolean stateless = false;
128:
129: if (authentication instanceof UsernamePasswordAuthenticationToken
130: && CasProcessingFilter.CAS_STATELESS_IDENTIFIER
131: .equals(authentication.getPrincipal())) {
132: stateless = true;
133: }
134:
135: CasAuthenticationToken result = null;
136:
137: if (stateless) {
138: // Try to obtain from cache
139: result = statelessTicketCache.getByTicketId(authentication
140: .getCredentials().toString());
141: }
142:
143: if (result == null) {
144: result = this .authenticateNow(authentication);
145: }
146:
147: if (stateless) {
148: // Add to cache
149: statelessTicketCache.putTicketInCache(result);
150: }
151:
152: return result;
153: }
154:
155: private CasAuthenticationToken authenticateNow(
156: Authentication authentication)
157: throws AuthenticationException {
158: // Validate
159: TicketResponse response = ticketValidator
160: .confirmTicketValid(authentication.getCredentials()
161: .toString());
162:
163: // Check proxy list is trusted
164: this .casProxyDecider.confirmProxyListTrusted(response
165: .getProxyList());
166:
167: // Lookup user details
168: UserDetails userDetails = this .casAuthoritiesPopulator
169: .getUserDetails(response.getUser());
170:
171: // Construct CasAuthenticationToken
172: return new CasAuthenticationToken(this .key, userDetails,
173: authentication.getCredentials(), userDetails
174: .getAuthorities(), userDetails, response
175: .getProxyList(), response
176: .getProxyGrantingTicketIou());
177: }
178:
179: public CasAuthoritiesPopulator getCasAuthoritiesPopulator() {
180: return casAuthoritiesPopulator;
181: }
182:
183: public CasProxyDecider getCasProxyDecider() {
184: return casProxyDecider;
185: }
186:
187: public String getKey() {
188: return key;
189: }
190:
191: public StatelessTicketCache getStatelessTicketCache() {
192: return statelessTicketCache;
193: }
194:
195: public TicketValidator getTicketValidator() {
196: return ticketValidator;
197: }
198:
199: public void setCasAuthoritiesPopulator(
200: CasAuthoritiesPopulator casAuthoritiesPopulator) {
201: this .casAuthoritiesPopulator = casAuthoritiesPopulator;
202: }
203:
204: public void setCasProxyDecider(CasProxyDecider casProxyDecider) {
205: this .casProxyDecider = casProxyDecider;
206: }
207:
208: public void setKey(String key) {
209: this .key = key;
210: }
211:
212: public void setMessageSource(MessageSource messageSource) {
213: this .messages = new MessageSourceAccessor(messageSource);
214: }
215:
216: public void setStatelessTicketCache(
217: StatelessTicketCache statelessTicketCache) {
218: this .statelessTicketCache = statelessTicketCache;
219: }
220:
221: public void setTicketValidator(TicketValidator ticketValidator) {
222: this .ticketValidator = ticketValidator;
223: }
224:
225: public boolean supports(Class authentication) {
226: if (UsernamePasswordAuthenticationToken.class
227: .isAssignableFrom(authentication)) {
228: return true;
229: } else if (CasAuthenticationToken.class
230: .isAssignableFrom(authentication)) {
231: return true;
232: } else {
233: return false;
234: }
235: }
236: }
|