https://issues.shibboleth.net/jira/browse/SSPCPP-320
authorScott Cantor <cantor.2@osu.edu>
Mon, 6 Dec 2010 19:19:39 +0000 (19:19 +0000)
committerScott Cantor <cantor.2@osu.edu>
Mon, 6 Dec 2010 19:19:39 +0000 (19:19 +0000)
configs/attribute-map.xml
configs/attribute-policy.xml
schemas/shibboleth-2.0-afp-mf-saml.xsd
shibsp/Makefile.am
shibsp/attribute/filtering/MatchFunctor.h
shibsp/attribute/filtering/impl/BasicFilteringContext.cpp
shibsp/attribute/filtering/impl/MatchFunctor.cpp
shibsp/attribute/filtering/impl/NameIDQualifierStringFunctor.cpp [new file with mode: 0644]
shibsp/shibsp.vcxproj
shibsp/shibsp.vcxproj.filters

index 4a54e90..dc61901 100644 (file)
 
     <!-- A persistent id attribute that supports personalized anonymous access. -->
     
-    <!-- First, the deprecated version, decoded as a scoped string: -->
+    <!-- First, the deprecated/incorrect version, decoded as a scoped string: -->
     <Attribute name="urn:mace:dir:attribute-def:eduPersonTargetedID" id="targeted-id">
         <AttributeDecoder xsi:type="ScopedAttributeDecoder"/>
         <!-- <AttributeDecoder xsi:type="NameIDFromScopedAttributeDecoder" formatter="$NameQualifier!$SPNameQualifier!$Name" defaultQualifiers="true"/> -->
     </Attribute>
     
-    <!-- Second, an alternate decoder that will turn the deprecated form into the newer form. -->
+    <!-- Second, an alternate decoder that will decode the incorrect form into the newer form. -->
     <!--
     <Attribute name="urn:mace:dir:attribute-def:eduPersonTargetedID" id="persistent-id">
         <AttributeDecoder xsi:type="NameIDFromScopedAttributeDecoder" formatter="$NameQualifier!$SPNameQualifier!$Name" defaultQualifiers="true"/>
index 22ae662..a2d1742 100644 (file)
@@ -1,5 +1,6 @@
 <afp:AttributeFilterPolicyGroup
     xmlns="urn:mace:shibboleth:2.0:afp:mf:basic"
+    xmlns:saml="urn:mace:shibboleth:2.0:afp:mf:saml"
     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">
@@ -24,7 +25,7 @@
         <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"/>
+        <Rule xsi:type="saml:AttributeScopeMatchesShibMDScope"/>
     </afp:PermitValueRule>
 
     <afp:AttributeFilterPolicy>
         <afp:AttributeRule attributeID="targeted-id">
             <afp:PermitValueRuleReference ref="ScopingRules"/>
         </afp:AttributeRule>
-        
+
+        <!-- Require NameQualifier/SPNameQualifier match IdP and SP entityID respectively. -->
+        <afp:AttributeRule attributeID="persistent-id">
+            <afp:PermitValueRule xsi:type="saml:NameIDQualifierString"/>
+        </afp:AttributeRule>
+
         <!-- Catch-all that passes everything else through unmolested. -->
         <afp:AttributeRule attributeID="*">
             <afp:PermitValueRule xsi:type="ANY"/>
index 921d778..ab6b457 100644 (file)
             <extension base="afp:MatchFunctorType" />
         </complexContent>
     </complexType>
+
+  <complexType name="NameIDQualifierString">
+    <annotation>
+      <documentation>
+        A match function that ensures that a NameID-valued attribute's qualifier(s), if set, match particular values.
+      </documentation>
+    </annotation>
+    <complexContent>
+      <extension base="afp:MatchFunctorType">
+        <attribute name="attributeID" type="string">
+          <annotation>
+            <documentation>
+              The ID of the attribute whose qualifiers should be matched. If no attribute ID is specified the
+              ID of the containing attribute rule is assumed.
+            </documentation>
+          </annotation>
+        </attribute>
+        <attribute name="NameQualifier" type="string">
+          <annotation>
+            <documentation>
+              A value to require in the NameQualifier field, or if omitted, require that it match the issuing IdP's entityID.
+            </documentation>
+          </annotation>
+        </attribute>
+        <attribute name="SPNameQualifier" type="string">
+          <annotation>
+            <documentation>
+              A value to require in the SPNameQualifier field, or if omitted, require that it match the SP's entityID.
+            </documentation>
+          </annotation>
+        </attribute>
+      </extension>
+    </complexContent>
+  </complexType>
+
 </schema>
