Ejemplo n.º 1
0
    def createAttributes(self):
        """Create SAML Attributes for use in an Assertion or AttributeQuery"""
        
        attributes = []
        if self.firstName is not None:    
            # special case handling for 'FirstName' attribute
            fnAttribute = Attribute()
            fnAttribute.name = "urn:esg:first:name"
            fnAttribute.nameFormat = SAMLUtil.XSSTRING_NS
            fnAttribute.friendlyName = "FirstName"

            firstName = XSStringAttributeValue()
            firstName.value = self.firstName
            fnAttribute.attributeValues.append(firstName)

            attributes.append(fnAttribute)
        

        if self.lastName is not None:
            # special case handling for 'LastName' attribute
            lnAttribute = Attribute()
            lnAttribute.name = "urn:esg:last:name"
            lnAttribute.nameFormat = SAMLUtil.XSSTRING_NS
            lnAttribute.friendlyName = "LastName"

            lastName = XSStringAttributeValue()
            lastName.value = self.lastName
            lnAttribute.attributeValues.append(lastName)

            attributes.append(lnAttribute)
        

        if self.emailAddress is not None:
            # special case handling for 'LastName' attribute
            emailAddressAttribute = Attribute()
            emailAddressAttribute.name = "urn:esg:email:address"
            emailAddressAttribute.nameFormat = SAMLConstants.XSD_NS+"#"+\
                                        XSStringAttributeValue.TYPE_LOCAL_NAME
            emailAddressAttribute.friendlyName = "emailAddress"

            emailAddress = XSStringAttributeValue()
            emailAddress.value = self.emailAddress
            emailAddressAttribute.attributeValues.append(emailAddress)

            attributes.append(emailAddressAttribute)
        
        for name, value in self.__miscAttrList:
            attribute = Attribute()
            attribute.name = name
            attribute.nameFormat = SAMLUtil.XSSTRING_NS

            stringAttributeValue = XSStringAttributeValue()
            stringAttributeValue.value = value
            attribute.attributeValues.append(stringAttributeValue)

            attributes.append(attribute)
            
        return attributes
Ejemplo n.º 2
0
    def test03InvalidAttributesRequested(self):
        attributeQuery = self._createAttributeQuery()
        
        # Add an unsupported Attribute name
        attribute = Attribute()
        attribute.name = "urn:my:attribute"
        attribute.nameFormat = XMLConstants.XSD_NS+"#"+\
                                    XSStringAttributeValue.TYPE_LOCAL_NAME
        attribute.friendlyName = "myAttribute"
        attributeQuery.attributes.append(attribute)     
        
        request = self._makeRequest(attributeQuery=attributeQuery)
           
        header = {
            'soapAction': "http://www.oasis-open.org/committees/security",
            'Content-length': str(len(request)),
            'Content-type': 'text/xml'
        }
       
        response = self.app.post(self.__class__.SERVICE_URI, 
                                 params=request, 
                                 headers=header, 
                                 status=200)
        
        print(("Response status=%d" % response.status))
        
        samlResponse = self._getSAMLResponse(response.body)

        self.assertTrue(samlResponse.status.statusCode.value == \
                     StatusCode.INVALID_ATTR_NAME_VALUE_URI)
    def test02SendQuery(self):
        query_binding = AttributeQuerySslSOAPBinding()
        
        attribute_query = AttributeQueryFactory.create()
        attribute_query.subject.nameID.format = self.__class__.SUBJECT_FORMAT
        attribute_query.subject.nameID.value = self.__class__.SUBJECT
        attribute_query.issuerName = '/O=Site A/CN=Authorisation Service'


        attribute = Attribute()
        attribute.name = 'urn:ndg:saml:emailaddress'
        attribute.friendlyName = 'emailAddress'
        attribute.nameFormat = 'http://www.w3.org/2001/XMLSchema'
        
        attribute_query.attributes.append(attribute)
        
        query_binding.clockSkewTolerance = 2.
        query_binding.sslCACertDir = self.__class__.CLIENT_CACERT_DIR
        query_binding.sslCertFilePath = self.__class__.CLIENT_CERT_FILEPATH
        query_binding.sslPriKeyFilePath = self.__class__.CLIENT_PRIKEY_FILEPATH
        query_binding.sslValidDNs = self.__class__.VALID_DNS
        
        response = query_binding.send(attribute_query, 
                                      uri=self.__class__.SERVICE_URI)
        
        # Convert back to ElementTree instance read for string output
        samlResponseElem = ResponseElementTree.toXML(response)
        
        print("SAML Response ...")
        print(ElementTree.tostring(samlResponseElem))
        print("Pretty print SAML Response ...")
        print(prettyPrint(samlResponseElem))
        
        self.assert_(response.status.statusCode.value==StatusCode.SUCCESS_URI)
