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.spring;
15  
16  import java.io.IOException;
17  import java.util.Locale;
18  
19  import javax.servlet.FilterChain;
20  import javax.servlet.ServletException;
21  import javax.servlet.ServletRequest;
22  import javax.servlet.ServletResponse;
23  import javax.servlet.http.HttpServletRequest;
24  import javax.servlet.http.HttpServletResponse;
25  
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  import org.springframework.security.core.Authentication;
29  import org.springframework.security.core.GrantedAuthority;
30  import org.springframework.security.core.context.SecurityContextHolder;
31  import org.springframework.web.filter.GenericFilterBean;
32  
33  import waffle.servlet.WindowsPrincipal;
34  import waffle.servlet.spi.SecurityFilterProviderCollection;
35  import waffle.util.AuthorizationHeader;
36  import waffle.windows.auth.IWindowsIdentity;
37  import waffle.windows.auth.PrincipalFormat;
38  
39  /**
40   * A Spring Negotiate security filter.
41   * 
42   * @author dblock[at]dblock[dot]org
43   */
44  public class NegotiateSecurityFilter extends GenericFilterBean {
45  
46      /** The Constant LOGGER. */
47      private static final Logger              LOGGER                  = LoggerFactory
48                                                                               .getLogger(NegotiateSecurityFilter.class);
49      
50      /** The provider. */
51      private SecurityFilterProviderCollection provider;
52      
53      /** The principal format. */
54      private PrincipalFormat                  principalFormat         = PrincipalFormat.FQN;
55      
56      /** The role format. */
57      private PrincipalFormat                  roleFormat              = PrincipalFormat.FQN;
58      
59      /** The allow guest login. */
60      private boolean                          allowGuestLogin         = true;
61  
62      /** The granted authority factory. */
63      private GrantedAuthorityFactory          grantedAuthorityFactory = WindowsAuthenticationToken.DEFAULT_GRANTED_AUTHORITY_FACTORY;
64      
65      /** The default granted authority. */
66      private GrantedAuthority                 defaultGrantedAuthority = WindowsAuthenticationToken.DEFAULT_GRANTED_AUTHORITY;
67  
68      /**
69       * Instantiates a new negotiate security filter.
70       */
71      public NegotiateSecurityFilter() {
72          super();
73          NegotiateSecurityFilter.LOGGER.debug("[waffle.spring.NegotiateSecurityFilter] loaded");
74      }
75  
76      /* (non-Javadoc)
77       * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
78       */
79      @Override
80      public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain)
81              throws IOException, ServletException {
82  
83          final HttpServletRequest request = (HttpServletRequest) req;
84          final HttpServletResponse response = (HttpServletResponse) res;
85  
86          NegotiateSecurityFilter.LOGGER.debug("{} {}, contentlength: {}", request.getMethod(), request.getRequestURI(),
87                  Integer.valueOf(request.getContentLength()));
88  
89          final AuthorizationHeader authorizationHeader = new AuthorizationHeader(request);
90  
91          // authenticate user
92          if (!authorizationHeader.isNull()
93                  && this.provider.isSecurityPackageSupported(authorizationHeader.getSecurityPackage())) {
94  
95              // log the user in using the token
96              IWindowsIdentity windowsIdentity;
97  
98              try {
99                  windowsIdentity = this.provider.doFilter(request, response);
100                 if (windowsIdentity == null) {
101                     return;
102                 }
103             } catch (final IOException e) {
104                 NegotiateSecurityFilter.LOGGER.warn("error logging in user: {}", e.getMessage());
105                 NegotiateSecurityFilter.LOGGER.trace("{}", e);
106                 this.sendUnauthorized(response, true);
107                 return;
108             }
109 
110             if (!this.allowGuestLogin && windowsIdentity.isGuest()) {
111                 NegotiateSecurityFilter.LOGGER.warn("guest login disabled: {}", windowsIdentity.getFqn());
112                 this.sendUnauthorized(response, true);
113                 return;
114             }
115 
116             try {
117                 NegotiateSecurityFilter.LOGGER.debug("logged in user: {} ({})", windowsIdentity.getFqn(), windowsIdentity.getSidString());
118 
119                 final WindowsPrincipal principal = new WindowsPrincipal(windowsIdentity, this.principalFormat,
120                         this.roleFormat);
121 
122                 NegotiateSecurityFilter.LOGGER.debug("roles: {}", principal.getRolesString());
123 
124                 final Authentication authentication = new WindowsAuthenticationToken(principal,
125                         this.grantedAuthorityFactory, this.defaultGrantedAuthority);
126 
127                 if (!this.setAuthentication(request, response, authentication)) {
128                     return;
129                 }
130 
131                 NegotiateSecurityFilter.LOGGER.info("successfully logged in user: {}", windowsIdentity.getFqn());
132 
133             } finally {
134                 windowsIdentity.dispose();
135             }
136         }
137 
138         chain.doFilter(request, response);
139     }
140 
141     /*
142      * Invoked when authentication towards ad was succesful to populate securitycontext Override to add service provider
143      * authorization checks.
144      * 
145      * @return if security context was set.
146      */
147     /**
148      * Sets the authentication.
149      *
150      * @param request
151      *            the request
152      * @param response
153      *            the response
154      * @param authentication
155      *            the authentication
156      * @return true, if successful
157      */
158     protected boolean setAuthentication(final HttpServletRequest request, final HttpServletResponse response,
159             final Authentication authentication) {
160         SecurityContextHolder.getContext().setAuthentication(authentication);
161         return true;
162     }
163 
164     /* (non-Javadoc)
165      * @see org.springframework.web.filter.GenericFilterBean#afterPropertiesSet()
166      */
167     @Override
168     public void afterPropertiesSet() throws ServletException {
169         super.afterPropertiesSet();
170 
171         if (this.provider == null) {
172             throw new ServletException("Missing NegotiateSecurityFilter.Provider");
173         }
174     }
175 
176     /**
177      * Send a 401 Unauthorized along with protocol authentication headers.
178      * 
179      * @param response
180      *            HTTP Response
181      * @param close
182      *            Close connection.
183      */
184     protected void sendUnauthorized(final HttpServletResponse response, final boolean close) {
185         try {
186             this.provider.sendUnauthorized(response);
187             if (close) {
188                 response.setHeader("Connection", "close");
189             } else {
190                 response.setHeader("Connection", "keep-alive");
191             }
192             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
193             response.flushBuffer();
194         } catch (final IOException e) {
195             throw new RuntimeException(e);
196         }
197     }
198 
199     /**
200      * Gets the principal format.
201      *
202      * @return the principal format
203      */
204     public PrincipalFormat getPrincipalFormat() {
205         return this.principalFormat;
206     }
207 
208     /**
209      * Sets the principal format enum.
210      *
211      * @param value
212      *            the new principal format enum
213      */
214     public void setPrincipalFormatEnum(final PrincipalFormat value) {
215         this.principalFormat = value;
216     }
217 
218     /**
219      * Sets the principal format.
220      *
221      * @param value
222      *            the new principal format
223      */
224     public void setPrincipalFormat(final String value) {
225         this.setPrincipalFormatEnum(PrincipalFormat.valueOf(value.toUpperCase(Locale.ENGLISH)));
226     }
227 
228     /**
229      * Gets the role format.
230      *
231      * @return the role format
232      */
233     public PrincipalFormat getRoleFormat() {
234         return this.roleFormat;
235     }
236 
237     /**
238      * Sets the role format enum.
239      *
240      * @param value
241      *            the new role format enum
242      */
243     public void setRoleFormatEnum(final PrincipalFormat value) {
244         this.roleFormat = value;
245     }
246 
247     /**
248      * Sets the role format.
249      *
250      * @param value
251      *            the new role format
252      */
253     public void setRoleFormat(final String value) {
254         this.setRoleFormatEnum(PrincipalFormat.valueOf(value.toUpperCase(Locale.ENGLISH)));
255     }
256 
257     /**
258      * Checks if is allow guest login.
259      *
260      * @return true, if is allow guest login
261      */
262     public boolean isAllowGuestLogin() {
263         return this.allowGuestLogin;
264     }
265 
266     /**
267      * Sets the allow guest login.
268      *
269      * @param value
270      *            the new allow guest login
271      */
272     public void setAllowGuestLogin(final boolean value) {
273         this.allowGuestLogin = value;
274     }
275 
276     /**
277      * Gets the provider.
278      *
279      * @return the provider
280      */
281     public SecurityFilterProviderCollection getProvider() {
282         return this.provider;
283     }
284 
285     /**
286      * Sets the provider.
287      *
288      * @param value
289      *            the new provider
290      */
291     public void setProvider(final SecurityFilterProviderCollection value) {
292         this.provider = value;
293     }
294 
295     /**
296      * Gets the granted authority factory.
297      *
298      * @return the granted authority factory
299      */
300     public GrantedAuthorityFactory getGrantedAuthorityFactory() {
301         return this.grantedAuthorityFactory;
302     }
303 
304     /**
305      * Sets the granted authority factory.
306      *
307      * @param value
308      *            the new granted authority factory
309      */
310     public void setGrantedAuthorityFactory(final GrantedAuthorityFactory value) {
311         this.grantedAuthorityFactory = value;
312     }
313 
314     /**
315      * Gets the default granted authority.
316      *
317      * @return the default granted authority
318      */
319     public GrantedAuthority getDefaultGrantedAuthority() {
320         return this.defaultGrantedAuthority;
321     }
322 
323     /**
324      * Sets the default granted authority.
325      *
326      * @param value
327      *            the new default granted authority
328      */
329     public void setDefaultGrantedAuthority(final GrantedAuthority value) {
330         this.defaultGrantedAuthority = value;
331     }
332 }