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 2016/09/15 17:19:45 UTC
[1/2] ambari git commit: AMBARI-18367. Create authentication filter
to encapsulate the various Ambari authentication methods (rlevas)
Repository: ambari
Updated Branches:
refs/heads/trunk edf1b9b9f -> d5cca62c8
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilterTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilterTest.java
new file mode 100644
index 0000000..f042a70
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilterTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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 com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+import org.apache.ambari.server.audit.AuditLogger;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.security.AmbariEntryPoint;
+import org.apache.ambari.server.security.authorization.PermissionHelper;
+import org.apache.ambari.server.security.authorization.User;
+import org.apache.ambari.server.security.authorization.UserType;
+import org.apache.ambari.server.security.authorization.Users;
+import org.apache.ambari.server.security.authorization.jwt.JwtAuthenticationProperties;
+import org.easymock.EasyMockSupport;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import javax.servlet.FilterChain;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+
+public class AmbariJWTAuthenticationFilterTest extends EasyMockSupport {
+ private static RSAPublicKey publicKey;
+ private static RSAPrivateKey privateKey;
+
+ @BeforeClass
+ public static void generateKeyPair() throws NoSuchAlgorithmException {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(512);
+ KeyPair keyPair = keyPairGenerator.generateKeyPair();
+ publicKey = (RSAPublicKey) keyPair.getPublic();
+ privateKey = (RSAPrivateKey) keyPair.getPrivate();
+ }
+
+ @Test
+ public void testDoFilterSuccess() throws Exception {
+ SignedJWT token = getSignedToken("foobar");
+
+ AmbariEntryPoint entryPoint = createMock(AmbariEntryPoint.class);
+
+ JwtAuthenticationProperties properties = createMock(JwtAuthenticationProperties.class);
+ expect(properties.getAuthenticationProviderUrl()).andReturn("some url").once();
+ expect(properties.getPublicKey()).andReturn(publicKey).once();
+ expect(properties.getAudiences()).andReturn(Collections.singletonList("foobar")).once();
+ expect(properties.getCookieName()).andReturn("chocolate chip").once();
+ expect(properties.getOriginalUrlQueryParam()).andReturn("question").once();
+
+ Configuration configuration = createMock(Configuration.class);
+ expect(configuration.getJwtProperties()).andReturn(properties).once();
+
+ User user = createMock(User.class);
+ expect(user.getUserName()).andReturn("test-user").once();
+ expect(user.getUserType()).andReturn(UserType.JWT).once();
+
+ Users users = createMock(Users.class);
+ expect(users.getUser("test-user", UserType.JWT)).andReturn(user).once();
+ expect(users.getUserAuthorities("test-user", UserType.JWT)).andReturn(null).once();
+
+ AuditLogger auditLogger = createMock(AuditLogger.class);
+ expect(auditLogger.isEnabled()).andReturn(false).times(2);
+
+ PermissionHelper permissionHelper = createMock(PermissionHelper.class);
+
+ Cookie cookie = createMock(Cookie.class);
+ expect(cookie.getName()).andReturn("chocolate chip").once();
+ expect(cookie.getValue()).andReturn(token.serialize()).once();
+
+
+ HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+ expect(servletRequest.getCookies()).andReturn(new Cookie[]{cookie}).once();
+
+ HttpServletResponse servletResponse = createMock(HttpServletResponse.class);
+
+ FilterChain filterChain = createMock(FilterChain.class);
+ filterChain.doFilter(servletRequest, servletResponse);
+ expectLastCall().once();
+
+ replayAll();
+
+ AmbariJWTAuthenticationFilter filter = new AmbariJWTAuthenticationFilter(entryPoint, configuration, users, auditLogger, permissionHelper);
+ filter.doFilter(servletRequest, servletResponse, filterChain);
+
+ verifyAll();
+ }
+
+ @Test
+ public void testDoFilterFailure() throws Exception {
+ AmbariEntryPoint entryPoint = createMock(AmbariEntryPoint.class);
+
+ JwtAuthenticationProperties properties = createMock(JwtAuthenticationProperties.class);
+ expect(properties.getAuthenticationProviderUrl()).andReturn("some url").once();
+ expect(properties.getPublicKey()).andReturn(publicKey).once();
+ expect(properties.getAudiences()).andReturn(Collections.singletonList("foobar")).once();
+ expect(properties.getCookieName()).andReturn("chocolate chip").once();
+ expect(properties.getOriginalUrlQueryParam()).andReturn("question").once();
+
+ Configuration configuration = createMock(Configuration.class);
+ expect(configuration.getJwtProperties()).andReturn(properties).once();
+
+ Users users = createMock(Users.class);
+
+ AuditLogger auditLogger = createMock(AuditLogger.class);
+ expect(auditLogger.isEnabled()).andReturn(false).times(2);
+
+ PermissionHelper permissionHelper = createMock(PermissionHelper.class);
+
+ Cookie cookie = createMock(Cookie.class);
+ expect(cookie.getName()).andReturn("chocolate chip").once();
+ expect(cookie.getValue()).andReturn("invalid token").once();
+
+
+ HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+ expect(servletRequest.getCookies()).andReturn(new Cookie[]{cookie}).once();
+
+ HttpServletResponse servletResponse = createMock(HttpServletResponse.class);
+
+ FilterChain filterChain = createMock(FilterChain.class);
+ filterChain.doFilter(servletRequest, servletResponse);
+ expectLastCall().once();
+
+ replayAll();
+
+ AmbariJWTAuthenticationFilter filter = new AmbariJWTAuthenticationFilter(entryPoint, configuration, users, auditLogger, permissionHelper);
+ filter.doFilter(servletRequest, servletResponse, filterChain);
+
+ verifyAll();
+ }
+
+
+ private SignedJWT getSignedToken(String audience) throws JOSEException {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(System.currentTimeMillis());
+ calendar.add(Calendar.DATE, 1); //add one day
+ return getSignedToken(calendar.getTime(), audience);
+ }
+
+ private SignedJWT getSignedToken(Date expirationTime, String audience) throws JOSEException {
+ RSASSASigner signer = new RSASSASigner(privateKey);
+
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(System.currentTimeMillis());
+ JWTClaimsSet claimsSet = new JWTClaimsSet();
+ claimsSet.setSubject("test-user");
+ claimsSet.setIssuer("unit-test");
+ claimsSet.setIssueTime(calendar.getTime());
+
+ claimsSet.setExpirationTime(expirationTime);
+
+ claimsSet.setAudience(audience);
+
+ SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsSet);
+ signedJWT.sign(signer);
+
+ return signedJWT;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilterTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilterTest.java
index 1d71fe6..10de0a9 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilterTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilterTest.java
@@ -30,6 +30,7 @@ import org.apache.ambari.server.audit.AuditLogger;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.orm.DBAccessor;
import org.apache.ambari.server.orm.dao.UserDAO;
+import org.apache.ambari.server.security.AmbariEntryPoint;
import org.apache.ambari.server.security.TestAuthenticationFactory;
import org.apache.ambari.server.state.stack.OsFamily;
import org.apache.ambari.server.view.ViewRegistry;
@@ -324,7 +325,8 @@ public class AmbariAuthorizationFilterTest {
}
});
- AmbariAuthorizationFilter filter = new AmbariAuthorizationFilter();
+ AmbariAuthorizationFilter filter = new AmbariAuthorizationFilter(createNiceMock(AmbariEntryPoint.class), injector.getInstance(Configuration.class),
+ injector.getInstance(Users.class), injector.getInstance(AuditLogger.class), injector.getInstance(PermissionHelper.class));
injector.injectMembers(filter);
filter.doFilter(request, response, chain);
@@ -338,14 +340,28 @@ public class AmbariAuthorizationFilterTest {
* @param authentication the authentication to use
* @param urlTests map of triples: url - http method - is allowed
* @param expectRedirect true if the requests should redirect to login
- * @throws Exception
+ * @throws Exception if an exception occurs
*/
private void performGeneralDoFilterTest(Authentication authentication, Table<String, String, Boolean> urlTests, boolean expectRedirect) throws Exception {
final SecurityContext securityContext = createNiceMock(SecurityContext.class);
final FilterConfig filterConfig = createNiceMock(FilterConfig.class);
+
+ final Configuration configuration = EasyMock.createMock(Configuration.class);
+ expect(configuration.getDefaultApiAuthenticatedUser()).andReturn(null).anyTimes();
+
+ final AuditLogger auditLogger = EasyMock.createNiceMock(AuditLogger.class);
+ expect(auditLogger.isEnabled()).andReturn(false).anyTimes();
+
final AmbariAuthorizationFilter filter = createMockBuilder(AmbariAuthorizationFilter.class)
- .addMockedMethod("getSecurityContext").addMockedMethod("getViewRegistry").withConstructor().createMock();
- injectMembers(filter);
+ .addMockedMethod("getSecurityContext")
+ .addMockedMethod("getViewRegistry")
+ .withConstructor(createNiceMock(AmbariEntryPoint.class),
+ configuration,
+ createNiceMock(Users.class),
+ auditLogger,
+ createNiceMock(PermissionHelper.class))
+ .createMock();
+
final ViewRegistry viewRegistry = createNiceMock(ViewRegistry.class);
expect(filterConfig.getInitParameter("realm")).andReturn("AuthFilter").anyTimes();
@@ -355,7 +371,7 @@ public class AmbariAuthorizationFilterTest {
expect(securityContext.getAuthentication()).andReturn(authentication).anyTimes();
expect(viewRegistry.checkPermission(EasyMock.eq("DeniedView"), EasyMock.<String>anyObject(), EasyMock.<String>anyObject(), EasyMock.anyBoolean())).andReturn(false).anyTimes();
- replay(filterConfig, filter, securityContext, viewRegistry);
+ replay(filterConfig, filter, securityContext, viewRegistry, configuration, auditLogger);
for (final Cell<String, String, Boolean> urlTest: urlTests.cellSet()) {
final FilterChain chain = EasyMock.createStrictMock(FilterChain.class);
@@ -399,26 +415,4 @@ public class AmbariAuthorizationFilterTest {
}
}
}
-
- private void injectMembers(AmbariAuthorizationFilter filter) {
- final Configuration configuration = EasyMock.createMock(Configuration.class);
- expect(configuration.getDefaultApiAuthenticatedUser()).andReturn(null).anyTimes();
- final AuditLogger auditLogger = EasyMock.createNiceMock(AuditLogger.class);
- expect(auditLogger.isEnabled()).andReturn(false).anyTimes();
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override
- protected void configure() {
- bind(Configuration.class).toInstance(configuration);
- bind(Users.class).toInstance(EasyMock.createMock(Users.class));
- bind(EntityManager.class).toInstance(EasyMock.createMock(EntityManager.class));
- bind(UserDAO.class).toInstance(EasyMock.createMock(UserDAO.class));
- bind(DBAccessor.class).toInstance(EasyMock.createMock(DBAccessor.class));
- bind(PasswordEncoder.class).toInstance(EasyMock.createMock(PasswordEncoder.class));
- bind(OsFamily.class).toInstance(EasyMock.createMock(OsFamily.class));
- bind(AuditLogger.class).toInstance(auditLogger);
- }
- });
- injector.injectMembers(filter);
- replay(configuration, auditLogger);
- }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilterTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilterTest.java
index b08d1ac..5814203 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilterTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/jwt/JwtAuthenticationFilterTest.java
@@ -24,13 +24,12 @@ import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
+import junit.framework.Assert;
import org.apache.ambari.server.security.authorization.AmbariGrantedAuthority;
import org.apache.ambari.server.security.authorization.AuthorizationHelper;
import org.apache.ambari.server.security.authorization.User;
import org.apache.ambari.server.security.authorization.UserType;
import org.apache.ambari.server.security.authorization.Users;
-import org.easymock.EasyMock;
-import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
@@ -46,10 +45,8 @@ import javax.servlet.http.HttpServletResponse;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
-import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
@@ -57,6 +54,7 @@ import java.util.List;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMockBuilder;
+import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
@@ -299,4 +297,75 @@ public class JwtAuthenticationFilterTest {
assertEquals(false, isValid);
}
+
+ @Test
+ public void testShouldApplyTrue() throws JOSEException {
+ JwtAuthenticationProperties properties = createTestProperties();
+ JwtAuthenticationFilter filter = new JwtAuthenticationFilter(properties, null, null);
+
+ SignedJWT token = getInvalidToken();
+
+ Cookie cookie = createMock(Cookie.class);
+ expect(cookie.getName()).andReturn("non-default").atLeastOnce();
+ expect(cookie.getValue()).andReturn(token.serialize()).atLeastOnce();
+
+ HttpServletRequest request = createMock(HttpServletRequest.class);
+ expect(request.getCookies()).andReturn(new Cookie[]{cookie});
+
+ replay(request, cookie);
+
+ Assert.assertTrue(filter.shouldApply(request));
+
+ verify(request, cookie);
+ }
+
+ @Test
+ public void testShouldApplyTrueBadToken() throws JOSEException {
+ JwtAuthenticationProperties properties = createTestProperties();
+ JwtAuthenticationFilter filter = new JwtAuthenticationFilter(properties, null, null);
+
+ Cookie cookie = createMock(Cookie.class);
+ expect(cookie.getName()).andReturn("non-default").atLeastOnce();
+ expect(cookie.getValue()).andReturn("bad token").atLeastOnce();
+
+ HttpServletRequest request = createMock(HttpServletRequest.class);
+ expect(request.getCookies()).andReturn(new Cookie[]{cookie});
+
+ replay(request, cookie);
+
+ Assert.assertTrue(filter.shouldApply(request));
+
+ verify(request, cookie);
+ }
+
+ @Test
+ public void testShouldApplyFalseMissingCookie() throws JOSEException {
+ JwtAuthenticationProperties properties = createTestProperties();
+ JwtAuthenticationFilter filter = new JwtAuthenticationFilter(properties, null, null);
+
+ Cookie cookie = createMock(Cookie.class);
+ expect(cookie.getName()).andReturn("some-other-cookie").atLeastOnce();
+
+ HttpServletRequest request = createMock(HttpServletRequest.class);
+ expect(request.getCookies()).andReturn(new Cookie[]{cookie});
+
+ replay(request, cookie);
+
+ Assert.assertFalse(filter.shouldApply(request));
+
+ verify(request, cookie);
+ }
+
+ @Test
+ public void testShouldApplyFalseNotEnabled() throws JOSEException {
+ JwtAuthenticationFilter filter = new JwtAuthenticationFilter((JwtAuthenticationProperties) null, null, null);
+
+ HttpServletRequest request = createMock(HttpServletRequest.class);
+
+ replay(request);
+
+ Assert.assertFalse(filter.shouldApply(request));
+
+ verify(request);
+ }
}
\ No newline at end of file
[2/2] ambari git commit: AMBARI-18367. Create authentication filter
to encapsulate the various Ambari authentication methods (rlevas)
Posted by rl...@apache.org.
AMBARI-18367. Create authentication filter to encapsulate the various Ambari authentication methods (rlevas)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/d5cca62c
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/d5cca62c
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/d5cca62c
Branch: refs/heads/trunk
Commit: d5cca62c85f97118ed9e2a3e14729d9e6cb6023d
Parents: edf1b9b
Author: Robert Levas <rl...@hortonworks.com>
Authored: Thu Sep 15 13:19:32 2016 -0400
Committer: Robert Levas <rl...@hortonworks.com>
Committed: Thu Sep 15 13:19:40 2016 -0400
----------------------------------------------------------------------
.../ambari/server/controller/AmbariServer.java | 15 +-
.../server/controller/ControllerModule.java | 12 +-
.../AmbariAuthenticationFilter.java | 158 ++------------
.../AmbariBasicAuthenticationFilter.java | 211 +++++++++++++++++++
.../AmbariDelegatingAuthenticationFilter.java | 146 +++++++++++++
.../AmbariJWTAuthenticationFilter.java | 139 ++++++++++++
.../AmbariAuthorizationFilter.java | 49 ++++-
.../AuthenticationJwtUserNotFoundException.java | 43 ++++
.../jwt/JwtAuthenticationFilter.java | 162 ++++++++------
.../webapp/WEB-INF/spring-security.xml | 31 ++-
.../AmbariAuthenticationFilterTest.java | 139 ------------
.../AmbariBasicAuthenticationFilterTest.java | 139 ++++++++++++
...mbariDelegatingAuthenticationFilterTest.java | 186 ++++++++++++++++
.../AmbariJWTAuthenticationFilterTest.java | 190 +++++++++++++++++
.../AmbariAuthorizationFilterTest.java | 48 ++---
.../jwt/JwtAuthenticationFilterTest.java | 77 ++++++-
16 files changed, 1347 insertions(+), 398 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/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 deac313..0e6e6b1 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
@@ -1,4 +1,4 @@
-/**
+/*
* 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
@@ -7,7 +7,7 @@
* "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
+ * 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,
@@ -99,15 +99,12 @@ import org.apache.ambari.server.security.AmbariServerSecurityHeaderFilter;
import org.apache.ambari.server.security.AmbariViewsSecurityHeaderFilter;
import org.apache.ambari.server.security.CertificateManager;
import org.apache.ambari.server.security.SecurityFilter;
-import org.apache.ambari.server.security.authentication.AmbariAuthenticationFilter;
-import org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter;
import org.apache.ambari.server.security.authorization.AmbariLdapAuthenticationProvider;
import org.apache.ambari.server.security.authorization.AmbariLocalUserProvider;
import org.apache.ambari.server.security.authorization.AmbariUserAuthorizationFilter;
import org.apache.ambari.server.security.authorization.PermissionHelper;
import org.apache.ambari.server.security.authorization.Users;
import org.apache.ambari.server.security.authorization.internal.AmbariInternalAuthenticationProvider;
-import org.apache.ambari.server.security.authorization.jwt.JwtAuthenticationFilter;
import org.apache.ambari.server.security.ldap.AmbariLdapDataPopulator;
import org.apache.ambari.server.security.unsecured.rest.CertificateDownload;
import org.apache.ambari.server.security.unsecured.rest.CertificateSign;
@@ -319,6 +316,8 @@ public class AmbariServer {
getBeanFactory();
factory.registerSingleton("guiceInjector", injector);
+ factory.registerSingleton("ambariConfiguration", injector.getInstance(Configuration.class));
+ factory.registerSingleton("ambariUsers", injector.getInstance(Users.class));
factory.registerSingleton("passwordEncoder",
injector.getInstance(PasswordEncoder.class));
factory.registerSingleton("auditLogger",
@@ -331,16 +330,10 @@ public class AmbariServer {
injector.getInstance(AmbariLocalUserProvider.class));
factory.registerSingleton("ambariLdapDataPopulator",
injector.getInstance(AmbariLdapDataPopulator.class));
- factory.registerSingleton("ambariAuthorizationFilter",
- injector.getInstance(AmbariAuthorizationFilter.class));
factory.registerSingleton("ambariUserAuthorizationFilter",
injector.getInstance(AmbariUserAuthorizationFilter.class));
factory.registerSingleton("ambariInternalAuthenticationProvider",
injector.getInstance(AmbariInternalAuthenticationProvider.class));
- factory.registerSingleton("ambariJwtAuthenticationFilter",
- injector.getInstance(JwtAuthenticationFilter.class));
- factory.registerSingleton("ambariAuthenticationFilter",
- injector.getInstance(AmbariAuthenticationFilter.class));
// Spring Security xml config depends on this Bean
String[] contextLocations = {SPRING_CONTEXT_LOCATION};
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
index bd34c25..af710ee 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
@@ -1,4 +1,4 @@
-/**
+/*
* 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
@@ -6,9 +6,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- * <p/>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p/>
+ *
+ * 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.
@@ -88,7 +88,6 @@ import org.apache.ambari.server.orm.PersistenceType;
import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
import org.apache.ambari.server.scheduler.ExecutionScheduler;
import org.apache.ambari.server.scheduler.ExecutionSchedulerImpl;
-import org.apache.ambari.server.security.AmbariEntryPoint;
import org.apache.ambari.server.security.SecurityHelper;
import org.apache.ambari.server.security.SecurityHelperImpl;
import org.apache.ambari.server.security.authorization.AuthorizationHelper;
@@ -144,7 +143,6 @@ import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
-import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.util.ClassUtils;
import org.springframework.web.filter.DelegatingFilterProxy;
@@ -369,8 +367,6 @@ public class ControllerModule extends AbstractModule {
bind(PersistedState.class).to(PersistedStateImpl.class);
- bind(AuthenticationEntryPoint.class).to(AmbariEntryPoint.class).in(Scopes.SINGLETON);
-
// factory to create LoggingRequestHelper instances for LogSearch integration
bind(LoggingRequestHelperFactory.class).to(LoggingRequestHelperFactoryImpl.class);
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/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 0a312f3..b3bc4c3 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
@@ -1,4 +1,4 @@
-/**
+/*
* 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
@@ -7,7 +7,7 @@
* "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
+ * 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,
@@ -15,151 +15,35 @@
* 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.Filter;
import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.ambari.server.audit.event.AuditEvent;
-import org.apache.ambari.server.audit.AuditLogger;
-import org.apache.ambari.server.audit.event.LoginAuditEvent;
-import org.apache.ambari.server.security.AmbariEntryPoint;
-import org.apache.ambari.server.security.authorization.AuthorizationHelper;
-import org.apache.ambari.server.security.authorization.PermissionHelper;
-import org.apache.ambari.server.utils.RequestUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.authentication.BadCredentialsException;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.crypto.codec.Base64;
-import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
/**
- * The purpose of this class is to check whether authentication is successful or not,
- * and make an audit event
+ * AmbariAuthenticationFilter is a {@link Filter} interface to be implemented
+ * by authentication filters used by the Ambari server. More specifically, implementations
+ * of this interface may be used in the {@link AmbariDelegatingAuthenticationFilter}.
+ *
+ * @see AmbariDelegatingAuthenticationFilter
*/
-public class AmbariAuthenticationFilter extends BasicAuthenticationFilter {
- private static final Logger LOG = LoggerFactory.getLogger(AmbariAuthenticationFilter.class);
-
- /**
- * Audit logger
- */
- private AuditLogger auditLogger;
-
- private PermissionHelper permissionHelper;
-
- public AmbariAuthenticationFilter() {
- super();
- }
-
- public AmbariAuthenticationFilter(AuthenticationManager authenticationManager, AuditLogger auditLogger, PermissionHelper permissionHelper, AmbariEntryPoint ambariEntryPoint) {
- super(authenticationManager, ambariEntryPoint);
- this.auditLogger = auditLogger;
- this.permissionHelper = permissionHelper;
- }
-
- /**
- * Checks whether the authentication information is filled. If it is not, then a login failed audit event is logged
- * @param req
- * @param res
- * @param chain
- * @throws IOException
- * @throws ServletException
- */
- @Override
- public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
- HttpServletRequest request = (HttpServletRequest) req;
- String header = request.getHeader("Authorization");
- if (auditLogger.isEnabled() && AuthorizationHelper.getAuthenticatedName() == null && (header == null || !header.startsWith("Basic "))) {
- AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
- .withRemoteIp(RequestUtils.getRemoteAddress(request))
- .withTimestamp(System.currentTimeMillis())
- .withReasonOfFailure("Authentication required")
- .withUserName(null)
- .build();
- auditLogger.log(loginFailedAuditEvent);
- }
- super.doFilter(req, res, chain);
- }
-
- /**
- * If the authentication was successful, then an audit event is logged about the success
- * @param request
- * @param response
- * @param authResult
- * @throws IOException
- */
- @Override
- protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException {
- if(!auditLogger.isEnabled()) {
- return;
- }
- AuditEvent loginSucceededAuditEvent = LoginAuditEvent.builder()
- .withRemoteIp(RequestUtils.getRemoteAddress(request))
- .withUserName(authResult.getName())
- .withTimestamp(System.currentTimeMillis())
- .withRoles(permissionHelper.getPermissionLabels(authResult))
- .build();
- auditLogger.log(loginSucceededAuditEvent);
- }
+public interface AmbariAuthenticationFilter extends Filter {
/**
- * In the case of invalid username or password, the authentication fails and it is logged
- * @param request
- * @param response
- * @param authEx
- * @throws IOException
+ * Tests this AmbariAuthenticationFilter to see if it should be applied to the filter chain
+ * - meaning its {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)} method
+ * should be called.
+ * <p>
+ * Each implementation will have its own requirements on whether it should be applied and care must
+ * be taken such that <code>true</code> is returned on if warranted, else other implementations may
+ * not get a chance to execute
+ *
+ * @param httpServletRequest the HttpServletRequest
+ * @return true if this AmbariAuthenticationFilter should be applied to the filter chain; otherwise false.
*/
- @Override
- protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx) throws IOException {
- String header = request.getHeader("Authorization");
- String username = null;
- try {
- String[] decodedAuth = decodeAuth(header, request);
- username = decodedAuth[0];
- } catch (Exception e) {
- LOG.warn("Error occurred during decoding authorization header.",e);
- }
- if(auditLogger.isEnabled()) {
- AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
- .withRemoteIp(RequestUtils.getRemoteAddress(request))
- .withTimestamp(System.currentTimeMillis())
- .withReasonOfFailure("Invalid username/password combination")
- .withUserName(username)
- .build();
- auditLogger.log(loginFailedAuditEvent);
- }
- }
-
- /**
- * Helper function to decode Authorization header
- * @param header
- * @param request
- * @return
- * @throws IOException
- */
- private String[] decodeAuth(String header, HttpServletRequest request) throws IOException {
- byte[] base64Token = header.substring(6).getBytes("UTF-8");
-
- byte[] decoded;
- try {
- decoded = Base64.decode(base64Token);
- } catch (IllegalArgumentException ex) {
- throw new BadCredentialsException("Failed to decode basic authentication token");
- }
-
- String token = new String(decoded, this.getCredentialsCharset(request));
- int delim = token.indexOf(":");
- if(delim == -1) {
- throw new BadCredentialsException("Invalid basic authentication token");
- } else {
- return new String[]{token.substring(0, delim), token.substring(delim + 1)};
- }
- }
+ boolean shouldApply(HttpServletRequest httpServletRequest);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/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
new file mode 100644
index 0000000..9e83f73
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilter.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.security.authentication;
+
+import java.io.IOException;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.AuditLogger;
+import org.apache.ambari.server.audit.event.LoginAuditEvent;
+import org.apache.ambari.server.security.AmbariEntryPoint;
+import org.apache.ambari.server.security.authorization.AuthorizationHelper;
+import org.apache.ambari.server.security.authorization.PermissionHelper;
+import org.apache.ambari.server.utils.RequestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.crypto.codec.Base64;
+import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+
+/**
+ * AmbariBasicAuthenticationFilter extends a {@link BasicAuthenticationFilter} to allow for auditing
+ * of authentication attempts
+ * <p>
+ * This authentication filter is expected to be used withing an {@link AmbariDelegatingAuthenticationFilter}.
+ *
+ * @see AmbariDelegatingAuthenticationFilter
+ */
+public class AmbariBasicAuthenticationFilter extends BasicAuthenticationFilter implements AmbariAuthenticationFilter {
+ private static final Logger LOG = LoggerFactory.getLogger(AmbariBasicAuthenticationFilter.class);
+
+ /**
+ * Audit logger
+ */
+ private AuditLogger auditLogger;
+
+ /**
+ * PermissionHelper to help create audit entries
+ */
+ private PermissionHelper permissionHelper;
+
+ /**
+ * Constructor.
+ *
+ * @param authenticationManager the Spring authencation manager
+ * @param ambariEntryPoint the Spring entry point
+ * @param auditLogger an Audit Logger
+ * @param permissionHelper a permission helper
+ */
+ public AmbariBasicAuthenticationFilter(AuthenticationManager authenticationManager,
+ AmbariEntryPoint ambariEntryPoint,
+ AuditLogger auditLogger,
+ PermissionHelper permissionHelper) {
+ super(authenticationManager, ambariEntryPoint);
+ this.auditLogger = auditLogger;
+ this.permissionHelper = permissionHelper;
+ }
+
+ /**
+ * Tests to see if this {@link AmbariBasicAuthenticationFilter} should be applied in the authentication
+ * filter chain.
+ * <p>
+ * <code>true</code> will be returned if the HTTP request contains the basic authentication header;
+ * otherwise <code>false</code> will be returned.
+ * <p>
+ * The basic authentication header is named "Authorization" and the value begins with the string
+ * "Basic" following by the encoded username and password information.
+ * <p>
+ * For example:
+ * <code>
+ * Authorization: Basic YWRtaW46YWRtaW4=
+ * </code>
+ *
+ * @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) {
+ String header = httpServletRequest.getHeader("Authorization");
+ return (header != null) && header.startsWith("Basic ");
+ }
+
+ /**
+ * 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 {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
+
+ if (auditLogger.isEnabled() && shouldApply(httpServletRequest) && (AuthorizationHelper.getAuthenticatedName() == null)) {
+ AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
+ .withRemoteIp(RequestUtils.getRemoteAddress(httpServletRequest))
+ .withTimestamp(System.currentTimeMillis())
+ .withReasonOfFailure("Authentication required")
+ .withUserName(null)
+ .build();
+ auditLogger.log(loginFailedAuditEvent);
+ }
+
+ super.doFilter(servletRequest, servletResponse, chain);
+ }
+
+ /**
+ * If the authentication was successful, then an audit event is logged about the success
+ *
+ * @param servletRequest the request
+ * @param servletResponse the response
+ * @param authResult the Authentication result
+ * @throws IOException
+ */
+ @Override
+ protected void onSuccessfulAuthentication(HttpServletRequest servletRequest,
+ HttpServletResponse servletResponse,
+ Authentication authResult) throws IOException {
+ if (auditLogger.isEnabled()) {
+ AuditEvent loginSucceededAuditEvent = LoginAuditEvent.builder()
+ .withRemoteIp(RequestUtils.getRemoteAddress(servletRequest))
+ .withUserName(authResult.getName())
+ .withTimestamp(System.currentTimeMillis())
+ .withRoles(permissionHelper.getPermissionLabels(authResult))
+ .build();
+ auditLogger.log(loginSucceededAuditEvent);
+ }
+ }
+
+ /**
+ * In the case of invalid username or password, the authentication fails and it is logged
+ *
+ * @param servletRequest the request
+ * @param servletResponse the response
+ * @param authExecption the exception, if any, causing the unsuccessful authentication attempt
+ * @throws IOException
+ */
+ @Override
+ protected void onUnsuccessfulAuthentication(HttpServletRequest servletRequest,
+ HttpServletResponse servletResponse,
+ AuthenticationException authExecption) throws IOException {
+ String header = servletRequest.getHeader("Authorization");
+ String username = null;
+ try {
+ username = getUsernameFromAuth(header, getCredentialsCharset(servletRequest));
+ } catch (Exception e) {
+ LOG.warn("Error occurred during decoding authorization header.", e);
+ }
+ if (auditLogger.isEnabled()) {
+ AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
+ .withRemoteIp(RequestUtils.getRemoteAddress(servletRequest))
+ .withTimestamp(System.currentTimeMillis())
+ .withReasonOfFailure("Invalid username/password combination")
+ .withUserName(username)
+ .build();
+ auditLogger.log(loginFailedAuditEvent);
+ }
+ }
+
+ /**
+ * Helper function to decode Authorization header
+ *
+ * @param authenticationValue the authentication value to parse
+ * @param charSet the character set of the authentication value
+ * @return the username parsed from the authentication header value
+ * @throws IOException
+ */
+ private String getUsernameFromAuth(String authenticationValue, String charSet) throws IOException {
+ byte[] base64Token = authenticationValue.substring(6).getBytes("UTF-8");
+
+ byte[] decoded;
+ try {
+ decoded = Base64.decode(base64Token);
+ } catch (IllegalArgumentException ex) {
+ throw new BadCredentialsException("Failed to decode basic authentication token");
+ }
+
+ String token = new String(decoded, charSet);
+ int delimiter = token.indexOf(":");
+ if (delimiter == -1) {
+ throw new BadCredentialsException("Invalid basic authentication token");
+ } else {
+ return token.substring(0, delimiter);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariDelegatingAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariDelegatingAuthenticationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariDelegatingAuthenticationFilter.java
new file mode 100644
index 0000000..aab87a2
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariDelegatingAuthenticationFilter.java
@@ -0,0 +1,146 @@
+/*
+ * 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * The AmbariDelegatingAuthenticationFilter is an authentication filter that holds zero or more
+ * {@link org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter}s.
+ * <p>
+ * This container iterates though the contained Filters to delegate {@link Filter} operations.
+ * For {@link Filter#init(FilterConfig)} and {@link Filter#destroy()} operations, all contained filters
+ * will be called. For {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}, each
+ * filter will be accessed in order to test whether is should be used or skipped. Once a filter
+ * claims it is to be used for the operation, interation stops ensuring at most only one of the contained
+ * filters is invoked.
+ */
+public class AmbariDelegatingAuthenticationFilter implements Filter {
+ private static final Logger LOG = LoggerFactory.getLogger(AmbariDelegatingAuthenticationFilter.class);
+
+ /**
+ * The ordered collections of {@link org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter}s
+ */
+ private final Collection<AmbariAuthenticationFilter> filters;
+
+ /**
+ * Constructor.
+ *
+ * @param filters an ordered collections of {@link org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter}s
+ */
+ public AmbariDelegatingAuthenticationFilter(Collection<AmbariAuthenticationFilter> filters) {
+
+ this.filters = (filters == null) ? Collections.<AmbariAuthenticationFilter>emptyList() : filters;
+
+ if (this.filters.isEmpty()) {
+ LOG.warn("The delegated filters list is empty. No authentication tests will be performed by this " +
+ "authentication filter.");
+ } else if (LOG.isDebugEnabled()) {
+ StringBuffer filterNames = new StringBuffer();
+
+ for (AmbariAuthenticationFilter filter : this.filters) {
+ filterNames.append("\n\t");
+ filterNames.append(filter.getClass().getName());
+ }
+
+ LOG.debug("This authentication filter will attempt to authenticate a user using one of the " +
+ "following delegated authentication filters: {}",
+ filterNames);
+ }
+ }
+
+ /**
+ * Iterates over the contained {@link org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter}s
+ * to invoke each's init method.
+ *
+ * @param filterConfig a filter configuration
+ * @throws ServletException
+ */
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ for (AmbariAuthenticationFilter filter : filters) {
+ filter.init(filterConfig);
+ }
+ }
+
+ /**
+ * Iterates over the contained {@link org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter}s
+ * to test each to see if they should be invoked. If one tests positive, its doFilter method will be
+ * invoked and processing will stop ensuring only at most one of the contained filter is invoked.
+ *
+ * @param servletRequest the request
+ * @param servletResponse the response
+ * @param chain the Spring filter change
+ * @throws IOException
+ * @throws ServletException
+ */
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
+ throws IOException, ServletException {
+ boolean handled = false;
+ HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
+
+ for (AmbariAuthenticationFilter filter : filters) {
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("Attempting to apply authentication filter {}", filter.getClass().getName());
+ }
+
+ if (filter.shouldApply(httpServletRequest)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Using authentication filter {} since it applies", filter.getClass().getName());
+ }
+
+ filter.doFilter(servletRequest, servletResponse, chain);
+ handled = true;
+ break;
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Filter {} does not apply skipping", filter.getClass().getName());
+ }
+ }
+ }
+
+ if (!handled) {
+ LOG.debug("No delegated filters applied while attempting to authenticate a user, continuing with the filter chain.");
+ chain.doFilter(servletRequest, servletResponse);
+ }
+ }
+
+ /**
+ * Iterates over the contained {@link org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter}s
+ * to invoke each's destroy method.
+ */
+ @Override
+ public void destroy() {
+ for (AmbariAuthenticationFilter filter : filters) {
+ filter.destroy();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/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
new file mode 100644
index 0000000..2c15a38
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariJWTAuthenticationFilter.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.security.authentication;
+
+import org.apache.ambari.server.audit.AuditLogger;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.event.LoginAuditEvent;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.security.authorization.AuthorizationHelper;
+import org.apache.ambari.server.security.authorization.PermissionHelper;
+import org.apache.ambari.server.security.authorization.Users;
+import org.apache.ambari.server.security.authorization.jwt.AuthenticationJwtUserNotFoundException;
+import org.apache.ambari.server.security.authorization.jwt.JwtAuthenticationFilter;
+import org.apache.ambari.server.utils.RequestUtils;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 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 {
+
+ /**
+ * Audit logger
+ */
+ private AuditLogger auditLogger;
+
+ /**
+ * PermissionHelper to help create audit entries
+ */
+ private PermissionHelper permissionHelper;
+
+
+ /**
+ * Constructor.
+ *
+ * @param ambariEntryPoint the Spring entry point
+ * @param configuration the Ambari configuration
+ * @param users the Ambari users object
+ * @param auditLogger an Audit Logger
+ * @param permissionHelper a permission helper
+ */
+ public AmbariJWTAuthenticationFilter(AuthenticationEntryPoint ambariEntryPoint,
+ Configuration configuration,
+ Users users,
+ AuditLogger auditLogger,
+ PermissionHelper permissionHelper) {
+ super(configuration, ambariEntryPoint, users);
+ this.auditLogger = auditLogger;
+ this.permissionHelper = permissionHelper;
+ }
+
+ /**
+ * Checks whether the authentication information is filled. If it is not, then a login failed audit event is logged
+ *
+ * @param 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 {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
+
+ if (auditLogger.isEnabled() && shouldApply(httpServletRequest) && (AuthorizationHelper.getAuthenticatedName() == null)) {
+ AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
+ .withRemoteIp(RequestUtils.getRemoteAddress(httpServletRequest))
+ .withTimestamp(System.currentTimeMillis())
+ .withReasonOfFailure("Authentication required")
+ .withUserName(null)
+ .build();
+ auditLogger.log(loginFailedAuditEvent);
+ }
+
+ super.doFilter(servletRequest, servletResponse, chain);
+ }
+
+ @Override
+ protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException {
+ if (auditLogger.isEnabled()) {
+ AuditEvent loginSucceededAuditEvent = LoginAuditEvent.builder()
+ .withRemoteIp(RequestUtils.getRemoteAddress(request))
+ .withUserName(authResult.getName())
+ .withTimestamp(System.currentTimeMillis())
+ .withRoles(permissionHelper.getPermissionLabels(authResult))
+ .build();
+ auditLogger.log(loginSucceededAuditEvent);
+ }
+ }
+
+ @Override
+ protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
+ if (auditLogger.isEnabled()) {
+ String username = null;
+ if (authException instanceof AuthenticationJwtUserNotFoundException) {
+ username = ((AuthenticationJwtUserNotFoundException) authException).getUsername();
+ }
+
+ AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
+ .withRemoteIp(RequestUtils.getRemoteAddress(request))
+ .withTimestamp(System.currentTimeMillis())
+ .withReasonOfFailure(authException.getLocalizedMessage())
+ .withUserName(username)
+ .build();
+ auditLogger.log(loginFailedAuditEvent);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java
index 2f676b4..ac26a43 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java
@@ -18,7 +18,6 @@
package org.apache.ambari.server.security.authorization;
-import com.google.inject.Inject;
import org.apache.ambari.server.audit.event.AccessUnauthorizedAuditEvent;
import org.apache.ambari.server.audit.event.AuditEvent;
import org.apache.ambari.server.audit.AuditLogger;
@@ -26,11 +25,13 @@ import org.apache.ambari.server.audit.event.LoginAuditEvent;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.orm.entities.PermissionEntity;
import org.apache.ambari.server.orm.entities.PrivilegeEntity;
+import org.apache.ambari.server.security.AmbariEntryPoint;
import org.apache.ambari.server.security.authorization.internal.InternalAuthenticationToken;
import org.apache.ambari.server.utils.RequestUtils;
import org.apache.ambari.server.view.ViewRegistry;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
@@ -91,27 +92,56 @@ public class AmbariAuthorizationFilter implements Filter {
protected static final String LOGIN_REDIRECT_BASE = "/#/login?targetURI=";
/**
+ * The Ambari authentication entry point
+ */
+ private final AmbariEntryPoint entryPoint;
+
+ /**
* Access to Ambari configuration data
*/
- @Inject
- private Configuration configuration;
+ private final Configuration configuration;
/**
* Access to user information
*/
- @Inject
- private Users users;
+ private final Users users;
- @Inject
- private AuditLogger auditLogger;
+ /**
+ * The audit logger
+ */
+ private final AuditLogger auditLogger;
- @Inject PermissionHelper permissionHelper;
+ /**
+ * A Permission Helper used to provided inforamtion for the Audit Logger
+ */
+ private final PermissionHelper permissionHelper;
/**
* The realm to use for the basic http auth
*/
private String realm;
+ /**
+ * Constructor.
+ *
+ * @param entryPoint the authentication entrypoint
+ * @param configuration the Ambari configuration
+ * @param users Ambari user access
+ * @param auditLogger the Audit logger
+ * @param permissionHelper the permission helper
+ */
+ public AmbariAuthorizationFilter(AmbariEntryPoint entryPoint,
+ Configuration configuration,
+ Users users,
+ AuditLogger auditLogger,
+ PermissionHelper permissionHelper) {
+ this.entryPoint = entryPoint;
+ this.configuration = configuration;
+ this.users = users;
+ this.auditLogger = auditLogger;
+ this.permissionHelper = permissionHelper;
+ }
+
@Override
public void init(FilterConfig filterConfig) throws ServletException {
realm = getParameterValue(filterConfig, REALM_PARAM, DEFAULT_REALM);
@@ -160,8 +190,7 @@ public class AmbariAuthorizationFilter implements Filter {
String redirectURL = httpResponse.encodeRedirectURL(LOGIN_REDIRECT_BASE + requestedURL);
httpResponse.sendRedirect(redirectURL);
} else {
- httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Authentication required");
- httpResponse.flushBuffer();
+ entryPoint.commence(httpRequest, httpResponse, new AuthenticationCredentialsNotFoundException("Missing authentication token"));
}
return;
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/AuthenticationJwtUserNotFoundException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/AuthenticationJwtUserNotFoundException.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/AuthenticationJwtUserNotFoundException.java
new file mode 100644
index 0000000..f18af10
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/jwt/AuthenticationJwtUserNotFoundException.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.springframework.security.core.AuthenticationException;
+
+/**
+ * AuthenticationJwtUserNotFoundException is an AuthenticationException implementation to be thrown
+ * when the user specified in a JTW token is not found in the Ambari user database.
+ */
+public class AuthenticationJwtUserNotFoundException extends AuthenticationException {
+ private final String username;
+
+ public AuthenticationJwtUserNotFoundException(String username, String message) {
+ super(message);
+ this.username = username;
+ }
+
+ public AuthenticationJwtUserNotFoundException(String username, String message, Throwable throwable) {
+ super(message, throwable);
+ this.username = username;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/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
index 760621e..890326f 100644
--- 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
@@ -17,13 +17,13 @@
*/
package org.apache.ambari.server.security.authorization.jwt;
-import com.google.inject.Inject;
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;
import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.security.authentication.AmbariAuthenticationFilter;
import org.apache.ambari.server.security.authorization.AmbariGrantedAuthority;
import org.apache.ambari.server.security.authorization.User;
import org.apache.ambari.server.security.authorization.UserType;
@@ -34,6 +34,7 @@ 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;
@@ -52,8 +53,8 @@ import java.util.List;
* 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 Filter {
- Logger LOG = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
+public class JwtAuthenticationFilter implements AmbariAuthenticationFilter {
+ private static final Logger LOG = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
private final JwtAuthenticationProperties jwtProperties;
@@ -67,7 +68,6 @@ public class JwtAuthenticationFilter implements Filter {
private AuthenticationEntryPoint entryPoint;
private Users users;
- @Inject
public JwtAuthenticationFilter(Configuration configuration, AuthenticationEntryPoint entryPoint, Users users) {
this.entryPoint = entryPoint;
this.users = users;
@@ -83,6 +83,28 @@ public class JwtAuthenticationFilter implements Filter {
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 {
@@ -91,7 +113,7 @@ public class JwtAuthenticationFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
- if(jwtProperties == null){
+ if (jwtProperties == null) {
//disable filter if not configured
filterChain.doFilter(servletRequest, servletResponse);
return;
@@ -100,71 +122,68 @@ public class JwtAuthenticationFilter implements Filter {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
- String serializedJWT = getJWTFromCookie(httpServletRequest);
- if (serializedJWT != null && isAuthenticationRequired(serializedJWT)) {
- SignedJWT jwtToken;
- try {
- jwtToken = SignedJWT.parse(serializedJWT);
-
- boolean valid = validateToken(jwtToken);
-
- if (valid) {
- String userName = jwtToken.getJWTClaimsSet().getSubject();
- User user = users.getUser(userName, UserType.JWT);
- //fixme temporary solution for LDAP username conflicts, auth ldap users via JWT
- if (user == null) {
- user = users.getUser(userName, UserType.LDAP);
- }
-
- if (user == null) {
+ try {
+ String serializedJWT = getJWTFromCookie(httpServletRequest);
+ if (serializedJWT != null && isAuthenticationRequired(serializedJWT)) {
+ try {
+ SignedJWT jwtToken = SignedJWT.parse(serializedJWT);
- //TODO this is temporary check for conflicts, until /users API will change to use user_id instead of name as PK
- User existingUser = users.getUser(userName, UserType.LOCAL);
- if (existingUser != null) {
+ boolean valid = validateToken(jwtToken);
- LOG.error("Access for JWT user [{}] restricted. Detected conflict with local user ", userName);
+ if (valid) {
+ String userName = jwtToken.getJWTClaimsSet().getSubject();
+ User user = users.getUser(userName, UserType.JWT);
+ //fixme temporary solution for LDAP username conflicts, auth ldap users via JWT
+ if (user == null) {
+ user = users.getUser(userName, UserType.LDAP);
}
- //TODO we temporary expect that LDAP is configured to same server as JWT source
- httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
- "Cannot find user from JWT. Please, ensure LDAP is configured and users are synced.");
-
- //interrupt filter chain
- return;
- }
-
- Collection<AmbariGrantedAuthority> userAuthorities =
- users.getUserAuthorities(user.getUserName(), user.getUserType());
-
- JwtAuthentication authentication = new JwtAuthentication(serializedJWT, user, userAuthorities);
- authentication.setAuthenticated(true);
+ if (user == null) {
+ //TODO this is temporary check for conflicts, until /users API will change to use user_id instead of name as PK
+ User existingUser = users.getUser(userName, UserType.LOCAL);
+ if (existingUser != null) {
+ LOG.error("Access for JWT user [{}] restricted. Detected conflict with local user ", userName);
+ }
- SecurityContextHolder.getContext().setAuthentication(authentication);
+ //TODO we temporary expect that LDAP is configured to same server as JWT source
+ throw new AuthenticationJwtUserNotFoundException(userName, "Cannot find user from JWT. Please, ensure LDAP is configured and users are synced.");
+ }
+ Collection<AmbariGrantedAuthority> userAuthorities =
+ users.getUserAuthorities(user.getUserName(), user.getUserType());
- } else {
- //clear security context if authentication was required, but failed
- SecurityContextHolder.clearContext();
+ JwtAuthentication authentication = new JwtAuthentication(serializedJWT, user, userAuthorities);
+ authentication.setAuthenticated(true);
- LOG.warn("JWT authentication failed");
- if (ignoreFailure) {
- filterChain.doFilter(servletRequest, servletResponse);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ onSuccessfulAuthentication(httpServletRequest, httpServletResponse, authentication);
} else {
- //used to indicate authentication failure, not used here as we have more than one filter
- entryPoint.commence(httpServletRequest, httpServletResponse, new BadCredentialsException("Invalid JWT " +
- "token"));
+ 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();
- } catch (ParseException e) {
- LOG.warn("Unable to parse the JWT token", e);
+ 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);
}
- } else {
- LOG.trace("No JWT cookie found, do nothing");
}
-
- filterChain.doFilter(servletRequest, servletResponse);
}
private void loadJwtProperties() {
@@ -179,18 +198,19 @@ public class JwtAuthenticationFilter implements Filter {
/**
* 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() ){
+ if (existingAuth == null || !existingAuth.isAuthenticated()) {
return true;
}
//revalidate if token was changed
- if(existingAuth instanceof JwtAuthentication && !StringUtils.equals(token, (String) existingAuth.getCredentials())){
+ if (existingAuth instanceof JwtAuthentication && !StringUtils.equals(token, (String) existingAuth.getCredentials())) {
return true;
}
@@ -224,6 +244,7 @@ public class JwtAuthenticationFilter implements Filter {
}
return serializedJWT;
}
+
/**
* Create the URL to be used for authentication of the user in the absence of
* a JWT token within the incoming request.
@@ -304,8 +325,7 @@ public class JwtAuthenticationFilter implements Filter {
* 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
+ * @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) {
@@ -366,6 +386,30 @@ public class JwtAuthenticationFilter implements Filter {
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/d5cca62c/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml b/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
index e1697e2..a86973c 100644
--- a/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
+++ b/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
@@ -26,8 +26,7 @@
disable-url-rewriting="true" entry-point-ref="ambariEntryPoint">
<intercept-url pattern="/**" access="isAuthenticated()"/>
<custom-filter ref="ambariUserAuthorizationFilter" before="BASIC_AUTH_FILTER"/>
- <custom-filter ref="ambariAuthenticationFilter" position="BASIC_AUTH_FILTER"/>
- <custom-filter ref="ambariJwtAuthenticationFilter" after="BASIC_AUTH_FILTER" />
+ <custom-filter ref="ambariDelegatingAuthenticationFilter" position="BASIC_AUTH_FILTER"/>
<custom-filter ref="ambariAuthorizationFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
</http>
@@ -46,10 +45,36 @@
<beans:bean id="ambariEntryPoint" class="org.apache.ambari.server.security.AmbariEntryPoint">
</beans:bean>
- <beans:bean id="ambariAuthenticationFilter" class="org.apache.ambari.server.security.authentication.AmbariAuthenticationFilter">
+ <beans:bean id="ambariDelegatingAuthenticationFilter" class="org.apache.ambari.server.security.authentication.AmbariDelegatingAuthenticationFilter">
+ <beans:constructor-arg>
+ <beans:list>
+ <beans:ref bean="ambariBasicAuthenticationFilter"/>
+ <beans:ref bean="ambariJwtAuthenticationFilter"/>
+ </beans:list>
+ </beans:constructor-arg>
+ </beans:bean>
+
+ <beans:bean id="ambariBasicAuthenticationFilter" class="org.apache.ambari.server.security.authentication.AmbariBasicAuthenticationFilter">
<beans:constructor-arg ref="authenticationManager"/>
+ <beans:constructor-arg ref="ambariEntryPoint"/>
<beans:constructor-arg ref="auditLogger"/>
<beans:constructor-arg ref="permissionHelper"/>
+ </beans:bean>
+
+ <beans:bean id="ambariJwtAuthenticationFilter" class="org.apache.ambari.server.security.authentication.AmbariJWTAuthenticationFilter">
<beans:constructor-arg ref="ambariEntryPoint"/>
+ <beans:constructor-arg ref="ambariConfiguration"/>
+ <beans:constructor-arg ref="ambariUsers"/>
+ <beans:constructor-arg ref="auditLogger"/>
+ <beans:constructor-arg ref="permissionHelper"/>
</beans:bean>
+
+ <beans:bean id="ambariAuthorizationFilter" class="org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter">
+ <beans:constructor-arg ref="ambariEntryPoint"/>
+ <beans:constructor-arg ref="ambariConfiguration"/>
+ <beans:constructor-arg ref="ambariUsers"/>
+ <beans:constructor-arg ref="auditLogger"/>
+ <beans:constructor-arg ref="permissionHelper"/>
+ </beans:bean>
+
</beans:beans>
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationFilterTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationFilterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationFilterTest.java
deleted file mode 100644
index 0f2b104..0000000
--- a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationFilterTest.java
+++ /dev/null
@@ -1,139 +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 java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.ambari.server.audit.event.AuditEvent;
-import org.apache.ambari.server.audit.AuditLogger;
-import org.apache.ambari.server.security.AmbariEntryPoint;
-import org.apache.ambari.server.security.authorization.AuthorizationHelper;
-import org.apache.ambari.server.security.authorization.PermissionHelper;
-import org.junit.runner.RunWith;
-import org.powermock.api.easymock.PowerMock;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
-import org.springframework.security.crypto.codec.Base64;
-
-@RunWith(PowerMockRunner.class)
-@PrepareForTest(AuthorizationHelper.class)
-public class AmbariAuthenticationFilterTest {
-
- private AmbariAuthenticationFilter underTest;
-
- private AuditLogger mockedAuditLogger;
-
- private PermissionHelper permissionHelper;
-
- private AmbariEntryPoint entryPoint;
-
- @Before
- public void setUp() {
- mockedAuditLogger = createMock(AuditLogger.class);
- permissionHelper = createMock(PermissionHelper.class);
- entryPoint = createMock(AmbariEntryPoint.class);
- underTest = new AmbariAuthenticationFilter(null, mockedAuditLogger, permissionHelper, entryPoint);
- replay(entryPoint);
- }
-
- @Test
- public void testDoFilter() throws IOException, ServletException {
- // GIVEN
- HttpServletRequest request = createMock(HttpServletRequest.class);
- HttpServletResponse response = createMock(HttpServletResponse.class);
- FilterChain filterChain = createMock(FilterChain.class);
- expect(request.getHeader("Authorization")).andReturn("header").andReturn(null);
- expect(request.getHeader("X-Forwarded-For")).andReturn("1.2.3.4");
- expect(mockedAuditLogger.isEnabled()).andReturn(true);
- mockedAuditLogger.log(anyObject(AuditEvent.class));
- expectLastCall().times(1);
- filterChain.doFilter(request, response);
- expectLastCall();
- replay(mockedAuditLogger, request, filterChain);
- // WHEN
- underTest.doFilter(request, response, filterChain);
- // THEN
- verify(mockedAuditLogger, request, filterChain);
- }
-
- @Test
- public void testOnSuccessfulAuthentication() throws IOException, ServletException {
- // GIVEN
- HttpServletRequest request = createMock(HttpServletRequest.class);
- HttpServletResponse response = createMock(HttpServletResponse.class);
- Authentication authentication = createMock(Authentication.class);
- PowerMock.mockStatic(AuthorizationHelper.class);
-
- Map<String, List<String>> roles = new HashMap<>();
- roles.put("a", Arrays.asList("r1", "r2", "r3"));
- expect(permissionHelper.getPermissionLabels(authentication))
- .andReturn(roles);
- expect(AuthorizationHelper.getAuthorizationNames(authentication))
- .andReturn(Arrays.asList("perm1", "perm2"));
- expect(AuthorizationHelper.getAuthenticatedName()).andReturn("perm1");
- expect(request.getHeader("X-Forwarded-For")).andReturn("1.2.3.4");
- expect(authentication.getName()).andReturn("admin");
- expect(mockedAuditLogger.isEnabled()).andReturn(true);
- mockedAuditLogger.log(anyObject(AuditEvent.class));
- expectLastCall().times(1);
- replay(mockedAuditLogger, request, authentication, permissionHelper);
- PowerMock.replayAll();
- // WHEN
- underTest.onSuccessfulAuthentication(request, response, authentication);
- // THEN
- verify(mockedAuditLogger, request);
- }
-
- @Test
- public void testOnUnsuccessfulAuthentication() throws IOException, ServletException {
- // GIVEN
- HttpServletRequest request = createMock(HttpServletRequest.class);
- HttpServletResponse response = createMock(HttpServletResponse.class);
- AuthenticationException authEx = createMock(AuthenticationException.class);
- expect(request.getHeader("X-Forwarded-For")).andReturn("1.2.3.4");
- expect(request.getHeader("Authorization")).andReturn(
- "Basic " + new String(Base64.encode("admin:admin".getBytes("UTF-8"))));
- expect(mockedAuditLogger.isEnabled()).andReturn(true);
- mockedAuditLogger.log(anyObject(AuditEvent.class));
- expectLastCall().times(1);
- replay(mockedAuditLogger, request, authEx);
- // WHEN
- underTest.onUnsuccessfulAuthentication(request, response, authEx);
- // THEN
- verify(mockedAuditLogger, request, authEx);
- }
-}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java
new file mode 100644
index 0000000..7344b62
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariBasicAuthenticationFilterTest.java
@@ -0,0 +1,139 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.security.authentication;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.ambari.server.audit.event.AuditEvent;
+import org.apache.ambari.server.audit.AuditLogger;
+import org.apache.ambari.server.security.AmbariEntryPoint;
+import org.apache.ambari.server.security.authorization.AuthorizationHelper;
+import org.apache.ambari.server.security.authorization.PermissionHelper;
+import org.junit.runner.RunWith;
+import org.powermock.api.easymock.PowerMock;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import org.springframework.security.crypto.codec.Base64;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(AuthorizationHelper.class)
+public class AmbariBasicAuthenticationFilterTest {
+
+ private AmbariBasicAuthenticationFilter underTest;
+
+ private AuditLogger mockedAuditLogger;
+
+ private PermissionHelper permissionHelper;
+
+ private AmbariEntryPoint entryPoint;
+
+ @Before
+ public void setUp() {
+ mockedAuditLogger = createMock(AuditLogger.class);
+ permissionHelper = createMock(PermissionHelper.class);
+ entryPoint = createMock(AmbariEntryPoint.class);
+ underTest = new AmbariBasicAuthenticationFilter(null, entryPoint, mockedAuditLogger, permissionHelper);
+ replay(entryPoint);
+ }
+
+ @Test
+ public void testDoFilter() throws IOException, ServletException {
+ // GIVEN
+ HttpServletRequest request = createMock(HttpServletRequest.class);
+ HttpServletResponse response = createMock(HttpServletResponse.class);
+ FilterChain filterChain = createMock(FilterChain.class);
+ expect(request.getHeader("Authorization")).andReturn("Basic ").andReturn(null);
+ expect(request.getHeader("X-Forwarded-For")).andReturn("1.2.3.4");
+ expect(mockedAuditLogger.isEnabled()).andReturn(true);
+ mockedAuditLogger.log(anyObject(AuditEvent.class));
+ expectLastCall().times(1);
+ filterChain.doFilter(request, response);
+ expectLastCall();
+ replay(mockedAuditLogger, request, filterChain);
+ // WHEN
+ underTest.doFilter(request, response, filterChain);
+ // THEN
+ verify(mockedAuditLogger, request, filterChain);
+ }
+
+ @Test
+ public void testOnSuccessfulAuthentication() throws IOException, ServletException {
+ // GIVEN
+ HttpServletRequest request = createMock(HttpServletRequest.class);
+ HttpServletResponse response = createMock(HttpServletResponse.class);
+ Authentication authentication = createMock(Authentication.class);
+ PowerMock.mockStatic(AuthorizationHelper.class);
+
+ Map<String, List<String>> roles = new HashMap<>();
+ roles.put("a", Arrays.asList("r1", "r2", "r3"));
+ expect(permissionHelper.getPermissionLabels(authentication))
+ .andReturn(roles);
+ expect(AuthorizationHelper.getAuthorizationNames(authentication))
+ .andReturn(Arrays.asList("perm1", "perm2"));
+ expect(AuthorizationHelper.getAuthenticatedName()).andReturn("perm1");
+ expect(request.getHeader("X-Forwarded-For")).andReturn("1.2.3.4");
+ expect(authentication.getName()).andReturn("admin");
+ expect(mockedAuditLogger.isEnabled()).andReturn(true);
+ mockedAuditLogger.log(anyObject(AuditEvent.class));
+ expectLastCall().times(1);
+ replay(mockedAuditLogger, request, authentication, permissionHelper);
+ PowerMock.replayAll();
+ // WHEN
+ underTest.onSuccessfulAuthentication(request, response, authentication);
+ // THEN
+ verify(mockedAuditLogger, request);
+ }
+
+ @Test
+ public void testOnUnsuccessfulAuthentication() throws IOException, ServletException {
+ // GIVEN
+ HttpServletRequest request = createMock(HttpServletRequest.class);
+ HttpServletResponse response = createMock(HttpServletResponse.class);
+ AuthenticationException authEx = createMock(AuthenticationException.class);
+ expect(request.getHeader("X-Forwarded-For")).andReturn("1.2.3.4");
+ expect(request.getHeader("Authorization")).andReturn(
+ "Basic " + new String(Base64.encode("admin:admin".getBytes("UTF-8"))));
+ expect(mockedAuditLogger.isEnabled()).andReturn(true);
+ mockedAuditLogger.log(anyObject(AuditEvent.class));
+ expectLastCall().times(1);
+ replay(mockedAuditLogger, request, authEx);
+ // WHEN
+ underTest.onUnsuccessfulAuthentication(request, response, authEx);
+ // THEN
+ verify(mockedAuditLogger, request, authEx);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d5cca62c/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariDelegatingAuthenticationFilterTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariDelegatingAuthenticationFilterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariDelegatingAuthenticationFilterTest.java
new file mode 100644
index 0000000..d775136
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariDelegatingAuthenticationFilterTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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 org.easymock.EasyMockSupport;
+import org.junit.Test;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Arrays;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+
+public class AmbariDelegatingAuthenticationFilterTest extends EasyMockSupport {
+ @Test
+ public void testInit() throws Exception {
+ FilterConfig filterConfig = createMock(FilterConfig.class);
+
+ AmbariAuthenticationFilter filter1 = createMock(AmbariAuthenticationFilter.class);
+ filter1.init(filterConfig);
+ expectLastCall().once();
+
+ AmbariAuthenticationFilter filter2 = createMock(AmbariAuthenticationFilter.class);
+ filter2.init(filterConfig);
+ expectLastCall().once();
+
+ AmbariAuthenticationFilter filter3 = createMock(AmbariAuthenticationFilter.class);
+ filter3.init(filterConfig);
+ expectLastCall().once();
+
+ replayAll();
+
+ Filter filter = new AmbariDelegatingAuthenticationFilter(Arrays.asList(filter1, filter2, filter3));
+ filter.init(filterConfig);
+
+ verifyAll();
+ }
+
+ @Test
+ public void testDoFilterNoneApply() throws Exception {
+ HttpServletRequest httpServletRequest = createMock(HttpServletRequest.class);
+ HttpServletResponse httpServletResponse = createMock(HttpServletResponse.class);
+
+ AmbariAuthenticationFilter filter1 = createMock(AmbariAuthenticationFilter.class);
+ expect(filter1.shouldApply(httpServletRequest)).andReturn(false).once();
+
+ AmbariAuthenticationFilter filter2 = createMock(AmbariAuthenticationFilter.class);
+ expect(filter2.shouldApply(httpServletRequest)).andReturn(false).once();
+
+ AmbariAuthenticationFilter filter3 = createMock(AmbariAuthenticationFilter.class);
+ expect(filter3.shouldApply(httpServletRequest)).andReturn(false).once();
+
+ FilterChain filterChain = createMock(FilterChain.class);
+ filterChain.doFilter(httpServletRequest, httpServletResponse);
+ expectLastCall().once();
+
+ replayAll();
+
+ Filter filter = new AmbariDelegatingAuthenticationFilter(Arrays.asList(filter1, filter2, filter3));
+ filter.doFilter(httpServletRequest, httpServletResponse, filterChain);
+
+ verifyAll();
+ }
+
+ @Test
+ public void testDoFilterFirstApplies() throws Exception {
+ HttpServletRequest httpServletRequest = createMock(HttpServletRequest.class);
+ HttpServletResponse httpServletResponse = createMock(HttpServletResponse.class);
+
+ FilterChain filterChain = createMock(FilterChain.class);
+
+ AmbariAuthenticationFilter filter1 = createMock(AmbariAuthenticationFilter.class);
+ expect(filter1.shouldApply(httpServletRequest)).andReturn(true).once();
+ filter1.doFilter(httpServletRequest, httpServletResponse, filterChain);
+ expectLastCall().once();
+
+ AmbariAuthenticationFilter filter2 = createMock(AmbariAuthenticationFilter.class);
+
+ AmbariAuthenticationFilter filter3 = createMock(AmbariAuthenticationFilter.class);
+
+ replayAll();
+
+ Filter filter = new AmbariDelegatingAuthenticationFilter(Arrays.asList(filter1, filter2, filter3));
+ filter.doFilter(httpServletRequest, httpServletResponse, filterChain);
+
+ verifyAll();
+ }
+
+ @Test
+ public void testDoFilterLastApplies() throws Exception {
+ HttpServletRequest httpServletRequest = createMock(HttpServletRequest.class);
+ HttpServletResponse httpServletResponse = createMock(HttpServletResponse.class);
+
+ FilterChain filterChain = createMock(FilterChain.class);
+
+ AmbariAuthenticationFilter filter1 = createMock(AmbariAuthenticationFilter.class);
+ expect(filter1.shouldApply(httpServletRequest)).andReturn(false).once();
+
+ AmbariAuthenticationFilter filter2 = createMock(AmbariAuthenticationFilter.class);
+ expect(filter2.shouldApply(httpServletRequest)).andReturn(false).once();
+
+ AmbariAuthenticationFilter filter3 = createMock(AmbariAuthenticationFilter.class);
+ expect(filter3.shouldApply(httpServletRequest)).andReturn(true).once();
+ filter3.doFilter(httpServletRequest, httpServletResponse, filterChain);
+ expectLastCall().once();
+
+ replayAll();
+
+ Filter filter = new AmbariDelegatingAuthenticationFilter(Arrays.asList(filter1, filter2, filter3));
+ filter.doFilter(httpServletRequest, httpServletResponse, filterChain);
+
+ verifyAll();
+ }
+
+ @Test
+ public void testDoFilterNthApplies() throws Exception {
+ HttpServletRequest httpServletRequest = createMock(HttpServletRequest.class);
+ HttpServletResponse httpServletResponse = createMock(HttpServletResponse.class);
+
+ FilterChain filterChain = createMock(FilterChain.class);
+
+ AmbariAuthenticationFilter filter1 = createMock(AmbariAuthenticationFilter.class);
+ expect(filter1.shouldApply(httpServletRequest)).andReturn(false).once();
+
+ AmbariAuthenticationFilter filter2 = createMock(AmbariAuthenticationFilter.class);
+ expect(filter2.shouldApply(httpServletRequest)).andReturn(false).once();
+
+ AmbariAuthenticationFilter filterN = createMock(AmbariAuthenticationFilter.class);
+ expect(filterN.shouldApply(httpServletRequest)).andReturn(true).once();
+ filterN.doFilter(httpServletRequest, httpServletResponse, filterChain);
+ expectLastCall().once();
+
+ AmbariAuthenticationFilter filter3 = createMock(AmbariAuthenticationFilter.class);
+
+ replayAll();
+
+ Filter filter = new AmbariDelegatingAuthenticationFilter(Arrays.asList(filter1, filter2, filterN, filter3));
+ filter.doFilter(httpServletRequest, httpServletResponse, filterChain);
+
+ verifyAll();
+ }
+
+ @Test
+ public void testDestroy() throws Exception {
+ AmbariAuthenticationFilter filter1 = createMock(AmbariAuthenticationFilter.class);
+ filter1.destroy();
+ expectLastCall().once();
+
+ AmbariAuthenticationFilter filter2 = createMock(AmbariAuthenticationFilter.class);
+ filter2.destroy();
+ expectLastCall().once();
+
+ AmbariAuthenticationFilter filter3 = createMock(AmbariAuthenticationFilter.class);
+ filter3.destroy();
+ expectLastCall().once();
+
+ replayAll();
+
+ Filter filter = new AmbariDelegatingAuthenticationFilter(Arrays.asList(filter1, filter2, filter3));
+ filter.destroy();
+
+ verifyAll();
+
+ }
+
+}
\ No newline at end of file