Starting to refactor session cache, eliminated IConfig class.
[shibboleth/cpp-sp.git] / shib-target / shib-handlers.cpp
index c717d07..822b703 100644 (file)
 #include <ctime>
 #include <saml/SAMLConfig.h>
 #include <saml/binding/URLEncoder.h>
+#include <saml/saml2/metadata/Metadata.h>
+#include <saml/saml2/metadata/EndpointManager.h>
 #include <saml/util/CommonDomainCookie.h>
+#include <shibsp/AbstractHandler.h>
 #include <shibsp/SPConfig.h>
 
 #ifdef HAVE_UNISTD_H
@@ -37,34 +40,40 @@ using namespace shibsp;
 using namespace shibtarget;
 using namespace shibboleth;
 using namespace saml;
+using namespace opensaml::saml2md;
 using namespace log4cpp;
 using namespace std;
 
 using opensaml::CommonDomainCookie;
 using opensaml::URLEncoder;
 
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4250 )
+#endif
+
 namespace {
-  class SessionInitiator : virtual public IHandler
+  class SessionInitiator : public AbstractHandler
   {
   public:
-    SessionInitiator(const DOMElement* e) {}
+    SessionInitiator(const DOMElement* e) : AbstractHandler(e) {}
     ~SessionInitiator() {}
-    pair<bool,void*> run(ShibTarget* st, bool isHandler=true) const;
-    pair<bool,void*> ShibAuthnRequest(
-        ShibTarget* st,
-        const IHandler* shire,
+    pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
+    pair<bool,long> ShibAuthnRequest(
+        SPRequest& request,
+        const Handler* shire,
         const char* dest,
         const char* target,
         const char* providerId
         ) const;
   };
 
-  class SAML1Consumer : virtual public IHandler, public virtual Remoted
+  class SAML1Consumer : public AbstractHandler, public virtual Remoted
   {
   public:
     SAML1Consumer(const DOMElement* e);
     ~SAML1Consumer();
-    pair<bool,void*> run(ShibTarget* st, bool isHandler=true) const;
+    pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
     DDF receive(const DDF& in);
   private:
     string m_address;
@@ -73,42 +82,45 @@ namespace {
 
   int SAML1Consumer::counter = 0;
 
-  class ShibLogout : virtual public IHandler
+  class ShibLogout : public AbstractHandler
   {
   public:
-    ShibLogout(const DOMElement* e) {}
+    ShibLogout(const DOMElement* e) : AbstractHandler(e) {}
     ~ShibLogout() {}
-    pair<bool,void*> run(ShibTarget* st, bool isHandler=true) const;
+    pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
   };
 }
 
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
 
-IPlugIn* ShibSessionInitiatorFactory(const DOMElement* e)
+Handler* ShibSessionInitiatorFactory(const DOMElement* const & e)
 {
     return new SessionInitiator(e);
 }
 
-IPlugIn* SAML1POSTFactory(const DOMElement* e)
+Handler* SAML1POSTFactory(const DOMElement* const & e)
 {
     return new SAML1Consumer(e);
 }
 
-IPlugIn* SAML1ArtifactFactory(const DOMElement* e)
+Handler* SAML1ArtifactFactory(const DOMElement* const & e)
 {
     return new SAML1Consumer(e);
 }
 
-IPlugIn* ShibLogoutFactory(const DOMElement* e)
+Handler* ShibLogoutFactory(const DOMElement* const & e)
 {
     return new ShibLogout(e);
 }
 
-pair<bool,void*> SessionInitiator::run(ShibTarget* st, bool isHandler) const
+pair<bool,long> SessionInitiator::run(SPRequest& request, bool isHandler) const
 {
     string dupresource;
     const char* resource=NULL;
-    const IHandler* ACS=NULL;
-    const IApplication* app=st->getApplication();
+    const Handler* ACS=NULL;
+    const IApplication& app=dynamic_cast<const IApplication&>(request.getApplication());
     
     if (isHandler) {
         /* 
@@ -117,18 +129,18 @@ pair<bool,void*> SessionInitiator::run(ShibTarget* st, bool isHandler) const
          *  acsIndex    optional index of an ACS to use on the way back in
          *  providerId  optional direct invocation of a specific IdP
          */
-        const char* option=st->getRequestParameter("acsIndex");
+        const char* option=request.getParameter("acsIndex");
         if (option)
-            ACS=app->getAssertionConsumerServiceByIndex(atoi(option));
-        option=st->getRequestParameter("providerId");
+            ACS=app.getAssertionConsumerServiceByIndex(atoi(option));
+        option=request.getParameter("providerId");
         
-        resource=st->getRequestParameter("target");
+        resource=request.getParameter("target");
         if (!resource || !*resource) {
-            pair<bool,const char*> home=app->getString("homeURL");
+            pair<bool,const char*> home=app.getString("homeURL");
             if (home.first)
                 resource=home.second;
             else
-                throw FatalProfileException("Session initiator requires a target parameter or a homeURL application property.");
+                throw opensaml::FatalProfileException("Session initiator requires a target parameter or a homeURL application property.");
         }
         else if (!option) {
             dupresource=resource;
@@ -138,66 +150,68 @@ pair<bool,void*> SessionInitiator::run(ShibTarget* st, bool isHandler) const
         if (option) {
             // Here we actually use metadata to invoke the SSO service directly.
             // The only currently understood binding is the Shibboleth profile.
-            Metadata m(app->getMetadataProviders());
-            const IEntityDescriptor* entity=m.lookup(option);
+            
+            MetadataProvider* m=app.getMetadataProvider();
+            xmltooling::Locker locker(m);
+            const EntityDescriptor* entity=m->getEntityDescriptor(option);
             if (!entity)
-                throw MetadataException("Session initiator unable to locate metadata for provider ($1).", params(1,option));
-            const IIDPSSODescriptor* role=entity->getIDPSSODescriptor(Constants::SHIB_NS);
+                throw MetadataException("Session initiator unable to locate metadata for provider ($1).", xmltooling::params(1,option));
+            const IDPSSODescriptor* role=entity->getIDPSSODescriptor(shibspconstants::SHIB1_PROTOCOL_ENUM);
             if (!role)
                 throw MetadataException(
-                    "Session initiator unable to locate a Shibboleth-aware identity provider role for provider ($1).", params(1,option)
+                    "Session initiator unable to locate a Shibboleth-aware identity provider role for provider ($1).",
+                    xmltooling::params(1,option)
                     );
-            const IEndpointManager* SSO=role->getSingleSignOnServiceManager();
-            const IEndpoint* ep=SSO->getEndpointByBinding(Constants::SHIB_AUTHNREQUEST_PROFILE_URI);
+            const EndpointType* ep=EndpointManager<SingleSignOnService>(role->getSingleSignOnServices()).getByBinding(
+                shibspconstants::SHIB1_AUTHNREQUEST_PROFILE_URI
+                );
             if (!ep)
                 throw MetadataException(
-                    "Session initiator unable to locate compatible SSO service for provider ($1).", params(1,option)
+                    "Session initiator unable to locate compatible SSO service for provider ($1).", xmltooling::params(1,option)
                     );
             auto_ptr_char dest(ep->getLocation());
             return ShibAuthnRequest(
-                st,ACS ? ACS : app->getDefaultAssertionConsumerService(),dest.get(),resource,app->getString("providerId").second
+                request,ACS ? ACS : app.getDefaultAssertionConsumerService(),dest.get(),resource,app.getString("providerId").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.
-        resource=st->getRequestURL();
+        resource=request.getRequestURL();
     }
     
-    if (!ACS) ACS=app->getDefaultAssertionConsumerService();
+    if (!ACS) ACS=app.getDefaultAssertionConsumerService();
     
     // For now, we only support external session initiation via a wayfURL
-    pair<bool,const char*> wayfURL=getProperties()->getString("wayfURL");
+    pair<bool,const char*> wayfURL=getString("wayfURL");
     if (!wayfURL.first)
         throw ConfigurationException("Session initiator is missing wayfURL property.");
 
-    pair<bool,const XMLCh*> wayfBinding=getProperties()->getXMLString("wayfBinding");
-    if (!wayfBinding.first || !XMLString::compareString(wayfBinding.second,Constants::SHIB_AUTHNREQUEST_PROFILE_URI))
+    pair<bool,const XMLCh*> wayfBinding=getXMLString("wayfBinding");
+    if (!wayfBinding.first || !XMLString::compareString(wayfBinding.second,shibspconstants::SHIB1_AUTHNREQUEST_PROFILE_URI))
         // Standard Shib 1.x
-        return ShibAuthnRequest(st,ACS,wayfURL.second,resource,app->getString("providerId").second);
-    else if (!XMLString::compareString(wayfBinding.second,Constants::SHIB_LEGACY_AUTHNREQUEST_PROFILE_URI))
-        // Shib pre-1.2
-        return ShibAuthnRequest(st,ACS,wayfURL.second,resource,NULL);
-    else if (!strcmp(getProperties()->getString("wayfBinding").second,"urn:mace:shibboleth:1.0:profiles:EAuth")) {
+        return ShibAuthnRequest(request,ACS,wayfURL.second,resource,app.getString("providerId").second);
+    else if (!strcmp(getString("wayfBinding").second,"urn:mace:shibboleth:1.0:profiles:EAuth")) {
         // TODO: Finalize E-Auth profile URI
-        pair<bool,bool> localRelayState=st->getConfig()->getPropertySet("InProcess")->getBool("localRelayState");
+        pair<bool,bool> localRelayState=request.getServiceProvider().getPropertySet("InProcess")->getBool("localRelayState");
         if (!localRelayState.first || !localRelayState.second)
             throw ConfigurationException("E-Authn requests cannot include relay state, so localRelayState must be enabled.");
 
         // Here we store the state in a cookie.
-        pair<string,const char*> shib_cookie=st->getCookieNameProps("_shibstate_");
-        st->setCookie(shib_cookie.first,opensaml::SAMLConfig::getConfig().getURLEncoder()->encode(resource) + shib_cookie.second);
-        return make_pair(true, st->sendRedirect(wayfURL.second));
+        pair<string,const char*> shib_cookie=app.getCookieNameProps("_shibstate_");
+        string stateval = opensaml::SAMLConfig::getConfig().getURLEncoder()->encode(resource) + shib_cookie.second;
+            request.setCookie(shib_cookie.first.c_str(),stateval.c_str());
+        return make_pair(true, request.sendRedirect(wayfURL.second));
     }
    
-    throw UnsupportedProfileException("Unsupported WAYF binding ($1).", params(1,getProperties()->getString("wayfBinding").second));
+    throw opensaml::BindingException("Unsupported WAYF binding ($1).", xmltooling::params(1,getString("wayfBinding").second));
 }
 
 // Handles Shib 1.x AuthnRequest profile.
-pair<bool,void*> SessionInitiator::ShibAuthnRequest(
-    ShibTarget* st,
-    const IHandler* shire,
+pair<bool,long> SessionInitiator::ShibAuthnRequest(
+    SPRequest& request,
+    const Handler* shire,
     const char* dest,
     const char* target,
     const char* providerId
@@ -205,8 +219,8 @@ pair<bool,void*> SessionInitiator::ShibAuthnRequest(
 {
     // Compute the ACS URL. We add the ACS location to the base handlerURL.
     // Legacy configs will not have the Location property specified, so no suffix will be added.
-    string ACSloc=st->getHandlerURL(target);
-    pair<bool,const char*> loc=shire ? shire->getProperties()->getString("Location") : pair<bool,const char*>(false,NULL);
+    string ACSloc=request.getHandlerURL(target);
+    pair<bool,const char*> loc=shire ? shire->getString("Location") : pair<bool,const char*>(false,NULL);
     if (loc.first) ACSloc+=loc.second;
     
     URLEncoder* urlenc = opensaml::SAMLConfig::getConfig().getURLEncoder();
@@ -216,7 +230,7 @@ pair<bool,void*> SessionInitiator::ShibAuthnRequest(
     string req=string(dest) + "?shire=" + urlenc->encode(ACSloc.c_str()) + "&time=" + timebuf;
 
     // How should the resource value be preserved?
-    pair<bool,bool> localRelayState=st->getConfig()->getPropertySet("InProcess")->getBool("localRelayState");
+    pair<bool,bool> localRelayState=request.getServiceProvider().getPropertySet("InProcess")->getBool("localRelayState");
     if (!localRelayState.first || !localRelayState.second) {
         // The old way, just send it along.
         req+="&target=" + urlenc->encode(target);
@@ -224,8 +238,9 @@ pair<bool,void*> SessionInitiator::ShibAuthnRequest(
     else {
         // Here we store the state in a cookie and send a fixed
         // value to the IdP so we can recognize it on the way back.
-        pair<string,const char*> shib_cookie=st->getCookieNameProps("_shibstate_");
-        st->setCookie(shib_cookie.first,urlenc->encode(target) + shib_cookie.second);
+        pair<string,const char*> shib_cookie=request.getApplication().getCookieNameProps("_shibstate_");
+        string stateval = urlenc->encode(target) + shib_cookie.second;
+        request.setCookie(shib_cookie.first.c_str(),stateval.c_str());
         req+="&target=cookie";
     }
     
@@ -233,27 +248,22 @@ pair<bool,void*> SessionInitiator::ShibAuthnRequest(
     if (providerId)
         req+="&providerId=" + urlenc->encode(providerId);
 
-    return make_pair(true, st->sendRedirect(req));
+    return make_pair(true, request.sendRedirect(req.c_str()));
 }
 
-SAML1Consumer::SAML1Consumer(const DOMElement* e)
+SAML1Consumer::SAML1Consumer(const DOMElement* e) : AbstractHandler(e)
 {
     m_address += ('A' + (counter++));
     m_address += "::SAML1Consumer::run";
 
     // Register for remoted messages.
-    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
-        ListenerService* listener=ShibTargetConfig::getConfig().getINI()->getListener();
-        if (listener)
-            listener->regListener(m_address.c_str(),this);
-        else
-            throw ListenerException("Plugin requires a Listener service");
-    }
+    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
+        SPConfig::getConfig().getServiceProvider()->getListenerService()->regListener(m_address.c_str(),this);
 }
 
 SAML1Consumer::~SAML1Consumer()
 {
-    ListenerService* listener=ShibTargetConfig::getConfig().getINI()->getListener();
+    ListenerService* listener=SPConfig::getConfig().getServiceProvider()->getListenerService(false);
     if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
         listener->unregListener(m_address.c_str(),this);
     counter--;
@@ -277,13 +287,13 @@ SAML1Consumer::~SAML1Consumer()
 DDF SAML1Consumer::receive(const DDF& in)
 {
 #ifdef _DEBUG
-    saml::NDC ndc("receive");
+    xmltooling::NDC ndc("receive");
 #endif
     Category& log=Category::getInstance(SHIBT_LOGCAT".SAML1Consumer");
 
     // Find application.
     const char* aid=in["application_id"].string();
-    const IApplication* app=aid ? ShibTargetConfig::getConfig().getINI()->getApplication(aid) : NULL;
+    const IApplication* app=aid ? dynamic_cast<const IApplication*>(SPConfig::getConfig().getServiceProvider()->getApplication(aid)) : NULL;
     if (!app) {
         // Something's horribly wrong.
         log.error("couldn't find application (%s) for new session", aid ? aid : "(missing)");
@@ -300,9 +310,9 @@ DDF SAML1Consumer::receive(const DDF& in)
     log.debug("recipient: %s", recipient);
     log.debug("application: %s", app->getId());
 
-    // Access the application config. It's already locked behind us.
-    STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
-    IConfig* conf=stc.getINI();
+    // Access the application config.
+    ServiceProvider* conf=SPConfig::getConfig().getServiceProvider();
+    xmltooling::Locker confLocker(conf);
 
     auto_ptr_XMLCh wrecipient(recipient);
 
@@ -319,12 +329,14 @@ DDF SAML1Consumer::receive(const DDF& in)
     }
 
     // Supports either version...
-    pair<bool,unsigned int> version=getProperties()->getUnsignedInt("MinorVersion","urn:oasis:names:tc:SAML:1.0:protocol");
+    pair<bool,unsigned int> version=getUnsignedInt("MinorVersion","urn:oasis:names:tc:SAML:1.0:protocol");
     if (!version.first)
         version.second=1;
 
-    const IRoleDescriptor* role=NULL;
-    Metadata m(app->getMetadataProviders());
+    const EntityDescriptor* provider=NULL;
+    const RoleDescriptor* role=NULL;
+    MetadataProvider* m=app->getMetadataProvider();
+    xmltooling::Locker locker(m);
     SAMLBrowserProfile::BrowserProfileResponse bpr;
 
     try {
@@ -335,7 +347,7 @@ DDF SAML1Consumer::receive(const DDF& in)
             bpr=app->getBrowserProfile()->receive(
                 samlResponse,
                 wrecipient.get(),
-                checkReplay.second ? conf->getReplayCache() : NULL,
+                NULL,
                 version.second
                 );
         }
@@ -354,7 +366,7 @@ DDF SAML1Consumer::receive(const DDF& in)
                 SAMLart,
                 wrecipient.get(),
                 artifactMapper.get(),
-                checkReplay.second ? conf->getReplayCache() : NULL,
+                NULL,
                 version.second
                 );
 
@@ -363,16 +375,15 @@ DDF SAML1Consumer::receive(const DDF& in)
         }
 
         // Try and map to metadata (again).
-        // Once the metadata layer is in the SAML core, the repetition should be fixed.
-        const IEntityDescriptor* provider=m.lookup(bpr.assertion->getIssuer());
+        // Once the metadata layer is in the SAML core, the repetition will be fixed.
+        provider=m->getEntityDescriptor(bpr.assertion->getIssuer());
         if (!provider && bpr.authnStatement->getSubject()->getNameIdentifier() &&
                 bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier())
-            provider=m.lookup(bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier());
+            provider=m->getEntityDescriptor(bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier());
         if (provider) {
-            const IIDPSSODescriptor* IDP=provider->getIDPSSODescriptor(
-                version.second==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
+            role=provider->getIDPSSODescriptor(
+                version.second==1 ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM
                 );
-            role=IDP;
         }
         
         // This isn't likely, since the profile must have found a role.
@@ -390,19 +401,18 @@ DDF SAML1Consumer::receive(const DDF& in)
                 // Verify the client address matches authentication
                 auto_ptr_char this_ip(wip);
                 if (strcmp(client_address, this_ip.get())) {
-                    FatalProfileException ex(
-                        SESSION_E_ADDRESSMISMATCH,
+                    opensaml::FatalProfileException ex(
                        "Your client's current address ($1) differs from the one used when you authenticated "
                         "to your identity provider. To correct this problem, you may need to bypass a proxy server. "
                         "Please contact your local support staff or help desk for assistance.",
-                        params(1,client_address)
+                        xmltooling::params(1,client_address)
                         );
                     annotateException(&ex,role); // throws it
                 }
             }
         }
     }
-    catch (SAMLException&) {
+    catch (exception&) {
         bpr.clear();
         throw;
     }
@@ -413,72 +423,61 @@ DDF SAML1Consumer::receive(const DDF& in)
         throw;
 #else
         SAMLException e("An unexpected error occurred while creating your session.");
-        annotateException(&e,role);
+        shibboleth::annotateException(&e,role);
 #endif
     }
 
     // It passes all our tests -- create a new session.
     log.info("creating new session");
 
-    DDF out;
-    try {
-        // Insert into cache.
-        auto_ptr_char authContext(bpr.authnStatement->getAuthMethod());
-        string key=conf->getSessionCache()->insert(
-            app,
-            role->getEntityDescriptor(),
-            client_address,
-            bpr.authnStatement->getSubject(),
-            authContext.get(),
-            bpr.response
-            );
-        // objects owned by cache now
-        log.debug("new session id: %s", key.c_str());
-        auto_ptr_char oname(role->getEntityDescriptor()->getId());
-        out=DDF(NULL).structure();
-        out.addmember("key").string(key.c_str());
-        out.addmember("provider_id").string(oname.get());
-    }
-    catch (...) {
-#ifdef _DEBUG
-        throw;
-#else
-        SAMLException e("An unexpected error occurred while creating your session.");
-        annotateException(&e,role);
-#endif
-    }
+    // Insert into cache.
+    auto_ptr_char authContext(bpr.authnStatement->getAuthMethod());
+    string key=dynamic_cast<ISessionCache*>(conf->getSessionCache())->insert(
+        app,
+        role,
+        client_address,
+        bpr.authnStatement->getSubject(),
+        authContext.get(),
+        bpr.response
+        );
+    // objects owned by cache now
+    log.debug("new session id: %s", key.c_str());
+    auto_ptr_char oname(provider->getEntityID());
+    DDF out=DDF(NULL).structure();
+    out.addmember("key").string(key.c_str());
+    out.addmember("provider_id").string(oname.get());
 
     return out;
 }
 
-pair<bool,void*> SAML1Consumer::run(ShibTarget* st, bool isHandler) const
+pair<bool,long> SAML1Consumer::run(SPRequest& request, bool isHandler) const
 {
     DDF in,out;
     DDFJanitor jin(in),jout(out);
 
-    pair<bool,const XMLCh*> binding=getProperties()->getXMLString("Binding");
+    pair<bool,const XMLCh*> binding=getXMLString("Binding");
     if (!binding.first || !XMLString::compareString(binding.second,SAMLBrowserProfile::BROWSER_POST)) {
 #ifdef HAVE_STRCASECMP
-        if (strcasecmp(st->getRequestMethod(), "POST")) {
+        if (strcasecmp(request.getMethod(), "POST")) {
 #else
-        if (_stricmp(st->getRequestMethod(), "POST")) {
+        if (_stricmp(request.getMethod(), "POST")) {
 #endif
-            st->log(ShibTarget::LogLevelInfo, "SAML 1.x Browser/POST handler ignoring non-POST request");
-            return pair<bool,void*>(false,NULL);
+            request.log(SPRequest::SPInfo, "SAML 1.x Browser/POST handler ignoring non-POST request");
+            return pair<bool,long>(false,NULL);
         }
 #ifdef HAVE_STRCASECMP
-        if (!st->getContentType() || strcasecmp(st->getContentType(),"application/x-www-form-urlencoded")) {
+        if (strcasecmp(request.getContentType().c_str(),"application/x-www-form-urlencoded")) {
 #else
-        if (!st->getContentType() || _stricmp(st->getContentType(),"application/x-www-form-urlencoded")) {
+        if (_stricmp(request.getContentType().c_str(),"application/x-www-form-urlencoded")) {
 #endif
-            st->log(ShibTarget::LogLevelInfo, "SAML 1.x Browser/POST handler ignoring submission with unknown content-type.");
-            return pair<bool,void*>(false,NULL);
+            request.log(SPRequest::SPInfo, "SAML 1.x Browser/POST handler ignoring submission with unknown content-type.");
+            return pair<bool,long>(false,0);
         }
 
-        const char* samlResponse = st->getRequestParameter("SAMLResponse");
+        const char* samlResponse = request.getParameter("SAMLResponse");
         if (!samlResponse) {
-            st->log(ShibTarget::LogLevelInfo, "SAML 1.x Browser/POST handler ignoring request with no SAMLResponse parameter.");
-            return pair<bool,void*>(false,NULL);
+            request.log(SPRequest::SPInfo, "SAML 1.x Browser/POST handler ignoring request with no SAMLResponse parameter.");
+            return pair<bool,long>(false,0);
         }
 
         in=DDF(m_address.c_str()).structure();
@@ -486,58 +485,56 @@ pair<bool,void*> SAML1Consumer::run(ShibTarget* st, bool isHandler) const
     }
     else if (!XMLString::compareString(binding.second,SAMLBrowserProfile::BROWSER_ARTIFACT)) {
 #ifdef HAVE_STRCASECMP
-        if (strcasecmp(st->getRequestMethod(), "GET")) {
+        if (strcasecmp(request.getMethod(), "GET")) {
 #else
-        if (_stricmp(st->getRequestMethod(), "GET")) {
+        if (_stricmp(request.getMethod(), "GET")) {
 #endif
-            st->log(ShibTarget::LogLevelInfo, "SAML 1.x Browser/Artifact handler ignoring non-GET request");
-            return pair<bool,void*>(false,NULL);
+            request.log(SPRequest::SPInfo, "SAML 1.x Browser/Artifact handler ignoring non-GET request");
+            return pair<bool,long>(false,0);
         }
 
-        const char* SAMLart=st->getRequestParameter("SAMLart");
-        if (!SAMLart) {
-            st->log(ShibTarget::LogLevelInfo, "SAML 1.x Browser/Artifact handler ignoring request with no SAMLart parameter.");
-            return pair<bool,void*>(false,NULL);
+        vector<const char*> arts;
+        if (request.getParameters("SAMLart",arts)==0) {
+            request.log(SPRequest::SPInfo, "SAML 1.x Browser/Artifact handler ignoring request with no SAMLart parameter.");
+            return pair<bool,long>(false,0);
         }
 
         in=DDF(m_address.c_str()).structure();
         DDF artlist=in.addmember("SAMLart").list();
 
-        while (SAMLart) {
-            artlist.add(DDF(NULL).string(SAMLart));
-            SAMLart=st->getRequestParameter("SAMLart",artlist.integer());
-        }
+        for (vector<const char*>::const_iterator a=arts.begin(); a!=arts.end(); ++a)
+            artlist.add(DDF(NULL).string(*a));
     }
     
     // Compute the endpoint location.
-    string hURL=st->getHandlerURL(st->getRequestURL());
-    pair<bool,const char*> loc=getProperties()->getString("Location");
+    string hURL=request.getHandlerURL(request.getRequestURL());
+    pair<bool,const char*> loc=getString("Location");
     string recipient=loc.first ? hURL + loc.second : hURL;
     in.addmember("recipient").string(recipient.c_str());
 
     // Add remaining parameters.
-    in.addmember("application_id").string(st->getApplication()->getId());
-    in.addmember("client_address").string(st->getRemoteAddr());
+    in.addmember("application_id").string(request.getApplication().getId());
+    in.addmember("client_address").string(request.getRemoteAddr().c_str());
 
-    out=st->getConfig()->getListener()->send(in);
+    out=request.getServiceProvider().getListenerService()->send(in);
     if (!out["key"].isstring())
-        throw FatalProfileException("Remote processing of SAML 1.x Browser profile did not return a usable session key.");
+        throw opensaml::FatalProfileException("Remote processing of SAML 1.x Browser profile did not return a usable session key.");
     string key=out["key"].string();
 
-    st->log(ShibTarget::LogLevelDebug, string("profile processing succeeded, new session created (") + key + ")");
+    request.log(SPRequest::SPDebug, string("profile processing succeeded, new session created (") + key + ")");
 
-    const char* target=st->getRequestParameter("TARGET");
+    const char* target=request.getParameter("TARGET");
     if (target && !strcmp(target,"default")) {
-        pair<bool,const char*> homeURL=st->getApplication()->getString("homeURL");
+        pair<bool,const char*> homeURL=request.getApplication().getString("homeURL");
         target=homeURL.first ? homeURL.second : "/";
     }
     else if (!target || !strcmp(target,"cookie")) {
         // Pull the target value from the "relay state" cookie.
-        pair<string,const char*> relay_cookie = st->getCookieNameProps("_shibstate_");
-        const char* relay_state = st->getCookie(relay_cookie.first);
+        pair<string,const char*> relay_cookie = request.getApplication().getCookieNameProps("_shibstate_");
+        const char* relay_state = request.getCookie(relay_cookie.first.c_str());
         if (!relay_state || !*relay_state) {
             // No apparent relay state value to use, so fall back on the default.
-            pair<bool,const char*> homeURL=st->getApplication()->getString("homeURL");
+            pair<bool,const char*> homeURL=request.getApplication().getString("homeURL");
             target=homeURL.first ? homeURL.second : "/";
         }
         else {
@@ -547,76 +544,78 @@ pair<bool,void*> SAML1Consumer::run(ShibTarget* st, bool isHandler) const
             free(rscopy);
             target=hURL.c_str();
         }
-        st->setCookie(relay_cookie.first,relay_cookie.second);
+        request.setCookie(relay_cookie.first.c_str(),relay_cookie.second);
     }
 
     // We've got a good session, set the session cookie.
-    pair<string,const char*> shib_cookie=st->getCookieNameProps("_shibsession_");
-    st->setCookie(shib_cookie.first, key + shib_cookie.second);
+    pair<string,const char*> shib_cookie=request.getApplication().getCookieNameProps("_shibsession_");
+    key += shib_cookie.second;
+    request.setCookie(shib_cookie.first.c_str(), key.c_str());
 
     const char* providerId=out["provider_id"].string();
     if (providerId) {
-        const PropertySet* sessionProps=st->getApplication()->getPropertySet("Sessions");
+        const PropertySet* sessionProps=request.getApplication().getPropertySet("Sessions");
         pair<bool,bool> idpHistory=sessionProps->getBool("idpHistory");
         if (!idpHistory.first || idpHistory.second) {
             // Set an IdP history cookie locally (essentially just a CDC).
-            CommonDomainCookie cdc(st->getCookie(CommonDomainCookie::CDCName));
+            CommonDomainCookie cdc(request.getCookie(CommonDomainCookie::CDCName));
 
             // Either leave in memory or set an expiration.
             pair<bool,unsigned int> days=sessionProps->getUnsignedInt("idpHistoryDays");
-                if (!days.first || days.second==0)
-                    st->setCookie(CommonDomainCookie::CDCName,string(cdc.set(providerId)) + shib_cookie.second);
-                else {
-                    time_t now=time(NULL) + (days.second * 24 * 60 * 60);
+            if (!days.first || days.second==0) {
+                key = string(cdc.set(providerId)) + shib_cookie.second;
+                request.setCookie(CommonDomainCookie::CDCName, key.c_str());
+            }
+            else {
+                time_t now=time(NULL) + (days.second * 24 * 60 * 60);
 #ifdef HAVE_GMTIME_R
-                    struct tm res;
-                    struct tm* ptime=gmtime_r(&now,&res);
+                struct tm res;
+                struct tm* ptime=gmtime_r(&now,&res);
 #else
-                    struct tm* ptime=gmtime(&now);
+                struct tm* ptime=gmtime(&now);
 #endif
-                    char timebuf[64];
-                    strftime(timebuf,64,"%a, %d %b %Y %H:%M:%S GMT",ptime);
-                    st->setCookie(
-                        CommonDomainCookie::CDCName,
-                        string(cdc.set(providerId)) + shib_cookie.second + "; expires=" + timebuf
-                        );
+                char timebuf[64];
+                strftime(timebuf,64,"%a, %d %b %Y %H:%M:%S GMT",ptime);
+                key = string(cdc.set(providerId)) + shib_cookie.second + "; expires=" + timebuf;
+                request.setCookie(CommonDomainCookie::CDCName, key.c_str());
             }
         }
     }
 
     // Now redirect to the target.
-    return make_pair(true, st->sendRedirect(target));
+    return make_pair(true, request.sendRedirect(target));
 }
 
-pair<bool,void*> ShibLogout::run(ShibTarget* st, bool isHandler) const
+pair<bool,long> ShibLogout::run(SPRequest& request, bool isHandler) const
 {
     // Recover the session key.
-    pair<string,const char*> shib_cookie = st->getCookieNameProps("_shibsession_");
-    const char* session_id = st->getCookie(shib_cookie.first);
+    pair<string,const char*> shib_cookie = request.getApplication().getCookieNameProps("_shibsession_");
+    const char* session_id = request.getCookie(shib_cookie.first.c_str());
     
     // Logout is best effort.
     if (session_id && *session_id) {
         try {
-            st->getConfig()->getSessionCache()->remove(session_id,st->getApplication(),st->getRemoteAddr());
+            // TODO: port to new cache API
+            //request.getServiceProvider().getSessionCache()->remove(session_id,request.getApplication(),request.getRemoteAddr().c_str());
         }
-        catch (SAMLException& e) {
-            st->log(ShibTarget::LogLevelError, string("logout processing failed with exception: ") + e.what());
+        catch (exception& e) {
+            request.log(SPRequest::SPError, string("logout processing failed with exception: ") + e.what());
         }
 #ifndef _DEBUG
         catch (...) {
-            st->log(ShibTarget::LogLevelError, "logout processing failed with unknown exception");
+            request.log(SPRequest::SPError, "logout processing failed with unknown exception");
         }
 #endif
         // We send the cookie property alone, which acts as an empty value.
-        st->setCookie(shib_cookie.first,shib_cookie.second);
+        request.setCookie(shib_cookie.first.c_str(),shib_cookie.second);
     }
     
-    const char* ret=st->getRequestParameter("return");
+    const char* ret=request.getParameter("return");
     if (!ret)
-        ret=getProperties()->getString("ResponseLocation").second;
+        ret=getString("ResponseLocation").second;
     if (!ret)
-        ret=st->getApplication()->getString("homeURL").second;
+        ret=request.getApplication().getString("homeURL").second;
     if (!ret)
         ret="/";
-    return make_pair(true, st->sendRedirect(ret));
+    return make_pair(true, request.sendRedirect(ret));
 }