Attribute lookup, port ACL code and mainline SP code to Session/Attribute API.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 5 Feb 2007 01:49:05 +0000 (01:49 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 5 Feb 2007 01:49:05 +0000 (01:49 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2155 cb58f699-b61c-0410-a6fe-9272a202ed29

17 files changed:
apache/mod_apache.cpp
apache/mod_shib13.vcproj
apache/mod_shib20.vcproj
apache/mod_shib22.vcproj
isapi_shib/isapi_shib.cpp
nsapi_shib/nsapi_shib.cpp
shibsp/AbstractSPRequest.cpp
shibsp/AbstractSPRequest.h
shibsp/SPRequest.h
shibsp/ServiceProvider.cpp
shibsp/SessionCache.h
shibsp/attribute/Attribute.h
shibsp/attribute/ScopedAttribute.h
shibsp/attribute/SimpleAttribute.h
shibsp/impl/RemotedSessionCache.cpp
shibsp/impl/StorageServiceSessionCache.cpp
shibsp/impl/XMLAccessControl.cpp

index bfff1a3..5acfaf5 100644 (file)
 #include <shibsp/exceptions.h>
 #include <shibsp/RequestMapper.h>
 #include <shibsp/SPConfig.h>
+#include <shibsp/attribute/Attribute.h>
 
-// SAML Runtime
-#include <saml/saml.h>
-#include <shib/shib.h>
 #include <shib-target/shib-target.h>
 #include <xercesc/util/regx/RegularExpression.hpp>
 #include <xmltooling/util/NDC.h>
@@ -826,7 +824,7 @@ bool htAccessControl::authorized(const SPRequest& request, const Session* sessio
                 grpstatus=groups_for_user(sta->m_req,remote_user.c_str(),sta->m_dc->szAuthGrpFile);
             }
             if (!grpstatus)
-                return false;
+                continue;
     
             while (*t) {
                 w=ap_getword_conf(sta->m_req->pool,&t);
@@ -837,26 +835,24 @@ bool htAccessControl::authorized(const SPRequest& request, const Session* sessio
             }
         }
         else {
-            saml::Iterator<shibboleth::IAAP*> provs=dynamic_cast<const shibtarget::IApplication&>(request.getApplication()).getAAPProviders();
-            shibboleth::AAP wrapper(provs,w);
-            if (wrapper.fail()) {
-                request.log(SPRequest::SPWarn, string("htAccessControl plugin didn't recognize require rule: ") + w);
+            // Map alias in rule to the attribute.
+            if (!session) {
+                request.log(SPRequest::SPError, "htAccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");
+                continue;
+            }
+            
+            // Find the attribute matching the require rule.
+            map<string,const Attribute*>::const_iterator attr = session->getAttributes().find(w);
+            if (attr == session->getAttributes().end()) {
+                request.log(SPRequest::SPWarn, string("htAccessControl rule requires attribute (") + w + "), not found in session");
                 continue;
             }
 
             bool regexp=false;
-            const char* vals;
-            if (!strcmp(wrapper->getHeader(),"REMOTE_USER"))
-                vals=remote_user.c_str();
-            else
-                if (sta->m_dc->bUseEnvVars!=0) {
-                   if (sta->m_rc && sta->m_rc->env) vals=ap_table_get(sta->m_rc->env,wrapper->getHeader());
-                   else vals = NULL;
-                } else {
-                   vals=ap_table_get(sta->m_req->headers_in,wrapper->getHeader());
-                }
+            bool caseSensitive = attr->second->isCaseSensitive();
+            const vector<string>& vals = attr->second->getSerializedValues();
 
-            while (*t && vals && *vals) {
+            while (!auth_OK[x] && *t) {
                 w=ap_getword_conf(sta->m_req->pool,&t);
                 if (*w=='~') {
                     regexp=true;
@@ -872,67 +868,34 @@ bool htAccessControl::authorized(const SPRequest& request, const Session* sessio
                         re=temp;
                     }
                     
-                    string vals_str(vals);
-                    int j = 0;
-                    for (unsigned int i = 0;  i < vals_str.length();  i++) {
-                        if (vals_str.at(i) == ';') {
-                            if (i == 0) {
-                                request.log(SPRequest::SPError, string("htAccessControl plugin found invalid header encoding (") +
-                                    vals + "): starts with a semicolon");
-                                throw saml::SAMLException("Invalid information supplied to authorization plugin.");
-                            }
-
-                            if (vals_str.at(i-1) == '\\') {
-                                vals_str.erase(i-1, 1);
-                                i--;
-                                continue;
-                            }
-
-                            string val = vals_str.substr(j, i-j);
-                            j = i+1;
-                            if (regexp) {
-                                auto_ptr<XMLCh> trans(fromUTF8(val.c_str()));
-                                if (re->matches(trans.get())) {
-                                    request.log(SPRequest::SPDebug, string("htAccessControl plugin expecting ") + w +
-                                       ", got " + val + ": authorization granted");
-                                    SHIB_AP_CHECK_IS_OK;
-                                }
-                            }
-                            else if ((wrapper->getCaseSensitive() && val==w) || (!wrapper->getCaseSensitive() && !strcasecmp(val.c_str(),w))) {
-                                request.log(SPRequest::SPDebug, string("htAccessControl plugin expecting ") + w +
-                                    ", got " + val + ": authorization granted.");
+                    for (vector<string>::const_iterator v=vals.begin(); !auth_OK[x] && v!=vals.end(); ++v) {
+                        if (regexp) {
+                            auto_ptr<XMLCh> trans(fromUTF8(v->c_str()));
+                            if (re->matches(trans.get())) {
+                                request.log(SPRequest::SPDebug,
+                                    string("htAccessControl plugin expecting ") + w + ", got " + *v + ": authorization granted"
+                                    );
                                 SHIB_AP_CHECK_IS_OK;
                             }
-                            else {
-                                request.log(SPRequest::SPDebug, string("htAccessControl plugin expecting ") + w +
-                                    ", got " + val + ": authoritzation not granted.");
-                            }
                         }
-                    }
-    
-                    string val = vals_str.substr(j, vals_str.length()-j);
-                    if (regexp) {
-                        auto_ptr<XMLCh> trans(fromUTF8(val.c_str()));
-                        if (re->matches(trans.get())) {
-                            request.log(SPRequest::SPDebug, string("htAccessControl plugin expecting ") + w +
-                                ", got " + val + ": authorization granted.");
+                        else if ((caseSensitive && *v == w) || (!caseSensitive && !strcasecmp(v->c_str(),w))) {
+                            request.log(SPRequest::SPDebug,
+                                string("htAccessControl plugin expecting ") + w + ", got " + *v + ": authorization granted."
+                                );
                             SHIB_AP_CHECK_IS_OK;
                         }
-                    }
-                    else if ((wrapper->getCaseSensitive() && val==w) || (!wrapper->getCaseSensitive() && !strcasecmp(val.c_str(),w))) {
-                        request.log(SPRequest::SPDebug, string("htAccessControl plugin expecting ") + w +
-                            ", got " + val + ": authorization granted");
-                        SHIB_AP_CHECK_IS_OK;
-                    }
-                    else {
-                            request.log(SPRequest::SPDebug, string("htAccessControl plugin expecting ") + w +
-                                ", got " + val + ": authorization not granted");
+                        else {
+                            request.log(SPRequest::SPDebug,
+                                string("htAccessControl plugin expecting ") + w + ", got " + *v + ": authorization not granted."
+                                );
+                        }
                     }
                 }
                 catch (XMLException& ex) {
                     auto_ptr_char tmp(ex.getMessage());
-                    request.log(SPRequest::SPError, string("htAccessControl plugin caught exception while parsing regular expression (")
-                        + w + "): " + tmp.get());
+                    request.log(SPRequest::SPError,
+                        string("htAccessControl plugin caught exception while parsing regular expression (") + w + "): " + tmp.get()
+                        );
                 }
             }
         }
index 8a0f87c..1a5527d 100644 (file)
        </References>
        <Files>
                <File
+                       RelativePath=".\mod_apache.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
                        RelativePath="mod_shib_13.cpp"
                        >
                </File>
index 14d9d6d..c7389d7 100644 (file)
        </References>
        <Files>
                <File
+                       RelativePath=".\mod_apache.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
                        RelativePath="mod_shib_20.cpp"
                        >
                </File>
index fab3e9b..f8aa8d6 100644 (file)
        </References>\r
        <Files>\r
                <File\r
+                       RelativePath=".\mod_apache.cpp"\r
+                       >\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32"\r
+                               ExcludedFromBuild="true"\r
+                               >\r
+                               <Tool\r
+                                       Name="VCCLCompilerTool"\r
+                               />\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
                        RelativePath="mod_shib_22.cpp"\r
                        >\r
                </File>\r
index 29dee0d..d14c932 100644 (file)
@@ -29,9 +29,6 @@
 #include <shibsp/SPConfig.h>
 #include <xmltooling/util/NDC.h>
 
-// SAML Runtime
-#include <saml/saml.h>
-#include <shib/shib.h>
 #include <shib-target/shib-target.h>
 
 #include <sstream>
index 1e572ec..1b3891f 100644 (file)
@@ -37,9 +37,6 @@
 #include <xmltooling/util/NDC.h>
 #include <xmltooling/util/Threads.h>
 
-// SAML Runtime
-#include <saml/saml.h>
-#include <shib/shib.h>
 #include <shib-target/shib-target.h>
 
 #include <fstream>
index 0229b2c..85dfe25 100644 (file)
@@ -35,7 +35,8 @@ using namespace log4cpp;
 using namespace std;
 
 AbstractSPRequest::AbstractSPRequest()
-    : m_sp(NULL), m_mapper(NULL), m_app(NULL), m_session(NULL), m_log(&Category::getInstance(SHIBSP_LOGCAT)), m_parser(NULL)
+    : m_sp(NULL), m_mapper(NULL), m_app(NULL), m_sessionTried(false), m_session(NULL),
+        m_log(&Category::getInstance(SHIBSP_LOGCAT)), m_parser(NULL)
 {
     m_sp=SPConfig::getConfig().getServiceProvider();
     m_sp->lock();
@@ -75,6 +76,39 @@ const Application& AbstractSPRequest::getApplication() const
     return *m_app;
 }
 
+Session* AbstractSPRequest::getSession() const
+{
+    // Only attempt this once.
+    if (m_sessionTried)
+        return m_session;
+    m_sessionTried = true;
+
+    // Get session ID from cookie.
+    const Application& app = getApplication();
+    pair<string,const char*> shib_cookie = app.getCookieNameProps("_shibsession_");
+    const char* session_id = getCookie(shib_cookie.first.c_str());
+    if (!session_id || !*session_id)
+        return NULL;
+
+    // Need address checking and timeout settings.
+    int timeout=0;
+    bool consistent=true;
+    const PropertySet* props=app.getPropertySet("Sessions");
+    if (props) {
+        pair<bool,unsigned int> p=props->getUnsignedInt("timeout");
+        if (p.first)
+            timeout = p.second;
+        pair<bool,bool> pcheck=props->getBool("consistentAddress");
+        if (pcheck.first)
+            consistent = pcheck.second;
+    }
+
+    // The cache will either silently pass a session or NULL back, or throw an exception out.
+    return m_session = getServiceProvider().getSessionCache()->find(
+        session_id, app, consistent ? getRemoteAddr().c_str() : NULL, timeout
+        );
+}
+
 const char* AbstractSPRequest::getRequestURL() const {
     if (m_url.empty()) {
         // Compute the full target URL
index 3487ab0..07000c3 100644 (file)
@@ -49,9 +49,7 @@ namespace shibsp {
 
         const Application& getApplication() const;
         
-        const Session* getSession() const {
-            return m_session;
-        }
+        Session* getSession() const;
 
         const char* getRequestURL() const;
         
@@ -72,6 +70,7 @@ namespace shibsp {
         mutable RequestMapper* m_mapper;
         mutable RequestMapper::Settings m_settings;
         mutable const Application* m_app;
+        mutable bool m_sessionTried;
         mutable Session* m_session;
         mutable std::string m_url;
         void* m_log; // declared void* to avoid log4cpp header conflicts in Apache
index 9ea140a..5a4785f 100644 (file)
@@ -76,7 +76,7 @@ namespace shibsp {
          * 
          * @return pointer to Session, or NULL
          */
-        virtual const Session* getSession() const=0;
+        virtual Session* getSession() const=0;
 
         /**
          * Returns the effective base Handler URL for a resource,
index 3429361..377d138 100644 (file)
@@ -28,6 +28,7 @@
 #include "ServiceProvider.h"
 #include "SessionCache.h"
 #include "SPRequest.h"
+#include "attribute/Attribute.h"
 #include "util/TemplateParameters.h"
 
 #include <fstream>
@@ -35,8 +36,8 @@
 #include <saml/saml2/metadata/Metadata.h>
 #include <saml/util/SAMLConstants.h>
 #include <xmltooling/XMLToolingConfig.h>
-#include <xmltooling/util/NDC.h>\r
-#include <xmltooling/util/XMLHelper.h>\r
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/XMLHelper.h>
 
 using namespace shibsp;
 using namespace opensaml::saml2md;
@@ -81,15 +82,15 @@ namespace shibsp {
     
     void SHIBSP_DLLLOCAL clearHeaders(SPRequest& request) {
         // Clear invariant stuff.
-        request.clearHeader("Shib-Origin-Site");
         request.clearHeader("Shib-Identity-Provider");
         request.clearHeader("Shib-Authentication-Method");
-        request.clearHeader("Shib-NameIdentifier-Format");
+        request.clearHeader("Shib-AuthnContext-Class");
+        request.clearHeader("Shib-AuthnContext-Decl");
         request.clearHeader("Shib-Attributes");
         request.clearHeader("Shib-Application-ID");
     
         // Clear out the list of mapped attributes
-        /* TODO: port
+        /* TODO: need some kind of master attribute list via the new resolver
         Iterator<IAAP*> provs=dynamic_cast<const IApplication&>(getApplication()).getAAPProviders();
         while (provs.hasNext()) {
             IAAP* aap=provs.next();
@@ -104,7 +105,7 @@ namespace shibsp {
         */
     }
 
-    static const XMLCh SessionInitiator[] =     UNICODE_LITERAL_16(S,e,s,s,i,o,n,I,n,i,t,i,a,t,o,r);\r
+    static const XMLCh SessionInitiator[] =     UNICODE_LITERAL_16(S,e,s,s,i,o,n,I,n,i,t,i,a,t,o,r);
 };
 
 void SHIBSP_API shibsp::registerServiceProviders()
@@ -114,237 +115,200 @@ void SHIBSP_API shibsp::registerServiceProviders()
 
 pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handler) const
 {
-#ifdef _DEBUG\r
-    xmltooling::NDC ndc("doAuthentication");\r
-#endif\r
-\r
-    const Application* app=NULL;\r
-    const char* procState = "Request Processing Error";\r
-    string targetURL = request.getRequestURL();\r
-\r
-    try {\r
-        RequestMapper::Settings settings = request.getRequestSettings();\r
-        app = &(request.getApplication());\r
-\r
-        // If not SSL, check to see if we should block or redirect it.\r
-        if (!request.isSecure()) {\r
-            pair<bool,const char*> redirectToSSL = settings.first->getString("redirectToSSL");\r
-            if (redirectToSSL.first) {\r
-#ifdef HAVE_STRCASECMP\r
-                if (!strcasecmp("GET",request.getMethod()) || !strcasecmp("HEAD",request.getMethod())) {\r
-#else\r
-                if (!stricmp("GET",request.getMethod()) || !stricmp("HEAD",request.getMethod())) {\r
-#endif\r
-                    // Compute the new target URL\r
-                    string redirectURL = string("https://") + request.getHostname();\r
-                    if (strcmp(redirectToSSL.second,"443")) {\r
-                        redirectURL = redirectURL + ':' + redirectToSSL.second;\r
-                    }\r
-                    redirectURL += request.getRequestURI();\r
-                    return make_pair(true, request.sendRedirect(redirectURL.c_str()));\r
-                }\r
-                else {\r
-                    TemplateParameters tp;\r
-                    tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));\r
-                    return make_pair(true,sendError(request, app, "ssl", tp));\r
-                }\r
-            }\r
-        }\r
-        \r
-        const char* handlerURL=request.getHandlerURL(targetURL.c_str());\r
-        if (!handlerURL)\r
-            throw ConfigurationException("Cannot determine handler from resource URL, check configuration.");\r
-\r
-        // If the request URL contains the handler base URL for this application, either dispatch\r
-        // directly (mainly Apache 2.0) or just pass back control.\r
-        if (strstr(targetURL.c_str(),handlerURL)) {\r
-            if (handler)\r
-                return doHandler(request);\r
-            else\r
-                return make_pair(true, request.returnOK());\r
-        }\r
-\r
-        // Three settings dictate how to proceed.\r
-        pair<bool,const char*> authType = settings.first->getString("authType");\r
-        pair<bool,bool> requireSession = settings.first->getBool("requireSession");\r
-        pair<bool,const char*> requireSessionWith = settings.first->getString("requireSessionWith");\r
-\r
-        // If no session is required AND the AuthType (an Apache-derived concept) isn't shibboleth,\r
-        // then we ignore this request and consider it unprotected. Apache might lie to us if\r
-        // ShibBasicHijack is on, but that's up to it.\r
-        if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first &&\r
-#ifdef HAVE_STRCASECMP\r
-                (!authType.first || strcasecmp(authType.second,"shibboleth")))\r
-#else\r
-                (!authType.first || _stricmp(authType.second,"shibboleth")))\r
-#endif\r
-            return make_pair(true,request.returnDecline());\r
-\r
-        // Fix for secadv 20050901\r
-        clearHeaders(request);\r
-\r
-        pair<string,const char*> shib_cookie = app->getCookieNameProps("_shibsession_");\r
-        const char* session_id = request.getCookie(shib_cookie.first.c_str());\r
-        if (!session_id || !*session_id) {\r
-            // No session.  Maybe that's acceptable?\r
-            if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first)\r
-                return make_pair(true,request.returnOK());\r
-\r
-            // No cookie, but we require a session. Initiate a new session using the indicated method.\r
-            procState = "Session Initiator Error";\r
-            const Handler* initiator=NULL;\r
-            if (requireSessionWith.first) {\r
-                initiator=app->getSessionInitiatorById(requireSessionWith.second);\r
-                if (!initiator)\r
-                    throw ConfigurationException(\r
-                        "No session initiator found with id ($1), check requireSessionWith command.",\r
-                        params(1,requireSessionWith.second)\r
-                        );\r
-            }\r
-            else {\r
-                initiator=app->getDefaultSessionInitiator();\r
-                if (!initiator)\r
-                    throw ConfigurationException("No default session initiator found, check configuration.");\r
-            }\r
-\r
-            return initiator->run(request,false);\r
-        }\r
-\r
-        procState = "Session Processing Error";\r
-        const Session* session=NULL;\r
-        try {\r
-            session=request.getSession();\r
-            // Make a localized exception throw if the session isn't valid.\r
-            if (!session)\r
-                throw RetryableProfileException("Session no longer valid.");\r
-        }\r
-        catch (exception& e) {\r
-            request.log(SPRequest::SPWarn, string("session processing failed: ") + e.what());\r
-\r
-            // If no session is required, bail now.\r
-            if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first)\r
-                // Has to be OK because DECLINED will just cause Apache\r
-                // to fail when it can't locate anything to process the\r
-                // AuthType.  No session plus requireSession false means\r
-                // do not authenticate the user at this time.\r
-                return make_pair(true, request.returnOK());\r
-\r
-            // Try and cast down.\r
-            exception* base = &e;\r
-            RetryableProfileException* trycast=dynamic_cast<RetryableProfileException*>(base);\r
-            if (trycast) {\r
-                // Session is invalid but we can retry -- initiate a new session.\r
-                procState = "Session Initiator Error";\r
-                const Handler* initiator=NULL;\r
-                if (requireSessionWith.first) {\r
-                    initiator=app->getSessionInitiatorById(requireSessionWith.second);\r
-                    if (!initiator)\r
-                        throw ConfigurationException(\r
-                            "No session initiator found with id ($1), check requireSessionWith command.",\r
-                            params(1,requireSessionWith.second)\r
-                            );\r
-                }\r
-                else {\r
-                    initiator=app->getDefaultSessionInitiator();\r
-                    if (!initiator)\r
-                        throw ConfigurationException("No default session initiator found, check configuration.");\r
-                }\r
-                return initiator->run(request,false);\r
-            }\r
-            throw;    // send it to the outer handler\r
-        }\r
-\r
-        // We're done.  Everything is okay.  Nothing to report.  Nothing to do..\r
-        // Let the caller decide how to proceed.\r
-        request.log(SPRequest::SPDebug, "doAuthentication succeeded");\r
-        return make_pair(false,0);\r
-    }\r
-    catch (XMLToolingException& e) {\r
-        TemplateParameters tp;\r
-        tp.m_map["errorType"] = procState;\r
-        tp.m_map["errorText"] = e.what();\r
-        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));\r
-        return make_pair(true,sendError(request, app, "session", tp, &e));\r
-    }\r
-    catch (exception& e) {\r
-        TemplateParameters tp;\r
-        tp.m_map["errorType"] = procState;\r
-        tp.m_map["errorText"] = e.what();\r
-        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));\r
-        return make_pair(true,sendError(request, app, "session", tp));\r
-    }\r
-#ifndef _DEBUG\r
-    catch (...) {\r
-        TemplateParameters tp;\r
-        tp.m_map["errorType"] = procState;\r
-        tp.m_map["errorText"] = "Caught an unknown exception.";\r
-        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));\r
-        return make_pair(true,sendError(request, app, "session", tp));\r
-    }\r
-#endif\r
+#ifdef _DEBUG
+    xmltooling::NDC ndc("doAuthentication");
+#endif
+
+    const Application* app=NULL;
+    const char* procState = "Request Processing Error";
+    string targetURL = request.getRequestURL();
+
+    try {
+        RequestMapper::Settings settings = request.getRequestSettings();
+        app = &(request.getApplication());
+
+        // If not SSL, check to see if we should block or redirect it.
+        if (!request.isSecure()) {
+            pair<bool,const char*> redirectToSSL = settings.first->getString("redirectToSSL");
+            if (redirectToSSL.first) {
+#ifdef HAVE_STRCASECMP
+                if (!strcasecmp("GET",request.getMethod()) || !strcasecmp("HEAD",request.getMethod())) {
+#else
+                if (!stricmp("GET",request.getMethod()) || !stricmp("HEAD",request.getMethod())) {
+#endif
+                    // Compute the new target URL
+                    string redirectURL = string("https://") + request.getHostname();
+                    if (strcmp(redirectToSSL.second,"443")) {
+                        redirectURL = redirectURL + ':' + redirectToSSL.second;
+                    }
+                    redirectURL += request.getRequestURI();
+                    return make_pair(true, request.sendRedirect(redirectURL.c_str()));
+                }
+                else {
+                    TemplateParameters tp;
+                    tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
+                    return make_pair(true,sendError(request, app, "ssl", tp));
+                }
+            }
+        }
+        
+        const char* handlerURL=request.getHandlerURL(targetURL.c_str());
+        if (!handlerURL)
+            throw ConfigurationException("Cannot determine handler from resource URL, check configuration.");
+
+        // If the request URL contains the handler base URL for this application, either dispatch
+        // directly (mainly Apache 2.0) or just pass back control.
+        if (strstr(targetURL.c_str(),handlerURL)) {
+            if (handler)
+                return doHandler(request);
+            else
+                return make_pair(true, request.returnOK());
+        }
+
+        // Three settings dictate how to proceed.
+        pair<bool,const char*> authType = settings.first->getString("authType");
+        pair<bool,bool> requireSession = settings.first->getBool("requireSession");
+        pair<bool,const char*> requireSessionWith = settings.first->getString("requireSessionWith");
+
+        // If no session is required AND the AuthType (an Apache-derived concept) isn't shibboleth,
+        // then we ignore this request and consider it unprotected. Apache might lie to us if
+        // ShibBasicHijack is on, but that's up to it.
+        if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first &&
+#ifdef HAVE_STRCASECMP
+                (!authType.first || strcasecmp(authType.second,"shibboleth")))
+#else
+                (!authType.first || _stricmp(authType.second,"shibboleth")))
+#endif
+            return make_pair(true,request.returnDecline());
+
+        // Fix for secadv 20050901
+        clearHeaders(request);
+
+        procState = "Session Processing Error";
+
+        Session* session = NULL;
+        try {
+            session = request.getSession();
+        }
+        catch (exception& e) {
+            request.log(SPRequest::SPWarn, string("error during session lookup: ") + e.what());
+            // If it's not a retryable session failure, we throw to the outer handler for reporting.
+            if (dynamic_cast<RetryableProfileException*>(&e)==NULL)
+                throw;
+        }
+
+        if (!session) {
+            // No session.  Maybe that's acceptable?
+            if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first)
+                return make_pair(true,request.returnOK());
+
+            // No session, but we require one. Initiate a new session using the indicated method.
+            procState = "Session Initiator Error";
+            const Handler* initiator=NULL;
+            if (requireSessionWith.first) {
+                initiator=app->getSessionInitiatorById(requireSessionWith.second);
+                if (!initiator)
+                    throw ConfigurationException(
+                        "No session initiator found with id ($1), check requireSessionWith command.",
+                        params(1,requireSessionWith.second)
+                        );
+            }
+            else {
+                initiator=app->getDefaultSessionInitiator();
+                if (!initiator)
+                    throw ConfigurationException("No default session initiator found, check configuration.");
+            }
+
+            return initiator->run(request,false);
+        }
+
+        // We're done.  Everything is okay.  Nothing to report.  Nothing to do..
+        // Let the caller decide how to proceed.
+        request.log(SPRequest::SPDebug, "doAuthentication succeeded");
+        return make_pair(false,0);
+    }
+    catch (XMLToolingException& e) {
+        TemplateParameters tp;
+        tp.m_map["errorType"] = procState;
+        tp.m_map["errorText"] = e.what();
+        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
+        return make_pair(true,sendError(request, app, "session", tp, &e));
+    }
+    catch (exception& e) {
+        TemplateParameters tp;
+        tp.m_map["errorType"] = procState;
+        tp.m_map["errorText"] = e.what();
+        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
+        return make_pair(true,sendError(request, app, "session", tp));
+    }
+#ifndef _DEBUG
+    catch (...) {
+        TemplateParameters tp;
+        tp.m_map["errorType"] = procState;
+        tp.m_map["errorText"] = "Caught an unknown exception.";
+        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
+        return make_pair(true,sendError(request, app, "session", tp));
+    }
+#endif
 }
 
 pair<bool,long> ServiceProvider::doAuthorization(SPRequest& request) const
 {
-#ifdef _DEBUG\r
-    xmltooling::NDC ndc("doAuthorization");\r
-#endif\r
-\r
-    const Application* app=NULL;\r
-    const char* procState = "Authorization Processing Error";\r
-    string targetURL = request.getRequestURL();\r
-\r
-    try {\r
-        RequestMapper::Settings settings = request.getRequestSettings();\r
-        app = &(request.getApplication());\r
-\r
-        // Three settings dictate how to proceed.\r
-        pair<bool,const char*> authType = settings.first->getString("authType");\r
-        pair<bool,bool> requireSession = settings.first->getBool("requireSession");\r
-        pair<bool,const char*> requireSessionWith = settings.first->getString("requireSessionWith");\r
-\r
-        // If no session is required AND the AuthType (an Apache-derived concept) isn't shibboleth,\r
-        // then we ignore this request and consider it unprotected. Apache might lie to us if\r
-        // ShibBasicHijack is on, but that's up to it.\r
-        if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first &&\r
-#ifdef HAVE_STRCASECMP\r
-                (!authType.first || strcasecmp(authType.second,"shibboleth")))\r
-#else\r
-                (!authType.first || _stricmp(authType.second,"shibboleth")))\r
-#endif\r
-            return make_pair(true,request.returnDecline());\r
-\r
-        // Do we have an access control plugin?\r
-        if (settings.second) {\r
-            const Session* session =NULL;\r
-               pair<string,const char*> shib_cookie=app->getCookieNameProps("_shibsession_");\r
-            const char *session_id = request.getCookie(shib_cookie.first.c_str());\r
-            try {\r
-                       if (session_id && *session_id) {\r
-                    session = request.getSession();\r
-                       }\r
-            }\r
-            catch (exception&) {\r
-                request.log(SPRequest::SPWarn, "unable to obtain session information to pass to access control provider");\r
-            }\r
-       \r
-            Locker acllock(settings.second);\r
-            if (settings.second->authorized(request,session)) {\r
-                // Let the caller decide how to proceed.\r
-                request.log(SPRequest::SPDebug, "access control provider granted access");\r
-                return make_pair(false,0);\r
-            }\r
-            else {\r
-                request.log(SPRequest::SPWarn, "access control provider denied access");\r
-                TemplateParameters tp;\r
-                tp.m_map["requestURL"] = targetURL;\r
-                return make_pair(true,sendError(request, app, "access", tp));\r
-            }\r
-            return make_pair(false,0);\r
-        }\r
-        else\r
-            return make_pair(true,request.returnDecline());\r
-    }\r
+#ifdef _DEBUG
+    xmltooling::NDC ndc("doAuthorization");
+#endif
+
+    const Application* app=NULL;
+    const char* procState = "Authorization Processing Error";
+    string targetURL = request.getRequestURL();
+
+    try {
+        RequestMapper::Settings settings = request.getRequestSettings();
+        app = &(request.getApplication());
+
+        // Three settings dictate how to proceed.
+        pair<bool,const char*> authType = settings.first->getString("authType");
+        pair<bool,bool> requireSession = settings.first->getBool("requireSession");
+        pair<bool,const char*> requireSessionWith = settings.first->getString("requireSessionWith");
+
+        // If no session is required AND the AuthType (an Apache-derived concept) isn't shibboleth,
+        // then we ignore this request and consider it unprotected. Apache might lie to us if
+        // ShibBasicHijack is on, but that's up to it.
+        if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first &&
+#ifdef HAVE_STRCASECMP
+                (!authType.first || strcasecmp(authType.second,"shibboleth")))
+#else
+                (!authType.first || _stricmp(authType.second,"shibboleth")))
+#endif
+            return make_pair(true,request.returnDecline());
+
+        // Do we have an access control plugin?
+        if (settings.second) {
+            const Session* session = NULL;
+            try {
+                session = request.getSession();
+            }
+            catch (exception& e) {
+                request.log(SPRequest::SPWarn, string("unable to obtain session to pass to access control provider: ") + e.what());
+            }
+       
+            Locker acllock(settings.second);
+            if (settings.second->authorized(request,session)) {
+                // Let the caller decide how to proceed.
+                request.log(SPRequest::SPDebug, "access control provider granted access");
+                return make_pair(false,0);
+            }
+            else {
+                request.log(SPRequest::SPWarn, "access control provider denied access");
+                TemplateParameters tp;
+                tp.m_map["requestURL"] = targetURL;
+                return make_pair(true,sendError(request, app, "access", tp));
+            }
+            return make_pair(false,0);
+        }
+        else
+            return make_pair(true,request.returnDecline());
+    }
     catch (XMLToolingException& e) {
         TemplateParameters tp;
         tp.m_map["errorType"] = procState;
@@ -352,169 +316,117 @@ pair<bool,long> ServiceProvider::doAuthorization(SPRequest& request) const
         tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
         return make_pair(true,sendError(request, app, "session", tp, &e));
     }
