Package org.apache.ldap.server.schema

Source Code of org.apache.ldap.server.schema.SchemaChecker

/*
*   Copyright 2004 The Apache Software Foundation
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
*
*/
package org.apache.ldap.server.schema;


import org.apache.ldap.common.util.NamespaceTools;
import org.apache.ldap.common.exception.LdapSchemaViolationException;
import org.apache.ldap.common.message.ResultCodeEnum;
import org.apache.ldap.common.schema.ObjectClass;
import org.apache.ldap.common.schema.ObjectClassTypeEnum;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.naming.NamingException;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.Attribute;
import java.util.Set;
import java.util.HashSet;


/**
* Performs schema checks on behalf of the SchemaService.
*
* @todo we really need to refactor this code since there's much duplication
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$, $Date$
*/
public class SchemaChecker
{
    /** the SLF4J logger for this class */
    private static Logger log = LoggerFactory.getLogger( SchemaChecker.class );

    /**
     * Makes sure modify operations do not leave the entry without a STRUCTURAL
     * objectClass.  At least one STRUCTURAL objectClass must be specified for
     * the entry after modifications take effect.
     *
     * @param registry the objectClass registry to lookup ObjectClass specifications
     * @param name the name of the entry being modified
     * @param mod the type of modification operation being performed (should be
     * REMOVE_ATTRIBUTE)
     * @param attribute the attribute being modified
     * @throws NamingException if modify operations leave the entry inconsistent
     * without a STRUCTURAL objectClass
     */
    public static void preventStructuralClassRemovalOnModifyReplace( ObjectClassRegistry registry, Name name, int mod,
                                                                     Attribute attribute )
            throws NamingException
    {
        if ( mod != DirContext.REPLACE_ATTRIBUTE )
        {
            return;
        }

        if ( ! "objectclass".equalsIgnoreCase( attribute.getID() ) )
        {
            return;
        }

        // whoever issued the modify operation is insane they want to delete
        // all the objectClass values in which case we must throw an exception
        if ( attribute.size() == 0 )
        {
            String msg = "Modify operation leaves no structural objectClass for entry " + name;
            if ( log.isInfoEnabled() )
            {
                log.info( msg + ".  Raising LdapSchemaViolationException." );
            }
            throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
        }

        // check that there is at least one structural objectClass in the replacement set
        for ( int ii = 0; ii < attribute.size(); ii++ )
        {
            ObjectClass ocType = registry.lookup( ( String ) attribute.get( ii ) );
            if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
            {
                return;
            }
        }

        // no structural object classes exist for the entry in the replacement
        // set for the objectClass attribute so we need to complain about that
        String msg = "Modify operation leaves no structural objectClass for entry " + name;
        if ( log.isInfoEnabled() )
        {
            log.info( msg + ".  Raising LdapSchemaViolationException." );
        }
        throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
    }


    /**
     * Makes sure modify operations do not leave the entry without a STRUCTURAL
     * objectClass.  At least one STRUCTURAL objectClass must be specified for
     * the entry after modifications take effect.
     *
     * @param registry the objectClass registry to lookup ObjectClass specifications
     * @param name the name of the entry being modified
     * @param mod the type of modification operation being performed (should be
     * REMOVE_ATTRIBUTE)
     * @param attributes the attributes being modified
     * @throws NamingException if modify operations leave the entry inconsistent
     * without a STRUCTURAL objectClass
     */
    public static void preventStructuralClassRemovalOnModifyReplace( ObjectClassRegistry registry, Name name, int mod,
                                                                     Attributes attributes )
            throws NamingException
    {
        if ( mod != DirContext.REPLACE_ATTRIBUTE )
        {
            return;
        }

        Attribute objectClass = attributes.get( "objectClass" );
        if ( objectClass == null )
        {
            return;
        }

        // whoever issued the modify operation is insane they want to delete
        // all the objectClass values in which case we must throw an exception
        if ( objectClass.size() == 0 )
        {
            String msg = "Modify operation leaves no structural objectClass for entry " + name;
            if ( log.isInfoEnabled() )
            {
                log.info( msg + ".  Raising LdapSchemaViolationException." );
            }
            throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
        }

        // check that there is at least one structural objectClass in the replacement set
        for ( int ii = 0; ii < objectClass.size(); ii++ )
        {
            ObjectClass ocType = registry.lookup( ( String ) objectClass.get( ii ) );
            if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
            {
                return;
            }
        }

        // no structural object classes exist for the entry in the replacement
        // set for the objectClass attribute so we need to complain about that
        String msg = "Modify operation leaves no structural objectClass for entry " + name;
        if ( log.isInfoEnabled() )
        {
            log.info( msg + ".  Raising LdapSchemaViolationException." );
        }
        throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
    }


