#include "internal.h"
#include "Application.h"
#include "exceptions.h"
+#include "ServiceProvider.h"
#include "SPRequest.h"
#include "handler/AbstractHandler.h"
+#include "remoting/ListenerService.h"
+#include "util/SPConstants.h"
+
+#ifndef SHIBSP_LITE
+# include <saml/SAMLConfig.h>
+# include <saml/binding/SAMLArtifact.h>
+# include <saml/saml1/core/Protocols.h>
+# include <saml/saml2/core/Protocols.h>
+# include <saml/util/SAMLConstants.h>
+# include <xmltooling/util/StorageService.h>
+#else
+# include "lite/SAMLConstants.h"
+#endif
-#include <saml/saml1/core/Protocols.h>
-#include <saml/saml2/core/Protocols.h>
-#include <saml/util/SAMLConstants.h>
#include <xmltooling/XMLToolingConfig.h>
#include <xmltooling/util/URLEncoder.h>
using namespace samlconstants;
using namespace opensaml;
using namespace xmltooling;
+using namespace log4cpp;
using namespace xercesc;
using namespace std;
namespace shibsp {
- SHIBSP_DLLLOCAL PluginManager<Handler,const DOMElement*>::Factory SAML1ConsumerFactory;
+ SHIBSP_DLLLOCAL PluginManager<Handler,string,pair<const DOMElement*,const char*>>::Factory SAML1ConsumerFactory;
+ SHIBSP_DLLLOCAL PluginManager<Handler,string,pair<const DOMElement*,const char*>>::Factory SAML2ConsumerFactory;
+ SHIBSP_DLLLOCAL PluginManager<Handler,string,pair<const DOMElement*,const char*>>::Factory SAML2ArtifactResolutionFactory;
};
void SHIBSP_API shibsp::registerHandlers()
{
SPConfig& conf=SPConfig::getConfig();
+
conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_ARTIFACT, SAML1ConsumerFactory);
conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_POST, SAML1ConsumerFactory);
+ conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_ARTIFACT, SAML2ConsumerFactory);
+ conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_POST, SAML2ConsumerFactory);
+ conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_POST_SIMPLESIGN, SAML2ConsumerFactory);
+
+ conf.ArtifactResolutionServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2ArtifactResolutionFactory);
}
AbstractHandler::AbstractHandler(
const DOMElement* e, log4cpp::Category& log, DOMNodeFilter* filter, const map<string,string>* remapper
- ) : m_log(log) {
+ ) : m_log(log), m_configNS(shibspconstants::SHIB2SPCONFIG_NS) {
load(e,log,filter,remapper);
}
+#ifndef SHIBSP_LITE
void AbstractHandler::checkError(const XMLObject* response) const
{
const saml2p::StatusResponseType* r2 = dynamic_cast<const saml2p::StatusResponseType*>(response);
}
}
-void AbstractHandler::recoverRelayState(HTTPRequest& httpRequest, string& relayState) const
+void AbstractHandler::prepareResponse(saml2p::StatusResponseType& response, const XMLCh* code, const XMLCh* subcode, const char* msg) const
+{
+ saml2p::Status* status = saml2p::StatusBuilder::buildStatus();
+ saml2p::StatusCode* scode = saml2p::StatusCodeBuilder::buildStatusCode();
+ status->setStatusCode(scode);
+ scode->setValue(code);
+ if (subcode) {
+ saml2p::StatusCode* ssubcode = saml2p::StatusCodeBuilder::buildStatusCode();
+ scode->setStatusCode(ssubcode);
+ ssubcode->setValue(subcode);
+ }
+ if (msg) {
+ pair<bool,bool> flag = getBool("detailedErrors", m_configNS.get());
+ auto_ptr_XMLCh widemsg((flag.first && flag.second) ? msg : "Error processing request.");
+ saml2p::StatusMessage* sm = saml2p::StatusMessageBuilder::buildStatusMessage();
+ status->setStatusMessage(sm);
+ sm->setMessage(widemsg.get());
+ }
+ response.setStatus(status);
+}
+#endif
+
+void AbstractHandler::preserveRelayState(const Application& application, HTTPResponse& response, string& relayState) const
+{
+ if (relayState.empty())
+ return;
+
+ // No setting means just pass it by value.
+ pair<bool,const char*> mech=getString("relayState");
+ if (!mech.first || !mech.second || !*mech.second)
+ return;
+
+ if (!strcmp(mech.second, "cookie")) {
+ // Here we store the state in a cookie and send a fixed
+ // value so we can recognize it on the way back.
+ if (relayState != "cookie") {
+ const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder();
+ pair<string,const char*> shib_cookie=application.getCookieNameProps("_shibstate_");
+ string stateval = urlenc->encode(relayState.c_str()) + shib_cookie.second;
+ response.setCookie(shib_cookie.first.c_str(),stateval.c_str());
+ relayState = "cookie";
+ }
+ }
+ else if (strstr(mech.second,"ss:")==mech.second) {
+ if (relayState.find("ss:")!=0) {
+ mech.second+=3;
+ if (*mech.second) {
+ if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+#ifndef SHIBSP_LITE
+ StorageService* storage = application.getServiceProvider().getStorageService(mech.second);
+ if (storage) {
+ string rsKey;
+ SAMLConfig::getConfig().generateRandomBytes(rsKey,10);
+ rsKey = SAMLArtifact::toHex(rsKey);
+ storage->createString("RelayState", rsKey.c_str(), relayState.c_str(), time(NULL) + 600);
+ relayState = string(mech.second-3) + ':' + rsKey;
+ }
+ else {
+ m_log.error("Storage-backed RelayState with invalid StorageService ID (%s)", mech.second);
+ relayState.erase();
+ }
+#endif
+ }
+ else if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
+ DDF out,in = DDF("set::RelayState").structure();
+ in.addmember("id").string(mech.second);
+ in.addmember("value").string(relayState.c_str());
+ DDFJanitor jin(in),jout(out);
+ out = application.getServiceProvider().getListenerService()->send(in);
+ if (!out.isstring())
+ throw IOException("StorageService-backed RelayState mechanism did not return a state key.");
+ relayState = string(mech.second-3) + ':' + out.string();
+ }
+ }
+ }
+ }
+ else
+ throw ConfigurationException("Unsupported relayState mechanism ($1).", params(1,mech.second));
+}
+
+void AbstractHandler::recoverRelayState(const Application& application, HTTPRequest& httpRequest, string& relayState, bool clear) const
{
SPConfig& conf = SPConfig::getConfig();
- if (conf.isEnabled(SPConfig::OutOfProcess)) {
- // Out of process, we look for StorageService-backed state.
- // TODO: something like ss:SSID:key?
+
+ // Look for StorageService-backed state of the form "ss:SSID:key".
+ const char* state = relayState.c_str();
+ if (strstr(state,"ss:")==state) {
+ state += 3;
+ const char* key = strchr(state,':');
+ if (key) {
+ string ssid = relayState.substr(3, key - state);
+ key++;
+ if (!ssid.empty() && *key) {
+ if (conf.isEnabled(SPConfig::OutOfProcess)) {
+#ifndef SHIBSP_LITE
+ StorageService* storage = conf.getServiceProvider()->getStorageService(ssid.c_str());
+ if (storage) {
+ ssid = key;
+ if (storage->readString("RelayState",ssid.c_str(),&relayState)>0) {
+ if (clear)
+ storage->deleteString("RelayState",ssid.c_str());
+ return;
+ }
+ else
+ relayState.erase();
+ }
+ else {
+ Category::getInstance(SHIBSP_LOGCAT".Handler").error(
+ "Storage-backed RelayState with invalid StorageService ID (%s)", ssid.c_str()
+ );
+ relayState.erase();
+ }
+#endif
+ }
+ else if (conf.isEnabled(SPConfig::InProcess)) {
+ DDF out,in = DDF("get::RelayState").structure();
+ in.addmember("id").string(ssid.c_str());
+ in.addmember("key").string(key);
+ in.addmember("clear").integer(clear ? 1 : 0);
+ DDFJanitor jin(in),jout(out);
+ out = application.getServiceProvider().getListenerService()->send(in);
+ if (!out.isstring()) {
+ m_log.error("StorageService-backed RelayState mechanism did not return a state value.");
+ relayState.erase();
+ }
+ else {
+ relayState = out.string();
+ return;
+ }
+ }
+ }
+ }
}
if (conf.isEnabled(SPConfig::InProcess)) {
- // In process, we should be able to cast down to a full SPRequest.
- SPRequest& request = dynamic_cast<SPRequest&>(httpRequest);
- if (relayState.empty() || relayState == "cookie") {
+ if (relayState == "cookie") {
// Pull the value from the "relay state" cookie.
- pair<string,const char*> relay_cookie = request.getApplication().getCookieNameProps("_shibstate_");
+ pair<string,const char*> relay_cookie = application.getCookieNameProps("_shibstate_");
+ // In process, we should be able to cast down to a full SPRequest.
+ SPRequest& request = dynamic_cast<SPRequest&>(httpRequest);
const char* state = request.getCookie(relay_cookie.first.c_str());
if (state && *state) {
// URL-decode the value.
relayState = rscopy;
free(rscopy);
- // Clear the cookie.
- request.setCookie(relay_cookie.first.c_str(),relay_cookie.second);
+ if (clear)
+ request.setCookie(relay_cookie.first.c_str(),relay_cookie.second);
+ return;
}
- else
- relayState = "default"; // fall through...
+
+ relayState.erase();
}
-
+
// Check for "default" value.
- if (relayState == "default") {
- pair<bool,const char*> homeURL=request.getApplication().getString("homeURL");
+ if (relayState.empty() || relayState == "default") {
+ pair<bool,const char*> homeURL=application.getString("homeURL");
relayState=homeURL.first ? homeURL.second : "/";
+ return;
}
}
+
+ if (relayState == "default")
+ relayState.empty();
}