*/\r
\r
#include "internal.h"\r
+#include "exceptions.h"\r
#include "AccessControl.h"\r
#include "RequestMapper.h"\r
#include "SPRequest.h"\r
#include <xmltooling/util/ReloadableXMLFile.h>\r
#include <xmltooling/util/XMLHelper.h>\r
#include <xercesc/util/XMLUniDefs.hpp>\r
+#include <xercesc/util/regx/RegularExpression.hpp>\r
\r
using namespace shibsp;\r
using namespace xmltooling;\r
-using namespace log4cpp;\r
using namespace std;\r
\r
namespace shibsp {\r
pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const;\r
pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const;\r
pair<bool,int> getInt(const char* name, const char* ns=NULL) const;\r
- const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:target:config:1.0") const;\r
+ const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:2.0:native:sp:config") const;\r
\r
// Provides filter to exclude special config elements.\r
short acceptNode(const DOMNode* node) const;\r
void loadACL(const DOMElement* e, Category& log);\r
\r
map<string,Override*> m_map;\r
+ vector< pair<RegularExpression*,Override*> > m_regexps;\r
\r
private:\r
const Override* m_base;\r
return new XMLRequestMapper(e);\r
}\r
\r
- static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);\r
+ static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);\r
static const XMLCh AccessControlProvider[] = UNICODE_LITERAL_21(A,c,c,e,s,s,C,o,n,t,r,o,l,P,r,o,v,i,d,e,r);\r
- static const XMLCh htaccess[] = UNICODE_LITERAL_8(h,t,a,c,c,e,s,s);\r
static const XMLCh Host[] = UNICODE_LITERAL_4(H,o,s,t);\r
+ static const XMLCh HostRegex[] = UNICODE_LITERAL_9(H,o,s,t,R,e,g,e,x);\r
+ static const XMLCh htaccess[] = UNICODE_LITERAL_8(h,t,a,c,c,e,s,s);\r
+ static const XMLCh ignoreCase[] = UNICODE_LITERAL_10(i,g,n,o,r,e,C,a,s,e);\r
+ static const XMLCh ignoreOption[] = UNICODE_LITERAL_1(i);\r
static const XMLCh Path[] = UNICODE_LITERAL_4(P,a,t,h);\r
+ static const XMLCh PathRegex[] = UNICODE_LITERAL_9(P,a,t,h,R,e,g,e,x);\r
static const XMLCh name[] = UNICODE_LITERAL_4(n,a,m,e);\r
- static const XMLCh type[] = UNICODE_LITERAL_4(t,y,p,e);\r
+ static const XMLCh regex[] = UNICODE_LITERAL_5(r,e,g,e,x);\r
+ static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);\r
}\r
\r
void SHIBSP_API shibsp::registerRequestMappers()\r
else {\r
acl=XMLHelper::getFirstChildElement(e,AccessControlProvider);\r
if (acl) {\r
- xmltooling::auto_ptr_char type(acl->getAttributeNS(NULL,type));\r
+ auto_ptr_char type(acl->getAttributeNS(NULL,_type));\r
log.info("building AccessControl provider of type %s...",type.get());\r
m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(type.get(),acl);\r
}\r
continue;\r
}\r
m_map[dup]=o;\r
+ log.debug("Added Path mapping (%s)", dup);\r
free(dup);\r
}\r
+\r
+ if (!XMLString::equals(e->getLocalName(), PathRegex)) {\r
+ // Handle nested PathRegexs.\r
+ path = XMLHelper::getFirstChildElement(e,PathRegex);\r
+ for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,PathRegex)) {\r
+ const XMLCh* n=path->getAttributeNS(NULL,regex);\r
+ if (!n || !*n) {\r
+ log.warn("Skipping PathRegex element (%d) with empty regex attribute",i);\r
+ continue;\r
+ }\r
+\r
+ auto_ptr<Override> o(new Override(path,log,this));\r
+\r
+ const XMLCh* flag=path->getAttributeNS(NULL,ignoreCase);\r
+ try {\r
+ auto_ptr<RegularExpression> re(\r
+ new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)\r
+ );\r
+ m_regexps.push_back(make_pair(re.release(), o.release()));\r
+ }\r
+ catch (XMLException& ex) {\r
+ auto_ptr_char tmp(ex.getMessage());\r
+ log.error("caught exception while parsing PathRegex regular expression (%d): %s", i, tmp.get());\r
+ throw ConfigurationException("Invalid regular expression in PathRegex element.");\r
+ }\r
+\r
+ log.debug("Added <PathRegex> mapping (%s)", m_regexps.back().second->getString("regex").second);\r
+ }\r
+ }\r
}\r
catch (exception&) {\r
delete m_acl;\r
{\r
delete m_acl;\r
for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());\r
+ for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {\r
+ delete i->first;\r
+ delete i->second;\r
+ }\r
}\r
\r
pair<bool,bool> Override::getBool(const char* name, const char* ns) const\r
\r
const Override* Override::locate(const char* path) const\r
{\r
+ // This function is confusing because it's *not* recursive.\r
+ // The whole path is tokenized and mapped in a loop, so the\r
+ // path parameter starts with the entire request path and\r
+ // we can skip the leading slash as irrelevant.\r
+ if (*path == '/')\r
+ path++;\r
+\r
+ // Now we copy the path, chop the query string, and lower case it.\r
char* dup=strdup(path);\r
char* sep=strchr(dup,'?');\r
if (sep)\r
*sep=0;\r
for (char* pch=dup; *pch; pch++)\r
*pch=tolower(*pch);\r
- \r
+\r
+ // Default is for the current object to provide settings.\r
const Override* o=this;\r
- \r
+\r
+ // Tokenize the path by segment and try and map each segment.\r
#ifdef HAVE_STRTOK_R\r
char* pos=NULL;\r
const char* token=strtok_r(dup,"/",&pos);\r
#else\r
const char* token=strtok(dup,"/");\r
#endif\r
- while (token)\r
- {\r
+ while (token) {\r
map<string,Override*>::const_iterator i=o->m_map.find(token);\r
if (i==o->m_map.end())\r
- break;\r
+ break; // Once there's no match, we've consumed as much of the path as possible here.\r
+ // We found a match, so reset the settings pointer.\r
o=i->second;\r
+ \r
+ // We descended a step down the path, so we need to advance the original\r
+ // parameter for the regex step later.\r
+ path += strlen(token);\r
+ if (*path == '/')\r
+ path++;\r
+\r
+ // Get the next segment, if any.\r
#ifdef HAVE_STRTOK_R\r
token=strtok_r(NULL,"/",&pos);\r
#else\r
}\r
\r
free(dup);\r
+\r
+ // If there's anything left, we try for a regex match on the rest of the path.\r
+ if (*path) {\r
+ for (vector< pair<RegularExpression*,Override*> >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {\r
+ if (re->first->matches(path))\r
+ return re->second;\r
+ }\r
+ }\r
+\r
return o;\r
}\r
\r
// Load any AccessControl provider.\r
loadACL(e,log);\r
\r
+ // Loop over the HostRegex elements.\r
+ const DOMElement* host = XMLHelper::getFirstChildElement(e,HostRegex);\r
+ for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,HostRegex)) {\r
+ const XMLCh* n=host->getAttributeNS(NULL,regex);\r
+ if (!n || !*n) {\r
+ log.warn("Skipping HostRegex element (%d) with empty regex attribute",i);\r
+ continue;\r
+ }\r
+\r
+ auto_ptr<Override> o(new Override(host,log,this));\r
+\r
+ const XMLCh* flag=host->getAttributeNS(NULL,ignoreCase);\r
+ try {\r
+ auto_ptr<RegularExpression> re(\r
+ new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)\r
+ );\r
+ m_regexps.push_back(make_pair(re.release(), o.release()));\r
+ }\r
+ catch (XMLException& ex) {\r
+ auto_ptr_char tmp(ex.getMessage());\r
+ log.error("caught exception while parsing HostRegex regular expression (%d): %s", i, tmp.get());\r
+ }\r
+\r
+ log.debug("Added <HostRegex> mapping for %s", m_regexps.back().second->getString("regex").second);\r
+ }\r
+\r
// Loop over the Host elements.\r
- const DOMElement* host = XMLHelper::getFirstChildElement(e,Host);\r
+ host = XMLHelper::getFirstChildElement(e,Host);\r
for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,Host)) {\r
const XMLCh* n=host->getAttributeNS(NULL,name);\r
if (!n || !*n) {\r
i=m_extras.find(vhost);\r
if (i!=m_extras.end())\r
o=i->second;\r
+ else {\r
+ for (vector< pair<RegularExpression*,Override*> >::const_iterator re = m_regexps.begin(); !o && re != m_regexps.end(); ++re) {\r
+ if (re->first->matches(vhost))\r
+ o=re->second;\r
+ }\r
+ }\r
}\r
\r
return o ? o->locate(path) : this;\r