    /**
     * Makes sure modify operations do not leave the entry without a STRUCTURAL
     * objectClass.  At least one STRUCTURAL objectClass must be specified for
     * the entry after modifications take effect.
     *
     * @param registry the objectClass registry to lookup ObjectClass specifications
     * @param name the name of the entry being modified
     * @param mod the type of modification operation being performed (should be
     * REMOVE_ATTRIBUTE)
     * @param attribute the attribute being modified
     * @param entryObjectClasses the entry being modified
     * @throws NamingException if modify operations leave the entry inconsistent
     * without a STRUCTURAL objectClass
     */
    public static void preventStructuralClassRemovalOnModifyRemove( ObjectClassRegistry registry, Name name, int mod,
                                                                    Attribute attribute, Attribute entryObjectClasses )
            throws NamingException
    {
        if ( mod != DirContext.REMOVE_ATTRIBUTE )
        {
            return;
        }

        if ( ! "objectclass".equalsIgnoreCase( attribute.getID() ) )
        {
            return;
        }

        // whoever issued the modify operation is insane they want to delete
        // all the objectClass values in which case we must throw an exception
        if ( attribute.size() == 0 )
        {
            String msg = "Modify operation leaves no structural objectClass for entry " + name;
            if ( log.isInfoEnabled() )
            {
                log.info( msg + ".  Raising LdapSchemaViolationException." );
            }
            throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
        }

        // remove all the objectClass attribute values from a cloned copy and then
        // we can analyze what remains in this attribute to make sure a structural
        // objectClass is present for the entry

        Attribute cloned = ( Attribute ) entryObjectClasses.clone();
        for ( int ii = 0; ii < attribute.size(); ii++ )
        {
            cloned.remove( attribute.get( ii ) );
        }

        // check resultant set of objectClass values for a structural objectClass
        for ( int ii = 0; ii < cloned.size(); ii++ )
        {
            ObjectClass ocType = registry.lookup( ( String ) cloned.get( ii ) );
            if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
            {
                return;
            }
        }

        // no structural object classes exist for the entry after the modifications
        // to the objectClass attribute so we need to complain about that
        String msg = "Modify operation leaves no structural objectClass for entry " + name;
        if ( log.isInfoEnabled() )
        {
            log.info( msg + ".  Raising LdapSchemaViolationException." );
        }
        throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
    }


