X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=shibsp%2Fhandler%2Fimpl%2FSAML2LogoutInitiator.cpp;h=bb5efd1e10448f9b6005b78f63629c0a26cf63d4;hb=a7da711268b228d8751a45213c6596ec2a7c1240;hp=36141f580ddd497e2b1874fae22abf27149c29d8;hpb=fbb08caf375f759f9f3ec707e0d7b797a945f8c5;p=shibboleth%2Fcpp-sp.git diff --git a/shibsp/handler/impl/SAML2LogoutInitiator.cpp b/shibsp/handler/impl/SAML2LogoutInitiator.cpp index 36141f5..bb5efd1 100644 --- a/shibsp/handler/impl/SAML2LogoutInitiator.cpp +++ b/shibsp/handler/impl/SAML2LogoutInitiator.cpp @@ -1,22 +1,26 @@ -/* - * 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. + * + * 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. */ /** * SAML2LogoutInitiator.cpp - * + * * Triggers SP-initiated logout for SAML 2.0 sessions. */ @@ -26,14 +30,20 @@ #include "ServiceProvider.h" #include "SessionCache.h" #include "handler/AbstractHandler.h" -#include "handler/LogoutHandler.h" +#include "handler/LogoutInitiator.h" #ifndef SHIBSP_LITE # include "binding/SOAPClient.h" +# include "metadata/MetadataProviderCriteria.h" +# include "security/SecurityPolicy.h" +# include +# include +# include # include # include # include # include +# include # include using namespace opensaml::saml2; using namespace opensaml::saml2p; @@ -45,6 +55,7 @@ using namespace opensaml; using namespace shibsp; using namespace xmltooling; +using namespace boost; using namespace std; namespace shibsp { @@ -53,44 +64,48 @@ namespace shibsp { #pragma warning( push ) #pragma warning( disable : 4250 ) #endif - - class SHIBSP_DLLLOCAL SAML2LogoutInitiator : public AbstractHandler, public LogoutHandler + + class SHIBSP_DLLLOCAL SAML2LogoutInitiator : public AbstractHandler, public LogoutInitiator { public: SAML2LogoutInitiator(const DOMElement* e, const char* appId); - virtual ~SAML2LogoutInitiator() { -#ifndef SHIBSP_LITE - if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { - XMLString::release(&m_outgoing); - for_each(m_encoders.begin(), m_encoders.end(), cleanup_pair()); - } -#endif - } - + virtual ~SAML2LogoutInitiator() {} + + void init(const char* location); // encapsulates actions that need to run either in the c'tor or setParent + void setParent(const PropertySet* parent); void receive(DDF& in, ostream& out); pair run(SPRequest& request, bool isHandler=true) const; -#ifndef SHIBSP_LITE - const char* getType() const { - return "LogoutInitiator"; + const XMLCh* getProtocolFamily() const { + return samlconstants::SAML20P_NS; } -#endif private: - pair doRequest(const Application& application, const char* requestURL, Session* session, HTTPResponse& httpResponse) const; + pair doRequest( + const Application& application, const HTTPRequest& request, HTTPResponse& httpResponse, Session* session + ) const; string m_appId; + auto_ptr_char m_protocol; #ifndef SHIBSP_LITE - LogoutRequest* buildRequest( - const Application& application, const Session& session, const IDPSSODescriptor& role, const MessageEncoder* encoder=NULL + auto_ptr buildRequest( + const Application& application, const Session& session, const RoleDescriptor& role, const MessageEncoder* encoder=nullptr ) const; - XMLCh* m_outgoing; - vector m_bindings; - map m_encoders; + LogoutEvent* newLogoutEvent( + const Application& application, const HTTPRequest* request=nullptr, const Session* session=nullptr + ) const { + LogoutEvent* e = LogoutHandler::newLogoutEvent(application, request, session); + if (e) + e->m_protocol = m_protocol.get(); + return e; + } + + bool m_async; + vector m_bindings; + map< string,boost::shared_ptr > m_encoders; #endif - auto_ptr_char m_protocol; }; #if defined (_MSC_VER) @@ -104,71 +119,72 @@ namespace shibsp { }; SAML2LogoutInitiator::SAML2LogoutInitiator(const DOMElement* e, const char* appId) - : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".LogoutInitiator.SAML2")), m_appId(appId), + : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".LogoutInitiator.SAML2")), m_appId(appId), m_protocol(samlconstants::SAML20P_NS) #ifndef SHIBSP_LITE - m_outgoing(NULL), + ,m_async(true) #endif - m_protocol(samlconstants::SAML20P_NS) { + // If Location isn't set, defer initialization until the setParent call. + pair loc = getString("Location"); + if (loc.first) { + init(loc.second); + } +} + +void SAML2LogoutInitiator::setParent(const PropertySet* parent) +{ + DOMPropertySet::setParent(parent); + pair loc = getString("Location"); + init(loc.second); +} + +void SAML2LogoutInitiator::init(const char* location) +{ + if (location) { + string address = m_appId + location + "::run::SAML2LI"; + setAddress(address.c_str()); + } + else { + m_log.warn("no Location property in SAML2 LogoutInitiator (or parent), can't register as remoted handler"); + } + #ifndef SHIBSP_LITE if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { - // Handle outgoing binding setup. - pair outgoing = getXMLString("outgoingBindings"); + pair async = getBool("asynchronous"); + m_async = !async.first || async.second; + + string dupBindings; + pair outgoing = getString("outgoingBindings"); if (outgoing.first) { - m_outgoing = XMLString::replicate(outgoing.second); - XMLString::trim(m_outgoing); + dupBindings = outgoing.second; } else { // No override, so we'll install a default binding precedence. - string prec = string(samlconstants::SAML20_BINDING_HTTP_REDIRECT) + ' ' + samlconstants::SAML20_BINDING_HTTP_POST + ' ' + + dupBindings = string(samlconstants::SAML20_BINDING_HTTP_REDIRECT) + ' ' + samlconstants::SAML20_BINDING_HTTP_POST + ' ' + samlconstants::SAML20_BINDING_HTTP_POST_SIMPLESIGN + ' ' + samlconstants::SAML20_BINDING_HTTP_ARTIFACT; - m_outgoing = XMLString::transcode(prec.c_str()); } - - int pos; - XMLCh* start = m_outgoing; - while (start && *start) { - pos = XMLString::indexOf(start,chSpace); - if (pos != -1) - *(start + pos)=chNull; - m_bindings.push_back(start); + split(m_bindings, dupBindings, is_space(), algorithm::token_compress_on); + for (vector::const_iterator b = m_bindings.begin(); b != m_bindings.end(); ++b) { try { - auto_ptr_char b(start); - MessageEncoder * encoder = - SAMLConfig::getConfig().MessageEncoderManager.newPlugin(b.get(),pair(e,NULL)); - m_encoders[start] = encoder; - m_log.debug("supporting outgoing binding (%s)", b.get()); + boost::shared_ptr encoder( + SAMLConfig::getConfig().MessageEncoderManager.newPlugin(*b, pair(getElement(),nullptr)) + ); + if (encoder->isUserAgentPresent() && XMLString::equals(getProtocolFamily(), encoder->getProtocolFamily())) { + m_encoders[*b] = encoder; + m_log.debug("supporting outgoing binding (%s)", b->c_str()); + } + else { + m_log.warn("skipping outgoing binding (%s), not a SAML 2.0 front-channel mechanism", b->c_str()); + } } - catch (exception& ex) { + catch (std::exception& ex) { m_log.error("error building MessageEncoder: %s", ex.what()); } - if (pos != -1) - start = start + pos + 1; - else - break; } } #endif - - pair loc = getString("Location"); - if (loc.first) { - string address = m_appId + loc.second + "::run::SAML2LI"; - setAddress(address.c_str()); - } } -void SAML2LogoutInitiator::setParent(const PropertySet* parent) -{ - DOMPropertySet::setParent(parent); - pair loc = getString("Location"); - if (loc.first) { - string address = m_appId + loc.second + "::run::SAML2LI"; - setAddress(address.c_str()); - } - else { - m_log.warn("no Location property in SAML2 LogoutInitiator (or parent), can't register as remoted handler"); - } -} pair SAML2LogoutInitiator::run(SPRequest& request, bool isHandler) const { @@ -180,35 +196,33 @@ pair SAML2LogoutInitiator::run(SPRequest& request, bool isHandler) co // At this point we know the front-channel is handled. // We need the session to do any other work. - Session* session = NULL; + Session* session = nullptr; try { session = request.getSession(false, true, false); // don't cache it and ignore all checks if (!session) - return make_pair(false,0); + return make_pair(false, 0L); // We only handle SAML 2.0 sessions. if (!XMLString::equals(session->getProtocol(), m_protocol.get())) { session->unlock(); - return make_pair(false,0); + return make_pair(false, 0L); } } - catch (exception& ex) { + catch (std::exception& ex) { m_log.error("error accessing current session: %s", ex.what()); - return make_pair(false,0); + return make_pair(false, 0L); } if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { // When out of process, we run natively. - return doRequest(request.getApplication(), request.getRequestURL(), session, request); + return doRequest(request.getApplication(), request, request, session); } else { // When not out of process, we remote the request. - Locker locker(session, false); - DDF out,in(m_address.c_str()); + session->unlock(); + vector headers(1,"Cookie"); + DDF out,in = wrap(request,&headers); DDFJanitor jin(in), jout(out); - in.addmember("application_id").string(request.getApplication().getId()); - in.addmember("session_id").string(session->getID()); - in.addmember("url").string(request.getRequestURL()); out=request.getServiceProvider().getListenerService()->send(in); return unwrap(request, out); } @@ -223,23 +237,26 @@ void SAML2LogoutInitiator::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 Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : nullptr; if (!app) { // Something's horribly wrong. m_log.error("couldn't find application (%s) for logout", aid ? aid : "(missing)"); throw ConfigurationException("Unable to locate application for logout, deleted?"); } - + + // Unpack the request. + scoped_ptr req(getRequest(in)); + // Set up a response shim. - DDF ret(NULL); + DDF ret(nullptr); DDFJanitor jout(ret); - auto_ptr resp(getResponse(ret)); - - Session* session = NULL; + scoped_ptr resp(getResponse(ret)); + + Session* session = nullptr; try { - session = app->getServiceProvider().getSessionCache()->find(in["session_id"].string(), *app, NULL, NULL); + session = app->getServiceProvider().getSessionCache()->find(*app, *req, nullptr, nullptr); } - catch (exception& ex) { + catch (std::exception& ex) { m_log.error("error accessing current session: %s", ex.what()); } @@ -249,16 +266,12 @@ void SAML2LogoutInitiator::receive(DDF& in, ostream& out) // Since we're remoted, the result should either be a throw, which we pass on, // a false/0 return, which we just return as an empty structure, or a response/redirect, // which we capture in the facade and send back. - doRequest(*app, in["url"].string(), session, *resp.get()); + doRequest(*app, *req, *resp, session); } else { - m_log.error("no NameID or issuing entityID found in session"); - session->unlock(); - app->getServiceProvider().getSessionCache()->remove(in["session_id"].string(), *app); - - // Clear the cookie. - pair shib_cookie=app->getCookieNameProps("_shibsession_"); - resp->setCookie(shib_cookie.first.c_str(), shib_cookie.second); + session->unlock(); + m_log.log(getParent() ? Priority::WARN : Priority::ERROR, "bypassing SAML 2.0 logout, no NameID or issuing entityID found in session"); + app->getServiceProvider().getSessionCache()->remove(*app, *req, resp.get()); } } out << ret; @@ -268,141 +281,226 @@ void SAML2LogoutInitiator::receive(DDF& in, ostream& out) } pair SAML2LogoutInitiator::doRequest( - const Application& application, const char* requestURL, Session* session, HTTPResponse& response + const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse, Session* session ) const { - // Clear the cookie. - pair shib_cookie=application.getCookieNameProps("_shibsession_"); - response.setCookie(shib_cookie.first.c_str(), shib_cookie.second); + Locker sessionLocker(session, false); +#ifndef SHIBSP_LITE + scoped_ptr logout_event(newLogoutEvent(application, &httpRequest, session)); +#endif // Do back channel notification. vector sessions(1, session->getID()); - if (!notifyBackChannel(application, requestURL, sessions, false)) { - session->unlock(); - application.getServiceProvider().getSessionCache()->remove(sessions.front().c_str(), application); - return sendLogoutPage(application, response, true, "Partial logout failure."); + if (!notifyBackChannel(application, httpRequest.getRequestURL(), sessions, false)) { +#ifndef SHIBSP_LITE + if (logout_event) { + logout_event->m_logoutType = LogoutEvent::LOGOUT_EVENT_PARTIAL; + application.getServiceProvider().getTransactionLog()->write(*logout_event); + } +#endif + sessionLocker.assign(); + session = nullptr; + application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse); + return sendLogoutPage(application, httpRequest, httpResponse, "partial"); } #ifndef SHIBSP_LITE - pair ret = make_pair(false,0); + pair ret = make_pair(false, 0L); try { // With a session in hand, we can create a LogoutRequest message, if we can find a compatible endpoint. - Locker metadataLocker(application.getMetadataProvider()); - const EntityDescriptor* entity = application.getMetadataProvider()->getEntityDescriptor(session->getEntityID()); - if (!entity) { + MetadataProvider* m = application.getMetadataProvider(); + Locker metadataLocker(m); + MetadataProviderCriteria mc(application, session->getEntityID(), &IDPSSODescriptor::ELEMENT_QNAME, samlconstants::SAML20P_NS); + pair entity = m->getEntityDescriptor(mc); + if (!entity.first) { + throw MetadataException( + "Unable to locate metadata for identity provider ($entityID)", namedparams(1, "entityID", session->getEntityID()) + ); + } + else if (!entity.second) { throw MetadataException( - "Unable to locate metadata for identity provider ($entityID)", - namedparams(1, "entityID", session->getEntityID()) + "Unable to locate SAML 2.0 IdP role for identity provider ($entityID).", namedparams(1, "entityID", session->getEntityID()) ); } - const IDPSSODescriptor* role = entity->getIDPSSODescriptor(samlconstants::SAML20P_NS); - if (!role) { + + const IDPSSODescriptor* role = dynamic_cast(entity.second); + if (role->getSingleLogoutServices().empty()) { throw MetadataException( - "Unable to locate SAML 2.0 IdP role for identity provider ($entityID).", - namedparams(1, "entityID", session->getEntityID()) + "No SingleLogoutService endpoints in metadata for identity provider ($entityID).", namedparams(1, "entityID", session->getEntityID()) ); } - const EndpointType* ep=NULL; - const MessageEncoder* encoder=NULL; - vector::const_iterator b; - for (b = m_bindings.begin(); b!=m_bindings.end(); ++b) { - if (ep=EndpointManager(role->getSingleLogoutServices()).getByBinding(*b)) { - map::const_iterator enc = m_encoders.find(*b); - if (enc!=m_encoders.end()) - encoder = enc->second; + const EndpointType* ep = nullptr; + const MessageEncoder* encoder = nullptr; + for (vector::const_iterator b = m_bindings.begin(); b != m_bindings.end(); ++b) { + auto_ptr_XMLCh wideb(b->c_str()); + if (ep = EndpointManager(role->getSingleLogoutServices()).getByBinding(wideb.get())) { + map< string,boost::shared_ptr >::const_iterator enc = m_encoders.find(*b); + if (enc != m_encoders.end()) + encoder = enc->second.get(); break; } } if (!ep || !encoder) { - m_log.warn("no compatible front channel SingleLogoutService, trying back channel..."); + m_log.debug("no compatible front channel SingleLogoutService, trying back channel..."); shibsp::SecurityPolicy policy(application); shibsp::SOAPClient soaper(policy); MetadataCredentialCriteria mcc(*role); - LogoutResponse* logoutResponse=NULL; + LogoutResponse* logoutResponse = nullptr; + scoped_ptr srt; auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP); - const vector& endpoints=role->getSingleLogoutServices(); - for (vector::const_iterator epit=endpoints.begin(); !logoutResponse && epit!=endpoints.end(); ++epit) { + const vector& endpoints = role->getSingleLogoutServices(); + for (indirect_iterator::const_iterator> epit = make_indirect_iterator(endpoints.begin()); + !logoutResponse && epit != make_indirect_iterator(endpoints.end()); ++epit) { try { - if (!XMLString::equals((*epit)->getBinding(),binding.get())) + if (!XMLString::equals(epit->getBinding(), binding.get())) continue; - LogoutRequest* msg = buildRequest(application, *session, *role); - auto_ptr_char dest((*epit)->getLocation()); + auto_ptr msg(buildRequest(application, *session, *role)); + + // Log the request. + if (logout_event) { + logout_event->m_logoutType = LogoutEvent::LOGOUT_EVENT_UNKNOWN; + logout_event->m_saml2Request = msg.get(); + application.getServiceProvider().getTransactionLog()->write(*logout_event); + logout_event->m_saml2Request = nullptr; + } + auto_ptr_char dest(epit->getLocation()); SAML2SOAPClient client(soaper, false); - client.sendSAML(msg, application.getId(), mcc, dest.get()); - StatusResponseType* srt = client.receiveSAML(); - if (!(logoutResponse = dynamic_cast(srt))) { - delete srt; + client.sendSAML(msg.release(), application.getId(), mcc, dest.get()); + srt.reset(client.receiveSAML()); + if (!(logoutResponse = dynamic_cast(srt.get()))) { break; } } - catch (exception& ex) { + catch (std::exception& ex) { m_log.error("error sending LogoutRequest message: %s", ex.what()); soaper.reset(); } } - if (!logoutResponse) - ret = sendLogoutPage(application, response, false, "Identity provider did not respond to logout request."); - else if (!logoutResponse->getStatus() || !logoutResponse->getStatus()->getStatusCode() || - !XMLString::equals(logoutResponse->getStatus()->getStatusCode()->getValue(), saml2p::StatusCode::SUCCESS)) { - delete logoutResponse; - ret = sendLogoutPage(application, response, false, "Identity provider returned a SAML error in response to logout request."); + // No answer at all? + if (!logoutResponse) { + if (endpoints.empty()) + m_log.info("IdP doesn't support single logout protocol over a compatible binding"); + else + m_log.warn("IdP didn't respond to logout request"); + + // Log the end result. + if (logout_event) { + logout_event->m_logoutType = LogoutEvent::LOGOUT_EVENT_PARTIAL; + application.getServiceProvider().getTransactionLog()->write(*logout_event); + } + + ret = sendLogoutPage(application, httpRequest, httpResponse, "partial"); } else { - delete logoutResponse; - ret = sendLogoutPage(application, response, false, "Logout completed successfully."); + // Check the status, looking for non-success or a partial logout code. + const StatusCode* sc = logoutResponse->getStatus() ? logoutResponse->getStatus()->getStatusCode() : nullptr; + bool partial = (!sc || !XMLString::equals(sc->getValue(), StatusCode::SUCCESS)); + if (!partial && sc->getStatusCode()) { + // Success, but still need to check for partial. + partial = XMLString::equals(sc->getStatusCode()->getValue(), StatusCode::PARTIAL_LOGOUT); + } + + // Log the end result. + if (logout_event) { + logout_event->m_logoutType = partial ? LogoutEvent::LOGOUT_EVENT_PARTIAL : LogoutEvent::LOGOUT_EVENT_GLOBAL; + logout_event->m_saml2Response = logoutResponse; + application.getServiceProvider().getTransactionLog()->write(*logout_event); + } + + if (partial) + ret = sendLogoutPage(application, httpRequest, httpResponse, "partial"); + else { + const char* returnloc = httpRequest.getParameter("return"); + if (returnloc) { + // Relative URLs get promoted, absolutes get validated. + if (*returnloc == '/') { + string loc(returnloc); + httpRequest.absolutize(loc); + ret.second = httpResponse.sendRedirect(loc.c_str()); + } + else { + application.limitRedirect(httpRequest, returnloc); + ret.second = httpResponse.sendRedirect(returnloc); + } + ret.first = true; + } + else { + ret = sendLogoutPage(application, httpRequest, httpResponse, "global"); + } + } } if (session) { - string session_id = session->getID(); - session->unlock(); - session = NULL; - application.getServiceProvider().getSessionCache()->remove(session_id.c_str(), application); + sessionLocker.assign(); + session = nullptr; + application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse); } + return ret; } - auto_ptr msg(buildRequest(application, *session, *role, encoder)); + // Save off return location as RelayState. + string relayState; + const char* returnloc = httpRequest.getParameter("return"); + if (returnloc) { + application.limitRedirect(httpRequest, returnloc); + relayState = returnloc; + httpRequest.absolutize(relayState); + cleanRelayState(application, httpRequest, httpResponse); + preserveRelayState(application, httpResponse, relayState); + } + auto_ptr msg(buildRequest(application, *session, *role, encoder)); msg->setDestination(ep->getLocation()); + + // Log the request. + if (logout_event) { + logout_event->m_logoutType = LogoutEvent::LOGOUT_EVENT_UNKNOWN; + logout_event->m_saml2Request = msg.get(); + application.getServiceProvider().getTransactionLog()->write(*logout_event); + } + auto_ptr_char dest(ep->getLocation()); - ret.second = sendMessage(*encoder, msg.get(), NULL, dest.get(), role, application, response); + ret.second = sendMessage(*encoder, msg.get(), relayState.c_str(), dest.get(), role, application, httpResponse, true); ret.first = true; msg.release(); // freed by encoder + + if (session) { + sessionLocker.assign(); + session = nullptr; + application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse); + } } - catch (exception& ex) { - m_log.error("error issuing SAML 2.0 logout request: %s", ex.what()); + catch (MetadataException& mex) { + // Less noise for IdPs that don't support logout (i.e. most) + m_log.info("unable to issue SAML 2.0 logout request: %s", mex.what()); } - - if (session) { - string session_id = session->getID(); - session->unlock(); - session = NULL; - application.getServiceProvider().getSessionCache()->remove(session_id.c_str(), application); + catch (std::exception& ex) { + m_log.error("error issuing SAML 2.0 logout request: %s", ex.what()); } return ret; #else - string session_id = session->getID(); - session->unlock(); - application.getServiceProvider().getSessionCache()->remove(session_id.c_str(), application); throw ConfigurationException("Cannot perform logout using lite version of shibsp library."); #endif } #ifndef SHIBSP_LITE -LogoutRequest* SAML2LogoutInitiator::buildRequest( - const Application& application, const Session& session, const IDPSSODescriptor& role, const MessageEncoder* encoder +auto_ptr SAML2LogoutInitiator::buildRequest( + const Application& application, const Session& session, const RoleDescriptor& role, const MessageEncoder* encoder ) const { + const PropertySet* relyingParty = application.getRelyingParty(dynamic_cast(role.getParent())); + auto_ptr msg(LogoutRequestBuilder::buildLogoutRequest()); Issuer* issuer = IssuerBuilder::buildIssuer(); msg->setIssuer(issuer); - issuer->setName(application.getXMLString("entityID").second); + issuer->setName(relyingParty->getXMLString("entityID").second); auto_ptr_XMLCh index(session.getSessionIndex()); if (index.get() && *index.get()) { SessionIndex* si = SessionIndexBuilder::buildSessionIndex(); @@ -411,7 +509,6 @@ LogoutRequest* SAML2LogoutInitiator::buildRequest( } const NameID* nameid = session.getNameID(); - const PropertySet* relyingParty = application.getRelyingParty(dynamic_cast(role.getParent())); pair flag = relyingParty->getString("encryption"); if (flag.first && (!strcmp(flag.second, "true") || (encoder && !strcmp(flag.second, "front")) || (!encoder && !strcmp(flag.second, "back")))) { @@ -424,53 +521,24 @@ LogoutRequest* SAML2LogoutInitiator::buildRequest( encoder ? encoder->isCompact() : false, relyingParty->getXMLString("encryptionAlg").second ); - msg->setEncryptedID(encrypted.release()); + msg->setEncryptedID(encrypted.get()); + encrypted.release(); + } + else { + msg->setNameID(nameid->cloneNameID()); } - if (!encoder) { - // No encoder being used, so sign for SOAP client manually. - flag = relyingParty->getString("signing"); - if (flag.first && (!strcmp(flag.second, "true") || !strcmp(flag.second, "back"))) { - CredentialResolver* credResolver=application.getCredentialResolver(); - if (credResolver) { - Locker credLocker(credResolver); - // Fill in criteria to use. - MetadataCredentialCriteria mcc(role); - mcc.setUsage(CredentialCriteria::SIGNING_CREDENTIAL); - pair keyName = relyingParty->getString("keyName"); - if (keyName.first) - mcc.getKeyNames().insert(keyName.second); - pair sigalg = relyingParty->getXMLString("signingAlg"); - if (sigalg.first) - mcc.setXMLAlgorithm(sigalg.second); - const Credential* cred = credResolver->resolve(&mcc); - if (cred) { - xmlsignature::Signature* sig = xmlsignature::SignatureBuilder::buildSignature(); - msg->setSignature(sig); - if (sigalg.first) - sig->setSignatureAlgorithm(sigalg.second); - sigalg = relyingParty->getXMLString("digestAlg"); - if (sigalg.first) { - ContentReference* cr = dynamic_cast(sig->getContentReference()); - if (cr) - cr->setDigestAlgorithm(sigalg.second); - } - - // Sign response while marshalling. - vector sigs(1,sig); - msg->marshall((DOMDocument*)NULL,&sigs,cred); - } - else { - m_log.warn("no signing credential resolved, leaving message unsigned"); - } - } - else { - m_log.warn("no credential resolver installed, leaving message unsigned"); - } - } + XMLCh* msgid = SAMLConfig::getConfig().generateIdentifier(); + msg->setID(msgid); + XMLString::release(&msgid); + msg->setIssueInstant(time(nullptr)); + + if (m_async && encoder) { + msg->setExtensions(saml2p::ExtensionsBuilder::buildExtensions()); + msg->getExtensions()->getUnknownXMLObjects().push_back(AsynchronousBuilder::buildAsynchronous()); } - return msg.release(); + return msg; } #endif