Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add logging implementation for AuditManager and audit more endpoints #15480

Merged
merged 31 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
560ca30
Add AuditLogger
kfaraz Dec 4, 2023
2415a17
Merge branch 'master' of github.com:apache/druid into add_audit_logger
kfaraz Dec 4, 2023
9cb9dc6
Fix compile and checkstyle
kfaraz Dec 4, 2023
74860c7
Audit more endpoints
kfaraz Dec 4, 2023
f418435
Fix codeql
kfaraz Dec 4, 2023
5adc89a
Audit some more endpoints
kfaraz Dec 4, 2023
383eb1d
Fix codeQL check
kfaraz Dec 4, 2023
011319e
Fix web checks
kfaraz Dec 4, 2023
0d88e64
Fix tests
kfaraz Dec 4, 2023
23c2939
Remove unused variable
kfaraz Dec 4, 2023
ce41276
Fix test
kfaraz Dec 5, 2023
b466857
Remove AuditRecord, use AuditEntry
kfaraz Dec 5, 2023
75e8e1a
Merge branch 'master' of github.com:apache/druid into add_audit_logger
kfaraz Dec 5, 2023
6c438d5
Add identity field in AuditInfo
kfaraz Dec 7, 2023
2f2eebc
Simplify usage of author and comment headers
kfaraz Dec 7, 2023
956e6a5
Fix tests
kfaraz Dec 8, 2023
6144091
Add RequestInfo to AuditEntry
kfaraz Dec 8, 2023
5e5a2fc
Fix tests
kfaraz Dec 8, 2023
71ae175
Minor cleanup
kfaraz Dec 8, 2023
737b796
Fix tests and coverage
kfaraz Dec 8, 2023
aae788c
Fix checks and tests
kfaraz Dec 8, 2023
ce7a17f
Set author header when using OverlordClient.runTask
kfaraz Dec 12, 2023
5853e2a
Use constructors in configs for better null-handling
kfaraz Dec 12, 2023
d0efb3b
Add config druid.audit.manager.auditSystemRequests
kfaraz Dec 12, 2023
ed3ecee
Use config auditSystemRequests
kfaraz Dec 12, 2023
f42d9a6
More audits, attempt to debug failing IT
kfaraz Dec 14, 2023
ed22509
More changes
kfaraz Dec 15, 2023
0b73b5f
Use Escalator to determine system identity
kfaraz Dec 18, 2023
c87fb13
Fix failing test
kfaraz Dec 19, 2023
33d8a22
Add more tests for coverage
kfaraz Dec 19, 2023
1cff032
Fix audit manager bindings
kfaraz Dec 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import com.fasterxml.jackson.jaxrs.smile.SmileMediaTypes;
import com.google.inject.Inject;
import com.sun.jersey.spi.container.ResourceFilters;
import org.apache.druid.audit.AuditEvent;
import org.apache.druid.audit.AuditInfo;
import org.apache.druid.audit.AuditManager;
import org.apache.druid.guice.LazySingleton;
import org.apache.druid.security.basic.BasicSecurityResourceFilter;
import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorCredentialUpdate;
Expand All @@ -30,30 +33,36 @@
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Collections;

