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:
Bondar Bogdan
2021-11-18 09:07:05 +01:00
committed by GitHub
parent 7e28fba104
commit 146735012a
74 changed files with 1214 additions and 324 deletions

View File

@@ -18,6 +18,7 @@ import org.eclipse.hawkbit.ui.common.data.suppliers.TargetFilterStateDataSupplie
import org.eclipse.hawkbit.ui.common.data.suppliers.TargetManagementStateDataSupplier;
import org.eclipse.hawkbit.ui.common.data.suppliers.TargetManagementStateDataSupplierImpl;
import org.eclipse.hawkbit.ui.error.HawkbitUIErrorHandler;
import org.eclipse.hawkbit.ui.error.extractors.ArtifactEncryptionErrorExtractor;
import org.eclipse.hawkbit.ui.error.extractors.ConstraintViolationErrorExtractor;
import org.eclipse.hawkbit.ui.error.extractors.EntityNotFoundErrorExtractor;
import org.eclipse.hawkbit.ui.error.extractors.IncompatibleTargetTypeErrorExtractor;
@@ -182,6 +183,18 @@ public class MgmtUiConfiguration {
return new InvalidDistributionSetErrorExtractor(i18n);
}
/**
* UI Artifact ecnryption operations Error details extractor bean.
*
* @param i18n
* VaadinMessageSource
* @return UI Artifact ecnryption operations Error details extractor
*/
@Bean
UiErrorDetailsExtractor artifactEncryptionErrorExtractor(final VaadinMessageSource i18n) {
return new ArtifactEncryptionErrorExtractor(i18n);
}
/**
* Vaadin4Spring servlet bean.
*

View File

@@ -12,7 +12,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.ui.common.CommonUiDependencies;
import org.eclipse.hawkbit.ui.common.builder.GridComponentBuilder;
@@ -33,11 +33,14 @@ import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.server.FileDownloader;
import com.vaadin.server.StreamResource;
import com.vaadin.ui.Button;
import com.vaadin.ui.UI;
/**
* Artifact Details grid which is shown on the Upload View.
@@ -45,6 +48,8 @@ import com.vaadin.ui.Button;
public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(ArtifactDetailsGrid.class);
private static final String ARTIFACT_NAME_ID = "artifactName";
private static final String ARTIFACT_SIZE_ID = "artifactSize";
private static final String ARTIFACT_MODIFIED_DATE_ID = "artifactModifiedDate";
@@ -60,6 +65,8 @@ public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
private final transient DeleteSupport<ProxyArtifact> artifactDeleteSupport;
private final transient MasterEntitySupport<ProxySoftwareModule> masterEntitySupport;
private boolean artifactsEncrypted;
/**
* Constructor
*
@@ -82,7 +89,8 @@ public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
new FilterSupport<>(new ArtifactDataProvider(artifactManagement, new ArtifactToProxyArtifactMapper())));
initFilterMappings();
this.masterEntitySupport = new MasterEntitySupport<>(getFilterSupport());
this.masterEntitySupport = new MasterEntitySupport<>(getFilterSupport(), null,
sm -> artifactsEncrypted = sm != null && sm.isEncrypted());
init();
}
@@ -107,9 +115,8 @@ public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
.map(ProxyIdentifiableEntity::getId).collect(Collectors.toList());
artifactToBeDeletedIds.forEach(artifactManagement::delete);
eventBus.publish(EventTopics.ENTITY_MODIFIED, this,
new EntityModifiedEventPayload(EntityModifiedEventType.ENTITY_UPDATED, ProxySoftwareModule.class,
getMasterEntitySupport().getMasterId()));
eventBus.publish(EventTopics.ENTITY_MODIFIED, this, new EntityModifiedEventPayload(
EntityModifiedEventType.ENTITY_UPDATED, ProxySoftwareModule.class, masterEntitySupport.getMasterId()));
return true;
}
@@ -171,12 +178,20 @@ public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
private void attachFileDownloader(final ProxyArtifact artifact, final Button downloadButton) {
final StreamResource artifactStreamResource = new StreamResource(() -> artifactManagement
.loadArtifactBinary(artifact.getSha1Hash()).map(AbstractDbArtifact::getFileInputStream).orElse(null),
artifact.getFilename());
.loadArtifactBinary(artifact.getSha1Hash(), masterEntitySupport.getMasterId(), artifactsEncrypted)
.map(DbArtifact::getFileInputStream).orElse(null), artifact.getFilename());
final FileDownloader fileDownloader = new FileDownloader(artifactStreamResource);
fileDownloader.setErrorHandler(event -> notification
.displayValidationError(i18n.getMessage(UIMessageIdProvider.ARTIFACT_DOWNLOAD_FAILURE_MSG)));
fileDownloader.setErrorHandler(event -> {
LOG.error("Download failed for artifact with id {}, filename {}", artifact.getId(), artifact.getFilename(),
event.getThrowable());
notification.displayValidationError(i18n.getMessage(UIMessageIdProvider.ARTIFACT_DOWNLOAD_FAILURE_MSG));
UI.getCurrent().access(() -> {
// give error details extractors a chance to process specific
// error
throw new DownloadException(event.getThrowable());
});
});
fileDownloader.extend(downloadButton);
}
@@ -225,4 +240,12 @@ public class ArtifactDetailsGrid extends AbstractGrid<ProxyArtifact, Long> {
public MasterEntitySupport<ProxySoftwareModule> getMasterEntitySupport() {
return masterEntitySupport;
}
private static class DownloadException extends RuntimeException {
private static final long serialVersionUID = 1L;
public DownloadException(final Throwable cause) {
super(cause);
}
}
}

View File

@@ -8,6 +8,7 @@
*/
package org.eclipse.hawkbit.ui.artifacts.smtable;
import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -62,6 +63,13 @@ public class AddSmWindowController
return layout;
}
@Override
protected void adaptLayout(final ProxySoftwareModule proxyEntity) {
if (!ArtifactEncryptionService.getInstance().isEncryptionSupported()) {
layout.disableEncryptionField();
}
}
@Override
protected ProxySoftwareModule buildEntityFromProxy(final ProxySoftwareModule proxyEntity) {
// We ignore the method parameter, because we are interested in the
@@ -73,7 +81,7 @@ public class AddSmWindowController
protected SoftwareModule persistEntityInRepository(final ProxySoftwareModule entity) {
final SoftwareModuleCreate smCreate = getEntityFactory().softwareModule().create()
.type(entity.getTypeInfo().getKey()).name(entity.getName()).version(entity.getVersion())
.vendor(entity.getVendor()).description(entity.getDescription());
.vendor(entity.getVendor()).description(entity.getDescription()).encrypted(entity.isEncrypted());
return smManagement.create(smCreate);
}

View File

@@ -60,8 +60,8 @@ public class SmWindowBuilder extends AbstractEntityWindowBuilder<ProxySoftwareMo
*/
@Override
public Window getWindowForAdd() {
return getWindowForNewEntity(
new AddSmWindowController(uiDependencies, smManagement, new SmWindowLayout(getI18n(), smTypeManagement), view));
return getWindowForNewEntity(new AddSmWindowController(uiDependencies, smManagement,
new SmWindowLayout(getI18n(), smTypeManagement), view));
}
@@ -73,7 +73,7 @@ public class SmWindowBuilder extends AbstractEntityWindowBuilder<ProxySoftwareMo
*/
@Override
public Window getWindowForUpdate(final ProxySoftwareModule proxySm) {
return getWindowForEntity(proxySm,
new UpdateSmWindowController(uiDependencies, smManagement, new SmWindowLayout(getI18n(), smTypeManagement)));
return getWindowForEntity(proxySm, new UpdateSmWindowController(uiDependencies, smManagement,
new SmWindowLayout(getI18n(), smTypeManagement)));
}
}

