001/* 002 GRANITE DATA SERVICES 003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S. 004 005 This file is part of Granite Data Services. 006 007 Granite Data Services is free software; you can redistribute it and/or modify 008 it under the terms of the GNU Library General Public License as published by 009 the Free Software Foundation; either version 2 of the License, or (at your 010 option) any later version. 011 012 Granite Data Services is distributed in the hope that it will be useful, but 013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License 015 for more details. 016 017 You should have received a copy of the GNU Library General Public License 018 along with this library; if not, see <http://www.gnu.org/licenses/>. 019*/ 020 021package org.granite.messaging.service.security; 022 023import java.lang.reflect.InvocationTargetException; 024import java.util.Arrays; 025import java.util.List; 026import java.util.Map; 027 028import javax.servlet.http.HttpServletRequest; 029import javax.servlet.http.HttpSession; 030 031import org.granite.context.GraniteContext; 032import org.granite.logging.Logger; 033import org.granite.messaging.webapp.HttpGraniteContext; 034import org.springframework.beans.factory.BeanFactoryUtils; 035import org.springframework.context.ApplicationContext; 036import org.springframework.security.AbstractAuthenticationManager; 037import org.springframework.security.AccessDeniedException; 038import org.springframework.security.Authentication; 039import org.springframework.security.AuthenticationException; 040import org.springframework.security.BadCredentialsException; 041import org.springframework.security.GrantedAuthority; 042import org.springframework.security.context.HttpSessionContextIntegrationFilter; 043import org.springframework.security.context.SecurityContext; 044import org.springframework.security.context.SecurityContextHolder; 045import org.springframework.security.providers.UsernamePasswordAuthenticationToken; 046import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken; 047import org.springframework.security.userdetails.UsernameNotFoundException; 048import org.springframework.web.context.support.WebApplicationContextUtils; 049 050/** 051 * @author Bouiaw 052 * @author wdrai 053 */ 054public class SpringSecurityService extends AbstractSecurityService { 055 056 private static final Logger log = Logger.getLogger(SpringSecurityService.class); 057 058 private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied"; 059 060 private AbstractSpringSecurityInterceptor securityInterceptor = null; 061 062 063 public SpringSecurityService() { 064 log.debug("Starting Spring Security Service!"); 065 } 066 067 public void configure(Map<String, String> params) { 068 log.debug("Configuring with parameters (NOOP) %s: ", params); 069 } 070 071 public void setSecurityInterceptor(AbstractSpringSecurityInterceptor securityInterceptor) { 072 this.securityInterceptor = securityInterceptor; 073 } 074 075 public void login(Object credentials, String charset) { 076 List<String> decodedCredentials = Arrays.asList(decodeBase64Credentials(credentials, charset)); 077 078 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 079 HttpServletRequest httpRequest = context.getRequest(); 080 081 String user = decodedCredentials.get(0); 082 String password = decodedCredentials.get(1); 083 Authentication auth = new UsernamePasswordAuthenticationToken(user, password); 084 085 ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext( 086 httpRequest.getSession().getServletContext() 087 ); 088 if (ctx != null) { 089 AbstractAuthenticationManager authenticationManager = 090 BeanFactoryUtils.beanOfTypeIncludingAncestors(ctx, AbstractAuthenticationManager.class); 091 try { 092 Authentication authentication = authenticationManager.authenticate(auth); 093 SecurityContext securityContext = SecurityContextHolder.getContext(); 094 securityContext.setAuthentication(authentication); 095 SecurityContextHolder.setContext(securityContext); 096 saveSecurityContextInSession(securityContext, 0); 097 098 endLogin(credentials, charset); 099 } 100 catch (AuthenticationException e) { 101 handleAuthenticationExceptions(e); 102 } 103 } 104 105 log.debug("User %s logged in", user); 106 } 107 108 protected void handleAuthenticationExceptions(AuthenticationException e) { 109 if (e instanceof BadCredentialsException || e instanceof UsernameNotFoundException) 110 throw SecurityServiceException.newInvalidCredentialsException(e.getMessage()); 111 112 throw SecurityServiceException.newAuthenticationFailedException(e.getMessage()); 113 } 114 115 public Object authorize(AbstractSecurityContext context) throws Exception { 116 log.debug("Authorize: %s", context); 117 log.debug("Is %s secured? %b", context.getDestination().getId(), context.getDestination().isSecured()); 118 119 startAuthorization(context); 120 121 HttpGraniteContext graniteContext = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 122 123 Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 124 125 SecurityContext securityContextBefore = null; 126 int securityContextHashBefore = 0; 127 if (graniteContext.getRequest().getAttribute(FILTER_APPLIED) == null) { 128 securityContextBefore = loadSecurityContextFromSession(); 129 if (securityContextBefore == null) 130 securityContextBefore = SecurityContextHolder.getContext(); 131 else 132 securityContextHashBefore = securityContextBefore.hashCode(); 133 SecurityContextHolder.setContext(securityContextBefore); 134 authentication = securityContextBefore.getAuthentication(); 135 } 136 137 if (context.getDestination().isSecured()) { 138 if (!isAuthenticated(authentication) || authentication instanceof AnonymousAuthenticationToken) { 139 log.debug("Is not authenticated!"); 140 throw SecurityServiceException.newNotLoggedInException("User not logged in"); 141 } 142 if (!userCanAccessService(context, authentication)) { 143 log.debug("Access denied for: %s", authentication.getName()); 144 throw SecurityServiceException.newAccessDeniedException("User not in required role"); 145 } 146 } 147 148 try { 149 Object returnedObject = securityInterceptor != null 150 ? securityInterceptor.invoke(context) 151 : endAuthorization(context); 152 153 return returnedObject; 154 } 155 catch (AccessDeniedException e) { 156 throw SecurityServiceException.newAccessDeniedException(e.getMessage()); 157 } 158 catch (InvocationTargetException e) { 159 handleAuthorizationExceptions(e); 160 throw e; 161 } 162 finally { 163 if (graniteContext.getRequest().getAttribute(FILTER_APPLIED) == null) { 164 // Do this only when not already filtered by Spring Security 165 SecurityContext securityContextAfter = SecurityContextHolder.getContext(); 166 SecurityContextHolder.clearContext(); 167 saveSecurityContextInSession(securityContextAfter, securityContextHashBefore); 168 } 169 } 170 } 171 172 public void logout() { 173 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 174 HttpSession session = context.getSession(false); 175 if (session != null && session.getAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY) != null) 176 session.invalidate(); 177 178 SecurityContextHolder.clearContext(); 179 } 180 181 protected boolean isUserInRole(Authentication authentication, String role) { 182 for (GrantedAuthority ga : authentication.getAuthorities()) { 183 if (ga.getAuthority().matches(role)) 184 return true; 185 } 186 return false; 187 } 188 189 protected boolean isAuthenticated(Authentication authentication) { 190 return authentication != null && authentication.isAuthenticated(); 191 } 192 193 protected boolean userCanAccessService(AbstractSecurityContext context, Authentication authentication) { 194 log.debug("Is authenticated as: %s", authentication.getName()); 195 196 for (String role : context.getDestination().getRoles()) { 197 if (isUserInRole(authentication, role)) { 198 log.debug("Allowed access to %s in role %s", authentication.getName(), role); 199 return true; 200 } 201 log.debug("Access denied for %s not in role %s", authentication.getName(), role); 202 } 203 return false; 204 } 205 206 protected SecurityContext loadSecurityContextFromSession() { 207 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 208 HttpServletRequest request = context.getRequest(); 209 return (SecurityContext)request.getSession().getAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY); 210 } 211 212 protected void saveSecurityContextInSession(SecurityContext securityContext, int securityContextHashBefore) { 213 if (securityContext.hashCode() != securityContextHashBefore && 214 !(securityContext.getAuthentication() instanceof AnonymousAuthenticationToken)) { 215 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance(); 216 HttpServletRequest request = context.getRequest(); 217 request.getSession().setAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY, securityContext); 218 } 219 } 220 221 protected void handleAuthorizationExceptions(InvocationTargetException e) { 222 for (Throwable t = e; t != null; t = t.getCause()) { 223 // Don't create a dependency to javax.ejb in SecurityService... 224 if (t instanceof SecurityException || 225 t instanceof AccessDeniedException || 226 "javax.ejb.EJBAccessException".equals(t.getClass().getName())) 227 throw SecurityServiceException.newAccessDeniedException(t.getMessage()); 228 } 229 } 230 231}