Remove DMF API dependency from security integration (#604)

* Dmf security token out of API.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Allow to override dispatching routines.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* TargetAssign event is bulk ready.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Completed Javadoc.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* readibility and fix serialization bug.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Fix sonar issue.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Simplify artifact management usage.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2017-12-07 15:55:09 +01:00
committed by GitHub
parent e22f25cc61
commit 5a6fc37a15
34 changed files with 289 additions and 348 deletions

View File

@@ -13,20 +13,18 @@ import java.util.Optional;
import java.util.UUID;
import org.eclipse.hawkbit.api.HostnameResolver;
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import org.eclipse.hawkbit.cache.DownloadArtifactCache;
import org.eclipse.hawkbit.cache.DownloadIdCache;
import org.eclipse.hawkbit.cache.DownloadType;
import org.eclipse.hawkbit.dmf.json.model.DmfArtifact;
import org.eclipse.hawkbit.dmf.json.model.DmfArtifactHash;
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.DmfTenantSecurityToken;
import org.eclipse.hawkbit.dmf.json.model.DmfTenantSecurityToken.FileResource;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -188,54 +186,53 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
return Optional.empty();
}
private static DmfArtifact convertDbArtifact(final AbstractDbArtifact dbArtifact) {
private static DmfArtifact convertDbArtifact(final Artifact dbArtifact) {
final DmfArtifact artifact = new DmfArtifact();
artifact.setSize(dbArtifact.getSize());
final DbArtifactHash dbArtifactHash = dbArtifact.getHashes();
artifact.setHashes(new DmfArtifactHash(dbArtifactHash.getSha1(), dbArtifactHash.getMd5()));
artifact.setHashes(new DmfArtifactHash(dbArtifact.getSha1Hash(), dbArtifact.getMd5Hash()));
return artifact;
}
private Message handleAuthenticationMessage(final Message message) {
final DmfDownloadResponse authentificationResponse = new DmfDownloadResponse();
final DmfDownloadResponse authenticationResponse = new DmfDownloadResponse();
final DmfTenantSecurityToken secruityToken = convertMessage(message, DmfTenantSecurityToken.class);
final FileResource fileResource = secruityToken.getFileResource();
try {
SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken));
final String sha1Hash = findArtifactByFileResource(fileResource).map(Artifact::getSha1Hash)
final Artifact artifact = findArtifactByFileResource(fileResource)
.orElseThrow(EntityNotFoundException::new);
checkIfArtifactIsAssignedToTarget(secruityToken, sha1Hash);
checkIfArtifactIsAssignedToTarget(secruityToken, artifact.getSha1Hash());
final DmfArtifact artifact = convertDbArtifact(artifactManagement.loadArtifactBinary(sha1Hash)
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, sha1Hash)));
final DmfArtifact dmfArtifact = convertDbArtifact(artifact);
authentificationResponse.setArtifact(artifact);
authenticationResponse.setArtifact(dmfArtifact);
final String downloadId = UUID.randomUUID().toString();
// SHA1 key is set, download by SHA1
final DownloadArtifactCache downloadCache = new DownloadArtifactCache(DownloadType.BY_SHA1, sha1Hash);
final DownloadArtifactCache downloadCache = new DownloadArtifactCache(DownloadType.BY_SHA1,
artifact.getSha1Hash());
cache.put(downloadId, downloadCache);
authentificationResponse.setDownloadUrl(UriComponentsBuilder
authenticationResponse.setDownloadUrl(UriComponentsBuilder
.fromUri(hostnameResolver.resolveHostname().toURI()).path("/api/v1/downloadserver/downloadId/")
.path(tenantAware.getCurrentTenant()).path("/").path(downloadId).build().toUriString());
authentificationResponse.setResponseCode(HttpStatus.OK.value());
authenticationResponse.setResponseCode(HttpStatus.OK.value());
} catch (final BadCredentialsException | AuthenticationServiceException | CredentialsExpiredException e) {
LOG.error("Login failed", e);
authentificationResponse.setResponseCode(HttpStatus.FORBIDDEN.value());
authentificationResponse.setMessage("Login failed");
authenticationResponse.setResponseCode(HttpStatus.FORBIDDEN.value());
authenticationResponse.setMessage("Login failed");
} catch (final URISyntaxException e) {
LOG.error("URI build exception", e);
authentificationResponse.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
authentificationResponse.setMessage("Building download URI failed");
authenticationResponse.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
authenticationResponse.setMessage("Building download URI failed");
} catch (final EntityNotFoundException e) {
final String errorMessage = "Artifact for resource " + fileResource + "not found ";
LOG.warn(errorMessage, e);
authentificationResponse.setResponseCode(HttpStatus.NOT_FOUND.value());
authentificationResponse.setMessage(errorMessage);
authenticationResponse.setResponseCode(HttpStatus.NOT_FOUND.value());
authenticationResponse.setMessage(errorMessage);
}
return getMessageConverter().toMessage(authentificationResponse, message.getMessageProperties());
return getMessageConverter().toMessage(authenticationResponse, message.getMessageProperties());
}
}

