Basic auth entry point added, so e.g. a browser shows basic auth pop-up. Also done some clean-up/formatting.

Signed-off-by: Dominic Schabel <dominic.schabel@bosch-si.com>
This commit is contained in:
Dominic Schabel
2016-06-02 18:17:38 +02:00
parent 5d4de130b4
commit 3cc6fdc21f
2 changed files with 62 additions and 48 deletions

View File

@@ -8,6 +8,10 @@
*/ */
package org.eclipse.hawkbit.autoconfigure.security; package org.eclipse.hawkbit.autoconfigure.security;
import static com.google.common.collect.Lists.newArrayList;
import static org.springframework.context.annotation.AdviceMode.ASPECTJ;
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
@@ -46,13 +50,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.embedded.FilterRegistrationBean; import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean; import org.springframework.boot.context.embedded.ServletListenerRegistrationBean;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
@@ -72,6 +75,7 @@ import org.springframework.security.web.access.intercept.FilterSecurityIntercept
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter; import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy; import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter; import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode; import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
@@ -83,20 +87,17 @@ import org.vaadin.spring.security.web.VaadinRedirectStrategy;
import org.vaadin.spring.security.web.authentication.VaadinAuthenticationSuccessHandler; import org.vaadin.spring.security.web.authentication.VaadinAuthenticationSuccessHandler;
import org.vaadin.spring.security.web.authentication.VaadinUrlAuthenticationSuccessHandler; import org.vaadin.spring.security.web.authentication.VaadinUrlAuthenticationSuccessHandler;
import com.google.common.collect.Lists;
/** /**
* All configurations related to SP authentication and authorization layer. * All configurations related to HawkBit's authentication and authorization
* * layer.
*
*
*/ */
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ, proxyTargetClass = true, securedEnabled = true) @EnableGlobalMethodSecurity(prePostEnabled = true, mode = ASPECTJ, proxyTargetClass = true, securedEnabled = true)
@EnableWebMvcSecurity @EnableWebMvcSecurity
@Order(value = Ordered.HIGHEST_PRECEDENCE) @Order(value = HIGHEST_PRECEDENCE)
public class SecurityManagedConfiguration { public class SecurityManagedConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(SecurityManagedConfiguration.class); private static final Logger LOG = LoggerFactory.getLogger(SecurityManagedConfiguration.class);
@Autowired @Autowired
@@ -104,9 +105,6 @@ public class SecurityManagedConfiguration {
/** /**
* {@link WebSecurityConfigurer} for the internal SP controller API. * {@link WebSecurityConfigurer} for the internal SP controller API.
*
*
*
*/ */
@Configuration @Configuration
@Order(300) @Order(300)
@@ -114,19 +112,25 @@ public class SecurityManagedConfiguration {
@Autowired @Autowired
private ControllerManagement controllerManagement; private ControllerManagement controllerManagement;
@Autowired @Autowired
private TenantConfigurationManagement tenantConfigurationManagement; private TenantConfigurationManagement tenantConfigurationManagement;
@Autowired @Autowired
private TenantAware tenantAware; private TenantAware tenantAware;
@Autowired @Autowired
private DdiSecurityProperties ddiSecurityConfiguration; private DdiSecurityProperties ddiSecurityConfiguration;
@Autowired @Autowired
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties; private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties;
@Autowired @Autowired
private SystemSecurityContext systemSecurityContext; private SystemSecurityContext systemSecurityContext;
@Override @Override
protected void configure(final HttpSecurity http) throws Exception { protected void configure(final HttpSecurity http) throws Exception {
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource(); final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter( final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
@@ -164,16 +168,19 @@ public class SecurityManagedConfiguration {
} }
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) { if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
LOG.info( LOG.info(
"******************\n** Anonymous controller security enabled, should only use for developing purposes **\n******************"); "******************\n** Anonymous controller security enabled, should only be used for developing purposes **\n******************");
final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter( final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter(
"controllerAnonymousFilter", "anonymous", "controllerAnonymousFilter", "anonymous",
Lists.newArrayList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS), newArrayList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS),
new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE))); new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE)));
anoymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource); anoymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
httpSec.requestMatchers().antMatchers("/*/controller/v1/**", "/*/controller/artifacts/v1/**").and() httpSec.requestMatchers().antMatchers("/*/controller/v1/**", "/*/controller/artifacts/v1/**").and()
.securityContext().disable().anonymous().authenticationFilter(anoymousFilter); .securityContext().disable().anonymous().authenticationFilter(anoymousFilter);
} else { } else {
httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter) httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter)
.addFilter(gatewaySecurityTokenFilter).addFilter(controllerAnonymousDownloadFilter) .addFilter(gatewaySecurityTokenFilter).addFilter(controllerAnonymousDownloadFilter)
.antMatcher("/*/controller/**").anonymous().disable().authorizeRequests().anyRequest() .antMatcher("/*/controller/**").anonymous().disable().authorizeRequests().anyRequest()
@@ -186,6 +193,7 @@ public class SecurityManagedConfiguration {
@Override @Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception { protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider( auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider(
ddiSecurityConfiguration.getRp().getTrustedIPs())); ddiSecurityConfiguration.getRp().getTrustedIPs()));
} }
@@ -200,6 +208,7 @@ public class SecurityManagedConfiguration {
@Bean @Bean
@Order(50) @Order(50)
public FilterRegistrationBean dosFilter() { public FilterRegistrationBean dosFilter() {
final FilterRegistrationBean filterRegBean = new FilterRegistrationBean(); final FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
filterRegBean.setFilter(new DosFilter(securityProperties.getDos().getFilter().getMaxRead(), filterRegBean.setFilter(new DosFilter(securityProperties.getDos().getFilter().getMaxRead(),
@@ -207,6 +216,7 @@ public class SecurityManagedConfiguration {
securityProperties.getDos().getFilter().getWhitelist(), securityProperties.getClients().getBlacklist(), securityProperties.getDos().getFilter().getWhitelist(), securityProperties.getClients().getBlacklist(),
securityProperties.getClients().getRemoteIpHeader())); securityProperties.getClients().getRemoteIpHeader()));
filterRegBean.addUrlPatterns("/{tenant}/controller/v1/*", "/rest/*"); filterRegBean.addUrlPatterns("/{tenant}/controller/v1/*", "/rest/*");
return filterRegBean; return filterRegBean;
} }
@@ -219,22 +229,21 @@ public class SecurityManagedConfiguration {
@Bean @Bean
@Order(100) @Order(100)
public FilterRegistrationBean eTagFilter() { public FilterRegistrationBean eTagFilter() {
final FilterRegistrationBean filterRegBean = new FilterRegistrationBean(); final FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
// eclude the URLs for downloading artifacts, so no eTag is generated in // Exclude the URLs for downloading artifacts, so no eTag is generated
// the // in the ShallowEtagHeaderFilter, just using the SH1 hash of the
// ShallowEtagHeaderFilter, just using the SH1 hash of the artifact // artifact itself as 'ETag', because otherwise the file will be copied
// itself as 'ETag', because // in memory!
// otherwise the file will be copied in memory!
filterRegBean.setFilter(new ExcludePathAwareShallowETagFilter( filterRegBean.setFilter(new ExcludePathAwareShallowETagFilter(
"/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download", "/{tenant}/controller/artifacts/**", "/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download", "/{tenant}/controller/artifacts/**",
"/{targetid}/softwaremodules/{softwareModuleId}/artifacts/**")); "/{targetid}/softwaremodules/{softwareModuleId}/artifacts/**"));
return filterRegBean; return filterRegBean;
} }
/** /**
* Security configuration for the REST management API of the health url. * Security configuration for the REST management API of the health url.
*
*
*/ */
@Configuration @Configuration
@Order(310) @Order(310)
@@ -249,28 +258,34 @@ public class SecurityManagedConfiguration {
/** /**
* Security configuration for the REST management API. * Security configuration for the REST management API.
*
*
*/ */
@Configuration @Configuration
@Order(350) @Order(350)
public static class RestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { public static class RestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Autowired @Autowired
private UserAuthenticationFilter userAuthenticationFilter; private UserAuthenticationFilter userAuthenticationFilter;
@Autowired @Autowired
private SystemManagement systemManagement; private SystemManagement systemManagement;
@Autowired @Autowired
private TenantAware tenantAware; private TenantAware tenantAware;
@Autowired @Autowired
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties; private SecurityProperties springSecurityProperties;
@Override @Override
protected void configure(final HttpSecurity http) throws Exception { protected void configure(final HttpSecurity http) throws Exception {
final BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
basicAuthEntryPoint.setRealmName(springSecurityProperties.getBasic().getRealm());
HttpSecurity httpSec = http.regexMatcher("\\/rest.*|\\/system.*").csrf().disable(); HttpSecurity httpSec = http.regexMatcher("\\/rest.*|\\/system.*").csrf().disable();
if (springSecurityProperties.isRequireSsl()) { if (springSecurityProperties.isRequireSsl()) {
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and(); httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
} }
httpSec.addFilterBefore(new Filter() { httpSec.addFilterBefore(new Filter() {
@Override @Override
public void init(final FilterConfig filterConfig) throws ServletException { public void init(final FilterConfig filterConfig) throws ServletException {
@@ -296,12 +311,13 @@ public class SecurityManagedConfiguration {
.hasAnyAuthority(SpPermission.SYSTEM_ADMIN) .hasAnyAuthority(SpPermission.SYSTEM_ADMIN)
.antMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/**") .antMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/**")
.hasAnyAuthority(SpPermission.SYSTEM_DIAG); .hasAnyAuthority(SpPermission.SYSTEM_DIAG);
httpSec.httpBasic().and().exceptionHandling().authenticationEntryPoint(basicAuthEntryPoint);
} }
} }
/** /**
* {@link WebSecurityConfigurer} for external (management) access. * {@link WebSecurityConfigurer} for external (management) access.
*
*/ */
@Configuration @Configuration
@Order(400) @Order(400)
@@ -311,10 +327,13 @@ public class SecurityManagedConfiguration {
private static final String XFRAME_OPTION_DENY = "DENY"; private static final String XFRAME_OPTION_DENY = "DENY";
private static final String XFRAME_OPTION_SAMEORIGIN = "SAMEORIGIN"; private static final String XFRAME_OPTION_SAMEORIGIN = "SAMEORIGIN";
private static final String XFAME_OPTION_ALLOW_FROM = "ALLOW-FROM"; private static final String XFAME_OPTION_ALLOW_FROM = "ALLOW-FROM";
@Autowired @Autowired
private VaadinSecurityContext vaadinSecurityContext; private VaadinSecurityContext vaadinSecurityContext;
@Autowired @Autowired
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties; private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties;
@Autowired @Autowired
private HawkbitSecurityProperties securityProperties; private HawkbitSecurityProperties securityProperties;
@@ -346,10 +365,13 @@ public class SecurityManagedConfiguration {
*/ */
@Bean @Bean
public VaadinAuthenticationSuccessHandler redirectSaveHandler() { public VaadinAuthenticationSuccessHandler redirectSaveHandler() {
final VaadinUrlAuthenticationSuccessHandler handler = new TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler(); final VaadinUrlAuthenticationSuccessHandler handler = new TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler();
handler.setRedirectStrategy(vaadinRedirectStrategy()); handler.setRedirectStrategy(vaadinRedirectStrategy());
handler.setDefaultTargetUrl("/UI/"); handler.setDefaultTargetUrl("/UI/");
handler.setTargetUrlParameter("r"); handler.setTargetUrlParameter("r");
return handler; return handler;
} }
@@ -371,7 +393,8 @@ public class SecurityManagedConfiguration {
// configuration xframe-option // configuration xframe-option
final String confXframeOption = securityProperties.getXframe().getOption(); final String confXframeOption = securityProperties.getXframe().getOption();
final String confAllowFromUri = securityProperties.getXframe().getAllowfrom(); final String confAllowFromUri = securityProperties.getXframe().getAllowfrom();
if (confXframeOption.equals(XFAME_OPTION_ALLOW_FROM) && confAllowFromUri.isEmpty()) {
if (XFAME_OPTION_ALLOW_FROM.equals(confXframeOption) && confAllowFromUri.isEmpty()) {
// if allow-from option is specified but no allowFromUri throw // if allow-from option is specified but no allowFromUri throw
// exception // exception
throw new IllegalStateException("hawkbit.server.security.xframe.option has been specified as ALLOW-FROM" throw new IllegalStateException("hawkbit.server.security.xframe.option has been specified as ALLOW-FROM"
@@ -380,9 +403,8 @@ public class SecurityManagedConfiguration {
} }
// workaround regex: we need to exclude the URL /UI/HEARTBEAT here // workaround regex: we need to exclude the URL /UI/HEARTBEAT here
// because we bound the // because we bound the vaadin application to /UI and not to root,
// vaadin application to /UI and not to root, described in // described in vaadin-forum:
// vaadin-forum:
// https://vaadin.com/forum#!/thread/3200565. // https://vaadin.com/forum#!/thread/3200565.
HttpSecurity httpSec = http.regexMatcher("(?!.*HEARTBEAT)^.*\\/UI.*$") HttpSecurity httpSec = http.regexMatcher("(?!.*HEARTBEAT)^.*\\/UI.*$")
// disable as CSRF is handled by Vaadin // disable as CSRF is handled by Vaadin
@@ -391,8 +413,9 @@ public class SecurityManagedConfiguration {
if (springSecurityProperties.isRequireSsl()) { if (springSecurityProperties.isRequireSsl()) {
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and(); httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
} else { } else {
LOG.info( LOG.info(
"\"******************\\n** Requires HTTPS Security has been disabled for UI, should only use for developing purposes **\\n******************\""); "\"******************\\n** Requires HTTPS Security has been disabled for UI, should only be used for developing purposes **\\n******************\"");
} }
// for UI integrator we allow frame integration on same origin // for UI integrator we allow frame integration on same origin
@@ -441,9 +464,6 @@ public class SecurityManagedConfiguration {
/** /**
* A Websecruity config to handle and filter the download ids. * A Websecruity config to handle and filter the download ids.
*
*
*
*/ */
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@@ -459,6 +479,7 @@ public class SecurityManagedConfiguration {
@Override @Override
protected void configure(final HttpSecurity http) throws Exception { protected void configure(final HttpSecurity http) throws Exception {
final HttpDownloadAuthenticationFilter downloadIdAuthenticationFilter = new HttpDownloadAuthenticationFilter( final HttpDownloadAuthenticationFilter downloadIdAuthenticationFilter = new HttpDownloadAuthenticationFilter(
downloadIdCache); downloadIdCache);
downloadIdAuthenticationFilter.setAuthenticationManager(authenticationManager()); downloadIdAuthenticationFilter.setAuthenticationManager(authenticationManager());
@@ -476,32 +497,21 @@ public class SecurityManagedConfiguration {
auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider( auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider(
ddiSecurityConfiguration.getRp().getTrustedIPs())); ddiSecurityConfiguration.getRp().getTrustedIPs()));
} }
} }
} }
/** /**
* After a successful login on the UI we need to ensure to create the tenant * After a successful login on the UI we need to ensure to create the tenant
* meta data within SP. * meta data within SP.
*
*
*/ */
class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends VaadinUrlAuthenticationSuccessHandler { class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends VaadinUrlAuthenticationSuccessHandler {
@Autowired @Autowired
private SystemManagement systemManagement; private SystemManagement systemManagement;
/*
* (non-Javadoc)
*
* @see org.vaadin.spring.security.web.authentication.
* SavedRequestAwareVaadinAuthenticationSuccessHandler
* #onAuthenticationSuccess(org.springframework.security.core.
* Authentication)
*/
@Override @Override
public void onAuthenticationSuccess(final Authentication authentication) throws Exception { public void onAuthenticationSuccess(final Authentication authentication) throws Exception {
if (authentication.getClass().equals(TenantUserPasswordAuthenticationToken.class)) { if (authentication.getClass().equals(TenantUserPasswordAuthenticationToken.class)) {
systemManagement systemManagement
.getTenantMetadata(((TenantUserPasswordAuthenticationToken) authentication).getTenant().toString()); .getTenantMetadata(((TenantUserPasswordAuthenticationToken) authentication).getTenant().toString());
@@ -515,14 +525,13 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends
// has been fixed. // has been fixed.
systemManagement.getTenantMetadata("DEFAULT"); systemManagement.getTenantMetadata("DEFAULT");
} }
super.onAuthenticationSuccess(authentication); super.onAuthenticationSuccess(authentication);
} }
} }
/** /**
* Sevletfilter to create metadata after successful authentication over RESTful. * Sevletfilter to create metadata after successful authentication over RESTful.
*
*
*/ */
class AuthenticationSuccessTenantMetadataCreationFilter implements Filter { class AuthenticationSuccessTenantMetadataCreationFilter implements Filter {
@@ -543,11 +552,13 @@ class AuthenticationSuccessTenantMetadataCreationFilter implements Filter {
@Override @Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException { throws IOException, ServletException {
final String currentTenant = tenantAware.getCurrentTenant(); final String currentTenant = tenantAware.getCurrentTenant();
if (currentTenant != null) { if (currentTenant != null) {
// lazy initialize tenant meta data after successful authentication // lazy initialize tenant meta data after successful authentication
systemManagement.getTenantMetadata(currentTenant); systemManagement.getTenantMetadata(currentTenant);
} }
chain.doFilter(request, response); chain.doFilter(request, response);
} }

View File

@@ -7,6 +7,9 @@
# http://www.eclipse.org/legal/epl-v10.html # http://www.eclipse.org/legal/epl-v10.html
# #
# Displayed basic auth realm
security.basic.realm=HawkBit
# JPA / Datasource # JPA / Datasource
spring.jpa.eclipselink.eclipselink.weaving=false spring.jpa.eclipselink.eclipselink.weaving=false
spring.jpa.database=H2 spring.jpa.database=H2