    /**
     * Makes sure modify operations do not leave the entry without a STRUCTURAL
     * objectClass.  At least one STRUCTURAL objectClass must be specified for
     * the entry after modifications take effect.
     *
     * @param registry the objectClass registry to lookup ObjectClass specifications
     * @param name the name of the entry being modified
     * @param mod the type of modification operation being performed (should be
     * REMOVE_ATTRIBUTE)
     * @param attributes the attributes being modified
     * @param entryObjectClasses the entry being modified
     * @throws NamingException if modify operations leave the entry inconsistent
     * without a STRUCTURAL objectClass
     */
    public static void preventStructuralClassRemovalOnModifyRemove( ObjectClassRegistry registry, Name name, int mod,
                                                                    Attributes attributes, Attribute entryObjectClasses )
            throws NamingException
    {
        if ( mod != DirContext.REMOVE_ATTRIBUTE )
        {
            return;
        }

        Attribute objectClass = attributes.get( "objectClass" );
        if ( objectClass == null )
        {
            return;
        }

        // whoever issued the modify operation is insane they want to delete
        // all the objectClass values in which case we must throw an exception
        if ( objectClass.size() == 0 )
        {
            String msg = "Modify operation leaves no structural objectClass for entry " + name;
            if ( log.isInfoEnabled() )
            {
                log.info( msg + ".  Raising LdapSchemaViolationException." );
            }
            throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
        }

        // remove all the objectClass attribute values from a cloned copy and then
        // we can analyze what remains in this attribute to make sure a structural
        // objectClass is present for the entry

        Attribute cloned = ( Attribute ) entryObjectClasses.clone();
        for ( int ii = 0; ii < objectClass.size(); ii++ )
        {
            cloned.remove( objectClass.get( ii ) );
        }

        // check resultant set of objectClass values for a structural objectClass
        for ( int ii = 0; ii < cloned.size(); ii++ )
        {
            ObjectClass ocType = registry.lookup( ( String ) cloned.get( ii ) );
            if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
            {
                return;
            }
        }

        // no structural object classes exist for the entry after the modifications
        // to the objectClass attribute so we need to complain about that
        String msg = "Modify operation leaves no structural objectClass for entry " + name;
        if ( log.isInfoEnabled() )
        {
            log.info( msg + ".  Raising LdapSchemaViolationException." );
        }
        throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
    }


    /**
     * Makes sure a modify operation does not replace RDN attributes or their value.
     * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
     * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
     * seen below:
     * <p/>
     * <pre>
     *     The Modify Operation cannot be used to remove from an entry any of
     *     its distinguished values, those values which form the entry's
     *     relative distinguished name.  An attempt to do so will result in the
     *     server returning the error notAllowedOnRDN.  The Modify DN Operation
     *     described in section 4.9 is used to rename an entry.
     * </pre>
     *
     * @param name the distinguished name of the attribute being modified
     * @param mod the modification operation being performed (should be REPLACE_ATTRIBUTE )
     * @param attribute the attribute being modified
     * @throws NamingException if the modify operation is removing an Rdn attribute
     */
    public static void preventRdnChangeOnModifyReplace( Name name, int mod, Attribute attribute )
            throws NamingException
    {
        if ( mod != DirContext.REPLACE_ATTRIBUTE )
        {
            return;
        }

        Set rdnAttributes = getRdnAttributes( name );
        String id = ( String ) attribute.getID();

        if ( ! rdnAttributes.contains( id ) )
        {
            return;
        }

        // if the attribute values to delete are not specified then all values
        // for the attribute are to be deleted in which case we must just throw
        // a schema violation exception with the notAllowedOnRdn result code
        if ( attribute.size() == 0 )
        {
            String msg = "Modify operation attempts to delete RDN attribute ";
            msg += id + " on entry " + name + " violates schema constraints";

            if ( log.isInfoEnabled() )
            {
                log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
            }
            throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
        }

        // from here on the modify operation replaces specific values
        // of the Rdn attribute so we must check to make sure all the old
        // rdn attribute values are present in the replacement set
        String rdnValue = getRdnValue( id, name );
        for ( int ii = 0; ii < attribute.size(); ii++ )
        {
            // if the old rdn value is not in the rdn attribute then
            // we must complain with a schema violation
            if ( ! attribute.contains( rdnValue ) )
            {
                String msg = "Modify operation attempts to delete RDN attribute values in use for ";
                msg += id + " on entry " + name + " and violates schema constraints";

                if ( log.isInfoEnabled() )
                {
                    log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
                }
                throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
            }
        }
    }


