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    }