Add regex support to acl plugin.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Sat, 29 Dec 2007 22:22:17 +0000 (22:22 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Sat, 29 Dec 2007 22:22:17 +0000 (22:22 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2683 cb58f699-b61c-0410-a6fe-9272a202ed29

schemas/shibboleth-2.0-native-sp-config.xsd
shibsp/impl/XMLAccessControl.cpp

index fb2d538..54bdd2b 100644 (file)
                        <element ref="conf:OR"/>\r
                        <element ref="conf:NOT"/>\r
                        <element ref="conf:Rule"/>\r
+                   <element ref="conf:RuleRegex"/>\r
                </choice>\r
        </complexType>\r
        <complexType name="MultiOperatorType">\r
                        <element ref="conf:OR"/>\r
                        <element ref="conf:NOT"/>\r
                        <element ref="conf:Rule"/>\r
+                   <element ref="conf:RuleRegex"/>\r
                </choice>\r
        </complexType>\r
        <element name="Rule">\r
                        <simpleContent>\r
                                <extension base="conf:listOfStrings">\r
                                        <attribute name="require" type="conf:string" use="required"/>\r
+                                   <attribute name="list" type="boolean"/>\r
                                </extension>\r
                        </simpleContent>\r
                </complexType>\r
        </element>\r
+    <element name="RuleRegex">\r
+        <complexType>\r
+            <simpleContent>\r
+                <extension base="conf:string">\r
+                    <attribute name="require" type="conf:string" use="required"/>\r
+                    <attribute name="ignoreCase" type="boolean"/>\r
+                </extension>\r
+            </simpleContent>\r
+        </complexType>\r
+    </element>\r
        \r
        <attributeGroup name="ContentSettings">\r
                <attribute name="authType" type="conf:string"/>\r
index 331a434..939ea68 100644 (file)
@@ -30,6 +30,7 @@
 #include <xmltooling/util/ReloadableXMLFile.h>\r
 #include <xmltooling/util/XMLHelper.h>\r
 #include <xercesc/util/XMLUniDefs.hpp>\r
+#include <xercesc/util/regx/RegularExpression.hpp>\r
 \r
 #ifndef HAVE_STRCASECMP\r
 # define strcasecmp _stricmp\r
@@ -57,6 +58,25 @@ namespace {
         vector <string> m_vals;\r
     };\r
     \r
+    class RuleRegex : public AccessControl\r
+    {\r
+    public:\r
+        RuleRegex(const DOMElement* e);\r
+        ~RuleRegex() {\r
+            delete m_re;\r
+        }\r
+        \r
+        Lockable* lock() {return this;}\r
+        void unlock() {}\r
+\r
+        aclresult_t authorized(const SPRequest& request, const Session* session) const;\r
+        \r
+    private:\r
+        string m_alias;\r
+        auto_arrayptr<char> m_exp;\r
+        RegularExpression* m_re;\r
+    };\r
+    \r
     class Operator : public AccessControl\r
     {\r
     public:\r
@@ -108,12 +128,16 @@ namespace {
         return new XMLAccessControl(e);\r
     }\r
 \r
-    static const XMLCh _AccessControl[] =    UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);\r
+    static const XMLCh _AccessControl[] =   UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);\r
+    static const XMLCh ignoreCase[] =       UNICODE_LITERAL_10(i,g,n,o,r,e,C,a,s,e);\r
+    static const XMLCh ignoreOption[] =     UNICODE_LITERAL_1(i);\r
+    static const XMLCh _list[] =            UNICODE_LITERAL_4(l,i,s,t);\r
     static const XMLCh require[] =          UNICODE_LITERAL_7(r,e,q,u,i,r,e);\r
     static const XMLCh NOT[] =              UNICODE_LITERAL_3(N,O,T);\r
     static const XMLCh AND[] =              UNICODE_LITERAL_3(A,N,D);\r
     static const XMLCh OR[] =               UNICODE_LITERAL_2(O,R);\r
     static const XMLCh _Rule[] =            UNICODE_LITERAL_4(R,u,l,e);\r
+    static const XMLCh _RuleRegex[] =       UNICODE_LITERAL_9(R,u,l,e,R,e,g,e,x);\r
 }\r
 \r
 void SHIBSP_API shibsp::registerAccessControls()\r
