[cpp-sp] branch main updated: Support Boost and STL regex via autoconf.

Scott Cantor cantor.2 at osu.edu
Fri Dec 20 18:21:24 UTC 2024


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch main
in repository cpp-sp.

View the commit online:
http://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=9243faed9ce4ed86ec18e9e6facf634a63e13f7c

The following commit(s) were added to refs/heads/main by this push:
     new 9243faed Support Boost and STL regex via autoconf.
9243faed is described below

commit 9243faed9ce4ed86ec18e9e6facf634a63e13f7c
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Fri Dec 20 13:21:19 2024 -0500

    Support Boost and STL regex via autoconf.
---
 apache/mod_shib_24.cpp               | 33 ++++++++++++-----------
 configure.ac                         | 16 +++++++++++
 shibsp/config_pub.h.in               |  4 +++
 shibsp/config_pub_win32.h            |  3 +++
 shibsp/impl/XMLAccessControl.cpp     | 52 +++++++++++++++++++-----------------
 shibsp/impl/XMLRequestMapper.cpp     | 52 +++++++++++++++++++++---------------
 tests/impl/XMLAccessControlTests.cpp | 26 ++++++++++++++++++
 7 files changed, 126 insertions(+), 60 deletions(-)

diff --git a/apache/mod_shib_24.cpp b/apache/mod_shib_24.cpp
index 69544ca4..68900c96 100644
--- a/apache/mod_shib_24.cpp
+++ b/apache/mod_shib_24.cpp
@@ -57,14 +57,19 @@
 #include <set>
 #include <memory>
 #include <fstream>
-#include <regex>
 #ifdef HAVE_CXX14
 # include <shared_mutex>
 #endif
 #include <stdexcept>
-#include <boost/lexical_cast.hpp>
 #include <boost/property_tree/xml_parser.hpp>
 
+#ifdef SHIBSP_USE_BOOST_REGEX
+# include <boost/regex.hpp>
+namespace exp = boost;
+#else
+# include <regex>
+namespace exp = std;
+#endif
 // Apache specific header files
 #include <httpd.h>
 #include <http_config.h>
@@ -89,7 +94,6 @@
 using namespace shibsp;
 using namespace xmltooling;
 using namespace boost::property_tree;
-using namespace boost;
 using namespace std;
 
 extern "C" module AP_MODULE_DECLARE_DATA shib_module;
