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