Repository: ambari Updated Branches: refs/heads/trunk 8425f1fa7 -> b4320b5a8
AMBARI-18406. Create authentication filter to perform Kerberos authentication for Ambari (rlevas) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/b4320b5a Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/b4320b5a Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/b4320b5a Branch: refs/heads/trunk Commit: b4320b5a8d29b812e9fe86da69a219a17d5e4ea7 Parents: 8425f1f Author: Robert Levas <rle...@hortonworks.com> Authored: Tue Sep 20 14:45:23 2016 -0400 Committer: Robert Levas <rle...@hortonworks.com> Committed: Tue Sep 20 14:45:23 2016 -0400 ---------------------------------------------------------------------- ambari-project/pom.xml | 5 + ambari-server/pom.xml | 6 +- .../server/configuration/Configuration.java | 30 ++- .../server/controller/KerberosHelper.java | 2 + .../server/controller/KerberosHelperImpl.java | 18 +- .../server/security/AmbariEntryPoint.java | 15 +- .../AmbariAuthToLocalUserDetailsService.java | 139 +++++++++++ .../AmbariKerberosAuthenticationFilter.java | 172 ++++++++++++++ .../kerberos/AmbariKerberosTicketValidator.java | 93 ++++++++ .../AbstractPrepareKerberosServerAction.java | 10 +- .../ConfigureAmbariIdentitiesServerAction.java | 235 +++++++++++++++++++ .../ConfigureAmbariIndetityServerAction.java | 208 ---------------- .../kerberos/CreatePrincipalsServerAction.java | 5 +- .../kerberos/KerberosServerAction.java | 12 +- .../webapp/WEB-INF/spring-security.xml | 32 ++- .../server/controller/KerberosHelperTest.java | 6 +- ...AmbariAuthToLocalUserDetailsServiceTest.java | 92 ++++++++ .../AmbariKerberosAuthenticationFilterTest.java | 133 +++++++++++ .../AmbariKerberosTicketValidatorTest.java | 49 ++++ 19 files changed, 1021 insertions(+), 241 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-project/pom.xml ---------------------------------------------------------------------- diff --git a/ambari-project/pom.xml b/ambari-project/pom.xml index 2615b46..4f045fe 100644 --- a/ambari-project/pom.xml +++ b/ambari-project/pom.xml @@ -132,6 +132,11 @@ <version>3.1.2.RELEASE</version> </dependency> <dependency> + <groupId>org.springframework.security.kerberos</groupId> + <artifactId>spring-security-kerberos-web</artifactId> + <version>1.0.1.RELEASE</version> + </dependency> + <dependency> <groupId>org.springframework</groupId> <artifactId>spring-mock</artifactId> <version>2.0.8</version> http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/pom.xml ---------------------------------------------------------------------- diff --git a/ambari-server/pom.xml b/ambari-server/pom.xml index 323ce22..32267c8 100644 --- a/ambari-server/pom.xml +++ b/ambari-server/pom.xml @@ -44,7 +44,7 @@ <stacksSrcLocation>src/main/resources/stacks/${stack.distribution}</stacksSrcLocation> <tarballResourcesFolder>src/main/resources</tarballResourcesFolder> <skipPythonTests>false</skipPythonTests> - <hadoop.version>2.7.1</hadoop.version> + <hadoop.version>2.7.2</hadoop.version> <empty.dir>src/main/package</empty.dir> <!-- any directory in project with not very big amount of files (not to waste-load them) --> </properties> <build> @@ -985,6 +985,10 @@ <artifactId>spring-security-web</artifactId> </dependency> <dependency> + <groupId>org.springframework.security.kerberos</groupId> + <artifactId>spring-security-kerberos-web</artifactId> + </dependency> + <dependency> <groupId>org.springframework</groupId> <artifactId>spring-mock</artifactId> <scope>test</scope> http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java index b2fa4c0..f1058b6 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java @@ -5284,9 +5284,11 @@ public class Configuration { try { orderedUserTypes.add(UserType.valueOf(type.toUpperCase())); } catch (IllegalArgumentException e) { - throw new IllegalArgumentException(String.format("While processing ordered user types from %s, " + + String message = String.format("While processing ordered user types from %s, " + "%s was found to be an invalid user type.", - KERBEROS_AUTH_USER_TYPES.getKey(), type), e); + KERBEROS_AUTH_USER_TYPES.getKey(), type); + LOG.error(message); + throw new IllegalArgumentException(message, e); } } } @@ -5320,9 +5322,11 @@ public class Configuration { // Validate the SPNEGO principal name to ensure it was set. // Log any found issues. if (StringUtils.isEmpty(kerberosAuthProperties.getSpnegoPrincipalName())) { - throw new IllegalArgumentException(String.format("The SPNEGO principal name specified in %s is empty. " + + String message = String.format("The SPNEGO principal name specified in %s is empty. " + "This will cause issues authenticating users using Kerberos.", - KERBEROS_AUTH_SPNEGO_PRINCIPAL.getKey())); + KERBEROS_AUTH_SPNEGO_PRINCIPAL.getKey()); + LOG.error(message); + throw new IllegalArgumentException(message); } // Get the SPNEGO keytab file. There is nothing special to process for this value. @@ -5331,19 +5335,25 @@ public class Configuration { // Validate the SPNEGO keytab file to ensure it was set, it exists and it is readable by Ambari. // Log any found issues. if (StringUtils.isEmpty(kerberosAuthProperties.getSpnegoKeytabFilePath())) { - throw new IllegalArgumentException(String.format("The SPNEGO keytab file path specified in %s is empty. " + + String message = String.format("The SPNEGO keytab file path specified in %s is empty. " + "This will cause issues authenticating users using Kerberos.", - KERBEROS_AUTH_SPNEGO_KEYTAB_FILE.getKey())); + KERBEROS_AUTH_SPNEGO_KEYTAB_FILE.getKey()); + LOG.error(message); + throw new IllegalArgumentException(message); } else { File keytabFile = new File(kerberosAuthProperties.getSpnegoKeytabFilePath()); if (!keytabFile.exists()) { - throw new IllegalArgumentException(String.format("The SPNEGO keytab file path (%s) specified in %s does not exist. " + + String message = String.format("The SPNEGO keytab file path (%s) specified in %s does not exist. " + "This will cause issues authenticating users using Kerberos.", - keytabFile.getAbsolutePath(), KERBEROS_AUTH_SPNEGO_KEYTAB_FILE.getKey())); + keytabFile.getAbsolutePath(), KERBEROS_AUTH_SPNEGO_KEYTAB_FILE.getKey()); + LOG.error(message); + throw new IllegalArgumentException(message); } else if (!keytabFile.canRead()) { - throw new IllegalArgumentException(String.format("The SPNEGO keytab file path (%s) specified in %s cannot be read. " + + String message = String.format("The SPNEGO keytab file path (%s) specified in %s cannot be read. " + "This will cause issues authenticating users using Kerberos.", - keytabFile.getAbsolutePath(), KERBEROS_AUTH_SPNEGO_KEYTAB_FILE.getKey())); + keytabFile.getAbsolutePath(), KERBEROS_AUTH_SPNEGO_KEYTAB_FILE.getKey()); + LOG.error(message); + throw new IllegalArgumentException(message); } } http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java index c4d21fc..1153d01 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java @@ -70,6 +70,8 @@ public interface KerberosHelper { String AMBARI_IDENTITY_NAME = "ambari-server"; + String SPNEGO_IDENTITY_NAME = "spnego"; + String CREATE_AMBARI_PRINCIPAL = "create_ambari_principal"; String MANAGE_IDENTITIES = "manage_identities"; http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java index 5bc5cd8..a3c6fd4 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java @@ -61,7 +61,7 @@ import org.apache.ambari.server.security.encryption.CredentialStoreService; import org.apache.ambari.server.serveraction.ActionLog; import org.apache.ambari.server.serveraction.ServerAction; import org.apache.ambari.server.serveraction.kerberos.CleanupServerAction; -import org.apache.ambari.server.serveraction.kerberos.ConfigureAmbariIndetityServerAction; +import org.apache.ambari.server.serveraction.kerberos.ConfigureAmbariIdentitiesServerAction; import org.apache.ambari.server.serveraction.kerberos.CreateKeytabFilesServerAction; import org.apache.ambari.server.serveraction.kerberos.CreatePrincipalsServerAction; import org.apache.ambari.server.serveraction.kerberos.DestroyPrincipalsServerAction; @@ -761,7 +761,7 @@ public class KerberosHelperImpl implements KerberosHelper { * @param ambariServerIdentity the ambari server's {@link KerberosIdentityDescriptor} * @param configurations a map of compiled configrations used for variable replacment * @throws AmbariException - * @see ConfigureAmbariIndetityServerAction#installAmbariServerIdentity(String, String, String, ActionLog) + * @see ConfigureAmbariIdentitiesServerAction#installAmbariServerIdentity(String, String, String, ActionLog) */ private void installAmbariIdentity(KerberosIdentityDescriptor ambariServerIdentity, Map<String, Map<String, String>> configurations) throws AmbariException { @@ -775,7 +775,7 @@ public class KerberosHelperImpl implements KerberosHelper { if(keytabDescriptor != null) { String keytabFilePath = variableReplacementHelper.replaceVariables(keytabDescriptor.getFile(), configurations); - injector.getInstance(ConfigureAmbariIndetityServerAction.class) + injector.getInstance(ConfigureAmbariIdentitiesServerAction.class) .installAmbariServerIdentity(principal, ambariServerPrincipalEntity.getCachedKeytabPath(), keytabFilePath, null); } } @@ -1259,7 +1259,7 @@ public class KerberosHelperImpl implements KerberosHelper { serviceName, componentName, kerberosDescriptor, filterContext); if (hostname.equals(ambariServerHostname)) { - addAmbariServerIdentity(kerberosEnvConfig.getProperties(), kerberosDescriptor, identities); + addAmbariServerIdentities(kerberosEnvConfig.getProperties(), kerberosDescriptor, identities); } if (!identities.isEmpty()) { @@ -1346,7 +1346,7 @@ public class KerberosHelperImpl implements KerberosHelper { * @param kerberosDescriptor the kerberos descriptor * @param identities the collection of identities to add to */ - void addAmbariServerIdentity(Map<String, String> kerberosEnvProperties, KerberosDescriptor kerberosDescriptor, List<KerberosIdentityDescriptor> identities) { + void addAmbariServerIdentities(Map<String, String> kerberosEnvProperties, KerberosDescriptor kerberosDescriptor, List<KerberosIdentityDescriptor> identities) { // Determine if we should _calculate_ the Ambari service identity. // If kerberos-env/create_ambari_principal is not set to false the identity should be calculated. boolean createAmbariPrincipal = (kerberosEnvProperties == null) || !"false".equalsIgnoreCase(kerberosEnvProperties.get(CREATE_AMBARI_PRINCIPAL)); @@ -1357,6 +1357,12 @@ public class KerberosHelperImpl implements KerberosHelper { if (ambariServerIdentity != null) { identities.add(ambariServerIdentity); } + + // Add the spnego principal for the Ambari server host.... + KerberosIdentityDescriptor spnegoIdentity = kerberosDescriptor.getIdentity(KerberosHelper.SPNEGO_IDENTITY_NAME); + if (spnegoIdentity != null) { + identities.add(spnegoIdentity); + } } } @@ -2799,7 +2805,7 @@ public class KerberosHelperImpl implements KerberosHelper { clusterHostInfoJson, "{}", hostParamsJson, - ConfigureAmbariIndetityServerAction.class, + ConfigureAmbariIdentitiesServerAction.class, event, commandParameters, "Configure Ambari Identity", http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/main/java/org/apache/ambari/server/security/AmbariEntryPoint.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/AmbariEntryPoint.java b/ambari-server/src/main/java/org/apache/ambari/server/security/AmbariEntryPoint.java index 2028f46..e37976f 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/AmbariEntryPoint.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/AmbariEntryPoint.java @@ -28,6 +28,19 @@ import java.io.IOException; public class AmbariEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { - response.sendError(HttpServletResponse.SC_FORBIDDEN, authException.getMessage()); + /* ***************************************************************************************** + * To maintain backward compatibility and respond with the appropriate response when + * authentication is needed, by default return an HTTP 403 status. + * + * However if requested by the user, respond such that the client is challenged to Negotiate + * and reissue the request with a Kerberos token. This response is an HTTP 401 status with the + * WWW-Authenticate: Negotiate" header. + * ****************************************************************************************** */ + if ("true".equalsIgnoreCase(request.getHeader("X-Negotiate-Authentication"))) { + response.setHeader("WWW-Authenticate", "Negotiate"); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication requested"); + } else { + response.sendError(HttpServletResponse.SC_FORBIDDEN, authException.getMessage()); + } } } http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariAuthToLocalUserDetailsService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariAuthToLocalUserDetailsService.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariAuthToLocalUserDetailsService.java new file mode 100644 index 0000000..3c62646 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariAuthToLocalUserDetailsService.java @@ -0,0 +1,139 @@ +/* + * 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.kerberos; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.security.authorization.AmbariGrantedAuthority; +import org.apache.ambari.server.security.authorization.UserType; +import org.apache.ambari.server.security.authorization.Users; +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.security.authentication.util.KerberosName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * AmbariAuthToLocalUserDetailsService is a {@link UserDetailsService} that translates + * a Kerberos principal name into a local username that may be used when looking up + * and Ambari user account. + */ +public class AmbariAuthToLocalUserDetailsService implements UserDetailsService { + private static final Logger LOG = LoggerFactory.getLogger(AmbariAuthToLocalUserDetailsService.class); + + private final Users users; + + private final List<UserType> userTypeOrder; + + /** + * Constructor. + * <p> + * Given the Ambari {@link Configuration}, initializes the {@link KerberosName} class using + * the <code>auth-to-local</code> rules from {@link AmbariKerberosAuthenticationProperties#getAuthToLocalRules()}. + * + * @param configuration the Ambari configuration data + * @param users the Ambari users access object + * @throws AmbariException if an error occurs parsing the user-provided auth-to-local rules + */ + public AmbariAuthToLocalUserDetailsService(Configuration configuration, Users users) throws AmbariException { + String authToLocalRules = null; + List<UserType> orderedUserTypes = null; + + if (configuration != null) { + AmbariKerberosAuthenticationProperties properties = configuration.getKerberosAuthenticationProperties(); + + if (properties != null) { + authToLocalRules = properties.getAuthToLocalRules(); + orderedUserTypes = properties.getOrderedUserTypes(); + } + } + + if (StringUtils.isEmpty(authToLocalRules)) { + authToLocalRules = "DEFAULT"; + } + + if ((orderedUserTypes == null) || orderedUserTypes.isEmpty()) { + orderedUserTypes = Collections.singletonList(UserType.LDAP); + } + + KerberosName.setRules(authToLocalRules); + + this.users = users; + this.userTypeOrder = orderedUserTypes; + } + + @Override + public UserDetails loadUserByUsername(String principal) throws UsernameNotFoundException { + KerberosName kerberosName = new KerberosName(principal); + + try { + String username = kerberosName.getShortName(); + + if (username == null) { + String message = String.format("Failed to translate %s to a local username during Kerberos authentication.", principal); + LOG.warn(message); + throw new UsernameNotFoundException(message); + } + + LOG.info("Translated {} to {} using auth-to-local rules during Kerberos authentication.", principal, username); + return createUser(username); + } catch (IOException e) { + String message = String.format("Failed to translate %s to a local username during Kerberos authentication: %s", principal, e.getLocalizedMessage()); + LOG.warn(message); + throw new UsernameNotFoundException(message, e); + } + } + + /** + * Given a username, finds an appropriate account in the Ambari database. + * <p> + * User accounts are searched in order of preferred user type as specified in the Ambari configuration + * ({@link Configuration#KERBEROS_AUTH_USER_TYPES}). + * + * @param username a username + * @return the user details of the found user, or <code>null</code> if an appropriate user was not found + */ + private UserDetails createUser(String username) { + // Iterate over the ordered user types... when an account for the username/type combination is + // found, build the related AmbariUserAuthentication instance and return it. Only the first + // match matters... this may be an issue and cause some ambiguity in the event multiple user + // types are specified in the configuration and multiple accounts for the same username, but + // different types (LOCAL vs LDAP, etc...). + for (UserType userType : userTypeOrder) { + org.apache.ambari.server.security.authorization.User user = users.getUser(username, userType); + + if (user != null) { + Collection<AmbariGrantedAuthority> userAuthorities = users.getUserAuthorities(user.getUserName(), user.getUserType()); + return new User(username, "", userAuthorities); + } + } + + String message = String.format("Failed find user account for user with username of %s during Kerberos authentication.", username); + LOG.warn(message); + throw new UsernameNotFoundException(message); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilter.java new file mode 100644 index 0000000..a5a3922 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilter.java @@ -0,0 +1,172 @@ +/* + * 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.kerberos; + +import org.apache.ambari.server.audit.AuditLogger; +import org.apache.ambari.server.audit.event.AuditEvent; +import org.apache.ambari.server.audit.event.LoginAuditEvent; +import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.security.authentication.AmbariAuthenticationFilter; +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.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; + +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 java.io.IOException; + +/** + * AmbariKerberosAuthenticationFilter extends the {@link SpnegoAuthenticationProcessingFilter} class + * to perform Kerberos-based authentication for Ambari. + * <p> + * If configured, auditing is performed using {@link AuditLogger}. + */ +public class AmbariKerberosAuthenticationFilter extends SpnegoAuthenticationProcessingFilter implements AmbariAuthenticationFilter { + + /** + * Audit logger + */ + private final AuditLogger auditLogger; + + /** + * A Boolean value indicating whether Kerberos authentication is enabled or not. + */ + private final boolean kerberosAuthenticationEnabled; + + /** + * Constructor. + * <p> + * Given supplied data, sets up the the {@link SpnegoAuthenticationProcessingFilter} to perform + * authentication and audit logging if configured do to so. + * + * @param authenticationManager the Spring authentication manager + * @param entryPoint the Spring entry point + * @param configuration the Ambari configuration data + * @param auditLogger an audit logger + * @param permissionHelper a permission helper to aid in audit logging + */ + public AmbariKerberosAuthenticationFilter(AuthenticationManager authenticationManager, final AuthenticationEntryPoint entryPoint, Configuration configuration, final AuditLogger auditLogger, final PermissionHelper permissionHelper) { + AmbariKerberosAuthenticationProperties kerberosAuthenticationProperties = (configuration == null) + ? null + : configuration.getKerberosAuthenticationProperties(); + + kerberosAuthenticationEnabled = (kerberosAuthenticationProperties != null) && kerberosAuthenticationProperties.isKerberosAuthenticationEnabled(); + + this.auditLogger = auditLogger; + + setAuthenticationManager(authenticationManager); + + setFailureHandler(new AuthenticationFailureHandler() { + @Override + public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { + if (auditLogger.isEnabled()) { + AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder() + .withRemoteIp(RequestUtils.getRemoteAddress(httpServletRequest)) + .withTimestamp(System.currentTimeMillis()) + .withReasonOfFailure(e.getLocalizedMessage()) + .build(); + auditLogger.log(loginFailedAuditEvent); + } + + entryPoint.commence(httpServletRequest, httpServletResponse, e); + } + }); + + setSuccessHandler(new AuthenticationSuccessHandler() { + @Override + public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { + if (auditLogger.isEnabled()) { + AuditEvent loginSucceededAuditEvent = LoginAuditEvent.builder() + .withRemoteIp(RequestUtils.getRemoteAddress(httpServletRequest)) + .withUserName(authentication.getName()) + .withTimestamp(System.currentTimeMillis()) + .withRoles(permissionHelper.getPermissionLabels(authentication)) + .build(); + auditLogger.log(loginSucceededAuditEvent); + } + } + }); + } + + /** + * Tests to determine if this authentication filter is applicable given the Ambari configuration + * and the user's HTTP request. + * <p> + * If the Ambari configuration indicates the Kerberos authentication is enabled and the HTTP request + * contains the appropriate <code>Authorization</code> header, than this filter may be applied; + * otherwise it should be skipped. + * + * @param httpServletRequest the request + * @return true if this filter should be applied; false otherwise + */ + @Override + public boolean shouldApply(HttpServletRequest httpServletRequest) { + if (kerberosAuthenticationEnabled) { + String header = httpServletRequest.getHeader("Authorization"); + return (header != null) && (header.startsWith("Negotiate ") || header.startsWith("Kerberos ")); + } else { + return false; + } + } + + /** + * Performs the logic for this filter. + * <p> + * Checks whether the authentication information is filled. If it is not, then a login failed audit event is logged. + * <p> + * Then, forwards the workflow to {@link SpnegoAuthenticationProcessingFilter#doFilter(ServletRequest, ServletResponse, FilterChain)} + * + * @param servletRequest the request + * @param servletResponse the response + * @param filterChain the Spring filter chain + * @throws IOException + * @throws ServletException + */ + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + + if (shouldApply(httpServletRequest)) { + if (auditLogger.isEnabled() && (AuthorizationHelper.getAuthenticatedName() == null)) { + AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder() + .withRemoteIp(RequestUtils.getRemoteAddress(httpServletRequest)) + .withTimestamp(System.currentTimeMillis()) + .withReasonOfFailure("Authentication required") + .withUserName(null) + .build(); + auditLogger.log(loginFailedAuditEvent); + } + + super.doFilter(servletRequest, servletResponse, filterChain); + } else { + filterChain.doFilter(servletRequest, servletResponse); + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosTicketValidator.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosTicketValidator.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosTicketValidator.java new file mode 100644 index 0000000..bb57108 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosTicketValidator.java @@ -0,0 +1,93 @@ +/* + * 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.kerberos; + +import org.apache.ambari.server.configuration.Configuration; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.kerberos.authentication.KerberosTicketValidation; +import org.springframework.security.kerberos.authentication.KerberosTicketValidator; +import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator; + +/** + * AmbariKerberosTicketValidator is a {@link KerberosTicketValidator} implementation that delegates + * to a {@link SunJaasKerberosTicketValidator}, if Kerberos authentication is enabled. + * <p> + * If Kerberos authentication is enabled, the following properties are set: + * <ul> + * <li>{@link SunJaasKerberosTicketValidator#setServicePrincipal(String)} using the Ambari server property from {@link Configuration#KERBEROS_AUTH_SPNEGO_PRINCIPAL}</li> + * <li>{@link SunJaasKerberosTicketValidator#setKeyTabLocation(Resource)} using the Ambari server property from {@link Configuration#KERBEROS_AUTH_SPNEGO_KEYTAB_FILE}</li> + * </ul> + */ +public class AmbariKerberosTicketValidator implements KerberosTicketValidator, InitializingBean { + + private final SunJaasKerberosTicketValidator kerberosTicketValidator; + + /** + * Creates a new AmbariKerberosTicketValidator + * + * @param configuration the Ambari server configuration + */ + public AmbariKerberosTicketValidator(Configuration configuration) { + + AmbariKerberosAuthenticationProperties properties = (configuration == null) + ? null + : configuration.getKerberosAuthenticationProperties(); + + if ((properties != null) && properties.isKerberosAuthenticationEnabled()) { + kerberosTicketValidator = new SunJaasKerberosTicketValidator(); + kerberosTicketValidator.setServicePrincipal(properties.getSpnegoPrincipalName()); + + if (properties.getSpnegoKeytabFilePath() != null) { + kerberosTicketValidator.setKeyTabLocation(new FileSystemResource(properties.getSpnegoKeytabFilePath())); + } + } else { + // Don't create the SunJaasKerberosTicketValidator if Kerberos authentication is not enabled. + kerberosTicketValidator = null; + } + } + + @Override + public void afterPropertiesSet() throws Exception { + // If Kerberos authentication is enabled, forward this method invocation to the backing + // SunJaasKerberosTicketValidator instance. + if (kerberosTicketValidator != null) { + kerberosTicketValidator.afterPropertiesSet(); + } + } + + @Override + public KerberosTicketValidation validateTicket(byte[] bytes) throws BadCredentialsException { + // If Kerberos authentication is enabled, forward this method invocation to the backing + // SunJaasKerberosTicketValidator instance. + return (kerberosTicketValidator == null) + ? null + : kerberosTicketValidator.validateTicket(bytes); + } + + public void setDebug(boolean debug) { + // If Kerberos authentication is enabled, forward this method invocation to the backing + // SunJaasKerberosTicketValidator instance. + if (kerberosTicketValidator != null) { + kerberosTicketValidator.setDebug(debug); + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java index b6b0713..c283a65 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java @@ -156,7 +156,15 @@ public abstract class AbstractPrepareKerberosServerAction extends KerberosServer if (ambariServerIdentity != null) { List<KerberosIdentityDescriptor> componentIdentities = Collections.singletonList(ambariServerIdentity); kerberosHelper.addIdentities(kerberosIdentityDataFileWriter, componentIdentities, - identityFilter, KerberosHelper.AMBARI_SERVER_HOST_NAME, "AMBARI_SEVER", "AMBARI_SEVER", kerberosConfigurations, configurations); + identityFilter, KerberosHelper.AMBARI_SERVER_HOST_NAME, "AMBARI_SERVER", "AMBARI_SERVER", kerberosConfigurations, configurations); + propertiesToIgnore = gatherPropertiesToIgnore(componentIdentities, propertiesToIgnore); + } + + KerberosIdentityDescriptor spnegoIdentity = kerberosDescriptor.getIdentity(KerberosHelper.SPNEGO_IDENTITY_NAME); + if (spnegoIdentity != null) { + List<KerberosIdentityDescriptor> componentIdentities = Collections.singletonList(spnegoIdentity); + kerberosHelper.addIdentities(kerberosIdentityDataFileWriter, componentIdentities, + identityFilter, KerberosHelper.AMBARI_SERVER_HOST_NAME, "AMBARI_SERVER", "SPNEGO", kerberosConfigurations, configurations); propertiesToIgnore = gatherPropertiesToIgnore(componentIdentities, propertiesToIgnore); } } http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerAction.java new file mode 100644 index 0000000..9c2c622 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerAction.java @@ -0,0 +1,235 @@ +/* + * 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.serveraction.kerberos; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + +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.controller.KerberosHelper; +import org.apache.ambari.server.controller.utilities.KerberosChecker; +import org.apache.ambari.server.orm.dao.HostDAO; +import org.apache.ambari.server.orm.dao.KerberosPrincipalHostDAO; +import org.apache.ambari.server.orm.entities.HostEntity; +import org.apache.ambari.server.serveraction.ActionLog; +import org.apache.ambari.server.utils.ShellCommandUtil; +import org.apache.ambari.server.utils.StageUtils; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ConfigureAmbariIdentitiesServerAction is a ServerAction implementation that creates keytab files as + * instructed. + * <p/> + * This class mainly relies on the KerberosServerAction to iterate through metadata identifying + * the Kerberos keytab files that need to be created. For each identity in the metadata, this + * implementation's + * {@link KerberosServerAction#processIdentity(Map, String, KerberosOperationHandler, Map, Map)} + * is invoked attempting the creation of the relevant keytab file. + */ +public class ConfigureAmbariIdentitiesServerAction extends KerberosServerAction { + + + private static final String KEYTAB_PATTERN = "keyTab=\"(.+)?\""; + private static final String PRINCIPAL_PATTERN = "principal=\"(.+)?\""; + + private final static Logger LOG = LoggerFactory.getLogger(ConfigureAmbariIdentitiesServerAction.class); + + @Inject + private KerberosPrincipalHostDAO kerberosPrincipalHostDAO; + + @Inject + private HostDAO hostDAO; + + /** + * Called to execute this action. Upon invocation, calls + * {@link KerberosServerAction#processIdentities(Map)} )} + * to iterate through the Kerberos identity metadata and call + * {@link ConfigureAmbariIdentitiesServerAction#processIdentities(Map)} + * for each identity to process. + * + * @param requestSharedDataContext a Map to be used a shared data among all ServerActions related + * to a given request + * @return a CommandReport indicating the result of this action + * @throws AmbariException + * @throws InterruptedException + */ + @Override + public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) throws + AmbariException, InterruptedException { + return processIdentities(requestSharedDataContext); + } + + + /** + * Creates keytab file for ambari-server identity. + * <p/> + * It is expected that the {@link CreatePrincipalsServerAction} + * (or similar) and {@link CreateKeytabFilesServerAction} has executed before this action. + * + * @param identityRecord a Map containing the data for the current identity record + * @param evaluatedPrincipal a String indicating the relevant principal + * @param operationHandler a KerberosOperationHandler used to perform Kerberos-related + * tasks for specific Kerberos implementations + * (MIT, Active Directory, etc...) + * @param kerberosConfiguration a Map of configuration properties from kerberos-env + * @param requestSharedDataContext a Map to be used a shared data among all ServerActions related + * to a given request @return a CommandReport, indicating an error + * condition; or null, indicating a success condition + * @throws AmbariException if an error occurs while processing the identity record + */ + @Override + protected CommandReport processIdentity(Map<String, String> identityRecord, String evaluatedPrincipal, + KerberosOperationHandler operationHandler, + Map<String, String> kerberosConfiguration, + Map<String, Object> requestSharedDataContext) + throws AmbariException { + CommandReport commandReport = null; + + if (identityRecord != null) { + String message; + String dataDirectory = getDataDirectoryPath(); + + 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 { + + String hostName = identityRecord.get(KerberosIdentityDataFileReader.HOSTNAME); + if (hostName != null && hostName.equalsIgnoreCase(KerberosHelper.AMBARI_SERVER_HOST_NAME)) { + String destKeytabFilePath = identityRecord.get(KerberosIdentityDataFileReader.KEYTAB_FILE_PATH); + File hostDirectory = new File(dataDirectory, hostName); + File srcKeytabFile = new File(hostDirectory, DigestUtils.sha1Hex(destKeytabFilePath)); + + if (srcKeytabFile.exists()) { + installAmbariServerIdentity(evaluatedPrincipal, srcKeytabFile.getAbsolutePath(), destKeytabFilePath, actionLog); + + if ("AMBARI_SERVER".equals(identityRecord.get(KerberosIdentityDataFileReader.COMPONENT))) { + // Create/update the JAASFile... + configureJAAS(evaluatedPrincipal, destKeytabFilePath, actionLog); + } + } + } + } + } + + return commandReport; + } + + /** + * Installs the Ambari Server Kerberos identity by copying its keytab file to the specified location + * and then creating the Ambari Server JAAS File. + * + * @param principal the ambari server principal name + * @param srcKeytabFilePath the source location of the ambari server keytab file + * @param destKeytabFilePath the destination location of the ambari server keytab file + * @param actionLog the logger + * @return true if success; false otherwise + * @throws AmbariException + */ + public boolean installAmbariServerIdentity(String principal, + String srcKeytabFilePath, + String destKeytabFilePath, + ActionLog actionLog) throws AmbariException { + + // Use sudo to copy the file into place.... + try { + ShellCommandUtil.Result result; + + // Ensure the parent directory exists... + File destKeytabFile = new File(destKeytabFilePath); + result = ShellCommandUtil.mkdir(destKeytabFile.getParent(), true); + if (!result.isSuccessful()) { + throw new AmbariException(result.getStderr()); + } + + // Copy the keytab file into place... + result = ShellCommandUtil.copyFile(srcKeytabFilePath, destKeytabFilePath, true, true); + if (!result.isSuccessful()) { + throw new AmbariException(result.getStderr()); + } else { + String ambariServerHostName = StageUtils.getHostName(); + HostEntity ambariServerHostEntity = hostDAO.findByName(ambariServerHostName); + Long ambariServerHostID = (ambariServerHostEntity == null) + ? null + : ambariServerHostEntity.getHostId(); + + if (ambariServerHostID == null) { + String message = String.format("Failed to add the kerberos_principal_host record for %s on " + + "the Ambari server host since the host id for Ambari server host, %s, was not found." + + " This is not an error if an Ambari agent is not installed on the Ambari server host.", + principal, ambariServerHostName); + LOG.warn(message); + actionLog.writeStdErr(message); + } else if (!kerberosPrincipalHostDAO.exists(principal, ambariServerHostID)) { + kerberosPrincipalHostDAO.create(principal, ambariServerHostID); + } + + actionLog.writeStdOut(String.format("Created Ambari server keytab file for %s at %s", principal, destKeytabFile)); + } + } catch (InterruptedException | IOException e) { + throw new AmbariException(e.getLocalizedMessage(), e); + } + + return true; + } + + private void configureJAAS(String evaluatedPrincipal, String keytabFilePath, ActionLog actionLog) { + String jaasConfPath = System.getProperty(KerberosChecker.JAVA_SECURITY_AUTH_LOGIN_CONFIG); + if (jaasConfPath != null) { + File jaasConfigFile = new File(jaasConfPath); + try { + String jaasConfig = FileUtils.readFileToString(jaasConfigFile); + File oldJaasConfigFile = new File(jaasConfPath + ".bak"); + FileUtils.writeStringToFile(oldJaasConfigFile, jaasConfig); + jaasConfig = jaasConfig.replaceFirst(KEYTAB_PATTERN, "keyTab=\"" + keytabFilePath + "\""); + jaasConfig = jaasConfig.replaceFirst(PRINCIPAL_PATTERN, "principal=\"" + evaluatedPrincipal + "\""); + FileUtils.writeStringToFile(jaasConfigFile, jaasConfig); + String message = String.format("JAAS config file %s modified successfully for principal %s.", jaasConfigFile + .getName(), evaluatedPrincipal); + if (actionLog != null) { + actionLog.writeStdOut(message); + } + } catch (IOException e) { + String message = String.format("Failed to configure JAAS file %s for %s - %s", jaasConfigFile, + evaluatedPrincipal, e.getMessage()); + if (actionLog != null) { + actionLog.writeStdErr(message); + } + LOG.error(message, e); + } + } else { + String message = String.format("Failed to configure JAAS, config file should be passed to Ambari server as: " + + "%s.", KerberosChecker.JAVA_SECURITY_AUTH_LOGIN_CONFIG); + if (actionLog != null) { + actionLog.writeStdErr(message); + } + LOG.error(message); + } + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIndetityServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIndetityServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIndetityServerAction.java deleted file mode 100644 index 96540ef..0000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIndetityServerAction.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * 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.serveraction.kerberos; - -import java.io.File; -import java.io.IOException; -import java.util.Map; -import java.util.concurrent.ConcurrentMap; - -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.controller.KerberosHelper; -import org.apache.ambari.server.controller.utilities.KerberosChecker; -import org.apache.ambari.server.serveraction.ActionLog; -import org.apache.ambari.server.utils.ShellCommandUtil; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.io.FileUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * ConfigureAmbariIndetityServerAction is a ServerAction implementation that creates keytab files as - * instructed. - * <p/> - * This class mainly relies on the KerberosServerAction to iterate through metadata identifying - * the Kerberos keytab files that need to be created. For each identity in the metadata, this - * implementation's - * {@link KerberosServerAction#processIdentity(Map, String, KerberosOperationHandler, Map, Map)} - * is invoked attempting the creation of the relevant keytab file. - */ -public class ConfigureAmbariIndetityServerAction extends KerberosServerAction { - - - private static final String KEYTAB_PATTERN = "keyTab=\"(.+)?\""; - private static final String PRINCIPAL_PATTERN = "principal=\"(.+)?\""; - - private final static Logger LOG = LoggerFactory.getLogger(ConfigureAmbariIndetityServerAction.class); - - /** - * Called to execute this action. Upon invocation, calls - * {@link KerberosServerAction#processIdentities(Map)} )} - * to iterate through the Kerberos identity metadata and call - * {@link ConfigureAmbariIndetityServerAction#processIdentities(Map)} - * for each identity to process. - * - * @param requestSharedDataContext a Map to be used a shared data among all ServerActions related - * to a given request - * @return a CommandReport indicating the result of this action - * @throws AmbariException - * @throws InterruptedException - */ - @Override - public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) throws - AmbariException, InterruptedException { - return processIdentities(requestSharedDataContext); - } - - - /** - * Creates keytab file for ambari-server identity. - * <p/> - * It is expected that the {@link CreatePrincipalsServerAction} - * (or similar) and {@link CreateKeytabFilesServerAction} has executed before this action. - * - * @param identityRecord a Map containing the data for the current identity record - * @param evaluatedPrincipal a String indicating the relevant principal - * @param operationHandler a KerberosOperationHandler used to perform Kerberos-related - * tasks for specific Kerberos implementations - * (MIT, Active Directory, etc...) - * @param kerberosConfiguration a Map of configuration properties from kerberos-env - * @param requestSharedDataContext a Map to be used a shared data among all ServerActions related - * to a given request @return a CommandReport, indicating an error - * condition; or null, indicating a success condition - * @throws AmbariException if an error occurs while processing the identity record - */ - @Override - protected CommandReport processIdentity(Map<String, String> identityRecord, String evaluatedPrincipal, - KerberosOperationHandler operationHandler, - Map<String, String> kerberosConfiguration, - Map<String, Object> requestSharedDataContext) - throws AmbariException { - CommandReport commandReport = null; - - 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 { - - String hostName = identityRecord.get(KerberosIdentityDataFileReader.HOSTNAME); - if (hostName != null && hostName.equalsIgnoreCase(KerberosHelper.AMBARI_SERVER_HOST_NAME)) { - String destKeytabFilePath = identityRecord.get(KerberosIdentityDataFileReader.KEYTAB_FILE_PATH); - File hostDirectory = new File(dataDirectory, hostName); - File srcKeytabFile = new File(hostDirectory, DigestUtils.sha1Hex(destKeytabFilePath)); - - if(srcKeytabFile.exists()) { - installAmbariServerIdentity(evaluatedPrincipal, srcKeytabFile.getAbsolutePath(), destKeytabFilePath, actionLog); - } - } - } - } - - return commandReport; - } - - /** - * Installs the Ambari Server Kerberos identity by copying its keytab file to the specified location - * and then creating the Ambari Server JAAS File. - * - * @param principal the ambari server principal name - * @param srcKeytabFilePath the source location of the ambari server keytab file - * @param destKeytabFilePath the destination location of the ambari server keytab file - * @param actionLog the logger - * @return true if success; false otherwise - * @throws AmbariException - */ - public boolean installAmbariServerIdentity(String principal, - String srcKeytabFilePath, - String destKeytabFilePath, - ActionLog actionLog) throws AmbariException { - - // Use sudo to copy the file into place.... - try { - ShellCommandUtil.Result result; - - // Ensure the parent directory exists... - File destKeytabFile = new File(destKeytabFilePath); - result = ShellCommandUtil.mkdir(destKeytabFile.getParent(), true); - if (!result.isSuccessful()) { - throw new AmbariException(result.getStderr()); - } - - // Copy the keytab file into place... - result = ShellCommandUtil.copyFile(srcKeytabFilePath, destKeytabFilePath, true, true); - if (!result.isSuccessful()) { - throw new AmbariException(result.getStderr()); - } - } catch (InterruptedException | IOException e) { - throw new AmbariException(e.getLocalizedMessage(), e); - } - - // Create/update the JAASFile... - configureJAAS(principal, destKeytabFilePath, actionLog); - - return true; - } - - private void configureJAAS(String evaluatedPrincipal, String keytabFilePath, ActionLog actionLog) { - String jaasConfPath = System.getProperty(KerberosChecker.JAVA_SECURITY_AUTH_LOGIN_CONFIG); - if (jaasConfPath != null) { - File jaasConfigFile = new File(jaasConfPath); - try { - String jaasConfig = FileUtils.readFileToString(jaasConfigFile); - File oldJaasConfigFile = new File(jaasConfPath + ".bak"); - FileUtils.writeStringToFile(oldJaasConfigFile, jaasConfig); - jaasConfig = jaasConfig.replaceFirst(KEYTAB_PATTERN, "keyTab=\"" + keytabFilePath + "\""); - jaasConfig = jaasConfig.replaceFirst(PRINCIPAL_PATTERN, "principal=\"" + evaluatedPrincipal + "\""); - FileUtils.writeStringToFile(jaasConfigFile, jaasConfig); - String message = String.format("JAAS config file %s modified successfully for principal %s.", jaasConfigFile - .getName(), evaluatedPrincipal); - if (actionLog != null) { - actionLog.writeStdOut(message); - } - } catch (IOException e) { - String message = String.format("Failed to configure JAAS file %s for %s - %s", jaasConfigFile, - evaluatedPrincipal, e.getMessage()); - if (actionLog != null) { - actionLog.writeStdErr(message); - } - LOG.error(message, e); - } - } else { - String message = String.format("Failed to configure JAAS, config file should be passed to Ambari server as: " + - "%s.", KerberosChecker.JAVA_SECURITY_AUTH_LOGIN_CONFIG); - if (actionLog != null) { - actionLog.writeStdErr(message); - } - LOG.error(message); - } - } - -} http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/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 e31e6ff..b99c25a 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 @@ -18,7 +18,6 @@ package org.apache.ambari.server.serveraction.kerberos; -import com.google.common.base.Optional; import com.google.inject.Inject; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.actionmanager.HostRoleStatus; @@ -145,7 +144,7 @@ public class CreatePrincipalsServerAction extends KerberosServerAction { // This principal has been processed and a keytab file has been distributed... do not process it. processPrincipal = false; } else { - // This principal has been processed but a keytab file for it has been distributed... process it. + // This principal has been processed but a keytab file for it has not been distributed... process it. processPrincipal = true; } } @@ -232,7 +231,7 @@ public class CreatePrincipalsServerAction extends KerberosServerAction { String password = securePasswordHelper.createSecurePassword(length, minLowercaseLetters, minUppercaseLetters, minDigits, minPunctuation, minWhitespace); try { - /** + /* * true indicates a new principal was created, false indicates an existing principal was updated */ boolean created; http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java index db210e0..1d8c1ca 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java @@ -28,6 +28,7 @@ import org.apache.ambari.server.security.credential.PrincipalKeyCredential; import org.apache.ambari.server.serveraction.AbstractServerAction; import org.apache.ambari.server.state.Cluster; import org.apache.ambari.server.state.Clusters; +import org.apache.ambari.server.utils.StageUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -514,12 +515,19 @@ public abstract class KerberosServerAction extends AbstractServerAction { if (record != null) { String principal = record.get(KerberosIdentityDataFileReader.PRINCIPAL); - String host = record.get(KerberosIdentityDataFileReader.HOSTNAME); if (principal != null) { + String hostname = record.get(KerberosIdentityDataFileReader.HOSTNAME); + + if(KerberosHelper.AMBARI_SERVER_HOST_NAME.equals(hostname)) { + // Replace KerberosHelper.AMBARI_SERVER_HOST_NAME with the actual hostname where the Ambari + // server is... this host + hostname = StageUtils.getHostName(); + } + // Evaluate the principal "pattern" found in the record to generate the "evaluated principal" // by replacing the _HOST and _REALM variables. - String evaluatedPrincipal = principal.replace("_HOST", host).replace("_REALM", defaultRealm); + String evaluatedPrincipal = principal.replace("_HOST", hostname).replace("_REALM", defaultRealm); commandReport = processIdentity(record, evaluatedPrincipal, operationHandler, kerberosConfiguration, requestSharedDataContext); } http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/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 a86973c..500c0bf 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 @@ -30,16 +30,11 @@ <custom-filter ref="ambariAuthorizationFilter" before="FILTER_SECURITY_INTERCEPTOR"/> </http> - <!--<ldap-server id="ldapServer" root="dc=ambari,dc=apache,dc=org"/>--> - <authentication-manager alias="authenticationManager"> - <authentication-provider ref="ambariLocalAuthenticationProvider"/> - <authentication-provider ref="ambariLdapAuthenticationProvider"/> - <authentication-provider ref="ambariInternalAuthenticationProvider"/> - + <authentication-provider ref="kerberosServiceAuthenticationProvider"/> </authentication-manager> <beans:bean id="ambariEntryPoint" class="org.apache.ambari.server.security.AmbariEntryPoint"> @@ -49,6 +44,7 @@ <beans:constructor-arg> <beans:list> <beans:ref bean="ambariBasicAuthenticationFilter"/> + <beans:ref bean="ambariKerberosAuthenticationFilter"/> <beans:ref bean="ambariJwtAuthenticationFilter"/> </beans:list> </beans:constructor-arg> @@ -69,6 +65,14 @@ <beans:constructor-arg ref="permissionHelper"/> </beans:bean> + <beans:bean id="ambariKerberosAuthenticationFilter" class="org.apache.ambari.server.security.authentication.kerberos.AmbariKerberosAuthenticationFilter"> + <beans:constructor-arg ref="authenticationManager"/> + <beans:constructor-arg ref="ambariEntryPoint"/> + <beans:constructor-arg ref="ambariConfiguration"/> + <beans:constructor-arg ref="auditLogger"/> + <beans:constructor-arg ref="permissionHelper"/> + </beans:bean> + <beans:bean id="ambariAuthorizationFilter" class="org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter"> <beans:constructor-arg ref="ambariEntryPoint"/> <beans:constructor-arg ref="ambariConfiguration"/> @@ -77,4 +81,20 @@ <beans:constructor-arg ref="permissionHelper"/> </beans:bean> + <beans:bean id="kerberosServiceAuthenticationProvider" class="org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider"> + <beans:property name="ticketValidator"> + <beans:bean class="org.apache.ambari.server.security.authentication.kerberos.AmbariKerberosTicketValidator"> + <beans:constructor-arg ref="ambariConfiguration"/> + <beans:property name="debug" value="false"/> + </beans:bean> + </beans:property> + + <beans:property name="userDetailsService" ref="authToLocalUserDetailsService"/> + </beans:bean> + + <beans:bean id="authToLocalUserDetailsService" class="org.apache.ambari.server.security.authentication.kerberos.AmbariAuthToLocalUserDetailsService"> + <beans:constructor-arg ref="ambariConfiguration"/> + <beans:constructor-arg ref="ambariUsers"/> + </beans:bean> + </beans:beans> http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java index 3c97ce9..7e6a056 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java @@ -104,7 +104,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -3755,6 +3754,7 @@ public class KerberosHelperTest extends EasyMockSupport { KerberosDescriptor kerberosDescriptor = createMock(KerberosDescriptor.class); if (createAmbariPrincipal) { expect(kerberosDescriptor.getIdentity(KerberosHelper.AMBARI_IDENTITY_NAME)).andReturn(ambariKerberosIdentity).once(); + expect(kerberosDescriptor.getIdentity(KerberosHelper.SPNEGO_IDENTITY_NAME)).andReturn(ambariKerberosIdentity).once(); } List<KerberosIdentityDescriptor> identities = new ArrayList<KerberosIdentityDescriptor>(); @@ -3764,12 +3764,12 @@ public class KerberosHelperTest extends EasyMockSupport { // Needed by infrastructure injector.getInstance(AmbariMetaInfo.class).init(); - kerberosHelper.addAmbariServerIdentity(kerberosEnvProperties, kerberosDescriptor, identities); + kerberosHelper.addAmbariServerIdentities(kerberosEnvProperties, kerberosDescriptor, identities); verifyAll(); if (createAmbariPrincipal) { - Assert.assertEquals(1, identities.size()); + Assert.assertEquals(2, identities.size()); Assert.assertSame(ambariKerberosIdentity, identities.get(0)); } else { Assert.assertTrue(identities.isEmpty()); http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariAuthToLocalUserDetailsServiceTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariAuthToLocalUserDetailsServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariAuthToLocalUserDetailsServiceTest.java new file mode 100644 index 0000000..e980808 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariAuthToLocalUserDetailsServiceTest.java @@ -0,0 +1,92 @@ +/* + * 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.kerberos; + +import junit.framework.Assert; +import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.security.authorization.AmbariGrantedAuthority; +import org.apache.ambari.server.security.authorization.User; +import org.apache.ambari.server.security.authorization.UserType; +import org.apache.ambari.server.security.authorization.Users; +import org.easymock.EasyMockSupport; +import org.junit.Test; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import java.util.Collection; +import java.util.Collections; + +import static org.easymock.EasyMock.expect; + +public class AmbariAuthToLocalUserDetailsServiceTest extends EasyMockSupport { + @Test + public void loadUserByUsernameSuccess() throws Exception { + AmbariKerberosAuthenticationProperties properties = new AmbariKerberosAuthenticationProperties(); + + Configuration configuration = createMock(Configuration.class); + expect(configuration.getKerberosAuthenticationProperties()).andReturn(properties).once(); + + User user = createMock(User.class); + expect(user.getUserName()).andReturn("user1").once(); + expect(user.getUserType()).andReturn(UserType.LDAP).once(); + + Collection<AmbariGrantedAuthority> userAuthorities = Collections.singletonList(createNiceMock(AmbariGrantedAuthority.class)); + + Users users = createMock(Users.class); + expect(users.getUser("user1", UserType.LDAP)).andReturn(user).once(); + expect(users.getUserAuthorities("user1", UserType.LDAP)).andReturn(userAuthorities).once(); + + replayAll(); + + UserDetailsService userdetailsService = new AmbariAuthToLocalUserDetailsService(configuration, users); + + UserDetails userDetails = userdetailsService.loadUserByUsername("us...@example.com"); + + verifyAll(); + + Assert.assertNotNull(userDetails); + Assert.assertEquals("user1", userDetails.getUsername()); + Assert.assertEquals(userAuthorities.size(), userDetails.getAuthorities().size()); + Assert.assertEquals("", userDetails.getPassword()); + } + + @Test(expected = UsernameNotFoundException.class) + public void loadUserByUsernameUserNotFound() throws Exception { + AmbariKerberosAuthenticationProperties properties = new AmbariKerberosAuthenticationProperties(); + + Configuration configuration = createMock(Configuration.class); + expect(configuration.getKerberosAuthenticationProperties()).andReturn(properties).once(); + + Users users = createMock(Users.class); + expect(users.getUser("user1", UserType.LDAP)).andReturn(null).once(); + expect(users.getUser("user1", UserType.LOCAL)).andReturn(null).once(); + + replayAll(); + + UserDetailsService userdetailsService = new AmbariAuthToLocalUserDetailsService(configuration, users); + + userdetailsService.loadUserByUsername("us...@example.com"); + + verifyAll(); + + Assert.fail("UsernameNotFoundException was not thrown"); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilterTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilterTest.java new file mode 100644 index 0000000..d855cda --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosAuthenticationFilterTest.java @@ -0,0 +1,133 @@ +/* + * 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.kerberos; + +import org.apache.ambari.server.audit.AuditLogger; +import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.security.authorization.PermissionHelper; +import org.easymock.EasyMockSupport; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.web.AuthenticationEntryPoint; + +import javax.servlet.http.HttpServletRequest; + +import static org.easymock.EasyMock.expect; + +public class AmbariKerberosAuthenticationFilterTest extends EasyMockSupport { + @Test + public void shouldApplyTrue() throws Exception { + HttpServletRequest httpServletRequest = createMock(HttpServletRequest.class); + expect(httpServletRequest.getHeader("Authorization")).andReturn("Negotiate .....").once(); + + AmbariKerberosAuthenticationProperties properties = createMock(AmbariKerberosAuthenticationProperties.class); + expect(properties.isKerberosAuthenticationEnabled()).andReturn(true).once(); + + Configuration configuration = createMock(Configuration.class); + expect(configuration.getKerberosAuthenticationProperties()).andReturn(properties).once(); + + AuthenticationManager authenticationManager = createMock(AuthenticationManager.class); + AuthenticationEntryPoint entryPoint = createMock(AuthenticationEntryPoint.class); + AuditLogger auditLogger = createMock(AuditLogger.class); + PermissionHelper permissionHelper = createMock(PermissionHelper.class); + + replayAll(); + + AmbariKerberosAuthenticationFilter filter = new AmbariKerberosAuthenticationFilter( + authenticationManager, + entryPoint, + configuration, + auditLogger, + permissionHelper + ); + + Assert.assertTrue(filter.shouldApply(httpServletRequest)); + + verifyAll(); + } + + @Test + public void shouldApplyFalseMissingHeader() throws Exception { + HttpServletRequest httpServletRequest = createMock(HttpServletRequest.class); + expect(httpServletRequest.getHeader("Authorization")).andReturn(null).once(); + + AmbariKerberosAuthenticationProperties properties = createMock(AmbariKerberosAuthenticationProperties.class); + expect(properties.isKerberosAuthenticationEnabled()).andReturn(true).once(); + + Configuration configuration = createMock(Configuration.class); + expect(configuration.getKerberosAuthenticationProperties()).andReturn(properties).once(); + + AuthenticationManager authenticationManager = createMock(AuthenticationManager.class); + AuthenticationEntryPoint entryPoint = createMock(AuthenticationEntryPoint.class); + AuditLogger auditLogger = createMock(AuditLogger.class); + PermissionHelper permissionHelper = createMock(PermissionHelper.class); + + replayAll(); + + AmbariKerberosAuthenticationFilter filter = new AmbariKerberosAuthenticationFilter( + authenticationManager, + entryPoint, + configuration, + auditLogger, + permissionHelper + ); + + Assert.assertFalse(filter.shouldApply(httpServletRequest)); + + verifyAll(); + } + + @Test + public void shouldApplyNotFalseEnabled() throws Exception { + HttpServletRequest httpServletRequest = createMock(HttpServletRequest.class); + + AmbariKerberosAuthenticationProperties properties = createMock(AmbariKerberosAuthenticationProperties.class); + expect(properties.isKerberosAuthenticationEnabled()).andReturn(false).once(); + + Configuration configuration = createMock(Configuration.class); + expect(configuration.getKerberosAuthenticationProperties()).andReturn(properties).once(); + + AuthenticationManager authenticationManager = createMock(AuthenticationManager.class); + AuthenticationEntryPoint entryPoint = createMock(AuthenticationEntryPoint.class); + AuditLogger auditLogger = createMock(AuditLogger.class); + PermissionHelper permissionHelper = createMock(PermissionHelper.class); + + replayAll(); + + AmbariKerberosAuthenticationFilter filter = new AmbariKerberosAuthenticationFilter( + authenticationManager, + entryPoint, + configuration, + auditLogger, + permissionHelper + ); + + Assert.assertFalse(filter.shouldApply(httpServletRequest)); + + verifyAll(); + } + + @Test + public void doFilter() throws Exception { + // Skip this test since the real work is being done by SpnegoAuthenticationProcessingFilter, which + // is a class in the Spring libraries. + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/b4320b5a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosTicketValidatorTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosTicketValidatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosTicketValidatorTest.java new file mode 100644 index 0000000..9bc87a4 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/kerberos/AmbariKerberosTicketValidatorTest.java @@ -0,0 +1,49 @@ +/* + * 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.kerberos; + +import org.apache.ambari.server.configuration.Configuration; +import org.easymock.EasyMockSupport; +import org.junit.Test; + +import static org.easymock.EasyMock.expect; + +public class AmbariKerberosTicketValidatorTest extends EasyMockSupport { + + /** + * Tests an {@link AmbariKerberosTicketValidator} to ensure that the Spnego identity is properly + * set in the base class during construction. + */ + @Test + public void testConstructor() throws NoSuchMethodException { + AmbariKerberosAuthenticationProperties properties = createMock(AmbariKerberosAuthenticationProperties.class); + expect(properties.isKerberosAuthenticationEnabled()).andReturn(true).once(); + expect(properties.getSpnegoPrincipalName()).andReturn("HTTP/somehost.example.com").times(1); + expect(properties.getSpnegoKeytabFilePath()).andReturn("/etc/security/keytabs/spnego.service.keytab").times(2); + + Configuration configuration = createMock(Configuration.class); + expect(configuration.getKerberosAuthenticationProperties()).andReturn(properties).once(); + + replayAll(); + + new AmbariKerberosTicketValidator(configuration); + + verifyAll(); + } +} \ No newline at end of file