Skip to content
Permalink
Browse files
AMBARI-25019. Update Ambari audit logger to handle proxied users (dly… (
#2702)

AMBARI-25019. Update Ambari audit logger to handle proxied users (dlysnichenko)
  • Loading branch information
dlysnichenko committed Dec 31, 2018
1 parent bde5e12 commit 0717fb84a2034b2204b346edcc86d94796f67395
Showing 14 changed files with 227 additions and 39 deletions.
@@ -936,20 +936,22 @@ private void addRequestToAuditlogCache(Request request) {
RequestDetails requestDetails = new RequestDetails();
requestDetails.setNumberOfTasks(numberOfTasks);
requestDetails.setUserName(AuthorizationHelper.getAuthenticatedName());
requestDetails.setProxyUserName(AuthorizationHelper.getProxyUserName());
auditlogRequestCache.put(request.getRequestId(), requestDetails);
}
}

/**
* AuditLog operation status change
*
* @param requestId
*/
private void auditLog(HostRoleCommandEntity commandEntity, Long requestId) {
if(!auditLogger.isEnabled()) {
if (!auditLogger.isEnabled()) {
return;
}

if(requestId != null) {
if (requestId != null) {
HostRoleStatus lastTaskStatus = updateAuditlogCache(commandEntity, requestId);

// details must not be null
@@ -961,12 +963,13 @@ private void auditLog(HostRoleCommandEntity commandEntity, Long requestId) {
RequestEntity request = requestDAO.findByPK(requestId);
String context = request != null ? request.getRequestContext() : null;
AuditEvent auditEvent = OperationStatusAuditEvent.builder()
.withRequestId(String.valueOf(requestId))
.withStatus(String.valueOf(calculatedStatus))
.withRequestContext(context)
.withUserName(details.getUserName())
.withTimestamp(System.currentTimeMillis())
.build();
.withRequestId(String.valueOf(requestId))
.withStatus(String.valueOf(calculatedStatus))
.withRequestContext(context)
.withUserName(details.getUserName())
.withProxyUserName(details.getProxyUserName())
.withTimestamp(System.currentTimeMillis())
.build();
auditLogger.log(auditEvent);

details.setLastStatus(calculatedStatus);
@@ -1011,6 +1014,7 @@ private void logTask(HostRoleCommandEntity commandEntity, Long requestId, HostRo
.withTaskId(String.valueOf(commandEntity.getTaskId()))
.withHostName(commandEntity.getHostName())
.withUserName(details.getUserName())
.withProxyUserName(details.getProxyUserName())
.withOperation(commandEntity.getRoleCommand() + " " + commandEntity.getRole())
.withDetails(commandEntity.getCommandDetail())
.withStatus(commandEntity.getStatus().toString())
@@ -1047,6 +1051,11 @@ private static class RequestDetails {
*/
Map<Component, HostRoleStatus> tasks = new HashMap<>();

/**
* Name of the proxy user if proxied
*/
private String proxyUserName;

public HostRoleStatus getLastStatus() {
return lastStatus;
}
@@ -1083,6 +1092,14 @@ public Collection<HostRoleStatus> getTaskStatuses() {
return getTasks().values();
}

public String getProxyUserName() {
return proxyUserName;
}

public void setProxyUserName(String proxyUserName) {
this.proxyUserName = proxyUserName;
}

/**
* This nested class is the key for the {@link RequestDetails#tasks} map
*/
@@ -65,6 +65,7 @@ private void auditLog(HttpServletRequest servletRequest) {
.withTimestamp(System.currentTimeMillis())
.withRemoteIp(RequestUtils.getRemoteAddress(servletRequest))
.withUserName(AuthorizationHelper.getAuthenticatedName())
.withProxyUserName(AuthorizationHelper.getProxyUserName())
.build();
auditLogger.log(logoutEvent);
}
@@ -19,6 +19,7 @@
package org.apache.ambari.server.audit.event;

import org.apache.ambari.server.security.authorization.AuthorizationHelper;
import org.apache.commons.lang.StringUtils;

/**
* Base class for audit events which are result of user actions. It appends
@@ -35,6 +36,11 @@ public abstract class AbstractUserAuditEvent extends AbstractAuditEvent {
*/
private String userName = AuthorizationHelper.getAuthenticatedName();

/**
* Name of the proxy user if proxied
*/
private String proxyUserName = AuthorizationHelper.getProxyUserName();

/**
* Ip of the user who started the operation. Note: remote ip might not be the original ip (proxies, routers can modify it)
*/
@@ -58,6 +64,12 @@ protected void buildAuditMessage(StringBuilder builder) {
.append("), RemoteIp(")
.append(this.remoteIp)
.append(")");
if (StringUtils.isNotEmpty(this.proxyUserName)){
builder
.append(", ProxyUser(")
.append(this.proxyUserName)
.append(")");
}
}

/**
@@ -72,6 +84,18 @@ public TBuilder withUserName(String userName) {
return self();
}

/**
* Sets the proxy user name.
*
* @param proxyUserName
* @return the builder
*/
public TBuilder withProxyUserName(String proxyUserName) {
this.proxyUserName = proxyUserName;

return self();
}

/**
* Sets the remote ip where the user action originated from.
*
@@ -25,9 +25,9 @@
* Audit event for tracking operations
*/
@Immutable
public class OperationStatusAuditEvent extends AbstractAuditEvent {
public class OperationStatusAuditEvent extends AbstractUserAuditEvent {

public static class OperationStatusAuditEventBuilder extends AbstractAuditEventBuilder<OperationStatusAuditEvent, OperationStatusAuditEventBuilder> {
public static class OperationStatusAuditEventBuilder extends AbstractUserAuditEventBuilder<OperationStatusAuditEvent, OperationStatusAuditEventBuilder> {

/**
* Request identifier
@@ -44,11 +44,6 @@ public static class OperationStatusAuditEventBuilder extends AbstractAuditEventB
*/
private String operation;

/**
* Name of the logged in user who sent the request
*/
private String userName;

private OperationStatusAuditEventBuilder() {
super(OperationStatusAuditEventBuilder.class);
}
@@ -65,10 +60,9 @@ protected OperationStatusAuditEvent newAuditEvent() {
*/
@Override
protected void buildAuditMessage(StringBuilder builder) {
super.buildAuditMessage(builder);
builder
.append("User(")
.append(this.userName)
.append("), Operation(")
.append(", Operation(")
.append(this.operation)
.append("), Status(")
.append(this.status)
@@ -92,11 +86,6 @@ public OperationStatusAuditEventBuilder withRequestContext(String operation) {
this.operation = operation;
return this;
}

public OperationStatusAuditEventBuilder withUserName(String userName) {
this.userName = userName;
return this;
}
}

private OperationStatusAuditEvent() {
@@ -25,9 +25,9 @@
* Audit event for tracking task status
*/
@Immutable
public class TaskStatusAuditEvent extends AbstractAuditEvent {
public class TaskStatusAuditEvent extends AbstractUserAuditEvent {

public static class TaskStatusAuditEventBuilder extends AbstractAuditEventBuilder<TaskStatusAuditEvent, TaskStatusAuditEventBuilder> {
public static class TaskStatusAuditEventBuilder extends AbstractUserAuditEventBuilder<TaskStatusAuditEvent, TaskStatusAuditEventBuilder> {

/**
* Request identifier
@@ -59,11 +59,6 @@ public static class TaskStatusAuditEventBuilder extends AbstractAuditEventBuilde
*/
private String details;

/**
* User name
*/
private String userName;

private TaskStatusAuditEventBuilder() {
super(TaskStatusAuditEventBuilder.class);
}
@@ -80,10 +75,9 @@ protected TaskStatusAuditEvent newAuditEvent() {
*/
@Override
protected void buildAuditMessage(StringBuilder builder) {
super.buildAuditMessage(builder);
builder
.append("User(")
.append(this.userName)
.append("), Operation(")
.append(", Operation(")
.append(this.operation);

if (details != null) {
@@ -132,10 +126,6 @@ public TaskStatusAuditEventBuilder withDetails(String details) {
this.details = details;
return this;
}
public TaskStatusAuditEventBuilder withUserName(String userName) {
this.userName = userName;
return this;
}
}

private TaskStatusAuditEvent() {
@@ -76,6 +76,7 @@ public void onSuccessfulAuthentication(AmbariAuthenticationFilter filter, HttpSe
AuditEvent loginSucceededAuditEvent = LoginAuditEvent.builder()
.withRemoteIp(RequestUtils.getRemoteAddress(servletRequest))
.withUserName(username)
.withProxyUserName(AuthorizationHelper.getProxyUserName(result))
.withTimestamp(System.currentTimeMillis())
.withRoles(permissionHelper.getPermissionLabels(result))
.build();
@@ -144,6 +145,7 @@ public void onUnsuccessfulAuthentication(AmbariAuthenticationFilter filter, Http
.withReasonOfFailure(message)
.withConsecutiveFailures(consecutiveFailures)
.withUserName(username)
.withProxyUserName(null)
.build();
auditLogger.log(loginFailedAuditEvent);
}
@@ -160,6 +162,7 @@ public void beforeAttemptAuthentication(AmbariAuthenticationFilter filter, Servl
.withTimestamp(System.currentTimeMillis())
.withReasonOfFailure("Authentication required")
.withUserName(null)
.withProxyUserName(null)
.build();
auditLogger.log(loginFailedAuditEvent);
}
@@ -182,6 +182,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
if(auditLogger.isEnabled()) {
LoginAuditEvent loginAuditEvent = LoginAuditEvent.builder()
.withUserName(internalAuthenticationToken.getName())
.withProxyUserName(AuthorizationHelper.getProxyUserName(internalAuthenticationToken))
.withRemoteIp(RequestUtils.getRemoteAddress(httpRequest))
.withRoles(permissionHelper.getPermissionLabels(authentication))
.withTimestamp(System.currentTimeMillis()).build();
@@ -264,6 +265,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
.withRemoteIp(RequestUtils.getRemoteAddress(httpRequest))
.withResourcePath(httpRequest.getRequestURI())
.withUserName(AuthorizationHelper.getAuthenticatedName())
.withProxyUserName(AuthorizationHelper.getProxyUserName())
.withTimestamp(System.currentTimeMillis())
.build();
auditLogger.log(auditEvent);
@@ -283,6 +285,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
.withRemoteIp(RequestUtils.getRemoteAddress(httpRequest))
.withResourcePath(httpRequest.getRequestURI())
.withUserName(AuthorizationHelper.getAuthenticatedName())
.withProxyUserName(AuthorizationHelper.getProxyUserName())
.withTimestamp(System.currentTimeMillis())
.build();
auditLogger.log(auditEvent);
@@ -29,6 +29,7 @@
import org.apache.ambari.server.orm.entities.PrivilegeEntity;
import org.apache.ambari.server.orm.entities.ResourceEntity;
import org.apache.ambari.server.orm.entities.RoleAuthorizationEntity;
import org.apache.ambari.server.security.authentication.AmbariProxiedUserDetailsImpl;
import org.apache.ambari.server.security.authentication.AmbariUserDetails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -58,6 +59,35 @@ public class AuthorizationHelper {
@Inject
static Provider<ViewInstanceDAO> viewInstanceDAOProvider;

/**
* Gets the name of the logged-in proxy user, if any.
*
* @param authentication
* @return the name of the logged-in proxy user
*/
public static String getProxyUserName(Authentication authentication) {
if (authentication==null){
return null;
}
Object userDetails = authentication.getPrincipal();
if (userDetails instanceof AmbariProxiedUserDetailsImpl) {
AmbariProxiedUserDetailsImpl ambariProxiedUserDetails = (AmbariProxiedUserDetailsImpl) userDetails;
return ambariProxiedUserDetails.getProxyUserDetails().getUsername();
}
return null;
}

/**
* Gets the name of the logged-in proxy user, if any.
*
* @return the name of the logged-in proxy user
*/
public static String getProxyUserName() {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication auth = securityContext.getAuthentication();
return getProxyUserName(auth);
}

/**
* Converts collection of RoleEntities to collection of GrantedAuthorities
*/
@@ -31,6 +31,7 @@ public class AccessUnauthorizedAuditEventTest {
public void testAuditMessage() throws Exception {
// Given
String testUserName = "USER1";
String testProxyUserName = "PROXYUSER1";
String testRemoteIp = "127.0.0.1";
String testHttpMethod = "GET";
String testResourcePath = "/api/v1/hosts";
@@ -39,6 +40,7 @@ public void testAuditMessage() throws Exception {
.withTimestamp(System.currentTimeMillis())
.withRemoteIp(testRemoteIp)
.withUserName(testUserName)
.withProxyUserName(null)
.withHttpMethodName(testHttpMethod)
.withResourcePath(testResourcePath)
.build();
@@ -50,6 +52,23 @@ public void testAuditMessage() throws Exception {
String expectedAuditMessage = String.format("User(%s), RemoteIp(%s), Operation(%s), ResourcePath(%s), Status(Failed), Reason(Access not authorized)", testUserName, testRemoteIp, testHttpMethod, testResourcePath);

assertThat(actualAuditMessage, equalTo(expectedAuditMessage));

evnt = AccessUnauthorizedAuditEvent.builder()
.withTimestamp(System.currentTimeMillis())
.withRemoteIp(testRemoteIp)
.withUserName(testUserName)
.withProxyUserName(testProxyUserName)
.withHttpMethodName(testHttpMethod)
.withResourcePath(testResourcePath)
.build();

// When
actualAuditMessage = evnt.getAuditMessage();

// Then
expectedAuditMessage = String.format("User(%s), RemoteIp(%s), ProxyUser(PROXYUSER1), Operation(%s), ResourcePath(%s), Status(Failed), Reason(Access not authorized)", testUserName, testRemoteIp, testHttpMethod, testResourcePath);

assertThat(actualAuditMessage, equalTo(expectedAuditMessage));
}


0 comments on commit 0717fb8

Please sign in to comment.