2 * Copyright 2001-2007 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * XMLAttributeFilter.cpp
20 * AttributeFilter based on an XML policy language.
24 #include "Application.h"
25 #include "ServiceProvider.h"
26 #include "attribute/Attribute.h"
27 #include "attribute/filtering/AttributeFilter.h"
28 #include "attribute/filtering/FilterPolicyContext.h"
29 #include "util/SPConstants.h"
31 #include <xmltooling/util/NDC.h>
32 #include <xmltooling/util/ReloadableXMLFile.h>
33 #include <xmltooling/util/XMLHelper.h>
34 #include <xercesc/util/XMLUniDefs.hpp>
36 using namespace shibsp;
37 using namespace opensaml::saml2md;
38 using namespace opensaml;
39 using namespace xmltooling;
44 #if defined (_MSC_VER)
45 #pragma warning( push )
46 #pragma warning( disable : 4250 )
49 struct SHIBSP_DLLLOCAL Policy
51 Policy() : m_applies(NULL) {}
52 const MatchFunctor* m_applies;
53 typedef multimap<string,const MatchFunctor*> rules_t;
57 class SHIBSP_DLLLOCAL XMLFilterImpl
60 XMLFilterImpl(const DOMElement* e, Category& log);
63 m_document->release();
64 for_each(m_policyReqRules.begin(), m_policyReqRules.end(), cleanup_pair<string,MatchFunctor>());
65 for_each(m_permitValRules.begin(), m_permitValRules.end(), cleanup_pair<string,MatchFunctor>());
68 void setDocument(DOMDocument* doc) {
72 void filterAttributes(const FilteringContext& context, vector<Attribute*>& attributes) const;
75 MatchFunctor* buildFunctor(
76 const DOMElement* e, const FilterPolicyContext& functorMap, const char* logname, bool standalone
78 pair<string,const MatchFunctor*> buildAttributeRule(const DOMElement* e, const FilterPolicyContext& functorMap, bool standalone);
81 DOMDocument* m_document;
82 vector<Policy> m_policies;
83 map< string,pair<string,const MatchFunctor*> > m_attrRules;
84 multimap<string,MatchFunctor*> m_policyReqRules;
85 multimap<string,MatchFunctor*> m_permitValRules;
88 class SHIBSP_DLLLOCAL XMLFilter : public AttributeFilter, public ReloadableXMLFile
91 XMLFilter(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".AttributeFilter")), m_impl(NULL) {
98 void filterAttributes(const FilteringContext& context, vector<Attribute*>& attributes) const {
99 m_impl->filterAttributes(context, attributes);
103 pair<bool,DOMElement*> load();
106 XMLFilterImpl* m_impl;
109 #if defined (_MSC_VER)
110 #pragma warning( pop )
113 AttributeFilter* SHIBSP_DLLLOCAL XMLAttributeFilterFactory(const DOMElement* const & e)
115 return new XMLFilter(e);
118 static const XMLCh AttributeFilterPolicyGroup[] = UNICODE_LITERAL_26(A,t,t,r,i,b,u,t,e,F,i,l,t,e,r,P,o,l,i,c,y,G,r,o,u,p);
119 static const XMLCh AttributeFilterPolicy[] = UNICODE_LITERAL_21(A,t,t,r,i,b,u,t,e,F,i,l,t,e,r,P,o,l,i,c,y);
120 static const XMLCh AttributeRule[] = UNICODE_LITERAL_13(A,t,t,r,i,b,u,t,e,R,u,l,e);
121 static const XMLCh AttributeRuleReference[] = UNICODE_LITERAL_22(A,t,t,r,i,b,u,t,e,R,u,l,e,R,e,f,e,r,e,n,c,e);
122 static const XMLCh PermitValueRule[] = UNICODE_LITERAL_15(P,e,r,m,i,t,V,a,l,u,e,R,u,l,e);
123 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);
124 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);
125 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);
126 static const XMLCh attributeID[] = UNICODE_LITERAL_11(a,t,t,r,i,b,u,t,e,I,D);
127 static const XMLCh _id[] = UNICODE_LITERAL_2(i,d);
128 static const XMLCh _ref[] = UNICODE_LITERAL_3(r,e,f);
131 XMLFilterImpl::XMLFilterImpl(const DOMElement* e, Category& log) : m_log(log), m_document(NULL)
134 xmltooling::NDC ndc("XMLFilterImpl");
137 if (!XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeFilterPolicyGroup))
138 throw ConfigurationException("XML AttributeFilter requires afp:AttributeFilterPolicyGroup at root of configuration.");
140 FilterPolicyContext reqFunctors(m_policyReqRules);
141 FilterPolicyContext valFunctors(m_permitValRules);
143 DOMElement* child = XMLHelper::getFirstChildElement(e);
145 if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PolicyRequirementRule)) {
146 buildFunctor(child, reqFunctors, "PolicyRequirementRule", true);
148 else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PermitValueRule)) {
149 buildFunctor(child, valFunctors, "PermitValueRule", true);
151 else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeRule)) {
152 buildAttributeRule(child, valFunctors, true);
154 else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeFilterPolicy)) {
155 e = XMLHelper::getFirstChildElement(child);
156 MatchFunctor* func = NULL;
157 if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PolicyRequirementRule)) {
158 func = buildFunctor(e, reqFunctors, "PolicyRequirementRule", false);
160 else if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PolicyRequirementRuleReference)) {
161 auto_ptr_char ref(e->getAttributeNS(NULL, _ref));
162 if (ref.get() && *ref.get()) {
163 multimap<string,MatchFunctor*>::const_iterator prr = m_policyReqRules.find(ref.get());
164 func = (prr!=m_policyReqRules.end()) ? prr->second : NULL;
168 m_policies.push_back(Policy());
169 m_policies.back().m_applies = func;
170 e = XMLHelper::getNextSiblingElement(e);
172 if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeRule)) {
173 pair<string,const MatchFunctor*> rule = buildAttributeRule(e, valFunctors, false);
175 m_policies.back().m_rules.insert(Policy::rules_t::value_type(rule.first, rule.second));
177 else if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeRuleReference)) {
178 auto_ptr_char ref(e->getAttributeNS(NULL, _ref));
179 if (ref.get() && *ref.get()) {
180 map< string,pair<string,const MatchFunctor*> >::const_iterator ar = m_attrRules.find(ref.get());
181 if (ar != m_attrRules.end())
182 m_policies.back().m_rules.insert(Policy::rules_t::value_type(ar->second.first, ar->second.second));
184 m_log.warn("skipping invalid AttributeRuleReference (%s)", ref.get());
187 e = XMLHelper::getNextSiblingElement(e);
191 m_log.warn("skipping AttributeFilterPolicy, PolicyRequirementRule invalid or missing");
194 child = XMLHelper::getNextSiblingElement(child);
198 MatchFunctor* XMLFilterImpl::buildFunctor(
199 const DOMElement* e, const FilterPolicyContext& functorMap, const char* logname, bool standalone
202 auto_ptr_char temp(e->getAttributeNS(NULL,_id));
203 const char* id = (temp.get() && *temp.get()) ? temp.get() : "";
205 if (standalone && !*id) {
206 m_log.warn("skipping stand-alone %s with no id", logname);
209 else if (*id && functorMap.getMatchFunctors().count(id)) {
211 m_log.warn("skipping duplicate stand-alone %s with id (%s)", logname, id);
218 auto_ptr<QName> type(XMLHelper::getXSIType(e));
221 MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), make_pair(&functorMap,e));
222 functorMap.getMatchFunctors().insert(multimap<string,MatchFunctor*>::value_type(id, func));
225 catch (exception& ex) {
226 m_log.error("error building %s with type (%s): %s", logname, type->toString().c_str(), ex.what());
230 m_log.warn("skipping stand-alone %s with no xsi:type", logname);
232 m_log.error("%s with no xsi:type", logname);
237 pair<string,const MatchFunctor*> XMLFilterImpl::buildAttributeRule(const DOMElement* e, const FilterPolicyContext& functorMap, bool standalone)
239 auto_ptr_char temp(e->getAttributeNS(NULL,_id));
240 const char* id = (temp.get() && *temp.get()) ? temp.get() : "";
242 if (standalone && !*id) {
243 m_log.warn("skipping stand-alone AttributeRule with no id");
244 return make_pair(string(),(const MatchFunctor*)NULL);
246 else if (*id && m_attrRules.count(id)) {
248 m_log.warn("skipping duplicate stand-alone AttributeRule with id (%s)", id);
249 return make_pair(string(),(const MatchFunctor*)NULL);
255 auto_ptr_char attrID(e->getAttributeNS(NULL,attributeID));
256 if (!attrID.get() || !*attrID.get())
257 m_log.warn("skipping AttributeRule with no attributeID");
259 e = XMLHelper::getFirstChildElement(e);
260 MatchFunctor* func=NULL;
261 if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PermitValueRule)) {
262 func = buildFunctor(e, functorMap, "PermitValueRule", false);
264 else if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PermitValueRuleReference)) {
265 auto_ptr_char ref(e->getAttributeNS(NULL, _ref));
266 if (ref.get() && *ref.get()) {
267 multimap<string,MatchFunctor*>::const_iterator pvr = m_permitValRules.find(ref.get());
268 func = (pvr!=m_permitValRules.end()) ? pvr->second : NULL;
274 return m_attrRules[id] = pair<string,const MatchFunctor*>(attrID.get(), func);
276 return pair<string,const MatchFunctor*>(attrID.get(), func);
279 m_log.warn("skipping AttributeRule (%s), PermitValueRule invalid or missing", id);
280 return make_pair(string(),(const MatchFunctor*)NULL);
283 void XMLFilterImpl::filterAttributes(const FilteringContext& context, vector<Attribute*>& attributes) const
285 auto_ptr_char issuer(context.getAttributeIssuer());
287 m_log.debug("filtering %lu attribute(s) from (%s)", attributes.size(), issuer.get() ? issuer.get() : "unknown source");
289 if (m_policies.empty()) {
290 m_log.warn("no filter policies were loaded, filtering out all attributes from (%s)", issuer.get() ? issuer.get() : "unknown source");
291 for_each(attributes.begin(), attributes.end(), xmltooling::cleanup<Attribute>());
299 for (vector<Policy>::const_iterator p=m_policies.begin(); p!=m_policies.end(); ++p) {
300 if (p->m_applies->evaluatePolicyRequirement(context)) {
301 // Loop over the attributes and look for possible rules to run.
302 for (vector<Attribute*>::size_type a=0; a<attributes.size();) {
303 bool ruleFound = false;
304 Attribute* attr = attributes[a];
305 pair<Policy::rules_t::const_iterator,Policy::rules_t::const_iterator> rules = p->m_rules.equal_range(attr->getId());
306 if (rules.first != rules.second) {
308 // Run each rule in sequence.
310 "applying filtering rule(s) for attribute (%s) from (%s)",
311 attr->getId(), issuer.get() ? issuer.get() : "unknown source"
313 for (; rules.first!=rules.second; ++rules.first) {
314 count = attr->valueCount();
315 for (index=0; index < count;) {
316 // The return value tells us whether to index past the accepted value, or stay put and decrement the count.
317 if (rules.first->second->evaluatePermitValue(context, *attr, index)) {
322 "filtered value at position (%lu) of attribute (%s) from (%s)",
323 index, attr->getId(), issuer.get() ? issuer.get() : "unknown source"
325 attr->removeValue(index);
332 rules = p->m_rules.equal_range("*");
333 if (rules.first != rules.second) {
334 // Run each rule in sequence.
337 "applying wildcard rule(s) for attribute (%s) from (%s)",
338 attr->getId(), issuer.get() ? issuer.get() : "unknown source"
342 for (; rules.first!=rules.second; ++rules.first) {
343 count = attr->valueCount();
344 for (index=0; index < count;) {
345 // The return value tells us whether to index past the accepted value, or stay put and decrement the count.
346 if (rules.first->second->evaluatePermitValue(context, *attr, index)) {
351 "filtered value at position (%lu) of attribute (%s) from (%s)",
352 index, attr->getId(), issuer.get() ? issuer.get() : "unknown source"
354 attr->removeValue(index);
361 if (!ruleFound || attr->valueCount() == 0) {
363 // No rule found, so we're filtering it out.
365 "no rule found, filtering out values of attribute (%s) from (%s)",
366 attr->getId(), issuer.get() ? issuer.get() : "unknown source"
370 attributes.erase(attributes.begin() + a);
380 pair<bool,DOMElement*> XMLFilter::load()
382 // Load from source using base class.
383 pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
385 // If we own it, wrap it.
386 XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);
388 XMLFilterImpl* impl = new XMLFilterImpl(raw.second, m_log);
390 // If we held the document, transfer it to the impl. If we didn't, it's a no-op.
391 impl->setDocument(docjanitor.release());
396 return make_pair(false,(DOMElement*)NULL);