2 * Copyright 2001-2010 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * AbstractSPRequest.cpp
20 * Abstract base for SPRequest implementations.
24 #include "exceptions.h"
25 #include "AbstractSPRequest.h"
26 #include "Application.h"
27 #include "ServiceProvider.h"
28 #include "SessionCache.h"
29 #include "util/CGIParser.h"
31 using namespace shibsp;
32 using namespace opensaml;
33 using namespace xmltooling;
36 SPRequest::SPRequest()
40 SPRequest::~SPRequest()
44 string SPRequest::getSecureHeader(const char* name) const
46 return getHeader(name);
49 void SPRequest::setAuthType(const char* authtype)
54 GSSRequest::GSSRequest()
58 GSSRequest::~GSSRequest()
63 AbstractSPRequest::AbstractSPRequest(const char* category)
64 : m_sp(nullptr), m_mapper(nullptr), m_app(nullptr), m_sessionTried(false), m_session(nullptr),
65 m_log(&Category::getInstance(category)), m_parser(nullptr)
67 m_sp=SPConfig::getConfig().getServiceProvider();
71 AbstractSPRequest::~AbstractSPRequest()
82 const ServiceProvider& AbstractSPRequest::getServiceProvider() const
87 RequestMapper::Settings AbstractSPRequest::getRequestSettings() const
90 // Map request to application and content settings.
91 m_mapper=m_sp->getRequestMapper();
93 m_settings = m_mapper->getSettings(*this);
95 if (reinterpret_cast<Category*>(m_log)->isDebugEnabled()) {
96 reinterpret_cast<Category*>(m_log)->debug(
97 "mapped %s to %s", getRequestURL(), m_settings.first->getString("applicationId").second
104 const Application& AbstractSPRequest::getApplication() const
107 // Now find the application from the URL settings
108 m_app=m_sp->getApplication(getRequestSettings().first->getString("applicationId").second);
110 throw ConfigurationException("Unable to map non-default applicationId to an ApplicationOverride, check configuration.");
115 Session* AbstractSPRequest::getSession(bool checkTimeout, bool ignoreAddress, bool cache)
117 // Only attempt this once.
118 if (cache && m_sessionTried)
121 m_sessionTried = true;
123 // Need address checking and timeout settings.
125 if (checkTimeout || !ignoreAddress) {
126 const PropertySet* props=getApplication().getPropertySet("Sessions");
129 pair<bool,unsigned int> p=props->getUnsignedInt("timeout");
133 pair<bool,bool> pcheck=props->getBool("consistentAddress");
135 ignoreAddress = !pcheck.second;
139 // The cache will either silently pass a session or nullptr back, or throw an exception out.
140 Session* session = getServiceProvider().getSessionCache()->find(
141 getApplication(), *this, ignoreAddress ? nullptr : getRemoteAddr().c_str(), checkTimeout ? &timeout : nullptr
148 static char _x2c(const char *what)
152 digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
154 digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
158 void AbstractSPRequest::setRequestURI(const char* uri)
160 // Fix for bug 574, secadv 20061002
161 // Unescape URI up to query string delimiter by looking for %XX escapes.
162 // Adapted from Apache's util.c, ap_unescape_url function.
169 else if (*uri != '%') {
174 if (!isxdigit(*uri) || !isxdigit(*(uri+1)))
175 throw ConfigurationException("Bad request, contained unsupported encoded characters.");
184 const char* AbstractSPRequest::getRequestURI() const
186 return m_uri.c_str();
189 const char* AbstractSPRequest::getRequestURL() const
192 // Compute the full target URL
193 int port = getPort();
194 const char* scheme = getScheme();
195 m_url = string(scheme) + "://" + getHostname();
196 if ((!strcmp(scheme,"http") && port!=80) || (!strcmp(scheme,"https") && port!=443)) {
197 ostringstream portstr;
199 m_url += ":" + portstr.str();
203 return m_url.c_str();
206 string AbstractSPRequest::getRemoteAddr() const
208 pair<bool,const char*> addr = getRequestSettings().first->getString("REMOTE_ADDR");
209 return addr.first ? getHeader(addr.second) : "";
212 const char* AbstractSPRequest::getParameter(const char* name) const
215 m_parser=new CGIParser(*this);
217 pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
218 return (bounds.first==bounds.second) ? nullptr : bounds.first->second;
221 vector<const char*>::size_type AbstractSPRequest::getParameters(const char* name, vector<const char*>& values) const
224 m_parser=new CGIParser(*this);
226 pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
227 while (bounds.first!=bounds.second) {
228 values.push_back(bounds.first->second);
231 return values.size();
234 const char* AbstractSPRequest::getHandlerURL(const char* resource) const
237 resource = getRequestURL();
239 if (!m_handlerURL.empty() && resource && !strcmp(getRequestURL(),resource))
240 return m_handlerURL.c_str();
242 string stackresource;
243 if (resource && *resource == '/') {
244 // Compute a URL to the root of the site and point resource at constructed string.
245 int port = getPort();
246 const char* scheme = getScheme();
247 stackresource = string(scheme) + "://" + getHostname();
248 if ((!strcmp(scheme,"http") && port!=80) || (!strcmp(scheme,"https") && port!=443)) {
249 ostringstream portstr;
251 stackresource += ":" + portstr.str();
253 stackresource += resource;
254 resource = stackresource.c_str();
257 #ifdef HAVE_STRCASECMP
258 if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))
260 if (!resource || (strnicmp(resource,"http://",7) && strnicmp(resource,"https://",8)))
262 throw ConfigurationException("Target resource was not an absolute URL.");
265 const char* handler=nullptr;
266 const PropertySet* props=getApplication().getPropertySet("Sessions");
268 pair<bool,bool> p=props->getBool("handlerSSL");
271 pair<bool,const char*> p2=props->getString("handlerURL");
277 handler = "/Shibboleth.sso";
279 else if (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)) {
280 throw ConfigurationException(
281 "Invalid handlerURL property ($1) in <Sessions> element for Application ($2)",
282 params(2, handler ? handler : "null", m_app->getId())
286 // The "handlerURL" property can be in one of three formats:
288 // 1) a full URI: http://host/foo/bar
289 // 2) a hostless URI: http:///foo/bar
290 // 3) a relative path: /foo/bar
292 // # Protocol Host Path
293 // 1 handler handler handler
294 // 2 handler resource handler
295 // 3 resource resource handler
297 // note: if ssl_only is true, make sure the protocol is https
299 const char* path = nullptr;
301 // Decide whether to use the handler or the resource for the "protocol"
303 if (*handler != '/') {
311 // break apart the "protocol" string into protocol, host, and "the rest"
312 const char* colon=strchr(prot,':');
314 const char* slash=strchr(colon,'/');
318 // Compute the actual protocol and store in member.
320 m_handlerURL.assign("https://");
322 m_handlerURL.assign(prot, colon-prot);
324 // create the "host" from either the colon/slash or from the target string
325 // If prot == handler then we're in either #1 or #2, else #3.
326 // If slash == colon then we're in #2.
327 if (prot != handler || slash == colon) {
328 colon = strchr(resource, ':');
329 colon += 3; // Get past the ://
330 slash = strchr(colon, '/');
332 string host(colon, (slash ? slash-colon : strlen(colon)));
334 // Build the handler URL
335 m_handlerURL += host + path;
336 return m_handlerURL.c_str();
339 void AbstractSPRequest::log(SPLogLevel level, const std::string& msg) const
341 reinterpret_cast<Category*>(m_log)->log(
342 (level == SPDebug ? Priority::DEBUG :
343 (level == SPInfo ? Priority::INFO :
344 (level == SPWarn ? Priority::WARN :
345 (level == SPError ? Priority::ERROR : Priority::CRIT)))),
350 bool AbstractSPRequest::isPriorityEnabled(SPLogLevel level) const
352 return reinterpret_cast<Category*>(m_log)->isPriorityEnabled(
353 (level == SPDebug ? Priority::DEBUG :
354 (level == SPInfo ? Priority::INFO :
355 (level == SPWarn ? Priority::WARN :
356 (level == SPError ? Priority::ERROR : Priority::CRIT))))