View Javadoc

1   package com.atlassian.seraph.filter;
2   
3   import java.io.IOException;
4   import java.security.Principal;
5   import java.util.HashSet;
6   import java.util.Iterator;
7   import java.util.Set;
8   
9   import javax.servlet.Filter;
10  import javax.servlet.FilterChain;
11  import javax.servlet.FilterConfig;
12  import javax.servlet.ServletException;
13  import javax.servlet.ServletRequest;
14  import javax.servlet.ServletResponse;
15  import javax.servlet.http.HttpServletRequest;
16  import javax.servlet.http.HttpServletResponse;
17  
18  import org.apache.log4j.Category;
19  
20  import com.atlassian.seraph.SecurityService;
21  import com.atlassian.seraph.auth.AuthenticationContext;
22  import com.atlassian.seraph.config.SecurityConfig;
23  import com.atlassian.seraph.config.SecurityConfigFactory;
24  import com.atlassian.seraph.util.RedirectUtils;
25  
26  /**
27   * The SecurityFilter determines which roles are required for a given request by querying all of the Services.
28   *
29   * @see SecurityService
30   */
31  public class SecurityFilter implements Filter
32  {
33      private FilterConfig config = null;
34      private SecurityConfig securityConfig = null;
35  
36      private static final Category log = Category.getInstance(SecurityFilter.class);
37      private static final String ALREADY_FILTERED = "os_securityfilter_already_filtered";
38      public static final String ORIGINAL_URL = "atlassian.core.seraph.original.url";
39  
40      public void init(FilterConfig config)
41      {
42          // log.debug("SecurityFilter.init");
43          this.config = config;
44  
45          String configFileLocation = null;
46  
47          if (config.getInitParameter("config.file") != null)
48          {
49              configFileLocation = config.getInitParameter("config.file");
50              log.debug("Security config file location: " + configFileLocation);
51          }
52  
53          securityConfig = SecurityConfigFactory.getInstance(configFileLocation);
54          config.getServletContext().setAttribute(SecurityConfig.STORAGE_KEY, securityConfig);
55      }
56  
57      public void destroy()
58      {
59          // log.debug("SecurityFilter.destroy");
60          securityConfig.destroy();
61          securityConfig = null;
62          config = null;
63      }
64  
65      /**
66       * @deprecated Not needed in latest version of Servlet 2.3 API
67       */
68      // NOTE: Filter doesn't work with Orion 1.5.2 without this method
69      public FilterConfig getFilterConfig()
70      {
71          return config;
72      }
73  
74      /**
75       * @deprecated Not needed in latest version of Servlet 2.3 API - replaced by init().
76       */
77      // NOTE: Filter doesn't work with Orion 1.5.2 without this method
78      public void setFilterConfig(FilterConfig filterConfig)
79      {
80          if (filterConfig != null) //it seems that Orion 1.5.2 calls this with a null config.
81              init(filterConfig);
82      }
83  
84      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
85              throws IOException, ServletException
86      {
87          if (req.getAttribute(ALREADY_FILTERED) != null || !getSecurityConfig().getController().isSecurityEnabled())
88          {
89              chain.doFilter(req, res);
90              return;
91          }
92          else
93          {
94              req.setAttribute(ALREADY_FILTERED, Boolean.TRUE);
95          }
96  
97          // Try and get around Orion's bug when redeploying
98          // it seems that filters are not called in the correct order
99          if (req.getAttribute(BaseLoginFilter.ALREADY_FILTERED) == null)
100         {
101             log.warn("LoginFilter not yet applied to this request - terminating filter chain");
102             return;
103         }
104 
105         HttpServletRequest request = (HttpServletRequest) req;
106         HttpServletResponse response = (HttpServletResponse) res;
107 
108         String originalURL = request.getServletPath() +
109                 (request.getPathInfo() == null ? "" : request.getPathInfo()) +
110                 (request.getQueryString() == null ? "" : "?" + request.getQueryString());
111 
112 
113         // store the original URL as a request attribute anyway - often useful for pages to access it (ie login links)
114         request.setAttribute(SecurityFilter.ORIGINAL_URL, originalURL);
115         if (log.isDebugEnabled())
116         	log.debug("Storing the originally requested URL (" + SecurityFilter.ORIGINAL_URL + "=" + originalURL + ")");
117 
118         Set requiredRoles = new HashSet();
119 
120         // loop through loaded services and get required roles
121         for (Iterator iterator = getSecurityConfig().getServices().iterator(); iterator.hasNext();)
122         {
123             SecurityService service = (SecurityService) iterator.next();
124 
125             Set serviceRoles = service.getRequiredRoles(request);
126             requiredRoles.addAll(serviceRoles);
127         }
128 
129         if (log.isDebugEnabled()) {
130             log.debug("requiredRoles = " + requiredRoles);
131         }
132 
133         // whether this URL needs authorisation
134         boolean needAuth = false;
135 
136         // try to get the user (for cookie logins)
137         Principal user = getSecurityConfig().getAuthenticator().getUser(request, response);
138 
139         // only redirect if we don't use basic HTTP authentication
140         boolean basicAuthentication = RedirectUtils.isBasicAuthentication(request, getSecurityConfig().getAuthType());
141         // if we are doing basic authentication, then we don't want to continue the filter chain - we need to commit
142         // the response to allow for the authentication challenge mechanism
143         if (basicAuthentication && user == null)
144         {
145         	log.debug("Basic authentication requested.");
146             return;
147         }
148 
149         // set the user in the context
150         getAuthenticationContext().setUser(user);
151 
152         // check if the current user has all required permissions
153         // if there is no current user, request.isUserInRole() always returns false so this works
154         for (Iterator iterator = requiredRoles.iterator(); iterator.hasNext();)
155         {
156             String role = (String) iterator.next();
157 
158             // this isUserInRole method is only used here and 'd be better off replaced by getRoleMapper().hasRole(user, request, role)) since we have the user already
159             // was : if (!securityConfig.getAuthenticator().isUserInRole(request, role))
160             if (!getSecurityConfig().getRoleMapper().hasRole(user, request, role))
161             {
162                 log.info("User '" + user + "' needs (and lacks) role '" + role + "' to access " + originalURL);
163                 needAuth = true;
164             }
165         }
166 
167         // check if we're at the signon page, in which case do not auth
168         if (request.getServletPath() != null && request.getServletPath().equals(getSecurityConfig().getLoginURL()))
169         {
170         	log.debug("Login page requested so no additional authorization required.");
171             needAuth = false;
172         }
173 
174         // if we need to authenticate, store current URL and forward
175         if (needAuth)
176         {
177             if (log.isDebugEnabled())
178                 log.debug("Need Authentication: Redirecting to: " + getSecurityConfig().getLoginURL() + " from: " + originalURL);
179 
180             request.getSession().setAttribute(getSecurityConfig().getOriginalURLKey(), originalURL);
181             // only redirect if we can. if isCommited==true, there might have been a redirection requested by a LoginInterceptor, for instance.
182             if (!response.isCommitted())
183             {
184                 response.sendRedirect(RedirectUtils.getLoginUrl(request));
185             }
186             return;
187         }
188         else
189         {
190             try
191             {
192                 chain.doFilter(req, res);
193             }
194             finally
195             {
196                 // clear the user from the context
197                 getAuthenticationContext().clearUser();
198             }
199         }
200     }
201 
202     protected SecurityConfig getSecurityConfig() {
203         // ?? is this any useful, since we already initalize this in the init method ??
204         if (securityConfig == null)
205         {
206             securityConfig = (SecurityConfig) config.getServletContext().getAttribute(SecurityConfig.STORAGE_KEY);
207         }
208         return securityConfig;
209     }
210 
211     protected AuthenticationContext getAuthenticationContext() {
212         return getSecurityConfig().getAuthenticationContext();
213     }
214 
215 }