Draft implementation. master
authorcantor <cantor@3ebe4818-3638-0410-822d-ece5924dabe2>
Sun, 3 Oct 2010 21:17:52 +0000 (21:17 +0000)
committercantor <cantor@3ebe4818-3638-0410-822d-ece5924dabe2>
Sun, 3 Oct 2010 21:17:52 +0000 (21:17 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/shib-extension/cpp-sp-resolver/trunk@275 3ebe4818-3638-0410-822d-ece5924dabe2

shibresolver/resolver-lite.vcxproj
shibresolver/resolver.cpp
shibresolver/resolver.h
shibresolver/resolver.vcxproj

index 67951ca..ca2b2f0 100644 (file)
       <GenerateDebugInformation>true</GenerateDebugInformation>\r
       <SubSystem>Windows</SubSystem>\r
       <ImportLibrary>$(TargetDir)$(ProjectName)1D.lib</ImportLibrary>\r
+      <AdditionalLibraryDirectories>..\..\cpp-sp\$(Configuration);..\..\cpp-opensaml2\$(Configuration);..\..\cpp-xmltooling\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
     </Link>\r
   </ItemDefinitionGroup>\r
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
       <OptimizeReferences>true</OptimizeReferences>\r
       <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
       <ImportLibrary>$(TargetDir)$(ProjectName)1.lib</ImportLibrary>\r
+      <AdditionalLibraryDirectories>..\..\cpp-sp\$(Configuration);..\..\cpp-opensaml2\$(Configuration);..\..\cpp-xmltooling\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
     </Link>\r
   </ItemDefinitionGroup>\r
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
index 2164f82..ce4a722 100644 (file)
 
 #include "internal.h"
 
+#include <shibsp/exceptions.h>
+#include <shibsp/Application.h>
+#include <shibsp/SPRequest.h>
 #include <shibsp/ServiceProvider.h>
 #include <shibsp/attribute/Attribute.h>
 #include <shibsp/remoting/ListenerService.h>
+#ifndef SHIBSP_LITE
+# include <saml/saml2/metadata/Metadata.h>
+# include <saml/saml2/metadata/MetadataProvider.h>
+# include <saml/util/SAMLConstants.h>
+# include <shibsp/attribute/filtering/AttributeFilter.h>
+# include <shibsp/attribute/filtering/BasicFilteringContext.h>
+# include <shibsp/attribute/resolver/AttributeExtractor.h>
+# include <shibsp/attribute/resolver/AttributeResolver.h>
+# include <shibsp/attribute/resolver/ResolutionContext.h>
+# include <shibsp/metadata/MetadataProviderCriteria.h>
+#endif
+#include <xmltooling/XMLObjectBuilder.h>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/ParserPool.h>
+#include <xmltooling/util/XMLHelper.h>
 
 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<XMLObject>());
+                for_each(inputAttrs.begin(), inputAttrs.end(), xmltooling::cleanup<Attribute>());
+                for_each(resolvedAttrs.begin(), resolvedAttrs.end(), xmltooling::cleanup<Attribute>());
+            }
+
+            vector<const XMLObject*> tokens;
+            vector<Attribute*> inputAttrs;
+            vector<Attribute*> resolvedAttrs;
+        };
+
         void receive(DDF& in, ostream& out);
+        void resolve(
+            const Application& app,
+            const char* issuer,
+            const vector<const XMLObject*>& tokens,
+            const vector<Attribute*>& inputAttrs,
+            vector <Attribute*>& 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<Attribute>());
-    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<Attribute*>& 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 XMLObject*>::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<Attribute*>::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<DOMDocument> 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<Attribute*>::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<const XMLObject*>& tokens,
+    const vector<Attribute*>& inputAttrs,
+    vector <Attribute*>& resolvedAttrs
+    ) const
+{
+#ifndef SHIBSP_LITE
+    Category& log = Category::getInstance(SHIBRESOLVER_LOGCAT);
+    pair<const EntityDescriptor*,const RoleDescriptor*> entity = pair<const EntityDescriptor*,const RoleDescriptor*>(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<const Assertion*> assertions;
+
+    AttributeExtractor* extractor = app.getAttributeExtractor();
+    if (extractor) {
+        Locker extlocker(extractor);
+        if (issuer) {
+            pair<bool,const char*> 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<Attribute*>::iterator a = resolvedAttrs.begin(); a != resolvedAttrs.end(); ++a) {
+                        vector<string>& ids = (*a)->getAliases();
+                        for (vector<string>::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 XMLObject*>::const_iterator t = tokens.begin(); t!=tokens.end(); ++t) {
+            try {
+                // Save off any assertions for later use by resolver.
+                const Assertion* assertion = dynamic_cast<const Assertion*>(*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<shibsp::Attribute>());
+                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<Attribute*> inputs = inputAttrs;
+            inputs.insert(inputs.end(), resolvedAttrs.begin(), resolvedAttrs.end());
+
+            Locker locker(resolver);
+            auto_ptr<ResolutionContext> 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)
index e3a6c0c..f9c1feb 100644 (file)
@@ -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.
          * <p>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.
-         * <p>The caller retains ownership of the object, but it MAY be modified
-         * during the resolution process.
+         * <p>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<const xmltooling::XMLObject*> m_tokens;
-#else
-        std::vector<const opensaml::saml2::Assertion*> m_tokens;
-#endif
+
         /** Input attributes. */
         std::vector<shibsp::Attribute*> m_inputAttributes;
 
     private:
         shibsp::ServiceProvider* m_sp;
-        shibsp::RequestMapper* m_mapper;
-        shibsp::RequestMapper::Settings m_settings;
         std::vector<shibsp::Attribute*> m_resolvedAttributes;
     };
 
index bda0978..93fdede 100644 (file)
       <SubSystem>Windows</SubSystem>\r
       <TargetMachine>MachineX86</TargetMachine>\r
       <ImportLibrary>$(TargetDir)$(ProjectName)1D.lib</ImportLibrary>\r
+      <AdditionalLibraryDirectories>..\..\cpp-sp\$(Configuration);..\..\cpp-opensaml2\$(Configuration);..\..\cpp-xmltooling\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
     </Link>\r
   </ItemDefinitionGroup>\r
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
       <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
       <TargetMachine>MachineX86</TargetMachine>\r
       <ImportLibrary>$(TargetDir)$(ProjectName)1.lib</ImportLibrary>\r
+      <AdditionalLibraryDirectories>..\..\cpp-sp\$(Configuration);..\..\cpp-opensaml2\$(Configuration);..\..\cpp-xmltooling\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
     </Link>\r
   </ItemDefinitionGroup>\r
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r