Anonymous download activated does not result in general DDI anonymous (#616)

* Fix unintended anonymous access in DDI API.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Fix test.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Fix ant matcher.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2017-12-19 17:53:17 +01:00
committed by GitHub
parent 7d0bf3a162
commit 5d774dc70a
17 changed files with 199 additions and 93 deletions

View File

@@ -22,6 +22,7 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import org.eclipse.hawkbit.cache.DownloadIdCache; import org.eclipse.hawkbit.cache.DownloadIdCache;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.ddi.rest.resource.DdiApiConfiguration; import org.eclipse.hawkbit.ddi.rest.resource.DdiApiConfiguration;
import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
@@ -134,32 +135,39 @@ public class SecurityManagedConfiguration {
} }
/** /**
* {@link WebSecurityConfigurer} for the internal SP controller API. * {@link WebSecurityConfigurer} for the hawkBit server DDI interface.
*/ */
@Configuration @Configuration
@Order(300) @Order(300)
@ConditionalOnClass(DdiApiConfiguration.class) @ConditionalOnClass(DdiApiConfiguration.class)
static class ControllerSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { static class ControllerSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
private static final String DDI_ANT_MATCHER = "/{tenant}/controller/**"; private static final String[] DDI_ANT_MATCHERS = { DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/deploymentBase/**",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/cancelAction/**",
DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/configData",
DdiRestConstants.BASE_V1_REQUEST_MAPPING
+ "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts" };
private final ControllerManagement controllerManagement;
private final TenantConfigurationManagement tenantConfigurationManagement;
private final TenantAware tenantAware;
private final DdiSecurityProperties ddiSecurityConfiguration;
private final SecurityProperties springSecurityProperties;
private final SystemSecurityContext systemSecurityContext;
@Autowired @Autowired
private ControllerManagement controllerManagement; ControllerSecurityConfigurationAdapter(final ControllerManagement controllerManagement,
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
@Autowired final DdiSecurityProperties ddiSecurityConfiguration, final SecurityProperties springSecurityProperties,
private TenantConfigurationManagement tenantConfigurationManagement; final SystemSecurityContext systemSecurityContext) {
this.controllerManagement = controllerManagement;
@Autowired this.tenantConfigurationManagement = tenantConfigurationManagement;
private TenantAware tenantAware; this.tenantAware = tenantAware;
this.ddiSecurityConfiguration = ddiSecurityConfiguration;
@Autowired this.springSecurityProperties = springSecurityProperties;
private DdiSecurityProperties ddiSecurityConfiguration; this.systemSecurityContext = systemSecurityContext;
}
@Autowired
private SecurityProperties springSecurityProperties;
@Autowired
private SystemSecurityContext systemSecurityContext;
/** /**
* Filter to protect the hawkBit server DDI interface against to many * Filter to protect the hawkBit server DDI interface against to many
@@ -175,7 +183,7 @@ public class SecurityManagedConfiguration {
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true) @ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
public FilterRegistrationBean dosDDiFilter(final HawkbitSecurityProperties securityProperties) { public FilterRegistrationBean dosDDiFilter(final HawkbitSecurityProperties securityProperties) {
final FilterRegistrationBean filterRegBean = dosFilter(Arrays.asList(DDI_ANT_MATCHER), final FilterRegistrationBean filterRegBean = dosFilter(Arrays.asList(DDI_ANT_MATCHERS),
securityProperties.getDos().getFilter(), securityProperties.getClients()); securityProperties.getDos().getFilter(), securityProperties.getClients());
filterRegBean.setOrder(DOS_FILTER_ORDER); filterRegBean.setOrder(DOS_FILTER_ORDER);
filterRegBean.setName("dosDDiFilter"); filterRegBean.setName("dosDDiFilter");
@@ -183,6 +191,122 @@ public class SecurityManagedConfiguration {
return filterRegBean; return filterRegBean;
} }
@Override
protected void configure(final HttpSecurity http) throws Exception {
final ControllerTenantAwareAuthenticationDetailsSource authenticationDetailsSource = new ControllerTenantAwareAuthenticationDetailsSource();
final HttpControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new HttpControllerPreAuthenticatedSecurityHeaderFilter(
ddiSecurityConfiguration.getRp().getCnHeader(),
ddiSecurityConfiguration.getRp().getSslIssuerHashHeader(), tenantConfigurationManagement,
tenantAware, systemSecurityContext);
securityHeaderFilter.setAuthenticationManager(authenticationManager());
securityHeaderFilter.setCheckForPrincipalChanges(true);
securityHeaderFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new HttpControllerPreAuthenticateSecurityTokenFilter(
tenantConfigurationManagement, tenantAware, controllerManagement, systemSecurityContext);
securityTokenFilter.setAuthenticationManager(authenticationManager());
securityTokenFilter.setCheckForPrincipalChanges(true);
securityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
tenantConfigurationManagement, tenantAware, systemSecurityContext);
gatewaySecurityTokenFilter.setAuthenticationManager(authenticationManager());
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
HttpSecurity httpSec = http.csrf().disable();
if (springSecurityProperties.isRequireSsl()) {
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
}
if (ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled()) {
LOG.info(
"******************\n** Anonymous controller security enabled, should only be used for developing purposes **\n******************");
final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter(
"controllerAnonymousFilter", "anonymous",
Arrays.asList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
anoymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
httpSec.requestMatchers().antMatchers(DDI_ANT_MATCHERS).and().securityContext().disable().anonymous()
.authenticationFilter(anoymousFilter);
} else {
httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter)
.addFilter(gatewaySecurityTokenFilter).requestMatchers().antMatchers(DDI_ANT_MATCHERS).and()
.anonymous().disable().authorizeRequests().anyRequest().authenticated().and()
.exceptionHandling()
.authenticationEntryPoint((request, response, authException) -> response
.setStatus(HttpStatus.UNAUTHORIZED.value()))
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new PreAuthTokenSourceTrustAuthenticationProvider(
ddiSecurityConfiguration.getRp().getTrustedIPs()));
}
}
/**
* {@link WebSecurityConfigurer} for the hawkBit server DDI download
* interface.
*/
@Configuration
@Order(301)
@ConditionalOnClass(DdiApiConfiguration.class)
static class ControllerDownloadSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
private static final String DDI_DL_ANT_MATCHER = DdiRestConstants.BASE_V1_REQUEST_MAPPING
+ "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/*";
private final ControllerManagement controllerManagement;
private final TenantConfigurationManagement tenantConfigurationManagement;
private final TenantAware tenantAware;
private final DdiSecurityProperties ddiSecurityConfiguration;
private final SecurityProperties springSecurityProperties;
private final SystemSecurityContext systemSecurityContext;
@Autowired
ControllerDownloadSecurityConfigurationAdapter(final ControllerManagement controllerManagement,
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
final DdiSecurityProperties ddiSecurityConfiguration, final SecurityProperties springSecurityProperties,
final SystemSecurityContext systemSecurityContext) {
this.controllerManagement = controllerManagement;
this.tenantConfigurationManagement = tenantConfigurationManagement;
this.tenantAware = tenantAware;
this.ddiSecurityConfiguration = ddiSecurityConfiguration;
this.springSecurityProperties = springSecurityProperties;
this.systemSecurityContext = systemSecurityContext;
}
/**
* Filter to protect the hawkBit server DDI download interface against
* to many requests.
*
* @param securityProperties
* for filter configuration
*
* @return the spring filter registration bean for registering a denial
* of service protection filter in the filter chain
*/
@Bean
@ConditionalOnProperty(prefix = "hawkbit.server.security.dos.filter", name = "enabled", matchIfMissing = true)
public FilterRegistrationBean dosDDiDlFilter(final HawkbitSecurityProperties securityProperties) {
final FilterRegistrationBean filterRegBean = dosFilter(Arrays.asList(DDI_DL_ANT_MATCHER),
securityProperties.getDos().getFilter(), securityProperties.getClients());
filterRegBean.setOrder(DOS_FILTER_ORDER);
filterRegBean.setName("dosDDiDlFilter");
return filterRegBean;
}
@Override @Override
protected void configure(final HttpSecurity http) throws Exception { protected void configure(final HttpSecurity http) throws Exception {
@@ -227,17 +351,16 @@ public class SecurityManagedConfiguration {
final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter( final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter(
"controllerAnonymousFilter", "anonymous", "controllerAnonymousFilter", "anonymous",
Arrays.asList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS), Arrays.asList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE)));
anoymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource); anoymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
httpSec.requestMatchers().antMatchers(DDI_ANT_MATCHER).and().securityContext().disable().anonymous() httpSec.requestMatchers().antMatchers(DDI_DL_ANT_MATCHER).and().securityContext().disable().anonymous()
.authenticationFilter(anoymousFilter); .authenticationFilter(anoymousFilter);
} else { } else {
httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter) httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter)
.addFilter(gatewaySecurityTokenFilter).addFilter(controllerAnonymousDownloadFilter) .addFilter(gatewaySecurityTokenFilter).addFilter(controllerAnonymousDownloadFilter)
.antMatcher(DDI_ANT_MATCHER).anonymous().disable().authorizeRequests().anyRequest() .requestMatchers().antMatchers(DDI_DL_ANT_MATCHER).and().anonymous().disable()
.authenticated().and().exceptionHandling() .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);

