Package org.apache.directory.server.ldap.handlers

Source Code of org.apache.directory.server.ldap.handlers.ReferralAwareRequestHandler

/*
*   Licensed to the Apache Software Foundation (ASF) under one
*   or more contributor license agreements.  See the NOTICE file
*   distributed with this work for additional information
*   regarding copyright ownership.  The ASF licenses this file
*   to you 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.directory.server.ldap.handlers;


import javax.naming.InvalidNameException;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;

import org.apache.directory.server.core.entry.ClonedServerEntry;
import org.apache.directory.server.core.entry.ServerAttribute;
import org.apache.directory.server.i18n.I18n;
import org.apache.directory.server.ldap.LdapSession;
import org.apache.directory.shared.ldap.codec.controls.ManageDsaITControl;
import org.apache.directory.shared.ldap.codec.util.LdapURLEncodingException;
import org.apache.directory.shared.ldap.constants.SchemaConstants;
import org.apache.directory.shared.ldap.entry.Value;
import org.apache.directory.shared.ldap.exception.LdapException;
import org.apache.directory.shared.ldap.message.ReferralImpl;
import org.apache.directory.shared.ldap.message.ResultCodeEnum;
import org.apache.directory.shared.ldap.message.internal.InternalLdapResult;
import org.apache.directory.shared.ldap.message.internal.InternalReferral;
import org.apache.directory.shared.ldap.message.internal.InternalResultResponseRequest;
import org.apache.directory.shared.ldap.message.internal.InternalSearchRequest;
import org.apache.directory.shared.ldap.name.DN;
import org.apache.directory.shared.ldap.util.ExceptionUtils;
import org.apache.directory.shared.ldap.util.LdapURL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* A based class for handlers which deal with SingleReplyRequests.  This class
* provides various capabilities out of the box for these kinds of requests so
* common handling code is not duplicated.  Namely, exception handling and
* referral handling code common to most SingleReplyRequests (minus
* ExtendedRequests) are handled thanks to this class.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$, $Date$
*/
public abstract class ReferralAwareRequestHandler<T extends InternalResultResponseRequest> extends LdapRequestHandler<T>
{
    private static final Logger LOG = LoggerFactory.getLogger( ReferralAwareRequestHandler.class );
   
