[cpp-sp] branch main updated: Reimplement XML plugins on ptrees.
Scott Cantor
cantor.2 at osu.edu
Fri Dec 13 18:41:30 UTC 2024
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=e0d576981fe36ce333d24268aeaed4bde97423e4
The following commit(s) were added to refs/heads/main by this push:
new e0d57698 Reimplement XML plugins on ptrees.
e0d57698 is described below
commit e0d576981fe36ce333d24268aeaed4bde97423e4
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Fri Dec 13 13:41:19 2024 -0500
Reimplement XML plugins on ptrees.
---
apache/mod_shib_24.cpp | 253 ++++----
shibsp/AbstractSPRequest.cpp | 38 +-
shibsp/AbstractSPRequest.h | 24 +-
shibsp/AccessControl.h | 28 +-
shibsp/Agent.h | 2 +-
shibsp/RequestMapper.h | 8 +-
shibsp/ServiceProvider.cpp | 203 +++----
shibsp/ServiceProvider.h | 24 +-
shibsp/handler/AbstractHandler.h | 52 +-
shibsp/handler/impl/AbstractHandler.cpp | 55 +-
shibsp/handler/impl/AttributeCheckerHandler.cpp | 4 +-
shibsp/handler/impl/RemotedHandler.cpp | 30 +-
shibsp/handler/impl/SAMLDSSessionInitiator.cpp | 35 +-
shibsp/handler/impl/SessionHandler.cpp | 20 +-
shibsp/handler/impl/SessionInitiator.cpp | 10 +-
shibsp/handler/impl/StatusHandler.cpp | 6 +-
shibsp/impl/ChainingAccessControl.cpp | 134 +++--
shibsp/impl/XMLAccessControl.cpp | 358 ++++++-----
shibsp/impl/XMLRequestMapper.cpp | 761 +++++++++++-------------
shibsp/util/BoostPropertySet.cpp | 14 +
shibsp/util/BoostPropertySet.h | 10 +-
shibsp/util/PropertySet.h | 8 +
shibsp/util/ReloadableXMLFile.h | 6 +-
23 files changed, 996 insertions(+), 1087 deletions(-)
diff --git a/apache/mod_shib_24.cpp b/apache/mod_shib_24.cpp
index bea85278..5f46fa00 100644
--- a/apache/mod_shib_24.cpp
+++ b/apache/mod_shib_24.cpp
@@ -39,17 +39,16 @@
#include <shibsp/exceptions.h>
#include <shibsp/AbstractSPRequest.h>
#include <shibsp/AccessControl.h>
+#include <shibsp/AgentConfig.h>
#include <shibsp/RequestMapper.h>
#include <shibsp/SPConfig.h>
#include <shibsp/ServiceProvider.h>
#include <shibsp/SessionCache.h>
#include <shibsp/attribute/Attribute.h>
+#include <shibsp/util/Lockable.h>
#include <xercesc/util/XMLUniDefs.hpp>
-#include <xercesc/util/regx/RegularExpression.hpp>
#include <xmltooling/XMLToolingConfig.h>
-#include <xmltooling/util/ParserPool.h>
-#include <xmltooling/util/Threads.h>
#include <xmltooling/util/XMLConstants.h>
#include <xmltooling/util/XMLHelper.h>
@@ -63,8 +62,13 @@
#include <set>
#include <memory>
#include <fstream>
+#include <regex>
+#ifdef HAVE_CXX14
+# include <shared_mutex>
+#endif
#include <stdexcept>
#include <boost/lexical_cast.hpp>
+#include <boost/property_tree/xml_parser.hpp>
// Apache specific header files
#include <httpd.h>
@@ -89,10 +93,9 @@
using namespace shibsp;
using namespace xmltooling;
+using namespace boost::property_tree;
using namespace boost;
using namespace std;
-using xercesc::RegularExpression;
-using xercesc::XMLException;
extern "C" module AP_MODULE_DECLARE_DATA shib_module;
static int* const aplog_module_index = &(shib_module.module_index);
@@ -825,13 +828,11 @@ extern "C" int shib_fixups(request_rec* r)
// With 2.4+, we have to register individual methods to respond
// to each require rule we want to handle, and have those call
// into these methods directly.
-class htAccessControl : virtual public AccessControl
+class htAccessControl : virtual public AccessControl, public NoOpSharedLockable
{
public:
htAccessControl() {}
~htAccessControl() {}
- Lockable* lock() {return this;}
- void unlock() {}
aclresult_t authorized(const SPRequest& request, const Session* session) const;
aclresult_t doAccessControl(const ShibTargetApache& sta, const Session* session, const char* plugin) const;
@@ -840,7 +841,7 @@ public:
aclresult_t doShibAttr(const ShibTargetApache& sta, const Session* session, const char* rule, const char* params) const;
private:
- bool checkAttribute(const SPRequest& request, const Attribute* attr, const char* toMatch, RegularExpression* re) const;
+ bool checkAttribute(const SPRequest& request, const Attribute* attr, const char* toMatch, bool isRegex=false) const;
};
AccessControl* htAccessFactory(const xercesc::DOMElement* const &, bool)
@@ -852,21 +853,19 @@ AccessControl::aclresult_t htAccessControl::doAccessControl(const ShibTargetApac
{
aclresult_t result = shib_acl_false;
try {
- ifstream aclfile(plugin);
- if (!aclfile)
- throw ConfigurationException("Unable to open access control file ($1).", params(1, plugin));
- xercesc::DOMDocument* acldoc = XMLToolingConfig::getConfig().getParser().parse(aclfile);
- XercesJanitor<xercesc::DOMDocument> docjanitor(acldoc);
- static XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
- string t(XMLHelper::getAttrString(acldoc ? acldoc->getDocumentElement() : nullptr, nullptr, _type));
+ ptree pt;
+ xml_parser::read_xml(plugin, pt, xml_parser::no_comments|xml_parser::trim_whitespace);
+ string t = pt.get("<xmlattr>.type", "");
if (t.empty())
throw ConfigurationException("Missing type attribute in AccessControl plugin configuration.");
- scoped_ptr<AccessControl> aclplugin(SPConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acldoc->getDocumentElement(), true));
- Locker acllock(aclplugin.get());
+ unique_ptr<AccessControl> aclplugin(AgentConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), pt, true));
+#ifdef HAVE_CXX14
+ shared_lock<AccessControl> acllock(*aclplugin);
+#endif
result = aclplugin->authorized(sta, session);
}
- catch (std::exception& ex) {
- sta.log(SPRequest::SPError, ex.what());
+ catch (const xml_parser_error& e) {
+ sta.log(SPRequest::SPError, e.what());
}
return result;
}
@@ -893,16 +892,13 @@ AccessControl::aclresult_t htAccessControl::doUser(const ShibTargetApache& sta,
bool match = false;
if (regexp) {
try {
- // To do regex matching, we have to convert from UTF-8.
- auto_arrayptr<XMLCh> trans(fromUTF8(w));
- RegularExpression re(trans.get());
- auto_arrayptr<XMLCh> trans2(fromUTF8(sta.getRemoteUser().c_str()));
- match = re.matches(trans2.get());
+ // TODO: support regex options?
+ regex re(w);
+ match = regex_match(sta.getRemoteUser(), re);
}
- catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
+ catch (const regex_error& e) {
sta.log(SPRequest::SPError,
- string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + tmp.get());
+ string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + e.what());
}
}
else if (sta.getRemoteUser() == w) {
@@ -942,13 +938,13 @@ AccessControl::aclresult_t htAccessControl::doAuthnContext(const ShibTargetApach
bool match = false;
if (regexp) {
try {
- RegularExpression re(w);
- match = re.matches(ref);
+ // TODO: support regex options?
+ regex re(w);
+ match = regex_match(ref, re);
}
- catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
+ catch (const regex_error& e) {
sta.log(SPRequest::SPError,
- string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + tmp.get());
+ string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + e.what());
}
}
else if (!strcmp(w, ref)) {
@@ -970,17 +966,26 @@ AccessControl::aclresult_t htAccessControl::doAuthnContext(const ShibTargetApach
return shib_acl_false;
}
-bool htAccessControl::checkAttribute(const SPRequest& request, const Attribute* attr, const char* toMatch, RegularExpression* re) const
+bool htAccessControl::checkAttribute(const SPRequest& request, const Attribute* attr, const char* toMatch, bool isRegex) const
{
bool caseSensitive = attr->isCaseSensitive();
const vector<string>& vals = attr->getSerializedValues();
for (vector<string>::const_iterator v = vals.begin(); v != vals.end(); ++v) {
- if (re) {
- auto_arrayptr<XMLCh> trans(fromUTF8(v->c_str()));
- if (re->matches(trans.get())) {
- if (request.isPriorityEnabled(SPRequest::SPDebug))
- request.log(SPRequest::SPDebug, string("htaccess: expecting regexp ") + toMatch + ", got " + *v + ": accepted");
- return true;
+ if (isRegex) {
+ regex::flag_type flags = regex_constants::optimize;
+ if (!caseSensitive) {
+ flags |= regex_constants::icase;
+ }
+ try {
+ regex exp(toMatch, flags);
+ if (regex_match(*v, exp)) {
+ if (request.isPriorityEnabled(SPRequest::SPDebug))
+ request.log(SPRequest::SPDebug, string("htaccess: expecting regexp ") + toMatch + ", got " + *v + ": accepted");
+ return true;
+ }
+ } catch (const regex_error& e) {
+ request.log(SPRequest::SPError,
+ string("htaccess plugin caught exception while parsing regular expression (") + toMatch + "): " + e.what());
}
}
else if ((caseSensitive && *v == toMatch) || (!caseSensitive && !strcasecmp(v->c_str(), toMatch))) {
@@ -995,7 +1000,9 @@ bool htAccessControl::checkAttribute(const SPRequest& request, const Attribute*
return false;
}
-AccessControl::aclresult_t htAccessControl::doShibAttr(const ShibTargetApache& sta, const Session* session, const char* rule, const char* params) const
+AccessControl::aclresult_t htAccessControl::doShibAttr(
+ const ShibTargetApache& sta, const Session* session, const char* rule, const char* params
+ ) const
{
// Find the attribute(s) matching the require rule.
pair<multimap<string,const Attribute*>::const_iterator,multimap<string,const Attribute*>::const_iterator> attrs =
@@ -1009,23 +1016,11 @@ AccessControl::aclresult_t htAccessControl::doShibAttr(const ShibTargetApache& s
continue;
}
- try {
- scoped_ptr<RegularExpression> re;
- if (regexp) {
- auto_arrayptr<XMLCh> trans(fromUTF8(w));
- re.reset(new xercesc::RegularExpression(trans.get()));
+ pair<multimap<string,const Attribute*>::const_iterator,multimap<string,const Attribute*>::const_iterator> attrs2(attrs);
+ for (; attrs2.first != attrs2.second; ++attrs2.first) {
+ if (checkAttribute(sta, attrs2.first->second, w, regexp)) {
+ return shib_acl_true;
}
-
- pair<multimap<string,const Attribute*>::const_iterator,multimap<string,const Attribute*>::const_iterator> attrs2(attrs);
- for (; attrs2.first != attrs2.second; ++attrs2.first) {
- if (checkAttribute(sta, attrs2.first->second, w, regexp ? re.get() : nullptr)) {
- return shib_acl_true;
- }
- }
- }
- catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- sta.log(SPRequest::SPError, string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + tmp.get());
}
}
return shib_acl_false;
@@ -1037,138 +1032,126 @@ AccessControl::aclresult_t htAccessControl::authorized(const SPRequest& request,
throw ConfigurationException("Save my walrus!");
}
-class ApacheRequestMapper : public virtual RequestMapper, public virtual PropertySet
+class ApacheRequestMapper : public virtual RequestMapper, public virtual PropertySet2
{
public:
- ApacheRequestMapper(const xercesc::DOMElement* e, bool deprecationSupport=true);
+ ApacheRequestMapper(const ptree& pt, bool deprecationSupport=true);
~ApacheRequestMapper() {}
- Lockable* lock() { return m_mapper->lock(); }
- void unlock() { m_staKey->setData(nullptr); m_propsKey->setData(nullptr); m_mapper->unlock(); }
+ void lock_shared() { m_mapper->lock_shared(); }
+ bool try_lock_shared() { return m_mapper->try_lock_shared(); }
+ void unlock_shared() { m_sta = nullptr; m_props = nullptr; m_mapper->unlock_shared(); }
Settings getSettings(const HTTPRequest& request) const;
- const PropertySet* getParent() const { return nullptr; }
- void setParent(const PropertySet*) {}
- pair<bool,bool> getBool(const char* name) const;
- pair<bool,const char*> getString(const char* name) const;
- pair<bool,unsigned int> getUnsignedInt(const char* name) const;
- pair<bool,int> getInt(const char* name) const;
- const PropertySet* getPropertySet(const char* name) const;
+ bool hasProperty(const char* name) const;
+ bool getBool(const char* name, bool defaultValue) const;
+ const char* getString(const char* name, const char* defaultValue=nullptr) const;
+ unsigned int getUnsignedInt(const char* name, unsigned int defaultValue) const;
+ int getInt(const char* name, int defaultValue) const;
const htAccessControl& getHTAccessControl() const { return m_htaccess; }
private:
- scoped_ptr<RequestMapper> m_mapper;
- scoped_ptr<ThreadKey> m_staKey,m_propsKey;
+ unique_ptr<RequestMapper> m_mapper;
+ static thread_local const ShibTargetApache* m_sta;
+ static thread_local const PropertySet2* m_props;
mutable htAccessControl m_htaccess;
};
-RequestMapper* ApacheRequestMapFactory(const xercesc::DOMElement* const & e, bool deprecationSupport)
+RequestMapper* ApacheRequestMapFactory(const ptree& pt, bool deprecationSupport)
{
- return new ApacheRequestMapper(e, deprecationSupport);
+ return new ApacheRequestMapper(pt, deprecationSupport);
}
-ApacheRequestMapper::ApacheRequestMapper(const xercesc::DOMElement* e, bool deprecationSupport)
- : m_mapper(SPConfig::getConfig().RequestMapperManager.newPlugin(XML_REQUEST_MAPPER,e, deprecationSupport)),
- m_staKey(ThreadKey::create(nullptr)),
- m_propsKey(ThreadKey::create(nullptr))
+ApacheRequestMapper::ApacheRequestMapper(const ptree& pt, bool deprecationSupport)
+ : m_mapper(AgentConfig::getConfig().RequestMapperManager.newPlugin(XML_REQUEST_MAPPER, pt, deprecationSupport))
{
}
RequestMapper::Settings ApacheRequestMapper::getSettings(const HTTPRequest& request) const
{
Settings s = m_mapper->getSettings(request);
- m_staKey->setData((void*)dynamic_cast<const ShibTargetApache*>(&request));
- m_propsKey->setData((void*)s.first);
- return pair<const PropertySet*,AccessControl*>(this, s.second);
+ m_sta = dynamic_cast<const ShibTargetApache*>(&request);
+ m_props = s.first;
+ return make_pair(this, s.second);
}
-pair<bool,bool> ApacheRequestMapper::getBool(const char* name) const
+bool ApacheRequestMapper::getBool(const char* name, bool defaultValue) const
{
- const ShibTargetApache* sta=reinterpret_cast<const ShibTargetApache*>(m_staKey->getData());
- const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());
- if (sta) {
+ if (m_sta) {
// Override Apache-settable boolean properties.
- if (name && !strcmp(name,"requireSession") && sta->m_dc->bRequireSession != -1)
- return make_pair(true, sta->m_dc->bRequireSession==1);
- else if (name && !strcmp(name,"exportAssertion") && sta->m_dc->bExportAssertion != -1)
- return make_pair(true, sta->m_dc->bExportAssertion==1);
- else if (sta->m_dc->tSettings) {
- const char* prop = apr_table_get(sta->m_dc->tSettings, name);
+ if (name && !strcmp(name,"requireSession") && m_sta->m_dc->bRequireSession != -1)
+ return m_sta->m_dc->bRequireSession == 1;
+ else if (name && !strcmp(name,"exportAssertion") && m_sta->m_dc->bExportAssertion != -1)
+ return m_sta->m_dc->bExportAssertion == 1;
+ else if (m_sta->m_dc->tSettings) {
+ const char* prop = apr_table_get(m_sta->m_dc->tSettings, name);
if (prop)
- return make_pair(true, !strcmp(prop, "true") || !strcmp(prop, "1") || !strcmp(prop, "On"));
+ return !strcmp(prop, "true") || !strcmp(prop, "1") || !strcmp(prop, "On");
}
}
- return s && (!sta->m_dc->tUnsettings || !apr_table_get(sta->m_dc->tUnsettings, name)) ? s->getBool(name) : make_pair(false,false);
+ return m_props && (!m_sta->m_dc->tUnsettings || !apr_table_get(m_sta->m_dc->tUnsettings, name))
+ ? m_props->getBool(name, defaultValue) : defaultValue;
}
-pair<bool,const char*> ApacheRequestMapper::getString(const char* name) const
+const char* ApacheRequestMapper::getString(const char* name, const char* defaultValue) const
{
- const ShibTargetApache* sta=reinterpret_cast<const ShibTargetApache*>(m_staKey->getData());
- const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());
- if (sta) {
+ if (m_sta) {
// Override Apache-settable string properties.
if (name && !strcmp(name,"authType")) {
- const char* auth_type = ap_auth_type(sta->m_req);
+ const char* auth_type = ap_auth_type(m_sta->m_req);
if (auth_type) {
// Check for Basic Hijack
- if (!strcasecmp(auth_type, "basic") && sta->m_dc->bBasicHijack == 1)
+ if (!strcasecmp(auth_type, "basic") && m_sta->m_dc->bBasicHijack == 1)
auth_type = "shibboleth";
- return make_pair(true, auth_type);
+ return auth_type;
}
}
- else if (name && !strcmp(name,"applicationId") && sta->m_dc->szApplicationId)
- return pair<bool,const char*>(true,sta->m_dc->szApplicationId);
- else if (name && !strcmp(name,"requireSessionWith") && sta->m_dc->szRequireWith)
- return pair<bool,const char*>(true,sta->m_dc->szRequireWith);
- else if (name && !strcmp(name,"redirectToSSL") && sta->m_dc->szRedirectToSSL)
- return pair<bool,const char*>(true,sta->m_dc->szRedirectToSSL);
- else if (sta->m_dc->tSettings) {
- const char* prop = apr_table_get(sta->m_dc->tSettings, name);
+ else if (name && !strcmp(name,"applicationId") && m_sta->m_dc->szApplicationId)
+ return m_sta->m_dc->szApplicationId;
+ else if (name && !strcmp(name,"requireSessionWith") && m_sta->m_dc->szRequireWith)
+ return m_sta->m_dc->szRequireWith;
+ else if (name && !strcmp(name,"redirectToSSL") && m_sta->m_dc->szRedirectToSSL)
+ return m_sta->m_dc->szRedirectToSSL;
+ else if (m_sta->m_dc->tSettings) {
+ const char* prop = apr_table_get(m_sta->m_dc->tSettings, name);
if (prop)
- return make_pair(true, prop);
+ return prop;
}
}
- return s && (!sta->m_dc->tUnsettings || !apr_table_get(sta->m_dc->tUnsettings, name)) ? s->getString(name) : pair<bool,const char*>(false,nullptr);
+ return m_props && (!m_sta->m_dc->tUnsettings || !apr_table_get(m_sta->m_dc->tUnsettings, name))
+ ? m_props->getString(name, defaultValue) : defaultValue;
}
-pair<bool,unsigned int> ApacheRequestMapper::getUnsignedInt(const char* name) const
+unsigned int ApacheRequestMapper::getUnsignedInt(const char* name, unsigned int defaultValue) const
{
- const ShibTargetApache* sta=reinterpret_cast<const ShibTargetApache*>(m_staKey->getData());
- const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());
- if (sta) {
+ if (m_sta) {
// Override Apache-settable int properties.
- if (name && !strcmp(name,"redirectToSSL") && sta->m_dc->szRedirectToSSL)
- return pair<bool,unsigned int>(true, strtol(sta->m_dc->szRedirectToSSL, nullptr, 10));
- else if (sta->m_dc->tSettings) {
- const char* prop = apr_table_get(sta->m_dc->tSettings, name);
+ if (name && !strcmp(name,"redirectToSSL") && m_sta->m_dc->szRedirectToSSL)
+ return atoi(m_sta->m_dc->szRedirectToSSL);
+ else if (m_sta->m_dc->tSettings) {
+ const char* prop = apr_table_get(m_sta->m_dc->tSettings, name);
if (prop)
- return pair<bool,unsigned int>(true, atoi(prop));
+ return atoi(prop);
}
}
- return s && (!sta->m_dc->tUnsettings || !apr_table_get(sta->m_dc->tUnsettings, name)) ? s->getUnsignedInt(name) : pair<bool,unsigned int>(false,0);
+ return m_props && (!m_sta->m_dc->tUnsettings || !apr_table_get(m_sta->m_dc->tUnsettings, name))
+ ? m_props->getUnsignedInt(name, defaultValue) : defaultValue;
}
-pair<bool,int> ApacheRequestMapper::getInt(const char* name) const
+int ApacheRequestMapper::getInt(const char* name, int defaultValue) const
{
- const ShibTargetApache* sta=reinterpret_cast<const ShibTargetApache*>(m_staKey->getData());
- const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());
- if (sta) {
+ if (m_sta) {
// Override Apache-settable int properties.
- if (name && !strcmp(name,"redirectToSSL") && sta->m_dc->szRedirectToSSL)
- return pair<bool,int>(true,atoi(sta->m_dc->szRedirectToSSL));
- else if (sta->m_dc->tSettings) {
- const char* prop = apr_table_get(sta->m_dc->tSettings, name);
+ if (name && !strcmp(name,"redirectToSSL") && m_sta->m_dc->szRedirectToSSL)
+ return atoi(m_sta->m_dc->szRedirectToSSL);
+ else if (m_sta->m_dc->tSettings) {
+ const char* prop = apr_table_get(m_sta->m_dc->tSettings, name);
if (prop)
- return make_pair(true, atoi(prop));
+ return atoi(prop);
}
}
- return s && (!sta->m_dc->tUnsettings || !apr_table_get(sta->m_dc->tUnsettings, name)) ? s->getInt(name) : pair<bool,int>(false,0);
-}
-
-const PropertySet* ApacheRequestMapper::getPropertySet(const char* name) const
-{
- const PropertySet* s=reinterpret_cast<const PropertySet*>(m_propsKey->getData());
- return s ? s->getPropertySet(name) : nullptr;
+ return m_props && (!m_sta->m_dc->tUnsettings || !apr_table_get(m_sta->m_dc->tUnsettings, name))
+ ? m_props->getInt(name, defaultValue) : defaultValue;
}
// Authz callbacks for Apache 2.4
@@ -1478,7 +1461,7 @@ apr_status_t shib_post_config(apr_pool_t* p, apr_pool_t*, apr_pool_t*, server_re
return !OK;
}
- g_Config->RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, &ApacheRequestMapFactory);
+ AgentConfig::getConfig().RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, &ApacheRequestMapFactory);
// Set the cleanup handler, passing in the server_rec for logging.
apr_pool_cleanup_register(p, s, &shib_exit, apr_pool_cleanup_null);
diff --git a/shibsp/AbstractSPRequest.cpp b/shibsp/AbstractSPRequest.cpp
index baaba8d9..68f65ce9 100644
--- a/shibsp/AbstractSPRequest.cpp
+++ b/shibsp/AbstractSPRequest.cpp
@@ -1,21 +1,15 @@
/**
- * 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.
+ * 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
*
- * 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
*
- * 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.
+ * 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.
*/
/**
@@ -35,7 +29,6 @@
#include <boost/lexical_cast.hpp>
using namespace shibsp;
-using namespace xmltooling;
using namespace std;
SPRequest::SPRequest()
@@ -60,7 +53,7 @@ AbstractSPRequest::~AbstractSPRequest()
if (m_session)
m_session->unlock();
if (m_mapper)
- m_mapper->unlock();
+ m_mapper->unlock_shared();
if (m_sp)
m_sp->unlock();
}
@@ -75,7 +68,7 @@ RequestMapper::Settings AbstractSPRequest::getRequestSettings() const
if (!m_mapper) {
// Map request to application and content settings.
m_mapper = m_sp->getRequestMapper();
- m_mapper->lock();
+ m_mapper->lock_shared();
m_settings = m_mapper->getSettings(*this);
/*
@@ -93,7 +86,7 @@ const Application& AbstractSPRequest::getApplication() const
{
if (!m_app) {
// Now find the application from the URL settings
- m_app = m_sp->getApplication(getRequestSettings().first->getString("applicationId").second);
+ m_app = m_sp->getApplication(getRequestSettings().first->getString("applicationId"));
if (!m_app)
throw ConfigurationException("Unable to map non-default applicationId to an ApplicationOverride, check configuration.");
}
@@ -162,8 +155,8 @@ const char* AbstractSPRequest::getRequestURL() const
string AbstractSPRequest::getRemoteAddr() const
{
- pair<bool,const char*> addr = getRequestSettings().first->getString("REMOTE_ADDR");
- return addr.first ? getHeader(addr.second) : "";
+ const char* addr = getRequestSettings().first->getString("REMOTE_ADDR");
+ return addr ? getHeader(addr) : "";
}
const char* AbstractSPRequest::getParameter(const char* name) const
@@ -233,8 +226,7 @@ const char* AbstractSPRequest::getHandlerURL(const char* resource) const
}
else if (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)) {
throw ConfigurationException(
- "Invalid handlerURL property ($1) in <Sessions> element for Application ($2)",
- params(2, handler ? handler : "null", m_app->getId())
+ string("Invalid handlerURL property in <Sessions> element for Application ") + m_app->getId()
);
}
diff --git a/shibsp/AbstractSPRequest.h b/shibsp/AbstractSPRequest.h
index 48a2e282..79c7b0a0 100644
--- a/shibsp/AbstractSPRequest.h
+++ b/shibsp/AbstractSPRequest.h
@@ -1,21 +1,15 @@
/**
- * 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.
+ * 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
*
- * 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
*
- * 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.
+ * 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.
*/
/**
diff --git a/shibsp/AccessControl.h b/shibsp/AccessControl.h
index be1472b3..6c2f758f 100644
--- a/shibsp/AccessControl.h
+++ b/shibsp/AccessControl.h
@@ -1,21 +1,15 @@
/**
- * 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.
+ * 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
*
- * 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
*
- * 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.
+ * 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.
*/
/**
@@ -28,7 +22,7 @@
#define __shibsp_acl_h__
#include <shibsp/base.h>
-#include <xmltooling/Lockable.h>
+#include <shibsp/util/Lockable.h>
namespace shibsp {
@@ -42,7 +36,7 @@ namespace shibsp {
* of the resource request and the active session. They can be implemented through
* cross-platform or platform-specific mechanisms.
*/
- class SHIBSP_API AccessControl : public virtual xmltooling::Lockable
+ class SHIBSP_API AccessControl : public virtual SharedLockable
{
MAKE_NONCOPYABLE(AccessControl);
protected:
diff --git a/shibsp/Agent.h b/shibsp/Agent.h
index 5b81ae03..7797cc85 100644
--- a/shibsp/Agent.h
+++ b/shibsp/Agent.h
@@ -46,7 +46,7 @@ namespace shibsp {
* <p>A ServiceProvider exposes configuration and infrastructure services required
* by the SP implementation, allowing a flexible configuration format.
*/
- class SHIBSP_API Agent : public virtual SharedLockable, public virtual PropertySet
+ class SHIBSP_API Agent : public virtual SharedLockable, public virtual PropertySet2
{
MAKE_NONCOPYABLE(Agent);
protected:
diff --git a/shibsp/RequestMapper.h b/shibsp/RequestMapper.h
index 36c1e1fa..d8c945d8 100644
--- a/shibsp/RequestMapper.h
+++ b/shibsp/RequestMapper.h
@@ -28,13 +28,13 @@
#define __shibsp_reqmap_h__
#include <shibsp/base.h>
-#include <xmltooling/Lockable.h>
+#include <shibsp/util/Lockable.h>
namespace shibsp {
class SHIBSP_API AccessControl;
class SHIBSP_API HTTPRequest;
- class SHIBSP_API PropertySet;
+ class SHIBSP_API PropertySet2;
/**
* Interface to a request mapping plugin
@@ -42,7 +42,7 @@ namespace shibsp {
* Request mapping plugins return configuration settings that apply to resource requests.
* They can be implemented through cross-platform or platform-specific mechanisms.
*/
- class SHIBSP_API RequestMapper : public virtual xmltooling::Lockable
+ class SHIBSP_API RequestMapper : public virtual SharedLockable
{
MAKE_NONCOPYABLE(RequestMapper);
protected:
@@ -51,7 +51,7 @@ namespace shibsp {
virtual ~RequestMapper();
/** Combination of configuration settings and effective access control. */
- typedef std::pair<const PropertySet*,AccessControl*> Settings;
+ typedef std::pair<const PropertySet2*,AccessControl*> Settings;
/**
* Map request to settings.
diff --git a/shibsp/ServiceProvider.cpp b/shibsp/ServiceProvider.cpp
index 735cf2c4..b95d8fde 100644
--- a/shibsp/ServiceProvider.cpp
+++ b/shibsp/ServiceProvider.cpp
@@ -1,21 +1,15 @@
/**
- * 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.
+ * 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
*
- * 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
*
- * 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.
+ * 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.
*/
/**
@@ -40,9 +34,13 @@
#include <fstream>
#include <sstream>
+#ifdef HAVE_CXX14
+# include <shared_mutex>
+#endif
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
+// This is there until we figure out the TemplateEngine remediation/removal.
#include <xmltooling/XMLToolingConfig.h>
using namespace shibsp;
@@ -59,8 +57,8 @@ namespace shibsp {
// The properties we need can be set in the RequestMap, or the Errors element.
bool mderror = false;
bool accesserror = (strcmp(page, "access")==0);
- pair<bool,const char*> redirectErrors = pair<bool,const char*>(false,nullptr);
- pair<bool,const char*> pathname = pair<bool,const char*>(false,nullptr);
+ const char* redirectErrors = nullptr;
+ const char* pathname = nullptr;
// Strictly for error handling, detect a nullptr application and point at the default.
if (!app)
@@ -80,7 +78,7 @@ namespace shibsp {
RequestMapper::Settings settings = request.getRequestSettings();
if (mderror)
pathname = settings.first->getString("metadataError");
- if (!pathname.first) {
+ if (!pathname) {
string pagename(page);
pagename += "Error";
pathname = settings.first->getString(pagename.c_str());
@@ -94,10 +92,10 @@ namespace shibsp {
// Check for redirection on errors instead of template.
if (mayRedirect) {
- if (!redirectErrors.first && props)
- redirectErrors = props->getString("redirectErrors");
- if (redirectErrors.first) {
- string loc(redirectErrors.second);
+ if (!redirectErrors && props)
+ redirectErrors = props->getString("redirectErrors").second;
+ if (redirectErrors) {
+ string loc(redirectErrors);
request.absolutize(loc);
loc = loc + '?' + tp.toQueryString();
return request.sendRedirect(loc.c_str());
@@ -109,23 +107,23 @@ namespace shibsp {
request.setResponseHeader("Cache-Control","private,no-store,no-cache,max-age=0");
// Nothing in the request map, so check for a property named "page" in the Errors property set.
- if (!pathname.first && props) {
+ if (!pathname && props) {
if (mderror)
- pathname=props->getString("metadata");
- if (!pathname.first)
- pathname=props->getString(page);
+ pathname=props->getString("metadata").second;
+ if (!pathname)
+ pathname=props->getString(page).second;
}
// If there's still no template to use, just use pageError.html unless it's an access issue.
string fname;
- if (!pathname.first) {
+ if (!pathname) {
if (!accesserror) {
fname = string(mderror ? "metadata" : page) + "Error.html";
- pathname.second = fname.c_str();
+ pathname = fname.c_str();
}
}
else {
- fname = pathname.second;
+ fname = pathname;
}
// If we have a template to use, use it.
@@ -145,7 +143,7 @@ namespace shibsp {
return request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN);
}
- log.error("sendError could not process error template (%s)", pathname.second);
+ log.error("sendError could not process error template (%s)", pathname);
istringstream msg("Internal Server Error. Please contact the site administrator.");
return request.sendError(msg);
}
@@ -170,24 +168,21 @@ namespace shibsp {
void SHIBSP_DLLLOCAL exportAttributes(SPRequest& request, const Session* session, RequestMapper::Settings settings) {
- pair<bool,const char*> enc = settings.first->getString("encoding");
- if (enc.first && strcmp(enc.second, "URL"))
- throw ConfigurationException("Unsupported value for 'encoding' content setting ($1).", params(1,enc.second));
+ 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.
- pair<bool,const char*> delim = settings.first->getString("attributeValueDelimiter");
- if (enc.first || !delim.first) {
- delim.second = ";";
- }
- size_t delim_len = strlen(delim.second);
+ const char* delim = settings.first->getString("attributeValueDelimiter", ";");
+ size_t delim_len = strlen(delim);
- pair<bool,bool> exportDups = settings.first->getBool("exportDuplicateValues");
+ bool exportDups = settings.first->getBool("exportDuplicateValues", true);
const multimap<string,const Attribute*>& attributes = session->getIndexedAttributes();
// Default export strategy will include duplicates.
- if (!exportDups.first || exportDups.second) {
+ if (exportDups) {
for (multimap<string,const Attribute*>::const_iterator a = attributes.begin(); a != attributes.end(); ++a) {
if (a->second->isInternal())
continue;
@@ -195,16 +190,16 @@ namespace shibsp {
const vector<string>& vals = a->second->getSerializedValues();
for (vector<string>::const_iterator v = vals.begin(); v != vals.end(); ++v) {
if (!header.empty())
- header += delim.second;
- if (enc.first) {
+ 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.second, string::size_type(0));
+ 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.second, pos)) {
+ for (; pos != string::npos; pos = value.find(delim, pos)) {
value.insert(pos, "\\");
pos += delim_len + 1;
}
@@ -233,16 +228,16 @@ namespace shibsp {
string header;
for (set<string>::const_iterator v = deduped->second.begin(); v != deduped->second.end(); ++v) {
if (!header.empty())
- header += delim.second;
- if (enc.first) {
+ 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.second, string::size_type(0));
+ 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.second, pos)) {
+ for (; pos != string::npos; pos = value.find(delim, pos)) {
value.insert(pos, "\\");
pos += delim_len + 1;
}
@@ -266,7 +261,7 @@ namespace shibsp {
for (; matches.first != matches.second; ++matches.first) {
const vector<string>& vals = matches.first->second->getSerializedValues();
if (!vals.empty()) {
- if (enc.first)
+ if (enc)
request.setRemoteUser(encoder.encode(vals.front().c_str()).c_str());
else
request.setRemoteUser(vals.front().c_str());
@@ -305,8 +300,8 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
// If not SSL, check to see if we should block or redirect it.
if (!request.isSecure()) {
- pair<bool,const char*> redirectToSSL = settings.first->getString("redirectToSSL");
- if (redirectToSSL.first) {
+ const char* redirectToSSL = settings.first->getString("redirectToSSL");
+ if (redirectToSSL) {
#ifdef HAVE_STRCASECMP
if (!strcasecmp("GET",request.getMethod()) || !strcasecmp("HEAD",request.getMethod())) {
#else
@@ -314,8 +309,8 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
#endif
// Compute the new target URL
string redirectURL = string("https://") + request.getHostname();
- if (strcmp(redirectToSSL.second,"443")) {
- redirectURL = redirectURL + ':' + redirectToSSL.second;
+ if (strcmp(redirectToSSL,"443")) {
+ redirectURL = redirectURL + ':' + redirectToSSL;
}
redirectURL += request.getRequestURI();
return make_pair(true, request.sendRedirect(redirectURL.c_str()));
@@ -342,16 +337,16 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
}
// These settings dictate how to proceed.
- pair<bool,const char*> authType = settings.first->getString("authType");
- pair<bool,bool> requireSession = settings.first->getBool("requireSession");
- pair<bool,const char*> requireSessionWith = settings.first->getString("requireSessionWith");
- pair<bool,const char*> requireLogoutWith = settings.first->getString("requireLogoutWith");
+ 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.first || !requireSession.second) && !requireSessionWith.first &&
- (!authType.first || m_authTypes.find(boost::to_lower_copy(string(authType.second))) == m_authTypes.end()))
+ 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
@@ -370,7 +365,7 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
Locker slocker(session, false); // pop existing lock on exit
if (session) {
// Check for logout interception.
- if (requireLogoutWith.first) {
+ if (requireLogoutWith) {
// Check for a completion parameter on the query string.
const char* qstr = request.getQueryString();
if (!qstr || !strstr(qstr, "shiblogoutdone=1")) {
@@ -381,7 +376,7 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
else
selfurl += '?';
selfurl += "shiblogoutdone=1";
- string loc = requireLogoutWith.second;
+ string loc(requireLogoutWith);
request.absolutize(loc);
if (loc.find('?') != string::npos)
loc += '&';
@@ -395,20 +390,18 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
}
else {
// No session. Maybe that's acceptable?
- if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first) {
+ 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.first) {
+ if (requireSessionWith) {
SPConfig::getConfig().deprecation().warn("requireSessionWith");
- initiator=app->getSessionInitiatorById(requireSessionWith.second);
+ initiator=app->getSessionInitiatorById(requireSessionWith);
if (!initiator) {
- throw ConfigurationException(
- "No session initiator found with id ($1), check requireSessionWith setting.", params(1, requireSessionWith.second)
- );
+ throw ConfigurationException(string("No session initiator found with id: ") + requireSessionWith);
}
}
else {
@@ -419,20 +412,13 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
// 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.
- try {
- 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.");
- }
- catch (XMLToolingException& ex) {
- if (!ex.getProperty("eventType") && initiator->getEventType())
- ex.addProperty("eventType", initiator->getEventType());
- throw;
- }
+ 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.second);
+ request.setAuthType(authType);
// We're done. Everything is okay. Nothing to report. Nothing to do..
// Let the caller decide how to proceed.
@@ -461,15 +447,15 @@ pair<bool,long> ServiceProvider::doAuthorization(SPRequest& request) const
app = &(request.getApplication());
// Three settings dictate how to proceed.
- pair<bool,const char*> authType = settings.first->getString("authType");
- pair<bool,bool> requireSession = settings.first->getBool("requireSession");
- pair<bool,const char*> requireSessionWith = settings.first->getString("requireSessionWith");
+ 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.first || !requireSession.second) && !requireSessionWith.first &&
- (!authType.first || m_authTypes.find(boost::to_lower_copy(string(authType.second))) == m_authTypes.end()))
+ 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?
@@ -483,7 +469,9 @@ pair<bool,long> ServiceProvider::doAuthorization(SPRequest& request) const
log.warn("unable to obtain session to pass to access control provider: %s", e.what());
}
- Locker acllock(settings.second);
+#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");
@@ -555,8 +543,8 @@ pair<bool,long> ServiceProvider::doExport(SPRequest& request, bool requireSessio
// 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.
- pair<bool,bool> stdvars = settings.first->getBool("exportStdVars");
- if (!stdvars.first || stdvars.second) {
+ bool stdvars = settings.first->getBool("exportStdVars", true);
+ if (stdvars) {
const char* hval = session->getEntityID();
if (hval)
app->setHeader(request, "Shib-Identity-Provider", hval);
@@ -583,25 +571,27 @@ pair<bool,long> ServiceProvider::doExport(SPRequest& request, bool requireSessio
}
// Check for export of algorithmically-derived portion of cookie names.
- stdvars = settings.first->getBool("exportCookie");
- if (stdvars.first && stdvars.second) {
+ 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());
}
// Maybe export the assertion keys.
- pair<bool,bool> exp = settings.first->getBool("exportAssertion");
- if (exp.first && exp.second) {
- pair<bool,const char*> exportLocation = sessionProps ? sessionProps->getString("exportLocation") : pair<bool,const char*>(false,nullptr);
+ bool exportAssertion = settings.first->getBool("exportAssertion", false);
+ if (exportAssertion) {
+ pair<bool,const char*> exportLocation = sessionProps ? sessionProps->getString("exportLocation") : make_pair(false,nullptr);
if (!exportLocation.first)
log.warn("can't export assertions without an exportLocation Sessions property");
else {
string exportName = "Shib-Assertion-00";
string baseURL;
- if (!strncmp(exportLocation.second, "http", 4))
+ if (!strncmp(exportLocation.second, "http", 4)) {
baseURL = exportLocation.second;
- else
+ }
+ else {
baseURL = string(request.getHandlerURL(targetURL.c_str())) + exportLocation.second;
+ }
baseURL = baseURL + "?key=" + session->getID() + "&ID=";
const vector<const char*>& tokens = session->getAssertionIDs();
vector<const char*>::size_type count = 0;
@@ -642,8 +632,8 @@ pair<bool,long> ServiceProvider::doHandler(SPRequest& request) const
// If not SSL, check to see if we should block or redirect it.
if (!request.isSecure()) {
- pair<bool,const char*> redirectToSSL = settings.first->getString("redirectToSSL");
- if (redirectToSSL.first) {
+ const char* redirectToSSL = settings.first->getString("redirectToSSL");
+ if (redirectToSSL) {
#ifdef HAVE_STRCASECMP
if (!strcasecmp("GET",request.getMethod()) || !strcasecmp("HEAD",request.getMethod())) {
#else
@@ -651,8 +641,8 @@ pair<bool,long> ServiceProvider::doHandler(SPRequest& request) const
#endif
// Compute the new target URL
string redirectURL = string("https://") + request.getHostname();
- if (strcmp(redirectToSSL.second,"443")) {
- redirectURL = redirectURL + ':' + redirectToSSL.second;
+ if (strcmp(redirectToSSL,"443")) {
+ redirectURL = redirectURL + ':' + redirectToSSL;
}
redirectURL += request.getRequestURI();
return make_pair(true, request.sendRedirect(redirectURL.c_str()));
@@ -690,18 +680,11 @@ pair<bool,long> ServiceProvider::doHandler(SPRequest& request) const
if (!handler)
throw ConfigurationException("Shibboleth handler invoked at an unconfigured location.");
- try {
- 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 (XMLToolingException& ex) {
- if (!ex.getProperty("eventType") && handler->getEventType())
- ex.addProperty("eventType", handler->getEventType());
- throw;
- }
+ 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(SPRequest::SPError, e.what());
diff --git a/shibsp/ServiceProvider.h b/shibsp/ServiceProvider.h
index d52b7e08..41d68d57 100644
--- a/shibsp/ServiceProvider.h
+++ b/shibsp/ServiceProvider.h
@@ -1,21 +1,15 @@
/**
- * 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.
+ * 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
*
- * 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
*
- * 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.
+ * 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.
*/
/**
diff --git a/shibsp/handler/AbstractHandler.h b/shibsp/handler/AbstractHandler.h
index 60a29057..3b0bba11 100644
--- a/shibsp/handler/AbstractHandler.h
+++ b/shibsp/handler/AbstractHandler.h
@@ -1,21 +1,15 @@
/**
- * 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.
+ * 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
*
- * 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
*
- * 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.
+ * 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.
*/
/**
@@ -193,19 +187,21 @@ namespace shibsp {
/**
* Returns a boolean-valued property.
*
- * @param name property name
- * @param request reference to incoming request
- * @param type bitmask of property sources to use
+ * @param name property name
+ * @param request reference to incoming request
+ * @param type bitmask of property sources to use
* @return a pair consisting of a nullptr indicator and the property value iff the indicator is true
*/
- std::pair<bool,bool> getBool(const char* name, const HTTPRequest& request, unsigned int type=HANDLER_PROPERTY_ALL) const;
+ std::pair<bool,bool> getBool(
+ const char* name, const HTTPRequest& request, unsigned int type=HANDLER_PROPERTY_ALL
+ ) const;
/**
* Returns a string-valued property.
*
- * @param name property name
- * @param request reference to incoming request
- * @param type bitmask of property sources to use
+ * @param name property name
+ * @param request reference to incoming request
+ * @param type bitmask of property sources to use
* @return a pair consisting of a nullptr indicator and the property value iff the indicator is true
*/
std::pair<bool,const char*> getString(const char* name, const HTTPRequest& request, unsigned int type=HANDLER_PROPERTY_ALL) const;
@@ -213,9 +209,9 @@ namespace shibsp {
/**
* Returns an unsigned integer-valued property.
*
- * @param name property name
- * @param request reference to incoming request
- * @param type bitmask of property sources to use
+ * @param name property name
+ * @param request reference to incoming request
+ * @param type bitmask of property sources to use
* @return a pair consisting of a nullptr indicator and the property value iff the indicator is true
*/
std::pair<bool,unsigned int> getUnsignedInt(const char* name, const HTTPRequest& request, unsigned int type=HANDLER_PROPERTY_ALL) const;
@@ -223,9 +219,9 @@ namespace shibsp {
/**
* Returns an integer-valued property.
*
- * @param name property name
- * @param request reference to incoming request
- * @param type bitmask of property sources to use
+ * @param name property name
+ * @param request reference to incoming request
+ * @param type bitmask of property sources to use
* @return a pair consisting of a nullptr indicator and the property value iff the indicator is true
*/
std::pair<bool,int> getInt(const char* name, const HTTPRequest& request, unsigned int type=HANDLER_PROPERTY_ALL) const;
diff --git a/shibsp/handler/impl/AbstractHandler.cpp b/shibsp/handler/impl/AbstractHandler.cpp
index d617661e..47c904f1 100644
--- a/shibsp/handler/impl/AbstractHandler.cpp
+++ b/shibsp/handler/impl/AbstractHandler.cpp
@@ -1,25 +1,19 @@
/**
- * 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.
+ * 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
*
- * 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
*
- * 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.
+ * 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.
*/
/**
- * AbstractHandler.cpp
+ * handler/impl/AbstractHandler.cpp
*
* Base class for handlers based on a DOMPropertySet.
*/
@@ -38,6 +32,7 @@
#include "util/SPConstants.h"
#include "util/PathResolver.h"
#include "util/TemplateParameters.h"
+#include "util/URLEncoder.h"
#include <vector>
#include <fstream>
@@ -49,7 +44,6 @@
#include <xmltooling/util/URLEncoder.h>
#include <xmltooling/XMLToolingConfig.h>
-#include <xmltooling/util/URLEncoder.h>
using namespace shibsp;
using namespace xmltooling;
@@ -804,9 +798,10 @@ pair<bool,bool> AbstractHandler::getBool(const char* name, const HTTPRequest& re
const SPRequest* sprequest = dynamic_cast<const SPRequest*>(&request);
if (sprequest && (type & HANDLER_PROPERTY_MAP)) {
- pair<bool,bool> ret = sprequest->getRequestSettings().first->getBool(name);
- if (ret.first)
- return ret;
+ if (sprequest->getRequestSettings().first->hasProperty(name)) {
+ // The default won't matter since we've already verified the property "exists".
+ return make_pair(true, sprequest->getRequestSettings().first->getBool(name, false));
+ }
}
if (type & HANDLER_PROPERTY_FIXED) {
@@ -826,9 +821,9 @@ pair<bool,const char*> AbstractHandler::getString(const char* name, const HTTPRe
const SPRequest* sprequest = dynamic_cast<const SPRequest*>(&request);
if (sprequest && (type & HANDLER_PROPERTY_MAP)) {
- pair<bool,const char*> ret = sprequest->getRequestSettings().first->getString(name);
- if (ret.first)
- return ret;
+ const char* ret = sprequest->getRequestSettings().first->getString(name);
+ if (ret)
+ return make_pair(true, ret);
}
if (type & HANDLER_PROPERTY_FIXED) {
@@ -854,9 +849,10 @@ pair<bool,unsigned int> AbstractHandler::getUnsignedInt(const char* name, const
const SPRequest* sprequest = dynamic_cast<const SPRequest*>(&request);
if (sprequest && (type & HANDLER_PROPERTY_MAP)) {
- pair<bool,unsigned int> ret = sprequest->getRequestSettings().first->getUnsignedInt(name);
- if (ret.first)
- return ret;
+ if (sprequest->getRequestSettings().first->hasProperty(name)) {
+ // The default won't matter since we've already verified the property "exists".
+ return make_pair(true, sprequest->getRequestSettings().first->getUnsignedInt(name, 0));
+ }
}
if (type & HANDLER_PROPERTY_FIXED) {
@@ -876,9 +872,10 @@ pair<bool,int> AbstractHandler::getInt(const char* name, const HTTPRequest& requ
const SPRequest* sprequest = dynamic_cast<const SPRequest*>(&request);
if (sprequest && (type & HANDLER_PROPERTY_MAP)) {
- pair<bool,int> ret = sprequest->getRequestSettings().first->getInt(name);
- if (ret.first)
- return ret;
+ if (sprequest->getRequestSettings().first->hasProperty(name)) {
+ // The default won't matter since we've already verified the property "exists".
+ return make_pair(true, sprequest->getRequestSettings().first->getInt(name, 0));
+ }
}
if (type & HANDLER_PROPERTY_FIXED) {
diff --git a/shibsp/handler/impl/AttributeCheckerHandler.cpp b/shibsp/handler/impl/AttributeCheckerHandler.cpp
index 308af6dc..f74bdac0 100644
--- a/shibsp/handler/impl/AttributeCheckerHandler.cpp
+++ b/shibsp/handler/impl/AttributeCheckerHandler.cpp
@@ -37,11 +37,11 @@
#include "util/PathResolver.h"
#include "util/TemplateParameters.h"
+#include <memory>
#include <fstream>
#include <sstream>
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/bind.hpp>
-#include <boost/scoped_ptr.hpp>
#include <boost/algorithm/string.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xmltooling/XMLToolingConfig.h>
@@ -89,7 +89,7 @@ namespace shibsp {
string m_template;
bool m_flushSession;
vector<string> m_attributes;
- scoped_ptr<AccessControl> m_acl;
+ unique_ptr<AccessControl> m_acl;
};
#if defined (_MSC_VER)
diff --git a/shibsp/handler/impl/RemotedHandler.cpp b/shibsp/handler/impl/RemotedHandler.cpp
index 20290c06..90c732aa 100644
--- a/shibsp/handler/impl/RemotedHandler.cpp
+++ b/shibsp/handler/impl/RemotedHandler.cpp
@@ -1,21 +1,15 @@
/**
- * 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.
+ * 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
*
- * 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
*
- * 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.
+ * 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.
*/
/**
@@ -311,9 +305,9 @@ void RemotedHandler::addRemotedHeader(const char* header)
DDF RemotedHandler::send(const SPRequest& request, DDF& in) const
{
// Capture and forward entityIDSelf content setting, if set.
- pair<bool, const char*> entityID = request.getRequestSettings().first->getString("entityIDSelf");
- if (entityID.first) {
- string s(entityID.second);
+ const char* entityID = request.getRequestSettings().first->getString("entityIDSelf");
+ if (entityID) {
+ string s(entityID);
string::size_type pos = s.find("$hostname");
if (pos != string::npos)
s.replace(pos, 9, request.getHostname());
diff --git a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp
index 7bbfa744..132263df 100644
--- a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp
+++ b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp
@@ -1,21 +1,15 @@
/**
- * 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.
+ * 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
*
- * 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
*
- * 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.
+ * 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.
*/
/**
@@ -144,7 +138,10 @@ pair<bool,long> SAMLDSSessionInitiator::run(SPRequest& request, string& entityID
pair<bool,bool> passopt = getBool("isPassive", request, HANDLER_PROPERTY_MAP|HANDLER_PROPERTY_FIXED);
isPassive = passopt.first && passopt.second;
- discoveryURL = request.getRequestSettings().first->getString("discoveryURL");
+ discoveryURL.second = request.getRequestSettings().first->getString("discoveryURL");
+ if (discoveryURL.second) {
+ discoveryURL.first = true;
+ }
}
if (!discoveryURL.first)
@@ -214,9 +211,9 @@ pair<bool,long> SAMLDSSessionInitiator::run(SPRequest& request, string& entityID
returnURL = returnURL + "&target=" + urlenc->encode(target.c_str());
// Preserve designated request settings on the URL.
for (vector<string>::const_iterator opt = m_preservedOptions.begin(); opt != m_preservedOptions.end(); ++ opt) {
- prop = request.getRequestSettings().first->getString(opt->c_str());
- if (prop.first)
- returnURL = returnURL + '&' + (*opt) + '=' + urlenc->encode(prop.second);
+ const char* optval = request.getRequestSettings().first->getString(opt->c_str());
+ if (optval)
+ returnURL = returnURL + '&' + (*opt) + '=' + urlenc->encode(optval);
}
}
diff --git a/shibsp/handler/impl/SessionHandler.cpp b/shibsp/handler/impl/SessionHandler.cpp
index 8b11c5dd..558353d1 100644
--- a/shibsp/handler/impl/SessionHandler.cpp
+++ b/shibsp/handler/impl/SessionHandler.cpp
@@ -174,8 +174,8 @@ pair<bool,long> SessionHandler::doJSON(SPRequest& request) const
json_safe(s, session->getProtocol());
}
- pair<bool,bool> stdvars = request.getRequestSettings().first->getBool("exportStdVars");
- if (!stdvars.first || stdvars.second) {
+ bool stdvars = request.getRequestSettings().first->getBool("exportStdVars", true);
+ if (stdvars) {
if (session->getEntityID()) {
s << ", \"identity_provider\": ";
json_safe(s, session->getEntityID());
@@ -287,8 +287,8 @@ pair<bool,long> SessionHandler::doHTML(SPRequest& request) const
s << "<strong>Client Address:</strong> " << (session->getClientAddress() ? session->getClientAddress() : "(none)") << endl;
s << "<strong>SSO Protocol:</strong> " << (session->getProtocol() ? session->getProtocol() : "(none)") << endl;
- pair<bool,bool> stdvars = request.getRequestSettings().first->getBool("exportStdVars");
- if (!stdvars.first || stdvars.second) {
+ bool stdvars = request.getRequestSettings().first->getBool("exportStdVars", true);
+ if (stdvars) {
s << "<strong>Identity Provider:</strong> " << (session->getEntityID() ? session->getEntityID() : "(none)") << endl;
s << "<strong>Authentication Time:</strong> " << (session->getAuthnInstant() ? session->getAuthnInstant() : "(none)") << endl;
s << "<strong>Authentication Context Class:</strong> " << (session->getAuthnContextClassRef() ? session->getAuthnContextClassRef() : "(none)") << endl;
@@ -315,19 +315,17 @@ pair<bool,long> SessionHandler::doHTML(SPRequest& request) const
if (m_values) {
// Default delimiter is semicolon but is now configurable.
- pair<bool,const char*> delim = request.getRequestSettings().first->getString("attributeValueDelimiter");
- if (!delim.first)
- delim.second = ";";
- size_t delim_len = strlen(delim.second);
+ const char* delim = request.getRequestSettings().first->getString("attributeValueDelimiter", ";");
+ size_t delim_len = strlen(delim);
const vector<string>& vals = a->second->getSerializedValues();
for (vector<string>::const_iterator v = vals.begin(); v!=vals.end(); ++v) {
if (v != vals.begin() || a->first == key)
- s << delim.second;
- string::size_type pos = v->find(delim.second, string::size_type(0));
+ s << delim;
+ 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.second, pos)) {
+ for (; pos != string::npos; pos = value.find(delim, pos)) {
value.insert(pos, "\\");
pos += delim_len + 1;
}
diff --git a/shibsp/handler/impl/SessionInitiator.cpp b/shibsp/handler/impl/SessionInitiator.cpp
index bef97f0c..47abb0e1 100644
--- a/shibsp/handler/impl/SessionInitiator.cpp
+++ b/shibsp/handler/impl/SessionInitiator.cpp
@@ -86,7 +86,11 @@ bool SessionInitiator::checkCompatibility(SPRequest& request, bool isHandler) co
}
else {
// It doesn't really make sense to use isPassive with automated sessions, but...
- pair<bool,bool> flagprop = request.getRequestSettings().first->getBool("isPassive");
+ pair<bool,bool> flagprop;
+ if (request.getRequestSettings().first->hasProperty("isPassive")) {
+ flagprop.second = request.getRequestSettings().first->getBool("isPassive", false);
+ flagprop.first = true;
+ }
if (!flagprop.first)
flagprop = getBool("isPassive");
isPassive = (flagprop.first && flagprop.second);
@@ -116,8 +120,8 @@ pair<bool,long> SessionInitiator::run(SPRequest& request, bool isHandler) const
entityID=request.getParameter("providerId");
}
if (!entityID || !*entityID) {
- param = request.getRequestSettings().first->getString("entityID");
- if (param.first)
+ param.second = request.getRequestSettings().first->getString("entityID");
+ if (param.second)
entityID = param.second;
}
if (!entityID || !*entityID)
diff --git a/shibsp/handler/impl/StatusHandler.cpp b/shibsp/handler/impl/StatusHandler.cpp
index 261bcd48..d0ae6bf1 100644
--- a/shibsp/handler/impl/StatusHandler.cpp
+++ b/shibsp/handler/impl/StatusHandler.cpp
@@ -256,9 +256,9 @@ pair<bool,long> StatusHandler::run(SPRequest& request, bool isHandler) const
const char* setting = request.getParameter("setting");
systemInfo(msg) << "<RequestSettings";
if (setting) {
- pair<bool, const char*> prop = settings.first->getString(setting);
- if (prop.first)
- msg << ' ' << setting << "='" << prop.second << "'";
+ const char* prop = settings.first->getString(setting);
+ if (prop)
+ msg << ' ' << setting << "='" << prop << "'";
}
msg << '>' << target << "</RequestSettings>";
msg << "<Status><OK/></Status>";
diff --git a/shibsp/impl/ChainingAccessControl.cpp b/shibsp/impl/ChainingAccessControl.cpp
index 01d86c0c..1632d91a 100644
--- a/shibsp/impl/ChainingAccessControl.cpp
+++ b/shibsp/impl/ChainingAccessControl.cpp
@@ -1,65 +1,74 @@
/**
- * 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.
+ * 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
*
- * 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
*
- * 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.
+ * 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.
*/
/**
- * ChainingAccessControl.cpp
+ * impl/ChainingAccessControl.cpp
*
* Access control plugin that combines other plugins.
*/
#include "internal.h"
#include "exceptions.h"
+
#include "AccessControl.h"
+#include "AgentConfig.h"
#include "SessionCache.h"
#include "SPRequest.h"
#include <algorithm>
#include <memory>
#include <vector>
-
-#include <xmltooling/unicode.h>
-#include <xmltooling/util/XMLHelper.h>
-#include <xercesc/util/XMLUniDefs.hpp>
+#include <boost/property_tree/ptree.hpp>
using namespace shibsp;
-using namespace xmltooling;
+using namespace boost::property_tree;
using namespace std;
namespace shibsp {
+ extern AccessControl* SHIBSP_DLLLOCAL XMLAccessControlFactory(const ptree& pt, bool deprecationSupport);
+}
+
+AccessControl::AccessControl()
+{
+}
+
+AccessControl::~AccessControl()
+{
+}
+
+
+namespace {
class ChainingAccessControl : public AccessControl
{
public:
- ChainingAccessControl(const DOMElement* e, bool deprecationSupport);
+ ChainingAccessControl(const ptree& pt, bool deprecationSupport);
~ChainingAccessControl() {}
- Lockable* lock() {
+ void lock_shared() {
for (auto& i : m_ac) {
- i->lock();
+ i->lock_shared();
}
- return this;
}
- void unlock() {
+ bool try_lock_shared() {
+ // This shouldn't be needed, so just fail it.
+ return false;
+ }
+ void unlock_shared() {
for (auto& i : m_ac) {
- i->unlock();
+ i->unlock_shared();
}
}
@@ -70,56 +79,59 @@ namespace shibsp {
vector<unique_ptr<AccessControl>> m_ac;
};
- AccessControl* SHIBSP_DLLLOCAL ChainingAccessControlFactory(const DOMElement* const & e, bool deprecationSupport)
+ AccessControl* SHIBSP_DLLLOCAL ChainingAccessControlFactory(const ptree& pt, bool deprecationSupport)
{
- return new ChainingAccessControl(e, deprecationSupport);
+ return new ChainingAccessControl(pt, deprecationSupport);
}
-
- static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);
- static const XMLCh _operator[] = UNICODE_LITERAL_8(o,p,e,r,a,t,o,r);
- static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
- static const XMLCh AND[] = UNICODE_LITERAL_3(A,N,D);
- static const XMLCh OR[] = UNICODE_LITERAL_2(O,R);
-
- extern AccessControl* SHIBSP_DLLLOCAL XMLAccessControlFactory(const DOMElement* const & e, bool);
}
void SHIBSP_API shibsp::registerAccessControls()
{
- SPConfig& conf=SPConfig::getConfig();
+ AgentConfig& conf=AgentConfig::getConfig();
conf.AccessControlManager.registerFactory(CHAINING_ACCESS_CONTROL, ChainingAccessControlFactory);
conf.AccessControlManager.registerFactory(XML_ACCESS_CONTROL, XMLAccessControlFactory);
}
-AccessControl::AccessControl()
+ChainingAccessControl::ChainingAccessControl(const ptree& pt, bool deprecationSupport) : m_op(OP_AND)
{
-}
+ static const char OPERATOR_PROP_PATH[] = "<xmlattr>.operator";
+ static const char AND_OPERATOR[] = "AND";
+ static const char OR_OPERATOR[] = "OR";
+ const boost::optional<string> op = pt.get_optional<string>(OPERATOR_PROP_PATH);
+ if (!op) {
+ throw ConfigurationException("Missing operator in Chaining AccessControl configuration.");
+ }
+ else if (op.get() == OR_OPERATOR) {
+ m_op = OP_OR;
+ } else if (op.get() == AND_OPERATOR) {
+ m_op = OP_AND;
+ } else {
+ throw ConfigurationException("Unsupported operator in Chaining AccessControl configuration.");
+ }
-AccessControl::~AccessControl()
-{
-}
+ Category& log = Category::getInstance(SHIBSP_LOGCAT ".AccessControl.Chaining");
-ChainingAccessControl::ChainingAccessControl(const DOMElement* e, bool deprecationSupport) : m_op(OP_AND)
-{
- const XMLCh* op = e ? e->getAttributeNS(nullptr, _operator) : nullptr;
- if (XMLString::equals(op, OR))
- m_op = OP_OR;
- else if (op && *op && !XMLString::equals(op, AND))
- throw ConfigurationException("Missing or unrecognized operator in Chaining AccessControl configuration.");
-
- e = XMLHelper::getFirstChildElement(e, _AccessControl);
- while (e) {
- string t(XMLHelper::getAttrString(e, nullptr, _type));
- if (!t.empty()) {
- Category::getInstance(SHIBSP_LOGCAT ".AccessControl.Chaining").info("building AccessControl provider of type (%s)...", t.c_str());
- m_ac.push_back(unique_ptr<AccessControl>(
- SPConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), e, deprecationSupport)
- ));
+ static const char ACCESS_CONTROL_PROP_PATH[] = "AccessControl";
+ for (const auto& child : pt) {
+ if (child.first != ACCESS_CONTROL_PROP_PATH) {
+ continue;
+ }
+
+ static const char TYPE_PROP_PATH[] = "<xmlattr>.type";
+ const boost::optional<string> type = child.second.get_optional<string>(TYPE_PROP_PATH);
+ if (!type) {
+ throw ConfigurationException("Missing type in AccessControl configuration.");
}
- e = XMLHelper::getNextSiblingElement(e, _AccessControl);
+
+ log.info("building AccessControl provider of type (%s)...", type.get().c_str());
+ m_ac.push_back(unique_ptr<AccessControl>(
+ AgentConfig::getConfig().AccessControlManager.newPlugin(type.get(), pt, deprecationSupport)
+ ));
}
- if (m_ac.empty())
+
+ if (m_ac.empty()) {
throw ConfigurationException("Chaining AccessControl plugin requires at least one child plugin.");
+ }
}
AccessControl::aclresult_t ChainingAccessControl::authorized(const SPRequest& request, const Session* session) const
diff --git a/shibsp/impl/XMLAccessControl.cpp b/shibsp/impl/XMLAccessControl.cpp
index aea0b0ba..ae239274 100644
--- a/shibsp/impl/XMLAccessControl.cpp
+++ b/shibsp/impl/XMLAccessControl.cpp
@@ -1,21 +1,15 @@
/**
- * 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.
+ * 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
*
- * 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
*
- * 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.
+ * 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.
*/
/**
@@ -26,43 +20,39 @@
#include "internal.h"
#include "exceptions.h"
+
#include "AccessControl.h"
#include "SessionCache.h"
#include "SPRequest.h"
#include "attribute/Attribute.h"
+#include "util/Lockable.h"
+#include "util/Misc.h"
+#include "util/ReloadableXMLFile.h"
#include <algorithm>
+#include <memory>
+#include <regex>
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
-#include <boost/bind.hpp>
#include <boost/algorithm/string.hpp>
-#include <boost/ptr_container/ptr_vector.hpp>
-#include <xmltooling/unicode.h>
-#include <xmltooling/util/ReloadableXMLFile.h>
-#include <xmltooling/util/Threads.h>
-#include <xmltooling/util/XMLHelper.h>
-#include <xercesc/util/XMLUniDefs.hpp>
-#include <xercesc/util/regx/RegularExpression.hpp>
+#include <boost/property_tree/ptree.hpp>
#ifndef HAVE_STRCASECMP
# define strcasecmp _stricmp
#endif
using namespace shibsp;
-using namespace xmltooling;
+using namespace boost::property_tree;
using namespace boost;
using namespace std;
-namespace shibsp {
+namespace {
- class Rule : public AccessControl
+ class Rule : public AccessControl, public NoOpSharedLockable
{
public:
- Rule(const DOMElement* e);
+ Rule(const ptree& pt);
~Rule() {}
- Lockable* lock() {return this;}
- void unlock() {}
-
aclresult_t authorized(const SPRequest& request, const Session* session) const;
private:
@@ -70,39 +60,38 @@ namespace shibsp {
set <string> m_vals;
};
- class RuleRegex : public AccessControl
+ class RuleRegex : public AccessControl, public NoOpSharedLockable
{
public:
- RuleRegex(const DOMElement* e);
+ RuleRegex(const ptree& pt);
~RuleRegex() {}
- Lockable* lock() {return this;}
- void unlock() {}
-
aclresult_t authorized(const SPRequest& request, const Session* session) const;
private:
string m_alias;
- auto_arrayptr<char> m_exp;
- scoped_ptr<RegularExpression> m_re;
+ string m_exp;
+ regex m_re;
};
- class Operator : public AccessControl
+ class Operator : public AccessControl, public NoOpSharedLockable
{
public:
- Operator(const DOMElement* e);
+ Operator(const string& name, const ptree& pt);
~Operator() {}
- Lockable* lock() {return this;}
- void unlock() {}
-
aclresult_t authorized(const SPRequest& request, const Session* session) const;
private:
enum operator_t { OP_NOT, OP_AND, OP_OR } m_op;
- ptr_vector<AccessControl> m_operands;
+ vector<unique_ptr<AccessControl>> m_operands;
};
+ static const char ACCESS_CONTROL_PROP_PATH[] = "AccessControl";
+ static const char REQUIRE_PROP_PATH[] = "<xmlattr>.require";
+ static const char RULE_PROP_PATH[] = "Rule";
+ static const char RULE_REGEX_PROP_PATH[] = "RuleRegex";
+
#if defined (_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4250 )
@@ -111,67 +100,56 @@ namespace shibsp {
class XMLAccessControl : public AccessControl, public ReloadableXMLFile
{
public:
- XMLAccessControl(const DOMElement* e, bool deprecationSupport=true);
- /*
- XMLAccessControl(const DOMElement* e, bool deprecationSupport=true)
- : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT ".AccessControl.XML"), true, deprecationSupport) {
- background_load(); // guarantees an exception or the policy is loaded
- }*/
-
- ~XMLAccessControl() {
- shutdown();
+ XMLAccessControl(const ptree& pt)
+ : ReloadableXMLFile(ACCESS_CONTROL_PROP_PATH, pt, Category::getInstance(SHIBSP_LOGCAT ".AccessControl.XML")) {
+ load(); // guarantees an exception or the policy is loaded
}
+ ~XMLAccessControl() {}
+
aclresult_t authorized(const SPRequest& request, const Session* session) const;
protected:
- pair<bool,DOMElement*> background_load();
+ pair<bool,ptree*> load() noexcept;
private:
- scoped_ptr<AccessControl> m_rootAuthz;
+ unique_ptr<AccessControl> processChild(const string& name, const ptree& pt);
+ unique_ptr<AccessControl> m_rootAuthz;
};
#if defined (_MSC_VER)
#pragma warning( pop )
#endif
+}
- AccessControl* SHIBSP_DLLLOCAL XMLAccessControlFactory(const DOMElement* const & e, bool deprecationSupport)
+namespace shibsp {
+ AccessControl* SHIBSP_DLLLOCAL XMLAccessControlFactory(const ptree& pt, bool deprecationSupport)
{
- return new XMLAccessControl(e, deprecationSupport);
+ return new XMLAccessControl(pt);
}
+};
- static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);
- static const XMLCh _Handler[] = UNICODE_LITERAL_7(H,a,n,d,l,e,r);
- static const XMLCh caseInsensitiveOption[] = UNICODE_LITERAL_1(i);
- static const XMLCh _list[] = UNICODE_LITERAL_4(l,i,s,t);
- static const XMLCh require[] = UNICODE_LITERAL_7(r,e,q,u,i,r,e);
- static const XMLCh NOT[] = UNICODE_LITERAL_3(N,O,T);
- static const XMLCh AND[] = UNICODE_LITERAL_3(A,N,D);
- static const XMLCh OR[] = UNICODE_LITERAL_2(O,R);
- static const XMLCh _Rule[] = UNICODE_LITERAL_4(R,u,l,e);
- static const XMLCh _RuleRegex[] = UNICODE_LITERAL_9(R,u,l,e,R,e,g,e,x);
-}
-
-Rule::Rule(const DOMElement* e) : m_alias(XMLHelper::getAttrString(e, nullptr, require))
+Rule::Rule(const ptree& pt) : m_alias(pt.get(REQUIRE_PROP_PATH, ""))
{
- if (m_alias.empty())
+ if (m_alias.empty()) {
throw ConfigurationException("Access control rule missing require attribute");
- if (!e->hasChildNodes())
- return; // empty rule
+ }
- auto_arrayptr<char> vals(toUTF8(XMLHelper::getTextContent(e)));
- if (!vals.get() || !*vals.get())
- throw ConfigurationException("Unable to convert Rule content into UTF-8.");
+ string vals = pt.get_value("");
+ if (vals.empty()) {
+ return; // empty rule
+ }
- bool listflag = XMLHelper::getAttrBool(e, true, _list);
+ static const char LIST_PROP_PATH[] = "list";
+ static string_to_bool_translator tr;
+ bool listflag = pt.get(LIST_PROP_PATH, true);
if (!listflag) {
- m_vals.insert(vals.get());
+ m_vals.insert(vals);
return;
}
- string temp(vals.get());
- trim(temp);
- split(m_vals, temp, boost::is_space(), algorithm::token_compress_on);
+ trim(vals);
+ split(m_vals, vals, boost::is_space(), algorithm::token_compress_on);
if (m_vals.empty())
throw ConfigurationException("Rule did not contain any usable values.");
}
@@ -248,26 +226,35 @@ AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Sess
return shib_acl_false;
}
-RuleRegex::RuleRegex(const DOMElement* e)
- : m_alias(XMLHelper::getAttrString(e, nullptr, require)),
- m_exp(toUTF8(e->hasChildNodes() ? e->getFirstChild()->getNodeValue() : nullptr))
+RuleRegex::RuleRegex(const ptree& pt)
+ : m_alias(pt.get(REQUIRE_PROP_PATH, "")), m_exp(pt.get_value(""))
{
- if (m_alias.empty() || !m_exp.get() || !*m_exp.get())
+ if (m_alias.empty() || m_exp.empty())
throw ConfigurationException("Access control rule missing require attribute or element content.");
- bool caseSensitive = XMLHelper::getCaseSensitive(e, true);
+ static const char CASE_SENSITIVE_PROP_PATH[] = "caseSensitive";
+ static string_to_bool_translator tr;
+ bool caseSensitive = pt.get(CASE_SENSITIVE_PROP_PATH, true);
try {
- m_re.reset(new RegularExpression(e->getFirstChild()->getNodeValue(), (caseSensitive ? &chNull: caseInsensitiveOption )));
+ // TODO: more flag options, particular for dialect.
+ regex::flag_type flags = regex_constants::optimize;
+ if (!caseSensitive) {
+ flags |= regex_constants::icase;
+ }
+ m_re = regex(m_exp, flags);
}
- catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- throw ConfigurationException("Caught exception while parsing RuleRegex regular expression: $1", params(1,tmp.get()));
+ catch (const regex_error& e) {
+ throw ConfigurationException("Caught exception while parsing RuleRegex regular expression.");
}
}
AccessControl::aclresult_t RuleRegex::authorized(const SPRequest& request, const Session* session) const
{
+ // TODO: Have to confirm we want regex_match here vs. regex_search.
+ // TODO: Have to consider match_flags as well, particularly against some open issues raised against the Xerces behavior.
+
// Map alias in rule to the attribute.
+
if (!session) {
request.log(SPRequest::SPWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");
return shib_acl_false;
@@ -281,87 +268,73 @@ AccessControl::aclresult_t RuleRegex::authorized(const SPRequest& request, const
return shib_acl_false;
}
- try {
- if (m_alias == "user") {
- if (m_re->matches(request.getRemoteUser().c_str())) {
- request.log(SPRequest::SPDebug, string("AccessControl plugin expecting REMOTE_USER (") + m_exp.get() + "), authz granted");
- return shib_acl_true;
- }
- return shib_acl_false;
+ if (m_alias == "user") {
+ if (regex_match(request.getRemoteUser(), m_re)) {
+ request.log(SPRequest::SPDebug, string("AccessControl plugin expecting REMOTE_USER (") + m_exp + "), authz granted");
+ return shib_acl_true;
}
- else if (m_alias == "authnContextClassRef") {
- if (session->getAuthnContextClassRef() && m_re->matches(session->getAuthnContextClassRef())) {
- request.log(SPRequest::SPDebug, string("AccessControl plugin expecting authnContextClassRef (") + m_exp.get() + "), authz granted");
- return shib_acl_true;
- }
- return shib_acl_false;
+ return shib_acl_false;
+ }
+ else if (m_alias == "authnContextClassRef") {
+ if (session->getAuthnContextClassRef() && regex_match(session->getAuthnContextClassRef(), m_re)) {
+ request.log(SPRequest::SPDebug, string("AccessControl plugin expecting authnContextClassRef (") + m_exp + "), authz granted");
+ return shib_acl_true;
}
- else if (m_alias == "authnContextDeclRef") {
- if (session->getAuthnContextDeclRef() && m_re->matches(session->getAuthnContextDeclRef())) {
- request.log(SPRequest::SPDebug, string("AccessControl plugin expecting authnContextDeclRef (") + m_exp.get() + "), authz granted");
- return shib_acl_true;
- }
- return shib_acl_false;
+ return shib_acl_false;
+ }
+ else if (m_alias == "authnContextDeclRef") {
+ if (session->getAuthnContextDeclRef() && regex_match(session->getAuthnContextDeclRef(), m_re)) {
+ request.log(SPRequest::SPDebug, string("AccessControl plugin expecting authnContextDeclRef (") + m_exp + "), authz granted");
+ return shib_acl_true;
}
+ return shib_acl_false;
+ }
- // Find the attribute(s) matching the require rule.
- pair<multimap<string,const Attribute*>::const_iterator, multimap<string,const Attribute*>::const_iterator> attrs =
- session->getIndexedAttributes().equal_range(m_alias);
- if (attrs.first == attrs.second) {
- request.log(SPRequest::SPWarn, string("rule requires attribute (") + m_alias + "), not found in session");
- return shib_acl_false;
- }
+ // Find the attribute(s) matching the require rule.
+ auto attrs = session->getIndexedAttributes().equal_range(m_alias);
+ if (attrs.first == attrs.second) {
+ request.log(SPRequest::SPWarn, string("rule requires attribute (") + m_alias + "), not found in session");
+ return shib_acl_false;
+ }
- for (; attrs.first != attrs.second; ++attrs.first) {
- // Now we have to intersect the attribute's values against the regular expression.
- const vector<string>& vals = attrs.first->second->getSerializedValues();
- for (vector<string>::const_iterator j = vals.begin(); j != vals.end(); ++j) {
- if (m_re->matches(j->c_str())) {
- request.log(SPRequest::SPDebug, string("AccessControl plugin expecting (") + m_exp.get() + "), authz granted");
- return shib_acl_true;
- }
+ for (; attrs.first != attrs.second; ++attrs.first) {
+ // Now we have to intersect the attribute's values against the regular expression.
+ for (const string& v : attrs.first->second->getSerializedValues()) {
+ if (regex_match(v, m_re)) {
+ request.log(SPRequest::SPDebug, string("AccessControl plugin expecting (") + m_exp + "), authz granted");
+ return shib_acl_true;
}
}
}
- catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- request.log(SPRequest::SPError, string("caught exception while parsing RuleRegex regular expression: ") + tmp.get());
- }
return shib_acl_false;
}
-Operator::Operator(const DOMElement* e)
+Operator::Operator(const string& name, const ptree& pt)
{
- if (XMLString::equals(e->getLocalName(),NOT))
+ if (name == "NOT")
m_op=OP_NOT;
- else if (XMLString::equals(e->getLocalName(),AND))
+ else if (name == "AND")
m_op=OP_AND;
- else if (XMLString::equals(e->getLocalName(),OR))
+ else if (name == "OR")
m_op=OP_OR;
else
- throw ConfigurationException("Unrecognized operator in access control rule");
+ throw ConfigurationException("Unrecognized access control rule type");
- e=XMLHelper::getFirstChildElement(e);
- if (XMLString::equals(e->getLocalName(),_Rule))
- m_operands.push_back(new Rule(e));
- else if (XMLString::equals(e->getLocalName(),_RuleRegex))
- m_operands.push_back(new RuleRegex(e));
- else
- m_operands.push_back(new Operator(e));
-
- if (m_op==OP_NOT)
- return;
+ for (const auto& child : pt) {
+ if (child.first == RULE_PROP_PATH) {
+ m_operands.push_back(unique_ptr<AccessControl>(new Rule(child.second)));
+ }
+ else if (child.first == RULE_REGEX_PROP_PATH) {
+ m_operands.push_back(unique_ptr<AccessControl>(new RuleRegex(child.second)));
+ }
+ else {
+ m_operands.push_back(unique_ptr<AccessControl>(new Operator(child.first, child.second)));
+ }
+ }
- e=XMLHelper::getNextSiblingElement(e);
- while (e) {
- if (XMLString::equals(e->getLocalName(),_Rule))
- m_operands.push_back(new Rule(e));
- else if (XMLString::equals(e->getLocalName(),_RuleRegex))
- m_operands.push_back(new RuleRegex(e));
- else
- m_operands.push_back(new Operator(e));
- e=XMLHelper::getNextSiblingElement(e);
+ if (m_op == OP_NOT && m_operands.size() != 1) {
+ throw new ConfigurationException("NOT operator contained more than one child");
}
}
@@ -369,7 +342,7 @@ AccessControl::aclresult_t Operator::authorized(const SPRequest& request, const
{
switch (m_op) {
case OP_NOT:
- switch (m_operands.front().authorized(request,session)) {
+ switch (m_operands.front()->authorized(request,session)) {
case shib_acl_true:
return shib_acl_false;
case shib_acl_false:
@@ -381,68 +354,73 @@ AccessControl::aclresult_t Operator::authorized(const SPRequest& request, const
case OP_AND:
{
// Look for a rule that returns non-true.
- for (ptr_vector<AccessControl>::const_iterator i = m_operands.begin(); i != m_operands.end(); ++i) {
+ for (const auto& i : m_operands) {
if (i->authorized(request,session) != shib_acl_true)
return shib_acl_false;
}
return shib_acl_true;
-
- ptr_vector<AccessControl>::const_iterator i = find_if(
- m_operands.begin(), m_operands.end(),
- boost::bind(&AccessControl::authorized, _1, boost::cref(request), session) != shib_acl_true
- );
- return (i != m_operands.end()) ? shib_acl_false : shib_acl_true;
}
case OP_OR:
{
// Look for a rule that returns true.
- ptr_vector<AccessControl>::const_iterator i = find_if(
- m_operands.begin(), m_operands.end(),
- boost::bind(&AccessControl::authorized, _1, boost::cref(request), session) == shib_acl_true
- );
- return (i != m_operands.end()) ? shib_acl_true : shib_acl_false;
+ for (const auto& i : m_operands) {
+ if (i->authorized(request,session) != shib_acl_true)
+ return shib_acl_false;
+ }
+ return shib_acl_false;
}
}
request.log(SPRequest::SPWarn,"unknown operation in access control policy, denying access");
return shib_acl_false;
}
-pair<bool,DOMElement*> XMLAccessControl::background_load()
+unique_ptr<AccessControl> processChild(const string& name, const ptree& pt)
{
- // Load from source using base class.
- pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
+ if (name == RULE_PROP_PATH) {
+ return unique_ptr<AccessControl>(new Rule(pt));
+ }
+ else if (name == RULE_REGEX_PROP_PATH) {
+ return unique_ptr<AccessControl>(new RuleRegex(pt));
+ }
+ else if (name != "<xmlattr>") {
+ return unique_ptr<AccessControl>(new Operator(name, pt));
+ }
- // If we own it, wrap it.
- XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr);
+}
- // Check for AccessControl wrapper and drop a level.
- if (XMLString::equals(raw.second->getLocalName(),_AccessControl)) {
- raw.second = XMLHelper::getFirstChildElement(raw.second);
- if (!raw.second)
- throw ConfigurationException("No child element found in AccessControl parent element.");
- }
- else if (XMLString::equals(raw.second->getLocalName(),_Handler)) {
- raw.second = XMLHelper::getFirstChildElement(raw.second);
- if (!raw.second)
- throw ConfigurationException("No child element found in Handler parent element.");
+pair<bool,ptree*> XMLAccessControl::load() noexcept
+{
+ // Load from source using base class.
+ pair<bool,ptree*> raw = ReloadableXMLFile::load();
+ if (!raw.second) {
+ return raw;
}
- scoped_ptr<AccessControl> authz;
- if (XMLString::equals(raw.second->getLocalName(),_Rule))
- authz.reset(new Rule(raw.second));
- else if (XMLString::equals(raw.second->getLocalName(),_RuleRegex))
- authz.reset(new RuleRegex(raw.second));
- else
- authz.reset(new Operator(raw.second));
+ // If we own it, wrap it, but we don't retain use of it.
+ unique_ptr<ptree> treejanitor(raw.first ? raw.second : nullptr);
+
+ // This is tentative and almost certainly wrong due to the way the XML
+ // worked in the original config.
+
+ // In the inline case, there should be a child element named
+ // AccessControl so we need to step down one level.
+ unique_ptr<AccessControl> authz;
+ const auto& child = raw.second->front();
+ if (child.first == ACCESS_CONTROL_PROP_PATH) {
+ const auto& child2 = child.second.front();
+ authz = processChild(child2.first, child2.second);
+ } else {
+ authz = processChild(child.first, child.second);
+ }
// Perform the swap inside a lock.
- if (m_lock)
- m_lock->wrlock();
- SharedLock locker(m_lock, false);
+#ifdef HAVE_CXX14
+ unique_lock<ReloadableXMLFile> locker(*this);
+#endif
m_rootAuthz.swap(authz);
- return make_pair(false,(DOMElement*)nullptr);
+ return make_pair(false, nullptr);
}
AccessControl::aclresult_t XMLAccessControl::authorized(const SPRequest& request, const Session* session) const
diff --git a/shibsp/impl/XMLRequestMapper.cpp b/shibsp/impl/XMLRequestMapper.cpp
index fbab8c7e..1908c703 100644
--- a/shibsp/impl/XMLRequestMapper.cpp
+++ b/shibsp/impl/XMLRequestMapper.cpp
@@ -1,21 +1,15 @@
/**
- * 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.
+ * 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
*
- * 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
*
- * 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.
+ * 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.
*/
/** XMLRequestMapper.cpp
@@ -26,92 +20,82 @@
#include "internal.h"
#include "exceptions.h"
#include "AccessControl.h"
+#include "AgentConfig.h"
#include "RequestMapper.h"
#include "SPRequest.h"
+#include "io/HTTPRequest.h"
+#include "logging/Category.h"
#include "util/CGIParser.h"
-#include "util/DOMPropertySet.h"
+#include "util/BoostPropertySet.h"
#include "util/Misc.h"
+#include "util/ReloadableXMLFile.h"
#include "util/SPConstants.h"
#include <algorithm>
-#include <boost/shared_ptr.hpp>
+#include <memory>
+#include <regex>
+#include <tuple>
+#include <utility>
+#include <boost/property_tree/ptree.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>
-#include <boost/tuple/tuple.hpp>
#include <boost/algorithm/string.hpp>
-#include <xmltooling/util/ReloadableXMLFile.h>
-#include <xmltooling/util/Threads.h>
-#include <xmltooling/util/XMLHelper.h>
-#include <xercesc/util/XMLUniDefs.hpp>
-#include <xercesc/util/regx/RegularExpression.hpp>
using namespace shibsp;
-using namespace xmltooling;
-using namespace boost;
+using namespace boost::property_tree;
using namespace std;
-namespace shibsp {
+namespace {
// Blocks access when an ACL plugin fails to load.
- class AccessControlDummy : public AccessControl
+ class AccessControlDummy : public AccessControl, public NoOpSharedLockable
{
public:
- Lockable* lock() {
- return this;
- }
-
- void unlock() {}
-
aclresult_t authorized(const SPRequest& request, const Session* session) const {
return shib_acl_false;
}
};
- class Override : public DOMPropertySet, public DOMNodeFilter
+ class Override : public BoostPropertySet
{
public:
Override(bool unicodeAware=false) : m_unicodeAware(unicodeAware) {}
- Override(bool unicodeAware, const DOMElement* e, Category& log, const Override* base=nullptr);
+ Override(bool unicodeAware, ptree& pt, Category& log, const Override* base=nullptr);
~Override() {}
- // Provides filter to exclude special config elements.
- FilterAction acceptNode(const DOMNode* node) const {
- return FILTER_REJECT;
- }
-
const Override* locate(const HTTPRequest& request) const;
- AccessControl* getAC() const { return (m_acl ? m_acl.get() : (getParent() ? dynamic_cast<const Override*>(getParent())->getAC() : nullptr)); }
+ AccessControl* getAC() const {
+ return (m_acl ? m_acl.get() : (getParent() ? dynamic_cast<const Override*>(getParent())->getAC() : nullptr));
+ }
protected:
- void loadACL(const DOMElement* e, Category& log);
+ void loadACL(const ptree& pt, Category& log);
bool m_unicodeAware;
- map< string,boost::shared_ptr<Override> > m_map;
- vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > > m_regexps;
- vector< boost::tuple< string,boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > > m_queries;
+ // This uses shared_ptr to support multiple mappings for a given Override for Host.
+ // For Path, it's just overhead.
+ map< string,shared_ptr<Override> > m_map;
+ vector< pair< regex,unique_ptr<Override> > > m_regexps;
+ vector< tuple< string,boost::optional<regex>,unique_ptr<Override> > > m_queries;
private:
- scoped_ptr<AccessControl> m_acl;
+ unique_ptr<AccessControl> m_acl;
};
class XMLRequestMapperImpl : public Override
{
public:
- XMLRequestMapperImpl(const DOMElement* e, Category& log);
+ XMLRequestMapperImpl(ptree& pt, Category& log);
+ ~XMLRequestMapperImpl() {}
- ~XMLRequestMapperImpl() {
- if (m_document)
- m_document->release();
- }
+ const Override* findOverride(const char* vhost, const HTTPRequest& request) const;
- void setDocument(DOMDocument* doc) {
- m_document = doc;
+ void setTree(ptree* pt) {
+ m_tree.reset(pt);
}
- const Override* findOverride(const char* vhost, const HTTPRequest& request) const;
-
private:
- DOMDocument* m_document;
+ unique_ptr<ptree> m_tree;
};
#if defined (_MSC_VER)
@@ -119,55 +103,40 @@ namespace shibsp {
#pragma warning( disable : 4250 )
#endif
+ static const char REQUEST_MAP_PROP_PATH[] = "RequestMap";
+
class XMLRequestMapper : public RequestMapper, public ReloadableXMLFile
{
public:
- XMLRequestMapper(const DOMElement* e, bool deprecationSupport=true);
-/* XMLRequestMapper(const DOMElement* e, bool deprecationSupport=true)
- : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT ".RequestMapper"), true, deprecationSupport) {
- background_load();
- }*/
-
- ~XMLRequestMapper() {
- shutdown();
+ XMLRequestMapper(const ptree& pt)
+ : ReloadableXMLFile(REQUEST_MAP_PROP_PATH, pt, Category::getInstance(SHIBSP_LOGCAT ".RequestMapper")) {
+ load(); // guarantees an exception or the map is loaded
}
+ ~XMLRequestMapper() {}
+
Settings getSettings(const HTTPRequest& request) const;
protected:
- pair<bool,DOMElement*> background_load();
+ pair<bool,ptree*> load() noexcept;
private:
- scoped_ptr<XMLRequestMapperImpl> m_impl;
+ unique_ptr<XMLRequestMapperImpl> m_impl;
};
#if defined (_MSC_VER)
#pragma warning( pop )
#endif
- RequestMapper* SHIBSP_DLLLOCAL XMLRequestMapperFactory(const DOMElement* const & e, bool deprecationSupport)
+ RequestMapper* SHIBSP_DLLLOCAL XMLRequestMapperFactory(const ptree& pt, bool deprecationSupport)
{
- return new XMLRequestMapper(e, deprecationSupport);
+ return new XMLRequestMapper(pt);
}
-
- static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);
- static const XMLCh AccessControlProvider[] = UNICODE_LITERAL_21(A,c,c,e,s,s,C,o,n,t,r,o,l,P,r,o,v,i,d,e,r);
- static const XMLCh caseInsensitiveOption[] = UNICODE_LITERAL_1(i);
- static const XMLCh Host[] = UNICODE_LITERAL_4(H,o,s,t);
- static const XMLCh HostRegex[] = UNICODE_LITERAL_9(H,o,s,t,R,e,g,e,x);
- static const XMLCh htaccess[] = UNICODE_LITERAL_8(h,t,a,c,c,e,s,s);
- static const XMLCh ignoreCase[] = UNICODE_LITERAL_10(i,g,n,o,r,e,C,a,s,e);
- static const XMLCh Path[] = UNICODE_LITERAL_4(P,a,t,h);
- static const XMLCh PathRegex[] = UNICODE_LITERAL_9(P,a,t,h,R,e,g,e,x);
- static const XMLCh Query[] = UNICODE_LITERAL_5(Q,u,e,r,y);
- static const XMLCh name[] = UNICODE_LITERAL_4(n,a,m,e);
- static const XMLCh regex[] = UNICODE_LITERAL_5(r,e,g,e,x);
- static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
}
void SHIBSP_API shibsp::registerRequestMappers()
{
- SPConfig& conf=SPConfig::getConfig();
+ AgentConfig& conf=AgentConfig::getConfig();
conf.RequestMapperManager.registerFactory(XML_REQUEST_MAPPER, XMLRequestMapperFactory);
conf.RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, XMLRequestMapperFactory);
}
@@ -180,28 +149,37 @@ RequestMapper::~RequestMapper()
{
}
-void Override::loadACL(const DOMElement* e, Category& log)
+void Override::loadACL(const ptree& pt, Category& log)
{
- bool deprecationSupport = e ? XMLString::equals(e->getNamespaceURI(), shibspconstants::SHIB2SPCONFIG_NS) : false;
+ // This method looks for a supported child element to use as the basis
+ // of constructing an AccessControl plugin.
+
+ static const char ACCESS_CONTROL_PROP_PATH[] = "AccessControl";
+ static const char ACCESS_CONTROL_PROVIDER_PROP_PATH[] = "AccessControlProvider";
+ static const char HTACCESS_PROP_PATH[] = "htaccess";
+ static const char TYPE_PROP_PATH[] = "<xmlattr>.type";
+
try {
- const DOMElement* acl = XMLHelper::getFirstChildElement(e,htaccess);
+ boost::optional<const ptree&> acl = pt.get_child_optional(HTACCESS_PROP_PATH);
if (acl) {
log.info("building Apache htaccess AccessControl provider...");
- m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL,acl, deprecationSupport));
+ m_acl.reset(AgentConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL, acl.get(), false));
}
else {
- acl = XMLHelper::getFirstChildElement(e,_AccessControl);
+ acl = pt.get_child_optional(ACCESS_CONTROL_PROP_PATH);
if (acl) {
log.info("building XML-based AccessControl provider...");
- m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL,acl, deprecationSupport));
+ // TODO: this is tenative, but it seems like we need to pass in the parent tree to allow it to
+ // walk down to the "expected" element, but TBD.
+ m_acl.reset(AgentConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL, pt, false));
}
else {
- acl = XMLHelper::getFirstChildElement(e,AccessControlProvider);
+ acl = pt.get_child_optional(ACCESS_CONTROL_PROVIDER_PROP_PATH);
if (acl) {
- string t(XMLHelper::getAttrString(acl, nullptr, _type));
+ string t(pt.get(TYPE_PROP_PATH, ""));
if (!t.empty()) {
log.info("building AccessControl provider of type %s...", t.c_str());
- m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acl, deprecationSupport));
+ m_acl.reset(AgentConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acl.get(), false));
}
else {
throw ConfigurationException("<AccessControlProvider> missing type attribute.");
@@ -210,159 +188,177 @@ void Override::loadACL(const DOMElement* e, Category& log)
}
}
}
- catch (const std::exception& ex) {
+ catch (const exception& ex) {
log.crit("exception building AccessControl provider: %s", ex.what());
m_acl.reset(new AccessControlDummy());
}
}
-Override::Override(bool unicodeAware, const DOMElement* e, Category& log, const Override* base)
+Override::Override(bool unicodeAware, ptree& pt, Category& log, const Override* base)
: m_unicodeAware(unicodeAware)
{
- // Load the property set.
- xmltooling::QName unsetter(nullptr, "unset");
- load(e, nullptr, this, nullptr, &unsetter);
+ // Load the <xmlattr> tree as a property set.
+ const boost::optional<ptree&> xmlattrs = pt.get_child_optional("<xmlattr>");
+ if (xmlattrs) {
+ load(xmlattrs.get(), "unset");
+ }
setParent(base);
// Load any AccessControl provider.
- loadACL(e, log);
+ loadACL(pt, log);
- // Handle nested Paths.
- DOMElement* path = XMLHelper::getFirstChildElement(e, Path);
- for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, Path)) {
- const XMLCh* n = path->getAttributeNS(nullptr,name);
+ static const char PATH_PROP_PATH[] = "Path";
+ static const char PATH_REGEX_PROP_PATH[] = "PathRegex";
+ static const char QUERY_PROP_PATH[] = "Query";
+ static const char NAME_PROP_PATH[] = "<xmlattr>.name";
+ static const char REGEX_PROP_PATH[] = "<xmlattr>.regex";
- // Skip any leading slashes.
- while (n && *n == chForwardSlash)
- n++;
+ // Process the various child types.
- // Check for empty name.
- if (!n || !*n) {
- log.warn("skipping Path element (%d) with empty name attribute", i);
- continue;
- }
+ for (auto& child : pt) {
+ if (child.first == PATH_PROP_PATH) {
+ const string nameprop(getString("name", ""));
+ const char* n = nameprop.c_str();
- // Check for an embedded slash.
- int slash = XMLString::indexOf(n, chForwardSlash);
- if (slash > 0) {
- // Copy the first path segment.
- xstring namebuf;
- for (int pos = 0; pos < slash; ++pos)
- namebuf += n[pos];
-
- // Move past the slash in the original pathname.
- n = n + slash + 1;
-
- // Skip any leading slashes again.
- while (*n == chForwardSlash)
- ++n;
-
- if (*n) {
- // Create a placeholder Path element for the first path segment and replant under it.
- DOMElement* newpath = path->getOwnerDocument()->createElementNS(path->getNamespaceURI(), Path);
- newpath->setAttributeNS(nullptr, name, namebuf.c_str());
- path->setAttributeNS(nullptr, name, n);
- path->getParentNode()->replaceChild(newpath, path);
- newpath->appendChild(path);
-
- // Repoint our locals at the new parent.
- path = newpath;
- n = path->getAttributeNS(nullptr, name);
+ // Skip any leading slashes.
+ while (n && *n == '/')
+ n++;
+
+ // Check for empty name.
+ if (!n || !*n) {
+ log.warn("skipping Path element with empty name attribute");
+ continue;
}
- else {
- // All we had was a pathname with trailing slash(es), so just reset it without them.
- path->setAttributeNS(nullptr, name, namebuf.c_str());
- n = path->getAttributeNS(nullptr, name);
+
+ // Check for an embedded slash.
+ const char* slash = strchr(n, '/');
+ if (slash) {
+ // Copy the first path segment.
+ string namebuf;
+ for (const char* pos = n; pos < slash; ++pos) {
+ namebuf += *pos;
+ }
+
+ // Move past the slash in the original pathname.
+ n = slash + 1;
+
+ // Skip any leading slashes again.
+ while (*n == '/')
+ ++n;
+
+ if (*n) {
+ // TODO: Seriously doubt any of this will work, but fixing it will
+ // require substantial redesign.
+
+ // namebuf has the segment to process at "this" level
+ // The "new" injected Path Oevrride containing it would have no other
+ // attributes since the settings in the slash-containing Path would
+ // apply only to the final "leaf" of the Path's directory tree.
+
+ // The currently iterated pair's second member is the original Path
+ // tree with the multi-part pathname and all the settings under <xmlattr>.
+ // We would have to make the iterated pair's second member be a tree
+ // containing the namebuf path segment under <xmlattr>.name and containing
+ // the original tree with a modified <xmlattr>.name set to *n under a child named Path.
+
+ // Copy the old child tree into a local variable and adjust its name.
+ ptree old_child(child.second);
+ old_child.put(NAME_PROP_PATH, n);
+
+ // Create a new tree with just the namebuf prefix and the new child under it.
+ ptree new_child;
+ new_child.put(NAME_PROP_PATH, namebuf);
+ new_child.add_child(PATH_PROP_PATH, old_child);
+
+ // Replace the original child iterated with the "new" child.
+ child.second = new_child;
+
+ // Repoint our locals at the new parent.
+ n = namebuf.c_str(); // seems like this shouldn't be needed
+ }
+ else {
+ // All we had was a pathname with trailing slash(es), so just reset it without them.
+ child.second.put(NAME_PROP_PATH, namebuf);
+ n = namebuf.c_str(); // seems like this shouldn't be needed
+ }
}
- }
- char* dup = nullptr;
- try {
- boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
- if (m_unicodeAware) {
- //dup = toUTF8(o->getXMLString("name").second, true /* use malloc */);
- dup = strdup(o->getString("name").second);
+ shared_ptr<Override> o(new Override(m_unicodeAware, child.second, log, this));
+ string mutable_path = o->getString("name", "");
+ if (mutable_path.empty()) {
+ throw new ConfigurationException("Path element did not contain a name attribute.");
}
- else {
- dup = strdup(o->getString("name").second);
- for (char* pch = dup; *pch; ++pch)
- *pch = tolower(*pch);
+
+ // The thinking here is that the Unicode flag tells it to treat the
+ // Path name as UTF-8, and thus can't be safely case-folded.
+ if (!m_unicodeAware) {
+ boost::algorithm::to_lower(mutable_path);
}
- if (m_map.count(dup)) {
- log.warn("skipping duplicate Path element (%s)", dup);
+
+ if (m_map.count(mutable_path)) {
+ log.warn("skipping duplicate Path element (%s)", mutable_path.c_str());
}
else {
- m_map[dup] = o;
- log.debug("added Path mapping (%s)", dup);
+ m_map[mutable_path] = o;
+ log.debug("added Path mapping (%s)", mutable_path.c_str());
}
- free(dup);
}
- catch (const std::exception&) {
- free(dup);
- throw;
- }
- }
-
- if (!XMLString::equals(e->getLocalName(), PathRegex)) {
- // Handle nested PathRegexs.
- path = XMLHelper::getFirstChildElement(e, PathRegex);
- for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, PathRegex)) {
- const XMLCh* n = path->getAttributeNS(nullptr, regex);
- if (!n || !*n) {
- log.warn("skipping PathRegex element (%d) with empty regex attribute",i);
+ else if (child.first == PATH_REGEX_PROP_PATH) {
+ const string regexpprop(child.second.get(REGEX_PROP_PATH, ""));
+ if (regexpprop.empty()) {
+ log.warn("skipping PathRegex element with empty regex attribute");
continue;
}
- boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
-
- bool caseSensitive;
- if (path && path->hasAttributeNS(nullptr, ignoreCase)) {
- // In this one case, we've left ignoreCase reversed (true means case sensitive, false means insensitive).
- // This was to protect people who followed the security advisory for SSPCPP-691 and reversed their setting.
- SPConfig::getConfig().deprecation().error("ignoreCase attribute in PathRegex element will be interpreted backwards. Replace with caseSensitive");
- caseSensitive = XMLHelper::getAttrBool(path, true, ignoreCase);
- } else {
- // If the old ignoreCase setting isn't set, then we just process normally.
- caseSensitive = XMLHelper::getCaseSensitive(path, false);
- }
+ unique_ptr<Override> o(new Override(m_unicodeAware, child.second, log, this));
+
try {
- boost::shared_ptr<RegularExpression> re(new RegularExpression(n, caseSensitive ? &chNull : caseInsensitiveOption));
- m_regexps.push_back(make_pair(re, o));
+ // TODO: more flag options, particular for dialect.
+ regex::flag_type flags = regex_constants::optimize;
+ if (!getBool("caseSensitive", false)) {
+ flags |= regex_constants::icase;
+ }
+ regex exp(regexpprop, flags);
+ m_regexps.push_back(make_pair(exp, std::move(o)));
+ log.debug("added <PathRegex> mapping (%s)", regexpprop.c_str());
}
- catch (const XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- log.error("caught exception while parsing PathRegex regular expression (%d): %s", i, tmp.get());
+ catch (const regex_error& e) {
+ log.error("error parsing PathRegex regular expression: %s", e.what());
throw ConfigurationException("Invalid regular expression in PathRegex element.");
}
-
- if (log.isDebugEnabled())
- log.debug("added <PathRegex> mapping (%s)", o->getString("regex").second);
}
- }
+ else if (child.first == QUERY_PROP_PATH) {
+ string nameprop(getString("name", ""));
+ if (nameprop.empty()) {
+ log.warn("skipping Query element with empty name attribute");
+ continue;
+ }
- // Handle nested Querys.
- path = XMLHelper::getFirstChildElement(e, Query);
- for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, Query)) {
- const XMLCh* n = path->getAttributeNS(nullptr, name);
- if (!n || !*n) {
- log.warn("skipping Query element (%d) with empty name attribute",i);
- continue;
- }
- auto_ptr_char ntemp(n);
- const XMLCh* v = path->getAttributeNS(nullptr, regex);
+ unique_ptr<Override> o(new Override(m_unicodeAware, child.second, log, this));
- try {
- boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
- boost::shared_ptr<RegularExpression> re((v && *v) ? new RegularExpression(v) : nullptr);
- m_queries.push_back(boost::make_tuple(string(ntemp.get()), re, o));
- }
- catch (const XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get());
- throw ConfigurationException("Invalid regular expression in Query element.");
- }
+ string regexpprop(getString("regex", ""));
- log.debug("added <Query> mapping (%s)", ntemp.get());
+ if (regexpprop.empty()) {
+ m_queries.push_back(make_tuple(nameprop, boost::optional<regex>(), std::move(o)));
+ }
+ else {
+ try {
+ // TODO: more flag options, particular for dialect.
+ regex::flag_type flags = regex_constants::optimize;
+ if (!getBool("caseSensitive", false)) {
+ flags |= regex_constants::icase;
+ }
+ regex exp(regexpprop, flags);
+
+ m_queries.push_back(make_tuple(nameprop, boost::optional<regex>(exp), std::move(o)));
+ log.debug("added <Query> mapping (%s)", nameprop.c_str());
+ }
+ catch (const regex_error& e) {
+ log.error("caught exception while parsing Query regular expression: %s", e.what());
+ throw ConfigurationException("Invalid regular expression in Query element.");
+ }
+ }
+ }
}
}
@@ -413,15 +409,15 @@ const Override* Override::locate(const HTTPRequest& request) const
path = dup.c_str();
// Tokenize the path by segment and try and map each segment.
- tokenizer< char_separator<char> > tokens(dup, char_separator<char>("/"));
- for (tokenizer< char_separator<char> >::iterator token = tokens.begin(); token != tokens.end(); ++token) {
+ boost::tokenizer< boost::char_separator<char> > tokens(dup, boost::char_separator<char>("/"));
+ for (const string& token : tokens) {
- string tokendup(*token);
+ string tokendup(token);
if (!m_unicodeAware) {
- to_lower(tokendup);
+ boost::algorithm::to_lower(tokendup);
}
- map< string,boost::shared_ptr<Override> >::const_iterator i = o->m_map.find(tokendup);
+ const auto& i = o->m_map.find(tokendup);
if (i == o->m_map.end())
break; // Once there's no match, we've consumed as much of the path as possible here.
// We found a match, so reset the settings pointer.
@@ -436,48 +432,38 @@ const Override* Override::locate(const HTTPRequest& request) const
// If there's anything left, we try for a regex match on the rest of the path minus the query string.
if (*path) {
- for (vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {
- try {
- if (re->first->matches(path)) {
- o = re->second.get();
- break;
- }
- } catch (const XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- throw ConfigurationException("Caught exception while matching PathRegex : $1", params(1, tmp.get()));
+ for (const auto& re : m_regexps) {
+ if (regex_match(path, re.first)) {
+ o = re.second.get();
+ break;
}
}
}
// Finally, check for query string matches. This is another "unrolled" recursive descent in a loop.
- // To avoid consuming any POST data, we use a dedicated CGIParser.
+ // To avoid consuming any POST data, we use a dedicated CGIParser that only consumes the query string.
if (!o->m_queries.empty()) {
bool descended;
CGIParser cgi(request, true);
do {
descended = false;
- for (vector< boost::tuple< string,boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {
- pair<CGIParser::walker,CGIParser::walker> vals = cgi.getParameters(q->get<0>().c_str());
+ for (auto q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {
+ pair<CGIParser::walker,CGIParser::walker> vals = cgi.getParameters(get<0>(*q).c_str());
if (vals.first != vals.second) {
- if (q->get<1>()) {
+ if (get<1>(*q)) {
// We have to match one of the values.
while (vals.first != vals.second) {
- try{
- if (q->get<1>()->matches(vals.first->second)) {
- o = q->get<2>().get();
- descended = true;
- break;
- }
- } catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- throw ConfigurationException("Caught exception while matching Query regular expression : $1", params(1, tmp.get()));
+ if (regex_match(vals.first->second, get<1>(*q).get())) {
+ o = get<2>(*q).get();
+ descended = true;
+ break;
}
++vals.first;
}
}
else {
// The simple presence of the parameter is sufficient to match.
- o = q->get<2>().get();
+ o = get<2>(*q).get();
descended = true;
}
}
@@ -488,163 +474,150 @@ const Override* Override::locate(const HTTPRequest& request) const
return o;
}
-XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : m_document(nullptr)
+XMLRequestMapperImpl::XMLRequestMapperImpl(ptree& pt, Category& log)
{
- static const XMLCh _RequestMap[] = UNICODE_LITERAL_10(R,e,q,u,e,s,t,M,a,p);
-
- if (e && !XMLHelper::isNodeNamed(e, shibspconstants::SHIB2SPCONFIG_NS, _RequestMap)
- && !XMLHelper::isNodeNamed(e, shibspconstants::SHIB3SPCONFIG_NS, _RequestMap)) {
- throw ConfigurationException("XML RequestMapper requires conf:RequestMap at root of configuration.");
- }
-
- if (XMLString::equals(e->getNamespaceURI(), shibspconstants::SHIB2SPCONFIG_NS)) {
- SPConfig::getConfig().deprecation().warn("legacy V2 configuration");
- }
-
// Load the property set.
- xmltooling::QName unsetter(nullptr, "unset");
- load(e, nullptr, this, nullptr, &unsetter);
+ load(pt, "unset");
+ // This probably will go away at some point but for now just leaving it.
// Inject "default" app ID if not explicit.
- if (!getString("applicationId").first)
- setProperty("applicationId", "default");
+ if (!getString("applicationId")) {
+ pt.put("applicationId", "default");
+ }
// Load any AccessControl provider.
- loadACL(e, log);
+ loadACL(pt, log);
- pair<bool,bool> unicodeAware = getBool("unicodeAware");
- m_unicodeAware = (unicodeAware.first && unicodeAware.second);
+ m_unicodeAware = getBool("unicodeAware", false);
- // Loop over the HostRegex elements.
- const DOMElement* host = XMLHelper::getFirstChildElement(e, HostRegex);
- for (int i = 1; host; ++i, host = XMLHelper::getNextSiblingElement(host, HostRegex)) {
- const XMLCh* n = host->getAttributeNS(nullptr,regex);
- if (!n || !*n) {
- log.warn("Skipping HostRegex element (%d) with empty regex attribute", i);
- continue;
- }
+ static const char HOST_PROP_PATH[] = "Host";
+ static const char HOST_REGEX_PROP_PATH[] = "HostRegex";
- boost::shared_ptr<Override> o(new Override(m_unicodeAware, host, log, this));
+ // Loop over the HostRegex elements.
+ for (auto& child : pt) {
+ if (child.first == HOST_REGEX_PROP_PATH) {
+ string regexprop(getString("regex", ""));
+ if (regexprop.empty()) {
+ log.warn("Skipping HostRegex element with empty regex attribute");
+ continue;
+ }
- const bool caseSensitive = XMLHelper::getCaseSensitive(host, false);
- try {
- boost::shared_ptr<RegularExpression> re(
- new RegularExpression(n, caseSensitive ? &chNull : caseInsensitiveOption)
- );
- m_regexps.push_back(make_pair(re, o));
- }
- catch (const XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- log.error("caught exception while parsing HostRegex regular expression (%d): %s", i, tmp.get());
- }
+ unique_ptr<Override> o(new Override(m_unicodeAware, child.second, log, this));
- log.debug("Added <HostRegex> mapping for %s", m_regexps.back().second->getString("regex").second);
- }
+ try {
+ regex::flag_type flags = regex_constants::optimize;
+ if (!getBool("caseSensitive", false)) {
+ flags |= regex_constants::icase;
+ }
+ regex exp(regexprop, flags);
+ m_regexps.push_back(make_pair(exp, std::move(o)));
+ }
+ catch (const regex_error& e) {
+ log.error("caught exception while parsing HostRegex regular expression: %s", e.what());
+ }
- // Loop over the Host elements.
- host = XMLHelper::getFirstChildElement(e, Host);
- for (int i = 1; host; ++i, host = XMLHelper::getNextSiblingElement(host, Host)) {
- const XMLCh* n=host->getAttributeNS(nullptr,name);
- if (!n || !*n) {
- log.warn("Skipping Host element (%d) with empty name attribute", i);
- continue;
+ log.debug("Added <HostRegex> mapping for %s", regexprop.c_str());
}
+ else if (child.first == HOST_PROP_PATH) {
+ string name(getString("name", ""));
+ if (name.empty()) {
+ log.warn("Skipping Host element with empty name attribute");
+ continue;
+ }
- boost::shared_ptr<Override> o(new Override(m_unicodeAware, host, log, this));
- pair<bool,const char*> name=o->getString("name");
- pair<bool,const char*> scheme=o->getString("scheme");
- pair<bool,const char*> port=o->getString("port");
+ shared_ptr<Override> o(new Override(m_unicodeAware, child.second, log, this));
+ const char* scheme = o->getString("scheme");
+ const char* port = o->getString("port");
- string dup(name.first ? name.second : "");
- to_lower(dup);
+ boost::algorithm::to_lower(name);
- if (!scheme.first && port.first) {
- // No scheme, but a port, so assume http.
- scheme = pair<bool,const char*>(true,"http");
- }
- else if (scheme.first && !port.first) {
- // Scheme, no port, so default it.
- // XXX Use getservbyname instead?
- port.first = true;
- if (!strcmp(scheme.second,"http"))
- port.second = "80";
- else if (!strcmp(scheme.second,"https"))
- port.second = "443";
- else if (!strcmp(scheme.second,"ftp"))
- port.second = "21";
- else if (!strcmp(scheme.second,"ldap"))
- port.second = "389";
- else if (!strcmp(scheme.second,"ldaps"))
- port.second = "636";
- }
+ if (!scheme && port) {
+ // No scheme, but a port, so assume http.
+ scheme = "http";
+ }
+ else if (scheme && !port) {
+ // Scheme, no port, so default it.
+ // XXX Use getservbyname instead?
+ if (!strcmp(scheme,"http"))
+ port = "80";
+ else if (!strcmp(scheme,"https"))
+ port = "443";
+ else if (!strcmp(scheme,"ftp"))
+ port = "21";
+ else if (!strcmp(scheme,"ldap"))
+ port = "389";
+ else if (!strcmp(scheme,"ldaps"))
+ port = "636";
+ }
- if (scheme.first) {
- string url(scheme.second);
- url=url + "://" + dup;
-
- // Is this the default port?
- if ((!strcmp(scheme.second,"http") && !strcmp(port.second,"80")) ||
- (!strcmp(scheme.second,"https") && !strcmp(port.second,"443")) ||
- (!strcmp(scheme.second,"ftp") && !strcmp(port.second,"21")) ||
- (!strcmp(scheme.second,"ldap") && !strcmp(port.second,"389")) ||
- (!strcmp(scheme.second,"ldaps") && !strcmp(port.second,"636"))) {
- // First store a port-less version.
+ if (scheme) {
+ string url(scheme);
+ url = url + "://" + name;
+
+ // Is this the default port?
+ if ((!strcmp(scheme,"http") && !strcmp(port,"80")) ||
+ (!strcmp(scheme,"https") && !strcmp(port,"443")) ||
+ (!strcmp(scheme,"ftp") && !strcmp(port,"21")) ||
+ (!strcmp(scheme,"ldap") && !strcmp(port,"389")) ||
+ (!strcmp(scheme,"ldaps") && !strcmp(port,"636"))) {
+ // First store a port-less version.
+ if (m_map.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)", url.c_str());
+ continue;
+ }
+ m_map[url] = o;
+ log.debug("Added <Host> mapping for %s", url.c_str());
+
+ // Now append the port. The shared_ptr should refcount the Override to avoid double deletes.
+ url=url + ':' + port;
+ m_map[url] = o;
+ log.debug("Added <Host> mapping for %s", url.c_str());
+ }
+ else {
+ url=url + ':' + port;
+ if (m_map.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)", url.c_str());
+ continue;
+ }
+ m_map[url] = o;
+ log.debug("Added <Host> mapping for %s", url.c_str());
+ }
+ }
+ else {
+ // No scheme or port, so we enter dual hosts on http:80 and https:443
+ string url("http://");
+ url += name;
if (m_map.count(url)) {
- log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ log.warn("Skipping duplicate Host element (%s)", url.c_str());
continue;
}
m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
+ log.debug("Added <Host> mapping for %s", url.c_str());
- // Now append the port. The shared_ptr should refcount the Override to avoid double deletes.
- url=url + ':' + port.second;
- m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
- }
- else {
- url=url + ':' + port.second;
+ url += ":80";
if (m_map.count(url)) {
- log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ log.warn("Skipping duplicate Host element (%s)", url.c_str());
continue;
}
m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
- }
- }
- else {
- // No scheme or port, so we enter dual hosts on http:80 and https:443
- string url("http://");
- url += dup;
- if (m_map.count(url)) {
- log.warn("Skipping duplicate Host element (%s)",url.c_str());
- continue;
- }
- m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
+ log.debug("Added <Host> mapping for %s", url.c_str());
- url += ":80";
- if (m_map.count(url)) {
- log.warn("Skipping duplicate Host element (%s)",url.c_str());
- continue;
- }
- m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
-
- url = "https://" + dup;
- if (m_map.count(url)) {
- log.warn("Skipping duplicate Host element (%s)",url.c_str());
- continue;
- }
- m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
+ url = "https://" + name;
+ if (m_map.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)", url.c_str());
+ continue;
+ }
+ m_map[url] = o;
+ log.debug("Added <Host> mapping for %s", url.c_str());
- url += ":443";
- if (m_map.count(url)) {
- log.warn("Skipping duplicate Host element (%s)",url.c_str());
- continue;
+ url += ":443";
+ if (m_map.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)", url.c_str());
+ continue;
+ }
+ m_map[url] = o;
+ log.debug("Added <Host> mapping for %s", url.c_str());
}
- m_map[url] = o;
- log.debug("Added <Host> mapping for %s",url.c_str());
}
}
}
@@ -652,60 +625,52 @@ XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) :
const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTPRequest& request) const
{
const Override* o = nullptr;
- map< string,boost::shared_ptr<Override> >::const_iterator i = m_map.find(vhost);
+ const auto& i = m_map.find(vhost);
if (i != m_map.end())
o = i->second.get();
else {
- for (vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator re = m_regexps.begin(); !o && re != m_regexps.end(); ++re) {
- try{
- if (re->first->matches(vhost))
- o=re->second.get();
- } catch (XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- throw ConfigurationException("Caught exception while matching HostRegex : $1", params(1, tmp.get()));
+ for (const auto& re : m_regexps) {
+ if (regex_match(vhost, re.first)) {
+ o = re.second.get();
}
-
}
}
return o ? o->locate(request) : this;
}
-pair<bool,DOMElement*> XMLRequestMapper::background_load()
+pair<bool,ptree*> XMLRequestMapper::load() noexcept
{
// Load from source using base class.
- pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
+ pair<bool,ptree*> raw = ReloadableXMLFile::load();
+ if (!raw.second) {
+ return raw;
+ }
// If we own it, wrap it.
- XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr);
+ unique_ptr<ptree> treejanitor(raw.first ? raw.second : nullptr);
- //scoped_ptr<XMLRequestMapperImpl> impl(new XMLRequestMapperImpl(raw.second, m_log));
- scoped_ptr<XMLRequestMapperImpl> impl(nullptr);
+ unique_ptr<XMLRequestMapperImpl> impl(new XMLRequestMapperImpl(*raw.second, m_log));
// If we held the document, transfer it to the impl. If we didn't, it's a no-op.
- impl->setDocument(docjanitor.release());
+ impl->setTree(treejanitor.release());
// Perform the swap inside a lock.
- if (m_lock)
- m_lock->wrlock();
- SharedLock locker(m_lock, false);
+#ifdef HAVE_CXX14
+ unique_lock<ReloadableXMLFile> locker(*this);
+#endif
m_impl.swap(impl);
- return make_pair(false,(DOMElement*)nullptr);
+ return make_pair(false,raw.second);
}
RequestMapper::Settings XMLRequestMapper::getSettings(const HTTPRequest& request) const
{
- try {
- string normalizedhost(request.getHostname());
- to_lower(normalizedhost);
- string vhost = string(request.getScheme()) + "://" + normalizedhost + ':' + lexical_cast<string>(request.getPort());
- const Override* o = m_impl->findOverride(vhost.c_str(), request);
- return Settings(o, o->getAC());
- }
- catch (const XMLException& ex) {
- auto_ptr_char tmp(ex.getMessage());
- m_log.error("caught exception while locating content settings: %s", tmp.get());
- throw ConfigurationException("XML-based RequestMapper failed to retrieve content settings.");
- }
+ string normalizedhost(request.getHostname());
+ boost::algorithm::to_lower(normalizedhost);
+ string vhost = string(request.getScheme()) + "://" + normalizedhost + ':' + boost::lexical_cast<string>(request.getPort());
+
+ const Override* o = m_impl->findOverride(vhost.c_str(), request);
+
+ return Settings(o, o->getAC());
}
diff --git a/shibsp/util/BoostPropertySet.cpp b/shibsp/util/BoostPropertySet.cpp
index 7c290dff..48cf1bd6 100644
--- a/shibsp/util/BoostPropertySet.cpp
+++ b/shibsp/util/BoostPropertySet.cpp
@@ -77,6 +77,20 @@ void BoostPropertySet::load(const property_tree::ptree& pt, const char* unsetter
}
}
+bool BoostPropertySet::hasProperty(const char* name) const
+{
+ if (m_pt) {
+ bool ret = m_pt->get_child_optional(name).has_value();
+ if (ret) {
+ return ret;
+ }
+ }
+
+ if (m_parent && m_unset.find(name) == m_unset.end()) {
+ return m_parent->hasProperty(name);
+ }
+}
+
bool BoostPropertySet::getBool(const char* name, bool defaultValue) const
{
if (m_pt) {
diff --git a/shibsp/util/BoostPropertySet.h b/shibsp/util/BoostPropertySet.h
index fd1208e0..c82fb31a 100644
--- a/shibsp/util/BoostPropertySet.h
+++ b/shibsp/util/BoostPropertySet.h
@@ -49,6 +49,7 @@ namespace shibsp {
BoostPropertySet();
virtual ~BoostPropertySet();
+ bool hasProperty(const char* name) const;
bool getBool(const char* name, bool defaultValue) const;
const char* getString(const char* name, const char* defaultValue=nullptr) const;
unsigned int getUnsignedInt(const char* name, unsigned int defaultValue) const;
@@ -63,6 +64,13 @@ namespace shibsp {
void load(const boost::property_tree::ptree& pt, const char* unsetter=nullptr);
protected:
+ /**
+ * Returns the parent PropertySet.
+ *
+ * @return parent PropertySet
+ */
+ const PropertySet2* getParent() const;
+
/**
* Installs a parent PropertySet to allow an inheritance relationship to a different instance.
*
@@ -71,8 +79,6 @@ namespace shibsp {
void setParent(const PropertySet2* parent);
private:
- const PropertySet2* getParent() const;
-
const PropertySet2* m_parent;
const boost::property_tree::ptree* m_pt;
std::set<std::string> m_unset;
diff --git a/shibsp/util/PropertySet.h b/shibsp/util/PropertySet.h
index fd74c2ce..a405bb8b 100644
--- a/shibsp/util/PropertySet.h
+++ b/shibsp/util/PropertySet.h
@@ -116,6 +116,14 @@ namespace shibsp {
public:
virtual ~PropertySet2();
+ /**
+ * Gets whether a matching property exists.
+ *
+ * @param name property name
+ * @return true iff the named property exists
+ */
+ virtual bool hasProperty(const char* name) const=0;
+
/**
* Returns a boolean-valued property.
*
diff --git a/shibsp/util/ReloadableXMLFile.h b/shibsp/util/ReloadableXMLFile.h
index 7e9a1a45..9a41cceb 100644
--- a/shibsp/util/ReloadableXMLFile.h
+++ b/shibsp/util/ReloadableXMLFile.h
@@ -154,13 +154,13 @@ namespace shibsp {
*/
void updateModificationTime(time_t t);
+ /** Logging object. */
+ Category& m_log;
+
private:
/** Root of configuration or of the pointer to the configuration. */
const boost::property_tree::ptree& m_root;
- /** Logging object. */
- Category& m_log;
-
/** Resource path. */
std::string m_source;
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list