001/*
002 * Copyright 2017-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2017-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) 2017-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.listener;
037
038
039
040import java.util.List;
041
042import com.unboundid.asn1.ASN1OctetString;
043import com.unboundid.ldap.matchingrules.OctetStringMatchingRule;
044import com.unboundid.ldap.sdk.LDAPException;
045import com.unboundid.ldap.sdk.ReadOnlyEntry;
046import com.unboundid.util.NotNull;
047import com.unboundid.util.Nullable;
048import com.unboundid.util.ThreadSafety;
049import com.unboundid.util.ThreadSafetyLevel;
050
051
052
053/**
054 * This class provides a data structure that encapsulates a password used by the
055 * in-memory directory server.  It may be optionally associated with an
056 * {@link InMemoryPasswordEncoder}.
057 */
058@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
059public final class InMemoryDirectoryServerPassword
060{
061  // The password as it is (or has the potential to be) stored in the in-memory
062  // directory server.
063  @NotNull private final ASN1OctetString storedPassword;
064
065  // The password encoder that should be used when interacting with the stored
066  // password.
067  @Nullable private final InMemoryPasswordEncoder passwordEncoder;
068
069  // The user entry with which the stored password is associated.
070  @NotNull private final ReadOnlyEntry userEntry;
071
072  // The name of the attribute with which the stored password is associated.
073  @NotNull private final String attributeName;
074
075
076
077  /**
078   * Creates a new in-memory directory server password with the provided
079   * information.
080   *
081   * @param  storedPassword    The password as it is (or has the potential to
082   *                           be) stored in the in-memory directory server.  It
083   *                           must not be {@code null}.
084   * @param  userEntry         The user entry with which the stored password is
085   *                           associated.  It must not be {@code nulL}.
086   * @param  attributeName     The name of the attribute with which the stored
087   *                           password is associated.  It must not be
088   *                           {@code null}.
089   * @param  passwordEncoders  The set of password encoders configured for the
090   *                           in-memory directory server.  It must not be
091   *                           {@code null} but may be empty.
092   */
093  InMemoryDirectoryServerPassword(@NotNull final ASN1OctetString storedPassword,
094       @NotNull final ReadOnlyEntry userEntry,
095       @NotNull final String attributeName,
096       @NotNull final List<InMemoryPasswordEncoder> passwordEncoders)
097  {
098    this.storedPassword = storedPassword;
099    this.userEntry = userEntry;
100    this.attributeName = attributeName;
101
102    InMemoryPasswordEncoder encoder = null;
103    for (final InMemoryPasswordEncoder e : passwordEncoders)
104    {
105      if (e.passwordStartsWithPrefix(storedPassword))
106      {
107        encoder = e;
108        break;
109      }
110    }
111
112    passwordEncoder = encoder;
113  }
114
115
116
117  /**
118   * Retrieves the password as it is (or has the potential to be) stored in the
119   * in-memory directory server.  If the {@link #isEncoded()} method returns
120   * {@code true}, then the stored password will be treated as an encoded
121   * password.  Otherwise, it will be treated as a clear-text password with
122   * no encoding or output formatting.
123   *
124   * @return  The password as it is (or has the potential to be) stored in the
125   *          in-memory directory server.
126   */
127  @NotNull()
128  public ASN1OctetString getStoredPassword()
129  {
130    return storedPassword;
131  }
132
133
134
135  /**
136   * Retrieves the name of the attribute with which the stored password is
137   * associated.
138   *
139   * @return  The name of the attribute with which the stored password is
140   *          associated.
141   */
142  @NotNull()
143  public String getAttributeName()
144  {
145    return attributeName;
146  }
147
148
149
150  /**
151   * Indicates whether the stored password is encoded or in the clear.
152   *
153   * @return  {@code true} if the stored password is encoded, or {@code false}
154   *          if it is the clear.
155   */
156  public boolean isEncoded()
157  {
158    return (passwordEncoder != null);
159  }
160
161
162
163  /**
164   * Retrieves the password encoder that should be used to interact with the
165   * stored password.
166   *
167   * @return  The password encoder that should be used to interact with the
168   *          stored password, or {@code null} if the password is not encoded.
169   */
170  @Nullable()
171  public InMemoryPasswordEncoder getPasswordEncoder()
172  {
173    return passwordEncoder;
174  }
175
176
177
178  /**
179   * Retrieves the clear-text representation of the stored password, if it
180   * is possible to obtain it.  If the password is not encoded, then the stored
181   * password will be returned as-is.  If the stored password is encoded, then
182   * the {@link InMemoryPasswordEncoder#extractClearPasswordFromEncodedPassword}
183   * method will be used in an attempt to
184   *
185   * @return  The clear-text representation of the stored password.
186   *
187   * @throws  LDAPException  If the stored password is encoded using a mechanism
188   *                         that does not permit extracting the clear-text
189   *                         password.
190   */
191  @NotNull()
192  public ASN1OctetString getClearPassword()
193         throws LDAPException
194  {
195    if (passwordEncoder == null)
196    {
197      return storedPassword;
198    }
199    else
200    {
201      return passwordEncoder.extractClearPasswordFromEncodedPassword(
202           storedPassword, userEntry);
203    }
204  }
205
206
207
208  /**
209   * Indicates whether this password matches the provided clear-text password.
210   *
211   * @param  clearPassword  The clear-text password for which to make the
212   *                        determination.
213   *
214   * @return  {@code true} if this password matches the provided clear-text
215   *          password, or {@code false} if not.
216   *
217   * @throws  LDAPException  If a problem is encountered while trying to make
218   *                         the determination.
219   */
220  public boolean matchesClearPassword(
221                      @NotNull final ASN1OctetString clearPassword)
222         throws LDAPException
223  {
224    if (passwordEncoder == null)
225    {
226      return OctetStringMatchingRule.getInstance().valuesMatch(clearPassword,
227           storedPassword);
228    }
229    else
230    {
231      return passwordEncoder.clearPasswordMatchesEncodedPassword(clearPassword,
232           storedPassword, userEntry);
233    }
234  }
235}