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 }