Rework authentication and session management code:
[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.MDC;
29 import org.springframework.context.ApplicationContext;
30 import org.springframework.context.ApplicationContextAware;
31
32 import edu.internet2.middleware.shibboleth.common.session.LoginEvent;
33 import edu.internet2.middleware.shibboleth.common.session.LogoutEvent;
34 import edu.internet2.middleware.shibboleth.common.session.SessionManager;
35 import edu.internet2.middleware.shibboleth.idp.session.Session;
36
37 /** Manager of IdP sessions. */
38 public class SessionManagerImpl implements SessionManager<Session>, ApplicationContextAware {
39
40     /** Spring context used to publish login and logout events. */
41     private ApplicationContext appCtx;
42
43     /** Number of random bits within a session ID. */
44     private final int sessionIDSize = 32;
45
46     /** A {@link SecureRandom} PRNG to generate session IDs. */
47     private final SecureRandom prng = new SecureRandom();
48
49     /** Backing service used to store sessions. */
50     private StorageService<String, SessionManagerEntry> sessionStore;
51
52     /** Partition in which entries are stored. */
53     private String partition;
54
55     /** Lifetime, in milliseconds, of session. */
56     private long sessionLifetime;
57
58     /**
59      * Constructor.
60      * 
61      * @param storageService service used to store sessions
62      * @param lifetime lifetime, in milliseconds, of sessions
63      */
64     public SessionManagerImpl(StorageService<String, SessionManagerEntry> storageService, long lifetime) {
65         sessionStore = storageService;
66         partition = "session";
67         sessionLifetime = lifetime;
68     }
69
70     /**
71      * Constructor.
72      * 
73      * @param storageService service used to store session
74      * @param storageParition partition in which sessions are stored
75      * @param lifetime lifetime, in milliseconds, of sessions
76      */
77     public SessionManagerImpl(StorageService<String, SessionManagerEntry> storageService, String storageParition,
78             long lifetime) {
79         sessionStore = storageService;
80         if (!DatatypeHelper.isEmpty(storageParition)) {
81             partition = DatatypeHelper.safeTrim(storageParition);
82         } else {
83             partition = "session";
84         }
85         sessionLifetime = lifetime;
86     }
87
88     /** {@inheritDoc} */
89     public Session createSession() {
90         // generate a random session ID
91         byte[] sid = new byte[sessionIDSize];
92         prng.nextBytes(sid);
93         String sessionID = Hex.encode(sid);
94
95         Session session = new SessionImpl(sessionID, sessionLifetime);
96         SessionManagerEntry sessionEntry = new SessionManagerEntry(this, session, sessionLifetime);
97         sessionStore.put(partition, sessionID, sessionEntry);
98
99         MDC.put("idpSessionId", sessionID);
100
101         appCtx.publishEvent(new LoginEvent(session));
102         return session;
103     }
104
105     /** {@inheritDoc} */
106     public Session createSession(String principal) {
107         // generate a random session ID
108         byte[] sid = new byte[sessionIDSize];
109         prng.nextBytes(sid);
110         String sessionID = Hex.encode(sid);
111
112         MDC.put("idpSessionId", sessionID);
113
114         Session session = new SessionImpl(sessionID, sessionLifetime);
115         SessionManagerEntry sessionEntry = new SessionManagerEntry(this, session, sessionLifetime);
116         sessionStore.put(partition, sessionID, sessionEntry);
117         appCtx.publishEvent(new LoginEvent(session));
118         return session;
119     }
120
121     /** {@inheritDoc} */
122     public void destroySession(String sessionID) {
123         if (sessionID == null) {
124             return;
125         }
126
127         SessionManagerEntry sessionEntry = sessionStore.get(partition, sessionID);
128         if (sessionEntry != null) {
129             appCtx.publishEvent(new LogoutEvent(sessionEntry.getSession()));
130         }
131     }
132
133     /** {@inheritDoc} */
134     public Session getSession(String sessionID) {
135         if (sessionID == null) {
136             return null;
137         }
138
139         SessionManagerEntry sessionEntry = sessionStore.get(partition, sessionID);
140         if (sessionEntry == null) {
141             return null;
142         }
143
144         if (sessionEntry.isExpired()) {
145             destroySession(sessionEntry.getSessionId());
146             return null;
147         } else {
148             return sessionEntry.getSession();
149         }
150     }
151
152     /** {@inheritDoc} */
153     public boolean indexSession(Session session, String index) {
154         if (sessionStore.contains(partition, index)) {
155             return false;
156         }
157
158         SessionManagerEntry sessionEntry = sessionStore.get(partition, session.getSessionID());
159         if (sessionEntry == null) {
160             return false;
161         }
162
163         if (sessionEntry.getSessionIndexes().contains(index)) {
164             return true;
165         }
166
167         sessionEntry.getSessionIndexes().add(index);
168         sessionStore.put(partition, index, sessionEntry);
169         return true;
170     }
171
172     /** {@inheritDoc} */
173     public void removeSessionIndex(String index) {
174         SessionManagerEntry sessionEntry = sessionStore.remove(partition, index);
175         if (sessionEntry != null) {
176             sessionEntry.getSessionIndexes().remove(index);
177         }
178     }
179
180     /** {@inheritDoc} */
181     public void setApplicationContext(ApplicationContext applicationContext) {
182         appCtx = applicationContext;
183     }
184
185     /**
186      * Session store entry.
187      */
188     public class SessionManagerEntry implements ExpiringObject {
189
190         /** User's session. */
191         private Session userSession;
192
193         /** Indexes for this session. */
194         private List<String> indexes;
195
196         /** Manager that owns the session. */
197         private SessionManager<Session> sessionManager;
198
199         /** Time this entry expires. */
200         private DateTime expirationTime;
201
202         /**
203          * Constructor.
204          * 
205          * @param manager manager that owns the session
206          * @param session user session
207          * @param lifetime lifetime of session
208          */
209         public SessionManagerEntry(SessionManager<Session> manager, Session session, long lifetime) {
210             sessionManager = manager;
211             userSession = session;
212             expirationTime = new DateTime().plus(lifetime);
213             indexes = new Vector<String>();
214             indexes.add(userSession.getSessionID());
215         }
216
217         /** {@inheritDoc} */
218         public DateTime getExpirationTime() {
219             return expirationTime;
220         }
221
222         /**
223          * Gets the user session.
224          * 
225          * @return user session
226          */
227         public Session getSession() {
228             return userSession;
229         }
230
231         /**
232          * Gets the ID of the user session.
233          * 
234          * @return ID of the user session
235          */
236         public String getSessionId() {
237             return userSession.getSessionID();
238         }
239
240         /**
241          * Gets the list of indexes for this session.
242          * 
243          * @return list of indexes for this session
244          */
245         public List<String> getSessionIndexes() {
246             return indexes;
247         }
248
249         /** {@inheritDoc} */
250         public boolean isExpired() {
251             return expirationTime.isBeforeNow();
252         }
253
254         /** {@inheritDoc} */
255         public void onExpire() {
256             sessionManager.destroySession(userSession.getSessionID());
257         }
258     }
259 }