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