From: Scott Cantor Date: Mon, 14 May 2007 00:29:49 +0000 (+0000) Subject: Fixed attribute-based functors. X-Git-Tag: 2.0-alpha1~55 X-Git-Url: http://www.project-moonshot.org/gitweb/?a=commitdiff_plain;ds=sidebyside;h=35f74892a217013b69cf9c69a5d7fbc38be75802;p=shibboleth%2Fcpp-sp.git Fixed attribute-based functors. Implemented special handling of REMOTE_USER. Added header clearing method to resolver/extractor. Added default policy file. --- diff --git a/configs/Makefile.am b/configs/Makefile.am index 8198bb5..0c5838f 100644 --- a/configs/Makefile.am +++ b/configs/Makefile.am @@ -22,6 +22,7 @@ BUILTCONFIGFILES = \ native.logger \ shibd.logger \ attribute-map.xml \ + attribute-policy.xml \ example-metadata.xml # While BUILTCONFIGFILES are processed, these are not; so we should pull @@ -78,6 +79,9 @@ shibboleth.xml: ${srcdir}/shibboleth.xml.in Makefile ${top_builddir}/config.stat attribute-map.xml: ${srcdir}/attribute-map.xml.in Makefile ${top_builddir}/config.status $(MAKE) do-build-file FILE=$@ +attribute-policy.xml: ${srcdir}/attribute-policy.xml.in Makefile ${top_builddir}/config.status + $(MAKE) do-build-file FILE=$@ + example-metadata.xml: ${srcdir}/example-metadata.xml.in Makefile ${top_builddir}/config.status $(MAKE) do-build-file FILE=$@ @@ -112,11 +116,13 @@ CLEANFILES = \ native.logger \ shibboleth.xml \ attribute-map.xml \ + attribute-policy.xml \ example-metadata.xml EXTRA_DIST = shibboleth.xml.in \ attribute-map.xml.in \ + attribute-policy.xml.in \ example-metadata.xml.in \ native.logger.in \ shibd.logger.in \ diff --git a/configs/attribute-map.xml.in b/configs/attribute-map.xml.in index c683112..e47d1b5 100644 --- a/configs/attribute-map.xml.in +++ b/configs/attribute-map.xml.in @@ -4,10 +4,10 @@ - + - + @@ -31,17 +31,17 @@ - + - + - + diff --git a/configs/attribute-policy.xml.in b/configs/attribute-policy.xml.in new file mode 100644 index 0000000..c04d18f --- /dev/null +++ b/configs/attribute-policy.xml.in @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/configs/shibboleth.xml.in b/configs/shibboleth.xml.in index 07bd8ec..462d902 100644 --- a/configs/shibboleth.xml.in +++ b/configs/shibboleth.xml.in @@ -228,6 +228,9 @@ + + + diff --git a/schemas/shibboleth-2.0-afp-mf-basic.xsd b/schemas/shibboleth-2.0-afp-mf-basic.xsd index 90ab2b8..8d4389f 100644 --- a/schemas/shibboleth-2.0-afp-mf-basic.xsd +++ b/schemas/shibboleth-2.0-afp-mf-basic.xsd @@ -1,7 +1,10 @@ - + diff --git a/schemas/shibboleth-2.0-afp.xsd b/schemas/shibboleth-2.0-afp.xsd index 857729e..e132a4f 100644 --- a/schemas/shibboleth-2.0-afp.xsd +++ b/schemas/shibboleth-2.0-afp.xsd @@ -1,7 +1,10 @@ - + diff --git a/schemas/shibboleth-2.0-attribute-map.xsd b/schemas/shibboleth-2.0-attribute-map.xsd index caf782c..1e4fb20 100644 --- a/schemas/shibboleth-2.0-attribute-map.xsd +++ b/schemas/shibboleth-2.0-attribute-map.xsd @@ -1,7 +1,8 @@ + xmlns:am="urn:mace:shibboleth:2.0:attribute-map" + elementFormDefault="qualified"> diff --git a/schemas/shibboleth-2.0-native-sp-config.xsd b/schemas/shibboleth-2.0-native-sp-config.xsd index 3cbd1fe..9f1a0f5 100644 --- a/schemas/shibboleth-2.0-native-sp-config.xsd +++ b/schemas/shibboleth-2.0-native-sp-config.xsd @@ -339,7 +339,7 @@ - + Container for global settings and application-specific overrides @@ -362,6 +362,7 @@ + @@ -387,6 +388,7 @@ + diff --git a/shibsp/Application.h b/shibsp/Application.h index 7f67005..28e970c 100644 --- a/shibsp/Application.h +++ b/shibsp/Application.h @@ -117,6 +117,14 @@ namespace shibsp { */ virtual AttributeResolver* getAttributeResolver() const=0; + /** + * Returns a set of attribute IDs to use as a REMOTE_USER value. + *

