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 getSettings(ShibTarget* st) const;
125 virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
126 virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
130 IPlugIn* XMLRequestMapFactory(const DOMElement* e)
132 auto_ptr<XMLRequestMapper> m(new XMLRequestMapper(e));
133 m->getImplementation();
137 short Override::acceptNode(const DOMNode* node) const
139 if (XMLString::compareString(node->getNamespaceURI(),shibtarget::XML::SHIBTARGET_NS))
140 return FILTER_ACCEPT;
141 const XMLCh* name=node->getLocalName();
142 if (XMLString::compareString(name,SHIBT_L(AccessControlProvider)) ||
143 XMLString::compareString(name,SHIBT_L(Host)) ||
144 XMLString::compareString(name,SHIBT_L(Path)))
145 return FILTER_REJECT;
147 return FILTER_ACCEPT;
150 void Override::loadACL(const DOMElement* e, Category& log)
152 IPlugIn* plugin=NULL;
153 const DOMElement* acl=saml::XML::getFirstChildElement(e,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(htaccess));
155 log.info("building Apache htaccess provider...");
156 plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(shibtarget::XML::htAccessControlType,acl);
159 acl=saml::XML::getFirstChildElement(e,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(AccessControl));
161 log.info("building XML-based Access Control provider...");
162 plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(shibtarget::XML::XMLAccessControlType,acl);
165 acl=saml::XML::getFirstChildElement(e,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(AccessControlProvider));
167 auto_ptr_char type(acl->getAttributeNS(NULL,SHIBT_L(type)));
168 log.info("building Access Control provider of type %s...",type.get());
169 plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(type.get(),acl);
174 IAccessControl* acl=dynamic_cast<IAccessControl*>(plugin);
179 log.fatal("plugin was not an Access Control provider");
180 throw UnsupportedExtensionException("plugin was not an Access Control provider");
185 Override::Override(const DOMElement* e, Category& log, const Override* base) : m_base(base), m_acl(NULL)
188 // Load the property set.
191 // Load any AccessControl provider.
194 // Handle nested Paths.
195 DOMNodeList* nlist=e->getElementsByTagNameNS(shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Path));
196 for (int i=0; nlist && i<nlist->getLength(); i++) {
197 DOMElement* path=static_cast<DOMElement*>(nlist->item(i));
198 const XMLCh* n=path->getAttributeNS(NULL,SHIBT_L(name));
200 log.warn("skipping Path element (%d) with empty name attribute",i);
203 else if (*n==chForwardSlash && !n[1]) {
204 log.warn("skipping Path element (%d) with a lone slash in the name attribute",i);
207 Override* o=new Override(path,log,this);
208 pair<bool,const char*> name=o->getString("name");
209 char* dup=strdup(name.second);
210 for (char* pch=dup; *pch; pch++)
212 if (m_map.count(dup)) {
213 log.warn("Skipping duplicate Path element (%s)",dup);
228 Override::~Override()
231 for (map<string,Override*>::iterator i=m_map.begin(); i!=m_map.end(); i++)
235 pair<bool,bool> Override::getBool(const char* name, const char* ns) const
237 pair<bool,bool> ret=XMLPropertySet::getBool(name,ns);
240 return m_base ? m_base->getBool(name,ns) : ret;
243 pair<bool,const char*> Override::getString(const char* name, const char* ns) const
245 pair<bool,const char*> ret=XMLPropertySet::getString(name,ns);
248 return m_base ? m_base->getString(name,ns) : ret;
251 pair<bool,const XMLCh*> Override::getXMLString(const char* name, const char* ns) const
253 pair<bool,const XMLCh*> ret=XMLPropertySet::getXMLString(name,ns);
256 return m_base ? m_base->getXMLString(name,ns) : ret;
259 pair<bool,unsigned int> Override::getUnsignedInt(const char* name, const char* ns) const
261 pair<bool,unsigned int> ret=XMLPropertySet::getUnsignedInt(name,ns);
264 return m_base ? m_base->getUnsignedInt(name,ns) : ret;
267 pair<bool,int> Override::getInt(const char* name, const char* ns) const
269 pair<bool,int> ret=XMLPropertySet::getInt(name,ns);
272 return m_base ? m_base->getInt(name,ns) : ret;
275 const IPropertySet* Override::getPropertySet(const char* name, const char* ns) const
277 const IPropertySet* ret=XMLPropertySet::getPropertySet(name,ns);
280 return m_base->getPropertySet(name,ns);
283 const Override* Override::locate(const char* path) const
285 char* dup=strdup(path);
286 char* sep=strchr(dup,'?');
289 for (char* pch=dup; *pch; pch++)
292 const Override* o=this;
296 const char* token=strtok_r(dup,"/",&pos);
298 const char* token=strtok(dup,"/");
302 map<string,Override*>::const_iterator i=o->m_map.find(token);
303 if (i==o->m_map.end())
307 token=strtok_r(NULL,"/",&pos);
309 token=strtok(NULL,"/");
317 void XMLRequestMapperImpl::init()
322 log=&Category::getInstance("shibtarget.RequestMapper");
325 if (!saml::XML::isElementNamed(ReloadableXMLFileImpl::m_root,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(RequestMap))) {
326 log->error("Construction requires a valid request mapping file: (conf:RequestMap as root element)");
327 throw MalformedException("Construction requires a valid request mapping file: (conf:RequestMap as root element)");
330 // Load the property set.
331 load(ReloadableXMLFileImpl::m_root,*log,this);
333 // Load any AccessControl provider.
334 loadACL(ReloadableXMLFileImpl::m_root,*log);
336 // Loop over the Host elements.
337 DOMNodeList* nlist = ReloadableXMLFileImpl::m_root->getElementsByTagNameNS(shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Host));
338 for (int i=0; nlist && i<nlist->getLength(); i++) {
339 DOMElement* host=static_cast<DOMElement*>(nlist->item(i));
340 const XMLCh* n=host->getAttributeNS(NULL,SHIBT_L(name));
342 log->warn("Skipping Host element (%d) with empty name attribute",i);
346 Override* o=new Override(host,*log,this);
347 pair<bool,const char*> name=o->getString("name");
348 pair<bool,const char*> scheme=o->getString("scheme");
349 pair<bool,const char*> port=o->getString("port");
351 char* dup=strdup(name.second);
352 for (char* pch=dup; *pch; pch++)
354 auto_ptr<char> dupwrap(dup);
356 if (!scheme.first && port.first) {
357 // No scheme, but a port, so assume http.
358 scheme = pair<bool,const char*>(true,"http");
360 else if (scheme.first && !port.first) {
361 // Scheme, no port, so default it.
362 // XXX Use getservbyname instead?
364 if (!strcmp(scheme.second,"http"))
366 else if (!strcmp(scheme.second,"https"))
368 else if (!strcmp(scheme.second,"ftp"))
370 else if (!strcmp(scheme.second,"ldap"))
372 else if (!strcmp(scheme.second,"ldaps"))
377 string url(scheme.second);
378 url=url + "://" + dup;
380 // Is this the default port?
381 if ((!strcmp(scheme.second,"http") && !strcmp(port.second,"80")) ||
382 (!strcmp(scheme.second,"https") && !strcmp(port.second,"443")) ||
383 (!strcmp(scheme.second,"ftp") && !strcmp(port.second,"21")) ||
384 (!strcmp(scheme.second,"ldap") && !strcmp(port.second,"389")) ||
385 (!strcmp(scheme.second,"ldaps") && !strcmp(port.second,"636"))) {
386 // First store a port-less version.
387 if (m_map.count(url) || m_extras.count(url)) {
388 log->warn("Skipping duplicate Host element (%s)",url.c_str());
393 log->debug("Added <Host> mapping for %s",url.c_str());
395 // Now append the port. We use the extras vector, to avoid double freeing the object later.
396 url=url + ':' + port.second;
398 log->debug("Added <Host> mapping for %s",url.c_str());
401 url=url + ':' + port.second;
402 if (m_map.count(url) || m_extras.count(url)) {
403 log->warn("Skipping duplicate Host element (%s)",url.c_str());
408 log->debug("Added <Host> mapping for %s",url.c_str());
412 // No scheme or port, so we enter dual hosts on http:80 and https:443
413 string url("http://");
415 if (m_map.count(url) || m_extras.count(url)) {
416 log->warn("Skipping duplicate Host element (%s)",url.c_str());
421 log->debug("Added <Host> mapping for %s",url.c_str());
424 if (m_map.count(url) || m_extras.count(url)) {
425 log->warn("Skipping duplicate Host element (%s)",url.c_str());
429 log->debug("Added <Host> mapping for %s",url.c_str());
433 if (m_map.count(url) || m_extras.count(url)) {
434 log->warn("Skipping duplicate Host element (%s)",url.c_str());
438 log->debug("Added <Host> mapping for %s",url.c_str());
441 if (m_map.count(url) || m_extras.count(url)) {
442 log->warn("Skipping duplicate Host element (%s)",url.c_str());
446 log->debug("Added <Host> mapping for %s",url.c_str());
450 catch (SAMLException& e) {
451 log->errorStream() << "Error while parsing request mapping configuration: " << e.what() << CategoryStream::ENDLINE;
457 log->error("Unexpected error while parsing request mapping configuration");
463 const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const char* path) const
465 const Override* o=NULL;
466 map<string,Override*>::const_iterator i=m_map.find(vhost);
470 i=m_extras.find(vhost);
471 if (i!=m_extras.end())
475 return o ? o->locate(path) : this;
478 ReloadableXMLFileImpl* XMLRequestMapper::newImplementation(const char* pathname, bool first) const
480 return new XMLRequestMapperImpl(pathname);
483 ReloadableXMLFileImpl* XMLRequestMapper::newImplementation(const DOMElement* e, bool first) const
485 return new XMLRequestMapperImpl(e);
488 IRequestMapper::Settings XMLRequestMapper::getSettings(ShibTarget* st) const
491 vhost << st->getProtocol() << "://" << st->getHostname() << ':' << st->getPort();
493 XMLRequestMapperImpl* impl=static_cast<XMLRequestMapperImpl*>(getImplementation());
494 const Override* o=impl->findOverride(vhost.str().c_str(), st->getRequestURI());
496 if (impl->log->isDebugEnabled()) {
498 saml::NDC ndc("getSettings");
500 pair<bool,const char*> ret=o->getString("applicationId");
501 impl->log->debug("mapped %s%s to %s", vhost.str().c_str(), st->getRequestURI() ? st->getRequestURI() : "", ret.second);
504 return Settings(o,o->m_acl);