View File

@@ -22,10 +22,10 @@ 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;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken;
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider; import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
import org.eclipse.hawkbit.security.PreAuthentificationFilter; import org.eclipse.hawkbit.security.PreAuthenticationFilter;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken;
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;
@@ -44,7 +44,7 @@ public class AmqpControllerAuthentication {
private final PreAuthTokenSourceTrustAuthenticationProvider preAuthenticatedAuthenticationProvider = new PreAuthTokenSourceTrustAuthenticationProvider(); private final PreAuthTokenSourceTrustAuthenticationProvider preAuthenticatedAuthenticationProvider = new PreAuthTokenSourceTrustAuthenticationProvider();
private List<PreAuthentificationFilter> filterChain; private List<PreAuthenticationFilter> filterChain;
private final ControllerManagement controllerManagement; private final ControllerManagement controllerManagement;
@@ -124,7 +124,7 @@ public class AmqpControllerAuthentication {
public Authentication doAuthenticate(final DmfTenantSecurityToken securityToken) { public Authentication doAuthenticate(final DmfTenantSecurityToken securityToken) {
resolveTenant(securityToken); resolveTenant(securityToken);
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(null, null); PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(null, null);
for (final PreAuthentificationFilter filter : filterChain) { for (final PreAuthenticationFilter filter : filterChain) {
final PreAuthenticatedAuthenticationToken authenticationRest = createAuthentication(filter, securityToken); final PreAuthenticatedAuthenticationToken authenticationRest = createAuthentication(filter, securityToken);
if (authenticationRest != null) { if (authenticationRest != null) {
authentication = authenticationRest; authentication = authenticationRest;
@@ -144,7 +144,7 @@ public class AmqpControllerAuthentication {
} }
private static PreAuthenticatedAuthenticationToken createAuthentication(final PreAuthentificationFilter filter, private static PreAuthenticatedAuthenticationToken createAuthentication(final PreAuthenticationFilter filter,
final DmfTenantSecurityToken secruityToken) { final DmfTenantSecurityToken secruityToken) {
if (!filter.isEnable(secruityToken)) { if (!filter.isEnable(secruityToken)) {
@@ -161,7 +161,8 @@ public class AmqpControllerAuthentication {
LOGGER.debug("preAuthenticatedPrincipal = {} trying to authenticate", principal); LOGGER.debug("preAuthenticatedPrincipal = {} trying to authenticate", principal);
return new PreAuthenticatedAuthenticationToken(principal, credentials); return new PreAuthenticatedAuthenticationToken(principal, credentials,
filter.getSuccessfulAuthenticationAuthorities());
} }
} }

View File

@@ -92,7 +92,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
@Test @Test
@Description("Target in the message is null.This message is invalid and should not requeued. Additional the receive message is null") @Description("Target in the message is null.This message is invalid and should not requeued. Additional the receive message is null")
public void securityTokenFileResourceIsNull() { public void securityTokenFileResourceIsNull() {
enableAnonymousAuthentification(); enableAnonymousAuthentication();
final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, null); final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, null);
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(returnMessage, HttpStatus.NOT_FOUND, null); verifyResult(returnMessage, HttpStatus.NOT_FOUND, null);
@@ -114,7 +114,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
@Test @Test
@Description("Verify that the receive message contains a 404 code,if the artifact could not found") @Description("Verify that the receive message contains a 404 code,if the artifact could not found")
public void fileResourceGetSha1InSecurityTokenIsNull() { public void fileResourceGetSha1InSecurityTokenIsNull() {
enableAnonymousAuthentification(); enableAnonymousAuthentication();
final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.createFileResourceBySha1(null)); FileResource.createFileResourceBySha1(null));
@@ -143,7 +143,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
@Test @Test
@Description("Verify that the receive message contains a 404 code, if there is no artifact for the given sha1") @Description("Verify that the receive message contains a 404 code, if there is no artifact for the given sha1")
public void artifactForFileResourceSHA1NotFound() { public void artifactForFileResourceSHA1NotFound() {
enableAnonymousAuthentification(); enableAnonymousAuthentication();
final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.createFileResourceBySha1(TARGET_SECRUITY_TOKEN)); FileResource.createFileResourceBySha1(TARGET_SECRUITY_TOKEN));
@@ -156,7 +156,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
@Test @Test
@Description("Verify that the receive message contains a 404 code, if there is no existing target for the given controller id") @Description("Verify that the receive message contains a 404 code, if there is no existing target for the given controller id")
public void artifactForFileResourceSHA1FoundTargetNotExists() { public void artifactForFileResourceSHA1FoundTargetNotExists() {
enableAnonymousAuthentification(); enableAnonymousAuthentication();
final DistributionSet distributionSet = createDistributionSet(); final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet); final List<Artifact> artifacts = createArtifacts(distributionSet);
final String sha1Hash = artifacts.get(0).getSha1Hash(); final String sha1Hash = artifacts.get(0).getSha1Hash();
@@ -226,7 +226,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
@Test @Test
@Description("Verify that the receive message contains a 200 code and a artifact without a controller id (anonymous enabled)") @Description("Verify that the receive message contains a 200 code and a artifact without a controller id (anonymous enabled)")
public void anonymousAuthentification() { public void anonymousAuthentification() {
enableAnonymousAuthentification(); enableAnonymousAuthentication();
final DistributionSet distributionSet = createDistributionSet(); final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet); final List<Artifact> artifacts = createArtifacts(distributionSet);
final Artifact artifact = artifacts.get(0); final Artifact artifact = artifacts.get(0);
@@ -260,7 +260,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
@Test @Test
@Description("Verify that the receive message contains a 404, if there is no artifact to the given filename") @Description("Verify that the receive message contains a 404, if there is no artifact to the given filename")
public void artifactForFileResourceFileNameNotFound() { public void artifactForFileResourceFileNameNotFound() {
enableAnonymousAuthentification(); enableAnonymousAuthentication();
final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.createFileResourceByFilename("Test.txt")); FileResource.createFileResourceByFilename("Test.txt"));
@@ -289,7 +289,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
@Test @Test
@Description("Verify that the receive message contains a 404, if there is no exisiting target") @Description("Verify that the receive message contains a 404, if there is no exisiting target")
public void artifactForFileResourceArtifactIdFoundTargetNotExists() { public void artifactForFileResourceArtifactIdFoundTargetNotExists() {
enableAnonymousAuthentification(); enableAnonymousAuthentication();
final DistributionSet distributionSet = createDistributionSet(); final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet); final List<Artifact> artifacts = createArtifacts(distributionSet);
final FileResource fileResource = FileResource.createFileResourceByArtifactId(artifacts.get(0).getId()); final FileResource fileResource = FileResource.createFileResourceByArtifactId(artifacts.get(0).getId());
@@ -323,7 +323,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
@Test @Test
@Description("Verify that the receive message contains a 404, if there is no artifact to the given softwareModuleFilename") @Description("Verify that the receive message contains a 404, if there is no artifact to the given softwareModuleFilename")
public void artifactForFileResourceSoftwareModuleFilenameNotFound() { public void artifactForFileResourceSoftwareModuleFilenameNotFound() {
enableAnonymousAuthentification(); enableAnonymousAuthentication();
final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.softwareModuleFilename(1L, "Test.txt")); FileResource.softwareModuleFilename(1L, "Test.txt"));
@@ -336,7 +336,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
@Test @Test
@Description("Verify that the receive message contains a 404, if there is no existing target for the file resource") @Description("Verify that the receive message contains a 404, if there is no existing target for the file resource")
public void artifactForFileResourceSoftwareModuleFilenameFoundTargetNotExists() { public void artifactForFileResourceSoftwareModuleFilenameFoundTargetNotExists() {
enableAnonymousAuthentification(); enableAnonymousAuthentication();
final DistributionSet distributionSet = createDistributionSet(); final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet); final List<Artifact> artifacts = createArtifacts(distributionSet);
@@ -376,7 +376,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
} }
private void verifyOkResult(Message returnMessage, Artifact artifact) { private void verifyOkResult(final Message returnMessage, final Artifact artifact) {
final DmfDownloadResponse convertedMessage = verifyResult(returnMessage, HttpStatus.OK, null); final DmfDownloadResponse convertedMessage = verifyResult(returnMessage, HttpStatus.OK, null);
assertThat(convertedMessage.getDownloadUrl()).isNotNull(); assertThat(convertedMessage.getDownloadUrl()).isNotNull();
assertThat(convertedMessage.getArtifact()).isNotNull(); assertThat(convertedMessage.getArtifact()).isNotNull();
@@ -384,7 +384,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
} }
private void enableAnonymousAuthentification() { private void enableAnonymousAuthentication() {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED, tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED,
true); true);
tenantConfigurationManagement.addOrUpdateConfiguration( tenantConfigurationManagement.addOrUpdateConfiguration(
@@ -405,15 +405,16 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
private DmfTenantSecurityToken createTenantSecurityToken(final String tenant, final String controllerId, private DmfTenantSecurityToken createTenantSecurityToken(final String tenant, final String controllerId,
final FileResource fileResource) { final FileResource fileResource) {
final DmfTenantSecurityToken tenantSecurityToken = new DmfTenantSecurityToken(tenant, controllerId, fileResource); final DmfTenantSecurityToken tenantSecurityToken = new DmfTenantSecurityToken(tenant, controllerId,
fileResource);
tenantSecurityToken.putHeader(DmfTenantSecurityToken.AUTHORIZATION_HEADER, TARGET_TOKEN_HEADER); tenantSecurityToken.putHeader(DmfTenantSecurityToken.AUTHORIZATION_HEADER, TARGET_TOKEN_HEADER);
return tenantSecurityToken; return tenantSecurityToken;
} }
private DmfTenantSecurityToken createTenantSecurityToken(final String tenant, final Long targetId, private DmfTenantSecurityToken createTenantSecurityToken(final String tenant, final Long targetId,
final String controllerId, final FileResource fileResource) { final String controllerId, final FileResource fileResource) {
final DmfTenantSecurityToken tenantSecurityToken = new DmfTenantSecurityToken(tenant, null, controllerId, targetId, final DmfTenantSecurityToken tenantSecurityToken = new DmfTenantSecurityToken(tenant, null, controllerId,
fileResource); targetId, fileResource);
tenantSecurityToken.putHeader(DmfTenantSecurityToken.AUTHORIZATION_HEADER, TARGET_TOKEN_HEADER); tenantSecurityToken.putHeader(DmfTenantSecurityToken.AUTHORIZATION_HEADER, TARGET_TOKEN_HEADER);
return tenantSecurityToken; return tenantSecurityToken;
} }

View File

@@ -65,7 +65,7 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
private final AntPathMatcher pathExtractor; private final AntPathMatcher pathExtractor;
private PreAuthentificationFilter abstractControllerAuthenticationFilter; private PreAuthenticationFilter abstractControllerAuthenticationFilter;
/** /**
* Constructor for sub-classes. * Constructor for sub-classes.
@@ -108,7 +108,7 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
} }
} }
protected abstract PreAuthentificationFilter createControllerAuthenticationFilter(); protected abstract PreAuthenticationFilter createControllerAuthenticationFilter();
@Override @Override
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response,

View File

@@ -39,7 +39,7 @@ public class HttpControllerPreAuthenticateAnonymousDownloadFilter extends Abstra
} }
@Override @Override
protected PreAuthentificationFilter createControllerAuthenticationFilter() { protected PreAuthenticationFilter createControllerAuthenticationFilter() {
return new ControllerPreAuthenticatedAnonymousDownload(tenantConfigurationManagement, tenantAware, return new ControllerPreAuthenticatedAnonymousDownload(tenantConfigurationManagement, tenantAware,
systemSecurityContext); systemSecurityContext);
} }

View File

@@ -55,7 +55,7 @@ public class HttpControllerPreAuthenticateSecurityTokenFilter extends AbstractHt
} }
@Override @Override
protected PreAuthentificationFilter createControllerAuthenticationFilter() { protected PreAuthenticationFilter createControllerAuthenticationFilter() {
return new ControllerPreAuthenticateSecurityTokenFilter(tenantConfigurationManagement, controllerManagement, return new ControllerPreAuthenticateSecurityTokenFilter(tenantConfigurationManagement, controllerManagement,
tenantAware, systemSecurityContext); tenantAware, systemSecurityContext);
} }

View File

@@ -34,7 +34,7 @@ public class HttpControllerPreAuthenticatedGatewaySecurityTokenFilter
* the tenant aware service to get configuration for the specific * the tenant aware service to get configuration for the specific
* tenant * tenant
* @param systemSecurityContext * @param systemSecurityContext
* * @param systemSecurityContext the system security context * the system security context
*/ */
public HttpControllerPreAuthenticatedGatewaySecurityTokenFilter( public HttpControllerPreAuthenticatedGatewaySecurityTokenFilter(
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware, final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
@@ -43,7 +43,7 @@ public class HttpControllerPreAuthenticatedGatewaySecurityTokenFilter
} }
@Override @Override
protected PreAuthentificationFilter createControllerAuthenticationFilter() { protected PreAuthenticationFilter createControllerAuthenticationFilter() {
return new ControllerPreAuthenticatedGatewaySecurityTokenFilter(tenantConfigurationManagement, tenantAware, return new ControllerPreAuthenticatedGatewaySecurityTokenFilter(tenantConfigurationManagement, tenantAware,
systemSecurityContext); systemSecurityContext);
} }

View File

@@ -54,7 +54,7 @@ public class HttpControllerPreAuthenticatedSecurityHeaderFilter extends Abstract
} }
@Override @Override
protected PreAuthentificationFilter createControllerAuthenticationFilter() { protected PreAuthenticationFilter createControllerAuthenticationFilter() {
return new ControllerPreAuthenticatedSecurityHeaderFilter(caCommonNameHeader, caAuthorityNameHeader, return new ControllerPreAuthenticatedSecurityHeaderFilter(caCommonNameHeader, caAuthorityNameHeader,
tenantConfigurationManagement, tenantAware, systemSecurityContext); tenantConfigurationManagement, tenantAware, systemSecurityContext);
} }

View File

@@ -204,7 +204,7 @@ public interface ArtifactManagement {
* *
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_CONTROLLER_DOWNLOAD) + SpringEvalExpressions.IS_CONTROLLER)
Optional<AbstractDbArtifact> loadArtifactBinary(@NotEmpty String sha1Hash); Optional<AbstractDbArtifact> loadArtifactBinary(@NotEmpty String sha1Hash);
} }

View File

@@ -12,7 +12,7 @@ security.user.name=admin
security.user.password=admin security.user.password=admin
# DDI authentication configuration # DDI authentication configuration
hawkbit.server.ddi.security.authentication.anonymous.enabled=true hawkbit.server.ddi.security.authentication.anonymous.enabled=false
hawkbit.server.ddi.security.authentication.targettoken.enabled=true hawkbit.server.ddi.security.authentication.targettoken.enabled=true
hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=true hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=true

View File

@@ -246,12 +246,6 @@ 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.
@@ -384,14 +378,6 @@ public final class SpPermission {
public static final String IS_CONTROLLER = "hasAnyRole('" + CONTROLLER_ROLE_ANONYMOUS + "', '" + CONTROLLER_ROLE public static final String IS_CONTROLLER = "hasAnyRole('" + CONTROLLER_ROLE_ANONYMOUS + "', '" + CONTROLLER_ROLE
+ "')"; + "')";
/**
* Spring security eval hasAuthority expression to check if the spring
* context contains the role to allow controllers to download specific
* role {@link SpringEvalExpressions#CONTROLLER_DOWNLOAD_ROLE}
*/
public static final String HAS_CONTROLLER_DOWNLOAD = HAS_AUTH_PREFIX + CONTROLLER_DOWNLOAD_ROLE
+ HAS_AUTH_SUFFIX;
/** /**
* Spring security eval hasAuthority expression to check if spring * Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#CREATE_REPOSITORY} and * context contains {@link SpPermission#CREATE_REPOSITORY} and

View File

@@ -8,19 +8,22 @@
*/ */
package org.eclipse.hawkbit.security; package org.eclipse.hawkbit.security;
import java.util.Arrays;
import java.util.Collection;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; 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.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
/** /**
* An abstraction for all controller based security. Check if the tenant * An abstraction for all controller based security. Check if the tenant
* configuration is enabled. * configuration is enabled.
*
*
*
*/ */
public abstract class AbstractControllerAuthenticationFilter implements PreAuthentificationFilter { public abstract class AbstractControllerAuthenticationFilter implements PreAuthenticationFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractControllerAuthenticationFilter.class); private static final Logger LOGGER = LoggerFactory.getLogger(AbstractControllerAuthenticationFilter.class);
@@ -54,4 +57,9 @@ public abstract class AbstractControllerAuthenticationFilter implements PreAuthe
} }
} }
@Override
public Collection<GrantedAuthority> getSuccessfulAuthenticationAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE));
}
} }

