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 <[email protected]>
> To: [email protected]
> 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 <[email protected]> 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