\ No newline at end of file
index 612ba34..9766dbd 100644 (file)
@@ -192,6 +192,7 @@ libshibsp_la_SOURCES = \
        attribute/filtering/impl/AttributeScopeRegexFunctor.cpp \
        attribute/filtering/impl/AttributeValueRegexFunctor.cpp \
        attribute/filtering/impl/AuthenticationMethodRegexFunctor.cpp \
+       attribute/filtering/impl/NameIDQualifierStringFunctor.cpp \
        attribute/filtering/impl/NumberOfAttributeValuesFunctor.cpp \
        attribute/filtering/impl/AttributeIssuerInEntityGroupFunctor.cpp \
        attribute/filtering/impl/AttributeRequesterInEntityGroupFunctor.cpp \
index a802d3d..1fd976e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2009 Internet2
+ *  Copyright 2001-2010 Internet2
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -118,6 +118,9 @@ namespace shibsp {
     /** Matches based on metadata Scope extensions. */
     extern SHIBSP_API xmltooling::QName AttributeScopeMatchesShibMDScopeType;
 
+    /** Matches based on NameID NameQualifiers. */
+    extern SHIBSP_API xmltooling::QName NameIDQualifierStringType;
+
     /**
      * Registers MatchFunctor classes into the runtime.
      */
index 06dada7..1fd98ac 100644 (file)
@@ -74,7 +74,12 @@ const XMLCh* BasicFilteringContext::getAuthnContextDeclRef() const
 
 const XMLCh* BasicFilteringContext::getAttributeRequester() const
 {
-    return m_app.getXMLString("entityID").second;
+    if (getAttributeIssuerMetadata()) {
+        return getApplication().getRelyingParty(
+            dynamic_cast<const EntityDescriptor*>(getAttributeIssuerMetadata()->getParent())
+            )->getXMLString("entityID").second;
+    }
+    return getApplication().getRelyingParty(getAttributeIssuer())->getXMLString("entityID").second;
 }
 
 const XMLCh* BasicFilteringContext::getAttributeIssuer() const
index a2e7e45..bdb8779 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2009 Internet2
+ *  Copyright 2001-2010 Internet2
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -62,6 +62,7 @@ namespace shibsp {
     DECL_FACTORY(AttributeIssuerInEntityGroup);
     DECL_FACTORY(AttributeRequesterInEntityGroup);
     DECL_FACTORY(AttributeScopeMatchesShibMDScope);
+    DECL_FACTORY(NameIDQualifierString);
 
 
     static const XMLCh ANY[] =                          UNICODE_LITERAL_3(A,N,Y);
@@ -82,6 +83,7 @@ namespace shibsp {
     static const XMLCh AttributeIssuerInEntityGroup[] = UNICODE_LITERAL_28(A,t,t,r,i,b,u,t,e,I,s,s,u,e,r,I,n,E,n,t,i,t,y,G,r,o,u,p);
     static const XMLCh AttributeRequesterInEntityGroup[] = UNICODE_LITERAL_31(A,t,t,r,i,b,u,t,e,R,e,q,u,e,s,t,e,r,I,n,E,n,t,i,t,y,G,r,o,u,p);
     static const XMLCh AttributeScopeMatchesShibMDScope[] = UNICODE_LITERAL_32(A,t,t,r,i,b,u,t,e,S,c,o,p,e,M,a,t,c,h,e,s,S,h,i,b,M,D,S,c,o,p,e);
+    static const XMLCh NameIDQualifierString[] =        UNICODE_LITERAL_21(N,a,m,e,I,D,Q,u,a,l,i,f,i,e,r,S,t,r,i,n,g);
 };
 
 DECL_BASIC_QNAME(AnyMatchFunctor, ANY);
@@ -102,6 +104,7 @@ DECL_BASIC_QNAME(NumberOfAttributeValues, NumberOfAttributeValues);
 DECL_SAML_QNAME(AttributeIssuerInEntityGroup, AttributeIssuerInEntityGroup);
 DECL_SAML_QNAME(AttributeRequesterInEntityGroup, AttributeRequesterInEntityGroup);
 DECL_SAML_QNAME(AttributeScopeMatchesShibMDScope, AttributeScopeMatchesShibMDScope);
+DECL_SAML_QNAME(NameIDQualifierString, NameIDQualifierString);
 
 void SHIBSP_API shibsp::registerMatchFunctors()
 {
@@ -125,6 +128,7 @@ void SHIBSP_API shibsp::registerMatchFunctors()
     REGISTER_FACTORY(AttributeIssuerInEntityGroup);
     REGISTER_FACTORY(AttributeRequesterInEntityGroup);
     REGISTER_FACTORY(AttributeScopeMatchesShibMDScope);
+    REGISTER_FACTORY(NameIDQualifierString);
 }
 
 MatchFunctor::MatchFunctor()
diff --git a/shibsp/attribute/filtering/impl/NameIDQualifierStringFunctor.cpp b/shibsp/attribute/filtering/impl/NameIDQualifierStringFunctor.cpp
new file mode 100644 (file)
index 0000000..66735d9
--- /dev/null
@@ -0,0 +1,169 @@
+/*\r
+ *  Copyright 2010 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
+ * NameIDQualifierStringFunctor.cpp\r
+ * \r
+ * A match function that ensures that a NameID-valued attribute's qualifier(s)\r
+ * match particular values.\r
+ */\r
+\r
+#include "internal.h"\r
+#include "exceptions.h"\r
+#include "attribute/NameIDAttribute.h"\r
+#include "attribute/filtering/FilteringContext.h"\r
+#include "attribute/filtering/FilterPolicyContext.h"\r
+#include "attribute/filtering/MatchFunctor.h"\r
+\r
+#include <saml/saml2/core/Assertions.h>\r
+#include <xmltooling/util/XMLHelper.h>\r
+\r
+using namespace shibsp;\r
+using namespace xmltooling::logging;\r
+using namespace xmltooling;\r
+using namespace std;\r
+using opensaml::saml2::NameID;\r
+\r
+namespace shibsp {\r
+\r
+    static const XMLCh attributeID[] =  UNICODE_LITERAL_11(a,t,t,r,i,b,u,t,e,I,D);\r
+\r
+    /**\r
+     * A match function that ensures that a NameID-valued attribute's qualifier(s)\r
+     * match particular values.\r
+     */\r
+    class SHIBSP_DLLLOCAL NameIDQualifierStringFunctor : public MatchFunctor\r
+    {\r
+        string m_attributeID,m_matchNameQualifier,m_matchSPNameQualifier;\r
+\r
+        bool hasValue(const FilteringContext& filterContext) const;\r
+        bool matches(const FilteringContext& filterContext, const Attribute& attribute, size_t index) const;\r
+\r
+    public:\r
+        NameIDQualifierStringFunctor(const DOMElement* e)\r
+            : m_attributeID(XMLHelper::getAttrString(e, nullptr, attributeID)),\r
+                m_matchNameQualifier(XMLHelper::getAttrString(e, nullptr, NameID::NAMEQUALIFIER_ATTRIB_NAME)),\r
+                m_matchSPNameQualifier(XMLHelper::getAttrString(e, nullptr, NameID::SPNAMEQUALIFIER_ATTRIB_NAME)) {\r
+        }\r
+\r
+        virtual ~NameIDQualifierStringFunctor() {\r
+        }\r
+\r
+        bool evaluatePolicyRequirement(const FilteringContext& filterContext) const {\r
+            if (m_attributeID.empty())\r
+                throw AttributeFilteringException("No attributeID specified.");\r
+            return hasValue(filterContext);\r
+        }\r
+\r
+        bool evaluatePermitValue(const FilteringContext& filterContext, const Attribute& attribute, size_t index) const {\r
+            if (m_attributeID.empty() || m_attributeID == attribute.getId())\r
+                return matches(filterContext, attribute, index);\r
+            return hasValue(filterContext);\r
+        }\r
+    };\r
+\r
+    MatchFunctor* SHIBSP_DLLLOCAL NameIDQualifierStringFactory(const std::pair<const FilterPolicyContext*,const DOMElement*>& p)\r
+    {\r
+        return new NameIDQualifierStringFunctor(p.second);\r
+    }\r
+\r
+};\r
+\r
+bool NameIDQualifierStringFunctor::hasValue(const FilteringContext& filterContext) const\r
+{\r
+    size_t count;\r
+    pair<multimap<string,Attribute*>::const_iterator,multimap<string,Attribute*>::const_iterator> attrs =\r
+        filterContext.getAttributes().equal_range(m_attributeID);\r
+    for (; attrs.first != attrs.second; ++attrs.first) {\r
+        count = attrs.first->second->valueCount();\r
+        for (size_t index = 0; index < count; ++index) {\r
+            if (matches(filterContext, *(attrs.first->second), index))\r
+                return true;\r
+        }\r
+    }\r
+    return false;\r
+}\r
+\r
+bool NameIDQualifierStringFunctor::matches(const FilteringContext& filterContext, const Attribute& attribute, size_t index) const\r
+{\r
+    const NameIDAttribute* nameattr = dynamic_cast<const NameIDAttribute*>(&attribute);\r
+    if (!nameattr) {\r
+        Category::getInstance(SHIBSP_LOGCAT".AttributeFilter").warn(\r
+            "NameIDQualifierString MatchFunctor applied to non-NameID-valued attribute (%s)", attribute.getId()\r
+            );\r
+        return false;\r
+    }\r
+\r
+    const NameIDAttribute::Value& val = nameattr->getValues()[index];\r
+    if (!val.m_NameQualifier.empty()) {\r
+        if (m_matchNameQualifier.empty()) {\r
+            auto_ptr_char issuer(filterContext.getAttributeIssuer());\r
+            if (issuer.get() && *issuer.get()) {\r
+                if (val.m_NameQualifier != issuer.get()) {\r
+                    Category::getInstance(SHIBSP_LOGCAT".AttributeFilter").warn(\r
+                        "NameIDQualifierString MatchFunctor rejecting NameQualifier (%s), should be (%s)",\r
+                        val.m_NameQualifier.c_str(), issuer.get()\r
+                        );\r
+                    return false;\r
+                }\r
+            }\r
+            else {\r
+                Category::getInstance(SHIBSP_LOGCAT".AttributeFilter").warn(\r
+                    "NameIDQualifierString MatchFunctor rejecting NameQualifier (%s), attribute issuer unknown",\r
+                    val.m_NameQualifier.c_str()\r
+                    );\r
+                return false;\r
+            }\r
+        }\r
+        else if (m_matchNameQualifier != val.m_NameQualifier) {\r
+            Category::getInstance(SHIBSP_LOGCAT".AttributeFilter").warn(\r
+                "NameIDQualifierString MatchFunctor rejecting NameQualifier (%s), should be (%s)",\r
+                val.m_NameQualifier.c_str(), m_matchNameQualifier.c_str()\r
+                );\r
+            return false;\r
+        }\r
+    }\r
+    if (!val.m_SPNameQualifier.empty()) {\r
+        if (m_matchSPNameQualifier.empty()) {\r
+            auto_ptr_char req(filterContext.getAttributeRequester());\r
+            if (req.get() && *req.get()) {\r
+                if (val.m_SPNameQualifier != req.get()) {\r
+                    Category::getInstance(SHIBSP_LOGCAT".AttributeFilter").warn(\r
+                        "NameIDQualifierString MatchFunctor rejecting SPNameQualifier (%s), should be (%s)",\r
+                        val.m_SPNameQualifier.c_str(), req.get()\r
+                        );\r
+                    return false;\r
+                }\r
+            }\r
+            else {\r
+                Category::getInstance(SHIBSP_LOGCAT".AttributeFilter").warn(\r
+                    "NameIDQualifierString MatchFunctor rejecting SPNameQualifier (%s), attribute requester unknown",\r
+                    val.m_SPNameQualifier.c_str()\r
+                    );\r
+                return false;\r
+            }\r
+        }\r
+        else if (m_matchSPNameQualifier != val.m_SPNameQualifier) {\r
+            Category::getInstance(SHIBSP_LOGCAT".AttributeFilter").warn(\r
+                "NameIDQualifierString MatchFunctor rejecting SPNameQualifier (%s), should be (%s)",\r
+                val.m_SPNameQualifier.c_str(), m_matchSPNameQualifier.c_str()\r
+                );\r
+            return false;\r
+        }\r
+    }\r
+\r
+    return true;\r
+}\r
index 8839b82..c2be54c 100644 (file)
     <ClCompile Include="AbstractSPRequest.cpp" />\r
     <ClCompile Include="Application.cpp" />\r
     <ClCompile Include="attribute\Base64AttributeDecoder.cpp" />\r
+    <ClCompile Include="attribute\filtering\impl\NameIDQualifierStringFunctor.cpp" />\r
     <ClCompile Include="binding\impl\XMLProtocolProvider.cpp" />\r
     <ClCompile Include="handler\impl\DiscoveryFeed.cpp" />\r
     <ClCompile Include="handler\impl\LogoutInitiator.cpp" />\r
index b52f78e..15b7d70 100644 (file)
     <ClCompile Include="attribute\Base64AttributeDecoder.cpp">\r
       <Filter>Source Files\attribute</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="attribute\filtering\impl\NameIDQualifierStringFunctor.cpp">\r
+      <Filter>Source Files\attribute\filtering\impl</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="remoting\impl\SocketListener.h">\r