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}