View Javadoc

1   /* Copyright 2004, 2005 Acegi Technology Pty Limited
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  package org.acegisecurity.providers.x509;
17  
18  import java.security.cert.X509Certificate;
19  
20  import org.acegisecurity.AcegiMessageSource;
21  import org.acegisecurity.Authentication;
22  import org.acegisecurity.AuthenticationException;
23  import org.acegisecurity.BadCredentialsException;
24  import org.acegisecurity.providers.AuthenticationProvider;
25  import org.acegisecurity.providers.x509.cache.NullX509UserCache;
26  import org.acegisecurity.userdetails.UserDetails;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.springframework.beans.factory.InitializingBean;
30  import org.springframework.context.MessageSource;
31  import org.springframework.context.MessageSourceAware;
32  import org.springframework.context.support.MessageSourceAccessor;
33  import org.springframework.util.Assert;
34  
35  
36  /***
37   * Processes an X.509 authentication request.
38   * 
39   * <p>
40   * The request will typically originate from {@link
41   * org.acegisecurity.ui.x509.X509ProcessingFilter}).
42   * </p>
43   */
44  public class X509AuthenticationProvider implements AuthenticationProvider,
45      InitializingBean, MessageSourceAware {
46      //~ Static fields/initializers =============================================
47  
48      private static final Log logger = LogFactory.getLog(X509AuthenticationProvider.class);
49  
50      //~ Instance fields ========================================================
51  
52      protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
53      private X509AuthoritiesPopulator x509AuthoritiesPopulator;
54      private X509UserCache userCache = new NullX509UserCache();
55  
56      //~ Methods ================================================================
57  
58      public void afterPropertiesSet() throws Exception {
59          Assert.notNull(userCache, "An x509UserCache must be set");
60          Assert.notNull(x509AuthoritiesPopulator,
61              "An X509AuthoritiesPopulator must be set");
62          Assert.notNull(this.messages, "A message source must be set");
63      }
64  
65      /***
66       * If the supplied authentication token contains a certificate then this
67       * will be passed to the configured {@link X509AuthoritiesPopulator} to
68       * obtain the user details and authorities for the user identified by the
69       * certificate.
70       * 
71       * <p>
72       * If no certificate is present (for example, if the filter is applied to
73       * an HttpRequest for which client authentication hasn't been configured
74       * in the container) then a BadCredentialsException will be raised.
75       * </p>
76       *
77       * @param authentication the authentication request.
78       *
79       * @return an X509AuthenticationToken containing the authorities of the
80       *         principal represented by the certificate.
81       *
82       * @throws AuthenticationException if the {@link X509AuthoritiesPopulator}
83       *         rejects the certficate.
84       * @throws BadCredentialsException if no certificate was presented in the
85       *         authentication request.
86       */
87      public Authentication authenticate(Authentication authentication)
88          throws AuthenticationException {
89          if (!supports(authentication.getClass())) {
90              return null;
91          }
92  
93          if (logger.isDebugEnabled()) {
94              logger.debug("X509 authentication request: " + authentication);
95          }
96  
97          X509Certificate clientCertificate = (X509Certificate) authentication
98                  .getCredentials();
99  
100             if (clientCertificate == null) {
101                 throw new BadCredentialsException(messages.getMessage(
102                         "X509AuthenticationProvider.certificateNull",
103                         "Certificate is null"));
104             }
105 
106             UserDetails user = userCache.getUserFromCache(clientCertificate);
107 
108             if (user == null) {
109                 logger.debug("Authenticating with certificate "
110                     + clientCertificate);
111                 user = x509AuthoritiesPopulator.getUserDetails(clientCertificate);
112                 userCache.putUserInCache(clientCertificate, user);
113             }
114 
115             return new X509AuthenticationToken(user, clientCertificate,
116                 user.getAuthorities());
117         }
118 
119         public void setMessageSource(MessageSource messageSource) {
120             this.messages = new MessageSourceAccessor(messageSource);
121         }
122 
123         public void setX509AuthoritiesPopulator(
124             X509AuthoritiesPopulator x509AuthoritiesPopulator) {
125             this.x509AuthoritiesPopulator = x509AuthoritiesPopulator;
126         }
127 
128         public void setX509UserCache(X509UserCache cache) {
129             this.userCache = cache;
130         }
131 
132         public boolean supports(Class authentication) {
133             return X509AuthenticationToken.class.isAssignableFrom(authentication);
134         }
135     }