Ejemplo n.º 4
0
    def test03InvalidAttributesRequested(self):
        attributeQuery = self._createAttributeQuery()

        # Add an unsupported Attribute name
        attribute = Attribute()
        attribute.name = "urn:my:attribute"
        attribute.nameFormat = XMLConstants.XSD_NS+"#"+\
                                    XSStringAttributeValue.TYPE_LOCAL_NAME
        attribute.friendlyName = "myAttribute"
        attributeQuery.attributes.append(attribute)

        request = self._makeRequest(attributeQuery=attributeQuery)

        header = {
            'soapAction': "http://www.oasis-open.org/committees/security",
            'Content-length': str(len(request)),
            'Content-type': 'text/xml'
        }

        response = self.app.post('/attributeauthority/saml',
                                 params=request,
                                 headers=header,
                                 status=200)

        print("Response status=%d" % response.status)

        samlResponse = self._getSAMLResponse(response.body)

        self.assert_(samlResponse.status.statusCode.value == \
                     StatusCode.INVALID_ATTR_NAME_VALUE_URI)
Ejemplo n.º 5
0
    def _createAttributeQuery(self,
                            issuer="/O=Site A/CN=Authorisation Service",
                            subject="https://openid.localhost/philip.kershaw"):
        """Helper to create a query"""
        attributeQuery = AttributeQuery()
        attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20)
        attributeQuery.id = str(uuid4())
        attributeQuery.issueInstant = datetime.utcnow()
        
        attributeQuery.issuer = Issuer()
        attributeQuery.issuer.format = Issuer.X509_SUBJECT
        attributeQuery.issuer.value = issuer
                        
        attributeQuery.subject = Subject()  
        attributeQuery.subject.nameID = NameID()
        attributeQuery.subject.nameID.format = "urn:ndg:saml:test:openid"
        attributeQuery.subject.nameID.value = subject
                                    
        
        # special case handling for 'FirstName' attribute
        fnAttribute = Attribute()
        fnAttribute.name = TestAttributeServiceMiddleware.FIRSTNAME_ATTRNAME
        fnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
        fnAttribute.friendlyName = "FirstName"

        attributeQuery.attributes.append(fnAttribute)
    
        # special case handling for 'LastName' attribute
        lnAttribute = Attribute()
        lnAttribute.name = TestAttributeServiceMiddleware.LASTNAME_ATTRNAME
        lnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
        lnAttribute.friendlyName = "LastName"

        attributeQuery.attributes.append(lnAttribute)
    
        # special case handling for 'LastName' attribute
        emailAddressAttribute = Attribute()
        emailAddressAttribute.name = \
                            TestAttributeServiceMiddleware.EMAILADDRESS_ATTRNAME
        emailAddressAttribute.nameFormat = XMLConstants.XSD_NS+"#"+\
                                    XSStringAttributeValue.TYPE_LOCAL_NAME
        emailAddressAttribute.friendlyName = "emailAddress"

        attributeQuery.attributes.append(emailAddressAttribute)  

        return attributeQuery
Ejemplo n.º 6
0
    def _createAttributeQuery(
            self,
            issuer="/O=Site A/CN=Authorisation Service",
            subject="https://openid.localhost/philip.kershaw"):
        attributeQuery = AttributeQuery()
        attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20)
        attributeQuery.id = str(uuid4())
        attributeQuery.issueInstant = datetime.utcnow()

        attributeQuery.issuer = Issuer()
        attributeQuery.issuer.format = Issuer.X509_SUBJECT
        attributeQuery.issuer.value = issuer

        attributeQuery.subject = Subject()
        attributeQuery.subject.nameID = NameID()
        attributeQuery.subject.nameID.format = ESGFSamlNamespaces.NAMEID_FORMAT
        attributeQuery.subject.nameID.value = subject

        # special case handling for 'FirstName' attribute
        fnAttribute = Attribute()
        fnAttribute.name = ESGFSamlNamespaces.FIRSTNAME_ATTRNAME
        fnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
        fnAttribute.friendlyName = "FirstName"

        attributeQuery.attributes.append(fnAttribute)

        # special case handling for 'LastName' attribute
        lnAttribute = Attribute()
        lnAttribute.name = ESGFSamlNamespaces.LASTNAME_ATTRNAME
        lnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
        lnAttribute.friendlyName = "LastName"

        attributeQuery.attributes.append(lnAttribute)

        # special case handling for 'LastName' attribute
        emailAddressAttribute = Attribute()
        emailAddressAttribute.name = ESGFSamlNamespaces.EMAILADDRESS_ATTRNAME
        emailAddressAttribute.nameFormat = XMLConstants.XSD_NS+"#"+\
                                    XSStringAttributeValue.TYPE_LOCAL_NAME
        emailAddressAttribute.friendlyName = "emailAddress"

        attributeQuery.attributes.append(emailAddressAttribute)

        return attributeQuery
Ejemplo n.º 7
0
    def create_attribute_query(self):
        attr_query = AttributeQuery()

        self._set_query_common_attrs(attr_query)
        
        for i, name in enumerate(self.attribute_names): 
            attribute = Attribute()
            attribute.name = name
            
            if self.attribute_friendly_names[i] is not None:
                attribute.friendlyName = self.attribute_friendly_names[i]
                
            attribute.nameFormat = self.attribute_formats[i]
            
            attr_query.attributes.append(attribute)
        
        return attr_query
