2 * The Shibboleth License, Version 1.
4 * University Corporation for Advanced Internet Development, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
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.
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
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.
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.
50 /* XMLRequestMapper.cpp - an XML-based map of URLs to application names and settings
60 #include <log4cpp/Category.hh>
63 using namespace log4cpp;
65 using namespace shibboleth;
66 using namespace shibtarget;
68 namespace shibtarget {
70 class Override : public XMLPropertySet, public DOMNodeFilter
73 Override() : m_base(NULL), m_acl(NULL) {}
74 Override(const DOMElement* e, Category& log, const Override* base=NULL);
76 IAccessControl* m_acl;
79 pair<bool,bool> getBool(const char* name, const char* ns=NULL) const;
80 pair<bool,const char*> getString(const char* name, const char* ns=NULL) const;
81 pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const;
82 pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const;
83 pair<bool,int> getInt(const char* name, const char* ns=NULL) const;
84 const IPropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:target:config:1.0") const;
86 // Provides filter to exclude special config elements.
87 short acceptNode(const DOMNode* node) const;
89 const Override* locate(const char* path) const;
92 void loadACL(const DOMElement* e, Category& log);
94 map<string,Override*> m_map;
97 const Override* m_base;
100 class XMLRequestMapperImpl : public ReloadableXMLFileImpl, public Override
103 XMLRequestMapperImpl(const char* pathname) : ReloadableXMLFileImpl(pathname) { init(); }
104 XMLRequestMapperImpl(const DOMElement* e) : ReloadableXMLFileImpl(e) { init(); }
106 ~XMLRequestMapperImpl() {}
108 const Override* findOverride(const char* vhost, const char* path) const;
112 map<string,Override*> m_extras;
115 // An implementation of the URL->application mapping API using an XML file
116 class XMLRequestMapper : public IRequestMapper, public ReloadableXMLFile
119 XMLRequestMapper(const DOMElement* e) : ReloadableXMLFile(e) {}
120 ~XMLRequestMapper() {}
122 virtual Settings getSettingsFromURL(const char* url) const;
123 virtual Settings getSettingsFromParsedURL(
124 const char* scheme, const char* hostname, unsigned int port, const char* path=NULL
128 virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
129 virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
133 IPlugIn* XMLRequestMapFactory(const DOMElement* e)
135 XMLRequestMapper* m=new XMLRequestMapper(e);
137 m->getImplementation();
146 short Override::acceptNode(const DOMNode* node) const
148 if (XMLString::compareString(node->getNamespaceURI(),ShibTargetConfig::SHIBTARGET_NS))
149 return FILTER_ACCEPT;
150 const XMLCh* name=node->getLocalName();
151 if (XMLString::compareString(name,SHIBT_L(AccessControlProvider)) ||
152 XMLString::compareString(name,SHIBT_L(Host)) ||
153 XMLString::compareString(name,SHIBT_L(Path)))
154 return FILTER_REJECT;
156 return FILTER_ACCEPT;
159 void Override::loadACL(const DOMElement* e, Category& log)
161 IPlugIn* plugin=NULL;
162 const DOMElement* acl=saml::XML::getFirstChildElement(e,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(htaccess));
164 log.info("building htaccess provider...");
165 plugin=ShibConfig::getConfig().m_plugMgr.newPlugin(shibtarget::XML::htaccessType,acl);
168 acl=saml::XML::getFirstChildElement(e,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(AccessControlProvider));
170 auto_ptr_char type(acl->getAttributeNS(NULL,SHIBT_L(type)));
171 log.info("building Access Control provider of type %s...",type.get());
172 plugin=ShibConfig::getConfig().m_plugMgr.newPlugin(type.get(),acl);
176 IAccessControl* acl=dynamic_cast<IAccessControl*>(plugin);
181 log.fatal("plugin was not an Access Control provider");
182 throw UnsupportedExtensionException("plugin was not an Access Control provider");
187 Override::Override(const DOMElement* e, Category& log, const Override* base) : m_base(base), m_acl(NULL)
190 // Load the property set.
193 // Load any AccessControl provider.
196 // Handle nested Paths.
197 DOMNodeList* nlist=e->getElementsByTagNameNS(ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Path));
198 for (int i=0; nlist && i<nlist->getLength(); i++) {
199 DOMElement* path=static_cast<DOMElement*>(nlist->item(i));
200 const XMLCh* n=path->getAttributeNS(NULL,SHIBT_L(name));
202 log.warn("skipping Path element (%d) with empty name attribute",i);
205 Override* o=new Override(path,log,this);
206 pair<bool,const char*> name=o->getString("name");
207 char* dup=strdup(name.second);
208 for (char* pch=dup; *pch; pch++)
210 if (m_map.count(dup)) {
211 log.warn("Skipping duplicate Path element (%s)",dup);
226 Override::~Override()
229 for (map<string,Override*>::iterator i=m_map.begin(); i!=m_map.end(); i++)
233 pair<bool,bool> Override::getBool(const char* name, const char* ns) const
235 pair<bool,bool> ret=XMLPropertySet::getBool(name,ns);
238 return m_base ? m_base->getBool(name,ns) : ret;
241 pair<bool,const char*> Override::getString(const char* name, const char* ns) const
243 pair<bool,const char*> ret=XMLPropertySet::getString(name,ns);
246 return m_base ? m_base->getString(name,ns) : ret;
249 pair<bool,const XMLCh*> Override::getXMLString(const char* name, const char* ns) const
251 pair<bool,const XMLCh*> ret=XMLPropertySet::getXMLString(name,ns);
254 return m_base ? m_base->getXMLString(name,ns) : ret;
257 pair<bool,unsigned int> Override::getUnsignedInt(const char* name, const char* ns) const
259 pair<bool,unsigned int> ret=XMLPropertySet::getUnsignedInt(name,ns);
262 return m_base ? m_base->getUnsignedInt(name,ns) : ret;
265 pair<bool,int> Override::getInt(const char* name, const char* ns) const
267 pair<bool,int> ret=XMLPropertySet::getInt(name,ns);
270 return m_base ? m_base->getInt(name,ns) : ret;
273 const IPropertySet* Override::getPropertySet(const char* name, const char* ns) const
275 const IPropertySet* ret=XMLPropertySet::getPropertySet(name,ns);
278 return m_base->getPropertySet(name,ns);
281 const Override* Override::locate(const char* path) const
283 char* dup=strdup(path);
284 char* sep=strchr(dup,'?');
287 for (char* pch=dup; *pch; pch++)
290 const Override* o=this;
294 const char* token=strtok_r(dup,"/",&pos);
296 const char* token=strtok(dup,"/");
300 map<string,Override*>::const_iterator i=o->m_map.find(token);
301 if (i==o->m_map.end())
305 token=strtok_r(NULL,"/",&pos);
307 token=strtok(NULL,"/");
315 void XMLRequestMapperImpl::init()
318 log=&Category::getInstance("shibtarget.XMLRequestMapper");
321 if (!saml::XML::isElementNamed(ReloadableXMLFileImpl::m_root,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(RequestMap))) {
322 log->error("Construction requires a valid request mapping file: (conf:RequestMap as root element)");
323 throw MalformedException("Construction requires a valid request mapping file: (conf:RequestMap as root element)");
326 // Load the property set.
327 load(ReloadableXMLFileImpl::m_root,*log,this);
329 // Load any AccessControl provider.
330 loadACL(ReloadableXMLFileImpl::m_root,*log);
332 // Loop over the Host elements.
333 DOMNodeList* nlist = ReloadableXMLFileImpl::m_root->getElementsByTagNameNS(ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Host));
334 for (int i=0; nlist && i<nlist->getLength(); i++) {
335 DOMElement* host=static_cast<DOMElement*>(nlist->item(i));
336 const XMLCh* n=host->getAttributeNS(NULL,SHIBT_L(name));
338 log->warn("Skipping Host element (%d) with empty name attribute",i);
342 Override* o=new Override(host,*log,this);
343 pair<bool,const char*> name=o->getString("name");
344 pair<bool,const char*> scheme=o->getString("scheme");
345 pair<bool,const char*> port=o->getString("port");
347 char* dup=strdup(name.second);
348 for (char* pch=dup; *pch; pch++)
351 string url(scheme.first ? scheme.second : "http");
352 url=url + "://" + dup;
355 // First store a port-less version.
356 if (m_map.count(url)) {
357 log->warn("Skipping duplicate Host element (%s)",url.c_str());
363 // Now append the default port.
364 // XXX Use getservbyname instead?
365 if (!scheme.first || !strcmp(scheme.second,"http"))
367 else if (!strcmp(scheme.second,"https"))
369 else if (!strcmp(scheme.second,"ftp"))
371 else if (!strcmp(scheme.second,"ldap"))
373 else if (!strcmp(scheme.second,"ldaps"))
379 url=url + ':' + port.second;
380 if (m_map.count(url)) {
381 log->warn("Skipping duplicate Host element (%s)",url.c_str());
387 log->debug("Added <Host> mapping for %s",url.c_str());
390 catch (SAMLException& e) {
391 log->errorStream() << "Error while parsing request mapping configuration: " << e.what() << CategoryStream::ENDLINE;
397 log->error("Unexpected error while parsing request mapping configuration");
403 const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const char* path) const
405 const Override* o=NULL;
406 map<string,Override*>::const_iterator i=m_map.find(vhost);
410 i=m_extras.find(vhost);
411 if (i!=m_extras.end())
415 return o ? o->locate(path) : this;
418 const char* split_url(const char* url, string& vhost)
420 const char* path=NULL;
421 const char* slash=strchr(url,'/');
424 slash=strchr(slash,'/');
427 path=strchr(slash,'/');
429 vhost.append(url,path-url);
437 ReloadableXMLFileImpl* XMLRequestMapper::newImplementation(const char* pathname, bool first) const
439 return new XMLRequestMapperImpl(pathname);
442 ReloadableXMLFileImpl* XMLRequestMapper::newImplementation(const DOMElement* e, bool first) const
444 return new XMLRequestMapperImpl(e);
447 IRequestMapper::Settings XMLRequestMapper::getSettingsFromURL(const char* url) const
450 const char* path=split_url(url,vhost);
452 XMLRequestMapperImpl* impl=static_cast<XMLRequestMapperImpl*>(getImplementation());
453 const Override* o=impl->findOverride(vhost.c_str(), path);
455 if (impl->log->isDebugEnabled()) {
456 saml::NDC ndc("getApplicationFromURL");
457 pair<bool,const char*> ret=o->getString("applicationId");
458 impl->log->debug("mapped %s to %s", url, ret.second);
461 return Settings(o,o->m_acl);
464 IRequestMapper::Settings XMLRequestMapper::getSettingsFromParsedURL(
465 const char* scheme, const char* hostname, unsigned int port, const char* path
469 string vhost(scheme);
470 vhost=vhost + "://" + hostname + ':';
472 _snprintf(buf,20,"%u",port);
474 snprintf(buf,20,"%u",port);
478 XMLRequestMapperImpl* impl=static_cast<XMLRequestMapperImpl*>(getImplementation());
479 const Override* o=impl->findOverride(vhost.c_str(), path);
481 if (impl->log->isDebugEnabled())
483 saml::NDC ndc("getApplicationFromParsedURL");
484 pair<bool,const char*> ret=o->getString("applicationId");
485 impl->log->debug("mapped %s%s to %s", vhost.c_str(), path ? path : "", ret.second);
488 return Settings(o,o->m_acl);