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.seam.security; 022 023import java.lang.reflect.Constructor; 024import java.lang.reflect.InvocationTargetException; 025import java.lang.reflect.Method; 026import java.util.ArrayList; 027import java.util.List; 028import java.util.Map; 029 030import javax.faces.application.FacesMessage; 031import javax.security.auth.login.LoginException; 032 033import org.granite.logging.Logger; 034import org.granite.messaging.service.security.AbstractSecurityContext; 035import org.granite.messaging.service.security.AbstractSecurityService; 036import org.granite.messaging.service.security.SecurityServiceException; 037import org.jboss.seam.contexts.Contexts; 038import org.jboss.seam.faces.FacesMessages; 039import org.jboss.seam.security.AuthorizationException; 040import org.jboss.seam.security.Identity; 041import org.jboss.seam.security.NotLoggedInException; 042 043/** 044 * @author Venkat DANDA 045 */ 046public class SeamSecurityService extends AbstractSecurityService { 047 048 private static final Logger log = Logger.getLogger(SeamSecurityService.class); 049 050 public void configure(Map<String, String> params) { 051 } 052 053 public void login(Object credentials, String charset) throws SecurityServiceException { 054 String[] decoded = decodeBase64Credentials(credentials, charset); 055 056 Contexts.getSessionContext().set("org.granite.seam.login", Boolean.TRUE); 057 058 Identity identity = Identity.instance(); 059 060 // Unauthenticate if username has changed (otherwise the previous session/principal is reused) 061 if (identity.isLoggedIn(false) && !decoded[0].equals(identity.getUsername())) { 062 try { 063 Method method = identity.getClass().getDeclaredMethod("unAuthenticate"); 064 method.setAccessible(true); 065 method.invoke(identity); 066 } catch (Exception e) { 067 log.error(e, "Could not call unAuthenticate method on: %s", identity.getClass()); 068 } 069 } 070 071 identity.setUsername(decoded[0]); 072 identity.setPassword(decoded[1]); 073 try { 074 identity.authenticate(); 075 076 endLogin(credentials, charset); 077 } 078 catch (LoginException e) { 079 identity.login(); // Force add of login error messages 080 081 handleAuthenticationExceptions(e); 082 } 083 } 084 085 protected void handleAuthenticationExceptions(LoginException e) { 086 throw SecurityServiceException.newInvalidCredentialsException("User authentication failed", e.getMessage()); 087 } 088 089 public Object authorize(AbstractSecurityContext context) throws Exception { 090 startAuthorization(context); 091 092 if (context.getDestination().isSecured()) { 093 094 Identity identity = Identity.instance(); 095 if (!identity.isLoggedIn()) { 096 // TODO: Session expiration detection... 097 // throw SecurityServiceException.newSessionExpiredException("Session expired"); 098 throw SecurityServiceException.newNotLoggedInException("User not logged in"); 099 } 100 101 boolean accessDenied = true; 102 for (String role : context.getDestination().getRoles()) { 103 if (identity.hasRole(role)) { 104 accessDenied = false; 105 break; 106 } 107 } 108 if (accessDenied) 109 throw SecurityServiceException.newAccessDeniedException("User not in required role"); 110 } 111 112 try { 113 return endAuthorization(context); 114 } catch (InvocationTargetException e) { 115 for (Throwable t = e; t != null; t = t.getCause()) { 116 // If destination is not secured... 117 if (t instanceof NotLoggedInException) 118 throw SecurityServiceException.newNotLoggedInException("User not logged in"); 119 // Don't create a dependency to javax.ejb in SecurityService... 120 if (t instanceof SecurityException || 121 t instanceof AuthorizationException || 122 "javax.ejb.EJBAccessException".equals(t.getClass().getName())) 123 throw SecurityServiceException.newAccessDeniedException(t.getMessage()); 124 } 125 throw e; 126 } 127 } 128 129 public void logout() throws SecurityServiceException { 130 // if (Identity.instance().isLoggedIn(false)) ? 131 Identity.instance().logout(); 132 } 133 134 135 @Override 136 public void handleSecurityException(SecurityServiceException e) { 137 // Add messages 138 //Prepare for the messages. First step is convert the tasks to Seam FacesMessages 139 FacesMessages.afterPhase(); 140 //Second step is add the Seam FacesMessages to JSF FacesContext Messages 141 FacesMessages.instance().beforeRenderResponse(); 142 143 List<FacesMessage> facesMessages = FacesMessages.instance().getCurrentMessages(); 144 145 try { 146 Class<?> c = Thread.currentThread().getContextClassLoader().loadClass("org.granite.tide.TideMessage"); 147 Constructor<?> co = c.getConstructor(String.class, String.class, String.class); 148 149 List<Object> tideMessages = new ArrayList<Object>(facesMessages.size()); 150 for (FacesMessage fm : facesMessages) { 151 String severity = null; 152 if (fm.getSeverity() == FacesMessage.SEVERITY_INFO) 153 severity = "INFO"; 154 else if (fm.getSeverity() == FacesMessage.SEVERITY_WARN) 155 severity = "WARNING"; 156 else if (fm.getSeverity() == FacesMessage.SEVERITY_ERROR) 157 severity = "ERROR"; 158 else if (fm.getSeverity() == FacesMessage.SEVERITY_FATAL) 159 severity = "FATAL"; 160 161 tideMessages.add(co.newInstance(severity, fm.getSummary(), fm.getDetail())); 162 } 163 164 e.getExtendedData().put("messages", tideMessages); 165 } 166 catch (Throwable t) { 167 e.getExtendedData().put("messages", facesMessages); 168 } 169 } 170}