Add "safe" query string support to mapper.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Fri, 31 Aug 2007 21:06:52 +0000 (21:06 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Fri, 31 Aug 2007 21:06:52 +0000 (21:06 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2443 cb58f699-b61c-0410-a6fe-9272a202ed29

schemas/shibboleth-2.0-native-sp-config.xsd
shibsp/handler/impl/AssertionConsumerService.cpp
shibsp/impl/XMLRequestMapper.cpp

index 9341fc6..6c497a2 100644 (file)
                        <choice minOccurs="0" maxOccurs="unbounded">\r
                                <element ref="conf:Path"/>\r
                                <element ref="conf:PathRegex"/>\r
+                               <element ref="conf:Query"/>\r
                        </choice>\r
                </sequence>\r
                <attribute name="scheme">\r
                                <choice minOccurs="0" maxOccurs="unbounded">\r
                                        <element ref="conf:Path"/>\r
                                        <element ref="conf:PathRegex"/>\r
+                                       <element ref="conf:Query"/>\r
                                </choice>\r
                        </sequence>\r
                        <attribute name="regex" type="conf:string" use="required"/>\r
                        <choice minOccurs="0" maxOccurs="unbounded">\r
                                <element ref="conf:Path"/>\r
                                <element ref="conf:PathRegex"/>\r
+                               <element ref="conf:Query"/>\r
                        </choice>\r
                </sequence>\r
                        <attribute name="name" type="conf:string" use="required"/>\r
                                        <element ref="conf:AccessControl"/>\r
                                        <element ref="conf:AccessControlProvider"/>\r
                                </choice>\r
+                               <element ref="conf:Query" minOccurs="0" maxOccurs="unbounded"/>\r
                        </sequence>\r
                        <attribute name="regex" type="conf:string" use="required"/>\r
                        <attribute name="ignoreCase" type="boolean" default="true"/>\r
                </complexType>\r
        </element>\r
        \r
+       <element name="Query">\r
+               <complexType>\r
+                       <sequence>\r
+                               <choice minOccurs="0">\r
+                                       <element ref="conf:htaccess"/>\r
+                                       <element ref="conf:AccessControl"/>\r
+                                       <element ref="conf:AccessControlProvider"/>\r
+                               </choice>\r
+                               <element ref="conf:Query" minOccurs="0" maxOccurs="unbounded"/>\r
+                       </sequence>\r
+                       <attribute name="name" type="conf:string" use="required"/>\r
+                       <attribute name="regex" type="conf:string"/>\r
+                       <attributeGroup ref="conf:ContentSettings"/>\r
+               </complexType>\r
+       </element>\r
+       \r
        <element name="Applications">\r
                <annotation>\r
                        <documentation>Container for global settings and application-specific overrides</documentation>\r
index f68ff6d..bef7527 100644 (file)
@@ -288,22 +288,22 @@ ResolutionContext* AssertionConsumerService::resolveAttributes(
     AttributeExtractor* extractor = application.getAttributeExtractor();
     if (extractor) {
         Locker extlocker(extractor);
-        if (entity) {\r
-            pair<bool,const char*> prefix = application.getString("metadataAttributePrefix");\r
-            if (prefix.first) {\r
-                m_log.debug("extracting metadata-derived attributes...");\r
-                try {\r
-                    extractor->extractAttributes(application, issuer, *entity, resolvedAttributes);\r
-                    for (vector<Attribute*>::iterator a = resolvedAttributes.begin(); a != resolvedAttributes.end(); ++a) {\r
-                        vector<string>& ids = (*a)->getAliases();\r
-                        for (vector<string>::iterator id = ids.begin(); id != ids.end(); ++id)\r
-                            *id = prefix.second + *id;\r
-                    }\r
-                }\r
-                catch (exception& ex) {\r
-                    m_log.error("caught exception extracting attributes: %s", ex.what());\r
-                }\r
-            }\r
+        if (entity) {
+            pair<bool,const char*> prefix = application.getString("metadataAttributePrefix");
+            if (prefix.first) {
+                m_log.debug("extracting metadata-derived attributes...");
+                try {
+                    extractor->extractAttributes(application, issuer, *entity, resolvedAttributes);
+                    for (vector<Attribute*>::iterator a = resolvedAttributes.begin(); a != resolvedAttributes.end(); ++a) {
+                        vector<string>& ids = (*a)->getAliases();
+                        for (vector<string>::iterator id = ids.begin(); id != ids.end(); ++id)
+                            *id = prefix.second + *id;
+                    }
+                }
+                catch (exception& ex) {
+                    m_log.error("caught exception extracting attributes: %s", ex.what());
+                }
+            }
         }
         m_log.debug("extracting pushed attributes...");
         if (v1nameid) {
index 9119623..22e2904 100644 (file)
@@ -70,9 +70,11 @@ namespace shibsp {
         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
+        short acceptNode(const DOMNode* node) const {\r
+            return FILTER_REJECT;\r
+        }\r
 \r
-        const Override* locate(const char* path) const;\r
+        const Override* locate(const SPRequest& request) const;\r
         AccessControl* getAC() const { return (m_acl ? m_acl : (m_base ? m_base->getAC() : NULL)); }\r
         \r
     protected:\r
@@ -80,6 +82,7 @@ namespace shibsp {
         \r
         map<string,Override*> m_map;\r
         vector< pair<RegularExpression*,Override*> > m_regexps;\r
+        vector< pair< pair<string,RegularExpression*>,Override*> > m_queries;\r
     \r
     private:\r
         const Override* m_base;\r
@@ -100,7 +103,7 @@ namespace shibsp {
             m_document = doc;\r
         }\r
     \r
-        const Override* findOverride(const char* vhost, const char* path) const;\r
+        const Override* findOverride(const char* vhost, const SPRequest& request) const;\r
 \r
     private:    \r
         map<string,Override*> m_extras;\r
@@ -150,6 +153,7 @@ namespace shibsp {
     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 Query[] =                    UNICODE_LITERAL_5(Q,u,e,r,y);\r
     static const XMLCh name[] =                     UNICODE_LITERAL_4(n,a,m,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
@@ -159,25 +163,7 @@ void SHIBSP_API shibsp::registerRequestMappers()
 {\r
     SPConfig& conf=SPConfig::getConfig();\r
     conf.RequestMapperManager.registerFactory(XML_REQUEST_MAPPER, XMLRequestMapperFactory);\r
-    conf.RequestMapperManager.registerFactory("edu.internet2.middleware.shibboleth.sp.provider.XMLRequestMapProvider", XMLRequestMapperFactory);\r
-    conf.RequestMapperManager.registerFactory("edu.internet2.middleware.shibboleth.target.provider.XMLRequestMap", XMLRequestMapperFactory);\r
     conf.RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, XMLRequestMapperFactory);\r
-    conf.RequestMapperManager.registerFactory("edu.internet2.middleware.shibboleth.sp.provider.NativeRequestMapProvider", XMLRequestMapperFactory);\r
-}\r
-\r
-short Override::acceptNode(const DOMNode* node) const\r
-{\r
-    if (!XMLString::equals(node->getNamespaceURI(),shibspconstants::SHIB2SPCONFIG_NS))\r
-        return FILTER_ACCEPT;\r
-    const XMLCh* name=node->getLocalName();\r
-    if (XMLString::equals(name,Host) ||\r
-        XMLString::equals(name,Path) ||\r
-        XMLString::equals(name,_AccessControl) ||\r
-        XMLString::equals(name,htaccess) ||\r
-        XMLString::equals(name,AccessControlProvider))\r
-        return FILTER_REJECT;\r
-\r
-    return FILTER_ACCEPT;\r
 }\r
 \r
 void Override::loadACL(const DOMElement* e, Category& log)\r
@@ -276,13 +262,13 @@ Override::Override(const DOMElement* e, Category& log, const Override* base) : m
             for (char* pch=dup; *pch; pch++)\r
                 *pch=tolower(*pch);\r
             if (m_map.count(dup)) {\r
-                log.warn("Skipping duplicate Path element (%s)",dup);\r
+                log.warn("skipping duplicate Path element (%s)",dup);\r
                 free(dup);\r
                 delete o;\r
                 continue;\r
             }\r
             m_map[dup]=o;\r
-            log.debug("Added Path mapping (%s)", dup);\r
+            log.debug("added Path mapping (%s)", dup);\r
             free(dup);\r
         }\r
 \r
@@ -292,7 +278,7 @@ Override::Override(const DOMElement* e, Category& log, const Override* base) : m
             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
+                    log.warn("skipping PathRegex element (%d) with empty regex attribute",i);\r
                     continue;\r
                 }\r
 \r
@@ -311,8 +297,36 @@ Override::Override(const DOMElement* e, Category& log, const Override* base) : m
                     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
+                if (log.isDebugEnabled())\r
+                    log.debug("added <PathRegex> mapping (%s)", m_regexps.back().second->getString("regex").second);\r
+            }\r
+        }\r
+\r
+        // Handle nested Querys.\r
+        path = XMLHelper::getFirstChildElement(e,Query);\r
+        for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Query)) {\r
+            const XMLCh* n=path->getAttributeNS(NULL,name);\r
+            if (!n || !*n) {\r
+                log.warn("skipping Query element (%d) with empty name attribute",i);\r
+                continue;\r
+            }\r
+            auto_ptr_char ntemp(n);\r
+            const XMLCh* v=path->getAttributeNS(NULL,regex);\r
+\r
+            auto_ptr<Override> o(new Override(path,log,this));\r
+            try {\r
+                RegularExpression* re = NULL;\r
+                if (v && *v)\r
+                    re = new RegularExpression(v);\r
+                m_queries.push_back(make_pair(make_pair(ntemp.get(),re), o.release()));\r
             }\r
+            catch (XMLException& ex) {\r
+                auto_ptr_char tmp(ex.getMessage());\r
+                log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get());\r
+                throw ConfigurationException("Invalid regular expression in Query element.");\r
+            }\r
+            \r
+            log.debug("added <Query> mapping (%s)", ntemp.get());\r
         }\r
     }\r
     catch (exception&) {\r
@@ -330,6 +344,10 @@ Override::~Override()
         delete i->first;\r
         delete i->second;\r
     }\r
+    for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {\r
+        delete j->first.second;\r
+        delete j->second;\r
+    }\r
 }\r
 \r
 pair<bool,bool> Override::getBool(const char* name, const char* ns) const\r
