[cpp-sp] branch main updated: Merge time-based AC rules back into main class.

Scott Cantor cantor.2 at osu.edu
Wed Oct 29 13:07:55 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:
https://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=f160d1c6c12dfc0564bc267d1384f0a2b99d7717

The following commit(s) were added to refs/heads/main by this push:
     new f160d1c6 Merge time-based AC rules back into main class.
f160d1c6 is described below

commit f160d1c6c12dfc0564bc267d1384f0a2b99d7717
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Oct 29 09:07:46 2025 -0400

    Merge time-based AC rules back into main class.
---
 Projects/vc22/Tests.vcxproj                        |   3 +-
 Projects/vc22/shibsp.vcxproj                       |   4 +-
 Projects/vc22/shibsp.vcxproj.filters               |   8 +-
 shibsp/AccessControl.h                             |   8 +-
 shibsp/Makefile.am                                 |   2 -
 shibsp/impl/ChainingAccessControl.cpp              | 165 -----------
 shibsp/impl/TimeAccessControl.cpp                  | 302 ---------------------
 shibsp/impl/XMLAccessControl.cpp                   | 233 +++++++++++++++-
 tests/Makefile.am                                  |   1 -
 tests/data/impl/acl/inline-new-time-invalid.xml    |   5 +
 tests/data/impl/acl/inline-new-time-valid.xml      |   6 +
 tests/data/impl/acl/inline-old-time-invalid.xml    |   6 +
 tests/data/impl/acl/inline-old-time-valid.xml      |   6 +
 tests/data/impl/acl/inline-timesinceauth.xml       |   5 +
 tests/data/impl/acl/inline-year-invalid.xml        |   6 +
 tests/data/impl/acl/inline-year-valid.xml          |   6 +
 tests/data/impl/acl/time/acl-badxml.xml            |   1 -
 tests/data/impl/acl/time/external-acl-badxml.xml   |   1 -
 .../data/impl/acl/time/inline-new-time-invalid.xml |   4 -
 tests/data/impl/acl/time/inline-new-time-valid.xml |   4 -
 .../data/impl/acl/time/inline-old-time-invalid.xml |   4 -
 tests/data/impl/acl/time/inline-old-time-valid.xml |   4 -
 tests/data/impl/acl/time/inline-timesinceauth.xml  |   3 -
 tests/data/impl/acl/time/inline-year-invalid.xml   |   4 -
 tests/data/impl/acl/time/inline-year-valid.xml     |   4 -
 tests/data/impl/acl/time/internal-acl-invalid2.xml |   3 -
 tests/impl/TimeAccessControlTests.cpp              | 299 --------------------
 tests/impl/XMLAccessControlTests.cpp               | 145 ++++++++++
 28 files changed, 415 insertions(+), 827 deletions(-)

diff --git a/Projects/vc22/Tests.vcxproj b/Projects/vc22/Tests.vcxproj
index 3be297b2..ef11b149 100644
--- a/Projects/vc22/Tests.vcxproj
+++ b/Projects/vc22/Tests.vcxproj
@@ -30,7 +30,6 @@
     <ClCompile Include="..\..\tests\AgentConfigTests.cpp" />
     <ClCompile Include="..\..\tests\AgentTestSuite.cpp" />
     <ClCompile Include="..\..\tests\attribute\impl\DefaultAttributeConfigurationTests.cpp" />
-    <ClCompile Include="..\..\tests\impl\TimeAccessControlTests.cpp" />
     <ClCompile Include="..\..\tests\impl\XMLAccessControlTests.cpp" />
     <ClCompile Include="..\..\tests\impl\XMLRequestMapperTests.cpp" />
     <ClCompile Include="..\..\tests\remoting\impl\SecretSourceTests.cpp">
@@ -341,4 +340,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/Projects/vc22/shibsp.vcxproj b/Projects/vc22/shibsp.vcxproj
index 35889131..0ab66ff9 100644
--- a/Projects/vc22/shibsp.vcxproj
+++ b/Projects/vc22/shibsp.vcxproj
@@ -111,9 +111,7 @@
     </ClCompile>
     <ClCompile Include="..\..\shibsp\handler\impl\TokenConsumer.cpp" />
     <ClCompile Include="..\..\shibsp\impl\AgentConfig.cpp" />
-    <ClCompile Include="..\..\shibsp\impl\ChainingAccessControl.cpp" />
     <ClCompile Include="..\..\shibsp\impl\DefaultAgent.cpp" />
-    <ClCompile Include="..\..\shibsp\impl\TimeAccessControl.cpp" />
     <ClCompile Include="..\..\shibsp\impl\XMLAccessControl.cpp" />
     <ClCompile Include="..\..\shibsp\impl\XMLRequestMapper.cpp" />
     <ClCompile Include="..\..\shibsp\io\impl\CookieManager.cpp" />
@@ -397,4 +395,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/Projects/vc22/shibsp.vcxproj.filters b/Projects/vc22/shibsp.vcxproj.filters
index 00cb30ea..2e50d4ef 100644
--- a/Projects/vc22/shibsp.vcxproj.filters
+++ b/Projects/vc22/shibsp.vcxproj.filters
@@ -263,9 +263,6 @@
     <ClCompile Include="..\..\shibsp\impl\AgentConfig.cpp">
       <Filter>Source Files\Impl</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\shibsp\impl\ChainingAccessControl.cpp">
-      <Filter>Source Files\Impl</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\shibsp\impl\XMLAccessControl.cpp">
       <Filter>Source Files\Impl</Filter>
     </ClCompile>
@@ -389,13 +386,10 @@
     <ClCompile Include="..\..\shibsp\csprng\impl\csprng.cpp">
       <Filter>Source Files\Impl</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\shibsp\impl\TimeAccessControl.cpp">
-      <Filter>Source Files\Impl</Filter>
-    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\..\shibsp\shibsp.rc">
       <Filter>Resource Files</Filter>
     </ResourceCompile>
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/shibsp/AccessControl.h b/shibsp/AccessControl.h
index e5f675ed..42b6405b 100644
--- a/shibsp/AccessControl.h
+++ b/shibsp/AccessControl.h
@@ -68,15 +68,9 @@ namespace shibsp {
      */
     void SHIBSP_API registerAccessControls();
 
-    /** Chains together multiple plugins. */
-    #define CHAINING_ACCESS_CONTROL "Chaining"
-
-    /** AccessControl based on rudimentary XML syntax. */
+    /** AccessControl based on XML syntax. */
     #define XML_ACCESS_CONTROL      "XML"
 
-    /** AccessControl with time-based rules. */
-    #define TIME_ACCESS_CONTROL      "Time"
-
     /** Reserved for Apache-style .htaccess support. */
     #define HT_ACCESS_CONTROL       "htaccess"
 };
diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am
index ab79e9e6..4ac1855e 100644
--- a/shibsp/Makefile.am
+++ b/shibsp/Makefile.am
@@ -114,8 +114,6 @@ libshibsp_la_SOURCES = \
 	handler/impl/TokenConsumer.cpp \
 	impl/AgentConfig.cpp \
 	impl/DefaultAgent.cpp \
-	impl/ChainingAccessControl.cpp \
-	impl/TimeAccessControl.cpp \
 	impl/XMLAccessControl.cpp \
 	impl/XMLRequestMapper.cpp \
 	io/impl/CookieManager.cpp \
diff --git a/shibsp/impl/ChainingAccessControl.cpp b/shibsp/impl/ChainingAccessControl.cpp
deleted file mode 100644
index a1919914..00000000
--- a/shibsp/impl/ChainingAccessControl.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * impl/ChainingAccessControl.cpp
- *
- * Access control plugin that combines other plugins.
- */
-
-#include "internal.h"
-#include "exceptions.h"
-
-#include "AccessControl.h"
-#include "AgentConfig.h"
-#include "SPRequest.h"
-#include "logging/Category.h"
-
-#include <algorithm>
-#include <memory>
-#include <vector>
-#include <boost/property_tree/ptree.hpp>
-
-using namespace shibsp;
-using namespace boost::property_tree;
-using namespace std;
-
-namespace shibsp {
-    extern AccessControl* SHIBSP_DLLLOCAL XMLAccessControlFactory(ptree& pt, bool deprecationSupport);
-    extern AccessControl* SHIBSP_DLLLOCAL TimeAccessControlFactory(ptree& pt, bool deprecationSupport);
-}
-
-AccessControl::AccessControl()
-{
-}
-
-AccessControl::~AccessControl()
-{
-}
-
-
-namespace {
-
-    class ChainingAccessControl : public AccessControl
-    {
-    public:
-        ChainingAccessControl(ptree& pt, bool deprecationSupport);
-
-        ~ChainingAccessControl() {}
-
-        void lock_shared() {
-            for (auto& i : m_ac) {
-                i->lock_shared();
-            }
-        }
-        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_shared();
-            }
-        }
-
-        aclresult_t authorized(const SPRequest& request, const Session* session) const;
-
-    private:
-        enum operator_t { OP_AND, OP_OR } m_op;
-        vector<unique_ptr<AccessControl>> m_ac;
-    };
-
-    AccessControl* SHIBSP_DLLLOCAL ChainingAccessControlFactory(ptree& pt, bool deprecationSupport)
-    {
-        return new ChainingAccessControl(pt, deprecationSupport);
-    }
-}
-
-void SHIBSP_API shibsp::registerAccessControls()
-{
-    AgentConfig& conf=AgentConfig::getConfig();
-    conf.AccessControlManager.registerFactory(CHAINING_ACCESS_CONTROL, ChainingAccessControlFactory);
-    conf.AccessControlManager.registerFactory(XML_ACCESS_CONTROL, XMLAccessControlFactory);
-    conf.AccessControlManager.registerFactory(TIME_ACCESS_CONTROL, TimeAccessControlFactory);
-}
-
-ChainingAccessControl::ChainingAccessControl(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.");
-    }
-
-    Category& log = Category::getInstance(SHIBSP_LOGCAT ".AccessControl.Chaining");
-
-    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.");
-        }
-
-        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()) {
-        throw ConfigurationException("Chaining AccessControl plugin requires at least one child plugin.");
-    }
-}
-
-AccessControl::aclresult_t ChainingAccessControl::authorized(const SPRequest& request, const Session* session) const
-{
-    switch (m_op) {
-        case OP_AND:
-        {
-            for (const auto& i : m_ac) {
-                if (i->authorized(request, session) != shib_acl_true) {
-                    request.log(Priority::SHIB_DEBUG, "embedded AccessControl plugin unsuccessful, denying access");
-                    return shib_acl_false;
-                }
-            }
-            return shib_acl_true;
-        }
-
-        case OP_OR:
-        {
-            for (const auto& i : m_ac) {
-                if (i->authorized(request, session) == shib_acl_true)
-                    return shib_acl_true;
-            }
-            request.log(Priority::SHIB_DEBUG, "all embedded AccessControl plugins unsuccessful, denying access");
-            return shib_acl_false;
-        }
-    }
-    request.log(Priority::SHIB_DEBUG, "unknown operation in access control policy, denying access");
-    return shib_acl_false;
-}
diff --git a/shibsp/impl/TimeAccessControl.cpp b/shibsp/impl/TimeAccessControl.cpp
deleted file mode 100644
index fc34e584..00000000
--- a/shibsp/impl/TimeAccessControl.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * TimeAccessControl.cpp
- *
- * Access control plugin for time-based policies.
- */
-
-#include "internal.h"
-#include "exceptions.h"
-
-#include "AccessControl.h"
-#include "Agent.h"
-#include "SPRequest.h"
-#include "attribute/AttributeConfiguration.h"
-#include "logging/Category.h"
-#include "remoting/ddf.h"
-#include "session/SessionCache.h"
-#include "util/Lockable.h"
-#include "util/Misc.h"
-#include "util/ReloadableXMLFile.h"
-
-#include <algorithm>
-#include <memory>
-#include <set>
-
-#include <boost/algorithm/string.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/property_tree/ptree.hpp>
-
-#ifndef HAVE_STRCASECMP
-# define strcasecmp _stricmp
-#endif
-
-using namespace shibsp;
-using namespace boost::property_tree;
-using namespace std;
-
-namespace {
-
-    class Rule : public AccessControl, public NoOpSharedLockable
-    {
-    public:
-        Rule(const string& name, const ptree& pt);
-        ~Rule() {}
-
-        aclresult_t authorized(const SPRequest& request, const Session* session) const;
-
-    private:
-        enum { TM_AUTHN, TM_TIME, TM_YEAR, TM_MONTH, TM_DAY, TM_HOUR, TM_MINUTE, TM_SECOND, TM_WDAY } m_type;
-        enum { OP_LT, OP_LE, OP_EQ, OP_GE, OP_GT } m_op;
-        time_t m_value;
-    };
-
-    class TimeAccessControl : public AccessControl, public NoOpSharedLockable
-    {
-    public:
-        TimeAccessControl(const ptree& pt);
-        ~TimeAccessControl() {}
-
-        aclresult_t authorized(const SPRequest& request, const Session* session) const;
-
-    private:
-        enum { OP_AND, OP_OR } m_op;
-        vector<unique_ptr<Rule>> m_rules;
-    };
-}
-
-namespace shibsp {
-    AccessControl* SHIBSP_DLLLOCAL TimeAccessControlFactory(ptree& pt, bool deprecationSupport)
-    {
-        return new TimeAccessControl(pt);
-    }
-};
-
-Rule::Rule(const string& name, const ptree& pt)
-{
-    static const char Day[] =               "Day";
-    static const char DayOfWeek[] =         "DayOfWeek";
-    static const char Hour[] =              "Hour";
-    static const char Minute[] =            "Minute";
-    static const char Month[] =             "Month";
-    static const char Second[] =            "Second";
-    static const char Time[] =              "Time";
-    static const char TimeSinceAuthn[] =    "TimeSinceAuthn";
-    static const char Year[] =              "Year";
-
-    // The TimeSinceAuthn rule operates on a Duration inside the element body,
-    // which should be the value of the tree.
-
-    if (name == TimeSinceAuthn) {
-        m_type = TM_AUTHN;
-        if ((m_value = parseISODuration(pt.get_value(""))) < 0) {
-            throw ConfigurationException("Unable to parse duration in TimeSinceAuthn rule.");
-        }
-        return;
-    }
-    
-    // Anything else we have to parse the element body.
-    string s = pt.get_value("");
-    boost::trim(s);
-    vector<string> tokens;
-    if (boost::split(tokens, s, boost::is_space(), boost::algorithm::token_compress_on).size() != 2) {
-        throw ConfigurationException("Time-based rule requires element content of the form \"LT|LE|EQ|GE|GT value\".");
-    }
-    string& op = tokens.front();
-    if (op == "LT")         { m_op = OP_LT; }
-    else if (op == "LE")    { m_op = OP_LE; }
-    else if (op == "EQ")    { m_op = OP_EQ; }
-    else if (op == "GE")    { m_op = OP_GE; }
-    else if (op == "GT")    { m_op = OP_GT; }
-    else {
-        throw ConfigurationException("First component of time-based rule must be one of LT, LE, EQ, GE, GT.");
-    }
-
-    if (name == Time) {
-        m_type = TM_TIME;
-        if ((m_value = parseISODateTime(tokens.back())) < 0) {
-            throw ConfigurationException("Error parsing timestamp in Time rule.");
-        }
-        return;
-    }
-
-    m_value = boost::lexical_cast<time_t>(tokens.back());
-    if (name == Year)           { m_type = TM_YEAR; }
-    else if (name == Month)     { m_type = TM_MONTH; }
-    else if (name == Day)       { m_type = TM_DAY; }
-    else if (name == Hour)      { m_type = TM_HOUR; }
-    else if (name == Minute)    { m_type = TM_MINUTE; }
-    else if (name == Second)    { m_type = TM_SECOND; }
-    else if (name == DayOfWeek) { m_type = TM_WDAY; }
-    else {
-        throw ConfigurationException("Unrecognized time-based rule.");
-    }
-}
-
-AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Session* session) const
-{
-    time_t operand = 0;
-
-    if (m_type == TM_AUTHN) {
-        if (session) {
-            // Locate the Attribute to be used for accessing the auth timestamp.
-            const AttributeConfiguration& config = request.getAgent().getAttributeConfiguration(
-                request.getRequestSettings().first->getString(RequestMapper::ATTRIBUTE_CONFIG_ID_PROP_NAME));
-            const auto attr = session->getAttributes().find(
-                config.getString(AttributeConfiguration::LEGACY_AUTHTIME_ATTRIBUTE_PROP_NAME,
-                    AttributeConfiguration::LEGACY_AUTHTIME_ATTRIBUTE_PROP_DEFAULT));
-            if (attr == session->getAttributes().end()) {
-                request.debug("Attribute carrying authentication time unnvailable");
-                return shib_acl_false;
-            }
-
-            DDF val = const_cast<DDF&>(attr->second).first();
-            if (val.isstring()) {
-                const char* authtime = const_cast<DDF&>(attr->second).first().string();
-                if (authtime) {
-                    if (operand = parseISODateTime(authtime) < 0) {
-                       request.error("Error parsing authentication time from designated Attribute.");
-                       return shib_acl_false;
-                    }
-                }
-            }
-            else if (val.islong()) {
-                operand = val.longinteger();
-            }
-            if (operand > 0) {
-                if (time(nullptr) - operand <= m_value) {
-                    return shib_acl_true;
-                }
-
-                request.debug("elapsed time since authentication exceeds limit");
-                return shib_acl_false;
-            }
-            else {
-                request.debug("Attribute carrying authentication time unnvailable");
-                return shib_acl_false;
-            }
-        }
-        else {
-            request.debug("session unnvailable");
-            return shib_acl_false;
-        }
-    }
-
-    // Extract value from tm struct or time directly.
-    operand = time(nullptr);
-    if (m_type != TM_TIME) {
-#ifndef HAVE_LOCALTIME_R
-        struct tm* ptime = localtime(&operand);
-#else
-        struct tm res;
-        struct tm* ptime = localtime_r(&operand, &res);
-#endif
-        switch (m_type) {
-            case TM_YEAR:
-                operand = ptime->tm_year + 1900;
-                break;
-            case TM_MONTH:
-                operand = ptime->tm_mon + 1;
-                break;
-            case TM_DAY:
-                operand = ptime->tm_mday;
-                break;
-            case TM_HOUR:
-                operand = ptime->tm_hour;
-                break;
-            case TM_MINUTE:
-                operand = ptime->tm_min;
-                break;
-            case TM_SECOND:
-                operand = ptime->tm_sec;
-                break;
-            case TM_WDAY:
-                operand = ptime->tm_wday;
-                break;
-        }
-    }
-
-    // Compare operand to test value in rule using rule operator.
-    switch (m_op) {
-        case OP_LT:
-            return (operand < m_value) ? shib_acl_true : shib_acl_false;
-        case OP_LE:
-            return (operand <= m_value) ? shib_acl_true : shib_acl_false;
-        case OP_EQ:
-            return (operand == m_value) ? shib_acl_true : shib_acl_false;
-        case OP_GE:
-            return (operand >= m_value) ? shib_acl_true : shib_acl_false;
-        case OP_GT:
-            return (operand > m_value) ? shib_acl_true : shib_acl_false;
-    }
-    return shib_acl_false;
-}
-
-TimeAccessControl::TimeAccessControl(const ptree& pt) : m_op(OP_AND)
-{
-    static const char OPERATOR_PROP_PATH[] = "<xmlattr>.operator";
-    static const char OR_OPERATOR_VALUE[] = "OR";
-    static const char AND_OPERATOR_VALUE[] = "AND";
-
-    string op = pt.get(OPERATOR_PROP_PATH, AND_OPERATOR_VALUE);
-    if (op == AND_OPERATOR_VALUE) {
-        m_op = OP_AND;
-    }
-    else if (op == OR_OPERATOR_VALUE) {
-        m_op = OP_OR;
-    }
-    else {
-        throw ConfigurationException("Unrecognized operator in Time AccessControl configuration.");
-    }
-
-    for (const auto& child : pt) {
-        if (child.first != "<xmlattr>") {
-            m_rules.push_back(unique_ptr<Rule>(new Rule(child.first, child.second)));
-        }
-    }
-
-    if (m_rules.empty())
-        throw ConfigurationException("Time AccessControl plugin requires at least one rule.");
-}
-
-
-AccessControl::aclresult_t TimeAccessControl::authorized(const SPRequest& request, const Session* session) const
-{
-    switch (m_op) {
-        case OP_AND:
-        {
-            for (auto& rule : m_rules) {
-                if (rule->authorized(request, session) != shib_acl_true) {
-                    request.debug("time-based rule unsuccessful, denying access");
-                    return shib_acl_false;
-                }
-            }
-            return shib_acl_true;
-        }
-
-        case OP_OR:
-        {
-            for (auto& rule : m_rules) {
-                if (rule->authorized(request,session) == shib_acl_true)
-                    return shib_acl_true;
-            }
-            request.debug("all time-based rules unsuccessful, denying access");
-            return shib_acl_false;
-        }
-    }
-    request.warn("unknown operator in access control policy, denying access");
-    return shib_acl_false;
-}
diff --git a/shibsp/impl/XMLAccessControl.cpp b/shibsp/impl/XMLAccessControl.cpp
index 5512aaaa..5beabf12 100644
--- a/shibsp/impl/XMLAccessControl.cpp
+++ b/shibsp/impl/XMLAccessControl.cpp
@@ -36,6 +36,8 @@
 #include <algorithm>
 #include <memory>
 #include <set>