View File

@@ -17,6 +17,7 @@ import org.eclipse.hawkbit.cache.DownloadIdCache;
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
@@ -305,31 +306,14 @@ public class AmqpConfiguration {
tenantAware, ddiSecruityProperties, systemSecurityContext);
}
/**
* Create the dispatcher bean.
*
* @param rabbitTemplate
* the rabbitTemplate
* @param amqpSenderService
* to send AMQP message
* @param artifactUrlHandler
* for generating download URLs
* @param systemSecurityContext
* for execution with system permissions
* @param systemManagement
* the systemManagement
* @param targetManagement
* to access target information
* @return the bean
*/
@Bean
@ConditionalOnMissingBean(AmqpMessageDispatcherService.class)
public AmqpMessageDispatcherService amqpMessageDispatcherService(final RabbitTemplate rabbitTemplate,
AmqpMessageDispatcherService amqpMessageDispatcherService(final RabbitTemplate rabbitTemplate,
final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
final TargetManagement targetManagement) {
final TargetManagement targetManagement, final DistributionSetManagement distributionSetManagement) {
return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler,
systemSecurityContext, systemManagement, targetManagement, serviceMatcher);
systemSecurityContext, systemManagement, targetManagement, serviceMatcher, distributionSetManagement);
}
private static Map<String, Object> getTTLMaxArgsAuthenticationQueue() {

View File

@@ -12,7 +12,6 @@ import java.util.List;
import javax.annotation.PostConstruct;
import org.eclipse.hawkbit.dmf.json.model.DmfTenantSecurityToken;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
@@ -23,6 +22,7 @@ import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedGatewaySecurityTokenFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter;
import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken;
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
import org.eclipse.hawkbit.security.PreAuthentificationFilter;
import org.eclipse.hawkbit.security.SystemSecurityContext;

View File

@@ -26,6 +26,7 @@ import org.eclipse.hawkbit.dmf.json.model.DmfArtifact;
import org.eclipse.hawkbit.dmf.json.model.DmfArtifactHash;
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadAndUpdateRequest;
import org.eclipse.hawkbit.dmf.json.model.DmfSoftwareModule;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
@@ -64,6 +65,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
private final SystemManagement systemManagement;
private final TargetManagement targetManagement;
private final ServiceMatcher serviceMatcher;
private final DistributionSetManagement distributionSetManagement;
/**
* Constructor.
@@ -83,11 +85,14 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
* @param serviceMatcher
* to check in cluster case if the message is from the same
* cluster node
* @param distributionSetManagement
* to retrieve modules
*/
public AmqpMessageDispatcherService(final RabbitTemplate rabbitTemplate,
protected AmqpMessageDispatcherService(final RabbitTemplate rabbitTemplate,
final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
final TargetManagement targetManagement, final ServiceMatcher serviceMatcher) {
final TargetManagement targetManagement, final ServiceMatcher serviceMatcher,
final DistributionSetManagement distributionSetManagement) {
super(rabbitTemplate);
this.artifactUrlHandler = artifactUrlHandler;
this.amqpSenderService = amqpSenderService;
@@ -95,6 +100,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
this.systemManagement = systemManagement;
this.targetManagement = targetManagement;
this.serviceMatcher = serviceMatcher;
this.distributionSetManagement = distributionSetManagement;
}
/**
@@ -105,21 +111,54 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
* the object to be send.
*/
@EventListener(classes = TargetAssignDistributionSetEvent.class)
public void targetAssignDistributionSet(final TargetAssignDistributionSetEvent assignedEvent) {
protected void targetAssignDistributionSet(final TargetAssignDistributionSetEvent assignedEvent) {
if (isNotFromSelf(assignedEvent)) {
return;
}
LOG.debug("targetAssignDistributionSet retrieved for controller {}. I will forward it to DMF broker.",
assignedEvent.getControllerId());
LOG.debug("targetAssignDistributionSet retrieved. I will forward it to DMF broker.");
targetManagement.getByControllerID(assignedEvent.getControllerId())
.ifPresent(target -> sendUpdateMessageToTarget(assignedEvent.getTenant(), target,
assignedEvent.getActionId(), assignedEvent.getModules()));
distributionSetManagement.get(assignedEvent.getDistributionSetId()).ifPresent(set ->
targetManagement.getByControllerID(assignedEvent.getActions().keySet())
.forEach(target -> sendUpdateMessageToTarget(assignedEvent.getTenant(), target,
assignedEvent.getActions().get(target.getControllerId()), set.getModules())));
}
void sendUpdateMessageToTarget(final String tenant, final Target target, final Long actionId,
/**
* Method to send a message to a RabbitMQ Exchange after the assignment of
* the Distribution set to a Target has been canceled.
*
* @param cancelEvent
* the object to be send.
*/
@EventListener(classes = CancelTargetAssignmentEvent.class)
protected void targetCancelAssignmentToDistributionSet(final CancelTargetAssignmentEvent cancelEvent) {
if (isNotFromSelf(cancelEvent)) {
return;
}
sendCancelMessageToTarget(cancelEvent.getTenant(), cancelEvent.getEntity().getControllerId(),
cancelEvent.getActionId(), cancelEvent.getEntity().getAddress());
}
/**
* Method to send a message to a RabbitMQ Exchange after a Target was
* deleted.
*
* @param deleteEvent
* the TargetDeletedEvent which holds the necessary data for
* sending a target delete message.
*/
@EventListener(classes = TargetDeletedEvent.class)
protected void targetDelete(final TargetDeletedEvent deleteEvent) {
if (isNotFromSelf(deleteEvent)) {
return;
}
sendDeleteMessage(deleteEvent.getTenant(), deleteEvent.getControllerId(), deleteEvent.getTargetAddress());
}
protected void sendUpdateMessageToTarget(final String tenant, final Target target, final Long actionId,
final Collection<SoftwareModule> modules) {
final URI targetAdress = target.getAddress();
@@ -144,7 +183,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
amqpSenderService.sendMessage(message, targetAdress);
}
void sendPingReponseToDmfReceiver(final Message ping, final String tenant, final String virtualHost) {
protected void sendPingReponseToDmfReceiver(final Message ping, final String tenant, final String virtualHost) {
final Message message = MessageBuilder.withBody(String.valueOf(System.currentTimeMillis()).getBytes())
.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN)
.setCorrelationId(ping.getMessageProperties().getCorrelationId())
@@ -155,40 +194,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
IpUtil.createAmqpUri(virtualHost, ping.getMessageProperties().getReplyTo()));
}
/**
* Method to send a message to a RabbitMQ Exchange after the assignment of
* the Distribution set to a Target has been canceled.
*
* @param cancelEvent
* the object to be send.
*/
@EventListener(classes = CancelTargetAssignmentEvent.class)
public void targetCancelAssignmentToDistributionSet(final CancelTargetAssignmentEvent cancelEvent) {
if (isNotFromSelf(cancelEvent)) {
return;
}
sendCancelMessageToTarget(cancelEvent.getTenant(), cancelEvent.getEntity().getControllerId(),
cancelEvent.getActionId(), cancelEvent.getEntity().getAddress());
}
/**
* Method to send a message to a RabbitMQ Exchange after a Target was
* deleted.
*
* @param deleteEvent
* the TargetDeletedEvent which holds the necessary data for
* sending a target delete message.
*/
@EventListener(classes = TargetDeletedEvent.class)
public void targetDelete(final TargetDeletedEvent deleteEvent) {
if (isNotFromSelf(deleteEvent)) {
return;
}
sendDeleteMessage(deleteEvent.getTenant(), deleteEvent.getControllerId(), deleteEvent.getTargetAddress());
}
void sendDeleteMessage(final String tenant, final String controllerId, final String targetAddress) {
protected void sendDeleteMessage(final String tenant, final String controllerId, final String targetAddress) {
if (!hasValidAddress(targetAddress)) {
return;
@@ -206,7 +212,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
return serviceMatcher != null && !serviceMatcher.isFromSelf(event);
}
void sendCancelMessageToTarget(final String tenant, final String controllerId, final Long actionId,
protected void sendCancelMessageToTarget(final String tenant, final String controllerId, final Long actionId,
final URI address) {
if (!IpUtil.isAmqpUri(address)) {
return;

View File

@@ -28,8 +28,6 @@ import org.eclipse.hawkbit.cache.DownloadIdCache;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.DmfTenantSecurityToken;
import org.eclipse.hawkbit.dmf.json.model.DmfTenantSecurityToken.FileResource;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
@@ -42,7 +40,9 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.repository.model.TenantMetaData;
import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken;
import org.eclipse.hawkbit.security.DdiSecurityProperties.Authentication.Anonymous;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource;
import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp;
import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SystemSecurityContext;

View File

@@ -108,28 +108,14 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
when(systemManagement.getTenantMetadata()).thenReturn(tenantMetaData);
amqpMessageDispatcherService = new AmqpMessageDispatcherService(rabbitTemplate, senderService,
artifactUrlHandlerMock, systemSecurityContext, systemManagement, targetManagement, serviceMatcher);
artifactUrlHandlerMock, systemSecurityContext, systemManagement, targetManagement, serviceMatcher,
distributionSetManagement);
}
@Test
@Description("Verifies that download and install event with no software modul works")
public void testSendDownloadRequesWithEmptySoftwareModules() {
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
"DEFAULT", 1L, 1L, CONTROLLER_ID, serviceMatcher.getServiceId());
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
final Message sendMessage = getCaptureAdressEvent(targetAssignDistributionSetEvent);
final DmfDownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage, 1L);
assertThat(downloadAndUpdateRequest.getTargetSecurityToken()).isEqualTo(TEST_TOKEN);
assertTrue("No softwaremmodule should be contained in the request",
downloadAndUpdateRequest.getSoftwareModules().isEmpty());
}
private Message getCaptureAdressEvent(final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent) {
final Target target = targetManagement.getByControllerID(targetAssignDistributionSetEvent.getControllerId())
.get();
final Target target = targetManagement
.getByControllerID(targetAssignDistributionSetEvent.getActions().keySet().iterator().next()).get();
final Message sendMessage = createArgumentCapture(target.getAddress());
return sendMessage;
}

View File

@@ -28,8 +28,6 @@ import java.util.Optional;
import org.eclipse.hawkbit.api.HostnameResolver;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import org.eclipse.hawkbit.cache.DownloadIdCache;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
@@ -38,8 +36,6 @@ import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfActionUpdateStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfAttributeUpdate;
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.DmfTenantSecurityToken;
import org.eclipse.hawkbit.dmf.json.model.DmfTenantSecurityToken.FileResource;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
@@ -52,6 +48,8 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource;
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.junit.Before;
@@ -370,15 +368,12 @@ public class AmqpMessageHandlerServiceTest {
// mock
final Artifact localArtifactMock = mock(Artifact.class);
when(localArtifactMock.getSha1Hash()).thenReturn(SHA1);
when(localArtifactMock.getMd5Hash()).thenReturn("md5");
when(localArtifactMock.getSize()).thenReturn(1L);
final AbstractDbArtifact dbArtifactMock = mock(AbstractDbArtifact.class);
when(artifactManagementMock.findFirstBySHA1(SHA1)).thenReturn(Optional.of(localArtifactMock));
when(controllerManagementMock.hasTargetArtifactAssigned(securityToken.getControllerId(), SHA1))
.thenReturn(true);
when(artifactManagementMock.loadArtifactBinary(anyString())).thenReturn(Optional.of(dbArtifactMock));
when(dbArtifactMock.getArtifactId()).thenReturn("artifactId");
when(dbArtifactMock.getSize()).thenReturn(1L);
when(dbArtifactMock.getHashes()).thenReturn(new DbArtifactHash(SHA1, "md5"));
when(hostnameResolverMock.resolveHostname()).thenReturn(new URL("http://localhost"));
// test

View File

@@ -18,8 +18,6 @@ import org.eclipse.hawkbit.amqp.AmqpProperties;
import org.eclipse.hawkbit.amqp.DmfApiConfiguration;
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.DmfTenantSecurityToken;
import org.eclipse.hawkbit.dmf.json.model.DmfTenantSecurityToken.FileResource;
import org.eclipse.hawkbit.rabbitmq.test.AbstractAmqpIntegrationTest;
import org.eclipse.hawkbit.rabbitmq.test.AmqpTestConfiguration;
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
@@ -28,6 +26,8 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.Before;
import org.junit.Test;