Further AA conversion.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / aa / AAServlet.java
1 /* 
2  * The Shibboleth License, Version 1. 
3  * Copyright (c) 2002 
4  * University Corporation for Advanced Internet Development, Inc. 
5  * All rights reserved
6  * 
7  * 
8  * Redistribution and use in source and binary forms, with or without 
9  * modification, are permitted provided that the following conditions are met:
10  * 
11  * Redistributions of source code must retain the above copyright notice, this 
12  * list of conditions and the following disclaimer.
13  * 
14  * Redistributions in binary form must reproduce the above copyright notice, 
15  * this list of conditions and the following disclaimer in the documentation 
16  * and/or other materials provided with the distribution, if any, must include 
17  * the following acknowledgment: "This product includes software developed by 
18  * the University Corporation for Advanced Internet Development 
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
20  * may appear in the software itself, if and wherever such third-party 
21  * acknowledgments normally appear.
22  * 
23  * Neither the name of Shibboleth nor the names of its contributors, nor 
24  * Internet2, nor the University Corporation for Advanced Internet Development, 
25  * Inc., nor UCAID may be used to endorse or promote products derived from this 
26  * software without specific prior written permission. For written permission, 
27  * please contact shibboleth@shibboleth.org
28  * 
29  * Products derived from this software may not be called Shibboleth, Internet2, 
30  * UCAID, or the University Corporation for Advanced Internet Development, nor 
31  * may Shibboleth appear in their name, without prior written permission of the 
32  * University Corporation for Advanced Internet Development.
33  * 
34  * 
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 package edu.internet2.middleware.shibboleth.aa;
51
52 import java.io.ByteArrayOutputStream;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.io.PrintStream;
56 import java.net.MalformedURLException;
57 import java.net.URI;
58 import java.net.URISyntaxException;
59 import java.net.URL;
60 import java.security.Principal;
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.Collections;
64 import java.util.Date;
65 import java.util.Enumeration;
66 import java.util.Iterator;
67 import java.util.List;
68 import java.util.Properties;
69 import java.util.StringTokenizer;
70
71 import javax.servlet.ServletException;
72 import javax.servlet.UnavailableException;
73 import javax.servlet.http.HttpServlet;
74 import javax.servlet.http.HttpServletRequest;
75 import javax.servlet.http.HttpServletResponse;
76
77 import org.apache.log4j.Logger;
78 import org.apache.log4j.MDC;
79 import org.apache.xerces.parsers.DOMParser;
80 import org.opensaml.QName;
81 import org.opensaml.SAMLAssertion;
82 import org.opensaml.SAMLAttribute;
83 import org.opensaml.SAMLAttributeQuery;
84 import org.opensaml.SAMLAttributeStatement;
85 import org.opensaml.SAMLAudienceRestrictionCondition;
86 import org.opensaml.SAMLBinding;
87 import org.opensaml.SAMLCondition;
88 import org.opensaml.SAMLException;
89 import org.opensaml.SAMLIdentifier;
90 import org.opensaml.SAMLRequest;
91 import org.opensaml.SAMLResponse;
92 import org.opensaml.SAMLStatement;
93 import org.opensaml.SAMLSubject;
94 import org.w3c.dom.Element;
95 import org.w3c.dom.NodeList;
96 import org.xml.sax.EntityResolver;
97 import org.xml.sax.ErrorHandler;
98 import org.xml.sax.InputSource;
99 import org.xml.sax.SAXException;
100 import org.xml.sax.SAXParseException;
101
102 import sun.misc.BASE64Decoder;
103 import edu.internet2.middleware.shibboleth.aa.arp.ArpEngine;
104 import edu.internet2.middleware.shibboleth.aa.arp.ArpException;
105 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver;
106 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolverException;
107 import edu.internet2.middleware.shibboleth.common.AuthNPrincipal;
108 import edu.internet2.middleware.shibboleth.common.NameIdentifierMapping;
109 import edu.internet2.middleware.shibboleth.common.NameIdentifierMappingException;
110 import edu.internet2.middleware.shibboleth.common.NameMapper;
111 import edu.internet2.middleware.shibboleth.common.RelyingParty;
112 import edu.internet2.middleware.shibboleth.common.SAMLBindingFactory;
113 import edu.internet2.middleware.shibboleth.common.ServiceProviderMapper;
114 import edu.internet2.middleware.shibboleth.common.ServiceProviderMapperException;
115 import edu.internet2.middleware.shibboleth.common.ShibResource;
116 import edu.internet2.middleware.shibboleth.common.ShibbolethOriginConfig;
117
118 /**
119  * @author Walter Hoehn
120  */
121
122 public class AAServlet extends HttpServlet {
123
124         private ShibbolethOriginConfig configuration;
125         protected AAResponder responder;
126         private NameMapper nameMapper;
127         private SAMLBinding binding;
128         private static Logger transactionLog = Logger.getLogger("Shibboleth-TRANSACTION");
129         private ServiceProviderMapper targetMapper;
130
131         private static Logger log = Logger.getLogger(AAServlet.class.getName());
132
133         public void init() throws ServletException {
134                 super.init();
135
136                 MDC.put("serviceId", "[AA] Core");
137                 log.info("Initializing Attribute Authority.");
138
139                 try {
140                         
141                         nameMapper = new NameMapper();
142                         loadConfiguration();
143
144                         //TODO pass in real config
145                         ArpEngine arpEngine = new ArpEngine(null);
146                         AttributeResolver resolver = new AttributeResolver(null);
147
148                         responder = new AAResponder(arpEngine, resolver);
149
150                         binding = SAMLBindingFactory.getInstance(SAMLBinding.SAML_SOAP_HTTPS);
151
152                         log.info("Attribute Authority initialization complete.");
153
154                 } catch (ArpException ae) {
155                         log.fatal("The AA could not be initialized due to a problem with the ARP Engine configuration: " + ae);
156                         throw new UnavailableException("Attribute Authority failed to initialize.");
157                 } catch (AttributeResolverException ne) {
158                         log.fatal(
159                                 "The AA could not be initialized due to a problem with the Attribute Resolver configuration: " + ne);
160                         throw new UnavailableException("Attribute Authority failed to initialize.");
161                 } catch (AAException ae) {
162                         log.fatal("The AA could not be initialized: " + ae);
163                         throw new UnavailableException("Attribute Authority failed to initialize.");
164                 } catch (SAMLException se) {
165                         log.fatal("SAML SOAP binding could not be loaded: " + se);
166                         throw new UnavailableException("Attribute Authority failed to initialize.");
167                 }
168
169         }
170         protected void loadConfiguration() throws AAException {
171
172                 //TODO could maybe factor some of the common stuff up a level.
173
174                 DOMParser parser = loadParser(true);
175
176                 String originConfigFile = getInitParameter("OriginConfigFile");
177                 if (originConfigFile == null) {
178                         originConfigFile = "/conf/origin.xml";
179                 }
180
181                 log.debug("Loading Configuration from (" + originConfigFile + ").");
182
183                 try {
184                         parser.parse(new InputSource(new ShibResource(originConfigFile, this.getClass()).getInputStream()));
185
186                 } catch (SAXException e) {
187                         log.error("Error while parsing origin configuration: " + e);
188                         throw new AAException("Error while parsing origin configuration.");
189                 } catch (IOException e) {
190                         log.error("Could not load origin configuration: " + e);
191                         throw new AAException("Could not load origin configuration.");
192                 }
193
194                 //Load global configuration properties
195                 configuration = new ShibbolethOriginConfig(parser.getDocument().getDocumentElement());
196
197                 //Load name mappings
198                 NodeList itemElements =
199                         parser.getDocument().getDocumentElement().getElementsByTagNameNS(
200                                 NameIdentifierMapping.mappingNamespace,
201                                 "NameMapping");
202
203                 for (int i = 0; i < itemElements.getLength(); i++) {
204                         try {
205                                 nameMapper.addNameMapping((Element) itemElements.item(i));
206                         } catch (NameIdentifierMappingException e) {
207                                 log.error("Name Identifier mapping could not be loaded: " + e);
208                         }
209                 }
210
211                 //Load relying party config
212                 try {
213                         targetMapper =
214                                 new ServiceProviderMapper(
215                                         parser.getDocument().getDocumentElement(),
216                                         configuration,
217                                         credentials,
218                                         nameMapper);
219                 } catch (ServiceProviderMapperException e) {
220                         log.error("Could not load origin configuration: " + e);
221                         throw new AAException("Could not load origin configuration.");
222                 }
223
224                 /*
225                                 //Set defaults
226                                 Properties defaultProps = new Properties();
227                                 defaultProps.setProperty(
228                                         "edu.internet2.middleware.shibboleth.aa.arp.provider.FileSystemArpRepository.Path",
229                                         "/conf/arps/");
230                                 defaultProps.setProperty(
231                                         "edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver.ResolverConfig",
232                                         "/conf/resolver.xml");
233                                 defaultProps.setProperty(
234                                         "edu.internet2.middleware.shibboleth.aa.arp.ArpRepository.implementation",
235                                         "edu.internet2.middleware.shibboleth.aa.arp.provider.FileSystemArpRepository");
236                                 defaultProps.setProperty("edu.internet2.middleware.shibboleth.audiences", "urn:mace:inqueue");
237                                 defaultProps.setProperty("edu.internet2.middleware.shibboleth.aa.AAServlet.passThruErrors", "false");
238                 
239                                 //Load from file
240                                 Properties properties = new Properties(defaultProps);
241                                 String propertiesFileLocation = getInitParameter("OriginPropertiesFile");
242                                 if (propertiesFileLocation == null) {
243                                         propertiesFileLocation = "/conf/origin.properties";
244                                 }
245                                 try {
246                                         log.debug("Loading Configuration from (" + propertiesFileLocation + ").");
247                                         properties.load(new ShibResource(propertiesFileLocation, this.getClass()).getInputStream());
248                 
249                                         //Make sure we have all required parameters
250                                         StringBuffer missingProperties = new StringBuffer();
251                                         String[] requiredProperties =
252                                                 {
253                                                         "edu.internet2.middleware.shibboleth.hs.HandleServlet.siteName",
254                                                         "edu.internet2.middleware.shibboleth.aa.AAServlet.authorityName",
255                                                         "edu.internet2.middleware.shibboleth.aa.arp.ArpRepository.implementation",
256                                                         "edu.internet2.middleware.shibboleth.audiences" };
257                 
258                                         for (int i = 0; i < requiredProperties.length; i++) {
259                                                 if (properties.getProperty(requiredProperties[i]) == null) {
260                                                         missingProperties.append("\"");
261                                                         missingProperties.append(requiredProperties[i]);
262                                                         missingProperties.append("\" ");
263                                                 }
264                                         }
265                                         if (missingProperties.length() > 0) {
266                                                 log.error(
267                                                         "Missing configuration data.  The following configuration properites have not been set: "
268                                                                 + missingProperties.toString());
269                                                 throw new AAException("Missing configuration data.");
270                                         }
271                 
272                                 } catch (IOException e) {
273                                         log.error("Could not load AA servlet configuration: " + e);
274                                         throw new AAException("Could not load AA servlet configuration.");
275                                 }
276                 
277                                 if (log.isDebugEnabled()) {
278                                         ByteArrayOutputStream debugStream = new ByteArrayOutputStream();
279                                         PrintStream debugPrinter = new PrintStream(debugStream);
280                                         properties.list(debugPrinter);
281                                         log.debug(
282                                                 "Runtime configuration parameters: " + System.getProperty("line.separator") + debugStream.toString());
283                                         try {
284                                                 debugStream.close();
285                                         } catch (IOException e) {
286                                                 log.error("Encountered a problem cleaning up resources: could not close debug stream.");
287                                         }
288                                 }
289                 
290                                 //Be nice and trim "extra" whitespace from config properties
291                                 Enumeration propNames = properties.propertyNames();
292                                 while (propNames.hasMoreElements()) {
293                                         String propName = (String) propNames.nextElement();
294                                         if (properties.getProperty(propName, "").matches(".+\\s$")) {
295                                                 log.debug("The configuration property (" + propName + ") contains trailing whitespace.  Trimming... ");
296                                                 properties.setProperty(propName, properties.getProperty(propName).trim());
297                                         }
298                                 }
299                 
300                                 return properties;
301                                 */
302         }
303         private DOMParser loadParser(boolean schemaChecking) throws AAException {
304
305                 DOMParser parser = new DOMParser();
306
307                 if (!schemaChecking) {
308                         return parser;
309                 }
310
311                 try {
312                         parser.setFeature("http://xml.org/sax/features/validation", true);
313                         parser.setFeature("http://apache.org/xml/features/validation/schema", true);
314
315                         parser.setEntityResolver(new EntityResolver() {
316                                 public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
317                                         log.debug("Resolving entity for System ID: " + systemId);
318                                         if (systemId != null) {
319                                                 StringTokenizer tokenString = new StringTokenizer(systemId, "/");
320                                                 String xsdFile = "";
321                                                 while (tokenString.hasMoreTokens()) {
322                                                         xsdFile = tokenString.nextToken();
323                                                 }
324                                                 if (xsdFile.endsWith(".xsd")) {
325                                                         InputStream stream;
326                                                         try {
327                                                                 stream = new ShibResource("/schemas/" + xsdFile, this.getClass()).getInputStream();
328                                                         } catch (IOException ioe) {
329                                                                 log.error("Error loading schema: " + xsdFile + ": " + ioe);
330                                                                 return null;
331                                                         }
332                                                         if (stream != null) {
333                                                                 return new InputSource(stream);
334                                                         }
335                                                 }
336                                         }
337                                         return null;
338                                 }
339                         });
340
341                         parser.setErrorHandler(new ErrorHandler() {
342                                 public void error(SAXParseException arg0) throws SAXException {
343                                         throw new SAXException("Error parsing xml file: " + arg0);
344                                 }
345                                 public void fatalError(SAXParseException arg0) throws SAXException {
346                                         throw new SAXException("Error parsing xml file: " + arg0);
347                                 }
348                                 public void warning(SAXParseException arg0) throws SAXException {
349                                         throw new SAXException("Error parsing xml file: " + arg0);
350                                 }
351                         });
352
353                 } catch (SAXException e) {
354                         log.error("Unable to setup a workable XML parser: " + e);
355                         throw new AAException("Unable to setup a workable XML parser.");
356                 }
357                 return parser;
358         }
359
360         public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
361
362                 MDC.put("serviceId", "[AA] " + new SAMLIdentifier().toString());
363                 MDC.put("remoteAddr", req.getRemoteAddr());
364                 log.info("Handling request.");
365
366                 StringBuffer credentialName = new StringBuffer();
367                 SAMLRequest samlRequest = binding.receive(req, credentialName);
368                 if (samlRequest.getQuery() == null || !(samlRequest.getQuery() instanceof SAMLAttributeQuery)) {
369                         //TODO better exception
370                         throw new SAMLException(
371                                 SAMLException.REQUESTER,
372                                 "AASaml.receive() can only respond to a SAML Attribute Query");
373                 }
374                 SAMLAttributeQuery attributeQuery = (SAMLAttributeQuery) samlRequest.getQuery();
375
376                 try {
377
378                         RelyingParty relyingParty = targetMapper.getRelyingParty(attributeQuery.getResource());
379
380                         if (relyingParty.getProviderId() != null
381                                 && !relyingParty.getProviderId().equals(attributeQuery.getSubject().getName().getNameQualifier())) {
382                                 log.error(
383                                         "The name qualifier for the referenced subject ("
384                                                 + attributeQuery.getSubject().getName().getNameQualifier()
385                                                 + ") is not valid for this identiy provider.");
386                                 throw new NameIdentifierMappingException(
387                                         "The name qualifier for the referenced subject ("
388                                                 + attributeQuery.getSubject().getName().getNameQualifier()
389                                                 + ") is not valid for this identiy provider.");
390                         }
391
392 //TODO fix logging
393                         //log.info("Attribute Query Handle for this request: (" + saml.getHandle() + ").");
394                         
395                         Principal principal = null;
396                         if (attributeQuery.getSubject().getName().getName().equalsIgnoreCase("foo")) {
397                                 // for testing
398                                 principal = new AuthNPrincipal("test-handle");
399                         } else {
400                                 principal = handleRepository.getPrincipal(attributeQuery.getSubject().getName().getName()), attributeQuery.getSubject().getName().getFormat());
401                         }
402
403                         URL resource = null;
404                         try {
405                                 if (attributeQuery.getResource() != null)
406                                         resource = new URL(attributeQuery.getResource());
407                         } catch (MalformedURLException mue) {
408                                 log.error(
409                                         "Request contained an improperly formatted resource identifier.  Attempting to "
410                                                 + "handle request without one.");
411                         }
412
413                         if (credentialName == null || credentialName.toString().equals("")) {
414                                 //TODO update messages
415                                 log.info("Request is from an unauthenticated SHAR.");
416                         } else {
417                                 log.info("Request is from SHAR: (" + credentialName + ").");
418                         }
419
420                         SAMLAttribute[] attrs;
421                         Iterator requestedAttrsIterator = attributeQuery.getDesignators();
422                         if (requestedAttrsIterator.hasNext()) {
423                                 log.info("Request designates specific attributes, resolving this set.");
424                                 ArrayList requestedAttrs = new ArrayList();
425                                 while (requestedAttrsIterator.hasNext()) {
426                                         SAMLAttribute attribute = (SAMLAttribute) requestedAttrsIterator.next();
427                                         try {
428                                                 log.debug("Designated attribute: (" + attribute.getName() + ")");
429                                                 requestedAttrs.add(new URI(attribute.getName()));
430                                         } catch (URISyntaxException use) {
431                                                 log.error(
432                                                         "Request designated an attribute name that does not conform to the required URI syntax ("
433                                                                 + attribute.getName()
434                                                                 + ").  Ignoring this attribute");
435                                         }
436                                 }
437                                 attrs =
438                                         responder.getReleaseAttributes(
439                                                 principal,
440                                                 credentialName.toString(),
441                                                 resource,
442                                                 (URI[]) requestedAttrs.toArray(new URI[0]));
443                         } else {
444                                 log.info("Request does not designate specific attributes, resolving all available.");
445                                 attrs = responder.getReleaseAttributes(principal, credentialName.toString(), resource);
446                         }
447
448                         log.info("Found " + attrs.length + " attribute(s) for " + principal.getName());
449                         sendResponse(resp, attrs, samlRequest, null);
450                         log.info("Successfully responded about " + principal.getName());
451
452                         //TODO place transaction log statement here
453
454                         //TODO probably need to change a bunch of these messages to not be handle-centric
455                 } catch (NameIdentifierMappingException e) {
456                         log.info("Could not associate the Attribute Query Handle with a principal: " + e);
457                         try {
458                                 QName[] codes =
459                                         {
460                                                 SAMLException.REQUESTER,
461                                                 new QName(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS, "InvalidHandle")};
462                                 if (configuration
463                                         .getProperty("edu.internet2.middleware.shibboleth.aa.AAServlet.passThruErrors", "false")
464                                         .equals("true")) {
465                                         saml.fail(
466                                                 resp,
467                                                 new SAMLException(
468                                                         Arrays.asList(codes),
469                                                         "The supplied Attribute Query Handle was unrecognized or expired.",
470                                                         e));
471
472                                 } else {
473                                         saml.fail(
474                                                 resp,
475                                                 new SAMLException(
476                                                         Arrays.asList(codes),
477                                                         "The supplied Attribute Query Handle was unrecognized or expired."));
478                                 }
479                                 return;
480                         } catch (Exception ee) {
481                                 log.fatal("Could not construct a SAML error response: " + ee);
482                                 throw new ServletException("Attribute Authority response failure.");
483                         }
484
485                 } catch (Exception e) {
486                         log.error("Error while processing request: " + e);
487                         try {
488                                 if (configuration
489                                         .getProperty("edu.internet2.middleware.shibboleth.aa.AAServlet.passThruErrors", "false")
490                                         .equals("true")) {
491                                         saml.fail(resp, new SAMLException(SAMLException.RESPONDER, "General error processing request.", e));
492                                 } else {
493                                         saml.fail(resp, new SAMLException(SAMLException.RESPONDER, "General error processing request."));
494                                 }
495                                 return;
496                         } catch (Exception ee) {
497                                 log.fatal("Could not construct a SAML error response: " + ee);
498                                 throw new ServletException("Attribute Authority response failure.");
499                         }
500
501                 }
502         }
503         public void sendResponse(
504                 HttpServletResponse resp,
505                 SAMLAttribute[] attrs,
506                 SAMLRequest samlRequest,
507                 RelyingParty relyingParty,
508                 SAMLException exception)
509                 throws IOException {
510
511                 SAMLException ourSE = null;
512                 SAMLResponse samlResponse = null;
513
514                 try {
515                         if (attrs == null || attrs.length == 0) {
516                                 samlResponse = new SAMLResponse(samlRequest.getId(), null, null, exception);
517
518                         } else {
519                                 // Determine max lifetime, and filter via query if necessary.
520                                 Date now = new Date();
521                                 Date then = null;
522                                 long min = 0;
523
524                                 if (samlRequest.getQuery() == null || !(samlRequest.getQuery() instanceof SAMLAttributeQuery)) {
525                                         //TODO better exception
526                                         throw new SAMLException(
527                                                 SAMLException.REQUESTER,
528                                                 "AASaml.receive() can only respond to a SAML Attribute Query");
529                                 }
530                                 SAMLAttributeQuery attributeQuery = (SAMLAttributeQuery) samlRequest.getQuery();
531
532                                 //Reference requested subject
533                                 SAMLSubject rSubject = (SAMLSubject) attributeQuery.getSubject().clone();
534
535                                 //Set appropriate audience
536                                 ArrayList audiences = new ArrayList();
537                                 if (relyingParty.getProviderId() != null) {
538                                         audiences.add(relyingParty.getProviderId());
539                                 }
540                                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
541                                         audiences.add(relyingParty.getName());
542                                 }
543                                 SAMLCondition condition = new SAMLAudienceRestrictionCondition(audiences);
544
545                                 //Put all attributes into an assertion
546                                 SAMLStatement statement = new SAMLAttributeStatement(rSubject, Arrays.asList(attrs));
547
548                                 //TODO double check this stuff
549                                 if (min > 0) {
550                                         then = new Date(now.getTime() + (min * 1000));
551                                 }
552
553                                 SAMLAssertion sAssertion =
554                                         new SAMLAssertion(
555                                                 relyingParty.getIdentityProvider().getProviderId(),
556                                                 now,
557                                                 then,
558                                                 Collections.singleton(condition),
559                                                 null,
560                                                 Collections.singleton(statement));
561
562                                 samlResponse =
563                                         new SAMLResponse(samlRequest.getId(), null, Collections.singleton(sAssertion), exception);
564                         }
565                 } catch (SAMLException se) {
566                         ourSE = se;
567                 } catch (CloneNotSupportedException ex) {
568                         ourSE = new SAMLException(SAMLException.RESPONDER, ex);
569
570                 } finally {
571
572                         if (log.isDebugEnabled()) {
573                                 try {
574                                         log.debug(
575                                                 "Dumping generated SAML Response:"
576                                                         + System.getProperty("line.separator")
577                                                         + new String(
578                                                                 new BASE64Decoder().decodeBuffer(new String(samlResponse.toBase64(), "ASCII")),
579                                                                 "UTF8"));
580                                 } catch (SAMLException e) {
581                                         log.error("Encountered an error while decoding SAMLReponse for logging purposes.");
582                                 } catch (IOException e) {
583                                         log.error("Encountered an error while decoding SAMLReponse for logging purposes.");
584                                 }
585                         }
586
587                         binding.respond(resp, samlResponse, ourSE);
588                 }
589         }
590
591         public void sendFailure(HttpServletResponse httpResponse, SAMLRequest samlRequest, SAMLException exception)
592                 throws IOException {
593                 try {
594                         SAMLResponse samlResponse =
595                                 new SAMLResponse((samlRequest != null) ? samlRequest.getId() : null, null, null, exception);
596                         if (log.isDebugEnabled()) {
597                                 try {
598                                         log.debug(
599                                                 "Dumping generated SAML Error Response:"
600                                                         + System.getProperty("line.separator")
601                                                         + new String(
602                                                                 new BASE64Decoder().decodeBuffer(new String(samlResponse.toBase64(), "ASCII")),
603                                                                 "UTF8"));
604                                 } catch (IOException e) {
605                                         log.error("Encountered an error while decoding SAMLReponse for logging purposes.");
606                                 }
607                         }
608                         binding.respond(httpResponse, samlResponse, null);
609                         log.debug("Returning SAML Error Response.");
610                 } catch (SAMLException se) {
611                         binding.respond(httpResponse, null, exception);
612                         log.error("AA failed to make an error message: " + se);
613                 }
614         }
615
616 }