Boost changes
[shibboleth/cpp-sp.git] / shibsp / handler / impl / AssertionLookup.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * AssertionLookup.cpp
23  *
24  * Handler for looking up assertions in the SessionCache.
25  */
26
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "Application.h"
30 #include "ServiceProvider.h"
31 #include "SessionCacheEx.h"
32 #include "SPRequest.h"
33 #include "handler/AbstractHandler.h"
34 #include "handler/RemotedHandler.h"
35 #include "util/IPRange.h"
36 #include "util/SPConstants.h"
37
38 #include <boost/bind.hpp>
39 #include <boost/scoped_ptr.hpp>
40 #include <boost/algorithm/string.hpp>
41
42 #ifndef SHIBSP_LITE
43 # include <saml/exceptions.h>
44 # include <saml/Assertion.h>
45 # include <xmltooling/util/XMLHelper.h>
46 using namespace opensaml;
47 #endif
48
49 using namespace shibspconstants;
50 using namespace shibsp;
51 using namespace xmltooling;
52 using namespace boost;
53 using namespace std;
54
55 namespace shibsp {
56
57 #if defined (_MSC_VER)
58     #pragma warning( push )
59     #pragma warning( disable : 4250 )
60 #endif
61
62     class SHIBSP_DLLLOCAL Blocker : public DOMNodeFilter
63     {
64     public:
65 #ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
66         short
67 #else
68         FilterAction
69 #endif
70         acceptNode(const DOMNode* node) const {
71             return FILTER_REJECT;
72         }
73     };
74
75     static SHIBSP_DLLLOCAL Blocker g_Blocker;
76
77     class SHIBSP_API AssertionLookup : public AbstractHandler, public RemotedHandler
78     {
79     public:
80         AssertionLookup(const DOMElement* e, const char* appId);
81         virtual ~AssertionLookup() {}
82
83         pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
84         void receive(DDF& in, ostream& out);
85
86         const char* getType() const {
87             return "AssertionLookup";
88         }
89
90     private:
91         pair<bool,long> processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const;
92         void parseACL(const string& acl) {
93             try {
94                 m_acl.push_back(IPRange::parseCIDRBlock(acl.c_str()));
95             }
96             catch (std::exception& ex) {
97                 m_log.error("invalid CIDR block (%s): %s", acl.c_str(), ex.what());
98             }
99         }
100
101         vector<IPRange> m_acl;
102     };
103
104 #if defined (_MSC_VER)
105     #pragma warning( pop )
106 #endif
107
108     Handler* SHIBSP_DLLLOCAL AssertionLookupFactory(const pair<const DOMElement*,const char*>& p)
109     {
110         return new AssertionLookup(p.first, p.second);
111     }
112
113 };
114
115 AssertionLookup::AssertionLookup(const DOMElement* e, const char* appId)
116     : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".AssertionLookup"), &g_Blocker)
117 {
118     if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
119         pair<bool,const char*> acl = getString("exportACL");
120         if (acl.first) {
121             string aclbuf=acl.second;
122             vector<string> aclarray;
123             split(aclarray, aclbuf, is_space(), algorithm::token_compress_on);
124             for_each(aclarray.begin(), aclarray.end(), boost::bind(&AssertionLookup::parseACL, this, _1));
125             if (m_acl.empty()) {
126                 m_log.warn("invalid CIDR range(s) in acl property, allowing 127.0.0.1 as a fall back");
127                 m_acl.push_back(IPRange::parseCIDRBlock("127.0.0.1"));
128             }
129         }
130         else {
131             m_acl.push_back(IPRange::parseCIDRBlock("127.0.0.1"));
132         }
133     }
134
135     setAddress("run::AssertionLookup");
136 }
137
138 pair<bool,long> AssertionLookup::run(SPRequest& request, bool isHandler) const
139 {
140     SPConfig& conf = SPConfig::getConfig();
141     if (conf.isEnabled(SPConfig::InProcess) && !m_acl.empty()) {
142         static bool (IPRange::* contains)(const char*) const = &IPRange::contains;
143         if (find_if(m_acl.begin(), m_acl.end(), boost::bind(contains, _1, request.getRemoteAddr().c_str())) == m_acl.end()) {
144             m_log.error("request for assertion lookup blocked from invalid address (%s)", request.getRemoteAddr().c_str());
145             istringstream msg("Assertion Lookup Blocked");
146             return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN));
147         }
148     }
149
150     try {
151         if (conf.isEnabled(SPConfig::OutOfProcess)) {
152             // When out of process, we run natively and directly process the message.
153             return processMessage(request.getApplication(), request, request);
154         }
155         else {
156             // When not out of process, we remote all the message processing.
157             DDF out,in = wrap(request);
158             DDFJanitor jin(in), jout(out);
159
160             out=request.getServiceProvider().getListenerService()->send(in);
161             return unwrap(request, out);
162         }
163     }
164     catch (std::exception& ex) {
165         m_log.error("error while processing request: %s", ex.what());
166         istringstream msg("Assertion Lookup Failed");
167         return make_pair(true, request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR));
168     }
169 }
170
171 void AssertionLookup::receive(DDF& in, ostream& out)
172 {
173     // Find application.
174     const char* aid=in["application_id"].string();
175     const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : nullptr;
176     if (!app) {
177         // Something's horribly wrong.
178         m_log.error("couldn't find application (%s) for assertion lookup", aid ? aid : "(missing)");
179         throw ConfigurationException("Unable to locate application for assertion lookup, deleted?");
180     }
181
182     // Unpack the request.
183     scoped_ptr<HTTPRequest> req(getRequest(in));
184     //m_log.debug("found %d client certificates", req->getClientCertificates().size());
185
186     // Wrap a response shim.
187     DDF ret(nullptr);
188     DDFJanitor jout(ret);
189     scoped_ptr<HTTPResponse> resp(getResponse(ret));
190
191     // Since we're remoted, the result should either be a throw, a false/0 return,
192     // which we just return as an empty structure, or a response/redirect,
193     // which we capture in the facade and send back.
194     processMessage(*app, *req, *resp);
195     out << ret;
196 }
197
198 pair<bool,long> AssertionLookup::processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const
199 {
200 #ifndef SHIBSP_LITE
201     const char* key = httpRequest.getParameter("key");
202     const char* ID = httpRequest.getParameter("ID");
203     if (!key || !*key || !ID || !*ID) {
204         m_log.error("assertion lookup request failed, missing required parameters");
205         throw FatalProfileException("Missing key or ID parameters.");
206     }
207
208     m_log.debug("processing assertion lookup request (session: %s, assertion: %s)", key, ID);
209
210     SessionCacheEx* cache = dynamic_cast<SessionCacheEx*>(application.getServiceProvider().getSessionCache());
211     if (!cache) {
212         m_log.error("session cache does not support extended API");
213         throw FatalProfileException("Session cache does not support assertion lookup.");
214     }
215
216     // The cache will either silently pass a session or nullptr back, or throw an exception out.
217     Session* session = cache->find(application, key);
218     if (!session) {
219         m_log.error("valid session (%s) not found for assertion lookup", key);
220         throw FatalProfileException("Session key not found.");
221     }
222
223     Locker locker(session, false);
224
225     const Assertion* assertion = session->getAssertion(ID);
226     if (!assertion) {
227         m_log.error("assertion (%s) not found in session (%s)", ID, key);
228         throw FatalProfileException("Assertion not found.");
229     }
230
231     stringstream s;
232     s << *assertion;
233     httpResponse.setContentType("application/samlassertion+xml");
234     return make_pair(true, httpResponse.sendResponse(s));
235 #else
236     return make_pair(false,0L);
237 #endif
238 }