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 27 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,10 +22,13 @@
import com.fasterxml.jackson.jaxrs.smile.SmileMediaTypes;
import com.google.inject.Inject;
import com.sun.jersey.spi.container.ResourceFilters;
import org.apache.druid.audit.AuditEntry;
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;
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 All @@ -38,22 +41,26 @@
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 @@ -151,7 +158,11 @@ public Response createUser(
)
{
authValidator.validateAuthenticatorName(authenticatorName);
return handler.createUser(authenticatorName, userName);

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

return response;
}

/**
Expand All @@ -174,7 +185,10 @@ public Response deleteUser(
)
{
authValidator.validateAuthenticatorName(authenticatorName);
return handler.deleteUser(authenticatorName, userName);
final Response response = handler.deleteUser(authenticatorName, userName);
performAuditIfSuccess(authenticatorName, userName, req, response);

return response;
}

/**
Expand All @@ -198,7 +212,10 @@ public Response updateUserCredentials(
)
{
authValidator.validateAuthenticatorName(authenticatorName);
return handler.updateUserCredentials(authenticatorName, userName, update);
final Response response = handler.updateUserCredentials(authenticatorName, userName, update);
performAuditIfSuccess(authenticatorName, userName, req, response);

return response;
}

/**
Expand Down Expand Up @@ -237,4 +254,34 @@ public Response authenticatorUpdateListener(
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 performAuditIfSuccess(
String authenticatorName,
String updatedUser,
HttpServletRequest request,
Response response
)
{
if (updatedUser != null && isSuccess(response)) {
auditManager.doAudit(
AuditEntry.builder()
.key(authenticatorName)
.type("basicAuthentication")
.auditInfo(AuthorizationUtils.buildAuditInfo(request))
.request(AuthorizationUtils.buildRequestInfo("coordinator", request))
.payload(Collections.singletonMap("username", updatedUser))
.build()
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@
import com.fasterxml.jackson.jaxrs.smile.SmileMediaTypes;
import com.google.inject.Inject;
import com.sun.jersey.spi.container.ResourceFilters;
import org.apache.druid.audit.AuditEntry;
import org.apache.druid.audit.AuditManager;
import org.apache.druid.guice.LazySingleton;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.security.basic.BasicSecurityResourceFilter;
import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerGroupMapping;
import org.apache.druid.server.security.AuthValidator;
import org.apache.druid.server.security.AuthorizationUtils;
import org.apache.druid.server.security.ResourceAction;

import javax.servlet.http.HttpServletRequest;
Expand All @@ -48,15 +52,18 @@ public class BasicAuthorizerResource
{
private final BasicAuthorizerResourceHandler resourceHandler;
private final AuthValidator authValidator;
private final AuditManager auditManager;

@Inject
public BasicAuthorizerResource(
BasicAuthorizerResourceHandler resourceHandler,
AuthValidator authValidator
AuthValidator authValidator,
AuditManager auditManager
)
{
this.resourceHandler = resourceHandler;
this.authValidator = authValidator;
this.auditManager = auditManager;
}

/**
Expand Down Expand Up @@ -198,7 +205,11 @@ public Response createUser(
)
{
authValidator.validateAuthorizerName(authorizerName);
return resourceHandler.createUser(authorizerName, userName);

final Response response = resourceHandler.createUser(authorizerName, userName);
performAuditIfSuccess(authorizerName, req, response, "Create user[%s]", userName);

return response;
}

/**
Expand All @@ -221,7 +232,11 @@ public Response deleteUser(
)
{
authValidator.validateAuthorizerName(authorizerName);
return resourceHandler.deleteUser(authorizerName, userName);

final Response response = resourceHandler.deleteUser(authorizerName, userName);
performAuditIfSuccess(authorizerName, req, response, "Delete user[%s]", userName);

return response;
}

/**
Expand All @@ -245,10 +260,21 @@ public Response createGroupMapping(
)
{
authValidator.validateAuthorizerName(authorizerName);
return resourceHandler.createGroupMapping(
final Response response = resourceHandler.createGroupMapping(
authorizerName,
new BasicAuthorizerGroupMapping(groupMappingName, groupMapping.getGroupPattern(), groupMapping.getRoles())
);
performAuditIfSuccess(
authorizerName,
req,
response,
"Create groupMapping[%s] with pattern[%s], roles[%s]",
groupMappingName,
groupMapping.getGroupPattern(),
groupMapping.getRoles()
);

return response;
}

/**
Expand All @@ -271,7 +297,11 @@ public Response deleteGroupMapping(
)
{
authValidator.validateAuthorizerName(authorizerName);
return resourceHandler.deleteGroupMapping(authorizerName, groupMappingName);

final Response response = resourceHandler.deleteGroupMapping(authorizerName, groupMappingName);
performAuditIfSuccess(authorizerName, req, response, "Delete groupMapping[%s]", groupMappingName);

return response;
}

/**
Expand Down Expand Up @@ -338,7 +368,11 @@ public Response createRole(
)
{
authValidator.validateAuthorizerName(authorizerName);
return resourceHandler.createRole(authorizerName, roleName);

final Response response = resourceHandler.createRole(authorizerName, roleName);
performAuditIfSuccess(authorizerName, req, response, "Create role[%s]", roleName);

return response;
}

/**
Expand All @@ -361,7 +395,11 @@ public Response deleteRole(
)
{
authValidator.validateAuthorizerName(authorizerName);
return resourceHandler.deleteRole(authorizerName, roleName);

final Response response = resourceHandler.deleteRole(authorizerName, roleName);
performAuditIfSuccess(authorizerName, req, response, "Delete role[%s]", roleName);

return response;
}

/**
Expand All @@ -386,7 +424,11 @@ public Response assignRoleToUser(
)
{
authValidator.validateAuthorizerName(authorizerName);
return resourceHandler.assignRoleToUser(authorizerName, userName, roleName);

final Response response = resourceHandler.assignRoleToUser(authorizerName, userName, roleName);
performAuditIfSuccess(authorizerName, req, response, "Assign role[%s] to user[%s]", roleName, userName);

return response;
}

/**
Expand All @@ -411,7 +453,11 @@ public Response unassignRoleFromUser(
)
{
authValidator.validateAuthorizerName(authorizerName);
return resourceHandler.unassignRoleFromUser(authorizerName, userName, roleName);

final Response response = resourceHandler.unassignRoleFromUser(authorizerName, userName, roleName);
performAuditIfSuccess(authorizerName, req, response, "Unassign role[%s] from user[%s]", roleName, userName);

return response;
}

/**
Expand All @@ -436,7 +482,12 @@ public Response assignRoleToGroupMapping(
)
{
authValidator.validateAuthorizerName(authorizerName);
return resourceHandler.assignRoleToGroupMapping(authorizerName, groupMappingName, roleName);
final Response response = resourceHandler.assignRoleToGroupMapping(authorizerName, groupMappingName, roleName);

String msgFormat = "Assign role[%s] to groupMapping[%s]";
performAuditIfSuccess(authorizerName, req, response, msgFormat, roleName, groupMappingName);

return response;
}

/**
Expand All @@ -461,7 +512,12 @@ public Response unassignRoleFromGroupMapping(
)
{
authValidator.validateAuthorizerName(authorizerName);
return resourceHandler.unassignRoleFromGroupMapping(authorizerName, groupMappingName, roleName);

final Response response = resourceHandler.unassignRoleFromGroupMapping(authorizerName, groupMappingName, roleName);
String msgFormat = "Unassign role[%s] from groupMapping[%s]";
performAuditIfSuccess(authorizerName, req, response, msgFormat, roleName, groupMappingName);

return response;
}

/**
Expand All @@ -486,7 +542,11 @@ public Response setRolePermissions(
)
{
authValidator.validateAuthorizerName(authorizerName);
return resourceHandler.setRolePermissions(authorizerName, roleName, permissions);

final Response response = resourceHandler.setRolePermissions(authorizerName, roleName, permissions);
performAuditIfSuccess(authorizerName, req, response, "Set permissions[%s] for role[%s]", permissions, roleName);

return response;
}

/**
Expand Down Expand Up @@ -607,4 +667,35 @@ public Response authorizerGroupMappingUpdateListener(
authValidator.validateAuthorizerName(authorizerName);
return resourceHandler.authorizerGroupMappingUpdateListener(authorizerName, serializedGroupMappingAndRoleMap);
}

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

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

private void performAuditIfSuccess(
String authorizerName,
HttpServletRequest request,
Response response,
String msgFormat,
Object... args
)
{
if (isSuccess(response)) {
auditManager.doAudit(
AuditEntry.builder()
.key(authorizerName)
.type("basicAuthorization")
.auditInfo(AuthorizationUtils.buildAuditInfo(request))
.request(AuthorizationUtils.buildRequestInfo("coordinator", request))
.payload(StringUtils.format(msgFormat, args))
.build()
);
}
}
}
Loading
Loading