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.InvocationTargetException; 025import java.util.Arrays; 026import java.util.List; 027import java.util.Map; 028 029import javax.servlet.http.HttpServletRequest; 030import javax.servlet.http.HttpSession; 031 032import org.granite.context.GraniteContext; 033import org.granite.logging.Logger; 034import org.granite.messaging.webapp.HttpGraniteContext; 035import org.springframework.beans.factory.BeanFactoryUtils; 036import org.springframework.context.ApplicationContext; 037import org.springframework.security.AbstractAuthenticationManager; 038import org.springframework.security.AccessDeniedException; 039import org.springframework.security.Authentication; 040import org.springframework.security.AuthenticationException; 041import org.springframework.security.BadCredentialsException; 042import org.springframework.security.GrantedAuthority; 043import org.springframework.security.context.HttpSessionContextIntegrationFilter; 044import org.springframework.security.context.SecurityContext; 045import org.springframework.security.context.SecurityContextHolder; 046import org.springframework.security.providers.UsernamePasswordAuthenticationToken; 047import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken; 048import org.springframework.security.userdetails.UsernameNotFoundException; 049import org.springframework.web.context.support.WebApplicationContextUtils; 050 051/** 052 * @author Bouiaw 053 * @author wdrai 054 */ 055public class SpringSecurityService extends AbstractSecurityService { 056 057 private static final Logger log = Logger.getLogger(SpringSecurityService.class); 058 059 private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied"; 060 061 private AbstractSpringSecurityInterceptor securityInterceptor = null; 062 063 064 public SpringSecurityService() { 065 log.debug("Starting Spring Security Service!"); 066 } 067 068 public void configure(Map<String, String> params) { 069 log.debug("Configuring with parameters (NOOP) %s: ", params); 070 } 071 072 public void setSecurityInterceptor(AbstractSpringSecurityInterceptor securityInterceptor) { 073 this.securityInterceptor = securityInterceptor; 074 } 075 076 public void login(Object credentials, String charset) { 077 List<String> decodedCredentials = Arrays.asList(decodeBase64Credentials(credentials, charset)); 078 079 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 080 HttpServletRequest httpRequest = context.getRequest(); 081 082 String user = decodedCredentials.get(0); 083 String password = decodedCredentials.get(1); 084 Authentication auth = new UsernamePasswordAuthenticationToken(user, password); 085 086 ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext( 087 httpRequest.getSession().getServletContext() 088 ); 089 if (ctx != null) { 090 AbstractAuthenticationManager authenticationManager = 091 BeanFactoryUtils.beanOfTypeIncludingAncestors(ctx, AbstractAuthenticationManager.class); 092 try { 093 Authentication authentication = authenticationManager.authenticate(auth); 094 SecurityContext securityContext = SecurityContextHolder.getContext(); 095 securityContext.setAuthentication(authentication); 096 SecurityContextHolder.setContext(securityContext); 097 saveSecurityContextInSession(securityContext, 0); 098 099 endLogin(credentials, charset); 100 } 101 catch (AuthenticationException e) { 102 handleAuthenticationExceptions(e); 103 } 104 } 105 106 log.debug("User %s logged in", user); 107 } 108 109 protected void handleAuthenticationExceptions(AuthenticationException e) { 110 if (e instanceof BadCredentialsException || e instanceof UsernameNotFoundException) 111 throw SecurityServiceException.newInvalidCredentialsException(e.getMessage()); 112 113 throw SecurityServiceException.newAuthenticationFailedException(e.getMessage()); 114 } 115 116 public Object authorize(AbstractSecurityContext context) throws Exception { 117 log.debug("Authorize: %s", context); 118 log.debug("Is %s secured? %b", context.getDestination().getId(), context.getDestination().isSecured()); 119 120 startAuthorization(context); 121 122 HttpGraniteContext graniteContext = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 123 124 Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 125 126 SecurityContext securityContextBefore = null; 127 int securityContextHashBefore = 0; 128 if (graniteContext.getRequest().getAttribute(FILTER_APPLIED) == null) { 129 securityContextBefore = loadSecurityContextFromSession(); 130 if (securityContextBefore == null) 131 securityContextBefore = SecurityContextHolder.getContext(); 132 else 133 securityContextHashBefore = securityContextBefore.hashCode(); 134 SecurityContextHolder.setContext(securityContextBefore); 135 authentication = securityContextBefore.getAuthentication(); 136 } 137 138 if (context.getDestination().isSecured()) { 139 if (!isAuthenticated(authentication) || authentication instanceof AnonymousAuthenticationToken) { 140 log.debug("Is not authenticated!"); 141 throw SecurityServiceException.newNotLoggedInException("User not logged in"); 142 } 143 if (!userCanAccessService(context, authentication)) { 144 log.debug("Access denied for: %s", authentication.getName()); 145 throw SecurityServiceException.newAccessDeniedException("User not in required role"); 146 } 147 } 148 149 try { 150 Object returnedObject = securityInterceptor != null 151 ? securityInterceptor.invoke(context) 152 : endAuthorization(context); 153 154 return returnedObject; 155 } 156 catch (AccessDeniedException e) { 157 throw SecurityServiceException.newAccessDeniedException(e.getMessage()); 158 } 159 catch (InvocationTargetException e) { 160 handleAuthorizationExceptions(e); 161 throw e; 162 } 163 finally { 164 if (graniteContext.getRequest().getAttribute(FILTER_APPLIED) == null) { 165 // Do this only when not already filtered by Spring Security 166 SecurityContext securityContextAfter = SecurityContextHolder.getContext(); 167 SecurityContextHolder.clearContext(); 168 saveSecurityContextInSession(securityContextAfter, securityContextHashBefore); 169 } 170 } 171 } 172 173 public void logout() { 174 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 175 HttpSession session = context.getSession(false); 176 if (session != null && session.getAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY) != null) 177 session.invalidate(); 178 179 SecurityContextHolder.clearContext(); 180 } 181 182 protected boolean isUserInRole(Authentication authentication, String role) { 183 for (GrantedAuthority ga : authentication.getAuthorities()) { 184 if (ga.getAuthority().matches(role)) 185 return true; 186 } 187 return false; 188 } 189 190 protected boolean isAuthenticated(Authentication authentication) { 191 return authentication != null && authentication.isAuthenticated(); 192 } 193 194 protected boolean userCanAccessService(AbstractSecurityContext context, Authentication authentication) { 195 log.debug("Is authenticated as: %s", authentication.getName()); 196 197 for (String role : context.getDestination().getRoles()) { 198 if (isUserInRole(authentication, role)) { 199 log.debug("Allowed access to %s in role %s", authentication.getName(), role); 200 return true; 201 } 202 log.debug("Access denied for %s not in role %s", authentication.getName(), role); 203 } 204 return false; 205 } 206 207 protected SecurityContext loadSecurityContextFromSession() { 208 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 209 HttpServletRequest request = context.getRequest(); 210 return (SecurityContext)request.getSession().getAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY); 211 } 212 213 protected void saveSecurityContextInSession(SecurityContext securityContext, int securityContextHashBefore) { 214 if (securityContext.hashCode() != securityContextHashBefore && 215 !(securityContext.getAuthentication() instanceof AnonymousAuthenticationToken)) { 216 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 217 HttpServletRequest request = context.getRequest(); 218 request.getSession().setAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY, securityContext); 219 } 220 } 221 222 protected void handleAuthorizationExceptions(InvocationTargetException e) { 223 for (Throwable t = e; t != null; t = t.getCause()) { 224 // Don't create a dependency to javax.ejb in SecurityService... 225 if (t instanceof SecurityException || 226 t instanceof AccessDeniedException || 227 "javax.ejb.EJBAccessException".equals(t.getClass().getName())) 228 throw SecurityServiceException.newAccessDeniedException(t.getMessage()); 229 } 230 } 231 232}