From 893153531f13cba410524ec5f32955eaa2f98bfd Mon Sep 17 00:00:00 2001 From: cantor Date: Mon, 5 Feb 2007 01:49:05 +0000 Subject: [PATCH 1/1] Attribute lookup, port ACL code and mainline SP code to Session/Attribute API. git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2155 cb58f699-b61c-0410-a6fe-9272a202ed29 --- apache/mod_apache.cpp | 105 ++--- apache/mod_shib13.vcproj | 12 + apache/mod_shib20.vcproj | 12 + apache/mod_shib22.vcproj | 12 + isapi_shib/isapi_shib.cpp | 3 - nsapi_shib/nsapi_shib.cpp | 3 - shibsp/AbstractSPRequest.cpp | 36 +- shibsp/AbstractSPRequest.h | 5 +- shibsp/SPRequest.h | 2 +- shibsp/ServiceProvider.cpp | 708 +++++++++++++---------------- shibsp/SessionCache.h | 6 +- shibsp/attribute/Attribute.h | 9 + shibsp/attribute/ScopedAttribute.h | 13 +- shibsp/attribute/SimpleAttribute.h | 15 +- shibsp/impl/RemotedSessionCache.cpp | 52 ++- shibsp/impl/StorageServiceSessionCache.cpp | 59 +-- shibsp/impl/XMLAccessControl.cpp | 67 +-- 17 files changed, 536 insertions(+), 583 deletions(-) diff --git a/apache/mod_apache.cpp b/apache/mod_apache.cpp index bfff1a3..5acfaf5 100644 --- a/apache/mod_apache.cpp +++ b/apache/mod_apache.cpp @@ -36,10 +36,8 @@ #include #include #include +#include -// SAML Runtime -#include -#include #include #include #include @@ -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 provs=dynamic_cast(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::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& 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 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::const_iterator v=vals.begin(); !auth_OK[x] && v!=vals.end(); ++v) { + if (regexp) { + auto_ptr 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 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() + ); } } } diff --git a/apache/mod_shib13.vcproj b/apache/mod_shib13.vcproj index 8a0f87c..1a5527d 100644 --- a/apache/mod_shib13.vcproj +++ b/apache/mod_shib13.vcproj @@ -214,6 +214,18 @@ + + + + + diff --git a/apache/mod_shib20.vcproj b/apache/mod_shib20.vcproj index 14d9d6d..c7389d7 100644 --- a/apache/mod_shib20.vcproj +++ b/apache/mod_shib20.vcproj @@ -214,6 +214,18 @@ + + + + + diff --git a/apache/mod_shib22.vcproj b/apache/mod_shib22.vcproj index fab3e9b..f8aa8d6 100644 --- a/apache/mod_shib22.vcproj +++ b/apache/mod_shib22.vcproj @@ -216,6 +216,18 @@ + + + + + diff --git a/isapi_shib/isapi_shib.cpp b/isapi_shib/isapi_shib.cpp index 29dee0d..d14c932 100644 --- a/isapi_shib/isapi_shib.cpp +++ b/isapi_shib/isapi_shib.cpp @@ -29,9 +29,6 @@ #include #include -// SAML Runtime -#include -#include #include #include diff --git a/nsapi_shib/nsapi_shib.cpp b/nsapi_shib/nsapi_shib.cpp index 1e572ec..1b3891f 100644 --- a/nsapi_shib/nsapi_shib.cpp +++ b/nsapi_shib/nsapi_shib.cpp @@ -37,9 +37,6 @@ #include #include -// SAML Runtime -#include -#include #include #include diff --git a/shibsp/AbstractSPRequest.cpp b/shibsp/AbstractSPRequest.cpp index 0229b2c..85dfe25 100644 --- a/shibsp/AbstractSPRequest.cpp +++ b/shibsp/AbstractSPRequest.cpp @@ -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 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 p=props->getUnsignedInt("timeout"); + if (p.first) + timeout = p.second; + pair 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 diff --git a/shibsp/AbstractSPRequest.h b/shibsp/AbstractSPRequest.h index 3487ab0..07000c3 100644 --- a/shibsp/AbstractSPRequest.h +++ b/shibsp/AbstractSPRequest.h @@ -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 diff --git a/shibsp/SPRequest.h b/shibsp/SPRequest.h index 9ea140a..5a4785f 100644 --- a/shibsp/SPRequest.h +++ b/shibsp/SPRequest.h @@ -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, diff --git a/shibsp/ServiceProvider.cpp b/shibsp/ServiceProvider.cpp index 3429361..377d138 100644 --- a/shibsp/ServiceProvider.cpp +++ b/shibsp/ServiceProvider.cpp @@ -28,6 +28,7 @@ #include "ServiceProvider.h" #include "SessionCache.h" #include "SPRequest.h" +#include "attribute/Attribute.h" #include "util/TemplateParameters.h" #include @@ -35,8 +36,8 @@ #include #include #include -#include -#include +#include +#include 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 provs=dynamic_cast(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); + 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 ServiceProvider::doAuthentication(SPRequest& request, bool handler) const { -#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 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 authType = settings.first->getString("authType"); - pair requireSession = settings.first->getBool("requireSession"); - pair 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); - - pair shib_cookie = app->getCookieNameProps("_shibsession_"); - const char* session_id = request.getCookie(shib_cookie.first.c_str()); - if (!session_id || !*session_id) { - // No session. Maybe that's acceptable? - if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first) - return make_pair(true,request.returnOK()); - - // No cookie, but we require a session. 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); - } - - procState = "Session Processing Error"; - const Session* session=NULL; - try { - session=request.getSession(); - // Make a localized exception throw if the session isn't valid. - if (!session) - throw RetryableProfileException("Session no longer valid."); - } - catch (exception& e) { - request.log(SPRequest::SPWarn, string("session processing failed: ") + e.what()); - - // If no session is required, bail now. - if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first) - // Has to be OK because DECLINED will just cause Apache - // to fail when it can't locate anything to process the - // AuthType. No session plus requireSession false means - // do not authenticate the user at this time. - return make_pair(true, request.returnOK()); - - // Try and cast down. - exception* base = &e; - RetryableProfileException* trycast=dynamic_cast(base); - if (trycast) { - // Session is invalid but we can retry -- initiate a new session. - 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); - } - throw; // send it to the outer handler - } - - // 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 +#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 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 authType = settings.first->getString("authType"); + pair requireSession = settings.first->getBool("requireSession"); + pair 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(&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 ServiceProvider::doAuthorization(SPRequest& request) const { -#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 authType = settings.first->getString("authType"); - pair requireSession = settings.first->getBool("requireSession"); - pair 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; - pair shib_cookie=app->getCookieNameProps("_shibsession_"); - const char *session_id = request.getCookie(shib_cookie.first.c_str()); - try { - if (session_id && *session_id) { - session = request.getSession(); - } - } - catch (exception&) { - request.log(SPRequest::SPWarn, "unable to obtain session information to pass to access control provider"); - } - - 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()); - } +#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 authType = settings.first->getString("authType"); + pair requireSession = settings.first->getBool("requireSession"); + pair 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 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) { - 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 + 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 ServiceProvider::doExport(SPRequest& request, bool requireSession) const { -#ifdef _DEBUG - xmltooling::NDC ndc("doExport"); -#endif - - const Application* app=NULL; - const char* procState = "Attribute Processing Error"; - string targetURL = request.getRequestURL(); - - try { +#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()); - - const Session* session=NULL; - pair shib_cookie=app->getCookieNameProps("_shibsession_"); - const char *session_id = request.getCookie(shib_cookie.first.c_str()); - try { - if (session_id && *session_id) { - session = request.getSession(); - } - } - catch (exception&) { - request.log(SPRequest::SPWarn, "unable to obtain session information to export into request headers"); - // 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 information for request."); - else - return make_pair(false,0); // just bail silently - } - - /* - TODO: port to new cache API - // Extract data from session. - pair sub=m_cacheEntry->getSubject(false,true); - pair unfiltered=m_cacheEntry->getTokens(true,false); - pair filtered=m_cacheEntry->getTokens(false,true); - - // Maybe export the tokens. - pair exp=m_settings.first->getBool("exportAssertion"); - if (exp.first && exp.second && unfiltered.first && *unfiltered.first) { - unsigned int outlen; - XMLByte* serialized = - Base64::encode(reinterpret_cast((char*)unfiltered.first), XMLString::stringLen(unfiltered.first), &outlen); - XMLByte *pos, *pos2; - for (pos=serialized, pos2=serialized; *pos2; pos2++) - if (isgraph(*pos2)) - *pos++=*pos2; - *pos=0; - setHeader("Shib-Attributes", reinterpret_cast(serialized)); - XMLString::release(&serialized); - } - - // Export the SAML AuthnMethod and the origin site name, and possibly the NameIdentifier. - setHeader("Shib-Origin-Site", m_cacheEntry->getProviderId()); - setHeader("Shib-Identity-Provider", m_cacheEntry->getProviderId()); - setHeader("Shib-Authentication-Method", m_cacheEntry->getAuthnContext()); - - // Get the AAP providers, which contain the attribute policy info. - Iterator provs=m_app->getAAPProviders(); - - // Export NameID? - while (provs.hasNext()) { - IAAP* aap=provs.next(); - xmltooling::Locker locker(aap); - const XMLCh* format = sub.second->getNameIdentifier()->getFormat(); - const IAttributeRule* rule=aap->lookup(format ? format : SAMLNameIdentifier::UNSPECIFIED); - if (rule && rule->getHeader()) { - auto_ptr_char form(format ? format : SAMLNameIdentifier::UNSPECIFIED); - auto_ptr_char nameid(sub.second->getNameIdentifier()->getName()); - setHeader("Shib-NameIdentifier-Format", form.get()); - if (!strcmp(rule->getHeader(),"REMOTE_USER")) - setRemoteUser(nameid.get()); - else - setHeader(rule->getHeader(), nameid.get()); - } - } - - setHeader("Shib-Application-ID", m_app->getId()); - - // Export the attributes. - Iterator a_iter(filtered.second ? filtered.second->getAssertions() : EMPTY(SAMLAssertion*)); - while (a_iter.hasNext()) { - SAMLAssertion* assert=a_iter.next(); - Iterator statements=assert->getStatements(); - while (statements.hasNext()) { - SAMLAttributeStatement* astate=dynamic_cast(statements.next()); - if (!astate) - continue; - Iterator attrs=astate->getAttributes(); - while (attrs.hasNext()) { - SAMLAttribute* attr=attrs.next(); - - // Are we supposed to export it? - provs.reset(); - while (provs.hasNext()) { - IAAP* aap=provs.next(); - xmltooling::Locker locker(aap); - const IAttributeRule* rule=aap->lookup(attr->getName(),attr->getNamespace()); - if (!rule || !rule->getHeader()) - continue; - - Iterator vals=attr->getSingleByteValues(); - if (!strcmp(rule->getHeader(),"REMOTE_USER") && vals.hasNext()) - setRemoteUser(vals.next().c_str()); - else { - int it=0; - string header = getSecureHeader(rule->getHeader()); - if (!header.empty()) - it++; - for (; vals.hasNext(); it++) { - string value = vals.next(); - for (string::size_type pos = value.find_first_of(";", string::size_type(0)); - pos != string::npos; - pos = value.find_first_of(";", pos)) { - value.insert(pos, "\\"); - pos += 2; - } - if (it) - header += ";"; - header += value; - } - setHeader(rule->getHeader(), header.c_str()); - } - } - } - } - } - */ - - return make_pair(false,0); - } - catch (XMLToolingException& e) { + + 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 exp=settings.first->getBool("exportAssertion"); + if (exp.first && exp.second) { + //setHeader("Shib-Attributes", reinterpret_cast(serialized)); + // TODO: export lookup URLs to access assertions by ID + const vector& tokens = session->getAssertionIDs(); + } + + // Export the attributes. + const map& attributes = session->getAttributes(); + for (map::const_iterator a = attributes.begin(); a!=attributes.end(); ++a) { + const vector& 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::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; - 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)); - } + 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 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 - catch (...) { +#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, "rm", tp)); - } -#endif + 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 ServiceProvider::doHandler(SPRequest& request) const @@ -563,7 +475,7 @@ pair ServiceProvider::doHandler(SPRequest& request) const pair 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, diff --git a/shibsp/SessionCache.h b/shibsp/SessionCache.h index a3a1925..46195d2 100644 --- a/shibsp/SessionCache.h +++ b/shibsp/SessionCache.h @@ -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& getAttributes() const=0; + virtual const std::map& getAttributes() const=0; /** * Adds additional attributes to the session. diff --git a/shibsp/attribute/Attribute.h b/shibsp/attribute/Attribute.h index 3171cc6..3a23ca5 100644 --- a/shibsp/attribute/Attribute.h +++ b/shibsp/attribute/Attribute.h @@ -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. diff --git a/shibsp/attribute/ScopedAttribute.h b/shibsp/attribute/ScopedAttribute.h index cf2247f..2156f2a 100644 --- a/shibsp/attribute/ScopedAttribute.h +++ b/shibsp/attribute/ScopedAttribute.h @@ -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 >::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 > m_values; }; diff --git a/shibsp/attribute/SimpleAttribute.h b/shibsp/attribute/SimpleAttribute.h index 8e8f2c6..7420b6f 100644 --- a/shibsp/attribute/SimpleAttribute.h +++ b/shibsp/attribute/SimpleAttribute.h @@ -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::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; }; }; diff --git a/shibsp/impl/RemotedSessionCache.cpp b/shibsp/impl/RemotedSessionCache.cpp index 95e33c7..fadb7ce 100644 --- a/shibsp/impl/RemotedSessionCache.cpp +++ b/shibsp/impl/RemotedSessionCache.cpp @@ -63,15 +63,6 @@ namespace shibsp { n->unmarshall(doc->getDocumentElement(), true); janitor.release(); - try { - DDF attrs = m_obj["attributes"]; - unmarshallAttributes(attrs); - } - catch (...) { - for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup()); - throw; - } - auto_ptr_XMLCh exp(m_obj["expires"].string()); if (exp.get()) { DateTime iso(exp.get()); @@ -79,16 +70,16 @@ namespace shibsp { m_expires = iso.getEpoch(); } - m_nameid = n.release(); m_lock = Mutex::create(); + m_nameid = n.release(); } ~RemotedSession() { delete m_lock; m_obj.destroy(); delete m_nameid; - for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup()); - for_each(m_tokens.begin(), m_tokens.end(), xmltooling::cleanup_pair()); + for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair()); + for_each(m_tokens.begin(), m_tokens.end(), cleanup_pair()); } Lockable* lock() { @@ -123,7 +114,9 @@ namespace shibsp { const char* getAuthnContextDeclRef() const { return m_obj["authncontext_decl"].string(); } - const vector& getAttributes() const { + const map& getAttributes() const { + if (m_attributes.empty()) + unmarshallAttributes(); return m_attributes; } const vector& getAssertionIDs() const { @@ -151,12 +144,12 @@ namespace shibsp { void validate(const Application& application, const char* client_addr, time_t timeout, bool local=true); private: - void unmarshallAttributes(DDF& in); + void unmarshallAttributes() const; int m_version; mutable DDF m_obj; saml2::NameID* m_nameid; - vector m_attributes; + mutable map m_attributes; mutable vector m_ids; mutable map m_tokens; time_t m_expires,m_lastAccess; @@ -207,15 +200,17 @@ namespace shibsp { } } -void RemotedSession::unmarshallAttributes(DDF& in) +void RemotedSession::unmarshallAttributes() const { - DDF attr = in.first(); + Attribute* attribute; + DDF attr = m_obj["attributes"].first(); while (!attr.isnull()) { try { - m_attributes.push_back(Attribute::unmarshall(attr)); + attribute = Attribute::unmarshall(attr); + m_attributes[attribute->getId()] = attribute; if (m_cache->m_log.isDebugEnabled()) m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s", - attr.first().name(), attr.first().integer(), attr.first().integer()!=1 ? "s" : ""); + attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : ""); } catch (AttributeException& ex) { const char* id = attr.first().name(); @@ -322,12 +317,10 @@ void RemotedSession::validate(const Application& application, const char* client if (out.isstruct()) { // We got an updated record back. m_ids.clear(); - for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup()); + for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair()); m_attributes.clear(); m_obj.destroy(); m_obj = out; - DDF attrs = m_obj["attributes"]; - unmarshallAttributes(attrs); } m_lastAccess = now; @@ -424,8 +417,6 @@ string RemotedCache::insert( DDF out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in); DDFJanitor jout(out); if (out["key"].isstring()) { - for_each(attributes->begin(), attributes->end(), xmltooling::cleanup()); - // Transaction Logging auto_ptr_char name(nameid.getName()); const char* pid = in["entity_id"].string(); @@ -444,6 +435,19 @@ string RemotedCache::insert( name.get() << ")"; + if (attributes) { + xlog->log.infoStream() << + "Cached the following attributes with session (ID: " << + out["key"].string() << + ") for (applicationId: " << + application.getId() << + ") {"; + for (vector::const_iterator a=attributes->begin(); a!=attributes->end(); ++a) + xlog->log.infoStream() << "\t" << (*a)->getId() << " (" << (*a)->valueCount() << " values)"; + xlog->log.info("}"); + for_each(attributes->begin(), attributes->end(), xmltooling::cleanup()); + } + return out["key"].string(); } throw RetryableProfileException("A remoted cache insertion operation did not return a usable session key."); diff --git a/shibsp/impl/StorageServiceSessionCache.cpp b/shibsp/impl/StorageServiceSessionCache.cpp index 7c1421f..fe53c1c 100644 --- a/shibsp/impl/StorageServiceSessionCache.cpp +++ b/shibsp/impl/StorageServiceSessionCache.cpp @@ -70,16 +70,6 @@ namespace shibsp { auto_ptr n(saml2::NameIDBuilder::buildNameID()); n->unmarshall(doc->getDocumentElement(), true); janitor.release(); - - try { - DDF attrs = m_obj["attributes"]; - unmarshallAttributes(attrs); - } - catch (...) { - for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup()); - throw; - } - m_nameid = n.release(); } @@ -113,7 +103,9 @@ namespace shibsp { const char* getAuthnContextDeclRef() const { return m_obj["authncontext_decl"].string(); } - const vector& getAttributes() const { + const map& getAttributes() const { + if (m_attributes.empty()) + unmarshallAttributes(); return m_attributes; } const vector& getAssertionIDs() const { @@ -132,11 +124,11 @@ namespace shibsp { void addAssertion(RootObject* assertion); private: - void unmarshallAttributes(DDF& in); + void unmarshallAttributes() const; DDF m_obj; saml2::NameID* m_nameid; - vector m_attributes; + mutable map m_attributes; mutable vector m_ids; mutable map m_tokens; SSCache* m_cache; @@ -182,19 +174,21 @@ StoredSession::~StoredSession() { m_obj.destroy(); delete m_nameid; - for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup()); - for_each(m_tokens.begin(), m_tokens.end(), xmltooling::cleanup_pair()); + for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair()); + for_each(m_tokens.begin(), m_tokens.end(), cleanup_pair()); } -void StoredSession::unmarshallAttributes(DDF& in) +void StoredSession::unmarshallAttributes() const { - DDF attr = in.first(); + Attribute* attribute; + DDF attr = m_obj["attributes"].first(); while (!attr.isnull()) { try { - m_attributes.push_back(Attribute::unmarshall(attr)); + attribute = Attribute::unmarshall(attr); + m_attributes[attribute->getId()] = attribute; if (m_cache->m_log.isDebugEnabled()) m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s", - attr.first().name(), attr.first().integer(), attr.first().integer()!=1 ? "s" : ""); + attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : ""); } catch (AttributeException& ex) { const char* id = attr.first().name(); @@ -268,21 +262,16 @@ void StoredSession::addAttributes(const vector& attributes) in >> newobj; m_ids.clear(); - for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup()); + for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair()); m_attributes.clear(); newobj["version"].integer(ver); m_obj.destroy(); m_obj = newobj; - DDF attrs = m_obj["attributes"]; - unmarshallAttributes(attrs); ver = -1; } } while (ver < 0); // negative indicates a sync issue so we retry - // Transfer ownership to us. - m_attributes.insert(m_attributes.end(), attributes.begin(), attributes.end()); - TransactionLog* xlog = SPConfig::getConfig().getServiceProvider()->getTransactionLog(); Locker locker(xlog); xlog->log.infoStream() << @@ -294,6 +283,9 @@ void StoredSession::addAttributes(const vector& attributes) for (vector::const_iterator a=attributes.begin(); a!=attributes.end(); ++a) xlog->log.infoStream() << "\t" << (*a)->getId() << " (" << (*a)->valueCount() << " values)"; xlog->log.info("}"); + + // We own them now, so clean them up. + for_each(attributes.begin(), attributes.end(), xmltooling::cleanup()); } const RootObject* StoredSession::getAssertion(const char* id) const @@ -392,13 +384,11 @@ void StoredSession::addAssertion(RootObject* assertion) in >> newobj; m_ids.clear(); - for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup()); + for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair()); m_attributes.clear(); newobj["version"].integer(ver); m_obj.destroy(); m_obj = newobj; - DDF attrs = m_obj["attributes"]; - unmarshallAttributes(attrs); ver = -1; } @@ -550,6 +540,19 @@ string SSCache::insert( name.get() << ")"; + if (attributes) { + xlog->log.infoStream() << + "Cached the following attributes with session (ID: " << + key.get() << + ") for (applicationId: " << + application.getId() << + ") {"; + for (vector::const_iterator a=attributes->begin(); a!=attributes->end(); ++a) + xlog->log.infoStream() << "\t" << (*a)->getId() << " (" << (*a)->valueCount() << " values)"; + xlog->log.info("}"); + for_each(attributes->begin(), attributes->end(), xmltooling::cleanup()); + } + return key.get(); } diff --git a/shibsp/impl/XMLAccessControl.cpp b/shibsp/impl/XMLAccessControl.cpp index 6d9e468..6058eff 100644 --- a/shibsp/impl/XMLAccessControl.cpp +++ b/shibsp/impl/XMLAccessControl.cpp @@ -24,6 +24,8 @@ #include "exceptions.h" #include "AccessControl.h" #include "SessionCache.h" +#include "SPRequest.h" +#include "attribute/Attribute.h" #include #include @@ -144,55 +146,36 @@ Rule::Rule(const DOMElement* e) bool Rule::authorized(const SPRequest& request, const Session* session) const { - /* - TODO: port... + // We can make this more complex later using pluggable comparison functions, + // but for now, just a straight port to the new Attribute API. + // Map alias in rule to the attribute. - Iterator provs=st->getApplication()->getAAPProviders(); - AAP wrapper(provs,m_alias.c_str()); - if (wrapper.fail()) { - st->log(ShibTarget::LogLevelWarn, string("AccessControl plugin didn't recognize rule (") + m_alias + "), check AAP for corresponding Alias"); + if (!session) { + request.log(SPRequest::SPWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?"); return false; } - else if (!entry) { - st->log(ShibTarget::LogLevelWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?"); + + // Find the attribute matching the require rule. + map::const_iterator attr = session->getAttributes().find(m_alias); + if (attr == session->getAttributes().end()) { + request.log(SPRequest::SPWarn, string("rule requires attribute (") + m_alias + "), not found in session"); return false; } - - // Find the corresponding attribute. This isn't very efficient... - pair filtered=entry->getFilteredTokens(false,true); - Iterator a_iter(filtered.second ? filtered.second->getAssertions() : EMPTY(SAMLAssertion*)); - while (a_iter.hasNext()) { - SAMLAssertion* assert=a_iter.next(); - Iterator statements=assert->getStatements(); - while (statements.hasNext()) { - SAMLAttributeStatement* astate=dynamic_cast(statements.next()); - if (!astate) - continue; - Iterator attrs=astate->getAttributes(); - while (attrs.hasNext()) { - SAMLAttribute* attr=attrs.next(); - if (!XMLString::compareString(attr->getName(),wrapper->getName()) && - !XMLString::compareString(attr->getNamespace(),wrapper->getNamespace())) { - // Now we have to intersect the attribute's values against the rule's list. - Iterator vals=attr->getSingleByteValues(); - if (!vals.hasNext()) - return false; - for (vector::const_iterator ival=m_vals.begin(); ival!=m_vals.end(); ival++) { - vals.reset(); - while (vals.hasNext()) { - const string& v=vals.next(); - if ((wrapper->getCaseSensitive() && v == *ival) || (!wrapper->getCaseSensitive() && !strcasecmp(v.c_str(),ival->c_str()))) { - st->log(ShibTarget::LogLevelDebug, string("XMLAccessControl plugin expecting " + *ival + ", authz granted")); - return true; - } - } - } - } + + bool caseSensitive = attr->second->isCaseSensitive(); + + // Now we have to intersect the attribute's values against the rule's list. + const vector& vals = attr->second->getSerializedValues(); + for (vector::const_iterator i=m_vals.begin(); i!=m_vals.end(); ++i) { + for (vector::const_iterator j=vals.begin(); j!=vals.end(); ++j) { + if ((caseSensitive && *i == *j) || (!caseSensitive && !strcasecmp(i->c_str(),j->c_str()))) { + request.log(SPRequest::SPDebug, string("AccessControl plugin expecting ") + *j + ", authz granted"); + return true; } } } - */ - return true; + + return false; } Operator::Operator(const DOMElement* e) @@ -260,7 +243,7 @@ bool Operator::authorized(const SPRequest& request, const Session* session) cons return false; } } - //st->log(ShibTarget::LogLevelWarn,"Unknown operation in access control policy, denying access"); + request.log(SPRequest::SPWarn,"unknown operation in access control policy, denying access"); return false; } -- 2.1.4