19
19
20
20
import org .apache .knox .gateway .security .GroupPrincipal ;
21
21
import 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 ;
22
26
import org .apache .knox .test .mock .MockServletContext ;
23
27
import org .easymock .EasyMock ;
24
28
import org .junit .Before ;
28
32
import javax .security .auth .Subject ;
29
33
import javax .servlet .FilterChain ;
30
34
import javax .servlet .FilterConfig ;
35
+ import javax .servlet .ServletContext ;
31
36
import javax .servlet .ServletException ;
32
37
import javax .servlet .ServletRequest ;
33
38
import javax .servlet .ServletResponse ;
45
50
import java .util .List ;
46
51
import java .util .Map ;
47
52
import java .util .Set ;
53
+ import java .security .KeyStore ;
48
54
49
55
import static org .junit .Assert .assertEquals ;
50
56
import static org .junit .Assert .assertFalse ;
51
57
import static org .junit .Assert .assertTrue ;
52
58
59
+ @ SuppressWarnings ("PMD.JUnit4TestShouldUseBeforeAnnotation" )
53
60
public class RemoteAuthFilterTest {
54
61
55
62
public static final String BEARER_INVALID_TOKEN = "Bearer invalid-token" ;
56
63
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" ;
59
66
public static final String X_AUTHENTICATED_USER = "X-Authenticated-User" ;
60
67
public static final String X_AUTHENTICATED_GROUP = "X-Authenticated-Group" ;
61
68
public static final String X_AUTHENTICATED_GROUP_2 = "X-Authenticated-Group-2" ;
@@ -65,22 +72,52 @@ public class RemoteAuthFilterTest {
65
72
private HttpServletRequest requestMock ;
66
73
private HttpServletResponse responseMock ;
67
74
private TestFilterChain chainMock ;
75
+ private GatewayServices gatewayServicesMock ;
76
+ private KeystoreService keystoreServiceMock ;
77
+ private ServletContext servletContextMock ;
78
+
68
79
@ Before
69
- public void setUp () {
70
- FilterConfig filterConfigMock = EasyMock .createNiceMock (FilterConfig .class );
80
+ public void createMocks () {
71
81
requestMock = EasyMock .createMock (HttpServletRequest .class );
72
82
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 );
73
90
chainMock = new TestFilterChain ();
74
91
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 ))
81
112
.andReturn (X_AUTHENTICATED_GROUP + "," + X_AUTHENTICATED_GROUP_2 + ",X-Custom-Group-*" ).anyTimes ();
82
113
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 );
84
121
85
122
filter = new RemoteAuthFilter ();
86
123
try {
@@ -90,6 +127,11 @@ public void setUp() {
90
127
}
91
128
}
92
129
130
+ // Default setup method for backward compatibility
131
+ private void setUp () {
132
+ setUp (null , null , null );
133
+ }
134
+
93
135
private void setupURLConnection (String url ) {
94
136
try {
95
137
filter .httpURLConnection = new MockHttpURLConnection (new URL (url ));
@@ -100,6 +142,8 @@ private void setupURLConnection(String url) {
100
142
101
143
@ Test
102
144
public void successfulAuthentication () throws Exception {
145
+ setUp ();
146
+
103
147
EasyMock .expect (requestMock .getServletContext ()).andReturn (new MockServletContext ()).anyTimes ();
104
148
EasyMock .expect (requestMock .getHeader ("Authorization" )).andReturn (BEARER_VALID_TOKEN ).anyTimes ();
105
149
EasyMock .expect (responseMock .getStatus ()).andReturn (200 ).anyTimes ();
@@ -134,6 +178,8 @@ public void successfulAuthentication() throws Exception {
134
178
135
179
@ Test
136
180
public void authenticationFailsWithInvalidToken () throws Exception {
181
+ setUp ();
182
+
137
183
EasyMock .expect (requestMock .getServletContext ()).andReturn (new MockServletContext ()).anyTimes ();
138
184
EasyMock .expect (responseMock .getStatus ()).andReturn (401 ).anyTimes ();
139
185
EasyMock .expect (requestMock .getHeader ("Authorization" )).andReturn (BEARER_INVALID_TOKEN ).anyTimes ();
@@ -160,11 +206,12 @@ public void authenticationFailsWithInvalidToken() throws Exception {
160
206
161
207
@ Test
162
208
public void testCacheBehavior () throws Exception {
209
+ setUp ();
210
+
163
211
String principalName = "lmccayiv" ;
164
212
String groupNames = "admin2,scientists" ;
165
213
Subject subject = new Subject ();
166
214
subject .getPrincipals ().add (new PrimaryPrincipal (principalName ));
167
- // Add groups to the principal if available
168
215
Arrays .stream (groupNames .split ("," )).forEach (groupName -> subject .getPrincipals ()
169
216
.add (new GroupPrincipal (groupName )));
170
217
filter .setCachedSubject (BEARER_VALID_TOKEN , subject );
@@ -202,9 +249,10 @@ public void testCacheBehavior() throws Exception {
202
249
203
250
@ Test
204
251
public void testTraceIdPropagation () throws Exception {
252
+ setUp ();
253
+
205
254
String expectedTraceId = "test-trace-123" ;
206
255
207
- // Set up mocks
208
256
EasyMock .expect (requestMock .getServletContext ())
209
257
.andReturn (new MockServletContext ())
210
258
.anyTimes ();
@@ -250,6 +298,8 @@ public String getRequestProperty(String key) {
250
298
251
299
@ Test
252
300
public void successfulAuthenticationWithMultipleGroups () throws Exception {
301
+ setUp ();
302
+
253
303
EasyMock .expect (requestMock .getServletContext ()).andReturn (new MockServletContext ()).anyTimes ();
254
304
EasyMock .expect (requestMock .getHeader ("Authorization" )).andReturn (BEARER_VALID_TOKEN ).anyTimes ();
255
305
EasyMock .expect (responseMock .getStatus ()).andReturn (200 ).anyTimes ();
@@ -287,6 +337,95 @@ public void successfulAuthenticationWithMultipleGroups() throws Exception {
287
337
}
288
338
}
289
339
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
+
290
429
public static class MockHttpURLConnection extends HttpURLConnection {
291
430
private final URL url ;
292
431
private int responseCode ;
0 commit comments