001/* 002 * Copyright 2022-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2022-2024 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) 2022-2024 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.sdk.unboundidds.logs.v2.syntax; 037 038 039 040import com.unboundid.util.ByteStringBuffer; 041import com.unboundid.util.Debug; 042import com.unboundid.util.NotNull; 043import com.unboundid.util.ThreadSafety; 044import com.unboundid.util.ThreadSafetyLevel; 045import com.unboundid.util.json.JSONBuffer; 046 047import static com.unboundid.ldap.sdk.unboundidds.logs.v2.syntax. 048 LogSyntaxMessages.*; 049 050 051 052/** 053 * This class defines a log field syntax for values that are integers. This 054 * syntax does not support redacting or tokenizing individual components within 055 * the integers. Redacted integer values will have a string representation of 056 * "-999999999999999999". Tokenized integer values will have a string 057 * representation of "-999999999" followed by nine digits that correspond to a 058 * token value generated from the actual value. 059 * <BR> 060 * <BLOCKQUOTE> 061 * <B>NOTE:</B> This class, and other classes within the 062 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 063 * supported for use against Ping Identity, UnboundID, and 064 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 065 * for proprietary functionality or for external specifications that are not 066 * considered stable or mature enough to be guaranteed to work in an 067 * interoperable way with other types of LDAP servers. 068 * </BLOCKQUOTE> 069 */ 070@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 071public final class IntegerLogFieldSyntax 072 extends LogFieldSyntax<Long> 073{ 074 /** 075 * The name for this syntax. 076 */ 077 @NotNull public static final String SYNTAX_NAME = "integer"; 078 079 080 081 /** 082 * The string representation that will be used for a floating-point value that 083 * is completely redacted. 084 */ 085 @NotNull private static final String REDACTED_INTEGER_STRING = 086 "-999999999999999999"; 087 088 089 090 /** 091 * A singleton instance of this log field syntax. 092 */ 093 @NotNull private static final IntegerLogFieldSyntax INSTANCE = 094 new IntegerLogFieldSyntax(); 095 096 097 098 /** 099 * Creates a new instance of this log field syntax implementation. 100 */ 101 private IntegerLogFieldSyntax() 102 { 103 super(100); 104 } 105 106 107 108 /** 109 * Retrieves a singleton instance of this log field syntax. 110 * 111 * @return A singleton instance of this log field syntax. 112 */ 113 @NotNull() 114 public static IntegerLogFieldSyntax getInstance() 115 { 116 return INSTANCE; 117 } 118 119 120 121 /** 122 * {@inheritDoc} 123 */ 124 @Override() 125 @NotNull() 126 public String getSyntaxName() 127 { 128 return SYNTAX_NAME; 129 } 130 131 132 133 /** 134 * Appends a sanitized string representation of the provided integer to the 135 * given buffer. 136 * 137 * @param value The value to be appended. 138 * @param buffer The buffer to which the string representation should be 139 * appended. It must not be {@code null}. 140 */ 141 public void valueToSanitizedString(final int value, 142 @NotNull final ByteStringBuffer buffer) 143 { 144 buffer.append(value); 145 } 146 147 148 149 /** 150 * Appends a sanitized string representation of the provided long to the given 151 * buffer. 152 * 153 * @param value The value to be appended. 154 * @param buffer The buffer to which the string representation should be 155 * appended. It must not be {@code null}. 156 */ 157 public void valueToSanitizedString(final long value, 158 @NotNull final ByteStringBuffer buffer) 159 { 160 buffer.append(value); 161 } 162 163 164 165 /** 166 * {@inheritDoc} 167 */ 168 @Override() 169 public void valueToSanitizedString(@NotNull final Long value, 170 @NotNull final ByteStringBuffer buffer) 171 { 172 buffer.append(value); 173 } 174 175 176 177 /** 178 * {@inheritDoc} 179 */ 180 @Override() 181 public void logSanitizedFieldToTextFormattedLog( 182 @NotNull final String fieldName, 183 @NotNull final Long fieldValue, 184 @NotNull final ByteStringBuffer buffer) 185 { 186 buffer.append(' '); 187 buffer.append(fieldName); 188 buffer.append('='); 189 buffer.append(fieldValue.longValue()); 190 } 191 192 193 194 /** 195 * {@inheritDoc} 196 */ 197 @Override() 198 public void logSanitizedFieldToJSONFormattedLog( 199 @NotNull final String fieldName, 200 @NotNull final Long fieldValue, 201 @NotNull final JSONBuffer buffer) 202 { 203 buffer.appendNumber(fieldName, fieldValue); 204 } 205 206 207 208 /** 209 * {@inheritDoc} 210 */ 211 @Override() 212 public void logSanitizedValueToJSONFormattedLog( 213 @NotNull final Long value, 214 @NotNull final JSONBuffer buffer) 215 { 216 buffer.appendNumber(value); 217 } 218 219 220 221 /** 222 * {@inheritDoc} 223 */ 224 @Override() 225 @NotNull() 226 public Long parseValue(@NotNull final String valueString) 227 throws RedactedValueException, TokenizedValueException, 228 LogSyntaxException 229 { 230 try 231 { 232 return Long.parseLong(valueString); 233 } 234 catch (final Exception e) 235 { 236 Debug.debugException(e); 237 if (valueStringIncludesRedactedComponent(valueString)) 238 { 239 throw new RedactedValueException( 240 ERR_INTEGER_LOG_SYNTAX_CANNOT_PARSE_REDACTED.get(), e); 241 } 242 else if (valueStringIncludesTokenizedComponent(valueString)) 243 { 244 throw new TokenizedValueException( 245 ERR_INTEGER_LOG_SYNTAX_CANNOT_PARSE_TOKENIZED.get(), e); 246 } 247 else 248 { 249 throw new LogSyntaxException( 250 ERR_INTEGER_LOG_SYNTAX_CANNOT_PARSE.get(), e); 251 } 252 } 253 } 254 255 256 257 /** 258 * {@inheritDoc} 259 */ 260 @Override() 261 public boolean valueStringIsCompletelyRedacted( 262 @NotNull final String valueString) 263 { 264 return valueString.equals(REDACTED_STRING) || 265 valueString.equals(REDACTED_INTEGER_STRING); 266 } 267 268 269 270 /** 271 * {@inheritDoc} 272 */ 273 @Override() 274 public boolean completelyRedactedValueConformsToSyntax() 275 { 276 return true; 277 } 278 279 280 281 /** 282 * {@inheritDoc} 283 */ 284 @Override() 285 public void redactEntireValue(@NotNull final ByteStringBuffer buffer) 286 { 287 buffer.append(REDACTED_INTEGER_STRING); 288 } 289 290 291 292 /** 293 * {@inheritDoc} 294 */ 295 @Override() 296 public void logCompletelyRedactedFieldToTextFormattedLog( 297 @NotNull final String fieldName, 298 @NotNull final ByteStringBuffer buffer) 299 { 300 buffer.append(' '); 301 buffer.append(fieldName); 302 buffer.append('='); 303 buffer.append(REDACTED_INTEGER_STRING); 304 } 305 306 307 308 /** 309 * {@inheritDoc} 310 */ 311 @Override() 312 public void logCompletelyRedactedFieldToJSONFormattedLog( 313 @NotNull final String fieldName, 314 @NotNull final JSONBuffer buffer) 315 { 316 buffer.appendNumber(fieldName, REDACTED_INTEGER_STRING); 317 } 318 319 320 321 /** 322 * {@inheritDoc} 323 */ 324 @Override() 325 public void logCompletelyRedactedValueToJSONFormattedLog( 326 @NotNull final JSONBuffer buffer) 327 { 328 buffer.appendNumber(REDACTED_INTEGER_STRING); 329 } 330 331 332 333 /** 334 * {@inheritDoc} 335 */ 336 @Override() 337 public boolean supportsRedactedComponents() 338 { 339 return false; 340 } 341 342 343 344 /** 345 * {@inheritDoc} 346 */ 347 @Override() 348 public boolean valueStringIncludesRedactedComponent( 349 @NotNull final String valueString) 350 { 351 return valueStringIsCompletelyRedacted(valueString); 352 } 353 354 355 356 /** 357 * {@inheritDoc} 358 */ 359 @Override() 360 public boolean valueWithRedactedComponentsConformsToSyntax() 361 { 362 return true; 363 } 364 365 366 367 /** 368 * {@inheritDoc} 369 */ 370 @Override() 371 public void logRedactedComponentsFieldToTextFormattedLog( 372 @NotNull final String fieldName, 373 @NotNull final Long fieldValue, 374 @NotNull final ByteStringBuffer buffer) 375 { 376 logCompletelyRedactedFieldToTextFormattedLog(fieldName, buffer); 377 } 378 379 380 381 /** 382 * {@inheritDoc} 383 */ 384 @Override() 385 public void logRedactedComponentsFieldToJSONFormattedLog( 386 @NotNull final String fieldName, 387 @NotNull final Long fieldValue, 388 @NotNull final JSONBuffer buffer) 389 { 390 logCompletelyRedactedFieldToJSONFormattedLog(fieldName, buffer); 391 } 392 393 394 395 /** 396 * {@inheritDoc} 397 */ 398 @Override() 399 public void logRedactedComponentsValueToJSONFormattedLog( 400 @NotNull final Long value, 401 @NotNull final JSONBuffer buffer) 402 { 403 logCompletelyRedactedValueToJSONFormattedLog(buffer); 404 } 405 406 407 408 /** 409 * {@inheritDoc} 410 */ 411 @Override() 412 public boolean valueStringIsCompletelyTokenized( 413 @NotNull final String valueString) 414 { 415 if (super.valueStringIsCompletelyTokenized(valueString)) 416 { 417 return true; 418 } 419 420 return ((valueString.length() == 19) && 421 valueString.startsWith("-999999999") && 422 (! valueString.equals(REDACTED_INTEGER_STRING))); 423 } 424 425 426 427 /** 428 * {@inheritDoc} 429 */ 430 @Override() 431 public boolean completelyTokenizedValueConformsToSyntax() 432 { 433 return true; 434 } 435 436 437 438 /** 439 * {@inheritDoc} 440 */ 441 @Override() 442 public void tokenizeEntireValue(@NotNull final Long value, 443 @NotNull final byte[] pepper, 444 @NotNull final ByteStringBuffer buffer) 445 { 446 // Get the bytes that comprise the bitwise encoding of the provided value. 447 final long longValue = value; 448 final byte[] valueBytes = 449 { 450 (byte) ((longValue >> 56) & 0xFFL), 451 (byte) ((longValue >> 48) & 0xFFL), 452 (byte) ((longValue >> 40) & 0xFFL), 453 (byte) ((longValue >> 32) & 0xFFL), 454 (byte) ((longValue >> 24) & 0xFFL), 455 (byte) ((longValue >> 16) & 0xFFL), 456 (byte) ((longValue >> 8) & 0xFFL), 457 (byte) (longValue & 0xFFL) 458 }; 459 460 461 // Concatenate the value bytes and the pepper and compute a SHA-256 digest 462 // of the result. 463 final byte[] tokenDigest; 464 final ByteStringBuffer tempBuffer = getTemporaryBuffer(); 465 try 466 { 467 tempBuffer.append(valueBytes); 468 tempBuffer.append(pepper); 469 tokenDigest = sha256(tempBuffer); 470 } 471 finally 472 { 473 releaseTemporaryBuffer(tempBuffer); 474 } 475 476 477 // Use the first four bytes of the token digest to generate a positive 478 // integer whose string representation is exactly ten digits long. To do 479 // this, AND the first byte with 0x7F (which will make it positive) and OR 480 // the first byte with 0x40 (which will ensure that the value will be 481 // greater than or equal to 1073741824, and we already know that int 482 // values cannot exceed 2147483647, so that means it will be exactly ten 483 // digits). 484 final int tokenValueInt = 485 (((tokenDigest[0] & 0x7F) | 0x40) << 24) | 486 ((tokenDigest[1] & 0xFF) << 16) | 487 ((tokenDigest[2] & 0xFF) << 8) | 488 (tokenDigest[3] & 0xFF); 489 490 491 // Take the last nine digits of the string representation of the generated 492 // integer. 493 String tokenDigits = String.valueOf(tokenValueInt).substring(1); 494 495 496 // Make sure that the resulting nine-digit string is not "999999999", so 497 // that the tokenized value won't be confused with a redacted value. 498 if (tokenDigits.equals("999999999")) 499 { 500 tokenDigits = "000000000"; 501 } 502 503 504 // Finally, generate the tokenized representation. It will be "-999999999" 505 // followed by the token digits generated above. 506 buffer.append("-999999999"); 507 buffer.append(tokenDigits); 508 } 509 510 511 512 /** 513 * {@inheritDoc} 514 */ 515 @Override() 516 public void logCompletelyTokenizedFieldToTextFormattedLog( 517 @NotNull final String fieldName, 518 @NotNull final Long fieldValue, 519 @NotNull final byte[] pepper, 520 @NotNull final ByteStringBuffer buffer) 521 { 522 buffer.append(' '); 523 buffer.append(fieldName); 524 buffer.append('='); 525 tokenizeEntireValue(fieldValue, pepper, buffer); 526 } 527 528 529 530 /** 531 * {@inheritDoc} 532 */ 533 @Override() 534 public void logCompletelyTokenizedFieldToJSONFormattedLog( 535 @NotNull final String fieldName, 536 @NotNull final Long fieldValue, 537 @NotNull final byte[] pepper, 538 @NotNull final JSONBuffer buffer) 539 { 540 buffer.appendNumber(fieldName, tokenizeEntireValue(fieldValue, pepper)); 541 } 542 543 544 545 /** 546 * {@inheritDoc} 547 */ 548 @Override() 549 public void logCompletelyTokenizedValueToJSONFormattedLog( 550 @NotNull final Long value, 551 @NotNull final byte[] pepper, 552 @NotNull final JSONBuffer buffer) 553 { 554 buffer.appendNumber(tokenizeEntireValue(value, pepper)); 555 } 556 557 558 559 /** 560 * {@inheritDoc} 561 */ 562 @Override() 563 public boolean supportsTokenizedComponents() 564 { 565 return false; 566 } 567 568 569 570 /** 571 * {@inheritDoc} 572 */ 573 @Override() 574 public boolean valueStringIncludesTokenizedComponent( 575 @NotNull final String valueString) 576 { 577 return valueStringIsCompletelyTokenized(valueString); 578 } 579 580 581 582 /** 583 * {@inheritDoc} 584 */ 585 @Override() 586 public boolean valueWithTokenizedComponentsConformsToSyntax() 587 { 588 return true; 589 } 590 591 592 593 /** 594 * {@inheritDoc} 595 */ 596 @Override() 597 public void logTokenizedComponentsFieldToTextFormattedLog( 598 @NotNull final String fieldName, 599 @NotNull final Long fieldValue, 600 @NotNull final byte[] pepper, 601 @NotNull final ByteStringBuffer buffer) 602 { 603 logCompletelyTokenizedFieldToTextFormattedLog(fieldName, fieldValue, pepper, 604 buffer); 605 } 606 607 608 609 /** 610 * {@inheritDoc} 611 */ 612 @Override() 613 public void logTokenizedComponentsFieldToJSONFormattedLog( 614 @NotNull final String fieldName, 615 @NotNull final Long fieldValue, 616 @NotNull final byte[] pepper, 617 @NotNull final JSONBuffer buffer) 618 { 619 logCompletelyTokenizedFieldToJSONFormattedLog(fieldName, fieldValue, pepper, 620 buffer); 621 } 622 623 624 625 /** 626 * {@inheritDoc} 627 */ 628 @Override() 629 public void logTokenizedComponentsValueToJSONFormattedLog( 630 @NotNull final Long value, 631 @NotNull final byte[] pepper, 632 @NotNull final JSONBuffer buffer) 633 { 634 logCompletelyTokenizedValueToJSONFormattedLog(value, pepper, buffer); 635 } 636}