Use shibboleth-sp as package name for compatibility.
[shibboleth/cpp-sp.git] / shibsp / AbstractSPRequest.cpp
index bfa6125..fe0de14 100644 (file)
-/*
- *  Copyright 2001-2006 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.
+ *
+ * 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
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ * 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.
+ * 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.
  */
 
 /**
  * AbstractSPRequest.cpp
- * 
- * Abstract base for SPRequest implementations  
+ *
+ * Abstract base for SPRequest implementations.
  */
 
 #include "internal.h"
+#include "exceptions.h"
 #include "AbstractSPRequest.h"
 #include "Application.h"
+#include "GSSRequest.h"
+#include "ServiceProvider.h"
+#include "SessionCache.h"
 #include "util/CGIParser.h"
 
-#include <log4cpp/Category.hh>
+#include <boost/lexical_cast.hpp>
 
 using namespace shibsp;
+using namespace opensaml;
 using namespace xmltooling;
-using namespace log4cpp;
 using namespace std;
 
-AbstractSPRequest::AbstractSPRequest(const Application* app)
-    : m_app(app), m_log(&Category::getInstance(SHIBSP_LOGCAT)), m_parser(NULL)
+SPRequest::SPRequest()
+{
+}
+
+SPRequest::~SPRequest()
 {
-    if (m_app)
-        return;
+}
+
+string SPRequest::getSecureHeader(const char* name) const
+{
+    return getHeader(name);
+}
+
+void SPRequest::setAuthType(const char* authtype)
+{
+}
+
+#ifdef SHIBSP_HAVE_GSSAPI
+GSSRequest::GSSRequest()
+{
+}
+
+GSSRequest::~GSSRequest()
+{
+}
+
+gss_name_t GSSRequest::getGSSName() const
+{
+    return GSS_C_NO_NAME;
+}
+#endif
+
+AbstractSPRequest::AbstractSPRequest(const char* category)
+    : m_sp(SPConfig::getConfig().getServiceProvider()),
+        m_mapper(nullptr), m_app(nullptr), m_sessionTried(false), m_session(nullptr),
+        m_log(&Category::getInstance(category))
+{
+    m_sp->lock();
 }
 
 AbstractSPRequest::~AbstractSPRequest()
 {
-    delete m_parser;
+    if (m_session)
+        m_session->unlock();
+    if (m_mapper)
+        m_mapper->unlock();
+    if (m_sp)
+        m_sp->unlock();
 }
 
-const char* AbstractSPRequest::getParameter(const char* name) const
+const ServiceProvider& AbstractSPRequest::getServiceProvider() const
 {
-    if (!m_parser)
-        m_parser=new CGIParser(*this);
-    
-    pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
-    return (bounds.first==bounds.second) ? NULL : bounds.first->second;
+    return *m_sp;
 }
 
