/* The contents of this file are subject to the license and copyright terms * detailed in the license directory at the root of the source tree (also * available online at http://fedora-commons.org/license/). */ package org.fcrepo.server.validation; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.fcrepo.common.FaultException; import org.fcrepo.common.PID; import org.fcrepo.server.errors.ServerException; import org.fcrepo.server.errors.StreamIOException; import org.fcrepo.server.errors.ValidationException; import org.fcrepo.server.security.PolicyParser; import org.fcrepo.server.storage.DOReader; import org.fcrepo.server.storage.types.Datastream; import org.fcrepo.server.storage.types.DatastreamManagedContent; /** * Misc validation-related functions. * * @author Chris Wilper * @author Edwin Shin * @version $Id$ */ public abstract class ValidationUtility { private static final Logger logger = LoggerFactory.getLogger(ValidationUtility.class); private static PolicyParser policyParser; private static PolicyParser feslPolicyParser; // FIXME: this to maintain backward compatibility, validation should be enforced private static boolean validateFeslPolicy = false; /** * Validates the candidate URL. The result of the validation also depends on the * control group of the datastream in question. Managed datastreams may be ingested * using the file URI scheme, other datastreams may not. * * @param url * The URL to validate. * @param controlGroup * The control group of the datastream the URL belongs to. * * @throws ValidationException * if the URL is malformed. */ public static void validateURL(String url, String controlGroup) throws ValidationException { if (!(controlGroup.equalsIgnoreCase("M") || controlGroup.equalsIgnoreCase("E")) && url.startsWith("file:")) { throw new ValidationException( "Malformed URL (file: not allowed for control group " + controlGroup + ") " + url); } try { new URL(url); } catch (MalformedURLException e) { if (url.startsWith(DatastreamManagedContent.UPLOADED_SCHEME)) { return; } throw new ValidationException("Malformed URL: " + url, e); } } /** * Sets the policy parser to be used to validate "POLICY" datastream. * * NOTE: This must be set before attempting to validate POLICY datastreams. * Otherwise, a runtime exception will be thrown. * * @param parser the parser to use. */ public static void setPolicyParser(PolicyParser parser) { policyParser = parser; } public static void setFeslPolicyParser(PolicyParser parser) { feslPolicyParser = parser; } public static void setValidateFeslPolicy(boolean validate) { validateFeslPolicy = validate; } /** * Validates the latest version of all reserved datastreams in the given * object. */ public static void validateReservedDatastreams(DOReader reader) throws ValidationException { try { for (Datastream ds: reader.GetDatastreams(null, null)) { if ("X".equals(ds.DSControlGrp) || "M".equals(ds.DSControlGrp)) { validateReservedDatastream(PID.getInstance(reader.GetObjectPID()), ds.DatastreamID, ds); } } } catch (ValidationException e) { throw e; } catch (ServerException e) { throw new FaultException(e); } } /** * Validates the given datastream if it's a reserved datastream. * * The given stream is guaranteed to be closed when this method completes. */ public static void validateReservedDatastream(PID pid, String dsId, Datastream ds) throws ValidationException { // NB: only want to generate inputstream from .getContentStream() once // we know that this is a datastream to validate // to prevent reading of non-reserved datstream content when it is not needed InputStream content = null; try { if ("POLICY".equals(dsId)) { content = ds.getContentStream(); validatePOLICY(content); } else if ("FESLPOLICY".equals(dsId)) { content = ds.getContentStream(); validateFESLPOLICY(content); } else if ("RELS-EXT".equals(dsId) || "RELS-INT".equals(dsId)) { content = ds.getContentStream(); validateRELS(pid, dsId, content); } } catch (StreamIOException e) { throw new ValidationException("Failed to get content stream for " + pid + "/" + dsId + ": " + e.getMessage(), e); } if (content != null) { try { content.close(); } catch (IOException e) { throw new ValidationException("Error closing content stream for " + pid + "/" + dsId + ": " + e.getMessage(), e); } } } private static void validatePOLICY(InputStream content) throws ValidationException { logger.debug("Validating POLICY datastream"); policyParser.copy().parse(content, true); logger.debug("POLICY datastream is valid"); } private static void validateFESLPOLICY(InputStream content) throws ValidationException { // if FeSL is not enabled, this won't be set if (feslPolicyParser != null) { logger.debug("Validating FESLPOLICY datastream"); // FIXME: maintaining backwards compatibility; policy validation should really be enforced feslPolicyParser.copy().parse(content, validateFeslPolicy); logger.debug("FESLPOLICY datastream is valid"); } } /** * validate relationships datastream * @param pid * @param dsId * @param content * @throws ValidationException */ private static void validateRELS(PID pid, String dsId, InputStream content) throws ValidationException { logger.debug("Validating " + dsId + " datastream"); new RelsValidator().validate(pid, dsId, content); logger.debug(dsId + " datastream is valid"); } }