https://issues.shibboleth.net/jira/browse/SSPCPP-349
[shibboleth/cpp-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     const char* entityID = nullptr;
147     pair<bool,const char*> param = getString("entityIDParam");
148     if (isHandler) {
149         entityID = request.getParameter(param.first ? param.second : "entityID");
150         if (!param.first && (!entityID || !*entityID))
151             entityID=request.getParameter("providerId");
152     }
153     if (!entityID || !*entityID) {
154         param = request.getRequestSettings().first->getString("entityID");
155         if (param.first)
156             entityID = param.second;
157     }
158     if (!entityID || !*entityID)
159         entityID = getString("entityID").second;
160
161     string copy(entityID ? entityID : "");
162
163     try {
164         return run(request, copy, isHandler);
165     }
166     catch (exception& ex) {
167         // If it's a handler operation, and isPassive is used or returnOnError is set, we trap the error.
168         if (isHandler) {
169             bool returnOnError = false;
170             const char* flag = request.getParameter("isPassive");
171             if (flag && (*flag == 't' || *flag == '1')) {
172                 returnOnError = true;
173             }
174             else {
175                 pair<bool,bool> flagprop = getBool("isPassive");
176                 if (flagprop.first && flagprop.second) {
177                     returnOnError = true;
178                 }
179                 else {
180                     flag = request.getParameter("returnOnError");
181                     if (flag) {
182                         returnOnError = (*flag=='1' || *flag=='t');
183                     }
184                     else {
185                         flagprop = getBool("returnOnError");
186                         returnOnError = (flagprop.first && flagprop.second);
187                     }
188                 }
189             }
190
191             if (returnOnError) {
192                 // Log it and attempt to recover relay state so we can get back.
193                 log(SPRequest::SPError, ex.what());
194                 log(SPRequest::SPInfo, "trapping SessionInitiator error condition and returning to target location");
195                 flag = request.getParameter("target");
196                 string target(flag ? flag : "");
197                 recoverRelayState(request.getApplication(), request, request, target, false);
198                 return make_pair(true, request.sendRedirect(target.c_str()));
199             }
200         }
201         throw;
202     }
203 }
204
205 #ifndef SHIBSP_LITE
206
207 AuthnRequestEvent* SessionInitiator::newAuthnRequestEvent(const Application& application, const xmltooling::HTTPRequest* request) const
208 {
209     if (!SPConfig::getConfig().isEnabled(SPConfig::Logging))
210         return nullptr;
211     try {
212         auto_ptr<TransactionLog::Event> event(SPConfig::getConfig().EventManager.newPlugin(AUTHNREQUEST_EVENT, nullptr));
213         AuthnRequestEvent* ar_event = dynamic_cast<AuthnRequestEvent*>(event.get());
214         if (ar_event) {
215             ar_event->m_request = request;
216             ar_event->m_app = &application;
217             event.release();
218             return ar_event;
219         }
220         else {
221             Category::getInstance(SHIBSP_LOGCAT".SessionInitiator").warn("unable to audit event, log event object was of an incorrect type");
222         }
223     }
224     catch (exception& ex) {
225         Category::getInstance(SHIBSP_LOGCAT".SessionInitiator").warn("exception auditing event: %s", ex.what());
226     }
227     return nullptr;
228 }
229
230 #endif