Expose a "user agent present" property.
[shibboleth/cpp-opensaml.git] / saml / saml2 / binding / impl / SAML2SOAPEncoder.cpp
1 /*
2  *  Copyright 2001-2007 Internet2
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 /**
18  * SAML2SOAPEncoder.cpp
19  * 
20  * SAML 2.0 SOAP binding message encoder
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "binding/MessageEncoder.h"
26 #include "signature/ContentReference.h"
27 #include "saml2/core/Protocols.h"
28
29 #include <sstream>
30 #include <log4cpp/Category.hh>
31 #include <xmltooling/io/HTTPResponse.h>
32 #include <xmltooling/util/NDC.h>
33 #include <xmltooling/soap/SOAP.h>
34
35 using namespace opensaml::saml2p;
36 using namespace opensaml::saml2md;
37 using namespace opensaml;
38 using namespace xmlsignature;
39 using namespace soap11;
40 using namespace xmltooling;
41 using namespace log4cpp;
42 using namespace std;
43
44 namespace opensaml {
45     namespace saml2p {              
46         class SAML_DLLLOCAL SAML2SOAPEncoder : public MessageEncoder
47         {
48         public:
49             SAML2SOAPEncoder(const DOMElement* e);
50             virtual ~SAML2SOAPEncoder() {}
51
52             bool isUserAgentPresent() const {
53                 return false;
54             }
55
56             long encode(
57                 GenericResponse& genericResponse,
58                 XMLObject* xmlObject,
59                 const char* destination,
60                 const EntityDescriptor* recipient=NULL,
61                 const char* relayState=NULL,
62                 const ArtifactGenerator* artifactGenerator=NULL,
63                 const Credential* credential=NULL,
64                 const XMLCh* signatureAlg=NULL,
65                 const XMLCh* digestAlg=NULL
66                 ) const;
67         };
68
69         MessageEncoder* SAML_DLLLOCAL SAML2SOAPEncoderFactory(const DOMElement* const & e)
70         {
71             return new SAML2SOAPEncoder(e);
72         }
73     };
74 };
75
76 SAML2SOAPEncoder::SAML2SOAPEncoder(const DOMElement* e) {}
77
78 long SAML2SOAPEncoder::encode(
79     GenericResponse& genericResponse,
80     XMLObject* xmlObject,
81     const char* destination,
82     const EntityDescriptor* recipient,
83     const char* relayState,
84     const ArtifactGenerator* artifactGenerator,
85     const Credential* credential,
86     const XMLCh* signatureAlg,
87     const XMLCh* digestAlg
88     ) const
89 {
90 #ifdef _DEBUG
91     xmltooling::NDC ndc("encode");
92 #endif
93     Category& log = Category::getInstance(SAML_LOGCAT".MessageEncoder.SAML2SOAP");
94
95     log.debug("validating input");
96     if (xmlObject->getParent())
97         throw BindingException("Cannot encode XML content with parent.");
98
99     genericResponse.setContentType("text/xml");
100     HTTPResponse* httpResponse = dynamic_cast<HTTPResponse*>(&genericResponse);
101     if (httpResponse) {
102         httpResponse->setResponseHeader("Cache-Control", "no-cache, no-store, must-revalidate, private");
103         httpResponse->setResponseHeader("Pragma", "no-cache");
104     }
105
106     DOMElement* rootElement = NULL;
107     StatusResponseType* response = dynamic_cast<StatusResponseType*>(xmlObject);
108     if (response) {
109         try {
110             Envelope* env = EnvelopeBuilder::buildEnvelope();
111             Body* body = BodyBuilder::buildBody();
112             env->setBody(body);
113             body->getUnknownXMLObjects().push_back(response);
114             if (credential) {
115                 if (response->getSignature()) {
116                     log.debug("response already signed, skipping signature operation");
117                     rootElement = env->marshall();
118                 }
119                 else {
120                     log.debug("signing and marshalling the response");
121         
122                     // Build a Signature.
123                     Signature* sig = SignatureBuilder::buildSignature();
124                     response->setSignature(sig);    
125                     if (signatureAlg)
126                         sig->setSignatureAlgorithm(signatureAlg);
127                     if (digestAlg) {
128                         opensaml::ContentReference* cr = dynamic_cast<opensaml::ContentReference*>(sig->getContentReference());
129                         if (cr)
130                             cr->setDigestAlgorithm(digestAlg);
131                     }
132             
133                     // Sign response while marshalling.
134                     vector<Signature*> sigs(1,sig);
135                     rootElement = env->marshall((DOMDocument*)NULL,&sigs,credential);
136                 }
137             }
138             else {
139                 log.debug("marshalling the response");
140                 rootElement = env->marshall();
141             }
142             
143             stringstream s;
144             s << *rootElement;
145             log.debug("sending serialized response");
146             long ret = genericResponse.sendResponse(s);
147         
148             // Cleanup by destroying XML.
149             delete env;
150             return ret;
151         }
152         catch (XMLToolingException&) {
153             // A bit weird...we have to "revert" things so that the response is isolated
154             // so the caller can free it.
155             if (response->getParent()) {
156                 response->getParent()->detach();
157                 response->detach();
158             }
159             throw;
160         }
161     }
162
163     Fault* fault = dynamic_cast<Fault*>(xmlObject);
164     if (fault) {
165         try {
166             log.debug("building Envelope and marshalling Fault");
167             Envelope* env = EnvelopeBuilder::buildEnvelope();
168             Body* body = BodyBuilder::buildBody();
169             env->setBody(body);
170             body->getUnknownXMLObjects().push_back(fault);
171             rootElement = env->marshall();
172     
173             string xmlbuf;
174             XMLHelper::serialize(rootElement, xmlbuf);
175             istringstream s(xmlbuf);
176             log.debug("sending serialized fault");
177             long ret = genericResponse.sendError(s);
178         
179             // Cleanup by destroying XML.
180             delete env;
181             return ret;
182         }
183         catch (XMLToolingException&) {
184             // A bit weird...we have to "revert" things so that the fault is isolated
185             // so the caller can free it.
186             if (fault->getParent()) {
187                 fault->getParent()->detach();
188                 fault->detach();
189             }
190             throw;
191         }
192     }
193
194     Envelope* env = dynamic_cast<Envelope*>(xmlObject);
195     if (env) {
196         log.debug("marshalling envelope");
197         rootElement = env->marshall();
198
199         bool error =
200             (env->getBody() &&
201                 env->getBody()->hasChildren() &&
202                     dynamic_cast<Fault*>(env->getBody()->getUnknownXMLObjects().front()));
203
204         string xmlbuf;
205         XMLHelper::serialize(rootElement, xmlbuf);
206         istringstream s(xmlbuf);
207         log.debug("sending serialized envelope");
208         long ret = error ? genericResponse.sendError(s) : genericResponse.sendResponse(s);
209     
210         // Cleanup by destroying XML.
211         delete env;
212         return ret;
213     }
214
215     throw BindingException("XML content for SAML 2.0 SOAP Encoder must be a SAML 2.0 response or SOAP Fault/Envelope.");
216 }