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
031 import org.acegisecurity.AbstractAuthenticationManager;
032 import org.acegisecurity.AccessDeniedException;
033 import org.acegisecurity.Authentication;
034 import org.acegisecurity.AuthenticationException;
035 import org.acegisecurity.BadCredentialsException;
036 import org.acegisecurity.GrantedAuthority;
037 import org.acegisecurity.context.SecurityContext;
038 import org.acegisecurity.context.SecurityContextHolder;
039 import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
040 import org.acegisecurity.userdetails.UsernameNotFoundException;
041 import org.granite.context.GraniteContext;
042 import org.granite.logging.Logger;
043 import org.granite.messaging.webapp.HttpGraniteContext;
044 import org.springframework.beans.factory.BeanFactoryUtils;
045 import org.springframework.context.ApplicationContext;
046 import org.springframework.web.context.support.WebApplicationContextUtils;
047
048 /**
049 * @author Francisco PEREDO
050 */
051 public class AcegiSecurityService extends AbstractSecurityService {
052
053 private static final Logger log = Logger.getLogger(AcegiSecurityService.class);
054 private static final String SPRING_AUTHENTICATION_TOKEN = AcegiSecurityService.class.getName() + ".SPRING_AUTHENTICATION_TOKEN";
055
056 public AcegiSecurityService() {
057 log.debug("Starting Service!");
058 }
059
060 public void configure(Map<String, String> params) {
061 log.debug("Configuring with parameters (NOOP) %s: ", params);
062 }
063
064 public void login(Object credentials, String charset) {
065 List<String> decodedCredentials = Arrays.asList(decodeBase64Credentials(credentials, charset));
066
067 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance();
068 HttpServletRequest httpRequest = context.getRequest();
069
070 String user = decodedCredentials.get(0);
071 String password = decodedCredentials.get(1);
072 Authentication auth = new UsernamePasswordAuthenticationToken(user, password);
073
074 ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(
075 httpRequest.getSession().getServletContext()
076 );
077 if (ctx != null) {
078 AbstractAuthenticationManager authenticationManager =
079 BeanFactoryUtils.beanOfTypeIncludingAncestors(ctx, AbstractAuthenticationManager.class);
080 try {
081 Authentication authentication = authenticationManager.authenticate(auth);
082 SecurityContextHolder.getContext().setAuthentication(authentication);
083 httpRequest.getSession().setAttribute(SPRING_AUTHENTICATION_TOKEN, authentication);
084
085 endLogin(credentials, charset);
086 }
087 catch (AuthenticationException e) {
088 handleAuthenticationExceptions(e);
089 }
090 }
091
092 log.debug("User %s logged in", user);
093 }
094
095 protected void handleAuthenticationExceptions(AuthenticationException e) {
096 if (e instanceof BadCredentialsException || e instanceof UsernameNotFoundException)
097 throw SecurityServiceException.newInvalidCredentialsException(e.getMessage());
098
099 throw SecurityServiceException.newAuthenticationFailedException(e.getMessage());
100 }
101
102 public Object authorize(AbstractSecurityContext context) throws Exception {
103 log.debug("Authorize: %s", context);
104 log.debug("Is %s secured? %b", context.getDestination().getId(), context.getDestination().isSecured());
105
106 startAuthorization(context);
107
108 Authentication authentication = getAuthentication();
109 if (context.getDestination().isSecured()) {
110 if (!isAuthenticated(authentication)) {
111 log.debug("Is not authenticated!");
112 throw SecurityServiceException.newNotLoggedInException("User not logged in");
113 }
114 if (!userCanAccessService(context, authentication)) {
115 log.debug("Access denied for: %s", authentication.getName());
116 throw SecurityServiceException.newAccessDeniedException("User not in required role");
117 }
118 }
119 if (isAuthenticated(authentication)) {
120 SecurityContext securityContext = SecurityContextHolder.getContext();
121 securityContext.setAuthentication(authentication);
122 }
123
124 try {
125 return endAuthorization(context);
126 } catch (InvocationTargetException e) {
127 handleAuthorizationExceptions(e);
128 throw e;
129 }
130 finally {
131 SecurityContextHolder.clearContext();
132 }
133 }
134
135 public void logout() {
136 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance();
137 context.getSession().invalidate();
138 SecurityContextHolder.getContext().setAuthentication(null);
139 SecurityContextHolder.clearContext();
140 }
141
142 protected boolean isUserInRole(Authentication authentication, String role) {
143 for (GrantedAuthority ga : authentication.getAuthorities()) {
144 if (ga.getAuthority().matches(role))
145 return true;
146 }
147 return false;
148 }
149
150 protected boolean isAuthenticated(Authentication authentication) {
151 return authentication != null && authentication.isAuthenticated();
152 }
153
154 protected boolean userCanAccessService(AbstractSecurityContext context, Authentication authentication) {
155 log.debug("Is authenticated as: %s", authentication.getName());
156
157 for (String role : context.getDestination().getRoles()) {
158 if (isUserInRole(authentication, role)) {
159 log.debug("Allowed access to %s in role %s", authentication.getName(), role);
160 return true;
161 }
162 log.debug("Access denied for %s not in role %s", authentication.getName(), role);
163 }
164 return false;
165 }
166
167 protected Authentication getAuthentication() {
168 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance();
169 HttpServletRequest httpRequest = context.getRequest();
170 Authentication authentication =
171 (Authentication) httpRequest.getSession().getAttribute(SPRING_AUTHENTICATION_TOKEN);
172 return authentication;
173 }
174
175 protected void handleAuthorizationExceptions(InvocationTargetException e) {
176 for (Throwable t = e; t != null; t = t.getCause()) {
177 // Don't create a dependency to javax.ejb in SecurityService...
178 if (t instanceof SecurityException ||
179 t instanceof AccessDeniedException ||
180 "javax.ejb.EJBAccessException".equals(t.getClass().getName()))
181 throw SecurityServiceException.newAccessDeniedException(t.getMessage());
182 }
183 }
184 }