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