All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.connid.bundles.ldap.commons.ServerNativeSchema Maven / Gradle / Ivy

The newest version!
/**
 * ====================
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved.
 * Copyright 2011-2013 Tirasa. 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 https://oss.oracle.com/licenses/CDDL
 * 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 https://oss.oracle.com/licenses/CDDL.
 * 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.connid.bundles.ldap.commons;

import org.connid.bundles.ldap.commons.LdapNativeSchema;
import org.connid.bundles.ldap.commons.LdapAttributeType;
import static java.util.Collections.unmodifiableSet;
import static org.identityconnectors.common.CollectionUtil.newCaseInsensitiveMap;
import static org.identityconnectors.common.CollectionUtil.newCaseInsensitiveSet;
import static org.connid.bundles.ldap.commons.LdapUtil.addStringAttrValues;
import static org.connid.bundles.ldap.commons.LdapUtil.attrNameEquals;
import static org.connid.bundles.ldap.commons.LdapUtil.getStringAttrValue;

import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;

import org.identityconnectors.framework.common.objects.AttributeInfo.Flags;
import org.connid.bundles.ldap.LdapConnection;

/**
 * Implements {@link LdapNativeSchema} by reading it from the server.
 */
public class ServerNativeSchema implements LdapNativeSchema {

    // The LDAP directory attributes to expose as framework attributes.
    private static final Set LDAP_DIRECTORY_ATTRS;

    private final LdapConnection conn;

    private final DirContext schemaCtx;

    private final Set structuralLdapClasses = newCaseInsensitiveSet();

    private final Map> ldapClass2MustAttrs = newCaseInsensitiveMap();

    private final Map> ldapClass2MayAttrs = newCaseInsensitiveMap();

    private final Map> ldapClass2Sup = newCaseInsensitiveMap();

    private final Map attrName2Type = newCaseInsensitiveMap();

    static {
        LDAP_DIRECTORY_ATTRS = newCaseInsensitiveSet();
        LDAP_DIRECTORY_ATTRS.add("createTimestamp");
        LDAP_DIRECTORY_ATTRS.add("modifyTimestamp");
        LDAP_DIRECTORY_ATTRS.add("creatorsName");
        LDAP_DIRECTORY_ATTRS.add("modifiersName");
    }

    public ServerNativeSchema(LdapConnection conn)
            throws NamingException {
        this.conn = conn;
        LDAP_DIRECTORY_ATTRS.add(this.conn.getConfiguration().getDnAttribute());
        schemaCtx = conn.getInitialContext().getSchema("");
        try {
            initObjectClasses();
            initAttributeDescriptions();
        } finally {
            schemaCtx.close();
        }
    }

    @Override
    public Set getStructuralObjectClasses() {
        return unmodifiableSet(structuralLdapClasses);
    }

    @Override
    public Set getRequiredAttributes(String ldapClass) {
        return getAttributes(ldapClass, true);
    }

    @Override
    public Set getOptionalAttributes(String ldapClass) {
        return getAttributes(ldapClass, false);
    }

    private Set getAttributes(String ldapClass, boolean required) {
        Set result = newCaseInsensitiveSet();
        Queue queue = new LinkedList();
        Set visited = new HashSet();
        queue.add(ldapClass);

        while (!queue.isEmpty()) {
            String current = queue.remove();
            if (!visited.contains(current)) {
                visited.add(current);
                Set attrs = required ? ldapClass2MustAttrs.get(current) : ldapClass2MayAttrs.
                        get(current);
                if (attrs != null) {
                    result.addAll(attrs);
                }
                Set supClasses = ldapClass2Sup.get(current);
                if (supClasses != null) {
                    queue.addAll(supClasses);
                }
            }
        }

        return result;
    }

    @Override
    public Set getEffectiveObjectClasses(String ldapClass) {
        Set result = newCaseInsensitiveSet();
        Queue classQueue = new LinkedList();
        classQueue.add(ldapClass);

        while (!classQueue.isEmpty()) {
            String classToVisit = classQueue.remove();
            if (!result.contains(classToVisit)) {
                result.add(classToVisit);
                Set supClasses = ldapClass2Sup.get(classToVisit);
                if (supClasses != null) {
                    classQueue.addAll(supClasses);
                }
            }
        }

        return result;
    }

