Revised metadata API.
[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             value_type toValueType(const DOMElement* e);
106             bool scopeCheck(const DOMElement* e, const IScopedRoleDescriptor* role=NULL) const;
107             bool accept(const DOMElement* e, const IScopedRoleDescriptor* role=NULL) const;
108             
109             struct SiteRule
110             {
111                 SiteRule() : anyValue(false) {}
112                 bool anyValue;
113                 vector<pair<value_type,const XMLCh*> > valueRules;
114                 vector<pair<value_type,const XMLCh*> > scopeDenials;
115                 vector<pair<value_type,const XMLCh*> > scopeAccepts;
116             };
117     
118             SiteRule m_anySiteRule;
119     #ifdef HAVE_GOOD_STL
120             typedef map<xstring,SiteRule> sitemap_t;
121     #else
122             typedef map<string,SiteRule> sitemap_t;
123     #endif
124             sitemap_t m_siteMap;
125         };
126     
127         bool anyAttribute;
128         vector<const IAttributeRule*> m_attrs;
129         map<string,const IAttributeRule*> m_aliasMap;
130     #ifdef HAVE_GOOD_STL
131         typedef map<xstring,AttributeRule*> attrmap_t;
132     #else
133         typedef map<string,AttributeRule*> attrmap_t;
134     #endif
135         attrmap_t m_attrMap;
136     };
137
138     class XMLAAP : public IAAP, public ReloadableXMLFile
139     {
140     public:
141         XMLAAP(const DOMElement* e) : ReloadableXMLFile(e) {}
142         ~XMLAAP() {}
143         
144         bool anyAttribute() const {return static_cast<XMLAAPImpl*>(getImplementation())->anyAttribute;}
145         const IAttributeRule* lookup(const XMLCh* attrName, const XMLCh* attrNamespace=NULL) const;
146         const IAttributeRule* lookup(const char* alias) const;
147         Iterator<const IAttributeRule*> getAttributeRules() const;
148
149     protected:
150         virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
151         virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
152     };
153
154 }
155
156 IPlugIn* XMLAAPFactory(const DOMElement* e)
157 {
158     XMLAAP* aap=new XMLAAP(e);
159     try
160     {
161         aap->getImplementation();
162     }
163     catch (...)
164     {
165         delete aap;
166         throw;
167     }
168     return aap;
169 }
170
171 ReloadableXMLFileImpl* XMLAAP::newImplementation(const DOMElement* e, bool first) const
172 {
173     return new XMLAAPImpl(e);
174 }
175
176 ReloadableXMLFileImpl* XMLAAP::newImplementation(const char* pathname, bool first) const
177 {
178     return new XMLAAPImpl(pathname);
179 }
180
181 void XMLAAPImpl::init()
182 {
183     NDC ndc("XMLAAPImpl");
184     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLAAPImpl");
185
186     try
187     {
188         if (!saml::XML::isElementNamed(m_root,::XML::SHIB_NS,SHIB_L(AttributeAcceptancePolicy)))
189         {
190             log.error("Construction requires a valid AAP file: (shib:AttributeAcceptancePolicy as root element)");
191             throw MalformedException("Construction requires a valid AAP file: (shib:AttributeAcceptancePolicy as root element)");
192         }
193
194         // Check for AnyAttribute element.
195         DOMElement* anyAttr = saml::XML::getFirstChildElement(m_root,::XML::SHIB_NS,SHIB_L(AnyAttribute));
196         if (anyAttr)
197             anyAttribute = true;
198
199         // Loop over the AttributeRule elements.
200         DOMNodeList* nlist = m_root->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(AttributeRule));
201         for (int i=0; nlist && i<nlist->getLength(); i++)
202         {
203             AttributeRule* rule=new AttributeRule(static_cast<DOMElement*>(nlist->item(i)));
204 #ifdef HAVE_GOOD_STL
205             xstring key=rule->getName();
206             key=key + chBang + chBang + (rule->getNamespace() ? rule->getNamespace() : Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
207 #else
208             auto_ptr_char aname(rule->getName());
209             string key(aname.get());
210             key+="!!";
211             if (rule->getNamespace())
212             {
213                 auto_ptr_char ans(rule->getNamespace());
214                 key+=ans.get();
215             }
216             else
217                 key+="urn:mace:shibboleth:1.0:attributeNamespace:uri";
218 #endif
219             m_attrMap[key]=rule;
220             m_attrs.push_back(rule);
221             if (rule->getAlias())
222                 m_aliasMap[rule->getAlias()]=rule;
223         }
224     }
225     catch (SAMLException& e)
226     {
227         log.errorStream() << "Error while parsing AAP: " << e.what() << CategoryStream::ENDLINE;
228         for (attrmap_t::iterator i=m_attrMap.begin(); i!=m_attrMap.end(); i++)
229             delete i->second;
230         throw;
231     }
232     catch (...)
233     {
234         log.error("Unexpected error while parsing AAP");
235         for (attrmap_t::iterator i=m_attrMap.begin(); i!=m_attrMap.end(); i++)
236             delete i->second;
237         throw;
238     }
239
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     DOMNode* anysite = e->getFirstChild();
268     while (anysite && anysite->getNodeType()!=DOMNode::ELEMENT_NODE)
269     {
270         anysite = anysite->getNextSibling();
271         continue;
272     }
273
274     if (anysite && saml::XML::isElementNamed(static_cast<DOMElement*>(anysite),::XML::SHIB_NS,SHIB_L(AnySite)))
275     {
276         // Process Scope elements.
277         DOMNodeList* vlist = static_cast<DOMElement*>(anysite)->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(Scope));
278         for (int i=0; vlist && i<vlist->getLength(); i++)
279         {
280             DOMElement* se=static_cast<DOMElement*>(vlist->item(i));
281             DOMNode* valnode=se->getFirstChild();
282             if (valnode && valnode->getNodeType()==DOMNode::TEXT_NODE)
283             {
284                 const XMLCh* accept=se->getAttributeNS(NULL,SHIB_L(Accept));
285                 if (!accept || !*accept || *accept==chDigit_1 || *accept==chLatin_t)
286                     m_anySiteRule.scopeAccepts.push_back(pair<value_type,const XMLCh*>(toValueType(se),valnode->getNodeValue()));
287                 else
288                     m_anySiteRule.scopeDenials.push_back(pair<value_type,const XMLCh*>(toValueType(se),valnode->getNodeValue()));
289             }
290         }
291
292         // Check for an AnyValue rule.
293         vlist = static_cast<DOMElement*>(anysite)->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(AnyValue));
294         if (vlist && vlist->getLength())
295         {
296             m_anySiteRule.anyValue=true;
297         }
298         else
299         {
300             // Process each Value element.
301             vlist = static_cast<DOMElement*>(anysite)->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(Value));
302             for (int j=0; vlist && j<vlist->getLength(); j++)
303             {
304                 DOMElement* ve=static_cast<DOMElement*>(vlist->item(j));
305                 DOMNode* valnode=ve->getFirstChild();
306                 if (valnode && valnode->getNodeType()==DOMNode::TEXT_NODE)
307                     m_anySiteRule.valueRules.push_back(pair<value_type,const XMLCh*>(toValueType(ve),valnode->getNodeValue()));
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             DOMElement* se=static_cast<DOMElement*>(vlist->item(i));
331             DOMNode* valnode=se->getFirstChild();
332             if (valnode && valnode->getNodeType()==DOMNode::TEXT_NODE)
333             {
334                 const XMLCh* accept=se->getAttributeNS(NULL,SHIB_L(Accept));
335                 if (!accept || !*accept || *accept==chDigit_1 || *accept==chLatin_t)
336                     srule.scopeAccepts.push_back(pair<value_type,const XMLCh*>(toValueType(se),valnode->getNodeValue()));
337                 else
338                     srule.scopeDenials.push_back(pair<value_type,const XMLCh*>(toValueType(se),valnode->getNodeValue()));
339             }
340         }
341
342         // Check for an AnyValue rule.
343         vlist = static_cast<DOMElement*>(slist->item(k))->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(AnyValue));
344         if (vlist && vlist->getLength())
345         {
346             srule.anyValue=true;
347         }
348         else
349         {
350             // Process each Value element.
351             vlist = static_cast<DOMElement*>(slist->item(k))->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(Value));
352             for (int j=0; vlist && j<vlist->getLength(); j++)
353             {
354                 DOMElement* ve=static_cast<DOMElement*>(vlist->item(j));
355                 DOMNode* valnode=ve->getFirstChild();
356                 if (valnode && valnode->getNodeType()==DOMNode::TEXT_NODE)
357                     srule.valueRules.push_back(pair<value_type,const XMLCh*>(toValueType(ve),valnode->getNodeValue()));
358             }
359         }
360     }
361 }
362
363 XMLAAPImpl::AttributeRule::value_type XMLAAPImpl::AttributeRule::toValueType(const DOMElement* e)
364 {
365     if (!XMLString::compareString(SHIB_L(literal),e->getAttributeNS(NULL,SHIB_L(Type))))
366         return literal;
367     else if (!XMLString::compareString(SHIB_L(regexp),e->getAttributeNS(NULL,SHIB_L(Type))))
368         return regexp;
369     else if (!XMLString::compareString(SHIB_L(xpath),e->getAttributeNS(NULL,SHIB_L(Type))))
370         return xpath;
371     throw MalformedException("Found an invalid value or scope rule type.");
372 }
373
374 const IAttributeRule* XMLAAP::lookup(const XMLCh* attrName, const XMLCh* attrNamespace) const
375 {
376 #ifdef HAVE_GOOD_STL
377     xstring key=attrName;
378     key=key + chBang + chBang + (attrNamespace ? attrNamespace : Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
379 #else
380     auto_ptr_char aname(attrName);
381     string key=aname.get();
382     key+="!!";
383     if (attrNamespace)
384     {
385         auto_ptr_char ans(attrNamespace);
386         key+=ans.get();
387     }
388     else
389         key+="urn:mace:shibboleth:1.0:attributeNamespace:uri";
390 #endif
391     XMLAAPImpl* impl=dynamic_cast<XMLAAPImpl*>(getImplementation());
392     XMLAAPImpl::attrmap_t::const_iterator i=impl->m_attrMap.find(key);
393     return (i==impl->m_attrMap.end()) ? NULL : i->second;
394 }
395
396 const IAttributeRule* XMLAAP::lookup(const char* alias) const
397 {
398     XMLAAPImpl* impl=dynamic_cast<XMLAAPImpl*>(getImplementation());
399     map<string,const IAttributeRule*>::const_iterator i=impl->m_aliasMap.find(alias);
400     return (i==impl->m_aliasMap.end()) ? NULL : i->second;
401 }
402
403 Iterator<const IAttributeRule*> XMLAAP::getAttributeRules() const
404 {
405     return dynamic_cast<XMLAAPImpl*>(getImplementation())->m_attrs;
406 }
407
408 namespace {
409     bool match(const XMLCh* exp, const XMLCh* test)
410     {
411         try
412         {
413             RegularExpression re(exp);
414             if (re.matches(test))
415                 return true;
416         }
417         catch (XMLException& ex)
418         {
419             auto_ptr<char> tmp(XMLString::transcode(ex.getMessage()));
420             Category::getInstance(XMLPROVIDERS_LOGCAT".XMLAAPImpl").errorStream()
421                 << "caught exception while parsing regular expression: " << tmp.get() << CategoryStream::ENDLINE;
422         }
423         return false;
424     }
425 }
426
427 bool XMLAAPImpl::AttributeRule::scopeCheck(const DOMElement* e, const IScopedRoleDescriptor* role) const
428 {
429     NDC ndc("scopeCheck");
430     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLAAPImpl");
431
432     // Are we scoped?
433     const XMLCh* scope=e->getAttributeNS(NULL,SHIB_L(Scope));
434     if (!scope || !*scope) {
435         // Are we allowed to be unscoped?
436         if (m_scoped && log.isWarnEnabled()) {
437                 auto_ptr_char temp(m_name);
438                 log.warn("attribute %s is scoped, no scope supplied, rejecting it",temp.get());
439         }
440         return !m_scoped;
441     }
442
443     vector<pair<value_type,const XMLCh*> >::const_iterator i;
444
445     // Denials take precedence, always.
446     
447     // Any site denials...
448     for (i=m_anySiteRule.scopeDenials.begin(); i!=m_anySiteRule.scopeDenials.end(); i++)
449     {
450         if ((i->first==literal && !XMLString::compareString(i->second,scope)) ||
451             (i->first==regexp && match(i->second,scope)))
452         {
453             if (log.isWarnEnabled())
454             {
455                 auto_ptr_char temp(m_name);
456                 auto_ptr_char temp2(scope);
457                 log.warn("attribute %s scope {%s} denied by any-site AAP, rejecting it",temp.get(),temp2.get());
458             }
459             return false;
460         }
461         else if (i->first==xpath)
462             log.warn("scope checking does not permit XPath rules");
463     }
464
465     sitemap_t::const_iterator srule=m_siteMap.end();
466     if (role) {
467 #ifdef HAVE_GOOD_STL
468         const XMLCh* os=role->getEntityDescriptor()->getId();
469 #else
470         auto_ptr_char pos(role->getEntityDescriptor()->getId());
471         const char* os=pos.get();
472 #endif
473         srule=m_siteMap.find(os);
474         if (srule!=m_siteMap.end())
475         {
476             // Site-specific denials...
477             for (i=srule->second.scopeDenials.begin(); i!=srule->second.scopeDenials.end(); i++)
478             {
479                 if ((i->first==literal && !XMLString::compareString(i->second,scope)) ||
480                     (i->first==regexp && match(i->second,scope)))
481                 {
482                     if (log.isWarnEnabled())
483                     {
484                         auto_ptr_char temp(m_name);
485                         auto_ptr_char temp2(scope);
486                         log.warn("attribute %s scope {%s} denied by site AAP, rejecting it",temp.get(),temp2.get());
487                     }
488                     return false;
489                 }
490                 else if (i->first==xpath)
491                     log.warn("scope checking does not permit XPath rules");
492             }
493         }
494     }
495     
496     // Any site accepts...
497     for (i=m_anySiteRule.scopeAccepts.begin(); i!=m_anySiteRule.scopeAccepts.end(); i++)
498     {
499         if ((i->first==literal && !XMLString::compareString(i->second,scope)) ||
500             (i->first==regexp && match(i->second,scope)))
501         {
502             log.debug("any site, scope match");
503             return true;
504         }
505         else if (i->first==xpath)
506             log.warn("scope checking does not permit XPath rules");
507     }
508
509     if (srule!=m_siteMap.end())
510     {
511         // Site-specific accepts...
512         for (i=srule->second.scopeAccepts.begin(); i!=srule->second.scopeAccepts.end(); i++)
513         {
514             if ((i->first==literal && !XMLString::compareString(i->second,scope)) ||
515                 (i->first==regexp && match(i->second,scope)))
516             {
517                 log.debug("matching site, scope match");
518                 return true;
519             }
520             else if (i->first==xpath)
521                 log.warn("scope checking does not permit XPath rules");
522         }
523     }
524     
525     // If we still can't decide, defer to metadata.
526     if (role) {
527         Iterator<pair<const XMLCh*,bool> > domains=role->getScopes();
528         while (domains.hasNext())
529         {
530             const pair<const XMLCh*,bool>& p=domains.next();
531             if ((p.second && match(p.first,scope)) || !XMLString::compareString(p.first,scope))
532             {
533                 log.debug("scope match via site metadata");
534                 return true;
535             }
536         }
537     }
538     
539     if (log.isWarnEnabled())
540     {
541         auto_ptr_char temp(m_name);
542         auto_ptr_char temp2(scope);
543         log.warn("attribute %s scope {%s} not accepted",temp.get(),temp2.get());
544     }
545     return false;
546 }
547
548 bool XMLAAPImpl::AttributeRule::accept(const DOMElement* e, const IScopedRoleDescriptor* role) const
549 {
550     NDC ndc("accept");
551     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLAAPImpl");
552     
553     if (log.isDebugEnabled())
554     {
555         auto_ptr_char temp(m_name);
556         auto_ptr_char temp2(role ? role->getEntityDescriptor()->getId() : NULL);
557         log.debug("evaluating value for attribute %s from site %s",temp.get(),temp2.get() ? temp2.get() : "<unspecified>");
558     }
559     
560     if (m_anySiteRule.anyValue)
561     {
562         log.debug("any site, any value, match");
563         return scopeCheck(e,role);
564     }
565
566     // Don't fully support complex content models...
567     DOMNode* n=e->getFirstChild();
568     bool bSimple=(n && n->getNodeType()==DOMNode::TEXT_NODE);
569
570     vector<pair<value_type,const XMLCh*> >::const_iterator i;
571     for (i=m_anySiteRule.valueRules.begin(); bSimple && i!=m_anySiteRule.valueRules.end(); i++)
572     {
573         if ((i->first==literal && !XMLString::compareString(i->second,n->getNodeValue())) ||
574             (i->first==regexp && match(i->second,n->getNodeValue())))
575         {
576             log.debug("any site, value match");
577             return scopeCheck(e,role);
578         }
579         else if (i->first==xpath)
580             log.warn("implementation does not support XPath value rules");
581     }
582
583     if (role) {
584 #ifdef HAVE_GOOD_STL
585         const XMLCh* os=role->getEntityDescriptor()->getId();
586 #else
587         auto_ptr_char pos(role->getEntityDescriptor()->getId());
588         const char* os=pos.get();
589 #endif
590         sitemap_t::const_iterator srule=m_siteMap.find(os);
591         if (srule==m_siteMap.end())
592         {
593             if (log.isWarnEnabled())
594             {
595                 auto_ptr_char temp(m_name);
596                 auto_ptr_char temp2(role->getEntityDescriptor()->getId());
597                 log.warn("site %s not found in attribute %s ruleset, any value is rejected",temp2.get(),temp.get());
598             }
599             return false;
600         }
601     
602         if (srule->second.anyValue)
603         {
604             log.debug("matching site, any value, match");
605             return scopeCheck(e,role);
606         }
607     
608         for (i=srule->second.valueRules.begin(); bSimple && i!=srule->second.valueRules.end(); i++) {
609             switch (i->first) {
610                 case literal:
611                     if ((m_caseSensitive && !XMLString::compareString(i->second,n->getNodeValue())) ||
612                         (!m_caseSensitive && !XMLString::compareIString(i->second,n->getNodeValue()))) {
613                         log.debug("matching site, value match");
614                         return scopeCheck(e,role);
615                     }
616                     break;
617                 
618                 case regexp:
619                     if (match(i->second,n->getNodeValue())) {
620                         log.debug("matching site, value match");
621                         return scopeCheck(e,role);
622                     }
623                     break;
624                 
625                 case xpath:
626                     log.warn("implementation does not support XPath value rules");
627                     break;
628             }
629         }
630     }
631     
632     if (log.isWarnEnabled())
633     {
634         auto_ptr_char temp(m_name);
635         auto_ptr_char temp2(n->getNodeValue());
636         log.warn("%sattribute %s value {%s} could not be validated by AAP, rejecting it",
637                  (bSimple ? "" : "complex "),temp.get(),temp2.get());
638     }
639     return false;
640 }
641
642 void XMLAAPImpl::AttributeRule::apply(SAMLAttribute& attribute, const IRoleDescriptor* role) const
643 {
644     // Check each value.
645     Iterator<const DOMElement*> vals=attribute.getValueElements();
646     for (unsigned int i=0; i < vals.size();) {
647         if (!accept(vals[i],role ? dynamic_cast<const IScopedRoleDescriptor*>(role) : NULL))
648             attribute.removeValue(i);
649         else
650             i++;
651     }
652     
653     // Now see if we trashed it irrevocably.
654     attribute.checkValidity();
655 }