Spring security configuration has moved to java.
This commit is contained in:
@@ -1,12 +1,9 @@
|
|||||||
package org.libresonic.player.boot;
|
package org.libresonic.player.boot;
|
||||||
|
|
||||||
import org.directwebremoting.servlet.DwrServlet;
|
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.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
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.servlet.ServletRegistrationBean;
|
||||||
import org.springframework.boot.web.support.SpringBootServletInitializer;
|
import org.springframework.boot.web.support.SpringBootServletInitializer;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -18,7 +15,6 @@ import org.springframework.context.annotation.ImportResource;
|
|||||||
@ImportResource(value = {"classpath:/applicationContext-service.xml",
|
@ImportResource(value = {"classpath:/applicationContext-service.xml",
|
||||||
"classpath:/applicationContext-cache.xml",
|
"classpath:/applicationContext-cache.xml",
|
||||||
"classpath:/applicationContext-sonos.xml",
|
"classpath:/applicationContext-sonos.xml",
|
||||||
"classpath:/applicationContext-security.xml",
|
|
||||||
"classpath:/libresonic-servlet.xml"})
|
"classpath:/libresonic-servlet.xml"})
|
||||||
public class Application extends SpringBootServletInitializer {
|
public class Application extends SpringBootServletInitializer {
|
||||||
|
|
||||||
@@ -34,21 +30,6 @@ public class Application extends SpringBootServletInitializer {
|
|||||||
return servlet;
|
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
|
@Override
|
||||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
package org.libresonic.player.security;
|
package org.libresonic.player.security;
|
||||||
|
|
||||||
import org.libresonic.player.Logger;
|
import org.libresonic.player.Logger;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs login failures. Can be used by tools like fail2ban for blocking IP addresses.
|
* 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
|
* @author Sindre Mehus
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
|
@Component
|
||||||
public class LoginFailureLogger {
|
public class LoginFailureLogger {
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(LoginFailureLogger.class);
|
private static final Logger LOG = Logger.getLogger(LoginFailureLogger.class);
|
||||||
|
|||||||
+35
-11
@@ -27,15 +27,14 @@ import org.libresonic.player.controller.RESTController;
|
|||||||
import org.libresonic.player.domain.User;
|
import org.libresonic.player.domain.User;
|
||||||
import org.libresonic.player.domain.Version;
|
import org.libresonic.player.domain.Version;
|
||||||
import org.libresonic.player.service.SecurityService;
|
import org.libresonic.player.service.SecurityService;
|
||||||
import org.libresonic.player.service.SettingsService;
|
|
||||||
import org.libresonic.player.util.StringUtil;
|
import org.libresonic.player.util.StringUtil;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.ProviderManager;
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
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.*;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@@ -53,21 +52,22 @@ import java.io.IOException;
|
|||||||
*
|
*
|
||||||
* @author Sindre Mehus
|
* @author Sindre Mehus
|
||||||
*/
|
*/
|
||||||
@Component(value = "restRequestParameterProcessingFilter")
|
|
||||||
public class RESTRequestParameterProcessingFilter implements Filter {
|
public class RESTRequestParameterProcessingFilter implements Filter {
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(RESTRequestParameterProcessingFilter.class);
|
private static final Logger LOG = Logger.getLogger(RESTRequestParameterProcessingFilter.class);
|
||||||
|
|
||||||
private final JAXBWriter jaxbWriter = new JAXBWriter();
|
private final JAXBWriter jaxbWriter = new JAXBWriter();
|
||||||
@Autowired
|
private AuthenticationManager authenticationManager;
|
||||||
private ProviderManager authenticationManager;
|
|
||||||
@Autowired
|
|
||||||
private SettingsService settingsService;
|
|
||||||
@Autowired
|
|
||||||
private SecurityService securityService;
|
private SecurityService securityService;
|
||||||
@Autowired
|
|
||||||
private LoginFailureLogger loginFailureLogger;
|
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 {
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||||
if (!(request instanceof HttpServletRequest)) {
|
if (!(request instanceof HttpServletRequest)) {
|
||||||
throw new ServletException("Can only process HttpServletRequest");
|
throw new ServletException("Can only process HttpServletRequest");
|
||||||
@@ -79,6 +79,13 @@ public class RESTRequestParameterProcessingFilter implements Filter {
|
|||||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||||
|
|
||||||
|
if (!requiresAuthentication(httpRequest, httpResponse)) {
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
String username = StringUtils.trimToNull(httpRequest.getParameter("u"));
|
String username = StringUtils.trimToNull(httpRequest.getParameter("u"));
|
||||||
String password = decrypt(StringUtils.trimToNull(httpRequest.getParameter("p")));
|
String password = decrypt(StringUtils.trimToNull(httpRequest.getParameter("p")));
|
||||||
String salt = StringUtils.trimToNull(httpRequest.getParameter("s"));
|
String salt = StringUtils.trimToNull(httpRequest.getParameter("s"));
|
||||||
@@ -192,4 +199,21 @@ public class RESTRequestParameterProcessingFilter implements Filter {
|
|||||||
public void destroy() {
|
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>
|
</head>
|
||||||
<body class="mainframe bgcolor1" onload="document.getElementById('j_username').focus()">
|
<body class="mainframe bgcolor1" onload="document.getElementById('j_username').focus()">
|
||||||
|
|
||||||
<form action="login" method="POST">
|
<form action="/login" method="POST">
|
||||||
|
|
||||||
<sec:csrfInput />
|
<sec:csrfInput />
|
||||||
<div class="bgcolor2 shadow" align="center" style="padding:20px 50px 20px 50px; margin-top:100px;margin-left:50px;margin-right:50px">
|
<div class="bgcolor2 shadow" align="center" style="padding:20px 50px 20px 50px; margin-top:100px;margin-left:50px;margin-right:50px">
|
||||||
|
|||||||
Reference in New Issue
Block a user