|
@@ -21,10 +21,17 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
|
|
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
|
|
import org.apache.hadoop.classification.InterfaceAudience;
|
|
|
import org.apache.hadoop.classification.InterfaceStability;
|
|
|
+import org.apache.hadoop.conf.Configuration;
|
|
|
|
|
|
import java.nio.charset.Charset;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
import java.util.Arrays;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_CALLER_CONTEXT_SEPARATOR_DEFAULT;
|
|
|
+import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_CALLER_CONTEXT_SEPARATOR_KEY;
|
|
|
|
|
|
/**
|
|
|
* A class defining the caller context for auditing coarse granularity
|
|
@@ -54,8 +61,8 @@ public final class CallerContext {
|
|
|
private final byte[] signature;
|
|
|
|
|
|
private CallerContext(Builder builder) {
|
|
|
- this.context = builder.context;
|
|
|
- this.signature = builder.signature;
|
|
|
+ this.context = builder.getContext();
|
|
|
+ this.signature = builder.getSignature();
|
|
|
}
|
|
|
|
|
|
public String getContext() {
|
|
@@ -109,11 +116,53 @@ public final class CallerContext {
|
|
|
|
|
|
/** The caller context builder. */
|
|
|
public static final class Builder {
|
|
|
- private final String context;
|
|
|
+ private static final String KEY_VALUE_SEPARATOR = ":";
|
|
|
+ /**
|
|
|
+ * The illegal separators include '\t', '\n', '='.
|
|
|
+ * User should not set illegal separator.
|
|
|
+ */
|
|
|
+ private static final Set<String> ILLEGAL_SEPARATORS =
|
|
|
+ Collections.unmodifiableSet(
|
|
|
+ new HashSet<>(Arrays.asList("\t", "\n", "=")));
|
|
|
+ private final String fieldSeparator;
|
|
|
+ private final StringBuilder sb = new StringBuilder();
|
|
|
private byte[] signature;
|
|
|
|
|
|
public Builder(String context) {
|
|
|
- this.context = context;
|
|
|
+ this(context, new Configuration());
|
|
|
+ }
|
|
|
+
|
|
|
+ public Builder(String context, Configuration conf) {
|
|
|
+ if (isValid(context)) {
|
|
|
+ sb.append(context);
|
|
|
+ }
|
|
|
+ fieldSeparator = conf.get(HADOOP_CALLER_CONTEXT_SEPARATOR_KEY,
|
|
|
+ HADOOP_CALLER_CONTEXT_SEPARATOR_DEFAULT);
|
|
|
+ checkFieldSeparator(fieldSeparator);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Check whether the separator is legal.
|
|
|
+ * The illegal separators include '\t', '\n', '='.
|
|
|
+ * Throw IllegalArgumentException if the separator is Illegal.
|
|
|
+ * @param separator the separator of fields.
|
|
|
+ */
|
|
|
+ private void checkFieldSeparator(String separator) {
|
|
|
+ if (ILLEGAL_SEPARATORS.contains(separator)) {
|
|
|
+ throw new IllegalArgumentException("Illegal field separator: "
|
|
|
+ + separator);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Whether the field is valid.
|
|
|
+ * The field should not contain '\t', '\n', '='.
|
|
|
+ * Because the context could be written to audit log.
|
|
|
+ * @param field one of the fields in context.
|
|
|
+ * @return true if the field is not null or empty.
|
|
|
+ */
|
|
|
+ private boolean isValid(String field) {
|
|
|
+ return field != null && field.length() > 0;
|
|
|
}
|
|
|
|
|
|
public Builder setSignature(byte[] signature) {
|
|
@@ -123,6 +172,54 @@ public final class CallerContext {
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Get the context.
|
|
|
+ * For example, the context is "key1:value1,key2:value2".
|
|
|
+ * @return the valid context or null.
|
|
|
+ */
|
|
|
+ public String getContext() {
|
|
|
+ return sb.length() > 0 ? sb.toString() : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get the signature.
|
|
|
+ * @return the signature.
|
|
|
+ */
|
|
|
+ public byte[] getSignature() {
|
|
|
+ return signature;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Append new field to the context.
|
|
|
+ * @param field one of fields to append.
|
|
|
+ * @return the builder.
|
|
|
+ */
|
|
|
+ public Builder append(String field) {
|
|
|
+ if (isValid(field)) {
|
|
|
+ if (sb.length() > 0) {
|
|
|
+ sb.append(fieldSeparator);
|
|
|
+ }
|
|
|
+ sb.append(field);
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Append new field which contains key and value to the context.
|
|
|
+ * @param key the key of field.
|
|
|
+ * @param value the value of field.
|
|
|
+ * @return the builder.
|
|
|
+ */
|
|
|
+ public Builder append(String key, String value) {
|
|
|
+ if (isValid(key) && isValid(value)) {
|
|
|
+ if (sb.length() > 0) {
|
|
|
+ sb.append(fieldSeparator);
|
|
|
+ }
|
|
|
+ sb.append(key).append(KEY_VALUE_SEPARATOR).append(value);
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
public CallerContext build() {
|
|
|
return new CallerContext(this);
|
|
|
}
|