http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationFilter.java new file mode 100644 index 0000000..5663ed2 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationFilter.java @@ -0,0 +1,160 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.security.authentication; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.ambari.server.audit.event.AuditEvent; +import org.apache.ambari.server.audit.AuditLogger; +import org.apache.ambari.server.audit.event.LoginAuditEvent; +import org.apache.ambari.server.security.AmbariEntryPoint; +import org.apache.ambari.server.security.authorization.AuthorizationHelper; +import org.apache.ambari.server.security.authorization.PermissionHelper; +import org.apache.ambari.server.utils.RequestUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.crypto.codec.Base64; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; + +/** + * The purpose of this class is to check whether authentication is successful or not, + * and make an audit event + */ +public class AmbariAuthenticationFilter extends BasicAuthenticationFilter { + private static final Logger LOG = LoggerFactory.getLogger(AmbariAuthenticationFilter.class); + + /** + * Audit logger + */ + private AuditLogger auditLogger; + + private PermissionHelper permissionHelper; + + public AmbariAuthenticationFilter() { + super(); + } + + public AmbariAuthenticationFilter(AuthenticationManager authenticationManager, AuditLogger auditLogger, PermissionHelper permissionHelper, AmbariEntryPoint ambariEntryPoint) { + super(authenticationManager, ambariEntryPoint); + this.auditLogger = auditLogger; + this.permissionHelper = permissionHelper; + } + + /** + * Checks whether the authentication information is filled. If it is not, then a login failed audit event is logged + * @param req + * @param res + * @param chain + * @throws IOException + * @throws ServletException + */ + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) req; + String header = request.getHeader("Authorization"); + if (AuthorizationHelper.getAuthenticatedName() == null && (header == null || !header.startsWith("Basic "))) { + AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder() + .withRemoteIp(RequestUtils.getRemoteAddress(request)) + .withTimestamp(System.currentTimeMillis()) + .withReasonOfFailure("Authentication required") + .withUserName(null) + .build(); + auditLogger.log(loginFailedAuditEvent); + } + super.doFilter(req, res, chain); + } + + /** + * If the authentication was successful, then an audit event is logged about the success + * @param request + * @param response + * @param authResult + * @throws IOException + */ + @Override + protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException { + AuditEvent loginSucceededAuditEvent = LoginAuditEvent.builder() + .withRemoteIp(RequestUtils.getRemoteAddress(request)) + .withUserName(authResult.getName()) + .withTimestamp(System.currentTimeMillis()) + .withRoles(permissionHelper.getPermissionLabels(authResult)) + .build(); + auditLogger.log(loginSucceededAuditEvent); + } + + /** + * In the case of invalid username or password, the authentication fails and it is logged + * @param request + * @param response + * @param authEx + * @throws IOException + */ + @Override + protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx) throws IOException { + String header = request.getHeader("Authorization"); + String username = null; + try { + String[] decodedAuth = decodeAuth(header, request); + username = decodedAuth[0]; + } catch (Exception e) { + LOG.warn("Error occurred during decoding authorization header.",e); + } + AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder() + .withRemoteIp(RequestUtils.getRemoteAddress(request)) + .withTimestamp(System.currentTimeMillis()) + .withReasonOfFailure("Invalid username/password combination") + .withUserName(username) + .build(); + auditLogger.log(loginFailedAuditEvent); + } + + /** + * Helper function to decode Authorization header + * @param header + * @param request + * @return + * @throws IOException + */ + private String[] decodeAuth(String header, HttpServletRequest request) throws IOException { + byte[] base64Token = header.substring(6).getBytes("UTF-8"); + + byte[] decoded; + try { + decoded = Base64.decode(base64Token); + } catch (IllegalArgumentException ex) { + throw new BadCredentialsException("Failed to decode basic authentication token"); + } + + String token = new String(decoded, this.getCredentialsCharset(request)); + int delim = token.indexOf(":"); + if(delim == -1) { + throw new BadCredentialsException("Invalid basic authentication token"); + } else { + return new String[]{token.substring(0, delim), token.substring(delim + 1)}; + } + } +}
http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java index 4be804d..96d6131 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java @@ -19,10 +19,15 @@ package org.apache.ambari.server.security.authorization; import com.google.inject.Inject; +import org.apache.ambari.server.audit.event.AccessUnauthorizedAuditEvent; +import org.apache.ambari.server.audit.event.AuditEvent; +import org.apache.ambari.server.audit.AuditLogger; +import org.apache.ambari.server.audit.event.LoginAuditEvent; import org.apache.ambari.server.configuration.Configuration; import org.apache.ambari.server.orm.entities.PermissionEntity; import org.apache.ambari.server.orm.entities.PrivilegeEntity; import org.apache.ambari.server.security.authorization.internal.InternalAuthenticationToken; +import org.apache.ambari.server.utils.RequestUtils; import org.apache.ambari.server.view.ViewRegistry; import org.apache.commons.lang.StringUtils; import org.springframework.security.authentication.AnonymousAuthenticationToken; @@ -90,6 +95,11 @@ public class AmbariAuthorizationFilter implements Filter { @Inject private Users users; + @Inject + private AuditLogger auditLogger; + + @Inject PermissionHelper permissionHelper; + /** * The realm to use for the basic http auth */ @@ -111,6 +121,8 @@ public class AmbariAuthorizationFilter implements Filter { Authentication authentication = context.getAuthentication(); + AuditEvent auditEvent = null; + // If no explicit authenticated user is set, set it to the default user (if one is specified) if (authentication == null || authentication instanceof AnonymousAuthenticationToken) { Authentication defaultAuthentication = getDefaultAuthentication(); @@ -123,19 +135,26 @@ public class AmbariAuthorizationFilter implements Filter { !authentication.isAuthenticated()) { String token = httpRequest.getHeader(INTERNAL_TOKEN_HEADER); if (token != null) { - context.setAuthentication(new InternalAuthenticationToken(token)); + InternalAuthenticationToken internalAuthenticationToken = new InternalAuthenticationToken(token); + context.setAuthentication(internalAuthenticationToken); + LoginAuditEvent loginAuditEvent = LoginAuditEvent.builder() + .withUserName(internalAuthenticationToken.getName()) + .withRemoteIp(RequestUtils.getRemoteAddress(httpRequest)) + .withRoles(permissionHelper.getPermissionLabels(authentication)) + .withTimestamp(System.currentTimeMillis()).build(); + auditLogger.log(loginAuditEvent); } else { // for view access, we should redirect to the Ambari login if (requestURI.matches(VIEWS_CONTEXT_ALL_PATTERN)) { String queryString = httpRequest.getQueryString(); String requestedURL = queryString == null ? requestURI : (requestURI + '?' + queryString); String redirectURL = httpResponse.encodeRedirectURL(LOGIN_REDIRECT_BASE + requestedURL); - httpResponse.sendRedirect(redirectURL); - return; } else { httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Authentication required"); + httpResponse.flushBuffer(); } + return; } } else if (!authorizationPerformedInternally(requestURI)) { boolean authorized = false; @@ -188,6 +207,14 @@ public class AmbariAuthorizationFilter implements Filter { if (!authorized && (!httpRequest.getMethod().equals("GET") || requestURI.matches(API_LDAP_SYNC_EVENTS_ALL_PATTERN))) { + auditEvent = AccessUnauthorizedAuditEvent.builder() + .withHttpMethodName(httpRequest.getMethod()) + .withRemoteIp(RequestUtils.getRemoteAddress(httpRequest)) + .withResourcePath(httpRequest.getRequestURI()) + .withUserName(AuthorizationHelper.getAuthenticatedName()) + .withTimestamp(System.currentTimeMillis()) + .build(); + auditLogger.log(auditEvent); httpResponse.setHeader("WWW-Authenticate", "Basic realm=\"" + realm + "\""); httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "You do not have permissions to access this resource."); @@ -197,6 +224,16 @@ public class AmbariAuthorizationFilter implements Filter { } if (AuthorizationHelper.getAuthenticatedName() != null) { httpResponse.setHeader("User", AuthorizationHelper.getAuthenticatedName()); + if (httpResponse.getStatus() == HttpServletResponse.SC_FORBIDDEN) { + auditEvent = AccessUnauthorizedAuditEvent.builder() + .withHttpMethodName(httpRequest.getMethod()) + .withRemoteIp(RequestUtils.getRemoteAddress(httpRequest)) + .withResourcePath(httpRequest.getRequestURI()) + .withUserName(AuthorizationHelper.getAuthenticatedName()) + .withTimestamp(System.currentTimeMillis()) + .build(); + auditLogger.log(auditEvent); + } } chain.doFilter(request, response); } http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java index 15ef8d0..4007c76 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AuthorizationHelper.java @@ -17,11 +17,17 @@ */ package org.apache.ambari.server.security.authorization; +import com.google.common.collect.Lists; import com.google.inject.Singleton; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.orm.dao.ClusterDAO; +import org.apache.ambari.server.orm.dao.ViewInstanceDAO; import org.apache.ambari.server.orm.entities.PermissionEntity; 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.state.Clusters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; @@ -283,4 +289,27 @@ public class AuthorizationHelper { return loginAlias; } + + /** + * Retrieve authorization names based on the details of the authenticated user + * @param authentication the authenticated user and associated access privileges + * @return human readable role authorizations + */ + public static List<String> getAuthorizationNames(Authentication authentication) { + List<String> authorizationNames = Lists.newArrayList(); + if (authentication.getAuthorities() != null) { + for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) { + AmbariGrantedAuthority ambariGrantedAuthority = (AmbariGrantedAuthority) grantedAuthority; + + PrivilegeEntity privilegeEntity = ambariGrantedAuthority.getPrivilegeEntity(); + Collection<RoleAuthorizationEntity> roleAuthorizationEntities = + privilegeEntity.getPermission().getAuthorizations(); + for (RoleAuthorizationEntity entity : roleAuthorizationEntities) { + authorizationNames.add(entity.getAuthorizationName()); + } + } + } + return authorizationNames; + } + } http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/PermissionHelper.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/PermissionHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/PermissionHelper.java new file mode 100644 index 0000000..ecf2d7a --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/PermissionHelper.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.security.authorization; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.ambari.server.orm.dao.ClusterDAO; +import org.apache.ambari.server.orm.dao.ViewInstanceDAO; +import org.apache.ambari.server.orm.entities.PrivilegeEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +@Singleton +public class PermissionHelper { + + private final static Logger LOG = LoggerFactory.getLogger(PermissionHelper.class); + + @Inject + private ClusterDAO clusterDAO; + + @Inject + private ViewInstanceDAO viewInstanceDAO; + + /** + * Retrieve permission labels based on the details of the authenticated user + * @param authentication the authenticated user and associated access privileges + * @return human-readable permissions + */ + public Map<String,List<String>> getPermissionLabels(Authentication authentication) { + Map<String,List<String>> permissionLabels = new HashMap<>(); + if (authentication.getAuthorities() != null) { + for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) { + AmbariGrantedAuthority ambariGrantedAuthority = (AmbariGrantedAuthority) grantedAuthority; + + PrivilegeEntity privilegeEntity = ambariGrantedAuthority.getPrivilegeEntity(); + + String key = null; + try { + switch(privilegeEntity.getResource().getResourceType().getName()) { + case "CLUSTER": + key = clusterDAO.findByResourceId(privilegeEntity.getResource().getId()).getClusterName(); + break; + case "AMBARI": + key = "Ambari"; + break; + default: + key = viewInstanceDAO.findByResourceId(privilegeEntity.getResource().getId()).getLabel(); + break; + } + } catch (Throwable ignored) { + LOG.warn("Error occurred when cluster or view is searched based on resource id", ignored); + } + + if(key != null) { + if(!permissionLabels.containsKey(key)) { + permissionLabels.put(key, new LinkedList<String>()); + } + permissionLabels.get(key).add(privilegeEntity.getPermission().getPermissionLabel()); + } + } + } + return permissionLabels; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/serveraction/AbstractServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/AbstractServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/AbstractServerAction.java index 33191bf..ca4a92c 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/AbstractServerAction.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/AbstractServerAction.java @@ -24,11 +24,16 @@ import org.apache.ambari.server.actionmanager.HostRoleCommand; import org.apache.ambari.server.actionmanager.HostRoleStatus; import org.apache.ambari.server.agent.CommandReport; import org.apache.ambari.server.agent.ExecutionCommand; +import org.apache.ambari.server.audit.event.AuditEvent; +import org.apache.ambari.server.audit.AuditLogger; import org.apache.ambari.server.utils.StageUtils; import java.util.Collections; import java.util.Map; +import com.google.inject.Inject; +import com.google.inject.Injector; + /** * AbstractServerActionImpl is an abstract implementation of a ServerAction. * <p/> @@ -52,6 +57,9 @@ public abstract class AbstractServerAction implements ServerAction { */ protected ActionLog actionLog = new ActionLog(); + @Inject + private AuditLogger auditLogger; + @Override public ExecutionCommand getExecutionCommand() { return this.executionCommand; @@ -175,4 +183,8 @@ public abstract class AbstractServerAction implements ServerAction { return getConfigurations().get(configurationName); } + protected void auditLog(AuditEvent ae) { + auditLogger.log(ae); + } + } http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java index 81dae0e..8c728db 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java @@ -22,6 +22,7 @@ import com.google.inject.Inject; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.actionmanager.HostRoleStatus; import org.apache.ambari.server.agent.CommandReport; +import org.apache.ambari.server.audit.event.kerberos.CreateKeyTabKerberosAuditEvent; import org.apache.ambari.server.configuration.Configuration; import org.apache.ambari.server.controller.KerberosHelper; import org.apache.ambari.server.orm.dao.HostDAO; @@ -148,51 +149,55 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction { Map<String, String> kerberosConfiguration, Map<String, Object> requestSharedDataContext) throws AmbariException { + + + CreateKeyTabKerberosAuditEvent.CreateKeyTabKerberosAuditEventBuilder auditEventBuilder = CreateKeyTabKerberosAuditEvent.builder().withTimestamp(System.currentTimeMillis()); CommandReport commandReport = null; + String message = null; + try { + if (identityRecord != null) { + String dataDirectory = getDataDirectoryPath(); - if (identityRecord != null) { - String message; - String dataDirectory = getDataDirectoryPath(); - - if (operationHandler == null) { - message = String.format("Failed to create keytab file for %s, missing KerberosOperationHandler", evaluatedPrincipal); - actionLog.writeStdErr(message); - LOG.error(message); - commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); - } else if (dataDirectory == null) { - message = "The data directory has not been set. Generated keytab files can not be stored."; - LOG.error(message); - commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); - } else { - Map<String, String> principalPasswordMap = getPrincipalPasswordMap(requestSharedDataContext); - Map<String, Integer> principalKeyNumberMap = getPrincipalKeyNumberMap(requestSharedDataContext); - - String hostName = identityRecord.get(KerberosIdentityDataFileReader.HOSTNAME); - String keytabFilePath = identityRecord.get(KerberosIdentityDataFileReader.KEYTAB_FILE_PATH); - - if ((hostName != null) && !hostName.isEmpty() && (keytabFilePath != null) && !keytabFilePath.isEmpty()) { - Set<String> visitedPrincipalKeys = visitedIdentities.get(evaluatedPrincipal); - String visitationKey = String.format("%s|%s", hostName, keytabFilePath); - - if ((visitedPrincipalKeys == null) || !visitedPrincipalKeys.contains(visitationKey)) { - // Look up the current evaluatedPrincipal's password. - // If found create the keytab file, else try to find it in the cache. - String password = principalPasswordMap.get(evaluatedPrincipal); - Integer keyNumber = principalKeyNumberMap.get(evaluatedPrincipal); - - message = String.format("Creating keytab file for %s on host %s", evaluatedPrincipal, hostName); - LOG.info(message); - actionLog.writeStdOut(message); - - // Determine where to store the keytab file. It should go into a host-specific - // directory under the previously determined data directory. - File hostDirectory = new File(dataDirectory, hostName); - - // Ensure the host directory exists... - if (!hostDirectory.exists() && hostDirectory.mkdirs()) { - // Make sure only Ambari has access to this directory. - ensureAmbariOnlyAccess(hostDirectory); - } + if (operationHandler == null) { + message = String.format("Failed to create keytab file for %s, missing KerberosOperationHandler", evaluatedPrincipal); + actionLog.writeStdErr(message); + LOG.error(message); + commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); + } else if (dataDirectory == null) { + message = "The data directory has not been set. Generated keytab files can not be stored."; + LOG.error(message); + commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); + } else { + Map<String, String> principalPasswordMap = getPrincipalPasswordMap(requestSharedDataContext); + Map<String, Integer> principalKeyNumberMap = getPrincipalKeyNumberMap(requestSharedDataContext); + + String hostName = identityRecord.get(KerberosIdentityDataFileReader.HOSTNAME); + String keytabFilePath = identityRecord.get(KerberosIdentityDataFileReader.KEYTAB_FILE_PATH); + + if ((hostName != null) && !hostName.isEmpty() && (keytabFilePath != null) && !keytabFilePath.isEmpty()) { + Set<String> visitedPrincipalKeys = visitedIdentities.get(evaluatedPrincipal); + String visitationKey = String.format("%s|%s", hostName, keytabFilePath); + + if ((visitedPrincipalKeys == null) || !visitedPrincipalKeys.contains(visitationKey)) { + // Look up the current evaluatedPrincipal's password. + // If found create the keytab file, else try to find it in the cache. + String password = principalPasswordMap.get(evaluatedPrincipal); + Integer keyNumber = principalKeyNumberMap.get(evaluatedPrincipal); + + message = String.format("Creating keytab file for %s on host %s", evaluatedPrincipal, hostName); + LOG.info(message); + actionLog.writeStdOut(message); + auditEventBuilder.withPrincipal(evaluatedPrincipal).withHostName(hostName).withKeyTabFilePath(keytabFilePath); + + // Determine where to store the keytab file. It should go into a host-specific + // directory under the previously determined data directory. + File hostDirectory = new File(dataDirectory, hostName); + + // Ensure the host directory exists... + if (!hostDirectory.exists() && hostDirectory.mkdirs()) { + // Make sure only Ambari has access to this directory. + ensureAmbariOnlyAccess(hostDirectory); + } if (hostDirectory.exists()) { File destinationKeytabFile = new File(hostDirectory, DigestUtils.sha1Hex(keytabFilePath)); @@ -217,71 +222,79 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction { KerberosPrincipalEntity principalEntity = kerberosPrincipalDAO.find(evaluatedPrincipal); String cachedKeytabPath = (principalEntity == null) ? null : principalEntity.getCachedKeytabPath(); - if (cachedKeytabPath == null) { - message = String.format("Failed to create keytab for %s, missing cached file", evaluatedPrincipal); - actionLog.writeStdErr(message); - LOG.error(message); - commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); - } else { - try { - operationHandler.createKeytabFile(new File(cachedKeytabPath), destinationKeytabFile); - } catch (KerberosOperationException e) { - message = String.format("Failed to create keytab file for %s - %s", evaluatedPrincipal, e.getMessage()); + if (cachedKeytabPath == null) { + message = String.format("Failed to create keytab for %s, missing cached file", evaluatedPrincipal); actionLog.writeStdErr(message); - LOG.error(message, e); + LOG.error(message); commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); + } else { + try { + operationHandler.createKeytabFile(new File(cachedKeytabPath), destinationKeytabFile); + } catch (KerberosOperationException e) { + message = String.format("Failed to create keytab file for %s - %s", evaluatedPrincipal, e.getMessage()); + actionLog.writeStdErr(message); + LOG.error(message, e); + commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); + } } } - } - } else { - boolean canCache = ("true".equalsIgnoreCase(identityRecord.get(KerberosIdentityDataFileReader.KEYTAB_FILE_IS_CACHABLE))); - - Keytab keytab = createKeytab(evaluatedPrincipal, password, keyNumber, operationHandler, visitedPrincipalKeys != null, canCache, actionLog); + } else { + boolean canCache = ("true".equalsIgnoreCase(identityRecord.get(KerberosIdentityDataFileReader.KEYTAB_FILE_IS_CACHABLE))); - if (keytab != null) { - try { - if (operationHandler.createKeytabFile(keytab, destinationKeytabFile)) { - ensureAmbariOnlyAccess(destinationKeytabFile); + Keytab keytab = createKeytab(evaluatedPrincipal, password, keyNumber, operationHandler, visitedPrincipalKeys != null, canCache, actionLog); - message = String.format("Successfully created keytab file for %s at %s", evaluatedPrincipal, destinationKeytabFile.getAbsolutePath()); - LOG.debug(message); - } else { - message = String.format("Failed to create keytab file for %s at %s", evaluatedPrincipal, destinationKeytabFile.getAbsolutePath()); + if (keytab != null) { + try { + if (operationHandler.createKeytabFile(keytab, destinationKeytabFile)) { + ensureAmbariOnlyAccess(destinationKeytabFile); + + message = String.format("Successfully created keytab file for %s at %s", evaluatedPrincipal, destinationKeytabFile.getAbsolutePath()); + LOG.debug(message); + auditEventBuilder.withPrincipal(evaluatedPrincipal).withHostName(hostName).withKeyTabFilePath(destinationKeytabFile.getAbsolutePath()); + } else { + message = String.format("Failed to create keytab file for %s at %s", evaluatedPrincipal, destinationKeytabFile.getAbsolutePath()); + actionLog.writeStdErr(message); + LOG.error(message); + commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); + } + } catch (KerberosOperationException e) { + message = String.format("Failed to create keytab file for %s - %s", evaluatedPrincipal, e.getMessage()); actionLog.writeStdErr(message); - LOG.error(message); + LOG.error(message, e); commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); } - } catch (KerberosOperationException e) { - message = String.format("Failed to create keytab file for %s - %s", evaluatedPrincipal, e.getMessage()); - actionLog.writeStdErr(message); - LOG.error(message, e); + } else { commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); } - } else { - commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); - } - if (visitedPrincipalKeys == null) { - visitedPrincipalKeys = new HashSet<String>(); - visitedIdentities.put(evaluatedPrincipal, visitedPrincipalKeys); - } + if (visitedPrincipalKeys == null) { + visitedPrincipalKeys = new HashSet<String>(); + visitedIdentities.put(evaluatedPrincipal, visitedPrincipalKeys); + } - visitedPrincipalKeys.add(visitationKey); + visitedPrincipalKeys.add(visitationKey); + } + } else { + message = String.format("Failed to create keytab file for %s, the container directory does not exist: %s", + evaluatedPrincipal, hostDirectory.getAbsolutePath()); + actionLog.writeStdErr(message); + LOG.error(message); + commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); } } else { - message = String.format("Failed to create keytab file for %s, the container directory does not exist: %s", - evaluatedPrincipal, hostDirectory.getAbsolutePath()); - actionLog.writeStdErr(message); - LOG.error(message); - commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); + LOG.debug(String.format("Skipping previously processed keytab for %s on host %s", evaluatedPrincipal, hostName)); } - } else { - LOG.debug(String.format("Skipping previously processed keytab for %s on host %s", evaluatedPrincipal, hostName)); } } } + } finally { + if(commandReport != null && HostRoleStatus.FAILED.toString().equals(commandReport.getStatus())) { + auditEventBuilder.withReasonOfFailure(message == null ? "Unknown error" : message); + } + if(commandReport != null || auditEventBuilder.hasPrincipal()) { + auditLog(auditEventBuilder.build()); + } } - return commandReport; } http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java index 8009ae1..03a181e 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java @@ -22,6 +22,7 @@ import com.google.inject.Inject; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.actionmanager.HostRoleStatus; import org.apache.ambari.server.agent.CommandReport; +import org.apache.ambari.server.audit.event.kerberos.CreatePrincipalKerberosAuditEvent; import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO; import org.apache.ambari.server.orm.dao.KerberosPrincipalHostDAO; import org.apache.ambari.server.orm.entities.KerberosPrincipalEntity; @@ -173,92 +174,103 @@ public class CreatePrincipalsServerAction extends KerberosServerAction { Map<String, String> kerberosConfiguration, KerberosOperationHandler kerberosOperationHandler, ActionLog actionLog) { + CreatePrincipalKerberosAuditEvent.CreatePrincipalKerberosAuditEventBuilder auditEventBuilder = CreatePrincipalKerberosAuditEvent.builder() + .withTimestamp(System.currentTimeMillis()) + .withPrincipal(principal); CreatePrincipalResult result = null; + String message = null; + try { - String message = String.format("Creating principal, %s", principal); - LOG.info(message); - if (actionLog != null) { - actionLog.writeStdOut(message); - } - - Integer length; - Integer minLowercaseLetters; - Integer minUppercaseLetters; - Integer minDigits; - Integer minPunctuation; - Integer minWhitespace; - - if (kerberosConfiguration == null) { - length = null; - minLowercaseLetters = null; - minUppercaseLetters = null; - minDigits = null; - minPunctuation = null; - minWhitespace = null; - } else { - length = toInt(kerberosConfiguration.get("password_length")); - minLowercaseLetters = toInt(kerberosConfiguration.get("password_min_lowercase_letters")); - minUppercaseLetters = toInt(kerberosConfiguration.get("password_min_uppercase_letters")); - minDigits = toInt(kerberosConfiguration.get("password_min_digits")); - minPunctuation = toInt(kerberosConfiguration.get("password_min_punctuation")); - minWhitespace = toInt(kerberosConfiguration.get("password_min_whitespace")); - } - - String password = securePasswordHelper.createSecurePassword(length, minLowercaseLetters, minUppercaseLetters, minDigits, minPunctuation, minWhitespace); + message = String.format("Creating principal, %s", principal); + LOG.info(message); + if (actionLog != null) { + actionLog.writeStdOut(message); + } - try { + Integer length; + Integer minLowercaseLetters; + Integer minUppercaseLetters; + Integer minDigits; + Integer minPunctuation; + Integer minWhitespace; + + if (kerberosConfiguration == null) { + length = null; + minLowercaseLetters = null; + minUppercaseLetters = null; + minDigits = null; + minPunctuation = null; + minWhitespace = null; + } else { + length = toInt(kerberosConfiguration.get("password_length")); + minLowercaseLetters = toInt(kerberosConfiguration.get("password_min_lowercase_letters")); + minUppercaseLetters = toInt(kerberosConfiguration.get("password_min_uppercase_letters")); + minDigits = toInt(kerberosConfiguration.get("password_min_digits")); + minPunctuation = toInt(kerberosConfiguration.get("password_min_punctuation")); + minWhitespace = toInt(kerberosConfiguration.get("password_min_whitespace")); + } - if (kerberosOperationHandler.principalExists(principal)) { - // Create a new password since we need to know what it is. - // A new password/key would have been generated after exporting the keytab anyways. - message = String.format("Principal, %s, already exists, setting new password", principal); - LOG.warn(message); - if (actionLog != null) { - actionLog.writeStdOut(message); - } + String password = securePasswordHelper.createSecurePassword(length, minLowercaseLetters, minUppercaseLetters, minDigits, minPunctuation, minWhitespace); - Integer keyNumber = kerberosOperationHandler.setPrincipalPassword(principal, password); + try { - if (keyNumber != null) { - result = new CreatePrincipalResult(principal, password, keyNumber); - message = String.format("Successfully set password for %s", principal); - LOG.debug(message); - } else { - message = String.format("Failed to set password for %s - unknown reason", principal); - LOG.error(message); + if (kerberosOperationHandler.principalExists(principal)) { + // Create a new password since we need to know what it is. + // A new password/key would have been generated after exporting the keytab anyways. + message = String.format("Principal, %s, already exists, setting new password", principal); + LOG.warn(message); if (actionLog != null) { - actionLog.writeStdErr(message); + actionLog.writeStdOut(message); } - } - } else { - message = String.format("Creating new principal, %s", principal); - LOG.debug(message); - Integer keyNumber = kerberosOperationHandler.createPrincipal(principal, password, isServicePrincipal); - - if (keyNumber != null) { - result = new CreatePrincipalResult(principal, password, keyNumber); - message = String.format("Successfully created new principal, %s", principal); - LOG.debug(message); + Integer keyNumber = kerberosOperationHandler.setPrincipalPassword(principal, password); + + if (keyNumber != null) { + result = new CreatePrincipalResult(principal, password, keyNumber); + message = String.format("Successfully set password for %s", principal); + LOG.debug(message); + } else { + message = String.format("Failed to set password for %s - unknown reason", principal); + LOG.error(message); + if (actionLog != null) { + actionLog.writeStdErr(message); + } + } } else { - message = String.format("Failed to create principal, %s - unknown reason", principal); - LOG.error(message); - if (actionLog != null) { - actionLog.writeStdErr(message); + message = String.format("Creating new principal, %s", principal); + LOG.debug(message); + + Integer keyNumber = kerberosOperationHandler.createPrincipal(principal, password, isServicePrincipal); + + if (keyNumber != null) { + result = new CreatePrincipalResult(principal, password, keyNumber); + message = String.format("Successfully created new principal, %s", principal); + LOG.debug(message); + } else { + message = String.format("Failed to create principal, %s - unknown reason", principal); + LOG.error(message); + if (actionLog != null) { + actionLog.writeStdErr(message); + } } } - } - if (!kerberosPrincipalDAO.exists(principal)) { - kerberosPrincipalDAO.create(principal, isServicePrincipal); - } + if (!kerberosPrincipalDAO.exists(principal)) { + kerberosPrincipalDAO.create(principal, isServicePrincipal); + } - } catch (KerberosOperationException e) { - message = String.format("Failed to create principal, %s - %s", principal, e.getMessage()); - LOG.error(message, e); - if (actionLog != null) { - actionLog.writeStdErr(message); + } catch (KerberosOperationException e) { + message = String.format("Failed to create principal, %s - %s", principal, e.getMessage()); + LOG.error(message, e); + if (actionLog != null) { + actionLog.writeStdErr(message); + } + } + } finally { + if(result == null) { + auditEventBuilder.withReasonOfFailure(message == null ? "Unknown error" : message); } + auditLog(auditEventBuilder.build()); } return result; http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/DestroyPrincipalsServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/DestroyPrincipalsServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/DestroyPrincipalsServerAction.java index c2d8f6a..95dd7a3 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/DestroyPrincipalsServerAction.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/DestroyPrincipalsServerAction.java @@ -21,6 +21,7 @@ package org.apache.ambari.server.serveraction.kerberos; import com.google.inject.Inject; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.agent.CommandReport; +import org.apache.ambari.server.audit.event.kerberos.DestroyPrincipalKerberosAuditEvent; import org.apache.ambari.server.controller.KerberosHelper; import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO; import org.apache.ambari.server.orm.entities.KerberosPrincipalEntity; @@ -90,22 +91,28 @@ public class DestroyPrincipalsServerAction extends KerberosServerAction { String message = String.format("Destroying identity, %s", evaluatedPrincipal); LOG.info(message); actionLog.writeStdOut(message); + DestroyPrincipalKerberosAuditEvent.DestroyPrincipalKerberosAuditEventBuilder auditEventBuilder = DestroyPrincipalKerberosAuditEvent.builder() + .withTimestamp(System.currentTimeMillis()) + .withPrincipal(evaluatedPrincipal); try { - operationHandler.removePrincipal(evaluatedPrincipal); - } catch (KerberosOperationException e) { - message = String.format("Failed to remove identity for %s from the KDC - %s", evaluatedPrincipal, e.getMessage()); - LOG.warn(message); - actionLog.writeStdErr(message); - } - try { - KerberosPrincipalEntity principalEntity = kerberosPrincipalDAO.find(evaluatedPrincipal); + try { + operationHandler.removePrincipal(evaluatedPrincipal); + } catch (KerberosOperationException e) { + message = String.format("Failed to remove identity for %s from the KDC - %s", evaluatedPrincipal, e.getMessage()); + LOG.warn(message); + actionLog.writeStdErr(message); + auditEventBuilder.withReasonOfFailure(message); + } - if(principalEntity != null) { - String cachedKeytabPath = principalEntity.getCachedKeytabPath(); + try { + KerberosPrincipalEntity principalEntity = kerberosPrincipalDAO.find(evaluatedPrincipal); - kerberosPrincipalDAO.remove(principalEntity); + if (principalEntity != null) { + String cachedKeytabPath = principalEntity.getCachedKeytabPath(); + + kerberosPrincipalDAO.remove(principalEntity); // If a cached keytabs file exists for this principal, delete it. if (cachedKeytabPath != null) { @@ -125,11 +132,14 @@ public class DestroyPrincipalsServerAction extends KerberosServerAction { } } } - } - catch (Throwable t) { - message = String.format("Failed to remove identity for %s from the Ambari database - %s", evaluatedPrincipal, t.getMessage()); - LOG.warn(message); - actionLog.writeStdErr(message); + } catch (Throwable t) { + message = String.format("Failed to remove identity for %s from the Ambari database - %s", evaluatedPrincipal, t.getMessage()); + LOG.warn(message); + actionLog.writeStdErr(message); + auditEventBuilder.withReasonOfFailure(message); + } + } finally { + auditLog(auditEventBuilder.build()); } // There is no reason to fail this task if an identity was not removed. The cluster will work http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java index c710b8e..922cadb 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java @@ -21,6 +21,7 @@ package org.apache.ambari.server.serveraction.kerberos; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.actionmanager.HostRoleStatus; import org.apache.ambari.server.agent.CommandReport; +import org.apache.ambari.server.audit.event.kerberos.ChangeSecurityStateKerberosAuditEvent; import org.apache.ambari.server.state.Cluster; import org.apache.ambari.server.state.Host; import org.apache.ambari.server.state.SecurityState; @@ -91,6 +92,14 @@ public class FinalizeKerberosServerAction extends KerberosServerAction { actionLog.writeStdOut(message); sch.setSecurityState(sch.getDesiredSecurityState()); + ChangeSecurityStateKerberosAuditEvent auditEvent = ChangeSecurityStateKerberosAuditEvent.builder() + .withTimestamp(System.currentTimeMillis()) + .withService(sch.getServiceName()) + .withComponent(sch.getServiceComponentName()) + .withHostName(sch.getHostName()) + .withState(sch.getDesiredSecurityState().toString()) + .build(); + auditLog(auditEvent); } } } http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/java/org/apache/ambari/server/utils/RequestUtils.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/utils/RequestUtils.java b/ambari-server/src/main/java/org/apache/ambari/server/utils/RequestUtils.java new file mode 100644 index 0000000..0ac782f --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/utils/RequestUtils.java @@ -0,0 +1,88 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.utils; + +import com.google.common.collect.ImmutableSet; +import java.util.Arrays; +import java.util.Set; +import javax.servlet.http.HttpServletRequest; + +import org.apache.ambari.server.api.services.Request; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** + * The purpose of this helper is to get remote address from an HTTP request + */ +public class RequestUtils { + + private static Set<String> headersToCheck= ImmutableSet.copyOf(Arrays.asList( + "X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR")); + + /** + * Returns remote address + * @param request contains the details of http request + * @return + */ + public static String getRemoteAddress(HttpServletRequest request) { + String ip = null; + for (String header : headersToCheck) { + ip = request.getHeader(header); + if (!isRemoteAddressUnknown(ip)) { + break; + } + } + if (isRemoteAddressUnknown(ip)) { + ip = request.getRemoteAddr(); + } + return ip; + } + + /** + * Returns remote address by using {@link HttpServletRequest} from {@link RequestContextHolder} + * @return + */ + public static String getRemoteAddress() { + + if(hasValidRequest()) { + return getRemoteAddress(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()); + } + + return null; + } + + /** + * Checks whether ip address is null, empty or unknown + * @param ip + * @return + */ + private static boolean isRemoteAddressUnknown(String ip) { + return ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip); + } + + /** + * Checks if RequestContextHolder contains a valid HTTP request + * @return + */ + private static boolean hasValidRequest() { + return RequestContextHolder.getRequestAttributes() != null && + RequestContextHolder.getRequestAttributes() instanceof ServletRequestAttributes && + ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest() != null; + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml b/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml index 8b44b94..01243ef 100644 --- a/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml +++ b/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml @@ -25,7 +25,7 @@ <http use-expressions="true" disable-url-rewriting="true" entry-point-ref="ambariEntryPoint"> <intercept-url pattern="/**" access="isAuthenticated()"/> - <custom-filter ref="basicFilter" position="BASIC_AUTH_FILTER"/> + <custom-filter ref="ambariAuthenticationFilter" position="BASIC_AUTH_FILTER"/> <custom-filter ref="ambariJwtAuthenticationFilter" after="BASIC_AUTH_FILTER" /> <custom-filter ref="ambariAuthorizationFilter" before="FILTER_SECURITY_INTERCEPTOR"/> </http> @@ -47,8 +47,10 @@ <beans:bean id="ambariEntryPoint" class="org.apache.ambari.server.security.AmbariEntryPoint"> </beans:bean> - <beans:bean id="basicFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter"> + <beans:bean id="ambariAuthenticationFilter" class="org.apache.ambari.server.security.authentication.AmbariAuthenticationFilter"> <beans:constructor-arg ref="authenticationManager"/> + <beans:constructor-arg ref="auditLogger"/> + <beans:constructor-arg ref="permissionHelper"/> <beans:constructor-arg ref="ambariEntryPoint"/> </beans:bean> </beans:beans> http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionDBAccessorImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionDBAccessorImpl.java b/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionDBAccessorImpl.java index f88cf8e..50021c2 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionDBAccessorImpl.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionDBAccessorImpl.java @@ -35,6 +35,7 @@ import org.apache.ambari.server.agent.ActionQueue; import org.apache.ambari.server.agent.CommandReport; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.api.services.BaseRequest; +import org.apache.ambari.server.audit.AuditLogger; import org.apache.ambari.server.configuration.Configuration; import org.apache.ambari.server.controller.HostsMap; import org.apache.ambari.server.controller.internal.RequestResourceFilter; @@ -50,6 +51,7 @@ import org.apache.ambari.server.state.Clusters; import org.apache.ambari.server.state.StackId; import org.apache.ambari.server.state.svccomphost.ServiceComponentHostStartEvent; import org.apache.ambari.server.utils.StageUtils; +import org.easymock.EasyMock; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -124,6 +126,8 @@ public class TestActionDBAccessorImpl { new HostsMap((String) null), injector.getInstance(UnitOfWork.class), injector.getInstance(RequestFactory.class), null, null); + + EasyMock.replay(injector.getInstance(AuditLogger.class)); } @After http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionManager.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionManager.java b/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionManager.java index d7d08b1..baee0d8 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionManager.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionManager.java @@ -37,6 +37,7 @@ import org.apache.ambari.server.Role; import org.apache.ambari.server.RoleCommand; import org.apache.ambari.server.agent.ActionQueue; import org.apache.ambari.server.agent.CommandReport; +import org.apache.ambari.server.audit.AuditLogger; import org.apache.ambari.server.controller.HostsMap; import org.apache.ambari.server.orm.GuiceJpaInitializer; import org.apache.ambari.server.orm.InMemoryDefaultTestModule; @@ -44,6 +45,7 @@ import org.apache.ambari.server.state.Clusters; import org.apache.ambari.server.state.StackId; import org.apache.ambari.server.state.svccomphost.ServiceComponentHostStartEvent; import org.apache.ambari.server.utils.StageUtils; +import org.easymock.EasyMock; import org.junit.After; import org.junit.Before; import org.junit.Ignore; @@ -79,6 +81,8 @@ public class TestActionManager { StackId stackId = new StackId("HDP-0.1"); clusters.addCluster(clusterName, stackId); unitOfWork = injector.getInstance(UnitOfWork.class); + + EasyMock.replay(injector.getInstance(AuditLogger.class)); } @After http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatProcessorTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatProcessorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatProcessorTest.java index 7f3d763..bdbb9ab 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatProcessorTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatProcessorTest.java @@ -38,6 +38,7 @@ import org.apache.ambari.server.actionmanager.RequestFactory; import org.apache.ambari.server.actionmanager.Stage; import org.apache.ambari.server.actionmanager.StageFactory; import org.apache.ambari.server.api.services.AmbariMetaInfo; +import org.apache.ambari.server.audit.AuditLogger; import org.apache.ambari.server.configuration.Configuration; import org.apache.ambari.server.controller.HostsMap; import org.apache.ambari.server.orm.GuiceJpaInitializer; @@ -62,6 +63,7 @@ import org.apache.ambari.server.state.svccomphost.ServiceComponentHostInstallEve import org.apache.ambari.server.state.svccomphost.ServiceComponentHostUpgradeEvent; import org.apache.ambari.server.utils.EventBusSynchronizer; import org.apache.ambari.server.utils.StageUtils; +import org.easymock.EasyMock; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -134,6 +136,8 @@ public class HeartbeatProcessorTest { clusters = injector.getInstance(Clusters.class); injector.injectMembers(this); unitOfWork = injector.getInstance(UnitOfWork.class); + + EasyMock.replay(injector.getInstance(AuditLogger.class)); } @After http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/test/java/org/apache/ambari/server/api/services/BaseServiceTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/BaseServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/BaseServiceTest.java index 19eeffb..26eb705 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/BaseServiceTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/BaseServiceTest.java @@ -22,8 +22,16 @@ import org.apache.ambari.server.api.resources.ResourceInstance; import org.apache.ambari.server.api.services.parsers.BodyParseException; import org.apache.ambari.server.api.services.parsers.RequestBodyParser; import org.apache.ambari.server.api.services.serializers.ResultSerializer; +import org.apache.ambari.server.audit.request.RequestAuditLogger; import org.easymock.Capture; +import org.easymock.EasyMock; +import org.easymock.EasyMockRunner; +import org.easymock.Mock; +import org.easymock.MockType; +import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.RunWith; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; @@ -45,6 +53,7 @@ import static org.junit.Assert.assertEquals; /** * Base class for service unit tests. */ +@RunWith(EasyMockRunner.class) public abstract class BaseServiceTest { protected ResourceInstance resourceInstance = createNiceMock(ResourceInstance.class); @@ -88,6 +97,14 @@ public abstract class BaseServiceTest { return serializer; } + @Mock(type = MockType.NICE) + public RequestAuditLogger requestAuditLogger; + + @Before + public void before() throws Exception { + BaseService.init(requestAuditLogger); + } + @Test public void testService() throws Exception { List<ServiceTestInvocation> listTestInvocations = getTestInvocations(); @@ -121,6 +138,7 @@ public abstract class BaseServiceTest { } protected void assertCreateRequest(ServiceTestInvocation testMethod) { + addExpectForInitialRequest(testMethod); expect(requestFactory.createRequest(httpHeaders, requestBody, uriInfo, testMethod.getRequestType(), resourceInstance)).andReturn(request); } @@ -128,6 +146,8 @@ public abstract class BaseServiceTest { private void testMethod_bodyParseException(ServiceTestInvocation testMethod) throws Exception { + addExpectForInitialRequest(testMethod); + Capture<Result> resultCapture = new Capture<Result>(); BodyParseException e = new BodyParseException("TEST MSG"); expect(bodyParser.parse(testMethod.getBody())).andThrow(e); @@ -173,6 +193,13 @@ public abstract class BaseServiceTest { reset(resourceInstance, requestFactory, request, result, requestBody, bodyParser, status, serializer); } + private void addExpectForInitialRequest(ServiceTestInvocation testMethod) { + RequestBody rb = new RequestBody(); + rb.setBody("body"); + expect(requestFactory.createRequest(EasyMock.eq(httpHeaders), EasyMock.anyObject(RequestBody.class), EasyMock.eq(uriInfo), + EasyMock.eq(testMethod.getRequestType()), EasyMock.eq(resourceInstance))).andReturn(request); + } + public static class ServiceTestInvocation { private Request.Type m_type; private BaseService m_instance; http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/test/java/org/apache/ambari/server/audit/AccessUnauthorizedAuditEventTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/audit/AccessUnauthorizedAuditEventTest.java b/ambari-server/src/test/java/org/apache/ambari/server/audit/AccessUnauthorizedAuditEventTest.java new file mode 100644 index 0000000..70e4b64 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/audit/AccessUnauthorizedAuditEventTest.java @@ -0,0 +1,77 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.audit; + +import org.apache.ambari.server.audit.event.AccessUnauthorizedAuditEvent; +import org.junit.Test; + +import nl.jqno.equalsverifier.EqualsVerifier; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; + +public class AccessUnauthorizedAuditEventTest { + + @Test + public void testAuditMessage() throws Exception { + // Given + String testUserName = "USER1"; + String testRemoteIp = "127.0.0.1"; + String testHttpMethod = "GET"; + String testResourcePath = "/api/v1/hosts"; + + AccessUnauthorizedAuditEvent evnt = AccessUnauthorizedAuditEvent.builder() + .withTimestamp(System.currentTimeMillis()) + .withRemoteIp(testRemoteIp) + .withUserName(testUserName) + .withHttpMethodName(testHttpMethod) + .withResourcePath(testResourcePath) + .build(); + + // When + String actualAuditMessage = evnt.getAuditMessage(); + + // Then + 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)); + } + + + @Test + public void testTimestamp() throws Exception { + // Given + long testTimestamp = System.currentTimeMillis(); + AccessUnauthorizedAuditEvent evnt = AccessUnauthorizedAuditEvent.builder() + .withTimestamp(testTimestamp) + .build(); + + // When + long actualTimestamp = evnt.getTimestamp(); + + // Then + assertThat(actualTimestamp, equalTo(testTimestamp)); + + } + + @Test + public void testEquals() throws Exception { + EqualsVerifier.forClass(AccessUnauthorizedAuditEvent.class) + .verify(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java b/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java new file mode 100644 index 0000000..a146176 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/audit/LoginAuditEventTest.java @@ -0,0 +1,118 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.audit; + + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.ambari.server.audit.event.LoginAuditEvent; +import org.junit.Test; + +import nl.jqno.equalsverifier.EqualsVerifier; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; + +public class LoginAuditEventTest { + + @Test + public void testAuditMessage() throws Exception { + // Given + String testUserName = "USER1"; + String testRemoteIp = "127.0.0.1"; + + Map<String, List<String>> roles = new HashMap<>(); + roles.put("a", Arrays.asList("r1", "r2", "r3")); + + LoginAuditEvent evnt = LoginAuditEvent.builder() + .withTimestamp(System.currentTimeMillis()) + .withRemoteIp(testRemoteIp) + .withUserName(testUserName) + .withRoles(roles) + .build(); + + // When + String actualAuditMessage = evnt.getAuditMessage(); + + String roleMessage = System.lineSeparator() + " a: r1, r2, r3" + System.lineSeparator(); + + // Then + String expectedAuditMessage = String.format("User(%s), RemoteIp(%s), Operation(User login), Roles(%s), Status(Success)", + testUserName, testRemoteIp, roleMessage); + + assertThat(actualAuditMessage, equalTo(expectedAuditMessage)); + + } + + @Test + public void testFailedAuditMessage() throws Exception { + // Given + String testUserName = "USER1"; + String testRemoteIp = "127.0.0.1"; + String reason = "Bad credentials"; + + Map<String, List<String>> roles = new HashMap<>(); + roles.put("a", Arrays.asList("r1", "r2", "r3")); + + LoginAuditEvent evnt = LoginAuditEvent.builder() + .withTimestamp(System.currentTimeMillis()) + .withRemoteIp(testRemoteIp) + .withUserName(testUserName) + .withRoles(roles) + .withReasonOfFailure(reason) + .build(); + + // When + String actualAuditMessage = evnt.getAuditMessage(); + + String roleMessage = System.lineSeparator() + " a: r1, r2, r3" + System.lineSeparator(); + + // Then + String expectedAuditMessage = String.format("User(%s), RemoteIp(%s), Operation(User login), Roles(%s), Status(Failed), Reason(%s)", + testUserName, testRemoteIp, roleMessage, reason); + + assertThat(actualAuditMessage, equalTo(expectedAuditMessage)); + + } + + @Test + public void testTimestamp() throws Exception { + // Given + long testTimestamp = System.currentTimeMillis(); + LoginAuditEvent evnt = LoginAuditEvent.builder() + .withTimestamp(testTimestamp) + .build(); + + // When + long actualTimestamp = evnt.getTimestamp(); + + // Then + assertThat(actualTimestamp, equalTo(testTimestamp)); + + } + + @Test + public void testEquals() throws Exception { + EqualsVerifier.forClass(LoginAuditEvent.class) + .verify(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/test/java/org/apache/ambari/server/audit/LogoutAuditEventTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/audit/LogoutAuditEventTest.java b/ambari-server/src/test/java/org/apache/ambari/server/audit/LogoutAuditEventTest.java new file mode 100644 index 0000000..7fb6fef --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/audit/LogoutAuditEventTest.java @@ -0,0 +1,74 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.audit; + +import nl.jqno.equalsverifier.EqualsVerifier; + +import org.apache.ambari.server.audit.event.LogoutAuditEvent; +import org.junit.Test; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; + +public class LogoutAuditEventTest { + + @Test + public void testAuditMessage() throws Exception { + // Given + String testUserName = "USER1"; + String testRemoteIp = "127.0.0.1"; + + LogoutAuditEvent evnt = LogoutAuditEvent.builder() + .withTimestamp(System.currentTimeMillis()) + .withRemoteIp(testRemoteIp) + .withUserName(testUserName) + .build(); + + // When + String actualAuditMessage = evnt.getAuditMessage(); + + // Then + String expectedAuditMessage = String.format("User(%s), RemoteIp(%s), Operation(Logout), Status(Success)", + testUserName, testRemoteIp); + + assertThat(actualAuditMessage, equalTo(expectedAuditMessage)); + + } + + @Test + public void testTimestamp() throws Exception { + // Given + long testTimestamp = System.currentTimeMillis(); + LogoutAuditEvent evnt = LogoutAuditEvent.builder() + .withTimestamp(testTimestamp) + .build(); + + // When + long actualTimestamp = evnt.getTimestamp(); + + // Then + assertThat(actualTimestamp, equalTo(testTimestamp)); + + } + + @Test + public void testEquals() throws Exception { + EqualsVerifier.forClass(LogoutAuditEvent.class) + .verify(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/test/java/org/apache/ambari/server/audit/OperationStatusAuditEventTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/audit/OperationStatusAuditEventTest.java b/ambari-server/src/test/java/org/apache/ambari/server/audit/OperationStatusAuditEventTest.java new file mode 100644 index 0000000..0d2e710 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/audit/OperationStatusAuditEventTest.java @@ -0,0 +1,74 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.audit; + +import org.apache.ambari.server.audit.event.OperationStatusAuditEvent; +import org.junit.Test; + +import nl.jqno.equalsverifier.EqualsVerifier; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; + +public class OperationStatusAuditEventTest { + + @Test + public void testAuditMessage() throws Exception { + // Given + Long testRequestId = 100L; + String testStatus = "IN PROGRESS"; + + OperationStatusAuditEvent evnt = OperationStatusAuditEvent.builder() + .withTimestamp(System.currentTimeMillis()) + .withRequestId(testRequestId.toString()) + .withStatus(testStatus) + .withRequestContext("Start Service") + .build(); + + // When + String actualAuditMessage = evnt.getAuditMessage(); + + // Then + String expectedAuditMessage = String.format("Operation(Start Service), Status(%s), RequestId(%s)", testStatus, testRequestId); + + assertThat(actualAuditMessage, equalTo(expectedAuditMessage)); + } + + @Test + public void testTimestamp() throws Exception { + // Given + long testTimestamp = System.currentTimeMillis(); + OperationStatusAuditEvent evnt = OperationStatusAuditEvent.builder() + .withTimestamp(testTimestamp) + .build(); + + // When + long actualTimestamp = evnt.getTimestamp(); + + // Then + assertThat(actualTimestamp, equalTo(testTimestamp)); + + } + + @Test + public void testEquals() throws Exception { + EqualsVerifier.forClass(OperationStatusAuditEvent.class) + .verify(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/test/java/org/apache/ambari/server/audit/StartOperationRequestAuditEventTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/audit/StartOperationRequestAuditEventTest.java b/ambari-server/src/test/java/org/apache/ambari/server/audit/StartOperationRequestAuditEventTest.java new file mode 100644 index 0000000..a2097d5 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/audit/StartOperationRequestAuditEventTest.java @@ -0,0 +1,79 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.audit; + +import org.apache.ambari.server.audit.event.LoginAuditEvent; +import org.apache.ambari.server.audit.event.request.StartOperationRequestAuditEvent; +import org.junit.Test; + +import nl.jqno.equalsverifier.EqualsVerifier; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; + +public class StartOperationRequestAuditEventTest { + + @Test + public void testAuditMessage() throws Exception { + // Given + String testUserName = "USER1"; + String testRemoteIp = "127.0.0.1"; + String testRequestDetails = "{ \"key\": \"value\"}"; + Long testRequestId = 100L; + + StartOperationRequestAuditEvent evnt = StartOperationRequestAuditEvent.builder() + .withTimestamp(System.currentTimeMillis()) + .withRemoteIp(testRemoteIp) + .withUserName(testUserName) + .withOperation(testRequestDetails) + .withRequestId(testRequestId.toString()) + .build(); + + // When + String actualAuditMessage = evnt.getAuditMessage(); + + // Then + String expectedAuditMessage = String.format("User(%s), RemoteIp(%s), Operation(%s), RequestId(%d), Status(Successfully queued)", testUserName, testRemoteIp, testRequestDetails, testRequestId); + + assertThat(actualAuditMessage, equalTo(expectedAuditMessage)); + + } + + @Test + public void testTimestamp() throws Exception { + // Given + long testTimestamp = System.currentTimeMillis(); + StartOperationRequestAuditEvent evnt = StartOperationRequestAuditEvent.builder() + .withTimestamp(testTimestamp) + .build(); + + // When + long actualTimestamp = evnt.getTimestamp(); + + // Then + assertThat(actualTimestamp, equalTo(testTimestamp)); + + } + + + @Test + public void testEquals() throws Exception { + EqualsVerifier.forClass(LoginAuditEvent.class) + .verify(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/test/java/org/apache/ambari/server/audit/request/AbstractBaseCreator.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/audit/request/AbstractBaseCreator.java b/ambari-server/src/test/java/org/apache/ambari/server/audit/request/AbstractBaseCreator.java new file mode 100644 index 0000000..02ecb00 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/audit/request/AbstractBaseCreator.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.audit.request; + +import org.apache.ambari.server.api.services.Request; +import org.apache.ambari.server.api.services.Result; +import org.apache.ambari.server.audit.event.AuditEvent; + +public abstract class AbstractBaseCreator implements RequestAuditEventCreator { + + public String getPrefix() { + return this.getClass().getName(); + } + + @Override + public AuditEvent createAuditEvent(final Request request, final Result result) { + return new AuditEvent() { + @Override + public Long getTimestamp() { + return System.currentTimeMillis(); + } + + @Override + public String getAuditMessage() { + return getPrefix() + " " + String.format("%s %s %s %s %s", request.getRequestType(), request.getURI(), result.getStatus().getStatusCode(), result.getStatus().getStatus(), result.getStatus().getMessage()); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/test/java/org/apache/ambari/server/audit/request/AllGetCreator.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/audit/request/AllGetCreator.java b/ambari-server/src/test/java/org/apache/ambari/server/audit/request/AllGetCreator.java new file mode 100644 index 0000000..b40e036 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/audit/request/AllGetCreator.java @@ -0,0 +1,44 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.audit.request; + +import java.util.Collections; +import java.util.Set; + +import org.apache.ambari.server.api.services.Request; +import org.apache.ambari.server.api.services.ResultStatus; +import org.apache.ambari.server.controller.spi.Resource; + +public class AllGetCreator extends AbstractBaseCreator { + + @Override + public Set<Request.Type> getRequestTypes() { + return Collections.singleton(Request.Type.GET); + } + + @Override + public Set<Resource.Type> getResourceTypes() { + return null; + } + + @Override + public Set<ResultStatus.STATUS> getResultStatuses() { + return null; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/46a34ccd/ambari-server/src/test/java/org/apache/ambari/server/audit/request/AllPostAndPutCreator.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/audit/request/AllPostAndPutCreator.java b/ambari-server/src/test/java/org/apache/ambari/server/audit/request/AllPostAndPutCreator.java new file mode 100644 index 0000000..45aa1df --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/audit/request/AllPostAndPutCreator.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.audit.request; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.ambari.server.api.services.Request; +import org.apache.ambari.server.api.services.ResultStatus; +import org.apache.ambari.server.controller.spi.Resource; + +public class AllPostAndPutCreator extends AbstractBaseCreator { + + @Override + public Set<Request.Type> getRequestTypes() { + return new HashSet<Request.Type>(Arrays.asList(Request.Type.POST, Request.Type.PUT)); + } + + @Override + public Set<Resource.Type> getResourceTypes() { + return null; + } + + @Override + public Set<ResultStatus.STATUS> getResultStatuses() { + return null; + } +}