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}