View File

@@ -9,7 +9,6 @@
package org.eclipse.hawkbit.ui.artifacts.smtable;
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.data.mappers.TypeToTypeInfoMapper;
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.utils.VaadinMessageSource;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.FormLayout;
@@ -34,6 +34,7 @@ public class SmWindowLayout extends AbstractEntityWindowLayout<ProxySoftwareModu
private final TextField smVersion;
private final TextField smVendor;
private final TextArea smDescription;
private final CheckBox artifactEncryption;
/**
* Constructor for AbstractTagWindowLayout
@@ -47,7 +48,7 @@ public class SmWindowLayout extends AbstractEntityWindowLayout<ProxySoftwareModu
super();
final SoftwareModuleTypeDataProvider<ProxyTypeInfo> smTypeDataProvider = new SoftwareModuleTypeDataProvider<>(
smTypeManagement, new TypeToTypeInfoMapper<SoftwareModuleType>());
smTypeManagement, new TypeToTypeInfoMapper<>());
this.smComponentBuilder = new SmWindowLayoutComponentBuilder(i18n, smTypeDataProvider);
this.smTypeSelect = smComponentBuilder.createSoftwareModuleTypeCombo(binder);
@@ -55,6 +56,7 @@ public class SmWindowLayout extends AbstractEntityWindowLayout<ProxySoftwareModu
this.smVersion = smComponentBuilder.createVersionField(binder);
this.smVendor = smComponentBuilder.createVendorField(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(artifactEncryption);
return smWindowLayout;
}
@@ -102,4 +106,11 @@ public class SmWindowLayout extends AbstractEntityWindowLayout<ProxySoftwareModu
public void disableVersionField() {
smVersion.setEnabled(false);
}
/**
* Disable the software module artifact encryption
*/
public void disableEncryptionField() {
artifactEncryption.setEnabled(false);
}
}

