[cpp-sp] branch main updated: Remove legacy SP classes and clean up header usage.
Scott Cantor
cantor.2 at osu.edu
Tue Jan 7 17:23:03 UTC 2025
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository cpp-sp.
View the commit online:
http://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=019c17951091fe091f1f89c8aa2a94d7598cd0bc
The following commit(s) were added to refs/heads/main by this push:
new 019c1795 Remove legacy SP classes and clean up header usage.
019c1795 is described below
commit 019c17951091fe091f1f89c8aa2a94d7598cd0bc
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Jan 7 12:22:59 2025 -0500
Remove legacy SP classes and clean up header usage.
---
shibsp/Agent.cpp | 553 ++++++++++++++++++++++++++++++++++++++++--
shibsp/SPConfig.cpp | 286 ----------------------
shibsp/SPConfig.h | 231 ------------------
shibsp/ServiceProvider.cpp | 592 ---------------------------------------------
shibsp/ServiceProvider.h | 173 -------------
5 files changed, 533 insertions(+), 1302 deletions(-)
diff --git a/shibsp/Agent.cpp b/shibsp/Agent.cpp
index 90422ca6..2d041fd9 100644
--- a/shibsp/Agent.cpp
+++ b/shibsp/Agent.cpp
@@ -13,16 +13,17 @@
*/
/**
- * Agent.cpp
+ * ServiceProvider.cpp
*
- * Base class implementation for a Shibboleth agent.
+ * Interface to a Shibboleth ServiceProvider instance.
*/
#include "internal.h"
-#include "Agent.h"
#include "AgentConfig.h"
#include "exceptions.h"
#include "AccessControl.h"
+#include "Application.h"
+#include "ServiceProvider.h"
#include "SessionCache.h"
#include "SPRequest.h"
#include "attribute/Attribute.h"
@@ -39,41 +40,553 @@
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
+#ifndef HAVE_STRCASECMP
+# define strcasecmp _stricmp
+#endif
+
using namespace shibsp;
+using namespace xmltooling;
using namespace std;
-Agent::Agent()
+namespace shibsp {
+ SHIBSP_DLLLOCAL PluginManager<ServiceProvider,string,const DOMElement*>::Factory XMLServiceProviderFactory;
+
+ long SHIBSP_DLLLOCAL handleError(
+ Category& log, SPRequest& request, const Session* session=nullptr, const exception* ex=nullptr, bool mayRedirect=true
+ )
+ {
+ // The properties we need can be set in the RequestMap, or the Errors element.
+ bool externalParameters = false;
+ const char* redirectErrors = nullptr;
+
+ const agent_exception* richEx = dynamic_cast<const agent_exception*>(ex);
+
+ // Now look for settings in the request map.
+ try {
+ RequestMapper::Settings settings = request.getRequestSettings();
+ externalParameters = settings.first->getBool("externalParameters", false);
+ if (mayRedirect)
+ redirectErrors = settings.first->getString("redirectErrors");
+ }
+ catch (const exception& ex) {
+ log.error(ex.what());
+ }
+
+ // Check for redirection on errors.
+ if (mayRedirect && redirectErrors) {
+ string loc(redirectErrors);
+ request.absolutize(loc);
+ const agent_exception* richEx = dynamic_cast<const agent_exception*>(ex);
+ if (richEx) {
+ // TODO: probably alter how this works or what's included.
+ loc = loc + '?' + richEx->toQueryString();
+ }
+ return request.sendRedirect(loc.c_str());
+ }
+
+ // TODO: this probably changes significantly, but ultimately we're trying to pass
+ // back a status code.
+
+ istringstream msg("Internal Server Error. Please contact the site administrator.");
+ return request.sendResponse(msg, richEx ? richEx->getStatusCode() : HTTPResponse::SHIBSP_HTTP_STATUS_ERROR);
+ }
+
+ void SHIBSP_DLLLOCAL clearHeaders(SPRequest& request) {
+ request.clearHeader("Shib-Cookie-Name", "HTTP_SHIB_COOKIE_NAME");
+ request.clearHeader("Shib-Session-ID", "HTTP_SHIB_SESSION_ID");
+ request.clearHeader("Shib-Session-Index", "HTTP_SHIB_SESSION_INDEX");
+ request.clearHeader("Shib-Session-Expires", "HTTP_SHIB_SESSION_EXPIRES");
+ request.clearHeader("Shib-Session-Inactivity", "HTTP_SHIB_SESSION_INACTIVITY");
+ request.clearHeader("Shib-Identity-Provider", "HTTP_SHIB_IDENTITY_PROVIDER");
+ request.clearHeader("Shib-Authentication-Method", "HTTP_SHIB_AUTHENTICATION_METHOD");
+ request.clearHeader("Shib-Authentication-Instant", "HTTP_SHIB_AUTHENTICATION_INSTANT");
+ request.clearHeader("Shib-AuthnContext-Class", "HTTP_SHIB_AUTHNCONTEXT_CLASS");
+ request.clearHeader("Shib-AuthnContext-Decl", "HTTP_SHIB_AUTHNCONTEXT_DECL");
+ request.clearHeader("Shib-Assertion-Count", "HTTP_SHIB_ASSERTION_COUNT");
+ request.clearHeader("Shib-Handler", "HTTP_SHIB_HANDLER");
+ request.clearHeader("REMOTE_USER", "HTTP_REMOTE_USER");
+ // TODO: Redo the handling of attribute headers in the code, likely supplanting all of the above...
+ //request.clearAttributeHeaders();
+ }
+
+ void SHIBSP_DLLLOCAL exportAttributes(SPRequest& request, const Session* session, RequestMapper::Settings settings) {
+
+ const char* enc = settings.first->getString("encoding");
+ if (enc && strcmp(enc, "URL"))
+ throw ConfigurationException(string("Unsupported value for 'encoding' content setting: ") + enc);
+
+ const URLEncoder& encoder = AgentConfig::getConfig().getURLEncoder();
+
+ // Default delimiter is semicolon but is now configurable.
+ const char* delim = settings.first->getString("attributeValueDelimiter", ";");
+ size_t delim_len = strlen(delim);
+
+ bool exportDups = settings.first->getBool("exportDuplicateValues", true);
+ const multimap<string,const Attribute*>& attributes = session->getIndexedAttributes();
+
+ // Default export strategy will include duplicates.
+ if (exportDups) {
+ for (multimap<string,const Attribute*>::const_iterator a = attributes.begin(); a != attributes.end(); ++a) {
+ if (a->second->isInternal())
+ continue;
+ string header(request.getSecureHeader(a->first.c_str()));
+ const vector<string>& vals = a->second->getSerializedValues();
+ for (vector<string>::const_iterator v = vals.begin(); v != vals.end(); ++v) {
+ if (!header.empty())
+ header += delim;
+ if (enc) {
+ // If URL-encoding, any semicolons will get escaped anyway.
+ header += encoder.encode(v->c_str());
+ }
+ else {
+ string::size_type pos = v->find(delim, string::size_type(0));
+ if (pos != string::npos) {
+ string value(*v);
+ for (; pos != string::npos; pos = value.find(delim, pos)) {
+ value.insert(pos, "\\");
+ pos += delim_len + 1;
+ }
+ header += value;
+ }
+ else {
+ header += (*v);
+ }
+ }
+ }
+ request.setHeader(a->first.c_str(), header.c_str());
+ }
+ }
+ else {
+ // Capture values in a map of sets to check for duplicates on the fly.
+ map< string,set<string> > valueMap;
+ for (multimap<string,const Attribute*>::const_iterator a = attributes.begin(); a != attributes.end(); ++a) {
+ if (a->second->isInternal())
+ continue;
+ const vector<string>& vals = a->second->getSerializedValues();
+ valueMap[a->first].insert(vals.begin(), vals.end());
+ }
+
+ // Export the mapped sets to the headers.
+ for (map< string,set<string> >::const_iterator deduped = valueMap.begin(); deduped != valueMap.end(); ++deduped) {
+ string header;
+ for (set<string>::const_iterator v = deduped->second.begin(); v != deduped->second.end(); ++v) {
+ if (!header.empty())
+ header += delim;
+ if (enc) {
+ // If URL-encoding, any semicolons will get escaped anyway.
+ header += encoder.encode(v->c_str());
+ }
+ else {
+ string::size_type pos = v->find(delim, string::size_type(0));
+ if (pos != string::npos) {
+ string value(*v);
+ for (; pos != string::npos; pos = value.find(delim, pos)) {
+ value.insert(pos, "\\");
+ pos += delim_len + 1;
+ }
+ header += value;
+ }
+ else {
+ header += (*v);
+ }
+ }
+ }
+ request.setHeader(deduped->first.c_str(), header.c_str());
+ }
+ }
+
+ // Check for REMOTE_USER.
+ bool remoteUserSet = false;
+ vector<string> dummy;
+ const vector<string>& rmids = dummy; // app.getRemoteUserAttributeIds(); TODO: re implement this elsewhere
+ for (vector<string>::const_iterator rmid = rmids.begin(); !remoteUserSet && rmid != rmids.end(); ++rmid) {
+ pair<multimap<string,const Attribute*>::const_iterator,multimap<string,const Attribute*>::const_iterator> matches =
+ attributes.equal_range(*rmid);
+ for (; matches.first != matches.second; ++matches.first) {
+ const vector<string>& vals = matches.first->second->getSerializedValues();
+ if (!vals.empty()) {
+ if (enc)
+ request.setRemoteUser(encoder.encode(vals.front().c_str()).c_str());
+ else
+ request.setRemoteUser(vals.front().c_str());
+ remoteUserSet = true;
+ break;
+ }
+ }
+ }
+ }
+};
+
+void SHIBSP_API shibsp::registerServiceProviders()
{
- m_authTypes.insert("shibboleth");
+ SPConfig::getConfig().ServiceProviderManager.registerFactory(XML_SERVICE_PROVIDER, XMLServiceProviderFactory);
}
-Agent::~Agent()
+ServiceProvider::ServiceProvider()
{
+ m_authTypes.insert("shibboleth");
}
-// TODO: we'll eventually copy/port in substantially similar versions of the old ServiceProvider
-// method impls.
+ServiceProvider::~ServiceProvider()
+{
+}
-pair<bool,long> Agent::doAuthentication(SPRequest& request, bool handler) const
+pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handler) const
{
- pair<bool,long> foo;
- return foo;
+ Category& log = Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider");
+
+ const Application* app = nullptr;
+ string targetURL = request.getRequestURL();
+
+ try {
+ RequestMapper::Settings settings = request.getRequestSettings();
+
+ // If not SSL, check to see if we should block or redirect it.
+ if (!request.isSecure()) {
+ const char* redirectToSSL = settings.first->getString("redirectToSSL");
+ if (redirectToSSL) {
+ if (!strcasecmp("GET",request.getMethod()) || !strcasecmp("HEAD",request.getMethod())) {
+ // Compute the new target URL
+ string redirectURL = string("https://") + request.getHostname();
+ if (strcmp(redirectToSSL,"443")) {
+ redirectURL = redirectURL + ':' + redirectToSSL;
+ }
+ redirectURL += request.getRequestURI();
+ return make_pair(true, request.sendRedirect(redirectURL.c_str()));
+ }
+ else {
+ agent_exception ex("Access via unencrypted HTTP was blocked.");
+ return make_pair(true, handleError(log, request, nullptr, &ex, false));
+ }
+ }
+ }
+
+ 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 (boost::contains(targetURL, handlerURL)) {
+ if (handler)
+ return doHandler(request);
+ else
+ return make_pair(true, request.returnOK());
+ }
+
+ // These settings dictate how to proceed.
+ const char* authType = settings.first->getString("authType");
+ bool requireSession = settings.first->getBool("requireSession", false);
+ const char* requireSessionWith = settings.first->getString("requireSessionWith");
+ const char* requireLogoutWith = settings.first->getString("requireLogoutWith");
+
+ // If no session is required AND the AuthType (an Apache-derived concept) isn't recognized,
+ // 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 && !requireSessionWith &&
+ (!authType || m_authTypes.find(boost::to_lower_copy(string(authType))) == m_authTypes.end()))
+ return make_pair(true, request.returnDecline());
+
+ // Fix for secadv 20050901
+ clearHeaders(request);
+
+ Session* session = nullptr;
+ try {
+ session = request.getSession(true, false, false); // don't cache it
+ }
+ catch (const exception& e) {
+ log.warn("error during session lookup: %s", e.what());
+ // If it's not a retryable session failure, we throw to the outer handler for reporting.
+ throw;
+ }
+
+ lock_guard<Session> slocker(*session, adopt_lock); // pop existing lock on exit
+ if (session) {
+ // Check for logout interception.
+ if (requireLogoutWith) {
+ // Check for a completion parameter on the query string.
+ const char* qstr = request.getQueryString();
+ if (!qstr || !strstr(qstr, "shiblogoutdone=1")) {
+ // First leg of circuit, so we redirect to the logout endpoint specified with this URL as a return location.
+ string selfurl = request.getRequestURL();
+ if (qstr)
+ selfurl += '&';
+ else
+ selfurl += '?';
+ selfurl += "shiblogoutdone=1";
+ string loc(requireLogoutWith);
+ request.absolutize(loc);
+ if (loc.find('?') != string::npos)
+ loc += '&';
+ else
+ loc += '?';
+ loc += "return=" + AgentConfig::getConfig().getURLEncoder().encode(selfurl.c_str());
+ return make_pair(true, request.sendRedirect(loc.c_str()));
+ }
+ }
+ app->setHeader(request, "Shib-Handler", handlerURL);
+ }
+ else {
+ // No session. Maybe that's acceptable?
+ if (!requireSession && !requireSessionWith) {
+ app->setHeader(request, "Shib-Handler", handlerURL);
+ return make_pair(true, request.returnOK());
+ }
+
+ // No session, but we require one. Initiate a new session using the indicated method.
+ const SessionInitiator* initiator=nullptr;
+ if (requireSessionWith) {
+ SPConfig::getConfig().deprecation().warn("requireSessionWith");
+ initiator=app->getSessionInitiatorById(requireSessionWith);
+ if (!initiator) {
+ throw ConfigurationException(string("No session initiator found with id: ") + requireSessionWith);
+ }
+ }
+ else {
+ initiator=app->getDefaultSessionInitiator();
+ if (!initiator)
+ throw ConfigurationException("No default session initiator found, check configuration.");
+ }
+
+ // Dispatch to SessionInitiator. This MUST handle the request, or we want to fail here.
+ // Used to fall through into doExport, but this is a cleaner exit path.
+ pair<bool, long> ret = initiator->run(request, false);
+ if (ret.first)
+ return ret;
+ throw ConfigurationException("Session initiator did not handle request for a new session, check configuration.");
+ }
+
+ request.setAuthType(authType);
+
+ // We're done. Everything is okay. Nothing to report. Nothing to do..
+ // Let the caller decide how to proceed.
+ log.debug("doAuthentication succeeded");
+ return make_pair(false,0L);
+ }
+ catch (const exception& e) {
+ request.log(Priority::SHIB_ERROR, e.what());
+ return make_pair(true, handleError(log, request, nullptr, &e));
+ }
}
-pair<bool,long> Agent::doAuthorization(SPRequest& request) const
+pair<bool,long> ServiceProvider::doAuthorization(SPRequest& request) const
{
- pair<bool, long> foo;
- return foo;
+ Category& log = Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider");
+
+ const Application* app = nullptr;
+ Session* session = nullptr;
+ unique_lock<Session> slocker;
+ string targetURL = request.getRequestURL();
+
+ try {
+ RequestMapper::Settings settings = request.getRequestSettings();
+
+ // Three settings dictate how to proceed.
+ const char* authType = settings.first->getString("authType");
+ bool requireSession = settings.first->getBool("requireSession", false);
+ const char* requireSessionWith = settings.first->getString("requireSessionWith");
+
+ // If no session is required AND the AuthType (an Apache-derived concept) isn't recognized,
+ // 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 && !requireSessionWith &&
+ (!authType || m_authTypes.find(boost::to_lower_copy(string(authType))) == m_authTypes.end()))
+ return make_pair(true, request.returnDecline());
+
+ // Do we have an access control plugin?
+ if (settings.second) {
+ try {
+ session = request.getSession(false, false, false); // ignore timeout and do not cache
+ if (session) {
+ unique_lock<Session> slocker2(*session, adopt_lock);
+ slocker.swap(slocker2); // assign to lock popper
+ }
+ }
+ catch (const exception& e) {
+ log.warn("unable to obtain session to pass to access control provider: %s", e.what());
+ }
+
+#ifdef HAVE_CXX14
+ shared_lock<AccessControl> acllock(*settings.second);
+#endif
+ switch (settings.second->authorized(request, session)) {
+ case AccessControl::shib_acl_true:
+ log.debug("access control provider granted access");
+ return make_pair(true, request.returnOK());
+
+ case AccessControl::shib_acl_false:
+ {
+ log.warn("access control provider denied access");
+ agent_exception ex("Access to resource denied.");
+ ex.setStatusCode(HTTPResponse::SHIBSP_HTTP_STATUS_FORBIDDEN);
+ return make_pair(true, handleError(log, request, session, nullptr, false));
+ }
+
+ default:
+ // Use the "DECLINE" interface to signal we don't know what to do.
+ return make_pair(true, request.returnDecline());
+ }
+ }
+ else {
+ return make_pair(true, request.returnDecline());
+ }
+ }
+ catch (const exception& e) {
+ request.log(Priority::SHIB_ERROR, e.what());
+ return make_pair(true, handleError(log, request, nullptr, &e));
+ }
}
-pair<bool,long> Agent::doExport(SPRequest& request, bool requireSession) const
+pair<bool,long> ServiceProvider::doExport(SPRequest& request, bool requireSession) const
{
- pair<bool, long> foo;
- return foo;
+ Category& log = Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider");
+
+ const Application* app = nullptr;
+ Session* session = nullptr;
+ unique_lock<Session> slocker;
+ string targetURL = request.getRequestURL();
+
+ try {
+ RequestMapper::Settings settings = request.getRequestSettings();
+
+ try {
+ session = request.getSession(false, false, false); // ignore timeout and do not cache
+ if (session) {
+ unique_lock<Session> slocker2(*session, adopt_lock);
+ slocker.swap(slocker2); // assign to lock popper
+ }
+ }
+ catch (const exception& e) {
+ log.warn("unable to obtain session to export to request: %s", 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 SessionException("Unable to obtain session to export to request.");
+ else
+ return make_pair(false, 0L); // just bail silently
+ }
+
+ app->setHeader(request, "Shib-Application-ID", app->getId());
+ app->setHeader(request, "Shib-Session-ID", session->getID());
+
+ const PropertySet* sessionProps = app->getPropertySet("Sessions");
+
+ // Check for export of "standard" variables.
+ // A 3.0 release would switch this default to false and rely solely on the
+ // Assertion extractor plugin and ship out of the box with the same defaults.
+ bool stdvars = settings.first->getBool("exportStdVars", true);
+ if (stdvars) {
+ const char* hval = session->getEntityID();
+ if (hval)
+ app->setHeader(request, "Shib-Identity-Provider", hval);
+ time_t ts = session->getAuthnInstant();
+ if (ts > 0) {
+ // TODO: Need to see what the output format of this really is.
+ ostringstream os;
+ os << date::format("%FT%TZ", chrono::system_clock::from_time_t(ts));
+ app->setHeader(request, "Shib-Authentication-Instant", os.str().c_str());
+ }
+ hval = session->getAuthnContextClassRef();
+ if (hval) {
+ app->setHeader(request, "Shib-Authentication-Method", hval);
+ app->setHeader(request, "Shib-AuthnContext-Class", hval);
+ }
+
+ app->setHeader(request, "Shib-Session-Expires", boost::lexical_cast<string>(session->getExpiration()).c_str());
+ pair<bool,unsigned int> timeout = sessionProps ? sessionProps->getUnsignedInt("timeout") : pair<bool,unsigned int>(false, 0);
+ if (timeout.first && timeout.second > 0) {
+ app->setHeader(request, "Shib-Session-Inactivity", boost::lexical_cast<string>(session->getLastAccess() + timeout.second).c_str());
+ }
+ }
+
+ // Check for export of algorithmically-derived portion of cookie names.
+ bool exportCookie = settings.first->getBool("exportCookie", false);
+ if (exportCookie) {
+ pair<string,const char*> cookieprops = app->getCookieNameProps(nullptr);
+ app->setHeader(request, "Shib-Cookie-Name", cookieprops.first.c_str());
+ }
+
+ // Export the attributes.
+ exportAttributes(request, session, settings);
+
+ return make_pair(false,0L);
+ }
+ catch (const exception& e) {
+ request.log(Priority::SHIB_ERROR, e.what());
+ return make_pair(true, handleError(log, request, session, &e));
+ }
}
-pair<bool,long> Agent::doHandler(SPRequest& request) const
+pair<bool,long> ServiceProvider::doHandler(SPRequest& request) const
{
- pair<bool, long> foo;
- return foo;
+ Category& log = Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider");
+
+ const Application* app = nullptr;
+ string targetURL = request.getRequestURL();
+
+ try {
+ RequestMapper::Settings settings = request.getRequestSettings();
+
+ // If not SSL, check to see if we should block or redirect it.
+ if (!request.isSecure()) {
+ const char* redirectToSSL = settings.first->getString("redirectToSSL");
+ if (redirectToSSL) {
+ if (!strcasecmp("GET",request.getMethod()) || !strcasecmp("HEAD",request.getMethod())) {
+ // Compute the new target URL
+ string redirectURL = string("https://") + request.getHostname();
+ if (strcmp(redirectToSSL,"443")) {
+ redirectURL = redirectURL + ':' + redirectToSSL;
+ }
+ redirectURL += request.getRequestURI();
+ return make_pair(true, request.sendRedirect(redirectURL.c_str()));
+ }
+ else {
+ throw IOException("Blocked non-SSL access to Shibboleth handler.");
+ }
+ }
+ }
+
+ const char* handlerURL = request.getHandlerURL(targetURL.c_str());
+ if (!handlerURL)
+ throw ConfigurationException("Cannot determine handler from resource URL, check configuration.");
+
+ // Make sure we only process handler requests.
+ if (!boost::contains(targetURL, handlerURL))
+ return make_pair(true, request.returnDecline());
+
+ const PropertySet* sessionProps = app->getPropertySet("Sessions");
+ if (!sessionProps)
+ throw ConfigurationException("Unable to map request to application session settings, check configuration.");
+
+ // Process incoming request.
+ pair<bool,bool> handlerSSL = sessionProps->getBool("handlerSSL");
+
+ // We dispatch based on our path info. We know the request URL begins with or equals the handler URL,
+ // so the path info is the next character (or null).
+ const Handler* handler = app->getHandler(targetURL.c_str() + strlen(handlerURL));
+ if (!handler)
+ throw ConfigurationException("Shibboleth handler invoked at an unconfigured location.");
+
+ pair<bool, long> hret = handler->run(request);
+ // Did the handler run successfully?
+ if (hret.first)
+ return hret;
+ throw ConfigurationException("Configured Shibboleth handler failed to process the request.");
+ }
+ catch (const exception& e) {
+ request.log(Priority::SHIB_ERROR, e.what());
+ Session* session = nullptr;
+ try {
+ session = request.getSession(false, true, false); // do not cache
+ }
+ catch (const exception&) {
+ }
+ lock_guard<Session> slocker(*session, adopt_lock); // pop existing lock on exit
+ return make_pair(true, handleError(log, request, session, &e));
+ }
}
diff --git a/shibsp/SPConfig.cpp b/shibsp/SPConfig.cpp
deleted file mode 100644
index ce967758..00000000
--- a/shibsp/SPConfig.cpp
+++ /dev/null
@@ -1,286 +0,0 @@
-/**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
- *
- * UCAID licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the
- * License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific
- * language governing permissions and limitations under the License.
- */
-
-/**
- * SPConfig.cpp
- *
- * Library configuration.
- */
-
-#include "internal.h"
-
-#include "exceptions.h"
-#include "version.h"
-#include "AccessControl.h"
-#include "RequestMapper.h"
-#include "ServiceProvider.h"
-#include "SessionCache.h"
-#include "SPConfig.h"
-#include "attribute/Attribute.h"
-#include "handler/LogoutInitiator.h"
-#include "handler/SessionInitiator.h"
-
-#include <ctime>
-#include <sstream>
-#include <xercesc/util/XMLUniDefs.hpp>
-#include <xmltooling/version.h>
-#include <xmltooling/XMLToolingConfig.h>
-#include <xmltooling/util/ParserPool.h>
-#include <xmltooling/util/Threads.h>
-#include <xmltooling/util/XMLHelper.h>
-
-using namespace shibsp;
-using namespace xmltooling;
-using namespace boost;
-using namespace std;
-
-namespace shibsp {
- class SHIBSP_DLLLOCAL SPInternalConfig : public SPConfig
- {
- public:
- SPInternalConfig() : m_initCount(0), m_lock(Mutex::create()) {}
- ~SPInternalConfig() {}
-
- bool init(const char* catalog_path=nullptr, const char* inst_prefix=nullptr);
- void term();
-
- private:
- int m_initCount;
- scoped_ptr<Mutex> m_lock;
- };
-
- SPInternalConfig g_config;
-}
-
-SPConfig& SPConfig::getConfig()
-{
- return g_config;
-}
-
-SPConfig::SPConfig() : attribute_value_delimeter(';'), m_serviceProvider(nullptr), m_features(0), m_configDoc(nullptr)
-{
-}
-
-SPConfig::~SPConfig()
-{
-}
-
-void SPConfig::setFeatures(unsigned long enabled)
-{
- m_features = enabled;
-}
-
-unsigned long SPConfig::getFeatures() const {
- return m_features;
-}
-
-bool SPConfig::isEnabled(components_t feature) const
-{
- return (m_features & feature)>0;
-}
-
-ServiceProvider* SPConfig::getServiceProvider() const
-{
- return m_serviceProvider;
-}
-
-void SPConfig::setServiceProvider(ServiceProvider* serviceProvider)
-{
- delete m_serviceProvider;
- m_serviceProvider = serviceProvider;
-}
-
-bool SPConfig::init(const char* catalog_path, const char* inst_prefix)
-{
- if (!inst_prefix)
- inst_prefix = getenv("SHIBSP_PREFIX");
- if (!inst_prefix)
- inst_prefix = SHIBSP_PREFIX;
- std::string inst_prefix2;
- while (*inst_prefix) {
- inst_prefix2.push_back((*inst_prefix=='\\') ? ('/') : (*inst_prefix));
- ++inst_prefix;
- }
-
- Category& log=Category::getInstance(SHIBSP_LOGCAT ".Config");
- log.debug("%s library initialization started", PACKAGE_STRING);
-
- XMLToolingConfig::getConfig().user_agent = string(PACKAGE_NAME) + '/' + PACKAGE_VERSION;
-
- if (!catalog_path)
- catalog_path = getenv("SHIBSP_SCHEMAS");
- if (!catalog_path || !*catalog_path)
- catalog_path = SHIBSP_SCHEMAS;
- if (!XMLToolingConfig::getConfig().getValidatingParser().loadCatalogs(catalog_path)) {
- log.warn("failed to load schema catalogs into validating parser");
- }
-
- registerAttributeFactories();
-
- if (isEnabled(Handlers)) {
- registerHandlers();
- }
-
- registerServiceProviders();
-
- if (isEnabled(RequestMapping)) {
- registerAccessControls();
- registerRequestMappers();
- }
-
- if (isEnabled(Caching))
- registerSessionCaches();
-
- // Yes, this isn't insecure, will review where we do any random generation
- // after full code cleanup is done.
- srand(static_cast<unsigned int>(std::time(nullptr)));
-
- log.info("%s library initialization complete", PACKAGE_STRING);
- return true;
-}
-
-void SPConfig::term()
-{
- Category& log=Category::getInstance(SHIBSP_LOGCAT ".Config");
- log.info("%s library shutting down", PACKAGE_STRING);
-
- setServiceProvider(nullptr);
- if (m_configDoc)
- m_configDoc->release();
- m_configDoc = nullptr;
-
- if (isEnabled(Handlers)) {
- AssertionConsumerServiceManager.deregisterFactories();
- LogoutInitiatorManager.deregisterFactories();
- SessionInitiatorManager.deregisterFactories();
- SingleLogoutServiceManager.deregisterFactories();
- HandlerManager.deregisterFactories();
- }
-
- ServiceProviderManager.deregisterFactories();
- Attribute::deregisterFactories();
-
- if (isEnabled(RequestMapping)) {
- AccessControlManager.deregisterFactories();
- RequestMapperManager.deregisterFactories();
- }
-
- if (isEnabled(Caching))
- SessionCacheManager.deregisterFactories();
-
- log.info("%s library shutdown complete", PACKAGE_STRING);
-}
-
-bool SPConfig::instantiate(const char* config, bool rethrow)
-{
- if (!config)
- config = getenv("SHIBSP_CONFIG");
- if (!config) {
- config = SHIBSP_CONFIG;
- }
- try {
- xercesc::DOMDocument* dummydoc;
- if (*config == '"' || *config == '\'') {
- throw ConfigurationException("The value of SHIBSP_CONFIG started with a quote.");
- }
- else if (*config != '<') {
- // Mock up some XML.
- string resolved(config);
- stringstream snippet;
- snippet
- << "<Dummy path='"
- << resolved
- << "' validate='1'/>";
- dummydoc = XMLToolingConfig::getConfig().getParser().parse(snippet);
- XercesJanitor<xercesc::DOMDocument> docjanitor(dummydoc);
- setServiceProvider(ServiceProviderManager.newPlugin(XML_SERVICE_PROVIDER, dummydoc->getDocumentElement(), true));
- if (m_configDoc)
- m_configDoc->release();
- m_configDoc = docjanitor.release();
- }
- else {
- stringstream snippet(config);
- dummydoc = XMLToolingConfig::getConfig().getParser().parse(snippet);
- XercesJanitor<xercesc::DOMDocument> docjanitor(dummydoc);
- static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
- auto_ptr_char type(dummydoc->getDocumentElement()->getAttributeNS(nullptr,_type));
- if (type.get() && *type.get())
- setServiceProvider(ServiceProviderManager.newPlugin(type.get(), dummydoc->getDocumentElement(), true));
- else
- throw ConfigurationException("The supplied XML bootstrapping configuration did not include a type attribute.");
- if (m_configDoc)
- m_configDoc->release();
- m_configDoc = docjanitor.release();
- }
-
- getServiceProvider()->init();
- return true;
- }
- catch (const std::exception& ex) {
- if (rethrow) {
- throw;
- }
- else {
- Category::getInstance(SHIBSP_LOGCAT ".Config").crit("caught exception while loading configuration: %s", ex.what());
- }
- }
- return false;
-}
-
-bool SPInternalConfig::init(const char* catalog_path, const char* inst_prefix)
-{
- Lock initLock(m_lock);
-
- if (m_initCount == INT_MAX) {
- Category::getInstance(SHIBSP_LOGCAT ".Config").crit("library initialized too many times");
- return false;
- }
-
- if (m_initCount >= 1) {
- ++m_initCount;
- return true;
- }
-
- if (!SPConfig::init(catalog_path, inst_prefix)) {
- return false;
- }
-
- ++m_initCount;
- return true;
-}
-
-void SPInternalConfig::term()
-{
- Lock initLock(m_lock);
- if (m_initCount == 0) {
- Category::getInstance(SHIBSP_LOGCAT ".Config").crit("term without corresponding init");
- return;
- }
- else if (--m_initCount > 0) {
- return;
- }
-
- SPConfig::term();
-}
-
-Category& SPConfig::deprecation() const
-{
- return Category::getInstance(SHIBSP_LOGCAT".DEPRECATION");
-}
diff --git a/shibsp/SPConfig.h b/shibsp/SPConfig.h
deleted file mode 100644
index 7cb837a2..00000000
--- a/shibsp/SPConfig.h
+++ /dev/null
@@ -1,231 +0,0 @@
-/**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
- *
- * UCAID licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the
- * License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific
- * language governing permissions and limitations under the License.
- */
-
-/**
- * @file shibsp/SPConfig.h
- *
- * Library configuration.
- */
-
-#ifndef __shibsp_config_h__
-#define __shibsp_config_h__
-
-#include <shibsp/base.h>
-#include <shibsp/logging/Category.h>
-#include <shibsp/util/PluginManager.h>
-
-#include <string>
-#include <xmltooling/QName.h>
-#include <xercesc/dom/DOM.hpp>
-
-/**
- * @namespace shibsp
- * Shibboleth Service Provider Library
- */
-namespace shibsp {
-
- class SHIBSP_API AccessControl;
- class SHIBSP_API Handler;
- class SHIBSP_API ListenerService;
- class SHIBSP_API RequestMapper;
- class SHIBSP_API ServiceProvider;
- class SHIBSP_API SessionCache;
- class SHIBSP_API SessionInitiator;
-
-#if defined (_MSC_VER)
- #pragma warning( push )
- #pragma warning( disable : 4250 4251 )
-#endif
-
- /**
- * Singleton object that manages library startup/shutdown.
- */
- class SHIBSP_API SPConfig
- {
- MAKE_NONCOPYABLE(SPConfig);
- public:
- SPConfig();
-
- virtual ~SPConfig();
-
- /**
- * Returns the global configuration object for the library.
- *
- * @return reference to the global library configuration object
- */
- static SPConfig& getConfig();
-
- /**
- * Bitmask values representing subsystems of the library.
- */
- enum components_t {
- Listener = 1,
- Caching = 2,
- RequestMapping = 64,
- OutOfProcess = 128,
- InProcess = 256,
- Logging = 512,
- Handlers = 1024
- };
-
- /**
- * Set a bitmask of subsystems to activate.
- *
- * @param enabled bitmask of component constants
- */
- void setFeatures(unsigned long enabled);
-
-
- /**
- * Gets the bitmask of subsystems being activated.
- *
- * @return bitmask of component constants
- */
- unsigned long getFeatures() const;
-
- /**
- * Test whether a subsystem is enabled.
- *
- * @param feature subsystem/component to test
- * @return true iff feature is enabled
- */
- bool isEnabled(components_t feature) const;
-
- /**
- * Initializes library
- *
- * Each process using the library MUST call this function exactly once
- * before using any library classes.
- *
- * @param catalog_path delimited set of schema catalog files to load
- * @param inst_prefix installation prefix for software
- * @return true iff initialization was successful
- */
- virtual bool init(const char* catalog_path=nullptr, const char* inst_prefix=nullptr);
-
- /**
- * Shuts down library
- *
- * Each process using the library SHOULD call this function exactly once
- * before terminating itself.
- */
- virtual void term();
-
- /**
- * Sets the global ServiceProvider instance.
- * This method must be externally synchronized with any code that uses the object.
- * Any previously set object is destroyed.
- *
- * @param serviceProvider new ServiceProvider instance to store
- */
- void setServiceProvider(ServiceProvider* serviceProvider);
-
- /**
- * Returns the global ServiceProvider instance.
- *
- * @return global ServiceProvider or nullptr
- */
- ServiceProvider* getServiceProvider() const;
-
- /**
- * Instantiates and installs a ServiceProvider instance based on an XML configuration string
- * or a configuration pathname.
- *
- * @param config a snippet of XML to parse (it <strong>MUST</strong> contain a type attribute) or a pathname
- * @param rethrow true iff caught exceptions should be rethrown instead of just returning the status
- * @return true iff instantiation was successful
- */
- virtual bool instantiate(const char* config=nullptr, bool rethrow=false);
-
- /**
- * Separator for serialized values of multi-valued attributes.
- *
- * <p>This is deprecated, and was never actually read within the code.</p>
- *
- * @deprecated
- */
- char attribute_value_delimeter;
-
- /**
- * Manages factories for AccessControl plugins.
- */
- PluginManager<AccessControl,std::string,const xercesc::DOMElement*> AccessControlManager;
-
- /**
- * Manages factories for Handler plugins that implement AssertionConsumerService functionality.
- */
- PluginManager< Handler,std::string,std::pair<const xercesc::DOMElement*,const char*> > AssertionConsumerServiceManager;
-
- /**
- * Manages factories for Handler plugins that implement customized functionality.
- */
- PluginManager< Handler,std::string,std::pair<const xercesc::DOMElement*,const char*> > HandlerManager;
-
- /**
- * Manages factories for Handler plugins that implement LogoutInitiator functionality.
- */
- PluginManager< Handler,std::string,std::pair<const xercesc::DOMElement*,const char*> > LogoutInitiatorManager;
-
- /**
- * Manages factories for RequestMapper plugins.
- */
- PluginManager<RequestMapper,std::string,const xercesc::DOMElement*> RequestMapperManager;
-
- /**
- * Manages factories for ServiceProvider plugins.
- */
- PluginManager<ServiceProvider,std::string,const xercesc::DOMElement*> ServiceProviderManager;
-
- /**
- * Manages factories for SessionCache plugins.
- */
- PluginManager<SessionCache,std::string,const xercesc::DOMElement*> SessionCacheManager;
-
- /**
- * Manages factories for Handler plugins that implement SessionInitiator functionality.
- */
- PluginManager< SessionInitiator,std::string,std::pair<const xercesc::DOMElement*,const char*> > SessionInitiatorManager;
-
- /**
- * Manages factories for Handler plugins that implement SingleLogoutService functionality.
- */
- PluginManager< Handler,std::string,std::pair<const xercesc::DOMElement*,const char*> > SingleLogoutServiceManager;
-
- /**
- * Helper for deprecation warnings about an at-risk feature or setting.
- */
- Category& deprecation() const;
-
- protected:
- /** Global ServiceProvider instance. */
- ServiceProvider* m_serviceProvider;
-
- private:
- unsigned long m_features;
- xercesc::DOMDocument* m_configDoc;
- };
-
-#if defined (_MSC_VER)
- #pragma warning( pop )
-#endif
-
-};
-
-#endif /* __shibsp_config_h__ */
diff --git a/shibsp/ServiceProvider.cpp b/shibsp/ServiceProvider.cpp
deleted file mode 100644
index 2d041fd9..00000000
--- a/shibsp/ServiceProvider.cpp
+++ /dev/null
@@ -1,592 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * ServiceProvider.cpp
- *
- * Interface to a Shibboleth ServiceProvider instance.
- */
-
-#include "internal.h"
-#include "AgentConfig.h"
-#include "exceptions.h"
-#include "AccessControl.h"
-#include "Application.h"
-#include "ServiceProvider.h"
-#include "SessionCache.h"
-#include "SPRequest.h"
-#include "attribute/Attribute.h"
-#include "handler/SessionInitiator.h"
-#include "util/Date.h"
-#include "util/PathResolver.h"
-#include "util/URLEncoder.h"
-
-#include <fstream>
-#include <sstream>
-#ifdef HAVE_CXX14
-# include <shared_mutex>
-#endif
-#include <boost/algorithm/string.hpp>
-#include <boost/lexical_cast.hpp>
-
-#ifndef HAVE_STRCASECMP
-# define strcasecmp _stricmp
-#endif
-
-using namespace shibsp;
-using namespace xmltooling;
-using namespace std;
-
-namespace shibsp {
- SHIBSP_DLLLOCAL PluginManager<ServiceProvider,string,const DOMElement*>::Factory XMLServiceProviderFactory;
-
- long SHIBSP_DLLLOCAL handleError(
- Category& log, SPRequest& request, const Session* session=nullptr, const exception* ex=nullptr, bool mayRedirect=true
- )
- {
- // The properties we need can be set in the RequestMap, or the Errors element.
- bool externalParameters = false;
- const char* redirectErrors = nullptr;
-
- const agent_exception* richEx = dynamic_cast<const agent_exception*>(ex);
-
- // Now look for settings in the request map.
- try {
- RequestMapper::Settings settings = request.getRequestSettings();
- externalParameters = settings.first->getBool("externalParameters", false);
- if (mayRedirect)
- redirectErrors = settings.first->getString("redirectErrors");
- }
- catch (const exception& ex) {
- log.error(ex.what());
- }
-
- // Check for redirection on errors.
- if (mayRedirect && redirectErrors) {
- string loc(redirectErrors);
- request.absolutize(loc);
- const agent_exception* richEx = dynamic_cast<const agent_exception*>(ex);
- if (richEx) {
- // TODO: probably alter how this works or what's included.
- loc = loc + '?' + richEx->toQueryString();
- }
- return request.sendRedirect(loc.c_str());
- }
-
- // TODO: this probably changes significantly, but ultimately we're trying to pass
- // back a status code.
-
- istringstream msg("Internal Server Error. Please contact the site administrator.");
- return request.sendResponse(msg, richEx ? richEx->getStatusCode() : HTTPResponse::SHIBSP_HTTP_STATUS_ERROR);
- }
-
- void SHIBSP_DLLLOCAL clearHeaders(SPRequest& request) {
- request.clearHeader("Shib-Cookie-Name", "HTTP_SHIB_COOKIE_NAME");
- request.clearHeader("Shib-Session-ID", "HTTP_SHIB_SESSION_ID");
- request.clearHeader("Shib-Session-Index", "HTTP_SHIB_SESSION_INDEX");
- request.clearHeader("Shib-Session-Expires", "HTTP_SHIB_SESSION_EXPIRES");
- request.clearHeader("Shib-Session-Inactivity", "HTTP_SHIB_SESSION_INACTIVITY");
- request.clearHeader("Shib-Identity-Provider", "HTTP_SHIB_IDENTITY_PROVIDER");
- request.clearHeader("Shib-Authentication-Method", "HTTP_SHIB_AUTHENTICATION_METHOD");
- request.clearHeader("Shib-Authentication-Instant", "HTTP_SHIB_AUTHENTICATION_INSTANT");
- request.clearHeader("Shib-AuthnContext-Class", "HTTP_SHIB_AUTHNCONTEXT_CLASS");
- request.clearHeader("Shib-AuthnContext-Decl", "HTTP_SHIB_AUTHNCONTEXT_DECL");
- request.clearHeader("Shib-Assertion-Count", "HTTP_SHIB_ASSERTION_COUNT");
- request.clearHeader("Shib-Handler", "HTTP_SHIB_HANDLER");
- request.clearHeader("REMOTE_USER", "HTTP_REMOTE_USER");
- // TODO: Redo the handling of attribute headers in the code, likely supplanting all of the above...
- //request.clearAttributeHeaders();
- }
-
- void SHIBSP_DLLLOCAL exportAttributes(SPRequest& request, const Session* session, RequestMapper::Settings settings) {
-
- const char* enc = settings.first->getString("encoding");
- if (enc && strcmp(enc, "URL"))
- throw ConfigurationException(string("Unsupported value for 'encoding' content setting: ") + enc);
-
- const URLEncoder& encoder = AgentConfig::getConfig().getURLEncoder();
-
- // Default delimiter is semicolon but is now configurable.
- const char* delim = settings.first->getString("attributeValueDelimiter", ";");
- size_t delim_len = strlen(delim);
-
- bool exportDups = settings.first->getBool("exportDuplicateValues", true);
- const multimap<string,const Attribute*>& attributes = session->getIndexedAttributes();
-
- // Default export strategy will include duplicates.
- if (exportDups) {
- for (multimap<string,const Attribute*>::const_iterator a = attributes.begin(); a != attributes.end(); ++a) {
- if (a->second->isInternal())
- continue;
- string header(request.getSecureHeader(a->first.c_str()));
- const vector<string>& vals = a->second->getSerializedValues();
- for (vector<string>::const_iterator v = vals.begin(); v != vals.end(); ++v) {
- if (!header.empty())
- header += delim;
- if (enc) {
- // If URL-encoding, any semicolons will get escaped anyway.
- header += encoder.encode(v->c_str());
- }
- else {
- string::size_type pos = v->find(delim, string::size_type(0));
- if (pos != string::npos) {
- string value(*v);
- for (; pos != string::npos; pos = value.find(delim, pos)) {
- value.insert(pos, "\\");
- pos += delim_len + 1;
- }
- header += value;
- }
- else {
- header += (*v);
- }
- }
- }
- request.setHeader(a->first.c_str(), header.c_str());
- }
- }
- else {
- // Capture values in a map of sets to check for duplicates on the fly.
- map< string,set<string> > valueMap;
- for (multimap<string,const Attribute*>::const_iterator a = attributes.begin(); a != attributes.end(); ++a) {
- if (a->second->isInternal())
- continue;
- const vector<string>& vals = a->second->getSerializedValues();
- valueMap[a->first].insert(vals.begin(), vals.end());
- }
-
- // Export the mapped sets to the headers.
- for (map< string,set<string> >::const_iterator deduped = valueMap.begin(); deduped != valueMap.end(); ++deduped) {
- string header;
- for (set<string>::const_iterator v = deduped->second.begin(); v != deduped->second.end(); ++v) {
- if (!header.empty())
- header += delim;
- if (enc) {
- // If URL-encoding, any semicolons will get escaped anyway.
- header += encoder.encode(v->c_str());
- }
- else {
- string::size_type pos = v->find(delim, string::size_type(0));
- if (pos != string::npos) {
- string value(*v);
- for (; pos != string::npos; pos = value.find(delim, pos)) {
- value.insert(pos, "\\");
- pos += delim_len + 1;
- }
- header += value;
- }
- else {
- header += (*v);
- }
- }
- }
- request.setHeader(deduped->first.c_str(), header.c_str());
- }
- }
-
- // Check for REMOTE_USER.
- bool remoteUserSet = false;
- vector<string> dummy;
- const vector<string>& rmids = dummy; // app.getRemoteUserAttributeIds(); TODO: re implement this elsewhere
- for (vector<string>::const_iterator rmid = rmids.begin(); !remoteUserSet && rmid != rmids.end(); ++rmid) {
- pair<multimap<string,const Attribute*>::const_iterator,multimap<string,const Attribute*>::const_iterator> matches =
- attributes.equal_range(*rmid);
- for (; matches.first != matches.second; ++matches.first) {
- const vector<string>& vals = matches.first->second->getSerializedValues();
- if (!vals.empty()) {
- if (enc)
- request.setRemoteUser(encoder.encode(vals.front().c_str()).c_str());
- else
- request.setRemoteUser(vals.front().c_str());
- remoteUserSet = true;
- break;
- }
- }
- }
- }
-};
-
-void SHIBSP_API shibsp::registerServiceProviders()
-{
- SPConfig::getConfig().ServiceProviderManager.registerFactory(XML_SERVICE_PROVIDER, XMLServiceProviderFactory);
-}
-
-ServiceProvider::ServiceProvider()
-{
- m_authTypes.insert("shibboleth");
-}
-
-ServiceProvider::~ServiceProvider()
-{
-}
-
-pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handler) const
-{
- Category& log = Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider");
-
- const Application* app = nullptr;
- string targetURL = request.getRequestURL();
-
- try {
- RequestMapper::Settings settings = request.getRequestSettings();
-
- // If not SSL, check to see if we should block or redirect it.
- if (!request.isSecure()) {
- const char* redirectToSSL = settings.first->getString("redirectToSSL");
- if (redirectToSSL) {
- if (!strcasecmp("GET",request.getMethod()) || !strcasecmp("HEAD",request.getMethod())) {
- // Compute the new target URL
- string redirectURL = string("https://") + request.getHostname();
- if (strcmp(redirectToSSL,"443")) {
- redirectURL = redirectURL + ':' + redirectToSSL;
- }
- redirectURL += request.getRequestURI();
- return make_pair(true, request.sendRedirect(redirectURL.c_str()));
- }
- else {
- agent_exception ex("Access via unencrypted HTTP was blocked.");
- return make_pair(true, handleError(log, request, nullptr, &ex, false));
- }
- }
- }
-
- 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 (boost::contains(targetURL, handlerURL)) {
- if (handler)
- return doHandler(request);
- else
- return make_pair(true, request.returnOK());
- }
-
- // These settings dictate how to proceed.
- const char* authType = settings.first->getString("authType");
- bool requireSession = settings.first->getBool("requireSession", false);
- const char* requireSessionWith = settings.first->getString("requireSessionWith");
- const char* requireLogoutWith = settings.first->getString("requireLogoutWith");
-
- // If no session is required AND the AuthType (an Apache-derived concept) isn't recognized,
- // 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 && !requireSessionWith &&
- (!authType || m_authTypes.find(boost::to_lower_copy(string(authType))) == m_authTypes.end()))
- return make_pair(true, request.returnDecline());
-
- // Fix for secadv 20050901
- clearHeaders(request);
-
- Session* session = nullptr;
- try {
- session = request.getSession(true, false, false); // don't cache it
- }
- catch (const exception& e) {
- log.warn("error during session lookup: %s", e.what());
- // If it's not a retryable session failure, we throw to the outer handler for reporting.
- throw;
- }
-
- lock_guard<Session> slocker(*session, adopt_lock); // pop existing lock on exit
- if (session) {
- // Check for logout interception.
- if (requireLogoutWith) {
- // Check for a completion parameter on the query string.
- const char* qstr = request.getQueryString();
- if (!qstr || !strstr(qstr, "shiblogoutdone=1")) {
- // First leg of circuit, so we redirect to the logout endpoint specified with this URL as a return location.
- string selfurl = request.getRequestURL();
- if (qstr)
- selfurl += '&';
- else
- selfurl += '?';
- selfurl += "shiblogoutdone=1";
- string loc(requireLogoutWith);
- request.absolutize(loc);
- if (loc.find('?') != string::npos)
- loc += '&';
- else
- loc += '?';
- loc += "return=" + AgentConfig::getConfig().getURLEncoder().encode(selfurl.c_str());
- return make_pair(true, request.sendRedirect(loc.c_str()));
- }
- }
- app->setHeader(request, "Shib-Handler", handlerURL);
- }
- else {
- // No session. Maybe that's acceptable?
- if (!requireSession && !requireSessionWith) {
- app->setHeader(request, "Shib-Handler", handlerURL);
- return make_pair(true, request.returnOK());
- }
-
- // No session, but we require one. Initiate a new session using the indicated method.
- const SessionInitiator* initiator=nullptr;
- if (requireSessionWith) {
- SPConfig::getConfig().deprecation().warn("requireSessionWith");
- initiator=app->getSessionInitiatorById(requireSessionWith);
- if (!initiator) {
- throw ConfigurationException(string("No session initiator found with id: ") + requireSessionWith);
- }
- }
- else {
- initiator=app->getDefaultSessionInitiator();
- if (!initiator)
- throw ConfigurationException("No default session initiator found, check configuration.");
- }
-
- // Dispatch to SessionInitiator. This MUST handle the request, or we want to fail here.
- // Used to fall through into doExport, but this is a cleaner exit path.
- pair<bool, long> ret = initiator->run(request, false);
- if (ret.first)
- return ret;
- throw ConfigurationException("Session initiator did not handle request for a new session, check configuration.");
- }
-
- request.setAuthType(authType);
-
- // We're done. Everything is okay. Nothing to report. Nothing to do..
- // Let the caller decide how to proceed.
- log.debug("doAuthentication succeeded");
- return make_pair(false,0L);
- }
- catch (const exception& e) {
- request.log(Priority::SHIB_ERROR, e.what());
- return make_pair(true, handleError(log, request, nullptr, &e));
- }
-}
-
-pair<bool,long> ServiceProvider::doAuthorization(SPRequest& request) const
-{
- Category& log = Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider");
-
- const Application* app = nullptr;
- Session* session = nullptr;
- unique_lock<Session> slocker;
- string targetURL = request.getRequestURL();
-
- try {
- RequestMapper::Settings settings = request.getRequestSettings();
-
- // Three settings dictate how to proceed.
- const char* authType = settings.first->getString("authType");
- bool requireSession = settings.first->getBool("requireSession", false);
- const char* requireSessionWith = settings.first->getString("requireSessionWith");
-
- // If no session is required AND the AuthType (an Apache-derived concept) isn't recognized,
- // 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 && !requireSessionWith &&
- (!authType || m_authTypes.find(boost::to_lower_copy(string(authType))) == m_authTypes.end()))
- return make_pair(true, request.returnDecline());
-
- // Do we have an access control plugin?
- if (settings.second) {
- try {
- session = request.getSession(false, false, false); // ignore timeout and do not cache
- if (session) {
- unique_lock<Session> slocker2(*session, adopt_lock);
- slocker.swap(slocker2); // assign to lock popper
- }
- }
- catch (const exception& e) {
- log.warn("unable to obtain session to pass to access control provider: %s", e.what());
- }
-
-#ifdef HAVE_CXX14
- shared_lock<AccessControl> acllock(*settings.second);
-#endif
- switch (settings.second->authorized(request, session)) {
- case AccessControl::shib_acl_true:
- log.debug("access control provider granted access");
- return make_pair(true, request.returnOK());
-
- case AccessControl::shib_acl_false:
- {
- log.warn("access control provider denied access");
- agent_exception ex("Access to resource denied.");
- ex.setStatusCode(HTTPResponse::SHIBSP_HTTP_STATUS_FORBIDDEN);
- return make_pair(true, handleError(log, request, session, nullptr, false));
- }
-
- default:
- // Use the "DECLINE" interface to signal we don't know what to do.
- return make_pair(true, request.returnDecline());
- }
- }
- else {
- return make_pair(true, request.returnDecline());
- }
- }
- catch (const exception& e) {
- request.log(Priority::SHIB_ERROR, e.what());
- return make_pair(true, handleError(log, request, nullptr, &e));
- }
-}
-
-pair<bool,long> ServiceProvider::doExport(SPRequest& request, bool requireSession) const
-{
- Category& log = Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider");
-
- const Application* app = nullptr;
- Session* session = nullptr;
- unique_lock<Session> slocker;
- string targetURL = request.getRequestURL();
-
- try {
- RequestMapper::Settings settings = request.getRequestSettings();
-
- try {
- session = request.getSession(false, false, false); // ignore timeout and do not cache
- if (session) {
- unique_lock<Session> slocker2(*session, adopt_lock);
- slocker.swap(slocker2); // assign to lock popper
- }
- }
- catch (const exception& e) {
- log.warn("unable to obtain session to export to request: %s", 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 SessionException("Unable to obtain session to export to request.");
- else
- return make_pair(false, 0L); // just bail silently
- }
-
- app->setHeader(request, "Shib-Application-ID", app->getId());
- app->setHeader(request, "Shib-Session-ID", session->getID());
-
- const PropertySet* sessionProps = app->getPropertySet("Sessions");
-
- // Check for export of "standard" variables.
- // A 3.0 release would switch this default to false and rely solely on the
- // Assertion extractor plugin and ship out of the box with the same defaults.
- bool stdvars = settings.first->getBool("exportStdVars", true);
- if (stdvars) {
- const char* hval = session->getEntityID();
- if (hval)
- app->setHeader(request, "Shib-Identity-Provider", hval);
- time_t ts = session->getAuthnInstant();
- if (ts > 0) {
- // TODO: Need to see what the output format of this really is.
- ostringstream os;
- os << date::format("%FT%TZ", chrono::system_clock::from_time_t(ts));
- app->setHeader(request, "Shib-Authentication-Instant", os.str().c_str());
- }
- hval = session->getAuthnContextClassRef();
- if (hval) {
- app->setHeader(request, "Shib-Authentication-Method", hval);
- app->setHeader(request, "Shib-AuthnContext-Class", hval);
- }
-
- app->setHeader(request, "Shib-Session-Expires", boost::lexical_cast<string>(session->getExpiration()).c_str());
- pair<bool,unsigned int> timeout = sessionProps ? sessionProps->getUnsignedInt("timeout") : pair<bool,unsigned int>(false, 0);
- if (timeout.first && timeout.second > 0) {
- app->setHeader(request, "Shib-Session-Inactivity", boost::lexical_cast<string>(session->getLastAccess() + timeout.second).c_str());
- }
- }
-
- // Check for export of algorithmically-derived portion of cookie names.
- bool exportCookie = settings.first->getBool("exportCookie", false);
- if (exportCookie) {
- pair<string,const char*> cookieprops = app->getCookieNameProps(nullptr);
- app->setHeader(request, "Shib-Cookie-Name", cookieprops.first.c_str());
- }
-
- // Export the attributes.
- exportAttributes(request, session, settings);
-
- return make_pair(false,0L);
- }
- catch (const exception& e) {
- request.log(Priority::SHIB_ERROR, e.what());
- return make_pair(true, handleError(log, request, session, &e));
- }
-}
-
-pair<bool,long> ServiceProvider::doHandler(SPRequest& request) const
-{
- Category& log = Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider");
-
- const Application* app = nullptr;
- string targetURL = request.getRequestURL();
-
- try {
- RequestMapper::Settings settings = request.getRequestSettings();
-
- // If not SSL, check to see if we should block or redirect it.
- if (!request.isSecure()) {
- const char* redirectToSSL = settings.first->getString("redirectToSSL");
- if (redirectToSSL) {
- if (!strcasecmp("GET",request.getMethod()) || !strcasecmp("HEAD",request.getMethod())) {
- // Compute the new target URL
- string redirectURL = string("https://") + request.getHostname();
- if (strcmp(redirectToSSL,"443")) {
- redirectURL = redirectURL + ':' + redirectToSSL;
- }
- redirectURL += request.getRequestURI();
- return make_pair(true, request.sendRedirect(redirectURL.c_str()));
- }
- else {
- throw IOException("Blocked non-SSL access to Shibboleth handler.");
- }
- }
- }
-
- const char* handlerURL = request.getHandlerURL(targetURL.c_str());
- if (!handlerURL)
- throw ConfigurationException("Cannot determine handler from resource URL, check configuration.");
-
- // Make sure we only process handler requests.
- if (!boost::contains(targetURL, handlerURL))
- return make_pair(true, request.returnDecline());
-
- const PropertySet* sessionProps = app->getPropertySet("Sessions");
- if (!sessionProps)
- throw ConfigurationException("Unable to map request to application session settings, check configuration.");
-
- // Process incoming request.
- pair<bool,bool> handlerSSL = sessionProps->getBool("handlerSSL");
-
- // We dispatch based on our path info. We know the request URL begins with or equals the handler URL,
- // so the path info is the next character (or null).
- const Handler* handler = app->getHandler(targetURL.c_str() + strlen(handlerURL));
- if (!handler)
- throw ConfigurationException("Shibboleth handler invoked at an unconfigured location.");
-
- pair<bool, long> hret = handler->run(request);
- // Did the handler run successfully?
- if (hret.first)
- return hret;
- throw ConfigurationException("Configured Shibboleth handler failed to process the request.");
- }
- catch (const exception& e) {
- request.log(Priority::SHIB_ERROR, e.what());
- Session* session = nullptr;
- try {
- session = request.getSession(false, true, false); // do not cache
- }
- catch (const exception&) {
- }
- lock_guard<Session> slocker(*session, adopt_lock); // pop existing lock on exit
- return make_pair(true, handleError(log, request, session, &e));
- }
-}
diff --git a/shibsp/ServiceProvider.h b/shibsp/ServiceProvider.h
deleted file mode 100644
index d6017d68..00000000
--- a/shibsp/ServiceProvider.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file shibsp/ServiceProvider.h
- *
- * Interface to a Shibboleth ServiceProvider instance.
- */
-
-#ifndef __shibsp_sp_h__
-#define __shibsp_sp_h__
-
-#include <shibsp/util/PropertySet.h>
-
-#include <set>
-#include <vector>
-#include <xmltooling/Lockable.h>
-
-namespace shibsp {
-
- class SHIBSP_API Handler;
- class SHIBSP_API ListenerService;
- class SHIBSP_API Remoted;
- class SHIBSP_API RequestMapper;
- class SHIBSP_API SessionCache;
- class SHIBSP_API SPRequest;
- class SHIBSP_API TemplateParameters;
-
-#if defined (_MSC_VER)
- #pragma warning( push )
- #pragma warning( disable : 4251 )
-#endif
-
- /**
- * Interface to a Shibboleth ServiceProvider instance.
- *
- * <p>A ServiceProvider exposes configuration and infrastructure services required
- * by the SP implementation, allowing a flexible configuration format.
- */
- class SHIBSP_API ServiceProvider : public virtual xmltooling::Lockable, public virtual PropertySet
- {
- MAKE_NONCOPYABLE(ServiceProvider);
- protected:
- ServiceProvider();
- public:
- virtual ~ServiceProvider();
-
- /**
- * Loads a configuration and prepares the instance for use.
- *
- * <p>Implemented as a separate method so that services can rely on
- * other services while they initialize by accessing the ServiceProvider
- * from the SPConfig singleton.
- */
- virtual void init()=0;
-
- /**
- * Returns a SessionCache instance.
- *
- * @param required true iff an exception should be thrown if no SessionCache is available
- * @return a SessionCache
- */
- virtual SessionCache* getSessionCache(bool required=true) const=0;
-
- /**
- * Returns a RequestMapper instance.
- *
- * @param required true iff an exception should be thrown if no RequestMapper is available
- * @return a RequestMapper
- */
- virtual RequestMapper* getRequestMapper(bool required=true) const=0;
-
- /**
- * Enforces requirements for an authenticated session.
- *
- * <p>If the return value's first member is true, then request processing should terminate
- * with the second member as a status value. If false, processing can continue.
- *
- * @param request SP request interface
- * @param handler true iff a request to a registered Handler location can be directly executed
- * @return a pair containing a "request completed" indicator and a server-specific response code
- */
- virtual std::pair<bool,long> doAuthentication(SPRequest& request, bool handler=false) const;
-
- /**
- * Enforces authorization requirements based on the authenticated session.
- *
- * <p>If the return value's first member is true, then request processing should terminate
- * with the second member as a status value. If false, processing can continue.
- *
- * @param request SP request interface
- * @return a pair containing a "request completed" indicator and a server-specific response code
- */
- virtual std::pair<bool,long> doAuthorization(SPRequest& request) const;
-
- /**
- * Publishes session contents to the request in the form of headers or environment variables.
- *
- * <p>If the return value's first member is true, then request processing should terminate
- * with the second member as a status value. If false, processing can continue.
- *
- * @param request SP request interface
- * @param requireSession set to true iff an error should result if no session exists
- * @return a pair containing a "request completed" indicator and a server-specific response code
- */
- virtual std::pair<bool,long> doExport(SPRequest& request, bool requireSession=true) const;
-
- /**
- * Services requests for registered Handler locations.
- *
- * <p>If the return value's first member is true, then request processing should terminate
- * with the second member as a status value. If false, processing can continue.
- *
- * @param request SP request interface
- * @return a pair containing a "request completed" indicator and a server-specific response code
- */
- virtual std::pair<bool,long> doHandler(SPRequest& request) const;
-
- /**
- * Register for a message.
- *
- * @param address message address to register
- * @param svc pointer to remote service
- */
- virtual void regListener(const char* address, Remoted* svc)=0;
-
- /**
- * Unregisters service from an address, possibly restoring an original.
- *
- * @param address message address to modify
- * @param current pointer to unregistering service
- * @return true iff the current service was still registered
- */
- virtual bool unregListener(const char* address, Remoted* current)=0;
-
- /**
- * Returns current service registered at an address, if any.
- *
- * @param address message address to access
- * @return registered service, or nullptr
- */
- virtual Remoted* lookupListener(const char* address) const=0;
-
- protected:
- /** The AuthTypes to "recognize" (defaults to "shibboleth"). */
- std::set<std::string> m_authTypes;
- };
-
-#if defined (_MSC_VER)
- #pragma warning( pop )
-#endif
-
- /**
- * Registers ServiceProvider classes into the runtime.
- */
- void SHIBSP_API registerServiceProviders();
-
- /** SP based on integrated XML and native server configuration. */
- #define XML_SERVICE_PROVIDER "XML"
-};
-
-#endif /* __shibsp_sp_h__ */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list