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 */
022 package org.granite.messaging.service.security;
023
024 import java.lang.reflect.InvocationTargetException;
025 import java.util.Arrays;
026 import java.util.List;
027 import java.util.Map;
028
029 import javax.servlet.http.HttpServletRequest;
030 import javax.servlet.http.HttpSession;
031
032 import org.granite.context.GraniteContext;
033 import org.granite.logging.Logger;
034 import org.granite.messaging.webapp.HttpGraniteContext;
035 import org.springframework.beans.factory.BeanFactoryUtils;
036 import org.springframework.context.ApplicationContext;
037 import org.springframework.security.AbstractAuthenticationManager;
038 import org.springframework.security.AccessDeniedException;
039 import org.springframework.security.Authentication;
040 import org.springframework.security.AuthenticationException;
041 import org.springframework.security.BadCredentialsException;
042 import org.springframework.security.GrantedAuthority;
043 import org.springframework.security.context.HttpSessionContextIntegrationFilter;
044 import org.springframework.security.context.SecurityContext;
045 import org.springframework.security.context.SecurityContextHolder;
046 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
047 import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
048 import org.springframework.security.userdetails.UsernameNotFoundException;
049 import org.springframework.web.context.support.WebApplicationContextUtils;
050
051 /**
052 * @author Bouiaw
053 * @author wdrai
054 */
055 public 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 }