Ejemplo n.º 8
0
        def attributeQuery(query, response):
            """Attribute Query interface called by the next middleware in the 
            stack the SAML SOAP Query interface middleware instance
            (ndg.saml.saml2.binding.soap.server.wsgi.queryinterface.SOAPQueryInterfaceMiddleware)
            """
            response.issueInstant = datetime.utcnow()
            response.id = str(uuid4())            
            response.inResponseTo = query.id

            if query.issuer.value not in self.__class__.VALID_QUERY_ISSUERS:
                response.status.statusCode.value = \
                                            StatusCode.REQUEST_DENIED_URI 
                response.status.statusMessage.value = 'Invalid issuer'  
                return response    

            if query.subject.nameID.value not in self.__class__.VALID_SUBJECTS:
                response.status.statusCode.value = \
                                            StatusCode.UNKNOWN_PRINCIPAL_URI 
                response.status.statusMessage.value = 'Unknown user'  
                return response    
                
            assertion = Assertion()
            
            assertion.version = SAMLVersion(SAMLVersion.VERSION_20)
            assertion.id = str(uuid4())
            assertion.issueInstant = response.issueInstant
            
            assertion.conditions = Conditions()
            assertion.conditions.notBefore = assertion.issueInstant
            assertion.conditions.notOnOrAfter = \
                assertion.conditions.notBefore + timedelta(seconds=60*60*8)
            
            assertion.subject = Subject()  
            assertion.subject.nameID = NameID()
            assertion.subject.nameID.format = query.subject.nameID.format
            assertion.subject.nameID.value = query.subject.nameID.value
    
            assertion.attributeStatements.append(AttributeStatement())                
            
            for attribute in query.attributes:
                if attribute.name == self.__class__.FIRSTNAME_ATTRNAME:
                    # special case handling for 'FirstName' attribute
                    fnAttribute = Attribute()
                    fnAttribute.name = attribute.name
                    fnAttribute.nameFormat = attribute.nameFormat
                    fnAttribute.friendlyName = attribute.friendlyName
        
                    firstName = XSStringAttributeValue()
                    firstName.value = self.firstName
                    fnAttribute.attributeValues.append(firstName)
        
                    assertion.attributeStatements[0].attributes.append(
                                                                    fnAttribute)
                
                elif attribute.name == self.__class__.LASTNAME_ATTRNAME:
                    lnAttribute = Attribute()
                    lnAttribute.name = attribute.name
                    lnAttribute.nameFormat = attribute.nameFormat
                    lnAttribute.friendlyName = attribute.friendlyName
        
                    lastName = XSStringAttributeValue()
                    lastName.value = self.lastName
                    lnAttribute.attributeValues.append(lastName)
        
                    assertion.attributeStatements[0].attributes.append(
                                                                    lnAttribute)
                   
                elif (attribute.name == self.__class__.EMAILADDRESS_ATTRNAME and
                      query.issuer.value == 
                                        self.__class__.VALID_QUERY_ISSUERS[0]):
                    emailAddressAttribute = Attribute()
                    emailAddressAttribute.name = attribute.name
                    emailAddressAttribute.nameFormat = attribute.nameFormat
                    emailAddressAttribute.friendlyName = attribute.friendlyName
        
                    emailAddress = XSStringAttributeValue()
                    emailAddress.value = self.emailAddress
                    emailAddressAttribute.attributeValues.append(emailAddress)
        
                    assertion.attributeStatements[0].attributes.append(
                                                        emailAddressAttribute)
                else:
                    response.status.statusCode.value = \
                                        StatusCode.INVALID_ATTR_NAME_VALUE_URI
                    return response
                                    
            
            response.assertions.append(assertion)
            response.status.statusCode.value = StatusCode.SUCCESS_URI        
    
            return response
    def getAttributes(self, attributeQuery, response):
        """Attribute Authority SAML AttributeQuery

        @type attributeQuery: saml.saml2.core.AttributeQuery
        @param userId: query containing requested attributes
        @type response: saml.saml2.core.Response
        @param response: add an assertion with the list of attributes
        for the given subject ID in the query or set an error Status code and
        message
        @raise AttributeInterfaceError: an error occured requesting
        attributes
        @raise AttributeReleaseDeniedError: Requestor was denied release of the
        requested attributes
        @raise AttributeNotKnownError: Requested attribute names are not known
        to this authority
        """
        userId = attributeQuery.subject.nameID.value
        requestedAttributeNames = [attribute.name
                                   for attribute in attributeQuery.attributes]

        requestorDN = X500DN.from_string(attributeQuery.issuer.value)

        if not self._queryDbForSamlSubject(userId):
            raise UserIdNotKnown('Subject Id "%s" is not known to this '
                                 'authority' % userId)

        if (len(self.samlValidRequestorDNs) > 0 and
            requestorDN not in self.samlValidRequestorDNs):
            raise InvalidRequestorId('Requester identity "%s" is invalid' %
                                     requestorDN)

        unknownAttrNames = [attrName for attrName in requestedAttributeNames
                            if attrName not in self.__samlAttribute2SqlQuery]

        if len(unknownAttrNames) > 0:
            raise AttributeNotKnownError("Unknown attributes requested: %r" %
                                         unknownAttrNames)

        # Create a new assertion to hold the attributes to be returned
        assertion = Assertion()

        assertion.version = SAMLVersion(SAMLVersion.VERSION_20)
        assertion.id = str(uuid4())
        assertion.issueInstant = response.issueInstant

        # Assumes SAML response issuer details as set by -
        # ndg.security.server.wsgi.saml.SOAPQueryInterfaceMiddleware
        assertion.issuer = Issuer()
        assertion.issuer.value = response.issuer.value

        if response.issuer.format:
            assertion.issuer.format = response.issuer.format

        assertion.conditions = Conditions()
        assertion.conditions.notBefore = assertion.issueInstant
        assertion.conditions.notOnOrAfter = (assertion.conditions.notBefore +
                                             self.samlAssertionLifetime)

        assertion.subject = Subject()
        assertion.subject.nameID = NameID()
        assertion.subject.nameID.format = attributeQuery.subject.nameID.format
        assertion.subject.nameID.value = attributeQuery.subject.nameID.value

        attributeStatement = AttributeStatement()

        # Query the database for the requested attributes and return them
        # mapped to their attribute names as specified by the attributeNames
        # property
        for requestedAttribute in attributeQuery.attributes:
            attributeVals = self._queryDbForSamlAttributes(
                                                    requestedAttribute.name,
                                                    userId)

            # Make a new SAML attribute object to hold the values obtained
            attribute = Attribute()
            attribute.name = requestedAttribute.name
            attribute.nameFormat = requestedAttribute.nameFormat

            if requestedAttribute.friendlyName is not None:
                attribute.friendlyName = requestedAttribute.friendlyName

            # Call specific conversion utility to convert the retrieved field
            # to the correct SAML attribute value type
            try:
                field2SamlAttributeVal = self.__samlAttribute2SqlQuery[
                                        requestedAttribute.name][-1]
            except (IndexError, TypeError) as e:
                raise AttributeInterfaceConfigError('Bad format for SAML '
                                                    'attribute to SQL query '
                                                    'look-up for attribute '
                                                    'name %r: %s' %
                                                    (requestedAttribute.name,
                                                    e))

            for val in attributeVals:
                attributeValue = field2SamlAttributeVal(val)
                attribute.attributeValues.append(attributeValue)

            attributeStatement.attributes.append(attribute)

        assertion.attributeStatements.append(attributeStatement)
        response.assertions.append(assertion)
    def test05SamlAttributeQuery(self):
        if self.skipTests:
            return
        
        # Prepare a client query
        attributeQuery = AttributeQuery()
        attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20)
        attributeQuery.id = str(uuid4())
        attributeQuery.issueInstant = datetime.utcnow()
        
        attributeQuery.issuer = Issuer()
        attributeQuery.issuer.format = Issuer.X509_SUBJECT
        attributeQuery.issuer.value = '/O=ESG/OU=NCAR/CN=Gateway'
                        
                        
        attributeQuery.subject = Subject()  
        attributeQuery.subject.nameID = NameID()
        attributeQuery.subject.nameID.format = ESGFSamlNamespaces.NAMEID_FORMAT
        attributeQuery.subject.nameID.value = \
                                SQLAlchemyAttributeInterfaceTestCase.OPENID_URI
    
        emailAddressAttribute = Attribute()
        emailAddressAttribute.name = ESGFSamlNamespaces.EMAILADDRESS_ATTRNAME
        emailAddressAttribute.nameFormat = "InvalidFormat"
        emailAddressAttribute.friendlyName = "EmailAddress"

        attributeQuery.attributes.append(emailAddressAttribute)                                   
    
        authzAttribute = Attribute()
        authzAttribute.name = \
            SQLAlchemyAttributeInterfaceTestCase.ATTRIBUTE_NAMES[0]
        authzAttribute.nameFormat = XSStringAttributeValue.DEFAULT_FORMAT
        authzAttribute.friendlyName = "authz"

        attributeQuery.attributes.append(authzAttribute)                                   
        
        # Add the response - the interface will populate with an assertion as
        # appropriate
        samlResponse = Response()
        
        samlResponse.issueInstant = datetime.utcnow()
        samlResponse.id = str(uuid4())
        samlResponse.issuer = Issuer()
        
        # Initialise to success status but reset on error
        samlResponse.status = Status()
        samlResponse.status.statusCode = StatusCode()
        samlResponse.status.statusMessage = StatusMessage()
        samlResponse.status.statusCode.value = StatusCode.SUCCESS_URI
        
        # Nb. SAML 2.0 spec says issuer format must be omitted
        samlResponse.issuer.value = "CEDA"
        
        samlResponse.inResponseTo = attributeQuery.id
        
        # Set up the interface object
        
        # Define queries for SAML attribute names
        samlAttribute2SqlQuery = {
            ESGFSamlNamespaces.FIRSTNAME_ATTRNAME: 
                SQLAlchemyAttributeInterfaceTestCase.SAML_FIRSTNAME_SQLQUERY,
            
            ESGFSamlNamespaces.LASTNAME_ATTRNAME: 
                SQLAlchemyAttributeInterfaceTestCase.SAML_LASTNAME_SQLQUERY,
        
            ESGFSamlNamespaces.EMAILADDRESS_ATTRNAME: 
                SQLAlchemyAttributeInterfaceTestCase.SAML_EMAILADDRESS_SQLQUERY,
        
            SQLAlchemyAttributeInterfaceTestCase.ATTRIBUTE_NAMES[0]: 
                SQLAlchemyAttributeInterfaceTestCase.SAML_ATTRIBUTES_SQLQUERY
        }
        
        attributeInterface = SQLAlchemyAttributeInterface(
                                samlAttribute2SqlQuery=samlAttribute2SqlQuery)
        
        attributeInterface.connectionString = \
                        SQLAlchemyAttributeInterfaceTestCase.DB_CONNECTION_STR
                
        attributeInterface.samlValidRequestorDNs = (
            '/O=STFC/OU=CEDA/CN=AuthorisationService',
            '/O=ESG/OU=NCAR/CN=Gateway')
        
        attributeInterface.setProperties(samlAssertionLifetime=28800.)
        
        attributeInterface.samlSubjectSqlQuery = (
            SQLAlchemyAttributeInterfaceTestCase.SAML_SUBJECT_SQLQUERY)
        
        # Make the query
        try:
            attributeInterface.getAttributes(attributeQuery, samlResponse)
        except InvalidAttributeFormat:
            print("PASSED: caught InvalidAttributeFormat exception")
        else:
            self.fail("Expecting InvalidAttributeFormat exception")
    def __call__(self, environ, start_response):
        soapRequestStream = environ['wsgi.input']
        soapRequest = SOAPEnvelope()
        soapRequest.parse(soapRequestStream)
        attributeQueryElem = soapRequest.body.elem[0]
        attributeQuery = AttributeQueryElementTree.fromXML(attributeQueryElem)
        
        print("Received request from client:\n")
        print soapRequest.prettyPrint()
        
        samlResponse = Response()
        
        samlResponse.issueInstant = datetime.utcnow()
        samlResponse.id = str(uuid4())
        samlResponse.issuer = Issuer()
        
        # SAML 2.0 spec says format must be omitted
        #samlResponse.issuer.format = Issuer.X509_SUBJECT
        samlResponse.issuer.value = \
                        "/O=NDG/OU=BADC/CN=attributeauthority.badc.rl.ac.uk"
        
        samlResponse.inResponseTo = attributeQuery.id
        
        assertion = Assertion()
        
        assertion.version = SAMLVersion(SAMLVersion.VERSION_20)
        assertion.id = str(uuid4())
        assertion.issueInstant = samlResponse.issueInstant
        
        assertion.conditions = Conditions()
        assertion.conditions.notBefore = assertion.issueInstant
        assertion.conditions.notOnOrAfter = assertion.conditions.notBefore + \
            timedelta(seconds=60*60*8)
        
        assertion.subject = Subject()  
        assertion.subject.nameID = NameID()
        assertion.subject.nameID.format = attributeQuery.subject.nameID.format
        assertion.subject.nameID.value = attributeQuery.subject.nameID.value

        assertion.attributeStatements.append(AttributeStatement())
        
        for attribute in attributeQuery.attributes:
            if attribute.name == SamlSoapBindingApp.FIRSTNAME_ATTRNAME:
                # special case handling for 'FirstName' attribute
                fnAttribute = Attribute()
                fnAttribute.name = attribute.name
                fnAttribute.nameFormat = attribute.nameFormat
                fnAttribute.friendlyName = attribute.friendlyName
    
                firstName = XSStringAttributeValue()
                firstName.value = self.firstName
                fnAttribute.attributeValues.append(firstName)
    
                assertion.attributeStatements[0].attributes.append(fnAttribute)
            
            elif attribute.name == SamlSoapBindingApp.LASTNAME_ATTRNAME:
                lnAttribute = Attribute()
                lnAttribute.name = attribute.name
                lnAttribute.nameFormat = attribute.nameFormat
                lnAttribute.friendlyName = attribute.friendlyName
    
                lastName = XSStringAttributeValue()
                lastName.value = self.lastName
                lnAttribute.attributeValues.append(lastName)
    
                assertion.attributeStatements[0].attributes.append(lnAttribute)
               
            elif attribute.name == SamlSoapBindingApp.EMAILADDRESS_ATTRNAME:
                emailAddressAttribute = Attribute()
                emailAddressAttribute.name = attribute.name
                emailAddressAttribute.nameFormat = attribute.nameFormat
                emailAddressAttribute.friendlyName = attribute.friendlyName
    
                emailAddress = XSStringAttributeValue()
                emailAddress.value = self.emailAddress
                emailAddressAttribute.attributeValues.append(emailAddress)
    
                assertion.attributeStatements[0].attributes.append(
                                                        emailAddressAttribute)
        
        samlResponse.assertions.append(assertion)
        
        samlResponse.status = Status()
        samlResponse.status.statusCode = StatusCode()
        samlResponse.status.statusCode.value = StatusCode.SUCCESS_URI        

        
        # Convert to ElementTree representation to enable attachment to SOAP
        # response body
        samlResponseElem = ResponseElementTree.toXML(samlResponse)
        xml = ElementTree.tostring(samlResponseElem)
        log.debug('Sending response to query:\n%s', xml)
        
        # Create SOAP response and attach the SAML Response payload
        soapResponse = SOAPEnvelope()
        soapResponse.create()
        soapResponse.body.elem.append(samlResponseElem)
        
        response = soapResponse.serialize()
        
        start_response("200 OK",
                       [('Content-length', str(len(response))),
                        ('Content-type', 'text/xml')])
        return [response]
    def test01AttributeQuery(self):
        attributeQuery = AttributeQuery()
        attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20)
        attributeQuery.id = str(uuid4())
        attributeQuery.issueInstant = datetime.utcnow()
        
        attributeQuery.issuer = Issuer()
        attributeQuery.issuer.format = Issuer.X509_SUBJECT
        attributeQuery.issuer.value = \
                        "/O=NDG/OU=BADC/CN=attributeauthority.badc.rl.ac.uk"
                        
                        
        attributeQuery.subject = Subject()  
        attributeQuery.subject.nameID = NameID()
        attributeQuery.subject.nameID.format = SamlSoapBindingApp.NAMEID_FORMAT
        attributeQuery.subject.nameID.value = \
                                    "https://openid.localhost/philip.kershaw"
        
        # special case handling for 'FirstName' attribute
        fnAttribute = Attribute()
        fnAttribute.name = SamlSoapBindingApp.FIRSTNAME_ATTRNAME
        fnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
        fnAttribute.friendlyName = "FirstName"

        attributeQuery.attributes.append(fnAttribute)
    
        # special case handling for 'LastName' attribute
        lnAttribute = Attribute()
        lnAttribute.name = SamlSoapBindingApp.LASTNAME_ATTRNAME
        lnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
        lnAttribute.friendlyName = "LastName"

        attributeQuery.attributes.append(lnAttribute)
    
        # special case handling for 'LastName' attribute
        emailAddressAttribute = Attribute()
        emailAddressAttribute.name = SamlSoapBindingApp.EMAILADDRESS_ATTRNAME
        emailAddressAttribute.nameFormat = XMLConstants.XSD_NS+"#"+\
                                    XSStringAttributeValue.TYPE_LOCAL_NAME
        emailAddressAttribute.friendlyName = "emailAddress"

        attributeQuery.attributes.append(emailAddressAttribute)                                   
        
        elem = AttributeQueryElementTree.toXML(attributeQuery)
        soapRequest = SOAPEnvelope()
        soapRequest.create()
        soapRequest.body.elem.append(elem)
        
        request = soapRequest.serialize()
        
        header = {
            'soapAction': "http://www.oasis-open.org/committees/security",
            'Content-length': str(len(request)),
            'Content-type': 'text/xml'
        }
        response = self.app.post('/attributeauthority', 
                                 params=request, 
                                 headers=header, 
                                 status=200)
        print("Response status=%d" % response.status)

        soapResponse = SOAPEnvelope()
        
        responseStream = StringIO()
        responseStream.write(response.body)
        responseStream.seek(0)
        
        soapResponse.parse(responseStream)
        
        print("Parsed response ...")
        print(soapResponse.serialize())