-    catch (exception& e) {\r
-        TemplateParameters tp;\r
-        tp.m_map["errorType"] = procState;\r
-        tp.m_map["errorText"] = e.what();\r
-        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));\r
-        return make_pair(true,sendError(request, app, "access", tp));\r
-    }\r
-#ifndef _DEBUG\r
-    catch (...) {\r
-        TemplateParameters tp;\r
-        tp.m_map["errorType"] = procState;\r
-        tp.m_map["errorText"] = "Caught an unknown exception.";\r
-        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));\r
-        return make_pair(true,sendError(request, app, "access", tp));\r
-    }\r
-#endif\r
+    catch (exception& e) {
+        TemplateParameters tp;
+        tp.m_map["errorType"] = procState;
+        tp.m_map["errorText"] = e.what();
+        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
+        return make_pair(true,sendError(request, app, "access", tp));
+    }
+#ifndef _DEBUG
+    catch (...) {
+        TemplateParameters tp;
+        tp.m_map["errorType"] = procState;
+        tp.m_map["errorText"] = "Caught an unknown exception.";
+        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
+        return make_pair(true,sendError(request, app, "access", tp));
+    }
+#endif
 }
 
 pair<bool,long> ServiceProvider::doExport(SPRequest& request, bool requireSession) const
 {
-#ifdef _DEBUG\r
-    xmltooling::NDC ndc("doExport");\r
-#endif\r
-\r
-    const Application* app=NULL;\r
-    const char* procState = "Attribute Processing Error";\r
-    string targetURL = request.getRequestURL();\r
-\r
-    try {\r
+#ifdef _DEBUG
+    xmltooling::NDC ndc("doExport");
+#endif
+
+    const Application* app=NULL;
+    const char* procState = "Attribute Processing Error";
+    string targetURL = request.getRequestURL();
+
+    try {
         RequestMapper::Settings settings = request.getRequestSettings();
         app = &(request.getApplication());
-\r
-        const Session* session=NULL;\r
-        pair<string,const char*> shib_cookie=app->getCookieNameProps("_shibsession_");\r
-        const char *session_id = request.getCookie(shib_cookie.first.c_str());\r
-        try {\r
-               if (session_id && *session_id) {\r
-                session = request.getSession();\r
-               }\r
-        }\r
-        catch (exception&) {\r
-            request.log(SPRequest::SPWarn, "unable to obtain session information to export into request headers");\r
-               // If we have to have a session, then this is a fatal error.\r
-               if (requireSession)\r
-                       throw;\r
-        }\r
-\r
-               // Still no data?\r
-        if (!session) {\r
-               if (requireSession)\r
-                       throw RetryableProfileException("Unable to obtain session information for request.");\r
-               else\r
-                       return make_pair(false,0);      // just bail silently\r
-        }\r
-        \r
-        /*\r
-        TODO: port to new cache API\r
-        // Extract data from session.\r
-        pair<const char*,const SAMLSubject*> sub=m_cacheEntry->getSubject(false,true);\r
-        pair<const char*,const SAMLResponse*> unfiltered=m_cacheEntry->getTokens(true,false);\r
-        pair<const char*,const SAMLResponse*> filtered=m_cacheEntry->getTokens(false,true);\r
-\r
-        // Maybe export the tokens.\r
-        pair<bool,bool> exp=m_settings.first->getBool("exportAssertion");\r
-        if (exp.first && exp.second && unfiltered.first && *unfiltered.first) {\r
-            unsigned int outlen;\r
-            XMLByte* serialized =\r
-                Base64::encode(reinterpret_cast<XMLByte*>((char*)unfiltered.first), XMLString::stringLen(unfiltered.first), &outlen);\r
-            XMLByte *pos, *pos2;\r
-            for (pos=serialized, pos2=serialized; *pos2; pos2++)\r
-                if (isgraph(*pos2))\r
-                    *pos++=*pos2;\r
-            *pos=0;\r
-            setHeader("Shib-Attributes", reinterpret_cast<char*>(serialized));\r
-            XMLString::release(&serialized);\r
-        }\r
-\r
-        // Export the SAML AuthnMethod and the origin site name, and possibly the NameIdentifier.\r
-        setHeader("Shib-Origin-Site", m_cacheEntry->getProviderId());\r
-        setHeader("Shib-Identity-Provider", m_cacheEntry->getProviderId());\r
-        setHeader("Shib-Authentication-Method", m_cacheEntry->getAuthnContext());\r
-        \r
-        // Get the AAP providers, which contain the attribute policy info.\r
-        Iterator<IAAP*> provs=m_app->getAAPProviders();\r
-\r
-        // Export NameID?\r
-        while (provs.hasNext()) {\r
-            IAAP* aap=provs.next();\r
-            xmltooling::Locker locker(aap);\r
-            const XMLCh* format = sub.second->getNameIdentifier()->getFormat();\r
-            const IAttributeRule* rule=aap->lookup(format ? format : SAMLNameIdentifier::UNSPECIFIED);\r
-            if (rule && rule->getHeader()) {\r
-                auto_ptr_char form(format ? format : SAMLNameIdentifier::UNSPECIFIED);\r
-                auto_ptr_char nameid(sub.second->getNameIdentifier()->getName());\r
-                setHeader("Shib-NameIdentifier-Format", form.get());\r
-                if (!strcmp(rule->getHeader(),"REMOTE_USER"))\r
-                    setRemoteUser(nameid.get());\r
-                else\r
-                    setHeader(rule->getHeader(), nameid.get());\r
-            }\r
-        }\r
-        \r
-        setHeader("Shib-Application-ID", m_app->getId());\r
-    \r
-        // Export the attributes.\r
-        Iterator<SAMLAssertion*> a_iter(filtered.second ? filtered.second->getAssertions() : EMPTY(SAMLAssertion*));\r
-        while (a_iter.hasNext()) {\r
-            SAMLAssertion* assert=a_iter.next();\r
-            Iterator<SAMLStatement*> statements=assert->getStatements();\r
-            while (statements.hasNext()) {\r
-                SAMLAttributeStatement* astate=dynamic_cast<SAMLAttributeStatement*>(statements.next());\r
-                if (!astate)\r
-                    continue;\r
-                Iterator<SAMLAttribute*> attrs=astate->getAttributes();\r
-                while (attrs.hasNext()) {\r
-                    SAMLAttribute* attr=attrs.next();\r
-            \r
-                    // Are we supposed to export it?\r
-                    provs.reset();\r
-                    while (provs.hasNext()) {\r
-                        IAAP* aap=provs.next();\r
-                        xmltooling::Locker locker(aap);\r
-                        const IAttributeRule* rule=aap->lookup(attr->getName(),attr->getNamespace());\r
-                        if (!rule || !rule->getHeader())\r
-                            continue;\r
-                    \r
-                        Iterator<string> vals=attr->getSingleByteValues();\r
-                        if (!strcmp(rule->getHeader(),"REMOTE_USER") && vals.hasNext())\r
-                            setRemoteUser(vals.next().c_str());\r
-                        else {\r
-                            int it=0;\r
-                            string header = getSecureHeader(rule->getHeader());\r
-                            if (!header.empty())\r
-                                it++;\r
-                            for (; vals.hasNext(); it++) {\r
-                                string value = vals.next();\r
-                                for (string::size_type pos = value.find_first_of(";", string::size_type(0));\r
-                                        pos != string::npos;\r
-                                        pos = value.find_first_of(";", pos)) {\r
-                                    value.insert(pos, "\\");\r
-                                    pos += 2;\r
-                                }\r
-                                if (it)\r
-                                    header += ";";\r
-                                header += value;\r
-                            }\r
-                            setHeader(rule->getHeader(), header.c_str());\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-        }\r
-        */\r
-    \r
-        return make_pair(false,0);\r
-    }\r
-    catch (XMLToolingException& e) {\r
+
+        const Session* session = NULL;
+        try {
+            session = request.getSession();
+        }
+        catch (exception& e) {
+            request.log(SPRequest::SPWarn, string("unable to obtain session to export to request: ") +  e.what());
+               // If we have to have a session, then this is a fatal error.
+               if (requireSession)
+                       throw;
+        }
+
+               // Still no data?
+        if (!session) {
+               if (requireSession)
+                       throw RetryableProfileException("Unable to obtain session to export to request.");
+               else
+                       return make_pair(false,0);      // just bail silently
+        }
+        
+        request.setHeader("Shib-Application-ID", app->getId());
+
+        // Export the IdP name and Authn method/context info.
+        const char* hval = session->getEntityID();
+        if (hval)
+            request.setHeader("Shib-Identity-Provider", hval);
+        hval = session->getAuthnContextClassRef();
+        if (hval) {
+            request.setHeader("Shib-Authentication-Method", hval);
+            request.setHeader("Shib-AuthnContext-Class", hval);
+        }
+        hval = session->getAuthnContextDeclRef();
+        if (hval)
+            request.setHeader("Shib-AuthnContext-Decl", hval);
+        
+        // Maybe export the assertion keys.
+        pair<bool,bool> exp=settings.first->getBool("exportAssertion");
+        if (exp.first && exp.second) {
+            //setHeader("Shib-Attributes", reinterpret_cast<char*>(serialized));
+            // TODO: export lookup URLs to access assertions by ID
+            const vector<const char*>& tokens = session->getAssertionIDs();
+        }
+
+        // Export the attributes.
+        const map<string,const Attribute*>& attributes = session->getAttributes();
+        for (map<string,const Attribute*>::const_iterator a = attributes.begin(); a!=attributes.end(); ++a) {
+            const vector<string>& vals = a->second->getSerializedValues();
+            if (!strcmp(a->second->getId(), "REMOTE_USER") && !vals.empty())
+                request.setRemoteUser(vals.front().c_str());
+            else {
+                string header(request.getSecureHeader(a->second->getId()));
+                for (vector<string>::const_iterator v = vals.begin(); v!=vals.end(); ++v) {
+                    if (!header.empty())
+                        header += ";";
+                    string::size_type pos = v->find_first_of(';',string::size_type(0));
+                    if (pos!=string::npos) {
+                        string value(*v);
+                        for (; pos != string::npos; pos = value.find_first_of(';',pos)) {
+                            value.insert(pos, "\\");
+                            pos += 2;
+                        }
+                        header += value;
+                    }
+                    else {
+                        header += (*v);
+                    }
+                }
+                request.setHeader(a->second->getId(), header.c_str());
+            }
+        }
+    
+        return make_pair(false,0);
+    }
+    catch (XMLToolingException& e) {
         TemplateParameters tp;
-        tp.m_map["errorType"] = procState;\r
-        tp.m_map["errorText"] = e.what();\r
-        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));\r
-        return make_pair(true,sendError(request, app, "rm", tp, &e));\r
-    }\r
+        tp.m_map["errorType"] = procState;
+        tp.m_map["errorText"] = e.what();
+        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
+        return make_pair(true,sendError(request, app, "rm", tp, &e));
+    }
     catch (exception& e) {
         TemplateParameters tp;
         tp.m_map["errorType"] = procState;
@@ -522,15 +434,15 @@ pair<bool,long> ServiceProvider::doExport(SPRequest& request, bool requireSessio
         tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
         return make_pair(true,sendError(request, app, "rm", tp));
     }
-#ifndef _DEBUG\r
-    catch (...) {\r
+#ifndef _DEBUG
+    catch (...) {
         TemplateParameters tp;
-        tp.m_map["errorType"] = procState;\r
-        tp.m_map["errorText"] = "Caught an unknown exception.";\r
-        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));\r
-        return make_pair(true,sendError(request, app, "rm", tp));\r
-    }\r
-#endif\r
+        tp.m_map["errorType"] = procState;
+        tp.m_map["errorText"] = "Caught an unknown exception.";
+        tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?'));
+        return make_pair(true,sendError(request, app, "rm", tp));
+    }
+#endif
 }
 
 pair<bool,long> ServiceProvider::doHandler(SPRequest& request) const
