View Javadoc

1   package com.atlassian.asap.core.server.jersey;
2   
3   import com.atlassian.asap.api.Jwt;
4   import com.atlassian.asap.api.JwtClaims;
5   import com.atlassian.asap.api.exception.AuthenticationFailedException;
6   import com.atlassian.asap.api.exception.PermanentAuthenticationFailedException;
7   import com.atlassian.asap.api.exception.TransientAuthenticationFailedException;
8   import com.atlassian.asap.api.server.http.RequestAuthenticator;
9   import com.atlassian.asap.core.server.jersey.test.ResourceWithPackageAsap;
10  import org.junit.Before;
11  import org.junit.Rule;
12  import org.junit.Test;
13  import org.mockito.Mock;
14  import org.mockito.junit.MockitoJUnit;
15  import org.mockito.junit.MockitoRule;
16  
17  import javax.ws.rs.container.ContainerRequestContext;
18  import javax.ws.rs.container.ResourceInfo;
19  import javax.ws.rs.core.HttpHeaders;
20  import javax.ws.rs.core.Response;
21  import java.io.IOException;
22  import java.lang.reflect.Method;
23  import java.util.Optional;
24  
25  import static com.atlassian.asap.core.server.filter.AbstractRequestAuthenticationFilter.AUTHENTIC_JWT_REQUEST_ATTRIBUTE;
26  import static com.atlassian.asap.core.server.jersey.AuthenticationRequestFilter.MAX_TRANSIENT_FAILURES_RETRIES;
27  import static com.google.common.collect.Sets.newHashSet;
28  import static org.mockito.Mockito.any;
29  import static org.mockito.Mockito.mock;
30  import static org.mockito.Mockito.never;
31  import static org.mockito.Mockito.times;
32  import static org.mockito.Mockito.verify;
33  import static org.mockito.Mockito.verifyNoMoreInteractions;
34  import static org.mockito.Mockito.verifyZeroInteractions;
35  import static org.mockito.Mockito.when;
36  
37  public class AuthenticationRequestFilterTest {
38      private static final String VALID_JWT = "Bearer valid_jwt";
39      private static final String INVALID_JWT_TRANSIENT = "Bearer transient_invalid_jwt";
40      private static final String INVALID_JWT_PERMANENT = "Bearer permanent_invalid_jwt";
41  
42      @SuppressWarnings("ThrowableInstanceNeverThrown")
43      private static final TransientAuthenticationFailedException TRANSIENT_AUTHENTICATION_FAILED_EXCEPTION
44              = new TransientAuthenticationFailedException(INVALID_JWT_TRANSIENT, null, null, null);
45  
46      @SuppressWarnings("ThrowableInstanceNeverThrown")
47      private static final PermanentAuthenticationFailedException PERMANENT_AUTHENTICATION_FAILED_EXCEPTION
48              = new PermanentAuthenticationFailedException(INVALID_JWT_PERMANENT, null);
49  
50      @Rule
51      public MockitoRule rule = MockitoJUnit.rule();
52  
53      @Mock
54      private RequestAuthenticator authenticator;
55      @Mock
56      private ResourceInfo resourceInfo;
57      @Mock
58      private FailureHandler failureHandler;
59      @Mock
60      private ContainerRequestContext context;
61      @Mock
62      private Jwt jwt;
63  
64      private AuthenticationRequestFilter filter;
65  
66      @Before
67      public void setUp() throws AuthenticationFailedException {
68          String audience = "presence-test";
69          JwtClaims claims = mock(JwtClaims.class);
70          when(claims.getAudience()).thenReturn(newHashSet(audience));
71          when(claims.getIssuer()).thenReturn("presence-test");
72          when(claims.getSubject()).thenReturn(Optional.empty());
73          when(jwt.getClaims()).thenReturn(claims);
74  
75          when(authenticator.authenticateRequest(VALID_JWT)).thenReturn(jwt);
76  
77          when(authenticator.authenticateRequest(INVALID_JWT_TRANSIENT))
78                  .thenThrow(TRANSIENT_AUTHENTICATION_FAILED_EXCEPTION);
79  
80          when(authenticator.authenticateRequest(INVALID_JWT_PERMANENT))
81                  .thenThrow(PERMANENT_AUTHENTICATION_FAILED_EXCEPTION);
82  
83          filter = new AuthenticationRequestFilter(authenticator, failureHandler);
84          filter.resourceInfo = resourceInfo;
85      }
86  
87      @Test
88      @SuppressWarnings("unchecked")
89      public void filterWithValidJwtOnProtectedResourceMethod()
90              throws IOException, AuthenticationFailedException, NoSuchMethodException {
91          Class resourceClass = TestResourceWithMethodAsap.class;
92          when(filter.resourceInfo.getResourceClass()).thenReturn(resourceClass);
93  
94          Method resourceMethod = resourceClass.getMethod("protectedGet");
95          when(filter.resourceInfo.getResourceMethod()).thenReturn(resourceMethod);
96  
97          mockAuthorization(VALID_JWT);
98  
99          filter.filter(context);
100 
101         verify(context).setProperty(AUTHENTIC_JWT_REQUEST_ATTRIBUTE, jwt);
102         verifyZeroInteractions(failureHandler);
103     }
104 
105     @Test
106     @SuppressWarnings("unchecked")
107     public void filterWithValidJwtOnProtectedResourceClass()
108             throws IOException, AuthenticationFailedException, NoSuchMethodException {
109         Class resourceClass = TestResourceWithClassAsap.class;
110         when(filter.resourceInfo.getResourceClass()).thenReturn(resourceClass);
111 
112         Method resourceMethod = resourceClass.getMethod("protectedGet");
113         when(filter.resourceInfo.getResourceMethod()).thenReturn(resourceMethod);
114 
115         when(context.getHeaderString(HttpHeaders.AUTHORIZATION)).thenReturn(VALID_JWT);
116 
117         filter.filter(context);
118 
119         verify(context).setProperty(AUTHENTIC_JWT_REQUEST_ATTRIBUTE, jwt);
120         verify(context, never()).abortWith(any(Response.class));
121     }
122 
123     @Test
124     @SuppressWarnings("unchecked")
125     public void filterWithValidJwtOnExtendedProtectedResourceClass()
126             throws IOException, AuthenticationFailedException, NoSuchMethodException {
127         Class resourceClass = ExtendedTestResourceWithClassAsap.class;
128         when(filter.resourceInfo.getResourceClass()).thenReturn(resourceClass);
129 
130         Method resourceMethod = resourceClass.getMethod("protectedGet");
131         when(filter.resourceInfo.getResourceMethod()).thenReturn(resourceMethod);
132 
133         mockAuthorization(VALID_JWT);
134 
135         filter.filter(context);
136 
137         verify(context).setProperty(AUTHENTIC_JWT_REQUEST_ATTRIBUTE, jwt);
138         verifyZeroInteractions(failureHandler);
139     }
140 
141     @Test
142     @SuppressWarnings("unchecked")
143     public void filterWithValidJwtOnPackageResourceClass() throws IOException, AuthenticationFailedException,
144             NoSuchMethodException {
145         Class resourceClass = ResourceWithPackageAsap.class;
146         when(filter.resourceInfo.getResourceClass()).thenReturn(resourceClass);
147 
148         Method resourceMethod = resourceClass.getMethod("protectedGet");
149         when(filter.resourceInfo.getResourceMethod()).thenReturn(resourceMethod);
150 
151         mockAuthorization(VALID_JWT);
152 
153         filter.filter(context);
154 
155         verify(context).setProperty(AUTHENTIC_JWT_REQUEST_ATTRIBUTE, jwt);
156         verifyZeroInteractions(failureHandler);
157     }
158 
159     @Test
160     @SuppressWarnings("unchecked")
161     public void filterWithPermanentInvalidJwt() throws IOException, NoSuchMethodException {
162         Class resourceClass = TestResourceWithMethodAsap.class;
163         when(filter.resourceInfo.getResourceClass()).thenReturn(resourceClass);
164 
165         Method resourceMethod = resourceClass.getMethod("protectedGet");
166         when(filter.resourceInfo.getResourceMethod()).thenReturn(resourceMethod);
167 
168         mockAuthorization(INVALID_JWT_PERMANENT);
169 
170         filter.filter(context);
171 
172         verify(context, never()).setProperty(AUTHENTIC_JWT_REQUEST_ATTRIBUTE, jwt);
173         verify(failureHandler).onPermanentAuthenticationFailure(context, PERMANENT_AUTHENTICATION_FAILED_EXCEPTION);
174         verifyNoMoreInteractions(failureHandler);
175     }
176 
177     @Test
178     @SuppressWarnings("unchecked")
179     public void filterWithTransientInvalidJwtNoRetry() throws IOException, NoSuchMethodException {
180         Class resourceClass = TestResourceWithMethodAsap.class;
181         when(filter.resourceInfo.getResourceClass()).thenReturn(resourceClass);
182 
183         Method resourceMethod = resourceClass.getDeclaredMethod("protectedGet");
184         when(filter.resourceInfo.getResourceMethod()).thenReturn(resourceMethod);
185 
186         when(failureHandler.onTransientAuthenticationFailure(any(), any())).thenReturn(false);
187 
188         mockAuthorization(INVALID_JWT_TRANSIENT);
189 
190         filter.filter(context);
191 
192         verify(context, never()).setProperty(AUTHENTIC_JWT_REQUEST_ATTRIBUTE, jwt);
193         verify(failureHandler).onTransientAuthenticationFailure(context, TRANSIENT_AUTHENTICATION_FAILED_EXCEPTION);
194         verifyNoMoreInteractions(failureHandler);
195     }
196 
197     @Test
198     @SuppressWarnings("unchecked")
199     public void filterWithTransientInvalidJwtWithOneRetry() throws IOException, NoSuchMethodException {
200         Class resourceClass = TestResourceWithMethodAsap.class;
201         when(filter.resourceInfo.getResourceClass()).thenReturn(resourceClass);
202 
203         Method resourceMethod = resourceClass.getDeclaredMethod("protectedGet");
204         when(filter.resourceInfo.getResourceMethod()).thenReturn(resourceMethod);
205 
206         when(failureHandler.onTransientAuthenticationFailure(any(), any())).thenReturn(true, false);
207 
208         mockAuthorization(INVALID_JWT_TRANSIENT);
209 
210         filter.filter(context);
211 
212         verify(context, never()).setProperty(AUTHENTIC_JWT_REQUEST_ATTRIBUTE, jwt);
213         verify(failureHandler, times(2)).onTransientAuthenticationFailure(context, TRANSIENT_AUTHENTICATION_FAILED_EXCEPTION);
214         verifyNoMoreInteractions(failureHandler);
215     }
216 
217     @Test
218     @SuppressWarnings("unchecked")
219     public void filterWithTransientInvalidJwtAlwaysRetry() throws IOException, NoSuchMethodException {
220         Class resourceClass = TestResourceWithMethodAsap.class;
221         when(filter.resourceInfo.getResourceClass()).thenReturn(resourceClass);
222 
223         Method resourceMethod = resourceClass.getDeclaredMethod("protectedGet");
224         when(filter.resourceInfo.getResourceMethod()).thenReturn(resourceMethod);
225 
226         when(failureHandler.onTransientAuthenticationFailure(any(), any())).thenReturn(true);
227 
228         mockAuthorization(INVALID_JWT_TRANSIENT);
229 
230         filter.filter(context);
231 
232         verify(context, never()).setProperty(AUTHENTIC_JWT_REQUEST_ATTRIBUTE, jwt);
233         verify(failureHandler, times(MAX_TRANSIENT_FAILURES_RETRIES - 1))
234                 .onTransientAuthenticationFailure(context, TRANSIENT_AUTHENTICATION_FAILED_EXCEPTION);
235         verify(failureHandler).onAuthenticationFailure(context, TRANSIENT_AUTHENTICATION_FAILED_EXCEPTION);
236         verifyNoMoreInteractions(failureHandler);
237     }
238 
239     @Test
240     @SuppressWarnings("unchecked")
241     public void filterWithoutAsap() throws IOException, NoSuchMethodException {
242         Class resourceClass = TestResourceWithMethodAsap.class;
243         when(filter.resourceInfo.getResourceClass()).thenReturn(resourceClass);
244 
245         Method resourceMethod = resourceClass.getMethod("unprotectedGet");
246         when(filter.resourceInfo.getResourceMethod()).thenReturn(resourceMethod);
247 
248         filter.filter(context);
249 
250         verify(context, never()).setProperty(AUTHENTIC_JWT_REQUEST_ATTRIBUTE, jwt);
251         verify(context, never()).abortWith(any(Response.class));
252     }
253 
254     @Test
255     @SuppressWarnings("unchecked")
256     public void filterWithoutAsapEnabled() throws IOException, NoSuchMethodException {
257         Class resourceClass = TestResourceWithClassAsap.class;
258         when(filter.resourceInfo.getResourceClass()).thenReturn(resourceClass);
259 
260         Method resourceMethod = resourceClass.getMethod("unprotectedGet");
261         when(filter.resourceInfo.getResourceMethod()).thenReturn(resourceMethod);
262 
263         filter.filter(context);
264 
265         verify(context, never()).setProperty(AUTHENTIC_JWT_REQUEST_ATTRIBUTE, jwt);
266         verifyZeroInteractions(failureHandler);
267     }
268 
269     private void mockAuthorization(String headerValue) {
270         when(context.getHeaderString(HttpHeaders.AUTHORIZATION)).thenReturn(headerValue);
271     }
272 
273     @Asap(authorizedSubjects = "presence-test")
274     private static class TestResourceWithClassAsap {
275         public void protectedGet() {
276         }
277 
278         @Asap(enabled = false)
279         public void unprotectedGet() {
280         }
281     }
282 
283     private static class TestResourceWithMethodAsap {
284         @Asap(authorizedSubjects = "presence-test")
285         public void protectedGet() {
286         }
287 
288         public void unprotectedGet() {
289         }
290     }
291 
292     private static class ExtendedTestResourceWithClassAsap extends TestResourceWithClassAsap {
293     }
294 
295 }