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    }