/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2009-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2015 ForgeRock AS */ package org.opends.server.core; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.ModificationType; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.SearchScope; import org.opends.server.TestCaseUtils; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.protocols.internal.InternalSearchOperation; import org.opends.server.protocols.internal.SearchRequest; import org.opends.server.protocols.ldap.LDAPAttribute; import org.opends.server.protocols.ldap.LDAPModification; import org.opends.server.tools.LDAPDelete; import org.opends.server.tools.LDAPModify; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; import org.opends.server.types.DN; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import org.opends.server.types.RawModification; import org.opends.server.types.SubEntry; import org.opends.server.types.SubtreeSpecification; import org.opends.server.util.StaticUtils; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import static org.assertj.core.api.Assertions.*; import static org.forgerock.opendj.ldap.ModificationType.*; import static org.opends.server.TestCaseUtils.*; import static org.opends.server.protocols.internal.InternalClientConnection.*; import static org.opends.server.protocols.internal.Requests.*; import static org.opends.server.util.CollectionUtils.*; import static org.opends.server.util.ServerConstants.*; import static org.testng.Assert.*; @SuppressWarnings("javadoc") public class SubentryManagerTestCase extends CoreTestCase { private static final String SUFFIX = "dc=example,dc=com"; private static final String BASE_RDN = "ou=Test SubEntry Manager"; private static final String BASE = BASE_RDN + "," + SUFFIX; private Entry testEntry; private Entry ldapSubentry; private Entry legacyLdapSubentry; private Entry collectiveSubentry; @BeforeClass public void setUp() throws Exception { TestCaseUtils.startServer(); TestCaseUtils.clearBackend("userRoot"); addTestEntries(); } @AfterClass public void cleanUp() throws Exception { TestCaseUtils.deleteEntry(collectiveSubentry.getName()); TestCaseUtils.deleteEntry(ldapSubentry.getName()); TestCaseUtils.deleteEntry(legacyLdapSubentry.getName()); SubentryManager manager = DirectoryServer.getSubentryManager(); assertNotNull(manager); List<SubEntry> subentryList = manager.getSubentries(testEntry.getName()); assertThat(subentryList).isEmpty(); subentryList = manager.getCollectiveSubentries(testEntry.getName()); assertThat(subentryList).isEmpty(); TestCaseUtils.clearBackend("userRoot"); } @BeforeMethod public void setUpQuiesceServer() { TestCaseUtils.quiesceServer(); } @Test public void testSubentryManager() throws Exception { SubentryManager manager = DirectoryServer.getSubentryManager(); assertNotNull(manager); List<SubEntry> subentryList = manager.getSubentries(testEntry.getName()); assertThat(getDns(subentryList)).containsExactly(ldapSubentry.getName(), legacyLdapSubentry.getName()); subentryList = manager.getSubentries(testEntry); assertThat(getEntries(subentryList)).containsExactly(ldapSubentry, legacyLdapSubentry); subentryList = manager.getCollectiveSubentries(testEntry.getName()); assertThat(getDns(subentryList)).containsExactly(collectiveSubentry.getName()); // Other unit tests may have modified the sub-entry causing it to contain // modify timestamps, etc, so get a fresh copy. Entry refreshedCollectiveSubentry = DirectoryServer.getEntry(collectiveSubentry.getName()); subentryList = manager.getCollectiveSubentries(testEntry); assertThat(getEntries(subentryList)).containsExactly(refreshedCollectiveSubentry); Entry refreshedLegacyLdapSubentry = DirectoryServer.getEntry(legacyLdapSubentry.getName()); subentryList = manager.getSubentries(legacyLdapSubentry.getName().parent()); assertThat(getEntries(subentryList)).containsExactly(refreshedLegacyLdapSubentry); subentryList = manager.getSubentries(legacyLdapSubentry.getName().parent().parent()); assertThat(subentryList).isEmpty(); } private List<Entry> getEntries(List<SubEntry> subentries) { final List<Entry> results = new ArrayList<>(); for (SubEntry subEntry : subentries) { results.add(subEntry.getEntry()); } return results; } private List<DN> getDns(List<SubEntry> subentries) { final List<DN> results = new ArrayList<>(); for (SubEntry subEntry : subentries) { results.add(subEntry.getDN()); } return results; } @Test public void testCollectiveAttributes() throws Exception { try { replaceAttribute(collectiveSubentry, "collectiveConflictBehavior", "real-overrides-virtual"); hasValues(testEntry.getName(), "c-l", "Savoie"); hasValues(testEntry.getName(), "preferredlanguage", "fr"); } finally { deleteAttribute(collectiveSubentry, "collectiveConflictBehavior"); } } /** * Tests that collective attributes can be applied to entries which match * subtree specification using virtual attributes. * * @throws Exception * If an unexpected problem occurs. */ @Test public void testCollectiveAttributesWorkWithVirtualAttributes() throws Exception { TestCaseUtils.initializeTestBackend(true); TestCaseUtils.addEntries( "dn: ou=people,o=test", "objectClass: top", "objectClass: organizationalUnit", "ou: people", "", "dn: cn=description collective attribute,ou=people,o=test", "objectClass: top", "objectClass: subentry", "objectClass: collectiveAttributeSubentry", "objectClass: extensibleObject", "subtreeSpecification: {base \"\", specificationFilter \"(isMemberOf=cn=collective users,ou=people,o=test)\"}", "cn: description collective attribute", "description;collective: inherited description", "", "dn: cn=collective users,ou=people,o=test", "objectClass: top", "objectClass: groupOfNames", "cn: ca users", "member: uid=collective user,ou=people,o=test", "", "dn: uid=collective user,ou=people,o=test", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: collective user", "givenName: collective", "sn: user", "cn: collective user", "userPassword: password", "", "dn: uid=normal user,ou=people,o=test", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: normal user", "givenName: normal", "sn: user", "cn: normal user", "userPassword: password" ); try { // Normal user will not inherit the collective description attribute. Entry e = DirectoryServer.getEntry(DN.valueOf("uid=normal user,ou=people,o=test")); assertNotNull(e); List<Attribute> description = e.getAttribute("description"); assertNull(description); // Collective user will inherit the collective description attribute. e = DirectoryServer.getEntry(DN.valueOf("uid=collective user,ou=people,o=test")); assertNotNull(e); description = e.getAttribute("description"); assertNotNull(description); assertEquals(description.size(), 1); Attribute attribute = description.get(0); assertEquals(attribute.size(), 1); assertFalse(attribute.hasOptions()); assertTrue(attribute.contains(ByteString.valueOfUtf8("inherited description"))); } finally { // Clear sub-entry and groups from test backend. TestCaseUtils.deleteEntry(DN.valueOf("cn=description collective attribute,ou=people,o=test")); TestCaseUtils.deleteEntry(DN.valueOf("cn=collective users,ou=people,o=test")); } } @Test public void testInheritedCollectiveAttributes() throws Exception { // Add test inherited from DN collective subentry. Entry collectiveDNInheritedSubentry = addEntry( "dn: cn=Inherited From DN Collective Subentry," + SUFFIX, "objectClass: top", "objectclass: subentry", "objectClass: inheritedCollectiveAttributeSubentry", "objectClass: inheritedFromDNCollectiveAttributeSubentry", "inheritFromDNAttribute: manager", "inheritAttribute: postalAddress", "subtreeSpecification: {base \"ou=Test SubEntry Manager\"}", "cn: Inherited From DN Collective Subentry"); // Add test inherited from RDN collective subentry. Entry collectiveRDNInheritedSubentry = addEntry( "dn: cn=Inherited From RDN Collective Subentry," + SUFFIX, "objectClass: top", "objectclass: subentry", "objectClass: inheritedCollectiveAttributeSubentry", "objectClass: inheritedFromRDNCollectiveAttributeSubentry", "inheritFromBaseRDN: " + BASE_RDN, "inheritFromRDNAttribute: title", "inheritFromRDNType: cn", "inheritAttribute: telephoneNumber", "subtreeSpecification: {base \"ou=Test SubEntry Manager\"}", "cn: Inherited From RDN Collective Subentry"); // Test Inherited Collective Attributes on test entry. hasValues(testEntry.getName(), "postaladdress", "Sub City, Collective Street, AK 47"); hasValues(testEntry.getName(), "telephonenumber", "+1 999 999 9999"); // Cleanup. TestCaseUtils.deleteEntry(collectiveRDNInheritedSubentry.getName()); TestCaseUtils.deleteEntry(collectiveDNInheritedSubentry.getName()); } @Test public void testCollectiveAttributeConflict() throws Exception { try { DN dn = testEntry.getName(); replaceAttribute(testEntry, "preferredLanguage", "ja"); // real-overrides-virtual. replaceAttribute(collectiveSubentry, "collectiveConflictBehavior", "real-overrides-virtual"); hasValues(dn, "preferredlanguage", "ja"); doesNotHaveValues(dn, "preferredlanguage", "fr"); // virtual-overrides-real. replaceAttribute(collectiveSubentry, "collectiveConflictBehavior", "virtual-overrides-real"); hasValues(dn, "preferredlanguage", "fr"); doesNotHaveValues(dn, "preferredlanguage", "ja"); // merge-real-and-virtual. replaceAttribute(collectiveSubentry, "collectiveConflictBehavior", "merge-real-and-virtual"); hasValues(dn, "preferredlanguage", "ja", "fr"); } finally { // cleanup. deleteAttribute(testEntry, "preferredLanguage"); deleteAttribute(collectiveSubentry, "collectiveConflictBehavior"); } } private void hasValues(DN dn, String attrTypeLower, String... values) throws DirectoryException { Entry entry = DirectoryServer.getEntry(dn); AttributeType attrType = DirectoryServer.getAttributeTypeOrNull(attrTypeLower); assertTrue(entry.hasAttribute(attrType)); for (String value : values) { assertTrue(entry.hasValue(attrType, null, ByteString.valueOfUtf8(value))); } } private void doesNotHaveValues(DN dn, String attrTypeLower, String... values) throws DirectoryException { Entry entry = DirectoryServer.getEntry(dn); AttributeType attrType = DirectoryServer.getAttributeTypeOrNull(attrTypeLower); assertTrue(entry.hasAttribute(attrType)); for (String value : values) { assertFalse(entry.hasValue(attrType, null, ByteString.valueOfUtf8(value))); } } private void hasNoAttribute(DN dn, String lowerName) throws Exception { Entry entry = DirectoryServer.getEntry(dn); AttributeType attrType = DirectoryServer.getAttributeTypeOrNull(lowerName); assertFalse(entry.hasAttribute(attrType)); } @Test public void testCollectiveAttributeSubentries() throws Exception { SearchRequest request = newSearchRequest(testEntry.getName(), SearchScope.BASE_OBJECT) .addAttribute("collectiveAttributeSubentries"); InternalSearchOperation searchOperation = getRootConnection().processSearch(request); assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS); assertEquals(searchOperation.getEntriesSent(), 1); AttributeType attrType = DirectoryServer.getAttributeTypeOrNull("collectiveattributesubentries"); Entry e = searchOperation.getSearchEntries().getFirst(); assertTrue(e.hasValue(attrType, null, ByteString.valueOfObject(collectiveSubentry.getName()))); } @Test public void testCollectiveExclusions() throws Exception { try { DN dn = testEntry.getName(); replaceAttribute(collectiveSubentry, "collectiveConflictBehavior", "real-overrides-virtual"); replaceAttribute(testEntry, "collectiveExclusions", "c-l"); hasNoAttribute(dn, "c-l"); hasValues(dn, "preferredlanguage", "fr"); replaceAttribute(testEntry, "collectiveExclusions", "preferredLanguage"); hasNoAttribute(dn, "preferredlanguage"); hasValues(dn, "c-l", "Savoie"); replaceAttribute(testEntry, "collectiveExclusions", "excludeAllCollectiveAttributes"); hasNoAttribute(dn, "preferredlanguage"); hasNoAttribute(dn, "c-l"); } finally { // cleanup. deleteAttribute(testEntry, "collectiveExclusions"); deleteAttribute(collectiveSubentry, "collectiveConflictBehavior"); } } private void deleteAttribute(Entry e, String attrType) { InternalClientConnection conn = getRootConnection(); List<RawModification> mods = newRawModifications(DELETE, attrType); ModifyOperation modifyOperation = conn.processModify(ByteString.valueOfUtf8(e.getName().toString()), mods); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); } private void replaceAttribute(Entry e, String attrType, String newValue) { InternalClientConnection conn = getRootConnection(); List<RawModification> mods = newRawModifications(REPLACE, attrType, newValue); ModifyOperation modifyOperation = conn.processModify(ByteString.valueOfUtf8(e.getName().toString()), mods); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); } private List<RawModification> newRawModifications(ModificationType modType, String attrType, String... values) { LDAPAttribute attr = new LDAPAttribute(attrType, Arrays.asList(values)); return newArrayList((RawModification) new LDAPModification(modType, attr)); } @Test public void testSubtreeDelete() throws Exception { String[] args = { "-h", "127.0.0.1", "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), "-D", "cn=Directory Manager", "-w", "password", "-J", OID_SUBTREE_DELETE_CONTROL + ":true", "--noPropertiesFile", SUFFIX }; assertEquals(LDAPDelete.mainDelete(args, false, null, System.err), 0); assertTrue(DirectoryServer.getSubentryManager().getCollectiveSubentries( DN.valueOf("uid=rogasawara," + BASE)).isEmpty()); assertTrue(DirectoryServer.getSubentryManager().getSubentries( DN.valueOf("uid=rogasawara," + BASE)).isEmpty()); // Re-add entries. addTestEntries(); } @Test public void testSubtreeModify() throws Exception { String OLDBASE = "ou=Test SubEntry Manager"; String NEWBASE = "ou=New SubEntry Manager Base"; String newPath = TestCaseUtils.createTempFile( "dn: " + BASE, "changetype: moddn", "newRDN: " + NEWBASE, "deleteOldRDN: 1"); String[] newArgs = { "-h", "127.0.0.1", "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), "-D", "cn=Directory Manager", "-w", "password", "--noPropertiesFile", "-f", newPath }; assertEquals(LDAPModify.mainModify(newArgs, false, null, System.err), 0); assertNotNull(DirectoryServer.getEntry(DN.valueOf( "uid=rogasawara," + NEWBASE + "," + SUFFIX))); assertTrue(DirectoryServer.getSubentryManager().getCollectiveSubentries( DN.valueOf("uid=rogasawara," + NEWBASE + "," + SUFFIX)).isEmpty()); // The legacyLdapSubentry should still apply. assertEquals(DirectoryServer.getSubentryManager().getSubentries( DN.valueOf("uid=rogasawara," + NEWBASE + "," + SUFFIX)).size(), 1); // Move it back. String oldPath = TestCaseUtils.createTempFile( "dn: " + NEWBASE + "," + SUFFIX, "changetype: moddn", "newRDN: " + OLDBASE, "deleteOldRDN: 1"); String[] oldArgs = { "-h", "127.0.0.1", "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), "-D", "cn=Directory Manager", "-w", "password", "--noPropertiesFile", "-f", oldPath }; assertEquals(LDAPModify.mainModify(oldArgs, false, null, System.err), 0); assertNotNull(DirectoryServer.getEntry(DN.valueOf( "uid=rogasawara," + OLDBASE + "," + SUFFIX))); assertFalse(DirectoryServer.getSubentryManager().getCollectiveSubentries( DN.valueOf("uid=rogasawara," + OLDBASE + "," + SUFFIX)).isEmpty()); assertFalse(DirectoryServer.getSubentryManager().getSubentries( DN.valueOf("uid=rogasawara," + OLDBASE + "," + SUFFIX)).isEmpty()); } @Test public void testExtendedSubtreeSpecification() throws Exception { // This one should have been added during test setup so just // do a quick check here to make sure it is available there. assertNotNull(DirectoryServer.getEntry(ldapSubentry.getName())); // RFC3672 Spec test subentry. List<SubEntry> rfc3672SubList = DirectoryServer.getSubentryManager().getSubentries(); for (SubEntry subentry : rfc3672SubList) { if (subentry.getDN().equals(ldapSubentry.getName())) { SubtreeSpecification spec = subentry.getSubTreeSpecification(); assertNull(spec.getRefinements()); } } // Add Relative Spec test subentry. Entry relativeSubentry = addEntry( "dn: cn=Relative Subentry," + SUFFIX, "objectClass: top", "objectclass: subentry", "subtreeSpecification: {base \"ou=Test SubEntry Manager\", specificationFilter \"(objectClass=*)\"}", "cn: Subentry"); List<SubEntry> relativeSubList = DirectoryServer.getSubentryManager().getSubentries(); for (SubEntry subentry : relativeSubList) { if (subentry.getDN().equals(relativeSubentry.getName())) { SubtreeSpecification spec = subentry.getSubTreeSpecification(); assertTrue(spec.getRefinements() instanceof SubtreeSpecification.FilterRefinement); } } // Remove Relative Spec test subentry. TestCaseUtils.deleteEntry(relativeSubentry.getName()); } private void addTestEntries() throws Exception { // Add suffix entry. DN suffixDN = DN.valueOf(SUFFIX); if (DirectoryServer.getEntry(suffixDN) == null) { addEntry(StaticUtils.createEntry(suffixDN)); } // Add base entry. DN baseDN = DN.valueOf(BASE); if (DirectoryServer.getEntry(baseDN) == null) { addEntry(StaticUtils.createEntry(baseDN)); } // Add role entry. addEntry( "dn: cn=Sales," + BASE, "objectclass: top", "objectclass: organizationalRole", "postalAddress: Sub City, Collective Street, AK 47", "telephoneNumber: +1 999 999 9999", "cn: Sales" ); // Add test entry. testEntry = addEntry( "dn: uid=rogasawara," + BASE, "objectclass: top", "objectclass: person", "objectclass: organizationalPerson", "objectclass: inetOrgPerson", "uid: rogasawara", "userpassword: password", "mail: rogasawara@example.com", "givenname: Rodney", "sn: Ogasawara", "cn: Rodney Ogasawara", "manager: cn=Sales," + BASE, "title: Sales" ); // Add test subentry. ldapSubentry = addEntry( "dn: cn=Subentry," + SUFFIX, "objectClass: top", "objectclass: subentry", "subtreeSpecification: {base \"ou=Test SubEntry Manager\"}", "cn: Subentry"); // Add test legacy subentry. legacyLdapSubentry = addEntry( "dn: cn=Legacy Subentry," + SUFFIX, "objectClass: top", "objectclass: ldapSubentry", "cn: Legacy Subentry"); // Add test collective subentry. collectiveSubentry = addEntry( "dn: cn=Collective Subentry," + SUFFIX, "objectClass: top", "objectclass: subentry", "objectClass: collectiveAttributeSubentry", "objectClass: extensibleObject", "c-l: Savoie", "preferredLanguage;collective: fr", "subtreeSpecification: {base \"ou=Test SubEntry Manager\"}", "cn: Collective Subentry"); } }