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