AMBARI-21217. Update JWT Authentication process to work with improved user management facility (rlevas)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/553e4f9d Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/553e4f9d Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/553e4f9d Branch: refs/heads/branch-feature-AMBARI-20859 Commit: 553e4f9d25573c54860d08b0494caefd77398977 Parents: 7bb4de1 Author: Robert Levas <[email protected]> Authored: Tue Oct 17 11:56:42 2017 -0400 Committer: Robert Levas <[email protected]> Committed: Tue Oct 17 11:58:06 2017 -0400 ---------------------------------------------------------------------- .../ambari/server/api/AmbariErrorHandler.java | 2 +- .../server/configuration/Configuration.java | 4 +- .../ambari/server/controller/AmbariServer.java | 3 + .../AmbariAuthenticationEventHandlerImpl.java | 20 +- .../AmbariAuthenticationFilter.java | 10 + .../AmbariAuthenticationProvider.java | 21 +- .../AmbariBasicAuthenticationFilter.java | 5 + .../AmbariJWTAuthenticationFilter.java | 114 ----- .../AmbariLocalAuthenticationProvider.java | 28 +- .../AmbariUserAuthentication.java | 79 +++ .../authentication/UserNotFoundException.java | 9 + .../jwt/AmbariJwtAuthenticationFilter.java | 419 ++++++++++++++++ .../jwt/AmbariJwtAuthenticationProvider.java | 126 +++++ .../jwt/JwtAuthenticationProperties.java | 87 ++++ .../jwt/JwtAuthenticationToken.java | 55 +++ .../AmbariKerberosAuthenticationFilter.java | 6 + .../AmbariPamAuthenticationProvider.java | 1 + .../authorization/AmbariUserAuthentication.java | 76 --- .../AmbariUserAuthorizationFilter.java | 1 + .../authorization/jwt/JwtAuthentication.java | 34 -- .../jwt/JwtAuthenticationFilter.java | 448 ----------------- .../jwt/JwtAuthenticationProperties.java | 87 ---- .../security/ldap/AmbariLdapDataPopulator.java | 2 +- .../webapp/WEB-INF/spring-security.xml | 4 +- .../server/security/SecurityHelperImplTest.java | 2 +- .../AbstractAuthenticationProviderTest.java | 1 - .../AmbariJWTAuthenticationFilterTest.java | 225 --------- .../AmbariLocalAuthenticationProviderTest.java | 1 - .../jwt/AmbariJwtAuthenticationFilterTest.java | 492 +++++++++++++++++++ .../jwt/JwtAuthenticationPropertiesTest.java | 51 ++ .../AmbariPamAuthenticationProviderTest.java | 1 + .../authorization/AuthorizationHelperTest.java | 1 + .../jwt/JwtAuthenticationFilterTest.java | 371 -------------- .../jwt/JwtAuthenticationPropertiesTest.java | 51 -- 34 files changed, 1395 insertions(+), 1442 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/api/AmbariErrorHandler.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/AmbariErrorHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/api/AmbariErrorHandler.java index a57effc..de416d7 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/AmbariErrorHandler.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/AmbariErrorHandler.java @@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.ambari.server.configuration.Configuration; -import org.apache.ambari.server.security.authorization.jwt.JwtAuthenticationProperties; +import org.apache.ambari.server.security.authentication.jwt.JwtAuthenticationProperties; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.server.AbstractHttpConnection; http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/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 62e8b86..2b14b4d 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 @@ -63,10 +63,10 @@ import org.apache.ambari.server.orm.PersistenceType; import org.apache.ambari.server.orm.dao.HostRoleCommandStatusSummaryDTO; import org.apache.ambari.server.orm.entities.StageEntity; import org.apache.ambari.server.security.ClientSecurityType; +import org.apache.ambari.server.security.authentication.jwt.JwtAuthenticationProperties; import org.apache.ambari.server.security.authentication.kerberos.AmbariKerberosAuthenticationProperties; import org.apache.ambari.server.security.authorization.LdapServerProperties; import org.apache.ambari.server.security.authorization.UserAuthenticationType; -import org.apache.ambari.server.security.authorization.jwt.JwtAuthenticationProperties; import org.apache.ambari.server.security.encryption.CertificateUtils; import org.apache.ambari.server.security.encryption.CredentialProvider; import org.apache.ambari.server.state.services.MetricsRetrievalService; @@ -5299,7 +5299,7 @@ public class Configuration { if (enableJwt) { String providerUrl = getProperty(JWT_AUTH_PROVIDER_URL); if (providerUrl == null) { - LOG.error("JWT authentication provider URL not specified. JWT auth will be disabled.", providerUrl); + LOG.error("JWT authentication provider URL not specified. JWT auth will be disabled."); return null; } String publicKeyPath = getProperty(JWT_PUBLIC); http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java index 0d24ef2..bb8e0fe 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java @@ -101,6 +101,7 @@ import org.apache.ambari.server.security.CertificateManager; import org.apache.ambari.server.security.SecurityFilter; import org.apache.ambari.server.security.authentication.AmbariAuthenticationEventHandlerImpl; import org.apache.ambari.server.security.authentication.AmbariLocalAuthenticationProvider; +import org.apache.ambari.server.security.authentication.jwt.AmbariJwtAuthenticationProvider; import org.apache.ambari.server.security.authorization.AmbariLdapAuthenticationProvider; import org.apache.ambari.server.security.authorization.AmbariPamAuthenticationProvider; import org.apache.ambari.server.security.authorization.AmbariUserAuthorizationFilter; @@ -349,6 +350,8 @@ public class AmbariServer { injector.getInstance(AmbariInternalAuthenticationProvider.class)); factory.registerSingleton("ambariPamAuthenticationProvider", injector.getInstance(AmbariPamAuthenticationProvider.class)); + factory.registerSingleton("ambariJwtAuthenticationProvider", + injector.getInstance(AmbariJwtAuthenticationProvider.class)); // Spring Security xml config depends on this Bean String[] contextLocations = {SPRING_CONTEXT_LOCATION}; http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java index 4cfce2a..e651d22 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java @@ -107,15 +107,21 @@ public class AmbariAuthenticationEventHandlerImpl implements AmbariAuthenticatio } if (!StringUtils.isEmpty(username)) { - // Increment the user's consecutive authentication failure count. - consecutiveFailures = users.incrementConsecutiveAuthenticationFailures(username); - - // If consecutiveFailures is NULL, then no user entry was found for the specified username. - if(consecutiveFailures == null) { - logMessage = String.format("Failed to authenticate %s: The user does not exist in the Ambari database", username); + // Only increment the authentication failure count if the authentication filter declares to + // do so. + if(filter.shouldIncrementFailureCount()) { + // Increment the user's consecutive authentication failure count. + consecutiveFailures = users.incrementConsecutiveAuthenticationFailures(username); + + // If consecutiveFailures is NULL, then no user entry was found for the specified username. + if (consecutiveFailures == null) { + logMessage = String.format("Failed to authenticate %s: The user does not exist in the Ambari database", username); + } else { + logMessage = String.format("Failed to authenticate %s (attempt #%d): %s", username, consecutiveFailures, message); + } } else { - logMessage = String.format("Failed to authenticate %s (attempt #%d): %s", username, consecutiveFailures, message); + logMessage = String.format("Failed to authenticate %s: %s", username, message); } } else { logMessage = String.format("Failed to authenticate an unknown user: %s", message); http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/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 index b3bc4c3..f5d5617 100644 --- 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 @@ -46,4 +46,14 @@ public interface AmbariAuthenticationFilter extends Filter { * @return true if this AmbariAuthenticationFilter should be applied to the filter chain; otherwise false. */ boolean shouldApply(HttpServletRequest httpServletRequest); + + /** + * Tests this AmbariAuthenticationFilter to see if authentication failures should count towards + * the consecutive authentication failure count. + * <p> + * This should typically be false for remote authentication sources such as LDAP or JWT. + * + * @return true if authentication failure should be counted; false, otherwise + */ + boolean shouldIncrementFailureCount(); } http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationProvider.java index 3d20cb9..d3d5b88 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationProvider.java @@ -34,13 +34,13 @@ import org.springframework.security.authentication.AuthenticationProvider; * <p> * This class contains common methods that may be used by authentication providers. */ -abstract class AmbariAuthenticationProvider implements AuthenticationProvider { +public abstract class AmbariAuthenticationProvider implements AuthenticationProvider { private static final Logger LOG = LoggerFactory.getLogger(AmbariAuthenticationProvider.class); private Users users; private Configuration configuration; - AmbariAuthenticationProvider(Users users, Configuration configuration) { + protected AmbariAuthenticationProvider(Users users, Configuration configuration) { this.users = users; this.configuration = configuration; } @@ -49,24 +49,31 @@ abstract class AmbariAuthenticationProvider implements AuthenticationProvider { * Gets the {@link UserEntity} for the user with the specified username. * <p> * The entity is validated such that the account is allowed to log in before returning. For example, - * if the account is not acitve, no user may not login as that account. + * if the account is not active, no user may not login as that account. * * @param userName * @return */ - UserEntity getUserEntity(String userName) { + protected UserEntity getUserEntity(String userName) { LOG.debug("Loading user by name: {}", userName); UserEntity userEntity = users.getUserEntity(userName); if (userEntity == null) { LOG.info("User not found: {}", userName); - throw new InvalidUsernamePasswordCombinationException(userName); + throw new UserNotFoundException(userName); } if (!userEntity.getActive()) { LOG.info("User account is disabled: {}", userName); + throw new AccountDisabledException(userName); + } + + int maxConsecutiveFailures = configuration.getMaxAuthenticationFailures(); + if (maxConsecutiveFailures > 0 && userEntity.getConsecutiveFailures() >= maxConsecutiveFailures) { + LOG.info("User account is locked out due to too many authentication failures ({}/{}): {}", + userEntity.getConsecutiveFailures(), maxConsecutiveFailures, userName); if (configuration.showLockedOutUserMessage()) { - throw new AccountDisabledException(userName); + throw new TooManyLoginFailuresException(userName); } else { throw new InvalidUsernamePasswordCombinationException(userName); } @@ -83,7 +90,7 @@ abstract class AmbariAuthenticationProvider implements AuthenticationProvider { * @param type the {@link UserAuthenticationType} to retrieve * @return a {@link UserAuthenticationEntity} if found; otherwise null */ - UserAuthenticationEntity getAuthenticationEntity(UserEntity userEntity, UserAuthenticationType type) { + protected UserAuthenticationEntity getAuthenticationEntity(UserEntity userEntity, UserAuthenticationType type) { Collection<UserAuthenticationEntity> authenticationEntities = (userEntity == null) ? null : userEntity.getAuthenticationEntities(); if (authenticationEntities != null) { for (UserAuthenticationEntity authenticationEntity : authenticationEntities) { http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilter.java index 3667012..f617a60 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilter.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilter.java @@ -92,6 +92,11 @@ public class AmbariBasicAuthenticationFilter extends BasicAuthenticationFilter i return (header != null) && header.startsWith("Basic "); } + @Override + public boolean shouldIncrementFailureCount() { + return true; + } + /** * Checks whether the authentication information is filled. If it is not, then a login failed audit event is logged * http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilter.java deleted file mode 100644 index 3d35578..0000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilter.java +++ /dev/null @@ -1,114 +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.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.configuration.Configuration; -import org.apache.ambari.server.security.authorization.Users; -import org.apache.ambari.server.security.authorization.jwt.JwtAuthenticationFilter; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; - -/** - * AmbariBasicAuthenticationFilter extends a {@link org.apache.ambari.server.security.authorization.jwt.JwtAuthenticationFilter} - * to allow for auditing of authentication attempts. - * <p> - * This authentication filter is expected to be used withing an {@link AmbariDelegatingAuthenticationFilter}. - * - * @see AmbariDelegatingAuthenticationFilter - */ -public class AmbariJWTAuthenticationFilter extends JwtAuthenticationFilter implements AmbariAuthenticationFilter { - - /** - * Ambari authentication event handler - */ - private final AmbariAuthenticationEventHandler eventHandler; - - - /** - * Constructor. - * - * @param ambariEntryPoint the Spring entry point - * @param configuration the Ambari configuration - * @param users the Ambari users object - * @param eventHandler the Ambari authentication event handler - */ - public AmbariJWTAuthenticationFilter(AuthenticationEntryPoint ambariEntryPoint, - Configuration configuration, - Users users, - AmbariAuthenticationEventHandler eventHandler) { - super(configuration, ambariEntryPoint, users); - - if(eventHandler == null) { - throw new IllegalArgumentException("The AmbariAuthenticationEventHandler must not be null"); - } - - this.eventHandler = eventHandler; - } - - /** - * Checks whether the authentication information is filled. If it is not, then a login failed audit event is logged - * - * @param servletRequest the request - * @param servletResponse the response - * @param chain the Spring filter chain - * @throws IOException - * @throws ServletException - */ - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { - - if (eventHandler != null) { - eventHandler.beforeAttemptAuthentication(this, servletRequest, servletResponse); - } - - super.doFilter(servletRequest, servletResponse, chain); - } - - @Override - protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException { - if (eventHandler != null) { - eventHandler.onSuccessfulAuthentication(this, request, response, authResult); - } - } - - @Override - protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { - if (eventHandler != null) { - AmbariAuthenticationException cause; - - if (authException instanceof AmbariAuthenticationException) { - cause = (AmbariAuthenticationException) authException; - } else { - cause = new AmbariAuthenticationException(null, authException.getMessage(), authException); - } - - eventHandler.onUnsuccessfulAuthentication(this, request, response, cause); - } - } -} http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java index dcdf471..7ef6524 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java @@ -20,7 +20,6 @@ package org.apache.ambari.server.security.authentication; import org.apache.ambari.server.configuration.Configuration; import org.apache.ambari.server.orm.entities.UserAuthenticationEntity; import org.apache.ambari.server.orm.entities.UserEntity; -import org.apache.ambari.server.security.authorization.AmbariUserAuthentication; import org.apache.ambari.server.security.authorization.User; import org.apache.ambari.server.security.authorization.UserAuthenticationType; import org.apache.ambari.server.security.authorization.Users; @@ -59,25 +58,28 @@ public class AmbariLocalAuthenticationProvider extends AmbariAuthenticationProvi public Authentication authenticate(Authentication authentication) throws AuthenticationException { String userName = authentication.getName().trim(); - UserEntity userEntity = getUserEntity(userName); + UserEntity userEntity; + try { + userEntity = getUserEntity(userName); - if (userEntity == null) { - LOG.info("User not found: {}", userName); - throw new InvalidUsernamePasswordCombinationException(userName); + if (userEntity == null) { + LOG.info("User not found: {}", userName); + throw new InvalidUsernamePasswordCombinationException(userName); + } } - - int maxConsecutiveFailures = configuration.getMaxAuthenticationFailures(); - if (maxConsecutiveFailures > 0 && userEntity.getConsecutiveFailures() >= maxConsecutiveFailures) { - LOG.info("User account is locked out due to too many authentication failures ({}/{}): {}", - userEntity.getConsecutiveFailures(), maxConsecutiveFailures, userName); + catch(UserNotFoundException e) { + // Do not give away information about the existence or status of a user + throw new InvalidUsernamePasswordCombinationException(userName, e); + } + catch (AccountDisabledException | TooManyLoginFailuresException e) { if (configuration.showLockedOutUserMessage()) { - throw new TooManyLoginFailuresException(userName); + throw e; } else { - throw new InvalidUsernamePasswordCombinationException(userName); + // Do not give away information about the existence or status of a user + throw new InvalidUsernamePasswordCombinationException(userName, e); } } - if (authentication.getCredentials() == null) { LOG.info("Authentication failed: no credentials provided: {}", userName); throw new InvalidUsernamePasswordCombinationException(userName); http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserAuthentication.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserAuthentication.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserAuthentication.java new file mode 100644 index 0000000..41347ad --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariUserAuthentication.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.security.authentication; + +import java.util.Collection; + +import org.apache.ambari.server.security.authorization.AmbariGrantedAuthority; +import org.apache.ambari.server.security.authorization.User; +import org.apache.ambari.server.security.authorization.UserIdAuthentication; +import org.springframework.security.core.Authentication; + +public class AmbariUserAuthentication implements Authentication, UserIdAuthentication { + + private String serializedToken; + private User user; + private Collection<AmbariGrantedAuthority> userAuthorities; + private boolean authenticated = false; + + public AmbariUserAuthentication(String token, User user, Collection<AmbariGrantedAuthority> userAuthorities) { + this.serializedToken = token; + this.user = user; + this.userAuthorities = userAuthorities; + } + + @Override + public Collection<? extends AmbariGrantedAuthority> getAuthorities() { + return userAuthorities; + } + + @Override + public String getCredentials() { + return serializedToken; + } + + @Override + public Object getDetails() { + return null; + } + + @Override + public User getPrincipal() { + return user; + } + + @Override + public boolean isAuthenticated() { + return authenticated; + } + + @Override + public void setAuthenticated(boolean authenticated) throws IllegalArgumentException { + this.authenticated = authenticated; + } + + @Override + public String getName() { + return user.getUserName(); + } + + @Override + public Integer getUserId() { + return user.getUserId(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/UserNotFoundException.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/UserNotFoundException.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/UserNotFoundException.java index 0f2fbb6..0760d9b 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/UserNotFoundException.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/UserNotFoundException.java @@ -23,6 +23,15 @@ package org.apache.ambari.server.security.authentication; * when the user specified in an authentication attempt is not found in the Ambari user database. */ public class UserNotFoundException extends AmbariAuthenticationException { + public static final String MESSAGE = "User does not exist."; + + public UserNotFoundException(String userName) { + super(userName, MESSAGE); + } + + public UserNotFoundException(String userName, Throwable cause) { + super(userName, MESSAGE, cause); + } public UserNotFoundException(String username, String message) { super(username, message); http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/AmbariJwtAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/AmbariJwtAuthenticationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/AmbariJwtAuthenticationFilter.java new file mode 100644 index 0000000..dcaf3e8 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/AmbariJwtAuthenticationFilter.java @@ -0,0 +1,419 @@ +/* + * 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.jwt; + +import java.io.IOException; +import java.security.interfaces.RSAPublicKey; +import java.text.ParseException; +import java.util.Date; +import java.util.List; + +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.security.authentication.AmbariAuthenticationEventHandler; +import org.apache.ambari.server.security.authentication.AmbariAuthenticationException; +import org.apache.ambari.server.security.authentication.AmbariAuthenticationFilter; +import org.apache.ambari.server.security.authentication.AmbariDelegatingAuthenticationFilter; +import org.apache.ambari.server.security.authentication.AmbariUserAuthentication; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.AuthenticationEntryPoint; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSObject; +import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.RSASSAVerifier; +import com.nimbusds.jwt.SignedJWT; + +/** + * AmbariBasicAuthenticationFilter is used to validate JWT token and authenticate users. + * <p> + * This authentication filter is expected to be used withing an {@link AmbariDelegatingAuthenticationFilter}. + * + * @see AmbariDelegatingAuthenticationFilter + */ +public class AmbariJwtAuthenticationFilter implements AmbariAuthenticationFilter { + private static final Logger LOG = LoggerFactory.getLogger(AmbariJwtAuthenticationFilter.class); + + /** + * Ambari authentication event handler + */ + private final AmbariAuthenticationEventHandler eventHandler; + + /** + * Authentication entry point implementation + */ + private final AuthenticationEntryPoint ambariEntryPoint; + + /** + * The /JWT authentication provider + */ + private final AuthenticationProvider authenticationProvider; + + /** + * Authentication properties for JWT authenticatioin + * <p> + * If null JWT authentication has not been enabled + */ + private final JwtAuthenticationProperties jwtProperties; + + /** + * The name of the HTTP cookie containing the authentication token + */ + private final String jwtCookieName; + + /** + * The expected/allowed JWT audiences + * <p> + * If empty, any audience is allowed + */ + private final List<String> audiences; + + /** + * The public key of the token producer, used to verify the signed token + */ + private final RSAPublicKey publicKey; + + /** + * Constructor. + * + * @param ambariEntryPoint the Spring entry point + * @param configuration the Ambari configuration + * @param eventHandler the Ambari authentication event handler + */ + AmbariJwtAuthenticationFilter(AuthenticationEntryPoint ambariEntryPoint, + Configuration configuration, + AuthenticationProvider authenticationProvider, + AmbariAuthenticationEventHandler eventHandler) { + if (eventHandler == null) { + throw new IllegalArgumentException("The AmbariAuthenticationEventHandler must not be null"); + } + + this.ambariEntryPoint = ambariEntryPoint; + this.eventHandler = eventHandler; + + this.jwtProperties = configuration.getJwtProperties(); + this.authenticationProvider = authenticationProvider; + + if (jwtProperties == null) { + this.jwtCookieName = null; + this.audiences = null; + this.publicKey = null; + } else { + this.jwtCookieName = jwtProperties.getCookieName(); + this.audiences = jwtProperties.getAudiences(); + this.publicKey = jwtProperties.getPublicKey(); + } + } + + /** + * Tests to see if this JwtAuthenticationFilter shold be applied in the authentication + * filter chain. + * <p> + * <code>true</code> will be returned if JWT authentication is enabled and the HTTP request contains + * a JWT authentication token cookie; otherwise <code>false</code> will be returned. + * + * @param httpServletRequest the HttpServletRequest the HTTP service request + * @return <code>true</code> if the HTTP request contains the basic authentication header; otherwise <code>false</code> + */ + @Override + public boolean shouldApply(HttpServletRequest httpServletRequest) { + boolean shouldApply = false; + + if (jwtProperties != null) { + String serializedJWT = getJWTFromCookie(httpServletRequest); + shouldApply = (serializedJWT != null && isAuthenticationRequired(serializedJWT)); + } + + return shouldApply; + } + + @Override + public boolean shouldIncrementFailureCount() { + return false; + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + /** + * Checks whether the authentication information is filled. If it is not, then a login failed audit event is logged + * + * @param servletRequest the request + * @param servletResponse the response + * @param chain the Spring filter chain + * @throws IOException + * @throws ServletException + */ + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { + + if (eventHandler != null) { + eventHandler.beforeAttemptAuthentication(this, servletRequest, servletResponse); + } + + if (jwtProperties == null) { + //disable filter if not configured + chain.doFilter(servletRequest, servletResponse); + return; + } + + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + + try { + String serializedJWT = getJWTFromCookie(httpServletRequest); + if (serializedJWT != null && isAuthenticationRequired(serializedJWT)) { + try { + SignedJWT jwtToken = SignedJWT.parse(serializedJWT); + + boolean valid = validateToken(jwtToken); + + if (valid) { + String userName = jwtToken.getJWTClaimsSet().getSubject(); + + Authentication authentication = authenticationProvider.authenticate(new JwtAuthenticationToken(userName, serializedJWT, null)); + SecurityContextHolder.getContext().setAuthentication(authentication); + + if (eventHandler != null) { + eventHandler.onSuccessfulAuthentication(this, httpServletRequest, httpServletResponse, authentication); + } + } else { + throw new BadCredentialsException("Invalid JWT token"); + } + } catch (ParseException e) { + LOG.warn("Unable to parse the JWT token", e); + throw new BadCredentialsException("Unable to parse the JWT token - " + e.getLocalizedMessage()); + } + } else { + LOG.trace("No JWT cookie found, do nothing"); + } + + chain.doFilter(servletRequest, servletResponse); + } catch (AuthenticationException e) { + LOG.warn("JWT authentication failed - {}", e.getLocalizedMessage()); + + //clear security context if authentication was required, but failed + SecurityContextHolder.clearContext(); + + if (eventHandler != null) { + AmbariAuthenticationException cause; + + if (e instanceof AmbariAuthenticationException) { + cause = (AmbariAuthenticationException) e; + } else { + cause = new AmbariAuthenticationException(null, e.getMessage(), e); + } + + eventHandler.onUnsuccessfulAuthentication(this, httpServletRequest, httpServletResponse, cause); + } + + //used to indicate authentication failure, not used here as we have more than one filter + ambariEntryPoint.commence(httpServletRequest, httpServletResponse, e); + } + } + + @Override + public void destroy() { + } + + /** + * Do not try to validate JWT if user already authenticated via other provider + * + * @return true, if JWT validation required + */ + private boolean isAuthenticationRequired(String token) { + Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); + + //authenticate if no auth + if (existingAuth == null || !existingAuth.isAuthenticated()) { + return true; + } + + //revalidate if token was changed + if (existingAuth instanceof AmbariUserAuthentication && !StringUtils.equals(token, (String) existingAuth.getCredentials())) { + return true; + } + + //always try to authenticate in case of anonymous user + return (existingAuth instanceof AnonymousAuthenticationToken); + } + + /** + * Encapsulate the acquisition of the JWT token from HTTP cookies within the + * request. + * + * @param req servlet request to get the JWT token from + * @return serialized JWT token + */ + String getJWTFromCookie(HttpServletRequest req) { + String serializedJWT = null; + Cookie[] cookies = req.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) { + if (jwtCookieName.equals(cookie.getName())) { + LOG.info("{} cookie has been found and is being processed", jwtCookieName); + serializedJWT = cookie.getValue(); + break; + } + } + } + return serializedJWT; + } + + /** + * This method provides a single method for validating the JWT for use in + * request processing. It provides for the override of specific aspects of + * this implementation through submethods used within but also allows for the + * override of the entire token validation algorithm. + * + * @param jwtToken the token to validate + * @return true if valid + */ + private boolean validateToken(SignedJWT jwtToken) { + boolean sigValid = validateSignature(jwtToken); + if (!sigValid) { + LOG.warn("Signature could not be verified"); + } + boolean audValid = validateAudiences(jwtToken); + if (!audValid) { + LOG.warn("Audience validation failed."); + } + boolean expValid = validateExpiration(jwtToken); + if (!expValid) { + LOG.info("Expiration validation failed."); + } + + return sigValid && audValid && expValid; + } + + /** + * Verify the signature of the JWT token in this method. This method depends + * on the public key that was established during init based upon the + * provisioned public key. Override this method in subclasses in order to + * customize the signature verification behavior. + * + * @param jwtToken the token that contains the signature to be validated + * @return valid true if signature verifies successfully; false otherwise + */ + boolean validateSignature(SignedJWT jwtToken) { + boolean valid = false; + if (JWSObject.State.SIGNED == jwtToken.getState()) { + LOG.debug("JWT token is in a SIGNED state"); + if (jwtToken.getSignature() != null) { + LOG.debug("JWT token signature is not null"); + try { + JWSVerifier verifier = new RSASSAVerifier(publicKey); + if (jwtToken.verify(verifier)) { + valid = true; + LOG.debug("JWT token has been successfully verified"); + } else { + LOG.warn("JWT signature verification failed."); + } + } catch (JOSEException je) { + LOG.warn("Error while validating signature", je); + } + } + } + return valid; + } + + /** + * Validate whether any of the accepted audience claims is present in the + * issued token claims list for audience. Override this method in subclasses + * in order to customize the audience validation behavior. + * + * @param jwtToken the JWT token where the allowed audiences will be found + * @return true if an expected audience is present, otherwise false + */ + boolean validateAudiences(SignedJWT jwtToken) { + boolean valid = false; + try { + List<String> tokenAudienceList = jwtToken.getJWTClaimsSet().getAudience(); + // if there were no expected audiences configured then just + // consider any audience acceptable + if (audiences == null) { + valid = true; + } else { + // if any of the configured audiences is found then consider it + // acceptable + if (tokenAudienceList == null) { + LOG.warn("JWT token has no audiences, validation failed."); + return false; + } + LOG.info("Audience List: {}", audiences); + for (String aud : tokenAudienceList) { + LOG.info("Found audience: {}", aud); + if (audiences.contains(aud)) { + LOG.debug("JWT token audience has been successfully validated"); + valid = true; + break; + } + } + if (!valid) { + LOG.warn("JWT audience validation failed."); + } + } + } catch (ParseException pe) { + LOG.warn("Unable to parse the JWT token.", pe); + } + return valid; + } + + /** + * Validate that the expiration time of the JWT token has not been violated. + * If it has then throw an AuthenticationException. Override this method in + * subclasses in order to customize the expiration validation behavior. + * + * @param jwtToken the token that contains the expiration date to validate + * @return valid true if the token has not expired; false otherwise + */ + boolean validateExpiration(SignedJWT jwtToken) { + boolean valid = false; + try { + Date expires = jwtToken.getJWTClaimsSet().getExpirationTime(); + if (expires == null || new Date().before(expires)) { + LOG.debug("JWT token expiration date has been successfully validated"); + valid = true; + } else { + LOG.warn("JWT expiration date validation failed."); + } + } catch (ParseException pe) { + LOG.warn("JWT expiration date validation failed.", pe); + } + return valid; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/AmbariJwtAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/AmbariJwtAuthenticationProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/AmbariJwtAuthenticationProvider.java new file mode 100644 index 0000000..9a5b825 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/AmbariJwtAuthenticationProvider.java @@ -0,0 +1,126 @@ +/* + * 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.jwt; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.orm.entities.UserAuthenticationEntity; +import org.apache.ambari.server.orm.entities.UserEntity; +import org.apache.ambari.server.security.authentication.AmbariAuthenticationException; +import org.apache.ambari.server.security.authentication.AmbariAuthenticationProvider; +import org.apache.ambari.server.security.authentication.AmbariUserAuthentication; +import org.apache.ambari.server.security.authentication.UserNotFoundException; +import org.apache.ambari.server.security.authorization.User; +import org.apache.ambari.server.security.authorization.UserAuthenticationType; +import org.apache.ambari.server.security.authorization.Users; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; + +import com.google.inject.Inject; + +/** + * AmbariLocalAuthenticationProvider is an {@link org.springframework.security.authentication.AuthenticationProvider} + * implementation used to authenticate users using username and password details from the local Ambari database. + * <p> + * Users will fail to authenticate, even if they supply the correct credentials if the account is locked out + * by being disabled or locked due to too many consecutive failure. + */ +public class AmbariJwtAuthenticationProvider extends AmbariAuthenticationProvider { + private static final Logger LOG = LoggerFactory.getLogger(AmbariJwtAuthenticationProvider.class); + + /** + * Helper object to provide logic for working with users. + */ + private Users users; + + /** + * Constructor. + * + * @param users the users helper + * @param configuration the configuration + */ + @Inject + public AmbariJwtAuthenticationProvider(Users users, Configuration configuration) { + super(users, configuration); + this.users = users; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String userName = authentication.getName().trim(); + + UserEntity userEntity; + try { + userEntity = getUserEntity(userName); + + if (userEntity == null) { + LOG.info("User not found: {}", userName); + throw new UserNotFoundException(userName, "Cannot find user from JWT. Please, ensure LDAP is configured and users are synced."); + } + } catch (UserNotFoundException e) { + throw new UserNotFoundException(userName, "Cannot find user from JWT. Please, ensure LDAP is configured and users are synced.", e); + } + + if (authentication.getCredentials() == null) { + LOG.info("Authentication failed: no token provided: {}", userName); + throw new AmbariAuthenticationException(userName, "Unexpected error due to missing JWT token"); + } + + // If the user was found and allowed to log in, make sure that user is allowed to authentcate using a JWT token. + boolean authOK = false; + UserAuthenticationEntity authenticationEntity = getAuthenticationEntity(userEntity, UserAuthenticationType.JWT); + if (authenticationEntity != null) { + authOK = true; + } else { + // TODO: Determine if LDAP users can authenticate using JWT - for now we assume yes. + // If a JWT entity was not found, see if an LDAP entity exists. If so, this user was synced + // with a remote server and this should be allowed to authenticate using JWT + authenticationEntity = getAuthenticationEntity(userEntity, UserAuthenticationType.LDAP); + + if (authenticationEntity != null) { + try { + users.addJWTAuthentication(userEntity, userName); + authOK = true; + } catch (AmbariException e) { + LOG.error(String.format("Failed to add the JWT authentication method for %s: %s", userName, e.getLocalizedMessage()), e); + throw new AmbariAuthenticationException(userName, "Unexpected error has occurred", e); + } + } + } + + if (authOK) { + // The user was authenticated, return the authenticated user object + LOG.debug("Authentication succeeded - a matching user was found: {}", userName); + User user = new User(userEntity); + Authentication auth = new AmbariUserAuthentication(authentication.getCredentials().toString(), user, users.getUserAuthorities(userEntity)); + auth.setAuthenticated(true); + return auth; + } else { + // The user was not authenticated, fail + LOG.debug("Authentication failed: password does not match stored value: {}", userName); + throw new UserNotFoundException(userName, "Cannot find user from JWT. Please, ensure LDAP is configured and users are synced."); + } + } + + @Override + public boolean supports(Class<?> authentication) { + return JwtAuthenticationToken.class.isAssignableFrom(authentication); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/JwtAuthenticationProperties.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/JwtAuthenticationProperties.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/JwtAuthenticationProperties.java new file mode 100644 index 0000000..162f7d9 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/JwtAuthenticationProperties.java @@ -0,0 +1,87 @@ +/* + * 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.jwt; + +import java.security.interfaces.RSAPublicKey; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.StringUtils; + +/** + * Class describes parameters of external JWT authentication provider + */ +public class JwtAuthenticationProperties { + private String authenticationProviderUrl = null; + private RSAPublicKey publicKey = null; + private List<String> audiences = null; + private String cookieName = "hadoop-jwt"; + private String originalUrlQueryParam = null; + + public String getAuthenticationProviderUrl() { + return authenticationProviderUrl; + } + + public void setAuthenticationProviderUrl(String authenticationProviderUrl) { + this.authenticationProviderUrl = authenticationProviderUrl; + } + + public RSAPublicKey getPublicKey() { + return publicKey; + } + + public void setPublicKey(RSAPublicKey publicKey) { + this.publicKey = publicKey; + } + + public List<String> getAudiences() { + return audiences; + } + + public void setAudiences(List<String> audiences) { + this.audiences = audiences; + } + + public void setAudiencesString(String audiencesString) { + if (StringUtils.isNotEmpty(audiencesString)) { + // parse into the list + String[] audArray = audiencesString.split(","); + audiences = new ArrayList<>(); + Collections.addAll(audiences, audArray); + } else { + audiences = null; + } + } + + public String getCookieName() { + return cookieName; + } + + public void setCookieName(String cookieName) { + this.cookieName = cookieName; + } + + public String getOriginalUrlQueryParam() { + return originalUrlQueryParam; + } + + public void setOriginalUrlQueryParam(String originalUrlQueryParam) { + this.originalUrlQueryParam = originalUrlQueryParam; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/JwtAuthenticationToken.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/JwtAuthenticationToken.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/JwtAuthenticationToken.java new file mode 100644 index 0000000..113a6ff --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/jwt/JwtAuthenticationToken.java @@ -0,0 +1,55 @@ +/* + * 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.jwt; + +import java.util.Collection; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +/** + * {@link AbstractAuthenticationToken} implementation for JWT authentication tokens. + */ +public class JwtAuthenticationToken extends AbstractAuthenticationToken { + private final String username; + private final String token; + + /** + * Constructor. + * + * @param username the principal's username + * @param token the JWT token (or credential) + * @param grantedAuthorities the granted authorities + */ + public JwtAuthenticationToken(String username, String token, Collection<? extends GrantedAuthority> grantedAuthorities) { + super(grantedAuthorities); + this.username = username; + this.token = token; + } + + @Override + public Object getCredentials() { + return token; + } + + @Override + public Object getPrincipal() { + return username; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/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 index 23fa171..41275a5 100644 --- 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 @@ -135,6 +135,12 @@ public class AmbariKerberosAuthenticationFilter extends SpnegoAuthenticationProc } } + @Override + public boolean shouldIncrementFailureCount() { + // Always return false since authentication happens remotely. + return false; + } + /** * Performs the logic for this filter. * <p> http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProvider.java index 0823729..a88bcab 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProvider.java @@ -28,6 +28,7 @@ import org.apache.ambari.server.orm.entities.GroupEntity; import org.apache.ambari.server.orm.entities.MemberEntity; import org.apache.ambari.server.orm.entities.UserEntity; import org.apache.ambari.server.security.ClientSecurityType; +import org.apache.ambari.server.security.authentication.AmbariUserAuthentication; import org.apache.ambari.server.security.authentication.pam.PamAuthenticationFactory; import org.jvnet.libpam.PAM; import org.jvnet.libpam.PAMException; http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthentication.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthentication.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthentication.java deleted file mode 100644 index 9445882..0000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthentication.java +++ /dev/null @@ -1,76 +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.security.authorization; - -import java.util.Collection; - -import org.springframework.security.core.Authentication; - -public class AmbariUserAuthentication implements Authentication, UserIdAuthentication { - - private String serializedToken; - private User user; - private Collection<AmbariGrantedAuthority> userAuthorities; - private boolean authenticated = false; - - public AmbariUserAuthentication(String token, User user, Collection<AmbariGrantedAuthority> userAuthorities) { - this.serializedToken = token; - this.user = user; - this.userAuthorities = userAuthorities; - } - - @Override - public Collection<? extends AmbariGrantedAuthority> getAuthorities() { - return userAuthorities; - } - - @Override - public String getCredentials() { - return serializedToken; - } - - @Override - public Object getDetails() { - return null; - } - - @Override - public User getPrincipal() { - return user; - } - - @Override - public boolean isAuthenticated() { - return authenticated; - } - - @Override - public void setAuthenticated(boolean authenticated) throws IllegalArgumentException { - this.authenticated = authenticated; - } - - @Override - public String getName() { - return user.getUserName(); - } - - @Override - public Integer getUserId() { - return user.getUserId(); - } -} http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthorizationFilter.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthorizationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthorizationFilter.java index 8fbd816..9cad29d 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthorizationFilter.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariUserAuthorizationFilter.java @@ -32,6 +32,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.ambari.server.orm.entities.UserEntity; import org.apache.ambari.server.scheduler.ExecutionScheduleManager; +import org.apache.ambari.server.security.authentication.AmbariUserAuthentication; import org.apache.ambari.server.security.authorization.internal.InternalTokenClientFilter; import org.apache.ambari.server.security.authorization.internal.InternalTokenStorage; import org.apache.commons.lang.math.NumberUtils; http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthentication.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthentication.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthentication.java deleted file mode 100644 index 7b21ce6..0000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthentication.java +++ /dev/null @@ -1,34 +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.security.authorization.jwt; - -import java.util.Collection; - -import org.apache.ambari.server.security.authorization.AmbariGrantedAuthority; -import org.apache.ambari.server.security.authorization.AmbariUserAuthentication; -import org.apache.ambari.server.security.authorization.User; - -/** - * Internal token which describes JWT authentication - */ -public class JwtAuthentication extends AmbariUserAuthentication { - - public JwtAuthentication(String token, User user, Collection<AmbariGrantedAuthority> userAuthorities) { - super(token, user, userAuthorities); - } -} http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilter.java deleted file mode 100644 index f42df6c..0000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilter.java +++ /dev/null @@ -1,448 +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.security.authorization.jwt; - -import java.io.IOException; -import java.security.interfaces.RSAPublicKey; -import java.text.ParseException; -import java.util.Collection; -import java.util.Date; -import java.util.List; - -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.ambari.server.AmbariException; -import org.apache.ambari.server.configuration.Configuration; -import org.apache.ambari.server.orm.entities.UserAuthenticationEntity; -import org.apache.ambari.server.orm.entities.UserEntity; -import org.apache.ambari.server.security.authentication.AmbariAuthenticationFilter; -import org.apache.ambari.server.security.authentication.UserNotFoundException; -import org.apache.ambari.server.security.authorization.AmbariGrantedAuthority; -import org.apache.ambari.server.security.authorization.UserAuthenticationType; -import org.apache.ambari.server.security.authorization.Users; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.AuthenticationEntryPoint; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.JWSObject; -import com.nimbusds.jose.JWSVerifier; -import com.nimbusds.jose.crypto.RSASSAVerifier; -import com.nimbusds.jwt.SignedJWT; - -/** - * Filter is used to validate JWT token and authenticate user. - * It is also responsive for creating user in local Ambari database for further management - */ -public class JwtAuthenticationFilter implements AmbariAuthenticationFilter { - private static final Logger LOG = LoggerFactory.getLogger(JwtAuthenticationFilter.class); - - private final JwtAuthenticationProperties jwtProperties; - - private String originalUrlQueryParam = "originalUrl"; - private String authenticationProviderUrl = null; - private RSAPublicKey publicKey = null; - private List<String> audiences = null; - private String cookieName = "hadoop-jwt"; - - private boolean ignoreFailure = false; - private AuthenticationEntryPoint entryPoint; - private Users users; - - public JwtAuthenticationFilter(Configuration configuration, AuthenticationEntryPoint entryPoint, Users users) { - this.entryPoint = entryPoint; - this.users = users; - jwtProperties = configuration.getJwtProperties(); - loadJwtProperties(); - } - - public JwtAuthenticationFilter(JwtAuthenticationProperties jwtProperties, AuthenticationEntryPoint entryPoint, - Users users) { - this.jwtProperties = jwtProperties; - this.entryPoint = entryPoint; - this.users = users; - loadJwtProperties(); - } - - /** - * Tests to see if this JwtAuthenticationFilter should be applied in the authentication - * filter chain. - * <p> - * <code>true</code> will be returned if JWT authentication is enabled and the HTTP request contains - * a JWT authentication token cookie; otherwise <code>false</code> will be returned. - * - * @param httpServletRequest the HttpServletRequest the HTTP service request - * @return <code>true</code> if the HTTP request contains the basic authentication header; otherwise <code>false</code> - */ - @Override - public boolean shouldApply(HttpServletRequest httpServletRequest) { - boolean shouldApply = false; - - if (jwtProperties != null) { - String serializedJWT = getJWTFromCookie(httpServletRequest); - shouldApply = (serializedJWT != null && isAuthenticationRequired(serializedJWT)); - } - - return shouldApply; - } - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - - } - - // TODO: ************ - // TODO: This is to be revisited for AMBARI-21217 (Update JWT Authentication process to work with improved user management facility) - // TODO: ************ - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - - if (jwtProperties == null) { - //disable filter if not configured - filterChain.doFilter(servletRequest, servletResponse); - return; - } - - HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; - HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; - - try { - String serializedJWT = getJWTFromCookie(httpServletRequest); - if (serializedJWT != null && isAuthenticationRequired(serializedJWT)) { - try { - SignedJWT jwtToken = SignedJWT.parse(serializedJWT); - - boolean valid = validateToken(jwtToken); - - if (valid) { - String userName = jwtToken.getJWTClaimsSet().getSubject(); - UserEntity userEntity = users.getUserEntity(userName); - - if (userEntity == null) { - //TODO we temporary expect that LDAP is configured to same server as JWT source - throw new UserNotFoundException(userName, "Cannot find user from JWT. Please, ensure LDAP is configured and users are synced."); - } else { - // Check to see if the user is allowed to authenticate using JWT or LDAP - Collection<UserAuthenticationEntity> authenticationEntities = userEntity.getAuthenticationEntities(); - boolean hasJWT = false; - boolean hasLDAP = false; - - if (authenticationEntities != null) { - for (UserAuthenticationEntity entity : authenticationEntities) { - if (entity.getAuthenticationType() == UserAuthenticationType.JWT) { - // TODO: possibly check the authentication key to see if it is relevant - hasJWT = true; - break; - } else if (entity.getAuthenticationType() == UserAuthenticationType.LDAP) { - hasLDAP = true; - } - } - } - - if(!hasJWT) { - if (hasLDAP) { - // TODO: Determine if LDAP users can authenticate using JWT - try { - users.addJWTAuthentication(userEntity, userName); - } catch (AmbariException e) { - LOG.error(String.format("Failed to add the JWT authentication method for %s: %s", userName, e.getLocalizedMessage()), e); - } - hasJWT = true; - } - } - - if (!hasJWT) { - throw new UserNotFoundException(userName, "User is not authorized to authenticate from JWT. Please, ensure LDAP is configured and users are synced."); - } - } - - // If we made it this far, the user was found and is authorized to authenticate via JWT - Collection<AmbariGrantedAuthority> userAuthorities = users.getUserAuthorities(userEntity); - - JwtAuthentication authentication = new JwtAuthentication(serializedJWT, users.getUser(userEntity), userAuthorities); - authentication.setAuthenticated(true); - - SecurityContextHolder.getContext().setAuthentication(authentication); - onSuccessfulAuthentication(httpServletRequest, httpServletResponse, authentication); - } else { - throw new BadCredentialsException("Invalid JWT token"); - } - } catch (ParseException e) { - LOG.warn("Unable to parse the JWT token", e); - throw new BadCredentialsException("Unable to parse the JWT token - " + e.getLocalizedMessage()); - } - } else { - LOG.trace("No JWT cookie found, do nothing"); - } - - filterChain.doFilter(servletRequest, servletResponse); - } catch (AuthenticationException e) { - LOG.warn("JWT authentication failed - {}", e.getLocalizedMessage()); - - //clear security context if authentication was required, but failed - SecurityContextHolder.clearContext(); - - onUnsuccessfulAuthentication(httpServletRequest, httpServletResponse, e); - - if (ignoreFailure) { - filterChain.doFilter(servletRequest, servletResponse); - } else { - //used to indicate authentication failure, not used here as we have more than one filter - entryPoint.commence(httpServletRequest, httpServletResponse, e); - } - } - } - - private void loadJwtProperties() { - if (jwtProperties != null) { - authenticationProviderUrl = jwtProperties.getAuthenticationProviderUrl(); - publicKey = jwtProperties.getPublicKey(); - audiences = jwtProperties.getAudiences(); - cookieName = jwtProperties.getCookieName(); - originalUrlQueryParam = jwtProperties.getOriginalUrlQueryParam(); - } - } - - /** - * Do not try to validate JWT if user already authenticated via other provider - * - * @return true, if JWT validation required - */ - private boolean isAuthenticationRequired(String token) { - Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); - - //authenticate if no auth - if (existingAuth == null || !existingAuth.isAuthenticated()) { - return true; - } - - //revalidate if token was changed - if (existingAuth instanceof JwtAuthentication && !StringUtils.equals(token, (String) existingAuth.getCredentials())) { - return true; - } - - //always try to authenticate in case of anonymous user - return (existingAuth instanceof AnonymousAuthenticationToken); - } - - /** - * Encapsulate the acquisition of the JWT token from HTTP cookies within the - * request. - * - * @param req servlet request to get the JWT token from - * @return serialized JWT token - */ - protected String getJWTFromCookie(HttpServletRequest req) { - String serializedJWT = null; - Cookie[] cookies = req.getCookies(); - if (cookies != null) { - for (Cookie cookie : cookies) { - if (cookieName.equals(cookie.getName())) { - LOG.info(cookieName - + " cookie has been found and is being processed"); - serializedJWT = cookie.getValue(); - break; - } - } - } - return serializedJWT; - } - - /** - * Create the URL to be used for authentication of the user in the absence of - * a JWT token within the incoming request. - * - * @param request for getting the original request URL - * @return url to use as login url for redirect - */ - protected String constructLoginURL(HttpServletRequest request) { - String delimiter = "?"; - if (authenticationProviderUrl.contains("?")) { - delimiter = "&"; - } - String loginURL = authenticationProviderUrl + delimiter - + originalUrlQueryParam + "=" - + request.getRequestURL(); - return loginURL; - } - - /** - * This method provides a single method for validating the JWT for use in - * request processing. It provides for the override of specific aspects of - * this implementation through submethods used within but also allows for the - * override of the entire token validation algorithm. - * - * @param jwtToken the token to validate - * @return true if valid - */ - protected boolean validateToken(SignedJWT jwtToken) { - boolean sigValid = validateSignature(jwtToken); - if (!sigValid) { - LOG.warn("Signature could not be verified"); - } - boolean audValid = validateAudiences(jwtToken); - if (!audValid) { - LOG.warn("Audience validation failed."); - } - boolean expValid = validateExpiration(jwtToken); - if (!expValid) { - LOG.info("Expiration validation failed."); - } - - return sigValid && audValid && expValid; - } - - /** - * Verify the signature of the JWT token in this method. This method depends - * on the public key that was established during init based upon the - * provisioned public key. Override this method in subclasses in order to - * customize the signature verification behavior. - * - * @param jwtToken the token that contains the signature to be validated - * @return valid true if signature verifies successfully; false otherwise - */ - protected boolean validateSignature(SignedJWT jwtToken) { - boolean valid = false; - if (JWSObject.State.SIGNED == jwtToken.getState()) { - LOG.debug("JWT token is in a SIGNED state"); - if (jwtToken.getSignature() != null) { - LOG.debug("JWT token signature is not null"); - try { - JWSVerifier verifier = new RSASSAVerifier(publicKey); - if (jwtToken.verify(verifier)) { - valid = true; - LOG.debug("JWT token has been successfully verified"); - } else { - LOG.warn("JWT signature verification failed."); - } - } catch (JOSEException je) { - LOG.warn("Error while validating signature", je); - } - } - } - return valid; - } - - /** - * Validate whether any of the accepted audience claims is present in the - * issued token claims list for audience. Override this method in subclasses - * in order to customize the audience validation behavior. - * - * @param jwtToken the JWT token where the allowed audiences will be found - * @return true if an expected audience is present, otherwise false - */ - protected boolean validateAudiences(SignedJWT jwtToken) { - boolean valid = false; - try { - List<String> tokenAudienceList = jwtToken.getJWTClaimsSet() - .getAudience(); - // if there were no expected audiences configured then just - // consider any audience acceptable - if (audiences == null) { - valid = true; - } else { - // if any of the configured audiences is found then consider it - // acceptable - if (tokenAudienceList == null) { - LOG.warn("JWT token has no audiences, validation failed."); - return false; - } - for (String aud : tokenAudienceList) { - if (audiences.contains(aud)) { - LOG.debug("JWT token audience has been successfully validated"); - valid = true; - break; - } - } - if (!valid) { - LOG.warn("JWT audience validation failed."); - } - } - } catch (ParseException pe) { - LOG.warn("Unable to parse the JWT token.", pe); - } - return valid; - } - - /** - * Validate that the expiration time of the JWT token has not been violated. - * If it has then throw an AuthenticationException. Override this method in - * subclasses in order to customize the expiration validation behavior. - * - * @param jwtToken the token that contains the expiration date to validate - * @return valid true if the token has not expired; false otherwise - */ - protected boolean validateExpiration(SignedJWT jwtToken) { - boolean valid = false; - try { - Date expires = jwtToken.getJWTClaimsSet().getExpirationTime(); - if (expires == null || new Date().before(expires)) { - LOG.debug("JWT token expiration date has been " - + "successfully validated"); - valid = true; - } else { - LOG.warn("JWT expiration date validation failed."); - } - } catch (ParseException pe) { - LOG.warn("JWT expiration date validation failed.", pe); - } - return valid; - } - - /** - * Called to declare an authentication attempt was successful. Classes may override this method - * to perform additional tasks when authentication completes. - * - * @param request the request - * @param response the response - * @param authResult the authenticated user - * @throws IOException - */ - protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException { - } - - /** - * Called to declare an authentication attempt failed. Classes may override this method - * to perform additional tasks when authentication fails. - * - * @param request the request - * @param response the response - * @param authException the cause for the faulure - * @throws IOException - */ - protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { - } - - @Override - public void destroy() { - - } -} http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationProperties.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationProperties.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationProperties.java deleted file mode 100644 index cb456fa..0000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationProperties.java +++ /dev/null @@ -1,87 +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.security.authorization.jwt; - -import java.security.interfaces.RSAPublicKey; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.apache.commons.lang.StringUtils; - -/** - * Class describes parameters of external JWT authentication provider - */ -public class JwtAuthenticationProperties { - private String authenticationProviderUrl = null; - private RSAPublicKey publicKey = null; - private List<String> audiences = null; - private String cookieName = "hadoop-jwt"; - private String originalUrlQueryParam = null; - - public String getAuthenticationProviderUrl() { - return authenticationProviderUrl; - } - - public void setAuthenticationProviderUrl(String authenticationProviderUrl) { - this.authenticationProviderUrl = authenticationProviderUrl; - } - - public RSAPublicKey getPublicKey() { - return publicKey; - } - - public void setPublicKey(RSAPublicKey publicKey) { - this.publicKey = publicKey; - } - - public List<String> getAudiences() { - return audiences; - } - - public void setAudiences(List<String> audiences) { - this.audiences = audiences; - } - - public void setAudiencesString(String audiencesString) { - if (StringUtils.isNotEmpty(audiencesString)) { - // parse into the list - String[] audArray = audiencesString.split(","); - audiences = new ArrayList<>(); - Collections.addAll(audiences, audArray); - } else { - audiences = null; - } - } - - public String getCookieName() { - return cookieName; - } - - public void setCookieName(String cookieName) { - this.cookieName = cookieName; - } - - public String getOriginalUrlQueryParam() { - return originalUrlQueryParam; - } - - public void setOriginalUrlQueryParam(String originalUrlQueryParam) { - this.originalUrlQueryParam = originalUrlQueryParam; - } -} http://git-wip-us.apache.org/repos/asf/ambari/blob/553e4f9d/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java index 32dd6dc..c3451dd 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java @@ -671,7 +671,7 @@ public class AmbariLdapDataPopulator { } } } while (configuration.getLdapServerProperties().isPaginationEnabled() - && processor.getCookie().getCookie() != null); + && (processor.getCookie() != null) && (processor.getCookie().getCookie() != null)); return users; }
