add security and filters for anonymous download via http and amqp
requests Signed-off-by: Michael Hirsch <michael.hirsch@bosch-si.com>
This commit is contained in:
@@ -10,7 +10,6 @@ package org.eclipse.hawkbit.autoconfigure.security;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
@@ -35,6 +34,7 @@ import org.eclipse.hawkbit.security.ControllerTenantAwareAuthenticationDetailsSo
|
|||||||
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
||||||
import org.eclipse.hawkbit.security.DosFilter;
|
import org.eclipse.hawkbit.security.DosFilter;
|
||||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||||
|
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateAnonymousDownloadFilter;
|
||||||
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateSecurityTokenFilter;
|
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateSecurityTokenFilter;
|
||||||
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter;
|
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter;
|
||||||
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedSecurityHeaderFilter;
|
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedSecurityHeaderFilter;
|
||||||
@@ -83,6 +83,8 @@ 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 SP authentication and authorization layer.
|
||||||
*
|
*
|
||||||
@@ -147,6 +149,12 @@ public class SecurityManagedConfiguration {
|
|||||||
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
|
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
|
||||||
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||||
|
|
||||||
|
final HttpControllerPreAuthenticateAnonymousDownloadFilter controllerAnonymousDownloadFilter = new HttpControllerPreAuthenticateAnonymousDownloadFilter(
|
||||||
|
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||||
|
controllerAnonymousDownloadFilter.setAuthenticationManager(authenticationManager());
|
||||||
|
controllerAnonymousDownloadFilter.setCheckForPrincipalChanges(true);
|
||||||
|
controllerAnonymousDownloadFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||||
|
|
||||||
HttpSecurity httpSec = http.csrf().disable().headers()
|
HttpSecurity httpSec = http.csrf().disable().headers()
|
||||||
.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY)).contentTypeOptions()
|
.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY)).contentTypeOptions()
|
||||||
.xssProtection().httpStrictTransportSecurity().and();
|
.xssProtection().httpStrictTransportSecurity().and();
|
||||||
@@ -159,15 +167,17 @@ public class SecurityManagedConfiguration {
|
|||||||
LOG.info(
|
LOG.info(
|
||||||
"******************\n** Anonymous controller security enabled, should only use for developing purposes **\n******************");
|
"******************\n** Anonymous controller security enabled, should only use for developing purposes **\n******************");
|
||||||
final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter(
|
final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter(
|
||||||
"controllerAnonymousFilter", "anonymous", Collections.singletonList(
|
"controllerAnonymousFilter", "anonymous",
|
||||||
new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
|
Lists.newArrayList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS),
|
||||||
|
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).antMatcher("/*/controller/**").anonymous().disable()
|
.addFilter(gatewaySecurityTokenFilter).addFilter(controllerAnonymousDownloadFilter)
|
||||||
.authorizeRequests().anyRequest().authenticated().and().exceptionHandling()
|
.antMatcher("/*/controller/**").anonymous().disable().authorizeRequests().anyRequest()
|
||||||
|
.authenticated().and().exceptionHandling()
|
||||||
.authenticationEntryPoint((request, response, authException) -> response
|
.authenticationEntryPoint((request, response, authException) -> response
|
||||||
.setStatus(HttpStatus.UNAUTHORIZED.value()))
|
.setStatus(HttpStatus.UNAUTHORIZED.value()))
|
||||||
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import org.eclipse.hawkbit.repository.ControllerManagement;
|
|||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.security.CoapAnonymousPreAuthenticatedFilter;
|
import org.eclipse.hawkbit.security.CoapAnonymousPreAuthenticatedFilter;
|
||||||
import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter;
|
import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter;
|
||||||
|
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousDownload;
|
||||||
|
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousFilter;
|
||||||
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedGatewaySecurityTokenFilter;
|
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedGatewaySecurityTokenFilter;
|
||||||
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter;
|
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter;
|
||||||
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
||||||
@@ -90,6 +92,11 @@ public class AmqpControllerAuthentfication {
|
|||||||
tenantConfigurationManagement, controllerManagement, tenantAware, systemSecurityContext);
|
tenantConfigurationManagement, controllerManagement, tenantAware, systemSecurityContext);
|
||||||
filterChain.add(securityTokenFilter);
|
filterChain.add(securityTokenFilter);
|
||||||
|
|
||||||
|
final ControllerPreAuthenticatedAnonymousDownload anonymousDownloadFilter = new ControllerPreAuthenticatedAnonymousDownload(
|
||||||
|
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||||
|
filterChain.add(anonymousDownloadFilter);
|
||||||
|
|
||||||
|
filterChain.add(new ControllerPreAuthenticatedAnonymousFilter(ddiSecruityProperties));
|
||||||
filterChain.add(new CoapAnonymousPreAuthenticatedFilter());
|
filterChain.add(new CoapAnonymousPreAuthenticatedFilter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
|||||||
import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus;
|
import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.Artifact;
|
import org.eclipse.hawkbit.dmf.json.model.Artifact;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.ArtifactHash;
|
import org.eclipse.hawkbit.dmf.json.model.ArtifactHash;
|
||||||
|
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
|
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
|
|
||||||
import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent;
|
import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent;
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||||
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
||||||
@@ -158,8 +158,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
private Message handleAuthentifiactionMessage(final Message message) {
|
private Message handleAuthentifiactionMessage(final Message message) {
|
||||||
final DownloadResponse authentificationResponse = new DownloadResponse();
|
final DownloadResponse authentificationResponse = new DownloadResponse();
|
||||||
final MessageProperties messageProperties = message.getMessageProperties();
|
final MessageProperties messageProperties = message.getMessageProperties();
|
||||||
final TenantSecurityToken secruityToken = convertMessage(message,
|
final TenantSecurityToken secruityToken = convertMessage(message, TenantSecurityToken.class);
|
||||||
TenantSecurityToken.class);
|
|
||||||
final FileResource fileResource = secruityToken.getFileResource();
|
final FileResource fileResource = secruityToken.getFileResource();
|
||||||
try {
|
try {
|
||||||
SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken));
|
SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken));
|
||||||
@@ -221,12 +220,11 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
} else if (fileResource.getFilename() != null) {
|
} else if (fileResource.getFilename() != null) {
|
||||||
localArtifact = artifactManagement.findLocalArtifactByFilename(fileResource.getFilename()).stream()
|
localArtifact = artifactManagement.findLocalArtifactByFilename(fileResource.getFilename()).stream()
|
||||||
.findFirst().orElse(null);
|
.findFirst().orElse(null);
|
||||||
} else if (fileResource.getArtifactId() != null) {
|
} else if (fileResource.getSoftwareModuleFilenameResource() != null) {
|
||||||
final org.eclipse.hawkbit.repository.model.Artifact artifact = artifactManagement
|
localArtifact = artifactManagement
|
||||||
.findArtifact(fileResource.getArtifactId());
|
.findByFilenameAndSoftwareModule(fileResource.getSoftwareModuleFilenameResource().getFilename(),
|
||||||
if (artifact instanceof LocalArtifact) {
|
fileResource.getSoftwareModuleFilenameResource().getSoftwareModuleId())
|
||||||
localArtifact = (LocalArtifact) artifact;
|
.stream().findFirst().orElse(null);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return localArtifact;
|
return localArtifact;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.eclipse.hawkbit.repository.ControllerManagement;
|
|||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
|
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
|
||||||
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
||||||
|
import org.eclipse.hawkbit.security.DdiSecurityProperties.Authentication.Anonymous;
|
||||||
import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp;
|
import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp;
|
||||||
import org.eclipse.hawkbit.security.SecurityContextTenantAware;
|
import org.eclipse.hawkbit.security.SecurityContextTenantAware;
|
||||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
@@ -80,8 +81,14 @@ public class AmqpControllerAuthenticationTest {
|
|||||||
|
|
||||||
final DdiSecurityProperties secruityProperties = mock(DdiSecurityProperties.class);
|
final DdiSecurityProperties secruityProperties = mock(DdiSecurityProperties.class);
|
||||||
final Rp rp = mock(Rp.class);
|
final Rp rp = mock(Rp.class);
|
||||||
|
final org.eclipse.hawkbit.security.DdiSecurityProperties.Authentication ddiAuthentication = mock(
|
||||||
|
org.eclipse.hawkbit.security.DdiSecurityProperties.Authentication.class);
|
||||||
|
final Anonymous anonymous = mock(Anonymous.class);
|
||||||
when(secruityProperties.getRp()).thenReturn(rp);
|
when(secruityProperties.getRp()).thenReturn(rp);
|
||||||
when(rp.getSslIssuerHashHeader()).thenReturn("X-Ssl-Issuer-Hash-%d");
|
when(rp.getSslIssuerHashHeader()).thenReturn("X-Ssl-Issuer-Hash-%d");
|
||||||
|
when(secruityProperties.getAuthentication()).thenReturn(ddiAuthentication);
|
||||||
|
when(ddiAuthentication.getAnonymous()).thenReturn(anonymous);
|
||||||
|
when(anonymous.isEnabled()).thenReturn(false);
|
||||||
authenticationManager.setSecruityProperties(secruityProperties);
|
authenticationManager.setSecruityProperties(secruityProperties);
|
||||||
|
|
||||||
tenantConfigurationManagement = mock(TenantConfigurationManagement.class);
|
tenantConfigurationManagement = mock(TenantConfigurationManagement.class);
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public class TenantSecurityToken {
|
|||||||
@JsonProperty(required = false)
|
@JsonProperty(required = false)
|
||||||
private String filename;
|
private String filename;
|
||||||
@JsonProperty(required = false)
|
@JsonProperty(required = false)
|
||||||
private Long artifactId;
|
private SoftwareModuleFilenameResource softwareModuleFilenameResource;
|
||||||
|
|
||||||
public String getSha1() {
|
public String getSha1() {
|
||||||
return sha1;
|
return sha1;
|
||||||
@@ -121,12 +121,13 @@ public class TenantSecurityToken {
|
|||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getArtifactId() {
|
public SoftwareModuleFilenameResource getSoftwareModuleFilenameResource() {
|
||||||
return artifactId;
|
return softwareModuleFilenameResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setArtifactId(final Long artifactId) {
|
public void setSoftwareModuleFilenameResource(
|
||||||
this.artifactId = artifactId;
|
final SoftwareModuleFilenameResource softwareModuleFilenameResource) {
|
||||||
|
this.softwareModuleFilenameResource = softwareModuleFilenameResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -156,15 +157,19 @@ public class TenantSecurityToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* factory method to create a file resource for an artifactId lookup.
|
* factory method to create a file resource for an softwaremodule +
|
||||||
|
* filename lookup, because an filename is not globally unique but
|
||||||
|
* within a softwaremodule.
|
||||||
*
|
*
|
||||||
* @param artifactId
|
* @param softwareModuleId
|
||||||
* the artifactId of the file to obtain
|
* the ID of the software module which contains the artifact
|
||||||
|
* @param filename
|
||||||
|
* the name of file to obtain within the software module
|
||||||
* @return the {@link FileResource} with artifactId set
|
* @return the {@link FileResource} with artifactId set
|
||||||
*/
|
*/
|
||||||
public static FileResource artifactId(final Long artifactId) {
|
public static FileResource softwareModuleFilename(final Long softwareModuleId, final String filename) {
|
||||||
final FileResource resource = new FileResource();
|
final FileResource resource = new FileResource();
|
||||||
resource.artifactId = artifactId;
|
resource.softwareModuleFilenameResource = new SoftwareModuleFilenameResource(softwareModuleId, filename);
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,5 +177,42 @@ public class TenantSecurityToken {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "FileResource [sha1=" + sha1 + ", filename=" + filename + "]";
|
return "FileResource [sha1=" + sha1 + ", filename=" + filename + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner class which holds the pointer to an artifact based on the
|
||||||
|
* softwaremoduleId and the filename.
|
||||||
|
*/
|
||||||
|
@JsonInclude(Include.NON_NULL)
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public static class SoftwareModuleFilenameResource {
|
||||||
|
@JsonProperty(required = false)
|
||||||
|
private final Long softwareModuleId;
|
||||||
|
@JsonProperty(required = false)
|
||||||
|
private final String filename;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param softwareModuleId
|
||||||
|
* the ID of the softwaremodule
|
||||||
|
* @param filename
|
||||||
|
* the name of the file of the artifact within the
|
||||||
|
* softwaremodule
|
||||||
|
*/
|
||||||
|
@JsonCreator
|
||||||
|
public SoftwareModuleFilenameResource(@JsonProperty("softwareModuleId") final Long softwareModuleId,
|
||||||
|
@JsonProperty("filename") final String filename) {
|
||||||
|
this.softwareModuleId = softwareModuleId;
|
||||||
|
this.filename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getSoftwareModuleId() {
|
||||||
|
return softwareModuleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
package org.eclipse.hawkbit.security;
|
package org.eclipse.hawkbit.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
@@ -16,6 +18,7 @@ import javax.servlet.ServletException;
|
|||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
|
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
|
||||||
@@ -23,8 +26,11 @@ import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
|||||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
|
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
|
||||||
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||||
import org.springframework.util.AntPathMatcher;
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
|
||||||
import com.google.common.collect.Iterators;
|
import com.google.common.collect.Iterators;
|
||||||
@@ -80,14 +86,6 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
|
|||||||
pathExtractor = new AntPathMatcher();
|
pathExtractor = new AntPathMatcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see org.springframework.security.web.authentication.preauth.
|
|
||||||
* AbstractPreAuthenticatedProcessingFilter
|
|
||||||
* #doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse,
|
|
||||||
* javax.servlet.FilterChain)
|
|
||||||
*/
|
|
||||||
@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 {
|
||||||
@@ -113,6 +111,18 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
|
|||||||
|
|
||||||
protected abstract PreAuthenficationFilter createControllerAuthenticationFilter();
|
protected abstract PreAuthenficationFilter createControllerAuthenticationFilter();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response,
|
||||||
|
final Authentication authResult) {
|
||||||
|
final Collection<GrantedAuthority> authorities = new ArrayList<>();
|
||||||
|
authorities.addAll(authResult.getAuthorities());
|
||||||
|
authorities.addAll(abstractControllerAuthenticationFilter.getSuccessfulAuthenticationAuthorities());
|
||||||
|
final PreAuthenticatedAuthenticationToken authTokenWithGrantedAuthorities = new PreAuthenticatedAuthenticationToken(
|
||||||
|
authResult.getPrincipal(), authResult.getCredentials(), authorities);
|
||||||
|
authTokenWithGrantedAuthorities.setDetails(authResult.getDetails());
|
||||||
|
super.successfulAuthentication(request, response, authTokenWithGrantedAuthorities);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts tenant and controllerId from the request URI as path variables.
|
* Extracts tenant and controllerId from the request URI as path variables.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.security;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||||
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An pre-authenticated processing filter which add the
|
||||||
|
* {@link SpringEvalExpressions#CONTROLLER_DOWNLOAD_ROLE_ANONYMOUS} to the
|
||||||
|
* security context in case the anonymous download is allowed through
|
||||||
|
* configuration.
|
||||||
|
*/
|
||||||
|
public class HttpControllerPreAuthenticateAnonymousDownloadFilter extends AbstractHttpControllerAuthenticationFilter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param tenantConfigurationManagement
|
||||||
|
* the system management service to retrieve configuration
|
||||||
|
* properties
|
||||||
|
* @param tenantAware
|
||||||
|
* the tenant aware service to get configuration for the specific
|
||||||
|
* tenant
|
||||||
|
* @param systemSecurityContext
|
||||||
|
* the system security context
|
||||||
|
*/
|
||||||
|
public HttpControllerPreAuthenticateAnonymousDownloadFilter(
|
||||||
|
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
|
||||||
|
final SystemSecurityContext systemSecurityContext) {
|
||||||
|
super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PreAuthenficationFilter createControllerAuthenticationFilter() {
|
||||||
|
return new ControllerPreAuthenticatedAnonymousDownload(tenantConfigurationManagement, tenantAware,
|
||||||
|
systemSecurityContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -404,7 +404,7 @@ public class ArtifactManagement {
|
|||||||
* if file could not be found in store
|
* if file could not be found in store
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR
|
||||||
+ SpringEvalExpressions.IS_CONTROLLER)
|
+ SpringEvalExpressions.HAS_CONTROLLER_DOWNLOAD)
|
||||||
public DbArtifact loadLocalArtifactBinary(@NotNull final LocalArtifact artifact) {
|
public DbArtifact loadLocalArtifactBinary(@NotNull final LocalArtifact artifact) {
|
||||||
final DbArtifact result = artifactRepository.getArtifactBySha1(artifact.getGridFsFileName());
|
final DbArtifact result = artifactRepository.getArtifactBySha1(artifact.getGridFsFileName());
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
|
|||||||
@@ -182,6 +182,12 @@ public final class SpPermission {
|
|||||||
*/
|
*/
|
||||||
public static final String CONTROLLER_ROLE_ANONYMOUS = "ROLE_CONTROLLER_ANONYMOUS";
|
public static final String CONTROLLER_ROLE_ANONYMOUS = "ROLE_CONTROLLER_ANONYMOUS";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The role which contains in the spring security context in case an
|
||||||
|
* controller is authenticated to download artifacts.
|
||||||
|
*/
|
||||||
|
public static final String CONTROLLER_DOWNLOAD_ROLE = "ROLE_CONTROLLER_DOWNLOAD";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The role which contains the spring security context in case the
|
* The role which contains the spring security context in case the
|
||||||
* system is executing code which is necessary to be privileged.
|
* system is executing code which is necessary to be privileged.
|
||||||
@@ -275,8 +281,16 @@ public final class SpPermission {
|
|||||||
* context contains the anoynmous role or the controller specific role
|
* context contains the anoynmous role or the controller specific role
|
||||||
* {@link SpPermission#CONTROLLER_ROLE}.
|
* {@link SpPermission#CONTROLLER_ROLE}.
|
||||||
*/
|
*/
|
||||||
public static final String IS_CONTROLLER = "hasAnyRole('" + CONTROLLER_ROLE_ANONYMOUS + "', '"
|
public static final String IS_CONTROLLER = "hasAnyRole('" + CONTROLLER_ROLE_ANONYMOUS + "', '" + CONTROLLER_ROLE
|
||||||
+ CONTROLLER_ROLE + "')";
|
+ "')";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if the spring
|
||||||
|
* context contains the role to allow controllers to download specific
|
||||||
|
* role {@link SpPermission#CONTROLLER_DOWNLOAD_ROLE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_CONTROLLER_DOWNLOAD = HAS_AUTH_PREFIX + CONTROLLER_DOWNLOAD_ROLE
|
||||||
|
+ HAS_AUTH_SUFFIX;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring security eval hasAnyRole expression to check if the spring
|
* Spring security eval hasAnyRole expression to check if the spring
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.security;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||||
|
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||||
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
|
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An pre-authenticated processing filter which add the
|
||||||
|
* {@link SpringEvalExpressions#CONTROLLER_DOWNLOAD_ROLE_ANONYMOUS} to the
|
||||||
|
* security context in case the anonymous download is allowed through
|
||||||
|
* configuration.
|
||||||
|
*/
|
||||||
|
public class ControllerPreAuthenticatedAnonymousDownload extends AbstractControllerAuthenticationFilter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param tenantConfigurationManagement
|
||||||
|
* the tenant management service to retrieve configuration
|
||||||
|
* properties
|
||||||
|
* @param tenantAware
|
||||||
|
* the tenant aware service to get configuration for the specific
|
||||||
|
* tenant
|
||||||
|
* @param systemSecurityContext
|
||||||
|
* the system security context to get access to tenant
|
||||||
|
* configuration
|
||||||
|
*/
|
||||||
|
public ControllerPreAuthenticatedAnonymousDownload(
|
||||||
|
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
|
||||||
|
final SystemSecurityContext systemSecurityContext) {
|
||||||
|
super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
|
||||||
|
return new HeaderAuthentication(secruityToken.getControllerId(), secruityToken.getControllerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
|
||||||
|
return new HeaderAuthentication(secruityToken.getControllerId(), secruityToken.getControllerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TenantConfigurationKey getTenantConfigurationKey() {
|
||||||
|
return TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<GrantedAuthority> getSuccessfulAuthenticationAuthorities() {
|
||||||
|
return Lists.newArrayList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.security;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An anonymous controller filter which is only enabled in case of anonymous
|
||||||
|
* access is granted. This should only be for development purposes.
|
||||||
|
*
|
||||||
|
* @see DdiSecurityProperties
|
||||||
|
*/
|
||||||
|
public class ControllerPreAuthenticatedAnonymousFilter implements PreAuthenficationFilter {
|
||||||
|
|
||||||
|
private final DdiSecurityProperties ddiSecurityConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ddiSecurityConfiguration
|
||||||
|
* the security configuration which holds the configuration if
|
||||||
|
* anonymous is enabled or not
|
||||||
|
*/
|
||||||
|
public ControllerPreAuthenticatedAnonymousFilter(final DdiSecurityProperties ddiSecurityConfiguration) {
|
||||||
|
this.ddiSecurityConfiguration = ddiSecurityConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
|
||||||
|
return new HeaderAuthentication(secruityToken.getControllerId(), secruityToken.getControllerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
|
||||||
|
return new HeaderAuthentication(secruityToken.getControllerId(), secruityToken.getControllerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnable(final TenantSecurityToken secruityToken) {
|
||||||
|
return ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -109,6 +109,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
|
|||||||
if (successAuthentication) {
|
if (successAuthentication) {
|
||||||
final Collection<GrantedAuthority> controllerAuthorities = new ArrayList<>();
|
final Collection<GrantedAuthority> controllerAuthorities = new ArrayList<>();
|
||||||
controllerAuthorities.add(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE));
|
controllerAuthorities.add(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE));
|
||||||
|
controllerAuthorities.add(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE));
|
||||||
final PreAuthenticatedAuthenticationToken successToken = new PreAuthenticatedAuthenticationToken(principal,
|
final PreAuthenticatedAuthenticationToken successToken = new PreAuthenticatedAuthenticationToken(principal,
|
||||||
credentials, controllerAuthorities);
|
credentials, controllerAuthorities);
|
||||||
successToken.setDetails(tokenDetails);
|
successToken.setDetails(tokenDetails);
|
||||||
|
|||||||
@@ -8,7 +8,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.security;
|
package org.eclipse.hawkbit.security;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for Pre Authenfication.
|
* Interface for Pre Authenfication.
|
||||||
@@ -45,4 +50,16 @@ public interface PreAuthenficationFilter {
|
|||||||
*/
|
*/
|
||||||
HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
|
HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows to add additional authorities to the successful authenticated
|
||||||
|
* token.
|
||||||
|
*
|
||||||
|
* @return the authorities granted to the principal, or an empty collection
|
||||||
|
* if the token has not been authenticated. Never null.
|
||||||
|
* @see Authentication#getAuthorities()
|
||||||
|
*/
|
||||||
|
default Collection<GrantedAuthority> getSuccessfulAuthenticationAuthorities() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user