directory-api mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Emmanuel Lécharny <elecha...@gmail.com>
Subject Re: Ldap API Custom Controls
Date Sun, 03 Sep 2017 21:54:12 GMT


Le 03/09/2017 à 20:57, Chris Pike a écrit :
> Trying to get Active Directory to honor password history when changing a password. 
>
> https://blogs.technet.microsoft.com/fieldcoding/2013/01/09/resetting-passwords-honoring-password-history-or-whats-happening-under-the-hood-when-changing-resetting-passwords/

Ok, the control is :

     PolicyHintsRequestValue ::= SEQUENCE {
         Flags    INTEGER
     }


You need many elements :

- An interface in the api-ldap-extra-codec-api module which exposes the
'flags' (a getter and setter is enough). Something like :

import org.apache.directory.api.ldap.model.message.Control;

public interface LdapServerPolicyHintsOid extends Control
{
    /** This control OID */
    String OID = "1.2.840.113556.1.4.20669";

    int getFlags();
   
    void setFlags( int flags );
}

- An implementation of this interface in the same package. Something like :


import org.apache.directory.api.ldap.model.message.controls.AbstractControl;

public class LdapServerPolicyHintsOidImpl extends AbstractControl
implements LdapServerPolicyHintsOid
{
    /** This control OID */
    private int flags;

    public LdapServerPolicyHintsOidImpl()
    {
        super( OID );
    }

    public int getFlags()
    {
        return flags;
    }
   
    public void setFlags( int flags )
    {
        this.flags = flags;
    }
}


- A decorator in the api-ldap-extras-codec module :


import org.apache.directory.api.asn1.Asn1Object;
import org.apache.directory.api.asn1.DecoderException;
import org.apache.directory.api.asn1.EncoderException;
import org.apache.directory.api.asn1.ber.Asn1Decoder;
import org.apache.directory.api.asn1.ber.tlv.BerValue;
import org.apache.directory.api.asn1.ber.tlv.TLV;
import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
import org.apache.directory.api.i18n.I18n;
import org.apache.directory.api.ldap.codec.api.ControlDecorator;
import org.apache.directory.api.ldap.codec.api.LdapApiService;


/**
 * The LdapServerPolicyHintsOid decorator
 *
 */
