View Javadoc
1   /*
2    * MIT License
3    *
4    * Copyright (c) 2010-2022 The Waffle Project Contributors: https://github.com/Waffle/waffle/graphs/contributors
5    *
6    * Permission is hereby granted, free of charge, to any person obtaining a copy
7    * of this software and associated documentation files (the "Software"), to deal
8    * in the Software without restriction, including without limitation the rights
9    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10   * copies of the Software, and to permit persons to whom the Software is
11   * furnished to do so, subject to the following conditions:
12   *
13   * The above copyright notice and this permission notice shall be included in all
14   * copies or substantial portions of the Software.
15   *
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22   * SOFTWARE.
23   */
24  package waffle.spring;
25  
26  import java.io.IOException;
27  
28  import javax.servlet.ServletException;
29  import javax.servlet.http.HttpServletRequest;
30  import javax.servlet.http.HttpServletResponse;
31  
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  import org.springframework.security.access.AccessDeniedException;
35  import org.springframework.security.authentication.AuthenticationManager;
36  import org.springframework.security.core.Authentication;
37  import org.springframework.security.core.AuthenticationException;
38  import org.springframework.security.core.context.SecurityContextHolder;
39  import org.springframework.security.web.access.AccessDeniedHandler;
40  import org.springframework.security.web.authentication.AuthenticationFailureHandler;
41  import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
42  
43  /**
44   * Supports optional injection of spring security entities, allowing Waffle to act as an interface towards an identity
45   * provider(the AD).
46   * <p>
47   * <i>Below mentioned entities are verified to be set before invoked, inherited entities are not.</i>
48   * <ul>
49   * <li>The <code>AuthenticationManager</code> allows for the service provider to authorize the principal.</li>
50   * <li>The <code>authenticationSuccessHandler</code> allows for the service provider to further populate the
51   * {@link org.springframework.security.core.Authentication Authentication} object.</li>
52   * <li>The <code>AuthenticationFailureHandler</code> is called if the AuthenticationManager throws an
53   * {@link org.springframework.security.core.AuthenticationException AuthenticationException}.</li>
54   * <li>The <code>AccessDeniedHandler</code> is called if the AuthenticationManager throws an
55   * {@link org.springframework.security.access.AccessDeniedException AccessDeniedException}.</li>
56   * </ul>
57   * Example configuration:
58   *
59   * <pre>
60   * {@code
61   * <bean id="waffleNegotiateSecurityFilter"
62   *      class="waffle.spring.DelegatingNegotiateSecurityFilter"
63   *      scope="tenant">
64   *      <property name="allowGuestLogin" value="false" />
65   *      <property name="Provider" ref="waffleSecurityFilterProviderCollection" />
66   *      <property name="authenticationManager" ref="authenticationManager" />
67   *      <property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
68   *      <property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
69   *      <property name="accessDeniedHandler" ref="accessDeniedHandler" />
70   *      <property name="defaultGrantedAuthority">
71   *          <null />
72   *      </property>
73   * </bean>
74   * }
75   * </pre>
76   */
77  public class DelegatingNegotiateSecurityFilter extends NegotiateSecurityFilter {
78  
79      /** The Constant LOGGER. */
80      private static final Logger LOGGER = LoggerFactory.getLogger(DelegatingNegotiateSecurityFilter.class);
81  
82      /** The authentication manager. */
83      private AuthenticationManager authenticationManager;
84  
85      /** The authentication success handler. */
86      private AuthenticationSuccessHandler authenticationSuccessHandler;
87  
88      /** The authentication failure handler. */
89      private AuthenticationFailureHandler authenticationFailureHandler;
90  
91      /** The access denied handler. */
92      private AccessDeniedHandler accessDeniedHandler;
93  
94      /**
95       * Instantiates a new delegating negotiate security filter.
96       */
97      public DelegatingNegotiateSecurityFilter() {
98          super();
99          DelegatingNegotiateSecurityFilter.LOGGER.debug("[waffle.spring.NegotiateSecurityFilter] loaded");
100     }
101 
102     /**
103      * Gets the access denied handler.
104      *
105      * @return the accessDeniedHandler
106      */
107     public AccessDeniedHandler getAccessDeniedHandler() {
108         return this.accessDeniedHandler;
109     }
110 
111     /**
112      * Sets the access denied handler.
113      *
114      * @param value
115      *            the accessDeniedHandler to set
116      */
117     public void setAccessDeniedHandler(final AccessDeniedHandler value) {
118         this.accessDeniedHandler = value;
119     }
120 
121     /**
122      * Gets the authentication failure handler.
123      *
124      * @return the authenticationFailureHandler
125      */
126     public AuthenticationFailureHandler getAuthenticationFailureHandler() {
127         return this.authenticationFailureHandler;
128     }
129 
130     /**
131      * Sets the authentication failure handler.
132      *
133      * @param value
134      *            the authenticationFailureHandler to set
135      */
136     public void setAuthenticationFailureHandler(final AuthenticationFailureHandler value) {
137         this.authenticationFailureHandler = value;
138     }
139 
140     @Override
141     protected boolean setAuthentication(final HttpServletRequest request, final HttpServletResponse response,
142             final Authentication authentication) {
143         try {
144             Authentication delegateAuthentication = authentication;
145             if (this.authenticationManager != null) {
146                 DelegatingNegotiateSecurityFilter.LOGGER.debug("Delegating to custom authenticationmanager");
147                 delegateAuthentication = this.authenticationManager.authenticate(authentication);
148             }
149             SecurityContextHolder.getContext().setAuthentication(delegateAuthentication);
150             if (this.authenticationSuccessHandler != null) {
151                 try {
152                     this.authenticationSuccessHandler.onAuthenticationSuccess(request, response,
153                             delegateAuthentication);
154                 } catch (final IOException | ServletException e) {
155                     DelegatingNegotiateSecurityFilter.LOGGER.warn("Error calling authenticationSuccessHandler: {}",
156                             e.getMessage());
157                     DelegatingNegotiateSecurityFilter.LOGGER.trace("", e);
158                     return false;
159                 }
160             }
161         } catch (final AuthenticationException e) {
162             DelegatingNegotiateSecurityFilter.LOGGER
163                     .warn("Error authenticating user in custom authenticationmanager: {}", e.getMessage());
164             this.sendAuthenticationFailed(request, response, e);
165             return false;
166         } catch (final AccessDeniedException e) {
167             DelegatingNegotiateSecurityFilter.LOGGER.warn("Error authorizing user in custom authenticationmanager: {}",
168                     e.getMessage());
169             this.sendAccessDenied(request, response, e);
170             return false;
171         }
172         return true;
173     }
174 
175     @Override
176     public void afterPropertiesSet() throws ServletException {
177         super.afterPropertiesSet();
178 
179         if (this.getProvider() == null) {
180             throw new ServletException("Missing NegotiateSecurityFilter.Provider");
181         }
182     }
183 
184     /**
185      * Forward to authenticationFailureHandler.
186      *
187      * @param request
188      *            the request
189      * @param response
190      *            HTTP Response
191      * @param ae
192      *            the ae
193      */
194     private void sendAuthenticationFailed(final HttpServletRequest request, final HttpServletResponse response,
195             final AuthenticationException ae) {
196         if (this.authenticationFailureHandler != null) {
197             try {
198                 this.authenticationFailureHandler.onAuthenticationFailure(request, response, ae);
199                 return;
200             } catch (final IOException e) {
201                 DelegatingNegotiateSecurityFilter.LOGGER.warn("IOException invoking authenticationFailureHandler: {}",
202                         e.getMessage());
203                 DelegatingNegotiateSecurityFilter.LOGGER.trace("", e);
204             } catch (final ServletException e) {
205                 DelegatingNegotiateSecurityFilter.LOGGER
206                         .warn("ServletException invoking authenticationFailureHandler: {}", e.getMessage());
207                 DelegatingNegotiateSecurityFilter.LOGGER.trace("", e);
208             }
209         }
210         super.sendUnauthorized(response, true);
211     }
212 
213     /**
214      * Forward to accessDeniedHandler.
215      *
216      * @param request
217      *            the request
218      * @param response
219      *            HTTP Response
220      * @param ae
221      *            the ae
222      */
223     private void sendAccessDenied(final HttpServletRequest request, final HttpServletResponse response,
224             final AccessDeniedException ae) {
225         if (this.accessDeniedHandler != null) {
226             try {
227                 this.accessDeniedHandler.handle(request, response, ae);
228                 return;
229             } catch (final IOException e) {
230                 DelegatingNegotiateSecurityFilter.LOGGER.warn("IOException invoking accessDeniedHandler: {}",
231                         e.getMessage());
232                 DelegatingNegotiateSecurityFilter.LOGGER.trace("", e);
233             } catch (final ServletException e) {
234                 DelegatingNegotiateSecurityFilter.LOGGER.warn("ServletException invoking accessDeniedHandler: {}",
235                         e.getMessage());
236                 DelegatingNegotiateSecurityFilter.LOGGER.trace("", e);
237             }
238         }
239         // fallback
240         this.sendUnauthorized(response, true);
241     }
242 
243     /**
244      * Gets the authentication success handler.
245      *
246      * @return the authenticationSuccessHandler
247      */
248     public AuthenticationSuccessHandler getAuthenticationSuccessHandler() {
249         return this.authenticationSuccessHandler;
250     }
251 
252     /**
253      * Sets the authentication success handler.
254      *
255      * @param value
256      *            the authenticationSuccessHandler to set
257      */
258     public void setAuthenticationSuccessHandler(final AuthenticationSuccessHandler value) {
259         this.authenticationSuccessHandler = value;
260     }
261 
262     /**
263      * Gets the authentication manager.
264      *
265      * @return the authenticationManager
266      */
267     public AuthenticationManager getAuthenticationManager() {
268         return this.authenticationManager;
269     }
270 
271     /**
272      * Sets the authentication manager.
273      *
274      * @param value
275      *            the authenticationManager to set
276      */
277     public void setAuthenticationManager(final AuthenticationManager value) {
278         this.authenticationManager = value;
279     }
280 
281 }