Factor out application ID when remoting.
[shibboleth/cpp-sp.git] / shibsp / handler / impl / RemotedHandler.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  * RemotedHandler.cpp
19  * 
20  * Base class for handlers that need SP request/response layer to be remoted. 
21  */
22
23 #include "internal.h"
24 #include "Application.h"
25 #include "exceptions.h"
26 #include "ServiceProvider.h"
27 #include "handler/RemotedHandler.h"
28
29 #include <algorithm>
30 #include <log4cpp/Category.hh>
31 #include <xmltooling/unicode.h>
32
33 #ifndef SHIBSP_LITE
34 # include <saml/util/CGIParser.h>
35 # include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
36 # include <xsec/enc/XSECCryptoException.hpp>
37 # include <xsec/framework/XSECException.hpp>
38 # include <xsec/framework/XSECProvider.hpp>
39 #endif
40
41 using namespace shibsp;
42 using namespace opensaml;
43 using namespace xmltooling;
44 using namespace log4cpp;
45 using namespace xercesc;
46 using namespace std;
47
48 #ifndef SHIBSP_LITE
49 namespace shibsp {
50     class SHIBSP_DLLLOCAL RemotedRequest : public virtual HTTPRequest 
51     {
52         DDF& m_input;
53         mutable CGIParser* m_parser;
54         mutable vector<XSECCryptoX509*> m_certs;
55     public:
56         RemotedRequest(DDF& input) : m_input(input), m_parser(NULL) {}
57         virtual ~RemotedRequest() {
58             for_each(m_certs.begin(), m_certs.end(), xmltooling::cleanup<XSECCryptoX509>());
59             delete m_parser;
60         }
61
62         // GenericRequest
63         const char* getScheme() const {
64             return m_input["scheme"].string();
65         }
66         const char* getHostname() const {
67             return m_input["hostname"].string();
68         }
69         int getPort() const {
70             return m_input["port"].integer();
71         }
72         std::string getContentType() const {
73             DDF s = m_input["content_type"];
74             return s.string() ? s.string() : "";
75         }
76         long getContentLength() const {
77             return m_input["content_length"].integer();
78         }
79         const char* getRequestBody() const {
80             return m_input["body"].string();
81         }
82
83         const char* getParameter(const char* name) const;
84         std::vector<const char*>::size_type getParameters(const char* name, std::vector<const char*>& values) const;
85         
86         std::string getRemoteUser() const {
87             DDF s = m_input["remote_user"];
88             return s.string() ? s.string() : "";
89         }
90         std::string getRemoteAddr() const {
91             DDF s = m_input["client_addr"];
92             return s.string() ? s.string() : "";
93         }
94
95         const std::vector<XSECCryptoX509*>& getClientCertificates() const;
96         
97         // HTTPRequest
98         const char* getMethod() const {
99             return m_input["method"].string();
100         }
101         const char* getRequestURI() const {
102             return m_input["uri"].string();
103         }
104         const char* getRequestURL() const {
105             return m_input["url"].string();
106         }
107         const char* getQueryString() const {
108             return m_input["query"].string();
109         }
110         std::string getHeader(const char* name) const {
111             DDF s = m_input["headers"][name];
112             return s.string() ? s.string() : "";
113         }
114     };
115
116     class SHIBSP_DLLLOCAL RemotedResponse : public virtual HTTPResponse 
117     {
118         DDF& m_output;
119     public:
120         RemotedResponse(DDF& output) : m_output(output) {}
121         virtual ~RemotedResponse() {}
122        
123         // GenericResponse
124         long sendResponse(std::istream& inputStream, long status);
125         
126         // HTTPResponse
127         void setResponseHeader(const char* name, const char* value);
128         long sendRedirect(const char* url);
129     };
130 }
131
132 const char* RemotedRequest::getParameter(const char* name) const
133 {
134     if (!m_parser)
135         m_parser=new CGIParser(*this);
136     
137     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
138     return (bounds.first==bounds.second) ? NULL : bounds.first->second;
139 }
140
141 std::vector<const char*>::size_type RemotedRequest::getParameters(const char* name, std::vector<const char*>& values) const
142 {
143     if (!m_parser)
144         m_parser=new CGIParser(*this);
145
146     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
147     while (bounds.first!=bounds.second) {
148         values.push_back(bounds.first->second);
149         ++bounds.first;
150     }
151     return values.size();
152 }
153
154 const std::vector<XSECCryptoX509*>& RemotedRequest::getClientCertificates() const
155 {
156     if (m_certs.empty()) {
157         DDF certs = m_input["certificates"];
158         DDF cert = certs.first();
159         while (cert.string()) {
160             try {
161                 auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());
162                 if (strstr(cert.string(), "BEGIN"))
163                     x509->loadX509PEM(cert.string(), cert.strlen());
164                 else
165                     x509->loadX509Base64Bin(cert.string(), cert.strlen());
166                 m_certs.push_back(x509.release());
167             }
168             catch(XSECException& e) {
169                 auto_ptr_char temp(e.getMsg());
170                 Category::getInstance(SHIBSP_LOGCAT".SPRequest").error("XML-Security exception loading client certificate: %s", temp.get());
171             }
172             catch(XSECCryptoException& e) {
173                 Category::getInstance(SHIBSP_LOGCAT".SPRequest").error("XML-Security exception loading client certificate: %s", e.getMsg());
174             }
175             cert = certs.next();
176         }
177     }
178     return m_certs;
179 }
180
181 long RemotedResponse::sendResponse(std::istream& in, long status)
182 {
183     string msg;
184     char buf[1024];
185     while (in) {
186         in.read(buf,1024);
187         msg.append(buf,in.gcount());
188     }
189     if (!m_output.isstruct())
190         m_output.structure();
191     m_output.addmember("response.data").string(msg.c_str());
192     m_output.addmember("response.status").integer(status);
193     return status;
194 }
195
196 void RemotedResponse::setResponseHeader(const char* name, const char* value)
197 {
198     if (!m_output.isstruct())
199         m_output.structure();
200     DDF hdrs = m_output["headers"];
201     if (hdrs.isnull())
202         hdrs = m_output.addmember("headers").structure();
203     hdrs.addmember(name).string(value);
204 }
205
206 long RemotedResponse::sendRedirect(const char* url)
207 {
208     if (!m_output.isstruct())
209         m_output.structure();
210     m_output.addmember("redirect").string(url);
211     return HTTPResponse::XMLTOOLING_HTTP_STATUS_MOVED;
212 }
213
214 #endif
215
216 void RemotedHandler::setAddress(const char* address)
217 {
218     if (!m_address.empty())
219         throw ConfigurationException("Cannot register a remoting address twice for the same Handler.");
220     m_address = address;
221     SPConfig& conf = SPConfig::getConfig();
222     if (!conf.isEnabled(SPConfig::InProcess)) {
223         ListenerService* listener = conf.getServiceProvider()->getListenerService(false);
224         if (listener)
225             listener->regListener(m_address.c_str(),this);
226         else
227             Category::getInstance(SHIBSP_LOGCAT".Handler").info("no ListenerService available, handler remoting disabled");
228     }
229 }
230
231 RemotedHandler::~RemotedHandler()
232 {
233     SPConfig& conf = SPConfig::getConfig();
234     ListenerService* listener=conf.getServiceProvider()->getListenerService(false);
235     if (listener && conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess))
236         listener->unregListener(m_address.c_str(),this);
237 }
238
239 DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers, bool certs) const
240 {
241     DDF in = DDF(m_address.c_str()).structure();
242     in.addmember("application_id").string(request.getApplication().getId());
243     in.addmember("scheme").string(request.getScheme());
244     in.addmember("hostname").string(request.getHostname());
245     in.addmember("port").integer(request.getPort());
246     in.addmember("content_type").string(request.getContentType().c_str());
247     in.addmember("content_length").integer(request.getContentLength());
248     in.addmember("body").string(request.getRequestBody());
249     in.addmember("remote_user").string(request.getRemoteUser().c_str());
250     in.addmember("client_addr").string(request.getRemoteAddr().c_str());
251     in.addmember("method").string(request.getMethod());
252     in.addmember("uri").string(request.getRequestURI());
253     in.addmember("url").string(request.getRequestURL());
254     in.addmember("query").string(request.getQueryString());
255
256     if (headers) {
257         string hdr;
258         DDF hin = in.addmember("headers").structure();
259         for (vector<string>::const_iterator h = headers->begin(); h!=headers->end(); ++h) {
260             hdr = request.getHeader(h->c_str());
261             if (!hdr.empty())
262                 hin.addmember(h->c_str()).string(hdr.c_str());
263         }
264     }
265
266     if (certs) {
267 #ifndef SHIBSP_LITE
268         const vector<XSECCryptoX509*>& xvec = request.getClientCertificates();
269         if (!xvec.empty()) {
270             DDF clist = in.addmember("certificates").list();
271             for (vector<XSECCryptoX509*>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
272                 DDF x509 = DDF(NULL).string((*x)->getDEREncodingSB().rawCharBuffer());
273                 clist.add(x509);
274             }
275         }
276 #else
277         const vector<string>& xvec = request.getClientCertificates();
278         if (!xvec.empty()) {
279             DDF clist = in.addmember("certificates").list();
280             for (vector<string>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
281                 DDF x509 = DDF(NULL).string(x->c_str());
282                 clist.add(x509);
283             }
284         }
285 #endif
286     }
287
288     return in;
289 }
290
291 pair<bool,long> RemotedHandler::unwrap(SPRequest& request, DDF& out) const
292 {
293     DDF h = out["headers"];
294     DDF hdr = h.first();
295     while (hdr.isstring()) {
296 #ifdef HAVE_STRCASECMP
297         if (!strcasecmp(hdr.name(), "Content-Type"))
298 #else
299         if (!stricmp(hdr.name(), "Content-Type"))
300 #endif
301             request.setContentType(hdr.string());
302         request.setResponseHeader(hdr.name(), hdr.string());
303         hdr = h.next();
304     }
305     h = out["redirect"];
306     if (h.isstring())
307         return make_pair(true, request.sendRedirect(h.string()));
308     h = out["response"];
309     if (h.isstruct()) {
310         istringstream s(h["data"].string());
311         return make_pair(true, request.sendResponse(s, h["status"].integer()));
312     }
313     return make_pair(false,0);
314 }
315
316 HTTPRequest* RemotedHandler::getRequest(DDF& in) const
317 {
318 #ifndef SHIBSP_LITE
319     return new RemotedRequest(in);
320 #else
321     throw ConfigurationException("Cannot process message using lite version of shibsp library.");
322 #endif
323 }
324
325 HTTPResponse* RemotedHandler::getResponse(DDF& out) const
326 {
327 #ifndef SHIBSP_LITE
328     return new RemotedResponse(out);
329 #else
330     throw ConfigurationException("Cannot process message using lite version of shibsp library.");
331 #endif
332 }