package alien4cloud.rest.deployment;
import java.security.Principal;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.common.collect.Maps;
import org.elasticsearch.mapping.MappingBuilder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import alien4cloud.dao.IGenericSearchDAO;
import alien4cloud.exception.NotFoundException;
import alien4cloud.model.application.ApplicationEnvironment;
import alien4cloud.model.deployment.Deployment;
import alien4cloud.paas.IPaasEventListener;
import alien4cloud.paas.IPaasEventService;
import alien4cloud.paas.model.AbstractMonitorEvent;
import alien4cloud.paas.model.AbstractPaaSWorkflowMonitorEvent;
import alien4cloud.paas.model.PaaSWorkflowMonitorEvent;
import alien4cloud.rest.websocket.ISecuredHandler;
import alien4cloud.security.AuthorizationUtil;
import alien4cloud.security.model.ApplicationEnvironmentRole;
import alien4cloud.security.model.ApplicationRole;
import alien4cloud.security.model.Role;
import alien4cloud.security.model.User;
@Slf4j
@Component
public class WorkflowEventHandler implements IPaasEventListener<AbstractMonitorEvent>, ISecuredHandler, InitializingBean {
private static final String TOPIC_PREFIX = "/topic/workflow-events";
private static final Pattern DESTINATION_PATTERN = Pattern.compile(TOPIC_PREFIX + "/(.*?)(:?/.*)?");
@Resource
private IPaasEventService paasEventService;
@Resource(name = "alien-es-dao")
private IGenericSearchDAO alienDAO;
@Resource
private SimpMessagingTemplate template;
protected void send(AbstractMonitorEvent event) {
String eventType = MappingBuilder.indexTypeFromClass(event.getClass());
String topicName = TOPIC_PREFIX + '/' + event.getDeploymentId() + '/' + eventType;
if (log.isDebugEnabled()) {
log.debug("Send [" + event.getClass().getSimpleName() + "] to [" + topicName + "]: " + event);
}
template.convertAndSend(topicName, event);
if (event instanceof PaaSWorkflowMonitorEvent) {
Deployment deployment = alienDAO.findById(Deployment.class, event.getDeploymentId());
if (deployment != null) {
PaaSWorkflowMonitorEvent pwme = (PaaSWorkflowMonitorEvent)event;
if (log.isDebugEnabled()) {
log.debug("Workflow {} started with executionId {} (subkworkflow: {})", pwme.getWorkflowId(), pwme.getExecutionId(), pwme.getSubworkflow());
}
String workflowId = pwme.getWorkflowId();
if (pwme.getSubworkflow() != null) {
workflowId = pwme.getSubworkflow();
}
updateDeploymentExecutionId(deployment, workflowId, pwme.getExecutionId());
}
}
}
private void updateDeploymentExecutionId(Deployment deployment, String workflowId, String executionId) {
if (deployment.getWorkflowExecutions() == null) {
Map<String, String> workflowExecutions = Maps.newHashMap();
deployment.setWorkflowExecutions(workflowExecutions);
}
String knownExecutionId = deployment.getWorkflowExecutions().get(workflowId);
if (knownExecutionId == null || !executionId.equals(knownExecutionId)) {
deployment.getWorkflowExecutions().put(workflowId, executionId);
alienDAO.save(deployment);
}
}
/**
* Check if the destination path can be handled by this event handler
*
* @param destination the destination
* @return true if the event handler manage the destination
*/
@Override
public boolean canHandleDestination(String destination) {
Matcher matcher = DESTINATION_PATTERN.matcher(destination);
return matcher.matches();
}
/**
* Check if the current user is authorized for this destination
*
* @param destination the destination
*/
@Override
public void checkAuthorization(Principal user, String destination) {
Matcher matcher = DESTINATION_PATTERN.matcher(destination);
Authentication authentication = (Authentication) user;
User a4cUser = (User) authentication.getPrincipal();
if (matcher.matches()) {
String deploymentId = matcher.group(1);
checkDeploymentAuthorization(authentication, a4cUser, deploymentId);
} else {
throw new IllegalArgumentException("Cannot handle this destination [" + destination + "]");
}
}
private void checkDeploymentAuthorization(Authentication authentication, User a4cUser, String deploymentId) {
Deployment deployment = alienDAO.findById(Deployment.class, deploymentId);
switch (deployment.getSourceType()) {
case APPLICATION:
// check if the user has right for the environment associated with the deployment.
ApplicationEnvironment environment = alienDAO.findById(ApplicationEnvironment.class, deployment.getEnvironmentId());
if (environment == null) {
log.error("Environment with id [{}] do not exist any more for deployment [{}]", deployment.getEnvironmentId(), deployment.getId());
throw new NotFoundException(
"Environment with id [" + deployment.getEnvironmentId() + "] do not exist any more for deployment [" + deployment.getId() + "]");
}
AuthorizationUtil.checkAuthorization(a4cUser, environment, ApplicationRole.APPLICATION_MANAGER, ApplicationEnvironmentRole.values());
break;
case CSAR:
AuthorizationUtil.checkHasOneRoleIn(authentication, Role.COMPONENTS_MANAGER);
}
}
@Override
public void eventHappened(AbstractMonitorEvent event) {
send(event);
if (log.isTraceEnabled()) {
log.trace("Pushed event {} for workflow {}", event, event.getDeploymentId());
}
}
@Override
public boolean canHandle(AbstractMonitorEvent event) {
return AbstractPaaSWorkflowMonitorEvent.class.isAssignableFrom(event.getClass());
}
@Override
public void afterPropertiesSet() throws Exception {
paasEventService.addListener(this);
}
}