    @Override
    public LdapAttributeType getAttributeDescription(String ldapAttrName) {
        return attrName2Type.get(ldapAttrName);
    }

    private void initObjectClasses()
            throws NamingException {
        DirContext objClassCtx = (DirContext) schemaCtx.lookup("ClassDefinition");
        NamingEnumeration objClassEnum = objClassCtx.list("");
        while (objClassEnum.hasMore()) {
            String objClassName = objClassEnum.next().getName();
            Attributes attrs = objClassCtx.getAttributes(objClassName);

            boolean abstractAttr = "true".equals(getStringAttrValue(attrs,
                    "ABSTRACT"));
            boolean structuralAttr = "true".equals(getStringAttrValue(attrs,
                    "STRUCTURAL"));
            boolean auxiliaryAttr = "true".equals(getStringAttrValue(attrs,
                    "AUXILIARY"));
            boolean structural = structuralAttr || !(abstractAttr || auxiliaryAttr);

            Set mustAttrs = newCaseInsensitiveSet();
            addStringAttrValues(attrs, "MUST", mustAttrs);
            Set mayAttrs = newCaseInsensitiveSet();
            addStringAttrValues(attrs, "MAY", mayAttrs);

            // The objectClass attribute must not be required, since it is handled internally by the connector.
            if (mustAttrs.remove("objectClass")) {
                mayAttrs.add("objectClass");
            }

            Set supClasses = newCaseInsensitiveSet();
            addStringAttrValues(attrs, "SUP", supClasses);
            if (structural && supClasses.isEmpty()) {
                // Hack for OpenDS, whose "referral" object class does not specify SUP.
                supClasses.add("top");
            }

            Set names = newCaseInsensitiveSet();
            addStringAttrValues(attrs, "NAME", names);
            for (String name : names) {
                if (structural) {
                    structuralLdapClasses.addAll(names);
                }
                ldapClass2MustAttrs.put(name, mustAttrs);
                ldapClass2MayAttrs.put(name, mayAttrs);
                ldapClass2Sup.put(name, supClasses);
            }
        }
    }

    private void initAttributeDescriptions()
            throws NamingException {
        DirContext attrsCtx = (DirContext) schemaCtx.lookup(
                "AttributeDefinition");
        NamingEnumeration attrsEnum = attrsCtx.list("");
        while (attrsEnum.hasMore()) {
            String attrName = attrsEnum.next().getName();
            Attributes attrs = attrsCtx.getAttributes(attrName);

            boolean singleValue = "true".equals(getStringAttrValue(attrs,
                    "SINGLE-VALUE"));
            boolean noUserModification = "true".equals(getStringAttrValue(attrs,
                    "NO-USER-MODIFICATION"));
            String usage = getStringAttrValue(attrs, "USAGE");
            boolean userApplications = "userApplications".equals(usage) || usage == null;

            Set names = newCaseInsensitiveSet();
            addStringAttrValues(attrs, "NAME", names);
            for (String name : names) {
                // The objectClass attribute must not be writable, since it is handled internally by the connector.
                boolean objectClass = attrNameEquals(name, "objectClass");
                boolean binary = conn.isBinarySyntax(attrName);

                Class type;
                if (binary) {
                    type = byte[].class;
                } else {
                    type = String.class;
                }
                Set flags = EnumSet.noneOf(Flags.class);
                if (!singleValue) {
                    flags.add(Flags.MULTIVALUED);
                }
                if (noUserModification || objectClass) {
                    flags.add(Flags.NOT_CREATABLE);
                    flags.add(Flags.NOT_UPDATEABLE);
                }
                // XXX perhaps this should be true for binary attributes too.
                if (!userApplications) {
                    flags.add(Flags.NOT_RETURNED_BY_DEFAULT);
                }
                attrName2Type.put(name, new LdapAttributeType(type, flags));
            }
        }

        for (String dirAttrName : LDAP_DIRECTORY_ATTRS) {
            attrName2Type.put(dirAttrName, new LdapAttributeType(String.class,
                    EnumSet.of(Flags.NOT_CREATABLE, Flags.NOT_UPDATEABLE,
                    Flags.NOT_RETURNED_BY_DEFAULT)));
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy