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