b45551d5a436aae9af653bd4ff48e52b533c15f5
[shibboleth/sp.git] / shibsp / handler / impl / SessionInitiator.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  * SessionInitiator.cpp
23  * 
24  * Pluggable runtime functionality that handles initiating sessions.
25  */
26
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "SPRequest.h"
30 #include "handler/SessionInitiator.h"
31
32 using namespace shibsp;
33 using namespace xmltooling;
34 using namespace std;
35
36 #ifndef SHIBSP_LITE
37 # include <saml/saml2/metadata/Metadata.h>
38 using namespace opensaml::saml2md;
39 #endif
40
41 namespace shibsp {
42     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory ChainingSessionInitiatorFactory;
43     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory Shib1SessionInitiatorFactory;
44     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory SAML2SessionInitiatorFactory;
45     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory WAYFSessionInitiatorFactory;
46     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory SAMLDSSessionInitiatorFactory;
47     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory TransformSessionInitiatorFactory;
48     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory FormSessionInitiatorFactory;
49     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory CookieSessionInitiatorFactory;
50 };
51
52 map<string,string> SessionInitiator::m_remapper;
53
54 void SHIBSP_API shibsp::registerSessionInitiators()
55 {
56     SPConfig& conf=SPConfig::getConfig();
57     conf.SessionInitiatorManager.registerFactory(CHAINING_SESSION_INITIATOR, ChainingSessionInitiatorFactory);
58     conf.SessionInitiatorManager.registerFactory(SHIB1_SESSION_INITIATOR, Shib1SessionInitiatorFactory);
59     conf.SessionInitiatorManager.registerFactory(SAML2_SESSION_INITIATOR, SAML2SessionInitiatorFactory);
60     conf.SessionInitiatorManager.registerFactory(WAYF_SESSION_INITIATOR, WAYFSessionInitiatorFactory);
61     conf.SessionInitiatorManager.registerFactory(SAMLDS_SESSION_INITIATOR, SAMLDSSessionInitiatorFactory);
62     conf.SessionInitiatorManager.registerFactory(TRANSFORM_SESSION_INITIATOR, TransformSessionInitiatorFactory);
63     conf.SessionInitiatorManager.registerFactory(FORM_SESSION_INITIATOR, FormSessionInitiatorFactory);
64     conf.SessionInitiatorManager.registerFactory(COOKIE_SESSION_INITIATOR, CookieSessionInitiatorFactory);
65
66     SessionInitiator::m_remapper["defaultACSIndex"] = "acsIndex";
67 }
68
69 SessionInitiator::SessionInitiator()
70 {
71 }
72
73 SessionInitiator::~SessionInitiator()
74 {
75 }
76
77 #ifndef SHIBSP_LITE
78 const char* SessionInitiator::getType() const
79 {
80     return "SessionInitiator";
81 }
82
83 void SessionInitiator::generateMetadata(SPSSODescriptor& role, const char* handlerURL) const
84 {
85     if (getParent())
86         return;
87     const char* loc = getString("Location").second;
88     string hurl(handlerURL);
89     if (*loc != '/')
90         hurl += '/';
91     hurl += loc;
92     auto_ptr_XMLCh widen(hurl.c_str());
93
94     RequestInitiator* ep = RequestInitiatorBuilder::buildRequestInitiator();
95     ep->setLocation(widen.get());
96     ep->setBinding(samlconstants::SP_REQUEST_INIT_NS);
97     Extensions* ext = role.getExtensions();
98     if (!ext) {
99         ext = ExtensionsBuilder::buildExtensions();
100         role.setExtensions(ext);
101     }
102     ext->getUnknownXMLObjects().push_back(ep);
103 }
104 #endif
105
106 const set<string>& SessionInitiator::getSupportedOptions() const
107 {
108     return m_supportedOptions;
109 }
110
111 bool SessionInitiator::checkCompatibility(SPRequest& request, bool isHandler) const
112 {
113     bool isPassive = false;
114     if (isHandler) {
115         const char* flag = request.getParameter("isPassive");
116         if (flag) {
117             isPassive = (*flag=='1' || *flag=='t');
118         }
119         else {
120             pair<bool,bool> flagprop = getBool("isPassive");
121             isPassive = (flagprop.first && flagprop.second);
122         }
123     }
124     else {
125         // It doesn't really make sense to use isPassive with automated sessions, but...
126         pair<bool,bool> flagprop = request.getRequestSettings().first->getBool("isPassive");
127         if (!flagprop.first)
128             flagprop = getBool("isPassive");
129         isPassive = (flagprop.first && flagprop.second);
130     }
131
132     // Check for support of isPassive if it's used.
133     if (isPassive && getSupportedOptions().count("isPassive") == 0) {
134         if (getParent()) {
135             log(SPRequest::SPInfo, "handler does not support isPassive option");
136             return false;
137         }
138         throw ConfigurationException("Unsupported option (isPassive) supplied to SessionInitiator.");
139     }
140
141     return true;
142 }
143
144 pair<bool,long> SessionInitiator::run(SPRequest& request, bool isHandler) const
145 {
146     cleanRelayState(request.getApplication(), request, request);
147
148     const char* entityID = nullptr;
149     pair<bool,const char*> param = getString("entityIDParam");
150     if (isHandler) {
151         entityID = request.getParameter(param.first ? param.second : "entityID");
152         if (!param.first && (!entityID || !*entityID))
153             entityID=request.getParameter("providerId");
154     }
155     if (!entityID || !*entityID) {
156         param = request.getRequestSettings().first->getString("entityID");
157         if (param.first)
158             entityID = param.second;
159     }
160     if (!entityID || !*entityID)
161         entityID = getString("entityID").second;
162
163     string copy(entityID ? entityID : "");
164
165     try {
166         return run(request, copy, isHandler);
167     }
168     catch (exception& ex) {
169         // If it's a handler operation, and isPassive is used or returnOnError is set, we trap the error.
170         if (isHandler) {
171             bool returnOnError = false;
172             const char* flag = request.getParameter("isPassive");
173             if (flag && (*flag == 't' || *flag == '1')) {
174                 returnOnError = true;
175             }
176             else {
177                 pair<bool,bool> flagprop = getBool("isPassive");
178                 if (flagprop.first && flagprop.second) {
179                     returnOnError = true;
180                 }
181                 else {
182                     flag = request.getParameter("returnOnError");
183                     if (flag) {
184                         returnOnError = (*flag=='1' || *flag=='t');
185                     }
186                     else {
187                         flagprop = getBool("returnOnError");
188                         returnOnError = (flagprop.first && flagprop.second);
189                     }
190                 }
191             }
192
193             if (returnOnError) {
194                 // Log it and attempt to recover relay state so we can get back.
195                 log(SPRequest::SPError, ex.what());
196                 log(SPRequest::SPInfo, "trapping SessionInitiator error condition and returning to target location");
197                 flag = request.getParameter("target");
198                 string target(flag ? flag : "");
199                 recoverRelayState(request.getApplication(), request, request, target, false);
200                 return make_pair(true, request.sendRedirect(target.c_str()));
201             }
202         }
203         throw;
204     }
205 }
206
207 #ifndef SHIBSP_LITE
208
209 AuthnRequestEvent* SessionInitiator::newAuthnRequestEvent(const Application& application, const xmltooling::HTTPRequest* request) const
210 {
211     if (!SPConfig::getConfig().isEnabled(SPConfig::Logging))
212         return nullptr;
213     try {
214         auto_ptr<TransactionLog::Event> event(SPConfig::getConfig().EventManager.newPlugin(AUTHNREQUEST_EVENT, nullptr));
215         AuthnRequestEvent* ar_event = dynamic_cast<AuthnRequestEvent*>(event.get());
216         if (ar_event) {
217             ar_event->m_request = request;
218             ar_event->m_app = &application;
219             event.release();
220             return ar_event;
221         }
222         else {
223             Category::getInstance(SHIBSP_LOGCAT".SessionInitiator").warn("unable to audit event, log event object was of an incorrect type");
224         }
225     }
226     catch (exception& ex) {
227         Category::getInstance(SHIBSP_LOGCAT".SessionInitiator").warn("exception auditing event: %s", ex.what());
228     }
229     return nullptr;
230 }
231
232 #endif