diff --git a/libresonic-main/src/main/java/org/libresonic/player/security/GlobalSecurityConfig.java b/libresonic-main/src/main/java/org/libresonic/player/security/GlobalSecurityConfig.java index 9920f1c3..f1244967 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/security/GlobalSecurityConfig.java +++ b/libresonic-main/src/main/java/org/libresonic/player/security/GlobalSecurityConfig.java @@ -8,6 +8,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; @@ -36,15 +37,14 @@ public class GlobalSecurityConfig extends GlobalAuthenticationConfigurerAdapter @Autowired private CsrfSecurityRequestMatcher csrfSecurityRequestMatcher; - @Autowired - LoginFailureLogger loginFailureLogger; - @Autowired SettingsService settingsService; @Autowired LibresonicUserDetailsContextMapper libresonicUserDetailsContextMapper; + @Autowired + ApplicationEventPublisher eventPublisher; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { @@ -115,7 +115,7 @@ public class GlobalSecurityConfig extends GlobalAuthenticationConfigurerAdapter RESTRequestParameterProcessingFilter restAuthenticationFilter = new RESTRequestParameterProcessingFilter(); restAuthenticationFilter.setAuthenticationManager(authenticationManagerBean()); restAuthenticationFilter.setSecurityService(securityService); - restAuthenticationFilter.setLoginFailureLogger(loginFailureLogger); + restAuthenticationFilter.setEventPublisher(eventPublisher); http = http.addFilterBefore(restAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); http diff --git a/libresonic-main/src/main/java/org/libresonic/player/security/LibresonicApplicationEventListener.java b/libresonic-main/src/main/java/org/libresonic/player/security/LoginFailureListener.java similarity index 80% rename from libresonic-main/src/main/java/org/libresonic/player/security/LibresonicApplicationEventListener.java rename to libresonic-main/src/main/java/org/libresonic/player/security/LoginFailureListener.java index dfeaee5f..7358a304 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/security/LibresonicApplicationEventListener.java +++ b/libresonic-main/src/main/java/org/libresonic/player/security/LoginFailureListener.java @@ -19,6 +19,8 @@ package org.libresonic.player.security; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.security.authentication.AbstractAuthenticationToken; @@ -29,9 +31,9 @@ import org.springframework.security.web.authentication.WebAuthenticationDetails; * @author Sindre Mehus * @version $Id$ */ -public class LibresonicApplicationEventListener implements ApplicationListener { +public class LoginFailureListener implements ApplicationListener { - private LoginFailureLogger loginFailureLogger; + private static final Logger LOG = LoggerFactory.getLogger(LoginFailureListener.class); @Override public void onApplicationEvent(ApplicationEvent event) { @@ -40,14 +42,10 @@ public class LibresonicApplicationEventListener implements ApplicationListener { AbstractAuthenticationToken token = (AbstractAuthenticationToken) event.getSource(); Object details = token.getDetails(); if (details instanceof WebAuthenticationDetails) { - loginFailureLogger.log(((WebAuthenticationDetails) details).getRemoteAddress(), String.valueOf(token.getPrincipal())); + LOG.info("Login failed from [" + ((WebAuthenticationDetails) details).getRemoteAddress() + "]"); } } } } - - public void setLoginFailureLogger(LoginFailureLogger loginFailureLogger) { - this.loginFailureLogger = loginFailureLogger; - } } diff --git a/libresonic-main/src/main/java/org/libresonic/player/security/LoginFailureLogger.java b/libresonic-main/src/main/java/org/libresonic/player/security/LoginFailureLogger.java deleted file mode 100644 index 2fce8bcd..00000000 --- a/libresonic-main/src/main/java/org/libresonic/player/security/LoginFailureLogger.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of Libresonic. - * - * Libresonic is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Libresonic is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Libresonic. If not, see . - * - * Copyright 2015 (C) Sindre Mehus - */ - -package org.libresonic.player.security; - -import org.libresonic.player.Logger; -import org.springframework.stereotype.Component; - -/** - * Logs login failures. Can be used by tools like fail2ban for blocking IP addresses. - * - * @author Sindre Mehus - * @version $Id$ - */ -@Component -public class LoginFailureLogger { - - private static final Logger LOG = Logger.getLogger(LoginFailureLogger.class); - - public void log(String remoteAddress, String username) { - LOG.info("Login failed for [" + username + "] from [" + remoteAddress + "]"); - } -} diff --git a/libresonic-main/src/main/java/org/libresonic/player/security/RESTRequestParameterProcessingFilter.java b/libresonic-main/src/main/java/org/libresonic/player/security/RESTRequestParameterProcessingFilter.java index e82c071b..31e59a00 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/security/RESTRequestParameterProcessingFilter.java +++ b/libresonic-main/src/main/java/org/libresonic/player/security/RESTRequestParameterProcessingFilter.java @@ -28,11 +28,15 @@ import org.libresonic.player.domain.User; import org.libresonic.player.domain.Version; import org.libresonic.player.service.SecurityService; import org.libresonic.player.util.StringUtil; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.security.web.util.matcher.RegexRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -60,7 +64,8 @@ public class RESTRequestParameterProcessingFilter implements Filter { private final JAXBWriter jaxbWriter = new JAXBWriter(); private AuthenticationManager authenticationManager; private SecurityService securityService; - private LoginFailureLogger loginFailureLogger; + private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); + private ApplicationEventPublisher eventPublisher; private static RequestMatcher requiresAuthenticationRequestMatcher = new RegexRequestMatcher("/rest/.+",null); @@ -110,15 +115,12 @@ public class RESTRequestParameterProcessingFilter implements Filter { } if (errorCode == null) { - errorCode = authenticate(username, password, salt, token, previousAuth); + errorCode = authenticate(httpRequest, username, password, salt, token, previousAuth); } if (errorCode == null) { chain.doFilter(request, response); } else { - if (errorCode == RESTController.ErrorCode.NOT_AUTHENTICATED) { - loginFailureLogger.log(request.getRemoteAddr(), username); - } SecurityContextHolder.getContext().setAuthentication(null); sendErrorXml(httpRequest, httpResponse, errorCode); } @@ -138,7 +140,7 @@ public class RESTRequestParameterProcessingFilter implements Filter { return null; } - private RESTController.ErrorCode authenticate(String username, String password, String salt, String token, Authentication previousAuth) { + private RESTController.ErrorCode authenticate(HttpServletRequest httpRequest, String username, String password, String salt, String token, Authentication previousAuth) { // Previously authenticated and username not overridden? if (username == null && previousAuth != null) { @@ -159,12 +161,14 @@ public class RESTRequestParameterProcessingFilter implements Filter { } if (password != null) { + UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); + authRequest.setDetails(authenticationDetailsSource.buildDetails(httpRequest)); try { - UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); Authentication authResult = authenticationManager.authenticate(authRequest); SecurityContextHolder.getContext().setAuthentication(authResult); return null; } catch (AuthenticationException x) { + eventPublisher.publishEvent(new AuthenticationFailureBadCredentialsEvent(authRequest, x)); return RESTController.ErrorCode.NOT_AUTHENTICATED; } } @@ -213,8 +217,7 @@ public class RESTRequestParameterProcessingFilter implements Filter { this.securityService = securityService; } - - public void setLoginFailureLogger(LoginFailureLogger loginFailureLogger) { - this.loginFailureLogger = loginFailureLogger; + public void setEventPublisher(ApplicationEventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; } } diff --git a/libresonic-main/src/main/resources/META-INF/spring.factories b/libresonic-main/src/main/resources/META-INF/spring.factories index c8b422f1..e2d4d747 100644 --- a/libresonic-main/src/main/resources/META-INF/spring.factories +++ b/libresonic-main/src/main/resources/META-INF/spring.factories @@ -1 +1,3 @@ -org.springframework.context.ApplicationListener=org.libresonic.player.spring.LoggingFileOverrideListener +org.springframework.context.ApplicationListener=\ + org.libresonic.player.spring.LoggingFileOverrideListener,\ + org.libresonic.player.security.LoginFailureListener