@@ -125,12 +149,22 @@ void SHIBSP_API shibsp::registerAccessControls()
 \r
 Rule::Rule(const DOMElement* e)\r
 {\r
-    xmltooling::auto_ptr_char req(e->getAttributeNS(NULL,require));\r
+    auto_ptr_char req(e->getAttributeNS(NULL,require));\r
     if (!req.get() || !*req.get())\r
         throw ConfigurationException("Access control rule missing require attribute");\r
     m_alias=req.get();\r
+\r
+    auto_arrayptr<char> vals(toUTF8(e->hasChildNodes() ? e->getFirstChild()->getNodeValue() : NULL));\r
+    if (!vals.get())\r
+        return;\r
+    \r
+    const XMLCh* flag = e->getAttributeNS(NULL,_list);\r
+    if (flag && (*flag == chLatin_f || *flag == chDigit_0)) {\r
+        if (*vals.get())\r
+            m_vals.push_back(vals.get());\r
+        return;\r
+    }\r
     \r
-    xmltooling::auto_ptr_char vals(e->hasChildNodes() ? e->getFirstChild()->getNodeValue() : NULL);\r
 #ifdef HAVE_STRTOK_R\r
     char* pos=NULL;\r
     const char* token=strtok_r(const_cast<char*>(vals.get())," ",&pos);\r
@@ -221,6 +255,90 @@ AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Sess
     return shib_acl_false;\r
 }\r
 \r
