Skip to content
UriService.java 6.85 KiB
Newer Older
package it.inaf.oats.vospace;

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.JobType;
import it.inaf.oats.vospace.datamodel.NodeProperties;
import it.inaf.oats.vospace.datamodel.NodeUtils;
import static it.inaf.oats.vospace.datamodel.NodeUtils.urlEncodePath;
import it.inaf.oats.vospace.exception.InternalFaultException;
import it.inaf.oats.vospace.exception.NodeNotFoundException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
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;
import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.uws.v1.JobSummary;
import net.ivoa.xml.uws.v1.ResultReference;
import net.ivoa.xml.vospace.v2.Node;
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;
    @Autowired
    private LocationDAO locationDAO;
    @Autowired
    private HttpServletRequest servletRequest;

    @Autowired
    private ServletRapClient rapClient;

    public void setTransferJobResult(JobSummary job, Transfer transfer) {

        List<ResultReference> results = new ArrayList<>();

        ResultReference result = new ResultReference();        
        result.setHref(getEndpoint(job, transfer));        
        results.add(result);

        job.setResults(results);
        // Moved phase setting to caller method for ERROR management
        // job.setPhase(ExecutionPhase.COMPLETED);
    public void setSyncTransferEndpoints(JobSummary job) {
        Transfer transfer = getTransfer(job);

        Protocol protocol = transfer.getProtocols().get(0);
        if (!"ivo://ivoa.net/vospace/core#httpget".equals(protocol.getUri())
                && !"ivo://ivoa.net/vospace/core#httpput".equals(protocol.getUri())) {
            // throw new IllegalStateException("Unsupported protocol " + protocol.getUri());
            throw new ProtocolNotSupportedException(protocol.getUri());
        protocol.setEndpoint(getEndpoint(job, transfer));
    private String getEndpoint(JobSummary job, Transfer transfer) {

        String relativePath = transfer.getTarget().substring("vos://".length() + authority.length());

        Node node = nodeDao.listNode(relativePath).orElseThrow(() -> new NodeNotFoundException(relativePath));
        
        User user = (User) servletRequest.getUserPrincipal();
        String creator = user.getName();
        List<String> groups = user.getGroups();

        // Check privileges write or read according to job type
        JobService.JobType jobType = JobType.valueOf(transfer.getDirection());

        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:
                throw new InternalFaultException("No job direction specified");
        }

        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()) {
            throw new IllegalStateException("Empty job payload for job " + job.getJobId());
        }
        if (jobPayload.size() > 1) {
            throw new IllegalStateException("Multiple objects in job payload not supported");
        }
        if (!(jobPayload.get(0) instanceof Transfer)) {
            throw new IllegalStateException(jobPayload.get(0).getClass().getCanonicalName()
                    + " not supported as job payload. Job id: " + job.getJobId());
        }

        return (Transfer) job.getJobInfo().getAny().get(0);
    }