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.schema;
028 import org.opends.messages.Message;
029
030
031
032 import org.opends.server.admin.std.server.AttributeSyntaxCfg;
033 import org.opends.server.api.ApproximateMatchingRule;
034 import org.opends.server.api.AttributeSyntax;
035 import org.opends.server.api.EqualityMatchingRule;
036 import org.opends.server.api.OrderingMatchingRule;
037 import org.opends.server.api.SubstringMatchingRule;
038 import org.opends.server.config.ConfigException;
039 import org.opends.server.core.DirectoryServer;
040 import org.opends.server.types.ByteString;
041 import org.opends.server.types.DirectoryException;
042
043
044 import org.opends.server.types.ResultCode;
045
046 import static org.opends.server.loggers.ErrorLogger.*;
047 import static org.opends.messages.SchemaMessages.*;
048 import org.opends.messages.MessageBuilder;
049 import static org.opends.server.schema.SchemaConstants.*;
050
051
052 /**
053 * This class defines the auth password attribute syntax, which is defined in
054 * RFC 3112 and is used to hold authentication information. Only equality
055 * matching will be allowed by default.
056 */
057 public class AuthPasswordSyntax
058 extends AttributeSyntax<AttributeSyntaxCfg>
059 {
060 // The default equality matching rule for this syntax.
061 private EqualityMatchingRule defaultEqualityMatchingRule;
062
063
064
065 /**
066 * Creates a new instance of this syntax. Note that the only thing that
067 * should be done here is to invoke the default constructor for the
068 * superclass. All initialization should be performed in the
069 * <CODE>initializeSyntax</CODE> method.
070 */
071 public AuthPasswordSyntax()
072 {
073 super();
074 }
075
076
077
078 /**
079 * {@inheritDoc}
080 */
081 public void initializeSyntax(AttributeSyntaxCfg configuration)
082 throws ConfigException
083 {
084 defaultEqualityMatchingRule =
085 DirectoryServer.getEqualityMatchingRule(EMR_AUTH_PASSWORD_EXACT_OID);
086 if (defaultEqualityMatchingRule == null)
087 {
088 logError(ERR_ATTR_SYNTAX_UNKNOWN_EQUALITY_MATCHING_RULE.get(
089 EMR_AUTH_PASSWORD_EXACT_NAME, SYNTAX_AUTH_PASSWORD_NAME));
090 }
091 }
092
093
094
095 /**
096 * Retrieves the common name for this attribute syntax.
097 *
098 * @return The common name for this attribute syntax.
099 */
100 public String getSyntaxName()
101 {
102 return SYNTAX_AUTH_PASSWORD_NAME;
103 }
104
105
106
107 /**
108 * Retrieves the OID for this attribute syntax.
109 *
110 * @return The OID for this attribute syntax.
111 */
112 public String getOID()
113 {
114 return SYNTAX_AUTH_PASSWORD_OID;
115 }
116
117
118
119 /**
120 * Retrieves a description for this attribute syntax.
121 *
122 * @return A description for this attribute syntax.
123 */
124 public String getDescription()
125 {
126 return SYNTAX_AUTH_PASSWORD_DESCRIPTION;
127 }
128
129
130
131 /**
132 * Retrieves the default equality matching rule that will be used for
133 * attributes with this syntax.
134 *
135 * @return The default equality matching rule that will be used for
136 * attributes with this syntax, or <CODE>null</CODE> if equality
137 * matches will not be allowed for this type by default.
138 */
139 public EqualityMatchingRule getEqualityMatchingRule()
140 {
141 return defaultEqualityMatchingRule;
142 }
143
144
145
146 /**
147 * Retrieves the default ordering matching rule that will be used for
148 * attributes with this syntax.
149 *
150 * @return The default ordering matching rule that will be used for
151 * attributes with this syntax, or <CODE>null</CODE> if ordering
152 * matches will not be allowed for this type by default.
153 */
154 public OrderingMatchingRule getOrderingMatchingRule()
155 {
156 // There is no ordering matching rule by default.
157 return null;
158 }
159
160
161
162 /**
163 * Retrieves the default substring matching rule that will be used for
164 * attributes with this syntax.
165 *
166 * @return The default substring matching rule that will be used for
167 * attributes with this syntax, or <CODE>null</CODE> if substring
168 * matches will not be allowed for this type by default.
169 */
170 public SubstringMatchingRule getSubstringMatchingRule()
171 {
172 // There is no substring matching rule by default.
173 return null;
174 }
175
176
177
178 /**
179 * Retrieves the default approximate matching rule that will be used for
180 * attributes with this syntax.
181 *
182 * @return The default approximate matching rule that will be used for
183 * attributes with this syntax, or <CODE>null</CODE> if approximate
184 * matches will not be allowed for this type by default.
185 */
186 public ApproximateMatchingRule getApproximateMatchingRule()
187 {
188 // There is no approximate matching rule by default.
189 return null;
190 }
191
192
193
194 /**
195 * Indicates whether the provided value is acceptable for use in an attribute
196 * with this syntax. If it is not, then the reason may be appended to the
197 * provided buffer.
198 *
199 * @param value The value for which to make the determination.
200 * @param invalidReason The buffer to which the invalid reason should be
201 * appended.
202 *
203 * @return <CODE>true</CODE> if the provided value is acceptable for use with
204 * this syntax, or <CODE>false</CODE> if not.
205 */
206 public boolean valueIsAcceptable(ByteString value,
207 MessageBuilder invalidReason)
208 {
209 try
210 {
211 decodeAuthPassword(value.stringValue());
212 return true;
213 }
214 catch (DirectoryException de)
215 {
216 invalidReason.append(de.getMessageObject());
217 return false;
218 }
219 }
220
221
222
223 /**
224 * Decodes the provided authentication password value into its component
225 * parts.
226 *
227 * @param authPasswordValue The authentication password value to be decoded.
228 *
229 * @return A three-element array, containing the scheme, authInfo, and
230 * authValue components of the given string, in that order.
231 *
232 * @throws DirectoryException If a problem is encountered while attempting
233 * to decode the value.
234 */
235 public static StringBuilder[] decodeAuthPassword(String authPasswordValue)
236 throws DirectoryException
237 {
238 // Create placeholders for the values to return.
239 StringBuilder scheme = new StringBuilder();
240 StringBuilder authInfo = new StringBuilder();
241 StringBuilder authValue = new StringBuilder();
242
243
244 // First, ignore any leading whitespace.
245 int length = authPasswordValue.length();
246 int pos = 0;
247 while ((pos < length) && (authPasswordValue.charAt(pos) == ' '))
248 {
249 pos++;
250 }
251
252
253 // The next set of characters will be the scheme, which must consist only
254 // of digits, uppercase alphabetic characters, dash, period, slash, and
255 // underscore characters. It must be immediately followed by one or more
256 // spaces or a dollar sign.
257 readScheme:
258 while (pos < length)
259 {
260 char c = authPasswordValue.charAt(pos);
261
262 switch (c)
263 {
264 case '0':
265 case '1':
266 case '2':
267 case '3':
268 case '4':
269 case '5':
270 case '6':
271 case '7':
272 case '8':
273 case '9':
274 case 'A':
275 case 'B':
276 case 'C':
277 case 'D':
278 case 'E':
279 case 'F':
280 case 'G':
281 case 'H':
282 case 'I':
283 case 'J':
284 case 'K':
285 case 'L':
286 case 'M':
287 case 'N':
288 case 'O':
289 case 'P':
290 case 'Q':
291 case 'R':
292 case 'S':
293 case 'T':
294 case 'U':
295 case 'V':
296 case 'W':
297 case 'X':
298 case 'Y':
299 case 'Z':
300 case '-':
301 case '.':
302 case '/':
303 case '_':
304 scheme.append(c);
305 pos++;
306 break;
307 case ' ':
308 case '$':
309 break readScheme;
310 default:
311 Message message = ERR_ATTR_SYNTAX_AUTHPW_INVALID_SCHEME_CHAR.get(pos);
312 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
313 message);
314 }
315 }
316
317
318 // The scheme must consist of at least one character.
319 if (scheme.length() == 0)
320 {
321 Message message = ERR_ATTR_SYNTAX_AUTHPW_NO_SCHEME.get();
322 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
323 message);
324 }
325
326
327 // Ignore any spaces before the dollar sign separator. Then read the dollar
328 // sign and ignore any trailing spaces.
329 while ((pos < length) && (authPasswordValue.charAt(pos) == ' '))
330 {
331 pos++;
332 }
333
334 if ((pos < length) && (authPasswordValue.charAt(pos) == '$'))
335 {
336 pos++;
337 }
338 else
339 {
340 Message message = ERR_ATTR_SYNTAX_AUTHPW_NO_SCHEME_SEPARATOR.get();
341 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
342 message);
343 }
344
345 while ((pos < length) && (authPasswordValue.charAt(pos) == ' '))
346 {
347 pos++;
348 }
349
350
351 // The next component must be the authInfo element, containing only
352 // printable characters other than the dollar sign and space character.
353 readAuthInfo:
354 while (pos < length)
355 {
356 char c = authPasswordValue.charAt(pos);
357 if ((c == ' ') || (c == '$'))
358 {
359 break readAuthInfo;
360 }
361 else if (PrintableString.isPrintableCharacter(c))
362 {
363 authInfo.append(c);
364 pos++;
365 }
366 else
367 {
368 Message message =
369 ERR_ATTR_SYNTAX_AUTHPW_INVALID_AUTH_INFO_CHAR.get(pos);
370 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
371 message);
372 }
373 }
374
375
376 // The authInfo element must consist of at least one character.
377 if (scheme.length() == 0)
378 {
379 Message message = ERR_ATTR_SYNTAX_AUTHPW_NO_AUTH_INFO.get();
380 throw new DirectoryException(
381 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
382 }
383
384
385 // Ignore any spaces before the dollar sign separator. Then read the dollar
386 // sign and ignore any trailing spaces.
387 while ((pos < length) && (authPasswordValue.charAt(pos) == ' '))
388 {
389 pos++;
390 }
391
392 if ((pos < length) && (authPasswordValue.charAt(pos) == '$'))
393 {
394 pos++;
395 }
396 else
397 {
398 Message message = ERR_ATTR_SYNTAX_AUTHPW_NO_AUTH_INFO_SEPARATOR.get();
399 throw new DirectoryException(
400 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
401 }
402
403 while ((pos < length) && (authPasswordValue.charAt(pos) == ' '))
404 {
405 pos++;
406 }
407
408
409 // The final component must be the authValue element, containing only
410 // printable characters other than the dollar sign and space character.
411 readAuthValue:
412 while (pos < length)
413 {
414 char c = authPasswordValue.charAt(pos);
415 if ((c == ' ') || (c == '$'))
416 {
417 break ;
418 }
419 else if (PrintableString.isPrintableCharacter(c))
420 {
421 authValue.append(c);
422 pos++;
423 }
424 else
425 {
426 Message message =
427 ERR_ATTR_SYNTAX_AUTHPW_INVALID_AUTH_VALUE_CHAR.get(pos);
428 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
429 message);
430 }
431 }
432
433
434 // The authValue element must consist of at least one character.
435 if (scheme.length() == 0)
436 {
437 Message message = ERR_ATTR_SYNTAX_AUTHPW_NO_AUTH_VALUE.get();
438 throw new DirectoryException(
439 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
440 }
441
442
443 // The only characters remaining must be whitespace.
444 while (pos < length)
445 {
446 char c = authPasswordValue.charAt(pos);
447 if (c == ' ')
448 {
449 pos++;
450 }
451 else
452 {
453 Message message = ERR_ATTR_SYNTAX_AUTHPW_INVALID_TRAILING_CHAR.get(pos);
454 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
455 message);
456 }
457 }
458
459
460 // If we've gotten here, then everything must be OK.
461 return new StringBuilder[]
462 {
463 scheme,
464 authInfo,
465 authValue
466 };
467 }
468
469
470
471 /**
472 * Indicates whether the provided value is encoded using the auth password
473 * syntax.
474 *
475 * @param value The value for which to make the determination.
476 *
477 * @return <CODE>true</CODE> if the value appears to be encoded using the
478 * auth password syntax, or <CODE>false</CODE> if not.
479 */
480 public static boolean isEncoded(ByteString value)
481 {
482 // FIXME -- Make this more efficient, and don't use exceptions for flow
483 // control.
484
485
486 try
487 {
488 decodeAuthPassword(value.stringValue());
489 return true;
490 }
491 catch (Exception e)
492 {
493 return false;
494 }
495 }
496 }
497