001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.extensions;
028    
029    
030    
031    import java.util.ArrayList;
032    import java.util.LinkedHashSet;
033    import java.util.List;
034    
035    import org.opends.messages.Message;
036    import org.opends.server.admin.std.server.
037                PasswordPolicyStateExtendedOperationHandlerCfg;
038    import org.opends.server.api.ClientConnection;
039    import org.opends.server.api.ExtendedOperationHandler;
040    import org.opends.server.config.ConfigException;
041    import org.opends.server.core.DirectoryServer;
042    import org.opends.server.core.ExtendedOperation;
043    import org.opends.server.core.ModifyOperation;
044    import org.opends.server.core.PasswordPolicy;
045    import org.opends.server.core.PasswordPolicyState;
046    import org.opends.server.loggers.debug.DebugTracer;
047    import org.opends.server.protocols.asn1.ASN1Element;
048    import org.opends.server.protocols.asn1.ASN1Enumerated;
049    import org.opends.server.protocols.asn1.ASN1OctetString;
050    import org.opends.server.protocols.asn1.ASN1Sequence;
051    import org.opends.server.protocols.internal.InternalClientConnection;
052    import org.opends.server.protocols.internal.InternalSearchOperation;
053    import org.opends.server.schema.GeneralizedTimeSyntax;
054    import org.opends.server.types.DebugLogLevel;
055    import org.opends.server.types.DereferencePolicy;
056    import org.opends.server.types.DirectoryException;
057    import org.opends.server.types.DN;
058    import org.opends.server.types.Entry;
059    import org.opends.server.types.InitializationException;
060    import org.opends.server.types.Modification;
061    import org.opends.server.types.Privilege;
062    import org.opends.server.types.ResultCode;
063    import org.opends.server.types.SearchFilter;
064    import org.opends.server.types.SearchResultEntry;
065    import org.opends.server.types.SearchScope;
066    
067    import static org.opends.messages.ExtensionMessages.*;
068    import static org.opends.server.loggers.debug.DebugLogger.*;
069    import static org.opends.server.util.ServerConstants.*;
070    import static org.opends.server.util.StaticUtils.*;
071    
072    
073    
074    /**
075     * This class implements an LDAP extended operation that can be used to query
076     * and update elements of the Directory Server password policy state for a given
077     * user.  The ASN.1 definition for the value of the extended request is:
078     * <BR>
079     * <PRE>
080     * PasswordPolicyStateValue ::= SEQUENCE {
081     *      targetUser     LDAPDN
082     *      operations     SEQUENCE OF PasswordPolicyStateOperation OPTIONAL }
083     *
084     * PasswordPolicyStateOperation ::= SEQUENCE {
085     *      opType       ENUMERATED {
086     *           getPasswordPolicyDN                          (0),
087     *           getAccountDisabledState                      (1),
088     *           setAccountDisabledState                      (2),
089     *           clearAccountDisabledState                    (3),
090     *           getAccountExpirationTime                     (4),
091     *           setAccountExpirationTime                     (5),
092     *           clearAccountExpirationTime                   (6),
093     *           getSecondsUntilAccountExpiration             (7),
094     *           getPasswordChangedTime                       (8),
095     *           setPasswordChangedTime                       (9),
096     *           clearPasswordChangedTime                     (10),
097     *           getPasswordExpirationWarnedTime              (11),
098     *           setPasswordExpirationWarnedTime              (12),
099     *           clearPasswordExpirationWarnedTime            (13),
100     *           getSecondsUntilPasswordExpiration            (14),
101     *           getSecondsUntilPasswordExpirationWarning     (15),
102     *           getAuthenticationFailureTimes                (16),
103     *           addAuthenticationFailureTime                 (17),
104     *           setAuthenticationFailureTimes                (18),
105     *           clearAuthenticationFailureTimes              (19),
106     *           getSecondsUntilAuthenticationFailureUnlock   (20),
107     *           getRemainingAuthenticationFailureCount       (21),
108     *           getLastLoginTime                             (22),
109     *           setLastLoginTime                             (23),
110     *           clearLastLoginTime                           (24),
111     *           getSecondsUntilIdleLockout                   (25),
112     *           getPasswordResetState                        (26),
113     *           setPasswordResetState                        (27),
114     *           clearPasswordResetState                      (28),
115     *           getSecondsUntilPasswordResetLockout          (29),
116     *           getGraceLoginUseTimes                        (30),
117     *           addGraceLoginUseTime                         (31),
118     *           setGraceLoginUseTimes                        (32),
119     *           clearGraceLoginUseTimes                      (33),
120     *           getRemainingGraceLoginCount                  (34),
121     *           getPasswordChangedByRequiredTime             (35),
122     *           setPasswordChangedByRequiredTime             (36),
123     *           clearPasswordChangedByRequiredTime           (37),
124     *           getSecondsUntilRequiredChangeTime            (38),
125     *           getPasswordHistory                           (39),
126     *           clearPasswordHistory                         (40),
127     *           ... },
128     *      opValues     SEQUENCE OF OCTET STRING OPTIONAL }
129     * </PRE>
130     * <BR>
131     * Both the request and response values use the same encoded form, and they both
132     * use the same OID of "1.3.6.1.4.1.26027.1.6.1".  The response value will only
133     * include get* elements.  If the request did not include any operations, then
134     * the response will include all get* elements; otherwise, the response will
135     * only include the get* elements that correspond to the state fields referenced
136     * in the request (regardless of whether that operation was included in a get*,
137     * set*, add*, remove*, or clear* operation).
138     */
139    public class PasswordPolicyStateExtendedOperation
140           extends ExtendedOperationHandler<
141                        PasswordPolicyStateExtendedOperationHandlerCfg>
142    {
143      /**
144       * The tracer object for the debug logger.
145       */
146      private static final DebugTracer TRACER = getTracer();
147    
148    
149    
150      /**
151       * The enumerated value for the getPasswordPolicyDN operation.
152       */
153      public static final int OP_GET_PASSWORD_POLICY_DN = 0;
154    
155    
156    
157      /**
158       * The enumerated value for the getAccountDisabledState operation.
159       */
160      public static final int OP_GET_ACCOUNT_DISABLED_STATE = 1;
161    
162    
163    
164      /**
165       * The enumerated value for the setAccountDisabledState operation.
166       */
167      public static final int OP_SET_ACCOUNT_DISABLED_STATE = 2;
168    
169    
170    
171      /**
172       * The enumerated value for the clearAccountDisabledState operation.
173       */
174      public static final int OP_CLEAR_ACCOUNT_DISABLED_STATE = 3;
175    
176    
177    
178      /**
179       * The enumerated value for the getAccountExpirationTime operation.
180       */
181      public static final int OP_GET_ACCOUNT_EXPIRATION_TIME = 4;
182    
183    
184    
185      /**
186       * The enumerated value for the setAccountExpirationTime operation.
187       */
188      public static final int OP_SET_ACCOUNT_EXPIRATION_TIME = 5;
189    
190    
191    
192      /**
193       * The enumerated value for the clearAccountExpirationTime operation.
194       */
195      public static final int OP_CLEAR_ACCOUNT_EXPIRATION_TIME = 6;
196    
197    
198    
199      /**
200       * The enumerated value for the getSecondsUntilAccountExpiration operation.
201       */
202      public static final int OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION = 7;
203    
204    
205    
206      /**
207       * The enumerated value for the getPasswordChangedTime operation.
208       */
209      public static final int OP_GET_PASSWORD_CHANGED_TIME = 8;
210    
211    
212    
213      /**
214       * The enumerated value for the setPasswordChangedTime operation.
215       */
216      public static final int OP_SET_PASSWORD_CHANGED_TIME = 9;
217    
218    
219    
220      /**
221       * The enumerated value for the clearPasswordChangedTime operation.
222       */
223      public static final int OP_CLEAR_PASSWORD_CHANGED_TIME = 10;
224    
225    
226    
227      /**
228       * The enumerated value for the getPasswordExpirationWarnedTime operation.
229       */
230      public static final int OP_GET_PASSWORD_EXPIRATION_WARNED_TIME = 11;
231    
232    
233    
234      /**
235       * The enumerated value for the setPasswordExpirationWarnedTime operation.
236       */
237      public static final int OP_SET_PASSWORD_EXPIRATION_WARNED_TIME = 12;
238    
239    
240    
241      /**
242       * The enumerated value for the clearPasswordExpirationWarnedTime operation.
243       */
244      public static final int OP_CLEAR_PASSWORD_EXPIRATION_WARNED_TIME = 13;
245    
246    
247    
248      /**
249       * The enumerated value for the getSecondsUntilPasswordExpiration operation.
250       */
251      public static final int OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION = 14;
252    
253    
254    
255      /**
256       * The enumerated value for the getSecondsUntilPasswordExpirationWarning
257       * operation.
258       */
259      public static final int OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING = 15;
260    
261    
262    
263      /**
264       * The enumerated value for the getAuthenticationFailureTimes operation.
265       */
266      public static final int OP_GET_AUTHENTICATION_FAILURE_TIMES = 16;
267    
268    
269    
270      /**
271       * The enumerated value for the addAuthenticationFailureTime operation.
272       */
273      public static final int OP_ADD_AUTHENTICATION_FAILURE_TIME = 17;
274    
275    
276    
277      /**
278       * The enumerated value for the setAuthenticationFailureTimes operation.
279       */
280      public static final int OP_SET_AUTHENTICATION_FAILURE_TIMES = 18;
281    
282    
283    
284      /**
285       * The enumerated value for the clearAuthenticationFailureTimes operation.
286       */
287      public static final int OP_CLEAR_AUTHENTICATION_FAILURE_TIMES = 19;
288    
289    
290    
291      /**
292       * The enumerated value for the getSecondsUntilAuthenticationFailureUnlock
293       * operation.
294       */
295      public static final int OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK =
296           20;
297    
298    
299    
300      /**
301       * The enumerated value for the getRemainingAuthenticationFailureCount
302       * operation.
303       */
304      public static final int OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT = 21;
305    
306    
307    
308      /**
309       * The enumerated value for the getLastLoginTime operation.
310       */
311      public static final int OP_GET_LAST_LOGIN_TIME = 22;
312    
313    
314    
315      /**
316       * The enumerated value for the setLastLoginTime operation.
317       */
318      public static final int OP_SET_LAST_LOGIN_TIME = 23;
319    
320    
321    
322      /**
323       * The enumerated value for the clearLastLoginTime operation.
324       */
325      public static final int OP_CLEAR_LAST_LOGIN_TIME = 24;
326    
327    
328    
329      /**
330       * The enumerated value for the getSecondsUntilIdleLockout operation.
331       */
332      public static final int OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT = 25;
333    
334    
335    
336      /**
337       * The enumerated value for the getPasswordResetState operation.
338       */
339      public static final int OP_GET_PASSWORD_RESET_STATE = 26;
340    
341    
342    
343      /**
344       * The enumerated value for the setPasswordResetState operation.
345       */
346      public static final int OP_SET_PASSWORD_RESET_STATE = 27;
347    
348    
349    
350      /**
351       * The enumerated value for the clearPasswordResetState operation.
352       */
353      public static final int OP_CLEAR_PASSWORD_RESET_STATE = 28;
354    
355    
356    
357      /**
358       * The enumerated value for the getSecondsUntilPasswordResetLockout operation.
359       */
360      public static final int OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT = 29;
361    
362    
363    
364      /**
365       * The enumerated value for the getGraceLoginUseTimes operation.
366       */
367      public static final int OP_GET_GRACE_LOGIN_USE_TIMES = 30;
368    
369    
370    
371      /**
372       * The enumerated value for the addGraceLoginUseTime operation.
373       */
374      public static final int OP_ADD_GRACE_LOGIN_USE_TIME = 31;
375    
376    
377    
378      /**
379       * The enumerated value for the setGraceLoginUseTimes operation.
380       */
381      public static final int OP_SET_GRACE_LOGIN_USE_TIMES = 32;
382    
383    
384    
385      /**
386       * The enumerated value for the clearGraceLoginUseTimes operation.
387       */
388      public static final int OP_CLEAR_GRACE_LOGIN_USE_TIMES = 33;
389    
390    
391    
392      /**
393       * The enumerated value for the getRemainingGraceLoginCount operation.
394       */
395      public static final int OP_GET_REMAINING_GRACE_LOGIN_COUNT = 34;
396    
397    
398    
399      /**
400       * The enumerated value for the getPasswordChangedByRequiredTime operation.
401       */
402      public static final int OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME = 35;
403    
404    
405    
406      /**
407       * The enumerated value for the setPasswordChangedByRequiredTime operation.
408       */
409      public static final int OP_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME = 36;
410    
411    
412    
413      /**
414       * The enumerated value for the clearPasswordChangedByRequiredTime operation.
415       */
416      public static final int OP_CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME = 37;
417    
418    
419    
420      /**
421       * The enumerated value for the getSecondsUntilRequiredChangeTime operation.
422       */
423      public static final int OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME = 38;
424    
425    
426    
427      /**
428       * The enumerated value for the getPasswordHistory operation.
429       */
430      public static final int OP_GET_PASSWORD_HISTORY = 39;
431    
432    
433    
434      /**
435       * The enumerated value for the clearPasswordHistory operation.
436       */
437      public static final int OP_CLEAR_PASSWORD_HISTORY = 40;
438    
439    
440    
441      // The set of attributes to request when retrieving a user's entry.
442      private LinkedHashSet<String> requestAttributes;
443    
444      // The search filter that will be used to retrieve user entries.
445      private SearchFilter userFilter;
446    
447    
448    
449      /**
450       * Create an instance of this password policy state extended operation.  All
451       * initialization should be performed in the
452       * {@code initializeExtendedOperationHandler} method.
453       */
454      public PasswordPolicyStateExtendedOperation()
455      {
456        super();
457      }
458    
459    
460      /**
461       * Initializes this extended operation handler based on the information in the
462       * provided configuration entry.  It should also register itself with the
463       * Directory Server for the particular kinds of extended operations that it
464       * will process.
465       *
466       * @param  config       The configuration that contains the information
467       *                      to use to initialize this extended operation handler.
468       *
469       * @throws  ConfigException  If an unrecoverable problem arises in the
470       *                           process of performing the initialization.
471       *
472       * @throws  InitializationException  If a problem occurs during initialization
473       *                                   that is not related to the server
474       *                                   configuration.
475       */
476      public void initializeExtendedOperationHandler(
477                       PasswordPolicyStateExtendedOperationHandlerCfg config)
478             throws ConfigException, InitializationException
479      {
480        // Construct the filter that will be used to retrieve user entries.
481        try
482        {
483          userFilter = SearchFilter.createFilterFromString("(objectClass=*)");
484        }
485        catch (Exception e)
486        {
487          // This should never happen.
488          if (debugEnabled())
489          {
490            TRACER.debugCaught(DebugLogLevel.ERROR, e);
491          }
492        }
493    
494    
495        // Construct the set of request attributes.
496        requestAttributes = new LinkedHashSet<String>(2);
497        requestAttributes.add("*");
498        requestAttributes.add("+");
499    
500    
501        DirectoryServer.registerSupportedExtension(OID_PASSWORD_POLICY_STATE_EXTOP,
502                                                   this);
503      }
504    
505    
506    
507      /**
508       * Performs any finalization that may be necessary for this extended
509       * operation handler.  By default, no finalization is performed.
510       */
511      public void finalizeExtendedOperationHandler()
512      {
513        DirectoryServer.deregisterSupportedExtension(OID_CANCEL_REQUEST);
514      }
515    
516    
517    
518      /**
519       * Processes the provided extended operation.
520       *
521       * @param  operation  The extended operation to be processed.
522       */
523      public void processExtendedOperation(ExtendedOperation operation)
524      {
525        operation.setResultCode(ResultCode.UNDEFINED);
526    
527    
528        // The user must have the password-reset privilege in order to be able to do
529        // anything with this extended operation.
530        ClientConnection clientConnection = operation.getClientConnection();
531        if (! clientConnection.hasPrivilege(Privilege.PASSWORD_RESET, operation))
532        {
533          Message message = ERR_PWPSTATE_EXTOP_NO_PRIVILEGE.get();
534          operation.appendErrorMessage(message);
535          operation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
536          return;
537        }
538    
539    
540        // There must be a request value, and it must be a sequence.  Decode it
541        // into its components.
542        ASN1OctetString requestValue = operation.getRequestValue();
543        if (requestValue == null)
544        {
545          Message message = ERR_PWPSTATE_EXTOP_NO_REQUEST_VALUE.get();
546          operation.appendErrorMessage(message);
547          operation.setResultCode(ResultCode.PROTOCOL_ERROR);
548          return;
549        }
550    
551        ASN1OctetString dnString;
552        ASN1Sequence    opSequence;
553        try
554        {
555          ASN1Sequence valueSequence =
556               ASN1Sequence.decodeAsSequence(requestValue.value());
557          List<ASN1Element> elements = valueSequence.elements();
558          dnString   = elements.get(0).decodeAsOctetString();
559    
560          if (elements.size() == 2)
561          {
562            opSequence = elements.get(1).decodeAsSequence();
563          }
564          else
565          {
566            opSequence = null;
567          }
568        }
569        catch (Exception e)
570        {
571          if (debugEnabled())
572          {
573            TRACER.debugCaught(DebugLogLevel.ERROR, e);
574          }
575    
576          Message message =
577              ERR_PWPSTATE_EXTOP_DECODE_FAILURE.get(getExceptionMessage(e));
578          operation.appendErrorMessage(message);
579          operation.setResultCode(ResultCode.PROTOCOL_ERROR);
580          return;
581        }
582    
583    
584        // Decode the DN and get the corresponding user entry.
585        DN targetDN;
586        try
587        {
588          targetDN = DN.decode(dnString);
589        }
590        catch (DirectoryException de)
591        {
592          if (debugEnabled())
593          {
594            TRACER.debugCaught(DebugLogLevel.ERROR, de);
595          }
596    
597          operation.setResponseData(de);
598          return;
599        }
600    
601        DN rootDN = DirectoryServer.getActualRootBindDN(targetDN);
602        if (rootDN != null)
603        {
604          targetDN = rootDN;
605        }
606    
607        Entry userEntry;
608        InternalClientConnection conn =
609             new InternalClientConnection(clientConnection.getAuthenticationInfo());
610        InternalSearchOperation internalSearch =
611             conn.processSearch(targetDN, SearchScope.BASE_OBJECT,
612                                DereferencePolicy.NEVER_DEREF_ALIASES, 1, 0,
613                                false, userFilter, requestAttributes, null);
614        if (internalSearch.getResultCode() != ResultCode.SUCCESS)
615        {
616          operation.setResultCode(internalSearch.getResultCode());
617          operation.setErrorMessage(internalSearch.getErrorMessage());
618          operation.setMatchedDN(internalSearch.getMatchedDN());
619          operation.setReferralURLs(internalSearch.getReferralURLs());
620          return;
621        }
622    
623        List<SearchResultEntry> matchingEntries = internalSearch.getSearchEntries();
624        if (matchingEntries.isEmpty())
625        {
626          operation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
627          return;
628        }
629        else if (matchingEntries.size() > 1)
630        {
631          Message message = ERR_PWPSTATE_EXTOP_MULTIPLE_ENTRIES.get(
632                  String.valueOf(targetDN));
633          operation.appendErrorMessage(message);
634          operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
635          return;
636        }
637        else
638        {
639          userEntry = matchingEntries.get(0);
640        }
641    
642    
643        // Get the password policy state for the user entry.
644        PasswordPolicyState pwpState;
645        PasswordPolicy      policy;
646        try
647        {
648          pwpState = new PasswordPolicyState(userEntry, false);
649          policy   = pwpState.getPolicy();
650        }
651        catch (DirectoryException de)
652        {
653          if (debugEnabled())
654          {
655            TRACER.debugCaught(DebugLogLevel.ERROR, de);
656          }
657    
658          operation.setResponseData(de);
659          return;
660        }
661    
662    
663        // Create a hash set that will be used to hold the types of the return
664        // types that should be included in the response.
665        boolean returnAll;
666        LinkedHashSet<Integer> returnTypes = new LinkedHashSet<Integer>();
667        if ((opSequence == null) || opSequence.elements().isEmpty())
668        {
669          returnAll = true;
670        }
671        else
672        {
673          returnAll = false;
674          for (ASN1Element element : opSequence.elements())
675          {
676            int opType;
677            ArrayList<String> opValues;
678            try
679            {
680              List<ASN1Element> opElements = element.decodeAsSequence().elements();
681              opType = opElements.get(0).decodeAsEnumerated().intValue();
682    
683              if (opElements.size() == 1)
684              {
685                opValues = null;
686              }
687              else
688              {
689                List<ASN1Element> valueElements =
690                     opElements.get(1).decodeAsSequence().elements();
691                if (valueElements.isEmpty())
692                {
693                  opValues = null;
694                }
695                else
696                {
697                  opValues = new ArrayList<String>(valueElements.size());
698                  for (ASN1Element e : valueElements)
699                  {
700                    opValues.add(e.decodeAsOctetString().stringValue());
701                  }
702                }
703              }
704            }
705            catch (Exception e)
706            {
707              if (debugEnabled())
708              {
709                TRACER.debugCaught(DebugLogLevel.ERROR, e);
710              }
711    
712              Message message = ERR_PWPSTATE_EXTOP_INVALID_OP_ENCODING.get(
713                e.getLocalizedMessage());
714              operation.appendErrorMessage(message);
715              operation.setResultCode(ResultCode.PROTOCOL_ERROR);
716              return;
717            }
718    
719            switch (opType)
720            {
721              case OP_GET_PASSWORD_POLICY_DN:
722                returnTypes.add(OP_GET_PASSWORD_POLICY_DN);
723                break;
724    
725              case OP_GET_ACCOUNT_DISABLED_STATE:
726                returnTypes.add(OP_GET_ACCOUNT_DISABLED_STATE);
727                break;
728    
729              case OP_SET_ACCOUNT_DISABLED_STATE:
730                if (opValues == null)
731                {
732                  operation.appendErrorMessage(
733                          ERR_PWPSTATE_EXTOP_NO_DISABLED_VALUE.get());
734                  operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
735                  return;
736                }
737                else if (opValues.size() != 1)
738                {
739                  operation.appendErrorMessage(
740                          ERR_PWPSTATE_EXTOP_BAD_DISABLED_VALUE_COUNT.get());
741                  operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
742                  return;
743                }
744                else
745                {
746                  String value = opValues.get(0);
747                  if (value.equalsIgnoreCase("true"))
748                  {
749                    pwpState.setDisabled(true);
750                  }
751                  else if (value.equalsIgnoreCase("false"))
752                  {
753                    pwpState.setDisabled(false);
754                  }
755                  else
756                  {
757                    operation.appendErrorMessage(
758                            ERR_PWPSTATE_EXTOP_BAD_DISABLED_VALUE.get());
759                    operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
760                    return;
761                  }
762                }
763    
764                returnTypes.add(OP_GET_ACCOUNT_DISABLED_STATE);
765                break;
766    
767              case OP_CLEAR_ACCOUNT_DISABLED_STATE:
768                pwpState.setDisabled(false);
769                returnTypes.add(OP_GET_ACCOUNT_DISABLED_STATE);
770                break;
771    
772              case OP_GET_ACCOUNT_EXPIRATION_TIME:
773                returnTypes.add(OP_GET_ACCOUNT_EXPIRATION_TIME);
774                break;
775    
776              case OP_SET_ACCOUNT_EXPIRATION_TIME:
777                if (opValues == null)
778                {
779                  pwpState.setAccountExpirationTime(pwpState.getCurrentTime());
780                }
781                else if (opValues.size() != 1)
782                {
783                  operation.appendErrorMessage(
784                          ERR_PWPSTATE_EXTOP_BAD_ACCT_EXP_VALUE_COUNT.get());
785                  operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
786                  return;
787                }
788                else
789                {
790                  try
791                  {
792                    ASN1OctetString valueString =
793                         new ASN1OctetString(opValues.get(0));
794                    long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
795                                     valueString);
796                    pwpState.setAccountExpirationTime(time);
797                  }
798                  catch (DirectoryException de)
799                  {
800                    operation.appendErrorMessage(
801                            ERR_PWPSTATE_EXTOP_BAD_ACCT_EXP_VALUE.get(
802                                    opValues.get(0),
803                                    de.getMessageObject()));
804                    operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
805                    return;
806                  }
807                }
808    
809                returnTypes.add(OP_GET_ACCOUNT_EXPIRATION_TIME);
810                break;
811    
812              case OP_CLEAR_ACCOUNT_EXPIRATION_TIME:
813                pwpState.clearAccountExpirationTime();
814                returnTypes.add(OP_GET_ACCOUNT_EXPIRATION_TIME);
815                break;
816    
817              case OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION:
818                returnTypes.add(OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION);
819                break;
820    
821              case OP_GET_PASSWORD_CHANGED_TIME:
822                returnTypes.add(OP_GET_PASSWORD_CHANGED_TIME);
823                break;
824    
825              case OP_SET_PASSWORD_CHANGED_TIME:
826                if (opValues == null)
827                {
828                  pwpState.setPasswordChangedTime();
829                }
830                else if (opValues.size() != 1)
831                {
832                  operation.appendErrorMessage(
833                          ERR_PWPSTATE_EXTOP_BAD_PWCHANGETIME_VALUE_COUNT.get());
834                  operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
835                  return;
836                }
837                else
838                {
839                  try
840                  {
841                    ASN1OctetString valueString =
842                         new ASN1OctetString(opValues.get(0));
843                    long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
844                                     valueString);
845                    pwpState.setPasswordChangedTime(time);
846                  }
847                  catch (DirectoryException de)
848                  {
849                    operation.appendErrorMessage(
850                            ERR_PWPSTATE_EXTOP_BAD_PWCHANGETIME_VALUE.get(
851                                    opValues.get(0),
852                                    de.getMessageObject()));
853                    operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
854                    return;
855                  }
856                }
857    
858                returnTypes.add(OP_GET_PASSWORD_CHANGED_TIME);
859                break;
860    
861              case OP_CLEAR_PASSWORD_CHANGED_TIME:
862                pwpState.clearPasswordChangedTime();
863                returnTypes.add(OP_GET_PASSWORD_CHANGED_TIME);
864                break;
865    
866              case OP_GET_PASSWORD_EXPIRATION_WARNED_TIME:
867                returnTypes.add(OP_GET_PASSWORD_EXPIRATION_WARNED_TIME);
868                break;
869    
870              case OP_SET_PASSWORD_EXPIRATION_WARNED_TIME:
871                if (opValues == null)
872                {
873                  pwpState.setWarnedTime();
874                }
875                else if (opValues.size() != 1)
876                {
877                  operation.appendErrorMessage(
878                          ERR_PWPSTATE_EXTOP_BAD_PWWARNEDTIME_VALUE_COUNT.get());
879                  operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
880                  return;
881                }
882                else
883                {
884                  try
885                  {
886                    ASN1OctetString valueString =
887                         new ASN1OctetString(opValues.get(0));
888                    long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
889                                     valueString);
890                    pwpState.setWarnedTime(time);
891                  }
892                  catch (DirectoryException de)
893                  {
894                    operation.appendErrorMessage(
895                            ERR_PWPSTATE_EXTOP_BAD_PWWARNEDTIME_VALUE.get(
896                                    opValues.get(0),
897                                    de.getMessageObject()));
898                    operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
899                    return;
900                  }
901                }
902    
903                returnTypes.add(OP_GET_PASSWORD_EXPIRATION_WARNED_TIME);
904                break;
905    
906              case OP_CLEAR_PASSWORD_EXPIRATION_WARNED_TIME:
907                pwpState.clearWarnedTime();
908                returnTypes.add(OP_GET_PASSWORD_EXPIRATION_WARNED_TIME);
909                break;
910    
911              case OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION:
912                returnTypes.add(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION);
913                break;
914    
915              case OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING:
916                returnTypes.add(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING);
917                break;
918    
919              case OP_GET_AUTHENTICATION_FAILURE_TIMES:
920                returnTypes.add(OP_GET_AUTHENTICATION_FAILURE_TIMES);
921                break;
922    
923              case OP_ADD_AUTHENTICATION_FAILURE_TIME:
924                if (opValues == null)
925                {
926                  if (policy.getLockoutFailureCount() == 0)
927                  {
928                    returnTypes.add(OP_GET_AUTHENTICATION_FAILURE_TIMES);
929                    break;
930                  }
931    
932                  pwpState.updateAuthFailureTimes();
933                }
934                else if (opValues.size() != 1)
935                {
936                  operation.appendErrorMessage(
937                          ERR_PWPSTATE_EXTOP_BAD_ADD_FAILURE_TIME_COUNT.get());
938                  operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
939                  return;
940                }
941                else
942                {
943                  try
944                  {
945                    ASN1OctetString valueString =
946                         new ASN1OctetString(opValues.get(0));
947                    long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
948                                     valueString);
949                    List<Long> authFailureTimes = pwpState.getAuthFailureTimes();
950                    ArrayList<Long> newFailureTimes =
951                         new ArrayList<Long>(authFailureTimes.size()+1);
952                    newFailureTimes.addAll(authFailureTimes);
953                    newFailureTimes.add(time);
954                    pwpState.setAuthFailureTimes(newFailureTimes);
955                  }
956                  catch (DirectoryException de)
957                  {
958                    Message message = ERR_PWPSTATE_EXTOP_BAD_AUTH_FAILURE_TIME.get(
959                            opValues.get(0),
960                            de.getMessageObject());
961                    operation.setResultCode(de.getResultCode());
962                    operation.appendErrorMessage(message);
963                    return;
964                  }
965                }
966    
967                returnTypes.add(OP_GET_AUTHENTICATION_FAILURE_TIMES);
968                break;
969    
970              case OP_SET_AUTHENTICATION_FAILURE_TIMES:
971                if (opValues == null)
972                {
973                  ArrayList<Long> valueList = new ArrayList<Long>(1);
974                  valueList.add(pwpState.getCurrentTime());
975                  pwpState.setAuthFailureTimes(valueList);
976                }
977                else
978                {
979                  ArrayList<Long> valueList = new ArrayList<Long>(opValues.size());
980                  for (String s : opValues)
981                  {
982                    try
983                    {
984                      valueList.add(
985                           GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
986                                new ASN1OctetString(s)));
987                    }
988                    catch (DirectoryException de)
989                    {
990                      Message message =
991                              ERR_PWPSTATE_EXTOP_BAD_AUTH_FAILURE_TIME.get(
992                                      s,
993                                      de.getMessageObject());
994                      operation.setResultCode(de.getResultCode());
995                      operation.appendErrorMessage(message);
996                      return;
997                    }
998                  }
999                  pwpState.setAuthFailureTimes(valueList);
1000                }
1001    
1002                returnTypes.add(OP_GET_AUTHENTICATION_FAILURE_TIMES);
1003                break;
1004    
1005              case OP_CLEAR_AUTHENTICATION_FAILURE_TIMES:
1006                pwpState.clearFailureLockout();
1007                returnTypes.add(OP_GET_AUTHENTICATION_FAILURE_TIMES);
1008                break;
1009    
1010              case OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK:
1011                returnTypes.add(OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK);
1012                break;
1013    
1014              case OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT:
1015                returnTypes.add(OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT);
1016                break;
1017    
1018              case OP_GET_LAST_LOGIN_TIME:
1019                returnTypes.add(OP_GET_LAST_LOGIN_TIME);
1020                break;
1021    
1022              case OP_SET_LAST_LOGIN_TIME:
1023                if (opValues == null)
1024                {
1025                  pwpState.setLastLoginTime();
1026                }
1027                else if (opValues.size() != 1)
1028                {
1029                  operation.appendErrorMessage(
1030                          ERR_PWPSTATE_EXTOP_BAD_LAST_LOGIN_TIME_COUNT.get());
1031                  operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
1032                  return;
1033                }
1034                else
1035                {
1036                  try
1037                  {
1038                    ASN1OctetString valueString =
1039                         new ASN1OctetString(opValues.get(0));
1040                    long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
1041                                     valueString);
1042                    pwpState.setLastLoginTime(time);
1043                  }
1044                  catch (DirectoryException de)
1045                  {
1046                    operation.appendErrorMessage(
1047                            ERR_PWPSTATE_EXTOP_BAD_LAST_LOGIN_TIME.get(
1048                                    opValues.get(0),
1049                                    de.getMessageObject()));
1050                    operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
1051                    return;
1052                  }
1053                }
1054    
1055                returnTypes.add(OP_GET_LAST_LOGIN_TIME);
1056                break;
1057    
1058              case OP_CLEAR_LAST_LOGIN_TIME:
1059                pwpState.clearLastLoginTime();
1060                returnTypes.add(OP_GET_LAST_LOGIN_TIME);
1061                break;
1062    
1063              case OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT:
1064                returnTypes.add(OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT);
1065                break;
1066    
1067              case OP_GET_PASSWORD_RESET_STATE:
1068                returnTypes.add(OP_GET_PASSWORD_RESET_STATE);
1069                break;
1070    
1071              case OP_SET_PASSWORD_RESET_STATE:
1072                if (opValues == null)
1073                {
1074                  operation.appendErrorMessage(
1075                          ERR_PWPSTATE_EXTOP_NO_RESET_STATE_VALUE.get());
1076                  operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
1077                  return;
1078                }
1079                else if (opValues.size() != 1)
1080                {
1081                  operation.appendErrorMessage(
1082                          ERR_PWPSTATE_EXTOP_BAD_RESET_STATE_VALUE_COUNT.get());
1083                  operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
1084                  return;
1085                }
1086                else
1087                {
1088                  String value = opValues.get(0);
1089                  if (value.equalsIgnoreCase("true"))
1090                  {
1091                    pwpState.setMustChangePassword(true);
1092                  }
1093                  else if (value.equalsIgnoreCase("false"))
1094                  {
1095                    pwpState.setMustChangePassword(false);
1096                  }
1097                  else
1098                  {
1099                    operation.appendErrorMessage(
1100                            ERR_PWPSTATE_EXTOP_BAD_RESET_STATE_VALUE.get());
1101                    operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
1102                    return;
1103                  }
1104                }
1105    
1106                returnTypes.add(OP_GET_PASSWORD_RESET_STATE);
1107                break;
1108    
1109              case OP_CLEAR_PASSWORD_RESET_STATE:
1110                pwpState.setMustChangePassword(false);
1111                returnTypes.add(OP_GET_PASSWORD_RESET_STATE);
1112                break;
1113    
1114              case OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT:
1115                returnTypes.add(OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT);
1116                break;
1117    
1118              case OP_GET_GRACE_LOGIN_USE_TIMES:
1119                returnTypes.add(OP_GET_GRACE_LOGIN_USE_TIMES);
1120                break;
1121    
1122              case OP_ADD_GRACE_LOGIN_USE_TIME:
1123                if (opValues == null)
1124                {
1125                  pwpState.updateGraceLoginTimes();
1126                }
1127                else if (opValues.size() != 1)
1128                {
1129                  operation.appendErrorMessage(
1130                          ERR_PWPSTATE_EXTOP_BAD_ADD_GRACE_LOGIN_TIME_COUNT.get());
1131                  operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
1132                  return;
1133                }
1134                else
1135                {
1136                  try
1137                  {
1138                    ASN1OctetString valueString =
1139                         new ASN1OctetString(opValues.get(0));
1140                    long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
1141                                     valueString);
1142                    List<Long> authFailureTimes = pwpState.getGraceLoginTimes();
1143                    ArrayList<Long> newGraceTimes =
1144                         new ArrayList<Long>(authFailureTimes.size()+1);
1145                    newGraceTimes.addAll(authFailureTimes);
1146                    newGraceTimes.add(time);
1147                    pwpState.setGraceLoginTimes(newGraceTimes);
1148                  }
1149                  catch (DirectoryException de)
1150                  {
1151                    Message message = ERR_PWPSTATE_EXTOP_BAD_GRACE_LOGIN_TIME.get(
1152                            opValues.get(0),
1153                            de.getMessageObject());
1154                    operation.setResultCode(de.getResultCode());
1155                    operation.appendErrorMessage(message);
1156                    return;
1157                  }
1158                }
1159    
1160                returnTypes.add(OP_GET_GRACE_LOGIN_USE_TIMES);
1161                break;
1162    
1163              case OP_SET_GRACE_LOGIN_USE_TIMES:
1164                if (opValues == null)
1165                {
1166                  ArrayList<Long> valueList = new ArrayList<Long>(1);
1167                  valueList.add(pwpState.getCurrentTime());
1168                  pwpState.setGraceLoginTimes(valueList);
1169                }
1170                else
1171                {
1172                  ArrayList<Long> valueList = new ArrayList<Long>(opValues.size());
1173                  for (String s : opValues)
1174                  {
1175                    try
1176                    {
1177                      valueList.add(
1178                           GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
1179                                new ASN1OctetString(s)));
1180                    }
1181                    catch (DirectoryException de)
1182                    {
1183                      Message message = ERR_PWPSTATE_EXTOP_BAD_GRACE_LOGIN_TIME.get(
1184                              s, de.getMessageObject());
1185                      operation.setResultCode(de.getResultCode());
1186                      operation.appendErrorMessage(message);
1187                      return;
1188                    }
1189                  }
1190                  pwpState.setGraceLoginTimes(valueList);
1191                }
1192    
1193                returnTypes.add(OP_GET_GRACE_LOGIN_USE_TIMES);
1194                break;
1195    
1196              case OP_CLEAR_GRACE_LOGIN_USE_TIMES:
1197                pwpState.clearGraceLoginTimes();
1198                returnTypes.add(OP_GET_GRACE_LOGIN_USE_TIMES);
1199                break;
1200    
1201              case OP_GET_REMAINING_GRACE_LOGIN_COUNT:
1202                returnTypes.add(OP_GET_REMAINING_GRACE_LOGIN_COUNT);
1203                break;
1204    
1205              case OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME:
1206                returnTypes.add(OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME);
1207                break;
1208    
1209              case OP_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME:
1210                if (opValues == null)
1211                {
1212                  pwpState.setRequiredChangeTime();
1213                }
1214                else if (opValues.size() != 1)
1215                {
1216                  operation.appendErrorMessage(
1217                          ERR_PWPSTATE_EXTOP_BAD_REQUIRED_CHANGE_TIME_COUNT.get());
1218                  operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
1219                  return;
1220                }
1221                else
1222                {
1223                  try
1224                  {
1225                    ASN1OctetString valueString =
1226                         new ASN1OctetString(opValues.get(0));
1227                    long time = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
1228                                     valueString);
1229                    pwpState.setRequiredChangeTime(time);
1230                  }
1231                  catch (DirectoryException de)
1232                  {
1233                    operation.appendErrorMessage(
1234                            ERR_PWPSTATE_EXTOP_BAD_REQUIRED_CHANGE_TIME.get(
1235                                    opValues.get(0),
1236                                    de.getMessageObject()));
1237                    operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
1238                    return;
1239                  }
1240                }
1241    
1242                returnTypes.add(OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME);
1243                break;
1244    
1245              case OP_CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME:
1246                pwpState.clearRequiredChangeTime();
1247                returnTypes.add(OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME);
1248                break;
1249    
1250              case OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME:
1251                returnTypes.add(OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME);
1252                break;
1253    
1254              case OP_GET_PASSWORD_HISTORY:
1255                returnTypes.add(OP_GET_PASSWORD_HISTORY);
1256                break;
1257    
1258              case OP_CLEAR_PASSWORD_HISTORY:
1259                pwpState.clearPasswordHistory();
1260                returnTypes.add(OP_GET_PASSWORD_HISTORY);
1261                break;
1262    
1263              default:
1264    
1265                operation.appendErrorMessage(ERR_PWPSTATE_EXTOP_UNKNOWN_OP_TYPE.get(
1266                        String.valueOf(opType)));
1267                operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
1268                return;
1269            }
1270          }
1271    
1272    
1273          // If there are any modifications that need to be made to the password
1274          // policy state, then apply them now.
1275          List<Modification> stateMods = pwpState.getModifications();
1276          if ((stateMods != null) && (! stateMods.isEmpty()))
1277          {
1278            ModifyOperation modifyOperation =
1279                 conn.processModify(targetDN, stateMods);
1280            if (modifyOperation.getResultCode() != ResultCode.SUCCESS)
1281            {
1282              operation.setResultCode(modifyOperation.getResultCode());
1283              operation.setErrorMessage(modifyOperation.getErrorMessage());
1284              operation.setMatchedDN(modifyOperation.getMatchedDN());
1285              operation.setReferralURLs(modifyOperation.getReferralURLs());
1286              return;
1287            }
1288          }
1289        }
1290    
1291    
1292        // Construct the sequence of values to return.
1293        ArrayList<ASN1Element> opElements = new ArrayList<ASN1Element>();
1294        if (returnAll || returnTypes.contains(OP_GET_PASSWORD_POLICY_DN))
1295        {
1296          opElements.add(encode(OP_GET_PASSWORD_POLICY_DN,
1297                                policy.getConfigEntryDN().toString()));
1298        }
1299    
1300        if (returnAll || returnTypes.contains(OP_GET_ACCOUNT_DISABLED_STATE))
1301        {
1302          opElements.add(encode(OP_GET_ACCOUNT_DISABLED_STATE,
1303                                String.valueOf(pwpState.isDisabled())));
1304        }
1305    
1306        if (returnAll || returnTypes.contains(OP_GET_ACCOUNT_EXPIRATION_TIME))
1307        {
1308          String expTimeStr;
1309          long expTime = pwpState.getAccountExpirationTime();
1310          if (expTime < 0)
1311          {
1312            expTimeStr = null;
1313          }
1314          else
1315          {
1316            expTimeStr = GeneralizedTimeSyntax.format(expTime);
1317          }
1318    
1319          opElements.add(encode(OP_GET_ACCOUNT_EXPIRATION_TIME, expTimeStr));
1320        }
1321    
1322        if (returnAll ||
1323            returnTypes.contains(OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION))
1324        {
1325          String secondsStr;
1326          long expTime = pwpState.getAccountExpirationTime();
1327          if (expTime < 0)
1328          {
1329            secondsStr = null;
1330          }
1331          else
1332          {
1333            secondsStr =
1334                 String.valueOf((expTime - pwpState.getCurrentTime()) / 1000);
1335          }
1336    
1337          opElements.add(encode(OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION,
1338                                secondsStr));
1339        }
1340    
1341        if (returnAll || returnTypes.contains(OP_GET_PASSWORD_CHANGED_TIME))
1342        {
1343          String timeStr;
1344          long changedTime = pwpState.getPasswordChangedTime();
1345          if (changedTime < 0)
1346          {
1347            timeStr = null;
1348          }
1349          else
1350          {
1351            timeStr = GeneralizedTimeSyntax.format(changedTime);
1352          }
1353    
1354          opElements.add(encode(OP_GET_PASSWORD_CHANGED_TIME, timeStr));
1355        }
1356    
1357        if (returnAll ||
1358            returnTypes.contains(OP_GET_PASSWORD_EXPIRATION_WARNED_TIME))
1359        {
1360          String timeStr;
1361          long warnedTime = pwpState.getWarnedTime();
1362          if (warnedTime < 0)
1363          {
1364            timeStr = null;
1365          }
1366          else
1367          {
1368            timeStr = GeneralizedTimeSyntax.format(warnedTime);
1369          }
1370    
1371          opElements.add(encode(OP_GET_PASSWORD_EXPIRATION_WARNED_TIME, timeStr));
1372        }
1373    
1374        if (returnAll ||
1375            returnTypes.contains(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION))
1376        {
1377          String secondsStr;
1378          int secondsUntilExp = pwpState.getSecondsUntilExpiration();
1379          if (secondsUntilExp < 0)
1380          {
1381            secondsStr = null;
1382          }
1383          else
1384          {
1385            secondsStr = String.valueOf(secondsUntilExp);
1386          }
1387    
1388          opElements.add(encode(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION,
1389                                secondsStr));
1390        }
1391    
1392        if (returnAll ||
1393            returnTypes.contains(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING))
1394        {
1395          String secondsStr;
1396          int secondsUntilExp = pwpState.getSecondsUntilExpiration();
1397          if (secondsUntilExp < 0)
1398          {
1399            secondsStr = null;
1400          }
1401          else
1402          {
1403            int secondsUntilWarning = secondsUntilExp - policy.getWarningInterval();
1404            if (secondsUntilWarning <= 0)
1405            {
1406              secondsStr = "0";
1407            }
1408            else
1409            {
1410              secondsStr = String.valueOf(secondsUntilWarning);
1411            }
1412          }
1413    
1414          opElements.add(encode(OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING,
1415                                secondsStr));
1416        }
1417    
1418        if (returnAll || returnTypes.contains(OP_GET_AUTHENTICATION_FAILURE_TIMES))
1419        {
1420          opElements.add(encode(OP_GET_AUTHENTICATION_FAILURE_TIMES,
1421                                pwpState.getAuthFailureTimes()));
1422        }
1423    
1424        if (returnAll || returnTypes.contains(
1425                              OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK))
1426        {
1427          // We have to check whether the account is locked due to failures before
1428          // we can get the length of time until the account is unlocked.
1429          String secondsStr;
1430          if (pwpState.lockedDueToFailures())
1431          {
1432            int seconds = pwpState.getSecondsUntilUnlock();
1433            if (seconds <= 0)
1434            {
1435              secondsStr = null;
1436            }
1437            else
1438            {
1439              secondsStr = String.valueOf(seconds);
1440            }
1441          }
1442          else
1443          {
1444            secondsStr = null;
1445          }
1446    
1447          opElements.add(encode(OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK,
1448                                secondsStr));
1449        }
1450    
1451        if (returnAll ||
1452            returnTypes.contains(OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT))
1453        {
1454          String remainingFailuresStr;
1455          int allowedFailureCount = policy.getLockoutFailureCount();
1456          if (allowedFailureCount > 0)
1457          {
1458            int remainingFailures =
1459                     allowedFailureCount - pwpState.getAuthFailureTimes().size();
1460            if (remainingFailures < 0)
1461            {
1462              remainingFailures = 0;
1463            }
1464    
1465            remainingFailuresStr = String.valueOf(remainingFailures);
1466          }
1467          else
1468          {
1469            remainingFailuresStr = null;
1470          }
1471    
1472          opElements.add(encode(OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT,
1473                                remainingFailuresStr));
1474        }
1475    
1476        if (returnAll || returnTypes.contains(OP_GET_LAST_LOGIN_TIME))
1477        {
1478          String timeStr;
1479          long lastLoginTime = pwpState.getLastLoginTime();
1480          if (lastLoginTime < 0)
1481          {
1482            timeStr = null;
1483          }
1484          else
1485          {
1486            timeStr = GeneralizedTimeSyntax.format(lastLoginTime);
1487          }
1488    
1489          opElements.add(encode(OP_GET_LAST_LOGIN_TIME, timeStr));
1490        }
1491    
1492        if (returnAll || returnTypes.contains(OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT))
1493        {
1494          String secondsStr;
1495          int lockoutInterval = policy.getIdleLockoutInterval();
1496          if (lockoutInterval > 0)
1497          {
1498            long lastLoginTime = pwpState.getLastLoginTime();
1499            if (lastLoginTime < 0)
1500            {
1501              secondsStr = "0";
1502            }
1503            else
1504            {
1505              long lockoutTime = lastLoginTime + (lockoutInterval*1000);
1506              long currentTime = pwpState.getCurrentTime();
1507              int secondsUntilLockout = (int) ((lockoutTime - currentTime) / 1000);
1508              if (secondsUntilLockout <= 0)
1509              {
1510                secondsStr = "0";
1511              }
1512              else
1513              {
1514                secondsStr = String.valueOf(secondsUntilLockout);
1515              }
1516            }
1517          }
1518          else
1519          {
1520            secondsStr = null;
1521          }
1522    
1523          opElements.add(encode(OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT, secondsStr));
1524        }
1525    
1526        if (returnAll || returnTypes.contains(OP_GET_PASSWORD_RESET_STATE))
1527        {
1528          opElements.add(encode(OP_GET_PASSWORD_RESET_STATE,
1529                                String.valueOf(pwpState.mustChangePassword())));
1530        }
1531    
1532        if (returnAll ||
1533            returnTypes.contains(OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT))
1534        {
1535          String secondsStr;
1536          if (pwpState.mustChangePassword())
1537          {
1538            int maxAge = policy.getMaximumPasswordResetAge();
1539            if (maxAge > 0)
1540            {
1541              long currentTime = pwpState.getCurrentTime();
1542              long changedTime = pwpState.getPasswordChangedTime();
1543              int changeAge = (int) ((currentTime - changedTime) / 1000);
1544              int timeToLockout = maxAge - changeAge;
1545              if (timeToLockout <= 0)
1546              {
1547                secondsStr = "0";
1548              }
1549              else
1550              {
1551                secondsStr = String.valueOf(timeToLockout);
1552              }
1553            }
1554            else
1555            {
1556              secondsStr = null;
1557            }
1558          }
1559          else
1560          {
1561            secondsStr = null;
1562          }
1563    
1564          opElements.add(encode(OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT,
1565                                secondsStr));
1566        }
1567    
1568        if (returnAll || returnTypes.contains(OP_GET_GRACE_LOGIN_USE_TIMES))
1569        {
1570          opElements.add(encode(OP_GET_GRACE_LOGIN_USE_TIMES,
1571                                pwpState.getGraceLoginTimes()));
1572        }
1573    
1574        if (returnAll || returnTypes.contains(OP_GET_REMAINING_GRACE_LOGIN_COUNT))
1575        {
1576          String remainingStr;
1577          int remainingGraceLogins = pwpState.getGraceLoginsRemaining();
1578          if (remainingGraceLogins <= 0)
1579          {
1580            remainingStr = "0";
1581          }
1582          else
1583          {
1584            remainingStr = String.valueOf(remainingGraceLogins);
1585          }
1586    
1587          opElements.add(encode(OP_GET_REMAINING_GRACE_LOGIN_COUNT, remainingStr));
1588        }
1589    
1590        if (returnAll ||
1591            returnTypes.contains(OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME))
1592        {
1593          String timeStr;
1594          long requiredChangeTime = pwpState.getRequiredChangeTime();
1595          if (requiredChangeTime < 0)
1596          {
1597            timeStr = null;
1598          }
1599          else
1600          {
1601            timeStr = GeneralizedTimeSyntax.format(requiredChangeTime);
1602          }
1603    
1604          opElements.add(encode(OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME, timeStr));
1605        }
1606    
1607        if (returnAll ||
1608            returnTypes.contains(OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME))
1609        {
1610          String secondsStr;
1611          long policyRequiredChangeTime = policy.getRequireChangeByTime();
1612          if (policyRequiredChangeTime > 0)
1613          {
1614            long accountRequiredChangeTime = pwpState.getRequiredChangeTime();
1615            if (accountRequiredChangeTime >= policyRequiredChangeTime)
1616            {
1617              secondsStr = null;
1618            }
1619            else
1620            {
1621              long currentTime = pwpState.getCurrentTime();
1622              if (currentTime >= policyRequiredChangeTime)
1623              {
1624                secondsStr = "0";
1625              }
1626              else
1627              {
1628                secondsStr =
1629                     String.valueOf((policyRequiredChangeTime-currentTime) / 1000);
1630    
1631              }
1632            }
1633          }
1634          else
1635          {
1636            secondsStr = null;
1637          }
1638    
1639          opElements.add(encode(OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME,
1640                                secondsStr));
1641        }
1642    
1643        if (returnAll || returnTypes.contains(OP_GET_PASSWORD_HISTORY))
1644        {
1645          opElements.add(encode(OP_GET_PASSWORD_HISTORY,
1646                                pwpState.getPasswordHistoryValues()));
1647        }
1648    
1649        ArrayList<ASN1Element> responseValueElements =
1650             new ArrayList<ASN1Element>(2);
1651        responseValueElements.add(dnString);
1652        responseValueElements.add(new ASN1Sequence(opElements));
1653    
1654        ASN1OctetString responseValue =
1655             new ASN1OctetString(new ASN1Sequence(responseValueElements).encode());
1656    
1657        operation.setResponseOID(OID_PASSWORD_POLICY_STATE_EXTOP);
1658        operation.setResponseValue(responseValue);
1659        operation.setResultCode(ResultCode.SUCCESS);
1660      }
1661    
1662    
1663    
1664      /**
1665       * Encodes the provided information in a form suitable for including in the
1666       * response value.
1667       *
1668       * @param  opType  The operation type to use for the value.
1669       * @param  value   The single value to include in the response.
1670       *
1671       * @return  The encoded ASN.1 element.
1672       */
1673      public static ASN1Element encode(int opType, String value)
1674      {
1675        ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
1676        elements.add(new ASN1Enumerated(opType));
1677    
1678        if (value != null)
1679        {
1680          ArrayList<ASN1Element> valueElements = new ArrayList<ASN1Element>(1);
1681          valueElements.add(new ASN1OctetString(value));
1682          elements.add(new ASN1Sequence(valueElements));
1683        }
1684    
1685        return new ASN1Sequence(elements);
1686      }
1687    
1688    
1689    
1690      /**
1691       * Encodes the provided information in a form suitable for including in the
1692       * response value.
1693       *
1694       * @param  opType  The operation type to use for the value.
1695       * @param  values  The set of string values to include in the response.
1696       *
1697       * @return  The encoded ASN.1 element.
1698       */
1699      public static ASN1Element encode(int opType, String[] values)
1700      {
1701        ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
1702        elements.add(new ASN1Enumerated(opType));
1703    
1704        if ((values != null) && (values.length > 0))
1705        {
1706          ArrayList<ASN1Element> valueElements =
1707               new ArrayList<ASN1Element>(values.length);
1708          for (int i=0; i < values.length; i++)
1709          {
1710            valueElements.add(new ASN1OctetString(values[i]));
1711          }
1712          elements.add(new ASN1Sequence(valueElements));
1713        }
1714    
1715        return new ASN1Sequence(elements);
1716      }
1717    
1718    
1719    
1720      /**
1721       * Encodes the provided information in a form suitable for including in the
1722       * response value.
1723       *
1724       * @param  opType  The operation type to use for the value.
1725       * @param  values  The set of timestamp values to include in the response.
1726       *
1727       * @return  The encoded ASN.1 element.
1728       */
1729      public static ASN1Element encode(int opType, List<Long> values)
1730      {
1731        ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
1732        elements.add(new ASN1Enumerated(opType));
1733    
1734        ArrayList<ASN1Element> valueElements =
1735             new ArrayList<ASN1Element>(values.size());
1736        for (long l : values)
1737        {
1738          valueElements.add(new ASN1OctetString(GeneralizedTimeSyntax.format(l)));
1739        }
1740        elements.add(new ASN1Sequence(valueElements));
1741    
1742        return new ASN1Sequence(elements);
1743      }
1744    }
1745