/* * ==================== * 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]" * ==================== * * Portions Copyrighted 2013-2014 ForgeRock AS */ package org.identityconnectors.ldap; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.naming.NamingException; import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.common.exceptions.ConfigurationException; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.OperationOptions; import org.identityconnectors.framework.common.objects.ResultsHandler; import org.identityconnectors.framework.common.objects.Schema; import org.identityconnectors.framework.common.objects.SyncResultsHandler; import org.identityconnectors.framework.common.objects.SyncToken; import org.identityconnectors.framework.common.objects.Uid; import org.identityconnectors.framework.common.objects.filter.FilterTranslator; import org.identityconnectors.framework.spi.Configuration; import org.identityconnectors.framework.spi.ConnectorClass; import org.identityconnectors.framework.spi.PoolableConnector; import org.identityconnectors.framework.spi.operations.AuthenticateOp; import org.identityconnectors.framework.spi.operations.CreateOp; import org.identityconnectors.framework.spi.operations.DeleteOp; import org.identityconnectors.framework.spi.operations.ResolveUsernameOp; import org.identityconnectors.framework.spi.operations.SchemaOp; import org.identityconnectors.framework.spi.operations.SearchOp; import org.identityconnectors.framework.spi.operations.SyncOp; import org.identityconnectors.framework.spi.operations.TestOp; import org.identityconnectors.framework.spi.operations.UpdateAttributeValuesOp; import org.identityconnectors.ldap.modify.LdapCreate; import org.identityconnectors.ldap.modify.LdapDelete; import org.identityconnectors.ldap.modify.LdapUpdate; import org.identityconnectors.ldap.search.LdapFilter; import org.identityconnectors.ldap.search.LdapFilterTranslator; import org.identityconnectors.ldap.search.LdapSearch; import org.identityconnectors.ldap.sync.activedirectory.ActiveDirectoryChangeLogSyncStrategy; import org.identityconnectors.ldap.sync.ibm.IBMDSChangeLogSyncStrategy; import org.identityconnectors.ldap.sync.sunds.SunDSChangeLogSyncStrategy; import org.identityconnectors.ldap.sync.timestamps.TimestampsSyncStrategy; @ConnectorClass(configurationClass = LdapConfiguration.class, displayNameKey = "LdapConnector") public class LdapConnector implements TestOp, PoolableConnector, SchemaOp, SearchOp<LdapFilter>, AuthenticateOp, ResolveUsernameOp, CreateOp, DeleteOp, UpdateAttributeValuesOp, SyncOp { // XXX groups. /** * The configuration for this connector instance. */ private LdapConfiguration config; /** * The login context if SASL-GSSAPI is used for authentication */ private LoginContext loginContext = null; /** * The connection to the LDAP server. */ private LdapConnection conn; private enum UpdateType { REPLACE, ADD, REMOVE } public LdapConnector() { } public Configuration getConfiguration() { return config; } public void init(Configuration cfg) { config = (LdapConfiguration) cfg; conn = new LdapConnection(config); if (LdapConnection.SASL_GSSAPI.equalsIgnoreCase(config.getAuthType())) { try { loginContext = new LoginContext(LdapConnector.class.getName()); loginContext.login(); } catch (LoginException le) { throw new ConfigurationException("Authentication attempt failed" + le); } } else { } } public void dispose() { conn.close(); } public void test() { if (loginContext != null) { Subject.doAs(loginContext.getSubject(), new PrivilegedAction() { public Object run() { doTest(); return null; } }); } else { doTest(); } } public void checkAlive() { if (loginContext != null) { Subject.doAs(loginContext.getSubject(), new PrivilegedAction() { public Object run() { conn.checkAlive(); return null; } }); } else { conn.checkAlive(); } } public Schema schema() { if (loginContext != null) { return Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Schema>() { public Schema run() { return conn.getSchemaMapping().schema(); } }); } else { return conn.getSchemaMapping().schema(); } } public Uid authenticate(final ObjectClass objectClass, final String username, final GuardedString password, final OperationOptions options) { if (loginContext != null) { return Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Uid>() { public Uid run() { return new LdapAuthenticate(conn, objectClass, username, options).authenticate(password); } }); } else { return new LdapAuthenticate(conn, objectClass, username, options).authenticate(password); } } public Uid resolveUsername(final ObjectClass objectClass, final String username, final OperationOptions options) { if (loginContext != null) { return Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Uid>() { public Uid run() { return new LdapAuthenticate(conn, objectClass, username, options).resolveUsername(); } }); } else { return new LdapAuthenticate(conn, objectClass, username, options).resolveUsername(); } } public FilterTranslator<LdapFilter> createFilterTranslator(ObjectClass objectClass, OperationOptions options) { return new LdapFilterTranslator(conn.getSchemaMapping(), objectClass); } public void executeQuery(final ObjectClass objectClass, final LdapFilter query, final ResultsHandler handler, final OperationOptions options) { if (objectClass.is(LdapUtil.SERVER_INFO_NAME)) { LdapUtil.getServerInfo(conn, handler); } else { if (loginContext != null) { Subject.doAs(loginContext.getSubject(), new PrivilegedAction() { public Object run() { new LdapSearch(conn, objectClass, query, handler, options).execute(); return null; } }); } else { new LdapSearch(conn, objectClass, query, handler, options).execute(); } } } public Uid create(final ObjectClass objectClass, final Set<Attribute> attrs, final OperationOptions options) { if (loginContext != null) { return Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Uid>() { public Uid run() { return new LdapCreate(conn, objectClass, attrs, options).execute(); } }); } else { return new LdapCreate(conn, objectClass, attrs, options).execute(); } } public void delete(final ObjectClass objectClass, final Uid uid, final OperationOptions options) { if (loginContext != null) { Subject.doAs(loginContext.getSubject(), new PrivilegedAction() { public Object run() { new LdapDelete(conn, objectClass, uid, options).execute(); return null; } }); } else { new LdapDelete(conn, objectClass, uid, options).execute(); } } public Uid update(final ObjectClass objectClass, final Uid uid, final Set<Attribute> replaceAttributes, final OperationOptions options) { if (loginContext != null) { return Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Uid>() { public Uid run() { return new LdapUpdate(conn, objectClass, uid, options).update(replaceAttributes); } }); } else { return new LdapUpdate(conn, objectClass, uid, options).update(replaceAttributes); } } public Uid addAttributeValues(final ObjectClass objectClass, final Uid uid, final Set<Attribute> valuesToAdd, final OperationOptions options) { if (loginContext != null) { return Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Uid>() { public Uid run() { return new LdapUpdate(conn, objectClass, uid, options).addAttributeValues(valuesToAdd); } }); } else { return new LdapUpdate(conn, objectClass, uid, options).addAttributeValues(valuesToAdd); } } public Uid removeAttributeValues(final ObjectClass objectClass, final Uid uid, final Set<Attribute> valuesToRemove, final OperationOptions options) { if (loginContext != null) { return Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Uid>() { public Uid run() { return new LdapUpdate(conn, objectClass, uid, options).removeAttributeValues(valuesToRemove); } }); } else { return new LdapUpdate(conn, objectClass, uid, options).removeAttributeValues(valuesToRemove); } } public SyncToken getLatestSyncToken(final ObjectClass objectClass) { if (loginContext != null) { return Subject.doAs(loginContext.getSubject(), new PrivilegedAction<SyncToken>() { public SyncToken run() { return lastSyncToken(objectClass); } }); } else { return lastSyncToken(objectClass); } } public void sync(final ObjectClass objectClass, final SyncToken token, final SyncResultsHandler handler, final OperationOptions options) { if (loginContext != null) { Subject.doAs(loginContext.getSubject(), new PrivilegedAction() { public Object run() { doSync(objectClass, token, handler, options); return null; } }); } else { doSync(objectClass, token, handler, options); } } private SyncToken lastSyncToken(ObjectClass objectClass){ if (config.isUseTimestampsForSync()) { return new TimestampsSyncStrategy(conn, objectClass).getLatestSyncToken(); } else { switch (conn.getServerType()) { case UNKNOWN: case OPENLDAP: case MSAD_GC: return new TimestampsSyncStrategy(conn, objectClass).getLatestSyncToken(); case IBM: return new IBMDSChangeLogSyncStrategy(conn, objectClass).getLatestSyncToken(); case MSAD: case MSAD_LDS: return new ActiveDirectoryChangeLogSyncStrategy(conn, objectClass).getLatestSyncToken(); default: return new SunDSChangeLogSyncStrategy(conn, objectClass).getLatestSyncToken(); } } } private void doSync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { if (config.isUseTimestampsForSync()) { new TimestampsSyncStrategy(conn, objectClass).sync(token, handler, options); } else { switch (conn.getServerType()) { case UNKNOWN: case OPENLDAP: case MSAD_GC: new TimestampsSyncStrategy(conn, objectClass).sync(token, handler, options); break; case IBM: new IBMDSChangeLogSyncStrategy(conn, objectClass).sync(token, handler, options); break; case MSAD: case MSAD_LDS: new ActiveDirectoryChangeLogSyncStrategy(conn, objectClass).sync(token, handler, options); break; default: new SunDSChangeLogSyncStrategy(conn, objectClass).sync(token, handler, options); } } } private void doTest(){ List<String> badBC = new ArrayList<String>(); List<String> badBCS = new ArrayList<String>(); config.validate(); conn.test(); // Need to check that all the base contexts are valid for (String context : config.getBaseContexts()) { try { conn.getInitialContext().getAttributes(context); } catch (NamingException e) { badBC.add(context); } } for (String context : config.getBaseContextsToSynchronize()) { try { conn.getInitialContext().getAttributes(context); } catch (NamingException e) { badBCS.add(context); } } if (!badBC.isEmpty()) { throw new ConfigurationException("Bad Base Context(s): " + badBC.toString()); } if (!badBCS.isEmpty()) { throw new ConfigurationException("Bad Base Context(s) to Synchronize: " + badBCS.toString()); } } }