Spring security configuration has moved to java.

master
Rémi Cocula 8 years ago
parent 97f3607b1c
commit fc01f8f83c
  1. 19
      libresonic-main/src/main/java/org/libresonic/player/boot/Application.java
  2. 2
      libresonic-main/src/main/java/org/libresonic/player/security/LoginFailureLogger.java
  3. 46
      libresonic-main/src/main/java/org/libresonic/player/security/RESTRequestParameterProcessingFilter.java
  4. 92
      libresonic-main/src/main/java/org/libresonic/player/security/WebSecurityConfig.java
  5. 80
      libresonic-main/src/main/resources/applicationContext-security.xml
  6. 2
      libresonic-main/src/main/webapp/WEB-INF/jsp/login.jsp

@ -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) {

@ -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);

@ -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;
}
}

@ -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");
}
}

@ -1,80 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.1.xsd">
<security:http auto-config='true'>
<security:csrf request-matcher-ref="csrfSecurityRequestMatcher"/>
<security:headers>
<security:frame-options policy="SAMEORIGIN"/>
</security:headers>
<!-- permitAll -->
<security:intercept-url pattern="/login.*" access="permitAll"/>
<security:intercept-url pattern="/recover.view" access="permitAll" />
<security:intercept-url pattern="/accessDenied.view" access="permitAll" />
<security:intercept-url pattern="/coverArt.view" access="permitAll" />
<security:intercept-url pattern="/hls/**" access="permitAll" />
<security:intercept-url pattern="/stream/**" access="permitAll" />
<security:intercept-url pattern="/ws/**" access="permitAll" />
<security:intercept-url pattern="/rest/**" access="permitAll" />
<security:intercept-url pattern="/share/**" access="permitAll" />
<security:intercept-url pattern="/style/**" access="permitAll" />
<security:intercept-url pattern="/icons/**" access="permitAll" />
<security:intercept-url pattern="/flash/**" access="permitAll" />
<security:intercept-url pattern="/script/**" access="permitAll" />
<security:intercept-url pattern="/sonos/**" access="permitAll" />
<security:intercept-url pattern="/crossdomain.xml" access="permitAll" />
<!-- hasRole('ROLE_SETTINGS') -->
<security:intercept-url pattern="/personalSettings.view" access="hasRole('ROLE_SETTINGS')" />
<security:intercept-url pattern="/passwordSettings.view" access="hasRole('ROLE_SETTINGS')" />
<security:intercept-url pattern="/playerSettings.view" access="hasRole('ROLE_SETTINGS')" />
<security:intercept-url pattern="/shareSettings.view" access="hasRole('ROLE_SETTINGS')" />
<!-- hasRole('ROLE_ADMIN') -->
<security:intercept-url pattern="/generalSettings.view" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/advancedSettings.view" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/userSettings.view" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/musicFolderSettings.view" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/networkSettings.view" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/dlnaSettings.view" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/sonosSettings.view" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/transcodingSettings.view" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/internetRadioSettings.view" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/podcastSettings.view" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/db.view" access="hasRole('ROLE_ADMIN')" />
<!-- MISC -->
<security:intercept-url pattern="/deletePlaylist.view" access="hasRole('ROLE_PLAYLIST')" />
<security:intercept-url pattern="/savePlaylist.view" access="hasRole('ROLE_PLAYLIST')" />
<security:intercept-url pattern="/download.view" access="hasRole('ROLE_DOWNLOAD')" />
<security:intercept-url pattern="/upload.view" access="hasRole('ROLE_UPLOAD')" />
<security:intercept-url pattern="/createShare.view" access="hasRole('ROLE_SHARE')" />
<security:intercept-url pattern="/changeCoverArt.view" access="hasRole('ROLE_COVERART')" />
<security:intercept-url pattern="/editTags.view" access="hasRole('ROLE_COVERART')" />
<security:intercept-url pattern="/setMusicFileInfo.view" access="hasRole('ROLE_COMMENT')" />
<security:intercept-url pattern="/podcastReceiverAdmin.view" access="hasRole('ROLE_PODCAST')" />
<!-- ROLE_USER -->
<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<security:form-login login-page="/login.view"
default-target-url="/index.view"
authentication-failure-url="/login.view?error=1"
always-use-default-target="true"
username-parameter="j_username"
password-parameter="j_password"/>
<security:remember-me user-service-ref="securityService" key="libresonic"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="securityService" />
</security:authentication-manager>
</beans>

@ -12,7 +12,7 @@
</head>
<body class="mainframe bgcolor1" onload="document.getElementById('j_username').focus()">
<form action="login" method="POST">
<form action="/login" method="POST">
<sec:csrfInput />
<div class="bgcolor2 shadow" align="center" style="padding:20px 50px 20px 50px; margin-top:100px;margin-left:50px;margin-right:50px">

Loading…
Cancel
Save