+ // Look for "shorthand" elements first.
+ set<string> protocols;
+ DOMElement* child = sessions ? XMLHelper::getFirstChildElement(sessions->getElement()) : nullptr;
+ while (child) {
+ if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2SPCONFIG_NS, SSO)) {
+ if (pp)
+ doSSO(*pp, protocols, child, log);
+ else
+ log.error("no ProtocolProvider, SSO auto-configure unsupported");
+ }
+ else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2SPCONFIG_NS, Logout)) {
+ if (pp)
+ doLogout(*pp, protocols, child, log);
+ else
+ log.error("no ProtocolProvider, Logout auto-configure unsupported");
+ }
+ else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2SPCONFIG_NS, NameIDMgmt)) {
+ if (pp)
+ doNameIDMgmt(*pp, protocols, child, log);
+ else
+ log.error("no ProtocolProvider, NameIDMgmt auto-configure unsupported");
+ }
+ else {
+ break; // drop into next while loop
+ }
+ child = XMLHelper::getNextSiblingElement(child);
+ }
+
+ // Process other handlers.
+ bool hardACS=false, hardSessionInit=false, hardArt=false;
+ while (child) {
+ if (!child->hasAttributeNS(nullptr, Location)) {
+ auto_ptr_char hclass(child->getLocalName());
+ log.error("%s handler with no Location property cannot be processed", hclass.get());
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ try {
+ boost::shared_ptr<Handler> handler;
+ if (XMLString::equals(child->getLocalName(), _AssertionConsumerService)) {
+ string bindprop(XMLHelper::getAttrString(child, nullptr, Binding));
+ if (bindprop.empty()) {
+ log.error("AssertionConsumerService element has no Binding attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler.reset(conf.AssertionConsumerServiceManager.newPlugin(bindprop.c_str(), pair<const DOMElement*,const char*>(child, getId())));
+ // Map by binding and protocol (may be > 1 per protocol and binding)
+ m_acsBindingMap[handler->getXMLString("Binding").second].push_back(handler.get());
+ const XMLCh* protfamily = handler->getProtocolFamily();
+ if (protfamily)
+ m_acsProtocolMap[protfamily].push_back(handler.get());
+ m_acsIndexMap[handler->getUnsignedInt("index").second] = handler.get();
+
+ if (!hardACS) {
+ pair<bool,bool> defprop = handler->getBool("isDefault");
+ if (defprop.first) {
+ if (defprop.second) {
+ hardACS = true;
+ m_acsDefault = handler.get();
+ }
+ }
+ else if (!m_acsDefault)
+ m_acsDefault = handler.get();
+ }
+ }
+ else if (XMLString::equals(child->getLocalName(), _SessionInitiator)) {
+ string t(XMLHelper::getAttrString(child, nullptr, _type));
+ if (t.empty()) {
+ log.error("SessionInitiator element has no type attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ boost::shared_ptr<SessionInitiator> sihandler(
+ conf.SessionInitiatorManager.newPlugin(t.c_str(), pair<const DOMElement*,const char*>(child, getId()))
+ );
+ handler = sihandler;
+ pair<bool,const char*> si_id = handler->getString("id");
+ if (si_id.first && si_id.second)
+ m_sessionInitMap[si_id.second] = sihandler.get();
+ if (!hardSessionInit) {
+ pair<bool,bool> defprop = handler->getBool("isDefault");
+ if (defprop.first) {
+ if (defprop.second) {
+ hardSessionInit = true;
+ m_sessionInitDefault = sihandler.get();
+ }
+ }
+ else if (!m_sessionInitDefault) {
+ m_sessionInitDefault = sihandler.get();
+ }
+ }
+ }
+ else if (XMLString::equals(child->getLocalName(), _LogoutInitiator)) {
+ string t(XMLHelper::getAttrString(child, nullptr, _type));
+ if (t.empty()) {
+ log.error("LogoutInitiator element has no type attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler.reset(conf.LogoutInitiatorManager.newPlugin(t.c_str(), pair<const DOMElement*,const char*>(child, getId())));
+ }
+ else if (XMLString::equals(child->getLocalName(), _ArtifactResolutionService)) {
+ string bindprop(XMLHelper::getAttrString(child, nullptr, Binding));
+ if (bindprop.empty()) {
+ log.error("ArtifactResolutionService element has no Binding attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler.reset(conf.ArtifactResolutionServiceManager.newPlugin(bindprop.c_str(), pair<const DOMElement*,const char*>(child, getId())));
+
+ if (!hardArt) {
+ pair<bool,bool> defprop = handler->getBool("isDefault");
+ if (defprop.first) {
+ if (defprop.second) {
+ hardArt = true;
+ m_artifactResolutionDefault = handler.get();
+ }
+ }
+ else if (!m_artifactResolutionDefault)
+ m_artifactResolutionDefault = handler.get();
+ }
+ }
+ else if (XMLString::equals(child->getLocalName(), _SingleLogoutService)) {
+ string bindprop(XMLHelper::getAttrString(child, nullptr, Binding));
+ if (bindprop.empty()) {
+ log.error("SingleLogoutService element has no Binding attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler.reset(conf.SingleLogoutServiceManager.newPlugin(bindprop.c_str(), pair<const DOMElement*,const char*>(child, getId())));
+ }
+ else if (XMLString::equals(child->getLocalName(), _ManageNameIDService)) {
+ string bindprop(XMLHelper::getAttrString(child, nullptr, Binding));
+ if (bindprop.empty()) {
+ log.error("ManageNameIDService element has no Binding attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler.reset(conf.ManageNameIDServiceManager.newPlugin(bindprop.c_str(), pair<const DOMElement*,const char*>(child, getId())));
+ }
+ else {
+ string t(XMLHelper::getAttrString(child, nullptr, _type));
+ if (t.empty()) {
+ log.error("Handler element has no type attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler.reset(conf.HandlerManager.newPlugin(t.c_str(), pair<const DOMElement*,const char*>(child, getId())));
+ }
+
+ m_handlers.push_back(handler);
+
+ // Insert into location map.
+ location = handler->getString("Location");
+ if (location.first && *location.second == '/')
+ m_handlerMap[location.second] = handler.get();
+ else if (location.first)
+ m_handlerMap[string("/") + location.second] = handler.get();
+ }
+ catch (std::exception& ex) {
+ log.error("caught exception processing handler element: %s", ex.what());
+ }
+
+ child = XMLHelper::getNextSiblingElement(child);
+ }
+}
+
+void XMLApplication::doSSO(const ProtocolProvider& pp, set<string>& protocols, DOMElement* e, Category& log)
+{
+ if (!e->hasChildNodes())
+ return;
+ DOMNamedNodeMap* ssoprops = e->getAttributes();
+ XMLSize_t ssopropslen = ssoprops ? ssoprops->getLength() : 0;
+
+ SPConfig& conf = SPConfig::getConfig();
+
+ int index = 0; // track ACS indexes globally across all protocols
+
+ // Tokenize the protocol list inside the element.
+ XMLStringTokenizer prottokens(e->getTextContent());
+ while (prottokens.hasMoreTokens()) {
+ auto_ptr_char prot(prottokens.nextToken());
+
+ // Look for initiator.
+ const PropertySet* initiator = pp.getInitiator(prot.get(), "SSO");
+ if (initiator) {
+ log.info("auto-configuring SSO initiation for protocol (%s)", prot.get());
+ pair<bool,const XMLCh*> inittype = initiator->getXMLString("id");
+ if (inittype.first) {
+ // Append a session initiator element of the designated type to the root element.
+ DOMElement* sidom = e->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS, _SessionInitiator);
+ sidom->setAttributeNS(nullptr, _type, inittype.second);
+ e->appendChild(sidom);
+ log.info("adding SessionInitiator of type (%s) to chain (/Login)", initiator->getString("id").second);
+
+ doArtifactResolution(pp, prot.get(), e, log);
+ protocols.insert(prot.get());
+ }
+ else {
+ log.error("missing id property on Initiator element, check config for protocol (%s)", prot.get());
+ }
+ }
+
+ // Look for incoming bindings.
+ const vector<const PropertySet*>& bindings = pp.getBindings(prot.get(), "SSO");
+ if (!bindings.empty()) {
+ log.info("auto-configuring SSO endpoints for protocol (%s)", prot.get());
+ pair<bool,const XMLCh*> idprop,pathprop;
+ for (vector<const PropertySet*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b, ++index) {
+ idprop = (*b)->getXMLString("id");
+ pathprop = (*b)->getXMLString("path");
+ if (idprop.first && pathprop.first) {
+ DOMElement* acsdom = e->getOwnerDocument()->createElementNS(samlconstants::SAML20MD_NS, _AssertionConsumerService);
+
+ // Copy in any attributes from the <SSO> element so they can be accessed as properties in the ACS handler.
+ for (XMLSize_t p = 0; p < ssopropslen; ++p) {
+ DOMNode* ssoprop = ssoprops->item(p);
+ if (ssoprop->getNodeType() == DOMNode::ATTRIBUTE_NODE) {
+ acsdom->setAttributeNS(
+ ((DOMAttr*)ssoprop)->getNamespaceURI(),
+ ((DOMAttr*)ssoprop)->getLocalName(),
+ ((DOMAttr*)ssoprop)->getValue()
+ );
+ }
+ }
+
+ // Set necessary properties based on context.
+ acsdom->setAttributeNS(nullptr, Binding, idprop.second);
+ acsdom->setAttributeNS(nullptr, Location, pathprop.second);
+ xstring indexbuf(1, chDigit_1 + (index % 10));
+ if (index / 10)
+ indexbuf = (XMLCh)(chDigit_1 + (index / 10)) + indexbuf;
+ acsdom->setAttributeNS(nullptr, _index, indexbuf.c_str());
+
+ log.info("adding AssertionConsumerService for Binding (%s) at (%s)", (*b)->getString("id").second, (*b)->getString("path").second);
+ boost::shared_ptr<Handler> handler(
+ conf.AssertionConsumerServiceManager.newPlugin(
+ (*b)->getString("id").second, pair<const DOMElement*,const char*>(acsdom, getId())
+ )
+ );
+ m_handlers.push_back(handler);
+
+ // Setup maps and defaults.
+ m_acsBindingMap[handler->getXMLString("Binding").second].push_back(handler.get());
+ const XMLCh* protfamily = handler->getProtocolFamily();
+ if (protfamily)
+ m_acsProtocolMap[protfamily].push_back(handler.get());
+ m_acsIndexMap[handler->getUnsignedInt("index").second] = handler.get();
+ if (!m_acsDefault)
+ m_acsDefault = handler.get();
+
+ // Insert into location map.
+ pair<bool,const char*> location = handler->getString("Location");
+ if (location.first && *location.second == '/')
+ m_handlerMap[location.second] = handler.get();
+ else if (location.first)
+ m_handlerMap[string("/") + location.second] = handler.get();
+ }
+ else {
+ log.error("missing id or path property on Binding element, check config for protocol (%s)", prot.get());
+ }
+ }
+ }
+
+ if (!initiator && bindings.empty()) {
+ log.error("no SSO Initiator or Binding config for protocol (%s)", prot.get());
+ }
+ }
+
+ // Handle discovery.
+ static const XMLCh discoveryProtocol[] = UNICODE_LITERAL_17(d,i,s,c,o,v,e,r,y,P,r,o,t,o,c,o,l);
+ static const XMLCh discoveryURL[] = UNICODE_LITERAL_12(d,i,s,c,o,v,e,r,y,U,R,L);
+ static const XMLCh _URL[] = UNICODE_LITERAL_3(U,R,L);
+ const XMLCh* discop = e->getAttributeNS(nullptr, discoveryProtocol);
+ if (discop && *discop) {
+ const XMLCh* discou = e->getAttributeNS(nullptr, discoveryURL);
+ if (discou && *discou) {
+ // Append a session initiator element of the designated type to the root element.
+ DOMElement* sidom = e->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS, _SessionInitiator);
+ sidom->setAttributeNS(nullptr, _type, discop);
+ sidom->setAttributeNS(nullptr, _URL, discou);
+ e->appendChild(sidom);
+ if (log.isInfoEnabled()) {
+ auto_ptr_char dp(discop);
+ log.info("adding SessionInitiator of type (%s) to chain (/Login)", dp.get());
+ }
+ }
+ else {
+ log.error("SSO discoveryProtocol specified without discoveryURL");
+ }
+ }
+
+ // Attach default Location to SSO element.
+ static const XMLCh _loc[] = { chForwardSlash, chLatin_L, chLatin_o, chLatin_g, chLatin_i, chLatin_n, chNull };
+ e->setAttributeNS(nullptr, Location, _loc);
+
+ // Instantiate Chaining initiator around the SSO element.
+ boost::shared_ptr<SessionInitiator> chain(
+ conf.SessionInitiatorManager.newPlugin(CHAINING_SESSION_INITIATOR, pair<const DOMElement*,const char*>(e, getId()))
+ );
+ m_handlers.push_back(chain);
+ m_sessionInitDefault = chain.get();
+ m_handlerMap["/Login"] = chain.get();
+}
+
+void XMLApplication::doLogout(const ProtocolProvider& pp, set<string>& protocols, DOMElement* e, Category& log)
+{
+ if (!e->hasChildNodes())
+ return;
+ DOMNamedNodeMap* sloprops = e->getAttributes();
+ XMLSize_t slopropslen = sloprops ? sloprops->getLength() : 0;
+
+ SPConfig& conf = SPConfig::getConfig();
+
+ // Tokenize the protocol list inside the element.
+ XMLStringTokenizer prottokens(e->getTextContent());
+ while (prottokens.hasMoreTokens()) {
+ auto_ptr_char prot(prottokens.nextToken());
+
+ // Look for initiator.
+ const PropertySet* initiator = pp.getInitiator(prot.get(), "Logout");
+ if (initiator) {
+ log.info("auto-configuring Logout initiation for protocol (%s)", prot.get());
+ pair<bool,const XMLCh*> inittype = initiator->getXMLString("id");
+ if (inittype.first) {
+ // Append a logout initiator element of the designated type to the root element.
+ DOMElement* lidom = e->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS, _LogoutInitiator);
+ lidom->setAttributeNS(nullptr, _type, inittype.second);
+ e->appendChild(lidom);
+ log.info("adding LogoutInitiator of type (%s) to chain (/Logout)", initiator->getString("id").second);
+
+ if (protocols.count(prot.get()) == 0) {
+ doArtifactResolution(pp, prot.get(), e, log);
+ protocols.insert(prot.get());
+ }
+ }
+ else {
+ log.error("missing id property on Initiator element, check config for protocol (%s)", prot.get());
+ }
+ }
+
+ // Look for incoming bindings.
+ const vector<const PropertySet*>& bindings = pp.getBindings(prot.get(), "Logout");
+ if (!bindings.empty()) {
+ log.info("auto-configuring Logout endpoints for protocol (%s)", prot.get());
+ pair<bool,const XMLCh*> idprop,pathprop;
+ for (vector<const PropertySet*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b) {
+ idprop = (*b)->getXMLString("id");
+ pathprop = (*b)->getXMLString("path");
+ if (idprop.first && pathprop.first) {
+ DOMElement* slodom = e->getOwnerDocument()->createElementNS(samlconstants::SAML20MD_NS, _SingleLogoutService);
+
+ // Copy in any attributes from the <Logout> element so they can be accessed as properties in the SLO handler.
+ for (XMLSize_t p = 0; p < slopropslen; ++p) {
+ DOMNode* sloprop = sloprops->item(p);
+ if (sloprop->getNodeType() == DOMNode::ATTRIBUTE_NODE) {
+ slodom->setAttributeNS(
+ ((DOMAttr*)sloprop)->getNamespaceURI(),
+ ((DOMAttr*)sloprop)->getLocalName(),
+ ((DOMAttr*)sloprop)->getValue()
+ );
+ }
+ }
+
+ // Set necessary properties based on context.
+ slodom->setAttributeNS(nullptr, Binding, idprop.second);
+ slodom->setAttributeNS(nullptr, Location, pathprop.second);
+ if (e->hasAttributeNS(nullptr, _policyId))
+ slodom->setAttributeNS(shibspconstants::SHIB2SPCONFIG_NS, _policyId, e->getAttributeNS(nullptr, _policyId));
+
+ log.info("adding SingleLogoutService for Binding (%s) at (%s)", (*b)->getString("id").second, (*b)->getString("path").second);
+ boost::shared_ptr<Handler> handler(
+ conf.SingleLogoutServiceManager.newPlugin((*b)->getString("id").second, pair<const DOMElement*,const char*>(slodom, getId()))
+ );
+ m_handlers.push_back(handler);
+
+ // Insert into location map.
+ pair<bool,const char*> location = handler->getString("Location");
+ if (location.first && *location.second == '/')
+ m_handlerMap[location.second] = handler.get();
+ else if (location.first)
+ m_handlerMap[string("/") + location.second] = handler.get();
+ }
+ else {
+ log.error("missing id or path property on Binding element, check config for protocol (%s)", prot.get());
+ }
+ }
+
+ if (protocols.count(prot.get()) == 0) {
+ doArtifactResolution(pp, prot.get(), e, log);
+ protocols.insert(prot.get());
+ }
+ }
+
+ if (!initiator && bindings.empty()) {
+ log.error("no Logout Initiator or Binding config for protocol (%s)", prot.get());
+ }
+ }
+
+ // Attach default Location to Logout element.
+ static const XMLCh _loc[] = { chForwardSlash, chLatin_L, chLatin_o, chLatin_g, chLatin_o, chLatin_u, chLatin_t, chNull };
+ e->setAttributeNS(nullptr, Location, _loc);
+
+ // Instantiate Chaining initiator around the SSO element.
+ boost::shared_ptr<Handler> chain(
+ conf.LogoutInitiatorManager.newPlugin(CHAINING_LOGOUT_INITIATOR, pair<const DOMElement*,const char*>(e, getId()))
+ );
+ m_handlers.push_back(chain);
+ m_handlerMap["/Logout"] = chain.get();
+}
+
+void XMLApplication::doNameIDMgmt(const ProtocolProvider& pp, set<string>& protocols, DOMElement* e, Category& log)
+{
+ if (!e->hasChildNodes())
+ return;
+ DOMNamedNodeMap* nimprops = e->getAttributes();
+ XMLSize_t nimpropslen = nimprops ? nimprops->getLength() : 0;
+
+ SPConfig& conf = SPConfig::getConfig();
+
+ // Tokenize the protocol list inside the element.
+ XMLStringTokenizer prottokens(e->getTextContent());
+ while (prottokens.hasMoreTokens()) {
+ auto_ptr_char prot(prottokens.nextToken());
+
+ // Look for incoming bindings.
+ const vector<const PropertySet*>& bindings = pp.getBindings(prot.get(), "NameIDMgmt");
+ if (!bindings.empty()) {
+ log.info("auto-configuring NameIDMgmt endpoints for protocol (%s)", prot.get());
+ pair<bool,const XMLCh*> idprop,pathprop;
+ for (vector<const PropertySet*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b) {
+ idprop = (*b)->getXMLString("id");
+ pathprop = (*b)->getXMLString("path");
+ if (idprop.first && pathprop.first) {
+ DOMElement* nimdom = e->getOwnerDocument()->createElementNS(samlconstants::SAML20MD_NS, _ManageNameIDService);
+
+ // Copy in any attributes from the <NameIDMgmt> element so they can be accessed as properties in the NIM handler.
+ for (XMLSize_t p = 0; p < nimpropslen; ++p) {
+ DOMNode* nimprop = nimprops->item(p);
+ if (nimprop->getNodeType() == DOMNode::ATTRIBUTE_NODE) {
+ nimdom->setAttributeNS(
+ ((DOMAttr*)nimprop)->getNamespaceURI(),
+ ((DOMAttr*)nimprop)->getLocalName(),
+ ((DOMAttr*)nimprop)->getValue()
+ );
+ }
+ }
+
+ // Set necessary properties based on context.
+ nimdom->setAttributeNS(nullptr, Binding, idprop.second);
+ nimdom->setAttributeNS(nullptr, Location, pathprop.second);
+ if (e->hasAttributeNS(nullptr, _policyId))
+ nimdom->setAttributeNS(shibspconstants::SHIB2SPCONFIG_NS, _policyId, e->getAttributeNS(nullptr, _policyId));
+
+ log.info("adding ManageNameIDService for Binding (%s) at (%s)", (*b)->getString("id").second, (*b)->getString("path").second);
+ boost::shared_ptr<Handler> handler(
+ conf.ManageNameIDServiceManager.newPlugin((*b)->getString("id").second, pair<const DOMElement*,const char*>(nimdom, getId()))
+ );
+ m_handlers.push_back(handler);
+
+ // Insert into location map.
+ pair<bool,const char*> location = handler->getString("Location");
+ if (location.first && *location.second == '/')
+ m_handlerMap[location.second] = handler.get();
+ else if (location.first)
+ m_handlerMap[string("/") + location.second] = handler.get();
+ }
+ else {
+ log.error("missing id or path property on Binding element, check config for protocol (%s)", prot.get());
+ }
+ }
+
+ if (protocols.count(prot.get()) == 0) {
+ doArtifactResolution(pp, prot.get(), e, log);
+ protocols.insert(prot.get());
+ }
+ }
+ else {
+ log.error("no NameIDMgmt Binding config for protocol (%s)", prot.get());
+ }
+ }
+}
+
+void XMLApplication::doArtifactResolution(const ProtocolProvider& pp, const char* protocol, DOMElement* e, Category& log)
+{
+ SPConfig& conf = SPConfig::getConfig();
+
+ int index = 0; // track indexes globally across all protocols
+
+ // Look for incoming bindings.
+ const vector<const PropertySet*>& bindings = pp.getBindings(protocol, "ArtifactResolution");
+ if (!bindings.empty()) {
+ log.info("auto-configuring ArtifactResolution endpoints for protocol (%s)", protocol);
+ pair<bool,const XMLCh*> idprop,pathprop;
+ for (vector<const PropertySet*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b, ++index) {
+ idprop = (*b)->getXMLString("id");
+ pathprop = (*b)->getXMLString("path");
+ if (idprop.first && pathprop.first) {
+ DOMElement* artdom = e->getOwnerDocument()->createElementNS(samlconstants::SAML20MD_NS, _ArtifactResolutionService);
+ artdom->setAttributeNS(nullptr, Binding, idprop.second);
+ artdom->setAttributeNS(nullptr, Location, pathprop.second);
+ xstring indexbuf(1, chDigit_1 + (index % 10));
+ if (index / 10)
+ indexbuf = (XMLCh)(chDigit_1 + (index / 10)) + indexbuf;
+ artdom->setAttributeNS(nullptr, _index, indexbuf.c_str());
+
+ log.info("adding ArtifactResolutionService for Binding (%s) at (%s)", (*b)->getString("id").second, (*b)->getString("path").second);
+ boost::shared_ptr<Handler> handler(
+ conf.ArtifactResolutionServiceManager.newPlugin((*b)->getString("id").second, pair<const DOMElement*,const char*>(artdom, getId()))
+ );
+ m_handlers.push_back(handler);
+
+ if (!m_artifactResolutionDefault)
+ m_artifactResolutionDefault = handler.get();
+
+ // Insert into location map.
+ pair<bool,const char*> location = handler->getString("Location");
+ if (location.first && *location.second == '/')
+ m_handlerMap[location.second] = handler.get();
+ else if (location.first)
+ m_handlerMap[string("/") + location.second] = handler.get();
+ }
+ else {
+ log.error("missing id or path property on Binding element, check config for protocol (%s)", protocol);
+ }
+ }
+ }
+}
+
+#ifndef SHIBSP_LITE
+void XMLApplication::doAttributePlugins(DOMElement* e, Category& log)
+{
+ SPConfig& conf = SPConfig::getConfig();
+
+ m_attrExtractor.reset(
+ doChainedPlugins(conf.AttributeExtractorManager, "AttributeExtractor", CHAINING_ATTRIBUTE_EXTRACTOR, _AttributeExtractor, e, log)
+ );
+
+ m_attrFilter.reset(
+ doChainedPlugins(conf.AttributeFilterManager, "AttributeFilter", CHAINING_ATTRIBUTE_FILTER, _AttributeFilter, e, log, DUMMY_ATTRIBUTE_FILTER)
+ );
+
+ m_attrResolver.reset(
+ doChainedPlugins(conf.AttributeResolverManager, "AttributeResolver", CHAINING_ATTRIBUTE_RESOLVER, _AttributeResolver, e, log)
+ );
+
+ if (m_unsetHeaders.empty()) {
+ vector<string> unsetHeaders;
+ if (m_attrExtractor) {
+ Locker extlock(m_attrExtractor.get());
+ m_attrExtractor->getAttributeIds(unsetHeaders);
+ }
+ else if (m_base && m_base->m_attrExtractor) {
+ Locker extlock(m_base->m_attrExtractor.get());
+ m_base->m_attrExtractor->getAttributeIds(unsetHeaders);
+ }
+ if (m_attrResolver) {
+ Locker reslock(m_attrResolver.get());
+ m_attrResolver->getAttributeIds(unsetHeaders);
+ }
+ else if (m_base && m_base->m_attrResolver) {
+ Locker extlock(m_base->m_attrResolver.get());
+ m_base->m_attrResolver->getAttributeIds(unsetHeaders);
+ }
+ if (!unsetHeaders.empty()) {
+ string transformedprefix(m_attributePrefix.second);
+ const char* pch;
+ pair<bool,const char*> prefix = getString("metadataAttributePrefix");
+ if (prefix.first) {
+ pch = prefix.second;
+ while (*pch) {
+ transformedprefix += (isalnum(*pch) ? toupper(*pch) : '_');
+ pch++;
+ }
+ }
+ for (vector<string>::const_iterator hdr = unsetHeaders.begin(); hdr!=unsetHeaders.end(); ++hdr) {
+ string transformed;
+ pch = hdr->c_str();
+ while (*pch) {
+ transformed += (isalnum(*pch) ? toupper(*pch) : '_');
+ pch++;
+ }
+ m_unsetHeaders.push_back(make_pair(m_attributePrefix.first + *hdr, m_attributePrefix.second + transformed));
+ if (prefix.first)
+ m_unsetHeaders.push_back(make_pair(m_attributePrefix.first + prefix.second + *hdr, transformedprefix + transformed));
+ }
+ }
+ m_unsetHeaders.push_back(make_pair(m_attributePrefix.first + "Shib-Application-ID", m_attributePrefix.second + "SHIB_APPLICATION_ID"));
+ }
+}
+#endif
+
+#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
+short
+#else
+DOMNodeFilter::FilterAction
+#endif
+XMLApplication::acceptNode(const DOMNode* node) const
+{
+ const XMLCh* name=node->getLocalName();
+ if (XMLString::equals(name,ApplicationOverride) ||
+ XMLString::equals(name,_Audience) ||
+ XMLString::equals(name,Notify) ||
+ XMLString::equals(name,_Handler) ||
+ XMLString::equals(name,_AssertionConsumerService) ||
+ XMLString::equals(name,_ArtifactResolutionService) ||
+ XMLString::equals(name,Logout) ||
+ XMLString::equals(name,_LogoutInitiator) ||
+ XMLString::equals(name,_ManageNameIDService) ||
+ XMLString::equals(name,NameIDMgmt) ||
+ XMLString::equals(name,_SessionInitiator) ||
+ XMLString::equals(name,_SingleLogoutService) ||
+ XMLString::equals(name,SSO) ||
+ XMLString::equals(name,RelyingParty) ||
+ XMLString::equals(name,_MetadataProvider) ||
+ XMLString::equals(name,_TrustEngine) ||
+ XMLString::equals(name,_CredentialResolver) ||
+ XMLString::equals(name,_AttributeFilter) ||
+ XMLString::equals(name,_AttributeExtractor) ||
+ XMLString::equals(name,_AttributeResolver))
+ return FILTER_REJECT;
+
+ return FILTER_ACCEPT;
+}
+
+#ifndef SHIBSP_LITE
+
+const PropertySet* XMLApplication::getRelyingParty(const EntityDescriptor* provider) const
+{
+ if (!provider)
+ return this;
+
+ // Check for exact match on name.
+ map< xstring,boost::shared_ptr<PropertySet> >::const_iterator i = m_partyMap.find(provider->getEntityID());
+ if (i != m_partyMap.end())
+ return i->second.get();
+
+ // Check for extensible matching.
+ vector < pair< boost::shared_ptr<EntityMatcher>,boost::shared_ptr<PropertySet> > >::const_iterator j;
+ for (j = m_partyVec.begin(); j != m_partyVec.end(); ++j) {
+ if (j->first->matches(*provider))
+ return j->second.get();
+ }
+
+ // Check for group match.
+ const EntitiesDescriptor* group = dynamic_cast<const EntitiesDescriptor*>(provider->getParent());
+ while (group) {
+ if (group->getName()) {
+ i = m_partyMap.find(group->getName());
+ if (i != m_partyMap.end())
+ return i->second.get();
+ }
+ group = dynamic_cast<const EntitiesDescriptor*>(group->getParent());
+ }
+ return this;
+}
+
+const PropertySet* XMLApplication::getRelyingParty(const XMLCh* entityID) const
+{
+ if (!entityID)
+ return this;
+ map< xstring,boost::shared_ptr<PropertySet> >::const_iterator i = m_partyMap.find(entityID);
+ return (i != m_partyMap.end()) ? i->second.get() : this;
+}
+
+#endif
+
+string XMLApplication::getNotificationURL(const char* resource, bool front, unsigned int index) const
+{
+ const vector<string>& locs = front ? m_frontLogout : m_backLogout;
+ if (locs.empty())
+ return m_base ? m_base->getNotificationURL(resource, front, index) : string();
+ else if (index >= locs.size())
+ return string();
+
+#ifdef HAVE_STRCASECMP
+ if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))
+#else
+ if (!resource || (strnicmp(resource,"http://",7) && strnicmp(resource,"https://",8)))
+#endif
+ throw ConfigurationException("Request URL was not absolute.");
+
+ const char* handler = locs[index].c_str();
+
+ // Should never happen...
+ if (!handler || (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)))
+ throw ConfigurationException(
+ "Invalid Location property ($1) in Notify element for Application ($2)",
+ params(2, handler ? handler : "null", getId())
+ );
+
+ // The "Location" property can be in one of three formats:
+ //
+ // 1) a full URI: http://host/foo/bar
+ // 2) a hostless URI: http:///foo/bar
+ // 3) a relative path: /foo/bar
+ //
+ // # Protocol Host Path
+ // 1 handler handler handler
+ // 2 handler resource handler
+ // 3 resource resource handler
+
+ const char* path = nullptr;
+
+ // Decide whether to use the handler or the resource for the "protocol"
+ const char* prot;
+ if (*handler != '/') {
+ prot = handler;
+ }
+ else {
+ prot = resource;
+ path = handler;
+ }
+
+ // break apart the "protocol" string into protocol, host, and "the rest"
+ const char* colon=strchr(prot,':');
+ colon += 3;
+ const char* slash=strchr(colon,'/');
+ if (!path)
+ path = slash;
+
+ // Compute the actual protocol and store.
+ string notifyURL(prot, colon-prot);
+
+ // create the "host" from either the colon/slash or from the target string
+ // If prot == handler then we're in either #1 or #2, else #3.
+ // If slash == colon then we're in #2.
+ if (prot != handler || slash == colon) {
+ colon = strchr(resource, ':');
+ colon += 3; // Get past the ://
+ slash = strchr(colon, '/');
+ }
+ string host(colon, (slash ? slash-colon : strlen(colon)));
+
+ // Build the URL
+ notifyURL += host + path;
+ return notifyURL;
+}
+
+void XMLApplication::clearHeader(SPRequest& request, const char* rawname, const char* cginame) const
+{
+ if (!m_attributePrefix.first.empty()) {
+ string temp = m_attributePrefix.first + rawname;
+ string temp2 = m_attributePrefix.second + (cginame + 5);
+ request.clearHeader(temp.c_str(), temp2.c_str());
+ }
+ else if (m_base) {
+ m_base->clearHeader(request, rawname, cginame);
+ }
+ else {
+ request.clearHeader(rawname, cginame);
+ }
+}
+
+void XMLApplication::setHeader(SPRequest& request, const char* name, const char* value) const
+{
+ if (!m_attributePrefix.first.empty()) {
+ string temp = m_attributePrefix.first + name;
+ request.setHeader(temp.c_str(), value);
+ }
+ else if (m_base) {
+ m_base->setHeader(request, name, value);
+ }
+ else {
+ request.setHeader(name, value);
+ }
+}
+
+string XMLApplication::getSecureHeader(const SPRequest& request, const char* name) const
+{
+ if (!m_attributePrefix.first.empty()) {
+ string temp = m_attributePrefix.first + name;
+ return request.getSecureHeader(temp.c_str());
+ }
+ else if (m_base) {
+ return m_base->getSecureHeader(request,name);
+ }
+ else {
+ return request.getSecureHeader(name);
+ }
+}
+
+const SessionInitiator* XMLApplication::getDefaultSessionInitiator() const
+{
+ if (m_sessionInitDefault) return m_sessionInitDefault;
+ return m_base ? m_base->getDefaultSessionInitiator() : nullptr;
+}
+
+const SessionInitiator* XMLApplication::getSessionInitiatorById(const char* id) const
+{
+ map<string,const SessionInitiator*>::const_iterator i = m_sessionInitMap.find(id);
+ if (i != m_sessionInitMap.end()) return i->second;
+ return m_base ? m_base->getSessionInitiatorById(id) : nullptr;
+}
+
+const Handler* XMLApplication::getDefaultAssertionConsumerService() const
+{
+ if (m_acsDefault) return m_acsDefault;
+ return m_base ? m_base->getDefaultAssertionConsumerService() : nullptr;
+}
+
+const Handler* XMLApplication::getAssertionConsumerServiceByIndex(unsigned short index) const
+{
+ map<unsigned int,const Handler*>::const_iterator i = m_acsIndexMap.find(index);
+ if (i != m_acsIndexMap.end()) return i->second;
+ return m_base ? m_base->getAssertionConsumerServiceByIndex(index) : nullptr;
+}
+
+const Handler* XMLApplication::getAssertionConsumerServiceByProtocol(const XMLCh* protocol, const char* binding) const
+{
+ ACSProtocolMap::const_iterator i = m_acsProtocolMap.find(protocol);
+ if (i != m_acsProtocolMap.end() && !i->second.empty()) {
+ if (!binding || !*binding)
+ return i->second.front();
+ for (ACSProtocolMap::value_type::second_type::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
+ if (!strcmp(binding, (*j)->getString("Binding").second))
+ return *j;
+ }
+ }
+ return m_base ? m_base->getAssertionConsumerServiceByProtocol(protocol, binding) : nullptr;
+}
+
+const vector<const Handler*>& XMLApplication::getAssertionConsumerServicesByBinding(const XMLCh* binding) const
+{
+ ACSBindingMap::const_iterator i = m_acsBindingMap.find(binding);
+ if (i != m_acsBindingMap.end())
+ return i->second;
+ return m_base ? m_base->getAssertionConsumerServicesByBinding(binding) : g_noHandlers;
+}
+
+const Handler* XMLApplication::getHandler(const char* path) const
+{
+ string wrap(path);
+ wrap = wrap.substr(0, wrap.find(';'));
+ map<string,const Handler*>::const_iterator i = m_handlerMap.find(wrap.substr(0, wrap.find('?')));
+ if (i != m_handlerMap.end())
+ return i->second;
+ return m_base ? m_base->getHandler(path) : nullptr;
+}
+
+void XMLApplication::getHandlers(vector<const Handler*>& handlers) const
+{
+ static void (vector<const Handler*>::* pb)(const Handler* const&) = &vector<const Handler*>::push_back;
+ for_each(m_handlers.begin(), m_handlers.end(), boost::bind(pb, boost::ref(handlers), boost::bind(&boost::shared_ptr<Handler>::get, _1)));
+ if (m_base) {
+ for (map<string,const Handler*>::const_iterator h = m_base->m_handlerMap.begin(); h != m_base->m_handlerMap.end(); ++h) {
+ if (m_handlerMap.count(h->first) == 0)
+ handlers.push_back(h->second);
+ }
+ }
+}
+
+void XMLApplication::limitRedirect(const GenericRequest& request, const char* url) const
+{
+ if (!url || *url == '/')
+ return;
+ if (m_redirectLimit == REDIRECT_LIMIT_INHERIT)
+ return m_base->limitRedirect(request, url);
+ if (m_redirectLimit != REDIRECT_LIMIT_NONE) {
+ vector<string> whitelist;
+ if (m_redirectLimit == REDIRECT_LIMIT_EXACT || m_redirectLimit == REDIRECT_LIMIT_EXACT_WHITELIST) {
+ // Scheme and hostname have to match.
+ if (request.isDefaultPort()) {
+ whitelist.push_back(string(request.getScheme()) + "://" + request.getHostname() + '/');
+ }
+ whitelist.push_back(string(request.getScheme()) + "://" + request.getHostname() + ':' + lexical_cast<string>(request.getPort()) + '/');
+ }
+ else if (m_redirectLimit == REDIRECT_LIMIT_HOST || m_redirectLimit == REDIRECT_LIMIT_HOST_WHITELIST) {
+ // Allow any scheme or port.
+ whitelist.push_back(string("https://") + request.getHostname() + '/');
+ whitelist.push_back(string("http://") + request.getHostname() + '/');
+ whitelist.push_back(string("https://") + request.getHostname() + ':');
+ whitelist.push_back(string("http://") + request.getHostname() + ':');
+ }
+
+ static bool (*startsWithI)(const char*,const char*) = XMLString::startsWithI;
+ if (!whitelist.empty() && find_if(whitelist.begin(), whitelist.end(),
+ boost::bind(startsWithI, url, boost::bind(&string::c_str, _1))) != whitelist.end()) {
+ return;
+ }
+ else if (!m_redirectWhitelist.empty() && find_if(m_redirectWhitelist.begin(), m_redirectWhitelist.end(),
+ boost::bind(startsWithI, url, boost::bind(&string::c_str, _1))) != m_redirectWhitelist.end()) {
+ return;
+ }
+ Category::getInstance(SHIBSP_LOGCAT ".Application").warn("redirectLimit policy enforced, blocked redirect to (%s)", url);
+ throw opensaml::SecurityPolicyException("Blocked unacceptable redirect location.");
+ }
+}
+
+#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
+short
+#else
+DOMNodeFilter::FilterAction
+#endif
+XMLConfigImpl::acceptNode(const DOMNode* node) const
+{
+ if (!XMLString::equals(node->getNamespaceURI(),shibspconstants::SHIB2SPCONFIG_NS))