Copied over mapping and filtering schemas.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 30 Apr 2007 03:21:57 +0000 (03:21 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 30 Apr 2007 03:21:57 +0000 (03:21 +0000)
New AttributeExtractor API for SAML decoding.
Rewrite of simple AttributeResolver as a query-only component.
Add missing logic for deriving case sensitive attribute flag.
Switch to multimaps for attribute APIs to allow duplicate IDs.

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

53 files changed:
apache/mod_apache.cpp
configs/Makefile.am
configs/attribute-map.xml.in [new file with mode: 0644]
configs/resolver-simple.xml.in [deleted file]
configs/shibboleth.xml.in
nsapi_shib/nsapi_shib.cpp
schemas/Makefile.am
schemas/catalog.xml.in
schemas/shibboleth-2.0-afp-mf-basic.xsd [new file with mode: 0644]
schemas/shibboleth-2.0-afp-mf-saml.xsd [new file with mode: 0644]
schemas/shibboleth-2.0-afp.xsd [new file with mode: 0644]
schemas/shibboleth-2.0-attribute-map.xsd [new file with mode: 0644]
schemas/shibboleth-2.0-attribute-resolver.xsd [deleted file]
schemas/shibboleth-2.0-native-sp-config.xsd [moved from schemas/shibboleth-spconfig-2.0.xsd with 95% similarity]
schemas/shibboleth-2.0-simple-resolver.xsd [deleted file]
shibsp/Application.h
shibsp/Makefile.am
shibsp/SPConfig.cpp
shibsp/SPConfig.h
shibsp/ServiceProvider.cpp
shibsp/SessionCache.cpp
shibsp/SessionCache.h
shibsp/attribute/Attribute.cpp
shibsp/attribute/Attribute.h
shibsp/attribute/AttributeDecoder.h
shibsp/attribute/NameIDAttributeDecoder.cpp
shibsp/attribute/ScopedAttribute.h
shibsp/attribute/ScopedAttributeDecoder.cpp
shibsp/attribute/SimpleAttribute.h
shibsp/attribute/StringAttributeDecoder.cpp [moved from shibsp/attribute/SimpleAttributeDecoder.cpp with 86% similarity]
shibsp/attribute/resolver/AttributeExtractor.h [new file with mode: 0644]
shibsp/attribute/resolver/AttributeResolver.h
shibsp/attribute/resolver/ResolutionContext.h
shibsp/attribute/resolver/impl/AttributeResolver.cpp [deleted file]
shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp [new file with mode: 0644]
shibsp/attribute/resolver/impl/SimpleAttributeResolver.cpp [deleted file]
shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp [new file with mode: 0644]
shibsp/exceptions.h
shibsp/handler/AssertionConsumerService.h
shibsp/handler/impl/AbstractHandler.cpp
shibsp/handler/impl/AssertionConsumerService.cpp
shibsp/handler/impl/SAML1Consumer.cpp
shibsp/handler/impl/SAML2Consumer.cpp
shibsp/handler/impl/SessionInitiator.cpp
shibsp/impl/RemotedSessionCache.cpp
shibsp/impl/StorageServiceSessionCache.cpp
shibsp/impl/XMLAccessControl.cpp
shibsp/impl/XMLServiceProvider.cpp
shibsp/remoting/impl/ListenerService.cpp
shibsp/security/PKIXTrustEngine.cpp
shibsp/shibsp.vcproj
shibsp/util/SPConstants.cpp
shibsp/util/SPConstants.h

index d4006da..319820c 100644 (file)
@@ -77,6 +77,8 @@
 using namespace shibsp;
 using namespace xmltooling;
 using namespace std;
+using xercesc::RegularExpression;
+using xercesc::XMLException;
 
 extern "C" module MODULE_VAR_EXPORT mod_shib;
 
@@ -559,6 +561,8 @@ public:
     Lockable* lock() {return this;}
     void unlock() {}
     bool authorized(const SPRequest& request, const Session* session) const;
+private:
+    bool checkAttribute(const SPRequest& request, const Attribute* attr, const char* toMatch, RegularExpression* re) const;
 };
 
 AccessControl* htAccessFactory(const xercesc::DOMElement* const & e)
@@ -741,6 +745,35 @@ static SH_AP_TABLE* groups_for_user(request_rec* r, const char* user, char* grpf
     return grps;
 }
 
