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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2012-2015 ForgeRock AS
026 */
027package org.opends.server.schema;
028import static org.opends.messages.SchemaMessages.*;
029import static org.opends.server.schema.SchemaConstants.*;
030import static org.opends.server.util.StaticUtils.*;
031
032import org.forgerock.i18n.LocalizableMessage;
033import org.forgerock.i18n.slf4j.LocalizedLogger;
034import org.forgerock.opendj.ldap.ByteSequence;
035import org.forgerock.opendj.ldap.ResultCode;
036import org.forgerock.opendj.ldap.schema.Schema;
037import org.forgerock.opendj.ldap.schema.Syntax;
038import org.opends.server.admin.std.server.AttributeSyntaxCfg;
039import org.opends.server.api.AttributeSyntax;
040import org.opends.server.types.DirectoryException;
041
042
043/**
044 * This class defines an attribute syntax used for storing values that have been
045 * encoded using a password storage scheme.  The format for attribute values
046 * with this syntax is the concatenation of the following elements in the given
047 * order:
048 * <BR>
049 * <UL>
050 *   <LI>An opening curly brace ("{
051
052  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
053") character.</LI>
054 *   <LI>The name of the storage scheme used to encode the value.</LI>
055 *   <LI>A closing curly brace ("}") character.</LI>
056 *   <LI>The encoded value.</LI>
057 * </UL>
058 */
059public class UserPasswordSyntax
060       extends AttributeSyntax<AttributeSyntaxCfg>
061{
062
063  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
064
065
066  /**
067   * Creates a new instance of this syntax.  Note that the only thing that
068   * should be done here is to invoke the default constructor for the
069   * superclass.  All initialization should be performed in the
070   * <CODE>initializeSyntax</CODE> method.
071   */
072  public UserPasswordSyntax()
073  {
074    super();
075  }
076
077  /** {@inheritDoc} */
078  @Override
079  public Syntax getSDKSyntax(Schema schema)
080  {
081    return schema.getSyntax(SchemaConstants.SYNTAX_USER_PASSWORD_OID);
082  }
083
084  /**
085   * Retrieves the common name for this attribute syntax.
086   *
087   * @return  The common name for this attribute syntax.
088   */
089  @Override
090  public String getName()
091  {
092    return SYNTAX_USER_PASSWORD_NAME;
093  }
094
095  /**
096   * Retrieves the OID for this attribute syntax.
097   *
098   * @return  The OID for this attribute syntax.
099   */
100  @Override
101  public String getOID()
102  {
103    return SYNTAX_USER_PASSWORD_OID;
104  }
105
106  /**
107   * Retrieves a description for this attribute syntax.
108   *
109   * @return  A description for this attribute syntax.
110   */
111  @Override
112  public String getDescription()
113  {
114    return SYNTAX_USER_PASSWORD_DESCRIPTION;
115  }
116
117
118  /**
119   * Decodes the provided user password value into its component parts.
120   *
121   * @param  userPasswordValue  The user password value to be decoded.
122   *
123   * @return  A two-element string array whose elements are the storage scheme
124   *          name (in all lowercase characters) and the encoded value, in that
125   *          order.
126   *
127   * @throws  DirectoryException  If a problem is encountered while attempting
128   *                              to decode the value.
129   */
130  public static String[] decodeUserPassword(String userPasswordValue)
131         throws DirectoryException
132  {
133    // Make sure that there actually is a value to decode.
134    if (userPasswordValue == null || userPasswordValue.length() == 0)
135    {
136      LocalizableMessage message = ERR_ATTR_SYNTAX_USERPW_NO_VALUE.get();
137      throw new DirectoryException(
138              ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
139    }
140
141
142    // The first character of an encoded value must be an opening curly brace.
143    if (userPasswordValue.charAt(0) != '{')
144    {
145      LocalizableMessage message = ERR_ATTR_SYNTAX_USERPW_NO_OPENING_BRACE.get();
146      throw new DirectoryException(
147              ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
148    }
149
150
151    // There must be a corresponding closing brace.
152    int closePos = userPasswordValue.indexOf('}');
153    if (closePos < 0)
154    {
155      LocalizableMessage message = ERR_ATTR_SYNTAX_USERPW_NO_CLOSING_BRACE.get();
156      throw new DirectoryException(
157              ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
158    }
159
160
161    // Get the storage scheme name and encoded value.
162    String schemeName   = userPasswordValue.substring(1, closePos);
163    String encodedValue = userPasswordValue.substring(closePos+1);
164
165    if (schemeName.length() == 0)
166    {
167      LocalizableMessage message = ERR_ATTR_SYNTAX_USERPW_NO_SCHEME.get();
168      throw new DirectoryException(
169              ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
170    }
171
172
173    return new String[] { toLowerCase(schemeName), encodedValue };
174  }
175
176  /**
177   * Indicates whether the provided value is encoded using the user password
178   * syntax.
179   *
180   * @param  value  The value for which to make the determination.
181   *
182   * @return  <CODE>true</CODE> if the value appears to be encoded using the
183   *          user password syntax, or <CODE>false</CODE> if not.
184   */
185  public static boolean isEncoded(ByteSequence value)
186  {
187    // If the value is null or empty, then it's not.
188    if (value == null || value.length() == 0)
189    {
190      return false;
191    }
192
193
194    // If the value doesn't start with an opening curly brace, then it's not.
195    if (value.byteAt(0) != '{')
196    {
197      return false;
198    }
199
200
201    // There must be a corresponding closing curly brace, and there must be at
202    // least one character inside the brace.
203    int closingBracePos = -1;
204    for (int i=1; i < value.length(); i++)
205    {
206      if (value.byteAt(i) == '}')
207      {
208        closingBracePos = i;
209        break;
210      }
211    }
212
213    return closingBracePos >= 0
214        && closingBracePos != 1
215        // The closing curly brace must not be the last character of the password.
216        && closingBracePos != value.length() - 1;
217  }
218
219}
220