#        print(prettyPrint(soapResponse.elem))
        
        response = ResponseElementTree.fromXML(soapResponse.body.elem[0])
        self.assert_(response.status.statusCode.value==StatusCode.SUCCESS_URI)
        self.assert_(response.inResponseTo == attributeQuery.id)
        self.assert_(response.assertions[0].subject.nameID.value == \
                     attributeQuery.subject.nameID.value)
    def test05SamlAttributeQuery(self):
        if self.skipTests:
            return

        # Prepare a client query
        attributeQuery = AttributeQuery()
        attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20)
        attributeQuery.id = str(uuid4())
        attributeQuery.issueInstant = datetime.utcnow()

        attributeQuery.issuer = Issuer()
        attributeQuery.issuer.format = Issuer.X509_SUBJECT
        attributeQuery.issuer.value = '/O=ESG/OU=NCAR/CN=Gateway'

        attributeQuery.subject = Subject()
        attributeQuery.subject.nameID = NameID()
        attributeQuery.subject.nameID.format = ESGFSamlNamespaces.NAMEID_FORMAT
        attributeQuery.subject.nameID.value = TestUserDatabase.OPENID_URI

        emailAddressAttribute = Attribute()
        emailAddressAttribute.name = ESGFSamlNamespaces.EMAILADDRESS_ATTRNAME
        emailAddressAttribute.nameFormat = "InvalidFormat"
        emailAddressAttribute.friendlyName = "EmailAddress"

        attributeQuery.attributes.append(emailAddressAttribute)

        authzAttribute = Attribute()
        authzAttribute.name = TestUserDatabase.ATTRIBUTE_NAMES[0]
        authzAttribute.nameFormat = XSStringAttributeValue.DEFAULT_FORMAT
        authzAttribute.friendlyName = "authz"

        attributeQuery.attributes.append(authzAttribute)

        # Add the response - the interface will populate with an assertion as
        # appropriate
        samlResponse = Response()

        samlResponse.issueInstant = datetime.utcnow()
        samlResponse.id = str(uuid4())
        samlResponse.issuer = Issuer()

        # Initialise to success status but reset on error
        samlResponse.status = Status()
        samlResponse.status.statusCode = StatusCode()
        samlResponse.status.statusMessage = StatusMessage()
        samlResponse.status.statusCode.value = StatusCode.SUCCESS_URI

        # Nb. SAML 2.0 spec says issuer format must be omitted
        samlResponse.issuer.value = "CEDA"

        samlResponse.inResponseTo = attributeQuery.id

        # Set up the interface object

        # Define queries for SAML attribute names
        samlAttribute2SqlQuery = {
            ESGFSamlNamespaces.FIRSTNAME_ATTRNAME:
            self.__class__.SAML_FIRSTNAME_SQLQUERY,
            ESGFSamlNamespaces.LASTNAME_ATTRNAME:
            self.__class__.SAML_LASTNAME_SQLQUERY,
            ESGFSamlNamespaces.EMAILADDRESS_ATTRNAME:
            self.__class__.SAML_EMAILADDRESS_SQLQUERY,
            TestUserDatabase.ATTRIBUTE_NAMES[0]:
            self.__class__.SAML_ATTRIBUTES_SQLQUERY
        }

        attributeInterface = SQLAlchemyAttributeInterface(
            samlAttribute2SqlQuery=samlAttribute2SqlQuery)

        attributeInterface.connectionString = TestUserDatabase.DB_CONNECTION_STR

        attributeInterface.samlValidRequestorDNs = (
            '/O=STFC/OU=CEDA/CN=AuthorisationService',
            '/O=ESG/OU=NCAR/CN=Gateway')

        attributeInterface.setProperties(samlAssertionLifetime=28800.)

        attributeInterface.samlSubjectSqlQuery = (
            SQLAlchemyAttributeInterfaceTestCase.SAML_SUBJECT_SQLQUERY)

        # Make the query
        attributeInterface.getAttributes(attributeQuery, samlResponse)
    def test04SamlAttributeQuery(self):
        if self.skipTests:
            return

        # Prepare a client query
        attributeQuery = AttributeQuery()
        attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20)
        attributeQuery.id = str(uuid4())
        attributeQuery.issueInstant = datetime.utcnow()

        attributeQuery.issuer = Issuer()
        attributeQuery.issuer.format = Issuer.X509_SUBJECT
        attributeQuery.issuer.value = '/O=ESG/OU=NCAR/CN=Gateway'


        attributeQuery.subject = Subject()
        attributeQuery.subject.nameID = NameID()
        attributeQuery.subject.nameID.format = ESGFSamlNamespaces.NAMEID_FORMAT
        attributeQuery.subject.nameID.value = \
                                TestUserDatabase.OPENID_URI

        fnAttribute = Attribute()
        fnAttribute.name = ESGFSamlNamespaces.FIRSTNAME_ATTRNAME
        fnAttribute.nameFormat = XSStringAttributeValue.DEFAULT_FORMAT
        fnAttribute.friendlyName = "FirstName"

        attributeQuery.attributes.append(fnAttribute)

        lnAttribute = Attribute()
        lnAttribute.name = ESGFSamlNamespaces.LASTNAME_ATTRNAME
        lnAttribute.nameFormat = XSStringAttributeValue.DEFAULT_FORMAT
        lnAttribute.friendlyName = "LastName"

        attributeQuery.attributes.append(lnAttribute)

        emailAddressAttribute = Attribute()
        emailAddressAttribute.name = ESGFSamlNamespaces.EMAILADDRESS_ATTRNAME
        emailAddressAttribute.nameFormat = XSStringAttributeValue.DEFAULT_FORMAT
        emailAddressAttribute.friendlyName = "EmailAddress"

        attributeQuery.attributes.append(emailAddressAttribute)

        authzAttribute = Attribute()
        authzAttribute.name = \
            TestUserDatabase.ATTRIBUTE_NAMES[0]
        authzAttribute.nameFormat = XSStringAttributeValue.DEFAULT_FORMAT
        authzAttribute.friendlyName = "authz"

        attributeQuery.attributes.append(authzAttribute)

        # Add the response - the interface will populate with an assertion as
        # appropriate
        samlResponse = Response()

        samlResponse.issueInstant = datetime.utcnow()
        samlResponse.id = str(uuid4())
        samlResponse.issuer = Issuer()

        # Initialise to success status but reset on error
        samlResponse.status = Status()
        samlResponse.status.statusCode = StatusCode()
        samlResponse.status.statusMessage = StatusMessage()
        samlResponse.status.statusCode.value = StatusCode.SUCCESS_URI

        # Nb. SAML 2.0 spec says issuer format must be omitted
        samlResponse.issuer.value = "CEDA"

        samlResponse.inResponseTo = attributeQuery.id

        # Set up the interface object

        # Define queries for SAML attribute names
        samlAttribute2SqlQuery = {
            ESGFSamlNamespaces.FIRSTNAME_ATTRNAME:
                self.__class__.SAML_FIRSTNAME_SQLQUERY,

            ESGFSamlNamespaces.LASTNAME_ATTRNAME:
                self.__class__.SAML_LASTNAME_SQLQUERY,

            ESGFSamlNamespaces.EMAILADDRESS_ATTRNAME:
                self.__class__.SAML_EMAILADDRESS_SQLQUERY,

            TestUserDatabase.ATTRIBUTE_NAMES[0]:
                self.__class__.SAML_ATTRIBUTES_SQLQUERY,
        }

        attributeInterface = SQLAlchemyAttributeInterface(
                                samlAttribute2SqlQuery=samlAttribute2SqlQuery)

        attributeInterface.connectionString = TestUserDatabase.DB_CONNECTION_STR

        attributeInterface.samlValidRequestorDNs = (
            '/O=STFC/OU=CEDA/CN=AuthorisationService',
            '/O=ESG/OU=NCAR/CN=Gateway')

        attributeInterface.setProperties(samlAssertionLifetime=28800.)

        attributeInterface.samlSubjectSqlQuery = (
            SQLAlchemyAttributeInterfaceTestCase.SAML_SUBJECT_SQLQUERY)

        # Make the query
        attributeInterface.getAttributes(attributeQuery, samlResponse)

        self.assert_(
                samlResponse.status.statusCode.value == StatusCode.SUCCESS_URI)
        self.assert_(samlResponse.inResponseTo == attributeQuery.id)
        self.assert_(samlResponse.assertions[0].subject.nameID.value == \
                     attributeQuery.subject.nameID.value)
        self.assert_(
            samlResponse.assertions[0].attributeStatements[0].attributes[1
                ].attributeValues[0].value == 'Kershaw')

        self.assert_(
            len(samlResponse.assertions[0].attributeStatements[0].attributes[3
                ].attributeValues) == TestUserDatabase.N_ATTRIBUTE_VALUES)
