001/* 002 * Copyright 2008-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.matchingrules; 022 023 024 025import java.text.ParseException; 026import java.text.SimpleDateFormat; 027import java.util.Date; 028import java.util.TimeZone; 029 030import com.unboundid.asn1.ASN1OctetString; 031import com.unboundid.ldap.sdk.LDAPException; 032import com.unboundid.ldap.sdk.ResultCode; 033import com.unboundid.util.ThreadSafety; 034import com.unboundid.util.ThreadSafetyLevel; 035 036import static com.unboundid.ldap.matchingrules.MatchingRuleMessages.*; 037import static com.unboundid.util.Debug.*; 038import static com.unboundid.util.StaticUtils.*; 039 040 041 042/** 043 * This class provides an implementation of a matching rule that performs 044 * equality and ordering comparisons against values that should be timestamps 045 * in the generalized time syntax. Substring matching is not supported. 046 */ 047@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 048public final class GeneralizedTimeMatchingRule 049 extends MatchingRule 050{ 051 /** 052 * The singleton instance that will be returned from the {@code getInstance} 053 * method. 054 */ 055 private static final GeneralizedTimeMatchingRule INSTANCE = 056 new GeneralizedTimeMatchingRule(); 057 058 059 060 /** 061 * The date format that will be used for formatting generalized time values, 062 * assuming that the associated formatter is using the UTC time zone. 063 */ 064 private static final String GENERALIZED_TIME_DATE_FORMAT = 065 "yyyyMMddHHmmss.SSS'Z'"; 066 067 068 069 /** 070 * A reference to the "UTC" time zone. 071 */ 072 private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC"); 073 074 075 076 /** 077 * The name for the generalizedTimeMatch equality matching rule. 078 */ 079 public static final String EQUALITY_RULE_NAME = "generalizedTimeMatch"; 080 081 082 083 /** 084 * The name for the generalizedTimeMatch equality matching rule, formatted in 085 * all lowercase characters. 086 */ 087 static final String LOWER_EQUALITY_RULE_NAME = 088 toLowerCase(EQUALITY_RULE_NAME); 089 090 091 092 /** 093 * The OID for the generalizedTimeMatch equality matching rule. 094 */ 095 public static final String EQUALITY_RULE_OID = "2.5.13.27"; 096 097 098 099 /** 100 * The name for the generalizedTimeOrderingMatch ordering matching rule. 101 */ 102 public static final String ORDERING_RULE_NAME = 103 "generalizedTimeOrderingMatch"; 104 105 106 107 /** 108 * The name for the generalizedTimeOrderingMatch ordering matching rule, 109 * formatted in all lowercase characters. 110 */ 111 static final String LOWER_ORDERING_RULE_NAME = 112 toLowerCase(ORDERING_RULE_NAME); 113 114 115 116 /** 117 * The OID for the generalizedTimeOrderingMatch ordering matching rule. 118 */ 119 public static final String ORDERING_RULE_OID = "2.5.13.28"; 120 121 122 123 /** 124 * The serial version UID for this serializable class. 125 */ 126 private static final long serialVersionUID = -6317451154598148593L; 127 128 129 130 // The thread-local date formatter for this class. 131 private static final ThreadLocal<SimpleDateFormat> dateFormat = 132 new ThreadLocal<SimpleDateFormat>(); 133 134 135 136 /** 137 * Creates a new instance of this generalized time matching rule. 138 */ 139 public GeneralizedTimeMatchingRule() 140 { 141 // No implementation is required. 142 } 143 144 145 146 /** 147 * Retrieves a singleton instance of this matching rule. 148 * 149 * @return A singleton instance of this matching rule. 150 */ 151 public static GeneralizedTimeMatchingRule getInstance() 152 { 153 return INSTANCE; 154 } 155 156 157 158 /** 159 * {@inheritDoc} 160 */ 161 @Override() 162 public String getEqualityMatchingRuleName() 163 { 164 return EQUALITY_RULE_NAME; 165 } 166 167 168 169 /** 170 * {@inheritDoc} 171 */ 172 @Override() 173 public String getEqualityMatchingRuleOID() 174 { 175 return EQUALITY_RULE_OID; 176 } 177 178 179 180 /** 181 * {@inheritDoc} 182 */ 183 @Override() 184 public String getOrderingMatchingRuleName() 185 { 186 return ORDERING_RULE_NAME; 187 } 188 189 190 191 /** 192 * {@inheritDoc} 193 */ 194 @Override() 195 public String getOrderingMatchingRuleOID() 196 { 197 return ORDERING_RULE_OID; 198 } 199 200 201 202 /** 203 * {@inheritDoc} 204 */ 205 @Override() 206 public String getSubstringMatchingRuleName() 207 { 208 return null; 209 } 210 211 212 213 /** 214 * {@inheritDoc} 215 */ 216 @Override() 217 public String getSubstringMatchingRuleOID() 218 { 219 return null; 220 } 221 222 223 224 /** 225 * {@inheritDoc} 226 */ 227 @Override() 228 public boolean valuesMatch(final ASN1OctetString value1, 229 final ASN1OctetString value2) 230 throws LDAPException 231 { 232 final Date d1; 233 try 234 { 235 d1 = decodeGeneralizedTime(value1.stringValue()); 236 } 237 catch (final ParseException pe) 238 { 239 debugException(pe); 240 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 241 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 242 } 243 244 final Date d2; 245 try 246 { 247 d2 = decodeGeneralizedTime(value2.stringValue()); 248 } 249 catch (final ParseException pe) 250 { 251 debugException(pe); 252 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 253 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 254 } 255 256 return d1.equals(d2); 257 } 258 259 260 261 /** 262 * {@inheritDoc} 263 */ 264 @Override() 265 public boolean matchesAnyValue(final ASN1OctetString assertionValue, 266 final ASN1OctetString[] attributeValues) 267 throws LDAPException 268 { 269 if ((assertionValue == null) || (attributeValues == null) || 270 (attributeValues.length == 0)) 271 { 272 return false; 273 } 274 275 final Date assertionValueDate; 276 try 277 { 278 assertionValueDate = decodeGeneralizedTime(assertionValue.stringValue()); 279 } 280 catch (final ParseException pe) 281 { 282 debugException(pe); 283 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 284 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 285 } 286 287 for (final ASN1OctetString attributeValue : attributeValues) 288 { 289 try 290 { 291 if (assertionValueDate.equals( 292 decodeGeneralizedTime(attributeValue.stringValue()))) 293 { 294 return true; 295 } 296 } 297 catch (final Exception e) 298 { 299 debugException(e); 300 } 301 } 302 303 return false; 304 } 305 306 307 308 /** 309 * {@inheritDoc} 310 */ 311 @Override() 312 public boolean matchesSubstring(final ASN1OctetString value, 313 final ASN1OctetString subInitial, 314 final ASN1OctetString[] subAny, 315 final ASN1OctetString subFinal) 316 throws LDAPException 317 { 318 throw new LDAPException(ResultCode.INAPPROPRIATE_MATCHING, 319 ERR_GENERALIZED_TIME_SUBSTRING_MATCHING_NOT_SUPPORTED.get()); 320 } 321 322 323 324 /** 325 * {@inheritDoc} 326 */ 327 @Override() 328 public int compareValues(final ASN1OctetString value1, 329 final ASN1OctetString value2) 330 throws LDAPException 331 { 332 final Date d1; 333 try 334 { 335 d1 = decodeGeneralizedTime(value1.stringValue()); 336 } 337 catch (final ParseException pe) 338 { 339 debugException(pe); 340 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 341 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 342 } 343 344 final Date d2; 345 try 346 { 347 d2 = decodeGeneralizedTime(value2.stringValue()); 348 } 349 catch (final ParseException pe) 350 { 351 debugException(pe); 352 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 353 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 354 } 355 356 return d1.compareTo(d2); 357 } 358 359 360 361 /** 362 * {@inheritDoc} 363 */ 364 @Override() 365 public ASN1OctetString normalize(final ASN1OctetString value) 366 throws LDAPException 367 { 368 final Date d; 369 try 370 { 371 d = decodeGeneralizedTime(value.stringValue()); 372 } 373 catch (final ParseException pe) 374 { 375 debugException(pe); 376 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 377 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 378 } 379 380 SimpleDateFormat f = dateFormat.get(); 381 if (f == null) 382 { 383 f = new SimpleDateFormat(GENERALIZED_TIME_DATE_FORMAT); 384 f.setTimeZone(UTC_TIME_ZONE); 385 dateFormat.set(f); 386 } 387 388 return new ASN1OctetString(f.format(d)); 389 } 390 391 392 393 /** 394 * {@inheritDoc} 395 */ 396 @Override() 397 public ASN1OctetString normalizeSubstring(final ASN1OctetString value, 398 final byte substringType) 399 throws LDAPException 400 { 401 throw new LDAPException(ResultCode.INAPPROPRIATE_MATCHING, 402 ERR_GENERALIZED_TIME_SUBSTRING_MATCHING_NOT_SUPPORTED.get()); 403 } 404}