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.auth.webac; 007 008import static java.util.Arrays.stream; 009import static javax.ws.rs.core.Response.Status.BAD_REQUEST; 010import static javax.ws.rs.core.Response.Status.CONFLICT; 011import static javax.ws.rs.core.Response.Status.CREATED; 012import static javax.ws.rs.core.Response.Status.FORBIDDEN; 013import static javax.ws.rs.core.Response.Status.NO_CONTENT; 014import static javax.ws.rs.core.Response.Status.OK; 015import static org.apache.http.HttpHeaders.CONTENT_TYPE; 016import static org.apache.http.HttpStatus.SC_CREATED; 017import static org.apache.http.HttpStatus.SC_FORBIDDEN; 018import static org.apache.http.HttpStatus.SC_NOT_FOUND; 019import static org.apache.http.HttpStatus.SC_NO_CONTENT; 020import static org.apache.jena.vocabulary.DC_11.title; 021import static org.fcrepo.http.commons.session.TransactionConstants.ATOMIC_ID_HEADER; 022import static org.fcrepo.kernel.api.FedoraTypes.FCR_METADATA; 023import static org.fcrepo.kernel.api.FedoraTypes.FCR_TX; 024import static org.fcrepo.kernel.api.RdfLexicon.DIRECT_CONTAINER; 025import static org.fcrepo.kernel.api.RdfLexicon.EMBED_CONTAINED; 026import static org.fcrepo.kernel.api.RdfLexicon.INDIRECT_CONTAINER; 027import static org.fcrepo.kernel.api.RdfLexicon.MEMBERSHIP_RESOURCE; 028import static org.junit.Assert.assertEquals; 029import static org.junit.Assert.assertTrue; 030 031import java.io.IOException; 032import java.io.InputStream; 033import java.io.UnsupportedEncodingException; 034import java.nio.file.Paths; 035import java.util.Arrays; 036import java.util.Optional; 037import java.util.regex.Pattern; 038 039import javax.ws.rs.core.Link; 040import javax.ws.rs.core.Response; 041 042import org.fcrepo.auth.webac.WebACRolesProvider; 043import org.fcrepo.http.commons.test.util.CloseableDataset; 044import org.fcrepo.integration.http.api.AbstractResourceIT; 045import org.fcrepo.integration.http.api.TestIsolationExecutionListener; 046 047import org.apache.commons.codec.binary.Base64; 048import org.apache.http.Header; 049import org.apache.http.HeaderElement; 050import org.apache.http.HttpEntity; 051import org.apache.http.HttpResponse; 052import org.apache.http.HttpStatus; 053import org.apache.http.NameValuePair; 054import org.apache.http.client.config.RequestConfig; 055import org.apache.http.client.methods.CloseableHttpResponse; 056import org.apache.http.client.methods.HttpDelete; 057import org.apache.http.client.methods.HttpGet; 058import org.apache.http.client.methods.HttpHead; 059import org.apache.http.client.methods.HttpOptions; 060import org.apache.http.client.methods.HttpPatch; 061import org.apache.http.client.methods.HttpPost; 062import org.apache.http.client.methods.HttpPut; 063import org.apache.http.entity.ContentType; 064import org.apache.http.entity.InputStreamEntity; 065import org.apache.http.entity.StringEntity; 066import org.apache.http.message.AbstractHttpMessage; 067import org.apache.jena.graph.Node; 068import org.apache.jena.graph.NodeFactory; 069import org.apache.jena.sparql.core.DatasetGraph; 070import org.glassfish.grizzly.utils.Charsets; 071import org.junit.Before; 072import org.junit.Ignore; 073import org.junit.Rule; 074import org.junit.Test; 075import org.junit.contrib.java.lang.system.RestoreSystemProperties; 076import org.slf4j.Logger; 077import org.slf4j.LoggerFactory; 078import org.springframework.test.context.TestExecutionListeners; 079 080/** 081 * @author Peter Eichman 082 * @author whikloj 083 * @since September 4, 2015 084 */ 085@TestExecutionListeners( 086 listeners = { TestIsolationExecutionListener.class }, 087 mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) 088public class WebACRecipesIT extends AbstractResourceIT { 089 090 private static final Logger logger = LoggerFactory.getLogger(WebACRecipesIT.class); 091 092 @Rule 093 public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); 094 095 private final ContentType turtleContentType = ContentType.create("text/turtle", "UTF-8"); 096 097 private final ContentType sparqlContentType = ContentType.create("application/sparql-update", "UTF-8"); 098 099 private WebACRolesProvider rolesProvider; 100 101 @Before 102 public void setup() { 103 this.rolesProvider = getBean(WebACRolesProvider.class); 104 authPropsConfig.setRootAuthAclPath(null); 105 rolesProvider.setGroupBaseUri(null); 106 rolesProvider.setUserBaseUri(null); 107 } 108 109 /** 110 * Convenience method to create an ACL with 0 or more authorization resources in the repository. 111 */ 112 private String ingestAcl(final String username, 113 final String aclFilePath, final String aclResourcePath) throws IOException { 114 115 // create the ACL 116 final HttpResponse aclResponse = ingestTurtleResource(username, aclFilePath, aclResourcePath); 117 118 // return the URI to the newly created resource 119 return aclResponse.getFirstHeader("Location").getValue(); 120 } 121 122 /** 123 * Convenience method to POST the contents of a Turtle file to the repository to create a new resource. Returns 124 * the HTTP response from that request. Throws an IOException if the server responds with anything other than a 125 * 201 Created response code. 126 */ 127 private HttpResponse ingestTurtleResource(final String username, final String path, final String requestURI) 128 throws IOException { 129 final HttpPut request = new HttpPut(requestURI); 130 131 logger.debug("PUT to {} to create {}", requestURI, path); 132 133 setAuth(request, username); 134 135 final InputStream file = this.getClass().getResourceAsStream(path); 136 final InputStreamEntity fileEntity = new InputStreamEntity(file); 137 request.setEntity(fileEntity); 138 request.setHeader("Content-Type", "text/turtle"); 139 140 try (final CloseableHttpResponse response = execute(request)) { 141 assertEquals("Didn't get a CREATED response!", CREATED.getStatusCode(), getStatus(response)); 142 return response; 143 } 144 145 } 146 147 /** 148 * Convenience method to set up a regular FedoraResource 149 * 150 * @param path Path to put the resource under 151 * @return the Location of the newly created resource 152 * @throws IOException on error 153 */ 154 private String ingestObj(final String path) throws IOException { 155 final HttpPut request = putObjMethod(path.replace(serverAddress, "")); 156 setAuth(request, "fedoraAdmin"); 157 try (final CloseableHttpResponse response = execute(request)) { 158 assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); 159 return response.getFirstHeader("Location").getValue(); 160 } 161 } 162 163 private String ingestBinary(final String path, final HttpEntity body) throws IOException { 164 logger.info("Ingesting {} binary to {}", body.getContentType().getValue(), path); 165 final HttpPut request = new HttpPut(serverAddress + path); 166 setAuth(request, "fedoraAdmin"); 167 request.setEntity(body); 168 request.setHeader(body.getContentType()); 169 final CloseableHttpResponse response = execute(request); 170 assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); 171 final String location = response.getFirstHeader("Location").getValue(); 172 logger.info("Created binary at {}", location); 173 return location; 174 175 } 176 177 private String ingestDatastream(final String path, final String ds) throws IOException { 178 final HttpPut request = putDSMethod(path, ds, "some not so random content"); 179 setAuth(request, "fedoraAdmin"); 180 try (final CloseableHttpResponse response = execute(request)) { 181 assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); 182 return response.getFirstHeader("Location").getValue(); 183 } 184 } 185 186 /** 187 * Convenience method for applying credentials to a request 188 * 189 * @param method the request to add the credentials to 190 * @param username the username to add 191 */ 192 private static void setAuth(final AbstractHttpMessage method, final String username) { 193 final String creds = username + ":password"; 194 final String encCreds = new String(Base64.encodeBase64(creds.getBytes())); 195 final String basic = "Basic " + encCreds; 196 method.setHeader("Authorization", basic); 197 } 198 199 @Test 200 public void scenario1() throws IOException { 201 final String testObj = ingestObj("/rest/webacl_box1"); 202 203 final String acl1 = ingestAcl("fedoraAdmin", "/acls/01/acl.ttl", 204 testObj + "/fcr:acl"); 205 final String aclLink = Link.fromUri(acl1).rel("acl").build().toString(); 206 207 final HttpGet request = getObjMethod(testObj.replace(serverAddress, "")); 208 assertEquals("Anonymous can read " + testObj, HttpStatus.SC_FORBIDDEN, getStatus(request)); 209 210 setAuth(request, "user01"); 211 try (final CloseableHttpResponse response = execute(request)) { 212 assertEquals("User 'user01' can't read" + testObj, HttpStatus.SC_OK, getStatus(response)); 213 // This gets the Link headers and filters for the correct one (aclLink::equals) defined above. 214 final Optional<String> header = stream(response.getHeaders("Link")).map(Header::getValue) 215 .filter(aclLink::equals).findFirst(); 216 // So you either have the correct Link header or you get nothing. 217 assertTrue("Missing Link header", header.isPresent()); 218 } 219 220 final String childObj = ingestObj("/rest/webacl_box1/child"); 221 final HttpGet getReq = getObjMethod(childObj.replace(serverAddress, "")); 222 setAuth(getReq, "user01"); 223 try (final CloseableHttpResponse response = execute(getReq)) { 224 assertEquals("User 'user01' can't read child of " + testObj, HttpStatus.SC_OK, getStatus(response)); 225 } 226 } 227 228 @Test 229 public void scenario2() throws IOException { 230 final String id = "/rest/box/bag/collection"; 231 final String testObj = ingestObj(id); 232 ingestAcl("fedoraAdmin", "/acls/02/acl.ttl", testObj + "/fcr:acl"); 233 234 logger.debug("Anonymous can not read " + testObj); 235 final HttpGet requestGet = getObjMethod(id); 236 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet)); 237 238 logger.debug("GroupId 'Editors' can read " + testObj); 239 final HttpGet requestGet2 = getObjMethod(id); 240 setAuth(requestGet2, "jones"); 241 requestGet2.setHeader("some-header", "Editors"); 242 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 243 244 logger.debug("Anonymous cannot write " + testObj); 245 final HttpPatch requestPatch = patchObjMethod(id); 246 requestPatch.setEntity(new StringEntity("INSERT { <> <" + title.getURI() + "> \"Test title\" . } WHERE {}")); 247 requestPatch.setHeader("Content-type", "application/sparql-update"); 248 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 249 250 logger.debug("Editors can write " + testObj); 251 final HttpPatch requestPatch2 = patchObjMethod(id); 252 setAuth(requestPatch2, "jones"); 253 requestPatch2.setHeader("some-header", "Editors"); 254 requestPatch2.setEntity( 255 new StringEntity("INSERT { <> <" + title.getURI() + "> \"Different title\" . } WHERE {}")); 256 requestPatch2.setHeader("Content-type", "application/sparql-update"); 257 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch2)); 258 } 259 260 @Test 261 public void scenario3() throws IOException { 262 final String idDark = "/rest/dark/archive"; 263 final String idLight = "/rest/dark/archive/sunshine"; 264 final String testObj = ingestObj(idDark); 265 final String testObj2 = ingestObjWithACL(idLight, "/acls/03/acl.ttl"); 266 ingestAcl("fedoraAdmin", "/acls/03/acl.ttl", testObj + "/fcr:acl"); 267 268 logger.debug("Anonymous can't read " + testObj); 269 final HttpGet requestGet = getObjMethod(idDark); 270 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet)); 271 272 logger.debug("Restricted can read " + testObj); 273 final HttpGet requestGet2 = getObjMethod(idDark); 274 setAuth(requestGet2, "jones"); 275 requestGet2.setHeader("some-header", "Restricted"); 276 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 277 278 logger.debug("Anonymous can read " + testObj2); 279 final HttpGet requestGet3 = getObjMethod(idLight); 280 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 281 282 logger.debug("Restricted can read " + testObj2); 283 final HttpGet requestGet4 = getObjMethod(idLight); 284 setAuth(requestGet4, "jones"); 285 requestGet4.setHeader("some-header", "Restricted"); 286 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 287 } 288 289 @Test 290 public void scenario4() throws IOException { 291 final String id = "/rest/public_collection"; 292 final String testObj = ingestObjWithACL(id, "/acls/04/acl.ttl"); 293 294 logger.debug("Anonymous can read " + testObj); 295 final HttpGet requestGet = getObjMethod(id); 296 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 297 298 logger.debug("Editors can read " + testObj); 299 final HttpGet requestGet2 = getObjMethod(id); 300 setAuth(requestGet2, "jones"); 301 requestGet2.setHeader("some-header", "Editors"); 302 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 303 304 logger.debug("Smith can access " + testObj); 305 final HttpGet requestGet3 = getObjMethod(id); 306 setAuth(requestGet3, "smith"); 307 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 308 309 logger.debug("Anonymous can't write " + testObj); 310 final HttpPatch requestPatch = patchObjMethod(id); 311 requestPatch.setHeader("Content-type", "application/sparql-update"); 312 requestPatch.setEntity(new StringEntity("INSERT { <> <" + title.getURI() + "> \"Change title\" . } WHERE {}")); 313 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 314 315 logger.debug("Editors can write " + testObj); 316 final HttpPatch requestPatch2 = patchObjMethod(id); 317 requestPatch2.setHeader("Content-type", "application/sparql-update"); 318 requestPatch2.setEntity(new StringEntity("INSERT { <> <" + title.getURI() + "> \"New title\" . } WHERE {}")); 319 setAuth(requestPatch2, "jones"); 320 requestPatch2.setHeader("some-header", "Editors"); 321 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch2)); 322 323 logger.debug("Editors can create (PUT) child objects of " + testObj); 324 final HttpPut requestPut1 = putObjMethod(id + "/child1"); 325 setAuth(requestPut1, "jones"); 326 requestPut1.setHeader("some-header", "Editors"); 327 assertEquals(HttpStatus.SC_CREATED, getStatus(requestPut1)); 328 329 final HttpGet requestGet4 = getObjMethod(id + "/child1"); 330 setAuth(requestGet4, "jones"); 331 requestGet4.setHeader("some-header", "Editors"); 332 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 333 334 logger.debug("Editors can create (POST) child objects of " + testObj); 335 final HttpPost requestPost1 = postObjMethod(id); 336 requestPost1.addHeader("Slug", "child2"); 337 setAuth(requestPost1, "jones"); 338 requestPost1.setHeader("some-header", "Editors"); 339 assertEquals(HttpStatus.SC_CREATED, getStatus(requestPost1)); 340 341 final HttpGet requestGet5 = getObjMethod(id + "/child2"); 342 setAuth(requestGet5, "jones"); 343 requestGet5.setHeader("some-header", "Editors"); 344 assertEquals(HttpStatus.SC_OK, getStatus(requestGet5)); 345 346 logger.debug("Editors can create nested child objects of " + testObj); 347 final HttpPut requestPut2 = putObjMethod(id + "/a/b/c/child"); 348 setAuth(requestPut2, "jones"); 349 requestPut2.setHeader("some-header", "Editors"); 350 assertEquals(HttpStatus.SC_CREATED, getStatus(requestPut2)); 351 352 final HttpGet requestGet6 = getObjMethod(id + "/a/b/c/child"); 353 setAuth(requestGet6, "jones"); 354 requestGet6.setHeader("some-header", "Editors"); 355 assertEquals(HttpStatus.SC_OK, getStatus(requestGet6)); 356 357 logger.debug("Smith can't write " + testObj); 358 final HttpPatch requestPatch3 = patchObjMethod(id); 359 requestPatch3.setHeader("Content-type", "application/sparql-update"); 360 requestPatch3.setEntity( 361 new StringEntity("INSERT { <> <" + title.getURI() + "> \"Different title\" . } WHERE {}")); 362 setAuth(requestPatch3, "smith"); 363 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch3)); 364 } 365 366 @Test 367 public void scenario5() throws IOException { 368 final String idPublic = "/rest/mixedCollection/publicObj"; 369 final String idPrivate = "/rest/mixedCollection/privateObj"; 370 ingestObjWithACL("/rest/mixedCollection", "/acls/05/acl.ttl"); 371 final String publicObj = ingestObj(idPublic); 372 final String privateObj = ingestObj(idPrivate); 373 final HttpPatch patch = patchObjMethod(idPublic); 374 375 setAuth(patch, "fedoraAdmin"); 376 patch.setHeader("Content-type", "application/sparql-update"); 377 patch.setEntity(new StringEntity("INSERT { <> a <http://example.com/terms#publicImage> . } WHERE {}")); 378 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patch)); 379 380 381 logger.debug("Anonymous can see eg:publicImage " + publicObj); 382 final HttpGet requestGet = getObjMethod(idPublic); 383 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 384 385 logger.debug("Anonymous can't see other resource " + privateObj); 386 final HttpGet requestGet2 = getObjMethod(idPrivate); 387 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet2)); 388 389 logger.debug("Admins can see eg:publicImage " + publicObj); 390 final HttpGet requestGet3 = getObjMethod(idPublic); 391 setAuth(requestGet3, "jones"); 392 requestGet3.setHeader("some-header", "Admins"); 393 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 394 395 logger.debug("Admins can see others" + privateObj); 396 final HttpGet requestGet4 = getObjMethod(idPrivate); 397 setAuth(requestGet4, "jones"); 398 requestGet4.setHeader("some-header", "Admins"); 399 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 400 } 401 402 @Ignore("Content-type with charset causes it to be a binary - FCREPO-3312") 403 @Test 404 public void scenario9() throws IOException { 405 final String idPublic = "/rest/anotherCollection/publicObj"; 406 final String groups = "/rest/group"; 407 final String fooGroup = groups + "/foo"; 408 final String testObj = ingestObj("/rest/anotherCollection"); 409 final String publicObj = ingestObj(idPublic); 410 411 final HttpPut request = putObjMethod(fooGroup); 412 setAuth(request, "fedoraAdmin"); 413 414 final InputStream file = this.getClass().getResourceAsStream("/acls/09/group.ttl"); 415 final InputStreamEntity fileEntity = new InputStreamEntity(file); 416 request.setEntity(fileEntity); 417 request.setHeader("Content-Type", "text/turtle;charset=UTF-8"); 418 419 assertEquals("Didn't get a CREATED response!", CREATED.getStatusCode(), getStatus(request)); 420 421 ingestAcl("fedoraAdmin", "/acls/09/acl.ttl", testObj + "/fcr:acl"); 422 423 logger.debug("Person1 can see object " + publicObj); 424 final HttpGet requestGet1 = getObjMethod(idPublic); 425 setAuth(requestGet1, "person1"); 426 assertEquals(HttpStatus.SC_OK, getStatus(requestGet1)); 427 428 logger.debug("Person2 can see object " + publicObj); 429 final HttpGet requestGet2 = getObjMethod(idPublic); 430 setAuth(requestGet2, "person2"); 431 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 432 433 logger.debug("Person3 user cannot see object " + publicObj); 434 final HttpGet requestGet3 = getObjMethod(idPublic); 435 setAuth(requestGet3, "person3"); 436 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet3)); 437 } 438 439 /** 440 * Test cases to verify authorization with only acl:Append mode configured 441 * in the acl authorization of an resource. 442 * Tests: 443 * 1. Deny(403) on GET. 444 * 2. Allow(204) on PATCH. 445 * 3. Deny(403) on DELETE. 446 * 4. Deny(403) on PATCH with SPARQL DELETE statements. 447 * 5. Allow(400) on PATCH with empty SPARQL content. 448 * 6. Deny(403) on PATCH with non-SPARQL content. 449 * 450 * @throws IOException thrown from injestObj() or *ObjMethod() calls 451 */ 452 @Test 453 public void scenario18Test1() throws IOException { 454 final String testObj = ingestObj("/rest/append_only_resource"); 455 final String id = "/rest/append_only_resource/" + getRandomUniqueId(); 456 ingestObj(id); 457 458 logger.debug("user18 can read (has ACL:READ): {}", id); 459 final HttpGet requestGet = getObjMethod(id); 460 setAuth(requestGet, "user18"); 461 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 462 463 logger.debug("user18 can't append (no ACL): {}", id); 464 final HttpPatch requestPatch = patchObjMethod(id); 465 setAuth(requestPatch, "user18"); 466 requestPatch.setHeader("Content-type", "application/sparql-update"); 467 requestPatch.setEntity(new StringEntity("INSERT { <> <" + title.getURI() + "> \"Test title\" . } WHERE {}")); 468 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 469 470 logger.debug("user18 can't delete (no ACL): {}", id); 471 final HttpDelete requestDelete = deleteObjMethod(id); 472 setAuth(requestDelete, "user18"); 473 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 474 475 ingestAcl("fedoraAdmin", "/acls/18/append-only-acl.ttl", testObj + "/fcr:acl"); 476 477 logger.debug("user18 still can't read (ACL append): {}", id); 478 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet)); 479 480 logger.debug("user18 can patch - SPARQL INSERTs (ACL append): {}", id); 481 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 482 483 // Alter the Content-type to include a character set, to ensure correct matching. 484 requestPatch.setHeader("Content-type", "application/sparql-update; charset=UTF-8"); 485 logger.debug("user18 can patch - SPARQL INSERTs (ACL append with charset): {}", id); 486 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 487 488 logger.debug("user18 still can't delete (ACL append): {}", id); 489 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 490 491 requestPatch.setEntity(new StringEntity("DELETE { <> <" + title.getURI() + "> \"Test title\" . } WHERE {}")); 492 493 logger.debug("user18 can not patch - SPARQL DELETEs (ACL append): {}", id); 494 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 495 496 requestPatch.setEntity(null); 497 498 logger.debug("user18 can patch (is authorized, but bad request) - Empty SPARQL (ACL append): {}", id); 499 assertEquals(HttpStatus.SC_BAD_REQUEST, getStatus(requestPatch)); 500 501 requestPatch.setHeader("Content-type", null); 502 503 logger.debug("user18 can not patch - Non SPARQL (ACL append): {}", id); 504 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 505 506 } 507 508 /** 509 * Test cases to verify authorization with acl:Read and acl:Append modes 510 * configured in the acl authorization of an resource. 511 * Tests: 512 * 1. Allow(200) on GET. 513 * 2. Allow(204) on PATCH. 514 * 3. Deny(403) on DELETE. 515 * 516 * @throws IOException thrown from called functions within this function 517 */ 518 @Test 519 public void scenario18Test2() throws IOException { 520 final String testObj = ingestObj("/rest/read_append_resource"); 521 522 final String id = "/rest/read_append_resource/" + getRandomUniqueId(); 523 ingestObj(id); 524 525 logger.debug("user18 can read (has ACL:READ): {}", id); 526 final HttpGet requestGet = getObjMethod(id); 527 setAuth(requestGet, "user18"); 528 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 529 530 logger.debug("user18 can't append (no ACL): {}", id); 531 final HttpPatch requestPatch = patchObjMethod(id); 532 setAuth(requestPatch, "user18"); 533 requestPatch.setHeader("Content-type", "application/sparql-update"); 534 requestPatch.setEntity(new StringEntity( 535 "INSERT { <> <" + title.getURI() + "> \"some title\" . } WHERE {}")); 536 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 537 538 ingestAcl("fedoraAdmin", "/acls/18/read-append-acl.ttl", testObj + "/fcr:acl"); 539 540 logger.debug("user18 can't delete (no ACL): {}", id); 541 final HttpDelete requestDelete = deleteObjMethod(id); 542 setAuth(requestDelete, "user18"); 543 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 544 545 logger.debug("user18 can read (ACL read, append): {}", id); 546 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 547 548 logger.debug("user18 can append (ACL read, append): {}", id); 549 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 550 551 logger.debug("user18 still can't delete (ACL read, append): {}", id); 552 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 553 } 554 555 /** 556 * Test cases to verify authorization with acl:Read, acl:Append and 557 * acl:Write modes configured in the acl authorization of an resource. 558 * Tests: 559 * 1. Allow(200) on GET. 560 * 2. Allow(204) on PATCH. 561 * 3. Allow(204) on DELETE. 562 * 563 * @throws IOException from functions called from this function 564 */ 565 @Test 566 public void scenario18Test3() throws IOException { 567 final String testObj = ingestObj("/rest/read_append_write_resource"); 568 569 final String id = "/rest/read_append_write_resource/" + getRandomUniqueId(); 570 ingestObj(id); 571 572 logger.debug("user18 can read (has ACL:READ): {}", id); 573 final HttpGet requestGet = getObjMethod(id); 574 setAuth(requestGet, "user18"); 575 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 576 577 logger.debug("user18 can't append (no ACL): {}", id); 578 final HttpPatch requestPatch = patchObjMethod(id); 579 setAuth(requestPatch, "user18"); 580 requestPatch.setHeader("Content-type", "application/sparql-update"); 581 requestPatch.setEntity(new StringEntity( 582 "INSERT { <> <http://purl.org/dc/elements/1.1/title> \"some title\" . } WHERE {}")); 583 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 584 585 logger.debug("user18 can't delete (no ACL): {}", id); 586 final HttpDelete requestDelete = deleteObjMethod(id); 587 setAuth(requestDelete, "user18"); 588 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 589 590 ingestAcl("fedoraAdmin", "/acls/18/read-append-write-acl.ttl", testObj + "/fcr:acl"); 591 592 logger.debug("user18 can read (ACL read, append, write): {}", id); 593 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 594 595 logger.debug("user18 can append (ACL read, append, write): {}", id); 596 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 597 598 logger.debug("user18 can delete (ACL read, append, write): {}", id); 599 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestDelete)); 600 } 601 602 @Test 603 public void testAccessToRoot() throws IOException { 604 final String id = "/rest/" + getRandomUniqueId(); 605 final String testObj = ingestObj(id); 606 607 logger.debug("Anonymous can read (has ACL:READ): {}", id); 608 final HttpGet requestGet1 = getObjMethod(id); 609 assertEquals(HttpStatus.SC_OK, getStatus(requestGet1)); 610 611 logger.debug("Can username 'user06a' read {} (has ACL:READ)", id); 612 final HttpGet requestGet2 = getObjMethod(id); 613 setAuth(requestGet2, "user06a"); 614 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 615 616 logger.debug("Can username 'notuser06b' read {} (has ACL:READ)", id); 617 final HttpGet requestGet3 = getObjMethod(id); 618 setAuth(requestGet3, "user06b"); 619 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 620 621 authPropsConfig.setRootAuthAclPath(Paths.get("./target/test-classes/test-root-authorization2.ttl")); 622 logger.debug("Can username 'user06a' read {} (overridden system ACL)", id); 623 final HttpGet requestGet4 = getObjMethod(id); 624 setAuth(requestGet4, "user06a"); 625 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 626 authPropsConfig.setRootAuthAclPath(null); 627 628 // Add ACL to root 629 final String rootURI = getObjMethod("/rest").getURI().toString(); 630 ingestAcl("fedoraAdmin", "/acls/06/acl.ttl", rootURI + "/fcr:acl"); 631 632 logger.debug("Anonymous still can't read (ACL present)"); 633 final HttpGet requestGet5 = getObjMethod(id); 634 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet5)); 635 636 logger.debug("Can username 'user06a' read {} (ACL present)", testObj); 637 final HttpGet requestGet6 = getObjMethod(id); 638 setAuth(requestGet6, "user06a"); 639 assertEquals(HttpStatus.SC_OK, getStatus(requestGet6)); 640 641 logger.debug("Can username 'user06b' read {} (ACL present)", testObj); 642 final HttpGet requestGet7 = getObjMethod(id); 643 setAuth(requestGet7, "user06b"); 644 assertEquals(HttpStatus.SC_OK, getStatus(requestGet7)); 645 } 646 647 @Test 648 public void scenario21TestACLNotForInheritance() throws IOException { 649 final String parentPath = "/rest/resource_acl_no_inheritance"; 650 // Ingest ACL with no acl:default statement to the parent resource 651 ingestObjWithACL(parentPath, "/acls/21/acl.ttl"); 652 653 final String id = parentPath + "/" + getRandomUniqueId(); 654 final String testObj = ingestObj(id); 655 656 657 // Test the parent ACL with no acl:default is applied for the parent resource authorization. 658 final HttpGet requestGet1 = getObjMethod(parentPath); 659 setAuth(requestGet1, "user21"); 660 assertEquals("Agent user21 can't read resource " + parentPath + " with its own ACL!", 661 HttpStatus.SC_OK, getStatus(requestGet1)); 662 663 final HttpGet requestGet2 = getObjMethod(id); 664 assertEquals("Agent user21 inherits read permission from parent ACL to read resource " + testObj + "!", 665 HttpStatus.SC_OK, getStatus(requestGet2)); 666 667 // Test the default root ACL is inherited for authorization while the parent ACL with no acl:default is ignored 668 authPropsConfig.setRootAuthAclPath(Paths.get("./target/test-classes/test-root-authorization2.ttl")); 669 final HttpGet requestGet3 = getObjMethod(id); 670 setAuth(requestGet3, "user06a"); 671 assertEquals("Agent user06a can't inherit read persmssion from root ACL to read resource " + testObj + "!", 672 HttpStatus.SC_OK, getStatus(requestGet3)); 673 } 674 675 @Test 676 public void scenario22TestACLAuthorizationNotForInheritance() throws IOException { 677 final String parentPath = "/rest/resource_mix_acl_default"; 678 final String parentObj = ingestObj(parentPath); 679 680 final String id = parentPath + "/" + getRandomUniqueId(); 681 final String testObj = ingestObj(id); 682 683 // Ingest ACL with mix acl:default authorization to the parent resource 684 ingestAcl("fedoraAdmin", "/acls/22/acl.ttl", parentObj + "/fcr:acl"); 685 686 // Test the parent ACL is applied for the parent resource authorization. 687 final HttpGet requestGet1 = getObjMethod(parentPath); 688 setAuth(requestGet1, "user22a"); 689 assertEquals("Agent user22a can't read resource " + parentPath + " with its own ACL!", 690 HttpStatus.SC_OK, getStatus(requestGet1)); 691 692 final HttpGet requestGet2 = getObjMethod(parentPath); 693 setAuth(requestGet2, "user22b"); 694 assertEquals("Agent user22b can't read resource " + parentPath + " with its own ACL!", 695 HttpStatus.SC_OK, getStatus(requestGet1)); 696 697 // Test the parent ACL is applied for the parent resource authorization. 698 final HttpGet requestGet3 = getObjMethod(id); 699 setAuth(requestGet3, "user22a"); 700 assertEquals("Agent user22a inherits read permission from parent ACL to read resource " + testObj + "!", 701 HttpStatus.SC_FORBIDDEN, getStatus(requestGet3)); 702 703 final HttpGet requestGet4 = getObjMethod(id); 704 setAuth(requestGet4, "user22b"); 705 assertEquals("Agent user22b can't inherits read permission from parent ACL to read resource " + testObj + "!", 706 HttpStatus.SC_OK, getStatus(requestGet4)); 707 } 708 709 @Test 710 public void testAccessToBinary() throws IOException { 711 // Block access to "book" 712 final String idBook = "/rest/book"; 713 final String bookURI = ingestObj(idBook); 714 715 // Open access datastream, "file" 716 final String id = idBook + "/file"; 717 final String testObj = ingestDatastream(idBook, "file"); 718 ingestAcl("fedoraAdmin", "/acls/07/acl.ttl", bookURI + "/fcr:acl"); 719 720 logger.debug("Anonymous can't read"); 721 final HttpGet requestGet1 = getObjMethod(id); 722 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet1)); 723 724 logger.debug("Can username 'user07' read {}", testObj); 725 final HttpGet requestGet2 = getObjMethod(id); 726 727 setAuth(requestGet2, "user07"); 728 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 729 } 730 731 @Test 732 public void testAccessToVersionedResources() throws IOException { 733 final String idVersion = "/rest/versionResource"; 734 final String idVersionUri = ingestObj(idVersion); 735 736 final HttpPatch requestPatch1 = patchObjMethod(idVersion); 737 setAuth(requestPatch1, "fedoraAdmin"); 738 requestPatch1.addHeader("Content-type", "application/sparql-update"); 739 requestPatch1.setEntity( 740 new StringEntity("PREFIX pcdm: <http://pcdm.org/models#> INSERT { <> a pcdm:Object } WHERE {}")); 741 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch1)); 742 743 ingestAcl("fedoraAdmin", "/acls/10/acl.ttl", idVersionUri + "/fcr:acl"); 744 745 final HttpGet requestGet1 = getObjMethod(idVersion); 746 setAuth(requestGet1, "user10"); 747 assertEquals("user10 can't read object", HttpStatus.SC_OK, getStatus(requestGet1)); 748 749 final HttpPost requestPost1 = postObjMethod(idVersion + "/fcr:versions"); 750 setAuth(requestPost1, "fedoraAdmin"); 751 assertEquals("Unable to create a new version", HttpStatus.SC_CREATED, getStatus(requestPost1)); 752 753 final HttpGet requestGet2 = getObjMethod(idVersion); 754 setAuth(requestGet2, "user10"); 755 assertEquals("user10 can't read versioned object", HttpStatus.SC_OK, getStatus(requestGet2)); 756 } 757 758 @Test 759 public void testDelegatedUserAccess() throws IOException { 760 logger.debug("testing delegated authentication"); 761 final String targetPath = "/rest/foo"; 762 final String targetResource = ingestObj(targetPath); 763 764 ingestAcl("fedoraAdmin", "/acls/11/acl.ttl", targetResource + "/fcr:acl"); 765 766 final HttpGet adminGet = getObjMethod(targetPath); 767 setAuth(adminGet, "fedoraAdmin"); 768 assertEquals("admin can read object", HttpStatus.SC_OK, getStatus(adminGet)); 769 770 final HttpGet adminDelegatedGet = getObjMethod(targetPath); 771 setAuth(adminDelegatedGet, "fedoraAdmin"); 772 adminDelegatedGet.addHeader("On-Behalf-Of", "user11"); 773 assertEquals("delegated user can read object", HttpStatus.SC_OK, getStatus(adminDelegatedGet)); 774 775 final HttpGet adminUnauthorizedDelegatedGet = getObjMethod(targetPath); 776 setAuth(adminUnauthorizedDelegatedGet, "fedoraAdmin"); 777 adminUnauthorizedDelegatedGet.addHeader("On-Behalf-Of", "fakeuser"); 778 assertEquals("delegated fakeuser cannot read object", HttpStatus.SC_FORBIDDEN, 779 getStatus(adminUnauthorizedDelegatedGet)); 780 781 final HttpGet adminDelegatedGet2 = getObjMethod(targetPath); 782 setAuth(adminDelegatedGet2, "fedoraAdmin"); 783 adminDelegatedGet2.addHeader("On-Behalf-Of", "info:user/user2"); 784 assertEquals("delegated user can read object", HttpStatus.SC_OK, getStatus(adminDelegatedGet2)); 785 786 final HttpGet adminUnauthorizedDelegatedGet2 = getObjMethod(targetPath); 787 setAuth(adminUnauthorizedDelegatedGet2, "fedoraAdmin"); 788 adminUnauthorizedDelegatedGet2.addHeader("On-Behalf-Of", "info:user/fakeuser"); 789 assertEquals("delegated fakeuser cannot read object", HttpStatus.SC_FORBIDDEN, 790 getStatus(adminUnauthorizedDelegatedGet2)); 791 792 // Now test with the system property in effect 793 rolesProvider.setUserBaseUri("info:user/"); 794 rolesProvider.setGroupBaseUri("info:group/"); 795 796 final HttpGet adminDelegatedGet3 = getObjMethod(targetPath); 797 setAuth(adminDelegatedGet3, "fedoraAdmin"); 798 adminDelegatedGet3.addHeader("On-Behalf-Of", "info:user/user2"); 799 assertEquals("delegated user can read object", HttpStatus.SC_OK, getStatus(adminDelegatedGet3)); 800 801 final HttpGet adminUnauthorizedDelegatedGet3 = getObjMethod(targetPath); 802 setAuth(adminUnauthorizedDelegatedGet3, "fedoraAdmin"); 803 adminUnauthorizedDelegatedGet3.addHeader("On-Behalf-Of", "info:user/fakeuser"); 804 assertEquals("delegated fakeuser cannot read object", HttpStatus.SC_FORBIDDEN, 805 getStatus(adminUnauthorizedDelegatedGet3)); 806 } 807 808 @Test 809 public void testAccessByUriToVersionedResources() throws IOException { 810 final String idVersionPath = "rest/versionResourceUri"; 811 final String idVersionResource = ingestObj(idVersionPath); 812 813 ingestAcl("fedoraAdmin", "/acls/12/acl.ttl", idVersionResource + "/fcr:acl"); 814 815 final HttpGet requestGet1 = getObjMethod(idVersionPath); 816 setAuth(requestGet1, "user12"); 817 assertEquals("testuser can't read object", HttpStatus.SC_OK, getStatus(requestGet1)); 818 819 final HttpPost requestPost1 = postObjMethod(idVersionPath + "/fcr:versions"); 820 setAuth(requestPost1, "user12"); 821 final String mementoLocation; 822 try (final CloseableHttpResponse response = execute(requestPost1)) { 823 assertEquals("Unable to create a new version", HttpStatus.SC_CREATED, getStatus(response)); 824 mementoLocation = getLocation(response); 825 } 826 827 final HttpGet requestGet2 = new HttpGet(mementoLocation); 828 setAuth(requestGet2, "user12"); 829 assertEquals("testuser can't read versioned object", HttpStatus.SC_OK, getStatus(requestGet2)); 830 } 831 832 @Test 833 public void testAgentAsUri() throws IOException { 834 final String id = "/rest/" + getRandomUniqueId(); 835 final String testObj = ingestObj(id); 836 837 logger.debug("Anonymous can read (has ACL:READ): {}", id); 838 final HttpGet requestGet1 = getObjMethod(id); 839 assertEquals(HttpStatus.SC_OK, getStatus(requestGet1)); 840 841 logger.debug("Can username 'smith123' read {} (no ACL)", id); 842 final HttpGet requestGet2 = getObjMethod(id); 843 setAuth(requestGet2, "smith123"); 844 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 845 846 rolesProvider.setUserBaseUri("info:user/"); 847 rolesProvider.setGroupBaseUri("info:group/"); 848 849 logger.debug("Can username 'smith123' read {} (overridden system ACL)", id); 850 final HttpGet requestGet3 = getObjMethod(id); 851 setAuth(requestGet3, "smith123"); 852 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 853 854 logger.debug("Can username 'group123' read {} (overridden system ACL)", id); 855 final HttpGet requestGet4 = getObjMethod(id); 856 setAuth(requestGet4, "group123"); 857 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 858 859 rolesProvider.setUserBaseUri(null); 860 rolesProvider.setGroupBaseUri(null); 861 862 // Add ACL to object 863 ingestAcl("fedoraAdmin", "/acls/16/acl.ttl", testObj + "/fcr:acl"); 864 865 logger.debug("Anonymous still can't read (ACL present)"); 866 final HttpGet requestGet5 = getObjMethod(id); 867 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet5)); 868 869 logger.debug("Can username 'smith123' read {} (ACL present, no system properties)", testObj); 870 final HttpGet requestGet6 = getObjMethod(id); 871 setAuth(requestGet6, "smith123"); 872 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet6)); 873 874 rolesProvider.setUserBaseUri("info:user/"); 875 rolesProvider.setGroupBaseUri("info:group/"); 876 877 logger.debug("Can username 'smith123' read {} (ACL, system properties present)", id); 878 final HttpGet requestGet7 = getObjMethod(id); 879 setAuth(requestGet7, "smith123"); 880 assertEquals(HttpStatus.SC_OK, getStatus(requestGet7)); 881 882 logger.debug("Can groupname 'group123' read {} (ACL, system properties present)", id); 883 final HttpGet requestGet8 = getObjMethod(id); 884 setAuth(requestGet8, "group123"); 885 assertEquals(HttpStatus.SC_OK, getStatus(requestGet8)); 886 } 887 888 @Test 889 public void testRegisterNamespace() throws IOException { 890 final String testObj = ingestObj("/rest/test_namespace"); 891 ingestAcl("fedoraAdmin", "/acls/13/acl.ttl", testObj + "/fcr:acl"); 892 893 final String id = "/rest/test_namespace/" + getRandomUniqueId(); 894 ingestObj(id); 895 896 final HttpPatch patchReq = patchObjMethod(id); 897 setAuth(patchReq, "user13"); 898 patchReq.addHeader("Content-type", "application/sparql-update"); 899 patchReq.setEntity(new StringEntity("PREFIX novel: <info://" + getRandomUniqueId() + ">\n" 900 + "INSERT DATA { <> novel:value 'test' }")); 901 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 902 } 903 904 @Test 905 public void testRegisterNodeType() throws IOException { 906 final String testObj = ingestObj("/rest/test_nodetype"); 907 ingestAcl("fedoraAdmin", "/acls/14/acl.ttl", testObj + "/fcr:acl"); 908 909 final String id = "/rest/test_nodetype/" + getRandomUniqueId(); 910 ingestObj(id); 911 912 final HttpPatch patchReq = patchObjMethod(id); 913 setAuth(patchReq, "user14"); 914 patchReq.addHeader("Content-type", "application/sparql-update"); 915 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 916 + "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" 917 + "INSERT DATA { <> rdf:type dc:type }")); 918 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 919 } 920 921 922 @Test 923 public void testDeletePropertyAsUser() throws IOException { 924 final String testObj = ingestObj("/rest/test_delete"); 925 ingestAcl("fedoraAdmin", "/acls/15/acl.ttl", testObj + "/fcr:acl"); 926 927 final String id = "/rest/test_delete/" + getRandomUniqueId(); 928 ingestObj(id); 929 930 HttpPatch patchReq = patchObjMethod(id); 931 setAuth(patchReq, "user15"); 932 patchReq.addHeader("Content-type", "application/sparql-update"); 933 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 934 + "INSERT DATA { <> dc:title 'title' . " + 935 " <> dc:rights 'rights' . }")); 936 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 937 938 patchReq = patchObjMethod(id); 939 setAuth(patchReq, "user15"); 940 patchReq.addHeader("Content-type", "application/sparql-update"); 941 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 942 + "DELETE { <> dc:title ?any . } WHERE { <> dc:title ?any . }")); 943 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 944 945 patchReq = patchObjMethod(id); 946 setAuth(patchReq, "notUser15"); 947 patchReq.addHeader("Content-type", "application/sparql-update"); 948 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 949 + "DELETE { <> dc:rights ?any . } WHERE { <> dc:rights ?any . }")); 950 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchReq)); 951 } 952 953 @Test 954 public void testHeadWithReadOnlyUser() throws IOException { 955 final String testObj = ingestObj("/rest/test_head"); 956 ingestAcl("fedoraAdmin", "/acls/19/acl.ttl", testObj + "/fcr:acl"); 957 958 final HttpHead headReq = new HttpHead(testObj); 959 setAuth(headReq, "user19"); 960 assertEquals(HttpStatus.SC_OK, getStatus(headReq)); 961 } 962 963 @Test 964 public void testOptionsWithReadOnlyUser() throws IOException { 965 final String testObj = ingestObj("/rest/test_options"); 966 ingestAcl("fedoraAdmin", "/acls/20/acl.ttl", testObj + "/fcr:acl"); 967 968 final HttpOptions optionsReq = new HttpOptions(testObj); 969 setAuth(optionsReq, "user20"); 970 assertEquals(HttpStatus.SC_OK, getStatus(optionsReq)); 971 } 972 973 private static HttpResponse HEAD(final String requestURI) throws IOException { 974 return HEAD(requestURI, "fedoraAdmin"); 975 } 976 977 private static HttpResponse HEAD(final String requestURI, final String username) throws IOException { 978 final HttpHead req = new HttpHead(requestURI); 979 setAuth(req, username); 980 return execute(req); 981 } 982 983 private static HttpResponse PUT(final String requestURI) throws IOException { 984 return PUT(requestURI, "fedoraAdmin"); 985 } 986 987 private static HttpResponse PUT(final String requestURI, final String username) throws IOException { 988 final HttpPut req = new HttpPut(requestURI); 989 setAuth(req, username); 990 return execute(req); 991 } 992 993 private static HttpResponse DELETE(final String requestURI, final String username) throws IOException { 994 final HttpDelete req = new HttpDelete(requestURI); 995 setAuth(req, username); 996 return execute(req); 997 } 998 999 private static HttpResponse GET(final String requestURI, final String username) throws IOException { 1000 final HttpGet req = new HttpGet(requestURI); 1001 setAuth(req, username); 1002 return execute(req); 1003 } 1004 1005 private static HttpResponse PATCH(final String requestURI, final HttpEntity body, final String username) 1006 throws IOException { 1007 final HttpPatch req = new HttpPatch(requestURI); 1008 setAuth(req, username); 1009 if (body != null) { 1010 req.setEntity(body); 1011 } 1012 return execute(req); 1013 } 1014 1015 private static String getLink(final HttpResponse res) { 1016 for (final Header h : res.getHeaders("Link")) { 1017 final HeaderElement link = h.getElements()[0]; 1018 for (final NameValuePair param : link.getParameters()) { 1019 if (param.getName().equals("rel") && param.getValue().equals("acl")) { 1020 return link.getName().replaceAll("^<|>$", ""); 1021 } 1022 } 1023 } 1024 return null; 1025 } 1026 1027 private String ingestObjWithACL(final String path, final String aclResourcePath) throws IOException { 1028 final String newURI = ingestObj(path); 1029 final HttpResponse res = HEAD(newURI); 1030 final String aclURI = getLink(res); 1031 1032 logger.debug("Creating ACL at {}", aclURI); 1033 ingestAcl("fedoraAdmin", aclResourcePath, aclURI); 1034 1035 return newURI; 1036 } 1037 1038 @Test 1039 public void testControl() throws IOException { 1040 final String controlObj = ingestObjWithACL("/rest/control", "/acls/25/control.ttl"); 1041 final String readwriteObj = ingestObjWithACL("/rest/readwrite", "/acls/25/readwrite.ttl"); 1042 1043 final String rwChildACL = getLink(PUT(readwriteObj + "/child")); 1044 assertEquals(SC_FORBIDDEN, getStatus(HEAD(rwChildACL, "testuser"))); 1045 assertEquals(SC_FORBIDDEN, getStatus(GET(rwChildACL, "testuser"))); 1046 assertEquals(SC_FORBIDDEN, getStatus(PUT(rwChildACL, "testuser"))); 1047 assertEquals(SC_FORBIDDEN, getStatus(DELETE(rwChildACL, "testuser"))); 1048 1049 final String controlChildACL = getLink(PUT(controlObj + "/child")); 1050 assertEquals(SC_NOT_FOUND, getStatus(HEAD(controlChildACL, "testuser"))); 1051 assertEquals(SC_NOT_FOUND, getStatus(GET(controlChildACL, "testuser"))); 1052 1053 ingestAcl("testuser", "/acls/25/child-control.ttl", controlChildACL); 1054 final StringEntity sparqlUpdate = new StringEntity( 1055 "PREFIX acl: <http://www.w3.org/ns/auth/acl#> INSERT { <#restricted> acl:mode acl:Read } WHERE { }", 1056 ContentType.create("application/sparql-update")); 1057 assertEquals(SC_NO_CONTENT, getStatus(PATCH(controlChildACL, sparqlUpdate, "testuser"))); 1058 1059 assertEquals(SC_NO_CONTENT, getStatus(DELETE(controlChildACL, "testuser"))); 1060 } 1061 1062 @Test 1063 public void testAppendOnlyToContainer() throws IOException { 1064 final String testObj = ingestObj("/rest/test_append"); 1065 ingestAcl("fedoraAdmin", "/acls/23/acl.ttl", testObj + "/fcr:acl"); 1066 final String username = "user23"; 1067 1068 final HttpOptions optionsReq = new HttpOptions(testObj); 1069 setAuth(optionsReq, username); 1070 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(optionsReq)); 1071 1072 final HttpHead headReq = new HttpHead(testObj); 1073 setAuth(headReq, username); 1074 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(headReq)); 1075 1076 final HttpGet getReq = new HttpGet(testObj); 1077 setAuth(getReq, username); 1078 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(getReq)); 1079 1080 final HttpPut putReq = new HttpPut(testObj); 1081 setAuth(putReq, username); 1082 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putReq)); 1083 1084 final HttpDelete deleteReq = new HttpDelete(testObj); 1085 setAuth(deleteReq, username); 1086 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteReq)); 1087 1088 final HttpPost postReq = new HttpPost(testObj); 1089 setAuth(postReq, username); 1090 assertEquals(HttpStatus.SC_CREATED, getStatus(postReq)); 1091 1092 final String[] legalSPARQLQueries = new String[] { 1093 "INSERT DATA { <> <http://purl.org/dc/terms/title> \"Test23\" . }", 1094 "INSERT { <> <http://purl.org/dc/terms/alternative> \"Test XXIII\" . } WHERE {}", 1095 "DELETE {} INSERT { <> <http://purl.org/dc/terms/description> \"Test append only\" . } WHERE {}" 1096 }; 1097 for (final String query : legalSPARQLQueries) { 1098 final HttpPatch patchReq = new HttpPatch(testObj); 1099 setAuth(patchReq, username); 1100 patchReq.setEntity(new StringEntity(query)); 1101 patchReq.setHeader("Content-Type", "application/sparql-update"); 1102 logger.debug("Testing SPARQL update: {}", query); 1103 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 1104 } 1105 1106 final String[] illegalSPARQLQueries = new String[] { 1107 "DELETE DATA { <> <http://purl.org/dc/terms/title> \"Test23\" . }", 1108 "DELETE { <> <http://purl.org/dc/terms/alternative> \"Test XXIII\" . } WHERE {}", 1109 "DELETE { <> <http://purl.org/dc/terms/description> \"Test append only\" . } INSERT {} WHERE {}" 1110 }; 1111 for (final String query : illegalSPARQLQueries) { 1112 final HttpPatch patchReq = new HttpPatch(testObj); 1113 setAuth(patchReq, username); 1114 patchReq.setEntity(new StringEntity(query)); 1115 patchReq.setHeader("Content-Type", "application/sparql-update"); 1116 logger.debug("Testing SPARQL update: {}", query); 1117 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchReq)); 1118 } 1119 final String[] allowedDeleteSPARQLQueries = new String[] { 1120 "DELETE DATA {}", 1121 "DELETE { } WHERE {}", 1122 "DELETE { } INSERT {} WHERE {}" 1123 }; 1124 for (final String query : allowedDeleteSPARQLQueries) { 1125 final HttpPatch patchReq = new HttpPatch(testObj); 1126 setAuth(patchReq, username); 1127 patchReq.setEntity(new StringEntity(query)); 1128 patchReq.setHeader("Content-Type", "application/sparql-update"); 1129 logger.debug("Testing SPARQL update: {}", query); 1130 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 1131 } 1132 1133 } 1134 1135 @Test 1136 public void testAppendOnlyToBinary() throws IOException { 1137 final String testObj = ingestBinary("/rest/test_append_binary", new StringEntity("foo")); 1138 ingestAcl("fedoraAdmin", "/acls/24/acl.ttl", testObj + "/fcr:acl"); 1139 final String username = "user24"; 1140 1141 final HttpOptions optionsReq = new HttpOptions(testObj); 1142 setAuth(optionsReq, username); 1143 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(optionsReq)); 1144 1145 final HttpHead headReq = new HttpHead(testObj); 1146 setAuth(headReq, username); 1147 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(headReq)); 1148 1149 final HttpGet getReq = new HttpGet(testObj); 1150 setAuth(getReq, username); 1151 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(getReq)); 1152 1153 final HttpPut putReq = new HttpPut(testObj); 1154 setAuth(putReq, username); 1155 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putReq)); 1156 1157 final HttpDelete deleteReq = new HttpDelete(testObj); 1158 setAuth(deleteReq, username); 1159 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteReq)); 1160 1161 final HttpPost postReq = new HttpPost(testObj); 1162 setAuth(postReq, username); 1163 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postReq)); 1164 } 1165 1166 @Test 1167 public void testFoafAgent() throws IOException { 1168 final String path = ingestObj("/rest/foaf-agent"); 1169 ingestAcl("fedoraAdmin", "/acls/26/foaf-agent.ttl", path + "/fcr:acl"); 1170 final String username = "user1"; 1171 1172 final HttpGet req = new HttpGet(path); 1173 1174 //NB: Actually no authentication headers should be set for this test 1175 //since the point of foaf:Agent is to allow unauthenticated access for everyone. 1176 //However at this time the test integration test server requires callers to 1177 //authenticate. 1178 setAuth(req, username); 1179 1180 assertEquals(HttpStatus.SC_OK, getStatus(req)); 1181 } 1182 1183 @Test 1184 public void testAuthenticatedAgent() throws IOException { 1185 final String path = ingestObj("/rest/authenticated-agent"); 1186 ingestAcl("fedoraAdmin", "/acls/26/authenticated-agent.ttl", path + "/fcr:acl"); 1187 final String username = "user1"; 1188 1189 final HttpGet darkReq = new HttpGet(path); 1190 setAuth(darkReq, username); 1191 assertEquals(HttpStatus.SC_OK, getStatus(darkReq)); 1192 } 1193 1194 @Test 1195 public void testAgentGroupWithHashUris() throws Exception { 1196 ingestTurtleResource("fedoraAdmin", "/acls/agent-group-list.ttl", 1197 serverAddress + "/rest/agent-group-list"); 1198 //check that the authorized are authorized. 1199 final String authorized = ingestObj("/rest/agent-group-with-hash-uri-authorized"); 1200 ingestAcl("fedoraAdmin", "/acls/agent-group-with-hash-uri-authorized.ttl", authorized + "/fcr:acl"); 1201 1202 final HttpGet getAuthorized = new HttpGet(authorized); 1203 setAuth(getAuthorized, "testuser"); 1204 assertEquals(HttpStatus.SC_OK, getStatus(getAuthorized)); 1205 1206 //check that the unauthorized are unauthorized. 1207 final String unauthorized = ingestObj("/rest/agent-group-with-hash-uri-unauthorized"); 1208 ingestAcl("fedoraAdmin", "/acls/agent-group-with-hash-uri-unauthorized.ttl", unauthorized + "/fcr:acl"); 1209 1210 final HttpGet getUnauthorized = new HttpGet(unauthorized); 1211 setAuth(getUnauthorized, "testuser"); 1212 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(getUnauthorized)); 1213 } 1214 1215 @Test 1216 public void testAgentGroupWithMembersAsURIs() throws Exception { 1217 rolesProvider.setUserBaseUri("http://example.com/"); 1218 ingestTurtleResource("fedoraAdmin", "/acls/agent-group-list-with-member-uris.ttl", 1219 serverAddress + "/rest/agent-group-list-with-member-uris"); 1220 final String authorized = ingestObj("/rest/agent-group-with-vcard-member-as-uri"); 1221 ingestAcl("fedoraAdmin", "/acls/agent-group-with-vcard-member-as-uri.ttl", authorized + "/fcr:acl"); 1222 //check that test user is authorized to write 1223 final HttpPut childPut = new HttpPut(authorized + "/child"); 1224 setAuth(childPut, "testuser"); 1225 assertEquals(HttpStatus.SC_CREATED, getStatus(childPut)); 1226 } 1227 1228 @Test 1229 public void testAgentGroup() throws Exception { 1230 ingestTurtleResource("fedoraAdmin", "/acls/agent-group-list-flat.ttl", 1231 serverAddress + "/rest/agent-group-list-flat"); 1232 //check that the authorized are authorized. 1233 final String flat = ingestObj("/rest/agent-group-flat"); 1234 ingestAcl("fedoraAdmin", "/acls/agent-group-flat.ttl", flat + "/fcr:acl"); 1235 1236 final HttpGet getFlat = new HttpGet(flat); 1237 setAuth(getFlat, "testuser"); 1238 assertEquals(HttpStatus.SC_OK, getStatus(getFlat)); 1239 } 1240 1241 @Test 1242 public void testAclAppendPermissions() throws Exception { 1243 final String testObj = ingestBinary("/rest/test-read-append", new StringEntity("foo")); 1244 ingestAcl("fedoraAdmin", "/acls/27/read-append.ttl", testObj + "/fcr:acl"); 1245 final String username = "user27"; 1246 1247 final HttpOptions optionsReq = new HttpOptions(testObj); 1248 setAuth(optionsReq, username); 1249 assertEquals(HttpStatus.SC_OK, getStatus(optionsReq)); 1250 1251 final HttpHead headReq = new HttpHead(testObj); 1252 setAuth(headReq, username); 1253 assertEquals(HttpStatus.SC_OK, getStatus(headReq)); 1254 1255 final HttpGet getReq = new HttpGet(testObj); 1256 setAuth(getReq, username); 1257 final String descriptionUri; 1258 try (final CloseableHttpResponse response = execute(getReq)) { 1259 assertEquals(HttpStatus.SC_OK, getStatus(response)); 1260 descriptionUri = Arrays.stream(response.getHeaders("Link")) 1261 .flatMap(header -> Arrays.stream(header.getValue().split(","))).map(linkStr -> Link.valueOf( 1262 linkStr)) 1263 .filter(link -> link.getRels().contains("describedby")).map(link -> link.getUri().toString()) 1264 .findFirst().orElse(null); 1265 } 1266 1267 1268 final HttpPut putReq = new HttpPut(testObj); 1269 setAuth(putReq, username); 1270 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putReq)); 1271 1272 final HttpDelete deleteReq = new HttpDelete(testObj); 1273 setAuth(deleteReq, username); 1274 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteReq)); 1275 1276 final HttpPost postReq = new HttpPost(testObj); 1277 setAuth(postReq, username); 1278 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postReq)); 1279 1280 if (descriptionUri != null) { 1281 final HttpOptions optionsDescReq = new HttpOptions(descriptionUri); 1282 setAuth(optionsDescReq, username); 1283 assertEquals(HttpStatus.SC_OK, getStatus(optionsDescReq)); 1284 1285 final HttpHead headDescReq = new HttpHead(descriptionUri); 1286 setAuth(headDescReq, username); 1287 assertEquals(HttpStatus.SC_OK, getStatus(headDescReq)); 1288 1289 final HttpGet getDescReq = new HttpGet(descriptionUri); 1290 setAuth(getDescReq, username); 1291 assertEquals(HttpStatus.SC_OK, getStatus(getDescReq)); 1292 1293 final HttpPut putDescReq = new HttpPut(descriptionUri); 1294 setAuth(putDescReq, username); 1295 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putDescReq)); 1296 1297 final HttpDelete deleteDescReq = new HttpDelete(descriptionUri); 1298 setAuth(deleteDescReq, username); 1299 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteDescReq)); 1300 1301 final HttpPost postDescReq = new HttpPost(descriptionUri); 1302 setAuth(postDescReq, username); 1303 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postDescReq)); 1304 } 1305 } 1306 1307 @Test 1308 public void testCreateAclWithAccessToClassForBinary() throws Exception { 1309 final String id = getRandomUniqueId(); 1310 final String subjectUri = serverAddress + id; 1311 ingestObj(subjectUri); 1312 ingestAcl("fedoraAdmin", "/acls/agent-access-to-class.ttl", subjectUri + "/fcr:acl"); 1313 1314 final String binaryUri = ingestBinary("/rest/" + id + "/binary", new StringEntity("foo")); 1315 1316 final HttpHead headBinary = new HttpHead(binaryUri); 1317 setAuth(headBinary, "testuser"); 1318 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(headBinary)); 1319 1320 final HttpHead headDesc = new HttpHead(binaryUri + "/fcr:metadata"); 1321 setAuth(headDesc, "testuser"); 1322 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(headDesc)); 1323 1324 // Add type to binary 1325 final HttpPatch requestPatch = patchObjMethod(id + "/binary/fcr:metadata"); 1326 setAuth(requestPatch, "fedoraAdmin"); 1327 final String sparql = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \n" + 1328 "PREFIX foaf: <http://xmlns.com/foaf/0.1/> \n" + 1329 "INSERT { <> rdf:type foaf:Document } WHERE {}"; 1330 requestPatch.setEntity(new StringEntity(sparql)); 1331 requestPatch.setHeader("Content-type", "application/sparql-update"); 1332 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 1333 1334 final HttpHead headBinary2 = new HttpHead(binaryUri); 1335 setAuth(headBinary2, "testuser"); 1336 assertEquals(HttpStatus.SC_OK, getStatus(headBinary2)); 1337 1338 final HttpHead headDesc2 = new HttpHead(binaryUri + "/fcr:metadata"); 1339 setAuth(headDesc2, "testuser"); 1340 assertEquals(HttpStatus.SC_OK, getStatus(headDesc2)); 1341 } 1342 1343 @Ignore("Until FCREPO-3310 and FCREPO-3311 are resolved") 1344 @Test 1345 public void testIndirectRelationshipForbidden() throws IOException { 1346 final String targetResource = "/rest/" + getRandomUniqueId(); 1347 final String writeableResource = "/rest/" + getRandomUniqueId(); 1348 final String username = "user28"; 1349 1350 final String targetUri = ingestObj(targetResource); 1351 1352 final String readonlyString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1353 "<#readauthz> a acl:Authorization ;\n" + 1354 " acl:agent \"" + username + "\" ;\n" + 1355 " acl:mode acl:Read ;\n" + 1356 " acl:accessTo <" + targetResource + "> ."; 1357 ingestAclString(targetUri, readonlyString, "fedoraAdmin"); 1358 1359 // User can read target resource. 1360 final HttpGet get1 = getObjMethod(targetResource); 1361 setAuth(get1, username); 1362 assertEquals(HttpStatus.SC_OK, getStatus(get1)); 1363 1364 // User can't patch target resource. 1365 final String patch = "INSERT DATA { <> <http://purl.org/dc/elements/1.1/title> \"Changed it\"}"; 1366 final HttpEntity patchEntity = new StringEntity(patch, sparqlContentType); 1367 try (final CloseableHttpResponse resp = (CloseableHttpResponse) PATCH(targetUri, patchEntity, 1368 username)) { 1369 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(resp)); 1370 } 1371 1372 // Make a user writable container. 1373 final String writeableUri = ingestObj(writeableResource); 1374 final String writeableAcl = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1375 "<#writeauth> a acl:Authorization ;\n" + 1376 " acl:agent \"" + username + "\" ;\n" + 1377 " acl:mode acl:Read, acl:Write ;\n" + 1378 " acl:accessTo <" + writeableResource + "> ;\n" + 1379 " acl:default <" + writeableResource + "> ."; 1380 ingestAclString(writeableUri, writeableAcl, "fedoraAdmin"); 1381 1382 // Ensure we can still POST/PUT to writeable resource. 1383 testCanWrite(writeableResource, username); 1384 1385 // Try to create indirect container referencing readonly resource with POST. 1386 final HttpPost userPost = postObjMethod(writeableResource); 1387 setAuth(userPost, username); 1388 userPost.addHeader("Link", "<" + INDIRECT_CONTAINER.toString() + ">; rel=type"); 1389 final String indirect = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1390 "@prefix example: <http://www.example.org/example1#> .\n" + 1391 "@prefix dc: <http://purl.org/dc/elements/1.1/> .\n" + 1392 "<> ldp:insertedContentRelation <http://example.org/test#something> ;\n" + 1393 "ldp:membershipResource <" + targetResource + "> ;\n" + 1394 "ldp:hasMemberRelation <http://example.org/test#predicateToCreate> ;\n" + 1395 "dc:title \"The indirect container\" ."; 1396 final HttpEntity indirectEntity = new StringEntity(indirect, turtleContentType); 1397 userPost.setEntity(indirectEntity); 1398 userPost.setHeader(CONTENT_TYPE, "text/turtle"); 1399 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(userPost)); 1400 1401 // Try to create indirect container referencing readonly resource with PUT. 1402 final String indirectString = getRandomUniqueId(); 1403 final HttpPut userPut = putObjMethod(writeableResource + "/" + indirectString); 1404 setAuth(userPut, username); 1405 userPut.addHeader("Link", "<" + INDIRECT_CONTAINER.toString() + ">; rel=type"); 1406 userPut.setEntity(indirectEntity); 1407 userPut.setHeader(CONTENT_TYPE, "text/turtle"); 1408 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(userPut)); 1409 1410 // Create an user writeable resource. 1411 final HttpPost targetPost = postObjMethod(writeableResource); 1412 setAuth(targetPost, username); 1413 final String tempTarget; 1414 try (final CloseableHttpResponse resp = execute(targetPost)) { 1415 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1416 tempTarget = getLocation(resp); 1417 } 1418 1419 // Try to create indirect container referencing an available resource. 1420 final String indirect_ok = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1421 "@prefix example: <http://www.example.org/example1#> .\n" + 1422 "@prefix dc: <http://purl.org/dc/elements/1.1/> .\n" + 1423 "<> ldp:insertedContentRelation <http://example.org/test#something> ;\n" + 1424 "ldp:membershipResource <" + tempTarget + "> ;\n" + 1425 "ldp:hasMemberRelation <http://example.org/test#predicateToCreate> ;\n" + 1426 "dc:title \"The indirect container\" ."; 1427 final HttpPost userPatchPost = postObjMethod(writeableResource); 1428 setAuth(userPatchPost, username); 1429 userPatchPost.addHeader("Link", "<" + INDIRECT_CONTAINER.toString() + ">; rel=type"); 1430 final HttpEntity in_ok = new StringEntity(indirect_ok, turtleContentType); 1431 userPatchPost.setEntity(in_ok); 1432 userPatchPost.setHeader(CONTENT_TYPE, "text/turtle"); 1433 final String indirectUri; 1434 try (final CloseableHttpResponse resp = execute(userPatchPost)) { 1435 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1436 indirectUri = getLocation(resp); 1437 } 1438 1439 // Then PATCH to the readonly resource. 1440 final HttpPatch patchIndirect = new HttpPatch(indirectUri); 1441 setAuth(patchIndirect, username); 1442 final String patch_text = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1443 "DELETE { <> ldp:membershipResource ?o } \n" + 1444 "INSERT { <> ldp:membershipResource <" + targetResource + "> } \n" + 1445 "WHERE { <> ldp:membershipResource ?o }"; 1446 patchIndirect.setEntity(new StringEntity(patch_text, sparqlContentType)); 1447 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchIndirect)); 1448 1449 // Delete the ldp:membershipRelation and add it with INSERT DATA {} 1450 final String patch_delete_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1451 "DELETE DATA { <> ldp:membershipResource <" + tempTarget + "> }"; 1452 final HttpPatch patchIndirect2 = new HttpPatch(indirectUri); 1453 setAuth(patchIndirect2, username); 1454 patchIndirect2.setEntity(new StringEntity(patch_delete_relation, sparqlContentType)); 1455 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchIndirect2)); 1456 1457 final String patch_insert_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1458 "INSERT DATA { <> ldp:membershipResource <" + targetResource + "> }"; 1459 final HttpPatch patchIndirect3 = new HttpPatch(indirectUri); 1460 setAuth(patchIndirect3, username); 1461 patchIndirect3.setEntity(new StringEntity(patch_insert_relation, sparqlContentType)); 1462 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchIndirect3)); 1463 1464 // Patch the indirect to the readonly target as admin 1465 final HttpPatch patchAsAdmin = new HttpPatch(indirectUri); 1466 setAuth(patchAsAdmin, "fedoraAdmin"); 1467 patchAsAdmin.setEntity(new StringEntity(patch_text, sparqlContentType)); 1468 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchAsAdmin)); 1469 1470 // Ensure the patching happened. 1471 final HttpGet verifyGet = new HttpGet(indirectUri); 1472 setAuth(verifyGet, "fedoraAdmin"); 1473 try (final CloseableHttpResponse response = execute(verifyGet)) { 1474 final CloseableDataset dataset = getDataset(response); 1475 final DatasetGraph graph = dataset.asDatasetGraph(); 1476 assertTrue("Can't find " + targetUri + " in graph", 1477 graph.contains( 1478 Node.ANY, 1479 NodeFactory.createURI(indirectUri), 1480 MEMBERSHIP_RESOURCE.asNode(), 1481 NodeFactory.createURI(targetUri) 1482 ) 1483 ); 1484 } 1485 1486 // Try to POST a child as user 1487 final HttpPost postChild = new HttpPost(indirectUri); 1488 final String postTarget = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1489 "@prefix test: <http://example.org/test#> .\n\n" + 1490 "<> test:something <" + tempTarget + "> ."; 1491 final HttpEntity putPostChild = new StringEntity(postTarget, turtleContentType); 1492 setAuth(postChild, username); 1493 postChild.setEntity(putPostChild); 1494 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postChild)); 1495 1496 // Try to PUT a child as user 1497 final String id = getRandomUniqueId(); 1498 final HttpPut putChild = new HttpPut(indirectUri + "/" + id); 1499 setAuth(putChild, username); 1500 putChild.setEntity(putPostChild); 1501 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putChild)); 1502 1503 // Put the child as Admin 1504 setAuth(putChild, "fedoraAdmin"); 1505 assertEquals(HttpStatus.SC_CREATED, getStatus(putChild)); 1506 1507 // Try to delete the child as user 1508 final HttpDelete deleteChild = new HttpDelete(indirectUri + "/" + id); 1509 setAuth(deleteChild, username); 1510 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteChild)); 1511 1512 // Try to delete the indirect container 1513 final HttpDelete deleteIndirect = new HttpDelete(indirectUri); 1514 setAuth(deleteIndirect, username); 1515 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteIndirect)); 1516 1517 // Ensure we can still write to the writeable resource. 1518 testCanWrite(writeableResource, username); 1519 1520 } 1521 1522 @Test 1523 public void testIndirectRelationshipOK() throws IOException { 1524 final String targetResource = "/rest/" + getRandomUniqueId(); 1525 final String writeableResource = "/rest/" + getRandomUniqueId(); 1526 final String username = "user28"; 1527 1528 final String targetUri = ingestObj(targetResource); 1529 1530 final String readwriteString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1531 "<#readauthz> a acl:Authorization ;\n" + 1532 " acl:agent \"" + username + "\" ;\n" + 1533 " acl:mode acl:Read, acl:Write ;\n" + 1534 " acl:accessTo <" + targetResource + "> ."; 1535 ingestAclString(targetUri, readwriteString, "fedoraAdmin"); 1536 1537 // User can read target resource. 1538 final HttpGet get1 = getObjMethod(targetResource); 1539 setAuth(get1, username); 1540 assertEquals(HttpStatus.SC_OK, getStatus(get1)); 1541 1542 // User can patch target resource. 1543 final String patch = "INSERT DATA { <> <http://purl.org/dc/elements/1.1/title> \"Changed it\"}"; 1544 final HttpEntity patchEntity = new StringEntity(patch, sparqlContentType); 1545 try (final CloseableHttpResponse resp = (CloseableHttpResponse) PATCH(targetUri, patchEntity, 1546 username)) { 1547 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(resp)); 1548 } 1549 1550 // Make a user writable container. 1551 final String writeableUri = ingestObj(writeableResource); 1552 final String writeableAcl = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1553 "<#writeauth> a acl:Authorization ;\n" + 1554 " acl:agent \"" + username + "\" ;\n" + 1555 " acl:mode acl:Read, acl:Write ;\n" + 1556 " acl:accessTo <" + writeableResource + "> ;\n" + 1557 " acl:default <" + writeableResource + "> ."; 1558 ingestAclString(writeableUri, writeableAcl, "fedoraAdmin"); 1559 1560 // Ensure we can write to the writeable resource. 1561 testCanWrite(writeableResource, username); 1562 1563 // Try to create indirect container referencing writeable resource with POST. 1564 final HttpPost userPost = postObjMethod(writeableResource); 1565 setAuth(userPost, username); 1566 userPost.addHeader("Link", "<" + INDIRECT_CONTAINER.toString() + ">; rel=type"); 1567 final String indirect = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1568 "@prefix test: <http://example.org/test#> .\n\n" + 1569 "<> ldp:insertedContentRelation test:something ;" + 1570 "ldp:membershipResource <" + targetResource + "> ;" + 1571 "ldp:hasMemberRelation test:predicateToCreate ."; 1572 final HttpEntity indirectEntity = new StringEntity(indirect, turtleContentType); 1573 userPost.setEntity(new StringEntity(indirect, turtleContentType)); 1574 userPost.setHeader("Content-type", "text/turtle"); 1575 assertEquals(HttpStatus.SC_CREATED, getStatus(userPost)); 1576 1577 // Try to create indirect container referencing writeable resource with PUT. 1578 final String indirectString = getRandomUniqueId(); 1579 final HttpPut userPut = putObjMethod(writeableResource + "/" + indirectString); 1580 setAuth(userPut, username); 1581 userPut.addHeader("Link", "<" + INDIRECT_CONTAINER.toString() + ">; rel=type"); 1582 userPut.setEntity(indirectEntity); 1583 userPut.setHeader("Content-type", "text/turtle"); 1584 assertEquals(HttpStatus.SC_CREATED, getStatus(userPut)); 1585 1586 // Create an user writeable resource. 1587 final HttpPost targetPost = postObjMethod(writeableResource); 1588 setAuth(targetPost, username); 1589 final String tempTarget; 1590 try (final CloseableHttpResponse resp = execute(targetPost)) { 1591 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1592 tempTarget = getLocation(resp); 1593 } 1594 1595 // Try to create indirect container referencing an available resource. 1596 final String indirect_ok = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1597 "@prefix test: <http://example.org/test#> .\n\n" + 1598 "<> ldp:insertedContentRelation test:something ;" + 1599 "ldp:membershipResource <" + tempTarget + "> ;" + 1600 "ldp:hasMemberRelation test:predicateToCreate ."; 1601 final HttpPost userPatchPost = postObjMethod(writeableResource); 1602 setAuth(userPatchPost, username); 1603 userPatchPost.addHeader("Link", "<" + INDIRECT_CONTAINER.toString() + ">; rel=type"); 1604 userPatchPost.setEntity(new StringEntity(indirect_ok, turtleContentType)); 1605 userPatchPost.setHeader("Content-type", "text/turtle"); 1606 final String indirectUri; 1607 try (final CloseableHttpResponse resp = execute(userPatchPost)) { 1608 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1609 indirectUri = getLocation(resp); 1610 } 1611 1612 // Then PATCH to the writeable resource. 1613 final HttpPatch patchIndirect = new HttpPatch(indirectUri); 1614 setAuth(patchIndirect, username); 1615 final String patch_text = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1616 "DELETE { <> ldp:membershipResource ?o } \n" + 1617 "INSERT { <> ldp:membershipResource <" + targetResource + "> } \n" + 1618 "WHERE { <> ldp:membershipResource ?o }"; 1619 patchIndirect.setEntity(new StringEntity(patch_text, sparqlContentType)); 1620 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchIndirect)); 1621 1622 // Delete the ldp:membershipRelation and add it back 1623 final String patch_delete_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1624 "DELETE DATA { <> ldp:membershipResource <" + targetResource + "> }"; 1625 final HttpPatch patchIndirect2 = new HttpPatch(indirectUri); 1626 setAuth(patchIndirect2, username); 1627 patchIndirect2.setEntity(new StringEntity(patch_delete_relation, sparqlContentType)); 1628 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchIndirect2)); 1629 1630 // Cannot insert membershipResource without deleting the default value 1631 final String patch_insert_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1632 "DELETE { <> ldp:membershipResource ?o } \n" + 1633 "INSERT { <> ldp:membershipResource <" + targetResource + "> } \n" + 1634 "WHERE { <> ldp:membershipResource ?o }"; 1635 final HttpPatch patchIndirect3 = new HttpPatch(indirectUri); 1636 setAuth(patchIndirect3, username); 1637 patchIndirect3.setEntity(new StringEntity(patch_insert_relation, sparqlContentType)); 1638 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchIndirect3)); 1639 1640 // Ensure we can still write to the writeable resource. 1641 testCanWrite(writeableResource, username); 1642 1643 } 1644 1645 @Ignore("Until FCREPO-3310 and FCREPO-3311 are resolved") 1646 @Test 1647 public void testDirectRelationshipForbidden() throws IOException { 1648 final String targetResource = "/rest/" + getRandomUniqueId(); 1649 final String writeableResource = "/rest/" + getRandomUniqueId(); 1650 final String username = "user28"; 1651 1652 final String targetUri = ingestObj(targetResource); 1653 1654 final String readonlyString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1655 "<#readauthz> a acl:Authorization ;\n" + 1656 " acl:agent \"" + username + "\" ;\n" + 1657 " acl:mode acl:Read ;\n" + 1658 " acl:accessTo <" + targetResource + "> ."; 1659 ingestAclString(targetUri, readonlyString, "fedoraAdmin"); 1660 1661 // User can read target resource. 1662 final HttpGet get1 = getObjMethod(targetResource); 1663 setAuth(get1, username); 1664 assertEquals(HttpStatus.SC_OK, getStatus(get1)); 1665 1666 // User can't patch target resource. 1667 final String patch = "INSERT DATA { <> <http://purl.org/dc/elements/1.1/title> \"Changed it\"}"; 1668 final HttpEntity patchEntity = new StringEntity(patch, sparqlContentType); 1669 try (final CloseableHttpResponse resp = (CloseableHttpResponse) PATCH(targetUri, patchEntity, 1670 username)) { 1671 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(resp)); 1672 } 1673 1674 // Make a user writable container. 1675 final String writeableUri = ingestObj(writeableResource); 1676 final String writeableAcl = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1677 "<#writeauth> a acl:Authorization ;\n" + 1678 " acl:agent \"" + username + "\" ;\n" + 1679 " acl:mode acl:Read, acl:Write ;\n" + 1680 " acl:accessTo <" + writeableResource + "> ;\n" + 1681 " acl:default <" + writeableResource + "> ."; 1682 ingestAclString(writeableUri, writeableAcl, "fedoraAdmin"); 1683 1684 // Ensure we can write to writeable resource. 1685 testCanWrite(writeableResource, username); 1686 1687 // Try to create direct container referencing readonly resource with POST. 1688 final HttpPost userPost = postObjMethod(writeableResource); 1689 setAuth(userPost, username); 1690 userPost.addHeader("Link", "<" + DIRECT_CONTAINER.toString() + ">; rel=type"); 1691 final String direct = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1692 "@prefix test: <http://example.org/test#> .\n\n" + 1693 "<> ldp:membershipResource <" + targetResource + "> ;" + 1694 "ldp:hasMemberRelation test:predicateToCreate ."; 1695 final HttpEntity directEntity = new StringEntity(direct, turtleContentType); 1696 userPost.setEntity(directEntity); 1697 userPost.setHeader("Content-type", "text/turtle"); 1698 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(userPost)); 1699 1700 // Try to create direct container referencing readonly resource with PUT. 1701 final String indirectString = getRandomUniqueId(); 1702 final HttpPut userPut = putObjMethod(writeableResource + "/" + indirectString); 1703 setAuth(userPut, username); 1704 userPut.addHeader("Link", "<" + DIRECT_CONTAINER.toString() + ">; rel=type"); 1705 userPut.setEntity(directEntity); 1706 userPut.setHeader("Content-type", "text/turtle"); 1707 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(userPut)); 1708 1709 // Create an user writeable resource. 1710 final HttpPost targetPost = postObjMethod(writeableResource); 1711 setAuth(targetPost, username); 1712 final String tempTarget; 1713 try (final CloseableHttpResponse resp = execute(targetPost)) { 1714 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1715 tempTarget = getLocation(resp); 1716 } 1717 1718 // Try to create direct container referencing an available resource. 1719 final String direct_ok = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1720 "@prefix test: <http://example.org/test#> .\n\n" + 1721 "<> ldp:membershipResource <" + tempTarget + "> ;\n" + 1722 "ldp:hasMemberRelation test:predicateToCreate ."; 1723 final HttpPost userPatchPost = postObjMethod(writeableResource); 1724 setAuth(userPatchPost, username); 1725 userPatchPost.addHeader("Link", "<" + DIRECT_CONTAINER.toString() + ">; rel=type"); 1726 userPatchPost.setEntity(new StringEntity(direct_ok, turtleContentType)); 1727 userPatchPost.setHeader("Content-type", "text/turtle"); 1728 final String directUri; 1729 try (final CloseableHttpResponse resp = execute(userPatchPost)) { 1730 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1731 directUri = getLocation(resp); 1732 } 1733 1734 // Then PATCH to the readonly resource. 1735 final HttpPatch patchDirect = new HttpPatch(directUri); 1736 setAuth(patchDirect, username); 1737 final String patch_text = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1738 "DELETE { <> ldp:membershipResource ?o } \n" + 1739 "INSERT { <> ldp:membershipResource <" + targetResource + "> } \n" + 1740 "WHERE { <> ldp:membershipResource ?o }"; 1741 patchDirect.setEntity(new StringEntity(patch_text, sparqlContentType)); 1742 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchDirect)); 1743 1744 // Delete the ldp:membershipRelation and add it with INSERT DATA {} 1745 final String patch_delete_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1746 "DELETE DATA { <> ldp:membershipResource <" + tempTarget + "> }"; 1747 final HttpPatch patchDirect2 = new HttpPatch(directUri); 1748 setAuth(patchDirect2, username); 1749 patchDirect2.setEntity(new StringEntity(patch_delete_relation, sparqlContentType)); 1750 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchDirect2)); 1751 1752 final String patch_insert_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1753 "INSERT DATA { <> ldp:membershipResource <" + targetResource + "> }"; 1754 final HttpPatch patchDirect3 = new HttpPatch(directUri); 1755 setAuth(patchDirect3, username); 1756 patchDirect3.setEntity(new StringEntity(patch_insert_relation, sparqlContentType)); 1757 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchDirect3)); 1758 1759 // Patch the indirect to the readonly target as admin 1760 final HttpPatch patchAsAdmin = new HttpPatch(directUri); 1761 setAuth(patchAsAdmin, "fedoraAdmin"); 1762 patchAsAdmin.setEntity(new StringEntity(patch_text, sparqlContentType)); 1763 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchAsAdmin)); 1764 1765 // Ensure the patching happened. 1766 final HttpGet verifyGet = new HttpGet(directUri); 1767 setAuth(verifyGet, "fedoraAdmin"); 1768 try (final CloseableHttpResponse response = execute(verifyGet)) { 1769 final CloseableDataset dataset = getDataset(response); 1770 final DatasetGraph graph = dataset.asDatasetGraph(); 1771 assertTrue("Can't find " + targetUri + " in graph", 1772 graph.contains( 1773 Node.ANY, 1774 NodeFactory.createURI(directUri), 1775 MEMBERSHIP_RESOURCE.asNode(), 1776 NodeFactory.createURI(targetUri) 1777 ) 1778 ); 1779 } 1780 1781 // Try to POST a child as user 1782 final HttpPost postChild = new HttpPost(directUri); 1783 final String postTarget = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1784 "@prefix test: <http://example.org/test#> .\n" + 1785 "<> test:something <" + tempTarget + "> ."; 1786 final HttpEntity putPostChild = new StringEntity(postTarget, turtleContentType); 1787 setAuth(postChild, username); 1788 postChild.setEntity(putPostChild); 1789 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postChild)); 1790 1791 // Try to PUT a child as user 1792 final String id = getRandomUniqueId(); 1793 final HttpPut putChild = new HttpPut(directUri + "/" + id); 1794 setAuth(putChild, username); 1795 putChild.setEntity(putPostChild); 1796 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putChild)); 1797 1798 // Put the child as Admin 1799 setAuth(putChild, "fedoraAdmin"); 1800 assertEquals(HttpStatus.SC_CREATED, getStatus(putChild)); 1801 1802 // Try to delete the child as user 1803 final HttpDelete deleteChild = new HttpDelete(directUri + "/" + id); 1804 setAuth(deleteChild, username); 1805 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteChild)); 1806 1807 // Try to delete the indirect container 1808 final HttpDelete deleteIndirect = new HttpDelete(directUri); 1809 setAuth(deleteIndirect, username); 1810 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteIndirect)); 1811 1812 // Ensure we can still write to the writeable resource. 1813 testCanWrite(writeableResource, username); 1814 1815 } 1816 1817 @Test 1818 public void testDirectRelationshipsOk() throws IOException { 1819 final String targetResource = "/rest/" + getRandomUniqueId(); 1820 final String writeableResource = "/rest/" + getRandomUniqueId(); 1821 final String username = "user28"; 1822 1823 final String targetUri = ingestObj(targetResource); 1824 1825 final String readwriteString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1826 "<#readauthz> a acl:Authorization ;\n" + 1827 " acl:agent \"" + username + "\" ;\n" + 1828 " acl:mode acl:Read, acl:Write ;\n" + 1829 " acl:accessTo <" + targetResource + "> ."; 1830 ingestAclString(targetUri, readwriteString, "fedoraAdmin"); 1831 1832 // User can read target resource. 1833 final HttpGet get1 = getObjMethod(targetResource); 1834 setAuth(get1, username); 1835 assertEquals(HttpStatus.SC_OK, getStatus(get1)); 1836 1837 // User can patch target resource. 1838 final String patch = "INSERT DATA { <> <http://purl.org/dc/elements/1.1/title> \"Changed it\"}"; 1839 final HttpEntity patchEntity = new StringEntity(patch, sparqlContentType); 1840 try (final CloseableHttpResponse resp = (CloseableHttpResponse) PATCH(targetUri, patchEntity, 1841 username)) { 1842 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(resp)); 1843 } 1844 1845 // Make a user writable container. 1846 final String writeableUri = ingestObj(writeableResource); 1847 final String writeableAcl = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1848 "<#writeauth> a acl:Authorization ;\n" + 1849 " acl:agent \"" + username + "\" ;\n" + 1850 " acl:mode acl:Read, acl:Write ;\n" + 1851 " acl:accessTo <" + writeableResource + "> ;\n" + 1852 " acl:default <" + writeableResource + "> ."; 1853 ingestAclString(writeableUri, writeableAcl, "fedoraAdmin"); 1854 1855 // Ensure we can write to the writeable resource. 1856 testCanWrite(writeableResource, username); 1857 1858 // Try to create direct container referencing writeable resource with POST. 1859 final HttpPost userPost = postObjMethod(writeableResource); 1860 setAuth(userPost, username); 1861 userPost.addHeader("Link", "<" + DIRECT_CONTAINER.toString() + ">; rel=type"); 1862 final String indirect = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1863 "@prefix test: <http://example.org/test#> .\n\n" + 1864 "<> ldp:membershipResource <" + targetResource + "> ;\n" + 1865 "ldp:hasMemberRelation test:predicateToCreate ."; 1866 final HttpEntity directEntity = new StringEntity(indirect, turtleContentType); 1867 userPost.setEntity(new StringEntity(indirect, turtleContentType)); 1868 userPost.setHeader("Content-type", "text/turtle"); 1869 assertEquals(HttpStatus.SC_CREATED, getStatus(userPost)); 1870 1871 // Try to create direct container referencing writeable resource with PUT. 1872 final String directString = getRandomUniqueId(); 1873 final HttpPut userPut = putObjMethod(writeableResource + "/" + directString); 1874 setAuth(userPut, username); 1875 userPut.addHeader("Link", "<" + DIRECT_CONTAINER.toString() + ">; rel=type"); 1876 userPut.setEntity(directEntity); 1877 userPut.setHeader("Content-type", "text/turtle"); 1878 assertEquals(HttpStatus.SC_CREATED, getStatus(userPut)); 1879 1880 // Create an user writeable resource. 1881 final HttpPost targetPost = postObjMethod(writeableResource); 1882 setAuth(targetPost, username); 1883 final String tempTarget; 1884 try (final CloseableHttpResponse resp = execute(targetPost)) { 1885 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1886 tempTarget = getLocation(resp); 1887 } 1888 1889 // Try to create direct container referencing an available resource. 1890 final String direct_ok = "@prefix ldp: <http://www.w3.org/ns/ldp#> .\n" + 1891 "@prefix test: <http://example.org/test#> .\n\n" + 1892 "<> ldp:membershipResource <" + tempTarget + "> ;\n" + 1893 "ldp:hasMemberRelation test:predicateToCreate ."; 1894 final HttpPost userPatchPost = postObjMethod(writeableResource); 1895 setAuth(userPatchPost, username); 1896 userPatchPost.addHeader("Link", "<" + DIRECT_CONTAINER.toString() + ">; rel=type"); 1897 userPatchPost.setEntity(new StringEntity(direct_ok, turtleContentType)); 1898 userPatchPost.setHeader("Content-type", "text/turtle"); 1899 final String directUri; 1900 try (final CloseableHttpResponse resp = execute(userPatchPost)) { 1901 assertEquals(HttpStatus.SC_CREATED, getStatus(resp)); 1902 directUri = getLocation(resp); 1903 } 1904 1905 // Then PATCH to the readonly resource. 1906 final HttpPatch patchDirect = new HttpPatch(directUri); 1907 setAuth(patchDirect, username); 1908 final String patch_text = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1909 "DELETE { <> ldp:membershipResource ?o } \n" + 1910 "INSERT { <> ldp:membershipResource <" + targetResource + "> } \n" + 1911 "WHERE { <> ldp:membershipResource ?o }"; 1912 patchDirect.setEntity(new StringEntity(patch_text, sparqlContentType)); 1913 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchDirect)); 1914 1915 // Delete the ldp:membershipRelation and add it with INSERT 1916 final String patch_delete_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1917 "DELETE DATA { <> ldp:membershipResource <" + targetResource + "> }"; 1918 final HttpPatch patchDirect2 = new HttpPatch(directUri); 1919 setAuth(patchDirect2, username); 1920 patchDirect2.setEntity(new StringEntity(patch_delete_relation, sparqlContentType)); 1921 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchDirect2)); 1922 1923 // Cannot insert membershipResource without deleting the default value 1924 final String patch_insert_relation = "prefix ldp: <http://www.w3.org/ns/ldp#> \n" + 1925 "DELETE { <> ldp:membershipResource ?o } \n" + 1926 "INSERT { <> ldp:membershipResource <" + targetResource + "> } \n" + 1927 "WHERE { <> ldp:membershipResource ?o }"; 1928 final HttpPatch patchDirect3 = new HttpPatch(directUri); 1929 setAuth(patchDirect3, username); 1930 patchDirect3.setEntity(new StringEntity(patch_insert_relation, sparqlContentType)); 1931 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchDirect3)); 1932 1933 // Ensure we can write to the writeable resource. 1934 testCanWrite(writeableResource, username); 1935 } 1936 1937 @Test 1938 public void testSameInTransaction() throws Exception { 1939 final String targetResource = "/rest/" + getRandomUniqueId(); 1940 final String username = "user28"; 1941 // Make a basic container. 1942 final String targetUri = ingestObj(targetResource); 1943 final String readwriteString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 1944 "<#readauthz> a acl:Authorization ;\n" + 1945 " acl:agent \"" + username + "\" ;\n" + 1946 " acl:mode acl:Read, acl:Write ;\n" + 1947 " acl:accessTo <" + targetResource + "> ."; 1948 // Allow user28 to read and write this object. 1949 ingestAclString(targetUri, readwriteString, "fedoraAdmin"); 1950 // Test that user28 can read target resource. 1951 final HttpGet getAllowed1 = getObjMethod(targetResource); 1952 setAuth(getAllowed1, username); 1953 assertEquals(HttpStatus.SC_OK, getStatus(getAllowed1)); 1954 // Test that user28 can patch target resource. 1955 final HttpPatch patchAllowed1 = patchObjMethod(targetResource); 1956 final String patchString = "prefix dc: <http://purl.org/dc/elements/1.1/> INSERT { <> dc:title " + 1957 "\"new title\" } WHERE {}"; 1958 final StringEntity patchEntity = new StringEntity(patchString, Charsets.UTF8_CHARSET); 1959 patchAllowed1.setEntity(patchEntity); 1960 patchAllowed1.setHeader(CONTENT_TYPE, "application/sparql-update"); 1961 setAuth(patchAllowed1, username); 1962 assertEquals(SC_NO_CONTENT, getStatus(patchAllowed1)); 1963 // Test that user28 can post to target resource. 1964 final HttpPost postAllowed1 = postObjMethod(targetResource); 1965 setAuth(postAllowed1, username); 1966 final String childResource; 1967 try (final CloseableHttpResponse response = execute(postAllowed1)) { 1968 assertEquals(SC_CREATED, getStatus(postAllowed1)); 1969 childResource = getLocation(response); 1970 } 1971 // Test that user28 cannot patch the child resource (ACL is not acl:default). 1972 final HttpPatch patchDisallowed1 = new HttpPatch(childResource); 1973 patchDisallowed1.setEntity(patchEntity); 1974 patchDisallowed1.setHeader(CONTENT_TYPE, "application/sparql-update"); 1975 setAuth(patchDisallowed1, username); 1976 assertEquals(SC_FORBIDDEN, getStatus(patchDisallowed1)); 1977 // Test that user28 cannot post to a child resource. 1978 final HttpPost postDisallowed1 = new HttpPost(childResource); 1979 setAuth(postDisallowed1, username); 1980 assertEquals(SC_FORBIDDEN, getStatus(postDisallowed1)); 1981 // Test another user cannot access the target resource. 1982 final HttpGet getDisallowed1 = getObjMethod(targetResource); 1983 setAuth(getDisallowed1, "user400"); 1984 assertEquals(SC_FORBIDDEN, getStatus(getDisallowed1)); 1985 // Get the transaction endpoint. 1986 final HttpGet getTransactionEndpoint = getObjMethod("/rest"); 1987 setAuth(getTransactionEndpoint, "fedoraAdmin"); 1988 final String transactionEndpoint; 1989 final Pattern linkHeaderMatcher = Pattern.compile("<([^>]+)>"); 1990 try (final CloseableHttpResponse response = execute(getTransactionEndpoint)) { 1991 final var linkheaders = getLinkHeaders(response); 1992 transactionEndpoint = linkheaders.stream() 1993 .filter(t -> t.contains("http://fedora.info/definitions/v4/transaction#endpoint")) 1994 .map(t -> { 1995 final var matches = linkHeaderMatcher.matcher(t); 1996 matches.find(); 1997 return matches.group(1); 1998 }) 1999 .findFirst() 2000 .orElseThrow(Exception::new); 2001 } 2002 // Create a transaction. 2003 final HttpPost postTransaction = new HttpPost(transactionEndpoint); 2004 setAuth(postTransaction, "fedoraAdmin"); 2005 final String transactionId; 2006 try (final CloseableHttpResponse response = execute(postTransaction)) { 2007 assertEquals(SC_CREATED, getStatus(response)); 2008 transactionId = getLocation(response); 2009 } 2010 // Test user28 can post to target resource in a transaction. 2011 final HttpPost postChildInTx = postObjMethod(targetResource); 2012 setAuth(postChildInTx, username); 2013 postChildInTx.setHeader(ATOMIC_ID_HEADER, transactionId); 2014 final String txChild; 2015 try (final CloseableHttpResponse response = execute(postChildInTx)) { 2016 assertEquals(SC_CREATED, getStatus(response)); 2017 txChild = getLocation(response); 2018 } 2019 // Test user28 cannot post to the child in a transaction. 2020 final HttpPost postDisallowed2 = new HttpPost(txChild); 2021 setAuth(postDisallowed2, username); 2022 postDisallowed2.setHeader(ATOMIC_ID_HEADER, transactionId); 2023 assertEquals(SC_FORBIDDEN, getStatus(postDisallowed2)); 2024 } 2025 2026 @Test 2027 public void testBinaryAndDescriptionAllowed() throws Exception { 2028 final String targetResource = "/rest/" + getRandomUniqueId(); 2029 final String username = "user88"; 2030 // Make a basic container. 2031 final String targetUri = ingestObj(targetResource); 2032 final String readwriteString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 2033 "<#readauthz> a acl:Authorization ;\n" + 2034 " acl:agent \"" + username + "\" ;\n" + 2035 " acl:mode acl:Read, acl:Write ;\n" + 2036 " acl:default <" + targetResource + "> ;" + 2037 " acl:accessTo <" + targetResource + "> ."; 2038 // Allow user to read and write this object. 2039 ingestAclString(targetUri, readwriteString, "fedoraAdmin"); 2040 // user creates a binary 2041 final HttpPost newBinary = postObjMethod(targetResource); 2042 setAuth(newBinary, username); 2043 newBinary.setHeader(CONTENT_TYPE, "text/plain"); 2044 final StringEntity stringData = new StringEntity("This is some data", Charsets.UTF8_CHARSET); 2045 newBinary.setEntity(stringData); 2046 final String binaryLocation; 2047 try (final CloseableHttpResponse response = execute(newBinary)) { 2048 assertEquals(SC_CREATED, getStatus(response)); 2049 binaryLocation = getLocation(response); 2050 } 2051 // Try PUTting a new binary 2052 final HttpPut putAgain = new HttpPut(binaryLocation); 2053 setAuth(putAgain, username); 2054 putAgain.setHeader(CONTENT_TYPE, "text/plain"); 2055 final StringEntity newStringData = new StringEntity("Some other data", Charsets.UTF8_CHARSET); 2056 putAgain.setEntity(newStringData); 2057 assertEquals(SC_NO_CONTENT, getStatus(putAgain)); 2058 // Try PUTting to binary description 2059 final HttpPut putDesc = new HttpPut(binaryLocation + "/" + FCR_METADATA); 2060 setAuth(putDesc, username); 2061 putDesc.setHeader(CONTENT_TYPE, "text/turtle"); 2062 final StringEntity putDescData = new StringEntity("<> <http://purl.org/dc/elements/1.1/title> \"Some title\".", 2063 Charsets.UTF8_CHARSET); 2064 putDesc.setEntity(putDescData); 2065 assertEquals(SC_NO_CONTENT, getStatus(putDesc)); 2066 // Check the title 2067 assertPredicateValue(binaryLocation + "/" + FCR_METADATA, "http://purl.org/dc/elements/1.1/title", 2068 "Some title"); 2069 // Try PATCHing to binary description 2070 final HttpPatch patchDesc = new HttpPatch(binaryLocation + "/" + FCR_METADATA); 2071 setAuth(patchDesc, username); 2072 patchDesc.setHeader(CONTENT_TYPE, "application/sparql-update"); 2073 final StringEntity patchDescData = new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/> " + 2074 "DELETE { <> dc:title ?o } INSERT { <> dc:title \"Some different title\" } WHERE { <> dc:title ?o }", 2075 Charsets.UTF8_CHARSET); 2076 patchDesc.setEntity(patchDescData); 2077 assertEquals(SC_NO_CONTENT, getStatus(patchDesc)); 2078 // Check the title 2079 assertPredicateValue(binaryLocation + "/" + FCR_METADATA, "http://purl.org/dc/elements/1.1/title", 2080 "Some different title"); 2081 2082 } 2083 2084 @Test 2085 public void testRequestWithEmptyPath() throws Exception { 2086 // Ensure HttpClient does not remove empty paths 2087 final RequestConfig config = RequestConfig.custom().setNormalizeUri(false).build(); 2088 2089 final String username = "testUser92"; 2090 final String parent = getRandomUniqueId(); 2091 final HttpPost postParent = postObjMethod(); 2092 postParent.setHeader("Slug", parent); 2093 setAuth(postParent, "fedoraAdmin"); 2094 final String parentUri; 2095 try (final CloseableHttpResponse response = execute(postParent)) { 2096 assertEquals(CREATED.getStatusCode(), getStatus(response)); 2097 parentUri = getLocation(response); 2098 } 2099 // Make parent only accessible to fedoraAdmin 2100 final String parentAcl = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 2101 "<#readauthz> a acl:Authorization ;\n" + 2102 " acl:agent \"fedoraAdmin\" ;\n" + 2103 " acl:mode acl:Read, acl:Write ;\n" + 2104 " acl:accessTo <" + parentUri + "> ."; 2105 ingestAclString(parentUri, parentAcl, "fedoraAdmin"); 2106 // Admin can see parent 2107 final HttpGet getAdminParent = getObjMethod(parent); 2108 setAuth(getAdminParent, "fedoraAdmin"); 2109 assertEquals(OK.getStatusCode(), getStatus(getAdminParent)); 2110 final HttpGet getParent = getObjMethod(parent); 2111 setAuth(getParent, username); 2112 // testUser92 cannot see parent. 2113 assertEquals(FORBIDDEN.getStatusCode(), getStatus(getParent)); 2114 2115 final String child = getRandomUniqueId(); 2116 final HttpPost postChild = postObjMethod(parent); 2117 postChild.setHeader("Slug", child); 2118 setAuth(postChild, "fedoraAdmin"); 2119 final String childUri; 2120 try (final CloseableHttpResponse response = execute(postChild)) { 2121 assertEquals(CREATED.getStatusCode(), getStatus(response)); 2122 childUri = getLocation(response); 2123 } 2124 // Make child accessible to testUser92 2125 final String childAcl = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 2126 "<#readauthz> a acl:Authorization ;\n" + 2127 " acl:agent \"" + username + "\" ;\n" + 2128 " acl:mode acl:Read, acl:Write ;\n" + 2129 " acl:accessTo <" + childUri + "> ."; 2130 ingestAclString(childUri, childAcl, "fedoraAdmin"); 2131 // Admin can see child. 2132 final HttpGet getAdminChild = getObjMethod(parent + "/" + child); 2133 setAuth(getAdminChild, "fedoraAdmin"); 2134 assertEquals(OK.getStatusCode(), getStatus(getAdminChild)); 2135 2136 // testUser92 can see child. 2137 final HttpGet getChild = getObjMethod(parent + "/" + child); 2138 setAuth(getChild, username); 2139 assertEquals(OK.getStatusCode(), getStatus(getChild)); 2140 2141 // Admin bypasses ACL resolution gets 409. 2142 final HttpGet getAdminRequest = getObjMethod(parent + "//" + child); 2143 setAuth(getAdminRequest, "fedoraAdmin"); 2144 getAdminRequest.setConfig(config); 2145 assertEquals(BAD_REQUEST.getStatusCode(), getStatus(getAdminRequest)); 2146 // User 2147 final HttpGet getUserRequest = getObjMethod(parent + "//" + child); 2148 setAuth(getUserRequest, username); 2149 getUserRequest.setConfig(config); 2150 assertEquals(BAD_REQUEST.getStatusCode(), getStatus(getUserRequest)); 2151 } 2152 2153 @Test 2154 public void testGetWithEmbeddedResourcesOk() throws Exception { 2155 final String targetResource = "/rest/" + getRandomUniqueId(); 2156 final String childResource = targetResource + "/" + getRandomUniqueId(); 2157 final String username = "user88"; 2158 // Make a basic container. 2159 final String targetUri = ingestObj(targetResource); 2160 ingestObj(childResource); 2161 final String readwriteString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 2162 "<#readauthz> a acl:Authorization ;\n" + 2163 " acl:agent \"" + username + "\" ;\n" + 2164 " acl:mode acl:Read, acl:Write ;\n" + 2165 " acl:default <" + targetResource + "> ;" + 2166 " acl:accessTo <" + targetResource + "> ."; 2167 // Allow user to read and write this object. 2168 ingestAclString(targetUri, readwriteString, "fedoraAdmin"); 2169 2170 final HttpGet getAdminChild = new HttpGet(targetUri); 2171 setAuth(getAdminChild, username); 2172 getAdminChild.addHeader("Prefer", "return=representation; include=\"" + EMBED_CONTAINED + "\""); 2173 assertEquals(OK.getStatusCode(), getStatus(getAdminChild)); 2174 } 2175 2176 @Test 2177 public void testGetWithEmbeddedResourceDenied() throws Exception { 2178 final String targetResource = "/rest/" + getRandomUniqueId(); 2179 final String childResource = targetResource + "/" + getRandomUniqueId(); 2180 final String username = "user88"; 2181 // Make a basic container. 2182 final String targetUri = ingestObj(targetResource); 2183 final String readwriteString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 2184 "<#readauthz> a acl:Authorization ;\n" + 2185 " acl:agent \"" + username + "\" ;\n" + 2186 " acl:mode acl:Read, acl:Write ;\n" + 2187 " acl:accessTo <" + targetResource + "> ."; 2188 // Allow user to read and write this object. 2189 ingestAclString(targetUri, readwriteString, "fedoraAdmin"); 2190 2191 final String childUri = ingestObj(childResource); 2192 final String noAccessString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 2193 "<#readauthz> a acl:Authorization ;\n" + 2194 " acl:agent \"fedoraAdmin\" ;\n" + 2195 " acl:mode acl:Read, acl:Write ;\n" + 2196 " acl:accessTo <" + childResource + "> ."; 2197 ingestAclString(childUri, noAccessString, "fedoraAdmin"); 2198 2199 // Can get the target. 2200 final HttpGet getTarget = new HttpGet(targetUri); 2201 setAuth(getTarget, username); 2202 assertEquals(OK.getStatusCode(), getStatus(getTarget)); 2203 2204 // Can't get the child. 2205 final HttpGet getChild = new HttpGet(childUri); 2206 setAuth(getChild, username); 2207 assertEquals(FORBIDDEN.getStatusCode(), getStatus(getChild)); 2208 2209 // So you can't get the target with embedded resources. 2210 final HttpGet getAdminChild = new HttpGet(targetUri); 2211 setAuth(getAdminChild, username); 2212 getAdminChild.addHeader("Prefer", "return=representation; include=\"" + EMBED_CONTAINED + "\""); 2213 assertEquals(FORBIDDEN.getStatusCode(), getStatus(getAdminChild)); 2214 } 2215 2216 @Test 2217 public void testDeepDeleteAllowed() throws Exception { 2218 final String targetResource = "/rest/" + getRandomUniqueId(); 2219 final String username = "user88"; 2220 // Make a basic container. 2221 final String targetUri = ingestObj(targetResource); 2222 final String readwriteString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 2223 "<#readauthz> a acl:Authorization ;\n" + 2224 " acl:agent \"" + username + "\" ;\n" + 2225 " acl:mode acl:Read, acl:Write ;\n" + 2226 " acl:accessTo <" + targetResource + "> ;\n" + 2227 " acl:default <" + targetResource + "> ."; 2228 // Allow user to read and write this object. 2229 ingestAclString(targetUri, readwriteString, "fedoraAdmin"); 2230 2231 final String child1 = targetResource + "/" + getRandomUniqueId(); 2232 final String child2 = targetResource + "/" + getRandomUniqueId(); 2233 final String child1_1 = child1 + "/" + getRandomUniqueId(); 2234 final String child2_1 = child2 + "/" + getRandomUniqueId(); 2235 ingestObj(child1); 2236 ingestObj(child2); 2237 ingestObj(child1_1); 2238 ingestObj(child2_1); 2239 2240 assertGetRequest(targetUri, username, OK); 2241 assertGetRequest(serverAddress + child1, username, OK); 2242 assertGetRequest(serverAddress + child2, username, OK); 2243 assertGetRequest(serverAddress + child1_1, username, OK); 2244 assertGetRequest(serverAddress + child2_1, username, OK); 2245 2246 final var delete = new HttpDelete(targetUri); 2247 setAuth(delete, username); 2248 assertEquals(NO_CONTENT.getStatusCode(), getStatus(delete)); 2249 } 2250 2251 @Test 2252 public void testDeepDeleteFailed() throws Exception { 2253 final String targetResource = "/rest/" + getRandomUniqueId(); 2254 final String username = "user88"; 2255 // Make a basic container. 2256 final String targetUri = ingestObj(targetResource); 2257 final String readwriteString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 2258 "<#readauthz> a acl:Authorization ;\n" + 2259 " acl:agent \"" + username + "\" ;\n" + 2260 " acl:mode acl:Read, acl:Write ;\n" + 2261 " acl:accessTo <" + targetResource + "> ;\n" + 2262 " acl:default <" + targetResource + "> ."; 2263 // Allow user to read and write this object. 2264 ingestAclString(targetUri, readwriteString, "fedoraAdmin"); 2265 2266 final String child1 = targetResource + "/" + getRandomUniqueId(); 2267 final String child2 = targetResource + "/" + getRandomUniqueId(); 2268 final String child1_1 = child1 + "/" + getRandomUniqueId(); 2269 final String child2_1 = child2 + "/" + getRandomUniqueId(); 2270 ingestObj(child1); 2271 ingestObj(child2); 2272 ingestObj(child1_1); 2273 final String child2_1_URI = ingestObj(child2_1); 2274 final String noAccessString = "@prefix acl: <http://www.w3.org/ns/auth/acl#> .\n" + 2275 "<#readauthz> a acl:Authorization ;\n" + 2276 " acl:agent \"fedoraAdmin\" ;\n" + 2277 " acl:mode acl:Read, acl:Write ;\n" + 2278 " acl:accessTo <" + child2_1 + "> ."; 2279 ingestAclString(child2_1_URI, noAccessString, "fedoraAdmin"); 2280 2281 assertGetRequest(targetUri, username, OK); 2282 assertGetRequest(serverAddress + child1, username, OK); 2283 assertGetRequest(serverAddress + child2, username, OK); 2284 assertGetRequest(serverAddress + child1_1, username, OK); 2285 assertGetRequest(serverAddress + child2_1, username, FORBIDDEN); 2286 2287 final var delete = new HttpDelete(targetUri); 2288 setAuth(delete, username); 2289 assertEquals(FORBIDDEN.getStatusCode(), getStatus(delete)); 2290 } 2291 2292 @Test 2293 public void testTransactionExceptions() throws Exception { 2294 // Ensure both admin and users get 409 for invalid transaction ids. 2295 final var invalidIx = serverAddress + FCR_TX + "/fake"; 2296 assertGetRequest(serverAddress, "fedoraAdmin", invalidIx, CONFLICT); 2297 assertGetRequest(serverAddress, "testuser", invalidIx, CONFLICT); 2298 // Ensure both admin and users get 409 for a non-existant transactions. 2299 final var fakeTx = serverAddress + FCR_TX + "/" + getRandomUniqueId(); 2300 assertGetRequest(serverAddress, "fedoraAdmin", fakeTx, CONFLICT); 2301 assertGetRequest(serverAddress, "testuser", fakeTx, CONFLICT); 2302 // Create a transaction. 2303 final var postTx = postObjMethod(FCR_TX); 2304 setAuth(postTx, "fedoraAdmin"); 2305 final String txId; 2306 try (final var response = execute(postTx)) { 2307 assertEquals(SC_CREATED, getStatus(response)); 2308 txId = getLocation(response); 2309 } 2310 // Create an object in the transaction. 2311 final var postObj = postObjMethod(); 2312 setAuth(postObj, "fedoraAdmin"); 2313 addTxTo(postObj, txId); 2314 final String targetUri; 2315 try (final var response = execute(postObj)) { 2316 assertEquals(SC_CREATED, getStatus(response)); 2317 targetUri = getLocation(response); 2318 } 2319 // Test the transaction works. 2320 assertGetRequest(targetUri, "testuser", txId, OK); 2321 // Commit the transaction 2322 final var commit = new HttpPut(txId); 2323 setAuth(commit, "fedoraAdmin"); 2324 assertEquals(SC_NO_CONTENT, getStatus(commit)); 2325 // Now try to get the transaction again, expect 409 Conflict.. 2326 assertGetRequest(targetUri, "fedoraAdmin", txId, CONFLICT); 2327 assertGetRequest(targetUri, "testuser", txId, CONFLICT); 2328 } 2329 2330 private void assertGetRequest(final String uri, final String username, final Response.Status expectedResponse) { 2331 assertGetRequest(uri, username, null, expectedResponse); 2332 } 2333 2334 private void assertGetRequest(final String uri, final String username, final String txId, 2335 final Response.Status expectedResponse) { 2336 final var getTarget = new HttpGet(uri); 2337 setAuth(getTarget, username); 2338 if (txId != null) { 2339 addTxTo(getTarget, txId); 2340 } 2341 assertEquals(expectedResponse.getStatusCode(), getStatus(getTarget)); 2342 } 2343 2344 /** 2345 * Check the graph has the predicate with the value. 2346 * @param targetUri Full URI of the resource to check. 2347 * @param predicateUri Full URI of the predicate to check. 2348 * @param predicateValue Literal value to look for. 2349 * @throws Exception if problems performing the GET. 2350 */ 2351 private void assertPredicateValue(final String targetUri, final String predicateUri, final String predicateValue) 2352 throws Exception { 2353 final HttpGet verifyGet = new HttpGet(targetUri); 2354 setAuth(verifyGet, "fedoraAdmin"); 2355 try (final CloseableHttpResponse response = execute(verifyGet)) { 2356 final CloseableDataset dataset = getDataset(response); 2357 final DatasetGraph graph = dataset.asDatasetGraph(); 2358 assertTrue("Can't find " + predicateValue + " for predicate " + predicateUri + " in graph", 2359 graph.contains( 2360 Node.ANY, 2361 Node.ANY, 2362 NodeFactory.createURI(predicateUri), 2363 NodeFactory.createLiteral(predicateValue) 2364 ) 2365 ); 2366 } 2367 } 2368 2369 2370 /** 2371 * Utility function to ingest a ACL from a string. 2372 * 2373 * @param resourcePath Path to the resource if doesn't end with "/fcr:acl" it is added. 2374 * @param acl the text/turtle ACL as a string 2375 * @param username user to ingest as 2376 * @return the response from the ACL ingest. 2377 * @throws IOException on StringEntity encoding or client execute 2378 */ 2379 private HttpResponse ingestAclString(final String resourcePath, final String acl, final String username) 2380 throws IOException { 2381 final String aclPath = (resourcePath.endsWith("/fcr:acl") ? resourcePath : resourcePath + "/fcr:acl"); 2382 final HttpPut putReq = new HttpPut(aclPath); 2383 setAuth(putReq, username); 2384 putReq.setHeader("Content-type", "text/turtle"); 2385 putReq.setEntity(new StringEntity(acl, turtleContentType)); 2386 return execute(putReq); 2387 } 2388 2389 /** 2390 * Ensure that a writeable resource is still writeable 2391 * 2392 * @param writeableResource the URI of the writeable resource. 2393 * @param username the user will write access. 2394 * @throws UnsupportedEncodingException if default charset for String Entity is unsupported 2395 */ 2396 private void testCanWrite(final String writeableResource, final String username) 2397 throws UnsupportedEncodingException { 2398 // Try to create a basic container inside the writeable resource with POST. 2399 final HttpPost okPost = postObjMethod(writeableResource); 2400 setAuth(okPost, username); 2401 assertEquals(HttpStatus.SC_CREATED, getStatus(okPost)); 2402 2403 // Try to PATCH the writeableResource 2404 final HttpPatch okPatch = patchObjMethod(writeableResource); 2405 final String patchString = "PREFIX dc: <http://purl.org/dc/elements/1.1/> DELETE { <> dc:title ?o1 } " + 2406 "INSERT { <> dc:title \"Changed title\" } WHERE { <> dc:title ?o1 }"; 2407 final HttpEntity patchEntity = new StringEntity(patchString, sparqlContentType); 2408 setAuth(okPatch, username); 2409 okPatch.setHeader("Content-type", "application/sparql-update"); 2410 okPatch.setEntity(patchEntity); 2411 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(okPatch)); 2412 } 2413 2414 @Test 2415 public void testAuthenticatedUserCanCreateTransaction() { 2416 final HttpPost txnCreatePost = postObjMethod("rest/fcr:tx"); 2417 setAuth(txnCreatePost, "testUser92"); 2418 assertEquals(SC_CREATED, getStatus(txnCreatePost)); 2419 } 2420}