2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
22 * AbstractSPRequest.cpp
24 * Abstract base for SPRequest implementations.
28 #include "exceptions.h"
29 #include "AbstractSPRequest.h"
30 #include "Application.h"
31 #include "GSSRequest.h"
32 #include "ServiceProvider.h"
33 #include "SessionCache.h"
34 #include "util/CGIParser.h"
36 #include <boost/lexical_cast.hpp>
38 using namespace shibsp;
39 using namespace opensaml;
40 using namespace xmltooling;
43 SPRequest::SPRequest()
47 SPRequest::~SPRequest()
51 string SPRequest::getSecureHeader(const char* name) const
53 return getHeader(name);
56 void SPRequest::setAuthType(const char* authtype)
60 #ifdef SHIBSP_HAVE_GSSAPI
61 GSSRequest::GSSRequest()
65 GSSRequest::~GSSRequest()
69 gss_name_t GSSRequest::getGSSName() const
75 AbstractSPRequest::AbstractSPRequest(const char* category)
76 : m_sp(SPConfig::getConfig().getServiceProvider()),
77 m_mapper(nullptr), m_app(nullptr), m_sessionTried(false), m_session(nullptr),
78 m_log(&Category::getInstance(category))
83 AbstractSPRequest::~AbstractSPRequest()
93 const ServiceProvider& AbstractSPRequest::getServiceProvider() const
98 RequestMapper::Settings AbstractSPRequest::getRequestSettings() const
101 // Map request to application and content settings.
102 m_mapper = m_sp->getRequestMapper();
104 m_settings = m_mapper->getSettings(*this);
106 if (reinterpret_cast<Category*>(m_log)->isDebugEnabled()) {
107 reinterpret_cast<Category*>(m_log)->debug(
108 "mapped %s to %s", getRequestURL(), m_settings.first->getString("applicationId").second
115 const Application& AbstractSPRequest::getApplication() const
118 // Now find the application from the URL settings
119 m_app = m_sp->getApplication(getRequestSettings().first->getString("applicationId").second);
121 throw ConfigurationException("Unable to map non-default applicationId to an ApplicationOverride, check configuration.");
126 Session* AbstractSPRequest::getSession(bool checkTimeout, bool ignoreAddress, bool cache)
128 // Only attempt this once.
129 if (cache && m_sessionTried)
132 m_sessionTried = true;
134 // Need address checking and timeout settings.
135 time_t timeout = 3600;
136 if (checkTimeout || !ignoreAddress) {
137 const PropertySet* props = getApplication().getPropertySet("Sessions");
140 pair<bool,unsigned int> p = props->getUnsignedInt("timeout");
144 pair<bool,bool> pcheck = props->getBool("consistentAddress");
146 ignoreAddress = !pcheck.second;
150 // The cache will either silently pass a session or nullptr back, or throw an exception out.
151 Session* session = getServiceProvider().getSessionCache()->find(
152 getApplication(), *this, (ignoreAddress ? nullptr : getRemoteAddr().c_str()), (checkTimeout ? &timeout : nullptr)
159 static char _x2c(const char *what)
163 digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
165 digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
169 void AbstractSPRequest::setRequestURI(const char* uri)
171 // Fix for bug 574, secadv 20061002
172 // Unescape URI up to query string delimiter by looking for %XX escapes.
173 // Adapted from Apache's util.c, ap_unescape_url function.
180 else if (*uri != '%') {
185 if (!isxdigit(*uri) || !isxdigit(*(uri+1)))
186 throw ConfigurationException("Bad request, contained unsupported encoded characters.");
195 const char* AbstractSPRequest::getRequestURI() const
197 return m_uri.c_str();
200 const char* AbstractSPRequest::getRequestURL() const
203 // Compute the full target URL
204 int port = getPort();
205 const char* scheme = getScheme();
206 m_url = string(scheme) + "://" + getHostname();
207 if (!isDefaultPort())
208 m_url += ":" + boost::lexical_cast<string>(port);
211 return m_url.c_str();
214 string AbstractSPRequest::getRemoteAddr() const
216 pair<bool,const char*> addr = getRequestSettings().first->getString("REMOTE_ADDR");
217 return addr.first ? getHeader(addr.second) : "";
220 const char* AbstractSPRequest::getParameter(const char* name) const
223 m_parser.reset(new CGIParser(*this));
225 pair<CGIParser::walker,CGIParser::walker> bounds = m_parser->getParameters(name);
226 return (bounds.first==bounds.second) ? nullptr : bounds.first->second;
229 vector<const char*>::size_type AbstractSPRequest::getParameters(const char* name, vector<const char*>& values) const
232 m_parser.reset(new CGIParser(*this));
234 pair<CGIParser::walker,CGIParser::walker> bounds = m_parser->getParameters(name);
235 while (bounds.first != bounds.second) {
236 values.push_back(bounds.first->second);
239 return values.size();
242 const char* AbstractSPRequest::getHandlerURL(const char* resource) const
245 resource = getRequestURL();
247 if (!m_handlerURL.empty() && resource && !strcmp(getRequestURL(), resource))
248 return m_handlerURL.c_str();
250 // Check for relative URL.
251 string stackresource;
252 if (resource && *resource == '/') {
253 // Compute a URL to the root of the site and point resource at constructed string.
254 int port = getPort();
255 const char* scheme = getScheme();
256 stackresource = string(scheme) + "://" + getHostname();
257 if (!isDefaultPort())
258 stackresource += ":" + boost::lexical_cast<string>(port);
259 stackresource += resource;
260 resource = stackresource.c_str();
263 #ifdef HAVE_STRCASECMP
264 if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))
266 if (!resource || (strnicmp(resource,"http://",7) && strnicmp(resource,"https://",8)))
268 throw ConfigurationException("Target resource was not an absolute URL.");
270 bool ssl_only = true;
271 const char* handler = nullptr;
272 const PropertySet* props = getApplication().getPropertySet("Sessions");
274 pair<bool,bool> p = props->getBool("handlerSSL");
277 pair<bool,const char*> p2 = props->getString("handlerURL");
283 handler = "/Shibboleth.sso";
285 else if (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)) {
286 throw ConfigurationException(
287 "Invalid handlerURL property ($1) in <Sessions> element for Application ($2)",
288 params(2, handler ? handler : "null", m_app->getId())
292 // The "handlerURL" property can be in one of three formats:
294 // 1) a full URI: http://host/foo/bar
295 // 2) a hostless URI: http:///foo/bar
296 // 3) a relative path: /foo/bar
298 // # Protocol Host Path
299 // 1 handler handler handler
300 // 2 handler resource handler
301 // 3 resource resource handler
303 // note: if ssl_only is true, make sure the protocol is https
305 const char* path = nullptr;
307 // Decide whether to use the handler or the resource for the "protocol"
309 if (*handler != '/') {
317 // break apart the "protocol" string into protocol, host, and "the rest"
318 const char* colon = strchr(prot, ':');
320 const char* slash = strchr(colon, '/');
324 // Compute the actual protocol and store in member.
326 m_handlerURL.assign("https://");
328 m_handlerURL.assign(prot, colon-prot);
330 // create the "host" from either the colon/slash or from the target string
331 // If prot == handler then we're in either #1 or #2, else #3.
332 // If slash == colon then we're in #2.
333 if (prot != handler || slash == colon) {
334 colon = strchr(resource, ':');
335 colon += 3; // Get past the ://
336 slash = strchr(colon, '/');
338 string host(colon, (slash ? slash-colon : strlen(colon)));
340 // Build the handler URL
341 m_handlerURL += host + path;
342 return m_handlerURL.c_str();
345 void AbstractSPRequest::log(SPLogLevel level, const std::string& msg) const
347 reinterpret_cast<Category*>(m_log)->log(
348 (level == SPDebug ? Priority::DEBUG :
349 (level == SPInfo ? Priority::INFO :
350 (level == SPWarn ? Priority::WARN :
351 (level == SPError ? Priority::ERROR : Priority::CRIT)))),
356 bool AbstractSPRequest::isPriorityEnabled(SPLogLevel level) const
358 return reinterpret_cast<Category*>(m_log)->isPriorityEnabled(
359 (level == SPDebug ? Priority::DEBUG :
360 (level == SPInfo ? Priority::INFO :
361 (level == SPWarn ? Priority::WARN :
362 (level == SPError ? Priority::ERROR : Priority::CRIT))))