001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2007-2010 ADEQUATE SYSTEMS SARL
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
021 package org.granite.spring.security;
022
023 import java.lang.reflect.InvocationTargetException;
024 import java.util.Arrays;
025 import java.util.List;
026 import java.util.Map;
027
028 import javax.servlet.http.HttpServletRequest;
029
030 import org.granite.context.GraniteContext;
031 import org.granite.logging.Logger;
032 import org.granite.messaging.service.security.AbstractSecurityContext;
033 import org.granite.messaging.service.security.AbstractSecurityService;
034 import org.granite.messaging.service.security.SecurityServiceException;
035 import org.granite.messaging.webapp.HttpGraniteContext;
036 import org.springframework.beans.factory.BeanFactoryUtils;
037 import org.springframework.context.ApplicationContext;
038 import org.springframework.security.access.AccessDeniedException;
039 import org.springframework.security.authentication.AuthenticationManager;
040 import org.springframework.security.authentication.BadCredentialsException;
041 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
042 import org.springframework.security.core.Authentication;
043 import org.springframework.security.core.GrantedAuthority;
044 import org.springframework.security.core.context.SecurityContext;
045 import org.springframework.security.core.context.SecurityContextHolder;
046 import org.springframework.web.context.support.WebApplicationContextUtils;
047
048 /**
049 * @author Bouiaw
050 */
051 public class SpringSecurity3Service extends AbstractSecurityService {
052
053 private static final Logger log = Logger.getLogger(SpringSecurity3Service.class);
054 private static final String SPRING_AUTHENTICATION_TOKEN = SpringSecurity3Service.class.getName() + ".SPRING_AUTHENTICATION_TOKEN";
055
056 private AuthenticationManager authenticationManager = null;
057 private String authenticationManagerBeanName = null;
058
059 public SpringSecurity3Service() {
060 log.debug("Starting Spring 3 Security Service!");
061 }
062
063 public void setAuthenticationManager(AuthenticationManager authenticationManager) {
064 this.authenticationManager = authenticationManager;
065 }
066
067 public void configure(Map<String, String> params) {
068 log.debug("Configuring with parameters %s: ", params);
069 if (params.containsKey("authentication-manager-bean-name"))
070 authenticationManagerBeanName = params.get("authentication-manager-bean-name");
071 }
072
073 public void login(Object credentials) {
074 List<String> decodedCredentials = Arrays.asList(decodeBase64Credentials(credentials));
075
076 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance();
077 HttpServletRequest httpRequest = context.getRequest();
078
079 String user = decodedCredentials.get(0);
080 String password = decodedCredentials.get(1);
081 Authentication auth = new UsernamePasswordAuthenticationToken(user, password);
082
083 ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(
084 httpRequest.getSession().getServletContext()
085 );
086 if (ctx != null) {
087 lookupAuthenticationManager(ctx, authenticationManagerBeanName);
088
089 try {
090 Authentication authentication = authenticationManager.authenticate(auth);
091 SecurityContextHolder.getContext().setAuthentication(authentication);
092 httpRequest.getSession().setAttribute(SPRING_AUTHENTICATION_TOKEN, authentication);
093 } catch (BadCredentialsException e) {
094 throw SecurityServiceException.newInvalidCredentialsException(e.getMessage());
095 }
096 }
097
098 log.debug("Logged In!");
099 }
100
101 public void lookupAuthenticationManager(ApplicationContext ctx, String authenticationManagerBeanName) throws SecurityServiceException {
102 if (this.authenticationManager != null)
103 return;
104
105 @SuppressWarnings("unchecked")
106 Map<String, AuthenticationManager> authManagers = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, AuthenticationManager.class);
107
108 if (authenticationManagerBeanName != null) {
109 this.authenticationManager = authManagers.get(authenticationManagerBeanName);
110 if (authenticationManager == null) {
111 log.error("AuthenticationManager bean not found " + authenticationManagerBeanName);
112 throw SecurityServiceException.newInvalidCredentialsException("Authentication failed");
113 }
114 return;
115 }
116 else if (authManagers.size() > 1) {
117 log.error("More than one AuthenticationManager beans found, specify which one to use in Spring config <graniteds:security-service authentication-manager='myAuthManager'/> or in granite-config.xml <security type='org.granite.spring.security.SpringSecurity3Service'><param name='authentication-manager-bean-name' value='myAuthManager'/></security>");
118 throw SecurityServiceException.newInvalidCredentialsException("Authentication failed");
119 }
120
121 this.authenticationManager = authManagers.values().iterator().next();
122 }
123
124
125 public Object authorize(AbstractSecurityContext context) throws Exception {
126 log.debug("Authorize: %s", context);
127 log.debug("Is %s secured? %b", context.getDestination().getId(), context.getDestination().isSecured());
128
129 startAuthorization(context);
130
131 Authentication authentication = getAuthentication();
132 if (context.getDestination().isSecured()) {
133 if (!isAuthenticated(authentication)) {
134 log.debug("Is not authenticated!");
135 throw SecurityServiceException.newNotLoggedInException("User not logged in");
136 }
137 if (!userCanAccessService(context, authentication)) {
138 log.debug("Access denied for: %s", authentication.getName());
139 throw SecurityServiceException.newAccessDeniedException("User not in required role");
140 }
141 }
142 if (isAuthenticated(authentication)) {
143 SecurityContext securityContext = SecurityContextHolder.getContext();
144 securityContext.setAuthentication(authentication);
145 }
146
147 try {
148 return endAuthorization(context);
149 } catch (InvocationTargetException e) {
150 handleAuthorizationExceptions(e);
151 throw e;
152 }
153 finally {
154 SecurityContextHolder.clearContext();
155 }
156 }
157
158 public void logout() {
159 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance();
160 context.getSession().invalidate();
161 SecurityContextHolder.getContext().setAuthentication(null);
162 SecurityContextHolder.clearContext();
163 }
164
165 protected boolean isUserInRole(Authentication authentication, String role) {
166 for (GrantedAuthority ga : authentication.getAuthorities()) {
167 if (ga.getAuthority().matches(role))
168 return true;
169 }
170 return false;
171 }
172
173 protected boolean isAuthenticated(Authentication authentication) {
174 return authentication != null && authentication.isAuthenticated();
175 }
176
177 protected boolean userCanAccessService(AbstractSecurityContext context, Authentication authentication) {
178 log.debug("Is authenticated as: %s", authentication.getName());
179
180 for (String role : context.getDestination().getRoles()) {
181 if (isUserInRole(authentication, role)) {
182 log.debug("Allowed access to %s in role %s", authentication.getName(), role);
183 return true;
184 }
185 log.debug("Access denied for %s not in role %s", authentication.getName(), role);
186 }
187 return false;
188 }
189
190 protected Authentication getAuthentication() {
191 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance();
192 HttpServletRequest httpRequest = context.getRequest();
193 Authentication authentication =
194 (Authentication) httpRequest.getSession().getAttribute(SPRING_AUTHENTICATION_TOKEN);
195
196 return authentication;
197 }
198
199 protected void handleAuthorizationExceptions(InvocationTargetException e) {
200 for (Throwable t = e; t != null; t = t.getCause()) {
201 // Don't create a dependency to javax.ejb in SecurityService...
202 if (t instanceof SecurityException ||
203 t instanceof AccessDeniedException ||
204 "javax.ejb.EJBAccessException".equals(t.getClass().getName()))
205 throw SecurityServiceException.newAccessDeniedException(t.getMessage());
206 }
207 }
208
209 }