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.Engine; 034import org.apache.catalina.Realm; 035import org.apache.catalina.Server; 036import org.apache.catalina.ServerFactory; 037import org.apache.catalina.Service; 038import org.apache.catalina.Session; 039import org.apache.catalina.authenticator.Constants; 040import org.apache.catalina.connector.RequestFacade; 041import org.apache.coyote.tomcat5.CoyoteRequest; 042import org.granite.context.GraniteContext; 043import org.granite.messaging.webapp.HttpGraniteContext; 044 045/** 046 * @author Franck WOLFF 047 */ 048public class GlassFishSecurityService extends AbstractSecurityService { 049 050 private final Field requestField; 051 private Engine engine = null; 052 053 public GlassFishSecurityService() { 054 super(); 055 try { 056 // We need to access the org.apache.catalina.connector.Request field from 057 // a org.apache.catalina.connector.RequestFacade. Unfortunately there is 058 // no public getter for this field (and I don't want to create a Valve)... 059 requestField = RequestFacade.class.getDeclaredField("request"); 060 requestField.setAccessible(true); 061 } catch (Exception e) { 062 throw new RuntimeException("Could not get 'request' field in Tomcat RequestFacade", e); 063 } 064 } 065 066 protected Field getRequestField() { 067 return requestField; 068 } 069 070 protected Engine getEngine() { 071 return engine; 072 } 073 074 public void configure(Map<String, String> params) { 075 String serviceId = params.get("service"); 076 077 Server server = ServerFactory.getServer(); 078 if (server == null) 079 throw new NullPointerException("Could not get Tomcat server"); 080 081 Service service = null; 082 if (serviceId != null) 083 service = server.findService(serviceId); 084 else { 085 Service[] services = server.findServices(); 086 if (services != null && services.length > 0) 087 service = services[0]; 088 } 089 if (service == null) 090 throw new NullPointerException("Could not find Tomcat service for: " + (serviceId != null ? serviceId : "(default)")); 091 092 engine = (Engine)service.getContainer(); 093 if (engine == null) 094 throw new NullPointerException("Could not find Tomcat container for: " + (serviceId != null ? serviceId : "(default)")); 095 } 096 097 public void login(Object credentials, String charset) throws SecurityServiceException { 098 String[] decoded = decodeBase64Credentials(credentials, charset); 099 100 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 101 HttpServletRequest httpRequest = context.getRequest(); 102 103 CoyoteRequest request = getRequest(httpRequest); 104 Realm realm = request.getContext().getRealm(); 105 Principal principal = realm.authenticate(decoded[0], decoded[1]); 106 if (principal == null) 107 throw SecurityServiceException.newInvalidCredentialsException("Wrong username or password"); 108 109 request.setAuthType(AUTH_TYPE); 110 request.setUserPrincipal(principal); 111 112 Session session = request.getSessionInternal(); 113 session.setAuthType(AUTH_TYPE); 114 session.setPrincipal(principal); 115 session.setNote(Constants.SESS_USERNAME_NOTE, decoded[0]); 116 session.setNote(Constants.SESS_PASSWORD_NOTE, decoded[1]); 117 118 endLogin(credentials, charset); 119 } 120 121 public Object authorize(AbstractSecurityContext context) throws Exception { 122 123 startAuthorization(context); 124 125 HttpGraniteContext graniteContext = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 126 HttpServletRequest httpRequest = graniteContext.getRequest(); 127 CoyoteRequest request = getRequest(httpRequest); 128 Session session = request.getSessionInternal(false); 129 130 Principal principal = null; 131 if (session != null) { 132 request.setAuthType(session.getAuthType()); 133 principal = session.getPrincipal(); 134 if (principal == null && tryRelogin()) 135 principal = session.getPrincipal(); 136 } 137 request.setUserPrincipal(principal); 138 139 if (context.getDestination().isSecured()) { 140 if (principal == null) { 141 if (httpRequest.getRequestedSessionId() != null) { 142 HttpSession httpSession = httpRequest.getSession(false); 143 if (httpSession == null || httpRequest.getRequestedSessionId().equals(httpSession.getId())) 144 throw SecurityServiceException.newSessionExpiredException("Session expired"); 145 } 146 throw SecurityServiceException.newNotLoggedInException("User not logged in"); 147 } 148 149 boolean accessDenied = true; 150 for (String role : context.getDestination().getRoles()) { 151 if (httpRequest.isUserInRole(role)) { 152 accessDenied = false; 153 break; 154 } 155 } 156 if (accessDenied) 157 throw SecurityServiceException.newAccessDeniedException("User not in required role"); 158 } 159 160 try { 161 return endAuthorization(context); 162 } catch (InvocationTargetException e) { 163 for (Throwable t = e; t != null; t = t.getCause()) { 164 // Don't create a dependency to javax.ejb in SecurityService... 165 if (t instanceof SecurityException || 166 "javax.ejb.EJBAccessException".equals(t.getClass().getName())) 167 throw SecurityServiceException.newAccessDeniedException(t.getMessage()); 168 } 169 throw e; 170 } 171 } 172 173 public void logout() throws SecurityServiceException { 174 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 175 176 Session session = getSession(context.getRequest(), false); 177 if (session != null && session.getPrincipal() != null) { 178 session.setAuthType(null); 179 session.setPrincipal(null); 180 session.removeNote(Constants.SESS_USERNAME_NOTE); 181 session.removeNote(Constants.SESS_PASSWORD_NOTE); 182 183 endLogout(); 184 185 session.expire(); 186 } 187 } 188 189 protected Principal getPrincipal(HttpServletRequest httpRequest) { 190 CoyoteRequest request = getRequest(httpRequest); 191 Session session = request.getSessionInternal(false); 192 return (session != null ? session.getPrincipal() : null); 193 } 194 195 protected Session getSession(HttpServletRequest httpRequest, boolean create) { 196 CoyoteRequest request = getRequest(httpRequest); 197 return request.getSessionInternal(create); 198 } 199 200 protected CoyoteRequest getRequest(HttpServletRequest request) { 201 while (request instanceof HttpServletRequestWrapper) 202 request = (HttpServletRequest)((HttpServletRequestWrapper)request).getRequest(); 203 try { 204 return (CoyoteRequest)requestField.get(request); 205 } catch (Exception e) { 206 throw new RuntimeException("Could not get tomcat request", e); 207 } 208 } 209 210 protected Realm getRealm(HttpServletRequest request) { 211 CoyoteRequest creq = getRequest(request); 212 return creq.getContext().getRealm(); 213 } 214}