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.Debug; 034import com.unboundid.util.StaticUtils; 035import com.unboundid.util.ThreadSafety; 036import com.unboundid.util.ThreadSafetyLevel; 037 038import static com.unboundid.ldap.matchingrules.MatchingRuleMessages.*; 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 StaticUtils.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 StaticUtils.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<>(); 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 = StaticUtils.decodeGeneralizedTime(value1.stringValue()); 236 } 237 catch (final ParseException pe) 238 { 239 Debug.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 = StaticUtils.decodeGeneralizedTime(value2.stringValue()); 248 } 249 catch (final ParseException pe) 250 { 251 Debug.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 = 279 StaticUtils.decodeGeneralizedTime(assertionValue.stringValue()); 280 } 281 catch (final ParseException pe) 282 { 283 Debug.debugException(pe); 284 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 285 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 286 } 287 288 for (final ASN1OctetString attributeValue : attributeValues) 289 { 290 try 291 { 292 if (assertionValueDate.equals( 293 StaticUtils.decodeGeneralizedTime(attributeValue.stringValue()))) 294 { 295 return true; 296 } 297 } 298 catch (final Exception e) 299 { 300 Debug.debugException(e); 301 } 302 } 303 304 return false; 305 } 306 307 308 309 /** 310 * {@inheritDoc} 311 */ 312 @Override() 313 public boolean matchesSubstring(final ASN1OctetString value, 314 final ASN1OctetString subInitial, 315 final ASN1OctetString[] subAny, 316 final ASN1OctetString subFinal) 317 throws LDAPException 318 { 319 throw new LDAPException(ResultCode.INAPPROPRIATE_MATCHING, 320 ERR_GENERALIZED_TIME_SUBSTRING_MATCHING_NOT_SUPPORTED.get()); 321 } 322 323 324 325 /** 326 * {@inheritDoc} 327 */ 328 @Override() 329 public int compareValues(final ASN1OctetString value1, 330 final ASN1OctetString value2) 331 throws LDAPException 332 { 333 final Date d1; 334 try 335 { 336 d1 = StaticUtils.decodeGeneralizedTime(value1.stringValue()); 337 } 338 catch (final ParseException pe) 339 { 340 Debug.debugException(pe); 341 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 342 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 343 } 344 345 final Date d2; 346 try 347 { 348 d2 = StaticUtils.decodeGeneralizedTime(value2.stringValue()); 349 } 350 catch (final ParseException pe) 351 { 352 Debug.debugException(pe); 353 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 354 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 355 } 356 357 return d1.compareTo(d2); 358 } 359 360 361 362 /** 363 * {@inheritDoc} 364 */ 365 @Override() 366 public ASN1OctetString normalize(final ASN1OctetString value) 367 throws LDAPException 368 { 369 final Date d; 370 try 371 { 372 d = StaticUtils.decodeGeneralizedTime(value.stringValue()); 373 } 374 catch (final ParseException pe) 375 { 376 Debug.debugException(pe); 377 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 378 ERR_GENERALIZED_TIME_INVALID_VALUE.get(pe.getMessage()), pe); 379 } 380 381 SimpleDateFormat f = dateFormat.get(); 382 if (f == null) 383 { 384 f = new SimpleDateFormat(GENERALIZED_TIME_DATE_FORMAT); 385 f.setTimeZone(UTC_TIME_ZONE); 386 dateFormat.set(f); 387 } 388 389 return new ASN1OctetString(f.format(d)); 390 } 391 392 393 394 /** 395 * {@inheritDoc} 396 */ 397 @Override() 398 public ASN1OctetString normalizeSubstring(final ASN1OctetString value, 399 final byte substringType) 400 throws LDAPException 401 { 402 throw new LDAPException(ResultCode.INAPPROPRIATE_MATCHING, 403 ERR_GENERALIZED_TIME_SUBSTRING_MATCHING_NOT_SUPPORTED.get()); 404 } 405}