@@ -563,7 +475,7 @@ pair<bool,long> ServiceProvider::doHandler(SPRequest& request) const
         pair<bool,bool> handlerSSL=sessionProps->getBool("handlerSSL");
       
         // Make sure this is SSL, if it should be
-        if ((!handlerSSL.first || handlerSSL.second) && strcmp(request.getScheme(),"https"))
+        if ((!handlerSSL.first || handlerSSL.second) && !request.isSecure())
             throw FatalProfileException("Blocked non-SSL access to Shibboleth handler.");
 
         // We dispatch based on our path info. We know the request URL begins with or equals the handler URL,
index a3a1925..46195d2 100644 (file)
@@ -94,11 +94,11 @@ namespace shibsp {
         virtual const char* getAuthnContextDeclRef() const=0;
         
         /**
-         * Returns the set of resolved attributes associated with the session.
+         * Returns the resolved attributes associated with the session.
          * 
-         * @return an immutable array of attributes
+         * @return an immutable map of attributes keyed by attribute ID
          */
-        virtual const std::vector<const Attribute*>& getAttributes() const=0;
+        virtual const std::map<std::string,const Attribute*>& getAttributes() const=0;
         
         /**
          * Adds additional attributes to the session.
index 3171cc6..3a23ca5 100644 (file)
@@ -98,6 +98,15 @@ namespace shibsp {
         const char* getId() const {
             return m_id.c_str();
         }
+
+        /**
+         * Indicates whether case sensitivity should apply to basic value comparisons.
+         *
+         * @return  true iff value comparisons should be case sensitive
+         */
+        virtual bool isCaseSensitive() const {
+            return true;
+        }
         
         /**
          * Returns the number of values.
index cf2247f..2156f2a 100644 (file)
@@ -50,14 +50,14 @@ namespace shibsp {
          * 
          * @param id    Attribute identifier
          */
-        ScopedAttribute(const char* id) : Attribute(id) {}
+        ScopedAttribute(const char* id) : Attribute(id), m_caseSensitive(true) {}
 
         /**
          * Constructs based on a remoted ScopedAttribute.
          * 
          * @param in    input object containing marshalled ScopedAttribute
          */
-        ScopedAttribute(DDF& in) : Attribute(in) {
+        ScopedAttribute(DDF& in) : Attribute(in), m_caseSensitive(in["case_insensitive"].isnull()) {
             DDF val = in.first().first();
             while (val.name() && val.string()) {
                 m_values.push_back(std::make_pair(val.name(), val.string()));
@@ -66,7 +66,11 @@ namespace shibsp {
         }
         
         virtual ~ScopedAttribute() {}
-        
+
+        bool isCaseSensitive() const {
+            return m_caseSensitive;
+        }
+
         /**
          * Returns the set of values encoded as UTF-8 strings.
          * 
@@ -97,6 +101,8 @@ namespace shibsp {
         DDF marshall() const {
             DDF ddf = Attribute::marshall();
             ddf.name("scoped");
+            if (!m_caseSensitive)
+                ddf.addmember("case_insensitive");
             DDF vlist = ddf.first();
             for (std::vector< std::pair<std::string,std::string> >::const_iterator i=m_values.begin(); i!=m_values.end(); ++i) {
                 DDF val = DDF(i->first.c_str()).string(i->second.c_str());
@@ -106,6 +112,7 @@ namespace shibsp {
         }
     
     private:
+        bool m_caseSensitive;
         std::vector< std::pair<std::string,std::string> > m_values;
     };
 
index 8e8f2c6..7420b6f 100644 (file)
@@ -38,14 +38,14 @@ namespace shibsp {
          * 
          * @param id    Attribute identifier
          */
-        SimpleAttribute(const char* id) : Attribute(id) {}
+        SimpleAttribute(const char* id) : Attribute(id), m_caseSensitive(true) {}
 
         /**
          * Constructs based on a remoted SimpleAttribute.
          * 
          * @param in    input object containing marshalled SimpleAttribute
          */
-        SimpleAttribute(DDF& in) : Attribute(in) {
+        SimpleAttribute(DDF& in) : Attribute(in), m_caseSensitive(in["case_insensitive"].isnull()) {
             DDF val = in.first().first();
             while (val.string()) {
                 m_serialized.push_back(val.string());
@@ -54,7 +54,11 @@ namespace shibsp {
         }
         
         virtual ~SimpleAttribute() {}
-        
+
+        bool isCaseSensitive() const {
+            return m_caseSensitive;
+        }
+
         /**
          * Returns the set of values encoded as UTF-8 strings.
          * 
@@ -73,11 +77,16 @@ namespace shibsp {
         
         DDF marshall() const {
             DDF ddf = Attribute::marshall();
+            if (!m_caseSensitive)
+                ddf.addmember("case_insensitive");
             DDF vlist = ddf.first();
             for (std::vector<std::string>::const_iterator i=m_serialized.begin(); i!=m_serialized.end(); ++i)
                 vlist.add(DDF(NULL).string(i->c_str()));
             return ddf;
         }
+
+    private:
+        bool m_caseSensitive;
     };
 
 };
index 95e33c7..fadb7ce 100644 (file)
@@ -63,15 +63,6 @@ namespace shibsp {
             n->unmarshall(doc->getDocumentElement(), true);\r
             janitor.release();\r
             \r
-            try {\r
-                DDF attrs = m_obj["attributes"];\r
-                unmarshallAttributes(attrs);\r
-            }\r
-            catch (...) {\r
-                for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
-                throw;\r
-            }\r
-\r
             auto_ptr_XMLCh exp(m_obj["expires"].string());\r
             if (exp.get()) {\r
                 DateTime iso(exp.get());\r
@@ -79,16 +70,16 @@ namespace shibsp {
                 m_expires = iso.getEpoch();\r
             }\r
 \r
-            m_nameid = n.release();\r
             m_lock = Mutex::create();\r
+            m_nameid = n.release();\r
         }\r
         \r
         ~RemotedSession() {\r
             delete m_lock;\r
             m_obj.destroy();\r
             delete m_nameid;\r
-            for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
-            for_each(m_tokens.begin(), m_tokens.end(), xmltooling::cleanup_pair<string,RootObject>());\r
+            for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair<string,Attribute>());\r
+            for_each(m_tokens.begin(), m_tokens.end(), cleanup_pair<string,RootObject>());\r
         }\r
         \r
         Lockable* lock() {\r
@@ -123,7 +114,9 @@ namespace shibsp {
         const char* getAuthnContextDeclRef() const {\r
             return m_obj["authncontext_decl"].string();\r
         }\r
-        const vector<const Attribute*>& getAttributes() const {\r
+        const map<string,const Attribute*>& getAttributes() const {\r
+            if (m_attributes.empty())\r
+                unmarshallAttributes();\r
             return m_attributes;\r
         }\r
         const vector<const char*>& getAssertionIDs() const {\r
@@ -151,12 +144,12 @@ namespace shibsp {
         void validate(const Application& application, const char* client_addr, time_t timeout, bool local=true);\r
 \r
     private:\r
-        void unmarshallAttributes(DDF& in);\r
+        void unmarshallAttributes() const;\r
 \r
         int m_version;\r
         mutable DDF m_obj;\r
         saml2::NameID* m_nameid;\r
-        vector<const Attribute*> m_attributes;\r
+        mutable map<string,const Attribute*> m_attributes;\r
         mutable vector<const char*> m_ids;\r
         mutable map<string,RootObject*> m_tokens;\r
         time_t m_expires,m_lastAccess;\r
@@ -207,15 +200,17 @@ namespace shibsp {
     }\r
 }\r
 \r
-void RemotedSession::unmarshallAttributes(DDF& in)\r
+void RemotedSession::unmarshallAttributes() const\r
 {\r
-    DDF attr = in.first();\r
+    Attribute* attribute;\r
+    DDF attr = m_obj["attributes"].first();\r
     while (!attr.isnull()) {\r
         try {\r
-            m_attributes.push_back(Attribute::unmarshall(attr));\r
+            attribute = Attribute::unmarshall(attr);\r
+            m_attributes[attribute->getId()] = attribute;\r
             if (m_cache->m_log.isDebugEnabled())\r
                 m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s",\r
-                    attr.first().name(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");\r
+                    attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");\r
         }\r
         catch (AttributeException& ex) {\r
             const char* id = attr.first().name();\r
@@ -322,12 +317,10 @@ void RemotedSession::validate(const Application& application, const char* client
     if (out.isstruct()) {\r
         // We got an updated record back.\r
         m_ids.clear();\r
-        for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
+        for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair<string,Attribute>());\r
         m_attributes.clear();\r
         m_obj.destroy();\r
         m_obj = out;\r
-        DDF attrs = m_obj["attributes"];\r
-        unmarshallAttributes(attrs);\r
     }\r
 \r
     m_lastAccess = now;\r
@@ -424,8 +417,6 @@ string RemotedCache::insert(
     DDF out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);\r
     DDFJanitor jout(out);\r
     if (out["key"].isstring()) {\r
-        for_each(attributes->begin(), attributes->end(), xmltooling::cleanup<Attribute>());\r
-\r
         // Transaction Logging\r
         auto_ptr_char name(nameid.getName());\r
         const char* pid = in["entity_id"].string();\r
@@ -444,6 +435,19 @@ string RemotedCache::insert(
                 name.get() <<\r
             ")";\r
 \r
+        if (attributes) {\r
+            xlog->log.infoStream() <<\r
+                "Cached the following attributes with session (ID: " <<\r
+                    out["key"].string() <<\r
+                ") for (applicationId: " <<\r
+                    application.getId() <<\r
+                ") {";\r
+            for (vector<Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a)\r
+                xlog->log.infoStream() << "\t" << (*a)->getId() << " (" << (*a)->valueCount() << " values)";\r
+            xlog->log.info("}");\r
+            for_each(attributes->begin(), attributes->end(), xmltooling::cleanup<Attribute>());\r
+        }\r
+\r
         return out["key"].string();\r
     }\r
     throw RetryableProfileException("A remoted cache insertion operation did not return a usable session key.");\r
index 7c1421f..fe53c1c 100644 (file)
@@ -70,16 +70,6 @@ namespace shibsp {
             auto_ptr<saml2::NameID> n(saml2::NameIDBuilder::buildNameID());\r
             n->unmarshall(doc->getDocumentElement(), true);\r
             janitor.release();\r
-            \r
-            try {\r
-                DDF attrs = m_obj["attributes"];\r
-                unmarshallAttributes(attrs);\r
-            }\r
-            catch (...) {\r
-                for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
-                throw;\r
-            }\r
-\r
             m_nameid = n.release();\r
         }\r
         \r
@@ -113,7 +103,9 @@ namespace shibsp {
         const char* getAuthnContextDeclRef() const {\r
             return m_obj["authncontext_decl"].string();\r
         }\r
-        const vector<const Attribute*>& getAttributes() const {\r
+        const map<string,const Attribute*>& getAttributes() const {\r
+            if (m_attributes.empty())\r
+                unmarshallAttributes();\r
             return m_attributes;\r
         }\r
         const vector<const char*>& getAssertionIDs() const {\r
@@ -132,11 +124,11 @@ namespace shibsp {
         void addAssertion(RootObject* assertion);\r
 \r
     private:\r
-        void unmarshallAttributes(DDF& in);\r
+        void unmarshallAttributes() const;\r
 \r
         DDF m_obj;\r
         saml2::NameID* m_nameid;\r
-        vector<const Attribute*> m_attributes;\r
+        mutable map<string,const Attribute*> m_attributes;\r
         mutable vector<const char*> m_ids;\r
         mutable map<string,RootObject*> m_tokens;\r
         SSCache* m_cache;\r
@@ -182,19 +174,21 @@ StoredSession::~StoredSession()
 {\r
     m_obj.destroy();\r
     delete m_nameid;\r
-    for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
-    for_each(m_tokens.begin(), m_tokens.end(), xmltooling::cleanup_pair<string,RootObject>());\r
+    for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair<string,Attribute>());\r
+    for_each(m_tokens.begin(), m_tokens.end(), cleanup_pair<string,RootObject>());\r
 }\r
 \r
-void StoredSession::unmarshallAttributes(DDF& in)\r
+void StoredSession::unmarshallAttributes() const\r
 {\r
-    DDF attr = in.first();\r
+    Attribute* attribute;\r
+    DDF attr = m_obj["attributes"].first();\r
     while (!attr.isnull()) {\r
         try {\r
-            m_attributes.push_back(Attribute::unmarshall(attr));\r
+            attribute = Attribute::unmarshall(attr);\r
+            m_attributes[attribute->getId()] = attribute;\r
             if (m_cache->m_log.isDebugEnabled())\r
                 m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s",\r
-                    attr.first().name(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");\r
+                    attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");\r
         }\r
         catch (AttributeException& ex) {\r
             const char* id = attr.first().name();\r
@@ -268,21 +262,16 @@ void StoredSession::addAttributes(const vector<Attribute*>& attributes)
             in >> newobj;\r
 \r
             m_ids.clear();\r
-            for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
+            for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair<string,Attribute>());\r
             m_attributes.clear();\r
             newobj["version"].integer(ver);\r
             m_obj.destroy();\r
             m_obj = newobj;\r
-            DDF attrs = m_obj["attributes"];\r
-            unmarshallAttributes(attrs);\r
 \r
             ver = -1;\r
         }\r
     } while (ver < 0);  // negative indicates a sync issue so we retry\r
 \r
-    // Transfer ownership to us.\r
-    m_attributes.insert(m_attributes.end(), attributes.begin(), attributes.end());\r
-\r
     TransactionLog* xlog = SPConfig::getConfig().getServiceProvider()->getTransactionLog();\r
     Locker locker(xlog);\r
     xlog->log.infoStream() <<\r
@@ -294,6 +283,9 @@ void StoredSession::addAttributes(const vector<Attribute*>& attributes)
     for (vector<Attribute*>::const_iterator a=attributes.begin(); a!=attributes.end(); ++a)\r
         xlog->log.infoStream() << "\t" << (*a)->getId() << " (" << (*a)->valueCount() << " values)";\r
     xlog->log.info("}");\r
+\r
+    // We own them now, so clean them up.\r
+    for_each(attributes.begin(), attributes.end(), xmltooling::cleanup<Attribute>());\r
 }\r
 \r
 const RootObject* StoredSession::getAssertion(const char* id) const\r
@@ -392,13 +384,11 @@ void StoredSession::addAssertion(RootObject* assertion)
             in >> newobj;\r
 \r
             m_ids.clear();\r
-            for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
+            for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair<string,Attribute>());\r
             m_attributes.clear();\r
             newobj["version"].integer(ver);\r
             m_obj.destroy();\r
             m_obj = newobj;\r
-            DDF attrs = m_obj["attributes"];\r
-            unmarshallAttributes(attrs);\r
             \r
             ver = -1;\r
         }\r
@@ -550,6 +540,19 @@ string SSCache::insert(
             name.get() <<\r
         ")";\r
     \r
+    if (attributes) {\r
+        xlog->log.infoStream() <<\r
+            "Cached the following attributes with session (ID: " <<\r
+                key.get() <<\r
+            ") for (applicationId: " <<\r
+                application.getId() <<\r
+            ") {";\r
+        for (vector<Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a)\r
+            xlog->log.infoStream() << "\t" << (*a)->getId() << " (" << (*a)->valueCount() << " values)";\r
+        xlog->log.info("}");\r
+        for_each(attributes->begin(), attributes->end(), xmltooling::cleanup<Attribute>());\r
+    }\r
+\r
     return key.get();\r
 }\r
 \r
index 6d9e468..6058eff 100644 (file)
@@ -24,6 +24,8 @@
 #include "exceptions.h"\r
 #include "AccessControl.h"\r
 #include "SessionCache.h"\r
+#include "SPRequest.h"\r
+#include "attribute/Attribute.h"\r
 \r
 #include <xmltooling/util/ReloadableXMLFile.h>\r
 #include <xmltooling/util/XMLHelper.h>\r
@@ -144,55 +146,36 @@ Rule::Rule(const DOMElement* e)
 \r
 bool Rule::authorized(const SPRequest& request, const Session* session) const\r
 {\r
-    /*\r
-    TODO: port...\r
+    // We can make this more complex later using pluggable comparison functions,\r
+    // but for now, just a straight port to the new Attribute API.\r
+\r
     // Map alias in rule to the attribute.\r
-    Iterator<IAAP*> provs=st->getApplication()->getAAPProviders();\r
-    AAP wrapper(provs,m_alias.c_str());\r
-    if (wrapper.fail()) {\r
-        st->log(ShibTarget::LogLevelWarn, string("AccessControl plugin didn't recognize rule (") + m_alias + "), check AAP for corresponding Alias");\r
+    if (!session) {\r
+        request.log(SPRequest::SPWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");\r
         return false;\r
     }\r
-    else if (!entry) {\r
-        st->log(ShibTarget::LogLevelWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");\r
+    \r
+    // Find the attribute matching the require rule.\r
+    map<string,const Attribute*>::const_iterator attr = session->getAttributes().find(m_alias);\r
+    if (attr == session->getAttributes().end()) {\r
+        request.log(SPRequest::SPWarn, string("rule requires attribute (") + m_alias + "), not found in session");\r
         return false;\r
     }\r
-    \r
-    // Find the corresponding attribute. This isn't very efficient...\r
-    pair<const char*,const SAMLResponse*> filtered=entry->getFilteredTokens(false,true);\r
-    Iterator<SAMLAssertion*> a_iter(filtered.second ? filtered.second->getAssertions() : EMPTY(SAMLAssertion*));\r
-    while (a_iter.hasNext()) {\r
-        SAMLAssertion* assert=a_iter.next();\r
-        Iterator<SAMLStatement*> statements=assert->getStatements();\r
-        while (statements.hasNext()) {\r
-            SAMLAttributeStatement* astate=dynamic_cast<SAMLAttributeStatement*>(statements.next());\r
-            if (!astate)\r
-                continue;\r
-            Iterator<SAMLAttribute*> attrs=astate->getAttributes();\r
-            while (attrs.hasNext()) {\r
-                SAMLAttribute* attr=attrs.next();\r
-                if (!XMLString::compareString(attr->getName(),wrapper->getName()) &&\r
-                    !XMLString::compareString(attr->getNamespace(),wrapper->getNamespace())) {\r
-                    // Now we have to intersect the attribute's values against the rule's list.\r
-                    Iterator<string> vals=attr->getSingleByteValues();\r
-                    if (!vals.hasNext())\r
-                        return false;\r
-                    for (vector<string>::const_iterator ival=m_vals.begin(); ival!=m_vals.end(); ival++) {\r
-                        vals.reset();\r
-                        while (vals.hasNext()) {\r
-                            const string& v=vals.next();\r
-                            if ((wrapper->getCaseSensitive() && v == *ival) || (!wrapper->getCaseSensitive() && !strcasecmp(v.c_str(),ival->c_str()))) {\r
-                                st->log(ShibTarget::LogLevelDebug, string("XMLAccessControl plugin expecting " + *ival + ", authz granted"));\r
-                                return true;\r
-                            }\r
-                        }\r
-                    }\r
-                }\r
+\r
+    bool caseSensitive = attr->second->isCaseSensitive();\r
+\r
+    // Now we have to intersect the attribute's values against the rule's list.\r
+    const vector<string>& vals = attr->second->getSerializedValues();\r
+    for (vector<string>::const_iterator i=m_vals.begin(); i!=m_vals.end(); ++i) {\r
+        for (vector<string>::const_iterator j=vals.begin(); j!=vals.end(); ++j) {\r
+            if ((caseSensitive && *i == *j) || (!caseSensitive && !strcasecmp(i->c_str(),j->c_str()))) {\r
+                request.log(SPRequest::SPDebug, string("AccessControl plugin expecting ") + *j + ", authz granted");\r
+                return true;\r
             }\r
         }\r
     }\r
-    */\r
-    return true;\r
+\r
+    return false;\r
 }\r
 \r
 Operator::Operator(const DOMElement* e)\r
@@ -260,7 +243,7 @@ bool Operator::authorized(const SPRequest& request, const Session* session) cons
             return false;\r
         }\r
     }\r
-    //st->log(ShibTarget::LogLevelWarn,"Unknown operation in access control policy, denying access");\r
+    request.log(SPRequest::SPWarn,"unknown operation in access control policy, denying access");\r
     return false;\r
 }\r
 \r