diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/cache/DownloadIdCacheAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/cache/DownloadIdCacheAutoConfiguration.java deleted file mode 100644 index fb9bd6f75f..0000000000 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/cache/DownloadIdCacheAutoConfiguration.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.autoconfigure.cache; - -import org.eclipse.hawkbit.cache.DefaultDownloadIdCache; -import org.eclipse.hawkbit.cache.DownloadIdCache; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.cache.CacheManager; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * A configuration for configuring a cache for the download id's. - * - * This is done by providing a named cache. - */ -@Configuration -public class DownloadIdCacheAutoConfiguration { - - /** - * Bean for the downloadId cache that returns the DefaultDownloadIdCache. - * The DefaultDownloadIdCache cannot be used within a cluster because the - * downloadId cache is not shared among notes. This means, a downloadId - * which is stored on note A for downloading an artifact can only be used - * for downloading the artifact form node A. - * - * @return the DefaultDownloadIdCache - */ - @Bean - @ConditionalOnMissingBean - public DownloadIdCache downloadIdCache(final CacheManager cacheManager) { - return new DefaultDownloadIdCache(cacheManager); - } - -} diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java index 00753bd9bc..1694e00137 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java @@ -16,7 +16,6 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; -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.im.authentication.SpPermission; @@ -34,7 +33,6 @@ import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateSecurityTokenFilter; import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter; import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedSecurityHeaderFilter; -import org.eclipse.hawkbit.security.HttpDownloadAuthenticationFilter; import org.eclipse.hawkbit.security.MDCHandler; import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider; import org.eclipse.hawkbit.security.SystemSecurityContext; @@ -76,8 +74,7 @@ import org.springframework.web.cors.CorsConfigurationSource; /** - * All configurations related to HawkBit's authentication and authorization - * layer. + * All configurations related to HawkBit's authentication and authorization layer. */ @Slf4j @Configuration @@ -359,40 +356,6 @@ private static FilterRegistrationBean dosFilter(final Collection armrRepository.anyRequest().authenticated()) - .csrf(AbstractHttpConfigurer::disable) - .anonymous(AbstractHttpConfigurer::disable) - .addFilterBefore(downloadIdAuthenticationFilter, AuthorizationFilter.class) - .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); - - MDCHandler.Filter.addLoggingFilter(http); - - return http.build(); - } - } - /** * Security configuration for the REST management API. */ @@ -420,8 +383,7 @@ public FilterRegistrationBean dosFilterREST() { securityProperties.getDos().getFilter(), securityProperties.getClients()); filterRegBean.setUrlPatterns(List.of( MgmtRestConstants.BASE_REST_MAPPING + "/*", - MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/*", - MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING_BASE + "/*")); + MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/*")); filterRegBean.setOrder(DOS_FILTER_ORDER); filterRegBean.setName("dosMgmtFilter"); diff --git a/hawkbit-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/hawkbit-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 9220326de5..ba9cb89b9e 100644 --- a/hawkbit-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/hawkbit-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,5 +1,4 @@ org.eclipse.hawkbit.autoconfigure.cache.CacheAutoConfiguration -org.eclipse.hawkbit.autoconfigure.cache.DownloadIdCacheAutoConfiguration org.eclipse.hawkbit.autoconfigure.ddi.DDiApiAutoConfiguration org.eclipse.hawkbit.autoconfigure.dmf.amqp.DmfApiAutoConfiguration org.eclipse.hawkbit.autoconfigure.mgmt.MgmtApiAutoConfiguration diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DefaultDownloadIdCache.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DefaultDownloadIdCache.java deleted file mode 100644 index 62e123ac4e..0000000000 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DefaultDownloadIdCache.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.cache; - -import java.util.Objects; - -import org.springframework.cache.Cache; -import org.springframework.cache.Cache.ValueWrapper; -import org.springframework.cache.CacheManager; - -/** - * A default implementation of the {@link DownloadIdCache} which uses the - * {@link CacheManager} implementation to store the download-ids. - */ -public class DefaultDownloadIdCache implements DownloadIdCache { - - static final String DOWNLOAD_ID_CACHE = "DownloadIdCache"; - - private final CacheManager cacheManager; - - /** - * @param cacheManager the underlying cache-manager to store the download-ids - */ - public DefaultDownloadIdCache(final CacheManager cacheManager) { - this.cacheManager = cacheManager; - } - - @Override - public void put(final String downloadId, final DownloadArtifactCache object) { - getCache().put(downloadId, object); - } - - @Override - public DownloadArtifactCache get(final String downloadId) { - final ValueWrapper valueWrapper = getCache().get(downloadId); - return (valueWrapper == null) ? null : (DownloadArtifactCache) valueWrapper.get(); - } - - @Override - public void evict(final String downloadId) { - getCache().evict(downloadId); - } - - private Cache getCache() { - final Cache cache = (cacheManager instanceof TenancyCacheManager) - ? ((TenancyCacheManager) cacheManager).getDirectCache(DOWNLOAD_ID_CACHE) - : cacheManager.getCache(DOWNLOAD_ID_CACHE); - return Objects.requireNonNull(cache, "Cache(s) returned by cache-manager must not be null!"); - } -} diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DownloadArtifactCache.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DownloadArtifactCache.java deleted file mode 100644 index f714170f4a..0000000000 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DownloadArtifactCache.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.cache; - -/** - * Cache Object for downloading an artifact. - */ -public class DownloadArtifactCache { - - private final DownloadType downloadType; - private final String id; - - /** - * Constructor. - * - * @param downloadType - * the type for searching the artifact. - * @param id - * the searching id e.g. sha1, md5 - */ - public DownloadArtifactCache(final DownloadType downloadType, final String id) { - this.downloadType = downloadType; - this.id = id; - } - - public String getId() { - return id; - } - - public DownloadType getDownloadType() { - return downloadType; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((downloadType == null) ? 0 : downloadType.hashCode()); - result = prime * result + ((id == null) ? 0 : id.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final DownloadArtifactCache other = (DownloadArtifactCache) obj; - if (downloadType != other.downloadType) { - return false; - } - if (id == null) { - if (other.id != null) { - return false; - } - } else if (!id.equals(other.id)) { - return false; - } - return true; - } -} diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DownloadIdCache.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DownloadIdCache.java deleted file mode 100644 index e2202e6198..0000000000 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DownloadIdCache.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.cache; - -/** - * A interface declaration of the download-id-cache which allows to store - * volatile generated download-IDs used to have a single unique download-request - * e.g. for distributed download-server. - * - * A valid download-id is created during a successful download-authorization - * request e.g. from a download-server. In the DMF response a unique - * download-URL containing a generated download-id which can be used to download - * the artifact using this single download-url. - * - * The {@link DownloadIdCache} handles storing this unique download-id from the - * DMF authorization request until the actual artifact download-request via HTTP - * with the unique ID is performed. - * - */ -public interface DownloadIdCache { - - /** - * Puts a given artifact cache object with the given downloadId key into the - * cache. - * - * @param downloadId - * the ID to store the cache object to look it up later on - * @param downloadArtifactCacheObject - * the object to store into the cache - */ - void put(final String downloadId, final DownloadArtifactCache downloadArtifactCacheObject); - - /** - * Retrieves a {@link DownloadArtifactCache} by a given downloadId. - * - * @param downloadId - * the ID to retrieve the artifact cache object - * @return the found {@link DownloadArtifactCache} or {@code null} if none - * exists for the given ID - */ - DownloadArtifactCache get(final String downloadId); - - /** - * Evicts a {@link DownloadArtifactCache} for the given downloadId - * - * @param downloadId - * the ID to be evicted - */ - void evict(String downloadId); - -} diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DownloadType.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DownloadType.java deleted file mode 100644 index 10e642f037..0000000000 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DownloadType.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.cache; - -/** - * The type of the id which is saved. - */ -public enum DownloadType { - BY_SHA1 -} diff --git a/hawkbit-core/src/test/java/org/eclipse/hawkbit/cache/DefaultDownloadIdCacheTest.java b/hawkbit-core/src/test/java/org/eclipse/hawkbit/cache/DefaultDownloadIdCacheTest.java deleted file mode 100644 index 9910c9769f..0000000000 --- a/hawkbit-core/src/test/java/org/eclipse/hawkbit/cache/DefaultDownloadIdCacheTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.cache; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; -import org.springframework.cache.support.SimpleValueWrapper; - -import io.qameta.allure.Description; -import io.qameta.allure.Feature; -import io.qameta.allure.Story; - -@Feature("Unit Tests - Cache") -@Story("Download ID Cache") -@ExtendWith(MockitoExtension.class) -class DefaultDownloadIdCacheTest { - - @Mock - private CacheManager cacheManagerMock; - - @Mock - private TenancyCacheManager tenancyCacheManagerMock; - - @Mock - private Cache cacheMock; - - @Captor - private ArgumentCaptor cacheManagerKeyCaptor; - - @Captor - private ArgumentCaptor cacheManagerValueCaptor; - - private DefaultDownloadIdCache underTest; - - private final String knownKey = "12345"; - - @BeforeEach - public void before() { - underTest = new DefaultDownloadIdCache(cacheManagerMock); - lenient().when(cacheManagerMock.getCache(DefaultDownloadIdCache.DOWNLOAD_ID_CACHE)).thenReturn(cacheMock); - } - - @Test - @Description("Verifies that putting key and value is delegated to the CacheManager implementation") - void putKeyAndValueIsDelegatedToCacheManager() { - final DownloadArtifactCache value = new DownloadArtifactCache(DownloadType.BY_SHA1, knownKey); - - underTest.put(knownKey, value); - - verify(cacheMock).put(cacheManagerKeyCaptor.capture(), cacheManagerValueCaptor.capture()); - - assertThat(cacheManagerKeyCaptor.getValue()).isEqualTo(knownKey); - assertThat(cacheManagerValueCaptor.getValue()).isEqualTo(value); - } - - @Test - @Description("Verifies that evicting a key is delegated to the CacheManager implementation") - void evictKeyIsDelegatedToCacheManager() { - - underTest.evict(knownKey); - - verify(cacheMock).evict(cacheManagerKeyCaptor.capture()); - - assertThat(cacheManagerKeyCaptor.getValue()).isEqualTo(knownKey); - } - - @Test - @Description("Verifies that retrieving a value for a specific key is delegated to the CacheManager implementation") - void getValueReturnsTheAssociatedValueForKey() { - final String knownKey = "12345"; - final DownloadArtifactCache knownValue = new DownloadArtifactCache(DownloadType.BY_SHA1, knownKey); - - when(cacheMock.get(knownKey)).thenReturn(new SimpleValueWrapper(knownValue)); - - final DownloadArtifactCache downloadArtifactCache = underTest.get(knownKey); - - assertThat(downloadArtifactCache).isEqualTo(knownValue); - } - - @Test - @Description("Verifies that retrieving a null value for a specific key is delegated to the CacheManager implementation") - void getValueReturnsNullIfNoKeyIsAssociated() { - - when(cacheMock.get(knownKey)).thenReturn(new SimpleValueWrapper(null)); - - final DownloadArtifactCache downloadArtifactCache = underTest.get(knownKey); - - assertThat(downloadArtifactCache).isNull(); - } - - @Test - @Description("Verifies that TenancyCacheManager is using direct cache because download-ids are global unique and don't need to run as tenant aware") - void tenancyCacheManagerIsUsingDirectCache() { - - when(tenancyCacheManagerMock.getDirectCache(DefaultDownloadIdCache.DOWNLOAD_ID_CACHE)).thenReturn(cacheMock); - underTest = new DefaultDownloadIdCache(tenancyCacheManagerMock); - - final DownloadArtifactCache value = new DownloadArtifactCache(DownloadType.BY_SHA1, knownKey); - - underTest.put(knownKey, value); - - verify(cacheMock).put(cacheManagerKeyCaptor.capture (), cacheManagerValueCaptor.capture()); - - verify(tenancyCacheManagerMock).getDirectCache(DefaultDownloadIdCache.DOWNLOAD_ID_CACHE); - assertThat(cacheManagerKeyCaptor.getValue()).isEqualTo(knownKey); - assertThat(cacheManagerValueCaptor.getValue()).isEqualTo(value); - } -} \ No newline at end of file diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpAuthenticationMessageHandler.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpAuthenticationMessageHandler.java deleted file mode 100644 index 911fd4be9c..0000000000 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpAuthenticationMessageHandler.java +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.amqp; - -import java.net.URISyntaxException; -import java.util.Optional; -import java.util.UUID; - -import lombok.extern.slf4j.Slf4j; -import org.eclipse.hawkbit.api.HostnameResolver; -import org.eclipse.hawkbit.cache.DownloadArtifactCache; -import org.eclipse.hawkbit.cache.DownloadIdCache; -import org.eclipse.hawkbit.cache.DownloadType; -import org.eclipse.hawkbit.dmf.json.model.DmfArtifact; -import org.eclipse.hawkbit.dmf.json.model.DmfArtifactHash; -import org.eclipse.hawkbit.dmf.json.model.DmfDownloadResponse; -import org.eclipse.hawkbit.repository.ArtifactManagement; -import org.eclipse.hawkbit.repository.ControllerManagement; -import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; -import org.eclipse.hawkbit.repository.model.Artifact; -import org.eclipse.hawkbit.security.DmfTenantSecurityToken; -import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource; -import org.eclipse.hawkbit.tenancy.TenantAware; -import org.springframework.amqp.AmqpRejectAndDontRequeueException; -import org.springframework.amqp.core.Message; -import org.springframework.amqp.rabbit.annotation.RabbitListener; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.http.HttpStatus; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.CredentialsExpiredException; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.util.UriComponentsBuilder; - -/** - * - * {@link AmqpMessageHandlerService} handles all incoming target authentication - * AMQP messages that can be used by 3rd party CDN services to check if a target - * is permitted to download certain artifact. This is handled by the queue that - * is configured for the property - * hawkbit.dmf.rabbitmq.authenticationReceiverQueue. - */ -@Slf4j -public class AmqpAuthenticationMessageHandler extends BaseAmqpService { - - private final AmqpControllerAuthentication authenticationManager; - - private final ArtifactManagement artifactManagement; - - private final DownloadIdCache cache; - - private final HostnameResolver hostnameResolver; - - private final ControllerManagement controllerManagement; - - private final TenantAware tenantAware; - - /** - * @param rabbitTemplate - * the configured amqp template. - * @param artifactManagement - * for artifact URI generation - * @param cache - * for download Ids - * @param hostnameResolver - * for resolving the host for downloads - * @param authenticationManager - * for target authentication - * @param controllerManagement - * for target repo access - * @param tenantAware - * to access current tenant - */ - public AmqpAuthenticationMessageHandler(final RabbitTemplate rabbitTemplate, - final AmqpControllerAuthentication authenticationManager, final ArtifactManagement artifactManagement, - final DownloadIdCache cache, final HostnameResolver hostnameResolver, - final ControllerManagement controllerManagement, final TenantAware tenantAware) { - super(rabbitTemplate); - this.authenticationManager = authenticationManager; - this.artifactManagement = artifactManagement; - this.cache = cache; - this.hostnameResolver = hostnameResolver; - this.controllerManagement = controllerManagement; - this.tenantAware = tenantAware; - } - - /** - * Executed on an authentication request. - * - * @param message - * the amqp message - * @return the rpc message back to supplier. - */ - @RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue:authentication_receiver}", containerFactory = "listenerContainerFactory") - public Message onAuthenticationRequest(final Message message) { - checkContentTypeJson(message); - final SecurityContext oldContext = SecurityContextHolder.getContext(); - try { - return handleAuthenticationMessage(message); - } catch (final RuntimeException ex) { - throw new AmqpRejectAndDontRequeueException(ex); - } finally { - SecurityContextHolder.setContext(oldContext); - } - } - - /** - * check action for this download purposes, the method will throw an - * EntityNotFoundException in case the controller is not allowed to download - * this file because it's not assigned to an action and not assigned to this - * controller. Otherwise no controllerId is set = anonymous download - * - * @param securityToken - * the security token which holds the target ID to check on - * @param sha1Hash - * of the artifact to verify if the given target is allowed to - * download it - */ - private void checkIfArtifactIsAssignedToTarget(final DmfTenantSecurityToken securityToken, final String sha1Hash) { - - if (securityToken.getControllerId() != null) { - checkByControllerId(sha1Hash, securityToken.getControllerId()); - } else if (securityToken.getTargetId() != null) { - checkByTargetId(sha1Hash, securityToken.getTargetId()); - } else { - log.info("anonymous download no authentication check for artifact {}", sha1Hash); - } - - } - - private void checkByTargetId(final String sha1Hash, final Long targetId) { - log.debug("no anonymous download request, doing authentication check for target {} and artifact {}", targetId, - sha1Hash); - if (!controllerManagement.hasTargetArtifactAssigned(targetId, sha1Hash)) { - log.info("target {} tried to download artifact {} which is not assigned to the target", targetId, sha1Hash); - throw new EntityNotFoundException(); - } - log.info("download security check for target {} and artifact {} granted", targetId, sha1Hash); - } - - private void checkByControllerId(final String sha1Hash, final String controllerId) { - log.debug("no anonymous download request, doing authentication check for target {} and artifact {}", - controllerId, sha1Hash); - if (!controllerManagement.hasTargetArtifactAssigned(controllerId, sha1Hash)) { - log.info("target {} tried to download artifact {} which is not assigned to the target", controllerId, - sha1Hash); - throw new EntityNotFoundException(); - } - log.info("download security check for target {} and artifact {} granted", controllerId, sha1Hash); - } - - private Optional findArtifactByFileResource(final FileResource fileResource) { - - if (fileResource == null) { - return Optional.empty(); - } - - if (fileResource.getSha1() != null) { - return artifactManagement.findFirstBySHA1(fileResource.getSha1()); - } - - if (fileResource.getFilename() != null) { - return artifactManagement.getByFilename(fileResource.getFilename()); - } - - if (fileResource.getArtifactId() != null) { - return artifactManagement.get(fileResource.getArtifactId()); - } - - if (fileResource.getSoftwareModuleFilenameResource() != null) { - return artifactManagement.getByFilenameAndSoftwareModule( - fileResource.getSoftwareModuleFilenameResource().getFilename(), - fileResource.getSoftwareModuleFilenameResource().getSoftwareModuleId()); - } - - return Optional.empty(); - } - - private static DmfArtifact convertDbArtifact(final Artifact dbArtifact) { - final DmfArtifact artifact = new DmfArtifact(); - artifact.setSize(dbArtifact.getSize()); - artifact.setLastModified(dbArtifact.getCreatedAt()); - artifact.setHashes(new DmfArtifactHash(dbArtifact.getSha1Hash(), dbArtifact.getMd5Hash())); - return artifact; - } - - // suppress warning, EntityNotFoundException has not to be logged or - // rethrown as the exception has no valuable information - @SuppressWarnings("squid:S1166") - private Message handleAuthenticationMessage(final Message message) { - final DmfDownloadResponse authenticationResponse = new DmfDownloadResponse(); - final DmfTenantSecurityToken securityToken = convertMessage(message, DmfTenantSecurityToken.class); - final FileResource fileResource = securityToken.getFileResource(); - try { - SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(securityToken)); - - final Artifact artifact = findArtifactByFileResource(fileResource) - .orElseThrow(EntityNotFoundException::new); - - checkIfArtifactIsAssignedToTarget(securityToken, artifact.getSha1Hash()); - - final DmfArtifact dmfArtifact = convertDbArtifact(artifact); - - authenticationResponse.setArtifact(dmfArtifact); - final String downloadId = UUID.randomUUID().toString(); - // SHA1 key is set, download by SHA1 - final DownloadArtifactCache downloadCache = new DownloadArtifactCache(DownloadType.BY_SHA1, - artifact.getSha1Hash()); - cache.put(downloadId, downloadCache); - authenticationResponse.setDownloadUrl(UriComponentsBuilder - .fromUri(hostnameResolver.resolveHostname().toURI()).path("/api/v1/downloadserver/downloadId/") - .path(tenantAware.getCurrentTenant()).path("/").path(downloadId).build().toUriString()); - authenticationResponse.setResponseCode(HttpStatus.OK.value()); - } catch (final BadCredentialsException | AuthenticationServiceException | CredentialsExpiredException e) { - log.error("Login failed", e); - authenticationResponse.setResponseCode(HttpStatus.FORBIDDEN.value()); - authenticationResponse.setMessage("Login failed"); - } catch (final URISyntaxException e) { - log.error("URI build exception", e); - authenticationResponse.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); - authenticationResponse.setMessage("Building download URI failed"); - } catch (final EntityNotFoundException e) { - final String errorMessage = "Artifact for resource " + fileResource + " not found "; - log.info(errorMessage); - authenticationResponse.setResponseCode(HttpStatus.NOT_FOUND.value()); - authenticationResponse.setMessage(errorMessage); - } - - return getMessageConverter().toMessage(authenticationResponse, message.getMessageProperties()); - } - -} diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java index 777861927e..cbec6aae90 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java @@ -11,10 +11,7 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.hawkbit.api.ArtifactUrlHandler; -import org.eclipse.hawkbit.api.HostnameResolver; -import org.eclipse.hawkbit.cache.DownloadIdCache; import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings; -import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ConfirmationManagement; import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.DeploymentManagement; @@ -24,9 +21,7 @@ import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; -import org.eclipse.hawkbit.security.DdiSecurityProperties; import org.eclipse.hawkbit.security.SystemSecurityContext; -import org.eclipse.hawkbit.tenancy.TenantAware; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.FanoutExchange; @@ -281,20 +276,6 @@ public AmqpMessageHandlerService amqpMessageHandlerService(final RabbitTemplate entityFactory, systemSecurityContext, tenantConfigurationManagement, confirmationManagement); } - /** - * Create AMQP handler service bean for authentication messages. - * - * @return handler service bean - */ - @Bean - AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandler(final RabbitTemplate rabbitTemplate, - final AmqpControllerAuthentication authenticationManager, final ArtifactManagement artifactManagement, - final DownloadIdCache downloadIdCache, final HostnameResolver hostnameResolver, - final ControllerManagement controllerManagement, final TenantAware tenantAware) { - return new AmqpAuthenticationMessageHandler(rabbitTemplate, authenticationManager, artifactManagement, - downloadIdCache, hostnameResolver, controllerManagement, tenantAware); - } - /** * Create default amqp sender service bean. * @@ -322,33 +303,6 @@ public RabbitListenerContainerFactory listenerCo return factory; } - /** - * create the authentication bean for controller over amqp. - * - * @param systemManagement - * the systemManagement - * @param controllerManagement - * the controllerManagement - * @param tenantConfigurationManagement - * the tenantConfigurationManagement - * @param tenantAware - * the tenantAware - * @param ddiSecruityProperties - * the ddiSecruityProperties - * @param systemSecurityContext - * the systemSecurityContext - * @return the bean - */ - @Bean - @ConditionalOnMissingBean(AmqpControllerAuthentication.class) - public AmqpControllerAuthentication amqpControllerAuthentication(final SystemManagement systemManagement, - final ControllerManagement controllerManagement, - final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware, - final DdiSecurityProperties ddiSecruityProperties, final SystemSecurityContext systemSecurityContext) { - return new AmqpControllerAuthentication(systemManagement, controllerManagement, tenantConfigurationManagement, - tenantAware, ddiSecruityProperties, systemSecurityContext); - } - @Bean @ConditionalOnMissingBean(AmqpMessageDispatcherService.class) AmqpMessageDispatcherService amqpMessageDispatcherService(final RabbitTemplate rabbitTemplate, diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentication.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentication.java deleted file mode 100644 index 9982e9ee92..0000000000 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentication.java +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.amqp; - -import java.util.ArrayList; -import java.util.List; - -import jakarta.annotation.PostConstruct; - -import lombok.extern.slf4j.Slf4j; -import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; -import org.eclipse.hawkbit.repository.ControllerManagement; -import org.eclipse.hawkbit.repository.SystemManagement; -import org.eclipse.hawkbit.repository.TenantConfigurationManagement; -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.ControllerPreAuthenticatedSecurityHeaderFilter; -import org.eclipse.hawkbit.security.DdiSecurityProperties; -import org.eclipse.hawkbit.security.DmfTenantSecurityToken; -import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider; -import org.eclipse.hawkbit.security.PreAuthenticationFilter; -import org.eclipse.hawkbit.security.SystemSecurityContext; -import org.eclipse.hawkbit.tenancy.TenantAware; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; - -/** - * A controller which handles the DMF AMQP authentication. - */ -@Slf4j -public class AmqpControllerAuthentication { - - private final PreAuthTokenSourceTrustAuthenticationProvider preAuthenticatedAuthenticationProvider = new PreAuthTokenSourceTrustAuthenticationProvider(); - - private List filterChain; - - private final ControllerManagement controllerManagement; - - private final SystemManagement systemManagement; - - private final TenantConfigurationManagement tenantConfigurationManagement; - - private final TenantAware tenantAware; - - private final DdiSecurityProperties ddiSecruityProperties; - - private final SystemSecurityContext systemSecurityContext; - - /** - * Constructor. - * - * @param systemManagement - * @param controllerManagement - * @param tenantConfigurationManagement - * @param tenantAware - * current tenant - * @param ddiSecruityProperties - * security configurations - * @param systemSecurityContext - * security context - */ - public AmqpControllerAuthentication(final SystemManagement systemManagement, - final ControllerManagement controllerManagement, - final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware, - final DdiSecurityProperties ddiSecruityProperties, final SystemSecurityContext systemSecurityContext) { - this.controllerManagement = controllerManagement; - this.systemManagement = systemManagement; - this.tenantConfigurationManagement = tenantConfigurationManagement; - this.tenantAware = tenantAware; - this.ddiSecruityProperties = ddiSecruityProperties; - this.systemSecurityContext = systemSecurityContext; - } - - /** - * Called by spring when bean instantiated and autowired. - */ - @PostConstruct - public void postConstruct() { - addFilter(); - } - - private void addFilter() { - filterChain = new ArrayList<>(5); - - final ControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new ControllerPreAuthenticatedGatewaySecurityTokenFilter( - tenantConfigurationManagement, tenantAware, systemSecurityContext); - filterChain.add(gatewaySecurityTokenFilter); - - final ControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new ControllerPreAuthenticatedSecurityHeaderFilter( - ddiSecruityProperties.getRp().getCnHeader(), ddiSecruityProperties.getRp().getSslIssuerHashHeader(), - tenantConfigurationManagement, tenantAware, systemSecurityContext); - filterChain.add(securityHeaderFilter); - - final ControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new ControllerPreAuthenticateSecurityTokenFilter( - tenantConfigurationManagement, controllerManagement, tenantAware, systemSecurityContext); - filterChain.add(securityTokenFilter); - - final ControllerPreAuthenticatedAnonymousDownload anonymousDownloadFilter = new ControllerPreAuthenticatedAnonymousDownload( - tenantConfigurationManagement, tenantAware, systemSecurityContext); - filterChain.add(anonymousDownloadFilter); - - filterChain.add(new ControllerPreAuthenticatedAnonymousFilter(ddiSecruityProperties)); - } - - /** - * Performs authentication with the security token. - * - * @param securityToken - * the authentication request object - * @return the authentication object - */ - public Authentication doAuthenticate(final DmfTenantSecurityToken securityToken) { - resolveTenant(securityToken); - PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(null, null); - for (final PreAuthenticationFilter filter : filterChain) { - final PreAuthenticatedAuthenticationToken authenticationRest = createAuthentication(filter, securityToken); - if (authenticationRest != null) { - authentication = authenticationRest; - authentication.setDetails(new TenantAwareAuthenticationDetails(securityToken.getTenant(), true)); - break; - } - } - return preAuthenticatedAuthenticationProvider.authenticate(authentication); - - } - - private void resolveTenant(final DmfTenantSecurityToken securityToken) { - if (securityToken.getTenant() == null) { - securityToken.setTenant(systemSecurityContext - .runAsSystem(() -> systemManagement.getTenantMetadata(securityToken.getTenantId()).getTenant())); - } - - } - - private static PreAuthenticatedAuthenticationToken createAuthentication(final PreAuthenticationFilter filter, - final DmfTenantSecurityToken securityToken) { - - if (!filter.isEnable(securityToken)) { - return null; - } - - final Object principal = filter.getPreAuthenticatedPrincipal(securityToken); - final Object credentials = filter.getPreAuthenticatedCredentials(securityToken); - - if (principal == null) { - log.debug("No pre-authenticated principal found in message"); - return null; - } - - log.debug("preAuthenticatedPrincipal = {} trying to authenticate", principal); - - return new PreAuthenticatedAuthenticationToken(principal, credentials, - filter.getSuccessfulAuthenticationAuthorities()); - } - -} diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java deleted file mode 100644 index da31d22152..0000000000 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java +++ /dev/null @@ -1,402 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.amqp; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Optional; - -import org.eclipse.hawkbit.api.HostnameResolver; -import org.eclipse.hawkbit.cache.DownloadIdCache; -import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; -import org.eclipse.hawkbit.dmf.amqp.api.MessageType; -import org.eclipse.hawkbit.dmf.json.model.DmfDownloadResponse; -import org.eclipse.hawkbit.repository.ArtifactManagement; -import org.eclipse.hawkbit.repository.ConfirmationManagement; -import org.eclipse.hawkbit.repository.ControllerManagement; -import org.eclipse.hawkbit.repository.SystemManagement; -import org.eclipse.hawkbit.repository.TenantConfigurationManagement; -import org.eclipse.hawkbit.repository.jpa.JpaEntityFactory; -import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact; -import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; -import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType; -import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; -import org.eclipse.hawkbit.repository.model.TenantMetaData; -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.DmfTenantSecurityToken; -import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource; -import org.eclipse.hawkbit.security.SecurityContextSerializer; -import org.eclipse.hawkbit.security.SecurityContextTenantAware; -import org.eclipse.hawkbit.security.SystemSecurityContext; -import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver; -import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.MessageProperties; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; -import org.springframework.amqp.support.converter.MessageConverter; -import org.springframework.http.HttpStatus; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; - -import io.qameta.allure.Description; -import io.qameta.allure.Feature; -import io.qameta.allure.Story; - -/** - * - * Test Amqp controller authentication. - */ -@Feature("Component Tests - Device Management Federation API") -@Story("AmqpController Authentication Test") -@ExtendWith(MockitoExtension.class) -public class AmqpControllerAuthenticationTest { - - private static final String SHA1 = "12345"; - private static final Long ARTIFACT_ID = 1123L; - private static final String TENANT = "DEFAULT"; - private static final Long TENANT_ID = 123L; - private static final String CONTROLLER_ID = "123"; - private static final Long TARGET_ID = 123L; - private AmqpMessageHandlerService amqpMessageHandlerService; - private AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandlerService; - - private final MessageConverter messageConverter = new Jackson2JsonMessageConverter(); - - private AmqpControllerAuthentication authenticationManager; - - private JpaArtifact testArtifact; - - @Mock - private TenantConfigurationManagement tenantConfigurationManagementMock; - - @Mock - private SystemManagement systemManagement; - - @Mock - private DownloadIdCache cacheMock; - - @Mock - private HostnameResolver hostnameResolverMock; - - @Mock - private ArtifactManagement artifactManagementMock; - - @Mock - private Target targetMock; - - @Mock - private UserAuthoritiesResolver authoritiesResolver; - - @Mock - private SecurityContextSerializer securityContextSerializer; - - @Mock - private RabbitTemplate rabbitTemplate; - - @Mock - private ControllerManagement controllerManagement; - - @Mock - private ConfirmationManagement confirmationManagement; - - @Mock - private DdiSecurityProperties securityProperties; - - @Mock - private Rp rp; - - @Mock - private DdiSecurityProperties.Authentication ddiAuthentication; - - @Mock - private Anonymous anonymous; - - private static final TenantConfigurationValue CONFIG_VALUE_FALSE = TenantConfigurationValue - . builder().value(Boolean.FALSE).build(); - - private static final TenantConfigurationValue CONFIG_VALUE_TRUE = TenantConfigurationValue - . builder().value(Boolean.TRUE).build(); - - @BeforeEach - public void before() { - when(securityProperties.getRp()).thenReturn(rp); - when(rp.getSslIssuerHashHeader()).thenReturn("X-Ssl-Issuer-Hash-%d"); - when(tenantConfigurationManagementMock.getConfigurationValue(any(), eq(Boolean.class))) - .thenReturn(CONFIG_VALUE_FALSE); - - final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(authoritiesResolver, securityContextSerializer); - final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware); - - authenticationManager = new AmqpControllerAuthentication(systemManagement, controllerManagement, - tenantConfigurationManagementMock, tenantAware, securityProperties, systemSecurityContext); - - authenticationManager.postConstruct(); - - testArtifact = new JpaArtifact(SHA1, "afilename", - new JpaSoftwareModule(new JpaSoftwareModuleType("a key", "a name", null, 1), "a name", "a version")); - testArtifact.setId(1L); - - amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, - mock(AmqpMessageDispatcherService.class), controllerManagement, new JpaEntityFactory(), - systemSecurityContext, tenantConfigurationManagementMock, confirmationManagement); - - amqpAuthenticationMessageHandlerService = new AmqpAuthenticationMessageHandler(rabbitTemplate, - authenticationManager, artifactManagementMock, cacheMock, hostnameResolverMock, controllerManagement, - tenantAware); - } - - private void mockAuthenticationWithoutPrincipal() { - lenient().when(securityProperties.getAuthentication()).thenReturn(ddiAuthentication); - lenient().when(ddiAuthentication.getAnonymous()).thenReturn(anonymous); - lenient().when(anonymous.isEnabled()).thenReturn(false); - } - - private void mockSuccessfulAuthentication() throws MalformedURLException { - when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); - when(hostnameResolverMock.resolveHostname()).thenReturn(new URL("http://localhost")); - when(tenantConfigurationManagementMock.getConfigurationValue( - eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) - .thenReturn(CONFIG_VALUE_TRUE); - when(targetMock.getSecurityToken()).thenReturn(CONTROLLER_ID); - when(targetMock.getControllerId()).thenReturn(CONTROLLER_ID); - } - - @Test - @Description("Tests authentication manager without principal") - public void testAuthenticationBadCredentialsWithoutPrincipal() { - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, - TARGET_ID, FileResource.createFileResourceBySha1(SHA1)); - - mockAuthenticationWithoutPrincipal(); - - assertThatExceptionOfType(BadCredentialsException.class) - .as("BadCredentialsException was expected since principal was missing") - .isThrownBy(() -> authenticationManager.doAuthenticate(securityToken)); - } - - @Test - @Description("Tests authentication manager without wrong credential") - public void testAuthenticationBadCredentialsWithWrongCredential() { - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, - TARGET_ID, FileResource.createFileResourceBySha1(SHA1)); - - when(tenantConfigurationManagementMock.getConfigurationValue( - eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) - .thenReturn(CONFIG_VALUE_TRUE); - - securityToken.putHeader(DmfTenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLER_ID); - - assertThatExceptionOfType(BadCredentialsException.class) - .as("BadCredentialsException was expected due to wrong credential") - .isThrownBy(() -> authenticationManager.doAuthenticate(securityToken)); - } - - @Test - @Description("Tests authentication successful") - public void testSuccessfulAuthentication() { - - when(controllerManagement.get(any(Long.class))).thenReturn(Optional.of(targetMock)); - - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, - TARGET_ID, FileResource.createFileResourceBySha1(SHA1)); - - when(tenantConfigurationManagementMock.getConfigurationValue( - eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) - .thenReturn(CONFIG_VALUE_TRUE); - when(targetMock.getSecurityToken()).thenReturn(CONTROLLER_ID); - when(targetMock.getControllerId()).thenReturn(CONTROLLER_ID); - - securityToken.putHeader(DmfTenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID); - final Authentication authentication = authenticationManager.doAuthenticate(securityToken); - assertThat(authentication).isNotNull(); - } - - @Test - @Description("Tests authentication message without principal") - public void testAuthenticationMessageBadCredentialsWithoutPrincipal() { - final MessageProperties messageProperties = createMessageProperties(null); - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, - TARGET_ID, FileResource.createFileResourceBySha1(SHA1)); - - mockAuthenticationWithoutPrincipal(); - when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); - - final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, - messageProperties); - - // test - final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); - - // verify - final DmfDownloadResponse downloadResponse = (DmfDownloadResponse) messageConverter.fromMessage(onMessage); - assertThat(downloadResponse).isNotNull(); - assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.FORBIDDEN.value()); - } - - @Test - @Description("Tests authentication message without wrong credential") - public void testAuthenticationMessageBadCredentialsWithWrongCredential() { - final MessageProperties messageProperties = createMessageProperties(null); - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, - TARGET_ID, FileResource.createFileResourceBySha1(SHA1)); - - when(tenantConfigurationManagementMock.getConfigurationValue( - eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) - .thenReturn(CONFIG_VALUE_TRUE); - - when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); - - securityToken.putHeader(DmfTenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLER_ID); - final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, - messageProperties); - - // test - final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); - - // verify - final DmfDownloadResponse downloadResponse = (DmfDownloadResponse) messageConverter.fromMessage(onMessage); - assertThat(downloadResponse).isNotNull(); - assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.FORBIDDEN.value()); - } - - @Test - @Description("Tests authentication message successful") - public void successfulMessageAuthentication() throws Exception { - - final MessageProperties messageProperties = createMessageProperties(null); - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, null, CONTROLLER_ID, null, - FileResource.createFileResourceBySha1(SHA1)); - - mockSuccessfulAuthentication(); - when(controllerManagement.getByControllerId(anyString())).thenReturn(Optional.of(targetMock)); - when(controllerManagement.hasTargetArtifactAssigned(CONTROLLER_ID, SHA1)).thenReturn(true); - when(artifactManagementMock.findFirstBySHA1(SHA1)).thenReturn(Optional.of(testArtifact)); - - securityToken.putHeader(DmfTenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID); - final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, - messageProperties); - - // test - final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); - - // verify - final DmfDownloadResponse downloadResponse = (DmfDownloadResponse) messageConverter.fromMessage(onMessage); - assertThat(downloadResponse).isNotNull(); - assertThat(downloadResponse.getDownloadUrl()).isNotNull(); - assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value()); - assertThat(SecurityContextHolder.getContext()).isNotNull(); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); - assertThat(SecurityContextHolder.getContext().getAuthentication().getClass().getName()) - .isEqualTo(PreAuthenticatedAuthenticationToken.class.getName()); - - } - - @Test - @Description("Tests authentication message successful with targetId intead of controllerId provided and artifactId instead of SHA1.") - public void successfulMessageAuthenticationWithTargetIdAndArtifactId() throws Exception { - final MessageProperties messageProperties = createMessageProperties(null); - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, null, null, TARGET_ID, - FileResource.createFileResourceByArtifactId(ARTIFACT_ID)); - - mockSuccessfulAuthentication(); - when(controllerManagement.get(any(Long.class))).thenReturn(Optional.of(targetMock)); - when(controllerManagement.hasTargetArtifactAssigned(TARGET_ID, SHA1)).thenReturn(true); - when(artifactManagementMock.get(ARTIFACT_ID)).thenReturn(Optional.of(testArtifact)); - - securityToken.putHeader(DmfTenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID); - final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, - messageProperties); - - // test - final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); - - // verify - final DmfDownloadResponse downloadResponse = (DmfDownloadResponse) messageConverter.fromMessage(onMessage); - assertThat(downloadResponse).isNotNull(); - assertThat(downloadResponse.getDownloadUrl()).isNotNull(); - assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value()); - assertThat(SecurityContextHolder.getContext()).isNotNull(); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); - assertThat(SecurityContextHolder.getContext().getAuthentication().getClass().getName()) - .isEqualTo(PreAuthenticatedAuthenticationToken.class.getName()); - - } - - @Test - @Description("Tests authentication message successful") - public void successfulMessageAuthenticationWithTenantId() throws Exception { - - final MessageProperties messageProperties = createMessageProperties(null); - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(null, TENANT_ID, CONTROLLER_ID, - TARGET_ID, FileResource.createFileResourceBySha1(SHA1)); - final TenantMetaData tenantMetaData = mock(TenantMetaData.class); - - mockSuccessfulAuthentication(); - when(controllerManagement.get(any(Long.class))).thenReturn(Optional.of(targetMock)); - when(controllerManagement.hasTargetArtifactAssigned(CONTROLLER_ID, SHA1)).thenReturn(true); - when(artifactManagementMock.findFirstBySHA1(SHA1)).thenReturn(Optional.of(testArtifact)); - when(tenantMetaData.getTenant()).thenReturn(TENANT); - when(systemManagement.getTenantMetadata(TENANT_ID)).thenReturn(tenantMetaData); - - securityToken.putHeader(DmfTenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID); - final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, - messageProperties); - - // test - final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); - - // verify - final DmfDownloadResponse downloadResponse = (DmfDownloadResponse) messageConverter.fromMessage(onMessage); - assertThat(downloadResponse).isNotNull(); - assertThat(downloadResponse.getDownloadUrl()).isNotNull(); - assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value()); - assertThat(SecurityContextHolder.getContext()).isNotNull(); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); - assertThat(SecurityContextHolder.getContext().getAuthentication().getClass().getName()) - .isEqualTo(PreAuthenticatedAuthenticationToken.class.getName()); - - } - - private MessageProperties createMessageProperties(final MessageType type) { - return createMessageProperties(type, "MyTest"); - } - - private MessageProperties createMessageProperties(final MessageType type, final String replyTo) { - final MessageProperties messageProperties = new MessageProperties(); - if (type != null) { - messageProperties.setHeader(MessageHeaderKey.TYPE, type.name()); - } - messageProperties.setHeader(MessageHeaderKey.TENANT, TENANT); - messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON); - messageProperties.setReplyTo(replyTo); - return messageProperties; - } - -} diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java index c6c3009e1b..c22bfa8392 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java @@ -22,15 +22,10 @@ import static org.mockito.Mockito.when; import java.lang.reflect.Field; -import java.net.MalformedURLException; import java.net.URI; -import java.net.URL; import java.util.Map; import java.util.Optional; -import org.eclipse.hawkbit.api.HostnameResolver; -import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; -import org.eclipse.hawkbit.cache.DownloadIdCache; import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; import org.eclipse.hawkbit.dmf.amqp.api.MessageType; @@ -39,9 +34,7 @@ import org.eclipse.hawkbit.dmf.json.model.DmfAttributeUpdate; import org.eclipse.hawkbit.dmf.json.model.DmfAutoConfirmation; import org.eclipse.hawkbit.dmf.json.model.DmfCreateThing; -import org.eclipse.hawkbit.dmf.json.model.DmfDownloadResponse; import org.eclipse.hawkbit.dmf.json.model.DmfUpdateMode; -import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ConfirmationManagement; import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.EntityFactory; @@ -54,19 +47,14 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder; import org.eclipse.hawkbit.repository.model.Action; -import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.ActionProperties; -import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; -import org.eclipse.hawkbit.security.DmfTenantSecurityToken; -import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource; import org.eclipse.hawkbit.security.SecurityContextSerializer; import org.eclipse.hawkbit.security.SecurityContextTenantAware; import org.eclipse.hawkbit.security.SecurityTokenGenerator; import org.eclipse.hawkbit.security.SystemSecurityContext; -import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -82,7 +70,6 @@ import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.MessageConversionException; import org.springframework.amqp.support.converter.MessageConverter; -import org.springframework.http.HttpStatus; import io.qameta.allure.Description; import io.qameta.allure.Feature; @@ -97,15 +84,10 @@ public class AmqpMessageHandlerServiceTest { private static final String FAIL_MESSAGE_AMQP_REJECT_REASON = AmqpRejectAndDontRequeueException.class .getSimpleName() + " was expected, "; - private static final String SHA1 = "12345"; private static final String VIRTUAL_HOST = "vHost"; private static final String TENANT = "DEFAULT"; - private static final Long TENANT_ID = 123L; - private static final String CONTROLLER_ID = "123"; - private static final Long TARGET_ID = 123L; private AmqpMessageHandlerService amqpMessageHandlerService; - private AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandlerService; private MessageConverter messageConverter; @@ -117,67 +99,37 @@ public class AmqpMessageHandlerServiceTest { @Mock private ConfirmationManagement confirmationManagementMock; - @Mock private EntityFactory entityFactoryMock; - - @Mock - private ArtifactManagement artifactManagementMock; - @Mock private TenantConfigurationManagement tenantConfigurationManagement; - - @Mock - private AmqpControllerAuthentication authenticationManagerMock; - - @Mock - private ArtifactRepository artifactRepositoryMock; - - @Mock - private DownloadIdCache downloadIdCache; - - @Mock - private HostnameResolver hostnameResolverMock; - @Mock private RabbitTemplate rabbitTemplate; - - @Mock - private TenantAware tenantAwareMock; - @Mock private UserAuthoritiesResolver authoritiesResolver; - @Mock private SecurityContextSerializer securityContextSerializer; @Captor private ArgumentCaptor> attributesCaptor; - @Captor private ArgumentCaptor targetIdCaptor; - @Captor private ArgumentCaptor initiatorCaptor; - @Captor private ArgumentCaptor remarkCaptor; - @Captor private ArgumentCaptor targetNameCaptor; - @Captor private ArgumentCaptor targetTypeNameCaptor; - @Captor private ArgumentCaptor uriCaptor; - @Captor private ArgumentCaptor modeCaptor; @BeforeEach @SuppressWarnings({ "rawtypes", "unchecked" }) - public void before() throws Exception { + public void before() { messageConverter = new Jackson2JsonMessageConverter(); lenient().when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); final TenantConfigurationValue multiAssignmentConfig = TenantConfigurationValue.builder().value(Boolean.FALSE) @@ -191,9 +143,6 @@ public void before() throws Exception { amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock, controllerManagementMock, entityFactoryMock, systemSecurityContext, tenantConfigurationManagement, confirmationManagementMock); - amqpAuthenticationMessageHandlerService = new AmqpAuthenticationMessageHandler(rabbitTemplate, - authenticationManagerMock, artifactManagementMock, downloadIdCache, hostnameResolverMock, - controllerManagementMock, tenantAwareMock); } @Test @@ -549,85 +498,12 @@ public void quotaExceeded() { VIRTUAL_HOST)); } - @Test - @Description("Tests that an download request is denied for an artifact which does not exists") - public void authenticationRequestDeniedForArtifactWhichDoesNotExists() { - final MessageProperties messageProperties = createMessageProperties(null); - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, - TARGET_ID, FileResource.createFileResourceBySha1("12345")); - final Message message = createMessage(securityToken, messageProperties); - - // test - final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); - - // verify - final DmfDownloadResponse downloadResponse = (DmfDownloadResponse) messageConverter.fromMessage(onMessage); - assertThat(downloadResponse).as("Message body should not null").isNotNull(); - assertThat(downloadResponse.getResponseCode()).as("Message body response code is wrong") - .isEqualTo(HttpStatus.NOT_FOUND.value()); - } - - @Test - @Description("Tests that an download request is denied for an artifact which is not assigned to the requested target") - public void authenticationRequestDeniedForArtifactWhichIsNotAssignedToTarget() { - final MessageProperties messageProperties = createMessageProperties(null); - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, - TARGET_ID, FileResource.createFileResourceBySha1("12345")); - final Message message = createMessage(securityToken, messageProperties); - - final Artifact localArtifactMock = mock(Artifact.class); - when(artifactManagementMock.findFirstBySHA1(anyString())).thenReturn(Optional.of(localArtifactMock)); - - // test - final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); - - // verify - final DmfDownloadResponse downloadResponse = (DmfDownloadResponse) messageConverter.fromMessage(onMessage); - assertThat(downloadResponse).as("Message body should not null").isNotNull(); - assertThat(downloadResponse.getResponseCode()).as("Message body response code is wrong") - .isEqualTo(HttpStatus.NOT_FOUND.value()); - } - - @Test - @Description("Tests that an download request is allowed for an artifact which exists and assigned to the requested target") - public void authenticationRequestAllowedForArtifactWhichExistsAndAssignedToTarget() throws MalformedURLException { - final MessageProperties messageProperties = createMessageProperties(null); - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, - TARGET_ID, FileResource.createFileResourceBySha1("12345")); - final Message message = createMessage(securityToken, messageProperties); - - // mock - final Artifact localArtifactMock = mock(Artifact.class); - when(localArtifactMock.getSha1Hash()).thenReturn(SHA1); - when(localArtifactMock.getMd5Hash()).thenReturn("md5"); - when(localArtifactMock.getSize()).thenReturn(1L); - - when(artifactManagementMock.findFirstBySHA1(SHA1)).thenReturn(Optional.of(localArtifactMock)); - when(controllerManagementMock.hasTargetArtifactAssigned(securityToken.getControllerId(), SHA1)) - .thenReturn(true); - when(hostnameResolverMock.resolveHostname()).thenReturn(new URL("http://localhost")); - - // test - final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); - - // verify - final DmfDownloadResponse downloadResponse = (DmfDownloadResponse) messageConverter.fromMessage(onMessage); - assertThat(downloadResponse).as("Message body should not null").isNotNull(); - assertThat(downloadResponse.getResponseCode()).as("Message body response code is wrong") - .isEqualTo(HttpStatus.OK.value()); - assertThat(downloadResponse.getArtifact().getSize()).as("Wrong artifact size in message body").isEqualTo(1L); - assertThat(downloadResponse.getArtifact().getHashes().getSha1()).as("Wrong sha1 hash").isEqualTo(SHA1); - assertThat(downloadResponse.getArtifact().getHashes().getMd5()).as("Wrong md5 hash").isEqualTo("md5"); - assertThat(downloadResponse.getDownloadUrl()).as("download url is wrong") - .startsWith("http://localhost/api/v1/downloadserver/downloadId/"); - } - @Test @Description("Test next update is provided on finished action") public void lookupNextUpdateActionAfterFinished() throws IllegalAccessException { // Mock - final Action action = createActionWithTarget(22L, Status.FINISHED); + final Action action = createActionWithTarget(22L); when(controllerManagementMock.findActionWithDetails(anyLong())).thenReturn(Optional.of(action)); when(controllerManagementMock.addUpdateActionStatus(any())).thenReturn(action); final ActionStatusBuilder builder = mock(ActionStatusBuilder.class); @@ -650,8 +526,8 @@ public void lookupNextUpdateActionAfterFinished() throws IllegalAccessException final ArgumentCaptor actionPropertiesCaptor = ArgumentCaptor.forClass(ActionProperties.class); final ArgumentCaptor targetCaptor = ArgumentCaptor.forClass(Target.class); - verify(amqpMessageDispatcherServiceMock, times(1)).sendUpdateMessageToTarget(actionPropertiesCaptor.capture(), - targetCaptor.capture(), any(Map.class)); + verify(amqpMessageDispatcherServiceMock, times(1)) + .sendUpdateMessageToTarget(actionPropertiesCaptor.capture(), targetCaptor.capture(), any(Map.class)); final ActionProperties actionProperties = actionPropertiesCaptor.getValue(); assertThat(actionProperties).isNotNull(); assertThat(actionProperties.getTenant()).as("event has tenant").isEqualTo("DEFAULT"); @@ -664,7 +540,7 @@ public void lookupNextUpdateActionAfterFinished() throws IllegalAccessException public void feedBackCodeIsPersistedInMessages() throws IllegalAccessException { // Mock - final Action action = createActionWithTarget(22L, Status.FINISHED); + final Action action = createActionWithTarget(22L); when(controllerManagementMock.findActionWithDetails(anyLong())).thenReturn(Optional.of(action)); when(controllerManagementMock.addUpdateActionStatus(any())).thenReturn(action); final ActionStatusBuilder builder = new JpaActionStatusBuilder(); @@ -770,7 +646,7 @@ private MessageProperties createMessageProperties(final MessageType type, final return messageProperties; } - private Action createActionWithTarget(final Long targetId, final Status status) throws IllegalAccessException { + private Action createActionWithTarget(final Long targetId) throws IllegalAccessException { // is needed for the creation of targets initializeSecurityTokenGenerator(); diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpAuthenticationMessageHandlerIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpAuthenticationMessageHandlerIntegrationTest.java deleted file mode 100644 index 0d16476201..0000000000 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpAuthenticationMessageHandlerIntegrationTest.java +++ /dev/null @@ -1,462 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.integration; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.hawkbit.amqp.AmqpProperties; -import org.eclipse.hawkbit.amqp.DmfApiConfiguration; -import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings; -import org.eclipse.hawkbit.dmf.json.model.DmfDownloadResponse; -import org.eclipse.hawkbit.rabbitmq.test.AbstractAmqpIntegrationTest; -import org.eclipse.hawkbit.repository.model.Artifact; -import org.eclipse.hawkbit.repository.model.DistributionSet; -import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.security.DmfTenantSecurityToken; -import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource; -import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.MessageProperties; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpStatus; -import org.springframework.util.StringUtils; - -import io.qameta.allure.Description; -import io.qameta.allure.Feature; -import io.qameta.allure.Story; - -@Feature("Component Tests - Device Management Federation API") -@Story("Amqp Authentication Message Handler") -@SpringBootTest(classes = { DmfApiConfiguration.class }) -public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmqpIntegrationTest { - - private static final String TARGET_SECRUITY_TOKEN = "12345"; - private static final String TARGET_TOKEN_HEADER = "TargetToken " + TARGET_SECRUITY_TOKEN; - private static final String TENANT_EXIST = "DEFAULT"; - private static final String TARGET = "NewDmfTarget"; - - @Autowired - private AmqpProperties amqpProperties; - - @BeforeEach - public void testSetup() { - enableTargetTokenAuthentication(); - } - - @Test - @Description("Tests wrong content type. This message is invalid and should not be requeued. Additional the receive message is null") - public void wrongContentType() { - final Message createAndSendMessage = getDmfClient() - .sendAndReceive(new Message("".getBytes(), new MessageProperties())); - assertThat(createAndSendMessage).isNull(); - assertEmptyAuthenticationMessageCount(); - } - - @Test - @Description("Tests a null message. This message is invalid and should not be requeued. Additional the receive message is null") - public void securityTokenIsNull() { - final Message createAndSendMessage = sendAndReceiveAuthenticationMessage(null); - assertThat(createAndSendMessage).isNull(); - assertEmptyAuthenticationMessageCount(); - } - - @Test - @Description("Tenant in the message is null. This message is invalid and should not be requeued. Additional the receive message is null") - public void securityTokenTenantIsNull() { - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(null, TARGET, - FileResource.createFileResourceBySha1(TARGET_SECRUITY_TOKEN)); - final Message createAndSendMessage = sendAndReceiveAuthenticationMessage(securityToken); - assertThat(createAndSendMessage).isNull(); - assertEmptyAuthenticationMessageCount(); - } - - @Test - @Description("Target in the message is null.This message is invalid and should not requeued. Additional the receive message is null") - public void securityTokenFileResourceIsNull() { - enableAnonymousAuthentication(); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, null); - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyResult(returnMessage, HttpStatus.NOT_FOUND, null); - } - - @Test - @Description("Verify that login fails if the given credential not match.") - public void loginFailedBadCredentials() { - tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED, - false); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, - FileResource.createFileResourceBySha1(TARGET_SECRUITY_TOKEN)); - final Message createAndSendMessage = sendAndReceiveAuthenticationMessage(securityToken); - - verifyResult(createAndSendMessage, HttpStatus.FORBIDDEN, "Login failed"); - - } - - @Test - @Description("Verify that the receive message contains a 404 code,if the artifact could not found") - public void fileResourceGetSha1InSecurityTokenIsNull() { - enableAnonymousAuthentication(); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, - FileResource.createFileResourceBySha1(null)); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyResult(returnMessage, HttpStatus.NOT_FOUND, - "Artifact for resource FileResource [sha1=null, artifactId=null, filename=null] not found "); - - } - - @Test - @Description("Verify that the receive message contains a 404 code , if the distributionSet is not assigned to the target") - public void artifactForFileResourceSHA1FoundByTargetIdTargetExistsButIsNotAssigned() { - final Target target = createTarget(TARGET); - - final DistributionSet distributionSet = createDistributionSet(); - final List artifacts = createArtifacts(distributionSet); - final String sha1Hash = artifacts.get(0).getSha1Hash(); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(target.getTenant(), target.getId(), null, - FileResource.createFileResourceBySha1(sha1Hash)); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyResult(returnMessage, HttpStatus.NOT_FOUND, "Artifact for resource FileResource [sha1=" + sha1Hash - + ", artifactId=null, filename=null] not found "); - } - - @Test - @Description("Verify that the receive message contains a 404 code, if there is no artifact for the given sha1") - public void artifactForFileResourceSHA1NotFound() { - enableAnonymousAuthentication(); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, - FileResource.createFileResourceBySha1(TARGET_SECRUITY_TOKEN)); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyResult(returnMessage, HttpStatus.NOT_FOUND, - "Artifact for resource FileResource [sha1=12345, artifactId=null, filename=null] not found "); - - } - - @Test - @Description("Verify that the receive message contains a 404 code, if there is no existing target for the given controller id") - public void artifactForFileResourceSHA1FoundTargetNotExists() { - enableAnonymousAuthentication(); - final DistributionSet distributionSet = createDistributionSet(); - final List artifacts = createArtifacts(distributionSet); - final String sha1Hash = artifacts.get(0).getSha1Hash(); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, - FileResource.createFileResourceBySha1(sha1Hash)); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyResult(returnMessage, HttpStatus.NOT_FOUND, "Artifact for resource FileResource [sha1=" + sha1Hash - + ", artifactId=null, filename=null] not found "); - - } - - @Test - @Description("Verify that the receive message contains a 404 code, if there is no existing artifact for the target") - public void artifactForFileResourceSHA1FoundTargetExistsButNotAssigned() { - createTarget(TARGET); - - final DistributionSet distributionSet = createDistributionSet(); - final List artifacts = createArtifacts(distributionSet); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, - FileResource.createFileResourceBySha1(artifacts.get(0).getSha1Hash())); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyResult(returnMessage, HttpStatus.NOT_FOUND, - "Artifact for resource FileResource [sha1=2f532b93ed23b4341a81dc9b1ee8a1c44b5526ab, artifactId=null, filename=null] not found "); - - } - - @Test - @Description("Verify that the receive message contains a 200 code and a artifact for the existing controller id ") - public void artifactForFileResourceSHA1FoundTargetExistsIsAssigned() { - createTarget(TARGET); - - final DistributionSet distributionSet = createDistributionSet(); - final List artifacts = createArtifacts(distributionSet); - final Artifact artifact = artifacts.get(0); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, - FileResource.createFileResourceBySha1(artifact.getSha1Hash())); - - assignDistributionSet(distributionSet.getId(), TARGET); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyOkResult(returnMessage, artifact); - - } - - @Test - @Description("Verify that the receive message contains a 200 code and a artifact for the existing target id ") - public void artifactForFileResourceSHA1FoundByTargetIdTargetExistsIsAssigned() { - final Target target = createTarget(TARGET); - - final DistributionSet distributionSet = createDistributionSet(); - final List artifacts = createArtifacts(distributionSet); - final Artifact artifact = artifacts.get(0); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, target.getId(), null, - FileResource.createFileResourceBySha1(artifact.getSha1Hash())); - - assignDistributionSet(distributionSet.getId(), TARGET); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyOkResult(returnMessage, artifact); - - } - - @Test - @Description("Verify that the receive message contains a 200 code and a artifact without a controller id (anonymous enabled)") - public void anonymousAuthentification() { - enableAnonymousAuthentication(); - final DistributionSet distributionSet = createDistributionSet(); - final List artifacts = createArtifacts(distributionSet); - final Artifact artifact = artifacts.get(0); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, null, null, - FileResource.createFileResourceBySha1(artifact.getSha1Hash())); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyOkResult(returnMessage, artifact); - - } - - @Test - @Description("Artifact is downloaded anonymous as there is not target id or controller id assigned to the target.") - public void targetTokenAuthentification() { - createTarget(TARGET); - - final DistributionSet distributionSet = createDistributionSet(); - final List artifacts = createArtifacts(distributionSet); - final Artifact artifact = artifacts.get(0); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, - FileResource.createFileResourceBySha1(artifact.getSha1Hash())); - - assignDistributionSet(distributionSet.getId(), TARGET); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyOkResult(returnMessage, artifact); - - } - - @Test - @Description("Verify that the receive message contains a 404, if there is no artifact to the given filename") - public void artifactForFileResourceFileNameNotFound() { - enableAnonymousAuthentication(); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, - FileResource.createFileResourceByFilename("Test.txt")); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyResult(returnMessage, HttpStatus.NOT_FOUND, - "Artifact for resource FileResource [sha1=null, artifactId=null, filename=Test.txt] not found "); - - } - - @Test - @Description("Verify that the receive message contains a 404, if there is no distribution set assigned to the target") - public void artifactForFileResourceFileNameFoundTargetExistsButNotAssigned() { - createTarget(TARGET); - - final DistributionSet distributionSet = createDistributionSet(); - final List artifacts = createArtifacts(distributionSet); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, - FileResource.createFileResourceByFilename(artifacts.get(0).getFilename())); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyResult(returnMessage, HttpStatus.NOT_FOUND, - "Artifact for resource FileResource [sha1=null, artifactId=null, filename=filename0] not found "); - - } - - @Test - @Description("Verify that the receive message contains a 404, if there is no exisiting target") - public void artifactForFileResourceArtifactIdFoundTargetNotExists() { - enableAnonymousAuthentication(); - final DistributionSet distributionSet = createDistributionSet(); - final List artifacts = createArtifacts(distributionSet); - final FileResource fileResource = FileResource.createFileResourceByArtifactId(artifacts.get(0).getId()); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyResult(returnMessage, HttpStatus.NOT_FOUND, "Artifact for resource FileResource [sha1=null, artifactId=" - + artifacts.get(0).getId() + ", filename=null] not found "); - - } - - @Test - @Description("Verify that the receive message contains a 200 and a artifact for the given artifact id") - public void artifactForFileResourceArtifactIdFoundTargetExistsIsAssigned() { - createTarget(TARGET); - - final DistributionSet distributionSet = createDistributionSet(); - final List artifacts = createArtifacts(distributionSet); - final Artifact artifact = artifacts.get(0); - final FileResource fileResource = FileResource.createFileResourceByArtifactId(artifact.getId()); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource); - - assignDistributionSet(distributionSet.getId(), TARGET); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyOkResult(returnMessage, artifact); - - } - - @Test - @Description("Verify that the receive message contains a 404, if there is no artifact to the given softwareModuleFilename") - public void artifactForFileResourceSoftwareModuleFilenameNotFound() { - enableAnonymousAuthentication(); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, - FileResource.softwareModuleFilename(1L, "Test.txt")); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyResult(returnMessage, HttpStatus.NOT_FOUND, - "Artifact for resource FileResource [sha1=null, artifactId=null, filename=null] not found "); - - } - - @Test - @Description("Verify that the receive message contains a 404, if there is no existing target for the file resource") - public void artifactForFileResourceSoftwareModuleFilenameFoundTargetNotExists() { - enableAnonymousAuthentication(); - final DistributionSet distributionSet = createDistributionSet(); - final List artifacts = createArtifacts(distributionSet); - - final SoftwareModule softwareModule = distributionSet.getModules().stream().findFirst().get(); - final Artifact artifact = findArtifactOfSoftwareModule(artifacts, softwareModule); - - final FileResource fileResource = FileResource.softwareModuleFilename(softwareModule.getId(), - softwareModule.getArtifact(artifact.getId()).get().getFilename()); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyResult(returnMessage, HttpStatus.NOT_FOUND, - "Artifact for resource FileResource [sha1=null, artifactId=null, filename=null] not found "); - - } - - @Test - @Description("Verify that the receive message contains a 200 and a artifact, if there is a existing artifct fpt the for the given softwareModuleFilename") - public void artifactForFileResourceSoftwareModuleFilenameFoundTargetExistsIsAssigned() { - createTarget(TARGET); - - final DistributionSet distributionSet = createDistributionSet(); - final List artifacts = createArtifacts(distributionSet); - - final SoftwareModule softwareModule = distributionSet.getModules().stream().findFirst().get(); - final Artifact artifact = findArtifactOfSoftwareModule(artifacts, softwareModule); - - final FileResource fileResource = FileResource.softwareModuleFilename(softwareModule.getId(), - softwareModule.getArtifact(artifact.getId()).get().getFilename()); - final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource); - - assignDistributionSet(distributionSet.getId(), TARGET); - - final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken); - verifyOkResult(returnMessage, artifact); - - } - - private void verifyOkResult(final Message returnMessage, final Artifact artifact) { - final DmfDownloadResponse convertedMessage = verifyResult(returnMessage, HttpStatus.OK, null); - assertThat(convertedMessage.getDownloadUrl()).isNotNull(); - assertThat(convertedMessage.getArtifact()).isNotNull(); - assertThat(convertedMessage.getArtifact().getLastModified()) - .isEqualTo(artifactManagement.findFirstBySHA1(artifact.getSha1Hash()).get().getCreatedAt()); - assertThat(convertedMessage.getArtifact().getHashes().getSha1()).isEqualTo(artifact.getSha1Hash()); - - } - - private void enableAnonymousAuthentication() { - tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED, - true); - tenantConfigurationManagement.addOrUpdateConfiguration( - TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED, false); - } - - private void enableTargetTokenAuthentication() { - tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED, - false); - tenantConfigurationManagement.addOrUpdateConfiguration( - TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED, true); - } - - private Target createTarget(final String controllerId) { - return targetManagement.create( - entityFactory.target().create().controllerId(controllerId).securityToken(TARGET_SECRUITY_TOKEN)); - } - - private DmfTenantSecurityToken createTenantSecurityToken(final String tenant, final String controllerId, - final FileResource fileResource) { - final DmfTenantSecurityToken tenantSecurityToken = new DmfTenantSecurityToken(tenant, controllerId, - fileResource); - tenantSecurityToken.putHeader(DmfTenantSecurityToken.AUTHORIZATION_HEADER, TARGET_TOKEN_HEADER); - return tenantSecurityToken; - } - - private DmfTenantSecurityToken createTenantSecurityToken(final String tenant, final Long targetId, - final String controllerId, final FileResource fileResource) { - final DmfTenantSecurityToken tenantSecurityToken = new DmfTenantSecurityToken(tenant, null, controllerId, - targetId, fileResource); - tenantSecurityToken.putHeader(DmfTenantSecurityToken.AUTHORIZATION_HEADER, TARGET_TOKEN_HEADER); - return tenantSecurityToken; - } - - private DistributionSet createDistributionSet() { - return testdataFactory.createDistributionSet("one"); - } - - private List createArtifacts(final DistributionSet distributionSet) { - final List artifacts = new ArrayList<>(); - for (final SoftwareModule module : distributionSet.getModules()) { - artifacts.addAll(testdataFactory.createArtifacts(module.getId())); - } - return artifacts; - } - - private DmfDownloadResponse verifyResult(final Message returnMessage, final HttpStatus expectedStatus, - final String expectedMessage) { - final DmfDownloadResponse convertedMessage = (DmfDownloadResponse) getDmfClient().getMessageConverter() - .fromMessage(returnMessage); - assertThat(convertedMessage.getResponseCode()).isEqualTo(expectedStatus.value()); - - if (!StringUtils.isEmpty(expectedMessage)) { - assertThat(convertedMessage.getMessage()).isEqualTo(expectedMessage); - } - return convertedMessage; - } - - private Message sendAndReceiveAuthenticationMessage(final DmfTenantSecurityToken securityToken) { - return getDmfClient().sendAndReceive(createMessage(securityToken, new MessageProperties())); - } - - private Artifact findArtifactOfSoftwareModule(final List artifacts, final SoftwareModule softwareModule) { - return artifacts.stream().filter(space -> space.getSoftwareModule().getId().equals(softwareModule.getId())) - .findFirst().get(); - } - - @Override - protected String getExchange() { - return AmqpSettings.AUTHENTICATION_EXCHANGE; - } - - private int getAuthenticationMessageCount() { - return Integer.parseInt(getRabbitAdmin().getQueueProperties(amqpProperties.getAuthenticationReceiverQueue()) - .get(RabbitAdmin.QUEUE_MESSAGE_COUNT).toString()); - } - - private void assertEmptyAuthenticationMessageCount() { - assertThat(getAuthenticationMessageCount()).isEqualTo(0); - } - -} diff --git a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfDownloadResponse.java b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfDownloadResponse.java deleted file mode 100644 index 5a569efdd0..0000000000 --- a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfDownloadResponse.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.dmf.json.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Data; - -/** - * The download response JSON representation. - */ -@Data -@JsonInclude(Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -public class DmfDownloadResponse { - - @JsonProperty - private String downloadUrl; - @JsonProperty - private DmfArtifact artifact; - @JsonProperty - private int responseCode; - @JsonProperty - private String message; -} \ No newline at end of file diff --git a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java index 1768c7183a..413891d259 100644 --- a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java +++ b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java @@ -22,9 +22,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; -import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource; import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.util.UrlUtils; import org.slf4j.Logger; @@ -159,8 +157,7 @@ protected DmfTenantSecurityToken createTenantSecurityTokenVariables(final HttpSe private DmfTenantSecurityToken createTenantSecurityTokenVariables(final HttpServletRequest request, final String tenant, final String controllerId) { - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(tenant, null, controllerId, null, - FileResource.createFileResourceBySha1("")); + final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken(tenant, null, controllerId, null); Collections.list(request.getHeaderNames()) .forEach(header -> securityToken.putHeader(header, request.getHeader(header))); diff --git a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpDownloadAuthenticationFilter.java b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpDownloadAuthenticationFilter.java deleted file mode 100644 index 082f02cc41..0000000000 --- a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/HttpDownloadAuthenticationFilter.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.security; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import jakarta.servlet.http.HttpServletRequest; - -import lombok.extern.slf4j.Slf4j; -import org.eclipse.hawkbit.cache.DownloadIdCache; -import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; - -/** - * Extracts download or upload id from the request URI security token and set - * the security context. - */ -@Slf4j -public class HttpDownloadAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter { - - public static final String REQUEST_ID_REGEX_PATTERN = ".*\\/downloadId\\/.*"; - - private final Pattern pattern; - private final DownloadIdCache downloadIdCache; - - /** - * Constructor. - * - * @param downloadIdCache - * the cache - */ - public HttpDownloadAuthenticationFilter(final DownloadIdCache downloadIdCache) { - this.downloadIdCache = downloadIdCache; - this.pattern = Pattern.compile(REQUEST_ID_REGEX_PATTERN); - - } - - private Object getDownloadByUri(final String requestURI) { - final Matcher matcher = pattern.matcher(requestURI); - if (!matcher.matches()) { - return null; - } - log.debug("retrieving id from URI request {}", requestURI); - final String[] groups = requestURI.split("\\/"); - final String id = groups[groups.length - 1]; - if (id == null) { - return null; - } - return downloadIdCache.get(id); - } - - @Override - protected Object getPreAuthenticatedPrincipal(final HttpServletRequest request) { - return getDownloadByUri(request.getRequestURI()); - } - - @Override - protected Object getPreAuthenticatedCredentials(final HttpServletRequest request) { - return getDownloadByUri(request.getRequestURI()); - } -} diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/TestConfiguration.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/TestConfiguration.java index c3532f669d..6d5070d01d 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/TestConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/TestConfiguration.java @@ -24,8 +24,6 @@ import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemProperties; import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemRepository; import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; -import org.eclipse.hawkbit.cache.DefaultDownloadIdCache; -import org.eclipse.hawkbit.cache.DownloadIdCache; import org.eclipse.hawkbit.cache.TenantAwareCacheManager; import org.eclipse.hawkbit.event.BusProtoStuffMessageConverter; import org.eclipse.hawkbit.im.authentication.SpRole; @@ -150,18 +148,6 @@ TenantAwareCacheManager cacheManager(final TenantAware tenantAware) { return new TenantAwareCacheManager(new CaffeineCacheManager(), tenantAware); } - /** - * Bean for the download id cache. - * - * @param cacheManager - * The {@link CacheManager} - * @return the cache - */ - @Bean - DownloadIdCache downloadIdCache(final CacheManager cacheManager) { - return new DefaultDownloadIdCache(cacheManager); - } - @Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME) SimpleApplicationEventMulticaster applicationEventMulticaster(final ApplicationEventFilter applicationEventFilter) { final SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new FilterEnabledApplicationEventPublisher( diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtDownloadRestApi.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtDownloadRestApi.java deleted file mode 100644 index 5bb556486f..0000000000 --- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtDownloadRestApi.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.mgmt.rest.api; - -import java.io.InputStream; - -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.ResponseBody; - -/** - * A resource for download artifacts. - */ -@FunctionalInterface -@Tag(name = "Download artifact by ID", description = "API to download artifacts by download Id.") -// no request mapping specified here to avoid CVE-2021-22044 in Feign client -public interface MgmtDownloadRestApi { - - /** - * Handles the GET request for downloading an artifact. - * - * @param tenant the download belongs to - * @param downloadId the generated download id - * @return {@link ResponseEntity} with status {@link HttpStatus#OK} if successful - */ - @GetMapping(value = MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING_BASE - + MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING) - @ResponseBody - ResponseEntity downloadArtifactByDownloadId(@PathVariable("tenant") String tenant, - @PathVariable("downloadId") String downloadId); - -} \ No newline at end of file diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java index fb320614c8..5452718b37 100644 --- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java +++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java @@ -53,10 +53,6 @@ public final class MgmtRestConstants { */ public static final String SOFTWAREMODULE_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING + "/softwaremodules"; - public static final String DOWNLOAD_ID_V1_REQUEST_MAPPING_BASE = "/api/" + API_VERSION + "/downloadserver"; - - public static final String DOWNLOAD_ID_V1_REQUEST_MAPPING = "/downloadId/{tenant}/{downloadId}"; - /** * The base URL mapping for the spring acuator management context path. */ diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResource.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResource.java deleted file mode 100644 index ff4ad83df4..0000000000 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResource.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.mgmt.rest.resource; - -import java.io.InputStream; - -import lombok.extern.slf4j.Slf4j; -import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; -import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact; -import org.eclipse.hawkbit.cache.DownloadArtifactCache; -import org.eclipse.hawkbit.cache.DownloadIdCache; -import org.eclipse.hawkbit.cache.DownloadType; -import org.eclipse.hawkbit.mgmt.rest.api.MgmtDownloadRestApi; -import org.eclipse.hawkbit.rest.util.FileStreamingUtil; -import org.eclipse.hawkbit.rest.util.RequestResponseContextHolder; -import org.springframework.context.annotation.Scope; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.context.WebApplicationContext; - -/** - * A resource for download artifacts. - */ -@Slf4j -@RestController -@Scope(value = WebApplicationContext.SCOPE_REQUEST) -public class MgmtDownloadResource implements MgmtDownloadRestApi { - - private final ArtifactRepository artifactRepository; - - private final DownloadIdCache downloadIdCache; - - MgmtDownloadResource(final ArtifactRepository artifactRepository, final DownloadIdCache downloadIdCache) { - this.artifactRepository = artifactRepository; - this.downloadIdCache = downloadIdCache; - } - - @Override - @ResponseBody - public ResponseEntity downloadArtifactByDownloadId(@PathVariable("tenant") final String tenant, - @PathVariable("downloadId") final String downloadId) { - - try { - final DownloadArtifactCache artifactCache = downloadIdCache.get(downloadId); - if (artifactCache == null) { - log.warn("Download Id {} could not be found", downloadId); - return ResponseEntity.notFound().build(); - } - - AbstractDbArtifact artifact = null; - - if (DownloadType.BY_SHA1 == artifactCache.getDownloadType()) { - artifact = artifactRepository.existsByTenantAndSha1(tenant, artifactCache.getId()) - ? artifactRepository.getArtifactBySha1(tenant, artifactCache.getId()) - : null; - } else { - log.warn("Download Type {} not supported", artifactCache.getDownloadType()); - } - - if (artifact == null) { - log.warn("Artifact with cached id {} and download type {} could not be found.", - artifactCache.getId(), artifactCache.getDownloadType()); - return ResponseEntity.notFound().build(); - } - - return FileStreamingUtil.writeFileResponse(artifact, downloadId, 0L, - RequestResponseContextHolder.getHttpServletResponse(), - RequestResponseContextHolder.getHttpServletRequest(), null); - - } finally { - downloadIdCache.evict(downloadId); - } - } -} diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResourceTest.java deleted file mode 100644 index 833808c5f0..0000000000 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResourceTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.mgmt.rest.resource; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import org.eclipse.hawkbit.cache.DownloadArtifactCache; -import org.eclipse.hawkbit.cache.DownloadIdCache; -import org.eclipse.hawkbit.cache.DownloadType; -import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; -import org.eclipse.hawkbit.repository.model.Artifact; -import org.eclipse.hawkbit.repository.model.DistributionSet; -import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import io.qameta.allure.Description; -import io.qameta.allure.Feature; -import io.qameta.allure.Story; - -@Feature("Component Tests - Management API") -@Story("Download Resource") -public class MgmtDownloadResourceTest extends AbstractManagementApiIntegrationTest { - - @Autowired - private DownloadIdCache downloadIdCache; - - private static final String DOWNLOAD_ID_SHA1 = "downloadIdSha1"; - - private static final String DOWNLOAD_ID_NOT_AVAILABLE = "downloadIdNotAvailable"; - - @BeforeEach - public void setupCache() { - - final DistributionSet distributionSet = testdataFactory.createDistributionSet("Test"); - final SoftwareModule softwareModule = distributionSet.getModules().stream().findAny().get(); - final Artifact artifact = testdataFactory.createArtifacts(softwareModule.getId()).stream().findAny().get(); - - downloadIdCache.put(DOWNLOAD_ID_SHA1, new DownloadArtifactCache(DownloadType.BY_SHA1, artifact.getSha1Hash())); - } - - @Test - @Description("This test verifies the call of download artifact without a valid download id fails.") - public void testNoDownloadIdAvailable() throws Exception { - mvc.perform(get( - MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING_BASE - + MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING, - tenantAware.getCurrentTenant(), DOWNLOAD_ID_NOT_AVAILABLE)).andDo(MockMvcResultPrinter.print()) - .andExpect(status().isNotFound()); - - } - - @Test - @Description("This test verifies the call of download artifact works and the download id will be removed.") - public void testDownload() throws Exception { - mvc.perform(get( - MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING_BASE - + MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING, - tenantAware.getCurrentTenant(), DOWNLOAD_ID_SHA1)).andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()); - - // because cache is empty - mvc.perform(get( - MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING_BASE - + MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING, - tenantAware.getCurrentTenant(), DOWNLOAD_ID_SHA1)).andDo(MockMvcResultPrinter.print()) - .andExpect(status().isNotFound()); - - } - -} diff --git a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/data/ResponseList.java b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/data/ResponseList.java index 600023e71e..b7bd91fad9 100644 --- a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/data/ResponseList.java +++ b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/data/ResponseList.java @@ -17,11 +17,9 @@ import org.springframework.hateoas.RepresentationModel; /** - * List that extends ResourceSupport to ensure that links in content are in HAL - * format. + * List that extends RepresentationModel to ensure that links in content are in HAL format. * - * @param - * of the response content + * @param of the response content */ public class ResponseList extends RepresentationModel> implements List { diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/DmfTenantSecurityToken.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/DmfTenantSecurityToken.java index 7ca96adb2d..d8d8210986 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/DmfTenantSecurityToken.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/DmfTenantSecurityToken.java @@ -9,7 +9,6 @@ */ package org.eclipse.hawkbit.security; -import java.util.Collections; import java.util.Map; import java.util.TreeMap; @@ -18,116 +17,66 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; /** * JSON representation to authenticate a tenant. */ - +@Data @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class DmfTenantSecurityToken { public static final String AUTHORIZATION_HEADER = "Authorization"; - @JsonProperty(required = false) + @JsonProperty private String tenant; - @JsonProperty(required = false) + @JsonProperty private final Long tenantId; - @JsonProperty(required = false) + @JsonProperty private final String controllerId; - @JsonProperty(required = false) + @JsonProperty private final Long targetId; - @JsonProperty(required = false) + @JsonProperty private Map headers; - @JsonProperty(required = false) - private final FileResource fileResource; - /** * Constructor. * - * @param tenant - * the tenant for the security token - * @param tenantId - * alternative tenant identification by technical ID - * @param controllerId - * the ID of the controller for the security token - * @param targetId - * alternative target identification by technical ID - * @param fileResource - * the file to obtain + * @param tenant the tenant for the security token + * @param tenantId alternative tenant identification by technical ID + * @param controllerId the ID of the controller for the security token + * @param targetId alternative target identification by technical ID */ @JsonCreator public DmfTenantSecurityToken(@JsonProperty("tenant") final String tenant, @JsonProperty("tenantId") final Long tenantId, @JsonProperty("controllerId") final String controllerId, - @JsonProperty("targetId") final Long targetId, - @JsonProperty("fileResource") final FileResource fileResource) { + @JsonProperty("targetId") final Long targetId) { this.tenant = tenant; this.tenantId = tenantId; this.controllerId = controllerId; this.targetId = targetId; - this.fileResource = fileResource; } /** * Constructor. * - * @param tenant - * the tenant for the security token - * @param controllerId - * the ID of the controller for the security token - * @param fileResource - * the file to obtain + * @param tenant the tenant for the security token + * @param controllerId the ID of the controller for the security token */ - public DmfTenantSecurityToken(final String tenant, final String controllerId, final FileResource fileResource) { - this(tenant, null, controllerId, null, fileResource); + public DmfTenantSecurityToken(final String tenant, final String controllerId) { + this(tenant, null, controllerId, null); } /** * Constructor. * - * @param tenantId - * the tenant for the security token - * @param targetId - * target identification by technical ID - * @param fileResource - * the file to obtain + * @param tenantId the tenant for the security token + * @param targetId target identification by technical ID */ - public DmfTenantSecurityToken(final Long tenantId, final Long targetId, final FileResource fileResource) { - this(null, tenantId, null, targetId, fileResource); - } - - public void setTenant(final String tenant) { - this.tenant = tenant; - } - - public String getTenant() { - return tenant; - } - - public String getControllerId() { - return controllerId; - } - - public Map getHeaders() { - if (headers == null) { - return Collections.emptyMap(); - } - - return Collections.unmodifiableMap(headers); - } - - public FileResource getFileResource() { - return fileResource; - } - - public Long getTenantId() { - return tenantId; - } - - public Long getTargetId() { - return targetId; + public DmfTenantSecurityToken(final Long tenantId, final Long targetId) { + this(null, tenantId, null, targetId); } /** @@ -145,21 +94,13 @@ public String getHeader(final String name) { return headers.get(name); } - public void setHeaders(final Map headers) { - this.headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - this.headers.putAll(headers); - } - /** * Associates the specified header value with the specified name. * - * @param name - * of the header - * @param value - * of the header + * @param name of the header + * @param value of the header * - * @return the previous value associated with the name, or - * null if there was no mapping for name. + * @return the previous value associated with the name, or null if there was no mapping for name. */ public String putHeader(final String name, final String value) { if (headers == null) { @@ -167,161 +108,4 @@ public String putHeader(final String name, final String value) { } return headers.put(name, value); } - - /** - * File resource descriptor which is used to ask for the resource to - * download e.g. The lookup of the file can be different e.g. by SHA1 hash - * or by filename. - */ - @JsonInclude(Include.NON_NULL) - @JsonIgnoreProperties(ignoreUnknown = true) - public static class FileResource { - @JsonProperty(required = false) - private String sha1; - @JsonProperty(required = false) - private Long artifactId; - @JsonProperty(required = false) - private String filename; - @JsonProperty(required = false) - private SoftwareModuleFilenameResource softwareModuleFilenameResource; - - public String getSha1() { - return sha1; - } - - public void setSha1(final String sha1) { - this.sha1 = sha1; - } - - public String getFilename() { - return filename; - } - - public void setFilename(final String filename) { - this.filename = filename; - } - - public SoftwareModuleFilenameResource getSoftwareModuleFilenameResource() { - return softwareModuleFilenameResource; - } - - public void setSoftwareModuleFilenameResource( - final SoftwareModuleFilenameResource softwareModuleFilenameResource) { - this.softwareModuleFilenameResource = softwareModuleFilenameResource; - } - - public Long getArtifactId() { - return artifactId; - } - - public void setArtifactId(final Long artifactId) { - this.artifactId = artifactId; - } - - /** - * factory method to create a file resource for an SHA1 lookup. - * - * @param sha1 - * the SHA1 key of the file to obtain - * @return the {@link FileResource} with SHA1 key set - */ - public static FileResource createFileResourceBySha1(final String sha1) { - final FileResource resource = new FileResource(); - resource.sha1 = sha1; - return resource; - } - - /** - * factory method to create a file resource for an artifact ID lookup. - * - * @param artifactId - * the artifact IF key of the file to obtain - * @return the {@link FileResource} with SHA1 key set - */ - public static FileResource createFileResourceByArtifactId(final Long artifactId) { - final FileResource resource = new FileResource(); - resource.artifactId = artifactId; - return resource; - } - - /** - * factory method to create a file resource for an filename lookup. - * - * @param filename - * the filename of the file to obtain - * @return the {@link FileResource} with filename set - */ - public static FileResource createFileResourceByFilename(final String filename) { - final FileResource resource = new FileResource(); - resource.filename = filename; - return resource; - } - - /** - * factory method to create a file resource for an softwaremodule + - * filename lookup, because an filename is not globally unique but - * within a softwaremodule. - * - * @param softwareModuleId - * 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 - */ - public static FileResource softwareModuleFilename(final Long softwareModuleId, final String filename) { - final FileResource resource = new FileResource(); - resource.softwareModuleFilenameResource = new SoftwareModuleFilenameResource(softwareModuleId, filename); - return resource; - } - - @Override - public String toString() { - return "FileResource [sha1=" + sha1 + ", artifactId=" + artifactId + ", 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 Long softwareModuleId; - @JsonProperty(required = false) - private 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; - } - - public void setSoftwareModuleId(final Long softwareModuleId) { - this.softwareModuleId = softwareModuleId; - } - - public void setFilename(final String filename) { - this.filename = filename; - } - } - } -} +} \ No newline at end of file diff --git a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java index 05add975ce..d10ad24380 100644 --- a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java +++ b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java @@ -16,7 +16,6 @@ import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; -import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource; import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; import org.junit.jupiter.api.BeforeEach; @@ -132,8 +131,7 @@ public void useDifferentValuesForIssuerHashHeader() { } private static DmfTenantSecurityToken prepareSecurityToken(final String issuerHashHeaderValue) { - final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken("DEFAULT", CA_COMMON_NAME_VALUE, - FileResource.createFileResourceBySha1("12345")); + final DmfTenantSecurityToken securityToken = new DmfTenantSecurityToken("DEFAULT", CA_COMMON_NAME_VALUE); securityToken.putHeader(CA_COMMON_NAME, CA_COMMON_NAME_VALUE); securityToken.putHeader(X_SSL_ISSUER_HASH_1, issuerHashHeaderValue); return securityToken; diff --git a/site/content/guides/clustering.md b/site/content/guides/clustering.md index 16da2c8114..3d66a6a6d1 100644 --- a/site/content/guides/clustering.md +++ b/site/content/guides/clustering.md @@ -32,16 +32,6 @@ Every node has multiple schedulers which run after a defined period of time. All ## Known constraints -### UI sessions -As of today hawkBit is not storing user sessions in a shared, cluster wide cache. Session is only bound to the node where the login took place. If this node is going down for whatever reason, the session is lost and the user is forced to login again. -In case that's not an option, you can help yourself by introducing a shared session cache based on e.g. Redis. -Furthermore hawkBit is not supporting session stickiness out of the box either. However most of the well known load balancer out there can solve this issue. - -### Caching of download IDs -The downloadId is generated and stored in the DownloadIdCache. It is used for downloading an artifact. -In hawkBit exists an interface called "[DownloadIdCache](https://github.com/eclipse/hawkbit/blob/master/hawkbit-core/src/main/java/org/eclipse/hawkbit/cache/DownloadIdCache.java)" and one implementation of it: "DefaultDownloadIdCache". This default implementation can't be used within a cluster. Its containing data is only available inside one node and can't be shared with other nodes. E.g. the downloadId which is stored in this cache after authentication on node A can only be used for downloading the artifact by node A. -In a cluster-capable environment this fact can lead to issues as it could happen, that the downloadId is stored on node A and node B would like to download the artifact by means of the downloadId which is not available on node B. To solve this issue you can use a cluster-shared cache e.g. Redis or create a new cluster-aware implementation of the interface "DownloadIdCache". - ### Denial-of-Service (DoS) filter hawkBit owns the feature of guarding itself from DoS attacks, a [DoS filter](https://github.com/eclipse/hawkbit/blob/master/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/DosFilter.java). It reduces the maximum number of requests per seconds which can be configured for read and write requests. This mechanism is only working for every node separately, i.e. in a cluster environment the worst-case behaviour would be that the maximum number of requests per seconds will be increased to its product if every request is handled by a different node.