70630496b43004ff8a392e1206bce24e7cba25ee
[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/RemotedHandler.h"
34 #include "handler/SecuredHandler.h"
35
36 #include <boost/scoped_ptr.hpp>
37
38 #ifndef SHIBSP_LITE
39 # include <saml/exceptions.h>
40 # include <saml/Assertion.h>
41 # include <xmltooling/util/XMLHelper.h>
42 using namespace opensaml;
43 #endif
44
45 using namespace shibspconstants;
46 using namespace shibsp;
47 using namespace xmltooling;
48 using namespace boost;
49 using namespace std;
50
51 namespace shibsp {
52
53 #if defined (_MSC_VER)
54     #pragma warning( push )
55     #pragma warning( disable : 4250 )
56 #endif
57
58     class SHIBSP_API AssertionLookup : public SecuredHandler, public RemotedHandler
59     {
60     public:
61         AssertionLookup(const DOMElement* e, const char* appId);
62         virtual ~AssertionLookup() {}
63
64         pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
65         void receive(DDF& in, ostream& out);
66
67         const char* getType() const {
68             return "AssertionLookup";
69         }
70
71     private:
72         pair<bool,long> processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const;
73     };
74
75 #if defined (_MSC_VER)
76     #pragma warning( pop )
77 #endif
78
79     Handler* SHIBSP_DLLLOCAL AssertionLookupFactory(const pair<const DOMElement*,const char*>& p)
80     {
81         return new AssertionLookup(p.first, p.second);
82     }
83
84 };
85
86 AssertionLookup::AssertionLookup(const DOMElement* e, const char* appId)
87     : SecuredHandler(e, Category::getInstance(SHIBSP_LOGCAT".AssertionLookup"), "exportACL", "127.0.0.1 ::1")
88 {
89     setAddress("run::AssertionLookup");
90 }
91
92 pair<bool,long> AssertionLookup::run(SPRequest& request, bool isHandler) const
93 {
94     // Check ACL in base class.
95     pair<bool,long> ret = SecuredHandler::run(request, isHandler);
96     if (ret.first)
97         return ret;
98
99     try {
100         if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
101             // When out of process, we run natively and directly process the message.
102             return processMessage(request.getApplication(), request, request);
103         }
104         else {
105             // When not out of process, we remote all the message processing.
106             DDF out,in = wrap(request);
107             DDFJanitor jin(in), jout(out);
108
109             out=request.getServiceProvider().getListenerService()->send(in);
110             return unwrap(request, out);
111         }
112     }
113     catch (std::exception& ex) {
114         m_log.error("error while processing request: %s", ex.what());
115         istringstream msg("Assertion Lookup Failed");
116         return make_pair(true, request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR));
117     }
118 }
119
120 void AssertionLookup::receive(DDF& in, ostream& out)
121 {
122     // Find application.
123     const char* aid = in["application_id"].string();
124     const Application* app = aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : nullptr;
125     if (!app) {
126         // Something's horribly wrong.
127         m_log.error("couldn't find application (%s) for assertion lookup", aid ? aid : "(missing)");
128         throw ConfigurationException("Unable to locate application for assertion lookup, deleted?");
129     }
130
131     // Unpack the request.
132     scoped_ptr<HTTPRequest> req(getRequest(in));
133     //m_log.debug("found %d client certificates", req->getClientCertificates().size());
134
135     // Wrap a response shim.
136     DDF ret(nullptr);
137     DDFJanitor jout(ret);
138     scoped_ptr<HTTPResponse> resp(getResponse(ret));
139
140     // Since we're remoted, the result should either be a throw, a false/0 return,
141     // which we just return as an empty structure, or a response/redirect,
142     // which we capture in the facade and send back.
143     processMessage(*app, *req, *resp);
144     out << ret;
145 }
146
147 pair<bool,long> AssertionLookup::processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const
148 {
149 #ifndef SHIBSP_LITE
150     const char* key = httpRequest.getParameter("key");
151     const char* ID = httpRequest.getParameter("ID");
152     if (!key || !*key || !ID || !*ID) {
153         m_log.error("assertion lookup request failed, missing required parameters");
154         throw FatalProfileException("Missing key or ID parameters.");
155     }
156
157     m_log.debug("processing assertion lookup request (session: %s, assertion: %s)", key, ID);
158
159     SessionCacheEx* cache = dynamic_cast<SessionCacheEx*>(application.getServiceProvider().getSessionCache());
160     if (!cache) {
161         m_log.error("session cache does not support extended API");
162         throw FatalProfileException("Session cache does not support assertion lookup.");
163     }
164
165     // The cache will either silently pass a session or nullptr back, or throw an exception out.
166     Session* session = cache->find(application, key);
167     if (!session) {
168         m_log.error("valid session (%s) not found for assertion lookup", key);
169         throw FatalProfileException("Session key not found.");
170     }
171
172     Locker locker(session, false);
173
174     const Assertion* assertion = session->getAssertion(ID);
175     if (!assertion) {
176         m_log.error("assertion (%s) not found in session (%s)", ID, key);
177         throw FatalProfileException("Assertion not found.");
178     }
179
180     stringstream s;
181     s << *assertion;
182     httpResponse.setContentType("application/samlassertion+xml");
183     return make_pair(true, httpResponse.sendResponse(s));
184 #else
185     return make_pair(false, 0L);
186 #endif
187 }