View Javadoc

1   package com.atlassian.seraph.util;
2   
3   import com.atlassian.seraph.config.SecurityConfig;
4   import com.atlassian.seraph.config.SecurityConfigFactory;
5   import com.atlassian.seraph.filter.SecurityFilter;
6   import com.atlassian.seraph.RequestParameterConstants;
7   
8   import javax.servlet.http.HttpServletRequest;
9   import java.net.URLEncoder;
10  
11  /**
12   * Utilities for login link redirection.
13   */
14  public class RedirectUtils
15  {
16      /**
17       * Returns a login URL that would log the user in to access resource indicated by <code>request</code>.
18       * <p/>
19       * For instance, if <code>request</code> is for protected path "/browse/JRA-123" and the user must login before
20       * accessing this resource, this method might return "/login.jsp?os_destination=%2Fbrowse%2FJRA-123". Presumably the
21       * login.jsp page will redirect back to 'os_destination' once logged in.
22       * <p/>
23       * The returned path is derived from the <code>login.url</code> parameter in seraph-config.xml, which in the example above would be
24       * "/login.jsp?os_destination={originalurl}". The '${originalurl}' token is replaced at runtime with a relative
25       * or absolute path to the original resource requested by <code>request</code> ('/browse/JRA-123').
26       * <p/>
27       * Both the returned URL and the ${originalurl} replacement URL may be absolute or root-relative, depending on whether
28       * the seraph-config.xml <code>login.url</code> parameter is.  This allows for redirection to external <acronym title="Single Sign-on">SSO</acronym>
29       * apps, which are passed an absolute path to the originally requested resource.
30       * <p/>
31       * No actual permission checks are performed to determine whether the user needs to log in to access the resource. The
32       * caller is assumed to have done this before calling this method.
33       *
34       * @param request The original request made by the user for a resource.
35       * @return A root-relative or absolute URL of a login link that would log the user in to access the resource.
36       */
37      public static String getLoginUrl(HttpServletRequest request)
38      {
39          SecurityConfig securityConfig = SecurityConfigFactory.getInstance();
40          String loginURL = securityConfig.getLoginURL();
41          return getLoginURL(loginURL, request);
42      }
43  
44      /**
45       * Returns a login URL that would log the user in to access resource indicated by <code>request</code>.
46       * Identical to {@link #getLoginUrl(javax.servlet.http.HttpServletRequest)}, except uses the 'link.login.url'
47       * parameter in seraph-config.xml instead of 'login.url', which allows for different login pages depending on whether
48       * invoked from a link ("link.login.url") or from a servlet filter that intercepted a request ("login.url").
49       *
50       * @see #getLoginUrl(javax.servlet.http.HttpServletRequest) for parameters, etc
51       */
52      public static String getLinkLoginURL(HttpServletRequest request)
53      {
54          SecurityConfig securityConfig = SecurityConfigFactory.getInstance();
55          String loginURL = securityConfig.getLinkLoginURL();
56          return getLoginURL(loginURL, request);
57      }
58  
59  
60      private static String getLoginURL(String loginURL, HttpServletRequest request)
61      {
62          boolean externalLoginLink = isExternalLoginLink(loginURL);
63          loginURL = replaceOriginalURL(loginURL, request, externalLoginLink);
64          if (externalLoginLink)
65          {
66              return loginURL;
67          }
68          else
69          {
70              return request.getContextPath() + loginURL;
71          }
72      }
73  
74      private static boolean isExternalLoginLink(String loginURL)
75      {
76          return (loginURL.indexOf("://") != -1);
77      }
78  
79      /**
80       * Replace ${originalurl} token in a string with a URL for a Request.
81       */
82      private static String replaceOriginalURL(final String loginURL, final HttpServletRequest request, boolean external)
83      {
84          final int i = loginURL.indexOf("${originalurl}");
85          if (i != -1)
86          {
87              final String originalURL = getOriginalURL(request, external);
88              String osDest = request.getParameter(RequestParameterConstants.OS_DESTINATION);
89              return loginURL.substring(0, i) + ((osDest != null) ? URLEncoder.encode(osDest) : URLEncoder.encode(originalURL) )+ loginURL.substring(i + "${originalurl}".length());
90          }
91          else
92              return loginURL;
93      }
94  
95      /**
96       * Recreate a URL from a Request.
97       */
98      private static String getOriginalURL(HttpServletRequest request, boolean external)
99      {
100         String originalURL = (String) request.getAttribute(SecurityFilter.ORIGINAL_URL);
101         if (originalURL != null)
102         {
103             if (external)
104                 return getServerNameAndPath(request) + originalURL;
105             else
106                 return originalURL;
107         }
108 
109         if (external)
110             return request.getRequestURL() + (request.getQueryString() == null ? "" : "?" + request.getQueryString());
111         else
112             return request.getServletPath() +
113                 (request.getPathInfo() == null ? "" : request.getPathInfo()) +
114                 (request.getQueryString() == null ? "" : "?" + request.getQueryString());
115 
116     }
117 
118     /**
119      * Reconstruct the request URL from a HttpServletRequest.
120      */
121     public static String getServerNameAndPath(HttpServletRequest request)
122     {
123         StringBuffer buf = new StringBuffer();
124         buf.append(request.getScheme()).
125                 append("://").
126                 append(request.getServerName());
127         if (! (("http".equals(request.getScheme()) && request.getServerPort() == 80) || ("https".equals(request.getScheme()) && request.getServerPort() == 443)))
128         {
129             buf.append(":").append(request.getServerPort());
130         }
131         buf.append(request.getContextPath());
132         return buf.toString();
133     }
134 
135     /**
136      * Checks whether the request contains a basicAuth parameter in its queryString
137      */
138     public static boolean isBasicAuthentication(HttpServletRequest request, String basicAuth)
139     {
140         String queryString = request.getQueryString();
141         if (queryString == null || queryString.equals(""))
142             return false;
143 
144         String authString = basicAuth + "=" + SecurityConfig.BASIC_AUTH;
145         if (queryString.indexOf(authString) != -1)
146             return true;
147         else
148             return false;
149     }
150 
151     /**
152      * Appends the path to the context, dealing with any missing slashes properly.
153      * Does NOT resolve the path using URL resolution rules. Does NOT attempt to
154      * normalise the resulting URL.
155      *
156      * @param context a context path as returned by HttpServletRequest.getContextPath, e.g. "/confluence".
157      * If null, it will be treated as the default context ("").
158      * @param path a path to be appended to the context, e.g. "/homepage.action".
159      * If this is null, the context will be returned if it is non-null, otherwise the empty string will be returned.
160      * @return a URL of the given path within the context, e.g. "/confluence/homepage.action".
161      */
162     public static String appendPathToContext(String context, String path)
163     {
164         if (context == null) context = "";
165         if (path == null) return context;
166 
167         StringBuffer result = new StringBuffer(context);
168         if (!context.endsWith("/"))
169             result.append("/");
170 
171         String pathToAppend = path;
172         if (pathToAppend.startsWith("/"))
173             pathToAppend = pathToAppend.substring(1);
174 
175         result.append(pathToAppend);
176         return result.toString();
177     }
178 }