001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.kim.client.acegi;
017
018import javax.servlet.http.HttpServletRequest;
019import javax.servlet.http.HttpServletResponse;
020
021import org.acegisecurity.Authentication;
022import org.acegisecurity.AuthenticationException;
023import org.acegisecurity.GrantedAuthority;
024import org.acegisecurity.context.SecurityContextHolder;
025import org.acegisecurity.ui.cas.CasProcessingFilter;
026import org.kuali.rice.kim.sesn.DistributedSession;
027
028/**
029 * This class is the main integration point for implementing the 
030 * distributed session in ACEGI. 
031 * 
032 * TODO: Need to add check for missing DST (update 
033 * {@link org.kuali.rice.kim.sesn.DistributedSession})  
034 * 
035 * @author Kuali Rice Team (rice.collab@kuali.org)
036 * @see org.acegisecurity.ui.cas.CasProcessingFilter#attemptAuthentication
037 */
038public class KualiDistributedSessionFilter extends CasProcessingFilter {
039    
040    private DistributedSession distributedSession;
041    
042    //~ Methods ========================================================================================================
043
044    /**
045     * This overridden method gets called if requiresAuthentication is true.  
046     * If Session is Invalid, throw a {@link KualiDistribtedSessionExpiredException}.  
047     * The session is determined invalid if the authentication is of type 
048     * {@link KualiDistribtedSessionExpiredAuthentication}.  Otherwise it 
049     * would have to verify if the DST is valid twice. 
050     *
051     * @return the authentication result of the super method
052     * @see org.acegisecurity.ui.cas.CasProcessingFilter#attemptAuthentication(javax.servlet.http.HttpServletRequest)
053     */
054    public Authentication attemptAuthentication(final HttpServletRequest request)
055        throws AuthenticationException { 
056        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
057        
058        if (authentication instanceof KualiDistributedSessionExpiredAuthentication) {
059            logger.debug("Authentication is dead in attemptAuthentication, setting authentication to null and throwing KualiDistributedSessionExpiredException");
060            SecurityContextHolder.getContext().setAuthentication(null);
061
062            throw new KualiDistributedSessionExpiredException("Session Expired");
063        }
064       
065        return super.attemptAuthentication(request);
066    }
067    
068    /**
069     * This overridden method checks if the DST is valid.  If it's not, the 
070     * authentication is set to a new, non-authenticated, 
071     * {@link KualiDistributedSessionExpiredAuthentication} which is the 
072     * indication for {@link attemptAuthentication} that the session has 
073     * expired 
074     * 
075     * @return true if DST is inValid or if super method returns true
076     * @see org.acegisecurity.ui.AbstractProcessingFilter#requiresAuthentication(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
077     */
078    protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
079        boolean bSesnValid = this.isSesnValid();
080        
081        if (!bSesnValid) {
082            if (this.getDST() != null) {
083                logger.debug("session invalid, setting dead authentication, and pushing through to attemptAuthentication");
084                SecurityContextHolder.getContext().setAuthentication(new KualiDistributedSessionExpiredAuthentication());
085                return true;
086            }
087        }
088        
089        return super.requiresAuthentication(request, response);
090    }
091    
092   
093    /**
094     * This method determines if the stored Distributed Session Ticket is
095     * valid.
096     * 
097     * @return true if valid, false if not
098     */
099    private boolean isSesnValid() {
100        String sDST = this.getDST();
101        
102        if (sDST != null) {
103            if (distributedSession.isSesnValid(sDST)) {
104                logger.debug("Session Valid");
105                distributedSession.touchSesn(sDST);
106                return true;
107            } else {
108                distributedSession.clearSesn(sDST);
109            }
110        }
111        logger.debug("Session Not Valid");
112        
113        return false;
114    }
115    
116    /**
117     * This method retrieves the Distributed Session Ticket
118     * 
119     * @return the Distributed Session Ticket if valid or null
120     */
121    private String getDST() {
122        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
123        String sDST = null;
124        
125        if (authentication != null) {
126            GrantedAuthority[] authorities = authentication.getAuthorities();
127            if (logger.isDebugEnabled()) {
128                logger.debug("Granted Authority Count:" + authorities.length);
129            }
130            
131            for (int i = 0; i < authorities.length; i++) {
132                if (logger.isDebugEnabled()) {
133                    logger.debug("Authority:" + authorities[i]);
134                }
135                if (authorities[i].toString().startsWith(DistributedSession.getPrefix())) {
136                    sDST = authorities[0].toString();
137                }
138            }
139        }
140        else {
141            logger.debug("Authentication is NULL");            
142        }
143        
144        return sDST;
145    }
146
147    /**
148     * @param distributedSession the distributedSession to set
149     */
150    public void setDistributedSession(DistributedSession distributedSession) {
151        this.distributedSession = distributedSession;
152    }
153
154}