+/*\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
+ * SimpleAttributeResolver.cpp\r
+ * \r
+ * AttributeResolver based on a simple mapping of SAML information.\r
+ */\r
+\r
+#include "internal.h"\r
+#include "Application.h"\r
+#include "SessionCache.h"\r
+#include "attribute/AttributeDecoder.h"\r
+#include "attribute/resolver/AttributeResolver.h"\r
+#include "attribute/resolver/ResolutionContext.h"\r
+#include "binding/SOAPClient.h"\r
+#include "util/SPConstants.h"\r
+\r
+\r
+#include <log4cpp/Category.hh>\r
+#include <saml/binding/SecurityPolicy.h>\r
+#include <saml/saml1/binding/SAML1SOAPClient.h>\r
+#include <saml/saml1/core/Assertions.h>\r
+#include <saml/saml1/core/Protocols.h>\r
+#include <saml/saml2/binding/SAML2SOAPClient.h>\r
+#include <saml/saml2/core/Protocols.h>\r
+#include <saml/saml2/metadata/Metadata.h>\r
+#include <saml/saml2/metadata/MetadataProvider.h>\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
+\r
+using namespace shibsp;\r
+using namespace opensaml::saml1;\r
+using namespace opensaml::saml1p;\r
+using namespace opensaml::saml2;\r
+using namespace opensaml::saml2p;\r
+using namespace opensaml::saml2md;\r
+using namespace opensaml;\r
+using namespace xmltooling;\r
+using namespace log4cpp;\r
+using namespace std;\r
+\r
+namespace shibsp {\r
+\r
+ class SHIBSP_DLLLOCAL SimpleContext : public ResolutionContext\r
+ {\r
+ public:\r
+ SimpleContext(const Application& application, const Session& session)\r
+ : m_app(application), m_session(&session), m_metadata(NULL), m_entity(NULL),\r
+ m_nameid(session.getNameID()), m_token(NULL) {\r
+ }\r
+ \r
+ SimpleContext(\r
+ const Application& application,\r
+ const char* client_addr,\r
+ const EntityDescriptor* issuer,\r
+ const NameID& nameid,\r
+ const opensaml::RootObject* ssoToken=NULL\r
+ ) : m_app(application), m_session(NULL), m_entity(issuer), m_nameid(nameid), m_token(ssoToken) {\r
+ if (client_addr)\r
+ m_client_addr = client_addr;\r
+ }\r
+ \r
+ ~SimpleContext() {\r
+ if (m_metadata)\r
+ m_metadata->unlock();\r
+ for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<shibsp::Attribute>());\r
+ for_each(m_assertions.begin(), m_assertions.end(), xmltooling::cleanup<opensaml::RootObject>());\r
+ }\r
+ \r
+ const Application& getApplication() const {\r
+ return m_app;\r
+ }\r
+ const char* getClientAddress() const {\r
+ return m_session ? m_session->getClientAddress() : m_client_addr.c_str();\r
+ }\r
+ const EntityDescriptor* getEntityDescriptor() const {\r
+ if (m_entity)\r
+ return m_entity;\r
+ if (m_session && m_session->getEntityID()) {\r
+ m_metadata = m_app.getMetadataProvider();\r
+ if (m_metadata) {\r
+ m_metadata->lock();\r
+ return m_entity = m_metadata->getEntityDescriptor(m_session->getEntityID());\r
+ }\r
+ }\r
+ return NULL;\r
+ }\r
+ const NameID& getNameID() const {\r
+ return m_nameid;\r
+ }\r
+ const opensaml::RootObject* getSSOToken() const {\r
+ if (m_token)\r
+ return m_token;\r
+ if (m_session) {\r
+ const vector<const char*>& ids = m_session->getAssertionIDs();\r
+ if (!ids.empty())\r
+ return m_token = m_session->getAssertion(ids.front());\r
+ }\r
+ return NULL;\r
+ }\r
+ const Session* getSession() const {\r
+ return m_session;\r
+ } \r
+ vector<shibsp::Attribute*>& getResolvedAttributes() {\r
+ return m_attributes;\r
+ }\r
+ vector<opensaml::RootObject*>& getResolvedAssertions() {\r
+ return m_assertions;\r
+ }\r
+\r
+ private:\r
+ const Application& m_app;\r
+ const Session* m_session;\r
+ string m_client_addr;\r
+ mutable MetadataProvider* m_metadata;\r
+ mutable const EntityDescriptor* m_entity;\r
+ const NameID& m_nameid;\r
+ mutable const opensaml::RootObject* m_token;\r
+ vector<shibsp::Attribute*> m_attributes;\r
+ vector<opensaml::RootObject*> m_assertions;\r
+ };\r
+ \r
+#if defined (_MSC_VER)\r
+ #pragma warning( push )\r
+ #pragma warning( disable : 4250 )\r
+#endif\r
+\r
+ class SimpleResolverImpl\r
+ {\r
+ public:\r
+ SimpleResolverImpl(const DOMElement* e);\r
+ ~SimpleResolverImpl() {\r
+ for_each(m_decoderMap.begin(), m_decoderMap.end(), cleanup_pair<string,AttributeDecoder>());\r
+ if (m_document)\r
+ m_document->release();\r
+ }\r
+\r
+ void setDocument(DOMDocument* doc) {\r
+ m_document = doc;\r
+ }\r
+\r
+ void query(\r
+ ResolutionContext& ctx, const opensaml::saml1::Assertion* token, const vector<const char*>* attributes=NULL\r
+ ) const;\r
+ void query(\r
+ ResolutionContext& ctx, const opensaml::saml2::Assertion* token, const vector<const char*>* attributes=NULL\r
+ ) const;\r
+ void resolve(\r
+ ResolutionContext& ctx, const opensaml::saml1::Assertion* token, const vector<const char*>* attributes=NULL\r
+ ) const;\r
+ void resolve(\r
+ ResolutionContext& ctx, const opensaml::saml2::Assertion* token, const vector<const char*>* attributes=NULL\r
+ ) const;\r
+\r
+ private:\r
+ DOMDocument* m_document;\r
+ bool m_allowQuery;\r
+ map<string,AttributeDecoder*> m_decoderMap;\r
+#ifdef HAVE_GOOD_STL\r
+ map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> > m_attrMap;\r
+#else\r
+ map< pair<string,string>,pair<const AttributeDecoder*,string> > m_attrMap;\r
+#endif\r
+ };\r
+ \r
+ class SimpleResolver : public AttributeResolver, public ReloadableXMLFile\r
+ {\r
+ public:\r
+ SimpleResolver(const DOMElement* e) : ReloadableXMLFile(e), m_impl(NULL) {\r
+ load();\r
+ }\r
+ ~SimpleResolver() {\r
+ delete m_impl;\r
+ }\r
+ \r
+ ResolutionContext* createResolutionContext(\r
+ const Application& application,\r
+ const char* client_addr,\r
+ const EntityDescriptor* issuer,\r
+ const NameID& nameid,\r
+ const opensaml::RootObject* ssoToken=NULL\r
+ ) const {\r
+ return new SimpleContext(application,client_addr,issuer,nameid,ssoToken);\r
+ }\r
+\r
+ ResolutionContext* createResolutionContext(const Application& application, const Session& session) const {\r
+ return new SimpleContext(application,session);\r
+ }\r
+ \r
+ void resolveAttributes(ResolutionContext& ctx, const vector<const char*>* attributes=NULL) const;\r
+\r
+ protected:\r
+ pair<bool,DOMElement*> load();\r
+ SimpleResolverImpl* m_impl;\r
+ };\r
+\r
+#if defined (_MSC_VER)\r
+ #pragma warning( pop )\r
+#endif\r
+\r
+ AttributeResolver* SHIBSP_DLLLOCAL SimpleAttributeResolverFactory(const DOMElement* const & e)\r
+ {\r
+ return new SimpleResolver(e);\r
+ }\r
+ \r
+ static const XMLCh SIMPLE_NS[] = {\r
+ chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_m, chLatin_a, chLatin_c, chLatin_e, chColon,\r
+ chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chColon,\r
+ chDigit_2, chPeriod, chDigit_0, chColon,\r
+ chLatin_r, chLatin_e, chLatin_s, chLatin_o, chLatin_l, chLatin_v, chLatin_e, chLatin_r, chColon,\r
+ chLatin_s, chLatin_i, chLatin_m, chLatin_p, chLatin_l, chLatin_e, chNull\r
+ };\r
+ static const XMLCh _AttributeResolver[] = UNICODE_LITERAL_17(A,t,t,r,i,b,u,t,e,R,e,s,o,l,v,e,r);\r
+ static const XMLCh allowQuery[] = UNICODE_LITERAL_10(a,l,l,o,w,Q,u,e,r,y);\r
+ static const XMLCh decoderType[] = UNICODE_LITERAL_11(d,e,c,o,d,e,r,T,y,p,e);\r
+};\r
+\r
+SimpleResolverImpl::SimpleResolverImpl(const DOMElement* e) : m_document(NULL), m_allowQuery(true)\r
+{\r
+#ifdef _DEBUG\r
+ xmltooling::NDC ndc("SimpleResolverImpl");\r
+#endif\r
+ Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
+ \r
+ if (!XMLHelper::isNodeNamed(e, SIMPLE_NS, _AttributeResolver))\r
+ throw ConfigurationException("Simple resolver requires resolver:AttributeResolver at root of configuration.");\r
+ \r
+ const XMLCh* flag = e->getAttributeNS(NULL,allowQuery);\r
+ if (flag && (*flag==chLatin_f || *flag==chDigit_0)) {\r
+ log.info("SAML attribute queries disabled");\r
+ m_allowQuery = false;\r
+ }\r
+ \r
+ e = XMLHelper::getFirstChildElement(e, samlconstants::SAML20_NS, opensaml::saml2::Attribute::LOCAL_NAME);\r
+ while (e) {\r
+ // Check for missing Name.\r
+ const XMLCh* name = e->getAttributeNS(NULL, opensaml::saml2::Attribute::NAME_ATTRIB_NAME);\r
+ if (!name || !*name) {\r
+ log.warn("skipping saml:Attribute declared with no Name");\r
+ e = XMLHelper::getNextSiblingElement(e, samlconstants::SAML20_NS, opensaml::saml2::Attribute::LOCAL_NAME);\r
+ continue;\r
+ }\r
+\r
+ auto_ptr_char id(e->getAttributeNS(NULL, opensaml::saml2::Attribute::FRIENDLYNAME_ATTRIB_NAME));\r
+ if (!id.get() || !*id.get()) {\r
+ log.warn("skipping saml:Attribute declared with no FriendlyName");\r
+ e = XMLHelper::getNextSiblingElement(e, samlconstants::SAML20_NS, opensaml::saml2::Attribute::LOCAL_NAME);\r
+ continue;\r
+ }\r
+ \r
+ auto_ptr_char d(e->getAttributeNS(SIMPLE_NS, decoderType));\r
+ const char* dtype = d.get();\r
+ if (!dtype || !*dtype)\r
+ dtype = SIMPLE_ATTRIBUTE_DECODER;\r
+ AttributeDecoder*& decoder = m_decoderMap[dtype];\r
+ if (!decoder) {\r
+ try {\r
+ decoder = SPConfig::getConfig().AttributeDecoderManager.newPlugin(dtype, NULL);\r
+ }\r
+ catch (exception& ex) {\r
+ log.error("error building AttributeDecoder: %s", ex.what());\r
+ e = XMLHelper::getNextSiblingElement(e, samlconstants::SAML20_NS, opensaml::saml2::Attribute::LOCAL_NAME);\r
+ continue;\r
+ }\r
+ }\r
+ \r
+ // Empty NameFormat implies the usual Shib URI naming defaults.\r
+ const XMLCh* format = e->getAttributeNS(NULL, opensaml::saml2::Attribute::NAMEFORMAT_ATTRIB_NAME);\r
+ if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI) ||\r
+ XMLString::equals(format, opensaml::saml2::Attribute::URI_REFERENCE))\r
+ format = &chNull; // ignore default Format/Namespace values\r
+\r
+ // Fetch/create the map entry and see if it's a duplicate rule.\r
+#ifdef HAVE_GOOD_STL\r
+ pair<const AttributeDecoder*,string>& decl = m_attrMap[make_pair(name,format)];\r
+#else\r
+ auto_ptr_char n(name);\r
+ auto_ptr_char f(format);\r
+ pair<const AttributeDecoder*,string>& decl = m_attrMap[make_pair(n.get(),f.get())];\r
+#endif\r
+ if (decl.first) {\r
+ log.warn("skipping duplicate saml:Attribute declaration (same Name and NameFormat)");\r
+ e = XMLHelper::getNextSiblingElement(e, samlconstants::SAML20_NS, opensaml::saml2::Attribute::LOCAL_NAME);\r
+ continue;\r
+ }\r
+\r
+ if (log.isDebugEnabled()) {\r
+#ifdef HAVE_GOOD_STL\r
+ auto_ptr_char n(name);\r
+ auto_ptr_char f(format);\r
+#endif\r
+ log.debug("creating declaration for (Name=%s) %s%s)", n.get(), *f.get() ? "(Format/Namespace=" : "", f.get());\r
+ }\r
+ \r
+ decl.first = decoder;\r
+ decl.second = id.get();\r
+ \r
+ e = XMLHelper::getNextSiblingElement(e, samlconstants::SAML20_NS, opensaml::saml2::Attribute::LOCAL_NAME);\r
+ }\r
+}\r
+\r
+void SimpleResolverImpl::resolve(\r
+ ResolutionContext& ctx, const opensaml::saml1::Assertion* token, const vector<const char*>* attributes\r
+ ) const\r
+{\r
+ set<string> aset;\r
+ if (attributes)\r
+ for(vector<const char*>::const_iterator i=attributes->begin(); i!=attributes->end(); ++i)\r
+ aset.insert(*i);\r
+\r
+ vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();\r
+\r
+#ifdef HAVE_GOOD_STL\r
+ map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
+#else\r
+ map< pair<string,string>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
+#endif\r
+\r
+ // Check the NameID based on the format.\r
+ const XMLCh* name;\r
+ const XMLCh* format = ctx.getNameID().getFormat();\r
+ if (!format) {\r
+ format = NameID::UNSPECIFIED;\r
+#ifdef HAVE_GOOD_STL\r
+ if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {\r
+#else\r
+ auto_ptr_char temp(format);\r
+ if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {\r
+#endif\r
+ if (aset.empty() || aset.count(rule->second.second))\r
+ resolved.push_back(rule->second.first->decode(rule->second.second.c_str(), &ctx.getNameID()));\r
+ }\r
+ }\r
+\r
+ const vector<opensaml::saml1::AttributeStatement*>& statements = token->getAttributeStatements();\r
+ for (vector<opensaml::saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {\r
+ const vector<opensaml::saml1::Attribute*>& attrs = const_cast<const opensaml::saml1::AttributeStatement*>(*s)->getAttributes();\r
+ for (vector<opensaml::saml1::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) {\r
+ name = (*a)->getAttributeName();\r
+ format = (*a)->getAttributeNamespace();\r
+ if (!name || !*name)\r
+ continue;\r
+ if (!format)\r
+ format = &chNull;\r
+#ifdef HAVE_GOOD_STL\r
+ if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
+#else\r
+ auto_ptr_char temp1(name);\r
+ auto_ptr_char temp2(format);\r
+ if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
+#endif\r
+ if (aset.empty() || aset.count(rule->second.second))\r
+ resolved.push_back(rule->second.first->decode(rule->second.second.c_str(), *a));\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+void SimpleResolverImpl::resolve(\r
+ ResolutionContext& ctx, const opensaml::saml2::Assertion* token, const vector<const char*>* attributes\r
+ ) const\r
+{\r
+ set<string> aset;\r
+ if (attributes)\r
+ for(vector<const char*>::const_iterator i=attributes->begin(); i!=attributes->end(); ++i)\r
+ aset.insert(*i);\r
+\r
+ vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();\r
+\r
+#ifdef HAVE_GOOD_STL\r
+ map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
+#else\r
+ map< pair<string,string>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
+#endif\r
+\r
+ // Check the NameID based on the format.\r
+ const XMLCh* name;\r
+ const XMLCh* format = ctx.getNameID().getFormat();\r
+ if (!format) {\r
+ format = NameID::UNSPECIFIED;\r
+#ifdef HAVE_GOOD_STL\r
+ if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {\r
+#else\r
+ auto_ptr_char temp(format);\r
+ if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {\r
+#endif\r
+ if (aset.empty() || aset.count(rule->second.second))\r
+ resolved.push_back(rule->second.first->decode(rule->second.second.c_str(), &ctx.getNameID()));\r
+ }\r
+ }\r
+\r
+ const vector<opensaml::saml2::AttributeStatement*>& statements = token->getAttributeStatements();\r
+ for (vector<opensaml::saml2::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {\r
+ const vector<opensaml::saml2::Attribute*>& attrs = const_cast<const opensaml::saml2::AttributeStatement*>(*s)->getAttributes();\r
+ for (vector<opensaml::saml2::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) {\r
+ name = (*a)->getName();\r
+ format = (*a)->getNameFormat();\r
+ if (!name || !*name)\r
+ continue;\r
+ if (!format)\r
+ format = &chNull;\r
+#ifdef HAVE_GOOD_STL\r
+ if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
+#else\r
+ auto_ptr_char temp1(name);\r
+ auto_ptr_char temp2(format);\r
+ if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
+#endif\r
+ if (aset.empty() || aset.count(rule->second.second))\r
+ resolved.push_back(rule->second.first->decode(rule->second.second.c_str(), *a));\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+void SimpleResolverImpl::query(ResolutionContext& ctx, const opensaml::saml1::Assertion* token, const vector<const char*>* attributes) const\r
+{\r
+ if (!m_allowQuery)\r
+ return;\r
+\r
+#ifdef _DEBUG\r
+ xmltooling::NDC ndc("query");\r
+#endif\r
+ Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
+\r
+ const EntityDescriptor* entity = ctx.getEntityDescriptor();\r
+ if (!entity) {\r
+ log.debug("no issuer information available, skipping query");\r
+ return;\r
+ }\r
+ const AttributeAuthorityDescriptor* AA =\r
+ entity->getAttributeAuthorityDescriptor(\r
+ token->getMinorVersion().second==1 ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM\r
+ );\r
+ if (!AA) {\r
+ log.debug("no SAML 1.%d AttributeAuthority role found in metadata", token->getMinorVersion().second);\r
+ return;\r
+ }\r
+\r
+ SecurityPolicy policy;\r
+ shibsp::SOAPClient soaper(ctx.getApplication(),policy);\r
+\r
+ auto_ptr_XMLCh binding(samlconstants::SAML1_BINDING_SOAP);\r
+ opensaml::saml1p::Response* response=NULL;\r
+ const vector<AttributeService*>& endpoints=AA->getAttributeServices();\r
+ for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {\r
+ try {\r
+ if (!XMLString::equals((*ep)->getBinding(),binding.get()))\r
+ continue;\r
+ auto_ptr_char loc((*ep)->getLocation());\r
+ auto_ptr_XMLCh issuer(ctx.getApplication().getString("providerId").second);\r
+ opensaml::saml1::Subject* subject = opensaml::saml1::SubjectBuilder::buildSubject();\r
+ subject->setNameIdentifier(token->getAuthenticationStatements().front()->getSubject()->getNameIdentifier()->cloneNameIdentifier());\r
+ opensaml::saml1p::AttributeQuery* query = opensaml::saml1p::AttributeQueryBuilder::buildAttributeQuery();\r
+ query->setSubject(subject);\r
+ Request* request = RequestBuilder::buildRequest();\r
+ request->setAttributeQuery(query);\r
+ query->setResource(issuer.get());\r
+ request->setMinorVersion(token->getMinorVersion().second);\r
+ SAML1SOAPClient client(soaper);\r
+ client.sendSAML(request, *AA, loc.get());\r
+ response = client.receiveSAML();\r
+ }\r
+ catch (exception& ex) {\r
+ log.error("exception making SAML query: %s", ex.what());\r
+ soaper.reset();\r
+ }\r
+ }\r
+\r
+ if (!response) {\r
+ log.error("unable to successfully query for attributes");\r
+ return;\r
+ }\r
+\r
+ time_t now = time(NULL);\r
+ const Validator* tokval = ctx.getApplication().getTokenValidator(now, AA);\r
+ const vector<opensaml::saml1::Assertion*>& assertions = const_cast<const opensaml::saml1p::Response*>(response)->getAssertions();\r
+ if (assertions.size()==1) {\r
+ auto_ptr<opensaml::saml1p::Response> wrapper(response);\r
+ opensaml::saml1::Assertion* newtoken = assertions.front();\r
+ if (!XMLString::equals(policy.getIssuer() ? policy.getIssuer()->getName() : NULL, newtoken->getIssuer())) {\r
+ log.error("assertion issued by someone other than AA, rejecting it");\r
+ return;\r
+ }\r
+ try {\r
+ tokval->validate(newtoken);\r
+ }\r
+ catch (exception& ex) {\r
+ log.error("assertion failed validation check: %s", ex.what());\r
+ }\r
+ newtoken->detach();\r
+ wrapper.release();\r
+ ctx.getResolvedAssertions().push_back(newtoken);\r
+ resolve(ctx, newtoken, attributes);\r
+ }\r
+ else {\r
+ auto_ptr<opensaml::saml1p::Response> wrapper(response);\r
+ for (vector<opensaml::saml1::Assertion*>::const_iterator a = assertions.begin(); a!=assertions.end(); ++a) {\r
+ if (!XMLString::equals(policy.getIssuer() ? policy.getIssuer()->getName() : NULL, (*a)->getIssuer())) {\r
+ log.error("assertion issued by someone other than AA, rejecting it");\r
+ continue;\r
+ }\r
+ try {\r
+ tokval->validate(*a);\r
+ }\r
+ catch (exception& ex) {\r
+ log.error("assertion failed validation check: %s", ex.what());\r
+ }\r
+ resolve(ctx, *a, attributes);\r
+ ctx.getResolvedAssertions().push_back((*a)->cloneAssertion());\r
+ }\r
+ }\r
+}\r
+\r
+void SimpleResolverImpl::query(ResolutionContext& ctx, const opensaml::saml2::Assertion* token, const vector<const char*>* attributes) const\r
+{\r
+ if (!m_allowQuery)\r
+ return;\r
+\r
+#ifdef _DEBUG\r
+ xmltooling::NDC ndc("query");\r
+#endif\r
+ Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
+\r
+ const EntityDescriptor* entity = ctx.getEntityDescriptor();\r
+ if (!entity) {\r
+ log.debug("no issuer information available, skipping query");\r
+ return;\r
+ }\r
+ const AttributeAuthorityDescriptor* AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML20P_NS);\r
+ if (!AA) {\r
+ log.debug("no SAML 2 AttributeAuthority role found in metadata");\r
+ return;\r
+ }\r
+\r
+ SecurityPolicy policy;\r
+ shibsp::SOAPClient soaper(ctx.getApplication(),policy);\r
+\r
+ auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);\r
+ opensaml::saml2p::StatusResponseType* srt=NULL;\r
+ const vector<AttributeService*>& endpoints=AA->getAttributeServices();\r
+ for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !srt && ep!=endpoints.end(); ++ep) {\r
+ try {\r
+ if (!XMLString::equals((*ep)->getBinding(),binding.get()))\r
+ continue;\r
+ auto_ptr_char loc((*ep)->getLocation());\r
+ auto_ptr_XMLCh issuer(ctx.getApplication().getString("providerId").second);\r
+ opensaml::saml2::Subject* subject = opensaml::saml2::SubjectBuilder::buildSubject();\r
+ subject->setNameID(token->getSubject()->getNameID()->cloneNameID());\r
+ opensaml::saml2p::AttributeQuery* query = opensaml::saml2p::AttributeQueryBuilder::buildAttributeQuery();\r
+ query->setSubject(subject);\r
+ Issuer* iss = IssuerBuilder::buildIssuer();\r
+ query->setIssuer(iss);\r
+ iss->setName(issuer.get());\r
+ SAML2SOAPClient client(soaper);\r
+ client.sendSAML(query, *AA, loc.get());\r
+ srt = client.receiveSAML();\r
+ }\r
+ catch (exception& ex) {\r
+ log.error("exception making SAML query: %s", ex.what());\r
+ soaper.reset();\r
+ }\r
+ }\r
+\r
+ if (!srt) {\r
+ log.error("unable to successfully query for attributes");\r
+ return;\r
+ }\r
+ opensaml::saml2p::Response* response = dynamic_cast<opensaml::saml2p::Response*>(srt);\r
+ if (!response) {\r
+ delete srt;\r
+ log.error("message was not a samlp:Response");\r
+ return;\r
+ }\r
+\r
+ time_t now = time(NULL);\r
+ const Validator* tokval = ctx.getApplication().getTokenValidator(now, AA);\r
+ const vector<opensaml::saml2::Assertion*>& assertions = const_cast<const opensaml::saml2p::Response*>(response)->getAssertions();\r
+ if (assertions.size()==1) {\r
+ auto_ptr<opensaml::saml2p::Response> wrapper(response);\r
+ opensaml::saml2::Assertion* newtoken = assertions.front();\r
+ if (!XMLString::equals(policy.getIssuer() ? policy.getIssuer()->getName() : NULL, newtoken->getIssuer() ? newtoken->getIssuer()->getName() : NULL)) {\r
+ log.error("assertion issued by someone other than AA, rejecting it");\r
+ return;\r
+ }\r
+ try {\r
+ tokval->validate(newtoken);\r
+ }\r
+ catch (exception& ex) {\r
+ log.error("assertion failed validation check: %s", ex.what());\r
+ }\r
+ newtoken->detach();\r
+ wrapper.release();\r
+ ctx.getResolvedAssertions().push_back(newtoken);\r
+ resolve(ctx, newtoken, attributes);\r
+ }\r
+ else {\r
+ auto_ptr<opensaml::saml2p::Response> wrapper(response);\r
+ for (vector<opensaml::saml2::Assertion*>::const_iterator a = assertions.begin(); a!=assertions.end(); ++a) {\r
+ if (!XMLString::equals(policy.getIssuer() ? policy.getIssuer()->getName() : NULL, (*a)->getIssuer() ? (*a)->getIssuer()->getName() : NULL)) {\r
+ log.error("assertion issued by someone other than AA, rejecting it");\r
+ return;\r
+ }\r
+ try {\r
+ tokval->validate(*a);\r
+ }\r
+ catch (exception& ex) {\r
+ log.error("assertion failed validation check: %s", ex.what());\r
+ }\r
+ resolve(ctx, *a, attributes);\r
+ ctx.getResolvedAssertions().push_back((*a)->cloneAssertion());\r
+ }\r
+ }\r
+}\r
+\r
+void SimpleResolver::resolveAttributes(ResolutionContext& ctx, const vector<const char*>* attributes) const\r
+{\r
+#ifdef _DEBUG\r
+ xmltooling::NDC ndc("resolveAttributes");\r
+#endif\r
+ Category& log=Category::getInstance(SHIBSP_LOGCAT".AttributeResolver");\r
+ \r
+ log.debug("examining incoming SSO token");\r
+\r
+ const opensaml::RootObject* token = ctx.getSSOToken();\r
+ if (!token) {\r
+ log.warn("no SSO token supplied to resolver, returning nothing");\r
+ return;\r
+ }\r
+ const opensaml::saml2::Assertion* token2 = dynamic_cast<const opensaml::saml2::Assertion*>(token);\r
+ if (token2) {\r
+ if (!token2->getAttributeStatements().empty()) {\r
+ log.debug("found SAML 2 SSO token with an AttributeStatement");\r
+ return m_impl->resolve(ctx, token2, attributes);\r
+ }\r
+ return m_impl->query(ctx, token2, attributes);\r
+ }\r
+\r
+ const opensaml::saml1::Assertion* token1 = dynamic_cast<const opensaml::saml1::Assertion*>(token);\r
+ if (token1) {\r
+ if (!token1->getAttributeStatements().empty()) {\r
+ log.debug("found SAML 1 SSO token with an AttributeStatement");\r
+ return m_impl->resolve(ctx, token1, attributes);\r
+ }\r
+ return m_impl->query(ctx, token1, attributes);\r
+ }\r
+\r
+ log.warn("unrecognized token type, returning nothing");\r
+}\r
+\r
+pair<bool,DOMElement*> SimpleResolver::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
+ SimpleResolverImpl* impl = new SimpleResolverImpl(raw.second);\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