From fc01f8f83ce4c7866f89a12a3e1a019783eb7ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Cocula?= Date: Fri, 30 Dec 2016 19:29:01 +0100 Subject: [PATCH] Spring security configuration has moved to java. --- .../libresonic/player/boot/Application.java | 19 ---- .../player/security/LoginFailureLogger.java | 2 + .../RESTRequestParameterProcessingFilter.java | 46 +++++++--- .../player/security/WebSecurityConfig.java | 92 +++++++++++++++++++ .../resources/applicationContext-security.xml | 80 ---------------- .../src/main/webapp/WEB-INF/jsp/login.jsp | 2 +- 6 files changed, 130 insertions(+), 111 deletions(-) create mode 100644 libresonic-main/src/main/java/org/libresonic/player/security/WebSecurityConfig.java delete mode 100644 libresonic-main/src/main/resources/applicationContext-security.xml diff --git a/libresonic-main/src/main/java/org/libresonic/player/boot/Application.java b/libresonic-main/src/main/java/org/libresonic/player/boot/Application.java index e9caf628..dfb807f6 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/boot/Application.java +++ b/libresonic-main/src/main/java/org/libresonic/player/boot/Application.java @@ -1,12 +1,9 @@ package org.libresonic.player.boot; import org.directwebremoting.servlet.DwrServlet; -import org.libresonic.player.filter.RESTFilter; -import org.libresonic.player.security.RESTRequestParameterProcessingFilter; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.boot.web.support.SpringBootServletInitializer; import org.springframework.context.annotation.Bean; @@ -18,7 +15,6 @@ import org.springframework.context.annotation.ImportResource; @ImportResource(value = {"classpath:/applicationContext-service.xml", "classpath:/applicationContext-cache.xml", "classpath:/applicationContext-sonos.xml", - "classpath:/applicationContext-security.xml", "classpath:/libresonic-servlet.xml"}) public class Application extends SpringBootServletInitializer { @@ -34,21 +30,6 @@ public class Application extends SpringBootServletInitializer { return servlet; } - /** - * Registers the rest servlet filter. - * - * @return a registration bean. - */ - @Bean - public FilterRegistrationBean restFilterRegistration() { - FilterRegistrationBean registration = new FilterRegistrationBean(); - registration.setFilter(new RESTFilter()); - registration.addUrlPatterns("/rest/*"); - registration.setName("RESTFilter"); - return registration; - } - - @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 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 index 0e7b056e..2fce8bcd 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/security/LoginFailureLogger.java +++ b/libresonic-main/src/main/java/org/libresonic/player/security/LoginFailureLogger.java @@ -20,6 +20,7 @@ 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. @@ -27,6 +28,7 @@ import org.libresonic.player.Logger; * @author Sindre Mehus * @version $Id$ */ +@Component public class LoginFailureLogger { private static final Logger LOG = Logger.getLogger(LoginFailureLogger.class); 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 dd974919..5e99b1f5 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 @@ -27,15 +27,14 @@ import org.libresonic.player.controller.RESTController; import org.libresonic.player.domain.User; import org.libresonic.player.domain.Version; import org.libresonic.player.service.SecurityService; -import org.libresonic.player.service.SettingsService; import org.libresonic.player.util.StringUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Component; +import org.springframework.security.web.util.matcher.RegexRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; @@ -53,21 +52,22 @@ import java.io.IOException; * * @author Sindre Mehus */ -@Component(value = "restRequestParameterProcessingFilter") public class RESTRequestParameterProcessingFilter implements Filter { private static final Logger LOG = Logger.getLogger(RESTRequestParameterProcessingFilter.class); private final JAXBWriter jaxbWriter = new JAXBWriter(); - @Autowired - private ProviderManager authenticationManager; - @Autowired - private SettingsService settingsService; - @Autowired + private AuthenticationManager authenticationManager; private SecurityService securityService; - @Autowired private LoginFailureLogger loginFailureLogger; + private static RequestMatcher requiresAuthenticationRequestMatcher = new RegexRequestMatcher("/rest/.+\\.view\\??.*",null); + + protected boolean requiresAuthentication(HttpServletRequest request, + HttpServletResponse response) { + return requiresAuthenticationRequestMatcher.matches(request); + } + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (!(request instanceof HttpServletRequest)) { throw new ServletException("Can only process HttpServletRequest"); @@ -79,6 +79,13 @@ public class RESTRequestParameterProcessingFilter implements Filter { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; + if (!requiresAuthentication(httpRequest, httpResponse)) { + chain.doFilter(request, response); + + return; + } + + String username = StringUtils.trimToNull(httpRequest.getParameter("u")); String password = decrypt(StringUtils.trimToNull(httpRequest.getParameter("p"))); String salt = StringUtils.trimToNull(httpRequest.getParameter("s")); @@ -192,4 +199,21 @@ public class RESTRequestParameterProcessingFilter implements Filter { public void destroy() { } + + public void setAuthenticationManager(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + public SecurityService getSecurityService() { + return securityService; + } + + public void setSecurityService(SecurityService securityService) { + this.securityService = securityService; + } + + + public void setLoginFailureLogger(LoginFailureLogger loginFailureLogger) { + this.loginFailureLogger = loginFailureLogger; + } } diff --git a/libresonic-main/src/main/java/org/libresonic/player/security/WebSecurityConfig.java b/libresonic-main/src/main/java/org/libresonic/player/security/WebSecurityConfig.java new file mode 100644 index 00000000..890c527d --- /dev/null +++ b/libresonic-main/src/main/java/org/libresonic/player/security/WebSecurityConfig.java @@ -0,0 +1,92 @@ +package org.libresonic.player.security; + +import org.libresonic.player.service.SecurityService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@EnableWebSecurity +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private SecurityService securityService; + @Autowired + private CsrfSecurityRequestMatcher csrfSecurityRequestMatcher; + @Autowired + LoginFailureLogger loginFailureLogger; + + @Override + @Bean(name = "authenticationManager") + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(securityService); + } + + + @Override + protected void configure(HttpSecurity http) throws Exception { + + RESTRequestParameterProcessingFilter restAuthenticationFilter = new RESTRequestParameterProcessingFilter(); + restAuthenticationFilter.setAuthenticationManager((AuthenticationManager) getApplicationContext().getBean("authenticationManager")); + restAuthenticationFilter.setSecurityService(securityService); + restAuthenticationFilter.setLoginFailureLogger(loginFailureLogger); + http = http.addFilterBefore(restAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + http + .csrf() + .requireCsrfProtectionMatcher(csrfSecurityRequestMatcher) + .and().headers() + .frameOptions() + .sameOrigin() + .and().authorizeRequests() + .antMatchers("recover.view", "accessDenied.view", + "coverArt.view", "/hls/**", "/stream/**", "/ws/**", + "/share/**", "/style/**", "/icons/**", + "/flash/**", "/script/**", "/sonos/**", "/crossdomain.xml") + .permitAll() + .antMatchers("/personalSettings.view", "/passwordSettings.view", + "/playerSettings.view", "/shareSettings.view") + .hasRole("SETTINGS") + .antMatchers("/generalSettings.view","/advancedSettings.view","/userSettings.view", + "/musicFolderSettings.view","/networkSettings.view") + .hasRole("ADMIN") + .antMatchers("/deletePlaylist.view","/savePlaylist.view") + .hasRole("PLAYLIST") + .antMatchers("/download.view") + .hasRole("DOWNLOAD") + .antMatchers("/upload.view") + .hasRole("UPLOAD") + .antMatchers("/createShare.view") + .hasRole("SHARE") + .antMatchers("/changeCoverArt.view","/editTags.view") + .hasRole("COVERART") + .antMatchers("/setMusicFileInfo.view") + .hasRole("COMMENT") + .antMatchers("/podcastReceiverAdmin.view") + .hasRole("PODCAST") + .antMatchers("/**") + .hasRole("USER") + .anyRequest().authenticated() + .and().formLogin() + .loginPage("/login") + .permitAll() + .defaultSuccessUrl("/index.view") + .failureUrl("/login?error=1") + .usernameParameter("j_username") + .passwordParameter("j_password") + .and().rememberMe().userDetailsService(securityService).key("libresonic"); + + } +} \ No newline at end of file diff --git a/libresonic-main/src/main/resources/applicationContext-security.xml b/libresonic-main/src/main/resources/applicationContext-security.xml deleted file mode 100644 index 7831aa7b..00000000 --- a/libresonic-main/src/main/resources/applicationContext-security.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/libresonic-main/src/main/webapp/WEB-INF/jsp/login.jsp b/libresonic-main/src/main/webapp/WEB-INF/jsp/login.jsp index d3081ef7..16acce0a 100644 --- a/libresonic-main/src/main/webapp/WEB-INF/jsp/login.jsp +++ b/libresonic-main/src/main/webapp/WEB-INF/jsp/login.jsp @@ -12,7 +12,7 @@ -
+