Add handlers and keys to status handler.
[shibboleth/cpp-sp.git] / shibsp / handler / impl / StatusHandler.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  * StatusHandler.cpp
19  * 
20  * Handler for exposing information about the internals of the SP.
21  */
22
23 #include "internal.h"
24 #include "Application.h"
25 #include "exceptions.h"
26 #include "ServiceProvider.h"
27 #include "handler/AbstractHandler.h"
28 #include "handler/RemotedHandler.h"
29
30 using namespace shibsp;
31 #ifndef SHIBSP_LITE
32 # include "SessionCache.h"
33 # include <saml/version.h>
34 using namespace opensaml::saml2md;
35 using namespace opensaml;
36 using namespace xmlsignature;
37 #endif
38 using namespace xmltooling;
39 using namespace std;
40
41 namespace shibsp {
42
43 #if defined (_MSC_VER)
44     #pragma warning( push )
45     #pragma warning( disable : 4250 )
46 #endif
47
48     class SHIBSP_DLLLOCAL Blocker : public DOMNodeFilter
49     {
50     public:
51         short acceptNode(const DOMNode* node) const {
52             return FILTER_REJECT;
53         }
54     };
55
56     static SHIBSP_DLLLOCAL Blocker g_Blocker;
57
58     class SHIBSP_API StatusHandler : public AbstractHandler, public RemotedHandler
59     {
60     public:
61         StatusHandler(const DOMElement* e, const char* appId);
62         virtual ~StatusHandler() {}
63
64         pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
65         void receive(DDF& in, ostream& out);
66
67     private:
68         pair<bool,long> processMessage(const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse) const;
69
70         set<string> m_acl;
71     };
72
73 #if defined (_MSC_VER)
74     #pragma warning( pop )
75 #endif
76
77     Handler* SHIBSP_DLLLOCAL StatusHandlerFactory(const pair<const DOMElement*,const char*>& p)
78     {
79         return new StatusHandler(p.first, p.second);
80     }
81
82 };
83
84 StatusHandler::StatusHandler(const DOMElement* e, const char* appId)
85     : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".StatusHandler"), &g_Blocker)
86 {
87     string address(appId);
88     address += getString("Location").second;
89     setAddress(address.c_str());
90     if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
91         pair<bool,const char*> acl = getString("acl");
92         if (acl.first) {
93             string aclbuf=acl.second;
94             int j = 0;
95             for (unsigned int i=0;  i < aclbuf.length();  i++) {
96                 if (aclbuf.at(i)==' ') {
97                     m_acl.insert(aclbuf.substr(j, i-j));
98                     j = i+1;
99                 }
100             }
101             m_acl.insert(aclbuf.substr(j, aclbuf.length()-j));
102         }
103     }
104 }
105
106 pair<bool,long> StatusHandler::run(SPRequest& request, bool isHandler) const
107 {
108     SPConfig& conf = SPConfig::getConfig();
109     if (conf.isEnabled(SPConfig::InProcess)) {
110         if (!m_acl.empty() && m_acl.count(request.getRemoteAddr()) == 0) {
111             m_log.error("status handler request blocked from invalid address (%s)", request.getRemoteAddr().c_str());
112             istringstream msg("Status Handler Blocked");
113             return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN));
114         }
115     }
116     
117     try {
118         if (conf.isEnabled(SPConfig::OutOfProcess)) {
119             // When out of process, we run natively and directly process the message.
120             return processMessage(request.getApplication(), request, request);
121         }
122         else {
123             // When not out of process, we remote all the message processing.
124             DDF out,in = wrap(request);
125             DDFJanitor jin(in), jout(out);            
126             out=request.getServiceProvider().getListenerService()->send(in);
127             return unwrap(request, out);
128         }
129     }
130     catch (XMLToolingException& ex) {
131         m_log.error("error while processing request: %s", ex.what());
132         request.setContentType("text/xml");
133         stringstream msg;
134         msg << "<StatusHandler>";
135             msg << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
136 #ifndef SHIBSP_LITE
137                 << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
138                 << "' OpenSAML-C='" << OPENSAML_FULLVERSIONDOT
139 #endif
140                 << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
141             msg << "<Status><Exception type='" << ex.getClassName() << "'>" << ex.what() << "</Exception></Status>";
142         msg << "</StatusHandler>";
143         return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR));
144     }
145     catch (exception& ex) {
146         m_log.error("error while processing request: %s", ex.what());
147         request.setContentType("text/xml");
148         stringstream msg;
149         msg << "<StatusHandler>";
150             msg << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
151 #ifndef SHIBSP_LITE
152                 << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
153                 << "' OpenSAML-C='" << OPENSAML_FULLVERSIONDOT
154 #endif
155                 << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
156             msg << "<Status><Exception type='std::exception'>" << ex.what() << "</Exception></Status>";
157         msg << "</StatusHandler>";
158         return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR));
159     }
160 }
161
162 void StatusHandler::receive(DDF& in, ostream& out)
163 {
164     // Find application.
165     const char* aid=in["application_id"].string();
166     const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL;
167     if (!app) {
168         // Something's horribly wrong.
169         m_log.error("couldn't find application (%s) for status request", aid ? aid : "(missing)");
170         throw ConfigurationException("Unable to locate application for status request, deleted?");
171     }
172     
173     // Wrap a response shim.
174     DDF ret(NULL);
175     DDFJanitor jout(ret);
176     auto_ptr<HTTPRequest> req(getRequest(in));
177     auto_ptr<HTTPResponse> resp(getResponse(ret));
178         
179     // Since we're remoted, the result should either be a throw, a false/0 return,
180     // which we just return as an empty structure, or a response/redirect,
181     // which we capture in the facade and send back.
182     processMessage(*app, *req.get(), *resp.get());
183     out << ret;
184 }
185
186 pair<bool,long> StatusHandler::processMessage(
187     const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse
188     ) const
189 {
190 #ifndef SHIBSP_LITE
191     m_log.debug("processing status request");
192
193     stringstream s;
194     s << "<StatusHandler>";
195     const char* status = "<OK/>";
196
197     s << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
198         << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
199         << "' OpenSAML-C='" << OPENSAML_FULLVERSIONDOT
200         << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
201
202     if (false) {
203     }
204     else {
205         // General configuration and status report.
206         try {
207             SessionCache* sc = application.getServiceProvider().getSessionCache(false);
208             if (sc) {
209                 sc->test();
210                 s << "<SessionCache><OK/></SessionCache>";
211             }
212             else {
213                 s << "<SessionCache><None/></SessionCache>";
214             }
215         }
216         catch (XMLToolingException& ex) {
217             s << "<SessionCache><Exception type='" << ex.getClassName() << "'>" << ex.what() << "</Exception></SessionCache>";
218             status = "<Partial/>";
219         }
220         catch (exception& ex) {
221             s << "<SessionCache><Exception type='std::exception'>" << ex.what() << "</Exception></SessionCache>";
222             status = "<Partial/>";
223         }
224
225         s << "<Application id='" << application.getId() << "' entityID='" << application.getString("entityID").second << "'/>";
226
227         s << "<Handlers>";
228         vector<const Handler*> handlers;
229         application.getHandlers(handlers);
230         for (vector<const Handler*>::const_iterator h = handlers.begin(); h != handlers.end(); ++h) {
231             s << "<Handler type='" << (*h)->getType() << "' Location='" << (*h)->getString("Location").second << "'";
232             if ((*h)->getString("Binding").first)
233                 s << " Binding='" << (*h)->getString("Binding").second << "'";
234             s << "/>";
235         }
236         s << "</Handlers>";
237
238         const PropertySet* relyingParty=NULL;
239         const char* entityID=httpRequest.getParameter("entityID");
240         if (entityID) {
241             Locker mlock(application.getMetadataProvider());
242             relyingParty = application.getRelyingParty(application.getMetadataProvider()->getEntityDescriptor(entityID));
243         }
244         if (!relyingParty)
245             relyingParty = application.getRelyingParty(NULL);
246         CredentialResolver* credResolver=application.getCredentialResolver();
247         if (credResolver) {
248             Locker credLocker(credResolver);
249             CredentialCriteria cc;
250             cc.setUsage(Credential::SIGNING_CREDENTIAL);
251             pair<bool,const char*> keyName = relyingParty->getString("keyName");
252             if (keyName.first)
253                 cc.getKeyNames().insert(keyName.second);
254             vector<const Credential*> creds;
255             credResolver->resolve(creds,&cc);
256             for (vector<const Credential*>::const_iterator c = creds.begin(); c != creds.end(); ++c) {
257                 KeyInfo* kinfo = (*c)->getKeyInfo();
258                 if (kinfo) {
259                     auto_ptr<KeyDescriptor> kd(KeyDescriptorBuilder::buildKeyDescriptor());
260                     kd->setUse(KeyDescriptor::KEYTYPE_SIGNING);
261                     kd->setKeyInfo(kinfo);
262                     s << *(kd.get());
263                 }
264             }
265
266             cc.setUsage(Credential::ENCRYPTION_CREDENTIAL);
267             creds.clear();
268             cc.getKeyNames().clear();
269             credResolver->resolve(creds,&cc);
270             for (vector<const Credential*>::const_iterator c = creds.begin(); c != creds.end(); ++c) {
271                 KeyInfo* kinfo = (*c)->getKeyInfo();
272                 if (kinfo) {
273                     auto_ptr<KeyDescriptor> kd(KeyDescriptorBuilder::buildKeyDescriptor());
274                     kd->setUse(KeyDescriptor::KEYTYPE_ENCRYPTION);
275                     kd->setKeyInfo(kinfo);
276                     s << *(kd.get());
277                 }
278             }
279         }
280
281     }
282
283     s << "<Status>" << status << "</Status></StatusHandler>";
284
285     httpResponse.setContentType("text/xml");
286     return make_pair(true, httpResponse.sendResponse(s));
287 #else
288     return make_pair(false,0);
289 #endif
290 }