Newer
Older
import it.inaf.ia2.aa.ServletRapClient;
import it.inaf.ia2.aa.data.User;
import it.inaf.ia2.rap.client.call.TokenExchangeRequest;
import it.inaf.oats.vospace.JobService.JobDirection;
import it.inaf.oats.vospace.datamodel.NodeProperties;
Nicola Fulvio Calabria
committed
import it.inaf.oats.vospace.datamodel.NodeUtils;
import static it.inaf.oats.vospace.datamodel.NodeUtils.urlEncodePath;
import it.inaf.oats.vospace.exception.InternalFaultException;
Sonia Zorba
committed
import it.inaf.oats.vospace.exception.InvalidArgumentException;
import it.inaf.oats.vospace.exception.NodeNotFoundException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
Nicola Fulvio Calabria
committed
import it.inaf.oats.vospace.exception.ProtocolNotSupportedException;
import it.inaf.oats.vospace.exception.NodeBusyException;
import it.inaf.oats.vospace.persistence.LocationDAO;
import it.inaf.oats.vospace.persistence.NodeDAO;
import it.inaf.oats.vospace.persistence.model.Location;
import it.inaf.oats.vospace.persistence.model.LocationType;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
Nicola Fulvio Calabria
committed
import java.util.Optional;
Sonia Zorba
committed
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.uws.v1.JobSummary;
import net.ivoa.xml.uws.v1.ResultReference;
Nicola Fulvio Calabria
committed
import net.ivoa.xml.vospace.v2.DataNode;
import net.ivoa.xml.vospace.v2.Protocol;
import net.ivoa.xml.vospace.v2.Transfer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class UriService {
@Value("${vospace-authority}")
private String authority;
@Value("${file-service-url}")
private String fileServiceUrl;
@Autowired
private NodeDAO nodeDao;
Nicola Fulvio Calabria
committed
@Autowired
private LocationDAO locationDAO;
@Autowired
private HttpServletRequest servletRequest;
@Autowired
private ServletRapClient rapClient;
Nicola Fulvio Calabria
committed
@Autowired
private CreateNodeController createNodeController;
public void setTransferJobResult(JobSummary job, Transfer transfer) {
List<ResultReference> results = new ArrayList<>();
Nicola Fulvio Calabria
committed
ResultReference result = new ResultReference();
result.setHref(getEndpoint(job, transfer));
results.add(result);
job.setResults(results);
Nicola Fulvio Calabria
committed
// Moved phase setting to caller method for ERROR management
Sonia Zorba
committed
/**
* Sets the endpoint value for all valid protocols (protocol negotiation).
*/
public void setSyncTransferEndpoints(JobSummary job) {
Transfer transfer = getTransfer(job);
Sonia Zorba
committed
if (transfer.getProtocols().isEmpty()) {
// At least one protocol is expected from client
throw new InvalidArgumentException("Transfer contains no protocols");
}
JobService.JobDirection jobDirection
= JobDirection.getJobDirectionEnumFromTransfer(transfer);
Sonia Zorba
committed
List<String> validProtocolUris = new ArrayList<>();
switch (jobDirection) {
Sonia Zorba
committed
case pullFromVoSpace:
validProtocolUris.add("ivo://ivoa.net/vospace/core#httpget");
break;
case pushToVoSpace:
validProtocolUris.add("ivo://ivoa.net/vospace/core#httpput");
break;
default:
throw new InternalFaultException("Unsupported job direction specified");
Sonia Zorba
committed
}
List<Protocol> validProtocols
= transfer.getProtocols().stream()
.filter(protocol -> validProtocolUris.contains(protocol.getUri()))
.collect(Collectors.toList());
Sonia Zorba
committed
if (validProtocols.isEmpty()) {
Protocol protocol = transfer.getProtocols().get(0);
Nicola Fulvio Calabria
committed
throw new ProtocolNotSupportedException(protocol.getUri());
Sonia Zorba
committed
String endpoint = getEndpoint(job, transfer);
validProtocols.stream().forEach(p -> p.setEndpoint(endpoint));
// Returns modified transfer containing only valid protocols
transfer.getProtocols().clear();
transfer.getProtocols().addAll(validProtocols);
Nicola Fulvio Calabria
committed
private Node getEndpointNode(String relativePath,
JobService.JobDirection jobType,
Nicola Fulvio Calabria
committed
User user) {
Optional<Node> optNode = nodeDao.listNode(relativePath);
Sonia Zorba
committed
if (optNode.isPresent()) {
Nicola Fulvio Calabria
committed
return optNode.get();
} else {
switch (jobType) {
case pullFromVoSpace:
throw new NodeNotFoundException(relativePath);
case pushToVoSpace:
case pullToVoSpace:
DataNode newNode = new DataNode();
newNode.setUri(relativePath);
Sonia Zorba
committed
return createNodeController.createNode(newNode, user);
Nicola Fulvio Calabria
committed
default:
throw new InternalFaultException("No supported job direction specified");
}
}
}
private String getEndpoint(JobSummary job, Transfer transfer) {
String relativePath = transfer.getTarget().substring("vos://".length() + authority.length());
Nicola Fulvio Calabria
committed
User user = (User) servletRequest.getUserPrincipal();
String creator = user.getName();
List<String> groups = user.getGroups();
// Check privileges write or read according to job type
JobService.JobDirection jobType =
JobDirection.getJobDirectionEnumFromTransfer(transfer);
Nicola Fulvio Calabria
committed
Node node = this.getEndpointNode(relativePath, jobType, user);
Nicola Fulvio Calabria
committed
switch (jobType) {
case pushToVoSpace:
case pullToVoSpace:
if (!NodeUtils.checkIfWritable(node, creator, groups)) {
throw new PermissionDeniedException(relativePath);
}
break;
case pullFromVoSpace:
if (!NodeUtils.checkIfReadable(node, creator, groups)) {
throw new PermissionDeniedException(relativePath);
}
break;
default:
Nicola Fulvio Calabria
committed
throw new InternalFaultException("No supported job direction specified");
Nicola Fulvio Calabria
committed
}
if (NodeUtils.getIsBusy(node)) {
throw new NodeBusyException(relativePath);
}
Location location = locationDAO.getNodeLocation(relativePath).orElse(null);
if (location != null && location.getType() == LocationType.PORTAL) {
String fileName = nodeDao.getNodeOsName(relativePath);
endpoint = "http://" + location.getSource().getHostname() + location.getSource().getBaseUrl();
if (!endpoint.endsWith("/")) {
endpoint += "/";
}
endpoint += fileName;
} else {
endpoint = fileServiceUrl + urlEncodePath(relativePath);
}
endpoint += "?jobId=" + job.getJobId();
if (!"true".equals(NodeProperties.getNodePropertyByURI(node, NodeProperties.PUBLIC_READ_URI))) {
endpoint += "&token=" + getEndpointToken(fileServiceUrl + relativePath);
}
return endpoint;
}
private String getEndpointToken(String endpoint) {
String token = ((User) servletRequest.getUserPrincipal()).getAccessToken();
if (token == null) {
throw new PermissionDeniedException("Token is null");
}
TokenExchangeRequest exchangeRequest = new TokenExchangeRequest()
.setSubjectToken(token)
.setResource(endpoint);
// TODO: add audience and scope
return rapClient.exchangeToken(exchangeRequest, servletRequest);
}
public void setNodeRemoteLocation(String nodeUri, String contentUri) {
URL url;
try {
url = new URL(contentUri);
} catch (MalformedURLException ex) {
throw new InternalFaultException(ex);
}
Location location = locationDAO.findPortalLocation(url.getHost()).orElseThrow(()
-> new InternalFaultException("No registered location found for host " + url.getHost()));
String vosPath = nodeUri.replaceAll("vos://[^/]+", "");
String fileName = url.getPath().substring(url.getPath().lastIndexOf("/") + 1);
nodeDao.setNodeLocation(vosPath, location.getId(), fileName);
}
public Transfer getTransfer(JobSummary job) {
List<Object> jobPayload = job.getJobInfo().getAny();
if (jobPayload.isEmpty()) {
Sonia Zorba
committed
throw new InternalFaultException("Empty job payload for job " + job.getJobId());
}
if (jobPayload.size() > 1) {
Sonia Zorba
committed
throw new InternalFaultException("Multiple objects in job payload not supported");
}
if (!(jobPayload.get(0) instanceof Transfer)) {
Sonia Zorba
committed
throw new InternalFaultException(jobPayload.get(0).getClass().getCanonicalName()
+ " not supported as job payload. Job id: " + job.getJobId());
}
return (Transfer) job.getJobInfo().getAny().get(0);
}