diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/AbstractDbArtifact.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/AbstractDbArtifact.java
index adffe3f99..4ef06268d 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/AbstractDbArtifact.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/AbstractDbArtifact.java
@@ -8,15 +8,13 @@
*/
package org.eclipse.hawkbit.artifact.repository.model;
-import java.io.InputStream;
-
import org.springframework.util.Assert;
/**
* Database representation of artifact.
*
*/
-public abstract class AbstractDbArtifact {
+public abstract class AbstractDbArtifact implements DbArtifact {
private final String artifactId;
private final long size;
@@ -34,46 +32,33 @@ public abstract class AbstractDbArtifact {
this.contentType = contentType;
}
- /**
- * @return ID of the artifact
- */
+ @Override
public String getArtifactId() {
return artifactId;
}
- /**
- * @return hashes of the artifact
- */
+ @Override
public DbArtifactHash getHashes() {
return hashes;
}
/**
* Set hashes of the artifact
+ *
+ * @param hashes
+ * artifact hashes
*/
public void setHashes(final DbArtifactHash hashes) {
this.hashes = hashes;
}
- /**
- * @return site of the artifact in bytes
- */
+ @Override
public long getSize() {
return size;
}
- /**
- * @return content-type if known by the repository or null
- */
+ @Override
public String getContentType() {
return contentType;
}
-
- /**
- * Creates an {@link InputStream} on this artifact. Caller has to take care of
- * closing the stream. Repeatable calls open a new {@link InputStream}.
- *
- * @return {@link InputStream} to read from artifact.
- */
- public abstract InputStream getFileInputStream();
}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/DbArtifact.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/DbArtifact.java
new file mode 100644
index 000000000..0af9d3fae
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/DbArtifact.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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.artifact.repository.model;
+
+import java.io.InputStream;
+
+/**
+ * Interface definition for artifact binary.
+ */
+public interface DbArtifact {
+
+ /**
+ * @return ID of the artifact
+ */
+ String getArtifactId();
+
+ /**
+ * @return hashes of the artifact
+ */
+ DbArtifactHash getHashes();
+
+ /**
+ * @return size of the artifact in bytes
+ */
+ long getSize();
+
+ /**
+ * @return content-type if known by the repository or null
+ */
+ String getContentType();
+
+ /**
+ * Creates an {@link InputStream} on this artifact. Caller has to take care of
+ * closing the stream. Repeatable calls open a new {@link InputStream}.
+ *
+ * @return {@link InputStream} to read from artifact.
+ */
+ InputStream getFileInputStream();
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java
index dd780f30f..c640d597a 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java
@@ -97,6 +97,18 @@ public enum SpServerError {
SP_ARTIFACT_UPLOAD_FAILED("hawkbit.server.error.artifact.uploadFailed",
"Upload of artifact failed with internal server error."),
+ /**
+ *
+ */
+ SP_ARTIFACT_ENCRYPTION_NOT_SUPPORTED("hawkbit.server.error.artifact.encryptionNotSupported",
+ "Artifact encryption is not supported."),
+
+ /**
+ *
+ */
+ SP_ARTIFACT_ENCRYPTION_FAILED("hawkbit.server.error.artifact.encryptionFailed",
+ "Artifact encryption operation failed."),
+
/**
*
*/
@@ -161,15 +173,15 @@ public enum SpServerError {
"Storage quota will be exceeded if file is uploaded."),
/**
- * error message, which describes that the action can not be canceled cause the
- * action is inactive.
+ * error message, which describes that the action can not be canceled cause
+ * the action is inactive.
*/
SP_ACTION_NOT_CANCELABLE("hawkbit.server.error.action.notcancelable",
"Only active actions which are in status pending are cancelable."),
/**
- * error message, which describes that the action can not be force quit cause
- * the action is inactive.
+ * error message, which describes that the action can not be force quit
+ * cause the action is inactive.
*/
SP_ACTION_NOT_FORCE_QUITABLE("hawkbit.server.error.action.notforcequitable",
"Only active actions which are in status pending can be force quit."),
@@ -250,7 +262,8 @@ public enum SpServerError {
"Information for schedule, duration or timezone is missing; or there is no valid maintenance window available in future."),
/**
- * Error message informing that the action type for auto-assignment is invalid.
+ * Error message informing that the action type for auto-assignment is
+ * invalid.
*/
SP_AUTO_ASSIGN_ACTION_TYPE_INVALID("hawkbit.server.error.repo.invalidAutoAssignActionType",
"The given action type for auto-assignment is invalid: allowed values are ['forced', 'soft', 'downloadonly']"),
diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java
index 8daa8916c..80e3113a2 100644
--- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java
+++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java
@@ -8,6 +8,18 @@
*/
package org.eclipse.hawkbit.amqp;
+import static org.eclipse.hawkbit.repository.RepositoryConstants.MAX_ACTION_COUNT;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
import org.eclipse.hawkbit.api.ApiType;
import org.eclipse.hawkbit.api.ArtifactUrl;
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
@@ -55,18 +67,6 @@ import org.springframework.context.event.EventListener;
import org.springframework.data.domain.PageRequest;
import org.springframework.util.CollectionUtils;
-import java.net.URI;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import static org.eclipse.hawkbit.repository.RepositoryConstants.MAX_ACTION_COUNT;
-
/**
* {@link AmqpMessageDispatcherService} create all outgoing AMQP messages and
* delegate the messages to a {@link AmqpMessageSenderService}.
@@ -105,8 +105,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
* @param targetManagement
* to access target information
* @param serviceMatcher
- * to check in cluster case if the message is from the same cluster
- * node
+ * to check in cluster case if the message is from the same
+ * cluster node
* @param distributionSetManagement
* to retrieve modules
*/
@@ -129,8 +129,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
}
/**
- * Method to send a message to a RabbitMQ Exchange after the Distribution set
- * has been assign to a Target.
+ * Method to send a message to a RabbitMQ Exchange after the Distribution
+ * set has been assign to a Target.
*
* @param assignedEvent
* the object to be send.
@@ -257,9 +257,9 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
/**
* Method to get the type of event depending on whether the action is a
- * DOWNLOAD_ONLY action or if it has a valid maintenance window available or not
- * based on defined maintenance schedule. In case of no maintenance schedule or
- * if there is a valid window available, the topic
+ * DOWNLOAD_ONLY action or if it has a valid maintenance window available or
+ * not based on defined maintenance schedule. In case of no maintenance
+ * schedule or if there is a valid window available, the topic
* {@link EventTopic#DOWNLOAD_AND_INSTALL} is returned else
* {@link EventTopic#DOWNLOAD} is returned.
*
@@ -275,8 +275,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
}
/**
- * Determines the {@link EventTopic} for the given {@link Action}, depending on
- * its action type.
+ * Determines the {@link EventTopic} for the given {@link Action}, depending
+ * on its action type.
*
* @param action
* to obtain the corresponding {@link EventTopic} for
@@ -291,8 +291,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
}
/**
- * Method to send a message to a RabbitMQ Exchange after the assignment of the
- * Distribution set to a Target has been canceled.
+ * Method to send a message to a RabbitMQ Exchange after the assignment of
+ * the Distribution set to a Target has been canceled.
*
* @param cancelEvent
* that is to be converted to a DMF message
@@ -315,11 +315,12 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
}
/**
- * Method to send a message to a RabbitMQ Exchange after a Target was deleted.
+ * Method to send a message to a RabbitMQ Exchange after a Target was
+ * deleted.
*
* @param deleteEvent
- * the TargetDeletedEvent which holds the necessary data for sending
- * a target delete message.
+ * the TargetDeletedEvent which holds the necessary data for
+ * sending a target delete message.
*/
@EventListener(classes = TargetDeletedEvent.class)
protected void targetDelete(final TargetDeletedEvent deleteEvent) {
@@ -345,7 +346,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
return;
}
- final DmfDownloadAndUpdateRequest downloadAndUpdateRequest = createDownloadAndUpdateRequest(target, action.getId(), modules);
+ final DmfDownloadAndUpdateRequest downloadAndUpdateRequest = createDownloadAndUpdateRequest(target,
+ action.getId(), modules);
final Message message = getMessageConverter().toMessage(downloadAndUpdateRequest,
createConnectorMessagePropertiesEvent(tenant, target.getControllerId(), getEventTypeForTarget(action)));
amqpSenderService.sendMessage(message, targetAddress);
@@ -445,6 +447,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
amqpSoftwareModule.setModuleId(entry.getKey().getId());
amqpSoftwareModule.setModuleType(entry.getKey().getType().getKey());
amqpSoftwareModule.setModuleVersion(entry.getKey().getVersion());
+ amqpSoftwareModule.setEncrypted(entry.getKey().isEncrypted() ? Boolean.TRUE : null);
amqpSoftwareModule.setArtifacts(convertArtifacts(target, entry.getKey().getArtifacts()));
if (!CollectionUtils.isEmpty(entry.getValue())) {
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
index df894849f..b123ee483 100644
--- 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
@@ -150,8 +150,8 @@ public class AmqpControllerAuthenticationTest {
authenticationManager.postConstruct();
- testArtifact = new JpaArtifact(SHA1, "afilename", new JpaSoftwareModule(
- new JpaSoftwareModuleType("a key", "a name", null, 1), "a name", null, null, null));
+ 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,
@@ -260,7 +260,7 @@ public class AmqpControllerAuthenticationTest {
when(tenantConfigurationManagementMock.getConfigurationValue(
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
- .thenReturn(CONFIG_VALUE_TRUE);
+ .thenReturn(CONFIG_VALUE_TRUE);
when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
diff --git a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfSoftwareModule.java b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfSoftwareModule.java
index 63f285d50..995b3bc4d 100644
--- a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfSoftwareModule.java
+++ b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfSoftwareModule.java
@@ -32,6 +32,8 @@ public class DmfSoftwareModule {
@JsonProperty
private String moduleVersion;
@JsonProperty
+ private Boolean encrypted;
+ @JsonProperty
private List artifacts;
@JsonProperty
private List metadata;
@@ -83,11 +85,18 @@ public class DmfSoftwareModule {
}
}
+ public Boolean getEncrypted() {
+ return encrypted;
+ }
+
+ public void setEncrypted(final Boolean encrypted) {
+ this.encrypted = encrypted;
+ }
+
@Override
public String toString() {
return String.format(
- "DmfSoftwareModule [moduleId=%d, moduleType='%s', moduleVersion='%s', artifacts=%s, metadata=%s]",
- moduleId, moduleType, moduleVersion, artifacts, metadata);
+ "DmfSoftwareModule [moduleId=%d, moduleType='%s', moduleVersion='%s', encrypted='%s' artifacts=%s, metadata=%s]",
+ moduleId, moduleType, moduleVersion, encrypted, artifacts, metadata);
}
-
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactEncryption.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactEncryption.java
new file mode 100644
index 000000000..febad31c9
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactEncryption.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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.repository;
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.hawkbit.repository.exception.ArtifactEncryptionFailedException;
+
+/**
+ * Interface definition for artifact encryption.
+ *
+ */
+public interface ArtifactEncryption {
+
+ /**
+ * Defines the required secret keys for particular encryption algorithm.
+ *
+ * @return list of required secret keys
+ */
+ Set requiredSecretKeys();
+
+ /**
+ * Generates required secrets key/value pairs.
+ *
+ * @return secrets key/value pairs
+ * @throws ArtifactEncryptionFailedException
+ */
+ Map generateSecrets();
+
+ /**
+ * Encrypts artifact stream with provided secrets.
+ *
+ * @param secrets
+ * secrets key/value pairs to be used for encryption
+ * @param stream
+ * artifact stream to encrypt
+ * @return encrypted input stream
+ * @throws ArtifactEncryptionFailedException
+ */
+ InputStream encryptStream(final Map secrets, final InputStream stream);
+
+ /**
+ * Decrypts encrypted artifact stream based on provided secrets.
+ *
+ * @param secrets
+ * secrets key/value pairs to be used for decryption
+ * @param stream
+ * artifact stream to decrypt
+ * @return decrypted input stream
+ * @throws ArtifactEncryptionFailedException
+ */
+ InputStream decryptStream(final Map secrets, final InputStream stream);
+
+ /**
+ * Size of the underlying encryption algorithm overhead in bytes
+ *
+ * @return encryption overhead in byte
+ */
+ int encryptionSizeOverhead();
+}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactEncryptionSecretsStore.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactEncryptionSecretsStore.java
new file mode 100644
index 000000000..dcbeb1a87
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactEncryptionSecretsStore.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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.repository;
+
+import java.util.Optional;
+
+import org.eclipse.hawkbit.repository.model.SoftwareModule;
+
+/**
+ * Interface definition for artifact encryption secrets store.
+ *
+ */
+public interface ArtifactEncryptionSecretsStore {
+
+ /**
+ * Adds secret key/value pair associated with particular
+ * {@link SoftwareModule} id to the store.
+ *
+ * @param softwareModuleId
+ * {@link SoftwareModule} id associated with the secret
+ * @param secretKey
+ * key of the secret
+ * @param secretValue
+ * value of the secret
+ */
+ void addSecret(final long softwareModuleId, final String secretKey, final String secretValue);
+
+ /**
+ * Checks if secret is present for particular {@link SoftwareModule} id and
+ * key in the store.
+ *
+ * @param softwareModuleId
+ * {@link SoftwareModule} id associated with the secret
+ * @param secretKey
+ * key of the secret
+ */
+ boolean secretExists(final long softwareModuleId, final String secretKey);
+
+ /**
+ * Retrieves secret value associated with particular {@link SoftwareModule}
+ * id and key from the store.
+ *
+ * @param softwareModuleId
+ * {@link SoftwareModule} id associated with the secret
+ * @param secretKey
+ * key of the secret
+ */
+ Optional getSecret(final long softwareModuleId, final String secretKey);
+
+ /**
+ * Removes secret key/value pair associated with particular
+ * {@link SoftwareModule} id from the store.
+ *
+ * @param softwareModuleId
+ * {@link SoftwareModule} id associated with the secret
+ * @param secretKey
+ * key of the secret
+ */
+ void removeSecret(final long softwareModuleId, final String secretKey);
+}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactEncryptionService.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactEncryptionService.java
new file mode 100644
index 000000000..81b12b629
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactEncryptionService.java
@@ -0,0 +1,127 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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.repository;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import org.eclipse.hawkbit.repository.exception.ArtifactEncryptionUnsupportedException;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Service responsible for encryption operations.
+ *
+ */
+public final class ArtifactEncryptionService {
+
+ private static final ArtifactEncryptionService SINGLETON = new ArtifactEncryptionService();
+
+ @Autowired(required = false)
+ private ArtifactEncryption artifactEncryption;
+
+ @Autowired(required = false)
+ private ArtifactEncryptionSecretsStore artifactEncryptionSecretsStore;
+
+ private ArtifactEncryptionService() {
+ }
+
+ /**
+ * @return the artifact encryption service singleton instance
+ */
+ public static ArtifactEncryptionService getInstance() {
+ return SINGLETON;
+ }
+
+ /**
+ * Checks if required encryption and secrets store beans are present.
+ *
+ * @return if encryption is supported
+ */
+ public boolean isEncryptionSupported() {
+ return artifactEncryption != null && artifactEncryptionSecretsStore != null;
+ }
+
+ /**
+ * Generates encryption secrets and saves them in secret store by software
+ * module id reference.
+ *
+ * @param smId
+ * software module id
+ */
+ public void addSoftwareModuleEncryptionSecrets(final long smId) {
+ if (!isEncryptionSupported()) {
+ throw new ArtifactEncryptionUnsupportedException("Encryption secrets generation is not supported.");
+ }
+
+ final Map secrets = artifactEncryption.generateSecrets();
+ secrets.forEach((key, value) -> artifactEncryptionSecretsStore.addSecret(smId, key, value));
+ // we want to clear secrets from memory as soon as possible
+ secrets.clear();
+ }
+
+ /**
+ * Encrypts artifact stream using the keys retrieved from secrets store by
+ * software module id reference.
+ *
+ * @param smId
+ * software module id
+ * @param artifactStream
+ * artifact stream to encrypt
+ * @return encrypted input stream
+ */
+ public InputStream encryptSoftwareModuleArtifact(final long smId, final InputStream artifactStream) {
+ if (!isEncryptionSupported()) {
+ throw new ArtifactEncryptionUnsupportedException("Artifact encryption is not supported.");
+ }
+
+ return artifactEncryption.encryptStream(getSoftwareModuleEncryptionSecrets(smId), artifactStream);
+ }
+
+ private Map getSoftwareModuleEncryptionSecrets(final long smId) {
+ final Set requiredSecretsKeys = artifactEncryption.requiredSecretKeys();
+ final Map requiredSecrets = new HashMap<>();
+ for (final String requiredSecretsKey : requiredSecretsKeys) {
+ final Optional requiredSecretsValue = artifactEncryptionSecretsStore.getSecret(smId,
+ requiredSecretsKey);
+ requiredSecretsValue.ifPresent(secretValue -> requiredSecrets.put(requiredSecretsKey, secretValue));
+ }
+
+ return requiredSecrets;
+ }
+
+ /**
+ * Decrypts artifact stream using the keys retrieved from secrets store by
+ * software module id reference.
+ *
+ * @param smId
+ * software module id
+ * @param encryptedArtifactStream
+ * artifact stream to decrypt
+ * @return decrypted input stream
+ */
+ public InputStream decryptSoftwareModuleArtifact(final long smId, final InputStream encryptedArtifactStream) {
+ if (!isEncryptionSupported()) {
+ throw new ArtifactEncryptionUnsupportedException("Artifact decryption is not supported.");
+ }
+
+ return artifactEncryption.decryptStream(getSoftwareModuleEncryptionSecrets(smId), encryptedArtifactStream);
+ }
+
+ /**
+ * Size of the underlying encryption algorithm overhead in bytes
+ *
+ * @return encryption overhead in byte
+ */
+ public int encryptionSizeOverhead() {
+ return artifactEncryption.encryptionSizeOverhead();
+ }
+}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java
index bb0b5f360..2f7adbcb4 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java
@@ -15,7 +15,7 @@ import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
-import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
+import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
@@ -69,8 +69,8 @@ public interface ArtifactManagement {
/**
* Garbage collects artifact binaries if only referenced by given
- * {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are marked
- * as deleted.
+ * {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are
+ * marked as deleted.
*
*
* @param artifactSha1Hash
@@ -161,15 +161,20 @@ public interface ArtifactManagement {
Page findBySoftwareModule(@NotNull Pageable pageReq, long swId);
/**
- * Loads {@link AbstractDbArtifact} from store for given {@link Artifact}.
+ * Loads {@link DbArtifact} from store for given {@link Artifact}.
*
* @param sha1Hash
* to search for
- * @return loaded {@link AbstractDbArtifact}
+ * @param softwareModuleId
+ * software module id.
+ * @param isEncrypted
+ * flag to indicate if artifact is encrypted.
+ * @return loaded {@link DbArtifact}
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
- Optional loadArtifactBinary(@NotEmpty String sha1Hash);
+ Optional loadArtifactBinary(@NotEmpty String sha1Hash, long softwareModuleId,
+ final boolean isEncrypted);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleCreate.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleCreate.java
index edba33722..552c41855 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleCreate.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/SoftwareModuleCreate.java
@@ -70,6 +70,13 @@ public interface SoftwareModuleCreate {
return type(Optional.ofNullable(type).map(SoftwareModuleType::getKey).orElse(null));
}
+ /**
+ * @param encrypted
+ * if should be encrypted
+ * @return updated builder instance
+ */
+ SoftwareModuleCreate encrypted(boolean encrypted);
+
/**
* @return peek on current state of {@link SoftwareModule} in the builder
*/
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/ArtifactEncryptionFailedException.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/ArtifactEncryptionFailedException.java
new file mode 100644
index 000000000..df2e1b04f
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/ArtifactEncryptionFailedException.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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.repository.exception;
+
+import org.eclipse.hawkbit.exception.AbstractServerRtException;
+import org.eclipse.hawkbit.exception.SpServerError;
+
+/**
+ * Exception being thrown in case of error while generating encryption secrets,
+ * encrypting or decrypting artifacts.
+ */
+public final class ArtifactEncryptionFailedException extends AbstractServerRtException {
+
+ private static final long serialVersionUID = 1L;
+
+ private final EncryptionOperation encryptionOperation;
+
+ public ArtifactEncryptionFailedException(final EncryptionOperation encryptionOperation) {
+ this(encryptionOperation, null, null);
+ }
+
+ public ArtifactEncryptionFailedException(final EncryptionOperation encryptionOperation, final String message) {
+ this(encryptionOperation, message, null);
+ }
+
+ public ArtifactEncryptionFailedException(final EncryptionOperation encryptionOperation, final String message,
+ final Throwable cause) {
+ super(message, SpServerError.SP_ARTIFACT_ENCRYPTION_FAILED, cause);
+ this.encryptionOperation = encryptionOperation;
+ }
+
+ public EncryptionOperation getEncryptionOperation() {
+ return encryptionOperation;
+ }
+
+ /**
+ * Encryption operation that caused the exception.
+ */
+ public enum EncryptionOperation {
+ GENERATE_SECRETS, ENCRYPT, DECRYPT;
+ }
+
+}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/ArtifactEncryptionUnsupportedException.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/ArtifactEncryptionUnsupportedException.java
new file mode 100644
index 000000000..30a2caa6a
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/ArtifactEncryptionUnsupportedException.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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.repository.exception;
+
+import org.eclipse.hawkbit.exception.AbstractServerRtException;
+import org.eclipse.hawkbit.exception.SpServerError;
+
+/**
+ * Exception being thrown when artifact encryption is not supported
+ */
+public final class ArtifactEncryptionUnsupportedException extends AbstractServerRtException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ */
+ public ArtifactEncryptionUnsupportedException() {
+ super(SpServerError.SP_ARTIFACT_ENCRYPTION_NOT_SUPPORTED);
+ }
+
+ /**
+ * @param message
+ * of the error
+ */
+ public ArtifactEncryptionUnsupportedException(final String message) {
+ super(message, SpServerError.SP_ARTIFACT_ENCRYPTION_NOT_SUPPORTED);
+ }
+}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java
index 8ab2ecfa8..e00086a2d 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java
@@ -67,4 +67,9 @@ public interface SoftwareModule extends NamedVersionedEntity {
*/
List getAssignedTo();
+ /**
+ * @return {@code true} if this software module is marked as encrypted
+ * otherwise {@code false}
+ */
+ boolean isEncrypted();
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/test/java/org/eclipse/hawkbit/repository/ArtifactEncryptionServiceTest.java b/hawkbit-repository/hawkbit-repository-api/src/test/java/org/eclipse/hawkbit/repository/ArtifactEncryptionServiceTest.java
new file mode 100644
index 000000000..682dca590
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-api/src/test/java/org/eclipse/hawkbit/repository/ArtifactEncryptionServiceTest.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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.repository;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+import org.eclipse.hawkbit.repository.exception.ArtifactEncryptionUnsupportedException;
+import org.junit.jupiter.api.Test;
+
+import io.qameta.allure.Description;
+import io.qameta.allure.Feature;
+import io.qameta.allure.Story;
+
+/**
+ * Test class to verify that no {@link ArtifactEncryptionService} required beans
+ * are loaded and therefore the encryption support is not given.
+ */
+@Feature("Unit Tests - Repository")
+@Story("Artifact Encryption Service")
+class ArtifactEncryptionServiceTest {
+
+ @Test
+ @Description("Verify that no artifact encryption support is given")
+ void verifyNoArtifactEncryptionSupport() {
+ final ArtifactEncryptionService artifactEncryptionService = ArtifactEncryptionService.getInstance();
+
+ assertThat(artifactEncryptionService.isEncryptionSupported()).isFalse();
+ assertThatExceptionOfType(ArtifactEncryptionUnsupportedException.class)
+ .isThrownBy(() -> artifactEncryptionService.addSoftwareModuleEncryptionSecrets(1L));
+ assertThatExceptionOfType(ArtifactEncryptionUnsupportedException.class)
+ .isThrownBy(() -> artifactEncryptionService.encryptSoftwareModuleArtifact(1L, null));
+ assertThatExceptionOfType(ArtifactEncryptionUnsupportedException.class)
+ .isThrownBy(() -> artifactEncryptionService.decryptSoftwareModuleArtifact(1L, null));
+ }
+}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/EncryptionAwareDbArtifact.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/EncryptionAwareDbArtifact.java
new file mode 100644
index 000000000..0c41dd5b1
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/EncryptionAwareDbArtifact.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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.repository.jpa;
+
+import java.io.InputStream;
+import java.util.function.UnaryOperator;
+
+import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
+import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
+
+/**
+ * {@link DbArtifact} implementation that decrypts the underlying artifact
+ * binary input stream.
+ */
+public class EncryptionAwareDbArtifact implements DbArtifact {
+
+ private final DbArtifact encryptedDbArtifact;
+ private final UnaryOperator decryptionFunction;
+ private final int encryptionOverhead;
+
+ public EncryptionAwareDbArtifact(final DbArtifact encryptedDbArtifact,
+ final UnaryOperator decryptionFunction) {
+ this.encryptedDbArtifact = encryptedDbArtifact;
+ this.decryptionFunction = decryptionFunction;
+ this.encryptionOverhead = 0;
+ }
+
+ public EncryptionAwareDbArtifact(final DbArtifact encryptedDbArtifact,
+ final UnaryOperator decryptionFunction, final int encryptionOverhead) {
+ this.encryptedDbArtifact = encryptedDbArtifact;
+ this.decryptionFunction = decryptionFunction;
+ this.encryptionOverhead = encryptionOverhead;
+ }
+
+ @Override
+ public String getArtifactId() {
+ return encryptedDbArtifact.getArtifactId();
+ }
+
+ @Override
+ public DbArtifactHash getHashes() {
+ return encryptedDbArtifact.getHashes();
+ }
+
+ @Override
+ public long getSize() {
+ return encryptedDbArtifact.getSize() - encryptionOverhead;
+ }
+
+ @Override
+ public String getContentType() {
+ return encryptedDbArtifact.getContentType();
+ }
+
+ @Override
+ public InputStream getFileInputStream() {
+ return decryptionFunction.apply(encryptedDbArtifact.getFileInputStream());
+ }
+}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java
index fcbd25d5d..68e0b36a0 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java
@@ -16,7 +16,9 @@ import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.eclipse.hawkbit.artifact.repository.ArtifactStoreException;
import org.eclipse.hawkbit.artifact.repository.HashNotMatchException;
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
+import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
+import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
@@ -104,15 +106,24 @@ public class JpaArtifactManagement implements ArtifactManagement {
assertArtifactQuota(moduleId, 1);
- final AbstractDbArtifact artifact = storeArtifact(artifactUpload);
+ final AbstractDbArtifact artifact = storeArtifact(artifactUpload, softwareModule.isEncrypted());
return storeArtifactMetadata(softwareModule, filename, artifact, existing);
}
- private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload) {
- try (final InputStream quotaStream = wrapInQuotaStream(artifactUpload.getInputStream())) {
- return artifactRepository.store(tenantAware.getCurrentTenant(), quotaStream, artifactUpload.getFilename(),
- artifactUpload.getContentType(), new DbArtifactHash(artifactUpload.getProvidedSha1Sum(),
- artifactUpload.getProvidedMd5Sum(), artifactUpload.getProvidedSha256Sum()));
+ private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload, final boolean isSmEncrypted) {
+ final String tenant = tenantAware.getCurrentTenant();
+ final long smId = artifactUpload.getModuleId();
+ final InputStream stream = artifactUpload.getInputStream();
+ final String fileName = artifactUpload.getFilename();
+ final String contentType = artifactUpload.getContentType();
+ final String providedSha1 = artifactUpload.getProvidedSha1Sum();
+ final String providedMd5 = artifactUpload.getProvidedMd5Sum();
+ final String providedSha256 = artifactUpload.getProvidedSha256Sum();
+
+ try (final InputStream wrappedStream = wrapInQuotaStream(
+ isSmEncrypted ? wrapInEncryptionStream(smId, stream) : stream)) {
+ return artifactRepository.store(tenant, wrappedStream, fileName, contentType,
+ new DbArtifactHash(providedSha1, providedMd5, providedSha256));
} catch (final ArtifactStoreException | IOException e) {
throw new ArtifactUploadFailedException(e);
} catch (final HashNotMatchException e) {
@@ -126,6 +137,10 @@ public class JpaArtifactManagement implements ArtifactManagement {
}
}
+ private InputStream wrapInEncryptionStream(final long smId, final InputStream stream) {
+ return ArtifactEncryptionService.getInstance().encryptSoftwareModuleArtifact(smId, stream);
+ }
+
private void assertArtifactQuota(final long id, final int requested) {
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxArtifactsPerSoftwareModule(),
Artifact.class, SoftwareModule.class, localArtifactRepository::countBySoftwareModuleId);
@@ -212,10 +227,26 @@ public class JpaArtifactManagement implements ArtifactManagement {
}
@Override
- public Optional loadArtifactBinary(final String sha1Hash) {
- return Optional.ofNullable(artifactRepository.existsByTenantAndSha1(tenantAware.getCurrentTenant(), sha1Hash)
- ? artifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), sha1Hash)
- : null);
+ public Optional loadArtifactBinary(final String sha1Hash, final long softwareModuleId,
+ final boolean isEncrypted) {
+ final String tenant = tenantAware.getCurrentTenant();
+ if (artifactRepository.existsByTenantAndSha1(tenant, sha1Hash)) {
+ final DbArtifact dbArtifact = artifactRepository.getArtifactBySha1(tenant, sha1Hash);
+ return Optional.ofNullable(
+ isEncrypted ? wrapInEncryptionAwareDbArtifact(softwareModuleId, dbArtifact) : dbArtifact);
+ }
+
+ return Optional.empty();
+ }
+
+ private final DbArtifact wrapInEncryptionAwareDbArtifact(final long smId, final DbArtifact dbArtifact) {
+ if (dbArtifact == null) {
+ return null;
+ }
+ final ArtifactEncryptionService encryptionService = ArtifactEncryptionService.getInstance();
+ return new EncryptionAwareDbArtifact(dbArtifact,
+ stream -> encryptionService.decryptSoftwareModuleArtifact(smId, stream),
+ encryptionService.encryptionSizeOverhead());
}
private Artifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename,
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java
index 1352c675f..93a755f18 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java
@@ -31,6 +31,7 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
+import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants;
@@ -155,7 +156,14 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
public SoftwareModule create(final SoftwareModuleCreate c) {
final JpaSoftwareModuleCreate create = (JpaSoftwareModuleCreate) c;
- return softwareModuleRepository.save(create.build());
+ final JpaSoftwareModule sm = softwareModuleRepository.save(create.build());
+ if (create.isEncrypted()) {
+ // flush sm creation in order to get an Id
+ entityManager.flush();
+ ArtifactEncryptionService.getInstance().addSoftwareModuleEncryptionSecrets(sm.getId());
+ }
+
+ return sm;
}
@Override
@@ -295,8 +303,8 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Override
public Page findByRsql(final Pageable pageable, final String rsqlParam) {
- final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleFields.class,
- virtualPropertyReplacer, database);
+ final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam,
+ SoftwareModuleFields.class, virtualPropertyReplacer, database);
return convertSmPage(softwareModuleRepository.findAll(spec, pageable), pageable);
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java
index 66ae45bf4..5a887bac2 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java
@@ -16,6 +16,9 @@ import javax.persistence.EntityManager;
import javax.sql.DataSource;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
+import org.eclipse.hawkbit.repository.ArtifactEncryption;
+import org.eclipse.hawkbit.repository.ArtifactEncryptionSecretsStore;
+import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.BaseRepositoryTypeProvider;
import org.eclipse.hawkbit.repository.ControllerManagement;
@@ -245,7 +248,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* @param dsTypeManagement
- * for loading {@link TargetType#getCompatibleDistributionSetTypes()}
+ * for loading
+ * {@link TargetType#getCompatibleDistributionSetTypes()}
* @return TargetTypeBuilder bean
*/
@Bean
@@ -261,8 +265,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* @param softwareManagement
- * for loading {@link DistributionSetType#getMandatoryModuleTypes()}
- * and {@link DistributionSetType#getOptionalModuleTypes()}
+ * for loading
+ * {@link DistributionSetType#getMandatoryModuleTypes()} and
+ * {@link DistributionSetType#getOptionalModuleTypes()}
* @return DistributionSetTypeBuilder bean
*/
@Bean
@@ -304,8 +309,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* @return the {@link SystemSecurityContext} singleton bean which make it
- * accessible in beans which cannot access the service directly, e.g.
- * JPA entities.
+ * accessible in beans which cannot access the service directly,
+ * e.g. JPA entities.
*/
@Bean
SystemSecurityContextHolder systemSecurityContextHolder() {
@@ -313,9 +318,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
}
/**
- * @return the {@link TenantConfigurationManagement} singleton bean which make
- * it accessible in beans which cannot access the service directly, e.g.
- * JPA entities.
+ * @return the {@link TenantConfigurationManagement} singleton bean which
+ * make it accessible in beans which cannot access the service
+ * directly, e.g. JPA entities.
*/
@Bean
TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
@@ -324,8 +329,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* @return the {@link SystemManagementHolder} singleton bean which holds the
- * current {@link SystemManagement} service and make it accessible in
- * beans which cannot access the service directly, e.g. JPA entities.
+ * current {@link SystemManagement} service and make it accessible
+ * in beans which cannot access the service directly, e.g. JPA
+ * entities.
*/
@Bean
SystemManagementHolder systemManagementHolder() {
@@ -333,9 +339,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
}
/**
- * @return the {@link TenantAwareHolder} singleton bean which holds the current
- * {@link TenantAware} service and make it accessible in beans which
- * cannot access the service directly, e.g. JPA entities.
+ * @return the {@link TenantAwareHolder} singleton bean which holds the
+ * current {@link TenantAware} service and make it accessible in
+ * beans which cannot access the service directly, e.g. JPA
+ * entities.
*/
@Bean
TenantAwareHolder tenantAwareHolder() {
@@ -343,9 +350,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
}
/**
- * @return the {@link SecurityTokenGeneratorHolder} singleton bean which holds
- * the current {@link SecurityTokenGenerator} service and make it
- * accessible in beans which cannot access the service via injection
+ * @return the {@link SecurityTokenGeneratorHolder} singleton bean which
+ * holds the current {@link SecurityTokenGenerator} service and make
+ * it accessible in beans which cannot access the service via
+ * injection
*/
@Bean
SecurityTokenGeneratorHolder securityTokenGeneratorHolder() {
@@ -940,7 +948,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
}
/**
- * Our default {@link BaseRepositoryTypeProvider} bean always provides the NoCountBaseRepository
+ * Our default {@link BaseRepositoryTypeProvider} bean always provides the
+ * NoCountBaseRepository
*
* @return a {@link BaseRepositoryTypeProvider} bean
*/
@@ -950,4 +959,17 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
return new NoCountBaseRepositoryTypeProvider();
}
+
+ /**
+ * Default artifact encryption service bean that internally uses
+ * {@link ArtifactEncryption} and {@link ArtifactEncryptionSecretsStore}
+ * beans for {@link SoftwareModule} artifacts encryption/decryption
+ *
+ * @return a {@link ArtifactEncryptionService} bean
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ ArtifactEncryptionService artifactEncryptionService() {
+ return ArtifactEncryptionService.getInstance();
+ }
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleCreate.java
index 964aee9bc..377e1cd36 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaSoftwareModuleCreate.java
@@ -26,13 +26,26 @@ public class JpaSoftwareModuleCreate extends AbstractSoftwareModuleUpdateCreate<
private final SoftwareModuleTypeManagement softwareModuleTypeManagement;
+ private boolean encrypted;
+
JpaSoftwareModuleCreate(final SoftwareModuleTypeManagement softwareModuleTypeManagement) {
this.softwareModuleTypeManagement = softwareModuleTypeManagement;
}
+ @Override
+ public SoftwareModuleCreate encrypted(final boolean encrypted) {
+ this.encrypted = encrypted;
+ return this;
+ }
+
+ public boolean isEncrypted() {
+ return encrypted;
+ }
+
@Override
public JpaSoftwareModule build() {
- return new JpaSoftwareModule(getSoftwareModuleTypeFromKeyString(type), name, version, description, vendor);
+ return new JpaSoftwareModule(getSoftwareModuleTypeFromKeyString(type), name, version, description, vendor,
+ encrypted);
}
private SoftwareModuleType getSoftwareModuleTypeFromKeyString(final String type) {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java
index 33622a14a..c3f172304 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java
@@ -90,6 +90,9 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
@OneToMany(mappedBy = "softwareModule", fetch = FetchType.LAZY, targetEntity = JpaSoftwareModuleMetadata.class)
private List metadata;
+ @Column(name = "encrypted")
+ private boolean encrypted;
+
/**
* Default constructor.
*/
@@ -97,6 +100,20 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
// Default constructor for JPA
}
+ /**
+ * parameterized constructor.
+ *
+ * @param type
+ * of the {@link SoftwareModule}
+ * @param name
+ * abstract name of the {@link SoftwareModule}
+ * @param version
+ * of the {@link SoftwareModule}
+ */
+ public JpaSoftwareModule(final SoftwareModuleType type, final String name, final String version) {
+ this(type, name, version, null, null, false);
+ }
+
/**
* parameterized constructor.
*
@@ -110,12 +127,15 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
* of the {@link SoftwareModule}
* @param vendor
* of the {@link SoftwareModule}
+ * @param encrypted
+ * encryption flag of the {@link SoftwareModule}
*/
public JpaSoftwareModule(final SoftwareModuleType type, final String name, final String version,
- final String description, final String vendor) {
+ final String description, final String vendor, final boolean encrypted) {
super(name, version, description);
this.vendor = vendor;
this.type = (JpaSoftwareModuleType) type;
+ this.encrypted = encrypted;
}
public void addArtifact(final Artifact artifact) {
@@ -175,8 +195,8 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
* Marks or un-marks this software module as deleted.
*
* @param deleted
- * {@code true} if the software module should be marked as
- * deleted otherwise {@code false}
+ * {@code true} if the software module should be marked as deleted
+ * otherwise {@code false}
*/
public void setDeleted(final boolean deleted) {
this.deleted = deleted;
@@ -186,10 +206,27 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
this.type = type;
}
+ @Override
+ public boolean isEncrypted() {
+ return encrypted;
+ }
+
+ /**
+ * Marks this software module as encrypted.
+ *
+ * @param encrypted
+ * {@code true} if the software module should be marked as encrypted
+ * otherwise {@code false}
+ */
+ public void setEncrypted(final boolean encrypted) {
+ this.encrypted = encrypted;
+ }
+
@Override
public String toString() {
- return "SoftwareModule [deleted=" + deleted + ", name=" + getName() + ", version=" + getVersion()
- + ", revision=" + getOptLockRevision() + ", Id=" + getId() + ", type=" + getType().getName() + "]";
+ return "SoftwareModule [deleted=" + isDeleted() + ", encrypted=" + isEncrypted() + ", name=" + getName()
+ + ", version=" + getVersion() + ", revision=" + getOptLockRevision() + ", Id=" + getId() + ", type="
+ + getType().getName() + "]";
}
@Override
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_20__add_encryption_flag_to_sm___DB2.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_20__add_encryption_flag_to_sm___DB2.sql
new file mode 100644
index 000000000..9249d14a1
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_20__add_encryption_flag_to_sm___DB2.sql
@@ -0,0 +1,3 @@
+ALTER TABLE sp_base_software_module ADD COLUMN encrypted BOOLEAN;
+
+UPDATE sp_base_software_module SET encrypted = 0;
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_20__add_encryption_flag_to_sm___H2.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_20__add_encryption_flag_to_sm___H2.sql
new file mode 100644
index 000000000..9249d14a1
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_20__add_encryption_flag_to_sm___H2.sql
@@ -0,0 +1,3 @@
+ALTER TABLE sp_base_software_module ADD COLUMN encrypted BOOLEAN;
+
+UPDATE sp_base_software_module SET encrypted = 0;
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_20__add_encryption_flag_to_sm___MYSQL.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_20__add_encryption_flag_to_sm___MYSQL.sql
new file mode 100644
index 000000000..9249d14a1
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_20__add_encryption_flag_to_sm___MYSQL.sql
@@ -0,0 +1,3 @@
+ALTER TABLE sp_base_software_module ADD COLUMN encrypted BOOLEAN;
+
+UPDATE sp_base_software_module SET encrypted = 0;
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/POSTGRESQL/V1_12_20__add_encryption_flag_to_sm___POSTGRESQL.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/POSTGRESQL/V1_12_20__add_encryption_flag_to_sm___POSTGRESQL.sql
new file mode 100644
index 000000000..9249d14a1
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/POSTGRESQL/V1_12_20__add_encryption_flag_to_sm___POSTGRESQL.sql
@@ -0,0 +1,3 @@
+ALTER TABLE sp_base_software_module ADD COLUMN encrypted BOOLEAN;
+
+UPDATE sp_base_software_module SET encrypted = 0;
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_19__add_valid_flag_to_ds___SQL_SERVER.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_19__add_valid_flag_to_ds___SQL_SERVER.sql
index 3ab0e7502..1ec1d0407 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_19__add_valid_flag_to_ds___SQL_SERVER.sql
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_19__add_valid_flag_to_ds___SQL_SERVER.sql
@@ -1,3 +1 @@
-ALTER TABLE sp_distribution_set ADD COLUMN valid BOOLEAN;
-
-UPDATE sp_distribution_set SET valid = 1;
\ No newline at end of file
+ALTER TABLE sp_distribution_set ADD valid BIT DEFAULT 1;
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_20__add_encryption_flag_to_sm___SQL_SERVER.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_20__add_encryption_flag_to_sm___SQL_SERVER.sql
new file mode 100644
index 000000000..092fdbdcc
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_20__add_encryption_flag_to_sm___SQL_SERVER.sql
@@ -0,0 +1 @@
+ALTER TABLE sp_base_software_module ADD encrypted BIT DEFAULT 0;
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java
index b92565d71..c8b945f62 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java
@@ -26,7 +26,7 @@ import javax.validation.ConstraintViolationException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
-import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
+import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.ArtifactManagement;
@@ -77,7 +77,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
.isFalse();
assertThat(artifactManagement.findFirstBySHA1(NOT_EXIST_ID)).isNotPresent();
- assertThat(artifactManagement.loadArtifactBinary(NOT_EXIST_ID)).isNotPresent();
+ assertThat(artifactManagement.loadArtifactBinary(NOT_EXIST_ID, module.getId(), module.isEncrypted()))
+ .isNotPresent();
}
@Test
@@ -116,10 +117,10 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
assertThat(artifactRepository.findAll()).hasSize(0);
final JpaSoftwareModule sm = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
+ .save(new JpaSoftwareModule(osType, "name 1", "version 1"));
final JpaSoftwareModule sm2 = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
- softwareModuleRepository.save(new JpaSoftwareModule(osType, "name 3", "version 3", null, null));
+ .save(new JpaSoftwareModule(osType, "name 2", "version 2"));
+ softwareModuleRepository.save(new JpaSoftwareModule(osType, "name 3", "version 3"));
final int artifactSize = 5 * 1024;
final byte[] randomBytes = randomBytes(artifactSize);
@@ -165,8 +166,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
final String artifactData = "test";
final int artifactSize = artifactData.length();
- final long smID = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "smIllegalFilenameTest", "1.0", null, null)).getId();
+ final long smID = softwareModuleRepository.save(new JpaSoftwareModule(osType, "smIllegalFilenameTest", "1.0"))
+ .getId();
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
() -> artifactManagement.create(new ArtifactUpload(IOUtils.toInputStream(artifactData, "UTF-8"), smID,
illegalFilename, false, artifactSize)));
@@ -178,8 +179,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
public void createArtifactsUntilQuotaIsExceeded() throws IOException {
// create a software module
- final long smId = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0", null, null))
- .getId();
+ final long smId = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0")).getId();
// now create artifacts for this module until the quota is exceeded
final long maxArtifacts = quotaManagement.getMaxArtifactsPerSoftwareModule();
@@ -217,14 +217,13 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
final int artifactSize = Math.toIntExact(quotaManagement.getMaxArtifactSize() / 10);
final int numArtifacts = Math.toIntExact(maxBytes / artifactSize);
for (int i = 0; i < numArtifacts; ++i) {
- final JpaSoftwareModule sm = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "smd" + i, "1.0", null, null));
+ final JpaSoftwareModule sm = softwareModuleRepository.save(new JpaSoftwareModule(osType, "smd" + i, "1.0"));
artifactIds.add(createArtifactForSoftwareModule("file" + i, sm.getId(), artifactSize).getId());
}
// upload one more artifact to trigger the quota exceeded error
final JpaSoftwareModule sm = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "smd" + numArtifacts, "1.0", null, null));
+ .save(new JpaSoftwareModule(osType, "smd" + numArtifacts, "1.0"));
assertThatExceptionOfType(StorageQuotaExceededException.class)
.isThrownBy(() -> createArtifactForSoftwareModule("file" + numArtifacts, sm.getId(), artifactSize));
@@ -240,8 +239,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
public void createArtifactFailsIfTooLarge() {
// create a software module
- final JpaSoftwareModule sm1 = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "sm1", "1.0", null, null));
+ final JpaSoftwareModule sm1 = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0"));
// create an artifact that exceeds the configured quota
final long maxSize = quotaManagement.getMaxArtifactSize();
@@ -254,7 +252,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
public void hardDeleteSoftwareModule() throws IOException {
final JpaSoftwareModule sm = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
+ .save(new JpaSoftwareModule(osType, "name 1", "version 1"));
createArtifactForSoftwareModule("file1", sm.getId(), 5 * 1024);
assertThat(artifactRepository.findAll()).hasSize(1);
@@ -273,9 +271,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Description("Tests the deletion of a local artifact including metadata.")
public void deleteArtifact() throws IOException {
final JpaSoftwareModule sm = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
+ .save(new JpaSoftwareModule(osType, "name 1", "version 1"));
final JpaSoftwareModule sm2 = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
+ .save(new JpaSoftwareModule(osType, "name 2", "version 2"));
assertThat(artifactRepository.findAll()).isEmpty();
@@ -326,9 +324,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
public void deleteDuplicateArtifacts() throws IOException {
final JpaSoftwareModule sm = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
+ .save(new JpaSoftwareModule(osType, "name 1", "version 1"));
final JpaSoftwareModule sm2 = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
+ .save(new JpaSoftwareModule(osType, "name 2", "version 2"));
final int artifactSize = 5 * 1024;
final byte[] randomBytes = randomBytes(artifactSize);
@@ -368,9 +366,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
public void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInSameTenants() throws IOException {
final JpaSoftwareModule sm = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
+ .save(new JpaSoftwareModule(osType, "name 1", "version 1"));
final JpaSoftwareModule sm2 = softwareModuleRepository
- .save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
+ .save(new JpaSoftwareModule(osType, "name 2", "version 2"));
final int artifactSize = 5 * 1024;
final byte[] randomBytes = randomBytes(artifactSize);
@@ -382,7 +380,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
final Artifact artifact2 = createArtifactForSoftwareModule("file2", sm2.getId(), artifactSize,
inputStream2);
- assertEqualFileContents(artifactManagement.loadArtifactBinary(artifact2.getSha1Hash()), randomBytes);
+ assertEqualFileContents(
+ artifactManagement.loadArtifactBinary(artifact2.getSha1Hash(), sm2.getId(), sm2.isEncrypted()),
+ randomBytes);
assertThat(artifactRepository.findAll()).hasSize(2);
@@ -447,9 +447,11 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
final int artifactSize = 5 * 1024;
final byte[] randomBytes = randomBytes(artifactSize);
try (final InputStream input = new ByteArrayInputStream(randomBytes)) {
- final Artifact artifact = createArtifactForSoftwareModule("file1",
- testdataFactory.createSoftwareModuleOs().getId(), artifactSize, input);
- assertEqualFileContents(artifactManagement.loadArtifactBinary(artifact.getSha1Hash()), randomBytes);
+ final SoftwareModule smOs = testdataFactory.createSoftwareModuleOs();
+ final Artifact artifact = createArtifactForSoftwareModule("file1", smOs.getId(), artifactSize, input);
+ assertEqualFileContents(
+ artifactManagement.loadArtifactBinary(artifact.getSha1Hash(), smOs.getId(), smOs.isEncrypted()),
+ randomBytes);
}
}
@@ -459,7 +461,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
public void loadArtifactBinaryWithoutDownloadArtifactThrowsPermissionDenied() {
assertThatExceptionOfType(InsufficientPermissionException.class)
.as("Should not have worked with missing permission.")
- .isThrownBy(() -> artifactManagement.loadArtifactBinary("123"));
+ .isThrownBy(() -> artifactManagement.loadArtifactBinary("123", 1, false));
}
@Test
@@ -541,7 +543,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
assertThat(createdArtifact.getSha256Hash()).isEqualTo(artifactHashes.getSha256());
}
- final Optional dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1());
+ final Optional dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1(),
+ sm.getId(), sm.isEncrypted());
assertThat(dbArtifact).isPresent();
}
@@ -561,7 +564,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
final Artifact artifact = artifactManagement.create(artifactUpload);
assertThat(artifact).isNotNull();
}
- final Optional dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1());
+ final Optional dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1(),
+ smOs.getId(), smOs.isEncrypted());
assertThat(dbArtifact).isPresent();
try (final InputStream inputStream = new ByteArrayInputStream(testData)) {
@@ -622,10 +626,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
assertThat(runAsTenant(tenant, () -> artifactRepository.findAll())).hasSize(count);
}
- private void assertEqualFileContents(final Optional artifact, final byte[] randomBytes)
+ private void assertEqualFileContents(final Optional artifact, final byte[] randomBytes)
throws IOException {
- try (final InputStream inputStream = artifactManagement.loadArtifactBinary(artifact.get().getHashes().getSha1())
- .get().getFileInputStream()) {
+ try (final InputStream inputStream = artifact.get().getFileInputStream()) {
assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(randomBytes), inputStream),
"The stored binary matches the given binary");
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleManagementTest.java
index ed93502e6..7713f030c 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleManagementTest.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleManagementTest.java
@@ -520,13 +520,13 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
Arrays.asList(testType.getId()));
// found in test
- final SoftwareModule unassigned = testdataFactory.createSoftwareModule("thetype", "unassignedfound");
- final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound");
- final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound");
- final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "a");
+ final SoftwareModule unassigned = testdataFactory.createSoftwareModule("thetype", "unassignedfound", false);
+ final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound", false);
+ final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound", false);
+ final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "a", false);
// ignored
- final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted");
+ final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted", false);
final SoftwareModule four = testdataFactory.createSoftwareModuleOs("e");
final DistributionSet set = distributionSetManagement
@@ -572,13 +572,13 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
Arrays.asList(testType.getId()));
// found in test
- testdataFactory.createSoftwareModule("thetype", "unassignedfound");
- final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound");
- final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound");
- final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "d");
+ testdataFactory.createSoftwareModule("thetype", "unassignedfound", false);
+ final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound", false);
+ final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound", false);
+ final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "d", false);
// ignored
- final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted");
+ final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted", false);
final SoftwareModule four = testdataFactory.createSoftwareModuleOs("e");
distributionSetManagement
diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java
index 1725a22e7..137ca235f 100644
--- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java
+++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java
@@ -540,7 +540,7 @@ public class TestdataFactory {
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModule(final String typeKey) {
- return createSoftwareModule(typeKey, "");
+ return createSoftwareModule(typeKey, "", false);
}
/**
@@ -552,7 +552,7 @@ public class TestdataFactory {
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModuleApp() {
- return createSoftwareModule(Constants.SMT_DEFAULT_APP_KEY, "");
+ return createSoftwareModule(Constants.SMT_DEFAULT_APP_KEY, "", false);
}
/**
@@ -567,7 +567,7 @@ public class TestdataFactory {
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModuleApp(final String prefix) {
- return createSoftwareModule(Constants.SMT_DEFAULT_APP_KEY, prefix);
+ return createSoftwareModule(Constants.SMT_DEFAULT_APP_KEY, prefix, false);
}
/**
@@ -579,7 +579,7 @@ public class TestdataFactory {
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModuleOs() {
- return createSoftwareModule(Constants.SMT_DEFAULT_OS_KEY, "");
+ return createSoftwareModule(Constants.SMT_DEFAULT_OS_KEY, "", false);
}
/**
@@ -594,7 +594,7 @@ public class TestdataFactory {
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModuleOs(final String prefix) {
- return createSoftwareModule(Constants.SMT_DEFAULT_OS_KEY, prefix);
+ return createSoftwareModule(Constants.SMT_DEFAULT_OS_KEY, prefix, false);
}
/**
@@ -609,10 +609,10 @@ public class TestdataFactory {
*
* @return persisted {@link SoftwareModule}.
*/
- public SoftwareModule createSoftwareModule(final String typeKey, final String prefix) {
+ public SoftwareModule createSoftwareModule(final String typeKey, final String prefix, final boolean encrypted) {
return softwareModuleManagement.create(entityFactory.softwareModule().create()
.type(findOrCreateSoftwareModuleType(typeKey)).name(prefix + typeKey).version(prefix + DEFAULT_VERSION)
- .description(LOREM.words(10)).vendor(DEFAULT_VENDOR));
+ .description(LOREM.words(10)).vendor(DEFAULT_VENDOR).encrypted(encrypted));
}
/**
diff --git a/hawkbit-rest/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiChunk.java b/hawkbit-rest/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiChunk.java
index 45c4ae73c..760104ac9 100644
--- a/hawkbit-rest/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiChunk.java
+++ b/hawkbit-rest/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiChunk.java
@@ -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 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 artifacts,
- final List metadata) {
+ public DdiChunk(final String part, final String version, final String name, final Boolean encrypted,
+ final List artifacts, final List 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 getArtifacts() {
if (artifacts == null) {
return Collections.emptyList();
diff --git a/hawkbit-rest/hawkbit-ddi-api/src/test/java/org/eclipse/hawkbit/ddi/json/model/DdiChunkTest.java b/hawkbit-rest/hawkbit-ddi-api/src/test/java/org/eclipse/hawkbit/ddi/json/model/DdiChunkTest.java
index e2b17707a..bb928337e 100644
--- a/hawkbit-rest/hawkbit-ddi-api/src/test/java/org/eclipse/hawkbit/ddi/json/model/DdiChunkTest.java
+++ b/hawkbit-rest/hawkbit-ddi-api/src/test/java/org/eclipse/hawkbit/ddi/json/model/DdiChunkTest.java
@@ -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 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 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));
}
}
\ No newline at end of file
diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java b/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java
index abcb81613..3635cebb6 100644
--- a/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java
+++ b/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java
@@ -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()));
diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java b/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java
index c64e7cc27..23cef1d4c 100644
--- a/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java
+++ b/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java
@@ -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);
diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/softwaremodule/MgmtSoftwareModule.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/softwaremodule/MgmtSoftwareModule.java
index 9eb69006f..fbc09f9b4 100644
--- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/softwaremodule/MgmtSoftwareModule.java
+++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/softwaremodule/MgmtSoftwareModule.java
@@ -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;
+ }
+
}
diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/softwaremodule/MgmtSoftwareModuleRequestBodyPost.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/softwaremodule/MgmtSoftwareModuleRequestBodyPost.java
index e39413915..c3a29d2dc 100644
--- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/softwaremodule/MgmtSoftwareModuleRequestBodyPost.java
+++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/softwaremodule/MgmtSoftwareModuleRequestBodyPost.java
@@ -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;
+ }
+
}
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadArtifactResource.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadArtifactResource.java
index 869149d3d..d1ae84aef 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadArtifactResource.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadArtifactResource.java
@@ -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);
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java
index 027bc9a3a..3bb4ef5ef 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java
@@ -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 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());
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java
index 268005e37..45494654b 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java
@@ -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 = "
.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());
diff --git a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java
index ffc6f629d..30f152dbf 100644
--- a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java
+++ b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java
@@ -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);
diff --git a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java
index 98ea2454a..6d06a13e4 100644
--- a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java
+++ b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java
@@ -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 writeFileResponse(final AbstractDbArtifact artifact,
- final String filename, final long lastModified, final HttpServletResponse response,
- final HttpServletRequest request, final FileStreamingProgressListener progressListener) {
+ public static ResponseEntity writeFileResponse(final DbArtifact artifact, final String filename,
+ final long lastModified, final HttpServletResponse response, final HttpServletRequest request,
+ final FileStreamingProgressListener progressListener) {
ResponseEntity result;
@@ -189,9 +189,9 @@ public final class FileStreamingUtil {
return result;
}
- private static ResponseEntity handleFullFileRequest(final AbstractDbArtifact artifact,
- final String filename, final HttpServletResponse response,
- final FileStreamingProgressListener progressListener, final ByteRange full) {
+ private static ResponseEntity 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 handleMultipartRangeRequest(final AbstractDbArtifact artifact,
+ private static ResponseEntity handleMultipartRangeRequest(final DbArtifact artifact,
final String filename, final HttpServletResponse response,
final FileStreamingProgressListener progressListener, final List ranges) {
@@ -291,7 +291,7 @@ public final class FileStreamingUtil {
return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).build();
}
- private static ResponseEntity handleStandardRangeRequest(final AbstractDbArtifact artifact,
+ private static ResponseEntity handleStandardRangeRequest(final DbArtifact artifact,
final String filename, final HttpServletResponse response,
final FileStreamingProgressListener progressListener, final List ranges) {
final ByteRange r = ranges.get(0);
diff --git a/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java b/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java
index fb613df1b..aa92f5a5e 100644
--- a/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java
+++ b/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java
@@ -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 targets, final boolean withToken, final long targetTypeId) throws JSONException {
+ public static String targets(final List 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);
}
diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java
index 9e091451d..772ced4a9 100644
--- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java
+++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java
@@ -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";
diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java
index efa7fd72c..e4333d7bf 100644
--- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java
+++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java
@@ -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),
diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/MgmtApiModelProperties.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/MgmtApiModelProperties.java
index 0e0a887b2..bc5660da4 100644
--- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/MgmtApiModelProperties.java
+++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/MgmtApiModelProperties.java
@@ -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.";
diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/SoftwaremodulesDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/SoftwaremodulesDocumentationTest.java
index fb3a0273a..041ca5e20 100644
--- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/SoftwaremodulesDocumentationTest.java
+++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/SoftwaremodulesDocumentationTest.java
@@ -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())
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/MgmtUiConfiguration.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/MgmtUiConfiguration.java
index 204fd90bc..7f60b3df3 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/MgmtUiConfiguration.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/MgmtUiConfiguration.java
@@ -18,6 +18,7 @@ import org.eclipse.hawkbit.ui.common.data.suppliers.TargetFilterStateDataSupplie
import org.eclipse.hawkbit.ui.common.data.suppliers.TargetManagementStateDataSupplier;
import org.eclipse.hawkbit.ui.common.data.suppliers.TargetManagementStateDataSupplierImpl;
import org.eclipse.hawkbit.ui.error.HawkbitUIErrorHandler;
+import org.eclipse.hawkbit.ui.error.extractors.ArtifactEncryptionErrorExtractor;
import org.eclipse.hawkbit.ui.error.extractors.ConstraintViolationErrorExtractor;
import org.eclipse.hawkbit.ui.error.extractors.EntityNotFoundErrorExtractor;
import org.eclipse.hawkbit.ui.error.extractors.IncompatibleTargetTypeErrorExtractor;
@@ -182,6 +183,18 @@ public class MgmtUiConfiguration {
return new InvalidDistributionSetErrorExtractor(i18n);
}
+ /**
+ * UI Artifact ecnryption operations Error details extractor bean.
+ *
+ * @param i18n
+ * VaadinMessageSource
+ * @return UI Artifact ecnryption operations Error details extractor
+ */
+ @Bean
+ UiErrorDetailsExtractor artifactEncryptionErrorExtractor(final VaadinMessageSource i18n) {
+ return new ArtifactEncryptionErrorExtractor(i18n);
+ }
+
/**
* Vaadin4Spring servlet bean.
*
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGrid.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGrid.java
index f94eee552..4cd8b4320 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGrid.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGrid.java
@@ -12,7 +12,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
-import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
+import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.ui.common.CommonUiDependencies;
import org.eclipse.hawkbit.ui.common.builder.GridComponentBuilder;
@@ -33,11 +33,14 @@ import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
import org.eclipse.hawkbit.ui.utils.UINotification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.server.FileDownloader;
import com.vaadin.server.StreamResource;
import com.vaadin.ui.Button;
+import com.vaadin.ui.UI;
/**
* Artifact Details grid which is shown on the Upload View.
@@ -45,6 +48,8 @@ import com.vaadin.ui.Button;
public class ArtifactDetailsGrid extends AbstractGrid {
private static final long serialVersionUID = 1L;
+ private static final Logger LOG = LoggerFactory.getLogger(ArtifactDetailsGrid.class);
+
private static final String ARTIFACT_NAME_ID = "artifactName";
private static final String ARTIFACT_SIZE_ID = "artifactSize";
private static final String ARTIFACT_MODIFIED_DATE_ID = "artifactModifiedDate";
@@ -60,6 +65,8 @@ public class ArtifactDetailsGrid extends AbstractGrid {
private final transient DeleteSupport artifactDeleteSupport;
private final transient MasterEntitySupport masterEntitySupport;
+ private boolean artifactsEncrypted;
+
/**
* Constructor
*
@@ -82,7 +89,8 @@ public class ArtifactDetailsGrid extends AbstractGrid {
new FilterSupport<>(new ArtifactDataProvider(artifactManagement, new ArtifactToProxyArtifactMapper())));
initFilterMappings();
- this.masterEntitySupport = new MasterEntitySupport<>(getFilterSupport());
+ this.masterEntitySupport = new MasterEntitySupport<>(getFilterSupport(), null,
+ sm -> artifactsEncrypted = sm != null && sm.isEncrypted());
init();
}
@@ -107,9 +115,8 @@ public class ArtifactDetailsGrid extends AbstractGrid {
.map(ProxyIdentifiableEntity::getId).collect(Collectors.toList());
artifactToBeDeletedIds.forEach(artifactManagement::delete);
- eventBus.publish(EventTopics.ENTITY_MODIFIED, this,
- new EntityModifiedEventPayload(EntityModifiedEventType.ENTITY_UPDATED, ProxySoftwareModule.class,
- getMasterEntitySupport().getMasterId()));
+ eventBus.publish(EventTopics.ENTITY_MODIFIED, this, new EntityModifiedEventPayload(
+ EntityModifiedEventType.ENTITY_UPDATED, ProxySoftwareModule.class, masterEntitySupport.getMasterId()));
return true;
}
@@ -171,12 +178,20 @@ public class ArtifactDetailsGrid extends AbstractGrid {
private void attachFileDownloader(final ProxyArtifact artifact, final Button downloadButton) {
final StreamResource artifactStreamResource = new StreamResource(() -> artifactManagement
- .loadArtifactBinary(artifact.getSha1Hash()).map(AbstractDbArtifact::getFileInputStream).orElse(null),
- artifact.getFilename());
+ .loadArtifactBinary(artifact.getSha1Hash(), masterEntitySupport.getMasterId(), artifactsEncrypted)
+ .map(DbArtifact::getFileInputStream).orElse(null), artifact.getFilename());
final FileDownloader fileDownloader = new FileDownloader(artifactStreamResource);
- fileDownloader.setErrorHandler(event -> notification
- .displayValidationError(i18n.getMessage(UIMessageIdProvider.ARTIFACT_DOWNLOAD_FAILURE_MSG)));
+ fileDownloader.setErrorHandler(event -> {
+ LOG.error("Download failed for artifact with id {}, filename {}", artifact.getId(), artifact.getFilename(),
+ event.getThrowable());
+ notification.displayValidationError(i18n.getMessage(UIMessageIdProvider.ARTIFACT_DOWNLOAD_FAILURE_MSG));
+ UI.getCurrent().access(() -> {
+ // give error details extractors a chance to process specific
+ // error
+ throw new DownloadException(event.getThrowable());
+ });
+ });
fileDownloader.extend(downloadButton);
}
@@ -225,4 +240,12 @@ public class ArtifactDetailsGrid extends AbstractGrid {
public MasterEntitySupport getMasterEntitySupport() {
return masterEntitySupport;
}
+
+ private static class DownloadException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public DownloadException(final Throwable cause) {
+ super(cause);
+ }
+ }
}
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/AddSmWindowController.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/AddSmWindowController.java
index 0b49288f9..6e1688b71 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/AddSmWindowController.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/AddSmWindowController.java
@@ -8,6 +8,7 @@
*/
package org.eclipse.hawkbit.ui.artifacts.smtable;
+import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -62,6 +63,13 @@ public class AddSmWindowController
return layout;
}
+ @Override
+ protected void adaptLayout(final ProxySoftwareModule proxyEntity) {
+ if (!ArtifactEncryptionService.getInstance().isEncryptionSupported()) {
+ layout.disableEncryptionField();
+ }
+ }
+
@Override
protected ProxySoftwareModule buildEntityFromProxy(final ProxySoftwareModule proxyEntity) {
// We ignore the method parameter, because we are interested in the
@@ -73,7 +81,7 @@ public class AddSmWindowController
protected SoftwareModule persistEntityInRepository(final ProxySoftwareModule entity) {
final SoftwareModuleCreate smCreate = getEntityFactory().softwareModule().create()
.type(entity.getTypeInfo().getKey()).name(entity.getName()).version(entity.getVersion())
- .vendor(entity.getVendor()).description(entity.getDescription());
+ .vendor(entity.getVendor()).description(entity.getDescription()).encrypted(entity.isEncrypted());
return smManagement.create(smCreate);
}
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowBuilder.java
index 576b4f04c..0a158b1da 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowBuilder.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowBuilder.java
@@ -60,8 +60,8 @@ public class SmWindowBuilder extends AbstractEntityWindowBuilder smTypeDataProvider = new SoftwareModuleTypeDataProvider<>(
- smTypeManagement, new TypeToTypeInfoMapper());
+ smTypeManagement, new TypeToTypeInfoMapper<>());
this.smComponentBuilder = new SmWindowLayoutComponentBuilder(i18n, smTypeDataProvider);
this.smTypeSelect = smComponentBuilder.createSoftwareModuleTypeCombo(binder);
@@ -55,6 +56,7 @@ public class SmWindowLayout extends AbstractEntityWindowLayout smTypeDataProvider;
@@ -56,7 +58,8 @@ public class SmWindowLayoutComponentBuilder {
*/
public ComboBox createSoftwareModuleTypeCombo(final Binder binder) {
return FormComponentBuilder
- .createTypeCombo(binder, smTypeDataProvider, i18n, UIComponentIdProvider.SW_MODULE_TYPE, true).getComponent();
+ .createTypeCombo(binder, smTypeDataProvider, i18n, UIComponentIdProvider.SW_MODULE_TYPE, true)
+ .getComponent();
}
/**
@@ -113,4 +116,18 @@ public class SmWindowLayoutComponentBuilder {
return FormComponentBuilder
.createDescriptionInput(binder, i18n, UIComponentIdProvider.ADD_SW_MODULE_DESCRIPTION).getComponent();
}
+
+ /**
+ * Create checkbox for artifact encryption
+ *
+ * @param binder
+ * binder the input will be bound to
+ *
+ * @return input component
+ */
+ public CheckBox createArtifactEncryptionCheck(final Binder binder) {
+ return FormComponentBuilder.createCheckBox(i18n.getMessage(ARTIFACT_ENCRYPTION),
+ UIComponentIdProvider.ARTIFACT_ENCRYPTION_ID, binder, ProxySoftwareModule::isEncrypted,
+ ProxySoftwareModule::setEncrypted);
+ }
}
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/UpdateSmWindowController.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/UpdateSmWindowController.java
index ac3ff67a9..11d3f9ef4 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/UpdateSmWindowController.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/UpdateSmWindowController.java
@@ -61,6 +61,7 @@ public class UpdateSmWindowController
sm.setVersion(proxyEntity.getVersion());
sm.setVendor(proxyEntity.getVendor());
sm.setDescription(proxyEntity.getDescription());
+ sm.setEncrypted(proxyEntity.isEncrypted());
nameBeforeEdit = proxyEntity.getName();
versionBeforeEdit = proxyEntity.getVersion();
@@ -78,6 +79,7 @@ public class UpdateSmWindowController
layout.disableSmTypeSelect();
layout.disableNameField();
layout.disableVersionField();
+ layout.disableEncryptionField();
}
@Override
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/AbstractFileTransferHandler.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/AbstractFileTransferHandler.java
index a4334c7f5..b38afd387 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/AbstractFileTransferHandler.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/AbstractFileTransferHandler.java
@@ -18,6 +18,8 @@ import java.util.concurrent.locks.Lock;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.RegexCharacterCollection;
import org.eclipse.hawkbit.repository.RegexCharacterCollection.RegexChar;
+import org.eclipse.hawkbit.repository.exception.ArtifactEncryptionFailedException;
+import org.eclipse.hawkbit.repository.exception.ArtifactEncryptionUnsupportedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.FileSizeQuotaExceededException;
@@ -137,6 +139,10 @@ public abstract class AbstractFileTransferHandler implements Serializable {
interruptUploadAndSetReason(i18n.getMessage("message.uploadedfile.illegalFilename"));
}
+ protected void interruptUploadDueToEncryptionError() {
+ interruptUploadAndSetReason(i18n.getMessage("message.encryption.failed"));
+ }
+
protected boolean isFileAlreadyContainedInSoftwareModule(final FileUploadId newFileUploadId,
final SoftwareModule softwareModule) {
for (final Artifact artifact : softwareModule.getArtifacts()) {
@@ -215,7 +221,7 @@ public abstract class AbstractFileTransferHandler implements Serializable {
try {
outputStream.close();
} catch (final IOException e1) {
- LOG.warn("Closing output stream caused an exception {}", e1);
+ LOG.warn("Closing output stream caused by", e1);
}
}
@@ -226,7 +232,7 @@ public abstract class AbstractFileTransferHandler implements Serializable {
try {
inputStream.close();
} catch (final IOException e1) {
- LOG.warn("Closing input stream caused an exception {}", e1);
+ LOG.warn("Closing input stream caused by", e1);
}
}
}
@@ -276,13 +282,20 @@ public abstract class AbstractFileTransferHandler implements Serializable {
streamToRepository();
} catch (final FileSizeQuotaExceededException e) {
interruptUploadDueToFileSizeQuotaExceeded(e.getExceededQuotaValueString());
+ publishUploadFailedAndFinishedEvent(fileUploadId);
LOG.debug("Upload failed due to file size quota exceeded:", e);
} catch (final StorageQuotaExceededException e) {
interruptUploadDueToStorageQuotaExceeded(e.getExceededQuotaValueString());
+ publishUploadFailedAndFinishedEvent(fileUploadId);
LOG.debug("Upload failed due to storage quota exceeded:", e);
} catch (final AssignmentQuotaExceededException e) {
interruptUploadDueToAssignmentQuotaExceeded();
+ publishUploadFailedAndFinishedEvent(fileUploadId);
LOG.debug("Upload failed due to assignment quota exceeded:", e);
+ } catch (final ArtifactEncryptionUnsupportedException | ArtifactEncryptionFailedException e) {
+ interruptUploadDueToEncryptionError();
+ publishUploadFailedAndFinishedEvent(fileUploadId);
+ LOG.warn("Upload failed due to encryption error", e);
} catch (final RuntimeException e) {
interruptUploadDueToUploadFailed();
publishUploadFailedAndFinishedEvent(fileUploadId);
@@ -297,25 +310,28 @@ public abstract class AbstractFileTransferHandler implements Serializable {
if (fileUploadId == null) {
throw new ArtifactUploadFailedException();
}
+
final String filename = fileUploadId.getFilename();
- LOG.debug("Transfering file {} directly to repository", filename);
final Artifact artifact = uploadArtifact(filename);
+
if (isUploadInterrupted()) {
LOG.warn("Upload of {} was interrupted", filename);
handleUploadFailure(artifact);
publishUploadFinishedEvent(fileUploadId);
return;
}
+
publishUploadSucceeded(fileUploadId, artifact.getSize());
publishUploadFinishedEvent(fileUploadId);
publishArtifactsChanged(fileUploadId);
}
private Artifact uploadArtifact(final String filename) {
+ LOG.debug("Transfering file {} directly to repository", filename);
try {
return artifactManagement.create(new ArtifactUpload(inputStream, fileUploadId.getSoftwareModuleId(),
filename, null, null, null, true, mimeType, -1));
- } catch (final ArtifactUploadFailedException | InvalidSHA1HashException | InvalidMD5HashException e) {
+ } catch (final InvalidSHA1HashException | InvalidMD5HashException e) {
throw new ArtifactUploadFailedException(e);
}
}
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileUploadId.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileUploadId.java
index 339075b14..3639c551e 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileUploadId.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileUploadId.java
@@ -9,9 +9,8 @@
package org.eclipse.hawkbit.ui.artifacts.upload;
import java.io.Serializable;
+import java.util.Objects;
-import org.apache.commons.lang3.builder.EqualsBuilder;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
@@ -21,19 +20,14 @@ import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
*
*/
public class FileUploadId implements Serializable {
-
private static final long serialVersionUID = 1L;
private final String filename;
+ private final Long softwareModuleId;
private final String softwareModuleName;
-
private final String softwareModuleVersion;
- private Long softwareModuleId;
-
- private final String id;
-
/**
* Creates a new {@link FileUploadId} instance.
*
@@ -44,36 +38,25 @@ public class FileUploadId implements Serializable {
*/
public FileUploadId(final String filename, final SoftwareModule softwareModule) {
this.filename = filename;
+ this.softwareModuleId = softwareModule.getId();
this.softwareModuleName = softwareModule.getName();
this.softwareModuleVersion = softwareModule.getVersion();
- this.softwareModuleId = softwareModule.getId();
- this.id = createFileUploadIdString(filename, softwareModuleName, softwareModuleVersion);
}
- /**
- * Creates a new {@link FileUploadId} instance.
- *
- * @param filename
- * the name of the file
- * @param softwareModuleName
- * the name of a {@link SoftwareModule} for which the file is
- * uploaded
- * @param softwareModuleVersion
- * the version of a {@link SoftwareModule} for which the file is
- * uploaded
- */
- public FileUploadId(final String filename, final String softwareModuleName, final String softwareModuleVersion) {
- this.filename = filename;
- this.softwareModuleName = softwareModuleName;
- this.softwareModuleVersion = softwareModuleVersion;
- this.id = createFileUploadIdString(filename, softwareModuleName, softwareModuleVersion);
+ public String getFilename() {
+ return filename;
}
- private static String createFileUploadIdString(final String filename, final String softwareModuleName,
- final String softwareModuleVersion) {
- return new StringBuilder(filename).append(":")
- .append(HawkbitCommonUtil.getFormattedNameVersion(softwareModuleName, softwareModuleVersion))
- .toString();
+ public Long getSoftwareModuleId() {
+ return softwareModuleId;
+ }
+
+ public String getSoftwareModuleName() {
+ return softwareModuleName;
+ }
+
+ public String getSoftwareModuleVersion() {
+ return softwareModuleVersion;
}
@Override
@@ -88,52 +71,21 @@ public class FileUploadId implements Serializable {
return false;
}
final FileUploadId other = (FileUploadId) obj;
- return new EqualsBuilder().append(id, other.id).isEquals();
+ return Objects.equals(this.getFilename(), other.getFilename())
+ && Objects.equals(this.getSoftwareModuleId(), other.getSoftwareModuleId())
+ && Objects.equals(this.getSoftwareModuleName(), other.getSoftwareModuleName())
+ && Objects.equals(this.getSoftwareModuleVersion(), other.getSoftwareModuleVersion());
}
@Override
public int hashCode() {
- return new HashCodeBuilder().append(id).toHashCode();
+ return Objects.hash(getFilename(), getSoftwareModuleId(), getSoftwareModuleName(), getSoftwareModuleVersion());
}
@Override
public String toString() {
- return id;
- }
-
- /**
- * Getter for the uploaded file name
- *
- * @return String
- */
- public String getFilename() {
- return filename;
- }
-
- /**
- * Getter for the software module name
- *
- * @return String
- */
- public String getSoftwareModuleName() {
- return softwareModuleName;
- }
-
- /**
- * Getter for the software module version
- *
- * @return String
- */
- public String getSoftwareModuleVersion() {
- return softwareModuleVersion;
- }
-
- /**
- * Getter for the software module ID
- *
- * @return Long
- */
- public Long getSoftwareModuleId() {
- return softwareModuleId;
+ return new StringBuilder(filename).append(":")
+ .append(HawkbitCommonUtil.getFormattedNameVersion(softwareModuleName, softwareModuleVersion))
+ .toString();
}
}
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/FormComponentBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/FormComponentBuilder.java
index 93e5e6358..354ca1bb5 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/FormComponentBuilder.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/FormComponentBuilder.java
@@ -377,9 +377,9 @@ public final class FormComponentBuilder {
* setter for the binder
* @return the bound box
*/
- public static CheckBox getCheckBox(final String id, final Binder binder,
+ public static CheckBox createCheckBox(final String id, final Binder binder,
final ValueProvider getter, final Setter setter) {
- return getCheckBox(null, id, binder, getter, setter);
+ return createCheckBox(null, id, binder, getter, setter);
}
/**
@@ -399,7 +399,7 @@ public final class FormComponentBuilder {
* setter for the binder
* @return the bound box
*/
- public static CheckBox getCheckBox(final String caption, final String id, final Binder binder,
+ public static CheckBox createCheckBox(final String caption, final String id, final Binder binder,
final ValueProvider getter, final Setter setter) {
final CheckBox checkBox;
if (StringUtils.isEmpty(caption)) {
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/mappers/SoftwareModuleToProxyMapper.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/mappers/SoftwareModuleToProxyMapper.java
index 14e1acabd..c3e610176 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/mappers/SoftwareModuleToProxyMapper.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/mappers/SoftwareModuleToProxyMapper.java
@@ -31,6 +31,7 @@ public class SoftwareModuleToProxyMapper
proxySoftwareModule.setNameAndVersion(
HawkbitCommonUtil.concatStrings(":", softwareModule.getName(), softwareModule.getVersion()));
proxySoftwareModule.setVendor(softwareModule.getVendor());
+ proxySoftwareModule.setEncrypted(softwareModule.isEncrypted());
final SoftwareModuleType type = softwareModule.getType();
final ProxyTypeInfo typeInfo = new ProxyTypeInfo(type.getId(), type.getName(), type.getKey());
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/proxies/ProxySoftwareModule.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/proxies/ProxySoftwareModule.java
index a917f4502..af42dc1c8 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/proxies/ProxySoftwareModule.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/proxies/ProxySoftwareModule.java
@@ -29,6 +29,8 @@ public class ProxySoftwareModule extends ProxyNamedEntity implements VersionAwar
private boolean assigned;
+ private boolean encrypted;
+
/**
* Gets the software module vendor
*
@@ -129,4 +131,12 @@ public class ProxySoftwareModule extends ProxyNamedEntity implements VersionAwar
public void setVersion(final String version) {
this.version = version;
}
+
+ public boolean isEncrypted() {
+ return encrypted;
+ }
+
+ public void setEncrypted(final boolean encrypted) {
+ this.encrypted = encrypted;
+ }
}
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/MetaDataAddUpdateWindowLayoutComponentBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/MetaDataAddUpdateWindowLayoutComponentBuilder.java
index afad332a7..ca3183915 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/MetaDataAddUpdateWindowLayoutComponentBuilder.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/MetaDataAddUpdateWindowLayoutComponentBuilder.java
@@ -91,7 +91,7 @@ public class MetaDataAddUpdateWindowLayoutComponentBuilder {
* @return Target field CheckBox
*/
public CheckBox createVisibleForTargetsField(final Binder binder) {
- return FormComponentBuilder.getCheckBox(i18n.getMessage(TARGET_VISIBLE),
+ return FormComponentBuilder.createCheckBox(i18n.getMessage(TARGET_VISIBLE),
UIComponentIdProvider.METADATA_TARGET_VISIBLE_ID, binder, ProxyMetaData::isVisibleForTargets,
ProxyMetaData::setVisibleForTargets);
}
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/SoftwareModuleDetails.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/SoftwareModuleDetails.java
index 227bf6763..8cf08fc59 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/SoftwareModuleDetails.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/SoftwareModuleDetails.java
@@ -54,7 +54,8 @@ public class SoftwareModuleDetails extends AbstractGridDetailsLayout implements MasterEntityAwareComponent {
private final FilterSupport, ?> filterSupport;
private final Function masterEntityToFilterMapper;
+ private final Consumer postMasterChangeCallback;
private Long masterId;
@@ -47,8 +49,24 @@ public class MasterEntitySupport implements M
*/
public MasterEntitySupport(final FilterSupport, ?> filterSupport,
final Function masterEntityToFilterMapper) {
+ this(filterSupport, masterEntityToFilterMapper, null);
+ }
+
+ /**
+ * Constructor for MasterEntitySupport
+ *
+ * @param filterSupport
+ * Filter support
+ * @param masterEntityToFilterMapper
+ * Master entity to filter mapper
+ * @param postMasterChangeCallback
+ * Callback called after master entity change
+ */
+ public MasterEntitySupport(final FilterSupport, ?> filterSupport, final Function masterEntityToFilterMapper,
+ final Consumer postMasterChangeCallback) {
this.filterSupport = filterSupport;
this.masterEntityToFilterMapper = masterEntityToFilterMapper;
+ this.postMasterChangeCallback = postMasterChangeCallback;
}
@Override
@@ -60,6 +78,10 @@ public class MasterEntitySupport implements M
filterSupport.updateFilter(FilterType.MASTER, getMasterEntityFilter(masterEntity));
masterId = masterEntity != null ? masterEntity.getId() : null;
+
+ if (postMasterChangeCallback != null) {
+ postMasterChangeCallback.accept(masterEntity);
+ }
}
private Object getMasterEntityFilter(final M masterEntity) {
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/disttype/SmTypeSelectedGrid.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/disttype/SmTypeSelectedGrid.java
index 7e29dc538..5accbe6a3 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/disttype/SmTypeSelectedGrid.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/disttype/SmTypeSelectedGrid.java
@@ -82,6 +82,6 @@ public class SmTypeSelectedGrid extends Grid {
binder.addValueChangeListener(event -> mandatoryPropertyChangedCallback.run());
final String id = "selected.sm.type." + smType.getId();
- return FormComponentBuilder.getCheckBox(id, binder, ProxyType::isMandatory, ProxyType::setMandatory);
+ return FormComponentBuilder.createCheckBox(id, binder, ProxyType::isMandatory, ProxyType::setMandatory);
}
}
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DsWindowLayoutComponentBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DsWindowLayoutComponentBuilder.java
index 65edb79ce..16f8d506b 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DsWindowLayoutComponentBuilder.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DsWindowLayoutComponentBuilder.java
@@ -107,7 +107,7 @@ public class DsWindowLayoutComponentBuilder {
* @return Migration step required checkbox
*/
public CheckBox createMigrationStepField(final Binder binder) {
- final CheckBox migrationRequired = FormComponentBuilder.getCheckBox(i18n.getMessage(MIGRATION_STEP),
+ final CheckBox migrationRequired = FormComponentBuilder.createCheckBox(i18n.getMessage(MIGRATION_STEP),
UIComponentIdProvider.DIST_ADD_MIGRATION_CHECK, binder, ProxyDistributionSet::isRequiredMigrationStep,
ProxyDistributionSet::setRequiredMigrationStep);
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/error/extractors/ArtifactEncryptionErrorExtractor.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/error/extractors/ArtifactEncryptionErrorExtractor.java
new file mode 100644
index 000000000..09fd6e035
--- /dev/null
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/error/extractors/ArtifactEncryptionErrorExtractor.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2021 Bosch.IO 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.ui.error.extractors;
+
+import java.util.Optional;
+
+import org.eclipse.hawkbit.repository.exception.ArtifactEncryptionFailedException;
+import org.eclipse.hawkbit.repository.exception.ArtifactEncryptionUnsupportedException;
+import org.eclipse.hawkbit.ui.error.UiErrorDetails;
+import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
+import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
+
+/**
+ * UI error details extractor for {@link ArtifactEncryptionUnsupportedException}
+ * and {@link ArtifactEncryptionFailedException}.
+ */
+public class ArtifactEncryptionErrorExtractor extends AbstractSingleUiErrorDetailsExtractor {
+ private final VaadinMessageSource i18n;
+
+ /**
+ * Constructor for {@link ArtifactEncryptionErrorExtractor}.
+ *
+ * @param i18n
+ * Message source used for localization
+ */
+ public ArtifactEncryptionErrorExtractor(final VaadinMessageSource i18n) {
+ this.i18n = i18n;
+ }
+
+ @Override
+ protected Optional findDetails(final Throwable error) {
+ return findExceptionOf(error, ArtifactEncryptionUnsupportedException.class)
+ .map(ex -> UiErrorDetails.create(i18n.getMessage(UIMessageIdProvider.CAPTION_ERROR),
+ i18n.getMessage(UIMessageIdProvider.MESSAGE_ERROR_ENCRYPTION_NOT_SUPPORTED)))
+ .map(Optional::of)
+ .orElseGet(() -> findExceptionOf(error, ArtifactEncryptionFailedException.class)
+ .map(ex -> UiErrorDetails.create(i18n.getMessage(UIMessageIdProvider.CAPTION_ERROR),
+ getEncryptionFailedDetailsMsg(ex))));
+ }
+
+ private String getEncryptionFailedDetailsMsg(final ArtifactEncryptionFailedException ex) {
+ switch (ex.getEncryptionOperation()) {
+ case GENERATE_SECRETS:
+ return i18n.getMessage(UIMessageIdProvider.MESSAGE_ERROR_ENCRYPTION_SECRETS_FAILED);
+ case ENCRYPT:
+ return i18n.getMessage(UIMessageIdProvider.MESSAGE_ERROR_ENCRYPTION_FAILED);
+ case DECRYPT:
+ return i18n.getMessage(UIMessageIdProvider.MESSAGE_ERROR_DECRYPTION_FAILED);
+ default:
+ return i18n.getMessage(UIMessageIdProvider.MESSAGE_ERROR_ENCRYPTION_FAILED);
+ }
+ }
+}
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/error/extractors/UploadErrorExtractor.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/error/extractors/UploadErrorExtractor.java
index 284dd50ad..16cb95884 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/error/extractors/UploadErrorExtractor.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/error/extractors/UploadErrorExtractor.java
@@ -21,7 +21,7 @@ public class UploadErrorExtractor extends AbstractSingleUiErrorDetailsExtractor
@Override
protected Optional findDetails(final Throwable error) {
- // UploadException is ignored
+ // UploadException is ignored as it is handled explicitly
return findExceptionOf(error, UploadException.class).map(ex -> UiErrorDetails.empty());
}
}
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoAssignmentWindowLayoutComponentBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoAssignmentWindowLayoutComponentBuilder.java
index 60a208dfc..9f7696748 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoAssignmentWindowLayoutComponentBuilder.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoAssignmentWindowLayoutComponentBuilder.java
@@ -65,7 +65,7 @@ public class AutoAssignmentWindowLayoutComponentBuilder {
*/
public CheckBox createEnableCheckbox(final Binder binder) {
final String caption = i18n.getMessage(UIMessageIdProvider.LABEL_AUTO_ASSIGNMENT_ENABLE);
- return FormComponentBuilder.getCheckBox(caption, UIComponentIdProvider.DIST_SET_SELECT_ENABLE_ID, binder,
+ return FormComponentBuilder.createCheckBox(caption, UIComponentIdProvider.DIST_SET_SELECT_ENABLE_ID, binder,
ProxyTargetFilterQuery::isAutoAssignmentEnabled, ProxyTargetFilterQuery::setAutoAssignmentEnabled);
}
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/miscs/AssignmentWindowLayoutComponentBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/miscs/AssignmentWindowLayoutComponentBuilder.java
index 24bf626e8..e9382dab7 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/miscs/AssignmentWindowLayoutComponentBuilder.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/miscs/AssignmentWindowLayoutComponentBuilder.java
@@ -81,7 +81,7 @@ public class AssignmentWindowLayoutComponentBuilder {
* @return Maintenance window checkbox
*/
public CheckBox createEnableMaintenanceWindowToggle(final Binder binder) {
- final CheckBox maintenanceWindowToggle = FormComponentBuilder.getCheckBox(
+ final CheckBox maintenanceWindowToggle = FormComponentBuilder.createCheckBox(
i18n.getMessage("caption.maintenancewindow.enabled"),
UIComponentIdProvider.MAINTENANCE_WINDOW_ENABLED_ID, binder,
ProxyAssignmentWindow::isMaintenanceWindowEnabled, ProxyAssignmentWindow::setMaintenanceWindowEnabled);
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/AuthenticationConfigurationView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/AuthenticationConfigurationView.java
index 0d286a6b7..8807961b9 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/AuthenticationConfigurationView.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/AuthenticationConfigurationView.java
@@ -106,7 +106,7 @@ public class AuthenticationConfigurationView extends BaseConfigurationView3.9.0
- 9.1.3
+ 9.1.6
1.14.2
2.13.6
2.7.9