View Javadoc
1   /*
2    * JBoss, Home of Professional Open Source
3    * Copyright 2014, Red Hat, Inc. and/or its affiliates, and individual
4    * contributors by the @authors tag. See the copyright.txt in the
5    * distribution for a full listing of individual contributors.
6    *
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   * http://www.apache.org/licenses/LICENSE-2.0
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.jboss.web.tomcat.security;
18  
19  import java.io.IOException;
20  import java.security.Principal;
21  import java.util.StringTokenizer;
22  
23  import javax.management.JMException;
24  import javax.management.ObjectName;
25  import javax.servlet.http.Cookie;
26  import javax.servlet.http.HttpServletRequest;
27  import javax.servlet.http.HttpServletResponse;
28  
29  import org.apache.catalina.Realm;
30  import org.apache.catalina.Session;
31  import org.apache.catalina.authenticator.Constants;
32  import org.apache.catalina.connector.Request;
33  import org.apache.catalina.deploy.LoginConfig;
34  import org.jboss.logging.Logger;
35  
36  import org.jboss.as.web.security.ExtendedFormAuthenticator;
37  import org.jboss.security.plugins.HostThreadLocal;
38  
39  /**
40   * JBAS-2283: Provide custom header based authentication support
41   * 
42   * Header Authenticator that deals with userid from the request header Requires
43   * two attributes configured on the Tomcat Service - one for the http header
44   * denoting the authenticated identity and the other is the SESSION cookie
45   * 
46   * @author <a href="mailto:Anil.Saldhana@jboss.org">Anil Saldhana</a>
47   * @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
48   * @version $Revision$
49   * @since Sep 11, 2006
50   */
51  public class GenericHeaderAuthenticator extends ExtendedFormAuthenticator {
52      protected static Logger log = Logger.getLogger(GenericHeaderAuthenticator.class);
53  
54      // JBAS-4804: GenericHeaderAuthenticator injection of ssoid and
55      // sessioncookie name.
56      private String httpHeaderForSSOAuth = null;
57  
58      private String sessionCookieForSSOAuth = null;
59  
60      /**
61       * <p>
62       * Obtain the value of the <code>httpHeaderForSSOAuth</code> attribute. This
63       * attribute is used to indicate the request header ids that have to be
64       * checked in order to retrieve the SSO identity set by a third party
65       * security system.
66       * </p>
67       * 
68       * @return a <code>String</code> containing the value of the
69       *         <code>httpHeaderForSSOAuth</code> attribute.
70       */
71      public String getHttpHeaderForSSOAuth() {
72          return httpHeaderForSSOAuth;
73      }
74  
75      /**
76       * <p>
77       * Set the value of the <code>httpHeaderForSSOAuth</code> attribute. This
78       * attribute is used to indicate the request header ids that have to be
79       * checked in order to retrieve the SSO identity set by a third party
80       * security system.
81       * </p>
82       * 
83       * @param httpHeaderForSSOAuth
84       *            a <code>String</code> containing the value of the
85       *            <code>httpHeaderForSSOAuth</code> attribute.
86       */
87      public void setHttpHeaderForSSOAuth(String httpHeaderForSSOAuth) {
88          this.httpHeaderForSSOAuth = httpHeaderForSSOAuth;
89      }
90  
91      /**
92       * <p>
93       * Obtain the value of the <code>sessionCookieForSSOAuth</code> attribute.
94       * This attribute is used to indicate the names of the SSO cookies that may
95       * be present in the request object.
96       * </p>
97       * 
98       * @return a <code>String</code> containing the names (separated by a
99       *         <code>','</code>) of the SSO cookies that may have been set by a
100      *         third party security system in the request.
101      */
102     public String getSessionCookieForSSOAuth() {
103         return sessionCookieForSSOAuth;
104     }
105 
106     /**
107      * <p>
108      * Set the value of the <code>sessionCookieForSSOAuth</code> attribute. This
109      * attribute is used to indicate the names of the SSO cookies that may be
110      * present in the request object.
111      * </p>
112      * 
113      * @param sessionCookieForSSOAuth
114      *            a <code>String</code> containing the names (separated by a
115      *            <code>','</code>) of the SSO cookies that may have been set by
116      *            a third party security system in the request.
117      */
118     public void setSessionCookieForSSOAuth(String sessionCookieForSSOAuth) {
119         this.sessionCookieForSSOAuth = sessionCookieForSSOAuth;
120     }
121 
122     /**
123      * <p>
124      * Creates an instance of <code>GenericHeaderAuthenticator</code>.
125      * </p>
126      */
127     public GenericHeaderAuthenticator() {
128         super();
129     }
130 
131     public boolean authenticate(Request request, HttpServletResponse response,
132             LoginConfig config) throws IOException {
133         // set remote host value
134         HostThreadLocal.set(request.getRemoteAddr());
135 
136         log.trace("Authenticating user");
137 
138         Principal principal = request.getUserPrincipal();
139         if (principal != null) {
140             log.trace("Already authenticated '" + principal.getName() + "'");
141             return true;
142         }
143 
144         Realm realm = context.getRealm();
145         Session session = request.getSessionInternal(true);
146 
147         String username = getUserId(request);
148         String password = getSessionCookie(request);
149 
150         // Check if there is sso id as well as sessionkey
151         if (username == null || password == null) {
152             log.trace("Username is null or password(sessionkey) is null:fallback to form auth");
153             return super.authenticate(request, response, config);
154         }
155         principal = realm.authenticate(username, password);
156 
157         if (principal == null) {
158             forwardToErrorPage(request, response, config);
159             return false;
160         }
161 
162         session.setNote(Constants.SESS_USERNAME_NOTE, username);
163         session.setNote(Constants.SESS_PASSWORD_NOTE, password);
164         request.setUserPrincipal(principal);
165 
166         register(request, response, principal, HttpServletRequest.FORM_AUTH, username, password);
167         return true;
168     }
169 
170     /**
171      * Get the username from the request header
172      * 
173      * @param request
174      * @return
175      */
176     protected String getUserId(Request request) {
177         String ssoid = null;
178         // We can have a comma-separated ids
179         String ids = "";
180         try {
181             ids = this.getIdentityHeaderId();
182         } catch (JMException e) {
183             log.trace("getUserId exception", e);
184         }
185         if (ids == null || ids.length() == 0)
186             throw new IllegalStateException(
187                     "Http headers configuration in tomcat service missing");
188 
189         StringTokenizer st = new StringTokenizer(ids, ",");
190         while (st.hasMoreTokens()) {
191             ssoid = request.getHeader(st.nextToken());
192             if (ssoid != null)
193                 break;
194         }
195         log.trace("SSOID-" + ssoid);
196         return ssoid;
197     }
198 
199     /**
200      * Obtain the session cookie from the request
201      * 
202      * @param request
203      * @return
204      */
205     protected String getSessionCookie(Request request) {
206         Cookie[] cookies = request.getCookies();
207         log.trace("Cookies:" + cookies);
208         int numCookies = cookies != null ? cookies.length : 0;
209 
210         // We can have comma-separated ids
211         String ids = "";
212         try {
213             ids = this.getSessionCookieId();
214             log.trace("Session Cookie Ids=" + ids);
215         } catch (JMException e) {
216             log.trace("checkSessionCookie exception", e);
217         }
218         if (ids == null || ids.length() == 0)
219             throw new IllegalStateException(
220                     "Session cookies configuration in tomcat service missing");
221 
222         StringTokenizer st = new StringTokenizer(ids, ",");
223         while (st.hasMoreTokens()) {
224             String cookieToken = st.nextToken();
225             String val = getCookieValue(cookies, numCookies, cookieToken);
226             if (val != null)
227                 return val;
228         }
229         log.trace("Session Cookie not found");
230         return null;
231     }
232 
233     /**
234      * Get the configured header identity id in the tomcat service
235      * 
236      * @return
237      * @throws JMException
238      */
239     protected String getIdentityHeaderId() throws JMException {
240         if (this.httpHeaderForSSOAuth != null)
241             return this.httpHeaderForSSOAuth;
242         return (String) mserver.getAttribute(new ObjectName(
243                 "jboss.web:service=WebServer"), "HttpHeaderForSSOAuth");
244     }
245 
246     /**
247      * Get the configured session cookie id in the tomcat service
248      * 
249      * @return
250      * @throws JMException
251      */
252     protected String getSessionCookieId() throws JMException {
253         if (this.sessionCookieForSSOAuth != null)
254             return this.sessionCookieForSSOAuth;
255         return (String) mserver.getAttribute(new ObjectName(
256                 "jboss.web:service=WebServer"), "SessionCookieForSSOAuth");
257     }
258 
259     /**
260      * Get the value of a cookie if the name matches the token
261      * 
262      * @param cookies
263      *            array of cookies
264      * @param numCookies
265      *            number of cookies in the array
266      * @param token
267      *            Key
268      * @return value of cookie
269      */
270     protected String getCookieValue(Cookie[] cookies, int numCookies,
271             String token) {
272         for (int i = 0; i < numCookies; i++) {
273             Cookie cookie = cookies[i];
274             log.trace("Matching cookieToken:" + token + " with cookie name="
275                     + cookie.getName());
276             if (token.equals(cookie.getName())) {
277                 log.trace("Cookie-" + token + " value=" + cookie.getValue());
278                 return cookie.getValue();
279             }
280         }
281         return null;
282     }
283 }