@@ -380,12 +398,13 @@ const PropertySet* Override::getPropertySet(const char* name, const char* ns) co
     return m_base->getPropertySet(name,ns);\r
 }\r
 \r
-const Override* Override::locate(const char* path) const\r
+const Override* Override::locate(const SPRequest& request) 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
+    const char* path = request.getRequestURI();\r
     if (*path == '/')\r
         path++;\r
 \r
@@ -430,14 +449,45 @@ const Override* Override::locate(const char* path) const
 \r
     free(dup);\r
 \r
-    // If there's anything left, we try for a regex match on the rest of the path.\r
+    // If there's anything left, we try for a regex match on the rest of the path minus the query string.\r
     if (*path) {\r
+        string path2(path);\r
+        path2 = path2.substr(0,path2.find('?'));\r
+\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
+            if (re->first->matches(path2.c_str())) {\r
+                o = re->second;\r
+                break;\r
+            }\r
         }\r
     }\r
 \r
+    // Finally, check for query string matches. This is another "unrolled" recursive descent in a loop.\r
+    bool descended;\r
+    do {\r
+        descended = false;\r
+        for (vector< pair< pair<string,RegularExpression*>,Override*> >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {\r
+            vector<const char*> vals;\r
+            if (request.getParameters(q->first.first.c_str(), vals)) {\r
+                if (q->first.second) {\r
+                    // We have to match one of the values.\r
+                    for (vector<const char*>::const_iterator v = vals.begin(); v != vals.end(); ++v) {\r
+                        if (q->first.second->matches(*v)) {\r
+                            o = q->second;\r
+                            descended = true;\r
+                            break;\r
+                        }\r
+                    }\r
+                }\r
+                else {\r
+                    // The simple presence of the parameter is sufficient to match.\r
+                    o = q->second;\r
+                    descended = true;\r
+                }\r
+            }\r
+        }\r
+    } while (descended);\r
+\r
     return o;\r
 }\r
 \r
@@ -593,7 +643,7 @@ XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) :
     }\r
 }\r
 \r
-const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const char* path) const\r
+const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const SPRequest& request) const\r
 {\r
     const Override* o=NULL;\r
     map<string,Override*>::const_iterator i=m_map.find(vhost);\r
@@ -611,7 +661,7 @@ const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const char
         }\r
     }\r
     \r
-    return o ? o->locate(path) : this;\r
+    return o ? o->locate(request) : this;\r
 }\r
 \r
 pair<bool,DOMElement*> XMLRequestMapper::load()\r
@@ -638,7 +688,7 @@ RequestMapper::Settings XMLRequestMapper::getSettings(const SPRequest& request)
     ostringstream vhost;\r
     vhost << request.getScheme() << "://" << request.getHostname() << ':' << request.getPort();\r
 \r
-    const Override* o=m_impl->findOverride(vhost.str().c_str(), request.getRequestURI());\r
+    const Override* o=m_impl->findOverride(vhost.str().c_str(), request);\r
 \r
     if (m_log.isDebugEnabled()) {\r
 #ifdef _DEBUG\r