@@ -891,10 +895,10 @@ AccessControl::aclresult_t htAccessControl::doUser(const ShibTargetApache& sta,
         if (regexp) {
             try {
                 // TODO: support regex options?
-                regex re(w);
-                match = regex_match(sta.getRemoteUser(), re);
+                exp::regex re(w, exp::regex_constants::extended);
+                match = exp::regex_match(sta.getRemoteUser(), re, exp::regex_constants::match_any | exp::regex_constants::match_not_null);
             }
-            catch (const regex_error& e) {
+            catch (const exp::regex_error& e) {
                 sta.log(SPRequest::SPError,
                     string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + e.what());
             }
@@ -936,11 +940,10 @@ AccessControl::aclresult_t htAccessControl::doAuthnContext(const ShibTargetApach
             bool match = false;
             if (regexp) {
                 try {
-                    // TODO: support regex options?
-                    regex re(w);
-                    match = regex_match(ref, re);
+                    exp::regex re(w, exp::regex_constants::extended);
+                    match = exp::regex_match(ref, re, exp::regex_constants::match_any | exp::regex_constants::match_not_null);
                 }
-                catch (const regex_error& e) {
+                catch (const exp::regex_error& e) {
                     sta.log(SPRequest::SPError,
                         string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + e.what());
                 }
@@ -970,18 +973,18 @@ bool htAccessControl::checkAttribute(const SPRequest& request, const Attribute*
     const vector<string>& vals = attr->getSerializedValues();
     for (vector<string>::const_iterator v = vals.begin(); v != vals.end(); ++v) {
         if (isRegex) {
-            regex::flag_type flags = regex_constants::optimize;
+            exp::regex_constants::syntax_option_type flags = exp::regex_constants::extended;
             if (!caseSensitive) {
-                flags |= regex_constants::icase;
+                flags |= exp::regex_constants::icase;
             }
             try {
-                regex exp(toMatch, flags);
-                if (regex_match(*v, exp)) {
+                exp::regex exp(toMatch, flags);
+                if (exp::regex_match(*v, exp, exp::regex_constants::match_any | exp::regex_constants::match_not_null)) {
                     if (request.isPriorityEnabled(SPRequest::SPDebug))
                         request.log(SPRequest::SPDebug, string("htaccess: expecting regexp ") + toMatch + ", got " + *v + ": accepted");
                     return true;
                 }
-            } catch (const regex_error& e) {
+            } catch (const exp::regex_error& e) {
                 request.log(SPRequest::SPError,
                     string("htaccess plugin caught exception while parsing regular expression (") + toMatch + "): " + e.what());
             }
diff --git a/configure.ac b/configure.ac
index 49f47f06..165298df 100644
--- a/configure.ac
+++ b/configure.ac
@@ -93,6 +93,22 @@ BOOST_STRING_ALGO
 BOOST_TEST
 BOOST_TUPLE
 
+## Use Boost for regex?
+AC_ARG_ENABLE([boost-regex],
+    AS_HELP_STRING([--disable-boost-regex],[do not use Boost for regular expression support]),
+    [boostregex_enabled=$enableval], [boostregex_enabled=yes])
+if test "x$boostregex_enabled" = "x" ; then
+    boostregex_enabled=yes
+fi
+AC_MSG_CHECKING(whether to use Boost for regular expressions)
+if test "$boostregex_enabled" = "no" ; then
+    AC_MSG_RESULT(no)
+else
+    AC_MSG_RESULT(yes)
+    BOOST_REGEX
+    AC_DEFINE([SHIBSP_USE_BOOST_REGEX],[1],[Define if using Boost for regular expressions.])
+fi
+
 # Thank you Solaris, really.
 AC_MSG_CHECKING(for ctime_r)
 if test -z "$ac_cv_ctime_args"; then
diff --git a/shibsp/config_pub.h.in b/shibsp/config_pub.h.in
index 4ca01720..cca80859 100644
--- a/shibsp/config_pub.h.in
+++ b/shibsp/config_pub.h.in
@@ -9,3 +9,7 @@
 
 /* Define to 1 if you have the <sys/socket.h> header file. */
 #undef SHIBSP_HAVE_SYS_SOCKET_H
+
+/* Define if using Boost for regular expressions. */
+#undef SHIBSP_USE_BOOST_REGEX
+
diff --git a/shibsp/config_pub_win32.h b/shibsp/config_pub_win32.h
index 229b8041..df065e09 100644
--- a/shibsp/config_pub_win32.h
+++ b/shibsp/config_pub_win32.h
@@ -21,3 +21,6 @@
 /* define if the compiler supports basic C++17 syntax */
 #define HAVE_CXX17 1
 
+/* Define if using Boost for regular expressions. */
+#define SHIBSP_USE_BOOST_REGEX 1
+
diff --git a/shibsp/impl/XMLAccessControl.cpp b/shibsp/impl/XMLAccessControl.cpp
index cb9051af..410670bf 100644
--- a/shibsp/impl/XMLAccessControl.cpp
+++ b/shibsp/impl/XMLAccessControl.cpp
@@ -32,10 +32,17 @@
 #include <algorithm>
 #include <memory>
 #include <set>
-#include <regex>
 #include <boost/algorithm/string.hpp>
 #include <boost/property_tree/ptree.hpp>
 
+#ifdef SHIBSP_USE_BOOST_REGEX
+# include <boost/regex.hpp>
+namespace exp = boost;
+#else
+# include <regex>
+namespace exp = std;
+#endif
+
 #ifndef HAVE_STRCASECMP
 # define strcasecmp _stricmp
 #endif
@@ -70,7 +77,7 @@ namespace {
     private:
         string m_alias;
         string m_exp;
-        regex m_re;
+        exp::regex m_re;
     };
 
     class Operator : public AccessControl, public NoOpSharedLockable
@@ -168,14 +175,14 @@ AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Sess
 
     if (m_alias == "valid-user") {
         if (session) {
-            request.log(SPRequest::SPDebug,"AccessControl plugin accepting valid-user based on active session");
+            request.log(SPRequest::SPDebug,"AccessControl rule accepting valid-user based on active session");
             return shib_acl_true;
         }
         return shib_acl_false;
     }
     if (m_alias == "user") {
         if (m_vals.find(request.getRemoteUser()) != m_vals.end()) {
-            request.log(SPRequest::SPDebug, string("AccessControl plugin expecting REMOTE_USER (") + request.getRemoteUser() + "), authz granted");
+            request.log(SPRequest::SPDebug, string("AccessControl rule expecting REMOTE_USER (") + request.getRemoteUser() + "), authz granted");
             return shib_acl_true;
         }
         return shib_acl_false;
@@ -183,7 +190,7 @@ AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Sess
     else if (m_alias == "authnContextClassRef") {
         const char* ref = session->getAuthnContextClassRef();
         if (ref && m_vals.find(ref) != m_vals.end()) {
-            request.log(SPRequest::SPDebug, string("AccessControl plugin expecting authnContextClassRef (") + ref + "), authz granted");
+            request.log(SPRequest::SPDebug, string("AccessControl rule expecting authnContextClassRef (") + ref + "), authz granted");
             return shib_acl_true;
         }
         return shib_acl_false;
@@ -193,11 +200,11 @@ AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Sess
     pair<multimap<string,const Attribute*>::const_iterator, multimap<string,const Attribute*>::const_iterator> attrs =
         session->getIndexedAttributes().equal_range(m_alias);
     if (attrs.first == attrs.second) {
-        request.log(SPRequest::SPWarn, string("rule requires attribute (") + m_alias + "), not found in session");
+        request.log(SPRequest::SPWarn, string("AccessControl rule requires attribute (") + m_alias + "), not found in session");
         return shib_acl_false;
     }
     else if (m_vals.empty()) {
-        request.log(SPRequest::SPDebug, string("AccessControl plugin requires presence of attribute (") + m_alias + "), authz granted");
+        request.log(SPRequest::SPDebug, string("AccessControl rule requires presence of attribute (") + m_alias + "), authz granted");
         return shib_acl_true;
     }
 
@@ -209,7 +216,7 @@ AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Sess
         for (set<string>::const_iterator i = m_vals.begin(); i != m_vals.end(); ++i) {
             for (vector<string>::const_iterator j = vals.begin(); j != vals.end(); ++j) {
                 if ((caseSensitive && *i == *j) || (!caseSensitive && !strcasecmp(i->c_str(),j->c_str()))) {
-                    request.log(SPRequest::SPDebug, string("AccessControl plugin expecting (") + *j + "), authz granted");
+                    request.log(SPRequest::SPDebug, string("AccessControl rule expecting (") + *j + "), authz granted");
                     return shib_acl_true;
                 }
             }
@@ -229,24 +236,21 @@ RuleRegex::RuleRegex(const ptree& pt)
     static string_to_bool_translator tr;
     bool caseSensitive = pt.get(CASE_SENSITIVE_PROP_PATH, true);
     try {
-        // TODO: more flag options, particular for dialect.
-        regex::flag_type flags = regex_constants::optimize;
+        exp::regex_constants::syntax_option_type flags = exp::regex_constants::extended | exp::regex_constants::optimize;
         if (!caseSensitive) {
-            flags |= regex_constants::icase;
+            flags |= exp::regex_constants::icase;
         }
-        m_re = regex(m_exp, flags);
+        m_re = exp::regex(m_exp, flags);
     }
-    catch (const regex_error&) {
+    catch (const exp::regex_error&) {
         throw ConfigurationException("Caught exception while parsing RuleRegex regular expression.");
     }
 }
 
 AccessControl::aclresult_t RuleRegex::authorized(const SPRequest& request, const Session* session) const
 {
-    // TODO: Have to confirm we want regex_match here vs. regex_search.
-    // TODO: Have to consider match_flags as well, particularly against some open issues raised against the Xerces behavior.
 
-    // Map alias in rule to the attribute.
+    static exp::regex_constants::match_flag_type match_flags = exp::regex_constants::match_any | exp::regex_constants::match_not_null;
 
     if (!session) {
         request.log(SPRequest::SPWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");
@@ -255,22 +259,22 @@ AccessControl::aclresult_t RuleRegex::authorized(const SPRequest& request, const
 
     if (m_alias == "valid-user") {
         if (session) {
-            request.log(SPRequest::SPDebug,"AccessControl plugin accepting valid-user based on active session");
+            request.log(SPRequest::SPDebug,"AccessControl rule accepting valid-user based on active session");
             return shib_acl_true;
         }
         return shib_acl_false;
     }
 
     if (m_alias == "user") {
-        if (regex_match(request.getRemoteUser(), m_re)) {
-            request.log(SPRequest::SPDebug, string("AccessControl plugin expecting REMOTE_USER (") + m_exp + "), authz granted");
+        if (exp::regex_match(request.getRemoteUser(), m_re, match_flags)) {
+            request.log(SPRequest::SPDebug, string("AccessControl rule expecting REMOTE_USER regex (") + m_exp + "), authz granted");
             return shib_acl_true;
         }
         return shib_acl_false;
     }
     else if (m_alias == "authnContextClassRef") {
-        if (session->getAuthnContextClassRef() && regex_match(session->getAuthnContextClassRef(), m_re)) {
-            request.log(SPRequest::SPDebug, string("AccessControl plugin expecting authnContextClassRef (") + m_exp + "), authz granted");
+        if (session->getAuthnContextClassRef() && exp::regex_match(session->getAuthnContextClassRef(), m_re, match_flags)) {
+            request.log(SPRequest::SPDebug, string("AccessControl rule expecting authnContextClassRef regex (") + m_exp + "), authz granted");
             return shib_acl_true;
         }
         return shib_acl_false;
@@ -279,15 +283,15 @@ AccessControl::aclresult_t RuleRegex::authorized(const SPRequest& request, const
     // Find the attribute(s) matching the require rule.
     auto attrs = session->getIndexedAttributes().equal_range(m_alias);
     if (attrs.first == attrs.second) {
-        request.log(SPRequest::SPWarn, string("rule requires attribute (") + m_alias + "), not found in session");
+        request.log(SPRequest::SPWarn, string("AccessControl rule requires attribute (") + m_alias + "), not found in session");
         return shib_acl_false;
     }
 
     for (; attrs.first != attrs.second; ++attrs.first) {
         // Now we have to intersect the attribute's values against the regular expression.
         for (const string& v : attrs.first->second->getSerializedValues()) {
-            if (regex_match(v, m_re)) {
-                request.log(SPRequest::SPDebug, string("AccessControl plugin expecting (") + m_exp + "), authz granted");
+            if (exp::regex_match(v, m_re, match_flags)) {
+                request.log(SPRequest::SPDebug, string("AccessControl rule expecting regex (") + m_exp + "), authz granted");
                 return shib_acl_true;
             }
         }
diff --git a/shibsp/impl/XMLRequestMapper.cpp b/shibsp/impl/XMLRequestMapper.cpp
index 4349c2e0..68ccc344 100644
--- a/shibsp/impl/XMLRequestMapper.cpp
+++ b/shibsp/impl/XMLRequestMapper.cpp
@@ -12,7 +12,7 @@
  * limitations under the License.
  */
 
-/** XMLRequestMapper.cpp
+/** impl/XMLRequestMapper.cpp
  *
  * XML-based RequestMapper implementation.
  */
@@ -33,7 +33,6 @@
 
 #include <algorithm>
 #include <memory>
-#include <regex>
 #include <tuple>
 #include <utility>
 #include <boost/property_tree/ptree.hpp>
@@ -41,6 +40,14 @@
 #include <boost/tokenizer.hpp>
 #include <boost/algorithm/string.hpp>
 
+#ifdef SHIBSP_USE_BOOST_REGEX
+# include <boost/regex.hpp>
+namespace exp = boost;
+#else
+# include <regex>
+namespace exp = std;
+#endif
+
 using namespace shibsp;
 using namespace boost::property_tree;
 using namespace std;
@@ -75,8 +82,8 @@ namespace {
         // This uses shared_ptr to support multiple mappings for a given Override for Host.
         // For Path, it's just overhead.
         map< string,shared_ptr<Override> > m_map;
-        vector< pair< regex,unique_ptr<Override> > > m_regexps;
-        vector< tuple< string,boost::optional<regex>,unique_ptr<Override> > > m_queries;
+        vector< pair< exp::regex,unique_ptr<Override> > > m_regexps;
+        vector< tuple< string,boost::optional<exp::regex>,unique_ptr<Override> > > m_queries;
 
     private:
         unique_ptr<AccessControl> m_acl;
@@ -134,6 +141,9 @@ namespace {
     {
         return new XMLRequestMapper(pt);
     }
+
+    static exp::regex_constants::match_flag_type match_flags =
+        exp::regex_constants::match_any | exp::regex_constants::match_not_null;
 }
 
 void SHIBSP_API shibsp::registerRequestMappers()
@@ -316,15 +326,15 @@ Override::Override(bool unicodeAware, ptree& pt, Category& log, const Override*
 
             try {
                 // TODO: more flag options, particular for dialect.
-                regex::flag_type flags = regex_constants::optimize;
+                exp::regex::flag_type flags = exp::regex_constants::extended | exp::regex_constants::optimize;
                 if (!getBool("caseSensitive", false)) {
-                    flags |= regex_constants::icase;
+                    flags |= exp::regex_constants::icase;
                 }
-                regex exp(regexpprop, flags);
+                exp::regex exp(regexpprop, flags);
                 m_regexps.push_back(make_pair(exp, std::move(o)));
                 log.debug("added <PathRegex> mapping (%s)", regexpprop.c_str());
             }
-            catch (const regex_error& e) {
+            catch (const exp::regex_error& e) {
                 log.error("error parsing PathRegex regular expression: %s", e.what());
                 throw ConfigurationException("Invalid regular expression in PathRegex element.");
             }
@@ -341,21 +351,21 @@ Override::Override(bool unicodeAware, ptree& pt, Category& log, const Override*
             string regexpprop(getString("regex", ""));
 
             if (regexpprop.empty()) {
-                m_queries.push_back(make_tuple(nameprop, boost::optional<regex>(), std::move(o)));
+                m_queries.push_back(make_tuple(nameprop, boost::optional<exp::regex>(), std::move(o)));
             }
             else {
                 try {
                     // TODO: more flag options, particular for dialect.
-                    regex::flag_type flags = regex_constants::optimize;
+                    exp::regex::flag_type flags = exp::regex_constants::extended | exp::regex_constants::optimize;
                     if (!getBool("caseSensitive", false)) {
-                        flags |= regex_constants::icase;
+                        flags |= exp::regex_constants::icase;
                     }
-                    regex exp(regexpprop, flags);
+                    exp::regex exp(regexpprop, flags);
 
-                    m_queries.push_back(make_tuple(nameprop, boost::optional<regex>(exp), std::move(o)));
+                    m_queries.push_back(make_tuple(nameprop, boost::optional<exp::regex>(exp), std::move(o)));
                     log.debug("added <Query> mapping (%s)", nameprop.c_str());
                 }
-                catch (const regex_error& e) {
+                catch (const exp::regex_error& e) {
                     log.error("caught exception while parsing Query regular expression: %s", e.what());
                     throw ConfigurationException("Invalid regular expression in Query element.");
                 }
@@ -435,7 +445,7 @@ const Override* Override::locate(const HTTPRequest& request) const
     // If there's anything left, we try for a regex match on the rest of the path minus the query string.
     if (*path) {
         for (const auto& re : m_regexps) {
-            if (regex_match(path, re.first)) {
+            if (regex_match(path, re.first, match_flags)) {
                 o = re.second.get();
                 break;
             }
@@ -455,7 +465,7 @@ const Override* Override::locate(const HTTPRequest& request) const
                     if (get<1>(*q)) {
                         // We have to match one of the values.
                         while (vals.first != vals.second) {
-                            if (regex_match(vals.first->second, get<1>(*q).get())) {
+                            if (exp::regex_match(vals.first->second, get<1>(*q).get(), match_flags)) {
                                 o = get<2>(*q).get();
                                 descended = true;
                                 break;
@@ -507,14 +517,14 @@ XMLRequestMapperImpl::XMLRequestMapperImpl(ptree& pt, Category& log)
             unique_ptr<Override> o(new Override(m_unicodeAware, child.second, log, this));
 
             try {
-                regex::flag_type flags = regex_constants::optimize;
+                exp::regex::flag_type flags = exp::regex_constants::extended | exp::regex_constants::optimize;
                 if (!getBool("caseSensitive", false)) {
-                    flags |= regex_constants::icase;
+                    flags |= exp::regex_constants::icase;
                 }
-                regex exp(regexprop, flags);
+                exp::regex exp(regexprop, flags);
                 m_regexps.push_back(make_pair(exp, std::move(o)));
             }
-            catch (const regex_error& e) {
+            catch (const exp::regex_error& e) {
                 log.error("caught exception while parsing HostRegex regular expression: %s", e.what());
             }
 
@@ -632,7 +642,7 @@ const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTP
         o = i->second.get();
     else {
         for (const auto& re : m_regexps) {
-            if (regex_match(vhost, re.first)) {
+            if (exp::regex_match(vhost, re.first, match_flags)) {
                 o = re.second.get();
             }
         }
diff --git a/tests/impl/XMLAccessControlTests.cpp b/tests/impl/XMLAccessControlTests.cpp
index 6c13caef..72f55e46 100644
--- a/tests/impl/XMLAccessControlTests.cpp
+++ b/tests/impl/XMLAccessControlTests.cpp
@@ -260,6 +260,32 @@ BOOST_FIXTURE_TEST_CASE(XMLAccessControl_inline_UserRule, XMLAccessControlFixtur
     BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_true);
 }
 
+/////////////
+// Inline ACL test for user regex rule.
+/////////////
+
+BOOST_FIXTURE_TEST_CASE(XMLAccessControl_inline_UserRegexRule, XMLAccessControlFixture)
+{
+    parse("inline-user-regex-acl.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
+
+    DummyRequest request;
+    DummySession session;
+
+    request.m_user = "smith";
+    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_false);
+
+    request.m_user = "jdoe";
+    BOOST_CHECK_EQUAL(acl->authorized(request, &session), AccessControl::shib_acl_true);
+}
+
 /////////////
 // Inline ACL test for authnContextClassRef rule.
 /////////////

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


More information about the commits mailing list