You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by rl...@apache.org on 2017/10/17 15:58:25 UTC
[2/2] ambari git commit: AMBARI-21217. Update JWT Authentication
process to work with improved user management facility (rlevas)
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 <rl...@hortonworks.com>
Authored: Tue Oct 17 11:56:42 2017 -0400
Committer: Robert Levas <rl...@hortonworks.com>
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;
}