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 021package org.granite.messaging.service.security; 022 023import java.lang.reflect.InvocationTargetException; 024import java.util.Arrays; 025import java.util.List; 026import java.util.Map; 027 028import javax.servlet.http.HttpServletRequest; 029 030import org.acegisecurity.AbstractAuthenticationManager; 031import org.acegisecurity.AccessDeniedException; 032import org.acegisecurity.Authentication; 033import org.acegisecurity.AuthenticationException; 034import org.acegisecurity.BadCredentialsException; 035import org.acegisecurity.GrantedAuthority; 036import org.acegisecurity.context.SecurityContext; 037import org.acegisecurity.context.SecurityContextHolder; 038import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; 039import org.acegisecurity.userdetails.UsernameNotFoundException; 040import org.granite.context.GraniteContext; 041import org.granite.logging.Logger; 042import org.granite.messaging.webapp.HttpGraniteContext; 043import org.springframework.beans.factory.BeanFactoryUtils; 044import org.springframework.context.ApplicationContext; 045import org.springframework.web.context.support.WebApplicationContextUtils; 046 047/** 048 * @author Francisco PEREDO 049 */ 050public 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}