The first attribute with a value (and only a single value) will be used. + * + * @return a set of attribute IDs, or an empty set + */ + virtual const std::set& getRemoteUserAttributeIds() const=0; + /** * Returns the CredentialResolver instance associated with this Application. * diff --git a/shibsp/ServiceProvider.cpp b/shibsp/ServiceProvider.cpp index 30eb28f..af2e8f0 100644 --- a/shibsp/ServiceProvider.cpp +++ b/shibsp/ServiceProvider.cpp @@ -28,6 +28,8 @@ #include "SessionCache.h" #include "SPRequest.h" #include "attribute/Attribute.h" +#include "attribute/resolver/AttributeExtractor.h" +#include "attribute/resolver/AttributeResolver.h" #include "handler/SessionInitiator.h" #include "util/TemplateParameters.h" @@ -98,23 +100,18 @@ namespace shibsp { request.clearHeader("Shib-Attributes"); request.clearHeader("Shib-Application-ID"); - // Clear out the list of mapped attributes - /* TODO: need some kind of master attribute list via the new resolver - Iterator provs=dynamic_cast(getApplication()).getAAPProviders(); - while (provs.hasNext()) { - IAAP* aap=provs.next(); - xmltooling::Locker locker(aap); - Iterator rules=aap->getAttributeRules(); - while (rules.hasNext()) { - const char* header=rules.next()->getHeader(); - if (header) - request.clearHeader(header); - } + // Let plugins do the rest. + AttributeExtractor* extractor = request.getApplication().getAttributeExtractor(); + if (extractor) { + Locker locker(extractor); + extractor->clearHeaders(request); + } + AttributeResolver* resolver = request.getApplication().getAttributeResolver(); + if (resolver) { + Locker locker(resolver); + resolver->clearHeaders(request); } - */ } - - static const XMLCh SessionInitiator[] = UNICODE_LITERAL_16(S,e,s,s,i,o,n,I,n,i,t,i,a,t,o,r); }; void SHIBSP_API shibsp::registerServiceProviders() @@ -376,31 +373,36 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio } // Export the attributes. + bool remoteUserSet = false; const multimap& attributes = session->getAttributes(); for (multimap::const_iterator a = attributes.begin(); a!=attributes.end(); ++a) { const vector& vals = a->second->getSerializedValues(); - if (a->first == "REMOTE_USER" && !vals.empty()) + + // See if this needs to be set as the REMOTE_USER value. + if (!remoteUserSet && !vals.empty() && app->getRemoteUserAttributeIds().count(a->first)) { request.setRemoteUser(vals.front().c_str()); - else { - string header(request.getSecureHeader(a->first.c_str())); - for (vector::const_iterator v = vals.begin(); v!=vals.end(); ++v) { - if (!header.empty()) - header += ";"; - string::size_type pos = v->find_first_of(';',string::size_type(0)); - if (pos!=string::npos) { - string value(*v); - for (; pos != string::npos; pos = value.find_first_of(';',pos)) { - value.insert(pos, "\\"); - pos += 2; - } - header += value; - } - else { - header += (*v); + remoteUserSet = true; + } + + // Handle the normal export case. + string header(request.getSecureHeader(a->first.c_str())); + for (vector::const_iterator v = vals.begin(); v!=vals.end(); ++v) { + if (!header.empty()) + header += ";"; + string::size_type pos = v->find_first_of(';',string::size_type(0)); + if (pos!=string::npos) { + string value(*v); + for (; pos != string::npos; pos = value.find_first_of(';',pos)) { + value.insert(pos, "\\"); + pos += 2; } + header += value; + } + else { + header += (*v); } - request.setHeader(a->first.c_str(), header.c_str()); } + request.setHeader(a->first.c_str(), header.c_str()); } return make_pair(false,0); diff --git a/shibsp/attribute/filtering/impl/AttributeScopeMatchesShibMDScopeFunctor.cpp b/shibsp/attribute/filtering/impl/AttributeScopeMatchesShibMDScopeFunctor.cpp index 5b7b26c..b7899b3 100644 --- a/shibsp/attribute/filtering/impl/AttributeScopeMatchesShibMDScopeFunctor.cpp +++ b/shibsp/attribute/filtering/impl/AttributeScopeMatchesShibMDScopeFunctor.cpp @@ -44,7 +44,7 @@ namespace shibsp { { public: bool evaluatePolicyRequirement(const FilteringContext& filterContext) const { - return false; + throw AttributeFilteringException("Metadata scope matching not usable as a PolicyRequirement."); } bool evaluatePermitValue(const FilteringContext& filterContext, const Attribute& attribute, size_t index) const { diff --git a/shibsp/attribute/filtering/impl/AttributeScopeRegexFunctor.cpp b/shibsp/attribute/filtering/impl/AttributeScopeRegexFunctor.cpp index 6164a76..c3e58e8 100644 --- a/shibsp/attribute/filtering/impl/AttributeScopeRegexFunctor.cpp +++ b/shibsp/attribute/filtering/impl/AttributeScopeRegexFunctor.cpp @@ -70,9 +70,9 @@ namespace shibsp { } bool evaluatePermitValue(const FilteringContext& filterContext, const Attribute& attribute, size_t index) const { - if (!XMLString::equals(m_attributeID.get(), attribute.getId())) - return hasScope(filterContext); - return matches(attribute, index); + if (!m_attributeID.get() || !*m_attributeID.get() || XMLString::equals(m_attributeID.get(), attribute.getId())) + return matches(attribute, index); + return hasScope(filterContext); } }; diff --git a/shibsp/attribute/filtering/impl/AttributeScopeStringFunctor.cpp b/shibsp/attribute/filtering/impl/AttributeScopeStringFunctor.cpp index 70a252f..d9749fc 100644 --- a/shibsp/attribute/filtering/impl/AttributeScopeStringFunctor.cpp +++ b/shibsp/attribute/filtering/impl/AttributeScopeStringFunctor.cpp @@ -58,9 +58,9 @@ namespace shibsp { } bool evaluatePermitValue(const FilteringContext& filterContext, const Attribute& attribute, size_t index) const { - if (!XMLString::equals(m_attributeID.get(), attribute.getId())) - return hasScope(filterContext); - return XMLString::equals(attribute.getScope(index), m_value.get()); + if (!m_attributeID.get() || !*m_attributeID.get() || XMLString::equals(m_attributeID.get(), attribute.getId())) + return XMLString::equals(attribute.getScope(index), m_value.get()); + return hasScope(filterContext); } }; diff --git a/shibsp/attribute/filtering/impl/AttributeValueRegexFunctor.cpp b/shibsp/attribute/filtering/impl/AttributeValueRegexFunctor.cpp index b4a899e..5fc4fa0 100644 --- a/shibsp/attribute/filtering/impl/AttributeValueRegexFunctor.cpp +++ b/shibsp/attribute/filtering/impl/AttributeValueRegexFunctor.cpp @@ -57,10 +57,10 @@ namespace shibsp { try { m_regex = new RegularExpression(r, e->getAttributeNS(NULL,options)); } - catch (XMLException& ex) { - xmltooling::auto_ptr_char temp(ex.getMessage()); - throw ConfigurationException(temp.get()); - } + catch (XMLException& ex) { + xmltooling::auto_ptr_char temp(ex.getMessage()); + throw ConfigurationException(temp.get()); + } } bool evaluatePolicyRequirement(const FilteringContext& filterContext) const { @@ -70,9 +70,9 @@ namespace shibsp { } bool evaluatePermitValue(const FilteringContext& filterContext, const Attribute& attribute, size_t index) const { - if (!XMLString::equals(m_attributeID.get(), attribute.getId())) - return hasValue(filterContext); - return matches(attribute, index); + if (!m_attributeID.get() || !*m_attributeID.get() || XMLString::equals(m_attributeID.get(), attribute.getId())) + return matches(attribute, index); + return hasValue(filterContext); } }; diff --git a/shibsp/attribute/filtering/impl/AttributeValueStringFunctor.cpp b/shibsp/attribute/filtering/impl/AttributeValueStringFunctor.cpp index c969ac5..0c2739c 100644 --- a/shibsp/attribute/filtering/impl/AttributeValueStringFunctor.cpp +++ b/shibsp/attribute/filtering/impl/AttributeValueStringFunctor.cpp @@ -59,9 +59,9 @@ namespace shibsp { } bool evaluatePermitValue(const FilteringContext& filterContext, const Attribute& attribute, size_t index) const { - if (!XMLString::equals(m_attributeID.get(), attribute.getId())) - return hasValue(filterContext); - return matches(attribute, index); + if (!m_attributeID.get() || !*m_attributeID.get() || XMLString::equals(m_attributeID.get(), attribute.getId())) + return matches(attribute, index); + return hasValue(filterContext); } }; diff --git a/shibsp/attribute/resolver/AttributeExtractor.h b/shibsp/attribute/resolver/AttributeExtractor.h index 80ffb85..af61e62 100644 --- a/shibsp/attribute/resolver/AttributeExtractor.h +++ b/shibsp/attribute/resolver/AttributeExtractor.h @@ -32,6 +32,7 @@ namespace shibsp { class SHIBSP_API Application; class SHIBSP_API Attribute; + class SHIBSP_API SPRequest; /** * A service that extracts and decodes attributes from XML objects. @@ -60,6 +61,14 @@ namespace shibsp { const xmltooling::XMLObject& xmlObject, std::multimap& attributes ) const=0; + + /** + * Clears possible HTTP request headers that might be populated + * during attribute export. + * + * @param request the SP request being processed + */ + virtual void clearHeaders(SPRequest& request) const=0; }; /** diff --git a/shibsp/attribute/resolver/AttributeResolver.h b/shibsp/attribute/resolver/AttributeResolver.h index 2e143b9..4b12938 100644 --- a/shibsp/attribute/resolver/AttributeResolver.h +++ b/shibsp/attribute/resolver/AttributeResolver.h @@ -33,8 +33,9 @@ namespace shibsp { class SHIBSP_API Application; class SHIBSP_API Attribute; - class SHIBSP_API Session; class SHIBSP_API ResolutionContext; + class SHIBSP_API Session; + class SHIBSP_API SPRequest; #if defined (_MSC_VER) #pragma warning( push ) @@ -95,6 +96,14 @@ namespace shibsp { * @throws AttributeResolutionException thrown if there is a problem resolving the attributes for the subject */ virtual void resolveAttributes(ResolutionContext& ctx) const=0; + + /** + * Clears possible HTTP request headers that might be populated + * during attribute export. + * + * @param request the SP request being processed + */ + virtual void clearHeaders(SPRequest& request) const=0; }; #if defined (_MSC_VER) diff --git a/shibsp/attribute/resolver/impl/ChainingAttributeResolver.cpp b/shibsp/attribute/resolver/impl/ChainingAttributeResolver.cpp index f2ef837..a2a1154 100644 --- a/shibsp/attribute/resolver/impl/ChainingAttributeResolver.cpp +++ b/shibsp/attribute/resolver/impl/ChainingAttributeResolver.cpp @@ -102,6 +102,11 @@ namespace shibsp { void resolveAttributes(ResolutionContext& ctx) const; + void clearHeaders(SPRequest& request) const { + for (vector::const_iterator i=m_resolvers.begin(); i!=m_resolvers.end(); ++i) + (*i)->clearHeaders(request); + } + private: vector m_resolvers; }; diff --git a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp index 52bb845..c846355 100644 --- a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp +++ b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp @@ -184,6 +184,10 @@ namespace shibsp { void resolveAttributes(ResolutionContext& ctx) const; + void clearHeaders(SPRequest& request) const { + // Doesn't have to do anything, the extractor is the only possibly source of attributes. + } + private: bool SAML1Query(QueryContext& ctx) const; bool SAML2Query(QueryContext& ctx) const; @@ -478,10 +482,11 @@ void QueryResolver::resolveAttributes(ResolutionContext& ctx) const if (qctx.getNameID() && qctx.getEntityDescriptor()) { m_log.debug("attempting SAML 2.0 attribute query"); - if (!SAML2Query(qctx)) { - m_log.debug("attempting SAML 1.x attribute query"); - SAML1Query(qctx); - } + if (SAML2Query(qctx)) + return; + m_log.debug("attempting SAML 1.x attribute query"); + SAML1Query(qctx); + return; } m_log.warn("can't attempt attribute query, either no NameID or no metadata to use"); } diff --git a/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp b/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp index c93d87d..9b73062 100644 --- a/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp +++ b/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp @@ -23,6 +23,7 @@ #include "internal.h" #include "Application.h" #include "ServiceProvider.h" +#include "SPRequest.h" #include "attribute/AttributeDecoder.h" #include "attribute/resolver/AttributeExtractor.h" #include "util/SPConstants.h" @@ -80,6 +81,11 @@ namespace shibsp { const Application& application, const char* assertingParty, const saml2::Attribute& attr, multimap& attributes ) const; + void clearHeaders(SPRequest& request) const { + for (vector::const_iterator i = m_attributeIds.begin(); i!=m_attributeIds.end(); ++i) + request.clearHeader(i->c_str()); + } + private: Category& m_log; DOMDocument* m_document; @@ -89,6 +95,7 @@ namespace shibsp { typedef map< pair,pair > attrmap_t; #endif attrmap_t m_attrMap; + vector m_attributeIds; }; class XMLExtractor : public AttributeExtractor, public ReloadableXMLFile @@ -105,6 +112,11 @@ namespace shibsp { const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, multimap& attributes ) const; + void clearHeaders(SPRequest& request) const { + if (m_impl) + m_impl->clearHeaders(request); + } + protected: pair load(); @@ -158,6 +170,11 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) : m_log(l child = XMLHelper::getNextSiblingElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME); continue; } + else if (!strcmp(id.get(), "REMOTE_USER")) { + m_log.warn("skipping Attribute, id of REMOTE_USER is a reserved name"); + child = XMLHelper::getNextSiblingElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME); + continue; + } AttributeDecoder* decoder=NULL; try { @@ -210,6 +227,7 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) : m_log(l decl.first = decoder; decl.second = id.get(); + m_attributeIds.push_back(id.get()); child = XMLHelper::getNextSiblingElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME); } @@ -341,6 +359,9 @@ void XMLExtractor::extractAttributes( const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, multimap& attributes ) const { + if (!m_impl) + return; + // Check for assertions. if (XMLString::equals(xmlObject.getElementQName().getLocalPart(), saml1::Assertion::LOCAL_NAME)) { const saml2::Assertion* token2 = dynamic_cast(&xmlObject); diff --git a/shibsp/impl/XMLServiceProvider.cpp b/shibsp/impl/XMLServiceProvider.cpp index a3fdf9b..92f7a08 100644 --- a/shibsp/impl/XMLServiceProvider.cpp +++ b/shibsp/impl/XMLServiceProvider.cpp @@ -101,6 +101,9 @@ namespace { AttributeResolver* getAttributeResolver() const { return (!m_attrResolver && m_base) ? m_base->getAttributeResolver() : m_attrResolver; } + const set& getRemoteUserAttributeIds() const { + return (m_attributeIds.empty() && m_base) ? m_base->getRemoteUserAttributeIds() : m_attributeIds; + } CredentialResolver* getCredentialResolver() const { return (!m_credResolver && m_base) ? m_base->getCredentialResolver() : m_credResolver; } @@ -132,6 +135,7 @@ namespace { AttributeResolver* m_attrResolver; CredentialResolver* m_credResolver; vector m_audiences; + set m_attributeIds; // manage handler objects vector m_handlers; @@ -372,6 +376,25 @@ XMLApplication::XMLApplication( m_hash+=getString("entityID").second; m_hash=samlConf.hashSHA1(m_hash.c_str(), true); + pair attributes = getString("REMOTE_USER"); + if (attributes.first) { + char* dup = strdup(attributes.second); + char* pos; + char* start = dup; + while (start && *start) { + while (*start && isspace(*start)) + start++; + if (!*start) + break; + pos = strchr(start,' '); + if (pos) + *pos=0; + m_attributeIds.insert(start); + start = pos ? pos+1 : NULL; + } + free(dup); + } + const PropertySet* sessions = getPropertySet("Sessions"); // Process handlers.