    /**
     * Makes sure a modify operation does not replace RDN attributes or their value.
     * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
     * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
     * seen below:
     * <p/>
     * <pre>
     *     The Modify Operation cannot be used to remove from an entry any of
     *     its distinguished values, those values which form the entry's
     *     relative distinguished name.  An attempt to do so will result in the
     *     server returning the error notAllowedOnRDN.  The Modify DN Operation
     *     described in section 4.9 is used to rename an entry.
     * </pre>
     *
     * @param name the distinguished name of the attribute being modified
     * @param mod the modification operation being performed (should be REPLACE_ATTRIBUTE )
     * @param attributes the attributes being modified
     * @throws NamingException if the modify operation is removing an Rdn attribute
     */
    public static void preventRdnChangeOnModifyReplace( Name name, int mod, Attributes attributes )
            throws NamingException
    {
        if ( mod != DirContext.REPLACE_ATTRIBUTE )
        {
            return;
        }

        Set rdnAttributes = getRdnAttributes( name );
        NamingEnumeration list = attributes.getIDs();
        while ( list.hasMore() )
        {
            String id = ( String ) list.next();

            if ( rdnAttributes.contains( id ) )
            {
                // if the attribute values to delete are not specified then all values
                // for the attribute are to be deleted in which case we must just throw
                // a schema violation exception with the notAllowedOnRdn result code
                if ( attributes.get( id ).size() == 0 )
                {
                    String msg = "Modify operation attempts to delete RDN attribute ";
                    msg += id + " on entry " + name + " violates schema constraints";

                    if ( log.isInfoEnabled() )
                    {
                        log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
                    }
                    throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
                }

                // from here on the modify operation replaces specific values
                // of the Rdn attribute so we must check to make sure all the old
                // rdn attribute values are present in the replacement set
                String rdnValue = getRdnValue( id, name );
                Attribute rdnAttr = attributes.get( id );
                for ( int ii = 0; ii < rdnAttr.size(); ii++ )
                {
                    // if the old rdn value is not in the rdn attribute then
                    // we must complain with a schema violation
                    if ( ! rdnAttr.contains( rdnValue ) )
                    {
                        String msg = "Modify operation attempts to delete RDN attribute values in use for ";
                        msg += id + " on entry " + name + " and violates schema constraints";

                        if ( log.isInfoEnabled() )
                        {
                            log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
                        }
                        throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
                    }
                }
            }
        }
    }


    /**
     * Makes sure a modify operation does not delete RDN attributes or their value.
     * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
     * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
     * seen below:
     * <p/>
     * <pre>
     *     The Modify Operation cannot be used to remove from an entry any of
     *     its distinguished values, those values which form the entry's
     *     relative distinguished name.  An attempt to do so will result in the
     *     server returning the error notAllowedOnRDN.  The Modify DN Operation
     *     described in section 4.9 is used to rename an entry.
     * </pre>
     *
     * @param name the distinguished name of the attribute being modified
     * @param mod the modification operation being performed (should be REMOVE_ATTRIBUTE )
     * @param attribute the attribute being modified
     * @throws NamingException if the modify operation is removing an Rdn attribute
     */
    public static void preventRdnChangeOnModifyRemove( Name name, int mod, Attribute attribute )
            throws NamingException
    {
        if ( mod != DirContext.REMOVE_ATTRIBUTE )
        {
            return;
        }

        Set rdnAttributes = getRdnAttributes( name );
        String id = attribute.getID();

        if ( ! rdnAttributes.contains( id ) )
        {
            return;
        }

        // if the attribute values to delete are not specified then all values
        // for the attribute are to be deleted in which case we must just throw
        // a schema violation exception with the notAllowedOnRdn result code
        if ( attribute.size() == 0 )
        {
            String msg = "Modify operation attempts to delete RDN attribute ";
            msg += id + " on entry " + name + " violates schema constraints";

            if ( log.isInfoEnabled() )
            {
                log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
            }
            throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
        }

        // from here on the modify operation only deletes specific values
        // of the Rdn attribute so we must check if one of those values
        // are used by the Rdn attribute value pair for the name of the entry
        String rdnValue = getRdnValue( id, name );
        for ( int ii = 0; ii < attribute.size(); ii++ )
        {
            if ( rdnValue.equals( attribute.get( ii ) ) )
            {
                String msg = "Modify operation attempts to delete RDN attribute values in use for ";
                msg += id + " on entry " + name + " and violates schema constraints";

                if ( log.isInfoEnabled() )
                {
                    log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
                }
                throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
            }
        }
    }