View File

@@ -18,6 +18,7 @@ import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import com.vaadin.data.Binder;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
@@ -28,6 +29,7 @@ import com.vaadin.ui.TextField;
public class SmWindowLayoutComponentBuilder {
public static final String TEXTFIELD_VENDOR = "textfield.vendor";
public static final String ARTIFACT_ENCRYPTION = "artifact.encryption";
private final VaadinMessageSource i18n;
private final SoftwareModuleTypeDataProvider<ProxyTypeInfo> smTypeDataProvider;
@@ -56,7 +58,8 @@ public class SmWindowLayoutComponentBuilder {
*/
public ComboBox<ProxyTypeInfo> createSoftwareModuleTypeCombo(final Binder<ProxySoftwareModule> binder) {
return FormComponentBuilder
.createTypeCombo(binder, smTypeDataProvider, i18n, UIComponentIdProvider.SW_MODULE_TYPE, true).getComponent();
.createTypeCombo(binder, smTypeDataProvider, i18n, UIComponentIdProvider.SW_MODULE_TYPE, true)
.getComponent();
}
/**
@@ -113,4 +116,18 @@ public class SmWindowLayoutComponentBuilder {
return FormComponentBuilder
.createDescriptionInput(binder, i18n, UIComponentIdProvider.ADD_SW_MODULE_DESCRIPTION).getComponent();
}
/**
* Create checkbox for artifact encryption
*
* @param binder
* binder the input will be bound to
*
* @return input component
*/
public CheckBox createArtifactEncryptionCheck(final Binder<ProxySoftwareModule> binder) {
return FormComponentBuilder.createCheckBox(i18n.getMessage(ARTIFACT_ENCRYPTION),
UIComponentIdProvider.ARTIFACT_ENCRYPTION_ID, binder, ProxySoftwareModule::isEncrypted,
ProxySoftwareModule::setEncrypted);
}
}

View File

@@ -61,6 +61,7 @@ public class UpdateSmWindowController
sm.setVersion(proxyEntity.getVersion());
sm.setVendor(proxyEntity.getVendor());
sm.setDescription(proxyEntity.getDescription());
sm.setEncrypted(proxyEntity.isEncrypted());
nameBeforeEdit = proxyEntity.getName();
versionBeforeEdit = proxyEntity.getVersion();
@@ -78,6 +79,7 @@ public class UpdateSmWindowController
layout.disableSmTypeSelect();
layout.disableNameField();
layout.disableVersionField();
layout.disableEncryptionField();
}
@Override

View File

