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