001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.integration.auth.webac; 019 020import static java.util.Arrays.stream; 021import static javax.ws.rs.core.Response.Status.CREATED; 022import static org.apache.http.HttpStatus.SC_FORBIDDEN; 023import static org.apache.http.HttpStatus.SC_NOT_FOUND; 024import static org.apache.http.HttpStatus.SC_NO_CONTENT; 025import static org.apache.jena.vocabulary.DC_11.title; 026import static org.fcrepo.auth.webac.WebACRolesProvider.GROUP_AGENT_BASE_URI_PROPERTY; 027import static org.fcrepo.http.api.FedoraAcl.ROOT_AUTHORIZATION_PROPERTY; 028import static org.fcrepo.kernel.modeshape.utils.FedoraSessionUserUtil.USER_AGENT_BASE_URI_PROPERTY; 029import static org.junit.Assert.assertEquals; 030import static org.junit.Assert.assertTrue; 031 032import java.io.IOException; 033import java.io.InputStream; 034import java.util.Arrays; 035import java.util.Optional; 036import javax.ws.rs.core.Link; 037 038import org.apache.commons.codec.binary.Base64; 039import org.apache.commons.io.IOUtils; 040import org.apache.http.Header; 041import org.apache.http.HeaderElement; 042import org.apache.http.HttpEntity; 043import org.apache.http.HttpResponse; 044import org.apache.http.HttpStatus; 045import org.apache.http.NameValuePair; 046import org.apache.http.client.methods.CloseableHttpResponse; 047import org.apache.http.client.methods.HttpDelete; 048import org.apache.http.client.methods.HttpGet; 049import org.apache.http.client.methods.HttpHead; 050import org.apache.http.client.methods.HttpOptions; 051import org.apache.http.client.methods.HttpPatch; 052import org.apache.http.client.methods.HttpPost; 053import org.apache.http.client.methods.HttpPut; 054import org.apache.http.entity.ContentType; 055import org.apache.http.entity.InputStreamEntity; 056import org.apache.http.entity.StringEntity; 057import org.apache.http.message.AbstractHttpMessage; 058import org.fcrepo.integration.http.api.AbstractResourceIT; 059import org.junit.Ignore; 060import org.junit.Rule; 061import org.junit.Test; 062import org.junit.contrib.java.lang.system.RestoreSystemProperties; 063import org.slf4j.Logger; 064import org.slf4j.LoggerFactory; 065 066/** 067 * @author Peter Eichman 068 * @author whikloj 069 * @since September 4, 2015 070 */ 071public class WebACRecipesIT extends AbstractResourceIT { 072 073 private static final Logger logger = LoggerFactory.getLogger(WebACRecipesIT.class); 074 075 @Rule 076 public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); 077 078 /** 079 * Convenience method to create an ACL with 0 or more authorization resources in the respository. 080 */ 081 private String ingestAcl(final String username, 082 final String aclFilePath, final String aclResourcePath) throws IOException { 083 084 // create the ACL 085 final HttpResponse aclResponse = ingestTurtleResource(username, aclFilePath, aclResourcePath); 086 087 // return the URI to the newly created resource 088 return aclResponse.getFirstHeader("Location").getValue(); 089 } 090 091 /** 092 * Convenience method to POST the contents of a Turtle file to the repository to create a new resource. Returns 093 * the HTTP response from that request. Throws an IOException if the server responds with anything other than a 094 * 201 Created response code. 095 */ 096 private HttpResponse ingestTurtleResource(final String username, final String path, final String requestURI) 097 throws IOException { 098 final HttpPut request = new HttpPut(requestURI); 099 100 logger.debug("PUT to {} to create {}", requestURI, path); 101 102 setAuth(request, username); 103 104 final InputStream file = this.getClass().getResourceAsStream(path); 105 final InputStreamEntity fileEntity = new InputStreamEntity(file); 106 request.setEntity(fileEntity); 107 request.setHeader("Content-Type", "text/turtle"); 108 109 try (final CloseableHttpResponse response = execute(request)) { 110 assertEquals( 111 "Didn't get a CREATED response!: " + IOUtils.toString(response.getEntity().getContent(), "UTF-8"), 112 CREATED.getStatusCode(), getStatus(response)); 113 return response; 114 } 115 116 } 117 118 /** 119 * Convenience method to set up a regular FedoraResource 120 * 121 * @param path Path to put the resource under 122 * @return the Location of the newly created resource 123 * @throws IOException on error 124 */ 125 private String ingestObj(final String path) throws IOException { 126 final HttpPut request = putObjMethod(path.replace(serverAddress, "")); 127 setAuth(request, "fedoraAdmin"); 128 try (final CloseableHttpResponse response = execute(request)) { 129 assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); 130 return response.getFirstHeader("Location").getValue(); 131 } 132 } 133 134 private String ingestBinary(final String path, final HttpEntity body) throws IOException { 135 logger.info("Ingesting {} binary to {}", body.getContentType().getValue(), path); 136 final HttpPut request = new HttpPut(serverAddress + path); 137 setAuth(request, "fedoraAdmin"); 138 request.setEntity(body); 139 request.setHeader(body.getContentType()); 140 final CloseableHttpResponse response = execute(request); 141 assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); 142 final String location = response.getFirstHeader("Location").getValue(); 143 logger.info("Created binary at {}", location); 144 return location; 145 146 } 147 148 private String ingestDatastream(final String path, final String ds) throws IOException { 149 final HttpPut request = putDSMethod(path, ds, "some not so random content"); 150 setAuth(request, "fedoraAdmin"); 151 try (final CloseableHttpResponse response = execute(request)) { 152 assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); 153 return response.getFirstHeader("Location").getValue(); 154 } 155 } 156 157 /** 158 * Convenience method for applying credentials to a request 159 * 160 * @param method the request to add the credentials to 161 * @param username the username to add 162 */ 163 private static void setAuth(final AbstractHttpMessage method, final String username) { 164 final String creds = username + ":password"; 165 final String encCreds = new String(Base64.encodeBase64(creds.getBytes())); 166 final String basic = "Basic " + encCreds; 167 method.setHeader("Authorization", basic); 168 } 169 170 @Test 171 public void scenario1() throws IOException { 172 final String testObj = ingestObj("/rest/webacl_box1"); 173 final String acl1 = ingestAcl("fedoraAdmin", "/acls/01/acl.ttl", 174 testObj + "/fcr:acl"); 175 final String aclLink = Link.fromUri(acl1).rel("acl").build().toString(); 176 177 final HttpGet request = getObjMethod(testObj.replace(serverAddress, "")); 178 assertEquals("Anonymous can read " + testObj, HttpStatus.SC_FORBIDDEN, getStatus(request)); 179 180 setAuth(request, "user01"); 181 try (final CloseableHttpResponse response = execute(request)) { 182 assertEquals("User 'user01' can't read" + testObj, HttpStatus.SC_OK, getStatus(response)); 183 // This gets the Link headers and filters for the correct one (aclLink::equals) defined above. 184 final Optional<String> header = stream(response.getHeaders("Link")).map(Header::getValue) 185 .filter(aclLink::equals).findFirst(); 186 // So you either have the correct Link header or you get nothing. 187 assertTrue("Missing Link header", header.isPresent()); 188 } 189 190 final String childObj = ingestObj("/rest/webacl_box1/child"); 191 final HttpGet getReq = getObjMethod(childObj.replace(serverAddress, "")); 192 setAuth(getReq, "user01"); 193 try (final CloseableHttpResponse response = execute(getReq)) { 194 assertEquals("User 'user01' can't read child of " + testObj, HttpStatus.SC_OK, getStatus(response)); 195 } 196 } 197 198 @Test 199 public void scenario2() throws IOException { 200 final String id = "/rest/box/bag/collection"; 201 final String testObj = ingestObj(id); 202 ingestAcl("fedoraAdmin", "/acls/02/acl.ttl", testObj + "/fcr:acl"); 203 204 logger.debug("Anonymous can not read " + testObj); 205 final HttpGet requestGet = getObjMethod(id); 206 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet)); 207 208 logger.debug("GroupId 'Editors' can read " + testObj); 209 final HttpGet requestGet2 = getObjMethod(id); 210 setAuth(requestGet2, "jones"); 211 requestGet2.setHeader("some-header", "Editors"); 212 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 213 214 logger.debug("Anonymous cannot write " + testObj); 215 final HttpPatch requestPatch = patchObjMethod(id); 216 requestPatch.setEntity(new StringEntity("INSERT { <> <" + title.getURI() + "> \"Test title\" . } WHERE {}")); 217 requestPatch.setHeader("Content-type", "application/sparql-update"); 218 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 219 220 logger.debug("Editors can write " + testObj); 221 final HttpPatch requestPatch2 = patchObjMethod(id); 222 setAuth(requestPatch2, "jones"); 223 requestPatch2.setHeader("some-header", "Editors"); 224 requestPatch2.setEntity( 225 new StringEntity("INSERT { <> <" + title.getURI() + "> \"Different title\" . } WHERE {}")); 226 requestPatch2.setHeader("Content-type", "application/sparql-update"); 227 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch2)); 228 } 229 230 @Test 231 public void scenario3() throws IOException { 232 final String idDark = "/rest/dark/archive"; 233 final String idLight = "/rest/dark/archive/sunshine"; 234 final String testObj = ingestObj(idDark); 235 final String testObj2 = ingestObjWithACL(idLight, "/acls/03/acl.ttl"); 236 ingestAcl("fedoraAdmin", "/acls/03/acl.ttl", testObj + "/fcr:acl"); 237 238 logger.debug("Anonymous can't read " + testObj); 239 final HttpGet requestGet = getObjMethod(idDark); 240 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet)); 241 242 logger.debug("Restricted can read " + testObj); 243 final HttpGet requestGet2 = getObjMethod(idDark); 244 setAuth(requestGet2, "jones"); 245 requestGet2.setHeader("some-header", "Restricted"); 246 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 247 248 logger.debug("Anonymous can read " + testObj2); 249 final HttpGet requestGet3 = getObjMethod(idLight); 250 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 251 252 logger.debug("Restricted can read " + testObj2); 253 final HttpGet requestGet4 = getObjMethod(idLight); 254 setAuth(requestGet4, "jones"); 255 requestGet4.setHeader("some-header", "Restricted"); 256 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 257 } 258 259 @Test 260 public void scenario4() throws IOException { 261 final String id = "/rest/public_collection"; 262 final String testObj = ingestObjWithACL(id, "/acls/04/acl.ttl"); 263 264 logger.debug("Anonymous can read " + testObj); 265 final HttpGet requestGet = getObjMethod(id); 266 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 267 268 logger.debug("Editors can read " + testObj); 269 final HttpGet requestGet2 = getObjMethod(id); 270 setAuth(requestGet2, "jones"); 271 requestGet2.setHeader("some-header", "Editors"); 272 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 273 274 logger.debug("Smith can access " + testObj); 275 final HttpGet requestGet3 = getObjMethod(id); 276 setAuth(requestGet3, "smith"); 277 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 278 279 logger.debug("Anonymous can't write " + testObj); 280 final HttpPatch requestPatch = patchObjMethod(id); 281 requestPatch.setHeader("Content-type", "application/sparql-update"); 282 requestPatch.setEntity(new StringEntity("INSERT { <> <" + title.getURI() + "> \"Change title\" . } WHERE {}")); 283 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 284 285 logger.debug("Editors can write " + testObj); 286 final HttpPatch requestPatch2 = patchObjMethod(id); 287 requestPatch2.setHeader("Content-type", "application/sparql-update"); 288 requestPatch2.setEntity(new StringEntity("INSERT { <> <" + title.getURI() + "> \"New title\" . } WHERE {}")); 289 setAuth(requestPatch2, "jones"); 290 requestPatch2.setHeader("some-header", "Editors"); 291 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch2)); 292 293 logger.debug("Editors can create (PUT) child objects of " + testObj); 294 final HttpPut requestPut1 = putObjMethod(id + "/child1"); 295 setAuth(requestPut1, "jones"); 296 requestPut1.setHeader("some-header", "Editors"); 297 assertEquals(HttpStatus.SC_CREATED, getStatus(requestPut1)); 298 299 final HttpGet requestGet4 = getObjMethod(id + "/child1"); 300 setAuth(requestGet4, "jones"); 301 requestGet4.setHeader("some-header", "Editors"); 302 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 303 304 logger.debug("Editors can create (POST) child objects of " + testObj); 305 final HttpPost requestPost1 = postObjMethod(id); 306 requestPost1.addHeader("Slug", "child2"); 307 setAuth(requestPost1, "jones"); 308 requestPost1.setHeader("some-header", "Editors"); 309 assertEquals(HttpStatus.SC_CREATED, getStatus(requestPost1)); 310 311 final HttpGet requestGet5 = getObjMethod(id + "/child2"); 312 setAuth(requestGet5, "jones"); 313 requestGet5.setHeader("some-header", "Editors"); 314 assertEquals(HttpStatus.SC_OK, getStatus(requestGet5)); 315 316 logger.debug("Editors can create nested child objects of " + testObj); 317 final HttpPut requestPut2 = putObjMethod(id + "/a/b/c/child"); 318 setAuth(requestPut2, "jones"); 319 requestPut2.setHeader("some-header", "Editors"); 320 assertEquals(HttpStatus.SC_CREATED, getStatus(requestPut2)); 321 322 final HttpGet requestGet6 = getObjMethod(id + "/a/b/c/child"); 323 setAuth(requestGet6, "jones"); 324 requestGet6.setHeader("some-header", "Editors"); 325 assertEquals(HttpStatus.SC_OK, getStatus(requestGet6)); 326 327 logger.debug("Smith can't write " + testObj); 328 final HttpPatch requestPatch3 = patchObjMethod(id); 329 requestPatch3.setHeader("Content-type", "application/sparql-update"); 330 requestPatch3.setEntity( 331 new StringEntity("INSERT { <> <" + title.getURI() + "> \"Different title\" . } WHERE {}")); 332 setAuth(requestPatch3, "smith"); 333 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch3)); 334 } 335 336 @Test 337 public void scenario5() throws IOException { 338 final String idPublic = "/rest/mixedCollection/publicObj"; 339 final String idPrivate = "/rest/mixedCollection/privateObj"; 340 ingestObjWithACL("/rest/mixedCollection", "/acls/05/acl.ttl"); 341 final String publicObj = ingestObj(idPublic); 342 final String privateObj = ingestObj(idPrivate); 343 final HttpPatch patch = patchObjMethod(idPublic); 344 345 setAuth(patch, "fedoraAdmin"); 346 patch.setHeader("Content-type", "application/sparql-update"); 347 patch.setEntity(new StringEntity("INSERT { <> a <http://example.com/terms#publicImage> . } WHERE {}")); 348 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patch)); 349 350 351 logger.debug("Anonymous can see eg:publicImage " + publicObj); 352 final HttpGet requestGet = getObjMethod(idPublic); 353 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 354 355 logger.debug("Anonymous can't see other resource " + privateObj); 356 final HttpGet requestGet2 = getObjMethod(idPrivate); 357 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet2)); 358 359 logger.debug("Admins can see eg:publicImage " + publicObj); 360 final HttpGet requestGet3 = getObjMethod(idPublic); 361 setAuth(requestGet3, "jones"); 362 requestGet3.setHeader("some-header", "Admins"); 363 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 364 365 logger.debug("Admins can see others" + privateObj); 366 final HttpGet requestGet4 = getObjMethod(idPrivate); 367 setAuth(requestGet4, "jones"); 368 requestGet4.setHeader("some-header", "Admins"); 369 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 370 } 371 372 @Test 373 public void scenario9() throws IOException { 374 final String idPublic = "/rest/anotherCollection/publicObj"; 375 final String groups = "/rest/group"; 376 final String fooGroup = groups + "/foo"; 377 final String testObj = ingestObj("/rest/anotherCollection"); 378 final String publicObj = ingestObj(idPublic); 379 380 final HttpPut request = putObjMethod(fooGroup); 381 setAuth(request, "fedoraAdmin"); 382 383 final InputStream file = this.getClass().getResourceAsStream("/acls/09/group.ttl"); 384 final InputStreamEntity fileEntity = new InputStreamEntity(file); 385 request.setEntity(fileEntity); 386 request.setHeader("Content-Type", "text/turtle;charset=UTF-8"); 387 388 assertEquals("Didn't get a CREATED response!", CREATED.getStatusCode(), getStatus(request)); 389 390 ingestAcl("fedoraAdmin", "/acls/09/acl.ttl", testObj + "/fcr:acl"); 391 392 logger.debug("Person1 can see object " + publicObj); 393 final HttpGet requestGet1 = getObjMethod(idPublic); 394 setAuth(requestGet1, "person1"); 395 assertEquals(HttpStatus.SC_OK, getStatus(requestGet1)); 396 397 logger.debug("Person2 can see object " + publicObj); 398 final HttpGet requestGet2 = getObjMethod(idPublic); 399 setAuth(requestGet2, "person2"); 400 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 401 402 logger.debug("Person3 user cannot see object " + publicObj); 403 final HttpGet requestGet3 = getObjMethod(idPublic); 404 setAuth(requestGet3, "person3"); 405 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet3)); 406 } 407 408 /** 409 * Test cases to verify authorization with only acl:Append mode configured 410 * in the acl authorization of an resource. 411 * Tests: 412 * 1. Deny(403) on GET. 413 * 2. Allow(204) on PATCH. 414 * 3. Deny(403) on DELETE. 415 * 4. Deny(403) on PATCH with SPARQL DELETE statements. 416 * 5. Allow(400) on PATCH with empty SPARQL content. 417 * 6. Deny(403) on PATCH with non-SPARQL content. 418 */ 419 @Test 420 public void scenario18Test1() throws IOException { 421 final String testObj = ingestObj("/rest/append_only_resource"); 422 final String id = "/rest/append_only_resource/" + getRandomUniqueId(); 423 ingestObj(id); 424 425 logger.debug("user18 can read (has ACL:READ): {}", id); 426 final HttpGet requestGet = getObjMethod(id); 427 setAuth(requestGet, "user18"); 428 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 429 430 logger.debug("user18 can't append (no ACL): {}", id); 431 final HttpPatch requestPatch = patchObjMethod(id); 432 setAuth(requestPatch, "user18"); 433 requestPatch.setHeader("Content-type", "application/sparql-update"); 434 requestPatch.setEntity(new StringEntity("INSERT { <> <" + title.getURI() + "> \"Test title\" . } WHERE {}")); 435 436 logger.debug("user18 can't delete (no ACL): {}", id); 437 final HttpDelete requestDelete = deleteObjMethod(id); 438 setAuth(requestDelete, "user18"); 439 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 440 441 ingestAcl("fedoraAdmin", "/acls/18/append-only-acl.ttl", testObj + "/fcr:acl"); 442 443 logger.debug("user18 still can't read (ACL append): {}", id); 444 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet)); 445 446 logger.debug("user18 can patch - SPARQL INSERTs (ACL append): {}", id); 447 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 448 449 // Alter the Content-type to include a character set, to ensure correct matching. 450 requestPatch.setHeader("Content-type", "application/sparql-update; charset=UTF-8"); 451 logger.debug("user18 can patch - SPARQL INSERTs (ACL append with charset): {}", id); 452 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 453 454 logger.debug("user18 still can't delete (ACL append): {}", id); 455 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 456 457 requestPatch.setEntity(new StringEntity("DELETE { <> <" + title.getURI() + "> \"Test title\" . } WHERE {}")); 458 459 logger.debug("user18 can not patch - SPARQL DELETEs (ACL append): {}", id); 460 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 461 462 requestPatch.setEntity(null); 463 464 logger.debug("user18 can patch (is authorized, but bad request) - Empty SPARQL (ACL append): {}", id); 465 assertEquals(HttpStatus.SC_BAD_REQUEST, getStatus(requestPatch)); 466 467 requestPatch.setHeader("Content-type", null); 468 469 logger.debug("user18 can not patch - Non SPARQL (ACL append): {}", id); 470 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 471 472 } 473 474 /** 475 * Test cases to verify authorization with acl:Read and acl:Append modes 476 * configured in the acl authorization of an resource. 477 * Tests: 478 * 1. Allow(200) on GET. 479 * 2. Allow(204) on PATCH. 480 * 3. Deny(403) on DELETE. 481 */ 482 @Test 483 public void scenario18Test2() throws IOException { 484 final String testObj = ingestObj("/rest/read_append_resource"); 485 486 final String id = "/rest/read_append_resource/" + getRandomUniqueId(); 487 ingestObj(id); 488 489 logger.debug("user18 can read (has ACL:READ): {}", id); 490 final HttpGet requestGet = getObjMethod(id); 491 setAuth(requestGet, "user18"); 492 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 493 494 logger.debug("user18 can't append (no ACL): {}", id); 495 final HttpPatch requestPatch = patchObjMethod(id); 496 setAuth(requestPatch, "user18"); 497 requestPatch.setHeader("Content-type", "application/sparql-update"); 498 requestPatch.setEntity(new StringEntity( 499 "INSERT { <> <" + title.getURI() + "> \"some title\" . } WHERE {}")); 500 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 501 502 ingestAcl("fedoraAdmin", "/acls/18/read-append-acl.ttl", testObj + "/fcr:acl"); 503 504 logger.debug("user18 can't delete (no ACL): {}", id); 505 final HttpDelete requestDelete = deleteObjMethod(id); 506 setAuth(requestDelete, "user18"); 507 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 508 509 logger.debug("user18 can read (ACL read, append): {}", id); 510 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 511 512 logger.debug("user18 can append (ACL read, append): {}", id); 513 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 514 515 logger.debug("user18 still can't delete (ACL read, append): {}", id); 516 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 517 } 518 519 /** 520 * Test cases to verify authorization with acl:Read, acl:Append and 521 * acl:Write modes configured in the acl authorization of an resource. 522 * Tests: 523 * 1. Allow(200) on GET. 524 * 2. Allow(204) on PATCH. 525 * 3. Allow(204) on DELETE. 526 */ 527 @Test 528 public void scenario18Test3() throws IOException { 529 final String testObj = ingestObj("/rest/read_append_write_resource"); 530 531 final String id = "/rest/read_append_write_resource/" + getRandomUniqueId(); 532 ingestObj(id); 533 534 logger.debug("user18 can read (has ACL:READ): {}", id); 535 final HttpGet requestGet = getObjMethod(id); 536 setAuth(requestGet, "user18"); 537 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 538 539 logger.debug("user18 can't append (no ACL): {}", id); 540 final HttpPatch requestPatch = patchObjMethod(id); 541 setAuth(requestPatch, "user18"); 542 requestPatch.setHeader("Content-type", "application/sparql-update"); 543 requestPatch.setEntity(new StringEntity( 544 "INSERT { <> <http://purl.org/dc/elements/1.1/title> \"some title\" . } WHERE {}")); 545 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 546 547 logger.debug("user18 can't delete (no ACL): {}", id); 548 final HttpDelete requestDelete = deleteObjMethod(id); 549 setAuth(requestDelete, "user18"); 550 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestDelete)); 551 552 ingestAcl("fedoraAdmin", "/acls/18/read-append-write-acl.ttl", testObj + "/fcr:acl"); 553 554 logger.debug("user18 can read (ACL read, append, write): {}", id); 555 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 556 557 logger.debug("user18 can append (ACL read, append, write): {}", id); 558 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch)); 559 560 logger.debug("user18 can delete (ACL read, append, write): {}", id); 561 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestDelete)); 562 } 563 564 @Test 565 public void testAccessToRoot() throws IOException { 566 final String id = "/rest/" + getRandomUniqueId(); 567 final String testObj = ingestObj(id); 568 569 logger.debug("Anonymous can read (has ACL:READ): {}", id); 570 final HttpGet requestGet1 = getObjMethod(id); 571 assertEquals(HttpStatus.SC_OK, getStatus(requestGet1)); 572 573 logger.debug("Can username 'user06a' read {} (has ACL:READ)", id); 574 final HttpGet requestGet2 = getObjMethod(id); 575 setAuth(requestGet2, "user06a"); 576 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 577 578 logger.debug("Can username 'notuser06b' read {} (has ACL:READ)", id); 579 final HttpGet requestGet3 = getObjMethod(id); 580 setAuth(requestGet3, "user06b"); 581 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 582 583 System.setProperty(ROOT_AUTHORIZATION_PROPERTY, "./target/test-classes/test-root-authorization2.ttl"); 584 logger.debug("Can username 'user06a' read {} (overridden system ACL)", id); 585 final HttpGet requestGet4 = getObjMethod(id); 586 setAuth(requestGet4, "user06a"); 587 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 588 System.clearProperty(ROOT_AUTHORIZATION_PROPERTY); 589 590 // Add ACL to root 591 final String rootURI = getObjMethod("/rest").getURI().toString(); 592 ingestAcl("fedoraAdmin", "/acls/06/acl.ttl", rootURI + "/fcr:acl"); 593 594 logger.debug("Anonymous still can't read (ACL present)"); 595 final HttpGet requestGet5 = getObjMethod(id); 596 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet5)); 597 598 logger.debug("Can username 'user06a' read {} (ACL present)", testObj); 599 final HttpGet requestGet6 = getObjMethod(id); 600 setAuth(requestGet6, "user06a"); 601 assertEquals(HttpStatus.SC_OK, getStatus(requestGet6)); 602 603 logger.debug("Can username 'user06b' read {} (ACL present)", testObj); 604 final HttpGet requestGet7 = getObjMethod(id); 605 setAuth(requestGet7, "user06b"); 606 assertEquals(HttpStatus.SC_OK, getStatus(requestGet7)); 607 } 608 609 @Test 610 public void scenario21TestACLNotForInheritance() throws IOException { 611 final String parentPath = "/rest/resource_acl_no_inheritance"; 612 // Ingest ACL with no acl:default statement to the parent resource 613 ingestObjWithACL(parentPath, "/acls/21/acl.ttl"); 614 615 final String id = parentPath + "/" + getRandomUniqueId(); 616 final String testObj = ingestObj(id); 617 618 619 // Test the parent ACL with no acl:default is applied for the parent resource authorization. 620 final HttpGet requestGet1 = getObjMethod(parentPath); 621 setAuth(requestGet1, "user21"); 622 assertEquals("Agent user21 can't read resource " + parentPath + " with its own ACL!", 623 HttpStatus.SC_OK, getStatus(requestGet1)); 624 625 final HttpGet requestGet2 = getObjMethod(id); 626 assertEquals("Agent user21 inherits read permission from parent ACL to read resource " + testObj + "!", 627 HttpStatus.SC_OK, getStatus(requestGet2)); 628 629 // Test the default root ACL is inherited for authorization while the parent ACL with no acl:default is ignored 630 System.setProperty(ROOT_AUTHORIZATION_PROPERTY, "./target/test-classes/test-root-authorization2.ttl"); 631 final HttpGet requestGet3 = getObjMethod(id); 632 setAuth(requestGet3, "user06a"); 633 assertEquals("Agent user06a can't inherit read persmssion from root ACL to read resource " + testObj + "!", 634 HttpStatus.SC_OK, getStatus(requestGet3)); 635 } 636 637 @Test 638 public void scenario22TestACLAuthorizationNotForInheritance() throws IOException { 639 final String parentPath = "/rest/resource_mix_acl_default"; 640 final String parentObj = ingestObj(parentPath); 641 642 final String id = parentPath + "/" + getRandomUniqueId(); 643 final String testObj = ingestObj(id); 644 645 // Ingest ACL with mix acl:default authorization to the parent resource 646 ingestAcl("fedoraAdmin", "/acls/22/acl.ttl", parentObj + "/fcr:acl"); 647 648 // Test the parent ACL is applied for the parent resource authorization. 649 final HttpGet requestGet1 = getObjMethod(parentPath); 650 setAuth(requestGet1, "user22a"); 651 assertEquals("Agent user22a can't read resource " + parentPath + " with its own ACL!", 652 HttpStatus.SC_OK, getStatus(requestGet1)); 653 654 final HttpGet requestGet2 = getObjMethod(parentPath); 655 setAuth(requestGet2, "user22b"); 656 assertEquals("Agent user22b can't read resource " + parentPath + " with its own ACL!", 657 HttpStatus.SC_OK, getStatus(requestGet1)); 658 659 // Test the parent ACL is applied for the parent resource authorization. 660 final HttpGet requestGet3 = getObjMethod(id); 661 setAuth(requestGet3, "user22a"); 662 assertEquals("Agent user22a inherits read permission from parent ACL to read resource " + testObj + "!", 663 HttpStatus.SC_FORBIDDEN, getStatus(requestGet3)); 664 665 final HttpGet requestGet4 = getObjMethod(id); 666 setAuth(requestGet4, "user22b"); 667 assertEquals("Agent user22b can't inherits read permission from parent ACL to read resource " + testObj + "!", 668 HttpStatus.SC_OK, getStatus(requestGet4)); 669 } 670 671 @Test 672 public void testAccessToBinary() throws IOException { 673 // Block access to "book" 674 final String idBook = "/rest/book"; 675 final String bookURI = ingestObj(idBook); 676 677 // Open access datastream, "file" 678 final String id = idBook + "/file"; 679 final String testObj = ingestDatastream(idBook, "file"); 680 ingestAcl("fedoraAdmin", "/acls/07/acl.ttl", bookURI + "/fcr:acl"); 681 682 logger.debug("Anonymous can't read"); 683 final HttpGet requestGet1 = getObjMethod(id); 684 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet1)); 685 686 logger.debug("Can username 'user07' read {}", testObj); 687 final HttpGet requestGet2 = getObjMethod(id); 688 689 setAuth(requestGet2, "user07"); 690 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 691 } 692 693 @Test 694 @Ignore("FAILING") 695 public void testAccessToHashResource() throws IOException { 696 final String id = "/rest/some/parent#hash-resource"; 697 final String testObj = ingestObj(id); 698 ingestAcl("fedoraAdmin", "/acls/08/acl.ttl", testObj + "/fcr:acl"); 699 700 logger.debug("Anonymous can't read"); 701 final HttpGet requestGet1 = getObjMethod(id); 702 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet1)); 703 704 logger.debug("Can username 'user08' read {}", testObj); 705 final HttpGet requestGet2 = getObjMethod(id); 706 setAuth(requestGet2, "user08"); 707 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 708 } 709 710 @Test 711 @Ignore ("Until implemented with Memento") 712 public void testAccessToVersionedResources() throws IOException { 713 final String idVersion = "/rest/versionResource"; 714 ingestObj(idVersion); 715 716 final HttpPatch requestPatch1 = patchObjMethod(idVersion); 717 setAuth(requestPatch1, "fedoraAdmin"); 718 requestPatch1.addHeader("Content-type", "application/sparql-update"); 719 requestPatch1.setEntity( 720 new StringEntity("PREFIX pcdm: <http://pcdm.org/models#> INSERT { <> a pcdm:Object } WHERE {}")); 721 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch1)); 722 723 ingestAcl("fedoraAdmin", "/acls/10/acl.ttl", idVersion + "/fcr:acl"); 724 725 final HttpGet requestGet1 = getObjMethod(idVersion); 726 setAuth(requestGet1, "user10"); 727 assertEquals("user10 can't read object", HttpStatus.SC_OK, getStatus(requestGet1)); 728 729 final HttpPost requestPost1 = postObjMethod(idVersion + "/fcr:versions"); 730 requestPost1.addHeader("Slug", "v0"); 731 setAuth(requestPost1, "fedoraAdmin"); 732 assertEquals("Unable to create a new version", HttpStatus.SC_CREATED, getStatus(requestPost1)); 733 734 final HttpGet requestGet2 = getObjMethod(idVersion); 735 setAuth(requestGet2, "user10"); 736 assertEquals("user10 can't read versioned object", HttpStatus.SC_OK, getStatus(requestGet2)); 737 } 738 739 @Test 740 public void testDelegatedUserAccess() throws IOException { 741 logger.debug("testing delegated authentication"); 742 final String targetPath = "/rest/foo"; 743 final String targetResource = ingestObj(targetPath); 744 745 ingestAcl("fedoraAdmin", "/acls/11/acl.ttl", targetResource + "/fcr:acl"); 746 747 final HttpGet adminGet = getObjMethod(targetPath); 748 setAuth(adminGet, "fedoraAdmin"); 749 assertEquals("admin can read object", HttpStatus.SC_OK, getStatus(adminGet)); 750 751 final HttpGet adminDelegatedGet = getObjMethod(targetPath); 752 setAuth(adminDelegatedGet, "fedoraAdmin"); 753 adminDelegatedGet.addHeader("On-Behalf-Of", "user11"); 754 assertEquals("delegated user can read object", HttpStatus.SC_OK, getStatus(adminDelegatedGet)); 755 756 final HttpGet adminUnauthorizedDelegatedGet = getObjMethod(targetPath); 757 setAuth(adminUnauthorizedDelegatedGet, "fedoraAdmin"); 758 adminUnauthorizedDelegatedGet.addHeader("On-Behalf-Of", "fakeuser"); 759 assertEquals("delegated fakeuser cannot read object", HttpStatus.SC_FORBIDDEN, 760 getStatus(adminUnauthorizedDelegatedGet)); 761 762 final HttpGet adminDelegatedGet2 = getObjMethod(targetPath); 763 setAuth(adminDelegatedGet2, "fedoraAdmin"); 764 adminDelegatedGet2.addHeader("On-Behalf-Of", "info:user/user2"); 765 assertEquals("delegated user can read object", HttpStatus.SC_OK, getStatus(adminDelegatedGet2)); 766 767 final HttpGet adminUnauthorizedDelegatedGet2 = getObjMethod(targetPath); 768 setAuth(adminUnauthorizedDelegatedGet2, "fedoraAdmin"); 769 adminUnauthorizedDelegatedGet2.addHeader("On-Behalf-Of", "info:user/fakeuser"); 770 assertEquals("delegated fakeuser cannot read object", HttpStatus.SC_FORBIDDEN, 771 getStatus(adminUnauthorizedDelegatedGet2)); 772 773 // Now test with the system property in effect 774 System.setProperty(USER_AGENT_BASE_URI_PROPERTY, "info:user/"); 775 System.setProperty(GROUP_AGENT_BASE_URI_PROPERTY, "info:group/"); 776 777 final HttpGet adminDelegatedGet3 = getObjMethod(targetPath); 778 setAuth(adminDelegatedGet3, "fedoraAdmin"); 779 adminDelegatedGet3.addHeader("On-Behalf-Of", "info:user/user2"); 780 assertEquals("delegated user can read object", HttpStatus.SC_OK, getStatus(adminDelegatedGet3)); 781 782 final HttpGet adminUnauthorizedDelegatedGet3 = getObjMethod(targetPath); 783 setAuth(adminUnauthorizedDelegatedGet3, "fedoraAdmin"); 784 adminUnauthorizedDelegatedGet3.addHeader("On-Behalf-Of", "info:user/fakeuser"); 785 assertEquals("delegated fakeuser cannot read object", HttpStatus.SC_FORBIDDEN, 786 getStatus(adminUnauthorizedDelegatedGet3)); 787 788 System.clearProperty(USER_AGENT_BASE_URI_PROPERTY); 789 System.clearProperty(GROUP_AGENT_BASE_URI_PROPERTY); 790 } 791 792 @Test 793 @Ignore ("Until implemented with Memento") 794 795 public void testAccessByUriToVersionedResources() throws IOException { 796 final String idVersion = "/rest/versionResourceUri"; 797 ingestObj(idVersion); 798 799 ingestAcl("fedoraAdmin", "/acls/12/acl.ttl", idVersion + "/fcr:acl"); 800 801 final HttpGet requestGet1 = getObjMethod(idVersion); 802 setAuth(requestGet1, "user12"); 803 assertEquals("testuser can't read object", HttpStatus.SC_OK, getStatus(requestGet1)); 804 805 final HttpPost requestPost1 = postObjMethod(idVersion + "/fcr:versions"); 806 requestPost1.addHeader("Slug", "v0"); 807 setAuth(requestPost1, "user12"); 808 assertEquals("Unable to create a new version", HttpStatus.SC_CREATED, getStatus(requestPost1)); 809 810 final HttpGet requestGet2 = getObjMethod(idVersion); 811 setAuth(requestGet2, "user12"); 812 assertEquals("testuser can't read versioned object", HttpStatus.SC_OK, getStatus(requestGet2)); 813 } 814 815 @Test 816 public void testAgentAsUri() throws IOException { 817 final String id = "/rest/" + getRandomUniqueId(); 818 final String testObj = ingestObj(id); 819 820 logger.debug("Anonymous can read (has ACL:READ): {}", id); 821 final HttpGet requestGet1 = getObjMethod(id); 822 assertEquals(HttpStatus.SC_OK, getStatus(requestGet1)); 823 824 logger.debug("Can username 'smith123' read {} (no ACL)", id); 825 final HttpGet requestGet2 = getObjMethod(id); 826 setAuth(requestGet2, "smith123"); 827 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 828 829 System.setProperty(USER_AGENT_BASE_URI_PROPERTY, "info:user/"); 830 System.setProperty(GROUP_AGENT_BASE_URI_PROPERTY, "info:group/"); 831 832 logger.debug("Can username 'smith123' read {} (overridden system ACL)", id); 833 final HttpGet requestGet3 = getObjMethod(id); 834 setAuth(requestGet3, "smith123"); 835 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 836 837 logger.debug("Can username 'group123' read {} (overridden system ACL)", id); 838 final HttpGet requestGet4 = getObjMethod(id); 839 setAuth(requestGet4, "group123"); 840 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 841 842 System.clearProperty(USER_AGENT_BASE_URI_PROPERTY); 843 System.clearProperty(GROUP_AGENT_BASE_URI_PROPERTY); 844 845 // Add ACL to object 846 ingestAcl("fedoraAdmin", "/acls/16/acl.ttl", testObj + "/fcr:acl"); 847 848 logger.debug("Anonymous still can't read (ACL present)"); 849 final HttpGet requestGet5 = getObjMethod(id); 850 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet5)); 851 852 logger.debug("Can username 'smith123' read {} (ACL present, no system properties)", testObj); 853 final HttpGet requestGet6 = getObjMethod(id); 854 setAuth(requestGet6, "smith123"); 855 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet6)); 856 857 System.setProperty(USER_AGENT_BASE_URI_PROPERTY, "info:user/"); 858 System.setProperty(GROUP_AGENT_BASE_URI_PROPERTY, "info:group/"); 859 860 logger.debug("Can username 'smith123' read {} (ACL, system properties present)", id); 861 final HttpGet requestGet7 = getObjMethod(id); 862 setAuth(requestGet7, "smith123"); 863 assertEquals(HttpStatus.SC_OK, getStatus(requestGet7)); 864 865 logger.debug("Can groupname 'group123' read {} (ACL, system properties present)", id); 866 final HttpGet requestGet8 = getObjMethod(id); 867 setAuth(requestGet8, "group123"); 868 assertEquals(HttpStatus.SC_OK, getStatus(requestGet8)); 869 870 System.clearProperty(USER_AGENT_BASE_URI_PROPERTY); 871 System.clearProperty(GROUP_AGENT_BASE_URI_PROPERTY); 872 } 873 874 @Test 875 public void testRegisterNamespace() throws IOException { 876 final String testObj = ingestObj("/rest/test_namespace"); 877 ingestAcl("fedoraAdmin", "/acls/13/acl.ttl", testObj + "/fcr:acl"); 878 879 final String id = "/rest/test_namespace/" + getRandomUniqueId(); 880 ingestObj(id); 881 882 final HttpPatch patchReq = patchObjMethod(id); 883 setAuth(patchReq, "user13"); 884 patchReq.addHeader("Content-type", "application/sparql-update"); 885 patchReq.setEntity(new StringEntity("PREFIX novel: <info://" + getRandomUniqueId() + ">\n" 886 + "INSERT DATA { <> novel:value 'test' }")); 887 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 888 } 889 890 @Test 891 public void testRegisterNodeType() throws IOException { 892 final String testObj = ingestObj("/rest/test_nodetype"); 893 ingestAcl("fedoraAdmin", "/acls/14/acl.ttl", testObj + "/fcr:acl"); 894 895 final String id = "/rest/test_nodetype/" + getRandomUniqueId(); 896 ingestObj(id); 897 898 final HttpPatch patchReq = patchObjMethod(id); 899 setAuth(patchReq, "user14"); 900 patchReq.addHeader("Content-type", "application/sparql-update"); 901 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 902 + "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" 903 + "INSERT DATA { <> rdf:type dc:type }")); 904 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 905 } 906 907 908 @Test 909 public void testDeletePropertyAsUser() throws IOException { 910 final String testObj = ingestObj("/rest/test_delete"); 911 ingestAcl("fedoraAdmin", "/acls/15/acl.ttl", testObj + "/fcr:acl"); 912 913 final String id = "/rest/test_delete/" + getRandomUniqueId(); 914 ingestObj(id); 915 916 HttpPatch patchReq = patchObjMethod(id); 917 setAuth(patchReq, "user15"); 918 patchReq.addHeader("Content-type", "application/sparql-update"); 919 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 920 + "INSERT DATA { <> dc:title 'title' . " + 921 " <> dc:rights 'rights' . }")); 922 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 923 924 patchReq = patchObjMethod(id); 925 setAuth(patchReq, "user15"); 926 patchReq.addHeader("Content-type", "application/sparql-update"); 927 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 928 + "DELETE { <> dc:title ?any . } WHERE { <> dc:title ?any . }")); 929 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 930 931 patchReq = patchObjMethod(id); 932 setAuth(patchReq, "notUser15"); 933 patchReq.addHeader("Content-type", "application/sparql-update"); 934 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 935 + "DELETE { <> dc:rights ?any . } WHERE { <> dc:rights ?any . }")); 936 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchReq)); 937 } 938 939 @Test 940 public void testHeadWithReadOnlyUser() throws IOException { 941 final String testObj = ingestObj("/rest/test_head"); 942 ingestAcl("fedoraAdmin", "/acls/19/acl.ttl", testObj + "/fcr:acl"); 943 944 final HttpHead headReq = new HttpHead(testObj); 945 setAuth(headReq, "user19"); 946 assertEquals(HttpStatus.SC_OK, getStatus(headReq)); 947 } 948 949 @Test 950 public void testOptionsWithReadOnlyUser() throws IOException { 951 final String testObj = ingestObj("/rest/test_options"); 952 ingestAcl("fedoraAdmin", "/acls/20/acl.ttl", testObj + "/fcr:acl"); 953 954 final HttpOptions optionsReq = new HttpOptions(testObj); 955 setAuth(optionsReq, "user20"); 956 assertEquals(HttpStatus.SC_OK, getStatus(optionsReq)); 957 } 958 959 private static HttpResponse HEAD(final String requestURI) throws IOException { 960 return HEAD(requestURI, "fedoraAdmin"); 961 } 962 963 private static HttpResponse HEAD(final String requestURI, final String username) throws IOException { 964 final HttpHead req = new HttpHead(requestURI); 965 setAuth(req, username); 966 return execute(req); 967 } 968 969 private static HttpResponse PUT(final String requestURI) throws IOException { 970 return PUT(requestURI, "fedoraAdmin"); 971 } 972 973 private static HttpResponse PUT(final String requestURI, final String username) throws IOException { 974 final HttpPut req = new HttpPut(requestURI); 975 setAuth(req, username); 976 return execute(req); 977 } 978 979 private static HttpResponse DELETE(final String requestURI, final String username) throws IOException { 980 final HttpDelete req = new HttpDelete(requestURI); 981 setAuth(req, username); 982 return execute(req); 983 } 984 985 private static HttpResponse GET(final String requestURI, final String username) throws IOException { 986 final HttpGet req = new HttpGet(requestURI); 987 setAuth(req, username); 988 return execute(req); 989 } 990 991 private static HttpResponse PATCH(final String requestURI, final HttpEntity body, final String username) 992 throws IOException { 993 final HttpPatch req = new HttpPatch(requestURI); 994 setAuth(req, username); 995 if (body != null) { 996 req.setEntity(body); 997 } 998 return execute(req); 999 } 1000 1001 private static String getLink(final HttpResponse res) { 1002 for (final Header h : res.getHeaders("Link")) { 1003 final HeaderElement link = h.getElements()[0]; 1004 for (final NameValuePair param : link.getParameters()) { 1005 if (param.getName().equals("rel") && param.getValue().equals("acl")) { 1006 return link.getName().replaceAll("^<|>$", ""); 1007 } 1008 } 1009 } 1010 return null; 1011 } 1012 1013 private String ingestObjWithACL(final String path, final String aclResourcePath) throws IOException { 1014 final String newURI = ingestObj(path); 1015 final HttpResponse res = HEAD(newURI); 1016 final String aclURI = getLink(res); 1017 1018 logger.debug("Creating ACL at {}", aclURI); 1019 ingestAcl("fedoraAdmin", aclResourcePath, aclURI); 1020 1021 return newURI; 1022 } 1023 1024 @Test 1025 public void testControl() throws IOException { 1026 final String controlObj = ingestObjWithACL("/rest/control", "/acls/25/control.ttl"); 1027 final String readwriteObj = ingestObjWithACL("/rest/readwrite", "/acls/25/readwrite.ttl"); 1028 1029 final String rwChildACL = getLink(PUT(readwriteObj + "/child")); 1030 assertEquals(SC_FORBIDDEN, getStatus(HEAD(rwChildACL, "testuser"))); 1031 assertEquals(SC_FORBIDDEN, getStatus(GET(rwChildACL, "testuser"))); 1032 assertEquals(SC_FORBIDDEN, getStatus(PUT(rwChildACL, "testuser"))); 1033 assertEquals(SC_FORBIDDEN, getStatus(DELETE(rwChildACL, "testuser"))); 1034 1035 final String controlChildACL = getLink(PUT(controlObj + "/child")); 1036 assertEquals(SC_NOT_FOUND, getStatus(HEAD(controlChildACL, "testuser"))); 1037 assertEquals(SC_NOT_FOUND, getStatus(GET(controlChildACL, "testuser"))); 1038 1039 ingestAcl("testuser", "/acls/25/child-control.ttl", controlChildACL); 1040 final StringEntity sparqlUpdate = new StringEntity( 1041 "PREFIX acl: <http://www.w3.org/ns/auth/acl#> INSERT { <#restricted> acl:mode acl:Read } WHERE { }", 1042 ContentType.create("application/sparql-update")); 1043 assertEquals(SC_NO_CONTENT, getStatus(PATCH(controlChildACL, sparqlUpdate, "testuser"))); 1044 1045 assertEquals(SC_NO_CONTENT, getStatus(DELETE(controlChildACL, "testuser"))); 1046 } 1047 1048 @Test 1049 public void testAppendOnlyToContainer() throws IOException { 1050 final String testObj = ingestObj("/rest/test_append"); 1051 ingestAcl("fedoraAdmin", "/acls/23/acl.ttl", testObj + "/fcr:acl"); 1052 final String username = "user23"; 1053 1054 final HttpOptions optionsReq = new HttpOptions(testObj); 1055 setAuth(optionsReq, username); 1056 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(optionsReq)); 1057 1058 final HttpHead headReq = new HttpHead(testObj); 1059 setAuth(headReq, username); 1060 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(headReq)); 1061 1062 final HttpGet getReq = new HttpGet(testObj); 1063 setAuth(getReq, username); 1064 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(getReq)); 1065 1066 final HttpPut putReq = new HttpPut(testObj); 1067 setAuth(putReq, username); 1068 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putReq)); 1069 1070 final HttpDelete deleteReq = new HttpDelete(testObj); 1071 setAuth(deleteReq, username); 1072 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteReq)); 1073 1074 final HttpPost postReq = new HttpPost(testObj); 1075 setAuth(postReq, username); 1076 assertEquals(HttpStatus.SC_CREATED, getStatus(postReq)); 1077 1078 final String[] legalSPARQLQueries = new String[] { 1079 "INSERT DATA { <> <http://purl.org/dc/terms/title> \"Test23\" . }", 1080 "INSERT { <> <http://purl.org/dc/terms/alternative> \"Test XXIII\" . } WHERE {}", 1081 "DELETE {} INSERT { <> <http://purl.org/dc/terms/description> \"Test append only\" . } WHERE {}" 1082 }; 1083 for (final String query : legalSPARQLQueries) { 1084 final HttpPatch patchReq = new HttpPatch(testObj); 1085 setAuth(patchReq, username); 1086 patchReq.setEntity(new StringEntity(query)); 1087 patchReq.setHeader("Content-Type", "application/sparql-update"); 1088 logger.debug("Testing SPARQL update: {}", query); 1089 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 1090 } 1091 1092 final String[] illegalSPARQLQueries = new String[] { 1093 "DELETE DATA { <> <http://purl.org/dc/terms/title> \"Test23\" . }", 1094 "DELETE { <> <http://purl.org/dc/terms/alternative> \"Test XXIII\" . } WHERE {}", 1095 "DELETE { <> <http://purl.org/dc/terms/description> \"Test append only\" . } INSERT {} WHERE {}" 1096 }; 1097 for (final String query : illegalSPARQLQueries) { 1098 final HttpPatch patchReq = new HttpPatch(testObj); 1099 setAuth(patchReq, username); 1100 patchReq.setEntity(new StringEntity(query)); 1101 patchReq.setHeader("Content-Type", "application/sparql-update"); 1102 logger.debug("Testing SPARQL update: {}", query); 1103 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchReq)); 1104 } 1105 final String[] allowedDeleteSPARQLQueries = new String[] { 1106 "DELETE DATA {}", 1107 "DELETE { } WHERE {}", 1108 "DELETE { } INSERT {} WHERE {}" 1109 }; 1110 for (final String query : allowedDeleteSPARQLQueries) { 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_NO_CONTENT, getStatus(patchReq)); 1117 } 1118 1119 } 1120 1121 @Test 1122 public void testAppendOnlyToBinary() throws IOException { 1123 final String testObj = ingestBinary("/rest/test_append_binary", new StringEntity("foo")); 1124 ingestAcl("fedoraAdmin", "/acls/24/acl.ttl", testObj + "/fcr:acl"); 1125 final String username = "user24"; 1126 1127 final HttpOptions optionsReq = new HttpOptions(testObj); 1128 setAuth(optionsReq, username); 1129 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(optionsReq)); 1130 1131 final HttpHead headReq = new HttpHead(testObj); 1132 setAuth(headReq, username); 1133 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(headReq)); 1134 1135 final HttpGet getReq = new HttpGet(testObj); 1136 setAuth(getReq, username); 1137 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(getReq)); 1138 1139 final HttpPut putReq = new HttpPut(testObj); 1140 setAuth(putReq, username); 1141 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putReq)); 1142 1143 final HttpDelete deleteReq = new HttpDelete(testObj); 1144 setAuth(deleteReq, username); 1145 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteReq)); 1146 1147 final HttpPost postReq = new HttpPost(testObj); 1148 setAuth(postReq, username); 1149 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postReq)); 1150 } 1151 1152 @Test 1153 public void testFoafAgent() throws IOException { 1154 final String path = ingestObj("/rest/foaf-agent"); 1155 ingestAcl("fedoraAdmin", "/acls/26/foaf-agent.ttl", path + "/fcr:acl"); 1156 final String username = "user1"; 1157 1158 final HttpGet req = new HttpGet(path); 1159 1160 //NB: Actually no authentication headers should be set for this test 1161 //since the point of foaf:Agent is to allow unauthenticated access for everyone. 1162 //However at this time the test integration test server requires callers to 1163 //authenticate. 1164 setAuth(req, username); 1165 1166 assertEquals(HttpStatus.SC_OK, getStatus(req)); 1167 } 1168 1169 @Test 1170 public void testAuthenticatedAgent() throws IOException { 1171 final String path = ingestObj("/rest/authenticated-agent"); 1172 ingestAcl("fedoraAdmin", "/acls/26/authenticated-agent.ttl", path + "/fcr:acl"); 1173 final String username = "user1"; 1174 1175 final HttpGet darkReq = new HttpGet(path); 1176 setAuth(darkReq, username); 1177 assertEquals(HttpStatus.SC_OK, getStatus(darkReq)); 1178 } 1179 1180 @Test 1181 public void testAgentGroupWithHashUris() throws Exception { 1182 ingestTurtleResource("fedoraAdmin", "/acls/agent-group-list.ttl", 1183 serverAddress + "/rest/agent-group-list"); 1184 //check that the authorized are authorized. 1185 final String authorized = ingestObj("/rest/agent-group-with-hash-uri-authorized"); 1186 ingestAcl("fedoraAdmin", "/acls/agent-group-with-hash-uri-authorized.ttl", authorized + "/fcr:acl"); 1187 1188 final HttpGet getAuthorized = new HttpGet(authorized); 1189 setAuth(getAuthorized, "testuser"); 1190 assertEquals(HttpStatus.SC_OK, getStatus(getAuthorized)); 1191 1192 //check that the unauthorized are unauthorized. 1193 final String unauthorized = ingestObj("/rest/agent-group-with-hash-uri-unauthorized"); 1194 ingestAcl("fedoraAdmin", "/acls/agent-group-with-hash-uri-unauthorized.ttl", unauthorized + "/fcr:acl"); 1195 1196 final HttpGet getUnauthorized = new HttpGet(unauthorized); 1197 setAuth(getUnauthorized, "testuser"); 1198 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(getUnauthorized)); 1199 } 1200 1201 @Test 1202 public void testAgentGroupWithMembersAsURIs() throws Exception { 1203 System.setProperty(USER_AGENT_BASE_URI_PROPERTY, "http://example.com/"); 1204 ingestTurtleResource("fedoraAdmin", "/acls/agent-group-list-with-member-uris.ttl", 1205 serverAddress + "/rest/agent-group-list-with-member-uris"); 1206 final String authorized = ingestObj("/rest/agent-group-with-vcard-member-as-uri"); 1207 ingestAcl("fedoraAdmin", "/acls/agent-group-with-vcard-member-as-uri.ttl", authorized + "/fcr:acl"); 1208 //check that test user is authorized to write 1209 final HttpPut childPut = new HttpPut(authorized + "/child"); 1210 setAuth(childPut, "testuser"); 1211 assertEquals(HttpStatus.SC_CREATED, getStatus(childPut)); 1212 } 1213 1214 @Test 1215 public void testAgentGroup() throws Exception { 1216 ingestTurtleResource("fedoraAdmin", "/acls/agent-group-list-flat.ttl", 1217 serverAddress + "/rest/agent-group-list-flat"); 1218 //check that the authorized are authorized. 1219 final String flat = ingestObj("/rest/agent-group-flat"); 1220 ingestAcl("fedoraAdmin", "/acls/agent-group-flat.ttl", flat + "/fcr:acl"); 1221 1222 final HttpGet getFlat = new HttpGet(flat); 1223 setAuth(getFlat, "testuser"); 1224 assertEquals(HttpStatus.SC_OK, getStatus(getFlat)); 1225 } 1226 1227 @Test 1228 public void testAclAppendPermissions() throws Exception { 1229 final String testObj = ingestBinary("/rest/test-read-append", new StringEntity("foo")); 1230 ingestAcl("fedoraAdmin", "/acls/27/read-append.ttl", testObj + "/fcr:acl"); 1231 final String username = "user27"; 1232 1233 final HttpOptions optionsReq = new HttpOptions(testObj); 1234 setAuth(optionsReq, username); 1235 assertEquals(HttpStatus.SC_OK, getStatus(optionsReq)); 1236 1237 final HttpHead headReq = new HttpHead(testObj); 1238 setAuth(headReq, username); 1239 assertEquals(HttpStatus.SC_OK, getStatus(headReq)); 1240 1241 final HttpGet getReq = new HttpGet(testObj); 1242 setAuth(getReq, username); 1243 final String descriptionUri; 1244 try (final CloseableHttpResponse response = execute(getReq)) { 1245 assertEquals(HttpStatus.SC_OK, getStatus(response)); 1246 descriptionUri = Arrays.stream(response.getHeaders("Link")) 1247 .flatMap(header -> Arrays.stream(header.getValue().split(","))).map(linkStr -> Link.valueOf( 1248 linkStr)) 1249 .filter(link -> link.getRels().contains("describedby")).map(link -> link.getUri().toString()) 1250 .findFirst().orElse(null); 1251 } 1252 1253 1254 final HttpPut putReq = new HttpPut(testObj); 1255 setAuth(putReq, username); 1256 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putReq)); 1257 1258 final HttpDelete deleteReq = new HttpDelete(testObj); 1259 setAuth(deleteReq, username); 1260 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteReq)); 1261 1262 final HttpPost postReq = new HttpPost(testObj); 1263 setAuth(postReq, username); 1264 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postReq)); 1265 1266 if (descriptionUri != null) { 1267 final HttpOptions optionsDescReq = new HttpOptions(descriptionUri); 1268 setAuth(optionsDescReq, username); 1269 assertEquals(HttpStatus.SC_OK, getStatus(optionsDescReq)); 1270 1271 final HttpHead headDescReq = new HttpHead(descriptionUri); 1272 setAuth(headDescReq, username); 1273 assertEquals(HttpStatus.SC_OK, getStatus(headDescReq)); 1274 1275 final HttpGet getDescReq = new HttpGet(descriptionUri); 1276 setAuth(getDescReq, username); 1277 assertEquals(HttpStatus.SC_OK, getStatus(getDescReq)); 1278 1279 final HttpPut putDescReq = new HttpPut(descriptionUri); 1280 setAuth(putDescReq, username); 1281 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(putDescReq)); 1282 1283 final HttpDelete deleteDescReq = new HttpDelete(descriptionUri); 1284 setAuth(deleteDescReq, username); 1285 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(deleteDescReq)); 1286 1287 final HttpPost postDescReq = new HttpPost(descriptionUri); 1288 setAuth(postDescReq, username); 1289 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(postDescReq)); 1290 } 1291 } 1292}