@@ -18,6 +18,8 @@ import java.util.concurrent.locks.Lock;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.RegexCharacterCollection;
import org.eclipse.hawkbit.repository.RegexCharacterCollection.RegexChar;
import org.eclipse.hawkbit.repository.exception.ArtifactEncryptionFailedException;
import org.eclipse.hawkbit.repository.exception.ArtifactEncryptionUnsupportedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.FileSizeQuotaExceededException;
@@ -137,6 +139,10 @@ public abstract class AbstractFileTransferHandler implements Serializable {
interruptUploadAndSetReason(i18n.getMessage("message.uploadedfile.illegalFilename"));
}
protected void interruptUploadDueToEncryptionError() {
interruptUploadAndSetReason(i18n.getMessage("message.encryption.failed"));
}
protected boolean isFileAlreadyContainedInSoftwareModule(final FileUploadId newFileUploadId,
final SoftwareModule softwareModule) {
for (final Artifact artifact : softwareModule.getArtifacts()) {
@@ -215,7 +221,7 @@ public abstract class AbstractFileTransferHandler implements Serializable {
try {
outputStream.close();
} catch (final IOException e1) {
LOG.warn("Closing output stream caused an exception {}", e1);
LOG.warn("Closing output stream caused by", e1);
}
}
@@ -226,7 +232,7 @@ public abstract class AbstractFileTransferHandler implements Serializable {
try {
inputStream.close();
} catch (final IOException e1) {
LOG.warn("Closing input stream caused an exception {}", e1);
LOG.warn("Closing input stream caused by", e1);
}
}
}
@@ -276,13 +282,20 @@ public abstract class AbstractFileTransferHandler implements Serializable {
streamToRepository();
} catch (final FileSizeQuotaExceededException e) {
interruptUploadDueToFileSizeQuotaExceeded(e.getExceededQuotaValueString());
publishUploadFailedAndFinishedEvent(fileUploadId);
LOG.debug("Upload failed due to file size quota exceeded:", e);
} catch (final StorageQuotaExceededException e) {
interruptUploadDueToStorageQuotaExceeded(e.getExceededQuotaValueString());
publishUploadFailedAndFinishedEvent(fileUploadId);
LOG.debug("Upload failed due to storage quota exceeded:", e);
} catch (final AssignmentQuotaExceededException e) {
interruptUploadDueToAssignmentQuotaExceeded();
publishUploadFailedAndFinishedEvent(fileUploadId);
LOG.debug("Upload failed due to assignment quota exceeded:", e);
} catch (final ArtifactEncryptionUnsupportedException | ArtifactEncryptionFailedException e) {
interruptUploadDueToEncryptionError();
publishUploadFailedAndFinishedEvent(fileUploadId);
LOG.warn("Upload failed due to encryption error", e);
} catch (final RuntimeException e) {
interruptUploadDueToUploadFailed();
publishUploadFailedAndFinishedEvent(fileUploadId);
@@ -297,25 +310,28 @@ public abstract class AbstractFileTransferHandler implements Serializable {
if (fileUploadId == null) {
throw new ArtifactUploadFailedException();
}
final String filename = fileUploadId.getFilename();
LOG.debug("Transfering file {} directly to repository", filename);
final Artifact artifact = uploadArtifact(filename);
if (isUploadInterrupted()) {
LOG.warn("Upload of {} was interrupted", filename);
handleUploadFailure(artifact);
publishUploadFinishedEvent(fileUploadId);
return;
}
publishUploadSucceeded(fileUploadId, artifact.getSize());
publishUploadFinishedEvent(fileUploadId);
publishArtifactsChanged(fileUploadId);
}
private Artifact uploadArtifact(final String filename) {
LOG.debug("Transfering file {} directly to repository", filename);
try {
return artifactManagement.create(new ArtifactUpload(inputStream, fileUploadId.getSoftwareModuleId(),
filename, null, null, null, true, mimeType, -1));
} catch (final ArtifactUploadFailedException | InvalidSHA1HashException | InvalidMD5HashException e) {
} catch (final InvalidSHA1HashException | InvalidMD5HashException e) {
throw new ArtifactUploadFailedException(e);
}
}

View File

@@ -9,9 +9,8 @@
package org.eclipse.hawkbit.ui.artifacts.upload;
import java.io.Serializable;
import java.util.Objects;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
@@ -21,19 +20,14 @@ import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
*
*/
public class FileUploadId implements Serializable {
private static final long serialVersionUID = 1L;
private final String filename;
private final Long softwareModuleId;
private final String softwareModuleName;
private final String softwareModuleVersion;
private Long softwareModuleId;
private final String id;
/**
* Creates a new {@link FileUploadId} instance.
*
@@ -44,36 +38,25 @@ public class FileUploadId implements Serializable {
*/
public FileUploadId(final String filename, final SoftwareModule softwareModule) {
this.filename = filename;
this.softwareModuleId = softwareModule.getId();
this.softwareModuleName = softwareModule.getName();
this.softwareModuleVersion = softwareModule.getVersion();
this.softwareModuleId = softwareModule.getId();
this.id = createFileUploadIdString(filename, softwareModuleName, softwareModuleVersion);
}
/**
* Creates a new {@link FileUploadId} instance.
*
* @param filename
* the name of the file
* @param softwareModuleName
* the name of a {@link SoftwareModule} for which the file is
* uploaded
* @param softwareModuleVersion
* the version of a {@link SoftwareModule} for which the file is
* uploaded
*/
public FileUploadId(final String filename, final String softwareModuleName, final String softwareModuleVersion) {
this.filename = filename;
this.softwareModuleName = softwareModuleName;
this.softwareModuleVersion = softwareModuleVersion;
this.id = createFileUploadIdString(filename, softwareModuleName, softwareModuleVersion);
public String getFilename() {
return filename;
}
private static String createFileUploadIdString(final String filename, final String softwareModuleName,
final String softwareModuleVersion) {
return new StringBuilder(filename).append(":")
.append(HawkbitCommonUtil.getFormattedNameVersion(softwareModuleName, softwareModuleVersion))
.toString();
public Long getSoftwareModuleId() {
return softwareModuleId;
}
public String getSoftwareModuleName() {
return softwareModuleName;
}
public String getSoftwareModuleVersion() {
return softwareModuleVersion;
}
@Override
@@ -88,52 +71,21 @@ public class FileUploadId implements Serializable {
return false;
}
final FileUploadId other = (FileUploadId) obj;
return new EqualsBuilder().append(id, other.id).isEquals();
return Objects.equals(this.getFilename(), other.getFilename())
&& Objects.equals(this.getSoftwareModuleId(), other.getSoftwareModuleId())
&& Objects.equals(this.getSoftwareModuleName(), other.getSoftwareModuleName())
&& Objects.equals(this.getSoftwareModuleVersion(), other.getSoftwareModuleVersion());
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(id).toHashCode();
return Objects.hash(getFilename(), getSoftwareModuleId(), getSoftwareModuleName(), getSoftwareModuleVersion());
}
@Override
public String toString() {
return id;
}
/**
* Getter for the uploaded file name
*
* @return String
*/
public String getFilename() {
return filename;
}
/**
* Getter for the software module name
*
* @return String
*/
public String getSoftwareModuleName() {
return softwareModuleName;
}
/**
* Getter for the software module version
*
* @return String
*/
public String getSoftwareModuleVersion() {
return softwareModuleVersion;
}
/**
* Getter for the software module ID
*
* @return Long
*/
public Long getSoftwareModuleId() {
return softwareModuleId;
return new StringBuilder(filename).append(":")
.append(HawkbitCommonUtil.getFormattedNameVersion(softwareModuleName, softwareModuleVersion))
.toString();
}
}

View File

@@ -377,9 +377,9 @@ public final class FormComponentBuilder {
* setter for the binder
* @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) {
return getCheckBox(null, id, binder, getter, setter);
return createCheckBox(null, id, binder, getter, setter);
}
/**
@@ -399,7 +399,7 @@ public final class FormComponentBuilder {
* setter for the binder
* @return the bound box
*/
public static <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 CheckBox checkBox;
if (StringUtils.isEmpty(caption)) {

View File

@@ -31,6 +31,7 @@ public class SoftwareModuleToProxyMapper
proxySoftwareModule.setNameAndVersion(
HawkbitCommonUtil.concatStrings(":", softwareModule.getName(), softwareModule.getVersion()));
proxySoftwareModule.setVendor(softwareModule.getVendor());
proxySoftwareModule.setEncrypted(softwareModule.isEncrypted());
final SoftwareModuleType type = softwareModule.getType();
final ProxyTypeInfo typeInfo = new ProxyTypeInfo(type.getId(), type.getName(), type.getKey());

View File

@@ -29,6 +29,8 @@ public class ProxySoftwareModule extends ProxyNamedEntity implements VersionAwar
private boolean assigned;
private boolean encrypted;
/**
* Gets the software module vendor
*
@@ -129,4 +131,12 @@ public class ProxySoftwareModule extends ProxyNamedEntity implements VersionAwar
public void setVersion(final String version) {
this.version = version;
}
public boolean isEncrypted() {
return encrypted;
}
public void setEncrypted(final boolean encrypted) {
this.encrypted = encrypted;
}
}

View File

@@ -91,7 +91,7 @@ public class MetaDataAddUpdateWindowLayoutComponentBuilder {
* @return Target field CheckBox
*/
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,
ProxyMetaData::setVisibleForTargets);
}

View File

@@ -54,7 +54,8 @@ public class SoftwareModuleDetails extends AbstractGridDetailsLayout<ProxySoftwa
* @param smMetaDataWindowBuilder
* SmMetaDataWindowBuilder
*/
public SoftwareModuleDetails(final CommonUiDependencies uiDependencies, final SoftwareModuleManagement softwareManagement,
public SoftwareModuleDetails(final CommonUiDependencies uiDependencies,
final SoftwareModuleManagement softwareManagement,
final SoftwareModuleTypeManagement softwareModuleTypeManagement,
final SmMetaDataWindowBuilder smMetaDataWindowBuilder) {
super(uiDependencies.getI18n());
@@ -93,6 +94,10 @@ public class SoftwareModuleDetails extends AbstractGridDetailsLayout<ProxySoftwa
: 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;
}

View File

@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.ui.common.grid.support;
import java.util.function.Consumer;
import java.util.function.Function;
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> {
private final FilterSupport<?, ?> filterSupport;
private final Function<M, ?> masterEntityToFilterMapper;
private final Consumer<M> postMasterChangeCallback;
private Long masterId;
@@ -47,8 +49,24 @@ public class MasterEntitySupport<M extends ProxyIdentifiableEntity> implements M
*/
public MasterEntitySupport(final FilterSupport<?, ?> filterSupport,
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.masterEntityToFilterMapper = masterEntityToFilterMapper;
this.postMasterChangeCallback = postMasterChangeCallback;
}
@Override
@@ -60,6 +78,10 @@ public class MasterEntitySupport<M extends ProxyIdentifiableEntity> implements M
filterSupport.updateFilter(FilterType.MASTER, getMasterEntityFilter(masterEntity));
masterId = masterEntity != null ? masterEntity.getId() : null;
if (postMasterChangeCallback != null) {
postMasterChangeCallback.accept(masterEntity);
}
}
private Object getMasterEntityFilter(final M masterEntity) {

View File

@@ -82,6 +82,6 @@ public class SmTypeSelectedGrid extends Grid<ProxyType> {
binder.addValueChangeListener(event -> mandatoryPropertyChangedCallback.run());
final String id = "selected.sm.type." + smType.getId();
return FormComponentBuilder.getCheckBox(id, binder, ProxyType::isMandatory, ProxyType::setMandatory);
return FormComponentBuilder.createCheckBox(id, binder, ProxyType::isMandatory, ProxyType::setMandatory);
}
}

View File

@@ -107,7 +107,7 @@ public class DsWindowLayoutComponentBuilder {
* @return Migration step required checkbox
*/
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,
ProxyDistributionSet::setRequiredMigrationStep);

View File

@@ -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);
}
}
}

