From: cantor Date: Sun, 3 Oct 2010 21:17:52 +0000 (+0000) Subject: Draft implementation. X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fresolver.git;a=commitdiff_plain;h=HEAD Draft implementation. git-svn-id: https://svn.middleware.georgetown.edu/shib-extension/cpp-sp-resolver/trunk@275 3ebe4818-3638-0410-822d-ece5924dabe2 --- diff --git a/shibresolver/resolver-lite.vcxproj b/shibresolver/resolver-lite.vcxproj index 67951ca..ca2b2f0 100644 --- a/shibresolver/resolver-lite.vcxproj +++ b/shibresolver/resolver-lite.vcxproj @@ -119,6 +119,7 @@ true Windows $(TargetDir)$(ProjectName)1D.lib + ..\..\cpp-sp\$(Configuration);..\..\cpp-opensaml2\$(Configuration);..\..\cpp-xmltooling\$(Configuration);%(AdditionalLibraryDirectories) @@ -139,6 +140,7 @@ true true $(TargetDir)$(ProjectName)1.lib + ..\..\cpp-sp\$(Configuration);..\..\cpp-opensaml2\$(Configuration);..\..\cpp-xmltooling\$(Configuration);%(AdditionalLibraryDirectories) diff --git a/shibresolver/resolver.cpp b/shibresolver/resolver.cpp index 2164f82..ce4a722 100644 --- a/shibresolver/resolver.cpp +++ b/shibresolver/resolver.cpp @@ -22,14 +22,33 @@ #include "internal.h" +#include +#include +#include #include #include #include +#ifndef SHIBSP_LITE +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif +#include +#include +#include +#include using namespace shibresolver; using namespace shibsp; #ifndef SHIBSP_LITE using namespace opensaml; +using namespace opensaml::saml2md; #endif using namespace xmltooling; using namespace std; @@ -40,7 +59,26 @@ namespace shibresolver { RemotedResolver() {} ~RemotedResolver() {} + struct Transaction { + ~Transaction() { + for_each(tokens.begin(), tokens.end(), xmltooling::cleanup()); + for_each(inputAttrs.begin(), inputAttrs.end(), xmltooling::cleanup()); + for_each(resolvedAttrs.begin(), resolvedAttrs.end(), xmltooling::cleanup()); + } + + vector tokens; + vector inputAttrs; + vector resolvedAttrs; + }; + void receive(DDF& in, ostream& out); + void resolve( + const Application& app, + const char* issuer, + const vector& tokens, + const vector& inputAttrs, + vector & resolvedAttrs + ) const; }; static RemotedResolver g_Remoted; @@ -51,26 +89,20 @@ ShibbolethResolver* ShibbolethResolver::create() return new ShibbolethResolver(); } -ShibbolethResolver::ShibbolethResolver() +ShibbolethResolver::ShibbolethResolver() : m_request(NULL), m_sp(NULL) { - m_mapper = NULL; - m_sp = NULL; } ShibbolethResolver::~ShibbolethResolver() { for_each(m_resolvedAttributes.begin(), m_resolvedAttributes.end(), xmltooling::cleanup()); - if (m_mapper) - m_mapper->unlock(); if (m_sp) m_sp->unlock(); } -void ShibbolethResolver::setServiceURI(const char* uri) +void ShibbolethResolver::setRequest(const SPRequest* request) { - m_serviceURI.erase(); - if (uri) - m_serviceURI = uri; + m_request = request; } void ShibbolethResolver::setApplicationID(const char* appID) @@ -87,13 +119,7 @@ void ShibbolethResolver::setIssuer(const char* issuer) m_issuer = issuer; } -void ShibbolethResolver::addToken( -#ifdef SHIBSP_LITE - const XMLObject* token -#else - const saml2::Assertion* token -#endif - ) +void ShibbolethResolver::addToken(const XMLObject* token) { if (token) m_tokens.push_back(token); @@ -112,15 +138,251 @@ vector& ShibbolethResolver::getResolvedAttributes() RequestMapper::Settings ShibbolethResolver::getSettings() const { - return m_settings; + if (!m_request) + throw ConfigurationException("Request settings not available without supplying SPRequest instance."); + return m_request->getRequestSettings(); } void ShibbolethResolver::resolve() { + Category& log = Category::getInstance(SHIBRESOLVER_LOGCAT); + SPConfig& conf = SPConfig::getConfig(); + if (!m_request) { + m_sp = conf.getServiceProvider(); + m_sp->lock(); + if (m_appID.empty()) + m_appID = "default"; + } + + const Application* app = m_request ? &(m_request->getApplication()) : m_sp->getApplication(m_appID.c_str()); + if (!app) + throw ConfigurationException("Unable to locate application for resolution."); + + if (conf.isEnabled(SPConfig::OutOfProcess)) { + g_Remoted.resolve( + *app, + m_issuer.c_str(), + m_tokens, + m_inputAttributes, + m_resolvedAttributes + ); + } + else { + // When not out of process, we remote all the message processing. + DDF out,in = DDF("org.project-moonshot.shibresolver"); + DDFJanitor jin(in), jout(out); + in.addmember("application_id").string(app->getId()); + if (!m_issuer.empty()) + in.addmember("issuer").string(m_issuer.c_str()); + if (!m_tokens.empty()) { + DDF& tokens = in.addmember("tokens").list(); + for (vector::const_iterator t = m_tokens.begin(); t != m_tokens.end(); ++t) { + ostringstream os; + os << *(*t); + tokens.add(DDF(NULL).string(os.str().c_str())); + } + } + if (!m_inputAttributes.empty()) { + DDF attr; + DDF& attrs = in.addmember("attributes").list(); + for (vector::const_iterator a = m_inputAttributes.begin(); a != m_inputAttributes.end(); ++a) { + attr = (*a)->marshall(); + attrs.add(attr); + } + } + + out = (m_request ? m_request->getServiceProvider() : (*m_sp)).getListenerService()->send(in); + + Attribute* attribute; + DDF attr = out.first(); + while (!attr.isnull()) { + try { + attribute = Attribute::unmarshall(attr); + m_resolvedAttributes.push_back(attribute); + if (log.isDebugEnabled()) + log.debug("unmarshalled attribute (ID: %s) with %d value%s", + attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : ""); + } + catch (AttributeException& ex) { + const char* id = attr.first().name(); + log.error("error unmarshalling attribute (ID: %s): %s", id ? id : "none", ex.what()); + } + attr = out.next(); + } + } } void RemotedResolver::receive(DDF& in, ostream& out) { + Category& log = Category::getInstance(SHIBRESOLVER_LOGCAT); + + // 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. + log.error("couldn't find application (%s) for resolution", aid ? aid : "(missing)"); + throw ConfigurationException("Unable to locate application for resolution, deleted?"); + } + + DDF ret(NULL); + DDFJanitor jout(ret); + + Transaction t; + + DDF tlist = in["tokens"]; + DDF token = tlist.first(); + while (token.isstring()) { + // Parse and bind the document into an XMLObject. + istringstream instr(token.string()); + DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(instr); + XercesJanitor janitor(doc); + XMLObject* xmlObject = XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true); + t.tokens.push_back(xmlObject); + janitor.release(); + token = tlist.next(); + } + + DDF alist = in["attributes"]; + Attribute* attribute; + DDF attr = alist.first(); + while (!attr.isnull()) { + attribute = Attribute::unmarshall(attr); + t.inputAttrs.push_back(attribute); + if (log.isDebugEnabled()) + log.debug("unmarshalled attribute (ID: %s) with %d value%s", + attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : ""); + attr = alist.next(); + } + + resolve(*app, in["issuer"].string(), t.tokens, t.inputAttrs, t.resolvedAttrs); + + if (!t.resolvedAttrs.empty()) { + ret.list(); + for (vector::const_iterator a = t.resolvedAttrs.begin(); a != t.resolvedAttrs.end(); ++a) { + attr = (*a)->marshall(); + ret.add(attr); + } + } + + out << ret; +} + +void RemotedResolver::resolve( + const Application& app, + const char* issuer, + const vector& tokens, + const vector& inputAttrs, + vector & resolvedAttrs + ) const +{ +#ifndef SHIBSP_LITE + Category& log = Category::getInstance(SHIBRESOLVER_LOGCAT); + pair entity = pair(nullptr,nullptr); + MetadataProvider* m = app.getMetadataProvider(); + Locker locker(m); + if (issuer) { + // Use metadata to locate the IdP's SSO service. + MetadataProviderCriteria mc(app, issuer, &IDPSSODescriptor::ELEMENT_QNAME, samlconstants::SAML20P_NS); + entity=m->getEntityDescriptor(mc); + if (!entity.first) { + log.warn("unable to locate metadata for provider (%s)", issuer); + throw MetadataException("Unable to locate metadata for identity provider ($entityID)", namedparams(1, "entityID", issuer)); + } + else if (!entity.second) { + log.warn("unable to locate SAML 2.0 identity provider role for provider (%s)", issuer); + throw MetadataException("Unable to locate SAML 2.0 identity provider role for provider ($entityID)", namedparams(1, "entityID", issuer)); + } + } + + vector assertions; + + AttributeExtractor* extractor = app.getAttributeExtractor(); + if (extractor) { + Locker extlocker(extractor); + if (issuer) { + pair mprefix = app.getString("metadataAttributePrefix"); + if (mprefix.first) { + log.debug("extracting metadata-derived attributes..."); + try { + // We pass nullptr for "issuer" because the IdP isn't the one asserting metadata-based attributes. + extractor->extractAttributes(app, nullptr, *entity.second, resolvedAttrs); + for (vector::iterator a = resolvedAttrs.begin(); a != resolvedAttrs.end(); ++a) { + vector& ids = (*a)->getAliases(); + for (vector::iterator id = ids.begin(); id != ids.end(); ++id) + *id = mprefix.second + *id; + } + } + catch (exception& ex) { + log.error("caught exception extracting attributes: %s", ex.what()); + } + } + } + log.debug("extracting pushed attributes..."); + for (vector::const_iterator t = tokens.begin(); t!=tokens.end(); ++t) { + try { + // Save off any assertions for later use by resolver. + const Assertion* assertion = dynamic_cast(*t); + if (assertion) + assertions.push_back(assertion); + extractor->extractAttributes(app, entity.second, *(*t), resolvedAttrs); + } + catch (exception& ex) { + log.error("caught exception extracting attributes: %s", ex.what()); + } + } + + AttributeFilter* filter = app.getAttributeFilter(); + if (filter && !resolvedAttrs.empty()) { + BasicFilteringContext fc(app, resolvedAttrs, entity.second); + Locker filtlocker(filter); + try { + filter->filterAttributes(fc, resolvedAttrs); + } + catch (exception& ex) { + log.error("caught exception filtering attributes: %s", ex.what()); + log.error("dumping extracted attributes due to filtering exception"); + for_each(resolvedAttrs.begin(), resolvedAttrs.end(), xmltooling::cleanup()); + resolvedAttrs.clear(); + } + } + } + else { + log.warn("no AttributeExtractor plugin installed, check log during startup"); + } + + try { + AttributeResolver* resolver = app.getAttributeResolver(); + if (resolver) { + log.debug("resolving attributes..."); + + vector inputs = inputAttrs; + inputs.insert(inputs.end(), resolvedAttrs.begin(), resolvedAttrs.end()); + + Locker locker(resolver); + auto_ptr ctx( + resolver->createResolutionContext( + app, + entity.first, + samlconstants::SAML20P_NS, + nullptr, + nullptr, + nullptr, + &assertions, + &inputs + ) + ); + resolver->resolveAttributes(*ctx.get()); + if (!ctx->getResolvedAttributes().empty()) + resolvedAttrs.insert(resolvedAttrs.end(), ctx->getResolvedAttributes().begin(), ctx->getResolvedAttributes().end()); + } + } + catch (exception& ex) { + log.error("attribute resolution failed: %s", ex.what()); + } +#else + throw ConfigurationException("Cannot process request using lite version of shibsp library."); +#endif } bool ShibbolethResolver::init(unsigned long features, const char* config, bool rethrow) diff --git a/shibresolver/resolver.h b/shibresolver/resolver.h index e3a6c0c..f9c1feb 100644 --- a/shibresolver/resolver.h +++ b/shibresolver/resolver.h @@ -35,16 +35,9 @@ namespace xmltooling { class XMLTOOL_API XMLObject; }; -#ifndef SHIBSP_LITE -namespace opensaml { - namespace saml2 { - class SAML_API Assertion; - }; -}; -#endif - namespace shibsp { class SHIBSP_API Attribute; + class SHIBSP_API SPRequest; }; namespace shibresolver { @@ -66,12 +59,12 @@ namespace shibresolver { virtual ~ShibbolethResolver(); /** - * Sets the calling service URI, making the Shibboleth SP responsible for + * Sets the calling service request, making the Shibboleth SP responsible for * mapping the service to an Application instance. * - * @param uri identifies the service performing attribute resolution + * @param request identifies the service request performing attribute resolution */ - void setServiceURI(const char* uri); + void setRequest(const shibsp::SPRequest* request); /** * Sets the application ID to use for resolution, bypassing the mapping @@ -89,23 +82,16 @@ namespace shibresolver { void setIssuer(const char* issuer); /** - * Adds a SAML token as input to the resolver. + * Adds an XML token as input to the resolver, generally a SAML assertion. *

