Artifact Encryption plug point (#1202)
* added ArtifactEncryption interface, injected it into SM creation UI module, added encryption metadata key generation upon SM creation, used encryptor during file upload Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * add default artifact encryption implementation based on gcm aes algorithm Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * changed ArtifactEncryptor interface to manage encryption secrets by itself Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * cleaned up stale code, fixed sonar Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * fixed software module encryption within transaction Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added artifact encryption secrets store Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * extended ArtifactEncryption interface to allow decryption, secrets store provides removeSecret, added missing javadocs Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * intriduced DbArtifact interface, use EncryptionAwareDbArtifact for artifact decryption during download Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * introduced ArtifactEncryptionService to minimize duplications and unneccessary dependency injections Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * declared ArtifactEncryptionService as a bean Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added persistant encryption flag to software module Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * further adptations for encryption flag persistence Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added ArtifactEncryptionException, fixed encryption check in UI Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added encryption error handling Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added encrypted flag to DDI/DMF, adapted exception handling Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * adapted rest docs Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * Add test to verify artifact encryption is not given by default Signed-off-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io> * Add isEncrypted() to toString() of JpaSoftwareModule, fix typos Signed-off-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io> * Fix sql migration scripts Signed-off-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io> * Calculate encrypted artifact size by subtract encryption size overhead Signed-off-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io> * publish upload failed without waiting for interuption during UI file upload Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * upgraded cron utils to 9.1.6 Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> Co-authored-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io>
This commit is contained in:
@@ -35,6 +35,10 @@ public class DdiChunk {
|
||||
@NotNull
|
||||
private String name;
|
||||
|
||||
@JsonProperty("encrypted")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private Boolean encrypted;
|
||||
|
||||
@JsonProperty("artifacts")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private List<DdiArtifact> artifacts;
|
||||
@@ -56,16 +60,19 @@ public class DdiChunk {
|
||||
* of the artifact
|
||||
* @param name
|
||||
* of the artifact
|
||||
* @param encrypted
|
||||
* if artifacts are encrypted
|
||||
* @param artifacts
|
||||
* download information
|
||||
* @param metadata
|
||||
* optional as additional information for the target/device
|
||||
*/
|
||||
public DdiChunk(final String part, final String version, final String name, final List<DdiArtifact> artifacts,
|
||||
final List<DdiMetadata> metadata) {
|
||||
public DdiChunk(final String part, final String version, final String name, final Boolean encrypted,
|
||||
final List<DdiArtifact> artifacts, final List<DdiMetadata> metadata) {
|
||||
this.part = part;
|
||||
this.version = version;
|
||||
this.name = name;
|
||||
this.encrypted = encrypted;
|
||||
this.artifacts = artifacts;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
@@ -82,6 +89,10 @@ public class DdiChunk {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Boolean isEncrypted() {
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
public List<DdiArtifact> getArtifacts() {
|
||||
if (artifacts == null) {
|
||||
return Collections.emptyList();
|
||||
|
||||
@@ -32,21 +32,21 @@ import io.qameta.allure.Story;
|
||||
@Story("Serializability of DDI api Models")
|
||||
public class DdiChunkTest {
|
||||
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@Test
|
||||
@Description("Verify the correct serialization and deserialization of the model")
|
||||
public void shouldSerializeAndDeserializeObject() throws IOException {
|
||||
// Setup
|
||||
String part = "1234";
|
||||
String version = "1.0";
|
||||
String name = "Dummy-Artifact";
|
||||
List<DdiArtifact> dummyArtifacts = Collections.emptyList();
|
||||
DdiChunk ddiChunk = new DdiChunk(part, version, name, dummyArtifacts, null);
|
||||
final String part = "1234";
|
||||
final String version = "1.0";
|
||||
final String name = "Dummy-Artifact";
|
||||
final List<DdiArtifact> dummyArtifacts = Collections.emptyList();
|
||||
final DdiChunk ddiChunk = new DdiChunk(part, version, name, null, dummyArtifacts, null);
|
||||
|
||||
// Test
|
||||
String serializedDdiChunk = mapper.writeValueAsString(ddiChunk);
|
||||
DdiChunk deserializedDdiChunk = mapper.readValue(serializedDdiChunk, DdiChunk.class);
|
||||
final String serializedDdiChunk = mapper.writeValueAsString(ddiChunk);
|
||||
final DdiChunk deserializedDdiChunk = mapper.readValue(serializedDdiChunk, DdiChunk.class);
|
||||
|
||||
assertThat(serializedDdiChunk).contains(part, version, name);
|
||||
assertThat(deserializedDdiChunk.getPart()).isEqualTo(part);
|
||||
@@ -59,10 +59,10 @@ public class DdiChunkTest {
|
||||
@Description("Verify the correct deserialization of a model with a additional unknown property")
|
||||
public void shouldDeserializeObjectWithUnknownProperty() throws IOException {
|
||||
// Setup
|
||||
String serializedDdiChunk = "{\"part\":\"1234\",\"version\":\"1.0\",\"name\":\"Dummy-Artifact\",\"artifacts\":[],\"unknownProperty\":\"test\"}";
|
||||
final String serializedDdiChunk = "{\"part\":\"1234\",\"version\":\"1.0\",\"name\":\"Dummy-Artifact\",\"artifacts\":[],\"unknownProperty\":\"test\"}";
|
||||
|
||||
// Test
|
||||
DdiChunk ddiChunk = mapper.readValue(serializedDdiChunk, DdiChunk.class);
|
||||
final DdiChunk ddiChunk = mapper.readValue(serializedDdiChunk, DdiChunk.class);
|
||||
|
||||
assertThat(ddiChunk.getPart()).isEqualTo("1234");
|
||||
assertThat(ddiChunk.getVersion()).isEqualTo("1.0");
|
||||
@@ -74,10 +74,10 @@ public class DdiChunkTest {
|
||||
@Description("Verify that deserialization fails for known properties with a wrong datatype")
|
||||
public void shouldFailForObjectWithWrongDataTypes() throws IOException {
|
||||
// Setup
|
||||
String serializedDdiChunk = "{\"part\":[\"1234\"],\"version\":\"1.0\",\"name\":\"Dummy-Artifact\",\"artifacts\":[]}";
|
||||
final String serializedDdiChunk = "{\"part\":[\"1234\"],\"version\":\"1.0\",\"name\":\"Dummy-Artifact\",\"artifacts\":[]}";
|
||||
|
||||
// Test
|
||||
assertThatExceptionOfType(MismatchedInputException.class).isThrownBy(
|
||||
() -> mapper.readValue(serializedDdiChunk, DdiChunk.class));
|
||||
assertThatExceptionOfType(MismatchedInputException.class)
|
||||
.isThrownBy(() -> mapper.readValue(serializedDdiChunk, DdiChunk.class));
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public final class DataConversionHelper {
|
||||
|
||||
return new ResponseList<>(uAction.getDistributionSet().getModules().stream()
|
||||
.map(module -> new DdiChunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(),
|
||||
module.getName(),
|
||||
module.getName(), module.isEncrypted() ? Boolean.TRUE : null,
|
||||
createArtifacts(target, module, artifactUrlHandler, systemManagement, request),
|
||||
mapMetadata(metadata.get(module.getId()))))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
@@ -18,7 +18,7 @@ import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiActionHistory;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiArtifact;
|
||||
@@ -183,7 +183,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
@SuppressWarnings("squid:S3655")
|
||||
final Artifact artifact = module.getArtifactByFilename(fileName).get();
|
||||
|
||||
final AbstractDbArtifact file = artifactManagement.loadArtifactBinary(artifact.getSha1Hash())
|
||||
final DbArtifact file = artifactManagement
|
||||
.loadArtifactBinary(artifact.getSha1Hash(), module.getId(), module.isEncrypted())
|
||||
.orElseThrow(() -> new ArtifactBinaryNotFoundException(artifact.getSha1Hash()));
|
||||
|
||||
final String ifMatch = requestResponseContextHolder.getHttpServletRequest().getHeader(HttpHeaders.IF_MATCH);
|
||||
|
||||
@@ -38,6 +38,9 @@ public class MgmtSoftwareModule extends MgmtNamedEntity {
|
||||
@JsonProperty
|
||||
private boolean deleted;
|
||||
|
||||
@JsonProperty
|
||||
private boolean encrypted;
|
||||
|
||||
public void setDeleted(final boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
@@ -79,4 +82,12 @@ public class MgmtSoftwareModule extends MgmtNamedEntity {
|
||||
this.vendor = vendor;
|
||||
}
|
||||
|
||||
public void setEncrypted(final boolean encrypted) {
|
||||
this.encrypted = encrypted;
|
||||
}
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@ public class MgmtSoftwareModuleRequestBodyPost {
|
||||
@JsonProperty
|
||||
private String vendor;
|
||||
|
||||
@JsonProperty
|
||||
private boolean encrypted;
|
||||
|
||||
/**
|
||||
* @return the name
|
||||
*/
|
||||
@@ -121,4 +124,22 @@ public class MgmtSoftwareModuleRequestBodyPost {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if encrypted
|
||||
*/
|
||||
public boolean isEncrypted() {
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param encrypted
|
||||
* if should be encrypted
|
||||
*
|
||||
* @return updated body
|
||||
*/
|
||||
public MgmtSoftwareModuleRequestBodyPost setEncrypted(final boolean encrypted) {
|
||||
this.encrypted = encrypted;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.io.InputStream;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDownloadArtifactRestApi;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||
@@ -70,7 +70,8 @@ public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi
|
||||
final Artifact artifact = module.getArtifact(artifactId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, artifactId));
|
||||
|
||||
final AbstractDbArtifact file = artifactManagement.loadArtifactBinary(artifact.getSha1Hash())
|
||||
final DbArtifact file = artifactManagement
|
||||
.loadArtifactBinary(artifact.getSha1Hash(), module.getId(), module.isEncrypted())
|
||||
.orElseThrow(() -> new ArtifactBinaryNotFoundException(artifact.getSha1Hash()));
|
||||
final HttpServletRequest request = requestResponseContextHolder.getHttpServletRequest();
|
||||
final String ifMatch = request.getHeader(HttpHeaders.IF_MATCH);
|
||||
|
||||
@@ -45,7 +45,8 @@ public final class MgmtSoftwareModuleMapper {
|
||||
private static SoftwareModuleCreate fromRequest(final EntityFactory entityFactory,
|
||||
final MgmtSoftwareModuleRequestBodyPost smsRest) {
|
||||
return entityFactory.softwareModule().create().type(smsRest.getType()).name(smsRest.getName())
|
||||
.version(smsRest.getVersion()).description(smsRest.getDescription()).vendor(smsRest.getVendor());
|
||||
.version(smsRest.getVersion()).description(smsRest.getDescription()).vendor(smsRest.getVendor())
|
||||
.encrypted(smsRest.isEncrypted());
|
||||
}
|
||||
|
||||
static List<SoftwareModuleMetadataCreate> fromRequestSwMetadata(final EntityFactory entityFactory,
|
||||
@@ -107,6 +108,7 @@ public final class MgmtSoftwareModuleMapper {
|
||||
response.setType(softwareModule.getType().getKey());
|
||||
response.setVendor(softwareModule.getVendor());
|
||||
response.setDeleted(softwareModule.isDeleted());
|
||||
response.setEncrypted(softwareModule.isEncrypted());
|
||||
|
||||
response.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class).getSoftwareModule(response.getModuleId()))
|
||||
.withSelfRel());
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
@@ -199,7 +200,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
||||
@Test
|
||||
@Description("Verifies that artifacts which exceed the configured maximum size cannot be uploaded.")
|
||||
public void uploadArtifactFailsIfTooLarge() throws Exception {
|
||||
final SoftwareModule sm = testdataFactory.createSoftwareModule("quota", "quota");
|
||||
final SoftwareModule sm = testdataFactory.createSoftwareModule("quota", "quota", false);
|
||||
final long maxSize = quotaManagement.getMaxArtifactSize();
|
||||
|
||||
// create a file which exceeds the configured maximum size
|
||||
@@ -218,7 +219,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
||||
@Test
|
||||
@Description("Verifies that artifact with invalid filename cannot be uploaded to prevent cross site scripting.")
|
||||
public void uploadArtifactFailsIfFilenameInvalide() throws Exception {
|
||||
final SoftwareModule sm = testdataFactory.createSoftwareModule("quota", "quota");
|
||||
final SoftwareModule sm = testdataFactory.createSoftwareModule("quota", "quota", false);
|
||||
final String illegalFilename = "<img src=ernw onerror=alert(1)>.xml";
|
||||
|
||||
final byte[] randomBytes = randomBytes(5 * 1024);
|
||||
@@ -236,11 +237,12 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
||||
assertThat(artifactManagement.count()).as("Wrong artifact size").isEqualTo(1);
|
||||
|
||||
// binary
|
||||
try (InputStream fileInputStream = artifactManagement
|
||||
.loadArtifactBinary(softwareModuleManagement.get(sm.getId()).get().getArtifacts().get(0).getSha1Hash())
|
||||
try (final InputStream fileInputStream = artifactManagement
|
||||
.loadArtifactBinary(softwareModuleManagement.get(sm.getId()).get().getArtifacts().get(0).getSha1Hash(),
|
||||
sm.getId(), sm.isEncrypted())
|
||||
.get().getFileInputStream()) {
|
||||
assertTrue(
|
||||
IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream), "Wrong artifact content");
|
||||
assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream),
|
||||
"Wrong artifact content");
|
||||
}
|
||||
|
||||
// hashes
|
||||
@@ -662,6 +664,14 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isUnsupportedMediaType());
|
||||
|
||||
final SoftwareModule swm = entityFactory.softwareModule().create().name("encryptedModule").type(osType)
|
||||
.version("version").vendor("vendor").description("description").encrypted(true).build();
|
||||
// artifact decryption is not supported
|
||||
mvc.perform(
|
||||
post("/rest/v1/softwaremodules").content(JsonBuilder.softwareModules(Collections.singletonList(swm)))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
|
||||
|
||||
// not allowed methods
|
||||
mvc.perform(put("/rest/v1/softwaremodules")).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isMethodNotAllowed());
|
||||
|
||||
@@ -8,14 +8,15 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.rest.exception;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.ValidationException;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
@@ -31,6 +32,8 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* General controller advice for exception handling.
|
||||
*/
|
||||
@@ -54,6 +57,8 @@ public class ResponseExceptionHandler {
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REST_RSQL_SEARCH_PARAM_SYNTAX, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_INSUFFICIENT_PERMISSION, HttpStatus.FORBIDDEN);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_ENCRYPTION_NOT_SUPPORTED, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_ENCRYPTION_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_SHA1_MATCH, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_SHA256_MATCH, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH, HttpStatus.BAD_REQUEST);
|
||||
|
||||
@@ -20,7 +20,7 @@ import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -118,9 +118,9 @@ public final class FileStreamingUtil {
|
||||
* @throws FileStreamingFailedException
|
||||
* if streaming fails
|
||||
*/
|
||||
public static ResponseEntity<InputStream> writeFileResponse(final AbstractDbArtifact artifact,
|
||||
final String filename, final long lastModified, final HttpServletResponse response,
|
||||
final HttpServletRequest request, final FileStreamingProgressListener progressListener) {
|
||||
public static ResponseEntity<InputStream> writeFileResponse(final DbArtifact artifact, final String filename,
|
||||
final long lastModified, final HttpServletResponse response, final HttpServletRequest request,
|
||||
final FileStreamingProgressListener progressListener) {
|
||||
|
||||
ResponseEntity<InputStream> result;
|
||||
|
||||
@@ -189,9 +189,9 @@ public final class FileStreamingUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ResponseEntity<InputStream> handleFullFileRequest(final AbstractDbArtifact artifact,
|
||||
final String filename, final HttpServletResponse response,
|
||||
final FileStreamingProgressListener progressListener, final ByteRange full) {
|
||||
private static ResponseEntity<InputStream> handleFullFileRequest(final DbArtifact artifact, final String filename,
|
||||
final HttpServletResponse response, final FileStreamingProgressListener progressListener,
|
||||
final ByteRange full) {
|
||||
final ByteRange r = full;
|
||||
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + r.getStart() + "-" + r.getEnd() + "/" + r.getTotal());
|
||||
response.setContentLengthLong(r.getLength());
|
||||
@@ -257,7 +257,7 @@ public final class FileStreamingUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static ResponseEntity<InputStream> handleMultipartRangeRequest(final AbstractDbArtifact artifact,
|
||||
private static ResponseEntity<InputStream> handleMultipartRangeRequest(final DbArtifact artifact,
|
||||
final String filename, final HttpServletResponse response,
|
||||
final FileStreamingProgressListener progressListener, final List<ByteRange> ranges) {
|
||||
|
||||
@@ -291,7 +291,7 @@ public final class FileStreamingUtil {
|
||||
return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).build();
|
||||
}
|
||||
|
||||
private static ResponseEntity<InputStream> handleStandardRangeRequest(final AbstractDbArtifact artifact,
|
||||
private static ResponseEntity<InputStream> handleStandardRangeRequest(final DbArtifact artifact,
|
||||
final String filename, final HttpServletResponse response,
|
||||
final FileStreamingProgressListener progressListener, final List<ByteRange> ranges) {
|
||||
final ByteRange r = ranges.get(0);
|
||||
|
||||
@@ -97,7 +97,8 @@ public abstract class JsonBuilder {
|
||||
builder.append(new JSONObject().put("name", module.getName()).put("description", module.getDescription())
|
||||
.put("type", module.getType().getKey()).put("id", Long.MAX_VALUE).put("vendor", module.getVendor())
|
||||
.put("version", module.getVersion()).put("createdAt", "0").put("updatedAt", "0")
|
||||
.put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh").toString());
|
||||
.put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh")
|
||||
.put("encrypted", module.isEncrypted()).toString());
|
||||
|
||||
if (++i < modules.size()) {
|
||||
builder.append(",");
|
||||
@@ -447,7 +448,8 @@ public abstract class JsonBuilder {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String targets(final List<Target> targets, final boolean withToken, final long targetTypeId) throws JSONException {
|
||||
public static String targets(final List<Target> targets, final boolean withToken, final long targetTypeId)
|
||||
throws JSONException {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("[");
|
||||
@@ -487,8 +489,8 @@ public abstract class JsonBuilder {
|
||||
});
|
||||
|
||||
result.put(new JSONObject().put("name", type.getName()).put("description", type.getDescription())
|
||||
.put("id", Long.MAX_VALUE).put("colour", type.getColour()).put("createdAt", "0").put("updatedAt", "0")
|
||||
.put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh")
|
||||
.put("id", Long.MAX_VALUE).put("colour", type.getColour()).put("createdAt", "0")
|
||||
.put("updatedAt", "0").put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh")
|
||||
.put("distributionsets", dsTypes));
|
||||
|
||||
}
|
||||
@@ -510,11 +512,10 @@ public abstract class JsonBuilder {
|
||||
}
|
||||
});
|
||||
|
||||
JSONObject json = new JSONObject().put("name", type.getName()).put("description", type.getDescription())
|
||||
final JSONObject json = new JSONObject().put("name", type.getName()).put("description", type.getDescription())
|
||||
.put("colour", type.getColour());
|
||||
|
||||
if(dsTypes.length() != 0)
|
||||
{
|
||||
if (dsTypes.length() != 0) {
|
||||
json.put("compatibledistributionsettypes", dsTypes);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ final class DdiApiModelProperties {
|
||||
|
||||
static final String CHUNK_TYPE = "Type of the chunk, e.g. firmware, bundle, app. In update server mapped to Software Module Type.";
|
||||
|
||||
static final String SOFTWARE_MODUL_TYPE = "type of the software module, e.g. firmware, bundle, app";
|
||||
static final String SOFTWARE_MODULE_TYPE = "type of the software module, e.g. firmware, bundle, app";
|
||||
|
||||
static final String SOFTWARE_MODULE_VERSION = "version of the software module";
|
||||
|
||||
@@ -68,7 +68,7 @@ final class DdiApiModelProperties {
|
||||
|
||||
static final String SOFTWARE_MODULE_ARTIFACT_LINKS = "artifact links of the software module";
|
||||
|
||||
static final String SOFTWARE_MODUL_ID = "id of the software module";
|
||||
static final String SOFTWARE_MODULE_ID = "id of the software module";
|
||||
|
||||
static final String CHUNK_VERSION = "software version of the chunk";
|
||||
|
||||
@@ -102,15 +102,15 @@ final class DdiApiModelProperties {
|
||||
|
||||
static final String CHUNK = "Software chunks of an update. In server mapped by Software Module.";
|
||||
|
||||
static final String SOFTWARE_MODUL = "software modules of an update";
|
||||
static final String SOFTWARE_MODULE = "software modules of an update";
|
||||
|
||||
static final String ARTIFACT = "artifact modules of an update";
|
||||
|
||||
static final String FILENAME = "file name of artifact";
|
||||
|
||||
static final String TARGET_CONFIG_DATA = "Link which is provided whenever the provisioning target or device is supposed "
|
||||
+ "to push its configuration data (aka. \"controller attributes\") to the server. Only shown for the initial "
|
||||
+ "configuration, after a successful update action, or if requested explicitly (e.g. via the management UI).";
|
||||
+ "to push its configuration data (aka. \"controller attributes\") to the server. Only shown for the initial "
|
||||
+ "configuration, after a successful update action, or if requested explicitly (e.g. via the management UI).";
|
||||
|
||||
static final String ARTIFACT_HASHES_SHA1 = "SHA1 hash of the artifact in Base 16 format";
|
||||
static final String ARTIFACT_HASHES_MD5 = "MD5 hash of the artifact";
|
||||
|
||||
@@ -183,7 +183,9 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
|
||||
pathParameters(parameterWithName("tenant").description(ApiModelPropertiesGeneric.TENANT),
|
||||
parameterWithName("controllerId").description(DdiApiModelProperties.CONTROLLER_ID),
|
||||
parameterWithName("actionId").description(DdiApiModelProperties.ACTION_ID_CANCELED)),
|
||||
requestFields(optionalRequestFieldWithPath("id").description(DdiApiModelProperties.FEEDBACK_ACTION_ID),
|
||||
requestFields(
|
||||
optionalRequestFieldWithPath("id")
|
||||
.description(DdiApiModelProperties.FEEDBACK_ACTION_ID),
|
||||
requestFieldWithPath("status").description(DdiApiModelProperties.TARGET_STATUS),
|
||||
requestFieldWithPath("status.execution")
|
||||
.description(DdiApiModelProperties.TARGET_EXEC_STATUS).type("enum")
|
||||
@@ -386,7 +388,9 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
|
||||
parameterWithName("controllerId").description(DdiApiModelProperties.CONTROLLER_ID),
|
||||
parameterWithName("actionId").description(DdiApiModelProperties.ACTION_ID)),
|
||||
|
||||
requestFields(optionalRequestFieldWithPath("id").description(DdiApiModelProperties.FEEDBACK_ACTION_ID),
|
||||
requestFields(
|
||||
optionalRequestFieldWithPath("id")
|
||||
.description(DdiApiModelProperties.FEEDBACK_ACTION_ID),
|
||||
requestFieldWithPath("status").description(DdiApiModelProperties.TARGET_STATUS),
|
||||
requestFieldWithPath("status.execution")
|
||||
.description(DdiApiModelProperties.TARGET_EXEC_STATUS).type("enum")
|
||||
@@ -428,7 +432,7 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
|
||||
.andDo(this.document.document(
|
||||
pathParameters(parameterWithName("tenant").description(ApiModelPropertiesGeneric.TENANT),
|
||||
parameterWithName("controllerId").description(DdiApiModelProperties.CONTROLLER_ID),
|
||||
parameterWithName("moduleId").description(DdiApiModelProperties.SOFTWARE_MODUL_ID)),
|
||||
parameterWithName("moduleId").description(DdiApiModelProperties.SOFTWARE_MODULE_ID)),
|
||||
responseFields(fieldWithPath("[]filename").description(DdiApiModelProperties.ARTIFACTS),
|
||||
fieldWithPath("[]hashes").description(DdiApiModelProperties.ARTIFACTS),
|
||||
fieldWithPath("[]hashes.sha1").description(DdiApiModelProperties.ARTIFACT_HASHES_SHA1),
|
||||
|
||||
@@ -43,6 +43,7 @@ public final class MgmtApiModelProperties {
|
||||
|
||||
// software module
|
||||
public static final String SM_TYPE = "The software module type " + ApiModelPropertiesGeneric.ENDING;
|
||||
public static final String ENCRYPTED = "Encryption flag, used to identify that artifacts should be encrypted upon upload.";
|
||||
public static final String ARTIFACT_HASHES = "Hashes of the artifact.";
|
||||
public static final String ARTIFACT_SIZE = "Size of the artifact.";
|
||||
public static final String ARTIFACT_PROVIDED_FILE = "Binary of file.";
|
||||
|
||||
@@ -84,6 +84,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati
|
||||
fieldWithPath("content[].name").description(ApiModelPropertiesGeneric.NAME),
|
||||
fieldWithPath("content[].description").description(ApiModelPropertiesGeneric.DESCRPTION),
|
||||
fieldWithPath("content[].vendor").description(MgmtApiModelProperties.VENDOR),
|
||||
fieldWithPath("content[].encrypted").description(MgmtApiModelProperties.ENCRYPTED),
|
||||
fieldWithPath("content[].createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||
fieldWithPath("content[].createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||
fieldWithPath("content[].lastModifiedBy")
|
||||
@@ -141,6 +142,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati
|
||||
fieldWithPath("[].name").description(ApiModelPropertiesGeneric.NAME),
|
||||
fieldWithPath("[].description").description(ApiModelPropertiesGeneric.DESCRPTION),
|
||||
fieldWithPath("[].vendor").description(MgmtApiModelProperties.VENDOR),
|
||||
fieldWithPath("[].encrypted").description(MgmtApiModelProperties.ENCRYPTED),
|
||||
fieldWithPath("[].deleted").description(ApiModelPropertiesGeneric.DELETED),
|
||||
fieldWithPath("[].createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||
fieldWithPath("[].createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||
@@ -188,6 +190,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati
|
||||
fieldWithPath("lastModifiedBy").description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY),
|
||||
fieldWithPath("lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT),
|
||||
fieldWithPath("vendor").description(MgmtApiModelProperties.VENDOR),
|
||||
fieldWithPath("encrypted").description(MgmtApiModelProperties.ENCRYPTED),
|
||||
fieldWithPath("deleted").description(ApiModelPropertiesGeneric.DELETED),
|
||||
fieldWithPath("type").description(MgmtApiModelProperties.SM_TYPE),
|
||||
fieldWithPath("version").description(MgmtApiModelProperties.VERSION),
|
||||
@@ -227,6 +230,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati
|
||||
fieldWithPath("type").description(MgmtApiModelProperties.SM_TYPE),
|
||||
fieldWithPath("version").description(MgmtApiModelProperties.VERSION),
|
||||
fieldWithPath("vendor").description(MgmtApiModelProperties.VENDOR),
|
||||
fieldWithPath("encrypted").description(MgmtApiModelProperties.ENCRYPTED),
|
||||
fieldWithPath("deleted").description(ApiModelPropertiesGeneric.DELETED),
|
||||
fieldWithPath("_links.self").ignored(),
|
||||
fieldWithPath("_links.type").description(MgmtApiModelProperties.SM_TYPE),
|
||||
@@ -279,7 +283,9 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati
|
||||
final byte random[] = RandomStringUtils.random(5).getBytes();
|
||||
final MockMultipartFile file = new MockMultipartFile("file", "origFilename", null, random);
|
||||
|
||||
mockMvc.perform(fileUpload(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/artifacts", sm.getId()).file(file))
|
||||
mockMvc.perform(
|
||||
fileUpload(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/artifacts",
|
||||
sm.getId()).file(file))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
||||
.andExpect(content().contentType(MediaTypes.HAL_JSON))
|
||||
.andDo(this.document.document(
|
||||
@@ -313,7 +319,8 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati
|
||||
final byte random[] = RandomStringUtils.random(5).getBytes();
|
||||
final MockMultipartFile file = new MockMultipartFile("file", "origFilename", null, random);
|
||||
|
||||
mockMvc.perform(fileUpload(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/artifacts",
|
||||
mockMvc.perform(
|
||||
fileUpload(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING + "/{softwareModuleId}/artifacts",
|
||||
sm.getId()).file(file).param("filename", "filename").param("file", "s")
|
||||
.param("md5sum", "md5sum").param("sha1sum", "sha1sum"))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest())
|
||||
|
||||
Reference in New Issue
Block a user