def attribute_query(environ, attribute_requests, subject_id, subject_id_format='urn:esg:openid'): """ Make a query for a set of attributes Returns a dictionary of values for each attribute in the query @param attribute_requests: SAML Attribute instances @param subject_id: The ID of the subject, e.g. openid @param subject_id_format: optional format of the ID, default is openid """ # Grab info from the environ saml_trusted_ca_dir = environ.get('saml_trusted_ca_dir', '') attr_service_uri = environ.get('attr_service_uri', '') client_binding = AttributeQuerySslSOAPBinding() client_binding.sslCACertDir = saml_trusted_ca_dir client_binding.clockSkewTolerance = 1 # 1 second tolerance # Make a new query object query = AttributeQuery() query.version = SAMLVersion(SAMLVersion.VERSION_20) query.id = str(uuid4()) query.issueInstant = datetime.utcnow() query.issuer = Issuer() query.issuer.format = Issuer.X509_SUBJECT query.issuer.value = '/O=STFC/OU=SPBU/CN=test' query.subject = Subject() query.subject.nameID = NameID() query.subject.nameID.format = subject_id_format query.subject.nameID.value = subject_id # Specify what attributes to query for for attribute_request in attribute_requests: query.attributes.append(attribute_request) try: response = client_binding.send(query, uri=attr_service_uri) attribute_results = {} assertion = next(iter(response.assertions or []), None) if assertion: statement = next(iter(assertion.attributeStatements or []), None) if statement: for attribute in statement.attributes: values = [] for attribute_value in attribute.attributeValues: values.append(attribute_value.value) attribute_results[attribute.name] = values return attribute_results except (RequestResponseError, Error) as e: logger.error("Error processing SOAP query for {0}: {1}".format(id, e))
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
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
def buildAttributeQuery(self, issuer, subjectNameID): """Make a SAML Attribute Query @type issuer: basestring @param issuer: attribute issuer name @type subjectNameID: basestring @param subjectNameID: identity to query attributes for """ 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 = self.__class__.NAMEID_FORMAT attributeQuery.subject.nameID.value = subjectNameID attributeQuery.attributes = self.createAttributes() return attributeQuery
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 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)