33ce367af343f7ef871cd37194260e10294b879a
[java-idp.git] / src / main / java / edu / internet2 / middleware / shibboleth / idp / session / impl / SessionManagerImpl.java
1 /*
2  * Copyright 2007 University Corporation for Advanced Internet Development, Inc.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
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
17 package edu.internet2.middleware.shibboleth.idp.session.impl;
18
19 import java.security.SecureRandom;
20 import java.util.List;
21 import java.util.Vector;
22
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;
30 import org.slf4j.MDC;
31 import org.springframework.context.ApplicationContext;
32 import org.springframework.context.ApplicationContextAware;
33 import org.springframework.context.ApplicationEvent;
34 import org.springframework.context.ApplicationListener;
35
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;
42
43 /** Manager of IdP sessions. */
44 public class SessionManagerImpl implements SessionManager<Session>, ApplicationContextAware, ApplicationListener {
45
46     /** Class logger. */
47     private final Logger log = LoggerFactory.getLogger(SessionManagerImpl.class);
48
49     /** Spring context used to publish login and logout events. */
50     private ApplicationContext appCtx;
51
52     /** Number of random bits within a session ID. */
53     private final int sessionIDSize = 32;
54
55     /** A {@link SecureRandom} PRNG to generate session IDs. */
56     private final SecureRandom prng = new SecureRandom();
57
58     /** Backing service used to store sessions. */
59     private StorageService<String, SessionManagerEntry> sessionStore;
60
61     /** Partition in which entries are stored. */
62     private String partition;
63
64     /** Lifetime, in milliseconds, of session. */
65     private long sessionLifetime;
66
67     /**
68      * Constructor.
69      * 
70      * @param storageService service used to store sessions
71      * @param lifetime lifetime, in milliseconds, of sessions
72      */
73     public SessionManagerImpl(StorageService<String, SessionManagerEntry> storageService, long lifetime) {
74         sessionStore = storageService;
75         partition = "session";
76         sessionLifetime = lifetime;
77     }
78
79     /**
80      * Constructor.
81      * 
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
85      */
86     public SessionManagerImpl(StorageService<String, SessionManagerEntry> storageService, String storageParition,
87             long lifetime) {
88         sessionStore = storageService;
89         if (!DatatypeHelper.isEmpty(storageParition)) {
90             partition = DatatypeHelper.safeTrim(storageParition);
91         } else {
92             partition = "session";
93         }
94         sessionLifetime = lifetime;
95     }
96
97     /** {@inheritDoc} */
98     public Session createSession() {
99         // generate a random session ID
100         byte[] sid = new byte[sessionIDSize];
101         prng.nextBytes(sid);
102         String sessionID = Hex.encode(sid);
103
104         Session session = new SessionImpl(sessionID, sessionLifetime);
105         SessionManagerEntry sessionEntry = new SessionManagerEntry(session, sessionLifetime);
106         sessionStore.put(partition, sessionID, sessionEntry);
107
108         MDC.put("idpSessionId", sessionID);
109         log.trace("Created session {}", sessionID);
110         appCtx.publishEvent(new LoginEvent(session));
111         return session;
112     }
113
114     /** {@inheritDoc} */
115     public Session createSession(String principal) {
116         // generate a random session ID
117         byte[] sid = new byte[sessionIDSize];
118         prng.nextBytes(sid);
119         String sessionID = Hex.encode(sid);
120
121         MDC.put("idpSessionId", sessionID);
122
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);
127         return session;
128     }
129
130     /** {@inheritDoc} */
131     public void destroySession(String sessionID) {
132         if (sessionID == null) {
133             return;
134         }
135
136         sessionStore.remove(partition, sessionID);
137     }
138
139     /** {@inheritDoc} */
140     public Session getSession(String sessionID) {
141         if (sessionID == null) {
142             return null;
143         }
144
145         SessionManagerEntry sessionEntry = sessionStore.get(partition, sessionID);
146         if (sessionEntry == null) {
147             return null;
148         }
149
150         if (sessionEntry.isExpired()) {
151             destroySession(sessionEntry.getSessionId());
152             return null;
153         } else {
154             return sessionEntry.getSession();
155         }
156     }
157
158     /** {@inheritDoc} */
159     public boolean indexSession(Session session, String index) {
160         if (sessionStore.contains(partition, index)) {
161             return false;
162         }
163
164         SessionManagerEntry sessionEntry = sessionStore.get(partition, session.getSessionID());
165         if (sessionEntry == null) {
166             return false;
167         }
168
169         if (sessionEntry.getSessionIndexes().contains(index)) {
170             return true;
171         }
172
173         sessionEntry.getSessionIndexes().add(index);
174         sessionStore.put(partition, index, sessionEntry);
175         log.trace("Added index {} to session {}", index, session.getSessionID());
176         return true;
177     }
178
179     /** {@inheritDoc} */
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()));
186             }
187         }
188         
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()));
194             }
195         }
196     }
197
198     /** {@inheritDoc} */
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);
204         }
205     }
206
207     /** {@inheritDoc} */
208     public void setApplicationContext(ApplicationContext applicationContext) {
209         ApplicationContext rootContext = applicationContext;
210         while(rootContext.getParent() != null){
211             rootContext = rootContext.getParent();
212         }
213         appCtx = rootContext;
214     }
215
216     /** Session store entry. */
217     public class SessionManagerEntry implements ExpiringObject {
218
219         /** User's session. */
220         private Session userSession;
221
222         /** Indexes for this session. */
223         private List<String> indexes;
224
225         /** Time this entry expires. */
226         private DateTime expirationTime;
227
228         /**
229          * Constructor.
230          * 
231          * @param session user session
232          * @param lifetime lifetime of session
233          */
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());
239         }
240
241         /** {@inheritDoc} */
242         public DateTime getExpirationTime() {
243             return expirationTime;
244         }
245
246         /**
247          * Gets the user session.
248          * 
249          * @return user session
250          */
251         public Session getSession() {
252             return userSession;
253         }
254
255         /**
256          * Gets the ID of the user session.
257          * 
258          * @return ID of the user session
259          */
260         public String getSessionId() {
261             return userSession.getSessionID();
262         }
263
264         /**
265          * Gets the list of indexes for this session.
266          * 
267          * @return list of indexes for this session
268          */
269         public List<String> getSessionIndexes() {
270             return indexes;
271         }
272
273         /** {@inheritDoc} */
274         public boolean isExpired() {
275             return expirationTime.isBeforeNow();
276         }
277
278         /** {@inheritDoc} */
279         public void onExpire() {
280
281         }
282     }
283 }