/* * 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.api.security.user; import org.apache.jackrabbit.api.security.user.QueryBuilder.Direction; import org.apache.jackrabbit.spi.commons.iterator.Iterators; import org.apache.jackrabbit.spi.commons.iterator.Predicate; import javax.jcr.RepositoryException; import javax.jcr.Value; import java.security.Principal; import java.util.*; public class UserManagerSearchTest extends AbstractUserTest { // users private User blackWidow; private User gardenSpider; private User jumpingSpider; private User ant; private User bee; private User fly; private User jackrabbit; private User deer; private User opposum; private User kangaroo; private User elephant; private User lemur; private User gibbon; private User crocodile; private User turtle; private User lizard; private User kestrel; private User goose; private User pelican; private User dove; private User salamander; private User goldenToad; private User poisonDartFrog; private final Set<User> users = new HashSet<User>(); // groups private Group animals; private Group invertebrates; private Group arachnids; private Group insects; private Group vertebrates; private Group mammals; private Group apes; private Group reptiles; private Group birds; private Group amphibians; private final Set<Group> groups = new HashSet<Group>(); private final Set<Authorizable> authorizables = new HashSet<Authorizable>(); private final Set<Authorizable> systemDefined = new HashSet<Authorizable>(); @Override public void setUp() throws Exception { super.setUp(); Iterator<Authorizable> systemAuthorizables = userMgr.findAuthorizables("rep:principalName", null); while (systemAuthorizables.hasNext()) { Authorizable authorizable = systemAuthorizables.next(); if (authorizable.isGroup()) { groups.add((Group) authorizable); } else { users.add((User) authorizable); } systemDefined.add(authorizable); } // Create a zoo. Please excuse my ignorance in zoology ;-) animals = createGroup("animals"); invertebrates = createGroup("invertebrates"); arachnids = createGroup("arachnids"); insects = createGroup("insects"); vertebrates = createGroup("vertebrates"); mammals = createGroup("mammals"); apes = createGroup("apes"); reptiles = createGroup("reptiles"); birds = createGroup("birds"); amphibians = createGroup("amphibians"); animals.addMember(invertebrates); animals.addMember(vertebrates); invertebrates.addMember(arachnids); invertebrates.addMember(insects); vertebrates.addMember(mammals); vertebrates.addMember(reptiles); vertebrates.addMember(birds); vertebrates.addMember(amphibians); mammals.addMember(apes); blackWidow = createUser("black widow", "flies", 2, false); gardenSpider = createUser("garden spider", "flies", 2, false); jumpingSpider = createUser("jumping spider", "insects", 1, false); addMembers(arachnids, blackWidow, gardenSpider, jumpingSpider); ant = createUser("ant", "leaves", 0.5, false); bee = createUser("bee", "honey", 2.5, true); fly = createUser("fly", "dirt", 1.3, false); addMembers(insects, ant, bee, fly); jackrabbit = createUser("jackrabbit", "carrots", 2500, true); deer = createUser("deer", "leaves", 120000, true); opposum = createUser("opposum", "fruit", 1200, true); kangaroo = createUser("kangaroo", "grass", 90000, true); elephant = createUser("elephant", "leaves", 5000000, true); addMembers(mammals, jackrabbit, deer, opposum, kangaroo, elephant); lemur = createUser("lemur", "nectar", 1100, true); gibbon = createUser("gibbon", "meat", 20000, true); addMembers(apes, lemur, gibbon); crocodile = createUser("crocodile", "meat", 80000, false); turtle = createUser("turtle", "leaves", 10000, true); lizard = createUser("lizard", "leaves", 1900, false); addMembers(reptiles, crocodile, turtle, lizard); kestrel = createUser("kestrel", "mice", 2000, false); goose = createUser("goose", "snails", 13000, true); pelican = createUser("pelican", "fish", 15000, true); dove = createUser("dove", "insects", 1600, false); addMembers(birds, kestrel, goose, pelican, dove); salamander = createUser("salamander", "insects", 800, true); goldenToad = createUser("golden toad", "insects", 700, false); poisonDartFrog = createUser("poison dart frog", "insects", 40, false); addMembers(amphibians, salamander, goldenToad, poisonDartFrog); setProperty("canFly", vf.createValue(true), bee, fly, kestrel, goose, pelican, dove); setProperty("poisonous",vf.createValue(true), blackWidow, bee, poisonDartFrog ); setProperty("poisonous", vf.createValue(false), turtle, lemur); setProperty("hasWings", vf.createValue(false), blackWidow, gardenSpider, jumpingSpider, ant, jackrabbit, deer, opposum, kangaroo, elephant, lemur, gibbon, crocodile, turtle, lizard, salamander, goldenToad, poisonDartFrog); setProperty("color", vf.createValue("black"), blackWidow, gardenSpider, ant, fly, lizard, salamander); setProperty("color", vf.createValue("WHITE"), opposum, goose, pelican, dove); setProperty("color", vf.createValue("gold"), goldenToad); setProperty("numberOfLegs", vf.createValue(2), kangaroo, gibbon, kestrel, goose, dove); setProperty("numberOfLegs", vf.createValue(4), jackrabbit, deer, opposum, elephant, lemur, crocodile, turtle, lizard, salamander, goldenToad, poisonDartFrog); setProperty("numberOfLegs", vf.createValue(6), ant, bee, fly); setProperty("numberOfLegs", vf.createValue(8), blackWidow, gardenSpider, jumpingSpider); elephant.getImpersonation().grantImpersonation(jackrabbit.getPrincipal()); authorizables.addAll(users); authorizables.addAll(groups); if (!userMgr.isAutoSave()) { superuser.save(); } } @Override public void tearDown() throws Exception { for (Authorizable authorizable : authorizables) { if (!systemDefined.contains(authorizable)) { authorizable.remove(); } } authorizables.clear(); groups.clear(); users.clear(); if (!userMgr.isAutoSave()) { superuser.save(); } super.tearDown(); } public void testAny() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { /* any */ } }); assertSameElements(result, authorizables.iterator()); } public void testSelector() throws RepositoryException { List<Class<? extends Authorizable>> selectors = new ArrayList<Class<? extends Authorizable>>(); selectors.add(Authorizable.class); selectors.add(Group.class); selectors.add(User.class); for (final Class<? extends Authorizable> s : selectors) { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setSelector(s); } }); if (User.class.isAssignableFrom(s)) { assertSameElements(result, users.iterator()); } else if (Group.class.isAssignableFrom(s)) { assertSameElements(result, groups.iterator()); } else { assertSameElements(result, authorizables.iterator()); } } } public void testDirectScope() throws RepositoryException { Group[] groups = new Group[]{mammals, vertebrates, apes}; for (final Group g : groups) { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { try { builder.setScope(g.getID(), true); } catch (RepositoryException e) { fail(e.getMessage()); } } }); Iterator<Authorizable> members = g.getDeclaredMembers(); assertSameElements(result, members); } } public void testIndirectScope() throws RepositoryException { Group[] groups = new Group[]{mammals, vertebrates, apes}; for (final Group g : groups) { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { try { builder.setScope(g.getID(), false); } catch (RepositoryException e) { fail(e.getMessage()); } } }); Iterator<Authorizable> members = g.getMembers(); assertSameElements(result, members); } } public void testFindUsersInGroup() throws RepositoryException { Group[] groups = new Group[]{mammals, vertebrates, apes}; for (final Group g : groups) { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { try { builder.setSelector(User.class); builder.setScope(g.getID(), false); } catch (RepositoryException e) { fail(e.getMessage()); } } }); Iterator<Authorizable> members = g.getMembers(); Iterator<Authorizable> users = Iterators.filterIterator(members, new Predicate<Authorizable>() { public boolean evaluate(Authorizable authorizable) { return !authorizable.isGroup(); } }); assertSameElements(result, users); } } public void testFindGroupsInGroup() throws RepositoryException { Group[] groups = new Group[]{mammals, vertebrates, apes}; for (final Group g : groups) { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { try { builder.setSelector(Group.class); builder.setScope(g.getID(), true); } catch (RepositoryException e) { fail(e.getMessage()); } } }); Iterator<Authorizable> members = g.getDeclaredMembers(); Iterator<Authorizable> users = Iterators.filterIterator(members, new Predicate<Authorizable>() { public boolean evaluate(Authorizable authorizable) { return authorizable.isGroup(); } }); assertSameElements(result, users); } } public void testNameMatch() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder.nameMatches("a%")); } }); Iterator<Authorizable> expected = Iterators.filterIterator(authorizables.iterator(), new Predicate<Authorizable>() { public boolean evaluate(Authorizable authorizable) { try { String name = authorizable.getID(); Principal principal = authorizable.getPrincipal(); return name.startsWith("a") || principal != null && principal.getName().startsWith("a"); } catch (RepositoryException e) { fail(e.getMessage()); } return false; } }); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testFindProperty1() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. eq("@canFly", vf.createValue(true))); } }); Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() { public boolean evaluate(User user) { try { Value[] canFly = user.getProperty("canFly"); return canFly != null && canFly.length == 1 && canFly[0].getBoolean(); } catch (RepositoryException e) { fail(e.getMessage()); } return false; } }); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testFindProperty2() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. gt("profile/@weight", vf.createValue(2000.0))); } }); Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() { public boolean evaluate(User user) { try { Value[] weight = user.getProperty("profile/weight"); return weight != null && weight.length == 1 && weight[0].getDouble() > 2000.0; } catch (RepositoryException e) { fail(e.getMessage()); } return false; } }); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testFindProperty3() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. eq("@numberOfLegs", vf.createValue(8))); } }); Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() { public boolean evaluate(User user) { try { Value[] numberOfLegs = user.getProperty("numberOfLegs"); return numberOfLegs != null && numberOfLegs.length == 1 && numberOfLegs[0].getLong() == 8; } catch (RepositoryException e) { fail(e.getMessage()); } return false; } }); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testPropertyExistence() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. exists("@poisonous")); } }); Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() { public boolean evaluate(User user) { try { Value[] poisonous = user.getProperty("poisonous"); return poisonous != null && poisonous.length == 1; } catch (RepositoryException e) { fail(e.getMessage()); } return false; } }); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testPropertyLike() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. like("profile/@food", "m%")); } }); Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() { public boolean evaluate(User user) { try { Value[] food = user.getProperty("profile/food"); if (food == null || food.length != 1) { return false; } else { String value = food[0].getString(); return value.startsWith("m"); } } catch (RepositoryException e) { fail(e.getMessage()); } return false; } }); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testContains1() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. contains(".", "gold")); } }); Iterator<User> expected = Iterators.singleton(goldenToad); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testContains2() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. contains("@color", "gold")); } }); Iterator<User> expected = Iterators.singleton(goldenToad); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testContains3() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. contains("profile/.", "grass")); } }); Iterator<User> expected = Iterators.singleton(kangaroo); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testContains4() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. contains("profile/@food", "grass")); } }); Iterator<User> expected = Iterators.singleton(kangaroo); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testCondition1() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. and(builder. eq("profile/@cute", vf.createValue(true)), builder. not(builder. eq("@color", vf.createValue("black"))))); } }); Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() { public boolean evaluate(User user) { try { Value[] cute = user.getProperty("profile/cute"); Value[] black = user.getProperty("color"); return cute != null && cute.length == 1 && cute[0].getBoolean() && !(black != null && black.length == 1 && black[0].getString().equals("black")); } catch (RepositoryException e) { fail(e.getMessage()); } return false; } }); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testCondition2() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. or(builder. eq("profile/@food", vf.createValue("mice")), builder. eq("profile/@food", vf.createValue("nectar")))); } }); Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() { public boolean evaluate(User user) { try { Value[] food = user.getProperty("profile/food"); return food != null && food.length == 1 && (food[0].getString().equals("mice") || food[0].getString().equals("nectar")); } catch (RepositoryException e) { fail(e.getMessage()); } return false; } }); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testImpersonation() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. impersonates("jackrabbit")); } }); Iterator<User> expected = Iterators.singleton(elephant); assertTrue(result.hasNext()); assertSameElements(result, expected); } public void testSortOrder1() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. exists("@color")); builder.setSortOrder("@color", Direction.DESCENDING); } }); assertTrue(result.hasNext()); String prev = null; while (result.hasNext()) { Authorizable authorizable = result.next(); Value[] color = authorizable.getProperty("color"); assertNotNull(color); assertEquals(1, color.length); assertTrue(prev == null || prev.compareToIgnoreCase(color[0].getString()) >= 0); prev = color[0].getString(); } } public void testSortOrder2() throws RepositoryException { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. exists("profile/@weight")); builder.setSortOrder("profile/@weight", Direction.ASCENDING, true); } }); assertTrue(result.hasNext()); double prev = Double.MIN_VALUE; while (result.hasNext()) { Authorizable authorizable = result.next(); Value[] weight = authorizable.getProperty("profile/weight"); assertNotNull(weight); assertEquals(1, weight.length); assertTrue(prev <= weight[0].getDouble()); prev = weight[0].getDouble(); } } public void testOffset() throws RepositoryException { long[] offsets = {2, 0, 3, 0, 100000}; long[] counts = {4, 4, 0, 100000, 100000}; for (int k = 0; k < offsets.length; k++) { final long offset = offsets[k]; final long count = counts[k]; Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setSortOrder("profile/@weight", Direction.ASCENDING); builder.setLimit(offset, count); } }); Iterator<Authorizable> expected = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setSortOrder("profile/@weight", Direction.ASCENDING); } }); skip(expected, offset); assertSame(expected, result, count); assertFalse(result.hasNext()); } } public void testSetBound() throws RepositoryException { List<User> sortedUsers = new ArrayList<User>(users); sortedUsers.removeAll(systemDefined); // remove system defined users: no @weight Comparator<? super User> comp = new Comparator<User>() { public int compare(User user1, User user2) { try { Value[] weight1 = user1.getProperty("profile/weight"); assertNotNull(weight1); assertEquals(1, weight1.length); Value[] weight2 = user2.getProperty("profile/weight"); assertNotNull(weight2); assertEquals(1, weight2.length); return weight1[0].getDouble() < weight2[0].getDouble() ? -1 : 1; } catch (RepositoryException e) { fail(e.getMessage()); return 0; // Make the compiler happy } } }; Collections.sort(sortedUsers, comp); long[] counts = {4, 0, 100000}; for (final long count : counts) { Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setCondition(builder. eq("profile/@cute", vf.createValue(true))); builder.setSortOrder("profile/@weight", Direction.ASCENDING, true); builder.setLimit(vf.createValue(1000.0), count); } }); Iterator<User> expected = Iterators.filterIterator(sortedUsers.iterator(), new Predicate<User>() { public boolean evaluate(User user) { try { Value[] cute = user.getProperty("profile/cute"); Value[] weight = user.getProperty("profile/weight"); return cute != null && cute.length == 1 && cute[0].getBoolean() && weight != null && weight.length == 1 && weight[0].getDouble() > 1000.0; } catch (RepositoryException e) { fail(e.getMessage()); } return false; } }); assertSame(expected, result, count); assertFalse(result.hasNext()); } } public void testScopeWithOffset() throws RepositoryException { final int offset = 5; final int count = 10000; Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setScope("vertebrates", false); builder.setSortOrder("profile/@weight", Direction.ASCENDING); builder.setLimit(offset, count); } }); Iterator<Authorizable> expected = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setScope("vertebrates", false); builder.setSortOrder("profile/@weight", Direction.ASCENDING); } }); skip(expected, offset); assertSame(expected, result, count); assertFalse(result.hasNext()); } public void testScopeWithMax() throws RepositoryException { final int offset = 0; final int count = 22; Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setScope("vertebrates", false); builder.setSortOrder("profile/@weight", Direction.ASCENDING); builder.setLimit(offset, count); } }); Iterator<Authorizable> expected = userMgr.findAuthorizables(new Query() { public <T> void build(QueryBuilder<T> builder) { builder.setScope("vertebrates", false); builder.setSortOrder("profile/@weight", Direction.ASCENDING); } }); assertSameElements(expected, result); assertFalse(result.hasNext()); } //------------------------------------------< private >--- private static void addMembers(Group group, Authorizable... authorizables) throws RepositoryException { for (Authorizable authorizable : authorizables) { group.addMember(authorizable); } } private Group createGroup(String name) throws RepositoryException { Group group = userMgr.createGroup(name); groups.add(group); return group; } private User createUser(String name, String food, double weight, boolean cute) throws RepositoryException { User user = userMgr.createUser(name, ""); user.setProperty("profile/food", vf.createValue(food)); user.setProperty("profile/weight", vf.createValue(weight)); user.setProperty("profile/cute", vf.createValue(cute)); users.add(user); return user; } private static void setProperty(String relPath, Value value, Authorizable... authorizables) throws RepositoryException { for (Authorizable authorizable : authorizables) { authorizable.setProperty(relPath, value); } } private static <T> void assertSameElements(Iterator<? extends T> it1, Iterator<? extends T> it2) { Set<? extends T> set1 = toSet(it1); Set<? extends T> set2 = toSet(it2); Set<? super T> missing = new HashSet<T>(); missing.addAll(set2); missing.removeAll(set1); Set<? super T> excess = new HashSet<T>(); excess.addAll(set1); excess.removeAll(set2); if (!missing.isEmpty()) { fail("Missing elements in query result: " + missing); } if (!excess.isEmpty()) { fail("Excess elements in query result: " + excess); } } private static <T> Set<T> toSet(Iterator<T> it) { Set<T> set = new HashSet<T>(); while (it.hasNext()) { set.add(it.next()); } return set; } private static <T> void assertSame(Iterator<? extends T> expected, Iterator<? extends T> actual, long count) { for (int k = 0; k < count && actual.hasNext(); k++) { assertTrue(expected.hasNext()); assertEquals(expected.next(), actual.next()); } } private static <T> void skip(Iterator<T> iterator, long count) { for (int k = 0; k < count && iterator.hasNext(); k++) { iterator.next(); } } }