Initial check in accordance with Parallel IP

This commit is contained in:
Kai Zimmermann
2016-01-21 13:18:55 +01:00
parent c5e4f1139f
commit 7497ab61ed
763 changed files with 114508 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
/**
* Copyright (c) 2015 Bosch Software Innovations 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.amqp;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* The spring AMQP configuration which is enabled by using the profile
* {@code amqp} to use a AMQP for communication with SP enabled devices.
*
*
*
*/
@EnableConfigurationProperties(AmqpProperties.class)
public class AmqpConfiguration {
@Autowired
protected AmqpProperties amqpProperties;
@Autowired
private ConnectionFactory connectionFactory;
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* Method to set the Jackson2JsonMessageConverter.
*
* @return the Jackson2JsonMessageConverter
*/
@Bean
public MessageConverter jsonMessageConverter() {
final Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);
return jackson2JsonMessageConverter;
}
/**
* Create the sp receiver queue.
*
* @return the receiver queue
*/
@Bean
public Queue receiverQueue() {
return new Queue(amqpProperties.getReceiverQueue(), true, false, false, getDeadLetterExchangeArgs());
}
/**
* Create the dead letter fanout exchange.
*
* @return the fanout exchange
*/
@Bean
public FanoutExchange senderExchange() {
return new FanoutExchange(AmqpSettings.DMF_EXCHANGE);
}
/**
* Create dead letter queue.
*
* @return the queue
*/
@Bean
public Queue deadLetterQueue() {
return new Queue(amqpProperties.getDeadLetterQueue());
}
/**
* Create the dead letter fanout exchange.
*
* @return the fanout exchange
*/
@Bean
public FanoutExchange exchangeDeadLetter() {
return new FanoutExchange(amqpProperties.getDeadLetterExchange());
}
/**
* Create the Binding deadLetterQueue to exchangeDeadLetter.
*
* @return the binding
*/
@Bean
public Binding bindDeadLetterQueueToLwm2mExchange() {
return BindingBuilder.bind(deadLetterQueue()).to(exchangeDeadLetter());
}
/**
* Create the Binding {@link AmqpConfiguration#receiverQueueFromSp()} to
* {@link AmqpConfiguration#senderConnectorToSpExchange()}.
*
* @return the binding and create the queue and exchange
*/
@Bean
public Binding bindSenderExchangeToSpQueue() {
return BindingBuilder.bind(receiverQueue()).to(senderExchange());
}
/**
* Create amqp handler service bean.
*
* @return
*/
@Bean
public AmqpMessageHandlerService amqpMessageHandlerService() {
return new AmqpMessageHandlerService();
}
/**
* Returns the Listener factory.
*
* @return the {@link SimpleMessageListenerContainer} that gets used receive
* AMQP messages
*/
@Bean(name = { "listenerContainerFactory" })
public SimpleRabbitListenerContainerFactory listenerContainerFactory() {
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
containerFactory.setDefaultRequeueRejected(false);
containerFactory.setConnectionFactory(connectionFactory);
return containerFactory;
}
private Map<String, Object> getDeadLetterExchangeArgs() {
final Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", amqpProperties.getDeadLetterExchange());
return args;
}
}

View File