-vector<const char*>::size_type AbstractSPRequest::getParameters(const char* name, vector<const char*>& values) const
+RequestMapper::Settings AbstractSPRequest::getRequestSettings() const
 {
-    if (!m_parser)
-        m_parser=new CGIParser(*this);
+    if (!m_mapper) {
+        // Map request to application and content settings.
+        m_mapper = m_sp->getRequestMapper();
+        m_mapper->lock();
+        m_settings = m_mapper->getSettings(*this);
 
-    pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
-    while (bounds.first!=bounds.second) {
-        values.push_back(bounds.first->second);
-        ++bounds.first;
+        if (reinterpret_cast<Category*>(m_log)->isDebugEnabled()) {
+            reinterpret_cast<Category*>(m_log)->debug(
+                "mapped %s to %s", getRequestURL(), m_settings.first->getString("applicationId").second
+                );
+        }
     }
-    return values.size();
+    return m_settings;
 }
 
-const char* AbstractSPRequest::getCookie(const char* name) const
+const Application& AbstractSPRequest::getApplication() const
 {
-    if (m_cookieMap.empty()) {
-        string cookies=getHeader("Cookie");
+    if (!m_app) {
+        // Now find the application from the URL settings
+        m_app = m_sp->getApplication(getRequestSettings().first->getString("applicationId").second);
+        if (!m_app)
+            throw ConfigurationException("Unable to map non-default applicationId to an ApplicationOverride, check configuration.");
+    }
+    return *m_app;
+}
 
-        string::size_type pos=0,cname,namelen,val,vallen;
-        while (pos !=string::npos && pos < cookies.length()) {
-            while (isspace(cookies[pos])) pos++;
-            cname=pos;
-            pos=cookies.find_first_of("=",pos);
-            if (pos == string::npos)
-                break;
-            namelen=pos-cname;
-            pos++;
-            if (pos==cookies.length())
+Session* AbstractSPRequest::getSession(bool checkTimeout, bool ignoreAddress, bool cache)
+{
+    // Only attempt this once.
+    if (cache && m_sessionTried)
+        return m_session;
+    else if (cache)
+        m_sessionTried = true;
+
+    // Need address checking and timeout settings.
+    time_t timeout = 3600;
+    if (checkTimeout || !ignoreAddress) {
+        const PropertySet* props = getApplication().getPropertySet("Sessions");
+        if (props) {
+            if (checkTimeout) {
+                pair<bool,unsigned int> p = props->getUnsignedInt("timeout");
+                if (p.first)
+                    timeout = p.second;
+            }
+            pair<bool,bool> pcheck = props->getBool("consistentAddress");
+            if (pcheck.first)
+                ignoreAddress = !pcheck.second;
+        }
+    }
+
+    // The cache will either silently pass a session or nullptr back, or throw an exception out.
+    Session* session = getServiceProvider().getSessionCache()->find(
+        getApplication(), *this, (ignoreAddress ? nullptr : getRemoteAddr().c_str()), (checkTimeout ? &timeout : nullptr)
+        );
+    if (cache)
+        m_session = session;
+    return session;
+}
+
+static char _x2c(const char *what)
+{
+    register char digit;
+
+    digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
+    digit *= 16;
+    digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
+    return(digit);
+}
+
+void AbstractSPRequest::setRequestURI(const char* uri)
+{
+    // Fix for bug 574, secadv 20061002
+    // Unescape URI up to query string delimiter by looking for %XX escapes.
+    // Adapted from Apache's util.c, ap_unescape_url function.
+    if (uri) {
+        while (*uri) {
+            if (*uri == '?') {
+                m_uri += uri;
                 break;
-            val=pos;
-            pos=cookies.find_first_of(";",pos);
-            if (pos != string::npos) {
-                vallen=pos-val;
-                pos++;
-                m_cookieMap.insert(make_pair(cookies.substr(cname,namelen),cookies.substr(val,vallen)));
             }
-            else
-                m_cookieMap.insert(make_pair(cookies.substr(cname,namelen),cookies.substr(val)));
+            else if (*uri != '%') {
+                m_uri += *uri;
+            }
+            else {
+                ++uri;
+                if (!isxdigit(*uri) || !isxdigit(*(uri+1)))
+                    throw ConfigurationException("Bad request, contained unsupported encoded characters.");
+                m_uri += _x2c(uri);
+                ++uri;
+            }
+            ++uri;
         }
     }
-    map<string,string>::const_iterator lookup=m_cookieMap.find(name);
-    return (lookup==m_cookieMap.end()) ? NULL : lookup->second.c_str();
+}
+
+const char* AbstractSPRequest::getRequestURI() const
+{
+    return m_uri.c_str();
+}
+
+const char* AbstractSPRequest::getRequestURL() const
+{
+    if (m_url.empty()) {
+        // Compute the full target URL
+        int port = getPort();
+        const char* scheme = getScheme();
+        m_url = string(scheme) + "://" + getHostname();
+        if (!isDefaultPort())
+            m_url += ":" + boost::lexical_cast<string>(port);
+        m_url += m_uri;
+    }
+    return m_url.c_str();
+}
+
+string AbstractSPRequest::getRemoteAddr() const
+{
+    pair<bool,const char*> addr = getRequestSettings().first->getString("REMOTE_ADDR");
+    return addr.first ? getHeader(addr.second) : "";
+}
+
+const char* AbstractSPRequest::getParameter(const char* name) const
+{
+    if (!m_parser.get())
+        m_parser.reset(new CGIParser(*this));
+
+    pair<CGIParser::walker,CGIParser::walker> bounds = m_parser->getParameters(name);
+    return (bounds.first==bounds.second) ? nullptr : bounds.first->second;
+}
+
+vector<const char*>::size_type AbstractSPRequest::getParameters(const char* name, vector<const char*>& values) const
+{
+    if (!m_parser.get())
+        m_parser.reset(new CGIParser(*this));
+
+    pair<CGIParser::walker,CGIParser::walker> bounds = m_parser->getParameters(name);
+    while (bounds.first != bounds.second) {
+        values.push_back(bounds.first->second);
+        ++bounds.first;
+    }
+    return values.size();
 }
 
 const char* AbstractSPRequest::getHandlerURL(const char* resource) const
 {
-    if (!m_handlerURL.empty() && resource && !strcmp(getRequestURL(),resource))
+    if (!resource)
+        resource = getRequestURL();
+
+    if (!m_handlerURL.empty() && resource && !strcmp(getRequestURL(), resource))
         return m_handlerURL.c_str();
-        
+
+    // Check for relative URL.
+    string stackresource;
+    if (resource && *resource == '/') {
+        // Compute a URL to the root of the site and point resource at constructed string.
+        int port = getPort();
+        const char* scheme = getScheme();
+        stackresource = string(scheme) + "://" + getHostname();
+        if (!isDefaultPort())
+            stackresource += ":" + boost::lexical_cast<string>(port);
+        stackresource += resource;
+        resource = stackresource.c_str();
+    }
+
 #ifdef HAVE_STRCASECMP
     if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))
 #else
@@ -109,24 +267,27 @@ const char* AbstractSPRequest::getHandlerURL(const char* resource) const
 #endif
         throw ConfigurationException("Target resource was not an absolute URL.");
 
-    bool ssl_only=false;
-    const char* handler=NULL;
-    const PropertySet* props=m_app->getPropertySet("Sessions");
+    bool ssl_only = true;
+    const char* handler = nullptr;
+    const PropertySet* props = getApplication().getPropertySet("Sessions");
     if (props) {
-        pair<bool,bool> p=props->getBool("handlerSSL");
+        pair<bool,bool> p = props->getBool("handlerSSL");
         if (p.first)
-            ssl_only=p.second;
-        pair<bool,const char*> p2=props->getString("handlerURL");
+            ssl_only = p.second;
+        pair<bool,const char*> p2 = props->getString("handlerURL");
         if (p2.first)
-            handler=p2.second;
+            handler = p2.second;
     }
-    
-    // Should never happen...
-    if (!handler || (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)))
+
+    if (!handler) {
+        handler = "/Shibboleth.sso";
+    }
+    else if (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)) {
         throw ConfigurationException(
-            "Invalid handlerURL property ($1) in Application ($2)",
+            "Invalid handlerURL property ($1) in <Sessions> element for Application ($2)",
             params(2, handler ? handler : "null", m_app->getId())
             );
+    }
 
     // The "handlerURL" property can be in one of three formats:
     //
@@ -141,7 +302,7 @@ const char* AbstractSPRequest::getHandlerURL(const char* resource) const
     //
     // note: if ssl_only is true, make sure the protocol is https
 
-    const char* path = NULL;
+    const char* path = nullptr;
 
     // Decide whether to use the handler or the resource for the "protocol"
     const char* prot;
@@ -154,9 +315,9 @@ const char* AbstractSPRequest::getHandlerURL(const char* resource) const
     }
 
     // break apart the "protocol" string into protocol, host, and "the rest"