View File

@@ -8,15 +8,10 @@
*/ */
package org.eclipse.hawkbit.security; package org.eclipse.hawkbit.security;
import java.util.Arrays;
import java.util.Collection;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
/** /**
* An pre-authenticated processing filter which add the * An pre-authenticated processing filter which add the
@@ -59,9 +54,4 @@ public class ControllerPreAuthenticatedAnonymousDownload extends AbstractControl
protected String getTenantConfigurationKey() { protected String getTenantConfigurationKey() {
return TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED; return TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED;
} }
@Override
public Collection<GrantedAuthority> getSuccessfulAuthenticationAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE));
}
} }

View File

@@ -14,7 +14,7 @@ package org.eclipse.hawkbit.security;
* *
* @see DdiSecurityProperties * @see DdiSecurityProperties
*/ */
public class ControllerPreAuthenticatedAnonymousFilter implements PreAuthentificationFilter { public class ControllerPreAuthenticatedAnonymousFilter implements PreAuthenticationFilter {
private final DdiSecurityProperties ddiSecurityConfiguration; private final DdiSecurityProperties ddiSecurityConfiguration;

View File

@@ -12,7 +12,6 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
@@ -20,7 +19,6 @@ import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
/** /**
@@ -91,19 +89,17 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
final Object credentials = token.getCredentials(); final Object credentials = token.getCredentials();
final Object principal = token.getPrincipal(); final Object principal = token.getPrincipal();
final Object tokenDetails = token.getDetails(); final Object tokenDetails = token.getDetails();
final Collection<GrantedAuthority> authorities = token.getAuthorities();
if (principal == null) { if (principal == null) {
throw new BadCredentialsException("The provided principal and credentials are not match"); throw new BadCredentialsException("The provided principal and credentials are not match");
} }
boolean successAuthentication = calculateAuthenticationSuccess(principal, credentials, tokenDetails); final boolean successAuthentication = calculateAuthenticationSuccess(principal, credentials, tokenDetails);
if (successAuthentication) { if (successAuthentication) {
final Collection<GrantedAuthority> controllerAuthorities = new ArrayList<>();
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, authorities);
successToken.setDetails(tokenDetails); successToken.setDetails(tokenDetails);
return successToken; return successToken;
} }
@@ -132,7 +128,8 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
* @return <code>true</code> if authentication succeeded, otherwise * @return <code>true</code> if authentication succeeded, otherwise
* <code>false</code> * <code>false</code>
*/ */
private boolean calculateAuthenticationSuccess(Object principal, Object credentials, Object tokenDetails) { private boolean calculateAuthenticationSuccess(final Object principal, final Object credentials,
final Object tokenDetails) {
boolean successAuthentication = false; boolean successAuthentication = false;
if (credentials instanceof Collection) { if (credentials instanceof Collection) {
final Collection<?> multiValueCredentials = (Collection<?>) credentials; final Collection<?> multiValueCredentials = (Collection<?>) credentials;

View File

@@ -15,9 +15,9 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
/** /**
* Interface for Pre Authentification. * Interface for Pre Authentication.
*/ */
public interface PreAuthentificationFilter { public interface PreAuthenticationFilter {
/** /**
* Check if the filter is enabled. * Check if the filter is enabled.

View File

@@ -56,6 +56,6 @@ public class ControllerPreAuthenticatedAnonymousDownloadTest {
public void successfulAuthenticationAdditionalAuthoritiesForDownload() { public void successfulAuthenticationAdditionalAuthoritiesForDownload() {
assertThat(underTest.getSuccessfulAuthenticationAuthorities()) assertThat(underTest.getSuccessfulAuthenticationAuthorities())
.as("Additional authorities should be containing the download anonymous role") .as("Additional authorities should be containing the download anonymous role")
.contains(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE)); .contains(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE));
} }
} }