View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kim.client.acegi;
17  
18  import org.acegisecurity.Authentication;
19  import org.acegisecurity.AuthenticationException;
20  import org.acegisecurity.BadCredentialsException;
21  import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
22  import org.acegisecurity.providers.cas.CasAuthenticationProvider;
23  import org.acegisecurity.providers.cas.CasAuthenticationToken;
24  import org.acegisecurity.providers.cas.StatelessTicketCache;
25  import org.acegisecurity.ui.cas.CasProcessingFilter;
26  import org.acegisecurity.userdetails.UserDetails;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  /**
31   * A {@link CasAuthenticationProvider} implementation that integrates with 
32   * Kuali Identity Management (KIM).<p>This 
33   * <code>CasAuthenticationProvider</code> is capable of validating {@link
34   * UsernamePasswordAuthenticationToken} requests which contains a 
35   * distributed session token. It can also validate a previously created 
36   * {@link CasAuthenticationToken}.</p>
37   *
38   * Verifies the the <code>UserDetails</code> based on a valid CAS ticket 
39   * response.
40   * 
41   * @author Kuali Rice Team (rice.collab@kuali.org)
42  */
43  public class KualiCasAuthenticationProvider extends CasAuthenticationProvider {
44      
45      private static final Log logger = LogFactory.getLog(KualiCasAuthenticationProvider.class);
46  
47      /**
48       * This overridden method is copied from CAS verbatim.  For some reason 
49       * {@link authenticateNow} would not override and the super method 
50       * would get called until did this method was also overridden.
51       * 
52       * @see org.acegisecurity.providers.cas.CasAuthenticationProvider#authenticate(org.acegisecurity.Authentication)
53       */
54      public Authentication authenticate(Authentication authentication) throws AuthenticationException {
55          StatelessTicketCache statelessTicketCache = this.getStatelessTicketCache();
56          String key = this.getKey();
57          if (!supports(authentication.getClass())) {
58              return null;
59          }
60  
61          if (authentication instanceof UsernamePasswordAuthenticationToken
62              && (!CasProcessingFilter.CAS_STATEFUL_IDENTIFIER.equals(authentication.getPrincipal().toString())
63              && !CasProcessingFilter.CAS_STATELESS_IDENTIFIER.equals(authentication.getPrincipal().toString()))) {
64              // UsernamePasswordAuthenticationToken not CAS related
65              return null;
66          }
67  
68          // If an existing CasAuthenticationToken, just check we created it
69          if (authentication instanceof CasAuthenticationToken) {
70              if (key.hashCode() == ((CasAuthenticationToken) authentication).getKeyHash()) {
71                  return authentication;
72              } else {
73                  throw new BadCredentialsException(messages.getMessage("CasAuthenticationProvider.incorrectKey",
74                          "The presented CasAuthenticationToken does not contain the expected key"));
75              }
76          }
77  
78          // Ensure credentials are presented
79          if ((authentication.getCredentials() == null) || "".equals(authentication.getCredentials())) {
80              throw new BadCredentialsException(messages.getMessage("CasAuthenticationProvider.noServiceTicket",
81                      "Failed to provide a CAS service ticket to validate"));
82          }
83  
84          boolean stateless = false;
85  
86          if (authentication instanceof UsernamePasswordAuthenticationToken
87              && CasProcessingFilter.CAS_STATELESS_IDENTIFIER.equals(authentication.getPrincipal())) {
88              stateless = true;
89          }
90  
91          CasAuthenticationToken result = null;
92  
93          if (stateless) {
94              // Try to obtain from cache
95              result = statelessTicketCache.getByTicketId(authentication.getCredentials().toString());
96          }
97  
98          if (result == null) {
99              result = this.authenticateNow(authentication);
100             result.setDetails(authentication.getDetails());
101         }
102 
103         if (stateless) {
104             // Add to cache
105             statelessTicketCache.putTicketInCache(result);
106         }
107 
108         return result;
109     }
110     
111     /**
112      * This overridden method is differs from the super method by 
113      * populating the user details by passing the full response
114      * 
115      * @see org.acegisecurity.providers.cas.CasAuthenticationProvider#authenticateNow(Authentication authentication)
116      */
117     private CasAuthenticationToken authenticateNow(Authentication authentication) throws AuthenticationException {
118         // Validate
119         KualiTicketResponse response = (KualiTicketResponse)this.getTicketValidator().confirmTicketValid(authentication.getCredentials().toString());
120 
121         // Check proxy list is trusted
122         this.getCasProxyDecider().confirmProxyListTrusted(response.getProxyList());
123         if (logger.isDebugEnabled()) {
124             logger.debug("authenticationNOW:" + response);
125         }
126         // Lookup user details      
127         logger.debug("\n\npopulating authorities\n\n");
128         UserDetails userDetails = ((KualiCasAuthoritiesPopulator)this.getCasAuthoritiesPopulator()).getUserDetails(response);        
129 
130         // Construct CasAuthenticationToken
131         return new CasAuthenticationToken(this.getKey(), userDetails, authentication.getCredentials(),
132             userDetails.getAuthorities(), userDetails, response.getProxyList(), response.getProxyGrantingTicketIou());
133     }
134 }