-    const char* colon=strchr(prot,':');
+    const char* colon = strchr(prot, ':');
     colon += 3;
-    const char* slash=strchr(colon,'/');
+    const char* slash = strchr(colon, '/');
     if (!path)
         path = slash;
 
@@ -184,10 +345,10 @@ const char* AbstractSPRequest::getHandlerURL(const char* resource) const
 void AbstractSPRequest::log(SPLogLevel level, const std::string& msg) const
 {
     reinterpret_cast<Category*>(m_log)->log(
-        (level == SPDebug ? log4cpp::Priority::DEBUG :
-        (level == SPInfo ? log4cpp::Priority::INFO :
-        (level == SPWarn ? log4cpp::Priority::WARN :
-        (level == SPError ? log4cpp::Priority::ERROR : log4cpp::Priority::CRIT)))),
+        (level == SPDebug ? Priority::DEBUG :
+        (level == SPInfo ? Priority::INFO :
+        (level == SPWarn ? Priority::WARN :
+        (level == SPError ? Priority::ERROR : Priority::CRIT)))),
         msg
         );
 }
@@ -195,9 +356,9 @@ void AbstractSPRequest::log(SPLogLevel level, const std::string& msg) const
 bool AbstractSPRequest::isPriorityEnabled(SPLogLevel level) const
 {
     return reinterpret_cast<Category*>(m_log)->isPriorityEnabled(
-        (level == SPDebug ? log4cpp::Priority::DEBUG :
-        (level == SPInfo ? log4cpp::Priority::INFO :
-        (level == SPWarn ? log4cpp::Priority::WARN :
-        (level == SPError ? log4cpp::Priority::ERROR : log4cpp::Priority::CRIT))))
+        (level == SPDebug ? Priority::DEBUG :
+        (level == SPInfo ? Priority::INFO :
+        (level == SPWarn ? Priority::WARN :
+        (level == SPError ? Priority::ERROR : Priority::CRIT))))
         );
 }