001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.messaging.service.security; 023 024import java.lang.reflect.Field; 025import java.lang.reflect.InvocationTargetException; 026import java.security.Principal; 027import java.util.Map; 028 029import javax.servlet.http.HttpServletRequest; 030import javax.servlet.http.HttpServletRequestWrapper; 031import javax.servlet.http.HttpSession; 032 033import org.apache.catalina.Context; 034import org.apache.catalina.Engine; 035import org.apache.catalina.Host; 036import org.apache.catalina.Realm; 037import org.apache.catalina.Server; 038import org.apache.catalina.ServerFactory; 039import org.apache.catalina.Service; 040import org.apache.catalina.Session; 041import org.apache.catalina.authenticator.Constants; 042import org.apache.catalina.connector.Request; 043import org.apache.catalina.connector.RequestFacade; 044import org.granite.context.GraniteContext; 045import org.granite.messaging.webapp.HttpGraniteContext; 046 047/** 048 * @author Franck WOLFF 049 */ 050public class TomcatSecurityService extends AbstractSecurityService { 051 052 private final Field requestField; 053 private Engine engine = null; 054 055 public TomcatSecurityService() { 056 super(); 057 try { 058 // We need to access the org.apache.catalina.connector.Request field from 059 // a org.apache.catalina.connector.RequestFacade. Unfortunately there is 060 // no public getter for this field (and I don't want to create a Valve)... 061 requestField = RequestFacade.class.getDeclaredField("request"); 062 requestField.setAccessible(true); 063 } catch (Exception e) { 064 throw new RuntimeException("Could not get 'request' field in Tomcat RequestFacade", e); 065 } 066 } 067 068 protected Field getRequestField() { 069 return requestField; 070 } 071 072 protected Engine getEngine() { 073 return engine; 074 } 075 076 public void configure(Map<String, String> params) { 077 String serviceId = params.get("service"); 078 079 Server server = ServerFactory.getServer(); 080 if (server == null) 081 throw new NullPointerException("Could not get Tomcat server"); 082 083 Service service = null; 084 if (serviceId != null) 085 service = server.findService(serviceId); 086 else { 087 Service[] services = server.findServices(); 088 if (services != null && services.length > 0) 089 service = services[0]; 090 } 091 if (service == null) 092 throw new NullPointerException("Could not find Tomcat service for: " + (serviceId != null ? serviceId : "(default)")); 093 094 engine = (Engine)service.getContainer(); 095 if (engine == null) 096 throw new NullPointerException("Could not find Tomcat container for: " + (serviceId != null ? serviceId : "(default)")); 097 } 098 099 public void login(Object credentials, String charset) throws SecurityServiceException { 100 String[] decoded = decodeBase64Credentials(credentials, charset); 101 102 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 103 HttpServletRequest httpRequest = context.getRequest(); 104 Realm realm = getRealm(httpRequest); 105 106 Principal principal = realm.authenticate(decoded[0], decoded[1]); 107 if (principal == null) 108 throw SecurityServiceException.newInvalidCredentialsException("Wrong username or password"); 109 110 Request request = getRequest(httpRequest); 111 request.setAuthType(AUTH_TYPE); 112 request.setUserPrincipal(principal); 113 114 Session session = request.getSessionInternal(true); 115 session.setAuthType(AUTH_TYPE); 116 session.setPrincipal(principal); 117 session.setNote(Constants.SESS_USERNAME_NOTE, decoded[0]); 118 session.setNote(Constants.SESS_PASSWORD_NOTE, decoded[1]); 119 120 endLogin(credentials, charset); 121 } 122 123 public Object authorize(AbstractSecurityContext context) throws Exception { 124 125 startAuthorization(context); 126 127 HttpGraniteContext graniteContext = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 128 HttpServletRequest httpRequest = graniteContext.getRequest(); 129 Request request = getRequest(httpRequest); 130 Session session = request.getSessionInternal(false); 131 132 Principal principal = null; 133 if (session != null) { 134 request.setAuthType(session.getAuthType()); 135 principal = session.getPrincipal(); 136 if (principal == null && tryRelogin()) 137 principal = session.getPrincipal(); 138 } 139 140 request.setUserPrincipal(principal); 141 142 if (context.getDestination().isSecured()) { 143 if (principal == null) { 144 if (httpRequest.getRequestedSessionId() != null) { 145 HttpSession httpSession = httpRequest.getSession(false); 146 if (httpSession == null || httpRequest.getRequestedSessionId().equals(httpSession.getId())) 147 throw SecurityServiceException.newSessionExpiredException("Session expired"); 148 } 149 throw SecurityServiceException.newNotLoggedInException("User not logged in"); 150 } 151 152 boolean accessDenied = true; 153 for (String role : context.getDestination().getRoles()) { 154 if (request.isUserInRole(role)) { 155 accessDenied = false; 156 break; 157 } 158 } 159 if (accessDenied) 160 throw SecurityServiceException.newAccessDeniedException("User not in required role"); 161 } 162 163 try { 164 return endAuthorization(context); 165 } catch (InvocationTargetException e) { 166 for (Throwable t = e; t != null; t = t.getCause()) { 167 // Don't create a dependency to javax.ejb in SecurityService... 168 if (t instanceof SecurityException || 169 "javax.ejb.EJBAccessException".equals(t.getClass().getName())) 170 throw SecurityServiceException.newAccessDeniedException(t.getMessage()); 171 } 172 throw e; 173 } 174 } 175 176 public void logout() throws SecurityServiceException { 177 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 178 179 Session session = getSession(context.getRequest(), false); 180 if (session != null && session.getPrincipal() != null) { 181 session.setAuthType(null); 182 session.setPrincipal(null); 183 session.removeNote(Constants.SESS_USERNAME_NOTE); 184 session.removeNote(Constants.SESS_PASSWORD_NOTE); 185 186 endLogout(); 187 188 session.expire(); 189 } 190 } 191 192 protected Principal getPrincipal(HttpServletRequest httpRequest) { 193 Request request = getRequest(httpRequest); 194 Session session = request.getSessionInternal(false); 195 return (session != null ? session.getPrincipal() : null); 196 } 197 198 protected Session getSession(HttpServletRequest httpRequest, boolean create) { 199 Request request = getRequest(httpRequest); 200 return request.getSessionInternal(create); 201 } 202 203 protected Request getRequest(HttpServletRequest request) { 204 while (request instanceof HttpServletRequestWrapper) 205 request = (HttpServletRequest)((HttpServletRequestWrapper)request).getRequest(); 206 try { 207 return (Request)requestField.get(request); 208 } catch (Exception e) { 209 throw new RuntimeException("Could not get tomcat request", e); 210 } 211 } 212 213 protected Context getContext(HttpServletRequest request) { 214 String serverName = request.getServerName(); 215 String contextPath = request.getContextPath(); 216 217 Host host = (Host)engine.findChild(serverName); 218 if (host == null) { 219 // if it cannot find host, then use the default host. 220 host = (Host)engine.findChild(engine.getDefaultHost()); 221 if (host == null) 222 throw new NullPointerException("Could not find Tomcat host for: " + serverName + " or: " + engine.getDefaultHost()); 223 } 224 225 Context context = (Context)host.findChild(contextPath); 226 if (context == null) 227 throw new NullPointerException("Could not find Tomcat context for: " + contextPath); 228 return context; 229 } 230 231 protected Realm getRealm(HttpServletRequest request) { 232 Context context = getContext(request); 233 Realm realm = context.getRealm(); 234 if (realm == null) 235 throw new NullPointerException("Could not find Tomcat realm for: " + context.getPath()); 236 237 return realm; 238 } 239}