+bool htAccessControl::checkAttribute(const SPRequest& request, const Attribute* attr, const char* toMatch, RegularExpression* re) const
+{
+    bool caseSensitive = attr->isCaseSensitive();
+    const vector<string>& vals = attr->getSerializedValues();
+    for (vector<string>::const_iterator v=vals.begin(); v!=vals.end(); ++v) {
+        if (re) {
+            auto_ptr<XMLCh> trans(fromUTF8(v->c_str()));
+            if (re->matches(trans.get())) {
+                request.log(SPRequest::SPDebug,
+                    string("htAccessControl plugin expecting regexp ") + toMatch + ", got " + *v + ": authorization granted"
+                    );
+                return true;
+            }
+        }
+        else if ((caseSensitive && *v == toMatch) || (!caseSensitive && !strcasecmp(v->c_str(), toMatch))) {
+            request.log(SPRequest::SPDebug,
+                string("htAccessControl plugin expecting ") + toMatch + ", got " + *v + ": authorization granted."
+                );
+            return true;
+        }
+        else {
+            request.log(SPRequest::SPDebug,
+                string("htAccessControl plugin expecting ") + toMatch + ", got " + *v + ": authorization not granted."
+                );
+        }
+    }
+    return false;
+}
+
 bool htAccessControl::authorized(const SPRequest& request, const Session* session) const
 {
     // Make sure the object is our type.
@@ -808,14 +841,14 @@ bool htAccessControl::authorized(const SPRequest& request, const Session* sessio
                     try {
                         // To do regex matching, we have to convert from UTF-8.
                         auto_ptr<XMLCh> trans(fromUTF8(w));
-                        xercesc::RegularExpression re(trans.get());
+                        RegularExpression re(trans.get());
                         auto_ptr<XMLCh> trans2(fromUTF8(remote_user.c_str()));
                         if (re.matches(trans2.get())) {
                             request.log(SPRequest::SPDebug, string("htAccessControl plugin accepting user (") + w + ")");
                             SHIB_AP_CHECK_IS_OK;
                         }
                     }
-                    catch (xercesc::XMLException& ex) {
+                    catch (XMLException& ex) {
                         auto_ptr_char tmp(ex.getMessage());
                         request.log(SPRequest::SPError,
                             string("htAccessControl plugin caught exception while parsing regular expression (") + w + "): " + tmp.get());
@@ -851,16 +884,15 @@ bool htAccessControl::authorized(const SPRequest& request, const Session* sessio
                 continue;
             }
             
-            // Find the attribute matching the require rule.
-            map<string,const Attribute*>::const_iterator attr = session->getAttributes().find(w);
-            if (attr == session->getAttributes().end()) {
+            // Find the attribute(s) matching the require rule.
+            pair<multimap<string,Attribute*>::const_iterator,multimap<string,Attribute*>::const_iterator> attrs =
+                session->getAttributes().equal_range(w);
+            if (attrs.first == attrs.second) {
                 request.log(SPRequest::SPWarn, string("htAccessControl rule requires attribute (") + w + "), not found in session");
                 continue;
             }
 
             bool regexp=false;
-            bool caseSensitive = attr->second->isCaseSensitive();
-            const vector<string>& vals = attr->second->getSerializedValues();
 
             while (!auth_OK[x] && *t) {
                 w=ap_getword_conf(sta->m_req->pool,&t);
@@ -870,7 +902,7 @@ bool htAccessControl::authorized(const SPRequest& request, const Session* sessio
                 }
 
                 try {
-                    auto_ptr<xercesc::RegularExpression> re;
+                    auto_ptr<RegularExpression> re;
                     if (regexp) {
                         delete re.release();
                         auto_ptr<XMLCh> trans(fromUTF8(w));
@@ -878,30 +910,13 @@ bool htAccessControl::authorized(const SPRequest& request, const Session* sessio
                         re=temp;
                     }
                     
-                    for (vector<string>::const_iterator v=vals.begin(); !auth_OK[x] && v!=vals.end(); ++v) {
-                        if (regexp) {
-                            auto_ptr<XMLCh> trans(fromUTF8(v->c_str()));
-                            if (re->matches(trans.get())) {
-                                request.log(SPRequest::SPDebug,
-                                    string("htAccessControl plugin expecting ") + w + ", got " + *v + ": authorization granted"
-                                    );
-                                SHIB_AP_CHECK_IS_OK;
-                            }
-                        }
-                        else if ((caseSensitive && *v == w) || (!caseSensitive && !strcasecmp(v->c_str(),w))) {
-                            request.log(SPRequest::SPDebug,
-                                string("htAccessControl plugin expecting ") + w + ", got " + *v + ": authorization granted."
-                                );
+                    for (; !auth_OK[x] && attrs.first!=attrs.second; ++attrs.first) {
+                        if (checkAttribute(request, attrs.first->second, w, regexp ? re.get() : NULL)) {
                             SHIB_AP_CHECK_IS_OK;
                         }
-                        else {
-                            request.log(SPRequest::SPDebug,
-                                string("htAccessControl plugin expecting ") + w + ", got " + *v + ": authorization not granted."
-                                );
-                        }
                     }
                 }
-                catch (xercesc::XMLException& ex) {
+                catch (XMLException& ex) {
                     auto_ptr_char tmp(ex.getMessage());
                     request.log(SPRequest::SPError,
                         string("htAccessControl plugin caught exception while parsing regular expression (") + w + "): " + tmp.get()
index f2b4010..8198bb5 100644 (file)
@@ -21,7 +21,7 @@ BUILTCONFIGFILES = \
        shibboleth.xml \
        native.logger \
        shibd.logger \
-       resolver-simple.xml \
+       attribute-map.xml \
        example-metadata.xml
 
 # While BUILTCONFIGFILES are processed, these are not; so we should pull
@@ -75,7 +75,7 @@ native.logger: ${srcdir}/native.logger.in Makefile ${top_builddir}/config.status
 shibboleth.xml: ${srcdir}/shibboleth.xml.in Makefile ${top_builddir}/config.status
        $(MAKE) do-build-file FILE=$@
 
-resolver-simple.xml: ${srcdir}/resolver-simple.xml.in Makefile ${top_builddir}/config.status
+attribute-map.xml: ${srcdir}/attribute-map.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
@@ -111,11 +111,13 @@ CLEANFILES = \
        shibd.logger \
        native.logger \
        shibboleth.xml \
-       resolver-simple.xml \
+       attribute-map.xml \
        example-metadata.xml
 
-EXTRA_DIST = .cvsignore \
+EXTRA_DIST =
        shibboleth.xml.in \
+       attribute-map.xml.in \
+       example-metadata.xml.in \
        native.logger.in \
        shibd.logger.in \
        apache.config.in \
@@ -129,7 +131,5 @@ EXTRA_DIST = .cvsignore \
        sessionError.html \
        metadataError.html \
        sslError.html \
-       resolver-simple.xml.in \
-       example-metadata.xml.in \
        sp-example.key \
        sp-example.crt
diff --git a/configs/attribute-map.xml.in b/configs/attribute-map.xml.in
new file mode 100644 (file)
index 0000000..5eb9abe
--- /dev/null
@@ -0,0 +1,78 @@
+<Attributes xmlns="urn:mace:shibboleth:2.0:attribute-map"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:attribute-map @-PKGXMLDIR-@/shibboleth-2.0-attribute-map.xsd">
+
+       <!-- First some useful eduPerson attributes that many sites might use. -->
+       
+       <Attribute name="urn:mace:dir:attribute-def:eduPersonPrincipalName" id="REMOTE_USER">
+               <AttributeDecoder xsi:type="ScopedAttributeDecoder"/>
+       </Attribute>
+       
+       <Attribute name="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" id="affiliation">
+               <AttributeDecoder xsi:type="ScopedAttributeDecoder" caseSensitive="false"/>
+       </Attribute>
+       
+       <Attribute name="urn:mace:dir:attribute-def:eduPersonAffiliation" id="unscoped-affiliation">
+               <AttributeDecoder xsi:type="ScopedAttributeDecoder" caseSensitive="false"/>
+       </Attribute>
+       
+       <Attribute name="urn:mace:dir:attribute-def:eduPersonEntitlement" id="entitlement"/>
+       
+       <!-- A persistent id attribute that supports personalized anonymous access. -->
+       
+       <!-- First, the deprecated version: -->
+       <Attribute name="urn:mace:dir:attribute-def:eduPersonTargetedID" id="REMOTE_USER">
+               <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">
+               <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">
+               <AttributeDecoder xsi:type="NameIDAttributeDecoder" formatter="$Name!!$NameQualifier!!$SPNameQualifier"/>
+       </Attribute>
+       
+       <!-- Some more eduPerson attributes, uncomment these to use them... -->
+       <!--
+       <Attribute name="urn:mace:dir:attribute-def:eduPersonPrimaryAffiliation" id="primary-affiliation">
+               <AttributeDecoder xsi:type="StringAttributeDecoder" caseSensitive="false"/>
+       </Attribute>
+       <Attribute name="urn:mace:dir:attribute-def:eduPersonNickname" id="nickname"/>
+       <Attribute name="urn:mace:dir:attribute-def:eduPersonPrimaryOrgUnitDN" id="primary-orgunit-dn"/>
+       <Attribute name="urn:mace:dir:attribute-def:eduPersonOrgUnitDN" id="orgunit-dn"/>
+       <Attribute name="urn:mace:dir:attribute-def:eduPersonOrgDN" id="org-dn"/>
+       -->
+
+       <!--Examples of LDAP-based attributes, uncomment to use these... -->
+       <!--
+       <Attribute name="urn:mace:dir:attribute-def:cn" id="cn"/>
+       <Attribute name="urn:mace:dir:attribute-def:sn" id="sn"/>
+       <Attribute name="urn:mace:dir:attribute-def:givenName" id="givenName"/>
+       <Attribute name="urn:mace:dir:attribute-def:mail" id="mail"/>
+       <Attribute name="urn:mace:dir:attribute-def:telephoneNumber" id="telephoneNumber"/>
+       <Attribute name="urn:mace:dir:attribute-def:title" id="title"/>
+       <Attribute name="urn:mace:dir:attribute-def:initials" id="initials"/>
+       <Attribute name="urn:mace:dir:attribute-def:description" id="description"/>
+       <Attribute name="urn:mace:dir:attribute-def:carLicense" id="carLicense"/>
+       <Attribute name="urn:mace:dir:attribute-def:departmentNumber" id="departmentNumber"/>
+       <Attribute name="urn:mace:dir:attribute-def:displayName" id="displayName"/>
+       <Attribute name="urn:mace:dir:attribute-def:employeeNumber" id="employeeNumber"/>
+       <Attribute name="urn:mace:dir:attribute-def:employeeType" id="employeeType"/>
+       <Attribute name="urn:mace:dir:attribute-def:preferredLanguage" id="preferredLanguage"/>
+       <Attribute name="urn:mace:dir:attribute-def:manager" id="manager"/>
+       <Attribute name="urn:mace:dir:attribute-def:seeAlso" id="seeAlso"/>
+       <Attribute name="urn:mace:dir:attribute-def:facsimileTelephoneNumber" id="facsimileTelephoneNumber"/>
+       <Attribute name="urn:mace:dir:attribute-def:street" id="street"/>
+       <Attribute name="urn:mace:dir:attribute-def:postOfficeBox" id="postOfficeBox"/>
+       <Attribute name="urn:mace:dir:attribute-def:postalCode" id="postalCode"/>
+       <Attribute name="urn:mace:dir:attribute-def:st" id="st"/>
+       <Attribute name="urn:mace:dir:attribute-def:l" id="l"/>
+       <Attribute name="urn:mace:dir:attribute-def:ou" id="ou"/>
+       <Attribute name="urn:mace:dir:attribute-def:businessCategory" id="businessCategory"/>
+       <Attribute name="urn:mace:dir:attribute-def:physicalDeliveryOfficeName" id="physicalDeliveryOfficeName"/>
+       -->
+
+</Attributes>
diff --git a/configs/resolver-simple.xml.in b/configs/resolver-simple.xml.in
deleted file mode 100644 (file)
index 142a7d0..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-<ar:AttributeResolver xmlns:ar="urn:mace:shibboleth:2.0:resolver:simple"
-       xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver:simple @-PKGXMLDIR-@/shibboleth-2.0-simple-resolver.xsd"
-    allowQuery="true">
-
-       <!--
-       Built-in decoders that can extract SAML Attribute data.
-       Custom decoders can be configured here as well.
-       -->
-       <ar:AttributeDecoder id="Simple" type="Simple"/>
-       <ar:AttributeDecoder id="Scoped" type="Scoped"/>
-       <ar:AttributeDecoder id="NameID" type="NameID" formatter="$Name!!$NameQualifier!!$SPNameQualifier"/>
-       
-       <!--
-       The simple resolver just enumerates SAML Attribute elements, each one
-       referencing a specific Attribute by its name on the wire, and its local
-       "friendly" ID. All Attributes that should be visible to an application
-       should be listed, or they will be ignored by the resolver.
-       -->
-       
-       <!-- First some useful eduPerson attributes that many sites might use. -->
-       
-       <saml:Attribute Name="urn:mace:dir:attribute-def:eduPersonPrincipalName" FriendlyName="REMOTE_USER"     ar:decoderId="Scoped"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" FriendlyName="affiliation" ar:decoderId="Scoped"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:eduPersonAffiliation" FriendlyName="unscoped-affiliation" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:eduPersonEntitlement" FriendlyName="entitlement" ar:decoderId="Simple"/>
-       
-       <!-- A persistent id attribute that supports personalized anonymous access. -->
-       
-       <!-- First, the deprecated version: -->
-       <saml:Attribute Name="urn:mace:dir:attribute-def:eduPersonTargetedID" FriendlyName="REMOTE_USER" ar:decoderId="Scoped"/>
-
-       <!-- Second, the new version (note the OID-style name): -->
-       <saml:Attribute Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" FriendlyName="REMOTE_USER" ar:decoderId="NameID"/>
-
-       <!-- Third, the SAML 2.0 NameID Format: -->
-       <saml:Attribute Name="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" FriendlyName="REMOTE_USER" ar:decoderId="NameID"/>
-       
-       <!-- Some more eduPerson attributes, uncomment these to use them... -->
-       <!--
-       <saml:Attribute Name="urn:mace:dir:attribute-def:eduPersonNickname" FriendlyName="nickname" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:eduPersonPrimaryAffiliation" FriendlyName="primary-affiliation" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:eduPersonPrimaryOrgUnitDN" FriendlyName="primary-orgunit-dn" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:eduPersonOrgUnitDN" FriendlyName="orgunit-dn" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:eduPersonOrgDN" FriendlyName="org-dn" ar:decoderId="Simple"/>
-       -->
-
-       <!--Examples of LDAP-based attributes, uncomment to use these... -->
-       <!--
-       <saml:Attribute Name="urn:mace:dir:attribute-def:cn" FriendlyName="cn" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:sn" FriendlyName="sn" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:givenName" FriendlyName="givenName" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:mail" FriendlyName="mail" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:telephoneNumber" FriendlyName="telephoneNumber" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:title" FriendlyName="title" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:initials" FriendlyName="initials" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:description" FriendlyName="description" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:carLicense" FriendlyName="carLicense" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:departmentNumber" FriendlyName="departmentNumber" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:displayName" FriendlyName="displayName" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:employeeNumber" FriendlyName="employeeNumber" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:employeeType" FriendlyName="employeeType" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:preferredLanguage" FriendlyName="preferredLanguage" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:manager" FriendlyName="manager" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:seeAlso" FriendlyName="seeAlso" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:facsimileTelephoneNumber" FriendlyName="facsimileTelephoneNumber" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:street" FriendlyName="street" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:postOfficeBox" FriendlyName="postOfficeBox" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:postalCode" FriendlyName="postalCode" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:st" FriendlyName="st" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:l" FriendlyName="l" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:ou" FriendlyName="ou" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:businessCategory" FriendlyName="businessCategory" ar:decoderId="Simple"/>
-       <saml:Attribute Name="urn:mace:dir:attribute-def:physicalDeliveryOfficeName" FriendlyName="physicalDeliveryOfficeName" ar:decoderId="Simple"/>
-       -->
-
-</ar::AttributeResolver>
index ffe9625..e40af74 100644 (file)
@@ -1,5 +1,5 @@
-<SPConfig xmlns="urn:mace:shibboleth:sp:config:2.0"
-       xmlns:conf="urn:mace:shibboleth:sp:config:2.0"
+<SPConfig xmlns="urn:mace:shibboleth:2.0:native:sp:config"
+       xmlns:conf="urn:mace:shibboleth:2.0:native:sp:config"
        xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
        xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"      
        xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
                        <TrustEngine type="PKIX"/>
                </TrustEngine>
 
-               <!-- Built-in attribute resolver to extract data from SAML assertions. -->
-               <AttributeResolver type="Simple" path="@-PKGSYSCONFDIR-@/resolver-simple.xml"/>
+               <!-- Map to extract attributes from SAML assertions. -->
+               <AttributeExtractor type="XML" path="@-PKGSYSCONFDIR-@/attribute-map.xml"/>
 
                <!-- Simple file-based resolver for key/certificate information. -->
                <CredentialResolver type="File">
index 0b83ec8..3ef06e2 100644 (file)
@@ -78,7 +78,7 @@ namespace {
     static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e);
 }
 
-PluginManager<RequestMapper,const xercesc::DOMElement*>::Factory SunRequestMapFactory;
+PluginManager<RequestMapper,string,const xercesc::DOMElement*>::Factory SunRequestMapFactory;
 
 extern "C" NSAPI_PUBLIC void nsapi_shib_exit(void*)
 {
index 5d83cd5..7edc155 100644 (file)
@@ -8,8 +8,11 @@ pkgxml_DATA = \
        catalog.xml \
        shibboleth.xsd \
     shibboleth-metadata-1.0.xsd \
-    shibboleth-spconfig-2.0.xsd \
-    shibboleth-2.0-attribute-resolver.xsd \
+    shibboleth-2.0-native-sp-config-2.0.xsd \
+    shibboleth-2.0-afp.xsd \
+    shibboleth-2.0-afp-mf-basic.xsd \
+    shibboleth-2.0-afp-mf-saml.xsd \
+    shibboleth-2.0-attribute-map.xsd \
     shibboleth-2.0-simple-resolver.xsd \
     metadata_v12_to_v13.xsl \
     metadata_v13_to_v12.xsl \
index d1ea637..e633dd3 100644 (file)
@@ -1,8 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
     <system systemId="urn:mace:shibboleth:metadata:1.0" uri="@-PKGXMLDIR-@/shibboleth-metadata-1.0.xsd"/>
-    <system systemId="urn:mace:shibboleth:sp:config:2.0" uri="@-PKGXMLDIR-@/shibboleth-spconfig-2.0.xsd"/>
-    <system systemId="urn:mace:shibboleth:2.0:resolver" uri="@-PKGXMLDIR-@/shibboleth-2.0-attribute-resolver.xsd"/>
-    <system systemId="urn:mace:shibboleth:2.0:resolver:simple" uri="@-PKGXMLDIR-@/shibboleth-2.0-simple-resolver.xsd"/>
+    <system systemId="urn:mace:shibboleth:2.0:native:sp:config" uri="@-PKGXMLDIR-@/shibboleth-2.0-native-sp-config.xsd"/>
+    <system systemId="urn:mace:shibboleth:2.0:afp" uri="@-PKGXMLDIR-@/shibboleth-2.0-afp.xsd"/>
+    <system systemId="urn:mace:shibboleth:2.0:afp:mf:basic" uri="@-PKGXMLDIR-@/shibboleth-2.0-afp-mf-basic.xsd"/>
+    <system systemId="urn:mace:shibboleth:2.0:afp:mf:saml" uri="@-PKGXMLDIR-@/shibboleth-2.0-afp-mf-saml.xsd"/>
+    <system systemId="urn:mace:shibboleth:2.0:attribute-map" uri="@-PKGXMLDIR-@/shibboleth-2.0-attribute-map.xsd"/>
     <system systemId="urn:mace:shibboleth:1.0" uri="@-PKGXMLDIR-@/shibboleth.xsd"/>
 </catalog>
diff --git a/schemas/shibboleth-2.0-afp-mf-basic.xsd b/schemas/shibboleth-2.0-afp-mf-basic.xsd
new file mode 100644 (file)
index 0000000..d178261
--- /dev/null
@@ -0,0 +1,363 @@
+<?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">
+
+    <import namespace="urn:mace:shibboleth:2.0:afp" schemaLocation="classpath:/schema/shibboleth-2.0-afp.xsd" />
+
+    <!-- Blanket Match Function -->
+    <complexType name="ANY">
+        <annotation>
+            <documentation>A match function that evaluates to true.</documentation>
+        </annotation>
+        <complexContent>
+            <extension base="afp:MatchFunctorType" />
+        </complexContent>
+    </complexType>
+
+    <!--  Boolean Match Functions -->
+    <complexType name="AND">
+        <annotation>
+            <documentation>
+                A match function that performs a logical AND on the results of all contained matching functions.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="afp:MatchFunctorType">
+                <choice minOccurs="2" maxOccurs="unbounded">
+                    <element name="Rule" type="afp:MatchFunctorType">
+                        <annotation>
+                            <documentation>
+                                The set of match function rules to be ANDed.
+                            </documentation>
+                        </annotation>
+                    </element>
+                    <element name="RuleReference" type="afp:ReferenceType">
+                        <annotation>
+                            <documentation>
+                                The set of match function rules to be ANDed.
+                            </documentation>
+                        </annotation>
+                    </element>
+                </choice>
+            </extension>
+        </complexContent>
+    </complexType>
+
+    <complexType name="OR">
+        <annotation>
+            <documentation>
+                A match function that performs a logical OR on the results of all contained matching functions.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="afp:MatchFunctorType">
+                <choice minOccurs="2" maxOccurs="unbounded">
+                    <element name="Rule" type="afp:MatchFunctorType">
+                        <annotation>
+                            <documentation>
+                                The set of match function rules to be ANDed.
+                            </documentation>
+                        </annotation>
+                    </element>
+                    <element name="RuleReference" type="afp:ReferenceType">
+                        <annotation>
+                            <documentation>
+                                The set of match function rules to be ANDed.
+                            </documentation>
+                        </annotation>
+                    </element>
+                </choice>
+            </extension>
+        </complexContent>
+    </complexType>
+
+    <complexType name="NOT">
+        <annotation>
+            <documentation>
+                A match function that performs a logical NOT on the resultof the contained matching function.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="afp:MatchFunctorType">
+                <choice>
+                    <element name="Rule" type="afp:MatchFunctorType">
+                        <annotation>
+                            <documentation>
+                                The set of match function rules to be ANDed.
+                            </documentation>
+                        </annotation>
+                    </element>
+                    <element name="RuleReference" type="afp:ReferenceType">
+                        <annotation>
+                            <documentation>
+                                The set of match function rules to be ANDed.
+                            </documentation>
+                        </annotation>
+                    </element>
+                </choice>
+            </extension>
+        </complexContent>
+    </complexType>
+
+    <!--  Literal String Match Functions -->
+    <complexType name="AttributeRequesterString">
+        <annotation>
+            <documentation>
+                A match function that matches the attribute request against the specified value.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="basic:StringMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="AttributeIssuerString">
+        <annotation>
+            <documentation>
+                A match function that matches the attribute issuer against the specified value.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="basic:StringMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="PrincipalNameString">
+        <annotation>
+            <documentation>A match function that matches the principal name against the specified value.</documentation>
+        </annotation>
+        <complexContent>
+            <extension base="basic:StringMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="AuthenticationMethodString">
+        <annotation>
+            <documentation>
+                A match function that matches the authentication method against the specified value.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="basic:StringMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="AttributeValueString">
+        <annotation>
+            <documentation>
+                A match function that matches the value of an attribute against the specified value. This match
+                evaluates to true if the attribute contains the specified value.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="basic:AttributeTargetedStringMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="AttributeScopeString">
+        <annotation>
+            <documentation>
+                A match function that matches the attribute scope against the specified value.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="basic:AttributeTargetedStringMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="AttributeTargetedStringMatchType">
+        <complexContent>
+            <extension base="basic:StringMatchType">
+                <attribute name="attributeID" type="string">
+                    <annotation>
+                        <documentation>
+                            The ID of the attribute whose value should be matched. If no attribute ID is specified the
+                            ID of the containing attribute rule is assumed.
+                        </documentation>
+                    </annotation>
+                </attribute>
+            </extension>
+        </complexContent>
+    </complexType>
+
+    <complexType name="StringMatchType" abstract="true">
+        <complexContent>
+            <extension base="afp:MatchFunctorType">
+                <attribute name="value" type="string" use="required">
+                    <annotation>
+                        <documentation>The string value to match.</documentation>
+                    </annotation>
+                </attribute>
+                <attribute name="ignoreCase" type="boolean" default="false">
+                    <annotation>
+                        <documentation>
+                            A boolean flag indicating whether case should be ignored when evaluating the match.
+                        </documentation>
+                    </annotation>
+                </attribute>
+            </extension>
+        </complexContent>
+    </complexType>
+
+    <!--  Regular Expression Match Functions -->
+    <complexType name="AttributeRequesterRegex">
+        <annotation>
+            <documentation>
+                A match function that matches the attribute requester against the specified regular expression.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="basic:RegexMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="AttributeIssuerRegex">
+        <annotation>
+            <documentation>
+                A match function that matches the attribute issuer against the specified regular expression.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="basic:RegexMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="PrincipalNameRegex">
+        <annotation>
+            <documentation>
+                A match function that matches the principal name against the specified regular expression.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="basic:RegexMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="AuthenticationMethodRegex">
+        <annotation>
+            <documentation>
+                A match function that matches the authentication method against the specified regular expression.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="basic:RegexMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="AttributeValueRegex">
+        <annotation>
+            <documentation>
+                A match function that matches an attribute value against the specified regular expression. This function
+                evaluates to true if any value matches the given expression
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="basic:AttributeTargetedRegexMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="AttributeScopeRegex">
+        <annotation>
+            <documentation>
+                A match function that matches the attribute scope against the specified regular expression.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="basic:AttributeTargetedRegexMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="AttributeTargetedRegexMatchType">
+        <complexContent>
+            <extension base="basic:RegexMatchType">
+                <attribute name="attributeID" type="string">
+                    <annotation>
+                        <documentation>
+                            The ID of the attribute whose value should be matched. If no attribute ID is specified the
+                            ID of the containing attribute rule is assumed.
+                        </documentation>
+                    </annotation>
+                </attribute>
+            </extension>
+        </complexContent>
+    </complexType>
+
+    <complexType name="RegexMatchType" abstract="true">
+        <complexContent>
+            <extension base="afp:MatchFunctorType">
+                <attribute name="regex" type="string" use="required">
+                    <annotation>
+                        <documentation>The regular expression values are matched against.</documentation>
+                    </annotation>
+                </attribute>
+            </extension>
+        </complexContent>
+    </complexType>
+
+    <!-- Misc. Functions -->
+    <complexType name="Script">
+        <annotation>
+            <documentation>
+                A match function that evaluates a script to determine if some criteria is met. The script MUST return a
+                boolean.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="afp:MatchFunctorType">
+                <choice>
+                    <element name="Script" type="string" minOccurs="0">
+                        <annotation>
+                            <documentation>The script to evaluate to construct the attribute.</documentation>
+                        </annotation>
+                    </element>
+                    <element name="ScriptFile" type="string" minOccurs="0">
+                        <annotation>
+                            <documentation>
+                                The filesystem path to the script to evaluate to construct the attribute.
+                            </documentation>
+                        </annotation>
+                    </element>
+                </choice>
+                <attribute name="language" type="string" default="javascript">
+                    <annotation>
+                        <documentation>
+                            The JSR-233 name for the scripting language that will be used. By default "javascript" is
+                            supported.
+                        </documentation>
+                    </annotation>
+                </attribute>
+            </extension>
+        </complexContent>
+    </complexType>
+
+    <complexType name="NumberOfAttributeValues">
+        <annotation>
+            <documentation>
+                A match function that evaluates to true if the given attribute has as a number of values that falls
+                between the minimum and maximum. This method may be used as a sanity check to ensure that an unexpected
+                number of values did not come from the attribute resolver and be released.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="afp:MatchFunctorType">
+                <attribute name="attributeID" type="string">
+                    <annotation>
+                        <documentation>The ID of the attribute whose value should be matched.</documentation>
+                    </annotation>
+                </attribute>
+                <attribute name="minimum" type="nonNegativeInteger" default="0">
+                    <annotation>
+                        <documentation>Minimum number of values an attribute may have.</documentation>
+                    </annotation>
+                </attribute>
+                <attribute name="maximum" type="positiveInteger" default="2147483647">
+                    <annotation>
+                        <documentation>Maximum number of values an attribute may have.</documentation>
+                    </annotation>
+                </attribute>
+            </extension>
+        </complexContent>
+    </complexType>
+
+</schema>
\ No newline at end of file
diff --git a/schemas/shibboleth-2.0-afp-mf-saml.xsd b/schemas/shibboleth-2.0-afp-mf-saml.xsd
new file mode 100644 (file)
index 0000000..9b98ed2
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<schema targetNamespace="urn:mace:shibboleth:2.0:afp:mf:saml" xmlns="http://www.w3.org/2001/XMLSchema"
+    xmlns:saml="urn:mace:shibboleth:2.0:afp:mf:saml" xmlns:afp="urn:mace:shibboleth:2.0:afp">
+
+    <import namespace="urn:mace:shibboleth:2.0:afp" schemaLocation="classpath:/schema/shibboleth-2.0-afp.xsd" />
+
+    <annotation>
+        <documentation>
+            A set of SAML specific match functions. These match functions only operate against a SAMLFilterContext.
+        </documentation>
+    </annotation>
+
+    <complexType name="AttributeRequesterInEntityGroup">
+        <annotation>
+            <documentation>
+                A match function that evaluates to true if the attribute requester is found in metadata and is a member
+                of the given entity group.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="saml:EntityGroupMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="AttributeIssuerInEntityGroup">
+        <annotation>
+            <documentation>
+                A match function that evaluates to true if the attribute producer is found in metadata and is a member
+                of the given entity group.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="saml:EntityGroupMatchType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="EntityGroupMatchType">
+        <complexContent>
+            <extension base="afp:MatchFunctorType">
+                <attribute name="groupID" type="string" use="required">
+                    <annotation>
+                        <documentation>The entity group ID that an entity must be in.</documentation>
+                    </annotation>
+                </attribute>
+            </extension>
+        </complexContent>
+    </complexType>
+    
+    <complexType name="AttributeScopeMatchesShibMDScope">
+        <annotation>
+            <documentation>
+                A match function that ensures that an attributes value's scope matches a scope given in metadata for the entity or role.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="afp:MatchFunctorType" />
+        </complexContent>
+    </complexType>
+</schema>
\ No newline at end of file
diff --git a/schemas/shibboleth-2.0-afp.xsd b/schemas/shibboleth-2.0-afp.xsd
new file mode 100644 (file)
index 0000000..9d3ad12
--- /dev/null
@@ -0,0 +1,184 @@
+<?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
+\r
+    <import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="classpath:/schema/xmldsig-core-schema.xsd" />\r
+\r
+    <annotation>
+        <documentation>Schema for the attribute filter policies.</documentation>
+    </annotation>
+
+    <element name="AttributeFilterPolicyGroup" type="afp:AttributeFilterPolicyGroupType">
+        <annotation>
+            <documentation>
+                Root element of the attribute filter policy. Represents a named group of filter policies.
+            </documentation>
+        </annotation>
+    </element>
+    <complexType name="AttributeFilterPolicyGroupType">
+        <complexContent>
+            <extension base="afp:IndentityType">
+                <sequence>
+                    <element ref="afp:PolicyRequirementRule" minOccurs="0" maxOccurs="unbounded">
+                        <annotation>
+                            <documentation>
+                                Defines a set of applications requirements that may be reused across multiple filter
+                                policies.
+                            </documentation>
+                        </annotation>
+                    </element>
+                    <element ref="afp:AttributeRule" minOccurs="0" maxOccurs="unbounded">
+                        <annotation>
+                            <documentation>
+                                Defines an attribute rule that may be reused across multiple filter policies.
+                            </documentation>
+                        </annotation>
+                    </element>
+                    <element ref="afp:PermitValueRule" minOccurs="0" maxOccurs="unbounded">
+                        <annotation>
+                            <documentation>
+                                Defines an attribute value filter that may be reused across multiple attribtue rules.
+                            </documentation>
+                        </annotation>
+                    </element>\r
+                    <element ref="afp:AttributeFilterPolicy" minOccurs="0" maxOccurs="unbounded">
+                        <annotation>
+                            <documentation>
+                                A policy that defines the set of attribute value filters that will be applied if its
+                                application requirements are met.
+                            </documentation>
+                        </annotation>
+                    </element>
+                    <element ref="ds:Signature" minOccurs="0">
+                        <annotation>
+                            <documentation>
+                                Digital signature for the policy. Policies that are fetched from an external source,
+                                such as a federation site, should be signed.
+                            </documentation>
+                        </annotation>
+                    </element>\r
+                </sequence>\r
+            </extension>
+        </complexContent>\r
+    </complexType>
+
+    <element name="AttributeFilterPolicy" type="afp:AttributeFilterPolicyType">
+        <annotation>
+            <documentation>
+                A policy that defines a set of attribute value filters rules that should be used if given requirements
+                are met.
+            </documentation>
+        </annotation>
+    </element>
+    <complexType name="AttributeFilterPolicyType">
+        <complexContent>
+            <extension base="afp:IndentityType">
+                <sequence>
+                    <choice>
+                        <element ref="afp:PolicyRequirementRule">
+                            <annotation>
+                                <documentation>
+                                    A requirement that if met signals that this filter policy should be used.
+                                </documentation>
+                            </annotation>
+                        </element>
+                        <element name="PolicyRequirementRuleReference" type="afp:ReferenceType">
+                            <annotation>
+                                <documentation>
+                                    Rerfence to a PolicyRequirement defined within this policy group or another.
+                                </documentation>
+                            </annotation>
+                        </element>
+                    </choice>
+                    <choice minOccurs="0" maxOccurs="unbounded">
+                        <element ref="afp:AttributeRule">
+                            <annotation>
+                                <documentation>
+                                    A rule that describes how values of an attribute will be filtered.
+                                </documentation>
+                            </annotation>
+                        </element>
+                        <element name="AttributeRuleReference" type="afp:ReferenceType">
+                            <annotation>
+                                <documentation>
+                                    Rerfence to a AttribtueRule defined within this policy group or another.
+                                </documentation>
+                            </annotation>
+                        </element>
+                    </choice>
+                </sequence>
+            </extension>
+        </complexContent>
+    </complexType>\r
+\r
+    <element name="AttributeRule" type="afp:AttributeRuleType">
+        <annotation>
+            <documentation>A rule that describes how values of an attribute will be filtered.</documentation>
+        </annotation>
+    </element>\r
+    <complexType name="AttributeRuleType">
+        <complexContent>
+            <extension base="afp:IndentityType">
+                <choice>
+                    <element ref="afp:PermitValueRule">
+                        <annotation>
+                            <documentation>
+                                A filter for attribute values. If the filter evaluates to true the value is permitted,
+                                otherwise it is filtered out.
+                            </documentation>
+                        </annotation>
+                    </element>
+                    <element name="PermitValueRuleReference" type="afp:ReferenceType">
+                        <annotation>
+                            <documentation>
+                                Rerfence to a PermitValueRule defined within this policy group or another.
+                            </documentation>
+                        </annotation>
+                    </element>
+                </choice>
+                <attribute name="attributeID" type="string" use="required">
+                    <annotation>
+                        <documentation>The ID of the attribute to which this rule applies.</documentation>
+                    </annotation>
+                </attribute>
+            </extension>
+        </complexContent>\r
+    </complexType>
+
+    <element name="PolicyRequirementRule" type="afp:MatchFunctorType">
+        <annotation>
+            <documentation>A requirement that if met signals that a filter policy should be used.</documentation>
+        </annotation>
+    </element>
+    <element name="PermitValueRule" type="afp:MatchFunctorType">
+        <annotation>
+            <documentation>
+                A filter for attribtue values. If the filter evaluates to true the value is permitted, otherwise it is
+                filtered out.
+            </documentation>
+        </annotation>
+    </element>
+    <complexType name="MatchFunctorType" abstract="true">
+        <complexContent>
+            <extension base="afp:IndentityType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="IndentityType">
+        <attribute name="id" type="string">
+            <annotation>
+                <documentation>An ID, unique within the policy and component type.</documentation>
+            </annotation>
+        </attribute>
+    </complexType>
+
+    <complexType name="ReferenceType">
+        <attribute name="ref" type="string">
+            <annotation>
+                <documentation>Used to reference a globally defined policy component.</documentation>
+            </annotation>
+        </attribute>
+    </complexType>
+
+</schema>
\ No newline at end of file
diff --git a/schemas/shibboleth-2.0-attribute-map.xsd b/schemas/shibboleth-2.0-attribute-map.xsd
new file mode 100644 (file)
index 0000000..caf782c
--- /dev/null
@@ -0,0 +1,117 @@
+<?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">
+
+    <annotation>
+        <documentation>
+            This schema maps SAML attributes into Shibboleth internal attributes.
+        </documentation>
+    </annotation>
+
+    <simpleType name="string">
+        <restriction base="string">
+            <minLength value="1"/>
+        </restriction>
+    </simpleType>
+    
+    <element name="Attributes">
+        <annotation>
+            <documentation>The set of SAML 1/2 attribute mappings.</documentation>
+        </annotation>
+        <complexType>
+            <sequence>
+                <element name="Attribute" type="am:AttributeType" maxOccurs="unbounded"/>
+            </sequence>
+        </complexType>
+    </element>
+
+    <complexType name="AttributeType">
+        <annotation>
+            <documentation>Rule for mapping a SAML 1/2 attribute to an internal attribute.</documentation>
+        </annotation>
+        <sequence>
+            <element name="AttributeDecoder" type="am:AttributeDecoderType" minOccurs="0"/>
+        </sequence>
+        <attribute name="id" type="am:string" use="required">
+            <annotation>
+                <documentation>The internal attribute ID to which this SAML attribute maps.</documentation>
+            </annotation>
+        </attribute>
+        <attribute name="name" type="am:string" use="required">
+            <annotation>
+                <documentation>The SAML 1 AttributeName or SAML 2 Name of the attribute.</documentation>
+            </annotation>
+        </attribute>
+        <attribute name="nameFormat" type="am:string">
+            <annotation>
+                <documentation>The SAML 1 Namespace or SAML 2 NameFormat of the attribute.</documentation>
+            </annotation>
+        </attribute>
+    </complexType>
+    
+    <complexType name="AttributeDecoderType" abstract="true">
+        <annotation>
+            <documentation>
+                Decodes a SAML attribute into its Shibboleth-internal representation.
+            </documentation>
+        </annotation>
+        <attribute name="caseSensitive" type="boolean" default="true">
+            <annotation>
+                <documentation>
+                    Flag controlling case sensitivity when comparisons to the attribute's values are done.
+                </documentation>
+            </annotation>
+        </attribute>
+    </complexType>
+    
+    <complexType name="StringAttributeDecoder">
+        <annotation>
+            <documentation>
+                Decoder for attributes with string values.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="am:AttributeDecoderType" />
+        </complexContent>
+    </complexType>
+
+    <complexType name="ScopedAttributeDecoder">
+        <annotation>
+            <documentation>
+                Decoder for attributes with scoped values.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="am:AttributeDecoderType">
+                <attribute name="scopeDelimiter" type="am:string" default="@">
+                    <annotation>
+                        <documentation>
+                            The character(s) used to delimit the scoped information from the scope.
+                        </documentation>
+                    </annotation>
+                </attribute>
+            </extension>
+        </complexContent>
+    </complexType>
+    
+    <complexType name="NameIDAttributeDecoder">
+        <annotation>
+            <documentation>
+                Decoder for attributes with NameID values.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="am:AttributeDecoderType">
+                <attribute name="formatter" type="am:string" default="$Name!!$NameQualifier!!$SPNameQualifier">
+                    <annotation>
+                        <documentation>
+                            The pattern used to generate string versions of the attribute's values.
+                        </documentation>
+                    </annotation>
+                </attribute>
+            </extension>
+        </complexContent>
+    </complexType>
+
+</schema>
\ No newline at end of file
diff --git a/schemas/shibboleth-2.0-attribute-resolver.xsd b/schemas/shibboleth-2.0-attribute-resolver.xsd
deleted file mode 100644 (file)
index 577e0a1..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-<schema targetNamespace="urn:mace:shibboleth:2.0:resolver" xmlns="http://www.w3.org/2001/XMLSchema"
-    xmlns:resolver="urn:mace:shibboleth:2.0:resolver">
-
-    <annotation>
-        <documentation>Shibboleth 2.0 Attribute Resolver configuration schema</documentation>
-    </annotation>
-
-    <element name="AttributeResolver" type="resolver:AttributeResolverType">
-        <annotation>
-            <documentation>Root of the attribute resolver configuration file.</documentation>
-        </annotation>
-        <keyref name="AttributeDefinitionAttributeDefinitionDependencyRef"
-            refer="resolver:AttributeDefinitionDependencyKey">
-            <selector xpath="./resolver:AttributeDefinition/resolver:AttributeDefinitionDependency" />
-            <field xpath="@ref" />
-        </keyref>
-        <keyref name="DataConnectorAttributeDefinitionDependencyRef"
-            refer="resolver:AttributeDefinitionDependencyKey">
-            <selector xpath="./resolver:DataConnector/resolver:AttributeDefinitionDependency" />
-            <field xpath="@ref" />
-        </keyref>
-        <keyref name="PrincipalConnectorAttributeDefinitionDependencyRef"
-            refer="resolver:AttributeDefinitionDependencyKey">
-            <selector xpath="./resolver:PrincipalConnector/resolver:AttributeDefinitionDependency" />
-            <field xpath="@ref" />
-        </keyref>
-        <key name="AttributeDefinitionDependencyKey">
-            <selector xpath="./resolver:AttributeDefinition" />
-            <field xpath="@id" />
-        </key>
-
-        <keyref name="AttributeDefinitionDataConnectorDependencyRef" refer="resolver:DataConnectorDependencyKey">
-            <selector xpath="./resolver:AttributeDefinition/resolver:DataConnectorDependency" />
-            <field xpath="@ref" />
-        </keyref>
-        <keyref name="DataConnectorDataConnectorDependencyRef" refer="resolver:DataConnectorDependencyKey">
-            <selector xpath="./resolver:DataConnector/resolver:DataConnectorDependency" />
-            <field xpath="@ref" />
-        </keyref>
-        <keyref name="FailoverDataConnectorDataConnectorDependencyRef" refer="resolver:DataConnectorDependencyKey">
-            <selector xpath="./resolver:DataConnector/resolver:FailoverDataConnector" />
-            <field xpath="@ref" />
-        </keyref>
-        <keyref name="PrincipalConnectorDataConnectorDependencyRef" refer="resolver:DataConnectorDependencyKey">
-            <selector xpath="./resolver:PrincipalConnector/resolver:DataConnectorDependency" />
-            <field xpath="@ref" />
-        </keyref>
-        <key name="DataConnectorDependencyKey">
-            <selector xpath="./resolver:DataConnector" />
-            <field xpath="@id" />
-        </key>
-    </element>
-    <complexType name="AttributeResolverType">
-        <choice minOccurs="0" maxOccurs="unbounded">
-            <element ref="resolver:AttributeDefinition" />
-            <element ref="resolver:DataConnector" />
-            <element ref="resolver:PrincipalConnector" />
-        </choice>
-    </complexType>
-
-    <element name="AttributeDefinition" type="resolver:BaseAttributeDefinitionType">
-        <annotation>
-            <documentation>Defines an attribute definition within this resolver.</documentation>
-        </annotation>
-    </element>
-    <complexType name="BaseAttributeDefinitionType">
-        <annotation>
-            <documentation>
-                Attribute definition define the finished attributes to be released by the resolver.
-            </documentation>
-        </annotation>
-        <complexContent>
-            <extension base="resolver:BaseResolutionPlugInType">
-                <sequence>
-                    <element ref="resolver:AttributeEncoder" minOccurs="0" maxOccurs="unbounded" />
-                </sequence>
-                <attribute name="dependencyOnly" type="boolean" default="false">
-                    <annotation>
-                        <documentation>
-                            A boolean flag that indicates whether this attribute definition is only defined becaue its
-                            data is needed elsewhere within the resolver and as such should not be released outside the
-                            resolver.
-                        </documentation>
-                    </annotation>
-                </attribute>
-            </extension>
-        </complexContent>
-    </complexType>
-
-    <element name="AttributeEncoder" type="resolver:BaseAttributeEncoderType">
-        <annotation>
-            <documentation>Defines an encoder for an attribute.</documentation>
-        </annotation>
-    </element>
-    <complexType name="BaseAttributeEncoderType">
-        <annotation>
-            <documentation>
-                An attribute encoder is responsible for converting an attribute, and it's values, into a protocol
-                specific representation such as a SAML 1 Attribute or a SAML 2 NameID.
-            </documentation>
-        </annotation>
-    </complexType>
-
-    <element name="DataConnector" type="resolver:BaseDataConnectorType">
-        <annotation>
-            <documentation>
-                Defines a data connector which is used to pull information from local infrastructure.
-            </documentation>
-        </annotation>
-    </element>
-    <complexType name="BaseDataConnectorType">
-        <annotation>
-            <documentation>
-                Data connectors pull information from local infrastructure, such as databases and LDAP directories, and
-                make these raw attributes available to attribute definitions for finishing.
-            </documentation>
-        </annotation>
-        <complexContent>
-            <extension base="resolver:BaseResolutionPlugInType">
-                <sequence>
-                    <element ref="resolver:FailoverDataConnector" minOccurs="0" maxOccurs="unbounded" />
-                </sequence>
-            </extension>
-        </complexContent>
-    </complexType>
-
-    <element name="PrincipalConnector" type="resolver:BasePrincipalConnectorType">
-        <annotation>
-            <documentation>
-                Principal connectors convert a subject's NameID into an internal principal name for use within attribute
-                definitions and data connectors.
-            </documentation>
-        </annotation>
-    </element>
-    <complexType name="BasePrincipalConnectorType">
-        <annotation>
-            <documentation>
-                Principal connectors convert a subject's NameID into an internal principal name for use within attribute
-                definitions and data connectors.
-            </documentation>
-        </annotation>
-        <complexContent>
-            <extension base="resolver:BaseResolutionPlugInType">
-                <sequence>
-                    <element name="RelyingParty" type="string" minOccurs="0" maxOccurs="unbounded">
-                        <annotation>
-                            <documentation>The relying party for which this connector is valid.</documentation>
-                        </annotation>
-                    </element>
-                </sequence>
-                <attribute name="nameIDFormat" type="anyURI" use="required">
-                    <annotation>
-                        <documentation>The format of the NameID for which this connector is valid.</documentation>
-                    </annotation>
-                </attribute>
-            </extension>
-        </complexContent>
-    </complexType>
-
-    <complexType name="BaseResolutionPlugInType" abstract="true">
-        <annotation>
-            <documentation>
-                A base type for all attribute resolver plugins: data and principal connectors and attribute definitions
-            </documentation>
-        </annotation>
-        <choice minOccurs="0" maxOccurs="unbounded">
-            <element ref="resolver:AttributeDefinitionDependency" minOccurs="0" maxOccurs="unbounded" />
-            <element ref="resolver:DataConnectorDependency" minOccurs="0" maxOccurs="unbounded" />
-        </choice>
-        <attribute name="id" type="string" use="required">
-            <annotation>
-                <documentation>A unique identifier for this definition.</documentation>
-            </annotation>
-        </attribute>
-        <attribute name="propagateErrors" type="boolean" default="true">
-            <annotation>
-                <documentation>
-                    A boolean flag indiciating whether errors within a definition should be propogated outside the
-                    resolver. Errors exposed outside the resolver will likely result in an error being returned to the
-                    relying party.
-                </documentation>
-            </annotation>
-        </attribute>
-    </complexType>
-
-    <element name="AttributeDefinitionDependency" type="resolver:PluginDependencyType">
-        <annotation>
-            <documentation>Defines a dependency on a specific attribute definition.</documentation>
-        </annotation>
-    </element>
-    <element name="DataConnectorDependency" type="resolver:PluginDependencyType">
-        <annotation>
-            <documentation>Defines a dependency on a specific data connector.</documentation>
-        </annotation>
-    </element>
-    <element name="FailoverDataConnector" type="resolver:PluginDependencyType">
-        <annotation>
-            <documentation>Defines a data connector to use should the parent data connector fail.</documentation>
-        </annotation>
-    </element>
-    <complexType name="PluginDependencyType">
-        <annotation>
-            <documentation>A type that represents a reference to another plugin</documentation>
-        </annotation>
-        <attribute name="ref" type="string" use="required" />
-    </complexType>
-
-</schema>
\ No newline at end of file
similarity index 95%
rename from schemas/shibboleth-spconfig-2.0.xsd
rename to schemas/shibboleth-2.0-native-sp-config.xsd
index f0f2fcb..3cbd1fe 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="US-ASCII"?>\r
-<schema targetNamespace="urn:mace:shibboleth:sp:config:2.0"\r
+<schema targetNamespace="urn:mace:shibboleth:2.0:native:sp:config"\r
        xmlns="http://www.w3.org/2001/XMLSchema"\r
        xmlns:conf="urn:mace:shibboleth:sp:config:2.0"\r
        xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"\r
@@ -16,7 +16,7 @@
 \r
        <annotation>\r
                <documentation>\r
-       2.0 schema for XML-based configuration of Shibboleth SP instances.\r
+       2.0 schema for XML-based configuration of Shibboleth Native SP instances.\r
        First appearing in Shibboleth 2.0 release.\r
        </documentation>\r
     </annotation>\r
                                <element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>
                 <element name="MetadataProvider" type="conf:PluggableType"/>
                                <element name="TrustEngine" type="conf:PluggableType"/>\r
-                               <element name="AttributeResolver" type="conf:PluggableType"/>\r
+                               <element name="AttributeExtractor" type="conf:PluggableType" minOccurs="0"/>\r
+                               <element name="AttributeResolver" type="conf:PluggableType" minOccurs="0"/>\r
+                               <element name="AttributeFilter" type="conf:PluggableType" minOccurs="0"/>\r
                                <element name="CredentialResolver" type="conf:PluggableType" minOccurs="0"/>\r
                                <element ref="conf:Application" minOccurs="0" maxOccurs="unbounded"/>\r
                        </sequence>\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="attributeIds" type="conf:listOfStrings"/>\r
                <anyAttribute namespace="##other" processContents="lax"/>\r
                </complexType>\r
        </element>\r
                                <element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>\r
                                <element name="MetadataProvider" type="conf:PluggableType" minOccurs="0"/>\r
                                <element name="TrustEngine" type="conf:PluggableType" minOccurs="0"/>\r
+                               <element name="AttributeExtractor" type="conf:PluggableType" minOccurs="0"/>\r
                                <element name="AttributeResolver" type="conf:PluggableType" minOccurs="0"/>\r
+                               <element name="AttributeFilter" type="conf:PluggableType" minOccurs="0"/>\r
                                <element name="CredentialResolver" type="conf:PluggableType" minOccurs="0"/>\r
                        </sequence>\r
                        <attribute name="id" type="conf:string" use="required"/>\r
                        <attribute name="entityID" type="anyURI"/>\r
                        <attribute name="policyId" type="conf:string"/>\r
                        <attribute name="homeURL" type="anyURI" default="/"/>\r
-                       <attribute name="attributeIds" type="conf:listOfStrings"/>\r
                        <anyAttribute namespace="##other" processContents="lax"/>\r
                </complexType>\r
        </element>\r
diff --git a/schemas/shibboleth-2.0-simple-resolver.xsd b/schemas/shibboleth-2.0-simple-resolver.xsd
deleted file mode 100644 (file)
index cf3710f..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<schema targetNamespace="urn:mace:shibboleth:2.0:resolver:simple" xmlns="http://www.w3.org/2001/XMLSchema"
-    xmlns:resolver="urn:mace:shibboleth:2.0:resolver:simple"
-    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
-
-    <annotation>
-        <documentation>Shibboleth 2.0 Simple Attribute Resolver configuration schema</documentation>
-    </annotation>
-
-    <import namespace="urn:oasis:names:tc:SAML:2.0:assertion" schemaLocation="saml-schema-assertion-2.0.xsd"/>
-    
-    <element name="AttributeResolver" type="resolver:AttributeResolverType">
-        <annotation>
-            <documentation>Root of the simple attribute resolver configuration file.</documentation>
-        </annotation>
-    </element>
-    <complexType name="AttributeResolverType">
-        <sequence>
-            <element ref="resolver:AttributeDecoder" maxOccurs="unbounded"/>
-            <element ref="saml:Attribute" maxOccurs="unbounded"/>
-        </sequence>
-        <attribute name="allowQuery" type="boolean" default="true"/>
-    </complexType>
-
-    <element name="AttributeDecoder">
-        <annotation>
-            <documentation>Instantiates decoders for use by Attribute definitions.</documentation>
-        </annotation>
-        <complexType>
-            <sequence>
-                <any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
-            </sequence>
-            <attribute name="type" type="resolver:string" use="required"/>
-            <attribute name="id" type="ID" use="required"/>
-            <anyAttribute namespace="##any" processContents="lax"/>
-        </complexType>
-    </element>
-
-    <simpleType name="string">
-        <restriction base="string">
-            <minLength value="1"/>
-        </restriction>
-    </simpleType>
-
-    <attribute name="decoderId" type="IDREF"/>
-</schema>
index f4ca169..1ad17bd 100644 (file)
@@ -30,6 +30,7 @@
 
 namespace shibsp {
     
+    class SHIBSP_API AttributeExtractor;
     class SHIBSP_API AttributeResolver;
     class SHIBSP_API Handler;
     class SHIBSP_API ServiceProvider;
@@ -93,18 +94,18 @@ namespace shibsp {
         virtual xmltooling::TrustEngine* getTrustEngine() const=0;
 
         /**
-         * Returns an AttributeResolver for use with this Application.
+         * Returns an AttributeExtractor for use with this Application.
          * 
-         * @return  an AttributeResolver, or NULL
+         * @return  an AttributeExtractor, or NULL
          */
-        virtual AttributeResolver* getAttributeResolver() const=0;
+        virtual AttributeExtractor* getAttributeExtractor() const=0;
 
         /**
-         * Returns a set of attribute IDs to resolve for the Application.
-         *
-         * @return  a set of attribute IDs, or an empty set
+         * Returns an AttributeResolver for use with this Application.
+         * 
+         * @return  an AttributeResolver, or NULL
          */
-        virtual const std::set<std::string>* getAttributeIds() const=0;
+        virtual AttributeResolver* getAttributeResolver() const=0;
 
         /**
          * Returns the CredentialResolver instance associated with this Application.
index d8450ba..f57fc3c 100644 (file)
@@ -46,6 +46,7 @@ attrinclude_HEADERS = \
        attribute/SimpleAttribute.h
 
 attrresinclude_HEADERS = \
+       attribute/resolver/AttributeExtractor.h \
        attribute/resolver/AttributeResolver.h \
        attribute/resolver/ResolutionContext.h
 
@@ -89,10 +90,10 @@ libshibsp_la_SOURCES = \
        SPConfig.cpp \
        attribute/Attribute.cpp \
        attribute/NameIDAttributeDecoder.cpp \
-       attribute/SimpleAttributeDecoder.cpp \
        attribute/ScopedAttributeDecoder.cpp \
-       attribute/resolver/impl/AttributeResolver.cpp \
-       attribute/resolver/impl/SimpleAttributeResolver.cpp \
+       attribute/StringAttributeDecoder.cpp \
+       attribute/resolver/impl/QueryAttributeResolver.cpp \
+       attribute/resolver/impl/XMLAttributeExtractor.cpp \
        binding/impl/ArtifactResolver.cpp \
        binding/impl/SOAPClient.cpp \
        handler/impl/AbstractHandler.cpp \
index 1de2dd4..e01a617 100644 (file)
@@ -29,6 +29,7 @@
 #include "SessionCache.h"
 #include "SPConfig.h"
 #include "attribute/AttributeDecoder.h"
+#include "attribute/resolver/AttributeExtractor.h"
 #include "attribute/resolver/AttributeResolver.h"
 #include "binding/ArtifactResolver.h"
 #include "handler/SessionInitiator.h"
@@ -47,6 +48,8 @@ using namespace xmltooling;
 using namespace log4cpp;
 
 DECL_XMLTOOLING_EXCEPTION_FACTORY(AttributeException,shibsp);
+DECL_XMLTOOLING_EXCEPTION_FACTORY(AttributeExtractionException,shibsp);
+DECL_XMLTOOLING_EXCEPTION_FACTORY(AttributeFilteringException,shibsp);
 DECL_XMLTOOLING_EXCEPTION_FACTORY(AttributeResolutionException,shibsp);
 DECL_XMLTOOLING_EXCEPTION_FACTORY(ConfigurationException,shibsp);
 DECL_XMLTOOLING_EXCEPTION_FACTORY(ListenerException,shibsp);
@@ -99,6 +102,8 @@ bool SPInternalConfig::init(const char* catalog_path)
     XMLToolingConfig::getConfig().getTemplateEngine()->setTagPrefix("shibmlp");
     
     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(AttributeException,shibsp);
+    REGISTER_XMLTOOLING_EXCEPTION_FACTORY(AttributeExtractionException,shibsp);
+    REGISTER_XMLTOOLING_EXCEPTION_FACTORY(AttributeFilteringException,shibsp);
     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(AttributeResolutionException,shibsp);
     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(ConfigurationException,shibsp);
     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(ListenerException,shibsp);
@@ -108,6 +113,7 @@ bool SPInternalConfig::init(const char* catalog_path)
 
     registerAccessControls();
     registerAttributeDecoders();
+    registerAttributeExtractors();
     registerAttributeFactories();
     registerAttributeResolvers();
     registerHandlers();
@@ -145,8 +151,9 @@ void SPInternalConfig::term()
     RequestMapperManager.deregisterFactories();
     ListenerServiceManager.deregisterFactories();
     HandlerManager.deregisterFactories();
-    AttributeResolverManager.deregisterFactories();
     AttributeDecoderManager.deregisterFactories();
+    AttributeExtractorManager.deregisterFactories();
+    AttributeResolverManager.deregisterFactories();
     Attribute::deregisterFactories();
     AccessControlManager.deregisterFactories();
 
index f56dd9f..4abaaa5 100644 (file)
@@ -36,6 +36,7 @@ namespace shibsp {
 
     class SHIBSP_API AccessControl;
     class SHIBSP_API AttributeDecoder;
+    class SHIBSP_API AttributeExtractor;
     class SHIBSP_API AttributeResolver;
     class SHIBSP_API Handler;
     class SHIBSP_API ListenerService;
@@ -165,62 +166,67 @@ namespace shibsp {
         /**
          * Manages factories for AccessControl plugins.
          */
-        xmltooling::PluginManager<AccessControl,const xercesc::DOMElement*> AccessControlManager;
+        xmltooling::PluginManager<AccessControl,std::string,const xercesc::DOMElement*> AccessControlManager;
 
         /**
          * Manages factories for AttributeDecoder plugins.
          */
-        xmltooling::PluginManager<AttributeDecoder,const xercesc::DOMElement*> AttributeDecoderManager;
+        xmltooling::PluginManager<AttributeDecoder,xmltooling::QName,const xercesc::DOMElement*> AttributeDecoderManager;
+
+        /**
+         * Manages factories for AttributeExtractor plugins.
+         */
+        xmltooling::PluginManager<AttributeExtractor,std::string,const xercesc::DOMElement*> AttributeExtractorManager;
 
         /**
          * Manages factories for AttributeResolver plugins.
          */
-        xmltooling::PluginManager<AttributeResolver,const xercesc::DOMElement*> AttributeResolverManager;
+        xmltooling::PluginManager<AttributeResolver,std::string,const xercesc::DOMElement*> AttributeResolverManager;
 
         /**
          * Manages factories for Handler plugins that implement AssertionConsumerService functionality.
          */
-        xmltooling::PluginManager< Handler,std::pair<const xercesc::DOMElement*,const char*> > AssertionConsumerServiceManager;
+        xmltooling::PluginManager< Handler,std::string,std::pair<const xercesc::DOMElement*,const char*> > AssertionConsumerServiceManager;
 
         /**
          * Manages factories for Handler plugins that implement customized functionality.
          */
-        xmltooling::PluginManager< Handler,std::pair<const xercesc::DOMElement*,const char*> > HandlerManager;
+        xmltooling::PluginManager< Handler,std::string,std::pair<const xercesc::DOMElement*,const char*> > HandlerManager;
 
         /**
          * Manages factories for ListenerService plugins.
          */
-        xmltooling::PluginManager<ListenerService,const xercesc::DOMElement*> ListenerServiceManager;
+        xmltooling::PluginManager<ListenerService,std::string,const xercesc::DOMElement*> ListenerServiceManager;
 
         /**
          * Manages factories for Handler plugins that implement ManageNameIDService functionality.
          */
-        xmltooling::PluginManager< Handler,std::pair<const xercesc::DOMElement*,const char*> > ManageNameIDServiceManager;
+        xmltooling::PluginManager< Handler,std::string,std::pair<const xercesc::DOMElement*,const char*> > ManageNameIDServiceManager;
 
         /**
          * Manages factories for RequestMapper plugins.
          */
-        xmltooling::PluginManager<RequestMapper,const xercesc::DOMElement*> RequestMapperManager;
+        xmltooling::PluginManager<RequestMapper,std::string,const xercesc::DOMElement*> RequestMapperManager;
 
         /**
          * Manages factories for ServiceProvider plugins.
          */
-        xmltooling::PluginManager<ServiceProvider,const xercesc::DOMElement*> ServiceProviderManager;
+        xmltooling::PluginManager<ServiceProvider,std::string,const xercesc::DOMElement*> ServiceProviderManager;
 
         /**
          * Manages factories for SessionCache plugins.
          */
-        xmltooling::PluginManager<SessionCache,const xercesc::DOMElement*> SessionCacheManager;
+        xmltooling::PluginManager<SessionCache,std::string,const xercesc::DOMElement*> SessionCacheManager;
 
         /**
          * Manages factories for Handler plugins that implement SessionInitiator functionality.
          */
-        xmltooling::PluginManager< SessionInitiator,std::pair<const xercesc::DOMElement*,const char*> > SessionInitiatorManager;
+        xmltooling::PluginManager< SessionInitiator,std::string,std::pair<const xercesc::DOMElement*,const char*> > SessionInitiatorManager;
 
         /**
          * Manages factories for Handler plugins that implement SingleLogoutService functionality.
          */
-        xmltooling::PluginManager< Handler,std::pair<const xercesc::DOMElement*,const char*> > SingleLogoutServiceManager;
+        xmltooling::PluginManager< Handler,std::string,std::pair<const xercesc::DOMElement*,const char*> > SingleLogoutServiceManager;
 
     protected:
         SPConfig() : attribute_value_delimeter(';'), m_serviceProvider(NULL), m_artifactResolver(NULL), m_features(0) {}
index 01e2ce9..30eb28f 100644 (file)
@@ -46,7 +46,7 @@ using namespace xmltooling;
 using namespace std;
 
 namespace shibsp {
-    SHIBSP_DLLLOCAL PluginManager<ServiceProvider,const DOMElement*>::Factory XMLServiceProviderFactory;
+    SHIBSP_DLLLOCAL PluginManager<ServiceProvider,string,const DOMElement*>::Factory XMLServiceProviderFactory;
 
     long SHIBSP_DLLLOCAL sendError(SPRequest& request, const Application* app, const char* page, TemplateParameters& tp, bool mayRedirect=false)
     {
@@ -376,13 +376,13 @@ pair<bool,long> ServiceProvider::doExport(SPRequest& request, bool requireSessio
         }
 
         // Export the attributes.
-        const map<string,const Attribute*>& attributes = session->getAttributes();
-        for (map<string,const Attribute*>::const_iterator a = attributes.begin(); a!=attributes.end(); ++a) {
+        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 (!strcmp(a->second->getId(), "REMOTE_USER") && !vals.empty())
+            if (a->first == "REMOTE_USER" && !vals.empty())
                 request.setRemoteUser(vals.front().c_str());
             else {
-                string header(request.getSecureHeader(a->second->getId()));
+                string header(request.getSecureHeader(a->first.c_str()));
                 for (vector<string>::const_iterator v = vals.begin(); v!=vals.end(); ++v) {
                     if (!header.empty())
                         header += ";";
@@ -399,7 +399,7 @@ pair<bool,long> ServiceProvider::doExport(SPRequest& request, bool requireSessio
                         header += (*v);
                     }
                 }
-                request.setHeader(a->second->getId(), header.c_str());
+                request.setHeader(a->first.c_str(), header.c_str());
             }
         }
     
index 89e6c9e..bbd19a0 100644 (file)
@@ -28,8 +28,8 @@ using namespace xmltooling;
 \r
 namespace shibsp {\r
 \r
-    SHIBSP_DLLLOCAL PluginManager<SessionCache,const DOMElement*>::Factory RemotedCacheFactory;\r
-    SHIBSP_DLLLOCAL PluginManager<SessionCache,const DOMElement*>::Factory StorageServiceCacheFactory;\r
+    SHIBSP_DLLLOCAL PluginManager<SessionCache,std::string,const DOMElement*>::Factory RemotedCacheFactory;\r
+    SHIBSP_DLLLOCAL PluginManager<SessionCache,std::string,const DOMElement*>::Factory StorageServiceCacheFactory;\r
 \r
     static const XMLCh cacheTimeout[] =     UNICODE_LITERAL_12(c,a,c,h,e,T,i,m,e,o,u,t);\r
 }\r
index 3b35712..341c224 100644 (file)
@@ -98,7 +98,7 @@ namespace shibsp {
          * 
          * @return an immutable map of attributes keyed by attribute ID
          */
-        virtual const std::map<std::string,const Attribute*>& getAttributes() const=0;
+        virtual const std::multimap<std::string,Attribute*>& getAttributes() const=0;
         
         /**
          * Adds additional attributes to the session.
@@ -181,7 +181,7 @@ namespace shibsp {
          * @param authncontext_class    method/category of authentication event, if known
          * @param authncontext_decl specifics of authentication event, if known
          * @param tokens            assertions to cache with session, if any
-         * @param attributes        optional set of resolved Attributes to cache with session
+         * @param attributes        optional map of resolved Attributes to cache with session
          * @return  newly created session's key
          */
         virtual std::string insert(
@@ -195,7 +195,7 @@ namespace shibsp {
             const char* authncontext_class=NULL,
             const char* authncontext_decl=NULL,
             const std::vector<const opensaml::Assertion*>* tokens=NULL,
-            const std::vector<Attribute*>* attributes=NULL
+            const std::multimap<std::string,Attribute*>* attributes=NULL
             )=0;
 
         /**
index cda7400..3809e97 100644 (file)
  */
 
 #include "internal.h"
+#include "SPConfig.h"
 #include "attribute/AttributeDecoder.h"
 #include "attribute/SimpleAttribute.h"
 #include "attribute/ScopedAttribute.h"
 #include "attribute/NameIDAttribute.h"
+#include "util/SPConstants.h"
 
-#include <shibsp/SPConfig.h>
+#include <xercesc/util/XMLUniDefs.hpp>
 
 using namespace shibsp;
 using namespace xmltooling;
@@ -46,17 +48,27 @@ namespace shibsp {
         return new NameIDAttribute(in);
     }
     
-    SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,const DOMElement*>::Factory SimpleAttributeDecoderFactory;
-    SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,const DOMElement*>::Factory ScopedAttributeDecoderFactory;
-    SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,const DOMElement*>::Factory NameIDAttributeDecoderFactory;
+    SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,QName,const DOMElement*>::Factory StringAttributeDecoderFactory;
+    SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,QName,const DOMElement*>::Factory ScopedAttributeDecoderFactory;
+    SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,QName,const DOMElement*>::Factory NameIDAttributeDecoderFactory;
+
+    static const XMLCh _StringAttributeDecoder[] = UNICODE_LITERAL_22(S,t,r,i,n,g,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
+    static const XMLCh _ScopedAttributeDecoder[] = UNICODE_LITERAL_22(S,c,o,p,e,d,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
+    static const XMLCh _NameIDAttributeDecoder[] = UNICODE_LITERAL_22(N,a,m,e,I,D,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
+
+    static const XMLCh caseSensitive[] =           UNICODE_LITERAL_13(c,a,s,e,S,e,n,s,i,t,i,v,e);
 };
 
+QName shibsp::StringAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _StringAttributeDecoder);
+QName shibsp::ScopedAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _ScopedAttributeDecoder);
+QName shibsp::NameIDAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _NameIDAttributeDecoder);
+
 void shibsp::registerAttributeDecoders()
 {
     SPConfig& conf = SPConfig::getConfig();
-    conf.AttributeDecoderManager.registerFactory(SIMPLE_ATTRIBUTE_DECODER, SimpleAttributeDecoderFactory);
-    conf.AttributeDecoderManager.registerFactory(SCOPED_ATTRIBUTE_DECODER, ScopedAttributeDecoderFactory);
-    conf.AttributeDecoderManager.registerFactory(NAMEID_ATTRIBUTE_DECODER, NameIDAttributeDecoderFactory);
+    conf.AttributeDecoderManager.registerFactory(StringAttributeDecoderType, StringAttributeDecoderFactory);
+    conf.AttributeDecoderManager.registerFactory(ScopedAttributeDecoderType, ScopedAttributeDecoderFactory);
+    conf.AttributeDecoderManager.registerFactory(NameIDAttributeDecoderType, NameIDAttributeDecoderFactory);
 }
 
 void shibsp::registerAttributeFactories()
@@ -76,3 +88,12 @@ Attribute* Attribute::unmarshall(DDF& in)
         throw AttributeException("No registered factory for Attribute of type ($1).", xmltooling::params(1,in.name()));
     return (i->second)(in);
 }
+
+AttributeDecoder::AttributeDecoder(const DOMElement *e) : m_caseSensitive(true)
+{
+    if (e) {
+        const XMLCh* flag = e->getAttributeNS(NULL,caseSensitive);
+        if (flag && (*flag == chLatin_f || *flag == chDigit_0))
+            m_caseSensitive = false;
+    }
+}
index 3a23ca5..0b282ad 100644 (file)
@@ -59,7 +59,7 @@ namespace shibsp {
          * 
          * @param id    Attribute identifier 
          */
-        Attribute(const char* id) : m_id(id ? id : "") {
+        Attribute(const char* id) : m_id(id ? id : ""), m_caseSensitive(true) {
         }
 
         /**
@@ -71,7 +71,7 @@ namespace shibsp {
          * 
          * @param in    input object containing marshalled Attribute
          */
-        Attribute(DDF& in) {
+        Attribute(DDF& in) : m_caseSensitive(in["case_insensitive"].isnull()) {
             const char* id = in.first().name();
             if (id && *id)
                 m_id = id;
@@ -100,12 +100,21 @@ namespace shibsp {
         }
 
         /**
+         * Sets whether case sensitivity should apply to basic value comparisons.
+         *
+         * @param caseSensitive  true iff value comparisons should be case sensitive
+         */
+        void setCaseSensitive(bool caseSensitive) {
+            m_caseSensitive = caseSensitive;
+        }
+
+        /**
          * Indicates whether case sensitivity should apply to basic value comparisons.
          *
          * @return  true iff value comparisons should be case sensitive
          */
-        virtual bool isCaseSensitive() const {
-            return true;
+        bool isCaseSensitive() const {
+            return m_caseSensitive;
         }
         
         /**
@@ -143,6 +152,8 @@ namespace shibsp {
         virtual DDF marshall() const {
             DDF ddf(NULL);
             ddf.structure().addmember(m_id.c_str()).list();
+            if (!m_caseSensitive)
+                ddf.addmember("case_insensitive");
             return ddf;
         }
         
@@ -186,6 +197,7 @@ namespace shibsp {
     private:
         static std::map<std::string,AttributeFactory*> m_factoryMap;
         std::string m_id;
+        bool m_caseSensitive;
     };
 
 #if defined (_MSC_VER)
index e11ce75..3f9c85b 100644 (file)
@@ -35,7 +35,16 @@ namespace shibsp {
     {
         MAKE_NONCOPYABLE(AttributeDecoder);
     protected:
-        AttributeDecoder() {}
+        /**
+         * Constructor.
+         *
+         * @param e root of DOM to configure the decoder
+         */
+        AttributeDecoder(const xercesc::DOMElement* e);
+
+        /** Flag for case sensitivity of decoded attributes. */
+        bool m_caseSensitive;
+
     public:
         virtual ~AttributeDecoder() {}
         
@@ -53,14 +62,15 @@ namespace shibsp {
             ) const=0;
     };
 
-    /** Decodes SimpleAttributes */
-    #define SIMPLE_ATTRIBUTE_DECODER "Simple"
+
+    /** Decodes into a SimpleAttribute. */
+    extern SHIBSP_API xmltooling::QName StringAttributeDecoderType;
     
-    /** Decodes ScopedAttributes */
-    #define SCOPED_ATTRIBUTE_DECODER "Scoped"
+    /** Decodes into a ScopedAttribute. */
+    extern SHIBSP_API xmltooling::QName ScopedAttributeDecoderType;
 
-    /** Decodes NameIDAttributes */
-    #define NAMEID_ATTRIBUTE_DECODER "NameID"
+    /** Decodes into a NameIDAttribute. */
+    extern SHIBSP_API xmltooling::QName NameIDAttributeDecoderType;
 
     /** Registers built-in AttributeDecoders into the runtime. */
     void registerAttributeDecoders();
index 2a4beef..832a327 100644 (file)
@@ -41,7 +41,7 @@ namespace shibsp {
     class SHIBSP_DLLLOCAL NameIDAttributeDecoder : virtual public AttributeDecoder\r
     {\r
     public:\r
-        NameIDAttributeDecoder(const DOMElement* e) : m_formatter(e ? e->getAttributeNS(NULL,formatter) : NULL) {}\r
+        NameIDAttributeDecoder(const DOMElement* e) : AttributeDecoder(e), m_formatter(e ? e->getAttributeNS(NULL,formatter) : NULL) {}\r
         ~NameIDAttributeDecoder() {}\r
 \r
         shibsp::Attribute* decode(\r
@@ -71,6 +71,7 @@ shibsp::Attribute* NameIDAttributeDecoder::decode(
     auto_ptr<NameIDAttribute> nameid(\r
         new NameIDAttribute(id, (m_formatter.get() && *m_formatter.get()) ? m_formatter.get() : DEFAULT_NAMEID_FORMATTER)\r
         );\r
+    nameid->setCaseSensitive(m_caseSensitive);\r
     vector<NameIDAttribute::Value>& dest = nameid->getValues();\r
     vector<XMLObject*>::const_iterator v,stop;\r
 \r
index 98b01bd..7adfaec 100644 (file)
@@ -50,14 +50,14 @@ namespace shibsp {
          * 
          * @param id    Attribute identifier
          */
-        ScopedAttribute(const char* id) : Attribute(id), m_caseSensitive(true) {}
+        ScopedAttribute(const char* id) : Attribute(id) {}
 
         /**
          * Constructs based on a remoted ScopedAttribute.
          * 
          * @param in    input object containing marshalled ScopedAttribute
          */
-        ScopedAttribute(DDF& in) : Attribute(in), m_caseSensitive(in["case_insensitive"].isnull()) {
+        ScopedAttribute(DDF& in) : Attribute(in) {
             DDF val = in.first().first();
             while (val.name() && val.string()) {
                 m_values.push_back(std::make_pair(val.name(), val.string()));
@@ -67,10 +67,6 @@ namespace shibsp {
         
         virtual ~ScopedAttribute() {}
 
-        bool isCaseSensitive() const {
-            return m_caseSensitive;
-        }
-
         /**
          * Returns the set of values encoded as UTF-8 strings.
          * 
@@ -101,8 +97,6 @@ namespace shibsp {
         DDF marshall() const {
             DDF ddf = Attribute::marshall();
             ddf.name("Scoped");
-            if (!m_caseSensitive)
-                ddf.addmember("case_insensitive");
             DDF vlist = ddf.first();
             for (std::vector< std::pair<std::string,std::string> >::const_iterator i=m_values.begin(); i!=m_values.end(); ++i) {
                 DDF val = DDF(i->first.c_str()).string(i->second.c_str());
@@ -112,7 +106,6 @@ namespace shibsp {
         }
     
     private:
-        bool m_caseSensitive;
         std::vector< std::pair<std::string,std::string> > m_values;
     };
 
index 312f3d4..464f482 100644 (file)
@@ -36,23 +36,34 @@ using namespace log4cpp;
 using namespace std;\r
 \r
 namespace shibsp {\r
+    static const XMLCh Scope[] =            UNICODE_LITERAL_5(S,c,o,p,e);\r
+    static const XMLCh scopeDelimeter[] =   UNICODE_LITERAL_14(s,c,o,p,e,D,e,l,i,m,e,t,e,r);\r
+\r
     class SHIBSP_DLLLOCAL ScopedAttributeDecoder : virtual public AttributeDecoder\r
     {\r
     public:\r
-        ScopedAttributeDecoder(const DOMElement* e) {}\r
+        ScopedAttributeDecoder(const DOMElement* e) : AttributeDecoder(e), m_delimeter('@') {\r
+            if (e) {\r
+                if (e->hasAttributeNS(NULL,scopeDelimeter)) {\r
+                    auto_ptr_char d(e->getAttributeNS(NULL,scopeDelimeter));\r
+                    m_delimeter = *(d.get());\r
+                }\r
+            }\r
+        }\r
         ~ScopedAttributeDecoder() {}\r
 \r
         shibsp::Attribute* decode(\r
             const char* id, const XMLObject* xmlObject, const char* assertingParty=NULL, const char* relyingParty=NULL\r
             ) const;\r
+\r
+    private:\r
+        char m_delimeter;\r
     };\r
 \r
     AttributeDecoder* SHIBSP_DLLLOCAL ScopedAttributeDecoderFactory(const DOMElement* const & e)\r
     {\r
         return new ScopedAttributeDecoder(e);\r
     }\r
-\r
-    static const XMLCh Scope[] = UNICODE_LITERAL_5(S,c,o,p,e);\r
 };\r
 \r
 shibsp::Attribute* ScopedAttributeDecoder::decode(\r
@@ -64,6 +75,7 @@ shibsp::Attribute* ScopedAttributeDecoder::decode(
     const XMLCh* xmlscope;\r
     QName scopeqname(NULL,Scope);\r
     auto_ptr<ScopedAttribute> scoped(new ScopedAttribute(id));\r
+    scoped->setCaseSensitive(m_caseSensitive);\r
     vector< pair<string,string> >& dest = scoped->getValues();\r
     vector<XMLObject*>::const_iterator v,stop;\r
 \r
@@ -109,7 +121,7 @@ shibsp::Attribute* ScopedAttributeDecoder::decode(
                         delete[] scope;\r
                     }\r
                     else {\r
-                        scope = strchr(val, '@');\r
+                        scope = strchr(val, m_delimeter);\r
                         if (scope) {\r
                             *scope++ = 0;\r
                             if (*scope)\r
@@ -158,8 +170,8 @@ shibsp::Attribute* ScopedAttributeDecoder::decode(
         }\r
     }\r
 \r
-    if (val && *val && *val!='@') {\r
-        scope = strchr(val, '@');\r
+    if (val && *val && *val!=m_delimeter) {\r
+        scope = strchr(val, m_delimeter);\r
         if (scope) {\r
             *scope++ = 0;\r
             if (*scope)\r
@@ -168,7 +180,7 @@ shibsp::Attribute* ScopedAttributeDecoder::decode(
                 log.warn("ignoring NameID with no scope");\r
         }\r
         else {\r
-            log.warn("ignoring NameID with no scope delimiter (@)");\r
+            log.warn("ignoring NameID with no scope delimiter (%c)", m_delimeter);\r
         }\r
     }\r
     else {\r
index 0f2bd15..452beaf 100644 (file)
@@ -38,14 +38,14 @@ namespace shibsp {
          * 
          * @param id    Attribute identifier
          */
-        SimpleAttribute(const char* id) : Attribute(id), m_caseSensitive(true) {}
+        SimpleAttribute(const char* id) : Attribute(id) {}
 
         /**
          * Constructs based on a remoted SimpleAttribute.
          * 
          * @param in    input object containing marshalled SimpleAttribute
          */
-        SimpleAttribute(DDF& in) : Attribute(in), m_caseSensitive(in["case_insensitive"].isnull()) {
+        SimpleAttribute(DDF& in) : Attribute(in) {
             DDF val = in.first().first();
             while (val.string()) {
                 m_serialized.push_back(val.string());
@@ -55,10 +55,6 @@ namespace shibsp {
         
         virtual ~SimpleAttribute() {}
 
-        bool isCaseSensitive() const {
-            return m_caseSensitive;
-        }
-
         /**
          * Returns the set of values encoded as UTF-8 strings.
          * 
@@ -77,16 +73,11 @@ namespace shibsp {
         
         DDF marshall() const {
             DDF ddf = Attribute::marshall();
-            if (!m_caseSensitive)
-                ddf.addmember("case_insensitive");
             DDF vlist = ddf.first();
             for (std::vector<std::string>::const_iterator i=m_serialized.begin(); i!=m_serialized.end(); ++i)
                 vlist.add(DDF(NULL).string(i->c_str()));
             return ddf;
         }
-
-    private:
-        bool m_caseSensitive;
     };
 
 };
similarity index 86%
rename from shibsp/attribute/SimpleAttributeDecoder.cpp
rename to shibsp/attribute/StringAttributeDecoder.cpp
index 1bcf8e6..4e0bcfb 100644 (file)
@@ -15,7 +15,7 @@
  */\r
 \r
 /**\r
- * SimpleAttributeDecoder.cpp\r
+ * StringAttributeDecoder.cpp\r
  * \r
  * Decodes SAML into SimpleAttributes\r
  */\r
@@ -36,29 +36,30 @@ using namespace log4cpp;
 using namespace std;\r
 \r
 namespace shibsp {\r
-    class SHIBSP_DLLLOCAL SimpleAttributeDecoder : virtual public AttributeDecoder\r
+    class SHIBSP_DLLLOCAL StringAttributeDecoder : virtual public AttributeDecoder\r
     {\r
     public:\r
-        SimpleAttributeDecoder(const DOMElement* e) {}\r
-        ~SimpleAttributeDecoder() {}\r
+        StringAttributeDecoder(const DOMElement* e) : AttributeDecoder(e) {}\r
+        ~StringAttributeDecoder() {}\r
 \r
         shibsp::Attribute* decode(\r
             const char* id, const XMLObject* xmlObject, const char* assertingParty=NULL, const char* relyingParty=NULL\r
             ) const;\r
     };\r
 \r
-    AttributeDecoder* SHIBSP_DLLLOCAL SimpleAttributeDecoderFactory(const DOMElement* const & e)\r
+    AttributeDecoder* SHIBSP_DLLLOCAL StringAttributeDecoderFactory(const DOMElement* const & e)\r
     {\r
-        return new SimpleAttributeDecoder(e);\r
+        return new StringAttributeDecoder(e);\r
     }\r
 };\r
 \r
-shibsp::Attribute* SimpleAttributeDecoder::decode(\r
+shibsp::Attribute* StringAttributeDecoder::decode(\r
     const char* id, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty\r
     ) const\r
 {\r
     char* val;\r
     auto_ptr<SimpleAttribute> simple(new SimpleAttribute(id));\r
+    simple->setCaseSensitive(m_caseSensitive);\r
     vector<string>& dest = simple->getValues();\r
     vector<XMLObject*>::const_iterator v,stop;\r
 \r
@@ -87,7 +88,7 @@ shibsp::Attribute* SimpleAttributeDecoder::decode(
                 }\r
             }\r
             else {\r
-                log.warn("XMLObject type not recognized by SimpleAttributeDecoder, no values returned");\r
+                log.warn("XMLObject type not recognized by StringAttributeDecoder, no values returned");\r
                 return NULL;\r
             }\r
         }\r
@@ -127,7 +128,7 @@ shibsp::Attribute* SimpleAttributeDecoder::decode(
             val = toUTF8(saml1name->getName());\r
         }\r
         else {\r
-            log.warn("XMLObject type not recognized by SimpleAttributeDecoder, no values returned");\r
+            log.warn("XMLObject type not recognized by StringAttributeDecoder, no values returned");\r
             return NULL;\r
         }\r
     }\r
diff --git a/shibsp/attribute/resolver/AttributeExtractor.h b/shibsp/attribute/resolver/AttributeExtractor.h
new file mode 100644 (file)
index 0000000..80ffb85
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *  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/resolver/AttributeExtractor.h
+ * 
+ * A service that extracts and decodes attributes from XML objects.
+ */
+
+#ifndef __shibsp_extractor_h__
+#define __shibsp_extractor_h__
+
+#include <shibsp/base.h>
+
+#include <saml/saml2/metadata/Metadata.h>
+#include <xmltooling/Lockable.h>
+
+namespace shibsp {
+
+    class SHIBSP_API Application;
+    class SHIBSP_API Attribute;
+
+    /**
+     * A service that extracts and decodes attributes from XML objects.
+     */
+    class SHIBSP_API AttributeExtractor : public virtual xmltooling::Lockable
+    {
+        MAKE_NONCOPYABLE(AttributeExtractor);
+    protected:
+        AttributeExtractor() {}
+    public:
+        virtual ~AttributeExtractor() {}
+
+        /**
+         * Extracts the attributes found in an XMLObject.
+         * 
+         * @param application   Application performing the extraction
+         * @param issuer        source of object, if known
+         * @param xmlObject     object to extract
+         * @param attributes    a map to populate with the extracted attributes, keyed by id
+         * 
+         * @throws AttributeExtractionException thrown if there is a problem extracting attributes
+         */
+        virtual void extractAttributes(
+            const Application& application,
+            const opensaml::saml2md::RoleDescriptor* issuer,
+            const xmltooling::XMLObject& xmlObject,
+            std::multimap<std::string,Attribute*>& attributes
+            ) const=0;
+    };
+
+    /**
+     * Registers AttributeExtractor classes into the runtime.
+     */
+    void SHIBSP_API registerAttributeExtractors();
+
+    /** AttributeExtractor based on an XML mapping schema. */
+    #define XML_ATTRIBUTE_EXTRACTOR "XML"
+};
+
+#endif /* __shibsp_extractor_h__ */
index e570c51..bcef3ee 100644 (file)
@@ -17,7 +17,7 @@
 /**
  * @file shibsp/attribute/resolver/AttributeResolver.h
  * 
- * The service that resolves the attributes for a particular subject.
+ * A service that transforms or resolves additional attributes for a particular subject.
  */
 
 #ifndef __shibsp_resolver_h__
@@ -59,18 +59,18 @@ namespace shibsp {
          * Attributes can be supplied while creating the session.
          * 
          * @param application       reference to Application that owns the eventual Session
-         * @param client_addr       network address of client
          * @param issuer            issuing metadata of assertion issuer, if known
          * @param nameid            principal identifier, normalized to SAML 2, if any
          * @param tokens            assertions initiating the session, if any
+         * @param attributes        map of previously resolved attributes, if any
          * @return  newly created ResolutionContext, owned by caller
          */
         virtual ResolutionContext* createResolutionContext(
             const Application& application,
-            const char* client_addr,
             const opensaml::saml2md::EntityDescriptor* issuer,
             const opensaml::saml2::NameID* nameid,
-            const std::vector<const opensaml::Assertion*>* tokens=NULL
+            const std::vector<const opensaml::Assertion*>* tokens=NULL,
+            const std::multimap<std::string,Attribute*>* attributes=NULL
             ) const=0;
 
         /**
@@ -84,14 +84,13 @@ namespace shibsp {
         
 
         /**
-         * Gets the attributes for a given subject and returns them in the supplied context.
+         * Resolves attributes for a given subject and returns them in the supplied context.
          * 
          * @param ctx           resolution context to use to resolve attributes
-         * @param attributes    set of attributes to resolve or NULL to resolve all attributes
          * 
          * @throws AttributeResolutionException thrown if there is a problem resolving the attributes for the subject
          */
-        virtual void resolveAttributes(ResolutionContext& ctx, const std::set<std::string>* attributes=NULL) const=0;
+        virtual void resolveAttributes(ResolutionContext& ctx) const=0;
     };
 
 #if defined (_MSC_VER)
@@ -103,8 +102,8 @@ namespace shibsp {
      */
     void SHIBSP_API registerAttributeResolvers();
 
-    /** AttributeResolver based on a simple mapping of SAML information. */
-    #define SIMPLE_ATTRIBUTE_RESOLVER "Simple"
+    /** AttributeResolver based on SAML queries to an IdP during SSO. */
+    #define QUERY_ATTRIBUTE_RESOLVER "Query"
 };
 
 #endif /* __shibsp_resolver_h__ */
index 980824a..f791b4f 100644 (file)
@@ -26,7 +26,6 @@
 #include <shibsp/base.h>
 
 #include <saml/Assertion.h>
-#include <saml/saml2/metadata/Metadata.h>
 
 namespace shibsp {
 
@@ -44,50 +43,6 @@ namespace shibsp {
         ResolutionContext() {}
     public:
         virtual ~ResolutionContext() {}
-
-        /**
-         * Returns the application resolving the attributes.
-         * 
-         * @return  the resolving application
-         */
-        virtual const Application& getApplication() const=0;
-        
-        /**
-         * Returns the address of the client associated with the subject.
-         * 
-         * @return  the client's network address
-         */
-        virtual const char* getClientAddress() const=0;
-
-        /**
-         * Returns the metadata for the IdP associated with the subject, if any.
-         * 
-         * @return the IdP's metadata, or NULL
-         */
-        virtual const opensaml::saml2md::EntityDescriptor* getEntityDescriptor() const=0;
-
-        /**
-         * Returns the NameID associated with the subject
-         * 
-         * <p>SAML 1.x identifiers will be promoted to the 2.0 type.
-         * 
-         * @return a SAML 2.0 NameID associated with the subject, if any
-         */
-        virtual const opensaml::saml2::NameID* getNameID() const=0;
-
-        /**
-         * Returns unresolved tokens associated with the subject, if any.
-         * 
-         * @return pointer to an array of tokens, or NULL
-         */
-        virtual const std::vector<const opensaml::Assertion*>* getTokens() const=0;
-        
-        /**
-         * Returns the active session associated with the subject, if any.
-         * 
-         * @return the active, locked session, or NULL
-         */
-        virtual const Session* getSession() const=0;
         
         /**
          * Returns the set of Attributes resolved and added to the context.
@@ -98,7 +53,7 @@ namespace shibsp {
          * 
          * @return  a mutable array of Attributes.
          */
-        virtual std::vector<Attribute*>& getResolvedAttributes()=0;
+        virtual std::multimap<std::string,Attribute*>& getResolvedAttributes()=0;
 
         /**
          * Returns the set of assertions resolved and added to the context.
diff --git a/shibsp/attribute/resolver/impl/AttributeResolver.cpp b/shibsp/attribute/resolver/impl/AttributeResolver.cpp
deleted file mode 100644 (file)
index 7050cc6..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- *  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.
- */
-
-/**
- * AttributeResolver.cpp
- * 
- * The service that resolves the attributes for a particular subject.
- */
-
-#include "internal.h"
-#include "attribute/resolver/AttributeResolver.h"
-
-#include <xercesc/dom/DOM.hpp>
-
-using namespace shibsp;
-using namespace xmltooling;
-using namespace xercesc;
-using namespace std;
-
-namespace shibsp {
-    SHIBSP_DLLLOCAL PluginManager<AttributeResolver,const DOMElement*>::Factory SimpleAttributeResolverFactory;
-};
-
-void SHIBSP_API shibsp::registerAttributeResolvers()
-{
-    SPConfig& conf=SPConfig::getConfig();
-    conf.AttributeResolverManager.registerFactory(SIMPLE_ATTRIBUTE_RESOLVER, SimpleAttributeResolverFactory);
-}
diff --git a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp
new file mode 100644 (file)
index 0000000..2c45326
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ *  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.
+ */
+
+/**
+ * QueryAttributeResolver.cpp
+ * 
+ * AttributeResolver based on SAML queries.
+ */
+
+#include "internal.h"
+#include "Application.h"
+#include "ServiceProvider.h"
+#include "SessionCache.h"
+#include "attribute/Attribute.h"
+#include "attribute/resolver/AttributeExtractor.h"
+#include "attribute/resolver/AttributeResolver.h"
+#include "attribute/resolver/ResolutionContext.h"
+#include "binding/SOAPClient.h"
+#include "util/SPConstants.h"
+
+#include <log4cpp/Category.hh>
+#include <saml/exceptions.h>
+#include <saml/binding/SecurityPolicy.h>
+#include <saml/saml1/binding/SAML1SOAPClient.h>
+#include <saml/saml1/core/Assertions.h>
+#include <saml/saml1/core/Protocols.h>
+#include <saml/saml1/profile/AssertionValidator.h>
+#include <saml/saml2/binding/SAML2SOAPClient.h>
+#include <saml/saml2/core/Protocols.h>
+#include <saml/saml2/metadata/Metadata.h>
+#include <saml/saml2/metadata/MetadataProvider.h>
+#include <saml/saml2/profile/AssertionValidator.h>
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/XMLHelper.h>
+#include <xercesc/util/XMLUniDefs.hpp>
+
+using namespace shibsp;
+using namespace opensaml::saml1;
+using namespace opensaml::saml1p;
+using namespace opensaml::saml2;
+using namespace opensaml::saml2p;
+using namespace opensaml::saml2md;
+using namespace opensaml;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace shibsp {
+
+    class SHIBSP_DLLLOCAL QueryContext : public ResolutionContext
+    {
+    public:
+        QueryContext(const Application& application, const Session& session)
+            : m_query(true), m_app(application), m_session(&session), m_metadata(NULL), m_entity(NULL), m_nameid(session.getNameID()) {
+        }
+        
+        QueryContext(
+            const Application& application,
+            const EntityDescriptor* issuer,
+            const NameID* nameid,
+            const vector<const opensaml::Assertion*>* tokens=NULL,
+            const multimap<string,Attribute*>* attributes=NULL
+            ) : m_query(true), m_app(application), m_session(NULL), m_metadata(NULL), m_entity(issuer), m_nameid(nameid) {
+
+            if (tokens) {
+                for (vector<const opensaml::Assertion*>::const_iterator t = tokens->begin(); t!=tokens->end(); ++t) {
+                    const saml2::Assertion* token2 = dynamic_cast<const saml2::Assertion*>(*t);
+                    if (token2 && !token2->getAttributeStatements().empty()) {
+                        m_query = false;
+                    }
+                    else {
+                        const saml1::Assertion* token1 = dynamic_cast<const saml1::Assertion*>(*t);
+                        if (token1 && !token1->getAttributeStatements().empty()) {
+                            m_query = false;
+                        }
+                    }
+                }
+            }
+        }
+        
+        ~QueryContext() {
+            if (m_metadata)
+                m_metadata->unlock();
+            for_each(m_attributes.begin(), m_attributes.end(), cleanup_pair<string,shibsp::Attribute>());
+            for_each(m_assertions.begin(), m_assertions.end(), xmltooling::cleanup<opensaml::Assertion>());
+        }
+    
+        bool doQuery() const {
+            return m_query;
+        }
+
+        const Application& getApplication() const {
+            return m_app;
+        }
+        const EntityDescriptor* getEntityDescriptor() const {
+            if (m_entity)
+                return m_entity;
+            if (m_session && m_session->getEntityID()) {
+                m_metadata = m_app.getMetadataProvider();
+                if (m_metadata) {
+                    m_metadata->lock();
+                    return m_entity = m_metadata->getEntityDescriptor(m_session->getEntityID());
+                }
+            }
+            return NULL;
+        }
+        const NameID* getNameID() const {
+            return m_nameid;
+        }
+        const Session* getSession() const {
+            return m_session;
+        }        
+        multimap<string,shibsp::Attribute*>& getResolvedAttributes() {
+            return m_attributes;
+        }
+        vector<opensaml::Assertion*>& getResolvedAssertions() {
+            return m_assertions;
+        }
+
+    private:
+        bool m_query;
+        const Application& m_app;
+        const Session* m_session;
+        mutable MetadataProvider* m_metadata;
+        mutable const EntityDescriptor* m_entity;
+        const NameID* m_nameid;
+        multimap<string,shibsp::Attribute*> m_attributes;
+        vector<opensaml::Assertion*> m_assertions;
+    };
+    
+    class SHIBSP_DLLLOCAL QueryResolver : public AttributeResolver
+    {
+    public:
+        QueryResolver(const DOMElement* e);
+        ~QueryResolver() {
+            for_each(m_SAML1Designators.begin(), m_SAML1Designators.end(), xmltooling::cleanup<AttributeDesignator>());
+            for_each(m_SAML2Designators.begin(), m_SAML2Designators.end(), xmltooling::cleanup<saml2::Attribute>());
+        }
+        
+        ResolutionContext* createResolutionContext(
+            const Application& application,
+            const EntityDescriptor* issuer,
+            const NameID* nameid,
+            const vector<const opensaml::Assertion*>* tokens=NULL,
+            const multimap<string,shibsp::Attribute*>* attributes=NULL
+            ) const {
+            return new QueryContext(application,issuer,nameid,tokens,attributes);
+        }
+
+        ResolutionContext* createResolutionContext(const Application& application, const Session& session) const {
+            return new QueryContext(application,session);
+        }
+
+        Lockable* lock() {return this;}
+        void unlock() {}
+        
+        void resolveAttributes(ResolutionContext& ctx) const;
+
+    private:
+        bool SAML1Query(QueryContext& ctx) const;
+        bool SAML2Query(QueryContext& ctx) const;
+
+        Category& m_log;
+        vector<AttributeDesignator*> m_SAML1Designators;
+        vector<saml2::Attribute*> m_SAML2Designators;
+    };
+
+    AttributeResolver* SHIBSP_DLLLOCAL QueryResolverFactory(const DOMElement* const & e)
+    {
+        return new QueryResolver(e);
+    }
+    
+};
+
+void SHIBSP_API shibsp::registerAttributeResolvers()
+{
+    SPConfig::getConfig().AttributeResolverManager.registerFactory(QUERY_ATTRIBUTE_RESOLVER, QueryResolverFactory);
+}
+
+QueryResolver::QueryResolver(const DOMElement* e) : m_log(Category::getInstance(SHIBSP_LOGCAT".AttributeResolver"))
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("QueryResolver");
+#endif
+    
+    DOMElement* child = XMLHelper::getFirstChildElement(e);
+    while (child) {
+        try {
+            if (XMLHelper::isNodeNamed(e, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME)) {
+                auto_ptr<XMLObject> obj(saml2::AttributeBuilder::buildOneFromElement(child));
+                saml2::Attribute* down = dynamic_cast<saml2::Attribute*>(obj.get());
+                if (down) {
+                    m_SAML2Designators.push_back(down);
+                    obj.release();
+                }
+            }
+            else if (XMLHelper::isNodeNamed(e, samlconstants::SAML1P_NS, AttributeDesignator::LOCAL_NAME)) {
+                auto_ptr<XMLObject> obj(AttributeDesignatorBuilder::buildOneFromElement(child));
+                AttributeDesignator* down = dynamic_cast<AttributeDesignator*>(obj.get());
+                if (down) {
+                    m_SAML1Designators.push_back(down);
+                    obj.release();
+                }
+            }
+        }
+        catch (exception& ex) {
+            m_log.error("exception loading attribute designator: %s", ex.what());
+        }
+        child = XMLHelper::getNextSiblingElement(child);
+    }
+}
+
+bool QueryResolver::SAML1Query(QueryContext& ctx) const
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("query");
+#endif
+
+    int version = 1;
+    const AttributeAuthorityDescriptor* AA = ctx.getEntityDescriptor()->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+    if (!AA) {
+        AA = ctx.getEntityDescriptor()->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+        version = 0;
+    }
+    if (!AA) {
+        m_log.info("no SAML 1.x AttributeAuthority role found in metadata");
+        return false;
+    }
+
+    shibsp::SecurityPolicy policy(ctx.getApplication());
+    MetadataCredentialCriteria mcc(*AA);
+    shibsp::SOAPClient soaper(policy);
+    const PropertySet* policySettings =
+        ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);
+    pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");
+
+    auto_ptr_XMLCh binding(samlconstants::SAML1_BINDING_SOAP);
+    saml1p::Response* response=NULL;
+    const vector<AttributeService*>& endpoints=AA->getAttributeServices();
+    for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {
+        try {
+            if (!XMLString::equals((*ep)->getBinding(),binding.get()))
+                continue;
+            auto_ptr_char loc((*ep)->getLocation());
+            auto_ptr_XMLCh issuer(ctx.getApplication().getString("entityID").second);
+            NameIdentifier* nameid = NameIdentifierBuilder::buildNameIdentifier();
+            nameid->setName(ctx.getNameID()->getName());
+            nameid->setFormat(ctx.getNameID()->getFormat());
+            nameid->setNameQualifier(ctx.getNameID()->getNameQualifier());
+            saml1::Subject* subject = saml1::SubjectBuilder::buildSubject();
+            subject->setNameIdentifier(nameid);
+            saml1p::AttributeQuery* query = saml1p::AttributeQueryBuilder::buildAttributeQuery();
+            query->setSubject(subject);
+            query->setResource(issuer.get());
+            for (vector<AttributeDesignator*>::const_iterator ad = m_SAML1Designators.begin(); ad!=m_SAML1Designators.end(); ++ad)
+                query->getAttributeDesignators().push_back((*ad)->cloneAttributeDesignator());
+            Request* request = RequestBuilder::buildRequest();
+            request->setAttributeQuery(query);
+            request->setMinorVersion(version);
+
+            SAML1SOAPClient client(soaper);
+            client.sendSAML(request, mcc, loc.get());
+            response = client.receiveSAML();
+        }
+        catch (exception& ex) {
+            m_log.error("exception making SAML query: %s", ex.what());
+            soaper.reset();
+        }
+    }
+
+    if (!response) {
+        m_log.error("unable to successfully query for attributes");
+        return false;
+    }
+
+    const vector<saml1::Assertion*>& assertions = const_cast<const saml1p::Response*>(response)->getAssertions();
+    if (assertions.size()>1)
+        m_log.warn("simple resolver only supports one assertion in the query response");
+
+    auto_ptr<saml1p::Response> wrapper(response);
+    saml1::Assertion* newtoken = assertions.front();
+
+    if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {
+        m_log.error("assertion unsigned, rejecting it based on signedAssertions policy");
+        return true;
+    }
+
+    try {
+        policy.evaluate(*newtoken);
+        if (!policy.isSecure())
+            throw SecurityPolicyException("Security of SAML 1.x query result not established.");
+        saml1::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));
+        tokval.validateAssertion(*newtoken);
+    }
+    catch (exception& ex) {
+        m_log.error("assertion failed policy/validation: %s", ex.what());
+        return true;
+    }
+
+    newtoken->detach();
+    wrapper.release();
+    ctx.getResolvedAssertions().push_back(newtoken);
+
+    // Finally, extract and filter the result.
+    try {
+        AttributeExtractor* extractor = ctx.getApplication().getAttributeExtractor();
+        if (extractor) {
+            Locker extlocker(extractor);
+            extractor->extractAttributes(ctx.getApplication(), AA, *newtoken, ctx.getResolvedAttributes());
+        }
+    }
+    catch (exception& ex) {
+        m_log.error("caught exception extracting/filtering attributes from query result: %s", ex.what());
+    }
+
+    return true;
+}
+
+bool QueryResolver::SAML2Query(QueryContext& ctx) const
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("query");
+#endif
+
+    const AttributeAuthorityDescriptor* AA = ctx.getEntityDescriptor()->getAttributeAuthorityDescriptor(samlconstants::SAML20P_NS);
+    if (!AA) {
+        m_log.info("no SAML 2 AttributeAuthority role found in metadata");
+        return false;
+    }
+
+    shibsp::SecurityPolicy policy(ctx.getApplication());
+    MetadataCredentialCriteria mcc(*AA);
+    shibsp::SOAPClient soaper(policy);
+    const PropertySet* policySettings =
+        ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);
+    pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");
+
+    auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);
+    saml2p::StatusResponseType* srt=NULL;
+    const vector<AttributeService*>& endpoints=AA->getAttributeServices();
+    for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !srt && ep!=endpoints.end(); ++ep) {
+        try {
+            if (!XMLString::equals((*ep)->getBinding(),binding.get()))
+                continue;
+            auto_ptr_char loc((*ep)->getLocation());
+            auto_ptr_XMLCh issuer(ctx.getApplication().getString("entityID").second);
+            saml2::Subject* subject = saml2::SubjectBuilder::buildSubject();
+            subject->setNameID(ctx.getNameID()->cloneNameID());
+            saml2p::AttributeQuery* query = saml2p::AttributeQueryBuilder::buildAttributeQuery();
+            query->setSubject(subject);
+            Issuer* iss = IssuerBuilder::buildIssuer();
+            iss->setName(issuer.get());
+            query->setIssuer(iss);
+            for (vector<saml2::Attribute*>::const_iterator ad = m_SAML2Designators.begin(); ad!=m_SAML2Designators.end(); ++ad)
+                query->getAttributes().push_back((*ad)->cloneAttribute());
+
+            SAML2SOAPClient client(soaper);
+            client.sendSAML(query, mcc, loc.get());
+            srt = client.receiveSAML();
+        }
+        catch (exception& ex) {
+            m_log.error("exception making SAML query: %s", ex.what());
+            soaper.reset();
+        }
+    }
+
+    if (!srt) {
+        m_log.error("unable to successfully query for attributes");
+        return false;
+    }
+    saml2p::Response* response = dynamic_cast<saml2p::Response*>(srt);
+    if (!response) {
+        delete srt;
+        m_log.error("message was not a samlp:Response");
+        return true;
+    }
+
+    const vector<saml2::Assertion*>& assertions = const_cast<const saml2p::Response*>(response)->getAssertions();
+    if (assertions.size()>1)
+        m_log.warn("simple resolver only supports one assertion in the query response");
+
+    auto_ptr<saml2p::Response> wrapper(response);
+    saml2::Assertion* newtoken = assertions.front();
+
+    if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {
+        m_log.error("assertion unsigned, rejecting it based on signedAssertions policy");
+        return true;
+    }
+
+    try {
+        policy.evaluate(*newtoken);
+        if (!policy.isSecure())
+            throw SecurityPolicyException("Security of SAML 2.0 query result not established.");
+        saml2::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));
+        tokval.validateAssertion(*newtoken);
+    }
+    catch (exception& ex) {
+        m_log.error("assertion failed policy/validation: %s", ex.what());
+        return true;
+    }
+
+    newtoken->detach();
+    wrapper.release();
+    ctx.getResolvedAssertions().push_back(newtoken);
+
+    // Finally, extract and filter the result.
+    try {
+        AttributeExtractor* extractor = ctx.getApplication().getAttributeExtractor();
+        if (extractor) {
+            Locker extlocker(extractor);
+            extractor->extractAttributes(ctx.getApplication(), AA, *newtoken, ctx.getResolvedAttributes());
+        }
+    }
+    catch (exception& ex) {
+        m_log.error("caught exception extracting/filtering attributes from query result: %s", ex.what());
+    }
+
+    return true;
+}
+
+void QueryResolver::resolveAttributes(ResolutionContext& ctx) const
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("resolveAttributes");
+#endif
+
+    QueryContext& qctx = dynamic_cast<QueryContext&>(ctx);
+    if (!qctx.doQuery()) {
+        m_log.debug("found AttributeStatement in input to new session, skipping query");
+        return;
+    }
+
+    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);
+        }
+    }
+    m_log.warn("can't attempt attribute query, either no NameID or no metadata to use");
+}
diff --git a/shibsp/attribute/resolver/impl/SimpleAttributeResolver.cpp b/shibsp/attribute/resolver/impl/SimpleAttributeResolver.cpp
deleted file mode 100644 (file)
index 6cae72c..0000000
+++ /dev/null
@@ -1,792 +0,0 @@
-/*\r
- *  Copyright 2001-2007 Internet2\r
- * \r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/**\r
- * SimpleAttributeResolver.cpp\r
- * \r
- * AttributeResolver based on a simple mapping of SAML information.\r
- */\r
-\r
-#include "internal.h"\r
-#include "Application.h"\r
-#include "ServiceProvider.h"\r
-#include "SessionCache.h"\r
-#include "attribute/AttributeDecoder.h"\r
-#include "attribute/resolver/AttributeResolver.h"\r
-#include "attribute/resolver/ResolutionContext.h"\r
-#include "binding/SOAPClient.h"\r
-#include "util/SPConstants.h"\r
-\r
-#include <saml/binding/SecurityPolicy.h>\r
-#include <saml/saml1/binding/SAML1SOAPClient.h>\r
-#include <saml/saml1/core/Assertions.h>\r
-#include <saml/saml1/core/Protocols.h>\r
-#include <saml/saml1/profile/AssertionValidator.h>\r
-#include <saml/saml2/binding/SAML2SOAPClient.h>\r
-#include <saml/saml2/core/Protocols.h>\r
-#include <saml/saml2/metadata/Metadata.h>\r
-#include <saml/saml2/metadata/MetadataProvider.h>\r
-#include <saml/saml2/profile/AssertionValidator.h>\r
-#include <xmltooling/util/NDC.h>\r
-#include <xmltooling/util/ReloadableXMLFile.h>\r
-#include <xmltooling/util/XMLHelper.h>\r
-#include <xercesc/util/XMLUniDefs.hpp>\r
-\r
-using namespace shibsp;\r
-using namespace opensaml::saml1;\r
-using namespace opensaml::saml1p;\r
-using namespace opensaml::saml2;\r
-using namespace opensaml::saml2p;\r
-using namespace opensaml::saml2md;\r
-using namespace opensaml;\r
-using namespace xmltooling;\r
-using namespace log4cpp;\r
-using namespace std;\r
-\r
-namespace shibsp {\r
-\r
-    class SHIBSP_DLLLOCAL SimpleContext : public ResolutionContext\r
-    {\r
-    public:\r
-        SimpleContext(const Application& application, const Session& session)\r
-            : m_app(application), m_session(&session), m_client_addr(NULL), m_metadata(NULL), m_entity(NULL),\r
-                m_nameid(session.getNameID()), m_tokens(NULL) {\r
-        }\r
-        \r
-        SimpleContext(\r
-            const Application& application,\r
-            const char* client_addr,\r
-            const EntityDescriptor* issuer,\r
-            const NameID* nameid,\r
-            const vector<const opensaml::Assertion*>* tokens=NULL\r
-            ) : m_app(application), m_session(NULL), m_client_addr(client_addr), m_metadata(NULL), m_entity(issuer),\r
-                m_nameid(nameid), m_tokens(tokens) {\r
-        }\r
-        \r
-        ~SimpleContext() {\r
-            if (m_metadata)\r
-                m_metadata->unlock();\r
-            for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<shibsp::Attribute>());\r
-            for_each(m_assertions.begin(), m_assertions.end(), xmltooling::cleanup<opensaml::Assertion>());\r
-        }\r
-    \r
-        const Application& getApplication() const {\r
-            return m_app;\r
-        }\r
-        const char* getClientAddress() const {\r
-            return m_session ? m_session->getClientAddress() : m_client_addr;\r
-        }\r
-        const EntityDescriptor* getEntityDescriptor() const {\r
-            if (m_entity)\r
-                return m_entity;\r
-            if (m_session && m_session->getEntityID()) {\r
-                m_metadata = m_app.getMetadataProvider();\r
-                if (m_metadata) {\r
-                    m_metadata->lock();\r
-                    return m_entity = m_metadata->getEntityDescriptor(m_session->getEntityID());\r
-                }\r
-            }\r
-            return NULL;\r
-        }\r
-        const NameID* getNameID() const {\r
-            return m_nameid;\r
-        }\r
-        const vector<const opensaml::Assertion*>* getTokens() const {\r
-            return m_tokens;\r
-        }\r
-        const Session* getSession() const {\r
-            return m_session;\r
-        }        \r
-        vector<shibsp::Attribute*>& getResolvedAttributes() {\r
-            return m_attributes;\r
-        }\r
-        vector<opensaml::Assertion*>& getResolvedAssertions() {\r
-            return m_assertions;\r
-        }\r
-\r
-    private:\r
-        const Application& m_app;\r
-        const Session* m_session;\r
-        const char* m_client_addr;\r
-        mutable MetadataProvider* m_metadata;\r
-        mutable const EntityDescriptor* m_entity;\r
-        const NameID* m_nameid;\r
-        const vector<const opensaml::Assertion*>* m_tokens;\r
-        vector<shibsp::Attribute*> m_attributes;\r
-        vector<opensaml::Assertion*> m_assertions;\r
-    };\r
-    \r
-#if defined (_MSC_VER)\r
-    #pragma warning( push )\r
-    #pragma warning( disable : 4250 )\r
-#endif\r
-\r
-    class SimpleResolverImpl\r
-    {\r
-    public:\r
-        SimpleResolverImpl(const DOMElement* e, Category& log);\r
-        ~SimpleResolverImpl() {\r
-            for_each(m_decoderMap.begin(), m_decoderMap.end(), cleanup_pair<string,AttributeDecoder>());\r
-            if (m_document)\r
-                m_document->release();\r
-        }\r
-\r
-        void setDocument(DOMDocument* doc) {\r
-            m_document = doc;\r
-        }\r
-\r
-        void query(\r
-            ResolutionContext& ctx, const NameIdentifier& nameid, const set<string>* attributes=NULL\r
-            ) const;\r
-        void query(\r
-            ResolutionContext& ctx, const NameID& nameid, const set<string>* attributes=NULL\r
-            ) const;\r
-        void resolve(\r
-            ResolutionContext& ctx, const saml1::Assertion* token, const set<string>* attributes=NULL\r
-            ) const;\r
-        void resolve(\r
-            ResolutionContext& ctx, const saml2::Assertion* token, const set<string>* attributes=NULL\r
-            ) const;\r
-\r
-        bool m_allowQuery;\r
-\r
-    private:\r
-        void populateQuery(saml1p::AttributeQuery& query, const string& id) const;\r
-        void populateQuery(saml2p::AttributeQuery& query, const string& id) const;\r
-\r
-        Category& m_log;\r
-        DOMDocument* m_document;\r
-        map<string,AttributeDecoder*> m_decoderMap;\r
-#ifdef HAVE_GOOD_STL\r
-        typedef map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> > attrmap_t;\r
-#else\r
-        typedef map< pair<string,string>,pair<const AttributeDecoder*,string> > attrmap_t;\r
-#endif\r
-        attrmap_t m_attrMap;\r
-    };\r
-    \r
-    class SimpleResolver : public AttributeResolver, public ReloadableXMLFile\r
-    {\r
-    public:\r
-        SimpleResolver(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".AttributeResolver")), m_impl(NULL) {\r
-            load();\r
-        }\r
-        ~SimpleResolver() {\r
-            delete m_impl;\r
-        }\r
-        \r
-        ResolutionContext* createResolutionContext(\r
-            const Application& application,\r
-            const char* client_addr,\r
-            const EntityDescriptor* issuer,\r
-            const NameID* nameid,\r
-            const vector<const opensaml::Assertion*>* tokens=NULL\r
-            ) const {\r
-            return new SimpleContext(application,client_addr,issuer,nameid,tokens);\r
-        }\r
-\r
-        ResolutionContext* createResolutionContext(const Application& application, const Session& session) const {\r
-            return new SimpleContext(application,session);\r
-        }\r
-        \r
-        void resolveAttributes(ResolutionContext& ctx, const set<string>* attributes=NULL) const;\r
-\r
-    protected:\r
-        pair<bool,DOMElement*> load();\r
-        SimpleResolverImpl* m_impl;\r
-    };\r
-\r
-#if defined (_MSC_VER)\r
-    #pragma warning( pop )\r
-#endif\r
-\r
-    AttributeResolver* SHIBSP_DLLLOCAL SimpleAttributeResolverFactory(const DOMElement* const & e)\r
-    {\r
-        return new SimpleResolver(e);\r
-    }\r
-    \r
-    static const XMLCh SIMPLE_NS[] = {\r
-        chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_m, chLatin_a, chLatin_c, chLatin_e, chColon,\r
-        chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chColon,\r
-        chDigit_2, chPeriod, chDigit_0, chColon,\r
-        chLatin_r, chLatin_e, chLatin_s, chLatin_o, chLatin_l, chLatin_v, chLatin_e, chLatin_r, chColon,\r
-        chLatin_s, chLatin_i, chLatin_m, chLatin_p, chLatin_l, chLatin_e, chNull\r
-    };\r
-    static const XMLCh _AttributeDecoder[] =    UNICODE_LITERAL_16(A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);\r
-    static const XMLCh _AttributeResolver[] =   UNICODE_LITERAL_17(A,t,t,r,i,b,u,t,e,R,e,s,o,l,v,e,r);\r
-    static const XMLCh allowQuery[] =           UNICODE_LITERAL_10(a,l,l,o,w,Q,u,e,r,y);\r
-    static const XMLCh decoderId[] =            UNICODE_LITERAL_9(d,e,c,o,d,e,r,I,d);\r
-    static const XMLCh _id[] =                  UNICODE_LITERAL_2(i,d);\r
-    static const XMLCh _type[] =                UNICODE_LITERAL_4(t,y,p,e);\r
-};\r
-\r
-SimpleResolverImpl::SimpleResolverImpl(const DOMElement* e, Category& log) : m_log(log), m_document(NULL), m_allowQuery(true)\r
-{\r
-#ifdef _DEBUG\r
-    xmltooling::NDC ndc("SimpleResolverImpl");\r
-#endif\r
-    \r
-    if (!XMLHelper::isNodeNamed(e, SIMPLE_NS, _AttributeResolver))\r
-        throw ConfigurationException("Simple resolver requires resolver:AttributeResolver at root of configuration.");\r
-    \r
-    const XMLCh* flag = e->getAttributeNS(NULL,allowQuery);\r
-    if (flag && (*flag==chLatin_f || *flag==chDigit_0)) {\r
-        m_log.info("SAML attribute queries disabled");\r
-        m_allowQuery = false;\r
-    }\r
-\r
-    DOMElement* child = XMLHelper::getFirstChildElement(e, SIMPLE_NS, _AttributeDecoder);\r
-    while (child) {\r
-        auto_ptr_char id(child->getAttributeNS(NULL, _id));\r
-        auto_ptr_char type(child->getAttributeNS(NULL, _type));\r
-        try {\r
-            m_log.info("building AttributeDecoder (%s) of type %s", id.get(), type.get());\r
-            m_decoderMap[id.get()] = SPConfig::getConfig().AttributeDecoderManager.newPlugin(type.get(), child);\r
-        }\r
-        catch (exception& ex) {\r
-            m_log.error("error building AttributeDecoder (%s): %s", id.get(), ex.what());\r
-        }\r
-        child = XMLHelper::getNextSiblingElement(child, SIMPLE_NS, _AttributeDecoder);\r
-    }\r
-    \r
-    child = XMLHelper::getFirstChildElement(e, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
-    while (child) {\r
-        // Check for missing Name.\r
-        const XMLCh* name = child->getAttributeNS(NULL, saml2::Attribute::NAME_ATTRIB_NAME);\r
-        if (!name || !*name) {\r
-            m_log.warn("skipping saml:Attribute declared with no Name");\r
-            child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
-            continue;\r
-        }\r
-\r
-        const AttributeDecoder* decoder=NULL;\r
-        auto_ptr_char id(child->getAttributeNS(NULL, saml2::Attribute::FRIENDLYNAME_ATTRIB_NAME));\r
-        auto_ptr_char d(child->getAttributeNS(SIMPLE_NS, decoderId));\r
-        if (!id.get() || !*id.get() || !d.get() || !*d.get() || !(decoder=m_decoderMap[d.get()])) {\r
-            m_log.warn("skipping saml:Attribute declared with no FriendlyName or resolvable AttributeDecoder");\r
-            child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
-            continue;\r
-        }\r
-        \r
-        // Empty NameFormat implies the usual Shib URI naming defaults.\r
-        const XMLCh* format = child->getAttributeNS(NULL, saml2::Attribute::NAMEFORMAT_ATTRIB_NAME);\r
-        if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI) ||\r
-                XMLString::equals(format, saml2::Attribute::URI_REFERENCE))\r
-            format = &chNull;  // ignore default Format/Namespace values\r
-\r
-        // Fetch/create the map entry and see if it's a duplicate rule.\r
-#ifdef HAVE_GOOD_STL\r
-        pair<const AttributeDecoder*,string>& decl = m_attrMap[make_pair(name,format)];\r
-#else\r
-        auto_ptr_char n(name);\r
-        auto_ptr_char f(format);\r
-        pair<const AttributeDecoder*,string>& decl = m_attrMap[make_pair(n.get(),f.get())];\r
-#endif\r
-        if (decl.first) {\r
-            m_log.warn("skipping duplicate saml:Attribute declaration (same Name and NameFormat)");\r
-            child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
-            continue;\r
-        }\r
-\r
-        if (m_log.isInfoEnabled()) {\r
-#ifdef HAVE_GOOD_STL\r
-            auto_ptr_char n(name);\r
-            auto_ptr_char f(format);\r
-#endif\r
-            m_log.info("creating declaration for Attribute %s%s%s", n.get(), *f.get() ? ", Format/Namespace:" : "", f.get());\r
-        }\r
-        \r
-        decl.first = decoder;\r
-        decl.second = id.get();\r
-        \r
-        child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME);\r
-    }\r
-}\r
-\r
-void SimpleResolverImpl::resolve(\r
-    ResolutionContext& ctx, const saml1::Assertion* token, const set<string>* attributes\r
-    ) const\r
-{\r
-    vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();\r
-\r
-    auto_ptr_char assertingParty(ctx.getEntityDescriptor() ? ctx.getEntityDescriptor()->getEntityID() : NULL);\r
-    const char* relyingParty = ctx.getApplication().getString("entityID").second;\r
-\r
-#ifdef HAVE_GOOD_STL\r
-    map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
-#else\r
-    map< pair<string,string>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
-#endif\r
-\r
-    const XMLCh* name;\r
-    const XMLCh* format;\r
-    \r
-    // Check the NameID based on the format.\r
-    if (ctx.getNameID()) {\r
-        format = ctx.getNameID()->getFormat();\r
-        if (!format || !*format)\r
-            format = NameID::UNSPECIFIED;\r
-#ifdef HAVE_GOOD_STL\r
-        if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {\r
-#else\r
-        auto_ptr_char temp(format);\r
-        if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {\r
-#endif\r
-            if (!attributes || attributes->count(rule->second.second)) {\r
-                resolved.push_back(\r
-                    rule->second.first->decode(\r
-                        rule->second.second.c_str(), ctx.getNameID(), assertingParty.get(), relyingParty\r
-                        )\r
-                    );\r
-            }\r
-        }\r
-    }\r
-\r
-    const vector<saml1::AttributeStatement*>& statements = token->getAttributeStatements();\r
-    for (vector<saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {\r
-        const vector<saml1::Attribute*>& attrs = const_cast<const saml1::AttributeStatement*>(*s)->getAttributes();\r
-        for (vector<saml1::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) {\r
-            name = (*a)->getAttributeName();\r
-            format = (*a)->getAttributeNamespace();\r
-            if (!name || !*name)\r
-                continue;\r
-            if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI))\r
-                format = &chNull;\r
-#ifdef HAVE_GOOD_STL\r
-            if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
-#else\r
-            auto_ptr_char temp1(name);\r
-            auto_ptr_char temp2(format);\r
-            if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
-#endif\r
-                if (!attributes || attributes->count(rule->second.second)) {\r
-                    resolved.push_back(\r
-                        rule->second.first->decode(rule->second.second.c_str(), *a, assertingParty.get(), relyingParty)\r
-                        );\r
-                }\r
-            }\r
-        }\r
-    }\r
-}\r
-\r
-void SimpleResolverImpl::resolve(\r
-    ResolutionContext& ctx, const saml2::Assertion* token, const set<string>* attributes\r
-    ) const\r
-{\r
-    vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();\r
-\r
-    auto_ptr_char assertingParty(ctx.getEntityDescriptor() ? ctx.getEntityDescriptor()->getEntityID() : NULL);\r
-    const char* relyingParty = ctx.getApplication().getString("entityID").second;\r
-\r
-#ifdef HAVE_GOOD_STL\r
-    map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
-#else\r
-    map< pair<string,string>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
-#endif\r
-\r
-    const XMLCh* name;\r
-    const XMLCh* format;\r
-    \r
-    // Check the NameID based on the format.\r
-    if (ctx.getNameID()) {\r
-        format = ctx.getNameID()->getFormat();\r
-        if (!format || !*format)\r
-            format = NameID::UNSPECIFIED;\r
-#ifdef HAVE_GOOD_STL\r
-        if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {\r
-#else\r
-        auto_ptr_char temp(format);\r
-        if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {\r
-#endif\r
-            if (!attributes || attributes->count(rule->second.second)) {\r
-                resolved.push_back(\r
-                    rule->second.first->decode(\r
-                        rule->second.second.c_str(), ctx.getNameID(), assertingParty.get(), relyingParty\r
-                        )\r
-                    );\r
-            }\r
-        }\r
-    }\r
-\r
-    const vector<saml2::AttributeStatement*>& statements = token->getAttributeStatements();\r
-    for (vector<saml2::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {\r
-        const vector<saml2::Attribute*>& attrs = const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();\r
-        for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) {\r
-            name = (*a)->getName();\r
-            format = (*a)->getNameFormat();\r
-            if (!name || !*name)\r
-                continue;\r
-            if (!format || !*format)\r
-                format = saml2::Attribute::UNSPECIFIED;\r
-            else if (XMLString::equals(format, saml2::Attribute::URI_REFERENCE))\r
-                format = &chNull;\r
-#ifdef HAVE_GOOD_STL\r
-            if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
-#else\r
-            auto_ptr_char temp1(name);\r
-            auto_ptr_char temp2(format);\r
-            if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
-#endif\r
-                if (!attributes || attributes->count(rule->second.second)) {\r
-                    resolved.push_back(\r
-                        rule->second.first->decode(rule->second.second.c_str(), *a, assertingParty.get(), relyingParty)\r
-                        );\r
-                }\r
-            }\r
-        }\r
-\r
-        const vector<saml2::EncryptedAttribute*>& encattrs = const_cast<const saml2::AttributeStatement*>(*s)->getEncryptedAttributes();\r
-        if (!encattrs.empty()) {\r
-            const XMLCh* recipient = ctx.getApplication().getXMLString("entityID").second;\r
-            CredentialResolver* cr = ctx.getApplication().getCredentialResolver();\r
-            if (!cr) {\r
-                Category::getInstance(SHIBSP_LOGCAT".AttributeResolver").warn(\r
-                    "found encrypted attributes, but no CredentialResolver was available"\r
-                    );\r
-                return;\r
-            }\r
-\r
-            // We look up credentials based on the peer who did the encrypting.\r
-            CredentialCriteria cc;\r
-            cc.setPeerName(assertingParty.get());\r
-\r
-            Locker credlocker(cr);\r
-            for (vector<saml2::EncryptedAttribute*>::const_iterator ea = encattrs.begin(); ea!=encattrs.end(); ++ea) {\r
-                auto_ptr<XMLObject> decrypted((*ea)->decrypt(*cr, recipient, &cc));\r
-                const saml2::Attribute* decattr = dynamic_cast<const saml2::Attribute*>(decrypted.get());\r
-                name = decattr->getName();\r
-                format = decattr->getNameFormat();\r
-                if (!name || !*name)\r
-                    continue;\r
-                if (!format || !*format)\r
-                    format = saml2::Attribute::UNSPECIFIED;\r
-                else if (XMLString::equals(format, saml2::Attribute::URI_REFERENCE))\r
-                    format = &chNull;\r
-#ifdef HAVE_GOOD_STL\r
-                if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
-#else\r
-                auto_ptr_char temp1(name);\r
-                auto_ptr_char temp2(format);\r
-                if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
-#endif\r
-                    if (!attributes || attributes->count(rule->second.second)) {\r
-                        resolved.push_back(\r
-                            rule->second.first->decode(rule->second.second.c_str(), decattr, assertingParty.get(), relyingParty)\r
-                            );\r
-                    }\r
-                }\r
-            }\r
-        }\r
-    }\r
-}\r
-\r
-void SimpleResolverImpl::query(ResolutionContext& ctx, const NameIdentifier& nameid, const set<string>* attributes) const\r
-{\r
-#ifdef _DEBUG\r
-    xmltooling::NDC ndc("query");\r
-#endif\r
-\r
-    const EntityDescriptor* entity = ctx.getEntityDescriptor();\r
-    if (!entity) {\r
-        m_log.debug("no issuer information available, skipping query");\r
-        return;\r
-    }\r
-\r
-    int version = 1;\r
-    const AttributeAuthorityDescriptor* AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);\r
-    if (!AA) {\r
-        AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);\r
-        version = 0;\r
-    }\r
-    if (!AA) {\r
-        m_log.info("no SAML 1.x AttributeAuthority role found in metadata");\r
-        return;\r
-    }\r
-\r
-    shibsp::SecurityPolicy policy(ctx.getApplication());\r
-    MetadataCredentialCriteria mcc(*AA);\r
-    shibsp::SOAPClient soaper(policy);\r
-    const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);\r
-    pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");\r
-\r
-    auto_ptr_XMLCh binding(samlconstants::SAML1_BINDING_SOAP);\r
-    saml1p::Response* response=NULL;\r
-    const vector<AttributeService*>& endpoints=AA->getAttributeServices();\r
-    for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {\r
-        try {\r
-            if (!XMLString::equals((*ep)->getBinding(),binding.get()))\r
-                continue;\r
-            auto_ptr_char loc((*ep)->getLocation());\r
-            auto_ptr_XMLCh issuer(ctx.getApplication().getString("entityID").second);\r
-            saml1::Subject* subject = saml1::SubjectBuilder::buildSubject();\r
-            subject->setNameIdentifier(nameid.cloneNameIdentifier());\r
-            saml1p::AttributeQuery* query = saml1p::AttributeQueryBuilder::buildAttributeQuery();\r
-            query->setSubject(subject);\r
-            Request* request = RequestBuilder::buildRequest();\r
-            request->setAttributeQuery(query);\r
-            query->setResource(issuer.get());\r
-            request->setMinorVersion(version);\r
-            if (attributes) {\r
-                for (set<string>::const_iterator a = attributes->begin(); a!=attributes->end(); ++a)\r
-                    populateQuery(*query, *a);\r
-            }\r
-\r
-            SAML1SOAPClient client(soaper);\r
-            client.sendSAML(request, mcc, loc.get());\r
-            response = client.receiveSAML();\r
-        }\r
-        catch (exception& ex) {\r
-            m_log.error("exception making SAML query: %s", ex.what());\r
-            soaper.reset();\r
-        }\r
-    }\r
-\r
-    if (!response) {\r
-        m_log.error("unable to successfully query for attributes");\r
-        return;\r
-    }\r
-\r
-    const vector<saml1::Assertion*>& assertions = const_cast<const saml1p::Response*>(response)->getAssertions();\r
-    if (assertions.size()>1)\r
-        m_log.warn("simple resolver only supports one assertion in the query response");\r
-\r
-    auto_ptr<saml1p::Response> wrapper(response);\r
-    saml1::Assertion* newtoken = assertions.front();\r
-\r
-    if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {\r
-        m_log.error("assertion unsigned, rejecting it based on signedAssertions policy");\r
-        return;\r
-    }\r
-\r
-    try {\r
-        policy.evaluate(*newtoken);\r
-        if (!policy.isSecure())\r
-            throw SecurityPolicyException("Security of SAML 1.x query result not established.");\r
-        saml1::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));\r
-        tokval.validateAssertion(*newtoken);\r
-    }\r
-    catch (exception& ex) {\r
-        m_log.error("assertion failed policy/validation: %s", ex.what());\r
-    }\r
-    newtoken->detach();\r
-    wrapper.release();\r
-    ctx.getResolvedAssertions().push_back(newtoken);\r
-    resolve(ctx, newtoken, attributes);\r
-}\r
-\r
-void SimpleResolverImpl::populateQuery(saml1p::AttributeQuery& query, const string& id) const\r
-{\r
-    for (attrmap_t::const_iterator i = m_attrMap.begin(); i!=m_attrMap.end(); ++i) {\r
-        if (i->second.second == id) {\r
-            AttributeDesignator* a = AttributeDesignatorBuilder::buildAttributeDesignator();\r
-#ifdef HAVE_GOOD_STL\r
-            a->setAttributeName(i->first.first.c_str());\r
-            a->setAttributeNamespace(i->first.second.empty() ? shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI : i->first.second.c_str());\r
-#else\r
-            auto_ptr_XMLCh n(i->first.first.c_str());\r
-            a->setAttributeName(n.get());\r
-            if (i->first.second.empty())\r
-                a->setAttributeNamespace(shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI);\r
-            else {\r
-                auto_ptr_XMLCh ns(i->first.second.c_str());\r
-                a->setAttributeNamespace(ns.get());\r
-            }\r
-#endif\r
-            query.getAttributeDesignators().push_back(a);\r
-        }\r
-    }\r
-}\r
-\r
-void SimpleResolverImpl::query(ResolutionContext& ctx, const NameID& nameid, const set<string>* attributes) const\r
-{\r
-#ifdef _DEBUG\r
-    xmltooling::NDC ndc("query");\r
-#endif\r
-\r
-    const EntityDescriptor* entity = ctx.getEntityDescriptor();\r
-    if (!entity) {\r
-        m_log.debug("no issuer information available, skipping query");\r
-        return;\r
-    }\r
-    const AttributeAuthorityDescriptor* AA = entity->getAttributeAuthorityDescriptor(samlconstants::SAML20P_NS);\r
-    if (!AA) {\r
-        m_log.info("no SAML 2 AttributeAuthority role found in metadata");\r
-        return;\r
-    }\r
-\r
-    shibsp::SecurityPolicy policy(ctx.getApplication());\r
-    MetadataCredentialCriteria mcc(*AA);\r
-    shibsp::SOAPClient soaper(policy);\r
-    const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);\r
-    pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");\r
-\r
-    auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);\r
-    saml2p::StatusResponseType* srt=NULL;\r
-    const vector<AttributeService*>& endpoints=AA->getAttributeServices();\r
-    for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !srt && ep!=endpoints.end(); ++ep) {\r
-        try {\r
-            if (!XMLString::equals((*ep)->getBinding(),binding.get()))\r
-                continue;\r
-            auto_ptr_char loc((*ep)->getLocation());\r
-            auto_ptr_XMLCh issuer(ctx.getApplication().getString("entityID").second);\r
-            saml2::Subject* subject = saml2::SubjectBuilder::buildSubject();\r
-            subject->setNameID(nameid.cloneNameID());\r
-            saml2p::AttributeQuery* query = saml2p::AttributeQueryBuilder::buildAttributeQuery();\r
-            query->setSubject(subject);\r
-            Issuer* iss = IssuerBuilder::buildIssuer();\r
-            query->setIssuer(iss);\r
-            iss->setName(issuer.get());\r
-            if (attributes) {\r
-                for (set<string>::const_iterator a = attributes->begin(); a!=attributes->end(); ++a)\r
-                    populateQuery(*query, *a);\r
-            }\r
-\r
-            SAML2SOAPClient client(soaper);\r
-            client.sendSAML(query, mcc, loc.get());\r
-            srt = client.receiveSAML();\r
-        }\r
-        catch (exception& ex) {\r
-            m_log.error("exception making SAML query: %s", ex.what());\r
-            soaper.reset();\r
-        }\r
-    }\r
-\r
-    if (!srt) {\r
-        m_log.error("unable to successfully query for attributes");\r
-        return;\r
-    }\r
-    saml2p::Response* response = dynamic_cast<saml2p::Response*>(srt);\r
-    if (!response) {\r
-        delete srt;\r
-        m_log.error("message was not a samlp:Response");\r
-        return;\r
-    }\r
-\r
-    const vector<saml2::Assertion*>& assertions = const_cast<const saml2p::Response*>(response)->getAssertions();\r
-    if (assertions.size()>1)\r
-        m_log.warn("simple resolver only supports one assertion in the query response");\r
-\r
-    auto_ptr<saml2p::Response> wrapper(response);\r
-    saml2::Assertion* newtoken = assertions.front();\r
-\r
-    if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {\r
-        m_log.error("assertion unsigned, rejecting it based on signedAssertions policy");\r
-        return;\r
-    }\r
-\r
-    try {\r
-        policy.evaluate(*newtoken);\r
-        if (!policy.isSecure())\r
-            throw SecurityPolicyException("Security of SAML 2.0 query result not established.");\r
-        saml2::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));\r
-        tokval.validateAssertion(*newtoken);\r
-    }\r
-    catch (exception& ex) {\r
-        m_log.error("assertion failed policy/validation: %s", ex.what());\r
-    }\r
-    newtoken->detach();\r
-    wrapper.release();\r
-    ctx.getResolvedAssertions().push_back(newtoken);\r
-    resolve(ctx, newtoken, attributes);\r
-}\r
-\r
-void SimpleResolverImpl::populateQuery(saml2p::AttributeQuery& query, const string& id) const\r
-{\r
-    for (attrmap_t::const_iterator i = m_attrMap.begin(); i!=m_attrMap.end(); ++i) {\r
-        if (i->second.second == id) {\r
-            saml2::Attribute* a = saml2::AttributeBuilder::buildAttribute();\r
-#ifdef HAVE_GOOD_STL\r
-            a->setName(i->first.first.c_str());\r
-            a->setNameFormat(i->first.second.empty() ? saml2::Attribute::URI_REFERENCE : i->first.second.c_str());\r
-#else\r
-            auto_ptr_XMLCh n(i->first.first.c_str());\r
-            a->setName(n.get());\r
-            if (i->first.second.empty())\r
-                a->setNameFormat(saml2::Attribute::URI_REFERENCE);\r
-            else {\r
-                auto_ptr_XMLCh ns(i->first.second.c_str());\r
-                a->setNameFormat(ns.get());\r
-            }\r
-#endif\r
-            query.getAttributes().push_back(a);\r
-        }\r
-    }\r
-}\r
-\r
-void SimpleResolver::resolveAttributes(ResolutionContext& ctx, const set<string>* attributes) const\r
-{\r
-#ifdef _DEBUG\r
-    xmltooling::NDC ndc("resolveAttributes");\r
-#endif\r
-    \r
-    m_log.debug("examining tokens to resolve");\r
-\r
-    bool query = m_impl->m_allowQuery;\r
-    const saml1::Assertion* token1;\r
-    const saml2::Assertion* token2;\r
-    if (ctx.getTokens()) {\r
-        for (vector<const opensaml::Assertion*>::const_iterator t = ctx.getTokens()->begin(); t!=ctx.getTokens()->end(); ++t) {\r
-            token2 = dynamic_cast<const saml2::Assertion*>(*t);\r
-            if (token2 && !token2->getAttributeStatements().empty()) {\r
-                m_log.debug("resolving SAML 2 token with an AttributeStatement");\r
-                m_impl->resolve(ctx, token2, attributes);\r
-                query = false;\r
-            }\r
-            else {\r
-                token1 = dynamic_cast<const saml1::Assertion*>(*t);\r
-                if (token1 && !token1->getAttributeStatements().empty()) {\r
-                    m_log.debug("resolving SAML 1 token with an AttributeStatement");\r
-                    m_impl->resolve(ctx, token1, attributes);\r
-                    query = false;\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    if (query) {\r
-        if (token1 && !token1->getAuthenticationStatements().empty()) {\r
-            const AuthenticationStatement* statement = token1->getAuthenticationStatements().front();\r
-            if (statement && statement->getSubject() && statement->getSubject()->getNameIdentifier()) {\r
-                m_log.debug("attempting SAML 1.x attribute query");\r
-                return m_impl->query(ctx, *(statement->getSubject()->getNameIdentifier()), attributes);\r
-            }\r
-        }\r
-        else if (token2 && ctx.getNameID()) {\r
-            m_log.debug("attempting SAML 2.0 attribute query");\r
-            return m_impl->query(ctx, *ctx.getNameID(), attributes);\r
-        }\r
-        m_log.warn("can't attempt attribute query, no identifier in assertion subject");\r
-    }\r
-}\r
-\r
-pair<bool,DOMElement*> SimpleResolver::load()\r
-{\r
-    // Load from source using base class.\r
-    pair<bool,DOMElement*> raw = ReloadableXMLFile::load();\r
-    \r
-    // If we own it, wrap it.\r
-    XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);\r
-\r
-    SimpleResolverImpl* impl = new SimpleResolverImpl(raw.second, m_log);\r
-    \r
-    // If we held the document, transfer it to the impl. If we didn't, it's a no-op.\r
-    impl->setDocument(docjanitor.release());\r
-\r
-    delete m_impl;\r
-    m_impl = impl;\r
-\r
-    return make_pair(false,(DOMElement*)NULL);\r
-}\r
diff --git a/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp b/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp
new file mode 100644 (file)
index 0000000..4e287ed
--- /dev/null
@@ -0,0 +1,452 @@
+/*\r
+ *  Copyright 2001-2007 Internet2\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/**\r
+ * XMLAttributeExtractor.cpp\r
+ * \r
+ * AttributeExtractor based on an XML mapping file.\r
+ */\r
+\r
+#include "internal.h"\r
+#include "Application.h"\r
+#include "ServiceProvider.h"\r
+#include "attribute/AttributeDecoder.h"\r
+#include "attribute/resolver/AttributeExtractor.h"\r
+#include "util/SPConstants.h"\r
+\r
+#include <saml/saml1/core/Assertions.h>\r
+#include <saml/saml2/core/Assertions.h>\r
+#include <saml/saml2/metadata/MetadataCredentialCriteria.h>\r
+#include <xmltooling/util/NDC.h>\r
+#include <xmltooling/util/ReloadableXMLFile.h>\r
+#include <xmltooling/util/XMLHelper.h>\r
+#include <xercesc/util/XMLUniDefs.hpp>\r
+\r
+using namespace shibsp;\r
+using namespace opensaml::saml2md;\r
+using namespace opensaml;\r
+using namespace xmltooling;\r
+using namespace log4cpp;\r
+using namespace std;\r
+using saml1::NameIdentifier;\r
+using saml2::NameID;\r
+using saml2::EncryptedAttribute;\r
+\r
+namespace shibsp {\r
+\r
+#if defined (_MSC_VER)\r
+    #pragma warning( push )\r
+    #pragma warning( disable : 4250 )\r
+#endif\r
+\r
+    class XMLExtractorImpl\r
+    {\r
+    public:\r
+        XMLExtractorImpl(const DOMElement* e, Category& log);\r
+        ~XMLExtractorImpl() {\r
+            for (attrmap_t::iterator i = m_attrMap.begin(); i!=m_attrMap.end(); ++i)\r
+                delete i->second.first;\r
+            if (m_document)\r
+                m_document->release();\r
+        }\r
+\r
+        void setDocument(DOMDocument* doc) {\r
+            m_document = doc;\r
+        }\r
+\r
+        void extractAttributes(\r
+            const Application& application, const char* assertingParty, const NameIdentifier& nameid, multimap<string,Attribute*>& attributes\r
+            ) const;\r
+        void extractAttributes(\r
+            const Application& application, const char* assertingParty, const NameID& nameid, multimap<string,Attribute*>& attributes\r
+            ) const;\r
+        void extractAttributes(\r
+            const Application& application, const char* assertingParty, const saml1::Attribute& attr, multimap<string,Attribute*>& attributes\r
+            ) const;\r
+        void extractAttributes(\r
+            const Application& application, const char* assertingParty, const saml2::Attribute& attr, multimap<string,Attribute*>& attributes\r
+            ) const;\r
+\r
+    private:\r
+        Category& m_log;\r
+        DOMDocument* m_document;\r
+#ifdef HAVE_GOOD_STL\r
+        typedef map< pair<xstring,xstring>,pair<AttributeDecoder*,string> > attrmap_t;\r
+#else\r
+        typedef map< pair<string,string>,pair<AttributeDecoder*,string> > attrmap_t;\r
+#endif\r
+        attrmap_t m_attrMap;\r
+    };\r
+    \r
+    class XMLExtractor : public AttributeExtractor, public ReloadableXMLFile\r
+    {\r
+    public:\r
+        XMLExtractor(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".AttributeExtractor")), m_impl(NULL) {\r
+            load();\r
+        }\r
+        ~XMLExtractor() {\r
+            delete m_impl;\r
+        }\r
+        \r
+        void extractAttributes(\r
+            const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, multimap<string,Attribute*>& attributes\r
+            ) const;\r
+\r
+    protected:\r
+        pair<bool,DOMElement*> load();\r
+\r
+    private:\r
+        XMLExtractorImpl* m_impl;\r
+    };\r
+\r
+#if defined (_MSC_VER)\r
+    #pragma warning( pop )\r
+#endif\r
+\r
+    AttributeExtractor* SHIBSP_DLLLOCAL XMLAttributeExtractorFactory(const DOMElement* const & e)\r
+    {\r
+        return new XMLExtractor(e);\r
+    }\r
+    \r
+    static const XMLCh _AttributeDecoder[] =    UNICODE_LITERAL_16(A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);\r
+    static const XMLCh Attributes[] =           UNICODE_LITERAL_10(A,t,t,r,i,b,u,t,e,s);\r
+    static const XMLCh _id[] =                  UNICODE_LITERAL_2(i,d);\r
+    static const XMLCh _name[] =                UNICODE_LITERAL_4(n,a,m,e);\r
+    static const XMLCh nameFormat[] =           UNICODE_LITERAL_10(n,a,m,e,F,o,r,m,a,t);\r
+};\r
+\r
+void SHIBSP_API shibsp::registerAttributeExtractors()
+{
+    SPConfig::getConfig().AttributeExtractorManager.registerFactory(XML_ATTRIBUTE_EXTRACTOR, XMLAttributeExtractorFactory);
+}
+\r
+XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) : m_log(log), m_document(NULL)\r
+{\r
+#ifdef _DEBUG\r
+    xmltooling::NDC ndc("XMLExtractorImpl");\r
+#endif\r
+    \r
+    if (!XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEMAP_NS, Attributes))\r
+        throw ConfigurationException("XML AttributeExtractor requires am:Attributes at root of configuration.");\r
+\r
+    DOMElement* child = XMLHelper::getFirstChildElement(e, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME);\r
+    while (child) {\r
+        // Check for missing name or id.\r
+        const XMLCh* name = child->getAttributeNS(NULL, _name);\r
+        if (!name || !*name) {\r
+            m_log.warn("skipping Attribute with no name");\r
+            child = XMLHelper::getNextSiblingElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME);\r
+            continue;\r
+        }\r
+\r
+        auto_ptr_char id(child->getAttributeNS(NULL, _id));\r
+        if (!id.get() || !*id.get()) {\r
+            m_log.warn("skipping Attribute with no id");\r
+            child = XMLHelper::getNextSiblingElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME);\r
+            continue;\r
+        }\r
+\r
+        AttributeDecoder* decoder=NULL;\r
+        try {\r
+            DOMElement* dchild = XMLHelper::getFirstChildElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, _AttributeDecoder);\r
+            if (child) {\r
+                auto_ptr<QName> q(XMLHelper::getXSIType(dchild));\r
+                if (q.get())\r
+                    decoder = SPConfig::getConfig().AttributeDecoderManager.newPlugin(*q.get(), dchild);\r
+            }\r
+            if (!decoder)\r
+                decoder = SPConfig::getConfig().AttributeDecoderManager.newPlugin(StringAttributeDecoderType, NULL);\r
+        }\r
+        catch (exception& ex) {\r
+            m_log.error("skipping Attribute (%s), error building AttributeDecoder: %s", id.get(), ex.what());\r
+        }\r
+\r
+        if (!decoder) {\r
+            child = XMLHelper::getNextSiblingElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME);\r
+            continue;\r
+        }\r
+\r
+        // Empty NameFormat implies the usual Shib URI naming defaults.\r
+        const XMLCh* format = child->getAttributeNS(NULL, nameFormat);\r
+        if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI) ||\r
+                XMLString::equals(format, saml2::Attribute::URI_REFERENCE))\r
+            format = &chNull;  // ignore default Format/Namespace values\r
+\r
+        // Fetch/create the map entry and see if it's a duplicate rule.\r
+#ifdef HAVE_GOOD_STL\r
+        pair<AttributeDecoder*,string>& decl = m_attrMap[make_pair(name,format)];\r
+#else\r
+        auto_ptr_char n(name);\r
+        auto_ptr_char f(format);\r
+        pair<AttributeDecoder*,string>& decl = m_attrMap[make_pair(n.get(),f.get())];\r
+#endif\r
+        if (decl.first) {\r
+            m_log.warn("skipping duplicate Attribute declaration (same name and nameFormat)");\r
+            delete decoder;\r
+            child = XMLHelper::getNextSiblingElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME);\r
+            continue;\r
+        }\r
+\r
+        if (m_log.isInfoEnabled()) {\r
+#ifdef HAVE_GOOD_STL\r
+            auto_ptr_char n(name);\r
+            auto_ptr_char f(format);\r
+#endif\r
+            m_log.info("creating declaration for Attribute %s%s%s", n.get(), *f.get() ? ", Format/Namespace:" : "", f.get());\r
+        }\r
+        \r
+        decl.first = decoder;\r
+        decl.second = id.get();\r
+        \r
+        child = XMLHelper::getNextSiblingElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME);\r
+    }\r
+}\r
+\r
+void XMLExtractorImpl::extractAttributes(\r
+    const Application& application, const char* assertingParty, const NameIdentifier& nameid, multimap<string,Attribute*>& attributes\r
+    ) const\r
+{\r
+#ifdef HAVE_GOOD_STL\r
+    map< pair<xstring,xstring>,pair<AttributeDecoder*,string> >::const_iterator rule;\r
+#else\r
+    map< pair<string,string>,pair<AttributeDecoder*,string> >::const_iterator rule;\r
+#endif\r
+\r
+    const XMLCh* format = nameid.getFormat();\r
+    if (!format || !*format)\r
+        format = NameIdentifier::UNSPECIFIED;\r
+#ifdef HAVE_GOOD_STL\r
+    if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {\r
+#else\r
+    auto_ptr_char temp(format);\r
+    if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {\r
+#endif\r
+        attributes.insert(\r
+            make_pair(\r
+                rule->second.second,\r
+                rule->second.first->decode(rule->second.second.c_str(), &nameid, assertingParty, application.getString("entityID").second)\r
+                )\r
+            );\r
+    }\r
+}\r
+\r
+void XMLExtractorImpl::extractAttributes(\r
+    const Application& application, const char* assertingParty, const NameID& nameid, multimap<string,Attribute*>& attributes\r
+    ) const\r
+{\r
+#ifdef HAVE_GOOD_STL\r
+    map< pair<xstring,xstring>,pair<AttributeDecoder*,string> >::const_iterator rule;\r
+#else\r
+    map< pair<string,string>,pair<AttributeDecoder*,string> >::const_iterator rule;\r
+#endif\r
+\r
+    const XMLCh* format = nameid.getFormat();\r
+    if (!format || !*format)\r
+        format = NameID::UNSPECIFIED;\r
+#ifdef HAVE_GOOD_STL\r
+    if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {\r
+#else\r
+    auto_ptr_char temp(format);\r
+    if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {\r
+#endif\r
+        attributes.insert(\r
+            make_pair(\r
+                rule->second.second,\r
+                rule->second.first->decode(rule->second.second.c_str(), &nameid, assertingParty, application.getString("entityID").second)\r
+                )\r
+            );\r
+    }\r
+}\r
+\r
+void XMLExtractorImpl::extractAttributes(\r
+    const Application& application, const char* assertingParty, const saml1::Attribute& attr, multimap<string,Attribute*>& attributes\r
+    ) const\r
+{\r
+#ifdef HAVE_GOOD_STL\r
+    map< pair<xstring,xstring>,pair<AttributeDecoder*,string> >::const_iterator rule;\r
+#else\r
+    map< pair<string,string>,pair<AttributeDecoder*,string> >::const_iterator rule;\r
+#endif\r
+\r
+    const XMLCh* name = attr.getAttributeName();\r
+    const XMLCh* format = attr.getAttributeNamespace();\r
+    if (!name || !*name)\r
+        return;\r
+    if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI))\r
+        format = &chNull;\r
+#ifdef HAVE_GOOD_STL\r
+    if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
+#else\r
+    auto_ptr_char temp1(name);\r
+    auto_ptr_char temp2(format);\r
+    if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
+#endif\r
+        attributes.insert(\r
+            make_pair(\r
+                rule->second.second,\r
+                rule->second.first->decode(rule->second.second.c_str(), &attr, assertingParty, application.getString("entityID").second)\r
+                )\r
+            );\r
+    }\r
+}\r
+\r
+void XMLExtractorImpl::extractAttributes(\r
+    const Application& application, const char* assertingParty, const saml2::Attribute& attr, multimap<string,Attribute*>& attributes\r
+    ) const\r
+{\r
+#ifdef HAVE_GOOD_STL\r
+    map< pair<xstring,xstring>,pair<AttributeDecoder*,string> >::const_iterator rule;\r
+#else\r
+    map< pair<string,string>,pair<AttributeDecoder*,string> >::const_iterator rule;\r
+#endif\r
+\r
+    const XMLCh* name = attr.getName();\r
+    const XMLCh* format = attr.getNameFormat();\r
+    if (!name || !*name)\r
+        return;\r
+    if (!format || !*format)\r
+        format = saml2::Attribute::UNSPECIFIED;\r
+    else if (XMLString::equals(format, saml2::Attribute::URI_REFERENCE))\r
+        format = &chNull;\r
+#ifdef HAVE_GOOD_STL\r
+    if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {\r
+#else\r
+    auto_ptr_char temp1(name);\r
+    auto_ptr_char temp2(format);\r
+    if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {\r
+#endif\r
+        attributes.insert(\r
+            make_pair(\r
+                rule->second.second,\r
+                rule->second.first->decode(rule->second.second.c_str(), &attr, assertingParty, application.getString("entityID").second)\r
+                )\r
+            );\r
+    }\r
+}\r
+\r
+void XMLExtractor::extractAttributes(\r
+    const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, multimap<string,Attribute*>& attributes\r
+    ) const\r
+{\r
+    // Check for assertions.\r
+    if (XMLString::equals(xmlObject.getElementQName().getLocalPart(), saml1::Assertion::LOCAL_NAME)) {\r
+        const saml2::Assertion* token2 = dynamic_cast<const saml2::Assertion*>(&xmlObject);\r
+        if (token2) {\r
+            auto_ptr_char assertingParty(issuer ? dynamic_cast<const EntityDescriptor*>(issuer->getParent())->getEntityID() : NULL);\r
+            const vector<saml2::AttributeStatement*>& statements = token2->getAttributeStatements();\r
+            for (vector<saml2::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {\r
+                const vector<saml2::Attribute*>& attrs = const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();\r
+                for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a)\r
+                    m_impl->extractAttributes(application, assertingParty.get(), *(*a), attributes);\r
+\r
+                const vector<saml2::EncryptedAttribute*>& encattrs = const_cast<const saml2::AttributeStatement*>(*s)->getEncryptedAttributes();\r
+                for (vector<saml2::EncryptedAttribute*>::const_iterator ea = encattrs.begin(); ea!=encattrs.end(); ++ea)\r
+                    extractAttributes(application, issuer, *(*ea), attributes);\r
+            }\r
+        }\r
+\r
+        const saml1::Assertion* token1 = dynamic_cast<const saml1::Assertion*>(&xmlObject);\r
+        if (token1) {\r
+            auto_ptr_char assertingParty(issuer ? dynamic_cast<const EntityDescriptor*>(issuer->getParent())->getEntityID() : NULL);\r
+            const vector<saml1::AttributeStatement*>& statements = token1->getAttributeStatements();\r
+            for (vector<saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {\r
+                const vector<saml1::Attribute*>& attrs = const_cast<const saml1::AttributeStatement*>(*s)->getAttributes();\r
+                for (vector<saml1::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a)\r
+                    m_impl->extractAttributes(application, assertingParty.get(), *(*a), attributes);\r
+            }\r
+        }\r
+\r
+        throw AttributeExtractionException("Unable to extract attributes, unknown object type.");\r
+    }\r
+\r
+    // Check for attributes.\r
+    if (XMLString::equals(xmlObject.getElementQName().getLocalPart(), saml1::Attribute::LOCAL_NAME)) {\r
+        auto_ptr_char assertingParty(issuer ? dynamic_cast<const EntityDescriptor*>(issuer->getParent())->getEntityID() : NULL);\r
+\r
+        const saml2::Attribute* attr2 = dynamic_cast<const saml2::Attribute*>(&xmlObject);\r
+        if (attr2)\r
+            return m_impl->extractAttributes(application, assertingParty.get(), *attr2, attributes);\r
+\r
+        const saml1::Attribute* attr1 = dynamic_cast<const saml1::Attribute*>(&xmlObject);\r
+        if (attr1)\r
+            return m_impl->extractAttributes(application, assertingParty.get(), *attr1, attributes);\r
+\r
+        throw AttributeExtractionException("Unable to extract attributes, unknown object type.");\r
+    }\r
+\r
+    if (XMLString::equals(xmlObject.getElementQName().getLocalPart(), EncryptedAttribute::LOCAL_NAME)) {\r
+        const EncryptedAttribute* encattr = dynamic_cast<const EncryptedAttribute*>(&xmlObject);\r
+        if (encattr) {\r
+            const XMLCh* recipient = application.getXMLString("entityID").second;\r
+            CredentialResolver* cr = application.getCredentialResolver();\r
+            if (!cr) {\r
+                m_log.warn("found encrypted attribute, but no CredentialResolver was available");\r
+                return;\r
+            }\r
+\r
+            try {\r
+                Locker credlocker(cr);\r
+                if (issuer) {\r
+                    MetadataCredentialCriteria mcc(*issuer);\r
+                    auto_ptr<XMLObject> decrypted(encattr->decrypt(*cr, recipient, &mcc));\r
+                    return extractAttributes(application, issuer, *(decrypted.get()), attributes);\r
+                }\r
+                else {\r
+                    auto_ptr<XMLObject> decrypted(encattr->decrypt(*cr, recipient));\r
+                    return extractAttributes(application, issuer, *(decrypted.get()), attributes);\r
+                }\r
+            }\r
+            catch (exception& ex) {\r
+                m_log.error("caught exception decrypting Attribute: %s", ex.what());\r
+                return;\r
+            }\r
+        }\r
+    }\r
+\r
+    // Check for NameIDs.\r
+    const NameID* name2 = dynamic_cast<const NameID*>(&xmlObject);\r
+    if (name2) {\r
+        auto_ptr_char assertingParty(issuer ? dynamic_cast<const EntityDescriptor*>(issuer->getParent())->getEntityID() : NULL);\r
+        return m_impl->extractAttributes(application, assertingParty.get(), *name2, attributes);\r
+    }\r
+\r
+    const NameIdentifier* name1 = dynamic_cast<const NameIdentifier*>(&xmlObject);\r
+    if (name1) {\r
+        auto_ptr_char assertingParty(issuer ? dynamic_cast<const EntityDescriptor*>(issuer->getParent())->getEntityID() : NULL);\r
+        return m_impl->extractAttributes(application, assertingParty.get(), *name1, attributes);\r
+    }\r
+\r
+    throw AttributeExtractionException("Unable to extract attributes, unknown object type.");\r
+}\r
+\r
+pair<bool,DOMElement*> XMLExtractor::load()\r
+{\r
+    // Load from source using base class.\r
+    pair<bool,DOMElement*> raw = ReloadableXMLFile::load();\r
+    \r
+    // If we own it, wrap it.\r
+    XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);\r
+\r
+    XMLExtractorImpl* impl = new XMLExtractorImpl(raw.second, m_log);\r
+    \r
+    // If we held the document, transfer it to the impl. If we didn't, it's a no-op.\r
+    impl->setDocument(docjanitor.release());\r
+\r
+    delete m_impl;\r
+    m_impl = impl;\r
+\r
+    return make_pair(false,(DOMElement*)NULL);\r
+}\r
index 7e6c8ee..5cd9625 100644 (file)
@@ -29,6 +29,8 @@
 namespace shibsp {
     
     DECL_XMLTOOLING_EXCEPTION(AttributeException,SHIBSP_EXCEPTIONAPI(SHIBSP_API),shibsp,xmltooling::XMLToolingException,Exceptions during attribute processing.);
+    DECL_XMLTOOLING_EXCEPTION(AttributeExtractionException,SHIBSP_EXCEPTIONAPI(SHIBSP_API),shibsp,shibsp::AttributeException,Exceptions during attribute extraction.);
+    DECL_XMLTOOLING_EXCEPTION(AttributeFilteringException,SHIBSP_EXCEPTIONAPI(SHIBSP_API),shibsp,shibsp::AttributeException,Exceptions during attribute filtering.);
     DECL_XMLTOOLING_EXCEPTION(AttributeResolutionException,SHIBSP_EXCEPTIONAPI(SHIBSP_API),shibsp,shibsp::AttributeException,Exceptions during attribute resolution.);
     DECL_XMLTOOLING_EXCEPTION(ConfigurationException,SHIBSP_EXCEPTIONAPI(SHIBSP_API),shibsp,xmltooling::XMLToolingException,Exceptions during configuration.);
     DECL_XMLTOOLING_EXCEPTION(ListenerException,SHIBSP_EXCEPTIONAPI(SHIBSP_API),shibsp,xmltooling::XMLToolingException,Exceptions during inter-process communication.);
index a2a5664..b428f3d 100644 (file)
@@ -96,17 +96,17 @@ namespace shibsp {
          * <p>The caller must free the returned context handle.
          * 
          * @param application   reference to application receiving message
-         * @param httpRequest   client request that initiated session
          * @param issuer        source of SSO tokens
          * @param nameid        identifier of principal
-         * @param tokens        tokens to resolve, if any
+         * @param tokens        available assertions, if any
+         * @param attributes    attributes already extracted, if any
          */
         ResolutionContext* resolveAttributes(
             const Application& application,
-            const opensaml::HTTPRequest& httpRequest,
             const opensaml::saml2md::EntityDescriptor* issuer=NULL,
             const opensaml::saml2::NameID* nameid=NULL,
-            const std::vector<const opensaml::Assertion*>* tokens=NULL
+            const std::vector<const opensaml::Assertion*>* tokens=NULL,
+            const std::multimap<std::string,Attribute*>* attributes=NULL
             ) const;
         
     private:
index 75c5432..837fc08 100644 (file)
@@ -46,8 +46,8 @@ using namespace xercesc;
 using namespace std;
 
 namespace shibsp {
-    SHIBSP_DLLLOCAL PluginManager<Handler,pair<const DOMElement*,const char*>>::Factory SAML1ConsumerFactory;
-    SHIBSP_DLLLOCAL PluginManager<Handler,pair<const DOMElement*,const char*>>::Factory SAML2ConsumerFactory;
+    SHIBSP_DLLLOCAL PluginManager<Handler,string,pair<const DOMElement*,const char*>>::Factory SAML1ConsumerFactory;
+    SHIBSP_DLLLOCAL PluginManager<Handler,string,pair<const DOMElement*,const char*>>::Factory SAML2ConsumerFactory;
 };
 
 void SHIBSP_API shibsp::registerHandlers()
index 75b38e8..06c5c64 100644 (file)
@@ -233,24 +233,26 @@ void AssertionConsumerService::checkAddress(
 
 ResolutionContext* AssertionConsumerService::resolveAttributes(
     const Application& application,
-    const HTTPRequest& httpRequest,
     const saml2md::EntityDescriptor* issuer,
     const saml2::NameID* nameid,
-    const vector<const Assertion*>* tokens
+    const vector<const Assertion*>* tokens,
+    const multimap<string,Attribute*>* attributes
     ) const
 {
-    AttributeResolver* resolver = application.getAttributeResolver();
-    if (!resolver) {
-        m_log.info("no AttributeResolver available, skipping resolution");
-        return NULL;
-    }
-    
     try {
+        AttributeResolver* resolver = application.getAttributeResolver();
+        if (!resolver) {
+            m_log.info("no AttributeResolver available, skipping resolution");
+            return NULL;
+        }
+        
         m_log.debug("resolving attributes...");
+
+        Locker locker(resolver);
         auto_ptr<ResolutionContext> ctx(
-            resolver->createResolutionContext(application, httpRequest.getRemoteAddr().c_str(), issuer, nameid, tokens)
+            resolver->createResolutionContext(application, issuer, nameid, tokens, attributes)
             );
-        resolver->resolveAttributes(*ctx.get(), application.getAttributeIds());
+        resolver->resolveAttributes(*ctx.get());
         return ctx.release();
     }
     catch (exception& ex) {
index 8addb99..c3df16b 100644 (file)
@@ -25,6 +25,8 @@
 #include "exceptions.h"
 #include "ServiceProvider.h"
 #include "SessionCache.h"
+#include "attribute/Attribute.h"
+#include "attribute/resolver/AttributeExtractor.h"
 #include "attribute/resolver/ResolutionContext.h"
 #include "handler/AssertionConsumerService.h"
 
@@ -183,7 +185,20 @@ string SAML1Consumer::implementProtocol(
     m_log.debug("SSO profile processing completed successfully");
 
     // We've successfully "accepted" at least one SSO token, along with any additional valid tokens.
-    // To complete processing, we need to resolve attributes and then create the session.
+    // To complete processing, we need to extract and resolve attributes and then create the session.
+    multimap<string,Attribute*> resolvedAttributes;
+    AttributeExtractor* extractor = application.getAttributeExtractor();
+    if (extractor) {
+        Locker extlocker(extractor);
+        for (vector<const opensaml::Assertion*>::const_iterator t = tokens.begin(); t!=tokens.end(); ++t) {
+            try {
+                extractor->extractAttributes(application, policy.getIssuerMetadata(), *(*t), resolvedAttributes);
+            }
+            catch (exception& ex) {
+                m_log.error("caught exception extracting attributes: %s", ex.what());
+            }
+        }
+    }
 
     // First, normalize the SAML 1.x NameIdentifier...
     NameIdentifier* n = ssoStatement->getSubject()->getNameIdentifier();
@@ -194,13 +209,20 @@ string SAML1Consumer::implementProtocol(
         nameid->setNameQualifier(n->getNameQualifier());
     }
 
-    const EntityDescriptor* issuerMetadata = dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent());
+    const EntityDescriptor* issuerMetadata =
+        policy.getIssuerMetadata() ? dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent()) : NULL;
     auto_ptr<ResolutionContext> ctx(
-        resolveAttributes(application, httpRequest, issuerMetadata, nameid.get(), &tokens)
+        resolveAttributes(application, issuerMetadata, nameid.get(), &tokens, &resolvedAttributes)
         );
 
-    // Copy over any new tokens, but leave them in the context for cleanup.
-    tokens.insert(tokens.end(), ctx->getResolvedAssertions().begin(), ctx->getResolvedAssertions().end());
+    if (ctx.get()) {
+        // Copy over any new tokens, but leave them in the context for cleanup.
+        tokens.insert(tokens.end(), ctx->getResolvedAssertions().begin(), ctx->getResolvedAssertions().end());
+
+        // Copy over new attributes, and transfer ownership.
+        resolvedAttributes.insert(ctx->getResolvedAttributes().begin(), ctx->getResolvedAttributes().end());
+        ctx->getResolvedAttributes().clear();
+    }
 
     // Now merge in bad tokens for caching.
     tokens.insert(tokens.end(), badtokens.begin(), badtokens.end());
@@ -217,20 +239,25 @@ string SAML1Consumer::implementProtocol(
         );
     auto_ptr_char authnMethod(ssoStatement->getAuthenticationMethod());
 
-    vector<shibsp::Attribute*>& attrs = ctx->getResolvedAttributes();
-    string key = application.getServiceProvider().getSessionCache()->insert(
-        lifetime.second ? now + lifetime.second : 0,
-        application,
-        httpRequest.getRemoteAddr().c_str(),
-        issuerMetadata,
-        nameid.get(),
-        authnInstant.get(),
-        NULL,
-        authnMethod.get(),
-        NULL,
-        &tokens,
-        &attrs
-        );
-    attrs.clear();  // Attributes are owned by cache now.
-    return key;
+    try {
+        string key = application.getServiceProvider().getSessionCache()->insert(
+            lifetime.second ? now + lifetime.second : 0,
+            application,
+            httpRequest.getRemoteAddr().c_str(),
+            issuerMetadata,
+            nameid.get(),
+            authnInstant.get(),
+            NULL,
+            authnMethod.get(),
+            NULL,
+            &tokens,
+            &resolvedAttributes
+            );
+        resolvedAttributes.clear();  // Attributes are owned by cache now.
+        return key;
+    }
+    catch (exception&) {
+        for_each(resolvedAttributes.begin(), resolvedAttributes.end(), cleanup_pair<string,Attribute>());
+        throw;
+    }
 }
index ad69d9e..b63dbb9 100644 (file)
 #include "exceptions.h"
 #include "ServiceProvider.h"
 #include "SessionCache.h"
+#include "attribute/Attribute.h"
+#include "attribute/resolver/AttributeExtractor.h"
 #include "attribute/resolver/ResolutionContext.h"
 #include "handler/AssertionConsumerService.h"
 
 #include <saml/saml2/core/Protocols.h>
 #include <saml/saml2/profile/BrowserSSOProfileValidator.h>
 #include <saml/saml2/metadata/Metadata.h>
+#include <saml/saml2/metadata/MetadataCredentialCriteria.h>
 
 using namespace shibsp;
 using namespace opensaml::saml2;
 using namespace opensaml::saml2p;
+using namespace opensaml::saml2md;
 using namespace opensaml;
 using namespace xmltooling;
 using namespace log4cpp;
 using namespace std;
-using saml2md::EntityDescriptor;
 
 namespace shibsp {
 
@@ -185,14 +188,8 @@ string SAML2Consumer::implementProtocol(
         }
     }
 
-    // We look up decryption credentials based on the peer who did the encrypting.
-    CredentialCriteria cc;
-    if (policy.getIssuerMetadata()) {
-        auto_ptr_char assertingParty(dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent())->getEntityID());
-        cc.setPeerName(assertingParty.get());
-    }
+    // In case we need decryption...
     CredentialResolver* cr=application.getCredentialResolver();
-
     if (!cr && !encassertions.empty())
         m_log.warn("found encrypted assertions, but no CredentialResolver was available");
 
@@ -201,7 +198,10 @@ string SAML2Consumer::implementProtocol(
         saml2::Assertion* decrypted=NULL;
         try {
             Locker credlocker(cr);
-            auto_ptr<XMLObject> wrapper((*ea)->decrypt(*cr, application.getXMLString("entityID").second, &cc));
+            auto_ptr<MetadataCredentialCriteria> mcc(
+                policy.getIssuerMetadata() ? new MetadataCredentialCriteria(*policy.getIssuerMetadata()) : NULL
+                );
+            auto_ptr<XMLObject> wrapper((*ea)->decrypt(*cr, application.getXMLString("entityID").second, mcc.get()));
             decrypted = dynamic_cast<saml2::Assertion*>(wrapper.get());
             if (decrypted) {
                 wrapper.release();
@@ -289,8 +289,11 @@ string SAML2Consumer::implementProtocol(
                 m_log.warn("found encrypted NameID, but no decryption credential was available");
             else {
                 Locker credlocker(cr);
+                auto_ptr<MetadataCredentialCriteria> mcc(
+                    policy.getIssuerMetadata() ? new MetadataCredentialCriteria(*policy.getIssuerMetadata()) : NULL
+                    );
                 try {
-                    auto_ptr<XMLObject> decryptedID(encname->decrypt(*cr,application.getXMLString("entityID").second,&cc));
+                    auto_ptr<XMLObject> decryptedID(encname->decrypt(*cr,application.getXMLString("entityID").second,mcc.get()));
                     ssoName = dynamic_cast<NameID*>(decryptedID.get());
                     if (ssoName) {
                         ownedName = true;
@@ -307,16 +310,37 @@ string SAML2Consumer::implementProtocol(
     m_log.debug("SSO profile processing completed successfully");
 
     // We've successfully "accepted" at least one SSO token, along with any additional valid tokens.
-    // To complete processing, we need to resolve attributes and then create the session.
+    // To complete processing, we need to extract and resolve attributes and then create the session.
+
+    multimap<string,Attribute*> resolvedAttributes;
+    AttributeExtractor* extractor = application.getAttributeExtractor();
+    if (extractor) {
+        Locker extlocker(extractor);
+        for (vector<const opensaml::Assertion*>::const_iterator t = tokens.begin(); t!=tokens.end(); ++t) {
+            try {
+                extractor->extractAttributes(application, policy.getIssuerMetadata(), *(*t), resolvedAttributes);
+            }
+            catch (exception& ex) {
+                m_log.error("caught exception extracting attributes: %s", ex.what());
+            }
+        }
+    }
 
     try {
-        const EntityDescriptor* issuerMetadata = dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent());
+        const EntityDescriptor* issuerMetadata =
+            policy.getIssuerMetadata() ? dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent()) : NULL;
         auto_ptr<ResolutionContext> ctx(
-            resolveAttributes(application, httpRequest, issuerMetadata, ssoName, &tokens)
+            resolveAttributes(application, issuerMetadata, ssoName, &tokens, &resolvedAttributes)
             );
 
-        // Copy over any new tokens, but leave them in the context for cleanup.
-        tokens.insert(tokens.end(), ctx->getResolvedAssertions().begin(), ctx->getResolvedAssertions().end());
+        if (ctx.get()) {
+            // Copy over any new tokens, but leave them in the context for cleanup.
+            tokens.insert(tokens.end(), ctx->getResolvedAssertions().begin(), ctx->getResolvedAssertions().end());
+
+            // Copy over new attributes, and transfer ownership.
+            resolvedAttributes.insert(ctx->getResolvedAttributes().begin(), ctx->getResolvedAttributes().end());
+            ctx->getResolvedAttributes().clear();
+        }
 
         // Now merge in bad tokens for caching.
         tokens.insert(tokens.end(), badtokens.begin(), badtokens.end());
@@ -343,7 +367,6 @@ string SAML2Consumer::implementProtocol(
         auto_ptr_char index(ssoStatement->getSessionIndex());
         auto_ptr_char authnInstant(ssoStatement->getAuthnInstant() ? ssoStatement->getAuthnInstant()->getRawData() : NULL);
 
-        vector<shibsp::Attribute*>& attrs = ctx->getResolvedAttributes();
         string key = application.getServiceProvider().getSessionCache()->insert(
             sessionExp,
             application,
@@ -355,9 +378,9 @@ string SAML2Consumer::implementProtocol(
             authnClass.get(),
             authnDecl.get(),
             &tokens,
-            &attrs
+            &resolvedAttributes
             );
-        attrs.clear();  // Attributes are owned by cache now.
+        resolvedAttributes.clear();  // Attributes are owned by cache now.
 
         if (ownedName)
             delete ssoName;
@@ -369,6 +392,7 @@ string SAML2Consumer::implementProtocol(
         if (ownedName)
             delete ssoName;
         for_each(ownedtokens.begin(), ownedtokens.end(), xmltooling::cleanup<saml2::Assertion>());
+        for_each(resolvedAttributes.begin(), resolvedAttributes.end(), cleanup_pair<string,Attribute>());
         throw;
     }
 }
index bb4e840..32f8028 100644 (file)
@@ -29,11 +29,11 @@ using namespace xmltooling;
 using namespace std;
 
 namespace shibsp {
-    SHIBSP_DLLLOCAL PluginManager<SessionInitiator,pair<const DOMElement*,const char*>>::Factory ChainingSessionInitiatorFactory;
-    SHIBSP_DLLLOCAL PluginManager<SessionInitiator,pair<const DOMElement*,const char*>>::Factory Shib1SessionInitiatorFactory;
-    SHIBSP_DLLLOCAL PluginManager<SessionInitiator,pair<const DOMElement*,const char*>>::Factory SAML2SessionInitiatorFactory;
-    SHIBSP_DLLLOCAL PluginManager<SessionInitiator,pair<const DOMElement*,const char*>>::Factory WAYFSessionInitiatorFactory;
-    SHIBSP_DLLLOCAL PluginManager<SessionInitiator,pair<const DOMElement*,const char*>>::Factory SAMLDSSessionInitiatorFactory;
+    SHIBSP_DLLLOCAL PluginManager<SessionInitiator,string,pair<const DOMElement*,const char*>>::Factory ChainingSessionInitiatorFactory;
+    SHIBSP_DLLLOCAL PluginManager<SessionInitiator,string,pair<const DOMElement*,const char*>>::Factory Shib1SessionInitiatorFactory;
+    SHIBSP_DLLLOCAL PluginManager<SessionInitiator,string,pair<const DOMElement*,const char*>>::Factory SAML2SessionInitiatorFactory;
+    SHIBSP_DLLLOCAL PluginManager<SessionInitiator,string,pair<const DOMElement*,const char*>>::Factory WAYFSessionInitiatorFactory;
+    SHIBSP_DLLLOCAL PluginManager<SessionInitiator,string,pair<const DOMElement*,const char*>>::Factory SAMLDSSessionInitiatorFactory;
 };
 
 void SHIBSP_API shibsp::registerSessionInitiators()
index 92fc12d..888775b 100644 (file)
@@ -77,7 +77,7 @@ namespace shibsp {
             delete m_lock;\r
             m_obj.destroy();\r
             delete m_nameid;\r
-            for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair<string,Attribute>());\r
+            for_each(m_attributes.begin(), m_attributes.end(), cleanup_pair<string,Attribute>());\r
             for_each(m_tokens.begin(), m_tokens.end(), cleanup_pair<string,Assertion>());\r
         }\r
         \r
@@ -113,7 +113,7 @@ namespace shibsp {
         const char* getAuthnContextDeclRef() const {\r
             return m_obj["authncontext_decl"].string();\r
         }\r
-        const map<string,const Attribute*>& getAttributes() const {\r
+        const multimap<string,Attribute*>& getAttributes() const {\r
             if (m_attributes.empty())\r
                 unmarshallAttributes();\r
             return m_attributes;\r
@@ -149,7 +149,7 @@ namespace shibsp {
         int m_version;\r
         mutable DDF m_obj;\r
         saml2::NameID* m_nameid;\r
-        mutable map<string,const Attribute*> m_attributes;\r
+        mutable multimap<string,Attribute*> m_attributes;\r
         mutable vector<const char*> m_ids;\r
         mutable map<string,Assertion*> m_tokens;\r
         time_t m_expires,m_lastAccess;\r
@@ -174,7 +174,7 @@ namespace shibsp {
             const char* authncontext_class=NULL,\r
             const char* authncontext_decl=NULL,\r
             const vector<const Assertion*>* tokens=NULL,\r
-            const vector<Attribute*>* attributes=NULL\r
+            const multimap<string,Attribute*>* attributes=NULL\r
             );\r
         Session* find(const char* key, const Application& application, const char* client_addr=NULL, time_t timeout=0);\r
         void remove(const char* key, const Application& application, const char* client_addr);\r
@@ -208,7 +208,7 @@ void RemotedSession::unmarshallAttributes() const
     while (!attr.isnull()) {\r
         try {\r
             attribute = Attribute::unmarshall(attr);\r
-            m_attributes[attribute->getId()] = attribute;\r
+            m_attributes.insert(make_pair(attribute->getId(),attribute));\r
             if (m_cache->m_log.isDebugEnabled())\r
                 m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s",\r
                     attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");\r
@@ -351,7 +351,7 @@ string RemotedCache::insert(
     const char* authncontext_class,\r
     const char* authncontext_decl,\r
     const vector<const Assertion*>* tokens,\r
-    const vector<Attribute*>* attributes\r
+    const multimap<string,Attribute*>* attributes\r
     )\r
 {\r
     DDF in("insert::"REMOTED_SESSION_CACHE"::SessionCache");\r
@@ -407,8 +407,8 @@ string RemotedCache::insert(
     if (attributes) {\r
         DDF attr;\r
         DDF attrs = in.addmember("attributes").list();\r
-        for (vector<Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a) {\r
-            attr = (*a)->marshall();\r
+        for (multimap<string,Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a) {\r
+            attr = a->second->marshall();\r
             attrs.add(attr);\r
         }\r
     }\r
@@ -441,10 +441,10 @@ string RemotedCache::insert(
                 ") for (applicationId: " <<\r
                     application.getId() <<\r
                 ") {";\r
-            for (vector<Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a)\r
-                xlog->log.infoStream() << "\t" << (*a)->getId() << " (" << (*a)->valueCount() << " values)";\r
+            for (multimap<string,Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a)\r
+                xlog->log.infoStream() << "\t" << a->second->getId() << " (" << a->second->valueCount() << " values)";\r
             xlog->log.info("}");\r
-            for_each(attributes->begin(), attributes->end(), xmltooling::cleanup<Attribute>());\r
+            for_each(attributes->begin(), attributes->end(), cleanup_pair<string,Attribute>());\r
         }\r
 \r
         return out["key"].string();\r
index 17ddd2f..f15ee52 100644 (file)
@@ -102,7 +102,7 @@ namespace shibsp {
         const char* getAuthnContextDeclRef() const {\r
             return m_obj["authncontext_decl"].string();\r
         }\r
-        const map<string,const Attribute*>& getAttributes() const {\r
+        const multimap<string,Attribute*>& getAttributes() const {\r
             if (m_attributes.empty())\r
                 unmarshallAttributes();\r
             return m_attributes;\r
@@ -128,7 +128,7 @@ namespace shibsp {
 \r
         DDF m_obj;\r
         saml2::NameID* m_nameid;\r
-        mutable map<string,const Attribute*> m_attributes;\r
+        mutable multimap<string,Attribute*> m_attributes;\r
         mutable vector<const char*> m_ids;\r
         mutable map<string,Assertion*> m_tokens;\r
         SSCache* m_cache;\r
@@ -153,7 +153,7 @@ namespace shibsp {
             const char* authncontext_class=NULL,\r
             const char* authncontext_decl=NULL,\r
             const vector<const Assertion*>* tokens=NULL,\r
-            const vector<Attribute*>* attributes=NULL\r
+            const multimap<string,Attribute*>* attributes=NULL\r
             );\r
         Session* find(const char* key, const Application& application, const char* client_addr=NULL, time_t timeout=0);\r
         void remove(const char* key, const Application& application, const char* client_addr);\r
@@ -174,7 +174,7 @@ StoredSession::~StoredSession()
 {\r
     m_obj.destroy();\r
     delete m_nameid;\r
-    for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair<string,Attribute>());\r
+    for_each(m_attributes.begin(), m_attributes.end(), cleanup_pair<string,Attribute>());\r
     for_each(m_tokens.begin(), m_tokens.end(), cleanup_pair<string,Assertion>());\r
 }\r
 \r
@@ -186,7 +186,7 @@ void StoredSession::unmarshallAttributes() const
     while (!attr.isnull()) {\r
         try {\r
             attribute = Attribute::unmarshall(attr);\r
-            m_attributes[attribute->getId()] = attribute;\r
+            m_attributes.insert(make_pair(attribute->getId(), attribute));\r
             if (m_cache->m_log.isDebugEnabled())\r
                 m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s",\r
                     attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");\r
@@ -457,7 +457,7 @@ string SSCache::insert(
     const char* authncontext_class,\r
     const char* authncontext_decl,\r
     const vector<const Assertion*>* tokens,\r
-    const vector<Attribute*>* attributes\r
+    const multimap<string,Attribute*>* attributes\r
     )\r
 {\r
 #ifdef _DEBUG\r
@@ -517,8 +517,8 @@ string SSCache::insert(
     if (attributes) {\r
         DDF attr;\r
         DDF attrlist = obj.addmember("attributes").list();\r
-        for (vector<Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a) {\r
-            attr = (*a)->marshall();\r
+        for (multimap<string,Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a) {\r
+            attr = a->second->marshall();\r
             attrlist.add(attr);\r
         }\r
     }\r
@@ -570,10 +570,10 @@ string SSCache::insert(
             ") for (applicationId: " <<\r
                 application.getId() <<\r
             ") {";\r
-        for (vector<Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a)\r
-            xlog->log.infoStream() << "\t" << (*a)->getId() << " (" << (*a)->valueCount() << " values)";\r
+        for (multimap<string,Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a)\r
+            xlog->log.infoStream() << "\t" << a->second->getId() << " (" << a->second->valueCount() << " values)";\r
         xlog->log.info("}");\r
-        for_each(attributes->begin(), attributes->end(), xmltooling::cleanup<Attribute>());\r
+        for_each(attributes->begin(), attributes->end(), cleanup_pair<string,Attribute>());\r
     }\r
 \r
     return key.get();\r
index 97222be..f134dee 100644 (file)
@@ -158,22 +158,25 @@ bool Rule::authorized(const SPRequest& request, const Session* session) const
         return false;\r
     }\r
     \r
-    // Find the attribute matching the require rule.\r
-    map<string,const Attribute*>::const_iterator attr = session->getAttributes().find(m_alias);\r
-    if (attr == session->getAttributes().end()) {\r
+    // Find the attribute(s) matching the require rule.\r
+    pair<multimap<string,Attribute*>::const_iterator, multimap<string,Attribute*>::const_iterator> attrs =\r
+        session->getAttributes().equal_range(m_alias);\r
+    if (attrs.first == attrs.second) {\r
         request.log(SPRequest::SPWarn, string("rule requires attribute (") + m_alias + "), not found in session");\r
         return false;\r
     }\r
 \r
-    bool caseSensitive = attr->second->isCaseSensitive();\r
+    for (; attrs.first != attrs.second; ++attrs.first) {\r
+        bool caseSensitive = attrs.first->second->isCaseSensitive();\r
 \r
-    // Now we have to intersect the attribute's values against the rule's list.\r
-    const vector<string>& vals = attr->second->getSerializedValues();\r
-    for (vector<string>::const_iterator i=m_vals.begin(); i!=m_vals.end(); ++i) {\r
-        for (vector<string>::const_iterator j=vals.begin(); j!=vals.end(); ++j) {\r
-            if ((caseSensitive && *i == *j) || (!caseSensitive && !strcasecmp(i->c_str(),j->c_str()))) {\r
-                request.log(SPRequest::SPDebug, string("AccessControl plugin expecting ") + *j + ", authz granted");\r
-                return true;\r
+        // Now we have to intersect the attribute's values against the rule's list.\r
+        const vector<string>& vals = attrs.first->second->getSerializedValues();\r
+        for (vector<string>::const_iterator i=m_vals.begin(); i!=m_vals.end(); ++i) {\r
+            for (vector<string>::const_iterator j=vals.begin(); j!=vals.end(); ++j) {\r
+                if ((caseSensitive && *i == *j) || (!caseSensitive && !strcasecmp(i->c_str(),j->c_str()))) {\r
+                    request.log(SPRequest::SPDebug, string("AccessControl plugin expecting ") + *j + ", authz granted");\r
+                    return true;\r
+                }\r
             }\r
         }\r
     }\r
index aba398c..ea3f662 100644 (file)
@@ -30,6 +30,7 @@
 #include "SPConfig.h"\r
 #include "SPRequest.h"\r
 #include "TransactionLog.h"\r
+#include "attribute/resolver/AttributeExtractor.h"\r
 #include "attribute/resolver/AttributeResolver.h"\r
 #include "handler/SessionInitiator.h"\r
 #include "remoting/ListenerService.h"\r
@@ -86,12 +87,12 @@ namespace {
         TrustEngine* getTrustEngine() const {\r
             return (!m_trust && m_base) ? m_base->getTrustEngine() : m_trust;\r
         }\r
+        AttributeExtractor* getAttributeExtractor() const {\r
+            return (!m_attrExtractor && m_base) ? m_base->getAttributeExtractor() : m_attrExtractor;\r
+        }\r
         AttributeResolver* getAttributeResolver() const {\r
             return (!m_attrResolver && m_base) ? m_base->getAttributeResolver() : m_attrResolver;\r
         }\r
-        const set<string>* getAttributeIds() const {\r
-            return (m_attributeIds.empty() && m_base) ? m_base->getAttributeIds() : (m_attributeIds.empty() ? NULL : &m_attributeIds);\r
-        }\r
         CredentialResolver* getCredentialResolver() const {\r
             return (!m_credResolver && m_base) ? m_base->getCredentialResolver() : m_credResolver;\r
         }\r
@@ -118,10 +119,10 @@ namespace {
         string m_hash;\r
         MetadataProvider* m_metadata;\r
         TrustEngine* m_trust;\r
+        AttributeExtractor* m_attrExtractor;\r
         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
@@ -287,6 +288,8 @@ namespace {
     static const XMLCh _Application[] =         UNICODE_LITERAL_11(A,p,p,l,i,c,a,t,i,o,n);\r
     static const XMLCh Applications[] =         UNICODE_LITERAL_12(A,p,p,l,i,c,a,t,i,o,n,s);\r
     static const XMLCh _ArtifactMap[] =         UNICODE_LITERAL_11(A,r,t,i,f,a,c,t,M,a,p);\r
+    static const XMLCh _AttributeExtractor[] =  UNICODE_LITERAL_18(A,t,t,r,i,b,u,t,e,E,x,t,r,a,c,t,o,r);\r
+    static const XMLCh _AttributeFilter[] =     UNICODE_LITERAL_15(A,t,t,r,i,b,u,t,e,F,i,l,t,e,r);\r
     static const XMLCh _AttributeResolver[] =   UNICODE_LITERAL_17(A,t,t,r,i,b,u,t,e,R,e,s,o,l,v,e,r);\r
     static const XMLCh _CredentialResolver[] =  UNICODE_LITERAL_18(C,r,e,d,e,n,t,i,a,l,R,e,s,o,l,v,e,r);\r
     static const XMLCh DefaultRelyingParty[] =  UNICODE_LITERAL_19(D,e,f,a,u,l,t,R,e,l,y,i,n,g,P,a,r,t,y);\r
@@ -338,8 +341,8 @@ XMLApplication::XMLApplication(
     const ServiceProvider* sp,\r
     const DOMElement* e,\r
     const XMLApplication* base\r
-    ) : m_sp(sp), m_base(base), m_metadata(NULL), m_trust(NULL), m_attrResolver(NULL), m_credResolver(NULL),\r
-        m_partyDefault(NULL), m_sessionInitDefault(NULL), m_acsDefault(NULL)\r
+    ) : m_sp(sp), m_base(base), m_metadata(NULL), m_trust(NULL), m_attrExtractor(NULL), m_attrResolver(NULL),\r
+        m_credResolver(NULL), m_partyDefault(NULL), m_sessionInitDefault(NULL), m_acsDefault(NULL)\r
 {\r
 #ifdef _DEBUG\r
     xmltooling::NDC ndc("XMLApplication");\r
@@ -360,25 +363,6 @@ 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("attributeIds");\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
@@ -526,6 +510,18 @@ XMLApplication::XMLApplication(
         }\r
 \r
         if (conf.isEnabled(SPConfig::AttributeResolution)) {\r
+            child = XMLHelper::getFirstChildElement(e,_AttributeExtractor);\r
+            if (child) {\r
+                auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
+                log.info("building AttributeExtractor of type %s...",type.get());\r
+                try {\r
+                    m_attrExtractor = conf.AttributeExtractorManager.newPlugin(type.get(),child);\r
+                }\r
+                catch (exception& ex) {\r
+                    log.crit("error building AttributeExtractor: %s", ex.what());\r
+                }\r
+            }\r
+\r
             child = XMLHelper::getFirstChildElement(e,_AttributeResolver);\r
             if (child) {\r
                 auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
@@ -596,6 +592,7 @@ void XMLApplication::cleanup()
     for_each(m_handlers.begin(),m_handlers.end(),xmltooling::cleanup<Handler>());\r
     delete m_credResolver;\r
     delete m_attrResolver;\r
+    delete m_attrExtractor;\r
     delete m_trust;\r
     delete m_metadata;\r
 }\r
@@ -617,6 +614,8 @@ short XMLApplication::acceptNode(const DOMNode* node) const
         XMLString::equals(name,_MetadataProvider) ||\r
         XMLString::equals(name,_TrustEngine) ||\r
         XMLString::equals(name,_CredentialResolver) ||\r
+        XMLString::equals(name,_AttributeFilter) ||\r
+        XMLString::equals(name,_AttributeExtractor) ||\r
         XMLString::equals(name,_AttributeResolver))\r
         return FILTER_REJECT;\r
 \r
index 835d8e2..2c40905 100644 (file)
@@ -36,9 +36,9 @@ using namespace std;
 
 namespace shibsp {
     //SHIBSP_DLLLOCAL PluginManager<ListenerService,const DOMElement*>::Factory MemoryListenerServiceFactory;
-    SHIBSP_DLLLOCAL PluginManager<ListenerService,const DOMElement*>::Factory TCPListenerServiceFactory;
+    SHIBSP_DLLLOCAL PluginManager<ListenerService,string,const DOMElement*>::Factory TCPListenerServiceFactory;
 #ifndef WIN32
-    SHIBSP_DLLLOCAL PluginManager<ListenerService,const DOMElement*>::Factory UnixListenerServiceFactory;
+    SHIBSP_DLLLOCAL PluginManager<ListenerService,string,const DOMElement*>::Factory UnixListenerServiceFactory;
 #endif
 };
 
index bc9f27c..c8ed9bc 100644 (file)
@@ -79,7 +79,7 @@ namespace shibsp {
         mutable map<const ObservableMetadataProvider*,credmap_t> m_credentialMap;
     };
     
-    SHIBSP_DLLLOCAL PluginManager<TrustEngine,const DOMElement*>::Factory PKIXTrustEngineFactory;
+    SHIBSP_DLLLOCAL PluginManager<TrustEngine,string,const DOMElement*>::Factory PKIXTrustEngineFactory;
 
     TrustEngine* SHIBSP_DLLLOCAL PKIXTrustEngineFactory(const DOMElement* const & e)
     {
index f8c15af..3f8930f 100644 (file)
                                        >\r
                                </File>\r
                                <File\r
-                                       RelativePath=".\attribute\SimpleAttributeDecoder.cpp"\r
+                                       RelativePath=".\attribute\StringAttributeDecoder.cpp"\r
                                        >\r
                                </File>\r
                                <Filter\r
                                                Name="impl"\r
                                                >\r
                                                <File\r
-                                                       RelativePath=".\attribute\resolver\impl\AttributeResolver.cpp"\r
+                                                       RelativePath=".\attribute\resolver\impl\QueryAttributeResolver.cpp"\r
                                                        >\r
                                                </File>\r
                                                <File\r
-                                                       RelativePath=".\attribute\resolver\impl\SimpleAttributeResolver.cpp"\r
+                                                       RelativePath=".\attribute\resolver\impl\XMLAttributeExtractor.cpp"\r
                                                        >\r
                                                </File>\r
                                        </Filter>\r
                                        Name="resolver"\r
                                        >\r
                                        <File\r
+                                               RelativePath=".\attribute\resolver\AttributeExtractor.h"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\attribute\resolver\AttributeResolver.h"\r
                                                >\r
                                        </File>\r
index 2c44f52..c069e02 100644 (file)
@@ -41,11 +41,19 @@ const XMLCh shibspconstants::SHIBMD_NS[] = // urn:mace:shibboleth:metadata:1.0
 \r
 const XMLCh shibspconstants::SHIBMD_PREFIX[] = UNICODE_LITERAL_6(s,h,i,b,m,d);\r
 \r
-const XMLCh shibspconstants::SHIB2SPCONFIG_NS[] = // urn:mace:shibboleth:sp:config:2.0\r
+const XMLCh shibspconstants::SHIB2SPCONFIG_NS[] = // urn:mace:shibboleth:2.0:native:sp:config\r
 { chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_m, chLatin_a, chLatin_c, chLatin_e, chColon,\r
   chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chColon,\r
-  chLatin_s, chLatin_p, chColon, chLatin_c, chLatin_o, chLatin_n, chLatin_f, chLatin_i, chLatin_g, chColon,\r
-  chDigit_2, chPeriod, chDigit_0, chNull\r
+  chDigit_2, chPeriod, chDigit_0, chColon, chLatin_n, chLatin_a, chLatin_t, chLatin_i, chLatin_v, chLatin_e, chColon,\r
+  chLatin_s, chLatin_p, chColon, chLatin_c, chLatin_o, chLatin_n, chLatin_f, chLatin_i, chLatin_g, chNull\r
+};\r
+\r
+const XMLCh shibspconstants::SHIB2ATTRIBUTEMAP_NS[] = // urn:mace:shibboleth:2.0:attribute-map\r
+{ chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_m, chLatin_a, chLatin_c, chLatin_e, chColon,\r
+  chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chColon,\r
+  chDigit_2, chPeriod, chDigit_0, chColon,\r
+  chLatin_a, chLatin_t, chLatin_t, chLatin_r, chLatin_i, chLatin_b, chLatin_u, chLatin_t, chLatin_e, chDash,\r
+  chLatin_m, chLatin_a, chLatin_p, chNull\r
 };\r
 \r
 const XMLCh shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI[] = // urn:mace:shibboleth:1.0:attributeNamespace:uri\r
index 74372c3..d0e39ea 100644 (file)
@@ -37,9 +37,12 @@ namespace shibspconstants {
     /** Shibboleth Metadata QName prefix ("shibmd") */
     extern SHIBSP_API const XMLCh SHIBMD_PREFIX[];
 
-    /** Shibboleth 2.0 SP configuration namespace ("urn:mace:shibboleth:sp:config:2.0") */
+    /** Shibboleth 2.0 SP configuration namespace ("urn:mace:shibboleth:2.0:native:sp:config") */
     extern SHIBSP_API const XMLCh SHIB2SPCONFIG_NS[];
 
+    /** Shibboleth 2.0 attribute mapping namespace ("urn:mace:shibboleth:2.0:attribute-map") */
+    extern SHIBSP_API const XMLCh SHIB2ATTRIBUTEMAP_NS[];
+
     /** Shibboleth 1.x Protocol Enumeration constant ("urn:mace:shibboleth:1.0") */
     extern SHIBSP_API const XMLCh SHIB1_PROTOCOL_ENUM[];