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 <elecha...@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 <clp...@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