+RuleRegex::RuleRegex(const DOMElement* e) : m_exp(toUTF8(e->hasChildNodes() ? e->getFirstChild()->getNodeValue() : NULL))\r
+{\r
+    auto_ptr_char req(e->getAttributeNS(NULL,require));\r
+    if (!req.get() || !*req.get() || !m_exp.get() || !*m_exp.get())\r
+        throw ConfigurationException("Access control rule missing require attribute or element content.");\r
+    m_alias=req.get();\r
+    \r
+    const XMLCh* flag = e->getAttributeNS(NULL,ignoreCase);\r
+    bool ignore = (flag && (*flag == chLatin_t || *flag == chDigit_1));\r
+    try {\r
+        m_re = new RegularExpression(e->getFirstChild()->getNodeValue(), (ignore ? ignoreOption : &chNull)); \r
+    }\r
+    catch (XMLException& ex) {\r
+        auto_ptr_char tmp(ex.getMessage());\r
+        throw ConfigurationException("Caught exception while parsing RuleRegex regular expression: $1", params(1,tmp.get()));\r
+    }\r
+}\r
+\r
+AccessControl::aclresult_t RuleRegex::authorized(const SPRequest& request, const Session* session) const\r
+{\r
+    // Map alias in rule to the attribute.\r
+    if (!session) {\r
+        request.log(SPRequest::SPWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");\r
+        return shib_acl_false;\r
+    }\r
+    \r
+    if (m_alias == "valid-user") {\r
+        if (session) {\r
+            request.log(SPRequest::SPDebug,"AccessControl plugin accepting valid-user based on active session");\r
+            return shib_acl_true;\r
+        }\r
+        return shib_acl_false;\r
+    }\r
+\r
+    try {\r
+        if (m_alias == "user") {\r
+            if (m_re->matches(request.getRemoteUser().c_str())) {\r
+                request.log(SPRequest::SPDebug, string("AccessControl plugin expecting REMOTE_USER (") + m_exp.get() + "), authz granted");\r
+                return shib_acl_true;\r
+            }\r
+            return shib_acl_false;\r
+        }\r
+        else if (m_alias == "authnContextClassRef") {\r
+            if (session->getAuthnContextClassRef() && m_re->matches(session->getAuthnContextClassRef())) {\r
+                request.log(SPRequest::SPDebug, string("AccessControl plugin expecting authnContextClassRef (") + m_exp.get() + "), authz granted");\r
+                return shib_acl_true;\r
+            }\r
+            return shib_acl_false;\r
+        }\r
+        else if (m_alias == "authnContextDeclRef") {\r
+            if (session->getAuthnContextDeclRef() && m_re->matches(session->getAuthnContextDeclRef())) {\r
+                request.log(SPRequest::SPDebug, string("AccessControl plugin expecting authnContextDeclRef (") + m_exp.get() + "), authz granted");\r
+                return shib_acl_true;\r
+            }\r
+            return shib_acl_false;\r
+        }\r
+\r
+        // Find the attribute(s) matching the require rule.\r
+        pair<multimap<string,const Attribute*>::const_iterator, multimap<string,const Attribute*>::const_iterator> attrs =\r
+            session->getIndexedAttributes().equal_range(m_alias);\r
+        if (attrs.first == attrs.second) {\r
+            request.log(SPRequest::SPWarn, string("rule requires attribute (") + m_alias + "), not found in session");\r
+            return shib_acl_false;\r
+        }\r
+\r
+        for (; attrs.first != attrs.second; ++attrs.first) {\r
+            // Now we have to intersect the attribute's values against the regular expression.\r
+            const vector<string>& vals = attrs.first->second->getSerializedValues();\r
+            for (vector<string>::const_iterator j=vals.begin(); j!=vals.end(); ++j) {\r
+                if (m_re->matches(j->c_str())) {\r
+                    request.log(SPRequest::SPDebug, string("AccessControl plugin expecting (") + m_exp.get() + "), authz granted");\r
+                    return shib_acl_true;\r
+                }\r
+            }\r
+        }\r
+    }\r
+    catch (XMLException& ex) {\r
+        auto_ptr_char tmp(ex.getMessage());\r
+        request.log(SPRequest::SPError, string("caught exception while parsing RuleRegex regular expression: ") + tmp.get());\r
+    }\r
+    \r
+    return shib_acl_false;\r
+}\r
+\r
 Operator::Operator(const DOMElement* e)\r
 {\r
     if (XMLString::equals(e->getLocalName(),NOT))\r
@@ -236,6 +354,8 @@ Operator::Operator(const DOMElement* e)
         e=XMLHelper::getFirstChildElement(e);\r
         if (XMLString::equals(e->getLocalName(),_Rule))\r
             m_operands.push_back(new Rule(e));\r
+        else if (XMLString::equals(e->getLocalName(),_RuleRegex))\r
+            m_operands.push_back(new RuleRegex(e));\r
         else\r
             m_operands.push_back(new Operator(e));\r
         \r
@@ -246,6 +366,8 @@ Operator::Operator(const DOMElement* e)
         while (e) {\r
             if (XMLString::equals(e->getLocalName(),_Rule))\r
                 m_operands.push_back(new Rule(e));\r
+            else if (XMLString::equals(e->getLocalName(),_RuleRegex))\r
+                m_operands.push_back(new RuleRegex(e));\r
             else\r
                 m_operands.push_back(new Operator(e));\r
             e=XMLHelper::getNextSiblingElement(e);\r
@@ -312,6 +434,8 @@ pair<bool,DOMElement*> XMLAccessControl::load()
     AccessControl* authz;\r
     if (XMLString::equals(raw.second->getLocalName(),_Rule))\r
         authz=new Rule(raw.second);\r
+    else if (XMLString::equals(raw.second->getLocalName(),_RuleRegex))\r
+        authz=new RuleRegex(raw.second);\r
     else\r
         authz=new Operator(raw.second);\r
 \r