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:
@@ -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.HashNotMatchException;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
||||
import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
|
||||
@@ -104,15 +106,24 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
|
||||
assertArtifactQuota(moduleId, 1);
|
||||
|
||||
final AbstractDbArtifact artifact = storeArtifact(artifactUpload);
|
||||
final AbstractDbArtifact artifact = storeArtifact(artifactUpload, softwareModule.isEncrypted());
|
||||
return storeArtifactMetadata(softwareModule, filename, artifact, existing);
|
||||
}
|
||||
|
||||
private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload) {
|
||||
try (final InputStream quotaStream = wrapInQuotaStream(artifactUpload.getInputStream())) {
|
||||
return artifactRepository.store(tenantAware.getCurrentTenant(), quotaStream, artifactUpload.getFilename(),
|
||||
artifactUpload.getContentType(), new DbArtifactHash(artifactUpload.getProvidedSha1Sum(),
|
||||
artifactUpload.getProvidedMd5Sum(), artifactUpload.getProvidedSha256Sum()));
|
||||
private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload, final boolean isSmEncrypted) {
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
final long smId = artifactUpload.getModuleId();
|
||||
final InputStream stream = artifactUpload.getInputStream();
|
||||
final String fileName = artifactUpload.getFilename();
|
||||
final String contentType = artifactUpload.getContentType();
|
||||
final String providedSha1 = artifactUpload.getProvidedSha1Sum();
|
||||
final String providedMd5 = artifactUpload.getProvidedMd5Sum();
|
||||
final String providedSha256 = artifactUpload.getProvidedSha256Sum();
|
||||
|
||||
try (final InputStream wrappedStream = wrapInQuotaStream(
|
||||
isSmEncrypted ? wrapInEncryptionStream(smId, stream) : stream)) {
|
||||
return artifactRepository.store(tenant, wrappedStream, fileName, contentType,
|
||||
new DbArtifactHash(providedSha1, providedMd5, providedSha256));
|
||||
} catch (final ArtifactStoreException | IOException e) {
|
||||
throw new ArtifactUploadFailedException(e);
|
||||
} catch (final HashNotMatchException e) {
|
||||
@@ -126,6 +137,10 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream wrapInEncryptionStream(final long smId, final InputStream stream) {
|
||||
return ArtifactEncryptionService.getInstance().encryptSoftwareModuleArtifact(smId, stream);
|
||||
}
|
||||
|
||||
private void assertArtifactQuota(final long id, final int requested) {
|
||||
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxArtifactsPerSoftwareModule(),
|
||||
Artifact.class, SoftwareModule.class, localArtifactRepository::countBySoftwareModuleId);
|
||||
@@ -212,10 +227,26 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<AbstractDbArtifact> loadArtifactBinary(final String sha1Hash) {
|
||||
return Optional.ofNullable(artifactRepository.existsByTenantAndSha1(tenantAware.getCurrentTenant(), sha1Hash)
|
||||
? artifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), sha1Hash)
|
||||
: null);
|
||||
public Optional<DbArtifact> loadArtifactBinary(final String sha1Hash, final long softwareModuleId,
|
||||
final boolean isEncrypted) {
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
if (artifactRepository.existsByTenantAndSha1(tenant, sha1Hash)) {
|
||||
final DbArtifact dbArtifact = artifactRepository.getArtifactBySha1(tenant, sha1Hash);
|
||||
return Optional.ofNullable(
|
||||
isEncrypted ? wrapInEncryptionAwareDbArtifact(softwareModuleId, dbArtifact) : dbArtifact);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private final DbArtifact wrapInEncryptionAwareDbArtifact(final long smId, final DbArtifact dbArtifact) {
|
||||
if (dbArtifact == null) {
|
||||
return null;
|
||||
}
|
||||
final ArtifactEncryptionService encryptionService = ArtifactEncryptionService.getInstance();
|
||||
return new EncryptionAwareDbArtifact(dbArtifact,
|
||||
stream -> encryptionService.decryptSoftwareModuleArtifact(smId, stream),
|
||||
encryptionService.encryptionSizeOverhead());
|
||||
}
|
||||
|
||||
private Artifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename,
|
||||
|
||||
@@ -31,6 +31,7 @@ import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
@@ -155,7 +156,14 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
public SoftwareModule create(final SoftwareModuleCreate c) {
|
||||
final JpaSoftwareModuleCreate create = (JpaSoftwareModuleCreate) c;
|
||||
|
||||
return softwareModuleRepository.save(create.build());
|
||||
final JpaSoftwareModule sm = softwareModuleRepository.save(create.build());
|
||||
if (create.isEncrypted()) {
|
||||
// flush sm creation in order to get an Id
|
||||
entityManager.flush();
|
||||
ArtifactEncryptionService.getInstance().addSoftwareModuleEncryptionSecrets(sm.getId());
|
||||
}
|
||||
|
||||
return sm;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -295,8 +303,8 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModule> findByRsql(final Pageable pageable, final String rsqlParam) {
|
||||
final Specification<JpaSoftwareModule> spec = RSQLUtility.buildRsqlSpecification(rsqlParam, SoftwareModuleFields.class,
|
||||
virtualPropertyReplacer, database);
|
||||
final Specification<JpaSoftwareModule> spec = RSQLUtility.buildRsqlSpecification(rsqlParam,
|
||||
SoftwareModuleFields.class, virtualPropertyReplacer, database);
|
||||
|
||||
return convertSmPage(softwareModuleRepository.findAll(spec, pageable), pageable);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ import javax.persistence.EntityManager;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
|
||||
import org.eclipse.hawkbit.repository.ArtifactEncryption;
|
||||
import org.eclipse.hawkbit.repository.ArtifactEncryptionSecretsStore;
|
||||
import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.BaseRepositoryTypeProvider;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
@@ -245,7 +248,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
/**
|
||||
* @param dsTypeManagement
|
||||
* for loading {@link TargetType#getCompatibleDistributionSetTypes()}
|
||||
* for loading
|
||||
* {@link TargetType#getCompatibleDistributionSetTypes()}
|
||||
* @return TargetTypeBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
@@ -261,8 +265,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
/**
|
||||
* @param softwareManagement
|
||||
* for loading {@link DistributionSetType#getMandatoryModuleTypes()}
|
||||
* and {@link DistributionSetType#getOptionalModuleTypes()}
|
||||
* for loading
|
||||
* {@link DistributionSetType#getMandatoryModuleTypes()} and
|
||||
* {@link DistributionSetType#getOptionalModuleTypes()}
|
||||
* @return DistributionSetTypeBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
@@ -304,8 +309,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
/**
|
||||
* @return the {@link SystemSecurityContext} singleton bean which make it
|
||||
* accessible in beans which cannot access the service directly, e.g.
|
||||
* JPA entities.
|
||||
* accessible in beans which cannot access the service directly,
|
||||
* e.g. JPA entities.
|
||||
*/
|
||||
@Bean
|
||||
SystemSecurityContextHolder systemSecurityContextHolder() {
|
||||
@@ -313,9 +318,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link TenantConfigurationManagement} singleton bean which make
|
||||
* it accessible in beans which cannot access the service directly, e.g.
|
||||
* JPA entities.
|
||||
* @return the {@link TenantConfigurationManagement} singleton bean which
|
||||
* make it accessible in beans which cannot access the service
|
||||
* directly, e.g. JPA entities.
|
||||
*/
|
||||
@Bean
|
||||
TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
|
||||
@@ -324,8 +329,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
/**
|
||||
* @return the {@link SystemManagementHolder} singleton bean which holds the
|
||||
* current {@link SystemManagement} service and make it accessible in
|
||||
* beans which cannot access the service directly, e.g. JPA entities.
|
||||
* current {@link SystemManagement} service and make it accessible
|
||||
* in beans which cannot access the service directly, e.g. JPA
|
||||
* entities.
|
||||
*/
|
||||
@Bean
|
||||
SystemManagementHolder systemManagementHolder() {
|
||||
@@ -333,9 +339,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link TenantAwareHolder} singleton bean which holds the current
|
||||
* {@link TenantAware} service and make it accessible in beans which
|
||||
* cannot access the service directly, e.g. JPA entities.
|
||||
* @return the {@link TenantAwareHolder} singleton bean which holds the
|
||||
* current {@link TenantAware} service and make it accessible in
|
||||
* beans which cannot access the service directly, e.g. JPA
|
||||
* entities.
|
||||
*/
|
||||
@Bean
|
||||
TenantAwareHolder tenantAwareHolder() {
|
||||
@@ -343,9 +350,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link SecurityTokenGeneratorHolder} singleton bean which holds
|
||||
* the current {@link SecurityTokenGenerator} service and make it
|
||||
* accessible in beans which cannot access the service via injection
|
||||
* @return the {@link SecurityTokenGeneratorHolder} singleton bean which
|
||||
* holds the current {@link SecurityTokenGenerator} service and make
|
||||
* it accessible in beans which cannot access the service via
|
||||
* injection
|
||||
*/
|
||||
@Bean
|
||||
SecurityTokenGeneratorHolder securityTokenGeneratorHolder() {
|
||||
@@ -940,7 +948,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Our default {@link BaseRepositoryTypeProvider} bean always provides the NoCountBaseRepository
|
||||
* Our default {@link BaseRepositoryTypeProvider} bean always provides the
|
||||
* NoCountBaseRepository
|
||||
*
|
||||
* @return a {@link BaseRepositoryTypeProvider} bean
|
||||
*/
|
||||
@@ -950,4 +959,17 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
return new NoCountBaseRepositoryTypeProvider();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Default artifact encryption service bean that internally uses
|
||||
* {@link ArtifactEncryption} and {@link ArtifactEncryptionSecretsStore}
|
||||
* beans for {@link SoftwareModule} artifacts encryption/decryption
|
||||
*
|
||||
* @return a {@link ArtifactEncryptionService} bean
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ArtifactEncryptionService artifactEncryptionService() {
|
||||
return ArtifactEncryptionService.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,13 +26,26 @@ public class JpaSoftwareModuleCreate extends AbstractSoftwareModuleUpdateCreate<
|
||||
|
||||
private final SoftwareModuleTypeManagement softwareModuleTypeManagement;
|
||||
|
||||
private boolean encrypted;
|
||||
|
||||
JpaSoftwareModuleCreate(final SoftwareModuleTypeManagement softwareModuleTypeManagement) {
|
||||
this.softwareModuleTypeManagement = softwareModuleTypeManagement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleCreate encrypted(final boolean encrypted) {
|
||||
this.encrypted = encrypted;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaSoftwareModule build() {
|
||||
return new JpaSoftwareModule(getSoftwareModuleTypeFromKeyString(type), name, version, description, vendor);
|
||||
return new JpaSoftwareModule(getSoftwareModuleTypeFromKeyString(type), name, version, description, vendor,
|
||||
encrypted);
|
||||
}
|
||||
|
||||
private SoftwareModuleType getSoftwareModuleTypeFromKeyString(final String type) {
|
||||
|
||||
@@ -90,6 +90,9 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
||||
@OneToMany(mappedBy = "softwareModule", fetch = FetchType.LAZY, targetEntity = JpaSoftwareModuleMetadata.class)
|
||||
private List<JpaSoftwareModuleMetadata> metadata;
|
||||
|
||||
@Column(name = "encrypted")
|
||||
private boolean encrypted;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
@@ -97,6 +100,20 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
||||
// Default constructor for JPA
|
||||
}
|
||||
|
||||
/**
|
||||
* parameterized constructor.
|
||||
*
|
||||
* @param type
|
||||
* of the {@link SoftwareModule}
|
||||
* @param name
|
||||
* abstract name of the {@link SoftwareModule}
|
||||
* @param version
|
||||
* of the {@link SoftwareModule}
|
||||
*/
|
||||
public JpaSoftwareModule(final SoftwareModuleType type, final String name, final String version) {
|
||||
this(type, name, version, null, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* parameterized constructor.
|
||||
*
|
||||
@@ -110,12 +127,15 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
||||
* of the {@link SoftwareModule}
|
||||
* @param vendor
|
||||
* of the {@link SoftwareModule}
|
||||
* @param encrypted
|
||||
* encryption flag of the {@link SoftwareModule}
|
||||
*/
|
||||
public JpaSoftwareModule(final SoftwareModuleType type, final String name, final String version,
|
||||
final String description, final String vendor) {
|
||||
final String description, final String vendor, final boolean encrypted) {
|
||||
super(name, version, description);
|
||||
this.vendor = vendor;
|
||||
this.type = (JpaSoftwareModuleType) type;
|
||||
this.encrypted = encrypted;
|
||||
}
|
||||
|
||||
public void addArtifact(final Artifact artifact) {
|
||||
@@ -175,8 +195,8 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
||||
* Marks or un-marks this software module as deleted.
|
||||
*
|
||||
* @param deleted
|
||||
* {@code true} if the software module should be marked as
|
||||
* deleted otherwise {@code false}
|
||||
* {@code true} if the software module should be marked as deleted
|
||||
* otherwise {@code false}
|
||||
*/
|
||||
public void setDeleted(final boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
@@ -186,10 +206,27 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEncrypted() {
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this software module as encrypted.
|
||||
*
|
||||
* @param encrypted
|
||||
* {@code true} if the software module should be marked as encrypted
|
||||
* otherwise {@code false}
|
||||
*/
|
||||
public void setEncrypted(final boolean encrypted) {
|
||||
this.encrypted = encrypted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SoftwareModule [deleted=" + deleted + ", name=" + getName() + ", version=" + getVersion()
|
||||
+ ", revision=" + getOptLockRevision() + ", Id=" + getId() + ", type=" + getType().getName() + "]";
|
||||
return "SoftwareModule [deleted=" + isDeleted() + ", encrypted=" + isEncrypted() + ", name=" + getName()
|
||||
+ ", version=" + getVersion() + ", revision=" + getOptLockRevision() + ", Id=" + getId() + ", type="
|
||||
+ getType().getName() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
|
||||
UPDATE sp_distribution_set SET valid = 1;
|
||||
ALTER TABLE sp_distribution_set ADD valid BIT DEFAULT 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.lang3.RandomStringUtils;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
@@ -77,7 +77,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
.isFalse();
|
||||
|
||||
assertThat(artifactManagement.findFirstBySHA1(NOT_EXIST_ID)).isNotPresent();
|
||||
assertThat(artifactManagement.loadArtifactBinary(NOT_EXIST_ID)).isNotPresent();
|
||||
assertThat(artifactManagement.loadArtifactBinary(NOT_EXIST_ID, module.getId(), module.isEncrypted()))
|
||||
.isNotPresent();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,10 +117,10 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(artifactRepository.findAll()).hasSize(0);
|
||||
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1"));
|
||||
final JpaSoftwareModule sm2 = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
|
||||
softwareModuleRepository.save(new JpaSoftwareModule(osType, "name 3", "version 3", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2"));
|
||||
softwareModuleRepository.save(new JpaSoftwareModule(osType, "name 3", "version 3"));
|
||||
|
||||
final int artifactSize = 5 * 1024;
|
||||
final byte[] randomBytes = randomBytes(artifactSize);
|
||||
@@ -165,8 +166,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
final String artifactData = "test";
|
||||
final int artifactSize = artifactData.length();
|
||||
|
||||
final long smID = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "smIllegalFilenameTest", "1.0", null, null)).getId();
|
||||
final long smID = softwareModuleRepository.save(new JpaSoftwareModule(osType, "smIllegalFilenameTest", "1.0"))
|
||||
.getId();
|
||||
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
|
||||
() -> artifactManagement.create(new ArtifactUpload(IOUtils.toInputStream(artifactData, "UTF-8"), smID,
|
||||
illegalFilename, false, artifactSize)));
|
||||
@@ -178,8 +179,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
public void createArtifactsUntilQuotaIsExceeded() throws IOException {
|
||||
|
||||
// create a software module
|
||||
final long smId = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0", null, null))
|
||||
.getId();
|
||||
final long smId = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0")).getId();
|
||||
|
||||
// now create artifacts for this module until the quota is exceeded
|
||||
final long maxArtifacts = quotaManagement.getMaxArtifactsPerSoftwareModule();
|
||||
@@ -217,14 +217,13 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
final int artifactSize = Math.toIntExact(quotaManagement.getMaxArtifactSize() / 10);
|
||||
final int numArtifacts = Math.toIntExact(maxBytes / artifactSize);
|
||||
for (int i = 0; i < numArtifacts; ++i) {
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "smd" + i, "1.0", null, null));
|
||||
final JpaSoftwareModule sm = softwareModuleRepository.save(new JpaSoftwareModule(osType, "smd" + i, "1.0"));
|
||||
artifactIds.add(createArtifactForSoftwareModule("file" + i, sm.getId(), artifactSize).getId());
|
||||
}
|
||||
|
||||
// upload one more artifact to trigger the quota exceeded error
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "smd" + numArtifacts, "1.0", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "smd" + numArtifacts, "1.0"));
|
||||
assertThatExceptionOfType(StorageQuotaExceededException.class)
|
||||
.isThrownBy(() -> createArtifactForSoftwareModule("file" + numArtifacts, sm.getId(), artifactSize));
|
||||
|
||||
@@ -240,8 +239,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
public void createArtifactFailsIfTooLarge() {
|
||||
|
||||
// create a software module
|
||||
final JpaSoftwareModule sm1 = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "sm1", "1.0", null, null));
|
||||
final JpaSoftwareModule sm1 = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0"));
|
||||
|
||||
// create an artifact that exceeds the configured quota
|
||||
final long maxSize = quotaManagement.getMaxArtifactSize();
|
||||
@@ -254,7 +252,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
public void hardDeleteSoftwareModule() throws IOException {
|
||||
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1"));
|
||||
|
||||
createArtifactForSoftwareModule("file1", sm.getId(), 5 * 1024);
|
||||
assertThat(artifactRepository.findAll()).hasSize(1);
|
||||
@@ -273,9 +271,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
@Description("Tests the deletion of a local artifact including metadata.")
|
||||
public void deleteArtifact() throws IOException {
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1"));
|
||||
final JpaSoftwareModule sm2 = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2"));
|
||||
|
||||
assertThat(artifactRepository.findAll()).isEmpty();
|
||||
|
||||
@@ -326,9 +324,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
public void deleteDuplicateArtifacts() throws IOException {
|
||||
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1"));
|
||||
final JpaSoftwareModule sm2 = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2"));
|
||||
|
||||
final int artifactSize = 5 * 1024;
|
||||
final byte[] randomBytes = randomBytes(artifactSize);
|
||||
@@ -368,9 +366,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
public void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInSameTenants() throws IOException {
|
||||
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1"));
|
||||
final JpaSoftwareModule sm2 = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2"));
|
||||
|
||||
final int artifactSize = 5 * 1024;
|
||||
final byte[] randomBytes = randomBytes(artifactSize);
|
||||
@@ -382,7 +380,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Artifact artifact2 = createArtifactForSoftwareModule("file2", sm2.getId(), artifactSize,
|
||||
inputStream2);
|
||||
|
||||
assertEqualFileContents(artifactManagement.loadArtifactBinary(artifact2.getSha1Hash()), randomBytes);
|
||||
assertEqualFileContents(
|
||||
artifactManagement.loadArtifactBinary(artifact2.getSha1Hash(), sm2.getId(), sm2.isEncrypted()),
|
||||
randomBytes);
|
||||
|
||||
assertThat(artifactRepository.findAll()).hasSize(2);
|
||||
|
||||
@@ -447,9 +447,11 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
final int artifactSize = 5 * 1024;
|
||||
final byte[] randomBytes = randomBytes(artifactSize);
|
||||
try (final InputStream input = new ByteArrayInputStream(randomBytes)) {
|
||||
final Artifact artifact = createArtifactForSoftwareModule("file1",
|
||||
testdataFactory.createSoftwareModuleOs().getId(), artifactSize, input);
|
||||
assertEqualFileContents(artifactManagement.loadArtifactBinary(artifact.getSha1Hash()), randomBytes);
|
||||
final SoftwareModule smOs = testdataFactory.createSoftwareModuleOs();
|
||||
final Artifact artifact = createArtifactForSoftwareModule("file1", smOs.getId(), artifactSize, input);
|
||||
assertEqualFileContents(
|
||||
artifactManagement.loadArtifactBinary(artifact.getSha1Hash(), smOs.getId(), smOs.isEncrypted()),
|
||||
randomBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,7 +461,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
public void loadArtifactBinaryWithoutDownloadArtifactThrowsPermissionDenied() {
|
||||
assertThatExceptionOfType(InsufficientPermissionException.class)
|
||||
.as("Should not have worked with missing permission.")
|
||||
.isThrownBy(() -> artifactManagement.loadArtifactBinary("123"));
|
||||
.isThrownBy(() -> artifactManagement.loadArtifactBinary("123", 1, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -541,7 +543,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(createdArtifact.getSha256Hash()).isEqualTo(artifactHashes.getSha256());
|
||||
}
|
||||
|
||||
final Optional<AbstractDbArtifact> dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1());
|
||||
final Optional<DbArtifact> dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1(),
|
||||
sm.getId(), sm.isEncrypted());
|
||||
assertThat(dbArtifact).isPresent();
|
||||
}
|
||||
|
||||
@@ -561,7 +564,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Artifact artifact = artifactManagement.create(artifactUpload);
|
||||
assertThat(artifact).isNotNull();
|
||||
}
|
||||
final Optional<AbstractDbArtifact> dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1());
|
||||
final Optional<DbArtifact> dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1(),
|
||||
smOs.getId(), smOs.isEncrypted());
|
||||
assertThat(dbArtifact).isPresent();
|
||||
|
||||
try (final InputStream inputStream = new ByteArrayInputStream(testData)) {
|
||||
@@ -622,10 +626,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(runAsTenant(tenant, () -> artifactRepository.findAll())).hasSize(count);
|
||||
}
|
||||
|
||||
private void assertEqualFileContents(final Optional<AbstractDbArtifact> artifact, final byte[] randomBytes)
|
||||
private void assertEqualFileContents(final Optional<DbArtifact> artifact, final byte[] randomBytes)
|
||||
throws IOException {
|
||||
try (final InputStream inputStream = artifactManagement.loadArtifactBinary(artifact.get().getHashes().getSha1())
|
||||
.get().getFileInputStream()) {
|
||||
try (final InputStream inputStream = artifact.get().getFileInputStream()) {
|
||||
assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(randomBytes), inputStream),
|
||||
"The stored binary matches the given binary");
|
||||
}
|
||||
|
||||
@@ -520,13 +520,13 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
Arrays.asList(testType.getId()));
|
||||
|
||||
// found in test
|
||||
final SoftwareModule unassigned = testdataFactory.createSoftwareModule("thetype", "unassignedfound");
|
||||
final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound");
|
||||
final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound");
|
||||
final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "a");
|
||||
final SoftwareModule unassigned = testdataFactory.createSoftwareModule("thetype", "unassignedfound", false);
|
||||
final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound", false);
|
||||
final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound", false);
|
||||
final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "a", false);
|
||||
|
||||
// ignored
|
||||
final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted");
|
||||
final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted", false);
|
||||
final SoftwareModule four = testdataFactory.createSoftwareModuleOs("e");
|
||||
|
||||
final DistributionSet set = distributionSetManagement
|
||||
@@ -572,13 +572,13 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
Arrays.asList(testType.getId()));
|
||||
|
||||
// found in test
|
||||
testdataFactory.createSoftwareModule("thetype", "unassignedfound");
|
||||
final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound");
|
||||
final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound");
|
||||
final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "d");
|
||||
testdataFactory.createSoftwareModule("thetype", "unassignedfound", false);
|
||||
final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound", false);
|
||||
final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound", false);
|
||||
final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "d", false);
|
||||
|
||||
// ignored
|
||||
final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted");
|
||||
final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted", false);
|
||||
final SoftwareModule four = testdataFactory.createSoftwareModuleOs("e");
|
||||
|
||||
distributionSetManagement
|
||||
|
||||
Reference in New Issue
Block a user