@@ -0,0 +1,2295 @@
+import os%0Aimport urllib%0Aimport saml2%0Aimport base64%0A%0Afrom saml2 import samlp, saml%0Afrom saml2.sigver import correctly_signed_response%0Afrom saml2.soap import SOAPClient%0Afrom saml2.client import Saml2Client%0A%0ADEFAULT_BINDING = saml2.BINDING_HTTP_REDIRECT%0A%0Aclass AttributeResolver():%0A    def __init__(self, environ, metadata=None, xmlsec_binary=None,%0A                        key_file=None, cert_file=None):%0A        self.metadata = metadata%0A        self.saml2client = Saml2Client(environ, metadata=metadata,%0A                            xmlsec_binary=xmlsec_binary,%0A                            key_file=key_file,%0A                            cert_file=cert_file)%0A        %0A    def extend(self, subject_id, issuer, vo_members, nameid_format,%0A                log=None):%0A        %22%22%22 %0A        :param subject_id: The identifier by which the subject is know%0A            among all the participents of the VO%0A        :param issuer: Who am I the poses the query%0A        :param vo_members: The entity IDs of the IdP who I'm going to ask%0A            for extra attributes%0A        :param nameid_format: Used to make the IdPs aware of what's going%0A            on here%0A        :param log: Where to log exciting information%0A        :return: A dictionary with all the collected information about the%0A            subject%0A        %22%22%22%0A        extended_identity = %7B%7D%0A        for member in vo_members:            %0A            for ass in self.metadata.attribute_services(member):%0A                for attr_serv in ass.attribute_service:%0A                    log and log.info(%22Send attribute request to %25s%22 %25 %5C%0A                                        attr_serv.location)%0A                    resp = self.saml2client.attribute_request(subject_id, %0A                                issuer, %0A                                attr_serv.location, %0A                                format=nameid_format, log=log)%0A                    if resp:%0A                        # unnecessary%0A                        del resp%5B%22__userid%22%5D%0A                        for attr,val in resp.items():%0A                            try:%0A                                extended_identity%5Battr%5D.extend(val)%0A                            except KeyError:%0A                                extended_identity%5Battr%5D = val%0A                    %0A        return extended_identity

Support for attribute aggregation