Change log message.
[shibboleth/sp.git] / shibsp / attribute / filtering / impl / XMLAttributeFilter.cpp
index 3b8fe52..47857f2 100644 (file)
@@ -25,7 +25,7 @@
 #include "ServiceProvider.h"
 #include "attribute/Attribute.h"
 #include "attribute/filtering/AttributeFilter.h"
-#include "attribute/filtering/MatchFunctor.h"
+#include "attribute/filtering/FilterPolicyContext.h"
 #include "util/SPConstants.h"
 
 #include <xmltooling/util/NDC.h>
@@ -37,7 +37,6 @@ using namespace shibsp;
 using namespace opensaml::saml2md;
 using namespace opensaml;
 using namespace xmltooling;
-using namespace log4cpp;
 using namespace std;
 
 namespace shibsp {
@@ -70,13 +69,13 @@ namespace shibsp {
             m_document = doc;
         }
 
-        void filterAttributes(const FilteringContext& context, multimap<string,Attribute*>& attributes) const;
+        void filterAttributes(const FilteringContext& context, vector<Attribute*>& attributes) const;
 
     private:
         MatchFunctor* buildFunctor(
-            const DOMElement* e, multimap<string,MatchFunctor*>& functorMap, const char* logname, bool standalone
+            const DOMElement* e, const FilterPolicyContext& functorMap, const char* logname, bool standalone
             );
-        pair<string,const MatchFunctor*> buildAttributeRule(const DOMElement* e, bool standalone);
+        pair<string,const MatchFunctor*> buildAttributeRule(const DOMElement* e, const FilterPolicyContext& functorMap, bool standalone);
 
         Category& m_log;
         DOMDocument* m_document;
@@ -96,7 +95,7 @@ namespace shibsp {
             delete m_impl;
         }
         
-        void filterAttributes(const FilteringContext& context, multimap<string,Attribute*>& attributes) const {
+        void filterAttributes(const FilteringContext& context, vector<Attribute*>& attributes) const {
             m_impl->filterAttributes(context, attributes);
         }
 
@@ -124,7 +123,7 @@ namespace shibsp {
     static const XMLCh PermitValueRuleReference[] =     UNICODE_LITERAL_24(P,e,r,m,i,t,V,a,l,u,e,R,u,l,e,R,e,f,e,r,e,n,c,e);
     static const XMLCh PolicyRequirementRule[] =        UNICODE_LITERAL_21(P,o,l,i,c,y,R,e,q,u,i,r,e,m,e,n,t,R,u,l,e);
     static const XMLCh PolicyRequirementRuleReference[]=UNICODE_LITERAL_30(P,o,l,i,c,y,R,e,q,u,i,r,e,m,e,n,t,R,u,l,e,R,e,f,e,r,e,n,c,e);
-    static const XMLCh attributeId[] =                  UNICODE_LITERAL_11(a,t,t,r,i,b,u,t,e,I,d);
+    static const XMLCh attributeID[] =                  UNICODE_LITERAL_11(a,t,t,r,i,b,u,t,e,I,D);
     static const XMLCh _id[] =                          UNICODE_LITERAL_2(i,d);
     static const XMLCh _ref[] =                         UNICODE_LITERAL_3(r,e,f);
 };
@@ -138,22 +137,25 @@ XMLFilterImpl::XMLFilterImpl(const DOMElement* e, Category& log) : m_log(log), m
     if (!XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeFilterPolicyGroup))
         throw ConfigurationException("XML AttributeFilter requires afp:AttributeFilterPolicyGroup at root of configuration.");
 
+    FilterPolicyContext reqFunctors(m_policyReqRules);
+    FilterPolicyContext valFunctors(m_permitValRules);
+
     DOMElement* child = XMLHelper::getFirstChildElement(e);
     while (child) {
         if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PolicyRequirementRule)) {
-            buildFunctor(child, m_policyReqRules, "PolicyRequirementRule", true);
+            buildFunctor(child, reqFunctors, "PolicyRequirementRule", true);
         }
         else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PermitValueRule)) {
-            buildFunctor(child, m_permitValRules, "PermitValueRule", true);
+            buildFunctor(child, valFunctors, "PermitValueRule", true);
         }
         else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeRule)) {
-            buildAttributeRule(child, true);
+            buildAttributeRule(child, valFunctors, true);
         }
         else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeFilterPolicy)) {
             e = XMLHelper::getFirstChildElement(child);
-            MatchFunctor* func;
+            MatchFunctor* func = NULL;
             if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PolicyRequirementRule)) {
-                func = buildFunctor(e, m_policyReqRules, "PolicyRequirementRule", false);
+                func = buildFunctor(e, reqFunctors, "PolicyRequirementRule", false);
             }
             else if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PolicyRequirementRuleReference)) {
                 auto_ptr_char ref(e->getAttributeNS(NULL, _ref));
@@ -168,16 +170,16 @@ XMLFilterImpl::XMLFilterImpl(const DOMElement* e, Category& log) : m_log(log), m
                 e = XMLHelper::getNextSiblingElement(e);
                 while (e) {
                     if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeRule)) {
-                        pair<string,const MatchFunctor*> rule = buildAttributeRule(e, false);
+                        pair<string,const MatchFunctor*> rule = buildAttributeRule(e, valFunctors, false);
                         if (rule.second)
-                            m_policies.back().m_rules.insert(rule);
+                            m_policies.back().m_rules.insert(Policy::rules_t::value_type(rule.first, rule.second));
                     }
                     else if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeRuleReference)) {
                         auto_ptr_char ref(e->getAttributeNS(NULL, _ref));
                         if (ref.get() && *ref.get()) {
                             map< string,pair<string,const MatchFunctor*> >::const_iterator ar = m_attrRules.find(ref.get());
                             if (ar != m_attrRules.end())
-                                m_policies.back().m_rules.insert(ar->second);
+                                m_policies.back().m_rules.insert(Policy::rules_t::value_type(ar->second.first, ar->second.second));
                             else
                                 m_log.warn("skipping invalid AttributeRuleReference (%s)", ref.get());
                         }
@@ -194,7 +196,7 @@ XMLFilterImpl::XMLFilterImpl(const DOMElement* e, Category& log) : m_log(log), m
 }
 
 MatchFunctor* XMLFilterImpl::buildFunctor(
-    const DOMElement* e, multimap<string,MatchFunctor*>& functorMap, const char* logname, bool standalone
+    const DOMElement* e, const FilterPolicyContext& functorMap, const char* logname, bool standalone
     )
 {
     auto_ptr_char temp(e->getAttributeNS(NULL,_id));
@@ -204,7 +206,7 @@ MatchFunctor* XMLFilterImpl::buildFunctor(
         m_log.warn("skipping stand-alone %s with no id", logname);
         return NULL;
     }
-    else if (*id && functorMap.count(id)) {
+    else if (*id && functorMap.getMatchFunctors().count(id)) {
         if (standalone) {
             m_log.warn("skipping duplicate stand-alone %s with id (%s)", logname, id);
             return NULL;
@@ -216,8 +218,8 @@ MatchFunctor* XMLFilterImpl::buildFunctor(
     auto_ptr<QName> type(XMLHelper::getXSIType(e));
     if (type.get()) {
         try {
-            MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), e);
-            functorMap.insert(make_pair(id, func));
+            MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), make_pair(&functorMap,e));
+            functorMap.getMatchFunctors().insert(multimap<string,MatchFunctor*>::value_type(id, func));
             return func;
         }
         catch (exception& ex) {
@@ -232,32 +234,32 @@ MatchFunctor* XMLFilterImpl::buildFunctor(
     return NULL;
 }
 
-pair<string,const MatchFunctor*> XMLFilterImpl::buildAttributeRule(const DOMElement* e, bool standalone)
+pair<string,const MatchFunctor*> XMLFilterImpl::buildAttributeRule(const DOMElement* e, const FilterPolicyContext& functorMap, bool standalone)
 {
     auto_ptr_char temp(e->getAttributeNS(NULL,_id));
     const char* id = (temp.get() && *temp.get()) ? temp.get() : "";
 
     if (standalone && !*id) {
         m_log.warn("skipping stand-alone AttributeRule with no id");
-        return make_pair(string(),(MatchFunctor*)NULL);
+        return make_pair(string(),(const MatchFunctor*)NULL);
     }
     else if (*id && m_attrRules.count(id)) {
         if (standalone) {
             m_log.warn("skipping duplicate stand-alone AttributeRule with id (%s)", id);
-            return make_pair(string(),(MatchFunctor*)NULL);
+            return make_pair(string(),(const MatchFunctor*)NULL);
         }
         else
             id = "";
     }
 
-    auto_ptr_char attrId(e->getAttributeNS(NULL,attributeId));
-    if (!attrId.get() || !*attrId.get())
-        m_log.warn("skipping AttributeRule with no attributeId");
+    auto_ptr_char attrID(e->getAttributeNS(NULL,attributeID));
+    if (!attrID.get() || !*attrID.get())
+        m_log.warn("skipping AttributeRule with no attributeID");
 
     e = XMLHelper::getFirstChildElement(e);
     MatchFunctor* func=NULL;
     if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PermitValueRule)) {
-        func = buildFunctor(e, m_permitValRules, "PermitValueRule", false);
+        func = buildFunctor(e, functorMap, "PermitValueRule", false);
     }
     else if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PermitValueRuleReference)) {
         auto_ptr_char ref(e->getAttributeNS(NULL, _ref));
@@ -269,27 +271,27 @@ pair<string,const MatchFunctor*> XMLFilterImpl::buildAttributeRule(const DOMElem
 
     if (func) {
         if (*id)
-            return m_attrRules[id] = make_pair(attrId.get(), func);
+            return m_attrRules[id] = pair<string,const MatchFunctor*>(attrID.get(), func);
         else
-            return make_pair(attrId.get(), func);
+            return pair<string,const MatchFunctor*>(attrID.get(), func);
     }
 
     m_log.warn("skipping AttributeRule (%s), PermitValueRule invalid or missing", id);
-    return make_pair(string(),(MatchFunctor*)NULL);
+    return make_pair(string(),(const MatchFunctor*)NULL);
 }
 
-void XMLFilterImpl::filterAttributes(const FilteringContext& context, multimap<string,Attribute*>& attributes) const
+void XMLFilterImpl::filterAttributes(const FilteringContext& context, vector<Attribute*>& attributes) const
 {
-    auto_ptr_char issuer(context.getAttributeIssuer());\r
-\r
-    m_log.debug("filtering %lu attribute(s) from (%s)", attributes.size(), issuer.get() ? issuer.get() : "unknown source");\r
-\r
-    if (m_policies.empty()) {\r
-        m_log.warn("no filter policies were loaded, filtering out all attributes from (%s)", issuer.get() ? issuer.get() : "unknown source");\r
-        for_each(attributes.begin(), attributes.end(), cleanup_pair<string,Attribute>());\r
-        attributes.clear();\r
-        return;\r
-    }\r
+    auto_ptr_char issuer(context.getAttributeIssuer());
+
+    m_log.debug("filtering %lu attribute(s) from (%s)", attributes.size(), issuer.get() ? issuer.get() : "unknown source");
+
+    if (m_policies.empty()) {
+        m_log.warn("no filter policies were loaded, filtering out all attributes from (%s)", issuer.get() ? issuer.get() : "unknown source");
+        for_each(attributes.begin(), attributes.end(), xmltooling::cleanup<Attribute>());
+        attributes.clear();
+        return;
+    }
 
     size_t count,index;
 
@@ -297,40 +299,79 @@ void XMLFilterImpl::filterAttributes(const FilteringContext& context, multimap<s
     for (vector<Policy>::const_iterator p=m_policies.begin(); p!=m_policies.end(); ++p) {
         if (p->m_applies->evaluatePolicyRequirement(context)) {
             // Loop over the attributes and look for possible rules to run.
-            for (multimap<string,Attribute*>::iterator a=attributes.begin(); a!=attributes.end();) {
-                pair<Policy::rules_t::const_iterator,Policy::rules_t::const_iterator> rules = p->m_rules.equal_range(a->second->getId());
-                if (rules.first == rules.second) {
-                    // No rule found, so we're filtering it out.
-                    m_log.warn(\r
-                        "no rule found, filtering out values of attribute (%s) from (%s)", a->second->getId(), issuer.get() ? issuer.get() : "unknown source"\r
-                        );\r
-                    multimap<string,Attribute*>::iterator dead = a++;\r
-                    delete dead->second;\r
-                    attributes.erase(dead);\r
-                }
-                else {
+            for (vector<Attribute*>::size_type a=0; a<attributes.size();) {
+                bool ruleFound = false;
+                Attribute* attr = attributes[a];
+                pair<Policy::rules_t::const_iterator,Policy::rules_t::const_iterator> rules = p->m_rules.equal_range(attr->getId());
+                if (rules.first != rules.second) {
+                    ruleFound = true;
                     // Run each rule in sequence.
-                    m_log.debug("filtering values of attribute (%s) from (%s)", a->second->getId(), issuer.get() ? issuer.get() : "unknown source");\r
+                    m_log.debug(
+                        "applying filtering rule(s) for attribute (%s) from (%s)",
+                        attr->getId(), issuer.get() ? issuer.get() : "unknown source"
+                        );
                     for (; rules.first!=rules.second; ++rules.first) {
-                        count = a->second->valueCount();
+                        count = attr->valueCount();
                         for (index=0; index < count;) {
                             // The return value tells us whether to index past the accepted value, or stay put and decrement the count.
-                            if (rules.first->second->evaluatePermitValue(context, *(a->second), index))
+                            if (rules.first->second->evaluatePermitValue(context, *attr, index)) {
                                 index++;
-                            else
+                            }
+                            else {
+                                m_log.warn(
+                                    "removed value at position (%lu) of attribute (%s) from (%s)",
+                                    index, attr->getId(), issuer.get() ? issuer.get() : "unknown source"
+                                    );
+                                attr->removeValue(index);
                                 count--;
+                            }
                         }
                     }
-                    // See if any values are left, delete if not.
-                    if (count>0) {
-                        ++a;
+                }
+
+                rules = p->m_rules.equal_range("*");
+                if (rules.first != rules.second) {
+                    // Run each rule in sequence.
+                    if (!ruleFound) {
+                        m_log.debug(
+                            "applying wildcard rule(s) for attribute (%s) from (%s)",
+                            attr->getId(), issuer.get() ? issuer.get() : "unknown source"
+                            );
+                        ruleFound = true;
                     }
-                    else {
-                        multimap<string,Attribute*>::iterator dead = a++;\r
-                        delete dead->second;\r
-                        attributes.erase(dead);\r
+                    for (; rules.first!=rules.second; ++rules.first) {
+                        count = attr->valueCount();
+                        for (index=0; index < count;) {
+                            // The return value tells us whether to index past the accepted value, or stay put and decrement the count.
+                            if (rules.first->second->evaluatePermitValue(context, *attr, index)) {
+                                index++;
+                            }
+                            else {
+                                m_log.warn(
+                                    "removed value at position (%lu) of attribute (%s) from (%s)",
+                                    index, attr->getId(), issuer.get() ? issuer.get() : "unknown source"
+                                    );
+                                attr->removeValue(index);
+                                count--;
+                            }
+                        }
                     }
                 }
+
+                if (!ruleFound || attr->valueCount() == 0) {
+                    if (!ruleFound) {
+                        // No rule found, so we're filtering it out.
+                        m_log.warn(
+                            "no rule found, removing all values of attribute (%s) from (%s)",
+                            attr->getId(), issuer.get() ? issuer.get() : "unknown source"
+                            );
+                    }
+                    delete attr;
+                    attributes.erase(attributes.begin() + a);
+                }
+                else {
+                    ++a;
+                }
             }
         }
     }