1919
2020import org .apache .knox .gateway .security .GroupPrincipal ;
2121import org .apache .knox .gateway .security .PrimaryPrincipal ;
22+ import org .apache .knox .gateway .services .GatewayServices ;
23+ import org .apache .knox .gateway .services .ServiceType ;
24+ import org .apache .knox .gateway .services .security .KeystoreService ;
25+ import org .apache .knox .gateway .services .security .KeystoreServiceException ;
2226import org .apache .knox .test .mock .MockServletContext ;
2327import org .easymock .EasyMock ;
2428import org .junit .Before ;
2832import javax .security .auth .Subject ;
2933import javax .servlet .FilterChain ;
3034import javax .servlet .FilterConfig ;
35+ import javax .servlet .ServletContext ;
3136import javax .servlet .ServletException ;
3237import javax .servlet .ServletRequest ;
3338import javax .servlet .ServletResponse ;
4550import java .util .List ;
4651import java .util .Map ;
4752import java .util .Set ;
53+ import java .security .KeyStore ;
4854
4955import static org .junit .Assert .assertEquals ;
5056import static org .junit .Assert .assertFalse ;
5157import static org .junit .Assert .assertTrue ;
5258
59+ @ SuppressWarnings ("PMD.JUnit4TestShouldUseBeforeAnnotation" )
5360public class RemoteAuthFilterTest {
5461
5562 public static final String BEARER_INVALID_TOKEN = "Bearer invalid-token" ;
5663 public static final String BEARER_VALID_TOKEN = "Bearer valid-token" ;
57- public static final String URL_SUCCESS = "http ://example.com/auth" ;
58- private static final String URL_FAIL = "http ://example.com/authfail" ;
64+ public static final String URL_SUCCESS = "https ://example.com/auth" ;
65+ private static final String URL_FAIL = "https ://example.com/authfail" ;
5966 public static final String X_AUTHENTICATED_USER = "X-Authenticated-User" ;
6067 public static final String X_AUTHENTICATED_GROUP = "X-Authenticated-Group" ;
6168 public static final String X_AUTHENTICATED_GROUP_2 = "X-Authenticated-Group-2" ;
@@ -65,22 +72,52 @@ public class RemoteAuthFilterTest {
6572 private HttpServletRequest requestMock ;
6673 private HttpServletResponse responseMock ;
6774 private TestFilterChain chainMock ;
75+ private GatewayServices gatewayServicesMock ;
76+ private KeystoreService keystoreServiceMock ;
77+ private ServletContext servletContextMock ;
78+
6879 @ Before
69- public void setUp () {
70- FilterConfig filterConfigMock = EasyMock .createNiceMock (FilterConfig .class );
80+ public void createMocks () {
7181 requestMock = EasyMock .createMock (HttpServletRequest .class );
7282 responseMock = EasyMock .createMock (HttpServletResponse .class );
83+ }
84+
85+ private void setUp (String trustStorePath , String trustStorePass , String trustStoreType ) {
86+ // Reset existing mocks
87+ EasyMock .reset (requestMock , responseMock );
88+
89+ FilterConfig filterConfigMock = EasyMock .createNiceMock (FilterConfig .class );
7390 chainMock = new TestFilterChain ();
7491
75- EasyMock .expect (filterConfigMock .getInitParameter ("remote.auth.url" )).andReturn ("http://example.com/auth" ).anyTimes ();
76- EasyMock .expect (filterConfigMock .getInitParameter ("remote.auth.include.headers" )).andReturn ("Authorization" ).anyTimes ();
77- EasyMock .expect (filterConfigMock .getInitParameter ("remote.auth.cache.key" )).andReturn ("Authorization" ).anyTimes ();
78- EasyMock .expect (filterConfigMock .getInitParameter ("remote.auth.expire.after" )).andReturn ("5" ).anyTimes ();
79- EasyMock .expect (filterConfigMock .getInitParameter ("remote.auth.user.header" )).andReturn (X_AUTHENTICATED_USER ).anyTimes ();
80- EasyMock .expect (filterConfigMock .getInitParameter ("remote.auth.group.header" ))
92+ // Create and configure Gateway Services mocks
93+ gatewayServicesMock = EasyMock .createNiceMock (GatewayServices .class );
94+ keystoreServiceMock = EasyMock .createNiceMock (KeystoreService .class );
95+ servletContextMock = EasyMock .createNiceMock (ServletContext .class );
96+
97+ // Set up Gateway Services expectations
98+ EasyMock .expect (gatewayServicesMock .getService (ServiceType .KEYSTORE_SERVICE ))
99+ .andReturn (keystoreServiceMock )
100+ .anyTimes ();
101+ EasyMock .expect (servletContextMock .getAttribute (GatewayServices .GATEWAY_SERVICES_ATTRIBUTE ))
102+ .andReturn (gatewayServicesMock )
103+ .anyTimes ();
104+
105+ // Basic config
106+ EasyMock .expect (filterConfigMock .getInitParameter (RemoteAuthFilter .CONFIG_REMOTE_AUTH_URL )).andReturn ("https://example.com/auth" ).anyTimes ();
107+ EasyMock .expect (filterConfigMock .getInitParameter (RemoteAuthFilter .CONFIG_INCLUDE_HEADERS )).andReturn ("Authorization" ).anyTimes ();
108+ EasyMock .expect (filterConfigMock .getInitParameter (RemoteAuthFilter .DEFAULT_CACHE_KEY_HEADER )).andReturn ("Authorization" ).anyTimes ();
109+ EasyMock .expect (filterConfigMock .getInitParameter (RemoteAuthFilter .CONFIG_EXPIRE_AFTER )).andReturn ("5" ).anyTimes ();
110+ EasyMock .expect (filterConfigMock .getInitParameter (RemoteAuthFilter .CONFIG_USER_HEADER )).andReturn (X_AUTHENTICATED_USER ).anyTimes ();
111+ EasyMock .expect (filterConfigMock .getInitParameter (RemoteAuthFilter .CONFIG_GROUP_HEADER ))
81112 .andReturn (X_AUTHENTICATED_GROUP + "," + X_AUTHENTICATED_GROUP_2 + ",X-Custom-Group-*" ).anyTimes ();
82113
83- EasyMock .replay (filterConfigMock );
114+ // Trust store config
115+ EasyMock .expect (filterConfigMock .getInitParameter (RemoteAuthFilter .CONFIG_TRUSTSTORE_PATH )).andReturn (trustStorePath ).anyTimes ();
116+ EasyMock .expect (filterConfigMock .getInitParameter (RemoteAuthFilter .CONFIG_TRUSTSTORE_PASSWORD )).andReturn (trustStorePass ).anyTimes ();
117+ EasyMock .expect (filterConfigMock .getInitParameter (RemoteAuthFilter .CONFIG_TRUSTSTORE_TYPE )).andReturn (trustStoreType ).anyTimes ();
118+
119+ // Only replay the mocks that won't need additional expectations
120+ EasyMock .replay (filterConfigMock , gatewayServicesMock , servletContextMock );
84121
85122 filter = new RemoteAuthFilter ();
86123 try {
@@ -90,6 +127,11 @@ public void setUp() {
90127 }
91128 }
92129
130+ // Default setup method for backward compatibility
131+ private void setUp () {
132+ setUp (null , null , null );
133+ }
134+
93135 private void setupURLConnection (String url ) {
94136 try {
95137 filter .httpURLConnection = new MockHttpURLConnection (new URL (url ));
@@ -100,6 +142,8 @@ private void setupURLConnection(String url) {
100142
101143 @ Test
102144 public void successfulAuthentication () throws Exception {
145+ setUp ();
146+
103147 EasyMock .expect (requestMock .getServletContext ()).andReturn (new MockServletContext ()).anyTimes ();
104148 EasyMock .expect (requestMock .getHeader ("Authorization" )).andReturn (BEARER_VALID_TOKEN ).anyTimes ();
105149 EasyMock .expect (responseMock .getStatus ()).andReturn (200 ).anyTimes ();
@@ -134,6 +178,8 @@ public void successfulAuthentication() throws Exception {
134178
135179 @ Test
136180 public void authenticationFailsWithInvalidToken () throws Exception {
181+ setUp ();
182+
137183 EasyMock .expect (requestMock .getServletContext ()).andReturn (new MockServletContext ()).anyTimes ();
138184 EasyMock .expect (responseMock .getStatus ()).andReturn (401 ).anyTimes ();
139185 EasyMock .expect (requestMock .getHeader ("Authorization" )).andReturn (BEARER_INVALID_TOKEN ).anyTimes ();
@@ -160,11 +206,12 @@ public void authenticationFailsWithInvalidToken() throws Exception {
160206
161207 @ Test
162208 public void testCacheBehavior () throws Exception {
209+ setUp ();
210+
163211 String principalName = "lmccayiv" ;
164212 String groupNames = "admin2,scientists" ;
165213 Subject subject = new Subject ();
166214 subject .getPrincipals ().add (new PrimaryPrincipal (principalName ));
167- // Add groups to the principal if available
168215 Arrays .stream (groupNames .split ("," )).forEach (groupName -> subject .getPrincipals ()
169216 .add (new GroupPrincipal (groupName )));
170217 filter .setCachedSubject (BEARER_VALID_TOKEN , subject );
@@ -202,9 +249,10 @@ public void testCacheBehavior() throws Exception {
202249
203250 @ Test
204251 public void testTraceIdPropagation () throws Exception {
252+ setUp ();
253+
205254 String expectedTraceId = "test-trace-123" ;
206255
207- // Set up mocks
208256 EasyMock .expect (requestMock .getServletContext ())
209257 .andReturn (new MockServletContext ())
210258 .anyTimes ();
@@ -250,6 +298,8 @@ public String getRequestProperty(String key) {
250298
251299 @ Test
252300 public void successfulAuthenticationWithMultipleGroups () throws Exception {
301+ setUp ();
302+
253303 EasyMock .expect (requestMock .getServletContext ()).andReturn (new MockServletContext ()).anyTimes ();
254304 EasyMock .expect (requestMock .getHeader ("Authorization" )).andReturn (BEARER_VALID_TOKEN ).anyTimes ();
255305 EasyMock .expect (responseMock .getStatus ()).andReturn (200 ).anyTimes ();
@@ -287,6 +337,95 @@ public void successfulAuthenticationWithMultipleGroups() throws Exception {
287337 }
288338 }
289339
340+ @ Test
341+ public void testSuccessfulHttpsRequestWithTrustStore () throws Exception {
342+ // Setup with valid trust store configuration
343+ setUp ("/path/to/truststore.jks" , "trustpass" , "JKS" );
344+
345+ KeyStore testTruststore = KeyStore .getInstance ("JKS" );
346+ EasyMock .expect (keystoreServiceMock .loadTruststore ("/path/to/truststore.jks" , "JKS" , "trustpass" ))
347+ .andReturn (testTruststore )
348+ .anyTimes ();
349+
350+ EasyMock .expect (requestMock .getServletContext ())
351+ .andReturn (servletContextMock )
352+ .anyTimes ();
353+ EasyMock .expect (requestMock .getHeader ("Authorization" ))
354+ .andReturn (BEARER_VALID_TOKEN )
355+ .anyTimes ();
356+ EasyMock .expect (responseMock .getStatus ())
357+ .andReturn (200 )
358+ .anyTimes ();
359+ responseMock .sendError (EasyMock .eq (HttpServletResponse .SC_UNAUTHORIZED ), EasyMock .anyString ());
360+ EasyMock .expectLastCall ().andThrow (new AssertionError ("Authentication should be successful, but was not." )).anyTimes ();
361+
362+ EasyMock .replay (requestMock , responseMock , keystoreServiceMock );
363+
364+ setupURLConnection ("https://example.com/auth" );
365+ filter .doFilter (requestMock , responseMock , chainMock );
366+
367+ assertTrue ("Filter chain should have been called" , chainMock .doFilterCalled );
368+ }
369+
370+ @ Test
371+ public void testHttpsRequestWithoutTrustStore () throws Exception {
372+ // Setup without trust store configuration
373+ setUp (null , null , null );
374+
375+ KeyStore defaultTruststore = KeyStore .getInstance ("JKS" );
376+ EasyMock .expect (keystoreServiceMock .getTruststoreForHttpClient ())
377+ .andReturn (defaultTruststore )
378+ .anyTimes ();
379+
380+ EasyMock .expect (requestMock .getServletContext ())
381+ .andReturn (servletContextMock )
382+ .anyTimes ();
383+ EasyMock .expect (requestMock .getHeader ("Authorization" ))
384+ .andReturn (BEARER_VALID_TOKEN )
385+ .anyTimes ();
386+ EasyMock .expect (responseMock .getStatus ())
387+ .andReturn (200 )
388+ .anyTimes ();
389+ responseMock .sendError (EasyMock .eq (HttpServletResponse .SC_UNAUTHORIZED ), EasyMock .anyString ());
390+ EasyMock .expectLastCall ().andThrow (new AssertionError ("Authentication should be successful, but was not." )).anyTimes ();
391+
392+ EasyMock .replay (requestMock , responseMock , keystoreServiceMock );
393+
394+ setupURLConnection ("https://example.com/auth" );
395+ filter .doFilter (requestMock , responseMock , chainMock );
396+
397+ assertTrue ("Filter chain should have been called with default trust store" , chainMock .doFilterCalled );
398+ }
399+
400+ @ Test
401+ public void testHttpsRequestWithInvalidTrustStoreConfig () throws Exception {
402+ // Setup with invalid trust store configuration
403+ setUp ("/nonexistent/path/truststore.jks" , "password" , "JKS" );
404+
405+ EasyMock .expect (keystoreServiceMock .loadTruststore ("/nonexistent/path/truststore.jks" , "JKS" , "password" ))
406+ .andThrow (new KeystoreServiceException ("Failed to load truststore" ))
407+ .anyTimes ();
408+
409+ EasyMock .expect (requestMock .getServletContext ())
410+ .andReturn (servletContextMock )
411+ .anyTimes ();
412+ EasyMock .expect (requestMock .getHeader ("Authorization" ))
413+ .andReturn (BEARER_VALID_TOKEN )
414+ .anyTimes ();
415+ EasyMock .expect (responseMock .getStatus ())
416+ .andReturn (500 )
417+ .anyTimes ();
418+ responseMock .sendError (HttpServletResponse .SC_INTERNAL_SERVER_ERROR , "Error processing authentication request" );
419+ EasyMock .expectLastCall ().once ();
420+
421+ EasyMock .replay (requestMock , responseMock , keystoreServiceMock );
422+
423+ filter .doFilter (requestMock , responseMock , chainMock );
424+
425+ assertFalse ("Filter chain should not have been called" , chainMock .doFilterCalled );
426+ EasyMock .verify (responseMock );
427+ }
428+
290429 public static class MockHttpURLConnection extends HttpURLConnection {
291430 private final URL url ;
292431 private int responseCode ;
0 commit comments