Skip to content

Commit a5ec116

Browse files
committed
SEC-1919: Log error when fail to communicate with LDAP
Previously communication errors with LDAP were only logged at debug level. Communication errors (along with other non-authenticated related NamingExceptions) are now logged as error messages. We created an InternalAuthetnicationServiceException to represent errors that should be logged as errors to distinguish between internal and external authentication failures. For example, we do not want an OpenID Provider being able to report errors that cause our logs to fill up. However, an LDAP system is internal and should be trusted so logging at an error level makes sense.
1 parent a19cc8f commit a5ec116

File tree

6 files changed

+94
-2
lines changed

6 files changed

+94
-2
lines changed

core/src/main/java/org/springframework/security/authentication/AuthenticationServiceException.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* This might be thrown if a backend authentication repository is unavailable, for example.
2424
*
2525
* @author Ben Alex
26+
* @see InternalAuthenticationServiceException
2627
*/
2728
public class AuthenticationServiceException extends AuthenticationException {
2829
//~ Constructors ===================================================================================================
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2002-2012 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. 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 distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
package org.springframework.security.authentication;
14+
15+
/**
16+
* <p>
17+
* Thrown if an authentication request could not be processed due to a system problem that occurred internally. It
18+
* differs from {@link AuthenticationServiceException} in that it would not be thrown if an external system has an
19+
* internal error or failure. This ensures that we can handle errors that are within our control distinctly from errors
20+
* of other systems. The advantage to this distinction is that the unrusted external system should not be able to fill
21+
* up logs and cause excessive IO. However, an internal system should report errors.
22+
* </p>
23+
* <p>
24+
* This might be thrown if a backend authentication repository is unavailable, for example. However, it would not be
25+
* thrown in the event that an error occurred when validating an OpenID response with an OpenID Provider.
26+
* </p>
27+
*
28+
* @author Rob Winch
29+
*
30+
*/
31+
public class InternalAuthenticationServiceException extends AuthenticationServiceException {
32+
33+
public InternalAuthenticationServiceException(String message, Throwable cause) {
34+
super(message, cause);
35+
}
36+
37+
public InternalAuthenticationServiceException(String message) {
38+
super(message);
39+
}
40+
}

ldap/src/main/java/org/springframework/security/ldap/authentication/LdapAuthenticationProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
import org.springframework.ldap.NamingException;
1919
import org.springframework.ldap.core.DirContextOperations;
20-
import org.springframework.security.authentication.AuthenticationServiceException;
2120
import org.springframework.security.authentication.BadCredentialsException;
21+
import org.springframework.security.authentication.InternalAuthenticationServiceException;
2222
import org.springframework.security.authentication.LockedException;
2323
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
2424
import org.springframework.security.core.GrantedAuthority;
@@ -188,7 +188,7 @@ protected DirContextOperations doAuthentication(UsernamePasswordAuthenticationTo
188188
throw notFound;
189189
}
190190
} catch (NamingException ldapAccessFailure) {
191-
throw new AuthenticationServiceException(ldapAccessFailure.getMessage(), ldapAccessFailure);
191+
throw new InternalAuthenticationServiceException(ldapAccessFailure.getMessage(), ldapAccessFailure);
192192
}
193193
}
194194

ldap/src/test/java/org/springframework/security/ldap/authentication/LdapAuthenticationProviderTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
import java.util.*;
2222

