2 * Copyright 2001-2007 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * Base class for handlers that need SP request/response layer to be remoted.
24 #include "Application.h"
25 #include "exceptions.h"
26 #include "ServiceProvider.h"
27 #include "handler/RemotedHandler.h"
30 #include <log4cpp/Category.hh>
31 #include <xmltooling/unicode.h>
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>
41 using namespace shibsp;
42 using namespace opensaml;
43 using namespace xmltooling;
44 using namespace log4cpp;
45 using namespace xercesc;
50 class SHIBSP_DLLLOCAL RemotedRequest : public virtual HTTPRequest
53 mutable CGIParser* m_parser;
54 mutable vector<XSECCryptoX509*> m_certs;
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>());
63 const char* getScheme() const {
64 return m_input["scheme"].string();
66 const char* getHostname() const {
67 return m_input["hostname"].string();
70 return m_input["port"].integer();
72 std::string getContentType() const {
73 DDF s = m_input["content_type"];
74 return s.string() ? s.string() : "";
76 long getContentLength() const {
77 return m_input["content_length"].integer();
79 const char* getRequestBody() const {
80 return m_input["body"].string();
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;
86 std::string getRemoteUser() const {
87 DDF s = m_input["remote_user"];
88 return s.string() ? s.string() : "";
90 std::string getRemoteAddr() const {
91 DDF s = m_input["client_addr"];
92 return s.string() ? s.string() : "";
95 const std::vector<XSECCryptoX509*>& getClientCertificates() const;
98 const char* getMethod() const {
99 return m_input["method"].string();
101 const char* getRequestURI() const {
102 return m_input["uri"].string();
104 const char* getRequestURL() const {
105 return m_input["url"].string();
107 const char* getQueryString() const {
108 return m_input["query"].string();
110 std::string getHeader(const char* name) const {
111 DDF s = m_input["headers"][name];
112 return s.string() ? s.string() : "";
116 class SHIBSP_DLLLOCAL RemotedResponse : public virtual HTTPResponse
120 RemotedResponse(DDF& output) : m_output(output) {}
121 virtual ~RemotedResponse() {}
124 long sendResponse(std::istream& inputStream, long status);
127 void setResponseHeader(const char* name, const char* value);
128 long sendRedirect(const char* url);
132 const char* RemotedRequest::getParameter(const char* name) const
135 m_parser=new CGIParser(*this);
137 pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
138 return (bounds.first==bounds.second) ? NULL : bounds.first->second;
141 std::vector<const char*>::size_type RemotedRequest::getParameters(const char* name, std::vector<const char*>& values) const
144 m_parser=new CGIParser(*this);
146 pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
147 while (bounds.first!=bounds.second) {
148 values.push_back(bounds.first->second);
151 return values.size();
154 const std::vector<XSECCryptoX509*>& RemotedRequest::getClientCertificates() const
156 if (m_certs.empty()) {
157 DDF certs = m_input["certificates"];
158 DDF cert = certs.first();
159 while (cert.string()) {
161 auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());
162 if (strstr(cert.string(), "BEGIN"))
163 x509->loadX509PEM(cert.string(), cert.strlen());
165 x509->loadX509Base64Bin(cert.string(), cert.strlen());
166 m_certs.push_back(x509.release());
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());
172 catch(XSECCryptoException& e) {
173 Category::getInstance(SHIBSP_LOGCAT".SPRequest").error("XML-Security exception loading client certificate: %s", e.getMsg());
181 long RemotedResponse::sendResponse(std::istream& in, long status)
187 msg.append(buf,in.gcount());
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);
196 void RemotedResponse::setResponseHeader(const char* name, const char* value)
198 if (!m_output.isstruct())
199 m_output.structure();
200 DDF hdrs = m_output["headers"];
202 hdrs = m_output.addmember("headers").structure();
203 hdrs.addmember(name).string(value);
206 long RemotedResponse::sendRedirect(const char* url)
208 if (!m_output.isstruct())
209 m_output.structure();
210 m_output.addmember("redirect").string(url);
211 return HTTPResponse::XMLTOOLING_HTTP_STATUS_MOVED;
216 void RemotedHandler::setAddress(const char* address)
218 if (!m_address.empty())
219 throw ConfigurationException("Cannot register a remoting address twice for the same Handler.");
221 SPConfig& conf = SPConfig::getConfig();
222 if (!conf.isEnabled(SPConfig::InProcess)) {
223 ListenerService* listener = conf.getServiceProvider()->getListenerService(false);
225 listener->regListener(m_address.c_str(),this);
227 Category::getInstance(SHIBSP_LOGCAT".Handler").info("no ListenerService available, handler remoting disabled");
231 RemotedHandler::~RemotedHandler()
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);
239 DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers, bool certs) const
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());
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());
262 hin.addmember(h->c_str()).string(hdr.c_str());
268 const vector<XSECCryptoX509*>& xvec = request.getClientCertificates();
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());
277 const vector<string>& xvec = request.getClientCertificates();
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());
291 pair<bool,long> RemotedHandler::unwrap(SPRequest& request, DDF& out) const
293 DDF h = out["headers"];
295 while (hdr.isstring()) {
296 #ifdef HAVE_STRCASECMP
297 if (!strcasecmp(hdr.name(), "Content-Type"))
299 if (!stricmp(hdr.name(), "Content-Type"))
301 request.setContentType(hdr.string());
302 request.setResponseHeader(hdr.name(), hdr.string());
307 return make_pair(true, request.sendRedirect(h.string()));
310 istringstream s(h["data"].string());
311 return make_pair(true, request.sendResponse(s, h["status"].integer()));
313 return make_pair(false,0);
316 HTTPRequest* RemotedHandler::getRequest(DDF& in) const
319 return new RemotedRequest(in);
321 throw ConfigurationException("Cannot process message using lite version of shibsp library.");
325 HTTPResponse* RemotedHandler::getResponse(DDF& out) const
328 return new RemotedResponse(out);
330 throw ConfigurationException("Cannot process message using lite version of shibsp library.");