001/*
002 * Copyright 2015-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2015-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2015-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk.unboundidds.extensions;
037
038
039
040import java.io.Serializable;
041import java.util.StringTokenizer;
042
043import com.unboundid.ldap.sdk.LDAPException;
044import com.unboundid.ldap.sdk.ResultCode;
045import com.unboundid.util.Debug;
046import com.unboundid.util.NotMutable;
047import com.unboundid.util.NotNull;
048import com.unboundid.util.Nullable;
049import com.unboundid.util.StaticUtils;
050import com.unboundid.util.ThreadSafety;
051import com.unboundid.util.ThreadSafetyLevel;
052import com.unboundid.util.Validator;
053
054import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
055
056
057
058/**
059 * This class defines a data structure that will provide information about
060 * errors that may affect an account's usability.  It includes a number of
061 * predefined error types, but also allows for the possibility of additional
062 * error types that have not been defined.
063 * <BR>
064 * <BLOCKQUOTE>
065 *   <B>NOTE:</B>  This class, and other classes within the
066 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
067 *   supported for use against Ping Identity, UnboundID, and
068 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
069 *   for proprietary functionality or for external specifications that are not
070 *   considered stable or mature enough to be guaranteed to work in an
071 *   interoperable way with other types of LDAP servers.
072 * </BLOCKQUOTE>
073 */
074@NotMutable()
075@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
076public final class PasswordPolicyStateAccountUsabilityError
077       implements Serializable
078{
079  /**
080   * The numeric value for the error type that indicates the user's account is
081   * disabled.
082   */
083  public static final int ERROR_TYPE_ACCOUNT_DISABLED = 1;
084
085
086
087  /**
088   * The name for the error type that indicates the user's account is disabled.
089   */
090  @NotNull public static final String ERROR_NAME_ACCOUNT_DISABLED =
091       "account-disabled";
092
093
094
095  /**
096   * The numeric value for the error type that indicates the user's account is
097   * not yet active.
098   */
099  public static final int ERROR_TYPE_ACCOUNT_NOT_YET_ACTIVE = 2;
100
101
102
103  /**
104   * The name for the error type that indicates the user's account is not yet
105   * valid.
106   */
107  @NotNull public static final String ERROR_NAME_ACCOUNT_NOT_YET_ACTIVE =
108       "account-not-yet-active";
109
110
111
112  /**
113   * The numeric value for the error type that indicates the user's account is
114   * expired.
115   */
116  public static final int ERROR_TYPE_ACCOUNT_EXPIRED = 3;
117
118
119
120  /**
121   * The name for the error type that indicates the user's account is expired.
122   */
123  @NotNull public static final String ERROR_NAME_ACCOUNT_EXPIRED =
124       "account-expired";
125
126
127
128  /**
129   * The numeric value for the error type that indicates the user's account is
130   * permanently locked (until the password is reset by an administrator) as a
131   * result of too many failed authentication attempts.
132   */
133  public static final int
134       ERROR_TYPE_ACCOUNT_PERMANENTLY_LOCKED_DUE_TO_BIND_FAILURES = 4;
135
136
137
138  /**
139   * The name for the error type that indicates the user's account is
140   * permanently locked (until the password is reset by an administrator) as a
141   * result of too many failed authentication attempts.
142   */
143  @NotNull public static final String
144       ERROR_NAME_ACCOUNT_PERMANENTLY_LOCKED_DUE_TO_BIND_FAILURES =
145       "account-permanently-locked-due-to-bind-failures";
146
147
148
149  /**
150   * The numeric value for the error type that indicates the user's account is
151   * temporarily locked (until the lockout period elapses or the password is
152   * reset by an administrator) as a result of too many failed authentication
153   * attempts.
154   */
155  public static final int
156       ERROR_TYPE_ACCOUNT_TEMPORARILY_LOCKED_DUE_TO_BIND_FAILURES = 5;
157
158
159
160  /**
161   * The name for the error type that indicates the user's account is
162   * temporarily locked (until the lockout period elapses or the password is
163   * reset by an administrator) as a result of too many failed authentication
164   * attempts.
165   */
166  @NotNull public static final String
167       ERROR_NAME_ACCOUNT_TEMPORARILY_LOCKED_DUE_TO_BIND_FAILURES =
168       "account-temporarily-locked-due-to-bind-failures";
169
170
171
172  /**
173   * The numeric value for the error type that indicates the user's account is
174   * locked (until the password is reset by an administrator) as a result of
175   * remaining idle for too long (i.e., it has been too long since the user last
176   * authenticated).
177   */
178  public static final int ERROR_TYPE_ACCOUNT_IDLE_LOCKED = 6;
179
180
181
182  /**
183   * The name for the error type that indicates the user's account is locked
184   * (until the password is reset by an administrator) as a result of remaining
185   * idle for too long (i.e., it has been too long since the user last
186   * authenticated).
187   */
188  @NotNull public static final String ERROR_NAME_ACCOUNT_IDLE_LOCKED =
189       "account-idle-locked";
190
191
192
193  /**
194   * The numeric value for the error type that indicates the user's account is
195   * locked (until the password is reset by an administrator) as a result of
196   * failing to change the password in a timely manner after it was reset by an
197   * administrator.
198   */
199  public static final int ERROR_TYPE_ACCOUNT_RESET_LOCKED = 7;
200
201
202
203  /**
204   * The name for the error type that indicates the user's account is locked
205   * (until the password is reset by an administrator) as a result of failing to
206   * change the password in a timely manner after it was reset by an
207   * administrator.
208   */
209  @NotNull public static final String ERROR_NAME_ACCOUNT_RESET_LOCKED =
210       "account-reset-locked";
211
212
213
214  /**
215   * The numeric value for the error type that indicates the user's password
216   * is expired.
217   */
218  public static final int ERROR_TYPE_PASSWORD_EXPIRED = 8;
219
220
221
222  /**
223   * The name for the error type that indicates the user's password is expired.
224   */
225  @NotNull public static final String ERROR_NAME_PASSWORD_EXPIRED =
226       "password-expired";
227
228
229
230  /**
231   * The numeric value for the error type that indicates the user's account is
232   * locked (until the password is reset by an administrator) as a result of
233   * failing to change the password by a required time.
234   */
235  public static final int ERROR_TYPE_PASSWORD_NOT_CHANGED_BY_REQUIRED_TIME = 9;
236
237
238
239  /**
240   * The name for the error type that indicates the user's account is locked
241   * (until the password is reset by an administrator) as a result of failing to
242   * change the password by a required time.
243   */
244  @NotNull public static final String
245       ERROR_NAME_PASSWORD_NOT_CHANGED_BY_REQUIRED_TIME =
246            "password-not-changed-by-required-time";
247
248
249
250  /**
251   * The numeric value for the error type that indicates the user's password
252   * has expired, but the user has one or more grace logins remaining.  The
253   * user may still authenticate with a grace login, but will not be permitted
254   * to submit any other requests until changing the password.
255   */
256  public static final int ERROR_TYPE_PASSWORD_EXPIRED_WITH_GRACE_LOGINS = 10;
257
258
259
260  /**
261   * The name for the error type that indicates the user's password has
262   * expired, but the user has one or more grace logins remaining.  The user may
263   * still authenticate with a grace login, but will not be permitted to submit
264   * any other requests until changing the password.
265   */
266  @NotNull public static final String
267       ERROR_NAME_PASSWORD_EXPIRED_WITH_GRACE_LOGINS =
268            "password-expired-with-grace-logins";
269
270
271
272  /**
273   * The numeric value for the error type that indicates the user must change
274   * their password after an administrative reset (or for a newly-created
275   * account) before they will be submit any requests.  The user's account may
276   * be locked if they do not change their password in a timely manner.
277   */
278  public static final int ERROR_TYPE_MUST_CHANGE_PASSWORD = 11;
279
280
281
282  /**
283   * The name for the error type that indicates the user must change their
284   * password after an administrative reset (or for a newly-created account)
285   * before they will be submit any requests.  The user's account may be locked
286   * if they do not change their password in a timely manner.
287   */
288  @NotNull public static final String ERROR_NAME_MUST_CHANGE_PASSWORD =
289       "must-change-password";
290
291
292
293  /**
294   * The numeric value for the error type that indicates the user's account is
295   * locked because it contains a password that does not satisfy all of the
296   * configured password validators.
297   */
298  public static final int ERROR_TYPE_ACCOUNT_VALIDATION_LOCKED = 12;
299
300
301
302  /**
303   * The name for the error type that indicates the user's account is locked
304   * because it contains a password that does not satisfy all of the configured
305   * password validators.
306   */
307  @NotNull public static final String ERROR_NAME_ACCOUNT_VALIDATION_LOCKED =
308       "account-validation-locked";
309
310
311
312  /**
313   * The serial version UID for this serializable class.
314   */
315  private static final long serialVersionUID = -8399539239321392737L;
316
317
318
319  // The integer value for this account usability error.
320  private final int intValue;
321
322  // A human-readable message that provides specific details about this account
323  // usability error.
324  @Nullable private final String message;
325
326  // The name for this account usability error.
327  @NotNull private final String name;
328
329  // The encoded string representation for this account usability error.
330  @NotNull private final String stringRepresentation;
331
332
333
334  /**
335   * Creates a new account usability error with the provided information.
336   *
337   * @param  intValue  The integer value for this account usability error.
338   * @param  name      The name for this account usability error.  It must not
339   *                   be {@code null}.
340   * @param  message   A human-readable message that provides specific details
341   *                   about this account usability error.  It may be
342   *                   {@code null} if no message is available.
343   */
344  public PasswordPolicyStateAccountUsabilityError(final int intValue,
345              @NotNull final String name,
346              @Nullable final String message)
347  {
348    Validator.ensureNotNull(name);
349
350    this.intValue = intValue;
351    this.name = name;
352    this.message = message;
353
354    final StringBuilder buffer = new StringBuilder();
355    buffer.append("code=");
356    buffer.append(intValue);
357    buffer.append("\tname=");
358    buffer.append(name);
359
360    if (message != null)
361    {
362      buffer.append("\tmessage=");
363      buffer.append(message);
364    }
365
366    stringRepresentation = buffer.toString();
367  }
368
369
370
371  /**
372   * Creates a new account usability error that is decoded from the provided
373   * string representation.
374   *
375   * @param  stringRepresentation  The string representation of the account
376   *                               usability error to decode.  It must not be
377   *                               {@code null}.
378   *
379   * @throws  LDAPException  If the provided string cannot be decoded as a valid
380   *                         account usability error.
381   */
382  public PasswordPolicyStateAccountUsabilityError(
383              @NotNull final String stringRepresentation)
384         throws LDAPException
385  {
386    this.stringRepresentation = stringRepresentation;
387
388    try
389    {
390      Integer i = null;
391      String  n = null;
392      String  m = null;
393
394      final StringTokenizer tokenizer =
395           new StringTokenizer(stringRepresentation, "\t");
396      while (tokenizer.hasMoreTokens())
397      {
398        final String token = tokenizer.nextToken();
399        final int equalPos = token.indexOf('=');
400        final String fieldName = token.substring(0, equalPos);
401        final String fieldValue = token.substring(equalPos+1);
402        if (fieldName.equals("code"))
403        {
404          i = Integer.valueOf(fieldValue);
405        }
406        else if (fieldName.equals("name"))
407        {
408          n = fieldValue;
409        }
410        else if (fieldName.equals("message"))
411        {
412          m = fieldValue;
413        }
414      }
415
416      if (i == null)
417      {
418        throw new LDAPException(ResultCode.DECODING_ERROR,
419             ERR_PWP_STATE_ACCOUNT_USABILITY_ERROR_CANNOT_DECODE.get(
420                  stringRepresentation,
421                  ERR_PWP_STATE_ACCOUNT_USABILITY_ERROR_NO_CODE.get()));
422      }
423
424      if (n == null)
425      {
426        throw new LDAPException(ResultCode.DECODING_ERROR,
427             ERR_PWP_STATE_ACCOUNT_USABILITY_ERROR_CANNOT_DECODE.get(
428                  stringRepresentation,
429                  ERR_PWP_STATE_ACCOUNT_USABILITY_ERROR_NO_NAME.get()));
430      }
431
432      intValue = i;
433      name     = n;
434      message  = m;
435    }
436    catch (final LDAPException le)
437    {
438      Debug.debugException(le);
439
440      throw le;
441    }
442    catch (final Exception e)
443    {
444      Debug.debugException(e);
445
446      throw new LDAPException(ResultCode.DECODING_ERROR,
447           ERR_PWP_STATE_ACCOUNT_USABILITY_ERROR_CANNOT_DECODE.get(
448                stringRepresentation, StaticUtils.getExceptionMessage(e)),
449           e);
450    }
451  }
452
453
454
455  /**
456   * Retrieves the integer value for this account usability error.
457   *
458   * @return  The integer value for this account usability error.
459   */
460  public int getIntValue()
461  {
462    return intValue;
463  }
464
465
466
467  /**
468   * Retrieves the name for this account usability error.
469   *
470   * @return  The name for this account usability error.
471   */
472  @NotNull()
473  public String getName()
474  {
475    return name;
476  }
477
478
479
480  /**
481   * Retrieves a human-readable message that provides specific details about
482   * this account usability error.
483   *
484   * @return  A human-readable message that provides specific details about this
485   *          account usability error, or {@code null} if no message is
486   *          available.
487   */
488  @Nullable()
489  public String getMessage()
490  {
491    return message;
492  }
493
494
495
496  /**
497   * Retrieves a string representation of this account usability error.
498   *
499   * @return  A string representation of this account usability error.
500   */
501  @Override()
502  @NotNull()
503  public String toString()
504  {
505    return stringRepresentation;
506  }
507}