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) {
064 List<String> decodedCredentials = Arrays.asList(decodeBase64Credentials(credentials));
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 catch (AuthenticationException e) {
085 handleAuthenticationExceptions(e);
086 }
087 }
088
089 log.debug("User %s logged in", user);
090 }
091
092 protected void handleAuthenticationExceptions(AuthenticationException e) {
093 if (e instanceof BadCredentialsException || e instanceof UsernameNotFoundException)
094 throw SecurityServiceException.newInvalidCredentialsException(e.getMessage());
095
096 throw SecurityServiceException.newAuthenticationFailedException(e.getMessage());
097 }
098
099 public Object authorize(AbstractSecurityContext context) throws Exception {
100 log.debug("Authorize: %s", context);
101 log.debug("Is %s secured? %b", context.getDestination().getId(), context.getDestination().isSecured());
102
103 startAuthorization(context);
104
105 Authentication authentication = getAuthentication();
106 if (context.getDestination().isSecured()) {
107 if (!isAuthenticated(authentication)) {
108 log.debug("Is not authenticated!");
109 throw SecurityServiceException.newNotLoggedInException("User not logged in");
110 }
111 if (!userCanAccessService(context, authentication)) {
112 log.debug("Access denied for: %s", authentication.getName());
113 throw SecurityServiceException.newAccessDeniedException("User not in required role");
114 }
115 }
116 if (isAuthenticated(authentication)) {
117 SecurityContext securityContext = SecurityContextHolder.getContext();
118 securityContext.setAuthentication(authentication);
119 }
120
121 try {
122 return endAuthorization(context);
123 } catch (InvocationTargetException e) {
124 handleAuthorizationExceptions(e);
125 throw e;
126 }
127 finally {
128 SecurityContextHolder.clearContext();
129 }
130 }
131
132 public void logout() {
133 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance();
134 context.getSession().invalidate();
135 SecurityContextHolder.getContext().setAuthentication(null);
136 SecurityContextHolder.clearContext();
137 }
138
139 protected boolean isUserInRole(Authentication authentication, String role) {
140 for (GrantedAuthority ga : authentication.getAuthorities()) {
141 if (ga.getAuthority().matches(role))
142 return true;
143 }
144 return false;
145 }
146
147 protected boolean isAuthenticated(Authentication authentication) {
148 return authentication != null && authentication.isAuthenticated();
149 }
150
151 protected boolean userCanAccessService(AbstractSecurityContext context, Authentication authentication) {
152 log.debug("Is authenticated as: %s", authentication.getName());
153
154 for (String role : context.getDestination().getRoles()) {
155 if (isUserInRole(authentication, role)) {
156 log.debug("Allowed access to %s in role %s", authentication.getName(), role);
157 return true;
158 }
159 log.debug("Access denied for %s not in role %s", authentication.getName(), role);
160 }
161 return false;
162 }
163
164 protected Authentication getAuthentication() {
165 HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance();
166 HttpServletRequest httpRequest = context.getRequest();
167 Authentication authentication =
168 (Authentication) httpRequest.getSession().getAttribute(SPRING_AUTHENTICATION_TOKEN);
169 return authentication;
170 }
171
172 protected void handleAuthorizationExceptions(InvocationTargetException e) {
173 for (Throwable t = e; t != null; t = t.getCause()) {
174 // Don't create a dependency to javax.ejb in SecurityService...
175 if (t instanceof SecurityException ||
176 t instanceof AccessDeniedException ||
177 "javax.ejb.EJBAccessException".equals(t.getClass().getName()))
178 throw SecurityServiceException.newAccessDeniedException(t.getMessage());
179 }
180 }
181 }