View Javadoc

1   package com.atlassian.plugins.rest.common.security.jersey;
2   
3   import com.atlassian.annotations.security.XsrfProtectionExcluded;
4   import com.atlassian.plugin.PluginAccessor;
5   import com.atlassian.plugin.event.PluginEventManager;
6   import com.atlassian.sal.api.features.DarkFeatureManager;
7   import com.atlassian.sal.api.web.context.HttpContext;
8   import com.atlassian.sal.api.xsrf.XsrfRequestValidator;
9   import com.atlassian.sal.core.xsrf.IndependentXsrfTokenAccessor;
10  import com.atlassian.sal.core.xsrf.IndependentXsrfTokenValidator;
11  import com.atlassian.sal.core.xsrf.XsrfRequestValidatorImpl;
12  import com.sun.jersey.api.model.AbstractMethod;
13  import com.sun.jersey.api.model.AbstractResource;
14  import com.sun.jersey.spi.container.ResourceFilter;
15  import org.junit.Before;
16  import org.junit.Test;
17  import org.junit.runner.RunWith;
18  import org.mockito.Mock;
19  import org.mockito.invocation.InvocationOnMock;
20  import org.mockito.runners.MockitoJUnitRunner;
21  import org.mockito.stubbing.Answer;
22  
23  import javax.ws.rs.Consumes;
24  import javax.ws.rs.GET;
25  import javax.ws.rs.Path;
26  import javax.ws.rs.POST;
27  import java.lang.annotation.Annotation;
28  import java.lang.reflect.AnnotatedElement;
29  import java.util.List;
30  
31  import static org.hamcrest.CoreMatchers.instanceOf;
32  import static org.hamcrest.CoreMatchers.not;
33  import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
34  import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
35  import static org.hamcrest.core.Is.is;
36  import static org.hamcrest.Matchers.empty;
37  import static org.junit.Assert.assertThat;
38  import static org.mockito.Mockito.mock;
39  import static org.mockito.Mockito.when;
40  
41  @RunWith(MockitoJUnitRunner.class)
42  public class TestXsrfResourceFilterFactory {
43      private static final String LEGACY_FEATURE_KEY = "atlassian.rest.xsrf.legacy.enabled";
44  
45      @Mock
46      private DarkFeatureManager darkFeatureManager;
47      @Mock
48      private HttpContext httpContext;
49      private XsrfRequestValidator xsrfRequestValidator;
50      @Mock
51      private PluginAccessor pluginAccessor;
52      @Mock
53      private PluginEventManager pluginEventManager;
54      private XsrfResourceFilterFactory xsrfResourceFilterFactory;
55  
56      private interface RequiresXsrfCheck {
57      }
58  
59      @Before
60      public void setUp() {
61          xsrfRequestValidator = new XsrfRequestValidatorImpl(
62              new IndependentXsrfTokenValidator(
63                  new IndependentXsrfTokenAccessor()));
64          when(darkFeatureManager.isFeatureEnabledForAllUsers(
65              LEGACY_FEATURE_KEY)).thenReturn(false);
66  
67          xsrfResourceFilterFactory = new XsrfResourceFilterFactory(
68              httpContext, xsrfRequestValidator, pluginAccessor, pluginEventManager, darkFeatureManager);
69      }
70  
71      @Test
72      public void testHasXsrfAnnotationWithRequiresXsrfCheckPresent() {
73          AnnotatedElement annotatedElement = getMockAnnotatedElement(true);
74          assertThat(xsrfResourceFilterFactory.hasRequiresXsrfCheckAnnotation(
75              annotatedElement), is(true));
76      }
77  
78      @Test
79      public void testHasXsrfAnnotationWithoutRequiresXsrfCheckPresent() {
80          AnnotatedElement annotatedElement = getMockAnnotatedElement(false);
81          assertThat(xsrfResourceFilterFactory.hasRequiresXsrfCheckAnnotation(
82              annotatedElement), is(false));
83      }
84  
85      @Test
86      public void testHasXsrfAnnotationWithRequiresXsrfCheckAnnotation() {
87          Annotation[] annotations = {getMockAnnotation(Path.class),
88              getMockAnnotation(
89                  com.atlassian.plugins.rest.common.security.RequiresXsrfCheck.class
90              )};
91          AnnotatedElement annotatedElement = getMockAnnotatedElement(false,
92              annotations);
93          assertThat(xsrfResourceFilterFactory.hasRequiresXsrfCheckAnnotation(
94              annotatedElement), is(true));
95      }
96  
97      @Test
98      public void testHasXsrfAnnotationWithAnotherRequiresXsrfCheckAnnotation() {
99          Annotation[] annotations = {getMockAnnotation(Path.class),
100             getMockAnnotation(RequiresXsrfCheck.class)};
101         AnnotatedElement annotatedElement = getMockAnnotatedElement(false,
102             annotations);
103         assertThat(xsrfResourceFilterFactory.hasRequiresXsrfCheckAnnotation(
104             annotatedElement), is(true));
105     }
106 
107     @Test
108     public void testHasXsrfAnnotationWithoutRequiresXsrfCheckAnnotation() {
109         Annotation[] annotations = {getMockAnnotation(Path.class),
110             getMockAnnotation(Consumes.class)};
111         AnnotatedElement annotatedElement = getMockAnnotatedElement(false,
112             annotations);
113         assertThat(xsrfResourceFilterFactory.hasRequiresXsrfCheckAnnotation(
114             annotatedElement), is(false));
115     }
116 
117     @Test
118     public void testCreateWithXsrfProtectionExcludedAnnotation() {
119         Annotation[] annotations = {getMockAnnotation(
120             XsrfProtectionExcluded.class)};
121         AbstractMethod abstractMethod = getMockAbstractMethod(annotations);
122         assertThat(xsrfResourceFilterFactory.create(abstractMethod), empty());
123     }
124 
125     @Test
126     public void testCreateWithoutXsrfProtectionExcludedAnnotation() {
127         AbstractMethod abstractMethod = getMockAbstractMethod(
128             new Annotation[0]);
129         assertThat(xsrfResourceFilterFactory.create(abstractMethod), hasSize(1));
130     }
131 
132     @Test
133     public void testCreateWithoutXsrfProtectionExcludedAnnotationInLegacyMode() {
134         enableLegacyXsrfMode();
135         AbstractMethod abstractMethod = getMockAbstractMethod(
136             new Annotation[0]);
137         assertThat(xsrfResourceFilterFactory.create(abstractMethod), empty());
138     }
139 
140     @Test
141     public void testCreateGetResourceWithRequiresXsrfCheckAnnotation() {
142         Annotation[] annotations = {getMockAnnotation(
143             RequiresXsrfCheck.class)};
144         AbstractMethod abstractMethod = getMockAbstractMethod(annotations);
145         when(abstractMethod.isAnnotationPresent(GET.class)).thenReturn(true);
146         assertThat(xsrfResourceFilterFactory.create(abstractMethod), hasSize(1));
147     }
148 
149     private void assertThatCreatePostProtectedByOriginBasedXsrfFilter(
150         boolean containsOriginBasedXsrfFilter) {
151         AbstractMethod abstractMethod = mock(AbstractMethod.class);
152         AbstractResource abstractResource = getMockAbstractResource(
153             false, new Annotation[0]);
154         Annotation[] annotations = {getMockAnnotation(POST.class)};
155         when(abstractMethod.getResource()).thenReturn(abstractResource);
156 
157         when(abstractMethod.getAnnotations()).thenReturn(annotations);
158         when(abstractMethod.isAnnotationPresent(POST.class)).thenReturn(true);
159 
160         List<ResourceFilter> resourceFilters = xsrfResourceFilterFactory.create(
161             abstractMethod);
162         if (containsOriginBasedXsrfFilter) {
163             assertThat(resourceFilters,
164                 contains(instanceOf(OriginBasedXsrfResourceFilter.class)));
165         } else {
166             assertThat(resourceFilters,
167                 not(contains(instanceOf(OriginBasedXsrfResourceFilter.class))));
168         }
169     }
170 
171     @Test
172     public void testCreatePostProtectedByOriginBasedXsrfFilter() {
173         assertThatCreatePostProtectedByOriginBasedXsrfFilter(false);
174     }
175 
176     @Test
177     public void testCreatePostProtectedByOriginBasedXsrfFilterInLegacyMode() {
178         enableLegacyXsrfMode();
179         assertThatCreatePostProtectedByOriginBasedXsrfFilter(true);
180     }
181 
182     @Test
183     public void testCreateGetNotProtectedByOriginBasedXsrfFilter() {
184         AbstractMethod abstractMethod = mock(AbstractMethod.class);
185         AbstractResource abstractResource = getMockAbstractResource(
186             false, new Annotation[0]);
187         Annotation[] annotations = {getMockAnnotation(GET.class)};
188         when(abstractMethod.getResource()).thenReturn(abstractResource);
189 
190         when(abstractMethod.getAnnotations()).thenReturn(annotations);
191         when(abstractMethod.isAnnotationPresent(GET.class)).thenReturn(true);
192 
193         List<ResourceFilter> resourceFilters = xsrfResourceFilterFactory.create(
194             abstractMethod);
195 
196         assertThat(resourceFilters, empty());
197     }
198 
199     // The OriginBasedXsrfFilter checks the request method before running
200     // its xsrf checks.
201     @Test(expected = AssertionError.class)
202     public void testCreateGetHasOriginBasedXsrfFilterInLegacyMode() {
203         enableLegacyXsrfMode();
204         testCreateGetNotProtectedByOriginBasedXsrfFilter();
205     }
206 
207     @Test
208     public void testCreateGetResourceWithRequiresXsrfCheckAnnotationInLegacyMode() {
209         enableLegacyXsrfMode();
210         testCreateGetResourceWithRequiresXsrfCheckAnnotation();
211     }
212 
213     @Test
214     public void testCreateGetResourceWithoutRequiresXsrfCheckAnnotation() {
215         AbstractMethod abstractMethod = getMockAbstractMethod(
216             new Annotation[0]);
217         when(abstractMethod.isAnnotationPresent(GET.class)).thenReturn(true);
218         assertThat(xsrfResourceFilterFactory.create(abstractMethod), empty());
219     }
220 
221     @Test
222     public void testCreateGetResourceWithoutRequiresXsrfCheckAnnotationInLegacyMode() {
223         enableLegacyXsrfMode();
224         testCreateGetResourceWithoutRequiresXsrfCheckAnnotation();
225     }
226 
227     private void enableLegacyXsrfMode() {
228         when(darkFeatureManager.isFeatureEnabledForAllUsers(
229             LEGACY_FEATURE_KEY)).thenReturn(true);
230     }
231 
232     private AbstractMethod getMockAbstractMethod(Annotation[] annotations) {
233         AbstractMethod abstractMethod = mock(AbstractMethod.class);
234         AbstractResource abstractResource = mock(AbstractResource.class);
235         when(abstractMethod.getResource()).thenReturn(abstractResource);
236         when(abstractMethod.getAnnotations()).thenReturn(annotations);
237         when(abstractResource.getAnnotations()).thenReturn(annotations);
238         return abstractMethod;
239     }
240 
241     private AnnotatedElement getMockAnnotatedElement(
242         boolean xsrfAnnotationPresent) {
243         return getMockAnnotatedElement(xsrfAnnotationPresent,
244             new Annotation[]{});
245     }
246 
247     private AnnotatedElement getMockAnnotatedElement(
248         boolean xsrfAnnotationPresent, Annotation[] annotations) {
249         return setupAnnotatedElement(mock(AnnotatedElement.class),
250             xsrfAnnotationPresent, annotations);
251     }
252 
253     private AnnotatedElement setupAnnotatedElement(
254         AnnotatedElement annotatedElement, boolean xsrfAnnotationPresent,
255         Annotation[] annotations) {
256         when(annotatedElement.isAnnotationPresent(
257             com.atlassian.plugins.rest.common.security.RequiresXsrfCheck.class)
258         ).thenReturn(xsrfAnnotationPresent);
259         when(annotatedElement.getAnnotations()).thenReturn(annotations);
260         return annotatedElement;
261     }
262 
263     private AbstractResource getMockAbstractResource(
264         boolean xsrfAnnotationPresent, Annotation[] annotations) {
265         AbstractResource abstractResource = mock(AbstractResource.class);
266         when(abstractResource.getAnnotations()).thenReturn(annotations);
267         when(abstractResource.isAnnotationPresent(
268             com.atlassian.plugins.rest.common.security.RequiresXsrfCheck.class)
269         ).thenReturn(xsrfAnnotationPresent);
270         return abstractResource;
271     }
272 
273     private Annotation getMockAnnotation(final Class clazz) {
274         Annotation annotation = mock(Annotation.class);
275         when(annotation.annotationType()).thenAnswer(
276             new Answer<Class<? extends Annotation>>() {
277                 public Class<? extends Annotation> answer(InvocationOnMock invoc) {
278                     return clazz;
279                 }
280             });
281         return annotation;
282     }
283 }