SSPCPP-616 - clean up concatenated string literals
[shibboleth/cpp-sp.git] / shibsp / handler / impl / TransformSessionInitiator.cpp
index 6b377ac..02e6dc4 100644 (file)
@@ -1,17 +1,21 @@
-/*
- *  Copyright 2001-2007 Internet2
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ * UCAID licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the
+ * License at
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
  */
 
 /**
 #include "util/SPConstants.h"
 
 #ifndef SHIBSP_LITE
+# include "metadata/MetadataProviderCriteria.h"
 # include <saml/saml2/metadata/Metadata.h>
 #endif
+#include <boost/tuple/tuple.hpp>
 #include <xmltooling/XMLToolingConfig.h>
-#include <xmltooling/signature/KeyInfo.h>
 #include <xmltooling/util/URLEncoder.h>
+#include <xercesc/util/XMLUniDefs.hpp>
+#include <xercesc/util/regx/RegularExpression.hpp>
 
 using namespace shibsp;
 using namespace opensaml::saml2md;
 using namespace opensaml;
 using namespace xmltooling;
+using namespace boost;
 using namespace std;
 
 namespace shibsp {
@@ -53,36 +61,61 @@ namespace shibsp {
     class SHIBSP_DLLLOCAL TransformSINodeFilter : public DOMNodeFilter
     {
     public:
-        short acceptNode(const DOMNode* node) const {
-            if (XMLString::equals(node->getLocalName(), xmlsignature::Transform::LOCAL_NAME))
-                return FILTER_REJECT;
-            return FILTER_ACCEPT;
+#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
+        short
+#else
+        FilterAction
+#endif
+        acceptNode(const DOMNode* node) const {
+            return FILTER_REJECT;
         }
     };
 
     static SHIBSP_DLLLOCAL TransformSINodeFilter g_TSINFilter;
 
+#ifndef SHIBSP_LITE
+    static const XMLCh force[] =        UNICODE_LITERAL_5(f,o,r,c,e);
+    static const XMLCh match[] =        UNICODE_LITERAL_5(m,a,t,c,h);
+    static const XMLCh Regex[] =        UNICODE_LITERAL_5(R,e,g,e,x);
+    static const XMLCh Subst[] =        UNICODE_LITERAL_5(S,u,b,s,t);
+#endif
+
     class SHIBSP_DLLLOCAL TransformSessionInitiator : public SessionInitiator, public AbstractHandler, public RemotedHandler
     {
     public:
         TransformSessionInitiator(const DOMElement* e, const char* appId)
-                : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator.Transform"), &g_TSINFilter), m_appId(appId) {
+                : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT ".SessionInitiator.Transform"), &g_TSINFilter), m_appId(appId) {
             // If Location isn't set, defer address registration until the setParent call.
             pair<bool,const char*> loc = getString("Location");
             if (loc.first) {
                 string address = m_appId + loc.second + "::run::TransformSI";
                 setAddress(address.c_str());
             }
+            m_supportedOptions.insert("isPassive");
 
 #ifndef SHIBSP_LITE
             if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
-                e = XMLHelper::getFirstChildElement(e, xmlsignature::Transform::LOCAL_NAME);
+                m_alwaysRun = getBool("alwaysRun").second;
+                e = XMLHelper::getFirstChildElement(e);
                 while (e) {
                     if (e->hasChildNodes()) {
-                        auto_ptr_char temp(e->getFirstChild()->getNodeValue());
-                        m_transforms.push_back(temp.get());
+                        bool flag = XMLHelper::getAttrBool(e, false, force);
+                        if (XMLString::equals(e->getLocalName(), Subst)) {
+                            auto_ptr_char temp(e->getTextContent());
+                            if (temp.get() && *temp.get())
+                                m_subst.push_back(pair<bool,string>(flag, temp.get()));
+                        }
+                        else if (XMLString::equals(e->getLocalName(), Regex) && e->hasAttributeNS(nullptr, match)) {
+                            auto_ptr_char m(e->getAttributeNS(nullptr, match));
+                            auto_ptr_char repl(e->getTextContent());
+                            if (m.get() && *m.get() && repl.get() && *repl.get())
+                                m_regex.push_back(tuple<bool,string,string>(flag, m.get(), repl.get()));
+                        }
+                        else {
+                            m_log.warn("Unknown element found in Transform SessionInitiator configuration, check for errors.");
+                        }
                     }
-                    e = XMLHelper::getNextSiblingElement(e, xmlsignature::Transform::LOCAL_NAME);
+                    e = XMLHelper::getNextSiblingElement(e);
                 }
             }
 #endif
@@ -98,7 +131,9 @@ namespace shibsp {
         void doRequest(const Application& application, string& entityID) const;
         string m_appId;
 #ifndef SHIBSP_LITE
-        vector<string> m_transforms;
+        bool m_alwaysRun;
+        vector< pair<bool, string> > m_subst;
+        vector< tuple<bool,string,string> > m_regex;
 #endif
     };
 
@@ -129,11 +164,10 @@ void TransformSessionInitiator::setParent(const PropertySet* parent)
 pair<bool,long> TransformSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const
 {
     // We have to have a candidate name to function.
-    if (entityID.empty())
-        return make_pair(false,0L);
+    if (entityID.empty() || !checkCompatibility(request, isHandler))
+        return make_pair(false, 0L);
 
-    string target;
-    const Application& app=request.getApplication();
+    const Application& app = request.getApplication();
 
     m_log.debug("attempting to transform input (%s) into a valid entityID", entityID.c_str());
 
@@ -152,14 +186,14 @@ pair<bool,long> TransformSessionInitiator::run(SPRequest& request, string& entit
             entityID = out.string();
     }
     
-    return make_pair(false,0L);
+    return make_pair(false, 0L);
 }
 
 void TransformSessionInitiator::receive(DDF& in, ostream& out)
 {
     // Find application.
-    const char* aid=in["application_id"].string();
-    const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL;
+    const char* aid = in["application_id"].string();
+    const Application* app = aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : nullptr;
     if (!app) {
         // Something's horribly wrong.
         m_log.error("couldn't find application (%s) to generate AuthnRequest", aid ? aid : "(missing)");
@@ -172,7 +206,7 @@ void TransformSessionInitiator::receive(DDF& in, ostream& out)
 
     string copy(entityID);
     doRequest(*app, copy);
-    DDF ret = DDF(NULL).string(copy.c_str());
+    DDF ret = DDF(nullptr).string(copy.c_str());
     DDFJanitor jout(ret);
     out << ret;
 }
@@ -180,34 +214,81 @@ void TransformSessionInitiator::receive(DDF& in, ostream& out)
 void TransformSessionInitiator::doRequest(const Application& application, string& entityID) const
 {
 #ifndef SHIBSP_LITE
-    MetadataProvider* m=application.getMetadataProvider();
+    MetadataProvider* m = application.getMetadataProvider();
     Locker locker(m);
 
-    // First check the original value, it might be valid already.
-    MetadataProvider::Criteria mc(entityID.c_str(), &IDPSSODescriptor::ELEMENT_QNAME);
-    pair<const EntityDescriptor*,const RoleDescriptor*> entity = m->getEntityDescriptor(mc);
-    if (entity.first)
-        return;
+    MetadataProviderCriteria mc(application, entityID.c_str(), &IDPSSODescriptor::ELEMENT_QNAME);
+    pair<const EntityDescriptor*,const RoleDescriptor*> entity;
+    if (!m_alwaysRun) {
+        // First check the original value, it might be valid already.
+        entity = m->getEntityDescriptor(mc);
+        if (entity.first)
+            return;
+    }
 
-    // Guess not, try each transform.
+    m_log.debug("attempting transform of (%s)", entityID.c_str());
+
+    // Guess not, try each subst.
     string transform;
-    for (vector<string>::const_iterator t = m_transforms.begin(); t != m_transforms.end(); ++t) {
-        transform = *t;
-        string::size_type pos = transform.find("$entityID");
+    for (vector< pair<bool,string> >::const_iterator t = m_subst.begin(); t != m_subst.end(); ++t) {
+        string::size_type pos = t->second.find("$entityID");
         if (pos == string::npos)
             continue;
+        transform = t->second;
         transform.replace(pos, 9, entityID);
+        if (t->first) {
+            m_log.info("forcibly transformed entityID from (%s) to (%s)", entityID.c_str(), transform.c_str());
+            entityID = transform;
+        }
+
         m_log.debug("attempting lookup with entityID (%s)", transform.c_str());
     
         mc.entityID_ascii = transform.c_str();
         entity = m->getEntityDescriptor(mc);
         if (entity.first) {
             m_log.info("transformed entityID from (%s) to (%s)", entityID.c_str(), transform.c_str());
-            entityID = transform;
+            if (!t->first)
+                entityID = transform;
             return;
         }
     }
 
+    // Now try regexs.
+    for (vector< tuple<bool,string,string> >::const_iterator r = m_regex.begin(); r != m_regex.end(); ++r) {
+        try {
+            RegularExpression exp(r->get<1>().c_str());
+            XMLCh* temp = exp.replace(entityID.c_str(), r->get<2>().c_str());
+            if (temp) {
+                auto_ptr_char narrow(temp);
+                XMLString::release(&temp);
+
+                // For some reason it returns the match string if it doesn't match the expression.
+                if (entityID == narrow.get())
+                    continue;
+
+                if (r->get<0>()) {
+                    m_log.info("forcibly transformed entityID from (%s) to (%s)", entityID.c_str(), narrow.get());
+                    entityID = narrow.get();
+                }
+
+                m_log.debug("attempting lookup with entityID (%s)", narrow.get());
+
+                mc.entityID_ascii = narrow.get();
+                entity = m->getEntityDescriptor(mc);
+                if (entity.first) {
+                    m_log.info("transformed entityID from (%s) to (%s)", entityID.c_str(), narrow.get());
+                    if (!r->get<0>())
+                        entityID = narrow.get();
+                    return;
+                }
+            }
+        }
+        catch (XMLException& ex) {
+            auto_ptr_char msg(ex.getMessage());
+            m_log.error("caught error applying regular expression: %s", msg.get());
+        }
+    }
+
     m_log.warn("unable to find a valid entityID based on the supplied input");
 #endif
 }