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 log.trace("Received event {}", event.getClass().getName());
182 if(event instanceof AddEntryEvent){
183 AddEntryEvent addEvent = (AddEntryEvent)event;
184 if(addEvent.getValue() instanceof SessionManagerEntry){
185 SessionManagerEntry sessionEntry = (SessionManagerEntry) addEvent.getValue();
186 appCtx.publishEvent(new LoginEvent(sessionEntry.getSession()));
190 if (event instanceof RemoveEntryEvent) {
191 RemoveEntryEvent removeEvent = (RemoveEntryEvent) event;
192 if (removeEvent.getValue() instanceof SessionManagerEntry) {
193 SessionManagerEntry sessionEntry = (SessionManagerEntry) removeEvent.getValue();
194 log.trace("Destroyed session {} for principal {}", sessionEntry.getSessionId(), sessionEntry
195 .getSession().getPrincipalName());
196 appCtx.publishEvent(new LogoutEvent(sessionEntry.getSession()));
202 public void removeSessionIndex(String index) {
203 SessionManagerEntry sessionEntry = sessionStore.remove(partition, index);
204 if (sessionEntry != null) {
205 log.trace("Removing index {} for session {}", index, sessionEntry.getSessionId());
206 sessionEntry.getSessionIndexes().remove(index);
211 public void setApplicationContext(ApplicationContext applicationContext) {
212 ApplicationContext rootContext = applicationContext;
213 while(rootContext.getParent() != null){
214 rootContext = rootContext.getParent();
216 appCtx = rootContext;
219 /** Session store entry. */
220 public class SessionManagerEntry implements ExpiringObject {
222 /** User's session. */
223 private Session userSession;
225 /** Indexes for this session. */
226 private List<String> indexes;
228 /** Time this entry expires. */
229 private DateTime expirationTime;
234 * @param session user session
235 * @param lifetime lifetime of session
237 public SessionManagerEntry(Session session, long lifetime) {
238 userSession = session;
239 expirationTime = new DateTime().plus(lifetime);
240 indexes = new Vector<String>();
241 indexes.add(userSession.getSessionID());
245 public DateTime getExpirationTime() {
246 return expirationTime;
250 * Gets the user session.
252 * @return user session
254 public Session getSession() {
259 * Gets the ID of the user session.
261 * @return ID of the user session
263 public String getSessionId() {
264 return userSession.getSessionID();
268 * Gets the list of indexes for this session.
270 * @return list of indexes for this session
272 public List<String> getSessionIndexes() {
277 public boolean isExpired() {
278 return expirationTime.isBeforeNow();
282 public void onExpire() {