@Path("/druid-ext/basic-security/authentication")
@LazySingleton
public class BasicAuthenticatorResource
{
private final BasicAuthenticatorResourceHandler handler;
private final AuthValidator authValidator;
private final AuditManager auditManager;

@Inject
public BasicAuthenticatorResource(
BasicAuthenticatorResourceHandler handler,
AuthValidator authValidator
AuthValidator authValidator,
AuditManager auditManager
)
{
this.handler = handler;
this.authValidator = authValidator;
this.auditManager = auditManager;
}

/**
Expand Down Expand Up @@ -147,11 +156,21 @@
public Response createUser(
@Context HttpServletRequest req,
@PathParam("authenticatorName") final String authenticatorName,
@PathParam("userName") String userName
@PathParam("userName") String userName,
@HeaderParam(AuditManager.X_DRUID_AUTHOR) @DefaultValue("") final String author,
@HeaderParam(AuditManager.X_DRUID_COMMENT) @DefaultValue("") final String comment
)
{
authValidator.validateAuthenticatorName(authenticatorName);
return handler.createUser(authenticatorName, userName);

final Response response = handler.createUser(authenticatorName, userName);

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

return response;
}

/**
Expand All @@ -170,11 +189,20 @@
public Response deleteUser(
@Context HttpServletRequest req,
@PathParam("authenticatorName") final String authenticatorName,
@PathParam("userName") String userName
@PathParam("userName") String userName,
@HeaderParam(AuditManager.X_DRUID_AUTHOR) @DefaultValue("") final String author,
@HeaderParam(AuditManager.X_DRUID_COMMENT) @DefaultValue("") final String comment
)
{
authValidator.validateAuthenticatorName(authenticatorName);
return handler.deleteUser(authenticatorName, userName);
final Response response = handler.deleteUser(authenticatorName, userName);

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

return response;
}

/**
Expand All @@ -194,11 +222,20 @@
@Context HttpServletRequest req,
@PathParam("authenticatorName") final String authenticatorName,
@PathParam("userName") String userName,
BasicAuthenticatorCredentialUpdate update
BasicAuthenticatorCredentialUpdate update,
@HeaderParam(AuditManager.X_DRUID_AUTHOR) @DefaultValue("") final String author,
@HeaderParam(AuditManager.X_DRUID_COMMENT) @DefaultValue("") final String comment
)
{
authValidator.validateAuthenticatorName(authenticatorName);
return handler.updateUserCredentials(authenticatorName, userName, update);
final Response response = handler.updateUserCredentials(authenticatorName, userName, update);

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

return response;
}

/**
Expand Down Expand Up @@ -229,7 +266,7 @@
@Consumes(MediaType.APPLICATION_JSON)
@ResourceFilters(BasicSecurityResourceFilter.class)
public Response authenticatorUpdateListener(
@Context HttpServletRequest req,

Check notice

Code scanning / CodeQL

Useless parameter Note

The parameter 'req' is never used.
@PathParam("authenticatorName") final String authenticatorName,
byte[] serializedUserMap
)
Expand All @@ -237,4 +274,26 @@
authValidator.validateAuthenticatorName(authenticatorName);
return handler.authenticatorUserUpdateListener(authenticatorName, serializedUserMap);
}

private boolean isSuccess(Response response)
{
if (response == null) {
return false;
}

int responseCode = response.getStatus();
return responseCode >= 200 && responseCode < 300;
}

private void performAudit(String authenticatorName, String action, Object payload, AuditInfo auditInfo)
{
auditManager.doAudit(
AuditEvent.builder()
.key(authenticatorName)
.type(action)
.auditInfo(auditInfo)
.payload(payload)
.build()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.fasterxml.jackson.dataformat.smile.SmileFactory;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.apache.druid.audit.AuditManager;
import org.apache.druid.metadata.DefaultPasswordProvider;
import org.apache.druid.metadata.MetadataStorageTablesConfig;
import org.apache.druid.metadata.TestDerbyConnector;
Expand Down Expand Up @@ -68,6 +69,9 @@

@Mock
private AuthValidator authValidator;

@Mock
private AuditManager auditManager;
private BasicAuthenticatorResource resource;
private CoordinatorBasicAuthenticatorMetadataStorageUpdater storageUpdater;
private HttpServletRequest req;
Expand All @@ -83,7 +87,7 @@
MetadataStorageTablesConfig tablesConfig = derbyConnectorRule.metadataTablesConfigSupplier().get();
connector.createConfigTable();

ObjectMapper objectMapper = new ObjectMapper(new SmileFactory());

Check notice

Code scanning / CodeQL

Possible confusion of local and field Note test

Confusing name: method
setUp
also refers to field
objectMapper
(without qualifying it with 'this').

AuthenticatorMapper authenticatorMapper = new AuthenticatorMapper(
ImmutableMap.of(
Expand Down Expand Up @@ -145,7 +149,8 @@
authenticatorMapper,
objectMapper
),
authValidator
authValidator,
auditManager
);

storageUpdater.start();
Expand Down Expand Up @@ -175,9 +180,9 @@
Assert.assertEquals(200, response.getStatus());
Assert.assertEquals(ImmutableSet.of(BasicAuthUtils.ADMIN_NAME, BasicAuthUtils.INTERNAL_USER_NAME), response.getEntity());

resource.createUser(req, AUTHENTICATOR_NAME, "druid");
resource.createUser(req, AUTHENTICATOR_NAME, "druid2");
resource.createUser(req, AUTHENTICATOR_NAME, "druid3");
resource.createUser(req, AUTHENTICATOR_NAME, "druid", null, null);
resource.createUser(req, AUTHENTICATOR_NAME, "druid2", null, null);
resource.createUser(req, AUTHENTICATOR_NAME, "druid3", null, null);

Set<String> expectedUsers = ImmutableSet.of(
BasicAuthUtils.ADMIN_NAME,
Expand Down Expand Up @@ -215,13 +220,13 @@
Assert.assertEquals(200, response.getStatus());
Assert.assertEquals(ImmutableSet.of(BasicAuthUtils.ADMIN_NAME, BasicAuthUtils.INTERNAL_USER_NAME), response.getEntity());

resource.createUser(req, AUTHENTICATOR_NAME, "druid");
resource.createUser(req, AUTHENTICATOR_NAME, "druid2");
resource.createUser(req, AUTHENTICATOR_NAME, "druid3");
resource.createUser(req, AUTHENTICATOR_NAME, "druid", null, null);
resource.createUser(req, AUTHENTICATOR_NAME, "druid2", null, null);
resource.createUser(req, AUTHENTICATOR_NAME, "druid3", null, null);

resource.createUser(req, AUTHENTICATOR_NAME2, "druid4");
resource.createUser(req, AUTHENTICATOR_NAME2, "druid5");
resource.createUser(req, AUTHENTICATOR_NAME2, "druid6");
resource.createUser(req, AUTHENTICATOR_NAME2, "druid4", null, null);
resource.createUser(req, AUTHENTICATOR_NAME2, "druid5", null, null);
resource.createUser(req, AUTHENTICATOR_NAME2, "druid6", null, null);

Set<String> expectedUsers = ImmutableSet.of(
BasicAuthUtils.ADMIN_NAME,
Expand Down Expand Up @@ -285,15 +290,15 @@
@Test
public void testCreateDeleteUser()
{
Response response = resource.createUser(req, AUTHENTICATOR_NAME, "druid");
Response response = resource.createUser(req, AUTHENTICATOR_NAME, "druid", null, null);
Assert.assertEquals(200, response.getStatus());

response = resource.getUser(req, AUTHENTICATOR_NAME, "druid");
Assert.assertEquals(200, response.getStatus());
BasicAuthenticatorUser expectedUser = new BasicAuthenticatorUser("druid", null);
Assert.assertEquals(expectedUser, response.getEntity());

response = resource.deleteUser(req, AUTHENTICATOR_NAME, "druid");
response = resource.deleteUser(req, AUTHENTICATOR_NAME, "druid", null, null);
Assert.assertEquals(200, response.getStatus());

response = resource.getCachedSerializedUserMap(req, AUTHENTICATOR_NAME);
Expand All @@ -303,7 +308,7 @@
Assert.assertNotNull(cachedUserMap);
Assert.assertNull(cachedUserMap.get("druid"));

response = resource.deleteUser(req, AUTHENTICATOR_NAME, "druid");
response = resource.deleteUser(req, AUTHENTICATOR_NAME, "druid", null, null);
Assert.assertEquals(400, response.getStatus());
Assert.assertEquals(errorMapWithMsg("User [druid] does not exist."), response.getEntity());

Expand All @@ -315,14 +320,15 @@
@Test
public void testUserCredentials()
{
Response response = resource.createUser(req, AUTHENTICATOR_NAME, "druid");
Response response = resource.createUser(req, AUTHENTICATOR_NAME, "druid", null, null);
Assert.assertEquals(200, response.getStatus());

response = resource.updateUserCredentials(
req,
AUTHENTICATOR_NAME,
"druid",
new BasicAuthenticatorCredentialUpdate("helloworld", null)
new BasicAuthenticatorCredentialUpdate("helloworld", null),
Fixed Show fixed Hide fixed
null, null
);
Assert.assertEquals(200, response.getStatus());

Expand Down Expand Up @@ -369,7 +375,7 @@
);
Assert.assertArrayEquals(recalculatedHash, hash);

response = resource.deleteUser(req, AUTHENTICATOR_NAME, "druid");
response = resource.deleteUser(req, AUTHENTICATOR_NAME, "druid", null, null);
Assert.assertEquals(200, response.getStatus());

response = resource.getUser(req, AUTHENTICATOR_NAME, "druid");
Expand All @@ -380,7 +386,8 @@
req,
AUTHENTICATOR_NAME,
"druid",
new BasicAuthenticatorCredentialUpdate("helloworld", null)
new BasicAuthenticatorCredentialUpdate("helloworld", null),
Fixed Show fixed Hide fixed
null, null
);
Assert.assertEquals(400, response.getStatus());
Assert.assertEquals(errorMapWithMsg("User [druid] does not exist."), response.getEntity());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void setUp()
.when(authValidator)
.validateAuthenticatorName(INVALID_AUTHENTICATOR_NAME);

target = new BasicAuthenticatorResource(handler, authValidator);
target = new BasicAuthenticatorResource(handler, authValidator, null);
}

@Test
Expand Down Expand Up @@ -88,37 +88,37 @@ public void getCachedSerializedUserMapWithInvalidAuthenticatorNameShouldReturnEx
@Test
public void updateUserCredentialsShouldReturnExpectedResponse()
{
Assert.assertNotNull(target.updateUserCredentials(req, AUTHENTICATOR_NAME, USER_NAME, update));
Assert.assertNotNull(target.updateUserCredentials(req, AUTHENTICATOR_NAME, USER_NAME, update, null, null));
}

@Test(expected = IllegalArgumentException.class)
public void updateUserCredentialsWithInvalidAuthenticatorNameShouldReturnExpectedResponse()
{
target.updateUserCredentials(req, INVALID_AUTHENTICATOR_NAME, USER_NAME, update);
target.updateUserCredentials(req, INVALID_AUTHENTICATOR_NAME, USER_NAME, update, null, null);
}

@Test
public void deleteUserShouldReturnExpectedResponse()
{
Assert.assertNotNull(target.deleteUser(req, AUTHENTICATOR_NAME, USER_NAME));
Assert.assertNotNull(target.deleteUser(req, AUTHENTICATOR_NAME, USER_NAME, null, null));
}

@Test(expected = IllegalArgumentException.class)
public void deleteUserWithInvalidAuthenticatorNameShouldReturnExpectedResponse()
{
target.deleteUser(req, INVALID_AUTHENTICATOR_NAME, USER_NAME);
target.deleteUser(req, INVALID_AUTHENTICATOR_NAME, USER_NAME, null, null);
}

@Test
public void createUserShouldReturnExpectedResponse()
{
Assert.assertNotNull(target.createUser(req, AUTHENTICATOR_NAME, USER_NAME));
Assert.assertNotNull(target.createUser(req, AUTHENTICATOR_NAME, USER_NAME, null, null));
}

@Test(expected = IllegalArgumentException.class)
public void createUserWithInvalidAuthenticatorNameShouldReturnExpectedResponse()
{
target.createUser(req, INVALID_AUTHENTICATOR_NAME, USER_NAME);
target.createUser(req, INVALID_AUTHENTICATOR_NAME, USER_NAME, null, null);
}

@Test
Expand Down
Loading
Loading