SDSS WAYF patch for multi-federation support
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / wayf / SamlIdPCookie.java
1 /*
2  * Copyright [2005] [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.wayf;
18
19 import java.io.UnsupportedEncodingException;
20 import java.net.URLDecoder;
21 import java.net.URLEncoder;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Iterator;
25
26 import javax.servlet.http.Cookie;
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29
30 import org.apache.log4j.Logger;
31 import org.bouncycastle.util.encoders.Base64;
32
33 /**
34  * Implementation of the <code>_saml_idp </code> cookie.
35  * 
36  * Note that any SamlIdPCookie is only valid for as long as the reqest/response 
37  * parameters provided to getIdPCookie remain valid.
38  * 
39  * @author Rod Widdowson
40  *
41  */
42 public class SamlIdPCookie  {
43
44         private static final String COOKIE_NAME = "_saml_idp";
45         private static final Logger log = Logger.getLogger(SamlIdPCookie.class.getName());
46         
47         private final HttpServletRequest req;
48         private final HttpServletResponse res;
49         private final String domain;
50         private final List /*<String>*/ idPList = new ArrayList/*<String>*/();
51         
52         /**
53          * Constructs a <code>SamlIdPCookie</code> from the provided string (which is the raw data 
54          * 
55          * @param codedData
56          *            the information read from the cookie
57          * @param domain - if non null the domain for any *created* cookie.
58          */
59         private SamlIdPCookie(String codedData, HttpServletRequest req, HttpServletResponse res, String domain) {
60                 
61                 this.req = req;
62                 this.res = res;
63                 this.domain = domain;
64                 
65                 int start;
66                 int end;
67                 
68                 if (codedData == null || codedData.equals(""))
69                 {
70                         log.info("Empty cookie");
71                         return;
72                 }
73                 //
74                 // An earlier version saved the cookie without URL encoding it, hence there may be 
75                 // speaces which in turn means we maybe quoted.  Strip any quotes.
76                 //
77                 if (codedData.charAt(0) == '"' && codedData.charAt(codedData.length()-1) == '"') {
78                         codedData= codedData.substring(1,codedData.length()-1);
79                 }
80                 
81                 try {
82                         codedData = URLDecoder.decode(codedData, "UTF-8");
83                 } catch (UnsupportedEncodingException e) {
84                         log.error("could not decode cookie");
85                         return;
86                 }
87                 
88                 start = 0;
89                 end = codedData.indexOf(' ', start);
90                 while (end > 0) {
91                         String value = codedData.substring(start, end);
92                         start = end + 1;
93                         end = codedData.indexOf(' ', start);
94                         if (!value.equals("")) {
95                             idPList.add(new String(Base64.decode(value)));
96                         }
97                 }
98                 if (start < codedData.length()) {
99                         String value = codedData.substring(start);
100                         if (!value.equals("")) {
101                             idPList.add(new String(Base64.decode(value)));
102                         }
103                 }
104         }
105         /**
106          * Create a SamlCookie with no data inside.
107          * @param domain - if non null, the domain of the new cookie 
108          *
109          */
110         public SamlIdPCookie(HttpServletRequest req, HttpServletResponse res, String domain) {
111                 this.req = req;
112                 this.res = res;
113                 this.domain = domain;
114         }
115
116         /**
117          * Add the specified Shibboleth IdP Name to the cookie list or move to 
118          * the front and then write it back.
119          * 
120          * We always add to the front (and remove from wherever it was)
121          * 
122          * @param idPName    - The name to be added
123          * @param expiration - The expiration of the cookie or zero if it is to be unchanged
124          */
125         public void addIdPName(String idPName, int expiration) {
126
127                 idPList.remove(idPName);
128                 idPList.add(0, idPName);
129
130                 writeCookie(expiration);
131         }
132         
133         /**
134          * Delete the <b>entire<\b> cookie contents
135          */
136
137         public static void deleteCookie(HttpServletRequest req, HttpServletResponse res) {
138                 Cookie cookie = getCookie(req);
139                 
140                 if (cookie == null) { 
141                         return; 
142                 }
143                 
144                 cookie.setPath("/");
145                 cookie.setMaxAge(0);
146                 res.addCookie(cookie);
147         }
148
149         /**
150          * Load up the cookie and convert it into a SamlIdPCookie.  If there is no
151          * underlying cookie return a null one.
152          * @param domain - if this is set then any <b>created</b> cookies are set to this domain 
153          */
154         
155         public static SamlIdPCookie getIdPCookie(HttpServletRequest req, HttpServletResponse res, String domain) {
156                 Cookie cookie = getCookie(req);
157                 
158                 if (cookie == null) {
159                         return new SamlIdPCookie(req, res, domain);
160                 } else {
161                         return new SamlIdPCookie(cookie.getValue(), req, res, domain);
162                 }
163         }
164
165         /**
166          * Remove origin from the cachedata and write it back.
167          * @param origin
168          */
169         
170         public void deleteIdPName(String origin, int expiration) {
171                 idPList.remove(origin);
172                 writeCookie(expiration);
173         }
174
175         private void writeCookie(int expiration)
176         {
177                 Cookie cookie = getCookie(req);
178                 
179                 if (idPList.size() == 0) {
180                         //
181                         // Nothing to write, so delete the cookie
182                         //
183                         cookie.setPath("/");
184                         cookie.setMaxAge(0);
185                         res.addCookie(cookie);
186                         return;
187                 }
188
189                 //
190                 // Otherwise encode up the cookie
191                 //
192                 
193                 StringBuffer buffer = new StringBuffer();
194                 Iterator /*<String>*/ it = idPList.iterator();
195                 
196                 while (it.hasNext()) {
197                         String next = (String) it.next();
198                         String what = new String(Base64.encode(next.getBytes()));
199                         buffer.append(what).append(' ');
200                 }
201                 
202                 String value;
203                 try {
204                         value = URLEncoder.encode(buffer.toString(), "UTF-8");
205                 } catch (UnsupportedEncodingException e) {
206                         log.error("Could not encode cookie");
207                         return;
208                 }
209                 
210                 if (cookie == null) { 
211                         cookie = new Cookie(COOKIE_NAME, value);
212                 } else {
213                         cookie.setValue(value);
214                 }
215                 cookie.setComment("Used to cache selection of a user's Shibboleth IdP");
216                 cookie.setPath("/");
217
218
219                 cookie.setMaxAge(expiration);
220                 
221                 if (domain != null && domain != "") {
222                         cookie.setDomain(domain);
223                 }
224                 res.addCookie(cookie);
225         
226         }
227
228         /**
229          * Lookup to see whether there is an IdP for the given SP 
230          */
231         
232         public List /*<IdPSite>*/ getIdPList(List /*<IdPSiteSet>*/ siteSets, String SPName)
233         {
234                 
235                 Iterator /*<String>*/ it = idPList.iterator();
236                 List /*<IdPSite>*/ result = new ArrayList /*<IdPSite>*/(idPList.size());
237                 
238                 while (it.hasNext()) {
239                         String idPName = (String) it.next();
240                         IdPSite site = IdPSiteSet.IdPforSP(siteSets, idPName, SPName);
241                         if (site != null){
242                                 result.add(site);
243                         }
244                 }
245                 return result;
246         }
247
248
249         private static Cookie getCookie(HttpServletRequest req) {
250                 
251                 Cookie[] cookies = req.getCookies();
252                 if (cookies != null) {
253                         for (int i = 0; i < cookies.length; i++) {
254                                 if (cookies[i].getName().equals(COOKIE_NAME)) { 
255                                         return cookies[i];
256                                 }
257                         }
258                 }
259                 return null;
260         }
261 }