Skip to content

Commit

Permalink
Add identity field in AuditInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
kfaraz committed Dec 7, 2023
1 parent 75e8e1a commit 6c438d5
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.druid.security.basic.BasicSecurityResourceFilter;
import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorCredentialUpdate;
import org.apache.druid.server.security.AuthValidator;
import org.apache.druid.server.security.AuthorizationUtils;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
Expand Down Expand Up @@ -166,7 +167,7 @@ public Response createUser(
final Response response = handler.createUser(authenticatorName, userName);

if (isSuccess(response)) {
final AuditInfo auditInfo = new AuditInfo(author, comment, req.getRemoteAddr());
final AuditInfo auditInfo = AuthorizationUtils.buildAuditInfo(author, comment, req);
performAudit(authenticatorName, "users.create", Collections.singletonMap("username", userName), auditInfo);
}

Expand Down Expand Up @@ -198,7 +199,7 @@ public Response deleteUser(
final Response response = handler.deleteUser(authenticatorName, userName);

if (isSuccess(response)) {
final AuditInfo auditInfo = new AuditInfo(author, comment, req.getRemoteAddr());
final AuditInfo auditInfo = AuthorizationUtils.buildAuditInfo(author, comment, req);
performAudit(authenticatorName, "users.delete", Collections.singletonMap("username", userName), auditInfo);
}

Expand Down Expand Up @@ -231,7 +232,7 @@ public Response updateUserCredentials(
final Response response = handler.updateUserCredentials(authenticatorName, userName, update);

if (isSuccess(response)) {
final AuditInfo auditInfo = new AuditInfo(author, comment, req.getRemoteAddr());
final AuditInfo auditInfo = AuthorizationUtils.buildAuditInfo(author, comment, req);
performAudit(authenticatorName, "users.update", Collections.singletonMap("username", userName), auditInfo);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,9 @@ public Response taskPost(
auditManager.doAudit(
AuditEntry.builder()
.key(task.getDataSource())
.type("ingestion.batch")
.type("overlord" + req.getServletPath())
.payload(new TaskIdentifier(task.getId(), task.getGroupId(), task.getType()))
.auditInfo(new AuditInfo(author, comment, req.getRemoteAddr()))
.auditInfo(AuthorizationUtils.buildAuditInfo(author, comment, req))
.build()
);
}
Expand Down
23 changes: 11 additions & 12 deletions processing/src/main/java/org/apache/druid/audit/AuditEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ public DateTime getAuditTime()
return auditTime;
}

public static Builder builder()
{
return new Builder();
}

@Override
public boolean equals(Object o)
{
Expand All @@ -119,27 +124,18 @@ public int hashCode()
return Objects.hash(key, type, auditInfo, payload, auditTime);
}

public static Builder builder()
{
return new Builder();
}

public static class Builder
{
private String key;
private String type;
private AuditInfo auditInfo;

private String serializedPayload;
private Object payload;
private String serializedPayload;

private DateTime auditTime;

private Builder()
{
this.key = null;
this.auditInfo = null;
this.serializedPayload = null;
this.auditTime = DateTimes.nowUtc();
}

Expand Down Expand Up @@ -185,6 +181,9 @@ public AuditEntry build()
}
}

/**
* Payload of an {@link AuditEntry} that may be specified {@link #raw()} or {@link #serialized()}.
*/
public static class Payload
{
private final String serialized;
Expand All @@ -200,7 +199,7 @@ public static Payload fromString(String serialized)
@Override
public String toString()
{
return serialized;
return serialized == null ? "" : serialized;
}

private Payload(String serialized, Object raw)
Expand All @@ -209,7 +208,7 @@ private Payload(String serialized, Object raw)
this.raw = raw;
}

public String asString()
public String serialized()
{
return serialized;
}
Expand Down
26 changes: 21 additions & 5 deletions processing/src/main/java/org/apache/druid/audit/AuditInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,41 @@
public class AuditInfo
{
private final String author;
private final String identity;
private final String comment;
private final String ip;

@JsonCreator
public AuditInfo(
@JsonProperty("author") String author,
@JsonProperty("identity") String identity,
@JsonProperty("comment") String comment,
@JsonProperty("ip") String ip
)
{
this.author = author;
this.identity = identity;
this.comment = comment;
this.ip = ip;
}

public AuditInfo(String author, String comment, String ip)
{
this(author, null, comment, ip);
}

@JsonProperty
public String getAuthor()
{
return author;
}

@JsonProperty
public String getIdentity()
{
return identity;
}

@JsonProperty
public String getComment()
{
Expand All @@ -69,23 +83,25 @@ public boolean equals(Object o)
if (o == null || getClass() != o.getClass()) {
return false;
}
AuditInfo auditInfo = (AuditInfo) o;
return Objects.equals(author, auditInfo.author)
&& Objects.equals(comment, auditInfo.comment)
&& Objects.equals(ip, auditInfo.ip);
AuditInfo that = (AuditInfo) o;
return Objects.equals(this.author, that.author)
&& Objects.equals(this.identity, that.identity)
&& Objects.equals(this.comment, that.comment)
&& Objects.equals(this.ip, that.ip);
}

@Override
public int hashCode()
{
return Objects.hash(author, comment, ip);
return Objects.hash(author, identity, comment, ip);
}

@Override
public String toString()
{
return "AuditInfo{" +
"author='" + author + '\'' +
", identity='" + identity + '\'' +
", comment='" + comment + '\'' +
", ip='" + ip + '\'' +
'}';
Expand Down
36 changes: 22 additions & 14 deletions processing/src/test/java/org/apache/druid/audit/AuditInfoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,18 @@

package org.apache.druid.audit;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.druid.jackson.DefaultObjectMapper;
import org.apache.druid.java.util.common.DateTimes;
import org.junit.Assert;
import org.junit.Test;

import java.io.IOException;

public class AuditInfoTest
{
private final ObjectMapper mapper = new DefaultObjectMapper();

@Test
public void testAuditInfoEquality()
{
Expand All @@ -34,21 +40,22 @@ public void testAuditInfoEquality()
Assert.assertEquals(auditInfo1.hashCode(), auditInfo2.hashCode());
}

@Test
public void testAuditInfoSerde() throws IOException
{
final AuditInfo auditInfo = new AuditInfo("author", "comment", "ip");
AuditInfo deserialized = mapper.readValue(mapper.writeValueAsString(auditInfo), AuditInfo.class);
Assert.assertEquals(auditInfo, deserialized);

final AuditInfo auditInfoWithIdentity = new AuditInfo("author", "identity", "comment", "ip");
deserialized = mapper.readValue(mapper.writeValueAsString(auditInfoWithIdentity), AuditInfo.class);
Assert.assertEquals(auditInfoWithIdentity, deserialized);
}

@Test(timeout = 60_000L)
public void testAuditEntryEquality()
public void testAuditEntrySerde() throws IOException
{
final AuditEntry event1 = new AuditEntry(
"testKey",
"testType",
new AuditInfo(
"testAuthor",
"testComment",
"127.0.0.1"
),
AuditEntry.Payload.fromString("testPayload"),
DateTimes.of("2013-01-01T00:00:00Z")
);
final AuditEntry event2 = new AuditEntry(
AuditEntry entry = new AuditEntry(
"testKey",
"testType",
new AuditInfo(
Expand All @@ -59,7 +66,8 @@ public void testAuditEntryEquality()
AuditEntry.Payload.fromString("testPayload"),
DateTimes.of("2013-01-01T00:00:00Z")
);
Assert.assertEquals(event1, event2);
AuditEntry serde = mapper.readValue(mapper.writeValueAsString(entry), AuditEntry.class);
Assert.assertEquals(entry, serde);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
* Audit utility class that can be used by different implementations of
* {@link org.apache.druid.audit.AuditManager} to serialize/deserialize audit
* payloads based on the values configured in {@link AuditManagerConfig}.
*/
public class AuditSerdeHelper
{
/**
Expand Down Expand Up @@ -60,12 +65,20 @@ public AuditSerdeHelper(
this.jsonMapperSkipNulls = jsonMapperSkipNulls;
}

/**
* Processes the given AuditEntry for further use such as logging or persistence.
* This involves serializing and truncating the payload based on the values
* configured in {@link AuditManagerConfig}.
*
* @return A new AuditEntry with a serialized payload that can be used for
* logging or persistence.
*/
public AuditEntry processAuditEntry(AuditEntry entry)
{
final AuditEntry.Payload payload = entry.getPayload();
final String serialized = payload.asString() == null
final String serialized = payload.serialized() == null
? serializePayloadToString(payload.raw())
: payload.asString();
: payload.serialized();

final AuditEntry.Payload processedPayload = AuditEntry.Payload.fromString(
truncateSerializedAuditPayload(serialized)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ private ServiceMetricEvent.Builder createMetricEventBuilder(AuditEntry entry)
.setDimension("created_date", entry.getAuditTime().toString());

if (config.isIncludePayloadAsDimensionInMetric()) {
builder.setDimension("payload", entry.getPayload().asString());
builder.setDimension("payload", entry.getPayload().serialized());
}

return builder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.apache.druid.server.coordinator.DataSourceCompactionConfig;
import org.apache.druid.server.coordinator.DataSourceCompactionConfigHistory;
import org.apache.druid.server.http.security.ConfigResourceFilter;
import org.apache.druid.server.security.AuthorizationUtils;
import org.joda.time.Interval;

import javax.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -107,7 +108,7 @@ public Response setCompactionTaskLimit(
maxCompactionTaskSlots,
useAutoScaleSlots
);
return updateConfigHelper(operator, new AuditInfo(author, comment, req.getRemoteAddr()));
return updateConfigHelper(operator, AuthorizationUtils.buildAuditInfo(author, comment, req));
}

@POST
Expand All @@ -132,7 +133,7 @@ public Response addOrUpdateCompactionConfig(
};
return updateConfigHelper(
callable,
new AuditInfo(author, comment, req.getRemoteAddr())
AuthorizationUtils.buildAuditInfo(author, comment, req)
);
}

Expand Down Expand Up @@ -183,7 +184,7 @@ public Response getCompactionConfigHistory(
DataSourceCompactionConfigHistory history = new DataSourceCompactionConfigHistory(dataSource);
for (AuditEntry audit : auditEntries) {
CoordinatorCompactionConfig coordinatorCompactionConfig = configManager.convertBytesToCompactionConfig(
audit.getPayload().asString().getBytes(StandardCharsets.UTF_8)
audit.getPayload().serialized().getBytes(StandardCharsets.UTF_8)
);
history.add(coordinatorCompactionConfig, audit.getAuditInfo(), audit.getAuditTime());
}
Expand Down Expand Up @@ -219,7 +220,7 @@ public Response deleteCompactionConfig(

return CoordinatorCompactionConfig.from(current, ImmutableList.copyOf(configs.values()));
};
return updateConfigHelper(callable, new AuditInfo(author, comment, req.getRemoteAddr()));
return updateConfigHelper(callable, AuthorizationUtils.buildAuditInfo(author, comment, req));
}

private Response updateConfigHelper(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.apache.druid.audit.AuditInfo;
import org.apache.druid.error.DruidException;
import org.apache.druid.java.util.common.ISE;

Expand Down Expand Up @@ -89,6 +90,33 @@ public static AuthenticationResult authenticationResultFromRequest(final HttpSer
return authenticationResult;
}

/**
* Extracts the identity from the authentication result if set as an atrribute
* of this request.
*/
public static String getAuthenticatedIdentity(HttpServletRequest request)
{
final AuthenticationResult authenticationResult = (AuthenticationResult) request.getAttribute(
AuthConfig.DRUID_AUTHENTICATION_RESULT
);

if (authenticationResult == null) {
return null;
} else {
return authenticationResult.getIdentity();
}
}

public static AuditInfo buildAuditInfo(String author, String comment, HttpServletRequest request)
{
return new AuditInfo(
author,
getAuthenticatedIdentity(request),
comment,
request.getRemoteAddr()
);
}

/**
* Check a list of resource-actions to be performed by the identity represented by authenticationResult.
*
Expand Down
Loading

0 comments on commit 6c438d5

Please sign in to comment.