Ejemplo n.º 15
0
    def getAttributes(self, attributeQuery, response):
        """Attribute Authority SAML AttributeQuery

        @type attributeQuery: saml.saml2.core.AttributeQuery
        @param userId: query containing requested attributes
        @type response: saml.saml2.core.Response
        @param response: add an assertion with the list of attributes
        for the given subject ID in the query or set an error Status code and
        message
        @raise AttributeInterfaceError: an error occured requesting
        attributes
        @raise AttributeReleaseDeniedError: Requestor was denied release of the
        requested attributes
        @raise AttributeNotKnownError: Requested attribute names are not known
        to this authority
        """
        userId = attributeQuery.subject.nameID.value
        requestedAttributeNames = [
            attribute.name for attribute in attributeQuery.attributes
        ]

        requestorDN = X500DN.from_string(attributeQuery.issuer.value)

        if not self._queryDbForSamlSubject(userId):
            raise UserIdNotKnown('Subject Id "%s" is not known to this '
                                 'authority' % userId)

        if (len(self.samlValidRequestorDNs) > 0
                and requestorDN not in self.samlValidRequestorDNs):
            raise InvalidRequestorId('Requester identity "%s" is invalid' %
                                     requestorDN)

        unknownAttrNames = [
            attrName for attrName in requestedAttributeNames
            if attrName not in self.__samlAttribute2SqlQuery
        ]

        if len(unknownAttrNames) > 0:
            raise AttributeNotKnownError("Unknown attributes requested: %r" %
                                         unknownAttrNames)

        # Create a new assertion to hold the attributes to be returned
        assertion = Assertion()

        assertion.version = SAMLVersion(SAMLVersion.VERSION_20)
        assertion.id = str(uuid4())
        assertion.issueInstant = response.issueInstant

        # Assumes SAML response issuer details as set by -
        # ndg.security.server.wsgi.saml.SOAPQueryInterfaceMiddleware
        assertion.issuer = Issuer()
        assertion.issuer.value = response.issuer.value

        if response.issuer.format:
            assertion.issuer.format = response.issuer.format

        assertion.conditions = Conditions()
        assertion.conditions.notBefore = assertion.issueInstant
        assertion.conditions.notOnOrAfter = (assertion.conditions.notBefore +
                                             self.samlAssertionLifetime)

        assertion.subject = Subject()
        assertion.subject.nameID = NameID()
        assertion.subject.nameID.format = attributeQuery.subject.nameID.format
        assertion.subject.nameID.value = attributeQuery.subject.nameID.value

        attributeStatement = AttributeStatement()

        # Query the database for the requested attributes and return them
        # mapped to their attribute names as specified by the attributeNames
        # property
        for requestedAttribute in attributeQuery.attributes:
            attributeVals = self._queryDbForSamlAttributes(
                requestedAttribute.name, userId)

            # Make a new SAML attribute object to hold the values obtained
            attribute = Attribute()
            attribute.name = requestedAttribute.name
            attribute.nameFormat = requestedAttribute.nameFormat

            if requestedAttribute.friendlyName is not None:
                attribute.friendlyName = requestedAttribute.friendlyName

            # Call specific conversion utility to convert the retrieved field
            # to the correct SAML attribute value type
            try:
                field2SamlAttributeVal = self.__samlAttribute2SqlQuery[
                    requestedAttribute.name][-1]
            except (IndexError, TypeError) as e:
                raise AttributeInterfaceConfigError(
                    'Bad format for SAML '
                    'attribute to SQL query '
                    'look-up for attribute '
                    'name %r: %s' % (requestedAttribute.name, e))

            for val in attributeVals:
                attributeValue = field2SamlAttributeVal(val)
                attribute.attributeValues.append(attributeValue)

            attributeStatement.attributes.append(attribute)

        assertion.attributeStatements.append(attributeStatement)
        response.assertions.append(assertion)