/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jackrabbit.core.security.authorization; import org.apache.jackrabbit.spi.NameFactory; import org.apache.jackrabbit.spi.PrivilegeDefinition; import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl; import org.apache.jackrabbit.spi.commons.privilege.PrivilegeDefinitionImpl; import org.apache.jackrabbit.spi.commons.privilege.PrivilegeDefinitionWriter; import org.apache.jackrabbit.core.RepositoryImpl; import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.core.fs.FileSystem; import org.apache.jackrabbit.core.fs.FileSystemException; import org.apache.jackrabbit.core.fs.FileSystemResource; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException; import org.apache.jackrabbit.spi.commons.conversion.NameResolver; import org.apache.jackrabbit.spi.commons.name.NameConstants; import org.apache.jackrabbit.test.AbstractJCRTest; import javax.jcr.RepositoryException; import javax.jcr.security.Privilege; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * <code>CustomPrivilegeTest</code>... */ public class CustomPrivilegeTest extends AbstractJCRTest { private NameResolver resolver; private FileSystem fs; private PrivilegeRegistry privilegeRegistry; @Override protected void setUp() throws Exception { super.setUp(); resolver = ((SessionImpl) superuser); // setup the custom privilege file with cyclic references fs = ((RepositoryImpl) superuser.getRepository()).getConfig().getFileSystem(); FileSystemResource resource = new FileSystemResource(fs, "/privileges/custom_privileges.xml"); if (!resource.exists()) { resource.makeParentDirs(); } privilegeRegistry = new PrivilegeRegistry(superuser.getWorkspace().getNamespaceRegistry(), fs); } @Override protected void tearDown() throws Exception { try { if (fs.exists("/privileges") && fs.isFolder("/privileges")) { fs.deleteFolder("/privileges"); } } finally { super.tearDown(); } } private static void assertPrivilege(PrivilegeRegistry registry, NameResolver resolver, PrivilegeDefinition def) throws RepositoryException { PrivilegeManagerImpl pmgr = new PrivilegeManagerImpl(registry, resolver); Privilege p = pmgr.getPrivilege(resolver.getJCRName(def.getName())); assertNotNull(p); assertEquals(def.isAbstract(), p.isAbstract()); Set<Name> danames = def.getDeclaredAggregateNames(); assertEquals(danames.size() > 0, p.isAggregate()); assertEquals(danames.size(), p.getDeclaredAggregatePrivileges().length); } private static Set<Name> createNameSet(Name... names) { Set<Name> set = new HashSet<Name>(); set.addAll(Arrays.asList(names)); return set; } public void testInvalidCustomDefinitions() throws RepositoryException, FileSystemException, IOException { // setup the custom privilege file with cyclic references FileSystem fs = ((RepositoryImpl) superuser.getRepository()).getConfig().getFileSystem(); FileSystemResource resource = new FileSystemResource(fs, "/privileges/custom_privileges.xml"); if (!resource.exists()) { resource.makeParentDirs(); } StringBuilder sb = new StringBuilder(); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><privileges><privilege isAbstract=\"false\" name=\"test\"><contains name=\"test2\"/></privilege></privileges>"); Writer writer = new OutputStreamWriter(resource.getOutputStream(), "utf-8"); writer.write(sb.toString()); writer.flush(); writer.close(); try { new PrivilegeRegistry(superuser.getWorkspace().getNamespaceRegistry(), fs); fail("Invalid names must be detected upon registry startup."); } catch (RepositoryException e) { // success } finally { fs.deleteFolder("/privileges"); } } public void testCustomDefinitionsWithCyclicReferences() throws RepositoryException, FileSystemException, IOException { // setup the custom privilege file with cyclic references FileSystem fs = ((RepositoryImpl) superuser.getRepository()).getConfig().getFileSystem(); FileSystemResource resource = new FileSystemResource(fs, "/privileges/custom_privileges.xml"); if (!resource.exists()) { resource.makeParentDirs(); } NameFactory nf = NameFactoryImpl.getInstance(); Name test = nf.create(Name.NS_DEFAULT_URI, "test"); Name test2 = nf.create(Name.NS_DEFAULT_URI, "test2"); Name test3 = nf.create(Name.NS_DEFAULT_URI, "test3"); Name test4 = nf.create(Name.NS_DEFAULT_URI, "test4"); Name test5 = nf.create(Name.NS_DEFAULT_URI, "test5"); OutputStream out = resource.getOutputStream(); try { List<PrivilegeDefinition> defs = new ArrayList<PrivilegeDefinition>(); defs.add(new PrivilegeDefinitionImpl(test, false, Collections.singleton(test2))); defs.add(new PrivilegeDefinitionImpl(test4, true, Collections.singleton(test5))); defs.add(new PrivilegeDefinitionImpl(test5, false, Collections.singleton(test3))); defs.add(new PrivilegeDefinitionImpl(test3, false, Collections.singleton(test))); defs.add(new PrivilegeDefinitionImpl(test2, false, Collections.singleton(test4))); PrivilegeDefinitionWriter pdw = new PrivilegeDefinitionWriter("text/xml"); pdw.writeDefinitions(out, defs.toArray(new PrivilegeDefinition[defs.size()]), Collections.<String, String>emptyMap()); new PrivilegeRegistry(superuser.getWorkspace().getNamespaceRegistry(), fs); fail("Cyclic definitions must be detected upon registry startup."); } catch (RepositoryException e) { // success } finally { out.close(); fs.deleteFolder("/privileges"); } } public void testCustomEquivalentDefinitions() throws RepositoryException, FileSystemException, IOException { // setup the custom privilege file with cyclic references FileSystem fs = ((RepositoryImpl) superuser.getRepository()).getConfig().getFileSystem(); FileSystemResource resource = new FileSystemResource(fs, "/privileges/custom_privileges.xml"); if (!resource.exists()) { resource.makeParentDirs(); } NameFactory nf = NameFactoryImpl.getInstance(); Name test = nf.create(Name.NS_DEFAULT_URI, "test"); Name test2 = nf.create(Name.NS_DEFAULT_URI, "test2"); Name test3 = nf.create(Name.NS_DEFAULT_URI, "test3"); Name test4 = nf.create(Name.NS_DEFAULT_URI, "test4"); Name test5 = nf.create(Name.NS_DEFAULT_URI, "test5"); Name test6 = nf.create(Name.NS_DEFAULT_URI, "test6"); OutputStream out = resource.getOutputStream(); try { List<PrivilegeDefinition> defs = new ArrayList<PrivilegeDefinition>(); defs.add(new PrivilegeDefinitionImpl(test, false, createNameSet(test2, test3))); defs.add(new PrivilegeDefinitionImpl(test2, true, Collections.singleton(test4))); defs.add(new PrivilegeDefinitionImpl(test3, true, Collections.singleton(test5))); defs.add(new PrivilegeDefinitionImpl(test4, true, Collections.<Name>emptySet())); defs.add(new PrivilegeDefinitionImpl(test5, true, Collections.<Name>emptySet())); // the equivalent definition to 'test' defs.add(new PrivilegeDefinitionImpl(test6, false, createNameSet(test2, test5))); PrivilegeDefinitionWriter pdw = new PrivilegeDefinitionWriter("text/xml"); pdw.writeDefinitions(out, defs.toArray(new PrivilegeDefinition[defs.size()]), Collections.<String, String>emptyMap()); new PrivilegeRegistry(superuser.getWorkspace().getNamespaceRegistry(), fs); fail("Equivalent definitions must be detected upon registry startup."); } catch (RepositoryException e) { // success } finally { out.close(); fs.deleteFolder("/privileges"); } } public void testRegisterBuiltInPrivilege() throws RepositoryException, IllegalNameException, FileSystemException { Map<Name, Set<Name>> builtIns = new HashMap<Name, Set<Name>>(); builtIns.put(NameConstants.JCR_READ, Collections.<Name>emptySet()); builtIns.put(NameConstants.JCR_LIFECYCLE_MANAGEMENT, Collections.singleton(NameConstants.JCR_ADD_CHILD_NODES)); builtIns.put(PrivilegeRegistry.REP_WRITE_NAME, Collections.<Name>emptySet()); builtIns.put(NameConstants.JCR_ALL, Collections.<Name>emptySet()); for (Name builtInName : builtIns.keySet()) { try { privilegeRegistry.registerDefinition(builtInName, false, builtIns.get(builtInName)); fail("Privilege name already in use -> Exception expected"); } catch (RepositoryException e) { // success } } } public void testRegisterInvalidNewAggregate() throws RepositoryException, IllegalNameException, FileSystemException { Map<Name, Set<Name>> newAggregates = new HashMap<Name, Set<Name>>(); // same as jcr:read newAggregates.put(resolver.getQName("jcr:newAggregate"), Collections.singleton(NameConstants.JCR_READ)); // aggregated combining built-in and an unknown privilege newAggregates.put(resolver.getQName("jcr:newAggregate"), createNameSet(NameConstants.JCR_READ, resolver.getQName("unknownPrivilege"))); // aggregate containing unknown privilege newAggregates.put(resolver.getQName("newAggregate"), createNameSet(resolver.getQName("unknownPrivilege"))); // custom aggregated contains itself newAggregates.put(resolver.getQName("newAggregate"), createNameSet(resolver.getQName("newAggregate"))); // same as rep:write newAggregates.put(resolver.getQName("repWriteAggregate"), createNameSet(NameConstants.JCR_MODIFY_PROPERTIES, NameConstants.JCR_ADD_CHILD_NODES, NameConstants.JCR_NODE_TYPE_MANAGEMENT, NameConstants.JCR_REMOVE_CHILD_NODES,NameConstants.JCR_REMOVE_NODE)); // aggregated combining built-in and unknown custom newAggregates.put(resolver.getQName("newAggregate"), createNameSet(NameConstants.JCR_READ, resolver.getQName("unknownPrivilege"))); for (Name name : newAggregates.keySet()) { try { privilegeRegistry.registerDefinition(name, true, newAggregates.get(name)); fail("New aggregate referring to unknown Privilege -> Exception expected"); } catch (RepositoryException e) { // success } } } public void testRegisterInvalidNewAggregate2() throws RepositoryException, FileSystemException { Map<Name, Set<Name>> newCustomPrivs = new LinkedHashMap<Name, Set<Name>>(); newCustomPrivs.put(resolver.getQName("new"), Collections.<Name>emptySet()); newCustomPrivs.put(resolver.getQName("new2"), Collections.<Name>emptySet()); Set<Name> decl = new HashSet<Name>(); decl.add(resolver.getQName("new")); decl.add(resolver.getQName("new2")); newCustomPrivs.put(resolver.getQName("new3"), decl); for (Name name : newCustomPrivs.keySet()) { boolean isAbstract = true; Set<Name> aggrNames = newCustomPrivs.get(name); privilegeRegistry.registerDefinition(name, isAbstract, aggrNames); } Map<Name, Set<Name>> newAggregates = new HashMap<Name, Set<Name>>(); // other illegal aggregates already represented by registered definition. newAggregates.put(resolver.getQName("newA2"), Collections.<Name>singleton(resolver.getQName("new"))); newAggregates.put(resolver.getQName("newA3"), Collections.<Name>singleton(resolver.getQName("new2"))); for (Name name : newAggregates.keySet()) { boolean isAbstract = false; Set<Name> aggrNames = newAggregates.get(name); try { privilegeRegistry.registerDefinition(name, isAbstract, aggrNames); fail("Invalid aggregation in definition '"+ name.toString()+"' : Exception expected"); } catch (RepositoryException e) { // success } } } public void testRegisterCustomPrivileges() throws RepositoryException, FileSystemException { Map<Name, Set<Name>> newCustomPrivs = new HashMap<Name, Set<Name>>(); newCustomPrivs.put(resolver.getQName("new"), Collections.<Name>emptySet()); newCustomPrivs.put(resolver.getQName("test:new"), Collections.<Name>emptySet()); for (Name name : newCustomPrivs.keySet()) { boolean isAbstract = true; Set<Name> aggrNames = newCustomPrivs.get(name); privilegeRegistry.registerDefinition(name, isAbstract, aggrNames); // validate definition PrivilegeDefinition definition = privilegeRegistry.get(name); assertNotNull(definition); assertEquals(name, definition.getName()); assertTrue(definition.isAbstract()); assertTrue(definition.getDeclaredAggregateNames().isEmpty()); assertEquals(aggrNames.size(), definition.getDeclaredAggregateNames().size()); for (Name n : aggrNames) { assertTrue(definition.getDeclaredAggregateNames().contains(n)); } Set<Name> allAgg = privilegeRegistry.get(NameConstants.JCR_ALL).getDeclaredAggregateNames(); assertTrue(allAgg.contains(name)); // re-read the filesystem resource and check if definition is correct PrivilegeRegistry registry = new PrivilegeRegistry(superuser.getWorkspace().getNamespaceRegistry(), fs); PrivilegeDefinition def = registry.get(name); assertEquals(isAbstract, def.isAbstract()); assertEquals(aggrNames.size(), def.getDeclaredAggregateNames().size()); for (Name n : aggrNames) { assertTrue(def.getDeclaredAggregateNames().contains(n)); } assertPrivilege(privilegeRegistry, (SessionImpl) superuser, definition); } Map<Name, Set<Name>> newAggregates = new HashMap<Name, Set<Name>>(); // a new aggregate of custom privileges newAggregates.put(resolver.getQName("newA2"), createNameSet(resolver.getQName("test:new"), resolver.getQName("new"))); // a new aggregate of custom and built-in privilege newAggregates.put(resolver.getQName("newA1"), createNameSet(resolver.getQName("new"), NameConstants.JCR_READ)); // aggregating built-in privileges newAggregates.put(resolver.getQName("aggrBuiltIn"), createNameSet(NameConstants.JCR_MODIFY_PROPERTIES, NameConstants.JCR_READ)); for (Name name : newAggregates.keySet()) { boolean isAbstract = false; Set<Name> aggrNames = newAggregates.get(name); privilegeRegistry.registerDefinition(name, isAbstract, aggrNames); PrivilegeDefinition definition = privilegeRegistry.get(name); assertNotNull(definition); assertEquals(name, definition.getName()); assertFalse(definition.isAbstract()); assertFalse(definition.getDeclaredAggregateNames().isEmpty()); assertEquals(aggrNames.size(), definition.getDeclaredAggregateNames().size()); for (Name n : aggrNames) { assertTrue(definition.getDeclaredAggregateNames().contains(n)); } Set<Name> allAgg = privilegeRegistry.get(NameConstants.JCR_ALL).getDeclaredAggregateNames(); assertTrue(allAgg.contains(name)); // re-read the filesystem resource and check if definition is correct PrivilegeRegistry registry = new PrivilegeRegistry(superuser.getWorkspace().getNamespaceRegistry(), fs); PrivilegeDefinition def = registry.get(name); assertEquals(isAbstract, def.isAbstract()); assertEquals(isAbstract, def.isAbstract()); assertEquals(aggrNames.size(), def.getDeclaredAggregateNames().size()); for (Name n : aggrNames) { assertTrue(def.getDeclaredAggregateNames().contains(n)); } assertPrivilege(registry, (SessionImpl) superuser, def); } } public void testCustomPrivilege() throws RepositoryException, FileSystemException { boolean isAbstract = false; Name name = ((SessionImpl) superuser).getQName("test"); privilegeRegistry.registerDefinition(name, isAbstract, Collections.<Name>emptySet()); PrivilegeManagerImpl pm = new PrivilegeManagerImpl(privilegeRegistry, resolver); String privName = resolver.getJCRName(name); Privilege priv = pm.getPrivilege(privName); assertEquals(privName, priv.getName()); assertEquals(isAbstract, priv.isAbstract()); assertFalse(priv.isAggregate()); assertFalse(pm.getBits(priv).isEmpty()); Privilege jcrWrite = pm.getPrivilege(Privilege.JCR_WRITE); assertFalse(pm.getBits(jcrWrite).equals(pm.getBits(priv, jcrWrite))); } public void testRegister100CustomPrivileges() throws RepositoryException, FileSystemException { PrivilegeBits previous = privilegeRegistry.getBits(privilegeRegistry.get(PrivilegeRegistry.REP_PRIVILEGE_MANAGEMENT_NAME)).unmodifiable(); for (int i = 0; i < 100; i++) { boolean isAbstract = true; Name name = ((SessionImpl) superuser).getQName("test"+i); privilegeRegistry.registerDefinition(name, isAbstract, Collections.<Name>emptySet()); PrivilegeDefinition definition = privilegeRegistry.get(name); assertNotNull(definition); assertEquals(name, definition.getName()); PrivilegeBits modifiable = privilegeRegistry.getBits(definition); PrivilegeBits bits = modifiable.unmodifiable(); assertNotNull(bits); assertFalse(bits.isEmpty()); assertEquals(modifiable, bits); assertFalse(previous.equals(bits)); assertEquals(previous.nextBits(), bits); PrivilegeDefinition all = privilegeRegistry.get(NameConstants.JCR_ALL); assertTrue(all.getDeclaredAggregateNames().contains(name)); assertTrue(privilegeRegistry.getBits(all).includes(bits)); previous = bits; } } }