View Javadoc
1   /**
2    * Waffle (https://github.com/dblock/waffle)
3    *
4    * Copyright (c) 2010 - 2015 Application Security, Inc.
5    *
6    * All rights reserved. This program and the accompanying materials
7    * are made available under the terms of the Eclipse Public License v1.0
8    * which accompanies this distribution, and is available at
9    * http://www.eclipse.org/legal/epl-v10.html
10   *
11   * Contributors:
12   *     Application Security, Inc.
13   */
14  package waffle.servlet.spi;
15  
16  import java.io.IOException;
17  import java.lang.reflect.Constructor;
18  import java.lang.reflect.InvocationTargetException;
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import javax.servlet.http.HttpServletRequest;
23  import javax.servlet.http.HttpServletResponse;
24  
25  import org.slf4j.Logger;
26  import org.slf4j.LoggerFactory;
27  
28  import com.sun.jna.platform.win32.Win32Exception;
29  
30  import waffle.util.AuthorizationHeader;
31  import waffle.windows.auth.IWindowsAuthProvider;
32  import waffle.windows.auth.IWindowsIdentity;
33  
34  /**
35   * A collection of security filter providers.
36   * 
37   * @author dblock[at]dblock[dot]org
38   */
39  public class SecurityFilterProviderCollection {
40  
41      /** The Constant LOGGER. */
42      private static final Logger          LOGGER    = LoggerFactory.getLogger(SecurityFilterProviderCollection.class);
43      
44      /** The providers. */
45      private final List<SecurityFilterProvider> providers = new ArrayList<SecurityFilterProvider>();
46  
47      /**
48       * Instantiates a new security filter provider collection.
49       *
50       * @param providerArray
51       *            the provider array
52       */
53      public SecurityFilterProviderCollection(final SecurityFilterProvider[] providerArray) {
54          for (final SecurityFilterProvider provider : providerArray) {
55              SecurityFilterProviderCollection.LOGGER.info("using '{}'", provider.getClass().getName());
56              this.providers.add(provider);
57          }
58      }
59  
60      /**
61       * Instantiates a new security filter provider collection.
62       *
63       * @param providerNames
64       *            the provider names
65       * @param auth
66       *            the auth
67       */
68      @SuppressWarnings("unchecked")
69      public SecurityFilterProviderCollection(final String[] providerNames, final IWindowsAuthProvider auth) {
70          Class<SecurityFilterProvider> providerClass;
71          Constructor<SecurityFilterProvider> providerConstructor;
72          for (String providerName : providerNames) {
73              providerName = providerName.trim();
74              SecurityFilterProviderCollection.LOGGER.info("loading '{}'", providerName);
75              try {
76                  providerClass = (Class<SecurityFilterProvider>) Class.forName(providerName);
77                  providerConstructor = providerClass.getConstructor(IWindowsAuthProvider.class);
78                  final SecurityFilterProvider provider = providerConstructor.newInstance(auth);
79                  this.providers.add(provider);
80              } catch (final ClassNotFoundException e) {
81                  SecurityFilterProviderCollection.LOGGER.error("error loading '{}': {}", providerName, e.getMessage());
82                  SecurityFilterProviderCollection.LOGGER.trace("{}", e);
83                  throw new RuntimeException(e);
84              } catch (final SecurityException e) {
85                  SecurityFilterProviderCollection.LOGGER.error("error loading '{}': {}", providerName, e.getMessage());
86                  SecurityFilterProviderCollection.LOGGER.trace("{}", e);
87              } catch (final NoSuchMethodException e) {
88                  SecurityFilterProviderCollection.LOGGER.error("error loading '{}': {}", providerName, e.getMessage());
89                  SecurityFilterProviderCollection.LOGGER.trace("{}", e);
90              } catch (final IllegalArgumentException e) {
91                  SecurityFilterProviderCollection.LOGGER.error("error loading '{}': {}", providerName, e.getMessage());
92                  SecurityFilterProviderCollection.LOGGER.trace("{}", e);
93              } catch (final InstantiationException e) {
94                  SecurityFilterProviderCollection.LOGGER.error("error loading '{}': {}", providerName, e.getMessage());
95                  SecurityFilterProviderCollection.LOGGER.trace("{}", e);
96              } catch (final IllegalAccessException e) {
97                  SecurityFilterProviderCollection.LOGGER.error("error loading '{}': {}", providerName, e.getMessage());
98                  SecurityFilterProviderCollection.LOGGER.trace("{}", e);
99              } catch (final InvocationTargetException e) {
100                 SecurityFilterProviderCollection.LOGGER.error("error loading '{}': {}", providerName, e.getMessage());
101                 SecurityFilterProviderCollection.LOGGER.trace("{}", e);
102             }
103         }
104     }
105 
106     /**
107      * Instantiates a new security filter provider collection.
108      *
109      * @param auth
110      *            the auth
111      */
112     public SecurityFilterProviderCollection(final IWindowsAuthProvider auth) {
113         this.providers.add(new NegotiateSecurityFilterProvider(auth));
114         this.providers.add(new BasicSecurityFilterProvider(auth));
115     }
116 
117     /**
118      * Tests whether a specific security package is supported by any of the underlying providers.
119      * 
120      * @param securityPackage
121      *            Security package.
122      * @return True if the security package is supported, false otherwise.
123      */
124     public boolean isSecurityPackageSupported(final String securityPackage) {
125         return this.get(securityPackage) != null;
126     }
127 
128     /**
129      * Gets the.
130      *
131      * @param securityPackage
132      *            the security package
133      * @return the security filter provider
134      */
135     private SecurityFilterProvider get(final String securityPackage) {
136         for (final SecurityFilterProvider provider : this.providers) {
137             if (provider.isSecurityPackageSupported(securityPackage)) {
138                 return provider;
139             }
140         }
141         return null;
142     }
143 
144     /**
145      * Filter.
146      * 
147      * @param request
148      *            Http Request
149      * @param response
150      *            Http Response
151      * @return Windows Identity or NULL.
152      * @throws IOException
153      *             on doFilter.
154      */
155     public IWindowsIdentity doFilter(final HttpServletRequest request, final HttpServletResponse response)
156             throws IOException {
157         final AuthorizationHeader authorizationHeader = new AuthorizationHeader(request);
158         final SecurityFilterProvider provider = this.get(authorizationHeader.getSecurityPackage());
159         if (provider == null) {
160             throw new RuntimeException("Unsupported security package: " + authorizationHeader.getSecurityPackage());
161         }
162         try {
163             return provider.doFilter(request, response);
164         } catch (final Win32Exception e) {
165             throw new IOException(e);
166         }
167     }
168 
169     /**
170      * Returns true if authentication still needs to happen despite an existing principal.
171      * 
172      * @param request
173      *            Http Request
174      * @return True if authentication is required.
175      */
176     public boolean isPrincipalException(final HttpServletRequest request) {
177         for (final SecurityFilterProvider provider : this.providers) {
178             if (provider.isPrincipalException(request)) {
179                 return true;
180             }
181         }
182         return false;
183     }
184 
185     /**
186      * Send authorization headers.
187      * 
188      * @param response
189      *            Http Response
190      */
191     public void sendUnauthorized(final HttpServletResponse response) {
192         for (final SecurityFilterProvider provider : this.providers) {
193             provider.sendUnauthorized(response);
194         }
195     }
196 
197     /**
198      * Number of providers.
199      * 
200      * @return Number of providers.
201      */
202     public int size() {
203         return this.providers.size();
204     }
205 
206     /**
207      * Get a security provider by class name.
208      * 
209      * @param name
210      *            Class name.
211      * @return A security provider instance.
212      * @throws ClassNotFoundException
213      *             when class not found.
214      */
215     public SecurityFilterProvider getByClassName(final String name) throws ClassNotFoundException {
216         for (final SecurityFilterProvider provider : this.providers) {
217             if (provider.getClass().getName().equals(name)) {
218                 return provider;
219             }
220         }
221         throw new ClassNotFoundException(name);
222     }
223 }