From fa1832aa42cb8a1f9461e31c9f105bd65a1981e1 Mon Sep 17 00:00:00 2001 From: cantor Date: Sun, 6 May 2007 23:44:32 +0000 Subject: [PATCH] Attribute filtering code. git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2232 cb58f699-b61c-0410-a6fe-9272a202ed29 --- .cdtproject | 6 +- schemas/shibboleth-2.0-afp.xsd | 22 +- shibsp/Application.h | 8 + shibsp/Makefile.am | 10 + shibsp/SPConfig.cpp | 63 ++-- shibsp/SPConfig.h | 12 + shibsp/attribute/filtering/AttributeFilter.h | 66 ++++ shibsp/attribute/filtering/BasicFilteringContext.h | 70 ++++ shibsp/attribute/filtering/FilteringContext.h | 83 +++++ shibsp/attribute/filtering/MatchFunctor.h | 73 +++++ .../attribute/filtering/impl/AttributeFilter.cpp | 40 +++ .../filtering/impl/ChainingAttributeFilter.cpp | 92 ++++++ shibsp/attribute/filtering/impl/MatchFunctor.cpp | 38 +++ .../filtering/impl/XMLAttributeFilter.cpp | 356 +++++++++++++++++++++ .../resolver/impl/QueryAttributeResolver.cpp | 20 ++ shibsp/handler/impl/SAML1Consumer.cpp | 17 + shibsp/handler/impl/SAML2Consumer.cpp | 17 + shibsp/impl/XMLServiceProvider.cpp | 20 +- shibsp/shibsp.vcproj | 44 +++ shibsp/util/SPConstants.cpp | 6 + shibsp/util/SPConstants.h | 3 + 21 files changed, 1034 insertions(+), 32 deletions(-) create mode 100644 shibsp/attribute/filtering/AttributeFilter.h create mode 100644 shibsp/attribute/filtering/BasicFilteringContext.h create mode 100644 shibsp/attribute/filtering/FilteringContext.h create mode 100644 shibsp/attribute/filtering/MatchFunctor.h create mode 100644 shibsp/attribute/filtering/impl/AttributeFilter.cpp create mode 100644 shibsp/attribute/filtering/impl/ChainingAttributeFilter.cpp create mode 100644 shibsp/attribute/filtering/impl/MatchFunctor.cpp create mode 100644 shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp diff --git a/.cdtproject b/.cdtproject index 10a5b0a..78aae40 100644 --- a/.cdtproject +++ b/.cdtproject @@ -8,8 +8,10 @@ - - + + + + diff --git a/schemas/shibboleth-2.0-afp.xsd b/schemas/shibboleth-2.0-afp.xsd index 9d3ad12..857729e 100644 --- a/schemas/shibboleth-2.0-afp.xsd +++ b/schemas/shibboleth-2.0-afp.xsd @@ -18,7 +18,7 @@ - + @@ -28,20 +28,20 @@ - + - Defines an attribute rule that may be reused across multiple filter policies. + Defines an attribute value filter that may be reused across multiple attribute rules. - - + + - Defines an attribute value filter that may be reused across multiple attribtue rules. + Defines an attribute rule that may be reused across multiple filter policies. - + @@ -73,7 +73,7 @@ - + @@ -119,7 +119,7 @@ - + @@ -161,11 +161,11 @@ - + - + An ID, unique within the policy and component type. diff --git a/shibsp/Application.h b/shibsp/Application.h index 1ad17bd..a60d5b8 100644 --- a/shibsp/Application.h +++ b/shibsp/Application.h @@ -31,6 +31,7 @@ namespace shibsp { class SHIBSP_API AttributeExtractor; + class SHIBSP_API AttributeFilter; class SHIBSP_API AttributeResolver; class SHIBSP_API Handler; class SHIBSP_API ServiceProvider; @@ -101,6 +102,13 @@ namespace shibsp { virtual AttributeExtractor* getAttributeExtractor() const=0; /** + * Returns an AttributeFilter for use with this Application. + * + * @return an AttributeFilter, or NULL + */ + virtual AttributeFilter* getAttributeFilter() const=0; + + /** * Returns an AttributeResolver for use with this Application. * * @return an AttributeResolver, or NULL diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am index 5795bef..61b152d 100644 --- a/shibsp/Makefile.am +++ b/shibsp/Makefile.am @@ -45,6 +45,12 @@ attrinclude_HEADERS = \ attribute/ScopedAttribute.h \ attribute/SimpleAttribute.h +attrfiltinclude_HEADERS = \ + attribute/filtering/AttributeFilter.h \ + attribute/filtering/BasicFilteringContext.h \ + attribute/filtering/FilteringContext.h \ + attribute/filtering/MatchFunctor.h + attrresinclude_HEADERS = \ attribute/resolver/AttributeExtractor.h \ attribute/resolver/AttributeResolver.h \ @@ -92,6 +98,10 @@ libshibsp_la_SOURCES = \ attribute/NameIDAttributeDecoder.cpp \ attribute/ScopedAttributeDecoder.cpp \ attribute/StringAttributeDecoder.cpp \ + attribute/filtering/AttributeFilter.cpp \ + attribute/filtering/ChainingAttributeFilter.cpp \ + attribute/filtering/MatchFunctor.cpp \ + attribute/filtering/XMLAttributeFilter.cpp \ attribute/resolver/impl/ChainingAttributeResolver.cpp \ attribute/resolver/impl/QueryAttributeResolver.cpp \ attribute/resolver/impl/XMLAttributeExtractor.cpp \ diff --git a/shibsp/SPConfig.cpp b/shibsp/SPConfig.cpp index e01a617..ff26b4a 100644 --- a/shibsp/SPConfig.cpp +++ b/shibsp/SPConfig.cpp @@ -29,6 +29,8 @@ #include "SessionCache.h" #include "SPConfig.h" #include "attribute/AttributeDecoder.h" +#include "attribute/filtering/AttributeFilter.h" +#include "attribute/filtering/MatchFunctor.h" #include "attribute/resolver/AttributeExtractor.h" #include "attribute/resolver/AttributeResolver.h" #include "binding/ArtifactResolver.h" @@ -108,21 +110,35 @@ bool SPInternalConfig::init(const char* catalog_path) REGISTER_XMLTOOLING_EXCEPTION_FACTORY(ConfigurationException,shibsp); REGISTER_XMLTOOLING_EXCEPTION_FACTORY(ListenerException,shibsp); - registerMetadataExtClasses(); - registerPKIXTrustEngine(); + if (isEnabled(Metadata)) + registerMetadataExtClasses(); + if (isEnabled(Trust)) + registerPKIXTrustEngine(); - registerAccessControls(); - registerAttributeDecoders(); - registerAttributeExtractors(); registerAttributeFactories(); - registerAttributeResolvers(); registerHandlers(); registerSessionInitiators(); - registerListenerServices(); - registerRequestMappers(); - registerSessionCaches(); registerServiceProviders(); + if (isEnabled(AttributeResolution)) { + registerAttributeDecoders(); + registerAttributeExtractors(); + registerAttributeFilters(); + registerAttributeResolvers(); + registerMatchFunctors(); + } + + if (isEnabled(Listener)) + registerListenerServices(); + + if (isEnabled(RequestMapping)) { + registerAccessControls(); + registerRequestMappers(); + } + + if (isEnabled(Caching)) + registerSessionCaches(); + if (isEnabled(OutOfProcess)) m_artifactResolver = new ArtifactResolver(); @@ -145,17 +161,28 @@ void SPInternalConfig::term() ManageNameIDServiceManager.deregisterFactories(); SessionInitiatorManager.deregisterFactories(); SingleLogoutServiceManager.deregisterFactories(); - - ServiceProviderManager.deregisterFactories(); - SessionCacheManager.deregisterFactories(); - RequestMapperManager.deregisterFactories(); - ListenerServiceManager.deregisterFactories(); HandlerManager.deregisterFactories(); - AttributeDecoderManager.deregisterFactories(); - AttributeExtractorManager.deregisterFactories(); - AttributeResolverManager.deregisterFactories(); + ServiceProviderManager.deregisterFactories(); Attribute::deregisterFactories(); - AccessControlManager.deregisterFactories(); + + if (isEnabled(AttributeResolution)) { + MatchFunctorManager.deregisterFactories(); + AttributeDecoderManager.deregisterFactories(); + AttributeFilterManager.deregisterFactories(); + AttributeExtractorManager.deregisterFactories(); + AttributeResolverManager.deregisterFactories(); + } + + if (isEnabled(Listener)) + ListenerServiceManager.deregisterFactories(); + + if (isEnabled(RequestMapping)) { + AccessControlManager.deregisterFactories(); + RequestMapperManager.deregisterFactories(); + } + + if (isEnabled(Caching)) + SessionCacheManager.deregisterFactories(); SAMLConfig::getConfig().term(); log.info("library shutdown complete"); diff --git a/shibsp/SPConfig.h b/shibsp/SPConfig.h index 4abaaa5..4e62b8c 100644 --- a/shibsp/SPConfig.h +++ b/shibsp/SPConfig.h @@ -37,9 +37,11 @@ namespace shibsp { class SHIBSP_API AccessControl; class SHIBSP_API AttributeDecoder; class SHIBSP_API AttributeExtractor; + class SHIBSP_API AttributeFilter; class SHIBSP_API AttributeResolver; class SHIBSP_API Handler; class SHIBSP_API ListenerService; + class SHIBSP_API MatchFunctor; class SHIBSP_API RequestMapper; class SHIBSP_API ServiceProvider; class SHIBSP_API SessionCache; @@ -179,6 +181,11 @@ namespace shibsp { xmltooling::PluginManager AttributeExtractorManager; /** + * Manages factories for AttributeFilter plugins. + */ + xmltooling::PluginManager AttributeFilterManager; + + /** * Manages factories for AttributeResolver plugins. */ xmltooling::PluginManager AttributeResolverManager; @@ -199,6 +206,11 @@ namespace shibsp { xmltooling::PluginManager ListenerServiceManager; /** + * Manages factories for MatchFunctor plugins. + */ + xmltooling::PluginManager MatchFunctorManager; + + /** * Manages factories for Handler plugins that implement ManageNameIDService functionality. */ xmltooling::PluginManager< Handler,std::string,std::pair > ManageNameIDServiceManager; diff --git a/shibsp/attribute/filtering/AttributeFilter.h b/shibsp/attribute/filtering/AttributeFilter.h new file mode 100644 index 0000000..7eeb63d --- /dev/null +++ b/shibsp/attribute/filtering/AttributeFilter.h @@ -0,0 +1,66 @@ +/* + * Copyright 2001-2007 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file shibsp/attribute/filtering/AttributeFilter.h + * + * Engine for filtering attribute values. + */ + +#ifndef __shibsp_attrfilt_h__ +#define __shibsp_attrfilt_h__ + +#include + +#include + +namespace shibsp { + + /** + * Engine for filtering attribute values. + */ + class SHIBSP_API AttributeFilter : public virtual xmltooling::Lockable + { + MAKE_NONCOPYABLE(AttributeFilter); + protected: + AttributeFilter() {} + public: + virtual ~AttributeFilter() {} + + /** + * Filters values out of a set of attributes. + * + * @param context a FilteringContext interface + * @param attributes a mutable map containing the attributes to filter + * + * @throws AttributeFileringException thrown if there is a problem filtering attributes + */ + virtual void filterAttributes(const FilteringContext& context, std::multimap& attributes) const=0; + }; + + /** + * Registers AttributeFilter classes into the runtime. + */ + void SHIBSP_API registerAttributeFilters(); + + /** AttributeFilter based on an XML mapping schema. */ + #define XML_ATTRIBUTE_FILTER "XML" + + /** AttributeFilter based on chaining together other filters. */ + #define CHAINING_ATTRIBUTE_FILTER "Chaining" +}; + +#endif /* __shibsp_attrfilt_h__ */ diff --git a/shibsp/attribute/filtering/BasicFilteringContext.h b/shibsp/attribute/filtering/BasicFilteringContext.h new file mode 100644 index 0000000..c671b4e --- /dev/null +++ b/shibsp/attribute/filtering/BasicFilteringContext.h @@ -0,0 +1,70 @@ +/* + * Copyright 2001-2007 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file shibsp/attribute/filtering/BasicFilteringContext.h + * + * A trivial FilteringContext implementation. + */ + +#ifndef __shibsp_basicfiltctx_h__ +#define __shibsp_basicfiltctx_h__ + +#include + +namespace shibsp { + + class SHIBSP_API BasicFilteringContext : public FilteringContext + { + public: + /** + * Constructor. + * + * @param app reference to Application + * @param role metadata role of attribute issuer, if any + */ + BasicFilteringContext(const Application& app, const opensaml::saml2md::RoleDescriptor* role) + : m_app(app), m_role(role), m_issuer(NULL) { + if (role) + m_issuer = dynamic_cast(role->getParent())->getEntityID(); + } + + virtual ~BasicFilteringContext() {} + + const Application& getApplication() const { + return m_app; + } + const XMLCh* getAttributeRequester() const { + return m_app.getXMLString("entityID").second; + } + const XMLCh* getAttributeIssuer() const { + return m_issuer; + } + const opensaml::saml2md::RoleDescriptor* getAttributeRequesterMetadata() const { + return NULL; + } + const opensaml::saml2md::RoleDescriptor* getAttributeIssuerMetadata() const { + return m_role; + } + + private: + const Application& m_app; + const opensaml::saml2md::RoleDescriptor* m_role; + const XMLCh* m_issuer; + }; +}; + +#endif /* __shibsp_basicfiltctx_h__ */ diff --git a/shibsp/attribute/filtering/FilteringContext.h b/shibsp/attribute/filtering/FilteringContext.h new file mode 100644 index 0000000..34e29cd --- /dev/null +++ b/shibsp/attribute/filtering/FilteringContext.h @@ -0,0 +1,83 @@ +/* + * Copyright 2001-2007 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file shibsp/attribute/filtering/FilteringContext.h + * + * Context for attribute filtering operations. + */ + +#ifndef __shibsp_filtctx_h__ +#define __shibsp_filtctx_h__ + +#include + +#include + +namespace shibsp { + + class SHIBSP_API Application; + class SHIBSP_API Attribute; + + /** + * Context for attribute filtering operations. + */ + class SHIBSP_API FilteringContext + { + MAKE_NONCOPYABLE(FilteringContext); + protected: + FilteringContext() {} + public: + virtual ~FilteringContext() {} + + /** + * Gets the Application doing the filtering. + * + * @return reference to an Application + */ + virtual const Application& getApplication() const=0; + + /** + * Gets the ID of the requester of the attributes, if known. + * + * @return requester of the attributes, or NULL + */ + virtual const XMLCh* getAttributeRequester() const=0; + + /** + * Gets the ID of the issuer of the attributes, if known. + * + * @return ID of the issuer of the attributes, or NULL + */ + virtual const XMLCh* getAttributeIssuer() const=0; + + /** + * Gets the SAML metadata for the attribute requesting role, if available. + * + * @return SAML metadata for the attribute requesting role, or NULL + */ + virtual const opensaml::saml2md::RoleDescriptor* getAttributeRequesterMetadata() const=0; + + /** + * Gets the SAML metadata for the attribute issuing role, if available. + * + * @return SAML metadata for the attribute issuing role, or NULL + */ + virtual const opensaml::saml2md::RoleDescriptor* getAttributeIssuerMetadata() const=0; + }; +}; + +#endif /* __shibsp_filtctx_h__ */ diff --git a/shibsp/attribute/filtering/MatchFunctor.h b/shibsp/attribute/filtering/MatchFunctor.h new file mode 100644 index 0000000..373aa01 --- /dev/null +++ b/shibsp/attribute/filtering/MatchFunctor.h @@ -0,0 +1,73 @@ +/* + * Copyright 2001-2007 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file shibsp/attribute/filtering/MatchFunctor.h + * + * A function that evaluates whether an expressed criteria is met by the current filter context. + */ + +#ifndef __shibsp_matchfunc_h__ +#define __shibsp_matchfunc_h__ + +#include + +namespace shibsp { + + class SHIBSP_API Attribute; + class SHIBSP_API FilteringContext; + + /** + * A function that evaluates whether an expressed criteria is met by the current filter context. + */ + class SHIBSP_API MatchFunctor + { + MAKE_NONCOPYABLE(MatchFunctor); + protected: + MatchFunctor() {} + public: + virtual ~MatchFunctor() {} + + /** + * Evaluates this matching criteria. This evaluation is used when a filtering engine determines policy + * applicability. + * + * @param filterContext current filtering context + * @return true if the criteria for this matching function are met + * @throws AttributeFilteringException thrown if the function can not be evaluated + */ + virtual bool evaluatePolicyRequirement(const FilteringContext& filterContext) const=0; + + /** + * Evaluates this matching criteria. This evaluation is used when a filtering engine is filtering attribute + * values. + * + * @param filterContext the current filtering context + * @param attribute the attribute being evaluated + * @param index the index of the attribute value being evaluated + * @return true if the criteria for this matching function are met + * @throws AttributeFilteringException thrown if the function can not be evaluated + */ + virtual bool evaluatePermitValue(const FilteringContext& filterContext, const Attribute& attribute, size_t index) const=0; + }; + + /** + * Registers MatchFunctor classes into the runtime. + */ + void SHIBSP_API registerMatchFunctors(); +}; + +#endif /* __shibsp_matchfunc_h__ */ diff --git a/shibsp/attribute/filtering/impl/AttributeFilter.cpp b/shibsp/attribute/filtering/impl/AttributeFilter.cpp new file mode 100644 index 0000000..ad21399 --- /dev/null +++ b/shibsp/attribute/filtering/impl/AttributeFilter.cpp @@ -0,0 +1,40 @@ +/* + * 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. + */ + +/** + * AttributeFilter.cpp + * + * Engine for filtering attribute values. + */ + +#include "internal.h" +#include "attribute/filtering/AttributeFilter.h" + +using namespace shibsp; +using namespace xmltooling; +using namespace std; + +namespace shibsp { + SHIBSP_DLLLOCAL PluginManager::Factory XMLAttributeFilterFactory; + SHIBSP_DLLLOCAL PluginManager::Factory ChainingAttributeFilterFactory; +}; + +void SHIBSP_API shibsp::registerAttributeFilters() +{ + SPConfig& conf = SPConfig::getConfig(); + conf.AttributeFilterManager.registerFactory(XML_ATTRIBUTE_FILTER, XMLAttributeFilterFactory); + conf.AttributeFilterManager.registerFactory(CHAINING_ATTRIBUTE_FILTER, ChainingAttributeFilterFactory); +} diff --git a/shibsp/attribute/filtering/impl/ChainingAttributeFilter.cpp b/shibsp/attribute/filtering/impl/ChainingAttributeFilter.cpp new file mode 100644 index 0000000..3069ffd --- /dev/null +++ b/shibsp/attribute/filtering/impl/ChainingAttributeFilter.cpp @@ -0,0 +1,92 @@ +/* + * 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. + */ + +/** + * ChainingAttributeFilter.cpp + * + * Chains together multiple AttributeFilter plugins. + */ + +#include "internal.h" +#include "attribute/filtering/AttributeFilter.h" +#include "attribute/filtering/FilteringContext.h" + +#include +#include +#include + +using namespace shibsp; +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +namespace shibsp { + + class SHIBSP_DLLLOCAL ChainingAttributeFilter : public AttributeFilter + { + public: + ChainingAttributeFilter(const DOMElement* e); + virtual ~ChainingAttributeFilter() { + for_each(m_filters.begin(), m_filters.end(), xmltooling::cleanup()); + } + + Lockable* lock() { + return this; + } + void unlock() { + } + + void filterAttributes(const FilteringContext& context, multimap& attributes) const { + for (vector::const_iterator i=m_filters.begin(); i!=m_filters.end(); ++i) { + Locker locker(*i); + (*i)->filterAttributes(context, attributes); + } + } + + private: + vector m_filters; + }; + + static const XMLCh _AttributeFilter[] = UNICODE_LITERAL_15(A,t,t,r,i,b,u,t,e,F,i,l,t,e,r); + static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e); + + AttributeFilter* SHIBSP_DLLLOCAL ChainingAttributeFilterFactory(const DOMElement* const & e) + { + return new ChainingAttributeFilter(e); + } +}; + +ChainingAttributeFilter::ChainingAttributeFilter(const DOMElement* e) +{ + SPConfig& conf = SPConfig::getConfig(); + + // Load up the chain of handlers. + e = e ? XMLHelper::getFirstChildElement(e, _AttributeFilter) : NULL; + while (e) { + auto_ptr_char type(e->getAttributeNS(NULL,_type)); + if (type.get() && *(type.get())) { + try { + m_filters.push_back(conf.AttributeFilterManager.newPlugin(type.get(),e)); + } + catch (exception& ex) { + Category::getInstance(SHIBSP_LOGCAT".AttributeFilter").error( + "caught exception processing embedded AttributeFilter element: %s", ex.what() + ); + } + } + e = XMLHelper::getNextSiblingElement(e, _AttributeFilter); + } +} diff --git a/shibsp/attribute/filtering/impl/MatchFunctor.cpp b/shibsp/attribute/filtering/impl/MatchFunctor.cpp new file mode 100644 index 0000000..2f81f89 --- /dev/null +++ b/shibsp/attribute/filtering/impl/MatchFunctor.cpp @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/** + * MatchFunctor.cpp + * + * A function that evaluates whether an expressed criteria is met by the current filter context. + */ + +#include "internal.h" +#include "attribute/filtering/MatchFunctor.h" + +using namespace shibsp; +using namespace xmltooling; +using namespace std; + +namespace shibsp { + //SHIBSP_DLLLOCAL PluginManager::Factory FunctorFactory; +}; + +void SHIBSP_API shibsp::registerMatchFunctors() +{ + SPConfig& conf = SPConfig::getConfig(); + //conf.MatchFunctorManager.registerFactory("", FunctorFactory); +} diff --git a/shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp b/shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp new file mode 100644 index 0000000..3b8fe52 --- /dev/null +++ b/shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp @@ -0,0 +1,356 @@ +/* + * 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. + */ + +/** + * XMLAttributeFilter.cpp + * + * AttributeFilter based on an XML policy language. + */ + +#include "internal.h" +#include "Application.h" +#include "ServiceProvider.h" +#include "attribute/Attribute.h" +#include "attribute/filtering/AttributeFilter.h" +#include "attribute/filtering/MatchFunctor.h" +#include "util/SPConstants.h" + +#include +#include +#include +#include + +using namespace shibsp; +using namespace opensaml::saml2md; +using namespace opensaml; +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +namespace shibsp { + +#if defined (_MSC_VER) + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + struct SHIBSP_DLLLOCAL Policy + { + Policy() : m_applies(NULL) {} + const MatchFunctor* m_applies; + typedef multimap rules_t; + rules_t m_rules; + }; + + class SHIBSP_DLLLOCAL XMLFilterImpl + { + public: + XMLFilterImpl(const DOMElement* e, Category& log); + ~XMLFilterImpl() { + if (m_document) + m_document->release(); + for_each(m_policyReqRules.begin(), m_policyReqRules.end(), cleanup_pair()); + for_each(m_permitValRules.begin(), m_permitValRules.end(), cleanup_pair()); + } + + void setDocument(DOMDocument* doc) { + m_document = doc; + } + + void filterAttributes(const FilteringContext& context, multimap& attributes) const; + + private: + MatchFunctor* buildFunctor( + const DOMElement* e, multimap& functorMap, const char* logname, bool standalone + ); + pair buildAttributeRule(const DOMElement* e, bool standalone); + + Category& m_log; + DOMDocument* m_document; + vector m_policies; + map< string,pair > m_attrRules; + multimap m_policyReqRules; + multimap m_permitValRules; + }; + + class SHIBSP_DLLLOCAL XMLFilter : public AttributeFilter, public ReloadableXMLFile + { + public: + XMLFilter(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".AttributeFilter")), m_impl(NULL) { + load(); + } + ~XMLFilter() { + delete m_impl; + } + + void filterAttributes(const FilteringContext& context, multimap& attributes) const { + m_impl->filterAttributes(context, attributes); + } + + protected: + pair load(); + + private: + XMLFilterImpl* m_impl; + }; + +#if defined (_MSC_VER) + #pragma warning( pop ) +#endif + + AttributeFilter* SHIBSP_DLLLOCAL XMLAttributeFilterFactory(const DOMElement* const & e) + { + return new XMLFilter(e); + } + + static const XMLCh AttributeFilterPolicyGroup[] = UNICODE_LITERAL_26(A,t,t,r,i,b,u,t,e,F,i,l,t,e,r,P,o,l,i,c,y,G,r,o,u,p); + static const XMLCh AttributeFilterPolicy[] = UNICODE_LITERAL_21(A,t,t,r,i,b,u,t,e,F,i,l,t,e,r,P,o,l,i,c,y); + static const XMLCh AttributeRule[] = UNICODE_LITERAL_13(A,t,t,r,i,b,u,t,e,R,u,l,e); + static const XMLCh AttributeRuleReference[] = UNICODE_LITERAL_22(A,t,t,r,i,b,u,t,e,R,u,l,e,R,e,f,e,r,e,n,c,e); + static const XMLCh PermitValueRule[] = UNICODE_LITERAL_15(P,e,r,m,i,t,V,a,l,u,e,R,u,l,e); + static const XMLCh PermitValueRuleReference[] = UNICODE_LITERAL_24(P,e,r,m,i,t,V,a,l,u,e,R,u,l,e,R,e,f,e,r,e,n,c,e); + static const XMLCh PolicyRequirementRule[] = UNICODE_LITERAL_21(P,o,l,i,c,y,R,e,q,u,i,r,e,m,e,n,t,R,u,l,e); + static const XMLCh PolicyRequirementRuleReference[]=UNICODE_LITERAL_30(P,o,l,i,c,y,R,e,q,u,i,r,e,m,e,n,t,R,u,l,e,R,e,f,e,r,e,n,c,e); + static const XMLCh attributeId[] = UNICODE_LITERAL_11(a,t,t,r,i,b,u,t,e,I,d); + static const XMLCh _id[] = UNICODE_LITERAL_2(i,d); + static const XMLCh _ref[] = UNICODE_LITERAL_3(r,e,f); +}; + +XMLFilterImpl::XMLFilterImpl(const DOMElement* e, Category& log) : m_log(log), m_document(NULL) +{ +#ifdef _DEBUG + xmltooling::NDC ndc("XMLFilterImpl"); +#endif + + if (!XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeFilterPolicyGroup)) + throw ConfigurationException("XML AttributeFilter requires afp:AttributeFilterPolicyGroup at root of configuration."); + + DOMElement* child = XMLHelper::getFirstChildElement(e); + while (child) { + if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PolicyRequirementRule)) { + buildFunctor(child, m_policyReqRules, "PolicyRequirementRule", true); + } + else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PermitValueRule)) { + buildFunctor(child, m_permitValRules, "PermitValueRule", true); + } + else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeRule)) { + buildAttributeRule(child, true); + } + else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeFilterPolicy)) { + e = XMLHelper::getFirstChildElement(child); + MatchFunctor* func; + if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PolicyRequirementRule)) { + func = buildFunctor(e, m_policyReqRules, "PolicyRequirementRule", false); + } + else if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PolicyRequirementRuleReference)) { + auto_ptr_char ref(e->getAttributeNS(NULL, _ref)); + if (ref.get() && *ref.get()) { + multimap::const_iterator prr = m_policyReqRules.find(ref.get()); + func = (prr!=m_policyReqRules.end()) ? prr->second : NULL; + } + } + if (func) { + m_policies.push_back(Policy()); + m_policies.back().m_applies = func; + e = XMLHelper::getNextSiblingElement(e); + while (e) { + if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeRule)) { + pair rule = buildAttributeRule(e, false); + if (rule.second) + m_policies.back().m_rules.insert(rule); + } + 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 >::const_iterator ar = m_attrRules.find(ref.get()); + if (ar != m_attrRules.end()) + m_policies.back().m_rules.insert(ar->second); + else + m_log.warn("skipping invalid AttributeRuleReference (%s)", ref.get()); + } + } + e = XMLHelper::getNextSiblingElement(e); + } + } + else { + m_log.warn("skipping AttributeFilterPolicy, PolicyRequirementRule invalid or missing"); + } + } + child = XMLHelper::getNextSiblingElement(child); + } +} + +MatchFunctor* XMLFilterImpl::buildFunctor( + const DOMElement* e, multimap& functorMap, const char* logname, bool standalone + ) +{ + auto_ptr_char temp(e->getAttributeNS(NULL,_id)); + const char* id = (temp.get() && *temp.get()) ? temp.get() : ""; + + if (standalone && !*id) { + m_log.warn("skipping stand-alone %s with no id", logname); + return NULL; + } + else if (*id && functorMap.count(id)) { + if (standalone) { + m_log.warn("skipping duplicate stand-alone %s with id (%s)", logname, id); + return NULL; + } + else + id = ""; + } + + auto_ptr type(XMLHelper::getXSIType(e)); + if (type.get()) { + try { + MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), e); + functorMap.insert(make_pair(id, func)); + return func; + } + catch (exception& ex) { + m_log.error("error building %s with type (%s): %s", logname, type->toString().c_str(), ex.what()); + } + } + else if (standalone) + m_log.warn("skipping stand-alone %s with no xsi:type", logname); + else + m_log.error("%s with no xsi:type", logname); + + return NULL; +} + +pair XMLFilterImpl::buildAttributeRule(const DOMElement* e, bool standalone) +{ + auto_ptr_char temp(e->getAttributeNS(NULL,_id)); + const char* id = (temp.get() && *temp.get()) ? temp.get() : ""; + + if (standalone && !*id) { + m_log.warn("skipping stand-alone AttributeRule with no id"); + return make_pair(string(),(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); + } + else + id = ""; + } + + auto_ptr_char attrId(e->getAttributeNS(NULL,attributeId)); + if (!attrId.get() || !*attrId.get()) + m_log.warn("skipping AttributeRule with no attributeId"); + + e = XMLHelper::getFirstChildElement(e); + MatchFunctor* func=NULL; + if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PermitValueRule)) { + func = buildFunctor(e, m_permitValRules, "PermitValueRule", false); + } + else if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, PermitValueRuleReference)) { + auto_ptr_char ref(e->getAttributeNS(NULL, _ref)); + if (ref.get() && *ref.get()) { + multimap::const_iterator pvr = m_permitValRules.find(ref.get()); + func = (pvr!=m_permitValRules.end()) ? pvr->second : NULL; + } + } + + if (func) { + if (*id) + return m_attrRules[id] = make_pair(attrId.get(), func); + else + return make_pair(attrId.get(), func); + } + + m_log.warn("skipping AttributeRule (%s), PermitValueRule invalid or missing", id); + return make_pair(string(),(MatchFunctor*)NULL); +} + +void XMLFilterImpl::filterAttributes(const FilteringContext& context, multimap& attributes) const +{ + auto_ptr_char issuer(context.getAttributeIssuer()); + + m_log.debug("filtering %lu attribute(s) from (%s)", attributes.size(), issuer.get() ? issuer.get() : "unknown source"); + + if (m_policies.empty()) { + m_log.warn("no filter policies were loaded, filtering out all attributes from (%s)", issuer.get() ? issuer.get() : "unknown source"); + for_each(attributes.begin(), attributes.end(), cleanup_pair()); + attributes.clear(); + return; + } + + size_t count,index; + + // Test each Policy. + for (vector::const_iterator p=m_policies.begin(); p!=m_policies.end(); ++p) { + if (p->m_applies->evaluatePolicyRequirement(context)) { + // Loop over the attributes and look for possible rules to run. + for (multimap::iterator a=attributes.begin(); a!=attributes.end();) { + pair rules = p->m_rules.equal_range(a->second->getId()); + if (rules.first == rules.second) { + // No rule found, so we're filtering it out. + m_log.warn( + "no rule found, filtering out values of attribute (%s) from (%s)", a->second->getId(), issuer.get() ? issuer.get() : "unknown source" + ); + multimap::iterator dead = a++; + delete dead->second; + attributes.erase(dead); + } + else { + // Run each rule in sequence. + m_log.debug("filtering values of attribute (%s) from (%s)", a->second->getId(), issuer.get() ? issuer.get() : "unknown source"); + for (; rules.first!=rules.second; ++rules.first) { + count = a->second->valueCount(); + for (index=0; index < count;) { + // The return value tells us whether to index past the accepted value, or stay put and decrement the count. + if (rules.first->second->evaluatePermitValue(context, *(a->second), index)) + index++; + else + count--; + } + } + // See if any values are left, delete if not. + if (count>0) { + ++a; + } + else { + multimap::iterator dead = a++; + delete dead->second; + attributes.erase(dead); + } + } + } + } + } +} + +pair XMLFilter::load() +{ + // Load from source using base class. + pair raw = ReloadableXMLFile::load(); + + // If we own it, wrap it. + XercesJanitor docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL); + + XMLFilterImpl* impl = new XMLFilterImpl(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); +} diff --git a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp index 33250ed..b873757 100644 --- a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp +++ b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp @@ -25,6 +25,8 @@ #include "ServiceProvider.h" #include "SessionCache.h" #include "attribute/Attribute.h" +#include "attribute/filtering/AttributeFilter.h" +#include "attribute/filtering/BasicFilteringContext.h" #include "attribute/resolver/AttributeExtractor.h" #include "attribute/resolver/AttributeResolver.h" #include "attribute/resolver/ResolutionContext.h" @@ -321,9 +323,18 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const Locker extlocker(extractor); extractor->extractAttributes(ctx.getApplication(), AA, *newtoken, ctx.getResolvedAttributes()); } + + AttributeFilter* filter = ctx.getApplication().getAttributeFilter(); + if (filter) { + BasicFilteringContext fc(ctx.getApplication(), AA); + Locker filtlocker(filter); + filter->filterAttributes(fc, ctx.getResolvedAttributes()); + } } catch (exception& ex) { m_log.error("caught exception extracting/filtering attributes from query result: %s", ex.what()); + for_each(ctx.getResolvedAttributes().begin(), ctx.getResolvedAttributes().end(), cleanup_pair()); + ctx.getResolvedAttributes().clear(); } return true; @@ -423,9 +434,18 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const Locker extlocker(extractor); extractor->extractAttributes(ctx.getApplication(), AA, *newtoken, ctx.getResolvedAttributes()); } + + AttributeFilter* filter = ctx.getApplication().getAttributeFilter(); + if (filter) { + BasicFilteringContext fc(ctx.getApplication(), AA); + Locker filtlocker(filter); + filter->filterAttributes(fc, ctx.getResolvedAttributes()); + } } catch (exception& ex) { m_log.error("caught exception extracting/filtering attributes from query result: %s", ex.what()); + for_each(ctx.getResolvedAttributes().begin(), ctx.getResolvedAttributes().end(), cleanup_pair()); + ctx.getResolvedAttributes().clear(); } return true; diff --git a/shibsp/handler/impl/SAML1Consumer.cpp b/shibsp/handler/impl/SAML1Consumer.cpp index c7caeb9..7ef4eb7 100644 --- a/shibsp/handler/impl/SAML1Consumer.cpp +++ b/shibsp/handler/impl/SAML1Consumer.cpp @@ -26,6 +26,8 @@ #include "ServiceProvider.h" #include "SessionCache.h" #include "attribute/Attribute.h" +#include "attribute/filtering/AttributeFilter.h" +#include "attribute/filtering/BasicFilteringContext.h" #include "attribute/resolver/AttributeExtractor.h" #include "attribute/resolver/ResolutionContext.h" #include "handler/AssertionConsumerService.h" @@ -209,6 +211,21 @@ string SAML1Consumer::implementProtocol( m_log.error("caught exception extracting attributes: %s", ex.what()); } } + + AttributeFilter* filter = application.getAttributeFilter(); + if (filter && !resolvedAttributes.empty()) { + BasicFilteringContext fc(application, policy.getIssuerMetadata()); + Locker filtlocker(filter); + try { + filter->filterAttributes(fc, resolvedAttributes); + } + catch (exception& ex) { + m_log.error("caught exception filtering attributes: %s", ex.what()); + m_log.error("dumping extracted attributes due to filtering exception"); + for_each(resolvedAttributes.begin(), resolvedAttributes.end(), cleanup_pair()); + resolvedAttributes.clear(); + } + } } // First, normalize the SAML 1.x NameIdentifier... diff --git a/shibsp/handler/impl/SAML2Consumer.cpp b/shibsp/handler/impl/SAML2Consumer.cpp index d1a0f27..fac1fc0 100644 --- a/shibsp/handler/impl/SAML2Consumer.cpp +++ b/shibsp/handler/impl/SAML2Consumer.cpp @@ -26,6 +26,8 @@ #include "ServiceProvider.h" #include "SessionCache.h" #include "attribute/Attribute.h" +#include "attribute/filtering/AttributeFilter.h" +#include "attribute/filtering/BasicFilteringContext.h" #include "attribute/resolver/AttributeExtractor.h" #include "attribute/resolver/ResolutionContext.h" #include "handler/AssertionConsumerService.h" @@ -333,6 +335,21 @@ string SAML2Consumer::implementProtocol( } } + AttributeFilter* filter = application.getAttributeFilter(); + if (filter && !resolvedAttributes.empty()) { + BasicFilteringContext fc(application, policy.getIssuerMetadata()); + Locker filtlocker(filter); + try { + filter->filterAttributes(fc, resolvedAttributes); + } + catch (exception& ex) { + m_log.error("caught exception filtering attributes: %s", ex.what()); + m_log.error("dumping extracted attributes due to filtering exception"); + for_each(resolvedAttributes.begin(), resolvedAttributes.end(), cleanup_pair()); + resolvedAttributes.clear(); + } + } + try { const EntityDescriptor* issuerMetadata = policy.getIssuerMetadata() ? dynamic_cast(policy.getIssuerMetadata()->getParent()) : NULL; diff --git a/shibsp/impl/XMLServiceProvider.cpp b/shibsp/impl/XMLServiceProvider.cpp index 2d3fa74..d24a5d7 100644 --- a/shibsp/impl/XMLServiceProvider.cpp +++ b/shibsp/impl/XMLServiceProvider.cpp @@ -30,6 +30,7 @@ #include "SPConfig.h" #include "SPRequest.h" #include "TransactionLog.h" +#include "attribute/filtering/AttributeFilter.h" #include "attribute/resolver/AttributeExtractor.h" #include "attribute/resolver/AttributeResolver.h" #include "handler/SessionInitiator.h" @@ -90,6 +91,9 @@ namespace { AttributeExtractor* getAttributeExtractor() const { return (!m_attrExtractor && m_base) ? m_base->getAttributeExtractor() : m_attrExtractor; } + AttributeFilter* getAttributeFilter() const { + return (!m_attrFilter && m_base) ? m_base->getAttributeFilter() : m_attrFilter; + } AttributeResolver* getAttributeResolver() const { return (!m_attrResolver && m_base) ? m_base->getAttributeResolver() : m_attrResolver; } @@ -120,6 +124,7 @@ namespace { MetadataProvider* m_metadata; TrustEngine* m_trust; AttributeExtractor* m_attrExtractor; + AttributeFilter* m_attrFilter; AttributeResolver* m_attrResolver; CredentialResolver* m_credResolver; vector m_audiences; @@ -341,7 +346,7 @@ XMLApplication::XMLApplication( const ServiceProvider* sp, const DOMElement* e, const XMLApplication* base - ) : m_sp(sp), m_base(base), m_metadata(NULL), m_trust(NULL), m_attrExtractor(NULL), m_attrResolver(NULL), + ) : m_sp(sp), m_base(base), m_metadata(NULL), m_trust(NULL), m_attrExtractor(NULL), m_attrFilter(NULL), m_attrResolver(NULL), m_credResolver(NULL), m_partyDefault(NULL), m_sessionInitDefault(NULL), m_acsDefault(NULL) { #ifdef _DEBUG @@ -522,6 +527,18 @@ XMLApplication::XMLApplication( } } + child = XMLHelper::getFirstChildElement(e,_AttributeFilter); + if (child) { + auto_ptr_char type(child->getAttributeNS(NULL,_type)); + log.info("building AttributeFilter of type %s...",type.get()); + try { + m_attrFilter = conf.AttributeFilterManager.newPlugin(type.get(),child); + } + catch (exception& ex) { + log.crit("error building AttributeFilter: %s", ex.what()); + } + } + child = XMLHelper::getFirstChildElement(e,_AttributeResolver); if (child) { auto_ptr_char type(child->getAttributeNS(NULL,_type)); @@ -592,6 +609,7 @@ void XMLApplication::cleanup() for_each(m_handlers.begin(),m_handlers.end(),xmltooling::cleanup()); delete m_credResolver; delete m_attrResolver; + delete m_attrFilter; delete m_attrExtractor; delete m_trust; delete m_metadata; diff --git a/shibsp/shibsp.vcproj b/shibsp/shibsp.vcproj index 8fc3389..74398a7 100644 --- a/shibsp/shibsp.vcproj +++ b/shibsp/shibsp.vcproj @@ -191,6 +191,18 @@ > + + + + + + @@ -202,6 +214,10 @@ RelativePath=".\SPConfig.cpp" > + + @@ -333,6 +349,14 @@ + + + + + + @@ -433,6 +461,10 @@ > + + @@ -555,6 +587,18 @@ > + + + + + +