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.plugin.tracker.DefaultPluginModuleTracker;
7   import com.atlassian.plugin.tracker.PluginModuleTracker;
8   import com.atlassian.plugins.rest.common.security.RequiresXsrfCheck;
9   import com.atlassian.plugins.rest.common.security.descriptor.CorsDefaults;
10  import com.atlassian.plugins.rest.common.security.descriptor.CorsDefaultsModuleDescriptor;
11  import com.atlassian.sal.api.features.DarkFeatureManager;
12  import com.atlassian.sal.api.web.context.HttpContext;
13  import com.atlassian.sal.api.xsrf.XsrfRequestValidator;
14  import com.google.common.annotations.VisibleForTesting;
15  import com.google.common.base.Preconditions;
16  import com.sun.jersey.api.model.AbstractMethod;
17  import com.sun.jersey.spi.container.ResourceFilter;
18  import com.sun.jersey.spi.container.ResourceFilterFactory;
19  import org.slf4j.Logger;
20  import org.slf4j.LoggerFactory;
21  
22  import javax.ws.rs.GET;
23  import javax.ws.rs.core.Response;
24  import javax.ws.rs.POST;
25  import javax.ws.rs.ext.Provider;
26  import java.lang.annotation.Annotation;
27  import java.lang.reflect.AnnotatedElement;
28  import java.util.Collections;
29  import java.util.List;
30  
31  /**
32   * Factory for the XSRF resource filter
33   *
34   * @since 2.4
35   */
36  @Provider
37  public class XsrfResourceFilterFactory implements ResourceFilterFactory {
38      private static final Logger log = LoggerFactory.getLogger(
39              XsrfResourceFilterFactory.class);
40      private static final String LEGACY_FEATURE_KEY = "atlassian.rest.xsrf.legacy.enabled";
41  
42      private final HttpContext httpContext;
43      private final XsrfRequestValidator xsrfRequestValidator;
44      private final PluginModuleTracker<CorsDefaults, CorsDefaultsModuleDescriptor> pluginModuleTracker;
45      private final DarkFeatureManager darkFeatureManager;
46  
47      public XsrfResourceFilterFactory(
48              HttpContext httpContext,
49              XsrfRequestValidator xsrfRequestValidator,
50              PluginAccessor pluginAccessor,
51              PluginEventManager pluginEventManager,
52              DarkFeatureManager darkFeatureManager) {
53          this.httpContext = Preconditions.checkNotNull(httpContext);
54          this.xsrfRequestValidator = Preconditions.checkNotNull(xsrfRequestValidator);
55          this.pluginModuleTracker = new DefaultPluginModuleTracker<CorsDefaults, CorsDefaultsModuleDescriptor>(
56                  pluginAccessor, pluginEventManager, CorsDefaultsModuleDescriptor.class);
57          this.darkFeatureManager = darkFeatureManager;
58      }
59  
60      @VisibleForTesting
61      boolean hasRequiresXsrfCheckAnnotation(AnnotatedElement annotatedElement) {
62          if (annotatedElement.isAnnotationPresent(RequiresXsrfCheck.class)) {
63              return true;
64          }
65          for (Annotation annotation : annotatedElement.getAnnotations()) {
66              if (annotation.annotationType().getSimpleName().equals(
67                      RequiresXsrfCheck.class.getSimpleName())) {
68                  return true;
69              }
70          }
71          return false;
72      }
73  
74      /**
75       * Returns true if legacy XSRF mode is active, otherwise returns false.
76       *
77       * @since 3.0.0
78       * @deprecated Only provided for legacy reasons, will be removed in 4.0.0
79       */
80      @VisibleForTesting
81      @Deprecated
82      boolean inLegacyXsrfMode() {
83          return darkFeatureManager.isFeatureEnabledForAllUsers(LEGACY_FEATURE_KEY);
84      }
85  
86      /**
87       * Returns true if any of the given annotations is the
88       * XsrfProtectionExcluded annotation.
89       *
90       * @param annotations the annotations to check against.
91       * @return true if any of the given annotations is the
92       * XsrfProtectionExcluded annotation. Otherwise returns false.
93       */
94      private static boolean isXsrfProtectionExcludedAnnotationPresent(
95              Annotation[] annotations) {
96          for (Annotation annotation : annotations) {
97              if (annotation.annotationType().getCanonicalName().equals(
98                      XsrfProtectionExcluded.class.getCanonicalName())) {
99                  if (!annotation.annotationType().equals(
100                         XsrfProtectionExcluded.class)) {
101                     log.warn("Detected usage of the " +
102                             "com.atlassian.annotations.security." +
103                             "XsrfProtectionExcluded annotation loaded " +
104                             "from elsewhere. " +
105                             XsrfProtectionExcluded.class.getClassLoader() +
106                             " != " + annotation.annotationType().getClassLoader()
107                     );
108                 }
109                 return true;
110             }
111         }
112         return false;
113     }
114 
115     public List<ResourceFilter> create(final AbstractMethod method) {
116         final boolean hasEnforceAnnotation = hasRequiresXsrfCheckAnnotation(
117             method) || hasRequiresXsrfCheckAnnotation(method.getResource());
118         final boolean hasExcludeAnnotation =
119             isXsrfProtectionExcludedAnnotationPresent(method.getAnnotations());
120         XsrfResourceFilter xsrfResourceFilter = null;
121         if (inLegacyXsrfMode()) {
122             if (!hasExcludeAnnotation) {
123                 if (hasEnforceAnnotation) {
124                     xsrfResourceFilter = new XsrfResourceFilter();
125                 } else if (method.getAnnotations().length != 0) {
126                     xsrfResourceFilter = new OriginBasedXsrfResourceFilter();
127                 }
128                 if (xsrfResourceFilter != null) {
129                     xsrfResourceFilter.setFailureStatus(Response.Status.NOT_FOUND);
130                 }
131             }
132         } else if ((
133             method.isAnnotationPresent(GET.class) && hasEnforceAnnotation) ||
134             (!method.isAnnotationPresent(GET.class) && !hasExcludeAnnotation)) {
135             xsrfResourceFilter = new XsrfResourceFilter();
136         }
137         if (xsrfResourceFilter != null) {
138             xsrfResourceFilter.setHttpContext(httpContext);
139             xsrfResourceFilter.setXsrfRequestValidator(xsrfRequestValidator);
140             xsrfResourceFilter.setPluginModuleTracker(pluginModuleTracker);
141             return Collections.<ResourceFilter>singletonList(xsrfResourceFilter);
142         }
143         return Collections.emptyList();
144     }
145 }