@@ -0,0 +1,152 @@
/**
* Copyright (c) 2015 Bosch Software Innovations 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.amqp;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.CoapAnonymousPreAuthenticatedFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedGatewaySecurityTokenFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter;
import org.eclipse.hawkbit.security.PreAuthTokenSourceTrustAuthenticationProvider;
import org.eclipse.hawkbit.security.PreAuthenficationFilter;
import org.eclipse.hawkbit.security.SecurityProperties;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.stereotype.Component;
/**
*
*
*/
@Component
public class AmqpControllerAuthentfication {
private static final Logger LOGGER = LoggerFactory.getLogger(AmqpControllerAuthentfication.class);
private final PreAuthTokenSourceTrustAuthenticationProvider preAuthenticatedAuthenticationProvider;
private final List<PreAuthenficationFilter> filterChain = new ArrayList<>();
@Autowired
private ControllerManagement controllerManagement;
@Autowired
private SystemManagement systemManagement;
@Autowired
private TenantAware tenantAware;
@Autowired
private SecurityProperties secruityProperties;
/**
* Constructor.
*/
public AmqpControllerAuthentfication() {
preAuthenticatedAuthenticationProvider = new PreAuthTokenSourceTrustAuthenticationProvider();
}
/**
* Called by spring when bean instantiated and autowired.
*/
@PostConstruct
public void postConstruct() {
addFilter();
}
private void addFilter() {
final ControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new ControllerPreAuthenticatedGatewaySecurityTokenFilter(
systemManagement, tenantAware);
filterChain.add(gatewaySecurityTokenFilter);
final ControllerPreAuthenticatedSecurityHeaderFilter securityHeaderFilter = new ControllerPreAuthenticatedSecurityHeaderFilter(
secruityProperties.getRpCnHeader(), secruityProperties.getRpSslIssuerHashHeader(), systemManagement,
tenantAware);
filterChain.add(securityHeaderFilter);
final ControllerPreAuthenticateSecurityTokenFilter securityTokenFilter = new ControllerPreAuthenticateSecurityTokenFilter(
systemManagement, controllerManagement, tenantAware);
filterChain.add(securityTokenFilter);
filterChain.add(new CoapAnonymousPreAuthenticatedFilter());
}
/**
* Performs authentication with the secruity token.
*
* @param secruityToken
* the authentication request object
* @return the authentfication object
*/
public Authentication doAuthenticate(final TenantSecruityToken secruityToken) {
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(null, null);
for (final PreAuthenficationFilter filter : filterChain) {
final PreAuthenticatedAuthenticationToken authenticationRest = createAuthentication(filter, secruityToken);
if (authenticationRest != null) {
authentication = authenticationRest;
authentication.setDetails(new TenantAwareAuthenticationDetails(secruityToken.getTenant(), true));
break;
}
}
return preAuthenticatedAuthenticationProvider.authenticate(authentication);
}
private PreAuthenticatedAuthenticationToken createAuthentication(final PreAuthenficationFilter filter,
final TenantSecruityToken secruityToken) {
if (!filter.isEnable(secruityToken)) {
return null;
}
final Object principal = filter.getPreAuthenticatedPrincipal(secruityToken);
final Object credentials = filter.getPreAuthenticatedCredentials(secruityToken);
if (principal == null) {
LOGGER.debug("No pre-authenticated principal found in message");
return null;
}
LOGGER.debug("preAuthenticatedPrincipal = {} trying to authenticate", principal);
final PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(principal,
credentials);
return authRequest;
}
public void setControllerManagement(final ControllerManagement controllerManagement) {
this.controllerManagement = controllerManagement;
}
public void setSecruityProperties(final SecurityProperties secruityProperties) {
this.secruityProperties = secruityProperties;
}
public void setSystemManagement(final SystemManagement systemManagement) {
this.systemManagement = systemManagement;
}
public void setTenantAware(final TenantAware tenantAware) {
this.tenantAware = tenantAware;
}
}

View File

