/* * ==================== * 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 2011 Radovan Semancik (Evolveum) * Portions Copyrighted 2013-2014 ForgeRock AS */ package org.identityconnectors.ldap.schema; import java.util.Collection; import java.util.EnumSet; import java.util.HashSet; import java.util.Set; import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.common.objects.AttributeInfo; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.ObjectClassInfo; import org.identityconnectors.framework.common.objects.ObjectClassInfoBuilder; import org.identityconnectors.framework.common.objects.Schema; import org.identityconnectors.framework.common.objects.SchemaBuilder; import org.identityconnectors.framework.common.objects.AttributeInfo.Flags; import org.identityconnectors.framework.common.objects.OperationOptionInfoBuilder; 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.SearchOp; import org.identityconnectors.framework.spi.operations.SyncOp; import org.identityconnectors.framework.spi.operations.UpdateAttributeValuesOp; import org.identityconnectors.framework.spi.operations.UpdateOp; import org.identityconnectors.ldap.LdapAttributeType; import org.identityconnectors.ldap.LdapConnection; import org.identityconnectors.ldap.LdapConnector; import org.identityconnectors.ldap.LdapNativeSchema; import org.identityconnectors.ldap.LdapUtil; import org.identityconnectors.ldap.ObjectClassMappingConfig; import org.identityconnectors.ldap.LdapConnection.ServerType; class LdapSchemaBuilder { private static final Log log = Log.getLog(LdapSchemaBuilder.class); private final LdapConnection conn; private final LdapNativeSchema nativeSchema; private Schema schema; private Set<String> ignoredAttrs; public LdapSchemaBuilder(LdapConnection conn) { this.conn = conn; this.nativeSchema = conn.createNativeSchema(); this.ignoredAttrs = CollectionUtil.newCaseInsensitiveSet(); } public Set<String> getIgnoredAttrs() { return ignoredAttrs; } public Schema getSchema() { if (schema == null) { buildSchema(); } return schema; } private void buildSchema() { SchemaBuilder schemaBld = new SchemaBuilder(LdapConnector.class); for (ObjectClassMappingConfig oclassConfig : conn.getConfiguration().getObjectClassMappingConfigs().values()) { ObjectClass oclass = oclassConfig.getObjectClass(); ObjectClassInfoBuilder objClassBld = new ObjectClassInfoBuilder(); objClassBld.setType(oclass.getObjectClassValue()); objClassBld.setContainer(oclassConfig.isContainer()); objClassBld.addAllAttributeInfo(createAttributeInfos(oclassConfig.getLdapClasses())); objClassBld.addAllAttributeInfo(oclassConfig.getOperationalAttributes()); ObjectClassInfo oci = objClassBld.build(); schemaBld.defineObjectClass(oci); // XXX hack: these decisions should be made outside of LdapSchemaBuilder. if (!oci.is(ObjectClass.ACCOUNT_NAME)) { schemaBld.removeSupportedObjectClass(AuthenticateOp.class, oci); } // Since we are not sure we can detect Sun DSEE correctly, only disable sync() for servers known not to support it. if (conn.getServerType() == ServerType.OPENDS) { schemaBld.removeSupportedObjectClass(SyncOp.class, oci); } } for (String ldapClass : nativeSchema.getStructuralObjectClasses()) { ObjectClassInfoBuilder objClassBld = new ObjectClassInfoBuilder(); objClassBld.setType(ldapClass); objClassBld.setContainer(true); // Any LDAP object class can contain sub-entries. objClassBld.addAllAttributeInfo(createAttributeInfos(nativeSchema.getEffectiveObjectClasses(ldapClass))); ObjectClassInfo oci = objClassBld.build(); schemaBld.defineObjectClass(oci); schemaBld.removeSupportedObjectClass(AuthenticateOp.class, oci); // Since we are not sure we can detect Sun DSEE correctly, only disable sync() for servers known not to support it. if (conn.getServerType() == ServerType.OPENDS) { schemaBld.removeSupportedObjectClass(SyncOp.class, oci); } } schemaBld.defineOperationOption(OperationOptionInfoBuilder.buildRunWithUser(), CreateOp.class, UpdateOp.class, DeleteOp.class); schemaBld.defineOperationOption(OperationOptionInfoBuilder.buildRunWithPassword(), CreateOp.class, UpdateOp.class, DeleteOp.class); schemaBld.defineOperationOption(OperationOptionInfoBuilder.buildPageSize(), SearchOp.class); schemaBld.defineOperationOption(OperationOptionInfoBuilder.buildPagedResultsCookie(), SearchOp.class); schemaBld.defineOperationOption(OperationOptionInfoBuilder.buildPagedResultsOffset(), SearchOp.class); schemaBld.defineOperationOption(OperationOptionInfoBuilder.buildSortKeys(), SearchOp.class); ObjectClassInfoBuilder objClassBld = new ObjectClassInfoBuilder(); objClassBld.setType(LdapUtil.SERVER_INFO_NAME); ObjectClassInfo oci = objClassBld.build(); schemaBld.defineObjectClass(oci); schemaBld.removeSupportedObjectClass(AuthenticateOp.class, oci); schemaBld.removeSupportedObjectClass(ResolveUsernameOp.class, oci); schemaBld.removeSupportedObjectClass(CreateOp.class, oci); schemaBld.removeSupportedObjectClass(UpdateAttributeValuesOp.class, oci); schemaBld.removeSupportedObjectClass(DeleteOp.class, oci); schemaBld.removeSupportedObjectClass(SyncOp.class, oci); schema = schemaBld.build(); } private Set<AttributeInfo> createAttributeInfos(Collection<String> ldapClasses) { Set<AttributeInfo> result = new HashSet<AttributeInfo>(); Set<String> requiredAttrs = getRequiredAttributes(ldapClasses); Set<String> optionalAttrs = getOptionalAttributes(ldapClasses); // OpenLDAP's ipProtocol has MUST ( ... $ description ) MAY ( description ) optionalAttrs.removeAll(requiredAttrs); requiredAttrs.removeAll(ignoredAttrs); optionalAttrs.removeAll(ignoredAttrs); addAttributeInfos(ldapClasses, requiredAttrs, EnumSet.of(Flags.REQUIRED), null, result); addAttributeInfos(ldapClasses, optionalAttrs, null, null, result); return result; } private Set<String> getRequiredAttributes(Collection<String> ldapClasses) { Set<String> result = new HashSet<String>(); for (String ldapClass : ldapClasses) { result.addAll(nativeSchema.getRequiredAttributes(ldapClass)); } return result; } private Set<String> getOptionalAttributes(Collection<String> ldapClasses) { Set<String> result = new HashSet<String>(); for (String ldapClass : ldapClasses) { result.addAll(nativeSchema.getOptionalAttributes(ldapClass)); } return result; } private void addAttributeInfos(Collection<String> ldapClasses, Set<String> attrs, Set<Flags> add, Set<Flags> remove, Set<AttributeInfo> toSet) { for (String attr : attrs) { addAttributeInfo(ldapClasses, attr, attr, add, remove, toSet); } } private void addAttributeInfo(Collection<String> ldapClasses, String ldapAttrName, String realName, Set<Flags> add, Set<Flags> remove, Set<AttributeInfo> toSet) { LdapAttributeType attrDesc = nativeSchema.getAttributeDescription(ldapAttrName); if (attrDesc != null) { toSet.add(attrDesc.createAttributeInfo(realName, add, remove)); } else { log.warn("Could not find attribute {0} in object classes {1}", ldapAttrName, ldapClasses); } } }