001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.integration.auth.webac; 019 020import static java.util.Arrays.stream; 021import static javax.ws.rs.core.Response.Status.CREATED; 022import static org.apache.http.HttpStatus.SC_FORBIDDEN; 023import static org.apache.http.HttpStatus.SC_NOT_FOUND; 024import static org.apache.http.HttpStatus.SC_NO_CONTENT; 025import static org.apache.http.HttpHeaders.CONTENT_TYPE; 026import static org.apache.jena.vocabulary.DC_11.title; 027import static org.fcrepo.auth.webac.WebACRolesProvider.GROUP_AGENT_BASE_URI_PROPERTY; 028import static org.fcrepo.http.api.FedoraAcl.ROOT_AUTHORIZATION_PROPERTY; 029import static org.fcrepo.kernel.api.RdfLexicon.DIRECT_CONTAINER; 030import static org.fcrepo.kernel.api.RdfLexicon.INDIRECT_CONTAINER; 031import static org.fcrepo.kernel.modeshape.utils.FedoraSessionUserUtil.USER_AGENT_BASE_URI_PROPERTY; 032import static org.junit.Assert.assertEquals; 033import static org.junit.Assert.assertTrue; 034 035import java.io.IOException; 036import java.io.InputStream; 037import java.io.UnsupportedEncodingException; 038import java.util.Arrays; 039import java.util.Optional; 040import javax.ws.rs.core.Link; 041 042import org.apache.commons.codec.binary.Base64; 043import org.apache.commons.io.IOUtils; 044import org.apache.http.Header; 045import org.apache.http.HeaderElement; 046import org.apache.http.HttpEntity; 047import org.apache.http.HttpResponse; 048import org.apache.http.HttpStatus; 049import org.apache.http.NameValuePair; 050import org.apache.http.client.methods.CloseableHttpResponse; 051import org.apache.http.client.methods.HttpDelete; 052import org.apache.http.client.methods.HttpGet; 053import org.apache.http.client.methods.HttpHead; 054import org.apache.http.client.methods.HttpOptions; 055import org.apache.http.client.methods.HttpPatch; 056import org.apache.http.client.methods.HttpPost; 057import org.apache.http.client.methods.HttpPut; 058import org.apache.http.entity.ContentType; 059import org.apache.http.entity.InputStreamEntity; 060import org.apache.http.entity.StringEntity; 061import org.apache.http.message.AbstractHttpMessage; 062import org.fcrepo.integration.http.api.AbstractResourceIT; 063import org.junit.Ignore; 064import org.junit.Rule; 065import org.junit.Test; 066import org.junit.contrib.java.lang.system.RestoreSystemProperties; 067import org.slf4j.Logger; 068import org.slf4j.LoggerFactory; 069 070/** 071 * @author Peter Eichman 072 * @author whikloj 073 * @since September 4, 2015 074 */ 075public class WebACRecipesIT extends AbstractResourceIT { 076 077 private static final Logger logger = LoggerFactory.getLogger(WebACRecipesIT.class); 078 079 @Rule 080 public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); 081 082 private final ContentType turtleContentType = ContentType.create("text/turtle", "UTF-8"); 083 084 private final ContentType sparqlContentType = ContentType.create("application/sparql-update", "UTF-8"); 085 086 /** 087 * Convenience method to create an ACL with 0 or more authorization resources in the respository. 088 */ 089 private String ingestAcl(final String username, 090 final String aclFilePath, final String aclResourcePath) throws IOException { 091 092 // create the ACL 093 final HttpResponse aclResponse = ingestTurtleResource(username, aclFilePath, aclResourcePath); 094 095 // return the URI to the newly created resource 096 return aclResponse.getFirstHeader("Location").getValue(); 097 } 098 099 /** 100 * Convenience method to POST the contents of a Turtle file to the repository to create a new resource. Returns 101 * the HTTP response from that request. Throws an IOException if the server responds with anything other than a 102 * 201 Created response code. 103 */ 104 private HttpResponse ingestTurtleResource(final String username, final String path, final String requestURI) 105 throws IOException { 106 final HttpPut request = new HttpPut(requestURI); 107 108 logger.debug("PUT to {} to create {}", requestURI, path); 109 110 setAuth(request, username); 111 112 final InputStream file = this.getClass().getResourceAsStream(path); 113 final InputStreamEntity fileEntity = new InputStreamEntity(file); 114 request.setEntity(fileEntity); 115 request.setHeader("Content-Type", "text/turtle"); 116 117 try (final CloseableHttpResponse response = execute(request)) { 118 assertEquals( 119 "Didn't get a CREATED response!: " + IOUtils.toString(response.getEntity().getContent(), "UTF-8"), 120 CREATED.getStatusCode(), getStatus(response)); 121 return response; 122 } 123 124 } 125 126 /** 127 * Convenience method to set up a regular FedoraResource 128 * 129 * @param path Path to put the resource under 130 * @return the Location of the newly created resource 131 * @throws IOException on error 132 */ 133 private String ingestObj(final String path) throws IOException { 134 final HttpPut request = putObjMethod(path.replace(serverAddress, "")); 135 setAuth(request, "fedoraAdmin"); 136 try (final CloseableHttpResponse response = execute(request)) { 137 assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); 138 return response.getFirstHeader("Location").getValue(); 139 } 140 } 141 142 private String ingestBinary(final String path, final HttpEntity body) throws IOException { 143 logger.info("Ingesting {} binary to {}", body.getContentType().getValue(), path); 144 final HttpPut request = new HttpPut(serverAddress + path); 145 setAuth(request, "fedoraAdmin"); 146 request.setEntity(body); 147 request.setHeader(body.getContentType()); 148 final CloseableHttpResponse response = execute(request); 149 assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); 150 final String location = response.getFirstHeader("Location").getValue(); 151 logger.info("Created binary at {}", location); 152 return location; 153 154 } 155 156 private String ingestDatastream(final String path, final String ds) throws IOException { 157 final HttpPut request = putDSMethod(path, ds, "some not so random content"); 158 setAuth(request, "fedoraAdmin"); 159 try (final CloseableHttpResponse response = execute(request)) { 160 assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); 161 return response.getFirstHeader("Location").getValue(); 162 } 163 } 164 165 /** 166 * Convenience method for applying credentials to a request 167 * 168 * @param method the request to add the credentials to 169 * @param username the username to add 170 */ 171 private static void setAuth(final AbstractHttpMessage method, final String username) { 172 final String creds = username + ":password"; 173 final String encCreds = new String(Base64.encodeBase64(creds.getBytes())); 174 final String basic = "Basic " + encCreds; 175 method.setHeader("Authorization", basic); 176 } 177 178 @Test 179 public void scenario1() throws IOException { 180 final String testObj = ingestObj("/rest/webacl_box1"); 181 final String acl1 = ingestAcl("fedoraAdmin", "/acls/01/acl.ttl", 182 testObj + "/fcr:acl"); 183 final String aclLink = Link.fromUri(acl1).rel("acl").build().toString(); 184 185 final HttpGet request = getObjMethod(testObj.replace(serverAddress, "")); 186 assertEquals("Anonymous can read " + testObj, HttpStatus.SC_FORBIDDEN, getStatus(request)); 187 188 setAuth(request, "user01"); 189 try (final CloseableHttpResponse response = execute(request)) { 190 assertEquals("User 'user01' can't read" + testObj, HttpStatus.SC_OK, getStatus(response)); 191 // This gets the Link headers and filters for the correct one (aclLink::equals) defined above. 192 final Optional<String> header = stream(response.getHeaders("Link")).map(Header::getValue) 193 .filter(aclLink::equals).findFirst(); 194 // So you either have the correct Link header or you get nothing. 195 assertTrue("Missing Link header", header.isPresent()); 196 } 197 198 final String childObj = ingestObj("/rest/webacl_box1/child"); 199 final HttpGet getReq = getObjMethod(childObj.replace(serverAddress, "")); 200 setAuth(getReq, "user01"); 201 try (final CloseableHttpResponse response = execute(getReq)) { 202 assertEquals("User 'user01' can't read child of " + testObj, HttpStatus.SC_OK, getStatus(response)); 203 } 204 } 205 206 @Test 207 public void scenario2() throws IOException { 208 final String id = "/rest/box/bag/collection"; 209 final String testObj = ingestObj(id); 210 ingestAcl("fedoraAdmin", "/acls/02/acl.ttl", testObj + "/fcr:acl"); 211 212 logger.debug("Anonymous can not read " + testObj); 213 final HttpGet requestGet = getObjMethod(id); 214 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet)); 215 216 logger.debug("GroupId 'Editors' can read " + testObj); 217 final HttpGet requestGet2 = getObjMethod(id); 218 setAuth(requestGet2, "jones"); 219 requestGet2.setHeader("some-header", "Editors"); 220 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 221 222 logger.debug("Anonymous cannot write " + testObj); 223 final HttpPatch requestPatch = patchObjMethod(id); 224 requestPatch.setEntity(new StringEntity("INSERT { <> <" + title.getURI() + "> \"Test title\" . } WHERE {}")); 225 requestPatch.setHeader("Content-type", "application/sparql-update"); 226 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 227 228 logger.debug("Editors can write " + testObj); 229 final HttpPatch requestPatch2 = patchObjMethod(id); 230 setAuth(requestPatch2, "jones"); 231 requestPatch2.setHeader("some-header", "Editors"); 232 requestPatch2.setEntity( 233 new StringEntity("INSERT { <> <" + title.getURI() + "> \"Different title\" . } WHERE {}")); 234 requestPatch2.setHeader("Content-type", "application/sparql-update"); 235 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch2)); 236 } 237 238 @Test 239 public void scenario3() throws IOException { 240 final String idDark = "/rest/dark/archive"; 241 final String idLight = "/rest/dark/archive/sunshine"; 242 final String testObj = ingestObj(idDark); 243 final String testObj2 = ingestObjWithACL(idLight, "/acls/03/acl.ttl"); 244 ingestAcl("fedoraAdmin", "/acls/03/acl.ttl", testObj + "/fcr:acl"); 245 246 logger.debug("Anonymous can't read " + testObj); 247 final HttpGet requestGet = getObjMethod(idDark); 248 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet)); 249 250 logger.debug("Restricted can read " + testObj); 251 final HttpGet requestGet2 = getObjMethod(idDark); 252 setAuth(requestGet2, "jones"); 253 requestGet2.setHeader("some-header", "Restricted"); 254 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 255 256 logger.debug("Anonymous can read " + testObj2); 257 final HttpGet requestGet3 = getObjMethod(idLight); 258 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 259 260 logger.debug("Restricted can read " + testObj2); 261 final HttpGet requestGet4 = getObjMethod(idLight); 262 setAuth(requestGet4, "jones"); 263 requestGet4.setHeader("some-header", "Restricted"); 264 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 265 } 266 267 @Test 268 public void scenario4() throws IOException { 269 final String id = "/rest/public_collection"; 270 final String testObj = ingestObjWithACL(id, "/acls/04/acl.ttl"); 271 272 logger.debug("Anonymous can read " + testObj); 273 final HttpGet requestGet = getObjMethod(id); 274 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 275 276 logger.debug("Editors can read " + testObj); 277 final HttpGet requestGet2 = getObjMethod(id); 278 setAuth(requestGet2, "jones"); 279 requestGet2.setHeader("some-header", "Editors"); 280 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 281 282 logger.debug("Smith can access " + testObj); 283 final HttpGet requestGet3 = getObjMethod(id); 284 setAuth(requestGet3, "smith"); 285 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 286 287 logger.debug("Anonymous can't write " + testObj); 288 final HttpPatch requestPatch = patchObjMethod(id); 289 requestPatch.setHeader("Content-type", "application/sparql-update"); 290 requestPatch.setEntity(new StringEntity("INSERT { <> <" + title.getURI() + "> \"Change title\" . } WHERE {}")); 291 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 292 293 logger.debug("Editors can write " + testObj); 294 final HttpPatch requestPatch2 = patchObjMethod(id); 295 requestPatch2.setHeader("Content-type", "application/sparql-update"); 296 requestPatch2.setEntity(new StringEntity("INSERT { <> <" + title.getURI() + "> \"New title\" . } WHERE {}")); 297 setAuth(requestPatch2, "jones"); 298 requestPatch2.setHeader("some-header", "Editors"); 299 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch2)); 300 301 logger.debug("Editors can create (PUT) child objects of " + testObj); 302 final HttpPut requestPut1 = putObjMethod(id + "/child1"); 303 setAuth(requestPut1, "jones"); 304 requestPut1.setHeader("some-header", "Editors"); 305 assertEquals(HttpStatus.SC_CREATED, getStatus(requestPut1)); 306 307 final HttpGet requestGet4 = getObjMethod(id + "/child1"); 308 setAuth(requestGet4, "jones"); 309 requestGet4.setHeader("some-header", "Editors"); 310 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 311 312 logger.debug("Editors can create (POST) child objects of " + testObj); 313 final HttpPost requestPost1 = postObjMethod(id); 314 requestPost1.addHeader("Slug", "child2"); 315 setAuth(requestPost1, "jones"); 316 requestPost1.setHeader("some-header", "Editors"); 317 assertEquals(HttpStatus.SC_CREATED, getStatus(requestPost1)); 318 319 final HttpGet requestGet5 = getObjMethod(id + "/child2"); 320 setAuth(requestGet5, "jones"); 321 requestGet5.setHeader("some-header", "Editors"); 322 assertEquals(HttpStatus.SC_OK, getStatus(requestGet5)); 323 324 logger.debug("Editors can create nested child objects of " + testObj); 325 final HttpPut requestPut2 = putObjMethod(id + "/a/b/c/child"); 326 setAuth(requestPut2, "jones"); 327 requestPut2.setHeader("some-header", "Editors"); 328 assertEquals(HttpStatus.SC_CREATED, getStatus(requestPut2)); 329 330 final HttpGet requestGet6 = getObjMethod(id + "/a/b/c/child"); 331 setAuth(requestGet6, "jones"); 332 requestGet6.setHeader("some-header", "Editors"); 333 assertEquals(HttpStatus.SC_OK, getStatus(requestGet6)); 334 335 logger.debug("Smith can't write " + testObj); 336 final HttpPatch requestPatch3 = patchObjMethod(id); 337 requestPatch3.setHeader("Content-type", "application/sparql-update"); 338 requestPatch3.setEntity( 339 new StringEntity("INSERT { <> <" + title.getURI() + "> \"Different title\" . } WHERE {}")); 340 setAuth(requestPatch3, "smith"); 341 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch3)); 342 } 343 344 @Test 345 public void scenario5() throws IOException { 346 final String idPublic = "/rest/mixedCollection/publicObj"; 347 final String idPrivate = "/rest/mixedCollection/privateObj"; 348 ingestObjWithACL("/rest/mixedCollection", "/acls/05/acl.ttl"); 349 final String publicObj = ingestObj(idPublic); 350 final String privateObj = ingestObj(idPrivate); 351 final HttpPatch patch = patchObjMethod(idPublic); 352 353 setAuth(patch, "fedoraAdmin"); 354 patch.setHeader("Content-type", "application/sparql-update"); 355 patch.setEntity(new StringEntity("INSERT { <> a <http://example.com/terms#publicImage> . } WHERE {}")); 356 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patch)); 357 358 359 logger.debug("Anonymous can see eg:publicImage " + publicObj); 360 final HttpGet requestGet = getObjMethod(idPublic); 361 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 362 363 logger.debug("Anonymous can't see other resource " + privateObj); 364 final HttpGet requestGet2 = getObjMethod(idPrivate); 365 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet2)); 366 367 logger.debug("Admins can see eg:publicImage " + publicObj); 368 final HttpGet requestGet3 = getObjMethod(idPublic); 369 setAuth(requestGet3, "jones"); 370 requestGet3.setHeader("some-header", "Admins"); 371 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 372 373 logger.debug("Admins can see others" + privateObj); 374 final HttpGet requestGet4 = getObjMethod(idPrivate); 375 setAuth(requestGet4, "jones"); 376 requestGet4.setHeader("some-header", "Admins"); 377 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 378 } 379 380 @Test 381 public void scenario9() throws IOException { 382 final String idPublic = "/rest/anotherCollection/publicObj"; 383 final String groups = "/rest/group"; 384 final String fooGroup = groups + "/foo"; 385 final String testObj = ingestObj("/rest/anotherCollection"); 386 final String publicObj = ingestObj(idPublic); 387 388 final HttpPut request = putObjMethod(fooGroup); 389 setAuth(request, "fedoraAdmin"); 390 391 final InputStream file = this.getClass().getResourceAsStream("/acls/09/group.ttl"); 392 final InputStreamEntity fileEntity = new InputStreamEntity(file); 393 request.setEntity(fileEntity); 394 request.setHeader("Content-Type", "text/turtle;charset=UTF-8"); 395 396 assertEquals("Didn't get a CREATED response!", CREATED.getStatusCode(), getStatus(request)); 397 398 ingestAcl("fedoraAdmin", "/acls/09/acl.ttl", testObj + "/fcr:acl"); 399 400 logger.debug("Person1 can see object " + publicObj); 401 final HttpGet requestGet1 = getObjMethod(idPublic); 402 setAuth(requestGet1, "person1"); 403 assertEquals(HttpStatus.SC_OK, getStatus(requestGet1)); 404 405 logger.debug("Person2 can see object " + publicObj); 406 final HttpGet requestGet2 = getObjMethod(idPublic); 407 setAuth(requestGet2, "person2"); 408 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 409 410 logger.debug("Person3 user cannot see object " + publicObj); 411 final HttpGet requestGet3 = getObjMethod(idPublic); 412 setAuth(requestGet3, "person3"); 413 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet3)); 414 } 415 416 /** 417 * Test cases to verify authorization with only acl:Append mode configured 418 * in the acl authorization of an resource. 419 * Tests: 420 * 1. Deny(403) on GET. 421 * 2. Allow(204) on PATCH. 422 * 3. Deny(403) on DELETE. 423 * 4. Deny(403) on PATCH with SPARQL DELETE statements. 424 * 5. Allow(400) on PATCH with empty SPARQL content. 425 * 6. Deny(403) on PATCH with non-SPARQL content. 426 */ 427 @Test 428 public void scenario18Test1() throws IOException { 429 final String testObj = ingestObj("/rest/append_only_resource"); 430 final String id = "/rest/append_only_resource/" + getRandomUniqueId(); 431 ingestObj(id); 432 433 logger.debug("user18 can read (has ACL:READ): {}", id); 434 final HttpGet requestGet = getObjMethod(id); 435 setAuth(requestGet, "user18"); 436 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 437 438 logger.debug("user18 can't append (no ACL): {}", id); 439 final HttpPatch requestPatch = patchObjMethod(id); 440 setAuth(requestPatch, "user18"); 441 requestPatch.setHeader("Content-type", "application/sparql-update"); 442 requestPatch.setEntity(new StringEntity("INSERT { <> <" + title.getURI() + "> \"Test title\" . } WHERE {}")); 443 444 logger.debug("user18 can't delete (no ACL): {}", id); 445 final HttpDelete requestDelete = deleteObjMethod(id); 446 setAuth(requestDelete, "user18"); 447 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 448 449 ingestAcl("fedoraAdmin", "/acls/18/append-only-acl.ttl", testObj + "/fcr:acl"); 450 451 logger.debug("user18 still can't read (ACL append): {}", id); 452 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet)); 453 454 logger.debug("user18 can patch - SPARQL INSERTs (ACL append): {}", id); 455 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 456 457 // Alter the Content-type to include a character set, to ensure correct matching. 458 requestPatch.setHeader("Content-type", "application/sparql-update; charset=UTF-8"); 459 logger.debug("user18 can patch - SPARQL INSERTs (ACL append with charset): {}", id); 460 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 461 462 logger.debug("user18 still can't delete (ACL append): {}", id); 463 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 464 465 requestPatch.setEntity(new StringEntity("DELETE { <> <" + title.getURI() + "> \"Test title\" . } WHERE {}")); 466 467 logger.debug("user18 can not patch - SPARQL DELETEs (ACL append): {}", id); 468 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 469 470 requestPatch.setEntity(null); 471 472 logger.debug("user18 can patch (is authorized, but bad request) - Empty SPARQL (ACL append): {}", id); 473 assertEquals(HttpStatus.SC_BAD_REQUEST, getStatus(requestPatch)); 474 475 requestPatch.setHeader("Content-type", null); 476 477 logger.debug("user18 can not patch - Non SPARQL (ACL append): {}", id); 478 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 479 480 } 481 482 /** 483 * Test cases to verify authorization with acl:Read and acl:Append modes 484 * configured in the acl authorization of an resource. 485 * Tests: 486 * 1. Allow(200) on GET. 487 * 2. Allow(204) on PATCH. 488 * 3. Deny(403) on DELETE. 489 */ 490 @Test 491 public void scenario18Test2() throws IOException { 492 final String testObj = ingestObj("/rest/read_append_resource"); 493 494 final String id = "/rest/read_append_resource/" + getRandomUniqueId(); 495 ingestObj(id); 496 497 logger.debug("user18 can read (has ACL:READ): {}", id); 498 final HttpGet requestGet = getObjMethod(id); 499 setAuth(requestGet, "user18"); 500 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 501 502 logger.debug("user18 can't append (no ACL): {}", id); 503 final HttpPatch requestPatch = patchObjMethod(id); 504 setAuth(requestPatch, "user18"); 505 requestPatch.setHeader("Content-type", "application/sparql-update"); 506 requestPatch.setEntity(new StringEntity( 507 "INSERT { <> <" + title.getURI() + "> \"some title\" . } WHERE {}")); 508 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 509 510 ingestAcl("fedoraAdmin", "/acls/18/read-append-acl.ttl", testObj + "/fcr:acl"); 511 512 logger.debug("user18 can't delete (no ACL): {}", id); 513 final HttpDelete requestDelete = deleteObjMethod(id); 514 setAuth(requestDelete, "user18"); 515 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 516 517 logger.debug("user18 can read (ACL read, append): {}", id); 518 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 519 520 logger.debug("user18 can append (ACL read, append): {}", id); 521 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 522 523 logger.debug("user18 still can't delete (ACL read, append): {}", id); 524 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 525 } 526 527 /** 528 * Test cases to verify authorization with acl:Read, acl:Append and 529 * acl:Write modes configured in the acl authorization of an resource. 530 * Tests: 531 * 1. Allow(200) on GET. 532 * 2. Allow(204) on PATCH. 533 * 3. Allow(204) on DELETE. 534 */ 535 @Test 536 public void scenario18Test3() throws IOException { 537 final String testObj = ingestObj("/rest/read_append_write_resource"); 538 539 final String id = "/rest/read_append_write_resource/" + getRandomUniqueId(); 540 ingestObj(id); 541 542 logger.debug("user18 can read (has ACL:READ): {}", id); 543 final HttpGet requestGet = getObjMethod(id); 544 setAuth(requestGet, "user18"); 545 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 546 547 logger.debug("user18 can't append (no ACL): {}", id); 548 final HttpPatch requestPatch = patchObjMethod(id); 549 setAuth(requestPatch, "user18"); 550 requestPatch.setHeader("Content-type", "application/sparql-update"); 551 requestPatch.setEntity(new StringEntity( 552 "INSERT { <> <http://purl.org/dc/elements/1.1/title> \"some title\" . } WHERE {}")); 553 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 554 555 logger.debug("user18 can't delete (no ACL): {}", id); 556 final HttpDelete requestDelete = deleteObjMethod(id); 557 setAuth(requestDelete, "user18"); 558 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 559 560 ingestAcl("fedoraAdmin", "/acls/18/read-append-write-acl.ttl", testObj + "/fcr:acl"); 561 562 logger.debug("user18 can read (ACL read, append, write): {}", id); 563 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 564 565 logger.debug("user18 can append (ACL read, append, write): {}", id); 566 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 567 568 logger.debug("user18 can delete (ACL read, append, write): {}", id); 569 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestDelete)); 570 } 571 572 @Test 573 public void testAccessToRoot() throws IOException { 574 final String id = "/rest/" + getRandomUniqueId(); 575 final String testObj = ingestObj(id); 576 577 logger.debug("Anonymous can read (has ACL:READ): {}", id); 578 final HttpGet requestGet1 = getObjMethod(id); 579 assertEquals(HttpStatus.SC_OK, getStatus(requestGet1)); 580 581 logger.debug("Can username 'user06a' read {} (has ACL:READ)", id); 582 final HttpGet requestGet2 = getObjMethod(id); 583 setAuth(requestGet2, "user06a"); 584 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 585 586 logger.debug("Can username 'notuser06b' read {} (has ACL:READ)", id); 587 final HttpGet requestGet3 = getObjMethod(id); 588 setAuth(requestGet3, "user06b"); 589 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 590 591 System.setProperty(ROOT_AUTHORIZATION_PROPERTY, "./target/test-classes/test-root-authorization2.ttl"); 592 logger.debug("Can username 'user06a' read {} (overridden system ACL)", id); 593 final HttpGet requestGet4 = getObjMethod(id); 594 setAuth(requestGet4, "user06a"); 595 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 596 System.clearProperty(ROOT_AUTHORIZATION_PROPERTY); 597 598 // Add ACL to root 599 final String rootURI = getObjMethod("/rest").getURI().toString(); 600 ingestAcl("fedoraAdmin", "/acls/06/acl.ttl", rootURI + "/fcr:acl"); 601 602 logger.debug("Anonymous still can't read (ACL present)"); 603 final HttpGet requestGet5 = getObjMethod(id); 604 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet5)); 605 606 logger.debug("Can username 'user06a' read {} (ACL present)", testObj); 607 final HttpGet requestGet6 = getObjMethod(id); 608 setAuth(requestGet6, "user06a"); 609 assertEquals(HttpStatus.SC_OK, getStatus(requestGet6)); 610 611 logger.debug("Can username 'user06b' read {} (ACL present)", testObj); 612 final HttpGet requestGet7 = getObjMethod(id); 613 setAuth(requestGet7, "user06b"); 614 assertEquals(HttpStatus.SC_OK, getStatus(requestGet7)); 615 } 616 617 @Test 618 public void scenario21TestACLNotForInheritance() throws IOException { 619 final String parentPath = "/rest/resource_acl_no_inheritance"; 620 // Ingest ACL with no acl:default statement to the parent resource 621 ingestObjWithACL(parentPath, "/acls/21/acl.ttl"); 622 623 final String id = parentPath + "/" + getRandomUniqueId(); 624 final String testObj = ingestObj(id); 625 626 627 // Test the parent ACL with no acl:default is applied for the parent resource authorization. 628 final HttpGet requestGet1 = getObjMethod(parentPath); 629 setAuth(requestGet1, "user21"); 630 assertEquals("Agent user21 can't read resource " + parentPath + " with its own ACL!", 631 HttpStatus.SC_OK, getStatus(requestGet1)); 632 633 final HttpGet requestGet2 = getObjMethod(id); 634 assertEquals("Agent user21 inherits read permission from parent ACL to read resource " + testObj + "!", 635 HttpStatus.SC_OK, getStatus(requestGet2)); 636 637 // Test the default root ACL is inherited for authorization while the parent ACL with no acl:default is ignored 638 System.setProperty(ROOT_AUTHORIZATION_PROPERTY, "./target/test-classes/test-root-authorization2.ttl"); 639 final HttpGet requestGet3 = getObjMethod(id); 640 setAuth(requestGet3, "user06a"); 641 assertEquals("Agent user06a can't inherit read persmssion from root ACL to read resource " + testObj + "!", 642 HttpStatus.SC_OK, getStatus(requestGet3)); 643 } 644 645 @Test 646 public void scenario22TestACLAuthorizationNotForInheritance() throws IOException { 647 final String parentPath = "/rest/resource_mix_acl_default"; 648 final String parentObj = ingestObj(parentPath); 649 650 final String id = parentPath + "/" + getRandomUniqueId(); 651 final String testObj = ingestObj(id); 652 653 // Ingest ACL with mix acl:default authorization to the parent resource 654 ingestAcl("fedoraAdmin", "/acls/22/acl.ttl", parentObj + "/fcr:acl"); 655 656 // Test the parent ACL is applied for the parent resource authorization. 657 final HttpGet requestGet1 = getObjMethod(parentPath); 658 setAuth(requestGet1, "user22a"); 659 assertEquals("Agent user22a can't read resource " + parentPath + " with its own ACL!", 660 HttpStatus.SC_OK, getStatus(requestGet1)); 661 662 final HttpGet requestGet2 = getObjMethod(parentPath); 663 setAuth(requestGet2, "user22b"); 664 assertEquals("Agent user22b can't read resource " + parentPath + " with its own ACL!", 665 HttpStatus.SC_OK, getStatus(requestGet1)); 666 667 // Test the parent ACL is applied for the parent resource authorization. 668 final HttpGet requestGet3 = getObjMethod(id); 669 setAuth(requestGet3, "user22a"); 670 assertEquals("Agent user22a inherits read permission from parent ACL to read resource " + testObj + "!", 671 HttpStatus.SC_FORBIDDEN, getStatus(requestGet3)); 672 673 final HttpGet requestGet4 = getObjMethod(id); 674 setAuth(requestGet4, "user22b"); 675 assertEquals("Agent user22b can't inherits read permission from parent ACL to read resource " + testObj + "!", 676 HttpStatus.SC_OK, getStatus(requestGet4)); 677 } 678 679 @Test 680 public void testAccessToBinary() throws IOException { 681 // Block access to "book" 682 final String idBook = "/rest/book"; 683 final String bookURI = ingestObj(idBook); 684 685 // Open access datastream, "file" 686 final String id = idBook + "/file"; 687 final String testObj = ingestDatastream(idBook, "file"); 688 ingestAcl("fedoraAdmin", "/acls/07/acl.ttl", bookURI + "/fcr:acl"); 689 690 logger.debug("Anonymous can't read"); 691 final HttpGet requestGet1 = getObjMethod(id); 692 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet1)); 693 694 logger.debug("Can username 'user07' read {}", testObj); 695 final HttpGet requestGet2 = getObjMethod(id); 696 697 setAuth(requestGet2, "user07"); 698 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 699 } 700 701 @Test 702 @Ignore("FAILING") 703 public void testAccessToHashResource() throws IOException { 704 final String id = "/rest/some/parent#hash-resource"; 705 final String testObj = ingestObj(id); 706 ingestAcl("fedoraAdmin", "/acls/08/acl.ttl", testObj + "/fcr:acl"); 707 708 logger.debug("Anonymous can't read"); 709 final HttpGet requestGet1 = getObjMethod(id); 710 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet1)); 711 712 logger.debug("Can username 'user08' read {}", testObj); 713 final HttpGet requestGet2 = getObjMethod(id); 714 setAuth(requestGet2, "user08"); 715 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 716 } 717 718 @Test 719 @Ignore ("Until implemented with Memento") 720 public void testAccessToVersionedResources() throws IOException { 721 final String idVersion = "/rest/versionResource"; 722 ingestObj(idVersion); 723 724 final HttpPatch requestPatch1 = patchObjMethod(idVersion); 725 setAuth(requestPatch1, "fedoraAdmin"); 726 requestPatch1.addHeader("Content-type", "application/sparql-update"); 727 requestPatch1.setEntity( 728 new StringEntity("PREFIX pcdm: <http://pcdm.org/models#> INSERT { <> a pcdm:Object } WHERE {}")); 729 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch1)); 730 731 ingestAcl("fedoraAdmin", "/acls/10/acl.ttl", idVersion + "/fcr:acl"); 732 733 final HttpGet requestGet1 = getObjMethod(idVersion); 734 setAuth(requestGet1, "user10"); 735 assertEquals("user10 can't read object", HttpStatus.SC_OK, getStatus(requestGet1)); 736 737 final HttpPost requestPost1 = postObjMethod(idVersion + "/fcr:versions"); 738 requestPost1.addHeader("Slug", "v0"); 739 setAuth(requestPost1, "fedoraAdmin"); 740 assertEquals("Unable to create a new version", HttpStatus.SC_CREATED, getStatus(requestPost1)); 741 742 final HttpGet requestGet2 = getObjMethod(idVersion); 743 setAuth(requestGet2, "user10"); 744 assertEquals("user10 can't read versioned object", HttpStatus.SC_OK, getStatus(requestGet2)); 745 } 746 747 @Test 748 public void testDelegatedUserAccess() throws IOException { 749 logger.debug("testing delegated authentication"); 750 final String targetPath = "/rest/foo"; 751 final String targetResource = ingestObj(targetPath); 752 753 ingestAcl("fedoraAdmin", "/acls/11/acl.ttl", targetResource + "/fcr:acl"); 754 755 final HttpGet adminGet = getObjMethod(targetPath); 756 setAuth(adminGet, "fedoraAdmin"); 757 assertEquals("admin can read object", HttpStatus.SC_OK, getStatus(adminGet)); 758 759 final HttpGet adminDelegatedGet = getObjMethod(targetPath); 760 setAuth(adminDelegatedGet, "fedoraAdmin"); 761 adminDelegatedGet.addHeader("On-Behalf-Of", "user11"); 762 assertEquals("delegated user can read object", HttpStatus.SC_OK, getStatus(adminDelegatedGet)); 763 764 final HttpGet adminUnauthorizedDelegatedGet = getObjMethod(targetPath); 765 setAuth(adminUnauthorizedDelegatedGet, "fedoraAdmin"); 766 adminUnauthorizedDelegatedGet.addHeader("On-Behalf-Of", "fakeuser"); 767 assertEquals("delegated fakeuser cannot read object", HttpStatus.SC_FORBIDDEN, 768 getStatus(adminUnauthorizedDelegatedGet)); 769 770 final HttpGet adminDelegatedGet2 = getObjMethod(targetPath); 771 setAuth(adminDelegatedGet2, "fedoraAdmin"); 772 adminDelegatedGet2.addHeader("On-Behalf-Of", "info:user/user2"); 773 assertEquals("delegated user can read object", HttpStatus.SC_OK, getStatus(adminDelegatedGet2)); 774 775 final HttpGet adminUnauthorizedDelegatedGet2 = getObjMethod(targetPath); 776 setAuth(adminUnauthorizedDelegatedGet2, "fedoraAdmin"); 777 adminUnauthorizedDelegatedGet2.addHeader("On-Behalf-Of", "info:user/fakeuser"); 778 assertEquals("delegated fakeuser cannot read object", HttpStatus.SC_FORBIDDEN, 779 getStatus(adminUnauthorizedDelegatedGet2)); 780 781 // Now test with the system property in effect 782 System.setProperty(USER_AGENT_BASE_URI_PROPERTY, "info:user/"); 783 System.setProperty(GROUP_AGENT_BASE_URI_PROPERTY, "info:group/"); 784 785 final HttpGet adminDelegatedGet3 = getObjMethod(targetPath); 786 setAuth(adminDelegatedGet3, "fedoraAdmin"); 787 adminDelegatedGet3.addHeader("On-Behalf-Of", "info:user/user2"); 788 assertEquals("delegated user can read object", HttpStatus.SC_OK, getStatus(adminDelegatedGet3)); 789 790 final HttpGet adminUnauthorizedDelegatedGet3 = getObjMethod(targetPath); 791 setAuth(adminUnauthorizedDelegatedGet3, "fedoraAdmin"); 792 adminUnauthorizedDelegatedGet3.addHeader("On-Behalf-Of", "info:user/fakeuser"); 793 assertEquals("delegated fakeuser cannot read object", HttpStatus.SC_FORBIDDEN, 794 getStatus(adminUnauthorizedDelegatedGet3)); 795 796 System.clearProperty(USER_AGENT_BASE_URI_PROPERTY); 797 System.clearProperty(GROUP_AGENT_BASE_URI_PROPERTY); 798 } 799 800 @Test 801 @Ignore ("Until implemented with Memento") 802 803 public void testAccessByUriToVersionedResources() throws IOException { 804 final String idVersion = "/rest/versionResourceUri"; 805 ingestObj(idVersion); 806 807 ingestAcl("fedoraAdmin", "/acls/12/acl.ttl", idVersion + "/fcr:acl"); 808 809 final HttpGet requestGet1 = getObjMethod(idVersion); 810 setAuth(requestGet1, "user12"); 811 assertEquals("testuser can't read object", HttpStatus.SC_OK, getStatus(requestGet1)); 812 813 final HttpPost requestPost1 = postObjMethod(idVersion + "/fcr:versions"); 814 requestPost1.addHeader("Slug", "v0"); 815 setAuth(requestPost1, "user12"); 816 assertEquals("Unable to create a new version", HttpStatus.SC_CREATED, getStatus(requestPost1)); 817 818 final HttpGet requestGet2 = getObjMethod(idVersion); 819 setAuth(requestGet2, "user12"); 820 assertEquals("testuser can't read versioned object", HttpStatus.SC_OK, getStatus(requestGet2)); 821 } 822 823 @Test 824 public void testAgentAsUri() throws IOException { 825 final String id = "/rest/" + getRandomUniqueId(); 826 final String testObj = ingestObj(id); 827 828 logger.debug("Anonymous can read (has ACL:READ): {}", id); 829 final HttpGet requestGet1 = getObjMethod(id); 830 assertEquals(HttpStatus.SC_OK, getStatus(requestGet1)); 831 832 logger.debug("Can username 'smith123' read {} (no ACL)", id); 833 final HttpGet requestGet2 = getObjMethod(id); 834 setAuth(requestGet2, "smith123"); 835 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 836 837 System.setProperty(USER_AGENT_BASE_URI_PROPERTY, "info:user/"); 838 System.setProperty(GROUP_AGENT_BASE_URI_PROPERTY, "info:group/"); 839 840 logger.debug("Can username 'smith123' read {} (overridden system ACL)", id); 841 final HttpGet requestGet3 = getObjMethod(id); 842 setAuth(requestGet3, "smith123"); 843 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 844 845 logger.debug("Can username 'group123' read {} (overridden system ACL)", id); 846 final HttpGet requestGet4 = getObjMethod(id); 847 setAuth(requestGet4, "group123"); 848 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 849 850 System.clearProperty(USER_AGENT_BASE_URI_PROPERTY); 851 System.clearProperty(GROUP_AGENT_BASE_URI_PROPERTY); 852 853 // Add ACL to object 854 ingestAcl("fedoraAdmin", "/acls/16/acl.ttl", testObj + "/fcr:acl"); 855 856 logger.debug("Anonymous still can't read (ACL present)"); 857 final HttpGet requestGet5 = getObjMethod(id); 858 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet5)); 859 860 logger.debug("Can username 'smith123' read {} (ACL present, no system properties)", testObj); 861 final HttpGet requestGet6 = getObjMethod(id); 862 setAuth(requestGet6, "smith123"); 863 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet6)); 864 865 System.setProperty(USER_AGENT_BASE_URI_PROPERTY, "info:user/"); 866 System.setProperty(GROUP_AGENT_BASE_URI_PROPERTY, "info:group/"); 867 868 logger.debug("Can username 'smith123' read {} (ACL, system properties present)", id); 869 final HttpGet requestGet7 = getObjMethod(id); 870 setAuth(requestGet7, "smith123"); 871 assertEquals(HttpStatus.SC_OK, getStatus(requestGet7)); 872 873 logger.debug("Can groupname 'group123' read {} (ACL, system properties present)", id); 874 final HttpGet requestGet8 = getObjMethod(id); 875 setAuth(requestGet8, "group123"); 876 assertEquals(HttpStatus.SC_OK, getStatus(requestGet8)); 877 878 System.clearProperty(USER_AGENT_BASE_URI_PROPERTY); 879 System.clearProperty(GROUP_AGENT_BASE_URI_PROPERTY); 880 } 881 882 @Test 883 public void testRegisterNamespace() throws IOException { 884 final String testObj = ingestObj("/rest/test_namespace"); 885 ingestAcl("fedoraAdmin", "/acls/13/acl.ttl", testObj + "/fcr:acl"); 886 887 final String id = "/rest/test_namespace/" + getRandomUniqueId(); 888 ingestObj(id); 889 890 final HttpPatch patchReq = patchObjMethod(id); 891 setAuth(patchReq, "user13"); 892 patchReq.addHeader("Content-type", "application/sparql-update"); 893 patchReq.setEntity(new StringEntity("PREFIX novel: <info://" + getRandomUniqueId() + ">\n" 894 + "INSERT DATA { <> novel:value 'test' }")); 895 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 896 } 897 898 @Test 899 public void testRegisterNodeType() throws IOException { 900 final String testObj = ingestObj("/rest/test_nodetype"); 901 ingestAcl("fedoraAdmin", "/acls/14/acl.ttl", testObj + "/fcr:acl"); 902 903 final String id = "/rest/test_nodetype/" + getRandomUniqueId(); 904 ingestObj(id); 905 906 final HttpPatch patchReq = patchObjMethod(id); 907 setAuth(patchReq, "user14"); 908 patchReq.addHeader("Content-type", "application/sparql-update"); 909 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 910 + "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" 911 + "INSERT DATA { <> rdf:type dc:type }")); 912 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 913 } 914 915 916 @Test 917 public void testDeletePropertyAsUser() throws IOException { 918 final String testObj = ingestObj("/rest/test_delete"); 919 ingestAcl("fedoraAdmin", "/acls/15/acl.ttl", testObj + "/fcr:acl"); 920 921 final String id = "/rest/test_delete/" + getRandomUniqueId(); 922 ingestObj(id); 923 924 HttpPatch patchReq = patchObjMethod(id); 925 setAuth(patchReq, "user15"); 926 patchReq.addHeader("Content-type", "application/sparql-update"); 927 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 928 + "INSERT DATA { <> dc:title 'title' . " + 929 " <> dc:rights 'rights' . }")); 930 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 931 932 patchReq = patchObjMethod(id); 933 setAuth(patchReq, "user15"); 934 patchReq.addHeader("Content-type", "application/sparql-update"); 935 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 936 + "DELETE { <> dc:title ?any . } WHERE { <> dc:title ?any . }")); 937 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 938 939 patchReq = patchObjMethod(id); 940 setAuth(patchReq, "notUser15"); 941 patchReq.addHeader("Content-type", "application/sparql-update"); 942 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 943 + "DELETE { <> dc:rights ?any . } WHERE { <> dc:rights ?any . }")); 944 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchReq)); 945 } 946 947 @Test 948 public void testHeadWithReadOnlyUser() throws IOException { 949 final String testObj = ingestObj("/rest/test_head"); 950 ingestAcl("fedoraAdmin", "/acls/19/acl.ttl", testObj + "/fcr:acl"); 951 952 final HttpHead headReq = new HttpHead(testObj); 953 setAuth(headReq, "user19"); 954 assertEquals(HttpStatus.SC_OK, getStatus(headReq)); 955 } 956 957 @Test 958 public void testOptionsWithReadOnlyUser() throws IOException { 959 final String testObj = ingestObj("/rest/test_options"); 960 ingestAcl("fedoraAdmin", "/acls/20/acl.ttl", testObj + "/fcr:acl"); 961 962 final HttpOptions optionsReq = new HttpOptions(testObj); 963 setAuth(optionsReq, "user20"); 964 assertEquals(HttpStatus.SC_OK, getStatus(optionsReq)); 965 } 966 967 private static HttpResponse HEAD(final String requestURI) throws IOException { 968 return HEAD(requestURI, "fedoraAdmin"); 969 } 970 971 private static HttpResponse HEAD(final String requestURI, final String username) throws IOException { 972 final HttpHead req = new HttpHead(requestURI); 973 setAuth(req, username); 974 return execute(req); 975 } 976 977 private static HttpResponse PUT(final String requestURI) throws IOException { 978 return PUT(requestURI, "fedoraAdmin"); 979 } 980 981 private static HttpResponse PUT(final String requestURI, final String username) throws IOException { 982 final HttpPut req = new HttpPut(requestURI); 983 setAuth(req, username); 984 return execute(req); 985 } 986 987 private static HttpResponse DELETE(final String requestURI, final String username) throws IOException { 988 final HttpDelete req = new HttpDelete(requestURI); 989 setAuth(req, username); 990 return execute(req); 991 } 992 993 private static HttpResponse GET(final String requestURI, final String username) throws IOException { 994 final HttpGet req = new HttpGet(requestURI); 995 setAuth(req, username); 996 return execute(req); 997 } 998 999 private static HttpResponse PATCH(final String requestURI, final HttpEntity body, final String username) 1000 throws IOException { 1001 final HttpPatch req = new HttpPatch(requestURI); 1002 setAuth(req, username); 1003 if (body != null) { 1004 req.setEntity(body); 1005 } 1006 return execute(req); 1007 } 1008 1009 private static String getLink(final HttpResponse res) { 1010 for (final Header h : res.getHeaders("Link")) { 1011 final HeaderElement link = h.getElements()[0]; 1012 for (final NameValuePair param : link.getParameters()) { 1013 if (param.getName().equals("rel") && param.getValue().equals("acl")) { 1014 return link.getName().replaceAll("^<|>$", ""); 1015 } 1016 } 1017 } 1018 return null; 1019 } 1020 1021 private String ingestObjWithACL(final String path, final String aclResourcePath) throws IOException { 1022 final String newURI = ingestObj(path); 1023 final HttpResponse res = HEAD(newURI); 1024 final String aclURI = getLink(res); 1025 1026 logger.debug("Creating ACL at {}", aclURI); 1027 ingestAcl("fedoraAdmin", aclResourcePath, aclURI); 1028 1029 return newURI; 1030 } 1031 1032 @Test 1033 public void testControl() throws IOException { 1034 final String controlObj = ingestObjWithACL("/rest/control", "/acls/25/control.ttl"); 1035 final String readwriteObj = ingestObjWithACL("/rest/readwrite", "/acls/25/readwrite.ttl"); 1036 1037 final String rwChildACL = getLink(PUT(readwriteObj + "/child")); 1038 assertEquals(SC_FORBIDDEN, getStatus(HEAD(rwChildACL, "testuser"))); 1039 assertEquals(SC_FORBIDDEN, getStatus(GET(rwChildACL, "testuser"))); 1040 assertEquals(SC_FORBIDDEN, getStatus(PUT(rwChildACL, "testuser"))); 1041 assertEquals(SC_FORBIDDEN, getStatus(DELETE(rwChildACL, "testuser"))); 1042 1043 final String controlChildACL = getLink(PUT(controlObj + "/child")); 1044 assertEquals(SC_NOT_FOUND, getStatus(HEAD(controlChildACL, "testuser"))); 1045 assertEquals(SC_NOT_FOUND, getStatus(GET(controlChildACL, "testuser"))); 1046 1047 ingestAcl("testuser", "/acls/25/child-control.ttl", controlChildACL); 1048 final StringEntity sparqlUpdate = new StringEntity( 1049 "PREFIX acl: <http://www.w3.org/ns/auth/acl#> INSERT { <#restricted> acl:mode acl:Read } WHERE { }", 1050 ContentType.create("application/sparql-update")); 1051 assertEquals(SC_NO_CONTENT, getStatus(PATCH(controlChildACL, sparqlUpdate, "testuser"))); 1052 1053 assertEquals(SC_NO_CONTENT, getStatus(DELETE(controlChildACL, "testuser"))); 1054 } 1055 1056 @Test 1057 public void testAppendOnlyToContainer() throws IOException { 1058 final String testObj = ingestObj("/rest/test_append"); 1059 ingestAcl("fedoraAdmin", "/acls/23/acl.ttl", testObj + "/fcr:acl"); 1060 final String username = "user23"; 1061 1062 final HttpOptions optionsReq = new HttpOptions(testObj); 1063 setAuth(optionsReq, username); 1064 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(optionsReq)); 1065 1066 final HttpHead headReq = new HttpHead(testObj); 1067 setAuth(headReq, username); 1068 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(headReq)); 1069 1070 final HttpGet getReq = new HttpGet(testObj); 1071 setAuth(getReq, username); 1072 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(getReq)); 1073 1074 final HttpPut putReq = new HttpPut(testObj); 1075 setAuth(putReq, username); 1076 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putReq)); 1077 1078 final HttpDelete deleteReq = new HttpDelete(testObj); 1079 setAuth(deleteReq, username); 1080 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteReq)); 1081 1082 final HttpPost postReq = new HttpPost(testObj); 1083 setAuth(postReq, username); 1084 assertEquals(HttpStatus.SC_CREATED, getStatus(postReq)); 1085 1086 final String[] legalSPARQLQueries = new String[] { 1087 "INSERT DATA { <> <http://purl.org/dc/terms/title> \"Test23\" . }", 1088 "INSERT { <> <http://purl.org/dc/terms/alternative> \"Test XXIII\" . } WHERE {}", 1089 "DELETE {} INSERT { <> <http://purl.org/dc/terms/description> \"Test append only\" . } WHERE {}" 1090 }; 1091 for (final String query : legalSPARQLQueries) { 1092 final HttpPatch patchReq = new HttpPatch(testObj); 1093 setAuth(patchReq, username); 1094 patchReq.setEntity(new StringEntity(query)); 1095 patchReq.setHeader("Content-Type", "application/sparql-update"); 1096 logger.debug("Testing SPARQL update: {}", query); 1097 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 1098 } 1099 1100 final String[] illegalSPARQLQueries = new String[] { 1101 "DELETE DATA { <> <http://purl.org/dc/terms/title> \"Test23\" . }", 1102 "DELETE { <> <http://purl.org/dc/terms/alternative> \"Test XXIII\" . } WHERE {}", 1103 "DELETE { <> <http://purl.org/dc/terms/description> \"Test append only\" . } INSERT {} WHERE {}" 1104 }; 1105 for (final String query : illegalSPARQLQueries) { 1106 final HttpPatch patchReq = new HttpPatch(testObj); 1107 setAuth(patchReq, username); 1108 patchReq.setEntity(new StringEntity(query)); 1109 patchReq.setHeader("Content-Type", "application/sparql-update"); 1110 logger.debug("Testing SPARQL update: {}", query); 1111 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchReq)); 1112 } 1113 final String[] allowedDeleteSPARQLQueries = new String[] { 1114 "DELETE DATA {}", 1115 "DELETE { } WHERE {}", 1116 "DELETE { } INSERT {} WHERE {}" 1117 }; 1118 for (final String query : allowedDeleteSPARQLQueries) { 1119 final HttpPatch patchReq = new HttpPatch(testObj); 1120 setAuth(patchReq, username); 1121 patchReq.setEntity(new StringEntity(query)); 1122 patchReq.setHeader("Content-Type", "application/sparql-update"); 1123 logger.debug("Testing SPARQL update: {}", query); 1124 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 1125 } 1126 1127 } 1128 1129 @Test 1130 public void testAppendOnlyToBinary() throws IOException { 1131 final String testObj = ingestBinary("/rest/test_append_binary", new StringEntity("foo")); 1132 ingestAcl("fedoraAdmin", "/acls/24/acl.ttl", testObj + "/fcr:acl"); 1133 final String username = "user24"; 1134 1135 final HttpOptions optionsReq = new HttpOptions(testObj); 1136 setAuth(optionsReq, username); 1137 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(optionsReq)); 1138 1139 final HttpHead headReq = new HttpHead(testObj); 1140 setAuth(headReq, username); 1141 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(headReq)); 1142 1143 final HttpGet getReq = new HttpGet(testObj); 1144 setAuth(getReq, username); 1145 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(getReq)); 1146 1147 final HttpPut putReq = new HttpPut(testObj); 1148 setAuth(putReq, username); 1149 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putReq)); 1150 1151 final HttpDelete deleteReq = new HttpDelete(testObj); 1152 setAuth(deleteReq, username); 1153 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteReq)); 1154 1155 final HttpPost postReq = new HttpPost(testObj); 1156 setAuth(postReq, username); 1157 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postReq)); 1158 } 1159 1160 @Test 1161 public void testFoafAgent() throws IOException { 1162 final String path = ingestObj("/rest/foaf-agent"); 1163 ingestAcl("fedoraAdmin", "/acls/26/foaf-agent.ttl", path + "/fcr:acl"); 1164 final String username = "user1"; 1165 1166 final HttpGet req = new HttpGet(path); 1167 1168 //NB: Actually no authentication headers should be set for this test 1169 //since the point of foaf:Agent is to allow unauthenticated access for everyone. 1170 //However at this time the test integration test server requires callers to 1171 //authenticate. 1172 setAuth(req, username); 1173 1174 assertEquals(HttpStatus.SC_OK, getStatus(req)); 1175 } 1176 1177 @Test 1178 public void testAuthenticatedAgent() throws IOException { 1179 final String path = ingestObj("/rest/authenticated-agent"); 1180 ingestAcl("fedoraAdmin", "/acls/26/authenticated-agent.ttl", path + "/fcr:acl"); 1181 final String username = "user1"; 1182 1183 final HttpGet darkReq = new HttpGet(path); 1184 setAuth(darkReq, username); 1185 assertEquals(HttpStatus.SC_OK, getStatus(darkReq)); 1186 } 1187 1188 @Test 1189 public void testAgentGroupWithHashUris() throws Exception { 1190 ingestTurtleResource("fedoraAdmin", "/acls/agent-group-list.ttl", 1191 serverAddress + "/rest/agent-group-list"); 1192 //check that the authorized are authorized. 1193 final String authorized = ingestObj("/rest/agent-group-with-hash-uri-authorized"); 1194 ingestAcl("fedoraAdmin", "/acls/agent-group-with-hash-uri-authorized.ttl", authorized + "/fcr:acl"); 1195 1196 final HttpGet getAuthorized = new HttpGet(authorized); 1197 setAuth(getAuthorized, "testuser"); 1198 assertEquals(HttpStatus.SC_OK, getStatus(getAuthorized)); 1199 1200 //check that the unauthorized are unauthorized. 1201 final String unauthorized = ingestObj("/rest/agent-group-with-hash-uri-unauthorized"); 1202 ingestAcl("fedoraAdmin", "/acls/agent-group-with-hash-uri-unauthorized.ttl", unauthorized + "/fcr:acl"); 1203 1204 final HttpGet getUnauthorized = new HttpGet(unauthorized); 1205 setAuth(getUnauthorized, "testuser"); 1206 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(getUnauthorized)); 1207 } 1208 1209 @Test 1210 public void testAgentGroupWithMembersAsURIs() throws Exception { 1211 System.setProperty(USER_AGENT_BASE_URI_PROPERTY, "http://example.com/"); 1212 ingestTurtleResource("fedoraAdmin", "/acls/agent-group-list-with-member-uris.ttl", 1213 serverAddress + "/rest/agent-group-list-with-member-uris"); 1214 final String authorized = ingestObj("/rest/agent-group-with-vcard-member-as-uri"); 1215 ingestAcl("fedoraAdmin", "/acls/agent-group-with-vcard-member-as-uri.ttl", authorized + "/fcr:acl"); 1216 //check that test user is authorized to write 1217 final HttpPut childPut = new HttpPut(authorized + "/child"); 1218 setAuth(childPut, "testuser"); 1219 assertEquals(HttpStatus.SC_CREATED, getStatus(childPut)); 1220 } 1221 1222 @Test 1223 public void testAgentGroup() throws Exception { 1224 ingestTurtleResource("fedoraAdmin", "/acls/agent-group-list-flat.ttl", 1225 serverAddress + "/rest/agent-group-list-flat"); 1226 //check that the authorized are authorized. 1227 final String flat = ingestObj("/rest/agent-group-flat"); 1228 ingestAcl("fedoraAdmin", "/acls/agent-group-flat.ttl", flat + "/fcr:acl"); 1229 1230 final HttpGet getFlat = new HttpGet(flat); 1231 setAuth(getFlat, "testuser"); 1232 assertEquals(HttpStatus.SC_OK, getStatus(getFlat)); 1233 } 1234 1235 @Test 1236 public void testAclAppendPermissions() throws Exception { 1237 final String testObj = ingestBinary("/rest/test-read-append", new StringEntity("foo")); 1238 ingestAcl("fedoraAdmin", "/acls/27/read-append.ttl", testObj + "/fcr:acl"); 1239 final String username = "user27"; 1240 1241 final HttpOptions optionsReq = new HttpOptions(testObj); 1242 setAuth(optionsReq, username); 1243 assertEquals(HttpStatus.SC_OK, getStatus(optionsReq)); 1244 1245 final HttpHead headReq = new HttpHead(testObj); 1246 setAuth(headReq, username); 1247 assertEquals(HttpStatus.SC_OK, getStatus(headReq)); 1248 1249 final HttpGet getReq = new HttpGet(testObj); 1250 setAuth(getReq, username); 1251 final String descriptionUri; 1252 try (final CloseableHttpResponse response = execute(getReq)) { 1253 assertEquals(HttpStatus.SC_OK, getStatus(response)); 1254 descriptionUri = Arrays.stream(response.getHeaders("Link")) 1255 .flatMap(header -> Arrays.stream(header.getValue().split(","))).map(linkStr -> Link.valueOf( 1256 linkStr)) 1257 .filter(link -> link.getRels().contains("describedby")).map(link -> link.getUri().toString()) 1258 .findFirst().orElse(null); 1259 } 1260 1261 1262 final HttpPut putReq = new HttpPut(testObj); 1263 setAuth(putReq, username); 1264 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putReq)); 1265 1266 final HttpDelete deleteReq = new HttpDelete(testObj); 1267 setAuth(deleteReq, username); 1268 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteReq)); 1269 1270 final HttpPost postReq = new HttpPost(testObj); 1271 setAuth(postReq, username); 1272 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postReq)); 1273 1274 if (descriptionUri != null) { 1275 final HttpOptions optionsDescReq = new HttpOptions(descriptionUri); 1276 setAuth(optionsDescReq, username); 1277 assertEquals(HttpStatus.SC_OK, getStatus(optionsDescReq)); 1278 1279 final HttpHead headDescReq = new HttpHead(descriptionUri); 1280 setAuth(headDescReq, username); 1281 assertEquals(HttpStatus.SC_OK, getStatus(headDescReq)); 1282 1283 final HttpGet getDescReq = new HttpGet(descriptionUri); 1284 setAuth(getDescReq, username); 1285 assertEquals(HttpStatus.SC_OK, getStatus(getDescReq)); 1286 1287 final HttpPut putDescReq = new HttpPut(descriptionUri); 1288 setAuth(putDescReq, username); 1289 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putDescReq)); 1290 1291 final HttpDelete deleteDescReq = new HttpDelete(descriptionUri); 1292 setAuth(deleteDescReq, username); 1293 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteDescReq)); 1294 1295 final HttpPost postDescReq = new HttpPost(descriptionUri); 1296 setAuth(postDescReq, username); 1297 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postDescReq)); 1298 } 1299 } 1300 1301 @Test 1302 public void testCreateAclWithAccessToClassForBinary() throws Exception { 1303 final String id = getRandomUniqueId(); 1304 final String subjectUri = serverAddress + id; 1305 ingestObj(subjectUri); 1306 ingestAcl("fedoraAdmin", "/acls/agent-access-to-class.ttl", subjectUri + "/fcr:acl"); 1307 1308 final String binaryUri = ingestBinary("/rest/" + id + "/binary", new StringEntity("foo")); 1309 1310 final HttpHead headBinary = new HttpHead(binaryUri); 1311 setAuth(headBinary, "testuser"); 1312 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(headBinary)); 1313 1314 final HttpHead headDesc = new HttpHead(binaryUri + "/fcr:metadata"); 1315 setAuth(headDesc, "testuser"); 1316 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(headDesc)); 1317 1318 // Add type to binary 1319 final HttpPatch requestPatch = patchObjMethod(id + "/binary/fcr:metadata"); 1320 setAuth(requestPatch, "fedoraAdmin"); 1321 final String sparql = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \n" + 1322 "PREFIX foaf: <http://xmlns.com/foaf/0.1/> \n" + 1323 "INSERT { <> rdf:type foaf:Document } WHERE {}"; 1324 requestPatch.setEntity(new StringEntity(sparql)); 1325 requestPatch.setHeader("Content-type", "application/sparql-update"); 1326 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 1327 1328 final HttpHead headBinary2 = new HttpHead(binaryUri); 1329 setAuth(headBinary2, "testuser"); 1330 assertEquals(HttpStatus.SC_OK, getStatus(headBinary2)); 1331 1332 final HttpHead headDesc2 = new HttpHead(binaryUri + "/fcr:metadata"); 1333 setAuth(headDesc2, "testuser"); 1334 assertEquals(HttpStatus.SC_OK, getStatus(headDesc2)); 1335 } 1336 1337 @Test 1338 public void testIndirectRelationshipForbidden() throws IOException { 1339 final String targetResource = "/rest/" + getRandomUniqueId(); 1340 final String writeableResource = "/rest/" + getRandomUniqueId(); 1341 final String username = "user28"; 1342 1343 final String targetUri = ingestObj(targetResource); 1344 1345 final String readonlyString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1346 "<#readauthz> a acl:Authorization ;\n" + 1347 " acl:agent \"" + username + "\" ;\n" + 1348 " acl:mode acl:Read ;\n" + 1349 " acl:accessTo <" + targetResource + "> ."; 1350 ingestAclString(targetUri, readonlyString, "fedoraAdmin"); 1351 1352 // User can read target resource. 1353 final HttpGet get1 = getObjMethod(targetResource); 1354 setAuth(get1, username); 1355 assertEquals(HttpStatus.SC_OK, getStatus(get1)); 1356 1357 // User can't patch target resource. 1358 final String patch = "INSERT DATA { <> <http://purl.org/dc/elements/1.1/title> \"Changed it\"}"; 1359 final HttpEntity patchEntity = new StringEntity(patch, sparqlContentType); 1360 try (final CloseableHttpResponse resp = (CloseableHttpResponse) PATCH(targetUri, patchEntity, 1361 username)) { 1362 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(resp)); 1363 } 1364 1365 // Make a user writable container. 1366 final String writeableUri = ingestObj(writeableResource); 1367 final String writeableAcl = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1368 "<#writeauth> a acl:Authorization ;\n" + 1369 " acl:agent \"" + username + "\" ;\n" + 1370 " acl:mode acl:Read, acl:Write ;\n" + 1371 " acl:accessTo <" + writeableResource + "> ;\n" + 1372 " acl:default <" + writeableResource + "> ."; 1373 ingestAclString(writeableUri, writeableAcl, "fedoraAdmin"); 1374 1375 // Ensure we can still POST/PUT to writeable resource. 1376 testCanWrite(writeableResource, username); 1377 1378 // Try to create indirect container referencing readonly resource with POST. 1379 final HttpPost userPost = postObjMethod(writeableResource); 1380 setAuth(userPost, username); 1381 userPost.addHeader("Link", "<" + INDIRECT_CONTAINER.toString() + ">; rel=type"); 1382 final String indirect = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1383 "@prefix example: <http://www.example.org/example1#> .\n" + 1384 "@prefix dc: <http://purl.org/dc/elements/1.1/> .\n" + 1385 "<> ldp:insertedContentRelation <http://example.org/test#something> ;\n" + 1386 "ldp:membershipResource <" + targetResource + "> ;\n" + 1387 "ldp:hasMemberRelation <http://example.org/test#predicateToCreate> ;\n" + 1388 "dc:title \"The indirect container\" ."; 1389 final HttpEntity indirectEntity = new StringEntity(indirect, turtleContentType); 1390 userPost.setEntity(indirectEntity); 1391 userPost.setHeader(CONTENT_TYPE, "text/turtle"); 1392 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(userPost)); 1393 1394 // Try to create indirect container referencing readonly resource with PUT. 1395 final String indirectString = getRandomUniqueId(); 1396 final HttpPut userPut = putObjMethod(writeableResource + "/" + indirectString); 1397 setAuth(userPut, username); 1398 userPut.addHeader("Link", "<" + INDIRECT_CONTAINER.toString() + ">; rel=type"); 1399 userPut.setEntity(indirectEntity); 1400 userPut.setHeader(CONTENT_TYPE, "text/turtle"); 1401 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(userPut)); 1402 1403 // Create an user writeable resource. 1404 final HttpPost targetPost = postObjMethod(writeableResource); 1405 setAuth(targetPost, username); 1406 final String tempTarget; 1407 try (final CloseableHttpResponse resp = execute(targetPost)) { 1408 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1409 tempTarget = getLocation(resp); 1410 } 1411 1412 // Try to create indirect container referencing an available resource. 1413 final String indirect_ok = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1414 "@prefix example: <http://www.example.org/example1#> .\n" + 1415 "@prefix dc: <http://purl.org/dc/elements/1.1/> .\n" + 1416 "<> ldp:insertedContentRelation <http://example.org/test#something> ;\n" + 1417 "ldp:membershipResource <" + tempTarget + "> ;\n" + 1418 "ldp:hasMemberRelation <http://example.org/test#predicateToCreate> ;\n" + 1419 "dc:title \"The indirect container\" ."; 1420 final HttpPost userPatchPost = postObjMethod(writeableResource); 1421 setAuth(userPatchPost, username); 1422 userPatchPost.addHeader("Link", "<" + INDIRECT_CONTAINER.toString() + ">; rel=type"); 1423 final HttpEntity in_ok = new StringEntity(indirect_ok, turtleContentType); 1424 userPatchPost.setEntity(in_ok); 1425 userPatchPost.setHeader(CONTENT_TYPE, "text/turtle"); 1426 final String indirectUri; 1427 try (final CloseableHttpResponse resp = execute(userPatchPost)) { 1428 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1429 indirectUri = getLocation(resp); 1430 } 1431 1432 // Then PATCH to the readonly resource. 1433 final HttpPatch patchIndirect = new HttpPatch(indirectUri); 1434 setAuth(patchIndirect, username); 1435 final String patch_text = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1436 "DELETE { <> ldp:membershipResource ?o } \n" + 1437 "INSERT { <> ldp:membershipResource <" + targetResource + "> } \n" + 1438 "WHERE { <> ldp:membershipResource ?o }"; 1439 patchIndirect.setEntity(new StringEntity(patch_text, sparqlContentType)); 1440 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchIndirect)); 1441 1442 // Delete the ldp:membershipRelation and add it with INSERT DATA {} 1443 final String patch_delete_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1444 "DELETE DATA { <> ldp:membershipResource <" + tempTarget + "> }"; 1445 final HttpPatch patchIndirect2 = new HttpPatch(indirectUri); 1446 setAuth(patchIndirect2, username); 1447 patchIndirect2.setEntity(new StringEntity(patch_delete_relation, sparqlContentType)); 1448 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchIndirect2)); 1449 1450 final String patch_insert_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1451 "INSERT DATA { <> ldp:membershipResource <" + targetResource + "> }"; 1452 final HttpPatch patchIndirect3 = new HttpPatch(indirectUri); 1453 setAuth(patchIndirect3, username); 1454 patchIndirect3.setEntity(new StringEntity(patch_insert_relation, sparqlContentType)); 1455 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchIndirect3)); 1456 1457 // Patch the indirect to the readonly target as admin 1458 final HttpPatch patchAsAdmin = new HttpPatch(indirectUri); 1459 setAuth(patchAsAdmin, "fedoraAdmin"); 1460 patchAsAdmin.setEntity(new StringEntity(patch_insert_relation, sparqlContentType)); 1461 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchAsAdmin)); 1462 1463 // Try to POST a child as user 1464 final HttpPost postChild = new HttpPost(indirectUri); 1465 final String postTarget = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1466 "@prefix test: <http://example.org/test#> .\n\n" + 1467 "<> test:something <" + tempTarget + "> ."; 1468 final HttpEntity putPostChild = new StringEntity(postTarget, turtleContentType); 1469 setAuth(postChild, username); 1470 postChild.setEntity(putPostChild); 1471 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postChild)); 1472 1473 // Try to PUT a child as user 1474 final String id = getRandomUniqueId(); 1475 final HttpPut putChild = new HttpPut(indirectUri + "/" + id); 1476 setAuth(putChild, username); 1477 putChild.setEntity(putPostChild); 1478 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putChild)); 1479 1480 // Put the child as Admin 1481 setAuth(putChild, "fedoraAdmin"); 1482 assertEquals(HttpStatus.SC_CREATED, getStatus(putChild)); 1483 1484 // Try to delete the child as user 1485 final HttpDelete deleteChild = new HttpDelete(indirectUri + "/" + id); 1486 setAuth(deleteChild, username); 1487 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteChild)); 1488 1489 // Try to delete the indirect container 1490 final HttpDelete deleteIndirect = new HttpDelete(indirectUri); 1491 setAuth(deleteIndirect, username); 1492 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteIndirect)); 1493 1494 // Ensure we can still write to the writeable resource. 1495 testCanWrite(writeableResource, username); 1496 1497 } 1498 1499 @Test 1500 public void testIndirectRelationshipOK() throws IOException { 1501 final String targetResource = "/rest/" + getRandomUniqueId(); 1502 final String writeableResource = "/rest/" + getRandomUniqueId(); 1503 final String username = "user28"; 1504 1505 final String targetUri = ingestObj(targetResource); 1506 1507 final String readwriteString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1508 "<#readauthz> a acl:Authorization ;\n" + 1509 " acl:agent \"" + username + "\" ;\n" + 1510 " acl:mode acl:Read, acl:Write ;\n" + 1511 " acl:accessTo <" + targetResource + "> ."; 1512 ingestAclString(targetUri, readwriteString, "fedoraAdmin"); 1513 1514 // User can read target resource. 1515 final HttpGet get1 = getObjMethod(targetResource); 1516 setAuth(get1, username); 1517 assertEquals(HttpStatus.SC_OK, getStatus(get1)); 1518 1519 // User can patch target resource. 1520 final String patch = "INSERT DATA { <> <http://purl.org/dc/elements/1.1/title> \"Changed it\"}"; 1521 final HttpEntity patchEntity = new StringEntity(patch, sparqlContentType); 1522 try (final CloseableHttpResponse resp = (CloseableHttpResponse) PATCH(targetUri, patchEntity, 1523 username)) { 1524 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(resp)); 1525 } 1526 1527 // Make a user writable container. 1528 final String writeableUri = ingestObj(writeableResource); 1529 final String writeableAcl = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1530 "<#writeauth> a acl:Authorization ;\n" + 1531 " acl:agent \"" + username + "\" ;\n" + 1532 " acl:mode acl:Read, acl:Write ;\n" + 1533 " acl:accessTo <" + writeableResource + "> ;\n" + 1534 " acl:default <" + writeableResource + "> ."; 1535 ingestAclString(writeableUri, writeableAcl, "fedoraAdmin"); 1536 1537 // Ensure we can write to the writeable resource. 1538 testCanWrite(writeableResource, username); 1539 1540 // Try to create indirect container referencing writeable resource with POST. 1541 final HttpPost userPost = postObjMethod(writeableResource); 1542 setAuth(userPost, username); 1543 userPost.addHeader("Link", "<" + INDIRECT_CONTAINER.toString() + ">; rel=type"); 1544 final String indirect = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1545 "@prefix test: <http://example.org/test#> .\n\n" + 1546 "<> ldp:insertedContentRelation test:something ;" + 1547 "ldp:membershipResource <" + targetResource + "> ;" + 1548 "ldp:hasMemberRelation test:predicateToCreate ."; 1549 final HttpEntity indirectEntity = new StringEntity(indirect, turtleContentType); 1550 userPost.setEntity(new StringEntity(indirect, turtleContentType)); 1551 userPost.setHeader("Content-type", "text/turtle"); 1552 assertEquals(HttpStatus.SC_CREATED, getStatus(userPost)); 1553 1554 // Try to create indirect container referencing writeable resource with PUT. 1555 final String indirectString = getRandomUniqueId(); 1556 final HttpPut userPut = putObjMethod(writeableResource + "/" + indirectString); 1557 setAuth(userPut, username); 1558 userPut.addHeader("Link", "<" + INDIRECT_CONTAINER.toString() + ">; rel=type"); 1559 userPut.setEntity(indirectEntity); 1560 userPut.setHeader("Content-type", "text/turtle"); 1561 assertEquals(HttpStatus.SC_CREATED, getStatus(userPut)); 1562 1563 // Create an user writeable resource. 1564 final HttpPost targetPost = postObjMethod(writeableResource); 1565 setAuth(targetPost, username); 1566 final String tempTarget; 1567 try (final CloseableHttpResponse resp = execute(targetPost)) { 1568 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1569 tempTarget = getLocation(resp); 1570 } 1571 1572 // Try to create indirect container referencing an available resource. 1573 final String indirect_ok = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1574 "@prefix test: <http://example.org/test#> .\n\n" + 1575 "<> ldp:insertedContentRelation test:something ;" + 1576 "ldp:membershipResource <" + tempTarget + "> ;" + 1577 "ldp:hasMemberRelation test:predicateToCreate ."; 1578 final HttpPost userPatchPost = postObjMethod(writeableResource); 1579 setAuth(userPatchPost, username); 1580 userPatchPost.addHeader("Link", "<" + INDIRECT_CONTAINER.toString() + ">; rel=type"); 1581 userPatchPost.setEntity(new StringEntity(indirect_ok, turtleContentType)); 1582 userPatchPost.setHeader("Content-type", "text/turtle"); 1583 final String indirectUri; 1584 try (final CloseableHttpResponse resp = execute(userPatchPost)) { 1585 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1586 indirectUri = getLocation(resp); 1587 } 1588 1589 // Then PATCH to the writeable resource. 1590 final HttpPatch patchIndirect = new HttpPatch(indirectUri); 1591 setAuth(patchIndirect, username); 1592 final String patch_text = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1593 "DELETE { <> ldp:membershipResource ?o } \n" + 1594 "INSERT { <> ldp:membershipResource <" + targetResource + "> } \n" + 1595 "WHERE { <> ldp:membershipResource ?o }"; 1596 patchIndirect.setEntity(new StringEntity(patch_text, sparqlContentType)); 1597 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchIndirect)); 1598 1599 // Delete the ldp:membershipRelation and add it with INSERT DATA {} 1600 final String patch_delete_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1601 "DELETE DATA { <> ldp:membershipResource <" + targetResource + "> }"; 1602 final HttpPatch patchIndirect2 = new HttpPatch(indirectUri); 1603 setAuth(patchIndirect2, username); 1604 patchIndirect2.setEntity(new StringEntity(patch_delete_relation, sparqlContentType)); 1605 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchIndirect2)); 1606 1607 final String patch_insert_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1608 "INSERT DATA { <> ldp:membershipResource <" + targetResource + "> }"; 1609 final HttpPatch patchIndirect3 = new HttpPatch(indirectUri); 1610 setAuth(patchIndirect3, username); 1611 patchIndirect3.setEntity(new StringEntity(patch_insert_relation, sparqlContentType)); 1612 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchIndirect3)); 1613 1614 // Ensure we can still write to the writeable resource. 1615 testCanWrite(writeableResource, username); 1616 1617 } 1618 1619 @Test 1620 public void testDirectRelationshipForbidden() throws IOException { 1621 final String targetResource = "/rest/" + getRandomUniqueId(); 1622 final String writeableResource = "/rest/" + getRandomUniqueId(); 1623 final String username = "user28"; 1624 1625 final String targetUri = ingestObj(targetResource); 1626 1627 final String readonlyString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1628 "<#readauthz> a acl:Authorization ;\n" + 1629 " acl:agent \"" + username + "\" ;\n" + 1630 " acl:mode acl:Read ;\n" + 1631 " acl:accessTo <" + targetResource + "> ."; 1632 ingestAclString(targetUri, readonlyString, "fedoraAdmin"); 1633 1634 // User can read target resource. 1635 final HttpGet get1 = getObjMethod(targetResource); 1636 setAuth(get1, username); 1637 assertEquals(HttpStatus.SC_OK, getStatus(get1)); 1638 1639 // User can't patch target resource. 1640 final String patch = "INSERT DATA { <> <http://purl.org/dc/elements/1.1/title> \"Changed it\"}"; 1641 final HttpEntity patchEntity = new StringEntity(patch, sparqlContentType); 1642 try (final CloseableHttpResponse resp = (CloseableHttpResponse) PATCH(targetUri, patchEntity, 1643 username)) { 1644 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(resp)); 1645 } 1646 1647 // Make a user writable container. 1648 final String writeableUri = ingestObj(writeableResource); 1649 final String writeableAcl = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1650 "<#writeauth> a acl:Authorization ;\n" + 1651 " acl:agent \"" + username + "\" ;\n" + 1652 " acl:mode acl:Read, acl:Write ;\n" + 1653 " acl:accessTo <" + writeableResource + "> ;\n" + 1654 " acl:default <" + writeableResource + "> ."; 1655 ingestAclString(writeableUri, writeableAcl, "fedoraAdmin"); 1656 1657 // Ensure we can write to writeable resource. 1658 testCanWrite(writeableResource, username); 1659 1660 // Try to create direct container referencing readonly resource with POST. 1661 final HttpPost userPost = postObjMethod(writeableResource); 1662 setAuth(userPost, username); 1663 userPost.addHeader("Link", "<" + DIRECT_CONTAINER.toString() + ">; rel=type"); 1664 final String direct = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1665 "@prefix test: <http://example.org/test#> .\n\n" + 1666 "<> ldp:membershipResource <" + targetResource + "> ;" + 1667 "ldp:hasMemberRelation test:predicateToCreate ."; 1668 final HttpEntity directEntity = new StringEntity(direct, turtleContentType); 1669 userPost.setEntity(directEntity); 1670 userPost.setHeader("Content-type", "text/turtle"); 1671 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(userPost)); 1672 1673 // Try to create direct container referencing readonly resource with PUT. 1674 final String indirectString = getRandomUniqueId(); 1675 final HttpPut userPut = putObjMethod(writeableResource + "/" + indirectString); 1676 setAuth(userPut, username); 1677 userPut.addHeader("Link", "<" + DIRECT_CONTAINER.toString() + ">; rel=type"); 1678 userPut.setEntity(directEntity); 1679 userPut.setHeader("Content-type", "text/turtle"); 1680 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(userPut)); 1681 1682 // Create an user writeable resource. 1683 final HttpPost targetPost = postObjMethod(writeableResource); 1684 setAuth(targetPost, username); 1685 final String tempTarget; 1686 try (final CloseableHttpResponse resp = execute(targetPost)) { 1687 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1688 tempTarget = getLocation(resp); 1689 } 1690 1691 // Try to create direct container referencing an available resource. 1692 final String direct_ok = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1693 "@prefix test: <http://example.org/test#> .\n\n" + 1694 "<> ldp:membershipResource <" + tempTarget + "> ;\n" + 1695 "ldp:hasMemberRelation test:predicateToCreate ."; 1696 final HttpPost userPatchPost = postObjMethod(writeableResource); 1697 setAuth(userPatchPost, username); 1698 userPatchPost.addHeader("Link", "<" + DIRECT_CONTAINER.toString() + ">; rel=type"); 1699 userPatchPost.setEntity(new StringEntity(direct_ok, turtleContentType)); 1700 userPatchPost.setHeader("Content-type", "text/turtle"); 1701 final String directUri; 1702 try (final CloseableHttpResponse resp = execute(userPatchPost)) { 1703 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1704 directUri = getLocation(resp); 1705 } 1706 1707 // Then PATCH to the readonly resource. 1708 final HttpPatch patchDirect = new HttpPatch(directUri); 1709 setAuth(patchDirect, username); 1710 final String patch_text = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1711 "DELETE { <> ldp:membershipResource ?o } \n" + 1712 "INSERT { <> ldp:membershipResource <" + targetResource + "> } \n" + 1713 "WHERE { <> ldp:membershipResource ?o }"; 1714 patchDirect.setEntity(new StringEntity(patch_text, sparqlContentType)); 1715 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchDirect)); 1716 1717 // Delete the ldp:membershipRelation and add it with INSERT DATA {} 1718 final String patch_delete_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1719 "DELETE DATA { <> ldp:membershipResource <" + tempTarget + "> }"; 1720 final HttpPatch patchDirect2 = new HttpPatch(directUri); 1721 setAuth(patchDirect2, username); 1722 patchDirect2.setEntity(new StringEntity(patch_delete_relation, sparqlContentType)); 1723 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchDirect2)); 1724 1725 final String patch_insert_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1726 "INSERT DATA { <> ldp:membershipResource <" + targetResource + "> }"; 1727 final HttpPatch patchDirect3 = new HttpPatch(directUri); 1728 setAuth(patchDirect3, username); 1729 patchDirect3.setEntity(new StringEntity(patch_insert_relation, sparqlContentType)); 1730 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchDirect3)); 1731 1732 // Patch the indirect to the readonly target as admin 1733 final HttpPatch patchAsAdmin = new HttpPatch(directUri); 1734 setAuth(patchAsAdmin, "fedoraAdmin"); 1735 patchAsAdmin.setEntity(new StringEntity(patch_insert_relation, sparqlContentType)); 1736 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchAsAdmin)); 1737 1738 // Try to POST a child as user 1739 final HttpPost postChild = new HttpPost(directUri); 1740 final String postTarget = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1741 "@prefix test: <http://example.org/test#> .\n" + 1742 "<> test:something <" + tempTarget + "> ."; 1743 final HttpEntity putPostChild = new StringEntity(postTarget, turtleContentType); 1744 setAuth(postChild, username); 1745 postChild.setEntity(putPostChild); 1746 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postChild)); 1747 1748 // Try to PUT a child as user 1749 final String id = getRandomUniqueId(); 1750 final HttpPut putChild = new HttpPut(directUri + "/" + id); 1751 setAuth(putChild, username); 1752 putChild.setEntity(putPostChild); 1753 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putChild)); 1754 1755 // Put the child as Admin 1756 setAuth(putChild, "fedoraAdmin"); 1757 assertEquals(HttpStatus.SC_CREATED, getStatus(putChild)); 1758 1759 // Try to delete the child as user 1760 final HttpDelete deleteChild = new HttpDelete(directUri + "/" + id); 1761 setAuth(deleteChild, username); 1762 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteChild)); 1763 1764 // Try to delete the indirect container 1765 final HttpDelete deleteIndirect = new HttpDelete(directUri); 1766 setAuth(deleteIndirect, username); 1767 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteIndirect)); 1768 1769 // Ensure we can still write to the writeable resource. 1770 testCanWrite(writeableResource, username); 1771 1772 } 1773 1774 @Test 1775 public void testDirectRelationshipsOk() throws IOException { 1776 final String targetResource = "/rest/" + getRandomUniqueId(); 1777 final String writeableResource = "/rest/" + getRandomUniqueId(); 1778 final String username = "user28"; 1779 1780 final String targetUri = ingestObj(targetResource); 1781 1782 final String readwriteString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1783 "<#readauthz> a acl:Authorization ;\n" + 1784 " acl:agent \"" + username + "\" ;\n" + 1785 " acl:mode acl:Read, acl:Write ;\n" + 1786 " acl:accessTo <" + targetResource + "> ."; 1787 ingestAclString(targetUri, readwriteString, "fedoraAdmin"); 1788 1789 // User can read target resource. 1790 final HttpGet get1 = getObjMethod(targetResource); 1791 setAuth(get1, username); 1792 assertEquals(HttpStatus.SC_OK, getStatus(get1)); 1793 1794 // User can patch target resource. 1795 final String patch = "INSERT DATA { <> <http://purl.org/dc/elements/1.1/title> \"Changed it\"}"; 1796 final HttpEntity patchEntity = new StringEntity(patch, sparqlContentType); 1797 try (final CloseableHttpResponse resp = (CloseableHttpResponse) PATCH(targetUri, patchEntity, 1798 username)) { 1799 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(resp)); 1800 } 1801 1802 // Make a user writable container. 1803 final String writeableUri = ingestObj(writeableResource); 1804 final String writeableAcl = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1805 "<#writeauth> a acl:Authorization ;\n" + 1806 " acl:agent \"" + username + "\" ;\n" + 1807 " acl:mode acl:Read, acl:Write ;\n" + 1808 " acl:accessTo <" + writeableResource + "> ;\n" + 1809 " acl:default <" + writeableResource + "> ."; 1810 ingestAclString(writeableUri, writeableAcl, "fedoraAdmin"); 1811 1812 // Ensure we can write to the writeable resource. 1813 testCanWrite(writeableResource, username); 1814 1815 // Try to create direct container referencing writeable resource with POST. 1816 final HttpPost userPost = postObjMethod(writeableResource); 1817 setAuth(userPost, username); 1818 userPost.addHeader("Link", "<" + DIRECT_CONTAINER.toString() + ">; rel=type"); 1819 final String indirect = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1820 "@prefix test: <http://example.org/test#> .\n\n" + 1821 "<> ldp:membershipResource <" + targetResource + "> ;\n" + 1822 "ldp:hasMemberRelation test:predicateToCreate ."; 1823 final HttpEntity directEntity = new StringEntity(indirect, turtleContentType); 1824 userPost.setEntity(new StringEntity(indirect, turtleContentType)); 1825 userPost.setHeader("Content-type", "text/turtle"); 1826 assertEquals(HttpStatus.SC_CREATED, getStatus(userPost)); 1827 1828 // Try to create direct container referencing writeable resource with PUT. 1829 final String directString = getRandomUniqueId(); 1830 final HttpPut userPut = putObjMethod(writeableResource + "/" + directString); 1831 setAuth(userPut, username); 1832 userPut.addHeader("Link", "<" + DIRECT_CONTAINER.toString() + ">; rel=type"); 1833 userPut.setEntity(directEntity); 1834 userPut.setHeader("Content-type", "text/turtle"); 1835 assertEquals(HttpStatus.SC_CREATED, getStatus(userPut)); 1836 1837 // Create an user writeable resource. 1838 final HttpPost targetPost = postObjMethod(writeableResource); 1839 setAuth(targetPost, username); 1840 final String tempTarget; 1841 try (final CloseableHttpResponse resp = execute(targetPost)) { 1842 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1843 tempTarget = getLocation(resp); 1844 } 1845 1846 // Try to create direct container referencing an available resource. 1847 final String direct_ok = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1848 "@prefix test: <http://example.org/test#> .\n\n" + 1849 "<> ldp:membershipResource <" + tempTarget + "> ;\n" + 1850 "ldp:hasMemberRelation test:predicateToCreate ."; 1851 final HttpPost userPatchPost = postObjMethod(writeableResource); 1852 setAuth(userPatchPost, username); 1853 userPatchPost.addHeader("Link", "<" + DIRECT_CONTAINER.toString() + ">; rel=type"); 1854 userPatchPost.setEntity(new StringEntity(direct_ok, turtleContentType)); 1855 userPatchPost.setHeader("Content-type", "text/turtle"); 1856 final String directUri; 1857 try (final CloseableHttpResponse resp = execute(userPatchPost)) { 1858 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1859 directUri = getLocation(resp); 1860 } 1861 1862 // Then PATCH to the readonly resource. 1863 final HttpPatch patchDirect = new HttpPatch(directUri); 1864 setAuth(patchDirect, username); 1865 final String patch_text = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1866 "DELETE { <> ldp:membershipResource ?o } \n" + 1867 "INSERT { <> ldp:membershipResource <" + targetResource + "> } \n" + 1868 "WHERE { <> ldp:membershipResource ?o }"; 1869 patchDirect.setEntity(new StringEntity(patch_text, sparqlContentType)); 1870 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchDirect)); 1871 1872 // Delete the ldp:membershipRelation and add it with INSERT DATA {} 1873 final String patch_delete_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1874 "DELETE DATA { <> ldp:membershipResource <" + targetResource + "> }"; 1875 final HttpPatch patchDirect2 = new HttpPatch(directUri); 1876 setAuth(patchDirect2, username); 1877 patchDirect2.setEntity(new StringEntity(patch_delete_relation, sparqlContentType)); 1878 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchDirect2)); 1879 1880 final String patch_insert_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1881 "INSERT DATA { <> ldp:membershipResource <" + targetResource + "> }"; 1882 final HttpPatch patchDirect3 = new HttpPatch(directUri); 1883 setAuth(patchDirect3, username); 1884 patchDirect3.setEntity(new StringEntity(patch_insert_relation, sparqlContentType)); 1885 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchDirect3)); 1886 1887 // Ensure we can write to the writeable resource. 1888 testCanWrite(writeableResource, username); 1889 } 1890 1891 /** 1892 * Utility function to ingest a ACL from a string. 1893 * 1894 * @param resourcePath Path to the resource if doesn't end with "/fcr:acl" it is added. 1895 * @param acl the text/turtle ACL as a string 1896 * @param username user to ingest as 1897 * @return 1898 * @throws IOException on StringEntity encoding or client execute 1899 */ 1900 private HttpResponse ingestAclString(final String resourcePath, final String acl, final String username) 1901 throws IOException { 1902 final String aclPath = (resourcePath.endsWith("/fcr:acl") ? resourcePath : resourcePath + "/fcr:acl"); 1903 final HttpPut putReq = new HttpPut(aclPath); 1904 setAuth(putReq, username); 1905 putReq.setHeader("Content-type", "text/turtle"); 1906 putReq.setEntity(new StringEntity(acl, turtleContentType)); 1907 return execute(putReq); 1908 } 1909 1910 /** 1911 * Ensure that a writeable resource is still writeable 1912 * 1913 * @param writeableResource the URI of the writeable resource. 1914 * @param username the user will write access. 1915 * @throws UnsupportedEncodingException if default charset for String Entity is unsupported 1916 */ 1917 private void testCanWrite(final String writeableResource, final String username) 1918 throws UnsupportedEncodingException { 1919 // Try to create a basic container inside the writeable resource with POST. 1920 final HttpPost okPost = postObjMethod(writeableResource); 1921 setAuth(okPost, username); 1922 assertEquals(HttpStatus.SC_CREATED, getStatus(okPost)); 1923 1924 // Try to PATCH the writeableResource 1925 final HttpPatch okPatch = patchObjMethod(writeableResource); 1926 final String patchString = "PREFIX dc: <http://purl.org/dc/elements/1.1/> DELETE { <> dc:title ?o1 } " + 1927 "INSERT { <> dc:title \"Changed title\" } WHERE { <> dc:title ?o1 }"; 1928 final HttpEntity patchEntity = new StringEntity(patchString, sparqlContentType); 1929 setAuth(okPatch, username); 1930 okPatch.setHeader("Content-type", "application/sparql-update"); 1931 okPatch.setEntity(patchEntity); 1932 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(okPatch)); 1933 } 1934}