@@ -0,0 +1,182 @@
/**
* Copyright (c) 2015 Bosch Software Innovations 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.amqp;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.Artifact;
import org.eclipse.hawkbit.dmf.json.model.ArtifactHash;
import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest;
import org.eclipse.hawkbit.dmf.json.model.SoftwareModule;
import org.eclipse.hawkbit.eventbus.EventSubscriber;
import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.util.ArtifactUrlHandler;
import org.eclipse.hawkbit.util.IpUtil;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import com.google.common.eventbus.Subscribe;
/**
* {@link AmqpMessageDispatcherService} handles all outgoing AMQP messages.
*
*
*
*/
@EventSubscriber
public class AmqpMessageDispatcherService {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private TenantAware tenantAware;
@Autowired
private ArtifactUrlHandler artifactUrlHandler;
/**
* Method to send a message to a RabbitMQ Exchange after the Distribution
* set has been assign to a Target.
*
* @param targetAssignDistributionSetEvent
* the object to be send.
*/
@Subscribe
public void targetAssignDistributionSet(final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent) {
final URI targetAdress = targetAssignDistributionSetEvent.getTargetAdress();
if (!IpUtil.isAmqpUri(targetAdress)) {
return;
}
final String controllerId = targetAssignDistributionSetEvent.getControllerId();
final Collection<org.eclipse.hawkbit.repository.model.SoftwareModule> modules = targetAssignDistributionSetEvent
.getSoftwareModules();
final DownloadAndUpdateRequest downloadAndUpdateRequest = new DownloadAndUpdateRequest();
downloadAndUpdateRequest.setActionId(targetAssignDistributionSetEvent.getActionId());
for (final org.eclipse.hawkbit.repository.model.SoftwareModule softwareModule : modules) {
final SoftwareModule amqpSoftwareModule = convertToAmqpSoftwareModule(controllerId, softwareModule);
downloadAndUpdateRequest.addSoftwareModule(amqpSoftwareModule);
}
final Message message = rabbitTemplate.getMessageConverter().toMessage(downloadAndUpdateRequest,
createConnectorMessageProperties(controllerId, EventTopic.DOWNLOAD_AND_INSTALL));
sendMessage(targetAdress.getHost(), message);
}
/**
* Method to send a message to a RabbitMQ Exchange after the assignment of
* the Distribution set to a Target has been canceled.
*
* @param cancelTargetAssignmentDistributionSetEvent
* the object to be send.
*/
@Subscribe
public void targetCancelAssignmentToDistributionSet(
final CancelTargetAssignmentEvent cancelTargetAssignmentDistributionSetEvent) {
final String controllerId = cancelTargetAssignmentDistributionSetEvent.getControllerId();
final Long actionId = cancelTargetAssignmentDistributionSetEvent.getActionId();
final Message message = rabbitTemplate.getMessageConverter().toMessage(actionId,
createConnectorMessageProperties(controllerId, EventTopic.CANCEL_DOWNLOAD));
sendMessage(cancelTargetAssignmentDistributionSetEvent.getTargetAdress().getHost(), message);
}
/**
* Send message to exchange.
*
* @param exchange
* the exchange
* @param message
* the message
*/
public void sendMessage(final String exchange, final Message message) {
rabbitTemplate.setExchange(exchange);
rabbitTemplate.send(message);
}
private MessageProperties createConnectorMessageProperties(final String controllerId, final EventTopic topic) {
final MessageProperties messageProperties = createMessageProperties();
messageProperties.setHeader(MessageHeaderKey.TOPIC, topic);
messageProperties.setHeader(MessageHeaderKey.THING_ID, controllerId);
messageProperties.setHeader(MessageHeaderKey.TENANT, tenantAware.getCurrentTenant());
messageProperties.setHeader(MessageHeaderKey.TYPE, MessageType.EVENT);
return messageProperties;
}
private MessageProperties createMessageProperties() {
final MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
messageProperties.setHeader(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
return messageProperties;
}
private SoftwareModule convertToAmqpSoftwareModule(final String targetId,
final org.eclipse.hawkbit.repository.model.SoftwareModule softwareModule) {
final SoftwareModule amqpSoftwareModule = new SoftwareModule();
amqpSoftwareModule.setModuleId(softwareModule.getId());
amqpSoftwareModule.setModuleType(softwareModule.getType().getKey());
amqpSoftwareModule.setModuleVersion(softwareModule.getVersion());
final List<Artifact> artifacts = convertArtifacts(targetId, softwareModule.getLocalArtifacts());
amqpSoftwareModule.setArtifacts(artifacts);
return amqpSoftwareModule;
}
private List<Artifact> convertArtifacts(final String targetId, final List<LocalArtifact> localArtifacts) {
if (localArtifacts.isEmpty()) {
return Collections.emptyList();
}
final List<Artifact> convertedArtifacts = localArtifacts.stream()
.map(localArtifact -> convertArtifact(targetId, localArtifact)).collect(Collectors.toList());
return convertedArtifacts;
}
private Artifact convertArtifact(final String targetId, final LocalArtifact localArtifact) {
final Artifact artifact = new Artifact();
artifact.getUrls().put(Artifact.UrlProtocol.COAP,
artifactUrlHandler.getUrl(targetId, localArtifact, Artifact.UrlProtocol.COAP));
artifact.getUrls().put(Artifact.UrlProtocol.HTTP,
artifactUrlHandler.getUrl(targetId, localArtifact, Artifact.UrlProtocol.HTTP));
artifact.getUrls().put(Artifact.UrlProtocol.HTTPS,
artifactUrlHandler.getUrl(targetId, localArtifact, Artifact.UrlProtocol.HTTPS));
artifact.setFilename(localArtifact.getFilename());
artifact.setHashes(new ArtifactHash(localArtifact.getSha1Hash(), null));
artifact.setSize(localArtifact.getSize());
return artifact;
}
public void setTenantAware(final TenantAware tenantAware) {
this.tenantAware = tenantAware;
}
public void setRabbitTemplate(final RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
public void setArtifactUrlHandler(final ArtifactUrlHandler artifactUrlHandler) {
this.artifactUrlHandler = artifactUrlHandler;
}
}

View File

@@ -0,0 +1,417 @@
/**
* Copyright (c) 2015 Bosch Software Innovations 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.amqp;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.api.HostnameResolver;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import org.eclipse.hawkbit.cache.CacheConstants;
import org.eclipse.hawkbit.cache.DownloadArtifactCache;
import org.eclipse.hawkbit.cache.DownloadType;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus;
import org.eclipse.hawkbit.dmf.json.model.Artifact;
import org.eclipse.hawkbit.dmf.json.model.ArtifactHash;
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
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.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.util.IpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.Cache;
import org.springframework.http.HttpStatus;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.web.util.UriComponentsBuilder;
import com.google.common.eventbus.EventBus;
/**
*
* {@link AmqpMessageHandlerService} handles all incoming AMQP messages.
*
*
*
*
*/
public class AmqpMessageHandlerService {
private static final Logger LOG = LoggerFactory.getLogger(AmqpMessageHandlerService.class);
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private ControllerManagement controllerManagement;
@Autowired
private AmqpControllerAuthentfication authenticationManager;
@Autowired
private ArtifactManagement artifactManagement;
@Autowired
private EventBus eventBus;
@Autowired
@Qualifier(CacheConstants.DOWNLOAD_ID_CACHE)
private Cache cache;
@Autowired
private HostnameResolver hostnameResolver;
/**
* /** Method to handle all incoming amqp messages.
*
* @param message
* incoming message
* @param type
* the message type
* @param contentType
* the contentType of the message
* @param tenant
* the contentType of the message
* @return a message if <null> no message is send back to sender
*/
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.receiverQueue}", containerFactory = "listenerContainerFactory")
public Message onMessage(final Message message, @Header(MessageHeaderKey.TYPE) final String type,
@Header(MessageHeaderKey.TENANT) final String tenant) {
checkContentTypeJson(message);
final SecurityContext oldContext = SecurityContextHolder.getContext();
try {
final MessageType messageType = MessageType.valueOf(type);
switch (messageType) {
case THING_CREATED:
setTenantSecurityContext(tenant);
registerTarget(message);
break;
case EVENT:
setTenantSecurityContext(tenant);
final String topicValue = getStringHeaderKey(message, MessageHeaderKey.TOPIC, "EventTopic is null");
final EventTopic eventTopic = EventTopic.valueOf(topicValue);
handleIncomingEvent(message, eventTopic);
break;
case AUTHENTIFICATION:
return handleAuthentifiactionMessage(message);
default:
logAndThrowMessageError(message, "No handle method was found for the given message type.");
}
} finally {
SecurityContextHolder.setContext(oldContext);
}
return null;
}
private Message handleAuthentifiactionMessage(final Message message) {
final DownloadResponse authentificationResponse = new DownloadResponse();
final MessageProperties messageProperties = message.getMessageProperties();
final TenantSecruityToken secruityToken = convertMessage(message, TenantSecruityToken.class);
final String sha1 = secruityToken.getSha1();
try {
SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken));
final LocalArtifact localArtifact = artifactManagement
.findFirstLocalArtifactsBySHA1(secruityToken.getSha1());
if (localArtifact == null) {
throw new EntityNotFoundException();
}
// check action for this download purposes, the method will throw an
// EntityNotFoundException in case the controller is not allowed to
// download this file
// because it's not assigned to an action and not assigned to this
// controller.
final Action action = controllerManagement.getActionForDownloadByTargetAndSoftwareModule(
secruityToken.getControllerId(), localArtifact.getSoftwareModule());
LOG.info("Found action for download authentication request action: {}, sha1: {}", action,
secruityToken.getSha1());
final Artifact artifact = convertDbArtifact(artifactManagement.loadLocalArtifactBinary(localArtifact));
if (artifact == null) {
throw new EntityNotFoundException();
}
authentificationResponse.setArtifact(artifact);
final String downloadId = UUID.randomUUID().toString();
final DownloadArtifactCache downloadCache = new DownloadArtifactCache(DownloadType.BY_SHA1, sha1);
cache.put(downloadId, downloadCache);
authentificationResponse
.setDownloadUrl(UriComponentsBuilder.fromUri(hostnameResolver.resolveHostname().toURI())
.path("/api/v1/downloadserver/downloadId/").path(downloadId).build().toUriString());
authentificationResponse.setResponseCode(HttpStatus.OK.value());
} catch (final BadCredentialsException | AuthenticationServiceException | CredentialsExpiredException e) {
LOG.error("Login failed", e);
authentificationResponse.setResponseCode(HttpStatus.FORBIDDEN.value());
authentificationResponse.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");
} catch (final EntityNotFoundException e) {
final String errorMessage = "Artifact with sha1 " + sha1 + "not found ";
LOG.warn(errorMessage, e);
authentificationResponse.setResponseCode(HttpStatus.NOT_FOUND.value());
authentificationResponse.setMessage(errorMessage);
}
return rabbitTemplate.getMessageConverter().toMessage(authentificationResponse, messageProperties);
}
private Artifact convertDbArtifact(final DbArtifact dbArtifact) {
final Artifact artifact = new Artifact();
artifact.setSize(dbArtifact.getSize());
final DbArtifactHash dbArtifactHash = dbArtifact.getHashes();
artifact.setHashes(new ArtifactHash(dbArtifactHash.getSha1(), dbArtifactHash.getMd5()));
return artifact;
}
protected void logAndThrowMessageError(final Message message, final String error) {
LOG.error("Error \"{}\" reported by message {}", error, message.getMessageProperties().getMessageId());
throw new IllegalArgumentException(error);
}
private void setSecurityContext(final Authentication authentication) {
final SecurityContextImpl securityContextImpl = new SecurityContextImpl();
securityContextImpl.setAuthentication(authentication);
SecurityContextHolder.setContext(securityContextImpl);
}
private void setTenantSecurityContext(final String tenantId) {
final AnonymousAuthenticationToken authenticationToken = new AnonymousAuthenticationToken(
UUID.randomUUID().toString(), "AMQP-Controller",
Collections.singletonList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
authenticationToken.setDetails(new TenantAwareAuthenticationDetails(tenantId, true));
setSecurityContext(authenticationToken);
}
private String getStringHeaderKey(final Message message, final String key, final String errorMessageIfNull) {
final Map<String, Object> header = message.getMessageProperties().getHeaders();
final Object value = header.get(key);
if (value == null) {
logAndThrowMessageError(message, errorMessageIfNull);
}
return value.toString();
}
/**
* Method to create a new target or to find the target if it already exists.
*
* @param targetID
* the ID of the target/thing
* @param ip
* the ip of the target/thing
*/
private void registerTarget(final Message message) {
final String thingId = getStringHeaderKey(message, MessageHeaderKey.THING_ID, "ThingId is null");
final String replyTo = message.getMessageProperties().getReplyTo();
if (StringUtils.isEmpty(replyTo)) {
logAndThrowMessageError(message, "No ReplyTo was set for the createThing Event.");
}
final URI amqpUri = IpUtil.createAmqpUri(replyTo);
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(thingId, amqpUri);
LOG.debug("Target {} reported online state.", thingId);
lookIfUpdateAvailable(target);
}
private void lookIfUpdateAvailable(final Target target) {
final List<Action> actions = controllerManagement.findActionByTargetAndActive(target);
if (actions.isEmpty()) {
return;
}
// action are ordered by ASC
final Action action = actions.get(0);
final DistributionSet distributionSet = action.getDistributionSet();
final List<SoftwareModule> softwareModuleList = controllerManagement
.findSoftwareModulesByDistributionSet(distributionSet);
eventBus.post(new TargetAssignDistributionSetEvent(target.getControllerId(), action.getId(), softwareModuleList,
target.getTargetInfo().getAddress()));
}
/**
* Method to handle the different topics to an event.
*
* @param message
* the incoming event message.
* @param topic
* the topic of the event.
*/
private void handleIncomingEvent(final Message message, final EventTopic topic) {
switch (topic) {
case UPDATE_ACTION_STATUS:
updateActionStatus(message);
return;
default:
logAndThrowMessageError(message, "Got event without appropriate topic.");
}
}
/**
* Method to update the action status of an action through the event.
*
* @param actionUpdateStatus
* the object form the ampq message
*/
private void updateActionStatus(final Message message) {
final ActionUpdateStatus actionUpdateStatus = convertMessage(message, ActionUpdateStatus.class);
final Long actionId = actionUpdateStatus.getActionId();
LOG.debug("Target notifies intermediate about action {} with status {}.", actionId,
actionUpdateStatus.getActionStatus().name());
if (actionId == null) {
logAndThrowMessageError(message, "Invalid message no action id");
}
final Action action = controllerManagement.findActionWithDetails(actionId);
if (action == null) {
logAndThrowMessageError(message,
"Got intermediate notification about action " + actionId + " but action does not exist");
}
final ActionStatus actionStatus = new ActionStatus();
final List<String> messageText = actionUpdateStatus.getMessage();
final String messageString = String.join(", ", messageText);
actionStatus.addMessage(messageString);
actionStatus.setAction(action);
actionStatus.setOccurredAt(System.currentTimeMillis());
switch (actionUpdateStatus.getActionStatus()) {
case DOWNLOAD:
actionStatus.setStatus(Status.DOWNLOAD);
break;
case RETRIEVED:
actionStatus.setStatus(Status.RETRIEVED);
break;
case RUNNING:
actionStatus.setStatus(Status.RUNNING);
break;
case FINISHED:
actionStatus.setStatus(Status.FINISHED);
break;
case ERROR:
actionStatus.setStatus(Status.ERROR);
break;
case WARNING:
actionStatus.setStatus(Status.WARNING);
break;
default:
logAndThrowMessageError(message, "Status for action does not exisit.");
}
final Action savedAction = controllerManagement.addUpdateActionStatus(actionStatus, action);
if (Status.FINISHED == savedAction.getStatus()) {
lookIfUpdateAvailable(action.getTarget());
}
}
/**
* Is needed to convert a incoming message to is originally object type.
*
* @param message
* the message to convert.
* @param clazz
* the class of the originally object.
* @return
*/
@SuppressWarnings("unchecked")
private <T> T convertMessage(final Message message, final Class<T> clazz) {
message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME,
clazz.getTypeName());
return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
}
/**
* Is needed to verify if an incoming message has the content type json.
*
* @param message
* the to verify
* @param contentType
* the content type
* @return true if the content type has json, false it not.
*/
private void checkContentTypeJson(final Message message) {
final MessageProperties messageProperties = message.getMessageProperties();
if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) {
return;
}
throw new IllegalArgumentException("Content-Type is not JSON compatible");
}
void setControllerManagement(final ControllerManagement controllerManagement) {
this.controllerManagement = controllerManagement;
}
void setHostnameResolver(final HostnameResolver hostnameResolver) {
this.hostnameResolver = hostnameResolver;
}
void setRabbitTemplate(final RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
MessageConverter getMessageConverter() {
return rabbitTemplate.getMessageConverter();
}
void setAuthenticationManager(final AmqpControllerAuthentfication authenticationManager) {
this.authenticationManager = authenticationManager;
}
void setArtifactManagement(final ArtifactManagement artifactManagement) {
this.artifactManagement = artifactManagement;
}
void setCache(final Cache cache) {
this.cache = cache;
}
void setEventBus(final EventBus eventBus) {
this.eventBus = eventBus;
}
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2015 Bosch Software Innovations 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.amqp;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Bean which holds the necessary properties for configuring the AMQP
* connection.
*
*
*
*
*/
@ConfigurationProperties("hawkbit.dmf.rabbitmq")
public class AmqpProperties {
private String deadLetterQueue = "dmf_connector_deadletter";
private String deadLetterExchange = "dmf.connector.deadletter";
private String receiverQueue = "dmf_receiver";
/**
* Returns the dead letter exchange.
*
* @return dead letter exchange
*/
public String getDeadLetterExchange() {
return deadLetterExchange;
}
/**
* Sets the dead letter exchange.
*
* @param deadLetterExchange
* the deadLetterExchange to be set
*/
public void setDeadLetterExchange(final String deadLetterExchange) {
this.deadLetterExchange = deadLetterExchange;
}
/**
* Returns the dead letter queue.
*
* @return the dead letter queue
*/
public String getDeadLetterQueue() {
return deadLetterQueue;
}
/**
* Sets the dead letter queue.
*
* @param deadLetterQueue
* the deadLetterQueue ro be set
*/
public void setDeadLetterQueue(final String deadLetterQueue) {
this.deadLetterQueue = deadLetterQueue;
}
public String getReceiverQueue() {
return receiverQueue;
}
public void setReceiverQueue(final String receiverQueue) {
this.receiverQueue = receiverQueue;
}
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2015 Bosch Software Innovations 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.amqp.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.eclipse.hawkbit.amqp.AmqpConfiguration;
import org.springframework.context.annotation.Import;
/**
* Annotation to enable amqp.
*
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(AmqpConfiguration.class)
public @interface EnableAmqp {
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2015 Bosch Software Innovations 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.util;
import org.eclipse.hawkbit.dmf.json.model.Artifact;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
/**
* Interface declaration of the {@link ArtifactUrlHandler} which generates the
* URLs to specific artifacts.
*
*
*
*/
public interface ArtifactUrlHandler {
/**
* Returns a generated URL for a given artifact for a specific protocol.
*
* @param controllerId
* the authentifacted controller id
* @param localArtifact
* the artifact to retrieve a URL to
* @param protocol
* the protocol the URL should be generated
* @return an URL for the given artifact in a given protocol
*/
String getUrl(String controllerId, LocalArtifact localArtifact, final Artifact.UrlProtocol protocol);
}

View File

@@ -0,0 +1,337 @@
/**
* Copyright (c) 2015 Bosch Software Innovations 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.util;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
*
*
*/
@ConfigurationProperties("hawkbit.artifact.url")
public class ArtifactUrlHandlerProperties {
private static final String DEFAULT_IP_LOCALHOST = "127.0.0.1";
private static final String LOCALHOST = "localhost";
private final Http http = new Http();
private final Https https = new Https();
private final Coap coap = new Coap();
/**
* @return the http
*/
public Http getHttp() {
return http;
}
/**
* @return the https
*/
public Https getHttps() {
return https;
}
/**
* @return the coap
*/
public Coap getCoap() {
return coap;
}
/**
* @param protocol
* the protocol schema to retrieve the properties.
* @return the properties to a protocol or {@code null} if protocol does not
* have properties or protocol not supported
*/
public ProtocolProperties getProperties(final String protocol) {
switch (protocol) {
case "http":
return getHttp();
case "https":
return getHttps();
case "coap":
return getCoap();
default:
return null;
}
}
/**
* Interface for declaring common properties through all supported protocols
* pattern.
*
*
*
*/
public interface ProtocolProperties {
/**
* @return the hostname value to resolve in the pattern.
*/
String getHostname();
/**
* @return the IP address value to resolve in the pattern.
*/
String getIp();
/**
* @return the port value to resolve in the pattern.
*/
String getPort();
/**
* @return the pattern to build the URL.
*/
String getPattern();
}
/**
* Object to hold the properties for the HTTP protocol.
*
*
*
*/
public static class Http implements ProtocolProperties {
private String hostname = LOCALHOST;
private String ip = DEFAULT_IP_LOCALHOST;
private String port = "";
/**
* An ant-URL pattern with placeholder to build the URL on. The URL can
* have specific artifact placeholder.
*/
private String pattern = "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}";
/**
* @return the hostname
*/
@Override
public String getHostname() {
return hostname;
}
/**
* @param hostname
* the hostname to set
*/
public void setHostname(final String hostname) {
this.hostname = hostname;
}
/**
* @return the ip
*/
@Override
public String getIp() {
return ip;
}
/**
* @param ip
* the ip to set
*/
public void setIp(final String ip) {
this.ip = ip;
}
/**
* @return the urlPattern
*/
@Override
public String getPattern() {
return pattern;
}
/**
* @param urlPattern
* the urlPattern to set
*/
public void setPattern(final String urlPattern) {
this.pattern = urlPattern;
}
/**
* @return the port
*/
@Override
public String getPort() {
return port;
}
/**
* @param port
* the port to set
*/
public void setPort(final String port) {
this.port = port;
}
}
/**
* Object to hold the properties for the HTTP protocol.
*
*
*
*/
public static class Https implements ProtocolProperties {
private String hostname = LOCALHOST;
private String ip = DEFAULT_IP_LOCALHOST;
private String port = "";
/**
* An ant-URL pattern with placeholder to build the URL on. The URL can
* have specific artifact placeholder.
*/
private String pattern = "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}";
/**
* @return the hostname
*/
@Override
public String getHostname() {
return hostname;
}
/**
* @param hostname
* the hostname to set
*/
public void setHostname(final String hostname) {
this.hostname = hostname;
}
/**
* @return the ip
*/
@Override
public String getIp() {
return ip;
}
/**
* @param ip
* the ip to set
*/
public void setIp(final String ip) {
this.ip = ip;
}
/**
* @return the urlPattern
*/
@Override
public String getPattern() {
return pattern;
}
/**
* @param urlPattern
* the urlPattern to set
*/
public void setPattern(final String urlPattern) {
this.pattern = urlPattern;
}
/**
* @return the port
*/
@Override
public String getPort() {
return port;
}
/**
* @param port
* the port to set
*/
public void setPort(final String port) {
this.port = port;
}
}
/**
* Object to hold the properties for the HTTP protocol.
*
*
*
*/
public static class Coap implements ProtocolProperties {
private String hostname = LOCALHOST;
private String ip = DEFAULT_IP_LOCALHOST;
private String port = "5683";
/**
* An ant-URL pattern with placeholder to build the URL on. The URL can
* have specific artifact placeholder.
*/
private String pattern = "{protocol}://{ip}:{port}/fw/{tenant}/{targetId}/sha1/{artifactSHA1}";
/**
* @return the hostname
*/
@Override
public String getHostname() {
return hostname;
}
/**
* @param hostname
* the hostname to set
*/
public void setHostname(final String hostname) {
this.hostname = hostname;
}
/**
* @return the ip
*/
@Override
public String getIp() {
return ip;
}
/**
* @param ip
* the ip to set
*/
public void setIp(final String ip) {
this.ip = ip;
}
/**
* @return the urlPattern
*/
@Override
public String getPattern() {
return pattern;
}
/**
* @param urlPattern
* the urlPattern to set
*/
public void setPattern(final String urlPattern) {
this.pattern = urlPattern;
}
/**
* @return the port
*/
@Override
public String getPort() {
return port;
}
/**
* @param port
* the port to set
*/
public void setPort(final String port) {
this.port = port;
}
}
}

View File

@@ -0,0 +1,87 @@
/**
* Copyright (c) 2015 Bosch Software Innovations 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.util;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.hawkbit.dmf.json.model.Artifact;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.util.ArtifactUrlHandlerProperties.ProtocolProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import com.google.common.base.Strings;
/**
*
*
*/
@Component
@EnableConfigurationProperties(ArtifactUrlHandlerProperties.class)
public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler {
private static final String PROTOCOL_PLACEHOLDER = "protocol";
private static final String TARGET_ID_PLACEHOLDER = "targetId";
private static final String IP_PLACEHOLDER = "ip";
private static final String PORT_PLACEHOLDER = "port";
private static final String HOSTNAME_PLACEHOLDER = "hostname";
private static final String ARTIFACT_FILENAME_PLACEHOLDER = "artifactFileName";
private static final String ARTIFACT_SHA1_PLACEHOLDER = "artifactSHA1";
private static final String TENANT_PLACEHOLDER = "tenant";
private static final String SOFTWARE_MODULE_ID_PLACDEHOLDER = "softwareModuleId";
@Autowired
private ArtifactUrlHandlerProperties urlHandlerProperties;
@Autowired
private TenantAware tenantAware;
@Override
public String getUrl(final String targetId, final LocalArtifact artifact, final Artifact.UrlProtocol protocol) {
final String protocolString = protocol.name().toLowerCase();
final ProtocolProperties properties = urlHandlerProperties.getProperties(protocolString);
if (properties == null || properties.getPattern() == null) {
return null;
}
String urlPattern = properties.getPattern();
final Set<Entry<String, String>> entrySet = getReplaceMap(targetId, artifact, protocolString, properties)
.entrySet();
for (final Entry<String, String> entry : entrySet) {
if (entry.getKey().equals(PORT_PLACEHOLDER)) {
urlPattern = urlPattern.replace(":{" + entry.getKey() + "}",
Strings.isNullOrEmpty(entry.getValue()) ? "" : ":" + entry.getValue());
} else {
urlPattern = urlPattern.replace("{" + entry.getKey() + "}", entry.getValue());
}
}
return urlPattern;
}
private Map<String, String> getReplaceMap(final String targetId, final LocalArtifact artifact,
final String protocol, final ProtocolProperties properties) {
final Map<String, String> replaceMap = new HashMap<>();
replaceMap.put(IP_PLACEHOLDER, properties.getIp());
replaceMap.put(HOSTNAME_PLACEHOLDER, properties.getHostname());
replaceMap.put(ARTIFACT_FILENAME_PLACEHOLDER, artifact.getFilename());
replaceMap.put(ARTIFACT_SHA1_PLACEHOLDER, artifact.getSha1Hash());
replaceMap.put(PROTOCOL_PLACEHOLDER, protocol);
replaceMap.put(PORT_PLACEHOLDER, properties.getPort());
replaceMap.put(TENANT_PLACEHOLDER, tenantAware.getCurrentTenant());
replaceMap.put(TARGET_ID_PLACEHOLDER, targetId);
replaceMap.put(SOFTWARE_MODULE_ID_PLACDEHOLDER, String.valueOf(artifact.getSoftwareModule().getId()));
return replaceMap;
}
}