+#include <boost/algorithm/string.hpp>
+#include <boost/lexical_cast.hpp>
 #include <boost/property_tree/ptree.hpp>
 
 #ifdef SHIBSP_USE_BOOST_REGEX
@@ -83,6 +85,23 @@ namespace {
         regexp::regex m_re;
     };
 
+    class TimeRule : public AccessControl, public NoOpSharedLockable
+    {
+    public:
+        TimeRule(const string& name, const ptree& pt);
+        ~TimeRule() {}
+
+        aclresult_t authorized(const SPRequest& request, const Session* session) const;
+
+        enum time_type_t {
+            TM_AUTHN, TM_TIME, TM_YEAR, TM_MONTH, TM_DAY, TM_HOUR, TM_MINUTE, TM_SECOND, TM_WDAY
+        };
+    private:
+        time_type_t m_type;
+        enum { OP_LT, OP_LE, OP_EQ, OP_GE, OP_GT } m_op;
+        time_t m_value;
+    };
+
     class Operator : public AccessControl, public NoOpSharedLockable
     {
     public:
@@ -96,10 +115,35 @@ namespace {
         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";
+    static const char AND_PROP_PATH[] =             "AND";
+    static const char OR_PROP_PATH[] =              "OR";
+    static const char NOT_PROP_PATH[] =             "NOT";
+    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";
+    static const char TIMESINCEAUTHN_PROP_PATH[] =  "TimeSinceAuthn";
+    static const char TIME_PROP_PATH[] =            "Time";
+    static const char DAY_PROP_PATH[] =             "Day";
+    static const char DAYOFWEEK_PROP_PATH[] =       "DayOfWeek";
+    static const char HOUR_PROP_PATH[] =            "Hour";
+    static const char MINUTE_PROP_PATH[] =          "Minute";
+    static const char MONTH_PROP_PATH[] =           "Month";
+    static const char SECOND_PROP_PATH[] =          "Second";
+    static const char YEAR_PROP_PATH[] =            "Year";
+
+    // Indexed time rules for comparison/lookup in constructors.
+    static map<string,TimeRule::time_type_t> g_timeRules = {
+        { TIMESINCEAUTHN_PROP_PATH, TimeRule::TM_AUTHN },
+        { TIME_PROP_PATH, TimeRule::TM_TIME },
+        { DAY_PROP_PATH, TimeRule::TM_DAY },
+        { DAYOFWEEK_PROP_PATH, TimeRule::TM_WDAY },
+        { HOUR_PROP_PATH, TimeRule::TM_HOUR },
+        { MINUTE_PROP_PATH, TimeRule::TM_MINUTE },
+        { MONTH_PROP_PATH, TimeRule::TM_MONTH },
+        { SECOND_PROP_PATH, TimeRule::TM_SECOND },
+        { YEAR_PROP_PATH, TimeRule::TM_YEAR }
+    };
 
 #if defined (_MSC_VER)
     #pragma warning( push )
@@ -140,6 +184,20 @@ namespace shibsp {
     }
 };
 
+void SHIBSP_API shibsp::registerAccessControls()
+{
+    AgentConfig& conf=AgentConfig::getConfig();
+    conf.AccessControlManager.registerFactory(XML_ACCESS_CONTROL, XMLAccessControlFactory);
+}
+
+AccessControl::AccessControl()
+{
+}
+
+AccessControl::~AccessControl()
+{
+}
+
 Rule::Rule(const ptree& pt) : m_alias(pt.get(REQUIRE_PROP_PATH, ""))
 {
     if (m_alias.empty()) {
@@ -236,7 +294,7 @@ RuleRegex::RuleRegex(const ptree& pt)
 
     if (m_alias == "authnContextClassRef") {
         AgentConfig::getConfig().deprecation().warn(
-            "RuleRegex specifying authnContextClassRef is deprecated and will be removed from a future version");
+            "Rule specifying authnContextClassRef is deprecated and will be removed from a future version");
     }
 
     static const char CASE_SENSITIVE_PROP_PATH[] = "caseSensitive";
@@ -308,6 +366,154 @@ AccessControl::aclresult_t RuleRegex::authorized(const SPRequest& request, const
     return shib_acl_false;
 }
 
+TimeRule::TimeRule(const string& name, const ptree& pt)
+{
+    // The TimeSinceAuthn rule operates on a Duration inside the element body,
+    // which should be the value of the tree.
+
+    if (name == TIMESINCEAUTHN_PROP_PATH) {
+        m_type = TM_AUTHN;
+        if ((m_value = parseISODuration(pt.get_value(""))) < 0) {
+            throw ConfigurationException("Unable to parse duration in TimeSinceAuthn rule.");
+        }
+        return;
+    }
+    
+    // Anything else we have to parse the element body.
+    string s = pt.get_value("");
+    boost::trim(s);
+    vector<string> tokens;
+    if (boost::split(tokens, s, boost::is_space(), boost::algorithm::token_compress_on).size() != 2) {
+        throw ConfigurationException("Time-based rule requires element content of the form \"LT|LE|EQ|GE|GT value\".");
+    }
+    string& op = tokens.front();
+    if (op == "LT")         { m_op = OP_LT; }
+    else if (op == "LE")    { m_op = OP_LE; }
+    else if (op == "EQ")    { m_op = OP_EQ; }
+    else if (op == "GE")    { m_op = OP_GE; }
+    else if (op == "GT")    { m_op = OP_GT; }
+    else {
+        throw ConfigurationException("First component of time-based rule must be one of LT, LE, EQ, GE, GT.");
+    }
+
+    if (name == TIME_PROP_PATH) {
+        m_type = g_timeRules[name];
+        if ((m_value = parseISODateTime(tokens.back())) < 0) {
+            throw ConfigurationException("Error parsing timestamp in Time rule.");
+        }
+        return;
+    }
+
+    const auto i = g_timeRules.find(name);
+    if (i != g_timeRules.end()) {
+        m_type = i->second;
+        m_value = boost::lexical_cast<time_t>(tokens.back());
+    }
+    else {
+        throw ConfigurationException("Unrecognized time-based rule.");
+    }
+}
+
+AccessControl::aclresult_t TimeRule::authorized(const SPRequest& request, const Session* session) const
+{
+    time_t operand = 0;
+
+    if (m_type == TM_AUTHN) {
+        if (session) {
+            // Locate the Attribute to be used for accessing the auth timestamp.
+            const AttributeConfiguration& config = request.getAgent().getAttributeConfiguration(
+                request.getRequestSettings().first->getString(RequestMapper::ATTRIBUTE_CONFIG_ID_PROP_NAME));
+            const auto attr = session->getAttributes().find(
+                config.getString(AttributeConfiguration::LEGACY_AUTHTIME_ATTRIBUTE_PROP_NAME,
+                    AttributeConfiguration::LEGACY_AUTHTIME_ATTRIBUTE_PROP_DEFAULT));
+            if (attr == session->getAttributes().end()) {
+                request.debug("Attribute carrying authentication time unnvailable");
+                return shib_acl_false;
+            }
+
+            DDF val = const_cast<DDF&>(attr->second).first();
+            if (val.isstring()) {
+                const char* authtime = const_cast<DDF&>(attr->second).first().string();
+                if (authtime) {
+                    if ((operand = parseISODateTime(authtime)) < 0) {
+                       request.error("Error parsing authentication time from designated Attribute.");
+                       return shib_acl_false;
+                    }
+                }
+            }
+            else if (val.islong()) {
+                operand = val.longinteger();
+            }
+            
+            if (operand > 0) {
+                if (time(nullptr) - operand <= m_value) {
+                    return shib_acl_true;
+                }
+
+                request.debug("elapsed time since authentication exceeds limit");
+                return shib_acl_false;
+            }
+            else {
+                request.debug("Attribute carrying authentication time unnvailable");
+                return shib_acl_false;
+            }
+        }
+        else {
+            request.debug("session unnvailable");
+            return shib_acl_false;
+        }
+    }
+
+    // Extract value from tm struct or time directly.
+    operand = time(nullptr);
+    if (m_type != TM_TIME) {
+#ifndef HAVE_LOCALTIME_R
+        struct tm* ptime = localtime(&operand);
+#else
+        struct tm res;
+        struct tm* ptime = localtime_r(&operand, &res);
+#endif
+        switch (m_type) {
+            case TM_YEAR:
+                operand = ptime->tm_year + 1900;
+                break;
+            case TM_MONTH:
+                operand = ptime->tm_mon + 1;
+                break;
+            case TM_DAY:
+                operand = ptime->tm_mday;
+                break;
+            case TM_HOUR:
+                operand = ptime->tm_hour;
+                break;
+            case TM_MINUTE:
+                operand = ptime->tm_min;
+                break;
+            case TM_SECOND:
+                operand = ptime->tm_sec;
+                break;
+            case TM_WDAY:
+                operand = ptime->tm_wday;
+                break;
+        }
+    }
+
+    // Compare operand to test value in rule using rule operator.
+    switch (m_op) {
+        case OP_LT:
+            return (operand < m_value) ? shib_acl_true : shib_acl_false;
+        case OP_LE:
+            return (operand <= m_value) ? shib_acl_true : shib_acl_false;
+        case OP_EQ:
+            return (operand == m_value) ? shib_acl_true : shib_acl_false;
+        case OP_GE:
+            return (operand >= m_value) ? shib_acl_true : shib_acl_false;
+        case OP_GT:
+            return (operand > m_value) ? shib_acl_true : shib_acl_false;
+    }
+    return shib_acl_false;
+}
+
 Operator::Operator(const string& name, const ptree& pt)
 {
     if (name == "NOT") {
@@ -330,9 +536,18 @@ Operator::Operator(const string& name, const ptree& pt)
         else if (child.first == RULE_REGEX_PROP_PATH) {
             m_operands.push_back(unique_ptr<AccessControl>(new RuleRegex(child.second)));
         }
-        else if (child.first != "<xmlattr>") {
+        else if (child.first == AND_PROP_PATH || child.first == OR_PROP_PATH || child.first == NOT_PROP_PATH) {
             m_operands.push_back(unique_ptr<AccessControl>(new Operator(child.first, child.second)));
         }
+        else if (g_timeRules.find(child.first) != g_timeRules.end()) {
+            m_operands.push_back(unique_ptr<AccessControl>(new TimeRule(child.first, child.second)));
+        }
+        else if (child.first == "<xmlattr>") {
+            continue;
+        }
+        else {
+            throw ConfigurationException("Unrecognized child element in policy.");
+        }
     }
 
     if (m_op == OP_NOT && m_operands.size() != 1) {
@@ -385,9 +600,13 @@ unique_ptr<AccessControl> XMLAccessControl::processChild(const string& name, con
     else if (name == RULE_REGEX_PROP_PATH) {
         return unique_ptr<AccessControl>(new RuleRegex(pt));
     }
-    else {
+    else if (name == AND_PROP_PATH || name == OR_PROP_PATH || name == NOT_PROP_PATH) {
         return unique_ptr<AccessControl>(new Operator(name, pt));
     }
+    else if (g_timeRules.find(name) != g_timeRules.end()) {
+        return unique_ptr<AccessControl>(new TimeRule(name, pt));
+    }
+    throw ConfigurationException("Unrecognized child element in policy.");
 }
 
 pair<bool,ptree*> XMLAccessControl::load() noexcept
diff --git a/tests/Makefile.am b/tests/Makefile.am
index ae6ae700..087c32d3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -16,7 +16,6 @@ shibsptest_SOURCES = \
 	AgentTestSuite.cpp \
 	AgentConfigTests.cpp \
 	attribute/impl/DefaultAttributeConfigurationtests.cpp \
-	impl/TimeAccessControlTests.cpp \
 	impl/XMLAccessControlTests.cpp \
 	impl/XMLRequestMapperTests.cpp \
 	platform/iis/ModuleConfigTests.cpp \
diff --git a/tests/data/impl/acl/inline-new-time-invalid.xml b/tests/data/impl/acl/inline-new-time-invalid.xml
new file mode 100644
index 00000000..3e569149
--- /dev/null
+++ b/tests/data/impl/acl/inline-new-time-invalid.xml
@@ -0,0 +1,5 @@
+<AccessControlProvider type="XML">
+        <AccessControl>
+                <Time>GT 3000-01-22T03:00:00Z</Time>
+        </AccessControl>
+</AccessControlProvider>
diff --git a/tests/data/impl/acl/inline-new-time-valid.xml b/tests/data/impl/acl/inline-new-time-valid.xml
new file mode 100644
index 00000000..21d23c4b
--- /dev/null
+++ b/tests/data/impl/acl/inline-new-time-valid.xml
@@ -0,0 +1,6 @@
+<AccessControlProvider type="XML">
+        <AccessControl>
+                <Time>LE 3000-01-22T03:00:00Z</Time>
+        </AccessControl>
+</AccessControlProvider>
+
diff --git a/tests/data/impl/acl/inline-old-time-invalid.xml b/tests/data/impl/acl/inline-old-time-invalid.xml
new file mode 100644
index 00000000..b8e68eb2
--- /dev/null
+++ b/tests/data/impl/acl/inline-old-time-invalid.xml
@@ -0,0 +1,6 @@
+<AccessControlProvider type="XML">
+        <AccessControl>
+                <Time>LE 2025-01-22T03:00:00Z</Time>
+        </AccessControl>
+</AccessControlProvider>
+
diff --git a/tests/data/impl/acl/inline-old-time-valid.xml b/tests/data/impl/acl/inline-old-time-valid.xml
new file mode 100644
index 00000000..12ee9967
--- /dev/null
+++ b/tests/data/impl/acl/inline-old-time-valid.xml
@@ -0,0 +1,6 @@
+<AccessControlProvider type="XML">
+        <AccessControl>
+                <Time>GT 2025-01-22T03:00:00Z</Time>
+        </AccessControl>
+</AccessControlProvider>
+
diff --git a/tests/data/impl/acl/inline-timesinceauth.xml b/tests/data/impl/acl/inline-timesinceauth.xml
new file mode 100644
index 00000000..f0952116
--- /dev/null
+++ b/tests/data/impl/acl/inline-timesinceauth.xml
@@ -0,0 +1,5 @@
+<AccessControlProvider type="XML">
+	<AccessControl>
+		<TimeSinceAuthn>PT1H</TimeSinceAuthn>
+	</AccessControl>
+</AccessControlProvider>
diff --git a/tests/data/impl/acl/inline-year-invalid.xml b/tests/data/impl/acl/inline-year-invalid.xml
new file mode 100644
index 00000000..fd4f1098
--- /dev/null
+++ b/tests/data/impl/acl/inline-year-invalid.xml
@@ -0,0 +1,6 @@
+<AccessControlProvider type="XML">
+		<AccessControl>
+				<Year>LT 2000</Year>
+		</AccessControl>
+</AccessControlProvider>
+
diff --git a/tests/data/impl/acl/inline-year-valid.xml b/tests/data/impl/acl/inline-year-valid.xml
new file mode 100644
index 00000000..fe5f7a2f
--- /dev/null
+++ b/tests/data/impl/acl/inline-year-valid.xml
@@ -0,0 +1,6 @@
+<AccessControlProvider type="XML">
+		<AccessControl>
+				<Year>LT 3000</Year>
+		</AccessControl>
+</AccessControlProvider>
+
diff --git a/tests/data/impl/acl/time/acl-badxml.xml b/tests/data/impl/acl/time/acl-badxml.xml
deleted file mode 100644
index 46663ff5..00000000
--- a/tests/data/impl/acl/time/acl-badxml.xml
+++ /dev/null
@@ -1 +0,0 @@
-<AccessControlProvider type="Time">
diff --git a/tests/data/impl/acl/time/external-acl-badxml.xml b/tests/data/impl/acl/time/external-acl-badxml.xml
deleted file mode 100644
index e647978b..00000000
--- a/tests/data/impl/acl/time/external-acl-badxml.xml
+++ /dev/null
@@ -1 +0,0 @@
-<AccessControlProvider type="XML" path="./data/impl/acl/time/acl-badxml.xml" />
diff --git a/tests/data/impl/acl/time/inline-new-time-invalid.xml b/tests/data/impl/acl/time/inline-new-time-invalid.xml
deleted file mode 100644
index 7dc1a3bd..00000000
--- a/tests/data/impl/acl/time/inline-new-time-invalid.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<AccessControlProvider type="Time">
-        <Time>GT 3000-01-22T03:00:00Z</Time>
-</AccessControlProvider>
-
diff --git a/tests/data/impl/acl/time/inline-new-time-valid.xml b/tests/data/impl/acl/time/inline-new-time-valid.xml
deleted file mode 100644
index ab0158ba..00000000
--- a/tests/data/impl/acl/time/inline-new-time-valid.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<AccessControlProvider type="Time">
-        <Time>LE 3000-01-22T03:00:00Z</Time>
-</AccessControlProvider>
-
diff --git a/tests/data/impl/acl/time/inline-old-time-invalid.xml b/tests/data/impl/acl/time/inline-old-time-invalid.xml
deleted file mode 100644
index 5fa20340..00000000
--- a/tests/data/impl/acl/time/inline-old-time-invalid.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<AccessControlProvider type="Time">
-        <Time>LE 2025-01-22T03:00:00Z</Time>
-</AccessControlProvider>
-
diff --git a/tests/data/impl/acl/time/inline-old-time-valid.xml b/tests/data/impl/acl/time/inline-old-time-valid.xml
deleted file mode 100644
index 9b826675..00000000
--- a/tests/data/impl/acl/time/inline-old-time-valid.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<AccessControlProvider type="Time">
-        <Time>GT 2025-01-22T03:00:00Z</Time>
-</AccessControlProvider>
-
diff --git a/tests/data/impl/acl/time/inline-timesinceauth.xml b/tests/data/impl/acl/time/inline-timesinceauth.xml
deleted file mode 100644
index 2b3c69fe..00000000
--- a/tests/data/impl/acl/time/inline-timesinceauth.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<AccessControlProvider type="Time">
-	<TimeSinceAuthn>PT1H</TimeSinceAuthn>
-</AccessControlProvider>
diff --git a/tests/data/impl/acl/time/inline-year-invalid.xml b/tests/data/impl/acl/time/inline-year-invalid.xml
deleted file mode 100644
index 92262ba0..00000000
--- a/tests/data/impl/acl/time/inline-year-invalid.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<AccessControlProvider type="Time" operator="AND">
-	<Year>LT 2000</Year>
-</AccessControlProvider>
-
diff --git a/tests/data/impl/acl/time/inline-year-valid.xml b/tests/data/impl/acl/time/inline-year-valid.xml
deleted file mode 100644
index e07754f6..00000000
--- a/tests/data/impl/acl/time/inline-year-valid.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<AccessControlProvider type="Time" operator="AND">
-	<Year>LT 3000</Year>
-</AccessControlProvider>
-
diff --git a/tests/data/impl/acl/time/internal-acl-invalid2.xml b/tests/data/impl/acl/time/internal-acl-invalid2.xml
deleted file mode 100644
index cd1c2466..00000000
--- a/tests/data/impl/acl/time/internal-acl-invalid2.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<AccessControlProvider type="Time">
-	<Foo/>
-</AccessControlProvider>
diff --git a/tests/impl/TimeAccessControlTests.cpp b/tests/impl/TimeAccessControlTests.cpp
deleted file mode 100644
index c82d1706..00000000
--- a/tests/impl/TimeAccessControlTests.cpp
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * TimeAccessControlTests.cpp
- *
- * Unit tests for Time AccessControl implementation.
- */
-
-#include "exceptions.h"
-#include "AbstractSPRequest.h"
-#include "AccessControl.h"
-#include "AgentConfig.h"
-#include "logging/Category.h"
-#include "remoting/ddf.h"
-#include "session/SessionCache.h"
-#include "util/BoostPropertySet.h"
-
-#include "DummyRequest.h"
-
-#ifdef HAVE_CXX14
-# include <shared_mutex>
-#endif
-
-#include <boost/test/unit_test.hpp>
-#include <boost/property_tree/xml_parser.hpp>
-
-using namespace shibsp;
-using namespace boost::property_tree;
-using namespace std;
-
-#define DATA_PATH "./data/impl/acl/time/"
-
-namespace {
-
-/** Open structure for testing manipulation. */
-struct DummySession : public Session, public NoOpBasicLockable {
-public:
-    DummySession() {}
-    ~DummySession() {
-        for (auto& a : m_attributes) {
-            a.second.destroy();
-        }
-    }
-
-    const char* getID() const {
-        return nullptr;
-    }
-    unsigned int getVersion() const {
-        return 1;
-    }
-    const char* getApplicationID() const {
-        return nullptr;
-    }
-    time_t getCreation() const {
-        return 0;
-    }
-    time_t getLastAccess() const {
-        return 0;
-    }
-    const map<string,DDF>& getAttributes() const {
-        return m_attributes;
-    }
-    DDF getOpaqueData() const {
-        return DDF();
-    }
-
-    map<string,DDF> m_attributes;
-};
-
-class MappableDummyRequest : public DummyRequest {
-public:
-    MappableDummyRequest() {}
-    ~MappableDummyRequest() {}
-    RequestMapper::Settings getRequestSettings() const { return make_pair(&m_map, nullptr); }
-
-    BoostPropertySet m_map;
-};
-
-class exceptionCheck {
-public:
-    exceptionCheck(const string& msg) : m_msg(msg) {}
-    bool check_message(const exception& e) {
-        if (m_msg.compare(e.what()) == 0) {
-            return true;
-        }
-        else {
-            cout << "Non-matching message: " << e.what() << endl;
-            return false;
-        }
-    }
-private:
-    string m_msg;
-};
-
-struct TimeAccessControlFixture
-{
-    TimeAccessControlFixture() : data_path(DATA_PATH) {
-        AgentConfig::getConfig().init(nullptr, (data_path + "../../console-agent.ini").c_str(), true);
-    }
-    ~TimeAccessControlFixture() {
-        AgentConfig::getConfig().term();
-    }
-
-    void parse(const string& filename) {
-        xml_parser::read_xml(data_path + filename, tree, xml_parser::no_comments|xml_parser::trim_whitespace);
-    }
-
-    ptree tree;
-    string data_path;
-};
-
-/////////////
-// File pointing to external ACL file that's invalid XML.
-/////////////
-
-BOOST_FIXTURE_TEST_CASE(TimeAccessControl_external_invalid, TimeAccessControlFixture)
-{
-    parse("external-acl-badxml.xml");
-    BOOST_CHECK_EQUAL(tree.size(), 1);
-
-    exceptionCheck checker("Initial AccessControl configuration was invalid.");
-    BOOST_CHECK_EXCEPTION(AgentConfig::getConfig().AccessControlManager.newPlugin(
-        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true),
-            ConfigurationException, checker.check_message);
-}
-
-/////////////
-// Inline ACL content that has a bad internal element.
-/////////////
-
-BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_invalid_internal, TimeAccessControlFixture)
-{
-    parse("internal-acl-invalid2.xml");
-    BOOST_CHECK_EQUAL(tree.size(), 1);
-
-    exceptionCheck checker("Time-based rule requires element content of the form \"LT|LE|EQ|GE|GT value\".");
-    BOOST_CHECK_EXCEPTION(AgentConfig::getConfig().AccessControlManager.newPlugin(
-        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true),
-            ConfigurationException, checker.check_message);
-}
-
-/////////////
-// Inline ACL test for TimeSinceAuthn rule.
-/////////////
-
-BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_TimeSinceAuthn, TimeAccessControlFixture)
-{
-    parse("inline-timesinceauth.xml");
-    BOOST_CHECK_EQUAL(tree.size(), 1);
-
-    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
-        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
-
-#ifdef HAVE_CXX14
-    shared_lock locker(*acl);
-#endif
-
-    MappableDummyRequest request;
-    DummySession session;
-
-    // No attribute available.
-    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_false);
-
-    DDF ac("Shib-Authentication-Instant");
-    ac.list();
-    ac.add(DDF(nullptr).longinteger(time(nullptr) - 300));
-    session.m_attributes[ac.name()] = ac;
-    
-    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_true);
-
-    ac.first().longinteger(time(nullptr) - 7200);
-    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_false);
-}
-
-/////////////
-// Inline ACL tests for absolute Time rule.
-/////////////
-
-BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_OldTimeValid, TimeAccessControlFixture)
-{
-    parse("inline-old-time-valid.xml");
-    BOOST_CHECK_EQUAL(tree.size(), 1);
-
-    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
-        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
-
-#ifdef HAVE_CXX14
-    shared_lock locker(*acl);
-#endif
-
-    MappableDummyRequest request;
-    DummySession session;
-
-    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_true);
-}
-
-BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_OldTimeInvalid, TimeAccessControlFixture)
-{
-    parse("inline-old-time-invalid.xml");
-    BOOST_CHECK_EQUAL(tree.size(), 1);
-
-    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
-        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
-
-#ifdef HAVE_CXX14
-    shared_lock locker(*acl);
-#endif
-
-    MappableDummyRequest request;
-    DummySession session;
-
-    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_false);
-}
-
-BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_NewTimeValid, TimeAccessControlFixture)
-{
-    parse("inline-new-time-valid.xml");
-    BOOST_CHECK_EQUAL(tree.size(), 1);
-
-    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
-        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
-
-#ifdef HAVE_CXX14
-    shared_lock locker(*acl);
-#endif
-
-    MappableDummyRequest request;
-    DummySession session;
-
-    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_true);
-}
-
-BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_NewTimeInvalid, TimeAccessControlFixture)
-{
-    parse("inline-new-time-invalid.xml");
-    BOOST_CHECK_EQUAL(tree.size(), 1);
-
-    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
-        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
-
-#ifdef HAVE_CXX14
-    shared_lock locker(*acl);
-#endif
-
-    MappableDummyRequest request;
-    DummySession session;
-
-    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_false);
-}
-
-BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_YearValid, TimeAccessControlFixture)
-{
-    parse("inline-year-valid.xml");
-    BOOST_CHECK_EQUAL(tree.size(), 1);
-
-    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
-        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
-
-#ifdef HAVE_CXX14
-    shared_lock locker(*acl);
-#endif
-
-    MappableDummyRequest request;
-    DummySession session;
-
-    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_true);
-}
-
-BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_YearInvalid, TimeAccessControlFixture)
-{
-    parse("inline-year-invalid.xml");
-    BOOST_CHECK_EQUAL(tree.size(), 1);
-
-    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
-        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
-
-#ifdef HAVE_CXX14
-    shared_lock locker(*acl);
-#endif
-
-    MappableDummyRequest request;
-    DummySession session;
-
-    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_false);
-}
-
-};
diff --git a/tests/impl/XMLAccessControlTests.cpp b/tests/impl/XMLAccessControlTests.cpp
index 2eb960a4..957a59e3 100644
--- a/tests/impl/XMLAccessControlTests.cpp
+++ b/tests/impl/XMLAccessControlTests.cpp
@@ -460,4 +460,149 @@ BOOST_FIXTURE_TEST_CASE(XMLAccessControl_external_NOT, XMLAccessControlFixture)
     BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_false);
 }
 
