Create, delete, update Session objects
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / serviceprovider / SessionManager.java
1 /*
2  * SessionManager creates, maintains, and caches Session objects.
3  * 
4  * The SessionManager is a singleton object.
5  * A reference to the unique SessionManger object can always be obtained
6  * from the ServiceProviderContext.getSessionManager() method.
7  * 
8  * Sessions should only be created, modified, and deleted through methods
9  * of this class so that the in-memory collection and any disk Cache can
10  * also be changed. Disk cache implementations are referenced through the
11  * SessionCache interface. 
12  * 
13  * --------------------
14  * Copyright 2002, 2004 
15  * University Corporation for Advanced Internet Development, Inc. 
16  * All rights reserved
17  * [Thats all we have to say to protect ourselves]
18  * Your permission to use this code is governed by "The Shibboleth License".
19  * A copy may be found at http://shibboleth.internet2.edu/license.html
20  * [Nothing in copyright law requires license text in every file.]
21  */
22 package edu.internet2.middleware.shibboleth.serviceprovider;
23
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.Map;
28 import java.util.TreeMap;
29
30 import org.apache.log4j.Logger;
31 import org.opensaml.SAMLAssertion;
32 import org.opensaml.SAMLAttribute;
33 import org.opensaml.SAMLAttributeStatement;
34 import org.opensaml.SAMLAuthenticationStatement;
35 import org.opensaml.SAMLResponse;
36 import org.opensaml.SAMLStatement;
37
38 import x0.maceShibbolethTargetConfig1.SessionsDocument.Sessions;
39
40 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig.ApplicationInfo;
41
42 /**
43  * <p>SessionManager manages the memory and disk Cache of Session objects.</p>
44  * 
45  * <p>setSessionCache(SessionCache s) is an "IOC" wiring point. Pass it
46  * an implementation of the SessionCache interface.</p> 
47  * 
48  * @author Howard Gilbert
49  */
50 public class SessionManager {
51         
52         /*
53          * Sessions can be saved using any Persistance Framework. If a Cache
54          * is created, the following pointer is filled in and we start to 
55          * use it.
56          */
57         private static Logger log = Logger.getLogger(SessionManager.class.getName());
58         
59         private SessionCache cache = null; // By default, use memory cache only
60         
61         private TreeMap sessions = new TreeMap(); // The memory cache of Sessions
62         
63         private static ServiceProviderContext context = ServiceProviderContext.getInstance();
64
65         
66         
67         
68         public synchronized Session findSession(String sessionId, String applicationId ) {
69                 Session s = (Session) sessions.get(sessionId);
70                 if (s==null)
71                         return null;
72                 if (!applicationId.equals(s.getApplicationId()))
73                         return null;
74                 return s;
75         }
76         
77         protected synchronized void add(Session s) {
78                 sessions.put(s.getKey(), s);
79                 if (cache!=null)
80                         cache.add(s);
81         }
82         
83         protected synchronized void update(Session s) {
84                 sessions.put(s.getKey(), s);
85                 if (cache!=null)
86                         cache.update(s);
87         }
88         
89         protected synchronized void remove(Session s) {
90                 sessions.remove(s.getKey());
91                 if (cache!=null)
92                         cache.remove(s);
93         }
94         
95
96         /**
97          * Test for valid Session
98          * 
99          * @param sessionId      typically, the cookie value from client browser
100          * @param applicationId  id of target application asking about session
101          * @param ipaddr         null, or IP address of client
102          * @return
103          */
104         public 
105                         boolean 
106         isValid(
107                         String sessionId,   
108                         String applicationId, 
109                         String ipaddr         
110                         ){
111                 Session session = findSession(sessionId,applicationId);
112                 ServiceProviderConfig.ApplicationInfo application = context.getServiceProviderConfig().getApplication(applicationId);
113                 if (session==null)
114                         return false; // Cookie value did not match cached session
115                 if (application == null)
116                         return false; // ApplicationConfig ID invalid
117                 if (ipaddr!=null && !ipaddr.equals(session.getIpaddr()))
118                         return false; // Client coming from a different machine
119                 // check for timeout
120                 // Note: RPC prefetches attributes here
121                 return true;
122         }
123
124         
125         /**
126          * Store Principal information identified by generated UUID.<br>
127          * Called from Authentication Assertion Consumer [SHIRE]
128          * 
129          * @param applicationId The application for this session
130          * @param shireUrl The managing SHIRE
131          * @param ipaddr The client's remote IP address from HTTP
132          * @param authentication SAMLAssertion from the Post data
133          * @return String (UUID) to go in the browser cookie
134          */
135         public 
136                         String 
137         newSession(
138                         String applicationId, 
139                         String ipaddr,
140                         String entityId,
141                         SAMLAssertion assertion,
142                         SAMLAuthenticationStatement authenticationStatement){
143                 
144                 ServiceProviderConfig config = context.getServiceProviderConfig();
145                 ApplicationInfo appinfo = config.getApplication(applicationId);
146                 Sessions appSessionValues = appinfo.getApplicationConfig().getSessions();
147                 
148                 String sessionId = null;
149                 
150                 Session session = new Session();
151                 session.setApplicationId(applicationId);
152                 session.setIpaddr(ipaddr);
153                 session.setEntityId(entityId);
154                 session.setAuthenticationAssertion(assertion);
155                 session.setAuthenticationStatement(authenticationStatement);
156                 session.setLifetime(appSessionValues.getLifetime());
157                 session.setTimeout(appSessionValues.getTimeout());
158                 
159                 sessionId = session.getKey();
160
161                 // This static method finds its unique instance variable
162                 add(session);
163             log.debug("New Session created "+sessionId);
164
165                 return sessionId;
166         }
167         /**
168          * <p>IOC wiring point to plug in an external SessionCache implementation.
169          * </p>
170          * 
171          * @param cache Plugin object implementing the SessionCache interface
172          */
173         public synchronized void 
174         setCache(
175                         SessionCache cache) {
176                 
177             log.info("Enabling Session Cache");
178                 /*
179                  * The following code supports dynamic switching from
180                  * one cache to another if, for example, you decide
181                  * to change databases without restarting Shibboleth.
182                  * Whether this is useful or not is a matter of dispute.
183                  */
184                 if (this.cache!=null) { // replacing an old cache
185                         this.cache.close(); // close it and leave it for GC
186                         return;
187                 }
188                 
189                 this.cache = cache; 
190                 
191                 /*
192                  * Make sure the Cache knows about in memory sessions
193                  * 
194                  * Note: The cache should probably be wired prior to letting
195                  * the Web server process requests, so in almost all cases this
196                  * block will not be neeed. However, we may allow the configuration
197                  * to change dynamically from uncached to cached in the middle
198                  * of a Shibboleth run, and this allows for that possiblity.
199                  */
200                 if (sessions.size()!=0) {
201                         for (Iterator i=sessions.values().iterator();i.hasNext();) {
202                                 Session s = (Session) i.next();
203                                 cache.add(s);
204                         }
205                 }
206                 
207                 /*
208                  * Now load any Sessions in the cache that are not in memory
209                  * (typically after a reboot).
210                  */
211                 cache.reload(sessions);
212         }
213         
214         public static StringBuffer dumpAttributes(Session session) {
215             StringBuffer sb = new StringBuffer();
216         SAMLResponse attributeResponse = session.getAttributeResponse();
217         Iterator assertions = attributeResponse.getAssertions();
218         while (assertions.hasNext()) {
219             SAMLAssertion assertion = (SAMLAssertion) assertions.next();
220             Iterator statements = assertion.getStatements();
221             while (statements.hasNext()) {
222                 SAMLStatement statement = (SAMLStatement) statements.next();
223                 if (statement instanceof SAMLAttributeStatement) {
224                     SAMLAttributeStatement attributeStatement = 
225                         (SAMLAttributeStatement) statement;
226                     
227                     // Foreach Attribute in the AttributeStatement
228                     Iterator attributes = attributeStatement.getAttributes();
229                     while (attributes.hasNext()) {
230                         SAMLAttribute attribute = 
231                             (SAMLAttribute) attributes.next();
232                         String name = attribute.getName();
233                         String namespace = attribute.getNamespace();
234                         Iterator values = attribute.getValues();
235                         while (values.hasNext()){
236                             String val = (String) values.next();
237                             sb.append(name+" "+namespace+" "+val);
238                         }
239                     }
240                 }
241             }
242         }
243             
244             return sb;
245         }
246
247         public static Map /*<String,String>*/
248         mapAttributes(Session session) {
249             Map /*<String,String>*/attributeMap = new HashMap/*<String,String>*/();
250             StringBuffer sb = new StringBuffer();
251         SAMLResponse attributeResponse = session.getAttributeResponse();
252         Iterator assertions = attributeResponse.getAssertions();
253         while (assertions.hasNext()) {
254             SAMLAssertion assertion = (SAMLAssertion) assertions.next();
255             Iterator statements = assertion.getStatements();
256             while (statements.hasNext()) {
257                 SAMLStatement statement = (SAMLStatement) statements.next();
258                 if (statement instanceof SAMLAttributeStatement) {
259                     SAMLAttributeStatement attributeStatement = 
260                         (SAMLAttributeStatement) statement;
261                     
262                     // Foreach Attribute in the AttributeStatement
263                     Iterator attributes = attributeStatement.getAttributes();
264                     while (attributes.hasNext()) {
265                         SAMLAttribute attribute = 
266                             (SAMLAttribute) attributes.next();
267                         String name = attribute.getName();
268                         String namespace = attribute.getNamespace();
269                         ArrayList list = new ArrayList();
270                         Iterator values = attribute.getValues();
271                         String val="";
272                         while (values.hasNext()){
273                             val = (String) values.next();
274                             list.add(val);
275                         }
276                         if (list.size()==1)
277                             attributeMap.put(name,val);
278                         else
279                             attributeMap.put(name,list);
280                     }
281                 }
282             }
283         }
284             
285             return attributeMap;
286         }
287         
288         
289 }