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