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.fcrepo.auth.webac.URIConstants.WEBAC_ACCESS_CONTROL_VALUE; 023import static org.fcrepo.auth.webac.WebACRolesProvider.GROUP_AGENT_BASE_URI_PROPERTY; 024import static org.fcrepo.auth.webac.WebACRolesProvider.ROOT_AUTHORIZATION_PROPERTY; 025import static org.fcrepo.auth.webac.WebACRolesProvider.USER_AGENT_BASE_URI_PROPERTY; 026import static org.fcrepo.kernel.api.RdfLexicon.DC_NAMESPACE; 027import static org.junit.Assert.assertEquals; 028import static org.junit.Assert.assertTrue; 029 030import java.io.IOException; 031import java.io.InputStream; 032import java.util.Optional; 033 034import javax.ws.rs.core.Link; 035 036import org.apache.commons.codec.binary.Base64; 037import org.apache.http.Header; 038import org.apache.http.HttpResponse; 039import org.apache.http.HttpStatus; 040import org.apache.http.client.methods.CloseableHttpResponse; 041import org.apache.http.client.methods.HttpGet; 042import org.apache.http.client.methods.HttpPatch; 043import org.apache.http.client.methods.HttpPost; 044import org.apache.http.client.methods.HttpPut; 045import org.apache.http.entity.InputStreamEntity; 046import org.apache.http.entity.StringEntity; 047import org.apache.http.message.AbstractHttpMessage; 048import org.fcrepo.integration.http.api.AbstractResourceIT; 049import org.junit.Ignore; 050import org.junit.Test; 051import org.slf4j.Logger; 052import org.slf4j.LoggerFactory; 053 054/** 055 * @author Peter Eichman 056 * @author whikloj 057 * @since September 4, 2015 058 */ 059public class WebACRecipesIT extends AbstractResourceIT { 060 061 private static final Logger logger = LoggerFactory.getLogger(WebACRecipesIT.class); 062 063 private static final String DC_TITLE = DC_NAMESPACE + "title"; 064 065 /** 066 * Convenience method to create an ACL with 0 or more authorization resources in the respository. 067 */ 068 private String ingestAcl(final String username, final String aclResourcePath, 069 final String... authorizationResourcePaths) throws IOException { 070 071 // create the ACL 072 final HttpResponse aclResponse = ingestTurtleResource(username, aclResourcePath, "/rest"); 073 074 // get the URI to the newly created resource 075 final String aclURI = aclResponse.getFirstHeader("Location").getValue(); 076 077 // add all the authorizations 078 for (final String authorizationResourcePath : authorizationResourcePaths) { 079 ingestTurtleResource(username, authorizationResourcePath, aclURI.replace(serverAddress, "")); 080 } 081 082 return aclURI; 083 } 084 085 /** 086 * Convenience method to POST the contents of a Turtle file to the repository to create a new resource. Returns 087 * the HTTP response from that request. Throws an IOException if the server responds with anything other than a 088 * 201 Created response code. 089 */ 090 private HttpResponse ingestTurtleResource(final String username, final String path, final String requestURI) 091 throws IOException { 092 final HttpPost request = postObjMethod(requestURI); 093 094 logger.debug("POST to {} to create {}", requestURI, path); 095 096 setAuth(request, username); 097 098 final InputStream file = this.getClass().getResourceAsStream(path); 099 final InputStreamEntity fileEntity = new InputStreamEntity(file); 100 request.setEntity(fileEntity); 101 request.setHeader("Content-Type", "text/turtle;charset=UTF-8"); 102 103 try (final CloseableHttpResponse response = execute(request)) { 104 assertEquals("Didn't get a CREATED response!", CREATED.getStatusCode(), getStatus(response)); 105 return response; 106 } 107 108 } 109 110 /** 111 * Convenience method to set up a regular FedoraResource 112 * 113 * @param path Path to put the resource under 114 * @return the Location of the newly created resource 115 * @throws IOException 116 */ 117 private String ingestObj(final String path) throws IOException { 118 final HttpPut request = putObjMethod(path.replace(serverAddress, "")); 119 setAuth(request, "fedoraAdmin"); 120 try (final CloseableHttpResponse response = execute(request)) { 121 assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); 122 return response.getFirstHeader("Location").getValue(); 123 } 124 } 125 126 private String ingestDatastream(final String path, final String ds) throws IOException { 127 final HttpPut request = putDSMethod(path, ds, "some not so random content"); 128 setAuth(request, "fedoraAdmin"); 129 try (final CloseableHttpResponse response = execute(request)) { 130 assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); 131 return response.getFirstHeader("Location").getValue(); 132 } 133 } 134 135 /** 136 * Convenience method to link a Resource to a WebACL resource 137 * 138 * @param protectedResource path of the resource to be protected by the 139 * @param aclResource path of the Acl resource 140 */ 141 private void linkToAcl(final String protectedResource, final String aclResource) 142 throws IOException { 143 final HttpPatch request = patchObjMethod(protectedResource.replace(serverAddress, "")); 144 setAuth(request, "fedoraAdmin"); 145 request.setHeader("Content-type", "application/sparql-update"); 146 request.setEntity(new StringEntity( 147 "INSERT { <> <" + WEBAC_ACCESS_CONTROL_VALUE + "> <" + aclResource + "> . } WHERE {}")); 148 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(request)); 149 } 150 151 /** 152 * Convenience method for applying credentials to a request 153 * 154 * @param method the request to add the credentials to 155 * @param username the username to add 156 */ 157 private static void setAuth(final AbstractHttpMessage method, final String username) { 158 final String creds = username + ":password"; 159 final String encCreds = new String(Base64.encodeBase64(creds.getBytes())); 160 final String basic = "Basic " + encCreds; 161 method.setHeader("Authorization", basic); 162 } 163 164 @Test 165 public void scenario1() throws IOException { 166 final String testObj = ingestObj("/rest/webacl_box1"); 167 final String acl1 = ingestAcl("fedoraAdmin", "/acls/01/acl.ttl", "/acls/01/authorization.ttl"); 168 linkToAcl(testObj, acl1); 169 final String aclLink = Link.fromUri(acl1).rel("acl").build().toString(); 170 171 final HttpGet request = getObjMethod(testObj.replace(serverAddress, "")); 172 assertEquals("Anonymous can read " + testObj, HttpStatus.SC_FORBIDDEN, getStatus(request)); 173 174 setAuth(request, "user01"); 175 try (final CloseableHttpResponse response = execute(request)) { 176 assertEquals("User 'user01' can't read" + testObj, HttpStatus.SC_OK, getStatus(response)); 177 // This gets the Link headers and filters for the correct one (aclLink::equals) defined above. 178 final Optional<String> header = stream(response.getHeaders("Link")).map(Header::getValue) 179 .filter(aclLink::equals).findFirst(); 180 // So you either have the correct Link header or you get nothing. 181 assertTrue("Missing Link header", header.isPresent()); 182 } 183 184 final String childObj = ingestObj("/rest/webacl_box1/child"); 185 final HttpGet getReq = getObjMethod(childObj.replace(serverAddress, "")); 186 setAuth(getReq, "user01"); 187 try (final CloseableHttpResponse response = execute(getReq)) { 188 assertEquals("User 'user01' can't read child of " + testObj, HttpStatus.SC_OK, getStatus(response)); 189 // This gets the Link headers and filters for the correct one (aclLink::equals) defined above. 190 final Optional<String> header = stream(response.getHeaders("Link")).map(Header::getValue) 191 .filter(aclLink::equals).findFirst(); 192 // So you either have the correct Link header or you get nothing. 193 assertTrue("Missing Link header to ACL on parent", header.isPresent()); 194 } 195 } 196 197 @Test 198 public void scenario2() throws IOException { 199 final String id = "/rest/box/bag/collection"; 200 final String testObj = ingestObj(id); 201 final String acl2 = ingestAcl("fedoraAdmin", "/acls/02/acl.ttl", "/acls/02/authorization.ttl"); 202 linkToAcl(testObj, acl2); 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 { <> <" + DC_TITLE + "> \"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(new StringEntity("INSERT { <> <" + DC_TITLE + "> \"Different title\" . } WHERE {}")); 225 requestPatch2.setHeader("Content-type", "application/sparql-update"); 226 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch2)); 227 } 228 229 @Test 230 public void scenario3() throws IOException { 231 final String idDark = "/rest/dark/archive"; 232 final String idLight = "/rest/dark/archive/sunshine"; 233 final String testObj = ingestObj(idDark); 234 final String testObj2 = ingestObj(idLight); 235 final String acl3 = 236 ingestAcl("fedoraAdmin", "/acls/03/acl.ttl", "/acls/03/auth_open.ttl", "/acls/03/auth_restricted.ttl"); 237 linkToAcl(testObj, acl3); 238 239 logger.debug("Anonymous can't read " + testObj); 240 final HttpGet requestGet = getObjMethod(idDark); 241 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet)); 242 243 logger.debug("Restricted can read " + testObj); 244 final HttpGet requestGet2 = getObjMethod(idDark); 245 setAuth(requestGet2, "jones"); 246 requestGet2.setHeader("some-header", "Restricted"); 247 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 248 249 logger.debug("Anonymous can read " + testObj2); 250 final HttpGet requestGet3 = getObjMethod(idLight); 251 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 252 253 logger.debug("Restricted can read " + testObj2); 254 final HttpGet requestGet4 = getObjMethod(idLight); 255 setAuth(requestGet4, "jones"); 256 requestGet4.setHeader("some-header", "Restricted"); 257 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 258 } 259 260 @Test 261 public void scenario4() throws IOException { 262 final String id = "/rest/public_collection"; 263 final String testObj = ingestObj(id); 264 final String acl4 = ingestAcl("fedoraAdmin", "/acls/04/acl.ttl", "/acls/04/auth1.ttl", "/acls/04/auth2.ttl"); 265 linkToAcl(testObj, acl4); 266 267 logger.debug("Anonymous can read " + testObj); 268 final HttpGet requestGet = getObjMethod(id); 269 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 270 271 logger.debug("Editors can read " + testObj); 272 final HttpGet requestGet2 = getObjMethod(id); 273 setAuth(requestGet2, "jones"); 274 requestGet2.setHeader("some-header", "Editors"); 275 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 276 277 logger.debug("Smith can access " + testObj); 278 final HttpGet requestGet3 = getObjMethod(id); 279 setAuth(requestGet3, "smith"); 280 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 281 282 logger.debug("Anonymous can't write " + testObj); 283 final HttpPatch requestPatch = patchObjMethod(id); 284 requestPatch.setHeader("Content-type", "application/sparql-update"); 285 requestPatch.setEntity(new StringEntity("INSERT { <> <" + DC_TITLE + "> \"Change title\" . } WHERE {}")); 286 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch)); 287 288 logger.debug("Editors can write " + testObj); 289 final HttpPatch requestPatch2 = patchObjMethod(id); 290 requestPatch2.setHeader("Content-type", "application/sparql-update"); 291 requestPatch2.setEntity(new StringEntity("INSERT { <> <" + DC_TITLE + "> \"New title\" . } WHERE {}")); 292 setAuth(requestPatch2, "jones"); 293 requestPatch2.setHeader("some-header", "Editors"); 294 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch2)); 295 296 logger.debug("Editors can create (PUT) child objects of " + testObj); 297 final HttpPut requestPut1 = putObjMethod(id + "/child1"); 298 setAuth(requestPut1, "jones"); 299 requestPut1.setHeader("some-header", "Editors"); 300 assertEquals(HttpStatus.SC_CREATED, getStatus(requestPut1)); 301 302 final HttpGet requestGet4 = getObjMethod(id + "/child1"); 303 setAuth(requestGet4, "jones"); 304 requestGet4.setHeader("some-header", "Editors"); 305 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 306 307 logger.debug("Editors can create (POST) child objects of " + testObj); 308 final HttpPost requestPost1 = postObjMethod(id); 309 requestPost1.addHeader("Slug", "child2"); 310 setAuth(requestPost1, "jones"); 311 requestPost1.setHeader("some-header", "Editors"); 312 assertEquals(HttpStatus.SC_CREATED, getStatus(requestPost1)); 313 314 final HttpGet requestGet5 = getObjMethod(id + "/child2"); 315 setAuth(requestGet5, "jones"); 316 requestGet5.setHeader("some-header", "Editors"); 317 assertEquals(HttpStatus.SC_OK, getStatus(requestGet5)); 318 319 logger.debug("Editors can create nested child objects of " + testObj); 320 final HttpPut requestPut2 = putObjMethod(id + "/a/b/c/child"); 321 setAuth(requestPut2, "jones"); 322 requestPut2.setHeader("some-header", "Editors"); 323 assertEquals(HttpStatus.SC_CREATED, getStatus(requestPut2)); 324 325 final HttpGet requestGet6 = getObjMethod(id + "/a/b/c/child"); 326 setAuth(requestGet6, "jones"); 327 requestGet6.setHeader("some-header", "Editors"); 328 assertEquals(HttpStatus.SC_OK, getStatus(requestGet6)); 329 330 logger.debug("Smith can't write " + testObj); 331 final HttpPatch requestPatch3 = patchObjMethod(id); 332 requestPatch3.setHeader("Content-type", "application/sparql-update"); 333 requestPatch3.setEntity(new StringEntity("INSERT { <> <" + DC_TITLE + "> \"Different title\" . } WHERE {}")); 334 setAuth(requestPatch3, "smith"); 335 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestPatch3)); 336 } 337 338 @Test 339 public void scenario5() throws IOException { 340 final String idPublic = "/rest/mixedCollection/publicObj"; 341 final String idPrivate = "/rest/mixedCollection/privateObj"; 342 final String testObj = ingestObj("/rest/mixedCollection"); 343 final String publicObj = ingestObj(idPublic); 344 final HttpPatch patch = patchObjMethod(idPublic); 345 final String acl5 = 346 ingestAcl("fedoraAdmin", "/acls/05/acl.ttl", "/acls/05/auth_open.ttl", "/acls/05/auth_restricted.ttl"); 347 linkToAcl(testObj, acl5); 348 349 setAuth(patch, "fedoraAdmin"); 350 patch.setHeader("Content-type", "application/sparql-update"); 351 patch.setEntity(new StringEntity("INSERT { <> a <http://example.com/terms#publicImage> . } WHERE {}")); 352 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patch)); 353 354 final String privateObj = ingestObj(idPrivate); 355 356 logger.debug("Anonymous can see eg:publicImage " + publicObj); 357 final HttpGet requestGet = getObjMethod(idPublic); 358 assertEquals(HttpStatus.SC_OK, getStatus(requestGet)); 359 360 logger.debug("Anonymous can't see other resource " + privateObj); 361 final HttpGet requestGet2 = getObjMethod(idPrivate); 362 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet2)); 363 364 logger.debug("Admins can see eg:publicImage " + publicObj); 365 final HttpGet requestGet3 = getObjMethod(idPublic); 366 setAuth(requestGet3, "jones"); 367 requestGet3.setHeader("some-header", "Admins"); 368 assertEquals(HttpStatus.SC_OK, getStatus(requestGet3)); 369 370 logger.debug("Admins can see others" + privateObj); 371 final HttpGet requestGet4 = getObjMethod(idPrivate); 372 setAuth(requestGet4, "jones"); 373 requestGet4.setHeader("some-header", "Admins"); 374 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 375 } 376 377 @Test 378 public void scenario9() throws IOException { 379 final String idPublic = "/rest/anotherCollection/publicObj"; 380 final String groups = "/rest/group"; 381 final String fooGroup = groups + "/foo"; 382 final String testObj = ingestObj("/rest/anotherCollection"); 383 final String publicObj = ingestObj(idPublic); 384 385 final HttpPut request = putObjMethod(fooGroup); 386 setAuth(request, "fedoraAdmin"); 387 388 final InputStream file = this.getClass().getResourceAsStream("/acls/09/group.ttl"); 389 final InputStreamEntity fileEntity = new InputStreamEntity(file); 390 request.setEntity(fileEntity); 391 request.setHeader("Content-Type", "text/turtle;charset=UTF-8"); 392 393 assertEquals("Didn't get a CREATED response!", CREATED.getStatusCode(), getStatus(request)); 394 395 final String acl9 = ingestAcl("fedoraAdmin", "/acls/09/acl.ttl", "/acls/09/authorization.ttl"); 396 linkToAcl(testObj, acl9); 397 398 logger.debug("Person1 can see object " + publicObj); 399 final HttpGet requestGet1 = getObjMethod(idPublic); 400 setAuth(requestGet1, "person1"); 401 assertEquals(HttpStatus.SC_OK, getStatus(requestGet1)); 402 403 logger.debug("Person2 can see object " + publicObj); 404 final HttpGet requestGet2 = getObjMethod(idPublic); 405 setAuth(requestGet2, "person2"); 406 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 407 408 logger.debug("Person3 user cannot see object " + publicObj); 409 final HttpGet requestGet3 = getObjMethod(idPublic); 410 setAuth(requestGet3, "person3"); 411 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet3)); 412 } 413 414 @Test 415 public void testAccessToRoot() throws IOException { 416 final String id = "/rest/" + getRandomUniqueId(); 417 final String testObj = ingestObj(id); 418 final String acl = ingestAcl("fedoraAdmin", "/acls/06/acl.ttl", "/acls/06/authorization.ttl", 419 "/acls/06/noslash.ttl"); 420 421 logger.debug("Anonymous can't read (no ACL): {}", id); 422 final HttpGet requestGet1 = getObjMethod(id); 423 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet1)); 424 425 logger.debug("Can username 'user06a' read {} (no ACL)", id); 426 final HttpGet requestGet2 = getObjMethod(id); 427 setAuth(requestGet2, "user06a"); 428 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet2)); 429 430 logger.debug("Can username 'notuser06b' read {} (no ACL)", id); 431 final HttpGet requestGet3 = getObjMethod(id); 432 setAuth(requestGet3, "user06b"); 433 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet3)); 434 435 System.setProperty(ROOT_AUTHORIZATION_PROPERTY, "./target/test-classes/test-root-authorization2.ttl"); 436 logger.debug("Can username 'user06a' read {} (overridden system ACL)", id); 437 final HttpGet requestGet4 = getObjMethod(id); 438 setAuth(requestGet4, "user06a"); 439 assertEquals(HttpStatus.SC_OK, getStatus(requestGet4)); 440 System.clearProperty(ROOT_AUTHORIZATION_PROPERTY); 441 442 // Add ACL to root 443 linkToAcl("/rest/", acl); 444 445 logger.debug("Anonymous still can't read (ACL present)"); 446 final HttpGet requestGet5 = getObjMethod(id); 447 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet5)); 448 449 logger.debug("Can username 'user06a' read {} (ACL present)", testObj); 450 final HttpGet requestGet6 = getObjMethod(id); 451 setAuth(requestGet6, "user06a"); 452 assertEquals(HttpStatus.SC_OK, getStatus(requestGet6)); 453 454 logger.debug("Can username 'user06b' read {} (ACL present)", testObj); 455 final HttpGet requestGet7 = getObjMethod(id); 456 setAuth(requestGet7, "user06b"); 457 assertEquals(HttpStatus.SC_OK, getStatus(requestGet7)); 458 } 459 460 @Test 461 public void testAccessToBinary() throws IOException { 462 // Block access to "book" 463 final String idBook = "/rest/book"; 464 ingestObj(idBook); 465 466 // Open access datastream, "file" 467 final String id = idBook + "/file"; 468 final String testObj = ingestDatastream(idBook, "file"); 469 final String acl = ingestAcl("fedoraAdmin", 470 "/acls/07/acl.ttl", 471 "/acls/07/authorization.ttl", 472 "/acls/07/authorization-book.ttl"); 473 474 linkToAcl(id + "/fcr:metadata", acl); 475 476 logger.debug("Anonymous can't read"); 477 final HttpGet requestGet1 = getObjMethod(id); 478 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet1)); 479 480 logger.debug("Can username 'user07' read {}", testObj); 481 final HttpGet requestGet2 = getObjMethod(id); 482 483 setAuth(requestGet2, "user07"); 484 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 485 } 486 487 @Test 488 @Ignore("FAILING") 489 public void testAccessToHashResource() throws IOException { 490 final String id = "/rest/some/parent#hash-resource"; 491 final String testObj = ingestObj(id); 492 final String acl = ingestAcl("fedoraAdmin", "/acls/08/acl.ttl", "/acls/08/authorization.ttl"); 493 linkToAcl(testObj, acl); 494 495 logger.debug("Anonymous can't read"); 496 final HttpGet requestGet1 = getObjMethod(id); 497 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet1)); 498 499 logger.debug("Can username 'user08' read {}", testObj); 500 final HttpGet requestGet2 = getObjMethod(id); 501 setAuth(requestGet2, "user08"); 502 assertEquals(HttpStatus.SC_OK, getStatus(requestGet2)); 503 } 504 505 @Test 506 public void testAccessToVersionedResources() throws IOException { 507 final String idVersion = "/rest/versionResource"; 508 ingestObj(idVersion); 509 510 final HttpPatch requestPatch1 = patchObjMethod(idVersion); 511 setAuth(requestPatch1, "fedoraAdmin"); 512 requestPatch1.addHeader("Content-type", "application/sparql-update"); 513 requestPatch1.setEntity( 514 new StringEntity("PREFIX pcdm: <http://pcdm.org/models#> INSERT { <> a pcdm:Object } WHERE {}")); 515 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(requestPatch1)); 516 517 final String acl = ingestAcl("fedoraAdmin", 518 "/acls/10/acl.ttl", 519 "/acls/10/authorization.ttl"); 520 521 linkToAcl(idVersion, acl); 522 523 final HttpGet requestGet1 = getObjMethod(idVersion); 524 setAuth(requestGet1, "user10"); 525 assertEquals("user10 can't read object", HttpStatus.SC_OK, getStatus(requestGet1)); 526 527 final HttpPost requestPost1 = postObjMethod(idVersion + "/fcr:versions"); 528 requestPost1.addHeader("Slug", "v0"); 529 setAuth(requestPost1, "fedoraAdmin"); 530 assertEquals("Unable to create a new version", HttpStatus.SC_CREATED, getStatus(requestPost1)); 531 532 final HttpGet requestGet2 = getObjMethod(idVersion); 533 setAuth(requestGet2, "user10"); 534 assertEquals("user10 can't read versioned object", HttpStatus.SC_OK, getStatus(requestGet2)); 535 } 536 537 @Test 538 public void testDelegatedUserAccess() throws IOException { 539 logger.debug("testing delegated authentication"); 540 final String targetPath = "/rest/foo"; 541 final String targetResource = ingestObj(targetPath); 542 543 final String acl = ingestAcl("fedoraAdmin", "/acls/11/acl.ttl", "/acls/11/authorization.ttl"); 544 linkToAcl(targetResource, acl); 545 546 final HttpGet adminGet = getObjMethod(targetPath); 547 setAuth(adminGet, "fedoraAdmin"); 548 assertEquals("admin can read object", HttpStatus.SC_OK, getStatus(adminGet)); 549 550 final HttpGet adminDelegatedGet = getObjMethod(targetPath); 551 setAuth(adminDelegatedGet, "fedoraAdmin"); 552 adminDelegatedGet.addHeader("On-Behalf-Of", "user11"); 553 assertEquals("delegated user can read object", HttpStatus.SC_OK, getStatus(adminDelegatedGet)); 554 555 final HttpGet adminUnauthorizedDelegatedGet = getObjMethod(targetPath); 556 setAuth(adminUnauthorizedDelegatedGet, "fedoraAdmin"); 557 adminUnauthorizedDelegatedGet.addHeader("On-Behalf-Of", "fakeuser"); 558 assertEquals("delegated fakeuser cannot read object", HttpStatus.SC_FORBIDDEN, 559 getStatus(adminUnauthorizedDelegatedGet)); 560 561 final HttpGet adminDelegatedGet2 = getObjMethod(targetPath); 562 setAuth(adminDelegatedGet2, "fedoraAdmin"); 563 adminDelegatedGet2.addHeader("On-Behalf-Of", "info:user/user2"); 564 assertEquals("delegated user can read object", HttpStatus.SC_OK, getStatus(adminDelegatedGet2)); 565 566 final HttpGet adminUnauthorizedDelegatedGet2 = getObjMethod(targetPath); 567 setAuth(adminUnauthorizedDelegatedGet2, "fedoraAdmin"); 568 adminUnauthorizedDelegatedGet2.addHeader("On-Behalf-Of", "info:user/fakeuser"); 569 assertEquals("delegated fakeuser cannot read object", HttpStatus.SC_FORBIDDEN, 570 getStatus(adminUnauthorizedDelegatedGet2)); 571 572 // Now test with the system property in effect 573 System.setProperty(USER_AGENT_BASE_URI_PROPERTY, "info:user/"); 574 System.setProperty(GROUP_AGENT_BASE_URI_PROPERTY, "info:group/"); 575 576 final HttpGet adminDelegatedGet3 = getObjMethod(targetPath); 577 setAuth(adminDelegatedGet3, "fedoraAdmin"); 578 adminDelegatedGet3.addHeader("On-Behalf-Of", "info:user/user2"); 579 assertEquals("delegated user can read object", HttpStatus.SC_OK, getStatus(adminDelegatedGet3)); 580 581 final HttpGet adminUnauthorizedDelegatedGet3 = getObjMethod(targetPath); 582 setAuth(adminUnauthorizedDelegatedGet3, "fedoraAdmin"); 583 adminUnauthorizedDelegatedGet3.addHeader("On-Behalf-Of", "info:user/fakeuser"); 584 assertEquals("delegated fakeuser cannot read object", HttpStatus.SC_FORBIDDEN, 585 getStatus(adminUnauthorizedDelegatedGet3)); 586 587 System.clearProperty(USER_AGENT_BASE_URI_PROPERTY); 588 System.clearProperty(GROUP_AGENT_BASE_URI_PROPERTY); 589 } 590 591 @Test 592 public void testAccessByUriToVersionedResources() throws IOException { 593 final String idVersion = "/rest/versionResourceUri"; 594 ingestObj(idVersion); 595 596 final String acl = ingestAcl("fedoraAdmin", 597 "/acls/12/acl.ttl", 598 "/acls/12/authorization.ttl"); 599 600 linkToAcl(idVersion, acl); 601 602 final HttpGet requestGet1 = getObjMethod(idVersion); 603 setAuth(requestGet1, "user12"); 604 assertEquals("testuser can't read object", HttpStatus.SC_OK, getStatus(requestGet1)); 605 606 final HttpPost requestPost1 = postObjMethod(idVersion + "/fcr:versions"); 607 requestPost1.addHeader("Slug", "v0"); 608 setAuth(requestPost1, "user12"); 609 assertEquals("Unable to create a new version", HttpStatus.SC_CREATED, getStatus(requestPost1)); 610 611 final HttpGet requestGet2 = getObjMethod(idVersion); 612 setAuth(requestGet2, "user12"); 613 assertEquals("testuser can't read versioned object", HttpStatus.SC_OK, getStatus(requestGet2)); 614 } 615 616 @Test 617 public void testAgentAsUri() throws IOException { 618 final String id = "/rest/" + getRandomUniqueId(); 619 final String testObj = ingestObj(id); 620 final String acl = ingestAcl("fedoraAdmin", "/acls/16/acl.ttl", "/acls/16/authorization.ttl"); 621 622 logger.debug("Anonymous can't read (no ACL): {}", id); 623 final HttpGet requestGet1 = getObjMethod(id); 624 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet1)); 625 626 logger.debug("Can username 'smith123' read {} (no ACL)", id); 627 final HttpGet requestGet2 = getObjMethod(id); 628 setAuth(requestGet2, "smith123"); 629 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet2)); 630 631 System.setProperty(USER_AGENT_BASE_URI_PROPERTY, "info:user/"); 632 System.setProperty(GROUP_AGENT_BASE_URI_PROPERTY, "info:group/"); 633 634 logger.debug("Can username 'smith123' read {} (overridden system ACL)", id); 635 final HttpGet requestGet3 = getObjMethod(id); 636 setAuth(requestGet3, "smith123"); 637 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet3)); 638 639 logger.debug("Can username 'group123' read {} (overridden system ACL)", id); 640 final HttpGet requestGet4 = getObjMethod(id); 641 setAuth(requestGet4, "group123"); 642 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet4)); 643 644 System.clearProperty(USER_AGENT_BASE_URI_PROPERTY); 645 System.clearProperty(GROUP_AGENT_BASE_URI_PROPERTY); 646 647 // Add ACL to object 648 linkToAcl(testObj, acl); 649 650 logger.debug("Anonymous still can't read (ACL present)"); 651 final HttpGet requestGet5 = getObjMethod(id); 652 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet5)); 653 654 logger.debug("Can username 'smith123' read {} (ACL present, no system properties)", testObj); 655 final HttpGet requestGet6 = getObjMethod(id); 656 setAuth(requestGet6, "smith123"); 657 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(requestGet6)); 658 659 System.setProperty(USER_AGENT_BASE_URI_PROPERTY, "info:user/"); 660 System.setProperty(GROUP_AGENT_BASE_URI_PROPERTY, "info:group/"); 661 662 logger.debug("Can username 'smith123' read {} (ACL, system properties present)", id); 663 final HttpGet requestGet7 = getObjMethod(id); 664 setAuth(requestGet7, "smith123"); 665 assertEquals(HttpStatus.SC_OK, getStatus(requestGet7)); 666 667 logger.debug("Can groupname 'group123' read {} (ACL, system properties present)", id); 668 final HttpGet requestGet8 = getObjMethod(id); 669 setAuth(requestGet8, "group123"); 670 assertEquals(HttpStatus.SC_OK, getStatus(requestGet8)); 671 672 System.clearProperty(USER_AGENT_BASE_URI_PROPERTY); 673 System.clearProperty(GROUP_AGENT_BASE_URI_PROPERTY); 674 } 675 676 @Test 677 public void testInvalidAccessControlLink() throws IOException { 678 final String id = "/rest/" + getRandomUniqueId(); 679 ingestObj(id); 680 681 final HttpPatch patchReq = patchObjMethod(id); 682 setAuth(patchReq, "fedoraAdmin"); 683 patchReq.addHeader("Content-type", "application/sparql-update"); 684 patchReq.setEntity(new StringEntity( 685 "INSERT { <> <" + WEBAC_ACCESS_CONTROL_VALUE + "> \"/rest/acl/badAclLink\" . } WHERE {}")); 686 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 687 688 final HttpGet getReq = getObjMethod(id); 689 setAuth(getReq, "fedoraAdmin"); 690 assertEquals("Non-URI accessControl property did not throw Exception", HttpStatus.SC_BAD_REQUEST, 691 getStatus(getReq)); 692 } 693 694 @Test 695 public void testRegisterNamespace() throws IOException { 696 final String testObj = ingestObj("/rest/test_namespace"); 697 final String acl1 = ingestAcl("fedoraAdmin", "/acls/13/acl.ttl", "/acls/13/authorization.ttl"); 698 linkToAcl(testObj, acl1); 699 700 final String id = "/rest/test_namespace/" + getRandomUniqueId(); 701 ingestObj(id); 702 703 final HttpPatch patchReq = patchObjMethod(id); 704 setAuth(patchReq, "user13"); 705 patchReq.addHeader("Content-type", "application/sparql-update"); 706 patchReq.setEntity(new StringEntity("PREFIX novel: <info://" + getRandomUniqueId() + ">\n" 707 + "INSERT DATA { <> novel:value 'test' }")); 708 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 709 } 710 711 @Test 712 public void testRegisterNodeType() throws IOException { 713 final String testObj = ingestObj("/rest/test_nodetype"); 714 final String acl1 = ingestAcl("fedoraAdmin", "/acls/14/acl.ttl", "/acls/14/authorization.ttl"); 715 linkToAcl(testObj, acl1); 716 717 final String id = "/rest/test_nodetype/" + getRandomUniqueId(); 718 ingestObj(id); 719 720 final HttpPatch patchReq = patchObjMethod(id); 721 setAuth(patchReq, "user14"); 722 patchReq.addHeader("Content-type", "application/sparql-update"); 723 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 724 + "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" 725 + "INSERT DATA { <> rdf:type dc:type }")); 726 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 727 } 728 729 730 @Test 731 public void testDeletePropertyAsUser() throws IOException { 732 final String testObj = ingestObj("/rest/test_delete"); 733 final String acl1 = ingestAcl("fedoraAdmin", "/acls/15/acl.ttl", "/acls/15/authorization.ttl"); 734 linkToAcl(testObj, acl1); 735 736 final String id = "/rest/test_delete/" + getRandomUniqueId(); 737 ingestObj(id); 738 739 HttpPatch patchReq = patchObjMethod(id); 740 setAuth(patchReq, "user15"); 741 patchReq.addHeader("Content-type", "application/sparql-update"); 742 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 743 + "INSERT DATA { <> dc:title 'title' . " + 744 " <> dc:rights 'rights' . }")); 745 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 746 747 patchReq = patchObjMethod(id); 748 setAuth(patchReq, "user15"); 749 patchReq.addHeader("Content-type", "application/sparql-update"); 750 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 751 + "DELETE { <> dc:title ?any . } WHERE { <> dc:title ?any . }")); 752 assertEquals(HttpStatus.SC_NO_CONTENT, getStatus(patchReq)); 753 754 patchReq = patchObjMethod(id); 755 setAuth(patchReq, "notUser15"); 756 patchReq.addHeader("Content-type", "application/sparql-update"); 757 patchReq.setEntity(new StringEntity("PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" 758 + "DELETE { <> dc:rights ?any . } WHERE { <> dc:rights ?any . }")); 759 assertEquals(HttpStatus.SC_FORBIDDEN, getStatus(patchReq)); 760 } 761 762 @Test 763 public void testNoAccessRolesResource() throws IOException { 764 final String objectPath = "/rest/test_accessroles"; 765 ingestObj(objectPath); 766 final String accessRolesResource = objectPath + "/fcr:accessroles"; 767 final HttpGet getReq = getObjMethod(accessRolesResource); 768 setAuth(getReq, "fedoraAdmin"); 769 assertEquals(HttpStatus.SC_NOT_FOUND, getStatus(getReq)); 770 } 771 772 773}