View File

@@ -21,7 +21,7 @@ public class UploadErrorExtractor extends AbstractSingleUiErrorDetailsExtractor
@Override
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());
}
}

View File

@@ -65,7 +65,7 @@ public class AutoAssignmentWindowLayoutComponentBuilder {
*/
public CheckBox createEnableCheckbox(final Binder<ProxyTargetFilterQuery> binder) {
final String caption = i18n.getMessage(UIMessageIdProvider.LABEL_AUTO_ASSIGNMENT_ENABLE);
return FormComponentBuilder.getCheckBox(caption, UIComponentIdProvider.DIST_SET_SELECT_ENABLE_ID, binder,
return FormComponentBuilder.createCheckBox(caption, UIComponentIdProvider.DIST_SET_SELECT_ENABLE_ID, binder,
ProxyTargetFilterQuery::isAutoAssignmentEnabled, ProxyTargetFilterQuery::setAutoAssignmentEnabled);
}

View File

@@ -81,7 +81,7 @@ public class AssignmentWindowLayoutComponentBuilder {
* @return Maintenance window checkbox
*/
public CheckBox createEnableMaintenanceWindowToggle(final Binder<ProxyAssignmentWindow> binder) {
final CheckBox maintenanceWindowToggle = FormComponentBuilder.getCheckBox(
final CheckBox maintenanceWindowToggle = FormComponentBuilder.createCheckBox(
i18n.getMessage("caption.maintenancewindow.enabled"),
UIComponentIdProvider.MAINTENANCE_WINDOW_ENABLED_ID, binder,
ProxyAssignmentWindow::isMaintenanceWindowEnabled, ProxyAssignmentWindow::setMaintenanceWindowEnabled);

View File

@@ -106,7 +106,7 @@ public class AuthenticationConfigurationView extends BaseConfigurationView<Proxy
}
protected void initCertificateAuthConfiguration(GridLayout gridLayout, int row) {
final CheckBox certificateAuthCheckbox = FormComponentBuilder.getCheckBox(
final CheckBox certificateAuthCheckbox = FormComponentBuilder.createCheckBox(
UIComponentIdProvider.CERT_AUTH_ALLOWED_CHECKBOX, getBinder(),
ProxySystemConfigAuthentication::isCertificateAuth,
ProxySystemConfigAuthentication::setCertificateAuth);
@@ -123,7 +123,7 @@ public class AuthenticationConfigurationView extends BaseConfigurationView<Proxy
}
protected void initTargetTokenConfiguration(GridLayout gridLayout, int row) {
final CheckBox targetSecTokenCheckBox = FormComponentBuilder.getCheckBox(
final CheckBox targetSecTokenCheckBox = FormComponentBuilder.createCheckBox(
UIComponentIdProvider.TARGET_SEC_TOKEN_ALLOWED_CHECKBOX, getBinder(),
ProxySystemConfigAuthentication::isTargetSecToken, ProxySystemConfigAuthentication::setTargetSecToken);
targetSecTokenCheckBox.setStyleName(DIST_CHECKBOX_STYLE);
@@ -132,7 +132,7 @@ public class AuthenticationConfigurationView extends BaseConfigurationView<Proxy
}
protected void initGatewayTokenConfiguration(GridLayout gridLayout, int row) {
final CheckBox gatewaySecTokenCheckBox = FormComponentBuilder.getCheckBox(
final CheckBox gatewaySecTokenCheckBox = FormComponentBuilder.createCheckBox(
UIComponentIdProvider.GATEWAY_SEC_TOKEN_ALLOWED_CHECKBOX, getBinder(),
ProxySystemConfigAuthentication::isGatewaySecToken,
ProxySystemConfigAuthentication::setGatewaySecToken);
@@ -149,7 +149,7 @@ public class AuthenticationConfigurationView extends BaseConfigurationView<Proxy
}
protected void initAnonymousDownloadConfiguration(GridLayout gridLayout, int row) {
final CheckBox downloadAnonymousCheckBox = FormComponentBuilder.getCheckBox(
final CheckBox downloadAnonymousCheckBox = FormComponentBuilder.createCheckBox(
UIComponentIdProvider.DOWNLOAD_ANONYMOUS_CHECKBOX, getBinder(),
ProxySystemConfigAuthentication::isDownloadAnonymous,
ProxySystemConfigAuthentication::setDownloadAnonymous);

View File

@@ -98,7 +98,7 @@ public class RepositoryConfigurationView extends BaseConfigurationView<ProxySyst
gridLayout.setColumnExpandRatio(1, 1.0F);
gridLayout.setSizeFull();
final CheckBox actionAutoCloseCheckBox = FormComponentBuilder.getCheckBox(
final CheckBox actionAutoCloseCheckBox = FormComponentBuilder.createCheckBox(
UIComponentIdProvider.REPOSITORY_ACTIONS_AUTOCLOSE_CHECKBOX, getBinder(),
ProxySystemConfigRepository::isActionAutoclose, ProxySystemConfigRepository::setActionAutoclose);
actionAutoCloseCheckBox.setStyleName(DIST_CHECKBOX_STYLE);
@@ -107,7 +107,7 @@ public class RepositoryConfigurationView extends BaseConfigurationView<ProxySyst
gridLayout.addComponent(actionAutoCloseCheckBox, 0, 0);
gridLayout.addComponent(actionAutocloseConfigurationItem, 1, 0);
multiAssignmentsCheckBox = FormComponentBuilder.getCheckBox(
multiAssignmentsCheckBox = FormComponentBuilder.createCheckBox(
UIComponentIdProvider.REPOSITORY_MULTI_ASSIGNMENTS_CHECKBOX, getBinder(),
ProxySystemConfigRepository::isMultiAssignments, ProxySystemConfigRepository::setMultiAssignments);
multiAssignmentsCheckBox.setStyleName(DIST_CHECKBOX_STYLE);
@@ -125,7 +125,7 @@ public class RepositoryConfigurationView extends BaseConfigurationView<ProxySyst
gridLayout.addComponent(multiAssignmentsCheckBox, 0, 1);
gridLayout.addComponent(multiAssignmentsConfigurationItem, 1, 1);
final CheckBox actionAutoCleanupCheckBox = FormComponentBuilder.getCheckBox(
final CheckBox actionAutoCleanupCheckBox = FormComponentBuilder.createCheckBox(
UIComponentIdProvider.REPOSITORY_ACTIONS_AUTOCLEANUP_CHECKBOX, getBinder(),
ProxySystemConfigRepository::isActionAutocleanup, ProxySystemConfigRepository::setActionAutocleanup);
actionAutoCleanupCheckBox.setStyleName(DIST_CHECKBOX_STYLE);

View File

@@ -71,7 +71,7 @@ public class RolloutConfigurationView extends BaseConfigurationView<ProxySystemC
gridLayout.setColumnExpandRatio(1, 1.0F);
gridLayout.setSizeFull();
final CheckBox approvalCheckbox = FormComponentBuilder.getCheckBox(
final CheckBox approvalCheckbox = FormComponentBuilder.createCheckBox(
UIComponentIdProvider.ROLLOUT_APPROVAL_ENABLED_CHECKBOX, getBinder(),
ProxySystemConfigRollout::isRolloutApproval, ProxySystemConfigRollout::setRolloutApproval);

View File

@@ -682,7 +682,7 @@ public final class UIComponentIdProvider {
/**
* 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.
@@ -697,7 +697,7 @@ public final class UIComponentIdProvider {
/**
* 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.
@@ -785,9 +785,14 @@ public final class UIComponentIdProvider {
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.
@@ -1431,7 +1436,8 @@ public final class UIComponentIdProvider {
*/
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";
/**
@@ -1581,6 +1587,11 @@ public final class UIComponentIdProvider {
*/
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.
*/

View File

@@ -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_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 TOOLTIP_OVERDUE = "tooltip.overdue";

View File

@@ -198,6 +198,9 @@ label.target.count = Targets
label.description = Description
label.ip = Address
label.type = Type
label.artifact.encryption = Artifact encryption
label.enabled = Enabled
label.disabled = Disabled
label.assigned.type = Assignment type
label.assigned.count = {0} Assigned
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.artifact.deleted = Artifact with file {0} deleted successfully
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.status.caption = Status
@@ -573,6 +580,7 @@ artifact.upload.progress.caption = Progress
artifact.upload.reason.caption = Reason
artifact.filename.caption = File name
artifact.filesize.bytes.caption = Size(B)
artifact.encryption = Enable artifact encryption
tooltip.upload.status.inprogress = In progress
tooltip.upload.status.finished = Finished