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.POST;
26  import javax.ws.rs.Path;
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     @Test
200     public void testCreateGetNotProtectedByOriginBasedXsrfFilterInLegacyMode() {
201         enableLegacyXsrfMode();
202         testCreateGetNotProtectedByOriginBasedXsrfFilter();
203     }
204 
205     @Test
206     public void testCreateGetResourceWithRequiresXsrfCheckAnnotationInLegacyMode() {
207         enableLegacyXsrfMode();
208         testCreateGetResourceWithRequiresXsrfCheckAnnotation();
209     }
210 
211     @Test
212     public void testCreateGetResourceWithoutRequiresXsrfCheckAnnotation() {
213         AbstractMethod abstractMethod = getMockAbstractMethod(
214                 new Annotation[0]);
215         when(abstractMethod.isAnnotationPresent(GET.class)).thenReturn(true);
216         assertThat(xsrfResourceFilterFactory.create(abstractMethod), empty());
217     }
218 
219     @Test
220     public void testCreateGetResourceWithoutRequiresXsrfCheckAnnotationInLegacyMode() {
221         enableLegacyXsrfMode();
222         testCreateGetResourceWithoutRequiresXsrfCheckAnnotation();
223     }
224 
225     private void enableLegacyXsrfMode() {
226         when(darkFeatureManager.isFeatureEnabledForAllUsers(
227                 LEGACY_FEATURE_KEY)).thenReturn(true);
228     }
229 
230     private AbstractMethod getMockAbstractMethod(Annotation[] annotations) {
231         AbstractMethod abstractMethod = mock(AbstractMethod.class);
232         AbstractResource abstractResource = mock(AbstractResource.class);
233         when(abstractMethod.getResource()).thenReturn(abstractResource);
234         when(abstractMethod.getAnnotations()).thenReturn(annotations);
235         when(abstractResource.getAnnotations()).thenReturn(annotations);
236         return abstractMethod;
237     }
238 
239     private AnnotatedElement getMockAnnotatedElement(
240             boolean xsrfAnnotationPresent) {
241         return getMockAnnotatedElement(xsrfAnnotationPresent,
242                 new Annotation[]{});
243     }
244 
245     private AnnotatedElement getMockAnnotatedElement(
246             boolean xsrfAnnotationPresent, Annotation[] annotations) {
247         return setupAnnotatedElement(mock(AnnotatedElement.class),
248                 xsrfAnnotationPresent, annotations);
249     }
250 
251     private AnnotatedElement setupAnnotatedElement(
252             AnnotatedElement annotatedElement, boolean xsrfAnnotationPresent,
253             Annotation[] annotations) {
254         when(annotatedElement.isAnnotationPresent(
255                 com.atlassian.plugins.rest.common.security.RequiresXsrfCheck.class)
256         ).thenReturn(xsrfAnnotationPresent);
257         when(annotatedElement.getAnnotations()).thenReturn(annotations);
258         return annotatedElement;
259     }
260 
261     private AbstractResource getMockAbstractResource(
262             boolean xsrfAnnotationPresent, Annotation[] annotations) {
263         AbstractResource abstractResource = mock(AbstractResource.class);
264         when(abstractResource.getAnnotations()).thenReturn(annotations);
265         when(abstractResource.isAnnotationPresent(
266                 com.atlassian.plugins.rest.common.security.RequiresXsrfCheck.class)
267         ).thenReturn(xsrfAnnotationPresent);
268         return abstractResource;
269     }
270 
271     private Annotation getMockAnnotation(final Class clazz) {
272         Annotation annotation = mock(Annotation.class);
273         when(annotation.annotationType()).thenAnswer(
274                 new Answer<Class<? extends Annotation>>() {
275                     public Class<? extends Annotation> answer(InvocationOnMock invoc) {
276                         return clazz;
277                     }
278                 });
279         return annotation;
280     }
281 }