2 * Copyright 2001-2009 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * WAYFSessionInitiator.cpp
20 * Shibboleth WAYF support.
24 #include "Application.h"
25 #include "exceptions.h"
26 #include "SPRequest.h"
27 #include "handler/AbstractHandler.h"
28 #include "handler/SessionInitiator.h"
31 #include <xmltooling/XMLToolingConfig.h>
32 #include <xmltooling/util/URLEncoder.h>
34 using namespace shibsp;
35 using namespace opensaml;
36 using namespace xmltooling;
41 #if defined (_MSC_VER)
42 #pragma warning( push )
43 #pragma warning( disable : 4250 )
46 class SHIBSP_DLLLOCAL WAYFSessionInitiator : public SessionInitiator, public AbstractHandler
49 WAYFSessionInitiator(const DOMElement* e, const char* appId)
50 : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator.WAYF")), m_url(NULL) {
51 pair<bool,const char*> url = getString("URL");
53 throw ConfigurationException("WAYF SessionInitiator requires a URL property.");
56 virtual ~WAYFSessionInitiator() {}
58 pair<bool,long> run(SPRequest& request, string& entityID, bool isHandler=true) const;
64 #if defined (_MSC_VER)
65 #pragma warning( pop )
68 SessionInitiator* SHIBSP_DLLLOCAL WAYFSessionInitiatorFactory(const pair<const DOMElement*,const char*>& p)
70 return new WAYFSessionInitiator(p.first, p.second);
75 pair<bool,long> WAYFSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const
77 // The IdP CANNOT be specified for us to run. Otherwise, we'd be redirecting to a WAYF
78 // anytime the IdP's metadata was wrong.
79 if (!entityID.empty())
80 return make_pair(false,0L);
85 const Handler* ACS=NULL;
86 const Application& app=request.getApplication();
89 option=request.getParameter("acsIndex");
91 ACS=app.getAssertionConsumerServiceByIndex(atoi(option));
93 request.log(SPRequest::SPWarn, "invalid acsIndex specified in request, using default ACS location");
96 option = request.getParameter("target");
99 recoverRelayState(request.getApplication(), request, request, target, false);
102 // We're running as a "virtual handler" from within the filter.
103 // The target resource is the current one and everything else is defaulted.
104 target=request.getRequestURL();
107 // Since we're not passing by index, we need to fully compute the return URL.
109 pair<bool,unsigned int> index = getUnsignedInt("defaultACSIndex");
111 ACS = app.getAssertionConsumerServiceByIndex(index.second);
113 request.log(SPRequest::SPWarn, "invalid defaultACSIndex, using default ACS location");
116 ACS = app.getDefaultAssertionConsumerService();
119 m_log.debug("sending request to WAYF (%s)", m_url);
121 // Compute the ACS URL. We add the ACS location to the base handlerURL.
122 string ACSloc=request.getHandlerURL(target.c_str());
123 pair<bool,const char*> loc=ACS ? ACS->getString("Location") : pair<bool,const char*>(false,NULL);
124 if (loc.first) ACSloc+=loc.second;
127 // We may already have RelayState set if we looped back here,
128 // but just in case target is a resource, we reset it back.
129 option = request.getParameter("target");
133 preserveRelayState(request.getApplication(), request, target);
135 preservePostData(request.getApplication(), request, request, target.c_str());
137 // WAYF requires a target value.
142 sprintf(timebuf,"%lu",time(NULL));
143 const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder();
144 string req=string(m_url) + (strchr(m_url,'?') ? '&' : '?') + "shire=" + urlenc->encode(ACSloc.c_str()) +
145 "&time=" + timebuf + "&target=" + urlenc->encode(target.c_str()) +
146 "&providerId=" + urlenc->encode(app.getString("entityID").second);
148 return make_pair(true, request.sendRedirect(req.c_str()));