    /**
     * Makes sure a modify operation does not delete RDN attributes or their value.
     * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
     * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
     * seen below:
     * <p/>
     * <pre>
     *     The Modify Operation cannot be used to remove from an entry any of
     *     its distinguished values, those values which form the entry's
     *     relative distinguished name.  An attempt to do so will result in the
     *     server returning the error notAllowedOnRDN.  The Modify DN Operation
     *     described in section 4.9 is used to rename an entry.
     * </pre>
     *
     * @param name the distinguished name of the attribute being modified
     * @param mod the modification operation being performed (should be REMOVE_ATTRIBUTE )
     * @param attributes the attributes being modified
     * @throws NamingException if the modify operation is removing an Rdn attribute
     */
    public static void preventRdnChangeOnModifyRemove( Name name, int mod, Attributes attributes )
            throws NamingException
    {
        if ( mod != DirContext.REMOVE_ATTRIBUTE )
        {
            return;
        }

        Set rdnAttributes = getRdnAttributes( name );
        NamingEnumeration list = attributes.getIDs();
        while ( list.hasMore() )
        {
            String id = ( String ) list.next();

            if ( rdnAttributes.contains( id ) )
            {
                // if the attribute values to delete are not specified then all values
                // for the attribute are to be deleted in which case we must just throw
                // a schema violation exception with the notAllowedOnRdn result code
                if ( attributes.get( id ).size() == 0 )
                {
                    String msg = "Modify operation attempts to delete RDN attribute ";
                    msg += id + " on entry " + name + " violates schema constraints";

                    if ( log.isInfoEnabled() )
                    {
                        log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
                    }
                    throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
                }

                // from here on the modify operation only deletes specific values
                // of the Rdn attribute so we must check if one of those values
                // are used by the Rdn attribute value pair for the name of the entry
                String rdnValue = getRdnValue( id, name );
                Attribute rdnAttr = attributes.get( id );
                for ( int ii = 0; ii < rdnAttr.size(); ii++ )
                {
                    if ( rdnValue.equals( rdnAttr.get( ii ) ) )
                    {
                        String msg = "Modify operation attempts to delete RDN attribute values in use for ";
                        msg += id + " on entry " + name + " and violates schema constraints";

                        if ( log.isInfoEnabled() )
                        {
                            log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
                        }
                        throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
                    }
                }
            }
        }
    }


    /**
     * Gets the Rdn attribute value. This method works even if the Rdn is
     * composed of multiple attributes.
     *
     * @param id the attribute id of the Rdn attribute to return
     * @param name the distinguished name of the entry
     * @return the Rdn attribute value corresponding to the id, or null if the
     * attribute is not an rdn attribute
     * @throws NamingException if the name is malformed in any way
     */
    private static String getRdnValue( String id, Name name ) throws NamingException
    {
        String [] comps = NamespaceTools.getCompositeComponents( name.get( name.size() - 1 ) );

        for ( int ii = 0; ii < comps.length; ii++ )
        {
            String rdnAttrId = NamespaceTools.getRdnAttribute( comps[ii] );

            if ( rdnAttrId.equalsIgnoreCase( id ) )
            {
                return NamespaceTools.getRdnValue( comps[ii] );
            }
        }

        return null;
    }


    /**
     * Collects the set of Rdn attributes whether or not the Rdn is based on a
     * single attribute or multiple attributes.
     *
     * @param name the distinguished name of an entry
     * @return the set of attributes composing the Rdn for the name
     * @throws NamingException if the syntax of the Rdn is incorrect
     */
    private static Set getRdnAttributes( Name name ) throws NamingException
    {
        String [] comps = NamespaceTools.getCompositeComponents( name.get( name.size() - 1 ) );
        Set attributes = new HashSet();

        for ( int ii = 0; ii < comps.length; ii++ )
        {
            attributes.add( NamespaceTools.getRdnAttribute( comps[ii] ) );
        }

        return attributes;
    }
}
TOP

Related Classes of org.apache.ldap.server.schema.SchemaChecker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.