Add StoraeService implementaiton that produces events when objects are add or created.
[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         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()));
187             }
188         }
189         
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()));
197             }
198         }
199     }
200
201     /** {@inheritDoc} */
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);
207         }
208     }
209
210     /** {@inheritDoc} */
211     public void setApplicationContext(ApplicationContext applicationContext) {
212         ApplicationContext rootContext = applicationContext;
213         while(rootContext.getParent() != null){
214             rootContext = rootContext.getParent();
215         }
216         appCtx = rootContext;
217     }
218
219     /** Session store entry. */
220     public class SessionManagerEntry implements ExpiringObject {
221
222         /** User's session. */
223         private Session userSession;
224
225         /** Indexes for this session. */
226         private List<String> indexes;
227
228         /** Time this entry expires. */
229         private DateTime expirationTime;
230
231         /**
232          * Constructor.
233          * 
234          * @param session user session
235          * @param lifetime lifetime of session
236          */
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());
242         }
243
244         /** {@inheritDoc} */
245         public DateTime getExpirationTime() {
246             return expirationTime;
247         }
248
249         /**
250          * Gets the user session.
251          * 
252          * @return user session
253          */
254         public Session getSession() {
255             return userSession;
256         }
257
258         /**
259          * Gets the ID of the user session.
260          * 
261          * @return ID of the user session
262          */
263         public String getSessionId() {
264             return userSession.getSessionID();
265         }
266
267         /**
268          * Gets the list of indexes for this session.
269          * 
270          * @return list of indexes for this session
271          */
272         public List<String> getSessionIndexes() {
273             return indexes;
274         }
275
276         /** {@inheritDoc} */
277         public boolean isExpired() {
278             return expirationTime.isBeforeNow();
279         }
280
281         /** {@inheritDoc} */
282         public void onExpire() {
283
284         }
285     }
286 }