2 * Copyright 2007 University Corporation for Advanced Internet Development, Inc.
4 * Licensed under the Apache 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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package edu.internet2.middleware.shibboleth.idp.session.impl;
19 import java.security.SecureRandom;
20 import java.util.List;
21 import java.util.Vector;
23 import org.apache.commons.ssl.util.Hex;
24 import org.joda.time.DateTime;
25 import org.opensaml.util.storage.ExpiringObject;
26 import org.opensaml.util.storage.StorageService;
27 import org.opensaml.xml.util.DatatypeHelper;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
31 import org.springframework.context.ApplicationContext;
32 import org.springframework.context.ApplicationContextAware;
33 import org.springframework.context.ApplicationEvent;
34 import org.springframework.context.ApplicationListener;
36 import edu.internet2.middleware.shibboleth.common.session.LoginEvent;
37 import edu.internet2.middleware.shibboleth.common.session.LogoutEvent;
38 import edu.internet2.middleware.shibboleth.common.session.SessionManager;
39 import edu.internet2.middleware.shibboleth.common.util.EventingMapBasedStorageService.AddEntryEvent;
40 import edu.internet2.middleware.shibboleth.common.util.EventingMapBasedStorageService.RemoveEntryEvent;
41 import edu.internet2.middleware.shibboleth.idp.session.Session;
43 /** Manager of IdP sessions. */
44 public class SessionManagerImpl implements SessionManager<Session>, ApplicationContextAware, ApplicationListener {
47 private final Logger log = LoggerFactory.getLogger(SessionManagerImpl.class);
49 /** Spring context used to publish login and logout events. */
50 private ApplicationContext appCtx;
52 /** Number of random bits within a session ID. */
53 private final int sessionIDSize = 32;
55 /** A {@link SecureRandom} PRNG to generate session IDs. */
56 private final SecureRandom prng = new SecureRandom();
58 /** Backing service used to store sessions. */
59 private StorageService<String, SessionManagerEntry> sessionStore;
61 /** Partition in which entries are stored. */
62 private String partition;
64 /** Lifetime, in milliseconds, of session. */
65 private long sessionLifetime;
70 * @param storageService service used to store sessions
71 * @param lifetime lifetime, in milliseconds, of sessions
73 public SessionManagerImpl(StorageService<String, SessionManagerEntry> storageService, long lifetime) {
74 sessionStore = storageService;
75 partition = "session";
76 sessionLifetime = lifetime;
82 * @param storageService service used to store session
83 * @param storageParition partition in which sessions are stored
84 * @param lifetime lifetime, in milliseconds, of sessions
86 public SessionManagerImpl(StorageService<String, SessionManagerEntry> storageService, String storageParition,
88 sessionStore = storageService;
89 if (!DatatypeHelper.isEmpty(storageParition)) {
90 partition = DatatypeHelper.safeTrim(storageParition);
92 partition = "session";
94 sessionLifetime = lifetime;
98 public Session createSession() {
99 // generate a random session ID
100 byte[] sid = new byte[sessionIDSize];
102 String sessionID = Hex.encode(sid);
104 Session session = new SessionImpl(sessionID, sessionLifetime);
105 SessionManagerEntry sessionEntry = new SessionManagerEntry(session, sessionLifetime);
106 sessionStore.put(partition, sessionID, sessionEntry);
108 MDC.put("idpSessionId", sessionID);
109 log.trace("Created session {}", sessionID);
110 appCtx.publishEvent(new LoginEvent(session));
115 public Session createSession(String principal) {
116 // generate a random session ID
117 byte[] sid = new byte[sessionIDSize];
119 String sessionID = Hex.encode(sid);
121 MDC.put("idpSessionId", sessionID);
123 Session session = new SessionImpl(sessionID, sessionLifetime);
124 SessionManagerEntry sessionEntry = new SessionManagerEntry(session, sessionLifetime);
125 sessionStore.put(partition, sessionID, sessionEntry);
126 log.trace("Created session {}", sessionID);
131 public void destroySession(String sessionID) {
132 if (sessionID == null) {
136 sessionStore.remove(partition, sessionID);
140 public Session getSession(String sessionID) {
141 if (sessionID == null) {
145 SessionManagerEntry sessionEntry = sessionStore.get(partition, sessionID);
146 if (sessionEntry == null) {
150 if (sessionEntry.isExpired()) {
151 destroySession(sessionEntry.getSessionId());
154 return sessionEntry.getSession();
159 public boolean indexSession(Session session, String index) {
160 if (sessionStore.contains(partition, index)) {
164 SessionManagerEntry sessionEntry = sessionStore.get(partition, session.getSessionID());
165 if (sessionEntry == null) {
169 if (sessionEntry.getSessionIndexes().contains(index)) {
173 sessionEntry.getSessionIndexes().add(index);
174 sessionStore.put(partition, index, sessionEntry);
175 log.trace("Added index {} to session {}", index, session.getSessionID());
180 public void onApplicationEvent(ApplicationEvent event) {
181 if(event instanceof AddEntryEvent){
182 AddEntryEvent addEvent = (AddEntryEvent)event;
183 if(addEvent.getValue() instanceof SessionManagerEntry){
184 SessionManagerEntry sessionEntry = (SessionManagerEntry) addEvent.getValue();
185 appCtx.publishEvent(new LoginEvent(sessionEntry.getSession()));
189 if (event instanceof RemoveEntryEvent) {
190 RemoveEntryEvent removeEvent = (RemoveEntryEvent) event;
191 if (removeEvent.getValue() instanceof SessionManagerEntry) {
192 SessionManagerEntry sessionEntry = (SessionManagerEntry) removeEvent.getValue();
193 appCtx.publishEvent(new LogoutEvent(sessionEntry.getSession()));
199 public void removeSessionIndex(String index) {
200 SessionManagerEntry sessionEntry = sessionStore.remove(partition, index);
201 if (sessionEntry != null) {
202 log.trace("Removing index {} for session {}", index, sessionEntry.getSessionId());
203 sessionEntry.getSessionIndexes().remove(index);
208 public void setApplicationContext(ApplicationContext applicationContext) {
209 ApplicationContext rootContext = applicationContext;
210 while(rootContext.getParent() != null){
211 rootContext = rootContext.getParent();
213 appCtx = rootContext;
216 /** Session store entry. */
217 public class SessionManagerEntry implements ExpiringObject {
219 /** User's session. */
220 private Session userSession;
222 /** Indexes for this session. */
223 private List<String> indexes;
225 /** Time this entry expires. */
226 private DateTime expirationTime;
231 * @param session user session
232 * @param lifetime lifetime of session
234 public SessionManagerEntry(Session session, long lifetime) {
235 userSession = session;
236 expirationTime = new DateTime().plus(lifetime);
237 indexes = new Vector<String>();
238 indexes.add(userSession.getSessionID());
242 public DateTime getExpirationTime() {
243 return expirationTime;
247 * Gets the user session.
249 * @return user session
251 public Session getSession() {
256 * Gets the ID of the user session.
258 * @return ID of the user session
260 public String getSessionId() {
261 return userSession.getSessionID();
265 * Gets the list of indexes for this session.
267 * @return list of indexes for this session
269 public List<String> getSessionIndexes() {
274 public boolean isExpired() {
275 return expirationTime.isBeforeNow();
279 public void onExpire() {