View Javadoc

1   package com.atlassian.seraph.filter;
2   
3   import com.atlassian.seraph.RequestParameterConstants;
4   import com.atlassian.seraph.auth.Authenticator;
5   import com.atlassian.seraph.config.SecurityConfig;
6   import com.atlassian.seraph.config.SecurityConfigFactory;
7   import com.atlassian.seraph.util.RedirectUtils;
8   import org.apache.log4j.Logger;
9   
10  import java.io.IOException;
11  import java.net.URI;
12  import java.net.URISyntaxException;
13  import java.security.Principal;
14  import javax.servlet.*;
15  import javax.servlet.http.HttpServletRequest;
16  import javax.servlet.http.HttpServletRequestWrapper;
17  import javax.servlet.http.HttpServletResponse;
18  import javax.servlet.http.HttpSession;
19  
20  /**
21   * This is a base authentication filter. It delegates the actual login process to a child class but takes care of the redirection process.
22   * <p/>
23   * If the authentication is successful, the user will be redirected by the filter to the URL given
24   * by the session attribute at SecurityFilter.ORIGINAL_URL_KEY.
25   * <p/>
26   * If this URL doesn't exist, it will look for a parameter 'os_destination' to use as the redirected URL instead.
27   * <p/>
28   * If neither is found, it is assumed that the page will check the authorisation status and handle redirection itself.
29   * <p/>
30   * From the any other filter in the request, or the servlet/JSP/action which processes the request, you can look up the
31   * status of the authorisation attempt. The status is a String request attribute, with the key 'os_authstatus'.
32   * <p/>
33   * The possible statuses are:
34   * <ul>
35   * <li> LoginFilter.LOGIN_SUCCESS - the login was processed, and user was logged in
36   * <li> LoginFilter.LOGIN_FAILURE - the login was processed, the user gave a bad username or password
37   * <li> LoginFilter.LOGIN_ERROR - the login was processed, an exception occurred trying to log the user in
38   * <li> LoginFilter.LOGIN_NOATTEMPT - the login was no processed, no form parameters existed
39   * </ul>
40   */
41  public abstract class BaseLoginFilter implements Filter
42  {
43      private static final Logger log = Logger.getLogger(BaseLoginFilter.class);
44  
45      private FilterConfig filterConfig = null;
46      protected static final String ALREADY_FILTERED = "loginfilter.already.filtered";
47      public static final String LOGIN_SUCCESS = "success";
48      public static final String LOGIN_FAILED = "failed";
49      public static final String LOGIN_ERROR = "error";
50      public static final String LOGIN_NOATTEMPT = null;
51      public static final String OS_AUTHSTATUS_KEY = "os_authstatus";
52      private SecurityConfig securityConfig = null;
53  
54      public BaseLoginFilter()
55      {
56          super();
57      }
58  
59      public void init(FilterConfig config)
60      {
61          // log.debug("LoginFilter.init");
62          this.filterConfig = config;
63      }
64  
65      public void destroy()
66      {
67          // log.debug("LoginFilter.destroy");
68          filterConfig = null;
69      }
70  
71      /**
72       * @deprecated Not needed in latest version of Servlet 2.3 API
73       */
74      public FilterConfig getFilterConfig()
75      {
76          return filterConfig;
77      }
78  
79      /**
80       * @deprecated Not needed in latest version of Servlet 2.3 API - replaced by init().
81       */
82      public void setFilterConfig(FilterConfig filterConfig)
83      {
84          if (filterConfig != null) //it seems that Orion 1.5.2 calls this with a null config.
85          {
86              init(filterConfig);
87          }
88      }
89  
90      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
91              throws IOException, ServletException
92      {
93          final boolean dbg = log.isDebugEnabled();
94          // wrap the request with one that returns the User as the Principal
95          req = new SecurityHttpRequestWrapper((HttpServletRequest) req);
96  
97          if (req.getAttribute(ALREADY_FILTERED) == null && getSecurityConfig().getController().isSecurityEnabled())
98          {
99              req.setAttribute(ALREADY_FILTERED, Boolean.TRUE);
100             req.setAttribute(OS_AUTHSTATUS_KEY, LOGIN_NOATTEMPT);
101 
102             HttpServletRequest request = (HttpServletRequest) req;
103             HttpServletResponse response = (HttpServletResponse) res;
104 
105             if (dbg)
106             {
107                 String url = request.getServletPath() +
108                              (request.getPathInfo() == null ? "" : request.getPathInfo()) +
109                              (request.getQueryString() == null ? "" : "?" + request.getQueryString());
110                 log.debug("____ Attempting login for : '" + url + "'");
111             }
112 
113             String status = login(request, response);
114             request.setAttribute(OS_AUTHSTATUS_KEY, status);
115             if (dbg)
116             {
117                 log.debug("Login completed - set " + OS_AUTHSTATUS_KEY + " attribute to '" + status + "'");
118             }
119 
120             // if we successfully logged in - look for an original URL to forward to
121             if (status == LOGIN_SUCCESS && redirectToOriginalDestination(request, response))
122             {
123                 return;
124             }
125             if(status == LOGIN_NOATTEMPT)
126             {
127                 issuePossibleRedirectIfUserIsAlreadyLoggedIn(req, request, response);
128 
129             }
130         }
131 
132         chain.doFilter(req, res);
133     }
134 
135     private void issuePossibleRedirectIfUserIsAlreadyLoggedIn(ServletRequest req, HttpServletRequest request, HttpServletResponse response)
136             throws IOException
137     {
138              /*Allow a redirect for an already logged in user under the following conditions
139             1: They are already logged in
140             2: They have os_destination in their request parameters
141             3: They do NOT have seraph_originalurl in their session. This avoids circular redirects when a User
142             tries to access a URL they do not have (role-based) privileges to view.*/
143         if (request.getParameterMap().get(RequestParameterConstants.OS_DESTINATION) != null)
144         {
145             Principal principal = getAuthenticator().getUser(request);
146             if (principal != null)
147             {
148                 HttpSession session = ((HttpServletRequest)req).getSession();
149                 if (session != null && session.getAttribute(SecurityConfigFactory.getInstance().getOriginalURLKey()) == null)
150                 {
151                     redirectToOriginalDestination(request, response);
152                 }
153             }
154         }
155     }
156 
157     /**
158      * Performs the actual authentication (if required) and returns the status code. Status code is chosen to be one of these:
159      * <p/>
160      * The possible statuses are:
161      * <ul>
162      * <li> BaseLoginFilter.LOGIN_SUCCESS - the login was processed, and user was logged in
163      * <li> BaseLoginFilter.LOGIN_FAILURE - the login was processed, the user gave a bad username or password
164      * <li> BaseLoginFilter.LOGIN_ERROR - the login was processed, an exception occurred trying to log the user in
165      * <li> BaseLoginFilter.LOGIN_NOATTEMPT - the login was no processed, no form parameters existed
166      * </ul>
167      *
168      * @param request
169      * @param response
170      * @return authentication status
171      */
172     public abstract String login(HttpServletRequest request, HttpServletResponse response);
173 
174     private class SecurityHttpRequestWrapper extends HttpServletRequestWrapper
175     {
176         private HttpServletRequest request;
177 
178         public SecurityHttpRequestWrapper(HttpServletRequest request)
179         {
180             super(request);
181             this.request = request;
182         }
183 
184         public String getRemoteUser()
185         {
186             Principal user = getUserPrincipal();
187             return (user == null) ? null : user.getName();
188         }
189 
190         public Principal getUserPrincipal()
191         {
192             return getSecurityConfig().getAuthenticator().getUser(request);
193         }
194     }
195 
196     /**
197      * Redirect the response to the original destination if present
198      *
199      * @param request
200      * @param response
201      * @return true if a redirect was needed and issued
202      * @throws IOException
203      */
204     protected boolean redirectToOriginalDestination(HttpServletRequest request, HttpServletResponse response)
205             throws IOException
206     {
207         String originalURL = (String) request.getSession().getAttribute(getSecurityConfig().getOriginalURLKey());
208         String destinationURL = request.getParameter(RequestParameterConstants.OS_DESTINATION);
209         String redirectURL = null;
210 
211         if (originalURL != null)
212         {
213             request.getSession().setAttribute(getSecurityConfig().getOriginalURLKey(), null);
214             redirectURL = originalURL;
215         }
216         else if (destinationURL != null)
217         {
218             redirectURL = destinationURL;
219         }
220 
221         if (redirectURL == null)
222         {
223             return false;
224         }
225 
226         if (!isAbsoluteUrl(redirectURL))
227         {
228             redirectURL = RedirectUtils.appendPathToContext(request.getContextPath(), redirectURL);
229         }
230 
231         if (log.isDebugEnabled())
232         {
233             log.debug("Logged In - redirecting to: " + redirectURL);
234         }
235 
236         response.sendRedirect(redirectURL);
237         return true;
238     }
239 
240     protected boolean isAbsoluteUrl(String url)
241     {
242         try
243         {
244             URI uri = new URI(url);
245             return uri.isAbsolute();
246         }
247         catch (URISyntaxException e)
248         {
249             return false;
250         }
251     }
252 
253     protected Authenticator getAuthenticator()
254     {
255         return getSecurityConfig().getAuthenticator();
256     }
257 
258     protected SecurityConfig getSecurityConfig()
259     {
260         if (securityConfig == null)
261         {
262             securityConfig = (SecurityConfig) filterConfig.getServletContext().getAttribute(SecurityConfig.STORAGE_KEY);
263         }
264         return securityConfig;
265     }
266 }