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 java.util.Arrays; 041import java.util.Collection; 042import java.util.Set; 043 044import com.unboundid.ldap.sdk.Attribute; 045import com.unboundid.ldap.sdk.Filter; 046import com.unboundid.ldap.sdk.schema.Schema; 047import com.unboundid.util.ByteStringBuffer; 048import com.unboundid.util.Debug; 049import com.unboundid.util.NotNull; 050import com.unboundid.util.Nullable; 051import com.unboundid.util.StaticUtils; 052import com.unboundid.util.ThreadSafety; 053import com.unboundid.util.ThreadSafetyLevel; 054import com.unboundid.util.json.JSONBuffer; 055 056import static com.unboundid.ldap.sdk.unboundidds.logs.v2.syntax. 057 LogSyntaxMessages.*; 058 059 060 061/** 062 * This class defines a log field syntax for search filter values. This syntax 063 * allows individual attribute values to be redacted or tokenized within the 064 * filters. If a filter is completely redacted, then the redacted 065 * representation will be "<code>(redacted={REDACTED})</code>". If a filter is 066 * completely tokenized, then the tokenized representation will be 067 * "<code>(tokenized={TOKENIZED:token-value})</code>", where token-value will be 068 * replaced with a generated value. 069 * <BR> 070 * <BLOCKQUOTE> 071 * <B>NOTE:</B> This class, and other classes within the 072 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 073 * supported for use against Ping Identity, UnboundID, and 074 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 075 * for proprietary functionality or for external specifications that are not 076 * considered stable or mature enough to be guaranteed to work in an 077 * interoperable way with other types of LDAP servers. 078 * </BLOCKQUOTE> 079 */ 080@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 081public final class FilterLogFieldSyntax 082 extends LogFieldSyntax<Filter> 083{ 084 /** 085 * The name for this syntax. 086 */ 087 @NotNull public static final String SYNTAX_NAME = "filter"; 088 089 090 091 /** 092 * The string representation that will be used for a DN that is completely 093 * redacted. 094 */ 095 @NotNull private static final String REDACTED_FILTER_STRING = 096 "(redacted={REDACTED})"; 097 098 099 100 // Indicates whether all attributes should be considered sensitive when 101 // redacting or tokenizing components. 102 private final boolean allAttributesAreSensitive; 103 104 // The set of the names and OIDs for the specific attributes whose values 105 // should not be redacted or tokenized. 106 @NotNull private final Set<String> excludedSensitiveAttributes; 107 108 // The set of the names and OIDs for the specific attributes whose values 109 // should be redacted or tokenized. 110 @NotNull private final Set<String> includedSensitiveAttributes; 111 112 113 114 /** 115 * Creates a new filter log field syntax instance that can optionally 116 * define specific attributes to include in or exclude from redaction or 117 * tokenization. If any include attributes are specified, then only the 118 * values of those attributes will be considered sensitive and will have 119 * their values tokenized or redacted. If any exclude 120 * attributes are specified, then the values of any attributes except those 121 * will be considered sensitive. If no include attributes and no exclude 122 * attributes are specified, then all attributes will be considered sensitive 123 * and will have their values tokenized or redacted. 124 * 125 * @param maxStringLengthCharacters The maximum length (in characters) to 126 * use for strings within values. 127 * Strings that are longer than this 128 * should be truncated before inclusion 129 * in the log. This value must be 130 * greater than or equal to zero. 131 * @param schema The schema to use in processing. It 132 * may optionally be {@code null} if no 133 * schema should be used. 134 * @param includedSensitiveAttributes The set of names and OIDs for the 135 * specific attributes whose values 136 * should be considered sensitive and 137 * should have their values redacted or 138 * tokenized by methods that operate on 139 * value components. This may be 140 * {@code null} or empty if no included 141 * sensitive attributes should be 142 * defined. 143 * @param excludedSensitiveAttributes The set of names and OIDs for the 144 * specific attributes whose values 145 * should not be considered sensitive and 146 * should not have their values redacted 147 * or tokenized by methods that operate 148 * on value components. This may be 149 * {@code null} or empty if no excluded 150 * sensitive attributes should be 151 * defined. 152 */ 153 public FilterLogFieldSyntax( 154 final int maxStringLengthCharacters, 155 @Nullable final Schema schema, 156 @Nullable final Collection<String> includedSensitiveAttributes, 157 @Nullable final Collection<String> excludedSensitiveAttributes) 158 { 159 super(maxStringLengthCharacters); 160 161 this.includedSensitiveAttributes = 162 AttributeBasedLogFieldSyntaxHelper.getAttributeSet(schema, 163 includedSensitiveAttributes); 164 this.excludedSensitiveAttributes = 165 AttributeBasedLogFieldSyntaxHelper.getAttributeSet(schema, 166 excludedSensitiveAttributes); 167 168 allAttributesAreSensitive = this.includedSensitiveAttributes.isEmpty() && 169 this.excludedSensitiveAttributes.isEmpty(); 170 } 171 172 173 174 /** 175 * Retrieves a set containing the names and/or OIDs of the attributes that 176 * will be considered sensitive and will have their values redacted or 177 * tokenized in methods that operate on filter components. 178 * 179 * @return A set containing the names and/or OIDs of the attributes that will 180 * be considered sensitive, or an empty list if no included sensitive 181 * attributes are defined. 182 */ 183 @NotNull() 184 public Set<String> getIncludedSensitiveAttributes() 185 { 186 return includedSensitiveAttributes; 187 } 188 189 190 191 /** 192 * Retrieves a set containing the names and/or OIDs of the attributes that 193 * will not be considered sensitive and will have not their values redacted or 194 * tokenized in methods that operate on filter components. 195 * 196 * @return A set containing the names and/or OIDs of the attributes that will 197 * not be considered sensitive, or an empty list if no excluded 198 * sensitive attributes are defined. 199 */ 200 @NotNull() 201 public Set<String> getExcludedSensitiveAttributes() 202 { 203 return excludedSensitiveAttributes; 204 } 205 206 207 208 /** 209 * {@inheritDoc} 210 */ 211 @Override() 212 @NotNull() 213 public String getSyntaxName() 214 { 215 return SYNTAX_NAME; 216 } 217 218 219 220 /** 221 * {@inheritDoc} 222 */ 223 @Override() 224 public void valueToSanitizedString(@NotNull final Filter value, 225 @NotNull final ByteStringBuffer buffer) 226 { 227 buffer.append(sanitizeFilter(value).toString()); 228 } 229 230 231 232 /** 233 * Retrieves a sanitized version of the provided filter. 234 * 235 * @param filter The filter to sanitize. It must not be {@code null}. 236 * 237 * @return A sanitized version of the provided filter. 238 */ 239 @NotNull() 240 private Filter sanitizeFilter(@NotNull final Filter filter) 241 { 242 switch (filter.getFilterType()) 243 { 244 case Filter.FILTER_TYPE_AND: 245 final Filter[] originalANDComponents = filter.getComponents(); 246 final Filter[] sanitizedANDComponents = 247 new Filter[originalANDComponents.length]; 248 for (int i=0; i < originalANDComponents.length; i++) 249 { 250 sanitizedANDComponents[i] = sanitizeFilter(originalANDComponents[i]); 251 } 252 return Filter.createANDFilter(sanitizedANDComponents); 253 254 case Filter.FILTER_TYPE_OR: 255 final Filter[] originalORComponents = filter.getComponents(); 256 final Filter[] sanitizedORComponents = 257 new Filter[originalORComponents.length]; 258 for (int i=0; i < originalORComponents.length; i++) 259 { 260 sanitizedORComponents[i] = sanitizeFilter(originalORComponents[i]); 261 } 262 return Filter.createORFilter(sanitizedORComponents); 263 264 case Filter.FILTER_TYPE_NOT: 265 return Filter.createNOTFilter(sanitizeFilter(filter.getNOTComponent())); 266 267 case Filter.FILTER_TYPE_EQUALITY: 268 return Filter.createEqualityFilter(filter.getAttributeName(), 269 sanitize(filter.getAssertionValue())); 270 271 case Filter.FILTER_TYPE_GREATER_OR_EQUAL: 272 return Filter.createGreaterOrEqualFilter(filter.getAttributeName(), 273 sanitize(filter.getAssertionValue())); 274 275 case Filter.FILTER_TYPE_LESS_OR_EQUAL: 276 return Filter.createLessOrEqualFilter(filter.getAttributeName(), 277 sanitize(filter.getAssertionValue())); 278 279 case Filter.FILTER_TYPE_APPROXIMATE_MATCH: 280 return Filter.createApproximateMatchFilter(filter.getAttributeName(), 281 sanitize(filter.getAssertionValue())); 282 283 case Filter.FILTER_TYPE_SUBSTRING: 284 final String originalSubInitial = filter.getSubInitialString(); 285 final String sanitizedSubInitial = 286 (originalSubInitial == null) ? null : sanitize(originalSubInitial); 287 288 final String originalSubFinal = filter.getSubFinalString(); 289 final String sanitizedSubFinal = 290 (originalSubFinal == null) ? null : sanitize(originalSubFinal); 291 292 final String[] originalSubAny = filter.getSubAnyStrings(); 293 final String[] sanitizedSubAny = new String[originalSubAny.length]; 294 for (int i=0; i < originalSubAny.length; i++) 295 { 296 sanitizedSubAny[i] = sanitize(originalSubAny[i]); 297 } 298 299 return Filter.createSubstringFilter(filter.getAttributeName(), 300 sanitizedSubInitial, sanitizedSubAny, sanitizedSubFinal); 301 302 case Filter.FILTER_TYPE_EXTENSIBLE_MATCH: 303 return Filter.createExtensibleMatchFilter(filter.getAttributeName(), 304 filter.getMatchingRuleID(), filter.getDNAttributes(), 305 sanitize(filter.getAssertionValue())); 306 307 case Filter.FILTER_TYPE_PRESENCE: 308 default: 309 return filter; 310 } 311 } 312 313 314 315 /** 316 * {@inheritDoc} 317 */ 318 @Override() 319 public void logSanitizedFieldToTextFormattedLog( 320 @NotNull final String fieldName, 321 @NotNull final Filter fieldValue, 322 @NotNull final ByteStringBuffer buffer) 323 { 324 buffer.append(' '); 325 buffer.append(fieldName); 326 buffer.append("=\""); 327 valueToSanitizedString(fieldValue, buffer); 328 buffer.append('"'); 329 } 330 331 332 333 /** 334 * {@inheritDoc} 335 */ 336 @Override() 337 public void logSanitizedFieldToJSONFormattedLog( 338 @NotNull final String fieldName, 339 @NotNull final Filter fieldValue, 340 @NotNull final JSONBuffer buffer) 341 { 342 buffer.appendString(fieldName, valueToSanitizedString(fieldValue)); 343 } 344 345 346 347 /** 348 * {@inheritDoc} 349 */ 350 @Override() 351 public void logSanitizedValueToJSONFormattedLog( 352 @NotNull final Filter value, 353 @NotNull final JSONBuffer buffer) 354 { 355 buffer.appendString(valueToSanitizedString(value)); 356 } 357 358 359 360 /** 361 * {@inheritDoc} 362 */ 363 @Override() 364 @NotNull() 365 public Filter parseValue(@NotNull final String valueString) 366 throws RedactedValueException, TokenizedValueException, 367 LogSyntaxException 368 { 369 try 370 { 371 return Filter.create(valueString); 372 } 373 catch (final Exception e) 374 { 375 Debug.debugException(e); 376 377 if (valueStringIsCompletelyRedacted(valueString)) 378 { 379 throw new RedactedValueException( 380 ERR_FILTER_LOG_SYNTAX_CANNOT_PARSE_REDACTED.get(), e); 381 } 382 else if (valueStringIsCompletelyTokenized(valueString)) 383 { 384 throw new TokenizedValueException( 385 ERR_FILTER_LOG_SYNTAX_CANNOT_PARSE_TOKENIZED.get(), e); 386 } 387 else 388 { 389 throw new LogSyntaxException( 390 ERR_FILTER_LOG_SYNTAX_CANNOT_PARSE.get(), e); 391 } 392 } 393 } 394 395 396 397 /** 398 * {@inheritDoc} 399 */ 400 @Override() 401 public boolean valueStringIsCompletelyRedacted( 402 @NotNull final String valueString) 403 { 404 return valueString.equals(REDACTED_STRING) || 405 valueString.equals(REDACTED_FILTER_STRING); 406 } 407 408 409 410 /** 411 * {@inheritDoc} 412 */ 413 @Override() 414 public boolean completelyRedactedValueConformsToSyntax() 415 { 416 return true; 417 } 418 419 420 421 /** 422 * {@inheritDoc} 423 */ 424 @Override() 425 public void redactEntireValue(@NotNull final ByteStringBuffer buffer) 426 { 427 buffer.append(REDACTED_FILTER_STRING); 428 } 429 430 431 432 /** 433 * {@inheritDoc} 434 */ 435 @Override() 436 public void logCompletelyRedactedFieldToTextFormattedLog( 437 @NotNull final String fieldName, 438 @NotNull final ByteStringBuffer buffer) 439 { 440 buffer.append(' '); 441 buffer.append(fieldName); 442 buffer.append("=\""); 443 buffer.append(REDACTED_FILTER_STRING); 444 buffer.append('"'); 445 } 446 447 448 449 /** 450 * {@inheritDoc} 451 */ 452 @Override() 453 public void logCompletelyRedactedFieldToJSONFormattedLog( 454 @NotNull final String fieldName, 455 @NotNull final JSONBuffer buffer) 456 { 457 buffer.appendString(fieldName, REDACTED_FILTER_STRING); 458 } 459 460 461 462 /** 463 * {@inheritDoc} 464 */ 465 @Override() 466 public void logCompletelyRedactedValueToJSONFormattedLog( 467 @NotNull final JSONBuffer buffer) 468 { 469 buffer.appendString(REDACTED_FILTER_STRING); 470 } 471 472 473 474 /** 475 * {@inheritDoc} 476 */ 477 @Override() 478 public boolean supportsRedactedComponents() 479 { 480 return true; 481 } 482 483 484 485 /** 486 * {@inheritDoc} 487 */ 488 @Override() 489 public boolean valueWithRedactedComponentsConformsToSyntax() 490 { 491 return true; 492 } 493 494 495 496 /** 497 * {@inheritDoc} 498 */ 499 @Override() 500 public void redactComponents(@NotNull final Filter value, 501 @NotNull final ByteStringBuffer buffer) 502 { 503 buffer.append(redactFilter(value).toString()); 504 } 505 506 507 508 /** 509 * Retrieves a redacted version of the provided filter. 510 * 511 * @param filter The filter to redact. It must not be {@code null}. 512 * 513 * @return A redacted version of the provided filter. 514 */ 515 @NotNull() 516 private Filter redactFilter(@NotNull final Filter filter) 517 { 518 switch (filter.getFilterType()) 519 { 520 case Filter.FILTER_TYPE_AND: 521 final Filter[] originalANDComponents = filter.getComponents(); 522 final Filter[] redactedANDComponents = 523 new Filter[originalANDComponents.length]; 524 for (int i=0; i < originalANDComponents.length; i++) 525 { 526 redactedANDComponents[i] = redactFilter(originalANDComponents[i]); 527 } 528 return Filter.createANDFilter(redactedANDComponents); 529 530 case Filter.FILTER_TYPE_OR: 531 final Filter[] originalORComponents = filter.getComponents(); 532 final Filter[] redactedORComponents = 533 new Filter[originalORComponents.length]; 534 for (int i=0; i < originalORComponents.length; i++) 535 { 536 redactedORComponents[i] = redactFilter(originalORComponents[i]); 537 } 538 return Filter.createORFilter(redactedORComponents); 539 540 case Filter.FILTER_TYPE_NOT: 541 return Filter.createNOTFilter(redactFilter(filter.getNOTComponent())); 542 543 case Filter.FILTER_TYPE_EQUALITY: 544 if (shouldRedactOrTokenize(filter.getAttributeName())) 545 { 546 return Filter.createEqualityFilter(filter.getAttributeName(), 547 REDACTED_STRING); 548 } 549 else 550 { 551 return Filter.createEqualityFilter(filter.getAttributeName(), 552 sanitize(filter.getAssertionValue())); 553 } 554 555 case Filter.FILTER_TYPE_GREATER_OR_EQUAL: 556 if (shouldRedactOrTokenize(filter.getAttributeName())) 557 { 558 return Filter.createGreaterOrEqualFilter(filter.getAttributeName(), 559 REDACTED_STRING); 560 } 561 else 562 { 563 return Filter.createGreaterOrEqualFilter(filter.getAttributeName(), 564 sanitize(filter.getAssertionValue())); 565 } 566 567 case Filter.FILTER_TYPE_LESS_OR_EQUAL: 568 if (shouldRedactOrTokenize(filter.getAttributeName())) 569 { 570 return Filter.createLessOrEqualFilter(filter.getAttributeName(), 571 REDACTED_STRING); 572 } 573 else 574 { 575 return Filter.createLessOrEqualFilter(filter.getAttributeName(), 576 sanitize(filter.getAssertionValue())); 577 } 578 579 case Filter.FILTER_TYPE_APPROXIMATE_MATCH: 580 if (shouldRedactOrTokenize(filter.getAttributeName())) 581 { 582 return Filter.createApproximateMatchFilter(filter.getAttributeName(), 583 REDACTED_STRING); 584 } 585 else 586 { 587 return Filter.createApproximateMatchFilter(filter.getAttributeName(), 588 sanitize(filter.getAssertionValue())); 589 } 590 591 case Filter.FILTER_TYPE_SUBSTRING: 592 final String redactedSubInitial; 593 final String redactedSubFinal; 594 final String originalSubInitial = filter.getSubInitialString(); 595 final String originalSubFinal = filter.getSubFinalString(); 596 final String[] originalSubAny = filter.getSubAnyStrings(); 597 final String[] redactedSubAny = new String[originalSubAny.length]; 598 if (shouldRedactOrTokenize(filter.getAttributeName())) 599 { 600 redactedSubInitial = 601 (originalSubInitial == null) ? null : REDACTED_STRING; 602 redactedSubFinal = 603 (originalSubFinal == null) ? null : REDACTED_STRING; 604 Arrays.fill(redactedSubAny, REDACTED_STRING); 605 } 606 else 607 { 608 redactedSubInitial = (originalSubInitial == null) 609 ? null 610 : sanitize(originalSubInitial); 611 redactedSubFinal = (originalSubFinal == null) 612 ? null 613 : sanitize(originalSubFinal); 614 for (int i=0; i < originalSubAny.length; i++) 615 { 616 redactedSubAny[i] = sanitize(originalSubAny[i]); 617 } 618 } 619 620 return Filter.createSubstringFilter(filter.getAttributeName(), 621 redactedSubInitial, redactedSubAny, redactedSubFinal); 622 623 case Filter.FILTER_TYPE_EXTENSIBLE_MATCH: 624 if (shouldRedactOrTokenize(filter.getAttributeName())) 625 { 626 return Filter.createExtensibleMatchFilter(filter.getAttributeName(), 627 filter.getMatchingRuleID(), filter.getDNAttributes(), 628 REDACTED_STRING); 629 } 630 else 631 { 632 return Filter.createExtensibleMatchFilter(filter.getAttributeName(), 633 filter.getMatchingRuleID(), filter.getDNAttributes(), 634 sanitize(filter.getAssertionValue())); 635 } 636 637 case Filter.FILTER_TYPE_PRESENCE: 638 return filter; 639 640 default: 641 // This should never happen. 642 return Filter.createEqualityFilter("redacted", "{REDACTED}"); 643 } 644 } 645 646 647 648 /** 649 * Indicates whether values of the specified attribute should be redacted or 650 * tokenized. 651 * 652 * @param attributeName The name or OID of the attribute for which to make 653 * the determination. It may be {@code null} if no 654 * attribute name is available. 655 * 656 * @return {@code true} if values of the specified attribute should be 657 * redacted or tokenized, or {@code false} if not. 658 */ 659 private boolean shouldRedactOrTokenize(@Nullable final String attributeName) 660 { 661 if (allAttributesAreSensitive) 662 { 663 return true; 664 } 665 666 if (attributeName == null) 667 { 668 return (! excludedSensitiveAttributes.isEmpty()); 669 } 670 671 final String lowerName = 672 StaticUtils.toLowerCase(Attribute.getBaseName(attributeName)); 673 if (includedSensitiveAttributes.contains(lowerName)) 674 { 675 return true; 676 } 677 678 if (excludedSensitiveAttributes.isEmpty()) 679 { 680 return false; 681 } 682 else 683 { 684 return (! excludedSensitiveAttributes.contains(lowerName)); 685 } 686 } 687 688 689 690 /** 691 * {@inheritDoc} 692 */ 693 @Override() 694 public void logRedactedComponentsFieldToTextFormattedLog( 695 @NotNull final String fieldName, 696 @NotNull final Filter fieldValue, 697 @NotNull final ByteStringBuffer buffer) 698 { 699 buffer.append(' '); 700 buffer.append(fieldName); 701 buffer.append("=\""); 702 redactComponents(fieldValue, buffer); 703 buffer.append('"'); 704 } 705 706 707 708 /** 709 * {@inheritDoc} 710 */ 711 @Override() 712 public void logRedactedComponentsFieldToJSONFormattedLog( 713 @NotNull final String fieldName, 714 @NotNull final Filter fieldValue, 715 @NotNull final JSONBuffer buffer) 716 { 717 buffer.appendString(fieldName, redactComponents(fieldValue)); 718 } 719 720 721 722 /** 723 * {@inheritDoc} 724 */ 725 @Override() 726 public void logRedactedComponentsValueToJSONFormattedLog( 727 @NotNull final Filter value, 728 @NotNull final JSONBuffer buffer) 729 { 730 buffer.appendString(redactComponents(value)); 731 } 732 733 734 735 /** 736 * {@inheritDoc} 737 */ 738 @Override() 739 public boolean valueStringIsCompletelyTokenized( 740 @NotNull final String valueString) 741 { 742 return super.valueStringIsCompletelyTokenized(valueString) || 743 (valueString.startsWith("(tokenized="+ TOKEN_PREFIX_STRING) && 744 valueString.endsWith(TOKEN_SUFFIX_STRING + ')')); 745 } 746 747 748 749 /** 750 * {@inheritDoc} 751 */ 752 @Override() 753 public boolean completelyTokenizedValueConformsToSyntax() 754 { 755 return true; 756 } 757 758 759 760 /** 761 * {@inheritDoc} 762 */ 763 @Override() 764 public void tokenizeEntireValue(@NotNull final Filter value, 765 @NotNull final byte[] pepper, 766 @NotNull final ByteStringBuffer buffer) 767 { 768 buffer.append("(tokenized="); 769 tokenize(value.toNormalizedString(), pepper, buffer); 770 buffer.append(')'); 771 } 772 773 774 775 /** 776 * {@inheritDoc} 777 */ 778 @Override() 779 public void logCompletelyTokenizedFieldToTextFormattedLog( 780 @NotNull final String fieldName, 781 @NotNull final Filter fieldValue, 782 @NotNull final byte[] pepper, 783 @NotNull final ByteStringBuffer buffer) 784 { 785 buffer.append(' '); 786 buffer.append(fieldName); 787 buffer.append("=\""); 788 tokenizeEntireValue(fieldValue, pepper, buffer); 789 buffer.append('"'); 790 } 791 792 793 794 /** 795 * {@inheritDoc} 796 */ 797 @Override() 798 public void logCompletelyTokenizedFieldToJSONFormattedLog( 799 @NotNull final String fieldName, 800 @NotNull final Filter fieldValue, 801 @NotNull final byte[] pepper, 802 @NotNull final JSONBuffer buffer) 803 { 804 buffer.appendString(fieldName, tokenizeEntireValue(fieldValue, pepper)); 805 } 806 807 808 809 /** 810 * {@inheritDoc} 811 */ 812 @Override() 813 public void logCompletelyTokenizedValueToJSONFormattedLog( 814 @NotNull final Filter value, 815 @NotNull final byte[] pepper, 816 @NotNull final JSONBuffer buffer) 817 { 818 buffer.appendString(tokenizeEntireValue(value, pepper)); 819 } 820 821 822 823 /** 824 * {@inheritDoc} 825 */ 826 @Override() 827 public boolean supportsTokenizedComponents() 828 { 829 return true; 830 } 831 832 833 834 /** 835 * {@inheritDoc} 836 */ 837 @Override() 838 public boolean valueWithTokenizedComponentsConformsToSyntax() 839 { 840 return true; 841 } 842 843 844 845 /** 846 * {@inheritDoc} 847 */ 848 @Override() 849 public void tokenizeComponents(@NotNull final Filter value, 850 @NotNull final byte[] pepper, 851 @NotNull final ByteStringBuffer buffer) 852 { 853 buffer.append(tokenizeFilter(value, pepper).toString()); 854 } 855 856 857 858 /** 859 * Retrieves a tokenized version of the provided filter. 860 * 861 * @param filter The filter to tokenize. It must not be {@code null}. 862 * @param pepper A pepper used to provide brute-force protection for the 863 * resulting token. The pepper value should be kept secret so 864 * that it is not available to unauthorized users who might be 865 * able to view log information, although the same pepper 866 * value should be consistently provided when tokenizing 867 * values so that the same value will consistently yield the 868 * same token. It must not be {@code null} and should not be 869 * empty. 870 * 871 * @return A tokenized version of the provided filter. 872 */ 873 @NotNull() 874 private Filter tokenizeFilter(@NotNull final Filter filter, 875 @NotNull final byte[] pepper) 876 { 877 switch (filter.getFilterType()) 878 { 879 case Filter.FILTER_TYPE_AND: 880 final Filter[] originalANDComponents = filter.getComponents(); 881 final Filter[] tokenizedANDComponents = 882 new Filter[originalANDComponents.length]; 883 for (int i=0; i < originalANDComponents.length; i++) 884 { 885 tokenizedANDComponents[i] = 886 tokenizeFilter(originalANDComponents[i], pepper); 887 } 888 return Filter.createANDFilter(tokenizedANDComponents); 889 890 case Filter.FILTER_TYPE_OR: 891 final Filter[] originalORComponents = filter.getComponents(); 892 final Filter[] tokenizedORComponents = 893 new Filter[originalORComponents.length]; 894 for (int i=0; i < originalORComponents.length; i++) 895 { 896 tokenizedORComponents[i] = 897 tokenizeFilter(originalORComponents[i], pepper); 898 } 899 return Filter.createORFilter(tokenizedORComponents); 900 901 case Filter.FILTER_TYPE_NOT: 902 return Filter.createNOTFilter( 903 tokenizeFilter(filter.getNOTComponent(), pepper)); 904 905 case Filter.FILTER_TYPE_EQUALITY: 906 if (shouldRedactOrTokenize(filter.getAttributeName())) 907 { 908 return Filter.createEqualityFilter(filter.getAttributeName(), 909 tokenize(filter.getAssertionValue(), pepper)); 910 } 911 else 912 { 913 return Filter.createEqualityFilter(filter.getAttributeName(), 914 sanitize(filter.getAssertionValue())); 915 } 916 917 case Filter.FILTER_TYPE_GREATER_OR_EQUAL: 918 if (shouldRedactOrTokenize(filter.getAttributeName())) 919 { 920 return Filter.createGreaterOrEqualFilter(filter.getAttributeName(), 921 tokenize(filter.getAssertionValue(), pepper)); 922 } 923 else 924 { 925 return Filter.createGreaterOrEqualFilter(filter.getAttributeName(), 926 sanitize(filter.getAssertionValue())); 927 } 928 929 case Filter.FILTER_TYPE_LESS_OR_EQUAL: 930 if (shouldRedactOrTokenize(filter.getAttributeName())) 931 { 932 return Filter.createLessOrEqualFilter(filter.getAttributeName(), 933 tokenize(filter.getAssertionValue(), pepper)); 934 } 935 else 936 { 937 return Filter.createLessOrEqualFilter(filter.getAttributeName(), 938 sanitize(filter.getAssertionValue())); 939 } 940 941 case Filter.FILTER_TYPE_APPROXIMATE_MATCH: 942 if (shouldRedactOrTokenize(filter.getAttributeName())) 943 { 944 return Filter.createApproximateMatchFilter(filter.getAttributeName(), 945 tokenize(filter.getAssertionValue(), pepper)); 946 } 947 else 948 { 949 return Filter.createApproximateMatchFilter(filter.getAttributeName(), 950 sanitize(filter.getAssertionValue())); 951 } 952 953 case Filter.FILTER_TYPE_SUBSTRING: 954 final String tokenizedSubInitial; 955 final String tokenizedSubFinal; 956 final String originalSubInitial = filter.getSubInitialString(); 957 final String originalSubFinal = filter.getSubFinalString(); 958 final String[] originalSubAny = filter.getSubAnyStrings(); 959 final String[] tokenizedSubAny = new String[originalSubAny.length]; 960 if (shouldRedactOrTokenize(filter.getAttributeName())) 961 { 962 tokenizedSubInitial = (originalSubInitial == null) 963 ? null 964 : tokenize(originalSubInitial, pepper); 965 tokenizedSubFinal = (originalSubFinal == null) 966 ? null 967 : tokenize(originalSubFinal, pepper); 968 for (int i=0; i < originalSubAny.length; i++) 969 { 970 tokenizedSubAny[i] = tokenize(originalSubAny[i], pepper); 971 } 972 } 973 else 974 { 975 tokenizedSubInitial = (originalSubInitial == null) 976 ? null 977 : sanitize(originalSubInitial); 978 tokenizedSubFinal = (originalSubFinal == null) 979 ? null 980 : sanitize(originalSubFinal); 981 for (int i=0; i < originalSubAny.length; i++) 982 { 983 tokenizedSubAny[i] = sanitize(originalSubAny[i]); 984 } 985 } 986 987 return Filter.createSubstringFilter(filter.getAttributeName(), 988 tokenizedSubInitial, tokenizedSubAny, tokenizedSubFinal); 989 990 case Filter.FILTER_TYPE_EXTENSIBLE_MATCH: 991 if (shouldRedactOrTokenize(filter.getAttributeName())) 992 { 993 return Filter.createExtensibleMatchFilter(filter.getAttributeName(), 994 filter.getMatchingRuleID(), filter.getDNAttributes(), 995 tokenize(filter.getAssertionValue(), pepper)); 996 } 997 else 998 { 999 return Filter.createExtensibleMatchFilter(filter.getAttributeName(), 1000 filter.getMatchingRuleID(), filter.getDNAttributes(), 1001 sanitize(filter.getAssertionValue())); 1002 } 1003 1004 case Filter.FILTER_TYPE_PRESENCE: 1005 default: 1006 return filter; 1007 } 1008 } 1009 1010 1011 1012 /** 1013 * {@inheritDoc} 1014 */ 1015 @Override() 1016 public void logTokenizedComponentsFieldToTextFormattedLog( 1017 @NotNull final String fieldName, 1018 @NotNull final Filter fieldValue, 1019 @NotNull final byte[] pepper, 1020 @NotNull final ByteStringBuffer buffer) 1021 { 1022 buffer.append(' '); 1023 buffer.append(fieldName); 1024 buffer.append("=\""); 1025 tokenizeComponents(fieldValue, pepper, buffer); 1026 buffer.append('"'); 1027 } 1028 1029 1030 1031 /** 1032 * {@inheritDoc} 1033 */ 1034 @Override() 1035 public void logTokenizedComponentsFieldToJSONFormattedLog( 1036 @NotNull final String fieldName, 1037 @NotNull final Filter fieldValue, 1038 @NotNull final byte[] pepper, 1039 @NotNull final JSONBuffer buffer) 1040 { 1041 buffer.appendString(fieldName, tokenizeComponents(fieldValue, pepper)); 1042 } 1043 1044 1045 1046 /** 1047 * {@inheritDoc} 1048 */ 1049 @Override() 1050 public void logTokenizedComponentsValueToJSONFormattedLog( 1051 @NotNull final Filter value, 1052 @NotNull final byte[] pepper, 1053 @NotNull final JSONBuffer buffer) 1054 { 1055 buffer.appendString(tokenizeComponents(value, pepper)); 1056 } 1057}