{
// We have to know the IdP to function.
if (!entityID || !*entityID)
- return make_pair(false,0);
+ return make_pair(false,0L);
string target;
const Handler* ACS=NULL;
const vector<const Handler*>& handlers = app.getAssertionConsumerServicesByBinding(m_binding.get());
// Index comes from request, or default set in the handler, or we just pick the first endpoint.
- pair<bool,unsigned int> index = make_pair(false,0);
+ pair<bool,unsigned int> index(false,0);
if (isHandler) {
option = request.getParameter("acsIndex");
if (option)
- index = make_pair(true, atoi(option));
+ index = pair<bool,unsigned int>(true, atoi(option));
}
if (!index.first)
index = getUnsignedInt("defaultACSIndex");
}
else if (!entity.second) {
m_log.error("unable to locate ADFS-aware identity provider role for provider (%s)", entityID);
- return make_pair(false,0);
+ return make_pair(false,0L);
}
const EndpointType* ep = EndpointManager<SingleSignOnService>(
dynamic_cast<const IDPSSODescriptor*>(entity.second)->getSingleSignOnServices()
).getByBinding(m_binding.get());
if (!ep) {
m_log.error("unable to locate compatible SSO service for provider (%s)", entityID);
- return make_pair(false,0);
+ return make_pair(false,0L);
}
preserveRelayState(app, httpResponse, relayState);
return make_pair(true, httpResponse.sendRedirect(req.c_str()));
#else
- return make_pair(false,0);
+ return make_pair(false,0L);
#endif
}
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 ADFS sessions.
if (!XMLString::equals(session->getProtocol(), WSFED_NS) || !session->getEntityID()) {
session->unlock();
- return make_pair(false,0);
+ return make_pair(false,0L);
}
}
catch (exception& ex) {
m_log.error("error accessing current session: %s", ex.what());
- return make_pair(false,0);
+ return make_pair(false,0L);
}
string entityID(session->getEntityID());
m_log.error("error issuing ADFS logout request: %s", ex.what());
}
- return make_pair(false,0);
+ return make_pair(false,0L);
#else
throw ConfigurationException("Cannot perform logout using lite version of shibsp library.");
#endif
else if (sta->m_dc->tSettings) {
const char* prop = ap_table_get(sta->m_dc->tSettings, name);
if (prop)
- return make_pair(true, strtol(prop, NULL, 10));
+ return pair<bool,unsigned int>(true, atoi(prop));
}
}
return s ? s->getUnsignedInt(name,ns) : pair<bool,unsigned int>(false,0);
-/*\r
- * Copyright 2001-2007 Internet2\r
- * \r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/**\r
- * ScopedAttributeDecoder.cpp\r
- * \r
- * Decodes SAML into ScopedAttributes\r
- */\r
-\r
-#include "internal.h"\r
-#include "attribute/AttributeDecoder.h"\r
-#include "attribute/ScopedAttribute.h"\r
-\r
-#include <saml/saml1/core/Assertions.h>\r
-#include <saml/saml2/core/Assertions.h>\r
-\r
-using namespace shibsp;\r
-using namespace opensaml::saml1;\r
-using namespace opensaml::saml2;\r
-using namespace xmltooling;\r
-using namespace std;\r
-\r
-namespace shibsp {\r
- static const XMLCh Scope[] = UNICODE_LITERAL_5(S,c,o,p,e);\r
- static const XMLCh scopeDelimeter[] = UNICODE_LITERAL_14(s,c,o,p,e,D,e,l,i,m,e,t,e,r);\r
-\r
- class SHIBSP_DLLLOCAL ScopedAttributeDecoder : virtual public AttributeDecoder\r
- {\r
- public:\r
- ScopedAttributeDecoder(const DOMElement* e) : AttributeDecoder(e), m_delimeter('@') {\r
- if (e) {\r
- if (e->hasAttributeNS(NULL,scopeDelimeter)) {\r
- auto_ptr_char d(e->getAttributeNS(NULL,scopeDelimeter));\r
- m_delimeter = *(d.get());\r
- }\r
- }\r
- }\r
- ~ScopedAttributeDecoder() {}\r
-\r
- shibsp::Attribute* decode(\r
- const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty=NULL, const char* relyingParty=NULL\r
- ) const;\r
-\r
- private:\r
- char m_delimeter;\r
- };\r
-\r
- AttributeDecoder* SHIBSP_DLLLOCAL ScopedAttributeDecoderFactory(const DOMElement* const & e)\r
- {\r
- return new ScopedAttributeDecoder(e);\r
- }\r
-};\r
-\r
-shibsp::Attribute* ScopedAttributeDecoder::decode(\r
- const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty\r
- ) const\r
-{\r
- char* val;\r
- char* scope;\r
- const XMLCh* xmlscope;\r
- QName scopeqname(NULL,Scope);\r
- auto_ptr<ScopedAttribute> scoped(new ScopedAttribute(ids, m_delimeter));\r
- scoped->setCaseSensitive(m_caseSensitive);\r
- vector< pair<string,string> >& dest = scoped->getValues();\r
- vector<XMLObject*>::const_iterator v,stop;\r
-\r
- Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder");\r
- \r
- if (xmlObject && XMLString::equals(opensaml::saml1::Attribute::LOCAL_NAME,xmlObject->getElementQName().getLocalPart())) {\r
- const opensaml::saml2::Attribute* saml2attr = dynamic_cast<const opensaml::saml2::Attribute*>(xmlObject);\r
- if (saml2attr) {\r
- const vector<XMLObject*>& values = saml2attr->getAttributeValues();\r
- v = values.begin();\r
- stop = values.end();\r
- if (log.isDebugEnabled()) {\r
- auto_ptr_char n(saml2attr->getName());\r
- log.debug(\r
- "decoding ScopedAttribute (%s) from SAML 2 Attribute (%s) with %lu value(s)",\r
- ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()\r
- );\r
- }\r
- }\r
- else {\r
- const opensaml::saml1::Attribute* saml1attr = dynamic_cast<const opensaml::saml1::Attribute*>(xmlObject);\r
- if (saml1attr) {\r
- const vector<XMLObject*>& values = saml1attr->getAttributeValues();\r
- v = values.begin();\r
- stop = values.end();\r
- if (log.isDebugEnabled()) {\r
- auto_ptr_char n(saml1attr->getAttributeName());\r
- log.debug(\r
- "decoding ScopedAttribute (%s) from SAML 1 Attribute (%s) with %lu value(s)",\r
- ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()\r
- );\r
- }\r
- }\r
- else {\r
- log.warn("XMLObject type not recognized by ScopedAttributeDecoder, no values returned");\r
- return NULL;\r
- }\r
- }\r
-\r
- for (; v!=stop; ++v) {\r
- if (!(*v)->hasChildren()) {\r
- val = toUTF8((*v)->getTextContent());\r
- if (val && *val) {\r
- const AttributeExtensibleXMLObject* aexo=dynamic_cast<const AttributeExtensibleXMLObject*>(*v);\r
- xmlscope = aexo->getAttribute(scopeqname);\r
- if (xmlscope && *xmlscope) {\r
- scope = toUTF8(xmlscope);\r
- dest.push_back(make_pair(val,scope));\r
- delete[] scope;\r
- }\r
- else {\r
- scope = strchr(val, m_delimeter);\r
- if (scope) {\r
- *scope++ = 0;\r
- if (*scope)\r
- dest.push_back(make_pair(val,scope));\r
- else\r
- log.warn("ignoring unscoped AttributeValue");\r
- }\r
- else {\r
- log.warn("ignoring unscoped AttributeValue");\r
- }\r
- }\r
- }\r
- else {\r
- log.warn("skipping empty AttributeValue");\r
- }\r
- delete[] val;\r
- }\r
- else {\r
- log.warn("skipping complex AttributeValue");\r
- }\r
- }\r
-\r
- return dest.empty() ? NULL : scoped.release();\r
- }\r
-\r
- const NameID* saml2name = dynamic_cast<const NameID*>(xmlObject);\r
- if (saml2name) {\r
- if (log.isDebugEnabled()) {\r
- auto_ptr_char f(saml2name->getFormat());\r
- log.debug("decoding ScopedAttribute (%s) from SAML 2 NameID with Format (%s)", ids.front().c_str(), f.get() ? f.get() : "unspecified");\r
- }\r
- val = toUTF8(saml2name->getName());\r
- }\r
- else {\r
- const NameIdentifier* saml1name = dynamic_cast<const NameIdentifier*>(xmlObject);\r
- if (saml1name) {\r
- if (log.isDebugEnabled()) {\r
- auto_ptr_char f(saml1name->getFormat());\r
- log.debug(\r
- "decoding ScopedAttribute (%s) from SAML 1 NameIdentifier with Format (%s)",\r
- ids.front().c_str(), f.get() ? f.get() : "unspecified"\r
- );\r
- }\r
- val = toUTF8(saml1name->getName());\r
- }\r
- else {\r
- log.warn("XMLObject type not recognized by ScopedAttributeDecoder, no values returned");\r
- return NULL;\r
- }\r
- }\r
-\r
- if (val && *val && *val!=m_delimeter) {\r
- scope = strchr(val, m_delimeter);\r
- if (scope) {\r
- *scope++ = 0;\r
- if (*scope)\r
- dest.push_back(make_pair(val,scope));\r
- else\r
- log.warn("ignoring NameID with no scope");\r
- }\r
- else {\r
- log.warn("ignoring NameID with no scope delimiter (%c)", m_delimeter);\r
- }\r
- }\r
- else {\r
- log.warn("ignoring empty NameID");\r
- }\r
- delete[] val;\r
- return dest.empty() ? NULL : scoped.release();\r
-}\r
+/*
+ * 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.
+ */
+
+/**
+ * ScopedAttributeDecoder.cpp
+ *
+ * Decodes SAML into ScopedAttributes
+ */
+
+#include "internal.h"
+#include "attribute/AttributeDecoder.h"
+#include "attribute/ScopedAttribute.h"
+
+#include <saml/saml1/core/Assertions.h>
+#include <saml/saml2/core/Assertions.h>
+
+using namespace shibsp;
+using namespace opensaml::saml1;
+using namespace opensaml::saml2;
+using namespace xmltooling;
+using namespace std;
+
+namespace shibsp {
+ static const XMLCh Scope[] = UNICODE_LITERAL_5(S,c,o,p,e);
+ static const XMLCh scopeDelimeter[] = UNICODE_LITERAL_14(s,c,o,p,e,D,e,l,i,m,e,t,e,r);
+
+ class SHIBSP_DLLLOCAL ScopedAttributeDecoder : virtual public AttributeDecoder
+ {
+ public:
+ ScopedAttributeDecoder(const DOMElement* e) : AttributeDecoder(e), m_delimeter('@') {
+ if (e) {
+ if (e->hasAttributeNS(NULL,scopeDelimeter)) {
+ auto_ptr_char d(e->getAttributeNS(NULL,scopeDelimeter));
+ m_delimeter = *(d.get());
+ }
+ }
+ }
+ ~ScopedAttributeDecoder() {}
+
+ shibsp::Attribute* decode(
+ const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty=NULL, const char* relyingParty=NULL
+ ) const;
+
+ private:
+ char m_delimeter;
+ };
+
+ AttributeDecoder* SHIBSP_DLLLOCAL ScopedAttributeDecoderFactory(const DOMElement* const & e)
+ {
+ return new ScopedAttributeDecoder(e);
+ }
+};
+
+shibsp::Attribute* ScopedAttributeDecoder::decode(
+ const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty
+ ) const
+{
+ char* val;
+ char* scope;
+ const XMLCh* xmlscope;
+ QName scopeqname(NULL,Scope);
+ auto_ptr<ScopedAttribute> scoped(new ScopedAttribute(ids, m_delimeter));
+ scoped->setCaseSensitive(m_caseSensitive);
+ vector< pair<string,string> >& dest = scoped->getValues();
+ vector<XMLObject*>::const_iterator v,stop;
+
+ Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder");
+
+ if (xmlObject && XMLString::equals(opensaml::saml1::Attribute::LOCAL_NAME,xmlObject->getElementQName().getLocalPart())) {
+ const opensaml::saml2::Attribute* saml2attr = dynamic_cast<const opensaml::saml2::Attribute*>(xmlObject);
+ if (saml2attr) {
+ const vector<XMLObject*>& values = saml2attr->getAttributeValues();
+ v = values.begin();
+ stop = values.end();
+ if (log.isDebugEnabled()) {
+ auto_ptr_char n(saml2attr->getName());
+ log.debug(
+ "decoding ScopedAttribute (%s) from SAML 2 Attribute (%s) with %lu value(s)",
+ ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()
+ );
+ }
+ }
+ else {
+ const opensaml::saml1::Attribute* saml1attr = dynamic_cast<const opensaml::saml1::Attribute*>(xmlObject);
+ if (saml1attr) {
+ const vector<XMLObject*>& values = saml1attr->getAttributeValues();
+ v = values.begin();
+ stop = values.end();
+ if (log.isDebugEnabled()) {
+ auto_ptr_char n(saml1attr->getAttributeName());
+ log.debug(
+ "decoding ScopedAttribute (%s) from SAML 1 Attribute (%s) with %lu value(s)",
+ ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()
+ );
+ }
+ }
+ else {
+ log.warn("XMLObject type not recognized by ScopedAttributeDecoder, no values returned");
+ return NULL;
+ }
+ }
+
+ for (; v!=stop; ++v) {
+ if (!(*v)->hasChildren()) {
+ val = toUTF8((*v)->getTextContent());
+ if (val && *val) {
+ const AttributeExtensibleXMLObject* aexo=dynamic_cast<const AttributeExtensibleXMLObject*>(*v);
+ xmlscope = aexo->getAttribute(scopeqname);
+ if (xmlscope && *xmlscope) {
+ scope = toUTF8(xmlscope);
+ dest.push_back(pair<string,string>(val,scope));
+ delete[] scope;
+ }
+ else {
+ scope = strchr(val, m_delimeter);
+ if (scope) {
+ *scope++ = 0;
+ if (*scope)
+ dest.push_back(pair<string,string>(val,scope));
+ else
+ log.warn("ignoring unscoped AttributeValue");
+ }
+ else {
+ log.warn("ignoring unscoped AttributeValue");
+ }
+ }
+ }
+ else {
+ log.warn("skipping empty AttributeValue");
+ }
+ delete[] val;
+ }
+ else {
+ log.warn("skipping complex AttributeValue");
+ }
+ }
+
+ return dest.empty() ? NULL : scoped.release();
+ }
+
+ const NameID* saml2name = dynamic_cast<const NameID*>(xmlObject);
+ if (saml2name) {
+ if (log.isDebugEnabled()) {
+ auto_ptr_char f(saml2name->getFormat());
+ log.debug("decoding ScopedAttribute (%s) from SAML 2 NameID with Format (%s)", ids.front().c_str(), f.get() ? f.get() : "unspecified");
+ }
+ val = toUTF8(saml2name->getName());
+ }
+ else {
+ const NameIdentifier* saml1name = dynamic_cast<const NameIdentifier*>(xmlObject);
+ if (saml1name) {
+ if (log.isDebugEnabled()) {
+ auto_ptr_char f(saml1name->getFormat());
+ log.debug(
+ "decoding ScopedAttribute (%s) from SAML 1 NameIdentifier with Format (%s)",
+ ids.front().c_str(), f.get() ? f.get() : "unspecified"
+ );
+ }
+ val = toUTF8(saml1name->getName());
+ }
+ else {
+ log.warn("XMLObject type not recognized by ScopedAttributeDecoder, no values returned");
+ return NULL;
+ }
+ }
+
+ if (val && *val && *val!=m_delimeter) {
+ scope = strchr(val, m_delimeter);
+ if (scope) {
+ *scope++ = 0;
+ if (*scope)
+ dest.push_back(pair<string,string>(val,scope));
+ else
+ log.warn("ignoring NameID with no scope");
+ }
+ else {
+ log.warn("ignoring NameID with no scope delimiter (%c)", m_delimeter);
+ }
+ }
+ else {
+ log.warn("ignoring empty NameID");
+ }
+ delete[] val;
+ return dest.empty() ? NULL : scoped.release();
+}
throw ConfigurationException("Child Rule found with no xsi:type.");
MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), make_pair(functorMap,e));
- functorMap->getMatchFunctors().insert(make_pair(id, func));
+ functorMap->getMatchFunctors().insert(multimap<string,MatchFunctor*>::value_type(id, func));
return func;
}
throw ConfigurationException("Child Rule found with no xsi:type.");
MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), make_pair(functorMap,e));
- functorMap->getMatchFunctors().insert(make_pair(id, func));
+ functorMap->getMatchFunctors().insert(multimap<string,MatchFunctor*>::value_type(id, func));
return func;
}
throw ConfigurationException("Child Rule found with no xsi:type.");
MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), make_pair(functorMap,e));
- functorMap->getMatchFunctors().insert(make_pair(id, func));
+ functorMap->getMatchFunctors().insert(multimap<string,MatchFunctor*>::value_type(id, func));
return func;
}
if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeRule)) {
pair<string,const MatchFunctor*> rule = buildAttributeRule(e, valFunctors, false);
if (rule.second)
- m_policies.back().m_rules.insert(rule);
+ m_policies.back().m_rules.insert(Policy::rules_t::value_type(rule.first, rule.second));
}
else if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeRuleReference)) {
auto_ptr_char ref(e->getAttributeNS(NULL, _ref));
if (ref.get() && *ref.get()) {
map< string,pair<string,const MatchFunctor*> >::const_iterator ar = m_attrRules.find(ref.get());
if (ar != m_attrRules.end())
- m_policies.back().m_rules.insert(ar->second);
+ m_policies.back().m_rules.insert(Policy::rules_t::value_type(ar->second.first, ar->second.second));
else
m_log.warn("skipping invalid AttributeRuleReference (%s)", ref.get());
}
if (type.get()) {
try {
MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), make_pair(&functorMap,e));
- functorMap.getMatchFunctors().insert(make_pair(id, func));
+ functorMap.getMatchFunctors().insert(multimap<string,MatchFunctor*>::value_type(id, func));
return func;
}
catch (exception& ex) {
if (standalone && !*id) {
m_log.warn("skipping stand-alone AttributeRule with no id");
- return make_pair(string(),(MatchFunctor*)NULL);
+ return make_pair(string(),(const MatchFunctor*)NULL);
}
else if (*id && m_attrRules.count(id)) {
if (standalone) {
m_log.warn("skipping duplicate stand-alone AttributeRule with id (%s)", id);
- return make_pair(string(),(MatchFunctor*)NULL);
+ return make_pair(string(),(const MatchFunctor*)NULL);
}
else
id = "";
if (func) {
if (*id)
- return m_attrRules[id] = make_pair(attrID.get(), func);
+ return m_attrRules[id] = pair<string,const MatchFunctor*>(attrID.get(), func);
else
- return make_pair(attrID.get(), func);
+ return pair<string,const MatchFunctor*>(attrID.get(), func);
}
m_log.warn("skipping AttributeRule (%s), PermitValueRule invalid or missing", id);
- return make_pair(string(),(MatchFunctor*)NULL);
+ return make_pair(string(),(const MatchFunctor*)NULL);
}
void XMLFilterImpl::filterAttributes(const FilteringContext& context, vector<Attribute*>& attributes) const
// Fetch/create the map entry and see if it's a duplicate rule.
#ifdef HAVE_GOOD_STL
- pair< AttributeDecoder*,vector<string> >& decl = m_attrMap[make_pair(name,format)];
+ pair< AttributeDecoder*,vector<string> >& decl = m_attrMap[pair<xstring,xstring>(name,format)];
#else
auto_ptr_char n(name);
auto_ptr_char f(format);
- pair< AttributeDecoder*,vector<string> >& decl = m_attrMap[make_pair(n.get(),f.get())];
+ pair< AttributeDecoder*,vector<string> >& decl = m_attrMap[pair<string,string>(n.get(),f.get())];
#endif
if (decl.first) {
m_log.warn("skipping duplicate Attribute mapping (same name and nameFormat)");
if (!format || !*format)
format = NameIdentifier::UNSPECIFIED;
#ifdef HAVE_GOOD_STL
- if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {
+ if ((rule=m_attrMap.find(pair<xstring,xstring>(format,xstring()))) != m_attrMap.end()) {
#else
auto_ptr_char temp(format);
- if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {
+ if ((rule=m_attrMap.find(pair<string,string>(temp.get(),string()))) != m_attrMap.end()) {
#endif
Attribute* a = rule->second.first->decode(rule->second.second, &nameid, assertingParty, application.getString("entityID").second);
if (a)
if (!format || !*format)
format = NameID::UNSPECIFIED;
#ifdef HAVE_GOOD_STL
- if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {
+ if ((rule=m_attrMap.find(pair<xstring,xstring>(format,xstring()))) != m_attrMap.end()) {
#else
auto_ptr_char temp(format);
- if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {
+ if ((rule=m_attrMap.find(pair<string,string>(temp.get(),string()))) != m_attrMap.end()) {
#endif
Attribute* a = rule->second.first->decode(rule->second.second, &nameid, assertingParty, application.getString("entityID").second);
if (a)
if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI))
format = &chNull;
#ifdef HAVE_GOOD_STL
- if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {
+ if ((rule=m_attrMap.find(pair<xstring,xstring>(name,format))) != m_attrMap.end()) {
#else
auto_ptr_char temp1(name);
auto_ptr_char temp2(format);
- if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {
+ if ((rule=m_attrMap.find(pair<string,string>(temp1.get(),temp2.get()))) != m_attrMap.end()) {
#endif
Attribute* a = rule->second.first->decode(rule->second.second, &attr, assertingParty, application.getString("entityID").second);
if (a)
else if (XMLString::equals(format, saml2::Attribute::URI_REFERENCE))
format = &chNull;
#ifdef HAVE_GOOD_STL
- if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {
+ if ((rule=m_attrMap.find(pair<xstring,xstring>(name,format))) != m_attrMap.end()) {
#else
auto_ptr_char temp1(name);
auto_ptr_char temp2(format);
- if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {
+ if ((rule=m_attrMap.find(pair<string,string>(temp1.get(),temp2.get()))) != m_attrMap.end()) {
#endif
Attribute* a = rule->second.first->decode(rule->second.second, &attr, assertingParty, application.getString("entityID").second);
if (a)
httpResponse.setContentType("application/samlassertion+xml");
return make_pair(true, httpResponse.sendResponse(s));
#else
- return make_pair(false,0);
+ return make_pair(false,0L);
#endif
}
httpResponse.setContentType("application/samlmetadata+xml");
return make_pair(true, httpResponse.sendResponse(s));
#else
- return make_pair(false,0);
+ return make_pair(false,0L);
#endif
}
return samlError(application, *req, httpResponse, StatusCode::RESPONDER, NULL, ex.what());
}
#else
- return make_pair(false,0);
+ return make_pair(false,0L);
#endif
}
SAMLConfig& conf = SAMLConfig::getConfig();
// Handle incoming binding.
- m_decoder = conf.MessageDecoderManager.newPlugin(getString("Binding").second,make_pair(e,shibspconstants::SHIB2SPCONFIG_NS));
+ m_decoder = conf.MessageDecoderManager.newPlugin(
+ getString("Binding").second, pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
+ );
m_decoder->setArtifactResolver(SPConfig::getConfig().getArtifactResolver());
if (m_decoder->isUserAgentPresent()) {
m_bindings.push_back(start);
try {
auto_ptr_char b(start);
- MessageEncoder * encoder = conf.MessageEncoderManager.newPlugin(b.get(),make_pair(e,shibspconstants::SHIB2SPCONFIG_NS));
+ MessageEncoder * encoder = conf.MessageEncoderManager.newPlugin(
+ b.get(), pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
+ );
m_encoders[start] = encoder;
m_log.debug("supporting outgoing front-channel binding (%s)", b.get());
}
}
else {
MessageEncoder* encoder = conf.MessageEncoderManager.newPlugin(
- getString("Binding").second,make_pair(e,shibspconstants::SHIB2SPCONFIG_NS)
+ getString("Binding").second, pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
);
m_encoders.insert(pair<const XMLCh*,MessageEncoder*>(NULL, encoder));
}
if (policy.getIssuerMetadata())
annotateException(&ex, policy.getIssuerMetadata()); // throws it
ex.raise();
- return make_pair(false,0); // never happen, satisfies compiler
+ return make_pair(false,0L); // never happen, satisfies compiler
#else
throw ConfigurationException("Cannot process logout message using lite version of shibsp library.");
#endif
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) {
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)) {
}
#ifndef SHIBSP_LITE
- pair<bool,long> ret = make_pair(false,0);
+ pair<bool,long> ret = make_pair(false,0L);
try {
// With a session in hand, we can create a LogoutRequest message, if we can find a compatible endpoint.
MetadataProvider* m = application.getMetadataProvider();
SAMLConfig& conf = SAMLConfig::getConfig();
// Handle incoming binding.
- m_decoder = conf.MessageDecoderManager.newPlugin(getString("Binding").second,make_pair(e,shibspconstants::SHIB2SPCONFIG_NS));
+ m_decoder = conf.MessageDecoderManager.newPlugin(
+ getString("Binding").second, pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
+ );
m_decoder->setArtifactResolver(SPConfig::getConfig().getArtifactResolver());
if (m_decoder->isUserAgentPresent()) {
m_bindings.push_back(start);
try {
auto_ptr_char b(start);
- MessageEncoder * encoder = conf.MessageEncoderManager.newPlugin(b.get(),make_pair(e,shibspconstants::SHIB2SPCONFIG_NS));
+ MessageEncoder * encoder = conf.MessageEncoderManager.newPlugin(
+ b.get(), pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
+ );
m_encoders[start] = encoder;
m_log.debug("supporting outgoing front-channel binding (%s)", b.get());
}
}
else {
MessageEncoder* encoder = conf.MessageEncoderManager.newPlugin(
- getString("Binding").second,make_pair(e,shibspconstants::SHIB2SPCONFIG_NS)
+ getString("Binding").second, pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
);
m_encoders.insert(pair<const XMLCh*,MessageEncoder*>(NULL, encoder));
}
if (policy.getIssuerMetadata())
annotateException(&ex, policy.getIssuerMetadata()); // throws it
ex.raise();
- return make_pair(false,0); // never happen, satisfies compiler
+ return make_pair(false,0L); // never happen, satisfies compiler
#else
throw ConfigurationException("Cannot process NameID mgmt message using lite version of shibsp library.");
#endif
// We have to know the IdP to function unless this is ECP.
if (!ECP && (!entityID || !*entityID))
- return make_pair(false,0);
+ return make_pair(false,0L);
string target;
const Handler* ACS=NULL;
encoder = m_ecp;
if (!encoder) {
m_log.error("MessageEncoder for PAOS binding not available");
- return make_pair(false,0);
+ return make_pair(false,0L);
}
}
else {
}
else if (!entity.second) {
m_log.error("unable to locate SAML 2.0 identity provider role for provider (%s)", entityID);
- return make_pair(false,0);
+ return make_pair(false,0L);
}
// Loop over the supportable outgoing bindings.
}
if (!ep || !encoder) {
m_log.error("unable to locate compatible SSO service for provider (%s)", entityID);
- return make_pair(false,0);
+ return make_pair(false,0L);
}
}
req.release(); // freed by encoder
return make_pair(true,ret);
#else
- return make_pair(false,0);
+ return make_pair(false,0L);
#endif
}
// The IdP CANNOT be specified for us to run. Otherwise, we'd be redirecting to a DS
// anytime the IdP's metadata was wrong.
if (entityID && *entityID)
- return make_pair(false,0);
+ return make_pair(false,0L);
string target;
const char* option;
{
// We have to know the IdP to function.
if (!entityID || !*entityID)
- return make_pair(false,0);
+ return make_pair(false,0L);
string target;
const Handler* ACS=NULL;
}
else if (!entity.second) {
m_log.error("unable to locate Shibboleth-aware identity provider role for provider (%s)", entityID);
- return make_pair(false,0);
+ return make_pair(false,0L);
}
const EndpointType* ep=EndpointManager<SingleSignOnService>(
dynamic_cast<const IDPSSODescriptor*>(entity.second)->getSingleSignOnServices()
).getByBinding(shibspconstants::SHIB1_AUTHNREQUEST_PROFILE_URI);
if (!ep) {
m_log.error("unable to locate compatible SSO service for provider (%s)", entityID);
- return make_pair(false,0);
+ return make_pair(false,0L);
}
preserveRelayState(app, httpResponse, relayState);
return make_pair(true, httpResponse.sendRedirect(req.c_str()));
#else
- return make_pair(false,0);
+ return make_pair(false,0L);
#endif
}
httpResponse.setContentType("text/xml");
return make_pair(true, httpResponse.sendResponse(s));
#else
- return make_pair(false,0);
+ return make_pair(false,0L);
#endif
}
// The IdP CANNOT be specified for us to run. Otherwise, we'd be redirecting to a WAYF
// anytime the IdP's metadata was wrong.
if (entityID && *entityID)
- return make_pair(false,0);
+ return make_pair(false,0L);
string target;
const char* option;
-/*\r
- * Copyright 2001-2007 Internet2\r
- * \r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/**\r
- * RemotedSessionCache.cpp\r
- * \r
- * SessionCache implementation that delegates to a remoted version.\r
- */\r
-\r
-#include "internal.h"\r
-#include "Application.h"\r
-#include "exceptions.h"\r
-#include "ServiceProvider.h"\r
-#include "SessionCache.h"\r
-#include "attribute/Attribute.h"\r
-#include "remoting/ListenerService.h"\r
-#include "util/SPConstants.h"\r
-\r
-#include <ctime>\r
-#include <sstream>\r
-#include <xmltooling/XMLToolingConfig.h>\r
-#include <xmltooling/util/DateTime.h>\r
-#include <xmltooling/util/NDC.h>\r
-#include <xmltooling/util/XMLHelper.h>\r
-\r
-using namespace shibsp;\r
-using namespace xmltooling;\r
-using namespace std;\r
-\r
-namespace shibsp {\r
-\r
- class RemotedCache;\r
- class RemotedSession : public virtual Session\r
- {\r
- public:\r
- RemotedSession(RemotedCache* cache, DDF& obj) : m_version(obj["version"].integer()), m_obj(obj),\r
- m_expires(0), m_lastAccess(time(NULL)), m_cache(cache), m_lock(NULL) {\r
- auto_ptr_XMLCh exp(m_obj["expires"].string());\r
- if (exp.get()) {\r
- DateTime iso(exp.get());\r
- iso.parseDateTime();\r
- m_expires = iso.getEpoch();\r
- }\r
-\r
- m_lock = Mutex::create();\r
- }\r
- \r
- ~RemotedSession() {\r
- delete m_lock;\r
- m_obj.destroy();\r
- for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
- }\r
- \r
- Lockable* lock() {\r
- m_lock->lock();\r
- return this;\r
- }\r
- void unlock() {\r
- m_lock->unlock();\r
- }\r
-\r
- const char* getID() const {\r
- return m_obj.name();\r
- }\r
- const char* getApplicationID() const {\r
- return m_obj["application_id"].string();\r
- }\r
- const char* getClientAddress() const {\r
- return m_obj["client_addr"].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 char* getSessionIndex() const {\r
- return m_obj["session_index"].string();\r
- }\r
- const char* getAuthnContextClassRef() const {\r
- return m_obj["authncontext_class"].string();\r
- }\r
- const char* getAuthnContextDeclRef() const {\r
- return m_obj["authncontext_decl"].string();\r
- }\r
- const vector<Attribute*>& getAttributes() const {\r
- if (m_attributes.empty())\r
- unmarshallAttributes();\r
- return m_attributes;\r
- }\r
- const multimap<string,const Attribute*>& getIndexedAttributes() const {\r
- if (m_attributeIndex.empty()) {\r
- if (m_attributes.empty())\r
- unmarshallAttributes();\r
- for (vector<Attribute*>::const_iterator a = m_attributes.begin(); a != m_attributes.end(); ++a) {\r
- const vector<string>& aliases = (*a)->getAliases();\r
- for (vector<string>::const_iterator alias = aliases.begin(); alias != aliases.end(); ++alias)\r
- m_attributeIndex.insert(make_pair(*alias, *a));\r
- }\r
- }\r
- return m_attributeIndex;\r
- }\r
- const vector<const char*>& getAssertionIDs() const {\r
- if (m_ids.empty()) {\r
- DDF ids = m_obj["assertions"];\r
- DDF id = ids.first();\r
- while (id.isstring()) {\r
- m_ids.push_back(id.string());\r
- id = ids.next();\r
- }\r
- }\r
- return m_ids;\r
- }\r
- \r
- time_t expires() const { return m_expires; }\r
- time_t lastAccess() const { return m_lastAccess; }\r
- void validate(const Application& application, const char* client_addr, time_t* timeout);\r
-\r
- private:\r
- void unmarshallAttributes() const;\r
-\r
- int m_version;\r
- mutable DDF m_obj;\r
- mutable vector<Attribute*> m_attributes;\r
- mutable multimap<string,const Attribute*> m_attributeIndex;\r
- mutable vector<const char*> m_ids;\r
- time_t m_expires,m_lastAccess;\r
- RemotedCache* m_cache;\r
- Mutex* m_lock;\r
- };\r
- \r
- class RemotedCache : public SessionCache\r
- {\r
- public:\r
- RemotedCache(const DOMElement* e);\r
- ~RemotedCache();\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
- \r
- void cleanup();\r
- \r
- Category& m_log;\r
- private:\r
- const DOMElement* m_root; // Only valid during initialization\r
- RWLock* m_lock;\r
- map<string,RemotedSession*> m_hashtable;\r
- \r
- void dormant(const char* key);\r
- static void* cleanup_fn(void*);\r
- bool shutdown;\r
- CondWait* shutdown_wait;\r
- Thread* cleanup_thread;\r
- };\r
-\r
- SessionCache* SHIBSP_DLLLOCAL RemotedCacheFactory(const DOMElement* const & e)\r
- {\r
- return new RemotedCache(e);\r
- }\r
-}\r
-\r
-void RemotedSession::unmarshallAttributes() const\r
-{\r
- Attribute* attribute;\r
- DDF attrs = m_obj["attributes"];\r
- DDF attr = attrs.first();\r
- while (!attr.isnull()) {\r
- try {\r
- attribute = Attribute::unmarshall(attr);\r
- m_attributes.push_back(attribute);\r
- if (m_cache->m_log.isDebugEnabled())\r
- m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s",\r
- attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");\r
- }\r
- catch (AttributeException& ex) {\r
- const char* id = attr.first().name();\r
- m_cache->m_log.error("error unmarshalling attribute (ID: %s): %s", id ? id : "none", ex.what());\r
- }\r
- attr = attrs.next();\r
- }\r
-}\r
-\r
-void RemotedSession::validate(const Application& application, const char* client_addr, time_t* timeout)\r
-{\r
- // Basic expiration?\r
- time_t now = time(NULL);\r
- if (now > m_expires) {\r
- m_cache->m_log.info("session expired (ID: %s)", getID());\r
- throw opensaml::RetryableProfileException("Your session has expired, and you must re-authenticate.");\r
- }\r
-\r
- // Address check?\r
- if (client_addr) {\r
- if (m_cache->m_log.isDebugEnabled())\r
- m_cache->m_log.debug("comparing client address %s against %s", client_addr, getClientAddress());\r
- if (strcmp(getClientAddress(),client_addr)) {\r
- m_cache->m_log.warn("client address mismatch");\r
- throw opensaml::RetryableProfileException(\r
- "Your IP address ($1) does not match the address recorded at the time the session was established.",\r
- params(1,client_addr)\r
- );\r
- }\r
- }\r
-\r
- if (!timeout)\r
- return;\r
- \r
- DDF in("touch::"REMOTED_SESSION_CACHE"::SessionCache"), out;\r
- DDFJanitor jin(in);\r
- in.structure();\r
- in.addmember("key").string(getID());\r
- in.addmember("version").integer(m_obj["version"].integer());\r
- if (*timeout) {\r
- // On 64-bit Windows, time_t doesn't fit in a long, so I'm using ISO timestamps. \r
-#ifndef HAVE_GMTIME_R\r
- struct tm* ptime=gmtime(timeout);\r
-#else\r
- struct tm res;\r
- struct tm* ptime=gmtime_r(timeout,&res);\r
-#endif\r
- char timebuf[32];\r
- strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);\r
- in.addmember("timeout").string(timebuf);\r
- }\r
-\r
- try {\r
- out=application.getServiceProvider().getListenerService()->send(in);\r
- }\r
- catch (...) {\r
- out.destroy();\r
- throw;\r
- }\r
-\r
- if (out.isstruct()) {\r
- // We got an updated record back.\r
- m_ids.clear();\r
- for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
- m_attributes.clear();\r
- m_attributeIndex.clear();\r
- m_obj.destroy();\r
- m_obj = out;\r
- }\r
-\r
- m_lastAccess = now;\r
-}\r
-\r
-RemotedCache::RemotedCache(const DOMElement* e)\r
- : SessionCache(e, 900), m_log(Category::getInstance(SHIBSP_LOGCAT".SessionCache")), m_root(e), m_lock(NULL), shutdown(false)\r
-{\r
- if (!SPConfig::getConfig().getServiceProvider()->getListenerService())\r
- throw ConfigurationException("RemotedCacheService requires a ListenerService, but none available.");\r
- \r
- m_lock = RWLock::create();\r
- shutdown_wait = CondWait::create();\r
- cleanup_thread = Thread::create(&cleanup_fn, (void*)this);\r
-}\r
-\r
-RemotedCache::~RemotedCache()\r
-{\r
- // Shut down the cleanup thread and let it know...\r
- shutdown = true;\r
- shutdown_wait->signal();\r
- cleanup_thread->join(NULL);\r
-\r
- for_each(m_hashtable.begin(),m_hashtable.end(),xmltooling::cleanup_pair<string,RemotedSession>());\r
- delete m_lock;\r
- delete shutdown_wait;\r
-}\r
-\r
-Session* RemotedCache::find(const char* key, const Application& application, const char* client_addr, time_t* timeout)\r
-{\r
-#ifdef _DEBUG\r
- xmltooling::NDC ndc("find");\r
-#endif\r
-\r
- RemotedSession* session=NULL;\r
- m_log.debug("searching local cache for session (%s)", key);\r
- m_lock->rdlock();\r
- map<string,RemotedSession*>::const_iterator i=m_hashtable.find(key);\r
- if (i==m_hashtable.end()) {\r
- m_lock->unlock();\r
- m_log.debug("session not found locally, searching remote cache");\r
-\r
- DDF in("find::"REMOTED_SESSION_CACHE"::SessionCache"), out;\r
- DDFJanitor jin(in);\r
- in.structure();\r
- in.addmember("key").string(key);\r
- in.addmember("application_id").string(application.getId());\r
- if (timeout && *timeout) {\r
- // On 64-bit Windows, time_t doesn't fit in a long, so I'm using ISO timestamps. \r
-#ifndef HAVE_GMTIME_R\r
- struct tm* ptime=gmtime(timeout);\r
-#else\r
- struct tm res;\r
- struct tm* ptime=gmtime_r(timeout,&res);\r
-#endif\r
- char timebuf[32];\r
- strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);\r
- in.addmember("timeout").string(timebuf);\r
- }\r
- \r
- try {\r
- out=application.getServiceProvider().getListenerService()->send(in);\r
- if (!out.isstruct()) {\r
- out.destroy();\r
- m_log.debug("session not found in remote cache");\r
- return NULL;\r
- }\r
- \r
- // Wrap the results in a local entry and save it.\r
- session = new RemotedSession(this, out);\r
- // The remote end has handled timeout issues, we handle address and expiration checks.\r
- timeout = NULL;\r
- }\r
- catch (...) {\r
- out.destroy();\r
- throw;\r
- }\r
-\r
- // Lock for writing and repeat the search to avoid duplication.\r
- m_lock->wrlock();\r
- SharedLock shared(m_lock, false);\r
- if (m_hashtable.count(key)) {\r
- // We're using an existing session entry.\r
- delete session;\r
- session = m_hashtable[key];\r
- session->lock();\r
- }\r
- else {\r
- m_hashtable[key]=session;\r
- session->lock();\r
- }\r
- }\r
- else {\r
- // Save off and lock the session.\r
- session = i->second;\r
- session->lock();\r
- m_lock->unlock();\r
- \r
- m_log.debug("session found locally, validating it for use");\r
- }\r
-\r
- if (!XMLString::equals(session->getApplicationID(), application.getId())) {\r
- m_log.error("an application (%s) tried to access another application's session", application.getId());\r
- session->unlock();\r
- return NULL;\r
- }\r
-\r
- // Verify currency and update the timestamp if indicated by caller.\r
- try {\r
- session->validate(application, client_addr, timeout);\r
- }\r
- catch (...) {\r
- session->unlock();\r
- remove(key, application);\r
- throw;\r
- }\r
- \r
- return session;\r
-}\r
-\r
-void RemotedCache::remove(const char* key, const Application& application)\r
-{\r
- // Take care of local copy.\r
- dormant(key);\r
- \r
- // Now remote...\r
- DDF in("remove::"REMOTED_SESSION_CACHE"::SessionCache");\r
- DDFJanitor jin(in);\r
- in.structure();\r
- in.addmember("key").string(key);\r
- in.addmember("application_id").string(application.getId());\r
- \r
- DDF out = application.getServiceProvider().getListenerService()->send(in);\r
- out.destroy();\r
-}\r
-\r
-void RemotedCache::dormant(const char* key)\r
-{\r
-#ifdef _DEBUG\r
- xmltooling::NDC ndc("dormant");\r
-#endif\r
-\r
- m_log.debug("deleting local copy of session (%s)", key);\r
-\r
- // lock the cache for writing, which means we know nobody is sitting in find()\r
- m_lock->wrlock();\r
-\r
- // grab the entry from the table\r
- map<string,RemotedSession*>::const_iterator i=m_hashtable.find(key);\r
- if (i==m_hashtable.end()) {\r
- m_lock->unlock();\r
- return;\r
- }\r
-\r
- // ok, remove the entry and lock it\r
- RemotedSession* entry=i->second;\r
- m_hashtable.erase(key);\r
- entry->lock();\r
- \r
- // unlock the cache\r
- m_lock->unlock();\r
-\r
- // we can release the cache entry lock because we know we're not in the cache anymore\r
- entry->unlock();\r
-\r
- delete entry;\r
-}\r
-\r
-void RemotedCache::cleanup()\r
-{\r
-#ifdef _DEBUG\r
- xmltooling::NDC ndc("cleanup");\r
-#endif\r
-\r
- Mutex* mutex = Mutex::create();\r
- \r
- // Load our configuration details...\r
- static const XMLCh cleanupInterval[] = UNICODE_LITERAL_15(c,l,e,a,n,u,p,I,n,t,e,r,v,a,l);\r
- const XMLCh* tag=m_root ? m_root->getAttributeNS(NULL,cleanupInterval) : NULL;\r
- int rerun_timer = 900;\r
- if (tag && *tag)\r
- rerun_timer = XMLString::parseInt(tag);\r
- if (rerun_timer <= 0)\r
- rerun_timer = 900;\r
-\r
- mutex->lock();\r
-\r
- m_log.info("cleanup thread started...run every %d secs; timeout after %d secs", rerun_timer, m_cacheTimeout);\r
-\r
- while (!shutdown) {\r
- shutdown_wait->timedwait(mutex,rerun_timer);\r
- if (shutdown)\r
- break;\r
-\r
- // Ok, let's run through the cleanup process and clean out\r
- // really old sessions. This is a two-pass process. The\r
- // first pass is done holding a read-lock while we iterate over\r
- // the cache. The second pass doesn't need a lock because\r
- // the 'deletes' will lock the cache.\r
- \r
- // Pass 1: iterate over the map and find all entries that have not been\r
- // used in X hours\r
- vector<string> stale_keys;\r
- time_t stale = time(NULL) - m_cacheTimeout;\r
- \r
- m_log.debug("cleanup thread running");\r
-\r
- m_lock->rdlock();\r
- for (map<string,RemotedSession*>::const_iterator i=m_hashtable.begin(); i!=m_hashtable.end(); ++i) {\r
- // If the last access was BEFORE the stale timeout...\r
- i->second->lock();\r
- time_t last=i->second->lastAccess();\r
- i->second->unlock();\r
- if (last < stale)\r
- stale_keys.push_back(i->first);\r
- }\r
- m_lock->unlock();\r
- \r
- if (!stale_keys.empty()) {\r
- m_log.info("purging %d old sessions", stale_keys.size());\r
- \r
- // Pass 2: walk through the list of stale entries and remove them from the cache\r
- for (vector<string>::const_iterator j = stale_keys.begin(); j != stale_keys.end(); ++j)\r
- dormant(j->c_str());\r
- }\r
-\r
- m_log.debug("cleanup thread completed");\r
- }\r
-\r
- m_log.info("cleanup thread exiting");\r
-\r
- mutex->unlock();\r
- delete mutex;\r
- Thread::exit(NULL);\r
-}\r
-\r
-void* RemotedCache::cleanup_fn(void* cache_p)\r
-{\r
- RemotedCache* cache = reinterpret_cast<RemotedCache*>(cache_p);\r
-\r
-#ifndef WIN32\r
- // First, let's block all signals \r
- Thread::mask_all_signals();\r
-#endif\r
-\r
- // Now run the cleanup process.\r
- cache->cleanup();\r
- return NULL;\r
-}\r
+/*
+ * 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.
+ */
+
+/**
+ * RemotedSessionCache.cpp
+ *
+ * SessionCache implementation that delegates to a remoted version.
+ */
+
+#include "internal.h"
+#include "Application.h"
+#include "exceptions.h"
+#include "ServiceProvider.h"
+#include "SessionCache.h"
+#include "attribute/Attribute.h"
+#include "remoting/ListenerService.h"
+#include "util/SPConstants.h"
+
+#include <ctime>
+#include <sstream>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/DateTime.h>
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/XMLHelper.h>
+
+using namespace shibsp;
+using namespace xmltooling;
+using namespace std;
+
+namespace shibsp {
+
+ class RemotedCache;
+ class RemotedSession : public virtual Session
+ {
+ public:
+ RemotedSession(RemotedCache* cache, DDF& obj) : m_version(obj["version"].integer()), m_obj(obj),
+ m_expires(0), m_lastAccess(time(NULL)), m_cache(cache), m_lock(NULL) {
+ auto_ptr_XMLCh exp(m_obj["expires"].string());
+ if (exp.get()) {
+ DateTime iso(exp.get());
+ iso.parseDateTime();
+ m_expires = iso.getEpoch();
+ }
+
+ m_lock = Mutex::create();
+ }
+
+ ~RemotedSession() {
+ delete m_lock;
+ m_obj.destroy();
+ for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());
+ }
+
+ Lockable* lock() {
+ m_lock->lock();
+ return this;
+ }
+ void unlock() {
+ m_lock->unlock();
+ }
+
+ const char* getID() const {
+ return m_obj.name();
+ }
+ const char* getApplicationID() const {
+ return m_obj["application_id"].string();
+ }
+ const char* getClientAddress() const {
+ return m_obj["client_addr"].string();
+ }
+ const char* getEntityID() const {
+ return m_obj["entity_id"].string();
+ }
+ const char* getProtocol() const {
+ return m_obj["protocol"].string();
+ }
+ const char* getAuthnInstant() const {
+ return m_obj["authn_instant"].string();
+ }
+ const char* getSessionIndex() const {
+ return m_obj["session_index"].string();
+ }
+ const char* getAuthnContextClassRef() const {
+ return m_obj["authncontext_class"].string();
+ }
+ const char* getAuthnContextDeclRef() const {
+ return m_obj["authncontext_decl"].string();
+ }
+ const vector<Attribute*>& getAttributes() const {
+ if (m_attributes.empty())
+ unmarshallAttributes();
+ return m_attributes;
+ }
+ const multimap<string,const Attribute*>& getIndexedAttributes() const {
+ if (m_attributeIndex.empty()) {
+ if (m_attributes.empty())
+ unmarshallAttributes();
+ for (vector<Attribute*>::const_iterator a = m_attributes.begin(); a != m_attributes.end(); ++a) {
+ const vector<string>& aliases = (*a)->getAliases();
+ for (vector<string>::const_iterator alias = aliases.begin(); alias != aliases.end(); ++alias)
+ m_attributeIndex.insert(multimap<string,const Attribute*>::value_type(*alias, *a));
+ }
+ }
+ return m_attributeIndex;
+ }
+ const vector<const char*>& getAssertionIDs() const {
+ if (m_ids.empty()) {
+ DDF ids = m_obj["assertions"];
+ DDF id = ids.first();
+ while (id.isstring()) {
+ m_ids.push_back(id.string());
+ id = ids.next();
+ }
+ }
+ return m_ids;
+ }
+
+ time_t expires() const { return m_expires; }
+ time_t lastAccess() const { return m_lastAccess; }
+ void validate(const Application& application, const char* client_addr, time_t* timeout);
+
+ private:
+ void unmarshallAttributes() const;
+
+ int m_version;
+ mutable DDF m_obj;
+ mutable vector<Attribute*> m_attributes;
+ mutable multimap<string,const Attribute*> m_attributeIndex;
+ mutable vector<const char*> m_ids;
+ time_t m_expires,m_lastAccess;
+ RemotedCache* m_cache;
+ Mutex* m_lock;
+ };
+
+ class RemotedCache : public SessionCache
+ {
+ public:
+ RemotedCache(const DOMElement* e);
+ ~RemotedCache();
+
+ Session* find(const char* key, const Application& application, const char* client_addr=NULL, time_t* timeout=NULL);
+ void remove(const char* key, const Application& application);
+
+ void cleanup();
+
+ Category& m_log;
+ private:
+ const DOMElement* m_root; // Only valid during initialization
+ RWLock* m_lock;
+ map<string,RemotedSession*> m_hashtable;
+
+ void dormant(const char* key);
+ static void* cleanup_fn(void*);
+ bool shutdown;
+ CondWait* shutdown_wait;
+ Thread* cleanup_thread;
+ };
+
+ SessionCache* SHIBSP_DLLLOCAL RemotedCacheFactory(const DOMElement* const & e)
+ {
+ return new RemotedCache(e);
+ }
+}
+
+void RemotedSession::unmarshallAttributes() const
+{
+ Attribute* attribute;
+ DDF attrs = m_obj["attributes"];
+ DDF attr = attrs.first();
+ while (!attr.isnull()) {
+ try {
+ attribute = Attribute::unmarshall(attr);
+ m_attributes.push_back(attribute);
+ if (m_cache->m_log.isDebugEnabled())
+ m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s",
+ attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");
+ }
+ catch (AttributeException& ex) {
+ const char* id = attr.first().name();
+ m_cache->m_log.error("error unmarshalling attribute (ID: %s): %s", id ? id : "none", ex.what());
+ }
+ attr = attrs.next();
+ }
+}
+
+void RemotedSession::validate(const Application& application, const char* client_addr, time_t* timeout)
+{
+ // Basic expiration?
+ time_t now = time(NULL);
+ if (now > m_expires) {
+ m_cache->m_log.info("session expired (ID: %s)", getID());
+ throw opensaml::RetryableProfileException("Your session has expired, and you must re-authenticate.");
+ }
+
+ // Address check?
+ if (client_addr) {
+ if (m_cache->m_log.isDebugEnabled())
+ m_cache->m_log.debug("comparing client address %s against %s", client_addr, getClientAddress());
+ if (strcmp(getClientAddress(),client_addr)) {
+ m_cache->m_log.warn("client address mismatch");
+ throw opensaml::RetryableProfileException(
+ "Your IP address ($1) does not match the address recorded at the time the session was established.",
+ params(1,client_addr)
+ );
+ }
+ }
+
+ if (!timeout)
+ return;
+
+ DDF in("touch::"REMOTED_SESSION_CACHE"::SessionCache"), out;
+ DDFJanitor jin(in);
+ in.structure();
+ in.addmember("key").string(getID());
+ in.addmember("version").integer(m_obj["version"].integer());
+ if (*timeout) {
+ // On 64-bit Windows, time_t doesn't fit in a long, so I'm using ISO timestamps.
+#ifndef HAVE_GMTIME_R
+ struct tm* ptime=gmtime(timeout);
+#else
+ struct tm res;
+ struct tm* ptime=gmtime_r(timeout,&res);
+#endif
+ char timebuf[32];
+ strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);
+ in.addmember("timeout").string(timebuf);
+ }
+
+ try {
+ out=application.getServiceProvider().getListenerService()->send(in);
+ }
+ catch (...) {
+ out.destroy();
+ throw;
+ }
+
+ if (out.isstruct()) {
+ // We got an updated record back.
+ m_ids.clear();
+ for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());
+ m_attributes.clear();
+ m_attributeIndex.clear();
+ m_obj.destroy();
+ m_obj = out;
+ }
+
+ m_lastAccess = now;
+}
+
+RemotedCache::RemotedCache(const DOMElement* e)
+ : SessionCache(e, 900), m_log(Category::getInstance(SHIBSP_LOGCAT".SessionCache")), m_root(e), m_lock(NULL), shutdown(false)
+{
+ if (!SPConfig::getConfig().getServiceProvider()->getListenerService())
+ throw ConfigurationException("RemotedCacheService requires a ListenerService, but none available.");
+
+ m_lock = RWLock::create();
+ shutdown_wait = CondWait::create();
+ cleanup_thread = Thread::create(&cleanup_fn, (void*)this);
+}
+
+RemotedCache::~RemotedCache()
+{
+ // Shut down the cleanup thread and let it know...
+ shutdown = true;
+ shutdown_wait->signal();
+ cleanup_thread->join(NULL);
+
+ for_each(m_hashtable.begin(),m_hashtable.end(),xmltooling::cleanup_pair<string,RemotedSession>());
+ delete m_lock;
+ delete shutdown_wait;
+}
+
+Session* RemotedCache::find(const char* key, const Application& application, const char* client_addr, time_t* timeout)
+{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("find");
+#endif
+
+ RemotedSession* session=NULL;
+ m_log.debug("searching local cache for session (%s)", key);
+ m_lock->rdlock();
+ map<string,RemotedSession*>::const_iterator i=m_hashtable.find(key);
+ if (i==m_hashtable.end()) {
+ m_lock->unlock();
+ m_log.debug("session not found locally, searching remote cache");
+
+ DDF in("find::"REMOTED_SESSION_CACHE"::SessionCache"), out;
+ DDFJanitor jin(in);
+ in.structure();
+ in.addmember("key").string(key);
+ in.addmember("application_id").string(application.getId());
+ if (timeout && *timeout) {
+ // On 64-bit Windows, time_t doesn't fit in a long, so I'm using ISO timestamps.
+#ifndef HAVE_GMTIME_R
+ struct tm* ptime=gmtime(timeout);
+#else
+ struct tm res;
+ struct tm* ptime=gmtime_r(timeout,&res);
+#endif
+ char timebuf[32];
+ strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);
+ in.addmember("timeout").string(timebuf);
+ }
+
+ try {
+ out=application.getServiceProvider().getListenerService()->send(in);
+ if (!out.isstruct()) {
+ out.destroy();
+ m_log.debug("session not found in remote cache");
+ return NULL;
+ }
+
+ // Wrap the results in a local entry and save it.
+ session = new RemotedSession(this, out);
+ // The remote end has handled timeout issues, we handle address and expiration checks.
+ timeout = NULL;
+ }
+ catch (...) {
+ out.destroy();
+ throw;
+ }
+
+ // Lock for writing and repeat the search to avoid duplication.
+ m_lock->wrlock();
+ SharedLock shared(m_lock, false);
+ if (m_hashtable.count(key)) {
+ // We're using an existing session entry.
+ delete session;
+ session = m_hashtable[key];
+ session->lock();
+ }
+ else {
+ m_hashtable[key]=session;
+ session->lock();
+ }
+ }
+ else {
+ // Save off and lock the session.
+ session = i->second;
+ session->lock();
+ m_lock->unlock();
+
+ m_log.debug("session found locally, validating it for use");
+ }
+
+ if (!XMLString::equals(session->getApplicationID(), application.getId())) {
+ m_log.error("an application (%s) tried to access another application's session", application.getId());
+ session->unlock();
+ return NULL;
+ }
+
+ // Verify currency and update the timestamp if indicated by caller.
+ try {
+ session->validate(application, client_addr, timeout);
+ }
+ catch (...) {
+ session->unlock();
+ remove(key, application);
+ throw;
+ }
+
+ return session;
+}
+
+void RemotedCache::remove(const char* key, const Application& application)
+{
+ // Take care of local copy.
+ dormant(key);
+
+ // Now remote...
+ DDF in("remove::"REMOTED_SESSION_CACHE"::SessionCache");
+ DDFJanitor jin(in);
+ in.structure();
+ in.addmember("key").string(key);
+ in.addmember("application_id").string(application.getId());
+
+ DDF out = application.getServiceProvider().getListenerService()->send(in);
+ out.destroy();
+}
+
+void RemotedCache::dormant(const char* key)
+{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("dormant");
+#endif
+
+ m_log.debug("deleting local copy of session (%s)", key);
+
+ // lock the cache for writing, which means we know nobody is sitting in find()
+ m_lock->wrlock();
+
+ // grab the entry from the table
+ map<string,RemotedSession*>::const_iterator i=m_hashtable.find(key);
+ if (i==m_hashtable.end()) {
+ m_lock->unlock();
+ return;
+ }
+
+ // ok, remove the entry and lock it
+ RemotedSession* entry=i->second;
+ m_hashtable.erase(key);
+ entry->lock();
+
+ // unlock the cache
+ m_lock->unlock();
+
+ // we can release the cache entry lock because we know we're not in the cache anymore
+ entry->unlock();
+
+ delete entry;
+}
+
+void RemotedCache::cleanup()
+{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("cleanup");
+#endif
+
+ Mutex* mutex = Mutex::create();
+
+ // Load our configuration details...
+ static const XMLCh cleanupInterval[] = UNICODE_LITERAL_15(c,l,e,a,n,u,p,I,n,t,e,r,v,a,l);
+ const XMLCh* tag=m_root ? m_root->getAttributeNS(NULL,cleanupInterval) : NULL;
+ int rerun_timer = 900;
+ if (tag && *tag)
+ rerun_timer = XMLString::parseInt(tag);
+ if (rerun_timer <= 0)
+ rerun_timer = 900;
+
+ mutex->lock();
+
+ m_log.info("cleanup thread started...run every %d secs; timeout after %d secs", rerun_timer, m_cacheTimeout);
+
+ while (!shutdown) {
+ shutdown_wait->timedwait(mutex,rerun_timer);
+ if (shutdown)
+ break;
+
+ // Ok, let's run through the cleanup process and clean out
+ // really old sessions. This is a two-pass process. The
+ // first pass is done holding a read-lock while we iterate over
+ // the cache. The second pass doesn't need a lock because
+ // the 'deletes' will lock the cache.
+
+ // Pass 1: iterate over the map and find all entries that have not been
+ // used in X hours
+ vector<string> stale_keys;
+ time_t stale = time(NULL) - m_cacheTimeout;
+
+ m_log.debug("cleanup thread running");
+
+ m_lock->rdlock();
+ for (map<string,RemotedSession*>::const_iterator i=m_hashtable.begin(); i!=m_hashtable.end(); ++i) {
+ // If the last access was BEFORE the stale timeout...
+ i->second->lock();
+ time_t last=i->second->lastAccess();
+ i->second->unlock();
+ if (last < stale)
+ stale_keys.push_back(i->first);
+ }
+ m_lock->unlock();
+
+ if (!stale_keys.empty()) {
+ m_log.info("purging %d old sessions", stale_keys.size());
+
+ // Pass 2: walk through the list of stale entries and remove them from the cache
+ for (vector<string>::const_iterator j = stale_keys.begin(); j != stale_keys.end(); ++j)
+ dormant(j->c_str());
+ }
+
+ m_log.debug("cleanup thread completed");
+ }
+
+ m_log.info("cleanup thread exiting");
+
+ mutex->unlock();
+ delete mutex;
+ Thread::exit(NULL);
+}
+
+void* RemotedCache::cleanup_fn(void* cache_p)
+{
+ RemotedCache* cache = reinterpret_cast<RemotedCache*>(cache_p);
+
+#ifndef WIN32
+ // First, let's block all signals
+ Thread::mask_all_signals();
+#endif
+
+ // Now run the cleanup process.
+ cache->cleanup();
+ return NULL;
+}
for (vector<Attribute*>::const_iterator a = m_attributes.begin(); a != m_attributes.end(); ++a) {
const vector<string>& aliases = (*a)->getAliases();
for (vector<string>::const_iterator alias = aliases.begin(); alias != aliases.end(); ++alias)
- m_attributeIndex.insert(make_pair(*alias, *a));
+ m_attributeIndex.insert(multimap<string,const Attribute*>::value_type(*alias, *a));
}
}
return m_attributeIndex;
try {
attribute = Attribute::unmarshall(attr);
m_attributes.push_back(attribute);
- m_attributeIndex.insert(make_pair(attribute->getId(), attribute));
+ m_attributeIndex.insert(multimap<string,const Attribute*>::value_type(attribute->getId(), attribute));
if (m_cache->m_log.isDebugEnabled())
m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s",
attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");
-/*\r
- * Copyright 2001-2007 Internet2\r
- * \r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/** XMLRequestMapper.cpp\r
- * \r
- * XML-based RequestMapper implementation\r
- */\r
-\r
-#include "internal.h"\r
-#include "exceptions.h"\r
-#include "AccessControl.h"\r
-#include "RequestMapper.h"\r
-#include "SPRequest.h"\r
-#include "util/DOMPropertySet.h"\r
-#include "util/SPConstants.h"\r
-\r
-#include <xmltooling/util/NDC.h>\r
-#include <xmltooling/util/ReloadableXMLFile.h>\r
-#include <xmltooling/util/XMLHelper.h>\r
-#include <xercesc/util/XMLUniDefs.hpp>\r
-#include <xercesc/util/regx/RegularExpression.hpp>\r
-\r
-using namespace shibsp;\r
-using namespace xmltooling;\r
-using namespace std;\r
-\r
-namespace shibsp {\r
-\r
- // Blocks access when an ACL plugin fails to load. \r
- class AccessControlDummy : public AccessControl\r
- {\r
- public:\r
- Lockable* lock() {\r
- return this;\r
- }\r
- \r
- void unlock() {}\r
- \r
- aclresult_t authorized(const SPRequest& request, const Session* session) const {\r
- return shib_acl_false;\r
- }\r
- };\r
-\r
- class Override : public DOMPropertySet, public DOMNodeFilter\r
- {\r
- public:\r
- Override() : m_acl(NULL) {}\r
- Override(const DOMElement* e, Category& log, const Override* base=NULL);\r
- ~Override();\r
-\r
- // Provides filter to exclude special config elements.\r
- short acceptNode(const DOMNode* node) const {\r
- return FILTER_REJECT;\r
- }\r
-\r
- const Override* locate(const HTTPRequest& request) const;\r
- AccessControl* getAC() const { return (m_acl ? m_acl : (getParent() ? dynamic_cast<const Override*>(getParent())->getAC() : NULL)); }\r
- \r
- protected:\r
- void loadACL(const DOMElement* e, Category& log);\r
- \r
- map<string,Override*> m_map;\r
- vector< pair<RegularExpression*,Override*> > m_regexps;\r
- vector< pair< pair<string,RegularExpression*>,Override*> > m_queries;\r
- \r
- private:\r
- AccessControl* m_acl;\r
- };\r
-\r
- class XMLRequestMapperImpl : public Override\r
- {\r
- public:\r
- XMLRequestMapperImpl(const DOMElement* e, Category& log);\r
-\r
- ~XMLRequestMapperImpl() {\r
- if (m_document)\r
- m_document->release();\r
- }\r
-\r
- void setDocument(DOMDocument* doc) {\r
- m_document = doc;\r
- }\r
- \r
- const Override* findOverride(const char* vhost, const HTTPRequest& request) const;\r
-\r
- private: \r
- map<string,Override*> m_extras;\r
- DOMDocument* m_document;\r
- };\r
-\r
-#if defined (_MSC_VER)\r
- #pragma warning( push )\r
- #pragma warning( disable : 4250 )\r
-#endif\r
-\r
- class XMLRequestMapper : public RequestMapper, public ReloadableXMLFile\r
- {\r
- public:\r
- XMLRequestMapper(const DOMElement* e) : ReloadableXMLFile(e,Category::getInstance(SHIBSP_LOGCAT".RequestMapper")), m_impl(NULL) {\r
- load();\r
- }\r
-\r
- ~XMLRequestMapper() {\r
- delete m_impl;\r
- }\r
-\r
- Settings getSettings(const HTTPRequest& request) const;\r
-\r
- protected:\r
- pair<bool,DOMElement*> load();\r
-\r
- private:\r
- XMLRequestMapperImpl* m_impl;\r
- };\r
-\r
-#if defined (_MSC_VER)\r
- #pragma warning( pop )\r
-#endif\r
-\r
- RequestMapper* SHIBSP_DLLLOCAL XMLRequestMapperFactory(const DOMElement* const & e)\r
- {\r
- return new XMLRequestMapper(e);\r
- }\r
-\r
- static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);\r
- static const XMLCh AccessControlProvider[] = UNICODE_LITERAL_21(A,c,c,e,s,s,C,o,n,t,r,o,l,P,r,o,v,i,d,e,r);\r
- static const XMLCh Host[] = UNICODE_LITERAL_4(H,o,s,t);\r
- static const XMLCh HostRegex[] = UNICODE_LITERAL_9(H,o,s,t,R,e,g,e,x);\r
- static const XMLCh htaccess[] = UNICODE_LITERAL_8(h,t,a,c,c,e,s,s);\r
- static const XMLCh ignoreCase[] = UNICODE_LITERAL_10(i,g,n,o,r,e,C,a,s,e);\r
- static const XMLCh ignoreOption[] = UNICODE_LITERAL_1(i);\r
- static const XMLCh Path[] = UNICODE_LITERAL_4(P,a,t,h);\r
- static const XMLCh PathRegex[] = UNICODE_LITERAL_9(P,a,t,h,R,e,g,e,x);\r
- static const XMLCh Query[] = UNICODE_LITERAL_5(Q,u,e,r,y);\r
- static const XMLCh name[] = UNICODE_LITERAL_4(n,a,m,e);\r
- static const XMLCh regex[] = UNICODE_LITERAL_5(r,e,g,e,x);\r
- static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);\r
-}\r
-\r
-void SHIBSP_API shibsp::registerRequestMappers()\r
-{\r
- SPConfig& conf=SPConfig::getConfig();\r
- conf.RequestMapperManager.registerFactory(XML_REQUEST_MAPPER, XMLRequestMapperFactory);\r
- conf.RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, XMLRequestMapperFactory);\r
-}\r
-\r
-void Override::loadACL(const DOMElement* e, Category& log)\r
-{\r
- try {\r
- const DOMElement* acl=XMLHelper::getFirstChildElement(e,htaccess);\r
- if (acl) {\r
- log.info("building Apache htaccess AccessControl provider...");\r
- m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL,acl);\r
- }\r
- else {\r
- acl=XMLHelper::getFirstChildElement(e,_AccessControl);\r
- if (acl) {\r
- log.info("building XML-based AccessControl provider...");\r
- m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL,acl);\r
- }\r
- else {\r
- acl=XMLHelper::getFirstChildElement(e,AccessControlProvider);\r
- if (acl) {\r
- auto_ptr_char type(acl->getAttributeNS(NULL,_type));\r
- log.info("building AccessControl provider of type %s...",type.get());\r
- m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(type.get(),acl);\r
- }\r
- }\r
- }\r
- }\r
- catch (exception& ex) {\r
- log.crit("exception building AccessControl provider: %s", ex.what());\r
- m_acl = new AccessControlDummy();\r
- }\r
-}\r
-\r
-Override::Override(const DOMElement* e, Category& log, const Override* base) : m_acl(NULL)\r
-{\r
- try {\r
- // Load the property set.\r
- load(e,log,this);\r
- setParent(base);\r
- \r
- // Load any AccessControl provider.\r
- loadACL(e,log);\r
- \r
- // Handle nested Paths.\r
- DOMElement* path = XMLHelper::getFirstChildElement(e,Path);\r
- for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Path)) {\r
- const XMLCh* n=path->getAttributeNS(NULL,name);\r
- \r
- // Skip any leading slashes.\r
- while (n && *n==chForwardSlash)\r
- n++;\r
- \r
- // Check for empty name.\r
- if (!n || !*n) {\r
- log.warn("skipping Path element (%d) with empty name attribute", i);\r
- continue;\r
- }\r
-\r
- // Check for an embedded slash.\r
- int slash=XMLString::indexOf(n,chForwardSlash);\r
- if (slash>0) {\r
- // Copy the first path segment.\r
- XMLCh* namebuf=new XMLCh[slash + 1];\r
- for (int pos=0; pos < slash; pos++)\r
- namebuf[pos]=n[pos];\r
- namebuf[slash]=chNull;\r
- \r
- // Move past the slash in the original pathname.\r
- n=n+slash+1;\r
- \r
- // Skip any leading slashes again.\r
- while (*n==chForwardSlash)\r
- n++;\r
- \r
- if (*n) {\r
- // Create a placeholder Path element for the first path segment and replant under it.\r
- DOMElement* newpath=path->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS,Path);\r
- newpath->setAttributeNS(NULL,name,namebuf);\r
- path->setAttributeNS(NULL,name,n);\r
- path->getParentNode()->replaceChild(newpath,path);\r
- newpath->appendChild(path);\r
- \r
- // Repoint our locals at the new parent.\r
- path=newpath;\r
- n=path->getAttributeNS(NULL,name);\r
- }\r
- else {\r
- // All we had was a pathname with trailing slash(es), so just reset it without them.\r
- path->setAttributeNS(NULL,name,namebuf);\r
- n=path->getAttributeNS(NULL,name);\r
- }\r
- delete[] namebuf;\r
- }\r
- \r
- Override* o=new Override(path,log,this);\r
- pair<bool,const char*> name=o->getString("name");\r
- char* dup=strdup(name.second);\r
- for (char* pch=dup; *pch; pch++)\r
- *pch=tolower(*pch);\r
- if (m_map.count(dup)) {\r
- log.warn("skipping duplicate Path element (%s)",dup);\r
- free(dup);\r
- delete o;\r
- continue;\r
- }\r
- m_map[dup]=o;\r
- log.debug("added Path mapping (%s)", dup);\r
- free(dup);\r
- }\r
-\r
- if (!XMLString::equals(e->getLocalName(), PathRegex)) {\r
- // Handle nested PathRegexs.\r
- path = XMLHelper::getFirstChildElement(e,PathRegex);\r
- for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,PathRegex)) {\r
- const XMLCh* n=path->getAttributeNS(NULL,regex);\r
- if (!n || !*n) {\r
- log.warn("skipping PathRegex element (%d) with empty regex attribute",i);\r
- continue;\r
- }\r
-\r
- auto_ptr<Override> o(new Override(path,log,this));\r
-\r
- const XMLCh* flag=path->getAttributeNS(NULL,ignoreCase);\r
- try {\r
- auto_ptr<RegularExpression> re(\r
- new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)\r
- );\r
- m_regexps.push_back(make_pair(re.release(), o.release()));\r
- }\r
- catch (XMLException& ex) {\r
- auto_ptr_char tmp(ex.getMessage());\r
- log.error("caught exception while parsing PathRegex regular expression (%d): %s", i, tmp.get());\r
- throw ConfigurationException("Invalid regular expression in PathRegex element.");\r
- }\r
-\r
- if (log.isDebugEnabled())\r
- log.debug("added <PathRegex> mapping (%s)", m_regexps.back().second->getString("regex").second);\r
- }\r
- }\r
-\r
- // Handle nested Querys.\r
- path = XMLHelper::getFirstChildElement(e,Query);\r
- for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Query)) {\r
- const XMLCh* n=path->getAttributeNS(NULL,name);\r
- if (!n || !*n) {\r
- log.warn("skipping Query element (%d) with empty name attribute",i);\r
- continue;\r
- }\r
- auto_ptr_char ntemp(n);\r
- const XMLCh* v=path->getAttributeNS(NULL,regex);\r
-\r
- auto_ptr<Override> o(new Override(path,log,this));\r
- try {\r
- RegularExpression* re = NULL;\r
- if (v && *v)\r
- re = new RegularExpression(v);\r
- m_queries.push_back(make_pair(make_pair(ntemp.get(),re), o.release()));\r
- }\r
- catch (XMLException& ex) {\r
- auto_ptr_char tmp(ex.getMessage());\r
- log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get());\r
- throw ConfigurationException("Invalid regular expression in Query element.");\r
- }\r
- \r
- log.debug("added <Query> mapping (%s)", ntemp.get());\r
- }\r
- }\r
- catch (exception&) {\r
- delete m_acl;\r
- for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());\r
- for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {\r
- delete i->first;\r
- delete i->second;\r
- }\r
- for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {\r
- delete j->first.second;\r
- delete j->second;\r
- }\r
- throw;\r
- }\r
-}\r
-\r
-Override::~Override()\r
-{\r
- delete m_acl;\r
- for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());\r
- for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {\r
- delete i->first;\r
- delete i->second;\r
- }\r
- for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {\r
- delete j->first.second;\r
- delete j->second;\r
- }\r
-}\r
-\r
-const Override* Override::locate(const HTTPRequest& request) const\r
-{\r
- // This function is confusing because it's *not* recursive.\r
- // The whole path is tokenized and mapped in a loop, so the\r
- // path parameter starts with the entire request path and\r
- // we can skip the leading slash as irrelevant.\r
- const char* path = request.getRequestURI();\r
- if (*path == '/')\r
- path++;\r
-\r
- // Now we copy the path, chop the query string, and lower case it.\r
- char* dup=strdup(path);\r
- char* sep=strchr(dup,'?');\r
- if (sep)\r
- *sep=0;\r
- for (char* pch=dup; *pch; pch++)\r
- *pch=tolower(*pch);\r
-\r
- // Default is for the current object to provide settings.\r
- const Override* o=this;\r
-\r
- // Tokenize the path by segment and try and map each segment.\r
-#ifdef HAVE_STRTOK_R\r
- char* pos=NULL;\r
- const char* token=strtok_r(dup,"/",&pos);\r
-#else\r
- const char* token=strtok(dup,"/");\r
-#endif\r
- while (token) {\r
- map<string,Override*>::const_iterator i=o->m_map.find(token);\r
- if (i==o->m_map.end())\r
- break; // Once there's no match, we've consumed as much of the path as possible here.\r
- // We found a match, so reset the settings pointer.\r
- o=i->second;\r
- \r
- // We descended a step down the path, so we need to advance the original\r
- // parameter for the regex step later.\r
- path += strlen(token);\r
- if (*path == '/')\r
- path++;\r
-\r
- // Get the next segment, if any.\r
-#ifdef HAVE_STRTOK_R\r
- token=strtok_r(NULL,"/",&pos);\r
-#else\r
- token=strtok(NULL,"/");\r
-#endif\r
- }\r
-\r
- free(dup);\r
-\r
- // If there's anything left, we try for a regex match on the rest of the path minus the query string.\r
- if (*path) {\r
- string path2(path);\r
- path2 = path2.substr(0,path2.find('?'));\r
-\r
- for (vector< pair<RegularExpression*,Override*> >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {\r
- if (re->first->matches(path2.c_str())) {\r
- o = re->second;\r
- break;\r
- }\r
- }\r
- }\r
-\r
- // Finally, check for query string matches. This is another "unrolled" recursive descent in a loop.\r
- bool descended;\r
- do {\r
- descended = false;\r
- for (vector< pair< pair<string,RegularExpression*>,Override*> >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {\r
- vector<const char*> vals;\r
- if (request.getParameters(q->first.first.c_str(), vals)) {\r
- if (q->first.second) {\r
- // We have to match one of the values.\r
- for (vector<const char*>::const_iterator v = vals.begin(); v != vals.end(); ++v) {\r
- if (q->first.second->matches(*v)) {\r
- o = q->second;\r
- descended = true;\r
- break;\r
- }\r
- }\r
- }\r
- else {\r
- // The simple presence of the parameter is sufficient to match.\r
- o = q->second;\r
- descended = true;\r
- }\r
- }\r
- }\r
- } while (descended);\r
-\r
- return o;\r
-}\r
-\r
-XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : m_document(NULL)\r
-{\r
-#ifdef _DEBUG\r
- xmltooling::NDC ndc("XMLRequestMapperImpl");\r
-#endif\r
-\r
- // Load the property set.\r
- load(e,log,this);\r
- \r
- // Load any AccessControl provider.\r
- loadACL(e,log);\r
-\r
- // Loop over the HostRegex elements.\r
- const DOMElement* host = XMLHelper::getFirstChildElement(e,HostRegex);\r
- for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,HostRegex)) {\r
- const XMLCh* n=host->getAttributeNS(NULL,regex);\r
- if (!n || !*n) {\r
- log.warn("Skipping HostRegex element (%d) with empty regex attribute",i);\r
- continue;\r
- }\r
-\r
- auto_ptr<Override> o(new Override(host,log,this));\r
-\r
- const XMLCh* flag=host->getAttributeNS(NULL,ignoreCase);\r
- try {\r
- auto_ptr<RegularExpression> re(\r
- new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)\r
- );\r
- m_regexps.push_back(make_pair(re.release(), o.release()));\r
- }\r
- catch (XMLException& ex) {\r
- auto_ptr_char tmp(ex.getMessage());\r
- log.error("caught exception while parsing HostRegex regular expression (%d): %s", i, tmp.get());\r
- }\r
-\r
- log.debug("Added <HostRegex> mapping for %s", m_regexps.back().second->getString("regex").second);\r
- }\r
-\r
- // Loop over the Host elements.\r
- host = XMLHelper::getFirstChildElement(e,Host);\r
- for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,Host)) {\r
- const XMLCh* n=host->getAttributeNS(NULL,name);\r
- if (!n || !*n) {\r
- log.warn("Skipping Host element (%d) with empty name attribute",i);\r
- continue;\r
- }\r
- \r
- Override* o=new Override(host,log,this);\r
- pair<bool,const char*> name=o->getString("name");\r
- pair<bool,const char*> scheme=o->getString("scheme");\r
- pair<bool,const char*> port=o->getString("port");\r
- \r
- char* dup=strdup(name.second);\r
- for (char* pch=dup; *pch; pch++)\r
- *pch=tolower(*pch);\r
- auto_ptr<char> dupwrap(dup);\r
-\r
- if (!scheme.first && port.first) {\r
- // No scheme, but a port, so assume http.\r
- scheme = pair<bool,const char*>(true,"http");\r
- }\r
- else if (scheme.first && !port.first) {\r
- // Scheme, no port, so default it.\r
- // XXX Use getservbyname instead?\r
- port.first = true;\r
- if (!strcmp(scheme.second,"http"))\r
- port.second = "80";\r
- else if (!strcmp(scheme.second,"https"))\r
- port.second = "443";\r
- else if (!strcmp(scheme.second,"ftp"))\r
- port.second = "21";\r
- else if (!strcmp(scheme.second,"ldap"))\r
- port.second = "389";\r
- else if (!strcmp(scheme.second,"ldaps"))\r
- port.second = "636";\r
- }\r
-\r
- if (scheme.first) {\r
- string url(scheme.second);\r
- url=url + "://" + dup;\r
- \r
- // Is this the default port?\r
- if ((!strcmp(scheme.second,"http") && !strcmp(port.second,"80")) ||\r
- (!strcmp(scheme.second,"https") && !strcmp(port.second,"443")) ||\r
- (!strcmp(scheme.second,"ftp") && !strcmp(port.second,"21")) ||\r
- (!strcmp(scheme.second,"ldap") && !strcmp(port.second,"389")) ||\r
- (!strcmp(scheme.second,"ldaps") && !strcmp(port.second,"636"))) {\r
- // First store a port-less version.\r
- if (m_map.count(url) || m_extras.count(url)) {\r
- log.warn("Skipping duplicate Host element (%s)",url.c_str());\r
- delete o;\r
- continue;\r
- }\r
- m_map[url]=o;\r
- log.debug("Added <Host> mapping for %s",url.c_str());\r
- \r
- // Now append the port. We use the extras vector, to avoid double freeing the object later.\r
- url=url + ':' + port.second;\r
- m_extras[url]=o;\r
- log.debug("Added <Host> mapping for %s",url.c_str());\r
- }\r
- else {\r
- url=url + ':' + port.second;\r
- if (m_map.count(url) || m_extras.count(url)) {\r
- log.warn("Skipping duplicate Host element (%s)",url.c_str());\r
- delete o;\r
- continue;\r
- }\r
- m_map[url]=o;\r
- log.debug("Added <Host> mapping for %s",url.c_str());\r
- }\r
- }\r
- else {\r
- // No scheme or port, so we enter dual hosts on http:80 and https:443\r
- string url("http://");\r
- url = url + dup;\r
- if (m_map.count(url) || m_extras.count(url)) {\r
- log.warn("Skipping duplicate Host element (%s)",url.c_str());\r
- delete o;\r
- continue;\r
- }\r
- m_map[url]=o;\r
- log.debug("Added <Host> mapping for %s",url.c_str());\r
- \r
- url = url + ":80";\r
- if (m_map.count(url) || m_extras.count(url)) {\r
- log.warn("Skipping duplicate Host element (%s)",url.c_str());\r
- continue;\r
- }\r
- m_extras[url]=o;\r
- log.debug("Added <Host> mapping for %s",url.c_str());\r
- \r
- url = "https://";\r
- url = url + dup;\r
- if (m_map.count(url) || m_extras.count(url)) {\r
- log.warn("Skipping duplicate Host element (%s)",url.c_str());\r
- continue;\r
- }\r
- m_extras[url]=o;\r
- log.debug("Added <Host> mapping for %s",url.c_str());\r
- \r
- url = url + ":443";\r
- if (m_map.count(url) || m_extras.count(url)) {\r
- log.warn("Skipping duplicate Host element (%s)",url.c_str());\r
- continue;\r
- }\r
- m_extras[url]=o;\r
- log.debug("Added <Host> mapping for %s",url.c_str());\r
- }\r
- }\r
-}\r
-\r
-const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTPRequest& request) const\r
-{\r
- const Override* o=NULL;\r
- map<string,Override*>::const_iterator i=m_map.find(vhost);\r
- if (i!=m_map.end())\r
- o=i->second;\r
- else {\r
- i=m_extras.find(vhost);\r
- if (i!=m_extras.end())\r
- o=i->second;\r
- else {\r
- for (vector< pair<RegularExpression*,Override*> >::const_iterator re = m_regexps.begin(); !o && re != m_regexps.end(); ++re) {\r
- if (re->first->matches(vhost))\r
- o=re->second;\r
- }\r
- }\r
- }\r
- \r
- return o ? o->locate(request) : this;\r
-}\r
-\r
-pair<bool,DOMElement*> XMLRequestMapper::load()\r
-{\r
- // Load from source using base class.\r
- pair<bool,DOMElement*> raw = ReloadableXMLFile::load();\r
- \r
- // If we own it, wrap it.\r
- XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);\r
-\r
- XMLRequestMapperImpl* impl = new XMLRequestMapperImpl(raw.second,m_log);\r
- \r
- // If we held the document, transfer it to the impl. If we didn't, it's a no-op.\r
- impl->setDocument(docjanitor.release());\r
-\r
- delete m_impl;\r
- m_impl = impl;\r
-\r
- return make_pair(false,(DOMElement*)NULL);\r
-}\r
-\r
-RequestMapper::Settings XMLRequestMapper::getSettings(const HTTPRequest& request) const\r
-{\r
- ostringstream vhost;\r
- vhost << request.getScheme() << "://" << request.getHostname() << ':' << request.getPort();\r
-\r
- const Override* o=m_impl->findOverride(vhost.str().c_str(), request);\r
-\r
- if (m_log.isDebugEnabled()) {\r
-#ifdef _DEBUG\r
- xmltooling::NDC ndc("getSettings");\r
-#endif\r
- pair<bool,const char*> ret=o->getString("applicationId");\r
- m_log.debug("mapped %s%s to %s", vhost.str().c_str(), request.getRequestURI() ? request.getRequestURI() : "", ret.second);\r
- }\r
-\r
- return Settings(o,o->getAC());\r
-}\r
+/*
+ * 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.
+ */
+
+/** XMLRequestMapper.cpp
+ *
+ * XML-based RequestMapper implementation
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "AccessControl.h"
+#include "RequestMapper.h"
+#include "SPRequest.h"
+#include "util/DOMPropertySet.h"
+#include "util/SPConstants.h"
+
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/ReloadableXMLFile.h>
+#include <xmltooling/util/XMLHelper.h>
+#include <xercesc/util/XMLUniDefs.hpp>
+#include <xercesc/util/regx/RegularExpression.hpp>
+
+using namespace shibsp;
+using namespace xmltooling;
+using namespace std;
+
+namespace shibsp {
+
+ // Blocks access when an ACL plugin fails to load.
+ class AccessControlDummy : public AccessControl
+ {
+ public:
+ Lockable* lock() {
+ return this;
+ }
+
+ void unlock() {}
+
+ aclresult_t authorized(const SPRequest& request, const Session* session) const {
+ return shib_acl_false;
+ }
+ };
+
+ class Override : public DOMPropertySet, public DOMNodeFilter
+ {
+ public:
+ Override() : m_acl(NULL) {}
+ Override(const DOMElement* e, Category& log, const Override* base=NULL);
+ ~Override();
+
+ // Provides filter to exclude special config elements.
+ short acceptNode(const DOMNode* node) const {
+ return FILTER_REJECT;
+ }
+
+ const Override* locate(const HTTPRequest& request) const;
+ AccessControl* getAC() const { return (m_acl ? m_acl : (getParent() ? dynamic_cast<const Override*>(getParent())->getAC() : NULL)); }
+
+ protected:
+ void loadACL(const DOMElement* e, Category& log);
+
+ map<string,Override*> m_map;
+ vector< pair<RegularExpression*,Override*> > m_regexps;
+ vector< pair< pair<string,RegularExpression*>,Override*> > m_queries;
+
+ private:
+ AccessControl* m_acl;
+ };
+
+ class XMLRequestMapperImpl : public Override
+ {
+ public:
+ XMLRequestMapperImpl(const DOMElement* e, Category& log);
+
+ ~XMLRequestMapperImpl() {
+ if (m_document)
+ m_document->release();
+ }
+
+ void setDocument(DOMDocument* doc) {
+ m_document = doc;
+ }
+
+ const Override* findOverride(const char* vhost, const HTTPRequest& request) const;
+
+ private:
+ map<string,Override*> m_extras;
+ DOMDocument* m_document;
+ };
+
+#if defined (_MSC_VER)
+ #pragma warning( push )
+ #pragma warning( disable : 4250 )
+#endif
+
+ class XMLRequestMapper : public RequestMapper, public ReloadableXMLFile
+ {
+ public:
+ XMLRequestMapper(const DOMElement* e) : ReloadableXMLFile(e,Category::getInstance(SHIBSP_LOGCAT".RequestMapper")), m_impl(NULL) {
+ load();
+ }
+
+ ~XMLRequestMapper() {
+ delete m_impl;
+ }
+
+ Settings getSettings(const HTTPRequest& request) const;
+
+ protected:
+ pair<bool,DOMElement*> load();
+
+ private:
+ XMLRequestMapperImpl* m_impl;
+ };
+
+#if defined (_MSC_VER)
+ #pragma warning( pop )
+#endif
+
+ RequestMapper* SHIBSP_DLLLOCAL XMLRequestMapperFactory(const DOMElement* const & e)
+ {
+ return new XMLRequestMapper(e);
+ }
+
+ static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);
+ static const XMLCh AccessControlProvider[] = UNICODE_LITERAL_21(A,c,c,e,s,s,C,o,n,t,r,o,l,P,r,o,v,i,d,e,r);
+ static const XMLCh Host[] = UNICODE_LITERAL_4(H,o,s,t);
+ static const XMLCh HostRegex[] = UNICODE_LITERAL_9(H,o,s,t,R,e,g,e,x);
+ static const XMLCh htaccess[] = UNICODE_LITERAL_8(h,t,a,c,c,e,s,s);
+ static const XMLCh ignoreCase[] = UNICODE_LITERAL_10(i,g,n,o,r,e,C,a,s,e);
+ static const XMLCh ignoreOption[] = UNICODE_LITERAL_1(i);
+ static const XMLCh Path[] = UNICODE_LITERAL_4(P,a,t,h);
+ static const XMLCh PathRegex[] = UNICODE_LITERAL_9(P,a,t,h,R,e,g,e,x);
+ static const XMLCh Query[] = UNICODE_LITERAL_5(Q,u,e,r,y);
+ static const XMLCh name[] = UNICODE_LITERAL_4(n,a,m,e);
+ static const XMLCh regex[] = UNICODE_LITERAL_5(r,e,g,e,x);
+ static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
+}
+
+void SHIBSP_API shibsp::registerRequestMappers()
+{
+ SPConfig& conf=SPConfig::getConfig();
+ conf.RequestMapperManager.registerFactory(XML_REQUEST_MAPPER, XMLRequestMapperFactory);
+ conf.RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, XMLRequestMapperFactory);
+}
+
+void Override::loadACL(const DOMElement* e, Category& log)
+{
+ try {
+ const DOMElement* acl=XMLHelper::getFirstChildElement(e,htaccess);
+ if (acl) {
+ log.info("building Apache htaccess AccessControl provider...");
+ m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL,acl);
+ }
+ else {
+ acl=XMLHelper::getFirstChildElement(e,_AccessControl);
+ if (acl) {
+ log.info("building XML-based AccessControl provider...");
+ m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL,acl);
+ }
+ else {
+ acl=XMLHelper::getFirstChildElement(e,AccessControlProvider);
+ if (acl) {
+ auto_ptr_char type(acl->getAttributeNS(NULL,_type));
+ log.info("building AccessControl provider of type %s...",type.get());
+ m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(type.get(),acl);
+ }
+ }
+ }
+ }
+ catch (exception& ex) {
+ log.crit("exception building AccessControl provider: %s", ex.what());
+ m_acl = new AccessControlDummy();
+ }
+}
+
+Override::Override(const DOMElement* e, Category& log, const Override* base) : m_acl(NULL)
+{
+ try {
+ // Load the property set.
+ load(e,log,this);
+ setParent(base);
+
+ // Load any AccessControl provider.
+ loadACL(e,log);
+
+ // Handle nested Paths.
+ DOMElement* path = XMLHelper::getFirstChildElement(e,Path);
+ for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Path)) {
+ const XMLCh* n=path->getAttributeNS(NULL,name);
+
+ // Skip any leading slashes.
+ while (n && *n==chForwardSlash)
+ n++;
+
+ // Check for empty name.
+ if (!n || !*n) {
+ log.warn("skipping Path element (%d) with empty name attribute", i);
+ continue;
+ }
+
+ // Check for an embedded slash.
+ int slash=XMLString::indexOf(n,chForwardSlash);
+ if (slash>0) {
+ // Copy the first path segment.
+ XMLCh* namebuf=new XMLCh[slash + 1];
+ for (int pos=0; pos < slash; pos++)
+ namebuf[pos]=n[pos];
+ namebuf[slash]=chNull;
+
+ // Move past the slash in the original pathname.
+ n=n+slash+1;
+
+ // Skip any leading slashes again.
+ while (*n==chForwardSlash)
+ n++;
+
+ if (*n) {
+ // Create a placeholder Path element for the first path segment and replant under it.
+ DOMElement* newpath=path->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS,Path);
+ newpath->setAttributeNS(NULL,name,namebuf);
+ path->setAttributeNS(NULL,name,n);
+ path->getParentNode()->replaceChild(newpath,path);
+ newpath->appendChild(path);
+
+ // Repoint our locals at the new parent.
+ path=newpath;
+ n=path->getAttributeNS(NULL,name);
+ }
+ else {
+ // All we had was a pathname with trailing slash(es), so just reset it without them.
+ path->setAttributeNS(NULL,name,namebuf);
+ n=path->getAttributeNS(NULL,name);
+ }
+ delete[] namebuf;
+ }
+
+ Override* o=new Override(path,log,this);
+ pair<bool,const char*> name=o->getString("name");
+ char* dup=strdup(name.second);
+ for (char* pch=dup; *pch; pch++)
+ *pch=tolower(*pch);
+ if (m_map.count(dup)) {
+ log.warn("skipping duplicate Path element (%s)",dup);
+ free(dup);
+ delete o;
+ continue;
+ }
+ m_map[dup]=o;
+ log.debug("added Path mapping (%s)", dup);
+ free(dup);
+ }
+
+ if (!XMLString::equals(e->getLocalName(), PathRegex)) {
+ // Handle nested PathRegexs.
+ path = XMLHelper::getFirstChildElement(e,PathRegex);
+ for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,PathRegex)) {
+ const XMLCh* n=path->getAttributeNS(NULL,regex);
+ if (!n || !*n) {
+ log.warn("skipping PathRegex element (%d) with empty regex attribute",i);
+ continue;
+ }
+
+ auto_ptr<Override> o(new Override(path,log,this));
+
+ const XMLCh* flag=path->getAttributeNS(NULL,ignoreCase);
+ try {
+ auto_ptr<RegularExpression> re(
+ new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)
+ );
+ m_regexps.push_back(make_pair(re.release(), o.release()));
+ }
+ catch (XMLException& ex) {
+ auto_ptr_char tmp(ex.getMessage());
+ log.error("caught exception while parsing PathRegex regular expression (%d): %s", i, tmp.get());
+ throw ConfigurationException("Invalid regular expression in PathRegex element.");
+ }
+
+ if (log.isDebugEnabled())
+ log.debug("added <PathRegex> mapping (%s)", m_regexps.back().second->getString("regex").second);
+ }
+ }
+
+ // Handle nested Querys.
+ path = XMLHelper::getFirstChildElement(e,Query);
+ for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Query)) {
+ const XMLCh* n=path->getAttributeNS(NULL,name);
+ if (!n || !*n) {
+ log.warn("skipping Query element (%d) with empty name attribute",i);
+ continue;
+ }
+ auto_ptr_char ntemp(n);
+ const XMLCh* v=path->getAttributeNS(NULL,regex);
+
+ auto_ptr<Override> o(new Override(path,log,this));
+ try {
+ RegularExpression* re = NULL;
+ if (v && *v)
+ re = new RegularExpression(v);
+ m_queries.push_back(make_pair(make_pair(string(ntemp.get()),re), o.release()));
+ }
+ catch (XMLException& ex) {
+ auto_ptr_char tmp(ex.getMessage());
+ log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get());
+ throw ConfigurationException("Invalid regular expression in Query element.");
+ }
+
+ log.debug("added <Query> mapping (%s)", ntemp.get());
+ }
+ }
+ catch (exception&) {
+ delete m_acl;
+ for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());
+ for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {
+ delete i->first;
+ delete i->second;
+ }
+ for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {
+ delete j->first.second;
+ delete j->second;
+ }
+ throw;
+ }
+}
+
+Override::~Override()
+{
+ delete m_acl;
+ for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());
+ for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {
+ delete i->first;
+ delete i->second;
+ }
+ for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {
+ delete j->first.second;
+ delete j->second;
+ }
+}
+
+const Override* Override::locate(const HTTPRequest& request) const
+{
+ // This function is confusing because it's *not* recursive.
+ // The whole path is tokenized and mapped in a loop, so the
+ // path parameter starts with the entire request path and
+ // we can skip the leading slash as irrelevant.
+ const char* path = request.getRequestURI();
+ if (*path == '/')
+ path++;
+
+ // Now we copy the path, chop the query string, and lower case it.
+ char* dup=strdup(path);
+ char* sep=strchr(dup,'?');
+ if (sep)
+ *sep=0;
+ for (char* pch=dup; *pch; pch++)
+ *pch=tolower(*pch);
+
+ // Default is for the current object to provide settings.
+ const Override* o=this;
+
+ // Tokenize the path by segment and try and map each segment.
+#ifdef HAVE_STRTOK_R
+ char* pos=NULL;
+ const char* token=strtok_r(dup,"/",&pos);
+#else
+ const char* token=strtok(dup,"/");
+#endif
+ while (token) {
+ map<string,Override*>::const_iterator i=o->m_map.find(token);
+ if (i==o->m_map.end())
+ break; // Once there's no match, we've consumed as much of the path as possible here.
+ // We found a match, so reset the settings pointer.
+ o=i->second;
+
+ // We descended a step down the path, so we need to advance the original
+ // parameter for the regex step later.
+ path += strlen(token);
+ if (*path == '/')
+ path++;
+
+ // Get the next segment, if any.
+#ifdef HAVE_STRTOK_R
+ token=strtok_r(NULL,"/",&pos);
+#else
+ token=strtok(NULL,"/");
+#endif
+ }
+
+ free(dup);
+
+ // If there's anything left, we try for a regex match on the rest of the path minus the query string.
+ if (*path) {
+ string path2(path);
+ path2 = path2.substr(0,path2.find('?'));
+
+ for (vector< pair<RegularExpression*,Override*> >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {
+ if (re->first->matches(path2.c_str())) {
+ o = re->second;
+ break;
+ }
+ }
+ }
+
+ // Finally, check for query string matches. This is another "unrolled" recursive descent in a loop.
+ bool descended;
+ do {
+ descended = false;
+ for (vector< pair< pair<string,RegularExpression*>,Override*> >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {
+ vector<const char*> vals;
+ if (request.getParameters(q->first.first.c_str(), vals)) {
+ if (q->first.second) {
+ // We have to match one of the values.
+ for (vector<const char*>::const_iterator v = vals.begin(); v != vals.end(); ++v) {
+ if (q->first.second->matches(*v)) {
+ o = q->second;
+ descended = true;
+ break;
+ }
+ }
+ }
+ else {
+ // The simple presence of the parameter is sufficient to match.
+ o = q->second;
+ descended = true;
+ }
+ }
+ }
+ } while (descended);
+
+ return o;
+}
+
+XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : m_document(NULL)
+{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("XMLRequestMapperImpl");
+#endif
+
+ // Load the property set.
+ load(e,log,this);
+
+ // Load any AccessControl provider.
+ loadACL(e,log);
+
+ // Loop over the HostRegex elements.
+ const DOMElement* host = XMLHelper::getFirstChildElement(e,HostRegex);
+ for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,HostRegex)) {
+ const XMLCh* n=host->getAttributeNS(NULL,regex);
+ if (!n || !*n) {
+ log.warn("Skipping HostRegex element (%d) with empty regex attribute",i);
+ continue;
+ }
+
+ auto_ptr<Override> o(new Override(host,log,this));
+
+ const XMLCh* flag=host->getAttributeNS(NULL,ignoreCase);
+ try {
+ auto_ptr<RegularExpression> re(
+ new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)
+ );
+ m_regexps.push_back(make_pair(re.release(), o.release()));
+ }
+ catch (XMLException& ex) {
+ auto_ptr_char tmp(ex.getMessage());
+ log.error("caught exception while parsing HostRegex regular expression (%d): %s", i, tmp.get());
+ }
+
+ log.debug("Added <HostRegex> mapping for %s", m_regexps.back().second->getString("regex").second);
+ }
+
+ // Loop over the Host elements.
+ host = XMLHelper::getFirstChildElement(e,Host);
+ for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,Host)) {
+ const XMLCh* n=host->getAttributeNS(NULL,name);
+ if (!n || !*n) {
+ log.warn("Skipping Host element (%d) with empty name attribute",i);
+ continue;
+ }
+
+ Override* o=new Override(host,log,this);
+ pair<bool,const char*> name=o->getString("name");
+ pair<bool,const char*> scheme=o->getString("scheme");
+ pair<bool,const char*> port=o->getString("port");
+
+ char* dup=strdup(name.second);
+ for (char* pch=dup; *pch; pch++)
+ *pch=tolower(*pch);
+ auto_ptr<char> dupwrap(dup);
+
+ if (!scheme.first && port.first) {
+ // No scheme, but a port, so assume http.
+ scheme = pair<bool,const char*>(true,"http");
+ }
+ else if (scheme.first && !port.first) {
+ // Scheme, no port, so default it.
+ // XXX Use getservbyname instead?
+ port.first = true;
+ if (!strcmp(scheme.second,"http"))
+ port.second = "80";
+ else if (!strcmp(scheme.second,"https"))
+ port.second = "443";
+ else if (!strcmp(scheme.second,"ftp"))
+ port.second = "21";
+ else if (!strcmp(scheme.second,"ldap"))
+ port.second = "389";
+ else if (!strcmp(scheme.second,"ldaps"))
+ port.second = "636";
+ }
+
+ if (scheme.first) {
+ string url(scheme.second);
+ url=url + "://" + dup;
+
+ // Is this the default port?
+ if ((!strcmp(scheme.second,"http") && !strcmp(port.second,"80")) ||
+ (!strcmp(scheme.second,"https") && !strcmp(port.second,"443")) ||
+ (!strcmp(scheme.second,"ftp") && !strcmp(port.second,"21")) ||
+ (!strcmp(scheme.second,"ldap") && !strcmp(port.second,"389")) ||
+ (!strcmp(scheme.second,"ldaps") && !strcmp(port.second,"636"))) {
+ // First store a port-less version.
+ if (m_map.count(url) || m_extras.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ delete o;
+ continue;
+ }
+ m_map[url]=o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+
+ // Now append the port. We use the extras vector, to avoid double freeing the object later.
+ url=url + ':' + port.second;
+ m_extras[url]=o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+ }
+ else {
+ url=url + ':' + port.second;
+ if (m_map.count(url) || m_extras.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ delete o;
+ continue;
+ }
+ m_map[url]=o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+ }
+ }
+ else {
+ // No scheme or port, so we enter dual hosts on http:80 and https:443
+ string url("http://");
+ url = url + dup;
+ if (m_map.count(url) || m_extras.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ delete o;
+ continue;
+ }
+ m_map[url]=o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+
+ url = url + ":80";
+ if (m_map.count(url) || m_extras.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ continue;
+ }
+ m_extras[url]=o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+
+ url = "https://";
+ url = url + dup;
+ if (m_map.count(url) || m_extras.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ continue;
+ }
+ m_extras[url]=o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+
+ url = url + ":443";
+ if (m_map.count(url) || m_extras.count(url)) {
+ log.warn("Skipping duplicate Host element (%s)",url.c_str());
+ continue;
+ }
+ m_extras[url]=o;
+ log.debug("Added <Host> mapping for %s",url.c_str());
+ }
+ }
+}
+
+const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTPRequest& request) const
+{
+ const Override* o=NULL;
+ map<string,Override*>::const_iterator i=m_map.find(vhost);
+ if (i!=m_map.end())
+ o=i->second;
+ else {
+ i=m_extras.find(vhost);
+ if (i!=m_extras.end())
+ o=i->second;
+ else {
+ for (vector< pair<RegularExpression*,Override*> >::const_iterator re = m_regexps.begin(); !o && re != m_regexps.end(); ++re) {
+ if (re->first->matches(vhost))
+ o=re->second;
+ }
+ }
+ }
+
+ return o ? o->locate(request) : this;
+}
+
+pair<bool,DOMElement*> XMLRequestMapper::load()
+{
+ // Load from source using base class.
+ pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
+
+ // If we own it, wrap it.
+ XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);
+
+ XMLRequestMapperImpl* impl = new XMLRequestMapperImpl(raw.second,m_log);
+
+ // If we held the document, transfer it to the impl. If we didn't, it's a no-op.
+ impl->setDocument(docjanitor.release());
+
+ delete m_impl;
+ m_impl = impl;
+
+ return make_pair(false,(DOMElement*)NULL);
+}
+
+RequestMapper::Settings XMLRequestMapper::getSettings(const HTTPRequest& request) const
+{
+ ostringstream vhost;
+ vhost << request.getScheme() << "://" << request.getHostname() << ':' << request.getPort();
+
+ const Override* o=m_impl->findOverride(vhost.str().c_str(), request);
+
+ if (m_log.isDebugEnabled()) {
+#ifdef _DEBUG
+ xmltooling::NDC ndc("getSettings");
+#endif
+ pair<bool,const char*> ret=o->getString("applicationId");
+ m_log.debug("mapped %s%s to %s", vhost.str().c_str(), request.getRequestURI() ? request.getRequestURI() : "", ret.second);
+ }
+
+ return Settings(o,o->getAC());
+}
static const XMLCh _acl[] = UNICODE_LITERAL_9(e,x,p,o,r,t,A,C,L);
exportElement->setAttributeNS(NULL,_acl,exportACL.second);
}
- handler = conf.HandlerManager.newPlugin(samlconstants::SAML20_BINDING_URI, make_pair(exportElement, getId()));
+ handler = conf.HandlerManager.newPlugin(
+ samlconstants::SAML20_BINDING_URI, pair<const DOMElement*,const char*>(exportElement, getId())
+ );
m_handlers.push_back(handler);
// Insert into location map. If it contains the handlerURL, we skip past that part.
auto_ptr_char value(rule->getFirstChild()->getNodeValue());
if (provider.get() && *provider.get() && option.get() && *option.get() && value.get() && *value.get()) {
m_transportOptionMap[id.get()].push_back(
- make_pair(provider.get(), make_pair(option.get(), value.get()))
+ make_pair(string(provider.get()), make_pair(string(option.get()), string(value.get())))
);
}
}
# include <netinet/in.h>
#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
using namespace shibsp;
using namespace xmltooling;
using namespace std;
i=m_map.find(name);
if (i!=m_map.end())
- return make_pair(true,i->second.first);
+ return pair<bool,const char*>(true,i->second.first);
else if (m_parent)
return m_parent->getString(name,ns);
return pair<bool,const char*>(false,NULL);
time_t now = time(NULL);
#ifdef HAVE_CTIME_R
char timebuf[32];
- m_map["now"] = ctime_r(&now,timebuf);
+ m_map["now"] = ctime_r(&now,timebuf,sizeof(timebuf));
#else
m_map["now"] = ctime(&now);
#endif