First SOAP encoder.
[shibboleth/cpp-opensaml.git] / saml / saml1 / binding / impl / SAML1SOAPEncoder.cpp
1 /*
2  *  Copyright 2001-2006 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  * SAML1SOAPEncoder.cpp
19  * 
20  * SAML 1.x SOAP binding message encoder
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "binding/HTTPResponse.h"
26 #include "saml1/binding/SAML1SOAPEncoder.h"
27 #include "saml1/core/Protocols.h"
28
29 #include <sstream>
30 #include <log4cpp/Category.hh>
31 #include <xmltooling/util/NDC.h>
32 #include <xmltooling/soap/SOAP.h>
33
34 using namespace opensaml::saml1p;
35 using namespace opensaml;
36 using namespace xmlsignature;
37 using namespace soap11;
38 using namespace xmltooling;
39 using namespace log4cpp;
40 using namespace std;
41
42 namespace opensaml {
43     namespace saml1p {              
44         MessageEncoder* SAML_DLLLOCAL SAML1SOAPEncoderFactory(const DOMElement* const & e)
45         {
46             return new SAML1SOAPEncoder(e);
47         }
48     };
49 };
50
51 SAML1SOAPEncoder::SAML1SOAPEncoder(const DOMElement* e) {}
52
53 long SAML1SOAPEncoder::encode(
54     GenericResponse& genericResponse,
55     XMLObject* xmlObject,
56     const char* destination,
57     const char* recipientID,
58     const char* relayState,
59     const CredentialResolver* credResolver,
60     const XMLCh* sigAlgorithm
61     ) const
62 {
63 #ifdef _DEBUG
64     xmltooling::NDC ndc("encode");
65 #endif
66     Category& log = Category::getInstance(SAML_LOGCAT".MessageEncoder.SAML1SOAP");
67
68     log.debug("validating input");
69     if (xmlObject->getParent())
70         throw BindingException("Cannot encode XML content with parent.");
71
72     genericResponse.setContentType("text/xml");
73
74     DOMElement* rootElement = NULL;
75     Response* response = dynamic_cast<Response*>(xmlObject);
76     if (response) {
77         try {
78             Envelope* env = EnvelopeBuilder::buildEnvelope();
79             Body* body = BodyBuilder::buildBody();
80             env->setBody(body);
81             body->getXMLObjects().push_back(response);
82             if (credResolver ) {
83                 if (response->getSignature()) {
84                     log.debug("response already signed, skipping signature operation");
85                     rootElement = env->marshall();
86                 }
87                 else {
88                     log.debug("signing and marshalling the response");
89         
90                     // Build a Signature.
91                     Signature* sig = buildSignature(credResolver, sigAlgorithm);
92                     response->setSignature(sig);
93             
94                     // Sign response while marshalling.
95                     vector<Signature*> sigs(1,sig);
96                     rootElement = env->marshall((DOMDocument*)NULL,&sigs);
97                 }
98             }
99             else {
100                 log.debug("marshalling the response");
101                 rootElement = env->marshall();
102             }
103             
104             string xmlbuf;
105             XMLHelper::serialize(rootElement, xmlbuf);
106             istringstream s(xmlbuf);
107             log.debug("sending serialized response");
108             long ret = genericResponse.sendResponse(s);
109         
110             // Cleanup by destroying XML.
111             delete env;
112             return ret;
113         }
114         catch (XMLToolingException&) {
115             // A bit weird...we have to "revert" things so that the response is isolated
116             // so the caller can free it.
117             if (response->getParent()) {
118                 response->getParent()->detach();
119                 response->detach();
120             }
121             throw;
122         }
123     }
124
125     Fault* fault = dynamic_cast<Fault*>(xmlObject);
126     if (fault) {
127         try {
128             log.debug("building Envelope and marshalling Fault");
129             Envelope* env = EnvelopeBuilder::buildEnvelope();
130             Body* body = BodyBuilder::buildBody();
131             env->setBody(body);
132             body->getXMLObjects().push_back(fault);
133             rootElement = env->marshall();
134     
135             string xmlbuf;
136             XMLHelper::serialize(rootElement, xmlbuf);
137             istringstream s(xmlbuf);
138             log.debug("sending serialized fault");
139             long ret = genericResponse.sendError(s);
140         
141             // Cleanup by destroying XML.
142             delete env;
143             return ret;
144         }
145         catch (XMLToolingException&) {
146             // A bit weird...we have to "revert" things so that the fault is isolated
147             // so the caller can free it.
148             if (fault->getParent()) {
149                 fault->getParent()->detach();
150                 fault->detach();
151             }
152             throw;
153         }
154     }
155
156     Envelope* env = dynamic_cast<Envelope*>(xmlObject);
157     if (env) {
158         log.debug("marshalling envelope");
159         rootElement = env->marshall();
160
161         bool error =
162             (env->getBody() &&
163                 env->getBody()->hasChildren() &&
164                     dynamic_cast<Fault*>(env->getBody()->getXMLObjects().front()));
165
166         string xmlbuf;
167         XMLHelper::serialize(rootElement, xmlbuf);
168         istringstream s(xmlbuf);
169         log.debug("sending serialized envelope");
170         long ret = error ? genericResponse.sendError(s) : genericResponse.sendResponse(s);
171     
172         // Cleanup by destroying XML.
173         delete env;
174         return ret;
175     }
176
177     throw BindingException("XML content for SAML 1.x SOAP Encoder must be a SAML 1.x <Response> or SOAP Fault/Envelope.");
178 }