From 4927f4cf1ef86bd5ad3f200ac650a08b1f5da40a Mon Sep 17 00:00:00 2001 From: cantor Date: Fri, 8 Feb 2008 19:55:16 +0000 Subject: [PATCH] Change API to permit session initiators to modify the entityID mid-chain. Add Transform initiator to do pattern-based conversions. git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2729 cb58f699-b61c-0410-a6fe-9272a202ed29 --- adfs/adfs.cpp | 12 +- shibsp/handler/SessionInitiator.h | 5 +- shibsp/handler/impl/ChainingSessionInitiator.cpp | 4 +- shibsp/handler/impl/SAML2SessionInitiator.cpp | 16 +- shibsp/handler/impl/SAMLDSSessionInitiator.cpp | 6 +- shibsp/handler/impl/SessionInitiator.cpp | 6 +- shibsp/handler/impl/Shib1SessionInitiator.cpp | 12 +- shibsp/handler/impl/TransformSessionInitiator.cpp | 213 ++++++++++++++++++++++ shibsp/handler/impl/WAYFSessionInitiator.cpp | 6 +- shibsp/shibsp-lite.vcproj | 4 + shibsp/shibsp.vcproj | 4 + 11 files changed, 257 insertions(+), 31 deletions(-) create mode 100644 shibsp/handler/impl/TransformSessionInitiator.cpp diff --git a/adfs/adfs.cpp b/adfs/adfs.cpp index b2be93b..ea2a356 100644 --- a/adfs/adfs.cpp +++ b/adfs/adfs.cpp @@ -128,7 +128,7 @@ namespace { } void receive(DDF& in, ostream& out); - pair run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const; + pair run(SPRequest& request, string& entityID, bool isHandler=true) const; private: pair doRequest( @@ -307,10 +307,10 @@ extern "C" void ADFS_EXPORTS xmltooling_extension_term() */ } -pair ADFSSessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const +pair ADFSSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const { // We have to know the IdP to function. - if (!entityID || !*entityID) + if (entityID.empty()) return make_pair(false,0L); string target; @@ -372,16 +372,16 @@ pair ADFSSessionInitiator::run(SPRequest& request, const char* entity target = option; } - m_log.debug("attempting to initiate session using ADFS with provider (%s)", entityID); + m_log.debug("attempting to initiate session using ADFS with provider (%s)", entityID.c_str()); if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) - return doRequest(app, request, entityID, ACSloc.c_str(), target); + return doRequest(app, request, entityID.c_str(), ACSloc.c_str(), target); // Remote the call. DDF out,in = DDF(m_address.c_str()).structure(); DDFJanitor jin(in), jout(out); in.addmember("application_id").string(app.getId()); - in.addmember("entity_id").string(entityID); + in.addmember("entity_id").string(entityID.c_str()); in.addmember("acsLocation").string(ACSloc.c_str()); if (!target.empty()) in.addmember("RelayState").string(target.c_str()); diff --git a/shibsp/handler/SessionInitiator.h b/shibsp/handler/SessionInitiator.h index bd939dc..a201c0a 100644 --- a/shibsp/handler/SessionInitiator.h +++ b/shibsp/handler/SessionInitiator.h @@ -52,7 +52,7 @@ namespace shibsp { * @param isHandler true iff executing in the context of a direct handler invocation * @return a pair containing a "request completed" indicator and a server-specific response code */ - virtual std::pair run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const=0; + virtual std::pair run(SPRequest& request, std::string& entityID, bool isHandler=true) const=0; std::pair run(SPRequest& request, bool isHandler=true) const; @@ -80,6 +80,9 @@ namespace shibsp { /** SessionInitiator that supports Shibboleth V1 WAYF redirects when no IdP is supplied. */ #define WAYF_SESSION_INITIATOR "WAYF" + + /** SessionInitiator that attempts a sequence of transforms of an input until an entityID is found. */ + #define TRANSFORM_SESSION_INITIATOR "Transform" }; #endif /* __shibsp_initiator_h__ */ diff --git a/shibsp/handler/impl/ChainingSessionInitiator.cpp b/shibsp/handler/impl/ChainingSessionInitiator.cpp index a5f1235..dcd29d6 100644 --- a/shibsp/handler/impl/ChainingSessionInitiator.cpp +++ b/shibsp/handler/impl/ChainingSessionInitiator.cpp @@ -48,7 +48,7 @@ namespace shibsp { for_each(m_handlers.begin(), m_handlers.end(), xmltooling::cleanup()); } - 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(opensaml::saml2md::SPSSODescriptor& role, const char* handlerURL) const { @@ -108,7 +108,7 @@ ChainingSessionInitiator::ChainingSessionInitiator(const DOMElement* e, const ch } } -pair ChainingSessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const +pair ChainingSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const { pair ret; for (vector::const_iterator i = m_handlers.begin(); i!=m_handlers.end(); ++i) { diff --git a/shibsp/handler/impl/SAML2SessionInitiator.cpp b/shibsp/handler/impl/SAML2SessionInitiator.cpp index 6556f0a..644772c 100644 --- a/shibsp/handler/impl/SAML2SessionInitiator.cpp +++ b/shibsp/handler/impl/SAML2SessionInitiator.cpp @@ -71,7 +71,7 @@ namespace shibsp { void setParent(const PropertySet* parent); void receive(DDF& in, ostream& out); - pair run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const; + pair run(SPRequest& request, string& entityID, bool isHandler=true) const; private: pair doRequest( @@ -204,7 +204,7 @@ void SAML2SessionInitiator::setParent(const PropertySet* parent) } } -pair SAML2SessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const +pair SAML2SessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const { // First check for ECP support, since that doesn't require an IdP to be known. bool ECP = false; @@ -215,7 +215,7 @@ pair SAML2SessionInitiator::run(SPRequest& request, const char* entit } // We have to know the IdP to function unless this is ECP. - if (!ECP && (!entityID || !*entityID)) + if (!ECP && (entityID.empty())) return make_pair(false,0L); string target; @@ -308,7 +308,7 @@ pair SAML2SessionInitiator::run(SPRequest& request, const char* entit if (ECP) m_log.debug("attempting to initiate session using SAML 2.0 Enhanced Client Profile"); else - m_log.debug("attempting to initiate session using SAML 2.0 with provider (%s)", entityID); + m_log.debug("attempting to initiate session using SAML 2.0 with provider (%s)", entityID.c_str()); if (!ACS) { if (ECP) { @@ -345,7 +345,7 @@ pair SAML2SessionInitiator::run(SPRequest& request, const char* entit target = option; } return doRequest( - app, request, entityID, + app, request, entityID.c_str(), ACS ? ACS->getXMLString("index").second : NULL, NULL, NULL, isPassive, forceAuthn, acClass.first ? acClass.second : NULL, @@ -370,7 +370,7 @@ pair SAML2SessionInitiator::run(SPRequest& request, const char* entit } return doRequest( - app, request, entityID, + app, request, entityID.c_str(), NULL, ACSloc.c_str(), ACS ? ACS->getXMLString("Binding").second : NULL, isPassive, forceAuthn, acClass.first ? acClass.second : NULL, @@ -383,8 +383,8 @@ pair SAML2SessionInitiator::run(SPRequest& request, const char* entit DDF out,in = DDF(m_address.c_str()).structure(); DDFJanitor jin(in), jout(out); in.addmember("application_id").string(app.getId()); - if (entityID) - in.addmember("entity_id").string(entityID); + if (!entityID.empty()) + in.addmember("entity_id").string(entityID.c_str()); if (isPassive) in.addmember("isPassive").integer(1); else if (forceAuthn) diff --git a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp index 6084d76..90d189c 100644 --- a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp +++ b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp @@ -66,7 +66,7 @@ namespace shibsp { } 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 { @@ -111,11 +111,11 @@ namespace shibsp { }; -pair SAMLDSSessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const +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) + if (!entityID.empty()) return make_pair(false,0L); string target; diff --git a/shibsp/handler/impl/SessionInitiator.cpp b/shibsp/handler/impl/SessionInitiator.cpp index 48f71b1..3bdefc1 100644 --- a/shibsp/handler/impl/SessionInitiator.cpp +++ b/shibsp/handler/impl/SessionInitiator.cpp @@ -34,6 +34,7 @@ namespace shibsp { SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair >::Factory SAML2SessionInitiatorFactory; SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair >::Factory WAYFSessionInitiatorFactory; SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair >::Factory SAMLDSSessionInitiatorFactory; + SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair >::Factory TransformSessionInitiatorFactory; }; void SHIBSP_API shibsp::registerSessionInitiators() @@ -43,7 +44,7 @@ void SHIBSP_API shibsp::registerSessionInitiators() conf.SessionInitiatorManager.registerFactory(SHIB1_SESSION_INITIATOR, Shib1SessionInitiatorFactory); conf.SessionInitiatorManager.registerFactory(SAML2_SESSION_INITIATOR, SAML2SessionInitiatorFactory); conf.SessionInitiatorManager.registerFactory(WAYF_SESSION_INITIATOR, WAYFSessionInitiatorFactory); - conf.SessionInitiatorManager.registerFactory(SAMLDS_SESSION_INITIATOR, SAMLDSSessionInitiatorFactory); + conf.SessionInitiatorManager.registerFactory(TRANSFORM_SESSION_INITIATOR, TransformSessionInitiatorFactory); } pair SessionInitiator::run(SPRequest& request, bool isHandler) const @@ -65,5 +66,6 @@ pair SessionInitiator::run(SPRequest& request, bool isHandler) const if (!entityID || !*entityID) entityID=getString("entityID").second; - return run(request, entityID, isHandler); + string copy(entityID ? entityID : ""); + return run(request, copy, isHandler); } diff --git a/shibsp/handler/impl/Shib1SessionInitiator.cpp b/shibsp/handler/impl/Shib1SessionInitiator.cpp index 8d5e065..38fb2d3 100644 --- a/shibsp/handler/impl/Shib1SessionInitiator.cpp +++ b/shibsp/handler/impl/Shib1SessionInitiator.cpp @@ -66,7 +66,7 @@ namespace shibsp { void setParent(const PropertySet* parent); void receive(DDF& in, ostream& out); - pair run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const; + pair run(SPRequest& request, string& entityID, bool isHandler=true) const; private: pair doRequest( @@ -103,10 +103,10 @@ void Shib1SessionInitiator::setParent(const PropertySet* parent) } } -pair Shib1SessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const +pair Shib1SessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const { // We have to know the IdP to function. - if (!entityID || !*entityID) + if (entityID.empty()) return make_pair(false,0L); string target; @@ -162,16 +162,16 @@ pair Shib1SessionInitiator::run(SPRequest& request, const char* entit target = option; } - m_log.debug("attempting to initiate session using Shibboleth with provider (%s)", entityID); + m_log.debug("attempting to initiate session using Shibboleth with provider (%s)", entityID.c_str()); if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) - return doRequest(app, request, entityID, ACSloc.c_str(), target); + return doRequest(app, request, entityID.c_str(), ACSloc.c_str(), target); // Remote the call. DDF out,in = DDF(m_address.c_str()).structure(); DDFJanitor jin(in), jout(out); in.addmember("application_id").string(app.getId()); - in.addmember("entity_id").string(entityID); + in.addmember("entity_id").string(entityID.c_str()); in.addmember("acsLocation").string(ACSloc.c_str()); if (!target.empty()) in.addmember("RelayState").string(target.c_str()); diff --git a/shibsp/handler/impl/TransformSessionInitiator.cpp b/shibsp/handler/impl/TransformSessionInitiator.cpp new file mode 100644 index 0000000..6b377ac --- /dev/null +++ b/shibsp/handler/impl/TransformSessionInitiator.cpp @@ -0,0 +1,213 @@ +/* + * 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 + * + * 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. + */ + +/** + * TransformSessionInitiator.cpp + * + * Support for mapping input into an entityID using a transform. + */ + +#include "internal.h" +#include "Application.h" +#include "exceptions.h" +#include "ServiceProvider.h" +#include "SPRequest.h" +#include "handler/AbstractHandler.h" +#include "handler/RemotedHandler.h" +#include "handler/SessionInitiator.h" +#include "util/SPConstants.h" + +#ifndef SHIBSP_LITE +# include +#endif +#include +#include +#include + +using namespace shibsp; +using namespace opensaml::saml2md; +using namespace opensaml; +using namespace xmltooling; +using namespace std; + +namespace shibsp { + +#if defined (_MSC_VER) + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class SHIBSP_DLLLOCAL TransformSINodeFilter : public DOMNodeFilter + { + public: + short acceptNode(const DOMNode* node) const { + if (XMLString::equals(node->getLocalName(), xmlsignature::Transform::LOCAL_NAME)) + return FILTER_REJECT; + return FILTER_ACCEPT; + } + }; + + static SHIBSP_DLLLOCAL TransformSINodeFilter g_TSINFilter; + + class SHIBSP_DLLLOCAL TransformSessionInitiator : public SessionInitiator, public AbstractHandler, public RemotedHandler + { + public: + TransformSessionInitiator(const DOMElement* e, const char* appId) + : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator.Transform"), &g_TSINFilter), m_appId(appId) { + // If Location isn't set, defer address registration until the setParent call. + pair loc = getString("Location"); + if (loc.first) { + string address = m_appId + loc.second + "::run::TransformSI"; + setAddress(address.c_str()); + } + +#ifndef SHIBSP_LITE + if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { + e = XMLHelper::getFirstChildElement(e, xmlsignature::Transform::LOCAL_NAME); + while (e) { + if (e->hasChildNodes()) { + auto_ptr_char temp(e->getFirstChild()->getNodeValue()); + m_transforms.push_back(temp.get()); + } + e = XMLHelper::getNextSiblingElement(e, xmlsignature::Transform::LOCAL_NAME); + } + } +#endif + } + + virtual ~TransformSessionInitiator() {} + + void setParent(const PropertySet* parent); + void receive(DDF& in, ostream& out); + pair run(SPRequest& request, string& entityID, bool isHandler=true) const; + + private: + void doRequest(const Application& application, string& entityID) const; + string m_appId; +#ifndef SHIBSP_LITE + vector m_transforms; +#endif + }; + +#if defined (_MSC_VER) + #pragma warning( pop ) +#endif + + SessionInitiator* SHIBSP_DLLLOCAL TransformSessionInitiatorFactory(const pair& p) + { + return new TransformSessionInitiator(p.first, p.second); + } + +}; + +void TransformSessionInitiator::setParent(const PropertySet* parent) +{ + DOMPropertySet::setParent(parent); + pair loc = getString("Location"); + if (loc.first) { + string address = m_appId + loc.second + "::run::TransformSI"; + setAddress(address.c_str()); + } + else { + m_log.warn("no Location property in Transform SessionInitiator (or parent), can't register as remoted handler"); + } +} + +pair TransformSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const +{ + // We have to have a candidate name to function. + if (entityID.empty()) + return make_pair(false,0L); + + string target; + const Application& app=request.getApplication(); + + m_log.debug("attempting to transform input (%s) into a valid entityID", entityID.c_str()); + + if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) + doRequest(app, entityID); + else { + // Remote the call. + DDF out,in = DDF(m_address.c_str()).structure(); + DDFJanitor jin(in), jout(out); + in.addmember("application_id").string(app.getId()); + in.addmember("entity_id").string(entityID.c_str()); + + // Remote the processing. + out = request.getServiceProvider().getListenerService()->send(in); + if (out.isstring()) + entityID = out.string(); + } + + return make_pair(false,0L); +} + +void TransformSessionInitiator::receive(DDF& in, ostream& out) +{ + // Find application. + const char* aid=in["application_id"].string(); + const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL; + if (!app) { + // Something's horribly wrong. + m_log.error("couldn't find application (%s) to generate AuthnRequest", aid ? aid : "(missing)"); + throw ConfigurationException("Unable to locate application for new session, deleted?"); + } + + const char* entityID = in["entity_id"].string(); + if (!entityID) + throw ConfigurationException("No entityID parameter supplied to remoted SessionInitiator."); + + string copy(entityID); + doRequest(*app, copy); + DDF ret = DDF(NULL).string(copy.c_str()); + DDFJanitor jout(ret); + out << ret; +} + +void TransformSessionInitiator::doRequest(const Application& application, string& entityID) const +{ +#ifndef SHIBSP_LITE + MetadataProvider* m=application.getMetadataProvider(); + Locker locker(m); + + // First check the original value, it might be valid already. + MetadataProvider::Criteria mc(entityID.c_str(), &IDPSSODescriptor::ELEMENT_QNAME); + pair entity = m->getEntityDescriptor(mc); + if (entity.first) + return; + + // Guess not, try each transform. + string transform; + for (vector::const_iterator t = m_transforms.begin(); t != m_transforms.end(); ++t) { + transform = *t; + string::size_type pos = transform.find("$entityID"); + if (pos == string::npos) + continue; + transform.replace(pos, 9, entityID); + m_log.debug("attempting lookup with entityID (%s)", transform.c_str()); + + mc.entityID_ascii = transform.c_str(); + entity = m->getEntityDescriptor(mc); + if (entity.first) { + m_log.info("transformed entityID from (%s) to (%s)", entityID.c_str(), transform.c_str()); + entityID = transform; + return; + } + } + + m_log.warn("unable to find a valid entityID based on the supplied input"); +#endif +} diff --git a/shibsp/handler/impl/WAYFSessionInitiator.cpp b/shibsp/handler/impl/WAYFSessionInitiator.cpp index 60c6352..19510ec 100644 --- a/shibsp/handler/impl/WAYFSessionInitiator.cpp +++ b/shibsp/handler/impl/WAYFSessionInitiator.cpp @@ -55,7 +55,7 @@ namespace shibsp { } virtual ~WAYFSessionInitiator() {} - pair run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const; + pair run(SPRequest& request, string& entityID, bool isHandler=true) const; private: const char* m_url; @@ -72,11 +72,11 @@ namespace shibsp { }; -pair WAYFSessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const +pair WAYFSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const { // The IdP CANNOT be specified for us to run. Otherwise, we'd be redirecting to a WAYF // anytime the IdP's metadata was wrong. - if (entityID && *entityID) + if (!entityID.empty()) return make_pair(false,0L); string target; diff --git a/shibsp/shibsp-lite.vcproj b/shibsp/shibsp-lite.vcproj index 498c712..cdfd090 100644 --- a/shibsp/shibsp-lite.vcproj +++ b/shibsp/shibsp-lite.vcproj @@ -368,6 +368,10 @@ > + + diff --git a/shibsp/shibsp.vcproj b/shibsp/shibsp.vcproj index 38ae4c3..d638725 100644 --- a/shibsp/shibsp.vcproj +++ b/shibsp/shibsp.vcproj @@ -539,6 +539,10 @@ > + + -- 2.1.4