The caller retains ownership of the object. * * @param token an input token to evaluate */ - void addToken( -#ifdef SHIBSP_LITE - const xmltooling::XMLObject* token -#else - const opensaml::saml2::Assertion* token -#endif - ); + void addToken(const xmltooling::XMLObject* token); /** * Adds an Attribute as input to the resolver. - *

The caller retains ownership of the object, but it MAY be modified - * during the resolution process. + *

The caller retains ownership of the object. * * @param attr an input Attribute */ @@ -168,8 +154,8 @@ namespace shibresolver { static ShibbolethResolver* create(); protected: - /** Service URI */ - std::string m_serviceURI; + /** Service request. */ + const shibsp::SPRequest* m_request; /** Application ID. */ std::string m_appID; @@ -178,18 +164,13 @@ namespace shibresolver { std::string m_issuer; /** Input tokens. */ -#ifdef SHIBSP_LITE std::vector m_tokens; -#else - std::vector m_tokens; -#endif + /** Input attributes. */ std::vector m_inputAttributes; private: shibsp::ServiceProvider* m_sp; - shibsp::RequestMapper* m_mapper; - shibsp::RequestMapper::Settings m_settings; std::vector m_resolvedAttributes; }; diff --git a/shibresolver/resolver.vcxproj b/shibresolver/resolver.vcxproj index bda0978..93fdede 100644 --- a/shibresolver/resolver.vcxproj +++ b/shibresolver/resolver.vcxproj @@ -120,6 +120,7 @@ Windows MachineX86 $(TargetDir)$(ProjectName)1D.lib + ..\..\cpp-sp\$(Configuration);..\..\cpp-opensaml2\$(Configuration);..\..\cpp-xmltooling\$(Configuration);%(AdditionalLibraryDirectories) @@ -138,6 +139,7 @@ true MachineX86 $(TargetDir)$(ProjectName)1.lib + ..\..\cpp-sp\$(Configuration);..\..\cpp-opensaml2\$(Configuration);..\..\cpp-xmltooling\$(Configuration);%(AdditionalLibraryDirectories)