View Javadoc

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  
21  import org.apache.commons.ssl.util.Hex;
22  import org.opensaml.util.storage.StorageService;
23  import org.opensaml.xml.util.DatatypeHelper;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  import org.slf4j.MDC;
27  import org.springframework.context.ApplicationContext;
28  import org.springframework.context.ApplicationContextAware;
29  import org.springframework.context.ApplicationEvent;
30  import org.springframework.context.ApplicationListener;
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.common.util.EventingMapBasedStorageService.AddEntryEvent;
36  import edu.internet2.middleware.shibboleth.common.util.EventingMapBasedStorageService.RemoveEntryEvent;
37  import edu.internet2.middleware.shibboleth.idp.session.Session;
38  
39  /** Manager of IdP sessions. */
40  public class SessionManagerImpl implements SessionManager<Session>, ApplicationContextAware, ApplicationListener {
41  
42      /** Class logger. */
43      private final Logger log = LoggerFactory.getLogger(SessionManagerImpl.class);
44  
45      /** Spring context used to publish login and logout events. */
46      private ApplicationContext appCtx;
47  
48      /** Number of random bits within a session ID. */
49      private final int sessionIDSize = 32;
50  
51      /** A {@link SecureRandom} PRNG to generate session IDs. */
52      private final SecureRandom prng = new SecureRandom();
53  
54      /** Backing service used to store sessions. */
55      private StorageService<String, SessionManagerEntry> sessionStore;
56  
57      /** Partition in which entries are stored. */
58      private String partition;
59  
60      /** Lifetime, in milliseconds, of session. */
61      private long sessionLifetime;
62  
63      /**
64       * Constructor.
65       * 
66       * @param storageService service used to store sessions
67       * @param lifetime lifetime, in milliseconds, of sessions
68       */
69      public SessionManagerImpl(StorageService<String, SessionManagerEntry> storageService, long lifetime) {
70          sessionStore = storageService;
71          partition = "session";
72          sessionLifetime = lifetime;
73      }
74  
75      /**
76       * Constructor.
77       * 
78       * @param storageService service used to store session
79       * @param storageParition partition in which sessions are stored
80       * @param lifetime lifetime, in milliseconds, of sessions
81       */
82      public SessionManagerImpl(StorageService<String, SessionManagerEntry> storageService, String storageParition,
83              long lifetime) {
84          sessionStore = storageService;
85          if (!DatatypeHelper.isEmpty(storageParition)) {
86              partition = DatatypeHelper.safeTrim(storageParition);
87          } else {
88              partition = "session";
89          }
90          sessionLifetime = lifetime;
91      }
92  
93      /** {@inheritDoc} */
94      public Session createSession() {
95          // generate a random session ID
96          byte[] sid = new byte[sessionIDSize];
97          prng.nextBytes(sid);
98          String sessionID = Hex.encode(sid);
99          
100         byte[] sessionSecret = new byte[16];
101         prng.nextBytes(sessionSecret);
102 
103         Session session = new SessionImpl(sessionID, sessionSecret, sessionLifetime);
104         SessionManagerEntry sessionEntry = new SessionManagerEntry(session, sessionLifetime);
105         sessionStore.put(partition, sessionID, sessionEntry);
106 
107         MDC.put("idpSessionId", sessionID);
108         log.trace("Created session {}", sessionID);
109         appCtx.publishEvent(new LoginEvent(session));
110         return session;
111     }
112 
113     /** {@inheritDoc} */
114     public Session createSession(String principal) {
115         // generate a random session ID
116         byte[] sid = new byte[sessionIDSize];
117         prng.nextBytes(sid);
118         String sessionID = Hex.encode(sid);
119         
120         byte[] sessionSecret = new byte[16];
121         prng.nextBytes(sessionSecret);
122 
123         Session session = new SessionImpl(sessionID, sessionSecret, sessionLifetime);
124         SessionManagerEntry sessionEntry = new SessionManagerEntry(session, sessionLifetime);
125         sessionStore.put(partition, sessionID, sessionEntry);
126         
127         MDC.put("idpSessionId", sessionID);
128         log.trace("Created session {}", sessionID);
129         return session;
130     }
131 
132     /** {@inheritDoc} */
133     public void destroySession(String sessionID) {
134         if (sessionID == null) {
135             return;
136         }
137 
138         sessionStore.remove(partition, sessionID);
139     }
140 
141     /** {@inheritDoc} */
142     public Session getSession(String sessionID) {
143         if (sessionID == null) {
144             return null;
145         }
146 
147         SessionManagerEntry sessionEntry = sessionStore.get(partition, sessionID);
148         if (sessionEntry == null) {
149             return null;
150         }
151 
152         if (sessionEntry.isExpired()) {
153             destroySession(sessionEntry.getSessionId());
154             return null;
155         } else {
156             return sessionEntry.getSession();
157         }
158     }
159 
160     /** {@inheritDoc} */
161     public boolean indexSession(Session session, String index) {
162         if (sessionStore.contains(partition, index)) {
163             return false;
164         }
165 
166         SessionManagerEntry sessionEntry = sessionStore.get(partition, session.getSessionID());
167         if (sessionEntry == null) {
168             return false;
169         }
170 
171         if (sessionEntry.getSessionIndexes().contains(index)) {
172             return true;
173         }
174 
175         sessionEntry.getSessionIndexes().add(index);
176         sessionStore.put(partition, index, sessionEntry);
177         log.trace("Added index {} to session {}", index, session.getSessionID());
178         return true;
179     }
180 
181     /** {@inheritDoc} */
182     public void onApplicationEvent(ApplicationEvent event) {
183         if (event instanceof AddEntryEvent) {
184             AddEntryEvent addEvent = (AddEntryEvent) event;
185             if (addEvent.getValue() instanceof SessionManagerEntry) {
186                 SessionManagerEntry sessionEntry = (SessionManagerEntry) addEvent.getValue();
187                 appCtx.publishEvent(new LoginEvent(sessionEntry.getSession()));
188             }
189         }
190 
191         if (event instanceof RemoveEntryEvent) {
192             RemoveEntryEvent removeEvent = (RemoveEntryEvent) event;
193             if (removeEvent.getValue() instanceof SessionManagerEntry) {
194                 SessionManagerEntry sessionEntry = (SessionManagerEntry) removeEvent.getValue();
195                 appCtx.publishEvent(new LogoutEvent(sessionEntry.getSession()));
196             }
197         }
198     }
199 
200     /** {@inheritDoc} */
201     public void removeSessionIndex(String index) {
202         SessionManagerEntry sessionEntry = sessionStore.remove(partition, index);
203         if (sessionEntry != null) {
204             log.trace("Removing index {} for session {}", index, sessionEntry.getSessionId());
205             sessionEntry.getSessionIndexes().remove(index);
206         }
207     }
208 
209     /** {@inheritDoc} */
210     public void setApplicationContext(ApplicationContext applicationContext) {
211         ApplicationContext rootContext = applicationContext;
212         while (rootContext.getParent() != null) {
213             rootContext = rootContext.getParent();
214         }
215         appCtx = rootContext;
216     }
217 }