X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=shibsp%2Fhandler%2Fimpl%2FSAMLDSSessionInitiator.cpp;h=1d77698375129839efe0bd9ff560aa6fd410ab57;hb=c51bfd77603cf0ddb0b5e374c35586a8435895d6;hp=42614f153b477edfd042383c9ccd269813d023f6;hpb=aebe94d53162cb5e0a50da51c7fa434e866072b6;p=shibboleth%2Fcpp-sp.git diff --git a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp index 42614f1..1d77698 100644 --- a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp +++ b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp @@ -1,40 +1,51 @@ -/* - * Copyright 2001-2007 Internet2 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at +/** + * Licensed to the University Corporation for Advanced Internet + * Development, Inc. (UCAID) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * UCAID licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the + * License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. */ /** * SAMLDSSessionInitiator.cpp - * + * * SAML Discovery Service support. */ #include "internal.h" #include "Application.h" #include "exceptions.h" -#include "SPRequest.h" #include "handler/AbstractHandler.h" #include "handler/SessionInitiator.h" +#include #include #include using namespace shibsp; using namespace opensaml; using namespace xmltooling; +using namespace boost; using namespace std; +#ifndef SHIBSP_LITE +# include +# include +using namespace opensaml::saml2md; +#endif + namespace shibsp { #if defined (_MSC_VER) @@ -45,23 +56,58 @@ namespace shibsp { class SHIBSP_DLLLOCAL SAMLDSSessionInitiator : public SessionInitiator, public AbstractHandler { public: - SAMLDSSessionInitiator(const DOMElement* e, const char* appId) - : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator")), m_url(NULL), m_returnParam(NULL) { - pair url = getString("URL"); - if (!url.first) - throw ConfigurationException("SAMLDS SessionInitiator requires a URL property."); - m_url = url.second; - url = getString("entityIDParam"); - if (url.first) - m_returnParam = url.second; - } + SAMLDSSessionInitiator(const DOMElement* e, const char* appId); virtual ~SAMLDSSessionInitiator() {} - - pair run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const; + + pair run(SPRequest& request, string& entityID, bool isHandler=true) const; + +#ifndef SHIBSP_LITE + void generateMetadata(SPSSODescriptor& role, const char* handlerURL) const { + // Initial guess at index to use. + pair ix = getUnsignedInt("index"); + if (!ix.first) + ix.second = 1; + + // Find maximum index in use and go one higher. + if (role.getExtensions()) { + const vector& exts = const_cast(role.getExtensions())->getUnknownXMLObjects(); + for (vector::const_reverse_iterator i = exts.rbegin(); i != exts.rend(); ++i) { + const DiscoveryResponse* sub = dynamic_cast(*i); + if (sub) { + pair val = sub->getIndex(); + if (val.first) { + if (ix.second <= val.second) + ix.second = val.second + 1; + break; + } + } + } + } + + const char* loc = getString("Location").second; + string hurl(handlerURL); + if (*loc != '/') + hurl += '/'; + hurl += loc; + auto_ptr_XMLCh widen(hurl.c_str()); + + DiscoveryResponse* ep = DiscoveryResponseBuilder::buildDiscoveryResponse(); + ep->setLocation(widen.get()); + ep->setBinding(samlconstants::IDP_DISCOVERY_PROTOCOL_NS); + ep->setIndex(ix.second); + Extensions* ext = role.getExtensions(); + if (!ext) { + ext = ExtensionsBuilder::buildExtensions(); + role.setExtensions(ext); + } + ext->getUnknownXMLObjects().push_back(ep); + } +#endif private: const char* m_url; const char* m_returnParam; + vector m_preservedOptions; }; #if defined (_MSC_VER) @@ -75,57 +121,106 @@ namespace shibsp { }; -pair SAMLDSSessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const +SAMLDSSessionInitiator::SAMLDSSessionInitiator(const DOMElement* e, const char* appId) + : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT ".SessionInitiator.SAMLDS")), m_url(nullptr), m_returnParam(nullptr) +{ + pair url = getString("URL"); + if (!url.first) + throw ConfigurationException("SAMLDS SessionInitiator requires a URL property."); + m_url = url.second; + url = getString("entityIDParam"); + if (url.first) + m_returnParam = url.second; + + pair options = getString("preservedOptions"); + if (options.first) { + string opt = options.second; + trim(opt); + split(m_preservedOptions, opt, is_space(), algorithm::token_compress_on); + } + else { + m_preservedOptions.push_back("isPassive"); + m_preservedOptions.push_back("forceAuthn"); + m_preservedOptions.push_back("authnContextClassRef"); + m_preservedOptions.push_back("authnContextComparison"); + m_preservedOptions.push_back("NameIDFormat"); + m_preservedOptions.push_back("SPNameQualifier"); + m_preservedOptions.push_back("acsIndex"); + } + + m_supportedOptions.insert("isPassive"); +} + +pair SAMLDSSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const { // The IdP CANNOT be specified for us to run. Otherwise, we'd be redirecting to a DS // anytime the IdP's metadata was wrong. - if (entityID && *entityID) - return make_pair(false,0); + if (!entityID.empty() || !checkCompatibility(request, isHandler)) + return make_pair(false,0L); string target; - const char* option; - bool isPassive=false; - const Application& app=request.getApplication(); + pair prop; + bool isPassive = false; + const Application& app = request.getApplication(); + pair discoveryURL = pair(true, m_url); if (isHandler) { - option = request.getParameter("SAMLDS"); - if (option && !strcmp(option,"1")) - throw saml2md::MetadataException("No identity provider was selected by user."); - - option = request.getParameter("target"); - if (option) - target = option; - recoverRelayState(request.getApplication(), request, target, false); - - option = request.getParameter("isPassive"); - if (option) - isPassive = !strcmp(option,"true"); + prop.second = request.getParameter("SAMLDS"); + if (prop.second && !strcmp(prop.second,"1")) { + saml2md::MetadataException ex("No identity provider was selected by user."); + ex.addProperty("statusCode", "urn:oasis:names:tc:SAML:2.0:status:Requester"); + ex.addProperty("statusCode2", "urn:oasis:names:tc:SAML:2.0:status:NoAvailableIDP"); + ex.raise(); + } + + prop = getString("target", request); + if (prop.first) + target = prop.second; + + recoverRelayState(app, request, request, target, false); + + pair passopt = getBool("isPassive", request); + isPassive = passopt.first && passopt.second; + + prop.second = request.getParameter("discoveryURL"); + if (prop.second && *prop.second) + discoveryURL.second = prop.second; } else { - // We're running as a "virtual handler" from within the filter. - // The target resource is the current one and everything else is - // defaulted or set by content policy. - target=request.getRequestURL(); - pair passopt = getBool("isPassive"); + // Check for a hardwired target value in the map or handler. + prop = getString("target", request, HANDLER_PROPERTY_MAP|HANDLER_PROPERTY_FIXED); + if (prop.first) + target = prop.second; + else + target = request.getRequestURL(); + + pair passopt = getBool("isPassive", request, HANDLER_PROPERTY_MAP|HANDLER_PROPERTY_FIXED); isPassive = passopt.first && passopt.second; + discoveryURL = request.getRequestSettings().first->getString("discoveryURL"); } - m_log.debug("sending request to SAMLDS (%s)", m_url); + if (!discoveryURL.first) + discoveryURL.second = m_url; + m_log.debug("sending request to SAMLDS (%s)", discoveryURL.second); // Compute the return URL. We start with a self-referential link. - string returnURL=request.getHandlerURL(target.c_str()); - pair thisloc = getString("Location"); - if (thisloc.first) returnURL += thisloc.second; + string returnURL = request.getHandlerURL(target.c_str()); + prop = getString("Location"); + if (prop.first) + returnURL += prop.second; returnURL += "?SAMLDS=1"; // signals us not to loop if we get no answer back if (isHandler) { // We may already have RelayState set if we looped back here, - // but just in case target is a resource, we reset it back. - option = request.getParameter("target"); - if (option) - target = option; + // but we've turned it back into a resource by this point, so if there's + // a target on the URL, reset to that value. + prop.second = request.getParameter("target"); + if (prop.second && *prop.second) + target = prop.second; } - preserveRelayState(request.getApplication(), request, target); + preserveRelayState(app, request, target); + if (!isHandler) + preservePostData(app, request, request, target.c_str()); const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder(); if (isHandler) { @@ -144,15 +239,15 @@ pair SAMLDSSessionInitiator::run(SPRequest& request, const char* enti } else { // There's something in the query before target appears, so we have to find it. - thisloc.second = strstr(query,"&target="); - if (thisloc.second) { + prop.second = strstr(query, "&target="); + if (prop.second) { // We found it, so first append everything up to it. returnURL += '&'; - returnURL.append(query, thisloc.second - query); - query = thisloc.second + 8; // move up just past the equals sign. - thisloc.second = strchr(query, '&'); - if (thisloc.second) - returnURL += thisloc.second; + returnURL.append(query, prop.second - query); + query = prop.second + 8; // move up just past the equals sign. + prop.second = strchr(query, '&'); + if (prop.second) + returnURL += prop.second; } else { // No target in the existing query, so just append it as is. @@ -165,17 +260,27 @@ pair SAMLDSSessionInitiator::run(SPRequest& request, const char* enti if (!target.empty()) returnURL = returnURL + "&target=" + urlenc->encode(target.c_str()); } - else if (!target.empty()) { - // For a virtual handler, we just append target to the return link. - returnURL = returnURL + "&target=" + urlenc->encode(target.c_str());; + else { + // For a virtual handler, we append target to the return link. + if (!target.empty()) + returnURL = returnURL + "&target=" + urlenc->encode(target.c_str()); + // Preserve designated request settings on the URL. + for (vector::const_iterator opt = m_preservedOptions.begin(); opt != m_preservedOptions.end(); ++ opt) { + prop = request.getRequestSettings().first->getString(opt->c_str()); + if (prop.first) + returnURL = returnURL + '&' + (*opt) + '=' + urlenc->encode(prop.second); + } } - string req=string(m_url) + (strchr(m_url,'?') ? '&' : '?') + "entityID=" + urlenc->encode(app.getString("entityID").second) + + string req=string(discoveryURL.second) + (strchr(discoveryURL.second,'?') ? '&' : '?') + "entityID=" + urlenc->encode(app.getString("entityID").second) + "&return=" + urlenc->encode(returnURL.c_str()); if (m_returnParam) req = req + "&returnIDParam=" + m_returnParam; if (isPassive) req += "&isPassive=true"; + prop = getString("discoveryPolicy"); + if (prop.first) + req += "&policy=" + urlenc->encode(prop.second); return make_pair(true, request.sendRedirect(req.c_str())); }