<complexType>\r
<choice minOccurs="0" maxOccurs="unbounded">\r
<element ref="conf:SessionInitiator"/>\r
+ <element ref="conf:LogoutInitiator"/>\r
<element ref="md:AssertionConsumerService"/>\r
<element ref="md:ArtifactResolutionService"/>\r
<element ref="md:SingleLogoutService"/>\r
</complexContent>\r
</complexType>\r
</element>\r
+\r
+ <element name="LogoutInitiator">\r
+ <annotation>\r
+ <documentation>Used to specify handlers that can issue LogoutRequests</documentation>\r
+ </annotation>\r
+ <complexType>\r
+ <complexContent>\r
+ <restriction base="conf:PluggableType">\r
+ <sequence>\r
+ <any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>\r
+ </sequence>\r
+ <attribute name="Location" type="anyURI"/>\r
+ <attribute name="relayState" type="conf:string"/>\r
+ <attribute name="outgoingBindings" type="conf:listOfURIs"/>\r
+ <attribute name="template" type="anyURI"/>\r
+ <attribute name="postArtifact" type="boolean"/>\r
+ <anyAttribute namespace="##any" processContents="lax"/>\r
+ </restriction>\r
+ </complexContent>\r
+ </complexType>\r
+ </element>\r
\r
<element name="Errors">\r
<annotation>\r
return *m_app;
}
-Session* AbstractSPRequest::getSession(bool checkTimeout) const
+Session* AbstractSPRequest::getSession(bool checkTimeout, bool ignoreAddress, bool cache) const
{
// Only attempt this once.
- if (m_sessionTried)
+ if (cache && m_sessionTried)
return m_session;
- m_sessionTried = true;
+ else if (cache)
+ m_sessionTried = true;
// Get session ID from cookie.
const Application& app = getApplication();
// Need address checking and timeout settings.
time_t timeout=0;
- bool consistent=true;
- const PropertySet* props=app.getPropertySet("Sessions");
- if (props) {
- if (checkTimeout) {
- pair<bool,unsigned int> p=props->getUnsignedInt("timeout");
- if (p.first)
- timeout = p.second;
+ if (checkTimeout || !ignoreAddress) {
+ const PropertySet* props=app.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;
}
- pair<bool,bool> pcheck=props->getBool("consistentAddress");
- if (pcheck.first)
- consistent = pcheck.second;
}
// The cache will either silently pass a session or NULL back, or throw an exception out.
- return m_session = getServiceProvider().getSessionCache()->find(
- session_id, app, consistent ? getRemoteAddr().c_str() : NULL, checkTimeout ? &timeout : NULL
+ Session* session = getServiceProvider().getSessionCache()->find(
+ session_id, app, ignoreAddress ? NULL : getRemoteAddr().c_str(), checkTimeout ? &timeout : NULL
);
+ if (cache)
+ m_session = session;
+ return session;
}
const char* AbstractSPRequest::getRequestURL() const {
const char* AbstractSPRequest::getHandlerURL(const char* resource) const
{
+ if (!resource)
+ resource = getRequestURL();
+
if (!m_handlerURL.empty() && resource && !strcmp(getRequestURL(),resource))
return m_handlerURL.c_str();
const Application& getApplication() const;
- Session* getSession(bool checkTimeout=true) const;
+ Session* getSession(bool checkTimeout=true, bool ignoreAddress=false, bool cache=true) const;
const char* getRequestURL() const;
ArtifactResolutionServiceManager.deregisterFactories();
AssertionConsumerServiceManager.deregisterFactories();
+ LogoutInitiatorManager.deregisterFactories();
ManageNameIDServiceManager.deregisterFactories();
SessionInitiatorManager.deregisterFactories();
SingleLogoutServiceManager.deregisterFactories();
xmltooling::PluginManager<ListenerService,std::string,const xercesc::DOMElement*> ListenerServiceManager;
/**
+ * Manages factories for Handler plugins that implement LogoutInitiator functionality.
+ */
+ xmltooling::PluginManager< Handler,std::string,std::pair<const xercesc::DOMElement*,const char*> > LogoutInitiatorManager;
+
+ /**
* Manages factories for Handler plugins that implement ManageNameIDService functionality.
*/
xmltooling::PluginManager< Handler,std::string,std::pair<const xercesc::DOMElement*,const char*> > ManageNameIDServiceManager;
/**
* Returns a locked Session associated with the request.
*
- * @param touch true iff the last-used timestamp should be updated and any timeout policy enforced
+ * @param checkTimeout true iff the last-used timestamp should be updated and any timeout policy enforced
+ * @param ignoreAddress true iff all address checking should be ignored, regardless of policy
+ * @param cache true iff the request should hold the Session lock itself and unlock during cleanup
* @return pointer to Session, or NULL
*/
- virtual Session* getSession(bool checkTimeout=true) const=0;
+ virtual Session* getSession(bool checkTimeout=true, bool ignoreAddress=false, bool cache=true) const=0;
/**
* Returns the effective base Handler URL for a resource,
* @return the IdP's entityID
*/
virtual const char* getEntityID() const=0;
-
+
+ /**
+ * Returns the protocol family used to initiate the session.
+ *
+ * @return the protocol constant that represents the general SSO protocol used
+ */
+ virtual const char* getProtocol() const=0;
+
/**
* Returns the UTC timestamp on the authentication event at the IdP.
*
* @param application reference to Application that owns the Session
* @param client_addr network address of client
* @param issuer issuing metadata of assertion issuer, if known
+ * @param protocol protocol family used to initiate the session
* @param nameid principal identifier, normalized to SAML 2, if any
* @param authn_instant UTC timestamp of authentication at IdP, if known
* @param session_index index of session between principal and IdP, if any
const Application& application,
const char* client_addr=NULL,
const opensaml::saml2md::EntityDescriptor* issuer=NULL,
+ const XMLCh* protocol=NULL,
const opensaml::saml2::NameID* nameid=NULL,
- const char* authn_instant=NULL,
- const char* session_index=NULL,
- const char* authncontext_class=NULL,
- const char* authncontext_decl=NULL,
+ const XMLCh* authn_instant=NULL,
+ const XMLCh* session_index=NULL,
+ const XMLCh* authncontext_class=NULL,
+ const XMLCh* authncontext_decl=NULL,
const std::vector<const opensaml::Assertion*>* tokens=NULL,
const std::multimap<std::string,Attribute*>* attributes=NULL
)=0;
/**
- * Locates an existing session or sessions by subject identifier.
- *
- * @param issuer source of session(s)
- * @param nameid name identifier associated with the session(s) to locate
- * @param index index of session, or NULL for all sessions associated with other parameters
- * @param application reference to Application that owns the session(s)
- * @param sessions on exit, contains the IDs of the matching sessions
- */
- virtual void find(
- const opensaml::saml2md::EntityDescriptor& issuer,
- const opensaml::saml2::NameID& nameid,
- const char* index,
- const Application& application,
- std::vector<std::string>& sessions
- )=0;
-
- /**
* Deletes an existing session or sessions.
*
* @param issuer source of session(s)
* @param sessions on exit, contains the IDs of the matching sessions removed
*/
virtual void remove(
- const opensaml::saml2md::EntityDescriptor& issuer,
+ const opensaml::saml2md::EntityDescriptor* issuer,
const opensaml::saml2::NameID& nameid,
const char* index,
const Application& application,
const Application& app,
const std::multimap<std::string,Attribute*>& attributes,
const opensaml::saml2md::RoleDescriptor* role=NULL,
- const char* authncontext_class=NULL,
- const char* authncontext_decl=NULL
+ const XMLCh* authncontext_class=NULL,
+ const XMLCh* authncontext_decl=NULL
) : m_app(app), m_attributes(attributes), m_role(role), m_issuer(NULL), m_class(authncontext_class), m_decl(authncontext_decl) {
if (role)
m_issuer = dynamic_cast<opensaml::saml2md::EntityDescriptor*>(role->getParent())->getEntityID();
const Application& getApplication() const {
return m_app;
}
- const char* getAuthnContextClassRef() const {
+ const XMLCh* getAuthnContextClassRef() const {
return m_class;
}
- const char* getAuthnContextDeclRef() const {
+ const XMLCh* getAuthnContextDeclRef() const {
return m_decl;
}
const XMLCh* getAttributeRequester() const {
const std::multimap<std::string,Attribute*>& m_attributes;
const opensaml::saml2md::RoleDescriptor* m_role;
const XMLCh* m_issuer;
- const char* m_class;
- const char* m_decl;
+ const XMLCh* m_class;
+ const XMLCh* m_decl;
};
};
*
* @return a URI identifying the authentication context class
*/
- virtual const char* getAuthnContextClassRef() const=0;
+ virtual const XMLCh* getAuthnContextClassRef() const=0;
/**
* Returns a URI containing an AuthnContextDeclRef associated with the subject.
*
* @return a URI identifying the authentication context declaration
*/
- virtual const char* getAuthnContextDeclRef() const=0;
+ virtual const XMLCh* getAuthnContextDeclRef() const=0;
/**
* Gets the ID of the requester of the attributes, if known.
*/
class SHIBSP_DLLLOCAL AuthenticationMethodStringFunctor : public MatchFunctor
{
- xmltooling::auto_ptr_char m_value;
+ const XMLCh* m_value;
public:
AuthenticationMethodStringFunctor(const DOMElement* e) : m_value(e ? e->getAttributeNS(NULL,value) : NULL) {
- if (!m_value.get() || !*m_value.get())
+ if (!m_value || !*m_value)
throw ConfigurationException("AuthenticationMethodString MatchFunctor requires non-empty value attribute.");
}
bool evaluatePolicyRequirement(const FilteringContext& filterContext) const {
- return XMLString::equals(m_value.get(), filterContext.getAuthnContextClassRef()) ||
- XMLString::equals(m_value.get(), filterContext.getAuthnContextDeclRef());
+ return XMLString::equals(m_value, filterContext.getAuthnContextClassRef()) ||
+ XMLString::equals(m_value, filterContext.getAuthnContextDeclRef());
}
bool evaluatePermitValue(const FilteringContext& filterContext, const Attribute& attribute, size_t index) const {
- return XMLString::equals(m_value.get(), filterContext.getAuthnContextClassRef()) ||
- XMLString::equals(m_value.get(), filterContext.getAuthnContextDeclRef());
+ return XMLString::equals(m_value, filterContext.getAuthnContextClassRef()) ||
+ XMLString::equals(m_value, filterContext.getAuthnContextDeclRef());
}
};
*
* @param application reference to Application that owns the eventual Session
* @param issuer issuing metadata of assertion issuer, if known
+ * @param protocol protocol used to establish Session
* @param nameid principal identifier, normalized to SAML 2, if any
* @param authncontext_class method/category of authentication event, if known
* @param authncontext_decl specifics of authentication event, if known
- * @param tokens assertions initiating the session, if any
+ * @param tokens assertions initiating the Session, if any
* @param attributes map of previously resolved attributes, if any
* @return newly created ResolutionContext, owned by caller
*/
virtual ResolutionContext* createResolutionContext(
const Application& application,
const opensaml::saml2md::EntityDescriptor* issuer,
+ const XMLCh* protocol,
const opensaml::saml2::NameID* nameid,
- const char* authncontext_class=NULL,
- const char* authncontext_decl=NULL,
+ const XMLCh* authncontext_class=NULL,
+ const XMLCh* authncontext_decl=NULL,
const std::vector<const opensaml::Assertion*>* tokens=NULL,
const std::multimap<std::string,Attribute*>* attributes=NULL
) const=0;
ResolutionContext* createResolutionContext(
const Application& application,
const EntityDescriptor* issuer,
+ const XMLCh* protocol,
const NameID* nameid,
- const char* authncontext_class=NULL,
- const char* authncontext_decl=NULL,
+ const XMLCh* authncontext_class=NULL,
+ const XMLCh* authncontext_decl=NULL,
const vector<const opensaml::Assertion*>* tokens=NULL,
const multimap<string,shibsp::Attribute*>* attributes=NULL
) const {
auto_ptr<ChainingContext> chain(new ChainingContext());
for (vector<AttributeResolver*>::const_iterator i=m_resolvers.begin(); i!=m_resolvers.end(); ++i)
chain->m_contexts.push_back(
- (*i)->createResolutionContext(application, issuer, nameid, authncontext_class, authncontext_decl, tokens, attributes)
+ (*i)->createResolutionContext(application, issuer, protocol, nameid, authncontext_class, authncontext_decl, tokens, attributes)
);
return chain.release();
}
{
public:
QueryContext(const Application& application, const Session& session)
- : m_query(true), m_app(application), m_session(&session), m_metadata(NULL), m_entity(NULL), m_nameid(NULL) {
+ : m_query(true), m_app(application), m_session(&session), m_metadata(NULL), m_entity(NULL), m_nameid(NULL) {
+ m_protocol = XMLString::transcode(session.getProtocol());
+ m_class = XMLString::transcode(session.getAuthnContextClassRef());
+ m_decl = XMLString::transcode(session.getAuthnContextDeclRef());
}
QueryContext(
const Application& application,
const EntityDescriptor* issuer,
+ const XMLCh* protocol,
const NameID* nameid,
- const char* authncontext_class=NULL,
- const char* authncontext_decl=NULL,
+ const XMLCh* authncontext_class=NULL,
+ const XMLCh* authncontext_decl=NULL,
const vector<const opensaml::Assertion*>* tokens=NULL,
const multimap<string,Attribute*>* attributes=NULL
) : m_query(true), m_app(application), m_session(NULL), m_metadata(NULL), m_entity(issuer),
- m_nameid(nameid), m_class(authncontext_class), m_decl(authncontext_decl) {
+ m_protocol(protocol), m_nameid(nameid), m_class(authncontext_class), m_decl(authncontext_decl) {
if (tokens) {
for (vector<const opensaml::Assertion*>::const_iterator t = tokens->begin(); t!=tokens->end(); ++t) {
}
~QueryContext() {
+ if (m_session) {
+ XMLString::release((XMLCh**)&m_protocol);
+ XMLString::release((XMLCh**)&m_class);
+ XMLString::release((XMLCh**)&m_decl);
+ }
if (m_metadata)
m_metadata->unlock();
for_each(m_attributes.begin(), m_attributes.end(), cleanup_pair<string,shibsp::Attribute>());
}
return NULL;
}
+ const XMLCh* getProtocol() const {
+ return m_protocol;
+ }
const NameID* getNameID() const {
return m_session ? m_session->getNameID() : m_nameid;
}
- const char* getClassRef() const {
- return m_session ? m_session->getAuthnContextClassRef() : m_class;
+ const XMLCh* getClassRef() const {
+ return m_class;
}
- const char* getDeclRef() const {
- return m_session ? m_session->getAuthnContextDeclRef() : m_decl;
+ const XMLCh* getDeclRef() const {
+ return m_decl;
}
const Session* getSession() const {
return m_session;
const Session* m_session;
mutable MetadataProvider* m_metadata;
mutable const EntityDescriptor* m_entity;
+ const XMLCh* m_protocol;
const NameID* m_nameid;
- const char* m_class;
- const char* m_decl;
+ const XMLCh* m_class;
+ const XMLCh* m_decl;
multimap<string,shibsp::Attribute*> m_attributes;
vector<opensaml::Assertion*> m_assertions;
};
ResolutionContext* createResolutionContext(
const Application& application,
const EntityDescriptor* issuer,
+ const XMLCh* protocol,
const NameID* nameid,
- const char* authncontext_class=NULL,
- const char* authncontext_decl=NULL,
+ const XMLCh* authncontext_class=NULL,
+ const XMLCh* authncontext_decl=NULL,
const vector<const opensaml::Assertion*>* tokens=NULL,
const multimap<string,shibsp::Attribute*>* attributes=NULL
) const {
- return new QueryContext(application,issuer,nameid,authncontext_class,authncontext_decl,tokens,attributes);
+ return new QueryContext(application,issuer,protocol,nameid,authncontext_class,authncontext_decl,tokens,attributes);
}
ResolutionContext* createResolutionContext(const Application& application, const Session& session) const {
request->setAttributeQuery(query);
request->setMinorVersion(version);
- SAML1SOAPClient client(soaper);
+ SAML1SOAPClient client(soaper, false);
client.sendSAML(request, mcc, loc.get());
response = client.receiveSAML();
}
}
if (!response) {
- m_log.error("unable to successfully query for attributes");
+ m_log.error("unable to obtain a SAML response from attribute authority");
return false;
}
+ else if (!response->getStatus() || !response->getStatus()->getStatusCode() || response->getStatus()->getStatusCode()->getValue()==NULL ||\r
+ *(response->getStatus()->getStatusCode()->getValue()) != saml1p::StatusCode::SUCCESS) {\r
+ delete response;\r
+ m_log.error("attribute authority returned a SAML error");\r
+ return true;\r
+ }\r
const vector<saml1::Assertion*>& assertions = const_cast<const saml1p::Response*>(response)->getAssertions();
if (assertions.size()>1)
for (vector<saml2::Attribute*>::const_iterator ad = m_SAML2Designators.begin(); ad!=m_SAML2Designators.end(); ++ad)
query->getAttributes().push_back((*ad)->cloneAttribute());
- SAML2SOAPClient client(soaper);
+ SAML2SOAPClient client(soaper, false);
client.sendSAML(query, mcc, loc.get());
srt = client.receiveSAML();
}
}
if (!srt) {
- m_log.error("unable to successfully query for attributes");
+ m_log.error("unable to obtain a SAML response from attribute authority");
return false;
}
saml2p::Response* response = dynamic_cast<saml2p::Response*>(srt);
m_log.error("message was not a samlp:Response");
return true;
}
+ else if (!response->getStatus() || !response->getStatus()->getStatusCode() ||
+ !XMLString::equals(response->getStatus()->getStatusCode()->getValue(), saml2p::StatusCode::SUCCESS)) {
+ delete srt;
+ m_log.error("attribute authority returned a SAML error");
+ return true;
+ }
const vector<saml2::Assertion*>& assertions = const_cast<const saml2p::Response*>(response)->getAssertions();
if (assertions.size()>1)
request->getAssertionArtifacts().push_back(aa);
}
- SAML1SOAPClient client(soaper);
+ SAML1SOAPClient client(soaper, false);
client.sendSAML(request, mcc, loc.get());
response = client.receiveSAML();
}
}
if (!response)
- throw BindingException("Unable to successfully resolve artifact(s).");
+ throw BindingException("Unable to resolve artifact(s) into a SAML response.");
+ const QName* code = (response->getStatus() && response->getStatus()->getStatusCode()) ? response->getStatus()->getStatusCode()->getValue() : NULL;
+ if (!code || *code != saml1p::StatusCode::SUCCESS) {
+ delete response;
+ throw BindingException("Identity provider returned a SAML error in response to artifact(s).");
+ }
+
return response;
}
a->setArtifact(artbuf.get());
request->setArtifact(a);
- SAML2SOAPClient client(soaper);
+ SAML2SOAPClient client(soaper, false);
client.sendSAML(request, mcc, loc.get());
StatusResponseType* srt = client.receiveSAML();
if (!(response = dynamic_cast<ArtifactResponse*>(srt))) {
}
if (!response)
- throw BindingException("Unable to successfully resolve artifact.");
+ throw BindingException("Unable to resolve artifact(s) into a SAML response.");
+ if (!response->getStatus() || !response->getStatus()->getStatusCode() ||
+ !XMLString::equals(response->getStatus()->getStatusCode()->getValue(), saml2p::StatusCode::SUCCESS)) {
+ delete response;
+ throw BindingException("Identity provider returned a SAML error in response to artifact.");
+ }
return response;
}
*
* @param application reference to application receiving message
* @param issuer source of SSO tokens
+ * @param protocol SSO protocol used
* @param nameid identifier of principal
* @param authncontext_class method/category of authentication event, if known
* @param authncontext_decl specifics of authentication event, if known
ResolutionContext* resolveAttributes(
const Application& application,
const opensaml::saml2md::EntityDescriptor* issuer=NULL,
+ const XMLCh* protocol=NULL,
const opensaml::saml2::NameID* nameid=NULL,
- const char* authncontext_class=NULL,
- const char* authncontext_decl=NULL,
+ const XMLCh* authncontext_class=NULL,
+ const XMLCh* authncontext_decl=NULL,
const std::vector<const opensaml::Assertion*>* tokens=NULL,
const std::multimap<std::string,Attribute*>* attributes=NULL
) const;
/** Registers Handler implementations. */
void SHIBSP_API registerHandlers();
+
+ /** LogoutInitiator that iterates through a set of protocol-specific versions. */
+ #define CHAINING_LOGOUT_INITIATOR "Chaining"
+
+ /** LogoutInitiator that supports SAML 2.0 LogoutRequests. */
+ #define SAML2_LOGOUT_INITIATOR "SAML2"
+
+ /** LogoutInitiator that supports local-only logout. */
+ #define LOCAL_LOGOUT_INITIATOR "Local"
};
#endif /* __shibsp_handler_h__ */
--- /dev/null
+/*
+ * 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
+ *
+ * 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.
+ */
+
+/**
+ * @file shibsp/handler/LogoutHandler.h
+ *
+ * Base class for logout-related handlers.
+ */
+
+#ifndef __shibsp_logout_h__
+#define __shibsp_logout_h__
+
+#include <shibsp/SPRequest.h>
+#include <shibsp/handler/RemotedHandler.h>
+
+namespace shibsp {
+
+ /**
+ * Base class for logout-related handlers.
+ */
+ class SHIBSP_API LogoutHandler : public RemotedHandler
+ {
+ protected:
+ LogoutHandler() {}
+
+ public:
+ virtual ~LogoutHandler() {}
+
+ /**
+ * The base method will iteratively attempt front-channel notification
+ * of logout of the current session, and after the final round trip will
+ * perform back-channel notification. Nothing will be done unless the
+ * handler detects that it is the "top" level logout handler.
+ * If the method returns false, then the specialized class should perform
+ * its work assuming that the notifications are completed.
+ *
+ * Note that the current session is NOT removed from the cache.
+ *
+ * @param request SP request context
+ * @param isHandler true iff executing in the context of a direct handler invocation
+ * @return a pair containing a "request completed" indicator and a server-specific response code
+ */
+ std::pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
+
+ /**
+ * A remoted procedure that will perform any necessary back-channel
+ * notifications. The input structure must contain an "application_id" member,
+ * and a "sessions" list containing the session keys, along with an integer
+ * member called "notify" with a value of 1.
+ *
+ * @param in incoming DDF message
+ * @param out stream to write outgoing DDF message to
+ */
+ void receive(DDF& in, std::ostream& out);
+
+ protected:
+ /**
+ * Perform front-channel logout notifications for an Application.
+ *
+ * If no sessions are supplied directly, the request object's "sessions" query string
+ * parameter will be used.
+ *
+ * @param application the Application to notify
+ * @param request last request from browser
+ * @param response response to use for next notification
+ * @param params map of query string parameters to preserve across notifications, optionally with initial values
+ * @param sessions optional array of session keys being logged out
+ * @return indicator of a completed response along with the status code to return from the handler
+ */
+ std::pair<bool,long> notifyFrontChannel(
+ const Application& application,
+ const xmltooling::HTTPRequest& request,
+ xmltooling::HTTPResponse& response,
+ const std::map<std::string,std::string>* params=NULL,
+ const std::vector<std::string>* sessions=NULL
+ ) const;
+
+ /**
+ * Sends a response template to the user agent informing it of the results of a logout attempt.
+ *
+ * @param application the Application to use in determining the logout template
+ * @param response the HTTP response to use
+ * @param local true iff the logout operation was local to the SP, false iff global
+ * @param status optional logoutStatus key value to add to template
+ */
+ std::pair<bool,long> sendLogoutPage(
+ const Application& application, xmltooling::HTTPResponse& response, bool local=true, const char* status=NULL
+ ) const;
+
+ /**
+ * Perform back-channel logout notifications for an Application.
+ *
+ * @param application the Application to notify
+ * @param sessions array of session keys being logged out
+ * @return true iff all notifications succeeded
+ */
+ bool notifyBackChannel(const Application& application, const std::vector<std::string>& sessions) const;
+ };
+
+};
+
+#endif /* __shibsp_logout_h__ */
SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2ConsumerFactory;
SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2ArtifactResolutionFactory;
SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory AssertionLookupFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory ChainingLogoutInitiatorFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory LocalLogoutInitiatorFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2LogoutInitiatorFactory;
};
void SHIBSP_API shibsp::registerHandlers()
conf.ArtifactResolutionServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2ArtifactResolutionFactory);
conf.HandlerManager.registerFactory(SAML20_BINDING_URI, AssertionLookupFactory);
+
+ conf.LogoutInitiatorManager.registerFactory(CHAINING_LOGOUT_INITIATOR, ChainingLogoutInitiatorFactory);
+ conf.LogoutInitiatorManager.registerFactory(LOCAL_LOGOUT_INITIATOR, LocalLogoutInitiatorFactory);
+ conf.LogoutInitiatorManager.registerFactory(SAML2_LOGOUT_INITIATOR, SAML2LogoutInitiatorFactory);
}
AbstractHandler::AbstractHandler(
if (role && flag.first &&
(!strcmp(flag.second, "true") ||
(encoder.isUserAgentPresent() && !strcmp(flag.second, "front")) ||
- ((!encoder.isUserAgentPresent() && !strcmp(flag.second, "back"))))) {
+ (!encoder.isUserAgentPresent() && !strcmp(flag.second, "back")))) {
CredentialResolver* credResolver=application.getCredentialResolver();
if (credResolver) {
Locker credLocker(credResolver);
m_log.warn("no signing credential resolved, leaving message unsigned");
}
}
+ else {
+ m_log.warn("no credential resolver installed, leaving message unsigned");
+ }
}
// Unsigned request.
ResolutionContext* AssertionConsumerService::resolveAttributes(
const Application& application,
const saml2md::EntityDescriptor* issuer,
+ const XMLCh* protocol,
const saml2::NameID* nameid,
- const char* authncontext_class,
- const char* authncontext_decl,
+ const XMLCh* authncontext_class,
+ const XMLCh* authncontext_decl,
const vector<const Assertion*>* tokens,
const multimap<string,Attribute*>* attributes
) const
Locker locker(resolver);
auto_ptr<ResolutionContext> ctx(
- resolver->createResolutionContext(application, issuer, nameid, authncontext_class, authncontext_decl, tokens, attributes)
+ resolver->createResolutionContext(application, issuer, protocol, nameid, authncontext_class, authncontext_decl, tokens, attributes)
);
resolver->resolveAttributes(*ctx.get());
return ctx.release();
--- /dev/null
+/*
+ * 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
+ *
+ * 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.
+ */
+
+/**
+ * ChainingLogoutInitiator.cpp
+ *
+ * Chains together multiple LogoutInitiator handlers in sequence.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "handler/AbstractHandler.h"
+#include "handler/LogoutHandler.h"
+#include "util/SPConstants.h"
+
+#include <xercesc/util/XMLUniDefs.hpp>
+#include <xmltooling/util/XMLHelper.h>
+
+using namespace shibsp;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace shibsp {
+
+#if defined (_MSC_VER)
+ #pragma warning( push )
+ #pragma warning( disable : 4250 )
+#endif
+
+ class SHIBSP_DLLLOCAL ChainingLogoutInitiator : public AbstractHandler, public LogoutHandler
+ {
+ public:
+ ChainingLogoutInitiator(const DOMElement* e, const char* appId);
+ virtual ~ChainingLogoutInitiator() {
+ for_each(m_handlers.begin(), m_handlers.end(), xmltooling::cleanup<Handler>());
+ }
+
+ pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
+
+ private:
+ vector<Handler*> m_handlers;
+ };
+
+#if defined (_MSC_VER)
+ #pragma warning( pop )
+#endif
+
+ static const XMLCh _LogoutInitiator[] = UNICODE_LITERAL_15(L,o,g,o,u,t,I,n,i,t,i,a,t,o,r);
+ static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
+
+ class SHIBSP_DLLLOCAL LogoutInitiatorNodeFilter : public DOMNodeFilter
+ {
+ public:
+ short acceptNode(const DOMNode* node) const {
+ if (XMLHelper::isNodeNamed(node,shibspconstants::SHIB2SPCONFIG_NS,_LogoutInitiator))
+ return FILTER_REJECT;
+ return FILTER_ACCEPT;
+ }
+ };
+
+ static SHIBSP_DLLLOCAL LogoutInitiatorNodeFilter g_LINFilter;
+
+ Handler* SHIBSP_DLLLOCAL ChainingLogoutInitiatorFactory(const pair<const DOMElement*,const char*>& p)
+ {
+ return new ChainingLogoutInitiator(p.first, p.second);
+ }
+};
+
+ChainingLogoutInitiator::ChainingLogoutInitiator(const DOMElement* e, const char* appId)
+ : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".LogoutInitiator"), &g_LINFilter)
+{
+ SPConfig& conf = SPConfig::getConfig();
+
+ // Load up the chain of handlers.
+ e = e ? XMLHelper::getFirstChildElement(e, _LogoutInitiator) : NULL;
+ while (e) {
+ auto_ptr_char type(e->getAttributeNS(NULL,_type));
+ if (type.get() && *(type.get())) {
+ try {
+ m_handlers.push_back(conf.LogoutInitiatorManager.newPlugin(type.get(),make_pair(e, appId)));
+ m_handlers.back()->setParent(this);
+ }
+ catch (exception& ex) {
+ m_log.error("caught exception processing embedded SessionInitiator element: %s", ex.what());
+ }
+ }
+ e = XMLHelper::getNextSiblingElement(e, _LogoutInitiator);
+ }
+
+ pair<bool,const char*> loc = getString("Location");
+ if (loc.first) {
+ string address = string(appId) + loc.second + "::run::ChainingLI";
+ setAddress(address.c_str());
+ }
+}
+
+pair<bool,long> ChainingLogoutInitiator::run(SPRequest& request, bool isHandler) const
+{
+ // Defer to base class first.
+ pair<bool,long> ret = LogoutHandler::run(request, isHandler);
+ if (ret.first)
+ return ret;
+
+ for (vector<Handler*>::const_iterator i = m_handlers.begin(); i!=m_handlers.end(); ++i) {
+ ret = (*i)->run(request, isHandler);
+ if (ret.first)
+ return ret;
+ }
+ throw ConfigurationException("None of the configured LogoutInitiators handled the request.");
+}
--- /dev/null
+/*
+ * 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
+ *
+ * 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.
+ */
+
+/**
+ * LogoutHandler.cpp
+ *
+ * Base class for logout-related handlers.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "Application.h"
+#include "ServiceProvider.h"
+#include "SessionCache.h"
+#include "handler/LogoutHandler.h"
+#include "util/TemplateParameters.h"
+
+#include <fstream>
+#include <log4cpp/Category.hh>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/URLEncoder.h>
+
+#ifndef SHIBSP_LITE
+#endif
+
+using namespace shibsp;
+using namespace xmltooling;
+using namespace std;
+
+pair<bool,long> LogoutHandler::run(SPRequest& request, bool isHandler) const
+{
+ // If no location for this handler, we're inside a chain, so do nothing.
+ if (!getString("Location").first)
+ return make_pair(false,0);
+
+ string session_id;
+ try {
+ // Get the session, ignoring most checks and don't cache the lock.
+ Session* session = request.getSession(false,true,false);
+ if (session)
+ session_id = session->getID();
+ session->unlock();
+ }
+ catch (exception& ex) {
+ log4cpp::Category::getInstance(SHIBSP_LOGCAT".Logout").error("error accessing current session: %s", ex.what());
+ }
+
+ if (session_id.empty())
+ return sendLogoutPage(request.getApplication(), request, true, "The local logout process was only partially completed.");
+
+ // Try another front-channel notification. No extra parameters and the session is implicit.
+ pair<bool,long> ret = notifyFrontChannel(request.getApplication(), request, request);
+ if (ret.first)
+ return ret;
+
+ // Now we complete with back-channel notification, which has to be out of process.
+
+ if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+ // When out of process, we run natively.
+ vector<string> sessions(1,session_id);
+ notifyBackChannel(request.getApplication(), sessions);
+ }
+ else {
+ // When not out of process, we remote the back channel work.
+ DDF out,in(m_address.c_str());
+ DDFJanitor jin(in), jout(out);
+ in.addmember("notify").integer(1);
+ in.addmember("application_id").string(request.getApplication().getId());
+ DDF s = DDF(NULL).string(session_id.c_str());
+ in.addmember("sessions").list().add(s);
+ out=request.getServiceProvider().getListenerService()->send(in);
+ }
+
+ return make_pair(false,0);
+}
+
+void LogoutHandler::receive(DDF& in, ostream& out)
+{
+ DDF ret(NULL);
+ DDFJanitor jout(ret);
+ if (in["notify"].integer() != 1)
+ throw ListenerException("Unsupported operation.");
+
+ // Find application.
+ const char* aid=in["application_id"].string();
+ const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL;
+ if (!app) {
+ // Something's horribly wrong.
+ log4cpp::Category::getInstance(SHIBSP_LOGCAT".Logout").error("couldn't find application (%s) for logout", aid ? aid : "(missing)");
+ throw ConfigurationException("Unable to locate application for logout, deleted?");
+ }
+
+ vector<string> sessions;
+ DDF s = in["sessions"];
+ DDF temp = s.first();
+ while (temp.isstring()) {
+ sessions.push_back(temp.string());
+ temp = s.next();
+ notifyBackChannel(*app, sessions);
+ }
+
+ out << ret;
+}
+
+pair<bool,long> LogoutHandler::notifyFrontChannel(
+ const Application& application,
+ const HTTPRequest& request,
+ HTTPResponse& response,
+ const map<string,string>* params,
+ const vector<string>* sessions
+ ) const
+{
+ // Index of notification point starts at 0.
+ unsigned int index = 0;
+ const char* param = request.getParameter("index");
+ if (param)
+ index = atoi(param);
+
+ // Fetch the next front notification URL and bump the index for the next round trip.
+ string loc = application.getNotificationURL(request, true, index++);
+ if (loc.empty())
+ return make_pair(false,0);
+
+ const URLEncoder* encoder = XMLToolingConfig::getConfig().getURLEncoder();
+
+ // Start with an "action" telling the application what this is about.
+ loc = loc + (strchr(loc.c_str(),'?') ? '&' : '?') + "action=logout";
+
+ // Attach the "sessions" parameter, if any.
+ if (sessions) {
+ string keys;
+ for (vector<string>::const_iterator k = sessions->begin(); k!=sessions->end(); ++k) {
+ if (!keys.empty())
+ keys += ',';
+ keys += *k;
+ }
+ loc = loc + "&sessions=" + keys;
+ }
+ else if (param = request.getParameter("sessions")) {
+ loc = loc + "&sessions=" + request.getParameter("sessions");
+ }
+
+ // Now we create a second URL representing the return location back to us.
+ const char* start = request.getRequestURL();
+ const char* end = strchr(start,'?');
+ string tempstr(start, end ? end-start : strlen(start));
+ ostringstream locstr(tempstr);
+
+ // Add a signal that we're coming back from notification and the next index.
+ locstr << "?notifying=1&index=" << index;
+
+ // Now we preserve anything we're instructed to (or populate the initial values from the map).
+ if (params) {
+ for (map<string,string>::const_iterator p = params->begin(); p!=params->end(); ++p) {
+ if (!p->second.empty())
+ locstr << '&' << p->first << '=' << encoder->encode(p->second.c_str());
+ else if (param = request.getParameter(p->first.c_str()))
+ locstr << '&' << p->first << '=' << encoder->encode(param);
+ }
+ }
+
+ // Add the return parameter to the destination location and redirect.
+ loc = loc + "&return=" + encoder->encode(locstr.str().c_str());
+ return make_pair(true,response.sendRedirect(loc.c_str()));
+}
+
+bool LogoutHandler::notifyBackChannel(const Application& application, const vector<string>& sessions) const
+{
+#ifndef SHIBSP_LITE
+ return false;
+#else
+ throw ConfigurationException("Cannot perform back channel notification using lite version of shibsp library.");
+#endif
+}
+
+pair<bool,long> LogoutHandler::sendLogoutPage(const Application& application, HTTPResponse& response, bool local, const char* status) const
+{
+ pair<bool,const char*> prop = application.getString(local ? "localLogout" : "globalLogout");
+ if (prop.first) {
+ response.setContentType("text/html");
+ response.setResponseHeader("Expires","01-Jan-1997 12:00:00 GMT");
+ response.setResponseHeader("Cache-Control","private,no-store,no-cache");
+ ifstream infile(prop.second);
+ if (!infile)
+ throw ConfigurationException("Unable to access $1 HTML template.", params(1,local ? "localLogout" : "globalLogout"));
+ TemplateParameters tp;
+ tp.setPropertySet(application.getPropertySet("Errors"));
+ if (status)
+ tp.m_map["logoutStatus"] = status;
+ stringstream str;
+ XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, tp);
+ return make_pair(true,response.sendResponse(str));
+ }
+ prop = application.getString("homeURL");
+ if (!prop.first)
+ prop.second = "/";
+ return make_pair(true,response.sendRedirect(prop.second));
+}
pair<bool,unsigned int> lifetime = sessionProps ? sessionProps->getUnsignedInt("lifetime") : pair<bool,unsigned int>(true,28800);
if (!lifetime.first || lifetime.second == 0)
lifetime.second = 28800;
- auto_ptr_char authnInstant(
- ssoStatement->getAuthenticationInstant() ? ssoStatement->getAuthenticationInstant()->getRawData() : NULL
- );
- auto_ptr_char authnMethod(ssoStatement->getAuthenticationMethod());
// We've successfully "accepted" at least one SSO token, along with any additional valid tokens.
// To complete processing, we need to extract and resolve attributes and then create the session.
AttributeFilter* filter = application.getAttributeFilter();
if (filter && !resolvedAttributes.empty()) {
- BasicFilteringContext fc(application, resolvedAttributes, policy.getIssuerMetadata(), authnMethod.get());
+ BasicFilteringContext fc(application, resolvedAttributes, policy.getIssuerMetadata(), ssoStatement->getAuthenticationMethod());
Locker filtlocker(filter);
try {
filter->filterAttributes(fc, resolvedAttributes);
const EntityDescriptor* issuerMetadata =
policy.getIssuerMetadata() ? dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent()) : NULL;
auto_ptr<ResolutionContext> ctx(
- resolveAttributes(application, issuerMetadata, nameid.get(), authnMethod.get(), NULL, &tokens, &resolvedAttributes)
+ resolveAttributes(
+ application,
+ issuerMetadata,
+ (!response->getMinorVersion().first || response->getMinorVersion().second==1) ?
+ samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM,
+ nameid.get(),
+ ssoStatement->getAuthenticationMethod(),
+ NULL,
+ &tokens,
+ &resolvedAttributes
+ )
);
if (ctx.get()) {
application,
httpRequest.getRemoteAddr().c_str(),
issuerMetadata,
+ (!response->getMinorVersion().first || response->getMinorVersion().second==1) ?
+ samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM,
nameid.get(),
- authnInstant.get(),
+ ssoStatement->getAuthenticationInstant() ? ssoStatement->getAuthenticationInstant()->getRawData() : NULL,
NULL,
- authnMethod.get(),
+ ssoStatement->getAuthenticationMethod(),
NULL,
&tokens,
&resolvedAttributes
else
sessionExp = min(sessionExp, now + lifetime.second); // Use the lowest.
- // Other details...
- const AuthnContext* authnContext = ssoStatement->getAuthnContext();
- auto_ptr_char authnClass((authnContext && authnContext->getAuthnContextClassRef()) ? authnContext->getAuthnContextClassRef()->getReference() : NULL);
- auto_ptr_char authnDecl((authnContext && authnContext->getAuthnContextDeclRef()) ? authnContext->getAuthnContextDeclRef()->getReference() : NULL);
- auto_ptr_char index(ssoStatement->getSessionIndex());
- auto_ptr_char authnInstant(ssoStatement->getAuthnInstant() ? ssoStatement->getAuthnInstant()->getRawData() : NULL);
-
multimap<string,Attribute*> resolvedAttributes;
AttributeExtractor* extractor = application.getAttributeExtractor();
if (extractor) {
}
}
+ const AuthnContext* authnContext = ssoStatement->getAuthnContext();
+
AttributeFilter* filter = application.getAttributeFilter();
if (filter && !resolvedAttributes.empty()) {
- BasicFilteringContext fc(application, resolvedAttributes, policy.getIssuerMetadata(), authnClass.get(), authnDecl.get());
+ BasicFilteringContext fc(
+ application,
+ resolvedAttributes,
+ policy.getIssuerMetadata(),
+ (authnContext && authnContext->getAuthnContextClassRef()) ? authnContext->getAuthnContextClassRef()->getReference() : NULL,
+ (authnContext && authnContext->getAuthnContextDeclRef()) ? authnContext->getAuthnContextDeclRef()->getReference() : NULL
+ );
Locker filtlocker(filter);
try {
filter->filterAttributes(fc, resolvedAttributes);
const EntityDescriptor* issuerMetadata =
policy.getIssuerMetadata() ? dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent()) : NULL;
auto_ptr<ResolutionContext> ctx(
- resolveAttributes(application, issuerMetadata, ssoName, authnClass.get(), authnDecl.get(), &tokens, &resolvedAttributes)
+ resolveAttributes(
+ application,
+ issuerMetadata,
+ samlconstants::SAML20P_NS,
+ ssoName,
+ (authnContext && authnContext->getAuthnContextClassRef()) ? authnContext->getAuthnContextClassRef()->getReference() : NULL,
+ (authnContext && authnContext->getAuthnContextDeclRef()) ? authnContext->getAuthnContextDeclRef()->getReference() : NULL,
+ &tokens,
+ &resolvedAttributes
+ )
);
if (ctx.get()) {
application,
httpRequest.getRemoteAddr().c_str(),
issuerMetadata,
+ samlconstants::SAML20P_NS,
ssoName,
- authnInstant.get(),
- index.get(),
- authnClass.get(),
- authnDecl.get(),
+ ssoStatement->getAuthnInstant() ? ssoStatement->getAuthnInstant()->getRawData() : NULL,
+ ssoStatement->getSessionIndex(),
+ (authnContext && authnContext->getAuthnContextClassRef()) ? authnContext->getAuthnContextClassRef()->getReference() : NULL,
+ (authnContext && authnContext->getAuthnContextDeclRef()) ? authnContext->getAuthnContextDeclRef()->getReference() : NULL,
&tokens,
&resolvedAttributes
);
const char* getEntityID() const {\r
return m_obj["entity_id"].string();\r
}\r
+ const char* getProtocol() const {\r
+ return m_obj["protocol"].string();\r
+ }\r
const char* getAuthnInstant() const {\r
return m_obj["authn_instant"].string();\r
}\r
const char* getEntityID() const {\r
return m_obj["entity_id"].string();\r
}\r
+ const char* getProtocol() const {\r
+ return m_obj["protocol"].string();\r
+ }\r
const char* getAuthnInstant() const {\r
return m_obj["authn_instant"].string();\r
}\r
const Application& application,\r
const char* client_addr=NULL,\r
const saml2md::EntityDescriptor* issuer=NULL,\r
+ const XMLCh* protocol=NULL,\r
const saml2::NameID* nameid=NULL,\r
- const char* authn_instant=NULL,\r
- const char* session_index=NULL,\r
- const char* authncontext_class=NULL,\r
- const char* authncontext_decl=NULL,\r
+ const XMLCh* authn_instant=NULL,\r
+ const XMLCh* session_index=NULL,\r
+ const XMLCh* authncontext_class=NULL,\r
+ const XMLCh* authncontext_decl=NULL,\r
const vector<const Assertion*>* tokens=NULL,\r
const multimap<string,Attribute*>* attributes=NULL\r
);\r
Session* find(const char* key, const Application& application, const char* client_addr=NULL, time_t* timeout=NULL);\r
void remove(const char* key, const Application& application);\r
- void find(\r
- const saml2md::EntityDescriptor& issuer,\r
- const saml2::NameID& nameid,\r
- const char* index,\r
- const Application& application,\r
- vector<string>& sessions\r
- ) {\r
- byname(issuer, nameid, index, application, sessions, false);\r
- }\r
void remove(\r
- const saml2md::EntityDescriptor& issuer,\r
+ const saml2md::EntityDescriptor* issuer,\r
const saml2::NameID& nameid,\r
const char* index,\r
const Application& application,\r
vector<string>& sessions\r
- ) {\r
- byname(issuer, nameid, index, application, sessions, true);\r
- }\r
+ );\r
\r
Category& m_log;\r
StorageService* m_storage;\r
private:\r
// maintain back-mappings of NameID/SessionIndex -> session key\r
void insert(const char* key, time_t expires, const char* name, const char* index);\r
- void byname(\r
- const saml2md::EntityDescriptor& issuer,\r
- const saml2::NameID& nameid,\r
- const char* index,\r
- const Application& application,\r
- vector<string>& sessions,\r
- bool logout\r
- );\r
\r
bool stronglyMatches(const XMLCh* idp, const XMLCh* sp, const saml2::NameID& n1, const saml2::NameID& n2) const;\r
};\r
// Since we can't guarantee uniqueness, check for an existing record.\r
string record;\r
time_t recordexp;\r
- int ver = m_storage->readText("Logout", name, &record, &recordexp);\r
+ int ver = m_storage->readText("NameID", name, &record, &recordexp);\r
if (ver > 0) {\r
// Existing record, so we need to unmarshall it.\r
istringstream in(record);\r
obj.structure();\r
}\r
\r
- if (!index)\r
+ if (!index || !*index)\r
index = "_shibnull";\r
DDF sessions = obj.addmember(index);\r
if (!sessions.islist())\r
\r
// Try and store it back...\r
if (ver > 0) {\r
- ver = m_storage->updateText("Logout", name, out.str().c_str(), max(expires, recordexp), ver);\r
+ ver = m_storage->updateText("NameID", name, out.str().c_str(), max(expires, recordexp), ver);\r
if (ver <= 0) {\r
// Out of sync, or went missing, so retry.\r
return insert(key, expires, name, index);\r
}\r
}\r
- else if (!m_storage->createText("Logout", name, out.str().c_str(), expires)) {\r
+ else if (!m_storage->createText("NameID", name, out.str().c_str(), expires)) {\r
// Hit a dup, so just retry, hopefully hitting the other branch.\r
return insert(key, expires, name, index);\r
}\r
const Application& application,\r
const char* client_addr,\r
const saml2md::EntityDescriptor* issuer,\r
+ const XMLCh* protocol,\r
const saml2::NameID* nameid,\r
- const char* authn_instant,\r
- const char* session_index,\r
- const char* authncontext_class,\r
- const char* authncontext_decl,\r
+ const XMLCh* authn_instant,\r
+ const XMLCh* session_index,\r
+ const XMLCh* authncontext_class,\r
+ const XMLCh* authncontext_decl,\r
const vector<const Assertion*>* tokens,\r
const multimap<string,Attribute*>* attributes\r
)\r
auto_ptr_char entity_id(issuer->getEntityID());\r
obj.addmember("entity_id").string(entity_id.get());\r
}\r
- if (authn_instant)\r
- obj.addmember("authn_instant").string(authn_instant);\r
+ if (protocol) {\r
+ auto_ptr_char prot(protocol);\r
+ obj.addmember("protocol").string(prot.get());\r
+ }\r
+ if (authn_instant) {\r
+ auto_ptr_char instant(authn_instant);\r
+ obj.addmember("authn_instant").string(instant.get());\r
+ }\r
+ auto_ptr_char index(session_index);\r
if (session_index)\r
- obj.addmember("session_index").string(session_index);\r
- if (authncontext_class)\r
- obj.addmember("authncontext_class").string(authncontext_class);\r
- if (authncontext_decl)\r
- obj.addmember("authncontext_decl").string(authncontext_decl);\r
+ obj.addmember("session_index").string(index.get());\r
+ if (authncontext_class) {\r
+ auto_ptr_char ac(authncontext_class);\r
+ obj.addmember("authncontext_class").string(ac.get());\r
+ }\r
+ if (authncontext_decl) {\r
+ auto_ptr_char ad(authncontext_decl);\r
+ obj.addmember("authncontext_decl").string(ad.get());\r
+ }\r
\r
if (nameid) {\r
ostringstream namestr;\r
auto_ptr_char name(nameid ? nameid->getName() : NULL);\r
try {\r
if (name.get())\r
- insert(key.get(), expires, name.get(), session_index);\r
+ insert(key.get(), expires, name.get(), index.get());\r
}\r
catch (exception& ex) {\r
m_log.error("error storing back mapping of NameID for logout: %s", ex.what());\r
xlog->log.info("Destroyed session (applicationId: %s) (ID: %s)", application.getId(), key);\r
}\r
\r
-void SSCache::byname(\r
- const saml2md::EntityDescriptor& issuer,\r
+void SSCache::remove(\r
+ const saml2md::EntityDescriptor* issuer,\r
const saml2::NameID& nameid,\r
const char* index,\r
const Application& application,\r
- vector<string>& sessionsKilled,\r
- bool logout\r
+ vector<string>& sessionsKilled\r
)\r
{\r
#ifdef _DEBUG\r
xmltooling::NDC ndc("remove");\r
#endif\r
\r
- auto_ptr_char entityID(issuer.getEntityID());\r
+ auto_ptr_char entityID(issuer ? issuer->getEntityID() : NULL);\r
auto_ptr_char name(nameid.getName());\r
\r
m_log.info(\r
- "request to %s sessions from (%s) for (%s) for session index (%s)",\r
- logout ? "logout" : "locate", entityID.get(), name.get(), index ? index : "all"\r
+ "request to logout sessions from (%s) for (%s) for session index (%s)",\r
+ entityID.get() ? entityID.get() : "unknown", name.get(), index ? index : "all"\r
);\r
\r
if (strlen(name.get()) > 255)\r
\r
// Read in potentially matching sessions.\r
string record;\r
- int ver = m_storage->readText("Logout", name.get(), &record);\r
+ int ver = m_storage->readText("NameID", name.get(), &record);\r
if (ver == 0) {\r
m_log.debug("no active sessions to remove for supplied issuer and name identifier");\r
return;\r
Locker locker(session);\r
if (session) {\r
// Same issuer?\r
- if (session->getEntityID() && !strcmp(session->getEntityID(), entityID.get())) {\r
+ if (XMLString::equals(session->getEntityID(), entityID.get())) {\r
// Same NameID?\r
- if (stronglyMatches(issuer.getEntityID(), application.getXMLString("entityID").second, nameid, *session->getNameID())) {\r
- if (logout) {\r
- remove(key.string(), application); // let this throw to detect errors in case the full logout fails?\r
- key.destroy();\r
- }\r
+ if (stronglyMatches(issuer->getEntityID(), application.getXMLString("entityID").second, nameid, *session->getNameID())) {\r
sessionsKilled.push_back(key.string());\r
+ remove(key.string(), application); // let this throw to detect errors in case the full logout fails?\r
+ key.destroy();\r
}\r
else {\r
m_log.debug("session (%s) contained a non-matching NameID, leaving it alone", key.string());\r
// If possible, write back the mapping record (this isn't crucial).\r
try {\r
if (obj.isnull()) {\r
- m_storage->deleteText("Logout", name.get());\r
+ m_storage->deleteText("NameID", name.get());\r
}\r
else if (!sessionsKilled.empty()) {\r
ostringstream out;\r
out << obj;\r
- if (m_storage->updateText("Logout", name.get(), out.str().c_str(), 0, ver) <= 0)\r
+ if (m_storage->updateText("NameID", name.get(), out.str().c_str(), 0, ver) <= 0)\r
m_log.warn("logout mapping record changed behind us, leaving it alone");\r
}\r
}\r
static const XMLCh Listener[] = UNICODE_LITERAL_8(L,i,s,t,e,n,e,r);
static const XMLCh Location[] = UNICODE_LITERAL_8(L,o,c,a,t,i,o,n);
static const XMLCh logger[] = UNICODE_LITERAL_6(l,o,g,g,e,r);
+ static const XMLCh _LogoutInitiator[] = UNICODE_LITERAL_15(L,o,g,o,u,t,I,n,i,t,i,a,t,o,r);
static const XMLCh _ManageNameIDService[] = UNICODE_LITERAL_19(M,a,n,a,g,e,N,a,m,e,I,D,S,e,r,v,i,c,e);
static const XMLCh MemoryListener[] = UNICODE_LITERAL_14(M,e,m,o,r,y,L,i,s,t,e,n,e,r);
static const XMLCh _MetadataProvider[] = UNICODE_LITERAL_16(M,e,t,a,d,a,t,a,P,r,o,v,i,d,e,r);
m_sessionInitDefault=sihandler;
}
}
+ else if (XMLString::equals(child->getLocalName(),_LogoutInitiator)) {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ if (!type.get() || !*(type.get())) {
+ log.warn("LogoutInitiator element has no type attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler=conf.LogoutInitiatorManager.newPlugin(type.get(),make_pair(child, getId()));
+ }
else if (XMLString::equals(child->getLocalName(),_ArtifactResolutionService)) {
auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));
if (!bindprop.get() || !*(bindprop.get())) {
XMLString::equals(name,Notify) ||
XMLString::equals(name,_AssertionConsumerService) ||
XMLString::equals(name,_ArtifactResolutionService) ||
- XMLString::equals(name,_SingleLogoutService) ||
+ XMLString::equals(name,_LogoutInitiator) ||
XMLString::equals(name,_ManageNameIDService) ||
XMLString::equals(name,_SessionInitiator) ||
+ XMLString::equals(name,_SingleLogoutService) ||
XMLString::equals(name,DefaultRelyingParty) ||
XMLString::equals(name,RelyingParty) ||
XMLString::equals(name,_MetadataProvider) ||