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