View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kim.sesn;
17  
18  import java.util.Date;
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.kuali.rice.kim.sesn.timeouthandlers.TimeoutHandler;
27  import org.springframework.dao.IncorrectResultSizeDataAccessException;
28  import org.springframework.jdbc.core.JdbcTemplate;
29  
30  /**
31   * This class is used to interface with the distributed session database.
32   * 
33   * TODO: add clear principals to clearSesn
34   * @author Kuali Rice Team (rice.collab@kuali.org)
35   *
36   */
37  public class DistributedSession {
38      public static final String DEFAULT_PREFIX="DST";
39      private static String prefix = DEFAULT_PREFIX;
40      private JdbcTemplate jdbcTemplate;
41      private TimeoutHandler timeoutHandler;
42      private boolean allowInsertOnTouch = false;
43      
44      private static final Log logger = LogFactory.getLog(DistributedSession.class);
45  
46      /**
47       * @param timeoutHandler the timeoutHandler to set
48       */
49      public void setTimeoutHandler(TimeoutHandler timeoutHandler) {
50          this.timeoutHandler = timeoutHandler;
51      }
52  
53      /**
54       * @param jdbcTemplate the jdbcTemplate to set
55       */
56      public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
57          this.jdbcTemplate = jdbcTemplate;
58      }
59      
60      /**
61       * This method determines if the Session Ticket is valid.
62       * 
63       * @param DST - the Distributed Session Ticket
64       * @return true if the session is valid
65       */
66      public boolean isSesnValid(String DST) {
67          logger.debug("isSesnValid(DST)");
68          return isSesnValid (DST, new HashMap<String,Object>());
69      }
70      
71      /**
72       * This method determines if the Session Ticket is valid.
73       * 
74       * @param DST - the Distributed Session Ticket
75       * @param timoutArgs - Additional information on which to base timeouts
76       * @return true if the session is valid
77       */
78      public boolean isSesnValid(String DST, Map<String,Object> timeoutArgs) {
79          logger.debug("isSesnValid(DST, timeoutArgs)");
80          boolean bRet = false;
81          String sql = "select sesnID, lastAccessDt, maxIdleTime from authnsesn where sesnID=?";
82          
83          if (DST != null) {
84              Object[] args = { DST };
85              
86              try {
87                  Map<String,Object> fields = jdbcTemplate.queryForMap(sql, args);
88                  fields.put("maxIdleTime", this.getMaxIdleTime((Long)fields.get("maxIdleTime"), (Date)fields.get("lastAccessDt")));
89                  fields.putAll(timeoutArgs);
90                  
91                  if (logger.isDebugEnabled()) {
92                      logger.debug("ARGUMENTS number:" + fields.size());
93                      for (Iterator<Map.Entry<String,Object>> i = fields.entrySet().iterator(); i.hasNext(); ) {
94                          Map.Entry<String,Object> entry = (Map.Entry<String,Object>)i.next();
95                          logger.debug("ARGUMENT " + entry.getKey() + ":" + entry.getValue());
96                      }
97                  }
98                  
99                  if(!timeoutHandler.hasTimedOut(fields)) {
100                     logger.debug("Session not timed out");
101                     bRet = true;
102                 } else {
103                     logger.debug("Session timed out");
104                 }
105             } catch (Exception e) {
106                 logger.debug(e);
107             }
108         } 
109         else {
110             logger.debug("Session ID is null");           
111         }
112                 
113         return bRet;
114     }
115     
116     
117     /**
118      * This method returns the list of principals currently authenticated
119      * that are associated with the provided Distributed Session Ticket
120      * 
121      * @param DST - the Distributed Session Ticket
122      * @return the List of authenticated principals
123      */
124     public List<String> getAuthenticatedPricipals(String DST) {
125         String sql = "select principalID from authnsesn where sesnID=?";
126         Object args[] = { DST };
127         
128         return jdbcTemplate.queryForList(sql, args, String.class);
129     }
130     
131     /**
132      * This method removes the session
133      * 
134      * @param DST - the Distributed Session Ticket
135      */
136     public void clearSesn(String DST) {
137         String sql = "delete from authnsesn where sesnID='" + DST + "'";
138         
139         jdbcTemplate.execute(sql);
140     }
141     
142     
143     /**
144      * This method takes an authenticated principal and generates a
145      * Distributed Session Ticket and updates the session database
146      * 
147      * @param principalID - the id of the authenticated entity
148      * @return DST - the Distributed Session Ticket
149      */
150     public String createSesn(String principalID) {
151         String DST = this.generateDST();
152         
153         this.touchSesn(DST);
154         this.addPrincipalToSesn(DST, principalID);
155 
156         return DST;
157     }
158     
159     /**
160      * This method generates a unique Distributed Session Ticket
161      * 
162      * @return DST - the Distributed Session Ticket
163      */
164     public String generateDST() {
165         return prefix + "-" + SessionIdGenerator.getNewString();
166     }
167     
168     /**
169      * This method updates the session
170      * 
171      * @param DST - the Distributed Session Ticket
172      */
173     public void touchSesn(String DST) {
174         String sql = "select lastAccessDt, maxIdleTime from authnsesn where sesnID=?";
175         String updateSql = "";
176         Object[] args = { DST },
177                updateArgs;
178         Long maxIdleTime;
179         
180         try {
181             if (logger.isDebugEnabled()) {
182                 logger.debug("ARGUMENTS number:" + args.length);
183                 logger.debug("ARGUMENTS 0:" + args[0]);
184             }
185             Map<String,Object> fields = jdbcTemplate.queryForMap(sql, args);
186             Date lastAccessDt = (Date)fields.get("lastAccessDt");
187             if (logger.isDebugEnabled()) {
188                 logger.debug("Last Access:" + lastAccessDt);
189             }
190             maxIdleTime = getMaxIdleTime((Long)fields.get("maxIdleTime"), lastAccessDt);
191             
192             
193             updateSql = "update authnsesn set lastAccessDt=NOW(), maxIdleTime = ? where sesnID=?";
194             updateArgs = new Object[] { maxIdleTime, DST };
195             jdbcTemplate.update(updateSql, updateArgs);
196         } 
197         // catch if no or more than 1 results are returned
198         catch (IncorrectResultSizeDataAccessException ex) {
199             if (this.allowInsertOnTouch) {
200                 maxIdleTime = new Long(0);
201                 
202                 updateSql = "insert into authnsesn (sesnID, insertDt, lastAccessDt, maxIdleTime) values (?, NOW(), NOW(), ?)";
203                 updateArgs = new Object[] { DST, maxIdleTime };
204                 jdbcTemplate.update(updateSql, updateArgs);
205             }
206         }
207     }
208     
209     
210     /**
211      * This method returns the greater of the stored max idle time and 
212      * time since last access
213      * 
214      * @param oldMaxIdleTime - the previous max idle time
215      * @param lastAccessDt - the timestamp of last access
216      * @return the max idle time
217      */
218     public Long getMaxIdleTime(Long oldMaxIdleTime, Date lastAccessDt) {
219         Long maxIdleTime = oldMaxIdleTime;
220         
221         if (logger.isDebugEnabled()) {
222             logger.debug("Max Idle:" + maxIdleTime);
223         }
224         long curIdleTime = System.currentTimeMillis()-lastAccessDt.getTime();
225         if (logger.isDebugEnabled()) {
226             logger.debug("Curr Idle:" + curIdleTime);
227         }
228         if (curIdleTime > maxIdleTime) {
229             maxIdleTime = new Long(curIdleTime);
230         }
231         
232         return maxIdleTime;
233     }
234     
235     /**
236      * This method appends a principal to an active session
237      * 
238      * @param DST - the Distributed Session Ticket
239      * @param principalID - the id of the authenticated entity
240      */
241     public void addPrincipalToSesn(String DST, String principalID) {
242         // this will fail if the record already exists 
243         try {
244             String updateSql = "insert into authnsesnprincipal (sesnID, principalID) values (?, ?)";
245             
246             jdbcTemplate.update(updateSql, new Object[] { DST, principalID });
247             if (logger.isDebugEnabled()) {
248                 logger.debug("Added Principal to Sesn:" + principalID + " " + DST);
249             }
250         }
251         catch (Exception e) {
252             if (logger.isDebugEnabled()) {
253                 logger.debug("Principal Probably already exists:" + principalID + " " + DST);
254             }
255         }
256     }
257 
258     /**
259      * @return the prefix
260      */
261     public static String getPrefix() {
262         return DistributedSession.prefix;
263     }
264 
265     /**
266      * @param prefix the prefix to set
267      */
268     public static void setPrefix(String prefix) {
269         DistributedSession.prefix = prefix;
270     }
271 
272     /**
273      * @param allowInsertOnTouch the allowInsertOnTouch to set
274      */
275     public void setAllowInsertOnTouch(boolean allowInsertOnTouch) {
276         this.allowInsertOnTouch = allowInsertOnTouch;
277     }
278 }