b7c1b93d10ae958aef2a3b18e8df743882577b84
[shibboleth/cpp-sp.git] / xmlproviders / XMLAAP.cpp
1 /*
2  *  Copyright 2001-2005 Internet2
3  * 
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /* XMLAAP.cpp - XML AAP implementation
18
19    Scott Cantor
20    12/21/02
21
22    $History:$
23 */
24
25 #include "internal.h"
26 #include <algorithm>
27 #include <log4cpp/Category.hh>
28
29 using namespace shibboleth;
30 using namespace saml;
31 using namespace log4cpp;
32 using namespace std;
33
34 #include <xercesc/util/regx/RegularExpression.hpp>
35
36 namespace {
37
38     class XMLAAPImpl : public ReloadableXMLFileImpl
39     {
40     public:
41         XMLAAPImpl(const char* pathname) : ReloadableXMLFileImpl(pathname), anyAttribute(false) { init(); }
42         XMLAAPImpl(const DOMElement* e) : ReloadableXMLFileImpl(e), anyAttribute(false) { init(); }
43         void init();
44         ~XMLAAPImpl();
45         
46         class AttributeRule : public IAttributeRule
47         {
48         public:
49             AttributeRule(const DOMElement* e);
50             ~AttributeRule() {}
51             
52             const XMLCh* getName() const { return m_name; }
53             const XMLCh* getNamespace() const { return m_namespace; }
54             const char* getAlias() const { return m_alias.get(); }
55             const char* getHeader() const { return m_header.get(); }
56             bool getCaseSensitive() const { return m_caseSensitive; }
57             bool getScoped() const { return m_scoped; }
58             void apply(SAMLAttribute& attribute, const IEntityDescriptor* source=NULL) const;
59     
60             enum value_type { literal, regexp, xpath };
61         private:    
62             const XMLCh* m_name;
63             const XMLCh* m_namespace;
64             auto_ptr_char m_alias;
65             auto_ptr_char m_header;
66             bool m_caseSensitive;
67             bool m_scoped;
68
69             struct SiteRule
70             {
71                 SiteRule() : anyValue(false) {}
72                 bool anyValue;
73                 vector<pair<value_type,const XMLCh*> > valueDenials;
74                 vector<pair<value_type,const XMLCh*> > valueAccepts;
75                 vector<pair<value_type,const XMLCh*> > scopeDenials;
76                 vector<pair<value_type,const XMLCh*> > scopeAccepts;
77             };
78             
79             value_type toValueType(const DOMElement* e);
80             bool scopeCheck(
81                 const DOMElement* e,
82                 const IExtendedEntityDescriptor* source,
83                 const vector<const SiteRule*>& ruleStack
84                 ) const;
85             bool accept(const DOMElement* e, const IExtendedEntityDescriptor* source=NULL) const;
86             
87             SiteRule m_anySiteRule;
88     #ifdef HAVE_GOOD_STL
89             typedef map<xstring,SiteRule> sitemap_t;
90     #else
91             typedef map<string,SiteRule> sitemap_t;
92     #endif
93             sitemap_t m_siteMap;
94         };
95     
96         bool anyAttribute;
97         vector<const IAttributeRule*> m_attrs;
98         map<string,const IAttributeRule*> m_aliasMap;
99     #ifdef HAVE_GOOD_STL
100         typedef map<xstring,AttributeRule*> attrmap_t;
101     #else
102         typedef map<string,AttributeRule*> attrmap_t;
103     #endif
104         attrmap_t m_attrMap;
105     };
106
107     class XMLAAP : public IAAP, public ReloadableXMLFile
108     {
109     public:
110         XMLAAP(const DOMElement* e) : ReloadableXMLFile(e) {}
111         ~XMLAAP() {}
112         
113         bool anyAttribute() const {return static_cast<XMLAAPImpl*>(getImplementation())->anyAttribute;}
114         const IAttributeRule* lookup(const XMLCh* attrName, const XMLCh* attrNamespace=NULL) const;
115         const IAttributeRule* lookup(const char* alias) const;
116         Iterator<const IAttributeRule*> getAttributeRules() const;
117
118     protected:
119         virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
120         virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
121     };
122
123 }
124
125 IPlugIn* XMLAAPFactory(const DOMElement* e)
126 {
127     auto_ptr<XMLAAP> aap(new XMLAAP(e));
128     aap->getImplementation();
129     return aap.release();
130 }
131
132 ReloadableXMLFileImpl* XMLAAP::newImplementation(const DOMElement* e, bool first) const
133 {
134     return new XMLAAPImpl(e);
135 }
136
137 ReloadableXMLFileImpl* XMLAAP::newImplementation(const char* pathname, bool first) const
138 {
139     return new XMLAAPImpl(pathname);
140 }
141
142 void XMLAAPImpl::init()
143 {
144 #ifdef _DEBUG
145     saml::NDC ndc("init");
146 #endif
147     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".AAP");
148
149     try
150     {
151         if (!saml::XML::isElementNamed(m_root,::XML::SHIB_NS,SHIB_L(AttributeAcceptancePolicy)))
152         {
153             log.error("Construction requires a valid AAP file: (shib:AttributeAcceptancePolicy as root element)");
154             throw MalformedException("Construction requires a valid AAP file: (shib:AttributeAcceptancePolicy as root element)");
155         }
156
157         // Check for AnyAttribute element.
158         DOMElement* anyAttr = saml::XML::getFirstChildElement(m_root,::XML::SHIB_NS,SHIB_L(AnyAttribute));
159         if (anyAttr) {
160             anyAttribute = true;
161             log.warn("<AnyAttribute> found, will short-circuit all attribute value and scope filtering");
162         }
163
164         // Loop over the AttributeRule elements.
165         DOMNodeList* nlist = m_root->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(AttributeRule));
166         for (XMLSize_t i=0; nlist && i<nlist->getLength(); i++)
167         {
168             AttributeRule* rule=new AttributeRule(static_cast<DOMElement*>(nlist->item(i)));
169 #ifdef HAVE_GOOD_STL
170             xstring key=rule->getName();
171             key=key + chBang + chBang + (rule->getNamespace() ? rule->getNamespace() : Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
172 #else
173             auto_ptr_char aname(rule->getName());
174             string key(aname.get());
175             key+="!!";
176             if (rule->getNamespace())
177             {
178                 auto_ptr_char ans(rule->getNamespace());
179                 key+=ans.get();
180             }
181             else
182                 key+="urn:mace:shibboleth:1.0:attributeNamespace:uri";
183 #endif
184             m_attrMap[key]=rule;
185             m_attrs.push_back(rule);
186             if (rule->getAlias()) {
187                 // user can only apply to REMOTE_USER
188                 if (!strcmp(rule->getAlias(),"user")) {
189                     if (strcmp(rule->getHeader(),"REMOTE_USER"))
190                         log.error("<AttributeRule> cannot specify Alias of 'user', please use alternate value");
191                     else
192                         m_aliasMap[rule->getAlias()]=rule;
193                 }
194                 else {
195                     m_aliasMap[rule->getAlias()]=rule;
196                 }
197             }
198         }
199     }
200     catch (SAMLException& e)
201     {
202         log.errorStream() << "Error while parsing AAP: " << e.what() << CategoryStream::ENDLINE;
203         this->~XMLAAPImpl();
204         throw;
205     }
206 #ifndef _DEBUG
207     catch (...)
208     {
209         log.error("Unexpected error while parsing AAP");
210         this->~XMLAAPImpl();
211         throw;
212     }
213 #endif
214 }
215
216 XMLAAPImpl::~XMLAAPImpl()
217 {
218 #ifdef HAVE_GOOD_STL
219     for_each(m_attrMap.begin(),m_attrMap.end(),xmltooling::cleanup_pair<xstring,AttributeRule>());
220 #else
221     for_each(m_attrMap.begin(),m_attrMap.end(),xmltooling::cleanup_pair<string,AttributeRule>());
222 #endif
223 }
224
225 XMLAAPImpl::AttributeRule::AttributeRule(const DOMElement* e) :
226     m_alias(e->hasAttributeNS(NULL,SHIB_L(Alias)) ? e->getAttributeNS(NULL,SHIB_L(Alias)) : NULL),
227     m_header(e->hasAttributeNS(NULL,SHIB_L(Header)) ? e->getAttributeNS(NULL,SHIB_L(Header)) : NULL),
228     m_scoped(false)
229     
230 {
231     m_name=e->getAttributeNS(NULL,SHIB_L(Name));
232     m_namespace=e->getAttributeNS(NULL,SHIB_L(Namespace));
233     if (!m_namespace || !*m_namespace)
234         m_namespace=Constants::SHIB_ATTRIBUTE_NAMESPACE_URI;
235     
236     const XMLCh* caseSensitive=e->getAttributeNS(NULL,SHIB_L(CaseSensitive));
237     m_caseSensitive=(!caseSensitive || !*caseSensitive || *caseSensitive==chDigit_1 || *caseSensitive==chLatin_t);
238     
239     const XMLCh* scoped=e->getAttributeNS(NULL,SHIB_L(Scoped));
240     m_scoped=(scoped && (*scoped==chDigit_1 || *scoped==chLatin_t));
241     
242     // Check for an AnySite rule.
243     DOMElement* anysite = saml::XML::getFirstChildElement(e);
244     if (anysite && saml::XML::isElementNamed(static_cast<DOMElement*>(anysite),::XML::SHIB_NS,SHIB_L(AnySite)))
245     {
246         // Process Scope elements.
247         DOMNodeList* vlist = static_cast<DOMElement*>(anysite)->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(Scope));
248         for (XMLSize_t i=0; vlist && i<vlist->getLength(); i++)
249         {
250             m_scoped=true;
251             DOMElement* se=static_cast<DOMElement*>(vlist->item(i));
252             DOMNode* valnode=se->getFirstChild();
253             if (valnode && valnode->getNodeType()==DOMNode::TEXT_NODE)
254             {
255                 const XMLCh* accept=se->getAttributeNS(NULL,SHIB_L(Accept));
256                 if (!accept || !*accept || *accept==chDigit_1 || *accept==chLatin_t)
257                     m_anySiteRule.scopeAccepts.push_back(pair<value_type,const XMLCh*>(toValueType(se),valnode->getNodeValue()));
258                 else
259                     m_anySiteRule.scopeDenials.push_back(pair<value_type,const XMLCh*>(toValueType(se),valnode->getNodeValue()));
260             }
261         }
262
263         // Check for an AnyValue rule.
264         vlist = static_cast<DOMElement*>(anysite)->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(AnyValue));
265         if (vlist && vlist->getLength())
266         {
267             m_anySiteRule.anyValue=true;
268         }
269         else
270         {
271             // Process each Value element.
272             vlist = static_cast<DOMElement*>(anysite)->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(Value));
273             for (XMLSize_t j=0; vlist && j<vlist->getLength(); j++)
274             {
275                 DOMElement* ve=static_cast<DOMElement*>(vlist->item(j));
276                 DOMNode* valnode=ve->getFirstChild();
277                 if (valnode && valnode->getNodeType()==DOMNode::TEXT_NODE) {
278                     const XMLCh* accept=ve->getAttributeNS(NULL,SHIB_L(Accept));
279                     if (!accept || !*accept || *accept==chDigit_1 || *accept==chLatin_t)
280                         m_anySiteRule.valueAccepts.push_back(pair<value_type,const XMLCh*>(toValueType(ve),valnode->getNodeValue()));
281                     else
282                         m_anySiteRule.valueDenials.push_back(pair<value_type,const XMLCh*>(toValueType(ve),valnode->getNodeValue()));
283                 }
284             }
285         }
286     }
287
288     // Loop over the SiteRule elements.
289     DOMNodeList* slist = e->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(SiteRule));
290     for (XMLSize_t k=0; slist && k<slist->getLength(); k++)
291     {
292         const XMLCh* srulename=static_cast<DOMElement*>(slist->item(k))->getAttributeNS(NULL,SHIB_L(Name));
293 #ifdef HAVE_GOOD_STL
294         m_siteMap[srulename]=SiteRule();
295         SiteRule& srule=m_siteMap[srulename];
296 #else
297         auto_ptr_char srulename2(srulename);
298         m_siteMap[srulename2.get()]=SiteRule();
299         SiteRule& srule=m_siteMap[srulename2.get()];
300 #endif
301
302         // Process Scope elements.
303         DOMNodeList* vlist = static_cast<DOMElement*>(slist->item(k))->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(Scope));
304         for (XMLSize_t i=0; vlist && i<vlist->getLength(); i++)
305         {
306             m_scoped=true;
307             DOMElement* se=static_cast<DOMElement*>(vlist->item(i));
308             DOMNode* valnode=se->getFirstChild();
309             if (valnode && valnode->getNodeType()==DOMNode::TEXT_NODE)
310             {
311                 const XMLCh* accept=se->getAttributeNS(NULL,SHIB_L(Accept));
312                 if (!accept || !*accept || *accept==chDigit_1 || *accept==chLatin_t)
313                     srule.scopeAccepts.push_back(pair<value_type,const XMLCh*>(toValueType(se),valnode->getNodeValue()));
314                 else
315                     srule.scopeDenials.push_back(pair<value_type,const XMLCh*>(toValueType(se),valnode->getNodeValue()));
316             }
317         }
318
319         // Check for an AnyValue rule.
320         vlist = static_cast<DOMElement*>(slist->item(k))->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(AnyValue));
321         if (vlist && vlist->getLength())
322         {
323             srule.anyValue=true;
324         }
325         else
326         {
327             // Process each Value element.
328             vlist = static_cast<DOMElement*>(slist->item(k))->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(Value));
329             for (XMLSize_t j=0; vlist && j<vlist->getLength(); j++)
330             {
331                 DOMElement* ve=static_cast<DOMElement*>(vlist->item(j));
332                 DOMNode* valnode=ve->getFirstChild();
333                 if (valnode && valnode->getNodeType()==DOMNode::TEXT_NODE) {
334                     const XMLCh* accept=ve->getAttributeNS(NULL,SHIB_L(Accept));
335                     if (!accept || !*accept || *accept==chDigit_1 || *accept==chLatin_t)
336                         srule.valueAccepts.push_back(pair<value_type,const XMLCh*>(toValueType(ve),valnode->getNodeValue()));
337                     else
338                         srule.valueDenials.push_back(pair<value_type,const XMLCh*>(toValueType(ve),valnode->getNodeValue()));
339                 }
340             }
341         }
342     }
343 }
344
345 XMLAAPImpl::AttributeRule::value_type XMLAAPImpl::AttributeRule::toValueType(const DOMElement* e)
346 {
347     if (!XMLString::compareString(SHIB_L(literal),e->getAttributeNS(NULL,SHIB_L(Type))))
348         return literal;
349     else if (!XMLString::compareString(SHIB_L(regexp),e->getAttributeNS(NULL,SHIB_L(Type))))
350         return regexp;
351     else if (!XMLString::compareString(SHIB_L(xpath),e->getAttributeNS(NULL,SHIB_L(Type))))
352         return xpath;
353     throw MalformedException("Found an invalid value or scope rule type.");
354 }
355
356 const IAttributeRule* XMLAAP::lookup(const XMLCh* attrName, const XMLCh* attrNamespace) const
357 {
358 #ifdef HAVE_GOOD_STL
359     xstring key=attrName;
360     key=key + chBang + chBang + (attrNamespace ? attrNamespace : Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
361 #else
362     auto_ptr_char aname(attrName);
363     string key=aname.get();
364     key+="!!";
365     if (attrNamespace)
366     {
367         auto_ptr_char ans(attrNamespace);
368         key+=ans.get();
369     }
370     else
371         key+="urn:mace:shibboleth:1.0:attributeNamespace:uri";
372 #endif
373     XMLAAPImpl* impl=dynamic_cast<XMLAAPImpl*>(getImplementation());
374     XMLAAPImpl::attrmap_t::const_iterator i=impl->m_attrMap.find(key);
375     return (i==impl->m_attrMap.end()) ? NULL : i->second;
376 }
377
378 const IAttributeRule* XMLAAP::lookup(const char* alias) const
379 {
380     XMLAAPImpl* impl=dynamic_cast<XMLAAPImpl*>(getImplementation());
381     map<string,const IAttributeRule*>::const_iterator i=impl->m_aliasMap.find(alias);
382     return (i==impl->m_aliasMap.end()) ? NULL : i->second;
383 }
384
385 Iterator<const IAttributeRule*> XMLAAP::getAttributeRules() const
386 {
387     return dynamic_cast<XMLAAPImpl*>(getImplementation())->m_attrs;
388 }
389
390 namespace {
391     bool match(const XMLCh* exp, const XMLCh* test)
392     {
393         try
394         {
395             RegularExpression re(exp);
396             if (re.matches(test))
397                 return true;
398         }
399         catch (XMLException& ex)
400         {
401             auto_ptr<char> tmp(XMLString::transcode(ex.getMessage()));
402             Category::getInstance(XMLPROVIDERS_LOGCAT".XMLAAPImpl").errorStream()
403                 << "caught exception while parsing regular expression: " << tmp.get() << CategoryStream::ENDLINE;
404         }
405         return false;
406     }
407 }
408
409 bool XMLAAPImpl::AttributeRule::scopeCheck(
410     const DOMElement* e,
411     const IExtendedEntityDescriptor* source,
412     const vector<const SiteRule*>& ruleStack
413     ) const
414 {
415 #ifdef _DEBUG
416     saml::NDC ndc("scopeCheck");
417 #endif
418     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".AAP");
419
420     // Are we scoped?
421     const XMLCh* scope=e->getAttributeNS(NULL,SHIB_L(Scope));
422     if (!scope || !*scope) {
423         // Are we allowed to be unscoped?
424         if (m_scoped && log.isWarnEnabled()) {
425                 auto_ptr_char temp(m_name);
426                 log.warn("attribute (%s) is scoped, no scope supplied, rejecting it",temp.get());
427         }
428         return !m_scoped;
429     }
430
431     // With the new algorithm, we evaluate each matching rule in sequence, separately.
432     for (vector<const SiteRule*>::const_iterator rule=ruleStack.begin(); rule!=ruleStack.end(); rule++) {
433
434         // Now run any denials.
435         vector<pair<value_type,const XMLCh*> >::const_iterator i;
436         for (i=(*rule)->scopeDenials.begin(); i!=(*rule)->scopeDenials.end(); i++) {
437             if ((i->first==literal && !XMLString::compareString(i->second,scope)) ||
438                 (i->first==regexp && match(i->second,scope))) {
439                 if (log.isWarnEnabled()) {
440                     auto_ptr_char temp(m_name);
441                     auto_ptr_char temp2(scope);
442                     log.warn("attribute (%s) scope (%s) denied by site rule, rejecting it",temp.get(),temp2.get());
443                 }
444                 return false;
445             }
446             else if (i->first==xpath)
447                 log.warn("scope checking does not permit XPath rules");
448         }
449
450         // Now run any accepts.
451         for (i=(*rule)->scopeAccepts.begin(); i!=(*rule)->scopeAccepts.end(); i++) {
452             if ((i->first==literal && !XMLString::compareString(i->second,scope)) ||
453                 (i->first==regexp && match(i->second,scope))) {
454                 log.debug("matching site rule, scope match");
455                 return true;
456             }
457             else if (i->first==xpath)
458                 log.warn("scope checking does not permit XPath rules");
459         }
460     }
461
462     // If we still can't decide, defer to metadata.
463     if (source) {
464         Iterator<pair<const XMLCh*,bool> > domains=source->getScopes();
465         while (domains.hasNext()) {
466             const pair<const XMLCh*,bool>& p=domains.next();
467             if ((p.second && match(p.first,scope)) || !XMLString::compareString(p.first,scope)) {
468                 log.debug("scope match via site metadata");
469                 return true;
470             }
471         }
472     }
473     
474     if (log.isWarnEnabled()) {
475         auto_ptr_char temp(m_name);
476         auto_ptr_char temp2(scope);
477         log.warn("attribute (%s) scope (%s) not accepted",temp.get(),temp2.get());
478     }
479     return false;
480 }
481
482 bool XMLAAPImpl::AttributeRule::accept(const DOMElement* e, const IExtendedEntityDescriptor* source) const
483 {
484 #ifdef _DEBUG
485     saml::NDC ndc("accept");
486 #endif
487     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".AAP");
488     
489     if (log.isDebugEnabled()) {
490         auto_ptr_char temp(m_name);
491         auto_ptr_char temp2(source ? source->getId() : NULL);
492         log.debug("evaluating value for attribute (%s) from site (%s)",temp.get(),temp2.get() ? temp2.get() : "<unspecified>");
493     }
494     
495     // This is a complete revamp. The "any" cases become a degenerate case, the "least-specific" matching rule.
496     // The first step is to build a list of matching rules, most-specific to least-specific.
497     
498     vector<const SiteRule*> ruleStack;
499     if (source) {
500         // Primary match is against entityID.
501 #ifdef HAVE_GOOD_STL
502         const XMLCh* os=source->getId();
503 #else
504         auto_ptr_char pos(source->getId());
505         const char* os=pos.get();
506 #endif
507         sitemap_t::const_iterator srule=m_siteMap.find(os);
508         if (srule!=m_siteMap.end())
509             ruleStack.push_back(&srule->second);
510         
511         // Secondary matches are on groups.
512         const IEntitiesDescriptor* group=source->getEntitiesDescriptor();
513         while (group) {
514             if (group->getName()) {
515 #ifdef HAVE_GOOD_STL
516                 os=group->getName();
517 #else
518                 auto_ptr_char gname(group->getName());
519                 const char* os=gname.get();
520 #endif
521                 srule=m_siteMap.find(os);
522                 if (srule!=m_siteMap.end())
523                     ruleStack.push_back(&srule->second);
524             }
525             group = group->getEntitiesDescriptor();
526         }
527     }
528     // Tertiary match is the AnySite rule.
529     ruleStack.push_back(&m_anySiteRule);
530
531     // Still don't support complex content models...
532     DOMNode* n=e->getFirstChild();
533     bool bSimple=(n && n->getNodeType()==DOMNode::TEXT_NODE);
534
535     // With the new algorithm, we evaluate each matching rule in sequence, separately.
536     for (vector<const SiteRule*>::const_iterator rule=ruleStack.begin(); rule!=ruleStack.end(); rule++) {
537
538         // Check for shortcut AnyValue blanket rule.
539         if ((*rule)->anyValue) {
540             log.debug("matching site rule, any value match");
541             return scopeCheck(e,source,ruleStack);
542         }
543
544         // Now run any denials.
545         vector<pair<value_type,const XMLCh*> >::const_iterator i;
546         for (i=(*rule)->valueDenials.begin(); bSimple && i!=(*rule)->valueDenials.end(); i++) {
547             switch (i->first) {
548                 case literal:
549                     if ((m_caseSensitive && !XMLString::compareString(i->second,n->getNodeValue())) ||
550                         (!m_caseSensitive && !XMLString::compareIString(i->second,n->getNodeValue()))) {
551                         if (log.isWarnEnabled()) {
552                             auto_ptr_char temp(m_name);
553                             log.warn("attribute (%s) value explicitly denied by site rule, rejecting it",temp.get());
554                         }
555                         return false;
556                     }
557                     break;
558                 
559                 case regexp:
560                     if (match(i->second,n->getNodeValue())) {
561                         if (log.isWarnEnabled()) {
562                             auto_ptr_char temp(m_name);
563                             log.warn("attribute (%s) value explicitly denied by site rule, rejecting it",temp.get());
564                         }
565                         return false;
566                     }
567                     break;
568                 
569                 case xpath:
570                     log.warn("implementation does not support XPath value rules");
571                     break;
572             }
573         }
574
575         // Now run any accepts.
576         for (i=(*rule)->valueAccepts.begin(); bSimple && i!=(*rule)->valueAccepts.end(); i++) {
577             switch (i->first) {
578                 case literal:
579                     if ((m_caseSensitive && !XMLString::compareString(i->second,n->getNodeValue())) ||
580                         (!m_caseSensitive && !XMLString::compareIString(i->second,n->getNodeValue()))) {
581                         log.debug("site rule, value match");
582                         return scopeCheck(e,source,ruleStack);
583                     }
584                     break;
585                 
586                 case regexp:
587                     if (match(i->second,n->getNodeValue())) {
588                         log.debug("site rule, value match");
589                         return scopeCheck(e,source,ruleStack);
590                     }
591                     break;
592                 
593                 case xpath:
594                     log.warn("implementation does not support XPath value rules");
595                     break;
596             }
597         }
598     }
599
600     if (log.isWarnEnabled()) {
601         auto_ptr_char temp(m_name);
602         auto_ptr_char temp2(n->getNodeValue());
603         log.warn("%sattribute (%s) value (%s) could not be validated by policy, rejecting it",
604                  (bSimple ? "" : "complex "),temp.get(),temp2.get());
605     }
606     return false;
607 }
608
609 void XMLAAPImpl::AttributeRule::apply(SAMLAttribute& attribute, const IEntityDescriptor* source) const
610 {
611     // Check each value.
612     DOMNodeList* vals=attribute.getValueElements();
613     int i2=0;
614     for (XMLSize_t i=0; vals && i < vals->getLength(); i++) {
615         if (!accept(static_cast<DOMElement*>(vals->item(i)),source ? dynamic_cast<const IExtendedEntityDescriptor*>(source) : NULL))
616             attribute.removeValue(i2);
617         else
618             i2++;
619     }
620     
621     // Now see if we trashed it irrevocably.
622     attribute.checkValidity();
623 }