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