public class LdapServerPolicyHintsOidDecorator extends
ControlDecorator<LdapServerPolicyHintsOid> implements
LdapServerPolicyHintsOid
{
    private int seqLength;

    private static final Asn1Decoder DECODER = new Asn1Decoder();


    /**
     * Creates a new instance of LdapServerPolicyHintsOidDecorator.
     *
     * @param codec The LDAP Service to use
     */
    public LdapServerPolicyHintsOidDecorator( LdapApiService codec )
    {
        this( codec, new LdapServerPolicyHintsOidImpl() );
    }


    /**
     * Creates a new instance of LdapServerPolicyHintsOidDecorator.
     *
     * @param codec The LDAP Service to use
     * @param vlvRequest The VLV request to use
     */
    public LdapServerPolicyHintsOidDecorator( LdapApiService codec,
LdapServerPolicyHintsOid vlvRequest )
    {
        super( codec, vlvRequest );
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public int computeLength()
    {
        seqLength = 1 + 1 + BerValue.getNbBytes( getFlags() );

        valueLength = 1 + TLV.getNbBytes( seqLength ) + seqLength;

        return valueLength;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
    {
        if ( buffer == null )
        {
            throw new EncoderException( I18n.err( I18n.ERR_04023 ) );
        }

        buffer.put( UniversalTag.SEQUENCE.getValue() );
        buffer.put( TLV.getBytes( seqLength ) );

        BerValue.encode( buffer, getFlags() );

        return buffer;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public byte[] getValue()
    {
        if ( value == null )
        {
            try
            {
                computeLength();
                ByteBuffer buffer = ByteBuffer.allocate( valueLength );

                value = encode( buffer ).array();
            }
            catch ( Exception e )
            {
                return null;
            }
        }

        return value;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public Asn1Object decode( byte[] controlBytes ) throws DecoderException
    {
        ByteBuffer buffer = ByteBuffer.wrap( controlBytes );
        LdapServerPolicyHintsOidContainer container = new
LdapServerPolicyHintsOidContainer( this, getCodecService() );
        DECODER.decode( buffer, container );
       
        return this;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public int getFlags()
    {
        return getDecorated().getFlags();
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public void setFlags( int flags )
    {
        getDecorated().setFlags( flags );
    }
}


- A container in the api-ldap-extras-codec module :


import org.apache.directory.api.asn1.ber.AbstractContainer;
import org.apache.directory.api.ldap.codec.api.LdapApiService;


/**
 * A container for the LdapServerPolicyHintsOid request control.
 */
public class LdapServerPolicyHintsOidContainer extends AbstractContainer
{
    private LdapServerPolicyHintsOidDecorator control;

    private LdapApiService codec;


    /**
     * Creates a new LdapServerPolicyHintsOidContainer instance
     *
     * @param codec The LDAP Service to use
     */
    public LdapServerPolicyHintsOidContainer( LdapApiService codec )
    {
        super();
        this.codec = codec;
        setGrammar( LdapServerPolicyHintsOidGrammar.getInstance() );
        setTransition( LdapServerPolicyHintsOidStates.START_STATE );
    }


    /**
     * Creates a new VirtualListViewRequestContainer instance
     *
     * @param control The VLV control
     * @param codec The LDAP Service to use
     */
    public LdapServerPolicyHintsOidContainer(
LdapServerPolicyHintsOidDecorator control, LdapApiService codec )
    {
        this( codec );
        decorate( control );
    }


    /**
     * @return The LdapServerPolicyHintsOid control
     */
    public LdapServerPolicyHintsOidDecorator getDecorator()
    {
        return control;
    }


    /**
     * Decorate the LdapServerPolicyHintsOid control
     *
     * @param control The control to decorate
     */
    public void decorate( LdapServerPolicyHintsOid control )
    {
        if ( control instanceof LdapServerPolicyHintsOidDecorator )
        {
            this.control = ( LdapServerPolicyHintsOidDecorator ) control;
        }
        else
        {
            this.control = new LdapServerPolicyHintsOidDecorator( codec,
control );
        }
    }


    /**
     * Sets the LdapServerPolicyHintsOid control
     *
     * @param control The LdapServerPolicyHintsOid control
     */
    public void setLdapServerPolicyHintsOidRequestControl(
LdapServerPolicyHintsOidDecorator control )
    {
        this.control = control;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public void clean()
    {
        super.clean();
        control = null;
    }
}

- A factory in the api-ldap-extras-codec module :

import org.apache.directory.api.ldap.codec.api.CodecControl;
import org.apache.directory.api.ldap.codec.api.ControlFactory;
import org.apache.directory.api.ldap.codec.api.LdapApiService;


/**
 * A {@link ControlFactory} for {@link LdapServerPolicyHintsOid} controls.
 *
 */
public class LdapServerPolicyHintsOidFactory implements
ControlFactory<LdapServerPolicyHintsOid>
{
    private LdapApiService codec;


    /**
     * Creates a new instance of LdapServerPolicyHintsOidFactory.
     *
     * @param codec The codec for this factory.
     */
    public LdapServerPolicyHintsOidFactory( LdapApiService codec )
    {
        this.codec = codec;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public String getOid()
    {
        return LdapServerPolicyHintsOid.OID;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public CodecControl<LdapServerPolicyHintsOid> newCodecControl()
    {
        return new LdapServerPolicyHintsOidDecorator( codec );
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public CodecControl<LdapServerPolicyHintsOid> newCodecControl(
LdapServerPolicyHintsOid control )
    {
        return new LdapServerPolicyHintsOidDecorator( codec, control );
    }
}

- A States class containing the grammar transition constants, used by
the grammar :


/**
 * This class store the LdapServerPolicyHintsOid grammar constants. It
is also used for
 * debugging purposes.
 *
 */
public enum LdapServerPolicyHintsOidStates implements States
{
    /** Initial state */
    START_STATE,
   
    /** LdapServerPolicyRequestValue ::= SEQUENCE  transition */
    LSPHO_SEQUENCE_STATE,
   
    /** flags    INTEGER transition */
    LSPHO_FLAGS_STATE,
   
    /** Final state */
    END_STATE;

    /**
     * Get the grammar name
     *
     * @return The grammar name
     */
    public String getGrammarName()
    {
        return "LSPHO_GRAMMAR";
    }


    /**
     * Get the string representing the state
     *
     * @param state The state number
     * @return The String representing the state
     */
    public String getState( int state )
    {
        return ( state == END_STATE.ordinal() ) ? "LSPHO_END_STATE" :
name();
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isEndState()
    {
        return this == END_STATE;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public Enum<?> getStartState()
    {
        return START_STATE;
    }
}

- The grammar itself :


import org.apache.directory.api.asn1.ber.grammar.AbstractGrammar;
import org.apache.directory.api.asn1.ber.grammar.Grammar;
import org.apache.directory.api.asn1.ber.grammar.GrammarTransition;
import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * The LdapServerPolicyHintsOid grammar
 *
 */
public final class LdapServerPolicyHintsOidGrammar extends
AbstractGrammar<LdapServerPolicyHintsOidContainer>
{
    static final Logger LOG = LoggerFactory.getLogger(
LdapServerPolicyHintsOidGrammar.class );

    static final boolean IS_DEBUG = LOG.isDebugEnabled();

    private static Grammar<?> instance = new
LdapServerPolicyHintsOidGrammar();


    @SuppressWarnings("unchecked")
    private LdapServerPolicyHintsOidGrammar()
    {
        setName( LdapServerPolicyHintsOidGrammar.class.getName() );

        super.transitions = new
GrammarTransition[LdapServerPolicyHintsOidStates.END_STATE.ordinal()][256];

       
super.transitions[LdapServerPolicyHintsOidStates.START_STATE.ordinal()][UniversalTag.SEQUENCE.getValue()]
=
            new GrammarTransition<LdapServerPolicyHintsOidContainer>(
                LdapServerPolicyHintsOidStates.START_STATE,
                LdapServerPolicyHintsOidStates.LSPHO_SEQUENCE_STATE,
                UniversalTag.SEQUENCE.getValue(),
                null );

       
super.transitions[LdapServerPolicyHintsOidStates.LSPHO_SEQUENCE_STATE.ordinal()][UniversalTag.INTEGER.getValue()]
=
            new GrammarTransition<LdapServerPolicyHintsOidContainer>(
                LdapServerPolicyHintsOidStates.LSPHO_SEQUENCE_STATE,
                LdapServerPolicyHintsOidStates.LSPHO_FLAGS_STATE,
                UniversalTag.INTEGER.getValue(),
                new StoreFlags() );
    }


    /**
     * @return the singleton instance of the LdapServerPolicyHintsOidGrammar
     */
    public static Grammar<?> getInstance()
    {
        return instance;
    }
}


- And the action used in the grammar to feed the Flags :


import org.apache.directory.api.asn1.actions.AbstractReadInteger;


/**
 * The action used to store the Flags value
 *
 */
public class StoreFlags extends
AbstractReadInteger<LdapServerPolicyHintsOidContainer>
{

    /**
     * Instantiates a new Flags action.
     */
    public StoreFlags()
    {
        super( "LdapServerPolicyHintsOid Flags" );
    }


    /**
     * {@inheritDoc}
     */
    @Override
    protected void setIntegerValue( int value,
LdapServerPolicyHintsOidContainer lsphoContainer )
    {
        lsphoContainer.getDecorator().setFlags( value );
    }
}


That's all for the code, but you also eed to declare the new control in
the bundle or in the standalone API :


- in ExtrasBundleActivator :

        private void registerExtrasControls( LdapApiService codec )
        {
            ...


            ControlFactory<LdapServerPolicyHintsOid>
ldapServerPolicyHintsOidFactory = new LdapServerPolicyHintsOidFactory(
                codec );
            codec.registerControl( ldapServerPolicyHintsOidFactory );

        }


and to deregister it :

        private void unregisterExtrasControls( LdapApiService codec )
        {

            ...

           codec.unregisterControl( LdapServerPolicyHintsOid.OID );

        }


- in CodecFactoryUtil :


    public static void loadStockControls( Map<String, ControlFactory<?>>
controlFactories, LdapApiService apiService )
    {
        ...

       
        ControlFactory<LdapServerPolicyHintsOid>
ldapServerPolicyHintsOidFactory = new LdapServerPolicyHintsOidFactory(
            apiService );
        controlFactories.put( ldapServerPolicyHintsOidFactory.getOid(),
ldapServerPolicyHintsOidFactory );
        LOG.info( "Registered pre-bundled control factory: {}",
ldapServerPolicyHintsOidFactory.getOid() );
    }


Ideally speaking, some unit test would be good to have, but I leave you
that as an exercise :-)


All this code is taken from the VLV request control, modifed to fit your
control. I think it should work pretty much pristine, typoes put aside.


Just let me know if it's fine for you, then we can push it in the API.



>
> ----- Original Message -----
> From: Emmanuel Lecharny <elecharny@apache.org>
> To: api@directory.apache.org
> Sent: Sun, 03 Sep 2017 14:38:26 -0400 (EDT)
> Subject: Re: Ldap API Custom Controls
>
> It's a bit tricky...
>
> What control do you want to implement? Do you have a description ?
>
> Le dim. 3 sept. 2017 à 15:58, Chris Pike <clp207@psu.edu> a écrit :
>
>> Hi,
>>
>> I am trying to add a custom control. I started by creating a class that
>> implements "org.apache.directory.api.ldap.model.message.Control" and
>> passing an instance into my request. This didn't seem to work, I'm guessing
>> because the value for the control is not passed.
>>
>> When looking at some of the other controls, I found a bunch of Decorator
>> and Factory classes in another package. Do I need to implement those types
>> of classes as well? If so, how do I register them? Is there a full example
>> of creating a custom control somewhere?
>>
>> Thanks for any help you can provide.
>>
>> ~Chris Pike
>>

-- 
Emmanuel Lecharny

Symas.com
directory.apache.org


Mime
View raw message