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