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