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  * LocalLogoutInitiator.cpp
23  *
24  * Logs out a session locally.
25  */
26 
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "Application.h"
30 #include "ServiceProvider.h"
31 #include "SessionCache.h"
32 #include "SPRequest.h"
33 #include "handler/AbstractHandler.h"
34 #include "handler/LogoutInitiator.h"
35 
36 #ifndef SHIBSP_LITE
37 using namespace boost;
38 #endif
39 
40 using namespace shibsp;
41 using namespace xmltooling;
42 using namespace std;
43 
44 namespace shibsp {
45 
46 #if defined (_MSC_VER)
47     #pragma warning( push )
48     #pragma warning( disable : 4250 )
49 #endif
50 
51     class SHIBSP_DLLLOCAL LocalLogoutInitiator : public AbstractHandler, public LogoutInitiator
52     {
53     public:
54         LocalLogoutInitiator(const DOMElement* e, const char* appId);
~LocalLogoutInitiator()55         virtual ~LocalLogoutInitiator() {}
56 
57         void setParent(const PropertySet* parent);
58         void receive(DDF& in, ostream& out);
59         pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
60 
61     private:
62         pair<bool,long> doRequest(
63             const Application& application, const HTTPRequest& request, HTTPResponse& httpResponse, Session* session
64             ) const;
65 
66         string m_appId;
67     };
68 
69 #if defined (_MSC_VER)
70     #pragma warning( pop )
71 #endif
72 
LocalLogoutInitiatorFactory(const pair<const DOMElement *,const char * > & p,bool)73     Handler* SHIBSP_DLLLOCAL LocalLogoutInitiatorFactory(const pair<const DOMElement*,const char*>& p, bool)
74     {
75         return new LocalLogoutInitiator(p.first, p.second);
76     }
77 };
78 
LocalLogoutInitiator(const DOMElement * e,const char * appId)79 LocalLogoutInitiator::LocalLogoutInitiator(const DOMElement* e, const char* appId)
80     : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT ".LogoutInitiator.Local")), m_appId(appId)
81 {
82     pair<bool,const char*> loc = getString("Location");
83     if (loc.first) {
84         string address = string(appId) + loc.second + "::run::LocalLI";
85         setAddress(address.c_str());
86     }
87 }
88 
setParent(const PropertySet * parent)89 void LocalLogoutInitiator::setParent(const PropertySet* parent)
90 {
91     DOMPropertySet::setParent(parent);
92     pair<bool,const char*> loc = getString("Location");
93     if (loc.first) {
94         string address = m_appId + loc.second + "::run::LocalLI";
95         setAddress(address.c_str());
96     }
97     else {
98         m_log.warn("no Location property in Local LogoutInitiator (or parent), can't register as remoted handler");
99     }
100 }
101 
run(SPRequest & request,bool isHandler) const102 pair<bool,long> LocalLogoutInitiator::run(SPRequest& request, bool isHandler) const
103 {
104     // Defer to base class first.
105     pair<bool,long> ret = LogoutHandler::run(request, isHandler);
106     if (ret.first)
107         return ret;
108 
109     if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
110         // When out of process, we run natively.
111         Session* session = nullptr;
112         try {
113             session = request.getSession(false, true, false);  // don't cache it and ignore all checks
114         }
115         catch (const std::exception& ex) {
116             m_log.error("error accessing current session: %s", ex.what());
117         }
118         return doRequest(request.getApplication(), request, request, session);
119     }
120     else {
121         // When not out of process, we remote the request.
122         vector<string> headers(1,"Cookie");
123         headers.push_back("User-Agent");
124         DDF out,in = wrap(request,&headers);
125         DDFJanitor jin(in), jout(out);
126         out = send(request, in);
127         return unwrap(request, out);
128     }
129 }
130 
receive(DDF & in,ostream & out)131 void LocalLogoutInitiator::receive(DDF& in, ostream& out)
132 {
133 #ifndef SHIBSP_LITE
134     // Defer to base class for back channel notifications
135     if (in["notify"].integer() == 1)
136         return LogoutHandler::receive(in, out);
137 
138     // Find application.
139     const char* aid=in["application_id"].string();
140     const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : nullptr;
141     if (!app) {
142         // Something's horribly wrong.
143         m_log.error("couldn't find application (%s) for logout", aid ? aid : "(missing)");
144         throw ConfigurationException("Unable to locate application for logout, deleted?");
145     }
146 
147     // Unpack the request.
148     scoped_ptr<HTTPRequest> req(getRequest(*app, in));
149 
150     // Set up a response shim.
151     DDF ret(nullptr);
152     DDFJanitor jout(ret);
153     scoped_ptr<HTTPResponse> resp(getResponse(*app, ret));
154 
155     Session* session = nullptr;
156     try {
157          session = app->getServiceProvider().getSessionCache()->find(*app, *req, nullptr, nullptr);
158     }
159     catch (const std::exception& ex) {
160         m_log.error("error accessing current session: %s", ex.what());
161     }
162 
163     // This is the "last chance" handler so even without a session, we "complete" the logout.
164     doRequest(*app, *req, *resp, session);
165 
166     out << ret;
167 #else
168     throw ConfigurationException("Cannot perform logout using lite version of shibsp library.");
169 #endif
170 }
171 
doRequest(const Application & application,const HTTPRequest & httpRequest,HTTPResponse & httpResponse,Session * session) const172 pair<bool,long> LocalLogoutInitiator::doRequest(
173     const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse, Session* session
174     ) const
175 {
176     if (session) {
177         // Guard the session in case of exception.
178         Locker locker(session, false);
179 
180         // Do back channel notification.
181         bool result;
182         vector<string> sessions(1, session->getID());
183         result = notifyBackChannel(application, httpRequest.getRequestURL(), sessions, true);
184 #ifndef SHIBSP_LITE
185         scoped_ptr<LogoutEvent> logout_event(newLogoutEvent(application, &httpRequest, session));
186         if (logout_event) {
187             logout_event->m_logoutType = result ? LogoutEvent::LOGOUT_EVENT_LOCAL : LogoutEvent::LOGOUT_EVENT_PARTIAL;
188             application.getServiceProvider().getTransactionLog()->write(*logout_event);
189         }
190 #endif
191         time_t revocationExp = session->getExpiration();
192         locker.assign();    // unlock the session
193         application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse, revocationExp);
194         if (!result)
195             return sendLogoutPage(application, httpRequest, httpResponse, "partial");
196     }
197 
198     // Route back to return location specified, or use the local template.
199     const char* dest = httpRequest.getParameter("return");
200     if (dest) {
201         // Relative URLs get promoted, absolutes get validated.
202         if (*dest == '/') {
203             string d(dest);
204             httpRequest.absolutize(d);
205             return make_pair(true, httpResponse.sendRedirect(d.c_str()));
206         }
207         application.limitRedirect(httpRequest, dest);
208         return make_pair(true, httpResponse.sendRedirect(dest));
209     }
210     return sendLogoutPage(application, httpRequest, httpResponse, "local");
211 }
212