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.ArrayList; 041import java.util.Collection; 042import java.util.Collections; 043import java.util.Iterator; 044import java.util.List; 045 046import com.unboundid.util.ByteStringBuffer; 047import com.unboundid.util.NotNull; 048import com.unboundid.util.ThreadSafety; 049import com.unboundid.util.ThreadSafetyLevel; 050import com.unboundid.util.json.JSONBuffer; 051 052 053 054/** 055 * This class defines a log field syntax for values that are a comma-delimited 056 * list of strings. This syntax does support redacting and tokenizing the 057 * individual items in the list. 058 * <BR> 059 * <BLOCKQUOTE> 060 * <B>NOTE:</B> This class, and other classes within the 061 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 062 * supported for use against Ping Identity, UnboundID, and 063 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 064 * for proprietary functionality or for external specifications that are not 065 * considered stable or mature enough to be guaranteed to work in an 066 * interoperable way with other types of LDAP servers. 067 * </BLOCKQUOTE> 068 */ 069@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 070public final class CommaDelimitedStringListLogFieldSyntax 071 extends LogFieldSyntax<Collection<? extends CharSequence>> 072{ 073 /** 074 * The name for this syntax. 075 */ 076 @NotNull public static final String SYNTAX_NAME = 077 "comma-delimited-string-list"; 078 079 080 081 /** 082 * Creates a new instance of this log field syntax implementation. 083 * 084 * @param maxStringLengthCharacters The maximum length (in characters) to 085 * use for strings within values. Strings 086 * that are longer than this should be 087 * truncated before inclusion in the log. 088 * This value must be greater than or equal 089 * to zero. 090 */ 091 public CommaDelimitedStringListLogFieldSyntax( 092 final int maxStringLengthCharacters) 093 { 094 super(maxStringLengthCharacters); 095 } 096 097 098 099 /** 100 * {@inheritDoc} 101 */ 102 @Override() 103 @NotNull() 104 public String getSyntaxName() 105 { 106 return SYNTAX_NAME; 107 } 108 109 110 111 /** 112 * {@inheritDoc} 113 */ 114 @Override() 115 public void valueToSanitizedString( 116 @NotNull final Collection<? extends CharSequence> value, 117 @NotNull final ByteStringBuffer buffer) 118 { 119 final Iterator<? extends CharSequence> iterator = value.iterator(); 120 while (iterator.hasNext()) 121 { 122 sanitize(iterator.next().toString(), buffer); 123 if (iterator.hasNext()) 124 { 125 buffer.append(','); 126 } 127 } 128 } 129 130 131 132 /** 133 * {@inheritDoc} 134 */ 135 @Override() 136 public void logSanitizedFieldToTextFormattedLog( 137 @NotNull final String fieldName, 138 @NotNull final Collection<? extends CharSequence> fieldValue, 139 @NotNull final ByteStringBuffer buffer) 140 { 141 buffer.append(' '); 142 buffer.append(fieldName); 143 buffer.append("=\""); 144 valueToSanitizedString(fieldValue, buffer); 145 buffer.append('"'); 146 } 147 148 149 150 /** 151 * {@inheritDoc} 152 */ 153 @Override() 154 public void logSanitizedFieldToJSONFormattedLog( 155 @NotNull final String fieldName, 156 @NotNull final Collection<? extends CharSequence> fieldValue, 157 @NotNull final JSONBuffer buffer) 158 { 159 buffer.appendString(fieldName, valueToSanitizedString(fieldValue)); 160 } 161 162 163 164 /** 165 * {@inheritDoc} 166 */ 167 @Override() 168 public void logSanitizedValueToJSONFormattedLog( 169 @NotNull final Collection<? extends CharSequence> value, 170 @NotNull final JSONBuffer buffer) 171 { 172 buffer.appendString(valueToSanitizedString(value)); 173 } 174 175 176 177 /** 178 * {@inheritDoc} 179 */ 180 @Override() 181 @NotNull() 182 public List<String> parseValue(@NotNull final String valueString) 183 { 184 final List<String> list = new ArrayList<>(); 185 int lastCommaPos = -1; 186 int commaPos = valueString.indexOf(','); 187 while (commaPos >= 0) 188 { 189 final String item = 190 valueString.substring((lastCommaPos + 1), commaPos).trim(); 191 list.add(item); 192 lastCommaPos = commaPos; 193 commaPos = valueString.indexOf(',', (lastCommaPos + 1)); 194 } 195 196 final String item = valueString.substring(lastCommaPos + 1).trim(); 197 if (! (item.isEmpty() && list.isEmpty())) 198 { 199 list.add(item); 200 } 201 202 return Collections.unmodifiableList(list); 203 } 204 205 206 207 /** 208 * {@inheritDoc} 209 */ 210 @Override() 211 public boolean completelyRedactedValueConformsToSyntax() 212 { 213 return true; 214 } 215 216 217 218 /** 219 * {@inheritDoc} 220 */ 221 @Override() 222 public void logCompletelyRedactedFieldToTextFormattedLog( 223 @NotNull final String fieldName, 224 @NotNull final ByteStringBuffer buffer) 225 { 226 buffer.append(' '); 227 buffer.append(fieldName); 228 buffer.append("=\"{REDACTED}\""); 229 } 230 231 232 233 /** 234 * {@inheritDoc} 235 */ 236 @Override() 237 public void logCompletelyRedactedFieldToJSONFormattedLog( 238 @NotNull final String fieldName, 239 @NotNull final JSONBuffer buffer) 240 { 241 buffer.appendString(fieldName, REDACTED_STRING); 242 } 243 244 245 246 /** 247 * {@inheritDoc} 248 */ 249 @Override() 250 public void logCompletelyRedactedValueToJSONFormattedLog( 251 @NotNull final JSONBuffer buffer) 252 { 253 buffer.appendString(REDACTED_STRING); 254 } 255 256 257 258 /** 259 * {@inheritDoc} 260 */ 261 @Override() 262 public boolean supportsRedactedComponents() 263 { 264 return true; 265 } 266 267 268 269 /** 270 * {@inheritDoc} 271 */ 272 @Override() 273 public boolean valueWithRedactedComponentsConformsToSyntax() 274 { 275 return true; 276 } 277 278 279 280 /** 281 * {@inheritDoc} 282 */ 283 @Override() 284 public void redactComponents( 285 @NotNull final Collection<? extends CharSequence> value, 286 @NotNull final ByteStringBuffer buffer) 287 { 288 final Iterator<? extends CharSequence> iterator = value.iterator(); 289 while (iterator.hasNext()) 290 { 291 buffer.append(REDACTED_STRING); 292 iterator.next(); 293 if (iterator.hasNext()) 294 { 295 buffer.append(','); 296 } 297 } 298 } 299 300 301 302 /** 303 * {@inheritDoc} 304 */ 305 @Override() 306 public void logRedactedComponentsFieldToTextFormattedLog( 307 @NotNull final String fieldName, 308 @NotNull final Collection<? extends CharSequence> fieldValue, 309 @NotNull final ByteStringBuffer buffer) 310 { 311 buffer.append(' '); 312 buffer.append(fieldName); 313 buffer.append("=\""); 314 redactComponents(fieldValue, buffer); 315 buffer.append('"'); 316 } 317 318 319 320 /** 321 * {@inheritDoc} 322 */ 323 @Override() 324 public void logRedactedComponentsFieldToJSONFormattedLog( 325 @NotNull final String fieldName, 326 @NotNull final Collection<? extends CharSequence> fieldValue, 327 @NotNull final JSONBuffer buffer) 328 { 329 buffer.appendString(fieldName, redactComponents(fieldValue)); 330 } 331 332 333 334 /** 335 * {@inheritDoc} 336 */ 337 @Override() 338 public void logRedactedComponentsValueToJSONFormattedLog( 339 @NotNull final Collection<? extends CharSequence> value, 340 @NotNull final JSONBuffer buffer) 341 { 342 buffer.appendString(redactComponents(value)); 343 } 344 345 346 347 /** 348 * {@inheritDoc} 349 */ 350 @Override() 351 public boolean completelyTokenizedValueConformsToSyntax() 352 { 353 return true; 354 } 355 356 357 358 /** 359 * {@inheritDoc} 360 */ 361 @Override() 362 public void tokenizeEntireValue( 363 @NotNull final Collection<? extends CharSequence> value, 364 @NotNull final byte[] pepper, 365 @NotNull final ByteStringBuffer buffer) 366 { 367 tokenize(valueToSanitizedString(value), pepper, buffer); 368 } 369 370 371 372 /** 373 * {@inheritDoc} 374 */ 375 @Override() 376 public void logCompletelyTokenizedFieldToTextFormattedLog( 377 @NotNull final String fieldName, 378 @NotNull final Collection<? extends CharSequence> fieldValue, 379 @NotNull final byte[] pepper, 380 @NotNull final ByteStringBuffer buffer) 381 { 382 buffer.append(' '); 383 buffer.append(fieldName); 384 buffer.append("=\""); 385 tokenizeEntireValue(fieldValue, pepper, buffer); 386 buffer.append('"'); 387 } 388 389 390 391 /** 392 * {@inheritDoc} 393 */ 394 @Override() 395 public void logCompletelyTokenizedFieldToJSONFormattedLog( 396 @NotNull final String fieldName, 397 @NotNull final Collection<? extends CharSequence> fieldValue, 398 @NotNull final byte[] pepper, 399 @NotNull final JSONBuffer buffer) 400 { 401 buffer.appendString(fieldName, tokenizeEntireValue(fieldValue, pepper)); 402 } 403 404 405 406 /** 407 * {@inheritDoc} 408 */ 409 @Override() 410 public void logCompletelyTokenizedValueToJSONFormattedLog( 411 @NotNull final Collection<? extends CharSequence> value, 412 @NotNull final byte[] pepper, 413 @NotNull final JSONBuffer buffer) 414 { 415 buffer.appendString(tokenizeEntireValue(value, pepper)); 416 } 417 418 419 420 /** 421 * {@inheritDoc} 422 */ 423 @Override() 424 public boolean supportsTokenizedComponents() 425 { 426 return true; 427 } 428 429 430 431 /** 432 * {@inheritDoc} 433 */ 434 @Override() 435 public boolean valueWithTokenizedComponentsConformsToSyntax() 436 { 437 return true; 438 } 439 440 441 442 /** 443 * {@inheritDoc} 444 */ 445 @Override() 446 public void tokenizeComponents( 447 @NotNull final Collection<? extends CharSequence> value, 448 @NotNull final byte[] pepper, 449 @NotNull final ByteStringBuffer buffer) 450 { 451 final Iterator<? extends CharSequence> iterator = value.iterator(); 452 while (iterator.hasNext()) 453 { 454 buffer.append(tokenize(iterator.next().toString(), pepper)); 455 456 if (iterator.hasNext()) 457 { 458 buffer.append(','); 459 } 460 } 461 } 462 463 464 465 /** 466 * {@inheritDoc} 467 */ 468 @Override() 469 public void logTokenizedComponentsFieldToTextFormattedLog( 470 @NotNull final String fieldName, 471 @NotNull final Collection<? extends CharSequence> fieldValue, 472 @NotNull final byte[] pepper, 473 @NotNull final ByteStringBuffer buffer) 474 { 475 buffer.append(' '); 476 buffer.append(fieldName); 477 buffer.append("=\""); 478 tokenizeComponents(fieldValue, pepper, buffer); 479 buffer.append('"'); 480 } 481 482 483 484 /** 485 * {@inheritDoc} 486 */ 487 @Override() 488 public void logTokenizedComponentsFieldToJSONFormattedLog( 489 @NotNull final String fieldName, 490 @NotNull final Collection<? extends CharSequence> fieldValue, 491 @NotNull final byte[] pepper, 492 @NotNull final JSONBuffer buffer) 493 { 494 buffer.appendString(fieldName, tokenizeComponents(fieldValue, pepper)); 495 } 496 497 498 499 /** 500 * {@inheritDoc} 501 */ 502 @Override() 503 public void logTokenizedComponentsValueToJSONFormattedLog( 504 @NotNull final Collection<? extends CharSequence> value, 505 @NotNull final byte[] pepper, 506 @NotNull final JSONBuffer buffer) 507 { 508 buffer.appendString(tokenizeComponents(value, pepper)); 509 } 510}