[cpp-sp] branch main updated: Untested config support for handlers.
Scott Cantor
cantor.2 at osu.edu
Mon Jan 27 21:01:44 UTC 2025
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository cpp-sp.
View the commit online:
http://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=eb33c2db73a2367db3d8e4b83f4e3edff67a065a
The following commit(s) were added to refs/heads/main by this push:
new eb33c2db Untested config support for handlers.
eb33c2db is described below
commit eb33c2db73a2367db3d8e4b83f4e3edff67a065a
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Jan 27 16:01:36 2025 -0500
Untested config support for handlers.
---
shibsp/Agent.h | 19 ++-
shibsp/Makefile.am | 2 +
shibsp/handler/Handler.h | 4 +-
shibsp/handler/HandlerConfiguration.h | 95 ++++++++++++++
.../handler/impl/DefaultHandlerConfiguration.cpp | 140 +++++++++++++++++++++
shibsp/impl/AgentConfig.cpp | 8 ++
shibsp/impl/DefaultAgent.cpp | 42 ++++++-
shibsp/paths.h.in | 3 -
tests/data/console-shibboleth.ini | 1 +
tests/data/fatal-exts-shibboleth.ini | 3 +
tests/data/impl/console-shibboleth.ini | 3 +
tests/data/nonfatal-exts-shibboleth.ini | 3 +
tests/data/platform/iis/console-shibboleth.ini | 3 +
tests/data/remoting/impl/shibboleth.ini | 3 +
tests/data/syslog-shibboleth.ini | 3 +
.../util/reloadablefile/console-shibboleth.ini | 3 +
16 files changed, 327 insertions(+), 8 deletions(-)
diff --git a/shibsp/Agent.h b/shibsp/Agent.h
index c33d12b5..c96d0f80 100644
--- a/shibsp/Agent.h
+++ b/shibsp/Agent.h
@@ -30,6 +30,7 @@ namespace shibsp {
class SHIBSP_API Category;
class SHIBSP_API Handler;
+ class SHIBSP_API HandlerConfiguration;
class SHIBSP_API RemotingService;
class SHIBSP_API RequestMapper;
class SHIBSP_API Session;
@@ -68,6 +69,7 @@ namespace shibsp {
* Returns a SessionCache instance.
*
* @param required true iff an exception should be thrown if no SessionCache is available
+ *
* @return a SessionCache
*/
virtual SessionCache* getSessionCache(bool required=true) const=0;
@@ -76,6 +78,7 @@ namespace shibsp {
* Returns a RemotingService instance.
*
* @param required true iff an exception should be thrown if no RemotingService is available
+ *
* @return a RemotingService
*/
virtual const RemotingService* getRemotingService(bool required=true) const=0;
@@ -84,10 +87,20 @@ namespace shibsp {
* Returns a RequestMapper instance.
*
* @param required true iff an exception should be thrown if no RequestMapper is available
+ *
* @return a RequestMapper
*/
virtual RequestMapper* getRequestMapper(bool required=true) const=0;
+ /**
+ * Gets the identified HandlerConfiguration.
+ *
+ * @param id identifier for configuration (null is assumed to be the default)
+ *
+ * @return the matching configuration or null if absent
+ */
+ virtual HandlerConfiguration* getHandlerConfiguration(const char* id=nullptr) const=0;
+
/**
* Enforces requirements for an authenticated session.
*
@@ -96,6 +109,7 @@ namespace shibsp {
*
* @param request SP request interface
* @param handler true iff a request to a registered Handler location can be directly executed
+ *
* @return a pair containing a "request completed" indicator and a server-specific response code
*/
virtual std::pair<bool,long> doAuthentication(SPRequest& request, bool handler=false) const;
@@ -107,6 +121,7 @@ namespace shibsp {
* with the second member as a status value. If false, processing can continue.</p>
*
* @param request SP request interface
+ *
* @return a pair containing a "request completed" indicator and a server-specific response code
*/
virtual std::pair<bool,long> doAuthorization(SPRequest& request) const;
@@ -118,7 +133,8 @@ namespace shibsp {
* with the second member as a status value. If false, processing can continue.</p>
*
* @param request SP request interface
- * @param requireSession set to true iff an error should result if no session exists
+ * @param requireSession set to true iff an error should result if no session exists
+ *
* @return a pair containing a "request completed" indicator and a server-specific response code
*/
virtual std::pair<bool,long> doExport(SPRequest& request, bool requireSession=true) const;
@@ -130,6 +146,7 @@ namespace shibsp {
* with the second member as a status value. If false, processing can continue.</p>
*
* @param request SP request interface
+ *
* @return a pair containing a "request completed" indicator and a server-specific response code
*/
virtual std::pair<bool,long> doHandler(SPRequest& request) const;
diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am
index cd142ca1..c67b271a 100644
--- a/shibsp/Makefile.am
+++ b/shibsp/Makefile.am
@@ -44,6 +44,7 @@ handinclude_HEADERS = \
handler/AbstractHandler.h \
handler/AssertionConsumerService.h \
handler/Handler.h \
+ handler/HandlerConfiguration.h \
handler/LogoutHandler.h \
handler/LogoutInitiator.h \
handler/RemotedHandler.h \
@@ -107,6 +108,7 @@ libshibsp_la_SOURCES = \
handler/impl/AssertionConsumerService.cpp \
handler/impl/AttributeCheckerHandler.cpp \
handler/impl/LocalLogoutInitiator.cpp \
+ handler/impl/DefaultHandlerConfiguration.cpp \
handler/impl/LogoutHandler.cpp \
handler/impl/LogoutInitiator.cpp \
handler/impl/MetadataGenerator.cpp \
diff --git a/shibsp/handler/Handler.h b/shibsp/handler/Handler.h
index e8d6dc7b..4aedd491 100644
--- a/shibsp/handler/Handler.h
+++ b/shibsp/handler/Handler.h
@@ -66,7 +66,7 @@ namespace shibsp {
void SHIBSP_API registerHandlers();
/** SessionInitiator that supports SAML 2.0 AuthnRequests. */
- #define SESSION_INITIATOR "SessionInitiator"
+ #define SESSION_INITIATOR_HANDLER "SessionInitiator"
/** Handler for SSO token handling (the inbound side of SSO). */
#define TOKEN_CONSUMER_HANDLER "TokenConsumer"
@@ -78,7 +78,7 @@ namespace shibsp {
#define LOGOUT_CONSUMER_HANDLER "LogoutConsumer"
/** LogoutInitiator that supports administrative logout. */
- #define ADMIN_LOGOUT "AdminLogout"
+ #define ADMIN_LOGOUT_HANDLER "AdminLogout"
/** Handler for hooking new sessions with attribute checking. */
#define ATTR_CHECKER_HANDLER "AttributeChecker"
diff --git a/shibsp/handler/HandlerConfiguration.h b/shibsp/handler/HandlerConfiguration.h
new file mode 100644
index 00000000..3f475757
--- /dev/null
+++ b/shibsp/handler/HandlerConfiguration.h
@@ -0,0 +1,95 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file shibsp/handler/HandlerConfiguration.h
+ *
+ * Interface to the set of handlers that are exposed by an agent at specific path(s).
+ */
+
+#ifndef __shibsp_handlerconfig_h__
+#define __shibsp_handlerconfig_h__
+
+#include <shibsp/base.h>
+
+#include <memory>
+
+namespace shibsp {
+
+ class SHIBSP_API Handler;
+ class SHIBSP_API DDF;
+
+ /**
+ * Pluggable runtime functionality that implement protocols and services
+ */
+ class SHIBSP_API HandlerConfiguration
+ {
+ MAKE_NONCOPYABLE(HandlerConfiguration);
+ protected:
+ HandlerConfiguration();
+
+ public:
+ virtual ~HandlerConfiguration();
+
+ /**
+ * Gets the Handler installed at a particular path, relative to a "base" URL used for
+ * triggering all Handlers.
+ *
+ * @param path relative path to map to a Handler
+ *
+ * @return the Handler configured at the supplied location, or null
+ */
+ virtual const Handler* getHandler(const char* path) const=0;
+
+ /**
+ * Gets the Handler used for SSO session initiation.
+ *
+ * <p>Only one such Handler may be defined within a configuration and one
+ * must be defined.</p>
+ *
+ * @return the session initiator Handler
+ */
+ virtual const Handler& getSessionInitiator() const=0;
+
+ /**
+ * Gets a DDF object suitable for copying into session initiator requests to the hub.
+ *
+ * <p>This is required to support the communication of the possible response
+ * paths for the agent to the hub when formulating SSO protocol requests, and encapsulates
+ * a number of technical details that may be required, insulating the handlers from
+ * dealing with this iinformation.</p>
+ *
+ * <p>Legacy configurations designed to avoid externally visible changes may continue
+ * to operate multiple handlers to support different SSO bindings or patterns, while
+ * newer deployments should avoid this practice and stick to a single location. The
+ * "meta-informatin" required to support this legacy practice will be embedded within
+ * the structure returned.</p>
+ *
+ * @return DDF object encapsulating token consumer handler metadata
+ */
+ virtual const DDF& getTokenConsumerInfo() const=0;
+
+ /**
+ * Create a new HandlerConfiguration based on the supplied configuration file.
+ *
+ * @param pathname configuration file
+ *
+ * @return the corresponding HandlerConfiguration
+ */
+ static std::unique_ptr<HandlerConfiguration> newHandlerConfiguration(const char* pathname);
+ };
+
+};
+
+#endif /* __shibsp_handlerconfig_h__ */
diff --git a/shibsp/handler/impl/DefaultHandlerConfiguration.cpp b/shibsp/handler/impl/DefaultHandlerConfiguration.cpp
new file mode 100644
index 00000000..9f809435
--- /dev/null
+++ b/shibsp/handler/impl/DefaultHandlerConfiguration.cpp
@@ -0,0 +1,140 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * handler/impl/DefaultHandlerConfiguration.cpp
+ *
+ * Default ptree-based HandlerConfiguration implementation.
+ */
+
+
+#include "internal.h"
+
+#include "exceptions.h"
+#include "AgentConfig.h"
+#include "handler/Handler.h"
+#include "handler/HandlerConfiguration.h"
+#include "logging/Category.h"
+#include "remoting/ddf.h"
+
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/ini_parser.hpp>
+
+#include <map>
+#include <string>
+#include <stdexcept>
+
+using namespace shibsp;
+using namespace boost::property_tree;
+using namespace std;
+
+namespace {
+
+ class DefaultHandlerConfiguration : public virtual HandlerConfiguration {
+ public:
+ DefaultHandlerConfiguration(const char* pathname);
+ ~DefaultHandlerConfiguration() {}
+
+ const Handler* getHandler(const char* path) const;
+ const Handler& getSessionInitiator() const;
+ const DDF& getTokenConsumerInfo() const;
+
+ private:
+ ptree m_pt;
+ map<string,unique_ptr<Handler>> m_handlerMap;
+ const Handler* m_sessionInitiator;
+ DDF m_tokenConsumerConfig;
+ };
+
+};
+
+HandlerConfiguration::HandlerConfiguration() {}
+
+HandlerConfiguration::~HandlerConfiguration() {}
+
+DefaultHandlerConfiguration::DefaultHandlerConfiguration(const char* pathname)
+ : m_sessionInitiator(nullptr), m_tokenConsumerConfig("token_consumers")
+{
+ ini_parser::read_ini(pathname, m_pt);
+
+ m_tokenConsumerConfig.list();
+
+ Category& log = Category::getInstance(SHIBSP_LOGCAT ".HandlerConfiguration");
+
+ for (auto& child : m_pt) {
+ boost::optional<string> type = child.second.get_optional<string>("type");
+ if (!type) {
+ log.warn("config (%s) skipping handler at %s with no type property", pathname, child.first.c_str());
+ continue;
+ }
+
+ if (*type == SESSION_INITIATOR_HANDLER && m_sessionInitiator) {
+ throw ConfigurationException("Multiple SessionInitiator handlers were configured, only one is permitted.");
+ }
+
+ // Handlers have to know their own path in some cases, so that has to be injected into the
+ // factory method, as ptrees don't know their own name unfortunately, thus the section header
+ // value is lost when passing the section tree itself in.
+ unique_ptr<Handler> handler(AgentConfig::getConfig().HandlerManager.newPlugin(
+ type.get(), pair<ptree&,const char*>(child.second, child.first.c_str()), false));
+
+ m_handlerMap[child.first] = std::move(handler);
+ log.info("config (%s) installed %s handler at %s", pathname, type.get().c_str(), child.first.c_str());
+
+ // Save off a single SessionInitiator.
+ if (*type == SESSION_INITIATOR_HANDLER) {
+ m_sessionInitiator = handler.get();
+ } else if (*type == TOKEN_CONSUMER_HANDLER) {
+ DDF tokenConsumer(nullptr);
+ // String value of DDF is the handler location.
+ tokenConsumer.string(child.first.c_str());
+ m_tokenConsumerConfig.add(tokenConsumer);
+
+ // Check for legacy "binding" value to carry along with path as the name of the node.
+ boost::optional<string> legacyBinding = child.second.get_optional<string>("legacyBinding");
+ if (legacyBinding) {
+ tokenConsumer.name(legacyBinding->c_str());
+ }
+ }
+ }
+}
+
+const Handler* DefaultHandlerConfiguration::getHandler(const char* path) const
+{
+ if (path) {
+ const auto& mapping = m_handlerMap.find(path);
+ if (mapping != m_handlerMap.end()) {
+ return mapping->second.get();
+ }
+ }
+ return nullptr;
+}
+
+const Handler& DefaultHandlerConfiguration::getSessionInitiator() const
+{
+ if (m_sessionInitiator) {
+ return *m_sessionInitiator;
+ }
+ throw runtime_error("No SessionInitiator configured?");
+}
+
+const DDF& DefaultHandlerConfiguration::getTokenConsumerInfo() const
+{
+ return m_tokenConsumerConfig;
+}
+
+unique_ptr<HandlerConfiguration> HandlerConfiguration::newHandlerConfiguration(const char* pathname)
+{
+ return unique_ptr<HandlerConfiguration>(new DefaultHandlerConfiguration(pathname));
+}
diff --git a/shibsp/impl/AgentConfig.cpp b/shibsp/impl/AgentConfig.cpp
index a54163c2..ce3174ba 100644
--- a/shibsp/impl/AgentConfig.cpp
+++ b/shibsp/impl/AgentConfig.cpp
@@ -177,19 +177,27 @@ bool AgentInternalConfig::_init(const char* inst_prefix, const char* config_file
// Set up PathResolver component.
m_pathResolver.setDefaultPackageName(PACKAGE_NAME);
m_pathResolver.setDefaultPrefix(inst_prefix2.c_str());
+
+ inst_prefix = getenv("SHIBSP_CFGDIR");
+ if (!inst_prefix || !*inst_prefix)
+ inst_prefix = SHIBSP_CFGDIR;
m_pathResolver.setCfgDir(inst_prefix);
+
inst_prefix = getenv("SHIBSP_LIBDIR");
if (!inst_prefix || !*inst_prefix)
inst_prefix = SHIBSP_LIBDIR;
m_pathResolver.setLibDir(inst_prefix);
+
inst_prefix = getenv("SHIBSP_LOGDIR");
if (!inst_prefix || !*inst_prefix)
inst_prefix = SHIBSP_LOGDIR;
m_pathResolver.setLogDir(inst_prefix);
+
inst_prefix = getenv("SHIBSP_RUNDIR");
if (!inst_prefix || !*inst_prefix)
inst_prefix = SHIBSP_RUNDIR;
m_pathResolver.setRunDir(inst_prefix);
+
inst_prefix = getenv("SHIBSP_CACHEDIR");
if (!inst_prefix || !*inst_prefix)
inst_prefix = SHIBSP_CACHEDIR;
diff --git a/shibsp/impl/DefaultAgent.cpp b/shibsp/impl/DefaultAgent.cpp
index 08740b51..86a120c5 100644
--- a/shibsp/impl/DefaultAgent.cpp
+++ b/shibsp/impl/DefaultAgent.cpp
@@ -25,11 +25,13 @@
#include "Agent.h"
#include "AgentConfig.h"
#include "RequestMapper.h"
+#include "handler/HandlerConfiguration.h"
#include "io/HTTPResponse.h"
#include "logging/Category.h"
#include "remoting/RemotingService.h"
#include "session/SessionCache.h"
#include "util/BoostPropertySet.h"
+#include "util/PathResolver.h"
#include "util/SPConstants.h"
#include "util/Misc.h"
@@ -75,10 +77,19 @@ namespace {
return m_requestMapper.get();
}
+ HandlerConfiguration* getHandlerConfiguration(const char* id=nullptr) const {
+ const auto& config = m_handlerConfigurations.find(id ? id : "default");
+ if (config != m_handlerConfigurations.end()) {
+ return config->second.get();
+ }
+ throw ConfigurationException(string("No HandlerConfiguration matching ID of ") + (id ? id : "default"));
+ }
+
private:
void doRemotingService();
void doSessionCache();
void doRequestMapper();
+ void doHandlerConfigurations();
ptree& m_pt;
Category& m_log;
@@ -89,6 +100,7 @@ namespace {
unique_ptr<RemotingService> m_remotingService;
unique_ptr<SessionCache> m_sessionCache;
unique_ptr<RequestMapper> m_requestMapper;
+ map<string,unique_ptr<HandlerConfiguration>> m_handlerConfigurations;
};
#if defined (_MSC_VER)
@@ -132,8 +144,7 @@ void DefaultAgent::init()
doRemotingService();
doSessionCache();
doRequestMapper();
-
- // TODO: the Application related material needs to be replaced with new approaches.
+ doHandlerConfigurations();
}
void DefaultAgent::doRemotingService()
@@ -177,3 +188,30 @@ void DefaultAgent::doRequestMapper()
m_log.debug("[request-mapper] section absent, skipping RequestMapper creation");
}
}
+
+void DefaultAgent::doHandlerConfigurations()
+{
+ // Check for testing boolean to disable handlers.
+ if (getBool("skipHandlers", false)) {
+ return;
+ }
+
+ boost::optional<ptree&> child = m_pt.get_child_optional("handlers");
+ if (child) {
+ for (const auto& keys : *child) {
+ boost::optional<string> path = keys.second.get_value_optional<string>();
+ if (!path) {
+ m_log.warn("skipping property key with no value in [handlers] section");
+ continue;
+ }
+ AgentConfig::getConfig().getPathResolver().resolve(*path, PathResolver::SHIBSP_CFG_FILE);
+ m_handlerConfigurations[keys.first] = HandlerConfiguration::newHandlerConfiguration(path->c_str());
+ m_log.info("installed '%s' HandlerConfiguration from %s", keys.first.c_str(), path->c_str());
+ }
+ } else {
+ string path("handlers.ini");
+ AgentConfig::getConfig().getPathResolver().resolve(path, PathResolver::SHIBSP_CFG_FILE);
+ m_handlerConfigurations["default"] = HandlerConfiguration::newHandlerConfiguration(path.c_str());
+ m_log.info("installed 'default' HandlerConfiguration from %s", path.c_str());
+ }
+}
diff --git a/shibsp/paths.h.in b/shibsp/paths.h.in
index a2b3d9b0..06a56ac6 100644
--- a/shibsp/paths.h.in
+++ b/shibsp/paths.h.in
@@ -21,9 +21,6 @@
#ifndef __shibsp_paths_h__
#define __shibsp_paths_h__
-/** Default schema catalogs. */
-#define SHIBSP_SCHEMAS "@-XMLTOOLINGXMLDIR-@/catalog.xml:@-OPENSAMLXMLDIR-@/saml20-catalog.xml:@-OPENSAMLXMLDIR-@/saml11-catalog.xml:@-PKGXMLDIR-@/catalog.xml"
-
/** Default prefix for installation (used to resolve relative paths). */
#define SHIBSP_PREFIX "@-PREFIX-@"
diff --git a/tests/data/console-shibboleth.ini b/tests/data/console-shibboleth.ini
index fc892452..1fba6104 100644
--- a/tests/data/console-shibboleth.ini
+++ b/tests/data/console-shibboleth.ini
@@ -1,4 +1,5 @@
[global]
+skipHandlers = true
# Use "partial" for partial matching
regexMatching = full
diff --git a/tests/data/fatal-exts-shibboleth.ini b/tests/data/fatal-exts-shibboleth.ini
index 4bed3bcd..a4d883c5 100644
--- a/tests/data/fatal-exts-shibboleth.ini
+++ b/tests/data/fatal-exts-shibboleth.ini
@@ -1,3 +1,6 @@
+[global]
+skipHandlers = true
+
[extensions]
/path/to/extension.so = true
diff --git a/tests/data/impl/console-shibboleth.ini b/tests/data/impl/console-shibboleth.ini
index 575185e6..b841324a 100644
--- a/tests/data/impl/console-shibboleth.ini
+++ b/tests/data/impl/console-shibboleth.ini
@@ -1,3 +1,6 @@
+[global]
+skipHandlers = true
+
[logging]
type = console
defaultLevel = INFO
diff --git a/tests/data/nonfatal-exts-shibboleth.ini b/tests/data/nonfatal-exts-shibboleth.ini
index cd562e2f..7912aab0 100644
--- a/tests/data/nonfatal-exts-shibboleth.ini
+++ b/tests/data/nonfatal-exts-shibboleth.ini
@@ -1,3 +1,6 @@
+[global]
+skipHandlers = true
+
[extensions]
/path/to/extension.so = false
diff --git a/tests/data/platform/iis/console-shibboleth.ini b/tests/data/platform/iis/console-shibboleth.ini
index 7b976b9e..738c4845 100644
--- a/tests/data/platform/iis/console-shibboleth.ini
+++ b/tests/data/platform/iis/console-shibboleth.ini
@@ -1,3 +1,6 @@
+[global]
+skipHandlers = true
+
[logging]
type = console
defaultLevel = INFO
diff --git a/tests/data/remoting/impl/shibboleth.ini b/tests/data/remoting/impl/shibboleth.ini
index d868cac8..b1baaf1e 100644
--- a/tests/data/remoting/impl/shibboleth.ini
+++ b/tests/data/remoting/impl/shibboleth.ini
@@ -1,3 +1,6 @@
+[global]
+skipHandlers = true
+
[remoting]
baseURL = https://localhost/idp/profile/sp
agentID = sp.example.org
diff --git a/tests/data/syslog-shibboleth.ini b/tests/data/syslog-shibboleth.ini
index 44c43ba8..7e164311 100644
--- a/tests/data/syslog-shibboleth.ini
+++ b/tests/data/syslog-shibboleth.ini
@@ -1,3 +1,6 @@
+[global]
+skipHandlers = true
+
[logging]
type = syslog
defaultLevel = WARN
diff --git a/tests/data/util/reloadablefile/console-shibboleth.ini b/tests/data/util/reloadablefile/console-shibboleth.ini
index e2d06c12..6699f3b1 100644
--- a/tests/data/util/reloadablefile/console-shibboleth.ini
+++ b/tests/data/util/reloadablefile/console-shibboleth.ini
@@ -1,3 +1,6 @@
+[global]
+skipHandlers = true
+
[logging]
type = console
default-level = INFO
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list