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.auth.webac;
019
020import static java.util.Collections.singletonList;
021import static org.apache.jena.graph.NodeFactory.createURI;
022import static org.apache.jena.rdf.model.ModelFactory.createDefaultModel;
023import static org.apache.jena.riot.Lang.TTL;
024import static org.fcrepo.auth.webac.URIConstants.FOAF_AGENT_VALUE;
025import static org.fcrepo.auth.webac.URIConstants.VCARD_GROUP;
026import static org.fcrepo.auth.webac.URIConstants.WEBAC_MODE_READ_VALUE;
027import static org.fcrepo.auth.webac.URIConstants.WEBAC_MODE_WRITE_VALUE;
028import static org.fcrepo.kernel.api.FedoraTypes.FEDORA_ID_PREFIX;
029import static org.fcrepo.kernel.api.RdfLexicon.BASIC_CONTAINER;
030import static org.fcrepo.kernel.api.RdfLexicon.FEDORA_RESOURCE;
031import static org.junit.Assert.assertEquals;
032import static org.junit.Assert.assertNull;
033import static org.junit.Assert.assertTrue;
034import static org.mockito.Mockito.when;
035import static org.springframework.test.util.ReflectionTestUtils.setField;
036
037import java.net.URI;
038import java.nio.file.Paths;
039import java.util.ArrayList;
040import java.util.Collection;
041import java.util.List;
042import java.util.Map;
043import java.util.Optional;
044
045import org.fcrepo.config.AuthPropsConfig;
046import org.fcrepo.kernel.api.RdfStream;
047import org.fcrepo.kernel.api.Transaction;
048import org.fcrepo.kernel.api.auth.ACLHandle;
049import org.fcrepo.kernel.api.exception.PathNotFoundException;
050import org.fcrepo.kernel.api.exception.RepositoryException;
051import org.fcrepo.kernel.api.identifiers.FedoraId;
052import org.fcrepo.kernel.api.models.FedoraResource;
053import org.fcrepo.kernel.api.models.ResourceFactory;
054import org.fcrepo.kernel.api.rdf.DefaultRdfStream;
055
056import org.apache.jena.graph.Triple;
057import org.apache.jena.rdf.model.Model;
058import org.apache.jena.riot.Lang;
059import org.apache.jena.riot.RDFDataMgr;
060import org.junit.Before;
061import org.junit.Ignore;
062import org.junit.Rule;
063import org.junit.Test;
064import org.junit.contrib.java.lang.system.RestoreSystemProperties;
065import org.junit.runner.RunWith;
066import org.mockito.Mock;
067import org.mockito.junit.MockitoJUnitRunner;
068
069import com.github.benmanes.caffeine.cache.Cache;
070import com.github.benmanes.caffeine.cache.Caffeine;
071
072/**
073 * @author acoburn
074 * @since 9/3/15
075 */
076@RunWith(MockitoJUnitRunner.Silent.class)
077public class WebACRolesProviderTest {
078
079    private WebACRolesProvider roleProvider;
080
081    private static final String FEDORA_PREFIX = "info:fedora";
082    private static final String FEDORA_URI_PREFIX = "file:///rest";
083    private static final URI FEDORA_RESOURCE_URI = URI.create(FEDORA_RESOURCE.getURI());
084
085    @Mock
086    private Transaction mockTransaction;
087
088    @Mock
089    private ResourceFactory mockResourceFactory;
090
091    @Mock
092    private FedoraResource mockResource, mockParentResource;
093
094    @Mock
095    private FedoraResource mockAclResource;
096
097    @Mock
098    private FedoraResource mockAgentClassResource;
099
100    @Rule
101    public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
102
103    private AuthPropsConfig propsConfig;
104
105    private Cache<String, Optional<ACLHandle>> authHandleCache;
106
107    @Before
108    public void setUp() throws RepositoryException {
109        authHandleCache = Caffeine.newBuilder().build();
110        propsConfig = new AuthPropsConfig();
111        roleProvider = new WebACRolesProvider();
112        setField(roleProvider, "resourceFactory", mockResourceFactory);
113        setField(roleProvider, "authPropsConfig", propsConfig);
114        setField(roleProvider, "authHandleCache", authHandleCache);
115
116        when(mockResource.getDescribedResource()).thenReturn(mockResource);
117        when(mockResource.getDescription()).thenReturn(mockResource);
118
119        when(mockResource.getOriginalResource()).thenReturn(mockResource);
120        when(mockResource.getInteractionModel()).thenReturn(BASIC_CONTAINER.getURI());
121    }
122
123    private void assertOnlyDefaultAgentInRoles(final Map<String, Collection<String>> roles) {
124        assertEquals(1, roles.size());
125        assertTrue(roles.keySet().contains(FOAF_AGENT_VALUE));
126    }
127
128    @Test
129    public void noAclTest() throws RepositoryException {
130        final String accessTo = "/dark/archive/sunshine";
131
132        when(mockResource.getAcl()).thenReturn(null);
133        when(mockParentResource.getAcl()).thenReturn(null);
134
135        when(mockResource.getId()).thenReturn(accessTo);
136        when(mockResource.getContainer()).thenReturn(mockParentResource);
137        when(mockResource.getTriples())
138                .thenReturn(new DefaultRdfStream(createURI("subject")));
139        when(mockResource.getOriginalResource()).thenReturn(mockResource);
140
141        when(mockParentResource.getOriginalResource()).thenReturn(mockParentResource);
142        when(mockParentResource.getId()).thenReturn(null);
143
144        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
145
146        assertOnlyDefaultAgentInRoles(roles);
147    }
148
149    @Ignore // TODO FIX THIS TEST
150    @Test
151    public void acl01ParentTest() throws RepositoryException {
152        final String agent = "user01";
153        final String parentPath = "/webacl_box1";
154        final String accessTo = parentPath + "/foo";
155        final String acl = "/acls/01/acl.ttl";
156
157        when(mockResource.getAcl()).thenReturn(null);
158        when(mockParentResource.getAcl()).thenReturn(mockAclResource);
159
160        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
161        when(mockResource.getContainer()).thenReturn(mockParentResource);
162        when(mockResource.getOriginalResource()).thenReturn(mockResource);
163        when(mockResource.getAcl()).thenReturn(mockAclResource);
164
165        when(mockParentResource.getId()).thenReturn(addPrefix(parentPath));
166        when(mockParentResource.getAcl()).thenReturn(mockAclResource);
167        when(mockAclResource.isAcl()).thenReturn(true);
168        when(mockAclResource.getId()).thenReturn(addPrefix(parentPath) + "/fcr:acl");
169
170        when(mockAclResource.getTriples())
171                .thenReturn(getRdfStreamFromResource(acl, TTL));
172
173        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
174
175        assertEquals("There should be exactly one agent in the role map", 1, roles.size());
176        assertEquals("The agent should have exactly two modes", 2, roles.get(agent).size());
177        assertTrue("The agent should be able to read", roles.get(agent).contains(WEBAC_MODE_READ_VALUE));
178        assertTrue("The agent should be able to write", roles.get(agent).contains(WEBAC_MODE_WRITE_VALUE));
179    }
180
181    @Test
182    public void acl21NoDefaultACLStatementTest() throws RepositoryException {
183        final String agent = "user21";
184        final String parentPath = "/resource_acl_no_inheritance";
185        final String accessTo = parentPath + "/foo";
186        final String acl = "/acls/21/acl.ttl";
187
188        when(mockResource.getAcl()).thenReturn(null);
189        when(mockParentResource.getAcl()).thenReturn(mockAclResource);
190        when(mockAclResource.hasProperty("acl:default")).thenReturn(false);
191
192        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
193        when(mockResource.getContainer()).thenReturn(mockParentResource);
194        when(mockResource.getOriginalResource()).thenReturn(mockResource);
195        when(mockResource.getAcl()).thenReturn(mockAclResource);
196
197        when(mockParentResource.getId()).thenReturn(addPrefix(parentPath));
198        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
199        when(mockParentResource.getAcl()).thenReturn(null);
200
201
202        when(mockAclResource.getTriples())
203                .thenReturn(getRdfStreamFromResource(acl, TTL));
204
205        propsConfig.setRootAuthAclPath(Paths.get("./target/test-classes/test-root-authorization2.ttl"));
206
207        // The default root ACL should be used for authorization instead of the parent ACL
208        final String rootAgent = "user06a";
209        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
210        assertEquals("Contains no agents in the role map!", 1, roles.size());
211        assertNull("Contains agent " + agent + " from ACL in parent node!", roles.get(agent));
212        assertEquals("Should have agent " + rootAgent + " from the root ACL!", 1, roles.get(rootAgent).size());
213        assertTrue("Should have read mode for agent " + rootAgent + " from the root ACL!",
214                roles.get(rootAgent).contains(WEBAC_MODE_READ_VALUE));
215    }
216
217    @Test
218    public void acl01Test1() throws RepositoryException, PathNotFoundException {
219        final String agent = "user01";
220        final String accessTo = "/webacl_box1";
221        final String acl = "/acls/01/acl.ttl";
222
223        when(mockResource.getAcl()).thenReturn(mockAclResource);
224        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(acl)))
225                .thenReturn(mockAclResource);
226        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
227        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
228        when(mockResource.getOriginalResource()).thenReturn(mockResource);
229        when(mockAclResource.getTriples())
230            .thenReturn(getRdfStreamFromResource(acl, TTL));
231        when(mockAclResource.isAcl()).thenReturn(true);
232        when(mockAclResource.getId()).thenReturn(addPrefix(accessTo) + "/fcr:acl");
233
234
235        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
236
237        assertEquals("There should be exactly one agent in the role map", 1, roles.size());
238        assertEquals("The agent should have exactly two modes", 2, roles.get(agent).size());
239        assertTrue("The agent should be able to read", roles.get(agent).contains(WEBAC_MODE_READ_VALUE));
240        assertTrue("The agent should be able to write", roles.get(agent).contains(WEBAC_MODE_WRITE_VALUE));
241    }
242
243    @Ignore // TODO FIX THIS TEST
244    @Test
245    public void acl01Test2() throws RepositoryException, PathNotFoundException {
246        final String accessTo = "/webacl_box2";
247        final String acl = "/acls/01/acl.ttl";
248
249        when(mockResource.getAcl()).thenReturn(mockAclResource);
250        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(acl))).thenReturn(
251                mockAclResource);
252        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
253        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
254        when(mockResource.getOriginalResource()).thenReturn(mockResource);
255        when(mockAclResource.getTriples())
256            .thenReturn(getRdfStreamFromResource(acl, TTL));
257
258        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
259
260        assertOnlyDefaultAgentInRoles(roles);
261    }
262
263    @Test
264    public void acl02Test() throws RepositoryException {
265        final String agent = "Editors";
266        final String accessTo = "/box/bag/collection";
267        final String acl = "/acls/02/acl.ttl";
268
269        when(mockResource.getAcl()).thenReturn(mockAclResource);
270        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
271        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
272        when(mockAclResource.getTriples())
273            .thenReturn(getRdfStreamFromResource(acl, TTL));
274        when(mockAclResource.isAcl()).thenReturn(true);
275        when(mockAclResource.getId()).thenReturn(addPrefix(accessTo) + "/fcr:acl");
276
277        when(mockResource.getOriginalResource()).thenReturn(mockResource);
278
279
280        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
281
282        assertEquals("There should be exactly one agent in the role map", 1, roles.size());
283        assertEquals("The agent should have exactly two modes", 2, roles.get(agent).size());
284        assertTrue("The agent should be able to read", roles.get(agent).contains(WEBAC_MODE_READ_VALUE));
285        assertTrue("The agent should be able to write", roles.get(agent).contains(WEBAC_MODE_WRITE_VALUE));
286    }
287
288    @Test
289    public void acl03Test1() throws RepositoryException, PathNotFoundException {
290        final String agent = "http://xmlns.com/foaf/0.1/Agent";
291        final String accessTo = "/dark/archive/sunshine";
292        final String acl = "/acls/03/acl.ttl";
293
294        when(mockResource.getAcl()).thenReturn(mockAclResource);
295        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(acl))).thenReturn(
296                mockAclResource);
297        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
298        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
299        when(mockResource.getOriginalResource()).thenReturn(mockResource);
300        when(mockAclResource.getTriples())
301            .thenReturn(getRdfStreamFromResource(acl, TTL));
302        when(mockAclResource.isAcl()).thenReturn(true);
303        when(mockAclResource.getId()).thenReturn(addPrefix(accessTo) + "/fcr:acl");
304
305        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
306
307        assertEquals("There should be exactly one agent in the roles map", 1, roles.size());
308        assertEquals("The agent should have exactly one mode", 1, roles.get(agent).size());
309        assertTrue("The agent should be able to read", roles.get(agent).contains(WEBAC_MODE_READ_VALUE));
310    }
311
312    @Test
313    public void acl03Test2() throws RepositoryException, PathNotFoundException {
314        final String agent = "Restricted";
315        final String accessTo = "/dark/archive";
316        final String acl = "/acls/03/acl.ttl";
317
318        when(mockResource.getAcl()).thenReturn(mockAclResource);
319        when(mockAclResource.isAcl()).thenReturn(true);
320        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(acl))).thenReturn(
321                mockAclResource);
322        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
323        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
324        when(mockResource.getOriginalResource()).thenReturn(mockResource);
325        when(mockAclResource.getTriples())
326            .thenReturn(getRdfStreamFromResource(acl, TTL));
327
328        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
329
330        assertEquals("There should be exactly one agent", 1, roles.size());
331        assertEquals("The agent should have one mode", 1, roles.get(agent).size());
332        assertTrue("The agent should be able to read", roles.get(agent).contains(WEBAC_MODE_READ_VALUE));
333    }
334
335    @Test
336    public void foafAgentTest() throws RepositoryException, PathNotFoundException {
337        final String agent = "http://xmlns.com/foaf/0.1/Agent";
338        final String accessTo = "/foaf-agent";
339        final String acl = "/acls/03/foaf-agent.ttl";
340
341        when(mockResource.getAcl()).thenReturn(mockAclResource);
342        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(acl)))
343                .thenReturn(mockAclResource);
344        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
345        when(mockAclResource.isAcl()).thenReturn(true);
346        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
347        when(mockResource.getOriginalResource()).thenReturn(mockResource);
348        when(mockAclResource.getTriples())
349            .thenReturn(getRdfStreamFromResource(acl, TTL));
350
351        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
352
353        assertEquals("There should be only one valid role", 1, roles.size());
354        assertEquals("The foaf:Agent should have exactly one valid mode", 1,
355                     roles.get(agent).size());
356        assertTrue("The foaf:Agent should be able to write",
357                   roles.get(agent).contains(WEBAC_MODE_READ_VALUE));
358    }
359
360    @Test
361    public void authenticatedAgentTest() throws RepositoryException, PathNotFoundException {
362        final String aclAuthenticatedAgent = "http://www.w3.org/ns/auth/acl#AuthenticatedAgent";
363        final String accessTo = "/authenticated-agent";
364        final String acl = "/acls/03/authenticated-agent.ttl";
365
366        when(mockResource.getAcl()).thenReturn(mockAclResource);
367        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(acl))).thenReturn(
368                mockAclResource);
369        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
370        when(mockAclResource.isAcl()).thenReturn(true);
371        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
372        when(mockResource.getOriginalResource()).thenReturn(mockResource);
373        when(mockAclResource.getTriples()).thenReturn(getRdfStreamFromResource(acl, TTL));
374
375        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
376
377        assertEquals("There should be only one valid role", 1, roles.size());
378        assertEquals("The acl:AuthenticatedAgent should have exactly one valid mode", 1,
379                     roles.get(aclAuthenticatedAgent).size());
380        assertTrue("The acl:AuthenticatedAgent should be able to write",
381                   roles.get(aclAuthenticatedAgent).contains(WEBAC_MODE_READ_VALUE));
382    }
383
384    @Test
385    public void acl04Test() throws RepositoryException, PathNotFoundException {
386        final String agent1 = "http://xmlns.com/foaf/0.1/Agent";
387        final String agent2 = "Editors";
388        final String accessTo = "/public_collection";
389        final String acl = "/acls/04/acl.ttl";
390
391        when(mockResource.getAcl()).thenReturn(mockAclResource);
392        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(acl))).thenReturn(
393                mockAclResource);
394        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
395        when(mockAclResource.isAcl()).thenReturn(true);
396        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
397        when(mockResource.getOriginalResource()).thenReturn(mockResource);
398        when(mockAclResource.getTriples()).thenReturn(getRdfStreamFromResource(acl, TTL));
399
400        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
401
402        assertEquals("There should be exactly two agents", 2, roles.size());
403        assertEquals("The agent should have one mode", 1, roles.get(agent1).size());
404        assertTrue("The agent should be able to read", roles.get(agent1).contains(WEBAC_MODE_READ_VALUE));
405        assertEquals("The agent should have two modes", 2, roles.get(agent2).size());
406        assertTrue("The agent should be able to read", roles.get(agent2).contains(WEBAC_MODE_READ_VALUE));
407        assertTrue("The agent should be able to write", roles.get(agent2).contains(WEBAC_MODE_READ_VALUE));
408    }
409
410    @Test
411    public void acl05Test() throws RepositoryException, PathNotFoundException {
412        final String agent1 = "http://xmlns.com/foaf/0.1/Agent";
413        final String agent2 = "Admins";
414        final String accessTo = "/mixedCollection";
415        final String acl = "/acls/05/acl.ttl";
416
417        when(mockResource.getAcl()).thenReturn(mockAclResource);
418        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(addPrefix(acl)))).thenReturn(
419                mockAclResource
420        );
421        when(mockResource.getTypes()).thenReturn(singletonList(URI.create("http://example.com/terms#publicImage")));
422        when(mockAclResource.isAcl()).thenReturn(true);
423        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
424        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
425        when(mockResource.getOriginalResource()).thenReturn(mockResource);
426        when(mockAclResource.getTriples()).thenReturn(getRdfStreamFromResource(acl, TTL));
427
428        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
429
430        assertEquals("There should be exactly two agents", 2, roles.size());
431        assertEquals("The agent should have one mode", 1, roles.get(agent1).size());
432        assertTrue("The agent should be able to read", roles.get(agent1).contains(WEBAC_MODE_READ_VALUE));
433        assertEquals("The agent should have one mode", 1, roles.get(agent2).size());
434        assertTrue("The agent should be able to read", roles.get(agent2).contains(WEBAC_MODE_READ_VALUE));
435    }
436
437    @Test
438    public void acl05Test2() throws RepositoryException, PathNotFoundException {
439        final String agent1 = "http://xmlns.com/foaf/0.1/Agent";
440        final String accessTo = "/someOtherCollection";
441        final String acl = "/acls/05/acl.ttl";
442
443        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(addPrefix(acl))))
444                .thenReturn(mockAclResource);
445        when(mockResource.getAcl()).thenReturn(mockAclResource);
446        when(mockResource.getTypes()).thenReturn(singletonList(URI.create("http://example.com/terms#publicImage")));
447        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
448        when(mockResource.getOriginalResource()).thenReturn(mockResource);
449        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
450        when(mockAclResource.isAcl()).thenReturn(true);
451        when(mockAclResource.getTriples()).thenReturn(getRdfStreamFromResource(acl, TTL));
452
453        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
454
455        assertEquals("There should be exactly one agent", 1, roles.size());
456        assertEquals("The agent should have one mode", 1, roles.get(agent1).size());
457        assertTrue("The agent should be able to read", roles.get(agent1).contains(WEBAC_MODE_READ_VALUE));
458    }
459
460    /* (non-Javadoc)
461     * Test that an in-repository resource used as a target for acl:agentGroup has
462     * the rdf:type of vcard:Group. This test mocks a vcard:Group resource and should
463     * therefore retrieve two agents.
464     */
465    @Test
466    public void acl09Test1() throws RepositoryException, PathNotFoundException {
467        final String agent1 = "person1";
468        final String accessTo = "/anotherCollection";
469
470        final String groupResource = "/group/foo";
471        final String aclDir = "/acls/09";
472        final String acl = aclDir + "/acl.ttl";
473        final String group = aclDir + "/group.ttl";
474
475        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(addPrefix(acl))))
476                .thenReturn(mockAclResource);
477        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(addPrefix(groupResource))))
478                .thenReturn(mockAgentClassResource);
479        when(mockResource.getAcl()).thenReturn(mockAclResource);
480        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
481        when(mockResource.getOriginalResource()).thenReturn(mockResource);
482        when(mockAclResource.getTriples()).thenReturn(getRdfStreamFromResource(acl, TTL));
483        when(mockAclResource.isAcl()).thenReturn(true);
484        when(mockAclResource.getId()).thenReturn(addPrefix(accessTo) + "/fcr:acl");
485
486        when(mockAgentClassResource.getTypes()).thenReturn(singletonList(VCARD_GROUP));
487        when(mockAgentClassResource.getId()).thenReturn(addPrefix(groupResource));
488        when(mockAgentClassResource.getTriples()).thenReturn(getRdfStreamFromResource(group, TTL));
489
490
491        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
492
493        assertEquals("There should be exactly two agents", 2, roles.size());
494        assertEquals("The agent should have two modes", 2, roles.get(agent1).size());
495        assertTrue("The agent should be able to read", roles.get(agent1).contains(WEBAC_MODE_READ_VALUE));
496        assertTrue("The agent should be able to write", roles.get(agent1).contains(WEBAC_MODE_WRITE_VALUE));
497    }
498
499    /* (non-Javadoc)
500     * Test that an in-repository resource used as a target for acl:agentClass has
501     * the rdf:type of foaf:Group. This test mocks a resource that is not of the type
502     * foaf:Group and therefore should retrieve zero agents.
503     */
504    @Ignore // TODO FIX THIS TEST
505    @Test
506    public void acl09Test2() throws RepositoryException, PathNotFoundException {
507        final String accessTo = "/anotherCollection";
508
509        final String groupResource = "/group/foo";
510        final String acl = "/acls/09/acl.ttl";
511        final String group = "/acls/09/group.ttl";
512
513        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(addPrefix(acl)))).thenReturn(
514                mockAclResource);
515        when(mockResourceFactory.getResource(mockTransaction,
516                FedoraId.create(addPrefix(groupResource)))).thenReturn(mockAgentClassResource);
517        when(mockResource.getAcl()).thenReturn(mockAclResource);
518        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
519        when(mockResource.getOriginalResource()).thenReturn(mockResource);
520        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
521        when(mockAclResource.getTriples()).thenReturn(getRdfStreamFromResource(acl, TTL));
522
523        when(mockAgentClassResource.getTypes()).thenReturn(new ArrayList<>());
524        when(mockAgentClassResource.getId()).thenReturn(addPrefix(groupResource));
525        when(mockAgentClassResource.getTriples())
526            .thenReturn(getRdfStreamFromResource(group, TTL));
527
528        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
529
530        assertOnlyDefaultAgentInRoles(roles);
531    }
532
533    @Test
534    public void acl17Test1() throws RepositoryException, PathNotFoundException {
535        final String foafAgent = "http://xmlns.com/foaf/0.1/Agent";
536        final String accessTo = "/dark/archive/sunshine";
537        final String acl = "/acls/17/acl.ttl";
538
539        when(mockResource.getAcl()).thenReturn(mockAclResource);
540        when(mockResourceFactory.getResource(mockTransaction, FedoraId.create(addPrefix(acl))))
541                .thenReturn(mockAclResource);
542        when(mockAclResource.getId()).thenReturn(addPrefix(acl));
543        when(mockAclResource.isAcl()).thenReturn(true);
544        when(mockResource.getId()).thenReturn(addPrefix(accessTo));
545        when(mockResource.getOriginalResource()).thenReturn(mockResource);
546        when(mockAclResource.getTriples())
547            .thenReturn(getRdfStreamFromResource(acl, TTL));
548
549        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
550
551        assertEquals("There should be only one valid role", 1, roles.size());
552        assertEquals("The foafAgent should have exactly one valid mode", 1, roles.get(foafAgent).size());
553        assertTrue("The foafAgent should be able to write", roles.get(foafAgent).contains(WEBAC_MODE_WRITE_VALUE));
554    }
555
556    @Test
557    public void noAclTest1() {
558        final String agent1 = "http://xmlns.com/foaf/0.1/Agent";
559
560        when(mockResource.getAcl()).thenReturn(null);
561
562        when(mockResource.getId()).thenReturn(FEDORA_ID_PREFIX);
563        when(mockResource.getTypes()).thenReturn(
564                singletonList(FEDORA_RESOURCE_URI));
565        when(mockResource.getOriginalResource()).thenReturn(mockResource);
566        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
567
568        assertEquals("There should be exactly one agent", 1, roles.size());
569        assertEquals("The agent should have one mode", 1, roles.get(agent1).size());
570    }
571
572    @Test(expected = RuntimeException.class)
573    public void noAclTestMalformedRdf2() {
574
575        when(mockResource.getAcl()).thenReturn(null);
576
577        when(mockResource.getId()).thenReturn(FEDORA_ID_PREFIX);
578        when(mockResource.getTypes()).thenReturn(
579                singletonList(FEDORA_RESOURCE_URI));
580        when(mockResource.getOriginalResource()).thenReturn(mockResource);
581
582        propsConfig.setRootAuthAclPath(Paths.get("./target/test-classes/logback-test.xml"));
583        roleProvider.getRoles(mockResource, mockTransaction);
584    }
585
586    @Test
587    public void noAclTestOkRdf3() {
588        final String agent1 = "testAdminUser";
589
590        when(mockResource.getAcl()).thenReturn(null);
591        when(mockResource.getId()).thenReturn(FEDORA_ID_PREFIX);
592        when(mockResource.getTypes()).thenReturn(
593                singletonList(FEDORA_RESOURCE_URI));
594
595        propsConfig.setRootAuthAclPath(Paths.get("./target/test-classes/test-root-authorization.ttl"));
596        final Map<String, Collection<String>> roles = roleProvider.getRoles(mockResource, mockTransaction);
597
598        assertEquals("There should be exactly one agent", 1, roles.size());
599        assertEquals("The agent should have one mode", 1, roles.get(agent1).size());
600        assertTrue("The agent should be able to read", roles.get(agent1).contains(WEBAC_MODE_READ_VALUE));
601    }
602
603    private static RdfStream getRdfStreamFromResource(final String resourcePath, final Lang lang) {
604        final Model model = createDefaultModel();
605
606        RDFDataMgr.read(model, WebACRolesProviderTest.class.getResourceAsStream(resourcePath), lang);
607
608        final List<Triple> triples = new ArrayList<>();
609        model.listStatements().forEachRemaining(x -> {
610            final Triple t = x.asTriple();
611            if (t.getObject().isURI() && t.getObject().getURI().startsWith(FEDORA_URI_PREFIX)) {
612                triples.add(new Triple(t.getSubject(), t.getPredicate(),
613                        createURI(FEDORA_PREFIX + t.getObject().getURI().substring(FEDORA_URI_PREFIX.length()))));
614            } else {
615                triples.add(t);
616            }
617        });
618
619        return new DefaultRdfStream(createURI("subject"), triples.stream());
620    }
621
622    private String addPrefix(final String id) {
623        final String cleanId = id.replaceFirst("^/", "");
624        if (!cleanId.startsWith(FEDORA_ID_PREFIX)) {
625            return FEDORA_ID_PREFIX + "/" + cleanId;
626        }
627        return cleanId;
628    }
629
630}