Artifact Encryption plug point (#1202)
* added ArtifactEncryption interface, injected it into SM creation UI module, added encryption metadata key generation upon SM creation, used encryptor during file upload Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * add default artifact encryption implementation based on gcm aes algorithm Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * changed ArtifactEncryptor interface to manage encryption secrets by itself Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * cleaned up stale code, fixed sonar Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * fixed software module encryption within transaction Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added artifact encryption secrets store Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * extended ArtifactEncryption interface to allow decryption, secrets store provides removeSecret, added missing javadocs Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * intriduced DbArtifact interface, use EncryptionAwareDbArtifact for artifact decryption during download Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * introduced ArtifactEncryptionService to minimize duplications and unneccessary dependency injections Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * declared ArtifactEncryptionService as a bean Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added persistant encryption flag to software module Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * further adptations for encryption flag persistence Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added ArtifactEncryptionException, fixed encryption check in UI Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added encryption error handling Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added encrypted flag to DDI/DMF, adapted exception handling Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * adapted rest docs Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * Add test to verify artifact encryption is not given by default Signed-off-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io> * Add isEncrypted() to toString() of JpaSoftwareModule, fix typos Signed-off-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io> * Fix sql migration scripts Signed-off-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io> * Calculate encrypted artifact size by subtract encryption size overhead Signed-off-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io> * publish upload failed without waiting for interuption during UI file upload Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * upgraded cron utils to 9.1.6 Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> Co-authored-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io>
This commit is contained in:
@@ -8,15 +8,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.artifact.repository.model;
|
package org.eclipse.hawkbit.artifact.repository.model;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database representation of artifact.
|
* Database representation of artifact.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractDbArtifact {
|
public abstract class AbstractDbArtifact implements DbArtifact {
|
||||||
|
|
||||||
private final String artifactId;
|
private final String artifactId;
|
||||||
private final long size;
|
private final long size;
|
||||||
@@ -34,46 +32,33 @@ public abstract class AbstractDbArtifact {
|
|||||||
this.contentType = contentType;
|
this.contentType = contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* @return ID of the artifact
|
|
||||||
*/
|
|
||||||
public String getArtifactId() {
|
public String getArtifactId() {
|
||||||
return artifactId;
|
return artifactId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* @return hashes of the artifact
|
|
||||||
*/
|
|
||||||
public DbArtifactHash getHashes() {
|
public DbArtifactHash getHashes() {
|
||||||
return hashes;
|
return hashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set hashes of the artifact
|
* Set hashes of the artifact
|
||||||
|
*
|
||||||
|
* @param hashes
|
||||||
|
* artifact hashes
|
||||||
*/
|
*/
|
||||||
public void setHashes(final DbArtifactHash hashes) {
|
public void setHashes(final DbArtifactHash hashes) {
|
||||||
this.hashes = hashes;
|
this.hashes = hashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* @return site of the artifact in bytes
|
|
||||||
*/
|
|
||||||
public long getSize() {
|
public long getSize() {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* @return content-type if known by the repository or <code>null</code>
|
|
||||||
*/
|
|
||||||
public String getContentType() {
|
public String getContentType() {
|
||||||
return contentType;
|
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();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 <code>null</code>
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
}
|
||||||
@@ -97,6 +97,18 @@ public enum SpServerError {
|
|||||||
SP_ARTIFACT_UPLOAD_FAILED("hawkbit.server.error.artifact.uploadFailed",
|
SP_ARTIFACT_UPLOAD_FAILED("hawkbit.server.error.artifact.uploadFailed",
|
||||||
"Upload of artifact failed with internal server error."),
|
"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."),
|
"Storage quota will be exceeded if file is uploaded."),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* error message, which describes that the action can not be canceled cause the
|
* error message, which describes that the action can not be canceled cause
|
||||||
* action is inactive.
|
* the action is inactive.
|
||||||
*/
|
*/
|
||||||
SP_ACTION_NOT_CANCELABLE("hawkbit.server.error.action.notcancelable",
|
SP_ACTION_NOT_CANCELABLE("hawkbit.server.error.action.notcancelable",
|
||||||
"Only active actions which are in status pending are cancelable."),
|
"Only active actions which are in status pending are cancelable."),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* error message, which describes that the action can not be force quit cause
|
* error message, which describes that the action can not be force quit
|
||||||
* the action is inactive.
|
* cause the action is inactive.
|
||||||
*/
|
*/
|
||||||
SP_ACTION_NOT_FORCE_QUITABLE("hawkbit.server.error.action.notforcequitable",
|
SP_ACTION_NOT_FORCE_QUITABLE("hawkbit.server.error.action.notforcequitable",
|
||||||
"Only active actions which are in status pending can be force quit."),
|
"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."),
|
"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",
|
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']"),
|
"The given action type for auto-assignment is invalid: allowed values are ['forced', 'soft', 'downloadonly']"),
|
||||||
|
|||||||
@@ -8,6 +8,18 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.amqp;
|
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.ApiType;
|
||||||
import org.eclipse.hawkbit.api.ArtifactUrl;
|
import org.eclipse.hawkbit.api.ArtifactUrl;
|
||||||
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
|
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.data.domain.PageRequest;
|
||||||
import org.springframework.util.CollectionUtils;
|
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
|
* {@link AmqpMessageDispatcherService} create all outgoing AMQP messages and
|
||||||
* delegate the messages to a {@link AmqpMessageSenderService}.
|
* delegate the messages to a {@link AmqpMessageSenderService}.
|
||||||
@@ -105,8 +105,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
* @param targetManagement
|
* @param targetManagement
|
||||||
* to access target information
|
* to access target information
|
||||||
* @param serviceMatcher
|
* @param serviceMatcher
|
||||||
* to check in cluster case if the message is from the same cluster
|
* to check in cluster case if the message is from the same
|
||||||
* node
|
* cluster node
|
||||||
* @param distributionSetManagement
|
* @param distributionSetManagement
|
||||||
* to retrieve modules
|
* 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
|
* Method to send a message to a RabbitMQ Exchange after the Distribution
|
||||||
* has been assign to a Target.
|
* set has been assign to a Target.
|
||||||
*
|
*
|
||||||
* @param assignedEvent
|
* @param assignedEvent
|
||||||
* the object to be send.
|
* 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
|
* 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
|
* DOWNLOAD_ONLY action or if it has a valid maintenance window available or
|
||||||
* based on defined maintenance schedule. In case of no maintenance schedule or
|
* not based on defined maintenance schedule. In case of no maintenance
|
||||||
* if there is a valid window available, the topic
|
* schedule or if there is a valid window available, the topic
|
||||||
* {@link EventTopic#DOWNLOAD_AND_INSTALL} is returned else
|
* {@link EventTopic#DOWNLOAD_AND_INSTALL} is returned else
|
||||||
* {@link EventTopic#DOWNLOAD} is returned.
|
* {@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
|
* Determines the {@link EventTopic} for the given {@link Action}, depending
|
||||||
* its action type.
|
* on its action type.
|
||||||
*
|
*
|
||||||
* @param action
|
* @param action
|
||||||
* to obtain the corresponding {@link EventTopic} for
|
* 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
|
* Method to send a message to a RabbitMQ Exchange after the assignment of
|
||||||
* Distribution set to a Target has been canceled.
|
* the Distribution set to a Target has been canceled.
|
||||||
*
|
*
|
||||||
* @param cancelEvent
|
* @param cancelEvent
|
||||||
* that is to be converted to a DMF message
|
* 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
|
* @param deleteEvent
|
||||||
* the TargetDeletedEvent which holds the necessary data for sending
|
* the TargetDeletedEvent which holds the necessary data for
|
||||||
* a target delete message.
|
* sending a target delete message.
|
||||||
*/
|
*/
|
||||||
@EventListener(classes = TargetDeletedEvent.class)
|
@EventListener(classes = TargetDeletedEvent.class)
|
||||||
protected void targetDelete(final TargetDeletedEvent deleteEvent) {
|
protected void targetDelete(final TargetDeletedEvent deleteEvent) {
|
||||||
@@ -345,7 +346,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final DmfDownloadAndUpdateRequest downloadAndUpdateRequest = createDownloadAndUpdateRequest(target, action.getId(), modules);
|
final DmfDownloadAndUpdateRequest downloadAndUpdateRequest = createDownloadAndUpdateRequest(target,
|
||||||
|
action.getId(), modules);
|
||||||
final Message message = getMessageConverter().toMessage(downloadAndUpdateRequest,
|
final Message message = getMessageConverter().toMessage(downloadAndUpdateRequest,
|
||||||
createConnectorMessagePropertiesEvent(tenant, target.getControllerId(), getEventTypeForTarget(action)));
|
createConnectorMessagePropertiesEvent(tenant, target.getControllerId(), getEventTypeForTarget(action)));
|
||||||
amqpSenderService.sendMessage(message, targetAddress);
|
amqpSenderService.sendMessage(message, targetAddress);
|
||||||
@@ -445,6 +447,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
amqpSoftwareModule.setModuleId(entry.getKey().getId());
|
amqpSoftwareModule.setModuleId(entry.getKey().getId());
|
||||||
amqpSoftwareModule.setModuleType(entry.getKey().getType().getKey());
|
amqpSoftwareModule.setModuleType(entry.getKey().getType().getKey());
|
||||||
amqpSoftwareModule.setModuleVersion(entry.getKey().getVersion());
|
amqpSoftwareModule.setModuleVersion(entry.getKey().getVersion());
|
||||||
|
amqpSoftwareModule.setEncrypted(entry.getKey().isEncrypted() ? Boolean.TRUE : null);
|
||||||
amqpSoftwareModule.setArtifacts(convertArtifacts(target, entry.getKey().getArtifacts()));
|
amqpSoftwareModule.setArtifacts(convertArtifacts(target, entry.getKey().getArtifacts()));
|
||||||
|
|
||||||
if (!CollectionUtils.isEmpty(entry.getValue())) {
|
if (!CollectionUtils.isEmpty(entry.getValue())) {
|
||||||
|
|||||||
@@ -150,8 +150,8 @@ public class AmqpControllerAuthenticationTest {
|
|||||||
|
|
||||||
authenticationManager.postConstruct();
|
authenticationManager.postConstruct();
|
||||||
|
|
||||||
testArtifact = new JpaArtifact(SHA1, "afilename", new JpaSoftwareModule(
|
testArtifact = new JpaArtifact(SHA1, "afilename",
|
||||||
new JpaSoftwareModuleType("a key", "a name", null, 1), "a name", null, null, null));
|
new JpaSoftwareModule(new JpaSoftwareModuleType("a key", "a name", null, 1), "a name", "a version"));
|
||||||
testArtifact.setId(1L);
|
testArtifact.setId(1L);
|
||||||
|
|
||||||
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate,
|
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate,
|
||||||
@@ -260,7 +260,7 @@ public class AmqpControllerAuthenticationTest {
|
|||||||
|
|
||||||
when(tenantConfigurationManagementMock.getConfigurationValue(
|
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||||
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
|
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
|
||||||
.thenReturn(CONFIG_VALUE_TRUE);
|
.thenReturn(CONFIG_VALUE_TRUE);
|
||||||
|
|
||||||
when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
|
when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ public class DmfSoftwareModule {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private String moduleVersion;
|
private String moduleVersion;
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
private Boolean encrypted;
|
||||||
|
@JsonProperty
|
||||||
private List<DmfArtifact> artifacts;
|
private List<DmfArtifact> artifacts;
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private List<DmfMetadata> metadata;
|
private List<DmfMetadata> metadata;
|
||||||
@@ -83,11 +85,18 @@ public class DmfSoftwareModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getEncrypted() {
|
||||||
|
return encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEncrypted(final Boolean encrypted) {
|
||||||
|
this.encrypted = encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format(
|
return String.format(
|
||||||
"DmfSoftwareModule [moduleId=%d, moduleType='%s', moduleVersion='%s', artifacts=%s, metadata=%s]",
|
"DmfSoftwareModule [moduleId=%d, moduleType='%s', moduleVersion='%s', encrypted='%s' artifacts=%s, metadata=%s]",
|
||||||
moduleId, moduleType, moduleVersion, artifacts, metadata);
|
moduleId, moduleType, moduleVersion, encrypted, artifacts, metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<String> requiredSecretKeys();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates required secrets key/value pairs.
|
||||||
|
*
|
||||||
|
* @return secrets key/value pairs
|
||||||
|
* @throws ArtifactEncryptionFailedException
|
||||||
|
*/
|
||||||
|
Map<String, String> 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<String, String> 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<String, String> secrets, final InputStream stream);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size of the underlying encryption algorithm overhead in bytes
|
||||||
|
*
|
||||||
|
* @return encryption overhead in byte
|
||||||
|
*/
|
||||||
|
int encryptionSizeOverhead();
|
||||||
|
}
|
||||||
@@ -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<String> 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);
|
||||||
|
}
|
||||||
@@ -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<String, String> 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<String, String> getSoftwareModuleEncryptionSecrets(final long smId) {
|
||||||
|
final Set<String> requiredSecretsKeys = artifactEncryption.requiredSecretKeys();
|
||||||
|
final Map<String, String> requiredSecrets = new HashMap<>();
|
||||||
|
for (final String requiredSecretsKey : requiredSecretsKeys) {
|
||||||
|
final Optional<String> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ import javax.validation.Valid;
|
|||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
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.im.authentication.SpPermission.SpringEvalExpressions;
|
||||||
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
|
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
|
||||||
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
|
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
|
||||||
@@ -69,8 +69,8 @@ public interface ArtifactManagement {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Garbage collects artifact binaries if only referenced by given
|
* Garbage collects artifact binaries if only referenced by given
|
||||||
* {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are marked
|
* {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are
|
||||||
* as deleted.
|
* marked as deleted.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param artifactSha1Hash
|
* @param artifactSha1Hash
|
||||||
@@ -161,15 +161,20 @@ public interface ArtifactManagement {
|
|||||||
Page<Artifact> findBySoftwareModule(@NotNull Pageable pageReq, long swId);
|
Page<Artifact> 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
|
* @param sha1Hash
|
||||||
* to search for
|
* 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
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR
|
||||||
+ SpringEvalExpressions.IS_CONTROLLER)
|
+ SpringEvalExpressions.IS_CONTROLLER)
|
||||||
Optional<AbstractDbArtifact> loadArtifactBinary(@NotEmpty String sha1Hash);
|
Optional<DbArtifact> loadArtifactBinary(@NotEmpty String sha1Hash, long softwareModuleId,
|
||||||
|
final boolean isEncrypted);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,13 @@ public interface SoftwareModuleCreate {
|
|||||||
return type(Optional.ofNullable(type).map(SoftwareModuleType::getKey).orElse(null));
|
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
|
* @return peek on current state of {@link SoftwareModule} in the builder
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,4 +67,9 @@ public interface SoftwareModule extends NamedVersionedEntity {
|
|||||||
*/
|
*/
|
||||||
List<DistributionSet> getAssignedTo();
|
List<DistributionSet> getAssignedTo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@code true} if this software module is marked as encrypted
|
||||||
|
* otherwise {@code false}
|
||||||
|
*/
|
||||||
|
boolean isEncrypted();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<InputStream> decryptionFunction;
|
||||||
|
private final int encryptionOverhead;
|
||||||
|
|
||||||
|
public EncryptionAwareDbArtifact(final DbArtifact encryptedDbArtifact,
|
||||||
|
final UnaryOperator<InputStream> decryptionFunction) {
|
||||||
|
this.encryptedDbArtifact = encryptedDbArtifact;
|
||||||
|
this.decryptionFunction = decryptionFunction;
|
||||||
|
this.encryptionOverhead = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EncryptionAwareDbArtifact(final DbArtifact encryptedDbArtifact,
|
||||||
|
final UnaryOperator<InputStream> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,9 @@ import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
|
|||||||
import org.eclipse.hawkbit.artifact.repository.ArtifactStoreException;
|
import org.eclipse.hawkbit.artifact.repository.ArtifactStoreException;
|
||||||
import org.eclipse.hawkbit.artifact.repository.HashNotMatchException;
|
import org.eclipse.hawkbit.artifact.repository.HashNotMatchException;
|
||||||
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
|
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.artifact.repository.model.DbArtifactHash;
|
||||||
|
import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
|
||||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
|
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
|
||||||
@@ -104,15 +106,24 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
|||||||
|
|
||||||
assertArtifactQuota(moduleId, 1);
|
assertArtifactQuota(moduleId, 1);
|
||||||
|
|
||||||
final AbstractDbArtifact artifact = storeArtifact(artifactUpload);
|
final AbstractDbArtifact artifact = storeArtifact(artifactUpload, softwareModule.isEncrypted());
|
||||||
return storeArtifactMetadata(softwareModule, filename, artifact, existing);
|
return storeArtifactMetadata(softwareModule, filename, artifact, existing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload) {
|
private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload, final boolean isSmEncrypted) {
|
||||||
try (final InputStream quotaStream = wrapInQuotaStream(artifactUpload.getInputStream())) {
|
final String tenant = tenantAware.getCurrentTenant();
|
||||||
return artifactRepository.store(tenantAware.getCurrentTenant(), quotaStream, artifactUpload.getFilename(),
|
final long smId = artifactUpload.getModuleId();
|
||||||
artifactUpload.getContentType(), new DbArtifactHash(artifactUpload.getProvidedSha1Sum(),
|
final InputStream stream = artifactUpload.getInputStream();
|
||||||
artifactUpload.getProvidedMd5Sum(), artifactUpload.getProvidedSha256Sum()));
|
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) {
|
} catch (final ArtifactStoreException | IOException e) {
|
||||||
throw new ArtifactUploadFailedException(e);
|
throw new ArtifactUploadFailedException(e);
|
||||||
} catch (final HashNotMatchException 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) {
|
private void assertArtifactQuota(final long id, final int requested) {
|
||||||
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxArtifactsPerSoftwareModule(),
|
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxArtifactsPerSoftwareModule(),
|
||||||
Artifact.class, SoftwareModule.class, localArtifactRepository::countBySoftwareModuleId);
|
Artifact.class, SoftwareModule.class, localArtifactRepository::countBySoftwareModuleId);
|
||||||
@@ -212,10 +227,26 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<AbstractDbArtifact> loadArtifactBinary(final String sha1Hash) {
|
public Optional<DbArtifact> loadArtifactBinary(final String sha1Hash, final long softwareModuleId,
|
||||||
return Optional.ofNullable(artifactRepository.existsByTenantAndSha1(tenantAware.getCurrentTenant(), sha1Hash)
|
final boolean isEncrypted) {
|
||||||
? artifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), sha1Hash)
|
final String tenant = tenantAware.getCurrentTenant();
|
||||||
: null);
|
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,
|
private Artifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import javax.persistence.criteria.Predicate;
|
|||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
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.ArtifactManagement;
|
||||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||||
@@ -155,7 +156,14 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
|||||||
public SoftwareModule create(final SoftwareModuleCreate c) {
|
public SoftwareModule create(final SoftwareModuleCreate c) {
|
||||||
final JpaSoftwareModuleCreate create = (JpaSoftwareModuleCreate) 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
|
@Override
|
||||||
@@ -295,8 +303,8 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<SoftwareModule> findByRsql(final Pageable pageable, final String rsqlParam) {
|
public Page<SoftwareModule> findByRsql(final Pageable pageable, final String rsqlParam) {
|
||||||
final Specification<JpaSoftwareModule> spec = RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleFields.class,
|
final Specification<JpaSoftwareModule> spec = RSQLUtility.buildRsqlSpecification(rsqlParam,
|
||||||
virtualPropertyReplacer, database);
|
SoftwareModuleFields.class, virtualPropertyReplacer, database);
|
||||||
|
|
||||||
return convertSmPage(softwareModuleRepository.findAll(spec, pageable), pageable);
|
return convertSmPage(softwareModuleRepository.findAll(spec, pageable), pageable);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import javax.persistence.EntityManager;
|
|||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
|
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.ArtifactManagement;
|
||||||
import org.eclipse.hawkbit.repository.BaseRepositoryTypeProvider;
|
import org.eclipse.hawkbit.repository.BaseRepositoryTypeProvider;
|
||||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||||
@@ -245,7 +248,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param dsTypeManagement
|
* @param dsTypeManagement
|
||||||
* for loading {@link TargetType#getCompatibleDistributionSetTypes()}
|
* for loading
|
||||||
|
* {@link TargetType#getCompatibleDistributionSetTypes()}
|
||||||
* @return TargetTypeBuilder bean
|
* @return TargetTypeBuilder bean
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@@ -261,8 +265,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param softwareManagement
|
* @param softwareManagement
|
||||||
* for loading {@link DistributionSetType#getMandatoryModuleTypes()}
|
* for loading
|
||||||
* and {@link DistributionSetType#getOptionalModuleTypes()}
|
* {@link DistributionSetType#getMandatoryModuleTypes()} and
|
||||||
|
* {@link DistributionSetType#getOptionalModuleTypes()}
|
||||||
* @return DistributionSetTypeBuilder bean
|
* @return DistributionSetTypeBuilder bean
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@@ -304,8 +309,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link SystemSecurityContext} singleton bean which make it
|
* @return the {@link SystemSecurityContext} singleton bean which make it
|
||||||
* accessible in beans which cannot access the service directly, e.g.
|
* accessible in beans which cannot access the service directly,
|
||||||
* JPA entities.
|
* e.g. JPA entities.
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
SystemSecurityContextHolder systemSecurityContextHolder() {
|
SystemSecurityContextHolder systemSecurityContextHolder() {
|
||||||
@@ -313,9 +318,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link TenantConfigurationManagement} singleton bean which make
|
* @return the {@link TenantConfigurationManagement} singleton bean which
|
||||||
* it accessible in beans which cannot access the service directly, e.g.
|
* make it accessible in beans which cannot access the service
|
||||||
* JPA entities.
|
* directly, e.g. JPA entities.
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
|
TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
|
||||||
@@ -324,8 +329,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link SystemManagementHolder} singleton bean which holds the
|
* @return the {@link SystemManagementHolder} singleton bean which holds the
|
||||||
* current {@link SystemManagement} service and make it accessible in
|
* current {@link SystemManagement} service and make it accessible
|
||||||
* beans which cannot access the service directly, e.g. JPA entities.
|
* in beans which cannot access the service directly, e.g. JPA
|
||||||
|
* entities.
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
SystemManagementHolder systemManagementHolder() {
|
SystemManagementHolder systemManagementHolder() {
|
||||||
@@ -333,9 +339,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link TenantAwareHolder} singleton bean which holds the current
|
* @return the {@link TenantAwareHolder} singleton bean which holds the
|
||||||
* {@link TenantAware} service and make it accessible in beans which
|
* current {@link TenantAware} service and make it accessible in
|
||||||
* cannot access the service directly, e.g. JPA entities.
|
* beans which cannot access the service directly, e.g. JPA
|
||||||
|
* entities.
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
TenantAwareHolder tenantAwareHolder() {
|
TenantAwareHolder tenantAwareHolder() {
|
||||||
@@ -343,9 +350,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link SecurityTokenGeneratorHolder} singleton bean which holds
|
* @return the {@link SecurityTokenGeneratorHolder} singleton bean which
|
||||||
* the current {@link SecurityTokenGenerator} service and make it
|
* holds the current {@link SecurityTokenGenerator} service and make
|
||||||
* accessible in beans which cannot access the service via injection
|
* it accessible in beans which cannot access the service via
|
||||||
|
* injection
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
SecurityTokenGeneratorHolder securityTokenGeneratorHolder() {
|
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
|
* @return a {@link BaseRepositoryTypeProvider} bean
|
||||||
*/
|
*/
|
||||||
@@ -950,4 +959,17 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
return new NoCountBaseRepositoryTypeProvider();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,26 @@ public class JpaSoftwareModuleCreate extends AbstractSoftwareModuleUpdateCreate<
|
|||||||
|
|
||||||
private final SoftwareModuleTypeManagement softwareModuleTypeManagement;
|
private final SoftwareModuleTypeManagement softwareModuleTypeManagement;
|
||||||
|
|
||||||
|
private boolean encrypted;
|
||||||
|
|
||||||
JpaSoftwareModuleCreate(final SoftwareModuleTypeManagement softwareModuleTypeManagement) {
|
JpaSoftwareModuleCreate(final SoftwareModuleTypeManagement softwareModuleTypeManagement) {
|
||||||
this.softwareModuleTypeManagement = softwareModuleTypeManagement;
|
this.softwareModuleTypeManagement = softwareModuleTypeManagement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SoftwareModuleCreate encrypted(final boolean encrypted) {
|
||||||
|
this.encrypted = encrypted;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEncrypted() {
|
||||||
|
return encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JpaSoftwareModule build() {
|
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) {
|
private SoftwareModuleType getSoftwareModuleTypeFromKeyString(final String type) {
|
||||||
|
|||||||
@@ -90,6 +90,9 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
|||||||
@OneToMany(mappedBy = "softwareModule", fetch = FetchType.LAZY, targetEntity = JpaSoftwareModuleMetadata.class)
|
@OneToMany(mappedBy = "softwareModule", fetch = FetchType.LAZY, targetEntity = JpaSoftwareModuleMetadata.class)
|
||||||
private List<JpaSoftwareModuleMetadata> metadata;
|
private List<JpaSoftwareModuleMetadata> metadata;
|
||||||
|
|
||||||
|
@Column(name = "encrypted")
|
||||||
|
private boolean encrypted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
*/
|
*/
|
||||||
@@ -97,6 +100,20 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
|||||||
// Default constructor for JPA
|
// 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.
|
* parameterized constructor.
|
||||||
*
|
*
|
||||||
@@ -110,12 +127,15 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
|||||||
* of the {@link SoftwareModule}
|
* of the {@link SoftwareModule}
|
||||||
* @param vendor
|
* @param vendor
|
||||||
* of the {@link SoftwareModule}
|
* of the {@link SoftwareModule}
|
||||||
|
* @param encrypted
|
||||||
|
* encryption flag of the {@link SoftwareModule}
|
||||||
*/
|
*/
|
||||||
public JpaSoftwareModule(final SoftwareModuleType type, final String name, final String version,
|
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);
|
super(name, version, description);
|
||||||
this.vendor = vendor;
|
this.vendor = vendor;
|
||||||
this.type = (JpaSoftwareModuleType) type;
|
this.type = (JpaSoftwareModuleType) type;
|
||||||
|
this.encrypted = encrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addArtifact(final Artifact artifact) {
|
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.
|
* Marks or un-marks this software module as deleted.
|
||||||
*
|
*
|
||||||
* @param deleted
|
* @param deleted
|
||||||
* {@code true} if the software module should be marked as
|
* {@code true} if the software module should be marked as deleted
|
||||||
* deleted otherwise {@code false}
|
* otherwise {@code false}
|
||||||
*/
|
*/
|
||||||
public void setDeleted(final boolean deleted) {
|
public void setDeleted(final boolean deleted) {
|
||||||
this.deleted = deleted;
|
this.deleted = deleted;
|
||||||
@@ -186,10 +206,27 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
|||||||
this.type = type;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "SoftwareModule [deleted=" + deleted + ", name=" + getName() + ", version=" + getVersion()
|
return "SoftwareModule [deleted=" + isDeleted() + ", encrypted=" + isEncrypted() + ", name=" + getName()
|
||||||
+ ", revision=" + getOptLockRevision() + ", Id=" + getId() + ", type=" + getType().getName() + "]";
|
+ ", version=" + getVersion() + ", revision=" + getOptLockRevision() + ", Id=" + getId() + ", type="
|
||||||
|
+ getType().getName() + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE sp_base_software_module ADD COLUMN encrypted BOOLEAN;
|
||||||
|
|
||||||
|
UPDATE sp_base_software_module SET encrypted = 0;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE sp_base_software_module ADD COLUMN encrypted BOOLEAN;
|
||||||
|
|
||||||
|
UPDATE sp_base_software_module SET encrypted = 0;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE sp_base_software_module ADD COLUMN encrypted BOOLEAN;
|
||||||
|
|
||||||
|
UPDATE sp_base_software_module SET encrypted = 0;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE sp_base_software_module ADD COLUMN encrypted BOOLEAN;
|
||||||
|
|
||||||
|
UPDATE sp_base_software_module SET encrypted = 0;
|
||||||
@@ -1,3 +1 @@
|
|||||||
ALTER TABLE sp_distribution_set ADD COLUMN valid BOOLEAN;
|
ALTER TABLE sp_distribution_set ADD valid BIT DEFAULT 1;
|
||||||
|
|
||||||
UPDATE sp_distribution_set SET valid = 1;
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE sp_base_software_module ADD encrypted BIT DEFAULT 0;
|
||||||
@@ -26,7 +26,7 @@ import javax.validation.ConstraintViolationException;
|
|||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
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.artifact.repository.model.DbArtifactHash;
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||||
@@ -77,7 +77,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
.isFalse();
|
.isFalse();
|
||||||
|
|
||||||
assertThat(artifactManagement.findFirstBySHA1(NOT_EXIST_ID)).isNotPresent();
|
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
|
@Test
|
||||||
@@ -116,10 +117,10 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(artifactRepository.findAll()).hasSize(0);
|
assertThat(artifactRepository.findAll()).hasSize(0);
|
||||||
|
|
||||||
final JpaSoftwareModule sm = softwareModuleRepository
|
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
|
final JpaSoftwareModule sm2 = softwareModuleRepository
|
||||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
|
.save(new JpaSoftwareModule(osType, "name 2", "version 2"));
|
||||||
softwareModuleRepository.save(new JpaSoftwareModule(osType, "name 3", "version 3", null, null));
|
softwareModuleRepository.save(new JpaSoftwareModule(osType, "name 3", "version 3"));
|
||||||
|
|
||||||
final int artifactSize = 5 * 1024;
|
final int artifactSize = 5 * 1024;
|
||||||
final byte[] randomBytes = randomBytes(artifactSize);
|
final byte[] randomBytes = randomBytes(artifactSize);
|
||||||
@@ -165,8 +166,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
final String artifactData = "test";
|
final String artifactData = "test";
|
||||||
final int artifactSize = artifactData.length();
|
final int artifactSize = artifactData.length();
|
||||||
|
|
||||||
final long smID = softwareModuleRepository
|
final long smID = softwareModuleRepository.save(new JpaSoftwareModule(osType, "smIllegalFilenameTest", "1.0"))
|
||||||
.save(new JpaSoftwareModule(osType, "smIllegalFilenameTest", "1.0", null, null)).getId();
|
.getId();
|
||||||
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
|
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
|
||||||
() -> artifactManagement.create(new ArtifactUpload(IOUtils.toInputStream(artifactData, "UTF-8"), smID,
|
() -> artifactManagement.create(new ArtifactUpload(IOUtils.toInputStream(artifactData, "UTF-8"), smID,
|
||||||
illegalFilename, false, artifactSize)));
|
illegalFilename, false, artifactSize)));
|
||||||
@@ -178,8 +179,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
public void createArtifactsUntilQuotaIsExceeded() throws IOException {
|
public void createArtifactsUntilQuotaIsExceeded() throws IOException {
|
||||||
|
|
||||||
// create a software module
|
// create a software module
|
||||||
final long smId = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0", null, null))
|
final long smId = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0")).getId();
|
||||||
.getId();
|
|
||||||
|
|
||||||
// now create artifacts for this module until the quota is exceeded
|
// now create artifacts for this module until the quota is exceeded
|
||||||
final long maxArtifacts = quotaManagement.getMaxArtifactsPerSoftwareModule();
|
final long maxArtifacts = quotaManagement.getMaxArtifactsPerSoftwareModule();
|
||||||
@@ -217,14 +217,13 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
final int artifactSize = Math.toIntExact(quotaManagement.getMaxArtifactSize() / 10);
|
final int artifactSize = Math.toIntExact(quotaManagement.getMaxArtifactSize() / 10);
|
||||||
final int numArtifacts = Math.toIntExact(maxBytes / artifactSize);
|
final int numArtifacts = Math.toIntExact(maxBytes / artifactSize);
|
||||||
for (int i = 0; i < numArtifacts; ++i) {
|
for (int i = 0; i < numArtifacts; ++i) {
|
||||||
final JpaSoftwareModule sm = softwareModuleRepository
|
final JpaSoftwareModule sm = softwareModuleRepository.save(new JpaSoftwareModule(osType, "smd" + i, "1.0"));
|
||||||
.save(new JpaSoftwareModule(osType, "smd" + i, "1.0", null, null));
|
|
||||||
artifactIds.add(createArtifactForSoftwareModule("file" + i, sm.getId(), artifactSize).getId());
|
artifactIds.add(createArtifactForSoftwareModule("file" + i, sm.getId(), artifactSize).getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload one more artifact to trigger the quota exceeded error
|
// upload one more artifact to trigger the quota exceeded error
|
||||||
final JpaSoftwareModule sm = softwareModuleRepository
|
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)
|
assertThatExceptionOfType(StorageQuotaExceededException.class)
|
||||||
.isThrownBy(() -> createArtifactForSoftwareModule("file" + numArtifacts, sm.getId(), artifactSize));
|
.isThrownBy(() -> createArtifactForSoftwareModule("file" + numArtifacts, sm.getId(), artifactSize));
|
||||||
|
|
||||||
@@ -240,8 +239,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
public void createArtifactFailsIfTooLarge() {
|
public void createArtifactFailsIfTooLarge() {
|
||||||
|
|
||||||
// create a software module
|
// create a software module
|
||||||
final JpaSoftwareModule sm1 = softwareModuleRepository
|
final JpaSoftwareModule sm1 = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0"));
|
||||||
.save(new JpaSoftwareModule(osType, "sm1", "1.0", null, null));
|
|
||||||
|
|
||||||
// create an artifact that exceeds the configured quota
|
// create an artifact that exceeds the configured quota
|
||||||
final long maxSize = quotaManagement.getMaxArtifactSize();
|
final long maxSize = quotaManagement.getMaxArtifactSize();
|
||||||
@@ -254,7 +252,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
public void hardDeleteSoftwareModule() throws IOException {
|
public void hardDeleteSoftwareModule() throws IOException {
|
||||||
|
|
||||||
final JpaSoftwareModule sm = softwareModuleRepository
|
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);
|
createArtifactForSoftwareModule("file1", sm.getId(), 5 * 1024);
|
||||||
assertThat(artifactRepository.findAll()).hasSize(1);
|
assertThat(artifactRepository.findAll()).hasSize(1);
|
||||||
@@ -273,9 +271,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
@Description("Tests the deletion of a local artifact including metadata.")
|
@Description("Tests the deletion of a local artifact including metadata.")
|
||||||
public void deleteArtifact() throws IOException {
|
public void deleteArtifact() throws IOException {
|
||||||
final JpaSoftwareModule sm = softwareModuleRepository
|
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
|
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();
|
assertThat(artifactRepository.findAll()).isEmpty();
|
||||||
|
|
||||||
@@ -326,9 +324,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
public void deleteDuplicateArtifacts() throws IOException {
|
public void deleteDuplicateArtifacts() throws IOException {
|
||||||
|
|
||||||
final JpaSoftwareModule sm = softwareModuleRepository
|
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
|
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 int artifactSize = 5 * 1024;
|
||||||
final byte[] randomBytes = randomBytes(artifactSize);
|
final byte[] randomBytes = randomBytes(artifactSize);
|
||||||
@@ -368,9 +366,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
public void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInSameTenants() throws IOException {
|
public void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInSameTenants() throws IOException {
|
||||||
|
|
||||||
final JpaSoftwareModule sm = softwareModuleRepository
|
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
|
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 int artifactSize = 5 * 1024;
|
||||||
final byte[] randomBytes = randomBytes(artifactSize);
|
final byte[] randomBytes = randomBytes(artifactSize);
|
||||||
@@ -382,7 +380,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
final Artifact artifact2 = createArtifactForSoftwareModule("file2", sm2.getId(), artifactSize,
|
final Artifact artifact2 = createArtifactForSoftwareModule("file2", sm2.getId(), artifactSize,
|
||||||
inputStream2);
|
inputStream2);
|
||||||
|
|
||||||
assertEqualFileContents(artifactManagement.loadArtifactBinary(artifact2.getSha1Hash()), randomBytes);
|
assertEqualFileContents(
|
||||||
|
artifactManagement.loadArtifactBinary(artifact2.getSha1Hash(), sm2.getId(), sm2.isEncrypted()),
|
||||||
|
randomBytes);
|
||||||
|
|
||||||
assertThat(artifactRepository.findAll()).hasSize(2);
|
assertThat(artifactRepository.findAll()).hasSize(2);
|
||||||
|
|
||||||
@@ -447,9 +447,11 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
final int artifactSize = 5 * 1024;
|
final int artifactSize = 5 * 1024;
|
||||||
final byte[] randomBytes = randomBytes(artifactSize);
|
final byte[] randomBytes = randomBytes(artifactSize);
|
||||||
try (final InputStream input = new ByteArrayInputStream(randomBytes)) {
|
try (final InputStream input = new ByteArrayInputStream(randomBytes)) {
|
||||||
final Artifact artifact = createArtifactForSoftwareModule("file1",
|
final SoftwareModule smOs = testdataFactory.createSoftwareModuleOs();
|
||||||
testdataFactory.createSoftwareModuleOs().getId(), artifactSize, input);
|
final Artifact artifact = createArtifactForSoftwareModule("file1", smOs.getId(), artifactSize, input);
|
||||||
assertEqualFileContents(artifactManagement.loadArtifactBinary(artifact.getSha1Hash()), randomBytes);
|
assertEqualFileContents(
|
||||||
|
artifactManagement.loadArtifactBinary(artifact.getSha1Hash(), smOs.getId(), smOs.isEncrypted()),
|
||||||
|
randomBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,7 +461,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
public void loadArtifactBinaryWithoutDownloadArtifactThrowsPermissionDenied() {
|
public void loadArtifactBinaryWithoutDownloadArtifactThrowsPermissionDenied() {
|
||||||
assertThatExceptionOfType(InsufficientPermissionException.class)
|
assertThatExceptionOfType(InsufficientPermissionException.class)
|
||||||
.as("Should not have worked with missing permission.")
|
.as("Should not have worked with missing permission.")
|
||||||
.isThrownBy(() -> artifactManagement.loadArtifactBinary("123"));
|
.isThrownBy(() -> artifactManagement.loadArtifactBinary("123", 1, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -541,7 +543,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(createdArtifact.getSha256Hash()).isEqualTo(artifactHashes.getSha256());
|
assertThat(createdArtifact.getSha256Hash()).isEqualTo(artifactHashes.getSha256());
|
||||||
}
|
}
|
||||||
|
|
||||||
final Optional<AbstractDbArtifact> dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1());
|
final Optional<DbArtifact> dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1(),
|
||||||
|
sm.getId(), sm.isEncrypted());
|
||||||
assertThat(dbArtifact).isPresent();
|
assertThat(dbArtifact).isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,7 +564,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
final Artifact artifact = artifactManagement.create(artifactUpload);
|
final Artifact artifact = artifactManagement.create(artifactUpload);
|
||||||
assertThat(artifact).isNotNull();
|
assertThat(artifact).isNotNull();
|
||||||
}
|
}
|
||||||
final Optional<AbstractDbArtifact> dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1());
|
final Optional<DbArtifact> dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1(),
|
||||||
|
smOs.getId(), smOs.isEncrypted());
|
||||||
assertThat(dbArtifact).isPresent();
|
assertThat(dbArtifact).isPresent();
|
||||||
|
|
||||||
try (final InputStream inputStream = new ByteArrayInputStream(testData)) {
|
try (final InputStream inputStream = new ByteArrayInputStream(testData)) {
|
||||||
@@ -622,10 +626,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(runAsTenant(tenant, () -> artifactRepository.findAll())).hasSize(count);
|
assertThat(runAsTenant(tenant, () -> artifactRepository.findAll())).hasSize(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertEqualFileContents(final Optional<AbstractDbArtifact> artifact, final byte[] randomBytes)
|
private void assertEqualFileContents(final Optional<DbArtifact> artifact, final byte[] randomBytes)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try (final InputStream inputStream = artifactManagement.loadArtifactBinary(artifact.get().getHashes().getSha1())
|
try (final InputStream inputStream = artifact.get().getFileInputStream()) {
|
||||||
.get().getFileInputStream()) {
|
|
||||||
assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(randomBytes), inputStream),
|
assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(randomBytes), inputStream),
|
||||||
"The stored binary matches the given binary");
|
"The stored binary matches the given binary");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -520,13 +520,13 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
Arrays.asList(testType.getId()));
|
Arrays.asList(testType.getId()));
|
||||||
|
|
||||||
// found in test
|
// found in test
|
||||||
final SoftwareModule unassigned = testdataFactory.createSoftwareModule("thetype", "unassignedfound");
|
final SoftwareModule unassigned = testdataFactory.createSoftwareModule("thetype", "unassignedfound", false);
|
||||||
final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound");
|
final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound", false);
|
||||||
final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound");
|
final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound", false);
|
||||||
final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "a");
|
final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "a", false);
|
||||||
|
|
||||||
// ignored
|
// ignored
|
||||||
final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted");
|
final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted", false);
|
||||||
final SoftwareModule four = testdataFactory.createSoftwareModuleOs("e");
|
final SoftwareModule four = testdataFactory.createSoftwareModuleOs("e");
|
||||||
|
|
||||||
final DistributionSet set = distributionSetManagement
|
final DistributionSet set = distributionSetManagement
|
||||||
@@ -572,13 +572,13 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
Arrays.asList(testType.getId()));
|
Arrays.asList(testType.getId()));
|
||||||
|
|
||||||
// found in test
|
// found in test
|
||||||
testdataFactory.createSoftwareModule("thetype", "unassignedfound");
|
testdataFactory.createSoftwareModule("thetype", "unassignedfound", false);
|
||||||
final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound");
|
final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound", false);
|
||||||
final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound");
|
final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound", false);
|
||||||
final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "d");
|
final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "d", false);
|
||||||
|
|
||||||
// ignored
|
// ignored
|
||||||
final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted");
|
final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted", false);
|
||||||
final SoftwareModule four = testdataFactory.createSoftwareModuleOs("e");
|
final SoftwareModule four = testdataFactory.createSoftwareModuleOs("e");
|
||||||
|
|
||||||
distributionSetManagement
|
distributionSetManagement
|
||||||
|
|||||||
@@ -540,7 +540,7 @@ public class TestdataFactory {
|
|||||||
* @return persisted {@link SoftwareModule}.
|
* @return persisted {@link SoftwareModule}.
|
||||||
*/
|
*/
|
||||||
public SoftwareModule createSoftwareModule(final String typeKey) {
|
public SoftwareModule createSoftwareModule(final String typeKey) {
|
||||||
return createSoftwareModule(typeKey, "");
|
return createSoftwareModule(typeKey, "", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -552,7 +552,7 @@ public class TestdataFactory {
|
|||||||
* @return persisted {@link SoftwareModule}.
|
* @return persisted {@link SoftwareModule}.
|
||||||
*/
|
*/
|
||||||
public SoftwareModule createSoftwareModuleApp() {
|
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}.
|
* @return persisted {@link SoftwareModule}.
|
||||||
*/
|
*/
|
||||||
public SoftwareModule createSoftwareModuleApp(final String prefix) {
|
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}.
|
* @return persisted {@link SoftwareModule}.
|
||||||
*/
|
*/
|
||||||
public SoftwareModule createSoftwareModuleOs() {
|
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}.
|
* @return persisted {@link SoftwareModule}.
|
||||||
*/
|
*/
|
||||||
public SoftwareModule createSoftwareModuleOs(final String prefix) {
|
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}.
|
* @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()
|
return softwareModuleManagement.create(entityFactory.softwareModule().create()
|
||||||
.type(findOrCreateSoftwareModuleType(typeKey)).name(prefix + typeKey).version(prefix + DEFAULT_VERSION)
|
.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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ public class DdiChunk {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@JsonProperty("encrypted")
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
private Boolean encrypted;
|
||||||
|
|
||||||
@JsonProperty("artifacts")
|
@JsonProperty("artifacts")
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
private List<DdiArtifact> artifacts;
|
private List<DdiArtifact> artifacts;
|
||||||
@@ -56,16 +60,19 @@ public class DdiChunk {
|
|||||||
* of the artifact
|
* of the artifact
|
||||||
* @param name
|
* @param name
|
||||||
* of the artifact
|
* of the artifact
|
||||||
|
* @param encrypted
|
||||||
|
* if artifacts are encrypted
|
||||||
* @param artifacts
|
* @param artifacts
|
||||||
* download information
|
* download information
|
||||||
* @param metadata
|
* @param metadata
|
||||||
* optional as additional information for the target/device
|
* optional as additional information for the target/device
|
||||||
*/
|
*/
|
||||||
public DdiChunk(final String part, final String version, final String name, final List<DdiArtifact> artifacts,
|
public DdiChunk(final String part, final String version, final String name, final Boolean encrypted,
|
||||||
final List<DdiMetadata> metadata) {
|
final List<DdiArtifact> artifacts, final List<DdiMetadata> metadata) {
|
||||||
this.part = part;
|
this.part = part;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.encrypted = encrypted;
|
||||||
this.artifacts = artifacts;
|
this.artifacts = artifacts;
|
||||||
this.metadata = metadata;
|
this.metadata = metadata;
|
||||||
}
|
}
|
||||||
@@ -82,6 +89,10 @@ public class DdiChunk {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean isEncrypted() {
|
||||||
|
return encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
public List<DdiArtifact> getArtifacts() {
|
public List<DdiArtifact> getArtifacts() {
|
||||||
if (artifacts == null) {
|
if (artifacts == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|||||||
@@ -32,21 +32,21 @@ import io.qameta.allure.Story;
|
|||||||
@Story("Serializability of DDI api Models")
|
@Story("Serializability of DDI api Models")
|
||||||
public class DdiChunkTest {
|
public class DdiChunkTest {
|
||||||
|
|
||||||
private ObjectMapper mapper = new ObjectMapper();
|
private final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verify the correct serialization and deserialization of the model")
|
@Description("Verify the correct serialization and deserialization of the model")
|
||||||
public void shouldSerializeAndDeserializeObject() throws IOException {
|
public void shouldSerializeAndDeserializeObject() throws IOException {
|
||||||
// Setup
|
// Setup
|
||||||
String part = "1234";
|
final String part = "1234";
|
||||||
String version = "1.0";
|
final String version = "1.0";
|
||||||
String name = "Dummy-Artifact";
|
final String name = "Dummy-Artifact";
|
||||||
List<DdiArtifact> dummyArtifacts = Collections.emptyList();
|
final List<DdiArtifact> dummyArtifacts = Collections.emptyList();
|
||||||
DdiChunk ddiChunk = new DdiChunk(part, version, name, dummyArtifacts, null);
|
final DdiChunk ddiChunk = new DdiChunk(part, version, name, null, dummyArtifacts, null);
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
String serializedDdiChunk = mapper.writeValueAsString(ddiChunk);
|
final String serializedDdiChunk = mapper.writeValueAsString(ddiChunk);
|
||||||
DdiChunk deserializedDdiChunk = mapper.readValue(serializedDdiChunk, DdiChunk.class);
|
final DdiChunk deserializedDdiChunk = mapper.readValue(serializedDdiChunk, DdiChunk.class);
|
||||||
|
|
||||||
assertThat(serializedDdiChunk).contains(part, version, name);
|
assertThat(serializedDdiChunk).contains(part, version, name);
|
||||||
assertThat(deserializedDdiChunk.getPart()).isEqualTo(part);
|
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")
|
@Description("Verify the correct deserialization of a model with a additional unknown property")
|
||||||
public void shouldDeserializeObjectWithUnknownProperty() throws IOException {
|
public void shouldDeserializeObjectWithUnknownProperty() throws IOException {
|
||||||
// Setup
|
// 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
|
// Test
|
||||||
DdiChunk ddiChunk = mapper.readValue(serializedDdiChunk, DdiChunk.class);
|
final DdiChunk ddiChunk = mapper.readValue(serializedDdiChunk, DdiChunk.class);
|
||||||
|
|
||||||
assertThat(ddiChunk.getPart()).isEqualTo("1234");
|
assertThat(ddiChunk.getPart()).isEqualTo("1234");
|
||||||
assertThat(ddiChunk.getVersion()).isEqualTo("1.0");
|
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")
|
@Description("Verify that deserialization fails for known properties with a wrong datatype")
|
||||||
public void shouldFailForObjectWithWrongDataTypes() throws IOException {
|
public void shouldFailForObjectWithWrongDataTypes() throws IOException {
|
||||||
// Setup
|
// 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
|
// Test
|
||||||
assertThatExceptionOfType(MismatchedInputException.class).isThrownBy(
|
assertThatExceptionOfType(MismatchedInputException.class)
|
||||||
() -> mapper.readValue(serializedDdiChunk, DdiChunk.class));
|
.isThrownBy(() -> mapper.readValue(serializedDdiChunk, DdiChunk.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ public final class DataConversionHelper {
|
|||||||
|
|
||||||
return new ResponseList<>(uAction.getDistributionSet().getModules().stream()
|
return new ResponseList<>(uAction.getDistributionSet().getModules().stream()
|
||||||
.map(module -> new DdiChunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(),
|
.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),
|
createArtifacts(target, module, artifactUrlHandler, systemManagement, request),
|
||||||
mapMetadata(metadata.get(module.getId()))))
|
mapMetadata(metadata.get(module.getId()))))
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import javax.validation.Valid;
|
|||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
|
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.DdiActionFeedback;
|
||||||
import org.eclipse.hawkbit.ddi.json.model.DdiActionHistory;
|
import org.eclipse.hawkbit.ddi.json.model.DdiActionHistory;
|
||||||
import org.eclipse.hawkbit.ddi.json.model.DdiArtifact;
|
import org.eclipse.hawkbit.ddi.json.model.DdiArtifact;
|
||||||
@@ -183,7 +183,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
|||||||
@SuppressWarnings("squid:S3655")
|
@SuppressWarnings("squid:S3655")
|
||||||
final Artifact artifact = module.getArtifactByFilename(fileName).get();
|
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()));
|
.orElseThrow(() -> new ArtifactBinaryNotFoundException(artifact.getSha1Hash()));
|
||||||
|
|
||||||
final String ifMatch = requestResponseContextHolder.getHttpServletRequest().getHeader(HttpHeaders.IF_MATCH);
|
final String ifMatch = requestResponseContextHolder.getHttpServletRequest().getHeader(HttpHeaders.IF_MATCH);
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ public class MgmtSoftwareModule extends MgmtNamedEntity {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private boolean deleted;
|
private boolean deleted;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private boolean encrypted;
|
||||||
|
|
||||||
public void setDeleted(final boolean deleted) {
|
public void setDeleted(final boolean deleted) {
|
||||||
this.deleted = deleted;
|
this.deleted = deleted;
|
||||||
}
|
}
|
||||||
@@ -79,4 +82,12 @@ public class MgmtSoftwareModule extends MgmtNamedEntity {
|
|||||||
this.vendor = vendor;
|
this.vendor = vendor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEncrypted(final boolean encrypted) {
|
||||||
|
this.encrypted = encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEncrypted() {
|
||||||
|
return encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ public class MgmtSoftwareModuleRequestBodyPost {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private String vendor;
|
private String vendor;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private boolean encrypted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the name
|
* @return the name
|
||||||
*/
|
*/
|
||||||
@@ -121,4 +124,22 @@ public class MgmtSoftwareModuleRequestBodyPost {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return if encrypted
|
||||||
|
*/
|
||||||
|
public boolean isEncrypted() {
|
||||||
|
return encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param encrypted
|
||||||
|
* if should be encrypted
|
||||||
|
*
|
||||||
|
* @return updated body
|
||||||
|
*/
|
||||||
|
public MgmtSoftwareModuleRequestBodyPost setEncrypted(final boolean encrypted) {
|
||||||
|
this.encrypted = encrypted;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import java.io.InputStream;
|
|||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import 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.mgmt.rest.api.MgmtDownloadArtifactRestApi;
|
||||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||||
@@ -70,7 +70,8 @@ public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi
|
|||||||
final Artifact artifact = module.getArtifact(artifactId)
|
final Artifact artifact = module.getArtifact(artifactId)
|
||||||
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, 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()));
|
.orElseThrow(() -> new ArtifactBinaryNotFoundException(artifact.getSha1Hash()));
|
||||||
final HttpServletRequest request = requestResponseContextHolder.getHttpServletRequest();
|
final HttpServletRequest request = requestResponseContextHolder.getHttpServletRequest();
|
||||||
final String ifMatch = request.getHeader(HttpHeaders.IF_MATCH);
|
final String ifMatch = request.getHeader(HttpHeaders.IF_MATCH);
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ public final class MgmtSoftwareModuleMapper {
|
|||||||
private static SoftwareModuleCreate fromRequest(final EntityFactory entityFactory,
|
private static SoftwareModuleCreate fromRequest(final EntityFactory entityFactory,
|
||||||
final MgmtSoftwareModuleRequestBodyPost smsRest) {
|
final MgmtSoftwareModuleRequestBodyPost smsRest) {
|
||||||
return entityFactory.softwareModule().create().type(smsRest.getType()).name(smsRest.getName())
|
return entityFactory.softwareModule().create().type(smsRest.getType()).name(smsRest.getName())
|
||||||
.version(smsRest.getVersion()).description(smsRest.getDescription()).vendor(smsRest.getVendor());
|
.version(smsRest.getVersion()).description(smsRest.getDescription()).vendor(smsRest.getVendor())
|
||||||
|
.encrypted(smsRest.isEncrypted());
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<SoftwareModuleMetadataCreate> fromRequestSwMetadata(final EntityFactory entityFactory,
|
static List<SoftwareModuleMetadataCreate> fromRequestSwMetadata(final EntityFactory entityFactory,
|
||||||
@@ -107,6 +108,7 @@ public final class MgmtSoftwareModuleMapper {
|
|||||||
response.setType(softwareModule.getType().getKey());
|
response.setType(softwareModule.getType().getKey());
|
||||||
response.setVendor(softwareModule.getVendor());
|
response.setVendor(softwareModule.getVendor());
|
||||||
response.setDeleted(softwareModule.isDeleted());
|
response.setDeleted(softwareModule.isDeleted());
|
||||||
|
response.setEncrypted(softwareModule.isEncrypted());
|
||||||
|
|
||||||
response.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class).getSoftwareModule(response.getModuleId()))
|
response.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class).getSoftwareModule(response.getModuleId()))
|
||||||
.withSelfRel());
|
.withSelfRel());
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
@@ -199,7 +200,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
@Test
|
@Test
|
||||||
@Description("Verifies that artifacts which exceed the configured maximum size cannot be uploaded.")
|
@Description("Verifies that artifacts which exceed the configured maximum size cannot be uploaded.")
|
||||||
public void uploadArtifactFailsIfTooLarge() throws Exception {
|
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();
|
final long maxSize = quotaManagement.getMaxArtifactSize();
|
||||||
|
|
||||||
// create a file which exceeds the configured maximum size
|
// create a file which exceeds the configured maximum size
|
||||||
@@ -218,7 +219,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
@Test
|
@Test
|
||||||
@Description("Verifies that artifact with invalid filename cannot be uploaded to prevent cross site scripting.")
|
@Description("Verifies that artifact with invalid filename cannot be uploaded to prevent cross site scripting.")
|
||||||
public void uploadArtifactFailsIfFilenameInvalide() throws Exception {
|
public void uploadArtifactFailsIfFilenameInvalide() throws Exception {
|
||||||
final SoftwareModule sm = testdataFactory.createSoftwareModule("quota", "quota");
|
final SoftwareModule sm = testdataFactory.createSoftwareModule("quota", "quota", false);
|
||||||
final String illegalFilename = "<img src=ernw onerror=alert(1)>.xml";
|
final String illegalFilename = "<img src=ernw onerror=alert(1)>.xml";
|
||||||
|
|
||||||
final byte[] randomBytes = randomBytes(5 * 1024);
|
final byte[] randomBytes = randomBytes(5 * 1024);
|
||||||
@@ -236,11 +237,12 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
assertThat(artifactManagement.count()).as("Wrong artifact size").isEqualTo(1);
|
assertThat(artifactManagement.count()).as("Wrong artifact size").isEqualTo(1);
|
||||||
|
|
||||||
// binary
|
// binary
|
||||||
try (InputStream fileInputStream = artifactManagement
|
try (final InputStream fileInputStream = artifactManagement
|
||||||
.loadArtifactBinary(softwareModuleManagement.get(sm.getId()).get().getArtifacts().get(0).getSha1Hash())
|
.loadArtifactBinary(softwareModuleManagement.get(sm.getId()).get().getArtifacts().get(0).getSha1Hash(),
|
||||||
|
sm.getId(), sm.isEncrypted())
|
||||||
.get().getFileInputStream()) {
|
.get().getFileInputStream()) {
|
||||||
assertTrue(
|
assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream),
|
||||||
IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream), "Wrong artifact content");
|
"Wrong artifact content");
|
||||||
}
|
}
|
||||||
|
|
||||||
// hashes
|
// hashes
|
||||||
@@ -662,6 +664,14 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)).andDo(MockMvcResultPrinter.print())
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)).andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isUnsupportedMediaType());
|
.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
|
// not allowed methods
|
||||||
mvc.perform(put("/rest/v1/softwaremodules")).andDo(MockMvcResultPrinter.print())
|
mvc.perform(put("/rest/v1/softwaremodules")).andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isMethodNotAllowed());
|
.andExpect(status().isMethodNotAllowed());
|
||||||
|
|||||||
@@ -8,14 +8,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.rest.exception;
|
package org.eclipse.hawkbit.rest.exception;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.validation.ConstraintViolationException;
|
import javax.validation.ConstraintViolationException;
|
||||||
import javax.validation.ValidationException;
|
import javax.validation.ValidationException;
|
||||||
|
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||||
import org.eclipse.hawkbit.exception.SpServerError;
|
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.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.multipart.MultipartException;
|
import org.springframework.web.multipart.MultipartException;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General controller advice for exception handling.
|
* 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_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_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_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_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_SHA256_MATCH, HttpStatus.BAD_REQUEST);
|
||||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH, HttpStatus.BAD_REQUEST);
|
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH, HttpStatus.BAD_REQUEST);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import javax.servlet.ServletOutputStream;
|
|||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
@@ -118,9 +118,9 @@ public final class FileStreamingUtil {
|
|||||||
* @throws FileStreamingFailedException
|
* @throws FileStreamingFailedException
|
||||||
* if streaming fails
|
* if streaming fails
|
||||||
*/
|
*/
|
||||||
public static ResponseEntity<InputStream> writeFileResponse(final AbstractDbArtifact artifact,
|
public static ResponseEntity<InputStream> writeFileResponse(final DbArtifact artifact, final String filename,
|
||||||
final String filename, final long lastModified, final HttpServletResponse response,
|
final long lastModified, final HttpServletResponse response, final HttpServletRequest request,
|
||||||
final HttpServletRequest request, final FileStreamingProgressListener progressListener) {
|
final FileStreamingProgressListener progressListener) {
|
||||||
|
|
||||||
ResponseEntity<InputStream> result;
|
ResponseEntity<InputStream> result;
|
||||||
|
|
||||||
@@ -189,9 +189,9 @@ public final class FileStreamingUtil {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResponseEntity<InputStream> handleFullFileRequest(final AbstractDbArtifact artifact,
|
private static ResponseEntity<InputStream> handleFullFileRequest(final DbArtifact artifact, final String filename,
|
||||||
final String filename, final HttpServletResponse response,
|
final HttpServletResponse response, final FileStreamingProgressListener progressListener,
|
||||||
final FileStreamingProgressListener progressListener, final ByteRange full) {
|
final ByteRange full) {
|
||||||
final ByteRange r = full;
|
final ByteRange r = full;
|
||||||
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + r.getStart() + "-" + r.getEnd() + "/" + r.getTotal());
|
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + r.getStart() + "-" + r.getEnd() + "/" + r.getTotal());
|
||||||
response.setContentLengthLong(r.getLength());
|
response.setContentLengthLong(r.getLength());
|
||||||
@@ -257,7 +257,7 @@ public final class FileStreamingUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResponseEntity<InputStream> handleMultipartRangeRequest(final AbstractDbArtifact artifact,
|
private static ResponseEntity<InputStream> handleMultipartRangeRequest(final DbArtifact artifact,
|
||||||
final String filename, final HttpServletResponse response,
|
final String filename, final HttpServletResponse response,
|
||||||
final FileStreamingProgressListener progressListener, final List<ByteRange> ranges) {
|
final FileStreamingProgressListener progressListener, final List<ByteRange> ranges) {
|
||||||
|
|
||||||
@@ -291,7 +291,7 @@ public final class FileStreamingUtil {
|
|||||||
return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).build();
|
return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResponseEntity<InputStream> handleStandardRangeRequest(final AbstractDbArtifact artifact,
|
private static ResponseEntity<InputStream> handleStandardRangeRequest(final DbArtifact artifact,
|
||||||
final String filename, final HttpServletResponse response,
|
final String filename, final HttpServletResponse response,
|
||||||
final FileStreamingProgressListener progressListener, final List<ByteRange> ranges) {
|
final FileStreamingProgressListener progressListener, final List<ByteRange> ranges) {
|
||||||
final ByteRange r = ranges.get(0);
|
final ByteRange r = ranges.get(0);
|
||||||
|
|||||||
@@ -97,7 +97,8 @@ public abstract class JsonBuilder {
|
|||||||
builder.append(new JSONObject().put("name", module.getName()).put("description", module.getDescription())
|
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("type", module.getType().getKey()).put("id", Long.MAX_VALUE).put("vendor", module.getVendor())
|
||||||
.put("version", module.getVersion()).put("createdAt", "0").put("updatedAt", "0")
|
.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()) {
|
if (++i < modules.size()) {
|
||||||
builder.append(",");
|
builder.append(",");
|
||||||
@@ -447,7 +448,8 @@ public abstract class JsonBuilder {
|
|||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String targets(final List<Target> targets, final boolean withToken, final long targetTypeId) throws JSONException {
|
public static String targets(final List<Target> targets, final boolean withToken, final long targetTypeId)
|
||||||
|
throws JSONException {
|
||||||
final StringBuilder builder = new StringBuilder();
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
builder.append("[");
|
builder.append("[");
|
||||||
@@ -487,8 +489,8 @@ public abstract class JsonBuilder {
|
|||||||
});
|
});
|
||||||
|
|
||||||
result.put(new JSONObject().put("name", type.getName()).put("description", type.getDescription())
|
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("id", Long.MAX_VALUE).put("colour", type.getColour()).put("createdAt", "0")
|
||||||
.put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh")
|
.put("updatedAt", "0").put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh")
|
||||||
.put("distributionsets", dsTypes));
|
.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());
|
.put("colour", type.getColour());
|
||||||
|
|
||||||
if(dsTypes.length() != 0)
|
if (dsTypes.length() != 0) {
|
||||||
{
|
|
||||||
json.put("compatibledistributionsettypes", dsTypes);
|
json.put("compatibledistributionsettypes", dsTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ final class DdiApiModelProperties {
|
|||||||
|
|
||||||
static final String CHUNK_TYPE = "Type of the chunk, e.g. firmware, bundle, app. In update server mapped to Software Module Type.";
|
static final String 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";
|
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_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";
|
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 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 ARTIFACT = "artifact modules of an update";
|
||||||
|
|
||||||
static final String FILENAME = "file name of artifact";
|
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 "
|
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 "
|
+ "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).";
|
+ "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_SHA1 = "SHA1 hash of the artifact in Base 16 format";
|
||||||
static final String ARTIFACT_HASHES_MD5 = "MD5 hash of the artifact";
|
static final String ARTIFACT_HASHES_MD5 = "MD5 hash of the artifact";
|
||||||
|
|||||||
@@ -183,7 +183,9 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
|
|||||||
pathParameters(parameterWithName("tenant").description(ApiModelPropertiesGeneric.TENANT),
|
pathParameters(parameterWithName("tenant").description(ApiModelPropertiesGeneric.TENANT),
|
||||||
parameterWithName("controllerId").description(DdiApiModelProperties.CONTROLLER_ID),
|
parameterWithName("controllerId").description(DdiApiModelProperties.CONTROLLER_ID),
|
||||||
parameterWithName("actionId").description(DdiApiModelProperties.ACTION_ID_CANCELED)),
|
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").description(DdiApiModelProperties.TARGET_STATUS),
|
||||||
requestFieldWithPath("status.execution")
|
requestFieldWithPath("status.execution")
|
||||||
.description(DdiApiModelProperties.TARGET_EXEC_STATUS).type("enum")
|
.description(DdiApiModelProperties.TARGET_EXEC_STATUS).type("enum")
|
||||||
@@ -386,7 +388,9 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
|
|||||||
parameterWithName("controllerId").description(DdiApiModelProperties.CONTROLLER_ID),
|
parameterWithName("controllerId").description(DdiApiModelProperties.CONTROLLER_ID),
|
||||||
parameterWithName("actionId").description(DdiApiModelProperties.ACTION_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").description(DdiApiModelProperties.TARGET_STATUS),
|
||||||
requestFieldWithPath("status.execution")
|
requestFieldWithPath("status.execution")
|
||||||
.description(DdiApiModelProperties.TARGET_EXEC_STATUS).type("enum")
|
.description(DdiApiModelProperties.TARGET_EXEC_STATUS).type("enum")
|
||||||
@@ -428,7 +432,7 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
|
|||||||
.andDo(this.document.document(
|
.andDo(this.document.document(
|
||||||
pathParameters(parameterWithName("tenant").description(ApiModelPropertiesGeneric.TENANT),
|
pathParameters(parameterWithName("tenant").description(ApiModelPropertiesGeneric.TENANT),
|
||||||
parameterWithName("controllerId").description(DdiApiModelProperties.CONTROLLER_ID),
|
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),
|
responseFields(fieldWithPath("[]filename").description(DdiApiModelProperties.ARTIFACTS),
|
||||||
fieldWithPath("[]hashes").description(DdiApiModelProperties.ARTIFACTS),
|
fieldWithPath("[]hashes").description(DdiApiModelProperties.ARTIFACTS),
|
||||||
fieldWithPath("[]hashes.sha1").description(DdiApiModelProperties.ARTIFACT_HASHES_SHA1),
|
fieldWithPath("[]hashes.sha1").description(DdiApiModelProperties.ARTIFACT_HASHES_SHA1),
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public final class MgmtApiModelProperties {
|
|||||||
|
|
||||||
// software module
|
// software module
|
||||||
public static final String SM_TYPE = "The software module type " + ApiModelPropertiesGeneric.ENDING;
|
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_HASHES = "Hashes of the artifact.";
|
||||||
public static final String ARTIFACT_SIZE = "Size of the artifact.";
|
public static final String ARTIFACT_SIZE = "Size of the artifact.";
|
||||||
public static final String ARTIFACT_PROVIDED_FILE = "Binary of file.";
|
public static final String ARTIFACT_PROVIDED_FILE = "Binary of file.";
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati
|
|||||||
fieldWithPath("content[].name").description(ApiModelPropertiesGeneric.NAME),
|
fieldWithPath("content[].name").description(ApiModelPropertiesGeneric.NAME),
|
||||||
fieldWithPath("content[].description").description(ApiModelPropertiesGeneric.DESCRPTION),
|
fieldWithPath("content[].description").description(ApiModelPropertiesGeneric.DESCRPTION),
|
||||||
fieldWithPath("content[].vendor").description(MgmtApiModelProperties.VENDOR),
|
fieldWithPath("content[].vendor").description(MgmtApiModelProperties.VENDOR),
|
||||||
|
fieldWithPath("content[].encrypted").description(MgmtApiModelProperties.ENCRYPTED),
|
||||||
fieldWithPath("content[].createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
fieldWithPath("content[].createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||||
fieldWithPath("content[].createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
fieldWithPath("content[].createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||||
fieldWithPath("content[].lastModifiedBy")
|
fieldWithPath("content[].lastModifiedBy")
|
||||||
@@ -141,6 +142,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati
|
|||||||
fieldWithPath("[].name").description(ApiModelPropertiesGeneric.NAME),
|
fieldWithPath("[].name").description(ApiModelPropertiesGeneric.NAME),
|
||||||
fieldWithPath("[].description").description(ApiModelPropertiesGeneric.DESCRPTION),
|
fieldWithPath("[].description").description(ApiModelPropertiesGeneric.DESCRPTION),
|
||||||
fieldWithPath("[].vendor").description(MgmtApiModelProperties.VENDOR),
|
fieldWithPath("[].vendor").description(MgmtApiModelProperties.VENDOR),
|
||||||
|
fieldWithPath("[].encrypted").description(MgmtApiModelProperties.ENCRYPTED),
|
||||||
fieldWithPath("[].deleted").description(ApiModelPropertiesGeneric.DELETED),
|
fieldWithPath("[].deleted").description(ApiModelPropertiesGeneric.DELETED),
|
||||||
fieldWithPath("[].createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
fieldWithPath("[].createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||||
fieldWithPath("[].createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
fieldWithPath("[].createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||||
@@ -188,6 +190,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati
|
|||||||
fieldWithPath("lastModifiedBy").description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY),
|
fieldWithPath("lastModifiedBy").description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY),
|
||||||
fieldWithPath("lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT),
|
fieldWithPath("lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT),
|
||||||
fieldWithPath("vendor").description(MgmtApiModelProperties.VENDOR),
|
fieldWithPath("vendor").description(MgmtApiModelProperties.VENDOR),
|
||||||
|
fieldWithPath("encrypted").description(MgmtApiModelProperties.ENCRYPTED),
|
||||||
fieldWithPath("deleted").description(ApiModelPropertiesGeneric.DELETED),
|
fieldWithPath("deleted").description(ApiModelPropertiesGeneric.DELETED),
|
||||||
fieldWithPath("type").description(MgmtApiModelProperties.SM_TYPE),
|
fieldWithPath("type").description(MgmtApiModelProperties.SM_TYPE),
|
||||||
fieldWithPath("version").description(MgmtApiModelProperties.VERSION),
|
fieldWithPath("version").description(MgmtApiModelProperties.VERSION),
|
||||||
@@ -227,6 +230,7 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati
|
|||||||
fieldWithPath("type").description(MgmtApiModelProperties.SM_TYPE),
|
fieldWithPath("type").description(MgmtApiModelProperties.SM_TYPE),
|
||||||
fieldWithPath("version").description(MgmtApiModelProperties.VERSION),
|
fieldWithPath("version").description(MgmtApiModelProperties.VERSION),
|
||||||
fieldWithPath("vendor").description(MgmtApiModelProperties.VENDOR),
|
fieldWithPath("vendor").description(MgmtApiModelProperties.VENDOR),
|
||||||
|
fieldWithPath("encrypted").description(MgmtApiModelProperties.ENCRYPTED),
|
||||||
fieldWithPath("deleted").description(ApiModelPropertiesGeneric.DELETED),
|
fieldWithPath("deleted").description(ApiModelPropertiesGeneric.DELETED),
|
||||||
fieldWithPath("_links.self").ignored(),
|
fieldWithPath("_links.self").ignored(),
|
||||||
fieldWithPath("_links.type").description(MgmtApiModelProperties.SM_TYPE),
|
fieldWithPath("_links.type").description(MgmtApiModelProperties.SM_TYPE),
|
||||||
@@ -279,7 +283,9 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati
|
|||||||
final byte random[] = RandomStringUtils.random(5).getBytes();
|
final byte random[] = RandomStringUtils.random(5).getBytes();
|
||||||
final MockMultipartFile file = new MockMultipartFile("file", "origFilename", null, random);
|
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())
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
||||||
.andExpect(content().contentType(MediaTypes.HAL_JSON))
|
.andExpect(content().contentType(MediaTypes.HAL_JSON))
|
||||||
.andDo(this.document.document(
|
.andDo(this.document.document(
|
||||||
@@ -313,7 +319,8 @@ public class SoftwaremodulesDocumentationTest extends AbstractApiRestDocumentati
|
|||||||
final byte random[] = RandomStringUtils.random(5).getBytes();
|
final byte random[] = RandomStringUtils.random(5).getBytes();
|
||||||
final MockMultipartFile file = new MockMultipartFile("file", "origFilename", null, random);
|
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")
|
sm.getId()).file(file).param("filename", "filename").param("file", "s")
|
||||||
.param("md5sum", "md5sum").param("sha1sum", "sha1sum"))
|
.param("md5sum", "md5sum").param("sha1sum", "sha1sum"))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest())
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest())
|
||||||
|
|||||||
@@ -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.TargetManagementStateDataSupplier;
|
||||||
import org.eclipse.hawkbit.ui.common.data.suppliers.TargetManagementStateDataSupplierImpl;
|
import org.eclipse.hawkbit.ui.common.data.suppliers.TargetManagementStateDataSupplierImpl;
|
||||||
import org.eclipse.hawkbit.ui.error.HawkbitUIErrorHandler;
|
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.ConstraintViolationErrorExtractor;
|
||||||
import org.eclipse.hawkbit.ui.error.extractors.EntityNotFoundErrorExtractor;
|
import org.eclipse.hawkbit.ui.error.extractors.EntityNotFoundErrorExtractor;
|
||||||
import org.eclipse.hawkbit.ui.error.extractors.IncompatibleTargetTypeErrorExtractor;
|
import org.eclipse.hawkbit.ui.error.extractors.IncompatibleTargetTypeErrorExtractor;
|
||||||
@@ -182,6 +183,18 @@ public class MgmtUiConfiguration {
|
|||||||
return new InvalidDistributionSetErrorExtractor(i18n);
|
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.
|
* Vaadin4Spring servlet bean.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.stream.Collectors;
|
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.repository.ArtifactManagement;
|
||||||
import org.eclipse.hawkbit.ui.common.CommonUiDependencies;
|
import org.eclipse.hawkbit.ui.common.CommonUiDependencies;
|
||||||
import org.eclipse.hawkbit.ui.common.builder.GridComponentBuilder;
|
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.UIComponentIdProvider;
|
||||||
import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
|
import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
|
||||||
import org.eclipse.hawkbit.ui.utils.UINotification;
|
import org.eclipse.hawkbit.ui.utils.UINotification;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.vaadin.icons.VaadinIcons;
|
import com.vaadin.icons.VaadinIcons;
|
||||||
import com.vaadin.server.FileDownloader;
|
import com.vaadin.server.FileDownloader;
|
||||||
import com.vaadin.server.StreamResource;
|
import com.vaadin.server.StreamResource;
|
||||||
import com.vaadin.ui.Button;
|
import com.vaadin.ui.Button;
|
||||||
|
import com.vaadin.ui.UI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Artifact Details grid which is shown on the Upload View.
|
* Artifact Details grid which is shown on the Upload View.
|
||||||
@@ -45,6 +48,8 @@ import com.vaadin.ui.Button;
|
|||||||
public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
|
public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
|
||||||
private static final long serialVersionUID = 1L;
|
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_NAME_ID = "artifactName";
|
||||||
private static final String ARTIFACT_SIZE_ID = "artifactSize";
|
private static final String ARTIFACT_SIZE_ID = "artifactSize";
|
||||||
private static final String ARTIFACT_MODIFIED_DATE_ID = "artifactModifiedDate";
|
private static final String ARTIFACT_MODIFIED_DATE_ID = "artifactModifiedDate";
|
||||||
@@ -60,6 +65,8 @@ public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
|
|||||||
private final transient DeleteSupport<ProxyArtifact> artifactDeleteSupport;
|
private final transient DeleteSupport<ProxyArtifact> artifactDeleteSupport;
|
||||||
private final transient MasterEntitySupport<ProxySoftwareModule> masterEntitySupport;
|
private final transient MasterEntitySupport<ProxySoftwareModule> masterEntitySupport;
|
||||||
|
|
||||||
|
private boolean artifactsEncrypted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@@ -82,7 +89,8 @@ public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
|
|||||||
new FilterSupport<>(new ArtifactDataProvider(artifactManagement, new ArtifactToProxyArtifactMapper())));
|
new FilterSupport<>(new ArtifactDataProvider(artifactManagement, new ArtifactToProxyArtifactMapper())));
|
||||||
initFilterMappings();
|
initFilterMappings();
|
||||||
|
|
||||||
this.masterEntitySupport = new MasterEntitySupport<>(getFilterSupport());
|
this.masterEntitySupport = new MasterEntitySupport<>(getFilterSupport(), null,
|
||||||
|
sm -> artifactsEncrypted = sm != null && sm.isEncrypted());
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
@@ -107,9 +115,8 @@ public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
|
|||||||
.map(ProxyIdentifiableEntity::getId).collect(Collectors.toList());
|
.map(ProxyIdentifiableEntity::getId).collect(Collectors.toList());
|
||||||
artifactToBeDeletedIds.forEach(artifactManagement::delete);
|
artifactToBeDeletedIds.forEach(artifactManagement::delete);
|
||||||
|
|
||||||
eventBus.publish(EventTopics.ENTITY_MODIFIED, this,
|
eventBus.publish(EventTopics.ENTITY_MODIFIED, this, new EntityModifiedEventPayload(
|
||||||
new EntityModifiedEventPayload(EntityModifiedEventType.ENTITY_UPDATED, ProxySoftwareModule.class,
|
EntityModifiedEventType.ENTITY_UPDATED, ProxySoftwareModule.class, masterEntitySupport.getMasterId()));
|
||||||
getMasterEntitySupport().getMasterId()));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -171,12 +178,20 @@ public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
|
|||||||
|
|
||||||
private void attachFileDownloader(final ProxyArtifact artifact, final Button downloadButton) {
|
private void attachFileDownloader(final ProxyArtifact artifact, final Button downloadButton) {
|
||||||
final StreamResource artifactStreamResource = new StreamResource(() -> artifactManagement
|
final StreamResource artifactStreamResource = new StreamResource(() -> artifactManagement
|
||||||
.loadArtifactBinary(artifact.getSha1Hash()).map(AbstractDbArtifact::getFileInputStream).orElse(null),
|
.loadArtifactBinary(artifact.getSha1Hash(), masterEntitySupport.getMasterId(), artifactsEncrypted)
|
||||||
artifact.getFilename());
|
.map(DbArtifact::getFileInputStream).orElse(null), artifact.getFilename());
|
||||||
|
|
||||||
final FileDownloader fileDownloader = new FileDownloader(artifactStreamResource);
|
final FileDownloader fileDownloader = new FileDownloader(artifactStreamResource);
|
||||||
fileDownloader.setErrorHandler(event -> notification
|
fileDownloader.setErrorHandler(event -> {
|
||||||
.displayValidationError(i18n.getMessage(UIMessageIdProvider.ARTIFACT_DOWNLOAD_FAILURE_MSG)));
|
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);
|
fileDownloader.extend(downloadButton);
|
||||||
}
|
}
|
||||||
@@ -225,4 +240,12 @@ public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
|
|||||||
public MasterEntitySupport<ProxySoftwareModule> getMasterEntitySupport() {
|
public MasterEntitySupport<ProxySoftwareModule> getMasterEntitySupport() {
|
||||||
return masterEntitySupport;
|
return masterEntitySupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class DownloadException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public DownloadException(final Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.ui.artifacts.smtable;
|
package org.eclipse.hawkbit.ui.artifacts.smtable;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
|
||||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate;
|
import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
@@ -62,6 +63,13 @@ public class AddSmWindowController
|
|||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void adaptLayout(final ProxySoftwareModule proxyEntity) {
|
||||||
|
if (!ArtifactEncryptionService.getInstance().isEncryptionSupported()) {
|
||||||
|
layout.disableEncryptionField();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ProxySoftwareModule buildEntityFromProxy(final ProxySoftwareModule proxyEntity) {
|
protected ProxySoftwareModule buildEntityFromProxy(final ProxySoftwareModule proxyEntity) {
|
||||||
// We ignore the method parameter, because we are interested in the
|
// We ignore the method parameter, because we are interested in the
|
||||||
@@ -73,7 +81,7 @@ public class AddSmWindowController
|
|||||||
protected SoftwareModule persistEntityInRepository(final ProxySoftwareModule entity) {
|
protected SoftwareModule persistEntityInRepository(final ProxySoftwareModule entity) {
|
||||||
final SoftwareModuleCreate smCreate = getEntityFactory().softwareModule().create()
|
final SoftwareModuleCreate smCreate = getEntityFactory().softwareModule().create()
|
||||||
.type(entity.getTypeInfo().getKey()).name(entity.getName()).version(entity.getVersion())
|
.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);
|
return smManagement.create(smCreate);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ public class SmWindowBuilder extends AbstractEntityWindowBuilder<ProxySoftwareMo
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Window getWindowForAdd() {
|
public Window getWindowForAdd() {
|
||||||
return getWindowForNewEntity(
|
return getWindowForNewEntity(new AddSmWindowController(uiDependencies, smManagement,
|
||||||
new AddSmWindowController(uiDependencies, smManagement, new SmWindowLayout(getI18n(), smTypeManagement), view));
|
new SmWindowLayout(getI18n(), smTypeManagement), view));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ public class SmWindowBuilder extends AbstractEntityWindowBuilder<ProxySoftwareMo
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Window getWindowForUpdate(final ProxySoftwareModule proxySm) {
|
public Window getWindowForUpdate(final ProxySoftwareModule proxySm) {
|
||||||
return getWindowForEntity(proxySm,
|
return getWindowForEntity(proxySm, new UpdateSmWindowController(uiDependencies, smManagement,
|
||||||
new UpdateSmWindowController(uiDependencies, smManagement, new SmWindowLayout(getI18n(), smTypeManagement)));
|
new SmWindowLayout(getI18n(), smTypeManagement)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
package org.eclipse.hawkbit.ui.artifacts.smtable;
|
package org.eclipse.hawkbit.ui.artifacts.smtable;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
|
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
|
||||||
import org.eclipse.hawkbit.ui.common.AbstractEntityWindowLayout;
|
import org.eclipse.hawkbit.ui.common.AbstractEntityWindowLayout;
|
||||||
import org.eclipse.hawkbit.ui.common.data.mappers.TypeToTypeInfoMapper;
|
import org.eclipse.hawkbit.ui.common.data.mappers.TypeToTypeInfoMapper;
|
||||||
import org.eclipse.hawkbit.ui.common.data.providers.SoftwareModuleTypeDataProvider;
|
import org.eclipse.hawkbit.ui.common.data.providers.SoftwareModuleTypeDataProvider;
|
||||||
@@ -17,6 +16,7 @@ import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule;
|
|||||||
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTypeInfo;
|
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTypeInfo;
|
||||||
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||||
|
|
||||||
|
import com.vaadin.ui.CheckBox;
|
||||||
import com.vaadin.ui.ComboBox;
|
import com.vaadin.ui.ComboBox;
|
||||||
import com.vaadin.ui.ComponentContainer;
|
import com.vaadin.ui.ComponentContainer;
|
||||||
import com.vaadin.ui.FormLayout;
|
import com.vaadin.ui.FormLayout;
|
||||||
@@ -34,6 +34,7 @@ public class SmWindowLayout extends AbstractEntityWindowLayout<ProxySoftwareModu
|
|||||||
private final TextField smVersion;
|
private final TextField smVersion;
|
||||||
private final TextField smVendor;
|
private final TextField smVendor;
|
||||||
private final TextArea smDescription;
|
private final TextArea smDescription;
|
||||||
|
private final CheckBox artifactEncryption;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for AbstractTagWindowLayout
|
* Constructor for AbstractTagWindowLayout
|
||||||
@@ -47,7 +48,7 @@ public class SmWindowLayout extends AbstractEntityWindowLayout<ProxySoftwareModu
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
final SoftwareModuleTypeDataProvider<ProxyTypeInfo> smTypeDataProvider = new SoftwareModuleTypeDataProvider<>(
|
final SoftwareModuleTypeDataProvider<ProxyTypeInfo> smTypeDataProvider = new SoftwareModuleTypeDataProvider<>(
|
||||||
smTypeManagement, new TypeToTypeInfoMapper<SoftwareModuleType>());
|
smTypeManagement, new TypeToTypeInfoMapper<>());
|
||||||
this.smComponentBuilder = new SmWindowLayoutComponentBuilder(i18n, smTypeDataProvider);
|
this.smComponentBuilder = new SmWindowLayoutComponentBuilder(i18n, smTypeDataProvider);
|
||||||
|
|
||||||
this.smTypeSelect = smComponentBuilder.createSoftwareModuleTypeCombo(binder);
|
this.smTypeSelect = smComponentBuilder.createSoftwareModuleTypeCombo(binder);
|
||||||
@@ -55,6 +56,7 @@ public class SmWindowLayout extends AbstractEntityWindowLayout<ProxySoftwareModu
|
|||||||
this.smVersion = smComponentBuilder.createVersionField(binder);
|
this.smVersion = smComponentBuilder.createVersionField(binder);
|
||||||
this.smVendor = smComponentBuilder.createVendorField(binder);
|
this.smVendor = smComponentBuilder.createVendorField(binder);
|
||||||
this.smDescription = smComponentBuilder.createDescription(binder);
|
this.smDescription = smComponentBuilder.createDescription(binder);
|
||||||
|
this.artifactEncryption = smComponentBuilder.createArtifactEncryptionCheck(binder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,6 +81,8 @@ public class SmWindowLayout extends AbstractEntityWindowLayout<ProxySoftwareModu
|
|||||||
|
|
||||||
smWindowLayout.addComponent(smDescription);
|
smWindowLayout.addComponent(smDescription);
|
||||||
|
|
||||||
|
smWindowLayout.addComponent(artifactEncryption);
|
||||||
|
|
||||||
return smWindowLayout;
|
return smWindowLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,4 +106,11 @@ public class SmWindowLayout extends AbstractEntityWindowLayout<ProxySoftwareModu
|
|||||||
public void disableVersionField() {
|
public void disableVersionField() {
|
||||||
smVersion.setEnabled(false);
|
smVersion.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable the software module artifact encryption
|
||||||
|
*/
|
||||||
|
public void disableEncryptionField() {
|
||||||
|
artifactEncryption.setEnabled(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
|
|||||||
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||||
|
|
||||||
import com.vaadin.data.Binder;
|
import com.vaadin.data.Binder;
|
||||||
|
import com.vaadin.ui.CheckBox;
|
||||||
import com.vaadin.ui.ComboBox;
|
import com.vaadin.ui.ComboBox;
|
||||||
import com.vaadin.ui.TextArea;
|
import com.vaadin.ui.TextArea;
|
||||||
import com.vaadin.ui.TextField;
|
import com.vaadin.ui.TextField;
|
||||||
@@ -28,6 +29,7 @@ import com.vaadin.ui.TextField;
|
|||||||
public class SmWindowLayoutComponentBuilder {
|
public class SmWindowLayoutComponentBuilder {
|
||||||
|
|
||||||
public static final String TEXTFIELD_VENDOR = "textfield.vendor";
|
public static final String TEXTFIELD_VENDOR = "textfield.vendor";
|
||||||
|
public static final String ARTIFACT_ENCRYPTION = "artifact.encryption";
|
||||||
|
|
||||||
private final VaadinMessageSource i18n;
|
private final VaadinMessageSource i18n;
|
||||||
private final SoftwareModuleTypeDataProvider<ProxyTypeInfo> smTypeDataProvider;
|
private final SoftwareModuleTypeDataProvider<ProxyTypeInfo> smTypeDataProvider;
|
||||||
@@ -56,7 +58,8 @@ public class SmWindowLayoutComponentBuilder {
|
|||||||
*/
|
*/
|
||||||
public ComboBox<ProxyTypeInfo> createSoftwareModuleTypeCombo(final Binder<ProxySoftwareModule> binder) {
|
public ComboBox<ProxyTypeInfo> createSoftwareModuleTypeCombo(final Binder<ProxySoftwareModule> binder) {
|
||||||
return FormComponentBuilder
|
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
|
return FormComponentBuilder
|
||||||
.createDescriptionInput(binder, i18n, UIComponentIdProvider.ADD_SW_MODULE_DESCRIPTION).getComponent();
|
.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<ProxySoftwareModule> binder) {
|
||||||
|
return FormComponentBuilder.createCheckBox(i18n.getMessage(ARTIFACT_ENCRYPTION),
|
||||||
|
UIComponentIdProvider.ARTIFACT_ENCRYPTION_ID, binder, ProxySoftwareModule::isEncrypted,
|
||||||
|
ProxySoftwareModule::setEncrypted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ public class UpdateSmWindowController
|
|||||||
sm.setVersion(proxyEntity.getVersion());
|
sm.setVersion(proxyEntity.getVersion());
|
||||||
sm.setVendor(proxyEntity.getVendor());
|
sm.setVendor(proxyEntity.getVendor());
|
||||||
sm.setDescription(proxyEntity.getDescription());
|
sm.setDescription(proxyEntity.getDescription());
|
||||||
|
sm.setEncrypted(proxyEntity.isEncrypted());
|
||||||
|
|
||||||
nameBeforeEdit = proxyEntity.getName();
|
nameBeforeEdit = proxyEntity.getName();
|
||||||
versionBeforeEdit = proxyEntity.getVersion();
|
versionBeforeEdit = proxyEntity.getVersion();
|
||||||
@@ -78,6 +79,7 @@ public class UpdateSmWindowController
|
|||||||
layout.disableSmTypeSelect();
|
layout.disableSmTypeSelect();
|
||||||
layout.disableNameField();
|
layout.disableNameField();
|
||||||
layout.disableVersionField();
|
layout.disableVersionField();
|
||||||
|
layout.disableEncryptionField();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import java.util.concurrent.locks.Lock;
|
|||||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||||
import org.eclipse.hawkbit.repository.RegexCharacterCollection;
|
import org.eclipse.hawkbit.repository.RegexCharacterCollection;
|
||||||
import org.eclipse.hawkbit.repository.RegexCharacterCollection.RegexChar;
|
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.ArtifactUploadFailedException;
|
||||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.FileSizeQuotaExceededException;
|
import org.eclipse.hawkbit.repository.exception.FileSizeQuotaExceededException;
|
||||||
@@ -137,6 +139,10 @@ public abstract class AbstractFileTransferHandler implements Serializable {
|
|||||||
interruptUploadAndSetReason(i18n.getMessage("message.uploadedfile.illegalFilename"));
|
interruptUploadAndSetReason(i18n.getMessage("message.uploadedfile.illegalFilename"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void interruptUploadDueToEncryptionError() {
|
||||||
|
interruptUploadAndSetReason(i18n.getMessage("message.encryption.failed"));
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean isFileAlreadyContainedInSoftwareModule(final FileUploadId newFileUploadId,
|
protected boolean isFileAlreadyContainedInSoftwareModule(final FileUploadId newFileUploadId,
|
||||||
final SoftwareModule softwareModule) {
|
final SoftwareModule softwareModule) {
|
||||||
for (final Artifact artifact : softwareModule.getArtifacts()) {
|
for (final Artifact artifact : softwareModule.getArtifacts()) {
|
||||||
@@ -215,7 +221,7 @@ public abstract class AbstractFileTransferHandler implements Serializable {
|
|||||||
try {
|
try {
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
} catch (final IOException e1) {
|
} 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 {
|
try {
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
} catch (final IOException e1) {
|
} 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();
|
streamToRepository();
|
||||||
} catch (final FileSizeQuotaExceededException e) {
|
} catch (final FileSizeQuotaExceededException e) {
|
||||||
interruptUploadDueToFileSizeQuotaExceeded(e.getExceededQuotaValueString());
|
interruptUploadDueToFileSizeQuotaExceeded(e.getExceededQuotaValueString());
|
||||||
|
publishUploadFailedAndFinishedEvent(fileUploadId);
|
||||||
LOG.debug("Upload failed due to file size quota exceeded:", e);
|
LOG.debug("Upload failed due to file size quota exceeded:", e);
|
||||||
} catch (final StorageQuotaExceededException e) {
|
} catch (final StorageQuotaExceededException e) {
|
||||||
interruptUploadDueToStorageQuotaExceeded(e.getExceededQuotaValueString());
|
interruptUploadDueToStorageQuotaExceeded(e.getExceededQuotaValueString());
|
||||||
|
publishUploadFailedAndFinishedEvent(fileUploadId);
|
||||||
LOG.debug("Upload failed due to storage quota exceeded:", e);
|
LOG.debug("Upload failed due to storage quota exceeded:", e);
|
||||||
} catch (final AssignmentQuotaExceededException e) {
|
} catch (final AssignmentQuotaExceededException e) {
|
||||||
interruptUploadDueToAssignmentQuotaExceeded();
|
interruptUploadDueToAssignmentQuotaExceeded();
|
||||||
|
publishUploadFailedAndFinishedEvent(fileUploadId);
|
||||||
LOG.debug("Upload failed due to assignment quota exceeded:", e);
|
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) {
|
} catch (final RuntimeException e) {
|
||||||
interruptUploadDueToUploadFailed();
|
interruptUploadDueToUploadFailed();
|
||||||
publishUploadFailedAndFinishedEvent(fileUploadId);
|
publishUploadFailedAndFinishedEvent(fileUploadId);
|
||||||
@@ -297,25 +310,28 @@ public abstract class AbstractFileTransferHandler implements Serializable {
|
|||||||
if (fileUploadId == null) {
|
if (fileUploadId == null) {
|
||||||
throw new ArtifactUploadFailedException();
|
throw new ArtifactUploadFailedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
final String filename = fileUploadId.getFilename();
|
final String filename = fileUploadId.getFilename();
|
||||||
LOG.debug("Transfering file {} directly to repository", filename);
|
|
||||||
final Artifact artifact = uploadArtifact(filename);
|
final Artifact artifact = uploadArtifact(filename);
|
||||||
|
|
||||||
if (isUploadInterrupted()) {
|
if (isUploadInterrupted()) {
|
||||||
LOG.warn("Upload of {} was interrupted", filename);
|
LOG.warn("Upload of {} was interrupted", filename);
|
||||||
handleUploadFailure(artifact);
|
handleUploadFailure(artifact);
|
||||||
publishUploadFinishedEvent(fileUploadId);
|
publishUploadFinishedEvent(fileUploadId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
publishUploadSucceeded(fileUploadId, artifact.getSize());
|
publishUploadSucceeded(fileUploadId, artifact.getSize());
|
||||||
publishUploadFinishedEvent(fileUploadId);
|
publishUploadFinishedEvent(fileUploadId);
|
||||||
publishArtifactsChanged(fileUploadId);
|
publishArtifactsChanged(fileUploadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Artifact uploadArtifact(final String filename) {
|
private Artifact uploadArtifact(final String filename) {
|
||||||
|
LOG.debug("Transfering file {} directly to repository", filename);
|
||||||
try {
|
try {
|
||||||
return artifactManagement.create(new ArtifactUpload(inputStream, fileUploadId.getSoftwareModuleId(),
|
return artifactManagement.create(new ArtifactUpload(inputStream, fileUploadId.getSoftwareModuleId(),
|
||||||
filename, null, null, null, true, mimeType, -1));
|
filename, null, null, null, true, mimeType, -1));
|
||||||
} catch (final ArtifactUploadFailedException | InvalidSHA1HashException | InvalidMD5HashException e) {
|
} catch (final InvalidSHA1HashException | InvalidMD5HashException e) {
|
||||||
throw new ArtifactUploadFailedException(e);
|
throw new ArtifactUploadFailedException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,8 @@
|
|||||||
package org.eclipse.hawkbit.ui.artifacts.upload;
|
package org.eclipse.hawkbit.ui.artifacts.upload;
|
||||||
|
|
||||||
import java.io.Serializable;
|
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.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
|
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
|
||||||
|
|
||||||
@@ -21,19 +20,14 @@ import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class FileUploadId implements Serializable {
|
public class FileUploadId implements Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final String filename;
|
private final String filename;
|
||||||
|
|
||||||
|
private final Long softwareModuleId;
|
||||||
private final String softwareModuleName;
|
private final String softwareModuleName;
|
||||||
|
|
||||||
private final String softwareModuleVersion;
|
private final String softwareModuleVersion;
|
||||||
|
|
||||||
private Long softwareModuleId;
|
|
||||||
|
|
||||||
private final String id;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link FileUploadId} instance.
|
* Creates a new {@link FileUploadId} instance.
|
||||||
*
|
*
|
||||||
@@ -44,36 +38,25 @@ public class FileUploadId implements Serializable {
|
|||||||
*/
|
*/
|
||||||
public FileUploadId(final String filename, final SoftwareModule softwareModule) {
|
public FileUploadId(final String filename, final SoftwareModule softwareModule) {
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
|
this.softwareModuleId = softwareModule.getId();
|
||||||
this.softwareModuleName = softwareModule.getName();
|
this.softwareModuleName = softwareModule.getName();
|
||||||
this.softwareModuleVersion = softwareModule.getVersion();
|
this.softwareModuleVersion = softwareModule.getVersion();
|
||||||
this.softwareModuleId = softwareModule.getId();
|
|
||||||
this.id = createFileUploadIdString(filename, softwareModuleName, softwareModuleVersion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public String getFilename() {
|
||||||
* Creates a new {@link FileUploadId} instance.
|
return filename;
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String createFileUploadIdString(final String filename, final String softwareModuleName,
|
public Long getSoftwareModuleId() {
|
||||||
final String softwareModuleVersion) {
|
return softwareModuleId;
|
||||||
return new StringBuilder(filename).append(":")
|
}
|
||||||
.append(HawkbitCommonUtil.getFormattedNameVersion(softwareModuleName, softwareModuleVersion))
|
|
||||||
.toString();
|
public String getSoftwareModuleName() {
|
||||||
|
return softwareModuleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSoftwareModuleVersion() {
|
||||||
|
return softwareModuleVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -88,52 +71,21 @@ public class FileUploadId implements Serializable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final FileUploadId other = (FileUploadId) obj;
|
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
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return new HashCodeBuilder().append(id).toHashCode();
|
return Objects.hash(getFilename(), getSoftwareModuleId(), getSoftwareModuleName(), getSoftwareModuleVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return id;
|
return new StringBuilder(filename).append(":")
|
||||||
}
|
.append(HawkbitCommonUtil.getFormattedNameVersion(softwareModuleName, softwareModuleVersion))
|
||||||
|
.toString();
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -377,9 +377,9 @@ public final class FormComponentBuilder {
|
|||||||
* setter for the binder
|
* setter for the binder
|
||||||
* @return the bound box
|
* @return the bound box
|
||||||
*/
|
*/
|
||||||
public static <T> CheckBox getCheckBox(final String id, final Binder<T> binder,
|
public static <T> CheckBox createCheckBox(final String id, final Binder<T> binder,
|
||||||
final ValueProvider<T, Boolean> getter, final Setter<T, Boolean> setter) {
|
final ValueProvider<T, Boolean> getter, final Setter<T, Boolean> 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
|
* setter for the binder
|
||||||
* @return the bound box
|
* @return the bound box
|
||||||
*/
|
*/
|
||||||
public static <T> CheckBox getCheckBox(final String caption, final String id, final Binder<T> binder,
|
public static <T> CheckBox createCheckBox(final String caption, final String id, final Binder<T> binder,
|
||||||
final ValueProvider<T, Boolean> getter, final Setter<T, Boolean> setter) {
|
final ValueProvider<T, Boolean> getter, final Setter<T, Boolean> setter) {
|
||||||
final CheckBox checkBox;
|
final CheckBox checkBox;
|
||||||
if (StringUtils.isEmpty(caption)) {
|
if (StringUtils.isEmpty(caption)) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class SoftwareModuleToProxyMapper
|
|||||||
proxySoftwareModule.setNameAndVersion(
|
proxySoftwareModule.setNameAndVersion(
|
||||||
HawkbitCommonUtil.concatStrings(":", softwareModule.getName(), softwareModule.getVersion()));
|
HawkbitCommonUtil.concatStrings(":", softwareModule.getName(), softwareModule.getVersion()));
|
||||||
proxySoftwareModule.setVendor(softwareModule.getVendor());
|
proxySoftwareModule.setVendor(softwareModule.getVendor());
|
||||||
|
proxySoftwareModule.setEncrypted(softwareModule.isEncrypted());
|
||||||
|
|
||||||
final SoftwareModuleType type = softwareModule.getType();
|
final SoftwareModuleType type = softwareModule.getType();
|
||||||
final ProxyTypeInfo typeInfo = new ProxyTypeInfo(type.getId(), type.getName(), type.getKey());
|
final ProxyTypeInfo typeInfo = new ProxyTypeInfo(type.getId(), type.getName(), type.getKey());
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ public class ProxySoftwareModule extends ProxyNamedEntity implements VersionAwar
|
|||||||
|
|
||||||
private boolean assigned;
|
private boolean assigned;
|
||||||
|
|
||||||
|
private boolean encrypted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the software module vendor
|
* Gets the software module vendor
|
||||||
*
|
*
|
||||||
@@ -129,4 +131,12 @@ public class ProxySoftwareModule extends ProxyNamedEntity implements VersionAwar
|
|||||||
public void setVersion(final String version) {
|
public void setVersion(final String version) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEncrypted() {
|
||||||
|
return encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEncrypted(final boolean encrypted) {
|
||||||
|
this.encrypted = encrypted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public class MetaDataAddUpdateWindowLayoutComponentBuilder {
|
|||||||
* @return Target field CheckBox
|
* @return Target field CheckBox
|
||||||
*/
|
*/
|
||||||
public CheckBox createVisibleForTargetsField(final Binder<ProxyMetaData> binder) {
|
public CheckBox createVisibleForTargetsField(final Binder<ProxyMetaData> binder) {
|
||||||
return FormComponentBuilder.getCheckBox(i18n.getMessage(TARGET_VISIBLE),
|
return FormComponentBuilder.createCheckBox(i18n.getMessage(TARGET_VISIBLE),
|
||||||
UIComponentIdProvider.METADATA_TARGET_VISIBLE_ID, binder, ProxyMetaData::isVisibleForTargets,
|
UIComponentIdProvider.METADATA_TARGET_VISIBLE_ID, binder, ProxyMetaData::isVisibleForTargets,
|
||||||
ProxyMetaData::setVisibleForTargets);
|
ProxyMetaData::setVisibleForTargets);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ public class SoftwareModuleDetails extends AbstractGridDetailsLayout<ProxySoftwa
|
|||||||
* @param smMetaDataWindowBuilder
|
* @param smMetaDataWindowBuilder
|
||||||
* SmMetaDataWindowBuilder
|
* SmMetaDataWindowBuilder
|
||||||
*/
|
*/
|
||||||
public SoftwareModuleDetails(final CommonUiDependencies uiDependencies, final SoftwareModuleManagement softwareManagement,
|
public SoftwareModuleDetails(final CommonUiDependencies uiDependencies,
|
||||||
|
final SoftwareModuleManagement softwareManagement,
|
||||||
final SoftwareModuleTypeManagement softwareModuleTypeManagement,
|
final SoftwareModuleTypeManagement softwareModuleTypeManagement,
|
||||||
final SmMetaDataWindowBuilder smMetaDataWindowBuilder) {
|
final SmMetaDataWindowBuilder smMetaDataWindowBuilder) {
|
||||||
super(uiDependencies.getI18n());
|
super(uiDependencies.getI18n());
|
||||||
@@ -93,6 +94,10 @@ public class SoftwareModuleDetails extends AbstractGridDetailsLayout<ProxySoftwa
|
|||||||
: i18n.getMessage("label.multiAssign.type")));
|
: i18n.getMessage("label.multiAssign.type")));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
details.add(new ProxyKeyValueDetails(UIComponentIdProvider.SWM_DTLS_ENCRYPTION,
|
||||||
|
i18n.getMessage("label.artifact.encryption"),
|
||||||
|
entity.isEncrypted() ? i18n.getMessage("label.enabled") : i18n.getMessage("label.disabled")));
|
||||||
|
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
package org.eclipse.hawkbit.ui.common.grid.support;
|
package org.eclipse.hawkbit.ui.common.grid.support;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyIdentifiableEntity;
|
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyIdentifiableEntity;
|
||||||
@@ -24,6 +25,7 @@ import org.eclipse.hawkbit.ui.common.layout.MasterEntityAwareComponent;
|
|||||||
public class MasterEntitySupport<M extends ProxyIdentifiableEntity> implements MasterEntityAwareComponent<M> {
|
public class MasterEntitySupport<M extends ProxyIdentifiableEntity> implements MasterEntityAwareComponent<M> {
|
||||||
private final FilterSupport<?, ?> filterSupport;
|
private final FilterSupport<?, ?> filterSupport;
|
||||||
private final Function<M, ?> masterEntityToFilterMapper;
|
private final Function<M, ?> masterEntityToFilterMapper;
|
||||||
|
private final Consumer<M> postMasterChangeCallback;
|
||||||
|
|
||||||
private Long masterId;
|
private Long masterId;
|
||||||
|
|
||||||
@@ -47,8 +49,24 @@ public class MasterEntitySupport<M extends ProxyIdentifiableEntity> implements M
|
|||||||
*/
|
*/
|
||||||
public MasterEntitySupport(final FilterSupport<?, ?> filterSupport,
|
public MasterEntitySupport(final FilterSupport<?, ?> filterSupport,
|
||||||
final Function<M, ?> masterEntityToFilterMapper) {
|
final Function<M, ?> 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<M, ?> masterEntityToFilterMapper,
|
||||||
|
final Consumer<M> postMasterChangeCallback) {
|
||||||
this.filterSupport = filterSupport;
|
this.filterSupport = filterSupport;
|
||||||
this.masterEntityToFilterMapper = masterEntityToFilterMapper;
|
this.masterEntityToFilterMapper = masterEntityToFilterMapper;
|
||||||
|
this.postMasterChangeCallback = postMasterChangeCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -60,6 +78,10 @@ public class MasterEntitySupport<M extends ProxyIdentifiableEntity> implements M
|
|||||||
filterSupport.updateFilter(FilterType.MASTER, getMasterEntityFilter(masterEntity));
|
filterSupport.updateFilter(FilterType.MASTER, getMasterEntityFilter(masterEntity));
|
||||||
|
|
||||||
masterId = masterEntity != null ? masterEntity.getId() : null;
|
masterId = masterEntity != null ? masterEntity.getId() : null;
|
||||||
|
|
||||||
|
if (postMasterChangeCallback != null) {
|
||||||
|
postMasterChangeCallback.accept(masterEntity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getMasterEntityFilter(final M masterEntity) {
|
private Object getMasterEntityFilter(final M masterEntity) {
|
||||||
|
|||||||
@@ -82,6 +82,6 @@ public class SmTypeSelectedGrid extends Grid<ProxyType> {
|
|||||||
binder.addValueChangeListener(event -> mandatoryPropertyChangedCallback.run());
|
binder.addValueChangeListener(event -> mandatoryPropertyChangedCallback.run());
|
||||||
|
|
||||||
final String id = "selected.sm.type." + smType.getId();
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ public class DsWindowLayoutComponentBuilder {
|
|||||||
* @return Migration step required checkbox
|
* @return Migration step required checkbox
|
||||||
*/
|
*/
|
||||||
public CheckBox createMigrationStepField(final Binder<ProxyDistributionSet> binder) {
|
public CheckBox createMigrationStepField(final Binder<ProxyDistributionSet> 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,
|
UIComponentIdProvider.DIST_ADD_MIGRATION_CHECK, binder, ProxyDistributionSet::isRequiredMigrationStep,
|
||||||
ProxyDistributionSet::setRequiredMigrationStep);
|
ProxyDistributionSet::setRequiredMigrationStep);
|
||||||
|
|
||||||
|
|||||||
@@ -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<UiErrorDetails> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ public class UploadErrorExtractor extends AbstractSingleUiErrorDetailsExtractor
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Optional<UiErrorDetails> findDetails(final Throwable error) {
|
protected Optional<UiErrorDetails> findDetails(final Throwable error) {
|
||||||
// UploadException is ignored
|
// UploadException is ignored as it is handled explicitly
|
||||||
return findExceptionOf(error, UploadException.class).map(ex -> UiErrorDetails.empty());
|
return findExceptionOf(error, UploadException.class).map(ex -> UiErrorDetails.empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public class AutoAssignmentWindowLayoutComponentBuilder {
|
|||||||
*/
|
*/
|
||||||
public CheckBox createEnableCheckbox(final Binder<ProxyTargetFilterQuery> binder) {
|
public CheckBox createEnableCheckbox(final Binder<ProxyTargetFilterQuery> binder) {
|
||||||
final String caption = i18n.getMessage(UIMessageIdProvider.LABEL_AUTO_ASSIGNMENT_ENABLE);
|
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);
|
ProxyTargetFilterQuery::isAutoAssignmentEnabled, ProxyTargetFilterQuery::setAutoAssignmentEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public class AssignmentWindowLayoutComponentBuilder {
|
|||||||
* @return Maintenance window checkbox
|
* @return Maintenance window checkbox
|
||||||
*/
|
*/
|
||||||
public CheckBox createEnableMaintenanceWindowToggle(final Binder<ProxyAssignmentWindow> binder) {
|
public CheckBox createEnableMaintenanceWindowToggle(final Binder<ProxyAssignmentWindow> binder) {
|
||||||
final CheckBox maintenanceWindowToggle = FormComponentBuilder.getCheckBox(
|
final CheckBox maintenanceWindowToggle = FormComponentBuilder.createCheckBox(
|
||||||
i18n.getMessage("caption.maintenancewindow.enabled"),
|
i18n.getMessage("caption.maintenancewindow.enabled"),
|
||||||
UIComponentIdProvider.MAINTENANCE_WINDOW_ENABLED_ID, binder,
|
UIComponentIdProvider.MAINTENANCE_WINDOW_ENABLED_ID, binder,
|
||||||
ProxyAssignmentWindow::isMaintenanceWindowEnabled, ProxyAssignmentWindow::setMaintenanceWindowEnabled);
|
ProxyAssignmentWindow::isMaintenanceWindowEnabled, ProxyAssignmentWindow::setMaintenanceWindowEnabled);
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ public class AuthenticationConfigurationView extends BaseConfigurationView<Proxy
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void initCertificateAuthConfiguration(GridLayout gridLayout, int row) {
|
protected void initCertificateAuthConfiguration(GridLayout gridLayout, int row) {
|
||||||
final CheckBox certificateAuthCheckbox = FormComponentBuilder.getCheckBox(
|
final CheckBox certificateAuthCheckbox = FormComponentBuilder.createCheckBox(
|
||||||
UIComponentIdProvider.CERT_AUTH_ALLOWED_CHECKBOX, getBinder(),
|
UIComponentIdProvider.CERT_AUTH_ALLOWED_CHECKBOX, getBinder(),
|
||||||
ProxySystemConfigAuthentication::isCertificateAuth,
|
ProxySystemConfigAuthentication::isCertificateAuth,
|
||||||
ProxySystemConfigAuthentication::setCertificateAuth);
|
ProxySystemConfigAuthentication::setCertificateAuth);
|
||||||
@@ -123,7 +123,7 @@ public class AuthenticationConfigurationView extends BaseConfigurationView<Proxy
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void initTargetTokenConfiguration(GridLayout gridLayout, int row) {
|
protected void initTargetTokenConfiguration(GridLayout gridLayout, int row) {
|
||||||
final CheckBox targetSecTokenCheckBox = FormComponentBuilder.getCheckBox(
|
final CheckBox targetSecTokenCheckBox = FormComponentBuilder.createCheckBox(
|
||||||
UIComponentIdProvider.TARGET_SEC_TOKEN_ALLOWED_CHECKBOX, getBinder(),
|
UIComponentIdProvider.TARGET_SEC_TOKEN_ALLOWED_CHECKBOX, getBinder(),
|
||||||
ProxySystemConfigAuthentication::isTargetSecToken, ProxySystemConfigAuthentication::setTargetSecToken);
|
ProxySystemConfigAuthentication::isTargetSecToken, ProxySystemConfigAuthentication::setTargetSecToken);
|
||||||
targetSecTokenCheckBox.setStyleName(DIST_CHECKBOX_STYLE);
|
targetSecTokenCheckBox.setStyleName(DIST_CHECKBOX_STYLE);
|
||||||
@@ -132,7 +132,7 @@ public class AuthenticationConfigurationView extends BaseConfigurationView<Proxy
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void initGatewayTokenConfiguration(GridLayout gridLayout, int row) {
|
protected void initGatewayTokenConfiguration(GridLayout gridLayout, int row) {
|
||||||
final CheckBox gatewaySecTokenCheckBox = FormComponentBuilder.getCheckBox(
|
final CheckBox gatewaySecTokenCheckBox = FormComponentBuilder.createCheckBox(
|
||||||
UIComponentIdProvider.GATEWAY_SEC_TOKEN_ALLOWED_CHECKBOX, getBinder(),
|
UIComponentIdProvider.GATEWAY_SEC_TOKEN_ALLOWED_CHECKBOX, getBinder(),
|
||||||
ProxySystemConfigAuthentication::isGatewaySecToken,
|
ProxySystemConfigAuthentication::isGatewaySecToken,
|
||||||
ProxySystemConfigAuthentication::setGatewaySecToken);
|
ProxySystemConfigAuthentication::setGatewaySecToken);
|
||||||
@@ -149,7 +149,7 @@ public class AuthenticationConfigurationView extends BaseConfigurationView<Proxy
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void initAnonymousDownloadConfiguration(GridLayout gridLayout, int row) {
|
protected void initAnonymousDownloadConfiguration(GridLayout gridLayout, int row) {
|
||||||
final CheckBox downloadAnonymousCheckBox = FormComponentBuilder.getCheckBox(
|
final CheckBox downloadAnonymousCheckBox = FormComponentBuilder.createCheckBox(
|
||||||
UIComponentIdProvider.DOWNLOAD_ANONYMOUS_CHECKBOX, getBinder(),
|
UIComponentIdProvider.DOWNLOAD_ANONYMOUS_CHECKBOX, getBinder(),
|
||||||
ProxySystemConfigAuthentication::isDownloadAnonymous,
|
ProxySystemConfigAuthentication::isDownloadAnonymous,
|
||||||
ProxySystemConfigAuthentication::setDownloadAnonymous);
|
ProxySystemConfigAuthentication::setDownloadAnonymous);
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public class RepositoryConfigurationView extends BaseConfigurationView<ProxySyst
|
|||||||
gridLayout.setColumnExpandRatio(1, 1.0F);
|
gridLayout.setColumnExpandRatio(1, 1.0F);
|
||||||
gridLayout.setSizeFull();
|
gridLayout.setSizeFull();
|
||||||
|
|
||||||
final CheckBox actionAutoCloseCheckBox = FormComponentBuilder.getCheckBox(
|
final CheckBox actionAutoCloseCheckBox = FormComponentBuilder.createCheckBox(
|
||||||
UIComponentIdProvider.REPOSITORY_ACTIONS_AUTOCLOSE_CHECKBOX, getBinder(),
|
UIComponentIdProvider.REPOSITORY_ACTIONS_AUTOCLOSE_CHECKBOX, getBinder(),
|
||||||
ProxySystemConfigRepository::isActionAutoclose, ProxySystemConfigRepository::setActionAutoclose);
|
ProxySystemConfigRepository::isActionAutoclose, ProxySystemConfigRepository::setActionAutoclose);
|
||||||
actionAutoCloseCheckBox.setStyleName(DIST_CHECKBOX_STYLE);
|
actionAutoCloseCheckBox.setStyleName(DIST_CHECKBOX_STYLE);
|
||||||
@@ -107,7 +107,7 @@ public class RepositoryConfigurationView extends BaseConfigurationView<ProxySyst
|
|||||||
gridLayout.addComponent(actionAutoCloseCheckBox, 0, 0);
|
gridLayout.addComponent(actionAutoCloseCheckBox, 0, 0);
|
||||||
gridLayout.addComponent(actionAutocloseConfigurationItem, 1, 0);
|
gridLayout.addComponent(actionAutocloseConfigurationItem, 1, 0);
|
||||||
|
|
||||||
multiAssignmentsCheckBox = FormComponentBuilder.getCheckBox(
|
multiAssignmentsCheckBox = FormComponentBuilder.createCheckBox(
|
||||||
UIComponentIdProvider.REPOSITORY_MULTI_ASSIGNMENTS_CHECKBOX, getBinder(),
|
UIComponentIdProvider.REPOSITORY_MULTI_ASSIGNMENTS_CHECKBOX, getBinder(),
|
||||||
ProxySystemConfigRepository::isMultiAssignments, ProxySystemConfigRepository::setMultiAssignments);
|
ProxySystemConfigRepository::isMultiAssignments, ProxySystemConfigRepository::setMultiAssignments);
|
||||||
multiAssignmentsCheckBox.setStyleName(DIST_CHECKBOX_STYLE);
|
multiAssignmentsCheckBox.setStyleName(DIST_CHECKBOX_STYLE);
|
||||||
@@ -125,7 +125,7 @@ public class RepositoryConfigurationView extends BaseConfigurationView<ProxySyst
|
|||||||
gridLayout.addComponent(multiAssignmentsCheckBox, 0, 1);
|
gridLayout.addComponent(multiAssignmentsCheckBox, 0, 1);
|
||||||
gridLayout.addComponent(multiAssignmentsConfigurationItem, 1, 1);
|
gridLayout.addComponent(multiAssignmentsConfigurationItem, 1, 1);
|
||||||
|
|
||||||
final CheckBox actionAutoCleanupCheckBox = FormComponentBuilder.getCheckBox(
|
final CheckBox actionAutoCleanupCheckBox = FormComponentBuilder.createCheckBox(
|
||||||
UIComponentIdProvider.REPOSITORY_ACTIONS_AUTOCLEANUP_CHECKBOX, getBinder(),
|
UIComponentIdProvider.REPOSITORY_ACTIONS_AUTOCLEANUP_CHECKBOX, getBinder(),
|
||||||
ProxySystemConfigRepository::isActionAutocleanup, ProxySystemConfigRepository::setActionAutocleanup);
|
ProxySystemConfigRepository::isActionAutocleanup, ProxySystemConfigRepository::setActionAutocleanup);
|
||||||
actionAutoCleanupCheckBox.setStyleName(DIST_CHECKBOX_STYLE);
|
actionAutoCleanupCheckBox.setStyleName(DIST_CHECKBOX_STYLE);
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class RolloutConfigurationView extends BaseConfigurationView<ProxySystemC
|
|||||||
gridLayout.setColumnExpandRatio(1, 1.0F);
|
gridLayout.setColumnExpandRatio(1, 1.0F);
|
||||||
gridLayout.setSizeFull();
|
gridLayout.setSizeFull();
|
||||||
|
|
||||||
final CheckBox approvalCheckbox = FormComponentBuilder.getCheckBox(
|
final CheckBox approvalCheckbox = FormComponentBuilder.createCheckBox(
|
||||||
UIComponentIdProvider.ROLLOUT_APPROVAL_ENABLED_CHECKBOX, getBinder(),
|
UIComponentIdProvider.ROLLOUT_APPROVAL_ENABLED_CHECKBOX, getBinder(),
|
||||||
ProxySystemConfigRollout::isRolloutApproval, ProxySystemConfigRollout::setRolloutApproval);
|
ProxySystemConfigRollout::isRolloutApproval, ProxySystemConfigRollout::setRolloutApproval);
|
||||||
|
|
||||||
|
|||||||
@@ -682,7 +682,7 @@ public final class UIComponentIdProvider {
|
|||||||
/**
|
/**
|
||||||
* Software module table details vendor label id.
|
* Software module table details vendor label id.
|
||||||
*/
|
*/
|
||||||
public static final String DETAILS_VENDOR_LABEL_ID = "details.vendor";
|
public static final String DETAILS_VENDOR_LABEL_ID = "sm.details.vendor";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Software module table details description label id.
|
* Software module table details description label id.
|
||||||
@@ -697,7 +697,7 @@ public final class UIComponentIdProvider {
|
|||||||
/**
|
/**
|
||||||
* Software module table details type label id.
|
* Software module table details type label id.
|
||||||
*/
|
*/
|
||||||
public static final String DETAILS_TYPE_LABEL_ID = "details.type";
|
public static final String DETAILS_TYPE_LABEL_ID = "sm.details.type";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table details Required Migration Step label id.
|
* Table details Required Migration Step label id.
|
||||||
@@ -785,9 +785,14 @@ public final class UIComponentIdProvider {
|
|||||||
public static final String TARGET_MAX_MIN_TABLE_ICON = "target.max.min.table.icon";
|
public static final String TARGET_MAX_MIN_TABLE_ICON = "target.max.min.table.icon";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Software module table in upload UI.
|
* Id of Assignment type in Software Module Details.
|
||||||
*/
|
*/
|
||||||
public static final String SWM_DTLS_MAX_ASSIGN = "max.assign";
|
public static final String SWM_DTLS_MAX_ASSIGN = "sm.details.max.assign";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Id of encryption mode in Software Module Details.
|
||||||
|
*/
|
||||||
|
public static final String SWM_DTLS_ENCRYPTION = "sm.details.encryption";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Documentation Link in Login view and menu.
|
* Documentation Link in Login view and menu.
|
||||||
@@ -1431,7 +1436,8 @@ public final class UIComponentIdProvider {
|
|||||||
*/
|
*/
|
||||||
public static final String INVALIDATE_DS_STOP_ROLLOUTS = "invalidate.distributionset.consequences.stop.rollouts.checkbox";
|
public static final String INVALIDATE_DS_STOP_ROLLOUTS = "invalidate.distributionset.consequences.stop.rollouts.checkbox";
|
||||||
/**
|
/**
|
||||||
* Distribution set invalidate consequences window, cancelation type radio button group
|
* Distribution set invalidate consequences window, cancelation type radio
|
||||||
|
* button group
|
||||||
*/
|
*/
|
||||||
public static final String INVALIDATE_DS_CANCELATION_TYPE = "invalidate.distributionset.consequences.cancelation.type.radio";
|
public static final String INVALIDATE_DS_CANCELATION_TYPE = "invalidate.distributionset.consequences.cancelation.type.radio";
|
||||||
/**
|
/**
|
||||||
@@ -1581,6 +1587,11 @@ public final class UIComponentIdProvider {
|
|||||||
*/
|
*/
|
||||||
public static final String UPLOAD_QUEUE_CLEAR_CONFIRMATION_DIALOG = "upload.queue.clear.confirmation.window";
|
public static final String UPLOAD_QUEUE_CLEAR_CONFIRMATION_DIALOG = "upload.queue.clear.confirmation.window";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Artifact encryption checkbox id.
|
||||||
|
*/
|
||||||
|
public static final String ARTIFACT_ENCRYPTION_ID = "artifact.encryption.id";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /* Private Constructor.
|
* /* Private Constructor.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -167,6 +167,14 @@ public final class UIMessageIdProvider {
|
|||||||
|
|
||||||
public static final String MESSAGE_ERROR_TARGET_TYPE_INCOMPATIBLE = "message.target.type.incompatible";
|
public static final String MESSAGE_ERROR_TARGET_TYPE_INCOMPATIBLE = "message.target.type.incompatible";
|
||||||
|
|
||||||
|
public static final String MESSAGE_ERROR_ENCRYPTION_NOT_SUPPORTED = "message.encryption.unsupported";
|
||||||
|
|
||||||
|
public static final String MESSAGE_ERROR_ENCRYPTION_SECRETS_FAILED = "message.encryption.secrets.failed";
|
||||||
|
|
||||||
|
public static final String MESSAGE_ERROR_ENCRYPTION_FAILED = "message.encryption.failed";
|
||||||
|
|
||||||
|
public static final String MESSAGE_ERROR_DECRYPTION_FAILED = "message.decryption.failed";
|
||||||
|
|
||||||
public static final String CRON_VALIDATION_ERROR = "message.maintenancewindow.schedule.validation.error";
|
public static final String CRON_VALIDATION_ERROR = "message.maintenancewindow.schedule.validation.error";
|
||||||
|
|
||||||
public static final String TOOLTIP_OVERDUE = "tooltip.overdue";
|
public static final String TOOLTIP_OVERDUE = "tooltip.overdue";
|
||||||
|
|||||||
@@ -198,6 +198,9 @@ label.target.count = Targets
|
|||||||
label.description = Description
|
label.description = Description
|
||||||
label.ip = Address
|
label.ip = Address
|
||||||
label.type = Type
|
label.type = Type
|
||||||
|
label.artifact.encryption = Artifact encryption
|
||||||
|
label.enabled = Enabled
|
||||||
|
label.disabled = Disabled
|
||||||
label.assigned.type = Assignment type
|
label.assigned.type = Assignment type
|
||||||
label.assigned.count = {0} Assigned
|
label.assigned.count = {0} Assigned
|
||||||
label.installed.count = {0} Installed
|
label.installed.count = {0} Installed
|
||||||
@@ -566,6 +569,10 @@ message.upload.storageQuota = Storage quota exceeded, {0} left
|
|||||||
message.uploadedfile.illegalFilename = Filename contains illegal characters
|
message.uploadedfile.illegalFilename = Filename contains illegal characters
|
||||||
message.artifact.deleted = Artifact with file {0} deleted successfully
|
message.artifact.deleted = Artifact with file {0} deleted successfully
|
||||||
message.artifact.download.failure = Artifact Download Failed
|
message.artifact.download.failure = Artifact Download Failed
|
||||||
|
message.encryption.unsupported = Artifact encryption not supported
|
||||||
|
message.encryption.secrets.failed = Artifact encryption secrets generation failed
|
||||||
|
message.encryption.failed = Artifact encryption failed
|
||||||
|
message.decryption.failed = Artifact decryption failed
|
||||||
|
|
||||||
artifact.upload.popup.caption = Upload status
|
artifact.upload.popup.caption = Upload status
|
||||||
artifact.upload.status.caption = Status
|
artifact.upload.status.caption = Status
|
||||||
@@ -573,6 +580,7 @@ artifact.upload.progress.caption = Progress
|
|||||||
artifact.upload.reason.caption = Reason
|
artifact.upload.reason.caption = Reason
|
||||||
artifact.filename.caption = File name
|
artifact.filename.caption = File name
|
||||||
artifact.filesize.bytes.caption = Size(B)
|
artifact.filesize.bytes.caption = Size(B)
|
||||||
|
artifact.encryption = Enable artifact encryption
|
||||||
|
|
||||||
tooltip.upload.status.inprogress = In progress
|
tooltip.upload.status.inprogress = In progress
|
||||||
tooltip.upload.status.finished = Finished
|
tooltip.upload.status.finished = Finished
|
||||||
|
|||||||
2
pom.xml
2
pom.xml
@@ -162,7 +162,7 @@
|
|||||||
<maven.site.plugin.version>3.9.0</maven.site.plugin.version>
|
<maven.site.plugin.version>3.9.0</maven.site.plugin.version>
|
||||||
|
|
||||||
<!-- Misc libraries versions - START -->
|
<!-- Misc libraries versions - START -->
|
||||||
<cron-utils.version>9.1.3</cron-utils.version>
|
<cron-utils.version>9.1.6</cron-utils.version>
|
||||||
<jsoup.version>1.14.2</jsoup.version>
|
<jsoup.version>1.14.2</jsoup.version>
|
||||||
<allure.version>2.13.6</allure.version>
|
<allure.version>2.13.6</allure.version>
|
||||||
<eclipselink.version>2.7.9</eclipselink.version>
|
<eclipselink.version>2.7.9</eclipselink.version>
|
||||||
|
|||||||
Reference in New Issue
Block a user