2323
import org.junit.Test;
24+
import org.springframework.ldap.CommunicationException;
2425
import org.springframework.ldap.core.DirContextAdapter;
2526
import org.springframework.ldap.core.DirContextOperations;
2627
import org.springframework.ldap.core.DistinguishedName;
2728
import org.springframework.security.authentication.BadCredentialsException;
29+
import org.springframework.security.authentication.InternalAuthenticationServiceException;
2830
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
2931
import org.springframework.security.core.Authentication;
3032
import org.springframework.security.core.GrantedAuthority;
@@ -39,6 +41,7 @@
3941
* Tests {@link LdapAuthenticationProvider}.
4042
*
4143
* @author Luke Taylor
44+
* @author Rob Winch
4245
*/
4346
public class LdapAuthenticationProviderTests {
4447

@@ -147,6 +150,22 @@ public void useWithNullAuthoritiesPopulatorReturnsCorrectRole() {
147150
assertTrue(AuthorityUtils.authorityListToSet(user.getAuthorities()).contains("ROLE_FROM_ENTRY"));
148151
}
149152

153+
@Test
154+
public void authenticateWithNamingException() {
155+
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("ben", "benspassword");
156+
LdapAuthenticator mockAuthenticator = mock(LdapAuthenticator.class);
157+
CommunicationException expectedCause = new CommunicationException(new javax.naming.CommunicationException());
158+
when(mockAuthenticator.authenticate(authRequest)).thenThrow(expectedCause);
159+
160+
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(mockAuthenticator);
161+
try {
162+
ldapProvider.authenticate(authRequest);
163+
fail("Expected Exception");
164+
} catch(InternalAuthenticationServiceException success) {
165+
assertSame(expectedCause, success.getCause());
166+
}
167+
}
168+
150169
//~ Inner Classes ==================================================================================================
151170

152171
class MockAuthenticator implements LdapAuthenticator {

web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.context.support.MessageSourceAccessor;
3232
import org.springframework.security.authentication.AuthenticationDetailsSource;
3333
import org.springframework.security.authentication.AuthenticationManager;
34+
import org.springframework.security.authentication.InternalAuthenticationServiceException;
3435
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
3536
import org.springframework.security.core.Authentication;
3637
import org.springframework.security.core.AuthenticationException;
@@ -197,6 +198,11 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
197198
return;
198199
}
199200
sessionStrategy.onAuthentication(authResult, request, response);
201+
} catch(InternalAuthenticationServiceException failed) {
202+
logger.error("An internal error occurred while trying to authenticate the user.", failed);
203+
unsuccessfulAuthentication(request, response, failed);
204+
205+
return;
200206
}
201207
catch (AuthenticationException failed) {
202208
// Authentication failed

web/src/test/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilterTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.junit.Assert.*;
1919
import static org.mockito.Mockito.*;
2020

21+
import org.apache.commons.logging.Log;
2122
import org.junit.After;
2223
import org.junit.Before;
2324
import org.junit.Test;
@@ -26,13 +27,15 @@
2627
import org.springframework.mock.web.MockHttpServletResponse;
2728
import org.springframework.security.authentication.AuthenticationManager;
2829
import org.springframework.security.authentication.BadCredentialsException;
30+
import org.springframework.security.authentication.InternalAuthenticationServiceException;
2931
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
3032
import org.springframework.security.core.Authentication;
3133
import org.springframework.security.core.AuthenticationException;
3234
import org.springframework.security.core.authority.AuthorityUtils;
3335
import org.springframework.security.core.context.SecurityContextHolder;
3436
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
3537
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
38+
import org.springframework.test.util.ReflectionTestUtils;
3639

3740
import javax.servlet.FilterChain;
3841
import javax.servlet.ServletException;
@@ -49,6 +52,7 @@
4952
*
5053
* @author Ben Alex
5154
* @author Luke Taylor
55+
* @author Rob Winch
5256
*/
5357
@SuppressWarnings("deprecation")
5458
public class AbstractAuthenticationProcessingFilterTests {
@@ -352,6 +356,28 @@ public void testLoginErrorWithNoFailureUrlSendsUnauthorizedStatus() throws Excep
352356
assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getStatus());
353357
}
354358

359+
/**
360+
* SEC-1919
361+
*/
362+
@Test
363+
public void loginErrorWithInternAuthenticationServiceExceptionLogsError() throws Exception {
364+
MockHttpServletRequest request = createMockAuthenticationRequest();
365+
366+
MockFilterChain chain = new MockFilterChain(true);
367+
MockHttpServletResponse response = new MockHttpServletResponse();
368+
369+
Log logger = mock(Log.class);
370+
MockAuthenticationFilter filter = new MockAuthenticationFilter(false);
371+
ReflectionTestUtils.setField(filter, "logger", logger);
372+
filter.exceptionToThrow = new InternalAuthenticationServiceException("Mock requested to do so");
373+
successHandler.setDefaultTargetUrl("http://monkeymachine.co.uk/");
374+
filter.setAuthenticationSuccessHandler(successHandler);
375+
376+
filter.doFilter(request, response, chain);
377+
378+
verify(logger).error(anyString(), eq(filter.exceptionToThrow));
379+
assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getStatus());
380+
}
355381

356382
//~ Inner Classes ==================================================================================================
357383

0 commit comments

Comments
 (0)