+/////////////
+// Inline ACL test for TimeSinceAuthn rule.
+/////////////
+
+BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_TimeSinceAuthn, XMLAccessControlFixture)
+{
+    parse("inline-timesinceauth.xml");
+    BOOST_CHECK_EQUAL(tree.size(), 1);
+
+    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
+        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
+
+#ifdef HAVE_CXX14
+    shared_lock locker(*acl);
+#endif
+
+    MappableDummyRequest request;
+    DummySession session;
+
+    // No attribute available.
+    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_false);
+
+    DDF ac("Shib-Authentication-Instant");
+    ac.list();
+    ac.add(DDF(nullptr).longinteger(time(nullptr) - 300));
+    session.m_attributes[ac.name()] = ac;
+    
+    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_true);
+
+    ac.first().longinteger(time(nullptr) - 7200);
+    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_false);
+}
+
+/////////////
+// Inline ACL tests for absolute Time rule.
+/////////////
+
+BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_OldTimeValid, XMLAccessControlFixture)
+{
+    parse("inline-old-time-valid.xml");
+    BOOST_CHECK_EQUAL(tree.size(), 1);
+
+    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
+        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
+
+#ifdef HAVE_CXX14
+    shared_lock locker(*acl);
+#endif
+
+    MappableDummyRequest request;
+    DummySession session;
+
+    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_true);
+}
+
+BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_OldTimeInvalid, XMLAccessControlFixture)
+{
+    parse("inline-old-time-invalid.xml");
+    BOOST_CHECK_EQUAL(tree.size(), 1);
+
+    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
+        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
+
+#ifdef HAVE_CXX14
+    shared_lock locker(*acl);
+#endif
+
+    MappableDummyRequest request;
+    DummySession session;
+
+    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_false);
+}
+
+BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_NewTimeValid, XMLAccessControlFixture)
+{
+    parse("inline-new-time-valid.xml");
+    BOOST_CHECK_EQUAL(tree.size(), 1);
+
+    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
+        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
+
+#ifdef HAVE_CXX14
+    shared_lock locker(*acl);
+#endif
+
+    MappableDummyRequest request;
+    DummySession session;
+
+    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_true);
+}
+
+BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_NewTimeInvalid, XMLAccessControlFixture)
+{
+    parse("inline-new-time-invalid.xml");
+    BOOST_CHECK_EQUAL(tree.size(), 1);
+
+    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
+        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
+
+#ifdef HAVE_CXX14
+    shared_lock locker(*acl);
+#endif
+
+    MappableDummyRequest request;
+    DummySession session;
+
+    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_false);
+}
+
+BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_YearValid, XMLAccessControlFixture)
+{
+    parse("inline-year-valid.xml");
+    BOOST_CHECK_EQUAL(tree.size(), 1);
+
+    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
+        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
+
+#ifdef HAVE_CXX14
+    shared_lock locker(*acl);
+#endif
+
+    MappableDummyRequest request;
+    DummySession session;
+
+    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_true);
+}
+
+BOOST_FIXTURE_TEST_CASE(TimeAccessControl_inline_YearInvalid, XMLAccessControlFixture)
+{
+    parse("inline-year-invalid.xml");
+    BOOST_CHECK_EQUAL(tree.size(), 1);
+
+    unique_ptr<AccessControl> acl(AgentConfig::getConfig().AccessControlManager.newPlugin(
+        tree.front().second.get<string>("<xmlattr>.type").c_str(), tree.front().second, true));
+
+#ifdef HAVE_CXX14
+    shared_lock locker(*acl);
+#endif
+
+    MappableDummyRequest request;
+    DummySession session;
+
+    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_false);
+}
+
 };
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list