Fixed attribute-based functors.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 14 May 2007 00:29:49 +0000 (00:29 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 14 May 2007 00:29:49 +0000 (00:29 +0000)
Implemented special handling of REMOTE_USER.
Added header clearing method to resolver/extractor.
Added default policy file.

git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2247 cb58f699-b61c-0410-a6fe-9272a202ed29

21 files changed:
configs/Makefile.am
configs/attribute-map.xml.in
configs/attribute-policy.xml.in [new file with mode: 0644]
configs/shibboleth.xml.in
schemas/shibboleth-2.0-afp-mf-basic.xsd
schemas/shibboleth-2.0-afp.xsd
schemas/shibboleth-2.0-attribute-map.xsd
schemas/shibboleth-2.0-native-sp-config.xsd
shibsp/Application.h
shibsp/ServiceProvider.cpp
shibsp/attribute/filtering/impl/AttributeScopeMatchesShibMDScopeFunctor.cpp
shibsp/attribute/filtering/impl/AttributeScopeRegexFunctor.cpp
shibsp/attribute/filtering/impl/AttributeScopeStringFunctor.cpp
shibsp/attribute/filtering/impl/AttributeValueRegexFunctor.cpp
shibsp/attribute/filtering/impl/AttributeValueStringFunctor.cpp
shibsp/attribute/resolver/AttributeExtractor.h
shibsp/attribute/resolver/AttributeResolver.h
shibsp/attribute/resolver/impl/ChainingAttributeResolver.cpp
shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp
shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp
shibsp/impl/XMLServiceProvider.cpp

index 8198bb5..0c5838f 100644 (file)
@@ -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 \
index c683112..e47d1b5 100644 (file)
@@ -4,10 +4,10 @@
 
        <!-- First some useful eduPerson attributes that many sites might use. -->
        
-       <Attribute name="urn:mace:dir:attribute-def:eduPersonPrincipalName" id="REMOTE_USER">
+       <Attribute name="urn:mace:dir:attribute-def:eduPersonPrincipalName" id="eppn">
                <AttributeDecoder xsi:type="ScopedAttributeDecoder"/>
        </Attribute>
-       <Attribute name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" id="REMOTE_USER">
+       <Attribute name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" id="eppn">
                <AttributeDecoder xsi:type="ScopedAttributeDecoder"/>
        </Attribute>
        
        <!-- A persistent id attribute that supports personalized anonymous access. -->
        
        <!-- First, the deprecated version: -->
-       <Attribute name="urn:mace:dir:attribute-def:eduPersonTargetedID" id="REMOTE_USER">
+       <Attribute name="urn:mace:dir:attribute-def:eduPersonTargetedID" id="targeted-id">
                <AttributeDecoder xsi:type="ScopedAttributeDecoder"/>
        </Attribute>
 
        <!-- Second, the new version (note the OID-style name): -->
-       <Attribute name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" id="REMOTE_USER">
+       <Attribute name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" id="persistent-id">
                <AttributeDecoder xsi:type="NameIDAttributeDecoder" formatter="$Name!!$NameQualifier!!$SPNameQualifier"/>
        </Attribute>
 
        <!-- Third, the SAML 2.0 NameID Format: -->
-       <Attribute name="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" id="REMOTE_USER">
+       <Attribute name="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" id="persistent-id">
                <AttributeDecoder xsi:type="NameIDAttributeDecoder" formatter="$Name!!$NameQualifier!!$SPNameQualifier"/>
        </Attribute>
        
diff --git a/configs/attribute-policy.xml.in b/configs/attribute-policy.xml.in
new file mode 100644 (file)
index 0000000..c04d18f
--- /dev/null
@@ -0,0 +1,63 @@
+<afp:AttributeFilterPolicyGroup
+    xmlns="urn:mace:shibboleth:2.0:afp:mf:basic"
+    xmlns:basic="urn:mace:shibboleth:2.0:afp:mf:basic"
+    xmlns:afp="urn:mace:shibboleth:2.0:afp"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:afp @-PKGXMLDIR-@/shibboleth-2.0-afp.xsd urn:mace:shibboleth:2.0:afp:mf:basic @-PKGXMLDIR-@/shibboleth-2.0-afp-mf-basic.xsd urn:mace:shibboleth:2.0:afp:mf:saml @-PKGXMLDIR-@/shibboleth-2.0-afp-mf-saml.xsd">
+
+    <!-- Shared rule for affiliation values. -->
+    <afp:PermitValueRule id="eduPersonAffiliationValues" xsi:type="OR">
+        <Rule xsi:type="AttributeValueString" value="faculty"/>
+        <Rule xsi:type="AttributeValueString" value="student"/>
+        <Rule xsi:type="AttributeValueString" value="staff"/>
+        <Rule xsi:type="AttributeValueString" value="alum"/>
+        <Rule xsi:type="AttributeValueString" value="member"/>
+        <Rule xsi:type="AttributeValueString" value="affiliate"/>
+        <Rule xsi:type="AttributeValueString" value="employee"/>
+    </afp:PermitValueRule>
+    
+    <!--
+    Shared rule for all "scoped" attributes, but you'll have to manually apply it inside
+    an AttributeRule for each attribute you want to check.
+    -->
+    <afp:PermitValueRule id="ScopingRules" xsi:type="AND">
+        <Rule xsi:type="NOT">
+            <Rule xsi:type="AttributeValueRegex" regex="@"/>
+        </Rule>
+        <Rule xsi:type="saml:AttributeScopeMatchesShibMDScope" xmlns:saml="urn:mace:shibboleth:2.0:afp:mf:saml"/>
+    </afp:PermitValueRule>
+
+    <afp:AttributeFilterPolicy>
+        <!-- This policy is in effect in all cases. -->
+        <afp:PolicyRequirementRule xsi:type="ANY"/>
+
+        <!-- Filter out undefined affiliations and ensure only one primary. -->
+        <afp:AttributeRule attributeID="affiliation">
+            <afp:PermitValueRule xsi:type="AND">
+                <RuleReference ref="eduPersonAffiliationValues"/>
+                <RuleReference ref="ScopingRules"/>
+            </afp:PermitValueRule>
+        </afp:AttributeRule>
+        <afp:AttributeRule attributeID="unscoped-affiliation">
+            <afp:PermitValueRuleReference ref="eduPersonAffiliationValues"/>
+        </afp:AttributeRule>
+        <afp:AttributeRule attributeID="primary-affiliation">
+            <afp:PermitValueRuleReference ref="eduPersonAffiliationValues"/>
+        </afp:AttributeRule>
+        
+        <afp:AttributeRule attributeID="eppn">
+            <afp:PermitValueRuleReference ref="ScopingRules"/>
+        </afp:AttributeRule>
+
+        <afp:AttributeRule attributeID="targeted-id">
+            <afp:PermitValueRuleReference ref="ScopingRules"/>
+        </afp:AttributeRule>
+        
+        <!-- Catch-all that passes everything else through unmolested. -->
+        <afp:AttributeRule attributeID="*">
+            <afp:PermitValueRule xsi:type="ANY"/>
+        </afp:AttributeRule>
+        
+    </afp:AttributeFilterPolicy>
+
+</afp:AttributeFilterPolicyGroup>
index 07bd8ec..462d902 100644 (file)
                <!-- Use a SAML query if no attributes are supplied during SSO. -->
                <AttributeResolver type="Query"/>
 
+               <!-- Default filtering policy for recognized attributes, lets other data pass. -->
+               <AttributeFilter type="XML" path="@-PKGSYSCONFDIR-@/attribute-policy.xml"/>
+
                <!-- Simple file-based resolver for key/certificate information. -->
                <CredentialResolver type="File">
                        <Key>
index 90ab2b8..8d4389f 100644 (file)
@@ -1,7 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<schema targetNamespace="urn:mace:shibboleth:2.0:afp:mf:basic" xmlns="http://www.w3.org/2001/XMLSchema"
-    xmlns:basic="urn:mace:shibboleth:2.0:afp:mf:basic" xmlns:afp="urn:mace:shibboleth:2.0:afp">
+<schema targetNamespace="urn:mace:shibboleth:2.0:afp:mf:basic"
+    xmlns="http://www.w3.org/2001/XMLSchema"
+    xmlns:basic="urn:mace:shibboleth:2.0:afp:mf:basic"
+    xmlns:afp="urn:mace:shibboleth:2.0:afp"
+    elementFormDefault="qualified">
 
     <import namespace="urn:mace:shibboleth:2.0:afp" schemaLocation="classpath:/schema/shibboleth-2.0-afp.xsd" />
 
index 857729e..e132a4f 100644 (file)
@@ -1,7 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<schema targetNamespace="urn:mace:shibboleth:2.0:afp" xmlns="http://www.w3.org/2001/XMLSchema"
-    xmlns:afp="urn:mace:shibboleth:2.0:afp" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">\r
+<schema targetNamespace="urn:mace:shibboleth:2.0:afp"
+    xmlns="http://www.w3.org/2001/XMLSchema"
+    xmlns:afp="urn:mace:shibboleth:2.0:afp"
+    xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+    elementFormDefault="qualified">\r
 \r
     <import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="classpath:/schema/xmldsig-core-schema.xsd" />\r
 \r
index caf782c..1e4fb20 100644 (file)
@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <schema targetNamespace="urn:mace:shibboleth:2.0:attribute-map"
         xmlns="http://www.w3.org/2001/XMLSchema"
-        xmlns:am="urn:mace:shibboleth:2.0:attribute-map">
+        xmlns:am="urn:mace:shibboleth:2.0:attribute-map"
+        elementFormDefault="qualified">
 
     <annotation>
         <documentation>
index 3cbd1fe..9f1a0f5 100644 (file)
                <attributeGroup ref="conf:ContentSettings"/>\r
         </complexType>\r
     </element>\r
-\r
+       \r
        <element name="Applications">\r
                <annotation>\r
                        <documentation>Container for global settings and application-specific overrides</documentation>\r
                        <attribute name="entityID" type="anyURI" use="required"/>\r
                        <attribute name="policyId" type="conf:string" use="required"/>\r
                        <attribute name="homeURL" type="anyURI" default="/"/>\r
+                       <attribute name="REMOTE_USER" type="conf:listOfStrings"/>\r
                <anyAttribute namespace="##other" processContents="lax"/>\r
                </complexType>\r
        </element>\r
                        <attribute name="entityID" type="anyURI"/>\r
                        <attribute name="policyId" type="conf:string"/>\r
                        <attribute name="homeURL" type="anyURI" default="/"/>\r
+                       <attribute name="REMOTE_USER" type="conf:listOfStrings"/>\r
                        <anyAttribute namespace="##other" processContents="lax"/>\r
                </complexType>\r
        </element>\r
index 7f67005..28e970c 100644 (file)
@@ -117,6 +117,14 @@ namespace shibsp {
          */
         virtual AttributeResolver* getAttributeResolver() const=0;
 
+        /**\r
+         * Returns a set of attribute IDs to use as a REMOTE_USER value.\r
+         * <p>The first attribute with a value (and only a single value) will be used.\r
+         *\r
+         * @return  a set of attribute IDs, or an empty set\r
+         */\r
+        virtual const std::set<std::string>& getRemoteUserAttributeIds() const=0;
+
         /**
          * Returns the CredentialResolver instance associated with this Application.
          * 
index 30eb28f..af2e8f0 100644 (file)
@@ -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<IAAP*> provs=dynamic_cast<const IApplication&>(getApplication()).getAAPProviders();
-        while (provs.hasNext()) {
-            IAAP* aap=provs.next();
-            xmltooling::Locker locker(aap);
-            Iterator<const IAttributeRule*> 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<bool,long> ServiceProvider::doExport(SPRequest& request, bool requireSessio
         }
 
         // Export the attributes.
+        bool remoteUserSet = false;
         const multimap<string,Attribute*>& attributes = session->getAttributes();
         for (multimap<string,Attribute*>::const_iterator a = attributes.begin(); a!=attributes.end(); ++a) {
             const vector<string>& 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<string>::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<string>::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);
index 5b7b26c..b7899b3 100644 (file)
@@ -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 {
index 6164a76..c3e58e8 100644 (file)
@@ -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);
         }
     };
 
index 70a252f..d9749fc 100644 (file)
@@ -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);
         }
     };
 
index b4a899e..5fc4fa0 100644 (file)
@@ -57,10 +57,10 @@ namespace shibsp {
             try {
                 m_regex = new RegularExpression(r, e->getAttributeNS(NULL,options));
             }
-            catch (XMLException& ex) {\r
-                xmltooling::auto_ptr_char temp(ex.getMessage());\r
-                throw ConfigurationException(temp.get());\r
-            }\r
+            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);
         }
     };
 
index c969ac5..0c2739c 100644 (file)
@@ -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);
         }
     };
 
index 80ffb85..af61e62 100644 (file)
@@ -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<std::string,Attribute*>& 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;
     };
 
     /**
index 2e143b9..4b12938 100644 (file)
@@ -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)
index f2ef837..a2a1154 100644 (file)
@@ -102,6 +102,11 @@ namespace shibsp {
 
         void resolveAttributes(ResolutionContext& ctx) const;
 
+        void clearHeaders(SPRequest& request) const {
+            for (vector<AttributeResolver*>::const_iterator i=m_resolvers.begin(); i!=m_resolvers.end(); ++i)
+                (*i)->clearHeaders(request);
+        }
+
     private:
         vector<AttributeResolver*> m_resolvers;
     };
index 52bb845..c846355 100644 (file)
@@ -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");
 }
index c93d87d..9b73062 100644 (file)
@@ -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<string,Attribute*>& attributes
             ) const;
 
+        void clearHeaders(SPRequest& request) const {
+            for (vector<string>::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<string,string>,pair<AttributeDecoder*,string> > attrmap_t;
 #endif
         attrmap_t m_attrMap;
+        vector<string> m_attributeIds;
     };
     
     class XMLExtractor : public AttributeExtractor, public ReloadableXMLFile
@@ -105,6 +112,11 @@ namespace shibsp {
             const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, multimap<string,Attribute*>& attributes
             ) const;
 
+        void clearHeaders(SPRequest& request) const {
+            if (m_impl)
+                m_impl->clearHeaders(request);
+        }
+
     protected:
         pair<bool,DOMElement*> 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<string,Attribute*>& 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<const saml2::Assertion*>(&xmlObject);
index a3fdf9b..92f7a08 100644 (file)
@@ -101,6 +101,9 @@ namespace {
         AttributeResolver* getAttributeResolver() const {\r
             return (!m_attrResolver && m_base) ? m_base->getAttributeResolver() : m_attrResolver;\r
         }\r
+        const set<string>& getRemoteUserAttributeIds() const {\r
+            return (m_attributeIds.empty() && m_base) ? m_base->getRemoteUserAttributeIds() : m_attributeIds;\r
+        }\r
         CredentialResolver* getCredentialResolver() const {\r
             return (!m_credResolver && m_base) ? m_base->getCredentialResolver() : m_credResolver;\r
         }\r
@@ -132,6 +135,7 @@ namespace {
         AttributeResolver* m_attrResolver;\r
         CredentialResolver* m_credResolver;\r
         vector<const XMLCh*> m_audiences;\r
+        set<string> m_attributeIds;\r
 \r
         // manage handler objects\r
         vector<Handler*> m_handlers;\r
@@ -372,6 +376,25 @@ XMLApplication::XMLApplication(
         m_hash+=getString("entityID").second;\r
         m_hash=samlConf.hashSHA1(m_hash.c_str(), true);\r
 \r
+        pair<bool,const char*> attributes = getString("REMOTE_USER");\r
+        if (attributes.first) {\r
+            char* dup = strdup(attributes.second);\r
+            char* pos;\r
+            char* start = dup;\r
+            while (start && *start) {\r
+                while (*start && isspace(*start))\r
+                    start++;\r
+                if (!*start)\r
+                    break;\r
+                pos = strchr(start,' ');\r
+                if (pos)\r
+                    *pos=0;\r
+                m_attributeIds.insert(start);\r
+                start = pos ? pos+1 : NULL;\r
+            }\r
+            free(dup);\r
+        }\r
+\r
         const PropertySet* sessions = getPropertySet("Sessions");\r
 \r
         // Process handlers.\r