[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