/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file * except in compliance with the License. * * You can obtain a copy of the License at * http://IdentityConnectors.dev.java.net/legal/license.txt * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file * and include the License file at identityconnectors/legal/license.txt. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== */ package org.identityconnectors.ldap.sync.sunds; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import static java.util.Collections.emptyList; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import javax.naming.NamingException; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.api.operations.SyncApiOp; import org.identityconnectors.framework.common.objects.AttributeBuilder; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.Name; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.ObjectClassInfo; import org.identityconnectors.framework.common.objects.OperationOptions; import org.identityconnectors.framework.common.objects.OperationOptionsBuilder; import org.identityconnectors.framework.common.objects.Schema; import org.identityconnectors.framework.common.objects.SyncDelta; import org.identityconnectors.framework.common.objects.SyncDeltaType; import org.identityconnectors.framework.common.objects.SyncResultsHandler; import org.identityconnectors.framework.common.objects.SyncToken; import org.identityconnectors.framework.common.objects.Uid; import org.identityconnectors.ldap.LdapConfiguration; import org.identityconnectors.ldap.LdapConnection; import org.identityconnectors.ldap.SunDSTestBase; import org.identityconnectors.ldap.LdapConnection.ServerType; public class SunDSChangeLogSyncStrategyTests extends SunDSTestBase { private static final Log log = Log.getLog(SunDSChangeLogSyncStrategyTests.class); private static final int STABLE_CHANGELOG_INTERVAL = 2000; /* milliseconds */ private static LdapConnection newConnection(LdapConfiguration config) throws NamingException { LdapConnection conn = new LdapConnection(config); cleanupBaseContext(conn); waitForChangeLogToStabilize(conn); return conn; } private static void waitForChangeLogToStabilize(LdapConnection conn) { int lastChangeNumber = -1; int previousLastChangeNumber; do { if (lastChangeNumber > 0) { log.ok("Waiting for change log to stabilize (last change number: {0})", lastChangeNumber); try { Thread.sleep(STABLE_CHANGELOG_INTERVAL); } catch (InterruptedException e) { // Ignore. } } previousLastChangeNumber = lastChangeNumber; lastChangeNumber = new SunDSChangeLogSyncStrategy(conn, ObjectClass.ACCOUNT).getChangeLogAttributes().getLastChangeNumber(); } while (lastChangeNumber != previousLastChangeNumber); } private List<SyncDelta> doTest(LdapConnection conn, String ldif, int expected) throws NamingException { SunDSChangeLogSyncStrategy sync = new SunDSChangeLogSyncStrategy(conn, ObjectClass.ACCOUNT); SyncToken token = sync.getLatestSyncToken(); LdapModifyForTests.modify(conn, ldif); waitForChangeLogToStabilize(conn); OperationOptionsBuilder builder = new OperationOptionsBuilder(); builder.setAttributesToGet("cn", "sn", "givenName", "uid"); OperationOptions options = builder.build(); final List<SyncDelta> result = new ArrayList<SyncDelta>(); sync.sync(token, new SyncResultsHandler() { public boolean handle(SyncDelta delta) { result.add(delta); return true; } }, options); return result; } @Test public void testSimple() throws Exception { LdapConnection conn = newConnection(newConfiguration()); String baseContext = conn.getConfiguration().getBaseContexts()[0]; String entryDN = "uid=foobar," + baseContext; List<SyncDelta> result = doTest(conn, "dn: " +entryDN + "\n" + "changetype: add\n" + "objectClass: inetOrgPerson\n" + "objectClass: organizationalPerson\n" + "objectClass: person\n" + "objectClass: top\n" + "uid: foobar\n" + "cn: Foo Bar\n" + "sn: Bar\n", 1); assertEquals(1, result.size()); SyncDelta delta = result.get(0); assertEquals(SyncDeltaType.CREATE_OR_UPDATE, delta.getDeltaType()); ConnectorObject object = delta.getObject(); assertEquals(new Uid(entryDN), object.getUid()); assertEquals(new Name(entryDN), object.getName()); assertEquals(AttributeBuilder.build("uid", "foobar"), object.getAttributeByName("uid")); assertEquals(AttributeBuilder.build("cn", "Foo Bar"), object.getAttributeByName("cn")); assertEquals(AttributeBuilder.build("sn", "Bar"), object.getAttributeByName("sn")); result = doTest(conn, "dn: " + entryDN + "\n" + "changeType: modrdn\n" + "newRdn: cn=Foo Bar", 1); entryDN = "cn=Foo Bar," + baseContext; assertEquals(1, result.size()); delta = result.get(0); assertEquals(SyncDeltaType.CREATE_OR_UPDATE, delta.getDeltaType()); object = delta.getObject(); assertEquals(new Uid(entryDN), object.getUid()); assertEquals(new Name(entryDN), object.getName()); assertEquals(AttributeBuilder.build("uid", emptyList()), object.getAttributeByName("uid")); assertEquals(AttributeBuilder.build("cn", "Foo Bar"), object.getAttributeByName("cn")); assertEquals(AttributeBuilder.build("sn", "Bar"), object.getAttributeByName("sn")); result = doTest(conn, "dn: " + entryDN + "\n" + "changeType: modify\n" + "add: cn\n" + "cn: Dummy User", 1); assertEquals(1, result.size()); delta = result.get(0); assertEquals(SyncDeltaType.CREATE_OR_UPDATE, delta.getDeltaType()); object = delta.getObject(); assertEquals(AttributeBuilder.build("cn", "Foo Bar", "Dummy User"), object.getAttributeByName("cn")); result = doTest(conn, "dn: " + entryDN + "\n" + "changeType: modrdn\n" + "newRdn: cn=Dummy User\n" + "deleteOldRdn: FALSE", 1); entryDN = "cn=Dummy User," + baseContext; assertEquals(1, result.size()); delta = result.get(0); assertEquals(SyncDeltaType.CREATE_OR_UPDATE, delta.getDeltaType()); object = delta.getObject(); assertEquals(new Uid(entryDN), object.getUid()); assertEquals(new Name(entryDN), object.getName()); assertEquals(AttributeBuilder.build("cn", "Foo Bar", "Dummy User"), object.getAttributeByName("cn")); result = doTest(conn, "dn: " + entryDN + "\n" + "changeType: modify\n" + "delete: cn\n" + "cn: Foo Bar", 1); assertEquals(1, result.size()); delta = result.get(0); assertEquals(SyncDeltaType.CREATE_OR_UPDATE, delta.getDeltaType()); object = delta.getObject(); assertEquals(AttributeBuilder.build("cn", "Dummy User"), object.getAttributeByName("cn")); result = doTest(conn, "dn: " + entryDN + "\n" + "changeType: delete", 1); assertEquals(1, result.size()); delta = result.get(0); assertEquals(SyncDeltaType.DELETE, delta.getDeltaType()); assertEquals(new Uid(entryDN), delta.getUid()); } @Test public void testAllBlocksReturnedFromSingleSyncCall() throws NamingException { LdapConfiguration config = newConfiguration(); // Set a small block size so connector would have to do // a couple of searches to return all deltas. config.setChangeLogBlockSize(2); LdapConnection conn = newConnection(config); String baseContext = conn.getConfiguration().getBaseContexts()[0]; int COUNT = 10; StringBuilder ldif = new StringBuilder(); for (int i = 0; i < COUNT; i++) { String name = "user." + i; String entryDN = "uid=" + name + "," + baseContext; ldif.append(MessageFormat.format( "dn: {0}\n" + "changetype: add\n" + "objectClass: inetOrgPerson\n" + "objectClass: organizationalPerson\n" + "objectClass: person\n" + "objectClass: top\n" + "uid: {1}\n" + "cn: {1}\n" + "sn: {1}\n" + "\n", entryDN, name)); } List<SyncDelta> result = doTest(conn, ldif.toString(), COUNT); assertEquals(10, result.size()); for (int i = 0; i < COUNT; i++) { String name = "user." + i; String entryDN = "uid=" + name + "," + baseContext; ConnectorObject object = result.get(i).getObject(); assertEquals(new Uid(entryDN), object.getUid()); assertEquals(new Name(entryDN), object.getName()); } } @Test public void testFilterOutByBaseContexts() throws NamingException { LdapConfiguration config = newConfiguration(); String baseContext = config.getBaseContexts()[0]; config.setBaseContextsToSynchronize("ou=Subcontext," + baseContext); LdapConnection conn = newConnection(config); testExpectingNoDelta(conn); } @Test public void testFilterOutByModifiersNames() throws NamingException { LdapConfiguration config = newConfiguration(); config.setModifiersNamesToFilterOut("cn=Directory Manager"); LdapConnection conn = newConnection(config); testExpectingNoDelta(conn); } @Test public void testFilterOutByAttributes() throws NamingException { LdapConfiguration config = newConfiguration(); config.setAttributesToSynchronize("telephoneNumber"); LdapConnection conn = newConnection(config); testExpectingNoDelta(conn); } @Test public void testFilterOutByObjectClasses() throws NamingException { LdapConfiguration config = newConfiguration(); config.setObjectClassesToSynchronize("groupOfUniqueNames"); LdapConnection conn = newConnection(config); testExpectingNoDelta(conn); } @Test public void testAccountSynchronizationFilter() throws NamingException { LdapConfiguration config = newConfiguration(); config.setAccountSynchronizationFilter("cn=value"); LdapConnection conn = newConnection(config); testExpectingNoDelta(conn); } @Test(enabled = false) private void testExpectingNoDelta(LdapConnection conn) throws NamingException { String baseContext = conn.getConfiguration().getBaseContexts()[0]; String entryDN = "uid=foobar," + baseContext; List<SyncDelta> result = doTest(conn, "dn: " +entryDN + "\n" + "changetype: add\n" + "objectClass: inetOrgPerson\n" + "objectClass: organizationalPerson\n" + "objectClass: person\n" + "objectClass: top\n" + "uid: foobar\n" + "cn: Foo Bar\n" + "sn: Bar\n", 0); assertTrue(result.isEmpty()); } @Test public void testSyncSupported() throws NamingException { LdapConfiguration config = newConfiguration(); LdapConnection conn = newConnection(config); assertEquals(ServerType.SUN_DSEE, conn.getServerType()); Schema schema = newFacade(config).schema(); ObjectClassInfo accountInfo = schema.findObjectClassInfo(ObjectClass.ACCOUNT_NAME); assertTrue(schema.getSupportedObjectClassesByOperation().get(SyncApiOp.class).contains(accountInfo)); } }