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 bc7cf5284..e6d4ccc5f 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
@@ -289,9 +289,10 @@ public class SecurityManagedConfiguration {
// in the ShallowEtagHeaderFilter, just using the SH1 hash of the
// artifact itself as 'ETag', because otherwise the file will be copied
// in memory!
- filterRegBean.setFilter(new ExcludePathAwareShallowETagFilter(
- "/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download", "/{tenant}/controller/artifacts/**",
- "/{targetid}/softwaremodules/{softwareModuleId}/artifacts/**", "/api/v1/downloadserver/**"));
+ filterRegBean.setFilter(
+ new ExcludePathAwareShallowETagFilter("/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download",
+ "/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/**",
+ "/api/v1/downloadserver/**"));
return filterRegBean;
}
diff --git a/hawkbit-ddi-dl-api/README.md b/hawkbit-ddi-dl-api/README.md
deleted file mode 100644
index ae8c9542e..000000000
--- a/hawkbit-ddi-dl-api/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Eclipse.IoT hawkBit - DDI Artifact Download API - Resources
-
-This module is part of the Direct Device Integration (DDI) API and is used by devices/targets for downloading artifacts through HTTP.
-
-# Version 1
-
-The model follows [semantic versioning](http://semver.org) with MAJOR version, i.e. breaking changes will result in a new MAJOR version.
-
-# Compile
-
-#### Build hawkbit-ddi-dl-api
-
-```
-$ cd hawkbit/hawkbit-ddi-dl-api
-$ mvn clean install
-```
diff --git a/hawkbit-ddi-dl-api/pom.xml b/hawkbit-ddi-dl-api/pom.xml
deleted file mode 100644
index 704267927..000000000
--- a/hawkbit-ddi-dl-api/pom.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
- 4.0.0
-
- org.eclipse.hawkbit
- hawkbit-parent
- 0.2.0-SNAPSHOT
-
- hawkbit-ddi-dl-api
- hawkBit :: DDI Download Server (DL) API
-
-
-
- org.springframework.security
- spring-security-web
-
-
-
diff --git a/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlArtifactStoreControllerRestApi.java b/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlArtifactStoreControllerRestApi.java
deleted file mode 100644
index 19b9962a3..000000000
--- a/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlArtifactStoreControllerRestApi.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- */
-package org.eclipse.hawkbit.ddi.dl.rest.api;
-
-import java.io.InputStream;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-/**
- * REST resource handling for artifact download operations.
- */
-@RequestMapping(DdiDlRestConstants.ARTIFACTS_V1_REQUEST_MAPPING)
-public interface DdiDlArtifactStoreControllerRestApi {
-
- /**
- * Handles GET download request. This could be full or partial download
- * request.
- *
- * @param tenant
- * name of the client
- * @param fileName
- * to search for
- * @param principal
- * the authentication principal stored in the security context
- *
- * @return response of the servlet which in case of success is status code
- * {@link HttpStatus#OK} or in case of partial download
- * {@link HttpStatus#PARTIAL_CONTENT}.
- */
- @RequestMapping(method = RequestMethod.GET, value = DdiDlRestConstants.ARTIFACT_DOWNLOAD_BY_FILENAME
- + "/{fileName}")
- @ResponseBody
- ResponseEntity downloadArtifactByFilename(@PathVariable("tenant") final String tenant,
- @PathVariable("fileName") final String fileName);
-
- /**
- * Handles GET MD5 checksum file download request.
- *
- * @param tenant
- * name of the client
- * @param fileName
- * to search for
- *
- * @return response of the servlet
- */
- @RequestMapping(method = RequestMethod.GET, value = DdiDlRestConstants.ARTIFACT_DOWNLOAD_BY_FILENAME + "/{fileName}"
- + DdiDlRestConstants.ARTIFACT_MD5_DWNL_SUFFIX)
- @ResponseBody
- ResponseEntity downloadArtifactMD5ByFilename(@PathVariable("tenant") final String tenant,
- @PathVariable("fileName") final String fileName);
-
-}
diff --git a/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlRestConstants.java b/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlRestConstants.java
deleted file mode 100644
index 40ba5050f..000000000
--- a/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlRestConstants.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- */
-package org.eclipse.hawkbit.ddi.dl.rest.api;
-
-/**
- * Constants for the direct device integration rest resources.
- */
-public final class DdiDlRestConstants {
-
- /**
- * The base URL mapping of the artifact repository rest resources.
- */
- public static final String ARTIFACTS_V1_REQUEST_MAPPING = "/{tenant}/controller/artifacts/v1";
-
- /**
- * The artifact URL mapping rest resource.
- */
- public static final String ARTIFACT_DOWNLOAD = "artifact";
-
- /**
- * The artifact by filename URL mapping rest resource.
- */
- public static final String ARTIFACT_DOWNLOAD_BY_FILENAME = "/filename";
-
- /**
- * File suffix for MDH hash download (see Linux md5sum).
- */
- public static final String ARTIFACT_MD5_DWNL_SUFFIX = ".MD5SUM";
-
- // constant class, private constructor.
- private DdiDlRestConstants() {
-
- }
-}
diff --git a/hawkbit-ddi-resource/pom.xml b/hawkbit-ddi-resource/pom.xml
index 117a61346..a593e4551 100644
--- a/hawkbit-ddi-resource/pom.xml
+++ b/hawkbit-ddi-resource/pom.xml
@@ -26,11 +26,6 @@
hawkbit-ddi-api
${project.version}
-
- org.eclipse.hawkbit
- hawkbit-ddi-dl-api
- ${project.version}
-
org.eclipse.hawkbit
hawkbit-rest-core
diff --git a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java
index 2e8a5f3fa..4b5f664c3 100644
--- a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java
+++ b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java
@@ -18,7 +18,6 @@ import org.eclipse.hawkbit.api.ApiType;
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
import org.eclipse.hawkbit.api.URLPlaceholder;
import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData;
-import org.eclipse.hawkbit.ddi.dl.rest.api.DdiDlRestConstants;
import org.eclipse.hawkbit.ddi.json.model.DdiArtifact;
import org.eclipse.hawkbit.ddi.json.model.DdiArtifactHash;
import org.eclipse.hawkbit.ddi.json.model.DdiChunk;
@@ -160,7 +159,7 @@ public final class DataConversionHelper {
final StringBuilder header = new StringBuilder();
header.append("attachment;filename=");
header.append(fileName);
- header.append(DdiDlRestConstants.ARTIFACT_MD5_DWNL_SUFFIX);
+ header.append(DdiRestConstants.ARTIFACT_MD5_DWNL_SUFFIX);
response.setContentLength(content.length);
response.setHeader("Content-Disposition", header.toString());
diff --git a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactStoreController.java b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactStoreController.java
deleted file mode 100644
index 21cdc39aa..000000000
--- a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactStoreController.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/**
- * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- */
-package org.eclipse.hawkbit.ddi.rest.resource;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Optional;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
-import org.eclipse.hawkbit.ddi.dl.rest.api.DdiDlArtifactStoreControllerRestApi;
-import org.eclipse.hawkbit.im.authentication.UserPrincipal;
-import org.eclipse.hawkbit.repository.ArtifactManagement;
-import org.eclipse.hawkbit.repository.ControllerManagement;
-import org.eclipse.hawkbit.repository.EntityFactory;
-import org.eclipse.hawkbit.repository.RepositoryConstants;
-import org.eclipse.hawkbit.repository.exception.ArtifactBinaryNotFoundException;
-import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
-import org.eclipse.hawkbit.repository.exception.SoftwareModuleNotAssignedToTargetException;
-import org.eclipse.hawkbit.repository.model.Action;
-import org.eclipse.hawkbit.repository.model.Action.Status;
-import org.eclipse.hawkbit.repository.model.ActionStatus;
-import org.eclipse.hawkbit.repository.model.Artifact;
-import org.eclipse.hawkbit.repository.model.Target;
-import org.eclipse.hawkbit.rest.util.RequestResponseContextHolder;
-import org.eclipse.hawkbit.rest.util.RestResourceConversionHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Scope;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.context.WebApplicationContext;
-
-/**
- * The {@link DdiArtifactStoreController} of the HawkBit server controller API
- * that is queried by the HawkBit target in order to download artifacts
- * independent of their own individual resource. This is offered in addition to
- * the {@link DdiRootController#downloadArtifact(String, Long, String)} for
- * legacy controllers that can not be fed with a download URI at runtime.
- */
-@RestController
-@Scope(WebApplicationContext.SCOPE_REQUEST)
-public class DdiArtifactStoreController implements DdiDlArtifactStoreControllerRestApi {
-
- private static final Logger LOG = LoggerFactory.getLogger(DdiArtifactStoreController.class);
-
- @Autowired
- private ArtifactManagement artifactManagement;
-
- @Autowired
- private ControllerManagement controllerManagement;
-
- @Autowired
- private RequestResponseContextHolder requestResponseContextHolder;
-
- @Autowired
- private EntityFactory entityFactory;
-
- @Override
- public ResponseEntity downloadArtifactByFilename(@PathVariable("tenant") final String tenant,
- @PathVariable("fileName") final String fileName) {
- final Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
- final Optional foundArtifacts = artifactManagement.findArtifactByFilename(fileName);
-
- if (!foundArtifacts.isPresent()) {
- LOG.warn("Software artifact with name {} could not be found.", fileName);
- return new ResponseEntity<>(HttpStatus.NOT_FOUND);
- }
-
- ResponseEntity result;
- final Artifact artifact = foundArtifacts.get();
-
- final String ifMatch = requestResponseContextHolder.getHttpServletRequest().getHeader("If-Match");
- if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) {
- result = new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
- } else {
- final DbArtifact file = artifactManagement.loadArtifactBinary(artifact.getSha1Hash())
- .orElseThrow(() -> new ArtifactBinaryNotFoundException(artifact.getSha1Hash()));
-
- // we set a download status only if we are aware of the
- // targetid, i.e. authenticated and not anonymous
- if (principal instanceof UserPrincipal && ((UserPrincipal) principal).getUsername() != null
- && !"anonymous".equals(((UserPrincipal) principal).getUsername())) {
- final ActionStatus actionStatus = checkAndReportDownloadByTarget(
- requestResponseContextHolder.getHttpServletRequest(), ((UserPrincipal) principal).getUsername(),
- artifact);
- result = RestResourceConversionHelper.writeFileResponse(artifact,
- requestResponseContextHolder.getHttpServletResponse(),
- requestResponseContextHolder.getHttpServletRequest(), file, controllerManagement,
- actionStatus.getId());
- } else {
- result = RestResourceConversionHelper.writeFileResponse(artifact,
- requestResponseContextHolder.getHttpServletResponse(),
- requestResponseContextHolder.getHttpServletRequest(), file);
- }
-
- }
- return result;
- }
-
- @Override
- public ResponseEntity downloadArtifactMD5ByFilename(@PathVariable("tenant") final String tenant,
- @PathVariable("fileName") final String fileName) {
- final Optional foundArtifacts = artifactManagement.findArtifactByFilename(fileName);
-
- if (!foundArtifacts.isPresent()) {
- LOG.warn("Software artifact with name {} could not be found.", fileName);
- return new ResponseEntity<>(HttpStatus.NOT_FOUND);
- }
-
- try {
- DataConversionHelper.writeMD5FileResponse(fileName, requestResponseContextHolder.getHttpServletResponse(),
- foundArtifacts.get());
- } catch (final IOException e) {
- LOG.error("Failed to stream MD5 File", e);
- return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
- }
-
- return new ResponseEntity<>(HttpStatus.OK);
- }
-
- private ActionStatus checkAndReportDownloadByTarget(final HttpServletRequest request, final String controllerId,
- final Artifact artifact) {
- final Target target = controllerManagement.findByControllerId(controllerId)
- .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
-
- final Action action = controllerManagement
- .getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(),
- artifact.getSoftwareModule().getId())
- .orElseThrow(() -> new SoftwareModuleNotAssignedToTargetException(artifact.getSoftwareModule().getId(),
- target.getControllerId()));
- final String range = request.getHeader("Range");
-
- String message;
- if (range != null) {
- message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads range " + range + " of: "
- + request.getRequestURI();
- } else {
- message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads: " + request.getRequestURI();
- }
-
- return controllerManagement.addInformationalActionStatus(
- entityFactory.actionStatus().create(action.getId()).status(Status.DOWNLOAD).message(message));
- }
-}
diff --git a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java
index 95fe29bdc..70583ae1d 100644
--- a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java
+++ b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java
@@ -31,8 +31,6 @@ import java.util.TimeZone;
import org.apache.commons.lang3.RandomUtils;
import org.eclipse.hawkbit.ddi.rest.resource.DdiArtifactDownloadTest.DownloadTestConfiguration;
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
-import org.eclipse.hawkbit.repository.model.Action;
-import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
@@ -42,9 +40,8 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Sort.Direction;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
@@ -66,8 +63,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
private static final int ARTIFACT_SIZE = 5 * 1024 * 1024;
- private volatile static int downLoadProgress = 0;
- private volatile static long shippedBytes = 0;
+ private static volatile int downLoadProgress = 0;
+ private static volatile long shippedBytes = 0;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH);
@@ -93,149 +90,71 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
ds.findFirstModuleByType(osType).get().getId(), "file1", false);
// no artifact available
- mvc.perform(get("/controller/v1/{targetid}/softwaremodules/{softwareModuleId}/artifacts/123455",
+ mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/123455",
target.getControllerId(), getOsModule(ds))).andExpect(status().isNotFound());
- mvc.perform(get("/controller/v1/{targetid}/softwaremodules/{softwareModuleId}/artifacts/123455.MD5SUM",
+ mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/123455.MD5SUM",
target.getControllerId(), getOsModule(ds))).andExpect(status().isNotFound());
// SM does not exist
- mvc.perform(get("/controller/v1/{targetid}/softwaremodules/1234567890/artifacts/{filename}",
+ mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/1234567890/artifacts/{filename}",
target.getControllerId(), artifact.getFilename())).andExpect(status().isNotFound());
- mvc.perform(get("/controller/v1/{targetid}/softwaremodules/1234567890/artifacts/{filename}.MD5SUM",
+ mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/1234567890/artifacts/{filename}.MD5SUM",
target.getControllerId(), artifact.getFilename())).andExpect(status().isNotFound());
// test now consistent data to test allowed methods
- mvc.perform(get("/{tenant}/controller/v1/{targetid}/softwaremodules/{smId}/artifacts/{filename}",
+ mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
.header(HttpHeaders.IF_MATCH, artifact.getSha1Hash()))
.andExpect(status().isOk());
- mvc.perform(get("/{tenant}/controller/v1/{targetid}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
+ mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isOk());
// test failed If-match
- mvc.perform(get("/{tenant}/controller/v1/{targetid}/softwaremodules/{smId}/artifacts/{filename}",
+ mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
.header("If-Match", "fsjkhgjfdhg"))
.andExpect(status().isPreconditionFailed());
// test invalid range
- mvc.perform(get("/{tenant}/controller/v1/{targetid}/softwaremodules/{smId}/artifacts/{filename}",
+ mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
.header("Range", "bytes=1-10,hdsfjksdh"))
.andExpect(header().string("Content-Range", "bytes */" + 5 * 1024))
.andExpect(status().isRequestedRangeNotSatisfiable());
- mvc.perform(get("/{tenant}/controller/v1/{targetid}/softwaremodules/{smId}/artifacts/{filename}",
+ mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
.header("Range", "bytes=100-10"))
.andExpect(header().string("Content-Range", "bytes */" + 5 * 1024))
.andExpect(status().isRequestedRangeNotSatisfiable());
// not allowed methods
- mvc.perform(put("/{tenant}/controller/v1/{targetid}/softwaremodules/{smId}/artifacts/{filename}",
+ mvc.perform(put("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
- mvc.perform(delete("/{tenant}/controller/v1/{targetid}/softwaremodules/{smId}/artifacts/{filename}",
+ mvc.perform(delete("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
- mvc.perform(post("/{tenant}/controller/v1/{targetid}/softwaremodules/{smId}/artifacts/{filename}",
+ mvc.perform(post("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
- mvc.perform(put("/{tenant}/controller/v1/{targetid}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
+ mvc.perform(put("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
- mvc.perform(delete("/{tenant}/controller/v1/{targetid}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
+ mvc.perform(delete("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
- mvc.perform(post("/{tenant}/controller/v1/{targetid}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
+ mvc.perform(post("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
}
- @Test
- @WithUser(principal = TestdataFactory.DEFAULT_CONTROLLER_ID, authorities = "ROLE_CONTROLLER", allSpPermissions = true)
- @Description("Tests non allowed requests on the artifact ressource, e.g. invalid URI, wrong if-match, wrong command.")
- public void invalidRequestsOnArtifactResourceByName() throws Exception {
- // create target
- final Target target = testdataFactory.createTarget();
- final List targets = Lists.newArrayList(target);
-
- // create ds
- final DistributionSet ds = testdataFactory.createDistributionSet("");
- assignDistributionSet(ds, targets);
-
- // create artifact
- final byte random[] = RandomUtils.nextBytes(5 * 1024);
-
- // Binary
- // no artifact available
- mvc.perform(get("/controller/artifacts/v1/filename/{filename}", "file1")).andExpect(status().isNotFound());
-
- // no artifact available
- mvc.perform(get("/controller/artifacts/v1/filename/{filename}.MD5SUM", "file1"))
- .andExpect(status().isNotFound());
-
- // test now consistent data to test allowed methods
- final Artifact artifact = artifactManagement.createArtifact(new ByteArrayInputStream(random), getOsModule(ds),
- "file1", false);
-
- mvc.perform(
- get("/{tenant}/controller/artifacts/v1/filename/{filename}", tenantAware.getCurrentTenant(), "file1")
- .header(HttpHeaders.IF_MATCH, artifact.getSha1Hash()))
- .andExpect(status().isOk());
-
- mvc.perform(get("/{tenant}/controller/artifacts/v1/filename/{filename}.MD5SUM", tenantAware.getCurrentTenant(),
- "file1")).andExpect(status().isOk());
-
- // test failed If-match
- mvc.perform(
- get("/{tenant}/controller/artifacts/v1/filename/{filename}", tenantAware.getCurrentTenant(), "file1")
- .header("If-Match", "fsjkhgjfdhg"))
- .andExpect(status().isPreconditionFailed());
-
- // test invalid range
- mvc.perform(
- get("/{tenant}/controller/artifacts/v1/filename/{filename}", tenantAware.getCurrentTenant(), "file1")
- .header("Range", "bytes=1-10,hdsfjksdh"))
- .andExpect(header().string("Content-Range", "bytes */" + 5 * 1024))
- .andExpect(status().isRequestedRangeNotSatisfiable());
-
- mvc.perform(
- get("/{tenant}/controller/artifacts/v1/filename/{filename}", tenantAware.getCurrentTenant(), "file1")
- .header("Range", "bytes=100-10"))
- .andExpect(header().string("Content-Range", "bytes */" + 5 * 1024))
- .andExpect(status().isRequestedRangeNotSatisfiable());
-
- // not allowed methods
- mvc.perform(
- put("/{tenant}/controller/artifacts/v1/filename/{filename}", tenantAware.getCurrentTenant(), "file1"))
- .andExpect(status().isMethodNotAllowed());
-
- mvc.perform(delete("/{tenant}/controller/artifacts/v1/filename/{filename}", tenantAware.getCurrentTenant(),
- "file1")).andExpect(status().isMethodNotAllowed());
-
- mvc.perform(
- post("/{tenant}/controller/artifacts/v1/filename/{filename}", tenantAware.getCurrentTenant(), "file1"))
- .andExpect(status().isMethodNotAllowed());
-
- // not allowed methods
- mvc.perform(put("/{tenant}/controller/artifacts/v1/filename/{filename}.MD5SUM", tenantAware.getCurrentTenant(),
- "file1")).andExpect(status().isMethodNotAllowed());
-
- mvc.perform(delete("/{tenant}/controller/artifacts/v1/filename/{filename}.MD5SUM",
- tenantAware.getCurrentTenant(), "file1")).andExpect(status().isMethodNotAllowed());
-
- mvc.perform(post("/{tenant}/controller/artifacts/v1/filename/{filename}.MD5SUM", tenantAware.getCurrentTenant(),
- "file1")).andExpect(status().isMethodNotAllowed());
-
- }
-
@Test
@WithUser(principal = "4712", authorities = "ROLE_CONTROLLER", allSpPermissions = true)
@Description("Tests valid downloads through the artifact resource by identifying the artifact not by ID but file name.")
@@ -257,14 +176,14 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
ds.findFirstModuleByType(osType).get().getId(), "file1", false);
// download fails as artifact is not yet assigned
- mvc.perform(get("/controller/v1/{targetid}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
+ mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
target.getControllerId(), getOsModule(ds), artifact.getFilename())).andExpect(status().isNotFound());
// now assign and download successful
assignDistributionSet(ds, targets);
final MvcResult result = mvc
.perform(
- get("/{tenant}/controller/v1/{targetid}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
+ get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds),
artifact.getFilename()))
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
@@ -298,7 +217,7 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// download
final MvcResult result = mvc
.perform(
- get("/{tenant}/controller/v1/{targetid}/softwaremodules/{softwareModuleId}/artifacts/{filename}.MD5SUM",
+ get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds),
artifact.getFilename()))
.andExpect(status().isOk()).andExpect(header().string("Content-Disposition",
@@ -309,95 +228,10 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
new String(artifact.getMd5Hash() + " " + artifact.getFilename()).getBytes(Charsets.US_ASCII));
}
- @Test
- @WithUser(authorities = "ROLE_CONTROLLER_ANONYMOUS", allSpPermissions = true)
- @Description("Ensures that even an authenticated controller is not permitted to download if "
- + "anonymous as authorization is notpossible, e.g. chekc if the controller has the artifact assigned.")
- public void downloadArtifactByNameFailsIfNotAuthenticated() throws Exception {
- downLoadProgress = 1;
- shippedBytes = 0;
-
- assertThat(softwareManagement.findSoftwareModulesAll(pageReq)).hasSize(0);
-
- // create target
- final Target target = testdataFactory.createTarget();
- final List targets = Lists.newArrayList(target);
-
- // create ds
- final DistributionSet ds = testdataFactory.createDistributionSet("");
-
- // create artifact
- final byte random[] = RandomUtils.nextBytes(ARTIFACT_SIZE);
- artifactManagement.createArtifact(new ByteArrayInputStream(random), getOsModule(ds), "file1.tar.bz2", false);
-
- // download fails as artifact is not yet assigned to target
- assignDistributionSet(ds, targets);
- mvc.perform(get("/controller/artifacts/v1/filename/{filename}", "file1.tar.bz2"))
- .andExpect(status().isNotFound());
-
- assertThat(downLoadProgress).isEqualTo(1);
- assertThat(shippedBytes).isEqualTo(0L);
- }
-
- @Test
- @WithUser(principal = TestdataFactory.DEFAULT_CONTROLLER_ID, authorities = "ROLE_CONTROLLER", allSpPermissions = true)
- @Description("Ensures that an authenticated and named controller is permitted to download.")
- public void downloadArtifactByNameByNamedController() throws Exception {
- downLoadProgress = 1;
- shippedBytes = 0;
-
- assertThat(softwareManagement.findSoftwareModulesAll(pageReq)).hasSize(0);
-
- // create target
- final Target target = testdataFactory.createTarget();
- final List targets = Lists.newArrayList(target);
-
- // create ds
- final DistributionSet ds = testdataFactory.createDistributionSet("");
-
- // create artifact
- final byte random[] = RandomUtils.nextBytes(ARTIFACT_SIZE);
- final Artifact artifact = artifactManagement.createArtifact(new ByteArrayInputStream(random), getOsModule(ds),
- "file1", false);
-
- // download fails as artifact is not yet assigned to target
- mvc.perform(get("/{tenant}/controller/artifacts/v1/filename/{filename}", tenantAware.getCurrentTenant(),
- "file1.tar.bz2")).andExpect(status().isNotFound());
-
- // now assign and download successful
- assignDistributionSet(ds, targets);
- final MvcResult result = mvc
- .perform(get("/{tenant}/controller/artifacts/v1/filename/{filename}", tenantAware.getCurrentTenant(),
- "file1"))
- .andExpect(status().isOk()).andExpect(header().string("ETag", artifact.getSha1Hash()))
- .andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
- .andExpect(header().string("Accept-Ranges", "bytes"))
- .andExpect(header().string("Last-Modified", dateFormat.format(new Date(artifact.getCreatedAt()))))
- .andExpect(header().string("Content-Disposition", "attachment;filename=file1")).andReturn();
-
- assertTrue("The same file that was uploaded is expected when downloaded",
- Arrays.equals(result.getResponse().getContentAsByteArray(), random));
-
- // one (update) action
- assertThat(deploymentManagement.countActionsByTarget(target.getControllerId())).isEqualTo(1);
- final Action action = deploymentManagement.findActionsByTarget(target.getControllerId(), pageReq).getContent()
- .get(0);
-
- // one status - download
- assertThat(action.getActionStatus()).hasSize(2);
- assertThat(deploymentManagement
- .findActionStatusByAction(new PageRequest(0, 400, Direction.DESC, "id"), action.getId()).getContent()
- .get(0).getStatus()).isEqualTo(Status.DOWNLOAD);
-
- // download complete
- assertThat(downLoadProgress).isEqualTo(10);
- assertThat(shippedBytes).isEqualTo(ARTIFACT_SIZE);
- }
-
@Test
@WithUser(principal = TestdataFactory.DEFAULT_CONTROLLER_ID, authorities = "ROLE_CONTROLLER", allSpPermissions = true)
@Description("Test various HTTP range requests for artifact download, e.g. chunk download or download resume.")
- public void rangeDownloadArtifactByName() throws Exception {
+ public void rangeDownloadArtifact() throws Exception {
// create target
final Target target = testdataFactory.createTarget();
final List targets = Lists.newArrayList(target);
@@ -425,8 +259,10 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
final String rangeString = "" + i * range + "-" + ((i + 1) * range - 1);
final MvcResult result = mvc
- .perform(get("/{tenant}/controller/artifacts/v1/filename/{filename}",
- tenantAware.getCurrentTenant(), "file1").header("Range", "bytes=" + rangeString))
+ .perform(
+ get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
+ tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
+ .header("Range", "bytes=" + rangeString))
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
.andExpect(header().string("Accept-Ranges", "bytes"))
@@ -442,8 +278,10 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// return last 1000 Bytes
MvcResult result = mvc
- .perform(get("/${tenant}/controller/artifacts/v1/filename/{filename}", tenantAware.getCurrentTenant(),
- "file1").header("Range", "bytes=-1000"))
+ .perform(
+ get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
+ tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
+ .header("Range", "bytes=-1000"))
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
.andExpect(header().string("Accept-Ranges", "bytes"))
@@ -458,8 +296,10 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// skip first 1000 Bytes and return the rest
result = mvc
- .perform(get("/{tenant}/controller/artifacts/v1/filename/{filename}", tenantAware.getCurrentTenant(),
- "file1").header("Range", "bytes=1000-"))
+ .perform(
+ get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
+ tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
+ .header("Range", "bytes=1000-"))
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
.andExpect(header().string("Accept-Ranges", "bytes"))
@@ -474,8 +314,10 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// multipart download - first 20 bytes in 2 parts
result = mvc
- .perform(get("/{tenant}/controller/artifacts/v1/filename/{filename}", tenantAware.getCurrentTenant(),
- "file1").header("Range", "bytes=0-9,10-19"))
+ .perform(
+ get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
+ tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
+ .header("Range", "bytes=0-9,10-19"))
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
.andExpect(content().contentType("multipart/byteranges; boundary=THIS_STRING_SEPARATES_MULTIPART"))
.andExpect(header().string("Accept-Ranges", "bytes"))
@@ -497,54 +339,7 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
}
- @Test
- @Description("Ensures that the download fails if te controller is not authenticated.")
- public void faildDownloadArtifactByNameIfAuthenticationMissing() throws Exception {
- assertThat(softwareManagement.findSoftwareModulesAll(pageReq)).hasSize(0);
-
- // create target
- final Target target = testdataFactory.createTarget();
- final List targets = Lists.newArrayList(target);
-
- // create ds
- final DistributionSet ds = testdataFactory.createDistributionSet("");
-
- // create artifact
- final byte random[] = RandomUtils.nextBytes(5 * 1024);
- final Artifact artifact = artifactManagement.createArtifact(new ByteArrayInputStream(random), getOsModule(ds),
- "file1.tar.bz2", false);
-
- // download fails as artifact is not yet assigned to target
- mvc.perform(get("/controller/artifacts/v1/filename/{filename}", "file1.tar.bz2"))
- .andExpect(status().isNotFound());
- }
-
- @Test
- @Description("Downloads an MD5SUM file by the related artifacts filename.")
- public void downloadMd5sumFileByName() throws Exception {
- // create target
- final Target target = testdataFactory.createTarget();
-
- // create ds
- final DistributionSet ds = testdataFactory.createDistributionSet("");
-
- // create artifact
- final byte random[] = RandomUtils.nextBytes(5 * 1024);
- final Artifact artifact = artifactManagement.createArtifact(new ByteArrayInputStream(random), getOsModule(ds),
- "file1.tar.bz2", false);
-
- // download
- final MvcResult result = mvc
- .perform(get("/{tenant}/controller/artifacts/v1/filename/file1.tar.bz2.MD5SUM",
- tenantAware.getCurrentTenant()))
- .andExpect(status().isOk())
- .andExpect(header().string("Content-Disposition", "attachment;filename=file1.tar.bz2.MD5SUM"))
- .andReturn();
-
- assertThat(result.getResponse().getContentAsByteArray())
- .isEqualTo(new String(artifact.getMd5Hash() + " file1.tar.bz2").getBytes(Charsets.US_ASCII));
- }
-
+ @Configuration
public static class DownloadTestConfiguration {
@Bean
@@ -557,7 +352,7 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
private static class Listener {
@EventListener(classes = DownloadProgressEvent.class)
- public void listen(final DownloadProgressEvent event) {
+ public static void listen(final DownloadProgressEvent event) {
downLoadProgress++;
shippedBytes += event.getShippedBytesSinceLast();
diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java
index e4af4c8bf..4ef020f1e 100644
--- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java
+++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java
@@ -304,7 +304,9 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware {
.addFilter(new DosFilter(100, 10, "127\\.0\\.0\\.1|\\[0:0:0:0:0:0:0:1\\]", "(^192\\.168\\.)",
"X-Forwarded-For"))
.addFilter(new ExcludePathAwareShallowETagFilter(
- "/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download", "/*/controller/artifacts/**"));
+ "/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download",
+ "/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/**",
+ "/api/v1/downloadserver/**"));
}
private static CIMySqlTestDatabase tesdatabase;
diff --git a/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/StreamAwareErrorController.java b/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/StreamAwareErrorController.java
new file mode 100644
index 000000000..e6bc8590d
--- /dev/null
+++ b/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/StreamAwareErrorController.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.hawkbit.app;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.boot.autoconfigure.web.BasicErrorController;
+import org.springframework.boot.autoconfigure.web.ErrorAttributes;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * Error page controller that ensures that ocet stream does not return text in
+ * case of an error.
+ *
+ */
+@Controller
+public class StreamAwareErrorController extends BasicErrorController {
+
+ /**
+ * A new {@link StreamAwareErrorController}.
+ *
+ * @param errorAttributes
+ * the error attributes
+ * @param serverProperties
+ * configuration properties
+ */
+ public StreamAwareErrorController(final ErrorAttributes errorAttributes, final ServerProperties serverProperties) {
+ super(errorAttributes, serverProperties.getError());
+ }
+
+ @RequestMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
+ ResponseEntity errorStream(final HttpServletRequest request, final HttpServletResponse response) {
+ final HttpStatus status = getStatus(request);
+ return new ResponseEntity<>(status);
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 451ba1ee4..e484c2c3d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,7 +37,6 @@
hawkbit-mgmt-api
hawkbit-mgmt-resource
hawkbit-ddi-api
- hawkbit-ddi-dl-api
hawkbit-ddi-resource
hawkbit-dmf
hawkbit-repository