Imported Upstream version 2.4+dfsg
[shibboleth/sp.git] / shibsp / handler / impl / SessionInitiator.cpp
1 /*
2  *  Copyright 2001-2010 Internet2
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * SessionInitiator.cpp
19  * 
20  * Pluggable runtime functionality that handles initiating sessions.
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "SPRequest.h"
26 #include "handler/SessionInitiator.h"
27
28 using namespace shibsp;
29 using namespace xmltooling;
30 using namespace std;
31
32 #ifndef SHIBSP_LITE
33 # include <saml/saml2/metadata/Metadata.h>
34 using namespace opensaml::saml2md;
35 #endif
36
37 namespace shibsp {
38     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory ChainingSessionInitiatorFactory;
39     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory Shib1SessionInitiatorFactory;
40     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory SAML2SessionInitiatorFactory;
41     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory WAYFSessionInitiatorFactory;
42     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory SAMLDSSessionInitiatorFactory;
43     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory TransformSessionInitiatorFactory;
44     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory FormSessionInitiatorFactory;
45     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory CookieSessionInitiatorFactory;
46 };
47
48 map<string,string> SessionInitiator::m_remapper;
49
50 void SHIBSP_API shibsp::registerSessionInitiators()
51 {
52     SPConfig& conf=SPConfig::getConfig();
53     conf.SessionInitiatorManager.registerFactory(CHAINING_SESSION_INITIATOR, ChainingSessionInitiatorFactory);
54     conf.SessionInitiatorManager.registerFactory(SHIB1_SESSION_INITIATOR, Shib1SessionInitiatorFactory);
55     conf.SessionInitiatorManager.registerFactory(SAML2_SESSION_INITIATOR, SAML2SessionInitiatorFactory);
56     conf.SessionInitiatorManager.registerFactory(WAYF_SESSION_INITIATOR, WAYFSessionInitiatorFactory);
57     conf.SessionInitiatorManager.registerFactory(SAMLDS_SESSION_INITIATOR, SAMLDSSessionInitiatorFactory);
58     conf.SessionInitiatorManager.registerFactory(TRANSFORM_SESSION_INITIATOR, TransformSessionInitiatorFactory);
59     conf.SessionInitiatorManager.registerFactory(FORM_SESSION_INITIATOR, FormSessionInitiatorFactory);
60     conf.SessionInitiatorManager.registerFactory(COOKIE_SESSION_INITIATOR, CookieSessionInitiatorFactory);
61
62     SessionInitiator::m_remapper["defaultACSIndex"] = "acsIndex";
63 }
64
65 SessionInitiator::SessionInitiator()
66 {
67 }
68
69 SessionInitiator::~SessionInitiator()
70 {
71 }
72
73 #ifndef SHIBSP_LITE
74 const char* SessionInitiator::getType() const
75 {
76     return "SessionInitiator";
77 }
78
79 void SessionInitiator::generateMetadata(SPSSODescriptor& role, const char* handlerURL) const
80 {
81     if (getParent())
82         return;
83     const char* loc = getString("Location").second;
84     string hurl(handlerURL);
85     if (*loc != '/')
86         hurl += '/';
87     hurl += loc;
88     auto_ptr_XMLCh widen(hurl.c_str());
89
90     RequestInitiator* ep = RequestInitiatorBuilder::buildRequestInitiator();
91     ep->setLocation(widen.get());
92     ep->setBinding(samlconstants::SP_REQUEST_INIT_NS);
93     Extensions* ext = role.getExtensions();
94     if (!ext) {
95         ext = ExtensionsBuilder::buildExtensions();
96         role.setExtensions(ext);
97     }
98     ext->getUnknownXMLObjects().push_back(ep);
99 }
100 #endif
101
102 const set<string>& SessionInitiator::getSupportedOptions() const
103 {
104     return m_supportedOptions;
105 }
106
107 bool SessionInitiator::checkCompatibility(SPRequest& request, bool isHandler) const
108 {
109     bool isPassive = false;
110     if (isHandler) {
111         const char* flag = request.getParameter("isPassive");
112         if (flag) {
113             isPassive = (*flag=='1' || *flag=='t');
114         }
115         else {
116             pair<bool,bool> flagprop = getBool("isPassive");
117             isPassive = (flagprop.first && flagprop.second);
118         }
119     }
120     else {
121         // It doesn't really make sense to use isPassive with automated sessions, but...
122         pair<bool,bool> flagprop = request.getRequestSettings().first->getBool("isPassive");
123         if (!flagprop.first)
124             flagprop = getBool("isPassive");
125         isPassive = (flagprop.first && flagprop.second);
126     }
127
128     // Check for support of isPassive if it's used.
129     if (isPassive && getSupportedOptions().count("isPassive") == 0) {
130         if (getParent()) {
131             log(SPRequest::SPInfo, "handler does not support isPassive option");
132             return false;
133         }
134         throw ConfigurationException("Unsupported option (isPassive) supplied to SessionInitiator.");
135     }
136
137     return true;
138 }
139
140 pair<bool,long> SessionInitiator::run(SPRequest& request, bool isHandler) const
141 {
142     const char* entityID = nullptr;
143     pair<bool,const char*> param = getString("entityIDParam");
144     if (isHandler) {
145         entityID = request.getParameter(param.first ? param.second : "entityID");
146         if (!param.first && (!entityID || !*entityID))
147             entityID=request.getParameter("providerId");
148     }
149     if (!entityID || !*entityID) {
150         param = request.getRequestSettings().first->getString("entityID");
151         if (param.first)
152             entityID = param.second;
153     }
154     if (!entityID || !*entityID)
155         entityID = getString("entityID").second;
156
157     string copy(entityID ? entityID : "");
158
159     try {
160         return run(request, copy, isHandler);
161     }
162     catch (exception& ex) {
163         // If it's a handler operation, and isPassive is used or returnOnError is set, we trap the error.
164         if (isHandler) {
165             bool returnOnError = false;
166             const char* flag = request.getParameter("isPassive");
167             if (flag && (*flag == 't' || *flag == '1')) {
168                 returnOnError = true;
169             }
170             else {
171                 pair<bool,bool> flagprop = getBool("isPassive");
172                 if (flagprop.first && flagprop.second) {
173                     returnOnError = true;
174                 }
175                 else {
176                     flag = request.getParameter("returnOnError");
177                     if (flag) {
178                         returnOnError = (*flag=='1' || *flag=='t');
179                     }
180                     else {
181                         flagprop = getBool("returnOnError");
182                         returnOnError = (flagprop.first && flagprop.second);
183                     }
184                 }
185             }
186
187             if (returnOnError) {
188                 // Log it and attempt to recover relay state so we can get back.
189                 log(SPRequest::SPError, ex.what());
190                 log(SPRequest::SPInfo, "trapping SessionInitiator error condition and returning to target location");
191                 flag = request.getParameter("target");
192                 string target(flag ? flag : "");
193                 recoverRelayState(request.getApplication(), request, request, target, false);
194                 return make_pair(true, request.sendRedirect(target.c_str()));
195             }
196         }
197         throw;
198     }
199 }