001/* 002 * The contents of this file are subject to the license and copyright 003 * detailed in the LICENSE and NOTICE files at the root of the source 004 * tree. 005 */ 006package org.fcrepo.integration.rdf; 007 008import static org.apache.jena.rdf.model.ResourceFactory.createProperty; 009import static org.fcrepo.kernel.api.RdfLexicon.CONTAINS; 010import static org.fcrepo.kernel.api.RdfLexicon.HAS_MEMBER_RELATION; 011import static org.fcrepo.kernel.api.RdfLexicon.PREMIS_NAMESPACE; 012import static org.fcrepo.kernel.api.RdfLexicon.RDF_NAMESPACE; 013import static org.junit.Assert.assertEquals; 014import static org.junit.Assert.assertFalse; 015import static org.junit.Assert.assertTrue; 016import static org.fcrepo.kernel.api.RdfLexicon.LDP_NAMESPACE; 017import static java.util.Arrays.asList; 018import static javax.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION; 019import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE; 020import static javax.ws.rs.core.HttpHeaders.LINK; 021import static javax.ws.rs.core.MediaType.TEXT_PLAIN; 022import static javax.ws.rs.core.Response.Status.CREATED; 023import static org.fcrepo.kernel.api.FedoraTypes.FCR_METADATA; 024import static org.fcrepo.kernel.api.RdfLexicon.WRITABLE; 025import static org.fcrepo.kernel.api.RdfLexicon.REPOSITORY_NAMESPACE; 026import static org.fcrepo.kernel.api.RdfLexicon.MEMENTO_NAMESPACE; 027import static org.fcrepo.kernel.api.RdfLexicon.MEMENTO_TYPE; 028import static org.fcrepo.kernel.api.RdfLexicon.FEDORA_CONTAINER; 029import static org.fcrepo.kernel.api.RdfLexicon.RESOURCE; 030import static org.fcrepo.kernel.api.RdfLexicon.HAS_SIZE; 031import static org.fcrepo.kernel.api.RdfLexicon.HAS_MIME_TYPE; 032import static org.fcrepo.kernel.api.RdfLexicon.HAS_ORIGINAL_NAME; 033import static org.apache.jena.rdf.model.ResourceFactory.createResource; 034 035import java.util.List; 036 037import org.apache.http.client.methods.CloseableHttpResponse; 038import org.apache.http.client.methods.HttpPatch; 039import org.apache.http.client.methods.HttpPost; 040import org.apache.http.entity.StringEntity; 041import org.apache.jena.datatypes.xsd.XSDDatatype; 042import org.apache.jena.rdf.model.Literal; 043import org.apache.jena.rdf.model.Model; 044import org.apache.jena.rdf.model.Property; 045import org.apache.jena.rdf.model.RDFNode; 046import org.apache.jena.rdf.model.Resource; 047import org.fcrepo.integration.http.api.AbstractResourceIT; 048import org.junit.Ignore; 049import org.junit.Test; 050 051/** 052 * @author bbpennel 053 */ 054@Ignore // TODO FIX THESE TESTS 055public class ServerManagedTriplesIT extends AbstractResourceIT { 056 057 // BINARY DESCRIPTIONS 058 public static final Property DESCRIBED_BY = 059 createProperty("http://www.iana.org/assignments/relation/describedby"); 060 061 private final static String NON_EXISTENT_PREDICATE = "any_predicate_will_do"; 062 063 private final static String NON_EXISTENT_TYPE = "any_type_is_fine"; 064 065 private final static List<String> INDIVIDUAL_SM_PREDS = asList( 066 PREMIS_NAMESPACE + "hasMessageDigest", 067 PREMIS_NAMESPACE + "hasFixity"); 068 069 @Test 070 public void testServerManagedPredicates() throws Exception { 071 for (final String predicate : INDIVIDUAL_SM_PREDS) { 072 verifyRejectLiteral(predicate); 073 verifyRejectUpdateLiteral(predicate); 074 } 075 } 076 077 @Test 078 public void testLdpNamespace() throws Exception { 079 // Verify that ldp:contains referencing another object is rejected 080 final String refPid = getRandomUniqueId(); 081 final String refURI = serverAddress + refPid; 082 createObject(refPid); 083 084 verifyRejectUriRef(CONTAINS.getURI(), refURI); 085 verifyRejectUpdateUriRef(CONTAINS.getURI(), refURI); 086 087 // Verify that ldp:hasMemberRelation referencing an SMT is rejected 088 verifyRejectUriRef(HAS_MEMBER_RELATION.getURI(), REPOSITORY_NAMESPACE + NON_EXISTENT_PREDICATE); 089 verifyRejectUpdateUriRef(HAS_MEMBER_RELATION.getURI(), REPOSITORY_NAMESPACE + NON_EXISTENT_PREDICATE); 090 verifyRejectUriRef(HAS_MEMBER_RELATION.getURI(), CONTAINS.getURI()); 091 verifyRejectUpdateUriRef(HAS_MEMBER_RELATION.getURI(), CONTAINS.getURI()); 092 093 // Verify that types in the ldp namespace are rejected 094 verifyRejectRdfType(RESOURCE.getURI()); 095 verifyRejectUpdateRdfType(RESOURCE.getURI()); 096 verifyRejectRdfType(LDP_NAMESPACE + NON_EXISTENT_TYPE); 097 verifyRejectUpdateRdfType(LDP_NAMESPACE + NON_EXISTENT_TYPE); 098 } 099 100 @Test 101 public void testFedoraNamespace() throws Exception { 102 // Verify rejection of known property 103 verifyRejectLiteral(WRITABLE.getURI()); 104 verifyRejectUpdateLiteral(WRITABLE.getURI()); 105 // Verify rejection of non-existent property 106 verifyRejectLiteral(REPOSITORY_NAMESPACE + NON_EXISTENT_PREDICATE); 107 verifyRejectUpdateLiteral(REPOSITORY_NAMESPACE + NON_EXISTENT_PREDICATE); 108 109 // Verify that types in this namespace are rejected 110 verifyRejectRdfType(FEDORA_CONTAINER.getURI()); 111 verifyRejectUpdateRdfType(FEDORA_CONTAINER.getURI()); 112 verifyRejectRdfType(REPOSITORY_NAMESPACE + NON_EXISTENT_TYPE); 113 verifyRejectUpdateRdfType(REPOSITORY_NAMESPACE + NON_EXISTENT_TYPE); 114 } 115 116 @Test 117 public void testMementoNamespace() throws Exception { 118 // Verify rejection of known property 119 verifyRejectLiteral(MEMENTO_NAMESPACE + "mementoDatetime"); 120 verifyRejectUpdateLiteral(MEMENTO_NAMESPACE + "mementoDatetime"); 121 // Verify rejection of non-existent property 122 verifyRejectLiteral(MEMENTO_NAMESPACE + NON_EXISTENT_PREDICATE); 123 verifyRejectUpdateLiteral(MEMENTO_NAMESPACE + NON_EXISTENT_PREDICATE); 124 125 // Verify rejection of known type 126 verifyRejectRdfType(MEMENTO_TYPE); 127 verifyRejectUpdateRdfType(MEMENTO_TYPE); 128 // Verify rejection of non-existent type 129 verifyRejectRdfType(MEMENTO_NAMESPACE + NON_EXISTENT_TYPE); 130 verifyRejectUpdateRdfType(MEMENTO_NAMESPACE + NON_EXISTENT_TYPE); 131 } 132 133 private void verifyRejectRdfType(final String typeURI) throws Exception { 134 verifyRejectUriRef(RDF_NAMESPACE + "type", typeURI); 135 } 136 137 private void verifyRejectUriRef(final String predicate, final String refURI) throws Exception { 138 final String pid = getRandomUniqueId(); 139 final String content = "<> <" + predicate + "> <" + refURI + "> ."; 140 try (final CloseableHttpResponse response = execute(putObjMethod(pid, "text/turtle", content))) { 141 assertEquals("Must reject server managed property <" + predicate + "> <" + refURI + ">", 142 409, response.getStatusLine().getStatusCode()); 143 } 144 } 145 146 private void verifyRejectLiteral(final String predicate) throws Exception { 147 final String pid = getRandomUniqueId(); 148 final String content = "<> <" + predicate + "> \"value\" ."; 149 try (final CloseableHttpResponse response = execute(putObjMethod(pid, "text/turtle", content))) { 150 assertEquals("Must reject server managed property <" + predicate + ">", 151 409, response.getStatusLine().getStatusCode()); 152 } 153 } 154 155 private void verifyRejectUpdateLiteral(final String predicate) throws Exception { 156 final String updateString = 157 "INSERT { <> <" + predicate + "> \"value\" } WHERE { }"; 158 159 final String pid = getRandomUniqueId(); 160 createObject(pid); 161 try (final CloseableHttpResponse response = performUpdate(pid, updateString)) { 162 assertEquals("Must reject update of server managed property <" + predicate + ">", 163 409, response.getStatusLine().getStatusCode()); 164 } 165 } 166 167 private void verifyRejectUpdateRdfType(final String typeURI) throws Exception { 168 verifyRejectUpdateUriRef(RDF_NAMESPACE + "type", typeURI); 169 } 170 171 private void verifyRejectUpdateUriRef(final String predicate, final String refURI) throws Exception { 172 final String updateString = 173 "INSERT { <> <" + predicate + "> <" + refURI + "> } WHERE { }"; 174 175 final String pid = getRandomUniqueId(); 176 createObject(pid); 177 try (final CloseableHttpResponse response = performUpdate(pid, updateString)) { 178 assertEquals("Must reject update of server managed property <" + predicate + "> <" + refURI + ">", 179 409, response.getStatusLine().getStatusCode()); 180 } 181 } 182 183 private CloseableHttpResponse performUpdate(final String pid, final String updateString) throws Exception { 184 final HttpPatch patchProp = patchObjMethod(pid); 185 patchProp.setHeader(CONTENT_TYPE, "application/sparql-update"); 186 patchProp.setEntity(new StringEntity(updateString)); 187 return execute(patchProp); 188 } 189 190 @Test 191 public void testNonRdfSourceServerGeneratedTriples() throws Exception { 192 final String pid = getRandomUniqueId(); 193 final String describedPid = pid + "/" + FCR_METADATA; 194 final String location = serverAddress + pid; 195 196 final String filename = "some-file.txt"; 197 final String content = "this is the content"; 198 createBinary(pid, filename, TEXT_PLAIN, content); 199 200 final Model model = getModel(describedPid); 201 202 // verify properties initially generated 203 final Resource resc = model.getResource(location); 204 assertEquals(content.length(), resc.getProperty(HAS_SIZE).getLong()); 205 assertEquals(serverAddress + describedPid, resc.getProperty(DESCRIBED_BY).getResource().getURI()); 206 assertEquals("text/plain", resc.getProperty(HAS_MIME_TYPE).getString()); 207 assertEquals(filename, resc.getProperty(HAS_ORIGINAL_NAME).getString()); 208 209 // verify properties can be deleted 210 // iana:describedby cannot be totally removed since it is added in the response 211 verifyDeleteExistingProperty(describedPid, location, HAS_SIZE, 212 resc.getProperty(HAS_SIZE).getObject()); 213 verifyDeleteExistingProperty(describedPid, location, HAS_MIME_TYPE, 214 resc.getProperty(HAS_MIME_TYPE).getObject()); 215 verifyDeleteExistingProperty(describedPid, location, HAS_ORIGINAL_NAME, 216 resc.getProperty(HAS_ORIGINAL_NAME).getObject()); 217 218 // verify property can be added 219 verifySetProperty(describedPid, location, DESCRIBED_BY, createResource("http://example.com")); 220 verifySetProperty(describedPid, location, HAS_SIZE, model.createTypedLiteral(99L)); 221 verifySetProperty(describedPid, location, HAS_MIME_TYPE, model.createLiteral("text/special")); 222 verifySetProperty(describedPid, location, HAS_ORIGINAL_NAME, model.createLiteral("different.txt")); 223 224 // Verify deletion of added describedby 225 verifyDeleteExistingProperty(describedPid, location, DESCRIBED_BY, 226 createResource("http://example.com")); 227 } 228 229 private void createBinary(final String pid, final String filename, final String contentType, final String content) 230 throws Exception { 231 final HttpPost post = new HttpPost(serverAddress); 232 post.setEntity(new StringEntity(content)); 233 post.setHeader("Slug", pid); 234 post.setHeader(CONTENT_TYPE, contentType); 235 post.setHeader(CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\""); 236 post.setHeader(LINK, NON_RDF_SOURCE_LINK_HEADER); 237 try (final CloseableHttpResponse response = execute(post)) { 238 assertEquals(CREATED.getStatusCode(), getStatus(response)); 239 } 240 } 241 242 private void verifyDeleteExistingProperty(final String pid, final String subjectURI, 243 final Property property, final RDFNode object) throws Exception { 244 final String value = rdfNodeToString(object); 245 final String deleteString = 246 "DELETE { <> <" + property.getURI() + "> " + value + " } WHERE { }"; 247 248 performUpdate(pid, deleteString).close(); 249 250 final Model resultModel = getModel(pid); 251 final Resource resultResc = resultModel.getResource(subjectURI); 252 assertFalse("Must not contain deleted property " + property, resultResc.hasProperty(property, object)); 253 } 254 255 private void verifySetProperty(final String pid, final String subjectURI, final Property property, 256 final RDFNode object) throws Exception { 257 final String value = rdfNodeToString(object); 258 final String updateString = 259 "INSERT { <> <" + property.getURI() + "> " + value + " } WHERE { }"; 260 261 performUpdate(pid, updateString).close(); 262 263 final Model model = getModel(pid); 264 final Resource resc = model.getResource(subjectURI); 265 assertTrue("Must contain updated property " + property, resc.hasProperty(property, object)); 266 } 267 268 private String rdfNodeToString(final RDFNode object) { 269 String value; 270 if (object.isLiteral()) { 271 final Literal literal = object.asLiteral(); 272 value = "\"" + literal.getValue().toString() + "\""; 273 if (!literal.getDatatype().equals(XSDDatatype.XSDstring)) { 274 value += "^^<" + literal.getDatatypeURI() + ">"; 275 } 276 } else { 277 value = "<" + object.toString() + ">"; 278 } 279 return value; 280 } 281}