    /** Speedup for logs */
    private static final boolean IS_DEBUG = LOG.isDebugEnabled();

   
    /* (non-Javadoc)
     * @see org.apache.directory.server.ldap.handlers.LdapRequestHandler#handle(org.apache.directory.server.ldap.LdapSession, org.apache.directory.shared.ldap.message.Request)
     */
    @Override
    public final void handle( LdapSession session, T req ) throws Exception
    {
        LOG.debug( "Handling single reply request: {}", req );
       
        // First, if we have the ManageDSAIt control, go directly
        // to the handling without pre-processing the request
        if ( req.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
        {
            // If the ManageDsaIT control is present, we will
            // consider that the user wants to get entry which
            // are referrals as plain entry. We have to return
            // SearchResponseEntry elements instead of
            // SearchResponseReference elements.
            LOG.debug( "ManageDsaITControl detected." );
            handleIgnoringReferrals( session, req );
        }
        else
        {
            // No ManageDsaIT control. If the found entries is a referral,
            // we will return SearchResponseReference elements.
            LOG.debug( "ManageDsaITControl NOT detected." );
   
            switch ( req.getType() )
            {
                case SEARCH_REQUEST:
                    handleWithReferrals( session, ( ( InternalSearchRequest ) req ).getBase(), req );
                    break;

                case EXTENDED_REQUEST:
                    throw new IllegalStateException( I18n.err( I18n.ERR_684 ) );
                   
                default:
                    throw new IllegalStateException( I18n.err( I18n.ERR_685, req ) );
            }
           
        }

    }

   
    public static final boolean isEntryReferral( ClonedServerEntry entry ) throws Exception
    {
        return entry.getOriginalEntry().contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.REFERRAL_OC );
    }
   
   
    /**
     * Searches up the ancestry of a DN searching for the farthest referral
     * ancestor.  This is required to properly handle referrals.  Note that
     * this function is quite costly since it attempts to lookup all the
     * ancestors up the hierarchy just to see if they represent referrals.
     * Techniques can be employed later to improve this performance hit by
     * having an intelligent referral cache.
     *
     * @return the farthest referral ancestor or null
     * @throws Exception if there are problems during this search
     */
    public static final ClonedServerEntry getFarthestReferralAncestor( LdapSession session, DN target )
        throws Exception
    {
        ClonedServerEntry entry;
        ClonedServerEntry farthestReferralAncestor = null;
        DN dn = ( DN ) target.clone();
       
        try
        {
            dn.remove( dn.size() - 1 );
        }
        catch ( InvalidNameException e2 )
        {
            // never thrown
        }
       
        while ( ! dn.isEmpty() )
        {
            LOG.debug( "Walking ancestors of {} to find referrals.", dn );
           
            try
            {
                entry = session.getCoreSession().lookup( dn );

                if ( isEntryReferral( entry ) )
                {
                    farthestReferralAncestor = entry;
                }

                dn.remove( dn.size() - 1 );
            }
            catch ( NameNotFoundException e )
            {
                LOG.debug( "Entry for {} not found.", dn );

                // update the DN as we strip last component
                try
                {
                    dn.remove( dn.size() - 1 );
                }
                catch ( InvalidNameException e1 )
                {
                    // never happens
                }
            }
        }
       
        return farthestReferralAncestor;
    }
   
   
    /**
     * Handles processing with referrals without ManageDsaIT control and with
     * an ancestor that is a referral.  The original entry was not found and
     * the walk of the ancestry returned a referral.
     *
     * @param referralAncestor the farthest referral ancestor of the missing
     * entry 
     */
    public InternalReferral getReferralOnAncestor( LdapSession session, DN reqTargetDn, T req,
        ClonedServerEntry referralAncestor ) throws Exception
    {
        LOG.debug( "Inside getReferralOnAncestor()" );
       
        ServerAttribute refAttr = ( ServerAttribute ) referralAncestor.getOriginalEntry()
            .get( SchemaConstants.REF_AT );
        InternalReferral referral = new ReferralImpl();

        for ( Value<?> value : refAttr )
        {
            String ref = value.getString();

            LOG.debug( "Calculating LdapURL for referrence value {}", ref );

            // need to add non-ldap URLs as-is
            if ( ! ref.startsWith( "ldap" ) )
            {
                referral.addLdapUrl( ref );
                continue;
            }
           
            // parse the ref value and normalize the DN 
            LdapURL ldapUrl = new LdapURL();
            try
            {
                ldapUrl.parse( ref.toCharArray() );
            }
            catch ( LdapURLEncodingException e )
            {
                LOG.error( I18n.err( I18n.ERR_165, ref, referralAncestor ) );
            }
           
            DN urlDn = new DN( ldapUrl.getDn().getName() );
            urlDn.normalize( session.getCoreSession().getDirectoryService().getSchemaManager()
                .getNormalizerMapping() );
           
            if ( urlDn.getNormName().equals( referralAncestor.getDn().getNormName() ) )
            {
                // according to the protocol there is no need for the dn since it is the same as this request
                StringBuilder buf = new StringBuilder();
                buf.append( ldapUrl.getScheme() );
                buf.append( ldapUrl.getHost() );

                if ( ldapUrl.getPort() > 0 )
                {
                    buf.append( ":" );
                    buf.append( ldapUrl.getPort() );
                }

                referral.addLdapUrl( buf.toString() );
                continue;
            }
           
            /*
             * If we get here then the DN of the referral was not the same as the
             * DN of the ref LDAP URL.  We must calculate the remaining (difference)
             * name past the farthest referral DN which the target name extends.
             */
            int diff = reqTargetDn.size() - referralAncestor.getDn().size();
            DN extra = new DN();

            // TODO - fix this by access unormalized RDN values
            // seems we have to do this because get returns normalized rdns
            DN reqUnnormalizedDn = new DN( reqTargetDn.getName() );
            for ( int jj = 0; jj < diff; jj++ )
            {
                extra.add( reqUnnormalizedDn.get( referralAncestor.getDn().size() + jj ) );
            }

            urlDn.addAll( extra );

            StringBuilder buf = new StringBuilder();
            buf.append( ldapUrl.getScheme() );
            buf.append( ldapUrl.getHost() );

            if ( ldapUrl.getPort() > 0 )
            {
                buf.append( ":" );
                buf.append( ldapUrl.getPort() );
            }

            buf.append( "/" );
            buf.append( LdapURL.urlEncode( urlDn.getName(), false ) );
            referral.addLdapUrl( buf.toString() );
        }
       
        return referral;
    }
   
   
    /**
     * Handles processing with referrals without ManageDsaIT control and with
     * an ancestor that is a referral.  The original entry was not found and
     * the walk of the ancestry returned a referral.
     *
     * @param referralAncestor the farthest referral ancestor of the missing
     * entry 
     */
    public InternalReferral getReferralOnAncestorForSearch( LdapSession session, InternalSearchRequest req,
        ClonedServerEntry referralAncestor ) throws Exception
    {
        LOG.debug( "Inside getReferralOnAncestor()" );
    
        ServerAttribute refAttr = ( ServerAttribute ) referralAncestor.getOriginalEntry()
            .get( SchemaConstants.REF_AT );
        InternalReferral referral = new ReferralImpl();

        for ( Value<?> value : refAttr )
        {
            String ref = value.getString();

            LOG.debug( "Calculating LdapURL for referrence value {}", ref );

            // need to add non-ldap URLs as-is
            if ( ! ref.startsWith( "ldap" ) )
            {
                referral.addLdapUrl( ref );
                continue;
            }
           
            // Parse the ref value  
            LdapURL ldapUrl = new LdapURL();
            try
            {
                ldapUrl.parse( ref.toCharArray() );
            }
            catch ( LdapURLEncodingException e )
            {
                LOG.error( I18n.err( I18n.ERR_165, ref, referralAncestor ) );
            }
           
            // Normalize the DN to check for same dn
            DN urlDn = new DN( ldapUrl.getDn().getName() );
            urlDn.normalize( session.getCoreSession().getDirectoryService().getSchemaManager()
                .getNormalizerMapping() );
           
            if ( urlDn.getNormName().equals( req.getBase().getNormName() ) )
            {
                ldapUrl.setForceScopeRendering( true );
                ldapUrl.setAttributes( req.getAttributes() );
                ldapUrl.setScope( req.getScope().getScope() );
                referral.addLdapUrl( ldapUrl.toString() );
                continue;
            }
           
            /*
             * If we get here then the DN of the referral was not the same as the
             * DN of the ref LDAP URL.  We must calculate the remaining (difference)
             * name past the farthest referral DN which the target name extends.
             */
            int diff = req.getBase().size() - referralAncestor.getDn().size();
            DN extra = new DN();

            // TODO - fix this by access unormalized RDN values
            // seems we have to do this because get returns normalized rdns
            DN reqUnnormalizedDn = new DN( req.getBase().getName() );
            for ( int jj = 0; jj < diff; jj++ )
            {
                extra.add( reqUnnormalizedDn.get( referralAncestor.getDn().size() + jj ) );
            }

            ldapUrl.getDn().addAll( extra );
            ldapUrl.setForceScopeRendering( true );
            ldapUrl.setAttributes( req.getAttributes() );
            ldapUrl.setScope( req.getScope().getScope() );
            referral.addLdapUrl( ldapUrl.toString() );
        }
       
        return referral;
    }
   
   
    /**
     * Handles processing with referrals without ManageDsaIT control.
     */
    public void handleException( LdapSession session, InternalResultResponseRequest req, Exception e )
    {
        InternalLdapResult result = req.getResultResponse().getLdapResult();

        /*
         * Set the result code or guess the best option.
         */
        ResultCodeEnum code;
       
        if ( e instanceof LdapException )
        {
            code = ( ( LdapException ) e ).getResultCode();
        }
        else
        {
            code = ResultCodeEnum.getBestEstimate( e, req.getType() );
        }
       
        result.setResultCode( code );

        /*
         * Setup the error message to put into the request and put entire
         * exception into the message if we are in debug mode.  Note we
         * embed the result code name into the message.
         */
        String msg = code.toString() + ": failed for " + req + ": " + e.getLocalizedMessage();
        LOG.debug( msg, e );
       
        if ( IS_DEBUG )
        {
            msg += ":\n" + ExceptionUtils.getStackTrace( e );
        }
       
        result.setErrorMessage( msg );

        if ( e instanceof NamingException )
        {
            NamingException ne = ( NamingException ) e;

            // Add the matchedDN if necessary
            boolean setMatchedDn =
                code == ResultCodeEnum.NO_SUCH_OBJECT             ||
                code == ResultCodeEnum.ALIAS_PROBLEM              ||
                code == ResultCodeEnum.INVALID_DN_SYNTAX          ||
                code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM;
           
            if ( ( ne.getResolvedName() != null ) && setMatchedDn )
            {
                result.setMatchedDn( ( DN ) ne.getResolvedName() );
            }
        }

        session.getIoSession().write( req.getResultResponse() );
    }

   
    /**
     * Handles processing without referral handling in effect: either with the
     * ManageDsaIT control or when the entry or all of it's ancestors are non-
     * referral entries.
     *
     * Implementors
     *
     * @param session the LDAP session under which processing occurs
     * @param reqTargetDn the target entry DN associated with the request
     * @param entry the target entry if it exists and has been looked up, may
     * be null even if the entry exists, offered in case the entry is looked
     * up to avoid repeat lookups.  Implementations should check if the entry
     * is null and attempt a lookup instead of presuming the entry does not
     * exist.
     * @param req the request to be handled
     */
    public abstract void handleIgnoringReferrals( LdapSession session, T req );


    /**
     * Handles processing with referrals without ManageDsaIT control.
     */
    public abstract void handleWithReferrals( LdapSession session, DN reqTargetDn, T req ) throws NamingException;
}
TOP

Related Classes of org.apache.directory.server.ldap.handlers.ReferralAwareRequestHandler

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.