4 changed files with 574 additions and 0 deletions
@ -0,0 +1,68 @@ |
|||||||
|
package org.acegisecurity.providers.ldap.authenticator.controls; |
||||||
|
|
||||||
|
import javax.naming.ldap.Control; |
||||||
|
|
||||||
|
/** |
||||||
|
* A Password Policy request control. |
||||||
|
* <p> |
||||||
|
* Based on the information in the corresponding internet draft on |
||||||
|
* LDAP password policy. |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @see PasswordPolicyResponseControl |
||||||
|
* @see <a href="http://www.ietf.org/internet-drafts/draft-behera-ldap-password-policy-09.txt">Password Policy for LDAP Directories</a> |
||||||
|
* |
||||||
|
* @author Stefan Zoerner |
||||||
|
* @author Luke Taylor |
||||||
|
* |
||||||
|
* @version $Id$ |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class PasswordPolicyControl implements Control { |
||||||
|
|
||||||
|
/** OID of the Password Policy Control */ |
||||||
|
public static final String OID = "1.3.6.1.4.1.42.2.27.8.5.1"; |
||||||
|
|
||||||
|
private boolean critical; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a non-critical (request) control. |
||||||
|
*/ |
||||||
|
public PasswordPolicyControl() { |
||||||
|
this(Control.NONCRITICAL); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a (request) control. |
||||||
|
* |
||||||
|
* @param critical indicates whether the control is |
||||||
|
* critical for the client |
||||||
|
*/ |
||||||
|
public PasswordPolicyControl(boolean critical) { |
||||||
|
this.critical = critical; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the OID of the Password Policy Control. |
||||||
|
*/ |
||||||
|
public String getID() { |
||||||
|
return OID; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns whether the control is critical for the client. |
||||||
|
*/ |
||||||
|
public boolean isCritical() { |
||||||
|
return critical; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Retrieves the ASN.1 BER encoded value of the LDAP control. The request |
||||||
|
* value for this control is always empty. |
||||||
|
* |
||||||
|
* @return always null |
||||||
|
*/ |
||||||
|
public byte[] getEncodedValue() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,35 @@ |
|||||||
|
package org.acegisecurity.providers.ldap.authenticator.controls; |
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream; |
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import javax.naming.ldap.Control; |
||||||
|
import javax.naming.ldap.ControlFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* Transforms a control object to a PasswordPolicyResponseControl object, if |
||||||
|
* appropriate. |
||||||
|
* |
||||||
|
* @author Stefan Zoerner |
||||||
|
* @author Luke Taylor |
||||||
|
* @version $Id$ |
||||||
|
*/ |
||||||
|
public class PasswordPolicyControlFactory extends ControlFactory { |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an instance of PasswordPolicyResponseControl if the passed |
||||||
|
* control is a response control of this type. Attributes of the result are |
||||||
|
* filled with the correct values (e.g. error code). |
||||||
|
* |
||||||
|
* @param ctl the control the check |
||||||
|
* @return a response control of type PasswordPolicyResponseControl, or null |
||||||
|
*/ |
||||||
|
public Control getControlInstance(Control ctl) { |
||||||
|
|
||||||
|
if (ctl.getID().equals(PasswordPolicyControl.OID)) { |
||||||
|
return new PasswordPolicyResponseControl(ctl.getEncodedValue()); |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,358 @@ |
|||||||
|
package org.acegisecurity.providers.ldap.authenticator.controls; |
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.ByteArrayInputStream; |
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
import org.acegisecurity.providers.ldap.LdapDataAccessException; |
||||||
|
import org.apache.commons.logging.Log; |
||||||
|
import org.apache.commons.logging.LogFactory; |
||||||
|
|
||||||
|
//import com.novell.ldap.asn1.LBERDecoder;
|
||||||
|
//import com.novell.ldap.asn1.ASN1Sequence;
|
||||||
|
//import com.novell.ldap.asn1.ASN1Tagged;
|
||||||
|
//import com.novell.ldap.asn1.ASN1OctetString;
|
||||||
|
import netscape.ldap.ber.stream.BERSequence; |
||||||
|
import netscape.ldap.ber.stream.BERElement; |
||||||
|
import netscape.ldap.ber.stream.BERTagDecoder; |
||||||
|
import netscape.ldap.ber.stream.BERTag; |
||||||
|
import netscape.ldap.ber.stream.BERChoice; |
||||||
|
import netscape.ldap.ber.stream.BERInteger; |
||||||
|
import netscape.ldap.ber.stream.BEREnumerated; |
||||||
|
|
||||||
|
/** |
||||||
|
* Represent the response control received when a <tt>PasswordPolicyControl</tt> |
||||||
|
* is used when binding to a directory. |
||||||
|
* |
||||||
|
* Currently tested with the OpenLDAP 2.3.19 implementation of the LDAP Password |
||||||
|
* Policy Draft. |
||||||
|
* |
||||||
|
* It extends the request control with the control specific data. This is |
||||||
|
* accomplished by the properties timeBeforeExpiration, graceLoginsRemaining and |
||||||
|
* errorCodes. getEncodedValue returns the |
||||||
|
* unchanged value of the response control as a byte array. |
||||||
|
* |
||||||
|
* @see PasswordPolicyControl |
||||||
|
* @see <a href="http://www.ibm.com/developerworks/tivoli/library/t-ldap-controls/">Stefan Zoerner's IBM developerworks article on LDAP controls.</a> |
||||||
|
* |
||||||
|
* @author Stefan Zoerner |
||||||
|
* @author Luke Taylor |
||||||
|
* @version $Id$ |
||||||
|
*/ |
||||||
|
public class PasswordPolicyResponseControl extends PasswordPolicyControl { |
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(PasswordPolicyResponseControl.class); |
||||||
|
|
||||||
|
public static final int ERROR_NONE = -1; |
||||||
|
|
||||||
|
public static final int ERROR_PASSWORD_EXPIRED = 0; |
||||||
|
public static final int ERROR_ACCOUNT_LOCKED = 1; |
||||||
|
|
||||||
|
public static final int WARNINGS_DEFAULT = -1; |
||||||
|
|
||||||
|
private byte[] encodedValue; |
||||||
|
|
||||||
|
private int errorCode = ERROR_NONE; |
||||||
|
|
||||||
|
private int timeBeforeExpiration = WARNINGS_DEFAULT; |
||||||
|
|
||||||
|
private int graceLoginsRemaining = WARNINGS_DEFAULT; |
||||||
|
|
||||||
|
private static final String[] errorText = { "password expired", "account locked", |
||||||
|
"change after reset", "password mod not allowed", "must supply old password", |
||||||
|
"invalid password syntax", "password too short", "password too young", |
||||||
|
"password in history" }; |
||||||
|
|
||||||
|
public PasswordPolicyResponseControl(byte[] encodedValue) { |
||||||
|
this.encodedValue = encodedValue; |
||||||
|
|
||||||
|
//PPolicyDecoder decoder = new JLdapDecoder();
|
||||||
|
PPolicyDecoder decoder = new NetscapeDecoder(); |
||||||
|
|
||||||
|
try { |
||||||
|
decoder.decode(); |
||||||
|
} catch (IOException e) { |
||||||
|
throw new LdapDataAccessException("Failed to parse control value", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Decodes the Ber encoded control data. |
||||||
|
* |
||||||
|
* The ASN.1 value of the control data is: |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* PasswordPolicyResponseValue ::= SEQUENCE { |
||||||
|
* warning [0] CHOICE { |
||||||
|
* timeBeforeExpiration [0] INTEGER (0 .. maxInt), |
||||||
|
* graceAuthNsRemaining [1] INTEGER (0 .. maxInt) } OPTIONAL, |
||||||
|
* error [1] ENUMERATED { |
||||||
|
* passwordExpired (0), |
||||||
|
* accountLocked (1), |
||||||
|
* changeAfterReset (2), |
||||||
|
* passwordModNotAllowed (3), |
||||||
|
* mustSupplyOldPassword (4), |
||||||
|
* insufficientPasswordQuality (5), |
||||||
|
* passwordTooShort (6), |
||||||
|
* passwordTooYoung (7), |
||||||
|
* passwordInHistory (8) } OPTIONAL } |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the graceLoginsRemaining. |
||||||
|
* |
||||||
|
* @return Returns the graceLoginsRemaining. |
||||||
|
*/ |
||||||
|
public int getGraceLoginsRemaining() { |
||||||
|
return graceLoginsRemaining; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the timeBeforeExpiration. |
||||||
|
* |
||||||
|
* @return Returns the time before expiration in seconds |
||||||
|
*/ |
||||||
|
public int getTimeBeforeExpiration() { |
||||||
|
return timeBeforeExpiration; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the unchanged value of the response control. |
||||||
|
* |
||||||
|
* Returns the unchanged value of the response control as byte array. |
||||||
|
*/ |
||||||
|
public byte[] getEncodedValue() { |
||||||
|
return encodedValue; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the error code, or ERROR_NONE, if no error is present. |
||||||
|
* |
||||||
|
* @return the error code (0-8), or ERROR_NONE |
||||||
|
*/ |
||||||
|
public int getErrorCode() { |
||||||
|
return errorCode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks whether an error is present. |
||||||
|
* |
||||||
|
* @return true, if an error is present |
||||||
|
*/ |
||||||
|
public boolean hasError() { |
||||||
|
return this.getErrorCode() != ERROR_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks whether a warning is present. |
||||||
|
* |
||||||
|
* @return true, if a warning is present |
||||||
|
*/ |
||||||
|
public boolean hasWarning() { |
||||||
|
return graceLoginsRemaining != WARNINGS_DEFAULT |
||||||
|
|| timeBeforeExpiration != WARNINGS_DEFAULT; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isExpired() { |
||||||
|
return errorCode == ERROR_PASSWORD_EXPIRED; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Determines whether an account locked error has |
||||||
|
* been returned. |
||||||
|
* |
||||||
|
* @return true if the account is locked. |
||||||
|
*/ |
||||||
|
public boolean isLocked() { |
||||||
|
return errorCode == ERROR_ACCOUNT_LOCKED; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a textual representation containing error and warning messages, if |
||||||
|
* any are present. |
||||||
|
* |
||||||
|
* @return error and warning messages |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
|
||||||
|
StringBuffer sb = new StringBuffer("PasswordPolicyResponseControl"); |
||||||
|
|
||||||
|
if (hasError()) { |
||||||
|
sb.append(", error: ").append(errorText[errorCode]); |
||||||
|
} |
||||||
|
if (graceLoginsRemaining != WARNINGS_DEFAULT) { |
||||||
|
sb.append(", warning: ").append(graceLoginsRemaining).append(" grace logins remain"); |
||||||
|
} |
||||||
|
if (timeBeforeExpiration != WARNINGS_DEFAULT) { |
||||||
|
sb.append(", warning: time before expiration is ").append(timeBeforeExpiration); |
||||||
|
} |
||||||
|
if (!hasError() && !hasWarning()) { |
||||||
|
sb.append(" (no error, no warning)"); |
||||||
|
} |
||||||
|
|
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
//~ Inner Classes =========================================================
|
||||||
|
|
||||||
|
private interface PPolicyDecoder { |
||||||
|
void decode() throws IOException; |
||||||
|
} |
||||||
|
|
||||||
|
/** Decoder based on Netscape ldapsdk library */ |
||||||
|
private class NetscapeDecoder implements PPolicyDecoder { |
||||||
|
|
||||||
|
public void decode() throws IOException { |
||||||
|
int[] bread = { 0 }; |
||||||
|
BERSequence seq = (BERSequence) BERElement.getElement( |
||||||
|
new SpecificTagDecoder(), new ByteArrayInputStream(encodedValue), bread); |
||||||
|
|
||||||
|
int size = seq.size(); |
||||||
|
|
||||||
|
if(logger.isDebugEnabled()) { |
||||||
|
logger.debug("PasswordPolicyResponse, ASN.1 sequence has " + |
||||||
|
size + " elements"); |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < seq.size(); i++) { |
||||||
|
BERTag elt = (BERTag)seq.elementAt(i); |
||||||
|
|
||||||
|
int tag = elt.getTag() & 0x1F; |
||||||
|
|
||||||
|
if(tag == 0) { |
||||||
|
BERChoice warning = (BERChoice)elt.getValue(); |
||||||
|
|
||||||
|
BERTag content = (BERTag)warning.getValue(); |
||||||
|
int value = ((BERInteger)content.getValue()).getValue(); |
||||||
|
|
||||||
|
if((content.getTag() & 0x1F) == 0) { |
||||||
|
timeBeforeExpiration = value; |
||||||
|
} else { |
||||||
|
graceLoginsRemaining = value; |
||||||
|
} |
||||||
|
} else if(tag == 1) { |
||||||
|
BEREnumerated error = (BEREnumerated)elt.getValue(); |
||||||
|
errorCode = error.getValue(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
class SpecificTagDecoder extends BERTagDecoder { |
||||||
|
/** Allows us to remember which of the two options we're decoding */ |
||||||
|
private Boolean inChoice = null; |
||||||
|
|
||||||
|
public BERElement getElement(BERTagDecoder decoder, int tag, InputStream stream, |
||||||
|
int[] bytesRead, boolean[] implicit) throws IOException { |
||||||
|
|
||||||
|
tag &= 0x1F; |
||||||
|
implicit[0] = false; |
||||||
|
|
||||||
|
if(tag == 0) { |
||||||
|
// Either the choice or the time before expiry within it
|
||||||
|
if(inChoice == null) { |
||||||
|
setInChoice(true); |
||||||
|
|
||||||
|
// Read the choice length from the stream (ignored)
|
||||||
|
BERElement.readLengthOctets(stream, bytesRead); |
||||||
|
|
||||||
|
int[] componentLength = new int[1]; |
||||||
|
BERElement choice = new BERChoice(decoder, stream, componentLength); |
||||||
|
bytesRead[0] += componentLength[0]; |
||||||
|
|
||||||
|
// inChoice = null;
|
||||||
|
return choice; |
||||||
|
} else { |
||||||
|
// Must be time before expiry
|
||||||
|
return new BERInteger(stream, bytesRead); |
||||||
|
} |
||||||
|
} else if(tag == 1) { |
||||||
|
// Either the graceLogins or the error enumeration.
|
||||||
|
if(inChoice == null) { |
||||||
|
// The enumeration
|
||||||
|
setInChoice(false); |
||||||
|
|
||||||
|
return new BEREnumerated(stream, bytesRead); |
||||||
|
|
||||||
|
} else { |
||||||
|
if(inChoice.booleanValue()) { |
||||||
|
// graceLogins
|
||||||
|
return new BERInteger(stream, bytesRead); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
throw new LdapDataAccessException("Unexpected tag " + tag); |
||||||
|
} |
||||||
|
|
||||||
|
private void setInChoice(boolean inChoice) { |
||||||
|
this.inChoice = new Boolean(inChoice); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Decoder based on the OpenLDAP/Novell JLDAP library */ |
||||||
|
// private class JLdapDecoder implements PPolicyDecoder {
|
||||||
|
//
|
||||||
|
// public void decode() throws IOException {
|
||||||
|
//
|
||||||
|
// LBERDecoder decoder = new LBERDecoder();
|
||||||
|
//
|
||||||
|
// ASN1Sequence seq = (ASN1Sequence)decoder.decode(encodedValue);
|
||||||
|
//
|
||||||
|
// if(seq == null) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// int size = seq.size();
|
||||||
|
//
|
||||||
|
// if(logger.isDebugEnabled()) {
|
||||||
|
// logger.debug("PasswordPolicyResponse, ASN.1 sequence has " +
|
||||||
|
// size + " elements");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for(int i=0; i < size; i++) {
|
||||||
|
//
|
||||||
|
// ASN1Tagged taggedObject = (ASN1Tagged)seq.get(i);
|
||||||
|
//
|
||||||
|
// int tag = taggedObject.getIdentifier().getTag();
|
||||||
|
//
|
||||||
|
// ASN1OctetString value = (ASN1OctetString)taggedObject.taggedValue();
|
||||||
|
// byte[] content = value.byteValue();
|
||||||
|
//
|
||||||
|
// if(tag == 0) {
|
||||||
|
// parseWarning(content, decoder);
|
||||||
|
//
|
||||||
|
// } else if(tag == 1) {
|
||||||
|
// // Error: set the code to the value
|
||||||
|
// errorCode = content[0];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void parseWarning(byte[] content, LBERDecoder decoder) {
|
||||||
|
// // It's the warning (choice). Parse the number and set either the
|
||||||
|
// // expiry time or number of logins remaining.
|
||||||
|
// ASN1Tagged taggedObject = (ASN1Tagged)decoder.decode(content);
|
||||||
|
// int contentTag = taggedObject.getIdentifier().getTag();
|
||||||
|
// content = ((ASN1OctetString)taggedObject.taggedValue()).byteValue();
|
||||||
|
// int number;
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// number = ((Long)decoder.decodeNumeric(new ByteArrayInputStream(content), content.length)).intValue();
|
||||||
|
// } catch(IOException e) {
|
||||||
|
// throw new LdapDataAccessException("Failed to parse number ", e);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if(contentTag == 0) {
|
||||||
|
// timeBeforeExpiration = number;
|
||||||
|
// } else if (contentTag == 1) {
|
||||||
|
// graceLoginsRemaining = number;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} |
||||||
@ -0,0 +1,113 @@ |
|||||||
|
package org.acegisecurity.providers.ldap.authenticator.controls; |
||||||
|
|
||||||
|
import junit.framework.TestCase; |
||||||
|
|
||||||
|
import javax.naming.Context; |
||||||
|
import javax.naming.NamingException; |
||||||
|
import javax.naming.ldap.Control; |
||||||
|
import javax.naming.ldap.InitialLdapContext; |
||||||
|
import javax.naming.ldap.LdapContext; |
||||||
|
import java.util.Hashtable; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for <tt>PasswordPolicyResponse</tt>. |
||||||
|
* |
||||||
|
* @author Luke Taylor |
||||||
|
* @version $Id$ |
||||||
|
*/ |
||||||
|
public class PasswordPolicyResponseControlTests extends TestCase { |
||||||
|
|
||||||
|
/** Useful method for obtaining data from a server for use in tests */ |
||||||
|
// public void testAgainstServer() throws Exception {
|
||||||
|
// Hashtable env = new Hashtable();
|
||||||
|
// env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
||||||
|
// env.put(Context.PROVIDER_URL, "ldap://gorille:389/");
|
||||||
|
// env.put(Context.SECURITY_AUTHENTICATION, "simple");
|
||||||
|
// env.put(Context.SECURITY_PRINCIPAL, "cn=manager,dc=acegisecurity,dc=org");
|
||||||
|
// env.put(Context.SECURITY_CREDENTIALS, "acegisecurity");
|
||||||
|
//
|
||||||
|
// InitialLdapContext ctx = new InitialLdapContext(env, null);
|
||||||
|
//
|
||||||
|
// Control[] rctls = { new PasswordPolicyControl(false) };
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// ctx.addToEnvironment(LdapContext.CONTROL_FACTORIES, PasswordPolicyControlFactory.class.getName());
|
||||||
|
// ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, "uid=bob,ou=people,dc=acegisecurity,dc=org" );
|
||||||
|
// ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, "bobspassword");
|
||||||
|
// ctx.reconnect(rctls);
|
||||||
|
// } catch(NamingException ne) {
|
||||||
|
// // Ok.
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// PasswordPolicyResponseControl ctrl = getPPolicyResponseCtl(ctx);
|
||||||
|
// System.out.println(ctrl);
|
||||||
|
//
|
||||||
|
// assertNotNull(ctrl);
|
||||||
|
//
|
||||||
|
// //com.sun.jndi.ldap.LdapPoolManager.showStats(System.out);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private PasswordPolicyResponseControl getPPolicyResponseCtl(InitialLdapContext ctx) throws NamingException {
|
||||||
|
// Control[] ctrls = ctx.getResponseControls();
|
||||||
|
//
|
||||||
|
// for (int i = 0; ctrls != null && i < ctrls.length; i++) {
|
||||||
|
// if (ctrls[i] instanceof PasswordPolicyResponseControl) {
|
||||||
|
// return (PasswordPolicyResponseControl) ctrls[i];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
public void testOpenLDAP33SecondsTillPasswordExpiryCtrlIsParsedCorrectly() { |
||||||
|
byte[] ctrlBytes = {0x30, 0x05, (byte)0xA0, 0x03, (byte)0xA0, 0x1, 0x21}; |
||||||
|
|
||||||
|
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes); |
||||||
|
|
||||||
|
assertTrue(ctrl.hasWarning()); |
||||||
|
assertEquals(33, ctrl.getTimeBeforeExpiration()); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void testOpenLDAPPasswordExpiredCtrlIsParsedCorrectly() { |
||||||
|
byte[] ctrlBytes = {0x30, 0x03, (byte)0xA1, 0x01, 0x00}; |
||||||
|
|
||||||
|
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes); |
||||||
|
|
||||||
|
assertTrue(ctrl.hasError() && ctrl.isExpired()); |
||||||
|
assertFalse(ctrl.hasWarning()); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void testOpenLDAPAccountLockedCtrlIsParsedCorrectly() { |
||||||
|
byte[] ctrlBytes = {0x30, 0x03, (byte)0xA1, 0x01, 0x01}; |
||||||
|
|
||||||
|
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes); |
||||||
|
|
||||||
|
assertTrue(ctrl.hasError() && ctrl.isLocked()); |
||||||
|
assertFalse(ctrl.hasWarning()); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void testOpenLDAP5GraceLoginsRemainingCtrlIsParsedCorrectly() { |
||||||
|
byte[] ctrlBytes = {0x30, 0x05, (byte)0xA0, 0x03, (byte)0xA1, 0x01, 0x05}; |
||||||
|
|
||||||
|
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes); |
||||||
|
|
||||||
|
assertTrue(ctrl.hasWarning()); |
||||||
|
assertEquals(5, ctrl.getGraceLoginsRemaining()); |
||||||
|
} |
||||||
|
|
||||||
|
public void testOpenLDAP496GraceLoginsRemainingCtrlIsParsedCorrectly() { |
||||||
|
byte[] ctrlBytes = {0x30, 0x06, (byte)0xA0, 0x04, (byte)0xA1, 0x02, 0x01, (byte)0xF0}; |
||||||
|
|
||||||
|
PasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes); |
||||||
|
|
||||||
|
assertTrue(ctrl.hasWarning()); |
||||||
|
assertEquals(496, ctrl.getGraceLoginsRemaining()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue